diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ba7406cf..d42c0d36 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,6 +20,7 @@ on: - 'clients/**' - 't/**' - 'scripts/**' + - 'docs/**' - '.github/workflows/**' jobs: @@ -82,7 +83,7 @@ jobs: run: | pacman-key --init pacman -Syu --noconfirm - pacman -S --noconfirm git meson clang wlroots0.19 libdrm libinput \ + pacman -S --noconfirm git meson clang wlroots0.20 libdrm libinput \ wayland-protocols cairo pango libxml2 xorg-xwayland librsvg \ libdisplay-info gdb ttf-dejavu foot libsfdo cmocka @@ -92,9 +93,15 @@ jobs: sed -i '/^Types/ s/deb/& deb-src/' /etc/apt/sources.list.d/debian.sources apt-get update apt-get upgrade -y - apt-get install -y git gcc clang gdb xwayland - apt-get build-dep -y labwc - apt-get build-dep -y libwlroots-0.19-dev + apt-get install -y git gcc clang gdb xwayland meson pkgconf + apt-get build-dep -y libwlroots-0.20-dev + # Debian Testing temporarily removed labwc due to wlroots 0.20 transition + #apt-get build-dep -y labwc + # Install the deps manually + apt-get install -y libxml2-dev liblzma-dev libglib2.0-dev \ + libcairo2-dev libpango1.0-dev libinput-dev libpng-dev \ + librsvg2-dev wayland-protocols libxkbcommon-dev libdrm-dev \ + scdoc gettext libwlroots-0.20-dev libsfdo-dev - name: Install FreeBSD dependencies if: matrix.name == 'FreeBSD' @@ -106,7 +113,7 @@ jobs: pkg set -yn pkg:mesa-dri # hack to skip llvm dependency pkg install -y git pcre2 meson gcc pkgconf cairo pango evdev-proto \ hwdata wayland-protocols libdisplay-info libepoll-shim \ - wlroots019 + wlroots020 run: echo "setup done" - name: Install Void Linux dependencies @@ -120,8 +127,8 @@ jobs: xbps-install -Syu xbps-install -y git meson gcc clang pkg-config scdoc \ cairo-devel glib-devel libpng-devel librsvg-devel libxml2-devel \ - pango-devel wlroots0.19-devel gdb bash xorg-server-xwayland \ - dejavu-fonts-ttf libsfdo-devel foot + pango-devel wlroots0.20-devel gdb bash xorg-server-xwayland \ + dejavu-fonts-ttf libsfdo-devel foot hwids # These builds are executed on all runners - name: Build with gcc @@ -209,6 +216,19 @@ jobs: meson compile -C build-gcc-no-feature ' | $TARGET + # No backend build, run on Arch only + - name: Build with gcc - no-backends test + if: matrix.name == 'Arch' + run: | + echo ' + cd "$GITHUB_WORKSPACE" + export CC=gcc + meson setup build-gcc-no-backends \ + -Dwlroots:backends= -Dwlroots:session=disabled --werror \ + --force-fallback-for=wlroots + meson compile -C build-gcc-no-backends + ' | $TARGET + # Unit tests, run on Arch only - name: Build with gcc - unit test if: matrix.name == 'Arch' diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1a2251ad..c1c70b47 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,7 +19,8 @@ - [6. Unit Tests](#unit-tests) - [7. Submitting Patches](#submitting-patches) - [8. Native Language Support](#native-language-support) -- [9. Upversion](#upversion) +- [9. Tools](#tools) +- [10. Upversion](#upversion) # How to Contribute @@ -472,6 +473,29 @@ This generates a new pot file at `po/labwc.pot` 3. Copy the header from the original `labwc.pot` to the new one, keeping the newly generated dates, check for sanity and commit. +# Tools + +We permit LLM and AI-assisted tooling, but only on the basis that humans remain +fully accountable. Contributors must use an `Assisted-by:` tag in the commit +logs if AI tools generated or significantly assisted with any part of the code +or documentation. Humans retain full legal and technical responsibility for the +resulting work, including any defects. + +We have received Pull Requests where the submitter clearly did not understand +the code, could not reason about it, and would be unable to maintain it. This +is not acceptable. + +Any AI-generated code contribution must be compatible with the GPL-2.0-only +license. + +While LLMs can be useful for triage, review, clean-up, API refactoring and +similar tasks, we strongly discourage AI slop. Code review effort is usually the +project's primary bottleneck, and submitting unreviewed, AI-generated garbage +wastes reviewer time, slows development, and harms the overall health of the +project. Likewise, AI-generated commit messages and AI-written text in issues +and discussions are strongly discouraged and may be treated as sabotage on this +basis. + # Upversion It is generally only the lead-maintainer who will upversion, but in order diff --git a/NEWS.md b/NEWS.md index 79a3c918..845451a1 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,6 +9,11 @@ The format is based on [Keep a Changelog] | Date | All Changes | wlroots version | lines-of-code | |------------|---------------|-----------------|---------------| +| 2026-06-15 | [0.20.1] | 0.20.1 | 28337 | +| 2026-05-25 | [0.20.0] | 0.20.1 | 28313 | +| 2026-06-11 | [0.9.8] | 0.19.3 | 29284 | +| 2026-04-17 | [0.9.7] | 0.19.2 | 29277 | +| 2026-03-15 | [0.9.6] | 0.19.2 | 29271 | | 2026-03-04 | [0.9.5] | 0.19.2 | 29251 | | 2026-02-27 | [0.9.4] | 0.19.2 | 29225 | | 2025-12-19 | [0.9.3] | 0.19.2 | 28968 | @@ -42,6 +47,11 @@ The format is based on [Keep a Changelog] | 2021-03-05 | [0.1.0] | 0.12.0 | 4627 | [unreleased]: NEWS.md#unreleased +[0.20.1]: NEWS.md#0201---2026-06-15 +[0.20.0]: NEWS.md#0200---2026-05-25 +[0.9.8]: NEWS.md#098---2026-06-11 +[0.9.7]: NEWS.md#097---2026-04-17 +[0.9.6]: NEWS.md#096---2026-03-15 [0.9.5]: NEWS.md#095---2026-03-04 [0.9.4]: NEWS.md#094---2026-02-27 [0.9.3]: NEWS.md#093---2025-12-19 @@ -74,6 +84,138 @@ The format is based on [Keep a Changelog] [0.2.0]: NEWS.md#020---2021-04-15 [0.1.0]: NEWS.md#010---2021-03-05 +## unreleased + +[unreleased-commits] + +## Notes on wlroots-0.20 + +There are some regression warnings worth noting for the switch to wlroots 0.20: + +- lxqt-panel auto-hiding does not work with a panel size greater than 40 [#3600] + [wlroots-5392] +- Some Wine game windows disappear after alt-tab. It is not yet clear where the + bug is, but the issue manifests itself when running wlroots-0.20 [#3615] + [wlroots-4103] + +[wlroots-5392]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5392 +[wlroots-4103]: https://gitlab.freedesktop.org/wlroots/wlroots/-/work_items/4103 + +## 0.20.1 - 2026-06-15 + +[0.20.1-commits] + +This is a small bug fix release. + +### Fixed + +- Handle titles with no visible characters, for example the left-to-right mark + (‎) @Consolatis [#3630] +- Protect against SIGABRT when TTY switching in unusual circumstances by + ensuring that xdg-shell windows have sensible width and height before trying + to set size on configure. This protects against an edge case experienced when + switching between labwc on one TTY and Xfce on XOrg on another TTY. + @johanmalm @Tamaranch [#3617] + +## 0.20.0 - 2026-05-25 + +[0.20.0-commits] + +This is the first release using wlroots-0.20 and therefore has an increased risk +of teething issues. Many thanks to @Consolatis for leading the effort to port +across [#2956]. + +In terms of new features, the most noteworthy ones include: (i) the frequently +requested show-desktop action; (ii) initial toplevel capture support to +screenshot specific windows; (iii) menu accelerators/shortcuts; and (iv) HDR10 +support when running with the Vulkan renderer option. + +The eagle-eyed amongst you will have noticed the sudden jump from labwc `0.9.7` +to `0.20.0`. The reason for this is to align the minor number to that of the +wlroots version against which the compositor is linked. + +The 0.9.x series has turned into a maintenance branch named v0.9 with bug fixes +only, for anyone preferring to build with wlroots-0.19. + +Note to maintainers: + +- libinput >=1.26 is required in support of tablet tool pressure range + configuration. +- wlroots >=0.20.1 is required to avoid some bugs that we do not want labwc to + ship with. For details, see: + https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5325 + +### Added + +- Partially support toplevel-capture. Note that the following are not yet + implemented: (i) XWayland child windows; (ii) XWayland unmanaged windows + (e.g. popups); (iii) xdg child window positioning; (iv) xdg subsurfaces; + and (v) xdg popup positioning. [#2968] @Consolatis +- Add command line option -t|--title to set the labwc window title when running + nested [#3577] @mdsib +- Add support for HDR10 output @kode54 @Consolatis [#3424] +- Include wlroots version in --version string @Consolatis [#3567] [#3581] +- Implement menu accelerators (one-letter mnemonics to quickly select/exec + items from the current menu) @ch3rn1ka [#3505] +- Add Next/PreviousWindowImmediate actions @elviosak @johanmalm [#3547] +- Add labnag options `--details-border-color` and `--details-margin` + @st0rm-shad0w [#3527] +- Add config option `` to defer raise-on-focus by a + small amount when `raiseOnFocus` is enabled @joske [#3513] +- Install `labwc-session.target` systemd user unit when the systemd dependency + is available @joske [#3534] +- Add `onbutton` to config option ``. Also add + associated option ``. @diniamo [#3540] +- Add `overrideInhibition` option to `` [#3507] @drougas +- Add action `ToggleShowDesktop` to hide/unhide windows, and default keybind + `Super-d` to trigger this action [#3500] [#3595] @johanmalm +- Add `` config option so that privileged protocols can be + restricted [#3493] @xi +- Add action `DebugToggleKeyStateIndicator` to show a key-state on-screen + display (OSD) for debugging. [#3499] @johanmalm @tokyo4j +- Add support for `color-management-v1` and `color-representation-manager-v1` + protocols [#3469] @ManuLinares +- Add configuration option `` + to enable tablet tool pressure range libinput settings [#2916] @jp7677 +- Add `wl_fixes` interface [#2956] @kode54 + +### Fixed + +- Enable labnag long option --exclusive-zone [#3576] @st0rm-shad0w +- Position chromium popup correctly when a window is maximized on a multi- + output setup @elviosak [#3547] +- Run session activation environment update synchronously to avoid a race + condition with the autostart script [#3543] @joske +- Allow interactive resize on fully maximized windows so that a resize + initiated by modifier plus right-mouse-button-drag is not ignored [#3525] + @bjorn +- Gracefully handle missing XWayland packages, so that a labwc compositor which + has been built with XWayland support (which is optional) can be run even if + XWayland is not installed. [#3401] @quite +- Rework how XWayland window initial geometry is set to ensure that the natural + geometry does not exceed the usable output area when handling initial + maximize/fullscreen requests. [#3439] @jlindgren90. +- For XWayland windows, sync always-on-top state back to X.Org Server. This + makes `wmctrl -b toggle,above` work. [#3446] @jlindgren90 +- Fix missing title and icon with XWayland client override-redirect toggle. + There are no known issues with clients, so this is purely for preventative + purposes. [#3450] @jlindgren90 +- Update titlebar title when set to empty and fix an associated issue causing + the title to be misplaced outside of the titlebar when the window is resized. + [#3443] @tokyo4j +- When running nested, exit compositor when last output is destroyed because + in this situation, each output corresponds to a window in the parent + compositor and, unlike DRM outputs, these cannot be reconnected after being + destroyed. [#3440] @marler8997 +- Allow policy-based placement to apply when an initially-maximized/fullscreen + view is restored to floating geometry. [#3387] [#3502] @jlindgren90 + +### Changed + +- Change the default keybinds for XF86Audio{LowerVolume,RaiseVolume,Mute} to use + pactl instead of amixer [#3484] @danielrrrr +- Drop cosmic-workspace protocol [#3031] @tokyo4j + ## Notes on wlroots-0.19 There are some regression warnings worth noting for the switch to wlroots 0.19: @@ -108,9 +250,73 @@ There are some regression warnings worth noting for the switch to wlroots 0.19: [wlroots-5098]:https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5098 [gtk-8792]: https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/8792 -## unreleased +## 0.9.8 - 2026-06-11 -[unreleased-commits] +[0.9.8-commits] + +### Fixed + +- Handle titles with no visible characters @Consolatis [#3631] +- Save window position on initial placement to fix Raspberry Pi pygame SDL + XWayland window starting off-screen. @tokyo4j [#3433] [#3625] [#3616] + +## 0.9.7 - 2026-04-17 + +[0.9.7-commits] + +This is a small bug fix release. + +``` + 0.9.6--------0.9.7 <--- v0.9 branch with bug-fixes only + / + / +0.9.4--------0.9.5-------- <-- master (built with wlroots-0.20) +``` + +## Fixed + +- Fix intermittent failed-to-get-texture issue with some clients (e.g. foot) + when using the window-switcher in the thumbnail mode. [#3511] @yuiiio +- Fix tablet tool tilt motion. [#3494] @jp7677 +- Handle window-switcher buffer allocation failure when in 'thumbnail' mode. + This is believed to be very unlikely to happen, but has been reported by one + user and is believed to be GPU driver related. [#3490] @Consolatis + +## 0.9.6 - 2026-03-15 + +[0.9.6-commits] + +This is an earlier-than-usual release containing bug fixes only. It has been +done on a separate branch (v0.9) to avoid the inclusion of refactoring and new +features. + +``` + 0.9.6 <--- bug-fixes only + / + / +0.9.4--------0.9.5-------- <-- master +``` + +### Fixed + +- Disable outputs where all modes fail [#3428] [#3429] @Consolatis @kode54 +- Fix regression in `0.9.4` that causes `NextWindow` action to segfault when + no outputs are connected. [#3425] [#3430] @Consolatis +- Fix typo to allow `xdg-dialog-v1` global [#3426] @xi + +### Changed + +- Disallow X11 window always-on-top requests by default to fix an issue whereby + Alt+Tab cannot be used to switch to other windows when using some XWayland + Wine games [#3441]. Add window-rule property `allowAlwaysOnTop` to optionally + allow this always-on-top requests. Add the snippet below to the `rc.xml` file + to restore the previous behaviour. [#3445] @Consolatis + +``` + + + +``` ## 0.9.5 - 2026-03-04 @@ -139,9 +345,9 @@ Note to package maintainers: This release requires wayland version >=1.22.90 - Add theme option window.button.hover.bg.color [#3365] @johanmalm - Implement scrollable window-switcher OSD [#3291] @tokyo4j - Support the `NextWindow` options listed below [#3271] @tokyo4j - - `` - - `` - - `` + - `` + - `` + - `` - Add config option `*` for setting the active workspace on startup. [#3265] @5trixs0f @@ -189,7 +395,7 @@ Note to package maintainers: This release requires wayland version >=1.22.90 and get keyboard focus so that they can be operated with the keyboard. An example use-case is the xfce4-panel applications-menu being opened by the command xfce4-popup-applicationsmenu. [#3165] @johanmalm - - On popup destory, return focus to whoever had it before the popop [#3165] + - On popup destroy, return focus to whoever had it before the popop [#3165] @johanmalm @tokyo4j - Unshade window if selected from client-list-combined-menu [#3345] @Amodio - Show non-dialog child windows in window-switcher [#3339] @tokyo4j @@ -223,7 +429,7 @@ A big thank you to all involved in this release. ### Added -- Add `` to optionally order windows by age +- Add `` to optionally order windows by age rather than most recent focus. @mbroemme [#3229] - Replace `` with `` to provide more granular control when configuring the size of snapping areas @@ -254,7 +460,7 @@ A big thank you to all involved in this release. [#3134] - labnag: add --keyboard-focus option @tokyo4j [#3120] - Allow window switcher to temporarily unshade windows using config option - `` @Amodio @Consolatis [#3124] + `` @Amodio @Consolatis [#3124] - For the 'classic' style window-switcher, add the following theme options: - `osd.window-switcher.style-classic.item.active.border.color` - `osd.window-switcher.style-classic.item.active.bg.color` @@ -284,7 +490,7 @@ A big thank you to all involved in this release. client surface. Fixes a regression in 885919f. @tokyo4j [#3211] - Set all foreign-toplevel initial states correctly. This is not believed to fix any particular user-issue, but just feels safer. @jlindgren90 [#3217] -- Update layer-shell client top layer visiblity on unmap instead of destroy +- Update layer-shell client top layer visibility on unmap instead of destroy because it is possible for fullscreen xwayland windows to be unmapped without being destroyed, and in this case the top layer visibility needs to be updated to unhide other layer-shell clients like panels. @jlindgren90 [#3199] @@ -296,7 +502,7 @@ A big thank you to all involved in this release. @elviosak [#3146] [#3168] - Work around client-side rounding issues at right/bottom pixel. This fixes an issue with some clients (notably Qt ones) where cursor coordinates in the - rightmost or bottom fixel are incorrectly rounded up putting them outside the + rightmost or bottom pixel are incorrectly rounded up putting them outside the surface bounds. The issue has been particularly noticeable with layer-shell clients like lxqt-panel. @jlindgren90 [#3157] [#2379] [#3099] Note: This also avoids a similar server-side rounding issue with some @@ -344,7 +550,7 @@ A big thank you to all involved in this release. when a window is using fullscreen mode. @johanmalm [#3158] - Call labnag with on-demand keyboard interactivity by default @tokyo4j [#3120] - Temporarily unshade windows when switching windows. Restore old behaviour with - `` @Amodio @Consolatis [#3124] + `` @Amodio @Consolatis [#3124] - In the classic style window-switcher, the default color of the selected window item has been changed to inherit the border color but with 15% opacity @tokyo4j [#3118] @@ -363,7 +569,7 @@ A big thank you to all involved in this release. ``. @tokyo4j [#2981] - Add `toggle` option to `GoToDesktop` action. This has the effect of going back to the last desktop if already on the target. @RainerKuemmerle [#3024] -- Add `` to allow hiding titlebar +- Add `` to allow hiding titlebar when window is maximized. @CosmicFusion @tokyo4j [#3015] - Use client-send-to-menu as 'Workspace' submenu in built-in client-menu @johanmalm [#2995] @@ -414,7 +620,7 @@ A big thank you to all involved in this release. - Change default keybind `W-` to combine cardinal directions to support resizing of windows to fill a quarter of an output. This only affects users who do not use an `rc.xml` (thereby using default keybinds) or use the - `` option. Previous behavior can be restored by setting + `` option. Previous behavior can be restored by setting `combine="no"` as shown below. [#3081] @tokyo4j ``` @@ -513,7 +719,7 @@ window.*.title.bg.colorTo.splitTo: window rule to enable this. @Consolatis @tokyo4j [#2840] - Add config option ``. This enables autoscroll (middle-click to scroll up/down) in Chromium and electron based clients - without inadvertantly pasting the primary clipboard. @johanmalm [#2832] + without inadvertently pasting the primary clipboard. @johanmalm [#2832] - Bump `xdg_shell` version from 3 to 6 @tokyo4j [#2814] - Bump `wl_compositor` version from 5 to 6 @tokyo4j [#2812] - Support tablet tool mouse buttons @jp7677 [#2778] @@ -590,7 +796,7 @@ window.*.title.bg.colorTo.splitTo: agnostic on choice of launcher. - `A-` for `MoveToEdge` because `Alt-` keybinds should be for clients to use and this one results in frequent user complaints because it prevents - some common usage patterns like alt-left/right in web browers. + some common usage patterns like alt-left/right in web browsers. - Change default titlebar menu button from a dot to an arrow @johanmalm [#2844] - When `dragLock` is set to `yes`, the drag no longer expires after a short delay (known as `Sticky` mode) as recommended by libinput [#2803]. The timeout @@ -625,7 +831,7 @@ release. - Localize desktop-entry application names used by the window switcher via `desktop_entry_name` or the `%n` specifier @tokyo4j [#2653] - Add `HideCursor` action @jp7677 [#2633] -- Support application icons in window-switcher using `` +- Support application icons in window-switcher using `` and use this by default. @tokyo4j [#2621] - Support application icons in client-list-combined-menu @tokyo4j [#2617] - Support the use of the keypad-enter key when using menu. @zeusgoose [#2610] @@ -762,7 +968,7 @@ Notes to package maintainers: closing a popup did not move the pointer focus to the main toplevel until the cursor was moved. [#2443] - Improve algorithm for menu placement with xdg-positioner [#2408] -- Do not forward IME key-release without correspinding key-press to avoid stuck +- Do not forward IME key-release without corresponding key-press to avoid stuck keys [#2437] ### Changed @@ -824,7 +1030,7 @@ Notes to package maintainers: ```xml - + ``` @@ -841,8 +1047,8 @@ menu.border.color: #aaaaaa ```xml - - + + ``` @@ -972,7 +1178,7 @@ Notes to package maintainers: - Support the openbox style menus listed below. Written-by: @droc12345 1. `client-list-combined-menu` shows windows across all workspaces. This can be used with a mouse/key bind using: - `` [#2101] + `` [#2101] 2. `client-send-to` shows all workspaces that the current window can be sent to. This can additional be used within a client menu using: `` [#2152] @@ -1131,14 +1337,14 @@ have been attributed with a 'Written-by' against each relevant log entry. ```xml cascade - + ``` - Support relative tablet motion. Written-by: @jp7677 [#1962] ```xml - + ``` ### Fixed @@ -1222,7 +1428,7 @@ joint effort by @spl237 and @Consolatis. - Respect `menu.overlap.x` when using pipemenus. [#1940] - Do not try to restore windows to very small width/height on unmaximize. This fixes a bug with Thonny (Python IDE made with Tk). [#1938] -- Conditially set squared server-side decoration (SSD) corners when a view is +- Conditionally set squared server-side decoration (SSD) corners when a view is tiled. Written-by: @jp7677 [#1926] - Remember initial direction when starting window-cycling with `PreviousView`. Also make the toggling of direction when shift is pressed relative to the @@ -1246,7 +1452,7 @@ joint effort by @spl237 and @Consolatis. Chromium and Steam. [#1861] - Session-lock: fix flashing & update cursor shape. [#1858] - Remove tearing-controller listeners on destroy. [#1853] -- Handle invalid `ForEach` and `If` action cofigs. [#1838] +- Handle invalid `ForEach` and `If` action configs. [#1838] - Delay startup of applications until event loop is ready. This avoids race conditions when using autostart scripts that trigger a labwc SIGHUP. [#1588] - With `SendToDesktop` action follow=no option, ensure the topmost window is @@ -1263,7 +1469,7 @@ joint effort by @spl237 and @Consolatis. - Remove subprojects/seatd.wrap as no longer needed - Action `MoveToCursor` is deprecated in favour of: - ``. + ``. ## 0.7.2 - 2024-05-10 @@ -1280,7 +1486,7 @@ contributions from others as noted in the log. ### Added - Add `` to prevent clicks with small movements - from inadvertantly closing a menu or selecting a menu item. This is the + from inadvertently closing a menu or selecting a menu item. This is the equivalent of `` on Openbox. [#1760] - Support drop-shadows (disabled by default) for windows using server-side decorations. Written-by: @cillian64 @@ -1308,7 +1514,7 @@ window.inactive.shadow.color: #00000040 ```xml - + @@ -1350,7 +1556,7 @@ osd.window-switcher.width: 75% yes|no - + ``` @@ -1513,7 +1719,7 @@ osd.window-switcher.preview.border.color: #ffffff,#00a2ff,#ffffff ```xml - + ``` @@ -1521,7 +1727,7 @@ osd.window-switcher.preview.border.color: #ffffff,#00a2ff,#ffffff is already used by the action itself). [#1589] ```xml - + ``` - Do not deactivate window when giving keyboard focus to a non-view @@ -1586,8 +1792,8 @@ osd.window-switcher.preview.border.color: #ffffff,#00a2ff,#ffffff Written-by: @jp7677 ```xml - - + + ``` - Add tablet support including: @@ -1800,7 +2006,7 @@ relating to surface focus and keyboard issues, amongst others. - Allow referencing the current workspace in actions, for example: ```xml - + ``` ### Fixed @@ -1956,7 +2162,7 @@ relating to surface focus and keyboard issues, amongst others. ```xml - + ``` @@ -2023,9 +2229,9 @@ relating to surface focus and keyboard issues, amongst others. ```xml - + - + ``` @@ -2110,7 +2316,7 @@ Unless otherwise stated all contributions are by the core-devs ```xml - + @@ -2374,7 +2580,7 @@ reported, tested and fixed issues. Particular mentions go to @bi4k8, actions to be de-coupled from buttons. As a result, "Drag" and "DoubleClick" actions previously defined against "TitleBar" should now come under the "Title" context, for example: - `` + `` - Remove default alt-escape keybind for Exit because too many people have exited the compositor by mistake trying to get out of alt-tab cycling or similar. @@ -2607,7 +2813,12 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16 ShowMenu [Keep a Changelog]: https://keepachangelog.com/en/1.0.0/ -[unreleased-commits]: https://github.com/labwc/labwc/compare/0.9.5...HEAD +[unreleased-commits]: https://github.com/labwc/labwc/compare/0.20.1...HEAD +[0.20.1-commits]: https://github.com/labwc/labwc/compare/0.20.0..0.20.1 +[0.20.0-commits]: https://github.com/labwc/labwc/compare/0.9.5..0.20.0 +[0.9.8-commits]: https://github.com/labwc/labwc/compare/0.9.7...0.9.8 +[0.9.7-commits]: https://github.com/labwc/labwc/compare/0.9.6...0.9.7 +[0.9.6-commits]: https://github.com/labwc/labwc/compare/0.9.5...0.9.6 [0.9.5-commits]: https://github.com/labwc/labwc/compare/0.9.4...0.9.5 [0.9.4-commits]: https://github.com/labwc/labwc/compare/0.9.3...0.9.4 [0.9.3-commits]: https://github.com/labwc/labwc/compare/0.9.2...0.9.3 @@ -3046,6 +3257,7 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16 [#2909]: https://github.com/labwc/labwc/pull/2909 [#2910]: https://github.com/labwc/labwc/pull/2910 [#2914]: https://github.com/labwc/labwc/pull/2914 +[#2916]: https://github.com/labwc/labwc/pull/2916 [#2933]: https://github.com/labwc/labwc/pull/2933 [#2937]: https://github.com/labwc/labwc/pull/2937 [#2939]: https://github.com/labwc/labwc/pull/2939 @@ -3053,8 +3265,10 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16 [#2943]: https://github.com/labwc/labwc/pull/2943 [#2944]: https://github.com/labwc/labwc/pull/2944 [#2948]: https://github.com/labwc/labwc/pull/2948 +[#2956]: https://github.com/labwc/labwc/pull/2956 [#2965]: https://github.com/labwc/labwc/pull/2965 [#2967]: https://github.com/labwc/labwc/pull/2967 +[#2968]: https://github.com/labwc/labwc/pull/2968 [#2970]: https://github.com/labwc/labwc/pull/2970 [#2971]: https://github.com/labwc/labwc/pull/2971 [#2972]: https://github.com/labwc/labwc/pull/2972 @@ -3070,6 +3284,7 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16 [#3020]: https://github.com/labwc/labwc/pull/3020 [#3024]: https://github.com/labwc/labwc/pull/3024 [#3028]: https://github.com/labwc/labwc/pull/3028 +[#3031]: https://github.com/labwc/labwc/pull/3031 [#3033]: https://github.com/labwc/labwc/pull/3033 [#3039]: https://github.com/labwc/labwc/pull/3039 [#3042]: https://github.com/labwc/labwc/pull/3042 @@ -3135,8 +3350,54 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16 [#3365]: https://github.com/labwc/labwc/pull/3365 [#3372]: https://github.com/labwc/labwc/pull/3372 [#3373]: https://github.com/labwc/labwc/pull/3373 +[#3387]: https://github.com/labwc/labwc/pull/3387 [#3400]: https://github.com/labwc/labwc/pull/3400 +[#3401]: https://github.com/labwc/labwc/pull/3401 [#3406]: https://github.com/labwc/labwc/pull/3406 [#3410]: https://github.com/labwc/labwc/pull/3410 [#3411]: https://github.com/labwc/labwc/pull/3411 [#3412]: https://github.com/labwc/labwc/pull/3412 +[#3424]: https://github.com/labwc/labwc/pull/3424 +[#3425]: https://github.com/labwc/labwc/pull/3425 +[#3426]: https://github.com/labwc/labwc/pull/3426 +[#3428]: https://github.com/labwc/labwc/pull/3428 +[#3429]: https://github.com/labwc/labwc/pull/3429 +[#3430]: https://github.com/labwc/labwc/pull/3430 +[#3433]: https://github.com/labwc/labwc/pull/3433 +[#3439]: https://github.com/labwc/labwc/pull/3439 +[#3440]: https://github.com/labwc/labwc/pull/3440 +[#3441]: https://github.com/labwc/labwc/pull/3441 +[#3443]: https://github.com/labwc/labwc/pull/3443 +[#3445]: https://github.com/labwc/labwc/pull/3445 +[#3446]: https://github.com/labwc/labwc/pull/3446 +[#3450]: https://github.com/labwc/labwc/pull/3450 +[#3469]: https://github.com/labwc/labwc/pull/3469 +[#3484]: https://github.com/labwc/labwc/pull/3484 +[#3490]: https://github.com/labwc/labwc/pull/3490 +[#3493]: https://github.com/labwc/labwc/pull/3493 +[#3494]: https://github.com/labwc/labwc/pull/3494 +[#3499]: https://github.com/labwc/labwc/pull/3499 +[#3500]: https://github.com/labwc/labwc/pull/3500 +[#3502]: https://github.com/labwc/labwc/pull/3502 +[#3505]: https://github.com/labwc/labwc/pull/3505 +[#3507]: https://github.com/labwc/labwc/pull/3507 +[#3511]: https://github.com/labwc/labwc/pull/3511 +[#3513]: https://github.com/labwc/labwc/pull/3513 +[#3525]: https://github.com/labwc/labwc/pull/3525 +[#3527]: https://github.com/labwc/labwc/pull/3527 +[#3534]: https://github.com/labwc/labwc/pull/3534 +[#3540]: https://github.com/labwc/labwc/pull/3540 +[#3543]: https://github.com/labwc/labwc/pull/3543 +[#3547]: https://github.com/labwc/labwc/pull/3547 +[#3567]: https://github.com/labwc/labwc/pull/3567 +[#3576]: https://github.com/labwc/labwc/pull/3576 +[#3577]: https://github.com/labwc/labwc/pull/3577 +[#3581]: https://github.com/labwc/labwc/pull/3581 +[#3595]: https://github.com/labwc/labwc/pull/3595 +[#3600]: https://github.com/labwc/labwc/pull/3600 +[#3615]: https://github.com/labwc/labwc/pull/3615 +[#3616]: https://github.com/labwc/labwc/pull/3616 +[#3617]: https://github.com/labwc/labwc/pull/3617 +[#3625]: https://github.com/labwc/labwc/pull/3625 +[#3630]: https://github.com/labwc/labwc/pull/3630 +[#3631]: https://github.com/labwc/labwc/pull/3631 diff --git a/README.md b/README.md index 379de77a..4fa5c0e4 100644 --- a/README.md +++ b/README.md @@ -153,7 +153,7 @@ Build dependencies include: - meson, ninja, gcc/clang - wayland-protocols -Disable xwayland with `meson -Dxwayland=disabled build/` +Disable xwayland with `meson setup -Dxwayland=disabled build/` For OS/distribution specific details see [wiki]. @@ -213,15 +213,16 @@ If you have not created an rc.xml config file, default bindings will be: | `super`-`return` | lab-sensible-terminal | `alt`-`F4` | close window | `super`-`a` | toggle maximize +| `super`-`d` | toggle show-desktop | `super`-`mouse-left` | move window | `super`-`mouse-right` | resize window | `super`-`arrow` | resize window to fill half the output | `alt`-`space` | show the window menu -| `XF86_AudioLowerVolume` | amixer sset Master 5%- -| `XF86_AudioRaiseVolume` | amixer sset Master 5%+ -| `XF86_AudioMute` | amixer sset Master toggle -| `XF86_MonBrightnessUp` | brightnessctl set +10% -| `XF86_MonBrightnessDown` | brightnessctl set 10%- +| `XF86AudioLowerVolume` | pactl set-sink-volume @DEFAULT_SINK@ -5% +| `XF86AudioRaiseVolume` | pactl set-sink-volume @DEFAULT_SINK@ +5% +| `XF86AudioMute` | pactl set-sink-mute @DEFAULT_SINK@ toggle +| `XF86MonBrightnessUp` | brightnessctl set +10% +| `XF86MonBrightnessDown` | brightnessctl set 10%- A root-menu can be opened by clicking on the desktop. diff --git a/clients/labnag.c b/clients/labnag.c index 31ac4e9d..60a5abb3 100644 --- a/clients/labnag.c +++ b/clients/labnag.c @@ -46,6 +46,7 @@ struct conf { uint32_t button_text; uint32_t button_background; uint32_t details_background; + uint32_t details_border_color; uint32_t background; uint32_t text; uint32_t button_border; @@ -60,6 +61,7 @@ struct conf { ssize_t button_gap_close; ssize_t button_margin_right; ssize_t button_padding; + ssize_t details_margin; }; struct pointer { @@ -206,6 +208,16 @@ static void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height, int *baseline, double scale, bool markup, const char *fmt, ...) { + if (width) { + *width = 0; + } + if (height) { + *height = 0; + } + if (baseline) { + *baseline = 0; + } + va_list args; va_start(args, fmt); gchar *buf = g_strdup_vprintf(fmt, args); @@ -290,10 +302,10 @@ render_details_scroll_button(cairo_t *cairo, struct nag *nag, get_text_size(cairo, nag->conf->font_description, &text_width, &text_height, NULL, 1, true, "%s", button->text); - int border = nag->conf->button_border_thickness; - int padding = nag->conf->button_padding; + int border = nag->conf->details_border_thickness; + int padding = (nag->conf->button_padding / 3) + 2; - cairo_set_source_u32(cairo, nag->conf->details_background); + cairo_set_source_u32(cairo, nag->conf->details_border_color); cairo_rectangle(cairo, button->x, button->y, button->width, button->height); cairo_fill(cairo); @@ -306,7 +318,7 @@ render_details_scroll_button(cairo_t *cairo, struct nag *nag, cairo_set_source_u32(cairo, nag->conf->button_text); cairo_move_to(cairo, button->x + border + padding, - button->y + border + (button->height - text_height) / 2); + button->y + (button->height - text_height) / 2); render_text(cairo, nag->conf->font_description, 1, true, "%s", button->text); } @@ -321,8 +333,8 @@ get_detailed_scroll_button_width(cairo_t *cairo, struct nag *nag) NULL, 1, true, "%s", nag->details.button_down.text); int text_width = up_width > down_width ? up_width : down_width; - int border = nag->conf->button_border_thickness; - int padding = nag->conf->button_padding; + int border = nag->conf->details_border_thickness; + int padding = (nag->conf->button_padding / 3) + 2; return text_width + border * 2 + padding * 2; } @@ -333,8 +345,9 @@ render_detailed(cairo_t *cairo, struct nag *nag, uint32_t y) uint32_t width = nag->width; int border = nag->conf->details_border_thickness; + int margin = nag->conf->details_margin; int padding = nag->conf->message_padding; - int decor = padding + border; + int decor = margin + border; nag->details.x = decor; nag->details.y = y + decor; @@ -362,7 +375,7 @@ render_detailed(cairo_t *cairo, struct nag *nag, uint32_t y) bool show_buttons = nag->details.offset > 0; int button_width = get_detailed_scroll_button_width(cairo, nag); if (show_buttons) { - nag->details.width -= button_width; + nag->details.width += border - button_width; pango_layout_set_width(layout, (nag->details.width - padding * 2) * PANGO_SCALE); } @@ -375,7 +388,7 @@ render_detailed(cairo_t *cairo, struct nag *nag, uint32_t y) if (!show_buttons) { show_buttons = true; - nag->details.width -= button_width; + nag->details.width += border - button_width; pango_layout_set_width(layout, (nag->details.width - padding * 2) * PANGO_SCALE); } @@ -391,21 +404,29 @@ render_detailed(cairo_t *cairo, struct nag *nag, uint32_t y) nag->details.visible_lines = pango_layout_get_line_count(layout); + int border_rect_height = nag->details.height + 2 * border; + if (show_buttons) { nag->details.button_up.x = nag->details.x + nag->details.width; - nag->details.button_up.y = nag->details.y; + nag->details.button_up.y = nag->details.y - border; nag->details.button_up.width = button_width; - nag->details.button_up.height = nag->details.height / 2; + nag->details.button_up.height = (border_rect_height + border) / 2; render_details_scroll_button(cairo, nag, &nag->details.button_up); nag->details.button_down.x = nag->details.x + nag->details.width; nag->details.button_down.y = - nag->details.button_up.y + nag->details.button_up.height; + nag->details.button_up.y + nag->details.button_up.height - border; nag->details.button_down.width = button_width; - nag->details.button_down.height = nag->details.height / 2; + nag->details.button_down.height = + border_rect_height - nag->details.button_up.height + border; render_details_scroll_button(cairo, nag, &nag->details.button_down); } + cairo_set_source_u32(cairo, nag->conf->details_border_color); + cairo_rectangle(cairo, margin, nag->details.y - border, + nag->details.width + 2 * border, border_rect_height); + cairo_fill(cairo); + cairo_set_source_u32(cairo, nag->conf->details_background); cairo_rectangle(cairo, nag->details.x, nag->details.y, nag->details.width, nag->details.height); @@ -437,7 +458,7 @@ render_button(cairo_t *cairo, struct nag *nag, struct button *button, } button->x = *x - border - text_width - padding * 2 + 1; - button->y = (int)(ideal_height - text_height) / 2 - padding + 1; + button->y = (int)(ideal_height - text_height) / 2 - padding; button->width = text_width + padding * 2; button->height = text_height + padding * 2; @@ -1388,6 +1409,7 @@ nag_setup(struct nag *nag) } sigset_t mask; + sigemptyset(&mask); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGTERM); sigprocmask(SIG_BLOCK, &mask, NULL); @@ -1453,14 +1475,16 @@ conf_init(struct conf *conf) conf->keyboard_focus = ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE; conf->bar_border_thickness = 2; conf->message_padding = 8; - conf->details_border_thickness = 3; conf->button_border_thickness = 3; conf->button_gap = 20; conf->button_gap_close = 15; conf->button_margin_right = 2; conf->button_padding = 3; conf->button_background = 0x680A0AFF; + conf->details_margin = 11; + conf->details_border_thickness = 3; conf->details_background = 0x680A0AFF; + conf->details_border_color = 0x680A0AFF; conf->background = 0x900000FF; conf->text = 0xFFFFFFFF; conf->button_text = 0xFFFFFFFF; @@ -1540,16 +1564,18 @@ nag_parse_options(int argc, char **argv, struct nag *nag, TO_COLOR_BORDER_BOTTOM, TO_COLOR_BUTTON_BG, TO_COLOR_DETAILS, + TO_COLOR_DETAILS_BORDER, TO_COLOR_TEXT, TO_COLOR_BUTTON_TEXT, TO_THICK_BAR_BORDER, TO_PADDING_MESSAGE, - TO_THICK_DET_BORDER, + TO_THICK_DETAILS_BORDER, TO_THICK_BTN_BORDER, TO_GAP_BTN, TO_GAP_BTN_DISMISS, TO_MARGIN_BTN_RIGHT, TO_PADDING_BTN, + TO_MARGIN_DETAILS, }; static const struct option opts[] = { @@ -1566,6 +1592,7 @@ nag_parse_options(int argc, char **argv, struct nag *nag, {"message", required_argument, NULL, 'm'}, {"output", required_argument, NULL, 'o'}, {"timeout", required_argument, NULL, 't'}, + {"exclusive-zone", no_argument, NULL, 'x'}, {"version", no_argument, NULL, 'v'}, {"background-color", required_argument, NULL, TO_COLOR_BACKGROUND}, @@ -1576,8 +1603,10 @@ nag_parse_options(int argc, char **argv, struct nag *nag, {"button-text-color", required_argument, NULL, TO_COLOR_BUTTON_TEXT}, {"border-bottom-size", required_argument, NULL, TO_THICK_BAR_BORDER}, {"message-padding", required_argument, NULL, TO_PADDING_MESSAGE}, - {"details-border-size", required_argument, NULL, TO_THICK_DET_BORDER}, + {"details-border-size", required_argument, NULL, TO_THICK_DETAILS_BORDER}, {"details-background-color", required_argument, NULL, TO_COLOR_DETAILS}, + {"details-border-color", required_argument, NULL, TO_COLOR_DETAILS_BORDER}, + {"details-margin", required_argument, NULL, TO_MARGIN_DETAILS}, {"button-border-size", required_argument, NULL, TO_THICK_BTN_BORDER}, {"button-gap", required_argument, NULL, TO_GAP_BTN}, {"button-dismiss-gap", required_argument, NULL, TO_GAP_BTN_DISMISS}, @@ -1622,6 +1651,9 @@ nag_parse_options(int argc, char **argv, struct nag *nag, " --details-border-size size Thickness for the details border.\n" " --details-background-color RRGGBB[AA]\n" " Details background color.\n" + " --details-border-color RRGGBB[AA]\n" + " Details border color.\n" + " --details-margin margin Margin for the details.\n" " --button-border-size size Thickness for the button border.\n" " --button-gap gap Size of the gap between buttons\n" " --button-dismiss-gap gap Size of the gap for dismiss button.\n" @@ -1758,6 +1790,11 @@ nag_parse_options(int argc, char **argv, struct nag *nag, fprintf(stderr, "Invalid details background color: %s\n", optarg); } break; + case TO_COLOR_DETAILS_BORDER: + if (!parse_color(optarg, &conf->details_border_color)) { + fprintf(stderr, "Invalid details border color: %s\n", optarg); + } + break; case TO_COLOR_TEXT: /* Text color */ if (!parse_color(optarg, &conf->text)) { fprintf(stderr, "Invalid text color: %s\n", optarg); @@ -1774,7 +1811,7 @@ nag_parse_options(int argc, char **argv, struct nag *nag, case TO_PADDING_MESSAGE: /* Message padding */ conf->message_padding = strtol(optarg, NULL, 0); break; - case TO_THICK_DET_BORDER: /* Details border thickness */ + case TO_THICK_DETAILS_BORDER: /* Details border thickness */ conf->details_border_thickness = strtol(optarg, NULL, 0); break; case TO_THICK_BTN_BORDER: /* Button border thickness */ @@ -1792,6 +1829,9 @@ nag_parse_options(int argc, char **argv, struct nag *nag, case TO_PADDING_BTN: /* Padding for the button text */ conf->button_padding = strtol(optarg, NULL, 0); break; + case TO_MARGIN_DETAILS: + conf->details_margin = strtol(optarg, NULL, 0); + break; default: /* Help or unknown flag */ fprintf(c == 'h' ? stdout : stderr, "%s", usage); return LAB_EXIT_FAILURE; diff --git a/clients/meson.build b/clients/meson.build index 467bc035..55a4c0e5 100644 --- a/clients/meson.build +++ b/clients/meson.build @@ -1,59 +1,61 @@ -wayland_client = dependency('wayland-client') -wayland_cursor = dependency('wayland-cursor') +if get_option('labnag').allowed() + wayland_client = dependency('wayland-client') + wayland_cursor = dependency('wayland-cursor') -nag_sources = files( - 'labnag.c', - 'pool-buffer.c', -) - -wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') - -protocols = [ - wl_protocol_dir / 'stable/tablet/tablet-v2.xml', - wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml', - wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', - '../protocols/wlr-layer-shell-unstable-v1.xml', -] - -foreach xml : protocols - nag_sources += custom_target( - xml.underscorify() + '_c', - input: xml, - output: '@BASENAME@-protocol.c', - command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'], + nag_sources = files( + 'labnag.c', + 'pool-buffer.c', ) - nag_sources += custom_target( - xml.underscorify() + '_client_h', - input: xml, - output: '@BASENAME@-client-protocol.h', - command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'], - ) -endforeach -if host_machine.system() in ['freebsd', 'openbsd'] - # For signalfd() - epoll_dep = dependency('epoll-shim') -else - epoll_dep = [] + wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') + + protocols = [ + wl_protocol_dir / 'stable/tablet/tablet-v2.xml', + wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml', + wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', + '../protocols/wlr-layer-shell-unstable-v1.xml', + ] + + foreach xml : protocols + nag_sources += custom_target( + xml.underscorify() + '_c', + input: xml, + output: '@BASENAME@-protocol.c', + command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'], + ) + nag_sources += custom_target( + xml.underscorify() + '_client_h', + input: xml, + output: '@BASENAME@-client-protocol.h', + command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'], + ) + endforeach + + if host_machine.system() in ['freebsd', 'openbsd'] + # For signalfd() + epoll_dep = dependency('epoll-shim') + else + epoll_dep = [] + endif + + executable( + 'labnag', + nag_sources, + dependencies: [ + cairo, + pangocairo, + glib, + wayland_client, + wayland_cursor, + wlroots, + server_protos, + epoll_dep, + xkbcommon, + ], + include_directories: [labwc_inc], + install: true, + ) endif -executable( - 'labnag', - nag_sources, - dependencies: [ - cairo, - pangocairo, - glib, - wayland_client, - wayland_cursor, - wlroots, - server_protos, - epoll_dep, - xkbcommon, - ], - include_directories: [labwc_inc], - install: true, -) - clients = files('lab-sensible-terminal') install_data(clients, install_dir: get_option('bindir')) diff --git a/data/labwc-session.target b/data/labwc-session.target new file mode 100644 index 00000000..e71f20e5 --- /dev/null +++ b/data/labwc-session.target @@ -0,0 +1,6 @@ +[Unit] +Description=labwc session +Documentation=man:labwc(1) man:systemd.special(7) +BindsTo=graphical-session.target +Wants=graphical-session-pre.target +After=graphical-session-pre.target diff --git a/docs/autostart b/docs/autostart index b045ed82..18adb23b 100644 --- a/docs/autostart +++ b/docs/autostart @@ -1,10 +1,18 @@ # Example autostart file +# When running under systemd, uncomment the systemctl line below to pull in +# graphical-session.target via labwc-session.target. This lets systemd user +# services declaring WantedBy=graphical-session.target (panels, portals, +# notification daemons, etc.) start in sync with the labwc session. Enable +# individual services with: systemctl --user enable +# +# systemctl --user --no-block start labwc-session.target + # Set background color. swaybg -c '#113344' >/dev/null 2>&1 & # Configure output directives such as mode, position, scale and transform. -# Use wlr-randr to get your output names +# Use wlr-randr to get your output names. # Example ~/.config/kanshi/config below: # profile { # output HDMI-A-1 position 1366,0 diff --git a/docs/labnag.1.scd b/docs/labnag.1.scd index 6d4b959f..bf237d00 100644 --- a/docs/labnag.1.scd +++ b/docs/labnag.1.scd @@ -95,6 +95,12 @@ _labnag_ [options...] *--details-border-size* Set the thickness for the details border. +*--details-border-color* + Set the color of the details border. + +*--details-margin* + Set the margin for the details. + *--button-border-size* Set the thickness for the button border. diff --git a/docs/labwc-actions.5.scd b/docs/labwc-actions.5.scd index fe468cd1..2677e348 100644 --- a/docs/labwc-actions.5.scd +++ b/docs/labwc-actions.5.scd @@ -16,7 +16,7 @@ Actions are used in menus and keyboard/mouse bindings. SIGTERM signal. ** - Execute command. Note that in the interest of backward compatibility, + Execute command. Note that in the interest of backward compatibility, labwc supports as an alternative to even though openbox documentation states that it is deprecated. Note: Tilde (~) is expanded in the command before passing to execvp(). @@ -126,30 +126,34 @@ Actions are used in menus and keyboard/mouse bindings. position if it had been maximized or tiled to a direction or region. **++ -** +**++ +**++ +**++ Cycle focus to next/previous window, respectively. Default keybinds for NextWindow and PreviousWindow are Alt-Tab and Shift-Alt-Tab. While cycling through windows, the arrow keys move the selected window forwards/backwards and the escape key halts the cycling. + NextWindowImmediate and PreviousWindowImmediate skip the Window Switcher + and OSD, useful for binding to keys without modifiers. + *workspace* [all|current] - This determines whether to cycle through windows on all workspaces or the - current workspace. Default is "current". + This determines whether to cycle through windows on all workspaces or + the current workspace. Default is "current". *output* [all|focused|cursor] - This determines whether to cycle through windows on all outputs, the focused - output, or the output under the cursor. Default is "all". + This determines whether to cycle through windows on all outputs, the + focused output, or the output under the cursor. Default is "all". *identifier* [all|current] - This determines whether to cycle through all windows or only windows of the - same application as the currently focused window. Default is "all". + This determines whether to cycle through all windows or only windows of + the same application as the currently focused window. Default is "all". ** Re-load configuration and theme files. -** - +** Show a menu. ``` @@ -295,7 +299,7 @@ Actions are used in menus and keyboard/mouse bindings. (if one exists). *wrap* [yes|no] When using the direction attribute, wrap around from - right-to-left or top-to-bottom, and vice versa. Default no. + right-to-left or top-to-bottom, and vice versa. Default is no. ** Resizes active window size to width and height of the output when the @@ -309,10 +313,10 @@ Actions are used in menus and keyboard/mouse bindings. workspace or its index (starting at 1) as configured in rc.xml. *wrap* [yes|no] Wrap around from last desktop to first, and vice - versa. Default yes. + versa. Default is yes. *toggle* [yes|no] Toggle to “last” if already on the workspace that - would be the actual destination. Default no. + would be the actual destination. Default is no. ** Send active window to workspace. @@ -320,10 +324,11 @@ Actions are used in menus and keyboard/mouse bindings. *to* The workspace to send the window to. Supported values are the same as for GoToDesktop. - *follow* [yes|no] Also switch to the specified workspace. Default yes. + *follow* [yes|no] Also switch to the specified workspace. + Default is yes. *wrap* [yes|no] Wrap around from last desktop to first, and vice - versa. Default yes. + versa. Default is yes. ** Add virtual output (headless backend). @@ -341,11 +346,11 @@ Actions are used in menus and keyboard/mouse bindings. ``` - - + + - + ``` @@ -366,7 +371,7 @@ Actions are used in menus and keyboard/mouse bindings. *output_name* The name of virtual output. If not supplied, will remove the last virtual output added. -** +** Reposition the window according to the desired placement policy. *policy* [automatic|cursor|center|cascade] Use the specified policy, @@ -424,6 +429,13 @@ Actions are used in menus and keyboard/mouse bindings. Toggle the screen magnifier on or off at the last magnification level used. +** + Minimize all windows in the current workspace so that the desktop + becomes visible. On calling the action again the hidden windows are + unminimized, provided that - since the initial `ShowDesktop` - (a) no + windows have been unminimized; (b) workspaces have not been switched; + and (c) no new applications have been started. + **++ ** Increase or decrease the magnification level for the screen magnifier. @@ -436,6 +448,10 @@ Actions are used in menus and keyboard/mouse bindings. If used as the only action for a binding: clear an earlier defined binding. +** + Toggle visibility of key-state on-screen display (OSD). Note: This is + for debugging purposes only. + # CONDITIONAL ACTIONS Actions that execute other actions. Used in keyboard/mouse bindings. @@ -448,10 +464,10 @@ Actions that execute other actions. Used in keyboard/mouse bindings. ``` - - - - + + + + ``` @@ -503,7 +519,7 @@ Actions that execute other actions. Used in keyboard/mouse bindings. "right-occupied" directions will not wrap. *tiled* [up|right|down|left|up-left|up-right|down-left|down-right|center|any] - Whether the client is tiled (snapped) along the the + Whether the client is tiled (snapped) along the indicated screen edge. *tiled_region* @@ -533,9 +549,9 @@ Actions that execute other actions. Used in keyboard/mouse bindings. ``` - + - + diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index e72354a3..6a16b89c 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -25,7 +25,7 @@ The XDG Base Directory Specification does not specify whether or not programs should (a) allow the first-identified configuration file to supersede any others, or (b) define rules for merging the information from more than one file. -By default, labwc uses option (a), reading only the first file identified. With +By default, labwc uses option (a), reading only the first file identified. With the --merge-config option, the search order is reversed, but every configuration file encountered is processed in turn. Thus, user-specific files will augment system-wide configurations, with conflicts favoring the user-specific @@ -175,6 +175,7 @@ this is for compatibility with Openbox. no no yes + no no no yes @@ -240,6 +241,12 @@ this is for compatibility with Openbox. 'pkill kanshi ; wlopm --off \*' resume 'kanshi & wlopm --on \*' ``` +** [yes|no] + Automatically enable HDR support on outputs when configuring them, + where supported by the particular output device and display. Default + is no. Additionally requires the Vulkan backend. Can be set + via `WLR_RENDERER=vulkan` in `~/.config/labwc/environment`. + ** [yes|no] Try to re-use the existing output mode (resolution / refresh rate). This may prevent unnecessary screenblank delays when starting labwc @@ -493,6 +500,13 @@ this is for compatibility with Openbox. ** [yes|no] Raise window to top when focused. Default is no. +** [milliseconds] + When raiseOnFocus is enabled, delay the actual raise by this many + milliseconds. Default is 0 (raise immediately). A subsequent focus + change before the timer elapses restarts or cancels the pending raise. + Useful together with followMouse to avoid brief passes of the cursor + stacking up z-order changes. + ## WINDOW SNAPPING Windows may be "snapped" to an edge or user-defined region of an output when @@ -512,7 +526,8 @@ extending outward from the snapped edge. ** and **, and 50 for **. ** [yes|no] - Show an overlay when snapping a window to an output edge. Default is yes. + Show an overlay when snapping a window to an output edge. + Default is yes. **++ ** @@ -581,7 +596,7 @@ extending outward from the snapped edge. A setting of 0 disables the OSD. Default is 1000 ms. ** - Set the prefix to use when using "number" above. Default is "Workspace" + Set the prefix to use when using "number" above. Default is "Workspace". ## THEME @@ -735,7 +750,7 @@ generate gesture events, like swipe and pinch. There are some related settings (e.g. *threeFingerDrag* and *twoFingerScroll*) in the ** section. In the Wayland Compositor domain, events associated with touchscreens are -sometimes simply referred to as *touch* events. Touchscreens can be configured +sometimes simply referred to as *touch* events. Touchscreens can be configured in both the ** and ** sections. Note that touchscreen gestures are not interpreted by libinput, nor labwc. Any touch point is passed to the client (application) for any interpretation of gestures. @@ -760,7 +775,8 @@ References: Stores the keyboard layout either globally or per window and restores it when switching back to the window. Default is global. -** +** Define a *key* binding in the format *modifier-key*, where supported modifiers are: - S (shift) @@ -808,6 +824,12 @@ References: *allowWhenLocked* [yes|no] Make this keybind work even if the screen is locked. Default is no. + *overrideInhibition* [yes|no] + Make this keybind work even if the view inhibits keybinds. + This can be used to prevent W-Tab and similar keybinds from being + delivered to Virtual Machines, VNC clients or nested compositors. + Default is no. + *onRelease* [yes|no] When yes, fires the keybind action when the key or key combination is released, rather than first pressed. This is useful to @@ -820,7 +842,7 @@ References: ``` - + ``` @@ -841,11 +863,12 @@ References: W-Return - lab-sensible-terminal A-F4 - close window W-a - toggle maximize + W-d - toggle show-desktop W- - resize window to fill half or quarter of the output A-Space - show window menu ``` - Audio and MonBrightness keys are also bound to amixer and + Audio and MonBrightness keys are also bound to pactl and brightnessctl, respectively. ** @@ -880,7 +903,7 @@ input-devices by the Wayland protocol. - Shade: A button that, by default, toggles window shading. - AllDesktops: A button that, by default, toggles omnipresence of a window. - - Close: A button that, by default, closses a window. + - Close: A button that, by default, closes a window. - Border: The window's border including Top...BRCorner below. - Top: The top edge of the window's border. - Bottom: The bottom edge of the window's border. @@ -954,10 +977,10 @@ input-devices by the Wayland protocol. ``` - + - - + + ``` @@ -965,7 +988,7 @@ input-devices by the Wayland protocol. ** Load default mousebinds. This is an addition to the openbox specification and provides a way to keep config files simpler whilst - allowing user specific binds. Note that if no rc.xml is found, or if no + allowing user specific binds. Note that if no rc.xml is found, or if no entries exist, the same default mousebinds will be loaded even if the element is not provided. @@ -977,7 +1000,7 @@ Note: To rotate touch events with output rotation, use the libinput *calibrationMatrix* setting. ``` - + ``` ** @@ -1090,7 +1113,8 @@ Note: To rotate touch events with output rotation, use the libinput ## TABLET TOOL ``` - + ``` ** [absolute|relative] @@ -1105,6 +1129,16 @@ Note: To rotate touch events with output rotation, use the libinput speed, using a value greater than 1.0 increases the speed of the cursor. The default is "1.0". +** +** + The pressure range of a tablet tool can be controlled by adjusting + *minPressure* and *maxPressure*. Setting the minimum pressure to + a value greater than zero requires more pressure for the tip + threshold, setting the maximum pressure to a value less than 1.0 + requires less pressure for the user before the maximum is reached. + The default is 0 for the minimum pressure and 1.0 for the maximum + pressure. + ## LIBINPUT ``` @@ -1123,6 +1157,7 @@ Note: To rotate touch events with output rotation, use the libinput + 1.0 @@ -1227,19 +1262,26 @@ Note: To rotate touch events with output rotation, use the libinput The default method depends on the touchpad hardware. -** [none|twofinger|edge] - Configure the method by which physical movements on a touchpad are - mapped to scroll events. +** [none|twofinger|edge|onbutton] + Configure the method by which physical movements are mapped to scroll + events. The scroll methods available are: - *twofinger* - Scroll by two fingers being placed on the surface of the touchpad, then moving those fingers vertically or horizontally. - *edge* - Scroll by moving a single finger along the right edge (vertical scroll) or bottom edge (horizontal scroll). + - *onbutton* - Scroll by pressing a button. - *none* - No scroll events will be produced. The default method depends on the touchpad hardware. +** [button] + Set the button used for the *onbutton* scroll method. + + *button* is the decimal form of a value + from `linux/input-event-codes.h`. + ** [yes|no|disabledOnExternalMouse] Optionally enable or disable sending any device events. @@ -1287,7 +1329,7 @@ defined as shown below. - + @@ -1354,6 +1396,13 @@ situation. *ignoreConfigureRequest* prevents a X11 window to position and size itself. +** [yes|no|default] + *allowAlwaysOnTop* allows a X11 window to control its always-on-top + state ('above' in X11 terms). + + Note: X11 window always-on-top requests are disallowed by default. + This window rule offers a means of allowing it. + ** [yes|no|default] *fixedPosition* disallows interactive move/resize and prevents re-positioning in response to changes in reserved output space, which @@ -1427,11 +1476,73 @@ situation. Whether to apply a bilinear filter to the magnified image, or just to use nearest-neighbour. Default is true - bilinear filtered. +## PRIVILEGED INTERFACES + +Labwc supports a small set of privileged wayland interfaces. All of these +interfaces are enabled by default for applications unless they are running +via a sandbox environment supporting the security-context-v1 protocol. + +Security conscious users are suggested to use a sandbox framework to run +potentially untrusted applications as it additionally limits access to the +filesystem (including labwc configuration) and other services like dbus. + +In addition to that, privileged protocols can be restricted for non-sandboxed +clients by defining a `` block: + +``` + + zwlr_layer_shell_v1 + zwlr_virtual_pointer_manager_v1 + +``` + +** + Name of the interface that should be allowed. + +This is the full list of interfaces that can be controlled with this mechanism: + +- `wp_drm_lease_device_v1` +- `zwlr_gamma_control_manager_v1` +- `zwlr_output_manager_v1` +- `zwlr_output_power_manager_v1` +- `zwp_input_method_manager_v2` +- `zwlr_virtual_pointer_manager_v1` +- `zwp_virtual_keyboard_manager_v1` +- `zwlr_export_dmabuf_manager_v1` +- `zwlr_screencopy_manager_v1` +- `ext_data_control_manager_v1` +- `zwlr_data_control_manager_v1` +- `wp_security_context_manager_v1` +- `ext_idle_notifier_v1` +- `zwlr_foreign_toplevel_manager_v1` +- `ext_foreign_toplevel_list_v1` +- `ext_session_lock_manager_v1` +- `zwlr_layer_shell_v1` +- `ext_workspace_manager_v1` +- `ext_image_copy_capture_manager_v1` +- `ext_output_image_capture_source_manager_v1` + ## ENVIRONMENT VARIABLES -*XCURSOR_THEME* and *XCURSOR_SIZE* are supported to set cursor theme -and size respectively. The default size is 24. System cursor themes can -typically be found with a command such as: +*XCURSOR_PATH* + Specify a colon-separated list of paths to look for mouse cursors in. + Default is + ~/.local/share/icons: + ~/.icons: + /usr/share/icons: + /usr/share/pixmaps: + ~/.cursors: + /usr/share/cursors/xorg-x11: + /usr/X11R6/lib/X11/icons: + +*XCURSOR_SIZE* + Specify an alternative mouse cursor size in pixels. Requires + XCURSOR_THEME to be set also. Default is 24. + +*XCURSOR_THEME* + Specify a mouse cursor theme within XCURSOR_PATH. + +System cursor themes can typically be found with a command such as: ``` find /usr/share/icons/ -type d -name "cursors" diff --git a/docs/labwc-menu.5.scd b/docs/labwc-menu.5.scd index cf7ccbc7..e2c40d84 100644 --- a/docs/labwc-menu.5.scd +++ b/docs/labwc-menu.5.scd @@ -12,7 +12,7 @@ Static menus are built based on the menu.xml file located at # SYNTAX The menu file must be entirely enclosed within and - tags. Inside these tags, menus are specified as follows: + tags. Inside these tags, menus are specified as follows: ``` @@ -111,7 +111,7 @@ Pipe menus are menus generated dynamically based on output of scripts or binaries. They are so-called because the output of the executable is piped to the labwc menu. -For any ** entry in menu.xml, the +For any ** entry in menu.xml, the COMMAND will be executed the first time the item is selected (for example by cursor or keyboard input). The XML output of the command will be parsed and shown as a submenu. The content of pipemenus is cached until the whole menu @@ -124,7 +124,7 @@ menus, for example: ``` - + ``` @@ -144,10 +144,25 @@ obmenu-generator with the menu generator of your choice): ``` - + ``` +# ACCELERATORS / MNEMONICS + +Menu accelerators are one-letter mnemonics to quickly select/exec items from +the current menu. For each menu item, the accelerator is defined as the first +character of the item label, converted to lowercase. A different accelerator +can be explicitly defined in menu.xml with the special '\_' character before the +target letter. Accelerators can be any unicode character and are not limited to +ASCII. A usual underscore can be shown by duplicating it. + +If the menu only contains a single instance of the pressed accelerator the item +will be executed directly. Otherwise, all matching items are cycled through. + +Example: +The accelerator for an item with the label "e_Macs" is 'm'. + # LOCALISATION Available localisation for the default "client-menu" is only shown if no diff --git a/docs/labwc-theme.5.scd b/docs/labwc-theme.5.scd index 5f99cae7..dce48bdf 100644 --- a/docs/labwc-theme.5.scd +++ b/docs/labwc-theme.5.scd @@ -135,7 +135,7 @@ window.*.title.bg.colorTo.splitTo: #557485 *window.active.title.bg* Texture for the focused window's titlebar. See texture section above. - Default is *Solid* + Default is *Solid*. *window.active.title.bg.color* Background color for the focused window's titlebar. See texture section @@ -144,7 +144,7 @@ window.*.title.bg.colorTo.splitTo: #557485 *window.inactive.title.bg* Texture for non-focused windows' titlebars. See texture section above. - Default is *Solid* + Default is *Solid*. *window.inactive.title.bg.color* Background color for non-focused windows' titlebars. See texture section @@ -470,7 +470,7 @@ all are supported. Width of magnifier window border in pixels. Default is 1. *magnifier.border.color* - Color of the magnfier window border. Default is #ff0000 (red). + Color of the magnifier window border. Default is #ff0000 (red). # BUTTONS diff --git a/docs/labwc.1.scd b/docs/labwc.1.scd index 4840c0fa..56dfe775 100644 --- a/docs/labwc.1.scd +++ b/docs/labwc.1.scd @@ -66,10 +66,18 @@ the `--exit` and `--reconfigure` options use. Manager, or the Window Manager can be launched independently first. On Wayland, the Compositor is both Display Server and Window Manager, so the described session management mechanisms do not work because the - Compositor needs to be running before the session can function. As some + Compositor needs to be running before the session can function. As some session clients support both X11 and Wayland, this command line option avoids re-writes and fragmentation. +*-t, --title* + Set the window title for labwc to use when it is running in a window + (i.e. nested in a compositor). is a format string to be used as + the window title, replacing `%o` with the name of the output + region. This is useful when simulating multiple screens, such as with + running labwc with the environment variable `WLR_WL_OUTPUTS=2`. In this + case, `%o` will be unique per simulated screen. + *-v, --version* Show the version number and quit @@ -118,6 +126,25 @@ this is accomplished by setting the session variables to empty strings. For systemd, the command `systemctl --user unset-environment` will be invoked to actually remove the variables from the activation environment. +A systemd user unit named `labwc-session.target` is also shipped alongside +the compositor for users who want to integrate labwc with systemd. It binds +to the standard `graphical-session.target`, so systemd user services can +start and stop in sync with the labwc session when they declare a WantedBy +or PartOf relationship to that target. Labwc does not activate the target +itself; users opt in by adding lines like the following to their +*autostart* and *shutdown* files: + +``` +systemctl --user --no-block start labwc-session.target +systemctl --user stop graphical-session.target +``` + +The example *autostart* and *shutdown* files shipped with labwc include +these commented out. To have a user service automatically started with +the session, enable it so the corresponding symlink under the +graphical-session.target.wants directory exists, for example by running +"systemctl --user enable dms.service". + # ENVIRONMENT VARIABLES Set the environment variables listed below to enable specific debug options. @@ -141,7 +168,7 @@ example: *LABWC_DEBUG_FOO=1 labwc*. *LABWC_DEBUG_KEY_STATE* Enable logging of press and release events for bound keys (generally - key-combinations like *Ctrl-Alt-t*) + key-combinations like *Ctrl-Alt-t*). # SEE ALSO diff --git a/docs/menu.xml b/docs/menu.xml index 4b4d5dda..3344161a 100644 --- a/docs/menu.xml +++ b/docs/menu.xml @@ -23,7 +23,7 @@ Any menu with the id "workspaces" will be hidden if there is only a single workspace available. --> - + + 0 @@ -276,6 +279,9 @@ + + + @@ -291,19 +297,19 @@ - - + + - - + + - - + + - + - + @@ -573,8 +579,11 @@ *relativeMotionSensitivity* controls the speed of the cursor. Using a value lower than 1.0 decreases the speed, using a value greater than 1.0 increases the speed of the cursor. + The pressure range of a tablet tool can be controlled by adjusting + *minPressure* and *maxPressure*. --> - + + 1.0 @@ -635,10 +645,10 @@ # string and '?' matches any single character. - - - - + + + + # Example below for `lxqt-panel` and `pcmanfm-qt \-\-desktop` @@ -649,18 +659,18 @@ yes - + yes yes yes - + - + --> diff --git a/docs/shutdown b/docs/shutdown index feed6508..a036ff53 100644 --- a/docs/shutdown +++ b/docs/shutdown @@ -3,3 +3,11 @@ # This file is executed as a shell script when labwc is preparing to terminate # itself. # For further details see labwc-config(5). + +# When running under systemd, uncomment the systemctl line below to tear down +# graphical-session.target (which cascades to labwc-session.target via +# BindsTo, and to any service declaring PartOf=graphical-session.target). +# Running synchronously here ensures those services are stopped before the +# Wayland socket goes away, avoiding "Broken pipe" failures on teardown. +# +# systemctl --user stop graphical-session.target diff --git a/include/action.h b/include/action.h index 7692e383..b09aa35c 100644 --- a/include/action.h +++ b/include/action.h @@ -47,8 +47,7 @@ bool actions_contain_toggle_keybinds(struct wl_list *action_list); * direction of resize or the position of the window menu button for ShowMenu * action. */ -void actions_run(struct view *activator, struct server *server, - struct wl_list *actions, struct cursor_context *ctx); +void actions_run(struct view *activator, struct wl_list *actions, struct cursor_context *ctx); void action_prompts_destroy(void); bool action_check_prompt_result(pid_t pid, int exit_code); diff --git a/include/common/font.h b/include/common/font.h index a7a5ba55..22be2ae8 100644 --- a/include/common/font.h +++ b/include/common/font.h @@ -4,6 +4,7 @@ #include #include +#include struct lab_data_buffer; @@ -43,10 +44,11 @@ void font_get_buffer_size(int max_width, const char *text, struct font *font, * @font: font description * @color: foreground color in rgba format * @bg_pattern: background pattern + * @use_markup: flag to render pango markup */ void font_buffer_create(struct lab_data_buffer **buffer, int max_width, int height, const char *text, struct font *font, const float *color, - cairo_pattern_t *bg_pattern, double scale); + cairo_pattern_t *bg_pattern, double scale, bool use_markup); /** * font_finish - free some font related resources diff --git a/include/common/macros.h b/include/common/macros.h index 25226261..f3ceee82 100644 --- a/include/common/macros.h +++ b/include/common/macros.h @@ -62,7 +62,11 @@ #define BOUNDED_INT(a) ((a) < INT_MAX && (a) > INT_MIN) #endif -#define LAB_WLR_VERSION_AT_LEAST(major, minor, micro) \ - (WLR_VERSION_NUM >= (((major) << 16) | ((minor) << 8) | (micro))) +#define _LAB_CALC_WLR_VERSION_NUM(major, minor, micro) (((major) << 16) | ((minor) << 8) | (micro)) + +#define LAB_WLR_VERSION_AT_LEAST(major, minor, micro) ( \ + server.wlr_version >= _LAB_CALC_WLR_VERSION_NUM(major, minor, micro)) + +#define LAB_WLR_VERSION_LOWER(major, minor, micro) (!LAB_WLR_VERSION_AT_LEAST(major, minor, micro)) #endif /* LABWC_MACROS_H */ diff --git a/include/common/scene-helpers.h b/include/common/scene-helpers.h index 453052fb..3b9b9521 100644 --- a/include/common/scene-helpers.h +++ b/include/common/scene-helpers.h @@ -4,6 +4,7 @@ #include +struct wlr_buffer; struct wlr_scene_node; struct wlr_surface; struct wlr_scene_output; @@ -11,6 +12,13 @@ struct wlr_output_state; struct wlr_surface *lab_wlr_surface_from_node(struct wlr_scene_node *node); +/* variants of wlr_scene_*_create() functions that don't return NULL */ +struct wlr_scene_tree *lab_wlr_scene_tree_create(struct wlr_scene_tree *parent); +struct wlr_scene_rect *lab_wlr_scene_rect_create(struct wlr_scene_tree *parent, + int width, int height, const float color[static 4]); +struct wlr_scene_buffer *lab_wlr_scene_buffer_create( + struct wlr_scene_tree *parent, struct wlr_buffer *buffer); + /** * lab_get_prev_node - return previous (sibling) node * @node: node to find the previous node from diff --git a/include/common/spawn.h b/include/common/spawn.h index 43123269..4df5ed0b 100644 --- a/include/common/spawn.h +++ b/include/common/spawn.h @@ -16,6 +16,12 @@ pid_t spawn_primary_client(const char *command); */ void spawn_async_no_shell(char const *command); +/** + * spawn_sync_no_shell - execute synchronously + * @command: command to be executed + */ +void spawn_sync_no_shell(char const *command); + /** * spawn_piped - execute asynchronously * @command: command to be executed diff --git a/include/config/default-bindings.h b/include/config/default-bindings.h index 9d42d237..d1a96562 100644 --- a/include/config/default-bindings.h +++ b/include/config/default-bindings.h @@ -28,6 +28,9 @@ static struct key_combos { }, { .binding = "W-a", .action = "ToggleMaximize", + }, { + .binding = "W-d", + .action = "ToggleShowDesktop", }, { .binding = "W-Left", .action = "SnapToEdge", @@ -84,35 +87,35 @@ static struct key_combos { .value = "no", }, }, { - .binding = "XF86_AudioLowerVolume", + .binding = "XF86AudioLowerVolume", .action = "Execute", .attributes[0] = { .name = "command", - .value = "amixer sset Master 5%-", + .value = "pactl set-sink-volume @DEFAULT_SINK@ -5%", }, }, { - .binding = "XF86_AudioRaiseVolume", + .binding = "XF86AudioRaiseVolume", .action = "Execute", .attributes[0] = { .name = "command", - .value = "amixer sset Master 5%+", + .value = "pactl set-sink-volume @DEFAULT_SINK@ +5%", }, }, { - .binding = "XF86_AudioMute", + .binding = "XF86AudioMute", .action = "Execute", .attributes[0] = { .name = "command", - .value = "amixer sset Master toggle", + .value = "pactl set-sink-mute @DEFAULT_SINK@ toggle", }, }, { - .binding = "XF86_MonBrightnessUp", + .binding = "XF86MonBrightnessUp", .action = "Execute", .attributes[0] = { .name = "command", .value = "brightnessctl set +10%", }, }, { - .binding = "XF86_MonBrightnessDown", + .binding = "XF86MonBrightnessDown", .action = "Execute", .attributes[0] = { .name = "command", @@ -141,14 +144,14 @@ static struct key_combos { * * * - * - * - * + * + * + * * * * * - * + * * * * diff --git a/include/config/keybind.h b/include/config/keybind.h index bef3e189..d02de387 100644 --- a/include/config/keybind.h +++ b/include/config/keybind.h @@ -23,6 +23,7 @@ struct keybind { struct wl_list actions; /* struct action.link */ struct wl_list link; /* struct rcxml.keybinds */ bool on_release; + bool override_inhibition; }; /** @@ -45,5 +46,5 @@ bool keybind_the_same(struct keybind *a, struct keybind *b); bool keybind_contains_keycode(struct keybind *keybind, xkb_keycode_t keycode); bool keybind_contains_keysym(struct keybind *keybind, xkb_keysym_t keysym); -void keybind_update_keycodes(struct server *server); +void keybind_update_keycodes(void); #endif /* LABWC_KEYBIND_H */ diff --git a/include/config/libinput.h b/include/config/libinput.h index 80b3fc10..077bc011 100644 --- a/include/config/libinput.h +++ b/include/config/libinput.h @@ -31,6 +31,7 @@ struct libinput_category { int dwt; /* -1 or libinput_config_dwt_state */ int click_method; /* -1 or libinput_config_click_method */ int scroll_method; /* -1 or libinput_config_scroll_method */ + int scroll_button; /* -1 or a button from linux/input_event_codes.h */ int send_events_mode; /* -1 or libinput_config_send_events_mode */ bool have_calibration_matrix; double scroll_factor; diff --git a/include/config/rcxml.h b/include/config/rcxml.h index a6f1a7d7..9c2183a8 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -36,6 +36,12 @@ enum tearing_mode { LAB_TEARING_FULLSCREEN_FORCED, }; +enum render_bit_depth { + LAB_RENDER_BIT_DEPTH_DEFAULT = 0, + LAB_RENDER_BIT_DEPTH_8, + LAB_RENDER_BIT_DEPTH_10, +}; + enum tiling_events_mode { LAB_TILING_EVENTS_NEVER = 0, LAB_TILING_EVENTS_REGION = 1 << 0, @@ -74,8 +80,10 @@ struct rcxml { int gap; enum adaptive_sync_mode adaptive_sync; enum tearing_mode allow_tearing; + enum render_bit_depth target_render_depth; bool auto_enable_outputs; bool reuse_output_mode; + uint32_t allowed_interfaces; bool xwayland_persistence; bool primary_selection; char *prompt_command; @@ -89,6 +97,7 @@ struct rcxml { bool focus_follow_mouse; bool focus_follow_mouse_requires_movement; bool raise_on_focus; + uint32_t raise_on_focus_delay_ms; /* theme */ char *theme_name; @@ -144,6 +153,8 @@ struct rcxml { struct tablet_tool_config { enum lab_motion motion; double relative_motion_sensitivity; + double min_pressure; + double max_pressure; } tablet_tool; /* libinput */ @@ -211,6 +222,7 @@ struct rcxml { bool mag_filter; }; +/* defined in main.c */ extern struct rcxml rc; void rcxml_read(const char *filename); @@ -222,4 +234,6 @@ void rcxml_finish(void); */ void append_parsed_actions(xmlNode *node, struct wl_list *list); +uint32_t parse_privileged_interface(const char *name); + #endif /* LABWC_RCXML_H */ diff --git a/include/config/session.h b/include/config/session.h index 61a95cf9..dec57feb 100644 --- a/include/config/session.h +++ b/include/config/session.h @@ -21,12 +21,12 @@ void session_environment_init(void); * session_autostart_init - run autostart file as shell script * Note: Same as `sh ~/.config/labwc/autostart` (or equivalent XDG config dir) */ -void session_autostart_init(struct server *server); +void session_autostart_init(void); /** * session_shutdown - run session shutdown file as shell script * Note: Same as `sh ~/.config/labwc/shutdown` (or equivalent XDG config dir) */ -void session_shutdown(struct server *server); +void session_shutdown(void); #endif /* LABWC_SESSION_H */ diff --git a/include/cycle.h b/include/cycle.h index c6e42810..440b938f 100644 --- a/include/cycle.h +++ b/include/cycle.h @@ -91,20 +91,24 @@ struct server; struct wlr_scene_node; /* Begin window switcher */ -void cycle_begin(struct server *server, enum lab_cycle_dir direction, +void cycle_begin(enum lab_cycle_dir direction, struct cycle_filter filter); /* Cycle the selected view in the window switcher */ -void cycle_step(struct server *server, enum lab_cycle_dir direction); +void cycle_step(enum lab_cycle_dir direction); /* Closes the OSD */ -void cycle_finish(struct server *server, bool switch_focus); +void cycle_finish(bool switch_focus); /* Re-initialize the window switcher */ -void cycle_reinitialize(struct server *server); +void cycle_reinitialize(void); + +/* Immediately cycle to next/previous window */ +void cycle_immediate(enum lab_cycle_dir direction, + struct cycle_filter filter); /* Focus the clicked window and close OSD */ -void cycle_on_cursor_release(struct server *server, struct wlr_scene_node *node); +void cycle_on_cursor_release(struct wlr_scene_node *node); /* Used by osd.c internally to render window switcher fields */ void cycle_osd_field_get_content(struct cycle_osd_field *field, @@ -133,7 +137,7 @@ struct cycle_osd_impl { */ void (*init)(struct cycle_osd_output *osd_output); /* - * Update the OSD to highlight server->cycle.selected_view. + * Update the OSD to highlight server.cycle.selected_view. */ void (*update)(struct cycle_osd_output *osd_output); }; @@ -157,7 +161,7 @@ void cycle_osd_scroll_init(struct cycle_osd_output *osd_output, int nr_cols, int nr_rows, int nr_visible_rows, float *border_color, float *bg_color); -/* Scroll the OSD to show server->cycle.selected_view if needed */ +/* Scroll the OSD to show server.cycle.selected_view if needed */ void cycle_osd_scroll_update(struct cycle_osd_output *osd_output); extern struct cycle_osd_impl cycle_osd_classic_impl; diff --git a/include/debug.h b/include/debug.h index ad128ee6..6ca96ee4 100644 --- a/include/debug.h +++ b/include/debug.h @@ -4,6 +4,6 @@ struct server; -void debug_dump_scene(struct server *server); +void debug_dump_scene(void); #endif /* LABWC_DEBUG_H */ diff --git a/include/decorations.h b/include/decorations.h index 4d23c14f..20ea3c6a 100644 --- a/include/decorations.h +++ b/include/decorations.h @@ -6,13 +6,13 @@ struct server; struct view; struct wlr_surface; -void kde_server_decoration_init(struct server *server); -void xdg_server_decoration_init(struct server *server); +void kde_server_decoration_init(void); +void xdg_server_decoration_init(void); void kde_server_decoration_update_default(void); void kde_server_decoration_set_view(struct view *view, struct wlr_surface *surface); -void kde_server_decoration_finish(struct server *server); -void xdg_server_decoration_finish(struct server *server); +void kde_server_decoration_finish(void); +void xdg_server_decoration_finish(void); #endif /* LABWC_DECORATIONS_H */ diff --git a/include/desktop-entry.h b/include/desktop-entry.h index f0d93e3a..4321cbd3 100644 --- a/include/desktop-entry.h +++ b/include/desktop-entry.h @@ -6,14 +6,12 @@ struct server; -void desktop_entry_init(struct server *server); -void desktop_entry_finish(struct server *server); +void desktop_entry_init(void); +void desktop_entry_finish(void); -struct lab_img *desktop_entry_load_icon_from_app_id( - struct server *server, const char *app_id, int size, float scale); +struct lab_img *desktop_entry_load_icon_from_app_id(const char *app_id, int size, float scale); -struct lab_img *desktop_entry_load_icon( - struct server *server, const char *icon_name, int size, float scale); +struct lab_img *desktop_entry_load_icon(const char *icon_name, int size, float scale); /** * desktop_entry_name_lookup() - return the application name @@ -22,8 +20,7 @@ struct lab_img *desktop_entry_load_icon( * The lifetime of the returned value is the same as that * of sfdo_desktop_db (from `struct sfdo.desktop_db`) */ -const char *desktop_entry_name_lookup(struct server *server, - const char *app_id); +const char *desktop_entry_name_lookup(const char *app_id); #endif /* HAVE_LIBSFDO */ #endif /* LABWC_DESKTOP_ENTRY_H */ diff --git a/include/edges.h b/include/edges.h index 439b5a34..922e637b 100644 --- a/include/edges.h +++ b/include/edges.h @@ -117,6 +117,6 @@ void edges_adjust_resize_geom(struct view *view, struct border edges, bool edges_traverse_edge(struct edge current, struct edge target, struct edge edge); -void edges_calculate_visibility(struct server *server, struct view *ignored_view); +void edges_calculate_visibility(struct view *ignored_view); #endif /* LABWC_EDGES_H */ diff --git a/include/idle.h b/include/idle.h index 845fdf41..80371d30 100644 --- a/include/idle.h +++ b/include/idle.h @@ -6,6 +6,6 @@ struct wl_display; struct wlr_seat; void idle_manager_create(struct wl_display *display); -void idle_manager_notify_activity(struct wlr_seat *seat); +void idle_manager_notify_activity(struct wlr_seat *wlr_seat); #endif /* LABWC_IDLE_H */ diff --git a/include/input/cursor.h b/include/input/cursor.h index 12b96aac..3d7aa71a 100644 --- a/include/input/cursor.h +++ b/include/input/cursor.h @@ -62,7 +62,7 @@ struct cursor_context_saved { * * If no node is found at cursor, ctx.type is set to ROOT. */ -struct cursor_context get_cursor_context(struct server *server); +struct cursor_context get_cursor_context(void); /** * cursor_set - set cursor icon @@ -82,7 +82,7 @@ void cursor_context_save(struct cursor_context_saved *saved_ctx, /** * cursor_get_resize_edges - calculate resize edge based on cursor position - * @cursor - the current cursor (usually server->seat.cursor) + * @cursor - the current cursor (usually server.seat.cursor) * @cursor_context - result of get_cursor_context() * * Calculates the resize edge combination that is most appropriate based @@ -92,7 +92,7 @@ void cursor_context_save(struct cursor_context_saved *saved_ctx, * keyboard modifier or when using the Resize action from a keybind. */ enum lab_edge cursor_get_resize_edges(struct wlr_cursor *cursor, - struct cursor_context *ctx); + const struct cursor_context *ctx); /** * cursor_get_from_edge - translate lab_edge enum to lab_cursor enum @@ -113,7 +113,7 @@ enum lab_cursors cursor_get_from_edge(enum lab_edge resize_edges); * or to force an update of the cursor icon by sending an exit and enter * event to an already focused surface. */ -void cursor_update_focus(struct server *server); +void cursor_update_focus(void); /** * cursor_update_image - re-set the labwc cursor image @@ -130,7 +130,7 @@ void cursor_update_image(struct seat *seat); * should be notified. Parameters sx, sy holds the surface coordinates * in that case. */ -bool cursor_process_motion(struct server *server, uint32_t time, double *sx, double *sy); +bool cursor_process_motion(uint32_t time, double *sx, double *sy); /** * Processes cursor button press. The return value indicates if a client diff --git a/include/input/key-state.h b/include/input/key-state.h index d9971b74..81374b6c 100644 --- a/include/input/key-state.h +++ b/include/input/key-state.h @@ -5,12 +5,17 @@ #include #include +struct seat; + /* * All keycodes in these functions are (Linux) libinput evdev scancodes which is * what 'wlr_keyboard' uses (e.g. 'seat->keyboard_group->keyboard->keycodes'). * Note: These keycodes are different to XKB scancodes by a value of 8. */ +void key_state_indicator_update(struct seat *seat); +void key_state_indicator_toggle(void); + /** * key_state_pressed_sent_keycodes - generate array of pressed+sent keys * Note: The array is generated by subtracting any bound keys from _all_ pressed diff --git a/include/labwc.h b/include/labwc.h index 88860de2..87a42198 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -23,8 +23,7 @@ enum input_mode { }; struct seat { - struct wlr_seat *seat; - struct server *server; + struct wlr_seat *wlr_seat; struct wlr_keyboard_group *keyboard_group; struct wl_list touch_points; /* struct touch_point.link */ @@ -149,8 +148,15 @@ struct seat { }; struct server { + uint32_t wlr_version; + struct wl_display *wl_display; struct wl_event_loop *wl_event_loop; /* Can be used for timer events */ + + /* Pending auto-raise timer (used when rc.raise_on_focus_delay_ms > 0) */ + struct view *pending_auto_raise_view; + struct wl_event_source *pending_auto_raise_timer; + struct wlr_renderer *renderer; struct wlr_allocator *allocator; struct wlr_backend *backend; @@ -188,6 +194,13 @@ struct server { struct wlr_xdg_toplevel_icon_manager_v1 *xdg_toplevel_icon_manager; struct wl_listener xdg_toplevel_icon_set_icon; + struct { + struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 *manager; + struct { + struct wl_listener new_request; + } on; + } toplevel_capture; + /* front to back order */ struct wl_list views; uint64_t next_view_creation_id; @@ -244,13 +257,14 @@ struct server { struct wl_list all; /* struct workspace.link */ struct workspace *current; struct workspace *last; - struct lab_cosmic_workspace_manager *cosmic_manager; - struct lab_cosmic_workspace_group *cosmic_group; - struct lab_ext_workspace_manager *ext_manager; - struct lab_ext_workspace_group *ext_group; + struct wlr_ext_workspace_manager_v1 *ext_manager; + struct wlr_ext_workspace_group_handle_v1 *ext_group; struct { struct wl_listener layout_output_added; } on; + struct { + struct wl_listener commit; + } on_ext_manager; } workspaces; struct wl_list outputs; @@ -274,7 +288,6 @@ struct server { struct wl_listener renderer_lost; struct wlr_gamma_control_manager_v1 *gamma_control_manager_v1; - struct wl_listener gamma_control_set_gamma; struct session_lock_manager *session_lock_manager; @@ -303,19 +316,22 @@ struct server { /* Set when in cycle (alt-tab) mode */ struct cycle_state cycle; - struct theme *theme; - struct menu *menu_current; struct wl_list menus; struct sfdo *sfdo; pid_t primary_client_pid; + + char *title_fmt; }; +/* defined in main.c */ +extern struct server server; + void xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup); -void xdg_shell_init(struct server *server); -void xdg_shell_finish(struct server *server); +void xdg_shell_init(void); +void xdg_shell_finish(void); /* * desktop.c routines deal with a collection of views @@ -343,6 +359,13 @@ void xdg_shell_finish(struct server *server); */ void desktop_focus_view(struct view *view, bool raise); +/** + * desktop_cancel_pending_auto_raise() - cancel any pending delayed auto-raise + * (from raiseOnFocusDelay). Called when a view is being destroyed, on config + * reload, or when a new focus change with raise=false supersedes the pending. + */ +void desktop_cancel_pending_auto_raise(void); + /** * desktop_focus_view_or_surface() - like desktop_focus_view() but can * also focus other (e.g. xwayland-unmanaged) surfaces @@ -350,14 +373,14 @@ void desktop_focus_view(struct view *view, bool raise); void desktop_focus_view_or_surface(struct seat *seat, struct view *view, struct wlr_surface *surface, bool raise); -void desktop_arrange_all_views(struct server *server); +void desktop_arrange_all_views(void); void desktop_focus_output(struct output *output); /** * Toggles the (output local) visibility of the layershell top layer * based on the existence of a fullscreen window on the current workspace. */ -void desktop_update_top_layer_visibility(struct server *server); +void desktop_update_top_layer_visibility(void); /** * desktop_focus_topmost_view() - focus the topmost view on the current @@ -367,11 +390,11 @@ void desktop_update_top_layer_visibility(struct server *server); * This function is typically called when the focused view is hidden * (closes, is minimized, etc.) to focus the "next" view underneath. */ -void desktop_focus_topmost_view(struct server *server); +void desktop_focus_topmost_view(void); -void seat_init(struct server *server); -void seat_finish(struct server *server); -void seat_reconfigure(struct server *server); +void seat_init(void); +void seat_finish(void); +void seat_reconfigure(void); void seat_force_focus_surface(struct seat *seat, struct wlr_surface *surface); void seat_focus_surface(struct seat *seat, struct wlr_surface *surface); @@ -403,15 +426,15 @@ void seat_focus_override_end(struct seat *seat, bool restore_focus); /** * interactive_anchor_to_cursor() - repositions the geometry to remain * underneath the cursor when its size changes during interactive move. - * This function also resizes server->grab_box and repositions it to remain - * underneath server->grab_{x,y}. + * This function also resizes server.grab_box and repositions it to remain + * underneath server.grab_{x,y}. * * geo->{width,height} are provided by the caller. * geo->{x,y} are computed by this function. */ -void interactive_anchor_to_cursor(struct server *server, struct wlr_box *geo); +void interactive_anchor_to_cursor(struct wlr_box *geo); -void interactive_set_grab_context(struct cursor_context *ctx); +void interactive_set_grab_context(const struct cursor_context *ctx); void interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges); void interactive_finish(struct view *view); @@ -429,12 +452,12 @@ bool edge_from_cursor(struct seat *seat, struct output **dest_output, void handle_tearing_new_object(struct wl_listener *listener, void *data); -void server_init(struct server *server); -void server_start(struct server *server); -void server_finish(struct server *server); +void server_init(void); +void server_start(void); +void server_finish(void); void create_constraint(struct wl_listener *listener, void *data); -void constrain_cursor(struct server *server, struct wlr_pointer_constraint_v1 +void constrain_cursor(struct wlr_pointer_constraint_v1 *constraint); #endif /* LABWC_H */ diff --git a/include/layers.h b/include/layers.h index 001347c8..8aaf23d3 100644 --- a/include/layers.h +++ b/include/layers.h @@ -12,7 +12,6 @@ struct seat; struct lab_layer_surface { struct wlr_layer_surface_v1 *layer_surface; struct wlr_scene_layer_surface_v1 *scene_layer_surface; - struct server *server; bool mapped; /* true only inside handle_unmap() */ @@ -29,7 +28,6 @@ struct lab_layer_surface { struct lab_layer_popup { struct wlr_xdg_popup *wlr_popup; struct wlr_scene_tree *scene_tree; - struct server *server; bool parent_was_focused; /* To simplify moving popup nodes from the bottom to the top layer */ @@ -41,8 +39,8 @@ struct lab_layer_popup { struct wl_listener reposition; }; -void layers_init(struct server *server); -void layers_finish(struct server *server); +void layers_init(void); +void layers_finish(void); void layers_arrange(struct output *output); void layer_try_set_focus(struct seat *seat, diff --git a/include/magnifier.h b/include/magnifier.h index afe79e22..aebc5721 100644 --- a/include/magnifier.h +++ b/include/magnifier.h @@ -14,8 +14,8 @@ enum magnify_dir { MAGNIFY_DECREASE }; -void magnifier_toggle(struct server *server); -void magnifier_set_scale(struct server *server, enum magnify_dir dir); +void magnifier_toggle(void); +void magnifier_set_scale(enum magnify_dir dir); bool output_wants_magnification(struct output *output); void magnifier_draw(struct output *output, struct wlr_buffer *output_buffer, struct wlr_box *damage); diff --git a/include/menu/menu.h b/include/menu/menu.h index f65ff9a3..3d546efa 100644 --- a/include/menu/menu.h +++ b/include/menu/menu.h @@ -23,9 +23,11 @@ struct menuitem { char *text; char *icon_name; const char *arrow; + uint32_t accelerator; struct menu *parent; struct menu *submenu; bool selectable; + bool use_markup; enum menuitem_type type; int native_width; struct wlr_scene_tree *tree; @@ -49,7 +51,6 @@ struct menu { int height; } size; struct wl_list menuitems; - struct server *server; struct { struct menu *menu; struct menuitem *item; @@ -65,14 +66,27 @@ struct menu { }; /* For keyboard support */ -void menu_item_select_next(struct server *server); -void menu_item_select_previous(struct server *server); -void menu_submenu_enter(struct server *server); -void menu_submenu_leave(struct server *server); -bool menu_call_selected_actions(struct server *server); +void menu_item_select_next(void); +void menu_item_select_previous(void); -void menu_init(struct server *server); -void menu_finish(struct server *server); +/** + * menu_item_select_by_accelerator - selects the next menu item with + * a matching accelerator, starting after the current selection + * + * @accelerator a shortcut to quickly select/open an item, defined in menu.xml + * with an underscore in the item label before the target letter. + * + * Return: a boolean value that represents whether the newly selected item + * needs to be executed. + */ +bool menu_item_select_by_accelerator(uint32_t accelerator); + +void menu_submenu_enter(void); +void menu_submenu_leave(void); +bool menu_call_selected_actions(void); + +void menu_init(void); +void menu_finish(void); void menu_on_view_destroy(struct view *view); /** @@ -80,15 +94,15 @@ void menu_on_view_destroy(struct view *view); * * @id id string defined in menu.xml like "root-menu" */ -struct menu *menu_get_by_id(struct server *server, const char *id); +struct menu *menu_get_by_id(const char *id); /** * menu_open_root - open menu on position (x, y) * - * This function will close server->menu_current, open the - * new menu and assign @menu to server->menu_current. + * This function will close server.menu_current, open the + * new menu and assign @menu to server.menu_current. * - * Additionally, server->input_mode will be set to LAB_INPUT_STATE_MENU. + * Additionally, server.input_mode will be set to LAB_INPUT_STATE_MENU. */ void menu_open_root(struct menu *menu, int x, int y); @@ -101,16 +115,16 @@ void menu_open_root(struct menu *menu, int x, int y); void menu_process_cursor_motion(struct wlr_scene_node *node); /** - * menu_close_root- close root menu + * menu_close_root - close root menu * - * This function will close server->menu_current and set it to NULL. - * Asserts that server->input_mode is set to LAB_INPUT_STATE_MENU. + * This function will close server.menu_current and set it to NULL. + * Asserts that server.input_mode is set to LAB_INPUT_STATE_MENU. * - * Additionally, server->input_mode will be set to LAB_INPUT_STATE_PASSTHROUGH. + * Additionally, server.input_mode will be set to LAB_INPUT_STATE_PASSTHROUGH. */ -void menu_close_root(struct server *server); +void menu_close_root(void); /* menu_reconfigure - reload theme and content */ -void menu_reconfigure(struct server *server); +void menu_reconfigure(void); #endif /* LABWC_MENU_H */ diff --git a/include/output-virtual.h b/include/output-virtual.h index 7822e4f7..33d05e9a 100644 --- a/include/output-virtual.h +++ b/include/output-virtual.h @@ -5,9 +5,9 @@ struct server; struct wlr_output; -void output_virtual_add(struct server *server, const char *output_name, +void output_virtual_add(const char *output_name, struct wlr_output **store_wlr_output); -void output_virtual_remove(struct server *server, const char *output_name); -void output_virtual_update_fallback(struct server *server); +void output_virtual_remove(const char *output_name); +void output_virtual_update_fallback(void); #endif diff --git a/include/output.h b/include/output.h index 2b1e4bcf..c17defa4 100644 --- a/include/output.h +++ b/include/output.h @@ -9,7 +9,6 @@ struct output { struct wl_list link; /* server.outputs */ - struct server *server; struct wlr_output *wlr_output; struct wlr_output_state pending; struct wlr_scene_output *scene_output; @@ -39,19 +38,16 @@ struct output { * disconnected and connected again. */ uint64_t id_bit; - - bool gamma_lut_changed; }; #undef LAB_NR_LAYERS -void output_init(struct server *server); -void output_finish(struct server *server); -struct output *output_from_wlr_output(struct server *server, - struct wlr_output *wlr_output); -struct output *output_from_name(struct server *server, const char *name); -struct output *output_nearest_to(struct server *server, int lx, int ly); -struct output *output_nearest_to_cursor(struct server *server); +void output_init(void); +void output_finish(void); +struct output *output_from_wlr_output(struct wlr_output *wlr_output); +struct output *output_from_name(const char *name); +struct output *output_nearest_to(int lx, int ly); +struct output *output_nearest_to_cursor(void); /** * output_get_adjacent() - get next output, in a given direction, @@ -69,12 +65,15 @@ struct output *output_get_adjacent(struct output *output, bool output_is_usable(struct output *output); void output_update_usable_area(struct output *output); -void output_update_all_usable_areas(struct server *server, bool layout_changed); +void output_update_all_usable_areas(bool layout_changed); bool output_get_tearing_allowance(struct output *output); struct wlr_box output_usable_area_in_layout_coords(struct output *output); void handle_output_power_manager_set_mode(struct wl_listener *listener, void *data); void output_enable_adaptive_sync(struct output *output, bool enabled); +void output_enable_hdr(struct output *output, struct wlr_output_state *os, + bool enabled, bool silent); +void output_state_setup_hdr(struct output *output, bool silent); /** * Notifies whether a fullscreen view is displayed on the given output. diff --git a/include/protocols/cosmic-workspaces-internal.h b/include/protocols/cosmic-workspaces-internal.h deleted file mode 100644 index 50c7e75b..00000000 --- a/include/protocols/cosmic-workspaces-internal.h +++ /dev/null @@ -1,24 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef LABWC_PROTOCOLS_COSMIC_WORKSPACES_INTERNAL_H -#define LABWC_PROTOCOLS_COSMIC_WORKSPACES_INTERNAL_H - -struct lab_cosmic_workspace; -struct lab_cosmic_workspace_group; -struct lab_cosmic_workspace_manager; - -enum pending_change { - /* group events */ - CW_PENDING_WS_CREATE = 1 << 0, - - /* ws events*/ - CW_PENDING_WS_ACTIVATE = 1 << 1, - CW_PENDING_WS_DEACTIVATE = 1 << 2, - CW_PENDING_WS_REMOVE = 1 << 3, -}; - -void cosmic_group_output_send_initial_state(struct lab_cosmic_workspace_group *group, - struct wl_resource *group_resource); - -void cosmic_manager_schedule_done_event(struct lab_cosmic_workspace_manager *manager); - -#endif /* LABWC_PROTOCOLS_COSMIC_WORKSPACES_INTERNAL_H */ diff --git a/include/protocols/cosmic-workspaces.h b/include/protocols/cosmic-workspaces.h deleted file mode 100644 index 8776bfad..00000000 --- a/include/protocols/cosmic-workspaces.h +++ /dev/null @@ -1,94 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef LABWC_PROTOCOLS_COSMIC_WORKSPACES_H -#define LABWC_PROTOCOLS_COSMIC_WORKSPACES_H - -#include -#include - -struct wlr_output; - -struct lab_cosmic_workspace_manager { - struct wl_global *global; - struct wl_list groups; - uint32_t caps; - struct wl_event_source *idle_source; - struct wl_event_loop *event_loop; - - struct { - struct wl_listener display_destroy; - } on; - - struct wl_list resources; -}; - -struct lab_cosmic_workspace_group { - struct lab_cosmic_workspace_manager *manager; - struct wl_list workspaces; - struct wl_array capabilities; - struct { - struct wl_signal create_workspace; - struct wl_signal destroy; - } events; - - struct wl_list link; - struct wl_list outputs; - struct wl_list resources; -}; - -struct lab_cosmic_workspace { - struct lab_cosmic_workspace_group *group; - char *name; - struct wl_array coordinates; - struct wl_array capabilities; - uint32_t state; /* enum lab_cosmic_workspace_state */ - uint32_t state_pending; /* enum lab_cosmic_workspace_state */ - - struct { - struct wl_signal activate; - struct wl_signal deactivate; - struct wl_signal remove; - struct wl_signal destroy; - } events; - - struct wl_list link; - struct wl_list resources; -}; - -enum lab_cosmic_workspace_caps { - CW_CAP_NONE = 0, - CW_CAP_GRP_ALL = 0x000000ff, - CW_CAP_WS_ALL = 0x0000ff00, - - /* group caps */ - CW_CAP_GRP_WS_CREATE = 1 << 0, - - /* workspace caps */ - CW_CAP_WS_ACTIVATE = 1 << 8, - CW_CAP_WS_DEACTIVATE = 1 << 9, - CW_CAP_WS_REMOVE = 1 << 10, -}; - -struct lab_cosmic_workspace_manager *lab_cosmic_workspace_manager_create( - struct wl_display *display, uint32_t caps, uint32_t version); - -struct lab_cosmic_workspace_group *lab_cosmic_workspace_group_create( - struct lab_cosmic_workspace_manager *manager); - -void lab_cosmic_workspace_group_output_enter( - struct lab_cosmic_workspace_group *group, struct wlr_output *output); - -void lab_cosmic_workspace_group_output_leave( - - struct lab_cosmic_workspace_group *group, struct wlr_output *output); -void lab_cosmic_workspace_group_destroy(struct lab_cosmic_workspace_group *group); - -struct lab_cosmic_workspace *lab_cosmic_workspace_create(struct lab_cosmic_workspace_group *group); -void lab_cosmic_workspace_set_name(struct lab_cosmic_workspace *workspace, const char *name); -void lab_cosmic_workspace_set_active(struct lab_cosmic_workspace *workspace, bool enabled); -void lab_cosmic_workspace_set_urgent(struct lab_cosmic_workspace *workspace, bool enabled); -void lab_cosmic_workspace_set_hidden(struct lab_cosmic_workspace *workspace, bool enabled); -void lab_cosmic_workspace_set_coordinates(struct lab_cosmic_workspace *workspace, - struct wl_array *coordinates); -void lab_cosmic_workspace_destroy(struct lab_cosmic_workspace *workspace); - -#endif /* LABWC_PROTOCOLS_COSMIC_WORKSPACES_H */ diff --git a/include/protocols/ext-workspace-internal.h b/include/protocols/ext-workspace-internal.h deleted file mode 100644 index c5e7575f..00000000 --- a/include/protocols/ext-workspace-internal.h +++ /dev/null @@ -1,25 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef LABWC_PROTOCOLS_EXT_WORKSPACES_INTERNAL_H -#define LABWC_PROTOCOLS_EXT_WORKSPACES_INTERNAL_H - -struct wl_resource; -struct lab_ext_workspace_group; -struct lab_ext_workspace_manager; - -enum pending_ext_workspaces_change { - /* group events */ - WS_PENDING_WS_CREATE = 1 << 0, - - /* ws events*/ - WS_PENDING_WS_ACTIVATE = 1 << 1, - WS_PENDING_WS_DEACTIVATE = 1 << 2, - WS_PENDING_WS_REMOVE = 1 << 3, - WS_PENDING_WS_ASSIGN = 1 << 4, -}; - -void ext_group_output_send_initial_state(struct lab_ext_workspace_group *group, - struct wl_resource *group_resource); - -void ext_manager_schedule_done_event(struct lab_ext_workspace_manager *manager); - -#endif /* LABWC_PROTOCOLS_EXT_WORKSPACES_INTERNAL_H */ diff --git a/include/protocols/ext-workspace.h b/include/protocols/ext-workspace.h deleted file mode 100644 index cc422ac5..00000000 --- a/include/protocols/ext-workspace.h +++ /dev/null @@ -1,109 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef LABWC_PROTOCOLS_EXT_WORKSPACES_H -#define LABWC_PROTOCOLS_EXT_WORKSPACES_H - -#include -#include - -struct wlr_output; - -struct lab_ext_workspace_manager { - struct wl_global *global; - struct wl_list groups; - struct wl_list workspaces; - uint32_t caps; - struct wl_event_source *idle_source; - struct wl_event_loop *event_loop; - - struct { - struct wl_listener display_destroy; - } on; - - struct wl_list resources; -}; - -struct lab_ext_workspace_group { - struct lab_ext_workspace_manager *manager; - uint32_t capabilities; - struct { - struct wl_signal create_workspace; - struct wl_signal destroy; - } events; - - struct wl_list link; - struct wl_list outputs; - struct wl_list resources; -}; - -struct lab_ext_workspace { - struct lab_ext_workspace_manager *manager; - struct lab_ext_workspace_group *group; - char *id; - char *name; - struct wl_array coordinates; - uint32_t capabilities; - uint32_t state; /* enum lab_ext_workspace_state */ - uint32_t state_pending; /* enum lab_ext_workspace_state */ - - struct { - struct wl_signal activate; - struct wl_signal deactivate; - struct wl_signal remove; - struct wl_signal assign; - struct wl_signal destroy; - } events; - - struct wl_list link; - struct wl_list resources; -}; - -enum lab_ext_workspace_caps { - WS_CAP_NONE = 0, - WS_CAP_GRP_ALL = 0x0000ffff, - WS_CAP_WS_ALL = 0xffff0000, - - /* group caps */ - WS_CAP_GRP_WS_CREATE = 1 << 0, - - /* workspace caps */ - WS_CAP_WS_ACTIVATE = 1 << 16, - WS_CAP_WS_DEACTIVATE = 1 << 17, - WS_CAP_WS_REMOVE = 1 << 18, - WS_CAP_WS_ASSIGN = 1 << 19, -}; - -struct lab_ext_workspace_manager *lab_ext_workspace_manager_create( - struct wl_display *display, uint32_t caps, uint32_t version); - -struct lab_ext_workspace_group *lab_ext_workspace_group_create( - struct lab_ext_workspace_manager *manager); - -void lab_ext_workspace_group_output_enter( - struct lab_ext_workspace_group *group, struct wlr_output *output); - -void lab_ext_workspace_group_output_leave( - struct lab_ext_workspace_group *group, struct wlr_output *output); - -void lab_ext_workspace_group_destroy(struct lab_ext_workspace_group *group); - -/* Create a new workspace, id may be NULL */ -struct lab_ext_workspace *lab_ext_workspace_create( - struct lab_ext_workspace_manager *manager, const char *id); - -void lab_ext_workspace_assign_to_group(struct lab_ext_workspace *workspace, - struct lab_ext_workspace_group *group); - -void lab_ext_workspace_set_name(struct lab_ext_workspace *workspace, const char *name); - -void lab_ext_workspace_set_active(struct lab_ext_workspace *workspace, bool enabled); - -void lab_ext_workspace_set_urgent(struct lab_ext_workspace *workspace, bool enabled); - -void lab_ext_workspace_set_hidden(struct lab_ext_workspace *workspace, bool enabled); - -void lab_ext_workspace_set_coordinates(struct lab_ext_workspace *workspace, - struct wl_array *coordinates); - -void lab_ext_workspace_destroy(struct lab_ext_workspace *workspace); - -#endif /* LABWC_PROTOCOLS_EXT_WORKSPACES_H */ diff --git a/include/protocols/transaction-addon.h b/include/protocols/transaction-addon.h deleted file mode 100644 index 2c40b3dd..00000000 --- a/include/protocols/transaction-addon.h +++ /dev/null @@ -1,84 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef LABWC_PROTOCOLS_TRANSACTION_ADDON_H -#define LABWC_PROTOCOLS_TRANSACTION_ADDON_H - -#include - -struct lab_transaction_op { - uint32_t change; - void *src; - void *data; - - struct { - struct wl_signal destroy; - } events; - - // Private - struct wl_list link; -}; - -struct lab_transaction_session_context { - int ref_count; - struct wl_list transaction_ops; -}; - -struct lab_wl_resource_addon { - struct lab_transaction_session_context *ctx; - void *data; -}; - -/* - * Creates a new addon which can be attached to a wl_resource via - * wl_resource_set_user_data() and retrieved via wl_resource_get_user_data(). - * - * Usually the ctx argument should be addon->ctx of the parent wl_resource. - * If it is NULL it will be created automatically which can be used for top - * level wl_resources (when a client binds a wl_global from the registry). - * - * The context refcount is increased by one after this call. - */ -struct lab_wl_resource_addon *lab_resource_addon_create( - struct lab_transaction_session_context *ctx); - -/* - * A generic transaction operation attached to - * a session context transaction operation list. - * - * All arguments other than the context are user defined. - * Use of an enum for pending_change is suggested. - * - * The client is responsible for eventually freeing the data - * passed in the void *src and *data arguments by listening - * to the events.destroy signal. The transaction operations can be - * looped through by using lab_transaction_for_each(trans_op, ctx). - */ -struct lab_transaction_op *lab_transaction_op_add( - struct lab_transaction_session_context *ctx, - uint32_t pending_change, void *src, void *data); - -/* - * Removes the transaction operation from the ctx list and frees it. - * - * Does *not* free any passed in src or data arguments. - * Use the events.destroy signal for that if necessary. - */ -void lab_transaction_op_destroy(struct lab_transaction_op *transaction_op); - -/* - * Destroys the addon. - * - * The context refcount is decreased by one. If it reaches - * zero the context will be free'd alongside the addon itself. - * If the context is destroyed all pending transaction operations - * are destroyed as well. - */ -void lab_resource_addon_destroy(struct lab_wl_resource_addon *addon); - -/* Convenience wrappers for looping through the pending transaction ops of a ctx */ -#define lab_transaction_for_each(transaction_op, ctx) \ - wl_list_for_each(transaction_op, &(ctx)->transaction_ops, link) - -#define lab_transaction_for_each_safe(trans_op, trans_op_tmp, ctx) \ - wl_list_for_each_safe(trans_op, trans_op_tmp, &(ctx)->transaction_ops, link) - -#endif /* LABWC_PROTOCOLS_TRANSACTIONS_ADDON_H */ diff --git a/include/regions.h b/include/regions.h index abe472be..1035400c 100644 --- a/include/regions.h +++ b/include/regions.h @@ -26,7 +26,7 @@ struct region { }; /* Returns true if we should show the region overlay or snap to region */ -bool regions_should_snap(struct server *server); +bool regions_should_snap(void); /** * regions_reconfigure*() - re-initializes all regions from struct rc. @@ -36,7 +36,7 @@ bool regions_should_snap(struct server *server); * - new output local regions are created from struct rc * - the region geometry is re-calculated */ -void regions_reconfigure(struct server *server); +void regions_reconfigure(void); void regions_reconfigure_output(struct output *output); /* re-calculate the geometry based on usable area */ @@ -62,7 +62,7 @@ void regions_evacuate_output(struct output *output); void regions_destroy(struct seat *seat, struct wl_list *regions); /* Get output local region from cursor or name, may be NULL */ -struct region *regions_from_cursor(struct server *server); +struct region *regions_from_cursor(void); struct region *regions_from_name(const char *region_name, struct output *output); #endif /* LABWC_REGIONS_H */ diff --git a/include/resize-indicator.h b/include/resize-indicator.h index e8fe8480..54f5c025 100644 --- a/include/resize-indicator.h +++ b/include/resize-indicator.h @@ -5,7 +5,7 @@ struct server; struct view; -void resize_indicator_reconfigure(struct server *server); +void resize_indicator_reconfigure(void); void resize_indicator_show(struct view *view); void resize_indicator_update(struct view *view); void resize_indicator_hide(struct view *view); diff --git a/include/scaled-buffer/scaled-buffer.h b/include/scaled-buffer/scaled-buffer.h index c2af6054..017e29fb 100644 --- a/include/scaled-buffer/scaled-buffer.h +++ b/include/scaled-buffer/scaled-buffer.h @@ -130,7 +130,7 @@ void scaled_buffer_request_update(struct scaled_buffer *self, /** * scaled_buffer_invalidate_sharing - clear the list of entire cached - * scaled_buffers used to share visually dupliated buffers. This should + * scaled_buffers used to share visually duplicated buffers. This should * be called on Reconfigure to force updates of newly created * scaled_buffers rather than reusing ones created before Reconfigure. */ diff --git a/include/scaled-buffer/scaled-font-buffer.h b/include/scaled-buffer/scaled-font-buffer.h index a5e95087..2f0fe422 100644 --- a/include/scaled-buffer/scaled-font-buffer.h +++ b/include/scaled-buffer/scaled-font-buffer.h @@ -15,6 +15,7 @@ struct scaled_font_buffer { /* Private */ char *text; + bool use_markup; int max_width; float color[4]; float bg_color[4]; @@ -69,8 +70,18 @@ scaled_font_buffer_create_for_titlebar(struct wlr_scene_tree *parent, * bg_color is ignored for font buffers created with * scaled_font_buffer_create_for_titlebar(). */ -void scaled_font_buffer_update(struct scaled_font_buffer *self, const char *text, - int max_width, struct font *font, const float *color, +void scaled_font_buffer_update(struct scaled_font_buffer *self, + const char *text, int max_width, struct font *font, const float *color, const float *bg_color); +/** + * Update an existing auto scaling font buffer allowing the use of pango markup. + * + * Behaves identically to scaled_font_buffer_update(), but allows customization + * of the `use_markup` field of the @self struct via @use_markup. + */ +void scaled_font_buffer_update_markup(struct scaled_font_buffer *self, + const char *text, int max_width, struct font *font, const float *color, + const float *bg_color, bool use_markup); + #endif /* LABWC_SCALED_FONT_BUFFER_H */ diff --git a/include/scaled-buffer/scaled-icon-buffer.h b/include/scaled-buffer/scaled-icon-buffer.h index 45492ec2..45f83c91 100644 --- a/include/scaled-buffer/scaled-icon-buffer.h +++ b/include/scaled-buffer/scaled-icon-buffer.h @@ -12,7 +12,6 @@ struct wlr_scene_buffer; struct scaled_icon_buffer { struct scaled_buffer *scaled_buffer; struct wlr_scene_buffer *scene_buffer; - struct server *server; /* for window icon */ struct view *view; char *view_app_id; @@ -39,8 +38,7 @@ struct scaled_icon_buffer { * wlr_scene_buffer (or one of its parents) is being destroyed. */ struct scaled_icon_buffer *scaled_icon_buffer_create( - struct wlr_scene_tree *parent, struct server *server, - int width, int height); + struct wlr_scene_tree *parent, int width, int height); void scaled_icon_buffer_set_view(struct scaled_icon_buffer *self, struct view *view); diff --git a/include/session-lock.h b/include/session-lock.h index 207f0393..52e4e4f2 100644 --- a/include/session-lock.h +++ b/include/session-lock.h @@ -8,7 +8,6 @@ struct output; struct server; struct session_lock_manager { - struct server *server; struct wlr_session_lock_manager_v1 *wlr_manager; /* View re-focused on unlock */ struct view *last_active_view; @@ -31,8 +30,8 @@ struct session_lock_manager { struct wl_listener lock_destroy; }; -void session_lock_init(struct server *server); +void session_lock_init(void); void session_lock_output_create(struct session_lock_manager *manager, struct output *output); -void session_lock_update_for_layout_change(struct server *server); +void session_lock_update_for_layout_change(void); #endif /* LABWC_SESSION_LOCK_H */ diff --git a/include/show-desktop.h b/include/show-desktop.h new file mode 100644 index 00000000..cc86bcb4 --- /dev/null +++ b/include/show-desktop.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_SHOW_DESKTOP_H +#define LABWC_SHOW_DESKTOP_H + +void show_desktop_toggle(void); +void show_desktop_reset(void); + +#endif /* LABWC_SHOW_DESKTOP_H */ diff --git a/include/ssd.h b/include/ssd.h index 1340c8dc..9c617474 100644 --- a/include/ssd.h +++ b/include/ssd.h @@ -51,8 +51,7 @@ void ssd_set_titlebar(struct ssd *ssd, bool enabled); void ssd_enable_keybind_inhibit_indicator(struct ssd *ssd, bool enable); void ssd_enable_shade(struct ssd *ssd, bool enable); -void ssd_update_hovered_button(struct server *server, - struct wlr_scene_node *node); +void ssd_update_hovered_button(struct wlr_scene_node *node); void ssd_button_free(struct ssd_button *button); diff --git a/include/theme.h b/include/theme.h index beba06cc..2fb499a9 100644 --- a/include/theme.h +++ b/include/theme.h @@ -213,16 +213,13 @@ struct theme { int mag_border_width; }; -struct server; - /** * theme_init - read openbox theme and generate button textures * @theme: theme data - * @server: server * @theme_name: theme-name in //labwc/themerc * Note is obtained in theme-dir.c */ -void theme_init(struct theme *theme, struct server *server, const char *theme_name); +void theme_init(struct theme *theme, const char *theme_name); /** * theme_finish - free button textures diff --git a/include/view.h b/include/view.h index 7c4a24d9..46943ab2 100644 --- a/include/view.h +++ b/include/view.h @@ -135,7 +135,6 @@ struct view_impl { }; struct view { - struct server *server; enum view_type type; const struct view_impl *impl; struct wl_list link; @@ -178,6 +177,12 @@ struct view { char *title; char *app_id; /* WM_CLASS for xwayland windows */ + struct { + struct wlr_scene *scene; + struct wlr_ext_image_capture_source_v1 *source; + struct wl_listener on_capture_source_destroy; + } capture; + bool mapped; bool been_mapped; uint64_t creation_id; @@ -185,6 +190,7 @@ struct view { enum ssd_preference ssd_preference; bool shaded; bool minimized; + bool was_minimized_by_show_desktop_action; enum view_axis maximized; bool fullscreen; bool tearing_hint; @@ -280,6 +286,7 @@ struct view { struct wl_signal minimized; struct wl_signal fullscreened; struct wl_signal activated; /* bool *activated */ + struct wl_signal always_on_top; /* * This is emitted when app_id, or icon set via xdg_toplevel_icon * is updated. This is listened by scaled_icon_buffer. @@ -318,6 +325,7 @@ struct xdg_toplevel_view { /* Events unique to xdg-toplevel views */ struct wl_listener set_app_id; struct wl_listener request_show_window_menu; + struct wl_listener set_parent; struct wl_listener new_popup; }; @@ -357,7 +365,7 @@ bool view_matches_query(struct view *view, struct view_query *query); * @criteria: Criteria to match against. * Example: * struct view *view; - * for_each_view(view, &server->views, LAB_VIEW_CRITERIA_NONE) { + * for_each_view(view, &server.views, LAB_VIEW_CRITERIA_NONE) { * printf("%s\n", view_get_string_prop(view, "app_id")); * } */ @@ -373,7 +381,7 @@ bool view_matches_query(struct view *view, struct view_query *query); * @criteria: Criteria to match against. * Example: * struct view *view; - * for_each_view_reverse(view, &server->views, LAB_VIEW_CRITERIA_NONE) { + * for_each_view_reverse(view, &server.views, LAB_VIEW_CRITERIA_NONE) { * printf("%s\n", view_get_string_prop(view, "app_id")); * } */ @@ -423,13 +431,13 @@ struct view *view_prev(struct wl_list *head, struct view *view, * struct view **view; * struct wl_array views; * wl_array_init(&views); - * view_array_append(server, &views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE); + * view_array_append(&views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE); * wl_array_for_each(view, &views) { * // Do something with *view * } * wl_array_release(&views); */ -void view_array_append(struct server *server, struct wl_array *views, +void view_array_append(struct wl_array *views, enum lab_view_criteria criteria); enum view_wants_focus view_wants_focus(struct view *view); @@ -486,7 +494,6 @@ void view_resize_relative(struct view *view, int left, int right, int top, int bottom); void view_move_relative(struct view *view, int x, int y); void view_move(struct view *view, int x, int y); -void view_move_to_cursor(struct view *view); void view_moved(struct view *view); void view_minimize(struct view *view, bool minimized); bool view_compute_centered_position(struct view *view, @@ -515,11 +522,30 @@ int view_effective_height(struct view *view, bool use_pending); */ void view_center(struct view *view, const struct wlr_box *ref); +/** + * view_compute_position_by_policy() - compute view placement + * @view: view to be placed + * @geom: floating view geometry to update (in/out) + * @allow_cursor: set to false to ignore center-on-cursor policy + * @policy: placement policy to apply + * + * Computes floating view placement according to configured strategy. + * Unlike view_place_by_policy(), this function doesn't actually move + * the view. It returns false if position could not be computed (e.g. + * if no outputs are connected). In that case, @geom is not modified. + */ +bool view_compute_position_by_policy(struct view *view, struct wlr_box *geom, + bool allow_cursor, enum lab_placement_policy policy); + /** * view_place_by_policy - apply placement strategy to view * @view: view to be placed * @allow_cursor: set to false to ignore center-on-cursor policy * @policy: placement policy to apply + * + * Places a floating view according to configured placement strategy. + * Clears any maximized/fullscreen/tiled state and restores natural + * geometry of the view before positioning. */ void view_place_by_policy(struct view *view, bool allow_cursor, enum lab_placement_policy policy); @@ -546,7 +572,15 @@ bool view_titlebar_visible(struct view *view); void view_set_ssd_mode(struct view *view, enum lab_ssd_mode mode); void view_set_decorations(struct view *view, enum lab_ssd_mode mode, bool force_ssd); void view_toggle_fullscreen(struct view *view); + +/* + * Saves the window position in view->last_placement. This should be called + * when a view is first mapped or manually moved by the user. + */ +void view_save_last_placement(struct view *view); +/* Restores and adjusts the view's position from the view->last_placement */ void view_adjust_for_layout_change(struct view *view); + void view_move_to_edge(struct view *view, enum lab_edge direction, bool snap_to_windows); void view_grow_to_edge(struct view *view, enum lab_edge direction); void view_shrink_to_edge(struct view *view, enum lab_edge direction); @@ -603,4 +637,6 @@ enum lab_placement_policy view_placement_parse(const char *policy); /* xdg.c */ struct wlr_xdg_surface *xdg_surface_from_view(struct view *view); +bool view_matches_criteria(struct view *view, enum lab_view_criteria criteria); + #endif /* LABWC_VIEW_H */ diff --git a/include/window-rules.h b/include/window-rules.h index 1bee4c09..624f3b85 100644 --- a/include/window-rules.h +++ b/include/window-rules.h @@ -40,6 +40,7 @@ struct window_rule { enum property ignore_configure_request; enum property fixed_position; enum property icon_prefer_client; + enum property allow_always_on_top; struct wl_list link; /* struct rcxml.window_rules */ }; diff --git a/include/workspaces.h b/include/workspaces.h index c12a9c45..3a5c6d06 100644 --- a/include/workspaces.h +++ b/include/workspaces.h @@ -12,34 +12,20 @@ struct wlr_scene_tree; struct workspace { struct wl_list link; /* struct server.workspaces */ - struct server *server; char *name; struct wlr_scene_tree *tree; struct wlr_scene_tree *view_trees[3]; - struct lab_cosmic_workspace *cosmic_workspace; - struct { - struct wl_listener activate; - struct wl_listener deactivate; - struct wl_listener remove; - } on_cosmic; - - struct lab_ext_workspace *ext_workspace; - struct { - struct wl_listener activate; - struct wl_listener deactivate; - struct wl_listener assign; - struct wl_listener remove; - } on_ext; + struct wlr_ext_workspace_handle_v1 *ext_workspace; }; -void workspaces_init(struct server *server); +void workspaces_init(void); void workspaces_switch_to(struct workspace *target, bool update_focus); -void workspaces_destroy(struct server *server); +void workspaces_destroy(void); void workspaces_osd_hide(struct seat *seat); struct workspace *workspaces_find(struct workspace *anchor, const char *name, bool wrap); -void workspaces_reconfigure(struct server *server); +void workspaces_reconfigure(void); #endif /* LABWC_WORKSPACES_H */ diff --git a/include/xwayland.h b/include/xwayland.h index dad4689d..379a2e8a 100644 --- a/include/xwayland.h +++ b/include/xwayland.h @@ -11,7 +11,6 @@ struct wlr_output; struct wlr_output_layout; struct xwayland_unmanaged { - struct server *server; struct wlr_xwayland_surface *xwayland_surface; struct wlr_scene_node *node; struct wl_list link; @@ -40,6 +39,7 @@ struct xwayland_view { struct view base; struct wlr_xwayland_surface *xwayland_surface; bool focused_before_map; + bool initial_geometry_set; /* Events unique to XWayland views */ struct wl_listener associate; @@ -53,33 +53,34 @@ struct xwayland_view { struct wl_listener set_override_redirect; struct wl_listener set_strut_partial; struct wl_listener set_window_type; + struct wl_listener set_icon; struct wl_listener focus_in; - struct wl_listener map_request; /* Not (yet) implemented */ /* struct wl_listener set_role; */ /* struct wl_listener set_hints; */ + + /* Events coming in from the view itself */ + struct { + struct wl_listener always_on_top; + } on_view; + }; -void xwayland_unmanaged_create(struct server *server, - struct wlr_xwayland_surface *xsurface, bool mapped); +void xwayland_unmanaged_create(struct wlr_xwayland_surface *xsurface, bool mapped); -void xwayland_view_create(struct server *server, - struct wlr_xwayland_surface *xsurface, bool mapped); +void xwayland_view_create(struct wlr_xwayland_surface *xsurface, bool mapped); -void xwayland_server_init(struct server *server, - struct wlr_compositor *compositor); -void xwayland_server_finish(struct server *server); +void xwayland_server_init(struct wlr_compositor *compositor); +void xwayland_server_finish(void); void xwayland_adjust_usable_area(struct view *view, struct wlr_output_layout *layout, struct wlr_output *output, struct wlr_box *usable); -void xwayland_update_workarea(struct server *server); +void xwayland_update_workarea(void); -void xwayland_reset_cursor(struct server *server); - -void xwayland_flush(struct server *server); +void xwayland_flush(void); #endif /* HAVE_XWAYLAND */ #endif /* LABWC_XWAYLAND_H */ diff --git a/meson.build b/meson.build index ddb3d42b..d85823c9 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'labwc', 'c', - version: '0.9.5', + version: '0.20.1', license: 'GPL-2.0-only', meson_version: '>=0.59.0', default_options: [ @@ -51,9 +51,9 @@ endif add_project_arguments('-DLABWC_VERSION=@0@'.format(version), language: 'c') wlroots = dependency( - 'wlroots-0.19', + 'wlroots-0.20', default_options: ['default_library=static', 'examples=false'], - version: ['>=0.19.0', '<0.20.0'], + version: ['>=0.20.1', '<0.21.0'], ) wlroots_has_xwayland = wlroots.get_variable('have_xwayland') == 'true' @@ -65,13 +65,13 @@ xkbcommon = dependency('xkbcommon') xcb = dependency('xcb', required: get_option('xwayland')) xcb_ewmh = dependency('xcb-ewmh', required: get_option('xwayland')) xcb_icccm = dependency('xcb-icccm', required: get_option('xwayland')) -drm_full = dependency('libdrm') +drm_full = dependency('libdrm', required: wlroots.get_variable('have_drm_backend') == 'true') drm = drm_full.partial_dependency(compile_args: true, includes: true) xml2 = dependency('libxml-2.0') glib = dependency('glib-2.0') cairo = dependency('cairo') pangocairo = dependency('pangocairo') -input = dependency('libinput', version: '>=1.14') +input = dependency('libinput', version: '>=1.26', required: wlroots.get_variable('have_libinput_backend') == 'true') pixman = dependency('pixman-1') math = cc.find_library('m') png = dependency('libpng') @@ -211,6 +211,17 @@ install_data('data/labwc.desktop', install_dir: get_option('datadir') / 'wayland install_data('data/labwc-portals.conf', install_dir: get_option('datadir') / 'xdg-desktop-portal') +# Install labwc-session.target so that systemd user services with +# WantedBy=graphical-session.target can be started and stopped in sync +# with a labwc session (see labwc(1) SESSION MANAGEMENT for the opt-in +# autostart/shutdown snippet). +systemd_feat = get_option('systemd-session') +systemd = dependency('systemd', required: systemd_feat) +if systemd.found() + install_data('data/labwc-session.target', + install_dir: systemd.get_variable('systemduserunitdir')) +endif + icons = ['labwc-symbolic.svg', 'labwc.svg'] foreach icon : icons icon_path = join_paths('data', icon) diff --git a/meson_options.txt b/meson_options.txt index a3da65a8..e059220d 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -2,7 +2,9 @@ option('man-pages', type: 'feature', value: 'auto', description: 'Generate and i option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications') option('svg', type: 'feature', value: 'enabled', description: 'Enable svg window buttons') option('icon', type: 'feature', value: 'enabled', description: 'Enable window icons') +option('labnag', type: 'feature', value: 'auto', description: 'Build labnag notification daemon') option('nls', type: 'feature', value: 'auto', description: 'Enable native language support') +option('systemd-session', type: 'feature', value: 'auto', description: 'Install labwc-session.target systemd user unit') option('static_analyzer', type: 'feature', value: 'disabled', description: 'Run gcc static analyzer') option('test', type: 'feature', value: 'disabled', description: 'Run tests') option('sections', type: 'feature', value: 'disabled', description: 'Show unused functions') diff --git a/po/LINGUAS b/po/LINGUAS index 33f036c9..50e5157a 100644 --- a/po/LINGUAS +++ b/po/LINGUAS @@ -1 +1 @@ -ar ca cs da de el es et eu fa fi fr gl he hr hu id it ja ka kk ko lt ms nl pa pl pt pt_BR ru sk sv tr uk vi zh_CN zh_TW +ar ca cs da de el es et eu fa fi fr gl he hr hu id it ja ka kab kk ko lt ms nl pa pl pt pt_BR ru sk sr_Latn sv tr uk vi zh_CN zh_TW diff --git a/po/de.po b/po/de.po index 5841f3f6..d83ef64a 100644 --- a/po/de.po +++ b/po/de.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: labwc\n" "Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" "POT-Creation-Date: 2024-09-19 21:09+1000\n" -"PO-Revision-Date: 2025-08-15 20:01+0000\n" -"Last-Translator: Ettore Atalan \n" +"PO-Revision-Date: 2026-05-18 18:28+0000\n" +"Last-Translator: Demian \n" "Language-Team: German \n" "Language: de\n" @@ -21,7 +21,7 @@ msgstr "" #: src/menu/menu.c:1016 msgid "Go there..." -msgstr "Dorthin gehen..." +msgstr "Dorthin wechseln ..." #: src/menu/menu.c:1034 msgid "Terminal" @@ -61,11 +61,11 @@ msgstr "Immer im Vordergrund" #: src/menu/menu.c:1071 msgid "Move Left" -msgstr "nach links" +msgstr "Nach links" #: src/menu/menu.c:1078 msgid "Move Right" -msgstr "nach rechts" +msgstr "Nach rechts" #: src/menu/menu.c:1083 msgid "Always on Visible Workspace" diff --git a/po/kab.po b/po/kab.po new file mode 100644 index 00000000..44961f47 --- /dev/null +++ b/po/kab.po @@ -0,0 +1,80 @@ +# Labwc pot file +# Copyright (C) 2024 +# This file is distributed under the same license as the labwc package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: labwc\n" +"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" +"POT-Creation-Date: 2024-09-19 21:09+1000\n" +"PO-Revision-Date: 2026-03-31 08:01+0000\n" +"Last-Translator: ButterflyOfFire \n" +"Language-Team: Kabyle \n" +"Language: kab\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 4.2.1\n" + +#: src/menu/menu.c:1016 +msgid "Go there..." +msgstr "" + +#: src/menu/menu.c:1034 +msgid "Terminal" +msgstr "Ixf" + +#: src/menu/menu.c:1040 +msgid "Reconfigure" +msgstr "" + +#: src/menu/menu.c:1042 +msgid "Exit" +msgstr "Ffeɣ" + +#: src/menu/menu.c:1056 +msgid "Minimize" +msgstr "" + +#: src/menu/menu.c:1058 +msgid "Maximize" +msgstr "" + +#: src/menu/menu.c:1060 +msgid "Fullscreen" +msgstr "Agdil aččuran" + +#: src/menu/menu.c:1062 +msgid "Roll Up/Down" +msgstr "" + +#: src/menu/menu.c:1064 +msgid "Decorations" +msgstr "" + +#: src/menu/menu.c:1066 +msgid "Always on Top" +msgstr "" + +#: src/menu/menu.c:1071 +msgid "Move Left" +msgstr "" + +#: src/menu/menu.c:1078 +msgid "Move Right" +msgstr "" + +#: src/menu/menu.c:1083 +msgid "Always on Visible Workspace" +msgstr "" + +#: src/menu/menu.c:1086 +msgid "Workspace" +msgstr "" + +#: src/menu/menu.c:1089 +msgid "Close" +msgstr "Mdel" diff --git a/po/sr_Latn.po b/po/sr_Latn.po new file mode 100644 index 00000000..ef41f62e --- /dev/null +++ b/po/sr_Latn.po @@ -0,0 +1,81 @@ +# Labwc pot file +# Copyright (C) 2024 +# This file is distributed under the same license as the labwc package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: labwc\n" +"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" +"POT-Creation-Date: 2024-09-19 21:09+1000\n" +"PO-Revision-Date: 2026-05-05 08:28+0000\n" +"Last-Translator: Tobias Si \n" +"Language-Team: Serbian (latin) \n" +"Language: sr_Latn\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"X-Generator: Weblate 4.2.1\n" + +#: src/menu/menu.c:1016 +msgid "Go there..." +msgstr "Idi tamo..." + +#: src/menu/menu.c:1034 +msgid "Terminal" +msgstr "Terminal" + +#: src/menu/menu.c:1040 +msgid "Reconfigure" +msgstr "Podesi" + +#: src/menu/menu.c:1042 +msgid "Exit" +msgstr "Izlaz" + +#: src/menu/menu.c:1056 +msgid "Minimize" +msgstr "Minimiziraj" + +#: src/menu/menu.c:1058 +msgid "Maximize" +msgstr "Maksimiziraj" + +#: src/menu/menu.c:1060 +msgid "Fullscreen" +msgstr "Prikaz preko celog ekrana" + +#: src/menu/menu.c:1062 +msgid "Roll Up/Down" +msgstr "Prevuci Gore/Dole" + +#: src/menu/menu.c:1064 +msgid "Decorations" +msgstr "Dekoracije" + +#: src/menu/menu.c:1066 +msgid "Always on Top" +msgstr "Uvek na vrhu" + +#: src/menu/menu.c:1071 +msgid "Move Left" +msgstr "Pomeri levo" + +#: src/menu/menu.c:1078 +msgid "Move Right" +msgstr "Pomeri desno" + +#: src/menu/menu.c:1083 +msgid "Always on Visible Workspace" +msgstr "Uvek na vidljivom radnom prostoru" + +#: src/menu/menu.c:1086 +msgid "Workspace" +msgstr "Radni prostor" + +#: src/menu/menu.c:1089 +msgid "Close" +msgstr "Zatvori" diff --git a/protocols/cosmic-workspace-unstable-v1.xml b/protocols/cosmic-workspace-unstable-v1.xml deleted file mode 100644 index 76adedd9..00000000 --- a/protocols/cosmic-workspace-unstable-v1.xml +++ /dev/null @@ -1,364 +0,0 @@ - - - - Copyright © 2019 Christopher Billington - Copyright © 2020 Ilia Bozhinov - Copyright © 2022 Victoria Brekenfeld - - Permission to use, copy, modify, distribute, and sell this - software and its documentation for any purpose is hereby granted - without fee, provided that the above copyright notice appear in - all copies and that both that copyright notice and this permission - notice appear in supporting documentation, and that the name of - the copyright holders not be used in advertising or publicity - pertaining to distribution of the software without specific, - written prior permission. The copyright holders make no - representations about the suitability of this software for any - purpose. It is provided "as is" without express or implied - warranty. - - THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN - AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, - ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF - THIS SOFTWARE. - - - - - Workspaces, also called virtual desktops, are groups of surfaces. A - compositor with a concept of workspaces may only show some such groups of - surfaces (those of 'active' workspaces) at a time. 'Activating' a - workspace is a request for the compositor to display that workspace's - surfaces as normal, whereas the compositor may hide or otherwise - de-emphasise surfaces that are associated only with 'inactive' workspaces. - Workspaces are grouped by which sets of outputs they correspond to, and - may contain surfaces only from those outputs. In this way, it is possible - for each output to have its own set of workspaces, or for all outputs (or - any other arbitrary grouping) to share workspaces. Compositors may - optionally conceptually arrange each group of workspaces in an - N-dimensional grid. - - The purpose of this protocol is to enable the creation of taskbars and - docks by providing them with a list of workspaces and their properties, - and allowing them to activate and deactivate workspaces. - - After a client binds the zcosmic_workspace_manager_v1, each workspace will be - sent via the workspace event. - - - - - This event is emitted whenever a new workspace group has been created. - - All initial details of the workspace group (workspaces, outputs) will be - sent immediately after this event via the corresponding events in - zcosmic_workspace_group_handle_v1. - - - - - - - The client must send this request after it has finished sending other - requests. The compositor must process a series of requests preceding a - commit request atomically. - - This allows changes to the workspace properties to be seen as atomic, - even if they happen via multiple events, and even if they involve - multiple zcosmic_workspace_handle_v1 objects, for example, deactivating one - workspace and activating another. - - - - - - This event is sent after all changes in all workspace groups have been - sent. - - This allows changes to one or more zcosmic_workspace_group_handle_v1 - properties and zcosmic_workspace_handle_v1 properties to be seen as atomic, - even if they happen via multiple events. - In particular, an output moving from one workspace group to - another sends an output_enter event and an output_leave event to the two - zcosmic_workspace_group_handle_v1 objects in question. The compositor sends - the done event only after updating the output information in both - workspace groups. - - - - - - This event indicates that the compositor is done sending events to the - zcosmic_workspace_manager_v1. The server will destroy the object - immediately after sending this request, so it will become invalid and - the client should free any resources associated with it. - - - - - - Indicates the client no longer wishes to receive events for new - workspace groups. However the compositor may emit further workspace - events, until the finished event is emitted. - - The client must not send any more requests after this one. - - - - - - - A zcosmic_workspace_group_handle_v1 object represents a a workspace group - that is assigned a set of outputs and contains a number of workspaces. - - The set of outputs assigned to the workspace group is conveyed to the client via - output_enter and output_leave events, and its workspaces are conveyed with - workspace events. - - For example, a compositor which has a set of workspaces for each output may - advertise a workspace group (and its workspaces) per output, whereas a compositor - where a workspace spans all outputs may advertise a single workspace group for all - outputs. - - - - - - - - - This event advertises the capabilities supported by the compositor. If - a capability isn't supported, clients should hide or disable the UI - elements that expose this functionality. For instance, if the - compositor doesn't advertise support for creating workspaces, a button - triggering the create_workspace request should not be displayed. - - The compositor will ignore requests it doesn't support. For instance, - a compositor which doesn't advertise support for creating workspaces will ignore - create_workspace requests. - - Compositors must send this event once after creation of an - zcosmic_workspace_group_handle_v1 . When the capabilities change, compositors - must send this event again. - - The capabilities are sent as an array of 32-bit unsigned integers in - native endianness. - - - - - - - This event is emitted whenever an output is assigned to the workspace - group. - - - - - - - This event is emitted whenever an output is removed from the workspace - group. - - - - - - - This event is emitted whenever a new workspace has been created. - A workspace can only be a member of a single workspace group and cannot - be re-assigned. - - All initial details of the workspace (name, coordinates, state) will - be sent immediately after this event via the corresponding events in - zcosmic_workspace_handle_v1. - - - - - - - This event means the zcosmic_workspace_group_handle_v1 has been destroyed. - It is guaranteed there won't be any more events for this - zcosmic_workspace_group_handle_v1. The zext_workspace_group_handle_v1 becomes - inert so any requests will be ignored except the destroy request. - - The compositor must remove all workspaces belonging to a workspace group - before removing the workspace group. - - - - - - Request that the compositor create a new workspace with the given name. - - There is no guarantee that the compositor will create a new workspace, - or that the created workspace will have the provided name. - - - - - - - Destroys the zcosmic_workspace_group_handle_v1 object. - - This request should be called either when the client does not want to - use the workspace object any more or after the remove event to finalize - the destruction of the object. - - - - - - - A zcosmic_workspace_handle_v1 object represents a a workspace that handles a - group of surfaces. - - Each workspace has a name, conveyed to the client with the name event; a - list of states, conveyed to the client with the state event; and - optionally a set of coordinates, conveyed to the client with the - coordinates event. The client may request that the compositor activate or - deactivate the workspace. - - Each workspace can belong to only a single workspace group. - Depepending on the compositor policy, there might be workspaces with - the same name in different workspace groups, but these workspaces are still - separate (e.g. one of them might be active while the other is not). - - - - - This event is emitted immediately after the zcosmic_workspace_handle_v1 is - created and whenever the name of the workspace changes. - - - - - - - This event is used to organize workspaces into an N-dimensional grid - within a workspace group, and if supported, is emitted immediately after - the zcosmic_workspace_handle_v1 is created and whenever the coordinates of - the workspace change. Compositors may not send this event if they do not - conceptually arrange workspaces in this way. If compositors simply - number workspaces, without any geometric interpretation, they may send - 1D coordinates, which clients should not interpret as implying any - geometry. Sending an empty array means that the compositor no longer - orders the workspace geometrically. - - Coordinates have an arbitrary number of dimensions N with an uint32 - position along each dimension. By convention if N > 1, the first - dimension is X, the second Y, the third Z, and so on. The compositor may - chose to utilize these events for a more novel workspace layout - convention, however. No guarantee is made about the grid being filled or - bounded; there may be a workspace at coordinate 1 and another at - coordinate 1000 and none in between. Within a workspace group, however, - workspaces must have unique coordinates of equal dimensionality. - - - - - - - This event is emitted immediately after the zcosmic_workspace_handle_v1 is - created and each time the workspace state changes, either because of a - compositor action or because of a request in this protocol. - - - - - - - The different states that a workspace can have. - - - - - - - The workspace is not visible in its workspace group, and clients - attempting to visualize the compositor workspace state should not - display such workspaces. - - - - - - - - - - - - - This event advertises the capabilities supported by the compositor. If - a capability isn't supported, clients should hide or disable the UI - elements that expose this functionality. For instance, if the - compositor doesn't advertise support for removing workspaces, a button - triggering the remove request should not be displayed. - - The compositor will ignore requests it doesn't support. For instance, - a compositor which doesn't advertise support for remove will ignore - remove requests. - - Compositors must send this event once after creation of an - zcosmic_workspace_handle_v1 . When the capabilities change, compositors - must send this event again. - - The capabilities are sent as an array of 32-bit unsigned integers in - native endianness. - - - - - - - This event means the zcosmic_workspace_handle_v1 has been destroyed. It is - guaranteed there won't be any more events for this - zcosmic_workspace_handle_v1. The zext_workspace_handle_v1 becomes inert so - any requests will be ignored except the destroy request. - - - - - - Destroys the zcosmic_workspace_handle_v1 object. - - This request should be called either when the client does not want to - use the workspace object any more or after the remove event to finalize - the destruction of the object. - - - - - - Request that this workspace be activated. - - There is no guarantee the workspace will be actually activated, and - behaviour may be compositor-dependent. For example, activating a - workspace may or may not deactivate all other workspaces in the same - group. - - - - - - Request that this workspace be deactivated. - - There is no guarantee the workspace will be actually deactivated. - - - - - - Request that this workspace be removed. - - There is no guarantee the workspace will be actually removed. - - - - diff --git a/protocols/meson.build b/protocols/meson.build index 67c0d3d2..928bd601 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -14,18 +14,6 @@ wayland_scanner_server = generator( ) server_protocols = [ - wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml', - wl_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml', - wl_protocol_dir / 'stable/tablet/tablet-v2.xml', - wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', - wl_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml', - wl_protocol_dir / 'staging/xwayland-shell/xwayland-shell-v1.xml', - wl_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml', - wl_protocol_dir / 'staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml', - wl_protocol_dir / 'staging/ext-workspace/ext-workspace-v1.xml', - wl_protocol_dir / 'staging/ext-image-capture-source/ext-image-capture-source-v1.xml', - wl_protocol_dir / 'staging/ext-image-copy-capture/ext-image-copy-capture-v1.xml', - 'cosmic-workspace-unstable-v1.xml', 'wlr-layer-shell-unstable-v1.xml', 'wlr-output-power-management-unstable-v1.xml', ] diff --git a/src/action.c b/src/action.c index 2f956d95..265ff6e7 100644 --- a/src/action.c +++ b/src/action.c @@ -21,12 +21,14 @@ #include "cycle.h" #include "debug.h" #include "input/keyboard.h" +#include "input/key-state.h" #include "labwc.h" #include "magnifier.h" #include "menu/menu.h" #include "output.h" #include "output-virtual.h" #include "regions.h" +#include "show-desktop.h" #include "ssd.h" #include "theme.h" #include "translate.h" @@ -68,145 +70,114 @@ struct action_arg_list { struct wl_list value; }; +#define ACTION_TYPE_LIST(X) \ + X(NONE, "None") \ + X(CLOSE, "Close") \ + X(KILL, "Kill") \ + X(DEBUG, "Debug") \ + X(EXECUTE, "Execute") \ + X(EXIT, "Exit") \ + X(MOVE_TO_EDGE, "MoveToEdge") \ + X(TOGGLE_SNAP_TO_EDGE, "ToggleSnapToEdge") \ + X(SNAP_TO_EDGE, "SnapToEdge") \ + X(GROW_TO_EDGE, "GrowToEdge") \ + X(SHRINK_TO_EDGE, "ShrinkToEdge") \ + X(NEXT_WINDOW, "NextWindow") \ + X(PREVIOUS_WINDOW, "PreviousWindow") \ + X(NEXT_WINDOW_IMMEDIATE, "NextWindowImmediate") \ + X(PREVIOUS_WINDOW_IMMEDIATE, "PreviousWindowImmediate") \ + X(RECONFIGURE, "Reconfigure") \ + X(SHOW_MENU, "ShowMenu") \ + X(TOGGLE_MAXIMIZE, "ToggleMaximize") \ + X(MAXIMIZE, "Maximize") \ + X(UNMAXIMIZE, "UnMaximize") \ + X(TOGGLE_FULLSCREEN, "ToggleFullscreen") \ + X(SET_DECORATIONS, "SetDecorations") \ + X(TOGGLE_DECORATIONS, "ToggleDecorations") \ + X(TOGGLE_ALWAYS_ON_TOP, "ToggleAlwaysOnTop") \ + X(TOGGLE_ALWAYS_ON_BOTTOM, "ToggleAlwaysOnBottom") \ + X(TOGGLE_OMNIPRESENT, "ToggleOmnipresent") \ + X(FOCUS, "Focus") \ + X(UNFOCUS, "Unfocus") \ + X(ICONIFY, "Iconify") \ + X(RAISE, "Raise") \ + X(LOWER, "Lower") \ + X(MOVE, "Move") \ + X(RESIZE, "Resize") \ + X(RESIZE_RELATIVE, "ResizeRelative") \ + X(MOVETO, "MoveTo") \ + X(RESIZETO, "ResizeTo") \ + X(MOVETO_CURSOR, "MoveToCursor") \ + X(MOVE_RELATIVE, "MoveRelative") \ + X(SEND_TO_DESKTOP, "SendToDesktop") \ + X(GO_TO_DESKTOP, "GoToDesktop") \ + X(TOGGLE_SNAP_TO_REGION, "ToggleSnapToRegion") \ + X(SNAP_TO_REGION, "SnapToRegion") \ + X(UNSNAP, "UnSnap") \ + X(TOGGLE_KEYBINDS, "ToggleKeybinds") \ + X(FOCUS_OUTPUT, "FocusOutput") \ + X(MOVE_TO_OUTPUT, "MoveToOutput") \ + X(FIT_TO_OUTPUT, "FitToOutput") \ + X(IF, "If") \ + X(FOR_EACH, "ForEach") \ + X(VIRTUAL_OUTPUT_ADD, "VirtualOutputAdd") \ + X(VIRTUAL_OUTPUT_REMOVE, "VirtualOutputRemove") \ + X(AUTO_PLACE, "AutoPlace") \ + X(TOGGLE_TEARING, "ToggleTearing") \ + X(SHADE, "Shade") \ + X(UNSHADE, "Unshade") \ + X(TOGGLE_SHADE, "ToggleShade") \ + X(ENABLE_SCROLL_WHEEL_EMULATION, "EnableScrollWheelEmulation") \ + X(DISABLE_SCROLL_WHEEL_EMULATION, "DisableScrollWheelEmulation") \ + X(TOGGLE_SCROLL_WHEEL_EMULATION, "ToggleScrollWheelEmulation") \ + X(ENABLE_TABLET_MOUSE_EMULATION, "EnableTabletMouseEmulation") \ + X(DISABLE_TABLET_MOUSE_EMULATION, "DisableTabletMouseEmulation") \ + X(TOGGLE_TABLET_MOUSE_EMULATION, "ToggleTabletMouseEmulation") \ + X(TOGGLE_MAGNIFY, "ToggleMagnify") \ + X(ZOOM_IN, "ZoomIn") \ + X(ZOOM_OUT, "ZoomOut") \ + X(TOGGLE_SHOW_DESKTOP, "ToggleShowDesktop") \ + X(WARP_CURSOR, "WarpCursor") \ + X(HIDE_CURSOR, "HideCursor") \ + X(DEBUG_TOGGLE_KEY_STATE_INDICATOR, "DebugToggleKeyStateIndicator") + +/* + * Will expand to: + * + * enum action_type { + * ACTION_TYPE_INVALID = 0, + * ACTION_TYPE_NONE, + * ACTION_TYPE_CLOSE, + * ACTION_TYPE_KILL, + * etc... + */ enum action_type { ACTION_TYPE_INVALID = 0, - ACTION_TYPE_NONE, - ACTION_TYPE_CLOSE, - ACTION_TYPE_KILL, - ACTION_TYPE_DEBUG, - ACTION_TYPE_EXECUTE, - ACTION_TYPE_EXIT, - ACTION_TYPE_MOVE_TO_EDGE, - ACTION_TYPE_TOGGLE_SNAP_TO_EDGE, - ACTION_TYPE_SNAP_TO_EDGE, - ACTION_TYPE_GROW_TO_EDGE, - ACTION_TYPE_SHRINK_TO_EDGE, - ACTION_TYPE_NEXT_WINDOW, - ACTION_TYPE_PREVIOUS_WINDOW, - ACTION_TYPE_RECONFIGURE, - ACTION_TYPE_SHOW_MENU, - ACTION_TYPE_TOGGLE_MAXIMIZE, - ACTION_TYPE_MAXIMIZE, - ACTION_TYPE_UNMAXIMIZE, - ACTION_TYPE_TOGGLE_FULLSCREEN, - ACTION_TYPE_SET_DECORATIONS, - ACTION_TYPE_TOGGLE_DECORATIONS, - ACTION_TYPE_TOGGLE_ALWAYS_ON_TOP, - ACTION_TYPE_TOGGLE_ALWAYS_ON_BOTTOM, - ACTION_TYPE_TOGGLE_OMNIPRESENT, - ACTION_TYPE_FOCUS, - ACTION_TYPE_UNFOCUS, - ACTION_TYPE_ICONIFY, - ACTION_TYPE_MOVE, - ACTION_TYPE_RAISE, - ACTION_TYPE_LOWER, - ACTION_TYPE_RESIZE, - ACTION_TYPE_RESIZE_RELATIVE, - ACTION_TYPE_MOVETO, - ACTION_TYPE_RESIZETO, - ACTION_TYPE_MOVETO_CURSOR, - ACTION_TYPE_MOVE_RELATIVE, - ACTION_TYPE_SEND_TO_DESKTOP, - ACTION_TYPE_GO_TO_DESKTOP, - ACTION_TYPE_TOGGLE_SNAP_TO_REGION, - ACTION_TYPE_SNAP_TO_REGION, - ACTION_TYPE_UNSNAP, - ACTION_TYPE_TOGGLE_KEYBINDS, - ACTION_TYPE_FOCUS_OUTPUT, - ACTION_TYPE_MOVE_TO_OUTPUT, - ACTION_TYPE_FIT_TO_OUTPUT, - ACTION_TYPE_IF, - ACTION_TYPE_FOR_EACH, - ACTION_TYPE_VIRTUAL_OUTPUT_ADD, - ACTION_TYPE_VIRTUAL_OUTPUT_REMOVE, - ACTION_TYPE_AUTO_PLACE, - ACTION_TYPE_TOGGLE_TEARING, - ACTION_TYPE_SHADE, - ACTION_TYPE_UNSHADE, - ACTION_TYPE_TOGGLE_SHADE, - ACTION_TYPE_ENABLE_SCROLL_WHEEL_EMULATION, - ACTION_TYPE_DISABLE_SCROLL_WHEEL_EMULATION, - ACTION_TYPE_TOGGLE_SCROLL_WHEEL_EMULATION, - ACTION_TYPE_ENABLE_TABLET_MOUSE_EMULATION, - ACTION_TYPE_DISABLE_TABLET_MOUSE_EMULATION, - ACTION_TYPE_TOGGLE_TABLET_MOUSE_EMULATION, - ACTION_TYPE_TOGGLE_MAGNIFY, - ACTION_TYPE_ZOOM_IN, - ACTION_TYPE_ZOOM_OUT, - ACTION_TYPE_WARP_CURSOR, - ACTION_TYPE_HIDE_CURSOR, +#define X(name, str) ACTION_TYPE_##name, + ACTION_TYPE_LIST(X) +#undef X }; +/* + * Will expand to: + * + * const char *action_names[] = { + * [ACTION_TYPE_INVALID] = "INVALID", + * [ACTION_TYPE_NONE] = "None", + * [ACTION_TYPE_CLOSE] = "Close", + * [ACTION_TYPE_KILL] = "Kill", + * etc... + */ const char *action_names[] = { - "INVALID", - "None", - "Close", - "Kill", - "Debug", - "Execute", - "Exit", - "MoveToEdge", - "ToggleSnapToEdge", - "SnapToEdge", - "GrowToEdge", - "ShrinkToEdge", - "NextWindow", - "PreviousWindow", - "Reconfigure", - "ShowMenu", - "ToggleMaximize", - "Maximize", - "UnMaximize", - "ToggleFullscreen", - "SetDecorations", - "ToggleDecorations", - "ToggleAlwaysOnTop", - "ToggleAlwaysOnBottom", - "ToggleOmnipresent", - "Focus", - "Unfocus", - "Iconify", - "Move", - "Raise", - "Lower", - "Resize", - "ResizeRelative", - "MoveTo", - "ResizeTo", - "MoveToCursor", - "MoveRelative", - "SendToDesktop", - "GoToDesktop", - "ToggleSnapToRegion", - "SnapToRegion", - "UnSnap", - "ToggleKeybinds", - "FocusOutput", - "MoveToOutput", - "FitToOutput", - "If", - "ForEach", - "VirtualOutputAdd", - "VirtualOutputRemove", - "AutoPlace", - "ToggleTearing", - "Shade", - "Unshade", - "ToggleShade", - "EnableScrollWheelEmulation", - "DisableScrollWheelEmulation", - "ToggleScrollWheelEmulation", - "EnableTabletMouseEmulation", - "DisableTabletMouseEmulation", - "ToggleTabletMouseEmulation", - "ToggleMagnify", - "ZoomIn", - "ZoomOut", - "WarpCursor", - "HideCursor", + [ACTION_TYPE_INVALID] = "INVALID", +#define X(name, str)[ACTION_TYPE_##name] = str, + ACTION_TYPE_LIST(X) +#undef X NULL }; +#undef ACTION_TYPE_LIST + void action_arg_add_str(struct action *action, const char *key, const char *value) { @@ -368,6 +339,8 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char break; case ACTION_TYPE_NEXT_WINDOW: case ACTION_TYPE_PREVIOUS_WINDOW: + case ACTION_TYPE_NEXT_WINDOW_IMMEDIATE: + case ACTION_TYPE_PREVIOUS_WINDOW_IMMEDIATE: if (!strcasecmp(argument, "workspace")) { if (!strcasecmp(content, "all")) { action_arg_add_int(action, argument, CYCLE_WORKSPACE_ALL); @@ -739,23 +712,23 @@ action_list_free(struct wl_list *action_list) } static void -show_menu(struct server *server, struct view *view, struct cursor_context *ctx, +show_menu(struct view *view, struct cursor_context *ctx, const char *menu_name, bool at_cursor, const char *pos_x, const char *pos_y) { - if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH - && server->input_mode != LAB_INPUT_STATE_MENU) { + if (server.input_mode != LAB_INPUT_STATE_PASSTHROUGH + && server.input_mode != LAB_INPUT_STATE_MENU) { /* Prevent opening a menu while resizing / moving a view */ return; } - struct menu *menu = menu_get_by_id(server, menu_name); + struct menu *menu = menu_get_by_id(menu_name); if (!menu) { return; } - int x = server->seat.cursor->x; - int y = server->seat.cursor->y; + int x = server.seat.cursor->x; + int y = server.seat.cursor->y; /* The client menu needs an active client */ bool is_client_menu = !strcasecmp(menu_name, "client-menu"); @@ -774,7 +747,7 @@ show_menu(struct server *server, struct view *view, struct cursor_context *ctx, int lx, ly; wlr_scene_node_coords(ctx->node, &lx, &ly); /* MAX() prevents negative x when the window is maximized */ - x = MAX(x, lx - server->theme->menu_border_width); + x = MAX(x, lx - rc.theme->menu_border_width); } } @@ -783,8 +756,8 @@ show_menu(struct server *server, struct view *view, struct cursor_context *ctx, * x/y can be number, "center" or a %percent of screen dimensions */ if (pos_x && pos_y) { - struct output *output = output_nearest_to(server, - server->seat.cursor->x, server->seat.cursor->y); + struct output *output = output_nearest_to( + server.seat.cursor->x, server.seat.cursor->y); struct wlr_box usable = output_usable_area_in_layout_coords(output); if (!strcasecmp(pos_x, "center")) { @@ -828,8 +801,7 @@ show_menu(struct server *server, struct view *view, struct cursor_context *ctx, } static struct view * -view_for_action(struct view *activator, struct server *server, - struct action *action, struct cursor_context *ctx) +view_for_action(struct view *activator, struct action *action, struct cursor_context *ctx) { /* View is explicitly specified for mousebinds */ if (activator) { @@ -841,17 +813,16 @@ view_for_action(struct view *activator, struct server *server, case ACTION_TYPE_FOCUS: case ACTION_TYPE_MOVE: case ACTION_TYPE_RESIZE: { - *ctx = get_cursor_context(server); + *ctx = get_cursor_context(); return ctx->view; } default: - return server->active_view; + return server.active_view; } } struct action_prompt { /* Set when created */ - struct server *server; struct action *action; struct view *view; @@ -928,7 +899,7 @@ print_prompt_command(struct buf *buf, const char *format, } static void -action_prompt_create(struct view *view, struct server *server, struct action *action) +action_prompt_create(struct view *view, struct action *action) { struct buf command = BUF_INIT; print_prompt_command(&command, rc.prompt_command, action, rc.theme); @@ -945,7 +916,6 @@ action_prompt_create(struct view *view, struct server *server, struct action *ac close(pipe_fd); struct action_prompt *prompt = znew(*prompt); - prompt->server = server; prompt->action = action; prompt->view = view; prompt->pid = prompt_pid; @@ -994,8 +964,7 @@ action_check_prompt_result(pid_t pid, int exit_code) } if (actions) { wlr_log(WLR_INFO, "Running actions"); - actions_run(prompt->view, prompt->server, - actions, /*cursor_ctx*/ NULL); + actions_run(prompt->view, actions, /*cursor_ctx*/ NULL); } else { wlr_log(WLR_INFO, "No actions for selected branch"); } @@ -1026,14 +995,13 @@ match_queries(struct view *view, struct action *action) } static struct output * -get_target_output(struct output *output, struct server *server, - struct action *action) +get_target_output(struct output *output, struct action *action) { const char *output_name = action_get_str(action, "output", NULL); struct output *target = NULL; if (output_name) { - target = output_from_name(server, output_name); + target = output_from_name(output_name); } else { enum lab_edge edge = action_get_int(action, "direction", LAB_EDGE_NONE); @@ -1049,9 +1017,9 @@ get_target_output(struct output *output, struct server *server, } static void -warp_cursor(struct server *server, struct view *view, const char *to, const char *x, const char *y) +warp_cursor(struct view *view, const char *to, const char *x, const char *y) { - struct output *output = output_nearest_to_cursor(server); + struct output *output = output_nearest_to_cursor(); struct wlr_box target_area = {0}; int goto_x; int goto_y; @@ -1082,12 +1050,12 @@ warp_cursor(struct server *server, struct view *view, const char *to, const char target_area.y + target_area.height + offset_y; } - wlr_cursor_warp(server->seat.cursor, NULL, goto_x, goto_y); - cursor_update_focus(server); + wlr_cursor_warp(server.seat.cursor, NULL, goto_x, goto_y); + cursor_update_focus(); } static void -run_action(struct view *view, struct server *server, struct action *action, +run_action(struct view *view, struct action *action, struct cursor_context *ctx) { switch (action->type) { @@ -1109,7 +1077,7 @@ run_action(struct view *view, struct server *server, struct action *action, } break; case ACTION_TYPE_DEBUG: - debug_dump_scene(server); + debug_dump_scene(); break; case ACTION_TYPE_EXECUTE: { struct buf cmd = BUF_INIT; @@ -1120,7 +1088,7 @@ run_action(struct view *view, struct server *server, struct action *action, break; } case ACTION_TYPE_EXIT: - wl_display_terminate(server->wl_display); + wl_display_terminate(server.wl_display); break; case ACTION_TYPE_MOVE_TO_EDGE: if (view) { @@ -1175,18 +1143,33 @@ run_action(struct view *view, struct server *server, struct action *action, .app_id = action_get_int(action, "identifier", CYCLE_APP_ID_ALL), }; - if (server->input_mode == LAB_INPUT_STATE_CYCLE) { - cycle_step(server, dir); + if (server.input_mode == LAB_INPUT_STATE_CYCLE) { + cycle_step(dir); } else { - cycle_begin(server, dir, filter); + cycle_begin(dir, filter); } break; } + case ACTION_TYPE_NEXT_WINDOW_IMMEDIATE: + case ACTION_TYPE_PREVIOUS_WINDOW_IMMEDIATE: { + enum lab_cycle_dir dir = (action->type == ACTION_TYPE_NEXT_WINDOW_IMMEDIATE) ? + LAB_CYCLE_DIR_FORWARD : LAB_CYCLE_DIR_BACKWARD; + struct cycle_filter filter = { + .workspace = action_get_int(action, "workspace", + rc.window_switcher.workspace_filter), + .output = action_get_int(action, "output", + CYCLE_OUTPUT_ALL), + .app_id = action_get_int(action, "identifier", + CYCLE_APP_ID_ALL), + }; + cycle_immediate(dir, filter); + break; + } case ACTION_TYPE_RECONFIGURE: kill(getpid(), SIGHUP); break; case ACTION_TYPE_SHOW_MENU: - show_menu(server, view, ctx, + show_menu(view, ctx, action_get_str(action, "menu", NULL), action_get_bool(action, "atCursor", true), action_get_str(action, "x.position", NULL), @@ -1253,27 +1236,13 @@ run_action(struct view *view, struct server *server, struct action *action, } break; case ACTION_TYPE_UNFOCUS: - seat_focus_surface(&server->seat, NULL); + seat_focus_surface(&server.seat, NULL); break; case ACTION_TYPE_ICONIFY: if (view) { view_minimize(view, true); } break; - case ACTION_TYPE_MOVE: - if (view) { - /* - * If triggered by mousebind, grab context was already - * set by button press handling. For keybind-triggered - * Move, set it now from current cursor position. - */ - if (view != server->seat.pressed.ctx.view) { - interactive_set_grab_context(ctx); - } - interactive_begin(view, LAB_INPUT_STATE_MOVE, - LAB_EDGE_NONE); - } - break; case ACTION_TYPE_RAISE: if (view) { view_move_to_front(view); @@ -1284,6 +1253,20 @@ run_action(struct view *view, struct server *server, struct action *action, view_move_to_back(view); } break; + case ACTION_TYPE_MOVE: + if (view) { + /* + * If triggered by mousebind, grab context was already + * set by button press handling. For keybind-triggered + * Move, set it now from current cursor position. + */ + if (view != server.seat.pressed.ctx.view) { + interactive_set_grab_context(ctx); + } + interactive_begin(view, LAB_INPUT_STATE_MOVE, + LAB_EDGE_NONE); + } + break; case ACTION_TYPE_RESIZE: if (view) { /* @@ -1298,7 +1281,7 @@ run_action(struct view *view, struct server *server, struct action *action, * set by button press handling. For keybind-triggered * Resize, set it now from current cursor position. */ - if (view != server->seat.pressed.ctx.view) { + if (view != server.seat.pressed.ctx.view) { interactive_set_grab_context(ctx); } interactive_begin(view, LAB_INPUT_STATE_RESIZE, @@ -1329,7 +1312,7 @@ run_action(struct view *view, struct server *server, struct action *action, /* * To support only setting one of width/height - * in + * in * we fall back to current dimension when unset. */ struct wlr_box box = { @@ -1354,7 +1337,8 @@ run_action(struct view *view, struct server *server, struct action *action, "Action MoveToCursor is deprecated. To ensure your config works in future labwc " "releases, please use "); if (view) { - view_move_to_cursor(view); + view_place_by_policy(view, /* allow_cursor */ true, + LAB_PLACE_CURSOR); } break; case ACTION_TYPE_SEND_TO_DESKTOP: @@ -1372,12 +1356,12 @@ run_action(struct view *view, struct server *server, struct action *action, * a required argument for both SendToDesktop and GoToDesktop. */ struct workspace *target_workspace = workspaces_find( - server->workspaces.current, to, wrap); + server.workspaces.current, to, wrap); if (action->type == ACTION_TYPE_GO_TO_DESKTOP) { bool toggle = action_get_bool(action, "toggle", false); - if (target_workspace == server->workspaces.current + if (target_workspace == server.workspaces.current && toggle) { - target_workspace = server->workspaces.last; + target_workspace = server.workspaces.last; } } if (!target_workspace) { @@ -1388,8 +1372,8 @@ run_action(struct view *view, struct server *server, struct action *action, follow = action_get_bool(action, "follow", true); /* Ensure that the focus is not on another desktop */ - if (!follow && server->active_view == view) { - desktop_focus_topmost_view(server); + if (!follow && server.active_view == view) { + desktop_focus_topmost_view(); } } if (follow) { @@ -1403,7 +1387,7 @@ run_action(struct view *view, struct server *server, struct action *action, break; } struct output *target_output = - get_target_output(view->output, server, action); + get_target_output(view->output, action); if (target_output) { view_move_to_output(view, target_output); } @@ -1455,9 +1439,9 @@ run_action(struct view *view, struct server *server, struct action *action, } break; case ACTION_TYPE_FOCUS_OUTPUT: { - struct output *output = output_nearest_to_cursor(server); + struct output *output = output_nearest_to_cursor(); struct output *target_output = - get_target_output(output, server, action); + get_target_output(output, action); if (target_output) { desktop_focus_output(target_output); } @@ -1470,7 +1454,7 @@ run_action(struct view *view, struct server *server, struct action *action, * We delay the selection and execution of the * branch until we get a response from the user. */ - action_prompt_create(view, server, action); + action_prompt_create(view, action); } else if (view) { struct wl_list *actions; if (match_queries(view, action)) { @@ -1479,7 +1463,7 @@ run_action(struct view *view, struct server *server, struct action *action, actions = action_get_actionlist(action, "else"); } if (actions) { - actions_run(view, server, actions, ctx); + actions_run(view, actions, ctx); } } break; @@ -1490,7 +1474,7 @@ run_action(struct view *view, struct server *server, struct action *action, struct wl_array views; wl_array_init(&views); - view_array_append(server, &views, LAB_VIEW_CRITERIA_NONE); + view_array_append(&views, LAB_VIEW_CRITERIA_NONE); struct view **item; wl_array_for_each(item, &views) { @@ -1501,14 +1485,14 @@ run_action(struct view *view, struct server *server, struct action *action, actions = action_get_actionlist(action, "else"); } if (actions) { - actions_run(*item, server, actions, ctx); + actions_run(*item, actions, ctx); } } wl_array_release(&views); if (!matches) { actions = action_get_actionlist(action, "none"); if (actions) { - actions_run(view, server, actions, NULL); + actions_run(view, actions, NULL); } } break; @@ -1517,7 +1501,7 @@ run_action(struct view *view, struct server *server, struct action *action, /* TODO: rename this argument to "outputName" */ const char *output_name = action_get_str(action, "output_name", NULL); - output_virtual_add(server, output_name, + output_virtual_add(output_name, /*store_wlr_output*/ NULL); break; } @@ -1525,7 +1509,7 @@ run_action(struct view *view, struct server *server, struct action *action, /* TODO: rename this argument to "outputName" */ const char *output_name = action_get_str(action, "output_name", NULL); - output_virtual_remove(server, output_name); + output_virtual_remove(output_name); break; } case ACTION_TYPE_AUTO_PLACE: @@ -1572,14 +1556,14 @@ run_action(struct view *view, struct server *server, struct action *action, } break; case ACTION_TYPE_ENABLE_SCROLL_WHEEL_EMULATION: - server->seat.cursor_scroll_wheel_emulation = true; + server.seat.cursor_scroll_wheel_emulation = true; break; case ACTION_TYPE_DISABLE_SCROLL_WHEEL_EMULATION: - server->seat.cursor_scroll_wheel_emulation = false; + server.seat.cursor_scroll_wheel_emulation = false; break; case ACTION_TYPE_TOGGLE_SCROLL_WHEEL_EMULATION: - server->seat.cursor_scroll_wheel_emulation = - !server->seat.cursor_scroll_wheel_emulation; + server.seat.cursor_scroll_wheel_emulation = + !server.seat.cursor_scroll_wheel_emulation; break; case ACTION_TYPE_ENABLE_TABLET_MOUSE_EMULATION: rc.tablet.force_mouse_emulation = true; @@ -1591,23 +1575,29 @@ run_action(struct view *view, struct server *server, struct action *action, rc.tablet.force_mouse_emulation = !rc.tablet.force_mouse_emulation; break; case ACTION_TYPE_TOGGLE_MAGNIFY: - magnifier_toggle(server); + magnifier_toggle(); break; case ACTION_TYPE_ZOOM_IN: - magnifier_set_scale(server, MAGNIFY_INCREASE); + magnifier_set_scale(MAGNIFY_INCREASE); break; case ACTION_TYPE_ZOOM_OUT: - magnifier_set_scale(server, MAGNIFY_DECREASE); + magnifier_set_scale(MAGNIFY_DECREASE); + break; + case ACTION_TYPE_TOGGLE_SHOW_DESKTOP: + show_desktop_toggle(); break; case ACTION_TYPE_WARP_CURSOR: { const char *to = action_get_str(action, "to", "output"); const char *x = action_get_str(action, "x", "center"); const char *y = action_get_str(action, "y", "center"); - warp_cursor(server, view, to, x, y); + warp_cursor(view, to, x, y); break; } case ACTION_TYPE_HIDE_CURSOR: - cursor_set_visible(&server->seat, false); + cursor_set_visible(&server.seat, false); + break; + case ACTION_TYPE_DEBUG_TOGGLE_KEY_STATE_INDICATOR: + key_state_indicator_toggle(); break; case ACTION_TYPE_INVALID: wlr_log(WLR_ERROR, "Not executing unknown action"); @@ -1625,8 +1615,7 @@ run_action(struct view *view, struct server *server, struct action *action, } void -actions_run(struct view *activator, struct server *server, - struct wl_list *actions, struct cursor_context *cursor_ctx) +actions_run(struct view *activator, struct wl_list *actions, struct cursor_context *cursor_ctx) { if (!actions) { wlr_log(WLR_ERROR, "empty actions"); @@ -1643,7 +1632,7 @@ actions_run(struct view *activator, struct server *server, struct action *action; wl_list_for_each(action, actions, link) { - if (server->input_mode == LAB_INPUT_STATE_CYCLE + if (server.input_mode == LAB_INPUT_STATE_CYCLE && action->type != ACTION_TYPE_NEXT_WINDOW && action->type != ACTION_TYPE_PREVIOUS_WINDOW) { wlr_log(WLR_INFO, "Only NextWindow or PreviousWindow " @@ -1658,8 +1647,8 @@ actions_run(struct view *activator, struct server *server, * Refetch view because it may have been changed due to the * previous action */ - struct view *view = view_for_action(activator, server, action, &ctx); + struct view *view = view_for_action(activator, action, &ctx); - run_action(view, server, action, &ctx); + run_action(view, action, &ctx); } } diff --git a/src/buffer.c b/src/buffer.c index 04ca4328..a9fa717e 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -95,6 +95,7 @@ buffer_adopt_cairo_surface(cairo_surface_t *surface) buffer->surface = surface; buffer->data = cairo_image_surface_get_data(buffer->surface); + assert(buffer->data); buffer->format = DRM_FORMAT_ARGB8888; buffer->stride = cairo_image_surface_get_stride(buffer->surface); buffer->logical_width = width; @@ -107,6 +108,12 @@ buffer_adopt_cairo_surface(cairo_surface_t *surface) struct lab_data_buffer * buffer_create_cairo(uint32_t logical_width, uint32_t logical_height, float scale) { + if (!logical_width || !logical_height) { + wlr_log(WLR_ERROR, "Failed to create cairo buffer of %ux%u: dimensions must be > 0", + logical_width, logical_height); + return NULL; + } + /* Create an image surface with the scaled size */ cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, @@ -142,6 +149,7 @@ buffer_create_from_data(void *pixel_data, uint32_t width, uint32_t height, buffer->logical_width = width; buffer->logical_height = height; buffer->data = pixel_data; + assert(buffer->data); buffer->format = DRM_FORMAT_ARGB8888; buffer->stride = stride; buffer->surface = cairo_image_surface_create_for_data( @@ -188,6 +196,10 @@ buffer_resize(struct lab_data_buffer *src_buffer, int width, int height, struct lab_data_buffer *buffer = buffer_create_cairo(width, height, scale); + if (!buffer) { + wlr_log(WLR_INFO, "Failed to resize buffer to %dx%d", width, height); + return NULL; + } cairo_t *cairo = cairo_create(buffer->surface); struct wlr_box container = { diff --git a/src/common/buf.c b/src/common/buf.c index c141b62e..2a68b734 100644 --- a/src/common/buf.c +++ b/src/common/buf.c @@ -53,15 +53,14 @@ buf_expand_shell_variables(struct buf *s) if (s->data[i] == '$' && isvalid(s->data[i+1])) { /* expand environment variable */ buf_clear(&environment_variable); - buf_add(&environment_variable, s->data + i + 1); - char *p = environment_variable.data; - while (isvalid(*p)) { - ++p; + int len = 0; + while (isvalid(s->data[i + 1 + len])) { + buf_add_char(&environment_variable, s->data[i + 1 + len]); + ++len; } - *p = '\0'; - i += strlen(environment_variable.data); + i += len; strip_curly_braces(environment_variable.data); - p = getenv(environment_variable.data); + char *p = getenv(environment_variable.data); if (p) { buf_add(&tmp, p); } diff --git a/src/common/font.c b/src/common/font.c index b307729c..642f864d 100644 --- a/src/common/font.c +++ b/src/common/font.c @@ -79,9 +79,10 @@ font_get_buffer_size(int max_width, const char *text, struct font *font, void font_buffer_create(struct lab_data_buffer **buffer, int max_width, int height, const char *text, struct font *font, const float *color, - cairo_pattern_t *bg_pattern, double scale) + cairo_pattern_t *bg_pattern, double scale, bool use_markup) { if (string_null_or_empty(text)) { + *buffer = NULL; return; } @@ -91,6 +92,13 @@ font_buffer_create(struct lab_data_buffer **buffer, int max_width, height = computed_height; } + if (height <= 0 || width <= 0) { + wlr_log(WLR_INFO, "Refusing to create invisible font buffer of %dx%d", + width, height); + *buffer = NULL; + return; + } + *buffer = buffer_create_cairo(width, height, scale); if (!*buffer) { wlr_log(WLR_ERROR, "Failed to create font buffer"); @@ -123,7 +131,13 @@ font_buffer_create(struct lab_data_buffer **buffer, int max_width, PangoLayout *layout = pango_cairo_create_layout(cairo); pango_context_set_round_glyph_positions(pango_layout_get_context(layout), false); pango_layout_set_width(layout, width * PANGO_SCALE); - pango_layout_set_text(layout, text, -1); + + if (use_markup) { + pango_layout_set_markup(layout, text, -1); + } else { + pango_layout_set_text(layout, text, -1); + } + pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); if (!opaque_bg) { diff --git a/src/common/lab-scene-rect.c b/src/common/lab-scene-rect.c index 2d0c7408..6689d48f 100644 --- a/src/common/lab-scene-rect.c +++ b/src/common/lab-scene-rect.c @@ -3,6 +3,7 @@ #include #include #include "common/mem.h" +#include "common/scene-helpers.h" struct border_scene { struct wlr_scene_tree *tree; @@ -26,20 +27,20 @@ lab_scene_rect_create(struct wlr_scene_tree *parent, rect->border_width = opts->border_width; rect->nr_borders = opts->nr_borders; rect->borders = znew_n(rect->borders[0], opts->nr_borders); - rect->tree = wlr_scene_tree_create(parent); + rect->tree = lab_wlr_scene_tree_create(parent); if (opts->bg_color) { - rect->fill = wlr_scene_rect_create(rect->tree, 0, 0, opts->bg_color); + rect->fill = lab_wlr_scene_rect_create(rect->tree, 0, 0, opts->bg_color); } for (int i = 0; i < rect->nr_borders; i++) { struct border_scene *border = &rect->borders[i]; float *color = opts->border_colors[i]; - border->tree = wlr_scene_tree_create(rect->tree); - border->top = wlr_scene_rect_create(border->tree, 0, 0, color); - border->right = wlr_scene_rect_create(border->tree, 0, 0, color); - border->bottom = wlr_scene_rect_create(border->tree, 0, 0, color); - border->left = wlr_scene_rect_create(border->tree, 0, 0, color); + border->tree = lab_wlr_scene_tree_create(rect->tree); + border->top = lab_wlr_scene_rect_create(border->tree, 0, 0, color); + border->right = lab_wlr_scene_rect_create(border->tree, 0, 0, color); + border->bottom = lab_wlr_scene_rect_create(border->tree, 0, 0, color); + border->left = lab_wlr_scene_rect_create(border->tree, 0, 0, color); } rect->node_destroy.notify = handle_node_destroy; diff --git a/src/common/scene-helpers.c b/src/common/scene-helpers.c index 517789c0..e9c80bb9 100644 --- a/src/common/scene-helpers.c +++ b/src/common/scene-helpers.c @@ -5,6 +5,7 @@ #include #include #include +#include "common/mem.h" #include "magnifier.h" #include "output.h" @@ -24,6 +25,34 @@ lab_wlr_surface_from_node(struct wlr_scene_node *node) return NULL; } +struct wlr_scene_tree * +lab_wlr_scene_tree_create(struct wlr_scene_tree *parent) +{ + struct wlr_scene_tree *tree = wlr_scene_tree_create(parent); + die_if_null(tree); + return tree; +} + +struct wlr_scene_rect * +lab_wlr_scene_rect_create(struct wlr_scene_tree *parent, + int width, int height, const float color[static 4]) +{ + struct wlr_scene_rect *rect = + wlr_scene_rect_create(parent, width, height, color); + die_if_null(rect); + return rect; +} + +struct wlr_scene_buffer * +lab_wlr_scene_buffer_create(struct wlr_scene_tree *parent, + struct wlr_buffer *buffer) +{ + struct wlr_scene_buffer *scene_buffer = + wlr_scene_buffer_create(parent, buffer); + die_if_null(scene_buffer); + return scene_buffer; +} + struct wlr_scene_node * lab_wlr_scene_get_prev_node(struct wlr_scene_node *node) { diff --git a/src/common/spawn.c b/src/common/spawn.c index 898c4800..6073d6ae 100644 --- a/src/common/spawn.c +++ b/src/common/spawn.c @@ -89,6 +89,37 @@ out: g_strfreev(argv); } +void +spawn_sync_no_shell(char const *command) +{ + GError *err = NULL; + gchar **argv = NULL; + + assert(command); + + g_shell_parse_argv((gchar *)command, NULL, &argv, &err); + if (err) { + g_message("%s", err->message); + g_error_free(err); + return; + } + + pid_t child = fork(); + switch (child) { + case -1: + wlr_log(WLR_ERROR, "unable to fork()"); + break; + case 0: + reset_signals_and_limits(); + execvp(argv[0], argv); + _exit(1); + default: + waitpid(child, NULL, 0); + break; + } + g_strfreev(argv); +} + pid_t spawn_primary_client(const char *command) { diff --git a/src/config/keybind.c b/src/config/keybind.c index 1705983a..8205ea93 100644 --- a/src/config/keybind.c +++ b/src/config/keybind.c @@ -121,9 +121,9 @@ update_keycodes_iter(struct xkb_keymap *keymap, xkb_keycode_t key, void *data) } void -keybind_update_keycodes(struct server *server) +keybind_update_keycodes(void) { - struct xkb_state *state = server->seat.keyboard_group->keyboard.xkb_state; + struct xkb_state *state = server.seat.keyboard_group->keyboard.xkb_state; struct xkb_keymap *keymap = xkb_state_get_keymap(state); struct keybind *keybind; diff --git a/src/config/libinput.c b/src/config/libinput.c index 1209d267..59bf469e 100644 --- a/src/config/libinput.c +++ b/src/config/libinput.c @@ -25,6 +25,7 @@ libinput_category_init(struct libinput_category *l) l->dwt = -1; l->click_method = -1; l->scroll_method = -1; + l->scroll_button = -1; l->send_events_mode = -1; l->have_calibration_matrix = false; l->scroll_factor = 1.0; diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 95285776..ab0639d1 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -94,6 +94,43 @@ parse_window_type(const char *type) } } +uint32_t +parse_privileged_interface(const char *name) +{ + static const char * const ifaces[] = { + "wp_drm_lease_device_v1", + "zwlr_gamma_control_manager_v1", + "zwlr_output_manager_v1", + "zwlr_output_power_manager_v1", + "zwp_input_method_manager_v2", + "zwlr_virtual_pointer_manager_v1", + "zwp_virtual_keyboard_manager_v1", + "zwlr_export_dmabuf_manager_v1", + "zwlr_screencopy_manager_v1", + "ext_data_control_manager_v1", + "zwlr_data_control_manager_v1", + "wp_security_context_manager_v1", + "ext_idle_notifier_v1", + "zwlr_foreign_toplevel_manager_v1", + "ext_foreign_toplevel_list_v1", + "ext_session_lock_manager_v1", + "zwlr_layer_shell_v1", + "ext_workspace_manager_v1", + "ext_image_copy_capture_manager_v1", + "ext_output_image_capture_source_manager_v1", + }; + + static_assert(ARRAY_SIZE(ifaces) <= 32, + "return type too small for amount of privileged protocols"); + + for (size_t i = 0; i < ARRAY_SIZE(ifaces); i++) { + if (!strcmp(name, ifaces[i])) { + return 1 << i; + } + } + return 0; +} + /* * Openbox/labwc comparison * @@ -300,6 +337,8 @@ fill_window_rule(xmlNode *node) set_property(content, &window_rule->ignore_configure_request); } else if (!strcasecmp(key, "fixedPosition")) { set_property(content, &window_rule->fixed_position); + } else if (!strcasecmp(key, "allowAlwaysOnTop")) { + set_property(content, &window_rule->allow_always_on_top); } } @@ -563,6 +602,7 @@ fill_keybind(xmlNode *node) lab_xml_get_bool(node, "onRelease", &keybind->on_release); lab_xml_get_bool(node, "layoutDependent", &keybind->use_syms_only); lab_xml_get_bool(node, "allowWhenLocked", &keybind->allow_when_locked); + lab_xml_get_bool(node, "overrideInhibition", &keybind->override_inhibition); append_parsed_actions(node, &keybind->actions); } @@ -573,9 +613,9 @@ fill_mousebind(xmlNode *node, const char *context) /* * Example of what we are parsing: * - * - * - * + * + * + * * */ @@ -851,9 +891,19 @@ fill_libinput_category(xmlNode *node) } else if (!strcasecmp(content, "twofinger")) { category->scroll_method = LIBINPUT_CONFIG_SCROLL_2FG; + } else if (!strcasecmp(content, "onbutton")) { + category->scroll_method = + LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN; } else { wlr_log(WLR_ERROR, "invalid scrollMethod"); } + } else if (!strcasecmp(key, "scrollButton")) { + int button = atoi(content); + if (button != 0) { + category->scroll_button = button; + } else { + wlr_log(WLR_ERROR, "invalid scrollButton"); + } } else if (!strcasecmp(key, "sendEventsMode")) { category->send_events_mode = get_send_events_mode(content); @@ -1030,6 +1080,16 @@ set_tearing_mode(const char *str, enum tearing_mode *variable) } } +static void +set_hdr_mode(const char *str, enum render_bit_depth *variable) +{ + if (parse_bool(str, -1) == 1) { + *variable = LAB_RENDER_BIT_DEPTH_10; + } else { + *variable = LAB_RENDER_BIT_DEPTH_8; + } +} + /* Returns true if the node's children should also be traversed */ static bool entry(xmlNode *node, char *nodename, char *content) @@ -1094,6 +1154,8 @@ entry(xmlNode *node, char *nodename, char *content) set_adaptive_sync_mode(content, &rc.adaptive_sync); } else if (!strcasecmp(nodename, "allowTearing.core")) { set_tearing_mode(content, &rc.allow_tearing); + } else if (!strcasecmp(nodename, "Hdr.core")) { + set_hdr_mode(content, &rc.target_render_depth); } else if (!strcasecmp(nodename, "autoEnableOutputs.core")) { set_bool(content, &rc.auto_enable_outputs); } else if (!strcasecmp(nodename, "reuseOutputMode.core")) { @@ -1145,6 +1207,9 @@ entry(xmlNode *node, char *nodename, char *content) set_bool(content, &rc.focus_follow_mouse_requires_movement); } else if (!strcasecmp(nodename, "raiseOnFocus.focus")) { set_bool(content, &rc.raise_on_focus); + } else if (!strcasecmp(nodename, "raiseOnFocusDelay.focus")) { + long val = strtol(content, NULL, 10); + rc.raise_on_focus_delay_ms = val > 0 ? (uint32_t)val : 0; } else if (!strcasecmp(nodename, "doubleClickTime.mouse")) { long doubleclick_time_parsed = strtol(content, NULL, 10); if (doubleclick_time_parsed > 0) { @@ -1353,6 +1418,12 @@ entry(xmlNode *node, char *nodename, char *content) } else if (!strcasecmp(nodename, "relativeMotionSensitivity.tabletTool")) { rc.tablet_tool.relative_motion_sensitivity = tablet_get_dbl_if_positive(content, "relativeMotionSensitivity"); + } else if (!strcasecmp(nodename, "minPressure.tabletTool")) { + rc.tablet_tool.min_pressure = + tablet_get_dbl_if_positive(content, "minPressure"); + } else if (!strcasecmp(nodename, "maxPressure.tabletTool")) { + rc.tablet_tool.max_pressure = + tablet_get_dbl_if_positive(content, "maxPressure"); } else if (!strcasecmp(nodename, "ignoreButtonReleasePeriod.menu")) { rc.menu_ignore_button_release_period = atoi(content); } else if (!strcasecmp(nodename, "showIcons.menu")) { @@ -1369,6 +1440,16 @@ entry(xmlNode *node, char *nodename, char *content) rc.mag_increment = MAX(0, rc.mag_increment); } else if (!strcasecmp(nodename, "useFilter.magnifier")) { set_bool(content, &rc.mag_filter); + } else if (!strcasecmp(nodename, "privilegedInterfaces")) { + rc.allowed_interfaces = 0; + } else if (!strcasecmp(nodename, "allow.privilegedInterfaces")) { + uint32_t iface_id = parse_privileged_interface(content); + if (iface_id) { + rc.allowed_interfaces |= iface_id; + } else { + wlr_log(WLR_ERROR, "invalid value for " + ""); + } } return false; @@ -1449,8 +1530,10 @@ rcxml_init(void) rc.gap = 0; rc.adaptive_sync = LAB_ADAPTIVE_SYNC_DISABLED; rc.allow_tearing = LAB_TEARING_DISABLED; + rc.target_render_depth = LAB_RENDER_BIT_DEPTH_DEFAULT; rc.auto_enable_outputs = true; rc.reuse_output_mode = false; + rc.allowed_interfaces = UINT32_MAX; rc.xwayland_persistence = false; rc.primary_selection = true; @@ -1463,6 +1546,7 @@ rcxml_init(void) rc.focus_follow_mouse = false; rc.focus_follow_mouse_requires_movement = true; rc.raise_on_focus = false; + rc.raise_on_focus_delay_ms = 0; rc.doubleclick_time = 500; @@ -1473,6 +1557,8 @@ rcxml_init(void) tablet_load_default_button_mappings(); rc.tablet_tool.motion = LAB_MOTION_ABSOLUTE; rc.tablet_tool.relative_motion_sensitivity = 1.0; + rc.tablet_tool.min_pressure = 0.0; + rc.tablet_tool.max_pressure = 1.0; rc.repeat_rate = 25; rc.repeat_delay = 600; @@ -1648,6 +1734,8 @@ deduplicate_key_bindings(void) wl_list_remove(¤t->link); keybind_destroy(current); cleared++; + } else if (actions_contain_toggle_keybinds(¤t->actions)) { + current->override_inhibition = true; } } if (replaced) { diff --git a/src/config/session.c b/src/config/session.c index 515b6803..61ce156f 100644 --- a/src/config/session.c +++ b/src/config/session.c @@ -8,8 +8,8 @@ #include #include #include -#include #include +#include #include #include "common/buf.h" #include "common/dir.h" @@ -20,8 +20,13 @@ #include "config/rcxml.h" #include "labwc.h" +#if WLR_HAS_DRM_BACKEND + #include +#else + #define wlr_backend_is_drm(backend) (false) +#endif + static const char *const env_vars[] = { - "DISPLAY", "WAYLAND_DISPLAY", "XDG_CURRENT_DESKTOP", "XCURSOR_SIZE", @@ -185,10 +190,8 @@ backend_check_drm(struct wlr_backend *backend, void *is_drm) } static bool -should_update_activation(struct server *server) +should_update_activation(void) { - assert(server); - static const char *act_env = "LABWC_UPDATE_ACTIVATION_ENV"; char *env = getenv(act_env); if (env) { @@ -206,14 +209,29 @@ should_update_activation(struct server *server) /* With no valid preference, update when a DRM backend is in use */ bool have_drm = false; - wlr_multi_for_each_backend(server->backend, backend_check_drm, &have_drm); + wlr_multi_for_each_backend(server.backend, backend_check_drm, &have_drm); return have_drm; } static void -update_activation_env(struct server *server, bool initialize) +execute_update(const char *env_keys, const char *env_unset_keys, bool initialize) { - if (!should_update_activation(server)) { + char *cmd = + strdup_printf("dbus-update-activation-environment %s", + initialize ? env_keys : env_unset_keys); + spawn_sync_no_shell(cmd); + free(cmd); + + cmd = strdup_printf("systemctl --user %s %s", + initialize ? "import-environment" : "unset-environment", env_keys); + spawn_sync_no_shell(cmd); + free(cmd); +} + +static void +update_activation_env(bool initialize) +{ + if (!should_update_activation()) { return; } @@ -229,19 +247,18 @@ update_activation_env(struct server *server, bool initialize) char *env_keys = str_join(env_vars, "%s", " "); char *env_unset_keys = initialize ? NULL : str_join(env_vars, "%s=", " "); - char *cmd = - strdup_printf("dbus-update-activation-environment %s", - initialize ? env_keys : env_unset_keys); - spawn_async_no_shell(cmd); - free(cmd); - - cmd = strdup_printf("systemctl --user %s %s", - initialize ? "import-environment" : "unset-environment", env_keys); - spawn_async_no_shell(cmd); - free(cmd); + execute_update(env_keys, env_unset_keys, initialize); free(env_keys); free(env_unset_keys); + +#if HAVE_XWAYLAND + if (server.xwayland) { + // DISPLAY is only set if xwayland was initialized successfully, + // so we only update the env in that case + execute_update("DISPLAY", "DISPLAY=", initialize); + } +#endif } void @@ -314,18 +331,18 @@ session_run_script(const char *script) } void -session_autostart_init(struct server *server) +session_autostart_init(void) { /* Update dbus and systemd user environment, each may fail gracefully */ - update_activation_env(server, /* initialize */ true); + update_activation_env(/* initialize */ true); session_run_script("autostart"); } void -session_shutdown(struct server *server) +session_shutdown(void) { session_run_script("shutdown"); /* Clear the dbus and systemd user environment, each may fail gracefully */ - update_activation_env(server, /* initialize */ false); + update_activation_env(/* initialize */ false); } diff --git a/src/cycle/cycle.c b/src/cycle/cycle.c index cec5cac5..0f183ddc 100644 --- a/src/cycle/cycle.c +++ b/src/cycle/cycle.c @@ -9,6 +9,7 @@ #include "common/mem.h" #include "common/scene-helpers.h" #include "config/rcxml.h" +#include "config/types.h" #include "labwc.h" #include "node.h" #include "output.h" @@ -18,17 +19,16 @@ #include "theme.h" #include "view.h" -static bool init_cycle(struct server *server, struct cycle_filter filter); -static void update_cycle(struct server *server); -static void destroy_cycle(struct server *server); +static bool init_cycle(struct cycle_filter filter); +static void update_cycle(void); +static void destroy_cycle(void); static void update_preview_outlines(struct view *view) { /* Create / Update preview outline tree */ - struct server *server = view->server; - struct theme *theme = server->theme; - struct lab_scene_rect *rect = view->server->cycle.preview_outline; + struct theme *theme = rc.theme; + struct lab_scene_rect *rect = server.cycle.preview_outline; if (!rect) { struct lab_scene_rect_options opts = { .border_colors = (float *[3]) { @@ -39,10 +39,10 @@ update_preview_outlines(struct view *view) .nr_borders = 3, .border_width = theme->osd_window_switcher_preview_border_width, }; - rect = lab_scene_rect_create(&server->scene->tree, &opts); + rect = lab_scene_rect_create(&server.scene->tree, &opts); wlr_scene_node_place_above(&rect->tree->node, - &server->cycle_preview_tree->node); - server->cycle.preview_outline = rect; + &server.cycle_preview_tree->node); + server.cycle.preview_outline = rect; } struct wlr_box geo = ssd_max_extents(view); @@ -52,21 +52,21 @@ update_preview_outlines(struct view *view) /* Returns the view to select next in the window switcher. */ static struct view * -get_next_selected_view(struct server *server, enum lab_cycle_dir dir) +get_next_selected_view(enum lab_cycle_dir dir) { - struct cycle_state *cycle = &server->cycle; + struct cycle_state *cycle = &server.cycle; assert(cycle->selected_view); - assert(!wl_list_empty(&server->cycle.views)); + assert(!wl_list_empty(&server.cycle.views)); struct wl_list *link; if (dir == LAB_CYCLE_DIR_FORWARD) { link = cycle->selected_view->cycle_link.next; - if (link == &server->cycle.views) { + if (link == &server.cycle.views) { link = link->next; } } else { link = cycle->selected_view->cycle_link.prev; - if (link == &server->cycle.views) { + if (link == &server.cycle.views) { link = link->prev; } } @@ -83,22 +83,22 @@ get_first_view(struct wl_list *views) } void -cycle_reinitialize(struct server *server) +cycle_reinitialize(void) { - struct cycle_state *cycle = &server->cycle; + struct cycle_state *cycle = &server.cycle; - if (server->input_mode != LAB_INPUT_STATE_CYCLE) { + if (server.input_mode != LAB_INPUT_STATE_CYCLE) { /* OSD not active, no need for clean up */ return; } struct view *selected_view = cycle->selected_view; struct view *selected_view_prev = - get_next_selected_view(server, LAB_CYCLE_DIR_BACKWARD); + get_next_selected_view(LAB_CYCLE_DIR_BACKWARD); struct cycle_filter filter = cycle->filter; - destroy_cycle(server); - if (init_cycle(server, filter)) { + destroy_cycle(); + if (init_cycle(filter)) { /* * Preserve the selected view (or its previous view) if it's * still in the cycle list @@ -110,104 +110,104 @@ cycle_reinitialize(struct server *server) } else { /* should be unreachable */ wlr_log(WLR_ERROR, "could not find view to select"); - cycle->selected_view = get_first_view(&server->cycle.views); + cycle->selected_view = get_first_view(&server.cycle.views); } - update_cycle(server); + update_cycle(); } else { /* Failed to re-init window switcher, exit */ - cycle_finish(server, /*switch_focus*/ false); + cycle_finish(/*switch_focus*/ false); } } void -cycle_on_cursor_release(struct server *server, struct wlr_scene_node *node) +cycle_on_cursor_release(struct wlr_scene_node *node) { - assert(server->input_mode == LAB_INPUT_STATE_CYCLE); + assert(server.input_mode == LAB_INPUT_STATE_CYCLE); struct cycle_osd_item *item = node_cycle_osd_item_from_node(node); - server->cycle.selected_view = item->view; - cycle_finish(server, /*switch_focus*/ true); + server.cycle.selected_view = item->view; + cycle_finish(/*switch_focus*/ true); } static void -restore_preview_node(struct server *server) +restore_preview_node(void) { - if (server->cycle.preview_node) { - wlr_scene_node_reparent(server->cycle.preview_node, - server->cycle.preview_dummy->parent); - wlr_scene_node_place_above(server->cycle.preview_node, - server->cycle.preview_dummy); - wlr_scene_node_destroy(server->cycle.preview_dummy); + if (server.cycle.preview_node) { + wlr_scene_node_reparent(server.cycle.preview_node, + server.cycle.preview_dummy->parent); + wlr_scene_node_place_above(server.cycle.preview_node, + server.cycle.preview_dummy); + wlr_scene_node_destroy(server.cycle.preview_dummy); /* Node was disabled / minimized before, disable again */ - if (!server->cycle.preview_was_enabled) { - wlr_scene_node_set_enabled(server->cycle.preview_node, false); + if (!server.cycle.preview_was_enabled) { + wlr_scene_node_set_enabled(server.cycle.preview_node, false); } - if (server->cycle.preview_was_shaded) { - struct view *view = node_view_from_node(server->cycle.preview_node); + if (server.cycle.preview_was_shaded) { + struct view *view = node_view_from_node(server.cycle.preview_node); view_set_shade(view, true); } - server->cycle.preview_node = NULL; - server->cycle.preview_dummy = NULL; - server->cycle.preview_was_enabled = false; - server->cycle.preview_was_shaded = false; + server.cycle.preview_node = NULL; + server.cycle.preview_dummy = NULL; + server.cycle.preview_was_enabled = false; + server.cycle.preview_was_shaded = false; } } void -cycle_begin(struct server *server, enum lab_cycle_dir direction, +cycle_begin(enum lab_cycle_dir direction, struct cycle_filter filter) { - if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) { + if (server.input_mode != LAB_INPUT_STATE_PASSTHROUGH) { return; } - if (!init_cycle(server, filter)) { + if (!init_cycle(filter)) { return; } - struct view *active_view = server->active_view; + struct view *active_view = server.active_view; if (active_view && active_view->cycle_link.next) { - /* Select the active view it's in the cycle list */ - server->cycle.selected_view = active_view; + /* Select the active view if it's in the cycle list */ + server.cycle.selected_view = active_view; } else { /* Otherwise, select the first view in the cycle list */ - server->cycle.selected_view = get_first_view(&server->cycle.views); + server.cycle.selected_view = get_first_view(&server.cycle.views); } /* Pre-select the next view in the given direction */ - server->cycle.selected_view = get_next_selected_view(server, direction); + server.cycle.selected_view = get_next_selected_view(direction); - seat_focus_override_begin(&server->seat, + seat_focus_override_begin(&server.seat, LAB_INPUT_STATE_CYCLE, LAB_CURSOR_DEFAULT); - update_cycle(server); + update_cycle(); /* Update cursor, in case it is within the area covered by OSD */ - cursor_update_focus(server); + cursor_update_focus(); } void -cycle_step(struct server *server, enum lab_cycle_dir direction) +cycle_step(enum lab_cycle_dir direction) { - assert(server->input_mode == LAB_INPUT_STATE_CYCLE); + assert(server.input_mode == LAB_INPUT_STATE_CYCLE); - server->cycle.selected_view = get_next_selected_view(server, direction); - update_cycle(server); + server.cycle.selected_view = get_next_selected_view(direction); + update_cycle(); } void -cycle_finish(struct server *server, bool switch_focus) +cycle_finish(bool switch_focus) { - if (server->input_mode != LAB_INPUT_STATE_CYCLE) { + if (server.input_mode != LAB_INPUT_STATE_CYCLE) { return; } - struct view *selected_view = server->cycle.selected_view; - destroy_cycle(server); + struct view *selected_view = server.cycle.selected_view; + destroy_cycle(); - seat_focus_override_end(&server->seat, /*restore_focus*/ false); + seat_focus_override_end(&server.seat, /*restore_focus*/ false); /* Hiding OSD may need a cursor change */ - cursor_update_focus(server); + cursor_update_focus(); if (switch_focus && selected_view) { if (rc.window_switcher.unshade) { @@ -222,16 +222,15 @@ preview_selected_view(struct view *view) { assert(view); assert(view->scene_tree); - struct server *server = view->server; - struct cycle_state *cycle = &server->cycle; + struct cycle_state *cycle = &server.cycle; /* Move previous selected node back to its original place */ - restore_preview_node(server); + restore_preview_node(); cycle->preview_node = &view->scene_tree->node; /* Create a dummy node at the original place of the previewed window */ - struct wlr_scene_rect *dummy_rect = wlr_scene_rect_create( + struct wlr_scene_rect *dummy_rect = lab_wlr_scene_rect_create( cycle->preview_node->parent, 0, 0, (float [4]) {0}); wlr_scene_node_place_below(&dummy_rect->node, cycle->preview_node); wlr_scene_node_set_enabled(&dummy_rect->node, false); @@ -246,7 +245,7 @@ preview_selected_view(struct view *view) } wlr_scene_node_reparent(cycle->preview_node, - view->server->cycle_preview_tree); + server.cycle_preview_tree); /* Finally raise selected node to the top */ wlr_scene_node_raise_to_top(cycle->preview_node); @@ -265,8 +264,7 @@ get_osd_impl(void) } static uint64_t -get_outputs_by_filter(struct server *server, - enum cycle_output_filter output_filter) +get_outputs_by_filter(enum cycle_output_filter output_filter) { struct output *output = NULL; @@ -274,15 +272,15 @@ get_outputs_by_filter(struct server *server, case CYCLE_OUTPUT_ALL: break; case CYCLE_OUTPUT_CURSOR: - output = output_nearest_to_cursor(server); + output = output_nearest_to_cursor(); break; case CYCLE_OUTPUT_FOCUSED: { - struct view *view = server->active_view; + struct view *view = server.active_view; if (view && output_is_usable(view->output)) { output = view->output; } else { /* Fallback to pointer */ - output = output_nearest_to_cursor(server); + output = output_nearest_to_cursor(); } break; } @@ -325,52 +323,111 @@ handle_osd_tree_destroy(struct wl_listener *listener, void *data) free(osd_output); } -/* Return false on failure */ -static bool -init_cycle(struct server *server, struct cycle_filter filter) +static enum lab_view_criteria +get_view_criteria(struct cycle_filter *filter) { enum lab_view_criteria criteria = LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER | LAB_VIEW_CRITERIA_NO_DIALOG; - if (filter.workspace == CYCLE_WORKSPACE_CURRENT) { + if (filter->workspace == CYCLE_WORKSPACE_CURRENT) { criteria |= LAB_VIEW_CRITERIA_CURRENT_WORKSPACE; } + return criteria; +} - uint64_t cycle_outputs = - get_outputs_by_filter(server, filter.output); +static const char * +get_cycle_app_id(struct cycle_filter *filter) +{ + if (filter->app_id == CYCLE_APP_ID_CURRENT && server.active_view) { + return server.active_view->app_id; + } + return NULL; +} - const char *cycle_app_id = NULL; - if (filter.app_id == CYCLE_APP_ID_CURRENT && server->active_view) { - cycle_app_id = server->active_view->app_id; +static struct wl_list *prev(struct wl_list *elm) { return elm->prev; } +static struct wl_list *next(struct wl_list *elm) { return elm->next; } + +void +cycle_immediate(enum lab_cycle_dir direction, struct cycle_filter filter) +{ + if (wl_list_empty(&server.views)) { + return; } - struct view *view; - for_each_view(view, &server->views, criteria) { - if (!(cycle_outputs & view->output->id_bit)) { + enum lab_view_criteria criteria = get_view_criteria(&filter); + uint64_t cycle_outputs = get_outputs_by_filter(filter.output); + const char *cycle_app_id = get_cycle_app_id(&filter); + + struct wl_list *head = &server.views; + struct wl_list *(*iter)(struct wl_list *list); + iter = direction == LAB_CYCLE_DIR_FORWARD ? next : prev; + + struct wl_list *from = (direction == LAB_CYCLE_DIR_FORWARD) && server.active_view + ? &server.active_view->link : head; + + for (struct wl_list *elm = iter(from); elm != head; elm = iter(elm)) { + struct view *view = wl_container_of(elm, view, link); + if (!view_matches_criteria(view, criteria)) { continue; } + if (filter.output != CYCLE_OUTPUT_ALL) { + if (!view->output || !(cycle_outputs & view->output->id_bit)) { + continue; + } + } + if (cycle_app_id && strcmp(view->app_id, cycle_app_id) != 0) { + continue; + } + if (server.active_view && direction == LAB_CYCLE_DIR_FORWARD) { + /* + * When cycling forward, the current active view needs to be + * sent to back to keep the same sequence and avoid getting + * stuck in the 2 topmost views. + */ + view_move_to_back(server.active_view); + } + desktop_focus_view(view, true); + break; + } + cursor_update_focus(); +} + +/* Return false on failure */ +static bool +init_cycle(struct cycle_filter filter) +{ + enum lab_view_criteria criteria = get_view_criteria(&filter); + uint64_t cycle_outputs = get_outputs_by_filter(filter.output); + const char *cycle_app_id = get_cycle_app_id(&filter); + + struct view *view; + for_each_view(view, &server.views, criteria) { + if (filter.output != CYCLE_OUTPUT_ALL) { + if (!view->output || !(cycle_outputs & view->output->id_bit)) { + continue; + } + } if (cycle_app_id && strcmp(view->app_id, cycle_app_id) != 0) { continue; } if (rc.window_switcher.order == WINDOW_SWITCHER_ORDER_AGE) { - insert_view_ordered_by_age(&server->cycle.views, view); + insert_view_ordered_by_age(&server.cycle.views, view); } else { - wl_list_append(&server->cycle.views, &view->cycle_link); + wl_list_append(&server.cycle.views, &view->cycle_link); } } - if (wl_list_empty(&server->cycle.views)) { + if (wl_list_empty(&server.cycle.views)) { wlr_log(WLR_DEBUG, "no views to switch between"); return false; } - server->cycle.filter = filter; + server.cycle.filter = filter; if (rc.window_switcher.osd.show) { /* Create OSD */ - uint64_t osd_outputs = get_outputs_by_filter(server, - rc.window_switcher.osd.output_filter); + uint64_t osd_outputs = get_outputs_by_filter(rc.window_switcher.osd.output_filter); struct output *output; - wl_list_for_each(output, &server->outputs, link) { + wl_list_for_each(output, &server.outputs, link) { if (!(osd_outputs & output->id_bit)) { continue; } @@ -379,7 +436,7 @@ init_cycle(struct server *server, struct cycle_filter filter) } struct cycle_osd_output *osd_output = znew(*osd_output); - wl_list_append(&server->cycle.osd_outputs, &osd_output->link); + wl_list_append(&server.cycle.osd_outputs, &osd_output->link); osd_output->output = output; wl_list_init(&osd_output->items); @@ -395,9 +452,9 @@ init_cycle(struct server *server, struct cycle_filter filter) } static void -update_cycle(struct server *server) +update_cycle(void) { - struct cycle_state *cycle = &server->cycle; + struct cycle_state *cycle = &server.cycle; if (rc.window_switcher.osd.show) { struct cycle_osd_output *osd_output; @@ -418,29 +475,29 @@ update_cycle(struct server *server) } } -/* Resets all the states in server->cycle */ +/* Resets all the states in server.cycle */ static void -destroy_cycle(struct server *server) +destroy_cycle(void) { struct cycle_osd_output *osd_output, *tmp; - wl_list_for_each_safe(osd_output, tmp, &server->cycle.osd_outputs, link) { + wl_list_for_each_safe(osd_output, tmp, &server.cycle.osd_outputs, link) { /* calls handle_osd_tree_destroy() */ wlr_scene_node_destroy(&osd_output->tree->node); } - restore_preview_node(server); + restore_preview_node(); - if (server->cycle.preview_outline) { - wlr_scene_node_destroy(&server->cycle.preview_outline->tree->node); + if (server.cycle.preview_outline) { + wlr_scene_node_destroy(&server.cycle.preview_outline->tree->node); } struct view *view, *tmp2; - wl_list_for_each_safe(view, tmp2, &server->cycle.views, cycle_link) { + wl_list_for_each_safe(view, tmp2, &server.cycle.views, cycle_link) { wl_list_remove(&view->cycle_link); view->cycle_link = (struct wl_list){0}; } - server->cycle = (struct cycle_state){0}; - wl_list_init(&server->cycle.views); - wl_list_init(&server->cycle.osd_outputs); + server.cycle = (struct cycle_state){0}; + wl_list_init(&server.cycle.views); + wl_list_init(&server.cycle.osd_outputs); } diff --git a/src/cycle/osd-classic.c b/src/cycle/osd-classic.c index c9b5497d..7e5abfb6 100644 --- a/src/cycle/osd-classic.c +++ b/src/cycle/osd-classic.c @@ -9,6 +9,7 @@ #include "common/lab-scene-rect.h" #include "common/list.h" #include "common/mem.h" +#include "common/scene-helpers.h" #include "common/string-helpers.h" #include "config/rcxml.h" #include "cycle.h" @@ -27,11 +28,11 @@ struct cycle_osd_classic_item { }; static void -create_fields_scene(struct server *server, struct view *view, +create_fields_scene(struct view *view, struct wlr_scene_tree *parent, const float *text_color, const float *bg_color, int field_widths_sum, int x, int y) { - struct theme *theme = server->theme; + struct theme *theme = rc.theme; struct window_switcher_classic_theme *switcher_theme = &theme->osd_window_switcher_classic; @@ -45,8 +46,7 @@ create_fields_scene(struct server *server, struct view *view, int icon_size = MIN(field_width, switcher_theme->item_icon_size); struct scaled_icon_buffer *icon_buffer = - scaled_icon_buffer_create(parent, - server, icon_size, icon_size); + scaled_icon_buffer_create(parent, icon_size, icon_size); scaled_icon_buffer_set_view(icon_buffer, view); node = &icon_buffer->scene_buffer->node; height = icon_size; @@ -80,17 +80,16 @@ static void cycle_osd_classic_init(struct cycle_osd_output *osd_output) { struct output *output = osd_output->output; - struct server *server = output->server; - struct theme *theme = server->theme; + struct theme *theme = rc.theme; struct window_switcher_classic_theme *switcher_theme = &theme->osd_window_switcher_classic; int padding = theme->osd_border_width + switcher_theme->padding; bool show_workspace = wl_list_length(&rc.workspace_config.workspaces) > 1; - const char *workspace_name = server->workspaces.current->name; - int nr_views = wl_list_length(&server->cycle.views); + const char *workspace_name = server.workspaces.current->name; + int nr_views = wl_list_length(&server.cycle.views); struct wlr_box output_box; - wlr_output_layout_get_box(server->output_layout, output->wlr_output, + wlr_output_layout_get_box(server.output_layout, output->wlr_output, &output_box); int w = switcher_theme->width; @@ -108,7 +107,7 @@ cycle_osd_classic_init(struct cycle_osd_output *osd_output) int h = workspace_name_h + nr_visible_views * switcher_theme->item_height + 2 * padding; - osd_output->tree = wlr_scene_tree_create(output->cycle_osd_tree); + osd_output->tree = lab_wlr_scene_tree_create(output->cycle_osd_tree); float *text_color = theme->osd_label_text_color; float *bg_color = theme->osd_bg_color; @@ -161,15 +160,15 @@ cycle_osd_classic_init(struct cycle_osd_output *osd_output) float *active_bg_color = switcher_theme->item_active_bg_color; float *active_border_color = switcher_theme->item_active_border_color; - osd_output->items_tree = wlr_scene_tree_create(osd_output->tree); + osd_output->items_tree = lab_wlr_scene_tree_create(osd_output->tree); /* Draw text for each node */ struct view *view; - wl_list_for_each(view, &server->cycle.views, cycle_link) { + wl_list_for_each(view, &server.cycle.views, cycle_link) { struct cycle_osd_classic_item *item = znew(*item); wl_list_append(&osd_output->items, &item->base.link); item->base.view = view; - item->base.tree = wlr_scene_tree_create(osd_output->items_tree); + item->base.tree = lab_wlr_scene_tree_create(osd_output->items_tree); node_descriptor_create(&item->base.tree->node, LAB_NODE_CYCLE_OSD_ITEM, NULL, item); /* @@ -191,8 +190,8 @@ cycle_osd_classic_init(struct cycle_osd_output *osd_output) int x = padding + switcher_theme->item_active_border_width + switcher_theme->item_padding_x; - item->normal_tree = wlr_scene_tree_create(item->base.tree); - item->active_tree = wlr_scene_tree_create(item->base.tree); + item->normal_tree = lab_wlr_scene_tree_create(item->base.tree); + item->active_tree = lab_wlr_scene_tree_create(item->base.tree); wlr_scene_node_set_enabled(&item->active_tree->node, false); /* Highlight around selected window's item */ @@ -209,13 +208,13 @@ cycle_osd_classic_init(struct cycle_osd_output *osd_output) wlr_scene_node_set_position(&highlight_rect->tree->node, padding, y); /* hitbox for mouse clicks */ - struct wlr_scene_rect *hitbox = wlr_scene_rect_create(item->base.tree, + struct wlr_scene_rect *hitbox = lab_wlr_scene_rect_create(item->base.tree, w - 2 * padding, switcher_theme->item_height, (float[4]) {0}); wlr_scene_node_set_position(&hitbox->node, padding, y); - create_fields_scene(server, view, item->normal_tree, + create_fields_scene(view, item->normal_tree, text_color, bg_color, field_widths_sum, x, y); - create_fields_scene(server, view, item->active_tree, + create_fields_scene(view, item->active_tree, text_color, active_bg_color, field_widths_sum, x, y); y += switcher_theme->item_height; @@ -242,12 +241,11 @@ error:; static void cycle_osd_classic_update(struct cycle_osd_output *osd_output) { - struct server *server = osd_output->output->server; cycle_osd_scroll_update(osd_output); struct cycle_osd_classic_item *item; wl_list_for_each(item, &osd_output->items, base.link) { - bool active = item->base.view == server->cycle.selected_view; + bool active = item->base.view == server.cycle.selected_view; wlr_scene_node_set_enabled(&item->normal_tree->node, !active); wlr_scene_node_set_enabled(&item->active_tree->node, active); } diff --git a/src/cycle/osd-field.c b/src/cycle/osd-field.c index cfc32811..5bc671ff 100644 --- a/src/cycle/osd-field.c +++ b/src/cycle/osd-field.c @@ -45,7 +45,7 @@ static const char * get_desktop_name(struct view *view) { #if HAVE_LIBSFDO - const char *name = desktop_entry_name_lookup(view->server, view->app_id); + const char *name = desktop_entry_name_lookup(view->app_id); if (name) { return name; } @@ -148,7 +148,7 @@ static void field_set_output_short(struct buf *buf, struct view *view, const char *format) { /* custom type conversion-specifier: o */ - if (wl_list_length(&view->server->outputs) > 1 && + if (wl_list_length(&server.outputs) > 1 && output_is_usable(view->output)) { buf_add(buf, view->output->wlr_output->name); } diff --git a/src/cycle/osd-scroll.c b/src/cycle/osd-scroll.c index 2bca8021..e788a479 100644 --- a/src/cycle/osd-scroll.c +++ b/src/cycle/osd-scroll.c @@ -2,6 +2,7 @@ #include #include #include "common/lab-scene-rect.h" +#include "common/scene-helpers.h" #include "labwc.h" #include "cycle.h" #include "output.h" @@ -23,7 +24,7 @@ cycle_osd_scroll_init(struct cycle_osd_output *osd_output, struct wlr_box bar_ar scroll->top_row_idx = 0; scroll->bar_area = bar_area; scroll->delta_y = delta_y; - scroll->bar_tree = wlr_scene_tree_create(osd_output->tree); + scroll->bar_tree = lab_wlr_scene_tree_create(osd_output->tree); wlr_scene_node_set_position(&scroll->bar_tree->node, bar_area.x, bar_area.y); @@ -41,12 +42,10 @@ cycle_osd_scroll_init(struct cycle_osd_output *osd_output, struct wlr_box bar_ar static int get_cycle_idx(struct cycle_osd_output *osd_output) { - struct server *server = osd_output->output->server; - int idx = 0; struct cycle_osd_item *item; wl_list_for_each(item, &osd_output->items, link) { - if (item->view == server->cycle.selected_view) { + if (item->view == server.cycle.selected_view) { return idx; } idx++; diff --git a/src/cycle/osd-thumbnail.c b/src/cycle/osd-thumbnail.c index d5a0bf3e..defedf09 100644 --- a/src/cycle/osd-thumbnail.c +++ b/src/cycle/osd-thumbnail.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include "config/rcxml.h" @@ -10,6 +11,7 @@ #include "common/lab-scene-rect.h" #include "common/list.h" #include "common/mem.h" +#include "common/scene-helpers.h" #include "cycle.h" #include "labwc.h" #include "node.h" @@ -27,7 +29,7 @@ struct cycle_osd_thumbnail_item { }; static void -render_node(struct server *server, struct wlr_render_pass *pass, +render_node(struct wlr_render_pass *pass, struct wlr_scene_node *node, int x, int y) { switch (node->type) { @@ -35,7 +37,7 @@ render_node(struct server *server, struct wlr_render_pass *pass, struct wlr_scene_tree *tree = wlr_scene_tree_from_node(node); struct wlr_scene_node *child; wl_list_for_each(child, &tree->children, link) { - render_node(server, pass, child, x + node->x, y + node->y); + render_node(pass, child, x + node->x, y + node->y); } break; } @@ -45,8 +47,12 @@ render_node(struct server *server, struct wlr_render_pass *pass, if (!scene_buffer->buffer) { break; } - struct wlr_texture *texture = wlr_texture_from_buffer( - server->renderer, scene_buffer->buffer); + struct wlr_texture *texture = NULL; + struct wlr_client_buffer *client_buffer = + wlr_client_buffer_get(scene_buffer->buffer); + if (client_buffer) { + texture = client_buffer->texture; + } if (!texture) { break; } @@ -61,7 +67,6 @@ render_node(struct server *server, struct wlr_render_pass *pass, }, .transform = scene_buffer->transform, }); - wlr_texture_destroy(texture); break; } case WLR_SCENE_NODE_RECT: @@ -81,13 +86,16 @@ render_thumb(struct output *output, struct view *view) */ return NULL; } - struct server *server = output->server; - struct wlr_buffer *buffer = wlr_allocator_create_buffer(server->allocator, + struct wlr_buffer *buffer = wlr_allocator_create_buffer(server.allocator, view->current.width, view->current.height, &output->wlr_output->swapchain->format); + if (!buffer) { + wlr_log(WLR_ERROR, "failed to allocate buffer for thumbnail"); + return NULL; + } struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass( - server->renderer, buffer, NULL); - render_node(server, pass, &view->content_tree->node, 0, 0); + server.renderer, buffer, NULL); + render_node(pass, &view->content_tree->node, 0, 0); if (!wlr_render_pass_submit(pass)) { wlr_log(WLR_ERROR, "failed to submit render pass"); wlr_buffer_drop(buffer); @@ -119,8 +127,7 @@ static struct cycle_osd_thumbnail_item * create_item_scene(struct wlr_scene_tree *parent, struct view *view, struct cycle_osd_output *osd_output) { - struct server *server = osd_output->output->server; - struct theme *theme = server->theme; + struct theme *theme = rc.theme; struct window_switcher_thumbnail_theme *switcher_theme = &theme->osd_window_switcher_thumbnail; int padding = theme->border_width + switcher_theme->item_padding; @@ -138,7 +145,7 @@ create_item_scene(struct wlr_scene_tree *parent, struct view *view, struct cycle_osd_thumbnail_item *item = znew(*item); wl_list_append(&osd_output->items, &item->base.link); - struct wlr_scene_tree *tree = wlr_scene_tree_create(parent); + struct wlr_scene_tree *tree = lab_wlr_scene_tree_create(parent); node_descriptor_create(&tree->node, LAB_NODE_CYCLE_OSD_ITEM, NULL, item); item->base.tree = tree; item->base.view = view; @@ -155,14 +162,14 @@ create_item_scene(struct wlr_scene_tree *parent, struct view *view, item->active_bg = lab_scene_rect_create(tree, &opts); /* hitbox for mouse clicks */ - wlr_scene_rect_create(tree, switcher_theme->item_width, + lab_wlr_scene_rect_create(tree, switcher_theme->item_width, switcher_theme->item_height, (float[4]) {0}); /* thumbnail */ struct wlr_buffer *thumb_buffer = render_thumb(osd_output->output, view); if (thumb_buffer) { struct wlr_scene_buffer *thumb_scene_buffer = - wlr_scene_buffer_create(tree, thumb_buffer); + lab_wlr_scene_buffer_create(tree, thumb_buffer); wlr_buffer_drop(thumb_buffer); struct wlr_box thumb_box = box_fit_within( thumb_buffer->width, thumb_buffer->height, @@ -184,7 +191,7 @@ create_item_scene(struct wlr_scene_tree *parent, struct view *view, /* icon */ int icon_size = switcher_theme->item_icon_size; struct scaled_icon_buffer *icon_buffer = - scaled_icon_buffer_create(tree, server, icon_size, icon_size); + scaled_icon_buffer_create(tree, icon_size, icon_size); scaled_icon_buffer_set_view(icon_buffer, view); int x = (switcher_theme->item_width - icon_size) / 2; int y = title_y - padding - icon_size + 10; /* slide by 10px */ @@ -197,7 +204,7 @@ static void get_items_geometry(struct output *output, int nr_thumbs, int *nr_cols, int *nr_rows, int *nr_visible_rows) { - struct theme *theme = output->server->theme; + struct theme *theme = rc.theme; struct window_switcher_thumbnail_theme *switcher_theme = &theme->osd_window_switcher_thumbnail; int output_width, output_height; @@ -233,16 +240,15 @@ static void cycle_osd_thumbnail_init(struct cycle_osd_output *osd_output) { struct output *output = osd_output->output; - struct server *server = output->server; - struct theme *theme = server->theme; + struct theme *theme = rc.theme; struct window_switcher_thumbnail_theme *switcher_theme = &theme->osd_window_switcher_thumbnail; int padding = theme->osd_border_width + switcher_theme->padding; - osd_output->tree = wlr_scene_tree_create(output->cycle_osd_tree); - osd_output->items_tree = wlr_scene_tree_create(osd_output->tree); + osd_output->tree = lab_wlr_scene_tree_create(output->cycle_osd_tree); + osd_output->items_tree = lab_wlr_scene_tree_create(osd_output->tree); - int nr_views = wl_list_length(&server->cycle.views); + int nr_views = wl_list_length(&server.cycle.views); assert(nr_views > 0); int nr_cols, nr_rows, nr_visible_rows; get_items_geometry(output, nr_views, &nr_cols, &nr_rows, &nr_visible_rows); @@ -250,7 +256,7 @@ cycle_osd_thumbnail_init(struct cycle_osd_output *osd_output) /* items */ struct view *view; int index = 0; - wl_list_for_each(view, &server->cycle.views, cycle_link) { + wl_list_for_each(view, &server.cycle.views, cycle_link) { struct cycle_osd_thumbnail_item *item = create_item_scene( osd_output->items_tree, view, osd_output); if (!item) { @@ -291,7 +297,7 @@ cycle_osd_thumbnail_init(struct cycle_osd_output *osd_output) /* center */ struct wlr_box output_box; - wlr_output_layout_get_box(server->output_layout, output->wlr_output, + wlr_output_layout_get_box(server.output_layout, output->wlr_output, &output_box); int lx = output_box.x + (output_box.width - bg_opts.width) / 2; int ly = output_box.y + (output_box.height - bg_opts.height) / 2; @@ -301,12 +307,11 @@ cycle_osd_thumbnail_init(struct cycle_osd_output *osd_output) static void cycle_osd_thumbnail_update(struct cycle_osd_output *osd_output) { - struct server *server = osd_output->output->server; cycle_osd_scroll_update(osd_output); struct cycle_osd_thumbnail_item *item; wl_list_for_each(item, &osd_output->items, base.link) { - bool active = (item->base.view == server->cycle.selected_view); + bool active = (item->base.view == server.cycle.selected_view); wlr_scene_node_set_enabled(&item->active_bg->tree->node, active); wlr_scene_node_set_enabled( &item->active_label->scene_buffer->node, active); diff --git a/src/debug.c b/src/debug.c index 62c90b52..fef4d819 100644 --- a/src/debug.c +++ b/src/debug.c @@ -89,10 +89,10 @@ get_view_part(struct view *view, struct wlr_scene_node *node) } static struct workspace * -get_workspace_from_node(struct server *server, struct wlr_scene_node *node) +get_workspace_from_node(struct wlr_scene_node *node) { struct workspace *workspace; - wl_list_for_each(workspace, &server->workspaces.all, link) { + wl_list_for_each(workspace, &server.workspaces.all, link) { if (&workspace->tree->node == node) { return workspace; } @@ -101,31 +101,31 @@ get_workspace_from_node(struct server *server, struct wlr_scene_node *node) } static const char * -get_special(struct server *server, struct wlr_scene_node *node) +get_special(struct wlr_scene_node *node) { struct wlr_scene_tree *grand_parent = node->parent ? node->parent->node.parent : NULL; struct wlr_scene_tree *grand_grand_parent = grand_parent ? grand_parent->node.parent : NULL; - if (node == &server->scene->tree.node) { - return "server->scene"; + if (node == &server.scene->tree.node) { + return "server.scene"; } - if (node == &server->menu_tree->node) { - return "server->menu_tree"; + if (node == &server.menu_tree->node) { + return "server.menu_tree"; } - if (node == &server->workspace_tree->node) { - return "server->workspace_tree"; + if (node == &server.workspace_tree->node) { + return "server.workspace_tree"; } - if (node->parent == server->workspace_tree) { - struct workspace *workspace = get_workspace_from_node(server, node); + if (node->parent == server.workspace_tree) { + struct workspace *workspace = get_workspace_from_node(node); if (workspace) { return workspace->name; } return "unknown workspace"; } - if (grand_parent == server->workspace_tree) { + if (grand_parent == server.workspace_tree) { struct workspace *workspace = - get_workspace_from_node(server, &node->parent->node); + get_workspace_from_node(&node->parent->node); if (workspace) { struct wlr_scene_tree **trees = workspace->view_trees; if (node == &trees[VIEW_LAYER_NORMAL]->node) { @@ -138,9 +138,9 @@ get_special(struct server *server, struct wlr_scene_node *node) } return "unknown tree"; } - if (node->parent == &server->scene->tree) { + if (node->parent == &server.scene->tree) { struct output *output; - wl_list_for_each(output, &server->outputs, link) { + wl_list_for_each(output, &server.outputs, link) { if (node == &output->cycle_osd_tree->node) { return "output->osd_tree"; } @@ -157,33 +157,33 @@ get_special(struct server *server, struct wlr_scene_node *node) } } } - if (node == &server->xdg_popup_tree->node) { - return "server->xdg_popup_tree"; + if (node == &server.xdg_popup_tree->node) { + return "server.xdg_popup_tree"; } - if (node == &server->seat.drag.icons->node) { + if (node == &server.seat.drag.icons->node) { return "seat->drag.icons"; } - if (server->seat.overlay.rect - && node == &server->seat.overlay.rect->tree->node) { + if (server.seat.overlay.rect + && node == &server.seat.overlay.rect->tree->node) { /* Created on-demand */ return "seat->overlay.rect"; } - if (server->seat.input_method_relay->popup_tree - && node == &server->seat.input_method_relay->popup_tree->node) { + if (server.seat.input_method_relay->popup_tree + && node == &server.seat.input_method_relay->popup_tree->node) { /* Created on-demand */ return "seat->im_relay->popup_tree"; } - if (server->cycle.preview_outline - && node == &server->cycle.preview_outline->tree->node) { + if (server.cycle.preview_outline + && node == &server.cycle.preview_outline->tree->node) { /* Created on-demand */ return "cycle_state->preview_outline"; } #if HAVE_XWAYLAND - if (node == &server->unmanaged_tree->node) { - return "server->unmanaged_tree"; + if (node == &server.unmanaged_tree->node) { + return "server.unmanaged_tree"; } #endif - if (grand_grand_parent == server->workspace_tree && node->data) { + if (grand_grand_parent == server.workspace_tree && node->data) { last_view = node_view_from_node(node); } const char *view_part = get_view_part(last_view, node); @@ -209,10 +209,10 @@ get_center_padding(const char *text, uint8_t max_width) } static void -dump_tree(struct server *server, struct wlr_scene_node *node, +dump_tree(struct wlr_scene_node *node, int pos, int x, int y) { - const char *type = get_special(server, node); + const char *type = get_special(node); if (pos) { printf("%*c+-- ", pos, ' '); @@ -234,13 +234,13 @@ dump_tree(struct server *server, struct wlr_scene_node *node, } printf("%.*s %*c %4d %4d [%p]\n", max_width - 1, type, padding, ' ', x, y, node); - if ((IGNORE_MENU && node == &server->menu_tree->node) + if ((IGNORE_MENU && node == &server.menu_tree->node) || (IGNORE_SSD && last_view && ssd_debug_is_root_node(last_view->ssd, node)) - || (IGNORE_CYCLE_PREVIEW_OUTLINE && server->cycle.preview_outline - && node == &server->cycle.preview_outline->tree->node) - || (IGNORE_SNAPPING_OVERLAY && server->seat.overlay.rect - && node == &server->seat.overlay.rect->tree->node)) { + || (IGNORE_CYCLE_PREVIEW_OUTLINE && server.cycle.preview_outline + && node == &server.cycle.preview_outline->tree->node) + || (IGNORE_SNAPPING_OVERLAY && server.seat.overlay.rect + && node == &server.seat.overlay.rect->tree->node)) { printf("%*c%s\n", pos + 4 + INDENT_SIZE, ' ', ""); return; } @@ -249,17 +249,17 @@ dump_tree(struct server *server, struct wlr_scene_node *node, struct wlr_scene_node *child; struct wlr_scene_tree *tree = wlr_scene_tree_from_node(node); wl_list_for_each(child, &tree->children, link) { - dump_tree(server, child, pos + INDENT_SIZE, + dump_tree(child, pos + INDENT_SIZE, x + child->x, y + child->y); } } } void -debug_dump_scene(struct server *server) +debug_dump_scene(void) { printf("\n"); - dump_tree(server, &server->scene->tree.node, 0, 0, 0); + dump_tree(&server.scene->tree.node, 0, 0, 0); printf("\n"); /* diff --git a/src/decorations/kde-deco.c b/src/decorations/kde-deco.c index bf746163..9b3a55f8 100644 --- a/src/decorations/kde-deco.c +++ b/src/decorations/kde-deco.c @@ -119,10 +119,10 @@ kde_server_decoration_update_default(void) } void -kde_server_decoration_init(struct server *server) +kde_server_decoration_init(void) { assert(!kde_deco_mgr); - kde_deco_mgr = wlr_server_decoration_manager_create(server->wl_display); + kde_deco_mgr = wlr_server_decoration_manager_create(server.wl_display); if (!kde_deco_mgr) { wlr_log(WLR_ERROR, "unable to create the kde server deco manager"); exit(EXIT_FAILURE); @@ -131,12 +131,12 @@ kde_server_decoration_init(struct server *server) wl_list_init(&decorations); kde_server_decoration_update_default(); - wl_signal_add(&kde_deco_mgr->events.new_decoration, &server->kde_server_decoration); - server->kde_server_decoration.notify = handle_new_server_decoration; + wl_signal_add(&kde_deco_mgr->events.new_decoration, &server.kde_server_decoration); + server.kde_server_decoration.notify = handle_new_server_decoration; } void -kde_server_decoration_finish(struct server *server) +kde_server_decoration_finish(void) { - wl_list_remove(&server->kde_server_decoration.link); + wl_list_remove(&server.kde_server_decoration.link); } diff --git a/src/decorations/xdg-deco.c b/src/decorations/xdg-deco.c index d9eb3918..e195e02f 100644 --- a/src/decorations/xdg-deco.c +++ b/src/decorations/xdg-deco.c @@ -115,22 +115,22 @@ xdg_toplevel_decoration(struct wl_listener *listener, void *data) } void -xdg_server_decoration_init(struct server *server) +xdg_server_decoration_init(void) { struct wlr_xdg_decoration_manager_v1 *xdg_deco_mgr = NULL; - xdg_deco_mgr = wlr_xdg_decoration_manager_v1_create(server->wl_display); + xdg_deco_mgr = wlr_xdg_decoration_manager_v1_create(server.wl_display); if (!xdg_deco_mgr) { wlr_log(WLR_ERROR, "unable to create the XDG deco manager"); exit(EXIT_FAILURE); } wl_signal_add(&xdg_deco_mgr->events.new_toplevel_decoration, - &server->xdg_toplevel_decoration); - server->xdg_toplevel_decoration.notify = xdg_toplevel_decoration; + &server.xdg_toplevel_decoration); + server.xdg_toplevel_decoration.notify = xdg_toplevel_decoration; } void -xdg_server_decoration_finish(struct server *server) +xdg_server_decoration_finish(void) { - wl_list_remove(&server->xdg_toplevel_decoration.link); + wl_list_remove(&server.xdg_toplevel_decoration.link); } diff --git a/src/desktop-entry.c b/src/desktop-entry.c index 5ba8a2f6..a708073e 100644 --- a/src/desktop-entry.c +++ b/src/desktop-entry.c @@ -55,7 +55,7 @@ log_handler(enum sfdo_log_level level, const char *fmt, va_list args, void *tag) } void -desktop_entry_init(struct server *server) +desktop_entry_init(void) { struct sfdo *sfdo = znew(*sfdo); @@ -134,7 +134,7 @@ desktop_entry_init(struct server *server) /* basedir_ctx is not referenced by other objects */ sfdo_basedir_ctx_destroy(basedir_ctx); - server->sfdo = sfdo; + server.sfdo = sfdo; return; err_icon_theme: @@ -155,9 +155,9 @@ err_basedir_ctx: } void -desktop_entry_finish(struct server *server) +desktop_entry_finish(void) { - struct sfdo *sfdo = server->sfdo; + struct sfdo *sfdo = server.sfdo; if (!sfdo) { return; } @@ -167,7 +167,7 @@ desktop_entry_finish(struct server *server) sfdo_icon_ctx_destroy(sfdo->icon_ctx); sfdo_desktop_ctx_destroy(sfdo->desktop_ctx); free(sfdo); - server->sfdo = NULL; + server.sfdo = NULL; } struct icon_ctx { @@ -348,14 +348,14 @@ convert_img_type(enum sfdo_icon_file_format fmt) } struct lab_img * -desktop_entry_load_icon(struct server *server, const char *icon_name, int size, float scale) +desktop_entry_load_icon(const char *icon_name, int size, float scale) { /* static analyzer isn't able to detect the NULL check in string_null_or_empty() */ if (string_null_or_empty(icon_name) || !icon_name) { return NULL; } - struct sfdo *sfdo = server->sfdo; + struct sfdo *sfdo = server.sfdo; if (!sfdo) { return NULL; } @@ -387,14 +387,13 @@ desktop_entry_load_icon(struct server *server, const char *icon_name, int size, } struct lab_img * -desktop_entry_load_icon_from_app_id(struct server *server, - const char *app_id, int size, float scale) +desktop_entry_load_icon_from_app_id(const char *app_id, int size, float scale) { if (string_null_or_empty(app_id)) { return NULL; } - struct sfdo *sfdo = server->sfdo; + struct sfdo *sfdo = server.sfdo; if (!sfdo) { return NULL; } @@ -405,22 +404,22 @@ desktop_entry_load_icon_from_app_id(struct server *server, icon_name = sfdo_desktop_entry_get_icon(entry, NULL); } - struct lab_img *img = desktop_entry_load_icon(server, icon_name, size, scale); + struct lab_img *img = desktop_entry_load_icon(icon_name, size, scale); if (!img) { /* Icon not defined in .desktop file or could not be loaded */ - img = desktop_entry_load_icon(server, app_id, size, scale); + img = desktop_entry_load_icon(app_id, size, scale); } return img; } const char * -desktop_entry_name_lookup(struct server *server, const char *app_id) +desktop_entry_name_lookup(const char *app_id) { if (string_null_or_empty(app_id)) { return NULL; } - struct sfdo *sfdo = server->sfdo; + struct sfdo *sfdo = server.sfdo; if (!sfdo) { return NULL; } diff --git a/src/desktop.c b/src/desktop.c index e5a33a78..57ef9e3c 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -9,11 +9,13 @@ #include #include #include "common/scene-helpers.h" +#include "config/rcxml.h" #include "dnd.h" #include "labwc.h" #include "layers.h" #include "node.h" #include "output.h" +#include "show-desktop.h" #include "ssd.h" #include "view.h" #include "workspaces.h" @@ -23,7 +25,7 @@ #endif void -desktop_arrange_all_views(struct server *server) +desktop_arrange_all_views(void) { /* * Adjust window positions/sizes. Skip views with no size since @@ -36,7 +38,7 @@ desktop_arrange_all_views(struct server *server) * views. */ struct view *view; - wl_list_for_each(view, &server->views, link) { + wl_list_for_each(view, &server.views, link) { if (!wlr_box_empty(&view->pending)) { view_adjust_for_layout_change(view); } @@ -46,16 +48,16 @@ desktop_arrange_all_views(struct server *server) static void set_or_offer_focus(struct view *view) { - struct seat *seat = &view->server->seat; + struct seat *seat = &server.seat; switch (view_wants_focus(view)) { case VIEW_WANTS_FOCUS_ALWAYS: - if (view->surface != seat->seat->keyboard_state.focused_surface) { + if (view->surface != seat->wlr_seat->keyboard_state.focused_surface) { seat_focus_surface(seat, view->surface); } break; case VIEW_WANTS_FOCUS_LIKELY: case VIEW_WANTS_FOCUS_UNLIKELY: - if (view->surface != seat->seat->keyboard_state.focused_surface) { + if (view->surface != seat->wlr_seat->keyboard_state.focused_surface) { view_offer_focus(view); } break; @@ -64,8 +66,51 @@ set_or_offer_focus(struct view *view) } } +static int +handle_auto_raise_timer(void *data) +{ + (void)data; + struct view *view = server.pending_auto_raise_view; + server.pending_auto_raise_view = NULL; + + if (view && view->mapped) { + view_move_to_front(view); + } + return 0; /* ignored per wl_event_loop docs */ +} + void -desktop_focus_view(struct view *view, bool raise) +desktop_cancel_pending_auto_raise(void) +{ + server.pending_auto_raise_view = NULL; + if (server.pending_auto_raise_timer) { + /* Disarm by setting to 0 ms */ + wl_event_source_timer_update(server.pending_auto_raise_timer, 0); + } +} + +static void +schedule_delayed_auto_raise(struct view *view) +{ + server.pending_auto_raise_view = view; + if (!server.pending_auto_raise_timer) { + server.pending_auto_raise_timer = + wl_event_loop_add_timer(server.wl_event_loop, + handle_auto_raise_timer, NULL); + } + wl_event_source_timer_update(server.pending_auto_raise_timer, + rc.raise_on_focus_delay_ms); +} + +/* + * The raise_on_focus_delay is only meant to dampen z-order churn from + * focus-follows-mouse cursor passes. Explicit focus changes (alt-tab, + * Focus action, xdg/xwayland activation, etc.) should raise immediately. + * allow_delay is therefore only set when the caller is the sloppy-focus + * path in desktop_focus_view_or_surface(). + */ +static void +desktop_focus_view_internal(struct view *view, bool raise, bool allow_delay) { assert(view); /* @@ -76,7 +121,7 @@ desktop_focus_view(struct view *view, bool raise) return; } - if (view->server->input_mode == LAB_INPUT_STATE_CYCLE) { + if (server.input_mode == LAB_INPUT_STATE_CYCLE) { wlr_log(WLR_DEBUG, "not focusing window while window switching"); return; } @@ -102,8 +147,17 @@ desktop_focus_view(struct view *view, bool raise) workspaces_switch_to(view->workspace, /*update_focus*/ false); } + /* + * A new focus change supersedes any pending auto-raise from a + * previous focus event, regardless of whether we raise now. + */ + desktop_cancel_pending_auto_raise(); if (raise) { - view_move_to_front(view); + if (allow_delay && rc.raise_on_focus_delay_ms > 0) { + schedule_delayed_auto_raise(view); + } else { + view_move_to_front(view); + } } /* @@ -113,6 +167,14 @@ desktop_focus_view(struct view *view, bool raise) */ struct view *dialog = view_get_modal_dialog(view); set_or_offer_focus(dialog ? dialog : view); + + show_desktop_reset(); +} + +void +desktop_focus_view(struct view *view, bool raise) +{ + desktop_focus_view_internal(view, raise, /*allow_delay*/ false); } /* TODO: focus layer-shell surfaces also? */ @@ -122,7 +184,7 @@ desktop_focus_view_or_surface(struct seat *seat, struct view *view, { assert(view || surface); if (view) { - desktop_focus_view(view, raise); + desktop_focus_view_internal(view, raise, /*allow_delay*/ true); #if HAVE_XWAYLAND } else { struct wlr_xwayland_surface *xsurface = @@ -135,10 +197,10 @@ desktop_focus_view_or_surface(struct seat *seat, struct view *view, } static struct view * -desktop_topmost_focusable_view(struct server *server) +desktop_topmost_focusable_view(void) { struct view *view; - for_each_view(view, &server->views, + for_each_view(view, &server.views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) { if (!view->minimized) { return view; @@ -148,9 +210,9 @@ desktop_topmost_focusable_view(struct server *server) } void -desktop_focus_topmost_view(struct server *server) +desktop_focus_topmost_view(void) { - struct view *view = desktop_topmost_focusable_view(server); + struct view *view = desktop_topmost_focusable_view(); if (view) { desktop_focus_view(view, /*raise*/ true); } else { @@ -158,49 +220,47 @@ desktop_focus_topmost_view(struct server *server) * Defocus previous focused surface/view if no longer * focusable (e.g. unmapped or on a different workspace). */ - seat_focus_surface(&server->seat, NULL); + seat_focus_surface(&server.seat, NULL); } } void desktop_focus_output(struct output *output) { - struct server *server = output->server; - - if (!output_is_usable(output) || server->input_mode + if (!output_is_usable(output) || server.input_mode != LAB_INPUT_STATE_PASSTHROUGH) { return; } struct view *view; - for_each_view(view, &server->views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) { + for_each_view(view, &server.views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) { if (view->outputs & output->id_bit) { desktop_focus_view(view, /*raise*/ false); - wlr_cursor_warp(server->seat.cursor, NULL, + wlr_cursor_warp(server.seat.cursor, NULL, view->current.x + view->current.width / 2, view->current.y + view->current.height / 2); - cursor_update_focus(server); + cursor_update_focus(); return; } } /* No view found on desired output */ struct wlr_box layout_box; - wlr_output_layout_get_box(server->output_layout, + wlr_output_layout_get_box(server.output_layout, output->wlr_output, &layout_box); - wlr_cursor_warp(server->seat.cursor, NULL, + wlr_cursor_warp(server.seat.cursor, NULL, layout_box.x + output->usable_area.x + output->usable_area.width / 2, layout_box.y + output->usable_area.y + output->usable_area.height / 2); - cursor_update_focus(server); + cursor_update_focus(); } void -desktop_update_top_layer_visibility(struct server *server) +desktop_update_top_layer_visibility(void) { struct view *view; struct output *output; uint32_t top = ZWLR_LAYER_SHELL_V1_LAYER_TOP; /* Enable all top layers */ - wl_list_for_each(output, &server->outputs, link) { + wl_list_for_each(output, &server.outputs, link) { if (!output_is_usable(output)) { continue; } @@ -212,7 +272,7 @@ desktop_update_top_layer_visibility(struct server *server) * any views above it */ uint64_t outputs_covered = 0; - for_each_view(view, &server->views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) { + for_each_view(view, &server.views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) { if (view->minimized) { continue; } @@ -262,22 +322,22 @@ avoid_edge_rounding_issues(struct cursor_context *ctx) /* TODO: make this less big and scary */ struct cursor_context -get_cursor_context(struct server *server) +get_cursor_context(void) { struct cursor_context ret = {.type = LAB_NODE_NONE}; - struct wlr_cursor *cursor = server->seat.cursor; + struct wlr_cursor *cursor = server.seat.cursor; /* Prevent drag icons to be on top of the hitbox detection */ - if (server->seat.drag.active) { - dnd_icons_show(&server->seat, false); + if (server.seat.drag.active) { + dnd_icons_show(&server.seat, false); } struct wlr_scene_node *node = - wlr_scene_node_at(&server->scene->tree.node, + wlr_scene_node_at(&server.scene->tree.node, cursor->x, cursor->y, &ret.sx, &ret.sy); - if (server->seat.drag.active) { - dnd_icons_show(&server->seat, true); + if (server.seat.drag.active) { + dnd_icons_show(&server.seat, true); } if (!node) { @@ -292,7 +352,7 @@ get_cursor_context(struct server *server) #if HAVE_XWAYLAND /* TODO: attach LAB_NODE_UNMANAGED node-descriptor to unmanaged surfaces */ if (node->type == WLR_SCENE_NODE_BUFFER) { - if (node->parent == server->unmanaged_tree) { + if (node->parent == server.unmanaged_tree) { ret.type = LAB_NODE_UNMANAGED; return ret; } diff --git a/src/dnd.c b/src/dnd.c index ff65cffa..5d7d050e 100644 --- a/src/dnd.c +++ b/src/dnd.c @@ -4,6 +4,7 @@ #include #include #include +#include "common/scene-helpers.h" #include "config/rcxml.h" #include "input/cursor.h" #include "labwc.h" /* for struct seat */ @@ -17,8 +18,8 @@ handle_drag_request(struct wl_listener *listener, void *data) struct wlr_seat_request_start_drag_event *event = data; if (wlr_seat_validate_pointer_grab_serial( - seat->seat, event->origin, event->serial)) { - wlr_seat_start_pointer_drag(seat->seat, event->drag, + seat->wlr_seat, event->origin, event->serial)) { + wlr_seat_start_pointer_drag(seat->wlr_seat, event->drag, event->serial); } else { wlr_data_source_destroy(event->drag->source); @@ -67,7 +68,7 @@ handle_drag_destroy(struct wl_listener *listener, void *data) return; } - struct cursor_context ctx = get_cursor_context(seat->server); + struct cursor_context ctx = get_cursor_context(); if (!ctx.surface) { return; } @@ -83,16 +84,16 @@ handle_drag_destroy(struct wl_listener *listener, void *data) void dnd_init(struct seat *seat) { - seat->drag.icons = wlr_scene_tree_create(&seat->server->scene->tree); + seat->drag.icons = lab_wlr_scene_tree_create(&server.scene->tree); wlr_scene_node_set_enabled(&seat->drag.icons->node, false); seat->drag.events.request.notify = handle_drag_request; seat->drag.events.start.notify = handle_drag_start; seat->drag.events.destroy.notify = handle_drag_destroy; - wl_signal_add(&seat->seat->events.request_start_drag, + wl_signal_add(&seat->wlr_seat->events.request_start_drag, &seat->drag.events.request); - wl_signal_add(&seat->seat->events.start_drag, &seat->drag.events.start); + wl_signal_add(&seat->wlr_seat->events.start_drag, &seat->drag.events.start); /* * destroy.notify is listened to in handle_drag_start() and reset in * handle_drag_destroy() diff --git a/src/edges.c b/src/edges.c index 56fb8bfa..4e5d7ca9 100644 --- a/src/edges.c +++ b/src/edges.c @@ -328,7 +328,7 @@ subtract_node_tree(struct wlr_scene_tree *tree, pixman_region32_t *available, } void -edges_calculate_visibility(struct server *server, struct view *ignored_view) +edges_calculate_visibility(struct view *ignored_view) { /* * The region stores the available output layout space @@ -353,17 +353,17 @@ edges_calculate_visibility(struct server *server, struct view *ignored_view) */ struct output *output; struct wlr_box layout_box; - wl_list_for_each(output, &server->outputs, link) { + wl_list_for_each(output, &server.outputs, link) { if (!output_is_usable(output)) { continue; } - wlr_output_layout_get_box(server->output_layout, + wlr_output_layout_get_box(server.output_layout, output->wlr_output, &layout_box); pixman_region32_union_rect(®ion, ®ion, layout_box.x, layout_box.y, layout_box.width, layout_box.height); } - subtract_node_tree(&server->scene->tree, ®ion, ignored_view); + subtract_node_tree(&server.scene->tree, ®ion, ignored_view); pixman_region32_fini(®ion); } @@ -389,7 +389,7 @@ edges_find_neighbors(struct border *nearest_edges, struct view *view, edges_for_target_geometry(&target_edges, view, target); struct view *v; - for_each_view(v, &view->server->views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) { + for_each_view(v, &server.views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) { if (v == view || v->minimized || !output_is_usable(v->output)) { continue; } @@ -447,7 +447,7 @@ edges_find_outputs(struct border *nearest_edges, struct view *view, edges_for_target_geometry(&target_edges, view, target); struct output *o; - wl_list_for_each(o, &view->server->outputs, link) { + wl_list_for_each(o, &server.outputs, link) { if (!output_is_usable(o)) { continue; } diff --git a/src/foreign-toplevel/ext-foreign.c b/src/foreign-toplevel/ext-foreign.c index 748050fa..1dfa1d96 100644 --- a/src/foreign-toplevel/ext-foreign.c +++ b/src/foreign-toplevel/ext-foreign.c @@ -59,7 +59,7 @@ void ext_foreign_toplevel_init(struct ext_foreign_toplevel *ext_toplevel, struct view *view) { - assert(view->server->foreign_toplevel_list); + assert(server.foreign_toplevel_list); ext_toplevel->view = view; struct wlr_ext_foreign_toplevel_handle_v1_state state = { @@ -67,7 +67,7 @@ ext_foreign_toplevel_init(struct ext_foreign_toplevel *ext_toplevel, .app_id = view->app_id, }; ext_toplevel->handle = wlr_ext_foreign_toplevel_handle_v1_create( - view->server->foreign_toplevel_list, &state); + server.foreign_toplevel_list, &state); if (!ext_toplevel->handle) { wlr_log(WLR_ERROR, "cannot create ext toplevel handle for (%s)", @@ -75,6 +75,9 @@ ext_foreign_toplevel_init(struct ext_foreign_toplevel *ext_toplevel, return; } + /* In support for ext-toplevel-capture */ + ext_toplevel->handle->data = view; + /* Client side requests */ ext_toplevel->on.handle_destroy.notify = handle_handle_destroy; wl_signal_add(&ext_toplevel->handle->events.destroy, &ext_toplevel->on.handle_destroy); diff --git a/src/foreign-toplevel/wlr-foreign.c b/src/foreign-toplevel/wlr-foreign.c index 70ce0890..3e589ef0 100644 --- a/src/foreign-toplevel/wlr-foreign.c +++ b/src/foreign-toplevel/wlr-foreign.c @@ -123,7 +123,7 @@ handle_new_outputs(struct wl_listener *listener, void *data) * wlr_foreign_toplevel handle the rest. */ struct output *output; - wl_list_for_each(output, &wlr_toplevel->view->server->outputs, link) { + wl_list_for_each(output, &server.outputs, link) { if (view_on_output(wlr_toplevel->view, output)) { wlr_foreign_toplevel_handle_v1_output_enter( wlr_toplevel->handle, output->wlr_output); @@ -184,11 +184,11 @@ void wlr_foreign_toplevel_init(struct wlr_foreign_toplevel *wlr_toplevel, struct view *view) { - assert(view->server->foreign_toplevel_manager); + assert(server.foreign_toplevel_manager); wlr_toplevel->view = view; wlr_toplevel->handle = wlr_foreign_toplevel_handle_v1_create( - view->server->foreign_toplevel_manager); + server.foreign_toplevel_manager); if (!wlr_toplevel->handle) { wlr_log(WLR_ERROR, "cannot create wlr foreign toplevel handle for (%s)", view->title); @@ -203,7 +203,7 @@ wlr_foreign_toplevel_init(struct wlr_foreign_toplevel *wlr_toplevel, handle_minimized(&wlr_toplevel->on_view.minimized, NULL); handle_fullscreened(&wlr_toplevel->on_view.fullscreened, NULL); handle_activated(&wlr_toplevel->on_view.activated, - &(bool){view == view->server->active_view}); + &(bool){view == server.active_view}); /* Client side requests */ CONNECT_SIGNAL(wlr_toplevel->handle, &wlr_toplevel->on, request_maximize); diff --git a/src/idle.c b/src/idle.c index b8e96c2d..8fc17366 100644 --- a/src/idle.c +++ b/src/idle.c @@ -84,7 +84,7 @@ idle_manager_create(struct wl_display *display) } void -idle_manager_notify_activity(struct wlr_seat *seat) +idle_manager_notify_activity(struct wlr_seat *wlr_seat) { /* * The display destroy event might have been triggered @@ -96,5 +96,5 @@ idle_manager_notify_activity(struct wlr_seat *seat) return; } - wlr_idle_notifier_v1_notify_activity(manager->ext, seat); + wlr_idle_notifier_v1_notify_activity(manager->ext, wlr_seat); } diff --git a/src/img/img-xbm.c b/src/img/img-xbm.c index 91269f64..3771444a 100644 --- a/src/img/img-xbm.c +++ b/src/img/img-xbm.c @@ -226,7 +226,7 @@ out: /* * Openbox built-in icons are not bigger than 8x8, so have only written this - * function to cope wit that max size + * function to cope with that max size */ #define LABWC_BUILTIN_ICON_MAX_SIZE (8) diff --git a/src/img/img-xpm.c b/src/img/img-xpm.c index 0d251b49..3bc7a9bb 100644 --- a/src/img/img-xpm.c +++ b/src/img/img-xpm.c @@ -351,7 +351,7 @@ xpm_load_to_surface(struct file_handle *handle) goto out; } - for (int n = 0, xcnt = 0; n < wbytes; n += cpp, xcnt++) { + for (int n = 0; n < wbytes; n += cpp) { g_strlcpy(pixel_str, &buffer[n], cpp + 1); struct xpm_color *color = diff --git a/src/input/cursor.c b/src/input/cursor.c index 98679565..e8ac7d96 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -3,7 +3,7 @@ #include "input/cursor.h" #include #include -#include +#include #include #include #include @@ -38,6 +38,10 @@ #include "view.h" #include "xwayland.h" +#if WLR_HAS_LIBINPUT_BACKEND + #include +#endif + #define LAB_CURSOR_SHAPE_V1_VERSION 1 struct constraint { @@ -148,7 +152,7 @@ handle_request_set_cursor(struct wl_listener *listener, void *data) { struct seat *seat = wl_container_of(listener, seat, request_set_cursor); - if (seat->server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) { + if (server.input_mode != LAB_INPUT_STATE_PASSTHROUGH) { /* Prevent setting a cursor image when moving or resizing */ return; } @@ -180,7 +184,7 @@ handle_request_set_cursor(struct wl_listener *listener, void *data) */ struct wlr_seat_pointer_request_set_cursor_event *event = data; struct wlr_seat_client *focused_client = - seat->seat->pointer_state.focused_client; + seat->wlr_seat->pointer_state.focused_client; /* * This can be sent by any client, so we check to make sure this one @@ -205,10 +209,10 @@ handle_request_set_shape(struct wl_listener *listener, void *data) struct wlr_cursor_shape_manager_v1_request_set_shape_event *event = data; const char *shape_name = wlr_cursor_shape_v1_name(event->shape); struct seat *seat = wl_container_of(listener, seat, request_set_shape); - struct wlr_seat_client *focused_client = seat->seat->pointer_state.focused_client; + struct wlr_seat_client *focused_client = seat->wlr_seat->pointer_state.focused_client; /* Prevent setting a cursor image when moving or resizing */ - if (seat->server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) { + if (server.input_mode != LAB_INPUT_STATE_PASSTHROUGH) { return; } @@ -250,7 +254,7 @@ handle_request_set_selection(struct wl_listener *listener, void *data) struct seat *seat = wl_container_of( listener, seat, request_set_selection); struct wlr_seat_request_set_selection_event *event = data; - wlr_seat_set_selection(seat->seat, event->source, + wlr_seat_set_selection(seat->wlr_seat, event->source, event->serial); } @@ -260,17 +264,17 @@ handle_request_set_primary_selection(struct wl_listener *listener, void *data) struct seat *seat = wl_container_of( listener, seat, request_set_primary_selection); struct wlr_seat_request_set_primary_selection_event *event = data; - wlr_seat_set_primary_selection(seat->seat, event->source, + wlr_seat_set_primary_selection(seat->wlr_seat, event->source, event->serial); } static void -process_cursor_move(struct server *server, uint32_t time) +process_cursor_move(uint32_t time) { - struct view *view = server->grabbed_view; + struct view *view = server.grabbed_view; - int x = server->grab_box.x + (server->seat.cursor->x - server->grab_x); - int y = server->grab_box.y + (server->seat.cursor->y - server->grab_y); + int x = server.grab_box.x + (server.seat.cursor->x - server.grab_x); + int y = server.grab_box.y + (server.seat.cursor->y - server.grab_y); /* Apply resistance for maximized/tiled view */ bool needs_untile = resistance_unsnap_apply(view, &x, &y); @@ -286,7 +290,7 @@ process_cursor_move(struct server *server, uint32_t time) .width = view->natural_geometry.width, .height = view->natural_geometry.height, }; - interactive_anchor_to_cursor(server, &new_geo); + interactive_anchor_to_cursor(&new_geo); /* Shaded clients will not process resize events until unshaded */ view_set_shade(view, false); view_set_maximized(view, VIEW_AXIS_NONE); @@ -300,18 +304,18 @@ process_cursor_move(struct server *server, uint32_t time) resistance_move_apply(view, &x, &y); view_move(view, x, y); - overlay_update(&server->seat); + overlay_update(&server.seat); } static void -process_cursor_resize(struct server *server, uint32_t time) +process_cursor_resize(uint32_t time) { /* Rate-limit resize events respecting monitor refresh rate */ static uint32_t last_resize_time = 0; static struct view *last_resize_view = NULL; - assert(server->grabbed_view); - if (server->grabbed_view == last_resize_view) { + assert(server.grabbed_view); + if (server.grabbed_view == last_resize_view) { int32_t refresh = 0; if (output_is_usable(last_resize_view->output)) { refresh = last_resize_view->output->wlr_output->refresh; @@ -327,43 +331,43 @@ process_cursor_resize(struct server *server, uint32_t time) } last_resize_time = time; - last_resize_view = server->grabbed_view; + last_resize_view = server.grabbed_view; - double dx = server->seat.cursor->x - server->grab_x; - double dy = server->seat.cursor->y - server->grab_y; + double dx = server.seat.cursor->x - server.grab_x; + double dy = server.seat.cursor->y - server.grab_y; - struct view *view = server->grabbed_view; + struct view *view = server.grabbed_view; struct wlr_box new_view_geo = view->current; - if (server->resize_edges & LAB_EDGE_TOP) { + if (server.resize_edges & LAB_EDGE_TOP) { /* Shift y to anchor bottom edge when resizing top */ - new_view_geo.y = server->grab_box.y + dy; - new_view_geo.height = server->grab_box.height - dy; - } else if (server->resize_edges & LAB_EDGE_BOTTOM) { - new_view_geo.height = server->grab_box.height + dy; + new_view_geo.y = server.grab_box.y + dy; + new_view_geo.height = server.grab_box.height - dy; + } else if (server.resize_edges & LAB_EDGE_BOTTOM) { + new_view_geo.height = server.grab_box.height + dy; } - if (server->resize_edges & LAB_EDGE_LEFT) { + if (server.resize_edges & LAB_EDGE_LEFT) { /* Shift x to anchor right edge when resizing left */ - new_view_geo.x = server->grab_box.x + dx; - new_view_geo.width = server->grab_box.width - dx; - } else if (server->resize_edges & LAB_EDGE_RIGHT) { - new_view_geo.width = server->grab_box.width + dx; + new_view_geo.x = server.grab_box.x + dx; + new_view_geo.width = server.grab_box.width - dx; + } else if (server.resize_edges & LAB_EDGE_RIGHT) { + new_view_geo.width = server.grab_box.width + dx; } resistance_resize_apply(view, &new_view_geo); view_adjust_size(view, &new_view_geo.width, &new_view_geo.height); - if (server->resize_edges & LAB_EDGE_TOP) { + if (server.resize_edges & LAB_EDGE_TOP) { /* After size adjustments, make sure to anchor bottom edge */ - new_view_geo.y = server->grab_box.y + - server->grab_box.height - new_view_geo.height; + new_view_geo.y = server.grab_box.y + + server.grab_box.height - new_view_geo.height; } - if (server->resize_edges & LAB_EDGE_LEFT) { + if (server.resize_edges & LAB_EDGE_LEFT) { /* After size adjustments, make sure to anchor bottom right */ - new_view_geo.x = server->grab_box.x + - server->grab_box.width - new_view_geo.width; + new_view_geo.x = server.grab_box.x + + server.grab_box.width - new_view_geo.width; } if (rc.resize_draw_contents) { @@ -418,11 +422,11 @@ cursor_update_image(struct seat *seat) * a new output instance), we have to force a re-enter of * the surface so the client sets its own cursor again. */ - if (seat->seat->pointer_state.focused_surface) { + if (seat->wlr_seat->pointer_state.focused_surface) { seat->server_cursor = LAB_CURSOR_DEFAULT; wlr_cursor_set_xcursor(seat->cursor, seat->xcursor_manager, ""); - wlr_seat_pointer_clear_focus(seat->seat); - cursor_update_focus(seat->server); + wlr_seat_pointer_clear_focus(seat->wlr_seat); + cursor_update_focus(); } return; } @@ -515,7 +519,7 @@ update_pressed_surface(struct seat *seat, const struct cursor_context *ctx) * context menus (in contrast) do not use an XDG popup grab and * do not work properly if we send leave/enter events. */ - if (!wlr_seat_pointer_has_grab(seat->seat)) { + if (!wlr_seat_pointer_has_grab(seat->wlr_seat)) { return false; } if (seat->pressed.ctx.surface && ctx->surface != seat->pressed.ctx.surface) { @@ -533,15 +537,15 @@ update_pressed_surface(struct seat *seat, const struct cursor_context *ctx) * and process_cursor_axis() */ static void -cursor_update_common(struct server *server, const struct cursor_context *ctx, +cursor_update_common(const struct cursor_context *ctx, struct cursor_context *notified_ctx) { - struct seat *seat = &server->seat; - struct wlr_seat *wlr_seat = seat->seat; + struct seat *seat = &server.seat; + struct wlr_seat *wlr_seat = seat->wlr_seat; - ssd_update_hovered_button(server, ctx->node); + ssd_update_hovered_button(ctx->node); - if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) { + if (server.input_mode != LAB_INPUT_STATE_PASSTHROUGH) { /* * Prevent updating focus/cursor image during * interactive move/resize, window switcher and @@ -565,8 +569,8 @@ cursor_update_common(struct server *server, const struct cursor_context *ctx, int lx, ly; wlr_scene_node_coords(seat->pressed.ctx.node, &lx, &ly); *notified_ctx = seat->pressed.ctx; - notified_ctx->sx = server->seat.cursor->x - lx; - notified_ctx->sy = server->seat.cursor->y - ly; + notified_ctx->sx = server.seat.cursor->x - lx; + notified_ctx->sy = server.seat.cursor->y - ly; } return; } @@ -604,7 +608,7 @@ cursor_update_common(struct server *server, const struct cursor_context *ctx, } enum lab_edge -cursor_get_resize_edges(struct wlr_cursor *cursor, struct cursor_context *ctx) +cursor_get_resize_edges(struct wlr_cursor *cursor, const struct cursor_context *ctx) { enum lab_edge resize_edges = node_type_to_edges(ctx->type); if (ctx->view && !resize_edges) { @@ -620,24 +624,24 @@ cursor_get_resize_edges(struct wlr_cursor *cursor, struct cursor_context *ctx) } bool -cursor_process_motion(struct server *server, uint32_t time, double *sx, double *sy) +cursor_process_motion(uint32_t time, double *sx, double *sy) { /* If the mode is non-passthrough, delegate to those functions. */ - if (server->input_mode == LAB_INPUT_STATE_MOVE) { - process_cursor_move(server, time); + if (server.input_mode == LAB_INPUT_STATE_MOVE) { + process_cursor_move(time); return false; - } else if (server->input_mode == LAB_INPUT_STATE_RESIZE) { - process_cursor_resize(server, time); + } else if (server.input_mode == LAB_INPUT_STATE_RESIZE) { + process_cursor_resize(time); return false; } /* Otherwise, find view under the pointer and send the event along */ - struct cursor_context ctx = get_cursor_context(server); - struct seat *seat = &server->seat; + struct cursor_context ctx = get_cursor_context(); + struct seat *seat = &server.seat; if (ctx.type == LAB_NODE_MENUITEM) { menu_process_cursor_motion(ctx.node); - cursor_set(&server->seat, LAB_CURSOR_DEFAULT); + cursor_set(&server.seat, LAB_CURSOR_DEFAULT); return false; } @@ -659,7 +663,7 @@ cursor_process_motion(struct server *server, uint32_t time, double *sx, double * * moving/resizing the wrong view */ mousebind->pressed_in_context = false; - actions_run(seat->pressed.ctx.view, server, + actions_run(seat->pressed.ctx.view, &mousebind->actions, &seat->pressed.ctx); } } @@ -670,7 +674,7 @@ cursor_process_motion(struct server *server, uint32_t time, double *sx, double * * and the pressed view is set while out-of-surface dragging. */ struct cursor_context notified_ctx = {0}; - cursor_update_common(server, &ctx, ¬ified_ctx); + cursor_update_common(&ctx, ¬ified_ctx); if (rc.focus_follow_mouse) { /* @@ -701,10 +705,10 @@ cursor_process_motion(struct server *server, uint32_t time, double *sx, double * } static void -_cursor_update_focus(struct server *server) +_cursor_update_focus(void) { /* Focus surface under cursor if it isn't already focused */ - struct cursor_context ctx = get_cursor_context(server); + struct cursor_context ctx = get_cursor_context(); if ((ctx.view || ctx.surface) && rc.focus_follow_mouse && !rc.focus_follow_mouse_requires_movement) { @@ -712,21 +716,21 @@ _cursor_update_focus(struct server *server) * Always focus the surface below the cursor when * followMouse=yes and followMouseRequiresMovement=no. */ - desktop_focus_view_or_surface(&server->seat, ctx.view, + desktop_focus_view_or_surface(&server.seat, ctx.view, ctx.surface, rc.raise_on_focus); } - cursor_update_common(server, &ctx, NULL); + cursor_update_common(&ctx, NULL); } void -cursor_update_focus(struct server *server) +cursor_update_focus(void) { /* Prevent recursion via view_move_to_front() */ static bool updating_focus = false; if (!updating_focus) { updating_focus = true; - _cursor_update_focus(server); + _cursor_update_focus(); updating_focus = false; } } @@ -735,7 +739,7 @@ static void warp_cursor_to_constraint_hint(struct seat *seat, struct wlr_pointer_constraint_v1 *constraint) { - if (!seat->server->active_view) { + if (!server.active_view) { return; } @@ -744,11 +748,11 @@ warp_cursor_to_constraint_hint(struct seat *seat, double sx = constraint->current.cursor_hint.x; double sy = constraint->current.cursor_hint.y; wlr_cursor_warp(seat->cursor, NULL, - seat->server->active_view->current.x + sx, - seat->server->active_view->current.y + sy); + server.active_view->current.x + sx, + server.active_view->current.y + sy); /* Make sure we are not sending unnecessary surface movements */ - wlr_seat_pointer_warp(seat->seat, sx, sy); + wlr_seat_pointer_warp(seat->wlr_seat, sx, sy); } } @@ -788,26 +792,24 @@ void create_constraint(struct wl_listener *listener, void *data) { struct wlr_pointer_constraint_v1 *wlr_constraint = data; - struct server *server = wl_container_of(listener, server, - new_constraint); struct constraint *constraint = znew(*constraint); constraint->constraint = wlr_constraint; - constraint->seat = &server->seat; + constraint->seat = &server.seat; constraint->destroy.notify = handle_constraint_destroy; wl_signal_add(&wlr_constraint->events.destroy, &constraint->destroy); - struct view *view = server->active_view; + struct view *view = server.active_view; if (view && view->surface == wlr_constraint->surface) { - constrain_cursor(server, wlr_constraint); + constrain_cursor(wlr_constraint); } } void -constrain_cursor(struct server *server, struct wlr_pointer_constraint_v1 +constrain_cursor(struct wlr_pointer_constraint_v1 *constraint) { - struct seat *seat = &server->seat; + struct seat *seat = &server.seat; if (seat->current_constraint == constraint) { return; } @@ -837,7 +839,7 @@ constrain_cursor(struct server *server, struct wlr_pointer_constraint_v1 static void apply_constraint(struct seat *seat, struct wlr_pointer *pointer, double *x, double *y) { - if (!seat->server->active_view) { + if (!server.active_view) { return; } if (!seat->current_constraint @@ -850,8 +852,8 @@ apply_constraint(struct seat *seat, struct wlr_pointer *pointer, double *x, doub double sx = seat->cursor->x; double sy = seat->cursor->y; - sx -= seat->server->active_view->current.x; - sy -= seat->server->active_view->current.y; + sx -= server.active_view->current.x; + sy -= server.active_view->current.y; double sx_confined, sy_confined; if (!wlr_region_confine(&seat->current_constraint->region, sx, sy, @@ -870,7 +872,7 @@ cursor_locked(struct seat *seat, struct wlr_pointer *pointer) && pointer->base.type == WLR_INPUT_DEVICE_POINTER && seat->current_constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED && seat->current_constraint->surface - == seat->seat->pointer_state.focused_surface; + == seat->wlr_seat->pointer_state.focused_surface; } static void @@ -892,14 +894,15 @@ preprocess_cursor_motion(struct seat *seat, struct wlr_pointer *pointer, */ wlr_cursor_move(seat->cursor, &pointer->base, dx, dy); double sx, sy; - bool notify = cursor_process_motion(seat->server, time_msec, &sx, &sy); + bool notify = cursor_process_motion(time_msec, &sx, &sy); if (notify) { - wlr_seat_pointer_notify_motion(seat->seat, time_msec, sx, sy); + wlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy); } } static double get_natural_scroll_factor(struct wlr_input_device *wlr_input_device) { +#if WLR_HAS_LIBINPUT_BACKEND if (wlr_input_device_is_libinput(wlr_input_device)) { struct libinput_device *libinput_device = wlr_libinput_get_device_handle(wlr_input_device); @@ -907,7 +910,7 @@ static double get_natural_scroll_factor(struct wlr_input_device *wlr_input_devic return -1.0; } } - +#endif return 1.0; } @@ -919,9 +922,8 @@ handle_motion(struct wl_listener *listener, void *data) * _relative_ pointer motion event (i.e. a delta) */ struct seat *seat = wl_container_of(listener, seat, on_cursor.motion); - struct server *server = seat->server; struct wlr_pointer_motion_event *event = data; - idle_manager_notify_activity(seat->seat); + idle_manager_notify_activity(seat->wlr_seat); cursor_set_visible(seat, /* visible */ true); if (seat->cursor_scroll_wheel_emulation) { @@ -952,8 +954,8 @@ handle_motion(struct wl_listener *listener, void *data) WL_POINTER_AXIS_SOURCE_CONTINUOUS, event->time_msec); } else { wlr_relative_pointer_manager_v1_send_relative_motion( - server->relative_pointer_manager, - seat->seat, (uint64_t)event->time_msec * 1000, + server.relative_pointer_manager, + seat->wlr_seat, (uint64_t)event->time_msec * 1000, event->delta_x, event->delta_y, event->unaccel_dx, event->unaccel_dy); @@ -975,7 +977,7 @@ handle_motion_absolute(struct wl_listener *listener, void *data) */ struct seat *seat = wl_container_of(listener, seat, on_cursor.motion_absolute); struct wlr_pointer_motion_absolute_event *event = data; - idle_manager_notify_activity(seat->seat); + idle_manager_notify_activity(seat->wlr_seat); cursor_set_visible(seat, /* visible */ true); double lx, ly; @@ -990,15 +992,14 @@ handle_motion_absolute(struct wl_listener *listener, void *data) } static void -process_release_mousebinding(struct server *server, - struct cursor_context *ctx, uint32_t button) +process_release_mousebinding(struct cursor_context *ctx, uint32_t button) { - if (server->input_mode == LAB_INPUT_STATE_CYCLE) { + if (server.input_mode == LAB_INPUT_STATE_CYCLE) { return; } struct mousebind *mousebind; - uint32_t modifiers = keyboard_get_all_modifiers(&server->seat); + uint32_t modifiers = keyboard_get_all_modifiers(&server.seat); wl_list_for_each(mousebind, &rc.mousebinds, link) { if (ctx->type == LAB_NODE_CLIENT @@ -1019,7 +1020,7 @@ process_release_mousebinding(struct server *server, default: continue; } - actions_run(ctx->view, server, &mousebind->actions, ctx); + actions_run(ctx->view, &mousebind->actions, ctx); } } } @@ -1059,17 +1060,17 @@ is_double_click(long double_click_speed, uint32_t button, } static bool -process_press_mousebinding(struct server *server, struct cursor_context *ctx, +process_press_mousebinding(struct cursor_context *ctx, uint32_t button) { - if (server->input_mode == LAB_INPUT_STATE_CYCLE) { + if (server.input_mode == LAB_INPUT_STATE_CYCLE) { return false; } struct mousebind *mousebind; bool double_click = is_double_click(rc.doubleclick_time, button, ctx); bool consumed_by_frame_context = false; - uint32_t modifiers = keyboard_get_all_modifiers(&server->seat); + uint32_t modifiers = keyboard_get_all_modifiers(&server.seat); wl_list_for_each(mousebind, &rc.mousebinds, link) { if (ctx->type == LAB_NODE_CLIENT @@ -1108,7 +1109,7 @@ process_press_mousebinding(struct server *server, struct cursor_context *ctx, } consumed_by_frame_context |= mousebind->context == LAB_NODE_FRAME; consumed_by_frame_context |= mousebind->context == LAB_NODE_ALL; - actions_run(ctx->view, server, &mousebind->actions, ctx); + actions_run(ctx->view, &mousebind->actions, ctx); } } return consumed_by_frame_context; @@ -1138,8 +1139,7 @@ static uint32_t press_msec; bool cursor_process_button_press(struct seat *seat, uint32_t button, uint32_t time_msec) { - struct server *server = seat->server; - struct cursor_context ctx = get_cursor_context(server); + struct cursor_context ctx = get_cursor_context(); /* Used on next button release to check if it can close menu or select menu item */ press_msec = time_msec; @@ -1150,7 +1150,7 @@ cursor_process_button_press(struct seat *seat, uint32_t button, uint32_t time_ms interactive_set_grab_context(&ctx); } - if (server->input_mode == LAB_INPUT_STATE_MENU) { + if (server.input_mode == LAB_INPUT_STATE_MENU) { /* * If menu was already opened on press, set a very small value * so subsequent release always closes menu or selects menu item. @@ -1171,7 +1171,7 @@ cursor_process_button_press(struct seat *seat, uint32_t button, uint32_t time_ms if (layer && layer->current.keyboard_interactive) { layer_try_set_focus(seat, layer); } -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND } else if (ctx.type == LAB_NODE_UNMANAGED) { desktop_focus_view_or_surface(seat, NULL, ctx.surface, /*raise*/ false); @@ -1179,7 +1179,7 @@ cursor_process_button_press(struct seat *seat, uint32_t button, uint32_t time_ms } if (ctx.type != LAB_NODE_CLIENT && ctx.type != LAB_NODE_LAYER_SURFACE - && wlr_seat_pointer_has_grab(seat->seat)) { + && wlr_seat_pointer_has_grab(seat->wlr_seat)) { /* * If we have an active popup grab (an open popup) we want to * cancel that grab whenever the user presses on anything that @@ -1188,14 +1188,14 @@ cursor_process_button_press(struct seat *seat, uint32_t button, uint32_t time_ms * * Note: This does not work for XWayland clients */ - wlr_seat_pointer_end_grab(seat->seat); + wlr_seat_pointer_end_grab(seat->wlr_seat); lab_set_add(&seat->bound_buttons, button); return false; } /* Bindings to the Frame context swallow mouse events if activated */ bool consumed_by_frame_context = - process_press_mousebinding(server, &ctx, button); + process_press_mousebinding(&ctx, button); if (ctx.surface && !consumed_by_frame_context) { /* Notify client with pointer focus of button press */ @@ -1210,8 +1210,7 @@ bool cursor_process_button_release(struct seat *seat, uint32_t button, uint32_t time_msec) { - struct server *server = seat->server; - struct cursor_context ctx = get_cursor_context(server); + struct cursor_context ctx = get_cursor_context(); struct wlr_surface *pressed_surface = seat->pressed.ctx.surface; /* Always notify button release event when it's not bound */ @@ -1219,26 +1218,26 @@ cursor_process_button_release(struct seat *seat, uint32_t button, cursor_context_save(&seat->pressed, NULL); - if (server->input_mode == LAB_INPUT_STATE_MENU) { + if (server.input_mode == LAB_INPUT_STATE_MENU) { /* TODO: take into account overflow of time_msec */ if (time_msec - press_msec > rc.menu_ignore_button_release_period) { if (ctx.type == LAB_NODE_MENUITEM) { - menu_call_selected_actions(server); + menu_call_selected_actions(); } else { - menu_close_root(server); - cursor_update_focus(server); + menu_close_root(); + cursor_update_focus(); } } return notify; } - if (server->input_mode == LAB_INPUT_STATE_CYCLE) { + if (server.input_mode == LAB_INPUT_STATE_CYCLE) { if (ctx.type == LAB_NODE_CYCLE_OSD_ITEM) { - cycle_on_cursor_release(server, ctx.node); + cycle_on_cursor_release(ctx.node); } return notify; } - if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) { + if (server.input_mode != LAB_INPUT_STATE_PASSTHROUGH) { return notify; } @@ -1250,7 +1249,7 @@ cursor_process_button_release(struct seat *seat, uint32_t button, return notify; } - process_release_mousebinding(server, &ctx, button); + process_release_mousebinding(&ctx, button); return notify; } @@ -1258,8 +1257,6 @@ cursor_process_button_release(struct seat *seat, uint32_t button, bool cursor_finish_button_release(struct seat *seat, uint32_t button) { - struct server *server = seat->server; - /* Clear "pressed" status for all bindings of this mouse button */ struct mousebind *mousebind; wl_list_for_each(mousebind, &rc.mousebinds, link) { @@ -1270,17 +1267,17 @@ cursor_finish_button_release(struct seat *seat, uint32_t button) lab_set_remove(&seat->bound_buttons, button); - if (server->input_mode == LAB_INPUT_STATE_MOVE - || server->input_mode == LAB_INPUT_STATE_RESIZE) { - if (resize_outlines_enabled(server->grabbed_view)) { - resize_outlines_finish(server->grabbed_view); + if (server.input_mode == LAB_INPUT_STATE_MOVE + || server.input_mode == LAB_INPUT_STATE_RESIZE) { + if (resize_outlines_enabled(server.grabbed_view)) { + resize_outlines_finish(server.grabbed_view); } /* Exit interactive move/resize mode */ - interactive_finish(server->grabbed_view); + interactive_finish(server.grabbed_view); return true; - } else if (server->grabbed_view) { + } else if (server.grabbed_view) { /* Button was released without starting move/resize */ - interactive_cancel(server->grabbed_view); + interactive_cancel(server.grabbed_view); } return false; @@ -1295,7 +1292,7 @@ handle_button(struct wl_listener *listener, void *data) */ struct seat *seat = wl_container_of(listener, seat, on_cursor.button); struct wlr_pointer_button_event *event = data; - idle_manager_notify_activity(seat->seat); + idle_manager_notify_activity(seat->wlr_seat); cursor_set_visible(seat, /* visible */ true); bool notify; @@ -1304,7 +1301,7 @@ handle_button(struct wl_listener *listener, void *data) notify = cursor_process_button_press(seat, event->button, event->time_msec); if (notify) { - wlr_seat_pointer_notify_button(seat->seat, event->time_msec, + wlr_seat_pointer_notify_button(seat->wlr_seat, event->time_msec, event->button, event->state); } break; @@ -1312,7 +1309,7 @@ handle_button(struct wl_listener *listener, void *data) notify = cursor_process_button_release(seat, event->button, event->time_msec); if (notify) { - wlr_seat_pointer_notify_button(seat->seat, event->time_msec, + wlr_seat_pointer_notify_button(seat->wlr_seat, event->time_msec, event->button, event->state); } cursor_finish_button_release(seat, event->button); @@ -1370,15 +1367,15 @@ compare_delta(double delta, double delta_discrete, struct accumulated_scroll *ac } static bool -process_cursor_axis(struct server *server, enum wl_pointer_axis orientation, +process_cursor_axis(enum wl_pointer_axis orientation, double delta, double delta_discrete) { - struct cursor_context ctx = get_cursor_context(server); - uint32_t modifiers = keyboard_get_all_modifiers(&server->seat); + struct cursor_context ctx = get_cursor_context(); + uint32_t modifiers = keyboard_get_all_modifiers(&server.seat); enum direction direction = LAB_DIRECTION_INVALID; struct scroll_info info = compare_delta(delta, delta_discrete, - &server->seat.accumulated_scrolls[orientation]); + &server.seat.accumulated_scrolls[orientation]); if (orientation == WL_POINTER_AXIS_HORIZONTAL_SCROLL) { if (info.direction < 0) { @@ -1415,7 +1412,7 @@ process_cursor_axis(struct server *server, enum wl_pointer_axis orientation, * on touchpads or hi-res mice doesn't exceed the threshold */ if (info.run_action) { - actions_run(ctx.view, server, &mousebind->actions, &ctx); + actions_run(ctx.view, &mousebind->actions, &ctx); } } } @@ -1424,7 +1421,7 @@ process_cursor_axis(struct server *server, enum wl_pointer_axis orientation, /* Bindings swallow mouse events if activated */ if (ctx.surface && !consumed) { /* Make sure we are sending the events to the surface under the cursor */ - cursor_update_common(server, &ctx, NULL); + cursor_update_common(&ctx, NULL); return true; } @@ -1440,9 +1437,8 @@ handle_axis(struct wl_listener *listener, void *data) * event, for example when you move the scroll wheel. */ struct seat *seat = wl_container_of(listener, seat, on_cursor.axis); - struct server *server = seat->server; struct wlr_pointer_axis_event *event = data; - idle_manager_notify_activity(seat->seat); + idle_manager_notify_activity(seat->wlr_seat); cursor_set_visible(seat, /* visible */ true); /* input->scroll_factor is set for pointer/touch devices */ @@ -1451,12 +1447,12 @@ handle_axis(struct wl_listener *listener, void *data) struct input *input = event->pointer->base.data; double scroll_factor = input->scroll_factor; - bool notify = process_cursor_axis(server, event->orientation, + bool notify = process_cursor_axis(event->orientation, event->delta, event->delta_discrete); if (notify) { /* Notify the client with pointer focus of the axis event. */ - wlr_seat_pointer_notify_axis(seat->seat, event->time_msec, + wlr_seat_pointer_notify_axis(seat->wlr_seat, event->time_msec, event->orientation, scroll_factor * event->delta, round(scroll_factor * event->delta_discrete), event->source, event->relative_direction); @@ -1475,7 +1471,7 @@ handle_frame(struct wl_listener *listener, void *data) */ struct seat *seat = wl_container_of(listener, seat, on_cursor.frame); /* Notify the client with pointer focus of the frame event. */ - wlr_seat_pointer_notify_frame(seat->seat); + wlr_seat_pointer_notify_frame(seat->wlr_seat); } void @@ -1483,7 +1479,6 @@ cursor_emulate_axis(struct seat *seat, struct wlr_input_device *device, enum wl_pointer_axis orientation, double delta, double delta_discrete, enum wl_pointer_axis_source source, uint32_t time_msec) { - struct server *server = seat->server; struct input *input = device->data; double scroll_factor = 1.0; @@ -1493,15 +1488,15 @@ cursor_emulate_axis(struct seat *seat, struct wlr_input_device *device, scroll_factor = input->scroll_factor; } - bool notify = process_cursor_axis(server, orientation, delta, delta_discrete); + bool notify = process_cursor_axis(orientation, delta, delta_discrete); if (notify) { /* Notify the client with pointer focus of the axis event. */ - wlr_seat_pointer_notify_axis(seat->seat, time_msec, + wlr_seat_pointer_notify_axis(seat->wlr_seat, time_msec, orientation, scroll_factor * delta, round(scroll_factor * delta_discrete), source, WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL); } - wlr_seat_pointer_notify_frame(seat->seat); + wlr_seat_pointer_notify_frame(seat->wlr_seat); } void @@ -1514,17 +1509,17 @@ cursor_emulate_move(struct seat *seat, struct wlr_input_device *device, } wlr_relative_pointer_manager_v1_send_relative_motion( - seat->server->relative_pointer_manager, - seat->seat, (uint64_t)time_msec * 1000, + server.relative_pointer_manager, + seat->wlr_seat, (uint64_t)time_msec * 1000, dx, dy, dx, dy); wlr_cursor_move(seat->cursor, device, dx, dy); double sx, sy; - bool notify = cursor_process_motion(seat->server, time_msec, &sx, &sy); + bool notify = cursor_process_motion(time_msec, &sx, &sy); if (notify) { - wlr_seat_pointer_notify_motion(seat->seat, time_msec, sx, sy); + wlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy); } - wlr_seat_pointer_notify_frame(seat->seat); + wlr_seat_pointer_notify_frame(seat->wlr_seat); } void @@ -1550,18 +1545,18 @@ cursor_emulate_button(struct seat *seat, uint32_t button, case WL_POINTER_BUTTON_STATE_PRESSED: notify = cursor_process_button_press(seat, button, time_msec); if (notify) { - wlr_seat_pointer_notify_button(seat->seat, time_msec, button, state); + wlr_seat_pointer_notify_button(seat->wlr_seat, time_msec, button, state); } break; case WL_POINTER_BUTTON_STATE_RELEASED: notify = cursor_process_button_release(seat, button, time_msec); if (notify) { - wlr_seat_pointer_notify_button(seat->seat, time_msec, button, state); + wlr_seat_pointer_notify_button(seat->wlr_seat, time_msec, button, state); } cursor_finish_button_release(seat, button); break; } - wlr_seat_pointer_notify_frame(seat->seat); + wlr_seat_pointer_notify_frame(seat->wlr_seat); } static void @@ -1614,9 +1609,6 @@ void cursor_reload(struct seat *seat) { cursor_load(seat); -#if HAVE_XWAYLAND - xwayland_reset_cursor(seat->server); -#endif cursor_update_image(seat); } @@ -1640,10 +1632,10 @@ cursor_init(struct seat *seat) touch_init(seat); tablet_init(seat); - CONNECT_SIGNAL(seat->seat, seat, request_set_cursor); + CONNECT_SIGNAL(seat->wlr_seat, seat, request_set_cursor); struct wlr_cursor_shape_manager_v1 *cursor_shape_manager = - wlr_cursor_shape_manager_v1_create(seat->server->wl_display, + wlr_cursor_shape_manager_v1_create(server.wl_display, LAB_CURSOR_SHAPE_V1_VERSION); if (!cursor_shape_manager) { wlr_log(WLR_ERROR, "unable to create cursor_shape interface"); @@ -1651,8 +1643,8 @@ cursor_init(struct seat *seat) } CONNECT_SIGNAL(cursor_shape_manager, seat, request_set_shape); - CONNECT_SIGNAL(seat->seat, seat, request_set_selection); - CONNECT_SIGNAL(seat->seat, seat, request_set_primary_selection); + CONNECT_SIGNAL(seat->wlr_seat, seat, request_set_selection); + CONNECT_SIGNAL(seat->wlr_seat, seat, request_set_primary_selection); } void cursor_finish(struct seat *seat) diff --git a/src/input/gestures.c b/src/input/gestures.c index b40da82c..6b488ed2 100644 --- a/src/input/gestures.c +++ b/src/input/gestures.c @@ -12,11 +12,11 @@ handle_pinch_begin(struct wl_listener *listener, void *data) struct seat *seat = wl_container_of(listener, seat, pinch_begin); struct wlr_pointer_pinch_begin_event *event = data; - idle_manager_notify_activity(seat->seat); + idle_manager_notify_activity(seat->wlr_seat); cursor_set_visible(seat, /* visible */ true); wlr_pointer_gestures_v1_send_pinch_begin(seat->pointer_gestures, - seat->seat, event->time_msec, event->fingers); + seat->wlr_seat, event->time_msec, event->fingers); } static void @@ -25,11 +25,11 @@ handle_pinch_update(struct wl_listener *listener, void *data) struct seat *seat = wl_container_of(listener, seat, pinch_update); struct wlr_pointer_pinch_update_event *event = data; - idle_manager_notify_activity(seat->seat); + idle_manager_notify_activity(seat->wlr_seat); cursor_set_visible(seat, /* visible */ true); wlr_pointer_gestures_v1_send_pinch_update(seat->pointer_gestures, - seat->seat, event->time_msec, event->dx, event->dy, + seat->wlr_seat, event->time_msec, event->dx, event->dy, event->scale, event->rotation); } @@ -39,11 +39,11 @@ handle_pinch_end(struct wl_listener *listener, void *data) struct seat *seat = wl_container_of(listener, seat, pinch_end); struct wlr_pointer_pinch_end_event *event = data; - idle_manager_notify_activity(seat->seat); + idle_manager_notify_activity(seat->wlr_seat); cursor_set_visible(seat, /* visible */ true); wlr_pointer_gestures_v1_send_pinch_end(seat->pointer_gestures, - seat->seat, event->time_msec, event->cancelled); + seat->wlr_seat, event->time_msec, event->cancelled); } static void @@ -52,11 +52,11 @@ handle_swipe_begin(struct wl_listener *listener, void *data) struct seat *seat = wl_container_of(listener, seat, swipe_begin); struct wlr_pointer_swipe_begin_event *event = data; - idle_manager_notify_activity(seat->seat); + idle_manager_notify_activity(seat->wlr_seat); cursor_set_visible(seat, /* visible */ true); wlr_pointer_gestures_v1_send_swipe_begin(seat->pointer_gestures, - seat->seat, event->time_msec, event->fingers); + seat->wlr_seat, event->time_msec, event->fingers); } static void @@ -65,11 +65,11 @@ handle_swipe_update(struct wl_listener *listener, void *data) struct seat *seat = wl_container_of(listener, seat, swipe_update); struct wlr_pointer_swipe_update_event *event = data; - idle_manager_notify_activity(seat->seat); + idle_manager_notify_activity(seat->wlr_seat); cursor_set_visible(seat, /* visible */ true); wlr_pointer_gestures_v1_send_swipe_update(seat->pointer_gestures, - seat->seat, event->time_msec, event->dx, event->dy); + seat->wlr_seat, event->time_msec, event->dx, event->dy); } static void @@ -78,11 +78,11 @@ handle_swipe_end(struct wl_listener *listener, void *data) struct seat *seat = wl_container_of(listener, seat, swipe_end); struct wlr_pointer_swipe_end_event *event = data; - idle_manager_notify_activity(seat->seat); + idle_manager_notify_activity(seat->wlr_seat); cursor_set_visible(seat, /* visible */ true); wlr_pointer_gestures_v1_send_swipe_end(seat->pointer_gestures, - seat->seat, event->time_msec, event->cancelled); + seat->wlr_seat, event->time_msec, event->cancelled); } static void @@ -91,11 +91,11 @@ handle_hold_begin(struct wl_listener *listener, void *data) struct seat *seat = wl_container_of(listener, seat, hold_begin); struct wlr_pointer_hold_begin_event *event = data; - idle_manager_notify_activity(seat->seat); + idle_manager_notify_activity(seat->wlr_seat); cursor_set_visible(seat, /* visible */ true); wlr_pointer_gestures_v1_send_hold_begin(seat->pointer_gestures, - seat->seat, event->time_msec, event->fingers); + seat->wlr_seat, event->time_msec, event->fingers); } static void @@ -104,17 +104,17 @@ handle_hold_end(struct wl_listener *listener, void *data) struct seat *seat = wl_container_of(listener, seat, hold_end); struct wlr_pointer_hold_end_event *event = data; - idle_manager_notify_activity(seat->seat); + idle_manager_notify_activity(seat->wlr_seat); cursor_set_visible(seat, /* visible */ true); wlr_pointer_gestures_v1_send_hold_end(seat->pointer_gestures, - seat->seat, event->time_msec, event->cancelled); + seat->wlr_seat, event->time_msec, event->cancelled); } void gestures_init(struct seat *seat) { - seat->pointer_gestures = wlr_pointer_gestures_v1_create(seat->server->wl_display); + seat->pointer_gestures = wlr_pointer_gestures_v1_create(server.wl_display); CONNECT_SIGNAL(seat->cursor, seat, pinch_begin); CONNECT_SIGNAL(seat->cursor, seat, pinch_update); diff --git a/src/input/ime.c b/src/input/ime.c index 92d88ffe..1758d1c0 100644 --- a/src/input/ime.c +++ b/src/input/ime.c @@ -12,6 +12,7 @@ #include #include #include "common/mem.h" +#include "common/scene-helpers.h" #include "input/keyboard.h" #include "labwc.h" #include "node.h" @@ -178,7 +179,6 @@ static void update_popup_position(struct input_method_popup *popup) { struct input_method_relay *relay = popup->relay; - struct server *server = relay->seat->server; struct text_input *text_input = relay->active_text_input; if (!text_input || !relay->focused_surface @@ -219,7 +219,7 @@ update_popup_position(struct input_method_popup *popup) } struct output *output = - output_nearest_to(server, cursor_rect.x, cursor_rect.y); + output_nearest_to(cursor_rect.x, cursor_rect.y); if (!output_is_usable(output)) { wlr_log(WLR_ERROR, "Cannot position IME popup (unusable output)"); @@ -227,7 +227,7 @@ update_popup_position(struct input_method_popup *popup) } struct wlr_box output_box; wlr_output_layout_get_box( - server->output_layout, output->wlr_output, &output_box); + server.output_layout, output->wlr_output, &output_box); /* Use xdg-positioner utilities to position popup */ struct wlr_xdg_positioner_rules rules = { @@ -309,7 +309,8 @@ handle_keyboard_grab_destroy(struct wl_listener *listener, void *data) { struct input_method_relay *relay = wl_container_of(listener, relay, keyboard_grab_destroy); - struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data; + struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = + relay->input_method->keyboard_grab; assert(keyboard_grab); wl_list_remove(&relay->keyboard_grab_destroy.link); @@ -330,7 +331,7 @@ handle_input_method_grab_keyboard(struct wl_listener *listener, void *data) struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data; struct wlr_keyboard *active_keyboard = - wlr_seat_get_keyboard(relay->seat->seat); + wlr_seat_get_keyboard(relay->seat->wlr_seat); if (!is_keyboard_emulated_by_input_method( active_keyboard, relay->input_method)) { @@ -398,6 +399,8 @@ handle_input_method_new_popup_surface(struct wl_listener *listener, void *data) popup->tree = wlr_scene_subsurface_tree_create( relay->popup_tree, popup->popup_surface->surface); + die_if_null(popup->tree); + node_descriptor_create(&popup->tree->node, LAB_NODE_IME_POPUP, /*view*/ NULL, /*data*/ NULL); @@ -412,7 +415,7 @@ handle_new_input_method(struct wl_listener *listener, void *data) struct input_method_relay *relay = wl_container_of(listener, relay, new_input_method); struct wlr_input_method_v2 *input_method = data; - if (relay->seat->seat != input_method->seat) { + if (relay->seat->wlr_seat != input_method->seat) { return; } @@ -534,7 +537,7 @@ handle_new_text_input(struct wl_listener *listener, void *data) struct input_method_relay *relay = wl_container_of(listener, relay, new_text_input); struct wlr_text_input_v3 *wlr_text_input = data; - if (relay->seat->seat != wlr_text_input->seat) { + if (relay->seat->wlr_seat != wlr_text_input->seat) { return; } @@ -580,14 +583,14 @@ input_method_relay_create(struct seat *seat) relay->seat = seat; wl_list_init(&relay->text_inputs); wl_list_init(&relay->popups); - relay->popup_tree = wlr_scene_tree_create(&seat->server->scene->tree); + relay->popup_tree = lab_wlr_scene_tree_create(&server.scene->tree); relay->new_text_input.notify = handle_new_text_input; - wl_signal_add(&seat->server->text_input_manager->events.text_input, + wl_signal_add(&server.text_input_manager->events.new_text_input, &relay->new_text_input); relay->new_input_method.notify = handle_new_input_method; - wl_signal_add(&seat->server->input_method_manager->events.input_method, + wl_signal_add(&server.input_method_manager->events.new_input_method, &relay->new_input_method); relay->focused_surface_destroy.notify = handle_focused_surface_destroy; diff --git a/src/input/key-state.c b/src/input/key-state.c index 492f7b0f..6d089891 100644 --- a/src/input/key-state.c +++ b/src/input/key-state.c @@ -4,10 +4,171 @@ #include #include #include +#include +#include +#include +#include "config/rcxml.h" +#include "common/buf.h" #include "common/set.h" +#include "input/keyboard.h" +#include "labwc.h" +#include "scaled-buffer/scaled-font-buffer.h" static struct lab_set pressed, bound, pressed_sent; +static bool show_debug_indicator; +static struct indicator_state { + struct wlr_scene_tree *tree; + struct scaled_font_buffer *sfb_pressed; + struct scaled_font_buffer *sfb_bound; + struct scaled_font_buffer *sfb_pressed_sent; + struct scaled_font_buffer *sfb_modifiers; + struct xkb_keymap *keymap; +} indicator_state; + +static const char * +keycode_to_keyname(struct xkb_keymap *keymap, uint32_t keycode) +{ + const xkb_keysym_t *syms; + int syms_len = xkb_keymap_key_get_syms_by_level(keymap, keycode + 8, 0, 0, &syms); + if (!syms_len) { + return NULL; + } + + static char buf[256]; + if (!xkb_keysym_get_name(syms[0], buf, sizeof(buf))) { + return NULL; + } + + return buf; +} + +static const char * +modifier_to_name(uint32_t modifier) +{ + switch (modifier) { + case WLR_MODIFIER_SHIFT: + return "S"; + case WLR_MODIFIER_CAPS: + return "caps"; + case WLR_MODIFIER_CTRL: + return "C"; + case WLR_MODIFIER_ALT: + return "A"; + case WLR_MODIFIER_MOD2: + return "numlock"; + case WLR_MODIFIER_MOD3: + return "H"; + case WLR_MODIFIER_LOGO: + return "W"; + case WLR_MODIFIER_MOD5: + return "M"; + default: + return "?"; + } +} + +static void +init_indicator(struct indicator_state *state) +{ + state->tree = wlr_scene_tree_create(&server.scene->tree); + wlr_scene_node_set_enabled(&state->tree->node, false); + + state->sfb_pressed = scaled_font_buffer_create(state->tree); + wlr_scene_node_set_position(&state->sfb_pressed->scene_buffer->node, 0, 0); + state->sfb_bound = scaled_font_buffer_create(state->tree); + wlr_scene_node_set_position(&state->sfb_bound->scene_buffer->node, 0, 20); + state->sfb_pressed_sent = scaled_font_buffer_create(state->tree); + wlr_scene_node_set_position(&state->sfb_pressed_sent->scene_buffer->node, 0, 40); + state->sfb_modifiers = scaled_font_buffer_create(state->tree); + wlr_scene_node_set_position(&state->sfb_modifiers->scene_buffer->node, 0, 60); + + struct xkb_rule_names rules = { 0 }; + struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + state->keymap = xkb_map_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); +} + +static void +update_key_indicator_callback(void *data) +{ + struct seat *seat = data; + uint32_t all_modifiers = keyboard_get_all_modifiers(seat); + float black[4] = {0, 0, 0, 1}; + float white[4] = {1, 1, 1, 1}; + + if (!indicator_state.tree) { + init_indicator(&indicator_state); + } + + if (show_debug_indicator) { + wlr_scene_node_set_enabled(&indicator_state.tree->node, true); + } else { + wlr_scene_node_set_enabled(&indicator_state.tree->node, false); + return; + } + + struct buf buf = BUF_INIT; + + buf_add(&buf, "pressed="); + for (int i = 0; i < pressed.size; i++) { + const char *keyname = keycode_to_keyname(indicator_state.keymap, + pressed.values[i]); + buf_add_fmt(&buf, "%s (%d), ", keyname, pressed.values[i]); + } + scaled_font_buffer_update(indicator_state.sfb_pressed, buf.data, + -1, &rc.font_osd, black, white); + + buf_clear(&buf); + buf_add(&buf, "bound="); + for (int i = 0; i < bound.size; i++) { + const char *keyname = keycode_to_keyname(indicator_state.keymap, + bound.values[i]); + buf_add_fmt(&buf, "%s (%d), ", keyname, bound.values[i]); + } + scaled_font_buffer_update(indicator_state.sfb_bound, buf.data, + -1, &rc.font_osd, black, white); + + buf_clear(&buf); + buf_add(&buf, "pressed_sent="); + for (int i = 0; i < pressed_sent.size; i++) { + const char *keyname = keycode_to_keyname(indicator_state.keymap, + pressed_sent.values[i]); + buf_add_fmt(&buf, "%s (%d), ", keyname, pressed_sent.values[i]); + } + scaled_font_buffer_update(indicator_state.sfb_pressed_sent, buf.data, -1, + &rc.font_osd, black, white); + + buf_clear(&buf); + buf_add(&buf, "modifiers="); + for (int i = 0; i <= 7; i++) { + uint32_t mod = 1 << i; + if (all_modifiers & mod) { + buf_add_fmt(&buf, "%s, ", modifier_to_name(mod)); + } + } + buf_add_fmt(&buf, "(%d)", all_modifiers); + scaled_font_buffer_update(indicator_state.sfb_modifiers, buf.data, -1, + &rc.font_osd, black, white); + + buf_reset(&buf); +} + +void +key_state_indicator_update(struct seat *seat) +{ + if (!show_debug_indicator) { + return; + } + wl_event_loop_add_idle(server.wl_event_loop, + update_key_indicator_callback, seat); +} + +void +key_state_indicator_toggle(void) +{ + show_debug_indicator = !show_debug_indicator; +} + static void report(struct lab_set *key_set, const char *msg) { diff --git a/src/input/keyboard.c b/src/input/keyboard.c index 0171dcd0..36884144 100644 --- a/src/input/keyboard.c +++ b/src/input/keyboard.c @@ -3,7 +3,7 @@ #include "input/keyboard.h" #include #include -#include +#include #include #include #include @@ -21,6 +21,10 @@ #include "view.h" #include "workspaces.h" +#if WLR_HAS_SESSION + #include +#endif + enum lab_key_handled { LAB_KEY_HANDLED_FALSE = 0, LAB_KEY_HANDLED_TRUE = 1, @@ -52,9 +56,11 @@ keyboard_reset_current_keybind(void) } static void -change_vt(struct server *server, unsigned int vt) +change_vt(unsigned int vt) { - wlr_session_change_vt(server->session, vt); +#if WLR_HAS_SESSION + wlr_session_change_vt(server.session, vt); +#endif } uint32_t @@ -90,7 +96,7 @@ seat_client_from_keyboard_resource(struct wl_resource *resource) } static void -broadcast_modifiers_to_unfocused_clients(struct wlr_seat *seat, +broadcast_modifiers_to_unfocused_clients(struct wlr_seat *wlr_seat, const struct keyboard *keyboard, const struct wlr_keyboard_modifiers *modifiers) { @@ -100,8 +106,8 @@ broadcast_modifiers_to_unfocused_clients(struct wlr_seat *seat, } struct wlr_seat_client *client; - wl_list_for_each(client, &seat->clients, link) { - if (client == seat->keyboard_state.focused_client) { + wl_list_for_each(client, &wlr_seat->clients, link) { + if (client == wlr_seat->keyboard_state.focused_client) { /* * We've already notified the focused client by calling * wlr_seat_keyboard_notify_modifiers() @@ -131,17 +137,18 @@ handle_modifiers(struct wl_listener *listener, void *data) { struct keyboard *keyboard = wl_container_of(listener, keyboard, modifiers); struct seat *seat = keyboard->base.seat; - struct server *server = seat->server; struct wlr_keyboard *wlr_keyboard = keyboard->wlr_keyboard; - if (server->input_mode == LAB_INPUT_STATE_MOVE) { + key_state_indicator_update(seat); + + if (server.input_mode == LAB_INPUT_STATE_MOVE) { /* Any change to the modifier state re-enable region snap */ seat->region_prevent_snap = false; /* Pressing/releasing modifier key may show/hide region overlay */ overlay_update(seat); } - bool cycling = server->input_mode == LAB_INPUT_STATE_CYCLE; + bool cycling = server.input_mode == LAB_INPUT_STATE_CYCLE; if ((cycling || seat->workspace_osd_shown_by_modifier) && !keyboard_get_all_modifiers(seat)) { @@ -150,7 +157,7 @@ handle_modifiers(struct wl_listener *listener, void *data) should_cancel_cycling_on_next_key_release = true; } else { should_cancel_cycling_on_next_key_release = false; - cycle_finish(server, /*switch_focus*/ true); + cycle_finish(/*switch_focus*/ true); } } if (seat->workspace_osd_shown_by_modifier) { @@ -160,7 +167,7 @@ handle_modifiers(struct wl_listener *listener, void *data) if (!input_method_keyboard_grab_forward_modifiers(keyboard)) { /* Send modifiers to focused client */ - wlr_seat_keyboard_notify_modifiers(seat->seat, + wlr_seat_keyboard_notify_modifiers(seat->wlr_seat, &wlr_keyboard->modifiers); /* @@ -182,13 +189,13 @@ handle_modifiers(struct wl_listener *listener, void *data) * consequences. If so, modifiers ought to still be passed to * clients with pointer-focus (see issue #2271) */ - broadcast_modifiers_to_unfocused_clients(seat->seat, + broadcast_modifiers_to_unfocused_clients(seat->wlr_seat, keyboard, &wlr_keyboard->modifiers); } } static struct keybind * -match_keybinding_for_sym(struct server *server, uint32_t modifiers, +match_keybinding_for_sym(uint32_t modifiers, xkb_keysym_t sym, xkb_keycode_t xkb_keycode) { struct keybind *keybind; @@ -196,8 +203,10 @@ match_keybinding_for_sym(struct server *server, uint32_t modifiers, if (modifiers ^ keybind->modifiers) { continue; } - if (view_inhibits_actions(server->active_view, &keybind->actions)) { - continue; + if (!(keybind->override_inhibition)) { + if (view_inhibits_actions(server.active_view, &keybind->actions)) { + continue; + } } if (sym == XKB_KEY_NoSymbol) { /* Use keycodes */ @@ -241,12 +250,12 @@ match_keybinding_for_sym(struct server *server, uint32_t modifiers, * the raw keysym fallback. */ static struct keybind * -match_keybinding(struct server *server, struct keyinfo *keyinfo, +match_keybinding(struct keyinfo *keyinfo, bool is_virtual) { if (!is_virtual) { /* First try keycodes */ - struct keybind *keybind = match_keybinding_for_sym(server, + struct keybind *keybind = match_keybinding_for_sym( keyinfo->modifiers, XKB_KEY_NoSymbol, keyinfo->xkb_keycode); if (keybind) { wlr_log(WLR_DEBUG, "keycode matched"); @@ -257,7 +266,7 @@ match_keybinding(struct server *server, struct keyinfo *keyinfo, /* Then fall back to keysyms */ for (int i = 0; i < keyinfo->translated.nr_syms; i++) { struct keybind *keybind = - match_keybinding_for_sym(server, keyinfo->modifiers, + match_keybinding_for_sym(keyinfo->modifiers, keyinfo->translated.syms[i], keyinfo->xkb_keycode); if (keybind) { wlr_log(WLR_DEBUG, "translated keysym matched"); @@ -268,7 +277,7 @@ match_keybinding(struct server *server, struct keyinfo *keyinfo, /* And finally test for keysyms without modifier */ for (int i = 0; i < keyinfo->raw.nr_syms; i++) { struct keybind *keybind = - match_keybinding_for_sym(server, keyinfo->modifiers, + match_keybinding_for_sym(keyinfo->modifiers, keyinfo->raw.syms[i], keyinfo->xkb_keycode); if (keybind) { wlr_log(WLR_DEBUG, "raw keysym matched"); @@ -366,7 +375,7 @@ get_keyinfo(struct wlr_keyboard *wlr_keyboard, uint32_t evdev_keycode) } static enum lab_key_handled -handle_key_release(struct server *server, uint32_t evdev_keycode) +handle_key_release(uint32_t evdev_keycode) { /* * Release events for keys that were not bound should always be @@ -387,7 +396,7 @@ handle_key_release(struct server *server, uint32_t evdev_keycode) */ if (should_cancel_cycling_on_next_key_release) { should_cancel_cycling_on_next_key_release = false; - cycle_finish(server, /*switch_focus*/ true); + cycle_finish(/*switch_focus*/ true); } /* @@ -399,7 +408,7 @@ handle_key_release(struct server *server, uint32_t evdev_keycode) } static bool -handle_change_vt_key(struct server *server, struct keyboard *keyboard, +handle_change_vt_key(struct keyboard *keyboard, struct keysyms *translated) { for (int i = 0; i < translated->nr_syms; i++) { @@ -407,7 +416,7 @@ handle_change_vt_key(struct server *server, struct keyboard *keyboard, translated->syms[i] - XKB_KEY_XF86Switch_VT_1 + 1; if (vt >= 1 && vt <= 12) { keyboard_cancel_keybind_repeat(keyboard); - change_vt(server, vt); + change_vt(vt); return true; } } @@ -415,42 +424,51 @@ handle_change_vt_key(struct server *server, struct keyboard *keyboard, } static void -handle_menu_keys(struct server *server, struct keysyms *syms) +handle_menu_keys(struct keysyms *syms) { - assert(server->input_mode == LAB_INPUT_STATE_MENU); + assert(server.input_mode == LAB_INPUT_STATE_MENU); for (int i = 0; i < syms->nr_syms; i++) { switch (syms->syms[i]) { case XKB_KEY_Down: - menu_item_select_next(server); + menu_item_select_next(); break; case XKB_KEY_Up: - menu_item_select_previous(server); + menu_item_select_previous(); break; case XKB_KEY_Right: - menu_submenu_enter(server); + menu_submenu_enter(); break; case XKB_KEY_Left: - menu_submenu_leave(server); + menu_submenu_leave(); break; case XKB_KEY_Return: case XKB_KEY_KP_Enter: - menu_call_selected_actions(server); + if (!menu_call_selected_actions()) { + menu_submenu_enter(); + }; break; case XKB_KEY_Escape: - menu_close_root(server); - cursor_update_focus(server); + menu_close_root(); + cursor_update_focus(); break; - default: - continue; - } + default: { + uint32_t accelerator = xkb_keysym_to_utf32(syms->syms[i]); + if (accelerator == 0) { + continue; + } + if (menu_item_select_by_accelerator(accelerator)) { + menu_call_selected_actions(); + } + break; + }} break; } } /* Returns true if the keystroke is consumed */ static bool -handle_cycle_view_key(struct server *server, struct keyinfo *keyinfo) +handle_cycle_view_key(struct keyinfo *keyinfo) { if (keyinfo->is_modifier) { return false; @@ -460,19 +478,19 @@ handle_cycle_view_key(struct server *server, struct keyinfo *keyinfo) for (int i = 0; i < keyinfo->translated.nr_syms; i++) { if (keyinfo->translated.syms[i] == XKB_KEY_Escape) { /* Esc deactivates window switcher */ - cycle_finish(server, /*switch_focus*/ false); + cycle_finish(/*switch_focus*/ false); return true; } if (keyinfo->translated.syms[i] == XKB_KEY_Up || keyinfo->translated.syms[i] == XKB_KEY_Left) { /* Up/Left cycles the window backward */ - cycle_step(server, LAB_CYCLE_DIR_BACKWARD); + cycle_step(LAB_CYCLE_DIR_BACKWARD); return true; } if (keyinfo->translated.syms[i] == XKB_KEY_Down || keyinfo->translated.syms[i] == XKB_KEY_Right) { /* Down/Right cycles the window forward */ - cycle_step(server, LAB_CYCLE_DIR_FORWARD); + cycle_step(LAB_CYCLE_DIR_FORWARD); return true; } } @@ -483,11 +501,9 @@ static enum lab_key_handled handle_compositor_keybindings(struct keyboard *keyboard, struct wlr_keyboard_key_event *event) { - struct seat *seat = keyboard->base.seat; - struct server *server = seat->server; struct wlr_keyboard *wlr_keyboard = keyboard->wlr_keyboard; struct keyinfo keyinfo = get_keyinfo(wlr_keyboard, event->keycode); - bool locked = seat->server->session_lock_manager->locked; + bool locked = server.session_lock_manager->locked; key_state_set_pressed(event->keycode, event->state == WL_KEYBOARD_KEY_STATE_PRESSED); @@ -499,15 +515,15 @@ handle_compositor_keybindings(struct keyboard *keyboard, cur_keybind = NULL; return LAB_KEY_HANDLED_TRUE; } - actions_run(NULL, server, &cur_keybind->actions, NULL); + actions_run(NULL, &cur_keybind->actions, NULL); return LAB_KEY_HANDLED_TRUE; } else { - return handle_key_release(server, event->keycode); + return handle_key_release(event->keycode); } } /* Catch C-A-F1 to C-A-F12 to change tty */ - if (handle_change_vt_key(server, keyboard, &keyinfo.translated)) { + if (handle_change_vt_key(keyboard, &keyinfo.translated)) { key_state_store_pressed_key_as_bound(event->keycode); return LAB_KEY_HANDLED_TRUE_AND_VT_CHANGED; } @@ -518,12 +534,12 @@ handle_compositor_keybindings(struct keyboard *keyboard, * _all_ key press/releases are registered */ if (!locked) { - if (server->input_mode == LAB_INPUT_STATE_MENU) { + if (server.input_mode == LAB_INPUT_STATE_MENU) { key_state_store_pressed_key_as_bound(event->keycode); - handle_menu_keys(server, &keyinfo.translated); + handle_menu_keys(&keyinfo.translated); return LAB_KEY_HANDLED_TRUE; - } else if (server->input_mode == LAB_INPUT_STATE_CYCLE) { - if (handle_cycle_view_key(server, &keyinfo)) { + } else if (server.input_mode == LAB_INPUT_STATE_CYCLE) { + if (handle_cycle_view_key(&keyinfo)) { key_state_store_pressed_key_as_bound(event->keycode); return LAB_KEY_HANDLED_TRUE; } @@ -533,7 +549,7 @@ handle_compositor_keybindings(struct keyboard *keyboard, /* * Handle compositor keybinds */ - cur_keybind = match_keybinding(server, &keyinfo, keyboard->is_virtual); + cur_keybind = match_keybinding(&keyinfo, keyboard->is_virtual); if (cur_keybind && (!locked || cur_keybind->allow_when_locked)) { /* * Update key-state before action_run() because the action @@ -542,7 +558,7 @@ handle_compositor_keybindings(struct keyboard *keyboard, */ key_state_store_pressed_key_as_bound(event->keycode); if (!cur_keybind->on_release) { - actions_run(NULL, server, &cur_keybind->actions, NULL); + actions_run(NULL, &cur_keybind->actions, NULL); } return LAB_KEY_HANDLED_TRUE; } @@ -572,7 +588,7 @@ handle_keybind_repeat(void *data) } static void -start_keybind_repeat(struct server *server, struct keyboard *keyboard, +start_keybind_repeat(struct keyboard *keyboard, struct wlr_keyboard_key_event *event) { struct wlr_keyboard *wlr_keyboard = keyboard->wlr_keyboard; @@ -583,7 +599,7 @@ start_keybind_repeat(struct server *server, struct keyboard *keyboard, keyboard->keybind_repeat_keycode = event->keycode; keyboard->keybind_repeat_rate = wlr_keyboard->repeat_info.rate; keyboard->keybind_repeat = wl_event_loop_add_timer( - server->wl_event_loop, handle_keybind_repeat, keyboard); + server.wl_event_loop, handle_keybind_repeat, keyboard); wl_event_source_timer_update(keyboard->keybind_repeat, wlr_keyboard->repeat_info.delay); } @@ -618,8 +634,11 @@ handle_key(struct wl_listener *listener, void *data) struct keyboard *keyboard = wl_container_of(listener, keyboard, key); struct seat *seat = keyboard->base.seat; struct wlr_keyboard_key_event *event = data; - struct wlr_seat *wlr_seat = seat->seat; - idle_manager_notify_activity(seat->seat); + struct wlr_seat *wlr_seat = seat->wlr_seat; + + key_state_indicator_update(seat); + + idle_manager_notify_activity(seat->wlr_seat); /* any new press/release cancels current keybind repeat */ keyboard_cancel_keybind_repeat(keyboard); @@ -639,7 +658,7 @@ handle_key(struct wl_listener *listener, void *data) */ if (!is_modifier(keyboard->wlr_keyboard, event->keycode) && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { - start_keybind_repeat(seat->server, keyboard, event); + start_keybind_repeat(keyboard, event); } } else if (!input_method_keyboard_grab_forward_key(keyboard, event)) { wlr_seat_set_keyboard(wlr_seat, keyboard->wlr_keyboard); @@ -715,7 +734,7 @@ keyboard_update_layout(struct seat *seat, xkb_layout_index_t layout) } static void -reset_window_keyboard_layout_groups(struct server *server) +reset_window_keyboard_layout_groups(void) { if (!rc.kb_layout_per_window) { return; @@ -727,15 +746,15 @@ reset_window_keyboard_layout_groups(struct server *server) * but let's keep it simple for now and just reset them all. */ struct view *view; - for_each_view(view, &server->views, LAB_VIEW_CRITERIA_NONE) { + for_each_view(view, &server.views, LAB_VIEW_CRITERIA_NONE) { view->keyboard_layout = 0; } - struct view *active_view = server->active_view; + struct view *active_view = server.active_view; if (!active_view) { return; } - keyboard_update_layout(&server->seat, active_view->keyboard_layout); + keyboard_update_layout(&server.seat, active_view->keyboard_layout); } /* @@ -743,12 +762,23 @@ reset_window_keyboard_layout_groups(struct server *server) * XKB_DEFAULT_OPTIONS, and friends. */ static void -set_layout(struct server *server, struct wlr_keyboard *kb) +set_layout(struct wlr_keyboard *kb) { static bool fallback_mode; struct xkb_rule_names rules = { 0 }; - struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + enum xkb_context_flags ctx_flags = XKB_CONTEXT_NO_FLAGS; +#ifdef __ANDROID__ + /* + * Android's bionic libc implements secure_getenv() as a function + * that always returns NULL (the app process has no AT_SECURE). + * This prevents xkbcommon from reading XKB_DEFAULT_LAYOUT and + * friends via secure_getenv(). Use the flag to fall back to + * regular getenv() which works fine on Android. + */ + ctx_flags |= XKB_CONTEXT_NO_SECURE_GETENV; +#endif + struct xkb_context *context = xkb_context_new(ctx_flags); struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); @@ -762,7 +792,7 @@ set_layout(struct server *server, struct wlr_keyboard *kb) if (keymap && !layout_empty) { if (!wlr_keyboard_keymaps_match(kb->keymap, keymap)) { wlr_keyboard_set_keymap(kb, keymap); - reset_window_keyboard_layout_groups(server); + reset_window_keyboard_layout_groups(); } xkb_keymap_unref(keymap); } else { @@ -772,7 +802,7 @@ set_layout(struct server *server, struct wlr_keyboard *kb) wlr_log(WLR_ERROR, "entering fallback mode with layout 'us'"); fallback_mode = true; setenv("XKB_DEFAULT_LAYOUT", "us", 1); - set_layout(server, kb); + set_layout(kb); } } xkb_context_unref(context); @@ -782,10 +812,10 @@ void keyboard_configure(struct seat *seat, struct wlr_keyboard *kb, bool is_virtual) { if (!is_virtual) { - set_layout(seat->server, kb); + set_layout(kb); } wlr_keyboard_set_repeat_info(kb, rc.repeat_rate, rc.repeat_delay); - keybind_update_keycodes(seat->server); + keybind_update_keycodes(); } void diff --git a/src/input/tablet-pad.c b/src/input/tablet-pad.c index 230b6e6b..7c52278c 100644 --- a/src/input/tablet-pad.c +++ b/src/input/tablet-pad.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #include "input/tablet-pad.h" #include -#include +#include #include #include #include @@ -14,6 +14,12 @@ #include "input/tablet.h" #include "labwc.h" +#if WLR_HAS_LIBINPUT_BACKEND + #include +#else + #define wlr_input_device_is_libinput(device) (false) +#endif + void tablet_pad_attach_tablet(struct seat *seat) { @@ -34,7 +40,7 @@ tablet_pad_attach_tablet(struct seat *seat) */ continue; } - +#if WLR_HAS_LIBINPUT_BACKEND struct libinput_device *tablet_device = wlr_libinput_get_device_handle(tablet->wlr_input_device); struct libinput_device_group *tablet_group = @@ -55,6 +61,7 @@ tablet_pad_attach_tablet(struct seat *seat) pad->tablet = tablet; } } +#endif } } @@ -183,9 +190,9 @@ tablet_pad_create(struct seat *seat, struct wlr_input_device *wlr_device) pad->seat = seat; pad->wlr_input_device = wlr_device; pad->pad = wlr_tablet_pad_from_input_device(wlr_device); - if (seat->server->tablet_manager) { + if (server.tablet_manager) { pad->pad_v2 = wlr_tablet_pad_create( - seat->server->tablet_manager, seat->seat, wlr_device); + server.tablet_manager, seat->wlr_seat, wlr_device); } pad->pad->data = pad; wlr_log(WLR_INFO, "tablet pad capabilities: %zu button(s) %zu strip(s) %zu ring(s)", diff --git a/src/input/tablet.c b/src/input/tablet.c index 319c06b3..82d5e801 100644 --- a/src/input/tablet.c +++ b/src/input/tablet.c @@ -2,11 +2,13 @@ #include "input/tablet.h" #include #include +#include #include #include #include #include #include +#include #include "common/macros.h" #include "common/mem.h" #include "common/scene-helpers.h" @@ -20,6 +22,10 @@ #include "action.h" #include "view.h" +#if WLR_HAS_LIBINPUT_BACKEND + #include +#endif + bool tablet_tool_has_focused_surface(struct seat *seat) { @@ -42,9 +48,9 @@ handle_set_cursor(struct wl_listener *listener, void *data) struct seat *seat = tool->seat; struct wlr_seat_client *focused_client = - seat->seat->pointer_state.focused_client; + seat->wlr_seat->pointer_state.focused_client; - if (seat->server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) { + if (server.input_mode != LAB_INPUT_STATE_PASSTHROUGH) { return; } @@ -76,8 +82,8 @@ tablet_tool_create(struct seat *seat, struct drawing_tablet_tool *tool = znew(*tool); tool->seat = seat; tool->tool_v2 = - wlr_tablet_tool_create(seat->server->tablet_manager, - seat->seat, wlr_tablet_tool); + wlr_tablet_tool_create(server.tablet_manager, + seat->wlr_seat, wlr_tablet_tool); wlr_tablet_tool->data = tool; wlr_log(WLR_INFO, "tablet tool capabilities:%s%s%s%s%s%s", wlr_tablet_tool->tilt ? " tilt" : "", @@ -239,7 +245,7 @@ tablet_get_coords(struct drawing_tablet *tablet, struct drawing_tablet_tool *too double sx, sy; struct wlr_scene_node *node = - wlr_scene_node_at(&tablet->seat->server->scene->tree.node, lx, ly, &sx, &sy); + wlr_scene_node_at(&server.scene->tree.node, lx, ly, &sx, &sy); /* find the surface and return it if it accepts tablet events */ struct wlr_surface *surface = lab_wlr_surface_from_node(node); @@ -275,7 +281,7 @@ notify_motion(struct drawing_tablet *tablet, struct drawing_tablet_tool *tool, } double sx, sy; - bool notify = cursor_process_motion(tablet->seat->server, time, &sx, &sy); + bool notify = cursor_process_motion(time, &sx, &sy); if (notify) { wlr_tablet_v2_tablet_tool_notify_motion(tool->tool_v2, sx, sy); if (enter_surface) { @@ -309,7 +315,7 @@ notify_motion(struct drawing_tablet *tablet, struct drawing_tablet_tool *tool, * sure what will break since I have the feeling that a * lot of internals rely on correct pointer focus. */ - wlr_seat_pointer_notify_motion(tool->seat->seat, time, -1, -1); + wlr_seat_pointer_notify_motion(tool->seat->wlr_seat, time, -1, -1); } } } @@ -325,7 +331,7 @@ handle_tablet_tool_proximity(struct wl_listener *listener, void *data) return; } - idle_manager_notify_activity(tablet->seat->seat); + idle_manager_notify_activity(tablet->seat->wlr_seat); cursor_set_visible(tablet->seat, /* visible */ true); if (!tool) { @@ -336,6 +342,29 @@ handle_tablet_tool_proximity(struct wl_listener *listener, void *data) tool = tablet_tool_create(tablet->seat, ev->tool); } +#if WLR_HAS_LIBINPUT_BACKEND + struct libinput_tablet_tool *libinput_tool = + wlr_libinput_get_tablet_tool_handle(tool->tool_v2->wlr_tool); + + /* + * Configure tool pressure range using libinput. Note that a runtime change + * needs two proximity-in events. First one is for applying the pressure range + * here and second one until it is effectively applied, probably because of + * how lininput applies pressure range changes internally. + */ + if (libinput_tablet_tool_config_pressure_range_is_available(libinput_tool) > 0 + && rc.tablet_tool.min_pressure >= 0.0 + && rc.tablet_tool.max_pressure <= 1.0) { + double min = libinput_tablet_tool_config_pressure_range_get_minimum(libinput_tool); + double max = libinput_tablet_tool_config_pressure_range_get_maximum(libinput_tool); + if (min != rc.tablet_tool.min_pressure || max != rc.tablet_tool.max_pressure) { + wlr_log(WLR_INFO, "tablet tool pressure range configured"); + libinput_tablet_tool_config_pressure_range_set(libinput_tool, + rc.tablet_tool.min_pressure, rc.tablet_tool.max_pressure); + } + } +#endif + /* * Enforce mouse emulation when the current tool is a tablet mouse. * Client support for tablet mouses in tablet mode is often incomplete @@ -393,7 +422,7 @@ handle_tablet_tool_axis(struct wl_listener *listener, void *data) return; } - idle_manager_notify_activity(tablet->seat->seat); + idle_manager_notify_activity(tablet->seat->wlr_seat); cursor_set_visible(tablet->seat, /* visible */ true); /* @@ -402,8 +431,6 @@ handle_tablet_tool_axis(struct wl_listener *listener, void *data) */ tool->dx = 0; tool->dy = 0; - tool->tilt_x = 0; - tool->tilt_y = 0; if (ev->updated_axes & WLR_TABLET_TOOL_AXIS_X) { tool->x = ev->x; @@ -449,7 +476,7 @@ handle_tablet_tool_axis(struct wl_listener *listener, void *data) * Note that surface is also NULL when mouse emulation is forced. */ if (!is_down_mouse_emulation && ((surface - && tablet->seat->server->input_mode == LAB_INPUT_STATE_PASSTHROUGH) + && server.input_mode == LAB_INPUT_STATE_PASSTHROUGH) || wlr_tablet_tool_v2_has_implicit_grab(tool->tool_v2))) { /* motion seems to be supported by all tools */ notify_motion(tablet, tool, surface, x, y, dx, dy, ev->time_msec); @@ -565,7 +592,7 @@ handle_tablet_tool_tip(struct wl_listener *listener, void *data) return; } - idle_manager_notify_activity(tablet->seat->seat); + idle_manager_notify_activity(tablet->seat->wlr_seat); cursor_set_visible(tablet->seat, /* visible */ true); double x, y, dx, dy; @@ -644,7 +671,7 @@ handle_tablet_tool_button(struct wl_listener *listener, void *data) return; } - idle_manager_notify_activity(tablet->seat->seat); + idle_manager_notify_activity(tablet->seat->wlr_seat); cursor_set_visible(tablet->seat, /* visible */ true); double x, y, dx, dy; @@ -667,8 +694,7 @@ handle_tablet_tool_button(struct wl_listener *listener, void *data) if (mousebind->mouse_event == MOUSE_ACTION_PRESS && mousebind->button == button && mousebind->context == LAB_NODE_CLIENT) { - actions_run(view, tool->seat->server, - &mousebind->actions, NULL); + actions_run(view, &mousebind->actions, NULL); } } } @@ -720,9 +746,9 @@ tablet_create(struct seat *seat, struct wlr_input_device *wlr_device) tablet->wlr_input_device = wlr_device; tablet->tablet = wlr_tablet_from_input_device(wlr_device); tablet->tablet->data = tablet; - if (seat->server->tablet_manager) { + if (server.tablet_manager) { tablet->tablet_v2 = wlr_tablet_create( - seat->server->tablet_manager, seat->seat, wlr_device); + server.tablet_manager, seat->wlr_seat, wlr_device); } wlr_log(WLR_INFO, "tablet dimensions: %.2fmm x %.2fmm", tablet->tablet->width_mm, tablet->tablet->height_mm); diff --git a/src/input/touch.c b/src/input/touch.c index d6557014..42fab358 100644 --- a/src/input/touch.c +++ b/src/input/touch.c @@ -52,7 +52,7 @@ touch_get_coords(struct seat *seat, struct wlr_touch *touch, double x, double y, * This matches normal pointer/mouse behavior where the first click on * a surface closes a root/client menu. */ - if (seat->server->input_mode == LAB_INPUT_STATE_MENU) { + if (server.input_mode == LAB_INPUT_STATE_MENU) { return NULL; } @@ -63,7 +63,7 @@ touch_get_coords(struct seat *seat, struct wlr_touch *touch, double x, double y, double sx, sy; struct wlr_scene_node *node = - wlr_scene_node_at(&seat->server->scene->tree.node, lx, ly, &sx, &sy); + wlr_scene_node_at(&server.scene->tree.node, lx, ly, &sx, &sy); *x_offset = lx - sx; *y_offset = ly - sy; @@ -71,7 +71,7 @@ touch_get_coords(struct seat *seat, struct wlr_touch *touch, double x, double y, /* Find the surface and return it if it accepts touch events */ struct wlr_surface *surface = lab_wlr_surface_from_node(node); - if (surface && !wlr_surface_accepts_touch(surface, seat->seat)) { + if (surface && !wlr_surface_accepts_touch(surface, seat->wlr_seat)) { surface = NULL; } return surface; @@ -83,7 +83,7 @@ handle_touch_motion(struct wl_listener *listener, void *data) struct seat *seat = wl_container_of(listener, seat, touch_motion); struct wlr_touch_motion_event *event = data; - idle_manager_notify_activity(seat->seat); + idle_manager_notify_activity(seat->wlr_seat); int touch_point_count = wl_list_length(&seat->touch_points); @@ -105,7 +105,7 @@ handle_touch_motion(struct wl_listener *listener, void *data) wlr_cursor_warp_absolute(seat->cursor, &event->touch->base, event->x, event->y); } - wlr_seat_touch_notify_motion(seat->seat, event->time_msec, + wlr_seat_touch_notify_motion(seat->wlr_seat, event->time_msec, event->touch_id, sx, sy); } else { if (touch_point_count == 1) { @@ -123,7 +123,7 @@ handle_touch_frame(struct wl_listener *listener, void *data) { struct seat *seat = wl_container_of(listener, seat, touch_frame); - wlr_seat_touch_notify_frame(seat->seat); + wlr_seat_touch_notify_frame(seat->wlr_seat); } static void @@ -132,7 +132,7 @@ handle_touch_down(struct wl_listener *listener, void *data) struct seat *seat = wl_container_of(listener, seat, touch_down); struct wlr_touch_down_event *event = data; - idle_manager_notify_activity(seat->seat); + idle_manager_notify_activity(seat->wlr_seat); /* Compute layout => surface offset and save for this touch point */ struct touch_point *touch_point = znew(*touch_point); @@ -152,7 +152,7 @@ handle_touch_down(struct wl_listener *listener, void *data) if (touch_point->surface) { seat_pointer_end_grab(seat, touch_point->surface); /* Clear focus to not interfere with touch input */ - wlr_seat_pointer_notify_clear_focus(seat->seat); + wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); /* Convert coordinates: first [0, 1] => layout */ double lx, ly; @@ -169,7 +169,7 @@ handle_touch_down(struct wl_listener *listener, void *data) if (mousebind->mouse_event == MOUSE_ACTION_PRESS && mousebind->button == BTN_LEFT && mousebind->context == LAB_NODE_CLIENT) { - actions_run(view, seat->server, &mousebind->actions, NULL); + actions_run(view, &mousebind->actions, NULL); } } @@ -177,7 +177,7 @@ handle_touch_down(struct wl_listener *listener, void *data) wlr_cursor_warp_absolute(seat->cursor, &event->touch->base, event->x, event->y); } - wlr_seat_touch_notify_down(seat->seat, touch_point->surface, + wlr_seat_touch_notify_down(seat->wlr_seat, touch_point->surface, event->time_msec, event->touch_id, sx, sy); } else { if (touch_point_count == 1) { @@ -195,19 +195,19 @@ handle_touch_up(struct wl_listener *listener, void *data) struct seat *seat = wl_container_of(listener, seat, touch_up); struct wlr_touch_up_event *event = data; - idle_manager_notify_activity(seat->seat); + idle_manager_notify_activity(seat->wlr_seat); /* Remove the touch point from the seat */ struct touch_point *touch_point, *tmp; wl_list_for_each_safe(touch_point, tmp, &seat->touch_points, link) { if (touch_point->touch_id == event->touch_id) { if (touch_point->surface) { - wlr_seat_touch_notify_up(seat->seat, event->time_msec, + wlr_seat_touch_notify_up(seat->wlr_seat, event->time_msec, event->touch_id); } else { cursor_emulate_button(seat, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED, event->time_msec); - ssd_update_hovered_button(seat->server, NULL); + ssd_update_hovered_button(NULL); } wl_list_remove(&touch_point->link); zfree(touch_point); diff --git a/src/interactive.c b/src/interactive.c index 28f428e7..9214ba30 100644 --- a/src/interactive.c +++ b/src/interactive.c @@ -36,22 +36,22 @@ max_move_scale(double pos_cursor, double pos_old, double size_old, } void -interactive_anchor_to_cursor(struct server *server, struct wlr_box *geo) +interactive_anchor_to_cursor(struct wlr_box *geo) { - assert(server->input_mode == LAB_INPUT_STATE_MOVE); + assert(server.input_mode == LAB_INPUT_STATE_MOVE); if (wlr_box_empty(geo)) { return; } /* Resize grab_box while anchoring it to grab_{x,y} */ - server->grab_box.x = max_move_scale(server->grab_x, server->grab_box.x, - server->grab_box.width, geo->width); - server->grab_box.y = max_move_scale(server->grab_y, server->grab_box.y, - server->grab_box.height, geo->height); - server->grab_box.width = geo->width; - server->grab_box.height = geo->height; + server.grab_box.x = max_move_scale(server.grab_x, server.grab_box.x, + server.grab_box.width, geo->width); + server.grab_box.y = max_move_scale(server.grab_y, server.grab_box.y, + server.grab_box.height, geo->height); + server.grab_box.width = geo->width; + server.grab_box.height = geo->height; - geo->x = server->grab_box.x + (server->seat.cursor->x - server->grab_x); - geo->y = server->grab_box.y + (server->seat.cursor->y - server->grab_y); + geo->x = server.grab_box.x + (server.seat.cursor->x - server.grab_x); + geo->y = server.grab_box.y + (server.seat.cursor->y - server.grab_y); } /* @@ -60,22 +60,21 @@ interactive_anchor_to_cursor(struct server *server, struct wlr_box *geo) * then interactive_begin() is called. */ void -interactive_set_grab_context(struct cursor_context *ctx) +interactive_set_grab_context(const struct cursor_context *ctx) { if (!ctx->view) { return; } - struct server *server = ctx->view->server; - if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) { + if (server.input_mode != LAB_INPUT_STATE_PASSTHROUGH) { return; } - server->grabbed_view = ctx->view; - server->grab_x = server->seat.cursor->x; - server->grab_y = server->seat.cursor->y; - server->grab_box = ctx->view->current; - server->resize_edges = - cursor_get_resize_edges(server->seat.cursor, ctx); + server.grabbed_view = ctx->view; + server.grab_x = server.seat.cursor->x; + server.grab_y = server.seat.cursor->y; + server.grab_box = ctx->view->current; + server.resize_edges = + cursor_get_resize_edges(server.seat.cursor, ctx); } void @@ -87,11 +86,10 @@ interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges) * the compositor stops propagating pointer events to clients and * instead consumes them itself, to move or resize windows. */ - struct server *server = view->server; - struct seat *seat = &server->seat; + struct seat *seat = &server.seat; - if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH - || view != server->grabbed_view) { + if (server.input_mode != LAB_INPUT_STATE_PASSTHROUGH + || view != server.grabbed_view) { return; } @@ -125,11 +123,12 @@ interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges) cursor_shape = LAB_CURSOR_GRAB; break; case LAB_INPUT_STATE_RESIZE: { - if (view->shaded || view->fullscreen || - view->maximized == VIEW_AXIS_BOTH) { + if (view->shaded || view->fullscreen) { /* - * We don't allow resizing while shaded, - * fullscreen or maximized in both directions. + * We don't allow resizing while shaded or fullscreen. + * Maximized views are handled below by un-maximizing + * the axes being resized while keeping the current + * geometry as the starting point. */ return; } @@ -139,24 +138,24 @@ interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges) * Otherwise, they were set already from cursor context. */ if (edges != LAB_EDGE_NONE) { - server->resize_edges = edges; + server.resize_edges = edges; } /* - * If tiled or maximized in only one direction, reset - * tiled state and un-maximize the relevant axes, but - * keep the same geometry as the starting point. + * If tiled or maximized, reset tiled state and un-maximize + * the axes that are being resized, but keep the same + * geometry as the starting point. */ enum view_axis maximized = view->maximized; - if (server->resize_edges & LAB_EDGES_LEFT_RIGHT) { + if (server.resize_edges & LAB_EDGES_LEFT_RIGHT) { maximized &= ~VIEW_AXIS_HORIZONTAL; } - if (server->resize_edges & LAB_EDGES_TOP_BOTTOM) { + if (server.resize_edges & LAB_EDGES_TOP_BOTTOM) { maximized &= ~VIEW_AXIS_VERTICAL; } view_set_maximized(view, maximized); view_set_untiled(view); - cursor_shape = cursor_get_from_edge(server->resize_edges); + cursor_shape = cursor_get_from_edge(server.resize_edges); break; } default: @@ -176,7 +175,7 @@ interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges) if (mode == LAB_INPUT_STATE_MOVE && !view_is_floating(view) && rc.unsnap_threshold <= 0) { struct wlr_box natural_geo = view->natural_geometry; - interactive_anchor_to_cursor(server, &natural_geo); + interactive_anchor_to_cursor(&natural_geo); /* Shaded clients will not process resize events until unshaded */ view_set_shade(view, false); view_set_maximized(view, VIEW_AXIS_NONE); @@ -188,7 +187,7 @@ interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges) resize_indicator_show(view); } if (rc.window_edge_strength) { - edges_calculate_visibility(server, view); + edges_calculate_visibility(view); } } @@ -200,7 +199,7 @@ edge_from_cursor(struct seat *seat, struct output **dest_output, *edge1 = LAB_EDGE_NONE; *edge2 = LAB_EDGE_NONE; - if (!view_is_floating(seat->server->grabbed_view)) { + if (!view_is_floating(server.grabbed_view)) { return false; } @@ -208,7 +207,7 @@ edge_from_cursor(struct seat *seat, struct output **dest_output, return false; } - struct output *output = output_nearest_to_cursor(seat->server); + struct output *output = output_nearest_to_cursor(); if (!output_is_usable(output)) { wlr_log(WLR_ERROR, "output at cursor is unusable"); return false; @@ -222,25 +221,25 @@ edge_from_cursor(struct seat *seat, struct output **dest_output, int bottom_range = rc.snap_edge_range_outer; int left_range = rc.snap_edge_range_outer; int right_range = rc.snap_edge_range_outer; - if (wlr_output_layout_adjacent_output(seat->server->output_layout, WLR_DIRECTION_UP, + if (wlr_output_layout_adjacent_output(server.output_layout, WLR_DIRECTION_UP, output->wlr_output, cursor_x, cursor_y)) { top_range = rc.snap_edge_range_inner; } - if (wlr_output_layout_adjacent_output(seat->server->output_layout, WLR_DIRECTION_DOWN, + if (wlr_output_layout_adjacent_output(server.output_layout, WLR_DIRECTION_DOWN, output->wlr_output, cursor_x, cursor_y)) { bottom_range = rc.snap_edge_range_inner; } - if (wlr_output_layout_adjacent_output(seat->server->output_layout, WLR_DIRECTION_LEFT, + if (wlr_output_layout_adjacent_output(server.output_layout, WLR_DIRECTION_LEFT, output->wlr_output, cursor_x, cursor_y)) { left_range = rc.snap_edge_range_inner; } - if (wlr_output_layout_adjacent_output(seat->server->output_layout, WLR_DIRECTION_RIGHT, + if (wlr_output_layout_adjacent_output(server.output_layout, WLR_DIRECTION_RIGHT, output->wlr_output, cursor_x, cursor_y)) { right_range = rc.snap_edge_range_inner; } /* Translate into output local coordinates */ - wlr_output_layout_output_coords(seat->server->output_layout, + wlr_output_layout_output_coords(server.output_layout, output->wlr_output, &cursor_x, &cursor_y); struct wlr_box *area = &output->usable_area; @@ -285,7 +284,7 @@ snap_to_edge(struct view *view) { struct output *output; enum lab_edge edge1, edge2; - if (!edge_from_cursor(&view->server->seat, &output, &edge1, &edge2)) { + if (!edge_from_cursor(&server.seat, &output, &edge1, &edge2)) { return false; } enum lab_edge edge = edge1 | edge2; @@ -305,11 +304,11 @@ snap_to_edge(struct view *view) static bool snap_to_region(struct view *view) { - if (!regions_should_snap(view->server)) { + if (!regions_should_snap()) { return false; } - struct region *region = regions_from_cursor(view->server); + struct region *region = regions_from_cursor(); if (region) { view_snap_to_region(view, region); return true; @@ -322,11 +321,11 @@ interactive_finish(struct view *view) { assert(view); - if (view->server->grabbed_view != view) { + if (server.grabbed_view != view) { return; } - if (view->server->input_mode == LAB_INPUT_STATE_MOVE) { + if (server.input_mode == LAB_INPUT_STATE_MOVE) { if (!snap_to_region(view)) { snap_to_edge(view); } @@ -345,25 +344,25 @@ interactive_cancel(struct view *view) { assert(view); - if (view->server->grabbed_view != view) { + if (server.grabbed_view != view) { return; } - view->server->grabbed_view = NULL; + server.grabbed_view = NULL; /* * It's possible that grabbed_view was set but interactive_begin() * wasn't called yet. In that case, we are done. */ - if (view->server->input_mode != LAB_INPUT_STATE_MOVE - && view->server->input_mode != LAB_INPUT_STATE_RESIZE) { + if (server.input_mode != LAB_INPUT_STATE_MOVE + && server.input_mode != LAB_INPUT_STATE_RESIZE) { return; } - overlay_finish(&view->server->seat); + overlay_finish(&server.seat); resize_indicator_hide(view); /* Restore keyboard/pointer focus */ - seat_focus_override_end(&view->server->seat, /*restore_focus*/ true); + seat_focus_override_end(&server.seat, /*restore_focus*/ true); } diff --git a/src/layers.c b/src/layers.c index 60421e39..eaa93f25 100644 --- a/src/layers.c +++ b/src/layers.c @@ -78,9 +78,8 @@ layers_arrange(struct output *output) apply_override(output, &usable_area); - struct server *server = output->server; struct wlr_scene_output *scene_output = - wlr_scene_get_scene_output(server->scene, output->wlr_output); + wlr_scene_get_scene_output(server.scene, output->wlr_output); if (!scene_output) { wlr_log(WLR_DEBUG, "no wlr_scene_output"); return; @@ -141,10 +140,10 @@ has_exclusive_interactivity(struct wlr_scene_layer_surface_v1 *scene) * toplevel if one exists. */ static void -try_to_focus_next_layer_or_toplevel(struct server *server) +try_to_focus_next_layer_or_toplevel(void) { - struct seat *seat = &server->seat; - struct output *output = output_nearest_to_cursor(server); + struct seat *seat = &server.seat; + struct output *output = output_nearest_to_cursor(); if (!output_is_usable(output)) { goto no_output; } @@ -235,7 +234,7 @@ layer_try_set_focus(struct seat *seat, struct wlr_layer_surface_v1 *layer_surfac case ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE: wlr_log(WLR_DEBUG, "interactive-none '%p'", layer_surface); if (seat->focused_layer == layer_surface) { - try_to_focus_next_layer_or_toplevel(seat->server); + try_to_focus_next_layer_or_toplevel(); } break; } @@ -278,19 +277,18 @@ handle_surface_commit(struct wl_listener *listener, void *data) * cursor-button-press). */ if (is_on_demand(layer_surface)) { - struct seat *seat = &layer->server->seat; + struct seat *seat = &server.seat; if (seat->focused_layer == layer_surface) { /* * Must be change from EXCLUSIVE to ON_DEMAND, * so we should give us focus. */ - struct server *server = layer->server; - try_to_focus_next_layer_or_toplevel(server); + try_to_focus_next_layer_or_toplevel(); } goto out; } /* Handle EXCLUSIVE and NONE requests */ - struct seat *seat = &layer->server->seat; + struct seat *seat = &server.seat; layer_try_set_focus(seat, layer_surface); } out: @@ -302,7 +300,7 @@ out: * Update cursor focus here to ensure we * enter a new/moved/resized layer surface. */ - cursor_update_focus(layer->server); + cursor_update_focus(); } } @@ -312,7 +310,7 @@ handle_node_destroy(struct wl_listener *listener, void *data) struct lab_layer_surface *layer = wl_container_of(listener, layer, node_destroy); - struct seat *seat = &layer->server->seat; + struct seat *seat = &server.seat; /* * If the surface of this node has the current keyboard focus, then we @@ -374,11 +372,11 @@ handle_unmap(struct wl_listener *listener, void *data) if (layer_surface->output) { output_update_usable_area(layer_surface->output->data); } - struct seat *seat = &layer->server->seat; + struct seat *seat = &server.seat; if (seat->focused_layer == layer_surface) { - try_to_focus_next_layer_or_toplevel(layer->server); + try_to_focus_next_layer_or_toplevel(); } - cursor_update_focus(layer->server); + cursor_update_focus(); layer->being_unmapped = false; } @@ -424,14 +422,14 @@ handle_map(struct wl_listener *listener, void *data) return; } - struct seat *seat = &layer->server->seat; + struct seat *seat = &server.seat; layer_try_set_focus(seat, layer->scene_layer_surface->layer_surface); } static bool surface_is_focused(struct seat *seat, struct wlr_surface *surface) { - return seat->seat->keyboard_state.focused_surface == surface; + return seat->wlr_seat->keyboard_state.focused_surface == surface; } static void @@ -439,7 +437,7 @@ handle_popup_destroy(struct wl_listener *listener, void *data) { struct lab_layer_popup *popup = wl_container_of(listener, popup, destroy); - struct seat *seat = &popup->server->seat; + struct seat *seat = &server.seat; struct wlr_xdg_popup *_popup, *tmp; wl_list_for_each_safe(_popup, tmp, &popup->wlr_popup->base->popups, link) { @@ -461,10 +459,10 @@ handle_popup_destroy(struct wl_listener *listener, void *data) if (popup->parent_was_focused && popup->wlr_popup->parent) { seat_force_focus_surface(seat, popup->wlr_popup->parent); } else { - desktop_focus_topmost_view(popup->server); + desktop_focus_topmost_view(); } } - cursor_update_focus(popup->server); + cursor_update_focus(); free(popup); } @@ -529,8 +527,7 @@ handle_popup_commit(struct wl_listener *listener, void *data) popup->commit.notify = NULL; /* Force focus when popup was triggered by IPC */ - struct server *server = popup->server; - struct seat *seat = &server->seat; + struct seat *seat = &server.seat; bool requesting_grab = !!popup->wlr_popup->seat; if (requesting_grab) { if (surface_is_focused(seat, popup->wlr_popup->parent)) { @@ -553,18 +550,14 @@ handle_popup_reposition(struct wl_listener *listener, void *data) static void handle_popup_new_popup(struct wl_listener *listener, void *data); static struct lab_layer_popup * -create_popup(struct server *server, struct wlr_xdg_popup *wlr_popup, +create_popup(struct wlr_xdg_popup *wlr_popup, struct wlr_scene_tree *parent) { struct lab_layer_popup *popup = znew(*popup); - popup->server = server; popup->wlr_popup = wlr_popup; popup->scene_tree = wlr_scene_xdg_surface_create(parent, wlr_popup->base); - if (!popup->scene_tree) { - free(popup); - return NULL; - } + die_if_null(popup->scene_tree); /* In support of IME popup */ wlr_popup->base->surface->data = popup->scene_tree; @@ -594,16 +587,9 @@ handle_popup_new_popup(struct wl_listener *listener, void *data) struct lab_layer_popup *lab_layer_popup = wl_container_of(listener, lab_layer_popup, new_popup); struct wlr_xdg_popup *wlr_popup = data; - struct lab_layer_popup *new_popup = create_popup( - lab_layer_popup->server, wlr_popup, + struct lab_layer_popup *new_popup = create_popup(wlr_popup, lab_layer_popup->scene_tree); - if (!new_popup) { - wl_resource_post_no_memory(wlr_popup->resource); - wlr_xdg_popup_destroy(wlr_popup); - return; - } - new_popup->output_toplevel_sx_box = lab_layer_popup->output_toplevel_sx_box; } @@ -616,12 +602,11 @@ static void move_popup_to_top_layer(struct lab_layer_surface *toplevel, struct lab_layer_popup *popup) { - struct server *server = toplevel->server; struct wlr_output *wlr_output = toplevel->scene_layer_surface->layer_surface->output; struct output *output = (struct output *)wlr_output->data; struct wlr_box box = { 0 }; - wlr_output_layout_get_box(server->output_layout, wlr_output, &box); + wlr_output_layout_get_box(server.output_layout, wlr_output, &box); int lx = toplevel->scene_layer_surface->tree->node.x + box.x; int ly = toplevel->scene_layer_surface->tree->node.y + box.y; @@ -639,7 +624,6 @@ handle_new_popup(struct wl_listener *listener, void *data) wl_container_of(listener, toplevel, new_popup); struct wlr_xdg_popup *wlr_popup = data; - struct server *server = toplevel->server; struct wlr_scene_layer_surface_v1 *surface = toplevel->scene_layer_surface; struct output *output = surface->layer_surface->output->data; @@ -647,7 +631,7 @@ handle_new_popup(struct wl_listener *listener, void *data) wlr_scene_node_coords(&surface->tree->node, &lx, &ly); struct wlr_box output_box = { 0 }; - wlr_output_layout_get_box(server->output_layout, + wlr_output_layout_get_box(server.output_layout, output->wlr_output, &output_box); /* @@ -663,13 +647,7 @@ handle_new_popup(struct wl_listener *listener, void *data) .height = output_box.height, }; struct lab_layer_popup *popup = - create_popup(server, wlr_popup, surface->tree); - if (!popup) { - wl_resource_post_no_memory(wlr_popup->resource); - wlr_xdg_popup_destroy(wlr_popup); - return; - } - + create_popup(wlr_popup, surface->tree); popup->output_toplevel_sx_box = output_toplevel_sx_box; if (surface->layer_surface->current.layer @@ -681,12 +659,10 @@ handle_new_popup(struct wl_listener *listener, void *data) static void handle_new_layer_surface(struct wl_listener *listener, void *data) { - struct server *server = wl_container_of( - listener, server, new_layer_surface); struct wlr_layer_surface_v1 *layer_surface = data; if (!layer_surface->output) { - struct output *output = output_nearest_to_cursor(server); + struct output *output = output_nearest_to_cursor(); if (!output || !output->scene_output) { /* * We are not using output_is_usable() here because @@ -716,11 +692,7 @@ handle_new_layer_surface(struct wl_listener *listener, void *data) surface->scene_layer_surface = wlr_scene_layer_surface_v1_create( selected_layer, layer_surface); - if (!surface->scene_layer_surface) { - wlr_layer_surface_v1_destroy(layer_surface); - wlr_log(WLR_ERROR, "could not create layer surface"); - return; - } + die_if_null(surface->scene_layer_surface); /* In support of IME popup */ layer_surface->surface->data = surface->scene_layer_surface->tree; @@ -728,7 +700,6 @@ handle_new_layer_surface(struct wl_listener *listener, void *data) node_descriptor_create(&surface->scene_layer_surface->tree->node, LAB_NODE_LAYER_SURFACE, /*view*/ NULL, surface); - surface->server = server; surface->scene_layer_surface->layer_surface = layer_surface; surface->surface_commit.notify = handle_surface_commit; @@ -754,17 +725,17 @@ handle_new_layer_surface(struct wl_listener *listener, void *data) } void -layers_init(struct server *server) +layers_init(void) { - server->layer_shell = wlr_layer_shell_v1_create(server->wl_display, + server.layer_shell = wlr_layer_shell_v1_create(server.wl_display, LAB_LAYERSHELL_VERSION); - server->new_layer_surface.notify = handle_new_layer_surface; - wl_signal_add(&server->layer_shell->events.new_surface, - &server->new_layer_surface); + server.new_layer_surface.notify = handle_new_layer_surface; + wl_signal_add(&server.layer_shell->events.new_surface, + &server.new_layer_surface); } void -layers_finish(struct server *server) +layers_finish(void) { - wl_list_remove(&server->new_layer_surface.link); + wl_list_remove(&server.new_layer_surface.link); } diff --git a/src/magnifier.c b/src/magnifier.c index 424865c9..43a71746 100644 --- a/src/magnifier.c +++ b/src/magnifier.c @@ -41,8 +41,7 @@ box_logical_to_physical(struct wlr_box *box, struct wlr_output *output) void magnifier_draw(struct output *output, struct wlr_buffer *output_buffer, struct wlr_box *damage) { - struct server *server = output->server; - struct theme *theme = server->theme; + struct theme *theme = rc.theme; bool fullscreen = (rc.mag_width == -1 || rc.mag_height == -1); struct wlr_box output_box = { @@ -51,9 +50,9 @@ magnifier_draw(struct output *output, struct wlr_buffer *output_buffer, struct w }; /* Cursor position in per-output logical coordinate */ - double cursor_logical_x = server->seat.cursor->x; - double cursor_logical_y = server->seat.cursor->y; - wlr_output_layout_output_coords(server->output_layout, + double cursor_logical_x = server.seat.cursor->x; + double cursor_logical_y = server.seat.cursor->y; + wlr_output_layout_output_coords(server.output_layout, output->wlr_output, &cursor_logical_x, &cursor_logical_y); /* Cursor position in per-output physical coordinate */ struct wlr_box cursor_pos = { @@ -96,7 +95,7 @@ magnifier_draw(struct output *output, struct wlr_buffer *output_buffer, struct w } if (!tmp_buffer) { tmp_buffer = wlr_allocator_create_buffer( - server->allocator, mag_box.width, mag_box.height, + server.allocator, mag_box.width, mag_box.height, &output->wlr_output->swapchain->format); } if (!tmp_buffer) { @@ -105,7 +104,7 @@ magnifier_draw(struct output *output, struct wlr_buffer *output_buffer, struct w } if (!tmp_texture) { - tmp_texture = wlr_texture_from_buffer(server->renderer, tmp_buffer); + tmp_texture = wlr_texture_from_buffer(server.renderer, tmp_buffer); } if (!tmp_texture) { wlr_log(WLR_ERROR, "Failed to allocate temporary magnifier texture"); @@ -116,7 +115,7 @@ magnifier_draw(struct output *output, struct wlr_buffer *output_buffer, struct w /* Extract source region into temporary buffer */ struct wlr_render_pass *tmp_render_pass = wlr_renderer_begin_buffer_pass( - server->renderer, tmp_buffer, NULL); + server.renderer, tmp_buffer, NULL); if (!tmp_render_pass) { wlr_log(WLR_ERROR, "Failed to begin magnifier render pass"); return; @@ -124,7 +123,7 @@ magnifier_draw(struct output *output, struct wlr_buffer *output_buffer, struct w wlr_buffer_lock(output_buffer); struct wlr_texture *output_texture = wlr_texture_from_buffer( - server->renderer, output_buffer); + server.renderer, output_buffer); if (!output_texture) { goto cleanup; } @@ -151,7 +150,7 @@ magnifier_draw(struct output *output, struct wlr_buffer *output_buffer, struct w /* Render to the output buffer itself */ tmp_render_pass = wlr_renderer_begin_buffer_pass( - server->renderer, output_buffer, NULL); + server.renderer, output_buffer, NULL); if (!tmp_render_pass) { wlr_log(WLR_ERROR, "Failed to begin second magnifier render pass"); goto cleanup; @@ -224,7 +223,7 @@ output_wants_magnification(struct output *output) { static double x = -1; static double y = -1; - struct wlr_cursor *cursor = output->server->seat.cursor; + struct wlr_cursor *cursor = server.seat.cursor; if (!magnify_on) { x = -1; y = -1; @@ -235,24 +234,24 @@ output_wants_magnification(struct output *output) } x = cursor->x; y = cursor->y; - return output_nearest_to_cursor(output->server) == output; + return output_nearest_to_cursor() == output; } static void -enable_magnifier(struct server *server, bool enable) +enable_magnifier(bool enable) { magnify_on = enable; - server->scene->WLR_PRIVATE.direct_scanout = enable ? false - : server->direct_scanout_enabled; + server.scene->WLR_PRIVATE.direct_scanout = enable ? false + : server.direct_scanout_enabled; } /* Toggles magnification on and off */ void -magnifier_toggle(struct server *server) +magnifier_toggle(void) { - enable_magnifier(server, !magnify_on); + enable_magnifier(!magnify_on); - struct output *output = output_nearest_to_cursor(server); + struct output *output = output_nearest_to_cursor(); if (output) { wlr_output_schedule_frame(output->wlr_output); } @@ -260,22 +259,22 @@ magnifier_toggle(struct server *server) /* Increases and decreases magnification scale */ void -magnifier_set_scale(struct server *server, enum magnify_dir dir) +magnifier_set_scale(enum magnify_dir dir) { - struct output *output = output_nearest_to_cursor(server); + struct output *output = output_nearest_to_cursor(); if (dir == MAGNIFY_INCREASE) { if (magnify_on) { mag_scale += rc.mag_increment; } else { - enable_magnifier(server, true); + enable_magnifier(true); mag_scale = 1.0 + rc.mag_increment; } } else { if (magnify_on && mag_scale > 1.0 + rc.mag_increment) { mag_scale -= rc.mag_increment; } else { - enable_magnifier(server, false); + enable_magnifier(false); } } diff --git a/src/main.c b/src/main.c index 755a0a0c..081bd62a 100644 --- a/src/main.c +++ b/src/main.c @@ -4,9 +4,12 @@ #include #include #include +#include #include "common/fd-util.h" #include "common/font.h" +#include "common/macros.h" #include "common/spawn.h" +#include "common/string-helpers.h" #include "config/rcxml.h" #include "config/session.h" #include "labwc.h" @@ -14,7 +17,16 @@ #include "translate.h" #include "menu/menu.h" +/* + * Globals + * + * Rationale: these are unlikely to ever have more than one instance + * per process, and need to last for the lifetime of the process. + * Accessing them indirectly through pointers embedded in every other + * struct just adds noise to the code. + */ struct rcxml rc = { 0 }; +struct server server = { 0 }; static const struct option long_options[] = { {"config", required_argument, NULL, 'c'}, @@ -26,6 +38,7 @@ static const struct option long_options[] = { {"reconfigure", no_argument, NULL, 'r'}, {"startup", required_argument, NULL, 's'}, {"session", required_argument, NULL, 'S'}, + {"title", required_argument, NULL, 't'}, {"version", no_argument, NULL, 'v'}, {"verbose", no_argument, NULL, 'V'}, {0, 0, 0, 0} @@ -42,6 +55,7 @@ static const char labwc_usage[] = " -r, --reconfigure Reload the compositor configuration\n" " -s, --startup Run command on startup\n" " -S, --session Run command on startup and terminate on exit\n" +" -t, --title Specify title to use when running in a window\n" " -v, --version Show version number and quit\n" " -V, --verbose Enable more verbose logging\n"; @@ -56,12 +70,15 @@ static void print_version(void) { #define FEATURE_ENABLED(feature) (HAVE_##feature ? "+" : "-") - printf("labwc %s (%sxwayland %snls %srsvg %slibsfdo)\n", + printf("labwc %s (%sxwayland %snls %srsvg %slibsfdo) wlroots-%d.%d.%d\n", LABWC_VERSION, FEATURE_ENABLED(XWAYLAND), FEATURE_ENABLED(NLS), FEATURE_ENABLED(RSVG), - FEATURE_ENABLED(LIBSFDO) + FEATURE_ENABLED(LIBSFDO), + wlr_version_get_major(), + wlr_version_get_minor(), + wlr_version_get_micro() ); #undef FEATURE_ENABLED } @@ -121,7 +138,6 @@ send_signal_to_labwc_pid(int signal) } struct idle_ctx { - struct server *server; const char *primary_client; const char *startup_cmd; }; @@ -134,16 +150,16 @@ idle_callback(void *data) /* Start session-manager if one is specified by -S|--session */ if (ctx->primary_client) { - ctx->server->primary_client_pid = spawn_primary_client(ctx->primary_client); - if (ctx->server->primary_client_pid < 0) { + server.primary_client_pid = spawn_primary_client(ctx->primary_client); + if (server.primary_client_pid < 0) { wlr_log(WLR_ERROR, "fatal error starting primary client: %s", ctx->primary_client); - wl_display_terminate(ctx->server->wl_display); + wl_display_terminate(server.wl_display); return; } } - session_autostart_init(ctx->server); + session_autostart_init(); if (ctx->startup_cmd) { spawn_async_no_shell(ctx->startup_cmd); } @@ -156,10 +172,16 @@ main(int argc, char *argv[]) char *primary_client = NULL; enum wlr_log_importance verbosity = WLR_ERROR; + server.wlr_version = _LAB_CALC_WLR_VERSION_NUM( + wlr_version_get_major(), + wlr_version_get_minor(), + wlr_version_get_micro() + ); + int c; while (1) { int index = 0; - c = getopt_long(argc, argv, "c:C:dehmrs:S:vV", long_options, &index); + c = getopt_long(argc, argv, "c:C:dehmrs:S:t:vV", long_options, &index); if (c == -1) { break; } @@ -188,6 +210,9 @@ main(int argc, char *argv[]) case 'S': primary_client = optarg; break; + case 't': + server.title_fmt = optarg; + break; case 'v': print_version(); exit(0); @@ -246,20 +271,21 @@ main(int argc, char *argv[]) increase_nofile_limit(); - struct server server = { 0 }; - server_init(&server); - server_start(&server); + if (string_null_or_empty(server.title_fmt)) { + server.title_fmt = "labwc - %o"; + } + + server_init(); + server_start(); struct theme theme = { 0 }; - theme_init(&theme, &server, rc.theme_name); + theme_init(&theme, rc.theme_name); rc.theme = &theme; - server.theme = &theme; - menu_init(&server); + menu_init(); /* Delay startup of applications until the event loop is ready */ struct idle_ctx idle_ctx = { - .server = &server, .primary_client = primary_client, .startup_cmd = startup_cmd }; @@ -267,14 +293,14 @@ main(int argc, char *argv[]) wl_display_run(server.wl_display); - session_shutdown(&server); + session_shutdown(); - menu_finish(&server); + menu_finish(); theme_finish(&theme); rcxml_finish(); font_finish(); - server_finish(&server); + server_finish(); return 0; } diff --git a/src/menu/menu.c b/src/menu/menu.c index 0c87f44d..c0e93c59 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -2,9 +2,10 @@ #define _POSIX_C_SOURCE 200809L #include "menu/menu.h" #include +#include #include #include -#include +#include #include #include #include @@ -19,6 +20,7 @@ #include "common/lab-scene-rect.h" #include "common/list.h" #include "common/mem.h" +#include "common/scene-helpers.h" #include "common/spawn.h" #include "common/string-helpers.h" #include "common/xml.h" @@ -54,10 +56,10 @@ struct menu_pipe_context { /* TODO: split this whole file into parser.c and actions.c*/ static bool -is_unique_id(struct server *server, const char *id) +is_unique_id(const char *id) { struct menu *menu; - wl_list_for_each(menu, &server->menus, link) { + wl_list_for_each(menu, &server.menus, link) { if (!strcmp(menu->id, id)) { return false; } @@ -66,33 +68,32 @@ is_unique_id(struct server *server, const char *id) } static struct menu * -menu_create(struct server *server, struct menu *parent, const char *id, +menu_create(struct menu *parent, const char *id, const char *label) { - if (!is_unique_id(server, id)) { + if (!is_unique_id(id)) { wlr_log(WLR_ERROR, "menu id %s already exists", id); } struct menu *menu = znew(*menu); - wl_list_append(&server->menus, &menu->link); + wl_list_append(&server.menus, &menu->link); wl_list_init(&menu->menuitems); menu->id = xstrdup(id); menu->label = xstrdup(label ? label : id); menu->parent = parent; - menu->server = server; menu->is_pipemenu_child = waiting_for_pipe_menu; return menu; } struct menu * -menu_get_by_id(struct server *server, const char *id) +menu_get_by_id(const char *id) { if (!id) { return NULL; } struct menu *menu; - wl_list_for_each(menu, &server->menus, link) { + wl_list_for_each(menu, &server.menus, link) { if (!strcmp(menu->id, id)) { return menu; } @@ -122,27 +123,114 @@ validate_menu(struct menu *menu) } static void -validate(struct server *server) +validate(void) { struct menu *menu; - wl_list_for_each(menu, &server->menus, link) { + wl_list_for_each(menu, &server.menus, link) { validate_menu(menu); } } +/* Read a single Unicode codepoint and convert it to lowercase */ +static uint32_t +read_unicode_char_lowercase(const char *first_byte, size_t *bytes_read) +{ + assert(bytes_read); + + if (string_null_or_empty(first_byte)) { + *bytes_read = 0; + return 0; + } + + gunichar codepoint = g_utf8_get_char_validated(first_byte, -1); + + bool partial_read = (codepoint == (gunichar)-2); + bool failed_read = (codepoint == (gunichar)-1); + if (partial_read || failed_read) { + /* Read only the first byte */ + *bytes_read = 1; + return (uint32_t)(unsigned char)first_byte[0]; + } + + *bytes_read = (size_t)(g_utf8_next_char(first_byte) - first_byte); + + return (uint32_t)g_unichar_tolower(codepoint); +} + +/* Retrieve the accelerator from an item label */ +static void +item_parse_accelerator(struct menuitem *item, const char *text) +{ + const char *accel_ptr = NULL; + char *underscore = strchr(text, '_'); + while (underscore) { + if (underscore[1] == '_') { + /* Ignore escaped underscores */ + underscore = strchr(underscore + 2, '_'); + } else if (underscore[1] != '\0') { + /* Found a valid accelerator */ + accel_ptr = underscore + 1; + break; + } else { + /* Ignore empty accelerator */ + break; + } + } + + size_t bytes_read = 0; + if (!accel_ptr) { + /* Default to the first char */ + item->text = xstrdup(text); + item->accelerator = read_unicode_char_lowercase(text, &bytes_read); + } else { + /* Set the accelerator and remove the preceding underscore */ + item->use_markup = true; + item->accelerator = read_unicode_char_lowercase(accel_ptr, &bytes_read); + item->text = strdup_printf("%.*s%.*s%s", + /* Prefix length + prefix */ + (int)(accel_ptr - 1 - text), text, + /* Accelerator (utf-8 byte) length + accelerator */ + (int)bytes_read, accel_ptr, + /* Remainder */ + accel_ptr + bytes_read); + } +} + +/* Remove underscores used for escaping other underscores from a string */ +static void +unescape_underscores(char *text) +{ + if (!text) { + return; + } + + char *src = text; + char *dst = text; + while (*src) { + if (*src == '_' && *(src + 1) == '_') { + *dst++ = '_'; + src += 2; + } else { + *dst++ = *src++; + } + } + *dst = '\0'; +} + static struct menuitem * item_create(struct menu *menu, const char *text, const char *icon_name, bool show_arrow) { assert(menu); assert(text); - struct theme *theme = menu->server->theme; + struct theme *theme = rc.theme; struct menuitem *menuitem = znew(*menuitem); menuitem->parent = menu; menuitem->selectable = true; menuitem->type = LAB_MENU_ITEM; - menuitem->text = xstrdup(text); menuitem->arrow = show_arrow ? "›" : NULL; + item_parse_accelerator(menuitem, text); + unescape_underscores(menuitem->text); #if HAVE_LIBSFDO if (rc.menu_show_icons && !string_null_or_empty(icon_name)) { @@ -167,10 +255,10 @@ item_create_scene_for_state(struct menuitem *item, float *text_color, float *bg_color) { struct menu *menu = item->parent; - struct theme *theme = menu->server->theme; + struct theme *theme = rc.theme; /* Tree to hold background and label buffers */ - struct wlr_scene_tree *tree = wlr_scene_tree_create(item->tree); + struct wlr_scene_tree *tree = lab_wlr_scene_tree_create(item->tree); int icon_width = 0; int icon_size = ICON_SIZE; @@ -190,14 +278,14 @@ item_create_scene_for_state(struct menuitem *item, float *text_color, } /* Create background */ - wlr_scene_rect_create(tree, bg_width, theme->menu_item_height, bg_color); + lab_wlr_scene_rect_create(tree, bg_width, theme->menu_item_height, bg_color); /* Create icon */ bool show_app_icon = !strcmp(item->parent->id, "client-list-combined-menu") && item->client_list_view; if (item->icon_name || show_app_icon) { struct scaled_icon_buffer *icon_buffer = scaled_icon_buffer_create( - tree, menu->server, icon_size, icon_size); + tree, icon_size, icon_size); if (item->icon_name) { /* icon set via */ scaled_icon_buffer_set_icon_name(icon_buffer, item->icon_name); @@ -213,8 +301,8 @@ item_create_scene_for_state(struct menuitem *item, float *text_color, /* Create label */ struct scaled_font_buffer *label_buffer = scaled_font_buffer_create(tree); assert(label_buffer); - scaled_font_buffer_update(label_buffer, item->text, label_max_width, - &rc.font_menuitem, text_color, bg_color); + scaled_font_buffer_update_markup(label_buffer, item->text, label_max_width, + &rc.font_menuitem, text_color, bg_color, item->use_markup); /* Vertically center and left-align label */ int x = theme->menu_items_padding_x + icon_width; int y = (theme->menu_item_height - label_buffer->height) / 2; @@ -243,10 +331,10 @@ item_create_scene(struct menuitem *menuitem, int *item_y) assert(menuitem); assert(menuitem->type == LAB_MENU_ITEM); struct menu *menu = menuitem->parent; - struct theme *theme = menu->server->theme; + struct theme *theme = rc.theme; /* Menu item root node */ - menuitem->tree = wlr_scene_tree_create(menu->scene_tree); + menuitem->tree = lab_wlr_scene_tree_create(menu->scene_tree); node_descriptor_create(&menuitem->tree->node, LAB_NODE_MENUITEM, /*view*/ NULL, menuitem); @@ -292,15 +380,15 @@ separator_create_scene(struct menuitem *menuitem, int *item_y) assert(menuitem); assert(menuitem->type == LAB_MENU_SEPARATOR_LINE); struct menu *menu = menuitem->parent; - struct theme *theme = menu->server->theme; + struct theme *theme = rc.theme; /* Menu item root node */ - menuitem->tree = wlr_scene_tree_create(menu->scene_tree); + menuitem->tree = lab_wlr_scene_tree_create(menu->scene_tree); node_descriptor_create(&menuitem->tree->node, LAB_NODE_MENUITEM, /*view*/ NULL, menuitem); /* Tree to hold background and line buffer */ - menuitem->normal_tree = wlr_scene_tree_create(menuitem->tree); + menuitem->normal_tree = lab_wlr_scene_tree_create(menuitem->tree); int bg_height = theme->menu_separator_line_thickness + 2 * theme->menu_separator_padding_height; @@ -313,11 +401,11 @@ separator_create_scene(struct menuitem *menuitem, int *item_y) } /* Item background nodes */ - wlr_scene_rect_create(menuitem->normal_tree, bg_width, bg_height, + lab_wlr_scene_rect_create(menuitem->normal_tree, bg_width, bg_height, theme->menu_items_bg_color); /* Draw separator line */ - struct wlr_scene_rect *line_rect = wlr_scene_rect_create( + struct wlr_scene_rect *line_rect = lab_wlr_scene_rect_create( menuitem->normal_tree, line_width, theme->menu_separator_line_thickness, theme->menu_separator_color); @@ -338,17 +426,17 @@ title_create_scene(struct menuitem *menuitem, int *item_y) assert(menuitem); assert(menuitem->type == LAB_MENU_TITLE); struct menu *menu = menuitem->parent; - struct theme *theme = menu->server->theme; + struct theme *theme = rc.theme; float *bg_color = theme->menu_title_bg_color; float *text_color = theme->menu_title_text_color; /* Menu item root node */ - menuitem->tree = wlr_scene_tree_create(menu->scene_tree); + menuitem->tree = lab_wlr_scene_tree_create(menu->scene_tree); node_descriptor_create(&menuitem->tree->node, LAB_NODE_MENUITEM, /*view*/ NULL, menuitem); /* Tree to hold background and text buffer */ - menuitem->normal_tree = wlr_scene_tree_create(menuitem->tree); + menuitem->normal_tree = lab_wlr_scene_tree_create(menuitem->tree); int bg_width = menu->size.width - 2 * theme->menu_border_width; int text_width = bg_width - 2 * theme->menu_items_padding_x; @@ -359,7 +447,7 @@ title_create_scene(struct menuitem *menuitem, int *item_y) } /* Background */ - wlr_scene_rect_create(menuitem->normal_tree, + lab_wlr_scene_rect_create(menuitem->normal_tree, bg_width, theme->menu_header_height, bg_color); /* Draw separator title */ @@ -412,11 +500,11 @@ static void menu_create_scene(struct menu *menu) { struct menuitem *item; - struct theme *theme = menu->server->theme; + struct theme *theme = rc.theme; assert(!menu->scene_tree); - menu->scene_tree = wlr_scene_tree_create(menu->server->menu_tree); + menu->scene_tree = lab_wlr_scene_tree_create(server.menu_tree); wlr_scene_node_set_enabled(&menu->scene_tree->node, false); /* Menu width is the maximum item width, capped by menu.width.{min,max} */ @@ -504,10 +592,10 @@ item_destroy(struct menuitem *item) free(item); } -static bool parse_buf(struct server *server, struct menu *menu, struct buf *buf); +static bool parse_buf(struct menu *menu, struct buf *buf); static int handle_pipemenu_readable(int fd, uint32_t mask, void *_ctx); static int handle_pipemenu_timeout(void *_ctx); -static void fill_menu_children(struct server *server, struct menu *parent, xmlNode *n); +static void fill_menu_children(struct menu *parent, xmlNode *n); /* * elements have three different roles: @@ -516,7 +604,7 @@ static void fill_menu_children(struct server *server, struct menu *parent, xmlNo * * Menuitem of submenu type - has ID only */ static void -fill_menu(struct server *server, struct menu *parent, xmlNode *n) +fill_menu(struct menu *parent, xmlNode *n) { char *label = (char *)xmlGetProp(n, (const xmlChar *)"label"); char *icon_name = (char *)xmlGetProp(n, (const xmlChar *)"icon"); @@ -531,7 +619,7 @@ fill_menu(struct server *server, struct menu *parent, xmlNode *n) if (execute && label) { wlr_log(WLR_DEBUG, "pipemenu '%s:%s:%s'", id, label, execute); - struct menu *pipemenu = menu_create(server, parent, id, label); + struct menu *pipemenu = menu_create(parent, id, label); pipemenu->execute = xstrdup(execute); if (!parent) { /* @@ -539,7 +627,7 @@ fill_menu(struct server *server, struct menu *parent, xmlNode *n) * * * - * + * * */ } else { @@ -568,7 +656,7 @@ fill_menu(struct server *server, struct menu *parent, xmlNode *n) * attribute to make it easier for users to define "root-menu" * and "client-menu". */ - struct menu *menu = menu_create(server, parent, id, label); + struct menu *menu = menu_create(parent, id, label); if (icon_name) { menu->icon_name = xstrdup(icon_name); } @@ -581,7 +669,7 @@ fill_menu(struct server *server, struct menu *parent, xmlNode *n) icon_name, true); item->submenu = menu; } - fill_menu_children(server, menu, n); + fill_menu_children(menu, n); } else { /* * (when inside another element) creates an @@ -598,7 +686,7 @@ fill_menu(struct server *server, struct menu *parent, xmlNode *n) goto error; } - struct menu *menu = menu_get_by_id(server, id); + struct menu *menu = menu_get_by_id(id); if (!menu) { wlr_log(WLR_ERROR, "no menu with id '%s'", id); goto error; @@ -636,13 +724,13 @@ fill_separator(struct menu *menu, xmlNode *n) /* parent==NULL when processing toplevel menus in menu.xml */ static void -fill_menu_children(struct server *server, struct menu *parent, xmlNode *n) +fill_menu_children(struct menu *parent, xmlNode *n) { xmlNode *child; char *key, *content; LAB_XML_FOR_EACH(n, child, key, content) { if (!strcasecmp(key, "menu")) { - fill_menu(server, parent, child); + fill_menu(parent, child); } else if (!strcasecmp(key, "separator")) { if (!parent) { wlr_log(WLR_ERROR, @@ -662,7 +750,7 @@ fill_menu_children(struct server *server, struct menu *parent, xmlNode *n) } static bool -parse_buf(struct server *server, struct menu *parent, struct buf *buf) +parse_buf(struct menu *parent, struct buf *buf) { int options = 0; xmlDoc *d = xmlReadMemory(buf->data, buf->len, NULL, NULL, options); @@ -672,7 +760,7 @@ parse_buf(struct server *server, struct menu *parent, struct buf *buf) } xmlNode *root = xmlDocGetRootElement(d); - fill_menu_children(server, parent, root); + fill_menu_children(parent, root); xmlFreeDoc(d); xmlCleanupParser(); @@ -680,7 +768,7 @@ parse_buf(struct server *server, struct menu *parent, struct buf *buf) } static void -parse_xml(const char *filename, struct server *server) +parse_xml(const char *filename) { struct wl_list paths; paths_config_create(&paths, filename); @@ -696,7 +784,7 @@ parse_xml(const char *filename, struct server *server) continue; } wlr_log(WLR_INFO, "read menu file %s", path->string); - parse_buf(server, /*parent*/ NULL, &buf); + parse_buf(/*parent*/ NULL, &buf); buf_reset(&buf); if (!should_merge_config) { break; @@ -729,8 +817,7 @@ static void menu_reposition(struct menu *menu, struct wlr_box anchor_rect) { /* Get output usable area to place the menu within */ - struct output *output = output_nearest_to(menu->server, - anchor_rect.x, anchor_rect.y); + struct output *output = output_nearest_to(anchor_rect.x, anchor_rect.y); if (!output_is_usable(output)) { wlr_log(WLR_ERROR, "no output found around (%d,%d)", anchor_rect.x, anchor_rect.y); @@ -775,14 +862,14 @@ menu_reposition(struct menu *menu, struct wlr_box anchor_rect) } static void -menu_hide_submenu(struct server *server, const char *id) +menu_hide_submenu(const char *id) { struct menu *menu, *hide_menu; - hide_menu = menu_get_by_id(server, id); + hide_menu = menu_get_by_id(id); if (!hide_menu) { return; } - wl_list_for_each(menu, &server->menus, link) { + wl_list_for_each(menu, &server.menus, link) { struct menuitem *item, *next; wl_list_for_each_safe(item, next, &menu->menuitems, link) { if (item->submenu == hide_menu) { @@ -809,9 +896,9 @@ item_add_action(struct menuitem *item, const char *action_name) * SendToDesktop, left/right options are included. */ static void -update_client_send_to_menu(struct server *server) +update_client_send_to_menu(void) { - struct menu *menu = menu_get_by_id(server, "client-send-to-menu"); + struct menu *menu = menu_get_by_id("client-send-to-menu"); assert(menu); reset_menu(menu); @@ -823,8 +910,8 @@ update_client_send_to_menu(struct server *server) * GoToDesktop will be called as part of the action. */ struct buf buf = BUF_INIT; - wl_list_for_each(workspace, &server->workspaces.all, link) { - if (workspace == server->workspaces.current) { + wl_list_for_each(workspace, &server.workspaces.all, link) { + if (workspace == server.workspaces.current) { buf_add_fmt(&buf, ">%s<", workspace->name); } else { buf_add(&buf, workspace->name); @@ -856,9 +943,9 @@ update_client_send_to_menu(struct server *server) * name. Active view is indicated by "*" preceding title. */ static void -update_client_list_combined_menu(struct server *server) +update_client_list_combined_menu(void) { - struct menu *menu = menu_get_by_id(server, "client-list-combined-menu"); + struct menu *menu = menu_get_by_id("client-list-combined-menu"); assert(menu); reset_menu(menu); @@ -868,20 +955,20 @@ update_client_list_combined_menu(struct server *server) struct view *view; struct buf buffer = BUF_INIT; - wl_list_for_each(workspace, &server->workspaces.all, link) { - buf_add_fmt(&buffer, workspace == server->workspaces.current ? ">%s<" : "%s", + wl_list_for_each(workspace, &server.workspaces.all, link) { + buf_add_fmt(&buffer, workspace == server.workspaces.current ? ">%s<" : "%s", workspace->name); separator_create(menu, buffer.data); buf_clear(&buffer); - wl_list_for_each(view, &server->views, link) { + wl_list_for_each(view, &server.views, link) { if (view->workspace == workspace) { if (!view->foreign_toplevel || string_null_or_empty(view->title)) { continue; } - if (view == server->active_view) { + if (view == server.active_view) { buf_add(&buffer, "*"); } if (view->minimized) { @@ -908,14 +995,14 @@ update_client_list_combined_menu(struct server *server) } static void -init_rootmenu(struct server *server) +init_rootmenu(void) { - struct menu *menu = menu_get_by_id(server, "root-menu"); + struct menu *menu = menu_get_by_id("root-menu"); struct menuitem *item; /* Default menu if no menu.xml found */ if (!menu) { - menu = menu_create(server, NULL, "root-menu", ""); + menu = menu_create(NULL, "root-menu", ""); item = item_create(menu, _("Terminal"), NULL, false); struct action *action = item_add_action(item, "Execute"); @@ -931,14 +1018,14 @@ init_rootmenu(struct server *server) } static void -init_windowmenu(struct server *server) +init_windowmenu(void) { - struct menu *menu = menu_get_by_id(server, "client-menu"); + struct menu *menu = menu_get_by_id("client-menu"); struct menuitem *item; /* Default menu if no menu.xml found */ if (!menu) { - menu = menu_create(server, NULL, "client-menu", ""); + menu = menu_create(NULL, "client-menu", ""); item = item_create(menu, _("Minimize"), NULL, false); item_add_action(item, "Iconify"); item = item_create(menu, _("Maximize"), NULL, false); @@ -954,37 +1041,37 @@ init_windowmenu(struct server *server) /* Workspace sub-menu */ item = item_create(menu, _("Workspace"), NULL, true); - item->submenu = menu_get_by_id(server, "client-send-to-menu"); + item->submenu = menu_get_by_id("client-send-to-menu"); item = item_create(menu, _("Close"), NULL, false); item_add_action(item, "Close"); } if (wl_list_length(&rc.workspace_config.workspaces) == 1) { - menu_hide_submenu(server, "workspaces"); + menu_hide_submenu("workspaces"); } } void -menu_init(struct server *server) +menu_init(void) { - wl_list_init(&server->menus); + wl_list_init(&server.menus); /* Just create placeholder. Contents will be created when launched */ - menu_create(server, NULL, "client-list-combined-menu", _("Windows")); - menu_create(server, NULL, "client-send-to-menu", _("Workspace")); + menu_create(NULL, "client-list-combined-menu", _("Windows")); + menu_create(NULL, "client-send-to-menu", _("Workspace")); - parse_xml("menu.xml", server); - init_rootmenu(server); - init_windowmenu(server); - validate(server); + parse_xml("menu.xml"); + init_rootmenu(); + init_windowmenu(); + validate(); } static void nullify_item_pointing_to_this_menu(struct menu *menu) { struct menu *iter; - wl_list_for_each(iter, &menu->server->menus, link) { + wl_list_for_each(iter, &server.menus, link) { struct menuitem *item; wl_list_for_each(item, &iter->menuitems, link) { if (item->submenu == menu) { @@ -1015,8 +1102,8 @@ menu_free(struct menu *menu) /* Keep items clean on pipemenu destruction */ nullify_item_pointing_to_this_menu(menu); - if (menu->server->menu_current == menu) { - menu_close_root(menu->server); + if (server.menu_current == menu) { + menu_close_root(); } struct menuitem *item, *next; @@ -1045,10 +1132,10 @@ menu_free(struct menu *menu) } void -menu_finish(struct server *server) +menu_finish(void) { struct menu *menu, *tmp_menu; - wl_list_for_each_safe(menu, tmp_menu, &server->menus, link) { + wl_list_for_each_safe(menu, tmp_menu, &server.menus, link) { menu_free(menu); } } @@ -1056,12 +1143,10 @@ menu_finish(struct server *server) void menu_on_view_destroy(struct view *view) { - struct server *server = view->server; - /* If the view being destroy has an open window menu, then close it */ - if (server->menu_current - && server->menu_current->triggered_by_view == view) { - menu_close_root(server); + if (server.menu_current + && server.menu_current->triggered_by_view == view) { + menu_close_root(); } /* @@ -1071,7 +1156,7 @@ menu_on_view_destroy(struct view *view) */ /* Also nullify the destroyed view in client-list-combined-menu */ - struct menu *menu = menu_get_by_id(server, "client-list-combined-menu"); + struct menu *menu = menu_get_by_id("client-list-combined-menu"); if (menu) { struct menuitem *item; wl_list_for_each(item, &menu->menuitems, link) { @@ -1109,13 +1194,13 @@ menu_set_selection(struct menu *menu, struct menuitem *item) * item may be selected multiple times. */ static void -reset_pipemenus(struct server *server) +reset_pipemenus(void) { wlr_log(WLR_DEBUG, "number of menus before close=%d", - wl_list_length(&server->menus)); + wl_list_length(&server.menus)); struct menu *iter, *tmp; - wl_list_for_each_safe(iter, tmp, &server->menus, link) { + wl_list_for_each_safe(iter, tmp, &server.menus, link) { if (iter->is_pipemenu_child) { /* Destroy submenus of pipemenus */ menu_free(iter); @@ -1129,7 +1214,7 @@ reset_pipemenus(struct server *server) } wlr_log(WLR_DEBUG, "number of menus after close=%d", - wl_list_length(&server->menus)); + wl_list_length(&server.menus)); } static void @@ -1163,9 +1248,9 @@ static void open_menu(struct menu *menu, struct wlr_box anchor_rect) { if (!strcmp(menu->id, "client-list-combined-menu")) { - update_client_list_combined_menu(menu->server); + update_client_list_combined_menu(); } else if (!strcmp(menu->id, "client-send-to-menu")) { - update_client_send_to_menu(menu->server); + update_client_send_to_menu(); } if (!menu->scene_tree) { @@ -1183,11 +1268,11 @@ menu_open_root(struct menu *menu, int x, int y) { assert(menu); - if (menu->server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) { + if (server.input_mode != LAB_INPUT_STATE_PASSTHROUGH) { return; } - assert(!menu->server->menu_current); + assert(!server.menu_current); struct wlr_box anchor_rect = {.x = x, .y = y}; if (menu->execute) { @@ -1196,21 +1281,20 @@ menu_open_root(struct menu *menu, int x, int y) open_menu(menu, anchor_rect); } - menu->server->menu_current = menu; + server.menu_current = menu; selected_item = NULL; - seat_focus_override_begin(&menu->server->seat, + seat_focus_override_begin(&server.seat, LAB_INPUT_STATE_MENU, LAB_CURSOR_DEFAULT); } static void create_pipe_menu(struct menu_pipe_context *ctx) { - struct server *server = ctx->pipemenu->server; - if (!parse_buf(server, ctx->pipemenu, &ctx->buf)) { + if (!parse_buf(ctx->pipemenu, &ctx->buf)) { return; } /* TODO: apply validate() only for generated pipemenus */ - validate(server); + validate(); /* Finally open the new submenu tree */ open_menu(ctx->pipemenu, ctx->anchor_rect); @@ -1292,8 +1376,6 @@ clean_up: static void open_pipemenu_async(struct menu *pipemenu, struct wlr_box anchor_rect) { - struct server *server = pipemenu->server; - assert(!pipemenu->pipe_ctx); assert(!pipemenu->scene_tree); @@ -1314,10 +1396,10 @@ open_pipemenu_async(struct menu *pipemenu, struct wlr_box anchor_rect) ctx->pipemenu = pipemenu; pipemenu->pipe_ctx = ctx; - ctx->event_read = wl_event_loop_add_fd(server->wl_event_loop, + ctx->event_read = wl_event_loop_add_fd(server.wl_event_loop, pipe_fd, WL_EVENT_READABLE, handle_pipemenu_readable, ctx); - ctx->event_timeout = wl_event_loop_add_timer(server->wl_event_loop, + ctx->event_timeout = wl_event_loop_add_timer(server.wl_event_loop, handle_pipemenu_timeout, ctx); wl_event_source_timer_update(ctx->event_timeout, PIPEMENU_TIMEOUT_IN_MS); @@ -1358,7 +1440,7 @@ menu_process_item_selection(struct menuitem *item) item->submenu->parent = item->parent; /* And open the new submenu tree */ struct wlr_box anchor_rect = - get_item_anchor_rect(item->submenu->server->theme, item); + get_item_anchor_rect(rc.theme, item); if (item->submenu->execute && !item->submenu->scene_tree) { open_pipemenu_async(item->submenu, anchor_rect); } else { @@ -1371,9 +1453,9 @@ menu_process_item_selection(struct menuitem *item) /* Get the deepest submenu with active item selection or the root menu itself */ static struct menu * -get_selection_leaf(struct server *server) +get_selection_leaf(void) { - struct menu *menu = server->menu_current; + struct menu *menu = server.menu_current; if (!menu) { return NULL; } @@ -1390,9 +1472,9 @@ get_selection_leaf(struct server *server) /* Selects the next or previous sibling of the currently selected item */ static void -menu_item_select(struct server *server, bool forward) +menu_item_select(bool forward) { - struct menu *menu = get_selection_leaf(server); + struct menu *menu = get_selection_leaf(); if (!menu) { return; } @@ -1427,10 +1509,9 @@ menu_execute_item(struct menuitem *item) return false; } - struct server *server = item->parent->server; - menu_close(server->menu_current); - server->menu_current = NULL; - seat_focus_override_end(&server->seat, /*restore_focus*/ true); + menu_close(server.menu_current); + server.menu_current = NULL; + seat_focus_override_end(&server.seat, /*restore_focus*/ true); /* * We call the actions after closing the menu so that virtual keyboard @@ -1446,33 +1527,86 @@ menu_execute_item(struct menuitem *item) if (item->client_list_view->shaded) { view_set_shade(item->client_list_view, false); } - actions_run(item->client_list_view, server, &item->actions, NULL); + actions_run(item->client_list_view, &item->actions, NULL); } else { - actions_run(item->parent->triggered_by_view, server, - &item->actions, NULL); + actions_run(item->parent->triggered_by_view, &item->actions, NULL); } - reset_pipemenus(server); + reset_pipemenus(); return true; } /* Keyboard based selection */ void -menu_item_select_next(struct server *server) +menu_item_select_next(void) { - menu_item_select(server, /* forward */ true); + menu_item_select(/* forward */ true); } void -menu_item_select_previous(struct server *server) +menu_item_select_previous(void) { - menu_item_select(server, /* forward */ false); + menu_item_select(/* forward */ false); } bool -menu_call_selected_actions(struct server *server) +menu_item_select_by_accelerator(uint32_t accelerator) { - struct menu *menu = get_selection_leaf(server); + struct menu *menu = get_selection_leaf(); + if (!menu || wl_list_empty(&menu->menuitems)) { + return false; + } + + bool needs_exec = false; + bool matched = false; + + struct menuitem *selection = menu->selection.item; + struct wl_list *start = selection ? &selection->link : &menu->menuitems; + struct wl_list *current = start; + struct menuitem *item = NULL; + struct menuitem *next_selection = NULL; + do { + current = current->next; + if (current == &menu->menuitems) { + /* Allow wrap around */ + continue; + } + item = wl_container_of(current, item, link); + if (item->accelerator == accelerator) { + if (!matched) { + /* Found first match */ + next_selection = item; + needs_exec = true; + matched = true; + } else { + /* + * Found another match, + * cycle selection instead of executing + */ + needs_exec = false; + break; + } + } + } while (current != start); + + if (!next_selection) { + return false; + } + + menu_process_item_selection(next_selection); + if (needs_exec && next_selection->submenu) { + /* Since we can't execute a submenu, enter it */ + needs_exec = false; + menu_submenu_enter(); + } + + return needs_exec; +} + +bool +menu_call_selected_actions(void) +{ + struct menu *menu = get_selection_leaf(); if (!menu || !menu->selection.item) { return false; } @@ -1482,9 +1616,9 @@ menu_call_selected_actions(struct server *server) /* Selects the first item on the submenu attached to the current selection */ void -menu_submenu_enter(struct server *server) +menu_submenu_enter(void) { - struct menu *menu = get_selection_leaf(server); + struct menu *menu = get_selection_leaf(); if (!menu || !menu->selection.menu) { return; } @@ -1505,9 +1639,9 @@ menu_submenu_enter(struct server *server) /* Re-selects the selected item on the parent menu of the current selection */ void -menu_submenu_leave(struct server *server) +menu_submenu_leave(void) { - struct menu *menu = get_selection_leaf(server); + struct menu *menu = get_selection_leaf(); if (!menu || !menu->parent || !menu->parent->selection.item) { return; } @@ -1525,21 +1659,21 @@ menu_process_cursor_motion(struct wlr_scene_node *node) } void -menu_close_root(struct server *server) +menu_close_root(void) { - assert(server->input_mode == LAB_INPUT_STATE_MENU); - assert(server->menu_current); + assert(server.input_mode == LAB_INPUT_STATE_MENU); + assert(server.menu_current); - menu_close(server->menu_current); - server->menu_current = NULL; - reset_pipemenus(server); - seat_focus_override_end(&server->seat, /*restore_focus*/ true); + menu_close(server.menu_current); + server.menu_current = NULL; + reset_pipemenus(); + seat_focus_override_end(&server.seat, /*restore_focus*/ true); } void -menu_reconfigure(struct server *server) +menu_reconfigure(void) { - menu_finish(server); - server->menu_current = NULL; - menu_init(server); + menu_finish(); + server.menu_current = NULL; + menu_init(); } diff --git a/src/meson.build b/src/meson.build index a9afdc4f..05163cfa 100644 --- a/src/meson.build +++ b/src/meson.build @@ -22,6 +22,7 @@ labwc_sources = files( 'seat.c', 'server.c', 'session-lock.c', + 'show-desktop.c', 'snap-constraints.c', 'snap.c', 'tearing.c', @@ -55,6 +56,5 @@ subdir('foreign-toplevel') subdir('img') subdir('input') subdir('menu') -subdir('protocols') subdir('scaled-buffer') subdir('ssd') diff --git a/src/output-state.c b/src/output-state.c index 7da1108b..37ebde8a 100644 --- a/src/output-state.c +++ b/src/output-state.c @@ -24,6 +24,7 @@ output_state_init(struct output *output) backup_config, output->wlr_output); wlr_output_head_v1_state_apply(&backup_head->state, &output->pending); + wlr_output_configuration_v1_destroy(backup_config); } diff --git a/src/output-virtual.c b/src/output-virtual.c index f3a40e10..07d07784 100644 --- a/src/output-virtual.c +++ b/src/output-virtual.c @@ -12,13 +12,13 @@ static struct wlr_output *fallback_output = NULL; void -output_virtual_add(struct server *server, const char *output_name, +output_virtual_add(const char *output_name, struct wlr_output **store_wlr_output) { if (output_name) { /* Prevent creating outputs with the same name */ struct output *output; - wl_list_for_each(output, &server->outputs, link) { + wl_list_for_each(output, &server.outputs, link) { if (wlr_output_is_headless(output->wlr_output) && !strcmp(output->wlr_output->name, output_name)) { wlr_log(WLR_DEBUG, @@ -46,13 +46,13 @@ output_virtual_add(struct server *server, const char *output_name, * we may end up calling the new output handler twice, one time manually * and one time by the headless backend when it starts up and sends the * signal for all its configured outputs. Rather than keeping a global - * server->headless.started state around that we could check here we just + * server.headless.started state around that we could check here we just * ignore duplicated new output calls in handle_new_output(). */ - wl_list_remove(&server->new_output.link); + wl_list_remove(&server.new_output.link); struct wlr_output *wlr_output = wlr_headless_add_output( - server->headless.backend, 1920, 1080); + server.headless.backend, 1920, 1080); if (!wlr_output) { wlr_log(WLR_ERROR, "Failed to create virtual output %s", @@ -69,20 +69,20 @@ output_virtual_add(struct server *server, const char *output_name, } /* Notify about the new output manually */ - if (server->new_output.notify) { - server->new_output.notify(&server->new_output, wlr_output); + if (server.new_output.notify) { + server.new_output.notify(&server.new_output, wlr_output); } restore_handler: /* And finally restore output notifications */ - wl_signal_add(&server->backend->events.new_output, &server->new_output); + wl_signal_add(&server.backend->events.new_output, &server.new_output); } void -output_virtual_remove(struct server *server, const char *output_name) +output_virtual_remove(const char *output_name) { struct output *output; - wl_list_for_each(output, &server->outputs, link) { + wl_list_for_each(output, &server.outputs, link) { if (!wlr_output_is_headless(output->wlr_output) || output->wlr_output == fallback_output) { continue; @@ -109,16 +109,16 @@ output_virtual_remove(struct server *server, const char *output_name) } void -output_virtual_update_fallback(struct server *server) +output_virtual_update_fallback(void) { - struct wl_list *layout_outputs = &server->output_layout->outputs; + struct wl_list *layout_outputs = &server.output_layout->outputs; const char *fallback_output_name = getenv("LABWC_FALLBACK_OUTPUT"); if (!fallback_output && wl_list_empty(layout_outputs) && !string_null_or_empty(fallback_output_name)) { wlr_log(WLR_DEBUG, "adding fallback output %s", fallback_output_name); - output_virtual_add(server, fallback_output_name, &fallback_output); + output_virtual_add(fallback_output_name, &fallback_output); } else if (fallback_output && (wl_list_length(layout_outputs) > 1 || string_null_or_empty(fallback_output_name))) { wlr_log(WLR_DEBUG, "destroying fallback output %s", diff --git a/src/output.c b/src/output.c index f62dd87e..2eab8ec3 100644 --- a/src/output.c +++ b/src/output.c @@ -9,11 +9,13 @@ #define _POSIX_C_SOURCE 200809L #include "output.h" #include +#include +#include #include -#include #include +#include #include -#include +#include #include #include #include @@ -25,30 +27,186 @@ #include "common/macros.h" #include "common/mem.h" #include "common/scene-helpers.h" +#include "common/string-helpers.h" #include "config/rcxml.h" #include "labwc.h" #include "layers.h" #include "node.h" #include "output-state.h" #include "output-virtual.h" -#include "protocols/cosmic-workspaces.h" -#include "protocols/ext-workspace.h" #include "regions.h" #include "session-lock.h" #include "view.h" #include "xwayland.h" +#if WLR_HAS_X11_BACKEND + #include +#endif + +#if WLR_HAS_DRM_BACKEND + #include + #include +#else + #define wlr_output_is_drm(output) (false) +#endif + +#if WLR_HAS_SESSION + #include +#endif + +static uint32_t output_formats_8bit[] = { + /* 32 bpp RGB with 8 bit component width */ + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_RGBX8888, + DRM_FORMAT_BGRX8888, + + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_BGRA8888, + + /* 24 bpp RGB */ + DRM_FORMAT_RGB888, + DRM_FORMAT_BGR888, +}; + +uint32_t output_formats_10bit[] = { + /* 32 bpp RGB with 10 bit component width */ + DRM_FORMAT_XRGB2101010, + DRM_FORMAT_XBGR2101010, + DRM_FORMAT_RGBX1010102, + DRM_FORMAT_BGRX1010102, + + DRM_FORMAT_ARGB2101010, + DRM_FORMAT_ABGR2101010, + DRM_FORMAT_RGBA1010102, + DRM_FORMAT_BGRA1010102, +}; + +static bool +output_set_render_format(struct output *output, uint32_t candidates[], size_t count) +{ + for (size_t i = 0; i < count; i++) { + wlr_output_state_set_render_format(&output->pending, candidates[i]); + if (wlr_output_test_state(output->wlr_output, &output->pending)) { + return true; + } + } + return false; +} + +static bool +output_format_in_candidates(uint32_t render_format, uint32_t candidates[], size_t count) +{ + for (size_t i = 0; i < count; i++) { + if (candidates[i] == render_format) { + return true; + } + } + return false; +} + +static enum render_bit_depth +bit_depth_from_format(uint32_t render_format) +{ + if (output_format_in_candidates(render_format, output_formats_10bit, + ARRAY_SIZE(output_formats_10bit))) { + return LAB_RENDER_BIT_DEPTH_10; + } else if (output_format_in_candidates(render_format, output_formats_8bit, + ARRAY_SIZE(output_formats_8bit))) { + return LAB_RENDER_BIT_DEPTH_8; + } + return LAB_RENDER_BIT_DEPTH_DEFAULT; +} + +static enum render_bit_depth +get_config_render_bit_depth(void) +{ + return rc.target_render_depth; +} + +static bool +output_supports_hdr(const struct wlr_output *output, const char **unsupported_reason_ptr) +{ + const char *unsupported_reason = NULL; + if (!(output->supported_primaries & WLR_COLOR_NAMED_PRIMARIES_BT2020)) { + unsupported_reason = "BT2020 primaries not supported by output"; + } else if (!(output->supported_transfer_functions & + WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ)) { + unsupported_reason = "PQ transfer function not supported by output"; + } else if (!server.renderer->features.output_color_transform) { + unsupported_reason = "renderer doesn't support output color transforms"; + } + if (unsupported_reason_ptr) { + *unsupported_reason_ptr = unsupported_reason; + } + return !unsupported_reason; +} + +void +output_state_setup_hdr(struct output *output, bool silent) +{ + uint32_t render_format = output->wlr_output->render_format; + const char *unsupported_reason = NULL; + bool hdr_supported = output_supports_hdr(output->wlr_output, + &unsupported_reason); + bool hdr_succeeded = false; + + enum render_bit_depth render_bit_depth = get_config_render_bit_depth(); + if (render_bit_depth == LAB_RENDER_BIT_DEPTH_DEFAULT) { + render_bit_depth = bit_depth_from_format(render_format); + } + + if (!hdr_supported && render_bit_depth == LAB_RENDER_BIT_DEPTH_10) { + if (!silent) { + wlr_log(WLR_INFO, "Cannot enable HDR on output %s: %s", + output->wlr_output->name, unsupported_reason); + } + render_bit_depth = LAB_RENDER_BIT_DEPTH_8; + } + + if (render_bit_depth == LAB_RENDER_BIT_DEPTH_10 && + bit_depth_from_format(render_format) == render_bit_depth) { + /* 10-bit was set successfully before, try to save some + * tests by reusing the format + */ + hdr_succeeded = true; + } else if (render_bit_depth == LAB_RENDER_BIT_DEPTH_10) { + hdr_succeeded = output_set_render_format(output, output_formats_10bit, + ARRAY_SIZE(output_formats_10bit)); + if (!hdr_succeeded) { + if (!silent) { + wlr_log(WLR_INFO, "No 10 bit color formats" + " supported, HDR disabled."); + } + if (!output_set_render_format(output, output_formats_8bit, + ARRAY_SIZE(output_formats_8bit))) { + if (!silent) { + wlr_log(WLR_ERROR, "No 8 bit color formats" + " supported either!"); + } + } + } + } else { + if (!output_set_render_format(output, output_formats_8bit, + ARRAY_SIZE(output_formats_8bit)) && !silent) { + wlr_log(WLR_ERROR, "No 8 bit color formats supported!"); + } + } + + output_enable_hdr(output, &output->pending, hdr_succeeded, silent); +} + bool output_get_tearing_allowance(struct output *output) { - struct server *server = output->server; - /* never allow tearing when disabled */ if (!rc.allow_tearing) { return false; } - struct view *view = server->active_view; + struct view *view = server.active_view; /* tearing is only allowed for the output with the active view */ if (!view || view->output != output) { @@ -79,36 +237,6 @@ output_get_tearing_allowance(struct output *output) return view->force_tearing == LAB_STATE_ENABLED; } -static void -output_apply_gamma(struct output *output) -{ - assert(output); - assert(output->gamma_lut_changed); - - struct server *server = output->server; - struct wlr_scene_output *scene_output = output->scene_output; - - struct wlr_output_state pending; - wlr_output_state_init(&pending); - - output->gamma_lut_changed = false; - struct wlr_gamma_control_v1 *gamma_control = - wlr_gamma_control_manager_v1_get_control( - server->gamma_control_manager_v1, - output->wlr_output); - - if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) { - wlr_output_state_finish(&pending); - return; - } - - if (!lab_wlr_scene_output_commit(scene_output, &pending)) { - wlr_gamma_control_v1_send_failed_and_destroy(gamma_control); - } - - wlr_output_state_finish(&pending); -} - static void handle_output_frame(struct wl_listener *listener, void *data) { @@ -121,30 +249,21 @@ handle_output_frame(struct wl_listener *listener, void *data) return; } +#if WLR_HAS_SESSION /* * skip painting the session when it exists but is not active. */ - if (output->server->session && !output->server->session->active) { + if (server.session && !server.session->active) { return; } +#endif - if (output->gamma_lut_changed) { - /* - * We are not mixing the gamma state with - * other pending output changes to make it - * easier to handle a failed output commit - * due to gamma without impacting other - * unrelated output changes. - */ - output_apply_gamma(output); - } else { - struct wlr_scene_output *scene_output = output->scene_output; - struct wlr_output_state *pending = &output->pending; + struct wlr_scene_output *scene_output = output->scene_output; + struct wlr_output_state *pending = &output->pending; - pending->tearing_page_flip = output_get_tearing_allowance(output); + pending->tearing_page_flip = output_get_tearing_allowance(output); - lab_wlr_scene_output_commit(scene_output, pending); - } + lab_wlr_scene_output_commit(scene_output, pending); struct timespec now = { 0 }; clock_gettime(CLOCK_MONOTONIC, &now); @@ -155,8 +274,7 @@ static void handle_output_destroy(struct wl_listener *listener, void *data) { struct output *output = wl_container_of(listener, output, destroy); - struct server *server = output->server; - struct seat *seat = &server->seat; + struct seat *seat = &server.seat; regions_evacuate_output(output); regions_destroy(seat, &output->regions); if (seat->overlay.active.output == output) { @@ -180,7 +298,7 @@ handle_output_destroy(struct wl_listener *listener, void *data) } struct view *view; - wl_list_for_each(view, &server->views, link) { + wl_list_for_each(view, &server.views, link) { if (view->output == output) { view_on_output_destroy(view); } @@ -195,6 +313,20 @@ handle_output_destroy(struct wl_listener *listener, void *data) */ output->wlr_output->data = NULL; + /* + * On nested backends (X11/Wayland), outputs correspond to + * windows and cannot be reconnected. Exit the compositor + * when the last one is destroyed. + */ + if (wl_list_empty(&server.outputs) && ( + wlr_output_is_wl(output->wlr_output) +#if WLR_HAS_X11_BACKEND + || wlr_output_is_x11(output->wlr_output) +#endif + )) { + wl_display_terminate(server.wl_display); + } + /* * output->scene_output (if still around at this point) is * destroyed automatically when the wlr_output is destroyed @@ -241,14 +373,14 @@ handle_output_request_state(struct wl_listener *listener, void *data) } } -static void do_output_layout_change(struct server *server); +static void do_output_layout_change(void); static void -add_output_to_layout(struct server *server, struct output *output) +add_output_to_layout(struct output *output) { struct wlr_output *wlr_output = output->wlr_output; struct wlr_output_layout_output *layout_output = - wlr_output_layout_add_auto(server->output_layout, wlr_output); + wlr_output_layout_add_auto(server.output_layout, wlr_output); if (!layout_output) { wlr_log(WLR_ERROR, "unable to add output to layout"); return; @@ -256,7 +388,7 @@ add_output_to_layout(struct server *server, struct output *output) if (!output->scene_output) { output->scene_output = - wlr_scene_output_create(server->scene, wlr_output); + wlr_scene_output_create(server.scene, wlr_output); if (!output->scene_output) { wlr_log(WLR_ERROR, "unable to create scene output"); return; @@ -266,21 +398,19 @@ add_output_to_layout(struct server *server, struct output *output) * safe to call twice, so we call it only when initially * creating the scene_output. */ - wlr_scene_output_layout_add_output(server->scene_layout, + wlr_scene_output_layout_add_output(server.scene_layout, layout_output, output->scene_output); } - lab_cosmic_workspace_group_output_enter( - server->workspaces.cosmic_group, output->wlr_output); - lab_ext_workspace_group_output_enter( - server->workspaces.ext_group, output->wlr_output); + wlr_ext_workspace_group_handle_v1_output_enter( + server.workspaces.ext_group, output->wlr_output); /* (Re-)create regions from config */ regions_reconfigure_output(output); /* Create lock surface if needed */ - if (server->session_lock_manager->locked) { - session_lock_output_create(server->session_lock_manager, output); + if (server.session_lock_manager->locked) { + session_lock_output_create(server.session_lock_manager, output); } } @@ -362,12 +492,14 @@ output_test_auto(struct wlr_output *wlr_output, struct wlr_output_state *state, } /* Reset mode if none worked (we may still try to commit) */ - wlr_output_state_set_mode(state, NULL); - return false; + wlr_log(WLR_DEBUG, "no working fixed mode found for output %s", wlr_output->name); + state->committed &= ~WLR_OUTPUT_STATE_MODE; + + return wlr_output_test_state(wlr_output, state); } static void -configure_new_output(struct server *server, struct output *output) +configure_new_output(struct output *output) { struct wlr_output *wlr_output = output->wlr_output; @@ -378,37 +510,45 @@ configure_new_output(struct server *server, struct output *output) /* is_client_request */ false)) { wlr_log(WLR_INFO, "mode test failed for output %s", wlr_output->name); - /* - * Continue anyway. For some reason, the test fails when - * running nested, yet the following commit succeeds. - */ + wlr_output_state_set_enabled(&output->pending, false); + return; } if (rc.adaptive_sync == LAB_ADAPTIVE_SYNC_ENABLED) { output_enable_adaptive_sync(output, true); } - output_state_commit(output); - - wlr_output_effective_resolution(wlr_output, - &output->usable_area.width, &output->usable_area.height); + output_state_setup_hdr(output, false); /* * Wait until wlr_output_layout_add_auto() returns before * calling do_output_layout_change(); this ensures that the * wlr_output_cursor is created for the new output. */ - server->pending_output_layout_change++; - add_output_to_layout(server, output); - server->pending_output_layout_change--; + server.pending_output_layout_change++; + add_output_to_layout(output); + server.pending_output_layout_change--; + + /* + * Commit the output this way instead, HDR needs a buffer, and + * this commit must be called after the output is added to the + * layout above. + */ + lab_wlr_scene_output_commit(output->scene_output, &output->pending); + + /* + * Collect the effective resolution after the final commit. + */ + wlr_output_effective_resolution(wlr_output, + &output->usable_area.width, &output->usable_area.height); } static uint64_t -get_unused_output_id_bit(struct server *server) +get_unused_output_id_bit(void) { uint64_t used_id_bits = 0; struct output *output; - wl_list_for_each(output, &server->outputs, link) { + wl_list_for_each(output, &server.outputs, link) { used_id_bits |= output->id_bit; } @@ -416,7 +556,7 @@ get_unused_output_id_bit(struct server *server) return 0; } - uint64_t id_bit = server->next_output_id_bit; + uint64_t id_bit = server.next_output_id_bit; /* * __builtin_popcountll() should be supported by GCC & clang. * If it causes portability issues, just remove the assert. @@ -433,7 +573,7 @@ get_unused_output_id_bit(struct server *server) * can cycle through all 64 available bits, making re-use less * frequent (on a best-effort basis). */ - server->next_output_id_bit = (id_bit << 1) | (id_bit >> 63); + server.next_output_id_bit = (id_bit << 1) | (id_bit >> 63); return id_bit; } @@ -445,11 +585,10 @@ handle_new_output(struct wl_listener *listener, void *data) * This event is raised by the backend when a new output (aka display * or monitor) becomes available. */ - struct server *server = wl_container_of(listener, server, new_output); struct wlr_output *wlr_output = data; struct output *output; - wl_list_for_each(output, &server->outputs, link) { + wl_list_for_each(output, &server.outputs, link) { if (output->wlr_output == wlr_output) { /* * This is a duplicated notification. @@ -461,16 +600,17 @@ handle_new_output(struct wl_listener *listener, void *data) } } - uint64_t id_bit = get_unused_output_id_bit(server); + uint64_t id_bit = get_unused_output_id_bit(); if (!id_bit) { wlr_log(WLR_ERROR, "Cannot add more than 64 outputs"); return; } if (wlr_output_is_wl(wlr_output)) { - char title[64]; - snprintf(title, sizeof(title), "%s - %s", "labwc", wlr_output->name); - wlr_wl_output_set_title(wlr_output, title); + GString *title = g_string_new(server.title_fmt); + g_string_replace(title, "%o", wlr_output->name, 0); + wlr_wl_output_set_title(wlr_output, title->str); + g_string_free(title, TRUE); wlr_wl_output_set_app_id(wlr_output, "labwc"); } @@ -481,17 +621,11 @@ handle_new_output(struct wl_listener *listener, void *data) * This is also useful for debugging the DRM parts of * another compositor. * - * All drm leasing is disabled due to a UAF bug in wlroots. - * We assume that the fix will be backported to 0.19.1 and thus - * check for a version >= 0.19.1. See following link for the fix status: - * https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5104 - * - * TODO: remove once labwc starts tracking 0.20.x and the fix has been merged. */ -#if LAB_WLR_VERSION_AT_LEAST(0, 19, 1) - if (server->drm_lease_manager && wlr_output_is_drm(wlr_output)) { +#if WLR_HAS_DRM_BACKEND + if (server.drm_lease_manager && wlr_output_is_drm(wlr_output)) { wlr_drm_lease_v1_manager_offer_output( - server->drm_lease_manager, wlr_output); + server.drm_lease_manager, wlr_output); } #endif @@ -507,8 +641,8 @@ handle_new_output(struct wl_listener *listener, void *data) * Configures the output created by the backend to use our allocator * and our renderer. Must be done once, before committing the output */ - if (!wlr_output_init_render(wlr_output, server->allocator, - server->renderer)) { + if (!wlr_output_init_render(wlr_output, server.allocator, + server.renderer)) { wlr_log(WLR_ERROR, "unable to init output renderer"); return; } @@ -516,11 +650,10 @@ handle_new_output(struct wl_listener *listener, void *data) output = znew(*output); output->wlr_output = wlr_output; wlr_output->data = output; - output->server = server; output->id_bit = id_bit; output_state_init(output); - wl_list_insert(&server->outputs, &output->link); + wl_list_insert(&server.outputs, &output->link); output->destroy.notify = handle_output_destroy; wl_signal_add(&wlr_output->events.destroy, &output->destroy); @@ -538,11 +671,11 @@ handle_new_output(struct wl_listener *listener, void *data) */ for (size_t i = 0; i < ARRAY_SIZE(output->layer_tree); i++) { output->layer_tree[i] = - wlr_scene_tree_create(&server->scene->tree); + lab_wlr_scene_tree_create(&server.scene->tree); } - output->layer_popup_tree = wlr_scene_tree_create(&server->scene->tree); - output->cycle_osd_tree = wlr_scene_tree_create(&server->scene->tree); - output->session_lock_tree = wlr_scene_tree_create(&server->scene->tree); + output->layer_popup_tree = lab_wlr_scene_tree_create(&server.scene->tree); + output->cycle_osd_tree = lab_wlr_scene_tree_create(&server.scene->tree); + output->session_lock_tree = lab_wlr_scene_tree_create(&server.scene->tree); /* * Set the z-positions to achieve the following order (from top to @@ -560,7 +693,7 @@ handle_new_output(struct wl_listener *listener, void *data) wlr_scene_node_lower_to_bottom(&output->layer_tree[1]->node); wlr_scene_node_lower_to_bottom(&output->layer_tree[0]->node); - struct wlr_scene_node *menu_node = &server->menu_tree->node; + struct wlr_scene_node *menu_node = &server.menu_tree->node; wlr_scene_node_place_below(&output->layer_tree[2]->node, menu_node); wlr_scene_node_place_below(&output->layer_tree[3]->node, menu_node); wlr_scene_node_place_below(&output->layer_popup_tree->node, menu_node); @@ -575,84 +708,85 @@ handle_new_output(struct wl_listener *listener, void *data) * might need tweaking if wlroots adds other output backends. */ if (rc.auto_enable_outputs || !wlr_output_is_drm(wlr_output)) { - configure_new_output(server, output); + configure_new_output(output); } - do_output_layout_change(server); + do_output_layout_change(); } -static void output_manager_init(struct server *server); +static void output_manager_init(void); void -output_init(struct server *server) +output_init(void) { - server->gamma_control_manager_v1 = - wlr_gamma_control_manager_v1_create(server->wl_display); + server.gamma_control_manager_v1 = + wlr_gamma_control_manager_v1_create(server.wl_display); + wlr_scene_set_gamma_control_manager_v1(server.scene, + server.gamma_control_manager_v1); - server->new_output.notify = handle_new_output; - wl_signal_add(&server->backend->events.new_output, &server->new_output); + server.new_output.notify = handle_new_output; + wl_signal_add(&server.backend->events.new_output, &server.new_output); /* * Create an output layout, which is a wlroots utility for working with * an arrangement of screens in a physical layout. */ - server->output_layout = wlr_output_layout_create(server->wl_display); - if (!server->output_layout) { + server.output_layout = wlr_output_layout_create(server.wl_display); + if (!server.output_layout) { wlr_log(WLR_ERROR, "unable to create output layout"); exit(EXIT_FAILURE); } - server->scene_layout = wlr_scene_attach_output_layout(server->scene, - server->output_layout); - if (!server->scene_layout) { + server.scene_layout = wlr_scene_attach_output_layout(server.scene, + server.output_layout); + if (!server.scene_layout) { wlr_log(WLR_ERROR, "unable to create scene layout"); exit(EXIT_FAILURE); } /* Enable screen recording with wf-recorder */ - wlr_xdg_output_manager_v1_create(server->wl_display, - server->output_layout); + wlr_xdg_output_manager_v1_create(server.wl_display, + server.output_layout); - wl_list_init(&server->outputs); - server->next_output_id_bit = (1 << 0); + wl_list_init(&server.outputs); + server.next_output_id_bit = (1 << 0); - output_manager_init(server); + output_manager_init(); } -static void output_manager_finish(struct server *server); +static void output_manager_finish(void); void -output_finish(struct server *server) +output_finish(void) { - wl_list_remove(&server->new_output.link); - output_manager_finish(server); + wl_list_remove(&server.new_output.link); + output_manager_finish(); } static void -output_update_for_layout_change(struct server *server) +output_update_for_layout_change(void) { - output_update_all_usable_areas(server, /*layout_changed*/ true); - session_lock_update_for_layout_change(server); + output_update_all_usable_areas(/*layout_changed*/ true); + session_lock_update_for_layout_change(); /* * "Move" each wlr_output_cursor (in per-output coordinates) to * align with the seat cursor. Re-set the cursor image so that * the cursor isn't invisible on new outputs. */ - wlr_cursor_move(server->seat.cursor, NULL, 0, 0); - cursor_update_image(&server->seat); + wlr_cursor_move(server.seat.cursor, NULL, 0, 0); + cursor_update_image(&server.seat); } static bool -output_config_apply(struct server *server, - struct wlr_output_configuration_v1 *config) +output_config_apply(struct wlr_output_configuration_v1 *config) { bool success = true; - server->pending_output_layout_change++; + server.pending_output_layout_change++; struct wlr_output_configuration_head_v1 *head; wl_list_for_each(head, &config->heads, link) { struct wlr_output *o = head->state.output; - struct output *output = output_from_wlr_output(server, o); + struct output *output = output_from_wlr_output(o); struct wlr_output_state *os = &output->pending; bool output_enabled = head->state.enabled; @@ -677,6 +811,7 @@ output_config_apply(struct server *server, wlr_output_state_set_transform(os, head->state.transform); output_enable_adaptive_sync(output, head->state.adaptive_sync_enabled); + output_state_setup_hdr(output, false); } if (!output_state_commit(output)) { /* @@ -690,37 +825,48 @@ output_config_apply(struct server *server, break; } + if (output_enabled) { + /* + * The above commit was likely made without an image + * buffer attached. This will break applying HDR color + * transformation, since image descriptions must be + * committed with a buffer attached. Queue the HDR mode + * again if output is enabled, but make it silent, + * since it would have emitted messages already when + * called above. + */ + output_state_setup_hdr(output, true); + } + /* * Add or remove output from layout only if the commit went * through. Note that at startup, the output may have already * been enabled but not yet been added to the layout. */ bool was_in_layout = - !!wlr_output_layout_get(server->output_layout, o); + !!wlr_output_layout_get(server.output_layout, o); if (output_enabled) { if (!was_in_layout) { - add_output_to_layout(server, output); + add_output_to_layout(output); } struct wlr_box pos = {0}; - wlr_output_layout_get_box(server->output_layout, o, &pos); + wlr_output_layout_get_box(server.output_layout, o, &pos); if (pos.x != head->state.x || pos.y != head->state.y) { /* * This overrides the automatic layout * * wlr_output_layout_add() in fact means _move() */ - wlr_output_layout_add(server->output_layout, o, + wlr_output_layout_add(server.output_layout, o, head->state.x, head->state.y); } } else if (was_in_layout) { regions_evacuate_output(output); - lab_cosmic_workspace_group_output_leave( - server->workspaces.cosmic_group, output->wlr_output); - lab_ext_workspace_group_output_leave( - server->workspaces.ext_group, output->wlr_output); + wlr_ext_workspace_group_handle_v1_output_leave( + server.workspaces.ext_group, output->wlr_output); /* * At time of writing, wlr_output_layout_remove() @@ -731,13 +877,13 @@ output_config_apply(struct server *server, * calling wlr_output_layout_remove(). */ wlr_scene_output_destroy(output->scene_output); - wlr_output_layout_remove(server->output_layout, o); + wlr_output_layout_remove(server.output_layout, o); output->scene_output = NULL; } } - server->pending_output_layout_change--; - do_output_layout_change(server); + server.pending_output_layout_change--; + do_output_layout_change(); return success; } @@ -816,27 +962,25 @@ handle_output_manager_test(struct wl_listener *listener, void *data) static void handle_output_manager_apply(struct wl_listener *listener, void *data) { - struct server *server = - wl_container_of(listener, server, output_manager_apply); struct wlr_output_configuration_v1 *config = data; bool config_is_good = verify_output_config_v1(config); - if (config_is_good && output_config_apply(server, config)) { + if (config_is_good && output_config_apply(config)) { wlr_output_configuration_v1_send_succeeded(config); } else { wlr_output_configuration_v1_send_failed(config); } wlr_output_configuration_v1_destroy(config); struct output *output; - wl_list_for_each(output, &server->outputs, link) { - wlr_xcursor_manager_load(server->seat.xcursor_manager, + wl_list_for_each(output, &server.outputs, link) { + wlr_xcursor_manager_load(server.seat.xcursor_manager, output->wlr_output->scale); } /* Re-set cursor image in case scale changed */ - cursor_update_focus(server); - cursor_update_image(&server->seat); + cursor_update_focus(); + cursor_update_image(&server.seat); } /* @@ -845,7 +989,7 @@ handle_output_manager_apply(struct wl_listener *listener, void *data) * interface */ static struct -wlr_output_configuration_v1 *create_output_config(struct server *server) +wlr_output_configuration_v1 *create_output_config(void) { struct wlr_output_configuration_v1 *config = wlr_output_configuration_v1_create(); @@ -855,7 +999,7 @@ wlr_output_configuration_v1 *create_output_config(struct server *server) } struct output *output; - wl_list_for_each(output, &server->outputs, link) { + wl_list_for_each(output, &server.outputs, link) { struct wlr_output_configuration_head_v1 *head = wlr_output_configuration_head_v1_create(config, output->wlr_output); @@ -874,86 +1018,65 @@ wlr_output_configuration_v1 *create_output_config(struct server *server) } static void -do_output_layout_change(struct server *server) +do_output_layout_change(void) { - if (!server->pending_output_layout_change) { + if (!server.pending_output_layout_change) { struct wlr_output_configuration_v1 *config = - create_output_config(server); + create_output_config(); if (config) { wlr_output_manager_v1_set_configuration( - server->output_manager, config); + server.output_manager, config); } else { wlr_log(WLR_ERROR, "wlr_output_manager_v1_set_configuration()"); } - output_update_for_layout_change(server); - seat_output_layout_changed(&server->seat); + output_update_for_layout_change(); + seat_output_layout_changed(&server.seat); } } static void handle_output_layout_change(struct wl_listener *listener, void *data) { - struct server *server = - wl_container_of(listener, server, output_layout_change); - /* Prevents unnecessary layout recalculations */ - server->pending_output_layout_change++; - output_virtual_update_fallback(server); - server->pending_output_layout_change--; + server.pending_output_layout_change++; + output_virtual_update_fallback(); + server.pending_output_layout_change--; - do_output_layout_change(server); + do_output_layout_change(); } static void -handle_gamma_control_set_gamma(struct wl_listener *listener, void *data) +output_manager_init(void) { - const struct wlr_gamma_control_manager_v1_set_gamma_event *event = data; + server.output_manager = wlr_output_manager_v1_create(server.wl_display); - struct output *output = event->output->data; - if (!output_is_usable(output)) { - return; - } - output->gamma_lut_changed = true; - wlr_output_schedule_frame(output->wlr_output); + server.output_layout_change.notify = handle_output_layout_change; + wl_signal_add(&server.output_layout->events.change, + &server.output_layout_change); + + server.output_manager_apply.notify = handle_output_manager_apply; + wl_signal_add(&server.output_manager->events.apply, + &server.output_manager_apply); + + server.output_manager_test.notify = handle_output_manager_test; + wl_signal_add(&server.output_manager->events.test, + &server.output_manager_test); } static void -output_manager_init(struct server *server) +output_manager_finish(void) { - server->output_manager = wlr_output_manager_v1_create(server->wl_display); - - server->output_layout_change.notify = handle_output_layout_change; - wl_signal_add(&server->output_layout->events.change, - &server->output_layout_change); - - server->output_manager_apply.notify = handle_output_manager_apply; - wl_signal_add(&server->output_manager->events.apply, - &server->output_manager_apply); - - server->output_manager_test.notify = handle_output_manager_test; - wl_signal_add(&server->output_manager->events.test, - &server->output_manager_test); - - server->gamma_control_set_gamma.notify = handle_gamma_control_set_gamma; - wl_signal_add(&server->gamma_control_manager_v1->events.set_gamma, - &server->gamma_control_set_gamma); -} - -static void -output_manager_finish(struct server *server) -{ - wl_list_remove(&server->output_layout_change.link); - wl_list_remove(&server->output_manager_apply.link); - wl_list_remove(&server->output_manager_test.link); - wl_list_remove(&server->gamma_control_set_gamma.link); + wl_list_remove(&server.output_layout_change.link); + wl_list_remove(&server.output_manager_apply.link); + wl_list_remove(&server.output_manager_test.link); } struct output * -output_from_wlr_output(struct server *server, struct wlr_output *wlr_output) +output_from_wlr_output(struct wlr_output *wlr_output) { struct output *output; - wl_list_for_each(output, &server->outputs, link) { + wl_list_for_each(output, &server.outputs, link) { if (output->wlr_output == wlr_output) { return output; } @@ -962,10 +1085,10 @@ output_from_wlr_output(struct server *server, struct wlr_output *wlr_output) } struct output * -output_from_name(struct server *server, const char *name) +output_from_name(const char *name) { struct output *output; - wl_list_for_each(output, &server->outputs, link) { + wl_list_for_each(output, &server.outputs, link) { if (!output_is_usable(output) || !output->wlr_output->name) { continue; } @@ -977,22 +1100,21 @@ output_from_name(struct server *server, const char *name) } struct output * -output_nearest_to(struct server *server, int lx, int ly) +output_nearest_to(int lx, int ly) { double closest_x, closest_y; - wlr_output_layout_closest_point(server->output_layout, NULL, lx, ly, + wlr_output_layout_closest_point(server.output_layout, NULL, lx, ly, &closest_x, &closest_y); - return output_from_wlr_output(server, - wlr_output_layout_output_at(server->output_layout, + return output_from_wlr_output(wlr_output_layout_output_at(server.output_layout, closest_x, closest_y)); } struct output * -output_nearest_to_cursor(struct server *server) +output_nearest_to_cursor(void) { - return output_nearest_to(server, server->seat.cursor->x, - server->seat.cursor->y); + return output_nearest_to(server.seat.cursor->x, + server.seat.cursor->y); } struct output * @@ -1016,7 +1138,7 @@ output_get_adjacent(struct output *output, enum lab_edge edge, bool wrap) /* Determine any adjacent output in the appropriate direction */ struct wlr_output *new_output = NULL; struct wlr_output *current_output = output->wlr_output; - struct wlr_output_layout *layout = output->server->output_layout; + struct wlr_output_layout *layout = server.output_layout; /* Cast from enum lab_edge to enum wlr_direction is safe */ new_output = wlr_output_layout_adjacent_output(layout, (enum wlr_direction)edge, current_output, lx, ly); @@ -1039,7 +1161,7 @@ output_get_adjacent(struct output *output, enum lab_edge edge, bool wrap) return NULL; } - output = output_from_wlr_output(output->server, new_output); + output = output_from_wlr_output(new_output); if (!output_is_usable(output)) { wlr_log(WLR_ERROR, "invalid output in layout"); return NULL; @@ -1070,10 +1192,10 @@ update_usable_area(struct output *output) #if HAVE_XWAYLAND struct view *view; - wl_list_for_each(view, &output->server->views, link) { + wl_list_for_each(view, &server.views, link) { if (view->mapped && view->type == LAB_XWAYLAND_VIEW) { xwayland_adjust_usable_area(view, - output->server->output_layout, + server.output_layout, output->wlr_output, &output->usable_area); } } @@ -1087,19 +1209,19 @@ output_update_usable_area(struct output *output) if (update_usable_area(output)) { regions_update_geometry(output); #if HAVE_XWAYLAND - xwayland_update_workarea(output->server); + xwayland_update_workarea(); #endif - desktop_arrange_all_views(output->server); + desktop_arrange_all_views(); } } void -output_update_all_usable_areas(struct server *server, bool layout_changed) +output_update_all_usable_areas(bool layout_changed) { bool usable_area_changed = false; struct output *output; - wl_list_for_each(output, &server->outputs, link) { + wl_list_for_each(output, &server.outputs, link) { if (update_usable_area(output)) { usable_area_changed = true; regions_update_geometry(output); @@ -1109,9 +1231,9 @@ output_update_all_usable_areas(struct server *server, bool layout_changed) } if (usable_area_changed || layout_changed) { #if HAVE_XWAYLAND - xwayland_update_workarea(server); + xwayland_update_workarea(); #endif - desktop_arrange_all_views(server); + desktop_arrange_all_views(); } } @@ -1123,7 +1245,7 @@ output_usable_area_in_layout_coords(struct output *output) } struct wlr_box box = output->usable_area; double ox = 0, oy = 0; - wlr_output_layout_output_coords(output->server->output_layout, + wlr_output_layout_output_coords(server.output_layout, output->wlr_output, &ox, &oy); box.x -= ox; box.y -= oy; @@ -1133,8 +1255,6 @@ output_usable_area_in_layout_coords(struct output *output) void handle_output_power_manager_set_mode(struct wl_listener *listener, void *data) { - struct server *server = wl_container_of(listener, server, - output_power_manager_set_mode); struct wlr_output_power_v1_set_mode_event *event = data; struct output *output = event->output->data; assert(output); @@ -1157,7 +1277,7 @@ handle_output_power_manager_set_mode(struct wl_listener *listener, void *data) * Re-set the cursor image so that the cursor * isn't invisible on the newly enabled output. */ - cursor_update_image(&server->seat); + cursor_update_image(&server.seat); break; } } @@ -1188,3 +1308,34 @@ output_set_has_fullscreen_view(struct output *output, bool has_fullscreen_view) output_enable_adaptive_sync(output, has_fullscreen_view); output_state_commit(output); } + +void +output_enable_hdr(struct output *output, struct wlr_output_state *os, + bool enabled, bool silent) +{ + if (enabled && !output_supports_hdr(output->wlr_output, NULL)) { + enabled = false; + } + + if (!enabled) { + if (output->wlr_output->supported_primaries != 0 || + output->wlr_output->supported_transfer_functions != 0) { + if (!silent) { + wlr_log(WLR_DEBUG, "Disabling HDR on output %s", + output->wlr_output->name); + } + wlr_output_state_set_image_description(os, NULL); + } + return; + } + + if (!silent) { + wlr_log(WLR_DEBUG, "Enabling HDR on output %s", + output->wlr_output->name); + } + const struct wlr_output_image_description image_desc = { + .primaries = WLR_COLOR_NAMED_PRIMARIES_BT2020, + .transfer_function = WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ, + }; + wlr_output_state_set_image_description(os, &image_desc); +} diff --git a/src/overlay.c b/src/overlay.c index 508a531a..817bb3fc 100644 --- a/src/overlay.c +++ b/src/overlay.c @@ -16,8 +16,7 @@ static void show_overlay(struct seat *seat, struct theme_snapping_overlay *overlay_theme, struct wlr_box *box) { - struct server *server = seat->server; - struct view *view = server->grabbed_view; + struct view *view = server.grabbed_view; assert(view); assert(!seat->overlay.rect); @@ -94,7 +93,7 @@ edge_has_adjacent_output_from_cursor(struct seat *seat, struct output *output, } /* Cast from enum lab_edge to enum wlr_direction is safe */ return wlr_output_layout_adjacent_output( - seat->server->output_layout, (enum wlr_direction)edge, + server.output_layout, (enum wlr_direction)edge, output->wlr_output, seat->cursor->x, seat->cursor->y); } @@ -124,7 +123,7 @@ show_edge_overlay(struct seat *seat, enum lab_edge edge1, enum lab_edge edge2, if (delay > 0) { if (!seat->overlay.timer) { seat->overlay.timer = wl_event_loop_add_timer( - seat->server->wl_event_loop, + server.wl_event_loop, handle_edge_overlay_timeout, seat); } /* Show overlay ms later */ @@ -138,11 +137,9 @@ show_edge_overlay(struct seat *seat, enum lab_edge edge1, enum lab_edge edge2, void overlay_update(struct seat *seat) { - struct server *server = seat->server; - /* Region-snapping overlay */ - if (regions_should_snap(server)) { - struct region *region = regions_from_cursor(server); + if (regions_should_snap()) { + struct region *region = regions_from_cursor(); if (region) { show_region_overlay(seat, region); return; diff --git a/src/placement.c b/src/placement.c index 2b5cdf61..002303f9 100644 --- a/src/placement.c +++ b/src/placement.c @@ -46,8 +46,6 @@ count_views(struct view *view) { assert(view); - struct server *server = view->server; - struct output *output = view->output; if (!output_is_usable(output)) { return 0; @@ -56,7 +54,7 @@ count_views(struct view *view) int nviews = 0; struct view *v; - for_each_view(v, &server->views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) { + for_each_view(v, &server.views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) { /* Ignore the target view or anything on a different output */ if (v == view || v->output != output) { continue; @@ -105,8 +103,6 @@ build_grid(struct overlap_bitmap *bmp, struct view *view) assert(bmp); assert(view); - struct server *server = view->server; - /* Always start with a fresh bitmap */ destroy_bitmap(bmp); @@ -145,7 +141,7 @@ build_grid(struct overlap_bitmap *bmp, struct view *view) int nr_cols = 2; struct view *v; - for_each_view(v, &server->views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) { + for_each_view(v, &server.views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) { if (v == view || v->output != output) { continue; } @@ -236,8 +232,6 @@ build_overlap(struct overlap_bitmap *bmp, struct view *view) assert(bmp); assert(view); - struct server *server = view->server; - if (bmp->nr_rows < 1 || bmp->nr_cols < 1) { return; } @@ -248,7 +242,7 @@ build_overlap(struct overlap_bitmap *bmp, struct view *view) } struct view *v; - for_each_view(v, &server->views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) { + for_each_view(v, &server.views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) { if (v == view || v->output != output) { continue; } diff --git a/src/protocols/cosmic_workspaces/cosmic-workspaces.c b/src/protocols/cosmic_workspaces/cosmic-workspaces.c deleted file mode 100644 index d12611cb..00000000 --- a/src/protocols/cosmic_workspaces/cosmic-workspaces.c +++ /dev/null @@ -1,717 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include -#include -#include "common/array.h" -#include "common/mem.h" -#include "common/list.h" -#include "cosmic-workspace-unstable-v1-protocol.h" -#include "protocols/cosmic-workspaces.h" -#include "protocols/cosmic-workspaces-internal.h" -#include "protocols/transaction-addon.h" - -/* - * .--------------------. - * | TODO | - * |--------------------| - * | - prevent empty | - * | done events | - * | - go through xml | - * | and verify impl | - * | - assert pub API | - * `--------------------´ - * - */ - -/* Only used within an assert() */ -#ifndef NDEBUG - #define COSMIC_WORKSPACE_V1_VERSION 1 -#endif - -/* These are just *waaay* too long */ -#define ZCOSMIC_CAP_WS_CREATE \ - ZCOSMIC_WORKSPACE_GROUP_HANDLE_V1_ZCOSMIC_WORKSPACE_GROUP_CAPABILITIES_V1_CREATE_WORKSPACE -#define ZCOSMIC_CAP_WS_ACTIVATE \ - ZCOSMIC_WORKSPACE_HANDLE_V1_ZCOSMIC_WORKSPACE_CAPABILITIES_V1_ACTIVATE -#define ZCOSMIC_CAP_WS_DEACTIVATE \ - ZCOSMIC_WORKSPACE_HANDLE_V1_ZCOSMIC_WORKSPACE_CAPABILITIES_V1_DEACTIVATE -#define ZCOSMIC_CAP_WS_REMOVE \ - ZCOSMIC_WORKSPACE_HANDLE_V1_ZCOSMIC_WORKSPACE_CAPABILITIES_V1_REMOVE - -enum workspace_state { - CW_WS_STATE_ACTIVE = 1 << 0, - CW_WS_STATE_URGENT = 1 << 1, - CW_WS_STATE_HIDDEN = 1 << 2, - - /* - * Set when creating a new workspace so we - * don't end up having to send the state twice. - */ - CW_WS_STATE_INVALID = 1 << 31, -}; - -struct ws_create_workspace_event { - char *name; - struct { - struct wl_listener transaction_op_destroy; - } on; -}; - -static void -add_caps(struct wl_array *caps_arr, uint32_t caps) -{ - if (caps == CW_CAP_NONE) { - return; - } - if (caps & CW_CAP_GRP_WS_CREATE) { - array_add(caps_arr, ZCOSMIC_CAP_WS_CREATE); - } - if (caps & CW_CAP_WS_ACTIVATE) { - array_add(caps_arr, ZCOSMIC_CAP_WS_ACTIVATE); - } - if (caps & CW_CAP_WS_DEACTIVATE) { - array_add(caps_arr, ZCOSMIC_CAP_WS_DEACTIVATE); - } - if (caps & CW_CAP_WS_REMOVE) { - array_add(caps_arr, ZCOSMIC_CAP_WS_REMOVE); - } -} - -/* Workspace */ -static void -workspace_handle_destroy(struct wl_client *client, struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static void -workspace_handle_activate(struct wl_client *client, struct wl_resource *resource) -{ - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource); - if (!addon) { - /* workspace was destroyed from the compositor side */ - return; - } - struct lab_cosmic_workspace *workspace = addon->data; - lab_transaction_op_add(addon->ctx, CW_PENDING_WS_ACTIVATE, - workspace, /*data*/ NULL); -} - -static void -workspace_handle_deactivate(struct wl_client *client, struct wl_resource *resource) -{ - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource); - if (!addon) { - /* Workspace was destroyed from the compositor side */ - return; - } - struct lab_cosmic_workspace *workspace = addon->data; - lab_transaction_op_add(addon->ctx, CW_PENDING_WS_DEACTIVATE, - workspace, /*data*/ NULL); -} - -static void -workspace_handle_remove(struct wl_client *client, struct wl_resource *resource) -{ - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource); - if (!addon) { - /* workspace was destroyed from the compositor side */ - return; - } - struct lab_cosmic_workspace *workspace = addon->data; - lab_transaction_op_add(addon->ctx, CW_PENDING_WS_REMOVE, - workspace, /*data*/ NULL); -} - -static const struct zcosmic_workspace_handle_v1_interface workspace_impl = { - .destroy = workspace_handle_destroy, - .activate = workspace_handle_activate, - .deactivate = workspace_handle_deactivate, - .remove = workspace_handle_remove, -}; - -static void -workspace_instance_resource_destroy(struct wl_resource *resource) -{ - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource); - if (addon) { - lab_resource_addon_destroy(addon); - wl_resource_set_user_data(resource, NULL); - } - - wl_list_remove(wl_resource_get_link(resource)); -} - -static struct wl_resource * -workspace_resource_create(struct lab_cosmic_workspace *workspace, - struct wl_resource *group_resource, struct lab_transaction_session_context *ctx) -{ - struct wl_client *client = wl_resource_get_client(group_resource); - struct wl_resource *resource = wl_resource_create(client, - &zcosmic_workspace_handle_v1_interface, - wl_resource_get_version(group_resource), 0); - if (!resource) { - wl_client_post_no_memory(client); - return NULL; - } - - struct lab_wl_resource_addon *addon = lab_resource_addon_create(ctx); - addon->data = workspace; - - wl_resource_set_implementation(resource, &workspace_impl, addon, - workspace_instance_resource_destroy); - - wl_list_insert(&workspace->resources, wl_resource_get_link(resource)); - return resource; -} - -/* Workspace internal helpers */ -static void -workspace_send_state(struct lab_cosmic_workspace *workspace, struct wl_resource *target) -{ - struct wl_array state; - wl_array_init(&state); - - if (workspace->state & CW_WS_STATE_ACTIVE) { - array_add(&state, ZCOSMIC_WORKSPACE_HANDLE_V1_STATE_ACTIVE); - } - if (workspace->state & CW_WS_STATE_URGENT) { - array_add(&state, ZCOSMIC_WORKSPACE_HANDLE_V1_STATE_URGENT); - } - if (workspace->state & CW_WS_STATE_HIDDEN) { - array_add(&state, ZCOSMIC_WORKSPACE_HANDLE_V1_STATE_HIDDEN); - } - - if (target) { - zcosmic_workspace_handle_v1_send_state(target, &state); - } else { - struct wl_resource *resource; - wl_resource_for_each(resource, &workspace->resources) { - zcosmic_workspace_handle_v1_send_state(resource, &state); - } - } - - wl_array_release(&state); -} - -static void -workspace_send_initial_state(struct lab_cosmic_workspace *workspace, struct wl_resource *resource) -{ - zcosmic_workspace_handle_v1_send_capabilities(resource, &workspace->capabilities); - if (workspace->coordinates.size > 0) { - zcosmic_workspace_handle_v1_send_coordinates(resource, &workspace->coordinates); - } - if (workspace->name) { - zcosmic_workspace_handle_v1_send_name(resource, workspace->name); - } -} - -static void -workspace_set_state(struct lab_cosmic_workspace *workspace, - enum workspace_state state, bool enabled) -{ - if (!!(workspace->state_pending & state) == enabled) { - return; - } - - if (enabled) { - workspace->state_pending |= state; - } else { - workspace->state_pending &= ~state; - } - cosmic_manager_schedule_done_event(workspace->group->manager); -} - -/* Group */ -static void -ws_create_workspace_handle_transaction_op_destroy(struct wl_listener *listener, void *data) -{ - struct ws_create_workspace_event *ev = - wl_container_of(listener, ev, on.transaction_op_destroy); - wl_list_remove(&ev->on.transaction_op_destroy.link); - free(ev->name); - free(ev); -} - -static void -group_handle_create_workspace(struct wl_client *client, - struct wl_resource *resource, const char *name) -{ - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource); - if (!addon) { - return; - } - - struct lab_cosmic_workspace_group *group = addon->data; - struct ws_create_workspace_event *ev = znew(*ev); - ev->name = xstrdup(name); - - struct lab_transaction_op *transaction_op = lab_transaction_op_add( - addon->ctx, CW_PENDING_WS_CREATE, group, ev); - - ev->on.transaction_op_destroy.notify = - ws_create_workspace_handle_transaction_op_destroy; - wl_signal_add(&transaction_op->events.destroy, &ev->on.transaction_op_destroy); -} - -static void -group_handle_destroy(struct wl_client *client, struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static const struct zcosmic_workspace_group_handle_v1_interface group_impl = { - .create_workspace = group_handle_create_workspace, - .destroy = group_handle_destroy, -}; - -static void -group_instance_resource_destroy(struct wl_resource *resource) -{ - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource); - if (addon) { - lab_resource_addon_destroy(addon); - wl_resource_set_user_data(resource, NULL); - } - wl_list_remove(wl_resource_get_link(resource)); -} - -static struct wl_resource * -group_resource_create(struct lab_cosmic_workspace_group *group, - struct wl_resource *manager_resource, struct lab_transaction_session_context *ctx) -{ - struct wl_client *client = wl_resource_get_client(manager_resource); - struct wl_resource *resource = wl_resource_create(client, - &zcosmic_workspace_group_handle_v1_interface, - wl_resource_get_version(manager_resource), 0); - if (!resource) { - wl_client_post_no_memory(client); - return NULL; - } - - struct lab_wl_resource_addon *addon = lab_resource_addon_create(ctx); - addon->data = group; - - wl_resource_set_implementation(resource, &group_impl, addon, - group_instance_resource_destroy); - - wl_list_insert(&group->resources, wl_resource_get_link(resource)); - return resource; -} - -/* Group internal helpers */ -static void -group_send_state(struct lab_cosmic_workspace_group *group, struct wl_resource *resource) -{ - zcosmic_workspace_group_handle_v1_send_capabilities( - resource, &group->capabilities); - - cosmic_group_output_send_initial_state(group, resource); -} - -/* Manager itself */ -static void -manager_handle_commit(struct wl_client *client, struct wl_resource *resource) -{ - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource); - if (!addon) { - return; - } - - struct lab_cosmic_workspace *workspace; - struct lab_cosmic_workspace_group *group; - struct lab_transaction_op *trans_op, *trans_op_tmp; - lab_transaction_for_each_safe(trans_op, trans_op_tmp, addon->ctx) { - switch (trans_op->change) { - case CW_PENDING_WS_CREATE: { - group = trans_op->src; - struct ws_create_workspace_event *ev = trans_op->data; - wl_signal_emit_mutable(&group->events.create_workspace, ev->name); - break; - } - case CW_PENDING_WS_ACTIVATE: - workspace = trans_op->src; - wl_signal_emit_mutable(&workspace->events.activate, NULL); - break; - case CW_PENDING_WS_DEACTIVATE: - workspace = trans_op->src; - wl_signal_emit_mutable(&workspace->events.deactivate, NULL); - break; - case CW_PENDING_WS_REMOVE: - workspace = trans_op->src; - wl_signal_emit_mutable(&workspace->events.remove, NULL); - break; - default: - wlr_log(WLR_ERROR, "Invalid transaction state: %u", trans_op->change); - } - - lab_transaction_op_destroy(trans_op); - } -} - -static void -manager_handle_stop(struct wl_client *client, struct wl_resource *resource) -{ - zcosmic_workspace_manager_v1_send_finished(resource); - wl_resource_destroy(resource); -} - -static const struct zcosmic_workspace_manager_v1_interface manager_impl = { - .commit = manager_handle_commit, - .stop = manager_handle_stop, -}; - -static void -manager_instance_resource_destroy(struct wl_resource *resource) -{ - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource); - if (addon) { - lab_resource_addon_destroy(addon); - wl_resource_set_user_data(resource, NULL); - } - - wl_list_remove(wl_resource_get_link(resource)); -} - -static void -manager_handle_bind(struct wl_client *client, void *data, - uint32_t version, uint32_t id) -{ - struct lab_cosmic_workspace_manager *manager = data; - struct wl_resource *resource = wl_resource_create(client, - &zcosmic_workspace_manager_v1_interface, - version, id); - if (!resource) { - wl_client_post_no_memory(client); - return; - } - - struct lab_wl_resource_addon *addon = - lab_resource_addon_create(/* session context*/ NULL); - addon->data = manager; - - wl_resource_set_implementation(resource, &manager_impl, - addon, manager_instance_resource_destroy); - - wl_list_insert(&manager->resources, wl_resource_get_link(resource)); - - struct lab_cosmic_workspace *workspace; - struct lab_cosmic_workspace_group *group; - wl_list_for_each(group, &manager->groups, link) { - /* Create group resource */ - struct wl_resource *group_resource = - group_resource_create(group, resource, addon->ctx); - zcosmic_workspace_manager_v1_send_workspace_group(resource, group_resource); - group_send_state(group, group_resource); - - /* Create workspace resource */ - wl_list_for_each(workspace, &group->workspaces, link) { - struct wl_resource *workspace_resource = - workspace_resource_create(workspace, group_resource, addon->ctx); - zcosmic_workspace_group_handle_v1_send_workspace( - group_resource, workspace_resource); - workspace_send_initial_state(workspace, workspace_resource); - /* Send the current workspace state manually */ - workspace_send_state(workspace, workspace_resource); - } - } - zcosmic_workspace_manager_v1_send_done(resource); -} - -static void -manager_handle_display_destroy(struct wl_listener *listener, void *data) -{ - struct lab_cosmic_workspace_manager *manager = - wl_container_of(listener, manager, on.display_destroy); - - struct lab_cosmic_workspace_group *group, *tmp; - wl_list_for_each_safe(group, tmp, &manager->groups, link) { - lab_cosmic_workspace_group_destroy(group); - } - - if (manager->idle_source) { - wl_event_source_remove(manager->idle_source); - } - - wl_list_remove(&manager->on.display_destroy.link); - wl_global_destroy(manager->global); - free(manager); -} - -/* Manager internal helpers */ -static void -manager_idle_send_done(void *data) -{ - struct lab_cosmic_workspace_manager *manager = data; - - struct lab_cosmic_workspace *workspace; - struct lab_cosmic_workspace_group *group; - wl_list_for_each(group, &manager->groups, link) { - wl_list_for_each(workspace, &group->workspaces, link) { - if (workspace->state != workspace->state_pending) { - workspace->state = workspace->state_pending; - workspace_send_state(workspace, /*target*/ NULL); - } - } - } - - struct wl_resource *resource; - wl_resource_for_each(resource, &manager->resources) { - zcosmic_workspace_manager_v1_send_done(resource); - } - manager->idle_source = NULL; -} - -/* Internal API */ -void -cosmic_manager_schedule_done_event(struct lab_cosmic_workspace_manager *manager) -{ - if (manager->idle_source) { - return; - } - if (!manager->event_loop) { - return; - } - manager->idle_source = wl_event_loop_add_idle( - manager->event_loop, manager_idle_send_done, manager); -} - -/* Public API */ -struct lab_cosmic_workspace_manager * -lab_cosmic_workspace_manager_create(struct wl_display *display, uint32_t caps, uint32_t version) -{ - assert(version <= COSMIC_WORKSPACE_V1_VERSION); - - struct lab_cosmic_workspace_manager *manager = znew(*manager); - manager->global = wl_global_create(display, - &zcosmic_workspace_manager_v1_interface, - version, manager, manager_handle_bind); - - if (!manager->global) { - free(manager); - return NULL; - } - - manager->caps = caps; - manager->event_loop = wl_display_get_event_loop(display); - - manager->on.display_destroy.notify = manager_handle_display_destroy; - wl_display_add_destroy_listener(display, &manager->on.display_destroy); - - wl_list_init(&manager->groups); - wl_list_init(&manager->resources); - return manager; -} - -struct lab_cosmic_workspace_group * -lab_cosmic_workspace_group_create(struct lab_cosmic_workspace_manager *manager) -{ - assert(manager); - - struct lab_cosmic_workspace_group *group = znew(*group); - group->manager = manager; - - wl_array_init(&group->capabilities); - add_caps(&group->capabilities, manager->caps & CW_CAP_GRP_ALL); - - wl_list_init(&group->outputs); - wl_list_init(&group->resources); - wl_list_init(&group->workspaces); - wl_signal_init(&group->events.create_workspace); - wl_signal_init(&group->events.destroy); - - wl_list_append(&manager->groups, &group->link); - - struct wl_resource *resource, *tmp; - wl_resource_for_each_safe(resource, tmp, &manager->resources) { - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource); - assert(addon && addon->ctx); - struct wl_resource *group_resource = - group_resource_create(group, resource, addon->ctx); - zcosmic_workspace_manager_v1_send_workspace_group(resource, group_resource); - group_send_state(group, group_resource); - } - cosmic_manager_schedule_done_event(manager); - - return group; -} - -void -lab_cosmic_workspace_group_destroy(struct lab_cosmic_workspace_group *group) -{ - if (!group) { - return; - } - wl_signal_emit_mutable(&group->events.destroy, NULL); - - struct lab_cosmic_workspace *ws, *ws_tmp; - wl_list_for_each_safe(ws, ws_tmp, &group->workspaces, link) { - lab_cosmic_workspace_destroy(ws); - } - - struct wl_resource *resource, *res_tmp; - wl_resource_for_each_safe(resource, res_tmp, &group->resources) { - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource); - if (addon) { - lab_resource_addon_destroy(addon); - wl_resource_set_user_data(resource, NULL); - } - zcosmic_workspace_group_handle_v1_send_remove(resource); - wl_list_remove(wl_resource_get_link(resource)); - wl_list_init(wl_resource_get_link(resource)); - } - - /* Cancel pending transaction operations involving this group */ - struct lab_transaction_op *trans_op, *trans_op_tmp; - wl_resource_for_each(resource, &group->manager->resources) { - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource); - if (!addon) { - continue; - } - lab_transaction_for_each_safe(trans_op, trans_op_tmp, addon->ctx) { - if (trans_op->src == group) { - lab_transaction_op_destroy(trans_op); - } - } - } - - wl_list_remove(&group->link); - wl_array_release(&group->capabilities); - free(group); -} - -struct lab_cosmic_workspace * -lab_cosmic_workspace_create(struct lab_cosmic_workspace_group *group) -{ - assert(group); - - struct lab_cosmic_workspace *workspace = znew(*workspace); - workspace->group = group; - /* - * Ensures we are sending workspace->state_pending on the done event, - * regardless if the compositor has changed any state in between here - * and the scheduled done event or not. - * - * Without this we might have to send the state twice, first here and - * then again in the scheduled done event when there were any changes. - */ - workspace->state = CW_WS_STATE_INVALID; - - wl_array_init(&workspace->capabilities); - add_caps(&workspace->capabilities, group->manager->caps & CW_CAP_WS_ALL); - - wl_list_init(&workspace->resources); - wl_array_init(&workspace->coordinates); - wl_signal_init(&workspace->events.activate); - wl_signal_init(&workspace->events.deactivate); - wl_signal_init(&workspace->events.remove); - wl_signal_init(&workspace->events.destroy); - - wl_list_append(&group->workspaces, &workspace->link); - - /* Notify clients */ - struct wl_resource *group_resource; - wl_resource_for_each(group_resource, &group->resources) { - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(group_resource); - assert(addon && addon->ctx); - struct wl_resource *workspace_resource = - workspace_resource_create(workspace, group_resource, addon->ctx); - zcosmic_workspace_group_handle_v1_send_workspace( - group_resource, workspace_resource); - workspace_send_initial_state(workspace, workspace_resource); - } - cosmic_manager_schedule_done_event(group->manager); - - return workspace; -} - -void -lab_cosmic_workspace_set_name(struct lab_cosmic_workspace *workspace, const char *name) -{ - assert(workspace); - assert(name); - - if (!workspace->name || strcmp(workspace->name, name)) { - free(workspace->name); - workspace->name = xstrdup(name); - struct wl_resource *resource; - wl_resource_for_each(resource, &workspace->resources) { - zcosmic_workspace_handle_v1_send_name(resource, workspace->name); - } - } - cosmic_manager_schedule_done_event(workspace->group->manager); -} - -void -lab_cosmic_workspace_set_active(struct lab_cosmic_workspace *workspace, bool enabled) -{ - workspace_set_state(workspace, CW_WS_STATE_ACTIVE, enabled); -} - -void -lab_cosmic_workspace_set_urgent(struct lab_cosmic_workspace *workspace, bool enabled) -{ - workspace_set_state(workspace, CW_WS_STATE_URGENT, enabled); -} - -void -lab_cosmic_workspace_set_hidden(struct lab_cosmic_workspace *workspace, bool enabled) -{ - workspace_set_state(workspace, CW_WS_STATE_HIDDEN, enabled); -} - -void -lab_cosmic_workspace_set_coordinates(struct lab_cosmic_workspace *workspace, - struct wl_array *coordinates) -{ - wl_array_release(&workspace->coordinates); - wl_array_init(&workspace->coordinates); - wl_array_copy(&workspace->coordinates, coordinates); - - struct wl_resource *resource; - wl_resource_for_each(resource, &workspace->resources) { - zcosmic_workspace_handle_v1_send_coordinates(resource, &workspace->coordinates); - } - cosmic_manager_schedule_done_event(workspace->group->manager); -} - -void -lab_cosmic_workspace_destroy(struct lab_cosmic_workspace *workspace) -{ - if (!workspace) { - return; - } - wl_signal_emit_mutable(&workspace->events.destroy, NULL); - - struct wl_resource *resource, *tmp; - wl_resource_for_each_safe(resource, tmp, &workspace->resources) { - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource); - if (addon) { - lab_resource_addon_destroy(addon); - wl_resource_set_user_data(resource, NULL); - } - zcosmic_workspace_handle_v1_send_remove(resource); - wl_list_remove(wl_resource_get_link(resource)); - wl_list_init(wl_resource_get_link(resource)); - } - cosmic_manager_schedule_done_event(workspace->group->manager); - - /* Cancel pending transaction operations involving this workspace */ - struct lab_transaction_op *trans_op, *trans_op_tmp; - wl_resource_for_each(resource, &workspace->group->manager->resources) { - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource); - if (!addon) { - continue; - } - lab_transaction_for_each_safe(trans_op, trans_op_tmp, addon->ctx) { - if (trans_op->src == workspace) { - lab_transaction_op_destroy(trans_op); - } - } - } - - wl_list_remove(&workspace->link); - wl_array_release(&workspace->coordinates); - wl_array_release(&workspace->capabilities); - zfree(workspace->name); - free(workspace); -} diff --git a/src/protocols/cosmic_workspaces/meson.build b/src/protocols/cosmic_workspaces/meson.build deleted file mode 100644 index 31ce18b8..00000000 --- a/src/protocols/cosmic_workspaces/meson.build +++ /dev/null @@ -1,4 +0,0 @@ -labwc_sources += files( - 'cosmic-workspaces.c', - 'output.c', -) diff --git a/src/protocols/cosmic_workspaces/output.c b/src/protocols/cosmic_workspaces/output.c deleted file mode 100644 index 1e170e85..00000000 --- a/src/protocols/cosmic_workspaces/output.c +++ /dev/null @@ -1,174 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include -#include -#include -#include "common/mem.h" -#include "cosmic-workspace-unstable-v1-protocol.h" -#include "protocols/cosmic-workspaces.h" -#include "protocols/cosmic-workspaces-internal.h" - -struct group_output { - struct wlr_output *wlr_output; - struct lab_cosmic_workspace_group *group; - struct { - struct wl_listener group_destroy; - struct wl_listener output_bind; - struct wl_listener output_destroy; - } on; - - struct wl_list link; -}; - -/* Internal helpers */ -static void -group_output_send_event(struct wl_list *group_resources, struct wl_list *output_resources, - void (*notifier)(struct wl_resource *group, struct wl_resource *output)) -{ - struct wl_client *client; - struct wl_resource *group_resource, *output_resource; - wl_resource_for_each(group_resource, group_resources) { - client = wl_resource_get_client(group_resource); - wl_resource_for_each(output_resource, output_resources) { - if (wl_resource_get_client(output_resource) == client) { - notifier(group_resource, output_resource); - } - } - } -} - -static void -group_output_destroy(struct group_output *group_output) -{ - group_output_send_event( - &group_output->group->resources, - &group_output->wlr_output->resources, - zcosmic_workspace_group_handle_v1_send_output_leave); - - cosmic_manager_schedule_done_event(group_output->group->manager); - - wl_list_remove(&group_output->link); - wl_list_remove(&group_output->on.group_destroy.link); - wl_list_remove(&group_output->on.output_bind.link); - wl_list_remove(&group_output->on.output_destroy.link); - free(group_output); -} - -/* Event handlers */ -static void -handle_output_bind(struct wl_listener *listener, void *data) -{ - struct group_output *group_output = - wl_container_of(listener, group_output, on.output_bind); - - struct wlr_output_event_bind *event = data; - struct wl_client *client = wl_resource_get_client(event->resource); - - bool sent = false; - struct wl_resource *group_resource; - wl_resource_for_each(group_resource, &group_output->group->resources) { - if (wl_resource_get_client(group_resource) == client) { - zcosmic_workspace_group_handle_v1_send_output_enter( - group_resource, event->resource); - sent = true; - } - } - if (!sent) { - return; - } - - struct wl_resource *manager_resource; - struct wl_list *manager_resources = &group_output->group->manager->resources; - wl_resource_for_each(manager_resource, manager_resources) { - if (wl_resource_get_client(manager_resource) == client) { - zcosmic_workspace_manager_v1_send_done(manager_resource); - } - } -} - -static void -handle_output_destroy(struct wl_listener *listener, void *data) -{ - struct group_output *group_output = - wl_container_of(listener, group_output, on.output_destroy); - group_output_destroy(group_output); -} - -static void -handle_group_destroy(struct wl_listener *listener, void *data) -{ - struct group_output *group_output = - wl_container_of(listener, group_output, on.group_destroy); - group_output_destroy(group_output); -} - -/* Internal API*/ -void -cosmic_group_output_send_initial_state(struct lab_cosmic_workspace_group *group, - struct wl_resource *group_resource) -{ - struct group_output *group_output; - struct wl_resource *output_resource; - struct wl_client *client = wl_resource_get_client(group_resource); - wl_list_for_each(group_output, &group->outputs, link) { - wl_resource_for_each(output_resource, &group_output->wlr_output->resources) { - if (wl_resource_get_client(output_resource) == client) { - zcosmic_workspace_group_handle_v1_send_output_enter( - group_resource, output_resource); - } - } - } -} - -/* Public API */ -void -lab_cosmic_workspace_group_output_enter(struct lab_cosmic_workspace_group *group, - struct wlr_output *wlr_output) -{ - struct group_output *group_output; - wl_list_for_each(group_output, &group->outputs, link) { - if (group_output->wlr_output == wlr_output) { - return; - } - } - group_output = znew(*group_output); - group_output->wlr_output = wlr_output; - group_output->group = group; - - group_output->on.group_destroy.notify = handle_group_destroy; - wl_signal_add(&group->events.destroy, &group_output->on.group_destroy); - - group_output->on.output_bind.notify = handle_output_bind; - wl_signal_add(&wlr_output->events.bind, &group_output->on.output_bind); - - group_output->on.output_destroy.notify = handle_output_destroy; - wl_signal_add(&wlr_output->events.destroy, &group_output->on.output_destroy); - - wl_list_insert(&group->outputs, &group_output->link); - - group_output_send_event( - &group_output->group->resources, - &group_output->wlr_output->resources, - zcosmic_workspace_group_handle_v1_send_output_enter); - - cosmic_manager_schedule_done_event(group->manager); -} - -void -lab_cosmic_workspace_group_output_leave(struct lab_cosmic_workspace_group *group, - struct wlr_output *wlr_output) -{ - struct group_output *tmp; - struct group_output *group_output = NULL; - wl_list_for_each(tmp, &group->outputs, link) { - if (tmp->wlr_output == wlr_output) { - group_output = tmp; - break; - } - } - if (!group_output) { - wlr_log(WLR_ERROR, "output %s was never entered", wlr_output->name); - return; - } - - group_output_destroy(group_output); -} diff --git a/src/protocols/ext-workspace/ext-workspace.c b/src/protocols/ext-workspace/ext-workspace.c deleted file mode 100644 index 30875811..00000000 --- a/src/protocols/ext-workspace/ext-workspace.c +++ /dev/null @@ -1,762 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include -#include -#include "common/mem.h" -#include "common/list.h" -#include "ext-workspace-v1-protocol.h" -#include "protocols/ext-workspace.h" -#include "protocols/ext-workspace-internal.h" -#include "protocols/transaction-addon.h" - -/* - * .--------------------. - * | TODO | - * |--------------------| - * | - go through xml | - * | and verify impl | - * | - assert pub API | - * `--------------------´ - * - */ - -/* Only used within an assert() */ -#ifndef NDEBUG - #define EXT_WORKSPACE_V1_VERSION 1 -#endif - -/* - * Set when creating a new workspace state so we - * don't end up having to send the state twice. - */ -#define WS_STATE_INVALID 0xffffffff - -struct ws_create_workspace_event { - char *name; - struct { - struct wl_listener transaction_op_destroy; - } on; -}; - -/* Workspace */ -static void -workspace_handle_destroy(struct wl_client *client, struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static void -workspace_handle_activate(struct wl_client *client, struct wl_resource *resource) -{ - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource); - if (!addon) { - /* Workspace was destroyed from the compositor side */ - return; - } - struct lab_ext_workspace *workspace = addon->data; - lab_transaction_op_add(addon->ctx, WS_PENDING_WS_ACTIVATE, - workspace, /*data*/ NULL); -} - -static void -workspace_handle_deactivate(struct wl_client *client, struct wl_resource *resource) -{ - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource); - if (!addon) { - /* Workspace was destroyed from the compositor side */ - return; - } - struct lab_ext_workspace *workspace = addon->data; - lab_transaction_op_add(addon->ctx, WS_PENDING_WS_DEACTIVATE, - workspace, /*data*/ NULL); -} - -static void -workspace_handle_assign(struct wl_client *client, struct wl_resource *resource, - struct wl_resource *new_group_resource) -{ - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource); - if (!addon) { - /* Workspace was destroyed from the compositor side */ - return; - } - struct lab_ext_workspace *workspace = addon->data; - struct lab_wl_resource_addon *grp_addon = - wl_resource_get_user_data(new_group_resource); - if (!grp_addon) { - /* Group was destroyed from the compositor side */ - return; - } - struct lab_ext_workspace_group *new_grp = grp_addon->data; - lab_transaction_op_add(addon->ctx, WS_PENDING_WS_ASSIGN, workspace, new_grp); -} - -static void -workspace_handle_remove(struct wl_client *client, struct wl_resource *resource) -{ - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource); - if (!addon) { - /* Workspace was destroyed from the compositor side */ - return; - } - struct lab_ext_workspace *workspace = addon->data; - lab_transaction_op_add(addon->ctx, WS_PENDING_WS_REMOVE, - workspace, /*data*/ NULL); -} - -static const struct ext_workspace_handle_v1_interface workspace_impl = { - .destroy = workspace_handle_destroy, - .activate = workspace_handle_activate, - .deactivate = workspace_handle_deactivate, - .assign = workspace_handle_assign, - .remove = workspace_handle_remove, -}; - -static void -workspace_instance_resource_destroy(struct wl_resource *resource) -{ - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource); - if (addon) { - lab_resource_addon_destroy(addon); - wl_resource_set_user_data(resource, NULL); - } - - wl_list_remove(wl_resource_get_link(resource)); -} - -static struct wl_resource * -workspace_resource_create(struct lab_ext_workspace *workspace, - struct wl_resource *manager_resource, - struct lab_transaction_session_context *ctx) -{ - struct wl_client *client = wl_resource_get_client(manager_resource); - struct wl_resource *resource = wl_resource_create(client, - &ext_workspace_handle_v1_interface, - wl_resource_get_version(manager_resource), 0); - if (!resource) { - wl_client_post_no_memory(client); - return NULL; - } - - struct lab_wl_resource_addon *addon = lab_resource_addon_create(ctx); - addon->data = workspace; - - wl_resource_set_implementation(resource, &workspace_impl, addon, - workspace_instance_resource_destroy); - - wl_list_insert(&workspace->resources, wl_resource_get_link(resource)); - return resource; -} - -/* Workspace internal helpers */ -static void -workspace_send_state(struct lab_ext_workspace *workspace, struct wl_resource *target) -{ - if (target) { - ext_workspace_handle_v1_send_state(target, workspace->state); - } else { - struct wl_resource *resource; - wl_resource_for_each(resource, &workspace->resources) { - ext_workspace_handle_v1_send_state(resource, workspace->state); - } - } -} - -static void -workspace_send_initial_state(struct lab_ext_workspace *workspace, struct wl_resource *resource) -{ - ext_workspace_handle_v1_send_capabilities(resource, workspace->capabilities); - if (workspace->coordinates.size > 0) { - ext_workspace_handle_v1_send_coordinates(resource, &workspace->coordinates); - } - if (workspace->name) { - ext_workspace_handle_v1_send_name(resource, workspace->name); - } - if (workspace->id) { - ext_workspace_handle_v1_send_id(resource, workspace->id); - } -} - -static void -workspace_set_state(struct lab_ext_workspace *workspace, - enum ext_workspace_handle_v1_state state, bool enabled) -{ - if (!!(workspace->state_pending & state) == enabled) { - return; - } - - if (enabled) { - workspace->state_pending |= state; - } else { - workspace->state_pending &= ~state; - } - ext_manager_schedule_done_event(workspace->manager); -} - -/* Group */ -static void -ws_create_workspace_handle_transaction_op_destroy(struct wl_listener *listener, void *data) -{ - struct ws_create_workspace_event *ev = - wl_container_of(listener, ev, on.transaction_op_destroy); - wl_list_remove(&ev->on.transaction_op_destroy.link); - free(ev->name); - free(ev); -} - -static void -group_handle_create_workspace(struct wl_client *client, - struct wl_resource *resource, const char *name) -{ - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource); - if (!addon) { - return; - } - - struct lab_ext_workspace_group *group = addon->data; - struct ws_create_workspace_event *ev = znew(*ev); - ev->name = xstrdup(name); - - struct lab_transaction_op *transaction_op = lab_transaction_op_add( - addon->ctx, WS_PENDING_WS_CREATE, group, ev); - - ev->on.transaction_op_destroy.notify = - ws_create_workspace_handle_transaction_op_destroy; - wl_signal_add(&transaction_op->events.destroy, &ev->on.transaction_op_destroy); -} - -static void -group_handle_destroy(struct wl_client *client, struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static const struct ext_workspace_group_handle_v1_interface group_impl = { - .create_workspace = group_handle_create_workspace, - .destroy = group_handle_destroy, -}; - -static void -group_instance_resource_destroy(struct wl_resource *resource) -{ - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource); - if (addon) { - lab_resource_addon_destroy(addon); - wl_resource_set_user_data(resource, NULL); - } - wl_list_remove(wl_resource_get_link(resource)); -} - -static struct wl_resource * -group_resource_create(struct lab_ext_workspace_group *group, - struct wl_resource *manager_resource, struct lab_transaction_session_context *ctx) -{ - struct wl_client *client = wl_resource_get_client(manager_resource); - struct wl_resource *resource = wl_resource_create(client, - &ext_workspace_group_handle_v1_interface, - wl_resource_get_version(manager_resource), 0); - if (!resource) { - wl_client_post_no_memory(client); - return NULL; - } - - struct lab_wl_resource_addon *addon = lab_resource_addon_create(ctx); - addon->data = group; - - wl_resource_set_implementation(resource, &group_impl, addon, - group_instance_resource_destroy); - - wl_list_insert(&group->resources, wl_resource_get_link(resource)); - return resource; -} - -/* Group internal helpers */ -static void -group_send_state(struct lab_ext_workspace_group *group, struct wl_resource *resource) -{ - ext_workspace_group_handle_v1_send_capabilities( - resource, group->capabilities); - - ext_group_output_send_initial_state(group, resource); -} - -/* Manager itself */ -static void -manager_handle_commit(struct wl_client *client, struct wl_resource *resource) -{ - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource); - if (!addon) { - return; - } - - struct lab_ext_workspace *workspace; - struct lab_ext_workspace_group *group; - struct lab_transaction_op *trans_op, *trans_op_tmp; - lab_transaction_for_each_safe(trans_op, trans_op_tmp, addon->ctx) { - switch (trans_op->change) { - case WS_PENDING_WS_CREATE: { - group = trans_op->src; - struct ws_create_workspace_event *ev = trans_op->data; - wl_signal_emit_mutable(&group->events.create_workspace, ev->name); - break; - } - case WS_PENDING_WS_ACTIVATE: - workspace = trans_op->src; - wl_signal_emit_mutable(&workspace->events.activate, NULL); - break; - case WS_PENDING_WS_DEACTIVATE: - workspace = trans_op->src; - wl_signal_emit_mutable(&workspace->events.deactivate, NULL); - break; - case WS_PENDING_WS_REMOVE: - workspace = trans_op->src; - wl_signal_emit_mutable(&workspace->events.remove, NULL); - break; - case WS_PENDING_WS_ASSIGN: - workspace = trans_op->src; - wl_signal_emit_mutable(&workspace->events.assign, trans_op->data); - break; - default: - wlr_log(WLR_ERROR, "Invalid transaction state: %u", trans_op->change); - } - - lab_transaction_op_destroy(trans_op); - } -} - -static void -manager_handle_stop(struct wl_client *client, struct wl_resource *resource) -{ - ext_workspace_manager_v1_send_finished(resource); - wl_resource_destroy(resource); -} - -static const struct ext_workspace_manager_v1_interface manager_impl = { - .commit = manager_handle_commit, - .stop = manager_handle_stop, -}; - -static void -manager_instance_resource_destroy(struct wl_resource *resource) -{ - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource); - if (addon) { - lab_resource_addon_destroy(addon); - wl_resource_set_user_data(resource, NULL); - } - wl_list_remove(wl_resource_get_link(resource)); -} - -static void -manager_handle_bind(struct wl_client *client, void *data, - uint32_t version, uint32_t id) -{ - struct lab_ext_workspace_manager *manager = data; - struct wl_resource *manager_resource = wl_resource_create(client, - &ext_workspace_manager_v1_interface, - version, id); - if (!manager_resource) { - wl_client_post_no_memory(client); - return; - } - - struct lab_wl_resource_addon *addon = - lab_resource_addon_create(/* session context*/ NULL); - addon->data = manager; - - wl_resource_set_implementation(manager_resource, &manager_impl, - addon, manager_instance_resource_destroy); - - wl_list_insert(&manager->resources, wl_resource_get_link(manager_resource)); - - struct lab_ext_workspace *workspace; - struct lab_ext_workspace_group *group; - wl_list_for_each(group, &manager->groups, link) { - /* Create group resource */ - struct wl_resource *group_resource = - group_resource_create(group, manager_resource, addon->ctx); - ext_workspace_manager_v1_send_workspace_group(manager_resource, group_resource); - group_send_state(group, group_resource); - wl_list_for_each(workspace, &manager->workspaces, link) { - if (workspace->group != group) { - continue; - } - /* Create workspace resource for the current group */ - struct wl_resource *workspace_resource = workspace_resource_create( - workspace, manager_resource, addon->ctx); - ext_workspace_manager_v1_send_workspace( - manager_resource, workspace_resource); - workspace_send_initial_state(workspace, workspace_resource); - workspace_send_state(workspace, workspace_resource); - ext_workspace_group_handle_v1_send_workspace_enter( - group_resource, workspace_resource); - } - } - - /* Create workspace resource for workspaces not belonging to any group */ - wl_list_for_each(workspace, &manager->workspaces, link) { - if (workspace->group) { - continue; - } - struct wl_resource *workspace_resource = - workspace_resource_create(workspace, manager_resource, addon->ctx); - ext_workspace_manager_v1_send_workspace(manager_resource, workspace_resource); - workspace_send_initial_state(workspace, workspace_resource); - workspace_send_state(workspace, workspace_resource); - } - ext_workspace_manager_v1_send_done(manager_resource); -} - -static void -manager_handle_display_destroy(struct wl_listener *listener, void *data) -{ - struct lab_ext_workspace_manager *manager = - wl_container_of(listener, manager, on.display_destroy); - - struct lab_ext_workspace_group *group, *tmp; - wl_list_for_each_safe(group, tmp, &manager->groups, link) { - lab_ext_workspace_group_destroy(group); - } - - struct lab_ext_workspace *ws, *ws_tmp; - wl_list_for_each_safe(ws, ws_tmp, &manager->workspaces, link) { - lab_ext_workspace_destroy(ws); - } - - if (manager->idle_source) { - wl_event_source_remove(manager->idle_source); - } - - wl_list_remove(&manager->on.display_destroy.link); - wl_global_destroy(manager->global); - free(manager); -} - -/* Manager internal helpers */ -static void -manager_idle_send_done(void *data) -{ - struct lab_ext_workspace_manager *manager = data; - - struct lab_ext_workspace *workspace; - wl_list_for_each(workspace, &manager->workspaces, link) { - if (workspace->state != workspace->state_pending) { - workspace->state = workspace->state_pending; - workspace_send_state(workspace, /*target*/ NULL); - } - } - - struct wl_resource *resource; - wl_resource_for_each(resource, &manager->resources) { - ext_workspace_manager_v1_send_done(resource); - } - manager->idle_source = NULL; -} - -/* Internal API */ -void -ext_manager_schedule_done_event(struct lab_ext_workspace_manager *manager) -{ - if (manager->idle_source) { - return; - } - if (!manager->event_loop) { - return; - } - manager->idle_source = wl_event_loop_add_idle( - manager->event_loop, manager_idle_send_done, manager); -} - -static void -send_group_workspace_event(struct lab_ext_workspace_group *group, - struct lab_ext_workspace *workspace, - void (*fn)(struct wl_resource *grp_res, struct wl_resource *ws_res)) -{ - struct lab_wl_resource_addon *workspace_addon, *group_addon; - struct wl_resource *workspace_resource, *group_resource; - wl_resource_for_each(workspace_resource, &workspace->resources) { - workspace_addon = wl_resource_get_user_data(workspace_resource); - wl_resource_for_each(group_resource, &group->resources) { - group_addon = wl_resource_get_user_data(group_resource); - if (group_addon->ctx != workspace_addon->ctx) { - continue; - } - fn(group_resource, workspace_resource); - break; - } - } -} - -/* Public API */ -struct lab_ext_workspace_manager * -lab_ext_workspace_manager_create(struct wl_display *display, uint32_t caps, uint32_t version) -{ - assert(display); - assert(version <= EXT_WORKSPACE_V1_VERSION); - - struct lab_ext_workspace_manager *manager = znew(*manager); - manager->global = wl_global_create(display, - &ext_workspace_manager_v1_interface, - version, manager, manager_handle_bind); - - if (!manager->global) { - free(manager); - return NULL; - } - - manager->caps = caps; - manager->event_loop = wl_display_get_event_loop(display); - - manager->on.display_destroy.notify = manager_handle_display_destroy; - wl_display_add_destroy_listener(display, &manager->on.display_destroy); - - wl_list_init(&manager->groups); - wl_list_init(&manager->workspaces); - wl_list_init(&manager->resources); - return manager; -} - -struct lab_ext_workspace_group * -lab_ext_workspace_group_create(struct lab_ext_workspace_manager *manager) -{ - assert(manager); - - struct lab_ext_workspace_group *group = znew(*group); - group->manager = manager; - group->capabilities = manager->caps & WS_CAP_GRP_ALL; - - wl_list_init(&group->outputs); - wl_list_init(&group->resources); - wl_signal_init(&group->events.create_workspace); - wl_signal_init(&group->events.destroy); - - wl_list_append(&manager->groups, &group->link); - - struct wl_resource *resource, *tmp; - wl_resource_for_each_safe(resource, tmp, &manager->resources) { - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource); - assert(addon && addon->ctx); - struct wl_resource *group_resource = - group_resource_create(group, resource, addon->ctx); - ext_workspace_manager_v1_send_workspace_group(resource, group_resource); - group_send_state(group, group_resource); - } - ext_manager_schedule_done_event(manager); - - return group; -} - -void -lab_ext_workspace_group_destroy(struct lab_ext_workspace_group *group) -{ - assert(group); - wl_signal_emit_mutable(&group->events.destroy, NULL); - - struct lab_ext_workspace *workspace; - wl_list_for_each(workspace, &group->manager->workspaces, link) { - if (workspace->group == group) { - send_group_workspace_event(group, workspace, - ext_workspace_group_handle_v1_send_workspace_leave); - workspace->group = NULL; - } - } - - struct wl_resource *resource, *res_tmp; - wl_resource_for_each_safe(resource, res_tmp, &group->resources) { - ext_workspace_group_handle_v1_send_removed(resource); - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource); - lab_resource_addon_destroy(addon); - wl_resource_set_user_data(resource, NULL); - wl_list_remove(wl_resource_get_link(resource)); - wl_list_init(wl_resource_get_link(resource)); - } - - /* Cancel pending transaction ops involving this group */ - struct lab_transaction_op *trans_op, *trans_op_tmp; - wl_resource_for_each(resource, &group->manager->resources) { - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource); - lab_transaction_for_each_safe(trans_op, trans_op_tmp, addon->ctx) { - if (trans_op->src == group || trans_op->data == group) { - lab_transaction_op_destroy(trans_op); - } - } - } - - ext_manager_schedule_done_event(group->manager); - - wl_list_remove(&group->link); - free(group); -} - -struct lab_ext_workspace * -lab_ext_workspace_create(struct lab_ext_workspace_manager *manager, const char *id) -{ - assert(manager); - - struct lab_ext_workspace *workspace = znew(*workspace); - /* - * Ensures we are sending workspace->state_pending on the done event, - * regardless if the compositor has changed any state in between here - * and the scheduled done event or not. - * - * Without this we might have to send the state twice, first here and - * then again in the scheduled done event when there were any changes. - */ - workspace->state = WS_STATE_INVALID; - workspace->capabilities = (manager->caps & WS_CAP_WS_ALL) >> 16; - workspace->manager = manager; - if (id) { - workspace->id = xstrdup(id); - } - - wl_list_init(&workspace->resources); - wl_array_init(&workspace->coordinates); - wl_signal_init(&workspace->events.activate); - wl_signal_init(&workspace->events.deactivate); - wl_signal_init(&workspace->events.remove); - wl_signal_init(&workspace->events.assign); - wl_signal_init(&workspace->events.destroy); - - wl_list_append(&manager->workspaces, &workspace->link); - - /* Notify clients */ - struct lab_wl_resource_addon *manager_addon; - struct wl_resource *manager_resource, *workspace_resource; - wl_resource_for_each(manager_resource, &manager->resources) { - manager_addon = wl_resource_get_user_data(manager_resource); - workspace_resource = workspace_resource_create( - workspace, manager_resource, manager_addon->ctx); - ext_workspace_manager_v1_send_workspace( - manager_resource, workspace_resource); - workspace_send_initial_state(workspace, workspace_resource); - } - - ext_manager_schedule_done_event(manager); - - return workspace; -} - -void -lab_ext_workspace_assign_to_group(struct lab_ext_workspace *workspace, - struct lab_ext_workspace_group *group) -{ - assert(workspace); - - if (workspace->group == group) { - return; - } - - if (workspace->group) { - /* Send leave event for the old group */ - send_group_workspace_event(workspace->group, workspace, - ext_workspace_group_handle_v1_send_workspace_leave); - ext_manager_schedule_done_event(workspace->manager); - } - workspace->group = group; - - if (!group) { - return; - } - - /* Send enter event for the new group */ - send_group_workspace_event(group, workspace, - ext_workspace_group_handle_v1_send_workspace_enter); - ext_manager_schedule_done_event(workspace->manager); -} - -void -lab_ext_workspace_set_name(struct lab_ext_workspace *workspace, const char *name) -{ - assert(workspace); - assert(name); - - if (!workspace->name || strcmp(workspace->name, name)) { - free(workspace->name); - workspace->name = xstrdup(name); - struct wl_resource *resource; - wl_resource_for_each(resource, &workspace->resources) { - ext_workspace_handle_v1_send_name(resource, workspace->name); - } - } - ext_manager_schedule_done_event(workspace->manager); -} - -void -lab_ext_workspace_set_active(struct lab_ext_workspace *workspace, bool enabled) -{ - assert(workspace); - workspace_set_state(workspace, EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE, enabled); -} - -void -lab_ext_workspace_set_urgent(struct lab_ext_workspace *workspace, bool enabled) -{ - assert(workspace); - workspace_set_state(workspace, EXT_WORKSPACE_HANDLE_V1_STATE_URGENT, enabled); -} - -void -lab_ext_workspace_set_hidden(struct lab_ext_workspace *workspace, bool enabled) -{ - assert(workspace); - workspace_set_state(workspace, EXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN, enabled); -} - -void -lab_ext_workspace_set_coordinates(struct lab_ext_workspace *workspace, - struct wl_array *coordinates) -{ - assert(workspace); - assert(coordinates); - - wl_array_release(&workspace->coordinates); - wl_array_init(&workspace->coordinates); - wl_array_copy(&workspace->coordinates, coordinates); - - struct wl_resource *resource; - wl_resource_for_each(resource, &workspace->resources) { - ext_workspace_handle_v1_send_coordinates(resource, &workspace->coordinates); - } - ext_manager_schedule_done_event(workspace->manager); -} - -void -lab_ext_workspace_destroy(struct lab_ext_workspace *workspace) -{ - assert(workspace); - - wl_signal_emit_mutable(&workspace->events.destroy, NULL); - - if (workspace->group) { - send_group_workspace_event(workspace->group, workspace, - ext_workspace_group_handle_v1_send_workspace_leave); - } - - struct wl_resource *ws_res, *ws_tmp; - wl_resource_for_each_safe(ws_res, ws_tmp, &workspace->resources) { - ext_workspace_handle_v1_send_removed(ws_res); - struct lab_wl_resource_addon *ws_addon = wl_resource_get_user_data(ws_res); - lab_resource_addon_destroy(ws_addon); - wl_resource_set_user_data(ws_res, NULL); - wl_list_remove(wl_resource_get_link(ws_res)); - wl_list_init(wl_resource_get_link(ws_res)); - } - ext_manager_schedule_done_event(workspace->manager); - - /* Cancel pending transaction ops involving this workspace */ - struct wl_resource *resource; - struct lab_transaction_op *trans_op, *trans_op_tmp; - wl_resource_for_each(resource, &workspace->manager->resources) { - struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource); - lab_transaction_for_each_safe(trans_op, trans_op_tmp, addon->ctx) { - if (trans_op->src == workspace) { - lab_transaction_op_destroy(trans_op); - } - } - } - - wl_list_remove(&workspace->link); - wl_array_release(&workspace->coordinates); - zfree(workspace->id); - zfree(workspace->name); - free(workspace); -} diff --git a/src/protocols/ext-workspace/meson.build b/src/protocols/ext-workspace/meson.build deleted file mode 100644 index e781d13a..00000000 --- a/src/protocols/ext-workspace/meson.build +++ /dev/null @@ -1,4 +0,0 @@ -labwc_sources += files( - 'ext-workspace.c', - 'output.c', -) diff --git a/src/protocols/ext-workspace/output.c b/src/protocols/ext-workspace/output.c deleted file mode 100644 index 0d6a2a44..00000000 --- a/src/protocols/ext-workspace/output.c +++ /dev/null @@ -1,174 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include -#include -#include -#include "common/mem.h" -#include "ext-workspace-v1-protocol.h" -#include "protocols/ext-workspace.h" -#include "protocols/ext-workspace-internal.h" - -struct group_output { - struct wlr_output *wlr_output; - struct lab_ext_workspace_group *group; - struct { - struct wl_listener group_destroy; - struct wl_listener output_bind; - struct wl_listener output_destroy; - } on; - - struct wl_list link; -}; - -/* Internal helpers */ -static void -group_output_send_event(struct wl_list *group_resources, struct wl_list *output_resources, - void (*notifier)(struct wl_resource *group, struct wl_resource *output)) -{ - struct wl_client *client; - struct wl_resource *group_resource, *output_resource; - wl_resource_for_each(group_resource, group_resources) { - client = wl_resource_get_client(group_resource); - wl_resource_for_each(output_resource, output_resources) { - if (wl_resource_get_client(output_resource) == client) { - notifier(group_resource, output_resource); - } - } - } -} - -static void -group_output_destroy(struct group_output *group_output) -{ - group_output_send_event( - &group_output->group->resources, - &group_output->wlr_output->resources, - ext_workspace_group_handle_v1_send_output_leave); - - ext_manager_schedule_done_event(group_output->group->manager); - - wl_list_remove(&group_output->link); - wl_list_remove(&group_output->on.group_destroy.link); - wl_list_remove(&group_output->on.output_bind.link); - wl_list_remove(&group_output->on.output_destroy.link); - free(group_output); -} - -/* Event handlers */ -static void -handle_output_bind(struct wl_listener *listener, void *data) -{ - struct group_output *group_output = - wl_container_of(listener, group_output, on.output_bind); - - struct wlr_output_event_bind *event = data; - struct wl_client *client = wl_resource_get_client(event->resource); - - bool sent = false; - struct wl_resource *group_resource; - wl_resource_for_each(group_resource, &group_output->group->resources) { - if (wl_resource_get_client(group_resource) == client) { - ext_workspace_group_handle_v1_send_output_enter( - group_resource, event->resource); - sent = true; - } - } - if (!sent) { - return; - } - - struct wl_resource *manager_resource; - struct wl_list *manager_resources = &group_output->group->manager->resources; - wl_resource_for_each(manager_resource, manager_resources) { - if (wl_resource_get_client(manager_resource) == client) { - ext_workspace_manager_v1_send_done(manager_resource); - } - } -} - -static void -handle_output_destroy(struct wl_listener *listener, void *data) -{ - struct group_output *group_output = - wl_container_of(listener, group_output, on.output_destroy); - group_output_destroy(group_output); -} - -static void -handle_group_destroy(struct wl_listener *listener, void *data) -{ - struct group_output *group_output = - wl_container_of(listener, group_output, on.group_destroy); - group_output_destroy(group_output); -} - -/* Internal API*/ -void -ext_group_output_send_initial_state(struct lab_ext_workspace_group *group, - struct wl_resource *group_resource) -{ - struct group_output *group_output; - struct wl_resource *output_resource; - struct wl_client *client = wl_resource_get_client(group_resource); - wl_list_for_each(group_output, &group->outputs, link) { - wl_resource_for_each(output_resource, &group_output->wlr_output->resources) { - if (wl_resource_get_client(output_resource) == client) { - ext_workspace_group_handle_v1_send_output_enter( - group_resource, output_resource); - } - } - } -} - -/* Public API */ -void -lab_ext_workspace_group_output_enter(struct lab_ext_workspace_group *group, - struct wlr_output *wlr_output) -{ - struct group_output *group_output; - wl_list_for_each(group_output, &group->outputs, link) { - if (group_output->wlr_output == wlr_output) { - return; - } - } - group_output = znew(*group_output); - group_output->wlr_output = wlr_output; - group_output->group = group; - - group_output->on.group_destroy.notify = handle_group_destroy; - wl_signal_add(&group->events.destroy, &group_output->on.group_destroy); - - group_output->on.output_bind.notify = handle_output_bind; - wl_signal_add(&wlr_output->events.bind, &group_output->on.output_bind); - - group_output->on.output_destroy.notify = handle_output_destroy; - wl_signal_add(&wlr_output->events.destroy, &group_output->on.output_destroy); - - wl_list_insert(&group->outputs, &group_output->link); - - group_output_send_event( - &group_output->group->resources, - &group_output->wlr_output->resources, - ext_workspace_group_handle_v1_send_output_enter); - - ext_manager_schedule_done_event(group->manager); -} - -void -lab_ext_workspace_group_output_leave(struct lab_ext_workspace_group *group, - struct wlr_output *wlr_output) -{ - struct group_output *tmp; - struct group_output *group_output = NULL; - wl_list_for_each(tmp, &group->outputs, link) { - if (tmp->wlr_output == wlr_output) { - group_output = tmp; - break; - } - } - if (!group_output) { - wlr_log(WLR_ERROR, "output %s was never entered", wlr_output->name); - return; - } - - group_output_destroy(group_output); -} diff --git a/src/protocols/meson.build b/src/protocols/meson.build deleted file mode 100644 index d9d17bf5..00000000 --- a/src/protocols/meson.build +++ /dev/null @@ -1,6 +0,0 @@ -labwc_sources += files( - 'transaction-addon.c', -) - -subdir('cosmic_workspaces') -subdir('ext-workspace') diff --git a/src/protocols/transaction-addon.c b/src/protocols/transaction-addon.c deleted file mode 100644 index 808a2096..00000000 --- a/src/protocols/transaction-addon.c +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include "protocols/transaction-addon.h" -#include -#include -#include "common/list.h" -#include "common/mem.h" - -void -lab_transaction_op_destroy(struct lab_transaction_op *trans_op) -{ - wl_signal_emit_mutable(&trans_op->events.destroy, trans_op); - wl_list_remove(&trans_op->link); - free(trans_op); -} - -static void -transaction_destroy(struct wl_list *list) -{ - struct lab_transaction_op *trans_op, *trans_op_tmp; - wl_list_for_each_safe(trans_op, trans_op_tmp, list, link) { - lab_transaction_op_destroy(trans_op); - } -} - -void -lab_resource_addon_destroy(struct lab_wl_resource_addon *addon) -{ - assert(addon); - assert(addon->ctx); - - addon->ctx->ref_count--; - assert(addon->ctx->ref_count >= 0); - - if (!addon->ctx->ref_count) { - transaction_destroy(&addon->ctx->transaction_ops); - free(addon->ctx); - } - - free(addon); -} - -struct lab_wl_resource_addon * -lab_resource_addon_create(struct lab_transaction_session_context *ctx) -{ - struct lab_wl_resource_addon *addon = znew(*addon); - if (!ctx) { - ctx = znew(*ctx); - wl_list_init(&ctx->transaction_ops); - } - addon->ctx = ctx; - addon->ctx->ref_count++; - return addon; -} - -struct lab_transaction_op * -lab_transaction_op_add(struct lab_transaction_session_context *ctx, - uint32_t pending_change, void *src, void *data) -{ - assert(ctx); - - struct lab_transaction_op *trans_op = znew(*trans_op); - trans_op->change = pending_change; - trans_op->src = src; - trans_op->data = data; - - wl_signal_init(&trans_op->events.destroy); - wl_list_append(&ctx->transaction_ops, &trans_op->link); - - return trans_op; -} diff --git a/src/regions.c b/src/regions.c index 8ee20169..9f195723 100644 --- a/src/regions.c +++ b/src/regions.c @@ -17,16 +17,16 @@ #include "view.h" bool -regions_should_snap(struct server *server) +regions_should_snap(void) { - if (server->input_mode != LAB_INPUT_STATE_MOVE + if (server.input_mode != LAB_INPUT_STATE_MOVE || wl_list_empty(&rc.regions) - || server->seat.region_prevent_snap - || !view_is_floating(server->grabbed_view)) { + || server.seat.region_prevent_snap + || !view_is_floating(server.grabbed_view)) { return false; } - return keyboard_get_all_modifiers(&server->seat); + return keyboard_get_all_modifiers(&server.seat); } struct region * @@ -44,15 +44,14 @@ regions_from_name(const char *region_name, struct output *output) } struct region * -regions_from_cursor(struct server *server) +regions_from_cursor(void) { - assert(server); - double lx = server->seat.cursor->x; - double ly = server->seat.cursor->y; + double lx = server.seat.cursor->x; + double ly = server.seat.cursor->y; struct wlr_output *wlr_output = wlr_output_layout_output_at( - server->output_layout, lx, ly); - struct output *output = output_from_wlr_output(server, wlr_output); + server.output_layout, lx, ly); + struct output *output = output_from_wlr_output(wlr_output); if (!output_is_usable(output)) { return NULL; } @@ -82,7 +81,7 @@ regions_reconfigure_output(struct output *output) /* Evacuate views and destroy current regions */ if (!wl_list_empty(&output->regions)) { regions_evacuate_output(output); - regions_destroy(&output->server->seat, &output->regions); + regions_destroy(&server.seat, &output->regions); } /* Initialize regions from config */ @@ -101,17 +100,17 @@ regions_reconfigure_output(struct output *output) } void -regions_reconfigure(struct server *server) +regions_reconfigure(void) { struct output *output; /* Evacuate views and initialize regions from config */ - wl_list_for_each(output, &server->outputs, link) { + wl_list_for_each(output, &server.outputs, link) { regions_reconfigure_output(output); } /* Tries to match the evacuated views to the new regions */ - desktop_arrange_all_views(server); + desktop_arrange_all_views(); } void @@ -150,7 +149,7 @@ regions_evacuate_output(struct output *output) { assert(output); struct view *view; - wl_list_for_each(view, &output->server->views, link) { + wl_list_for_each(view, &server.views, link) { if (view->tiled_region && view->tiled_region->output == output) { view_evacuate_region(view); } diff --git a/src/resistance.c b/src/resistance.c index 12cbec90..6d34c404 100644 --- a/src/resistance.c +++ b/src/resistance.c @@ -165,7 +165,7 @@ resistance_resize_apply(struct view *view, struct wlr_box *new_geom) edges_initialize(&next_edges); /* Use a constrained, effective geometry for snapping if appropriate */ - enum lab_edge resize_edges = view->server->resize_edges; + enum lab_edge resize_edges = server.resize_edges; struct wlr_box origin = snap_constraints_effective(view, resize_edges, /* use_pending */ false); diff --git a/src/resize-outlines.c b/src/resize-outlines.c index fdb8baa1..113f128d 100644 --- a/src/resize-outlines.c +++ b/src/resize-outlines.c @@ -4,7 +4,7 @@ #include #include "common/border.h" #include "common/lab-scene-rect.h" -#include "labwc.h" +#include "config/rcxml.h" #include "resize-indicator.h" #include "ssd.h" #include "theme.h" @@ -25,9 +25,9 @@ resize_outlines_update(struct view *view, struct wlr_box new_geo) if (!outlines->rect) { struct lab_scene_rect_options opts = { .border_colors = (float *[3]) { - view->server->theme->osd_bg_color, - view->server->theme->osd_label_text_color, - view->server->theme->osd_bg_color, + rc.theme->osd_bg_color, + rc.theme->osd_label_text_color, + rc.theme->osd_bg_color, }, .nr_borders = 3, .border_width = 1, diff --git a/src/scaled-buffer/scaled-buffer.c b/src/scaled-buffer/scaled-buffer.c index 8bef0a75..95ec896b 100644 --- a/src/scaled-buffer/scaled-buffer.c +++ b/src/scaled-buffer/scaled-buffer.c @@ -12,6 +12,7 @@ #include "common/list.h" #include "common/macros.h" #include "common/mem.h" +#include "common/scene-helpers.h" #include "node.h" /* @@ -191,12 +192,7 @@ scaled_buffer_create(struct wlr_scene_tree *parent, assert(impl->create_buffer); struct scaled_buffer *self = znew(*self); - self->scene_buffer = wlr_scene_buffer_create(parent, NULL); - if (!self->scene_buffer) { - wlr_log(WLR_ERROR, "Failed to create scene buffer"); - free(self); - return NULL; - } + self->scene_buffer = lab_wlr_scene_buffer_create(parent, NULL); self->impl = impl; /* diff --git a/src/scaled-buffer/scaled-font-buffer.c b/src/scaled-buffer/scaled-font-buffer.c index bb93fc67..b85a79ff 100644 --- a/src/scaled-buffer/scaled-font-buffer.c +++ b/src/scaled-buffer/scaled-font-buffer.c @@ -26,10 +26,10 @@ _create_buffer(struct scaled_buffer *scaled_buffer, double scale) /* Buffer gets free'd automatically along the backing wlr_buffer */ font_buffer_create(&buffer, self->max_width, self->height, self->text, - &self->font, self->color, bg_pattern, scale); + &self->font, self->color, bg_pattern, scale, self->use_markup); if (!buffer) { - wlr_log(WLR_ERROR, "font_buffer_create() failed"); + wlr_log(WLR_INFO, "font_buffer_create() failed"); } zfree_pattern(solid_bg_pattern); @@ -56,6 +56,7 @@ _equal(struct scaled_buffer *scaled_buffer_a, struct scaled_font_buffer *b = scaled_buffer_b->data; return str_equal(a->text, b->text) + && a->use_markup == b->use_markup && a->max_width == b->max_width && str_equal(a->font.name, b->font.name) && a->font.size == b->font.size @@ -104,10 +105,10 @@ scaled_font_buffer_create_for_titlebar(struct wlr_scene_tree *parent, return self; } -void -scaled_font_buffer_update(struct scaled_font_buffer *self, const char *text, +static void +_scaled_font_buffer_update(struct scaled_font_buffer *self, const char *text, int max_width, struct font *font, const float *color, - const float *bg_color) + const float *bg_color, bool use_markup) { assert(self); assert(text); @@ -120,6 +121,7 @@ scaled_font_buffer_update(struct scaled_font_buffer *self, const char *text, /* Update internal state */ self->text = xstrdup(text); + self->use_markup = use_markup; self->max_width = max_width; if (font->name) { self->font.name = xstrdup(font->name); @@ -139,3 +141,19 @@ scaled_font_buffer_update(struct scaled_font_buffer *self, const char *text, scaled_buffer_request_update(self->scaled_buffer, self->width, self->height); } + +void +scaled_font_buffer_update(struct scaled_font_buffer *self, const char *text, + int max_width, struct font *font, const float *color, + const float *bg_color) +{ + _scaled_font_buffer_update(self, text, max_width, font, color, bg_color, false); +} + +void +scaled_font_buffer_update_markup(struct scaled_font_buffer *self, const char *text, + int max_width, struct font *font, const float *color, + const float *bg_color, bool use_markup) +{ + _scaled_font_buffer_update(self, text, max_width, font, color, bg_color, use_markup); +} diff --git a/src/scaled-buffer/scaled-icon-buffer.c b/src/scaled-buffer/scaled-icon-buffer.c index ceab0509..f4cfb140 100644 --- a/src/scaled-buffer/scaled-icon-buffer.c +++ b/src/scaled-buffer/scaled-icon-buffer.c @@ -59,8 +59,7 @@ img_to_buffer(struct lab_img *img, int width, int height, double scale) static struct lab_data_buffer * load_client_icon(struct scaled_icon_buffer *self, int icon_size, double scale) { - struct lab_img *img = desktop_entry_load_icon(self->server, - self->view_icon_name, icon_size, scale); + struct lab_img *img = desktop_entry_load_icon(self->view_icon_name, icon_size, scale); if (img) { wlr_log(WLR_DEBUG, "loaded icon from client icon name"); return img_to_buffer(img, self->width, self->height, scale); @@ -83,7 +82,7 @@ load_client_icon(struct scaled_icon_buffer *self, int icon_size, double scale) static struct lab_data_buffer * load_server_icon(struct scaled_icon_buffer *self, int icon_size, double scale) { - struct lab_img *img = desktop_entry_load_icon_from_app_id(self->server, + struct lab_img *img = desktop_entry_load_icon_from_app_id( self->view_app_id, icon_size, scale); if (img) { wlr_log(WLR_DEBUG, "loaded icon by app_id"); @@ -106,7 +105,7 @@ _create_buffer(struct scaled_buffer *scaled_buffer, double scale) if (self->icon_name) { /* generic icon (e.g. menu icons) */ - img = desktop_entry_load_icon(self->server, self->icon_name, + img = desktop_entry_load_icon(self->icon_name, icon_size, scale); if (img) { wlr_log(WLR_DEBUG, "loaded icon by icon name"); @@ -136,7 +135,7 @@ _create_buffer(struct scaled_buffer *scaled_buffer, double scale) } } /* If both client and server icons are unavailable, use the fallback icon */ - img = desktop_entry_load_icon(self->server, rc.fallback_app_icon_name, + img = desktop_entry_load_icon(rc.fallback_app_icon_name, icon_size, scale); if (img) { wlr_log(WLR_DEBUG, "loaded fallback icon"); @@ -215,8 +214,7 @@ static struct scaled_buffer_impl impl = { }; struct scaled_icon_buffer * -scaled_icon_buffer_create(struct wlr_scene_tree *parent, struct server *server, - int width, int height) +scaled_icon_buffer_create(struct wlr_scene_tree *parent, int width, int height) { assert(parent); assert(width >= 0 && height >= 0); @@ -226,7 +224,6 @@ scaled_icon_buffer_create(struct wlr_scene_tree *parent, struct server *server, struct scaled_icon_buffer *self = znew(*self); self->scaled_buffer = scaled_buffer; self->scene_buffer = scaled_buffer->scene_buffer; - self->server = server; self->width = width; self->height = height; diff --git a/src/seat.c b/src/seat.c index 8aa395cf..fe85dc26 100644 --- a/src/seat.c +++ b/src/seat.c @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include #include #include @@ -31,6 +31,12 @@ #include "session-lock.h" #include "view.h" +#if WLR_HAS_LIBINPUT_BACKEND + #include +#else + #define wlr_input_device_is_libinput(device) (false) +#endif + static void input_device_destroy(struct wl_listener *listener, void *data) { @@ -48,6 +54,7 @@ input_device_destroy(struct wl_listener *listener, void *data) free(input); } +#if WLR_HAS_LIBINPUT_BACKEND static enum lab_libinput_device_type device_type_from_wlr_device(struct wlr_input_device *wlr_input_device) { @@ -101,6 +108,7 @@ get_category(struct wlr_input_device *device) /* Use default profile as a fallback */ return libinput_category_get_default(); } +#endif static void configure_libinput(struct wlr_input_device *wlr_input_device) @@ -135,7 +143,7 @@ configure_libinput(struct wlr_input_device *wlr_input_device) input->scroll_factor = 1.0; return; } - +#if WLR_HAS_LIBINPUT_BACKEND struct libinput_device *libinput_dev = wlr_libinput_get_device_handle(wlr_input_device); if (!libinput_dev) { @@ -320,6 +328,16 @@ configure_libinput(struct wlr_input_device *wlr_input_device) libinput_device_config_scroll_set_method(libinput_dev, dc->scroll_method); } + libinput_device_config_scroll_set_button(libinput_dev, + libinput_device_config_scroll_get_default_button(libinput_dev)); + if (dc->scroll_button < 0) { + wlr_log(WLR_INFO, "scroll button not configured"); + } else { + wlr_log(WLR_INFO, "scroll button configured (%d)", + dc->scroll_button); + libinput_device_config_scroll_set_button(libinput_dev, dc->scroll_button); + } + libinput_device_config_send_events_set_mode(libinput_dev, libinput_device_config_send_events_get_default_mode(libinput_dev)); if ((dc->send_events_mode != LIBINPUT_CONFIG_SEND_EVENTS_ENABLED @@ -347,14 +365,15 @@ configure_libinput(struct wlr_input_device *wlr_input_device) wlr_log(WLR_INFO, "scroll factor configured (%g)", dc->scroll_factor); input->scroll_factor = dc->scroll_factor; +#endif } static struct wlr_output * -output_by_name(struct server *server, const char *name) +output_by_name(const char *name) { assert(name); struct output *output; - wl_list_for_each(output, &server->outputs, link) { + wl_list_for_each(output, &server.outputs, link) { if (!strcasecmp(output->wlr_output->name, name)) { return output->wlr_output; } @@ -367,7 +386,7 @@ map_input_to_output(struct seat *seat, struct wlr_input_device *dev, char *outpu { struct wlr_output *output = NULL; if (output_name) { - output = output_by_name(seat->server, output_name); + output = output_by_name(output_name); } wlr_cursor_map_input_to_output(seat->cursor, dev, output); wlr_cursor_map_input_to_region(seat->cursor, dev, NULL); @@ -434,7 +453,7 @@ new_keyboard(struct seat *seat, struct wlr_input_device *device, bool is_virtual keyboard_setup_handlers(keyboard); - wlr_seat_set_keyboard(seat->seat, kb); + wlr_seat_set_keyboard(seat->wlr_seat, kb); return (struct input *)keyboard; } @@ -515,7 +534,7 @@ seat_update_capabilities(struct seat *seat) break; } } - wlr_seat_set_capabilities(seat->seat, caps); + wlr_seat_set_capabilities(seat->wlr_seat, caps); } static void @@ -594,7 +613,6 @@ handle_focus_change(struct wl_listener *listener, void *data) { struct seat *seat = wl_container_of(listener, seat, focus_change); struct wlr_seat_keyboard_focus_change_event *event = data; - struct server *server = seat->server; struct wlr_surface *surface = event->new_surface; struct view *view = surface ? view_from_wlr_surface(surface) : NULL; @@ -610,30 +628,29 @@ handle_focus_change(struct wl_listener *listener, void *data) * We clear the keyboard focus at the beginning of Move/Resize, window * switcher and opening menus, but don't want to deactivate the view. */ - if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) { + if (server.input_mode != LAB_INPUT_STATE_PASSTHROUGH) { return; } - if (view != server->active_view) { - if (server->active_view) { - view_set_activated(server->active_view, false); + if (view != server.active_view) { + if (server.active_view) { + view_set_activated(server.active_view, false); } if (view) { view_set_activated(view, true); tablet_pad_enter_surface(seat, surface); } - server->active_view = view; + server.active_view = view; } } void -seat_init(struct server *server) +seat_init(void) { - struct seat *seat = &server->seat; - seat->server = server; + struct seat *seat = &server.seat; - seat->seat = wlr_seat_create(server->wl_display, "seat0"); - if (!seat->seat) { + seat->wlr_seat = wlr_seat_create(server.wl_display, "seat0"); + if (!seat->wlr_seat) { wlr_log(WLR_ERROR, "cannot allocate seat"); exit(EXIT_FAILURE); } @@ -642,15 +659,15 @@ seat_init(struct server *server) wl_list_init(&seat->constraint_commit.link); wl_list_init(&seat->inputs); - CONNECT_SIGNAL(server->backend, seat, new_input); - CONNECT_SIGNAL(&seat->seat->keyboard_state, seat, focus_change); + CONNECT_SIGNAL(server.backend, seat, new_input); + CONNECT_SIGNAL(&seat->wlr_seat->keyboard_state, seat, focus_change); seat->virtual_pointer = wlr_virtual_pointer_manager_v1_create( - server->wl_display); + server.wl_display); CONNECT_SIGNAL(seat->virtual_pointer, seat, new_virtual_pointer); seat->virtual_keyboard = wlr_virtual_keyboard_manager_v1_create( - server->wl_display); + server.wl_display); CONNECT_SIGNAL(seat->virtual_keyboard, seat, new_virtual_keyboard); seat->input_method_relay = input_method_relay_create(seat); @@ -662,7 +679,7 @@ seat_init(struct server *server) wlr_log(WLR_ERROR, "unable to create cursor"); exit(EXIT_FAILURE); } - wlr_cursor_attach_output_layout(seat->cursor, server->output_layout); + wlr_cursor_attach_output_layout(seat->cursor, server.output_layout); wl_list_init(&seat->tablets); wl_list_init(&seat->tablet_tools); @@ -672,9 +689,9 @@ seat_init(struct server *server) } void -seat_finish(struct server *server) +seat_finish(void) { - struct seat *seat = &server->seat; + struct seat *seat = &server.seat; wl_list_remove(&seat->new_input.link); wl_list_remove(&seat->focus_change.link); wl_list_remove(&seat->new_virtual_pointer.link); @@ -708,7 +725,7 @@ configure_keyboard(struct seat *seat, struct input *input) void seat_pointer_end_grab(struct seat *seat, struct wlr_surface *surface) { - if (!surface || !wlr_seat_pointer_has_grab(seat->seat)) { + if (!surface || !wlr_seat_pointer_has_grab(seat->wlr_seat)) { return; } @@ -723,15 +740,15 @@ seat_pointer_end_grab(struct seat *seat, struct wlr_surface *surface) * on button notifications in another client (observed in GTK4), * so end the grab manually. */ - wlr_seat_pointer_end_grab(seat->seat); + wlr_seat_pointer_end_grab(seat->wlr_seat); } } /* This is called on SIGHUP (generally in response to labwc --reconfigure */ void -seat_reconfigure(struct server *server) +seat_reconfigure(void) { - struct seat *seat = &server->seat; + struct seat *seat = &server.seat; struct input *input; cursor_reload(seat); overlay_finish(seat); @@ -762,14 +779,14 @@ seat_reconfigure(struct server *server) void seat_force_focus_surface(struct seat *seat, struct wlr_surface *surface) { - if (seat->server->session_lock_manager->locked) { + if (server.session_lock_manager->locked) { return; } uint32_t *pressed_sent_keycodes = key_state_pressed_sent_keycodes(); int nr_pressed_sent_keycodes = key_state_nr_pressed_sent_keycodes(); struct wlr_keyboard *kb = &seat->keyboard_group->keyboard; - wlr_seat_keyboard_enter(seat->seat, surface, + wlr_seat_keyboard_enter(seat->wlr_seat, surface, pressed_sent_keycodes, nr_pressed_sent_keycodes, &kb->modifiers); } @@ -789,24 +806,23 @@ seat_focus(struct seat *seat, struct wlr_surface *surface, * It should also come before the !surface condition, or the * lock screen may lose focus and become impossible to unlock. */ - struct server *server = seat->server; - if (server->session_lock_manager->locked && !is_lock_surface) { + if (server.session_lock_manager->locked && !is_lock_surface) { return; } if (!surface) { - wlr_seat_keyboard_notify_clear_focus(seat->seat); + wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); input_method_relay_set_focus(seat->input_method_relay, NULL); return; } - if (!wlr_seat_get_keyboard(seat->seat)) { + if (!wlr_seat_get_keyboard(seat->wlr_seat)) { /* * wlr_seat_keyboard_notify_enter() sends wl_keyboard.modifiers, * but it may crash some apps (e.g. Chromium) if * wl_keyboard.keymap is not sent beforehand. */ - wlr_seat_set_keyboard(seat->seat, &seat->keyboard_group->keyboard); + wlr_seat_set_keyboard(seat->wlr_seat, &seat->keyboard_group->keyboard); } /* @@ -820,22 +836,22 @@ seat_focus(struct seat *seat, struct wlr_surface *surface, int nr_pressed_sent_keycodes = key_state_nr_pressed_sent_keycodes(); struct wlr_keyboard *kb = &seat->keyboard_group->keyboard; - wlr_seat_keyboard_notify_enter(seat->seat, surface, + wlr_seat_keyboard_notify_enter(seat->wlr_seat, surface, pressed_sent_keycodes, nr_pressed_sent_keycodes, &kb->modifiers); input_method_relay_set_focus(seat->input_method_relay, surface); struct wlr_pointer_constraint_v1 *constraint = - wlr_pointer_constraints_v1_constraint_for_surface(server->constraints, - surface, seat->seat); - constrain_cursor(server, constraint); + wlr_pointer_constraints_v1_constraint_for_surface(server.constraints, + surface, seat->wlr_seat); + constrain_cursor(constraint); } void seat_focus_surface(struct seat *seat, struct wlr_surface *surface) { /* Don't update focus while window switcher, Move/Resize and menu interaction */ - if (seat->server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) { + if (server.input_mode != LAB_INPUT_STATE_PASSTHROUGH) { return; } seat_focus(seat, surface, /*replace_exclusive_layer*/ false, @@ -854,7 +870,7 @@ seat_set_focus_layer(struct seat *seat, struct wlr_layer_surface_v1 *layer) { if (!layer) { seat->focused_layer = NULL; - desktop_focus_topmost_view(seat->server); + desktop_focus_topmost_view(); return; } seat_focus(seat, layer->surface, /*replace_exclusive_layer*/ true, @@ -897,11 +913,11 @@ seat_focus_override_begin(struct seat *seat, enum input_mode input_mode, enum lab_cursors cursor_shape) { assert(!seat->focus_override.surface); - assert(seat->server->input_mode == LAB_INPUT_STATE_PASSTHROUGH); + assert(server.input_mode == LAB_INPUT_STATE_PASSTHROUGH); - seat->server->input_mode = input_mode; + server.input_mode = input_mode; - seat->focus_override.surface = seat->seat->keyboard_state.focused_surface; + seat->focus_override.surface = seat->wlr_seat->keyboard_state.focused_surface; if (seat->focus_override.surface) { seat->focus_override.surface_destroy.notify = handle_focus_override_surface_destroy; @@ -911,14 +927,14 @@ seat_focus_override_begin(struct seat *seat, enum input_mode input_mode, seat_focus(seat, NULL, /*replace_exclusive_layer*/ false, /*is_lock_surface*/ false); - wlr_seat_pointer_clear_focus(seat->seat); + wlr_seat_pointer_clear_focus(seat->wlr_seat); cursor_set(seat, cursor_shape); } void seat_focus_override_end(struct seat *seat, bool restore_focus) { - seat->server->input_mode = LAB_INPUT_STATE_PASSTHROUGH; + server.input_mode = LAB_INPUT_STATE_PASSTHROUGH; if (seat->focus_override.surface) { if (restore_focus) { @@ -931,6 +947,6 @@ seat_focus_override_end(struct seat *seat, bool restore_focus) } if (restore_focus) { - cursor_update_focus(seat->server); + cursor_update_focus(); } } diff --git a/src/server.c b/src/server.c index 93d95503..d0c8c04c 100644 --- a/src/server.c +++ b/src/server.c @@ -1,22 +1,26 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L #include "config.h" +#include #include #include #include #include #include +#include #include #include +#include +#include #include #include #include -#include #include #include #include #include #include +#include #include #include #include @@ -40,12 +44,17 @@ #include #if HAVE_XWAYLAND -#include -#include "xwayland-shell-v1-protocol.h" + #include +#endif + +#if WLR_HAS_DRM_BACKEND + #include #endif #include "action.h" #include "common/macros.h" +#include "common/mem.h" +#include "common/scene-helpers.h" #include "config/rcxml.h" #include "config/session.h" #include "decorations.h" @@ -76,45 +85,49 @@ #define LAB_WLR_PRESENTATION_TIME_VERSION 2 static void -reload_config_and_theme(struct server *server) +reload_config_and_theme(void) { /* Avoid UAF when dialog client is used during reconfigure */ action_prompts_destroy(); + /* + * Cancel any pending auto-raise before reloading config in case the + * raiseOnFocusDelay option was disabled or changed. + */ + desktop_cancel_pending_auto_raise(); + scaled_buffer_invalidate_sharing(); rcxml_finish(); rcxml_read(rc.config_file); - theme_finish(server->theme); - theme_init(server->theme, server, rc.theme_name); + theme_finish(rc.theme); + theme_init(rc.theme, rc.theme_name); #if HAVE_LIBSFDO - desktop_entry_finish(server); - desktop_entry_init(server); + desktop_entry_finish(); + desktop_entry_init(); #endif struct view *view; - wl_list_for_each(view, &server->views, link) { + wl_list_for_each(view, &server.views, link) { view_reload_ssd(view); } - cycle_finish(server, /*switch_focus*/ false); - menu_reconfigure(server); - seat_reconfigure(server); - regions_reconfigure(server); - resize_indicator_reconfigure(server); + cycle_finish(/*switch_focus*/ false); + menu_reconfigure(); + seat_reconfigure(); + regions_reconfigure(); + resize_indicator_reconfigure(); kde_server_decoration_update_default(); - workspaces_reconfigure(server); + workspaces_reconfigure(); } static int handle_sighup(int signal, void *data) { - struct server *server = data; - - keyboard_cancel_all_keybind_repeats(&server->seat); + keyboard_cancel_all_keybind_repeats(&server.seat); session_environment_init(); - reload_config_and_theme(server); - output_virtual_update_fallback(server); + reload_config_and_theme(); + output_virtual_update_fallback(); return 0; } @@ -132,7 +145,6 @@ handle_sigchld(int signal, void *data) { siginfo_t info; info.si_pid = 0; - struct server *server = data; /* First call waitid() with NOWAIT which doesn't consume the zombie */ if (waitid(P_ALL, /*id*/ 0, &info, WEXITED | WNOHANG | WNOWAIT) == -1) { @@ -146,8 +158,8 @@ handle_sigchld(int signal, void *data) #if HAVE_XWAYLAND /* Ensure that we do not break xwayland lazy initialization */ - if (server->xwayland && server->xwayland->server - && info.si_pid == server->xwayland->server->pid) { + if (server.xwayland && server.xwayland->server + && info.si_pid == server.xwayland->server->pid) { return 0; } #endif @@ -185,14 +197,15 @@ handle_sigchld(int signal, void *data) " please report", (long)info.si_pid, info.si_code); } - if (info.si_pid == server->primary_client_pid) { + if (info.si_pid == server.primary_client_pid) { wlr_log(WLR_INFO, "primary client %ld exited", (long)info.si_pid); - wl_display_terminate(server->wl_display); + wl_display_terminate(server.wl_display); } return 0; } +#if WLR_HAS_DRM_BACKEND static void handle_drm_lease_request(struct wl_listener *listener, void *data) { @@ -204,40 +217,7 @@ handle_drm_lease_request(struct wl_listener *listener, void *data) return; } } - -static bool -protocol_is_privileged(const struct wl_interface *iface) -{ - static const char * const rejected[] = { - "wp_drm_lease_device_v1", - "zwlr_gamma_control_manager_v1", - "zwlr_output_manager_v1", - "zwlr_output_power_manager_v1", - "zwp_input_method_manager_v2", - "zwlr_virtual_pointer_manager_v1", - "zwp_virtual_keyboard_manager_v1", - "zwlr_export_dmabuf_manager_v1", - "zwlr_screencopy_manager_v1", - "ext_data_control_manager_v1", - "zwlr_data_control_manager_v1", - "wp_security_context_manager_v1", - "ext_idle_notifier_v1", - "zcosmic_workspace_manager_v1", - "zwlr_foreign_toplevel_manager_v1", - "ext_foreign_toplevel_list_v1", - "ext_session_lock_manager_v1", - "zwlr_layer_shell_v1", - "ext_workspace_manager_v1", - "ext_image_copy_capture_manager_v1", - "ext_output_image_capture_source_manager_v1", - }; - for (size_t i = 0; i < ARRAY_SIZE(rejected); i++) { - if (!strcmp(iface->name, rejected[i])) { - return true; - } - } - return false; -} +#endif static bool allow_for_sandbox(const struct wlr_security_context_v1_state *security_state, @@ -256,6 +236,7 @@ allow_for_sandbox(const struct wlr_security_context_v1_state *security_state, "wl_data_device_manager", /* would be great if we could drop this one */ "wl_seat", "xdg_wm_base", + "wl_fixes", /* enhanced */ "wl_output", "wl_drm", @@ -277,9 +258,11 @@ allow_for_sandbox(const struct wlr_security_context_v1_state *security_state, "zxdg_importer_v1", "zxdg_importer_v2", "xdg_toplevel_icon_manager_v1", - "xdg_dialog_v1", + "xdg_wm_dialog_v1", /* plus */ "wp_alpha_modifier_v1", + "wp_color_manager_v1", + "wp_color_representation_manager_v1", "wp_linux_drm_syncobj_manager_v1", "zxdg_exporter_v1", "zxdg_exporter_v2", @@ -300,26 +283,34 @@ static bool server_global_filter(const struct wl_client *client, const struct wl_global *global, void *data) { const struct wl_interface *iface = wl_global_get_interface(global); - struct server *server = (struct server *)data; /* Silence unused var compiler warnings */ - (void)iface; (void)server; + (void)iface; #if HAVE_XWAYLAND - struct wl_client *xwayland_client = (server->xwayland && server->xwayland->server) - ? server->xwayland->server->client + struct wl_client *xwayland_client = (server.xwayland && server.xwayland->server) + ? server.xwayland->server->client : NULL; - if (client != xwayland_client && !strcmp(iface->name, xwayland_shell_v1_interface.name)) { + /* + * The interface name `xwayland_shell_v1_interface.name` is hard-coded + * here to avoid generating and including `xwayland-shell-v1-protocol.h` + */ + if (client != xwayland_client && !strcmp(iface->name, "xwayland_shell_v1")) { /* Filter out the xwayland shell for usual clients */ return false; } #endif + uint32_t iface_id = parse_privileged_interface(iface->name); + if (iface_id && !(iface_id & rc.allowed_interfaces)) { + return false; + } + /* Do not allow security_context_manager_v1 to clients with a security context attached */ const struct wlr_security_context_v1_state *security_context = wlr_security_context_manager_v1_lookup_client( - server->security_context_manager_v1, (struct wl_client *)client); - if (security_context && global == server->security_context_manager_v1->global) { + server.security_context_manager_v1, (struct wl_client *)client); + if (security_context && global == server.security_context_manager_v1->global) { return false; } else if (security_context) { /* @@ -330,11 +321,11 @@ server_global_filter(const struct wl_client *client, const struct wl_global *glo /* * TODO: The following call is basically useless right now * and should be replaced with - * assert(allow || protocol_is_privileged(iface)); + * assert(allow || iface_id); * This ensures that our lists are in sync with what * protocols labwc supports. */ - if (!allow && !protocol_is_privileged(iface)) { + if (!allow && !iface_id) { wlr_log(WLR_ERROR, "Blocking unknown protocol %s", iface->name); } else if (!allow) { wlr_log(WLR_DEBUG, "Blocking %s for security context %s->%s->%s", @@ -380,41 +371,39 @@ get_headless_backend(struct wlr_backend *backend, void *data) static void handle_renderer_lost(struct wl_listener *listener, void *data) { - struct server *server = wl_container_of(listener, server, renderer_lost); - wlr_log(WLR_INFO, "Re-creating renderer after GPU reset"); - struct wlr_renderer *renderer = wlr_renderer_autocreate(server->backend); + struct wlr_renderer *renderer = wlr_renderer_autocreate(server.backend); if (!renderer) { wlr_log(WLR_ERROR, "Unable to create renderer"); return; } struct wlr_allocator *allocator = - wlr_allocator_autocreate(server->backend, renderer); + wlr_allocator_autocreate(server.backend, renderer); if (!allocator) { wlr_log(WLR_ERROR, "Unable to create allocator"); wlr_renderer_destroy(renderer); return; } - struct wlr_renderer *old_renderer = server->renderer; - struct wlr_allocator *old_allocator = server->allocator; - server->renderer = renderer; - server->allocator = allocator; + struct wlr_renderer *old_renderer = server.renderer; + struct wlr_allocator *old_allocator = server.allocator; + server.renderer = renderer; + server.allocator = allocator; - wl_list_remove(&server->renderer_lost.link); - wl_signal_add(&server->renderer->events.lost, &server->renderer_lost); + wl_list_remove(&server.renderer_lost.link); + wl_signal_add(&server.renderer->events.lost, &server.renderer_lost); - wlr_compositor_set_renderer(server->compositor, renderer); + wlr_compositor_set_renderer(server.compositor, renderer); struct output *output; - wl_list_for_each(output, &server->outputs, link) { + wl_list_for_each(output, &server.outputs, link) { wlr_output_init_render(output->wlr_output, - server->allocator, server->renderer); + server.allocator, server.renderer); } - reload_config_and_theme(server); + reload_config_and_theme(); magnifier_reset(); @@ -422,30 +411,65 @@ handle_renderer_lost(struct wl_listener *listener, void *data) wlr_renderer_destroy(old_renderer); } -void -server_init(struct server *server) +static void +handle_toplevel_capture_source_destroy(struct wl_listener *listener, void *data) { - server->primary_client_pid = -1; - server->wl_display = wl_display_create(); - if (!server->wl_display) { + struct view *view = wl_container_of(listener, view, capture.on_capture_source_destroy); + assert(view->capture.source); + view->capture.source = NULL; + wl_list_remove(&listener->link); + wl_list_init(&listener->link); +} + +static void +handle_toplevel_capture_request(struct wl_listener *listener, void *data) +{ + struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request *request = data; + struct view *view = request->toplevel_handle->data; + assert(view); + wlr_log(WLR_INFO, "Capturing toplevel %s", view->app_id); + + if (!view->capture.source) { + view->capture.source = wlr_ext_image_capture_source_v1_create_with_scene_node( + &view->capture.scene->tree.node, server.wl_event_loop, + server.allocator, server.renderer); + assert(view->capture.source); + + view->capture.on_capture_source_destroy.notify = + handle_toplevel_capture_source_destroy; + wl_signal_add(&view->capture.source->events.destroy, + &view->capture.on_capture_source_destroy); + } + wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request_accept( + request, view->capture.source); +} + +void +server_init(void) +{ + server.primary_client_pid = -1; + server.wl_display = wl_display_create(); + if (!server.wl_display) { wlr_log(WLR_ERROR, "cannot allocate a wayland display"); exit(EXIT_FAILURE); } /* Increase max client buffer size to make slow clients less likely to terminate */ - wl_display_set_default_max_buffer_size(server->wl_display, 1024 * 1024); + wl_display_set_default_max_buffer_size(server.wl_display, 1024 * 1024); - wl_display_set_global_filter(server->wl_display, server_global_filter, server); - server->wl_event_loop = wl_display_get_event_loop(server->wl_display); + wl_display_set_global_filter(server.wl_display, server_global_filter, NULL); + server.wl_event_loop = wl_display_get_event_loop(server.wl_display); + + wlr_fixes_create(server.wl_display, 1); /* Catch signals */ - server->sighup_source = wl_event_loop_add_signal( - server->wl_event_loop, SIGHUP, handle_sighup, server); - server->sigint_source = wl_event_loop_add_signal( - server->wl_event_loop, SIGINT, handle_sigterm, server->wl_display); - server->sigterm_source = wl_event_loop_add_signal( - server->wl_event_loop, SIGTERM, handle_sigterm, server->wl_display); - server->sigchld_source = wl_event_loop_add_signal( - server->wl_event_loop, SIGCHLD, handle_sigchld, server); + server.sighup_source = wl_event_loop_add_signal( + server.wl_event_loop, SIGHUP, handle_sighup, NULL); + server.sigint_source = wl_event_loop_add_signal( + server.wl_event_loop, SIGINT, handle_sigterm, server.wl_display); + server.sigterm_source = wl_event_loop_add_signal( + server.wl_event_loop, SIGTERM, handle_sigterm, server.wl_display); + server.sigchld_source = wl_event_loop_add_signal( + server.wl_event_loop, SIGCHLD, handle_sigchld, NULL); /* * Prevent wayland clients that request the X11 clipboard but closing @@ -463,31 +487,31 @@ server_init(struct server *server) * backend based on the current environment, such as opening an x11 * window if an x11 server is running. */ - server->backend = wlr_backend_autocreate( - server->wl_event_loop, &server->session); - if (!server->backend) { + server.backend = wlr_backend_autocreate( + server.wl_event_loop, &server.session); + if (!server.backend) { wlr_log(WLR_ERROR, "unable to create backend"); fprintf(stderr, helpful_seat_error_message); exit(EXIT_FAILURE); } /* Create headless backend to enable adding virtual outputs later on */ - wlr_multi_for_each_backend(server->backend, - get_headless_backend, &server->headless.backend); + wlr_multi_for_each_backend(server.backend, + get_headless_backend, &server.headless.backend); - if (!server->headless.backend) { + if (!server.headless.backend) { wlr_log(WLR_DEBUG, "manually creating headless backend"); - server->headless.backend = wlr_headless_backend_create( - server->wl_event_loop); + server.headless.backend = wlr_headless_backend_create( + server.wl_event_loop); } else { wlr_log(WLR_DEBUG, "headless backend already exists"); } - if (!server->headless.backend) { + if (!server.headless.backend) { wlr_log(WLR_ERROR, "unable to create headless backend"); exit(EXIT_FAILURE); } - wlr_multi_backend_add(server->backend, server->headless.backend); + wlr_multi_backend_add(server.backend, server.headless.backend); /* * If we don't populate headless backend with a virtual output (that we @@ -495,7 +519,7 @@ server_init(struct server *server) * later do not work properly when overlaid on real output. Content is * drawn on the virtual output, but not drawn on the real output. */ - wlr_output_destroy(wlr_headless_add_output(server->headless.backend, 0, 0)); + wlr_output_destroy(wlr_headless_add_output(server.headless.backend, 0, 0)); /* * Autocreates a renderer, either Pixman, GLES2 or Vulkan for us. The @@ -503,38 +527,38 @@ server_init(struct server *server) * The renderer is responsible for defining the various pixel formats it * supports for shared memory, this configures that for clients. */ - server->renderer = wlr_renderer_autocreate(server->backend); - if (!server->renderer) { + server.renderer = wlr_renderer_autocreate(server.backend); + if (!server.renderer) { wlr_log(WLR_ERROR, "unable to create renderer"); exit(EXIT_FAILURE); } - server->renderer_lost.notify = handle_renderer_lost; - wl_signal_add(&server->renderer->events.lost, &server->renderer_lost); + server.renderer_lost.notify = handle_renderer_lost; + wl_signal_add(&server.renderer->events.lost, &server.renderer_lost); - if (!wlr_renderer_init_wl_shm(server->renderer, server->wl_display)) { + if (!wlr_renderer_init_wl_shm(server.renderer, server.wl_display)) { wlr_log(WLR_ERROR, "Failed to initialize shared memory pool"); exit(EXIT_FAILURE); } if (wlr_renderer_get_texture_formats( - server->renderer, WLR_BUFFER_CAP_DMABUF)) { - if (wlr_renderer_get_drm_fd(server->renderer) >= 0) { - wlr_drm_create(server->wl_display, server->renderer); + server.renderer, WLR_BUFFER_CAP_DMABUF)) { + if (wlr_renderer_get_drm_fd(server.renderer) >= 0) { + wlr_drm_create(server.wl_display, server.renderer); } - server->linux_dmabuf = wlr_linux_dmabuf_v1_create_with_renderer( - server->wl_display, + server.linux_dmabuf = wlr_linux_dmabuf_v1_create_with_renderer( + server.wl_display, LAB_WLR_LINUX_DMABUF_VERSION, - server->renderer); + server.renderer); } else { wlr_log(WLR_DEBUG, "unable to initialize dmabuf"); } - if (wlr_renderer_get_drm_fd(server->renderer) >= 0 && - server->renderer->features.timeline && - server->backend->features.timeline) { - wlr_linux_drm_syncobj_manager_v1_create(server->wl_display, 1, - wlr_renderer_get_drm_fd(server->renderer)); + if (wlr_renderer_get_drm_fd(server.renderer) >= 0 && + server.renderer->features.timeline && + server.backend->features.timeline) { + wlr_linux_drm_syncobj_manager_v1_create(server.wl_display, 1, + wlr_renderer_get_drm_fd(server.renderer)); } /* @@ -542,24 +566,22 @@ server_init(struct server *server) * the renderer and the backend. It handles the buffer creation, * allowing wlroots to render onto the screen */ - server->allocator = wlr_allocator_autocreate( - server->backend, server->renderer); - if (!server->allocator) { + server.allocator = wlr_allocator_autocreate( + server.backend, server.renderer); + if (!server.allocator) { wlr_log(WLR_ERROR, "unable to create allocator"); exit(EXIT_FAILURE); } - wl_list_init(&server->views); - wl_list_init(&server->unmanaged_surfaces); - wl_list_init(&server->cycle.views); - wl_list_init(&server->cycle.osd_outputs); + wl_list_init(&server.views); + wl_list_init(&server.unmanaged_surfaces); + wl_list_init(&server.cycle.views); + wl_list_init(&server.cycle.osd_outputs); - server->scene = wlr_scene_create(); - if (!server->scene) { - wlr_log(WLR_ERROR, "unable to create scene"); - exit(EXIT_FAILURE); - } - server->direct_scanout_enabled = server->scene->WLR_PRIVATE.direct_scanout; + server.scene = wlr_scene_create(); + die_if_null(server.scene); + + server.direct_scanout_enabled = server.scene->WLR_PRIVATE.direct_scanout; /* * The order in which the scene-trees below are created determines the @@ -570,14 +592,14 @@ server_init(struct server *server) * | ---------------------------------- | ------------------------------------- * | output->session_lock_tree | session lock surfaces (e.g. swaylock) * | output->cycle_osd_tree | window switcher's on-screen display - * | server->cycle_preview_tree | window switcher's previewed window - * | server->menu_tree | labwc's server-side menus + * | server.cycle_preview_tree | window switcher's previewed window + * | server.menu_tree | labwc's server-side menus * | output->layer_popup_tree | xdg popups on layer surfaces * | output->layer_tree[3] | overlay layer surfaces (e.g. rofi) * | output->layer_tree[2] | top layer surfaces (e.g. waybar) - * | server->unmanaged_tree | unmanaged X11 surfaces (e.g. dmenu) - * | server->xdg_popup_tree | xdg popups on xdg windows - * | server->workspace_tree | + * | server.unmanaged_tree | unmanaged X11 surfaces (e.g. dmenu) + * | server.xdg_popup_tree | xdg popups on xdg windows + * | server.workspace_tree | * | + workspace->tree | * | + workspace->view_trees[1] | always-on-top xdg/X11 windows * | + workspace->view_trees[0] | normal xdg/X11 windows (e.g. firefox) @@ -586,17 +608,60 @@ server_init(struct server *server) * | output->layer_tree[0] | background layer surfaces (e.g. swaybg) */ - server->workspace_tree = wlr_scene_tree_create(&server->scene->tree); - server->xdg_popup_tree = wlr_scene_tree_create(&server->scene->tree); + if (server.renderer->features.input_color_transform) { + const enum wp_color_manager_v1_render_intent render_intents[] = { + WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL, + }; + size_t transfer_functions_len = 0; + enum wp_color_manager_v1_transfer_function *transfer_functions = + wlr_color_manager_v1_transfer_function_list_from_renderer( + server.renderer, &transfer_functions_len); + + size_t primaries_len = 0; + enum wp_color_manager_v1_primaries *primaries = + wlr_color_manager_v1_primaries_list_from_renderer( + server.renderer, &primaries_len); + + struct wlr_color_manager_v1 *cm = wlr_color_manager_v1_create( + server.wl_display, 2, &(struct wlr_color_manager_v1_options){ + .features = { + .parametric = true, + .set_mastering_display_primaries = true, + }, + .render_intents = render_intents, + .render_intents_len = ARRAY_SIZE(render_intents), + .transfer_functions = transfer_functions, + .transfer_functions_len = transfer_functions_len, + .primaries = primaries, + .primaries_len = primaries_len, + }); + + free(transfer_functions); + free(primaries); + + if (cm) { + wlr_scene_set_color_manager_v1(server.scene, cm); + } else { + wlr_log(WLR_ERROR, "unable to create color manager"); + } + } + + wlr_color_representation_manager_v1_create_with_renderer( + server.wl_display, 1, server.renderer); + + server.workspace_tree = lab_wlr_scene_tree_create(&server.scene->tree); + server.xdg_popup_tree = lab_wlr_scene_tree_create(&server.scene->tree); #if HAVE_XWAYLAND - server->unmanaged_tree = wlr_scene_tree_create(&server->scene->tree); + // Creating/setting this is harmless when xwayland support is built-in + // but xwayland could not be successfully started. + server.unmanaged_tree = lab_wlr_scene_tree_create(&server.scene->tree); #endif - server->menu_tree = wlr_scene_tree_create(&server->scene->tree); - server->cycle_preview_tree = wlr_scene_tree_create(&server->scene->tree); + server.menu_tree = lab_wlr_scene_tree_create(&server.scene->tree); + server.cycle_preview_tree = lab_wlr_scene_tree_create(&server.scene->tree); - workspaces_init(server); + workspaces_init(); - output_init(server); + output_init(); /* * Create some hands-off wlroots interfaces. The compositor is @@ -605,16 +670,16 @@ server_init(struct server *server) * room for you to dig your fingers in and play with their behavior if * you want. */ - server->compositor = wlr_compositor_create(server->wl_display, - LAB_WLR_COMPOSITOR_VERSION, server->renderer); - if (!server->compositor) { + server.compositor = wlr_compositor_create(server.wl_display, + LAB_WLR_COMPOSITOR_VERSION, server.renderer); + if (!server.compositor) { wlr_log(WLR_ERROR, "unable to create the wlroots compositor"); exit(EXIT_FAILURE); } - wlr_subcompositor_create(server->wl_display); + wlr_subcompositor_create(server.wl_display); struct wlr_data_device_manager *device_manager = NULL; - device_manager = wlr_data_device_manager_create(server->wl_display); + device_manager = wlr_data_device_manager_create(server.wl_display); if (!device_manager) { wlr_log(WLR_ERROR, "unable to create data device manager"); exit(EXIT_FAILURE); @@ -631,111 +696,126 @@ server_init(struct server *server) * https://wayfire.org/2020/08/04/Wayfire-0-5.html */ if (rc.primary_selection) { - wlr_primary_selection_v1_device_manager_create(server->wl_display); + wlr_primary_selection_v1_device_manager_create(server.wl_display); } - server->input_method_manager = wlr_input_method_manager_v2_create( - server->wl_display); - server->text_input_manager = wlr_text_input_manager_v3_create( - server->wl_display); - seat_init(server); - xdg_shell_init(server); - kde_server_decoration_init(server); - xdg_server_decoration_init(server); + server.input_method_manager = wlr_input_method_manager_v2_create( + server.wl_display); + server.text_input_manager = wlr_text_input_manager_v3_create( + server.wl_display); + seat_init(); + xdg_shell_init(); + kde_server_decoration_init(); + xdg_server_decoration_init(); struct wlr_presentation *presentation = wlr_presentation_create( - server->wl_display, server->backend, + server.wl_display, server.backend, LAB_WLR_PRESENTATION_TIME_VERSION); if (!presentation) { wlr_log(WLR_ERROR, "unable to create presentation interface"); exit(EXIT_FAILURE); } - if (server->linux_dmabuf) { - wlr_scene_set_linux_dmabuf_v1(server->scene, server->linux_dmabuf); + if (server.linux_dmabuf) { + wlr_scene_set_linux_dmabuf_v1(server.scene, server.linux_dmabuf); } - wlr_export_dmabuf_manager_v1_create(server->wl_display); - wlr_screencopy_manager_v1_create(server->wl_display); - wlr_ext_image_copy_capture_manager_v1_create(server->wl_display, 1); - wlr_ext_output_image_capture_source_manager_v1_create(server->wl_display, 1); - wlr_data_control_manager_v1_create(server->wl_display); - wlr_ext_data_control_manager_v1_create(server->wl_display, + wlr_export_dmabuf_manager_v1_create(server.wl_display); + wlr_screencopy_manager_v1_create(server.wl_display); + wlr_ext_image_copy_capture_manager_v1_create(server.wl_display, 1); + wlr_ext_output_image_capture_source_manager_v1_create(server.wl_display, 1); + + server.toplevel_capture.manager = + wlr_ext_foreign_toplevel_image_capture_source_manager_v1_create( + server.wl_display, 1); + if (server.toplevel_capture.manager) { + server.toplevel_capture.on.new_request.notify = handle_toplevel_capture_request; + wl_signal_add(&server.toplevel_capture.manager->events.new_request, + &server.toplevel_capture.on.new_request); + } else { + /* Allow safe removal on shutdown */ + wl_list_init(&server.toplevel_capture.on.new_request.link); + } + + wlr_data_control_manager_v1_create(server.wl_display); + wlr_ext_data_control_manager_v1_create(server.wl_display, LAB_EXT_DATA_CONTROL_VERSION); - server->security_context_manager_v1 = - wlr_security_context_manager_v1_create(server->wl_display); - wlr_viewporter_create(server->wl_display); - wlr_single_pixel_buffer_manager_v1_create(server->wl_display); - wlr_fractional_scale_manager_v1_create(server->wl_display, + server.security_context_manager_v1 = + wlr_security_context_manager_v1_create(server.wl_display); + wlr_viewporter_create(server.wl_display); + wlr_single_pixel_buffer_manager_v1_create(server.wl_display); + wlr_fractional_scale_manager_v1_create(server.wl_display, LAB_WLR_FRACTIONAL_SCALE_V1_VERSION); - idle_manager_create(server->wl_display); + idle_manager_create(server.wl_display); - server->relative_pointer_manager = wlr_relative_pointer_manager_v1_create( - server->wl_display); - server->constraints = wlr_pointer_constraints_v1_create( - server->wl_display); + server.relative_pointer_manager = wlr_relative_pointer_manager_v1_create( + server.wl_display); + server.constraints = wlr_pointer_constraints_v1_create( + server.wl_display); - server->new_constraint.notify = create_constraint; - wl_signal_add(&server->constraints->events.new_constraint, - &server->new_constraint); + server.new_constraint.notify = create_constraint; + wl_signal_add(&server.constraints->events.new_constraint, + &server.new_constraint); - server->foreign_toplevel_manager = - wlr_foreign_toplevel_manager_v1_create(server->wl_display); + server.foreign_toplevel_manager = + wlr_foreign_toplevel_manager_v1_create(server.wl_display); - server->foreign_toplevel_list = + server.foreign_toplevel_list = wlr_ext_foreign_toplevel_list_v1_create( - server->wl_display, LAB_EXT_FOREIGN_TOPLEVEL_LIST_VERSION); + server.wl_display, LAB_EXT_FOREIGN_TOPLEVEL_LIST_VERSION); - wlr_alpha_modifier_v1_create(server->wl_display); + wlr_alpha_modifier_v1_create(server.wl_display); - session_lock_init(server); + session_lock_init(); - server->drm_lease_manager = wlr_drm_lease_v1_manager_create( - server->wl_display, server->backend); - if (server->drm_lease_manager) { - server->drm_lease_request.notify = handle_drm_lease_request; - wl_signal_add(&server->drm_lease_manager->events.request, - &server->drm_lease_request); +#if WLR_HAS_DRM_BACKEND + server.drm_lease_manager = wlr_drm_lease_v1_manager_create( + server.wl_display, server.backend); + if (server.drm_lease_manager) { + server.drm_lease_request.notify = handle_drm_lease_request; + wl_signal_add(&server.drm_lease_manager->events.request, + &server.drm_lease_request); } else { wlr_log(WLR_DEBUG, "Failed to create wlr_drm_lease_device_v1"); wlr_log(WLR_INFO, "VR will not be available"); } +#endif - server->output_power_manager_v1 = - wlr_output_power_manager_v1_create(server->wl_display); - server->output_power_manager_set_mode.notify = + server.output_power_manager_v1 = + wlr_output_power_manager_v1_create(server.wl_display); + server.output_power_manager_set_mode.notify = handle_output_power_manager_set_mode; - wl_signal_add(&server->output_power_manager_v1->events.set_mode, - &server->output_power_manager_set_mode); + wl_signal_add(&server.output_power_manager_v1->events.set_mode, + &server.output_power_manager_set_mode); - server->tearing_control = wlr_tearing_control_manager_v1_create(server->wl_display, 1); - server->tearing_new_object.notify = handle_tearing_new_object; - wl_signal_add(&server->tearing_control->events.new_object, &server->tearing_new_object); + server.tearing_control = wlr_tearing_control_manager_v1_create(server.wl_display, 1); + server.tearing_new_object.notify = handle_tearing_new_object; + wl_signal_add(&server.tearing_control->events.new_object, &server.tearing_new_object); - server->tablet_manager = wlr_tablet_v2_create(server->wl_display); + server.tablet_manager = wlr_tablet_v2_create(server.wl_display); - layers_init(server); + layers_init(); /* These get cleaned up automatically on display destroy */ struct wlr_xdg_foreign_registry *registry = - wlr_xdg_foreign_registry_create(server->wl_display); - wlr_xdg_foreign_v1_create(server->wl_display, registry); - wlr_xdg_foreign_v2_create(server->wl_display, registry); + wlr_xdg_foreign_registry_create(server.wl_display); + wlr_xdg_foreign_v1_create(server.wl_display, registry); + wlr_xdg_foreign_v2_create(server.wl_display, registry); #if HAVE_LIBSFDO - desktop_entry_init(server); + desktop_entry_init(); #endif #if HAVE_XWAYLAND - xwayland_server_init(server, server->compositor); + xwayland_server_init(server.compositor); #endif } void -server_start(struct server *server) +server_start(void) { /* Add a Unix socket to the Wayland display. */ - const char *socket = wl_display_add_socket_auto(server->wl_display); + const char *socket = wl_display_add_socket_auto(server.wl_display); if (!socket) { wlr_log_errno(WLR_ERROR, "unable to open wayland socket"); exit(EXIT_FAILURE); @@ -745,13 +825,13 @@ server_start(struct server *server) * Start the backend. This will enumerate outputs and inputs, become * the DRM master, etc */ - if (!wlr_backend_start(server->backend)) { + if (!wlr_backend_start(server.backend)) { wlr_log(WLR_ERROR, "unable to start the wlroots backend"); exit(EXIT_FAILURE); } /* Potentially set up the initial fallback output */ - output_virtual_update_fallback(server); + output_virtual_update_fallback(); if (setenv("WAYLAND_DISPLAY", socket, true) < 0) { wlr_log_errno(WLR_ERROR, "unable to set WAYLAND_DISPLAY"); @@ -761,43 +841,45 @@ server_start(struct server *server) } void -server_finish(struct server *server) +server_finish(void) { #if HAVE_XWAYLAND - xwayland_server_finish(server); + xwayland_server_finish(); #endif #if HAVE_LIBSFDO - desktop_entry_finish(server); + desktop_entry_finish(); #endif - wl_event_source_remove(server->sighup_source); - wl_event_source_remove(server->sigint_source); - wl_event_source_remove(server->sigterm_source); - wl_event_source_remove(server->sigchld_source); + wl_event_source_remove(server.sighup_source); + wl_event_source_remove(server.sigint_source); + wl_event_source_remove(server.sigterm_source); + wl_event_source_remove(server.sigchld_source); - wl_display_destroy_clients(server->wl_display); + wl_display_destroy_clients(server.wl_display); - seat_finish(server); - output_finish(server); - xdg_shell_finish(server); - layers_finish(server); - kde_server_decoration_finish(server); - xdg_server_decoration_finish(server); - wl_list_remove(&server->new_constraint.link); - wl_list_remove(&server->output_power_manager_set_mode.link); - wl_list_remove(&server->tearing_new_object.link); - if (server->drm_lease_request.notify) { - wl_list_remove(&server->drm_lease_request.link); - server->drm_lease_request.notify = NULL; + seat_finish(); + output_finish(); + xdg_shell_finish(); + layers_finish(); + kde_server_decoration_finish(); + xdg_server_decoration_finish(); + wl_list_remove(&server.new_constraint.link); + wl_list_remove(&server.output_power_manager_set_mode.link); + wl_list_remove(&server.tearing_new_object.link); + if (server.drm_lease_request.notify) { + wl_list_remove(&server.drm_lease_request.link); + server.drm_lease_request.notify = NULL; } - wlr_backend_destroy(server->backend); - wlr_allocator_destroy(server->allocator); + wl_list_remove(&server.toplevel_capture.on.new_request.link); - wl_list_remove(&server->renderer_lost.link); - wlr_renderer_destroy(server->renderer); + wlr_backend_destroy(server.backend); + wlr_allocator_destroy(server.allocator); - workspaces_destroy(server); - wlr_scene_node_destroy(&server->scene->tree.node); + wl_list_remove(&server.renderer_lost.link); + wlr_renderer_destroy(server.renderer); - wl_display_destroy(server->wl_display); + workspaces_destroy(); + wlr_scene_node_destroy(&server.scene->tree.node); + + wl_display_destroy(server.wl_display); } diff --git a/src/session-lock.c b/src/session-lock.c index a370241e..4e5eb8ab 100644 --- a/src/session-lock.c +++ b/src/session-lock.c @@ -6,6 +6,7 @@ #include #include #include "common/mem.h" +#include "common/scene-helpers.h" #include "labwc.h" #include "node.h" #include "output.h" @@ -30,7 +31,7 @@ static void focus_surface(struct session_lock_manager *manager, struct wlr_surface *focused) { manager->focused = focused; - seat_focus_lock_surface(&manager->server->seat, focused); + seat_focus_lock_surface(&server.seat, focused); } static void @@ -58,7 +59,7 @@ static void update_focus(void *data) { struct session_lock_output *output = data; - cursor_update_focus(output->manager->server); + cursor_update_focus(); if (!output->manager->focused) { focus_surface(output->manager, output->surface->surface); } @@ -77,7 +78,7 @@ handle_surface_map(struct wl_listener *listener, void *data) * buffer has not been actually attached to the surface. */ wl_event_loop_add_idle( - output->manager->server->wl_event_loop, update_focus, output); + server.wl_event_loop, update_focus, output); } static void @@ -97,7 +98,7 @@ static void lock_output_reconfigure(struct session_lock_output *output) { struct wlr_box box; - wlr_output_layout_get_box(output->manager->server->output_layout, + wlr_output_layout_get_box(server.output_layout, output->output->wlr_output, &box); wlr_scene_rect_set_size(output->background, box.width, box.height); if (output->surface) { @@ -135,8 +136,11 @@ handle_new_surface(struct wl_listener *listener, void *data) } lock_output->surface = lock_surface; + struct wlr_scene_tree *surface_tree = wlr_scene_subsurface_tree_create(lock_output->tree, lock_surface->surface); + die_if_null(surface_tree); + node_descriptor_create(&surface_tree->node, LAB_NODE_SESSION_LOCK_SURFACE, /*view*/ NULL, /*data*/ NULL); @@ -187,7 +191,7 @@ static void align_session_lock_tree(struct output *output) { struct wlr_box box; - wlr_output_layout_get_box(output->server->output_layout, + wlr_output_layout_get_box(server.output_layout, output->wlr_output, &box); wlr_scene_node_set_position(&output->session_lock_tree->node, box.x, box.y); } @@ -209,12 +213,7 @@ session_lock_output_create(struct session_lock_manager *manager, struct output * struct session_lock_output *lock_output = znew(*lock_output); - struct wlr_scene_tree *tree = wlr_scene_tree_create(output->session_lock_tree); - if (!tree) { - wlr_log(WLR_ERROR, "session-lock: wlr_scene_tree_create()"); - free(lock_output); - goto exit_session; - } + struct wlr_scene_tree *tree = lab_wlr_scene_tree_create(output->session_lock_tree); /* * The ext-session-lock protocol says that the compositor should blank @@ -222,20 +221,14 @@ session_lock_output_create(struct session_lock_manager *manager, struct output * * fully hidden */ float black[4] = { 0.f, 0.f, 0.f, 1.f }; - struct wlr_scene_rect *background = wlr_scene_rect_create(tree, 0, 0, black); - if (!background) { - wlr_log(WLR_ERROR, "session-lock: wlr_scene_rect_create()"); - wlr_scene_node_destroy(&tree->node); - free(lock_output); - goto exit_session; - } + struct wlr_scene_rect *background = lab_wlr_scene_rect_create(tree, 0, 0, black); /* * Delay blanking output by 100ms to prevent flicker. If the session is * already locked, blank immediately. */ lock_output->blank_timer = - wl_event_loop_add_timer(manager->server->wl_event_loop, + wl_event_loop_add_timer(server.wl_event_loop, handle_output_blank_timeout, lock_output); if (!manager->locked) { wlr_scene_node_set_enabled(&background->node, false); @@ -258,12 +251,6 @@ session_lock_output_create(struct session_lock_manager *manager, struct output * lock_output_reconfigure(lock_output); wl_list_insert(&manager->lock_outputs, &lock_output->link); - return; - -exit_session: - /* TODO: Consider a better - but secure - way to deal with this */ - wlr_log(WLR_ERROR, "out of memory"); - exit(EXIT_FAILURE); } static void @@ -292,11 +279,11 @@ handle_lock_unlock(struct wl_listener *listener, void *data) if (manager->last_active_view) { desktop_focus_view(manager->last_active_view, /* raise */ false); } else { - desktop_focus_topmost_view(manager->server); + desktop_focus_topmost_view(); } manager->last_active_view = NULL; - cursor_update_focus(manager->server); + cursor_update_focus(); } /* Called when session-lock is destroyed without unlock */ @@ -336,11 +323,11 @@ handle_new_session_lock(struct wl_listener *listener, void *data) assert(wl_list_empty(&manager->lock_outputs)); /* Remember the focused view to restore it on unlock */ - manager->last_active_view = manager->server->active_view; - seat_focus_surface(&manager->server->seat, NULL); + manager->last_active_view = server.active_view; + seat_focus_surface(&server.seat, NULL); struct output *output; - wl_list_for_each(output, &manager->server->outputs, link) { + wl_list_for_each(output, &server.outputs, link) { session_lock_output_create(manager, output); } @@ -366,17 +353,16 @@ handle_manager_destroy(struct wl_listener *listener, void *data) session_lock_destroy(manager); wl_list_remove(&manager->new_lock.link); wl_list_remove(&manager->destroy.link); - manager->server->session_lock_manager = NULL; + server.session_lock_manager = NULL; free(manager); } void -session_lock_init(struct server *server) +session_lock_init(void) { struct session_lock_manager *manager = znew(*manager); - server->session_lock_manager = manager; - manager->server = server; - manager->wlr_manager = wlr_session_lock_manager_v1_create(server->wl_display); + server.session_lock_manager = manager; + manager->wlr_manager = wlr_session_lock_manager_v1_create(server.wl_display); wl_list_init(&manager->lock_outputs); manager->new_lock.notify = handle_new_session_lock; @@ -387,18 +373,18 @@ session_lock_init(struct server *server) } void -session_lock_update_for_layout_change(struct server *server) +session_lock_update_for_layout_change(void) { - if (!server->session_lock_manager->locked) { + if (!server.session_lock_manager->locked) { return; } struct output *output; - wl_list_for_each(output, &server->outputs, link) { + wl_list_for_each(output, &server.outputs, link) { align_session_lock_tree(output); } - struct session_lock_manager *manager = server->session_lock_manager; + struct session_lock_manager *manager = server.session_lock_manager; struct session_lock_output *lock_output; wl_list_for_each(lock_output, &manager->lock_outputs, link) { lock_output_reconfigure(lock_output); diff --git a/src/show-desktop.c b/src/show-desktop.c new file mode 100644 index 00000000..efddf2de --- /dev/null +++ b/src/show-desktop.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include "show-desktop.h" +#include +#include "common/array.h" +#include "config/types.h" +#include "labwc.h" +#include "view.h" + +static bool is_showing_desktop; + +static void +minimize_views(struct wl_array *views, bool minimize) +{ + struct view **view; + wl_array_for_each_reverse(view, views) { + view_minimize(*view, minimize); + } +} + +static void +show(void) +{ + static struct wl_array views; + wl_array_init(&views); + + /* Build array first as minimize changes server.views */ + struct view *view; + for_each_view(view, &server.views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) { + if (view->minimized) { + continue; + } + view->was_minimized_by_show_desktop_action = true; + array_add(&views, view); + } + minimize_views(&views, true); + is_showing_desktop = true; + + wl_array_release(&views); +} + +static void +restore(void) +{ + static struct wl_array views; + wl_array_init(&views); + + struct view *view; + for_each_view(view, &server.views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) { + if (view->was_minimized_by_show_desktop_action) { + array_add(&views, view); + } + } + minimize_views(&views, false); + show_desktop_reset(); + + wl_array_release(&views); +} + +void +show_desktop_toggle(void) +{ + if (is_showing_desktop) { + restore(); + } else { + show(); + } +} + +void +show_desktop_reset(void) +{ + is_showing_desktop = false; + + struct view *view; + for_each_view(view, &server.views, LAB_VIEW_CRITERIA_NONE) { + view->was_minimized_by_show_desktop_action = false; + } +} diff --git a/src/ssd/resize-indicator.c b/src/ssd/resize-indicator.c index 3a635edc..fa1166a3 100644 --- a/src/ssd/resize-indicator.c +++ b/src/ssd/resize-indicator.c @@ -3,6 +3,7 @@ #include #include #include +#include "common/scene-helpers.h" #include "config/rcxml.h" #include "labwc.h" #include "resize-indicator.h" @@ -44,10 +45,10 @@ resize_indicator_init(struct view *view) struct resize_indicator *indicator = &view->resize_indicator; assert(!indicator->tree); - indicator->tree = wlr_scene_tree_create(view->scene_tree); - indicator->border = wlr_scene_rect_create( + indicator->tree = lab_wlr_scene_tree_create(view->scene_tree); + indicator->border = lab_wlr_scene_rect_create( indicator->tree, 0, 0, rc.theme->osd_border_color); - indicator->background = wlr_scene_rect_create( + indicator->background = lab_wlr_scene_rect_create( indicator->tree, 0, 0, rc.theme->osd_bg_color); indicator->text = scaled_font_buffer_create(indicator->tree); @@ -61,7 +62,7 @@ wants_indicator(struct view *view) assert(view); if (rc.resize_indicator == LAB_RESIZE_INDICATOR_NON_PIXEL) { - if (view->server->input_mode != LAB_INPUT_STATE_RESIZE) { + if (server.input_mode != LAB_INPUT_STATE_RESIZE) { return false; } struct view_size_hints hints = view_get_size_hints(view); @@ -73,15 +74,15 @@ wants_indicator(struct view *view) } void -resize_indicator_reconfigure(struct server *server) +resize_indicator_reconfigure(void) { struct view *view; - wl_list_for_each(view, &server->views, link) { + wl_list_for_each(view, &server.views, link) { struct resize_indicator *indicator = &view->resize_indicator; if (indicator->tree) { resize_indicator_reconfigure_view(indicator); } - if (view != server->grabbed_view) { + if (view != server.grabbed_view) { continue; } @@ -142,7 +143,7 @@ void resize_indicator_update(struct view *view) { assert(view); - assert(view == view->server->grabbed_view); + assert(view == server.grabbed_view); if (!wants_indicator(view)) { return; @@ -172,21 +173,21 @@ resize_indicator_update(struct view *view) view_box.height = view_effective_height(view, /* use_pending */ false); } - if (view->server->input_mode == LAB_INPUT_STATE_RESIZE) { + if (server.input_mode == LAB_INPUT_STATE_RESIZE) { struct view_size_hints hints = view_get_size_hints(view); snprintf(text, sizeof(text), "%d x %d", MAX(0, view_box.width - hints.base_width) / MAX(1, hints.width_inc), MAX(0, view_box.height - hints.base_height) / MAX(1, hints.height_inc)); - } else if (view->server->input_mode == LAB_INPUT_STATE_MOVE) { + } else if (server.input_mode == LAB_INPUT_STATE_MOVE) { struct border margin = ssd_get_margin(view->ssd); snprintf(text, sizeof(text), "%d , %d", view_box.x - margin.left, view_box.y - margin.top); } else { wlr_log(WLR_ERROR, "Invalid input mode for indicator update %u", - view->server->input_mode); + server.input_mode); return; } diff --git a/src/ssd/ssd-border.c b/src/ssd/ssd-border.c index f0db4fff..c2c99f8d 100644 --- a/src/ssd/ssd-border.c +++ b/src/ssd/ssd-border.c @@ -3,7 +3,8 @@ #include #include #include "common/macros.h" -#include "labwc.h" +#include "common/scene-helpers.h" +#include "config/rcxml.h" #include "ssd.h" #include "ssd-internal.h" #include "theme.h" @@ -16,38 +17,38 @@ ssd_border_create(struct ssd *ssd) assert(!ssd->border.tree); struct view *view = ssd->view; - struct theme *theme = view->server->theme; + struct theme *theme = rc.theme; int width = view->current.width; int height = view_effective_height(view, /* use_pending */ false); int full_width = width + 2 * theme->border_width; int corner_width = ssd_get_corner_width(); - ssd->border.tree = wlr_scene_tree_create(ssd->tree); + ssd->border.tree = lab_wlr_scene_tree_create(ssd->tree); wlr_scene_node_set_position(&ssd->border.tree->node, -theme->border_width, 0); enum ssd_active_state active; FOR_EACH_ACTIVE_STATE(active) { struct ssd_border_subtree *subtree = &ssd->border.subtrees[active]; - subtree->tree = wlr_scene_tree_create(ssd->border.tree); + subtree->tree = lab_wlr_scene_tree_create(ssd->border.tree); struct wlr_scene_tree *parent = subtree->tree; wlr_scene_node_set_enabled(&parent->node, active); float *color = theme->window[active].border_color; - subtree->left = wlr_scene_rect_create(parent, + subtree->left = lab_wlr_scene_rect_create(parent, theme->border_width, height, color); wlr_scene_node_set_position(&subtree->left->node, 0, 0); - subtree->right = wlr_scene_rect_create(parent, + subtree->right = lab_wlr_scene_rect_create(parent, theme->border_width, height, color); wlr_scene_node_set_position(&subtree->right->node, theme->border_width + width, 0); - subtree->bottom = wlr_scene_rect_create(parent, + subtree->bottom = lab_wlr_scene_rect_create(parent, full_width, theme->border_width, color); wlr_scene_node_set_position(&subtree->bottom->node, 0, height); - subtree->top = wlr_scene_rect_create(parent, + subtree->top = lab_wlr_scene_rect_create(parent, MAX(width - 2 * corner_width, 0), theme->border_width, color); wlr_scene_node_set_position(&subtree->top->node, theme->border_width + corner_width, @@ -89,7 +90,7 @@ ssd_border_update(struct ssd *ssd) ssd->margin = ssd_thickness(ssd->view); } - struct theme *theme = view->server->theme; + struct theme *theme = rc.theme; int width = view->current.width; int height = view_effective_height(view, /* use_pending */ false); diff --git a/src/ssd/ssd-button.c b/src/ssd/ssd-button.c index 50131a12..7342dfad 100644 --- a/src/ssd/ssd-button.c +++ b/src/ssd/ssd-button.c @@ -5,6 +5,7 @@ #include "config/rcxml.h" #include "common/list.h" #include "common/mem.h" +#include "common/scene-helpers.h" #include "node.h" #include "scaled-buffer/scaled-icon-buffer.h" #include "scaled-buffer/scaled-img-buffer.h" @@ -19,7 +20,7 @@ attach_ssd_button(struct wl_list *button_parts, enum lab_node_type type, struct lab_img *imgs[LAB_BS_ALL + 1], int x, int y, struct view *view) { - struct wlr_scene_tree *root = wlr_scene_tree_create(parent); + struct wlr_scene_tree *root = lab_wlr_scene_tree_create(parent); wlr_scene_node_set_position(&root->node, x, y); assert(node_type_contains(LAB_NODE_BUTTON, type)); @@ -31,7 +32,7 @@ attach_ssd_button(struct wl_list *button_parts, enum lab_node_type type, /* Hitbox */ float invisible[4] = { 0, 0, 0, 0 }; - wlr_scene_rect_create(root, rc.theme->window_button_width, + lab_wlr_scene_rect_create(root, rc.theme->window_button_width, rc.theme->window_button_height, invisible); /* Icons */ @@ -49,8 +50,8 @@ attach_ssd_button(struct wl_list *button_parts, enum lab_node_type type, if (type == LAB_NODE_BUTTON_WINDOW_ICON) { struct scaled_icon_buffer *icon_buffer = - scaled_icon_buffer_create(root, view->server, - button_width - 2 * icon_padding, button_height); + scaled_icon_buffer_create(root, button_width + - 2 * icon_padding, button_height); assert(icon_buffer); struct wlr_scene_node *icon_node = &icon_buffer->scene_buffer->node; scaled_icon_buffer_set_view(icon_buffer, view); diff --git a/src/ssd/ssd-extents.c b/src/ssd/ssd-extents.c index 1a301f29..3840b107 100644 --- a/src/ssd/ssd-extents.c +++ b/src/ssd/ssd-extents.c @@ -2,6 +2,7 @@ #include #include +#include "common/scene-helpers.h" #include "config/rcxml.h" #include "labwc.h" #include "output.h" @@ -13,11 +14,11 @@ void ssd_extents_create(struct ssd *ssd) { struct view *view = ssd->view; - struct theme *theme = view->server->theme; + struct theme *theme = rc.theme; int border_width = MAX(0, MAX(rc.resize_minimum_area, theme->border_width)); - ssd->extents.tree = wlr_scene_tree_create(ssd->tree); + ssd->extents.tree = lab_wlr_scene_tree_create(ssd->tree); struct wlr_scene_tree *parent = ssd->extents.tree; if (view->fullscreen || view->maximized == VIEW_AXIS_BOTH) { wlr_scene_node_set_enabled(&parent->node, false); @@ -26,10 +27,10 @@ ssd_extents_create(struct ssd *ssd) -border_width, -(ssd->titlebar.height + border_width)); float invisible[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; - ssd->extents.top = wlr_scene_rect_create(parent, 0, 0, invisible); - ssd->extents.left = wlr_scene_rect_create(parent, 0, 0, invisible); - ssd->extents.right = wlr_scene_rect_create(parent, 0, 0, invisible); - ssd->extents.bottom = wlr_scene_rect_create(parent, 0, 0, invisible); + ssd->extents.top = lab_wlr_scene_rect_create(parent, 0, 0, invisible); + ssd->extents.left = lab_wlr_scene_rect_create(parent, 0, 0, invisible); + ssd->extents.right = lab_wlr_scene_rect_create(parent, 0, 0, invisible); + ssd->extents.bottom = lab_wlr_scene_rect_create(parent, 0, 0, invisible); /* Initial manual update to keep X11 applications happy */ ssd_extents_update(ssd); @@ -99,7 +100,7 @@ ssd_extents_update(struct ssd *ssd) return; } - struct theme *theme = view->server->theme; + struct theme *theme = rc.theme; int width = view->current.width; int height = view_effective_height(view, /* use_pending */ false); @@ -119,7 +120,7 @@ ssd_extents_update(struct ssd *ssd) pixman_region32_t usable; pixman_region32_init(&usable); struct output *output; - wl_list_for_each(output, &view->server->outputs, link) { + wl_list_for_each(output, &server.outputs, link) { if (!view_on_output(view, output)) { continue; } diff --git a/src/ssd/ssd-shadow.c b/src/ssd/ssd-shadow.c index 2ead17ce..9a456124 100644 --- a/src/ssd/ssd-shadow.c +++ b/src/ssd/ssd-shadow.c @@ -3,6 +3,7 @@ #include #include #include "buffer.h" +#include "common/scene-helpers.h" #include "config/rcxml.h" #include "labwc.h" #include "ssd.h" @@ -144,7 +145,7 @@ static void set_shadow_geometry(struct ssd *ssd) { struct view *view = ssd->view; - struct theme *theme = view->server->theme; + struct theme *theme = rc.theme; int titlebar_height = ssd->titlebar.height; int width = view->current.width; int height = view_effective_height(view, false) + titlebar_height; @@ -183,7 +184,7 @@ make_shadow(struct view *view, enum wl_output_transform tx) { struct wlr_scene_buffer *scene_buf = - wlr_scene_buffer_create(parent, buf); + lab_wlr_scene_buffer_create(parent, buf); wlr_scene_buffer_set_transform(scene_buf, tx); scene_buf->point_accepts_input = never_accepts_input; /* @@ -200,9 +201,9 @@ ssd_shadow_create(struct ssd *ssd) assert(ssd); assert(!ssd->shadow.tree); - ssd->shadow.tree = wlr_scene_tree_create(ssd->tree); + ssd->shadow.tree = lab_wlr_scene_tree_create(ssd->tree); - struct theme *theme = ssd->view->server->theme; + struct theme *theme = rc.theme; struct view *view = ssd->view; enum ssd_active_state active; @@ -218,7 +219,7 @@ ssd_shadow_create(struct ssd *ssd) continue; } - subtree->tree = wlr_scene_tree_create(ssd->shadow.tree); + subtree->tree = lab_wlr_scene_tree_create(ssd->shadow.tree); struct wlr_scene_tree *parent = subtree->tree; struct wlr_buffer *corner_top_buffer = &theme->window[active].shadow_corner_top->base; @@ -255,7 +256,7 @@ ssd_shadow_update(struct ssd *ssd) assert(ssd->shadow.tree); struct view *view = ssd->view; - struct theme *theme = ssd->view->server->theme; + struct theme *theme = rc.theme; bool maximized = view->maximized == VIEW_AXIS_BOTH; bool tiled_shadows = false; if (rc.shadows_on_tiled) { diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 09f5362a..d1a08810 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -7,7 +7,7 @@ #include #include "buffer.h" #include "common/mem.h" -#include "common/string-helpers.h" +#include "common/scene-helpers.h" #include "config/rcxml.h" #include "labwc.h" #include "node.h" @@ -27,18 +27,18 @@ void ssd_titlebar_create(struct ssd *ssd) { struct view *view = ssd->view; - struct theme *theme = view->server->theme; + struct theme *theme = rc.theme; int width = view->current.width; int corner_width = ssd_get_corner_width(); - ssd->titlebar.tree = wlr_scene_tree_create(ssd->tree); + ssd->titlebar.tree = lab_wlr_scene_tree_create(ssd->tree); node_descriptor_create(&ssd->titlebar.tree->node, LAB_NODE_TITLEBAR, view, /*data*/ NULL); enum ssd_active_state active; FOR_EACH_ACTIVE_STATE(active) { struct ssd_titlebar_subtree *subtree = &ssd->titlebar.subtrees[active]; - subtree->tree = wlr_scene_tree_create(ssd->titlebar.tree); + subtree->tree = lab_wlr_scene_tree_create(ssd->titlebar.tree); struct wlr_scene_tree *parent = subtree->tree; wlr_scene_node_set_enabled(&parent->node, active); wlr_scene_node_set_position(&parent->node, 0, -theme->titlebar_height); @@ -51,24 +51,24 @@ ssd_titlebar_create(struct ssd *ssd) &theme->window[active].corner_top_right_normal->base; /* Background */ - subtree->bar = wlr_scene_buffer_create(parent, titlebar_fill); + subtree->bar = lab_wlr_scene_buffer_create(parent, titlebar_fill); /* * Work around the wlroots/pixman bug that widened 1px buffer * becomes translucent when bilinear filtering is used. * TODO: remove once https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3990 * is solved */ - if (wlr_renderer_is_pixman(view->server->renderer)) { + if (wlr_renderer_is_pixman(server.renderer)) { wlr_scene_buffer_set_filter_mode( subtree->bar, WLR_SCALE_FILTER_NEAREST); } wlr_scene_node_set_position(&subtree->bar->node, corner_width, 0); - subtree->corner_left = wlr_scene_buffer_create(parent, corner_top_left); + subtree->corner_left = lab_wlr_scene_buffer_create(parent, corner_top_left); wlr_scene_node_set_position(&subtree->corner_left->node, -rc.theme->border_width, -rc.theme->border_width); - subtree->corner_right = wlr_scene_buffer_create(parent, corner_top_right); + subtree->corner_right = lab_wlr_scene_buffer_create(parent, corner_top_right); wlr_scene_node_set_position(&subtree->corner_right->node, width - corner_width, -rc.theme->border_width); @@ -160,7 +160,7 @@ set_squared_corners(struct ssd *ssd, bool enable) struct view *view = ssd->view; int width = view->current.width; int corner_width = ssd_get_corner_width(); - struct theme *theme = view->server->theme; + struct theme *theme = rc.theme; int x = enable ? 0 : corner_width; @@ -220,7 +220,7 @@ static void update_visible_buttons(struct ssd *ssd) { struct view *view = ssd->view; - struct theme *theme = view->server->theme; + struct theme *theme = rc.theme; int width = MAX(view->current.width - 2 * theme->window_titlebar_padding_width, 0); int button_width = theme->window_button_width; int button_spacing = theme->window_button_spacing; @@ -273,7 +273,7 @@ ssd_titlebar_update(struct ssd *ssd) struct view *view = ssd->view; int width = view->current.width; int corner_width = ssd_get_corner_width(); - struct theme *theme = view->server->theme; + struct theme *theme = rc.theme; bool maximized = view->maximized == VIEW_AXIS_BOTH; bool squared = ssd_should_be_squared(ssd); @@ -365,7 +365,7 @@ static void ssd_update_title_positions(struct ssd *ssd, int offset_left, int offset_right) { struct view *view = ssd->view; - struct theme *theme = view->server->theme; + struct theme *theme = rc.theme; int width = view->current.width; int title_bg_width = width - offset_left - offset_right; @@ -413,9 +413,9 @@ static void get_title_offsets(struct ssd *ssd, int *offset_left, int *offset_right) { struct ssd_titlebar_subtree *subtree = &ssd->titlebar.subtrees[SSD_ACTIVE]; - int button_width = ssd->view->server->theme->window_button_width; - int button_spacing = ssd->view->server->theme->window_button_spacing; - int padding_width = ssd->view->server->theme->window_titlebar_padding_width; + int button_width = rc.theme->window_button_width; + int button_spacing = rc.theme->window_button_spacing; + int padding_width = rc.theme->window_titlebar_padding_width; *offset_left = padding_width; *offset_right = padding_width; @@ -440,11 +440,10 @@ ssd_update_title(struct ssd *ssd) } struct view *view = ssd->view; - if (string_null_or_empty(view->title)) { - return; - } + /* view->title is never NULL (instead it can be an empty string) */ + assert(view->title); - struct theme *theme = view->server->theme; + struct theme *theme = rc.theme; struct ssd_state_title *state = &ssd->state.title; bool title_unchanged = state->text && !strcmp(view->title, state->text); @@ -488,23 +487,23 @@ ssd_update_title(struct ssd *ssd) } void -ssd_update_hovered_button(struct server *server, struct wlr_scene_node *node) +ssd_update_hovered_button(struct wlr_scene_node *node) { struct ssd_button *button = NULL; if (node && node->data) { button = node_try_ssd_button_from_node(node); - if (button == server->hovered_button) { + if (button == server.hovered_button) { /* Cursor is still on the same button */ return; } } /* Disable old hover */ - if (server->hovered_button) { - update_button_state(server->hovered_button, LAB_BS_HOVERED, false); + if (server.hovered_button) { + update_button_state(server.hovered_button, LAB_BS_HOVERED, false); } - server->hovered_button = button; + server.hovered_button = button; if (button) { update_button_state(button, LAB_BS_HOVERED, true); } diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index d1381c1a..e2ab6375 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -12,6 +12,7 @@ #include #include #include "common/mem.h" +#include "common/scene-helpers.h" #include "config/rcxml.h" #include "labwc.h" #include "node.h" @@ -38,7 +39,7 @@ ssd_thickness(struct view *view) return (struct border){ 0 }; } - struct theme *theme = view->server->theme; + struct theme *theme = rc.theme; if (view->maximized == VIEW_AXIS_BOTH) { struct border thickness = { 0 }; @@ -100,7 +101,7 @@ ssd_get_resizing_type(const struct ssd *ssd, struct wlr_cursor *cursor) if (view_titlebar_visible(view)) { /* If the titlebar is visible, consider it part of the view */ - int titlebar_height = view->server->theme->titlebar_height; + int titlebar_height = rc.theme->titlebar_height; view_box.y -= titlebar_height; view_box.height += titlebar_height; } @@ -145,7 +146,7 @@ ssd_create(struct view *view, bool active) struct ssd *ssd = znew(*ssd); ssd->view = view; - ssd->tree = wlr_scene_tree_create(view->scene_tree); + ssd->tree = lab_wlr_scene_tree_create(view->scene_tree); /* * Attach node_descriptor to the root node so that get_cursor_context() @@ -155,7 +156,7 @@ ssd_create(struct view *view, bool active) LAB_NODE_SSD_ROOT, view, /*data*/ NULL); wlr_scene_node_lower_to_bottom(&ssd->tree->node); - ssd->titlebar.height = view->server->theme->titlebar_height; + ssd->titlebar.height = rc.theme->titlebar_height; ssd_shadow_create(ssd); ssd_extents_create(ssd); /* @@ -256,7 +257,7 @@ ssd_set_titlebar(struct ssd *ssd, bool enabled) return; } wlr_scene_node_set_enabled(&ssd->titlebar.tree->node, enabled); - ssd->titlebar.height = enabled ? ssd->view->server->theme->titlebar_height : 0; + ssd->titlebar.height = enabled ? rc.theme->titlebar_height : 0; ssd_border_update(ssd); ssd_extents_update(ssd); ssd_shadow_update(ssd); @@ -272,10 +273,9 @@ ssd_destroy(struct ssd *ssd) /* Maybe reset hover view */ struct view *view = ssd->view; - struct server *server = view->server; - if (server->hovered_button && node_view_from_node( - server->hovered_button->node) == view) { - server->hovered_button = NULL; + if (server.hovered_button && node_view_from_node( + server.hovered_button->node) == view) { + server.hovered_button = NULL; } /* Destroy subcomponents */ diff --git a/src/tearing.c b/src/tearing.c index eaba51a0..541bf813 100644 --- a/src/tearing.c +++ b/src/tearing.c @@ -40,12 +40,11 @@ handle_controller_destroy(struct wl_listener *listener, void *data) void handle_tearing_new_object(struct wl_listener *listener, void *data) { - struct server *server = wl_container_of(listener, server, tearing_new_object); struct wlr_tearing_control_v1 *tearing_control = data; enum wp_tearing_control_v1_presentation_hint hint = wlr_tearing_control_manager_v1_surface_hint_from_surface - (server->tearing_control, tearing_control->surface); + (server.tearing_control, tearing_control->surface); wlr_log(WLR_DEBUG, "New presentation hint %d received for surface %p", hint, tearing_control->surface); diff --git a/src/theme.c b/src/theme.c index eeba3050..71e577ce 100644 --- a/src/theme.c +++ b/src/theme.c @@ -529,7 +529,7 @@ parse_justification(const char *str) * theme_builtin() applies a theme that is similar to vanilla GTK */ static void -theme_builtin(struct theme *theme, struct server *server) +theme_builtin(struct theme *theme) { theme->border_width = 1; theme->window_titlebar_padding_height = 0; @@ -634,7 +634,7 @@ theme_builtin(struct theme *theme, struct server *server) theme->osd_border_color[0] = FLT_MIN; theme->osd_label_text_color[0] = FLT_MIN; - if (wlr_renderer_is_pixman(server->renderer)) { + if (wlr_renderer_is_pixman(server.renderer)) { /* Draw only outlined overlay by default to save CPU resource */ theme->snapping_overlay_region.bg_enabled = false; theme->snapping_overlay_edge.bg_enabled = false; @@ -788,7 +788,7 @@ entry(struct theme *theme, const char *key, const char *value) value, "window.button.spacing"); } - /* botton hover overlay */ + /* button hover overlay */ if (match_glob(key, "window.button.hover.bg.color")) { parse_color(value, theme->window_button_hover_bg_color); } @@ -1815,13 +1815,13 @@ post_processing(struct theme *theme) } void -theme_init(struct theme *theme, struct server *server, const char *theme_name) +theme_init(struct theme *theme, const char *theme_name) { /* * Set some default values. This is particularly important on * reconfigure as not all themes set all options */ - theme_builtin(theme, server); + theme_builtin(theme); struct wl_list paths; diff --git a/src/view-impl-common.c b/src/view-impl-common.c index 23f37321..092ca55f 100644 --- a/src/view-impl-common.c +++ b/src/view-impl-common.c @@ -50,13 +50,12 @@ view_impl_unmap(struct view *view) * When exiting an xwayland application with multiple views * mapped, a race condition can occur: after the topmost view * is unmapped, the next view under it is offered focus, but is - * also unmapped before accepting focus (so server->active_view + * also unmapped before accepting focus (so server.active_view * remains NULL). To avoid being left with no active view at * all, check for that case also. */ - struct server *server = view->server; - if (view == server->active_view || !server->active_view) { - desktop_focus_topmost_view(server); + if (view == server.active_view || !server.active_view) { + desktop_focus_topmost_view(); } /* @@ -72,10 +71,9 @@ view_impl_unmap(struct view *view) static bool resizing_edge(struct view *view, enum lab_edge edge) { - struct server *server = view->server; - return server->input_mode == LAB_INPUT_STATE_RESIZE - && server->grabbed_view == view - && (server->resize_edges & edge); + return server.input_mode == LAB_INPUT_STATE_RESIZE + && server.grabbed_view == view + && (server.resize_edges & edge); } void diff --git a/src/view.c b/src/view.c index cb82ab22..21005f20 100644 --- a/src/view.c +++ b/src/view.c @@ -54,6 +54,8 @@ view_from_wlr_surface(struct wlr_surface *surface) return xdg_surface->data; } #if HAVE_XWAYLAND + // Doing this is harmless even in the case that xwayland could not be + // successfully started. struct wlr_xwayland_surface *xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface); if (xsurface) { @@ -69,7 +71,7 @@ security_context_from_view(struct view *view) if (view && view->surface && view->surface->resource) { struct wl_client *client = wl_resource_get_client(view->surface->resource); return wlr_security_context_manager_v1_lookup_client( - view->server->security_context_manager_v1, client); + server.security_context_manager_v1, client); } return NULL; } @@ -78,7 +80,7 @@ struct view_query * view_query_create(void) { struct view_query *query = znew(*query); - /* Must be synced with view_matches_criteria() in window-rules.c */ + /* Must be synced with view_matches_rule() in window-rules.c */ query->window_type = LAB_WINDOW_TYPE_INVALID; query->maximized = VIEW_AXIS_INVALID; query->decoration = LAB_SSD_MODE_INVALID; @@ -176,7 +178,7 @@ view_matches_query(struct view *view, struct view_query *query) return false; } - if (!query_tristate_match(query->focused, view->server->active_view == view)) { + if (!query_tristate_match(query->focused, server.active_view == view)) { return false; } @@ -202,7 +204,7 @@ view_matches_query(struct view *view, struct view_query *query) if (query->desktop) { const char *view_workspace = view->workspace->name; - struct workspace *current = view->server->workspaces.current; + struct workspace *current = server.workspaces.current; if (!strcasecmp(query->desktop, "other")) { /* "other" means the view is NOT on the current desktop */ @@ -225,7 +227,7 @@ view_matches_query(struct view *view, struct view_query *query) } if (query->monitor) { - struct output *current = output_nearest_to_cursor(view->server); + struct output *current = output_nearest_to_cursor(); if (!strcasecmp(query->monitor, "current")) { if (current != view->output) { return false; @@ -241,7 +243,7 @@ view_matches_query(struct view *view, struct view_query *query) return false; } } else { - if (output_from_name(view->server, query->monitor) + if (output_from_name(query->monitor) != view->output) { return false; } @@ -261,14 +263,14 @@ view_get_root(struct view *view) return view; } -static bool -matches_criteria(struct view *view, enum lab_view_criteria criteria) +bool +view_matches_criteria(struct view *view, enum lab_view_criteria criteria) { if (!view_is_focusable(view)) { return false; } if (criteria & LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) { - if (view->workspace != view->server->workspaces.current) { + if (view->workspace != server.workspaces.current) { return false; } } @@ -314,7 +316,7 @@ view_next(struct wl_list *head, struct view *view, enum lab_view_criteria criter for (elm = elm->next; elm != head; elm = elm->next) { view = wl_container_of(elm, view, link); - if (matches_criteria(view, criteria)) { + if (view_matches_criteria(view, criteria)) { return view; } } @@ -330,7 +332,7 @@ view_prev(struct wl_list *head, struct view *view, enum lab_view_criteria criter for (elm = elm->prev; elm != head; elm = elm->prev) { view = wl_container_of(elm, view, link); - if (matches_criteria(view, criteria)) { + if (view_matches_criteria(view, criteria)) { return view; } } @@ -338,11 +340,11 @@ view_prev(struct wl_list *head, struct view *view, enum lab_view_criteria criter } void -view_array_append(struct server *server, struct wl_array *views, +view_array_append(struct wl_array *views, enum lab_view_criteria criteria) { struct view *view; - for_each_view(view, &server->views, criteria) { + for_each_view(view, &server.views, criteria) { struct view **entry = wl_array_add(views, sizeof(*entry)); if (!entry) { wlr_log(WLR_ERROR, "wl_array_add(): out of memory"); @@ -446,8 +448,7 @@ view_discover_output(struct view *view, struct wlr_box *geometry) } struct output *output = - output_nearest_to(view->server, - geometry->x + geometry->width / 2, + output_nearest_to(geometry->x + geometry->width / 2, geometry->y + geometry->height / 2); if (output && output != view->output) { @@ -473,10 +474,10 @@ view_set_activated(struct view *view, bool activated) if (!activated) { /* Store configured keyboard layout per view */ view->keyboard_layout = - view->server->seat.keyboard_group->keyboard.modifiers.group; + server.seat.keyboard_group->keyboard.modifiers.group; } else { /* Switch to previously stored keyboard layout */ - keyboard_update_layout(&view->server->seat, view->keyboard_layout); + keyboard_update_layout(&server.seat, view->keyboard_layout); } } output_set_has_fullscreen_view(view->output, view->fullscreen); @@ -493,7 +494,7 @@ view_set_output(struct view *view, struct output *output) view->output = output; /* Show fullscreen views above top-layer */ if (view->fullscreen) { - desktop_update_top_layer_visibility(view->server); + desktop_update_top_layer_visibility(); } } @@ -510,10 +511,10 @@ static void view_update_outputs(struct view *view) { struct output *output; - struct wlr_output_layout *layout = view->server->output_layout; + struct wlr_output_layout *layout = server.output_layout; uint64_t new_outputs = 0; - wl_list_for_each(output, &view->server->outputs, link) { + wl_list_for_each(output, &server.outputs, link) { if (output_is_usable(output) && wlr_output_layout_intersects( layout, output->wlr_output, &view->current)) { new_outputs |= output->id_bit; @@ -523,7 +524,7 @@ view_update_outputs(struct view *view) if (new_outputs != view->outputs) { view->outputs = new_outputs; wl_signal_emit_mutable(&view->events.new_outputs, NULL); - desktop_update_top_layer_visibility(view->server); + desktop_update_top_layer_visibility(); } } @@ -562,14 +563,12 @@ view_moved(struct view *view) } view_update_outputs(view); ssd_update_geometry(view->ssd); - cursor_update_focus(view->server); - if (rc.resize_indicator && view->server->grabbed_view == view) { + cursor_update_focus(); + if (rc.resize_indicator && server.grabbed_view == view) { resize_indicator_update(view); } } -static void save_last_placement(struct view *view); - void view_move_resize(struct view *view, struct wlr_box geo) { @@ -589,7 +588,7 @@ view_move_resize(struct view *view, struct wlr_box geo) * Not sure if it might have other side-effects though. */ if (!view->adjusting_for_layout_change) { - save_last_placement(view); + view_save_last_placement(view); } } @@ -625,30 +624,12 @@ view_move_relative(struct view *view, int x, int y) view_move(view, view->pending.x + x, view->pending.y + y); } -void -view_move_to_cursor(struct view *view) +static bool +view_compute_near_cursor_position(struct view *view, struct wlr_box *geom) { assert(view); - struct output *pending_output = output_nearest_to_cursor(view->server); - if (!output_is_usable(pending_output)) { - return; - } - view_set_fullscreen(view, false); - view_maximize(view, VIEW_AXIS_NONE); - if (view_is_tiled(view)) { - view_set_untiled(view); - view_move_resize(view, view->natural_geometry); - } - - struct border margin = ssd_thickness(view); - struct wlr_box geo = view->pending; - geo.width += margin.left + margin.right; - geo.height += margin.top + margin.bottom; - - int x = view->server->seat.cursor->x - (geo.width / 2); - int y = view->server->seat.cursor->y - (geo.height / 2); - + struct output *pending_output = output_nearest_to_cursor(); struct wlr_box usable = output_usable_area_in_layout_coords(pending_output); /* Limit usable region to account for gap */ @@ -657,17 +638,30 @@ view_move_to_cursor(struct view *view) usable.width -= 2 * rc.gap; usable.height -= 2 * rc.gap; - if (x + geo.width > usable.x + usable.width) { - x = usable.x + usable.width - geo.width; + if (wlr_box_empty(geom) || wlr_box_empty(&usable)) { + return false; } - x = MAX(x, usable.x) + margin.left; - if (y + geo.height > usable.y + usable.height) { - y = usable.y + usable.height - geo.height; - } - y = MAX(y, usable.y) + margin.top; + struct border margin = ssd_thickness(view); + struct seat *seat = &server.seat; - view_move(view, x, y); + int total_width = geom->width + margin.left + margin.right; + int total_height = geom->height + margin.top + margin.bottom; + + int x = (int)seat->cursor->x - (total_width / 2); + int y = (int)seat->cursor->y - (total_height / 2); + + /* + * Order of MIN/MAX is significant here (so that the top-left + * corner of the view remains visible even if the view is larger + * than the usable output area) + */ + x = MIN(x, usable.x + usable.width - total_width); + geom->x = MAX(x, usable.x) + margin.left; + y = MIN(y, usable.y + usable.height - total_height); + geom->y = MAX(y, usable.y) + margin.top; + + return true; } struct view_size_hints @@ -754,7 +748,7 @@ _minimize(struct view *view, bool minimized, bool *need_refocus) * - unminimizing any mapped view */ *need_refocus |= (minimized ? - (view == view->server->active_view) : view->mapped); + (view == server.active_view) : view->mapped); } static void @@ -791,10 +785,9 @@ void view_minimize(struct view *view, bool minimized) { assert(view); - struct server *server = view->server; bool need_refocus = false; - if (server->input_mode == LAB_INPUT_STATE_CYCLE) { + if (server.input_mode == LAB_INPUT_STATE_CYCLE) { wlr_log(WLR_ERROR, "not minimizing window while window switching"); return; } @@ -814,7 +807,7 @@ view_minimize(struct view *view, bool minimized) */ if (need_refocus) { if (minimized) { - desktop_focus_topmost_view(server); + desktop_focus_topmost_view(); } else { desktop_focus_view(view, /* raise */ true); } @@ -869,7 +862,7 @@ adjust_floating_geometry(struct view *view, struct wlr_box *geometry, bool adjusted = false; bool onscreen = false; - if (wlr_output_layout_intersects(view->server->output_layout, + if (wlr_output_layout_intersects(server.output_layout, view->output->wlr_output, geometry)) { /* Always make sure the titlebar starts within the usable area */ struct border margin = ssd_get_margin(view->ssd); @@ -907,16 +900,8 @@ adjust_floating_geometry(struct view *view, struct wlr_box *geometry, } /* Reposition offscreen automatically if configured to do so */ - if (rc.placement_policy == LAB_PLACE_AUTOMATIC) { - if (placement_find_best(view, geometry)) { - return true; - } - } - - /* If automatic placement failed or was not enabled, just center */ - return view_compute_centered_position(view, NULL, - geometry->width, geometry->height, - &geometry->x, &geometry->y); + return view_compute_position_by_policy(view, geometry, + /* allow_cursor */ true, rc.placement_policy); } struct wlr_box @@ -926,8 +911,8 @@ view_get_fallback_natural_geometry(struct view *view) .width = VIEW_FALLBACK_WIDTH, .height = VIEW_FALLBACK_HEIGHT, }; - view_compute_centered_position(view, NULL, - box.width, box.height, &box.x, &box.y); + view_compute_position_by_policy(view, &box, + /* allow_cursor */ true, rc.placement_policy); return box; } @@ -987,13 +972,16 @@ view_center(struct view *view, const struct wlr_box *ref) * Algorithm based on KWin's implementation: * https://github.com/KDE/kwin/blob/df9f8f8346b5b7645578e37365dabb1a7b02ca5a/src/placement.cpp#L589 */ -static void -view_cascade(struct view *view) +static bool +view_compute_cascaded_position(struct view *view, struct wlr_box *geom) { /* "cascade" policy places a new view at center by default */ - struct wlr_box center = view->pending; - view_compute_centered_position(view, NULL, - center.width, center.height, ¢er.x, ¢er.y); + struct wlr_box center = *geom; + if (!view_compute_centered_position(view, NULL, center.width, + center.height, ¢er.x, ¢er.y)) { + return false; + } + struct border margin = ssd_get_margin(view->ssd); center.x -= margin.left; center.y -= margin.top; @@ -1008,7 +996,7 @@ view_cascade(struct view *view) /* TODO: move this logic to rcxml.c */ int offset_x = rc.placement_cascade_offset_x; int offset_y = rc.placement_cascade_offset_y; - struct theme *theme = view->server->theme; + struct theme *theme = rc.theme; int default_offset = theme->titlebar_height + theme->border_width + 5; if (offset_x <= 0) { offset_x = default_offset; @@ -1028,7 +1016,7 @@ view_cascade(struct view *view) /* Iterate over views from top to bottom */ struct view *other_view; - for_each_view(other_view, &view->server->views, + for_each_view(other_view, &server.views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) { struct wlr_box other = ssd_max_extents(other_view); if (other_view == view @@ -1068,28 +1056,42 @@ view_cascade(struct view *view) } } - view_move(view, candidate.x + margin.left, candidate.y + margin.top); + geom->x = candidate.x + margin.left; + geom->y = candidate.y + margin.top; + return true; +} + +bool +view_compute_position_by_policy(struct view *view, struct wlr_box *geom, + bool allow_cursor, enum lab_placement_policy policy) +{ + if (allow_cursor && policy == LAB_PLACE_CURSOR) { + return view_compute_near_cursor_position(view, geom); + } else if (policy == LAB_PLACE_AUTOMATIC) { + return placement_find_best(view, geom); + } else if (policy == LAB_PLACE_CASCADE) { + return view_compute_cascaded_position(view, geom); + } else { + return view_compute_centered_position(view, NULL, + geom->width, geom->height, &geom->x, &geom->y); + } } void view_place_by_policy(struct view *view, bool allow_cursor, enum lab_placement_policy policy) { - if (allow_cursor && policy == LAB_PLACE_CURSOR) { - view_move_to_cursor(view); - return; - } else if (policy == LAB_PLACE_AUTOMATIC) { - struct wlr_box geometry = view->pending; - if (placement_find_best(view, &geometry)) { - view_move(view, geometry.x, geometry.y); - return; - } - } else if (policy == LAB_PLACE_CASCADE) { - view_cascade(view); - return; + view_set_fullscreen(view, false); + view_maximize(view, VIEW_AXIS_NONE); + if (view_is_tiled(view)) { + view_set_untiled(view); + view_move_resize(view, view->natural_geometry); } - view_center(view, NULL); + struct wlr_box geom = view->pending; + if (view_compute_position_by_policy(view, &geom, allow_cursor, policy)) { + view_move(view, geom.x, geom.y); + } } void @@ -1249,7 +1251,7 @@ view_apply_fullscreen_geometry(struct view *view) assert(output_is_usable(view->output)); struct wlr_box box = { 0 }; - wlr_output_layout_get_box(view->server->output_layout, + wlr_output_layout_get_box(server.output_layout, view->output->wlr_output, &box); view_move_resize(view, box); } @@ -1403,8 +1405,8 @@ view_set_untiled(struct view *view) static bool in_interactive_move(struct view *view) { - return (view->server->input_mode == LAB_INPUT_STATE_MOVE - && view->server->grabbed_view == view); + return (server.input_mode == LAB_INPUT_STATE_MOVE + && server.grabbed_view == view); } void @@ -1541,9 +1543,14 @@ void view_set_layer(struct view *view, enum view_layer layer) { assert(view); + if (view->layer == layer) { + return; + } view->layer = layer; wlr_scene_node_reparent(&view->scene_tree->node, view->workspace->view_trees[layer]); + + wl_signal_emit_mutable(&view->events.always_on_top, NULL); } void @@ -1593,7 +1600,7 @@ decorate(struct view *view) { if (!view->ssd) { view->ssd = ssd_create(view, - view == view->server->active_view); + view == server.active_view); } } @@ -1678,7 +1685,7 @@ set_fullscreen(struct view *view, bool fullscreen) /* Show fullscreen views above top-layer */ if (view->output) { - desktop_update_top_layer_visibility(view->server); + desktop_update_top_layer_visibility(); } } @@ -1715,11 +1722,11 @@ view_set_fullscreen(struct view *view, bool fullscreen) * scene node ending up under the cursor even if view_moved() * isn't called. Update cursor focus explicitly for that case. */ - cursor_update_focus(view->server); + cursor_update_focus(); } -static void -save_last_placement(struct view *view) +void +view_save_last_placement(struct view *view) { assert(view); struct output *output = view->output; @@ -1759,8 +1766,7 @@ view_adjust_for_layout_change(struct view *view) view->adjusting_for_layout_change = true; struct wlr_box new_geo; - struct output *output = output_from_name(view->server, - view->last_placement.output_name); + struct output *output = output_from_name(view->last_placement.output_name); if (output_is_usable(output)) { /* * When the previous output (which might have been reconnected @@ -2162,7 +2168,7 @@ static void move_to_front(struct view *view) { wl_list_remove(&view->link); - wl_list_insert(&view->server->views, &view->link); + wl_list_insert(&server.views, &view->link); wlr_scene_node_raise_to_top(&view->scene_tree->node); } @@ -2170,7 +2176,7 @@ static void move_to_back(struct view *view) { wl_list_remove(&view->link); - wl_list_append(&view->server->views, &view->link); + wl_list_append(&server.views, &view->link); wlr_scene_node_lower_to_bottom(&view->scene_tree->node); } @@ -2184,15 +2190,14 @@ void view_move_to_front(struct view *view) { assert(view); - struct server *server = view->server; - assert(!wl_list_empty(&server->views)); + assert(!wl_list_empty(&server.views)); /* * Check whether the view is already in front, or is the root * parent of the view in front (in which case we don't want to * raise it in front of its sub-view). */ - struct view *front = wl_container_of(server->views.next, front, link); + struct view *front = wl_container_of(server.views.next, front, link); if (view == front || view == view_get_root(front)) { return; } @@ -2217,11 +2222,11 @@ view_move_to_front(struct view *view) * race, perform an explicit flush after restacking. */ if (view->type == LAB_XWAYLAND_VIEW) { - xwayland_flush(view->server); + xwayland_flush(); } #endif - cursor_update_focus(view->server); - desktop_update_top_layer_visibility(view->server); + cursor_update_focus(); + desktop_update_top_layer_visibility(); } void @@ -2234,8 +2239,8 @@ view_move_to_back(struct view *view) for_each_subview(root, move_to_back); move_to_back(root); - cursor_update_focus(view->server); - desktop_update_top_layer_visibility(view->server); + cursor_update_focus(); + desktop_update_top_layer_visibility(); } bool @@ -2380,7 +2385,7 @@ view_update_visibility(struct view *view) * Show top layer when a fullscreen view is hidden. * Hide it if a fullscreen view is shown (or uncovered). */ - desktop_update_top_layer_visibility(view->server); + desktop_update_top_layer_visibility(); /* * We may need to disable adaptive sync if view was fullscreen. @@ -2395,7 +2400,7 @@ view_update_visibility(struct view *view) /* Update usable area to account for XWayland "struts" (panels) */ if (view_has_strut_partial(view)) { - output_update_all_usable_areas(view->server, false); + output_update_all_usable_areas(false); } /* View might have been unmapped/minimized during move/resize */ @@ -2419,7 +2424,7 @@ view_set_shade(struct view *view, bool shaded) } /* If this window is being resized, cancel the resize when shading */ - if (shaded && view->server->input_mode == LAB_INPUT_STATE_RESIZE) { + if (shaded && server.input_mode == LAB_INPUT_STATE_RESIZE) { interactive_cancel(view); } @@ -2471,18 +2476,22 @@ view_init(struct view *view) wl_signal_init(&view->events.minimized); wl_signal_init(&view->events.fullscreened); wl_signal_init(&view->events.activated); + wl_signal_init(&view->events.always_on_top); wl_signal_init(&view->events.set_icon); wl_signal_init(&view->events.destroy); view->title = xstrdup(""); view->app_id = xstrdup(""); + + view->capture.scene = wlr_scene_create(); + view->capture.scene->restack_xwayland_surfaces = false; + wl_list_init(&view->capture.on_capture_source_destroy.link); } void view_destroy(struct view *view) { assert(view); - struct server *server = view->server; wl_signal_emit_mutable(&view->events.destroy, NULL); snap_constraints_invalidate(view); @@ -2498,9 +2507,9 @@ view_destroy(struct view *view) wl_list_remove(&view->request_fullscreen.link); wl_list_remove(&view->set_title.link); wl_list_remove(&view->destroy.link); + wl_list_remove(&view->capture.on_capture_source_destroy.link); - zfree(view->title); - zfree(view->app_id); + wlr_scene_node_destroy(&view->capture.scene->tree.node); if (view->foreign_toplevel) { foreign_toplevel_destroy(view->foreign_toplevel); @@ -2511,16 +2520,20 @@ view_destroy(struct view *view) * This check is (in theory) redundant since interactive_cancel() * is called at unmap. Leaving it here just to be sure. */ - if (server->grabbed_view == view) { + if (server.grabbed_view == view) { interactive_cancel(view); } - if (server->active_view == view) { - server->active_view = NULL; + if (server.active_view == view) { + server.active_view = NULL; } - if (server->session_lock_manager->last_active_view == view) { - server->session_lock_manager->last_active_view = NULL; + if (server.pending_auto_raise_view == view) { + desktop_cancel_pending_auto_raise(); + } + + if (server.session_lock_manager->last_active_view == view) { + server.session_lock_manager->last_active_view = NULL; } if (view->tiled_region_evacuate) { @@ -2528,7 +2541,7 @@ view_destroy(struct view *view) } /* TODO: call this on map/unmap instead */ - cycle_reinitialize(server); + cycle_reinitialize(); undecorate(view); @@ -2553,12 +2566,16 @@ view_destroy(struct view *view) assert(wl_list_empty(&view->events.minimized.listener_list)); assert(wl_list_empty(&view->events.fullscreened.listener_list)); assert(wl_list_empty(&view->events.activated.listener_list)); + assert(wl_list_empty(&view->events.always_on_top.listener_list)); assert(wl_list_empty(&view->events.set_icon.listener_list)); assert(wl_list_empty(&view->events.destroy.listener_list)); - /* Remove view from server->views */ + zfree(view->title); + zfree(view->app_id); + + /* Remove view from server.views */ wl_list_remove(&view->link); free(view); - cursor_update_focus(server); + cursor_update_focus(); } diff --git a/src/window-rules.c b/src/window-rules.c index 0b8f1101..4118ae32 100644 --- a/src/window-rules.c +++ b/src/window-rules.c @@ -12,7 +12,7 @@ static bool other_instances_exist(struct view *self, struct view_query *query) { - struct wl_list *views = &self->server->views; + struct wl_list *views = &server.views; struct view *view; wl_list_for_each(view, views, link) { @@ -24,7 +24,7 @@ other_instances_exist(struct view *self, struct view_query *query) } static bool -view_matches_criteria(struct window_rule *rule, struct view *view) +view_matches_rule(struct window_rule *rule, struct view *view) { struct view_query query = { .identifier = rule->identifier, @@ -52,8 +52,8 @@ window_rules_apply(struct view *view, enum window_rule_event event) if (rule->event != event) { continue; } - if (view_matches_criteria(rule, view)) { - actions_run(view, view->server, &rule->actions, NULL); + if (view_matches_rule(rule, view)) { + actions_run(view, &rule->actions, NULL); } } } @@ -69,8 +69,8 @@ window_rules_get_property(struct view *view, const char *property) * for foot's "serverDecoration" property to be "default". * * - * - * + * + * * */ struct window_rule *rule; @@ -81,7 +81,7 @@ window_rules_get_property(struct view *view, const char *property) * attribute would still return here if that property was asked * for. */ - if (view_matches_criteria(rule, view)) { + if (view_matches_rule(rule, view)) { if (rule->server_decoration && !strcasecmp(property, "serverDecoration")) { return rule->server_decoration; @@ -110,6 +110,10 @@ window_rules_get_property(struct view *view, const char *property) && !strcasecmp(property, "iconPreferClient")) { return rule->icon_prefer_client; } + if (rule->allow_always_on_top + && !strcasecmp(property, "allowAlwaysOnTop")) { + return rule->allow_always_on_top; + } } } return LAB_PROP_UNSPECIFIED; diff --git a/src/workspaces.c b/src/workspaces.c index 95e1fb25..a1ed9112 100644 --- a/src/workspaces.c +++ b/src/workspaces.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include "buffer.h" @@ -15,16 +16,15 @@ #include "common/graphic-helpers.h" #include "common/list.h" #include "common/mem.h" +#include "common/scene-helpers.h" #include "config/rcxml.h" #include "input/keyboard.h" #include "labwc.h" #include "output.h" -#include "protocols/cosmic-workspaces.h" -#include "protocols/ext-workspace.h" +#include "show-desktop.h" #include "theme.h" #include "view.h" -#define COSMIC_WORKSPACES_VERSION 1 #define EXT_WORKSPACES_VERSION 1 /* Internal helpers */ @@ -61,9 +61,9 @@ parse_workspace_index(const char *name) } static void -_osd_update(struct server *server) +_osd_update(void) { - struct theme *theme = server->theme; + struct theme *theme = rc.theme; /* Settings */ uint16_t margin = 10; @@ -74,7 +74,7 @@ _osd_update(struct server *server) theme->osd_workspace_switcher_boxes_height == 0; /* Dimensions */ - size_t workspace_count = wl_list_length(&server->workspaces.all); + size_t workspace_count = wl_list_length(&server.workspaces.all); uint16_t marker_width = workspace_count * (rect_width + padding) - padding; uint16_t width = margin * 2 + (marker_width < 200 ? 200 : marker_width); uint16_t height = margin * (hide_boxes ? 2 : 3) + rect_height + font_height(&rc.font_osd); @@ -84,7 +84,7 @@ _osd_update(struct server *server) struct workspace *workspace; struct output *output; - wl_list_for_each(output, &server->outputs, link) { + wl_list_for_each(output, &server.outputs, link) { if (!output_is_usable(output)) { continue; } @@ -114,9 +114,9 @@ _osd_update(struct server *server) uint16_t x; if (!hide_boxes) { x = (width - marker_width) / 2; - wl_list_for_each(workspace, &server->workspaces.all, link) { - bool active = workspace == server->workspaces.current; - set_cairo_color(cairo, server->theme->osd_label_text_color); + wl_list_for_each(workspace, &server.workspaces.all, link) { + bool active = workspace == server.workspaces.current; + set_cairo_color(cairo, rc.theme->osd_label_text_color); struct wlr_fbox fbox = { .x = x, .y = margin, @@ -135,13 +135,13 @@ _osd_update(struct server *server) } /* Text */ - set_cairo_color(cairo, server->theme->osd_label_text_color); + set_cairo_color(cairo, rc.theme->osd_label_text_color); PangoLayout *layout = pango_cairo_create_layout(cairo); pango_context_set_round_glyph_positions(pango_layout_get_context(layout), false); pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); /* Center workspace indicator on the x axis */ - int req_width = font_width(&rc.font_osd, server->workspaces.current->name); + int req_width = font_width(&rc.font_osd, server.workspaces.current->name); req_width = MIN(req_width, width - 2 * margin); x = (width - req_width) / 2; if (!hide_boxes) { @@ -154,7 +154,7 @@ _osd_update(struct server *server) pango_layout_set_font_description(layout, desc); pango_layout_set_width(layout, req_width * PANGO_SCALE); pango_font_description_free(desc); - pango_layout_set_text(layout, server->workspaces.current->name, -1); + pango_layout_set_text(layout, server.workspaces.current->name, -1); pango_cairo_show_layout(cairo, layout); g_object_unref(layout); @@ -163,12 +163,12 @@ _osd_update(struct server *server) cairo_destroy(cairo); if (!output->workspace_osd) { - output->workspace_osd = wlr_scene_buffer_create( - &server->scene->tree, NULL); + output->workspace_osd = lab_wlr_scene_buffer_create( + &server.scene->tree, NULL); } /* Position the whole thing */ struct wlr_box output_box; - wlr_output_layout_get_box(output->server->output_layout, + wlr_output_layout_get_box(server.output_layout, output->wlr_output, &output_box); int lx = output_box.x + (output_box.width - width) / 2; int ly = output_box.y + (output_box.height - height) / 2; @@ -183,7 +183,7 @@ _osd_update(struct server *server) } static struct workspace * -workspace_find_by_name(struct server *server, const char *name) +workspace_find_by_name(const char *name) { struct workspace *workspace; @@ -191,7 +191,7 @@ workspace_find_by_name(struct server *server, const char *name) size_t parsed_index = parse_workspace_index(name); if (parsed_index) { size_t index = 0; - wl_list_for_each(workspace, &server->workspaces.all, link) { + wl_list_for_each(workspace, &server.workspaces.all, link) { if (parsed_index == ++index) { return workspace; } @@ -199,7 +199,7 @@ workspace_find_by_name(struct server *server, const char *name) } /* by name */ - wl_list_for_each(workspace, &server->workspaces.all, link) { + wl_list_for_each(workspace, &server.workspaces.all, link) { if (!strcmp(workspace->name, name)) { return workspace; } @@ -209,58 +209,44 @@ workspace_find_by_name(struct server *server, const char *name) return NULL; } -/* cosmic workspace handlers */ static void -handle_cosmic_workspace_activate(struct wl_listener *listener, void *data) +handle_ext_workspace_commit(struct wl_listener *listener, void *data) { - struct workspace *workspace = wl_container_of(listener, workspace, on_cosmic.activate); - workspaces_switch_to(workspace, /* update_focus */ true); - wlr_log(WLR_INFO, "cosmic activating workspace %s", workspace->name); -} + struct wlr_ext_workspace_v1_commit_event *event = data; -/* ext workspace handlers */ -static void -handle_ext_workspace_activate(struct wl_listener *listener, void *data) -{ - struct workspace *workspace = wl_container_of(listener, workspace, on_ext.activate); - workspaces_switch_to(workspace, /* update_focus */ true); - wlr_log(WLR_INFO, "ext activating workspace %s", workspace->name); + struct wlr_ext_workspace_v1_request *req; + wl_list_for_each(req, event->requests, link) { + if (req->type == WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE) { + struct workspace *workspace = req->activate.workspace->data; + workspaces_switch_to(workspace, /* update_focus */ true); + wlr_log(WLR_INFO, "activating workspace %s", workspace->name); + } + } } /* Internal API */ static void -add_workspace(struct server *server, const char *name) +add_workspace(const char *name) { struct workspace *workspace = znew(*workspace); - workspace->server = server; workspace->name = xstrdup(name); - workspace->tree = wlr_scene_tree_create(server->workspace_tree); + workspace->tree = lab_wlr_scene_tree_create(server.workspace_tree); workspace->view_trees[VIEW_LAYER_ALWAYS_ON_BOTTOM] = - wlr_scene_tree_create(workspace->tree); + lab_wlr_scene_tree_create(workspace->tree); workspace->view_trees[VIEW_LAYER_NORMAL] = - wlr_scene_tree_create(workspace->tree); + lab_wlr_scene_tree_create(workspace->tree); workspace->view_trees[VIEW_LAYER_ALWAYS_ON_TOP] = - wlr_scene_tree_create(workspace->tree); - wl_list_append(&server->workspaces.all, &workspace->link); + lab_wlr_scene_tree_create(workspace->tree); + wl_list_append(&server.workspaces.all, &workspace->link); wlr_scene_node_set_enabled(&workspace->tree->node, false); - /* cosmic */ - workspace->cosmic_workspace = lab_cosmic_workspace_create(server->workspaces.cosmic_group); - lab_cosmic_workspace_set_name(workspace->cosmic_workspace, name); - - workspace->on_cosmic.activate.notify = handle_cosmic_workspace_activate; - wl_signal_add(&workspace->cosmic_workspace->events.activate, - &workspace->on_cosmic.activate); - - /* ext */ - workspace->ext_workspace = lab_ext_workspace_create( - server->workspaces.ext_manager, /*id*/ NULL); - lab_ext_workspace_assign_to_group(workspace->ext_workspace, server->workspaces.ext_group); - lab_ext_workspace_set_name(workspace->ext_workspace, name); - - workspace->on_ext.activate.notify = handle_ext_workspace_activate; - wl_signal_add(&workspace->ext_workspace->events.activate, - &workspace->on_ext.activate); + workspace->ext_workspace = wlr_ext_workspace_handle_v1_create( + server.workspaces.ext_manager, /*id*/ NULL, + EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ACTIVATE); + workspace->ext_workspace->data = workspace; + wlr_ext_workspace_handle_v1_set_group( + workspace->ext_workspace, server.workspaces.ext_group); + wlr_ext_workspace_handle_v1_set_name(workspace->ext_workspace, name); } static struct workspace * @@ -294,11 +280,11 @@ get_next(struct workspace *current, struct wl_list *workspaces, bool wrap) } static bool -workspace_has_views(struct workspace *workspace, struct server *server) +workspace_has_views(struct workspace *workspace) { struct view *view; - for_each_view(view, &server->views, LAB_VIEW_CRITERIA_NO_OMNIPRESENT) { + for_each_view(view, &server.views, LAB_VIEW_CRITERIA_NO_OMNIPRESENT) { if (view->workspace == workspace) { return true; } @@ -310,7 +296,6 @@ static struct workspace * get_adjacent_occupied(struct workspace *current, struct wl_list *workspaces, bool wrap, bool reverse) { - struct server *server = current->server; struct wl_list *start = ¤t->link; struct wl_list *link = reverse ? start->prev : start->next; bool has_wrapped = false; @@ -339,7 +324,7 @@ get_adjacent_occupied(struct workspace *current, struct wl_list *workspaces, } /* Check if it's occupied (and not current) */ - if (target != current && workspace_has_views(target, server)) { + if (target != current && workspace_has_views(target)) { return target; } @@ -372,56 +357,52 @@ _osd_handle_timeout(void *data) } static void -_osd_show(struct server *server) +_osd_show(void) { if (!rc.workspace_config.popuptime) { return; } - _osd_update(server); + _osd_update(); struct output *output; - wl_list_for_each(output, &server->outputs, link) { + wl_list_for_each(output, &server.outputs, link) { if (output_is_usable(output) && output->workspace_osd) { wlr_scene_node_set_enabled(&output->workspace_osd->node, true); } } - if (keyboard_get_all_modifiers(&server->seat)) { + if (keyboard_get_all_modifiers(&server.seat)) { /* Hidden by release of all modifiers */ - server->seat.workspace_osd_shown_by_modifier = true; + server.seat.workspace_osd_shown_by_modifier = true; } else { /* Hidden by timer */ - if (!server->seat.workspace_osd_timer) { - server->seat.workspace_osd_timer = wl_event_loop_add_timer( - server->wl_event_loop, _osd_handle_timeout, &server->seat); + if (!server.seat.workspace_osd_timer) { + server.seat.workspace_osd_timer = wl_event_loop_add_timer( + server.wl_event_loop, _osd_handle_timeout, &server.seat); } - wl_event_source_timer_update(server->seat.workspace_osd_timer, + wl_event_source_timer_update(server.seat.workspace_osd_timer, rc.workspace_config.popuptime); } } /* Public API */ void -workspaces_init(struct server *server) +workspaces_init(void) { - server->workspaces.cosmic_manager = lab_cosmic_workspace_manager_create( - server->wl_display, /* capabilities */ CW_CAP_WS_ACTIVATE, - COSMIC_WORKSPACES_VERSION); + server.workspaces.ext_manager = wlr_ext_workspace_manager_v1_create( + server.wl_display, EXT_WORKSPACES_VERSION); - server->workspaces.ext_manager = lab_ext_workspace_manager_create( - server->wl_display, /* capabilities */ WS_CAP_WS_ACTIVATE, - EXT_WORKSPACES_VERSION); + server.workspaces.ext_group = wlr_ext_workspace_group_handle_v1_create( + server.workspaces.ext_manager, /*caps*/ 0); - server->workspaces.cosmic_group = lab_cosmic_workspace_group_create( - server->workspaces.cosmic_manager); + server.workspaces.on_ext_manager.commit.notify = handle_ext_workspace_commit; + wl_signal_add(&server.workspaces.ext_manager->events.commit, + &server.workspaces.on_ext_manager.commit); - server->workspaces.ext_group = lab_ext_workspace_group_create( - server->workspaces.ext_manager); - - wl_list_init(&server->workspaces.all); + wl_list_init(&server.workspaces.all); struct workspace_config *conf; wl_list_for_each(conf, &rc.workspace_config.workspaces, link) { - add_workspace(server, conf->name); + add_workspace(conf->name); } /* @@ -431,19 +412,18 @@ workspaces_init(struct server *server) char *initial_name = rc.workspace_config.initial_workspace_name; struct workspace *initial = NULL; struct workspace *first = wl_container_of( - server->workspaces.all.next, first, link); + server.workspaces.all.next, first, link); if (initial_name) { - initial = workspace_find_by_name(server, initial_name); + initial = workspace_find_by_name(initial_name); } if (!initial) { initial = first; } - server->workspaces.current = initial; + server.workspaces.current = initial; wlr_scene_node_set_enabled(&initial->tree->node, true); - lab_cosmic_workspace_set_active(initial->cosmic_workspace, true); - lab_ext_workspace_set_active(initial->ext_workspace, true); + wlr_ext_workspace_handle_v1_set_active(initial->ext_workspace, true); } /* @@ -455,19 +435,16 @@ void workspaces_switch_to(struct workspace *target, bool update_focus) { assert(target); - struct server *server = target->server; - if (target == server->workspaces.current) { + if (target == server.workspaces.current) { return; } /* Disable the old workspace */ wlr_scene_node_set_enabled( - &server->workspaces.current->tree->node, false); + &server.workspaces.current->tree->node, false); - lab_cosmic_workspace_set_active( - server->workspaces.current->cosmic_workspace, false); - lab_ext_workspace_set_active( - server->workspaces.current->ext_workspace, false); + wlr_ext_workspace_handle_v1_set_active( + server.workspaces.current->ext_workspace, false); /* * Move Omnipresent views to new workspace. @@ -475,7 +452,7 @@ workspaces_switch_to(struct workspace *target, bool update_focus) * view_is_focusable() returns false (e.g. Conky). */ struct view *view; - wl_list_for_each_reverse(view, &server->views, link) { + wl_list_for_each_reverse(view, &server.views, link) { if (view->visible_on_all_workspaces) { view_move_to_workspace(view, target); } @@ -485,12 +462,12 @@ workspaces_switch_to(struct workspace *target, bool update_focus) wlr_scene_node_set_enabled(&target->tree->node, true); /* Save the last visited workspace */ - server->workspaces.last = server->workspaces.current; + server.workspaces.last = server.workspaces.current; /* Make sure new views will spawn on the new workspace */ - server->workspaces.current = target; + server.workspaces.current = target; - struct view *grabbed_view = server->grabbed_view; + struct view *grabbed_view = server.grabbed_view; if (grabbed_view) { view_move_to_workspace(grabbed_view, target); } @@ -500,26 +477,27 @@ workspaces_switch_to(struct workspace *target, bool update_focus) * the focus is not already on an omnipresent view. */ if (update_focus) { - struct view *active_view = server->active_view; + struct view *active_view = server.active_view; if (!(active_view && active_view->visible_on_all_workspaces)) { - desktop_focus_topmost_view(server); + desktop_focus_topmost_view(); } } /* And finally show the OSD */ - _osd_show(server); + _osd_show(); /* * Make sure we are not carrying around a * cursor image from the previous desktop */ - cursor_update_focus(server); + cursor_update_focus(); /* Ensure that only currently visible fullscreen windows hide the top layer */ - desktop_update_top_layer_visibility(server); + desktop_update_top_layer_visibility(); - lab_cosmic_workspace_set_active(target->cosmic_workspace, true); - lab_ext_workspace_set_active(target->ext_workspace, true); + wlr_ext_workspace_handle_v1_set_active(target->ext_workspace, true); + + show_desktop_reset(); } void @@ -527,8 +505,7 @@ workspaces_osd_hide(struct seat *seat) { assert(seat); struct output *output; - struct server *server = seat->server; - wl_list_for_each(output, &server->outputs, link) { + wl_list_for_each(output, &server.outputs, link) { if (!output->workspace_osd) { continue; } @@ -538,7 +515,7 @@ workspaces_osd_hide(struct seat *seat) seat->workspace_osd_shown_by_modifier = false; /* Update the cursor focus in case it was on top of the OSD before */ - cursor_update_focus(server); + cursor_update_focus(); } struct workspace * @@ -548,13 +525,12 @@ workspaces_find(struct workspace *anchor, const char *name, bool wrap) if (!name) { return NULL; } - struct server *server = anchor->server; - struct wl_list *workspaces = &server->workspaces.all; + struct wl_list *workspaces = &server.workspaces.all; if (!strcasecmp(name, "current")) { return anchor; } else if (!strcasecmp(name, "last")) { - return server->workspaces.last; + return server.workspaces.last; } else if (!strcasecmp(name, "left")) { return get_prev(anchor, workspaces, wrap); } else if (!strcasecmp(name, "right")) { @@ -564,7 +540,7 @@ workspaces_find(struct workspace *anchor, const char *name, bool wrap) } else if (!strcasecmp(name, "right-occupied")) { return get_next_occupied(anchor, workspaces, wrap); } - return workspace_find_by_name(server, name); + return workspace_find_by_name(name); } static void @@ -573,16 +549,13 @@ destroy_workspace(struct workspace *workspace) wlr_scene_node_destroy(&workspace->tree->node); zfree(workspace->name); wl_list_remove(&workspace->link); - wl_list_remove(&workspace->on_cosmic.activate.link); - wl_list_remove(&workspace->on_ext.activate.link); - lab_cosmic_workspace_destroy(workspace->cosmic_workspace); - lab_ext_workspace_destroy(workspace->ext_workspace); + wlr_ext_workspace_handle_v1_destroy(workspace->ext_workspace); free(workspace); } void -workspaces_reconfigure(struct server *server) +workspaces_reconfigure(void) { /* * Compare actual workspace list with the new desired configuration to: @@ -591,18 +564,18 @@ workspaces_reconfigure(struct server *server) * - Destroy workspaces if fewer workspace are desired */ - struct wl_list *workspace_link = server->workspaces.all.next; + struct wl_list *workspace_link = server.workspaces.all.next; struct workspace_config *conf; wl_list_for_each(conf, &rc.workspace_config.workspaces, link) { struct workspace *workspace = wl_container_of( workspace_link, workspace, link); - if (workspace_link == &server->workspaces.all) { + if (workspace_link == &server.workspaces.all) { /* # of configured workspaces increased */ wlr_log(WLR_DEBUG, "Adding workspace \"%s\"", conf->name); - add_workspace(server, conf->name); + add_workspace(conf->name); continue; } if (strcmp(workspace->name, conf->name)) { @@ -610,24 +583,22 @@ workspaces_reconfigure(struct server *server) wlr_log(WLR_DEBUG, "Renaming workspace \"%s\" to \"%s\"", workspace->name, conf->name); xstrdup_replace(workspace->name, conf->name); - lab_cosmic_workspace_set_name( - workspace->cosmic_workspace, workspace->name); - lab_ext_workspace_set_name( + wlr_ext_workspace_handle_v1_set_name( workspace->ext_workspace, workspace->name); } workspace_link = workspace_link->next; } - if (workspace_link == &server->workspaces.all) { + if (workspace_link == &server.workspaces.all) { return; } /* # of configured workspaces decreased */ - overlay_finish(&server->seat); + overlay_finish(&server.seat); struct workspace *first_workspace = - wl_container_of(server->workspaces.all.next, first_workspace, link); + wl_container_of(server.workspaces.all.next, first_workspace, link); - while (workspace_link != &server->workspaces.all) { + while (workspace_link != &server.workspaces.all) { struct workspace *workspace = wl_container_of( workspace_link, workspace, link); @@ -635,18 +606,18 @@ workspaces_reconfigure(struct server *server) workspace->name); struct view *view; - wl_list_for_each(view, &server->views, link) { + wl_list_for_each(view, &server.views, link) { if (view->workspace == workspace) { view_move_to_workspace(view, first_workspace); } } - if (server->workspaces.current == workspace) { + if (server.workspaces.current == workspace) { workspaces_switch_to(first_workspace, /* update_focus */ true); } - if (server->workspaces.last == workspace) { - server->workspaces.last = first_workspace; + if (server.workspaces.last == workspace) { + server.workspaces.last = first_workspace; } workspace_link = workspace_link->next; @@ -655,11 +626,12 @@ workspaces_reconfigure(struct server *server) } void -workspaces_destroy(struct server *server) +workspaces_destroy(void) { struct workspace *workspace, *tmp; - wl_list_for_each_safe(workspace, tmp, &server->workspaces.all, link) { + wl_list_for_each_safe(workspace, tmp, &server.workspaces.all, link) { destroy_workspace(workspace); } - assert(wl_list_empty(&server->workspaces.all)); + assert(wl_list_empty(&server.workspaces.all)); + wl_list_remove(&server.workspaces.on_ext_manager.commit.link); } diff --git a/src/xdg-popup.c b/src/xdg-popup.c index aded80b5..7ca9ab01 100644 --- a/src/xdg-popup.c +++ b/src/xdg-popup.c @@ -30,7 +30,6 @@ static void popup_unconstrain(struct xdg_popup *popup) { struct view *view = popup->parent_view; - struct server *server = view->server; /* Get position of parent toplevel/popup */ int parent_lx, parent_ly; @@ -44,11 +43,13 @@ popup_unconstrain(struct xdg_popup *popup) * than zero, typically with Qt apps. We therefore clamp it to avoid for * example the 'File' menu of a maximized window to end up on an another * output. + * Also some apps open the menu exactly at the right border when maximized, + * causing popup_box->x (or y?) to be in the next output. We subtract one + * inside MAX to avoid the problem mentioned above. */ struct wlr_box *popup_box = &popup->wlr_popup->scheduled.geometry; - struct output *output = output_nearest_to(server, - parent_lx + MAX(popup_box->x, 0), - parent_ly + MAX(popup_box->y, 0)); + struct output *output = output_nearest_to(parent_lx + MAX(popup_box->x - 1, 0), + parent_ly + MAX(popup_box->y - 1, 0)); struct wlr_box usable = output_usable_area_in_layout_coords(output); /* Get offset of toplevel window from its surface */ @@ -91,7 +92,7 @@ handle_destroy(struct wl_listener *listener, void *data) wl_list_remove(&popup->commit.link); } - cursor_update_focus(popup->parent_view->server); + cursor_update_focus(); free(popup); } @@ -151,19 +152,23 @@ xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup) * this, we always set the user data field of wlr_surfaces to the * corresponding scene node. * - * xdg-popups live in server->xdg_popup_tree so that they can be + * xdg-popups live in server.xdg_popup_tree so that they can be * rendered above always-on-top windows */ struct wlr_scene_tree *parent_tree = NULL; if (parent->role == WLR_XDG_SURFACE_ROLE_POPUP) { parent_tree = parent->surface->data; } else { - parent_tree = view->server->xdg_popup_tree; - wlr_scene_node_set_position(&view->server->xdg_popup_tree->node, + parent_tree = server.xdg_popup_tree; + wlr_scene_node_set_position(&server.xdg_popup_tree->node, view->current.x, view->current.y); } wlr_popup->base->surface->data = wlr_scene_xdg_surface_create(parent_tree, wlr_popup->base); + die_if_null(wlr_popup->base->surface->data); + node_descriptor_create(wlr_popup->base->surface->data, LAB_NODE_XDG_POPUP, view, /*data*/ NULL); + + wlr_scene_xdg_surface_create(&view->capture.scene->tree, wlr_popup->base); } diff --git a/src/xdg.c b/src/xdg.c index d3774801..ccba0de1 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -13,6 +13,7 @@ #include "common/box.h" #include "common/macros.h" #include "common/mem.h" +#include "common/scene-helpers.h" #include "config/rcxml.h" #include "decorations.h" #include "foreign-toplevel/foreign.h" @@ -29,6 +30,8 @@ #define LAB_XDG_SHELL_VERSION 6 #define CONFIGURE_TIMEOUT_MS 100 +static struct view *xdg_toplevel_view_get_root(struct view *view); + static struct xdg_toplevel_view * xdg_toplevel_view_from_view(struct view *view) { @@ -55,6 +58,13 @@ xdg_toplevel_from_view(struct view *view) return xdg_surface->toplevel; } +static struct view * +xdg_toplevel_view_get_parent(struct view *view) +{ + struct wlr_xdg_toplevel *toplevel = xdg_toplevel_from_view(view); + return toplevel->parent ? toplevel->parent->base->data : NULL; +} + static struct view_size_hints xdg_toplevel_view_get_size_hints(struct view *view) { @@ -108,26 +118,39 @@ set_fullscreen_from_request(struct view *view, { if (!view->fullscreen && requested->fullscreen && requested->fullscreen_output) { - view_set_output(view, output_from_wlr_output(view->server, - requested->fullscreen_output)); + view_set_output(view, output_from_wlr_output(requested->fullscreen_output)); } view_set_fullscreen(view, requested->fullscreen); } +/* Called from map/commit handler and updates view->pending.x/y directly */ static void -do_late_positioning(struct view *view) +set_initial_position(struct view *view) { - struct server *server = view->server; - if (server->input_mode == LAB_INPUT_STATE_MOVE - && view == server->grabbed_view) { - /* Reposition the view while anchoring it to cursor */ - interactive_anchor_to_cursor(server, &view->pending); - } else { - /* TODO: smart placement? */ - view_compute_centered_position(view, NULL, - view->pending.width, view->pending.height, - &view->pending.x, &view->pending.y); + if (!view_is_floating(view)) { + return; } + + view_constrain_size_to_that_of_usable_area(view); + + if (server.input_mode == LAB_INPUT_STATE_MOVE + && view == server.grabbed_view) { + /* Reposition the view while anchoring it to cursor */ + interactive_anchor_to_cursor(&view->pending); + } else { + struct view *parent = xdg_toplevel_view_get_parent(view); + if (parent) { + /* Center relative to parent view */ + view_compute_centered_position(view, &parent->pending, + view->pending.width, view->pending.height, + &view->pending.x, &view->pending.y); + } else { + view_compute_position_by_policy(view, &view->pending, + /* allow_cursor */ true, rc.placement_policy); + } + } + + view_save_last_placement(view); } static void @@ -152,7 +175,7 @@ center_fullscreen_if_needed(struct view *view) } struct wlr_box output_box = {0}; - wlr_output_layout_get_box(view->server->output_layout, + wlr_output_layout_get_box(server.output_layout, view->output->wlr_output, &output_box); box_center(view->current.width, view->current.height, &output_box, &output_box, &view->current.x, &view->current.y); @@ -171,7 +194,7 @@ center_fullscreen_if_needed(struct view *view) if (!xdg_view->fullscreen_bg) { const float black[4] = {0, 0, 0, 1}; xdg_view->fullscreen_bg = - wlr_scene_rect_create(view->scene_tree, 0, 0, black); + lab_wlr_scene_rect_create(view->scene_tree, 0, 0, black); wlr_scene_node_lower_to_bottom(&xdg_view->fullscreen_bg->node); } @@ -206,7 +229,15 @@ handle_commit(struct wl_listener *listener, void *data) | WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE; wlr_xdg_toplevel_set_wm_capabilities(toplevel, wm_caps); - if (view->output) { + /* Put view on the same output as its parent if possible */ + struct view *parent = xdg_toplevel_view_get_parent(view); + if (parent && output_is_usable(parent->output)) { + view_set_output(view, parent->output); + } else { + view_set_output(view, output_nearest_to_cursor()); + } + + if (output_is_usable(view->output)) { wlr_xdg_toplevel_set_bounds(toplevel, view->output->usable_area.width, view->output->usable_area.height); @@ -242,7 +273,7 @@ handle_commit(struct wl_listener *listener, void *data) if (wlr_box_empty(&view->pending) && !wlr_box_empty(&size)) { view->pending.width = size.width; view->pending.height = size.height; - do_late_positioning(view); + set_initial_position(view); update_required = true; } @@ -407,7 +438,7 @@ set_pending_configure_serial(struct view *view, uint32_t serial) view->pending_configure_serial = serial; if (!view->pending_configure_timeout) { view->pending_configure_timeout = - wl_event_loop_add_timer(view->server->wl_event_loop, + wl_event_loop_add_timer(server.wl_event_loop, handle_configure_timeout, view); } wl_event_source_timer_update(view->pending_configure_timeout, @@ -433,6 +464,7 @@ handle_destroy(struct wl_listener *listener, void *data) /* Remove xdg-shell view specific listeners */ wl_list_remove(&xdg_toplevel_view->set_app_id.link); wl_list_remove(&xdg_toplevel_view->request_show_window_menu.link); + wl_list_remove(&xdg_toplevel_view->set_parent.link); wl_list_remove(&xdg_toplevel_view->new_popup.link); wl_list_remove(&view->commit.link); @@ -455,7 +487,7 @@ handle_request_move(struct wl_listener *listener, void *data) * this client, to prevent the client from requesting this whenever they * want. * - * Note: interactive_begin() checks that view == server->grabbed_view. + * Note: interactive_begin() checks that view == server.grabbed_view. */ struct view *view = wl_container_of(listener, view, request_move); interactive_begin(view, LAB_INPUT_STATE_MOVE, LAB_EDGE_NONE); @@ -472,7 +504,7 @@ handle_request_resize(struct wl_listener *listener, void *data) * this client, to prevent the client from requesting this whenever they * want. * - * Note: interactive_begin() checks that view == server->grabbed_view. + * Note: interactive_begin() checks that view == server.grabbed_view. */ struct wlr_xdg_toplevel_resize_event *event = data; struct view *view = wl_container_of(listener, view, request_resize); @@ -500,9 +532,6 @@ handle_request_maximize(struct wl_listener *listener, void *data) return; } - if (!view->mapped && !output_is_usable(view->output)) { - view_set_output(view, output_nearest_to_cursor(view->server)); - } bool maximized = toplevel->requested.maximized; view_maximize(view, maximized ? VIEW_AXIS_BOTH : VIEW_AXIS_NONE); } @@ -521,9 +550,6 @@ handle_request_fullscreen(struct wl_listener *listener, void *data) return; } - if (!view->mapped && !output_is_usable(view->output)) { - view_set_output(view, output_nearest_to_cursor(view->server)); - } set_fullscreen_from_request(view, &xdg_toplevel_from_view(view)->requested); } @@ -533,16 +559,32 @@ handle_request_show_window_menu(struct wl_listener *listener, void *data) { struct xdg_toplevel_view *xdg_toplevel_view = wl_container_of( listener, xdg_toplevel_view, request_show_window_menu); - struct server *server = xdg_toplevel_view->base.server; - struct menu *menu = menu_get_by_id(server, "client-menu"); + struct menu *menu = menu_get_by_id("client-menu"); assert(menu); menu->triggered_by_view = &xdg_toplevel_view->base; - struct wlr_cursor *cursor = server->seat.cursor; + struct wlr_cursor *cursor = server.seat.cursor; menu_open_root(menu, cursor->x, cursor->y); } +static void +handle_set_parent(struct wl_listener *listener, void *data) +{ + struct xdg_toplevel_view *xdg_toplevel_view = wl_container_of( + listener, xdg_toplevel_view, set_parent); + struct view *view = &xdg_toplevel_view->base; + struct view *view_root = xdg_toplevel_view_get_root(view); + if (view_root == view) { + return; + } + struct wlr_scene_node *node, *tmp; + wl_list_for_each_safe(node, tmp, &view->capture.scene->tree.children, link) { + wlr_log(WLR_DEBUG, "moving capture scene node to view_root"); + wlr_scene_node_reparent(node, &view_root->capture.scene->tree); + } +} + static void handle_set_title(struct wl_listener *listener, void *data) { @@ -579,7 +621,24 @@ xdg_toplevel_view_configure(struct view *view, struct wlr_box geo) if (geo.width != view->pending.width || geo.height != view->pending.height) { if (toplevel->base->initialized) { - serial = wlr_xdg_toplevel_set_size(toplevel, geo.width, geo.height); + /* + * geo.{width,height} are checked here to guard against + * an odd edge-case reported in #3608 which involves TTY + * switching between labwc and Xfce running on X.Org on + * another TTY. It is not yet clear what causes this, + * but hitting wlroots assert() is not great, so let's + * protect against it. + * + * Ref: + * - https://github.com/labwc/labwc/issues/3608 + */ + if (geo.width > 0 && geo.height > 0) { + serial = wlr_xdg_toplevel_set_size(toplevel, + geo.width, geo.height); + } else { + wlr_log(WLR_ERROR, "cannot set size %dx%d", + geo.width, geo.height); + } } else { /* * This may happen, for example, when a panel resizes because a @@ -636,14 +695,6 @@ xdg_toplevel_view_minimize(struct view *view, bool minimized) /* noop */ } -static struct view * -xdg_toplevel_view_get_parent(struct view *view) -{ - struct wlr_xdg_toplevel *toplevel = xdg_toplevel_from_view(view); - return toplevel->parent ? - (struct view *)toplevel->parent->base->data : NULL; -} - static struct wlr_xdg_toplevel * top_parent_of(struct view *view) { @@ -668,7 +719,7 @@ xdg_toplevel_view_append_children(struct view *self, struct wl_array *children) struct wlr_xdg_toplevel *toplevel = xdg_toplevel_from_view(self); struct view *view; - wl_list_for_each_reverse(view, &self->server->views, link) { + wl_list_for_each_reverse(view, &server.views, link) { if (view == self) { continue; } @@ -790,23 +841,6 @@ xdg_toplevel_view_notify_tiled(struct view *view) } } -static void -set_initial_position(struct view *view) -{ - view_constrain_size_to_that_of_usable_area(view); - - struct view *parent = xdg_toplevel_view_get_parent(view); - if (parent) { - /* Child views are center-aligned relative to their parents */ - view_set_output(view, parent->output); - view_center(view, &parent->pending); - return; - } - - /* All other views are placed according to a configured strategy */ - view_place_by_policy(view, /* allow_cursor */ true, rc.placement_policy); -} - static void handle_map(struct wl_listener *listener, void *data) { @@ -815,14 +849,6 @@ handle_map(struct wl_listener *listener, void *data) return; } - /* - * An output should have been chosen when the surface was first - * created, but take one more opportunity to assign an output if not. - */ - if (!output_is_usable(view->output)) { - view_set_output(view, output_nearest_to_cursor(view->server)); - } - view->mapped = true; if (!view->been_mapped) { @@ -835,24 +861,17 @@ handle_map(struct wl_listener *listener, void *data) /* * Set initial "pending" dimensions. "Current" * dimensions remain zero until handle_commit(). + * Note: this must be done before view_impl_map() + * for window rules to work correctly. */ if (wlr_box_empty(&view->pending)) { struct wlr_xdg_surface *xdg_surface = xdg_surface_from_view(view); view->pending.width = xdg_surface->geometry.width; view->pending.height = xdg_surface->geometry.height; - } - - /* - * Set initial "pending" position for floating views. - */ - if (view_is_floating(view)) { set_initial_position(view); } - /* Disable background fill at map (paranoid?) */ - disable_fullscreen_bg(view); - /* * Set initial "current" position directly before * calling view_moved() to reduce flicker @@ -990,8 +1009,6 @@ handle_xdg_activation_request(struct wl_listener *listener, void *data) static void handle_new_xdg_toplevel(struct wl_listener *listener, void *data) { - struct server *server = - wl_container_of(listener, server, new_xdg_toplevel); struct wlr_xdg_toplevel *xdg_toplevel = data; struct wlr_xdg_surface *xdg_surface = xdg_toplevel->base; @@ -1000,7 +1017,6 @@ handle_new_xdg_toplevel(struct wl_listener *listener, void *data) struct xdg_toplevel_view *xdg_toplevel_view = znew(*xdg_toplevel_view); struct view *view = &xdg_toplevel_view->base; - view->server = server; view->type = LAB_XDG_SHELL_VIEW; view->impl = &xdg_toplevel_view_impl; view_init(view); @@ -1011,26 +1027,25 @@ handle_new_xdg_toplevel(struct wl_listener *listener, void *data) * Pick an output for the surface as soon as its created, so that the * client can be notified about any fractional scale before it is given * the chance to configure itself (and possibly pick its dimensions). + * + * FIXME: this may be the wrong output since the parent view isn't + * known yet. The correct output will be set at initial commit. */ - view_set_output(view, output_nearest_to_cursor(server)); - if (view->output) { + view_set_output(view, output_nearest_to_cursor()); + if (output_is_usable(view->output)) { wlr_fractional_scale_v1_notify_scale(xdg_surface->surface, view->output->wlr_output->scale); } - view->workspace = server->workspaces.current; - view->scene_tree = wlr_scene_tree_create( + view->workspace = server.workspaces.current; + view->scene_tree = lab_wlr_scene_tree_create( view->workspace->view_trees[VIEW_LAYER_NORMAL]); wlr_scene_node_set_enabled(&view->scene_tree->node, false); struct wlr_scene_tree *tree = wlr_scene_xdg_surface_create( view->scene_tree, xdg_surface); - if (!tree) { - /* TODO: might need further clean up */ - wl_resource_post_no_memory(xdg_surface->resource); - free(xdg_toplevel_view); - return; - } + die_if_null(tree); + view->content_tree = tree; node_descriptor_create(&view->scene_tree->node, LAB_NODE_VIEW, view, /*data*/ NULL); @@ -1068,6 +1083,9 @@ handle_new_xdg_toplevel(struct wl_listener *listener, void *data) mappable_connect(&view->mappable, xdg_surface->surface, handle_map, handle_unmap); + struct view *root_view = xdg_toplevel_view_get_root(view); + wlr_scene_xdg_surface_create(&root_view->capture.scene->tree, xdg_surface); + struct wlr_xdg_toplevel *toplevel = xdg_surface->toplevel; CONNECT_SIGNAL(toplevel, view, destroy); CONNECT_SIGNAL(toplevel, view, request_move); @@ -1081,10 +1099,11 @@ handle_new_xdg_toplevel(struct wl_listener *listener, void *data) /* Events specific to XDG toplevel views */ CONNECT_SIGNAL(toplevel, xdg_toplevel_view, set_app_id); CONNECT_SIGNAL(toplevel, xdg_toplevel_view, request_show_window_menu); + CONNECT_SIGNAL(toplevel, xdg_toplevel_view, set_parent); CONNECT_SIGNAL(xdg_surface, xdg_toplevel_view, new_popup); - wl_list_insert(&server->views, &view->link); - view->creation_id = server->next_view_creation_id++; + wl_list_insert(&server.views, &view->link); + view->creation_id = server.next_view_creation_id++; } static void @@ -1118,46 +1137,46 @@ handle_xdg_toplevel_icon_set_icon(struct wl_listener *listener, void *data) } void -xdg_shell_init(struct server *server) +xdg_shell_init(void) { - server->xdg_shell = wlr_xdg_shell_create(server->wl_display, + server.xdg_shell = wlr_xdg_shell_create(server.wl_display, LAB_XDG_SHELL_VERSION); - if (!server->xdg_shell) { + if (!server.xdg_shell) { wlr_log(WLR_ERROR, "unable to create the XDG shell interface"); exit(EXIT_FAILURE); } - server->new_xdg_toplevel.notify = handle_new_xdg_toplevel; - wl_signal_add(&server->xdg_shell->events.new_toplevel, &server->new_xdg_toplevel); + server.new_xdg_toplevel.notify = handle_new_xdg_toplevel; + wl_signal_add(&server.xdg_shell->events.new_toplevel, &server.new_xdg_toplevel); - server->xdg_activation = wlr_xdg_activation_v1_create(server->wl_display); - if (!server->xdg_activation) { + server.xdg_activation = wlr_xdg_activation_v1_create(server.wl_display); + if (!server.xdg_activation) { wlr_log(WLR_ERROR, "unable to create xdg_activation interface"); exit(EXIT_FAILURE); } - server->xdg_activation_request.notify = handle_xdg_activation_request; - wl_signal_add(&server->xdg_activation->events.request_activate, - &server->xdg_activation_request); + server.xdg_activation_request.notify = handle_xdg_activation_request; + wl_signal_add(&server.xdg_activation->events.request_activate, + &server.xdg_activation_request); - server->xdg_activation_new_token.notify = handle_xdg_activation_new_token; - wl_signal_add(&server->xdg_activation->events.new_token, - &server->xdg_activation_new_token); + server.xdg_activation_new_token.notify = handle_xdg_activation_new_token; + wl_signal_add(&server.xdg_activation->events.new_token, + &server.xdg_activation_new_token); - server->xdg_toplevel_icon_manager = wlr_xdg_toplevel_icon_manager_v1_create( - server->wl_display, 1); - server->xdg_toplevel_icon_set_icon.notify = handle_xdg_toplevel_icon_set_icon; - wl_signal_add(&server->xdg_toplevel_icon_manager->events.set_icon, - &server->xdg_toplevel_icon_set_icon); + server.xdg_toplevel_icon_manager = wlr_xdg_toplevel_icon_manager_v1_create( + server.wl_display, 1); + server.xdg_toplevel_icon_set_icon.notify = handle_xdg_toplevel_icon_set_icon; + wl_signal_add(&server.xdg_toplevel_icon_manager->events.set_icon, + &server.xdg_toplevel_icon_set_icon); - wlr_xdg_wm_dialog_v1_create(server->wl_display, 1); + wlr_xdg_wm_dialog_v1_create(server.wl_display, 1); } void -xdg_shell_finish(struct server *server) +xdg_shell_finish(void) { - wl_list_remove(&server->new_xdg_toplevel.link); - wl_list_remove(&server->xdg_activation_request.link); - wl_list_remove(&server->xdg_activation_new_token.link); - wl_list_remove(&server->xdg_toplevel_icon_set_icon.link); + wl_list_remove(&server.new_xdg_toplevel.link); + wl_list_remove(&server.xdg_activation_request.link); + wl_list_remove(&server.xdg_activation_new_token.link); + wl_list_remove(&server.xdg_toplevel_icon_set_icon.link); } diff --git a/src/xwayland-unmanaged.c b/src/xwayland-unmanaged.c index 00fd2f55..89d40e84 100644 --- a/src/xwayland-unmanaged.c +++ b/src/xwayland-unmanaged.c @@ -19,7 +19,7 @@ handle_grab_focus(struct wl_listener *listener, void *data) unmanaged->ever_grabbed_focus = true; if (unmanaged->node) { assert(unmanaged->xwayland_surface->surface); - seat_focus_surface(&unmanaged->server->seat, + seat_focus_surface(&server.seat, unmanaged->xwayland_surface->surface); } } @@ -34,7 +34,7 @@ handle_request_configure(struct wl_listener *listener, void *data) wlr_xwayland_surface_configure(xsurface, ev->x, ev->y, ev->width, ev->height); if (unmanaged->node) { wlr_scene_node_set_position(unmanaged->node, ev->x, ev->y); - cursor_update_focus(unmanaged->server); + cursor_update_focus(); } } @@ -46,7 +46,7 @@ handle_set_geometry(struct wl_listener *listener, void *data) struct wlr_xwayland_surface *xsurface = unmanaged->xwayland_surface; if (unmanaged->node) { wlr_scene_node_set_position(unmanaged->node, xsurface->x, xsurface->y); - cursor_update_focus(unmanaged->server); + cursor_update_focus(); } } @@ -59,33 +59,35 @@ handle_map(struct wl_listener *listener, void *data) assert(!unmanaged->node); /* Stack new surface on top */ - wl_list_append(&unmanaged->server->unmanaged_surfaces, &unmanaged->link); + wl_list_append(&server.unmanaged_surfaces, &unmanaged->link); CONNECT_SIGNAL(xsurface, unmanaged, set_geometry); if (wlr_xwayland_surface_override_redirect_wants_focus(xsurface) || unmanaged->ever_grabbed_focus) { - seat_focus_surface(&unmanaged->server->seat, xsurface->surface); + seat_focus_surface(&server.seat, xsurface->surface); } - unmanaged->node = &wlr_scene_surface_create( - unmanaged->server->unmanaged_tree, - xsurface->surface)->buffer->node; + struct wlr_scene_surface *scene_surface = wlr_scene_surface_create( + server.unmanaged_tree, xsurface->surface); + die_if_null(scene_surface); + unmanaged->node = &scene_surface->buffer->node; + wlr_scene_node_set_position(unmanaged->node, xsurface->x, xsurface->y); - cursor_update_focus(unmanaged->server); + cursor_update_focus(); } static void -focus_next_surface(struct server *server, struct wlr_xwayland_surface *xsurface) +focus_next_surface(struct wlr_xwayland_surface *xsurface) { /* Try to focus on last created unmanaged xwayland surface */ struct xwayland_unmanaged *u; - struct wl_list *list = &server->unmanaged_surfaces; + struct wl_list *list = &server.unmanaged_surfaces; wl_list_for_each_reverse(u, list, link) { struct wlr_xwayland_surface *prev = u->xwayland_surface; if (wlr_xwayland_surface_override_redirect_wants_focus(prev) || u->ever_grabbed_focus) { - seat_focus_surface(&server->seat, prev->surface); + seat_focus_surface(&server.seat, prev->surface); return; } } @@ -111,8 +113,8 @@ focus_next_surface(struct server *server, struct wlr_xwayland_surface *xsurface) * If modifying this logic, please test for regressions with * menus/tooltips in JetBrains CLion or similar. */ - if (server->active_view) { - seat_focus_surface(&server->seat, server->active_view->surface); + if (server.active_view) { + seat_focus_surface(&server.seat, server.active_view->surface); } } @@ -122,7 +124,7 @@ handle_unmap(struct wl_listener *listener, void *data) struct xwayland_unmanaged *unmanaged = wl_container_of(listener, unmanaged, mappable.unmap); struct wlr_xwayland_surface *xsurface = unmanaged->xwayland_surface; - struct seat *seat = &unmanaged->server->seat; + struct seat *seat = &server.seat; assert(unmanaged->node); wl_list_remove(&unmanaged->link); @@ -136,10 +138,10 @@ handle_unmap(struct wl_listener *listener, void *data) wlr_scene_node_destroy(unmanaged->node); unmanaged->node = NULL; - cursor_update_focus(unmanaged->server); + cursor_update_focus(); - if (seat->seat->keyboard_state.focused_surface == xsurface->surface) { - focus_next_surface(unmanaged->server, xsurface); + if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { + focus_next_surface(xsurface); } } @@ -192,7 +194,6 @@ handle_set_override_redirect(struct wl_listener *listener, void *data) struct xwayland_unmanaged *unmanaged = wl_container_of(listener, unmanaged, set_override_redirect); struct wlr_xwayland_surface *xsurface = unmanaged->xwayland_surface; - struct server *server = unmanaged->server; bool mapped = xsurface->surface && xsurface->surface->mapped; if (mapped) { @@ -200,7 +201,7 @@ handle_set_override_redirect(struct wl_listener *listener, void *data) } handle_destroy(&unmanaged->destroy, NULL); - xwayland_view_create(server, xsurface, mapped); + xwayland_view_create(xsurface, mapped); } static void @@ -213,8 +214,7 @@ handle_request_activate(struct wl_listener *listener, void *data) if (!xsurface->surface || !xsurface->surface->mapped) { return; } - struct server *server = unmanaged->server; - struct seat *seat = &server->seat; + struct seat *seat = &server.seat; /* * Validate that the unmanaged surface trying to grab focus is actually @@ -223,7 +223,7 @@ handle_request_activate(struct wl_listener *listener, void *data) * FIXME: this logic is a bit incomplete/inconsistent. Refer to * https://github.com/labwc/labwc/discussions/2821 for more info. */ - struct view *view = server->active_view; + struct view *view = server.active_view; if (view && view->type == LAB_XWAYLAND_VIEW) { struct wlr_xwayland_surface *surf = wlr_xwayland_surface_try_from_wlr_surface(view->surface); @@ -236,11 +236,9 @@ handle_request_activate(struct wl_listener *listener, void *data) } void -xwayland_unmanaged_create(struct server *server, - struct wlr_xwayland_surface *xsurface, bool mapped) +xwayland_unmanaged_create(struct wlr_xwayland_surface *xsurface, bool mapped) { struct xwayland_unmanaged *unmanaged = znew(*unmanaged); - unmanaged->server = server; unmanaged->xwayland_surface = xsurface; /* * xsurface->data is presumed to be a (struct view *) if set, diff --git a/src/xwayland.c b/src/xwayland.c index 707ab390..5838d412 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -13,6 +13,7 @@ #include "common/array.h" #include "common/macros.h" #include "common/mem.h" +#include "common/scene-helpers.h" #include "config/rcxml.h" #include "config/session.h" #include "foreign-toplevel/foreign.h" @@ -24,20 +25,6 @@ #include "window-rules.h" #include "workspaces.h" -enum atoms { - ATOM_NET_WM_ICON = 0, - - ATOM_COUNT, -}; - -static const char * const atom_names[] = { - [ATOM_NET_WM_ICON] = "_NET_WM_ICON", -}; - -static_assert(ARRAY_SIZE(atom_names) == ATOM_COUNT, "atom names out of sync"); - -static xcb_atom_t atoms[ATOM_COUNT] = {0}; - static void set_surface(struct view *view, struct wlr_surface *surface); static void handle_map(struct wl_listener *listener, void *data); static void handle_unmap(struct wl_listener *listener, void *data); @@ -210,33 +197,6 @@ top_parent_of(struct view *view) return s; } -static void -ensure_initial_geometry_and_output(struct view *view) -{ - if (wlr_box_empty(&view->current)) { - struct wlr_xwayland_surface *xwayland_surface = - xwayland_surface_from_view(view); - view->current.x = xwayland_surface->x; - view->current.y = xwayland_surface->y; - view->current.width = xwayland_surface->width; - view->current.height = xwayland_surface->height; - /* - * If there is no pending move/resize yet, then set - * current values (used in map()). - */ - if (wlr_box_empty(&view->pending)) { - view->pending = view->current; - } - } - if (!output_is_usable(view->output)) { - /* - * Just use the cursor output since we don't know yet - * whether the surface position is meaningful. - */ - view_set_output(view, output_nearest_to_cursor(view->server)); - } -} - static bool want_deco(struct wlr_xwayland_surface *xwayland_surface) { @@ -256,6 +216,74 @@ want_deco(struct wlr_xwayland_surface *xwayland_surface) WLR_XWAYLAND_SURFACE_DECORATIONS_ALL; } +/* + * FIXME: this almost duplicates view_discover_output(), which however + * (1) is private to view.c and (2) uses current (not pending) geometry + */ +static void +set_output_from_pending_geometry(struct view *view) +{ + view_set_output(view, output_nearest_to( + view->pending.x + view->pending.width / 2, + view->pending.y + view->pending.height / 2)); +} + +static void +ensure_initial_geometry_and_output(struct view *view) +{ + struct xwayland_view *xwayland_view = xwayland_view_from_view(view); + if (xwayland_view->initial_geometry_set) { + /* Just make sure we still have an output */ + if (!output_is_usable(view->output)) { + set_output_from_pending_geometry(view); + } + return; + } + xwayland_view->initial_geometry_set = true; + + /* + * Note that wlr_xwayland_surface::x/y/width/height is subject + * to race conditions when wlr_xwayland_surface_configure() is + * called multiple times (e.g. due to client configure-requests), + * as newer values can get stomped on by an older ConfigureNotify. + * To avoid the issue, prefer view->pending when not empty. + */ + struct wlr_xwayland_surface *xsurface = xwayland_surface_from_view(view); + if (wlr_box_empty(&view->pending)) { + view->pending.x = xsurface->x; + view->pending.y = xsurface->y; + view->pending.width = xsurface->width; + view->pending.height = xsurface->height; + } + + /* + * Also set initial output and decoration mode at this point, + * since they affect the geometry calcs. (The decoration mode + * could still change again before map, so this is best-effort.) + */ + bool has_position = xsurface->size_hints && (xsurface->size_hints->flags + & (XCB_ICCCM_SIZE_HINT_US_POSITION | XCB_ICCCM_SIZE_HINT_P_POSITION)); + + if (has_position) { + set_output_from_pending_geometry(view); + } else { + view_set_output(view, output_nearest_to_cursor()); + } + + if (want_deco(xsurface)) { + view_set_ssd_mode(view, LAB_SSD_MODE_FULL); + } else { + view_set_ssd_mode(view, LAB_SSD_MODE_NONE); + } + + view_constrain_size_to_that_of_usable_area(view); + + if (!has_position) { + view_place_by_policy(view, /* allow_cursor */ true, + rc.placement_policy); + } +} + static void handle_commit(struct wl_listener *listener, void *data) { @@ -289,7 +317,7 @@ handle_request_move(struct wl_listener *listener, void *data) * this client, to prevent the client from requesting this whenever they * want. * - * Note: interactive_begin() checks that view == server->grabbed_view. + * Note: interactive_begin() checks that view == server.grabbed_view. */ struct view *view = wl_container_of(listener, view, request_move); interactive_begin(view, LAB_INPUT_STATE_MOVE, LAB_EDGE_NONE); @@ -306,7 +334,7 @@ handle_request_resize(struct wl_listener *listener, void *data) * this client, to prevent the client from requesting this whenever they * want. * - * Note: interactive_begin() checks that view == server->grabbed_view. + * Note: interactive_begin() checks that view == server.grabbed_view. */ struct wlr_xwayland_resize_event *event = data; struct view *view = wl_container_of(listener, view, request_resize); @@ -318,11 +346,53 @@ handle_associate(struct wl_listener *listener, void *data) { struct xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, associate); - assert(xwayland_view->xwayland_surface && - xwayland_view->xwayland_surface->surface); + struct view *view = &xwayland_view->base; + struct wlr_xwayland_surface *xsurface = xwayland_view->xwayland_surface; + assert(xsurface && xsurface->surface); - set_surface(&xwayland_view->base, - xwayland_view->xwayland_surface->surface); + set_surface(view, xsurface->surface); + + /* + * Empirically, the associate event is always seen just after + * map_request but before surface map. Window properties are + * also read by wlroots just before emitting associate. So after + * some trial and error, this seems to be the best place to set + * initial view states and compute initial placement. + */ + ensure_initial_geometry_and_output(view); + + /* + * Per the Extended Window Manager Hints (EWMH) spec: "The Window + * Manager SHOULD honor _NET_WM_STATE whenever a withdrawn window + * requests to be mapped." + * + * The following order of operations is intended to reduce the + * number of resize (Configure) events: + * 1. set fullscreen state + * 2. set decorations (depends on fullscreen state) + * 3. set maximized (geometry depends on decorations) + */ + view_set_fullscreen(view, xsurface->fullscreen); + if (!view->been_mapped) { + if (want_deco(xsurface)) { + view_set_ssd_mode(view, LAB_SSD_MODE_FULL); + } else { + view_set_ssd_mode(view, LAB_SSD_MODE_NONE); + } + } + enum view_axis axis = VIEW_AXIS_NONE; + if (xsurface->maximized_horz) { + axis |= VIEW_AXIS_HORIZONTAL; + } + if (xsurface->maximized_vert) { + axis |= VIEW_AXIS_VERTICAL; + } + view_maximize(view, axis); + + if (window_rules_get_property(view, "allowAlwaysOnTop") == LAB_PROP_TRUE) { + view_set_layer(view, xsurface->above + ? VIEW_LAYER_ALWAYS_ON_TOP : VIEW_LAYER_NORMAL); + } } static void @@ -364,8 +434,10 @@ handle_destroy(struct wl_listener *listener, void *data) wl_list_remove(&xwayland_view->set_override_redirect.link); wl_list_remove(&xwayland_view->set_strut_partial.link); wl_list_remove(&xwayland_view->set_window_type.link); + wl_list_remove(&xwayland_view->set_icon.link); wl_list_remove(&xwayland_view->focus_in.link); - wl_list_remove(&xwayland_view->map_request.link); + + wl_list_remove(&xwayland_view->on_view.always_on_top.link); view_destroy(view); } @@ -385,7 +457,7 @@ xwayland_view_configure(struct view *view, struct wlr_box geo) * workaround, move offscreen surfaces immediately. */ bool is_offscreen = !wlr_box_empty(&view->current) && - !wlr_output_layout_intersects(view->server->output_layout, NULL, + !wlr_output_layout_intersects(server.output_layout, NULL, &view->current); /* If not resizing, process the move immediately */ @@ -431,6 +503,11 @@ handle_request_above(struct wl_listener *listener, void *data) wl_container_of(listener, xwayland_view, request_above); struct view *view = &xwayland_view->base; + if (window_rules_get_property(view, "allowAlwaysOnTop") != LAB_PROP_TRUE) { + wlr_log(WLR_INFO, "X11 client side always on top request rejected"); + return; + } + view_set_layer(view, xwayland_view->xwayland_surface->above ? VIEW_LAYER_ALWAYS_ON_TOP : VIEW_LAYER_NORMAL); } @@ -474,15 +551,6 @@ handle_request_maximize(struct wl_listener *listener, void *data) struct wlr_xwayland_surface *surf = xwayland_surface_from_view(view); if (!view->mapped) { ensure_initial_geometry_and_output(view); - /* - * Set decorations early to avoid changing geometry - * after maximize (reduces visual glitches). - */ - if (want_deco(surf)) { - view_set_ssd_mode(view, LAB_SSD_MODE_FULL); - } else { - view_set_ssd_mode(view, LAB_SSD_MODE_NONE); - } } enum view_axis maximize = VIEW_AXIS_NONE; @@ -567,14 +635,13 @@ handle_set_override_redirect(struct wl_listener *listener, void *data) struct view *view = &xwayland_view->base; struct wlr_xwayland_surface *xsurface = xwayland_view->xwayland_surface; - struct server *server = view->server; bool mapped = xsurface->surface && xsurface->surface->mapped; if (mapped) { handle_unmap(&view->mappable.unmap, NULL); } handle_destroy(&view->destroy, xsurface); /* view is invalid after this point */ - xwayland_unmanaged_create(server, xsurface, mapped); + xwayland_unmanaged_create(xsurface, mapped); } static void @@ -585,35 +652,25 @@ handle_set_strut_partial(struct wl_listener *listener, void *data) struct view *view = &xwayland_view->base; if (view->mapped) { - output_update_all_usable_areas(view->server, false); + output_update_all_usable_areas(false); } } static void -update_icon(struct xwayland_view *xwayland_view) +handle_set_icon(struct wl_listener *listener, void *data) { - if (!xwayland_view->xwayland_surface) { - return; - } + struct xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, set_icon); - xcb_window_t window_id = xwayland_view->xwayland_surface->window_id; - - xcb_connection_t *xcb_conn = wlr_xwayland_get_xwm_connection( - xwayland_view->base.server->xwayland); - xcb_get_property_cookie_t cookie = xcb_get_property(xcb_conn, 0, - window_id, atoms[ATOM_NET_WM_ICON], XCB_ATOM_CARDINAL, 0, 0x10000); - xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_conn, cookie, NULL); - if (!reply) { - return; - } - xcb_ewmh_get_wm_icon_reply_t icon; - if (!xcb_ewmh_get_wm_icon_from_reply(&icon, reply)) { + xcb_ewmh_get_wm_icon_reply_t icon_reply = {0}; + if (!wlr_xwayland_surface_fetch_icon(xwayland_view->xwayland_surface, + &icon_reply)) { wlr_log(WLR_INFO, "Invalid x11 icon"); view_set_icon(&xwayland_view->base, NULL, NULL); goto out; } - xcb_ewmh_wm_icon_iterator_t iter = xcb_ewmh_get_wm_icon_iterator(&icon); + xcb_ewmh_wm_icon_iterator_t iter = xcb_ewmh_get_wm_icon_iterator(&icon_reply); struct wl_array buffers; wl_array_init(&buffers); for (; iter.rem; xcb_ewmh_get_wm_icon_next(&iter)) { @@ -643,7 +700,7 @@ update_icon(struct xwayland_view *xwayland_view) wl_array_release(&buffers); out: - free(reply); + xcb_ewmh_get_wm_icon_reply_wipe(&icon_reply); } static void @@ -652,7 +709,7 @@ handle_focus_in(struct wl_listener *listener, void *data) struct xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, focus_in); struct view *view = &xwayland_view->base; - struct seat *seat = &view->server->seat; + struct seat *seat = &server.seat; if (!view->surface) { /* @@ -672,70 +729,11 @@ handle_focus_in(struct wl_listener *listener, void *data) return; } - if (view->surface != seat->seat->keyboard_state.focused_surface) { + if (view->surface != seat->wlr_seat->keyboard_state.focused_surface) { seat_focus_surface(seat, view->surface); } } -/* - * Sets the initial geometry of maximized/fullscreen views before - * actually mapping them, so that they can do their initial layout and - * drawing with the correct geometry. This avoids visual glitches and - * also avoids undesired layout changes with some apps (e.g. HomeBank). - */ -static void -handle_map_request(struct wl_listener *listener, void *data) -{ - struct xwayland_view *xwayland_view = - wl_container_of(listener, xwayland_view, map_request); - struct view *view = &xwayland_view->base; - struct wlr_xwayland_surface *xsurface = xwayland_view->xwayland_surface; - - if (view->mapped) { - /* Probably shouldn't happen, but be sure */ - return; - } - - /* Keep the view invisible until actually mapped */ - wlr_scene_node_set_enabled(&view->scene_tree->node, false); - ensure_initial_geometry_and_output(view); - - /* - * Per the Extended Window Manager Hints (EWMH) spec: "The Window - * Manager SHOULD honor _NET_WM_STATE whenever a withdrawn window - * requests to be mapped." - * - * The following order of operations is intended to reduce the - * number of resize (Configure) events: - * 1. set fullscreen state - * 2. set decorations (depends on fullscreen state) - * 3. set maximized (geometry depends on decorations) - */ - view_set_fullscreen(view, xsurface->fullscreen); - if (!view->been_mapped) { - if (want_deco(xsurface)) { - view_set_ssd_mode(view, LAB_SSD_MODE_FULL); - } else { - view_set_ssd_mode(view, LAB_SSD_MODE_NONE); - } - } - enum view_axis axis = VIEW_AXIS_NONE; - if (xsurface->maximized_horz) { - axis |= VIEW_AXIS_HORIZONTAL; - } - if (xsurface->maximized_vert) { - axis |= VIEW_AXIS_VERTICAL; - } - view_maximize(view, axis); - view_set_layer(view, xsurface->above - ? VIEW_LAYER_ALWAYS_ON_TOP : VIEW_LAYER_NORMAL); - /* - * We could also call set_initial_position() here, but it's not - * really necessary until the view is actually mapped (and at - * that point the output layout is known for sure). - */ -} - static void check_natural_geometry(struct view *view) { @@ -751,44 +749,6 @@ check_natural_geometry(struct view *view) } } -static void -set_initial_position(struct view *view, - struct wlr_xwayland_surface *xwayland_surface) -{ - /* Don't center views with position explicitly specified */ - bool has_position = xwayland_surface->size_hints && - (xwayland_surface->size_hints->flags & ( - XCB_ICCCM_SIZE_HINT_US_POSITION | - XCB_ICCCM_SIZE_HINT_P_POSITION)); - - if (!has_position) { - view_constrain_size_to_that_of_usable_area(view); - - if (view_is_floating(view)) { - view_place_by_policy(view, - /* allow_cursor */ true, rc.placement_policy); - } else { - /* - * View is maximized/fullscreen. Center the - * stored natural geometry without actually - * moving the view. - */ - view_compute_centered_position(view, NULL, - view->natural_geometry.width, - view->natural_geometry.height, - &view->natural_geometry.x, - &view->natural_geometry.y); - } - } - - /* - * Always make sure the view is onscreen and adjusted for any - * layout changes that could have occurred between map_request - * and the actual map event. - */ - view_adjust_for_layout_change(view); -} - static void set_surface(struct view *view, struct wlr_surface *surface) { @@ -811,41 +771,36 @@ handle_map(struct wl_listener *listener, void *data) { struct view *view = wl_container_of(listener, view, mappable.map); struct xwayland_view *xwayland_view = xwayland_view_from_view(view); - struct wlr_xwayland_surface *xwayland_surface = - xwayland_view->xwayland_surface; - assert(xwayland_surface); - assert(xwayland_surface->surface); - assert(xwayland_surface->surface == view->surface); + assert(view->surface); if (view->mapped) { return; } - /* - * The map_request event may not be received when an unmanaged - * (override-redirect) surface becomes managed. To make sure we - * have valid geometry in that case, call handle_map_request() - * explicitly (calling it twice is harmless). - */ - handle_map_request(&xwayland_view->map_request, NULL); - view->mapped = true; if (!view->content_tree) { view->content_tree = wlr_scene_subsurface_tree_create( view->scene_tree, view->surface); - if (!view->content_tree) { - /* TODO: might need further clean up */ - wl_resource_post_no_memory(view->surface->resource); - return; - } + die_if_null(view->content_tree); + wlr_scene_subsurface_tree_create(&view->capture.scene->tree, view->surface); } wlr_scene_node_set_enabled(&view->content_tree->node, !view->shaded); if (!view->been_mapped) { check_natural_geometry(view); - set_initial_position(view, xwayland_surface); + /* + * view->last_placement might not have been set yet if + * view->pending is unchanged from the surface geometry. + */ + view_save_last_placement(view); + /* + * Make sure the view is onscreen and adjusted for any + * layout changes that could have occurred between + * ensure_initial_geometry_and_output() and map. + */ + view_adjust_for_layout_change(view); /* * When mapping the view for the first time, visual * artifacts are reduced if we display it immediately at @@ -865,7 +820,7 @@ handle_map(struct wl_listener *listener, void *data) */ if (xwayland_view->focused_before_map) { xwayland_view->focused_before_map = false; - seat_focus_surface(&view->server->seat, view->surface); + seat_focus_surface(&server.seat, view->surface); } view_impl_map(view); @@ -928,7 +883,7 @@ xwayland_view_append_children(struct view *self, struct wl_array *children) struct wlr_xwayland_surface *surface = xwayland_surface_from_view(self); struct view *view; - wl_list_for_each_reverse(view, &self->server->views, link) { + wl_list_for_each_reverse(view, &server.views, link) { if (view == self) { continue; } @@ -977,7 +932,7 @@ xwayland_view_set_activated(struct view *view, bool activated) * activated window, if it receives mouse/pointer events over the * parallel wayland connection first. */ - xwayland_flush(view->server); + xwayland_flush(); } static void @@ -1019,14 +974,21 @@ static const struct view_impl xwayland_view_impl = { .get_pid = xwayland_view_get_pid, }; +static void +handle_always_on_top(struct wl_listener *listener, void *data) +{ + struct xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, on_view.always_on_top); + wlr_xwayland_surface_set_above(xwayland_view->xwayland_surface, + xwayland_view->base.layer == VIEW_LAYER_ALWAYS_ON_TOP); +} + void -xwayland_view_create(struct server *server, - struct wlr_xwayland_surface *xsurface, bool mapped) +xwayland_view_create(struct wlr_xwayland_surface *xsurface, bool mapped) { struct xwayland_view *xwayland_view = znew(*xwayland_view); struct view *view = &xwayland_view->base; - view->server = server; view->type = LAB_XWAYLAND_VIEW; view->impl = &xwayland_view_impl; view_init(view); @@ -1041,9 +1003,10 @@ xwayland_view_create(struct server *server, xwayland_view->xwayland_surface = xsurface; xsurface->data = view; - view->workspace = server->workspaces.current; - view->scene_tree = wlr_scene_tree_create( + view->workspace = server.workspaces.current; + view->scene_tree = lab_wlr_scene_tree_create( view->workspace->view_trees[VIEW_LAYER_NORMAL]); + wlr_scene_node_set_enabled(&view->scene_tree->node, false); node_descriptor_create(&view->scene_tree->node, LAB_NODE_VIEW, view, /*data*/ NULL); @@ -1067,14 +1030,24 @@ xwayland_view_create(struct server *server, CONNECT_SIGNAL(xsurface, xwayland_view, set_override_redirect); CONNECT_SIGNAL(xsurface, xwayland_view, set_strut_partial); CONNECT_SIGNAL(xsurface, xwayland_view, set_window_type); + CONNECT_SIGNAL(xsurface, xwayland_view, set_icon); CONNECT_SIGNAL(xsurface, xwayland_view, focus_in); - CONNECT_SIGNAL(xsurface, xwayland_view, map_request); - wl_list_insert(&view->server->views, &view->link); - view->creation_id = view->server->next_view_creation_id++; + /* Events from the view itself */ + CONNECT_SIGNAL(view, &xwayland_view->on_view, always_on_top); + + wl_list_insert(&server.views, &view->link); + view->creation_id = server.next_view_creation_id++; if (xsurface->surface) { handle_associate(&xwayland_view->associate, NULL); + /* + * If a surface is already associated, then we've + * missed the various initial set_* events as well. + */ + handle_set_title(&view->set_title, NULL); + handle_set_class(&xwayland_view->set_class, NULL); + handle_set_icon(&xwayland_view->set_icon, NULL); } if (mapped) { handle_map(&xwayland_view->base.mappable.map, NULL); @@ -1084,8 +1057,6 @@ xwayland_view_create(struct server *server, static void handle_new_surface(struct wl_listener *listener, void *data) { - struct server *server = - wl_container_of(listener, server, xwayland_new_surface); struct wlr_xwayland_surface *xsurface = data; /* @@ -1093,88 +1064,9 @@ handle_new_surface(struct wl_listener *listener, void *data) * but add them to server.unmanaged_surfaces so that we can render them */ if (xsurface->override_redirect) { - xwayland_unmanaged_create(server, xsurface, /* mapped */ false); + xwayland_unmanaged_create(xsurface, /* mapped */ false); } else { - xwayland_view_create(server, xsurface, /* mapped */ false); - } -} - -static struct xwayland_view * -xwayland_view_from_window_id(struct server *server, xcb_window_t id) -{ - struct view *view; - wl_list_for_each(view, &server->views, link) { - if (view->type != LAB_XWAYLAND_VIEW) { - continue; - } - struct xwayland_view *xwayland_view = xwayland_view_from_view(view); - if (xwayland_view->xwayland_surface - && xwayland_view->xwayland_surface->window_id == id) { - return xwayland_view; - } - } - return NULL; -} - -#define XCB_EVENT_RESPONSE_TYPE_MASK 0x7f -static bool -handle_x11_event(struct wlr_xwayland *wlr_xwayland, xcb_generic_event_t *event) -{ - switch (event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) { - case XCB_PROPERTY_NOTIFY: { - xcb_property_notify_event_t *ev = (void *)event; - if (ev->atom == atoms[ATOM_NET_WM_ICON]) { - struct server *server = wlr_xwayland->data; - struct xwayland_view *xwayland_view = - xwayland_view_from_window_id(server, ev->window); - if (xwayland_view) { - update_icon(xwayland_view); - } else { - wlr_log(WLR_DEBUG, "icon property changed for unknown window"); - } - return true; - } - break; - } - default: - break; - } - - return false; -} - -static void -sync_atoms(struct server *server) -{ - xcb_connection_t *xcb_conn = - wlr_xwayland_get_xwm_connection(server->xwayland); - assert(xcb_conn); - - wlr_log(WLR_DEBUG, "Syncing X11 atoms"); - xcb_intern_atom_cookie_t cookies[ATOM_COUNT]; - - /* First request everything and then loop over the results to reduce latency */ - for (size_t i = 0; i < ATOM_COUNT; i++) { - cookies[i] = xcb_intern_atom(xcb_conn, 0, - strlen(atom_names[i]), atom_names[i]); - } - - for (size_t i = 0; i < ATOM_COUNT; i++) { - xcb_generic_error_t *err = NULL; - xcb_intern_atom_reply_t *reply = - xcb_intern_atom_reply(xcb_conn, cookies[i], &err); - if (reply) { - atoms[i] = reply->atom; - wlr_log(WLR_DEBUG, "Got X11 atom for %s: %u", - atom_names[i], reply->atom); - } - if (err) { - atoms[i] = XCB_ATOM_NONE; - wlr_log(WLR_INFO, "Failed to get X11 atom for %s", - atom_names[i]); - } - free(reply); - free(err); + xwayland_view_create(xsurface, /* mapped */ false); } } @@ -1183,128 +1075,76 @@ handle_server_ready(struct wl_listener *listener, void *data) { /* Fire an Xwayland startup script if one (or many) can be found */ session_run_script("xinitrc"); - - struct server *server = - wl_container_of(listener, server, xwayland_server_ready); - sync_atoms(server); } static void handle_xwm_ready(struct wl_listener *listener, void *data) { - struct server *server = - wl_container_of(listener, server, xwayland_xwm_ready); - wlr_xwayland_set_seat(server->xwayland, server->seat.seat); - xwayland_update_workarea(server); + wlr_xwayland_set_seat(server.xwayland, server.seat.wlr_seat); + xwayland_update_workarea(); } void -xwayland_server_init(struct server *server, struct wlr_compositor *compositor) +xwayland_server_init(struct wlr_compositor *compositor) { - server->xwayland = - wlr_xwayland_create(server->wl_display, + server.xwayland = + wlr_xwayland_create(server.wl_display, compositor, /* lazy */ !rc.xwayland_persistence); - if (!server->xwayland) { - wlr_log(WLR_ERROR, "cannot create xwayland server"); - exit(EXIT_FAILURE); + if (!server.xwayland) { + wlr_log(WLR_ERROR, "failed to create xwayland server, continuing without"); + unsetenv("DISPLAY"); + return; } - server->xwayland_new_surface.notify = handle_new_surface; - wl_signal_add(&server->xwayland->events.new_surface, - &server->xwayland_new_surface); + server.xwayland_new_surface.notify = handle_new_surface; + wl_signal_add(&server.xwayland->events.new_surface, + &server.xwayland_new_surface); - server->xwayland_server_ready.notify = handle_server_ready; - wl_signal_add(&server->xwayland->server->events.ready, - &server->xwayland_server_ready); + server.xwayland_server_ready.notify = handle_server_ready; + wl_signal_add(&server.xwayland->server->events.ready, + &server.xwayland_server_ready); - server->xwayland_xwm_ready.notify = handle_xwm_ready; - wl_signal_add(&server->xwayland->events.ready, - &server->xwayland_xwm_ready); + server.xwayland_xwm_ready.notify = handle_xwm_ready; + wl_signal_add(&server.xwayland->events.ready, + &server.xwayland_xwm_ready); - server->xwayland->data = server; - server->xwayland->user_event_handler = handle_x11_event; - - if (setenv("DISPLAY", server->xwayland->display_name, true) < 0) { + if (setenv("DISPLAY", server.xwayland->display_name, true) < 0) { wlr_log_errno(WLR_ERROR, "unable to set DISPLAY for xwayland"); } else { wlr_log(WLR_DEBUG, "xwayland is running on display %s", - server->xwayland->display_name); + server.xwayland->display_name); } struct wlr_xcursor *xcursor; xcursor = wlr_xcursor_manager_get_xcursor( - server->seat.xcursor_manager, XCURSOR_DEFAULT, 1); + server.seat.xcursor_manager, XCURSOR_DEFAULT, 1); if (xcursor) { struct wlr_xcursor_image *image = xcursor->images[0]; - wlr_xwayland_set_cursor(server->xwayland, image->buffer, - image->width * 4, image->width, - image->height, image->hotspot_x, - image->hotspot_y); + struct wlr_buffer *cursor_buffer = wlr_xcursor_image_get_buffer(image); + if (cursor_buffer) { + wlr_xwayland_set_cursor(server.xwayland, cursor_buffer, + image->hotspot_x, image->hotspot_y); + } } } void -xwayland_reset_cursor(struct server *server) +xwayland_server_finish(void) { - /* - * As xwayland caches the pixel data when not yet started up - * due to the delayed lazy startup approach, we do have to - * re-set the xwayland cursor image. Otherwise the first X11 - * client connected will cause the xwayland server to use - * the cached (and potentially destroyed) pixel data. - * - * Calling this function after reloading the cursor theme - * ensures that the cached pixel data keeps being valid. - * - * To reproduce: - * - Compile with b_sanitize=address,undefined - * - Start labwc (nothing in autostart that could create - * a X11 connection, e.g. no GTK or X11 application) - * - Reconfigure - * - Start some X11 client - */ + struct wlr_xwayland *xwayland = server.xwayland; - if (!server->xwayland) { + if (!xwayland) { return; } - struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor( - server->seat.xcursor_manager, XCURSOR_DEFAULT, 1); - - if (xcursor && !server->xwayland->xwm) { - /* Prevents setting the cursor on an active xwayland server */ - struct wlr_xcursor_image *image = xcursor->images[0]; - wlr_xwayland_set_cursor(server->xwayland, image->buffer, - image->width * 4, image->width, - image->height, image->hotspot_x, - image->hotspot_y); - return; - } - - if (server->xwayland->cursor) { - /* - * The previous configured theme has set the - * default cursor or the xwayland server is - * currently running but still has a cached - * xcursor set that will be used on the next - * xwayland destroy -> lazy startup cycle. - */ - zfree(server->xwayland->cursor); - } -} - -void -xwayland_server_finish(struct server *server) -{ - struct wlr_xwayland *xwayland = server->xwayland; - wl_list_remove(&server->xwayland_new_surface.link); - wl_list_remove(&server->xwayland_server_ready.link); - wl_list_remove(&server->xwayland_xwm_ready.link); + wl_list_remove(&server.xwayland_new_surface.link); + wl_list_remove(&server.xwayland_server_ready.link); + wl_list_remove(&server.xwayland_xwm_ready.link); /* - * Reset server->xwayland to NULL first to prevent callbacks (like + * Reset server.xwayland to NULL first to prevent callbacks (like * server_global_filter) from accessing it as it is destroyed */ - server->xwayland = NULL; + server.xwayland = NULL; wlr_xwayland_destroy(xwayland); } @@ -1397,18 +1237,18 @@ xwayland_adjust_usable_area(struct view *view, struct wlr_output_layout *layout, } void -xwayland_update_workarea(struct server *server) +xwayland_update_workarea(void) { /* * Do nothing if called during destroy or before xwayland is ready. * This function will be called again from the ready signal handler. */ - if (!server->xwayland || !server->xwayland->xwm) { + if (!server.xwayland || !server.xwayland->xwm) { return; } struct wlr_box lb; - wlr_output_layout_get_box(server->output_layout, NULL, &lb); + wlr_output_layout_get_box(server.output_layout, NULL, &lb); /* Compute outer edges of layout (excluding negative regions) */ int layout_left = MAX(0, lb.x); @@ -1423,13 +1263,13 @@ xwayland_update_workarea(struct server *server) int workarea_bottom = layout_bottom; struct output *output; - wl_list_for_each(output, &server->outputs, link) { + wl_list_for_each(output, &server.outputs, link) { if (!output_is_usable(output)) { continue; } struct wlr_box ob; - wlr_output_layout_get_box(server->output_layout, + wlr_output_layout_get_box(server.output_layout, output->wlr_output, &ob); /* Compute edges of output */ @@ -1472,15 +1312,15 @@ xwayland_update_workarea(struct server *server) .width = workarea_right - workarea_left, .height = workarea_bottom - workarea_top, }; - wlr_xwayland_set_workareas(server->xwayland, &workarea, 1); + wlr_xwayland_set_workareas(server.xwayland, &workarea, 1); } void -xwayland_flush(struct server *server) +xwayland_flush(void) { - if (!server->xwayland || !server->xwayland->xwm) { + if (!server.xwayland || !server.xwayland->xwm) { return; } - xcb_flush(wlr_xwayland_get_xwm_connection(server->xwayland)); + xcb_flush(wlr_xwayland_get_xwm_connection(server.xwayland)); } diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap index 25a947ed..eb797ad5 100644 --- a/subprojects/wlroots.wrap +++ b/subprojects/wlroots.wrap @@ -1,7 +1,7 @@ [wrap-git] url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = 0.19 +revision = 0.20 [provide] -dependency_names = wlroots-0.19 -wlroots-0.19=wlroots +dependency_names = wlroots-0.20 +wlroots-0.20=wlroots