mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-03-24 09:05:48 -04: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
|
# Changelog
|
||||||
|
|
||||||
|
* [Unreleased](#unreleased)
|
||||||
* [1.8.1](#1-8-1)
|
* [1.8.1](#1-8-1)
|
||||||
* [1.8.0](#1-8-0)
|
* [1.8.0](#1-8-0)
|
||||||
* [1.7.2](#1-7-2)
|
* [1.7.2](#1-7-2)
|
||||||
|
|
@ -27,6 +28,58 @@
|
||||||
* [1.2.0](#1-2-0)
|
* [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
|
## 1.8.1
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
@ -43,7 +96,7 @@
|
||||||
* Grapheme cluster width is now limited to two cells by default. This
|
* Grapheme cluster width is now limited to two cells by default. This
|
||||||
may cause cursor synchronization issues with many applications. You
|
may cause cursor synchronization issues with many applications. You
|
||||||
can set `[tweak].grapheme-width-method=wcswidth` to revert to the
|
can set `[tweak].grapheme-width-method=wcswidth` to revert to the
|
||||||
behavior from foot-1.8.0.
|
behavior in foot-1.8.0.
|
||||||
|
|
||||||
|
|
||||||
### Fixed
|
### 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);
|
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
|
static void
|
||||||
draw_box_drawings_light_arc(struct buf *buf, wchar_t wc)
|
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) {
|
if (fmt == PIXMAN_a1) {
|
||||||
size_t idx = c / 8;
|
size_t idx = c / 8;
|
||||||
size_t bit_no = 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
|
} else
|
||||||
data[r * stride + c] = 0xff;
|
data[r * stride + c] = 0xff;
|
||||||
}
|
}
|
||||||
|
|
@ -1376,7 +1386,7 @@ draw_box_drawings_light_arc(struct buf *buf, wchar_t wc)
|
||||||
if (fmt == PIXMAN_a1) {
|
if (fmt == PIXMAN_a1) {
|
||||||
size_t ofs = col / 8;
|
size_t ofs = col / 8;
|
||||||
size_t bit_no = 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
|
} else
|
||||||
data[row * stride + col] = 0xff;
|
data[row * stride + col] = 0xff;
|
||||||
}
|
}
|
||||||
|
|
@ -1392,7 +1402,7 @@ draw_box_drawings_light_arc(struct buf *buf, wchar_t wc)
|
||||||
if (fmt == PIXMAN_a1) {
|
if (fmt == PIXMAN_a1) {
|
||||||
size_t ofs = col / 8;
|
size_t ofs = col / 8;
|
||||||
size_t bit_no = 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
|
} else
|
||||||
data[row * stride + col] = 0xff;
|
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) {
|
for (size_t col = 0; col < buf->width; col += 2) {
|
||||||
size_t idx = col / 8;
|
size_t idx = col / 8;
|
||||||
size_t bit_no = 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) {
|
for (size_t col = row % 2; col < buf->width; col += 2) {
|
||||||
size_t idx = col / 8;
|
size_t idx = col / 8;
|
||||||
size_t bit_no = 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) {
|
for (size_t col = 0; col < buf->width; col += 1 + row % 2) {
|
||||||
size_t idx = col / 8;
|
size_t idx = col / 8;
|
||||||
size_t bit_no = 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);
|
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) {
|
else if (strcmp(key, "app-id") == 0) {
|
||||||
free(conf->app_id);
|
free(conf->app_id);
|
||||||
conf->app_id = xstrdup(value);
|
conf->app_id = xstrdup(value);
|
||||||
|
|
@ -2210,16 +2213,10 @@ parse_section_tweak(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (strcmp(key, "allow-overflowing-double-width-glyphs") == 0) {
|
else if (strcmp(key, "overflowing-glyphs") == 0) {
|
||||||
conf->tweak.allow_overflowing_double_width_glyphs = str_to_bool(value);
|
conf->tweak.overflowing_glyphs = str_to_bool(value);
|
||||||
if (!conf->tweak.allow_overflowing_double_width_glyphs)
|
if (!conf->tweak.overflowing_glyphs)
|
||||||
LOG_WARN("tweak: disabled overflowing double-width glyphs");
|
LOG_WARN("tweak: disabled overflowing 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, "damage-whole-window") == 0) {
|
else if (strcmp(key, "damage-whole-window") == 0) {
|
||||||
|
|
@ -2830,7 +2827,7 @@ config_load(struct config *conf, const char *conf_path,
|
||||||
|
|
||||||
.tweak = {
|
.tweak = {
|
||||||
.fcft_filter = FCFT_SCALING_FILTER_LANCZOS3,
|
.fcft_filter = FCFT_SCALING_FILTER_LANCZOS3,
|
||||||
.allow_overflowing_double_width_glyphs = true,
|
.overflowing_glyphs = true,
|
||||||
.grapheme_shaping = false,
|
.grapheme_shaping = false,
|
||||||
.grapheme_width_method = GRAPHEME_WIDTH_DOUBLE,
|
.grapheme_width_method = GRAPHEME_WIDTH_DOUBLE,
|
||||||
.delayed_render_lower_ns = 500000, /* 0.5ms */
|
.delayed_render_lower_ns = 500000, /* 0.5ms */
|
||||||
|
|
@ -2841,7 +2838,6 @@ config_load(struct config *conf, const char *conf_path,
|
||||||
.damage_whole_window = false,
|
.damage_whole_window = false,
|
||||||
.box_drawing_base_thickness = 0.04,
|
.box_drawing_base_thickness = 0.04,
|
||||||
.box_drawing_solid_shades = true,
|
.box_drawing_solid_shades = true,
|
||||||
.pua_double_width = false,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
.notifications = tll_init(),
|
.notifications = tll_init(),
|
||||||
|
|
|
||||||
4
config.h
4
config.h
|
|
@ -77,6 +77,7 @@ struct config {
|
||||||
wchar_t *word_delimiters;
|
wchar_t *word_delimiters;
|
||||||
bool login_shell;
|
bool login_shell;
|
||||||
bool no_wait;
|
bool no_wait;
|
||||||
|
bool locked_title;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
enum conf_size_type type;
|
enum conf_size_type type;
|
||||||
|
|
@ -244,7 +245,7 @@ struct config {
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
enum fcft_scaling_filter fcft_filter;
|
enum fcft_scaling_filter fcft_filter;
|
||||||
bool allow_overflowing_double_width_glyphs;
|
bool overflowing_glyphs;
|
||||||
bool grapheme_shaping;
|
bool grapheme_shaping;
|
||||||
enum {GRAPHEME_WIDTH_WCSWIDTH, GRAPHEME_WIDTH_DOUBLE} grapheme_width_method;
|
enum {GRAPHEME_WIDTH_WCSWIDTH, GRAPHEME_WIDTH_DOUBLE} grapheme_width_method;
|
||||||
bool render_timer_osd;
|
bool render_timer_osd;
|
||||||
|
|
@ -255,7 +256,6 @@ struct config {
|
||||||
off_t max_shm_pool_size;
|
off_t max_shm_pool_size;
|
||||||
float box_drawing_base_thickness;
|
float box_drawing_base_thickness;
|
||||||
bool box_drawing_solid_shades;
|
bool box_drawing_solid_shades;
|
||||||
bool pua_double_width;
|
|
||||||
} tweak;
|
} tweak;
|
||||||
|
|
||||||
user_notifications_t notifications;
|
user_notifications_t notifications;
|
||||||
|
|
|
||||||
29
csi.c
29
csi.c
|
|
@ -402,7 +402,7 @@ decset_decrst(struct terminal *term, unsigned param, bool enable)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 80:
|
case 80:
|
||||||
term->sixel.scrolling = enable;
|
term->sixel.scrolling = !enable;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1000:
|
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 12: *enabled = term->cursor_blink.decset; return true;
|
||||||
case 25: *enabled = !term->hide_cursor; return true;
|
case 25: *enabled = !term->hide_cursor; return true;
|
||||||
case 45: *enabled = term->reverse_wrap; 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 1000: *enabled = term->mouse_tracking == MOUSE_CLICK; return true;
|
||||||
case 1001: *enabled = false; return true;
|
case 1001: *enabled = false; return true;
|
||||||
case 1002: *enabled = term->mouse_tracking == MOUSE_DRAG; 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 25: term->xtsave.show_cursor = !term->hide_cursor; break;
|
||||||
case 45: term->xtsave.reverse_wrap = term->reverse_wrap; break;
|
case 45: term->xtsave.reverse_wrap = term->reverse_wrap; break;
|
||||||
case 47: term->xtsave.alt_screen = term->grid == &term->alt; 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 1000: term->xtsave.mouse_click = term->mouse_tracking == MOUSE_CLICK; break;
|
||||||
case 1001: break;
|
case 1001: break;
|
||||||
case 1002: term->xtsave.mouse_drag = term->mouse_tracking == MOUSE_DRAG; 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 25: enable = term->xtsave.show_cursor; break;
|
||||||
case 45: enable = term->xtsave.reverse_wrap; break;
|
case 45: enable = term->xtsave.reverse_wrap; break;
|
||||||
case 47: enable = term->xtsave.alt_screen; 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 1000: enable = term->xtsave.mouse_click; break;
|
||||||
case 1001: return;
|
case 1001: return;
|
||||||
case 1002: enable = term->xtsave.mouse_drag; break;
|
case 1002: enable = term->xtsave.mouse_drag; break;
|
||||||
|
|
@ -917,26 +917,7 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
||||||
|
|
||||||
case 3: {
|
case 3: {
|
||||||
/* Erase scrollback */
|
/* Erase scrollback */
|
||||||
int end = (term->grid->offset + term->rows - 1) % term->grid->num_rows;
|
term_erase_scrollback(term);
|
||||||
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);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -204,7 +204,7 @@ Note that these are just the defaults; they can be changed in
|
||||||
characters are whitespace characters.
|
characters are whitespace characters.
|
||||||
|
|
||||||
*ctrl*+*v*, *ctrl*+*y*
|
*ctrl*+*v*, *ctrl*+*y*
|
||||||
Paste from clipboard into the searh buffer.
|
Paste from clipboard into the search buffer.
|
||||||
|
|
||||||
*shift*+*insert*
|
*shift*+*insert*
|
||||||
Paste from primary selection into the search buffer.
|
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
|
In other words, while you are fiddling with the window size, foot
|
||||||
does not send the updated dimensions to the client. Only when you
|
does not send the updated dimensions to the client. Only when you
|
||||||
pause the fiddling for *relay-size-ms* milliseconds is the client
|
pause the fiddling for *resize-delay-ms* milliseconds is the
|
||||||
updated.
|
client updated.
|
||||||
|
|
||||||
Emphasis is on _while_ here; as soon as the interactive resize
|
Emphasis is on _while_ here; as soon as the interactive resize
|
||||||
ends (i.e. when you let go of the window border), the final
|
ends (i.e. when you let go of the window border), the final
|
||||||
|
|
@ -224,6 +224,10 @@ in this order:
|
||||||
*title*
|
*title*
|
||||||
Initial window title. Default: _foot_.
|
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*
|
*app-id*
|
||||||
Value to set the *app-id* property on the Wayland window to. The
|
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
|
compositor can use this value to e.g. group multiple windows, or
|
||||||
|
|
@ -843,34 +847,24 @@ any of these options.
|
||||||
|
|
||||||
Default: _lanczos3_.
|
Default: _lanczos3_.
|
||||||
|
|
||||||
*allow-overflowing-double-width-glyphs*
|
*overflowing-glyphs*
|
||||||
Boolean. When enabled, double width glyphs with a character width
|
Boolean. When enabled, glyphs wider than their cell(s) are allowed
|
||||||
of 1 are allowed to overflow into the neighbouring cell.
|
to render into one additional neighbouring cell.
|
||||||
|
|
||||||
One use case for this is fonts "icon" characters in the Unicode
|
One use case for this are fonts with wide italic characters that
|
||||||
private usage area, e.g. Nerd Fonts, or Powerline Fonts. Without
|
"bend" into the next cell. Without this option, such glyphs will
|
||||||
this option, such glyphs will appear "cut off".
|
appear "cut off".
|
||||||
|
|
||||||
Another use case are legacy emoji characters like *WHITE FROWNING
|
Another use case are fonts with "icon" characters in the Unicode
|
||||||
FACE*.
|
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
|
Note: might impact performance depending on the font used.
|
||||||
should be allowed to overflow.
|
Especially small font sizes can cause many overflowing glyphs
|
||||||
|
because of subpixel rendering.
|
||||||
See also: *pua-double-width*
|
|
||||||
|
|
||||||
Default: _yes_.
|
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*
|
*render-timer*
|
||||||
Enables a frame rendering timer, that prints the time it takes to
|
Enables a frame rendering timer, that prints the time it takes to
|
||||||
render each frame, in microseconds, either on-screen, to stderr,
|
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)
|
# term=foot (or xterm-256color if built with -Dterminfo=disabled)
|
||||||
# login-shell=no
|
# login-shell=no
|
||||||
|
|
||||||
|
# app-id=foot
|
||||||
|
# title=foot
|
||||||
|
# locked-title=no
|
||||||
|
|
||||||
# font=monospace:size=8
|
# font=monospace:size=8
|
||||||
# font-bold=<bold variant of regular font>
|
# font-bold=<bold variant of regular font>
|
||||||
# font-italic=<italic variant of regular font>
|
# font-italic=<italic variant of regular font>
|
||||||
|
|
@ -122,7 +126,7 @@
|
||||||
# show-urls-copy=none
|
# show-urls-copy=none
|
||||||
|
|
||||||
[search-bindings]
|
[search-bindings]
|
||||||
# cancel=Control+g Escape
|
# cancel=Control+g Control+c Escape
|
||||||
# commit=Return
|
# commit=Return
|
||||||
# find-prev=Control+r
|
# find-prev=Control+r
|
||||||
# find-next=Control+s
|
# find-next=Control+s
|
||||||
|
|
@ -142,7 +146,7 @@
|
||||||
# primary-paste=Shift+Insert
|
# primary-paste=Shift+Insert
|
||||||
|
|
||||||
[url-bindings]
|
[url-bindings]
|
||||||
# cancel=Control+g Control+d Escape
|
# cancel=Control+g Control+c Control+d Escape
|
||||||
# toggle-url-visible=t
|
# toggle-url-visible=t
|
||||||
|
|
||||||
[mouse-bindings]
|
[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
|
static int
|
||||||
render_cell(struct terminal *term, pixman_image_t *pix,
|
render_cell(struct terminal *term, pixman_image_t *pix,
|
||||||
struct row *row, int col, int row_no, bool has_cursor)
|
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;
|
return 0;
|
||||||
|
|
||||||
cell->attrs.clean = 1;
|
cell->attrs.clean = 1;
|
||||||
|
cell->attrs.confined = true;
|
||||||
|
|
||||||
int width = term->cell_width;
|
int width = term->cell_width;
|
||||||
int height = term->cell_height;
|
int height = term->cell_height;
|
||||||
|
|
@ -458,7 +473,7 @@ render_cell(struct terminal *term, pixman_image_t *pix,
|
||||||
uint32_t _fg = 0;
|
uint32_t _fg = 0;
|
||||||
uint32_t _bg = 0;
|
uint32_t _bg = 0;
|
||||||
|
|
||||||
bool apply_alpha = false;
|
uint16_t alpha = 0xffff;
|
||||||
|
|
||||||
if (is_selected && term->colors.use_custom_selection) {
|
if (is_selected && term->colors.use_custom_selection) {
|
||||||
_fg = term->colors.selection_fg;
|
_fg = term->colors.selection_fg;
|
||||||
|
|
@ -472,14 +487,14 @@ render_cell(struct terminal *term, pixman_image_t *pix,
|
||||||
uint32_t swap = _fg;
|
uint32_t swap = _fg;
|
||||||
_fg = _bg;
|
_fg = _bg;
|
||||||
_bg = swap;
|
_bg = swap;
|
||||||
} else
|
} else if (!cell->attrs.have_bg)
|
||||||
apply_alpha = !cell->attrs.have_bg;
|
alpha = term->colors.alpha;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(is_selected && _fg == _bg)) {
|
if (unlikely(is_selected && _fg == _bg)) {
|
||||||
/* Invert bg when selected/highlighted text has same fg/bg */
|
/* Invert bg when selected/highlighted text has same fg/bg */
|
||||||
_bg = ~_bg;
|
_bg = ~_bg;
|
||||||
apply_alpha = false;
|
alpha = 0xffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cell->attrs.dim)
|
if (cell->attrs.dim)
|
||||||
|
|
@ -491,8 +506,7 @@ render_cell(struct terminal *term, pixman_image_t *pix,
|
||||||
_fg = color_dim(_fg);
|
_fg = color_dim(_fg);
|
||||||
|
|
||||||
pixman_color_t fg = color_hex_to_pixman(_fg);
|
pixman_color_t fg = color_hex_to_pixman(_fg);
|
||||||
pixman_color_t bg = color_hex_to_pixman_with_alpha(
|
pixman_color_t bg = color_hex_to_pixman_with_alpha(_bg, alpha);
|
||||||
_bg, apply_alpha ? term->colors.alpha : 0xffff);
|
|
||||||
|
|
||||||
if (term->is_searching && !is_selected) {
|
if (term->is_searching && !is_selected) {
|
||||||
color_dim_for_search(&fg);
|
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));
|
cell_cols = max(1, min(cell_cols, cols_left));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Hack!
|
* Determine cells that will bleed into their right neighbor and remember
|
||||||
*
|
* them for cleanup in the next frame.
|
||||||
* 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
|
|
||||||
*/
|
*/
|
||||||
if (term->conf->tweak.allow_overflowing_double_width_glyphs &&
|
int render_width = cell_cols * width;
|
||||||
((glyph_count > 0 &&
|
if (term->conf->tweak.overflowing_glyphs &&
|
||||||
cell_cols == 1 &&
|
glyph_count > 0)
|
||||||
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' '))
|
|
||||||
{
|
{
|
||||||
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;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
if (glyph_width > render_width) {
|
||||||
* Ensure the cell we’re overflowing into gets re-rendered, to
|
render_width = min(glyph_width, render_width + width);
|
||||||
* 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
|
for (int i = 0; i < cell_cols; i++)
|
||||||
* the cell if nothing else on the row has changed.
|
row->cells[col + i].attrs.confined = false;
|
||||||
*/
|
}
|
||||||
row->cells[col].attrs.clean = 0;
|
|
||||||
row->cells[col + 1].attrs.clean = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pixman_region32_t clip;
|
pixman_region32_t clip;
|
||||||
pixman_region32_init_rect(
|
pixman_region32_init_rect(
|
||||||
&clip, x, y,
|
&clip, x, y,
|
||||||
cell_cols * term->cell_width, term->cell_height);
|
render_width, term->cell_height);
|
||||||
pixman_image_set_clip_region32(pix, &clip);
|
pixman_image_set_clip_region32(pix, &clip);
|
||||||
|
|
||||||
/* Background */
|
/* Background */
|
||||||
|
|
@ -772,6 +767,10 @@ static void
|
||||||
render_row(struct terminal *term, pixman_image_t *pix, struct row *row,
|
render_row(struct terminal *term, pixman_image_t *pix, struct row *row,
|
||||||
int row_no, int cursor_col)
|
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--)
|
for (int col = term->cols - 1; col >= 0; col--)
|
||||||
render_cell(term, pix, row, col, row_no, cursor_col == 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;
|
const int line_count = end_line - start_line;
|
||||||
|
|
||||||
uint32_t _bg = !term->reverse ? term->colors.bg : term->colors.fg;
|
uint32_t _bg = !term->reverse ? term->colors.bg : term->colors.fg;
|
||||||
if (term->is_searching)
|
pixman_color_t bg = color_hex_to_pixman_with_alpha(_bg, term->colors.alpha);
|
||||||
_bg = color_dim(_bg);
|
|
||||||
|
|
||||||
pixman_color_t bg = color_hex_to_pixman_with_alpha(
|
if (term->is_searching)
|
||||||
_bg,
|
color_dim_for_search(&bg);
|
||||||
(_bg == (term->reverse ? term->colors.fg : term->colors.bg)
|
|
||||||
? term->colors.alpha : 0xffff));
|
|
||||||
|
|
||||||
pixman_image_fill_rectangles(
|
pixman_image_fill_rectangles(
|
||||||
PIXMAN_OP_SRC, buf->pix[0], &bg, 4,
|
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) {
|
if (try_shm_scroll) {
|
||||||
did_shm_scroll = 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.top, dmg->region.start * term->cell_height,
|
||||||
term->margins.bottom, (term->rows - dmg->region.end) * 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);
|
term, buf, dmg->region.end - dmg->lines, term->rows, false);
|
||||||
} else {
|
} else {
|
||||||
/* Fallback for when we either cannot do SHM scrolling, or it failed */
|
/* 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,
|
memmove(raw + dst_y * buf->stride,
|
||||||
raw + src_y * buf->stride,
|
raw + src_y * buf->stride,
|
||||||
height * buf->stride);
|
height * buf->stride);
|
||||||
|
|
@ -1012,7 +1008,7 @@ grid_render_scroll_reverse(struct terminal *term, struct buffer *buf,
|
||||||
|
|
||||||
if (try_shm_scroll) {
|
if (try_shm_scroll) {
|
||||||
did_shm_scroll = 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.top, dmg->region.start * term->cell_height,
|
||||||
term->margins.bottom, (term->rows - dmg->region.end) * 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);
|
term, buf, dmg->region.start, dmg->region.start + dmg->lines, false);
|
||||||
} else {
|
} else {
|
||||||
/* Fallback for when we either cannot do SHM scrolling, or it failed */
|
/* 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,
|
memmove(raw + dst_y * buf->stride,
|
||||||
raw + src_y * buf->stride,
|
raw + src_y * buf->stride,
|
||||||
height * buf->stride);
|
height * buf->stride);
|
||||||
|
|
@ -1196,8 +1192,10 @@ render_sixel(struct terminal *term, pixman_image_t *pix,
|
||||||
(last_col_needs_erase && last_col))
|
(last_col_needs_erase && last_col))
|
||||||
{
|
{
|
||||||
render_cell(term, pix, row, col, term_row_no, cursor_col == col);
|
render_cell(term, pix, row, col, term_row_no, cursor_col == col);
|
||||||
} else
|
} else {
|
||||||
cell->attrs.clean = 1;
|
cell->attrs.clean = 1;
|
||||||
|
cell->attrs.confined = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1577,21 +1575,16 @@ render_csd_part(struct terminal *term,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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);
|
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;
|
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);
|
||||||
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);
|
|
||||||
|
|
||||||
uint32_t _color = term->conf->colors.fg;
|
uint32_t _color = term->conf->colors.fg;
|
||||||
uint16_t alpha = 0xffff;
|
uint16_t alpha = 0xffff;
|
||||||
|
|
@ -1605,31 +1598,27 @@ render_csd_title(struct terminal *term)
|
||||||
_color = color_dim(_color);
|
_color = color_dim(_color);
|
||||||
|
|
||||||
pixman_color_t color = color_hex_to_pixman_with_alpha(_color, alpha);
|
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);
|
csd_commit(term, surf, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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(term->window->csd_mode == CSD_YES);
|
||||||
xassert(surf_idx >= CSD_SURF_LEFT && surf_idx <= CSD_SURF_BOTTOM);
|
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;
|
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;
|
return;
|
||||||
|
|
||||||
xassert(info.width % term->scale == 0);
|
xassert(info->width % term->scale == 0);
|
||||||
xassert(info.height % 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);
|
|
||||||
|
|
||||||
pixman_color_t color = color_hex_to_pixman_with_alpha(0, 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);
|
csd_commit(term, surf, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1796,23 +1785,19 @@ render_csd_button_close(struct terminal *term, struct buffer *buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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(term->window->csd_mode == CSD_YES);
|
||||||
xassert(surf_idx >= CSD_SURF_MINIMIZE && surf_idx <= CSD_SURF_CLOSE);
|
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;
|
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;
|
return;
|
||||||
|
|
||||||
xassert(info.width % term->scale == 0);
|
xassert(info->width % term->scale == 0);
|
||||||
xassert(info.height % 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);
|
|
||||||
|
|
||||||
uint32_t _color;
|
uint32_t _color;
|
||||||
uint16_t alpha = 0xffff;
|
uint16_t alpha = 0xffff;
|
||||||
|
|
@ -1861,7 +1846,7 @@ render_csd_button(struct terminal *term, enum csd_surface surf_idx)
|
||||||
_color = color_dim(_color);
|
_color = color_dim(_color);
|
||||||
|
|
||||||
pixman_color_t color = color_hex_to_pixman_with_alpha(_color, alpha);
|
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) {
|
switch (surf_idx) {
|
||||||
case CSD_SURF_MINIMIZE: render_csd_button_minimize(term, buf); break;
|
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)
|
if (term->window->is_fullscreen)
|
||||||
return;
|
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++) {
|
for (size_t i = 0; i < CSD_SURF_COUNT; i++) {
|
||||||
struct csd_data info = get_csd_data(term, i);
|
infos[i] = get_csd_data(term, i);
|
||||||
const int x = info.x;
|
const int x = infos[i].x;
|
||||||
const int y = info.y;
|
const int y = infos[i].y;
|
||||||
const int width = info.width;
|
const int width = infos[i].width;
|
||||||
const int height = info.height;
|
const int height = infos[i].height;
|
||||||
|
|
||||||
struct wl_surface *surf = term->window->csd.surface[i].surf;
|
struct wl_surface *surf = term->window->csd.surface[i].surf;
|
||||||
struct wl_subsurface *sub = term->window->csd.surface[i].sub;
|
struct wl_subsurface *sub = term->window->csd.surface[i].sub;
|
||||||
|
|
@ -1899,20 +1888,27 @@ render_csd(struct terminal *term)
|
||||||
xassert(sub != NULL);
|
xassert(sub != NULL);
|
||||||
|
|
||||||
if (width == 0 || height == 0) {
|
if (width == 0 || height == 0) {
|
||||||
|
widths[i] = heights[i] = 0;
|
||||||
wl_subsurface_set_position(sub, 0, 0);
|
wl_subsurface_set_position(sub, 0, 0);
|
||||||
wl_surface_attach(surf, NULL, 0, 0);
|
wl_surface_attach(surf, NULL, 0, 0);
|
||||||
wl_surface_commit(surf);
|
wl_surface_commit(surf);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
widths[i] = width;
|
||||||
|
heights[i] = height;
|
||||||
|
|
||||||
wl_subsurface_set_position(sub, x / term->scale, y / term->scale);
|
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++)
|
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++)
|
for (size_t i = CSD_SURF_MINIMIZE; i <= CSD_SURF_CLOSE; i++)
|
||||||
render_csd_button(term, i);
|
render_csd_button(term, i, &infos[i], bufs[i]);
|
||||||
render_csd_title(term);
|
render_csd_title(term, &infos[CSD_SURF_TITLE], bufs[CSD_SURF_TITLE]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -2051,10 +2047,6 @@ render_scrollback_position(struct terminal *term)
|
||||||
const int height =
|
const int height =
|
||||||
(2 * margin + term->cell_height + scale - 1) / scale * scale;
|
(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 */
|
/* *Where* to render - parent relative coordinates */
|
||||||
int surf_top = 0;
|
int surf_top = 0;
|
||||||
switch (term->conf->scrollback.indicator.position) {
|
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 */
|
/* Make sure we don't collide with the scrollback search box */
|
||||||
lines--;
|
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);
|
surf_top = term->cell_height - margin + (int)(percent * pixels);
|
||||||
break;
|
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(
|
wl_subsurface_set_position(
|
||||||
win->scrollback_indicator.sub,
|
win->scrollback_indicator.sub, x / scale, y / scale);
|
||||||
(term->width - margin - width) / scale,
|
|
||||||
(term->margins.top + surf_top) / scale);
|
|
||||||
|
|
||||||
render_osd(
|
render_osd(
|
||||||
term,
|
term,
|
||||||
|
|
@ -2092,7 +2095,6 @@ render_scrollback_position(struct terminal *term)
|
||||||
buf, text,
|
buf, text,
|
||||||
term->colors.table[0], term->colors.table[8 + 4],
|
term->colors.table[0], term->colors.table[8 + 4],
|
||||||
width - margin - wcslen(text) * term->cell_width, margin);
|
width - margin - wcslen(text) * term->cell_width, margin);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -2112,9 +2114,8 @@ render_render_timer(struct terminal *term, struct timeval render_time)
|
||||||
const int height =
|
const int height =
|
||||||
(2 * margin + term->cell_height + scale - 1) / scale * scale;
|
(2 * margin + term->cell_height + scale - 1) / scale * scale;
|
||||||
|
|
||||||
unsigned long cookie = shm_cookie_render_timer(term);
|
struct buffer_chain *chain = term->render.chains.render_timer;
|
||||||
struct buffer *buf = shm_get_buffer(
|
struct buffer *buf = shm_get_buffer(chain, width, height);
|
||||||
term->wl->shm, width, height, cookie, false, 1);
|
|
||||||
|
|
||||||
wl_subsurface_set_position(
|
wl_subsurface_set_position(
|
||||||
win->render_timer.sub,
|
win->render_timer.sub,
|
||||||
|
|
@ -2157,7 +2158,7 @@ reapply_old_damage(struct terminal *term, struct buffer *new, struct buffer *old
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new->age > 1) {
|
if (new->age > 1) {
|
||||||
memcpy(new->mmapped, old->mmapped, new->size);
|
memcpy(new->data, old->data, new->height * new->stride);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2287,9 +2288,8 @@ grid_render(struct terminal *term)
|
||||||
xassert(term->width > 0);
|
xassert(term->width > 0);
|
||||||
xassert(term->height > 0);
|
xassert(term->height > 0);
|
||||||
|
|
||||||
unsigned long cookie = shm_cookie_grid(term);
|
struct buffer_chain *chain = term->render.chains.grid;
|
||||||
struct buffer *buf = shm_get_buffer(
|
struct buffer *buf = shm_get_buffer(chain, term->width, term->height);
|
||||||
term->wl->shm, term->width, term->height, cookie, true, 1 + term->render.workers.count);
|
|
||||||
|
|
||||||
/* Dirty old and current cursor cell, to ensure they’re repainted */
|
/* Dirty old and current cursor cell, to ensure they’re repainted */
|
||||||
dirty_old_cursor(term);
|
dirty_old_cursor(term);
|
||||||
|
|
@ -2306,7 +2306,7 @@ grid_render(struct terminal *term)
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (buf->age > 0) {
|
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 != NULL);
|
||||||
xassert(term->render.last_buf != buf);
|
xassert(term->render.last_buf != buf);
|
||||||
|
|
@ -2319,17 +2319,18 @@ grid_render(struct terminal *term)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (term->render.last_buf != NULL) {
|
if (term->render.last_buf != NULL) {
|
||||||
free(term->render.last_buf->scroll_damage);
|
shm_unref(term->render.last_buf);
|
||||||
term->render.last_buf->scroll_damage = NULL;
|
term->render.last_buf = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
term->render.last_buf = buf;
|
term->render.last_buf = buf;
|
||||||
term->render.was_flashing = term->flash.active;
|
term->render.was_flashing = term->flash.active;
|
||||||
term->render.was_searching = term->is_searching;
|
term->render.was_searching = term->is_searching;
|
||||||
|
|
||||||
|
shm_addref(buf);
|
||||||
buf->age = 0;
|
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_count = tll_length(term->grid->scroll_damage);
|
||||||
buf->scroll_damage = xmalloc(
|
buf->scroll_damage = xmalloc(
|
||||||
buf->scroll_damage_count * sizeof(buf->scroll_damage[0]));
|
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;
|
const size_t visible_cells = (visible_width - 2 * margin) / term->cell_width;
|
||||||
size_t glyph_offset = term->render.search_glyph_offset;
|
size_t glyph_offset = term->render.search_glyph_offset;
|
||||||
|
|
||||||
unsigned long cookie = shm_cookie_search(term);
|
struct buffer_chain *chain = term->render.chains.search;
|
||||||
struct buffer *buf = shm_get_buffer(term->wl->shm, width, height, cookie, false, 1);
|
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_X(x) (margin + x)
|
||||||
#define WINDOW_Y(y) (term->height - margin - height + y)
|
#define WINDOW_Y(y) (term->height - margin - height + y)
|
||||||
|
|
@ -2875,6 +2882,10 @@ render_urls(struct terminal *term)
|
||||||
struct wl_window *win = term->window;
|
struct wl_window *win = term->window;
|
||||||
xassert(tll_length(win->urls) > 0);
|
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 */
|
/* Calculate view start, counted from the *current* scrollback start */
|
||||||
const int scrollback_end
|
const int scrollback_end
|
||||||
= (term->grid->offset + term->rows) & (term->grid->num_rows - 1);
|
= (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;
|
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) {
|
tll_foreach(win->urls, it) {
|
||||||
const struct url *url = it->item.url;
|
const struct url *url = it->item.url;
|
||||||
const wchar_t *key = url->key;
|
const wchar_t *key = url->key;
|
||||||
|
|
@ -2942,6 +2992,7 @@ render_urls(struct terminal *term)
|
||||||
/* Maximum width of label, in pixels */
|
/* Maximum width of label, in pixels */
|
||||||
const int max_width =
|
const int max_width =
|
||||||
term->width - term->margins.left - term->margins.right - x;
|
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);
|
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
|
* Do it in a way such that we don’t cut the label in the
|
||||||
* middle of a double-width character.
|
* 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;
|
int cols = 0;
|
||||||
|
|
||||||
|
|
@ -3010,23 +3057,48 @@ render_urls(struct terminal *term)
|
||||||
const int height =
|
const int height =
|
||||||
(2 * y_margin + term->cell_height + scale - 1) / scale * scale;
|
(2 * y_margin + term->cell_height + scale - 1) / scale * scale;
|
||||||
|
|
||||||
struct buffer *buf = shm_get_buffer(
|
info[render_count].url = &it->item;
|
||||||
term->wl->shm, width, height, shm_cookie_url(url), false, 1);
|
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(
|
wl_subsurface_set_position(
|
||||||
sub_surf,
|
sub_surf,
|
||||||
(term->margins.left + x) / term->scale,
|
(term->margins.left + x) / term->scale,
|
||||||
(term->margins.top + y) / 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(
|
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)
|
if (events & EPOLLIN)
|
||||||
tiocswinsz(term);
|
tiocswinsz(term);
|
||||||
|
|
||||||
fdm_del(fdm, fd);
|
if (term->window->resize_timeout_fd >= 0) {
|
||||||
term->window->resize_timeout_fd = -1;
|
fdm_del(fdm, term->window->resize_timeout_fd);
|
||||||
|
term->window->resize_timeout_fd = -1;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3405,6 +3479,7 @@ damage_view:
|
||||||
tll_free(term->normal.scroll_damage);
|
tll_free(term->normal.scroll_damage);
|
||||||
tll_free(term->alt.scroll_damage);
|
tll_free(term->alt.scroll_damage);
|
||||||
|
|
||||||
|
shm_unref(term->render.last_buf);
|
||||||
term->render.last_buf = NULL;
|
term->render.last_buf = NULL;
|
||||||
term_damage_view(term);
|
term_damage_view(term);
|
||||||
render_refresh_csd(term);
|
render_refresh_csd(term);
|
||||||
|
|
|
||||||
|
|
@ -69,10 +69,8 @@ selection_on_rows(const struct terminal *term, int row_start, int row_end)
|
||||||
end = tmp;
|
end = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (row_start >= start->row && row_end <= end->row) {
|
if (row_start >= start->row && row_end <= end->row)
|
||||||
LOG_INFO("ON ROWS");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
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 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->terminal = NULL;
|
||||||
instance_destroy(instance, exit_code);
|
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 off_t max_pool_size = 512 * 1024 * 1024;
|
||||||
|
|
||||||
static tll(struct buffer) buffers;
|
|
||||||
|
|
||||||
static bool can_punch_hole = false;
|
static bool can_punch_hole = false;
|
||||||
static bool can_punch_hole_initialized = 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
|
#undef MEASURE_SHM_ALLOCS
|
||||||
#if defined(MEASURE_SHM_ALLOCS)
|
#if defined(MEASURE_SHM_ALLOCS)
|
||||||
static size_t max_alloced = 0;
|
static size_t max_alloced = 0;
|
||||||
|
|
@ -85,34 +117,70 @@ buffer_destroy_dont_close(struct buffer *buf)
|
||||||
free(buf->pix);
|
free(buf->pix);
|
||||||
buf->pix = NULL;
|
buf->pix = NULL;
|
||||||
buf->wl_buf = NULL;
|
buf->wl_buf = NULL;
|
||||||
buf->mmapped = NULL;
|
buf->data = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
buffer_destroy(struct buffer *buf)
|
pool_unref(struct buffer_pool *pool)
|
||||||
{
|
{
|
||||||
buffer_destroy_dont_close(buf);
|
if (pool == NULL)
|
||||||
if (buf->real_mmapped != MAP_FAILED)
|
return;
|
||||||
munmap(buf->real_mmapped, buf->mmap_size);
|
|
||||||
if (buf->pool != NULL)
|
|
||||||
wl_shm_pool_destroy(buf->pool);
|
|
||||||
if (buf->fd >= 0)
|
|
||||||
close(buf->fd);
|
|
||||||
|
|
||||||
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->pool = NULL;
|
||||||
buf->fd = -1;
|
|
||||||
|
|
||||||
free(buf->scroll_damage);
|
free(buf->public.scroll_damage);
|
||||||
pixman_region32_fini(&buf->dirty);
|
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
|
void
|
||||||
shm_fini(void)
|
shm_fini(void)
|
||||||
{
|
{
|
||||||
tll_foreach(buffers, it) {
|
LOG_DBG("deferred buffers: %zu", tll_length(deferred));
|
||||||
buffer_destroy(&it->item);
|
|
||||||
tll_remove(buffers, it);
|
tll_foreach(deferred, it) {
|
||||||
|
buffer_destroy(it->item);
|
||||||
|
tll_remove(deferred, it);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(MEASURE_SHM_ALLOCS) && MEASURE_SHM_ALLOCS
|
#if defined(MEASURE_SHM_ALLOCS) && MEASURE_SHM_ALLOCS
|
||||||
|
|
@ -123,11 +191,28 @@ shm_fini(void)
|
||||||
static void
|
static void
|
||||||
buffer_release(void *data, struct wl_buffer *wl_buffer)
|
buffer_release(void *data, struct wl_buffer *wl_buffer)
|
||||||
{
|
{
|
||||||
struct buffer *buffer = data;
|
struct buffer_private *buffer = data;
|
||||||
LOG_DBG("release: cookie=%lx (buf=%p)", buffer->cookie, (void *)buffer);
|
|
||||||
xassert(buffer->wl_buf == wl_buffer);
|
xassert(buffer->public.wl_buf == wl_buffer);
|
||||||
xassert(buffer->busy);
|
xassert(buffer->busy);
|
||||||
buffer->busy = false;
|
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 = {
|
static const struct wl_buffer_listener buffer_listener = {
|
||||||
|
|
@ -154,47 +239,53 @@ page_size(void)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static bool
|
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->public.data == NULL);
|
||||||
xassert(buf->mmapped == NULL);
|
xassert(buf->public.pix == NULL);
|
||||||
xassert(buf->wl_buf == NULL);
|
xassert(buf->public.wl_buf == NULL);
|
||||||
xassert(buf->pix == NULL);
|
xassert(buf->pool != NULL);
|
||||||
|
|
||||||
|
const struct buffer_pool *pool = buf->pool;
|
||||||
|
|
||||||
void *mmapped = MAP_FAILED;
|
void *mmapped = MAP_FAILED;
|
||||||
struct wl_buffer *wl_buf = NULL;
|
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(
|
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) {
|
if (wl_buf == NULL) {
|
||||||
LOG_ERR("failed to create SHM buffer");
|
LOG_ERR("failed to create SHM buffer");
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* One pixman image for each worker thread (do we really need multiple?) */
|
/* 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(
|
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) {
|
if (pix[i] == NULL) {
|
||||||
LOG_ERR("failed to create pixman image");
|
LOG_ERR("failed to create pixman image");
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buf->public.data = mmapped;
|
||||||
|
buf->public.wl_buf = wl_buf;
|
||||||
|
buf->public.pix = pix;
|
||||||
buf->offset = new_offset;
|
buf->offset = new_offset;
|
||||||
buf->mmapped = mmapped;
|
|
||||||
buf->wl_buf = wl_buf;
|
|
||||||
buf->pix = pix;
|
|
||||||
|
|
||||||
wl_buffer_add_listener(wl_buf, &buffer_listener, buf);
|
wl_buffer_add_listener(wl_buf, &buffer_listener, buf);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
if (pix != NULL) {
|
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)
|
if (pix[i] != NULL)
|
||||||
pixman_image_unref(pix[i]);
|
pixman_image_unref(pix[i]);
|
||||||
}
|
}
|
||||||
|
|
@ -206,76 +297,12 @@ err:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct buffer *
|
static void NOINLINE
|
||||||
shm_get_buffer(struct wl_shm *shm, int width, int height, unsigned long cookie, bool scrollable, size_t pix_instances)
|
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 */
|
xassert(count == 1 || !chain->scrollable);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* No existing buffer available. Create a new one by:
|
* 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.
|
* 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;
|
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;
|
void *real_mmapped = MAP_FAILED;
|
||||||
struct wl_shm_pool *pool = NULL;
|
struct wl_shm_pool *wl_pool = NULL;
|
||||||
|
struct buffer_pool *pool = NULL;
|
||||||
LOG_DBG("cookie=%lx: allocating new buffer: %zu KB", cookie, size / 1024);
|
|
||||||
|
|
||||||
/* Backing memory for SHM */
|
/* Backing memory for SHM */
|
||||||
#if defined(MEMFD_CREATE)
|
#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
|
#if __SIZEOF_POINTER__ == 8
|
||||||
off_t initial_offset = scrollable && max_pool_size > 0 ? (max_pool_size / 4) & ~(page_size() - 1) : 0;
|
off_t offset = chain->scrollable && max_pool_size > 0
|
||||||
off_t memfd_size = scrollable && max_pool_size > 0 ? max_pool_size : size;
|
? (max_pool_size / 4) & ~(page_size() - 1)
|
||||||
|
: 0;
|
||||||
|
off_t memfd_size = chain->scrollable && max_pool_size > 0
|
||||||
|
? max_pool_size
|
||||||
|
: total_size;
|
||||||
#else
|
#else
|
||||||
off_t initial_offset = 0;
|
off_t offset = 0;
|
||||||
off_t memfd_size = size;
|
off_t memfd_size = total_size;
|
||||||
#endif
|
#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) {
|
if (ftruncate(pool_fd, memfd_size) == -1) {
|
||||||
LOG_ERRNO("failed to set size of SHM backing memory file");
|
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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scrollable && !can_punch_hole) {
|
if (chain->scrollable && !can_punch_hole) {
|
||||||
initial_offset = 0;
|
offset = 0;
|
||||||
memfd_size = size;
|
memfd_size = total_size;
|
||||||
scrollable = false;
|
chain->scrollable = false;
|
||||||
|
|
||||||
if (ftruncate(pool_fd, memfd_size) < 0) {
|
if (ftruncate(pool_fd, memfd_size) < 0) {
|
||||||
LOG_ERRNO("failed to set size of SHM backing memory file");
|
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
|
#endif
|
||||||
|
|
||||||
pool = wl_shm_create_pool(shm, pool_fd, memfd_size);
|
wl_pool = wl_shm_create_pool(chain->shm, pool_fd, memfd_size);
|
||||||
if (pool == NULL) {
|
if (wl_pool == NULL) {
|
||||||
LOG_ERR("failed to create SHM pool");
|
LOG_ERR("failed to create SHM pool");
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Push to list of available buffers, but marked as 'busy' */
|
pool = xmalloc(sizeof(*pool));
|
||||||
tll_push_back(
|
if (pool == NULL) {
|
||||||
buffers,
|
LOG_ERRNO("failed to allocate buffer pool");
|
||||||
((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))
|
|
||||||
goto err;
|
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
|
#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
|
#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:
|
err:
|
||||||
if (pool != NULL)
|
pool_unref(pool);
|
||||||
wl_shm_pool_destroy(pool);
|
if (wl_pool != NULL)
|
||||||
|
wl_shm_pool_destroy(wl_pool);
|
||||||
if (real_mmapped != MAP_FAILED)
|
if (real_mmapped != MAP_FAILED)
|
||||||
munmap(real_mmapped, memfd_size);
|
munmap(real_mmapped, memfd_size);
|
||||||
if (pool_fd != -1)
|
if (pool_fd != -1)
|
||||||
|
|
@ -429,13 +506,80 @@ err:
|
||||||
|
|
||||||
/* We don't handle this */
|
/* We don't handle this */
|
||||||
abort();
|
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
|
bool
|
||||||
shm_can_scroll(const struct buffer *buf)
|
shm_can_scroll(const struct buffer *_buf)
|
||||||
{
|
{
|
||||||
#if __SIZEOF_POINTER__ == 8
|
#if __SIZEOF_POINTER__ == 8
|
||||||
|
const struct buffer_private *buf = (const struct buffer_private *)_buf;
|
||||||
return can_punch_hole && max_pool_size > 0 && buf->scrollable;
|
return can_punch_hole && max_pool_size > 0 && buf->scrollable;
|
||||||
#else
|
#else
|
||||||
/* Not enough virtual address space in 32-bit */
|
/* 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)
|
#if __SIZEOF_POINTER__ == 8 && defined(FALLOC_FL_PUNCH_HOLE)
|
||||||
static bool
|
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 */
|
/* We don't allow overlapping offsets */
|
||||||
off_t UNUSED diff =
|
off_t UNUSED diff = new_offset < buf->offset
|
||||||
new_offset < buf->offset ? buf->offset - new_offset : new_offset - buf->offset;
|
? buf->offset - new_offset
|
||||||
|
: new_offset - buf->offset;
|
||||||
xassert(diff > buf->size);
|
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;
|
off_t trim_ofs, trim_len;
|
||||||
if (new_offset > buf->offset) {
|
if (new_offset > buf->offset) {
|
||||||
|
|
@ -462,11 +612,11 @@ wrap_buffer(struct wl_shm *shm, struct buffer *buf, off_t new_offset)
|
||||||
} else {
|
} else {
|
||||||
/* Trim everything *after* the new buffer location */
|
/* Trim everything *after* the new buffer location */
|
||||||
trim_ofs = new_offset + buf->size;
|
trim_ofs = new_offset + buf->size;
|
||||||
trim_len = buf->mmap_size - trim_ofs;
|
trim_len = pool->mmap_size - trim_ofs;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fallocate(
|
if (fallocate(
|
||||||
buf->fd,
|
pool->fd,
|
||||||
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
|
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
|
||||||
trim_ofs, trim_len) < 0)
|
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 */
|
/* Re-instantiate pixman+wl_buffer+raw pointersw */
|
||||||
buffer_destroy_dont_close(buf);
|
buffer_destroy_dont_close(&buf->public);
|
||||||
return instantiate_offset(shm, buf, new_offset);
|
return instantiate_offset(buf, new_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
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 top_margin, int top_keep_rows,
|
||||||
int bottom_margin, int bottom_keep_rows)
|
int bottom_margin, int bottom_keep_rows)
|
||||||
{
|
{
|
||||||
|
struct buffer_pool *pool = buf->pool;
|
||||||
|
|
||||||
xassert(can_punch_hole);
|
xassert(can_punch_hole);
|
||||||
xassert(buf->busy);
|
xassert(buf->busy);
|
||||||
xassert(buf->pix);
|
xassert(buf->public.pix != NULL);
|
||||||
xassert(buf->wl_buf);
|
xassert(buf->public.wl_buf != NULL);
|
||||||
xassert(buf->fd >= 0);
|
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(rows > 0);
|
||||||
xassert(diff < buf->size);
|
xassert(diff < buf->size);
|
||||||
|
|
||||||
if (buf->offset + diff + buf->size > max_pool_size) {
|
if (buf->offset + diff + buf->size > max_pool_size) {
|
||||||
LOG_DBG("memfd offset wrap around");
|
LOG_DBG("memfd offset wrap around");
|
||||||
if (!wrap_buffer(shm, buf, 0))
|
if (!wrap_buffer(buf, 0))
|
||||||
goto err;
|
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);
|
xassert(new_offset + buf->size <= max_pool_size);
|
||||||
|
|
||||||
#if TIME_SCROLL
|
#if TIME_SCROLL
|
||||||
|
struct timeval tot;
|
||||||
struct timeval time1;
|
struct timeval time1;
|
||||||
gettimeofday(&time1, NULL);
|
gettimeofday(&time1, NULL);
|
||||||
|
|
||||||
|
|
@ -515,10 +670,13 @@ shm_scroll_forward(struct wl_shm *shm, struct buffer *buf, int rows,
|
||||||
|
|
||||||
if (top_keep_rows > 0) {
|
if (top_keep_rows > 0) {
|
||||||
/* Copy current 'top' region to its new location */
|
/* Copy current 'top' region to its new location */
|
||||||
|
const int stride = buf->public.stride;
|
||||||
|
uint8_t *base = buf->public.data;
|
||||||
|
|
||||||
memmove(
|
memmove(
|
||||||
(uint8_t *)buf->mmapped + (top_margin + rows) * buf->stride,
|
base + (top_margin + rows) * stride,
|
||||||
(uint8_t *)buf->mmapped + (top_margin + 0) * buf->stride,
|
base + (top_margin + 0) * stride,
|
||||||
top_keep_rows * buf->stride);
|
top_keep_rows * stride);
|
||||||
|
|
||||||
#if TIME_SCROLL
|
#if TIME_SCROLL
|
||||||
gettimeofday(&time2, NULL);
|
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) */
|
/* 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 */
|
/* Free unused memory - everything up until the new offset */
|
||||||
const off_t trim_ofs = 0;
|
const off_t trim_ofs = 0;
|
||||||
const off_t trim_len = new_offset;
|
const off_t trim_len = new_offset;
|
||||||
|
|
||||||
if (fallocate(
|
if (fallocate(
|
||||||
buf->fd,
|
pool->fd,
|
||||||
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
|
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
|
||||||
trim_ofs, trim_len) < 0)
|
trim_ofs, trim_len) < 0)
|
||||||
{
|
{
|
||||||
|
|
@ -551,7 +709,7 @@ shm_scroll_forward(struct wl_shm *shm, struct buffer *buf, int rows,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Re-instantiate pixman+wl_buffer+raw pointersw */
|
/* 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
|
#if TIME_SCROLL
|
||||||
struct timeval time4;
|
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) {
|
if (ret && bottom_keep_rows > 0) {
|
||||||
/* Copy 'bottom' region to its new location */
|
/* 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(
|
memmove(
|
||||||
(uint8_t *)buf->mmapped + buf->size - (bottom_margin + bottom_keep_rows) * buf->stride,
|
base + size - (bottom_margin + bottom_keep_rows) * stride,
|
||||||
(uint8_t *)buf->mmapped + buf->size - (bottom_margin + rows + bottom_keep_rows) * buf->stride,
|
base + size - (bottom_margin + rows + bottom_keep_rows) * stride,
|
||||||
bottom_keep_rows * buf->stride);
|
bottom_keep_rows * stride);
|
||||||
|
|
||||||
#if TIME_SCROLL
|
#if TIME_SCROLL
|
||||||
struct timeval time5;
|
struct timeval time5;
|
||||||
|
|
@ -584,16 +746,19 @@ err:
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
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 top_margin, int top_keep_rows,
|
||||||
int bottom_margin, int bottom_keep_rows)
|
int bottom_margin, int bottom_keep_rows)
|
||||||
{
|
{
|
||||||
xassert(rows > 0);
|
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) {
|
if (diff > buf->offset) {
|
||||||
LOG_DBG("memfd offset reverse wrap-around");
|
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;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -611,10 +776,14 @@ shm_scroll_reverse(struct wl_shm *shm, struct buffer *buf, int rows,
|
||||||
|
|
||||||
if (bottom_keep_rows > 0) {
|
if (bottom_keep_rows > 0) {
|
||||||
/* Copy 'bottom' region to its new location */
|
/* 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(
|
memmove(
|
||||||
(uint8_t *)buf->mmapped + buf->size - (bottom_margin + rows + bottom_keep_rows) * buf->stride,
|
base + size - (bottom_margin + rows + bottom_keep_rows) * stride,
|
||||||
(uint8_t *)buf->mmapped + buf->size - (bottom_margin + bottom_keep_rows) * buf->stride,
|
base + size - (bottom_margin + bottom_keep_rows) * stride,
|
||||||
bottom_keep_rows * buf->stride);
|
bottom_keep_rows * stride);
|
||||||
|
|
||||||
#if TIME_SCROLL
|
#if TIME_SCROLL
|
||||||
gettimeofday(&time1, NULL);
|
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) */
|
/* 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 */
|
/* Free unused memory - everything after the relocated buffer */
|
||||||
const off_t trim_ofs = new_offset + buf->size;
|
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(
|
if (fallocate(
|
||||||
buf->fd,
|
pool->fd,
|
||||||
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
|
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
|
||||||
trim_ofs, trim_len) < 0)
|
trim_ofs, trim_len) < 0)
|
||||||
{
|
{
|
||||||
|
|
@ -646,7 +815,7 @@ shm_scroll_reverse(struct wl_shm *shm, struct buffer *buf, int rows,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Re-instantiate pixman+wl_buffer+raw pointers */
|
/* 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
|
#if TIME_SCROLL
|
||||||
struct timeval time3;
|
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) {
|
if (ret && top_keep_rows > 0) {
|
||||||
/* Copy current 'top' region to its new location */
|
/* Copy current 'top' region to its new location */
|
||||||
|
const int stride = buf->public.stride;
|
||||||
|
uint8_t *base = buf->public.data;
|
||||||
|
|
||||||
memmove(
|
memmove(
|
||||||
(uint8_t *)buf->mmapped + (top_margin + 0) * buf->stride,
|
base + (top_margin + 0) * stride,
|
||||||
(uint8_t *)buf->mmapped + (top_margin + rows) * buf->stride,
|
base + (top_margin + rows) * stride,
|
||||||
top_keep_rows * buf->stride);
|
top_keep_rows * stride);
|
||||||
|
|
||||||
#if TIME_SCROLL
|
#if TIME_SCROLL
|
||||||
struct timeval time4;
|
struct timeval time4;
|
||||||
|
|
@ -679,36 +851,88 @@ err:
|
||||||
#endif /* FALLOC_FL_PUNCH_HOLE */
|
#endif /* FALLOC_FL_PUNCH_HOLE */
|
||||||
|
|
||||||
bool
|
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 top_margin, int top_keep_rows,
|
||||||
int bottom_margin, int bottom_keep_rows)
|
int bottom_margin, int bottom_keep_rows)
|
||||||
{
|
{
|
||||||
#if __SIZEOF_POINTER__ == 8 && defined(FALLOC_FL_PUNCH_HOLE)
|
#if __SIZEOF_POINTER__ == 8 && defined(FALLOC_FL_PUNCH_HOLE)
|
||||||
if (!shm_can_scroll(buf))
|
if (!shm_can_scroll(_buf))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
struct buffer_private *buf = (struct buffer_private *)_buf;
|
||||||
|
|
||||||
xassert(rows != 0);
|
xassert(rows != 0);
|
||||||
return rows > 0
|
return rows > 0
|
||||||
? shm_scroll_forward(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(shm, 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
|
#else
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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 */
|
/* Purge old buffers associated with this cookie */
|
||||||
tll_foreach(buffers, it) {
|
tll_foreach(chain->bufs, it) {
|
||||||
if (it->item.cookie != cookie)
|
if (buffer_unref_no_remove_from_chain(it->item))
|
||||||
continue;
|
tll_remove(chain->bufs, it);
|
||||||
|
|
||||||
xassert(!it->item.busy);
|
|
||||||
|
|
||||||
buffer_destroy(&it->item);
|
|
||||||
tll_remove(buffers, 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 <pixman.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
|
|
||||||
#include "terminal.h"
|
#include <tllist.h>
|
||||||
|
|
||||||
|
struct damage;
|
||||||
|
|
||||||
struct buffer {
|
struct buffer {
|
||||||
unsigned long cookie;
|
|
||||||
|
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
int stride;
|
int stride;
|
||||||
|
|
||||||
bool busy;
|
void *data;
|
||||||
size_t size; /* Buffer size */
|
|
||||||
void *mmapped; /* Raw data (TODO: rename) */
|
|
||||||
|
|
||||||
struct wl_buffer *wl_buf;
|
struct wl_buffer *wl_buf;
|
||||||
pixman_image_t **pix;
|
pixman_image_t **pix;
|
||||||
size_t pix_instances;
|
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;
|
unsigned age;
|
||||||
|
|
||||||
struct damage *scroll_damage;
|
struct damage *scroll_damage;
|
||||||
size_t scroll_damage_count;
|
size_t scroll_damage_count;
|
||||||
pixman_region32_t dirty;
|
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_fini(void);
|
||||||
|
|
||||||
void shm_set_max_pool_size(off_t max_pool_size);
|
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_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 top_margin, int top_keep_rows,
|
||||||
int bottom_margin, int bottom_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;
|
void shm_purge(struct buffer_chain *chain);
|
||||||
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; }
|
|
||||||
|
|
|
||||||
5
sixel.c
5
sixel.c
|
|
@ -88,9 +88,10 @@ sixel_init(struct terminal *term, int p1, int p2, int p3)
|
||||||
void
|
void
|
||||||
sixel_destroy(struct sixel *sixel)
|
sixel_destroy(struct sixel *sixel)
|
||||||
{
|
{
|
||||||
pixman_image_unref(sixel->pix);
|
if (sixel->pix != NULL)
|
||||||
free(sixel->data);
|
pixman_image_unref(sixel->pix);
|
||||||
|
|
||||||
|
free(sixel->data);
|
||||||
sixel->pix = NULL;
|
sixel->pix = NULL;
|
||||||
sixel->data = NULL;
|
sixel->data = NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
228
terminal.c
228
terminal.c
|
|
@ -35,6 +35,7 @@
|
||||||
#include "selection.h"
|
#include "selection.h"
|
||||||
#include "sixel.h"
|
#include "sixel.h"
|
||||||
#include "slave.h"
|
#include "slave.h"
|
||||||
|
#include "shm.h"
|
||||||
#include "spawn.h"
|
#include "spawn.h"
|
||||||
#include "url-mode.h"
|
#include "url-mode.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
@ -1143,6 +1144,14 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
||||||
.tab_stops = tll_init(),
|
.tab_stops = tll_init(),
|
||||||
.wl = wayl,
|
.wl = wayl,
|
||||||
.render = {
|
.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,
|
.scrollback_lines = conf->scrollback.lines,
|
||||||
.app_sync_updates.timer_fd = app_sync_updates_fd,
|
.app_sync_updates.timer_fd = app_sync_updates_fd,
|
||||||
.title = {
|
.title = {
|
||||||
|
|
@ -1458,6 +1467,14 @@ term_destroy(struct terminal *term)
|
||||||
xassert(tll_length(term->render.workers.queue) == 0);
|
xassert(tll_length(term->render.workers.queue) == 0);
|
||||||
tll_free(term->render.workers.queue);
|
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_free(term->tab_stops);
|
||||||
|
|
||||||
tll_foreach(term->ptmx_buffers, it) {
|
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);
|
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
|
int
|
||||||
term_row_rel_to_abs(const struct terminal *term, int row)
|
term_row_rel_to_abs(const struct terminal *term, int row)
|
||||||
{
|
{
|
||||||
|
|
@ -2634,9 +2853,13 @@ term_xcursor_update(struct terminal *term)
|
||||||
void
|
void
|
||||||
term_set_window_title(struct terminal *term, const char *title)
|
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);
|
free(term->window_title);
|
||||||
term->window_title = xstrdup(title);
|
term->window_title = xstrdup(title);
|
||||||
render_refresh_title(term);
|
render_refresh_title(term);
|
||||||
|
term->window_title_has_been_set = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -2756,6 +2979,7 @@ print_linewrap(struct terminal *term)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
term->grid->cur_row->linebreak = false;
|
||||||
term->grid->cursor.lcf = false;
|
term->grid->cursor.lcf = false;
|
||||||
|
|
||||||
const int row = term->grid->cursor.point.row;
|
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;
|
cell->attrs = term->vt.attrs;
|
||||||
|
|
||||||
row->dirty = true;
|
row->dirty = true;
|
||||||
row->linebreak = false;
|
row->linebreak = true;
|
||||||
|
|
||||||
/* Advance cursor the 'additional' columns while dirty:ing the cells */
|
/* 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++) {
|
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;
|
cell->attrs = term->vt.attrs;
|
||||||
|
|
||||||
row->dirty = true;
|
row->dirty = true;
|
||||||
row->linebreak = false;
|
row->linebreak = true;
|
||||||
|
|
||||||
/* Advance cursor */
|
/* Advance cursor */
|
||||||
if (unlikely(++term->grid->cursor.point.col >= term->cols)) {
|
if (unlikely(++term->grid->cursor.point.col >= term->cols)) {
|
||||||
|
|
|
||||||
18
terminal.h
18
terminal.h
|
|
@ -21,6 +21,7 @@
|
||||||
#include "fdm.h"
|
#include "fdm.h"
|
||||||
#include "macros.h"
|
#include "macros.h"
|
||||||
#include "reaper.h"
|
#include "reaper.h"
|
||||||
|
#include "shm.h"
|
||||||
#include "wayland.h"
|
#include "wayland.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -42,11 +43,12 @@ struct attributes {
|
||||||
uint32_t fg:24;
|
uint32_t fg:24;
|
||||||
|
|
||||||
bool clean:1;
|
bool clean:1;
|
||||||
|
bool confined:1;
|
||||||
bool have_fg:1;
|
bool have_fg:1;
|
||||||
bool have_bg:1;
|
bool have_bg:1;
|
||||||
uint32_t selected:2;
|
uint32_t selected:2;
|
||||||
bool url:1;
|
bool url:1;
|
||||||
uint32_t reserved:2;
|
uint32_t reserved:1;
|
||||||
uint32_t bg:24;
|
uint32_t bg:24;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(struct attributes) == 8, "VT attribute struct too large");
|
static_assert(sizeof(struct attributes) == 8, "VT attribute struct too large");
|
||||||
|
|
@ -264,6 +266,7 @@ struct url {
|
||||||
enum url_action action;
|
enum url_action action;
|
||||||
bool url_mode_dont_change_url_attr; /* Entering/exiting URL mode doesn’t touch the cells’ attr.url */
|
bool url_mode_dont_change_url_attr; /* Entering/exiting URL mode doesn’t touch the cells’ attr.url */
|
||||||
bool osc8;
|
bool osc8;
|
||||||
|
bool duplicate;
|
||||||
};
|
};
|
||||||
typedef tll(struct url) url_list_t;
|
typedef tll(struct url) url_list_t;
|
||||||
|
|
||||||
|
|
@ -377,11 +380,12 @@ struct terminal {
|
||||||
bool ime:1;
|
bool ime:1;
|
||||||
bool app_sync_updates:1;
|
bool app_sync_updates:1;
|
||||||
|
|
||||||
bool sixel_scrolling:1;
|
bool sixel_display_mode:1;
|
||||||
bool sixel_private_palette:1;
|
bool sixel_private_palette:1;
|
||||||
bool sixel_cursor_right_of_graphics:1;
|
bool sixel_cursor_right_of_graphics:1;
|
||||||
} xtsave;
|
} xtsave;
|
||||||
|
|
||||||
|
bool window_title_has_been_set;
|
||||||
char *window_title;
|
char *window_title;
|
||||||
tll(char *) window_title_stack;
|
tll(char *) window_title_stack;
|
||||||
|
|
||||||
|
|
@ -472,6 +476,15 @@ struct terminal {
|
||||||
enum term_surface active_surface;
|
enum term_surface active_surface;
|
||||||
|
|
||||||
struct {
|
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 */
|
/* Scheduled for rendering, as soon-as-possible */
|
||||||
struct {
|
struct {
|
||||||
bool grid;
|
bool grid;
|
||||||
|
|
@ -649,6 +662,7 @@ void term_damage_scroll(
|
||||||
|
|
||||||
void term_erase(
|
void term_erase(
|
||||||
struct terminal *term, const struct coord *start, const struct coord *end);
|
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);
|
int term_row_rel_to_abs(const struct terminal *term, int row);
|
||||||
void term_cursor_home(struct terminal *term);
|
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);
|
xassert(in->osc8 || out->osc8);
|
||||||
|
|
||||||
if (in->osc8) {
|
if (in->osc8)
|
||||||
url_destroy(&outer->item);
|
outer->item.duplicate = true;
|
||||||
tll_remove(*urls, outer);
|
else
|
||||||
} else {
|
inner->item.duplicate = true;
|
||||||
url_destroy(&inner->item);
|
|
||||||
tll_remove(*urls, inner);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tll_foreach(*urls, it) {
|
||||||
|
if (it->item.duplicate) {
|
||||||
|
url_destroy(&it->item);
|
||||||
|
tll_remove(*urls, it);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -649,6 +653,11 @@ tag_cells_for_url(struct terminal *term, const struct url *url, bool value)
|
||||||
c = 0;
|
c = 0;
|
||||||
|
|
||||||
row = term->grid->rows[r];
|
row = term->grid->rows[r];
|
||||||
|
if (row == NULL) {
|
||||||
|
/* Un-allocated scrollback. This most likely means a
|
||||||
|
* runaway OSC-8 URL. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
row->dirty = true;
|
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");
|
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
|
static void
|
||||||
action_esc_dispatch(struct terminal *term, uint8_t final)
|
action_esc_dispatch(struct terminal *term, uint8_t final)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
19
wayland.c
19
wayland.c
|
|
@ -26,6 +26,7 @@
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "render.h"
|
#include "render.h"
|
||||||
#include "selection.h"
|
#include "selection.h"
|
||||||
|
#include "shm.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "xmalloc.h"
|
#include "xmalloc.h"
|
||||||
|
|
||||||
|
|
@ -50,8 +51,11 @@ csd_instantiate(struct wl_window *win)
|
||||||
static void
|
static void
|
||||||
csd_destroy(struct wl_window *win)
|
csd_destroy(struct wl_window *win)
|
||||||
{
|
{
|
||||||
|
struct terminal *term = win->term;
|
||||||
|
|
||||||
for (size_t i = 0; i < ALEN(win->csd.surface); i++)
|
for (size_t i = 0; i < ALEN(win->csd.surface); i++)
|
||||||
wayl_win_subsurface_destroy(&win->csd.surface[i]);
|
wayl_win_subsurface_destroy(&win->csd.surface[i]);
|
||||||
|
shm_purge(term->render.chains.csd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -1410,6 +1414,8 @@ wayl_win_destroy(struct wl_window *win)
|
||||||
if (win == NULL)
|
if (win == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
struct terminal *term = win->term;
|
||||||
|
|
||||||
if (win->csd.move_timeout_fd != -1)
|
if (win->csd.move_timeout_fd != -1)
|
||||||
close(win->csd.move_timeout_fd);
|
close(win->csd.move_timeout_fd);
|
||||||
|
|
||||||
|
|
@ -1438,6 +1444,12 @@ wayl_win_destroy(struct wl_window *win)
|
||||||
wl_surface_commit(win->search.surf);
|
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 */
|
/* CSD */
|
||||||
for (size_t i = 0; i < ALEN(win->csd.surface); i++) {
|
for (size_t i = 0; i < ALEN(win->csd.surface); i++) {
|
||||||
if (win->csd.surface[i].surf != NULL) {
|
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->scrollback_indicator);
|
||||||
wayl_win_subsurface_destroy(&win->render_timer);
|
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 defined(HAVE_XDG_ACTIVATION)
|
||||||
if (win->xdg_activation_token != NULL)
|
if (win->xdg_activation_token != NULL)
|
||||||
xdg_activation_token_v1_destroy(win->xdg_activation_token);
|
xdg_activation_token_v1_destroy(win->xdg_activation_token);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue