foot/grid.h

139 lines
3.8 KiB
C
Raw Normal View History

#pragma once
#include <stddef.h>
#include "debug.h"
#include "terminal.h"
struct grid *grid_snapshot(const struct grid *grid);
2021-02-22 10:20:52 +01:00
void grid_free(struct grid *grid);
void grid_swap_row(struct grid *grid, int row_a, int row_b);
struct row *grid_row_alloc(int cols, bool initialize);
void grid_row_free(struct row *row);
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(
struct grid *grid, const struct terminal *term, int new_rows, int new_cols,
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]);
/* 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);
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);
}
static inline struct row *
_grid_row_maybe_alloc(struct grid *grid, int row_no, bool alloc_if_null)
{
xassert(grid->offset >= 0);
int real_row = grid_row_absolute(grid, row_no);
struct row *row = grid->rows[real_row];
if (row == NULL && alloc_if_null) {
row = grid_row_alloc(grid->num_cols, false);
grid->rows[real_row] = row;
}
xassert(row != NULL);
return row;
}
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);
}
static inline struct row *
grid_row_in_view(struct grid *grid, int row_no)
{
xassert(grid->view >= 0);
int real_row = grid_row_absolute_in_view(grid, row_no);
struct row *row = grid->rows[real_row];
xassert(row != NULL);
return row;
}
void grid_row_uri_range_put(
struct row *row, int col, const char *uri, uint64_t id);
void grid_row_uri_range_erase(struct row *row, int start, int end);
void grid_row_underline_range_put(
struct row *row, int col, struct underline_range_data data);
void grid_row_underline_range_erase(struct row *row, int start, int end);
2021-05-22 20:20:45 +02:00
static inline void
grid_row_uri_range_destroy(struct row_range *range)
2021-05-22 20:20:45 +02:00
{
free(range->uri.uri);
}
static inline void
grid_row_underline_range_destroy(struct row_range *range)
{
}
static inline void
grid_row_range_destroy(struct row_range *range, enum row_range_type type)
{
switch (type) {
case ROW_RANGE_URI: grid_row_uri_range_destroy(range); break;
case ROW_RANGE_UNDERLINE: grid_row_underline_range_destroy(range); break;
}
}
static inline void
grid_row_ranges_destroy(struct row_ranges *ranges, enum row_range_type type)
{
for (int i = 0; i < ranges->count; i++) {
grid_row_range_destroy(&ranges->v[i], type);
}
2021-05-22 20:20:45 +02:00
}
static inline void
grid_row_reset_extra(struct row *row)
{
struct row_data *extra = row->extra;
if (likely(extra == NULL))
return;
grid_row_ranges_destroy(&extra->uri_ranges, ROW_RANGE_URI);
grid_row_ranges_destroy(&extra->underline_ranges, ROW_RANGE_UNDERLINE);
free(extra->uri_ranges.v);
free(extra->underline_ranges.v);
free(extra);
row->extra = NULL;
}