Merge branch 'fractional-scaling'

This commit is contained in:
Daniel Eklöf 2023-06-29 15:38:42 +02:00
commit a44a0b4ebe
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
18 changed files with 508 additions and 315 deletions

View file

@ -50,6 +50,9 @@
is `auto`, which will select `libutempter` on Linux, `ulog` on
FreeBSD, and `none` for all others.
* Sixel aspect ratio.
* Support for the new fractional-scaling-v1 Wayland protocol. This
brings true fractional scaling to Wayland in general, and with this
release, foot.
### Changed
@ -77,6 +80,8 @@
is explicitly added.
* Default sixel aspect ratio is now 2:1 instead of 1:1.
* Sixel images are no longer cropped to the last non-transparent row.
* `dpi-aware` now defaults to `no`, and the `auto` value has been
removed.
[1371]: https://codeberg.org/dnkl/foot/pulls/1371
@ -89,6 +94,10 @@
### Removed
* `auto` value for the `dpi-aware` option.
### Fixed
* Incorrect icon in dock and window switcher on Gnome ([#1317][1317])

View file

@ -411,27 +411,53 @@ This is not how it is meant to be. Fonts are measured in _point sizes_
**for a reason**; a given point size should have the same height on
all mediums, be it printers or monitors, regardless of their DPI.
Foots default behavior is to use the monitors DPI to size fonts when
output scaling has been disabled on **all** monitors. If at least one
monitor has output scaling enabled, fonts will instead by sized using
the scaling factor.
That said, on Wayland, Hi-DPI monitors are typically handled by
configuring a _"scaling factor"_ in the compositor. This is usually
expressed as either a rational value (e.g. _1.5_), or as a percentage
(e.g. _150%_), by which all fonts and window sizes are supposed to be
multiplied.
This can be changed to either **always** use the monitors DPI
(regardless of scaling factor), or to **never** use it, with the
`dpi-aware` option in `foot.ini`. See the man page, **foot.ini**(5)
for more information.
For this reason, and because of the new _fractional scaling_ protocol
(see below for details), and because this is how Wayland applications
are expected to behave, foot >= 1.15 will default to scaling fonts
using the compositors scaling factor, and **not** the monitor
DPI.
When fonts are sized using the monitors DPI, glyphs should always
have the same physical height, regardless of monitor.
This means the (assuming the monitors are at the same viewing
distance) the font size will appear to change when you move the foot
window across different monitors, **unless** you have configured the
monitors scaling factors correctly in the compositor.
Furthermore, foot will re-size the fonts on-the-fly when the window is
moved between screens with different DPIs values. If the window covers
multiple screens, with different DPIs, the highest DPI will be used.
This can be changed by setting the `dpi-aware` option to `yes` in
`foot.ini`. When enabled, fonts will **not** be sized using the
scaling factor, but will instead be sized using the monitors
DPI. When the foot window is moved across monitors, the font size is
updated for the current monitors DPI.
This means that, assuming the monitors are **at the same viewing
distance**, the font size will appear to be the same, at all times.
_Note_: if you configure **pixelsize**, rather than **size**, then DPI
changes will **not** change the font size. Pixels are always pixels.
### Fractional scaling on Wayland
For a long time, there was no **true** support for _fractional
scaling_. That is, values like 1.5 (150%), 1.8 (180%) etc, only
integer values, like 2 (200%).
Compositors that _did_ support fractional scaling did so using a hack;
all applications were told to scale to 200%, and then the compositor
would down-scale the rendered image to e.g. 150%. This works OK for
everything **except fonts**, which ended up blurry.
With _wayland-protocols 1.32_, a new protocol was introduced, that
allows compositors to tell applications the _actual_ scaling
factor. Applications can then scale the image using a _viewport_
object, instead of setting a scale factor on the raw pixel buffer.
## Supported OSCs
OSC, _Operating System Command_, are escape sequences that interacts

View file

@ -66,11 +66,13 @@ static const char *
version_and_features(void)
{
static char buf[256];
snprintf(buf, sizeof(buf), "version: %s %cpgo %cime %cgraphemes %cassertions",
snprintf(buf, sizeof(buf),
"version: %s %cpgo %cime %cgraphemes %cfractional-scaling %cassertions",
FOOT_VERSION,
feature_pgo() ? '+' : '-',
feature_ime() ? '+' : '-',
feature_graphemes() ? '+' : '-',
feature_fractional_scaling() ? '+' : ':',
feature_assertions() ? '+' : '-');
return buf;
}

View file

@ -972,17 +972,8 @@ parse_section_main(struct context *ctx)
else if (strcmp(key, "underline-thickness") == 0)
return value_to_pt_or_px(ctx, &conf->underline_thickness);
else if (strcmp(key, "dpi-aware") == 0) {
if (strcmp(value, "auto") == 0)
conf->dpi_aware = DPI_AWARE_AUTO;
else {
bool value;
if (!value_to_bool(ctx, &value))
return false;
conf->dpi_aware = value ? DPI_AWARE_YES : DPI_AWARE_NO;
}
return true;
}
else if (strcmp(key, "dpi-aware") == 0)
return value_to_bool(ctx, &conf->dpi_aware);
else if (strcmp(key, "workers") == 0)
return value_to_uint16(ctx, 10, &conf->render_worker_count);
@ -2939,7 +2930,7 @@ config_load(struct config *conf, const char *conf_path,
.use_custom_underline_offset = false,
.box_drawings_uses_font_glyphs = false,
.underline_thickness = {.pt = 0., .px = -1},
.dpi_aware = DPI_AWARE_AUTO, /* DPI-aware when scaling-factor == 1 */
.dpi_aware = false,
.bell = {
.urgent = false,
.notify = false,

View file

@ -137,7 +137,7 @@ struct config {
enum { STARTUP_WINDOWED, STARTUP_MAXIMIZED, STARTUP_FULLSCREEN } startup_mode;
enum {DPI_AWARE_AUTO, DPI_AWARE_YES, DPI_AWARE_NO} dpi_aware;
bool dpi_aware;
struct config_font_list fonts[4];
struct font_size_adjustment font_size_adjustment;

20
csi.c
View file

@ -1206,8 +1206,10 @@ csi_dispatch(struct terminal *term, uint8_t final)
if (width >= 0 && height >= 0) {
char reply[64];
size_t n = xsnprintf(reply, sizeof(reply), "\033[4;%d;%dt",
height / term->scale, width / term->scale);
size_t n = xsnprintf(
reply, sizeof(reply), "\033[4;%d;%dt",
(int)round(height / term->scale),
(int)(width / term->scale));
term_to_slave(term, reply, n);
}
break;
@ -1229,9 +1231,10 @@ csi_dispatch(struct terminal *term, uint8_t final)
case 16: { /* report cell size in pixels */
char reply[64];
size_t n = xsnprintf(reply, sizeof(reply), "\033[6;%d;%dt",
term->cell_height / term->scale,
term->cell_width / term->scale);
size_t n = xsnprintf(
reply, sizeof(reply), "\033[6;%d;%dt",
(int)round(term->cell_height / term->scale),
(int)round(term->cell_width / term->scale));
term_to_slave(term, reply, n);
break;
}
@ -1247,9 +1250,10 @@ csi_dispatch(struct terminal *term, uint8_t final)
case 19: { /* report screen size in chars */
tll_foreach(term->window->on_outputs, it) {
char reply[64];
size_t n = xsnprintf(reply, sizeof(reply), "\033[9;%d;%dt",
it->item->dim.px_real.height / term->cell_height / term->scale,
it->item->dim.px_real.width / term->cell_width / term->scale);
size_t n = xsnprintf(
reply, sizeof(reply), "\033[9;%d;%dt",
(int)round(it->item->dim.px_real.height / term->cell_height / term->scale),
(int)round(it->item->dim.px_real.width / term->cell_width / term->scale));
term_to_slave(term, reply, n);
break;
}

View file

@ -185,7 +185,7 @@ empty string to be set, but it must be quoted: *KEY=""*)
Default: _no_.
*dpi-aware*
*auto*, *yes*, or *no*.
Boolean.
When set to *yes*, fonts are sized using the monitor's DPI, making
a font of a given size have the same physical size, regardless of
@ -199,12 +199,6 @@ empty string to be set, but it must be quoted: *KEY=""*)
instead sized using the monitor's scaling factor; doubling the
scaling factor *does* double the font size.
Finally, if set to *auto*, fonts will be sized using the monitor's
DPI if _all_ monitors have a scaling factor of 1. If at least one
monitor as a scaling factor larger than 1 (regardless of whether
the foot window is mapped on that monitor or not), fonts will be
scaled using the scaling factor.
Note that this option typically does not work with bitmap fonts,
which only contains a pre-defined set of sizes, and cannot be
dynamically scaled. Whichever size (of the available ones) that
@ -217,7 +211,7 @@ empty string to be set, but it must be quoted: *KEY=""*)
to size the font (*dpi-aware=no*), the font's pixel size will be
multiplied with the scaling factor.
Default: _auto_
Default: _no_
*pad*
Padding between border and glyphs, in pixels (subject to output

View file

@ -37,3 +37,12 @@ static inline bool feature_graphemes(void)
return false;
#endif
}
static inline bool feature_fractional_scaling(void)
{
#if defined(HAVE_FRACTIONAL_SCALE)
return true;
#else
return false;
#endif
}

View file

@ -20,7 +20,7 @@
# underline-offset=<font metrics>
# underline-thickness=<font underline thickness>
# box-drawings-uses-font-glyphs=no
# dpi-aware=auto
# dpi-aware=no
# initial-window-size-pixels=700x500 # Or,
# initial-window-size-chars=<COLSxROWS>

4
main.c
View file

@ -52,11 +52,13 @@ static const char *
version_and_features(void)
{
static char buf[256];
snprintf(buf, sizeof(buf), "version: %s %cpgo %cime %cgraphemes %cassertions",
snprintf(buf, sizeof(buf),
"version: %s %cpgo %cime %cgraphemes %cfractional-scaling %cassertions",
FOOT_VERSION,
feature_pgo() ? '+' : '-',
feature_ime() ? '+' : '-',
feature_graphemes() ? '+' : '-',
feature_fractional_scaling() ? '+' : '-',
feature_assertions() ? '+' : '-');
return buf;
}

View file

@ -158,6 +158,11 @@ if wayland_protocols.version().version_compare('>=1.21')
add_project_arguments('-DHAVE_XDG_ACTIVATION', language: 'c')
wl_proto_xml += [wayland_protocols_datadir + '/staging/xdg-activation/xdg-activation-v1.xml']
endif
if wayland_protocols.version().version_compare('>=1.31')
add_project_arguments('-DHAVE_FRACTIONAL_SCALE', language: 'c')
wl_proto_xml += [wayland_protocols_datadir + '/stable/viewporter/viewporter.xml']
wl_proto_xml += [wayland_protocols_datadir + '/staging/fractional-scale/fractional-scale-v1.xml']
endif
foreach prot : wl_proto_xml
wl_proto_headers += custom_target(

View file

@ -89,5 +89,5 @@ quirk_sway_subsurface_unmap(struct terminal *term)
if (!is_sway())
return;
wl_surface_damage_buffer(term->window->surface, 0, 0, INT32_MAX, INT32_MAX);
wl_surface_damage_buffer(term->window->surface.surf, 0, 0, INT32_MAX, INT32_MAX);
}

231
render.c
View file

@ -311,7 +311,7 @@ static void
draw_unfocused_block(const struct terminal *term, pixman_image_t *pix,
const pixman_color_t *color, int x, int y, int cell_cols)
{
const int scale = term->scale;
const int scale = round(term->scale);
const int width = min(min(scale, term->cell_width), term->cell_height);
pixman_image_fill_rectangles(
@ -905,21 +905,21 @@ render_margin(struct terminal *term, struct buffer *buf,
if (apply_damage) {
/* Top */
wl_surface_damage_buffer(
term->window->surface, 0, 0, term->width, term->margins.top);
term->window->surface.surf, 0, 0, term->width, term->margins.top);
/* Bottom */
wl_surface_damage_buffer(
term->window->surface, 0, bmargin, term->width, term->margins.bottom);
term->window->surface.surf, 0, bmargin, term->width, term->margins.bottom);
/* Left */
wl_surface_damage_buffer(
term->window->surface,
term->window->surface.surf,
0, term->margins.top + start_line * term->cell_height,
term->margins.left, line_count * term->cell_height);
/* Right */
wl_surface_damage_buffer(
term->window->surface,
term->window->surface.surf,
rmargin, term->margins.top + start_line * term->cell_height,
term->margins.right, line_count * term->cell_height);
}
@ -1027,7 +1027,7 @@ grid_render_scroll(struct terminal *term, struct buffer *buf,
#endif
wl_surface_damage_buffer(
term->window->surface, term->margins.left, dst_y,
term->window->surface.surf, term->margins.left, dst_y,
term->width - term->margins.left - term->margins.right, height);
/*
@ -1104,7 +1104,7 @@ grid_render_scroll_reverse(struct terminal *term, struct buffer *buf,
#endif
wl_surface_damage_buffer(
term->window->surface, term->margins.left, dst_y,
term->window->surface.surf, term->margins.left, dst_y,
term->width - term->margins.left - term->margins.right, height);
/*
@ -1153,7 +1153,7 @@ render_sixel_chunk(struct terminal *term, pixman_image_t *pix, const struct sixe
x, y,
width, height);
wl_surface_damage_buffer(term->window->surface, x, y, width, height);
wl_surface_damage_buffer(term->window->surface.surf, x, y, width, height);
}
static void
@ -1480,7 +1480,7 @@ render_ime_preedit_for_seat(struct terminal *term, struct seat *seat,
free(real_cells);
wl_surface_damage_buffer(
term->window->surface,
term->window->surface.surf,
term->margins.left,
term->margins.top + row_idx * term->cell_height,
term->width - term->margins.left - term->margins.right,
@ -1502,7 +1502,7 @@ render_ime_preedit(struct terminal *term, struct buffer *buf)
static void
render_overlay(struct terminal *term)
{
struct wl_surf_subsurf *overlay = &term->window->overlay;
struct wayl_sub_surface *overlay = &term->window->overlay;
bool unicode_mode_active = false;
/* Check if unicode mode is active on at least one seat focusing
@ -1523,8 +1523,8 @@ render_overlay(struct terminal *term)
if (likely(style == OVERLAY_NONE)) {
if (term->render.last_overlay_style != OVERLAY_NONE) {
/* Unmap overlay sub-surface */
wl_surface_attach(overlay->surf, NULL, 0, 0);
wl_surface_commit(overlay->surf);
wl_surface_attach(overlay->surface.surf, NULL, 0, 0);
wl_surface_commit(overlay->surface.surf);
term->render.last_overlay_style = OVERLAY_NONE;
term->render.last_overlay_buf = NULL;
@ -1691,17 +1691,18 @@ render_overlay(struct terminal *term)
&(pixman_rectangle16_t){0, 0, term->width, term->height});
quirk_weston_subsurface_desync_on(overlay->sub);
wayl_surface_scale(
term->window, &overlay->surface, buf, term->scale);
wl_subsurface_set_position(overlay->sub, 0, 0);
wl_surface_set_buffer_scale(overlay->surf, term->scale);
wl_surface_attach(overlay->surf, buf->wl_buf, 0, 0);
wl_surface_attach(overlay->surface.surf, buf->wl_buf, 0, 0);
wl_surface_damage_buffer(
overlay->surf,
overlay->surface.surf,
damage_bounds.x1, damage_bounds.y1,
damage_bounds.x2 - damage_bounds.x1,
damage_bounds.y2 - damage_bounds.y1);
wl_surface_commit(overlay->surf);
wl_surface_commit(overlay->surface.surf);
quirk_weston_subsurface_desync_off(overlay->sub);
buf->age = 0;
@ -1828,15 +1829,12 @@ get_csd_data(const struct terminal *term, enum csd_surface surf_idx)
}
static void
csd_commit(struct terminal *term, struct wl_surface *surf, struct buffer *buf)
csd_commit(struct terminal *term, struct wayl_surface *surf, struct buffer *buf)
{
xassert(buf->width % term->scale == 0);
xassert(buf->height % term->scale == 0);
wl_surface_attach(surf, buf->wl_buf, 0, 0);
wl_surface_damage_buffer(surf, 0, 0, buf->width, buf->height);
wl_surface_set_buffer_scale(surf, term->scale);
wl_surface_commit(surf);
wayl_surface_scale(term->window, surf, buf, term->scale);
wl_surface_attach(surf->surf, buf->wl_buf, 0, 0);
wl_surface_damage_buffer(surf->surf, 0, 0, buf->width, buf->height);
wl_surface_commit(surf->surf);
}
static void
@ -1852,8 +1850,7 @@ render_csd_part(struct terminal *term,
}
static void
render_osd(struct terminal *term,
struct wl_surface *surf, struct wl_subsurface *sub_surf,
render_osd(struct terminal *term, const struct wayl_sub_surface *sub_surf,
struct fcft_font *font, struct buffer *buf,
const char32_t *text, uint32_t _fg, uint32_t _bg,
unsigned x, unsigned y)
@ -1926,23 +1923,20 @@ render_osd(struct terminal *term,
pixman_image_unref(src);
pixman_image_set_clip_region32(buf->pix[0], NULL);
xassert(buf->width % term->scale == 0);
xassert(buf->height % term->scale == 0);
quirk_weston_subsurface_desync_on(sub_surf);
wl_surface_attach(surf, buf->wl_buf, 0, 0);
wl_surface_damage_buffer(surf, 0, 0, buf->width, buf->height);
wl_surface_set_buffer_scale(surf, term->scale);
quirk_weston_subsurface_desync_on(sub_surf->sub);
wayl_surface_scale(term->window, &sub_surf->surface, buf, term->scale);
wl_surface_attach(sub_surf->surface.surf, buf->wl_buf, 0, 0);
wl_surface_damage_buffer(sub_surf->surface.surf, 0, 0, buf->width, buf->height);
struct wl_region *region = wl_compositor_create_region(term->wl->compositor);
if (region != NULL) {
wl_region_add(region, 0, 0, buf->width, buf->height);
wl_surface_set_opaque_region(surf, region);
wl_surface_set_opaque_region(sub_surf->surface.surf, region);
wl_region_destroy(region);
}
wl_surface_commit(surf);
quirk_weston_subsurface_desync_off(sub_surf);
wl_surface_commit(sub_surf->surface.surf);
quirk_weston_subsurface_desync_off(sub_surf->sub);
}
static void
@ -1951,13 +1945,10 @@ render_csd_title(struct terminal *term, const struct csd_data *info,
{
xassert(term->window->csd_mode == CSD_YES);
struct wl_surf_subsurf *surf = &term->window->csd.surface[CSD_SURF_TITLE];
struct wayl_sub_surface *surf = &term->window->csd.surface[CSD_SURF_TITLE];
if (info->width == 0 || info->height == 0)
return;
xassert(info->width % term->scale == 0);
xassert(info->height % term->scale == 0);
uint32_t bg = term->conf->csd.color.title_set
? term->conf->csd.color.title
: 0xffu << 24 | term->conf->colors.fg;
@ -1980,11 +1971,10 @@ render_csd_title(struct terminal *term, const struct csd_data *info,
const int margin = M != NULL ? M->advance.x : win->csd.font->max_advance.x;
render_osd(term, surf->surf, surf->sub, win->csd.font,
buf, title_text, fg, bg, margin,
render_osd(term, surf, win->csd.font, buf, title_text, fg, bg, margin,
(buf->height - win->csd.font->height) / 2);
csd_commit(term, surf->surf, buf);
csd_commit(term, &surf->surface, buf);
free(_title_text);
}
@ -1995,26 +1985,23 @@ render_csd_border(struct terminal *term, enum csd_surface surf_idx,
xassert(term->window->csd_mode == CSD_YES);
xassert(surf_idx >= CSD_SURF_LEFT && surf_idx <= CSD_SURF_BOTTOM);
struct wl_surface *surf = term->window->csd.surface[surf_idx].surf;
struct wayl_surface *surf = &term->window->csd.surface[surf_idx].surface;
if (info->width == 0 || info->height == 0)
return;
xassert(info->width % term->scale == 0);
xassert(info->height % term->scale == 0);
{
pixman_color_t color = color_hex_to_pixman_with_alpha(0, 0);
render_csd_part(term, surf, buf, info->width, info->height, &color);
render_csd_part(term, surf->surf, buf, info->width, info->height, &color);
}
/*
* The visible border.
*/
int scale = term->scale;
int bwidth = term->conf->csd.border_width * scale;
int vwidth = term->conf->csd.border_width_visible * scale; /* Visible size */
float scale = term->scale;
int bwidth = round(term->conf->csd.border_width * scale);
int vwidth = round(term->conf->csd.border_width_visible * scale); /* Visible size */
xassert(bwidth >= vwidth);
@ -2067,7 +2054,6 @@ render_csd_border(struct terminal *term, enum csd_surface surf_idx,
uint16_t alpha = _color >> 24 | (_color >> 24 << 8);
pixman_color_t color = color_hex_to_pixman_with_alpha(_color, alpha);
pixman_image_fill_rectangles(
PIXMAN_OP_SRC, buf->pix[0], &color, 1,
&(pixman_rectangle16_t){x, y, w, h});
@ -2284,14 +2270,11 @@ render_csd_button(struct terminal *term, enum csd_surface surf_idx,
xassert(term->window->csd_mode == CSD_YES);
xassert(surf_idx >= CSD_SURF_MINIMIZE && surf_idx <= CSD_SURF_CLOSE);
struct wl_surface *surf = term->window->csd.surface[surf_idx].surf;
struct wayl_surface *surf = &term->window->csd.surface[surf_idx].surface;
if (info->width == 0 || info->height == 0)
return;
xassert(info->width % term->scale == 0);
xassert(info->height % term->scale == 0);
uint32_t _color;
uint16_t alpha = 0xffff;
bool is_active = false;
@ -2339,7 +2322,7 @@ render_csd_button(struct terminal *term, enum csd_surface surf_idx,
_color = color_dim(term, _color);
pixman_color_t color = color_hex_to_pixman_with_alpha(_color, alpha);
render_csd_part(term, surf, buf, info->width, info->height, &color);
render_csd_part(term, surf->surf, buf, info->width, info->height, &color);
switch (surf_idx) {
case CSD_SURF_MINIMIZE: render_csd_button_minimize(term, buf); break;
@ -2374,7 +2357,7 @@ render_csd(struct terminal *term)
const int width = infos[i].width;
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].surface.surf;
struct wl_subsurface *sub = term->window->csd.surface[i].sub;
xassert(surf != NULL);
@ -2413,7 +2396,7 @@ render_scrollback_position(struct terminal *term)
struct wl_window *win = term->window;
if (term->grid->view == term->grid->offset) {
if (win->scrollback_indicator.surf != NULL) {
if (win->scrollback_indicator.surface.surf != NULL) {
wayl_win_subsurface_destroy(&win->scrollback_indicator);
/* Work around Sway bug - unmapping a sub-surface does not damage
@ -2423,7 +2406,7 @@ render_scrollback_position(struct terminal *term)
return;
}
if (win->scrollback_indicator.surf == NULL) {
if (win->scrollback_indicator.surface.surf == NULL) {
if (!wayl_win_subsurface_new(
win, &win->scrollback_indicator, false))
{
@ -2432,7 +2415,7 @@ render_scrollback_position(struct terminal *term)
}
}
xassert(win->scrollback_indicator.surf != NULL);
xassert(win->scrollback_indicator.surface.surf != NULL);
xassert(win->scrollback_indicator.sub != NULL);
/* Find absolute row number of the scrollback start */
@ -2530,8 +2513,8 @@ render_scrollback_position(struct terminal *term)
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);
wl_surface_attach(win->scrollback_indicator.surface.surf, NULL, 0, 0);
wl_surface_commit(win->scrollback_indicator.surface.surf);
return;
}
@ -2550,8 +2533,7 @@ render_scrollback_position(struct terminal *term)
render_osd(
term,
win->scrollback_indicator.surf,
win->scrollback_indicator.sub,
&win->scrollback_indicator,
term->fonts[0], buf, text,
fg, 0xffu << 24 | bg,
width - margin - c32len(text) * term->cell_width, margin);
@ -2569,7 +2551,7 @@ render_render_timer(struct terminal *term, struct timespec render_time)
char32_t text[256];
mbstoc32(text, usecs_str, ALEN(text));
const int scale = term->scale;
const int scale = round(term->scale);
const int cell_count = c32len(text);
const int margin = 3 * scale;
const int width =
@ -2587,8 +2569,7 @@ render_render_timer(struct terminal *term, struct timespec render_time)
render_osd(
term,
win->render_timer.surf,
win->render_timer.sub,
&win->render_timer,
term->fonts[0], buf, text,
term->colors.table[0], 0xffu << 24 | term->colors.table[8 + 1],
margin, margin);
@ -2935,7 +2916,7 @@ grid_render(struct terminal *term)
int height = (r - first_dirty_row) * term->cell_height;
wl_surface_damage_buffer(
term->window->surface, x, y, width, height);
term->window->surface.surf, x, y, width, height);
pixman_region32_union_rect(
&buf->dirty, &buf->dirty, 0, y, buf->width, height);
}
@ -2963,7 +2944,7 @@ grid_render(struct terminal *term)
int width = term->width - term->margins.left - term->margins.right;
int height = (term->rows - first_dirty_row) * term->cell_height;
wl_surface_damage_buffer(term->window->surface, x, y, width, height);
wl_surface_damage_buffer(term->window->surface.surf, x, y, width, height);
pixman_region32_union_rect(&buf->dirty, &buf->dirty, 0, y, buf->width, height);
}
@ -3030,17 +3011,17 @@ grid_render(struct terminal *term)
xassert(term->grid->view >= 0 && term->grid->view < term->grid->num_rows);
xassert(term->window->frame_callback == NULL);
term->window->frame_callback = wl_surface_frame(term->window->surface);
term->window->frame_callback = wl_surface_frame(term->window->surface.surf);
wl_callback_add_listener(term->window->frame_callback, &frame_listener, term);
wl_surface_set_buffer_scale(term->window->surface, term->scale);
wayl_win_scale(term->window, buf);
if (term->wl->presentation != NULL && term->conf->presentation_timings) {
struct timespec commit_time;
clock_gettime(term->wl->presentation_clock_id, &commit_time);
struct wp_presentation_feedback *feedback = wp_presentation_feedback(
term->wl->presentation, term->window->surface);
term->wl->presentation, term->window->surface.surf);
if (feedback == NULL) {
LOG_WARN("failed to create presentation feedback");
@ -3064,14 +3045,11 @@ grid_render(struct terminal *term)
if (term->conf->tweak.damage_whole_window) {
wl_surface_damage_buffer(
term->window->surface, 0, 0, INT32_MAX, INT32_MAX);
term->window->surface.surf, 0, 0, INT32_MAX, INT32_MAX);
}
xassert(buf->width % term->scale == 0);
xassert(buf->height % term->scale == 0);
wl_surface_attach(term->window->surface, buf->wl_buf, 0, 0);
wl_surface_commit(term->window->surface);
wl_surface_attach(term->window->surface.surf, buf->wl_buf, 0, 0);
wl_surface_commit(term->window->surface.surf);
}
static void
@ -3133,17 +3111,17 @@ render_search_box(struct terminal *term)
const size_t wanted_visible_cells = max(20, total_cells);
xassert(term->scale >= 1);
const int scale = term->scale;
const int rounded_scale = round(term->scale);
const size_t margin = 3 * scale;
const size_t margin = 3 * rounded_scale;
const size_t width = term->width - 2 * margin;
const size_t visible_width = min(
term->width - 2 * margin,
(2 * margin + wanted_visible_cells * term->cell_width + scale - 1) / scale * scale);
(2 * margin + wanted_visible_cells * term->cell_width + rounded_scale - 1) / rounded_scale * rounded_scale);
const size_t height = min(
term->height - 2 * margin,
(2 * margin + 1 * term->cell_height + scale - 1) / scale * scale);
(2 * margin + 1 * term->cell_height + rounded_scale - 1) / rounded_scale * rounded_scale);
const size_t visible_cells = (visible_width - 2 * margin) / term->cell_width;
size_t glyph_offset = term->render.search_glyph_offset;
@ -3390,24 +3368,21 @@ render_search_box(struct terminal *term)
/* TODO: this is only necessary on a window resize */
wl_subsurface_set_position(
term->window->search.sub,
margin / scale,
max(0, (int32_t)term->height - height - margin) / scale);
margin / term->scale,
max(0, (int32_t)term->height - height - margin) / term->scale);
xassert(buf->width % scale == 0);
xassert(buf->height % scale == 0);
wl_surface_attach(term->window->search.surf, buf->wl_buf, 0, 0);
wl_surface_damage_buffer(term->window->search.surf, 0, 0, width, height);
wl_surface_set_buffer_scale(term->window->search.surf, scale);
wayl_surface_scale(term->window, &term->window->search.surface, buf, term->scale);
wl_surface_attach(term->window->search.surface.surf, buf->wl_buf, 0, 0);
wl_surface_damage_buffer(term->window->search.surface.surf, 0, 0, width, height);
struct wl_region *region = wl_compositor_create_region(term->wl->compositor);
if (region != NULL) {
wl_region_add(region, width - visible_width, 0, visible_width, height);
wl_surface_set_opaque_region(term->window->search.surf, region);
wl_surface_set_opaque_region(term->window->search.surface.surf, region);
wl_region_destroy(region);
}
wl_surface_commit(term->window->search.surf);
wl_surface_commit(term->window->search.surface.surf);
quirk_weston_subsurface_desync_off(term->window->search.sub);
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
@ -3423,7 +3398,7 @@ render_urls(struct terminal *term)
struct wl_window *win = term->window;
xassert(tll_length(win->urls) > 0);
const int scale = term->scale;
const int scale = round(term->scale);
const int x_margin = 2 * scale;
const int y_margin = 1 * scale;
@ -3488,7 +3463,7 @@ render_urls(struct terminal *term)
continue;
}
struct wl_surface *surf = it->item.surf.surf;
struct wl_surface *surf = it->item.surf.surface.surf;
struct wl_subsurface *sub_surf = it->item.surf.sub;
if (surf == NULL || sub_surf == NULL)
@ -3623,23 +3598,22 @@ render_urls(struct terminal *term)
: 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 struct wayl_sub_surface *sub_surf = &info[i].url->surf;
const char32_t *label = info[i].text;
const int x = info[i].x;
const int y = info[i].y;
xassert(surf != NULL);
xassert(sub_surf != NULL);
xassert(sub_surf->surface.surf != NULL);
xassert(sub_surf->sub != NULL);
wl_subsurface_set_position(
sub_surf,
sub_surf->sub,
(term->margins.left + x) / term->scale,
(term->margins.top + y) / term->scale);
render_osd(
term, surf, sub_surf, term->fonts[0], bufs[i], label,
term, sub_surf, term->fonts[0], bufs[i], label,
fg, 0xffu << 24 | bg, x_margin, y_margin);
free(info[i].text);
@ -3869,13 +3843,17 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
if (term->cell_width == 0 && term->cell_height == 0)
return false;
int scale = -1;
tll_foreach(term->window->on_outputs, it) {
if (it->item->scale > scale)
scale = it->item->scale;
float scale = -1;
if (wayl_fractional_scaling(term->wl)) {
scale = term->window->scale;
} else {
tll_foreach(term->window->on_outputs, it) {
if (it->item->scale > scale)
scale = it->item->scale;
}
}
if (scale < 0) {
if (scale < 0.) {
/* Haven't 'entered' an output yet? */
scale = term->scale;
}
@ -3923,13 +3901,18 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
* Ensure we can scale to logical size, and back to
* pixels without truncating.
*/
if (width % scale)
width += scale - width % scale;
if (height % scale)
height += scale - height % scale;
if (wayl_fractional_scaling(term->wl)) {
xassert((int)round(scale) == (int)scale);
xassert(width % scale == 0);
xassert(height % scale == 0);
int iscale = scale;
if (width % iscale)
width += iscale - width % iscale;
if (height % iscale)
height += iscale - height % iscale;
xassert(width % iscale == 0);
xassert(height % iscale == 0);
}
break;
}
}
@ -4270,34 +4253,36 @@ render_xcursor_update(struct seat *seat)
if (seat->pointer.xcursor == XCURSOR_HIDDEN) {
/* Hide cursor */
wl_surface_attach(seat->pointer.surface, NULL, 0, 0);
wl_surface_commit(seat->pointer.surface);
wl_surface_attach(seat->pointer.surface.surf, NULL, 0, 0);
wl_surface_commit(seat->pointer.surface.surf);
return;
}
xassert(seat->pointer.cursor != NULL);
const int scale = seat->pointer.scale;
const float scale = seat->pointer.scale;
struct wl_cursor_image *image = seat->pointer.cursor->images[0];
struct wl_buffer *buf = wl_cursor_image_get_buffer(image);
wl_surface_attach(
seat->pointer.surface, wl_cursor_image_get_buffer(image), 0, 0);
wayl_surface_scale_explicit_width_height(
seat->mouse_focus->window,
&seat->pointer.surface, image->width, image->height, scale);
wl_surface_attach(seat->pointer.surface.surf, buf, 0, 0);
wl_pointer_set_cursor(
seat->wl_pointer, seat->pointer.serial,
seat->pointer.surface,
seat->pointer.surface.surf,
image->hotspot_x / scale, image->hotspot_y / scale);
wl_surface_damage_buffer(
seat->pointer.surface, 0, 0, INT32_MAX, INT32_MAX);
wl_surface_set_buffer_scale(seat->pointer.surface, scale);
seat->pointer.surface.surf, 0, 0, INT32_MAX, INT32_MAX);
xassert(seat->pointer.xcursor_callback == NULL);
seat->pointer.xcursor_callback = wl_surface_frame(seat->pointer.surface);
seat->pointer.xcursor_callback = wl_surface_frame(seat->pointer.surface.surf);
wl_callback_add_listener(seat->pointer.xcursor_callback, &xcursor_listener, seat);
wl_surface_commit(seat->pointer.surface);
wl_surface_commit(seat->pointer.surface.surf);
}
static void

View file

@ -912,42 +912,6 @@ get_font_subpixel(const struct terminal *term)
return FCFT_SUBPIXEL_DEFAULT;
}
static bool
term_font_size_by_dpi(const struct terminal *term)
{
switch (term->conf->dpi_aware) {
case DPI_AWARE_YES: return true;
case DPI_AWARE_NO: return false;
case DPI_AWARE_AUTO:
/*
* Scale using DPI if all monitors have a scaling factor or 1.
*
* The idea is this: if a user, with multiple monitors, have
* enabled scaling on at least one monitor, then he/she has
* most likely done so to match the size of his/hers other
* monitors.
*
* I.e. if the user has one monitor with a scaling factor of
* one, and another with a scaling factor of two, he/she
* expects things to be twice as large on the second
* monitor.
*
* If we (foot) scale using DPI on the first monitor, and
* using the scaling factor on the second monitor, foot will
* *not* look twice as big on the second monitor.
*/
tll_foreach(term->wl->monitors, it) {
const struct monitor *mon = &it->item;
if (mon->scale > 1)
return false;
}
return true;
}
BUG("unhandled DPI awareness value");
}
int
term_pt_or_px_as_pixels(const struct terminal *term,
const struct pt_or_px *pt_or_px)
@ -1000,14 +964,14 @@ reload_fonts(struct terminal *term)
bool use_px_size = term->font_sizes[i][j].px_size > 0;
char size[64];
const int scale = term->font_is_sized_by_dpi ? 1 : term->scale;
const float scale = term->font_is_sized_by_dpi ? 1. : term->scale;
if (use_px_size)
snprintf(size, sizeof(size), ":pixelsize=%d",
term->font_sizes[i][j].px_size * scale);
(int)round(term->font_sizes[i][j].px_size * scale));
else
snprintf(size, sizeof(size), ":size=%.2f",
term->font_sizes[i][j].pt_size * (double)scale);
term->font_sizes[i][j].pt_size * scale);
size_t len = strlen(font->pattern) + strlen(size) + 1;
names[i][j] = xmalloc(len);
@ -1232,7 +1196,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
.reverse_wrap = true,
.auto_margin = true,
.window_title_stack = tll_init(),
.scale = 1,
.scale = 1.,
.flash = {.fd = flash_fd},
.blink = {.fd = -1},
.vt = {
@ -2158,7 +2122,7 @@ term_font_dpi_changed(struct terminal *term, int old_scale)
xassert(term->scale > 0);
bool was_scaled_using_dpi = term->font_is_sized_by_dpi;
bool will_scale_using_dpi = term_font_size_by_dpi(term);
bool will_scale_using_dpi = term->conf->dpi_aware;
bool need_font_reload =
was_scaled_using_dpi != will_scale_using_dpi ||
@ -3589,23 +3553,23 @@ term_single_shift(struct terminal *term, enum charset_designator idx)
enum term_surface
term_surface_kind(const struct terminal *term, const struct wl_surface *surface)
{
if (likely(surface == term->window->surface))
if (likely(surface == term->window->surface.surf))
return TERM_SURF_GRID;
else if (surface == term->window->csd.surface[CSD_SURF_TITLE].surf)
else if (surface == term->window->csd.surface[CSD_SURF_TITLE].surface.surf)
return TERM_SURF_TITLE;
else if (surface == term->window->csd.surface[CSD_SURF_LEFT].surf)
else if (surface == term->window->csd.surface[CSD_SURF_LEFT].surface.surf)
return TERM_SURF_BORDER_LEFT;
else if (surface == term->window->csd.surface[CSD_SURF_RIGHT].surf)
else if (surface == term->window->csd.surface[CSD_SURF_RIGHT].surface.surf)
return TERM_SURF_BORDER_RIGHT;
else if (surface == term->window->csd.surface[CSD_SURF_TOP].surf)
else if (surface == term->window->csd.surface[CSD_SURF_TOP].surface.surf)
return TERM_SURF_BORDER_TOP;
else if (surface == term->window->csd.surface[CSD_SURF_BOTTOM].surf)
else if (surface == term->window->csd.surface[CSD_SURF_BOTTOM].surface.surf)
return TERM_SURF_BORDER_BOTTOM;
else if (surface == term->window->csd.surface[CSD_SURF_MINIMIZE].surf)
else if (surface == term->window->csd.surface[CSD_SURF_MINIMIZE].surface.surf)
return TERM_SURF_BUTTON_MINIMIZE;
else if (surface == term->window->csd.surface[CSD_SURF_MAXIMIZE].surf)
else if (surface == term->window->csd.surface[CSD_SURF_MAXIMIZE].surface.surf)
return TERM_SURF_BUTTON_MAXIMIZE;
else if (surface == term->window->csd.surface[CSD_SURF_CLOSE].surf)
else if (surface == term->window->csd.surface[CSD_SURF_CLOSE].surface.surf)
return TERM_SURF_BUTTON_CLOSE;
else
return TERM_SURF_NONE;

View file

@ -454,7 +454,7 @@ struct terminal {
int fd;
} blink;
int scale;
float scale;
int width; /* pixels */
int height; /* pixels */
int stashed_width;

View file

@ -511,6 +511,7 @@ test_section_main(void)
test_boolean(&ctx, &parse_section_main, "box-drawings-uses-font-glyphs", &conf.box_drawings_uses_font_glyphs);
test_boolean(&ctx, &parse_section_main, "locked-title", &conf.locked_title);
test_boolean(&ctx, &parse_section_main, "notify-focus-inhibit", &conf.notify_focus_inhibit);
test_boolean(&ctx, &parse_section_main, "dpi-aware", &conf.dpi_aware);
test_pt_or_px(&ctx, &parse_section_main, "font-size-adjustment", &conf.font_size_adjustment.pt_or_px); /* TODO: test N% values too */
test_pt_or_px(&ctx, &parse_section_main, "line-height", &conf.line_height);
@ -524,17 +525,6 @@ test_section_main(void)
test_spawn_template(&ctx, &parse_section_main, "notify", &conf.notify);
test_enum(
&ctx, &parse_section_main, "dpi-aware",
9,
(const char *[]){"on", "true", "yes", "1",
"off", "false", "no", "0",
"auto"},
(int []){DPI_AWARE_YES, DPI_AWARE_YES, DPI_AWARE_YES, DPI_AWARE_YES,
DPI_AWARE_NO, DPI_AWARE_NO, DPI_AWARE_NO, DPI_AWARE_NO,
DPI_AWARE_AUTO},
(int *)&conf.dpi_aware);
test_enum(&ctx, &parse_section_main, "selection-target",
4,
(const char *[]){"none", "primary", "clipboard", "both"},

311
wayland.c
View file

@ -32,12 +32,12 @@
#include "xmalloc.h"
static void
csd_reload_font(struct wl_window *win, int old_scale)
csd_reload_font(struct wl_window *win, float old_scale)
{
struct terminal *term = win->term;
const struct config *conf = term->conf;
const int scale = term->scale;
const float scale = term->scale;
bool enable_csd = win->csd_mode == CSD_YES && !win->is_fullscreen;
if (!enable_csd)
@ -52,10 +52,10 @@ csd_reload_font(struct wl_window *win, int old_scale)
patterns[i] = conf->csd.font.arr[i].pattern;
char pixelsize[32];
snprintf(pixelsize, sizeof(pixelsize),
"pixelsize=%u", conf->csd.title_height * scale * 1 / 2);
snprintf(pixelsize, sizeof(pixelsize), "pixelsize=%u",
(int)round(conf->csd.title_height * scale * 1 / 2));
LOG_DBG("loading CSD font \"%s:%s\" (old-scale=%d, scale=%d)",
LOG_DBG("loading CSD font \"%s:%s\" (old-scale=%.2f, scale=%.2f)",
patterns[0], pixelsize, old_scale, scale);
win->csd.font = fcft_from_name(conf->csd.font.count, patterns, pixelsize);
@ -74,12 +74,12 @@ csd_instantiate(struct wl_window *win)
for (size_t i = CSD_SURF_MINIMIZE; i < CSD_SURF_COUNT; i++) {
bool ret = wayl_win_subsurface_new_with_custom_parent(
win, win->csd.surface[CSD_SURF_TITLE].surf, &win->csd.surface[i],
win, win->csd.surface[CSD_SURF_TITLE].surface.surf, &win->csd.surface[i],
true);
xassert(ret);
}
csd_reload_font(win, -1);
csd_reload_font(win, -1.);
}
static void
@ -187,8 +187,12 @@ seat_destroy(struct seat *seat)
if (seat->pointer.theme != NULL)
wl_cursor_theme_destroy(seat->pointer.theme);
if (seat->pointer.surface != NULL)
wl_surface_destroy(seat->pointer.surface);
if (seat->pointer.surface.surf != NULL)
wl_surface_destroy(seat->pointer.surface.surf);
#if defined(HAVE_FRACTIONAL_SCALE)
if (seat->pointer.surface.viewport != NULL)
wp_viewport_destroy(seat->pointer.surface.viewport);
#endif
if (seat->pointer.xcursor_callback != NULL)
wl_callback_destroy(seat->pointer.xcursor_callback);
@ -288,27 +292,46 @@ seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
if (caps & WL_SEAT_CAPABILITY_POINTER) {
if (seat->wl_pointer == NULL) {
xassert(seat->pointer.surface == NULL);
seat->pointer.surface = wl_compositor_create_surface(seat->wayl->compositor);
xassert(seat->pointer.surface.surf == NULL);
seat->pointer.surface.surf =
wl_compositor_create_surface(seat->wayl->compositor);
if (seat->pointer.surface == NULL) {
if (seat->pointer.surface.surf == NULL) {
LOG_ERR("%s: failed to create pointer surface", seat->name);
return;
}
#if defined(HAVE_FRACTIONAL_SCALE)
xassert(seat->pointer.surface.viewport == NULL);
seat->pointer.surface.viewport = wp_viewporter_get_viewport(
seat->wayl->viewporter, seat->pointer.surface.surf);
if (seat->pointer.surface.viewport == NULL) {
LOG_ERR("%s: failed to create pointer viewport", seat->name);
wl_surface_destroy(seat->pointer.surface.surf);
seat->pointer.surface.surf = NULL;
return;
}
#endif
seat->wl_pointer = wl_seat_get_pointer(wl_seat);
wl_pointer_add_listener(seat->wl_pointer, &pointer_listener, seat);
}
} else {
if (seat->wl_pointer != NULL) {
wl_pointer_release(seat->wl_pointer);
wl_surface_destroy(seat->pointer.surface);
wl_surface_destroy(seat->pointer.surface.surf);
#if defined(HAVE_FRACTIONAL_SCALE)
wp_viewport_destroy(seat->pointer.surface.viewport);
seat->pointer.surface.viewport = NULL;
#endif
if (seat->pointer.theme != NULL)
wl_cursor_theme_destroy(seat->pointer.theme);
seat->wl_pointer = NULL;
seat->pointer.surface = NULL;
seat->pointer.surface.surf = NULL;
seat->pointer.theme = NULL;
seat->pointer.cursor = NULL;
}
@ -334,7 +357,7 @@ update_term_for_output_change(struct terminal *term)
if (tll_length(term->window->on_outputs) == 0)
return;
int old_scale = term->scale;
float old_scale = term->scale;
render_resize(term, term->width / term->scale, term->height / term->scale);
term_font_dpi_changed(term, old_scale);
@ -350,11 +373,6 @@ update_terms_on_monitor(struct monitor *mon)
tll_foreach(wayl->terms, it) {
struct terminal *term = it->item;
if (term->conf->dpi_aware == DPI_AWARE_AUTO) {
update_term_for_output_change(term);
continue;
}
tll_foreach(term->window->on_outputs, it2) {
if (it2->item == mon) {
update_term_for_output_change(term);
@ -848,7 +866,7 @@ xdg_surface_configure(void *data, struct xdg_surface *xdg_surface,
* anytime soon. Some compositors require a commit in
* combination with an ack - make them happy.
*/
wl_surface_commit(win->surface);
wl_surface_commit(win->surface.surf);
}
if (wasnt_configured)
@ -1121,6 +1139,27 @@ handle_global(void *data, struct wl_registry *registry,
}
#endif
#if defined(HAVE_FRACTIONAL_SCALE)
else if (strcmp(interface, wp_viewporter_interface.name) == 0) {
const uint32_t required = 1;
if (!verify_iface_version(interface, version, required))
return;
wayl->viewporter = wl_registry_bind(
wayl->registry, name, &wp_viewporter_interface, required);
}
else if (strcmp(interface, wp_fractional_scale_manager_v1_interface.name) == 0) {
const uint32_t required = 1;
if (!verify_iface_version(interface, version, required))
return;
wayl->fractional_scale_manager = wl_registry_bind(
wayl->registry, name,
&wp_fractional_scale_manager_v1_interface, required);
}
#endif
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
else if (strcmp(interface, zwp_text_input_manager_v3_interface.name) == 0) {
const uint32_t required = 1;
@ -1204,7 +1243,7 @@ handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
if (seat->wl_keyboard != NULL)
keyboard_listener.leave(
seat, seat->wl_keyboard, -1, seat->kbd_focus->window->surface);
seat, seat->wl_keyboard, -1, seat->kbd_focus->window->surface.surf);
}
if (seat->mouse_focus != NULL) {
@ -1214,7 +1253,7 @@ handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
if (seat->wl_pointer != NULL)
pointer_listener.leave(
seat, seat->wl_pointer, -1, seat->mouse_focus->window->surface);
seat, seat->wl_pointer, -1, seat->mouse_focus->window->surface.surf);
}
seat_destroy(seat);
@ -1347,6 +1386,14 @@ wayl_init(struct fdm *fdm, struct key_binding_manager *key_binding_manager,
"bell.urgent will fall back to coloring the window margins red");
}
#if defined(HAVE_FRACTIONAL_SCALE)
if (wayl->fractional_scale_manager == NULL || wayl->viewporter == NULL) {
#else
if (true) {
#endif
LOG_WARN("fractional scaling not available");
}
if (presentation_timings && wayl->presentation == NULL) {
LOG_ERR("presentation time interface not implemented by compositor");
goto out;
@ -1435,6 +1482,12 @@ wayl_destroy(struct wayland *wayl)
zwp_text_input_manager_v3_destroy(wayl->text_input_manager);
#endif
#if defined(HAVE_FRACTIONAL_SCALE)
if (wayl->fractional_scale_manager != NULL)
wp_fractional_scale_manager_v1_destroy(wayl->fractional_scale_manager);
if (wayl->viewporter != NULL)
wp_viewporter_destroy(wayl->viewporter);
#endif
#if defined(HAVE_XDG_ACTIVATION)
if (wayl->xdg_activation != NULL)
xdg_activation_v1_destroy(wayl->xdg_activation);
@ -1469,6 +1522,24 @@ wayl_destroy(struct wayland *wayl)
free(wayl);
}
#if defined(HAVE_FRACTIONAL_SCALE)
static void fractional_scale_preferred_scale(
void *data, struct wp_fractional_scale_v1 *wp_fractional_scale_v1,
uint32_t scale)
{
struct wl_window *win = data;
win->scale = (float)scale / 120.;
win->have_preferred_scale = true;
LOG_DBG("fractional scale: %.3f", win->scale);
update_term_for_output_change(win->term);
}
static const struct wp_fractional_scale_v1_listener fractional_scale_listener = {
.preferred_scale = &fractional_scale_preferred_scale,
};
#endif
struct wl_window *
wayl_win_init(struct terminal *term, const char *token)
{
@ -1485,21 +1556,34 @@ wayl_win_init(struct terminal *term, const char *token)
win->csd_mode = CSD_UNKNOWN;
win->csd.move_timeout_fd = -1;
win->resize_timeout_fd = -1;
win->scale = -1.;
win->wm_capabilities.maximize = true;
win->wm_capabilities.minimize = true;
win->surface = wl_compositor_create_surface(wayl->compositor);
if (win->surface == NULL) {
win->surface.surf = wl_compositor_create_surface(wayl->compositor);
if (win->surface.surf == NULL) {
LOG_ERR("failed to create wayland surface");
goto out;
}
wayl_win_alpha_changed(win);
wl_surface_add_listener(win->surface, &surface_listener, win);
wl_surface_add_listener(win->surface.surf, &surface_listener, win);
win->xdg_surface = xdg_wm_base_get_xdg_surface(wayl->shell, win->surface);
#if defined(HAVE_FRACTIONAL_SCALE)
if (wayl->fractional_scale_manager != NULL && wayl->viewporter != NULL) {
win->surface.viewport = wp_viewporter_get_viewport(wayl->viewporter, win->surface.surf);
win->fractional_scale =
wp_fractional_scale_manager_v1_get_fractional_scale(
wayl->fractional_scale_manager, win->surface.surf);
wp_fractional_scale_v1_add_listener(
win->fractional_scale, &fractional_scale_listener, win);
}
#endif
win->xdg_surface = xdg_wm_base_get_xdg_surface(wayl->shell, win->surface.surf);
xdg_surface_add_listener(win->xdg_surface, &xdg_surface_listener, win);
win->xdg_toplevel = xdg_surface_get_toplevel(win->xdg_surface);
@ -1532,12 +1616,12 @@ wayl_win_init(struct terminal *term, const char *token)
LOG_WARN("no decoration manager available - using CSDs unconditionally");
}
wl_surface_commit(win->surface);
wl_surface_commit(win->surface.surf);
#if defined(HAVE_XDG_ACTIVATION)
/* Complete XDG startup notification */
if (token)
xdg_activation_v1_activate(wayl->xdg_activation, token, win->surface);
xdg_activation_v1_activate(wayl->xdg_activation, token, win->surface.surf);
#endif
if (!wayl_win_subsurface_new(win, &win->overlay, false)) {
@ -1587,33 +1671,33 @@ wayl_win_destroy(struct wl_window *win)
* nor mouse focus).
*/
if (win->render_timer.surf != NULL) {
wl_surface_attach(win->render_timer.surf, NULL, 0, 0);
wl_surface_commit(win->render_timer.surf);
if (win->render_timer.surface.surf != NULL) {
wl_surface_attach(win->render_timer.surface.surf, NULL, 0, 0);
wl_surface_commit(win->render_timer.surface.surf);
}
if (win->scrollback_indicator.surf != NULL) {
wl_surface_attach(win->scrollback_indicator.surf, NULL, 0, 0);
wl_surface_commit(win->scrollback_indicator.surf);
if (win->scrollback_indicator.surface.surf != NULL) {
wl_surface_attach(win->scrollback_indicator.surface.surf, NULL, 0, 0);
wl_surface_commit(win->scrollback_indicator.surface.surf);
}
/* Scrollback search */
if (win->search.surf != NULL) {
wl_surface_attach(win->search.surf, NULL, 0, 0);
wl_surface_commit(win->search.surf);
if (win->search.surface.surf != NULL) {
wl_surface_attach(win->search.surface.surf, NULL, 0, 0);
wl_surface_commit(win->search.surface.surf);
}
/* URLs */
tll_foreach(win->urls, it) {
wl_surface_attach(it->item.surf.surf, NULL, 0, 0);
wl_surface_commit(it->item.surf.surf);
wl_surface_attach(it->item.surf.surface.surf, NULL, 0, 0);
wl_surface_commit(it->item.surf.surface.surf);
}
/* CSD */
for (size_t i = 0; i < ALEN(win->csd.surface); i++) {
if (win->csd.surface[i].surf != NULL) {
wl_surface_attach(win->csd.surface[i].surf, NULL, 0, 0);
wl_surface_commit(win->csd.surface[i].surf);
if (win->csd.surface[i].surface.surf != NULL) {
wl_surface_attach(win->csd.surface[i].surface.surf, NULL, 0, 0);
wl_surface_commit(win->csd.surface[i].surface.surf);
}
}
@ -1621,8 +1705,8 @@ wayl_win_destroy(struct wl_window *win)
/* Main window */
win->unmapped = true;
wl_surface_attach(win->surface, NULL, 0, 0);
wl_surface_commit(win->surface);
wl_surface_attach(win->surface.surf, NULL, 0, 0);
wl_surface_commit(win->surface.surf);
wayl_roundtrip(win->term->wl);
tll_free(win->on_outputs);
@ -1652,6 +1736,12 @@ wayl_win_destroy(struct wl_window *win)
tll_remove(win->xdg_tokens, it);
}
#endif
#if defined(HAVE_FRACTIONAL_SCALE)
if (win->fractional_scale != NULL)
wp_fractional_scale_v1_destroy(win->fractional_scale);
if (win->surface.viewport != NULL)
wp_viewport_destroy(win->surface.viewport);
#endif
if (win->frame_callback != NULL)
wl_callback_destroy(win->frame_callback);
@ -1661,8 +1751,8 @@ wayl_win_destroy(struct wl_window *win)
xdg_toplevel_destroy(win->xdg_toplevel);
if (win->xdg_surface != NULL)
xdg_surface_destroy(win->xdg_surface);
if (win->surface != NULL)
wl_surface_destroy(win->surface);
if (win->surface.surf != NULL)
wl_surface_destroy(win->surface.surf);
wayl_roundtrip(win->term->wl);
@ -1672,7 +1762,7 @@ wayl_win_destroy(struct wl_window *win)
}
bool
wayl_reload_xcursor_theme(struct seat *seat, int new_scale)
wayl_reload_xcursor_theme(struct seat *seat, float new_scale)
{
if (seat->pointer.theme != NULL && seat->pointer.scale == new_scale) {
/* We already have a theme loaded, and the scale hasn't changed */
@ -1705,7 +1795,7 @@ wayl_reload_xcursor_theme(struct seat *seat, int new_scale)
const char *xcursor_theme = getenv("XCURSOR_THEME");
LOG_INFO("cursor theme: %s, size: %d, scale: %d",
LOG_INFO("cursor theme: %s, size: %d, scale: %.2f",
xcursor_theme ? xcursor_theme : "(null)",
xcursor_size, new_scale);
@ -1789,6 +1879,68 @@ wayl_roundtrip(struct wayland *wayl)
wayl_flush(wayl);
}
bool
wayl_fractional_scaling(const struct wayland *wayl)
{
#if defined(HAVE_FRACTIONAL_SCALE)
return wayl->fractional_scale_manager != NULL;
#else
return false;
#endif
}
void
wayl_surface_scale_explicit_width_height(
const struct wl_window *win, const struct wayl_surface *surf,
int width, int height, float scale)
{
if (wayl_fractional_scaling(win->term->wl) && win->have_preferred_scale) {
#if defined(HAVE_FRACTIONAL_SCALE)
LOG_DBG("scaling by a factor of %.2f using fractional scaling "
"(width=%d, height=%d) ", scale, width, height);
wl_surface_set_buffer_scale(surf->surf, 1);
wp_viewport_set_destination(
surf->viewport,
round((float)width / scale),
round((float)height / scale));
#else
BUG("wayl_fraction_scaling() returned true, "
"but fractional scaling was not available at compile time").
#endif
} else {
LOG_DBG("scaling by a factor of %.2f using legacy mode "
"(width=%d, height=%d)", scale, width, height);
xassert(scale == floor(scale));
const int iscale = (int)scale;
xassert(width % iscale == 0);
xassert(height % iscale == 0);
wl_surface_set_buffer_scale(surf->surf, iscale);
}
}
void
wayl_surface_scale(const struct wl_window *win, const struct wayl_surface *surf,
const struct buffer *buf, float scale)
{
wayl_surface_scale_explicit_width_height(
win, surf, buf->width, buf->height, scale);
}
void
wayl_win_scale(struct wl_window *win, const struct buffer *buf)
{
const struct terminal *term = win->term;
const float scale = term->scale;
wayl_surface_scale(win, &win->surface, buf, scale);
}
void
wayl_win_alpha_changed(struct wl_window *win)
{
@ -1800,11 +1952,11 @@ wayl_win_alpha_changed(struct wl_window *win)
if (region != NULL) {
wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX);
wl_surface_set_opaque_region(win->surface, region);
wl_surface_set_opaque_region(win->surface.surf, region);
wl_region_destroy(region);
}
} else
wl_surface_set_opaque_region(win->surface, NULL);
wl_surface_set_opaque_region(win->surface.surf, NULL);
}
#if defined(HAVE_XDG_ACTIVATION)
@ -1815,7 +1967,7 @@ activation_token_for_urgency_done(const char *token, void *data)
struct wayland *wayl = win->term->wl;
win->urgency_token_is_pending = false;
xdg_activation_v1_activate(wayl->xdg_activation, token, win->surface);
xdg_activation_v1_activate(wayl->xdg_activation, token, win->surface.surf);
}
#endif /* HAVE_XDG_ACTIVATION */
@ -1860,27 +2012,43 @@ wayl_win_csd_borders_visible(const struct wl_window *win)
bool
wayl_win_subsurface_new_with_custom_parent(
struct wl_window *win, struct wl_surface *parent,
struct wl_surf_subsurf *surf, bool allow_pointer_input)
struct wayl_sub_surface *surf, bool allow_pointer_input)
{
struct wayland *wayl = win->term->wl;
surf->surf = NULL;
surf->surface.surf = NULL;
surf->sub = NULL;
struct wl_surface *main_surface
= wl_compositor_create_surface(wayl->compositor);
if (main_surface == NULL)
if (main_surface == NULL) {
LOG_ERR("failed to instantiate surface for sub-surface");
return false;
}
struct wl_subsurface *sub = wl_subcompositor_get_subsurface(
wayl->sub_compositor, main_surface, parent);
if (sub == NULL) {
LOG_ERR("failed to instantiate sub-surface");
wl_surface_destroy(main_surface);
return false;
}
#if defined(HAVE_FRACTIONAL_SCALE)
struct wp_viewport *viewport = NULL;
if (wayl->fractional_scale_manager != NULL && wayl->viewporter != NULL) {
viewport = wp_viewporter_get_viewport(wayl->viewporter, main_surface);
if (viewport == NULL) {
LOG_ERR("failed to instantiate viewport for sub-surface");
wl_subsurface_destroy(sub);
wl_surface_destroy(main_surface);
return false;
}
}
#endif
wl_surface_set_user_data(main_surface, win);
wl_subsurface_set_sync(sub);
@ -1892,31 +2060,42 @@ wayl_win_subsurface_new_with_custom_parent(
wl_region_destroy(empty);
}
surf->surf = main_surface;
surf->surface.surf = main_surface;
surf->sub = sub;
#if defined(HAVE_FRACTIONAL_SCALE)
surf->surface.viewport = viewport;
#endif
return true;
}
bool
wayl_win_subsurface_new(struct wl_window *win, struct wl_surf_subsurf *surf,
wayl_win_subsurface_new(struct wl_window *win, struct wayl_sub_surface *surf,
bool allow_pointer_input)
{
return wayl_win_subsurface_new_with_custom_parent(
win, win->surface, surf, allow_pointer_input);
win, win->surface.surf, surf, allow_pointer_input);
}
void
wayl_win_subsurface_destroy(struct wl_surf_subsurf *surf)
wayl_win_subsurface_destroy(struct wayl_sub_surface *surf)
{
if (surf == NULL)
return;
if (surf->sub != NULL)
wl_subsurface_destroy(surf->sub);
if (surf->surf != NULL)
wl_surface_destroy(surf->surf);
surf->surf = NULL;
surf->sub = NULL;
#if defined(HAVE_FRACTIONAL_SCALE)
if (surf->surface.viewport != NULL) {
wp_viewport_destroy(surf->surface.viewport);
surf->surface.viewport = NULL;
}
#endif
if (surf->sub != NULL) {
wl_subsurface_destroy(surf->sub);
surf->sub = NULL;
}
if (surf->surface.surf != NULL) {
wl_surface_destroy(surf->surface.surf);
surf->surface.surf = NULL;
}
}
#if defined(HAVE_XDG_ACTIVATION)
@ -1981,7 +2160,7 @@ wayl_get_activation_token(
if (seat != NULL && serial != 0)
xdg_activation_token_v1_set_serial(token, serial, seat->wl_seat);
xdg_activation_token_v1_set_surface(token, win->surface);
xdg_activation_token_v1_set_surface(token, win->surface.surf);
xdg_activation_token_v1_add_listener(token, &activation_token_listener, ctx);
xdg_activation_token_v1_commit(token);
return true;

View file

@ -20,6 +20,11 @@
#include <xdg-activation-v1.h>
#endif
#if defined(HAVE_FRACTIONAL_SCALE)
#include <viewporter.h>
#include <fractional-scale-v1.h>
#endif
#include <fcft/fcft.h>
#include <tllist.h>
@ -27,6 +32,7 @@
/* Forward declarations */
struct terminal;
struct buffer;
/* Mime-types we support when dealing with data offers (e.g. copy-paste, or DnD) */
enum data_offer_mime_type {
@ -40,6 +46,18 @@ enum data_offer_mime_type {
DATA_OFFER_MIME_TEXT_UTF8_STRING,
};
struct wayl_surface {
struct wl_surface *surf;
#if defined(HAVE_FRACTIONAL_SCALE)
struct wp_viewport *viewport;
#endif
};
struct wayl_sub_surface {
struct wayl_surface surface;
struct wl_subsurface *sub;
};
struct wl_window;
struct wl_clipboard {
struct wl_window *window; /* For DnD */
@ -127,10 +145,10 @@ struct seat {
struct {
uint32_t serial;
struct wl_surface *surface;
struct wayl_surface surface;
struct wl_cursor_theme *theme;
struct wl_cursor *cursor;
int scale;
float scale;
bool hidden;
const char *xcursor;
@ -289,14 +307,9 @@ struct monitor {
bool use_output_release;
};
struct wl_surf_subsurf {
struct wl_surface *surf;
struct wl_subsurface *sub;
};
struct wl_url {
const struct url *url;
struct wl_surf_subsurf surf;
struct wayl_sub_surface surf;
};
enum csd_mode {CSD_UNKNOWN, CSD_NO, CSD_YES};
@ -320,21 +333,27 @@ struct xdg_activation_token_context {
struct wayland;
struct wl_window {
struct terminal *term;
struct wl_surface *surface;
struct wayl_surface surface;
struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
#if defined(HAVE_XDG_ACTIVATION)
tll(struct xdg_activation_token_context *) xdg_tokens;
bool urgency_token_is_pending;
#endif
#if defined(HAVE_FRACTIONAL_SCALE)
struct wp_fractional_scale_v1 *fractional_scale;
#endif
bool unmapped;
float scale;
bool have_preferred_scale;
struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration;
enum csd_mode csd_mode;
struct {
struct wl_surf_subsurf surface[CSD_SURF_COUNT];
struct wayl_sub_surface surface[CSD_SURF_COUNT];
struct fcft_font *font;
int move_timeout_fd;
uint32_t serial;
@ -345,10 +364,10 @@ struct wl_window {
bool minimize:1;
} wm_capabilities;
struct wl_surf_subsurf search;
struct wl_surf_subsurf scrollback_indicator;
struct wl_surf_subsurf render_timer;
struct wl_surf_subsurf overlay;
struct wayl_sub_surface search;
struct wayl_sub_surface scrollback_indicator;
struct wayl_sub_surface render_timer;
struct wayl_sub_surface overlay;
struct wl_callback *frame_callback;
@ -414,6 +433,11 @@ struct wayland {
struct zwp_text_input_manager_v3 *text_input_manager;
#endif
#if defined(HAVE_FRACTIONAL_SCALE)
struct wp_viewporter *viewporter;
struct wp_fractional_scale_manager_v1 *fractional_scale_manager;
#endif
bool have_argb8888;
tll(struct monitor) monitors; /* All available outputs */
tll(struct seat) seats;
@ -426,14 +450,23 @@ struct wayland *wayl_init(
bool presentation_timings);
void wayl_destroy(struct wayland *wayl);
bool wayl_reload_xcursor_theme(struct seat *seat, int new_scale);
bool wayl_reload_xcursor_theme(struct seat *seat, float new_scale);
void wayl_flush(struct wayland *wayl);
void wayl_roundtrip(struct wayland *wayl);
bool wayl_fractional_scaling(const struct wayland *wayl);
void wayl_surface_scale(
const struct wl_window *win, const struct wayl_surface *surf,
const struct buffer *buf, float scale);
void wayl_surface_scale_explicit_width_height(
const struct wl_window *win, const struct wayl_surface *surf,
int width, int height, float scale);
struct wl_window *wayl_win_init(struct terminal *term, const char *token);
void wayl_win_destroy(struct wl_window *win);
void wayl_win_scale(struct wl_window *win, const struct buffer *buf);
void wayl_win_alpha_changed(struct wl_window *win);
bool wayl_win_set_urgent(struct wl_window *win);
@ -441,12 +474,12 @@ bool wayl_win_csd_titlebar_visible(const struct wl_window *win);
bool wayl_win_csd_borders_visible(const struct wl_window *win);
bool wayl_win_subsurface_new(
struct wl_window *win, struct wl_surf_subsurf *surf,
struct wl_window *win, struct wayl_sub_surface *surf,
bool allow_pointer_input);
bool wayl_win_subsurface_new_with_custom_parent(
struct wl_window *win, struct wl_surface *parent,
struct wl_surf_subsurf *surf, bool allow_pointer_input);
void wayl_win_subsurface_destroy(struct wl_surf_subsurf *surf);
struct wayl_sub_surface *surf, bool allow_pointer_input);
void wayl_win_subsurface_destroy(struct wayl_sub_surface *surf);
#if defined(HAVE_XDG_ACTIVATION)
bool wayl_get_activation_token(