Commit graph

115 commits

Author SHA1 Message Date
Daniel Eklöf
6658740982
sixel: store current row position in pixels, not characters 2021-03-11 17:32:55 +01:00
Daniel Eklöf
c95c663989
sixel: size provided by DECGRA does *not* limit the sixel size
Foot has, up until now, used a fixed image size when the application
used DECGRA (Raster Attributes) to “configure” the image size.

This was based on a misunderstanding, that this was how you emitted
sixels where the height was *not* a multiple of 6.

This isn’t the case. The VT340 documentation is actually pretty clear
about this:

  Ph and Pv do not limit the size of the image defined by the sixel
  data. However, Ph and Pv let you omit background sixel data from the
  image definition and still have a color background. They also
  provide a concise way for the application or terminal to encode the
  size of an image.

This is also how XTerm behaves. Test image:

  \EPq
  "1;1;1;1
  #0;2;0;0;0#1;2;100;100;0#2;2;0;100;0
  #1~~@@vv@@~~@@~~$
  #2??}}GG}}??}}??-
  #1!14@
  \E\

This uses DECGRA to set the image size to 1x1. The sixel however
is *not* clipped to 1x1, but is resized to 14x12
2021-03-06 15:03:47 +01:00
Daniel Eklöf
1563fecc20
sixel: don’t go past the bottom scroll margin when sixel scrolling is disabled
When sixel scrolling is disabled (private mode 80 is off), and scroll
margins have been set, XTerm seems to ignore the top margin (sixel
still begins at (0,0)), but does not go past the bottom margin.

This patch implements the same behavior in foot.
2021-02-26 14:20:00 +01:00
Daniel Eklöf
849427bf10
sixel: implement private mode 80 - sixel scrolling
When enabled (the default), sixels behave much like normal output; the
start where the cursor is, and the cursor moves with the
sixel. I.e. after emitting a sixel the cursor is left after the image;
either to the right, if private mode 8452 is enabled, or otherwise on
the next line. Terminal content is scrolled up if the sixel is larger
than the screen.

When disabled, sixels *always* start at (0,0), the cursor never moves,
and the terminal content never scrolls.

In other words, the ‘disabled’ mode is a much simpler mode.

All we need to do to support both modes is re-write the sixel-emitting
loop to:

* break early if we’re “out of rows”, i.e. we’ve reached the bottom of
  the screen.
* not linefeed, or move the cursor when scrolling is disabled

This patch also fixes a bug in the (new) implementation of private
mode 8452.

When emitting a sixel, we may break it up into smaller pieces, to
ensure a single sixel (as tracked internally) does not cross the
scrollback wrap-around.

The code that checked if we should do a linefeed or not, would skip
the linefeed on the last row of *each* such sixel piece. The correct
thing to do is to skip it only on the last row of the *last* piece.

I chose not to fix this bug in a separate patch since doing so would
have meant re-writing it again when implementing private mode 80.
2021-02-26 09:28:03 +01:00
Daniel Eklöf
03c675c6e2
sixel: implement private mode 8452 - cursor positioning after sixel
When disabled (the default), the cursor is positioned on a new line
after emitting a sixel image.

When enabled, the cursor is positioned to the right of the sixel
image.

Closes #363
2021-02-23 10:36:02 +01:00
Daniel Eklöf
8c44b63938
sixel: free private color registers on unhook 2021-02-23 09:40:22 +01:00
Daniel Eklöf
4aa980a6a2
sixel: implement private mode 1070 - private color palette
When enabled (the default), sixels use private color registers. That
is, the color palette from the last sixel is *not* re-used.

When disabled, sixels share (i.e. re-use) the same color palette.

Closes #362
2021-02-23 09:40:22 +01:00
Craig Barnes
e56136ce11 debug: rename assert() to xassert(), to avoid clashing with <assert.h> 2021-01-16 20:16:00 +00:00
Craig Barnes
22f25a9e4f Print stack trace on assert() failure or when calling fatal_error()
Note: this uses the __sanitizer_print_stack_trace() function from the
AddressSanitizer runtime, so it only works when AddressSanitizer is
in use.
2021-01-16 19:56:33 +00:00
Craig Barnes
3f4cfa338b Add xsnprintf() and remove some unnecessary strlen(3) calls 2021-01-14 21:30:06 +00:00
Daniel Eklöf
5efd34c3c4
sixel: current geometry: don’t exceed current window dimensions
This is similar to what XTerm does, and fixes an issue with lsix,
where the output did not wrap.
2021-01-14 14:41:34 +01:00
Daniel Eklöf
ba8b15d675
sixel: change default max size to 10000x10000
It used to be the size of the window. This caused images to be cropped
when the application emitting them didn’t change the max size.
2020-11-23 20:10:55 +01:00
Daniel Eklöf
275f97381d
sixel: fix crash when an explicit sixel size had a height less than 6 pixels 2020-11-23 19:22:40 +01:00
Daniel Eklöf
e0297daa1f
hsl: add our own implementations of rgb-to-hsl and hsl-to-rgb
* New function: rgb_to_hsl()
* New function: hsl_to_rgb()
* Replace XTerm’s hls_to_rgb() with our own, hsl_to_rgb().
* Ensure hue/lum/sat values are within range before calling
  hsl_to_rgb()

Note that sixels’ use the following primary hues:

*  blue:  0°
*  red:   120
*  green: 240°

While “standard” HSL uses:

*  red:   0°
*  green: 120°
*  blue:  240°

Thus, we need to adjust the sixel’s hue value before calling
hsl_to_rgb().
2020-11-15 19:45:33 +01:00
Daniel Eklöf
4b645376fd
render: improve sixel rendering performance
Up until now, we’ve always re-rendered the entire image (the part of
it that is visible at least), *every* time we render a frame.

This is not really needed. In many cases, the cells covered by the
image hasn’t been touched.

Rewrite the sixel rendering code to only render the part of the sixel
image that covers dirty cells.

This is done on a per-row basis. I.e. Each *row* of the image that
covers at least one dirty cell is re-rendered. For this to work, we
now also dirty all cells covered by the image when we emit the image.

Finally, for this to work, the sixels need to be rendered *before* we
do the normal grid render pass (since that will clear all dirty bits).
2020-11-13 16:54:40 +01:00
Daniel Eklöf
468add5591
sixel: reflow: drop sixels that crosses the scrollback history’s end
This fixes various crashes that occurred after a reflow due to the
sixel image list invariants no longer being true.
2020-10-09 07:44:18 +02:00
Daniel Eklöf
2c952761f2
sixel: unhook: do overwrite *after* linefeeding
This ensures the overwrite is done when the scrollback history is in
the same state as when we then insert the new image.
2020-10-09 07:44:18 +02:00
Daniel Eklöf
93f5e743cc
sixel: overwrite: use pixman to calculate new the sixel boundaries
When punching a hole in a sixel (and thus splitting it up into up to
four new sixels), use pixman to calculate the new sixel coordinates
and sizes.
2020-10-09 07:44:18 +02:00
Daniel Eklöf
91c0aed26e
sixel: ovewrite-by-rectangle: in debug builds, cross-reference against pixman
Use pixman to calculate the intersection of the rectangle being
overwritten, and the sixel(s). Verify our code matches.
2020-10-09 07:44:18 +02:00
Daniel Eklöf
5ebab9dea9
sixel: verify scrollback consistency: new verify function
Verifies sixels have been scrolled out correctly
2020-10-09 07:44:18 +02:00
Daniel Eklöf
11760e9071
sixel: add comments to verify_*() functions 2020-10-09 07:44:17 +02:00
Daniel Eklöf
5607e5d658
sixel: verify-*: don’t return anything; rely on asserts only 2020-10-09 07:44:17 +02:00
Daniel Eklöf
0a5a6cb7fa
sixel: scroll up/down: early return when list is empty
Branch tagged as ‘likely’ for performance reason
2020-10-09 07:44:17 +02:00
Daniel Eklöf
984083bf19
sixel: cell-size-changed: don’t verify sixels here
The state after this function is an intermediate state and isn’t
necessarily valid.

This sixels needs to be ‘reflowed’ to ensure a valid state. This is
something that should be done by the caller after the text grid has
been reflowed and the sixel coordinates have been re-mapped to the new
grid.

TODO: can/should we update the sixel cols/rows in sixel_reflow()
instead?
2020-10-09 07:44:17 +02:00
Daniel Eklöf
cf620cf3d0
sixel: unhook: look total number of sixels
This helps debug sixel overwrites, as it makes it more visible when
there are sixels that _should_ have been removed.
2020-10-09 07:44:17 +02:00
Daniel Eklöf
b66e235e84
sixel: scroll-up: don’t break out early of loop
This function loops the list of sixels, and discards those that would
be scrolled out if the grid offset is moved forward by the specified
number of rows.

The criteria is when the rebased row value is less than the number of
rows to scroll.

A rebased row number is a zero-based number starting at the beginning
of the scrollback. Thus, when scrolling 5 rows, rows with a rebased
row number between 0-4 will be scrolled out.

For performance reasons, we used to break out of the loop as soon as a
row number *larger* than the scroll count.

This however does not work. The sixels are sorted by their *end*
row. While this works in most cases (think images outputted in the
shell in the normal screen), it doesn’t always.

In the alt screen, where applications can print images just about
anywhere, it is possible to have *any* start row number anywhere in
the sixel list. Just as long as their *end* row numbers are sorted.

For example, a huuuge sixel that covers the entire scrollback. This
sixel will naturally be first in the list (and thus sixel_scroll_up()
will visit it *last*, since it iterates the list in reverse), but
should still be destroyed when scrolling.
2020-10-09 07:44:17 +02:00
Daniel Eklöf
e870a0068f
sixel: verify-list-order: add an ‘index’ variable
This will make it easier when debugging assertions in this function.
2020-10-09 07:44:17 +02:00
Daniel Eklöf
675f3e56c8
sixel: overwrite-by-rectangle: assert sixels don’t cross scrollback wrap-around 2020-10-09 07:44:17 +02:00
Daniel Eklöf
8168fc19df
sixel: scroll: call sixel_erase() instead of sixel_destroy()
This ensures the screen is updated correctly. Without this, the sixel
image would remain on screen until force-refreshed by some other means.
2020-10-09 07:44:17 +02:00
Daniel Eklöf
42cc84eab7
sixel: TOOD -> TODO (fixes codespell build error) 2020-10-09 07:44:17 +02:00
Daniel Eklöf
8129ff69c9
sixel: verify-no-overlap: free pixman regions 2020-10-09 07:44:16 +02:00
Daniel Eklöf
2f5df30ef5
sixel: verify-no-overlap: initialize ‘intersection’ pixman region 2020-10-09 07:44:16 +02:00
Daniel Eklöf
47298776dc
sixel: unhook: only call render_refresh() once 2020-10-09 07:44:16 +02:00
Daniel Eklöf
dbfc636ade
sixel: implement reflow
Move sixel reflow from grid_reflow() to sixel_reflow(). This lets us
use internal sixel functions to update the sixels.

Note that grid_reflow() still needs to remap the sixelss coordinates.
2020-10-09 07:44:16 +02:00
Daniel Eklöf
9102194846
sixel: verify-sixels: check for bad list order last 2020-10-09 07:44:16 +02:00
Daniel Eklöf
14b4231c09
sixel: verify-no-wraparound-crossover: fix calculation of ‘end’ row 2020-10-09 07:44:16 +02:00
Daniel Eklöf
52a7155897
sixel: add sixel_cell_size_changed()
This function should be called *after* the cell dimensions have
changed, but *before* resizing/reflowing the grids.
2020-10-09 07:44:16 +02:00
Daniel Eklöf
cf48d1dc4c
sixel: debug: more fine-grained verification of sixel image list
* Verify no sixel crosses the scrollback wrap-around
* Verify no sixels overlap
2020-10-09 07:44:15 +02:00
Daniel Eklöf
5a6b96817d
sixel: overwrite: calculate split-up image pieces’ rows/cols from their width/height 2020-10-09 07:44:15 +02:00
Daniel Eklöf
a53a81cebf
sixel: overwrite: remove asserts
Sixels may extend outside the visible screen area
2020-10-09 07:44:15 +02:00
Daniel Eklöf
bc75f4744c
sixel: fix sheared image when image crosses scrollback wrap-around
When a sixel image crosses the scrollback wrap-around, it is split up
into (at least) two pieces.

We use cursor->point.col for all pieces’ x-coordinate. This caused the
final image to appear sheared, since we do a carriage-return (after a
number of linefeeds) after each piece - this causes the cursor’s
position to be reset to the left margin.

The solution is simple; remember the cursor’s initial x-coordinate,
and use that to position all image pieces.

Closes #151.
2020-10-09 07:44:15 +02:00
Daniel Eklöf
96405c2ca9
sixel: overwrite-by-rectangle expects ‘width’ to not exceed screen
This fixes a crash when the emitted sixel extends beyond the right
margin. The crash only happens when there are other sixel images
already emitted.

Fixes part of #151
2020-10-09 07:44:13 +02:00
Daniel Eklöf
7e026ba119
sixel: fold long line 2020-10-09 07:43:57 +02:00
Daniel Eklöf
aa3985a298
Don't use "case X ... Y:", if possible/where it makes sense 2020-08-23 10:07:08 +02:00
Daniel Eklöf
e32c0d9bf6
Cast printf formatter %p arguments to void* 2020-08-23 10:07:08 +02:00
Daniel Eklöf
dabdffafa5
don't use empty struct initializers 2020-08-23 10:07:00 +02:00
Craig Barnes
7a77958ba2 Convert most dynamic allocations to use functions from xmalloc.h 2020-08-08 20:37:57 +01:00
Daniel Eklöf
f6473756d9
sixel: remove sixel-to-be-splitted *before* splitting it
This fixes an assertion in debug builds, due to the pre-splitted and
splitted sixels overlapping.
2020-07-23 18:15:29 +02:00
Daniel Eklöf
fda6e9c2c9
sixel: overwrite: use target sixel's height, not cell height
When copying the image data for the left and right parts of the
splitted images, use the sixel's height, not the cell height.

This fixes a crash when the sixel didn't cover the entire cell height.
2020-07-23 18:13:54 +02:00
Daniel Eklöf
674f0dd0fc
sixel: verify list order: fix assertion when two sixels are on the same row
Two sixel may in fact exist on the same row, assuming their columns
don't overlap.
2020-07-16 08:13:44 +02:00