diff --git a/include/wlr/xwayland.h b/include/wlr/xwayland.h index 8646e7acb6..d1b705f62d 100644 --- a/include/wlr/xwayland.h +++ b/include/wlr/xwayland.h @@ -30,6 +30,8 @@ struct wlr_xwayland { time_t server_start; + int32_t scale; + /* Anything above display is reset on Xwayland restart, rest is conserved */ int display; @@ -205,6 +207,8 @@ struct wlr_xwayland *wlr_xwayland_create(struct wl_display *wl_display, void wlr_xwayland_destroy(struct wlr_xwayland *wlr_xwayland); +void wlr_xwayland_set_scale(struct wlr_xwayland *wlr_xwayland, int32_t scale); + void wlr_xwayland_set_cursor(struct wlr_xwayland *wlr_xwayland, uint8_t *pixels, uint32_t stride, uint32_t width, uint32_t height, int32_t hotspot_x, int32_t hotspot_y); diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index 351fce4414..25db63c1ed 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -122,6 +122,7 @@ struct wlr_xwm { struct wlr_xwayland_surface *drag_focus; const xcb_query_extension_reply_t *xfixes; + const xcb_query_extension_reply_t *xwayland_ext; #if WLR_HAS_XCB_ERRORS xcb_errors_context_t *errors_context; #endif @@ -155,4 +156,6 @@ char *xwm_get_atom_name(struct wlr_xwm *xwm, xcb_atom_t atom); bool xwm_atoms_contains(struct wlr_xwm *xwm, xcb_atom_t *atoms, size_t num_atoms, enum atom_name needle); +void xwm_scale_changed(struct wlr_xwm *xwm); + #endif diff --git a/xwayland/xwayland.c b/xwayland/xwayland.c index 77698f4493..0620799021 100644 --- a/xwayland/xwayland.c +++ b/xwayland/xwayland.c @@ -409,6 +409,8 @@ struct wlr_xwayland *wlr_xwayland_create(struct wl_display *wl_display, wlr_xwayland->wl_fd[0] = wlr_xwayland->wl_fd[1] = -1; wlr_xwayland->wm_fd[0] = wlr_xwayland->wm_fd[1] = -1; + wlr_xwayland->scale = 1; + wl_signal_init(&wlr_xwayland->events.new_surface); wl_signal_init(&wlr_xwayland->events.ready); @@ -436,6 +438,13 @@ struct wlr_xwayland *wlr_xwayland_create(struct wl_display *wl_display, return NULL; } +void wlr_xwayland_set_scale(struct wlr_xwayland *wlr_xwayland, int32_t scale) { + wlr_xwayland->scale = scale; + if (wlr_xwayland->xwm != NULL) { + xwm_scale_changed(wlr_xwayland->xwm); + } +} + void wlr_xwayland_set_cursor(struct wlr_xwayland *wlr_xwayland, uint8_t *pixels, uint32_t stride, uint32_t width, uint32_t height, int32_t hotspot_x, int32_t hotspot_y) { diff --git a/xwayland/xwm.c b/xwayland/xwm.c index b19efbcf10..020460f1a5 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -13,9 +13,23 @@ #include #include #include +#include #include "util/signal.h" #include "xwayland/xwm.h" + +static int32_t scale(struct wlr_xwm *xwm, int32_t val) { + return val * xwm->xwayland->scale; +} + +static int32_t unscale(struct wlr_xwm *xwm, int32_t val) { + return (val + xwm->xwayland->scale/2) / xwm->xwayland->scale; +} + +static xcb_extension_t xwayland_ext_id = { + .name = "XWAYLAND", +}; + const char *atom_map[ATOM_LAST] = { [WL_SURFACE_ID] = "WL_SURFACE_ID", [WM_DELETE_WINDOW] = "WM_DELETE_WINDOW", @@ -834,8 +848,13 @@ static void xwm_handle_create_notify(struct wlr_xwm *xwm, return; } - xwayland_surface_create(xwm, ev->window, ev->x, ev->y, - ev->width, ev->height, ev->override_redirect); + xwayland_surface_create(xwm, ev->window, + unscale(xwm, ev->x), + unscale(xwm, ev->y), + unscale(xwm, ev->width), + unscale(xwm, ev->height), + ev->override_redirect + ); } static void xwm_handle_destroy_notify(struct wlr_xwm *xwm, @@ -866,10 +885,10 @@ static void xwm_handle_configure_request(struct wlr_xwm *xwm, struct wlr_xwayland_surface_configure_event wlr_event = { .surface = surface, - .x = mask & XCB_CONFIG_WINDOW_X ? ev->x : surface->x, - .y = mask & XCB_CONFIG_WINDOW_Y ? ev->y : surface->y, - .width = mask & XCB_CONFIG_WINDOW_WIDTH ? ev->width : surface->width, - .height = mask & XCB_CONFIG_WINDOW_HEIGHT ? ev->height : surface->height, + .x = unscale(xwm, mask & XCB_CONFIG_WINDOW_X ? ev->x : surface->x), + .y = unscale(xwm, mask & XCB_CONFIG_WINDOW_Y ? ev->y : surface->y), + .width = unscale(xwm, mask & XCB_CONFIG_WINDOW_WIDTH ? ev->width : surface->width), + .height = unscale(xwm, mask & XCB_CONFIG_WINDOW_HEIGHT ? ev->height : surface->height), .mask = mask, }; wlr_log(WLR_DEBUG, "XCB_CONFIGURE_REQUEST (%u) [%ux%u+%d,%d]", ev->window, @@ -885,10 +904,10 @@ static void xwm_handle_configure_notify(struct wlr_xwm *xwm, return; } - xsurface->x = ev->x; - xsurface->y = ev->y; - xsurface->width = ev->width; - xsurface->height = ev->height; + xsurface->x = unscale(xwm, ev->x); + xsurface->y = unscale(xwm, ev->y); + xsurface->width = unscale(xwm, ev->width); + xsurface->height = unscale(xwm, ev->height); if (xsurface->override_redirect != ev->override_redirect) { xsurface->override_redirect = ev->override_redirect; @@ -1431,7 +1450,13 @@ void wlr_xwayland_surface_configure(struct wlr_xwayland_surface *xsurface, uint32_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT | XCB_CONFIG_WINDOW_BORDER_WIDTH; - uint32_t values[] = {x, y, width, height, 0}; + uint32_t values[] = { + scale(xsurface->xwm, x), + scale(xsurface->xwm, y), + scale(xsurface->xwm, width), + scale(xsurface->xwm, height), + 0, + }; xcb_configure_window(xwm->xcb_conn, xsurface->window_id, mask, values); xcb_flush(xwm->xcb_conn); } @@ -1497,6 +1522,7 @@ void xwm_destroy(struct wlr_xwm *xwm) { static void xwm_get_resources(struct wlr_xwm *xwm) { xcb_prefetch_extension_data(xwm->xcb_conn, &xcb_xfixes_id); xcb_prefetch_extension_data(xwm->xcb_conn, &xcb_composite_id); + xcb_prefetch_extension_data(xwm->xcb_conn, &xwayland_ext_id); // TODO what if extension is not present?? size_t i; xcb_intern_atom_cookie_t cookies[ATOM_LAST]; @@ -1528,6 +1554,8 @@ static void xwm_get_resources(struct wlr_xwm *xwm) { wlr_log(WLR_DEBUG, "xfixes not available"); } + xwm->xwayland_ext = xcb_get_extension_data(xwm->xcb_conn, &xwayland_ext_id); + xcb_xfixes_query_version_cookie_t xfixes_cookie; xcb_xfixes_query_version_reply_t *xfixes_reply; xfixes_cookie = @@ -1858,3 +1886,39 @@ bool wlr_xwayland_or_surface_wants_focus( return ret; } + + +typedef struct { + uint8_t major_opcode; + uint8_t minor_opcode; + uint16_t length; + uint16_t screen; + uint16_t scale; +} xwayland_ext_set_scale_request_t; + +void xwm_scale_changed(struct wlr_xwm *xwm) { + xcb_protocol_request_t req = { + .count = 1, + .ext = &xwayland_ext_id, + .opcode = 1, + .isvoid = false, + }; + + xwayland_ext_set_scale_request_t xcb_out = { + .screen = 0, + .scale = xwm->xwayland->scale, + }; + + struct iovec xcb_parts[3]; + xcb_parts[2].iov_base = (char *) &xcb_out; + xcb_parts[2].iov_len = sizeof(xcb_out); + xcb_send_request(xwm->xcb_conn, 0, xcb_parts+2, &req); + + // Reconfigure all surfaces with the new scale. + struct wlr_xwayland_surface *surface; + wl_list_for_each(surface, &xwm->surfaces, link) { + wlr_xwayland_surface_configure(surface, surface->x, surface->y, surface->width, surface->height); + } + + xcb_flush(xwm->xcb_conn); +}