This is basically just a loop that renders additional glyphs on top
off the base glyph.
Since we now need access to the row struct, for the combining
characters, the function prototype for 'render_cell()' has changed.
When rendering the combining characters we also need to deal with
broken fonts; some fonts use positive offsets (as if the combining
character was a regular character), while others use a negative
offset (to be used as if you had already advanced the pen position).
We handle both - a positive offset is rendered just like a regular
glyph. When we see a negative offset we simply add the width of a cell
first.
The data is *not* added to the cell struct, since that one is too
performance critical.
Instead, the data is added as a separate array in the row struct.
This allows our performance critical code paths that e.g. clear cells
to perform as before.
The way things works right now, we cannot enable the ptmx FDM callback
right away. We need to wait for the Wayland window to have been
configured.
Before the window is configured, we don't have a size, and no
grid. Thus, if we try to process ptmx data we'll crash since we have
no where to write it to.
So, registering the ptmx fd with the FDM is now delayed until we've
received the first 'configure' event from Wayland.
We now create a copy of the config for each client, and updates it
with the values passed from the client.
Since we're not actually cloning it (and e.g. strdup() all strings
etc) we can't call conf_destroy() to free it, but need to free just
the strings we've replaced.
Track whether app-sync updates were enabled or disabled while handling
the current chunk of PTMX data.
This fixes and issue where we called render_refresh() unnecessarily
under (at least) the following conditions:
* Application sent "BSU <data> ESU" in the *same* chunk. In this case
we never saw that app sync was enabled and triggered delayed
rendering as usual.
* Application sent ESU. While we had noticed app sync updates being
enabled in earlier PTMX reads, when it was disabled *in the current*
PTMX read, we treated it as if it had not been enabled at all.
This caused us to trigger delayed rendering.
Now we call render_refresh() directly from ESU, and detect the "flip
off" case in PTMX read and avoid triggering a delayed rendering.
The end result of all this is that each key press (for e.g. scrolling
in a pager application) went from two frames being rendered, to a
single frame.
This fixes an issue where we failed to restore the cursor correctly
when exiting from the alternate screen, if the client had sent escapes
to save the cursor position while inside the alternate screen.
This was because we used the *same* storage for saving the cursor
position through escapes, as for saving it when entering the alternate
screen.
Fix by using a custom variable dedicated to normal <--> alt screen
switching.
Update the sixels' 'row' attribute when re-flowing a grid, to ensure
it is rendered at the correct place.
This should work in most cases, but will break when the cell size has
changed (e.g. font size increase/decrease, or a DPI change).
This patch also moves the sixel image list from the terminal struct
into the grid struct. The sixels are per-grid after all.
Handle the CSDs and the search box the same way we handle the main
grid; when we need to redraw them, call
render_refresh_{csd,search}(). This sets a flag that is checked after
each FDM iteration. All actual rendering is done here.
This also ties the commits of the Wayland sub-surfaces to the commit
of the main surface.
When the compositor wants us to decide the size (it sends a configure
event with width/height == 0), then use the last unmaximized size, if
there is one.
If there isn't one, use the size from the user configuration.
This is needed to handle pointer motion and button events correctly,
since mouse actions in e.g. CSD surfaces are very different from mouse
actions in the main window.
In unhook, add the generated image to a list of finished sixel images,
along with positioning and size information.
When rendering, loop this list of images, and render the images (or
parts of) that are visible.
When scrolling, check if any part of the images cover the re-cycled
lines, and if so, remove the *entire* image from the list.
This means we have the following limitations:
* The renderer always renders the whole (visible area of) the
image(s). There are times when this isn't necessary - for example,
when the image is scrolled inside the visible area.
* It would be nice if we could crop the image when parts of it is
scrolled out.
This implements basic parsing of sixel data. Lots of limitations and
temporary solutions as this is still work-in-progress:
* Maximum image size hardcoded to 800x800
* No HLS color format support
* Image is always rendered at 0x0 in the terminal
This function reloads the font *if* the DPI has changed. To handle
user run-time adjusted font sizes, we record the number of adjustments
made.
Then, when re-loading the font, we first load the font as specified in
the configuration. Then, we re-apply the size adjustment using
font_size_adjust().
Note that this means we end up loading the fonts twice; first using
the default size (but with adjusted DPI), and then again with the
adjusted size. This can probably be improved upon.
The existing font code has been refactored to avoid code
duplication. For example, term_init() now calls
term_font_dpi_changed() to load the initial fonts, instead of directly
instantiating them.
Finally, the way we calculate the DPI to use has changed: instead of
using the highest DPI of all available outputs, we use the highest DPI
of the output's we're actually mapped on. If we're not mapped at all,
we use the globally highest DPI.
Doing it this way means we usually only have to load the fonts
once. Otherwise, we'd end up using the default DPI of 96 when the
terminal is first instantiated (since it's not mapped at that time).
On a single monitor system, we'll use the globally highest DPI at
first, before being mapped. Then when we get mapped, we re-load the
fonts using the highest mapped DPI. But since they'll be the same,
we can skip actually reloading the fonts.
To do text reflow, we only need to know if a line has been explicitly
linebreaked or not. If not, that means it wrapped, and that we
should *not* insert a linebreak when reflowing text.
When reflowing text, when reaching the end of a row in the old grid,
only insert a linebreak in the new grid if the old row had been
explicitly linebreaked.
Furthermore, when reflowing text and wrapping a row in the new grid,
mark the previous row as linebreaked if either the last cell was
(the last column in the last row) empty, or the current cell (the
first column in the new row) is empty. If both are non-empty, then we
assume a linewrap.
To handle text reflow correctly when a line has a printable character
in the last column, but was still line breaked, we need to track the
fact that the slave inserted a line break here.
Otherwise, when the window width is increased, we'll end up pulling up
the next line, when we really should have inserted a line break.
Add data structure to term->vt. This structure tracks the free-form
data that is passed-through, and the handler to call at the end.
Intermediates and parameters are collected by the normal VT
parser. Then, when we enter the passthrough state, we call dcs_hook().
This function checks the intermediate(s) and parameters, and selects
the appropriate unhook handler (and optionally does some execution
already).
In passthrough mode, we simply append strings to an internal
buffer. This might have to be changed in the future, if we need to
support a DCS that needs to execute as we go.
In unhook (i.e. when the DCS is terminated), we execute the unhook
handler.
As a proof-of-concept, handlers for BSU/ESU (Begin/End Synchronized
Update) has been added (but are left unimplemented).
Previously when updating a selection, we would unmark *all* cells in
the old selection, and then mark all cells in the new selection.
This caused *all* cells to be dirtied and thus re-rendered.
Avoid this, by adding a temporary state to the cells' selected state.
Before unmarking the old selection, pre-mark the new selection using a
temporary state.
When unmarking the old selection, ignore cells in this temporary state.
When marking the new selection, ignore cells in this temporary
state (except clearing the temporary state).