Skip to content

Commit

Permalink
sixel: Implement DEC-accurate calculation of cursor position for jumping
Browse files Browse the repository at this point in the history
Right now only known to be implemented by development version of
Windows Terminal.
Until we can detect that this is the terminal we're running, requires
to set environment variable `TIMG_SIXEL_FULL_CELL_JUMP=1`

Issues: #145
  • Loading branch information
hzeller committed Jan 10, 2025
1 parent e5931b9 commit f49fb5a
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 13 deletions.
24 changes: 17 additions & 7 deletions src/sixel-canvas.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,11 @@ namespace timg {

SixelCanvas::SixelCanvas(BufferedWriteSequencer *ws, ThreadPool *thread_pool,
bool required_cursor_placement_workaround,
const DisplayOptions &opts)
: TerminalCanvas(ws), options_(opts), executor_(thread_pool) {
bool full_cell_jump, const DisplayOptions &opts)
: TerminalCanvas(ws),
options_(opts),
full_cell_jump_(full_cell_jump),
executor_(thread_pool) {
// Terminals might have different understanding where the curosr is placed
// after an image is sent.
// Apparently the original dec terminal placed it afterwards, but some
Expand Down Expand Up @@ -153,11 +156,18 @@ void SixelCanvas::Send(int x, int dy, const Framebuffer &fb_orig,
int SixelCanvas::cell_height_for_pixels(int pixels) const {
assert(pixels <= 0); // Currently only use-case
pixels = -pixels;
// Unlike the other exact pixel canvases where we have to round to the
// next even cell_y_px, here we first need to round to the next even 6
// first.
return -((round_to_sixel(pixels) + options_.cell_y_px - 1) /
options_.cell_y_px);
if (full_cell_jump_) {
// https://github.com/hzeller/timg/issues/145#issuecomment-2579962760
// As DEC intended.
return -((round_to_sixel(pixels) - 6) / options_.cell_y_px + 1);
}
else {
// Unlike the other exact pixel canvases where we have to round to the
// next even cell_y_px, here we first need to round to the next even 6
// first.
return -((round_to_sixel(pixels) + options_.cell_y_px - 1) /
options_.cell_y_px);
}
}

} // namespace timg
3 changes: 2 additions & 1 deletion src/sixel-canvas.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class ThreadPool;
class SixelCanvas final : public TerminalCanvas {
public:
SixelCanvas(BufferedWriteSequencer *ws, ThreadPool *thread_pool,
bool required_cursor_placement_workaround,
bool required_cursor_placement_workaround, bool full_cell_jump,
const DisplayOptions &opts);

int cell_height_for_pixels(int pixels) const final;
Expand All @@ -38,6 +38,7 @@ class SixelCanvas final : public TerminalCanvas {

private:
const DisplayOptions &options_;
const bool full_cell_jump_;
ThreadPool *const executor_;
const char *cursor_move_before_;
const char *cursor_move_after_;
Expand Down
8 changes: 8 additions & 0 deletions src/term-query.cc
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,8 @@ TermGraphicsInfo QuerySupportedGraphicsProtocol() {
result.preferred_graphics = GraphicsProtocol::kNone;
result.known_broken_sixel_cursor_placement =
timg::GetBoolenEnv("TIMG_SIXEL_NEWLINE_WORKAROUND");
result.sixel_full_cell_jump =
timg::GetBoolenEnv("TIMG_SIXEL_FULL_CELL_JUMP");
result.in_tmux = false;

// Environment variables can be changed, so guesses from environment
Expand Down Expand Up @@ -299,6 +301,12 @@ TermGraphicsInfo QuerySupportedGraphicsProtocol() {
if (find_str(data, len, "tmux")) {
result.in_tmux = true;
}
if (find_str(data, len, "WindowsTerminal")) {
// TODO: check again once name is established
// https://github.com/microsoft/terminal/issues/18382
result.known_broken_sixel_cursor_placement = true;
result.sixel_full_cell_jump = true;
}
// We finish once we found the response to DSR5
return find_str(data, len, TERM_CSI "0");
});
Expand Down
1 change: 1 addition & 0 deletions src/term-query.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ enum class GraphicsProtocol {
struct TermGraphicsInfo {
GraphicsProtocol preferred_graphics;
bool known_broken_sixel_cursor_placement; // see SixelCanvas impl. doc
bool sixel_full_cell_jump;
bool in_tmux;
};

Expand Down
13 changes: 8 additions & 5 deletions src/timg.cc
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ struct PresentationOptions {
// Rendering
Pixelation pixelation = Pixelation::kNotChosen;
bool sixel_cursor_workaround = false;
bool sixel_full_cell_jump = false;
bool tmux_workaround = false;
bool terminal_use_upper_block = false;
bool use_256_color = false; // For terminals that don't do 24 bit color
Expand Down Expand Up @@ -331,9 +332,9 @@ static int PresentImages(LoadedImageSources *loaded_sources,
#ifdef WITH_TIMG_SIXEL
case Pixelation::kSixelGraphics:
compression_pool.reset(new ThreadPool(sequencer->max_queue_len() + 1));
canvas.reset(new timg::SixelCanvas(sequencer, compression_pool.get(),
present.sixel_cursor_workaround,
display_opts));
canvas.reset(new timg::SixelCanvas(
sequencer, compression_pool.get(), present.sixel_cursor_workaround,
present.sixel_full_cell_jump, display_opts));
break;
#endif
case Pixelation::kHalfBlock:
Expand Down Expand Up @@ -770,8 +771,8 @@ int main(int argc, char *argv[]) {
if (present.pixelation == Pixelation::kNotChosen) {
present.pixelation = Pixelation::kQuarterBlock; // Good default.
if (term.font_width_px > 0 && term.font_height_px > 0) {
auto graphics_info = timg::QuerySupportedGraphicsProtocol();
present.tmux_workaround = graphics_info.in_tmux;
const auto graphics_info = timg::QuerySupportedGraphicsProtocol();
present.tmux_workaround = graphics_info.in_tmux;
switch (graphics_info.preferred_graphics) {
case timg::GraphicsProtocol::kIterm2:
present.pixelation = Pixelation::kiTerm2Graphics;
Expand All @@ -784,6 +785,8 @@ int main(int argc, char *argv[]) {
present.pixelation = Pixelation::kSixelGraphics;
present.sixel_cursor_workaround =
graphics_info.known_broken_sixel_cursor_placement;
present.sixel_full_cell_jump =
graphics_info.sixel_full_cell_jump;
#else
present.pixelation = Pixelation::kQuarterBlock;
#endif
Expand Down

0 comments on commit f49fb5a

Please sign in to comment.