diff --git a/.github/workflows/nix-build.yaml b/.github/workflows/nix-build.yaml index bf388ed1..51699280 100644 --- a/.github/workflows/nix-build.yaml +++ b/.github/workflows/nix-build.yaml @@ -5,6 +5,11 @@ jobs: nix: name: "Build Hyprland (Nix)" runs-on: ubuntu-latest + strategy: + matrix: + package: + - default + - hyprland-no-hidpi steps: - name: Clone repository uses: actions/checkout@v3 @@ -22,4 +27,4 @@ jobs: name: hyprland authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - name: Build Hyprland with default settings - run: nix build --print-build-logs + run: nix build .#${{ matrix.package }} --print-build-logs diff --git a/flake.nix b/flake.nix index 5232f434..6aca67ee 100644 --- a/flake.nix +++ b/flake.nix @@ -32,12 +32,15 @@ version = mkDate (inputs.wlroots.lastModifiedDate or "19700101"); src = inputs.wlroots; }); + hyprland = prev.callPackage ./nix/default.nix { stdenv = prev.gcc12Stdenv; version = "0.11.1beta" + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty"); wlroots = wlroots-hyprland; }; hyprland-debug = hyprland.override {debug = true;}; + hyprland-no-hidpi = hyprland.override {hidpiXWayland = false;}; + waybar-hyprland = prev.waybar.overrideAttrs (oldAttrs: { mesonFlags = oldAttrs.mesonFlags ++ ["-Dexperimental=true"]; }); diff --git a/nix/default.nix b/nix/default.nix index 3bec375d..f840da1a 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -2,6 +2,7 @@ lib, stdenv, fetchFromGitHub, + fetchpatch, pkg-config, meson, ninja, @@ -21,78 +22,88 @@ xwayland, debug ? false, enableXWayland ? true, + hidpiXWayland ? true, legacyRenderer ? false, version ? "git", -}: -stdenv.mkDerivation { - pname = "hyprland" + lib.optionalString debug "-debug"; - inherit version; - - src = lib.cleanSourceWith { - filter = name: type: let - baseName = baseNameOf (toString name); - in - ! ( - lib.hasSuffix ".nix" baseName - ); - src = lib.cleanSource ../.; - }; - - nativeBuildInputs = [ - meson - ninja - pkg-config - ]; - - outputs = [ - "out" - "man" - ]; - - buildInputs = - [ - git - libdrm - libinput - libxcb - libxkbcommon - mesa - pango - wayland - wayland-protocols - wayland-scanner - (wlroots.override {inherit enableXWayland;}) - xcbutilwm - ] - ++ lib.optional enableXWayland xwayland; - - mesonBuildType = - if debug - then "debug" - else "release"; - - mesonFlags = builtins.concatLists [ - (lib.optional (!enableXWayland) "-Dxwayland=disabled") - (lib.optional legacyRenderer "-DLEGACY_RENDERER:STRING=true") - ]; - - patches = [ - # make meson use the provided wlroots instead of the git submodule - ./meson-build.patch - ]; - - # Fix hardcoded paths to /usr installation - postPatch = '' - sed -i "s#/usr#$out#" src/render/OpenGL.cpp +}: let + assertXWayland = lib.assertMsg (hidpiXWayland -> enableXWayland) '' + Hyprland: cannot have hidpiXWayland when enableXWayland is false. ''; +in + assert assertXWayland; + stdenv.mkDerivation { + pname = "hyprland" + lib.optionalString debug "-debug"; + inherit version; - passthru.providedSessions = ["hyprland"]; + src = lib.cleanSourceWith { + filter = name: type: let + baseName = baseNameOf (toString name); + in + ! ( + lib.hasSuffix ".nix" baseName + ); + src = lib.cleanSource ../.; + }; - meta = with lib; { - homepage = "https://github.com/vaxerski/Hyprland"; - description = "A dynamic tiling Wayland compositor that doesn't sacrifice on its looks"; - license = licenses.bsd3; - platforms = platforms.linux; - mainProgram = "Hyprland"; - }; -} + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + outputs = [ + "out" + "man" + ]; + + buildInputs = + [ + git + libdrm + libinput + libxcb + libxkbcommon + mesa + pango + wayland + wayland-protocols + wayland-scanner + ( + if hidpiXWayland + then (import ./hidpi_wlroots.nix {inherit wlroots xwayland fetchpatch;}) + else wlroots.override {inherit enableXWayland;} + ) + xcbutilwm + ] + ++ lib.optional enableXWayland xwayland; + + mesonBuildType = + if debug + then "debug" + else "release"; + + mesonFlags = builtins.concatLists [ + (lib.optional (!enableXWayland) "-Dxwayland=disabled") + (lib.optional legacyRenderer "-DLEGACY_RENDERER:STRING=true") + ]; + + patches = [ + # make meson use the provided wlroots instead of the git submodule + ./meson-build.patch + ]; + + # Fix hardcoded paths to /usr installation + postPatch = '' + sed -i "s#/usr#$out#" src/render/OpenGL.cpp + ''; + + passthru.providedSessions = ["hyprland"]; + + meta = with lib; { + homepage = "https://github.com/vaxerski/Hyprland"; + description = "A dynamic tiling Wayland compositor that doesn't sacrifice on its looks"; + license = licenses.bsd3; + platforms = platforms.linux; + mainProgram = "Hyprland"; + }; + } diff --git a/nix/hidpi_wlroots.nix b/nix/hidpi_wlroots.nix new file mode 100644 index 00000000..c0f7db55 --- /dev/null +++ b/nix/hidpi_wlroots.nix @@ -0,0 +1,31 @@ +{ + wlroots, + xwayland, + fetchpatch, +}: +(wlroots.overrideAttrs + (old: { + patches = + (old.patches or []) + ++ [ + (fetchpatch { + url = "https://gitlab.freedesktop.org/lilydjwg/wlroots/-/commit/6c5ffcd1fee9e44780a6a8792f74ecfbe24a1ca7.diff"; + sha256 = "sha256-Eo1pTa/PIiJsRZwIUnHGTIFFIedzODVf0ZeuXb0a3TQ="; + }) + (fetchpatch { + url = "https://gitlab.freedesktop.org/wlroots/wlroots/-/commit/18595000f3a21502fd60bf213122859cc348f9af.diff"; + sha256 = "sha256-jvfkAMh3gzkfuoRhB4E9T5X1Hu62wgUjj4tZkJm0mrI="; + revert = true; + }) + ]; + })) +.override { + xwayland = xwayland.overrideAttrs (old: { + patches = + (old.patches or []) + ++ [ + ./xwayland-vsync.patch + ./xwayland-hidpi.patch + ]; + }); +} diff --git a/nix/hm-module.nix b/nix/hm-module.nix index 04641c2b..44316412 100644 --- a/nix/hm-module.nix +++ b/nix/hm-module.nix @@ -6,7 +6,8 @@ self: { }: let cfg = config.wayland.windowManager.hyprland; defaultHyprlandPackage = self.packages.${pkgs.system}.default.override { - enableXWayland = cfg.xwayland; + enableXWayland = cfg.xwayland.enable; + hidpiXWayland = cfg.xwayland.hidpi; }; in { options.wayland.windowManager.hyprland = { @@ -41,12 +42,21 @@ in { ''; }; - xwayland = lib.mkOption { - type = lib.types.bool; - default = true; - description = '' - Enable xwayland. - ''; + xwayland = { + enable = lib.mkOption { + type = lib.types.bool; + default = true; + description = '' + Enable XWayland. + ''; + }; + hidpi = lib.mkOption { + type = lib.types.bool; + default = true; + description = '' + Enable HiDPI XWayland. + ''; + }; }; extraConfig = lib.mkOption { @@ -56,20 +66,28 @@ in { Extra configuration lines to add to ~/.config/hypr/hyprland.conf. ''; }; + + imports = [ + ( + lib.mkRenamedOptionModule + ["wayland" "windowManager" "hyprland" "xwayland"] + ["wayland" "windowManager" "hyprland" "xwayland" "enable"] + ) + ]; }; config = lib.mkIf cfg.enable { home.packages = lib.optional (cfg.package != null) cfg.package - ++ lib.optional cfg.xwayland pkgs.xwayland; + ++ lib.optional cfg.xwayland.enable pkgs.xwayland; xdg.configFile."hypr/hyprland.conf" = { text = (lib.optionalString cfg.systemdIntegration '' - exec-once=export XDG_SESSION_TYPE=wayland - exec-once=${pkgs.dbus}/bin/dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP - exec-once=systemctl --user start hyprland-session.target - '') + exec-once=export XDG_SESSION_TYPE=wayland + exec-once=${pkgs.dbus}/bin/dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP + exec-once=systemctl --user start hyprland-session.target + '') + cfg.extraConfig; onChange = let diff --git a/nix/xwayland-hidpi.patch b/nix/xwayland-hidpi.patch new file mode 100644 index 00000000..85f56e02 --- /dev/null +++ b/nix/xwayland-hidpi.patch @@ -0,0 +1,498 @@ +diff --git a/hw/xwayland/xwayland-cursor.c b/hw/xwayland/xwayland-cursor.c +index c4457cc2a61b2103b47f996b51dbbe9eb87bd715..4a33e1f33e73c35c1691564ef4852e7501b02245 100644 +--- a/hw/xwayland/xwayland-cursor.c ++++ b/hw/xwayland/xwayland-cursor.c +@@ -171,6 +171,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); +@@ -190,6 +192,7 @@ xwl_cursor_attach_pixmap(struct xwl_seat *xwl_seat, + 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; +@@ -220,8 +223,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); + } +@@ -230,6 +233,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; +@@ -258,9 +262,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 26b3630c73b62514fe3ba7824371f79868e953f3..55cd8d466a55db03948abe93ffa03bf129b5e17a 100644 +--- a/hw/xwayland/xwayland-input.c ++++ b/hw/xwayland/xwayland-input.c +@@ -412,8 +412,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; +@@ -592,13 +592,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); +@@ -672,7 +673,8 @@ pointer_handle_axis(void *data, struct wl_pointer *pointer, + xorg_list_del(&pending->l); + free(pending); + } else { +- valuator_mask_set_double(&mask, index, wl_fixed_to_double(value) / divisor); ++ double scaled_value = wl_fixed_to_double(value); ++ valuator_mask_set_double(&mask, index, scaled_value / divisor); + } + + QueuePointerEvents(get_pointer_device(xwl_seat), +@@ -740,12 +742,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; +@@ -1057,8 +1060,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); +@@ -1094,8 +1097,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); + } + +@@ -1726,8 +1729,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; +@@ -2714,6 +2717,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; +@@ -2725,6 +2729,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 || +@@ -2733,8 +2738,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 ef705bc01bf8c2d2f170cda9ba21ed8293f50559..b8f6cd51bd240ed5e16271eb4749db18868bea7b 100644 +--- a/hw/xwayland/xwayland-output.c ++++ b/hw/xwayland/xwayland-output.c +@@ -191,6 +191,9 @@ update_screen_size(struct xwl_output *xwl_output, int width, int height) + { + struct xwl_screen *xwl_screen = xwl_output->xwl_screen; + ++ width *= xwl_screen->global_output_scale; ++ height *= xwl_screen->global_output_scale; ++ + if (xwl_screen->root_clip_mode == ROOT_CLIP_FULL) + SetRootClip(xwl_screen->screen, ROOT_CLIP_NONE); + +@@ -497,14 +500,15 @@ xwl_output_set_emulated_mode(struct xwl_output *xwl_output, ClientPtr client, + xwl_output_set_randr_emu_props(xwl_output->xwl_screen, client); + } + +-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; +@@ -523,10 +527,10 @@ apply_output_change(struct xwl_output *xwl_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. +@@ -567,7 +571,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 +@@ -610,7 +614,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 +@@ -678,6 +682,8 @@ xwl_output_create(struct xwl_screen *xwl_screen, uint32_t id) + RROutputSetConnection(xwl_output->randr_output, RR_Connected); + RRTellChanged(xwl_screen->screen); + ++ 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 02b9831083e82a33d85d4404e39d00f06f6c56fd..ec089757f44178dcd7f9c48907f790ce1b2a2729 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; +@@ -100,6 +100,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 c9cf8c2f569a319034e0789e7587414e50237065..5be0c208ca46b1a53a136885fdc8ab44251fe7ff 100644 +--- a/hw/xwayland/xwayland-present.c ++++ b/hw/xwayland/xwayland-present.c +@@ -680,6 +680,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 (!xwl_window->frame_callback) + xwl_window_create_frame_callback(xwl_window); +diff --git a/hw/xwayland/xwayland-screen.c b/hw/xwayland/xwayland-screen.c +index bb18e5c94fbc7134c801e4e1979e8184079d352e..4ec2de7d123dd36315df07a1e95b1f417925f0f8 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" +@@ -110,6 +111,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) +@@ -127,6 +134,37 @@ 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; ++ } ++} ++ + static void + xwl_property_callback(CallbackListPtr *pcbl, void *closure, + void *calldata) +@@ -134,19 +172,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) ++ 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); ++ } + } + + Bool +@@ -521,8 +564,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 +@@ -538,10 +587,34 @@ 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); ++ } ++ } ++} ++ + 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; +@@ -573,6 +646,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) { +@@ -743,6 +817,12 @@ 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); + + xwl_screen_roundtrip(xwl_screen); +diff --git a/hw/xwayland/xwayland-screen.h b/hw/xwayland/xwayland-screen.h +index b965dddd7f964b1d100bbb9d10da1c35ab39810e..7446829d098fbe235e605084a016daff1a8eaea2 100644 +--- a/hw/xwayland/xwayland-screen.h ++++ b/hw/xwayland/xwayland-screen.h +@@ -72,6 +72,8 @@ 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; +@@ -107,6 +109,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; +@@ -134,5 +137,7 @@ 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); + + #endif /* XWAYLAND_SCREEN_H */ +diff --git a/hw/xwayland/xwayland-window.c b/hw/xwayland/xwayland-window.c +index 00f161eda084e335ac07471a2198176d75d9fcf0..ed3903853f0dab1dad390cd8429639541546157d 100644 +--- a/hw/xwayland/xwayland-window.c ++++ b/hw/xwayland/xwayland-window.c +@@ -470,7 +470,8 @@ ensure_surface_for_window(WindowPtr 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); + } +@@ -820,6 +821,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 + diff --git a/nix/xwayland-vsync.patch b/nix/xwayland-vsync.patch new file mode 100644 index 00000000..375db880 --- /dev/null +++ b/nix/xwayland-vsync.patch @@ -0,0 +1,12 @@ +--- a/hw/xwayland/xwayland-present.c ++++ b/hw/xwayland/xwayland-present.c +@@ -824,7 +824,8 @@ + dixDestroyPixmap(vblank->pixmap, vblank->pixmap->drawable.id); + vblank->pixmap = NULL; + +- if (xwl_present_queue_vblank(screen, window, vblank->crtc, ++ if (vblank->target_msc > crtc_msc && ++ xwl_present_queue_vblank(screen, window, vblank->crtc, + vblank->event_id, crtc_msc + 1) + == Success) + return;