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 is `auto`, which will select `libutempter` on Linux, `ulog` on
FreeBSD, and `none` for all others. FreeBSD, and `none` for all others.
* Sixel aspect ratio. * 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 ### Changed
@ -77,6 +80,8 @@
is explicitly added. is explicitly added.
* Default sixel aspect ratio is now 2:1 instead of 1:1. * Default sixel aspect ratio is now 2:1 instead of 1:1.
* Sixel images are no longer cropped to the last non-transparent row. * 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 [1371]: https://codeberg.org/dnkl/foot/pulls/1371
@ -89,6 +94,10 @@
### Removed ### Removed
* `auto` value for the `dpi-aware` option.
### Fixed ### Fixed
* Incorrect icon in dock and window switcher on Gnome ([#1317][1317]) * 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 **for a reason**; a given point size should have the same height on
all mediums, be it printers or monitors, regardless of their DPI. all mediums, be it printers or monitors, regardless of their DPI.
Foots default behavior is to use the monitors DPI to size fonts when That said, on Wayland, Hi-DPI monitors are typically handled by
output scaling has been disabled on **all** monitors. If at least one configuring a _"scaling factor"_ in the compositor. This is usually
monitor has output scaling enabled, fonts will instead by sized using expressed as either a rational value (e.g. _1.5_), or as a percentage
the scaling factor. (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 For this reason, and because of the new _fractional scaling_ protocol
(regardless of scaling factor), or to **never** use it, with the (see below for details), and because this is how Wayland applications
`dpi-aware` option in `foot.ini`. See the man page, **foot.ini**(5) are expected to behave, foot >= 1.15 will default to scaling fonts
for more information. using the compositors scaling factor, and **not** the monitor
DPI.
When fonts are sized using the monitors DPI, glyphs should always This means the (assuming the monitors are at the same viewing
have the same physical height, regardless of monitor. 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 This can be changed by setting the `dpi-aware` option to `yes` in
moved between screens with different DPIs values. If the window covers `foot.ini`. When enabled, fonts will **not** be sized using the
multiple screens, with different DPIs, the highest DPI will be used. 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 _Note_: if you configure **pixelsize**, rather than **size**, then DPI
changes will **not** change the font size. Pixels are always pixels. 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 ## Supported OSCs
OSC, _Operating System Command_, are escape sequences that interacts OSC, _Operating System Command_, are escape sequences that interacts

View file

@ -66,11 +66,13 @@ static const char *
version_and_features(void) version_and_features(void)
{ {
static char buf[256]; 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, FOOT_VERSION,
feature_pgo() ? '+' : '-', feature_pgo() ? '+' : '-',
feature_ime() ? '+' : '-', feature_ime() ? '+' : '-',
feature_graphemes() ? '+' : '-', feature_graphemes() ? '+' : '-',
feature_fractional_scaling() ? '+' : ':',
feature_assertions() ? '+' : '-'); feature_assertions() ? '+' : '-');
return buf; return buf;
} }

View file

@ -972,17 +972,8 @@ parse_section_main(struct context *ctx)
else if (strcmp(key, "underline-thickness") == 0) else if (strcmp(key, "underline-thickness") == 0)
return value_to_pt_or_px(ctx, &conf->underline_thickness); return value_to_pt_or_px(ctx, &conf->underline_thickness);
else if (strcmp(key, "dpi-aware") == 0) { else if (strcmp(key, "dpi-aware") == 0)
if (strcmp(value, "auto") == 0) return value_to_bool(ctx, &conf->dpi_aware);
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, "workers") == 0) else if (strcmp(key, "workers") == 0)
return value_to_uint16(ctx, 10, &conf->render_worker_count); 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, .use_custom_underline_offset = false,
.box_drawings_uses_font_glyphs = false, .box_drawings_uses_font_glyphs = false,
.underline_thickness = {.pt = 0., .px = -1}, .underline_thickness = {.pt = 0., .px = -1},
.dpi_aware = DPI_AWARE_AUTO, /* DPI-aware when scaling-factor == 1 */ .dpi_aware = false,
.bell = { .bell = {
.urgent = false, .urgent = false,
.notify = false, .notify = false,

View file

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

View file

@ -185,7 +185,7 @@ empty string to be set, but it must be quoted: *KEY=""*)
Default: _no_. Default: _no_.
*dpi-aware* *dpi-aware*
*auto*, *yes*, or *no*. Boolean.
When set to *yes*, fonts are sized using the monitor's DPI, making 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 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 instead sized using the monitor's scaling factor; doubling the
scaling factor *does* double the font size. 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, Note that this option typically does not work with bitmap fonts,
which only contains a pre-defined set of sizes, and cannot be which only contains a pre-defined set of sizes, and cannot be
dynamically scaled. Whichever size (of the available ones) that 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 to size the font (*dpi-aware=no*), the font's pixel size will be
multiplied with the scaling factor. multiplied with the scaling factor.
Default: _auto_ Default: _no_
*pad* *pad*
Padding between border and glyphs, in pixels (subject to output Padding between border and glyphs, in pixels (subject to output

View file

@ -37,3 +37,12 @@ static inline bool feature_graphemes(void)
return false; return false;
#endif #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-offset=<font metrics>
# underline-thickness=<font underline thickness> # underline-thickness=<font underline thickness>
# box-drawings-uses-font-glyphs=no # box-drawings-uses-font-glyphs=no
# dpi-aware=auto # dpi-aware=no
# initial-window-size-pixels=700x500 # Or, # initial-window-size-pixels=700x500 # Or,
# initial-window-size-chars=<COLSxROWS> # initial-window-size-chars=<COLSxROWS>

4
main.c
View file

@ -52,11 +52,13 @@ static const char *
version_and_features(void) version_and_features(void)
{ {
static char buf[256]; 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, FOOT_VERSION,
feature_pgo() ? '+' : '-', feature_pgo() ? '+' : '-',
feature_ime() ? '+' : '-', feature_ime() ? '+' : '-',
feature_graphemes() ? '+' : '-', feature_graphemes() ? '+' : '-',
feature_fractional_scaling() ? '+' : '-',
feature_assertions() ? '+' : '-'); feature_assertions() ? '+' : '-');
return buf; return buf;
} }

View file

@ -158,6 +158,11 @@ if wayland_protocols.version().version_compare('>=1.21')
add_project_arguments('-DHAVE_XDG_ACTIVATION', language: 'c') add_project_arguments('-DHAVE_XDG_ACTIVATION', language: 'c')
wl_proto_xml += [wayland_protocols_datadir + '/staging/xdg-activation/xdg-activation-v1.xml'] wl_proto_xml += [wayland_protocols_datadir + '/staging/xdg-activation/xdg-activation-v1.xml']
endif 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 foreach prot : wl_proto_xml
wl_proto_headers += custom_target( wl_proto_headers += custom_target(

View file

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

View file

@ -912,42 +912,6 @@ get_font_subpixel(const struct terminal *term)
return FCFT_SUBPIXEL_DEFAULT; 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 int
term_pt_or_px_as_pixels(const struct terminal *term, term_pt_or_px_as_pixels(const struct terminal *term,
const struct pt_or_px *pt_or_px) 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; bool use_px_size = term->font_sizes[i][j].px_size > 0;
char size[64]; 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) if (use_px_size)
snprintf(size, sizeof(size), ":pixelsize=%d", 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 else
snprintf(size, sizeof(size), ":size=%.2f", 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; size_t len = strlen(font->pattern) + strlen(size) + 1;
names[i][j] = xmalloc(len); names[i][j] = xmalloc(len);
@ -1232,7 +1196,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
.reverse_wrap = true, .reverse_wrap = true,
.auto_margin = true, .auto_margin = true,
.window_title_stack = tll_init(), .window_title_stack = tll_init(),
.scale = 1, .scale = 1.,
.flash = {.fd = flash_fd}, .flash = {.fd = flash_fd},
.blink = {.fd = -1}, .blink = {.fd = -1},
.vt = { .vt = {
@ -2158,7 +2122,7 @@ term_font_dpi_changed(struct terminal *term, int old_scale)
xassert(term->scale > 0); xassert(term->scale > 0);
bool was_scaled_using_dpi = term->font_is_sized_by_dpi; 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 = bool need_font_reload =
was_scaled_using_dpi != will_scale_using_dpi || 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 enum term_surface
term_surface_kind(const struct terminal *term, const struct wl_surface *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; 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; 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; 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; 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; 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; 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; 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; 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; return TERM_SURF_BUTTON_CLOSE;
else else
return TERM_SURF_NONE; return TERM_SURF_NONE;

View file

@ -454,7 +454,7 @@ struct terminal {
int fd; int fd;
} blink; } blink;
int scale; float scale;
int width; /* pixels */ int width; /* pixels */
int height; /* pixels */ int height; /* pixels */
int stashed_width; 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, "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, "locked-title", &conf.locked_title);
test_boolean(&ctx, &parse_section_main, "notify-focus-inhibit", &conf.notify_focus_inhibit); 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, "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); 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_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", test_enum(&ctx, &parse_section_main, "selection-target",
4, 4,
(const char *[]){"none", "primary", "clipboard", "both"}, (const char *[]){"none", "primary", "clipboard", "both"},

311
wayland.c
View file

@ -32,12 +32,12 @@
#include "xmalloc.h" #include "xmalloc.h"
static void 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; struct terminal *term = win->term;
const struct config *conf = term->conf; 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; bool enable_csd = win->csd_mode == CSD_YES && !win->is_fullscreen;
if (!enable_csd) 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; patterns[i] = conf->csd.font.arr[i].pattern;
char pixelsize[32]; char pixelsize[32];
snprintf(pixelsize, sizeof(pixelsize), snprintf(pixelsize, sizeof(pixelsize), "pixelsize=%u",
"pixelsize=%u", conf->csd.title_height * scale * 1 / 2); (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); patterns[0], pixelsize, old_scale, scale);
win->csd.font = fcft_from_name(conf->csd.font.count, patterns, pixelsize); 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++) { for (size_t i = CSD_SURF_MINIMIZE; i < CSD_SURF_COUNT; i++) {
bool ret = wayl_win_subsurface_new_with_custom_parent( 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); true);
xassert(ret); xassert(ret);
} }
csd_reload_font(win, -1); csd_reload_font(win, -1.);
} }
static void static void
@ -187,8 +187,12 @@ seat_destroy(struct seat *seat)
if (seat->pointer.theme != NULL) if (seat->pointer.theme != NULL)
wl_cursor_theme_destroy(seat->pointer.theme); wl_cursor_theme_destroy(seat->pointer.theme);
if (seat->pointer.surface != NULL) if (seat->pointer.surface.surf != NULL)
wl_surface_destroy(seat->pointer.surface); 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) if (seat->pointer.xcursor_callback != NULL)
wl_callback_destroy(seat->pointer.xcursor_callback); 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 (caps & WL_SEAT_CAPABILITY_POINTER) {
if (seat->wl_pointer == NULL) { if (seat->wl_pointer == NULL) {
xassert(seat->pointer.surface == NULL); xassert(seat->pointer.surface.surf == NULL);
seat->pointer.surface = wl_compositor_create_surface(seat->wayl->compositor); 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); LOG_ERR("%s: failed to create pointer surface", seat->name);
return; 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); seat->wl_pointer = wl_seat_get_pointer(wl_seat);
wl_pointer_add_listener(seat->wl_pointer, &pointer_listener, seat); wl_pointer_add_listener(seat->wl_pointer, &pointer_listener, seat);
} }
} else { } else {
if (seat->wl_pointer != NULL) { if (seat->wl_pointer != NULL) {
wl_pointer_release(seat->wl_pointer); 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) if (seat->pointer.theme != NULL)
wl_cursor_theme_destroy(seat->pointer.theme); wl_cursor_theme_destroy(seat->pointer.theme);
seat->wl_pointer = NULL; seat->wl_pointer = NULL;
seat->pointer.surface = NULL; seat->pointer.surface.surf = NULL;
seat->pointer.theme = NULL; seat->pointer.theme = NULL;
seat->pointer.cursor = 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) if (tll_length(term->window->on_outputs) == 0)
return; return;
int old_scale = term->scale; float old_scale = term->scale;
render_resize(term, term->width / term->scale, term->height / term->scale); render_resize(term, term->width / term->scale, term->height / term->scale);
term_font_dpi_changed(term, old_scale); term_font_dpi_changed(term, old_scale);
@ -350,11 +373,6 @@ update_terms_on_monitor(struct monitor *mon)
tll_foreach(wayl->terms, it) { tll_foreach(wayl->terms, it) {
struct terminal *term = it->item; 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) { tll_foreach(term->window->on_outputs, it2) {
if (it2->item == mon) { if (it2->item == mon) {
update_term_for_output_change(term); 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 * anytime soon. Some compositors require a commit in
* combination with an ack - make them happy. * combination with an ack - make them happy.
*/ */
wl_surface_commit(win->surface); wl_surface_commit(win->surface.surf);
} }
if (wasnt_configured) if (wasnt_configured)
@ -1121,6 +1139,27 @@ handle_global(void *data, struct wl_registry *registry,
} }
#endif #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 #if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
else if (strcmp(interface, zwp_text_input_manager_v3_interface.name) == 0) { else if (strcmp(interface, zwp_text_input_manager_v3_interface.name) == 0) {
const uint32_t required = 1; 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) if (seat->wl_keyboard != NULL)
keyboard_listener.leave( 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) { 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) if (seat->wl_pointer != NULL)
pointer_listener.leave( 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); 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"); "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) { if (presentation_timings && wayl->presentation == NULL) {
LOG_ERR("presentation time interface not implemented by compositor"); LOG_ERR("presentation time interface not implemented by compositor");
goto out; goto out;
@ -1435,6 +1482,12 @@ wayl_destroy(struct wayland *wayl)
zwp_text_input_manager_v3_destroy(wayl->text_input_manager); zwp_text_input_manager_v3_destroy(wayl->text_input_manager);
#endif #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 defined(HAVE_XDG_ACTIVATION)
if (wayl->xdg_activation != NULL) if (wayl->xdg_activation != NULL)
xdg_activation_v1_destroy(wayl->xdg_activation); xdg_activation_v1_destroy(wayl->xdg_activation);
@ -1469,6 +1522,24 @@ wayl_destroy(struct wayland *wayl)
free(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 * struct wl_window *
wayl_win_init(struct terminal *term, const char *token) 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_mode = CSD_UNKNOWN;
win->csd.move_timeout_fd = -1; win->csd.move_timeout_fd = -1;
win->resize_timeout_fd = -1; win->resize_timeout_fd = -1;
win->scale = -1.;
win->wm_capabilities.maximize = true; win->wm_capabilities.maximize = true;
win->wm_capabilities.minimize = true; win->wm_capabilities.minimize = true;
win->surface = wl_compositor_create_surface(wayl->compositor); win->surface.surf = wl_compositor_create_surface(wayl->compositor);
if (win->surface == NULL) { if (win->surface.surf == NULL) {
LOG_ERR("failed to create wayland surface"); LOG_ERR("failed to create wayland surface");
goto out; goto out;
} }
wayl_win_alpha_changed(win); 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); xdg_surface_add_listener(win->xdg_surface, &xdg_surface_listener, win);
win->xdg_toplevel = xdg_surface_get_toplevel(win->xdg_surface); 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"); 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) #if defined(HAVE_XDG_ACTIVATION)
/* Complete XDG startup notification */ /* Complete XDG startup notification */
if (token) if (token)
xdg_activation_v1_activate(wayl->xdg_activation, token, win->surface); xdg_activation_v1_activate(wayl->xdg_activation, token, win->surface.surf);
#endif #endif
if (!wayl_win_subsurface_new(win, &win->overlay, false)) { if (!wayl_win_subsurface_new(win, &win->overlay, false)) {
@ -1587,33 +1671,33 @@ wayl_win_destroy(struct wl_window *win)
* nor mouse focus). * nor mouse focus).
*/ */
if (win->render_timer.surf != NULL) { if (win->render_timer.surface.surf != NULL) {
wl_surface_attach(win->render_timer.surf, NULL, 0, 0); wl_surface_attach(win->render_timer.surface.surf, NULL, 0, 0);
wl_surface_commit(win->render_timer.surf); wl_surface_commit(win->render_timer.surface.surf);
} }
if (win->scrollback_indicator.surf != NULL) { if (win->scrollback_indicator.surface.surf != NULL) {
wl_surface_attach(win->scrollback_indicator.surf, NULL, 0, 0); wl_surface_attach(win->scrollback_indicator.surface.surf, NULL, 0, 0);
wl_surface_commit(win->scrollback_indicator.surf); wl_surface_commit(win->scrollback_indicator.surface.surf);
} }
/* Scrollback search */ /* Scrollback search */
if (win->search.surf != NULL) { if (win->search.surface.surf != NULL) {
wl_surface_attach(win->search.surf, NULL, 0, 0); wl_surface_attach(win->search.surface.surf, NULL, 0, 0);
wl_surface_commit(win->search.surf); wl_surface_commit(win->search.surface.surf);
} }
/* URLs */ /* URLs */
tll_foreach(win->urls, it) { tll_foreach(win->urls, it) {
wl_surface_attach(it->item.surf.surf, NULL, 0, 0); wl_surface_attach(it->item.surf.surface.surf, NULL, 0, 0);
wl_surface_commit(it->item.surf.surf); wl_surface_commit(it->item.surf.surface.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].surface.surf != NULL) {
wl_surface_attach(win->csd.surface[i].surf, NULL, 0, 0); wl_surface_attach(win->csd.surface[i].surface.surf, NULL, 0, 0);
wl_surface_commit(win->csd.surface[i].surf); wl_surface_commit(win->csd.surface[i].surface.surf);
} }
} }
@ -1621,8 +1705,8 @@ wayl_win_destroy(struct wl_window *win)
/* Main window */ /* Main window */
win->unmapped = true; win->unmapped = true;
wl_surface_attach(win->surface, NULL, 0, 0); wl_surface_attach(win->surface.surf, NULL, 0, 0);
wl_surface_commit(win->surface); wl_surface_commit(win->surface.surf);
wayl_roundtrip(win->term->wl); wayl_roundtrip(win->term->wl);
tll_free(win->on_outputs); tll_free(win->on_outputs);
@ -1652,6 +1736,12 @@ wayl_win_destroy(struct wl_window *win)
tll_remove(win->xdg_tokens, it); 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 #endif
if (win->frame_callback != NULL) if (win->frame_callback != NULL)
wl_callback_destroy(win->frame_callback); wl_callback_destroy(win->frame_callback);
@ -1661,8 +1751,8 @@ wayl_win_destroy(struct wl_window *win)
xdg_toplevel_destroy(win->xdg_toplevel); xdg_toplevel_destroy(win->xdg_toplevel);
if (win->xdg_surface != NULL) if (win->xdg_surface != NULL)
xdg_surface_destroy(win->xdg_surface); xdg_surface_destroy(win->xdg_surface);
if (win->surface != NULL) if (win->surface.surf != NULL)
wl_surface_destroy(win->surface); wl_surface_destroy(win->surface.surf);
wayl_roundtrip(win->term->wl); wayl_roundtrip(win->term->wl);
@ -1672,7 +1762,7 @@ wayl_win_destroy(struct wl_window *win)
} }
bool 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) { if (seat->pointer.theme != NULL && seat->pointer.scale == new_scale) {
/* We already have a theme loaded, and the scale hasn't changed */ /* 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"); 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_theme ? xcursor_theme : "(null)",
xcursor_size, new_scale); xcursor_size, new_scale);
@ -1789,6 +1879,68 @@ wayl_roundtrip(struct wayland *wayl)
wayl_flush(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 void
wayl_win_alpha_changed(struct wl_window *win) wayl_win_alpha_changed(struct wl_window *win)
{ {
@ -1800,11 +1952,11 @@ wayl_win_alpha_changed(struct wl_window *win)
if (region != NULL) { if (region != NULL) {
wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); 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); wl_region_destroy(region);
} }
} else } else
wl_surface_set_opaque_region(win->surface, NULL); wl_surface_set_opaque_region(win->surface.surf, NULL);
} }
#if defined(HAVE_XDG_ACTIVATION) #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; struct wayland *wayl = win->term->wl;
win->urgency_token_is_pending = false; 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 */ #endif /* HAVE_XDG_ACTIVATION */
@ -1860,27 +2012,43 @@ wayl_win_csd_borders_visible(const struct wl_window *win)
bool bool
wayl_win_subsurface_new_with_custom_parent( wayl_win_subsurface_new_with_custom_parent(
struct wl_window *win, struct wl_surface *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; struct wayland *wayl = win->term->wl;
surf->surf = NULL; surf->surface.surf = NULL;
surf->sub = NULL; surf->sub = NULL;
struct wl_surface *main_surface struct wl_surface *main_surface
= wl_compositor_create_surface(wayl->compositor); = 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; return false;
}
struct wl_subsurface *sub = wl_subcompositor_get_subsurface( struct wl_subsurface *sub = wl_subcompositor_get_subsurface(
wayl->sub_compositor, main_surface, parent); wayl->sub_compositor, main_surface, parent);
if (sub == NULL) { if (sub == NULL) {
LOG_ERR("failed to instantiate sub-surface");
wl_surface_destroy(main_surface); wl_surface_destroy(main_surface);
return false; 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_surface_set_user_data(main_surface, win);
wl_subsurface_set_sync(sub); wl_subsurface_set_sync(sub);
@ -1892,31 +2060,42 @@ wayl_win_subsurface_new_with_custom_parent(
wl_region_destroy(empty); wl_region_destroy(empty);
} }
surf->surf = main_surface; surf->surface.surf = main_surface;
surf->sub = sub; surf->sub = sub;
#if defined(HAVE_FRACTIONAL_SCALE)
surf->surface.viewport = viewport;
#endif
return true; return true;
} }
bool 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) bool allow_pointer_input)
{ {
return wayl_win_subsurface_new_with_custom_parent( return wayl_win_subsurface_new_with_custom_parent(
win, win->surface, surf, allow_pointer_input); win, win->surface.surf, surf, allow_pointer_input);
} }
void void
wayl_win_subsurface_destroy(struct wl_surf_subsurf *surf) wayl_win_subsurface_destroy(struct wayl_sub_surface *surf)
{ {
if (surf == NULL) if (surf == NULL)
return; return;
if (surf->sub != NULL)
wl_subsurface_destroy(surf->sub);
if (surf->surf != NULL)
wl_surface_destroy(surf->surf);
surf->surf = NULL; #if defined(HAVE_FRACTIONAL_SCALE)
surf->sub = NULL; 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) #if defined(HAVE_XDG_ACTIVATION)
@ -1981,7 +2160,7 @@ wayl_get_activation_token(
if (seat != NULL && serial != 0) if (seat != NULL && serial != 0)
xdg_activation_token_v1_set_serial(token, serial, seat->wl_seat); 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_add_listener(token, &activation_token_listener, ctx);
xdg_activation_token_v1_commit(token); xdg_activation_token_v1_commit(token);
return true; return true;

View file

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