2019-06-17 19:33:10 +02:00
|
|
|
#pragma once
|
|
|
|
|
|
2019-07-01 12:23:38 +02:00
|
|
|
#include <stddef.h>
|
2021-01-16 20:16:00 +00:00
|
|
|
#include "debug.h"
|
2019-06-17 19:33:10 +02:00
|
|
|
#include "terminal.h"
|
2019-07-01 12:23:38 +02:00
|
|
|
|
2021-02-22 10:21:58 +01:00
|
|
|
struct grid *grid_snapshot(const struct grid *grid);
|
2021-02-22 10:20:52 +01:00
|
|
|
void grid_free(struct grid *grid);
|
|
|
|
|
|
2020-05-16 23:43:05 +02:00
|
|
|
void grid_swap_row(struct grid *grid, int row_a, int row_b);
|
2019-08-22 17:33:23 +02:00
|
|
|
struct row *grid_row_alloc(int cols, bool initialize);
|
2019-07-10 16:27:55 +02:00
|
|
|
void grid_row_free(struct row *row);
|
resize: don’t reflow text on alt screen
Alt screen applications normally reflow/readjust themselves on a
window resize.
When we do it too, the result is graphical glitches/flashes since our
re-flowed text is rendered in one frame, and the application re-flowed
text soon thereafter.
We can’t avoid rendering some kind of re-flowed frame, since we don’t
know when, or even if, the application will update itself. To avoid
glitches, we need to render, as closely as possible, what the
application itself will render shortly.
This is actually pretty simple; we just need to copy the visible
content over from the old grid to the new grid. We don’t bother with
text re-flow, but simply truncate long lines.
To simplify things, we simply cancel any active selection (since often
times, it will be corrupted anyway when the application redraws
itself).
Since we’re not reflowing text, there’s no need to translate e.g. the
cursor position - we just keep the current position (but bounded to
the new dimensions).
Fun thing: ‘less’ gets corrupted if we don’t leave the cursor at
the (new) bottom row. To handle this, we check if the cursor (before
resize) is at the bottom row, and if so, we move it to the new bottom
row.
Closes #221
2020-11-24 19:00:57 +01:00
|
|
|
|
|
|
|
|
void grid_resize_without_reflow(
|
|
|
|
|
struct grid *grid, int new_rows, int new_cols,
|
|
|
|
|
int old_screen_rows, int new_screen_rows);
|
|
|
|
|
|
|
|
|
|
void grid_resize_and_reflow(
|
2020-02-15 22:19:08 +01:00
|
|
|
struct grid *grid, int new_rows, int new_cols,
|
2020-04-17 21:04:32 +02:00
|
|
|
int old_screen_rows, int new_screen_rows,
|
|
|
|
|
size_t tracking_points_count,
|
composed: store compose chains in a binary search tree
The previous implementation stored compose chains in a dynamically
allocated array. Adding a chain was easy: resize the array and append
the new chain at the end. Looking up a compose chain given a compose
chain key/index was also easy: just index into the array.
However, searching for a pre-existing chain given a codepoint sequence
was very slow. Since the array wasn’t sorted, we typically had to scan
through the entire array, just to realize that there is no
pre-existing chain, and that we need to add a new one.
Since this happens for *each* codepoint in a grapheme cluster, things
quickly became really slow.
Things were ok:ish as long as the compose chain struct was small, as
that made it possible to hold all the chains in the cache. Once the
number of chains reached a certain point, or when we were forced to
bump maximum number of allowed codepoints in a chain, we started
thrashing the cache and things got much much worse.
So what can we do?
We can’t sort the array, because
a) that would invalidate all existing chain keys in the grid (and
iterating the entire scrollback and updating compose keys is *not* an
option).
b) inserting a chain becomes slow as we need to first find _where_ to
insert it, and then memmove() the rest of the array.
This patch uses a binary search tree to store the chains instead of a
simple array.
The tree is sorted on a “key”, which is the XOR of all codepoints,
truncated to the CELL_COMB_CHARS_HI-CELL_COMB_CHARS_LO range.
The grid now stores CELL_COMB_CHARS_LO+key, instead of
CELL_COMB_CHARS_LO+index.
Since the key is truncated, collisions may occur. This is handled by
incrementing the key by 1.
Lookup is of course slower than before, O(log n) instead of
O(1).
Insertion is slightly slower as well: technically it’s O(log n)
instead of O(1). However, we also need to take into account the
re-allocating the array will occasionally force a full copy of the
array when it cannot simply be growed.
But finding a pre-existing chain is now *much* faster: O(log n)
instead of O(n). In most cases, the first lookup will either
succeed (return a true match), or fail (return NULL). However, since
key collisions are possible, it may also return false matches. This
means we need to verify the contents of the chain before deciding to
use it instead of inserting a new chain. But remember that this
comparison was being done for each and every chain in the previous
implementation.
With lookups being much faster, and in particular, no longer requiring
us to check the chain contents for every singlec chain, we can now use
a dynamically allocated ‘chars’ array in the chain. This was
previously a hardcoded array of 10 chars.
Using a dynamic allocated array means looking in the array is slower,
since we now need two loads: one to load the pointer, and a second to
load _from_ the pointer.
As a result, the base size of a compose chain (i.e. an “empty” chain)
has now been reduced from 48 bytes to 32. A chain with two codepoints
is 40 bytes. This means we have up to 4 codepoints while still using
less, or the same amount, of memory as before.
Furthermore, the Unicode random test (i.e. write random “unicode”
chars) is now **faster** than current master (i.e. before text-shaping
support was added), **with** test-shaping enabled. With text-shaping
disabled, we’re _even_ faster.
2021-06-24 13:17:07 +02:00
|
|
|
struct coord *const _tracking_points[static tracking_points_count]);
|
2019-07-10 16:27:55 +02:00
|
|
|
|
2022-04-25 19:57:18 +02:00
|
|
|
/* Convert row numbers between scrollback-relative and absolute coordinates */
|
|
|
|
|
int grid_row_abs_to_sb(const struct grid *grid, int screen_rows, int abs_row);
|
|
|
|
|
int grid_row_sb_to_abs(const struct grid *grid, int screen_rows, int sb_rel_row);
|
|
|
|
|
|
osc: add support for OSC 133;A (prompt markers)
This patch adds support for the OSC-133;A sequence, introduced by
FinalTerm and implemented by iTerm2, Kitty and more. See
https://iterm2.com/documentation-one-page.html#documentation-escape-codes.html.
The shell emits the OSC just before printing the prompt. This lets the
terminal know where, in the scrollback, there are prompts.
We implement this using a simple boolean in the row struct ("this row
has a prompt"). The prompt marker must be reflowed along with the text
on window resizes.
In an ideal world, erasing, or overwriting the cell where the OSC was
emitted, would remove the prompt mark. Since we don't store this
information in the cell struct, we can't do that. The best we can do
is reset it in erase_line(). This works well enough in the "normal"
screen, when used with a "normal" shell. It doesn't really work in
fullscreen apps, on the alt screen. But that doesn't matter since we
don't support jumping between prompts on the alt screen anyway.
To be able to jump between prompts, two new key bindings have been
added: prompt-prev and prompt-next, bound to ctrl+shift+z and
ctrl+shift+x respectively.
prompt-prev will jump to the previous, not currently visible, prompt,
by moving the viewport, ensuring the prompt is at the top of the
screen.
prompt-next jumps to the next prompt, visible or not. Again, by moving
the viewport to ensure the prompt is at the top of the screen. If
we're at the bottom of the scrollback, the viewport is instead moved
as far down as possible.
Closes #30
2022-06-15 18:44:23 +02:00
|
|
|
int grid_sb_start_ignore_uninitialized(const struct grid *grid, int screen_rows);
|
|
|
|
|
int grid_row_abs_to_sb_precalc_sb_start(
|
|
|
|
|
const struct grid *grid, int sb_start, int abs_row);
|
|
|
|
|
int grid_row_sb_to_abs_precalc_sb_start(
|
|
|
|
|
const struct grid *grid, int sb_start, int sb_rel_row);
|
|
|
|
|
|
2019-08-27 19:33:19 +02:00
|
|
|
static inline int
|
|
|
|
|
grid_row_absolute(const struct grid *grid, int row_no)
|
|
|
|
|
{
|
|
|
|
|
return (grid->offset + row_no) & (grid->num_rows - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
|
grid_row_absolute_in_view(const struct grid *grid, int row_no)
|
|
|
|
|
{
|
|
|
|
|
return (grid->view + row_no) & (grid->num_rows - 1);
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-08 13:57:31 +02:00
|
|
|
static inline struct row *
|
2019-08-22 17:33:23 +02:00
|
|
|
_grid_row_maybe_alloc(struct grid *grid, int row_no, bool alloc_if_null)
|
2019-07-08 13:57:31 +02:00
|
|
|
{
|
2021-01-16 20:16:00 +00:00
|
|
|
xassert(grid->offset >= 0);
|
2019-07-10 16:27:55 +02:00
|
|
|
|
2019-08-27 19:33:19 +02:00
|
|
|
int real_row = grid_row_absolute(grid, row_no);
|
2019-07-10 16:27:55 +02:00
|
|
|
struct row *row = grid->rows[real_row];
|
|
|
|
|
|
2019-08-22 17:33:23 +02:00
|
|
|
if (row == NULL && alloc_if_null) {
|
|
|
|
|
row = grid_row_alloc(grid->num_cols, false);
|
2019-07-10 16:27:55 +02:00
|
|
|
grid->rows[real_row] = row;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
xassert(row != NULL);
|
2019-07-10 16:27:55 +02:00
|
|
|
return row;
|
2019-07-08 13:57:31 +02:00
|
|
|
}
|
|
|
|
|
|
2019-08-22 17:33:23 +02:00
|
|
|
static inline struct row *
|
|
|
|
|
grid_row(struct grid *grid, int row_no)
|
|
|
|
|
{
|
|
|
|
|
return _grid_row_maybe_alloc(grid, row_no, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline struct row *
|
|
|
|
|
grid_row_and_alloc(struct grid *grid, int row_no)
|
|
|
|
|
{
|
|
|
|
|
return _grid_row_maybe_alloc(grid, row_no, true);
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-09 16:26:36 +02:00
|
|
|
static inline struct row *
|
2019-07-10 16:27:55 +02:00
|
|
|
grid_row_in_view(struct grid *grid, int row_no)
|
2019-07-09 16:26:36 +02:00
|
|
|
{
|
2021-01-16 20:16:00 +00:00
|
|
|
xassert(grid->view >= 0);
|
2019-07-09 16:26:36 +02:00
|
|
|
|
2019-08-27 19:33:19 +02:00
|
|
|
int real_row = grid_row_absolute_in_view(grid, row_no);
|
2019-07-10 16:27:55 +02:00
|
|
|
struct row *row = grid->rows[real_row];
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
xassert(row != NULL);
|
2019-07-10 16:27:55 +02:00
|
|
|
return row;
|
|
|
|
|
}
|
2021-02-14 20:32:38 +01:00
|
|
|
|
osc8: update URI ranges as we print data, *not* when the URI is closed
At first, an OSC-8 URI range was added when we received the closing
OSC-8 escape (i.e. with an empty URI).
But, this meant that cursor movements while the OSC-8 escape was in
effect wasn’t handled correctly, since we’d add a range that spanned
the cursor movements.
Attempts were made to handle this in the cursor movement functions, by
closing and re-opening the URI.
However, there are too many corner cases to make this a viable
approach. Scrolling is one such example, line-wrapping another.
This patch takes a different approach; emit, or update the URI range
when we print to the grid. This models the intended behavior much more
closely, where an active OSC-8 URI act like any other SGR attribute -
it is applied to all cells printed to, but otherwise have no effect.
To avoid killing performance, this is only done in the “generic”
printer. This means OSC-8 open/close calls must now “switch” the ASCII
printer.
Note that the “fast” printer still needs to *erase* pre-existing OSC-8
URIs.
Closes #816
2021-11-25 19:22:52 +01:00
|
|
|
void grid_row_uri_range_put(
|
|
|
|
|
struct row *row, int col, const char *uri, uint64_t id);
|
2021-11-20 13:42:07 +01:00
|
|
|
void grid_row_uri_range_add(struct row *row, struct row_uri_range range);
|
|
|
|
|
void grid_row_uri_range_erase(struct row *row, int start, int end);
|
2021-02-14 20:50:33 +01:00
|
|
|
|
2021-05-22 20:20:45 +02:00
|
|
|
static inline void
|
|
|
|
|
grid_row_uri_range_destroy(struct row_uri_range *range)
|
|
|
|
|
{
|
|
|
|
|
free(range->uri);
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-14 20:32:38 +01:00
|
|
|
static inline void
|
|
|
|
|
grid_row_reset_extra(struct row *row)
|
|
|
|
|
{
|
2021-11-26 19:55:27 +01:00
|
|
|
struct row_data *extra = row->extra;
|
|
|
|
|
|
|
|
|
|
if (likely(extra == NULL))
|
2021-02-14 20:32:38 +01:00
|
|
|
return;
|
|
|
|
|
|
2021-11-26 19:55:27 +01:00
|
|
|
for (size_t i = 0; i < extra->uri_ranges.count; i++)
|
|
|
|
|
grid_row_uri_range_destroy(&extra->uri_ranges.v[i]);
|
|
|
|
|
free(extra->uri_ranges.v);
|
2021-02-14 20:32:38 +01:00
|
|
|
|
2021-11-26 19:55:27 +01:00
|
|
|
free(extra);
|
2021-02-14 20:32:38 +01:00
|
|
|
row->extra = NULL;
|
|
|
|
|
}
|