diff --git a/hw/xwayland/xwayland-cursor.c b/hw/xwayland/xwayland-cursor.c index e3c1aaa50..eba29b5ba 100644 --- a/hw/xwayland/xwayland-cursor.c +++ b/hw/xwayland/xwayland-cursor.c @@ -164,6 +164,8 @@ xwl_cursor_attach_pixmap(struct xwl_seat *xwl_seat, } wl_surface_attach(xwl_cursor->surface, buffer, 0, 0); + wl_surface_set_buffer_scale(xwl_cursor->surface, + xwl_seat->xwl_screen->global_output_scale); xwl_surface_damage(xwl_seat->xwl_screen, xwl_cursor->surface, 0, 0, xwl_seat->x_cursor->bits->width, xwl_seat->x_cursor->bits->height); @@ -195,6 +197,7 @@ xwl_cursor_clear_frame_cb(struct xwl_cursor *xwl_cursor) void xwl_seat_set_cursor(struct xwl_seat *xwl_seat) { + struct xwl_screen *xwl_screen = xwl_seat->xwl_screen; struct xwl_cursor *xwl_cursor = &xwl_seat->cursor; PixmapPtr pixmap; CursorPtr cursor; @@ -225,8 +228,8 @@ xwl_seat_set_cursor(struct xwl_seat *xwl_seat) wl_pointer_set_cursor(xwl_seat->wl_pointer, xwl_seat->pointer_enter_serial, xwl_cursor->surface, - xwl_seat->x_cursor->bits->xhot, - xwl_seat->x_cursor->bits->yhot); + xwl_scale_to(xwl_screen, xwl_seat->x_cursor->bits->xhot), + xwl_scale_to(xwl_screen, xwl_seat->x_cursor->bits->yhot)); xwl_cursor_attach_pixmap(xwl_seat, xwl_cursor, pixmap); } @@ -235,6 +238,7 @@ void xwl_tablet_tool_set_cursor(struct xwl_tablet_tool *xwl_tablet_tool) { struct xwl_seat *xwl_seat = xwl_tablet_tool->seat; + struct xwl_screen *xwl_screen = xwl_seat->xwl_screen; struct xwl_cursor *xwl_cursor = &xwl_tablet_tool->cursor; PixmapPtr pixmap; CursorPtr cursor; @@ -263,8 +267,9 @@ xwl_tablet_tool_set_cursor(struct xwl_tablet_tool *xwl_tablet_tool) zwp_tablet_tool_v2_set_cursor(xwl_tablet_tool->tool, xwl_tablet_tool->proximity_in_serial, xwl_cursor->surface, - xwl_seat->x_cursor->bits->xhot, - xwl_seat->x_cursor->bits->yhot); + xwl_scale_to(xwl_screen, xwl_seat->x_cursor->bits->xhot), + xwl_scale_to(xwl_screen, xwl_seat->x_cursor->bits->yhot)); + wl_surface_set_buffer_scale(xwl_cursor->surface, xwl_screen->global_output_scale); xwl_cursor_attach_pixmap(xwl_seat, xwl_cursor, pixmap); } diff --git a/hw/xwayland/xwayland-input.c b/hw/xwayland/xwayland-input.c index 6e0600e4e..4a22ebff0 100644 --- a/hw/xwayland/xwayland-input.c +++ b/hw/xwayland/xwayland-input.c @@ -507,8 +507,8 @@ pointer_handle_enter(void *data, struct wl_pointer *pointer, DeviceIntPtr dev = get_pointer_device(xwl_seat); DeviceIntPtr master; int i; - int sx = wl_fixed_to_int(sx_w); - int sy = wl_fixed_to_int(sy_w); + int sx = wl_fixed_to_int(sx_w) * xwl_seat->xwl_screen->global_output_scale; + int sy = wl_fixed_to_int(sy_w) * xwl_seat->xwl_screen->global_output_scale; int dx, dy; ScreenPtr pScreen = xwl_seat->xwl_screen->screen; ValuatorMask mask; @@ -731,13 +731,14 @@ pointer_handle_motion(void *data, struct wl_pointer *pointer, uint32_t time, wl_fixed_t sx_w, wl_fixed_t sy_w) { struct xwl_seat *xwl_seat = data; + int32_t scale = xwl_seat->xwl_screen->global_output_scale; if (!xwl_seat->focus_window) return; xwl_seat->pending_pointer_event.has_absolute = TRUE; - xwl_seat->pending_pointer_event.x = sx_w; - xwl_seat->pending_pointer_event.y = sy_w; + xwl_seat->pending_pointer_event.x = sx_w * scale; + xwl_seat->pending_pointer_event.y = sy_w * scale; if (wl_proxy_get_version((struct wl_proxy *) xwl_seat->wl_pointer) < 5) dispatch_pointer_motion_event(xwl_seat); @@ -887,12 +888,13 @@ relative_pointer_handle_relative_motion(void *data, wl_fixed_t dy_unaccelf) { struct xwl_seat *xwl_seat = data; + int32_t scale = xwl_seat->xwl_screen->global_output_scale; xwl_seat->pending_pointer_event.has_relative = TRUE; - xwl_seat->pending_pointer_event.dx = wl_fixed_to_double(dxf); - xwl_seat->pending_pointer_event.dy = wl_fixed_to_double(dyf); - xwl_seat->pending_pointer_event.dx_unaccel = wl_fixed_to_double(dx_unaccelf); - xwl_seat->pending_pointer_event.dy_unaccel = wl_fixed_to_double(dy_unaccelf); + xwl_seat->pending_pointer_event.dx = wl_fixed_to_double(dxf) * scale; + xwl_seat->pending_pointer_event.dy = wl_fixed_to_double(dyf) * scale; + xwl_seat->pending_pointer_event.dx_unaccel = wl_fixed_to_double(dx_unaccelf) * scale; + xwl_seat->pending_pointer_event.dy_unaccel = wl_fixed_to_double(dy_unaccelf) * scale; if (!xwl_seat->focus_window) return; @@ -1382,8 +1384,8 @@ touch_handle_down(void *data, struct wl_touch *wl_touch, xwl_touch->window = wl_surface_get_user_data(surface); xwl_touch->id = id; - xwl_touch->x = wl_fixed_to_int(sx_w); - xwl_touch->y = wl_fixed_to_int(sy_w); + xwl_touch->x = wl_fixed_to_int(sx_w) * xwl_seat->xwl_screen->global_output_scale; + xwl_touch->y = wl_fixed_to_int(sy_w) * xwl_seat->xwl_screen->global_output_scale; xorg_list_add(&xwl_touch->link_touch, &xwl_seat->touches); xwl_touch_send_event(xwl_touch, xwl_seat, XI_TouchBegin); @@ -1419,8 +1421,8 @@ touch_handle_motion(void *data, struct wl_touch *wl_touch, if (!xwl_touch) return; - xwl_touch->x = wl_fixed_to_int(sx_w); - xwl_touch->y = wl_fixed_to_int(sy_w); + xwl_touch->x = wl_fixed_to_int(sx_w) * xwl_seat->xwl_screen->global_output_scale; + xwl_touch->y = wl_fixed_to_int(sy_w) * xwl_seat->xwl_screen->global_output_scale; xwl_touch_send_event(xwl_touch, xwl_seat, XI_TouchUpdate); } @@ -2110,8 +2112,8 @@ tablet_tool_motion(void *data, struct zwp_tablet_tool_v2 *tool, struct xwl_tablet_tool *xwl_tablet_tool = data; struct xwl_seat *xwl_seat = xwl_tablet_tool->seat; int32_t dx, dy; - double sx = wl_fixed_to_double(x); - double sy = wl_fixed_to_double(y); + double sx = wl_fixed_to_double(x) * xwl_seat->xwl_screen->global_output_scale; + double sy = wl_fixed_to_double(y) * xwl_seat->xwl_screen->global_output_scale; if (!xwl_seat->tablet_focus_window) return; @@ -3152,6 +3154,7 @@ xwl_pointer_warp_emulator_set_fake_pos(struct xwl_pointer_warp_emulator *warp_em int x, int y) { + struct xwl_screen *xwl_screen; struct zwp_locked_pointer_v1 *locked_pointer = warp_emulator->locked_pointer; WindowPtr window; @@ -3163,6 +3166,7 @@ xwl_pointer_warp_emulator_set_fake_pos(struct xwl_pointer_warp_emulator *warp_em if (!warp_emulator->xwl_seat->focus_window) return; + xwl_screen = warp_emulator->xwl_seat->xwl_screen; window = warp_emulator->xwl_seat->focus_window->window; if (x >= window->drawable.x || y >= window->drawable.y || @@ -3171,8 +3175,8 @@ xwl_pointer_warp_emulator_set_fake_pos(struct xwl_pointer_warp_emulator *warp_em sx = x - window->drawable.x; sy = y - window->drawable.y; zwp_locked_pointer_v1_set_cursor_position_hint(locked_pointer, - wl_fixed_from_int(sx), - wl_fixed_from_int(sy)); + wl_fixed_from_int(xwl_scale_to(xwl_screen, sx)), + wl_fixed_from_int(xwl_scale_to(xwl_screen, sy))); wl_surface_commit(warp_emulator->xwl_seat->focus_window->surface); } } diff --git a/hw/xwayland/xwayland-output.c b/hw/xwayland/xwayland-output.c index 661e1828d..6c60aba34 100644 --- a/hw/xwayland/xwayland-output.c +++ b/hw/xwayland/xwayland-output.c @@ -186,6 +186,9 @@ update_backing_pixmaps(struct xwl_screen *xwl_screen, int width, int height) static void update_screen_size(struct xwl_screen *xwl_screen, int width, int height) { + width *= xwl_screen->global_output_scale; + height *= xwl_screen->global_output_scale; + xwl_screen->width = width; xwl_screen->height = height; @@ -597,14 +600,15 @@ xwl_output_set_emulated_mode(struct xwl_output *xwl_output, ClientPtr client, new_emulated_height); } -static void -apply_output_change(struct xwl_output *xwl_output) +void +xwl_output_apply_changes(struct xwl_output *xwl_output) { struct xwl_screen *xwl_screen = xwl_output->xwl_screen; struct xwl_output *it; int mode_width, mode_height, count; int width = 0, height = 0, has_this_output = 0; RRModePtr *randr_modes; + int32_t scale = xwl_screen->global_output_scale; /* Clear out the "done" received flags */ xwl_output->wl_output_done = FALSE; @@ -623,10 +627,10 @@ apply_output_change(struct xwl_output *xwl_output) } if (xwl_output->randr_output) { /* Build a fresh modes array using the current refresh rate */ - randr_modes = output_get_rr_modes(xwl_output, mode_width, mode_height, &count); + randr_modes = output_get_rr_modes(xwl_output, mode_width * scale, mode_height * scale, &count); RROutputSetModes(xwl_output->randr_output, randr_modes, count, 1); RRCrtcNotify(xwl_output->randr_crtc, randr_modes[0], - xwl_output->x, xwl_output->y, + xwl_output->x * scale, xwl_output->y * scale, xwl_output->rotation, NULL, 1, &xwl_output->randr_output); /* RROutputSetModes takes ownership of the passed in modes, so we only * have to free the pointer array. @@ -686,7 +690,7 @@ output_handle_done(void *data, struct wl_output *wl_output) */ if (xwl_output->xdg_output_done || !xwl_output->xdg_output || zxdg_output_v1_get_version(xwl_output->xdg_output) >= 3) - apply_output_change(xwl_output); + xwl_output_apply_changes(xwl_output); } static void @@ -746,7 +750,7 @@ xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) xwl_output->xdg_output_done = TRUE; if (xwl_output->wl_output_done && zxdg_output_v1_get_version(xdg_output) < 3) - apply_output_change(xwl_output); + xwl_output_apply_changes(xwl_output); } static void @@ -857,6 +861,8 @@ xwl_output_create(struct xwl_screen *xwl_screen, uint32_t id, RRCrtcGammaSetSize(xwl_output->randr_crtc, 256); RROutputSetCrtcs(xwl_output->randr_output, &xwl_output->randr_crtc, 1); RROutputSetConnection(xwl_output->randr_output, RR_Connected); + + xwl_output->scale = 1; } /* We want the output to be in the list as soon as created so we can * use it when binding to the xdg-output protocol... diff --git a/hw/xwayland/xwayland-output.h b/hw/xwayland/xwayland-output.h index a95288e4f..46d1ead2a 100644 --- a/hw/xwayland/xwayland-output.h +++ b/hw/xwayland/xwayland-output.h @@ -53,7 +53,7 @@ struct xwl_output { struct wl_output *output; struct zxdg_output_v1 *xdg_output; uint32_t server_output_id; - int32_t x, y, width, height, refresh; + int32_t x, y, width, height, refresh, scale; Rotation rotation; Bool wl_output_done; Bool xdg_output_done; @@ -102,6 +102,8 @@ void xwl_output_set_emulated_mode(struct xwl_output *xwl_output, void xwl_output_set_window_randr_emu_props(struct xwl_screen *xwl_screen, WindowPtr window); +void xwl_output_apply_changes(struct xwl_output *xwl_output); + void xwl_screen_init_xdg_output(struct xwl_screen *xwl_screen); #endif /* XWAYLAND_OUTPUT_H */ diff --git a/hw/xwayland/xwayland-present.c b/hw/xwayland/xwayland-present.c index 189e7cfd6..555434031 100644 --- a/hw/xwayland/xwayland-present.c +++ b/hw/xwayland/xwayland-present.c @@ -764,6 +764,8 @@ xwl_present_flip(WindowPtr present_window, /* We can flip directly to the main surface (full screen window without clips) */ wl_surface_attach(xwl_window->surface, buffer, 0, 0); + wl_surface_set_buffer_scale(xwl_window->surface, + xwl_window->xwl_screen->global_output_scale); if (xorg_list_is_empty(&xwl_present_window->frame_callback_list)) { xorg_list_add(&xwl_present_window->frame_callback_list, diff --git a/hw/xwayland/xwayland-screen.c b/hw/xwayland/xwayland-screen.c index 46ab4fed7..b2d7022e6 100644 --- a/hw/xwayland/xwayland-screen.c +++ b/hw/xwayland/xwayland-screen.c @@ -51,6 +51,7 @@ #include "xwayland-pixmap.h" #include "xwayland-present.h" #include "xwayland-shm.h" +#include "xwayland-window-buffers.h" #ifdef MITSHM #include "shmint.h" @@ -111,6 +112,12 @@ xwl_screen_has_resolution_change_emulation(struct xwl_screen *xwl_screen) return xwl_screen->rootless && xwl_screen_has_viewport_support(xwl_screen); } +int +xwl_scale_to(struct xwl_screen *xwl_screen, int value) +{ + return value / (double)xwl_screen->global_output_scale + 0.5; +} + /* Return the output @ 0x0, falling back to the first output in the list */ struct xwl_output * xwl_screen_get_first_output(struct xwl_screen *xwl_screen) @@ -128,6 +135,38 @@ xwl_screen_get_first_output(struct xwl_screen *xwl_screen) return xorg_list_first_entry(&xwl_screen->output_list, struct xwl_output, link); } +static void +xwl_screen_set_global_scale_from_property(struct xwl_screen *screen, + PropertyPtr prop) +{ + CARD32 *propdata; + + if (prop->type != XA_CARDINAL || prop->format != 32 || prop->size != 1) { + // TODO: handle warnings more cleanly. + LogMessageVerb(X_WARNING, 0, "Bad value for property %s.\n", + NameForAtom(prop->propertyName)); + return; + } + + propdata = prop->data; + xwl_screen_set_global_scale(screen, propdata[0]); +} + +static void +xwl_screen_update_property(struct xwl_screen *screen, + PropertyStateRec *propstate) +{ + switch (propstate->state) { + case PropertyNewValue: + xwl_screen_set_global_scale_from_property(screen, propstate->prop); + break; + case PropertyDelete: + xwl_screen_set_global_scale(screen, 1); + break; + } +} + + struct xwl_output * xwl_screen_get_fixed_or_first_output(struct xwl_screen *xwl_screen) { @@ -144,19 +183,24 @@ xwl_property_callback(CallbackListPtr *pcbl, void *closure, ScreenPtr screen = closure; PropertyStateRec *rec = calldata; struct xwl_screen *xwl_screen; - struct xwl_window *xwl_window; if (rec->win->drawable.pScreen != screen) return; - xwl_window = xwl_window_get(rec->win); - if (!xwl_window) - return; - xwl_screen = xwl_screen_get(screen); - if (rec->prop->propertyName == xwl_screen->allow_commits_prop) - xwl_window_update_property(xwl_window, rec); + if (rec->prop->propertyName == xwl_screen->allow_commits_prop) { + struct xwl_window *xwl_window; + + xwl_window = xwl_window_get(rec->win); + if (!xwl_window) + return; + + xwl_window_update_property(xwl_window, rec); + } + else if (rec->prop->propertyName == xwl_screen->global_output_scale_prop) { + xwl_screen_update_property(xwl_screen, rec); + } } static void @@ -638,8 +682,14 @@ void xwl_surface_damage(struct xwl_screen *xwl_screen, { if (wl_surface_get_version(surface) >= WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION) wl_surface_damage_buffer(surface, x, y, width, height); - else + else { + x = xwl_scale_to(xwl_screen, x); + y = xwl_scale_to(xwl_screen, y); + width = xwl_scale_to(xwl_screen, width); + height = xwl_scale_to(xwl_screen, height); + wl_surface_damage(surface, x, y, width, height); + } } void @@ -655,6 +705,30 @@ xwl_screen_roundtrip(struct xwl_screen *xwl_screen) xwl_give_up("could not connect to wayland server\n"); } +void +xwl_screen_set_global_scale(struct xwl_screen *xwl_screen, int32_t scale) +{ + struct xwl_output *it; + struct xwl_window *xwl_window; + + xwl_screen->global_output_scale = scale; + + /* change randr resolutions and positions */ + xorg_list_for_each_entry(it, &xwl_screen->output_list, link) { + xwl_output_apply_changes(it); + } + + if (!xwl_screen->rootless && xwl_screen->screen->root) { + /* Clear all the buffers, so that they'll be remade with the new sizes + * (this doesn't occur automatically because as far as Xorg is + * concerned, the window's size is the same) */ + xorg_list_for_each_entry(xwl_window, &xwl_screen->window_list, link_window) { + xwl_window_buffers_recycle(xwl_window); + } + } +} + + static int xwl_server_grab(ClientPtr client) { @@ -712,6 +786,7 @@ Bool xwl_screen_init(ScreenPtr pScreen, int argc, char **argv) { static const char allow_commits[] = "_XWAYLAND_ALLOW_COMMITS"; + static const char global_output_scale[] = "_XWAYLAND_GLOBAL_OUTPUT_SCALE"; struct xwl_screen *xwl_screen; Pixel red_mask, blue_mask, green_mask; int ret, bpc, green_bpc, i; @@ -746,6 +821,7 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv) #ifdef XWL_HAS_GLAMOR xwl_screen->glamor = 1; #endif + xwl_screen->global_output_scale = 1; for (i = 1; i < argc; i++) { if (strcmp(argv[i], "-rootless") == 0) { @@ -988,6 +1064,13 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv) if (xwl_screen->allow_commits_prop == BAD_RESOURCE) return FALSE; + xwl_screen->global_output_scale_prop = MakeAtom(global_output_scale, + strlen(global_output_scale), + TRUE); + if (xwl_screen->global_output_scale_prop == BAD_RESOURCE) + return FALSE; + + AddCallback(&PropertyStateCallback, xwl_property_callback, pScreen); AddCallback(&RootWindowFinalizeCallback, xwl_root_window_finalized_callback, pScreen); diff --git a/hw/xwayland/xwayland-screen.h b/hw/xwayland/xwayland-screen.h index fadd0526e..2ce6ce5ab 100644 --- a/hw/xwayland/xwayland-screen.h +++ b/hw/xwayland/xwayland-screen.h @@ -87,6 +87,7 @@ struct xwl_screen { struct xorg_list damage_window_list; struct xorg_list window_list; + int32_t global_output_scale; int wayland_fd; struct wl_display *display; struct wl_registry *registry; @@ -134,6 +135,7 @@ struct xwl_screen { struct glamor_context *glamor_ctx; Atom allow_commits_prop; + Atom global_output_scale_prop; /* The preferred GLVND vendor. If NULL, "mesa" is assumed. */ const char *glvnd_vendor; @@ -166,6 +168,8 @@ void xwl_screen_roundtrip (struct xwl_screen *xwl_screen); void xwl_surface_damage(struct xwl_screen *xwl_screen, struct wl_surface *surface, int32_t x, int32_t y, int32_t width, int32_t height); +int xwl_scale_to(struct xwl_screen *xwl_screen, int value); +void xwl_screen_set_global_scale(struct xwl_screen *xwl_screen, int32_t scale); int xwl_screen_get_next_output_serial(struct xwl_screen * xwl_screen); #endif /* XWAYLAND_SCREEN_H */ diff --git a/hw/xwayland/xwayland-window.c b/hw/xwayland/xwayland-window.c index 6b7f38605..2f1e0dee1 100644 --- a/hw/xwayland/xwayland-window.c +++ b/hw/xwayland/xwayland-window.c @@ -788,7 +788,8 @@ xwl_create_root_surface(struct xwl_window *xwl_window) } wl_region_add(region, 0, 0, - window->drawable.width, window->drawable.height); + xwl_scale_to(xwl_screen, window->drawable.width), + xwl_scale_to(xwl_screen, window->drawable.height)); wl_surface_set_opaque_region(xwl_window->surface, region); wl_region_destroy(region); @@ -1322,6 +1323,7 @@ xwl_window_post_damage(struct xwl_window *xwl_window) #endif wl_surface_attach(xwl_window->surface, buffer, 0, 0); + wl_surface_set_buffer_scale(xwl_window->surface, xwl_screen->global_output_scale); /* Arbitrary limit to try to avoid flooding the Wayland * connection. If we flood it too much anyway, this could