mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-08 10:06:22 -05:00
Merge branch 'master' into releases/1.8
This commit is contained in:
commit
4d82e04860
19 changed files with 1088 additions and 457 deletions
55
CHANGELOG.md
55
CHANGELOG.md
|
|
@ -1,5 +1,6 @@
|
|||
# Changelog
|
||||
|
||||
* [Unreleased](#unreleased)
|
||||
* [1.8.1](#1-8-1)
|
||||
* [1.8.0](#1-8-0)
|
||||
* [1.7.2](#1-7-2)
|
||||
|
|
@ -27,6 +28,58 @@
|
|||
* [1.2.0](#1-2-0)
|
||||
|
||||
|
||||
## Unreleased
|
||||
### Added
|
||||
|
||||
* `locked-title=no|yes` to `foot.ini`
|
||||
(https://codeberg.org/dnkl/foot/issues/386).
|
||||
* `tweak.overflowing-glyphs` option, which can be enabled to fix rendering
|
||||
issues with glyphs of any width that appear cut-off
|
||||
(https://codeberg.org/dnkl/foot/issues/592).
|
||||
|
||||
|
||||
### Changed
|
||||
|
||||
* Non-empty lines are now considered to have a hard linebreak,
|
||||
_unless_ an actual word-wrap is inserted.
|
||||
* Setting `DECSDM` now _disables_ sixel scrolling, while resetting it
|
||||
_enables_ scrolling (https://codeberg.org/dnkl/foot/issues/631).
|
||||
|
||||
|
||||
### Deprecated
|
||||
### Removed
|
||||
|
||||
* The `tweak.allow-overflowing-double-width-glyphs` and
|
||||
`tweak.pua-double-width` options (which have been superseded by
|
||||
`tweak.overflowing-glyphs`).
|
||||
|
||||
|
||||
### Fixed
|
||||
|
||||
* FD exhaustion when repeatedly entering/exiting URL mode with many
|
||||
URLs.
|
||||
* Double free of URL while removing duplicated and/or overlapping URLs
|
||||
in URL mode (https://codeberg.org/dnkl/foot/issues/627).
|
||||
* Crash when an unclosed OSC-8 URL ran into un-allocated scrollback
|
||||
rows.
|
||||
* Some box-drawing characters were rendered incorrectly on big-endian
|
||||
architectures.
|
||||
* Crash when resizing the window to the smallest possible size while
|
||||
scrollback search is active.
|
||||
* Scrollback indicator being incorrectly rendered when window size is
|
||||
very small.
|
||||
* Reduced memory usage in URL mode.
|
||||
* Crash when the `E3` escape (`\E[3J`) was executed, and there was a
|
||||
selection, or sixel image, in the scrollback
|
||||
(https://codeberg.org/dnkl/foot/issues/633).
|
||||
|
||||
|
||||
### Security
|
||||
### Contributors
|
||||
|
||||
* [clktmr](https://codeberg.org/clktmr)
|
||||
|
||||
|
||||
## 1.8.1
|
||||
|
||||
### Added
|
||||
|
|
@ -43,7 +96,7 @@
|
|||
* Grapheme cluster width is now limited to two cells by default. This
|
||||
may cause cursor synchronization issues with many applications. You
|
||||
can set `[tweak].grapheme-width-method=wcswidth` to revert to the
|
||||
behavior from foot-1.8.0.
|
||||
behavior in foot-1.8.0.
|
||||
|
||||
|
||||
### Fixed
|
||||
|
|
|
|||
|
|
@ -1244,6 +1244,16 @@ draw_box_drawings_double_vertical_and_horizontal(struct buf *buf)
|
|||
vline(hmid + 2 * thick, buf->height, vmid + 2 * thick, thick);
|
||||
}
|
||||
|
||||
static inline void
|
||||
set_a1_bit(uint8_t *data, size_t ofs, size_t bit_no)
|
||||
{
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
data[ofs] |= 1 << bit_no;
|
||||
#else
|
||||
data[ofs] |= 1 << (7 - bit_no);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
draw_box_drawings_light_arc(struct buf *buf, wchar_t wc)
|
||||
{
|
||||
|
|
@ -1354,7 +1364,7 @@ draw_box_drawings_light_arc(struct buf *buf, wchar_t wc)
|
|||
if (fmt == PIXMAN_a1) {
|
||||
size_t idx = c / 8;
|
||||
size_t bit_no = c % 8;
|
||||
data[r * stride + idx] |= 1 << bit_no;
|
||||
set_a1_bit(data, r * stride + idx, bit_no);
|
||||
} else
|
||||
data[r * stride + c] = 0xff;
|
||||
}
|
||||
|
|
@ -1376,7 +1386,7 @@ draw_box_drawings_light_arc(struct buf *buf, wchar_t wc)
|
|||
if (fmt == PIXMAN_a1) {
|
||||
size_t ofs = col / 8;
|
||||
size_t bit_no = col % 8;
|
||||
data[row * stride + ofs] |= 1 << bit_no;
|
||||
set_a1_bit(data, row * stride + ofs, bit_no);
|
||||
} else
|
||||
data[row * stride + col] = 0xff;
|
||||
}
|
||||
|
|
@ -1392,7 +1402,7 @@ draw_box_drawings_light_arc(struct buf *buf, wchar_t wc)
|
|||
if (fmt == PIXMAN_a1) {
|
||||
size_t ofs = col / 8;
|
||||
size_t bit_no = col % 8;
|
||||
data[row * stride + ofs] |= 1 << bit_no;
|
||||
set_a1_bit(data, row * stride + ofs, bit_no);
|
||||
} else
|
||||
data[row * stride + col] = 0xff;
|
||||
}
|
||||
|
|
@ -1767,7 +1777,7 @@ draw_light_shade(struct buf *buf)
|
|||
for (size_t col = 0; col < buf->width; col += 2) {
|
||||
size_t idx = col / 8;
|
||||
size_t bit_no = col % 8;
|
||||
buf->data[row * buf->stride + idx] |= 1 << bit_no;
|
||||
set_a1_bit(buf->data, row * buf->stride + idx, bit_no);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1790,7 +1800,7 @@ draw_medium_shade(struct buf *buf)
|
|||
for (size_t col = row % 2; col < buf->width; col += 2) {
|
||||
size_t idx = col / 8;
|
||||
size_t bit_no = col % 8;
|
||||
buf->data[row * buf->stride + idx] |= 1 << bit_no;
|
||||
set_a1_bit(buf->data, row * buf->stride + idx, bit_no);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1813,7 +1823,7 @@ draw_dark_shade(struct buf *buf)
|
|||
for (size_t col = 0; col < buf->width; col += 1 + row % 2) {
|
||||
size_t idx = col / 8;
|
||||
size_t bit_no = col % 8;
|
||||
buf->data[row * buf->stride + idx] |= 1 << bit_no;
|
||||
set_a1_bit(buf->data, row * buf->stride + idx, bit_no);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
20
config.c
20
config.c
|
|
@ -643,6 +643,9 @@ parse_section_main(const char *key, const char *value, struct config *conf,
|
|||
conf->title = xstrdup(value);
|
||||
}
|
||||
|
||||
else if (strcmp(key, "locked-title") == 0)
|
||||
conf->locked_title = str_to_bool(value);
|
||||
|
||||
else if (strcmp(key, "app-id") == 0) {
|
||||
free(conf->app_id);
|
||||
conf->app_id = xstrdup(value);
|
||||
|
|
@ -2210,16 +2213,10 @@ parse_section_tweak(
|
|||
return false;
|
||||
}
|
||||
|
||||
else if (strcmp(key, "allow-overflowing-double-width-glyphs") == 0) {
|
||||
conf->tweak.allow_overflowing_double_width_glyphs = str_to_bool(value);
|
||||
if (!conf->tweak.allow_overflowing_double_width_glyphs)
|
||||
LOG_WARN("tweak: disabled overflowing double-width glyphs");
|
||||
}
|
||||
|
||||
else if (strcmp(key, "pua-double-width") == 0) {
|
||||
conf->tweak.pua_double_width = str_to_bool(value);
|
||||
if (conf->tweak.pua_double_width)
|
||||
LOG_WARN("tweak: PUA double width glyphs enabled");
|
||||
else if (strcmp(key, "overflowing-glyphs") == 0) {
|
||||
conf->tweak.overflowing_glyphs = str_to_bool(value);
|
||||
if (!conf->tweak.overflowing_glyphs)
|
||||
LOG_WARN("tweak: disabled overflowing glyphs");
|
||||
}
|
||||
|
||||
else if (strcmp(key, "damage-whole-window") == 0) {
|
||||
|
|
@ -2830,7 +2827,7 @@ config_load(struct config *conf, const char *conf_path,
|
|||
|
||||
.tweak = {
|
||||
.fcft_filter = FCFT_SCALING_FILTER_LANCZOS3,
|
||||
.allow_overflowing_double_width_glyphs = true,
|
||||
.overflowing_glyphs = true,
|
||||
.grapheme_shaping = false,
|
||||
.grapheme_width_method = GRAPHEME_WIDTH_DOUBLE,
|
||||
.delayed_render_lower_ns = 500000, /* 0.5ms */
|
||||
|
|
@ -2841,7 +2838,6 @@ config_load(struct config *conf, const char *conf_path,
|
|||
.damage_whole_window = false,
|
||||
.box_drawing_base_thickness = 0.04,
|
||||
.box_drawing_solid_shades = true,
|
||||
.pua_double_width = false,
|
||||
},
|
||||
|
||||
.notifications = tll_init(),
|
||||
|
|
|
|||
4
config.h
4
config.h
|
|
@ -77,6 +77,7 @@ struct config {
|
|||
wchar_t *word_delimiters;
|
||||
bool login_shell;
|
||||
bool no_wait;
|
||||
bool locked_title;
|
||||
|
||||
struct {
|
||||
enum conf_size_type type;
|
||||
|
|
@ -244,7 +245,7 @@ struct config {
|
|||
|
||||
struct {
|
||||
enum fcft_scaling_filter fcft_filter;
|
||||
bool allow_overflowing_double_width_glyphs;
|
||||
bool overflowing_glyphs;
|
||||
bool grapheme_shaping;
|
||||
enum {GRAPHEME_WIDTH_WCSWIDTH, GRAPHEME_WIDTH_DOUBLE} grapheme_width_method;
|
||||
bool render_timer_osd;
|
||||
|
|
@ -255,7 +256,6 @@ struct config {
|
|||
off_t max_shm_pool_size;
|
||||
float box_drawing_base_thickness;
|
||||
bool box_drawing_solid_shades;
|
||||
bool pua_double_width;
|
||||
} tweak;
|
||||
|
||||
user_notifications_t notifications;
|
||||
|
|
|
|||
29
csi.c
29
csi.c
|
|
@ -402,7 +402,7 @@ decset_decrst(struct terminal *term, unsigned param, bool enable)
|
|||
break;
|
||||
|
||||
case 80:
|
||||
term->sixel.scrolling = enable;
|
||||
term->sixel.scrolling = !enable;
|
||||
break;
|
||||
|
||||
case 1000:
|
||||
|
|
@ -611,7 +611,7 @@ decrqm(const struct terminal *term, unsigned param, bool *enabled)
|
|||
case 12: *enabled = term->cursor_blink.decset; return true;
|
||||
case 25: *enabled = !term->hide_cursor; return true;
|
||||
case 45: *enabled = term->reverse_wrap; return true;
|
||||
case 80: *enabled = term->sixel.scrolling; return true;
|
||||
case 80: *enabled = !term->sixel.scrolling; return true;
|
||||
case 1000: *enabled = term->mouse_tracking == MOUSE_CLICK; return true;
|
||||
case 1001: *enabled = false; return true;
|
||||
case 1002: *enabled = term->mouse_tracking == MOUSE_DRAG; return true;
|
||||
|
|
@ -654,7 +654,7 @@ xtsave(struct terminal *term, unsigned param)
|
|||
case 25: term->xtsave.show_cursor = !term->hide_cursor; break;
|
||||
case 45: term->xtsave.reverse_wrap = term->reverse_wrap; break;
|
||||
case 47: term->xtsave.alt_screen = term->grid == &term->alt; break;
|
||||
case 80: term->xtsave.sixel_scrolling = term->sixel.scrolling; break;
|
||||
case 80: term->xtsave.sixel_display_mode = !term->sixel.scrolling; break;
|
||||
case 1000: term->xtsave.mouse_click = term->mouse_tracking == MOUSE_CLICK; break;
|
||||
case 1001: break;
|
||||
case 1002: term->xtsave.mouse_drag = term->mouse_tracking == MOUSE_DRAG; break;
|
||||
|
|
@ -696,7 +696,7 @@ xtrestore(struct terminal *term, unsigned param)
|
|||
case 25: enable = term->xtsave.show_cursor; break;
|
||||
case 45: enable = term->xtsave.reverse_wrap; break;
|
||||
case 47: enable = term->xtsave.alt_screen; break;
|
||||
case 80: enable = term->xtsave.sixel_scrolling; break;
|
||||
case 80: enable = term->xtsave.sixel_display_mode; break;
|
||||
case 1000: enable = term->xtsave.mouse_click; break;
|
||||
case 1001: return;
|
||||
case 1002: enable = term->xtsave.mouse_drag; break;
|
||||
|
|
@ -917,26 +917,7 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
|||
|
||||
case 3: {
|
||||
/* Erase scrollback */
|
||||
int end = (term->grid->offset + term->rows - 1) % term->grid->num_rows;
|
||||
for (size_t i = 0; i < term->grid->num_rows; i++) {
|
||||
if (end >= term->grid->offset) {
|
||||
/* Not wrapped */
|
||||
if (i >= term->grid->offset && i <= end)
|
||||
continue;
|
||||
} else {
|
||||
/* Wrapped */
|
||||
if (i >= term->grid->offset || i <= end)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (term->render.last_cursor.row == term->grid->rows[i])
|
||||
term->render.last_cursor.row = NULL;
|
||||
|
||||
grid_row_free(term->grid->rows[i]);
|
||||
term->grid->rows[i] = NULL;
|
||||
}
|
||||
term->grid->view = term->grid->offset;
|
||||
term_damage_view(term);
|
||||
term_erase_scrollback(term);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -204,7 +204,7 @@ Note that these are just the defaults; they can be changed in
|
|||
characters are whitespace characters.
|
||||
|
||||
*ctrl*+*v*, *ctrl*+*y*
|
||||
Paste from clipboard into the searh buffer.
|
||||
Paste from clipboard into the search buffer.
|
||||
|
||||
*shift*+*insert*
|
||||
Paste from primary selection into the search buffer.
|
||||
|
|
|
|||
|
|
@ -188,8 +188,8 @@ in this order:
|
|||
|
||||
In other words, while you are fiddling with the window size, foot
|
||||
does not send the updated dimensions to the client. Only when you
|
||||
pause the fiddling for *relay-size-ms* milliseconds is the client
|
||||
updated.
|
||||
pause the fiddling for *resize-delay-ms* milliseconds is the
|
||||
client updated.
|
||||
|
||||
Emphasis is on _while_ here; as soon as the interactive resize
|
||||
ends (i.e. when you let go of the window border), the final
|
||||
|
|
@ -224,6 +224,10 @@ in this order:
|
|||
*title*
|
||||
Initial window title. Default: _foot_.
|
||||
|
||||
*locked-title*
|
||||
Boolean. If enabled, applications are not allowed to change the
|
||||
title at run-time. Default: _no_.
|
||||
|
||||
*app-id*
|
||||
Value to set the *app-id* property on the Wayland window to. The
|
||||
compositor can use this value to e.g. group multiple windows, or
|
||||
|
|
@ -843,34 +847,24 @@ any of these options.
|
|||
|
||||
Default: _lanczos3_.
|
||||
|
||||
*allow-overflowing-double-width-glyphs*
|
||||
Boolean. When enabled, double width glyphs with a character width
|
||||
of 1 are allowed to overflow into the neighbouring cell.
|
||||
*overflowing-glyphs*
|
||||
Boolean. When enabled, glyphs wider than their cell(s) are allowed
|
||||
to render into one additional neighbouring cell.
|
||||
|
||||
One use case for this is fonts "icon" characters in the Unicode
|
||||
private usage area, e.g. Nerd Fonts, or Powerline Fonts. Without
|
||||
this option, such glyphs will appear "cut off".
|
||||
One use case for this are fonts with wide italic characters that
|
||||
"bend" into the next cell. Without this option, such glyphs will
|
||||
appear "cut off".
|
||||
|
||||
Another use case are legacy emoji characters like *WHITE FROWNING
|
||||
FACE*.
|
||||
Another use case are fonts with "icon" characters in the Unicode
|
||||
private usage area, e.g. Nerd Fonts, or Powerline Fonts and legacy
|
||||
emoji characters like *WHITE FROWNING FACE*.
|
||||
|
||||
Note: this feature uses _heuristics_ to determine *which* glyphs
|
||||
should be allowed to overflow.
|
||||
|
||||
See also: *pua-double-width*
|
||||
Note: might impact performance depending on the font used.
|
||||
Especially small font sizes can cause many overflowing glyphs
|
||||
because of subpixel rendering.
|
||||
|
||||
Default: _yes_.
|
||||
|
||||
*pua-double-width*
|
||||
Boolean. When enabled, Unicode code points from the private usage
|
||||
area (PUA) are always considered to be double width, regardless of
|
||||
the actual glyph width.
|
||||
|
||||
Ignored if *allow-overflowing-double-width-glyphs* has been
|
||||
disabled.
|
||||
|
||||
Default: _no_.
|
||||
|
||||
*render-timer*
|
||||
Enables a frame rendering timer, that prints the time it takes to
|
||||
render each frame, in microseconds, either on-screen, to stderr,
|
||||
|
|
|
|||
8
foot.ini
8
foot.ini
|
|
@ -4,6 +4,10 @@
|
|||
# term=foot (or xterm-256color if built with -Dterminfo=disabled)
|
||||
# login-shell=no
|
||||
|
||||
# app-id=foot
|
||||
# title=foot
|
||||
# locked-title=no
|
||||
|
||||
# font=monospace:size=8
|
||||
# font-bold=<bold variant of regular font>
|
||||
# font-italic=<italic variant of regular font>
|
||||
|
|
@ -122,7 +126,7 @@
|
|||
# show-urls-copy=none
|
||||
|
||||
[search-bindings]
|
||||
# cancel=Control+g Escape
|
||||
# cancel=Control+g Control+c Escape
|
||||
# commit=Return
|
||||
# find-prev=Control+r
|
||||
# find-next=Control+s
|
||||
|
|
@ -142,7 +146,7 @@
|
|||
# primary-paste=Shift+Insert
|
||||
|
||||
[url-bindings]
|
||||
# cancel=Control+g Control+d Escape
|
||||
# cancel=Control+g Control+c Control+d Escape
|
||||
# toggle-url-visible=t
|
||||
|
||||
[mouse-bindings]
|
||||
|
|
|
|||
339
render.c
339
render.c
|
|
@ -437,6 +437,20 @@ draw_cursor(const struct terminal *term, const struct cell *cell,
|
|||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
render_cell_prepass(struct terminal *term, struct row *row, int col)
|
||||
{
|
||||
for (; col < term->cols - 1; col++) {
|
||||
if (row->cells[col].attrs.confined ||
|
||||
(row->cells[col].attrs.clean == row->cells[col + 1].attrs.clean)) {
|
||||
break;
|
||||
}
|
||||
|
||||
row->cells[col].attrs.clean = 0;
|
||||
row->cells[col + 1].attrs.clean = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
render_cell(struct terminal *term, pixman_image_t *pix,
|
||||
struct row *row, int col, int row_no, bool has_cursor)
|
||||
|
|
@ -446,6 +460,7 @@ render_cell(struct terminal *term, pixman_image_t *pix,
|
|||
return 0;
|
||||
|
||||
cell->attrs.clean = 1;
|
||||
cell->attrs.confined = true;
|
||||
|
||||
int width = term->cell_width;
|
||||
int height = term->cell_height;
|
||||
|
|
@ -458,7 +473,7 @@ render_cell(struct terminal *term, pixman_image_t *pix,
|
|||
uint32_t _fg = 0;
|
||||
uint32_t _bg = 0;
|
||||
|
||||
bool apply_alpha = false;
|
||||
uint16_t alpha = 0xffff;
|
||||
|
||||
if (is_selected && term->colors.use_custom_selection) {
|
||||
_fg = term->colors.selection_fg;
|
||||
|
|
@ -472,14 +487,14 @@ render_cell(struct terminal *term, pixman_image_t *pix,
|
|||
uint32_t swap = _fg;
|
||||
_fg = _bg;
|
||||
_bg = swap;
|
||||
} else
|
||||
apply_alpha = !cell->attrs.have_bg;
|
||||
} else if (!cell->attrs.have_bg)
|
||||
alpha = term->colors.alpha;
|
||||
}
|
||||
|
||||
if (unlikely(is_selected && _fg == _bg)) {
|
||||
/* Invert bg when selected/highlighted text has same fg/bg */
|
||||
_bg = ~_bg;
|
||||
apply_alpha = false;
|
||||
alpha = 0xffff;
|
||||
}
|
||||
|
||||
if (cell->attrs.dim)
|
||||
|
|
@ -491,8 +506,7 @@ render_cell(struct terminal *term, pixman_image_t *pix,
|
|||
_fg = color_dim(_fg);
|
||||
|
||||
pixman_color_t fg = color_hex_to_pixman(_fg);
|
||||
pixman_color_t bg = color_hex_to_pixman_with_alpha(
|
||||
_bg, apply_alpha ? term->colors.alpha : 0xffff);
|
||||
pixman_color_t bg = color_hex_to_pixman_with_alpha(_bg, alpha);
|
||||
|
||||
if (term->is_searching && !is_selected) {
|
||||
color_dim_for_search(&fg);
|
||||
|
|
@ -598,51 +612,32 @@ render_cell(struct terminal *term, pixman_image_t *pix,
|
|||
cell_cols = max(1, min(cell_cols, cols_left));
|
||||
|
||||
/*
|
||||
* Hack!
|
||||
*
|
||||
* Deal with double-width glyphs for which wcwidth() returns
|
||||
* 1. Typically Unicode private usage area characters,
|
||||
* e.g. powerline, or nerd hack fonts.
|
||||
*
|
||||
* Users can enable a tweak option that lets this glyphs
|
||||
* overflow/bleed into the neighbouring cell.
|
||||
*
|
||||
* We only apply this workaround if:
|
||||
* - the user has explicitly enabled this feature
|
||||
* - the *character* width is 1
|
||||
* - the *glyph* width is at least 1.5 cells
|
||||
* - the *glyph* width is less than 3 cells
|
||||
* - *this* column isn’t the last column
|
||||
* - *this* cells is followed by an empty cell, or a space
|
||||
* Determine cells that will bleed into their right neighbor and remember
|
||||
* them for cleanup in the next frame.
|
||||
*/
|
||||
if (term->conf->tweak.allow_overflowing_double_width_glyphs &&
|
||||
((glyph_count > 0 &&
|
||||
cell_cols == 1 &&
|
||||
glyphs[0]->width >= term->cell_width * 15 / 10 &&
|
||||
glyphs[0]->width < 3 * term->cell_width &&
|
||||
col < term->cols - 1) ||
|
||||
(term->conf->tweak.pua_double_width &&
|
||||
((base >= 0x00e000 && base <= 0x00f8ff) ||
|
||||
(base >= 0x0f0000 && base <= 0x0ffffd) ||
|
||||
(base >= 0x100000 && base <= 0x10fffd)))) &&
|
||||
(row->cells[col + 1].wc == 0 || row->cells[col + 1].wc == L' '))
|
||||
int render_width = cell_cols * width;
|
||||
if (term->conf->tweak.overflowing_glyphs &&
|
||||
glyph_count > 0)
|
||||
{
|
||||
cell_cols = 2;
|
||||
int glyph_width = 0, advance = 0;
|
||||
for (size_t i = 0; i < glyph_count; i++) {
|
||||
glyph_width = max(glyph_width,
|
||||
advance + glyphs[i]->x + glyphs[i]->width);
|
||||
advance += glyphs[i]->advance.x;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure the cell we’re overflowing into gets re-rendered, to
|
||||
* ensure it is erased if *this* cell is erased. Note that we
|
||||
* do *not* mark the row as dirty - we don’t need to re-render
|
||||
* the cell if nothing else on the row has changed.
|
||||
*/
|
||||
row->cells[col].attrs.clean = 0;
|
||||
row->cells[col + 1].attrs.clean = 0;
|
||||
if (glyph_width > render_width) {
|
||||
render_width = min(glyph_width, render_width + width);
|
||||
|
||||
for (int i = 0; i < cell_cols; i++)
|
||||
row->cells[col + i].attrs.confined = false;
|
||||
}
|
||||
}
|
||||
|
||||
pixman_region32_t clip;
|
||||
pixman_region32_init_rect(
|
||||
&clip, x, y,
|
||||
cell_cols * term->cell_width, term->cell_height);
|
||||
render_width, term->cell_height);
|
||||
pixman_image_set_clip_region32(pix, &clip);
|
||||
|
||||
/* Background */
|
||||
|
|
@ -772,6 +767,10 @@ static void
|
|||
render_row(struct terminal *term, pixman_image_t *pix, struct row *row,
|
||||
int row_no, int cursor_col)
|
||||
{
|
||||
if (term->conf->tweak.overflowing_glyphs)
|
||||
for (int col = term->cols - 1; col >= 0; col--)
|
||||
render_cell_prepass(term, row, col);
|
||||
|
||||
for (int col = term->cols - 1; col >= 0; col--)
|
||||
render_cell(term, pix, row, col, row_no, cursor_col == col);
|
||||
}
|
||||
|
|
@ -816,13 +815,10 @@ render_margin(struct terminal *term, struct buffer *buf,
|
|||
const int line_count = end_line - start_line;
|
||||
|
||||
uint32_t _bg = !term->reverse ? term->colors.bg : term->colors.fg;
|
||||
if (term->is_searching)
|
||||
_bg = color_dim(_bg);
|
||||
pixman_color_t bg = color_hex_to_pixman_with_alpha(_bg, term->colors.alpha);
|
||||
|
||||
pixman_color_t bg = color_hex_to_pixman_with_alpha(
|
||||
_bg,
|
||||
(_bg == (term->reverse ? term->colors.fg : term->colors.bg)
|
||||
? term->colors.alpha : 0xffff));
|
||||
if (term->is_searching)
|
||||
color_dim_for_search(&bg);
|
||||
|
||||
pixman_image_fill_rectangles(
|
||||
PIXMAN_OP_SRC, buf->pix[0], &bg, 4,
|
||||
|
|
@ -947,7 +943,7 @@ grid_render_scroll(struct terminal *term, struct buffer *buf,
|
|||
|
||||
if (try_shm_scroll) {
|
||||
did_shm_scroll = shm_scroll(
|
||||
term->wl->shm, buf, dmg->lines * term->cell_height,
|
||||
buf, dmg->lines * term->cell_height,
|
||||
term->margins.top, dmg->region.start * term->cell_height,
|
||||
term->margins.bottom, (term->rows - dmg->region.end) * term->cell_height);
|
||||
}
|
||||
|
|
@ -958,7 +954,7 @@ grid_render_scroll(struct terminal *term, struct buffer *buf,
|
|||
term, buf, dmg->region.end - dmg->lines, term->rows, false);
|
||||
} else {
|
||||
/* Fallback for when we either cannot do SHM scrolling, or it failed */
|
||||
uint8_t *raw = buf->mmapped;
|
||||
uint8_t *raw = buf->data;
|
||||
memmove(raw + dst_y * buf->stride,
|
||||
raw + src_y * buf->stride,
|
||||
height * buf->stride);
|
||||
|
|
@ -1012,7 +1008,7 @@ grid_render_scroll_reverse(struct terminal *term, struct buffer *buf,
|
|||
|
||||
if (try_shm_scroll) {
|
||||
did_shm_scroll = shm_scroll(
|
||||
term->wl->shm, buf, -dmg->lines * term->cell_height,
|
||||
buf, -dmg->lines * term->cell_height,
|
||||
term->margins.top, dmg->region.start * term->cell_height,
|
||||
term->margins.bottom, (term->rows - dmg->region.end) * term->cell_height);
|
||||
}
|
||||
|
|
@ -1023,7 +1019,7 @@ grid_render_scroll_reverse(struct terminal *term, struct buffer *buf,
|
|||
term, buf, dmg->region.start, dmg->region.start + dmg->lines, false);
|
||||
} else {
|
||||
/* Fallback for when we either cannot do SHM scrolling, or it failed */
|
||||
uint8_t *raw = buf->mmapped;
|
||||
uint8_t *raw = buf->data;
|
||||
memmove(raw + dst_y * buf->stride,
|
||||
raw + src_y * buf->stride,
|
||||
height * buf->stride);
|
||||
|
|
@ -1196,8 +1192,10 @@ render_sixel(struct terminal *term, pixman_image_t *pix,
|
|||
(last_col_needs_erase && last_col))
|
||||
{
|
||||
render_cell(term, pix, row, col, term_row_no, cursor_col == col);
|
||||
} else
|
||||
} else {
|
||||
cell->attrs.clean = 1;
|
||||
cell->attrs.confined = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1577,21 +1575,16 @@ render_csd_part(struct terminal *term,
|
|||
}
|
||||
|
||||
static void
|
||||
render_csd_title(struct terminal *term)
|
||||
render_csd_title(struct terminal *term, const struct csd_data *info,
|
||||
struct buffer *buf)
|
||||
{
|
||||
xassert(term->window->csd_mode == CSD_YES);
|
||||
|
||||
struct csd_data info = get_csd_data(term, CSD_SURF_TITLE);
|
||||
struct wl_surface *surf = term->window->csd.surface[CSD_SURF_TITLE].surf;
|
||||
xassert(info->width > 0 && info->height > 0);
|
||||
|
||||
xassert(info.width > 0 && info.height > 0);
|
||||
|
||||
xassert(info.width % term->scale == 0);
|
||||
xassert(info.height % term->scale == 0);
|
||||
|
||||
unsigned long cookie = shm_cookie_csd(term, CSD_SURF_TITLE);
|
||||
struct buffer *buf = shm_get_buffer(
|
||||
term->wl->shm, info.width, info.height, cookie, false, 1);
|
||||
xassert(info->width % term->scale == 0);
|
||||
xassert(info->height % term->scale == 0);
|
||||
|
||||
uint32_t _color = term->conf->colors.fg;
|
||||
uint16_t alpha = 0xffff;
|
||||
|
|
@ -1605,31 +1598,27 @@ render_csd_title(struct terminal *term)
|
|||
_color = color_dim(_color);
|
||||
|
||||
pixman_color_t color = color_hex_to_pixman_with_alpha(_color, alpha);
|
||||
render_csd_part(term, surf, buf, info.width, info.height, &color);
|
||||
render_csd_part(term, surf, buf, info->width, info->height, &color);
|
||||
csd_commit(term, surf, buf);
|
||||
}
|
||||
|
||||
static void
|
||||
render_csd_border(struct terminal *term, enum csd_surface surf_idx)
|
||||
render_csd_border(struct terminal *term, enum csd_surface surf_idx,
|
||||
const struct csd_data *info, struct buffer *buf)
|
||||
{
|
||||
xassert(term->window->csd_mode == CSD_YES);
|
||||
xassert(surf_idx >= CSD_SURF_LEFT && surf_idx <= CSD_SURF_BOTTOM);
|
||||
|
||||
struct csd_data info = get_csd_data(term, surf_idx);
|
||||
struct wl_surface *surf = term->window->csd.surface[surf_idx].surf;
|
||||
|
||||
if (info.width == 0 || info.height == 0)
|
||||
if (info->width == 0 || info->height == 0)
|
||||
return;
|
||||
|
||||
xassert(info.width % term->scale == 0);
|
||||
xassert(info.height % term->scale == 0);
|
||||
|
||||
unsigned long cookie = shm_cookie_csd(term, surf_idx);
|
||||
struct buffer *buf = shm_get_buffer(
|
||||
term->wl->shm, info.width, info.height, cookie, false, 1);
|
||||
xassert(info->width % term->scale == 0);
|
||||
xassert(info->height % term->scale == 0);
|
||||
|
||||
pixman_color_t color = color_hex_to_pixman_with_alpha(0, 0);
|
||||
render_csd_part(term, surf, buf, info.width, info.height, &color);
|
||||
render_csd_part(term, surf, buf, info->width, info->height, &color);
|
||||
csd_commit(term, surf, buf);
|
||||
}
|
||||
|
||||
|
|
@ -1796,23 +1785,19 @@ render_csd_button_close(struct terminal *term, struct buffer *buf)
|
|||
}
|
||||
|
||||
static void
|
||||
render_csd_button(struct terminal *term, enum csd_surface surf_idx)
|
||||
render_csd_button(struct terminal *term, enum csd_surface surf_idx,
|
||||
const struct csd_data *info, struct buffer *buf)
|
||||
{
|
||||
xassert(term->window->csd_mode == CSD_YES);
|
||||
xassert(surf_idx >= CSD_SURF_MINIMIZE && surf_idx <= CSD_SURF_CLOSE);
|
||||
|
||||
struct csd_data info = get_csd_data(term, surf_idx);
|
||||
struct wl_surface *surf = term->window->csd.surface[surf_idx].surf;
|
||||
|
||||
if (info.width == 0 || info.height == 0)
|
||||
if (info->width == 0 || info->height == 0)
|
||||
return;
|
||||
|
||||
xassert(info.width % term->scale == 0);
|
||||
xassert(info.height % term->scale == 0);
|
||||
|
||||
unsigned long cookie = shm_cookie_csd(term, surf_idx);
|
||||
struct buffer *buf = shm_get_buffer(
|
||||
term->wl->shm, info.width, info.height, cookie, false, 1);
|
||||
xassert(info->width % term->scale == 0);
|
||||
xassert(info->height % term->scale == 0);
|
||||
|
||||
uint32_t _color;
|
||||
uint16_t alpha = 0xffff;
|
||||
|
|
@ -1861,7 +1846,7 @@ render_csd_button(struct terminal *term, enum csd_surface surf_idx)
|
|||
_color = color_dim(_color);
|
||||
|
||||
pixman_color_t color = color_hex_to_pixman_with_alpha(_color, alpha);
|
||||
render_csd_part(term, surf, buf, info.width, info.height, &color);
|
||||
render_csd_part(term, surf, buf, info->width, info->height, &color);
|
||||
|
||||
switch (surf_idx) {
|
||||
case CSD_SURF_MINIMIZE: render_csd_button_minimize(term, buf); break;
|
||||
|
|
@ -1885,12 +1870,16 @@ render_csd(struct terminal *term)
|
|||
if (term->window->is_fullscreen)
|
||||
return;
|
||||
|
||||
struct csd_data infos[CSD_SURF_COUNT];
|
||||
int widths[CSD_SURF_COUNT];
|
||||
int heights[CSD_SURF_COUNT];
|
||||
|
||||
for (size_t i = 0; i < CSD_SURF_COUNT; i++) {
|
||||
struct csd_data info = get_csd_data(term, i);
|
||||
const int x = info.x;
|
||||
const int y = info.y;
|
||||
const int width = info.width;
|
||||
const int height = info.height;
|
||||
infos[i] = get_csd_data(term, i);
|
||||
const int x = infos[i].x;
|
||||
const int y = infos[i].y;
|
||||
const int width = infos[i].width;
|
||||
const int height = infos[i].height;
|
||||
|
||||
struct wl_surface *surf = term->window->csd.surface[i].surf;
|
||||
struct wl_subsurface *sub = term->window->csd.surface[i].sub;
|
||||
|
|
@ -1899,20 +1888,27 @@ render_csd(struct terminal *term)
|
|||
xassert(sub != NULL);
|
||||
|
||||
if (width == 0 || height == 0) {
|
||||
widths[i] = heights[i] = 0;
|
||||
wl_subsurface_set_position(sub, 0, 0);
|
||||
wl_surface_attach(surf, NULL, 0, 0);
|
||||
wl_surface_commit(surf);
|
||||
continue;
|
||||
}
|
||||
|
||||
widths[i] = width;
|
||||
heights[i] = height;
|
||||
|
||||
wl_subsurface_set_position(sub, x / term->scale, y / term->scale);
|
||||
}
|
||||
|
||||
struct buffer *bufs[CSD_SURF_COUNT];
|
||||
shm_get_many(term->render.chains.csd, CSD_SURF_COUNT, widths, heights, bufs);
|
||||
|
||||
for (size_t i = CSD_SURF_LEFT; i <= CSD_SURF_BOTTOM; i++)
|
||||
render_csd_border(term, i);
|
||||
render_csd_border(term, i, &infos[i], bufs[i]);
|
||||
for (size_t i = CSD_SURF_MINIMIZE; i <= CSD_SURF_CLOSE; i++)
|
||||
render_csd_button(term, i);
|
||||
render_csd_title(term);
|
||||
render_csd_button(term, i, &infos[i], bufs[i]);
|
||||
render_csd_title(term, &infos[CSD_SURF_TITLE], bufs[CSD_SURF_TITLE]);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -2051,10 +2047,6 @@ render_scrollback_position(struct terminal *term)
|
|||
const int height =
|
||||
(2 * margin + term->cell_height + scale - 1) / scale * scale;
|
||||
|
||||
unsigned long cookie = shm_cookie_scrollback_indicator(term);
|
||||
struct buffer *buf = shm_get_buffer(
|
||||
term->wl->shm, width, height, cookie, false, 1);
|
||||
|
||||
/* *Where* to render - parent relative coordinates */
|
||||
int surf_top = 0;
|
||||
switch (term->conf->scrollback.indicator.position) {
|
||||
|
|
@ -2072,18 +2064,29 @@ render_scrollback_position(struct terminal *term)
|
|||
/* Make sure we don't collide with the scrollback search box */
|
||||
lines--;
|
||||
}
|
||||
xassert(lines > 0);
|
||||
|
||||
int pixels = lines * term->cell_height - height + 2 * margin;
|
||||
lines = max(lines, 0);
|
||||
|
||||
int pixels = max(lines * term->cell_height - height + 2 * margin, 0);
|
||||
surf_top = term->cell_height - margin + (int)(percent * pixels);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const int x = (term->width - margin - width) / scale * scale;
|
||||
const int y = (term->margins.top + surf_top) / scale * scale;
|
||||
|
||||
if (y + height > term->height) {
|
||||
wl_surface_attach(win->scrollback_indicator.surf, NULL, 0, 0);
|
||||
wl_surface_commit(win->scrollback_indicator.surf);
|
||||
return;
|
||||
}
|
||||
|
||||
struct buffer_chain *chain = term->render.chains.scrollback_indicator;
|
||||
struct buffer *buf = shm_get_buffer(chain, width, height);
|
||||
|
||||
wl_subsurface_set_position(
|
||||
win->scrollback_indicator.sub,
|
||||
(term->width - margin - width) / scale,
|
||||
(term->margins.top + surf_top) / scale);
|
||||
win->scrollback_indicator.sub, x / scale, y / scale);
|
||||
|
||||
render_osd(
|
||||
term,
|
||||
|
|
@ -2092,7 +2095,6 @@ render_scrollback_position(struct terminal *term)
|
|||
buf, text,
|
||||
term->colors.table[0], term->colors.table[8 + 4],
|
||||
width - margin - wcslen(text) * term->cell_width, margin);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -2112,9 +2114,8 @@ render_render_timer(struct terminal *term, struct timeval render_time)
|
|||
const int height =
|
||||
(2 * margin + term->cell_height + scale - 1) / scale * scale;
|
||||
|
||||
unsigned long cookie = shm_cookie_render_timer(term);
|
||||
struct buffer *buf = shm_get_buffer(
|
||||
term->wl->shm, width, height, cookie, false, 1);
|
||||
struct buffer_chain *chain = term->render.chains.render_timer;
|
||||
struct buffer *buf = shm_get_buffer(chain, width, height);
|
||||
|
||||
wl_subsurface_set_position(
|
||||
win->render_timer.sub,
|
||||
|
|
@ -2157,7 +2158,7 @@ reapply_old_damage(struct terminal *term, struct buffer *new, struct buffer *old
|
|||
}
|
||||
|
||||
if (new->age > 1) {
|
||||
memcpy(new->mmapped, old->mmapped, new->size);
|
||||
memcpy(new->data, old->data, new->height * new->stride);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -2287,9 +2288,8 @@ grid_render(struct terminal *term)
|
|||
xassert(term->width > 0);
|
||||
xassert(term->height > 0);
|
||||
|
||||
unsigned long cookie = shm_cookie_grid(term);
|
||||
struct buffer *buf = shm_get_buffer(
|
||||
term->wl->shm, term->width, term->height, cookie, true, 1 + term->render.workers.count);
|
||||
struct buffer_chain *chain = term->render.chains.grid;
|
||||
struct buffer *buf = shm_get_buffer(chain, term->width, term->height);
|
||||
|
||||
/* Dirty old and current cursor cell, to ensure they’re repainted */
|
||||
dirty_old_cursor(term);
|
||||
|
|
@ -2306,7 +2306,7 @@ grid_render(struct terminal *term)
|
|||
}
|
||||
|
||||
else if (buf->age > 0) {
|
||||
LOG_DBG("buffer age: %u", buf->age);
|
||||
LOG_DBG("buffer age: %u (%p)", buf->age, (void *)buf);
|
||||
|
||||
xassert(term->render.last_buf != NULL);
|
||||
xassert(term->render.last_buf != buf);
|
||||
|
|
@ -2319,17 +2319,18 @@ grid_render(struct terminal *term)
|
|||
}
|
||||
|
||||
if (term->render.last_buf != NULL) {
|
||||
free(term->render.last_buf->scroll_damage);
|
||||
term->render.last_buf->scroll_damage = NULL;
|
||||
shm_unref(term->render.last_buf);
|
||||
term->render.last_buf = NULL;
|
||||
}
|
||||
|
||||
term->render.last_buf = buf;
|
||||
term->render.was_flashing = term->flash.active;
|
||||
term->render.was_searching = term->is_searching;
|
||||
|
||||
shm_addref(buf);
|
||||
buf->age = 0;
|
||||
|
||||
xassert(buf->scroll_damage == NULL);
|
||||
free(term->render.last_buf->scroll_damage);
|
||||
buf->scroll_damage_count = tll_length(term->grid->scroll_damage);
|
||||
buf->scroll_damage = xmalloc(
|
||||
buf->scroll_damage_count * sizeof(buf->scroll_damage[0]));
|
||||
|
|
@ -2620,8 +2621,14 @@ render_search_box(struct terminal *term)
|
|||
const size_t visible_cells = (visible_width - 2 * margin) / term->cell_width;
|
||||
size_t glyph_offset = term->render.search_glyph_offset;
|
||||
|
||||
unsigned long cookie = shm_cookie_search(term);
|
||||
struct buffer *buf = shm_get_buffer(term->wl->shm, width, height, cookie, false, 1);
|
||||
struct buffer_chain *chain = term->render.chains.search;
|
||||
struct buffer *buf = shm_get_buffer(chain, width, height);
|
||||
|
||||
pixman_region32_t clip;
|
||||
pixman_region32_init_rect(&clip, 0, 0, width, height);
|
||||
pixman_image_set_clip_region32(buf->pix[0], &clip);
|
||||
pixman_region32_fini(&clip);
|
||||
|
||||
|
||||
#define WINDOW_X(x) (margin + x)
|
||||
#define WINDOW_Y(y) (term->height - margin - height + y)
|
||||
|
|
@ -2875,6 +2882,10 @@ render_urls(struct terminal *term)
|
|||
struct wl_window *win = term->window;
|
||||
xassert(tll_length(win->urls) > 0);
|
||||
|
||||
const int scale = term->scale;
|
||||
const int x_margin = 2 * scale;
|
||||
const int y_margin = 1 * scale;
|
||||
|
||||
/* Calculate view start, counted from the *current* scrollback start */
|
||||
const int scrollback_end
|
||||
= (term->grid->offset + term->rows) & (term->grid->num_rows - 1);
|
||||
|
|
@ -2886,6 +2897,45 @@ render_urls(struct terminal *term)
|
|||
|
||||
const bool show_url = term->urls_show_uri_on_jump_label;
|
||||
|
||||
/*
|
||||
* There can potentially be a lot of URLs.
|
||||
*
|
||||
* Since each URL is a separate sub-surface, and requires its own
|
||||
* SHM buffer, we may be allocating a lot of buffers.
|
||||
*
|
||||
* SHM buffers normally have their own, private SHM buffer
|
||||
* pool. Each pool is mmapped, and thus allocates *at least*
|
||||
* 4K. Since URL labels are typically small, we end up using an
|
||||
* excessive amount of both virtual and physical memory.
|
||||
*
|
||||
* For this reason, we instead use shm_get_many(), which uses a
|
||||
* single, shared pool for all buffers.
|
||||
*
|
||||
* To be able to use it, we need to have all the *all* the buffer
|
||||
* dimensions up front.
|
||||
*
|
||||
* Thus, the first iteration through the URLs do the heavy
|
||||
* lifting: builds the label contents and calculates both its
|
||||
* position and size. But instead of rendering the label
|
||||
* immediately, we store the calculated data, and then do a second
|
||||
* pass, where we first get all our buffers, and then render to
|
||||
* them.
|
||||
*/
|
||||
|
||||
/* Positioning data + label contents */
|
||||
struct {
|
||||
const struct wl_url *url;
|
||||
wchar_t *text;
|
||||
int x;
|
||||
int y;
|
||||
} info[tll_length(win->urls)];
|
||||
|
||||
/* For shm_get_many() */
|
||||
int widths[tll_length(win->urls)];
|
||||
int heights[tll_length(win->urls)];
|
||||
|
||||
size_t render_count = 0;
|
||||
|
||||
tll_foreach(win->urls, it) {
|
||||
const struct url *url = it->item.url;
|
||||
const wchar_t *key = url->key;
|
||||
|
|
@ -2942,6 +2992,7 @@ render_urls(struct terminal *term)
|
|||
/* Maximum width of label, in pixels */
|
||||
const int max_width =
|
||||
term->width - term->margins.left - term->margins.right - x;
|
||||
const int max_cols = max_width / term->cell_width;
|
||||
|
||||
const size_t key_len = wcslen(key);
|
||||
|
||||
|
|
@ -2979,10 +3030,6 @@ render_urls(struct terminal *term)
|
|||
* Do it in a way such that we don’t cut the label in the
|
||||
* middle of a double-width character.
|
||||
*/
|
||||
const int scale = term->scale;
|
||||
const int x_margin = 2 * scale;
|
||||
const int y_margin = 1 * scale;
|
||||
const int max_cols = max_width / term->cell_width;
|
||||
|
||||
int cols = 0;
|
||||
|
||||
|
|
@ -3010,23 +3057,48 @@ render_urls(struct terminal *term)
|
|||
const int height =
|
||||
(2 * y_margin + term->cell_height + scale - 1) / scale * scale;
|
||||
|
||||
struct buffer *buf = shm_get_buffer(
|
||||
term->wl->shm, width, height, shm_cookie_url(url), false, 1);
|
||||
info[render_count].url = &it->item;
|
||||
info[render_count].text = xwcsdup(label);
|
||||
info[render_count].x = x;
|
||||
info[render_count].y = y;
|
||||
|
||||
widths[render_count] = width;
|
||||
heights[render_count] = height;
|
||||
|
||||
render_count++;
|
||||
}
|
||||
|
||||
struct buffer_chain *chain = term->render.chains.url;
|
||||
struct buffer *bufs[render_count];
|
||||
shm_get_many(chain, render_count, widths, heights, bufs);
|
||||
|
||||
uint32_t fg = term->conf->colors.use_custom.jump_label
|
||||
? term->conf->colors.jump_label.fg
|
||||
: term->colors.table[0];
|
||||
uint32_t bg = term->conf->colors.use_custom.jump_label
|
||||
? term->conf->colors.jump_label.bg
|
||||
: term->colors.table[3];
|
||||
|
||||
for (size_t i = 0; i < render_count; i++) {
|
||||
struct wl_surface *surf = info[i].url->surf.surf;
|
||||
struct wl_subsurface *sub_surf = info[i].url->surf.sub;
|
||||
|
||||
const wchar_t *label = info[i].text;
|
||||
const int x = info[i].x;
|
||||
const int y = info[i].y;
|
||||
|
||||
xassert(surf != NULL);
|
||||
xassert(sub_surf != NULL);
|
||||
|
||||
wl_subsurface_set_position(
|
||||
sub_surf,
|
||||
(term->margins.left + x) / term->scale,
|
||||
(term->margins.top + y) / term->scale);
|
||||
|
||||
uint32_t fg = term->conf->colors.use_custom.jump_label
|
||||
? term->conf->colors.jump_label.fg
|
||||
: term->colors.table[0];
|
||||
uint32_t bg = term->conf->colors.use_custom.jump_label
|
||||
? term->conf->colors.jump_label.bg
|
||||
: term->colors.table[3];
|
||||
|
||||
render_osd(
|
||||
term, surf, sub_surf, buf, label, fg, bg, x_margin, y_margin);
|
||||
term, surf, sub_surf, bufs[i], label, fg, bg, x_margin, y_margin);
|
||||
|
||||
free(info[i].text);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3119,8 +3191,10 @@ fdm_tiocswinsz(struct fdm *fdm, int fd, int events, void *data)
|
|||
if (events & EPOLLIN)
|
||||
tiocswinsz(term);
|
||||
|
||||
fdm_del(fdm, fd);
|
||||
term->window->resize_timeout_fd = -1;
|
||||
if (term->window->resize_timeout_fd >= 0) {
|
||||
fdm_del(fdm, term->window->resize_timeout_fd);
|
||||
term->window->resize_timeout_fd = -1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -3405,6 +3479,7 @@ damage_view:
|
|||
tll_free(term->normal.scroll_damage);
|
||||
tll_free(term->alt.scroll_damage);
|
||||
|
||||
shm_unref(term->render.last_buf);
|
||||
term->render.last_buf = NULL;
|
||||
term_damage_view(term);
|
||||
render_refresh_csd(term);
|
||||
|
|
|
|||
|
|
@ -69,10 +69,8 @@ selection_on_rows(const struct terminal *term, int row_start, int row_end)
|
|||
end = tmp;
|
||||
}
|
||||
|
||||
if (row_start >= start->row && row_end <= end->row) {
|
||||
LOG_INFO("ON ROWS");
|
||||
if (row_start >= start->row && row_end <= end->row)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
7
server.c
7
server.c
|
|
@ -132,13 +132,6 @@ term_shutdown_handler(void *data, int exit_code)
|
|||
{
|
||||
struct terminal_instance *instance = data;
|
||||
|
||||
struct wl_shm *shm = instance->server->wayl->shm;
|
||||
|
||||
shm_purge(shm, shm_cookie_grid(instance->terminal));
|
||||
shm_purge(shm, shm_cookie_search(instance->terminal));
|
||||
for (enum csd_surface surf = 0; surf < CSD_SURF_COUNT; surf++)
|
||||
shm_purge(shm, shm_cookie_csd(instance->terminal, surf));
|
||||
|
||||
instance->terminal = NULL;
|
||||
instance_destroy(instance, exit_code);
|
||||
}
|
||||
|
|
|
|||
622
shm.c
622
shm.c
|
|
@ -55,11 +55,43 @@
|
|||
*/
|
||||
static off_t max_pool_size = 512 * 1024 * 1024;
|
||||
|
||||
static tll(struct buffer) buffers;
|
||||
|
||||
static bool can_punch_hole = false;
|
||||
static bool can_punch_hole_initialized = false;
|
||||
|
||||
struct buffer_pool {
|
||||
int fd; /* memfd */
|
||||
struct wl_shm_pool *wl_pool;
|
||||
|
||||
void *real_mmapped; /* Address returned from mmap */
|
||||
size_t mmap_size; /* Size of mmap (>= size) */
|
||||
|
||||
size_t ref_count;
|
||||
};
|
||||
|
||||
struct buffer_chain;
|
||||
struct buffer_private {
|
||||
struct buffer public;
|
||||
struct buffer_chain *chain;
|
||||
|
||||
size_t ref_count;
|
||||
bool busy; /* Owned by compositor */
|
||||
|
||||
struct buffer_pool *pool;
|
||||
off_t offset; /* Offset into memfd where data begins */
|
||||
size_t size;
|
||||
|
||||
bool scrollable;
|
||||
};
|
||||
|
||||
struct buffer_chain {
|
||||
tll(struct buffer_private *) bufs;
|
||||
struct wl_shm *shm;
|
||||
size_t pix_instances;
|
||||
bool scrollable;
|
||||
};
|
||||
|
||||
static tll(struct buffer_private *) deferred;
|
||||
|
||||
#undef MEASURE_SHM_ALLOCS
|
||||
#if defined(MEASURE_SHM_ALLOCS)
|
||||
static size_t max_alloced = 0;
|
||||
|
|
@ -85,34 +117,70 @@ buffer_destroy_dont_close(struct buffer *buf)
|
|||
free(buf->pix);
|
||||
buf->pix = NULL;
|
||||
buf->wl_buf = NULL;
|
||||
buf->mmapped = NULL;
|
||||
buf->data = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
buffer_destroy(struct buffer *buf)
|
||||
pool_unref(struct buffer_pool *pool)
|
||||
{
|
||||
buffer_destroy_dont_close(buf);
|
||||
if (buf->real_mmapped != MAP_FAILED)
|
||||
munmap(buf->real_mmapped, buf->mmap_size);
|
||||
if (buf->pool != NULL)
|
||||
wl_shm_pool_destroy(buf->pool);
|
||||
if (buf->fd >= 0)
|
||||
close(buf->fd);
|
||||
if (pool == NULL)
|
||||
return;
|
||||
|
||||
buf->real_mmapped = MAP_FAILED;
|
||||
xassert(pool->ref_count > 0);
|
||||
pool->ref_count--;
|
||||
|
||||
if (pool->ref_count > 0)
|
||||
return;
|
||||
|
||||
if (pool->real_mmapped != MAP_FAILED)
|
||||
munmap(pool->real_mmapped, pool->mmap_size);
|
||||
if (pool->wl_pool != NULL)
|
||||
wl_shm_pool_destroy(pool->wl_pool);
|
||||
if (pool->fd >= 0)
|
||||
close(pool->fd);
|
||||
|
||||
pool->real_mmapped = MAP_FAILED;
|
||||
pool->wl_pool = NULL;
|
||||
pool->fd = -1;
|
||||
free(pool);
|
||||
}
|
||||
|
||||
static void
|
||||
buffer_destroy(struct buffer_private *buf)
|
||||
{
|
||||
buffer_destroy_dont_close(&buf->public);
|
||||
pool_unref(buf->pool);
|
||||
buf->pool = NULL;
|
||||
buf->fd = -1;
|
||||
|
||||
free(buf->scroll_damage);
|
||||
pixman_region32_fini(&buf->dirty);
|
||||
free(buf->public.scroll_damage);
|
||||
pixman_region32_fini(&buf->public.dirty);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static bool
|
||||
buffer_unref_no_remove_from_chain(struct buffer_private *buf)
|
||||
{
|
||||
xassert(buf->ref_count > 0);
|
||||
buf->ref_count--;
|
||||
|
||||
if (buf->ref_count > 0)
|
||||
return false;
|
||||
|
||||
if (buf->busy)
|
||||
tll_push_back(deferred, buf);
|
||||
else
|
||||
buffer_destroy(buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
shm_fini(void)
|
||||
{
|
||||
tll_foreach(buffers, it) {
|
||||
buffer_destroy(&it->item);
|
||||
tll_remove(buffers, it);
|
||||
LOG_DBG("deferred buffers: %zu", tll_length(deferred));
|
||||
|
||||
tll_foreach(deferred, it) {
|
||||
buffer_destroy(it->item);
|
||||
tll_remove(deferred, it);
|
||||
}
|
||||
|
||||
#if defined(MEASURE_SHM_ALLOCS) && MEASURE_SHM_ALLOCS
|
||||
|
|
@ -123,11 +191,28 @@ shm_fini(void)
|
|||
static void
|
||||
buffer_release(void *data, struct wl_buffer *wl_buffer)
|
||||
{
|
||||
struct buffer *buffer = data;
|
||||
LOG_DBG("release: cookie=%lx (buf=%p)", buffer->cookie, (void *)buffer);
|
||||
xassert(buffer->wl_buf == wl_buffer);
|
||||
struct buffer_private *buffer = data;
|
||||
|
||||
xassert(buffer->public.wl_buf == wl_buffer);
|
||||
xassert(buffer->busy);
|
||||
buffer->busy = false;
|
||||
|
||||
if (buffer->ref_count == 0) {
|
||||
bool found = false;
|
||||
tll_foreach(deferred, it) {
|
||||
if (it->item == buffer) {
|
||||
found = true;
|
||||
tll_remove(deferred, it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
buffer_destroy(buffer);
|
||||
|
||||
xassert(found);
|
||||
if (!found)
|
||||
LOG_WARN("deferred delete: buffer not on the 'deferred' list");
|
||||
}
|
||||
}
|
||||
|
||||
static const struct wl_buffer_listener buffer_listener = {
|
||||
|
|
@ -154,47 +239,53 @@ page_size(void)
|
|||
#endif
|
||||
|
||||
static bool
|
||||
instantiate_offset(struct wl_shm *shm, struct buffer *buf, off_t new_offset)
|
||||
instantiate_offset(struct buffer_private *buf, off_t new_offset)
|
||||
{
|
||||
xassert(buf->fd >= 0);
|
||||
xassert(buf->mmapped == NULL);
|
||||
xassert(buf->wl_buf == NULL);
|
||||
xassert(buf->pix == NULL);
|
||||
xassert(buf->public.data == NULL);
|
||||
xassert(buf->public.pix == NULL);
|
||||
xassert(buf->public.wl_buf == NULL);
|
||||
xassert(buf->pool != NULL);
|
||||
|
||||
const struct buffer_pool *pool = buf->pool;
|
||||
|
||||
void *mmapped = MAP_FAILED;
|
||||
struct wl_buffer *wl_buf = NULL;
|
||||
pixman_image_t **pix = xcalloc(buf->pix_instances, sizeof(*pix));
|
||||
pixman_image_t **pix = xcalloc(buf->public.pix_instances, sizeof(*pix));
|
||||
|
||||
mmapped = (uint8_t *)buf->real_mmapped + new_offset;
|
||||
mmapped = (uint8_t *)pool->real_mmapped + new_offset;
|
||||
|
||||
wl_buf = wl_shm_pool_create_buffer(
|
||||
buf->pool, new_offset, buf->width, buf->height, buf->stride, WL_SHM_FORMAT_ARGB8888);
|
||||
pool->wl_pool, new_offset,
|
||||
buf->public.width, buf->public.height, buf->public.stride,
|
||||
WL_SHM_FORMAT_ARGB8888);
|
||||
|
||||
if (wl_buf == NULL) {
|
||||
LOG_ERR("failed to create SHM buffer");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* One pixman image for each worker thread (do we really need multiple?) */
|
||||
for (size_t i = 0; i < buf->pix_instances; i++) {
|
||||
for (size_t i = 0; i < buf->public.pix_instances; i++) {
|
||||
pix[i] = pixman_image_create_bits_no_clear(
|
||||
PIXMAN_a8r8g8b8, buf->width, buf->height, (uint32_t *)mmapped, buf->stride);
|
||||
PIXMAN_a8r8g8b8, buf->public.width, buf->public.height,
|
||||
(uint32_t *)mmapped, buf->public.stride);
|
||||
if (pix[i] == NULL) {
|
||||
LOG_ERR("failed to create pixman image");
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
buf->public.data = mmapped;
|
||||
buf->public.wl_buf = wl_buf;
|
||||
buf->public.pix = pix;
|
||||
buf->offset = new_offset;
|
||||
buf->mmapped = mmapped;
|
||||
buf->wl_buf = wl_buf;
|
||||
buf->pix = pix;
|
||||
|
||||
wl_buffer_add_listener(wl_buf, &buffer_listener, buf);
|
||||
return true;
|
||||
|
||||
err:
|
||||
if (pix != NULL) {
|
||||
for (size_t i = 0; i < buf->pix_instances; i++)
|
||||
for (size_t i = 0; i < buf->public.pix_instances; i++)
|
||||
if (pix[i] != NULL)
|
||||
pixman_image_unref(pix[i]);
|
||||
}
|
||||
|
|
@ -206,76 +297,12 @@ err:
|
|||
return false;
|
||||
}
|
||||
|
||||
struct buffer *
|
||||
shm_get_buffer(struct wl_shm *shm, int width, int height, unsigned long cookie, bool scrollable, size_t pix_instances)
|
||||
static void NOINLINE
|
||||
get_new_buffers(struct buffer_chain *chain, size_t count,
|
||||
int widths[static count], int heights[static count],
|
||||
struct buffer *bufs[static count], bool immediate_purge)
|
||||
{
|
||||
/* Purge buffers marked for purging */
|
||||
tll_foreach(buffers, it) {
|
||||
if (it->item.cookie != cookie)
|
||||
continue;
|
||||
|
||||
if (!it->item.purge)
|
||||
continue;
|
||||
|
||||
xassert(!it->item.busy);
|
||||
|
||||
LOG_DBG("cookie=%lx: purging buffer %p (width=%d, height=%d): %zu KB",
|
||||
cookie, (void *)&it->item, it->item.width, it->item.height,
|
||||
it->item.size / 1024);
|
||||
|
||||
buffer_destroy(&it->item);
|
||||
tll_remove(buffers, it);
|
||||
}
|
||||
|
||||
struct buffer *cached = NULL;
|
||||
|
||||
tll_foreach(buffers, it) {
|
||||
if (it->item.width != width)
|
||||
continue;
|
||||
if (it->item.height != height)
|
||||
continue;
|
||||
if (it->item.cookie != cookie)
|
||||
continue;
|
||||
|
||||
if (it->item.busy)
|
||||
it->item.age++;
|
||||
else
|
||||
#if FORCED_DOUBLE_BUFFERING
|
||||
if (it->item.age == 0)
|
||||
it->item.age++;
|
||||
else
|
||||
#endif
|
||||
{
|
||||
LOG_DBG("cookie=%lx: re-using buffer from cache (buf=%p)",
|
||||
cookie, (void *)&it->item);
|
||||
it->item.busy = true;
|
||||
it->item.purge = false;
|
||||
pixman_region32_clear(&it->item.dirty);
|
||||
free(it->item.scroll_damage);
|
||||
it->item.scroll_damage = NULL;
|
||||
xassert(it->item.pix_instances == pix_instances);
|
||||
cached = &it->item;
|
||||
}
|
||||
}
|
||||
|
||||
if (cached != NULL)
|
||||
return cached;
|
||||
|
||||
/* Purge old buffers associated with this cookie */
|
||||
tll_foreach(buffers, it) {
|
||||
if (it->item.cookie != cookie)
|
||||
continue;
|
||||
|
||||
if (it->item.busy)
|
||||
continue;
|
||||
|
||||
if (it->item.width == width && it->item.height == height)
|
||||
continue;
|
||||
|
||||
LOG_DBG("cookie=%lx: marking buffer %p for purging", cookie, (void *)&it->item);
|
||||
it->item.purge = true;
|
||||
}
|
||||
|
||||
xassert(count == 1 || !chain->scrollable);
|
||||
/*
|
||||
* No existing buffer available. Create a new one by:
|
||||
*
|
||||
|
|
@ -286,14 +313,21 @@ shm_get_buffer(struct wl_shm *shm, int width, int height, unsigned long cookie,
|
|||
* The pixman image and the wayland buffer are now sharing memory.
|
||||
*/
|
||||
|
||||
int stride[count];
|
||||
int sizes[count];
|
||||
|
||||
size_t total_size = 0;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
stride[i] = stride_for_format_and_width(PIXMAN_a8r8g8b8, widths[i]);
|
||||
sizes[i] = stride[i] * heights[i];
|
||||
total_size += sizes[i];
|
||||
}
|
||||
|
||||
int pool_fd = -1;
|
||||
const int stride = stride_for_format_and_width(PIXMAN_a8r8g8b8, width);
|
||||
const size_t size = stride * height;
|
||||
|
||||
void *real_mmapped = MAP_FAILED;
|
||||
struct wl_shm_pool *pool = NULL;
|
||||
|
||||
LOG_DBG("cookie=%lx: allocating new buffer: %zu KB", cookie, size / 1024);
|
||||
struct wl_shm_pool *wl_pool = NULL;
|
||||
struct buffer_pool *pool = NULL;
|
||||
|
||||
/* Backing memory for SHM */
|
||||
#if defined(MEMFD_CREATE)
|
||||
|
|
@ -312,14 +346,20 @@ shm_get_buffer(struct wl_shm *shm, int width, int height, unsigned long cookie,
|
|||
}
|
||||
|
||||
#if __SIZEOF_POINTER__ == 8
|
||||
off_t initial_offset = scrollable && max_pool_size > 0 ? (max_pool_size / 4) & ~(page_size() - 1) : 0;
|
||||
off_t memfd_size = scrollable && max_pool_size > 0 ? max_pool_size : size;
|
||||
off_t offset = chain->scrollable && max_pool_size > 0
|
||||
? (max_pool_size / 4) & ~(page_size() - 1)
|
||||
: 0;
|
||||
off_t memfd_size = chain->scrollable && max_pool_size > 0
|
||||
? max_pool_size
|
||||
: total_size;
|
||||
#else
|
||||
off_t initial_offset = 0;
|
||||
off_t memfd_size = size;
|
||||
off_t offset = 0;
|
||||
off_t memfd_size = total_size;
|
||||
#endif
|
||||
|
||||
LOG_DBG("memfd-size: %lu, initial offset: %lu", memfd_size, initial_offset);
|
||||
xassert(chain->scrollable || (offset == 0 && memfd_size == total_size));
|
||||
|
||||
LOG_DBG("memfd-size: %lu, initial offset: %lu", memfd_size, offset);
|
||||
|
||||
if (ftruncate(pool_fd, memfd_size) == -1) {
|
||||
LOG_ERRNO("failed to set size of SHM backing memory file");
|
||||
|
|
@ -344,10 +384,10 @@ shm_get_buffer(struct wl_shm *shm, int width, int height, unsigned long cookie,
|
|||
#endif
|
||||
}
|
||||
|
||||
if (scrollable && !can_punch_hole) {
|
||||
initial_offset = 0;
|
||||
memfd_size = size;
|
||||
scrollable = false;
|
||||
if (chain->scrollable && !can_punch_hole) {
|
||||
offset = 0;
|
||||
memfd_size = total_size;
|
||||
chain->scrollable = false;
|
||||
|
||||
if (ftruncate(pool_fd, memfd_size) < 0) {
|
||||
LOG_ERRNO("failed to set size of SHM backing memory file");
|
||||
|
|
@ -375,37 +415,66 @@ shm_get_buffer(struct wl_shm *shm, int width, int height, unsigned long cookie,
|
|||
}
|
||||
#endif
|
||||
|
||||
pool = wl_shm_create_pool(shm, pool_fd, memfd_size);
|
||||
if (pool == NULL) {
|
||||
wl_pool = wl_shm_create_pool(chain->shm, pool_fd, memfd_size);
|
||||
if (wl_pool == NULL) {
|
||||
LOG_ERR("failed to create SHM pool");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Push to list of available buffers, but marked as 'busy' */
|
||||
tll_push_back(
|
||||
buffers,
|
||||
((struct buffer){
|
||||
.cookie = cookie,
|
||||
.width = width,
|
||||
.height = height,
|
||||
.stride = stride,
|
||||
.busy = true,
|
||||
.size = size,
|
||||
.pix_instances = pix_instances,
|
||||
.fd = pool_fd,
|
||||
.pool = pool,
|
||||
.scrollable = scrollable,
|
||||
.real_mmapped = real_mmapped,
|
||||
.mmap_size = memfd_size,
|
||||
.offset = 0,
|
||||
.age = 1234, /* Force a full repaint */
|
||||
}));
|
||||
|
||||
struct buffer *ret = &tll_back(buffers);
|
||||
if (!instantiate_offset(shm, ret, initial_offset))
|
||||
pool = xmalloc(sizeof(*pool));
|
||||
if (pool == NULL) {
|
||||
LOG_ERRNO("failed to allocate buffer pool");
|
||||
goto err;
|
||||
}
|
||||
|
||||
pixman_region32_init(&ret->dirty);
|
||||
*pool = (struct buffer_pool){
|
||||
.fd = pool_fd,
|
||||
.wl_pool = wl_pool,
|
||||
.real_mmapped = real_mmapped,
|
||||
.mmap_size = memfd_size,
|
||||
.ref_count = 0,
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if (sizes[i] == 0) {
|
||||
bufs[i] = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Push to list of available buffers, but marked as 'busy' */
|
||||
struct buffer_private *buf = xmalloc(sizeof(*buf));
|
||||
*buf = (struct buffer_private){
|
||||
.public = {
|
||||
.width = widths[i],
|
||||
.height = heights[i],
|
||||
.stride = stride[i],
|
||||
.pix_instances = chain->pix_instances,
|
||||
.age = 1234, /* Force a full repaint */
|
||||
},
|
||||
.chain = chain,
|
||||
.ref_count = immediate_purge ? 0 : 1,
|
||||
.busy = true,
|
||||
.pool = pool,
|
||||
.offset = 0,
|
||||
.size = sizes[i],
|
||||
.scrollable = chain->scrollable,
|
||||
};
|
||||
|
||||
if (!instantiate_offset(buf, offset)) {
|
||||
free(buf);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (immediate_purge)
|
||||
tll_push_front(deferred, buf);
|
||||
else
|
||||
tll_push_front(chain->bufs, buf);
|
||||
|
||||
pixman_region32_init(&buf->public.dirty);
|
||||
pool->ref_count++;
|
||||
offset += buf->size;
|
||||
bufs[i] = &buf->public;
|
||||
}
|
||||
|
||||
#if defined(MEASURE_SHM_ALLOCS) && MEASURE_SHM_ALLOCS
|
||||
{
|
||||
|
|
@ -417,11 +486,19 @@ shm_get_buffer(struct wl_shm *shm, int width, int height, unsigned long cookie,
|
|||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
if (!shm_can_scroll(bufs[0])) {
|
||||
/* We only need to keep the pool FD open if we’re going to SHM
|
||||
* scroll it */
|
||||
close(pool_fd);
|
||||
pool->fd = -1;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
if (pool != NULL)
|
||||
wl_shm_pool_destroy(pool);
|
||||
pool_unref(pool);
|
||||
if (wl_pool != NULL)
|
||||
wl_shm_pool_destroy(wl_pool);
|
||||
if (real_mmapped != MAP_FAILED)
|
||||
munmap(real_mmapped, memfd_size);
|
||||
if (pool_fd != -1)
|
||||
|
|
@ -429,13 +506,80 @@ err:
|
|||
|
||||
/* We don't handle this */
|
||||
abort();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
shm_get_many(struct buffer_chain *chain, size_t count,
|
||||
int widths[static count], int heights[static count],
|
||||
struct buffer *bufs[static count])
|
||||
{
|
||||
get_new_buffers(chain, count, widths, heights, bufs, true);
|
||||
}
|
||||
|
||||
struct buffer *
|
||||
shm_get_buffer(struct buffer_chain *chain, int width, int height)
|
||||
{
|
||||
LOG_DBG(
|
||||
"chain=%p: looking for a re-usable %dx%d buffer "
|
||||
"among %zu potential buffers",
|
||||
(void *)chain, width, height, tll_length(chain->bufs));
|
||||
|
||||
struct buffer_private *cached = NULL;
|
||||
tll_foreach(chain->bufs, it) {
|
||||
struct buffer_private *buf = it->item;
|
||||
|
||||
if (buf->public.width != width || buf->public.height != height) {
|
||||
LOG_DBG("purging mismatching buffer %p", (void *)buf);
|
||||
if (buffer_unref_no_remove_from_chain(buf))
|
||||
tll_remove(chain->bufs, it);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (buf->busy)
|
||||
buf->public.age++;
|
||||
else
|
||||
#if FORCED_DOUBLE_BUFFERING
|
||||
if (buf->age == 0)
|
||||
buf->age++;
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (cached == NULL) {
|
||||
LOG_DBG("re-using buffer %p from cache", (void *)buf);
|
||||
buf->busy = true;
|
||||
pixman_region32_clear(&buf->public.dirty);
|
||||
free(buf->public.scroll_damage);
|
||||
buf->public.scroll_damage = NULL;
|
||||
xassert(buf->public.pix_instances == chain->pix_instances);
|
||||
cached = it->item;
|
||||
} else {
|
||||
/* We have multiple buffers eligible for
|
||||
* re-use. Pick the “youngest” one, and mark the
|
||||
* other one for purging */
|
||||
if (buf->public.age < cached->public.age) {
|
||||
shm_unref(&cached->public);
|
||||
cached = buf;
|
||||
} else {
|
||||
if (buffer_unref_no_remove_from_chain(buf))
|
||||
tll_remove(chain->bufs, it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cached != NULL)
|
||||
return &cached->public;
|
||||
|
||||
struct buffer *ret;
|
||||
get_new_buffers(chain, 1, &width, &height, &ret, false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
shm_can_scroll(const struct buffer *buf)
|
||||
shm_can_scroll(const struct buffer *_buf)
|
||||
{
|
||||
#if __SIZEOF_POINTER__ == 8
|
||||
const struct buffer_private *buf = (const struct buffer_private *)_buf;
|
||||
return can_punch_hole && max_pool_size > 0 && buf->scrollable;
|
||||
#else
|
||||
/* Not enough virtual address space in 32-bit */
|
||||
|
|
@ -445,14 +589,20 @@ shm_can_scroll(const struct buffer *buf)
|
|||
|
||||
#if __SIZEOF_POINTER__ == 8 && defined(FALLOC_FL_PUNCH_HOLE)
|
||||
static bool
|
||||
wrap_buffer(struct wl_shm *shm, struct buffer *buf, off_t new_offset)
|
||||
wrap_buffer(struct buffer_private *buf, off_t new_offset)
|
||||
{
|
||||
struct buffer_pool *pool = buf->pool;
|
||||
xassert(pool->ref_count == 1);
|
||||
|
||||
/* We don't allow overlapping offsets */
|
||||
off_t UNUSED diff =
|
||||
new_offset < buf->offset ? buf->offset - new_offset : new_offset - buf->offset;
|
||||
off_t UNUSED diff = new_offset < buf->offset
|
||||
? buf->offset - new_offset
|
||||
: new_offset - buf->offset;
|
||||
xassert(diff > buf->size);
|
||||
|
||||
memcpy((uint8_t *)buf->real_mmapped + new_offset, buf->mmapped, buf->size);
|
||||
memcpy((uint8_t *)pool->real_mmapped + new_offset,
|
||||
buf->public.data,
|
||||
buf->size);
|
||||
|
||||
off_t trim_ofs, trim_len;
|
||||
if (new_offset > buf->offset) {
|
||||
|
|
@ -462,11 +612,11 @@ wrap_buffer(struct wl_shm *shm, struct buffer *buf, off_t new_offset)
|
|||
} else {
|
||||
/* Trim everything *after* the new buffer location */
|
||||
trim_ofs = new_offset + buf->size;
|
||||
trim_len = buf->mmap_size - trim_ofs;
|
||||
trim_len = pool->mmap_size - trim_ofs;
|
||||
}
|
||||
|
||||
if (fallocate(
|
||||
buf->fd,
|
||||
pool->fd,
|
||||
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
|
||||
trim_ofs, trim_len) < 0)
|
||||
{
|
||||
|
|
@ -475,30 +625,34 @@ wrap_buffer(struct wl_shm *shm, struct buffer *buf, off_t new_offset)
|
|||
}
|
||||
|
||||
/* Re-instantiate pixman+wl_buffer+raw pointersw */
|
||||
buffer_destroy_dont_close(buf);
|
||||
return instantiate_offset(shm, buf, new_offset);
|
||||
buffer_destroy_dont_close(&buf->public);
|
||||
return instantiate_offset(buf, new_offset);
|
||||
}
|
||||
|
||||
static bool
|
||||
shm_scroll_forward(struct wl_shm *shm, struct buffer *buf, int rows,
|
||||
shm_scroll_forward(struct buffer_private *buf, int rows,
|
||||
int top_margin, int top_keep_rows,
|
||||
int bottom_margin, int bottom_keep_rows)
|
||||
{
|
||||
struct buffer_pool *pool = buf->pool;
|
||||
|
||||
xassert(can_punch_hole);
|
||||
xassert(buf->busy);
|
||||
xassert(buf->pix);
|
||||
xassert(buf->wl_buf);
|
||||
xassert(buf->fd >= 0);
|
||||
xassert(buf->public.pix != NULL);
|
||||
xassert(buf->public.wl_buf != NULL);
|
||||
xassert(pool != NULL);
|
||||
xassert(pool->ref_count == 1);
|
||||
xassert(pool->fd >= 0);
|
||||
|
||||
LOG_DBG("scrolling %d rows (%d bytes)", rows, rows * buf->stride);
|
||||
LOG_DBG("scrolling %d rows (%d bytes)", rows, rows * buf->public.stride);
|
||||
|
||||
const off_t diff = rows * buf->stride;
|
||||
const off_t diff = rows * buf->public.stride;
|
||||
xassert(rows > 0);
|
||||
xassert(diff < buf->size);
|
||||
|
||||
if (buf->offset + diff + buf->size > max_pool_size) {
|
||||
LOG_DBG("memfd offset wrap around");
|
||||
if (!wrap_buffer(shm, buf, 0))
|
||||
if (!wrap_buffer(buf, 0))
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
|
@ -507,6 +661,7 @@ shm_scroll_forward(struct wl_shm *shm, struct buffer *buf, int rows,
|
|||
xassert(new_offset + buf->size <= max_pool_size);
|
||||
|
||||
#if TIME_SCROLL
|
||||
struct timeval tot;
|
||||
struct timeval time1;
|
||||
gettimeofday(&time1, NULL);
|
||||
|
||||
|
|
@ -515,10 +670,13 @@ shm_scroll_forward(struct wl_shm *shm, struct buffer *buf, int rows,
|
|||
|
||||
if (top_keep_rows > 0) {
|
||||
/* Copy current 'top' region to its new location */
|
||||
const int stride = buf->public.stride;
|
||||
uint8_t *base = buf->public.data;
|
||||
|
||||
memmove(
|
||||
(uint8_t *)buf->mmapped + (top_margin + rows) * buf->stride,
|
||||
(uint8_t *)buf->mmapped + (top_margin + 0) * buf->stride,
|
||||
top_keep_rows * buf->stride);
|
||||
base + (top_margin + rows) * stride,
|
||||
base + (top_margin + 0) * stride,
|
||||
top_keep_rows * stride);
|
||||
|
||||
#if TIME_SCROLL
|
||||
gettimeofday(&time2, NULL);
|
||||
|
|
@ -528,14 +686,14 @@ shm_scroll_forward(struct wl_shm *shm, struct buffer *buf, int rows,
|
|||
}
|
||||
|
||||
/* Destroy old objects (they point to the old offset) */
|
||||
buffer_destroy_dont_close(buf);
|
||||
buffer_destroy_dont_close(&buf->public);
|
||||
|
||||
/* Free unused memory - everything up until the new offset */
|
||||
const off_t trim_ofs = 0;
|
||||
const off_t trim_len = new_offset;
|
||||
|
||||
if (fallocate(
|
||||
buf->fd,
|
||||
pool->fd,
|
||||
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
|
||||
trim_ofs, trim_len) < 0)
|
||||
{
|
||||
|
|
@ -551,7 +709,7 @@ shm_scroll_forward(struct wl_shm *shm, struct buffer *buf, int rows,
|
|||
#endif
|
||||
|
||||
/* Re-instantiate pixman+wl_buffer+raw pointersw */
|
||||
bool ret = instantiate_offset(shm, buf, new_offset);
|
||||
bool ret = instantiate_offset(buf, new_offset);
|
||||
|
||||
#if TIME_SCROLL
|
||||
struct timeval time4;
|
||||
|
|
@ -562,10 +720,14 @@ shm_scroll_forward(struct wl_shm *shm, struct buffer *buf, int rows,
|
|||
|
||||
if (ret && bottom_keep_rows > 0) {
|
||||
/* Copy 'bottom' region to its new location */
|
||||
const size_t size = buf->size;
|
||||
const int stride = buf->public.stride;
|
||||
uint8_t *base = buf->public.data;
|
||||
|
||||
memmove(
|
||||
(uint8_t *)buf->mmapped + buf->size - (bottom_margin + bottom_keep_rows) * buf->stride,
|
||||
(uint8_t *)buf->mmapped + buf->size - (bottom_margin + rows + bottom_keep_rows) * buf->stride,
|
||||
bottom_keep_rows * buf->stride);
|
||||
base + size - (bottom_margin + bottom_keep_rows) * stride,
|
||||
base + size - (bottom_margin + rows + bottom_keep_rows) * stride,
|
||||
bottom_keep_rows * stride);
|
||||
|
||||
#if TIME_SCROLL
|
||||
struct timeval time5;
|
||||
|
|
@ -584,16 +746,19 @@ err:
|
|||
}
|
||||
|
||||
static bool
|
||||
shm_scroll_reverse(struct wl_shm *shm, struct buffer *buf, int rows,
|
||||
shm_scroll_reverse(struct buffer_private *buf, int rows,
|
||||
int top_margin, int top_keep_rows,
|
||||
int bottom_margin, int bottom_keep_rows)
|
||||
{
|
||||
xassert(rows > 0);
|
||||
|
||||
const off_t diff = rows * buf->stride;
|
||||
struct buffer_pool *pool = buf->pool;
|
||||
xassert(pool->ref_count == 1);
|
||||
|
||||
const off_t diff = rows * buf->public.stride;
|
||||
if (diff > buf->offset) {
|
||||
LOG_DBG("memfd offset reverse wrap-around");
|
||||
if (!wrap_buffer(shm, buf, (max_pool_size - buf->size) & ~(page_size() - 1)))
|
||||
if (!wrap_buffer(buf, (max_pool_size - buf->size) & ~(page_size() - 1)))
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
|
@ -611,10 +776,14 @@ shm_scroll_reverse(struct wl_shm *shm, struct buffer *buf, int rows,
|
|||
|
||||
if (bottom_keep_rows > 0) {
|
||||
/* Copy 'bottom' region to its new location */
|
||||
const size_t size = buf->size;
|
||||
const int stride = buf->public.stride;
|
||||
uint8_t *base = buf->public.data;
|
||||
|
||||
memmove(
|
||||
(uint8_t *)buf->mmapped + buf->size - (bottom_margin + rows + bottom_keep_rows) * buf->stride,
|
||||
(uint8_t *)buf->mmapped + buf->size - (bottom_margin + bottom_keep_rows) * buf->stride,
|
||||
bottom_keep_rows * buf->stride);
|
||||
base + size - (bottom_margin + rows + bottom_keep_rows) * stride,
|
||||
base + size - (bottom_margin + bottom_keep_rows) * stride,
|
||||
bottom_keep_rows * stride);
|
||||
|
||||
#if TIME_SCROLL
|
||||
gettimeofday(&time1, NULL);
|
||||
|
|
@ -624,14 +793,14 @@ shm_scroll_reverse(struct wl_shm *shm, struct buffer *buf, int rows,
|
|||
}
|
||||
|
||||
/* Destroy old objects (they point to the old offset) */
|
||||
buffer_destroy_dont_close(buf);
|
||||
buffer_destroy_dont_close(&buf->public);
|
||||
|
||||
/* Free unused memory - everything after the relocated buffer */
|
||||
const off_t trim_ofs = new_offset + buf->size;
|
||||
const off_t trim_len = buf->mmap_size - trim_ofs;
|
||||
const off_t trim_len = pool->mmap_size - trim_ofs;
|
||||
|
||||
if (fallocate(
|
||||
buf->fd,
|
||||
pool->fd,
|
||||
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
|
||||
trim_ofs, trim_len) < 0)
|
||||
{
|
||||
|
|
@ -646,7 +815,7 @@ shm_scroll_reverse(struct wl_shm *shm, struct buffer *buf, int rows,
|
|||
#endif
|
||||
|
||||
/* Re-instantiate pixman+wl_buffer+raw pointers */
|
||||
bool ret = instantiate_offset(shm, buf, new_offset);
|
||||
bool ret = instantiate_offset(buf, new_offset);
|
||||
|
||||
#if TIME_SCROLL
|
||||
struct timeval time3;
|
||||
|
|
@ -657,10 +826,13 @@ shm_scroll_reverse(struct wl_shm *shm, struct buffer *buf, int rows,
|
|||
|
||||
if (ret && top_keep_rows > 0) {
|
||||
/* Copy current 'top' region to its new location */
|
||||
const int stride = buf->public.stride;
|
||||
uint8_t *base = buf->public.data;
|
||||
|
||||
memmove(
|
||||
(uint8_t *)buf->mmapped + (top_margin + 0) * buf->stride,
|
||||
(uint8_t *)buf->mmapped + (top_margin + rows) * buf->stride,
|
||||
top_keep_rows * buf->stride);
|
||||
base + (top_margin + 0) * stride,
|
||||
base + (top_margin + rows) * stride,
|
||||
top_keep_rows * stride);
|
||||
|
||||
#if TIME_SCROLL
|
||||
struct timeval time4;
|
||||
|
|
@ -679,36 +851,88 @@ err:
|
|||
#endif /* FALLOC_FL_PUNCH_HOLE */
|
||||
|
||||
bool
|
||||
shm_scroll(struct wl_shm *shm, struct buffer *buf, int rows,
|
||||
shm_scroll(struct buffer *_buf, int rows,
|
||||
int top_margin, int top_keep_rows,
|
||||
int bottom_margin, int bottom_keep_rows)
|
||||
{
|
||||
#if __SIZEOF_POINTER__ == 8 && defined(FALLOC_FL_PUNCH_HOLE)
|
||||
if (!shm_can_scroll(buf))
|
||||
if (!shm_can_scroll(_buf))
|
||||
return false;
|
||||
|
||||
struct buffer_private *buf = (struct buffer_private *)_buf;
|
||||
|
||||
xassert(rows != 0);
|
||||
return rows > 0
|
||||
? shm_scroll_forward(shm, buf, rows, top_margin, top_keep_rows, bottom_margin, bottom_keep_rows)
|
||||
: shm_scroll_reverse(shm, buf, -rows, top_margin, top_keep_rows, bottom_margin, bottom_keep_rows);
|
||||
? shm_scroll_forward(buf, rows, top_margin, top_keep_rows, bottom_margin, bottom_keep_rows)
|
||||
: shm_scroll_reverse(buf, -rows, top_margin, top_keep_rows, bottom_margin, bottom_keep_rows);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
shm_purge(struct wl_shm *shm, unsigned long cookie)
|
||||
shm_purge(struct buffer_chain *chain)
|
||||
{
|
||||
LOG_DBG("cookie=%lx: purging all buffers", cookie);
|
||||
LOG_DBG("chain: %p: purging all buffers", (void *)chain);
|
||||
|
||||
/* Purge old buffers associated with this cookie */
|
||||
tll_foreach(buffers, it) {
|
||||
if (it->item.cookie != cookie)
|
||||
continue;
|
||||
|
||||
xassert(!it->item.busy);
|
||||
|
||||
buffer_destroy(&it->item);
|
||||
tll_remove(buffers, it);
|
||||
tll_foreach(chain->bufs, it) {
|
||||
if (buffer_unref_no_remove_from_chain(it->item))
|
||||
tll_remove(chain->bufs, it);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
shm_addref(struct buffer *_buf)
|
||||
{
|
||||
struct buffer_private *buf = (struct buffer_private *)_buf;
|
||||
buf->ref_count++;
|
||||
}
|
||||
|
||||
void
|
||||
shm_unref(struct buffer *_buf)
|
||||
{
|
||||
if (_buf == NULL)
|
||||
return;
|
||||
|
||||
struct buffer_private *buf = (struct buffer_private *)_buf;
|
||||
struct buffer_chain *chain = buf->chain;
|
||||
|
||||
tll_foreach(chain->bufs, it) {
|
||||
if (it->item != buf)
|
||||
continue;
|
||||
|
||||
if (buffer_unref_no_remove_from_chain(buf))
|
||||
tll_remove(chain->bufs, it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
struct buffer_chain *
|
||||
shm_chain_new(struct wl_shm *shm, bool scrollable, size_t pix_instances)
|
||||
{
|
||||
struct buffer_chain *chain = xmalloc(sizeof(*chain));
|
||||
*chain = (struct buffer_chain){
|
||||
.bufs = tll_init(),
|
||||
.shm = shm,
|
||||
.pix_instances = pix_instances,
|
||||
.scrollable = scrollable,
|
||||
};
|
||||
return chain;
|
||||
}
|
||||
|
||||
void
|
||||
shm_chain_free(struct buffer_chain *chain)
|
||||
{
|
||||
if (chain == NULL)
|
||||
return;
|
||||
|
||||
shm_purge(chain);
|
||||
|
||||
if (tll_length(chain->bufs) > 0) {
|
||||
BUG("chain=%p: there are buffers remaining; "
|
||||
"is there a missing call to shm_unref()?", (void *)chain);
|
||||
}
|
||||
|
||||
free(chain);
|
||||
}
|
||||
|
|
|
|||
75
shm.h
75
shm.h
|
|
@ -7,58 +7,71 @@
|
|||
#include <pixman.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include "terminal.h"
|
||||
#include <tllist.h>
|
||||
|
||||
struct damage;
|
||||
|
||||
struct buffer {
|
||||
unsigned long cookie;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
int stride;
|
||||
|
||||
bool busy;
|
||||
size_t size; /* Buffer size */
|
||||
void *mmapped; /* Raw data (TODO: rename) */
|
||||
void *data;
|
||||
|
||||
struct wl_buffer *wl_buf;
|
||||
pixman_image_t **pix;
|
||||
size_t pix_instances;
|
||||
|
||||
/* Internal */
|
||||
int fd; /* memfd */
|
||||
struct wl_shm_pool *pool;
|
||||
|
||||
void *real_mmapped; /* Address returned from mmap */
|
||||
size_t mmap_size; /* Size of mmap (>= size) */
|
||||
off_t offset; /* Offset into memfd where data begins */
|
||||
|
||||
bool scrollable;
|
||||
bool purge; /* True if this buffer should be destroyed */
|
||||
|
||||
unsigned age;
|
||||
|
||||
struct damage *scroll_damage;
|
||||
size_t scroll_damage_count;
|
||||
pixman_region32_t dirty;
|
||||
};
|
||||
|
||||
struct buffer *shm_get_buffer(
|
||||
struct wl_shm *shm, int width, int height, unsigned long cookie, bool scrollable, size_t pix_instances);
|
||||
void shm_fini(void);
|
||||
|
||||
void shm_set_max_pool_size(off_t max_pool_size);
|
||||
|
||||
struct buffer_chain;
|
||||
struct buffer_chain *shm_chain_new(
|
||||
struct wl_shm *shm, bool scrollable, size_t pix_instances);
|
||||
void shm_chain_free(struct buffer_chain *chain);
|
||||
|
||||
/*
|
||||
* Returns a single buffer.
|
||||
*
|
||||
* May returned a cached buffer. If so, the buffer’s age indicates how
|
||||
* many shm_get_buffer() calls have been made for the same
|
||||
* width/height while the buffer was still busy.
|
||||
*
|
||||
* A newly allocated buffer has an age of 1234.
|
||||
*/
|
||||
struct buffer *shm_get_buffer(struct buffer_chain *chain, int width, int height);
|
||||
/*
|
||||
* Returns many buffers, described by ‘info’, all sharing the same SHM
|
||||
* buffer pool.
|
||||
*
|
||||
* Never returns cached buffers. However, the newly created buffers
|
||||
* are all inserted into the regular buffer cache, and are treated
|
||||
* just like buffers created by shm_get_buffer().
|
||||
*
|
||||
* This function is useful when allocating many small buffers, with
|
||||
* (roughly) the same life time.
|
||||
*
|
||||
* Buffers are tagged for immediate purging, and will be destroyed as
|
||||
* soon as the compositor releases them.
|
||||
*/
|
||||
void shm_get_many(
|
||||
struct buffer_chain *chain, size_t count,
|
||||
int widths[static count], int heights[static count],
|
||||
struct buffer *bufs[static count]);
|
||||
|
||||
bool shm_can_scroll(const struct buffer *buf);
|
||||
bool shm_scroll(struct wl_shm *shm, struct buffer *buf, int rows,
|
||||
bool shm_scroll(struct buffer *buf, int rows,
|
||||
int top_margin, int top_keep_rows,
|
||||
int bottom_margin, int bottom_keep_rows);
|
||||
|
||||
void shm_purge(struct wl_shm *shm, unsigned long cookie);
|
||||
void shm_addref(struct buffer *buf);
|
||||
void shm_unref(struct buffer *buf);
|
||||
|
||||
struct terminal;
|
||||
static inline unsigned long shm_cookie_grid(const struct terminal *term) { return (unsigned long)((uintptr_t)term + 0); }
|
||||
static inline unsigned long shm_cookie_search(const struct terminal *term) { return (unsigned long)((uintptr_t)term + 1); }
|
||||
static inline unsigned long shm_cookie_scrollback_indicator(const struct terminal *term) { return (unsigned long)(uintptr_t)term + 2; }
|
||||
static inline unsigned long shm_cookie_render_timer(const struct terminal *term) { return (unsigned long)(uintptr_t)term + 3; }
|
||||
static inline unsigned long shm_cookie_csd(const struct terminal *term, int n) { return (unsigned long)((uintptr_t)term + 4 + (n)); }
|
||||
|
||||
struct url;
|
||||
static inline unsigned long shm_cookie_url(const struct url *url) { return (unsigned long)(uintptr_t)url; }
|
||||
void shm_purge(struct buffer_chain *chain);
|
||||
|
|
|
|||
5
sixel.c
5
sixel.c
|
|
@ -88,9 +88,10 @@ sixel_init(struct terminal *term, int p1, int p2, int p3)
|
|||
void
|
||||
sixel_destroy(struct sixel *sixel)
|
||||
{
|
||||
pixman_image_unref(sixel->pix);
|
||||
free(sixel->data);
|
||||
if (sixel->pix != NULL)
|
||||
pixman_image_unref(sixel->pix);
|
||||
|
||||
free(sixel->data);
|
||||
sixel->pix = NULL;
|
||||
sixel->data = NULL;
|
||||
}
|
||||
|
|
|
|||
228
terminal.c
228
terminal.c
|
|
@ -35,6 +35,7 @@
|
|||
#include "selection.h"
|
||||
#include "sixel.h"
|
||||
#include "slave.h"
|
||||
#include "shm.h"
|
||||
#include "spawn.h"
|
||||
#include "url-mode.h"
|
||||
#include "util.h"
|
||||
|
|
@ -1143,6 +1144,14 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
|||
.tab_stops = tll_init(),
|
||||
.wl = wayl,
|
||||
.render = {
|
||||
.chains = {
|
||||
.grid = shm_chain_new(wayl->shm, true, 1 + conf->render_worker_count),
|
||||
.search = shm_chain_new(wayl->shm, false, 1),
|
||||
.scrollback_indicator = shm_chain_new(wayl->shm, false, 1),
|
||||
.render_timer = shm_chain_new(wayl->shm, false, 1),
|
||||
.url = shm_chain_new(wayl->shm, false, 1),
|
||||
.csd = shm_chain_new(wayl->shm, false, 1),
|
||||
},
|
||||
.scrollback_lines = conf->scrollback.lines,
|
||||
.app_sync_updates.timer_fd = app_sync_updates_fd,
|
||||
.title = {
|
||||
|
|
@ -1458,6 +1467,14 @@ term_destroy(struct terminal *term)
|
|||
xassert(tll_length(term->render.workers.queue) == 0);
|
||||
tll_free(term->render.workers.queue);
|
||||
|
||||
shm_unref(term->render.last_buf);
|
||||
shm_chain_free(term->render.chains.grid);
|
||||
shm_chain_free(term->render.chains.search);
|
||||
shm_chain_free(term->render.chains.scrollback_indicator);
|
||||
shm_chain_free(term->render.chains.render_timer);
|
||||
shm_chain_free(term->render.chains.url);
|
||||
shm_chain_free(term->render.chains.csd);
|
||||
|
||||
tll_free(term->tab_stops);
|
||||
|
||||
tll_foreach(term->ptmx_buffers, it) {
|
||||
|
|
@ -1984,6 +2001,208 @@ term_erase(struct terminal *term, const struct coord *start, const struct coord
|
|||
sixel_overwrite_by_row(term, end->row, 0, end->col + 1);
|
||||
}
|
||||
|
||||
void
|
||||
term_erase_scrollback(struct terminal *term)
|
||||
{
|
||||
const int num_rows = term->grid->num_rows;
|
||||
const int mask = num_rows - 1;
|
||||
|
||||
const int start = (term->grid->offset + term->rows) & mask;
|
||||
const int end = (term->grid->offset - 1) & mask;
|
||||
|
||||
const int scrollback_start = term->grid->offset + term->rows;
|
||||
const int rel_start = (start - scrollback_start + num_rows) & mask;
|
||||
const int rel_end = (end - scrollback_start + num_rows) & mask;
|
||||
|
||||
const int sel_start = term->selection.start.row;
|
||||
const int sel_end = term->selection.end.row;
|
||||
|
||||
if (sel_end >= 0) {
|
||||
/*
|
||||
* Cancel selection if it touches any of the rows in the
|
||||
* scrollback, since we can’t have the selection reference
|
||||
* soon-to-be deleted rows.
|
||||
*
|
||||
* This is done by range checking the selection range against
|
||||
* the scrollback range.
|
||||
*
|
||||
* To make this comparison simpler, the start/end absolute row
|
||||
* numbers are “rebased” against the scrollback start, where
|
||||
* row 0 is the *first* row in the scrollback. A high number
|
||||
* thus means the row is further *down* in the scrollback,
|
||||
* closer to the screen bottom.
|
||||
*/
|
||||
|
||||
const int rel_sel_start = (sel_start - scrollback_start + num_rows) & mask;
|
||||
const int rel_sel_end = (sel_end - scrollback_start + num_rows) & mask;
|
||||
|
||||
if ((rel_sel_start <= rel_start && rel_sel_end >= rel_start) ||
|
||||
(rel_sel_start <= rel_end && rel_sel_end >= rel_end) ||
|
||||
(rel_sel_start >= rel_start && rel_sel_end <= rel_end))
|
||||
{
|
||||
selection_cancel(term);
|
||||
}
|
||||
}
|
||||
|
||||
tll_foreach(term->grid->sixel_images, it) {
|
||||
struct sixel *six = &it->item;
|
||||
const int six_start = (six->pos.row - scrollback_start + num_rows) & mask;
|
||||
const int six_end = (six->pos.row + six->rows - 1 - scrollback_start + num_rows) & mask;
|
||||
|
||||
if ((six_start <= rel_start && six_end >= rel_start) ||
|
||||
(six_start <= rel_end && six_end >= rel_end) ||
|
||||
(six_start >= rel_start && six_end <= rel_end))
|
||||
{
|
||||
sixel_destroy(six);
|
||||
tll_remove(term->grid->sixel_images, it);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = start;; i = (i + 1) & mask) {
|
||||
struct row *row = term->grid->rows[i];
|
||||
if (row != NULL) {
|
||||
if (term->render.last_cursor.row == row)
|
||||
term->render.last_cursor.row = NULL;
|
||||
|
||||
grid_row_free(row);
|
||||
term->grid->rows[i] = NULL;
|
||||
}
|
||||
|
||||
if (i == end)
|
||||
break;
|
||||
}
|
||||
|
||||
term->grid->view = term->grid->offset;
|
||||
term_damage_view(term);
|
||||
}
|
||||
|
||||
UNITTEST
|
||||
{
|
||||
const int scrollback_rows = 16;
|
||||
const int term_rows = 5;
|
||||
const int cols = 5;
|
||||
|
||||
struct fdm *fdm = fdm_init();
|
||||
xassert(fdm != NULL);
|
||||
|
||||
struct terminal term = {
|
||||
.fdm = fdm,
|
||||
.rows = term_rows,
|
||||
.cols = cols,
|
||||
.normal = {
|
||||
.rows = xcalloc(scrollback_rows, sizeof(term.normal.rows[0])),
|
||||
.num_rows = scrollback_rows,
|
||||
.num_cols = cols,
|
||||
},
|
||||
.grid = &term.normal,
|
||||
.selection = {
|
||||
.start = {-1, -1},
|
||||
.end = {-1, -1},
|
||||
.kind = SELECTION_NONE,
|
||||
.auto_scroll = {
|
||||
.fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
xassert(term.selection.auto_scroll.fd >= 0);
|
||||
|
||||
#define populate_scrollback() do { \
|
||||
for (int i = 0; i < scrollback_rows; i++) { \
|
||||
if (term.normal.rows[i] == NULL) { \
|
||||
struct row *r = xcalloc(1, sizeof(*term.normal.rows[i])); \
|
||||
r->cells = xcalloc(cols, sizeof(r->cells[0])); \
|
||||
term.normal.rows[i] = r; \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Test case 1 - no selection, just verify all rows except those
|
||||
* on screen have been deleted.
|
||||
*/
|
||||
|
||||
populate_scrollback();
|
||||
term.normal.offset = 11;
|
||||
term_erase_scrollback(&term);
|
||||
for (int i = 0; i < scrollback_rows; i++) {
|
||||
if (i >= term.normal.offset && i < term.normal.offset + term_rows)
|
||||
xassert(term.normal.rows[i] != NULL);
|
||||
else
|
||||
xassert(term.normal.rows[i] == NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test case 2 - selection that touches the scrollback. Verify the
|
||||
* selection is cancelled.
|
||||
*/
|
||||
|
||||
term.normal.offset = 14; /* Screen covers rows 14,15,0,1,2 */
|
||||
|
||||
/* Selection covers rows 15,0,1,2,3 */
|
||||
term.selection.start = (struct coord){.row = 15};
|
||||
term.selection.end = (struct coord){.row = 19};
|
||||
term.selection.kind = SELECTION_CHAR_WISE;
|
||||
|
||||
populate_scrollback();
|
||||
term_erase_scrollback(&term);
|
||||
xassert(term.selection.start.row < 0);
|
||||
xassert(term.selection.end.row < 0);
|
||||
xassert(term.selection.kind == SELECTION_NONE);
|
||||
|
||||
/*
|
||||
* Test case 3 - selection that does *not* touch the
|
||||
* scrollback. Verify the selection is *not* cancelled.
|
||||
*/
|
||||
|
||||
/* Selection covers rows 15,0 */
|
||||
term.selection.start = (struct coord){.row = 15};
|
||||
term.selection.end = (struct coord){.row = 16};
|
||||
term.selection.kind = SELECTION_CHAR_WISE;
|
||||
|
||||
populate_scrollback();
|
||||
term_erase_scrollback(&term);
|
||||
xassert(term.selection.start.row == 15);
|
||||
xassert(term.selection.end.row == 16);
|
||||
xassert(term.selection.kind == SELECTION_CHAR_WISE);
|
||||
|
||||
term.selection.start = (struct coord){-1, -1};
|
||||
term.selection.end = (struct coord){-1, -1};
|
||||
term.selection.kind = SELECTION_NONE;
|
||||
|
||||
/*
|
||||
* Test case 4 - sixel that touch the scrollback
|
||||
*/
|
||||
|
||||
struct sixel six = {
|
||||
.rows = 5,
|
||||
.pos = {
|
||||
.row = 15,
|
||||
},
|
||||
};
|
||||
tll_push_back(term.normal.sixel_images, six);
|
||||
populate_scrollback();
|
||||
term_erase_scrollback(&term);
|
||||
xassert(tll_length(term.normal.sixel_images) == 0);
|
||||
|
||||
/*
|
||||
* Test case 5 - sixel that does *not* touch the scrollback
|
||||
*/
|
||||
six.rows = 3;
|
||||
tll_push_back(term.normal.sixel_images, six);
|
||||
populate_scrollback();
|
||||
term_erase_scrollback(&term);
|
||||
xassert(tll_length(term.normal.sixel_images) == 1);
|
||||
|
||||
/* Cleanup */
|
||||
tll_free(term.normal.sixel_images);
|
||||
close(term.selection.auto_scroll.fd);
|
||||
for (int i = 0; i < scrollback_rows; i++)
|
||||
grid_row_free(term.normal.rows[i]);
|
||||
free(term.normal.rows);
|
||||
fdm_destroy(fdm);
|
||||
}
|
||||
|
||||
int
|
||||
term_row_rel_to_abs(const struct terminal *term, int row)
|
||||
{
|
||||
|
|
@ -2634,9 +2853,13 @@ term_xcursor_update(struct terminal *term)
|
|||
void
|
||||
term_set_window_title(struct terminal *term, const char *title)
|
||||
{
|
||||
if (term->conf->locked_title && term->window_title_has_been_set)
|
||||
return;
|
||||
|
||||
free(term->window_title);
|
||||
term->window_title = xstrdup(title);
|
||||
render_refresh_title(term);
|
||||
term->window_title_has_been_set = true;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -2756,6 +2979,7 @@ print_linewrap(struct terminal *term)
|
|||
return;
|
||||
}
|
||||
|
||||
term->grid->cur_row->linebreak = false;
|
||||
term->grid->cursor.lcf = false;
|
||||
|
||||
const int row = term->grid->cursor.point.row;
|
||||
|
|
@ -2848,7 +3072,7 @@ term_print(struct terminal *term, wchar_t wc, int width)
|
|||
cell->attrs = term->vt.attrs;
|
||||
|
||||
row->dirty = true;
|
||||
row->linebreak = false;
|
||||
row->linebreak = true;
|
||||
|
||||
/* Advance cursor the 'additional' columns while dirty:ing the cells */
|
||||
for (int i = 1; i < width && term->grid->cursor.point.col < term->cols - 1; i++) {
|
||||
|
|
@ -2887,7 +3111,7 @@ ascii_printer_fast(struct terminal *term, wchar_t wc)
|
|||
cell->attrs = term->vt.attrs;
|
||||
|
||||
row->dirty = true;
|
||||
row->linebreak = false;
|
||||
row->linebreak = true;
|
||||
|
||||
/* Advance cursor */
|
||||
if (unlikely(++term->grid->cursor.point.col >= term->cols)) {
|
||||
|
|
|
|||
18
terminal.h
18
terminal.h
|
|
@ -21,6 +21,7 @@
|
|||
#include "fdm.h"
|
||||
#include "macros.h"
|
||||
#include "reaper.h"
|
||||
#include "shm.h"
|
||||
#include "wayland.h"
|
||||
|
||||
/*
|
||||
|
|
@ -42,11 +43,12 @@ struct attributes {
|
|||
uint32_t fg:24;
|
||||
|
||||
bool clean:1;
|
||||
bool confined:1;
|
||||
bool have_fg:1;
|
||||
bool have_bg:1;
|
||||
uint32_t selected:2;
|
||||
bool url:1;
|
||||
uint32_t reserved:2;
|
||||
uint32_t reserved:1;
|
||||
uint32_t bg:24;
|
||||
};
|
||||
static_assert(sizeof(struct attributes) == 8, "VT attribute struct too large");
|
||||
|
|
@ -264,6 +266,7 @@ struct url {
|
|||
enum url_action action;
|
||||
bool url_mode_dont_change_url_attr; /* Entering/exiting URL mode doesn’t touch the cells’ attr.url */
|
||||
bool osc8;
|
||||
bool duplicate;
|
||||
};
|
||||
typedef tll(struct url) url_list_t;
|
||||
|
||||
|
|
@ -377,11 +380,12 @@ struct terminal {
|
|||
bool ime:1;
|
||||
bool app_sync_updates:1;
|
||||
|
||||
bool sixel_scrolling:1;
|
||||
bool sixel_display_mode:1;
|
||||
bool sixel_private_palette:1;
|
||||
bool sixel_cursor_right_of_graphics:1;
|
||||
} xtsave;
|
||||
|
||||
bool window_title_has_been_set;
|
||||
char *window_title;
|
||||
tll(char *) window_title_stack;
|
||||
|
||||
|
|
@ -472,6 +476,15 @@ struct terminal {
|
|||
enum term_surface active_surface;
|
||||
|
||||
struct {
|
||||
struct {
|
||||
struct buffer_chain *grid;
|
||||
struct buffer_chain *search;
|
||||
struct buffer_chain *scrollback_indicator;
|
||||
struct buffer_chain *render_timer;
|
||||
struct buffer_chain *url;
|
||||
struct buffer_chain *csd;
|
||||
} chains;
|
||||
|
||||
/* Scheduled for rendering, as soon-as-possible */
|
||||
struct {
|
||||
bool grid;
|
||||
|
|
@ -649,6 +662,7 @@ void term_damage_scroll(
|
|||
|
||||
void term_erase(
|
||||
struct terminal *term, const struct coord *start, const struct coord *end);
|
||||
void term_erase_scrollback(struct terminal *term);
|
||||
|
||||
int term_row_rel_to_abs(const struct terminal *term, int row);
|
||||
void term_cursor_home(struct terminal *term);
|
||||
|
|
|
|||
23
url-mode.c
23
url-mode.c
|
|
@ -469,16 +469,20 @@ remove_overlapping(url_list_t *urls, int cols)
|
|||
*/
|
||||
xassert(in->osc8 || out->osc8);
|
||||
|
||||
if (in->osc8) {
|
||||
url_destroy(&outer->item);
|
||||
tll_remove(*urls, outer);
|
||||
} else {
|
||||
url_destroy(&inner->item);
|
||||
tll_remove(*urls, inner);
|
||||
}
|
||||
if (in->osc8)
|
||||
outer->item.duplicate = true;
|
||||
else
|
||||
inner->item.duplicate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tll_foreach(*urls, it) {
|
||||
if (it->item.duplicate) {
|
||||
url_destroy(&it->item);
|
||||
tll_remove(*urls, it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -649,6 +653,11 @@ tag_cells_for_url(struct terminal *term, const struct url *url, bool value)
|
|||
c = 0;
|
||||
|
||||
row = term->grid->rows[r];
|
||||
if (row == NULL) {
|
||||
/* Un-allocated scrollback. This most likely means a
|
||||
* runaway OSC-8 URL. */
|
||||
break;
|
||||
}
|
||||
row->dirty = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
23
vt.c
23
vt.c
|
|
@ -412,6 +412,29 @@ action_collect(struct terminal *term, uint8_t c)
|
|||
LOG_WARN("only four private/intermediate characters supported");
|
||||
}
|
||||
|
||||
UNITTEST
|
||||
{
|
||||
struct terminal term = {.vt = {.private = 0}};
|
||||
uint32_t expected = ' ';
|
||||
action_collect(&term, ' ');
|
||||
xassert(term.vt.private == expected);
|
||||
|
||||
expected |= '/' << 8;
|
||||
action_collect(&term, '/');
|
||||
xassert(term.vt.private == expected);
|
||||
|
||||
expected |= '<' << 16;
|
||||
action_collect(&term, '<');
|
||||
xassert(term.vt.private == expected);
|
||||
|
||||
expected |= '?' << 24;
|
||||
action_collect(&term, '?');
|
||||
xassert(term.vt.private == expected);
|
||||
|
||||
action_collect(&term, '?');
|
||||
xassert(term.vt.private == expected);
|
||||
}
|
||||
|
||||
static void
|
||||
action_esc_dispatch(struct terminal *term, uint8_t final)
|
||||
{
|
||||
|
|
|
|||
19
wayland.c
19
wayland.c
|
|
@ -26,6 +26,7 @@
|
|||
#include "input.h"
|
||||
#include "render.h"
|
||||
#include "selection.h"
|
||||
#include "shm.h"
|
||||
#include "util.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
|
|
@ -50,8 +51,11 @@ csd_instantiate(struct wl_window *win)
|
|||
static void
|
||||
csd_destroy(struct wl_window *win)
|
||||
{
|
||||
struct terminal *term = win->term;
|
||||
|
||||
for (size_t i = 0; i < ALEN(win->csd.surface); i++)
|
||||
wayl_win_subsurface_destroy(&win->csd.surface[i]);
|
||||
shm_purge(term->render.chains.csd);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -1410,6 +1414,8 @@ wayl_win_destroy(struct wl_window *win)
|
|||
if (win == NULL)
|
||||
return;
|
||||
|
||||
struct terminal *term = win->term;
|
||||
|
||||
if (win->csd.move_timeout_fd != -1)
|
||||
close(win->csd.move_timeout_fd);
|
||||
|
||||
|
|
@ -1438,6 +1444,12 @@ wayl_win_destroy(struct wl_window *win)
|
|||
wl_surface_commit(win->search.surf);
|
||||
}
|
||||
|
||||
/* URLs */
|
||||
tll_foreach(win->urls, it) {
|
||||
wl_surface_attach(it->item.surf.surf, NULL, 0, 0);
|
||||
wl_surface_commit(it->item.surf.surf);
|
||||
}
|
||||
|
||||
/* CSD */
|
||||
for (size_t i = 0; i < ALEN(win->csd.surface); i++) {
|
||||
if (win->csd.surface[i].surf != NULL) {
|
||||
|
|
@ -1465,6 +1477,13 @@ wayl_win_destroy(struct wl_window *win)
|
|||
wayl_win_subsurface_destroy(&win->scrollback_indicator);
|
||||
wayl_win_subsurface_destroy(&win->render_timer);
|
||||
|
||||
shm_purge(term->render.chains.search);
|
||||
shm_purge(term->render.chains.scrollback_indicator);
|
||||
shm_purge(term->render.chains.render_timer);
|
||||
shm_purge(term->render.chains.grid);
|
||||
shm_purge(term->render.chains.url);
|
||||
shm_purge(term->render.chains.csd);
|
||||
|
||||
#if defined(HAVE_XDG_ACTIVATION)
|
||||
if (win->xdg_activation_token != NULL)
|
||||
xdg_activation_token_v1_destroy(win->xdg_activation_token);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue