diff --git a/GNUmakefile b/GNUmakefile
index 724b071..a1fc704 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -85,15 +85,33 @@ lib/renderers/wayland/wlr-layer-shell-unstable-v1.h: lib/renderers/wayland/wlr-l
lib/renderers/wayland/wlr-layer-shell-unstable-v1.c: lib/renderers/wayland/wlr-layer-shell-unstable-v1.xml
wayland-scanner private-code < $^ > $@
+lib/renderers/wayland/fractional-scale-v1.h: lib/renderers/wayland/fractional-scale-v1.xml
+ wayland-scanner client-header < $^ > $@
+
+lib/renderers/wayland/fractional-scale-v1.c: lib/renderers/wayland/fractional-scale-v1.xml
+ wayland-scanner private-code < $^ > $@
+
+lib/renderers/wayland/viewporter.h: lib/renderers/wayland/viewporter.xml
+ wayland-scanner client-header < $^ > $@
+
+lib/renderers/wayland/viewporter.c: lib/renderers/wayland/viewporter.xml
+ wayland-scanner private-code < $^ > $@
+
xdg-shell.a: private override LDFLAGS += -fPIC
xdg-shell.a: private override CPPFLAGS += $(shell $(PKG_CONFIG) --cflags-only-I wayland-client)
xdg-shell.a: lib/renderers/wayland/xdg-shell.c
wlr-layer-shell.a: private override LDFLAGS += -fPIC
wlr-layer-shell.a: private override CPPFLAGS += $(shell $(PKG_CONFIG) --cflags-only-I wayland-client)
wlr-layer-shell.a: lib/renderers/wayland/wlr-layer-shell-unstable-v1.c lib/renderers/wayland/wlr-layer-shell-unstable-v1.h
+fractional-scale.a: private override LDFLAGS += -fPIC
+fractional-scale.a: private override CPPFLAGS += $(shell $(PKG_CONFIG) --cflags-only-I wayland-client)
+fractional-scale.a: lib/renderers/wayland/fractional-scale-v1.c lib/renderers/wayland/fractional-scale-v1.h
+viewporter.a: private override LDFLAGS += -fPIC
+viewporter.a: private override CPPFLAGS += $(shell $(PKG_CONFIG) --cflags-only-I wayland-client)
+viewporter.a: lib/renderers/wayland/viewporter.c lib/renderers/wayland/viewporter.h
bemenu-renderer-wayland.so: private override LDLIBS += $(shell $(PKG_CONFIG) --libs wayland-client cairo pango pangocairo xkbcommon)
bemenu-renderer-wayland.so: private override CPPFLAGS += $(shell $(PKG_CONFIG) --cflags-only-I wayland-client cairo pango pangocairo xkbcommon)
-bemenu-renderer-wayland.so: lib/renderers/cairo_renderer.h lib/renderers/wayland/wayland.c lib/renderers/wayland/wayland.h lib/renderers/wayland/registry.c lib/renderers/wayland/window.c xdg-shell.a wlr-layer-shell.a util.a
+bemenu-renderer-wayland.so: lib/renderers/cairo_renderer.h lib/renderers/wayland/wayland.c lib/renderers/wayland/wayland.h lib/renderers/wayland/registry.c lib/renderers/wayland/window.c xdg-shell.a wlr-layer-shell.a fractional-scale.a viewporter.a util.a
common.a: client/common/common.c client/common/common.h
bemenu: common.a client/bemenu.c
diff --git a/lib/renderers/cairo_renderer.h b/lib/renderers/cairo_renderer.h
index 80e9cfe..8b3ef70 100644
--- a/lib/renderers/cairo_renderer.h
+++ b/lib/renderers/cairo_renderer.h
@@ -12,7 +12,7 @@ struct cairo {
cairo_t *cr;
cairo_surface_t *surface;
PangoContext *pango;
- int scale;
+ double scale;
};
struct cairo_color {
@@ -67,6 +67,8 @@ bm_cairo_create_for_surface(struct cairo *cairo, cairo_surface_t *surface)
if (!(cairo->pango = pango_cairo_create_context(cairo->cr)))
goto fail;
+ cairo_set_antialias(cairo->cr, CAIRO_ANTIALIAS_NONE);
+
cairo->surface = surface;
assert(cairo->scale > 0);
cairo_surface_set_device_scale(surface, cairo->scale, cairo->scale);
diff --git a/lib/renderers/wayland/fractional-scale-v1.xml b/lib/renderers/wayland/fractional-scale-v1.xml
new file mode 100644
index 0000000..350bfc0
--- /dev/null
+++ b/lib/renderers/wayland/fractional-scale-v1.xml
@@ -0,0 +1,102 @@
+
+
+
+ Copyright © 2022 Kenny Levinsen
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+
+
+ This protocol allows a compositor to suggest for surfaces to render at
+ fractional scales.
+
+ A client can submit scaled content by utilizing wp_viewport. This is done by
+ creating a wp_viewport object for the surface and setting the destination
+ rectangle to the surface size before the scale factor is applied.
+
+ The buffer size is calculated by multiplying the surface size by the
+ intended scale.
+
+ The wl_surface buffer scale should remain set to 1.
+
+ If a surface has a surface-local size of 100 px by 50 px and wishes to
+ submit buffers with a scale of 1.5, then a buffer of 150px by 75 px should
+ be used and the wp_viewport destination rectangle should be 100 px by 50 px.
+
+ For toplevel surfaces, the size is rounded halfway away from zero. The
+ rounding algorithm for subsurface position and size is not defined.
+
+
+
+
+ A global interface for requesting surfaces to use fractional scales.
+
+
+
+
+ Informs the server that the client will not be using this protocol
+ object anymore. This does not affect any other objects,
+ wp_fractional_scale_v1 objects included.
+
+
+
+
+
+
+
+
+
+ Create an add-on object for the the wl_surface to let the compositor
+ request fractional scales. If the given wl_surface already has a
+ wp_fractional_scale_v1 object associated, the fractional_scale_exists
+ protocol error is raised.
+
+
+
+
+
+
+
+
+ An additional interface to a wl_surface object which allows the compositor
+ to inform the client of the preferred scale.
+
+
+
+
+ Destroy the fractional scale object. When this object is destroyed,
+ preferred_scale events will no longer be sent.
+
+
+
+
+
+ Notification of a new preferred scale for this surface that the
+ compositor suggests that the client should use.
+
+ The sent scale is the numerator of a fraction with a denominator of 120.
+
+
+
+
+
diff --git a/lib/renderers/wayland/registry.c b/lib/renderers/wayland/registry.c
index 40861aa..e41ae0a 100644
--- a/lib/renderers/wayland/registry.c
+++ b/lib/renderers/wayland/registry.c
@@ -595,6 +595,10 @@ registry_handle_global(void *data, struct wl_registry *registry, uint32_t id, co
output->output = wl_output;
wl_list_insert(&wayland->outputs, &output->link);
wl_output_add_listener(wl_output, &output_listener, output);
+ } else if (strcmp(interface, "wp_fractional_scale_manager_v1") == 0) {
+ wayland->wfs_mgr = wl_registry_bind(registry, id, &wp_fractional_scale_manager_v1_interface, 1);
+ } else if (strcmp(interface, "wp_viewporter") == 0) {
+ wayland->viewporter = wl_registry_bind(registry, id, &wp_viewporter_interface, 1);
}
}
@@ -660,6 +664,9 @@ bm_wl_registry_register(struct wayland *wayland)
if (!wayland->input.keyboard || !(wayland->formats & (1 << WL_SHM_FORMAT_ARGB8888)))
return false;
+ if (wayland->wfs_mgr && wayland->viewporter)
+ wayland->fractional_scaling = true;
+
set_repeat_info(&wayland->input, 40, 400);
return true;
}
diff --git a/lib/renderers/wayland/viewporter.xml b/lib/renderers/wayland/viewporter.xml
new file mode 100644
index 0000000..d1048d1
--- /dev/null
+++ b/lib/renderers/wayland/viewporter.xml
@@ -0,0 +1,180 @@
+
+
+
+
+ Copyright © 2013-2016 Collabora, Ltd.
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+
+
+
+ The global interface exposing surface cropping and scaling
+ capabilities is used to instantiate an interface extension for a
+ wl_surface object. This extended interface will then allow
+ cropping and scaling the surface contents, effectively
+ disconnecting the direct relationship between the buffer and the
+ surface size.
+
+
+
+
+ Informs the server that the client will not be using this
+ protocol object anymore. This does not affect any other objects,
+ wp_viewport objects included.
+
+
+
+
+
+
+
+
+
+ Instantiate an interface extension for the given wl_surface to
+ crop and scale its content. If the given wl_surface already has
+ a wp_viewport object associated, the viewport_exists
+ protocol error is raised.
+
+
+
+
+
+
+
+
+ An additional interface to a wl_surface object, which allows the
+ client to specify the cropping and scaling of the surface
+ contents.
+
+ This interface works with two concepts: the source rectangle (src_x,
+ src_y, src_width, src_height), and the destination size (dst_width,
+ dst_height). The contents of the source rectangle are scaled to the
+ destination size, and content outside the source rectangle is ignored.
+ This state is double-buffered, and is applied on the next
+ wl_surface.commit.
+
+ The two parts of crop and scale state are independent: the source
+ rectangle, and the destination size. Initially both are unset, that
+ is, no scaling is applied. The whole of the current wl_buffer is
+ used as the source, and the surface size is as defined in
+ wl_surface.attach.
+
+ If the destination size is set, it causes the surface size to become
+ dst_width, dst_height. The source (rectangle) is scaled to exactly
+ this size. This overrides whatever the attached wl_buffer size is,
+ unless the wl_buffer is NULL. If the wl_buffer is NULL, the surface
+ has no content and therefore no size. Otherwise, the size is always
+ at least 1x1 in surface local coordinates.
+
+ If the source rectangle is set, it defines what area of the wl_buffer is
+ taken as the source. If the source rectangle is set and the destination
+ size is not set, then src_width and src_height must be integers, and the
+ surface size becomes the source rectangle size. This results in cropping
+ without scaling. If src_width or src_height are not integers and
+ destination size is not set, the bad_size protocol error is raised when
+ the surface state is applied.
+
+ The coordinate transformations from buffer pixel coordinates up to
+ the surface-local coordinates happen in the following order:
+ 1. buffer_transform (wl_surface.set_buffer_transform)
+ 2. buffer_scale (wl_surface.set_buffer_scale)
+ 3. crop and scale (wp_viewport.set*)
+ This means, that the source rectangle coordinates of crop and scale
+ are given in the coordinates after the buffer transform and scale,
+ i.e. in the coordinates that would be the surface-local coordinates
+ if the crop and scale was not applied.
+
+ If src_x or src_y are negative, the bad_value protocol error is raised.
+ Otherwise, if the source rectangle is partially or completely outside of
+ the non-NULL wl_buffer, then the out_of_buffer protocol error is raised
+ when the surface state is applied. A NULL wl_buffer does not raise the
+ out_of_buffer error.
+
+ If the wl_surface associated with the wp_viewport is destroyed,
+ all wp_viewport requests except 'destroy' raise the protocol error
+ no_surface.
+
+ If the wp_viewport object is destroyed, the crop and scale
+ state is removed from the wl_surface. The change will be applied
+ on the next wl_surface.commit.
+
+
+
+
+ The associated wl_surface's crop and scale state is removed.
+ The change is applied on the next wl_surface.commit.
+
+
+
+
+
+
+
+
+
+
+
+
+ Set the source rectangle of the associated wl_surface. See
+ wp_viewport for the description, and relation to the wl_buffer
+ size.
+
+ If all of x, y, width and height are -1.0, the source rectangle is
+ unset instead. Any other set of values where width or height are zero
+ or negative, or x or y are negative, raise the bad_value protocol
+ error.
+
+ The crop and scale state is double-buffered state, and will be
+ applied on the next wl_surface.commit.
+
+
+
+
+
+
+
+
+
+ Set the destination size of the associated wl_surface. See
+ wp_viewport for the description, and relation to the wl_buffer
+ size.
+
+ If width is -1 and height is -1, the destination size is unset
+ instead. Any other pair of values for width and height that
+ contains zero or negative values raises the bad_value protocol
+ error.
+
+ The crop and scale state is double-buffered state, and will be
+ applied on the next wl_surface.commit.
+
+
+
+
+
+
+
diff --git a/lib/renderers/wayland/wayland.h b/lib/renderers/wayland/wayland.h
index ab498f7..242a4d1 100644
--- a/lib/renderers/wayland/wayland.h
+++ b/lib/renderers/wayland/wayland.h
@@ -8,6 +8,8 @@
#include
#include "wlr-layer-shell-unstable-v1.h"
+#include "fractional-scale-v1.h"
+#include "viewporter.h"
#include "renderers/cairo_renderer.h"
struct bm_menu;
@@ -119,12 +121,13 @@ struct window {
struct wl_surface *surface;
struct wl_callback *frame_cb;
struct zwlr_layer_surface_v1 *layer_surface;
+ struct wp_viewport *viewport_surface;
struct wl_shm *shm;
struct buffer buffers[2];
uint32_t width, height, max_height;
uint32_t hmargin_size;
float width_factor;
- int32_t scale;
+ double scale;
uint32_t displayed;
struct wl_list link;
enum bm_align align;
@@ -167,6 +170,9 @@ struct wayland {
struct input input;
struct wl_list windows;
uint32_t formats;
+ struct wp_fractional_scale_manager_v1 *wfs_mgr;
+ struct wp_viewporter *viewporter;
+ bool fractional_scaling;
};
void bm_wl_repeat(struct wayland *wayland);
diff --git a/lib/renderers/wayland/window.c b/lib/renderers/wayland/window.c
index f7f4bc3..0043c2b 100644
--- a/lib/renderers/wayland/window.c
+++ b/lib/renderers/wayland/window.c
@@ -6,6 +6,7 @@
#include
#include
#include
+#include
static int
set_cloexec_or_close(int fd)
@@ -107,7 +108,7 @@ destroy_buffer(struct buffer *buffer)
}
static bool
-create_buffer(struct wl_shm *shm, struct buffer *buffer, int32_t width, int32_t height, uint32_t format, int32_t scale)
+create_buffer(struct wl_shm *shm, struct buffer *buffer, int32_t width, int32_t height, uint32_t format, double scale)
{
int fd = -1;
struct wl_shm_pool *pool = NULL;
@@ -188,10 +189,10 @@ next_buffer(struct window *window)
if (!buffer)
return NULL;
- if (window->width * window->scale != buffer->width || window->height * window->scale != buffer->height)
+ if ((uint32_t) ceil(window->width * window->scale) != buffer->width || (uint32_t) ceil(window->height * window->scale) != buffer->height)
destroy_buffer(buffer);
- if (!buffer->buffer && !create_buffer(window->shm, buffer, window->width * window->scale, window->height * window->scale, WL_SHM_FORMAT_ARGB8888, window->scale))
+ if (!buffer->buffer && !create_buffer(window->shm, buffer, ceil(window->width * window->scale), ceil(window->height * window->scale), WL_SHM_FORMAT_ARGB8888, window->scale))
return NULL;
return buffer;
@@ -258,17 +259,24 @@ bm_wl_window_render(struct window *window, struct wl_display *display, struct bm
window->notify.render(&buffer->cairo, buffer->width, window->max_height, menu, &result);
window->displayed = result.displayed;
- if (window->height == result.height / window->scale)
+ if (window->height == (uint32_t) ceil(result.height / window->scale))
break;
- window->height = result.height / window->scale;
+ window->height = ceil(result.height / window->scale);
zwlr_layer_surface_v1_set_size(window->layer_surface, window->width, window->height);
destroy_buffer(buffer);
}
- assert(window->width * window->scale == buffer->width);
- assert(window->height * window->scale == buffer->height);
- wl_surface_set_buffer_scale(window->surface, window->scale);
+ assert(ceil(window->width * window->scale) == buffer->width);
+ assert(ceil(window->height * window->scale) == buffer->height);
+
+ if (window->wayland->fractional_scaling) {
+ assert(window->viewport_surface);
+ wp_viewport_set_destination(window->viewport_surface, window->width, window->height);
+ } else {
+ wl_surface_set_buffer_scale(window->surface, window->scale);
+ }
+
wl_surface_damage_buffer(window->surface, 0, 0, buffer->width, buffer->height);
wl_surface_attach(window->surface, buffer->buffer, 0, 0);
wl_surface_commit(window->surface);
@@ -323,6 +331,21 @@ get_window_width(struct window *window)
return width;
}
+static void
+fractional_scale_preferred_scale(
+ void *data, struct wp_fractional_scale_v1 *wp_fractional_scale_v1,
+ uint32_t scale)
+{
+ (void)wp_fractional_scale_v1;
+ struct window *window = data;
+
+ window->scale = (double)scale / 120;
+}
+
+static const struct wp_fractional_scale_v1_listener fractional_scale_listener = {
+ .preferred_scale = fractional_scale_preferred_scale,
+};
+
static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
.configure = layer_surface_configure,
.closed = layer_surface_closed,
@@ -393,6 +416,17 @@ bm_wl_window_create(struct window *window, struct wl_display *display, struct wl
{
assert(window);
+ struct wayland *wayland = window->wayland;
+
+ if (wayland->fractional_scaling) {
+ assert(wayland->wfs_mgr && wayland->viewporter);
+
+ struct wp_fractional_scale_v1 *wfs_surf = wp_fractional_scale_manager_v1_get_fractional_scale(wayland->wfs_mgr, surface);
+ wp_fractional_scale_v1_add_listener(
+ wfs_surf, &fractional_scale_listener, window);
+ window->viewport_surface = wp_viewporter_get_viewport(wayland->viewporter, surface);
+ }
+
enum zwlr_layer_shell_v1_layer layer = ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY;
if (layer_shell && (window->layer_surface = zwlr_layer_shell_v1_get_layer_surface(layer_shell, surface, output, layer, "menu"))) {
zwlr_layer_surface_v1_add_listener(window->layer_surface, &layer_surface_listener, window);