diff --git a/.clang-format b/.clang-format index b3b6af52..a8e09433 100644 --- a/.clang-format +++ b/.clang-format @@ -12,21 +12,14 @@ UseTab: Always IndentWidth: 8 ContinuationIndentWidth: 8 AlignAfterOpenBracket: DontAlign -AlignOperands: false AlwaysBreakAfterDefinitionReturnType: true BreakBeforeBinaryOperators: NonAssignment BreakBeforeBraces: Linux IndentCaseLabels: false -PenaltyBreakOpenParenthesis: 100 -PenaltyReturnTypeOnItsOwnLine: 500 SpaceBeforeParens: ControlStatementsExceptControlMacros ForEachMacros: ['for_each_view', - 'for_each_view_reverse', 'wl_array_for_each', 'wl_list_for_each', 'wl_list_for_each_reverse', 'wl_list_for_each_reverse_safe', 'wl_list_for_each_safe'] -IncludeCategories: - - Regex: '<.*>' - - Regex: '.*' diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b7329c7b..892d8867 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,8 +17,6 @@ on: - 'src/**' - 'include/**' - 'protocols/**' - - 'clients/**' - - 't/**' - 'scripts/**' - '.github/workflows/**' @@ -84,7 +82,7 @@ jobs: pacman -Syu --noconfirm pacman -S --noconfirm git meson clang wlroots0.19 libdrm libinput \ wayland-protocols cairo pango libxml2 xorg-xwayland librsvg \ - libdisplay-info gdb ttf-dejavu foot libsfdo cmocka + libdisplay-info gdb ttf-dejavu foot libsfdo - name: Install Debian Testing dependencies if: matrix.name == 'Debian' @@ -94,7 +92,7 @@ jobs: 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 build-dep -y libwlroots-0.18-dev - name: Install FreeBSD dependencies if: matrix.name == 'FreeBSD' @@ -105,8 +103,7 @@ jobs: sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf pkg set -yn pkg:mesa-dri # hack to skip llvm dependency pkg install -y git meson gcc pkgconf cairo pango evdev-proto \ - hwdata wayland-protocols libdisplay-info libepoll-shim \ - wlroots019 + hwdata wayland-protocols wlroots019 libdisplay-info run: echo "setup done" - name: Install Void Linux dependencies @@ -170,7 +167,6 @@ jobs: meson setup build-gcc-release -Dxwayland=enabled \ -Dbuildtype=release -Db_ndebug=true --werror meson configure build-gcc-release -Dwlroots:b_ndebug=false || true - meson configure build-gcc-release -Dlibsfdo:b_ndebug=false || true meson compile -C build-gcc-release ' | $TARGET @@ -193,7 +189,6 @@ jobs: meson setup build-clang-release -Dxwayland=enabled \ -Dbuildtype=release -Db_ndebug=true --werror meson configure build-clang-release -Dwlroots:b_ndebug=false || true - meson configure build-clang-release -Dlibsfdo:b_ndebug=false || true meson compile -C build-clang-release ' | $TARGET @@ -209,18 +204,6 @@ jobs: meson compile -C build-gcc-no-feature ' | $TARGET - # Unit tests, run on Arch only - - name: Build with gcc - unit test - if: matrix.name == 'Arch' - run: | - echo ' - cd "$GITHUB_WORKSPACE" - export CC=gcc - meson setup build-gcc-unit-test -Dtest=enabled --werror - meson compile -C build-gcc-unit-test - meson test -C build-gcc-unit-test --print-errorlogs - ' | $TARGET - # Runtime tests, these run on Arch and Void only (the later due to libmusl being used) - name: Build with gcc - runtime test if: matrix.name == 'Arch' diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1a2251ad..37cfa4e4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,11 +10,9 @@ - [4.2 Devault Deviations](#devault-deviations) - [4.3 Labwc Specifics](#labwc-specifics) - [4.3.1 API](#api) - - [4.3.2 The Use of GLib](#the-use-of-glib) - - [4.3.3 The Use of GNU Extensions](#the-use-of-gnu-extensions) + - [4.3.2 The Use of glib](#the-use-of-glib) + - [4.3.3 The use of GNU extensions](#the-use-of-gnu-extensions) - [4.3.4 Naming Conventions](#naming-conventions) - - [4.3.5 Switch Statements with Variable Declarations](#switch-statements-with-variable-declarations) - - [4.3.6 Order of #includes](#order-of-includes) - [5. Commit Messages](#commit-messages) - [6. Unit Tests](#unit-tests) - [7. Submitting Patches](#submitting-patches) @@ -23,14 +21,14 @@ # How to Contribute -1. Report bugs as GitHub issues. We use a template prompting you to provide +1. Report bugs as github issues. We use a template prompting you to provide some sensible information such as what happened, what you expected to happen - and steps to reproduce. If applicable try with default configuration. If - you are able to, try debugging (guidelines below). + and steps to reproduce. If applicable try with default configuration. If + you are able to, try to do some debugging (guidelines below). -2. Submit patches as GitHub pull requests. If you wish to introduce significant +2. Submit patches as github pull-requests. If you wish to introduces significant changes or new features, consult the [scope document], discuss on IRC or via - a GitHub issue first. + a github issue first. # Debugging @@ -244,21 +242,21 @@ We have a very small, modest API and encourage you to use it. [common/array.h]: https://github.com/labwc/labwc/blob/master/include/common/array.h [common/macros.h]: https://github.com/labwc/labwc/blob/master/include/common/macros.h -### The Use of GLib +### The Use of glib -We try to keep the use of GLib pretty minimal for the following reasons: +We try to keep the use of glib pretty minimal for the following reasons: -- The use of GLib has been known to make AddressSanitiser diagnose false +- The use of glib has been known to make AddressSanitiser diagnose false positives and negatives. -- Log messages coming from GLib functions look inconsistent. -- The use of GLib functions, naming-conventions and iterators in a codebase +- Log messages coming from glib functions look inconsistent. +- The use of glib functions, naming-conventions and iterators in a code base that is predominantly ANSI C creates a clash which makes readability and maintainability harder. - Mixing gmalloc()/malloc() and respective free()s can create problems with memory pools [^1] -Having said that, with our use of cairo and pango we depend on glib-2.0 anyway, -so linking with it and making use of some of its helper functions comes for free, +Having said that, with our use of cairo and pango we depend on glib-2.0 anyway +so linking with it and making use of some of its helper functions comes for free and can keep the code simpler. For example, if we were going to carry out extensive string manipulation, @@ -274,7 +272,7 @@ devs: - `g_pattern_match_simple()` When using these types of functions it is often desirable to support with some -GLib code, which is okay provided it is kept local and self-contained. See +glib code, which is okay provided it is kept local and self-contained. See example from `src/theme.c`: ``` @@ -289,9 +287,9 @@ match(const gchar *pattern, const gchar *string) } ``` -### The Use of GNU Extensions +### The use of GNU extensions -We avoid [GNU C extensions] because we want to fit into the ecosystem +We avoid [GNU C extensions] because we want to fit into the eco-system (wayland and wlroots) we live in. We do use `__typeof__` which strictly speaking is a GNU C extension (`typeof`) @@ -300,18 +298,18 @@ but through the use of `__` is supported by gcc and clang without defining in the [`wl_container_of()`] macro which is needed in `wl_list*` and it does provide pretty big benefits in terms of type safety. -We compile with `-std=c11` because that's what 'wlroots' uses, and we do not +We compile with `-std=c11` because that's what 'wlroots' uses and we do not want to increase the entry-level for OSs without good reason (and currently we can't think of one). ### Naming Conventions -There are three types of coordinate systems: surface, output, and layout — for +There are three types of coordinate systems: surface, output and layout - for which the variables (sx, sy), (ox, oy) and (lx, ly) are used respectively in line with wlroots. With the introduction of the scene-graph API, some wlroots functions also use -node coordinates (nx, ny), but we prefer (sx, sy) where possible. +node coordinates (nx, ny) but we prefer (sx, sy) where possible. We do not worry about namespace issues too much and we try to not make the code a pain to use just to uniquify names. If we were writing a library we would @@ -325,7 +323,7 @@ We use the prefix `handle_` for signal-handler-functions in order to be consistent with sway and rootston. For example `view->request_resize.notify = handle_request_resize` -### Switch Statements with Variable Declarations +### Switch statements with variable declarations Unlike many modern languages, C doesn't create a new scope after `case FOO:`. Therefore, we wrap codes following `case FOO:` that include variable @@ -351,26 +349,6 @@ case BAZ: But please also consider refactoring the code into a separate function if it becomes lengthy. -### Order of #includes - -In new files, please order `#include` lines as follows: - -- In each `.c` file, first include the matching `.h` file, if there is - one. For example, `#include "common/font.h"` should come first in - `src/common/font.c`. This practice helps to ensure that each header - compiles cleanly on its own, without implicit dependencies on other - headers being included first. - -- Then list any "system" headers (those not part of labwc) in alphabetical - order, using angle brackets. This includes 3rd-party library headers - such as ``, as well as wlroots headers. - -- Then list any other labwc headers in alphabetical order, using quotation - marks and relative to the `include/` folder. Subfolders below `include/`, - such as `common/`, should be specified even when including one header - from another in the same folder (for example, `#include "common/buf.h"` - from `include/common/grab-file.h`). - # Commit Messages The log messages that explain changes are just as important as the changes @@ -388,11 +366,11 @@ This first line should: - In most cases be prefixed with "area: " where area refers to a filename or identifier for the general area of the code being modified. - Not capitalize the first word following the "area: " prefix, unless - it's a name, acronym, or similar. + it's a name, acronym or similar. - Skip the full stop And please wrap the commit message at max 74 characters, otherwise `git log` -and similar look very weird. URLs and other references are exempt. +and similar look so weird. URLs and other references are exempt. # Unit Tests @@ -404,9 +382,9 @@ In the bigger scheme of validating that the compositor meets users' needs, unit tests do not contribute a great deal. However, they have a role to play in providing some verification that stand-alone functions behave as expected. -On this project, writing unit-tests is not compulsory, nor do we measure +On this project, writing unit-tests is not compulsory nor do we measure coverage. The inclusion of the t/ directory does not signify a move towards -test-driven development. We intend to use unit tests sparingly, and only when +test-driven development. We intend to use unit tests sparingly and only when devs find them useful. ## Usage @@ -432,17 +410,17 @@ and use the web interface. Adding new languages should work, otherwise the administrators can be contacted. Suggestions for improving existing translations can be added without account. -### GitHub Pull Request +### Github Pull Request Translators can add their `MY_LOCALE.po` files to the `po` directory -based on `po/labwc.pot`, and issue a pull request. To do this they can +based on `po/labwc.pot` and issue a pull request. To do this they can generate their `MY_LOCALE.po` file in a few steps: 1. Edit the `po/LINGUAS` file to add their locale code in English alphabetical order to the field of locale codes. 2. Copy the `po/labwc.pot` to `po/MY_LOCALE.po` 3. Edit the newly generated `MY_LOCALE.po` file with some of their -contact and locale details in the header of the file. Then, add the +contact and locale details in the header of the file then add the translation strings under each English string. [See this tutorial for further guidance](https://www.labri.fr/perso/fleury/posts/programming/a-quick-gettext-tutorial.html) @@ -483,13 +461,13 @@ follow the steps to be taken: 2. Update `NEWS.md` with the release details and run `git commit -m 'NEWS.md: update notes for X.Y.Z'` Note: If new dependencies are needed, make this clear. -3. In `meson.build`, update the version, and (if required) the wlroots +3. In `meson.build` update the version and (if required) the wlroots dependency version. Then run `git commit -m 'build: bump version to X.Y.Z'` 4. Run `git tag -a X.Y.Z`. The first line of the commit message should be "labwc X.Y.Z" and the body should be the `NEWS.md` additions removing hash characters (#) from the headings as these will otherwise be ignored by git. -5. On GitHub, create a 'Release' as some distros use this as a trigger. Set it +5. On github, create a 'Release' as some distros use this as a trigger. Set it as 'latest release'. [scope document]: https://github.com/labwc/labwc-scope#readme @@ -503,10 +481,11 @@ follow the steps to be taken: [GNU C extensions]: https://gcc.gnu.org/onlinedocs/gcc/C-Extensions.html [`wl_container_of()`]: https://github.com/wayland-project/wayland/blob/985ab55d59db45ea62795c76dff5949343e86b2f/src/wayland-util.h#L409 -[^1]: The reference documentation for GLib notes that: +[^1]: The reference documentation for glib notes that: "It's important to match g_malloc() with g_free(), plain malloc() with free(), and (if you're using C++) new with delete and new[] with delete[]. Otherwise bad things can happen, since these allocators may use different memory pools (and new/delete call constructors and destructors)." - See: https://docs.gtk.org/glib/memory.html + See: https://developer.gimp.org/api/2.0/glib/glib-Memory-Allocation.html + diff --git a/NEWS.md b/NEWS.md index e7d8b383..46b27395 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,8 +9,6 @@ The format is based on [Keep a Changelog] | Date | All Changes | wlroots version | lines-of-code | |------------|---------------|-----------------|---------------| -| 2025-10-10 | [0.9.2] | 0.19.1 | 28818 | -| 2025-08-02 | [0.9.1] | 0.19.0 | 28605 | | 2025-07-11 | [0.9.0] | 0.19.0 | 28586 | | 2025-05-02 | [0.8.4] | 0.18.2 | 27679 | | 2025-02-21 | [0.8.3] | 0.18.2 | 27671 | @@ -38,58 +36,13 @@ The format is based on [Keep a Changelog] | 2021-04-15 | [0.2.0] | 0.13.0 | 5011 | | 2021-03-05 | [0.1.0] | 0.12.0 | 4627 | -[unreleased]: NEWS.md#unreleased -[0.9.2]: NEWS.md#092---2025-10-10 -[0.9.1]: NEWS.md#091---2025-08-02 -[0.9.0]: NEWS.md#090---2025-07-11 -[0.8.4]: NEWS.md#084---2025-05-02 -[0.8.3]: NEWS.md#083---2025-02-21 -[0.8.2]: NEWS.md#082---2024-12-13 -[0.8.1]: NEWS.md#081---2024-10-25 -[0.8.0]: NEWS.md#080---2024-08-16 -[0.7.4]: NEWS.md#074---2024-06-19 -[0.7.3]: NEWS.md#073---2024-06-12 -[0.7.2]: NEWS.md#072---2024-05-10 -[0.7.1]: NEWS.md#071---2024-03-01 -[0.7.0]: NEWS.md#070---2023-12-22 -[0.6.6]: NEWS.md#065---2023-11-25 -[0.6.5]: NEWS.md#064---2023-09-23 -[0.6.4]: NEWS.md#063---2023-07-14 -[0.6.3]: NEWS.md#062---2023-05-08 -[0.6.2]: NEWS.md#061---2023-03-20 -[0.6.1]: NEWS.md#060---2023-01-29 -[0.6.0]: NEWS.md#060---2022-11-17 -[0.5.3]: NEWS.md#053---2022-07-15 -[0.5.2]: NEWS.md#052---2022-05-17 -[0.5.1]: NEWS.md#051---2022-04-08 -[0.5.0]: NEWS.md#050---2022-02-18 -[0.4.0]: NEWS.md#040---2021-12-31 -[0.3.0]: NEWS.md#030---2021-06-28 -[0.2.0]: NEWS.md#020---2021-04-15 -[0.1.0]: NEWS.md#010---2021-03-05 +## [0.9.0] -## Notes on wlroots-0.19 +The main focus has been to port labwc to wlroots 0.19 [#2388] and fix associated +issues. Special thanks to @Consolatis @jlindgren90 for this. -There are some regression warnings worth noting for the switch to wlroots 0.19: +There is a regression warning worth noting for the switch to wlroots 0.19: -- The DRM backend now destroys/recreates outputs on VT switch and in some cases - on suspend/resume too. The reason for this change was that (i) the KMS state - is undefined when a VT is switched away; and (ii) the previous outputs had - issues with restoration, particularly when the output configuration had - changed whilst switched away. This change causes two issues for users: - - Some layer-shell clients do not re-appear on output re-connection, or may - appear on a different output. Whilst this has always been the case, it will - now also happen in said situations. We recommend layer-shell clients to - handle the new-output and surface-destroy signals to achieve desired - behaviours. - - Some Gtk clients issue critical warnings as they assume that at least one - output is always available. This will be fixed in `Gtk-3.24.50`. It is - believed to be a harmless warning, but it can be avoided by running labwc - with the environment variable `LABWC_FALLBACK_OUTPUT=NOOP-fallback` to - temporarily create a fallback-output when the last physical display - disconnects. [#2914] [#2939] [wlroots-4878] [gtk-8792] -- Due to a single-pixel protocol issue, `waylock` and `chayang` do not work. - This will be fixed in `wlroots-0.19.1`. [#2943] [wlroots-5098] - Menu item can no longer be activated in any Gtk applications with a single press-drag-release mouse action. For context: This is due to ambiguity in the specifications and contrary implementations. For example, Gtk applications are @@ -101,204 +54,6 @@ There are some regression warnings worth noting for the switch to wlroots 0.19: around a bug on the wlroots side which is expected to be fixed in wlroots `0.19.1` [#2887] -With wlroots compiled with libwayland (>= 1.24.0), there is an invisible margin -preventing pointer focus on some layer-shell surfaces including those created by -Gtk. In simple words, this is because libwayland now rounds floats a bit -differently [#3099]. There is a pending fix [wlroots-5159]. - -[wlroots-4878]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4878 -[wlroots-5098]:https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5098 -[wlroots-5159]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5159 -[gtk-8792]: https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/8792 - -## unreleased - -[unreleased-commits] - -### Added - -- With the window-switcher custom field state specifiers 's' and 'S', show 's' - for shaded window @domo141 [#2895] -- Support `xdg-dialog` protocol to enable better handling of modal dialogs @xi - [#3134] -- labnag: add --keyboard-focus option @tokyo4j [#3120] -- Allow window switcher to temporarily unshade windows using config option - `` @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` - @tokyo4j [#3118] - -### Fixed - -- Don't remove newlines when parsing config, menu and XBM because doing so can - cause parser error in some unusual situations like the one shown below. - @tokyo4j [#3148] - -``` - -``` - -### Changed - -- If XML documents (like rc.xml and menu.xml) have an XML declaration (typically - ``), this XML declaration must be the first thing in the - document. In previous versions, line breaks (`\n`) were allowed before due to - the way the files were parsed, but this is approach caused other issues like - [#3145] and is contrary to XML syntax. [#3148] [#3153] -- With the window-switcher custom field state specifiers 's' and 'S', change the - display order from M|m|F to m|s|M|F; and increase the size from three - characters wide to four. @domo141 [#2895] -- Call labnag with on-demand keyboard interactivity by default @tokyo4j [#3120] -- Temporarily unshade windows when switching windows. Restore old behaviour with - `` @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] - -## 0.9.2 - 2025-10-10 - -[0.9.2-commits] - -### Added - -- Allow `SnapToEdge` and `ToggleSnapToEdge` to combine two cardinal directions - with the config option `combine="yes|no"`. [#3081] @tokyo4j -- Support `Border` context for mousebinds as an alias for `Top`...`BRCorner` to - make configuration easier. @tokyo4j [#3047] -- Add window-switcher mode with thumbnails. This can be enabled with: - ``. @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 - when window is maximized. @CosmicFusion @tokyo4j [#3015] -- Use client-send-to-menu as 'Workspace' submenu in built-in client-menu - @johanmalm [#2995] -- Allow overwriting submenu icon to increase flexibility and enhance Openbox - compatibility. @tokyo4j [#2998] -- Allow client-{list-combined,send-to}-menu as submenu of static menu @tokyo4j - [#2994] -- Add `labnag` (a dialog client with message and buttons) and associated - `` option in 'If' actions. @johanmalm @Consolatis @tokyo4j [#2699] -- Support config option `` @johanmalm [#3097] -- Allow snapping to corner edges during interactive move with associated config - options ``. @tokyo4j [#2885] -- Support new values "up-left", "up-right", "down-left" and "down-right" with - `` and - ``. @tokyo4j [#2885] -- XML parsing improvements as listed below. @tokyo4j [#2667] [#2967] [#2971] - - Support nested `If` and `ForEach` actions - - Parse CDATA as text all nodes - - Remove ordering constraint of attributes in ``, `` and - `` - - `If` actions now works for menus - - For menus, the `name` argument no longer has to be the first argument of - ``; and the `label` argument no longer has to be the first argument - of `` -- Toggle mousebinds with the `ToggleKeybinds` action @tokyo4j [#2942] -- Add support for direction value 'any' with tiled queries. This allows users - to query for any snap directions without using multiple query statements - @lynxy [#2883] - -### Fixed - -- On detecting broken icon theme, fall back on 'hicolor' @Consolatis [#3126] -- Restore initially-maximized window position after unplug/plug @tokyo4j [#3042] -- Fix large client-side icon not being loaded when the rendered icon size is - larger than icon sizes from the client. @tokyo4j [#3033] -- Improve debug logging for configuring input devices @jlindgren90 [#3028] -- Fix false positives when matching desktop entries @datMaffin [#3004] -- Prevent accidental downcasting of scale in scaled-icon-buffer to avoid blurry - icons on non-integer scales and a cairo assert when using a output scale < 1. - @Consolatis #2984 -- Fix xdg-shell windows moving between outputs due to configure timeout - @jlindgren90 [#2976] -- Fix segfault with toplevel `` in `menu.xml` @tokyo4j [#2970] -- Prevent hi-res mice triggering scroll actions too often @tokyo4j [#2933] - -### Changed - -- 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 - `combine="no"` as shown below. [#3081] @tokyo4j - -``` - - - - - - - - - - - - -``` - -- `Focus` and `Raise` on window border press because it is probably what most - people expect and it makes the behavior consistent with that of Openbox. - @johanmalm [#3039] [#3049] -- On interactive resize, only un-maximize the axis/axes that are being resized. - @jlindgren90 [#3043] -- Change theme setting `osd.window-switcher.*` to - `osd.window-switcher.style-classic.*`. Backward compatibility is preserved. - @tokyo4j [#2981] -- In client-list menu, add brackets around the titles of any minimised windows - @davidphilipbarr [#3002] -- Respect client-initiated window resize of non-maximized axis, for example - remember the width of vertically-maximized window resizing itself - horizontally. @jlindgren90 [#3020] -- Remember position of window along non-maximized axis during interactive move. - @jlindgren90 [#3020] -- Restore default libinput device values on reconfigure with empty value, rather - than leaving the old configuration. This makes rc.xml more declarative. - @tokyo4j [#3011] -- Change `If` action when used without a focused window to execute the `` - branch (previously it was just ignored). The reason for this is to make things - more consistent with ``. It is not anticipated that this will affect - anyone's workflow but is mentioned here for completeness. -- Make `autoEnableOutputs=no` apply only to drm outputs @jlindgren90 [#2972] -- Take into account `` for edge and region overlays @tokyo4j [#2965] - -## 0.9.1 - 2025-08-02 - -[0.9.1-commits] - -This is an earlier-than-usual release containinig bug fixes only. It has been -done on a separate branch to avoid the inclusion of refactoring and new -features. - -``` - 0.9.1 <--- bug-fixes only - / - / -0.8.4--------0.9.0-------- <-- master -``` - -### Fixed - -- Prevent interaction with un-initialized xdg-shell windows after unmap to fix a - bug exposed by `wlroots-0.19.0` resulting in a compositor crash in certain - (unusual) circumstances [#2948] [#2937] [#2944] @Consolatis -- Fix double-free in `img_svg_render()` failure path [#2910] @jlindgren90 -- Fix swapped width/height in XWayland client `_NET_WM_ICON` stride calculation - [#2909] @jlindgren90 - -## 0.9.0 - 2025-07-11 - -[0.9.0-commits] - -The main focus has been to port labwc to wlroots 0.19 [#2388] and fix associated -issues. Special thanks to @Consolatis @jlindgren90 for this. - ### Added - Add client `lab-sensible-terminal` and add a `Terminal` entry to the default @@ -415,9 +170,7 @@ window.*.title.bg.colorTo.splitTo: ``` -## 0.8.4 - 2025-05-02 - -[0.8.4-commits] +## [0.8.4] This release predominantly consists of bug-fixes, code simplification and usability improvements. Amongst the new features the most noteworthy is the @@ -481,9 +234,7 @@ release. - Increase default `` to 10 to make it easier to snap windows on the edge between two monitors. @johanmalm [#2602] [#2608] -## 0.8.3 - 2025-02-21 - -[0.8.3-commits] +## [0.8.3] The eye-catching new features of this release are undoubtedly: 1. Support for the `ext-workspace` protocol with big thanks to @Consolatis @@ -601,9 +352,7 @@ Notes to package maintainers: and followMouseRequiresMovement="no" in rc.xml, keyboard-focus semantics have subtly changed when using the window-switcher. [#2455] -## 0.8.2 - 2024-12-13 - -[0.8.2-commits] +## [0.8.2] This is a shorter release cycle compared with the usual 10-week one because it contains a significant number of stability and cleanliness fixes which warrant @@ -742,9 +491,7 @@ menu.border.color: #aaaaaa Openbox's behavior and (ii) behave as already described in our own documentation. [#2380] -## 0.8.1 - 2024-10-25 - -[0.8.1-commits] +## [0.8.1] The most noteworthy additions in this release are: @@ -892,9 +639,7 @@ window.inactive.button.shade.unpressed.image.color per-device configuration of scroll factor (e.g. setting different scroll factors for mice and touchpads). [#2057] -## 0.8.0 - 2024-08-16 - -[0.8.0-commits] +## [0.8.0] The main focus in this release has been to port labwc to wlroots 0.18 and to grind out associated regressions. Nonetheless, it contains a few non-related @@ -975,9 +720,7 @@ have been attributed with a 'Written-by' against each relevant log entry. - Make windows stay fullscreen when associated output is disconnected. [#2040] -## 0.7.4 - 2024-06-19 - -[0.7.4-commits] +## [0.7.4] ### Fixed @@ -985,9 +728,7 @@ have been attributed with a 'Written-by' against each relevant log entry. - Fix magnifier by disabling direct scanout when active. [#1989] - Fix crash triggered by pipemenu without parent `` element. [#1988] -## 0.7.3 - 2024-06-12 - -[0.7.3-commits] +## [0.7.3] Following a couple of big releases, this one feels like more steady with lots of focus on bug fixes and stability. In terms of new features the most noteworthy @@ -1076,9 +817,7 @@ joint effort by @spl237 and @Consolatis. - Action `MoveToCursor` is deprecated in favour of: ``. -## 0.7.2 - 2024-05-10 - -[0.7.2-commits] +## [0.7.2] This release shaped up to be the second in a row that is larger than usual in terms of both fixes and new features. Significant additions include @@ -1346,9 +1085,7 @@ osd.window-switcher.preview.border.color: #ffffff,#00a2ff,#ffffff the DRM backend or by explicit request using environment variable `LABWC_UPDATE_ACTIVATION_ENV`. -## 0.7.1 - 2024-03-01 - -[0.7.1-commits] +## [0.7.1] ### Added @@ -1519,9 +1256,7 @@ osd.window-switcher.preview.border.color: #ffffff,#00a2ff,#ffffff full `app_id`. Consult the labwc-config(5) manual page for more details. [#1309] -## 0.7.0 - 2023-12-22 - -[0.7.0-commits] +## [0.7.0] - 2023-12-22 The main effort in this release has gone into porting labwc to wlroots 0.17 and tidying up regressions. Nonetheless, it contains a significant number of @@ -1581,9 +1316,7 @@ Should bug fixes be required against `0.6.6` (built with wlroots `0.16`), a - Use the GTK3 notebook header color as the default active title color (small change from `#dddad6` to `#e1dedb`). Written-by: @dimkr -## 0.6.6 - 2023-11-25 - -[0.6.6-commits] +## [0.6.6] - 2023-11-25 We do not normally call out contributions by core devs in the changelog, but a special thanks goes to @jlindgren90 in this release for lots of work @@ -1697,9 +1430,7 @@ relating to surface focus and keyboard issues, amongst others. will not appear anymore. - Always switch to the workspace containing the view being focused. -## 0.6.5 - 2023-09-23 - -[0.6.5-commits] +## [0.6.5] - 2023-09-23 ### Added @@ -1775,9 +1506,7 @@ relating to surface focus and keyboard issues, amongst others. - Do not expand environment variables in `Exec` action `` argument (but still resolve tilde). -## 0.6.4 - 2023-07-14 - -[0.6.4-commits] +## [0.6.4] - 2023-07-14 ### Added @@ -1818,9 +1547,7 @@ relating to surface focus and keyboard issues, amongst others. - Make `ToggleKeybinds` applicable only to the window that has keyboard focus when the action is executed. -## 0.6.3 - 2023-05-08 - -[0.6.3-commits] +## [0.6.3] - 2023-05-08 ### Added @@ -1892,9 +1619,7 @@ relating to surface focus and keyboard issues, amongst others. - Default to follow="true" for SendToDesktop action as per Openbox 3.6 specification. -## 0.6.2 - 2023-03-20 - -[0.6.2-commits] +## [0.6.2] - 2023-03-20 This release contains refactoring and simplification relating to view-output association and xdg/xwayland configure/map events. @@ -1956,9 +1681,7 @@ Unless otherwise stated all contributions are by the core-devs ``` -## 0.6.1 - 2023-01-29 - -[0.6.1-commits] +## [0.6.1] - 2023-01-29 As usual, this release contains lots of refactoring and bug fixes with particular thanks going to @Consolatis, @jlindgren90, @bi4k8, @Flrian and @@ -2021,9 +1744,7 @@ particular thanks going to @Consolatis, @jlindgren90, @bi4k8, @Flrian and VS Code and Discord lagging over time. [#553] Written-by: @Joshua-Ashton - Do not switch output on SnapToEdge if view is maximized. Written-by: @Flrian -## 0.6.0 - 2022-11-17 - -[0.6.0-commits] +## [0.6.0] - 2022-11-17 This release contains significant refactoring to use the wlroots scene-graph API. This touches many areas of the code, particularly @@ -2190,9 +1911,7 @@ reported, tested and fixed issues. Particular mentions go to @bi4k8, exited the compositor by mistake trying to get out of alt-tab cycling or similar. -## 0.5.3 - 2022-07-15 - -[0.5.3-commits] +## [0.5.3] - 2022-07-15 ### Added @@ -2214,9 +1933,7 @@ reported, tested and fixed issues. Particular mentions go to @bi4k8, - Do not segfault on missing drag icon. Written-by: @Consolatis - Fix windows erratically sticking to edges during move/resize [#331] [#309] -## 0.5.2 - 2022-05-17 - -[0.5.2-commits] +## [0.5.2] - 2022-05-17 This is a minor bugfix release mostly to ease packaging. @@ -2224,9 +1941,7 @@ This is a minor bugfix release mostly to ease packaging. - Properly use system provided wlroots. Written-by: @eli-schwartz -## 0.5.1 - 2022-04-08 - -[0.5.1-commits] +## [0.5.1] - 2022-04-08 ### Added @@ -2253,9 +1968,7 @@ This is a minor bugfix release mostly to ease packaging. - Fix qt application crash on touchpad scroll [#225] Written-by: @Consolatis -## 0.5.0 - 2022-02-18 - -[0.5.0-commits] +## [0.5.0] - 2022-02-18 As usual, this release contains a bunch of fixes and improvements, of which the most notable feature-type changes are listed below. A big @@ -2292,9 +2005,7 @@ This release contains the following two breaking changes: updating any local `rc.xml` settings in accordance with `docs/rc.xml.all` -## 0.4.0 - 2021-12-31 - -[0.4.0-commits] +## [0.4.0] - 2021-12-31 Compile with wlroots 0.15.0 @@ -2369,9 +2080,7 @@ feature-type changes are listed below. - The config option `` has changed to `` (breaking change) -## 0.3.0 - 2021-06-28 - -[0.3.0-commits] +## [0.3.0] - 2021-06-28 Compile with wlroots 0.14.0 @@ -2382,9 +2091,7 @@ Compile with wlroots 0.14.0 - Do not use Clearlooks-3.4 theme by default, just use built-in theme - Fix bug which triggered Qt application segfault -## 0.2.0 - 2021-04-15 - -[0.2.0-commits] +## [0.2.0] - 2021-04-15 Compile with wlroots 0.13.0 @@ -2397,9 +2104,7 @@ Compile with wlroots 0.13.0 - Add labwc-environment(5) - Call `wlr_output_enable_adaptive_sync()` if `LABWC_ADAPTIVE_SYNC` set -## 0.1.0 - 2021-03-05 - -[0.1.0-commits] +## [0.1.0] - 2021-03-05 Compile with wlroots 0.12.0 and wayland-server >=1.16 @@ -2418,35 +2123,33 @@ 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.2...HEAD -[0.9.2-commits]: https://github.com/labwc/labwc/compare/0.9.1...0.9.2 -[0.9.1-commits]: https://github.com/labwc/labwc/compare/0.9.0...0.9.1 -[0.9.0-commits]: https://github.com/labwc/labwc/compare/0.8.4...0.9.0 -[0.8.4-commits]: https://github.com/labwc/labwc/compare/0.8.3...0.8.4 -[0.8.3-commits]: https://github.com/labwc/labwc/compare/0.8.2...0.8.3 -[0.8.2-commits]: https://github.com/labwc/labwc/compare/0.8.1...0.8.2 -[0.8.1-commits]: https://github.com/labwc/labwc/compare/0.8.0...0.8.1 -[0.8.0-commits]: https://github.com/labwc/labwc/compare/0.7.3...0.8.0 -[0.7.4-commits]: https://github.com/labwc/labwc/compare/0.7.3...0.7.4 -[0.7.3-commits]: https://github.com/labwc/labwc/compare/0.7.2...0.7.3 -[0.7.2-commits]: https://github.com/labwc/labwc/compare/0.7.1...0.7.2 -[0.7.1-commits]: https://github.com/labwc/labwc/compare/0.7.0...0.7.1 -[0.7.0-commits]: https://github.com/labwc/labwc/compare/0.6.6...0.7.0 -[0.6.6-commits]: https://github.com/labwc/labwc/compare/0.6.5...0.6.6 -[0.6.5-commits]: https://github.com/labwc/labwc/compare/0.6.4...0.6.5 -[0.6.4-commits]: https://github.com/labwc/labwc/compare/0.6.3...0.6.4 -[0.6.3-commits]: https://github.com/labwc/labwc/compare/0.6.2...0.6.3 -[0.6.2-commits]: https://github.com/labwc/labwc/compare/0.6.1...0.6.2 -[0.6.1-commits]: https://github.com/labwc/labwc/compare/0.6.0...0.6.1 -[0.6.0-commits]: https://github.com/labwc/labwc/compare/0.5.0...0.6.0 -[0.5.3-commits]: https://github.com/labwc/labwc/compare/0.5.2...0.5.3 -[0.5.2-commits]: https://github.com/labwc/labwc/compare/0.5.1...0.5.2 -[0.5.1-commits]: https://github.com/labwc/labwc/compare/0.5.0...0.5.1 -[0.5.0-commits]: https://github.com/labwc/labwc/compare/0.4.0...0.5.0 -[0.4.0-commits]: https://github.com/labwc/labwc/compare/0.3.0...0.4.0 -[0.3.0-commits]: https://github.com/labwc/labwc/compare/0.2.0...0.3.0 -[0.2.0-commits]: https://github.com/labwc/labwc/compare/0.1.0...0.2.0 -[0.1.0-commits]: https://github.com/labwc/labwc/compare/081339e...0.1.0 +[unreleased]: https://github.com/labwc/labwc/compare/0.9.0...HEAD +[0.9.0]: https://github.com/labwc/labwc/compare/0.8.4...0.9.0 +[0.8.4]: https://github.com/labwc/labwc/compare/0.8.3...0.8.4 +[0.8.3]: https://github.com/labwc/labwc/compare/0.8.2...0.8.3 +[0.8.2]: https://github.com/labwc/labwc/compare/0.8.1...0.8.2 +[0.8.1]: https://github.com/labwc/labwc/compare/0.8.0...0.8.1 +[0.8.0]: https://github.com/labwc/labwc/compare/0.7.3...0.8.0 +[0.7.4]: https://github.com/labwc/labwc/compare/0.7.3...0.7.4 +[0.7.3]: https://github.com/labwc/labwc/compare/0.7.2...0.7.3 +[0.7.2]: https://github.com/labwc/labwc/compare/0.7.1...0.7.2 +[0.7.1]: https://github.com/labwc/labwc/compare/0.7.0...0.7.1 +[0.7.0]: https://github.com/labwc/labwc/compare/0.6.6...0.7.0 +[0.6.6]: https://github.com/labwc/labwc/compare/0.6.5...0.6.6 +[0.6.5]: https://github.com/labwc/labwc/compare/0.6.4...0.6.5 +[0.6.4]: https://github.com/labwc/labwc/compare/0.6.3...0.6.4 +[0.6.3]: https://github.com/labwc/labwc/compare/0.6.2...0.6.3 +[0.6.2]: https://github.com/labwc/labwc/compare/0.6.1...0.6.2 +[0.6.1]: https://github.com/labwc/labwc/compare/0.6.0...0.6.1 +[0.6.0]: https://github.com/labwc/labwc/compare/0.5.0...0.6.0 +[0.5.3]: https://github.com/labwc/labwc/compare/0.5.2...0.5.3 +[0.5.2]: https://github.com/labwc/labwc/compare/0.5.1...0.5.2 +[0.5.1]: https://github.com/labwc/labwc/compare/0.5.0...0.5.1 +[0.5.0]: https://github.com/labwc/labwc/compare/0.4.0...0.5.0 +[0.4.0]: https://github.com/labwc/labwc/compare/0.3.0...0.4.0 +[0.3.0]: https://github.com/labwc/labwc/compare/0.2.0...0.3.0 +[0.2.0]: https://github.com/labwc/labwc/compare/0.1.0...0.2.0 +[0.1.0]: https://github.com/labwc/labwc/compare/081339e...0.1.0 [#162]: https://github.com/labwc/labwc/pull/162 [#163]: https://github.com/labwc/labwc/pull/163 @@ -2793,14 +2496,12 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16 [#2652]: https://github.com/labwc/labwc/pull/2652 [#2653]: https://github.com/labwc/labwc/pull/2653 [#2657]: https://github.com/labwc/labwc/pull/2657 -[#2667]: https://github.com/labwc/labwc/pull/2667 [#2669]: https://github.com/labwc/labwc/pull/2669 [#2678]: https://github.com/labwc/labwc/pull/2678 [#2686]: https://github.com/labwc/labwc/pull/2686 [#2688]: https://github.com/labwc/labwc/pull/2688 [#2692]: https://github.com/labwc/labwc/pull/2692 [#2693]: https://github.com/labwc/labwc/pull/2693 -[#2699]: https://github.com/labwc/labwc/pull/2699 [#2700]: https://github.com/labwc/labwc/pull/2700 [#2710]: https://github.com/labwc/labwc/pull/2710 [#2713]: https://github.com/labwc/labwc/pull/2713 @@ -2844,53 +2545,6 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16 [#2873]: https://github.com/labwc/labwc/pull/2873 [#2874]: https://github.com/labwc/labwc/pull/2874 [#2877]: https://github.com/labwc/labwc/pull/2877 -[#2883]: https://github.com/labwc/labwc/pull/2883 -[#2885]: https://github.com/labwc/labwc/pull/2885 [#2886]: https://github.com/labwc/labwc/pull/2886 [#2887]: https://github.com/labwc/labwc/pull/2887 [#2891]: https://github.com/labwc/labwc/pull/2891 -[#2895]: https://github.com/labwc/labwc/pull/2895 -[#2909]: https://github.com/labwc/labwc/pull/2909 -[#2910]: https://github.com/labwc/labwc/pull/2910 -[#2914]: https://github.com/labwc/labwc/pull/2914 -[#2933]: https://github.com/labwc/labwc/pull/2933 -[#2937]: https://github.com/labwc/labwc/pull/2937 -[#2939]: https://github.com/labwc/labwc/pull/2939 -[#2942]: https://github.com/labwc/labwc/pull/2942 -[#2943]: https://github.com/labwc/labwc/pull/2943 -[#2944]: https://github.com/labwc/labwc/pull/2944 -[#2948]: https://github.com/labwc/labwc/pull/2948 -[#2965]: https://github.com/labwc/labwc/pull/2965 -[#2967]: https://github.com/labwc/labwc/pull/2967 -[#2970]: https://github.com/labwc/labwc/pull/2970 -[#2971]: https://github.com/labwc/labwc/pull/2971 -[#2972]: https://github.com/labwc/labwc/pull/2972 -[#2976]: https://github.com/labwc/labwc/pull/2976 -[#2981]: https://github.com/labwc/labwc/pull/2981 -[#2994]: https://github.com/labwc/labwc/pull/2994 -[#2995]: https://github.com/labwc/labwc/pull/2995 -[#2998]: https://github.com/labwc/labwc/pull/2998 -[#3002]: https://github.com/labwc/labwc/pull/3002 -[#3004]: https://github.com/labwc/labwc/pull/3004 -[#3011]: https://github.com/labwc/labwc/pull/3011 -[#3015]: https://github.com/labwc/labwc/pull/3015 -[#3020]: https://github.com/labwc/labwc/pull/3020 -[#3024]: https://github.com/labwc/labwc/pull/3024 -[#3028]: https://github.com/labwc/labwc/pull/3028 -[#3033]: https://github.com/labwc/labwc/pull/3033 -[#3039]: https://github.com/labwc/labwc/pull/3039 -[#3042]: https://github.com/labwc/labwc/pull/3042 -[#3043]: https://github.com/labwc/labwc/pull/3043 -[#3047]: https://github.com/labwc/labwc/pull/3047 -[#3049]: https://github.com/labwc/labwc/pull/3049 -[#3081]: https://github.com/labwc/labwc/pull/3081 -[#3097]: https://github.com/labwc/labwc/pull/3097 -[#3099]: https://github.com/labwc/labwc/pull/3099 -[#3118]: https://github.com/labwc/labwc/pull/3118 -[#3120]: https://github.com/labwc/labwc/pull/3120 -[#3124]: https://github.com/labwc/labwc/pull/3124 -[#3126]: https://github.com/labwc/labwc/pull/3126 -[#3134]: https://github.com/labwc/labwc/pull/3134 -[#3145]: https://github.com/labwc/labwc/pull/3145 -[#3148]: https://github.com/labwc/labwc/pull/3148 -[#3153]: https://github.com/labwc/labwc/pull/3153 diff --git a/README.md b/README.md index 1e8d3759..26ce81ca 100644 --- a/README.md +++ b/README.md @@ -101,8 +101,21 @@ spend our effort. A lot of emphasis is put on code simplicity when considering features. +The main development effort is focused on producing a solid foundation for a +stacking compositor rather than adding configuration and theming options. + See [scope] for full details on implemented features. +High-level summary of items that Labwc supports: + +- [x] Config files (rc.xml, autostart, shutdown, environment, menu.xml) +- [x] Theme files and xbm/png/svg icons +- [x] Basic desktop and client menus +- [x] HiDPI +- [x] wlroots protocols such as `output-management`, `layer-shell` and + `foreign-toplevel` +- [x] Optionally xwayland + ### 1.5 Videos | video link | date | duration @@ -236,7 +249,7 @@ Suggested apps to use with Labwc: - Screen shooter: [grim] - Screen recorder: [wf-recorder] - Background image: [swaybg] -- Panel: [waybar], [lavalauncher], [sfwbar], [xfce4-panel] +- Panel: [waybar], [yambar], [lavalauncher], [sfwbar], [xfce4-panel] - Launchers: [bemenu], [fuzzel], [wofi] - Output managers: [wlopm], [kanshi], [wlr-randr] - Screen locker: [swaylock] @@ -279,6 +292,7 @@ The default window bar menu can be translated on the [weblate platform](https:// [wf-recorder]: https://github.com/ammen99/wf-recorder [swaybg]: https://github.com/swaywm/swaybg [waybar]: https://github.com/Alexays/Waybar +[yambar]: https://codeberg.org/dnkl/yambar [lavalauncher]: https://sr.ht/~leon_plickat/LavaLauncher [sfwbar]: https://github.com/LBCrion/sfwbar [xfce4-panel]: https://gitlab.xfce.org/xfce/xfce4-panel diff --git a/clients/labnag.c b/clients/labnag.c deleted file mode 100644 index 31ac4e9d..00000000 --- a/clients/labnag.c +++ /dev/null @@ -1,1874 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Based on https://github.com/swaywm/sway/tree/master/swaynag - * - * Copyright (C) 2016-2017 Drew DeVault - * Copyright (C) 2025 Johan Malm - */ -#define _POSIX_C_SOURCE 200809L -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef __FreeBSD__ -#include /* For signalfd() */ -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "action-prompt-codes.h" -#include "pool-buffer.h" -#include "cursor-shape-v1-client-protocol.h" -#include "wlr-layer-shell-unstable-v1-client-protocol.h" - -#define LABNAG_MAX_HEIGHT 500 - -struct conf { - PangoFontDescription *font_description; - char *output; - uint32_t anchors; - int32_t layer; /* enum zwlr_layer_shell_v1_layer or -1 if unset */ - enum zwlr_layer_surface_v1_keyboard_interactivity keyboard_focus; - - /* Colors */ - uint32_t button_text; - uint32_t button_background; - uint32_t details_background; - uint32_t background; - uint32_t text; - uint32_t button_border; - uint32_t border_bottom; - - /* Sizing */ - ssize_t bar_border_thickness; - ssize_t message_padding; - ssize_t details_border_thickness; - ssize_t button_border_thickness; - ssize_t button_gap; - ssize_t button_gap_close; - ssize_t button_margin_right; - ssize_t button_padding; -}; - -struct pointer { - struct wl_pointer *pointer; - uint32_t serial; - struct wl_cursor_theme *cursor_theme; - struct wl_cursor_image *cursor_image; - struct wl_surface *cursor_surface; - int x; - int y; -}; - -struct keyboard { - struct wl_keyboard *keyboard; - struct xkb_keymap *keymap; - struct xkb_state *state; -}; - -struct seat { - struct wl_seat *wl_seat; - uint32_t wl_name; - struct nag *nag; - struct pointer pointer; - struct keyboard keyboard; - struct wl_list link; /* nag.seats */ -}; - -struct output { - char *name; - struct wl_output *wl_output; - uint32_t wl_name; - uint32_t scale; - struct nag *nag; - struct wl_list link; /* nag.outputs */ -}; - -struct button { - char *text; - char *action; - int x; - int y; - int width; - int height; - bool expand; - bool dismiss; - struct wl_list link; -}; - -enum { - FD_WAYLAND, - FD_TIMER, - FD_SIGNAL, - - NR_FDS, -}; - -struct nag { - bool run_display; - - struct wl_display *display; - struct wl_compositor *compositor; - struct wl_seat *seat; - struct wl_shm *shm; - struct wl_list outputs; - struct wl_list seats; - struct output *output; - struct zwlr_layer_shell_v1 *layer_shell; - struct zwlr_layer_surface_v1 *layer_surface; - struct wp_cursor_shape_manager_v1 *cursor_shape_manager; - struct wl_surface *surface; - - uint32_t width; - uint32_t height; - int32_t scale; - struct pool_buffer buffers[2]; - struct pool_buffer *current_buffer; - - struct conf *conf; - char *message; - struct wl_list buttons; - int selected_button; - struct pollfd pollfds[NR_FDS]; - - struct { - bool visible; - char *message; - char *details_text; - int close_timeout; - bool use_exclusive_zone; - - int x; - int y; - int width; - int height; - - int offset; - int visible_lines; - int total_lines; - struct button *button_details; - struct button button_up; - struct button button_down; - } details; -}; - -static int exit_status = LAB_EXIT_FAILURE; - -static void close_pollfd(struct pollfd *pollfd); - -static PangoLayout * -get_pango_layout(cairo_t *cairo, const PangoFontDescription *desc, - const char *text, double scale, bool markup) -{ - PangoLayout *layout = pango_cairo_create_layout(cairo); - pango_context_set_round_glyph_positions(pango_layout_get_context(layout), false); - - PangoAttrList *attrs; - if (markup) { - char *buf; - GError *error = NULL; - if (pango_parse_markup(text, -1, 0, &attrs, &buf, NULL, &error)) { - pango_layout_set_text(layout, buf, -1); - free(buf); - } else { - wlr_log(WLR_ERROR, "pango_parse_markup '%s' -> error %s", - text, error->message); - g_error_free(error); - markup = false; /* fallback to plain text */ - } - } - if (!markup) { - attrs = pango_attr_list_new(); - pango_layout_set_text(layout, text, -1); - } - - pango_attr_list_insert(attrs, pango_attr_scale_new(scale)); - pango_layout_set_font_description(layout, desc); - pango_layout_set_single_paragraph_mode(layout, 1); - pango_layout_set_attributes(layout, attrs); - pango_attr_list_unref(attrs); - return layout; -} - -static void -get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height, - int *baseline, double scale, bool markup, const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - gchar *buf = g_strdup_vprintf(fmt, args); - va_end(args); - if (!buf) { - return; - } - - PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup); - pango_cairo_update_layout(cairo, layout); - pango_layout_get_pixel_size(layout, width, height); - if (baseline) { - *baseline = pango_layout_get_baseline(layout) / PANGO_SCALE; - } - g_object_unref(layout); - - g_free(buf); -} - -static void -render_text(cairo_t *cairo, const PangoFontDescription *desc, double scale, - bool markup, const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - gchar *buf = g_strdup_vprintf(fmt, args); - va_end(args); - if (!buf) { - return; - } - - PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup); - cairo_font_options_t *fo = cairo_font_options_create(); - cairo_get_font_options(cairo, fo); - pango_cairo_context_set_font_options(pango_layout_get_context(layout), fo); - cairo_font_options_destroy(fo); - pango_cairo_update_layout(cairo, layout); - pango_cairo_show_layout(cairo, layout); - g_object_unref(layout); - - g_free(buf); -} - -static void -cairo_set_source_u32(cairo_t *cairo, uint32_t color) -{ - cairo_set_source_rgba(cairo, - (color >> (3*8) & 0xFF) / 255.0, - (color >> (2*8) & 0xFF) / 255.0, - (color >> (1*8) & 0xFF) / 255.0, - (color >> (0*8) & 0xFF) / 255.0); -} - -static uint32_t -render_message(cairo_t *cairo, struct nag *nag) -{ - int text_width, text_height; - get_text_size(cairo, nag->conf->font_description, &text_width, - &text_height, NULL, 1, true, "%s", nag->message); - - int padding = nag->conf->message_padding; - - uint32_t ideal_height = text_height + padding * 2; - uint32_t ideal_surface_height = ideal_height; - if (nag->height < ideal_surface_height) { - return ideal_surface_height; - } - - cairo_set_source_u32(cairo, nag->conf->text); - cairo_move_to(cairo, padding, (int)(ideal_height - text_height) / 2); - render_text(cairo, nag->conf->font_description, 1, false, - "%s", nag->message); - - return ideal_surface_height; -} - -static void -render_details_scroll_button(cairo_t *cairo, struct nag *nag, - struct button *button) -{ - int text_width, text_height; - 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; - - cairo_set_source_u32(cairo, nag->conf->details_background); - cairo_rectangle(cairo, button->x, button->y, - button->width, button->height); - cairo_fill(cairo); - - cairo_set_source_u32(cairo, nag->conf->button_background); - cairo_rectangle(cairo, button->x + border, button->y + border, - button->width - (border * 2), - button->height - (border * 2)); - cairo_fill(cairo); - - 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); - render_text(cairo, nag->conf->font_description, 1, true, - "%s", button->text); -} - -static int -get_detailed_scroll_button_width(cairo_t *cairo, struct nag *nag) -{ - int up_width, down_width, temp_height; - get_text_size(cairo, nag->conf->font_description, &up_width, &temp_height, - NULL, 1, true, "%s", nag->details.button_up.text); - get_text_size(cairo, nag->conf->font_description, &down_width, &temp_height, - 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; - - return text_width + border * 2 + padding * 2; -} - -static uint32_t -render_detailed(cairo_t *cairo, struct nag *nag, uint32_t y) -{ - uint32_t width = nag->width; - - int border = nag->conf->details_border_thickness; - int padding = nag->conf->message_padding; - int decor = padding + border; - - nag->details.x = decor; - nag->details.y = y + decor; - nag->details.width = width - decor * 2; - - PangoLayout *layout = get_pango_layout(cairo, nag->conf->font_description, - nag->details.message, 1, false); - pango_layout_set_width(layout, - (nag->details.width - padding * 2) * PANGO_SCALE); - pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); - pango_layout_set_single_paragraph_mode(layout, false); - pango_cairo_update_layout(cairo, layout); - nag->details.total_lines = pango_layout_get_line_count(layout); - - PangoLayoutLine *line; - line = pango_layout_get_line_readonly(layout, nag->details.offset); - gint offset = line->start_index; - const char *text = pango_layout_get_text(layout); - pango_layout_set_text(layout, text + offset, strlen(text) - offset); - - int text_width, text_height; - pango_cairo_update_layout(cairo, layout); - pango_layout_get_pixel_size(layout, &text_width, &text_height); - - 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; - pango_layout_set_width(layout, - (nag->details.width - padding * 2) * PANGO_SCALE); - } - - uint32_t ideal_height; - do { - ideal_height = nag->details.y + text_height + decor + padding * 2; - if (ideal_height > LABNAG_MAX_HEIGHT) { - ideal_height = LABNAG_MAX_HEIGHT; - - if (!show_buttons) { - show_buttons = true; - nag->details.width -= button_width; - pango_layout_set_width(layout, - (nag->details.width - padding * 2) * PANGO_SCALE); - } - } - - nag->details.height = ideal_height - nag->details.y - decor; - pango_layout_set_height(layout, - (nag->details.height - padding * 2) * PANGO_SCALE); - pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); - pango_cairo_update_layout(cairo, layout); - pango_layout_get_pixel_size(layout, &text_width, &text_height); - } while (text_height != (nag->details.height - padding * 2)); - - nag->details.visible_lines = pango_layout_get_line_count(layout); - - 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.width = button_width; - nag->details.button_up.height = nag->details.height / 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_down.width = button_width; - nag->details.button_down.height = nag->details.height / 2; - render_details_scroll_button(cairo, nag, &nag->details.button_down); - } - - cairo_set_source_u32(cairo, nag->conf->details_background); - cairo_rectangle(cairo, nag->details.x, nag->details.y, - nag->details.width, nag->details.height); - cairo_fill(cairo); - - cairo_move_to(cairo, nag->details.x + padding, nag->details.y + padding); - cairo_set_source_u32(cairo, nag->conf->text); - pango_cairo_show_layout(cairo, layout); - g_object_unref(layout); - - return ideal_height; -} - -static uint32_t -render_button(cairo_t *cairo, struct nag *nag, struct button *button, - bool selected, int *x) -{ - int text_width, text_height; - 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; - - uint32_t ideal_height = text_height + padding * 2 + border * 2; - uint32_t ideal_surface_height = ideal_height; - if (nag->height < ideal_surface_height) { - return ideal_surface_height; - } - - button->x = *x - border - text_width - padding * 2 + 1; - button->y = (int)(ideal_height - text_height) / 2 - padding + 1; - button->width = text_width + padding * 2; - button->height = text_height + padding * 2; - - cairo_set_source_u32(cairo, nag->conf->button_border); - cairo_rectangle(cairo, button->x - border, button->y - border, - button->width + border * 2, button->height + border * 2); - cairo_fill(cairo); - - cairo_set_source_u32(cairo, nag->conf->button_background); - cairo_rectangle(cairo, button->x, button->y, - button->width, button->height); - cairo_fill(cairo); - - if (selected) { - cairo_set_source_u32(cairo, nag->conf->button_border); - cairo_set_line_width(cairo, 1); - cairo_rectangle(cairo, button->x + 1.5, button->y + 1.5, - button->width - 3, button->height - 3); - cairo_stroke(cairo); - } - - cairo_set_source_u32(cairo, nag->conf->button_text); - cairo_move_to(cairo, button->x + padding, button->y + padding); - render_text(cairo, nag->conf->font_description, 1, true, - "%s", button->text); - - *x = button->x - border; - - return ideal_surface_height; -} - -static uint32_t -render_to_cairo(cairo_t *cairo, struct nag *nag) -{ - uint32_t max_height = 0; - - cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); - cairo_set_source_u32(cairo, nag->conf->background); - cairo_paint(cairo); - - uint32_t h = render_message(cairo, nag); - max_height = h > max_height ? h : max_height; - - int x = nag->width - nag->conf->button_margin_right; - x -= nag->conf->button_gap_close; - - int idx = 0; - struct button *button; - wl_list_for_each(button, &nag->buttons, link) { - h = render_button(cairo, nag, button, idx == nag->selected_button, &x); - max_height = h > max_height ? h : max_height; - x -= nag->conf->button_gap; - idx++; - } - - if (nag->details.visible) { - h = render_detailed(cairo, nag, max_height); - max_height = h > max_height ? h : max_height; - } - - int border = nag->conf->bar_border_thickness; - if (max_height > nag->height) { - max_height += border; - } - cairo_set_source_u32(cairo, nag->conf->border_bottom); - cairo_rectangle(cairo, 0, - nag->height - border, - nag->width, - border); - cairo_fill(cairo); - - return max_height; -} - -static void -render_frame(struct nag *nag) -{ - if (!nag->run_display) { - return; - } - - cairo_surface_t *recorder = cairo_recording_surface_create( - CAIRO_CONTENT_COLOR_ALPHA, NULL); - cairo_t *cairo = cairo_create(recorder); - cairo_scale(cairo, nag->scale, nag->scale); - cairo_save(cairo); - cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); - cairo_paint(cairo); - cairo_restore(cairo); - uint32_t height = render_to_cairo(cairo, nag); - if (height != nag->height) { - zwlr_layer_surface_v1_set_size(nag->layer_surface, 0, height); - if (nag->details.use_exclusive_zone) { - zwlr_layer_surface_v1_set_exclusive_zone( - nag->layer_surface, height); - } - wl_surface_commit(nag->surface); - wl_display_roundtrip(nag->display); - } else { - nag->current_buffer = get_next_buffer(nag->shm, - nag->buffers, - nag->width * nag->scale, - nag->height * nag->scale); - if (!nag->current_buffer) { - wlr_log(WLR_DEBUG, "Failed to get buffer. Skipping frame."); - goto cleanup; - } - - cairo_t *shm = nag->current_buffer->cairo; - cairo_save(shm); - cairo_set_operator(shm, CAIRO_OPERATOR_CLEAR); - cairo_paint(shm); - cairo_restore(shm); - cairo_set_source_surface(shm, recorder, 0.0, 0.0); - cairo_paint(shm); - - wl_surface_set_buffer_scale(nag->surface, nag->scale); - wl_surface_attach(nag->surface, - nag->current_buffer->buffer, 0, 0); - wl_surface_damage(nag->surface, 0, 0, - nag->width, nag->height); - wl_surface_commit(nag->surface); - wl_display_roundtrip(nag->display); - } - -cleanup: - cairo_surface_destroy(recorder); - cairo_destroy(cairo); -} - -static void -seat_destroy(struct seat *seat) -{ - if (seat->pointer.cursor_theme) { - wl_cursor_theme_destroy(seat->pointer.cursor_theme); - } - if (seat->pointer.pointer) { - wl_pointer_destroy(seat->pointer.pointer); - } - if (seat->keyboard.keyboard) { - wl_keyboard_destroy(seat->keyboard.keyboard); - } - if (seat->keyboard.keymap) { - xkb_keymap_unref(seat->keyboard.keymap); - } - if (seat->keyboard.state) { - xkb_state_unref(seat->keyboard.state); - } - wl_seat_destroy(seat->wl_seat); - wl_list_remove(&seat->link); - free(seat); -} - -static void -nag_destroy(struct nag *nag) -{ - nag->run_display = false; - - struct button *button, *next; - wl_list_for_each_safe(button, next, &nag->buttons, link) { - wl_list_remove(&button->link); - free(button); - } - free(nag->details.message); - - pango_font_description_free(nag->conf->font_description); - - if (nag->layer_surface) { - zwlr_layer_surface_v1_destroy(nag->layer_surface); - } - - if (nag->surface) { - wl_surface_destroy(nag->surface); - } - - if (nag->layer_shell) { - zwlr_layer_shell_v1_destroy(nag->layer_shell); - } - - if (nag->cursor_shape_manager) { - wp_cursor_shape_manager_v1_destroy(nag->cursor_shape_manager); - } - - struct seat *seat, *tmpseat; - wl_list_for_each_safe(seat, tmpseat, &nag->seats, link) { - seat_destroy(seat); - } - - destroy_buffer(&nag->buffers[0]); - destroy_buffer(&nag->buffers[1]); - - if (nag->outputs.prev || nag->outputs.next) { - struct output *output, *temp; - wl_list_for_each_safe(output, temp, &nag->outputs, link) { - wl_output_destroy(output->wl_output); - free(output->name); - wl_list_remove(&output->link); - free(output); - }; - } - - if (nag->compositor) { - wl_compositor_destroy(nag->compositor); - } - - if (nag->shm) { - wl_shm_destroy(nag->shm); - } - - if (nag->display) { - wl_display_disconnect(nag->display); - } - pango_cairo_font_map_set_default(NULL); - - close_pollfd(&nag->pollfds[FD_TIMER]); - close_pollfd(&nag->pollfds[FD_SIGNAL]); -} - -static void -button_execute(struct nag *nag, struct button *button) -{ - wlr_log(WLR_DEBUG, "Executing [%s]: %s", button->text, button->action); - if (button->expand) { - nag->details.visible = !nag->details.visible; - render_frame(nag); - return; - } - if (button->dismiss) { - nag->run_display = false; - } - if (button->action) { - pid_t pid = fork(); - if (pid < 0) { - wlr_log_errno(WLR_DEBUG, "Failed to fork"); - return; - } else if (pid == 0) { - /* - * Child process. Will be used to prevent zombie - * processes - */ - pid = fork(); - if (pid < 0) { - wlr_log_errno(WLR_DEBUG, "Failed to fork"); - return; - } else if (pid == 0) { - /* - * Child of the child. Will be reparented to the - * init process - */ - execlp("sh", "sh", "-c", button->action, NULL); - wlr_log_errno(WLR_DEBUG, "execlp failed"); - _exit(LAB_EXIT_FAILURE); - } - _exit(EXIT_SUCCESS); - } - - if (waitpid(pid, NULL, 0) < 0) { - wlr_log_errno(WLR_DEBUG, "waitpid failed"); - } - } -} - -static void -layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, - uint32_t serial, uint32_t width, uint32_t height) -{ - struct nag *nag = data; - nag->width = width; - nag->height = height; - zwlr_layer_surface_v1_ack_configure(surface, serial); - render_frame(nag); -} - -static void -layer_surface_closed(void *data, struct zwlr_layer_surface_v1 *surface) -{ - struct nag *nag = data; - nag_destroy(nag); -} - -static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { - .configure = layer_surface_configure, - .closed = layer_surface_closed, -}; - -static void -surface_enter(void *data, struct wl_surface *surface, struct wl_output *output) -{ - struct nag *nag = data; - struct output *nag_output; - wl_list_for_each(nag_output, &nag->outputs, link) { - if (nag_output->wl_output == output) { - wlr_log(WLR_DEBUG, "Surface enter on output %s", - nag_output->name); - nag->output = nag_output; - nag->scale = nag->output->scale; - render_frame(nag); - break; - } - } -} - -static void -surface_leave(void *data, struct wl_surface *wl_surface, struct wl_output *output) -{ - /* nop */ -} - -static const struct wl_surface_listener surface_listener = { - .enter = surface_enter, - .leave = surface_leave, -}; - -static void -update_cursor(struct seat *seat) -{ - struct pointer *pointer = &seat->pointer; - struct nag *nag = seat->nag; - if (pointer->cursor_theme) { - wl_cursor_theme_destroy(pointer->cursor_theme); - } - const char *cursor_theme = getenv("XCURSOR_THEME"); - unsigned int cursor_size = 24; - const char *env_cursor_size = getenv("XCURSOR_SIZE"); - if (env_cursor_size && *env_cursor_size) { - errno = 0; - char *end; - unsigned int size = strtoul(env_cursor_size, &end, 10); - if (!*end && errno == 0) { - cursor_size = size; - } - } - pointer->cursor_theme = wl_cursor_theme_load( - cursor_theme, cursor_size * nag->scale, nag->shm); - if (!pointer->cursor_theme) { - wlr_log(WLR_ERROR, "Failed to load cursor theme"); - return; - } - struct wl_cursor *cursor = - wl_cursor_theme_get_cursor(pointer->cursor_theme, "default"); - if (!cursor) { - wlr_log(WLR_ERROR, "Failed to get default cursor from theme"); - return; - } - pointer->cursor_image = cursor->images[0]; - wl_surface_set_buffer_scale(pointer->cursor_surface, - nag->scale); - wl_surface_attach(pointer->cursor_surface, - wl_cursor_image_get_buffer(pointer->cursor_image), 0, 0); - wl_pointer_set_cursor(pointer->pointer, pointer->serial, - pointer->cursor_surface, - pointer->cursor_image->hotspot_x / nag->scale, - pointer->cursor_image->hotspot_y / nag->scale); - wl_surface_damage_buffer(pointer->cursor_surface, 0, 0, - INT32_MAX, INT32_MAX); - wl_surface_commit(pointer->cursor_surface); -} - -static void -update_all_cursors(struct nag *nag) -{ - struct seat *seat; - wl_list_for_each(seat, &nag->seats, link) { - if (seat->pointer.pointer) { - update_cursor(seat); - } - } -} - -static void -wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, - struct wl_surface *surface, wl_fixed_t surface_x, - wl_fixed_t surface_y) -{ - struct seat *seat = data; - - struct pointer *pointer = &seat->pointer; - pointer->x = wl_fixed_to_int(surface_x); - pointer->y = wl_fixed_to_int(surface_y); - - if (seat->nag->cursor_shape_manager) { - struct wp_cursor_shape_device_v1 *device = - wp_cursor_shape_manager_v1_get_pointer( - seat->nag->cursor_shape_manager, wl_pointer); - wp_cursor_shape_device_v1_set_shape(device, serial, - WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT); - wp_cursor_shape_device_v1_destroy(device); - } else { - pointer->serial = serial; - update_cursor(seat); - } -} - -static void -wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, - uint32_t serial, struct wl_surface *surface) -{ - /* nop */ -} - -static void -wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, - wl_fixed_t surface_x, wl_fixed_t surface_y) -{ - struct seat *seat = data; - seat->pointer.x = wl_fixed_to_int(surface_x); - seat->pointer.y = wl_fixed_to_int(surface_y); -} - -static void -wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, - uint32_t time, uint32_t button, uint32_t state) -{ - struct seat *seat = data; - struct nag *nag = seat->nag; - - if (state != WL_POINTER_BUTTON_STATE_PRESSED) { - return; - } - - double x = seat->pointer.x; - double y = seat->pointer.y; - - int index = 0; - struct button *nagbutton; - wl_list_for_each(nagbutton, &nag->buttons, link) { - if (x >= nagbutton->x - && y >= nagbutton->y - && x < nagbutton->x + nagbutton->width - && y < nagbutton->y + nagbutton->height) { - button_execute(nag, nagbutton); - exit_status = index; - return; - } - ++index; - } - - if (nag->details.visible && - nag->details.total_lines != nag->details.visible_lines) { - struct button button_up = nag->details.button_up; - if (x >= button_up.x - && y >= button_up.y - && x < button_up.x + button_up.width - && y < button_up.y + button_up.height - && nag->details.offset > 0) { - nag->details.offset--; - render_frame(nag); - return; - } - - struct button button_down = nag->details.button_down; - int bot = nag->details.total_lines; - bot -= nag->details.visible_lines; - if (x >= button_down.x - && y >= button_down.y - && x < button_down.x + button_down.width - && y < button_down.y + button_down.height - && nag->details.offset < bot) { - nag->details.offset++; - render_frame(nag); - return; - } - } -} - -static void -wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, - uint32_t axis, wl_fixed_t value) -{ - struct seat *seat = data; - struct nag *nag = seat->nag; - if (!nag->details.visible - || seat->pointer.x < nag->details.x - || seat->pointer.y < nag->details.y - || seat->pointer.x >= nag->details.x + nag->details.width - || seat->pointer.y >= nag->details.y + nag->details.height - || nag->details.total_lines == nag->details.visible_lines) { - return; - } - - int direction = wl_fixed_to_int(value); - int bot = nag->details.total_lines - nag->details.visible_lines; - if (direction < 0 && nag->details.offset > 0) { - nag->details.offset--; - } else if (direction > 0 && nag->details.offset < bot) { - nag->details.offset++; - } - - render_frame(nag); -} - -static void -wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) -{ - struct seat *seat = data; - /* pointer inputs clears timer for auto-closing */ - close_pollfd(&seat->nag->pollfds[FD_TIMER]); -} - -static void -wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer, - uint32_t axis_source) -{ - /* nop */ -} - -static void -wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, - uint32_t time, uint32_t axis) -{ - /* nop */ -} - -static void -wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, - uint32_t axis, int32_t discrete) -{ - /* nop */ -} - -static const struct wl_pointer_listener pointer_listener = { - .enter = wl_pointer_enter, - .leave = wl_pointer_leave, - .motion = wl_pointer_motion, - .button = wl_pointer_button, - .axis = wl_pointer_axis, - .frame = wl_pointer_frame, - .axis_source = wl_pointer_axis_source, - .axis_stop = wl_pointer_axis_stop, - .axis_discrete = wl_pointer_axis_discrete, -}; - -static void -wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, - uint32_t format, int32_t fd, uint32_t size) -{ - struct seat *seat = data; - - if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { - wlr_log(WLR_ERROR, "unreconizable keymap format: %d", format); - close(fd); - return; - } - - char *map_shm = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); - if (map_shm == MAP_FAILED) { - wlr_log_errno(WLR_ERROR, "mmap()"); - close(fd); - return; - } - - if (seat->keyboard.keymap) { - xkb_keymap_unref(seat->keyboard.keymap); - seat->keyboard.keymap = NULL; - } - if (seat->keyboard.state) { - xkb_state_unref(seat->keyboard.state); - seat->keyboard.state = NULL; - } - struct xkb_context *xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - seat->keyboard.keymap = xkb_keymap_new_from_string(xkb, map_shm, - XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); - if (seat->keyboard.keymap) { - seat->keyboard.state = xkb_state_new(seat->keyboard.keymap); - } else { - wlr_log(WLR_ERROR, "failed to compile keymap"); - } - xkb_context_unref(xkb); - - munmap(map_shm, size); - close(fd); -} - -static void -wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, - struct wl_surface *surface, struct wl_array *keys) -{ -} - -static void -wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, - struct wl_surface *surface) -{ -} - -static void -wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, - uint32_t time, uint32_t key, uint32_t state) -{ - struct seat *seat = data; - struct nag *nag = seat->nag; - - if (!seat->keyboard.keymap || !seat->keyboard.state) { - wlr_log(WLR_ERROR, "keymap/state unavailable"); - return; - } - - if (state != WL_KEYBOARD_KEY_STATE_PRESSED) { - return; - } - - key += 8; - const xkb_keysym_t *syms; - if (!xkb_keymap_key_get_syms_by_level(seat->keyboard.keymap, - key, 0, 0, &syms)) { - wlr_log(WLR_ERROR, "failed to translate key: %d", key); - return; - } - xkb_mod_mask_t mods = xkb_state_serialize_mods(seat->keyboard.state, - XKB_STATE_MODS_EFFECTIVE); - xkb_mod_index_t shift_idx = xkb_keymap_mod_get_index( - seat->keyboard.keymap, XKB_MOD_NAME_SHIFT); - bool shift = shift_idx != XKB_MOD_INVALID && (mods & (1 << shift_idx)); - - int nr_buttons = wl_list_length(&nag->buttons); - - switch (syms[0]) { - case XKB_KEY_Left: - case XKB_KEY_Right: - case XKB_KEY_Tab: { - if (nr_buttons <= 0) { - break; - } - int direction; - if (syms[0] == XKB_KEY_Left || (syms[0] == XKB_KEY_Tab && shift)) { - direction = 1; - } else { - direction = -1; - } - nag->selected_button += nr_buttons + direction; - nag->selected_button %= nr_buttons; - render_frame(nag); - close_pollfd(&nag->pollfds[FD_TIMER]); - break; - } - case XKB_KEY_Escape: - exit_status = LAB_EXIT_CANCELLED; - nag->run_display = false; - break; - case XKB_KEY_Return: - case XKB_KEY_KP_Enter: { - int idx = 0; - struct button *button; - wl_list_for_each(button, &nag->buttons, link) { - if (idx == nag->selected_button) { - button_execute(nag, button); - close_pollfd(&nag->pollfds[FD_TIMER]); - exit_status = idx; - break; - } - idx++; - } - break; - } - } -} - -static void -wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, - uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, - uint32_t mods_locked, uint32_t group) -{ - struct seat *seat = data; - - if (!seat->keyboard.state) { - wlr_log(WLR_ERROR, "xkb state unavailable"); - return; - } - - xkb_state_update_mask(seat->keyboard.state, mods_depressed, - mods_latched, mods_locked, 0, 0, group); -} - -static void -wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, - int32_t rate, int32_t delay) -{ -} - -static const struct wl_keyboard_listener keyboard_listener = { - .keymap = wl_keyboard_keymap, - .enter = wl_keyboard_enter, - .leave = wl_keyboard_leave, - .key = wl_keyboard_key, - .modifiers = wl_keyboard_modifiers, - .repeat_info = wl_keyboard_repeat_info, -}; - -static void -seat_handle_capabilities(void *data, struct wl_seat *wl_seat, - enum wl_seat_capability caps) -{ - struct seat *seat = data; - bool cap_pointer = caps & WL_SEAT_CAPABILITY_POINTER; - bool cap_keyboard = caps & WL_SEAT_CAPABILITY_KEYBOARD; - - if (cap_pointer && !seat->pointer.pointer) { - seat->pointer.pointer = wl_seat_get_pointer(wl_seat); - wl_pointer_add_listener(seat->pointer.pointer, - &pointer_listener, seat); - } else if (!cap_pointer && seat->pointer.pointer) { - wl_pointer_destroy(seat->pointer.pointer); - seat->pointer.pointer = NULL; - } - - if (cap_keyboard && !seat->keyboard.keyboard) { - seat->keyboard.keyboard = wl_seat_get_keyboard(wl_seat); - wl_keyboard_add_listener(seat->keyboard.keyboard, - &keyboard_listener, seat); - } else if (!cap_keyboard && seat->keyboard.keyboard) { - wl_keyboard_destroy(seat->keyboard.keyboard); - seat->keyboard.keyboard = NULL; - } -} - -static void -seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name) -{ - /* nop */ -} - -static const struct wl_seat_listener seat_listener = { - .capabilities = seat_handle_capabilities, - .name = seat_handle_name, -}; - -static void -output_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, - int32_t physical_width, int32_t physical_height, - int32_t subpixel, const char *make, const char *model, - int32_t transform) -{ - /* nop */ -} - -static void -output_mode(void *data, struct wl_output *wl_output, uint32_t flags, - int32_t width, int32_t height, int32_t refresh) -{ - /* nop */ -} - -static void -output_done(void *data, struct wl_output *output) -{ - /* nop */ -} - -static void -output_scale(void *data, struct wl_output *output, int32_t factor) -{ - struct output *nag_output = data; - nag_output->scale = factor; - if (nag_output->nag->output == nag_output) { - nag_output->nag->scale = nag_output->scale; - if (!nag_output->nag->cursor_shape_manager) { - update_all_cursors(nag_output->nag); - } - render_frame(nag_output->nag); - } -} - -static void -output_name(void *data, struct wl_output *output, const char *name) -{ - struct output *nag_output = data; - nag_output->name = strdup(name); - - const char *outname = nag_output->nag->conf->output; - if (!nag_output->nag->output && outname && - strcmp(outname, name) == 0) { - wlr_log(WLR_DEBUG, "Using output %s", name); - nag_output->nag->output = nag_output; - } -} - -static void -output_description(void *data, struct wl_output *wl_output, - const char *description) -{ - /* nop */ -} - -static const struct wl_output_listener output_listener = { - .geometry = output_geometry, - .mode = output_mode, - .done = output_done, - .scale = output_scale, - .name = output_name, - .description = output_description, -}; - -static void -handle_global(void *data, struct wl_registry *registry, uint32_t name, - const char *interface, uint32_t version) -{ - struct nag *nag = data; - if (strcmp(interface, wl_compositor_interface.name) == 0) { - nag->compositor = wl_registry_bind(registry, name, - &wl_compositor_interface, 4); - } else if (strcmp(interface, wl_seat_interface.name) == 0) { - struct seat *seat = calloc(1, sizeof(*seat)); - if (!seat) { - perror("calloc"); - return; - } - - seat->nag = nag; - seat->wl_name = name; - seat->wl_seat = - wl_registry_bind(registry, name, &wl_seat_interface, 5); - - wl_seat_add_listener(seat->wl_seat, &seat_listener, seat); - - wl_list_insert(&nag->seats, &seat->link); - } else if (strcmp(interface, wl_shm_interface.name) == 0) { - nag->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); - } else if (strcmp(interface, wl_output_interface.name) == 0) { - if (!nag->output) { - struct output *output = calloc(1, sizeof(*output)); - if (!output) { - perror("calloc"); - return; - } - output->wl_output = wl_registry_bind(registry, name, - &wl_output_interface, 4); - output->wl_name = name; - output->scale = 1; - output->nag = nag; - wl_list_insert(&nag->outputs, &output->link); - wl_output_add_listener(output->wl_output, - &output_listener, output); - } - } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { - nag->layer_shell = wl_registry_bind( - registry, name, &zwlr_layer_shell_v1_interface, 4); - } else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) { - nag->cursor_shape_manager = wl_registry_bind( - registry, name, &wp_cursor_shape_manager_v1_interface, 1); - } -} - -static void -handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) -{ - struct nag *nag = data; - if (nag->output->wl_name == name) { - nag->run_display = false; - } - - struct seat *seat, *tmpseat; - wl_list_for_each_safe(seat, tmpseat, &nag->seats, link) { - if (seat->wl_name == name) { - seat_destroy(seat); - } - } -} - -static const struct wl_registry_listener registry_listener = { - .global = handle_global, - .global_remove = handle_global_remove, -}; - -static void -nag_setup_cursors(struct nag *nag) -{ - struct seat *seat; - - wl_list_for_each(seat, &nag->seats, link) { - struct pointer *p = &seat->pointer; - - p->cursor_surface = - wl_compositor_create_surface(nag->compositor); - assert(p->cursor_surface); - } -} - -static void -nag_setup(struct nag *nag) -{ - nag->display = wl_display_connect(NULL); - if (!nag->display) { - wlr_log(WLR_ERROR, "Unable to connect to the compositor. " - "If your compositor is running, check or set the " - "WAYLAND_DISPLAY environment variable."); - exit(LAB_EXIT_FAILURE); - } - - nag->scale = 1; - - struct wl_registry *registry = wl_display_get_registry(nag->display); - wl_registry_add_listener(registry, ®istry_listener, nag); - if (wl_display_roundtrip(nag->display) < 0) { - wlr_log(WLR_ERROR, "failed to register with the wayland display"); - exit(LAB_EXIT_FAILURE); - } - - assert(nag->compositor && nag->layer_shell && nag->shm); - - /* Second roundtrip to get wl_output properties */ - if (wl_display_roundtrip(nag->display) < 0) { - wlr_log(WLR_ERROR, "Error during outputs init."); - nag_destroy(nag); - exit(LAB_EXIT_FAILURE); - } - - if (!nag->output && nag->conf->output) { - wlr_log(WLR_ERROR, "Output '%s' not found", nag->conf->output); - nag_destroy(nag); - exit(LAB_EXIT_FAILURE); - } - - if (!nag->cursor_shape_manager) { - nag_setup_cursors(nag); - } - - nag->surface = wl_compositor_create_surface(nag->compositor); - assert(nag->surface); - wl_surface_add_listener(nag->surface, &surface_listener, nag); - - nag->layer_surface = zwlr_layer_shell_v1_get_layer_surface( - nag->layer_shell, nag->surface, - nag->output ? nag->output->wl_output : NULL, - nag->conf->layer, - "nag"); - assert(nag->layer_surface); - zwlr_layer_surface_v1_add_listener(nag->layer_surface, - &layer_surface_listener, nag); - zwlr_layer_surface_v1_set_anchor(nag->layer_surface, - nag->conf->anchors); - zwlr_layer_surface_v1_set_keyboard_interactivity(nag->layer_surface, - nag->conf->keyboard_focus); - - wl_registry_destroy(registry); - - nag->pollfds[FD_WAYLAND].fd = wl_display_get_fd(nag->display); - nag->pollfds[FD_WAYLAND].events = POLLIN; - - if (nag->details.close_timeout != 0) { - nag->pollfds[FD_TIMER].fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); - nag->pollfds[FD_TIMER].events = POLLIN; - struct itimerspec timeout = { - .it_value.tv_sec = nag->details.close_timeout, - }; - timerfd_settime(nag->pollfds[FD_TIMER].fd, 0, &timeout, NULL); - } else { - nag->pollfds[FD_TIMER].fd = -1; - } - - sigset_t mask; - sigaddset(&mask, SIGINT); - sigaddset(&mask, SIGTERM); - sigprocmask(SIG_BLOCK, &mask, NULL); - nag->pollfds[FD_SIGNAL].fd = signalfd(-1, &mask, SFD_CLOEXEC | SFD_NONBLOCK); - nag->pollfds[FD_SIGNAL].events = POLLIN; -} - -static void -close_pollfd(struct pollfd *pollfd) -{ - if (pollfd->fd == -1) { - return; - } - close(pollfd->fd); - pollfd->fd = -1; - pollfd->events = 0; - pollfd->revents = 0; -} - -static void -nag_run(struct nag *nag) -{ - nag->run_display = true; - render_frame(nag); - while (nag->run_display) { - while (wl_display_prepare_read(nag->display) != 0) { - wl_display_dispatch_pending(nag->display); - } - - errno = 0; - if (wl_display_flush(nag->display) == -1 && errno != EAGAIN) { - break; - } - - if (!nag->run_display) { - break; - } - - poll(nag->pollfds, NR_FDS, -1); - if (nag->pollfds[FD_WAYLAND].revents & POLLIN) { - wl_display_read_events(nag->display); - } else { - wl_display_cancel_read(nag->display); - } - if (nag->pollfds[FD_TIMER].revents & POLLIN) { - exit_status = LAB_EXIT_CANCELLED; - break; - } - if (nag->pollfds[FD_SIGNAL].revents & POLLIN) { - break; - } - } -} - -static void -conf_init(struct conf *conf) -{ - conf->font_description = pango_font_description_from_string("pango:Sans 10"); - conf->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP - | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT - | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - conf->layer = ZWLR_LAYER_SHELL_V1_LAYER_TOP; - 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_background = 0x680A0AFF; - conf->background = 0x900000FF; - conf->text = 0xFFFFFFFF; - conf->button_text = 0xFFFFFFFF; - conf->button_border = 0xD92424FF; - conf->border_bottom = 0x470909FF; -} - -static bool -parse_color(const char *color, uint32_t *result) -{ - if (color[0] == '#') { - ++color; - } - int len = strlen(color); - if ((len != 6 && len != 8) || !isxdigit(color[0]) || !isxdigit(color[1])) { - return false; - } - char *ptr; - uint32_t parsed = strtoul(color, &ptr, 16); - if (*ptr != '\0') { - return false; - } - *result = len == 6 ? ((parsed << 8) | 0xFF) : parsed; - return true; -} - -/* - * As labnag is slow for large "detailed messages" we curtail stdin at an - * arbitrary size to avoid hogging the CPU. - */ -#define MAX_STDIN_LINES 200 -static char * -read_and_trim_stdin(void) -{ - char *buffer = NULL, *line = NULL; - size_t buffer_len = 0, line_size = 0; - int line_count = 0; - while (line_count < MAX_STDIN_LINES) { - ssize_t nread = getline(&line, &line_size, stdin); - if (nread == -1) { - if (feof(stdin)) { - break; - } else { - perror("getline"); - goto freeline; - } - } - buffer = realloc(buffer, buffer_len + nread + 1); - if (!buffer) { - perror("realloc"); - return NULL; - } - memcpy(&buffer[buffer_len], line, nread + 1); - buffer_len += nread; - ++line_count; - } - free(line); - - while (buffer_len && buffer[buffer_len - 1] == '\n') { - buffer[--buffer_len] = '\0'; - } - - return buffer; - -freeline: - free(line); - return NULL; -} - -static int -nag_parse_options(int argc, char **argv, struct nag *nag, - struct conf *conf, bool *debug) -{ - enum type_options { - TO_COLOR_BACKGROUND = 256, - TO_COLOR_BUTTON_BORDER, - TO_COLOR_BORDER_BOTTOM, - TO_COLOR_BUTTON_BG, - TO_COLOR_DETAILS, - TO_COLOR_TEXT, - TO_COLOR_BUTTON_TEXT, - TO_THICK_BAR_BORDER, - TO_PADDING_MESSAGE, - TO_THICK_DET_BORDER, - TO_THICK_BTN_BORDER, - TO_GAP_BTN, - TO_GAP_BTN_DISMISS, - TO_MARGIN_BTN_RIGHT, - TO_PADDING_BTN, - }; - - static const struct option opts[] = { - {"button", required_argument, NULL, 'B'}, - {"button-dismiss", required_argument, NULL, 'Z'}, - {"debug", no_argument, NULL, 'd'}, - {"edge", required_argument, NULL, 'e'}, - {"layer", required_argument, NULL, 'y'}, - {"keyboard-focus", required_argument, NULL, 'k'}, - {"font", required_argument, NULL, 'f'}, - {"help", no_argument, NULL, 'h'}, - {"detailed-message", no_argument, NULL, 'l'}, - {"detailed-button", required_argument, NULL, 'L'}, - {"message", required_argument, NULL, 'm'}, - {"output", required_argument, NULL, 'o'}, - {"timeout", required_argument, NULL, 't'}, - {"version", no_argument, NULL, 'v'}, - - {"background-color", required_argument, NULL, TO_COLOR_BACKGROUND}, - {"button-border-color", required_argument, NULL, TO_COLOR_BUTTON_BORDER}, - {"border-bottom-color", required_argument, NULL, TO_COLOR_BORDER_BOTTOM}, - {"button-background-color", required_argument, NULL, TO_COLOR_BUTTON_BG}, - {"text-color", required_argument, NULL, TO_COLOR_TEXT}, - {"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-background-color", required_argument, NULL, TO_COLOR_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}, - {"button-margin-right", required_argument, NULL, TO_MARGIN_BTN_RIGHT}, - {"button-padding", required_argument, NULL, TO_PADDING_BTN}, - - {0, 0, 0, 0} - }; - - const char *usage = - "Usage: labnag [options...]\n" - "\n" - " -B, --button [] Create a button with text\n" - " -Z, --button-dismiss []\n" - " Like -B but dismiss nag when pressed\n" - " -d, --debug Enable debugging.\n" - " -e, --edge top|bottom Set the edge to use.\n" - " -y, --layer overlay|top|bottom|background\n" - " Set the layer to use.\n" - " -k, --keyboard-focus none|exclusive|on-demand|\n" - " Set the policy for keyboard focus.\n" - " -f, --font Set the font to use.\n" - " -h, --help Show help message and quit.\n" - " -l, --detailed-message Read a detailed message from stdin.\n" - " -L, --detailed-button Set the text of the detail button.\n" - " -m, --message Set the message text.\n" - " -o, --output Set the output to use.\n" - " -t, --timeout Set duration to close dialog.\n" - " -x, --exclusive-zone Use exclusive zone.\n" - " -v, --version Show the version number and quit.\n" - "\n" - "The following appearance options can also be given:\n" - " --background-color RRGGBB[AA] Background color.\n" - " --button-border-color RRGGBB[AA] Button border color.\n" - " --border-bottom-color RRGGBB[AA] Bottom border color.\n" - " --button-background-color RRGGBB[AA]\n" - " Button background color.\n" - " --text-color RRGGBB[AA] Text color.\n" - " --button-text-color RRGGBB[AA] Button text color.\n" - " --border-bottom-size size Thickness of the bar border.\n" - " --message-padding padding Padding for the message.\n" - " --details-border-size size Thickness for the details border.\n" - " --details-background-color RRGGBB[AA]\n" - " Details background color.\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" - " --button-margin-right margin Margin from dismiss button to edge.\n" - " --button-padding padding Padding for the button text.\n"; - - optind = 1; - while (1) { - int c = getopt_long(argc, argv, "B:Z:c:de:y:k:f:hlL:m:o:s:t:vx", opts, NULL); - if (c == -1) { - break; - } - switch (c) { - case 'B': /* Button */ - case 'Z': /* Button (Dismiss) */ { - struct button *button = calloc(1, sizeof(*button)); - if (!button) { - perror("calloc"); - return LAB_EXIT_FAILURE; - } - if (argv[optind] && argv[optind][0] != '-') { - button->action = argv[optind]; - optind++; - } - button->text = optarg; - button->dismiss = c == 'Z'; - wl_list_insert(&nag->buttons, &button->link); - break; - } - case 'd': /* Debug */ - *debug = true; - break; - case 'e': /* Edge */ - if (strcmp(optarg, "top") == 0) { - conf->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP - | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT - | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - } else if (strcmp(optarg, "bottom") == 0) { - conf->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM - | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT - | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - } else { - fprintf(stderr, "Invalid edge: %s\n", optarg); - return LAB_EXIT_FAILURE; - } - break; - case 'y': /* Layer */ - if (strcmp(optarg, "background") == 0) { - conf->layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND; - } else if (strcmp(optarg, "bottom") == 0) { - conf->layer = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; - } else if (strcmp(optarg, "top") == 0) { - conf->layer = ZWLR_LAYER_SHELL_V1_LAYER_TOP; - } else if (strcmp(optarg, "overlay") == 0) { - conf->layer = ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY; - } else { - fprintf(stderr, "Invalid layer: %s\n" - "Usage: --layer overlay|top|bottom|background\n", - optarg); - return LAB_EXIT_FAILURE; - } - break; - case 'k': - if (strcmp(optarg, "none") == 0) { - conf->keyboard_focus = - ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE; - } else if (strcmp(optarg, "exclusive") == 0) { - conf->keyboard_focus = - ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE; - } else if (strcmp(optarg, "on-demand") == 0) { - conf->keyboard_focus = - ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND; - } else { - fprintf(stderr, "Invalid keyboard focus: %s\n" - "Usage: --keyboard-focus none|exclusive|on-demand\n", - optarg); - return LAB_EXIT_FAILURE; - } - break; - case 'f': /* Font */ - pango_font_description_free(conf->font_description); - conf->font_description = pango_font_description_from_string(optarg); - break; - case 'l': /* Detailed Message */ - free(nag->details.message); - nag->details.message = read_and_trim_stdin(); - if (!nag->details.message) { - return LAB_EXIT_FAILURE; - } - nag->details.button_up.text = "▲"; - nag->details.button_down.text = "▼"; - break; - case 'L': /* Detailed Button Text */ - nag->details.details_text = optarg; - break; - case 'm': /* Message */ - nag->message = optarg; - break; - case 'o': /* Output */ - free(conf->output); - conf->output = optarg; - break; - case 't': - nag->details.close_timeout = atoi(optarg); - break; - case 'x': - nag->details.use_exclusive_zone = true; - break; - case 'v': /* Version */ - printf("labnag " LABWC_VERSION "\n"); - return LAB_EXIT_FAILURE; - case TO_COLOR_BACKGROUND: /* Background color */ - if (!parse_color(optarg, &conf->background)) { - fprintf(stderr, "Invalid background color: %s\n", optarg); - } - break; - case TO_COLOR_BUTTON_BORDER: /* Border color */ - if (!parse_color(optarg, &conf->button_border)) { - fprintf(stderr, "Invalid border color: %s\n", optarg); - } - break; - case TO_COLOR_BORDER_BOTTOM: /* Bottom border color */ - if (!parse_color(optarg, &conf->border_bottom)) { - fprintf(stderr, "Invalid border bottom color: %s\n", optarg); - } - break; - case TO_COLOR_BUTTON_BG: /* Button background color */ - if (!parse_color(optarg, &conf->button_background)) { - fprintf(stderr, "Invalid button background color: %s\n", optarg); - } - break; - case TO_COLOR_DETAILS: /* Details background color */ - if (!parse_color(optarg, &conf->details_background)) { - fprintf(stderr, "Invalid details background 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); - } - break; - case TO_COLOR_BUTTON_TEXT: /* Button text color */ - if (!parse_color(optarg, &conf->button_text)) { - fprintf(stderr, "Invalid button text color: %s\n", optarg); - } - break; - case TO_THICK_BAR_BORDER: /* Bottom border thickness */ - conf->bar_border_thickness = strtol(optarg, NULL, 0); - break; - case TO_PADDING_MESSAGE: /* Message padding */ - conf->message_padding = strtol(optarg, NULL, 0); - break; - case TO_THICK_DET_BORDER: /* Details border thickness */ - conf->details_border_thickness = strtol(optarg, NULL, 0); - break; - case TO_THICK_BTN_BORDER: /* Button border thickness */ - conf->button_border_thickness = strtol(optarg, NULL, 0); - break; - case TO_GAP_BTN: /* Gap between buttons */ - conf->button_gap = strtol(optarg, NULL, 0); - break; - case TO_GAP_BTN_DISMISS: /* Gap between dismiss button */ - conf->button_gap_close = strtol(optarg, NULL, 0); - break; - case TO_MARGIN_BTN_RIGHT: /* Margin on the right side of button area */ - conf->button_margin_right = strtol(optarg, NULL, 0); - break; - case TO_PADDING_BTN: /* Padding for the button text */ - conf->button_padding = strtol(optarg, NULL, 0); - break; - default: /* Help or unknown flag */ - fprintf(c == 'h' ? stdout : stderr, "%s", usage); - return LAB_EXIT_FAILURE; - } - } - - return LAB_EXIT_SUCCESS; -} - -int -main(int argc, char **argv) -{ - struct conf conf = { 0 }; - conf_init(&conf); - - struct nag nag = { - .conf = &conf, - }; - - wl_list_init(&nag.buttons); - wl_list_init(&nag.outputs); - wl_list_init(&nag.seats); - - nag.details.details_text = "Toggle details"; - nag.details.close_timeout = 5; - nag.details.use_exclusive_zone = false; - - bool debug = false; - if (argc > 1) { - exit_status = nag_parse_options(argc, argv, &nag, &conf, &debug); - if (exit_status == LAB_EXIT_FAILURE) { - goto cleanup; - } - } - wlr_log_init(debug ? WLR_DEBUG : WLR_ERROR, NULL); - - if (!nag.message) { - wlr_log(WLR_ERROR, "No message passed. Please provide --message/-m"); - exit_status = LAB_EXIT_FAILURE; - goto cleanup; - } - - if (nag.details.message) { - nag.details.button_details = calloc(1, sizeof(struct button)); - assert(nag.details.button_details); - nag.details.button_details->text = nag.details.details_text; - assert(nag.details.button_details->text); - nag.details.button_details->expand = true; - wl_list_insert(nag.buttons.prev, &nag.details.button_details->link); - } - - int nr_buttons = wl_list_length(&nag.buttons); - if (conf.keyboard_focus && nr_buttons > 0) { - /* select the leftmost button */ - nag.selected_button = nr_buttons - 1; - } else { - nag.selected_button = -1; - } - - wlr_log(WLR_DEBUG, "Output: %s", nag.conf->output); - wlr_log(WLR_DEBUG, "Anchors: %lu", (unsigned long)nag.conf->anchors); - wlr_log(WLR_DEBUG, "Message: %s", nag.message); - char *font = pango_font_description_to_string(nag.conf->font_description); - wlr_log(WLR_DEBUG, "Font: %s", font); - free(font); - wlr_log(WLR_DEBUG, "Buttons"); - - struct button *button; - wl_list_for_each(button, &nag.buttons, link) { - wlr_log(WLR_DEBUG, "\t[%s] `%s`", button->text, button->action); - } - - nag_setup(&nag); - - nag_run(&nag); - -cleanup: - nag_destroy(&nag); - return exit_status; -} diff --git a/clients/meson.build b/clients/meson.build deleted file mode 100644 index 467bc035..00000000 --- a/clients/meson.build +++ /dev/null @@ -1,59 +0,0 @@ -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 += 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, -) - -clients = files('lab-sensible-terminal') -install_data(clients, install_dir: get_option('bindir')) diff --git a/clients/pool-buffer.c b/clients/pool-buffer.c deleted file mode 100644 index 35e86033..00000000 --- a/clients/pool-buffer.c +++ /dev/null @@ -1,141 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copied from https://github.com/swaywm/sway - * - * Copyright (C) 2016-2017 Drew DeVault - */ -#define _POSIX_C_SOURCE 200809L -#include -#include -#include -#include -#include -#include -#include -#include -#include "pool-buffer.h" - -static int anonymous_shm_open(void) -{ - int retries = 100; - - do { - // try a probably-unique name - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - pid_t pid = getpid(); - char name[50]; - snprintf(name, sizeof(name), "/labnag-%x-%x", - (unsigned int)pid, (unsigned int)ts.tv_nsec); - - // shm_open guarantees that O_CLOEXEC is set - int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); - if (fd >= 0) { - shm_unlink(name); - return fd; - } - - --retries; - } while (retries > 0 && errno == EEXIST); - - return -1; -} - -static void buffer_release(void *data, struct wl_buffer *wl_buffer) -{ - struct pool_buffer *buffer = data; - buffer->busy = false; -} - -static const struct wl_buffer_listener buffer_listener = { - .release = buffer_release -}; - -static struct pool_buffer *create_buffer(struct wl_shm *shm, - struct pool_buffer *buf, int32_t width, int32_t height, - uint32_t format) -{ - uint32_t stride = width * 4; - size_t size = stride * height; - - int fd = anonymous_shm_open(); - if (fd == -1) { - return NULL; - } - if (ftruncate(fd, size) < 0) { - close(fd); - return NULL; - } - void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size); - buf->buffer = wl_shm_pool_create_buffer(pool, 0, - width, height, stride, format); - wl_shm_pool_destroy(pool); - close(fd); - - buf->size = size; - buf->width = width; - buf->height = height; - buf->data = data; - buf->surface = cairo_image_surface_create_for_data(data, - CAIRO_FORMAT_ARGB32, width, height, stride); - buf->cairo = cairo_create(buf->surface); - buf->pango = pango_cairo_create_context(buf->cairo); - - wl_buffer_add_listener(buf->buffer, &buffer_listener, buf); - return buf; -} - -void destroy_buffer(struct pool_buffer *buffer) -{ - if (buffer->buffer) { - wl_buffer_destroy(buffer->buffer); - buffer->buffer = NULL; - } - if (buffer->cairo) { - cairo_destroy(buffer->cairo); - buffer->cairo = NULL; - } - if (buffer->surface) { - cairo_surface_destroy(buffer->surface); - buffer->surface = NULL; - } - if (buffer->pango) { - g_object_unref(buffer->pango); - buffer->pango = NULL; - } - if (buffer->data) { - munmap(buffer->data, buffer->size); - buffer->data = NULL; - } -} - -struct pool_buffer *get_next_buffer(struct wl_shm *shm, - struct pool_buffer pool[static 2], uint32_t width, uint32_t height) -{ - struct pool_buffer *buffer = NULL; - - for (size_t i = 0; i < 2; ++i) { - if (pool[i].busy) { - continue; - } - buffer = &pool[i]; - } - - if (!buffer) { - return NULL; - } - - if (buffer->width != width || buffer->height != height) { - destroy_buffer(buffer); - } - - if (!buffer->buffer) { - if (!create_buffer(shm, buffer, width, height, - WL_SHM_FORMAT_ARGB8888)) { - return NULL; - } - } - buffer->busy = true; - return buffer; -} diff --git a/clients/pool-buffer.h b/clients/pool-buffer.h deleted file mode 100644 index 28282ca0..00000000 --- a/clients/pool-buffer.h +++ /dev/null @@ -1,30 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copied from https://github.com/swaywm/sway - * - * Copyright (C) 2016-2017 Drew DeVault - */ -#ifndef LAB_POOL_BUFFER_H -#define LAB_POOL_BUFFER_H -#include -#include -#include -#include -#include - -struct pool_buffer { - struct wl_buffer *buffer; - cairo_surface_t *surface; - cairo_t *cairo; - PangoContext *pango; - uint32_t width, height; - void *data; - size_t size; - bool busy; -}; - -struct pool_buffer *get_next_buffer(struct wl_shm *shm, - struct pool_buffer pool[static 2], uint32_t width, uint32_t height); -void destroy_buffer(struct pool_buffer *buffer); - -#endif /* LAB_POOL_BUFFER_H */ diff --git a/docs/labnag.1.scd b/docs/labnag.1.scd deleted file mode 100644 index a2a36c10..00000000 --- a/docs/labnag.1.scd +++ /dev/null @@ -1,143 +0,0 @@ -labnag(1) - -# NAME - -labnag - Show dialog with message and buttons - -# SYNOPSIS - -_labnag_ [options...] - -# OPTIONS - -*-B, --button* [] - Create a button with the text _text_ that optionally executes _action_ - when pressed. Multiple buttons can be defined by providing the flag - multiple times. Buttons will appear in the order they are provided from - left to right. - -*-Z, --button-dismiss* [] - Create a button with the text _text_ that optionally executes _action_ - when pressed, and dismisses labnag. Multiple buttons can be defined by - providing the flag multiple times. Buttons will appear in the order - they are provided from left to right. - -*-d, --debug* - Enable debugging. - -*-e, --edge* top|bottom - Set the edge to use. - -*-y, --layer* overlay|top|bottom|background - Set the layer to use. - -*-k, --keyboard-focus none|exclusive|on-demand* - Set the policy for keyboard focus. - -*-f, --font* - Set the font to use. - -*-h, --help* - Show help message and quit. - -*-l, --detailed-message* - Read a detailed message from stdin. A button to toggle details will be - added. Details are shown in a scrollable multi-line text area. - -*-L, --detailed-button* - Set the text for the button that toggles details. This has no effect if - there is not a detailed message. The default is _Toggle details_. - -*-m, --message* - Set the message text. - -*-o, --output* - Set the output to use. This should be the name of a _xdg\_output_. - -*-t, --timeout* - Set duration to close dialog. Default is 5 seconds. - -*-x, --exclusive-zone* - Use exclusive zone. Default is false. - -*-v, --version* - Show the version number and quit. - -# APPEARANCE OPTIONS - -*--background-color* - Set the color of the background. - -*--button-border-color* - Set the color of the button border. - -*--border-bottom-color* - Set the color of the bottom border. - -*--button-background-color* - Set the color for the background for buttons. - -*--text-color* - Set the text color. - -*--button-text-color* - Set the button text color. - -*--border-bottom-size* - Set the thickness of the bottom border. - -*--message-padding* - Set the padding for the message. - -*--details-background-color* - Set the color for the background for details. - -*--details-border-size* - Set the thickness for the details border. - -*--button-border-size* - Set the thickness for the button border. - -*--button-gap* - Set the size of the gap between buttons. - -*--button-dismiss-gap* - Set the size of the gap between the dismiss button and another button. - -*--button-margin-right* - Set the margin from the right of the dismiss button to edge. - -*--button-padding* - Set the padding for the button text. - -# EXAMPLE - -This is a simple example of a _labnag_ logout GUI. - -``` -#!/bin/sh - -# logout with labnag - -labnag \\ - -f "Hack Regular 10"\\ - -m "Choose your logout option"\\ - -Z " Lock " "gtklock -d"\\ - -Z " Logout " "labwc -e"\\ - -Z "Shutdown " "systemctl poweroff"\\ - -Z " Reboot " "systemctl reboot"\\ - -Z "Hibernate" "systemctl hibernate"\\ - -Z " Suspend " "systemctl suspend"\\ - -Z " Cancel "\\ - --background-color 00ffff\\ - --button-background-color 00ffff\\ - --button-border-color 00ccccaa\\ - --text-color 000000\\ - --button-text-color 000000\\ - --button-gap 8\\ - --button-margin-right 0\\ - --button-padding 5\\ - --button-border-size 2\\ - -t 60 -``` - diff --git a/docs/labwc-actions.5.scd b/docs/labwc-actions.5.scd index 4a76a374..bf760fdb 100644 --- a/docs/labwc-actions.5.scd +++ b/docs/labwc-actions.5.scd @@ -92,18 +92,10 @@ Actions are used in menus and keyboard/mouse bindings. Move window relative to its current position. Positive value of x moves it right, negative left. Positive value of y moves it down, negative up. -**++ -** - Resize window to fill half or quarter the output in the given direction. - - *direction* [up|down|left|right|up-left|up-right|down-left|down-right|center] - Direction in which to snap the window. - - *combine* [yes|no] - Allows to snap a window to an output corner by combining two - directions. For example, snapping a window to *right* and then - to *up* places it in the *up-right* quarter of the output. - Default is no. +**++ +** + Resize window to fill half the output in the given direction. Supports + directions "left", "up", "right", "down" and "center". ToggleSnapToEdge additionally toggles the active window between tiled to the given direction and its untiled position. @@ -235,10 +227,10 @@ Actions are used in menus and keyboard/mouse bindings. window. ** - Stop handling keybinds/mousebinds other than ToggleKeybinds itself. - This can be used to allow A-Tab and similar keybinds/mousebinds to be - delivered to Virtual Machines, VNC clients or nested compositors. - A second call will restore all original keybinds/mousebinds. + Stop handling keybinds other than ToggleKeybinds itself. + This can be used to allow A-Tab and similar keybinds to be delivered + to Virtual Machines, VNC clients or nested compositors. + A second call will restore all original keybinds. This action will only affect the window that had keyboard focus when the binding was executed. Thus when switching to another window, all @@ -285,7 +277,7 @@ Actions are used in menus and keyboard/mouse bindings. Resizes active window size to width and height of the output when the window size exceeds the output size. -** +** Switch to workspace. *to* The workspace to switch to. Supported values are "current", "last", @@ -295,9 +287,6 @@ Actions are used in menus and keyboard/mouse bindings. *wrap* [yes|no] Wrap around from last desktop to first, and vice versa. Default yes. - *toggle* [yes|no] Toggle to “last” if already on the workspace that - would be the actual destination. Default no. - ** Send active window to workspace. @@ -433,7 +422,6 @@ Actions that execute other actions. Used in keyboard/mouse bindings. ``` - @@ -486,7 +474,7 @@ Actions that execute other actions. Used in keyboard/mouse bindings. The "left" , "right", "left-occupied" and "right-occupied" directions will not wrap. - *tiled* [up|right|down|left|up-left|up-right|down-left|down-right|center|any] + *tiled* [up|right|down|left|center] Whether the client is tiled (snapped) along the the indicated screen edge. @@ -505,26 +493,6 @@ Actions that execute other actions. Used in keyboard/mouse bindings. This argument is optional. - *prompt* - Display a yes/no prompt dialog (labnag by default). If 'yes' is - selected, the *then* branch will be taken; and similarly with - 'no' and *else*. This argument is optional. Note that the syntax - is different to that of Openbox where a prompt element is not - tied to If-actions but would just be a child of the downstream - action. The reason for this difference is increased flexibility - and functionality gained by optionally using an *else* branch. - - ``` - - - - - - - - - ``` - *then* A list of actions to be executed if the window matches any query. This argument is optional. diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index 00817898..5b303138 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -178,7 +178,6 @@ this is for compatibility with Openbox. no no yes - [see details below] ``` @@ -217,29 +216,12 @@ this is for compatibility with Openbox. ** [yes|no] Automatically enable outputs at startup and when new outputs are - connected. This option applies only to drm outputs. Default is yes. + connected. Default is yes. Caution: Disabling this option will make the labwc session unusable unless an external tool such as `wlr-randr` or `kanshi` is used to manage outputs. - The reason for the existance of this option is that after losing signal - from the PC (e.g. by `wlopm -off`), some monitors do an input detection - that makes it appear (from the PC side) to disconnect and reconnect a - few seconds later, causing the monitor to turn back on again (as labwc - auto-enables newly connected outputs by default). - - An example usage pattern to avoid the above behavior looks as follows: - - Set ** to *no* - - Run kanshi (e.g. from autostart) and rely on it to enable new outputs - - Have swayidle kill and restart kanshi when entering powersave as - follows: - - ``` - swayidle -w timeout 600 \\ - 'pkill kanshi ; wlopm --off \*' resume 'kanshi & wlopm --on \*' - ``` - ** [yes|no] Try to re-use the existing output mode (resolution / refresh rate). This may prevent unnecessary screenblank delays when starting labwc @@ -260,55 +242,6 @@ this is for compatibility with Openbox. up/down) in Chromium and electron based clients without inadvertantly pasting the primary clipboard. Default is yes. -** - Set command to be invoked for an action prompt (**) - - The following conversion specifiers are supported: - - *%m*: the ** message option - - *%n*: "No" (in local language if translation is available) - - *%y*: "Yes" (in local language if translation is available) - - *%b*: osd.bg.color - - *%t*: osd.label.text.color - - The default prompt command is: - - ``` - labnag \\ - --message '%m' \\ - --button-dismiss '%n' \\ - --button-dismiss '%y' \\ - --background-color '%b' \\ - --text-color '%t' \\ - --button-border-color '%t' \\ - --border-bottom-color '%t' \\ - --button-background-color '%b' \\ - --button-text-color '%t' \\ - --border-bottom-size 1 \\ - --button-border-size 3 \\ - --keyboard-focus on-demand \\ - --layer overlay \\ - --timeout 0 - ``` - - Example 1: The prompt can be configured to use a different dialog client - - ``` - - zenity --question --text="%m" - - ``` - - Example 2: A more complex zenity command could be used: - - ``` - zenity \\ - --question \\ - --title="" \\ - --text="%m" \\ - --ok-label="%y" \\ - --cancel-label="%n" - ``` - ## PLACEMENT ``` @@ -339,7 +272,7 @@ this is for compatibility with Openbox. ## WINDOW SWITCHER ``` - + @@ -348,14 +281,10 @@ this is for compatibility with Openbox. ``` -** +** *show* [yes|no] Draw the OnScreenDisplay when switching between windows. Default is yes. - *style* [classic|thumbnail] Configures the style of the OnScreenDisplay. - "classic" displays window information like icons and titles in a vertical list. - "thumbnail" shows window thumbnail, icon and title in grids. - *preview* [yes|no] Preview the contents of the selected window when switching between windows. Default is yes. @@ -366,11 +295,8 @@ this is for compatibility with Openbox. they are on. Default no (that is only windows on the current workspace are shown). - *unshade* [yes|no] Temporarily unshade windows when switching between - them and permanently unshade on the final selection. Default is yes. - ** - Define window switcher fields when using **. + Define window switcher fields. *content* defines what the field shows and can be any of: @@ -402,9 +328,9 @@ this is for compatibility with Openbox. fields are: - 'B' - shell type, values [xwayland|xdg-shell] - 'b' - shell type (short form), values [X|W] - - 'S' - state of window, values [m|s|M|F] (4 spaces allocated) - (minimized, shaded, maximized, fullscreen) - - 's' - state of window (short form), values [m|s|M|F] (1 space) + - 'S' - state of window, values [M|m|F] (3 spaces allocated) + (maximized, minimized, fullscreen) + - 's' - state of window (short form), values [M|m|F] (1 space) - 'I' - wm-class/app-id - 'i' - wm-class/app-id trimmed, remove "org." if available - 'n' - desktop entry/file application name, falls back to @@ -486,13 +412,11 @@ activated with SnapToEdge actions or, optionally, by dragging windows to the edges of an output. Edge snapping causes a window to occupy half of its output, extending outward from the snapped edge. -**++ -** - If an interactive move ends with the cursor within ** pixels of an - output edge, the window is snapped to the edge. If it's also within - ** pixels of an output corner, the window is snapped to the - corner instead. A ** of 0 disables snapping. - Default is 10 for ** and 50 for **. +** + If an interactive move ends with the cursor a maximum distance *range*, + (in pixels) from the edge of an output, the move will trigger a + SnapToEdge action for that edge. A *range* of 0 disables snapping via + interactive moves. Default is 10. ** [yes|no] Show an overlay when snapping to a window to an edge. Default is yes. @@ -602,11 +526,6 @@ extending outward from the snapped edge. Even when disabling server side decorations via ToggleDecorations, keep a small border (and resize area) around the window. Default is yes. -** [titlebar|none] - Specify how server side decorations are shown for maximized windows. - *titlebar* shows titlebar above a maximized window. *none* shows no server - side decorations around a maximized window. Default is titlebar. - ** [yes|no] Should drop-shadows be rendered behind windows. Default is no. @@ -779,7 +698,7 @@ extending outward from the snapped edge. W-Return - lab-sensible-terminal A-F4 - close window W-a - toggle maximize - W- - resize window to fill half or quarter of the output + W- - resize window to fill half the output A-Space - show window menu ``` @@ -816,7 +735,6 @@ extending outward from the snapped edge. - AllDesktops: A button that, by default, toggles omnipresence of a window. - Close: A button that, by default, closses 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. - Left: The left edge of the window's border. diff --git a/docs/labwc-theme.5.scd b/docs/labwc-theme.5.scd index 8097615b..fd21edd8 100644 --- a/docs/labwc-theme.5.scd +++ b/docs/labwc-theme.5.scd @@ -303,79 +303,32 @@ all are supported. Text color of on-screen-display. Inherits *window.active.label.text.color* if not set. -*osd.window-switcher.style-classic* - Theme for window switcher when using . - See below for details. +*osd.window-switcher.width* + Width of window switcher in pixels. Default is 600. + Width can also be percent of the width of the monitor. + % is mandatory as last character in this case, max 100% -*osd.window-switcher.style-classic.width* - Width of window switcher in pixels. Width can also be a percentage of the - monitor width by adding '%' as suffix (e.g. 70%). Default is 600. - -*osd.window-switcher.style-classic.padding* +*osd.window-switcher.padding* Padding of window switcher in pixels. This is the space between the window-switcher border and its items. Default is 4. -*osd.window-switcher.style-classic.item.padding.x* +*osd.window-switcher.item.padding.x* Horizontal padding of window switcher entries in pixels. Default is 10. -*osd.window-switcher.style-classic.item.padding.y* +*osd.window-switcher.item.padding.y* Vertical padding of window switcher entries in pixels. Default is 1. -*osd.window-switcher.style-classic.item.active.border.width* +*osd.window-switcher.item.active.border.width* Border width of the selection box in the window switcher in pixels. Default is 2. -*osd.window-switcher.style-classic.item.active.border.color* - Border color around the selected window switcher item. - Default is *osd.label.text.color* with 50% opacity. - -*osd.window-switcher.style-classic.item.active.bg.color* - Background color of the selected window switcher item. - Default is *osd.label.text.color* with 15% opacity. - -*osd.window-switcher.style-classic.item.icon.size* +*osd.window-switcher.item.icon.size* Size of the icon in window switcher, in pixels. If not set, the font size derived from is used. -*osd.window-switcher.style-thumbnail* - Theme for window switcher when using . - See below for details. - -*osd.window-switcher.style-thumbnail.width.max* - Maximum width of window switcher in pixels. Width can also be a percentage of - the monitor width by adding '%' as suffix (e.g. 70%). Default is 80%. - -*osd.window-switcher.style-thumbnail.padding* - Padding of window switcher in pixels. This is the space between the - window-switcher border and its items. Default is 4. - -*osd.window-switcher.style-thumbnail.item.width* - Width of window switcher items in pixels. Default is 300. - -*osd.window-switcher.style-thumbnail.item.height* - Height of window switcher items in pixels. Default is 250. - -*osd.window-switcher.style-thumbnail.item.padding* - Padding of window switcher items in pixels. This is the space between the - border around selected items and window thumbnail. Default is 2. - -*osd.window-switcher.style-thumbnail.item.active.border.width* - Border width of selected window switcher items in pixels. Default is 2. - -*osd.window-switcher.style-thumbnail.item.active.border.color* - Color of border around selected window switcher items. - Default is *osd.label.text.color* with 50% opacity. - -*osd.window-switcher.style-thumbnail.item.active.bg.color* - Color of selected window switcher items. - Default is *osd.label.text.color* with 15% opacity. - -*osd.window-switcher.style-thumbnail.item.icon.size* - Size of window icons in window switcher items in pixels. Default is 60. - *osd.window-switcher.preview.border.width* Border width of the outlines shown as the preview of the window selected by window switcher. Inherits *osd.border.width* if not set. diff --git a/docs/labwc.1.scd b/docs/labwc.1.scd index 4840c0fa..4e643918 100644 --- a/docs/labwc.1.scd +++ b/docs/labwc.1.scd @@ -134,7 +134,8 @@ example: *LABWC_DEBUG_FOO=1 labwc*. Increase logging of paths for config files (for example rc.xml, autostart, environment and menu.xml) as well as titlebar buttons. -*LABWC_DEBUG_CONFIG_NODENAMES* +*LABWC_DEBUG_CONFIG_NODENAMES*++ +*LABWC_DEBUG_MENU_NODENAMES* Enable logging of all nodenames (for example *policy.placement: Cascade* for *Cascade*) for config and menu files respectively. diff --git a/docs/menu.xml b/docs/menu.xml index 4b4d5dda..d1ea8607 100644 --- a/docs/menu.xml +++ b/docs/menu.xml @@ -1,3 +1,5 @@ + + @@ -23,7 +25,18 @@ Any menu with the id "workspaces" will be hidden if there is only a single workspace available. --> - + + + + + + + + + + + + diff --git a/docs/meson.build b/docs/meson.build index 2f89f2e7..a6676784 100644 --- a/docs/meson.build +++ b/docs/meson.build @@ -1,16 +1,16 @@ scdoc = find_program('scdoc', required: get_option('man-pages')) if scdoc.found() - manpages = [ - 'labwc.1', - 'labwc-actions.5', - 'labwc-config.5', - 'labwc-menu.5', - 'labwc-theme.5', - 'labnag.1', + sections = [ + '.1', + '-actions.5', + '-config.5', + '-menu.5', + '-theme.5', ] - foreach manpage : manpages - markdown = manpage + '.scd' + foreach s : sections + markdown = 'labwc' + s + '.scd' + manpage = 'labwc' + s custom_target( manpage, input: markdown, @@ -19,7 +19,7 @@ if scdoc.found() feed: true, capture: true, install: true, - install_dir: join_paths(get_option('mandir'), 'man' + manpage.split('.')[-1]) + install_dir: join_paths(get_option('mandir'), 'man' + s.split('.')[-1]) ) endforeach endif diff --git a/docs/rc.xml b/docs/rc.xml index ec9a2f6f..edcca868 100644 --- a/docs/rc.xml +++ b/docs/rc.xml @@ -1,3 +1,5 @@ + + @@ -16,10 +16,6 @@ no no yes - @@ -42,7 +38,6 @@ 8 yes - titlebar no no @@ -77,8 +72,7 @@ - + @@ -161,7 +155,6 @@ 10 - 50 @@ -274,16 +267,16 @@ - + - + - + - + @@ -378,11 +371,42 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -584,13 +608,7 @@ - sendEventsMode [yes|no|disabledOnExternalMouse] - calibrationMatrix [six float values split by space] - scrollFactor [float] - - The following ... block may not be complete for - your requirements. Default values are device specific. Only set an option - if you require to override the default. Valid values must be inserted. - --> - diff --git a/docs/themerc b/docs/themerc index 9139170c..045c1021 100644 --- a/docs/themerc +++ b/docs/themerc @@ -91,27 +91,14 @@ osd.label.text.color: #000000 # width can be set as percent (of screen width) # example 50% or 75% instead of 600, max 100% -osd.window-switcher.style-classic.width: 600 +osd.window-switcher.width: 600 -osd.window-switcher.style-classic.padding: 4 -osd.window-switcher.style-classic.item.padding.x: 10 -osd.window-switcher.style-classic.item.padding.y: 1 -osd.window-switcher.style-classic.item.active.border.width: 2 -osd.window-switcher.style-classic.item.active.border.color: #706f6d -osd.window-switcher.style-classic.item.active.bg.color: #bfbcba +osd.window-switcher.padding: 4 +osd.window-switcher.item.padding.x: 10 +osd.window-switcher.item.padding.y: 1 +osd.window-switcher.item.active.border.width: 2 # The icon size the same as the font size by default -# osd.window-switcher.style-classic.item.icon.size: 50 - -osd.window-switcher.style-thumbnail.width.max: 80% -osd.window-switcher.style-thumbnail.padding: 4 -osd.window-switcher.style-thumbnail.item.width: 300 -osd.window-switcher.style-thumbnail.item.height: 250 -osd.window-switcher.style-thumbnail.item.padding: 10 -osd.window-switcher.style-thumbnail.item.active.border.width: 2 -osd.window-switcher.style-thumbnail.item.active.border.color: #706f6d -osd.window-switcher.style-thumbnail.item.active.bg.color: #bfbcba -osd.window-switcher.style-thumbnail.item.icon.size: 60 - +# osd.window-switcher.item.icon.size: 50 osd.window-switcher.preview.border.width: 1 osd.window-switcher.preview.border.color: #dddda6,#000000,#dddda6 diff --git a/include/action-prompt-codes.h b/include/action-prompt-codes.h deleted file mode 100644 index 68d1b066..00000000 --- a/include/action-prompt-codes.h +++ /dev/null @@ -1,9 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef LABWC_ACTION_PROMPT_CODES_H -#define LABWC_ACTION_PROMPT_CODES_H - -#define LAB_EXIT_FAILURE 255 -#define LAB_EXIT_CANCELLED 254 -#define LAB_EXIT_SUCCESS 0 - -#endif /* LABWC_ACTION_PROMPT_CODES_H */ diff --git a/include/action.h b/include/action.h index 7692e383..b8a271e9 100644 --- a/include/action.h +++ b/include/action.h @@ -3,7 +3,6 @@ #define LABWC_ACTION_H #include -#include #include struct view; @@ -23,8 +22,6 @@ struct action { struct action *action_create(const char *action_name); -const char *action_get_str(struct action *action, const char *key, - const char *default_value); bool action_is_valid(struct action *action); bool action_is_show_menu(struct action *action); @@ -50,9 +47,6 @@ bool actions_contain_toggle_keybinds(struct wl_list *action_list); void actions_run(struct view *activator, struct server *server, struct wl_list *actions, struct cursor_context *ctx); -void action_prompts_destroy(void); -bool action_check_prompt_result(pid_t pid, int exit_code); - void action_free(struct action *action); void action_list_free(struct wl_list *action_list); diff --git a/include/common/array.h b/include/common/array.h index b60bafd6..a1ea7499 100644 --- a/include/common/array.h +++ b/include/common/array.h @@ -1,9 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0-only */ #ifndef LABWC_ARRAY_H #define LABWC_ARRAY_H - +#include +#include #include -#include "common/mem.h" /* * Wayland's wl_array API is a bit sparse consisting only of @@ -66,7 +66,10 @@ wl_array_len(struct wl_array *array) #define array_add(_arr, _val) do { \ __typeof__(_val) *_entry = wl_array_add( \ (_arr), sizeof(__typeof__(_val))); \ - die_if_null(_entry); \ + if (!_entry) { \ + perror("Failed to allocate memory"); \ + exit(EXIT_FAILURE); \ + } \ *_entry = (_val); \ } while (0) diff --git a/include/common/box.h b/include/common/box.h index 45c0fc4d..3d900a0e 100644 --- a/include/common/box.h +++ b/include/common/box.h @@ -18,7 +18,7 @@ void box_union(struct wlr_box *box_dest, struct wlr_box *box_a, * The returned x & y coordinates are the centered content position * relative to the top-left corner of the bounding box. */ -struct wlr_box box_fit_within(int width, int height, struct wlr_box *bound); +struct wlr_box box_fit_within(int width, int height, struct wlr_box *bounding_box); struct wlr_fbox box_to_fbox(struct wlr_box *box); diff --git a/include/common/buf.h b/include/common/buf.h index 1298cac2..a75c1144 100644 --- a/include/common/buf.h +++ b/include/common/buf.h @@ -50,17 +50,6 @@ void buf_expand_shell_variables(struct buf *s); */ void buf_add_fmt(struct buf *s, const char *fmt, ...); -/** - * buf_add_hex_color - add rgb color as hex string to C string buffer - * @s: buffer - * @color: rgb color to be added - * - * For example: - * - With the input 'red' (defined as red[4] = { 1.0f, 0.0f, 0.0f, 1.0f}) the - * string "#ff0000ff" will be written to the buffer. - */ -void buf_add_hex_color(struct buf *s, float color[4]); - /** * buf_add - add data to C string buffer * @s: buffer @@ -71,9 +60,9 @@ void buf_add(struct buf *s, const char *data); /** * buf_add_char - add single char to C string buffer * @s: buffer - * @ch: char to be added + * @data: char to be added */ -void buf_add_char(struct buf *s, char ch); +void buf_add_char(struct buf *s, char data); /** * buf_clear - clear the buffer, internal allocations are preserved @@ -109,11 +98,4 @@ void buf_reset(struct buf *s); */ void buf_move(struct buf *dst, struct buf *src); -/** - * buf_from_file - read file into memory buffer - * @filename: file to read - * Free returned buffer with buf_reset(). - */ -struct buf buf_from_file(const char *filename); - #endif /* LABWC_BUF_H */ diff --git a/include/common/direction.h b/include/common/direction.h new file mode 100644 index 00000000..bc8cbd0c --- /dev/null +++ b/include/common/direction.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_DIRECTION_H +#define LABWC_DIRECTION_H + +#include "view.h" + +enum wlr_direction direction_from_view_edge(enum view_edge edge); +enum wlr_direction direction_get_opposite(enum wlr_direction direction); + +#endif /* LABWC_DIRECTION_H */ diff --git a/include/common/edge.h b/include/common/edge.h deleted file mode 100644 index 07d517f5..00000000 --- a/include/common/edge.h +++ /dev/null @@ -1,81 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef LABWC_EDGE_H -#define LABWC_EDGE_H - -#include - -/** - * Unified/overloaded enum representing edges, corners, and directions. - * Used in many different contexts (moving, resizing, tiling) and with - * somewhat different semantics depending on context. - * - * Examples: - * - LAB_EDGE_TOP can also mean "up" or "north". - * - LAB_EDGES_TOP_LEFT can mean "top left corner" or "northwest". - * - * The enum is designed to be used as a bitset, and combinations of - * edges typically mean what you'd expect from the context. For example, - * LAB_EDGES_TOP_LEFT is used when resizing a view from its top-left - * corner, or when tiling a view in the top-left corner of an output. - * - * All 16 possible combinations of TOP/BOTTOM/LEFT/RIGHT are listed for - * completeness. Not all combinations make sense in all contexts. - * - * LAB_EDGE_NONE is sometimes used to mean "invalid". - * - * LAB_EDGE_ANY means "any edge or combination of edges (except NONE)" - * and is distinct from LAB_EDGE_ALL (which means all 4 edges). - * - * LAB_EDGE_TOP/BOTTOM/LEFT/RIGHT match the corresponding values of - * enum wlr_edges and enum wlr_direction, so that conversion between - * enums can be done with a simple cast. - */ -enum lab_edge { - LAB_EDGE_NONE = 0, - - LAB_EDGE_TOP = (1 << 0), /* or UP */ - LAB_EDGE_BOTTOM = (1 << 1), /* or DOWN */ - LAB_EDGE_LEFT = (1 << 2), - LAB_EDGE_RIGHT = (1 << 3), - LAB_EDGE_CENTER = (1 << 4), /* for window tiling */ - LAB_EDGE_ANY = (1 << 5), /* for window rules */ - - /* corners or ordinal directions (NW/NE/SW/SE) */ - LAB_EDGES_TOP_LEFT = (LAB_EDGE_TOP | LAB_EDGE_LEFT), - LAB_EDGES_TOP_RIGHT = (LAB_EDGE_TOP | LAB_EDGE_RIGHT), - LAB_EDGES_BOTTOM_LEFT = (LAB_EDGE_BOTTOM | LAB_EDGE_LEFT), - LAB_EDGES_BOTTOM_RIGHT = (LAB_EDGE_BOTTOM | LAB_EDGE_RIGHT), - - /* opposite edges */ - LAB_EDGES_TOP_BOTTOM = (LAB_EDGE_TOP | LAB_EDGE_BOTTOM), - LAB_EDGES_LEFT_RIGHT = (LAB_EDGE_LEFT | LAB_EDGE_RIGHT), - - /* all 4 edges */ - LAB_EDGES_ALL = (LAB_EDGE_TOP | LAB_EDGE_BOTTOM | - LAB_EDGE_LEFT | LAB_EDGE_RIGHT), - - /* 3-edge combinations (for completeness) */ - LAB_EDGES_EXCEPT_TOP = (LAB_EDGES_ALL ^ LAB_EDGE_TOP), - LAB_EDGES_EXCEPT_BOTTOM = (LAB_EDGES_ALL ^ LAB_EDGE_BOTTOM), - LAB_EDGES_EXCEPT_LEFT = (LAB_EDGES_ALL ^ LAB_EDGE_LEFT), - LAB_EDGES_EXCEPT_RIGHT = (LAB_EDGES_ALL ^ LAB_EDGE_RIGHT), -}; - -enum lab_edge lab_edge_parse(const char *direction, bool tiled, bool any); - -/** - * Returns true if edge is TOP, BOTTOM, LEFT, or RIGHT - * (i.e. one of the four cardinal directions N/S/W/E) - */ -bool lab_edge_is_cardinal(enum lab_edge edge); - -/** - * lab_edge_invert() - select the opposite of a provided edge - * - * Returns LAB_EDGE_NONE for edges other than TOP/BOTTOM/LEFT/RIGHT. - * - * @edge: edge to be inverted - */ -enum lab_edge lab_edge_invert(enum lab_edge edge); - -#endif /* LABWC_EDGE_H */ diff --git a/include/common/grab-file.h b/include/common/grab-file.h new file mode 100644 index 00000000..6a6de56d --- /dev/null +++ b/include/common/grab-file.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Read file into memory + * + * Copyright Johan Malm 2020 + */ + +#ifndef LABWC_GRAB_FILE_H +#define LABWC_GRAB_FILE_H + +#include "common/buf.h" + +/** + * grab_file - read file into memory buffer + * @filename: file to read + * Free returned buffer with buf_reset(). + */ +struct buf grab_file(const char *filename); + +#endif /* LABWC_GRAB_FILE_H */ diff --git a/include/common/graphic-helpers.h b/include/common/graphic-helpers.h index 886a4c92..3fdde6ad 100644 --- a/include/common/graphic-helpers.h +++ b/include/common/graphic-helpers.h @@ -2,8 +2,6 @@ #ifndef LABWC_GRAPHIC_HELPERS_H #define LABWC_GRAPHIC_HELPERS_H -#include -#include #include struct wlr_fbox; diff --git a/include/common/mem.h b/include/common/mem.h index ba19c1d6..d0d5fdc5 100644 --- a/include/common/mem.h +++ b/include/common/mem.h @@ -4,12 +4,6 @@ #include -/* - * If ptr is NULL, prints an error (based on errno) and exits. - * Suitable for checking the return value of malloc() etc. - */ -void die_if_null(void *ptr); - /* * As defined in busybox, weston, etc. * Allocates zero-filled memory; calls exit() on error. diff --git a/include/common/node-type.h b/include/common/node-type.h deleted file mode 100644 index ac83b2b5..00000000 --- a/include/common/node-type.h +++ /dev/null @@ -1,75 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef LABWC_NODE_TYPE_H -#define LABWC_NODE_TYPE_H - -#include "common/edge.h" - -/* - * In labwc, "node type" indicates the role of a wlr_scene_node in the - * overall desktop. It also maps more-or-less to the openbox concept of - * "context" (as used when defining mouse bindings). - * - * Some types (BUTTON, BORDER, etc.) refer to groups or categories of - * other node types rather than individual nodes. These categories are - * defined as ranges (e.g. BUTTON means anything from BUTTON_FIRST to - * BUTTON_LAST), therefore order is significant. - * - * Be sure to keep node_type_contains() in sync with any changes! - */ -enum lab_node_type { - LAB_NODE_NONE = 0, - - LAB_NODE_BUTTON_CLOSE = 1, - LAB_NODE_BUTTON_MAXIMIZE, - LAB_NODE_BUTTON_ICONIFY, - LAB_NODE_BUTTON_WINDOW_ICON, - LAB_NODE_BUTTON_WINDOW_MENU, - LAB_NODE_BUTTON_SHADE, - LAB_NODE_BUTTON_OMNIPRESENT, - LAB_NODE_BUTTON_FIRST = LAB_NODE_BUTTON_CLOSE, - LAB_NODE_BUTTON_LAST = LAB_NODE_BUTTON_OMNIPRESENT, - LAB_NODE_BUTTON, - - LAB_NODE_TITLEBAR, - LAB_NODE_TITLE, - - LAB_NODE_CORNER_TOP_LEFT, - LAB_NODE_CORNER_TOP_RIGHT, - LAB_NODE_CORNER_BOTTOM_RIGHT, - LAB_NODE_CORNER_BOTTOM_LEFT, - LAB_NODE_BORDER_TOP, - LAB_NODE_BORDER_RIGHT, - LAB_NODE_BORDER_BOTTOM, - LAB_NODE_BORDER_LEFT, - LAB_NODE_BORDER, - - LAB_NODE_CLIENT, - LAB_NODE_FRAME, - LAB_NODE_ROOT, - LAB_NODE_MENUITEM, - LAB_NODE_OSD, - LAB_NODE_LAYER_SURFACE, - LAB_NODE_UNMANAGED, - LAB_NODE_ALL, - - /* translated to LAB_NODE_CLIENT by get_cursor_context() */ - LAB_NODE_VIEW, - LAB_NODE_XDG_POPUP, - LAB_NODE_LAYER_POPUP, - LAB_NODE_SESSION_LOCK_SURFACE, - LAB_NODE_IME_POPUP, - - /* - * translated to LAB_CORNER_* or LAB_BORDER* by - * ssd_get_resizing_type() - */ - LAB_NODE_SSD_ROOT, -}; - -enum lab_node_type node_type_parse(const char *context); - -bool node_type_contains(enum lab_node_type whole, enum lab_node_type part); - -enum lab_edge node_type_to_edges(enum lab_node_type type); - -#endif /* LABWC_NODE_TYPE_H */ diff --git a/include/common/nodename.h b/include/common/nodename.h index 89a4fb1a..f05c1283 100644 --- a/include/common/nodename.h +++ b/include/common/nodename.h @@ -4,6 +4,7 @@ #include #include +#include /** * nodename - give xml node an ascii name diff --git a/include/common/parse-bool.h b/include/common/parse-bool.h index 5f306d10..97afd586 100644 --- a/include/common/parse-bool.h +++ b/include/common/parse-bool.h @@ -1,18 +1,17 @@ /* SPDX-License-Identifier: GPL-2.0-only */ #ifndef LABWC_PARSE_BOOL_H #define LABWC_PARSE_BOOL_H - #include -#include "config/types.h" +#include "common/three-state.h" /** - * parse_tristate() - Parse boolean value of string as a three-state enum. + * parse_three_state() - Parse boolean value of string as a three-state enum. * @string: String to interpret. This check is case-insensitive. * * Return: LAB_STATE_DISABLED for false; LAB_STATE_ENABLED for true; * LAB_STATE_UNSPECIFIED for non-boolean */ -enum lab_tristate parse_tristate(const char *str); +enum three_state parse_three_state(const char *str); /** * parse_bool() - Parse boolean value of string. diff --git a/include/scaled-buffer/scaled-font-buffer.h b/include/common/scaled-font-buffer.h similarity index 87% rename from include/scaled-buffer/scaled-font-buffer.h rename to include/common/scaled-font-buffer.h index a5e95087..ddccbc82 100644 --- a/include/scaled-buffer/scaled-font-buffer.h +++ b/include/common/scaled-font-buffer.h @@ -6,7 +6,7 @@ struct wlr_scene_tree; struct wlr_scene_buffer; -struct scaled_buffer; +struct scaled_scene_buffer; struct scaled_font_buffer { struct wlr_scene_buffer *scene_buffer; @@ -19,7 +19,7 @@ struct scaled_font_buffer { float color[4]; float bg_color[4]; struct font font; - struct scaled_buffer *scaled_buffer; + struct scaled_scene_buffer *scaled_buffer; /* * The following fields are used only for the titlebar, where @@ -34,7 +34,7 @@ struct scaled_font_buffer { /** * Create an auto scaling font buffer, providing a wlr_scene_buffer node for - * display. It gets destroyed automatically when the backing scaled_buffer + * display. It gets destroyed automatically when the backing scaled_scene_buffer * is being destroyed which in turn happens automatically when the backing * wlr_scene_buffer (or one of its parents) is being destroyed. * @@ -73,4 +73,12 @@ 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 the max width of an existing auto scaling font buffer + * and force a new render. + * + * No steps are taken to detect if its actually required to render a new buffer. + */ +void scaled_font_buffer_set_max_width(struct scaled_font_buffer *self, int max_width); + #endif /* LABWC_SCALED_FONT_BUFFER_H */ diff --git a/include/scaled-buffer/scaled-icon-buffer.h b/include/common/scaled-icon-buffer.h similarity index 87% rename from include/scaled-buffer/scaled-icon-buffer.h rename to include/common/scaled-icon-buffer.h index 45492ec2..46e71ebf 100644 --- a/include/scaled-buffer/scaled-icon-buffer.h +++ b/include/common/scaled-icon-buffer.h @@ -10,7 +10,7 @@ struct wlr_scene_node; struct wlr_scene_buffer; struct scaled_icon_buffer { - struct scaled_buffer *scaled_buffer; + struct scaled_scene_buffer *scaled_buffer; struct wlr_scene_buffer *scene_buffer; struct server *server; /* for window icon */ @@ -34,7 +34,7 @@ struct scaled_icon_buffer { /* * Create an auto scaling icon buffer, providing a wlr_scene_buffer node for - * display. It gets destroyed automatically when the backing scaled_buffer + * display. It gets destroyed automatically when the backing scaled_scene_buffer * is being destroyed which in turn happens automatically when the backing * wlr_scene_buffer (or one of its parents) is being destroyed. */ @@ -48,4 +48,7 @@ void scaled_icon_buffer_set_view(struct scaled_icon_buffer *self, void scaled_icon_buffer_set_icon_name(struct scaled_icon_buffer *self, const char *icon_name); +/* Obtain scaled_icon_buffer from wlr_scene_node */ +struct scaled_icon_buffer *scaled_icon_buffer_from_node(struct wlr_scene_node *node); + #endif /* LABWC_SCALED_ICON_BUFFER_H */ diff --git a/include/scaled-buffer/scaled-img-buffer.h b/include/common/scaled-img-buffer.h similarity index 94% rename from include/scaled-buffer/scaled-img-buffer.h rename to include/common/scaled-img-buffer.h index e8784eb0..938baeb2 100644 --- a/include/scaled-buffer/scaled-img-buffer.h +++ b/include/common/scaled-img-buffer.h @@ -2,13 +2,15 @@ #ifndef LABWC_SCALED_IMG_BUFFER_H #define LABWC_SCALED_IMG_BUFFER_H +#include + struct wlr_scene_tree; struct wlr_scene_node; struct wlr_scene_buffer; struct lab_img; struct scaled_img_buffer { - struct scaled_buffer *scaled_buffer; + struct scaled_scene_buffer *scaled_buffer; struct wlr_scene_buffer *scene_buffer; struct lab_img *img; int width; @@ -54,7 +56,7 @@ struct scaled_img_buffer { /* * Create an auto scaling image buffer, providing a wlr_scene_buffer node for - * display. It gets destroyed automatically when the backing scaled_buffer + * display. It gets destroyed automatically when the backing scaled_scene_buffer * is being destroyed which in turn happens automatically when the backing * wlr_scene_buffer (or one of its parents) is being destroyed. * @@ -64,4 +66,7 @@ struct scaled_img_buffer { struct scaled_img_buffer *scaled_img_buffer_create(struct wlr_scene_tree *parent, struct lab_img *img, int width, int height); +/* Obtain scaled_img_buffer from wlr_scene_node */ +struct scaled_img_buffer *scaled_img_buffer_from_node(struct wlr_scene_node *node); + #endif /* LABWC_SCALED_IMG_BUFFER_H */ diff --git a/include/scaled-buffer/scaled-buffer.h b/include/common/scaled-scene-buffer.h similarity index 73% rename from include/scaled-buffer/scaled-buffer.h rename to include/common/scaled-scene-buffer.h index c2af6054..657b9b57 100644 --- a/include/scaled-buffer/scaled-buffer.h +++ b/include/common/scaled-scene-buffer.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef LABWC_SCALED_BUFFER_H -#define LABWC_SCALED_BUFFER_H +#ifndef LABWC_SCALED_SCENE_BUFFER_H +#define LABWC_SCALED_SCENE_BUFFER_H #include @@ -9,20 +9,20 @@ struct wlr_buffer; struct wlr_scene_tree; struct lab_data_buffer; -struct scaled_buffer; +struct scaled_scene_buffer; -struct scaled_buffer_impl { +struct scaled_scene_buffer_impl { /* Return a new buffer optimized for the new scale */ struct lab_data_buffer *(*create_buffer) - (struct scaled_buffer *scaled_buffer, double scale); + (struct scaled_scene_buffer *scaled_buffer, double scale); /* Might be NULL or used for cleaning up */ - void (*destroy)(struct scaled_buffer *scaled_buffer); + void (*destroy)(struct scaled_scene_buffer *scaled_buffer); /* Returns true if the two buffers are visually the same */ - bool (*equal)(struct scaled_buffer *scaled_buffer_a, - struct scaled_buffer *scaled_buffer_b); + bool (*equal)(struct scaled_scene_buffer *scaled_buffer_a, + struct scaled_scene_buffer *scaled_buffer_b); }; -struct scaled_buffer { +struct scaled_scene_buffer { struct wlr_scene_buffer *scene_buffer; int width; /* unscaled, read only */ int height; /* unscaled, read only */ @@ -35,7 +35,7 @@ struct scaled_buffer { struct wl_list cache; /* struct scaled_buffer_cache_entry.link */ struct wl_listener destroy; struct wl_listener outputs_update; - const struct scaled_buffer_impl *impl; + const struct scaled_scene_buffer_impl *impl; struct wl_list link; /* all_scaled_buffers */ }; @@ -78,22 +78,22 @@ struct scaled_buffer { * to handle the majority of use cases where a view is moved between no more * than two different scales. * - * scaled_buffer will clean up automatically once the internal + * scaled_scene_buffer will clean up automatically once the internal * wlr_scene_buffer is being destroyed. If implementation->destroy is set * it will also get called so a consumer of this API may clean up its own * allocations. * - * Besides caching buffers for each scale per scaled_buffer, we also - * store all the scaled_buffers from all the implementers in a list + * Besides caching buffers for each scale per scaled_scene_buffer, we also + * store all the scaled_scene_buffers from all the implementers in a list * in order to reuse backing buffers for visually duplicated - * scaled_buffers found via impl->equal(). + * scaled_scene_buffers found via impl->equal(). * * All requested lab_data_buffers via impl->create_buffer() will be locked * during the lifetime of the buffer in the internal cache and unlocked * when being evacuated from the cache (due to LAB_SCALED_BUFFER_MAX_CACHE * or the internal wlr_scene_buffer being destroyed). * - * If drop_buffer was set during creation of the scaled_buffer, the + * If drop_buffer was set during creation of the scaled_scene_buffer, the * backing wlr_buffer behind a lab_data_buffer will also get dropped * (via wlr_buffer_drop). If there are no more locks (consumers) of the * respective buffer this will then cause the lab_data_buffer to be free'd. @@ -103,21 +103,21 @@ struct scaled_buffer { * destroyed until the buffer is evacuated from the internal cache and thus * unlocked. * - * This allows using scaled_buffer for an autoscaling font_buffer + * This allows using scaled_scene_buffer for an autoscaling font_buffer * (which gets free'd automatically) and also for theme components like * rounded corner images or button icons whose buffers only exist once but - * are references by multiple windows with their own scaled_buffers. + * are references by multiple windows with their own scaled_scene_buffers. * * The rough idea is: use drop_buffer = true for one-shot buffers and false - * for buffers that should outlive the scaled_buffer instance itself. + * for buffers that should outlive the scaled_scene_buffer instance itself. */ -struct scaled_buffer *scaled_buffer_create( +struct scaled_scene_buffer *scaled_scene_buffer_create( struct wlr_scene_tree *parent, - const struct scaled_buffer_impl *implementation, + const struct scaled_scene_buffer_impl *implementation, bool drop_buffer); /** - * scaled_buffer_request_update - mark the buffer that needs to be + * scaled_scene_buffer_request_update - mark the buffer that needs to be * updated * @width: the width of the buffer to be rendered, in scene coordinates * @height: the height of the buffer to be rendered, in scene coordinates @@ -125,22 +125,22 @@ struct scaled_buffer *scaled_buffer_create( * This function should be called when the states bound to the buffer are * updated and ready for rendering. */ -void scaled_buffer_request_update(struct scaled_buffer *self, +void scaled_scene_buffer_request_update(struct scaled_scene_buffer *self, int width, int height); /** - * scaled_buffer_invalidate_sharing - clear the list of entire cached - * scaled_buffers used to share visually dupliated buffers. This should + * scaled_scene_buffer_invalidate_sharing - clear the list of entire cached + * scaled_scene_buffers used to share visually dupliated buffers. This should * be called on Reconfigure to force updates of newly created - * scaled_buffers rather than reusing ones created before Reconfigure. + * scaled_scene_buffers rather than reusing ones created before Reconfigure. */ -void scaled_buffer_invalidate_sharing(void); +void scaled_scene_buffer_invalidate_sharing(void); /* Private */ -struct scaled_buffer_cache_entry { - struct wl_list link; /* struct scaled_buffer.cache */ +struct scaled_scene_buffer_cache_entry { + struct wl_list link; /* struct scaled_scene_buffer.cache */ struct wlr_buffer *buffer; double scale; }; -#endif /* LABWC_SCALED_BUFFER_H */ +#endif /* LABWC_SCALED_SCENE_BUFFER_H */ diff --git a/include/common/scene-helpers.h b/include/common/scene-helpers.h index 453052fb..021d3b16 100644 --- a/include/common/scene-helpers.h +++ b/include/common/scene-helpers.h @@ -20,6 +20,6 @@ struct wlr_scene_node *lab_wlr_scene_get_prev_node(struct wlr_scene_node *node); /* A variant of wlr_scene_output_commit() that respects wlr_output->pending */ bool lab_wlr_scene_output_commit(struct wlr_scene_output *scene_output, - struct wlr_output_state *state); + struct wlr_output_state *output_state); #endif /* LABWC_SCENE_HELPERS_H */ diff --git a/include/common/string-helpers.h b/include/common/string-helpers.h index 35c994b2..efa5bfd2 100644 --- a/include/common/string-helpers.h +++ b/include/common/string-helpers.h @@ -10,10 +10,13 @@ bool string_null_or_empty(const char *s); /** - * str_space_only - Check if the string only contains white-space characters - * @s: string to check + * trim_last_field() - Trim last field of string splitting on provided delim + * @buf: string to trim + * @delim: delimitator + * + * Example: With delim='_' and buf="foo_bar_baz" the return value is "foo_bar" */ -bool str_space_only(const char *s); +void trim_last_field(char *buf, char delim); /** * string_strip - strip white space left and right @@ -56,7 +59,8 @@ char *strdup_printf(const char *fmt, ...); * The separator is arbitrary. When the separator is NULL, a single space will * be used. */ -char *str_join(const char *const parts[], const char *fmt, const char *sep); +char *str_join(const char *const parts[], + const char *restrict fmt, const char *restrict sep); /** * str_endswith - indicate whether a string ends with a given suffix diff --git a/include/common/surface-helpers.h b/include/common/surface-helpers.h new file mode 100644 index 00000000..1dd7fd74 --- /dev/null +++ b/include/common/surface-helpers.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_SURFACE_HELPERS_H +#define LABWC_SURFACE_HELPERS_H + +struct wlr_surface; +struct wlr_layer_surface_v1; + +/** + * subsurface_parent_layer() - Get wlr_layer_surface from layer-subsurface + * @wlr_surface: The wlr_surface of the wlr_subsurface for which to get the + * layer-surface. + */ +struct wlr_layer_surface_v1 *subsurface_parent_layer( + struct wlr_surface *wlr_surface); + +#endif /* LABWC_SURFACE_HELPERS_H */ diff --git a/include/common/three-state.h b/include/common/three-state.h new file mode 100644 index 00000000..2e3d69d2 --- /dev/null +++ b/include/common/three-state.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_THREE_STATE_H +#define LABWC_THREE_STATE_H + +enum three_state { + LAB_STATE_UNSPECIFIED = 0, + LAB_STATE_ENABLED, + LAB_STATE_DISABLED +}; + +#endif /* LABWC_THREE_STATE_H */ diff --git a/include/common/xml.h b/include/common/xml.h deleted file mode 100644 index e218226f..00000000 --- a/include/common/xml.h +++ /dev/null @@ -1,66 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef LABWC_XML_H -#define LABWC_XML_H - -#include -#include - -/* - * Converts dotted attributes into nested nodes. - * For example, the following node: - * - * - * - * is converted to: - * - * - * - * ShowMenu - * root-menu - * - * 1 - * 2 - * - * - * - */ -void lab_xml_expand_dotted_attributes(xmlNode *parent); - -/* Returns true if the node only contains a string or is empty */ -bool lab_xml_node_is_leaf(xmlNode *node); - -bool lab_xml_get_string(xmlNode *node, const char *key, char *s, size_t len); -bool lab_xml_get_int(xmlNode *node, const char *key, int *i); -bool lab_xml_get_bool(xmlNode *node, const char *key, bool *b); - -/* also skips other unusual nodes like comments */ -static inline xmlNode * -lab_xml_skip_text(xmlNode *child) -{ - while (child && child->type != XML_ELEMENT_NODE) { - child = child->next; - } - return child; -} - -static inline void -lab_xml_get_key_and_content(xmlNode *node, char **name, char **content) -{ - if (node) { - *name = (char *)node->name; - *content = (char *)xmlNodeGetContent(node); - } -} - -#define LAB_XML_FOR_EACH(parent, child, key, content) \ - for ((child) = lab_xml_skip_text((parent)->children), \ - lab_xml_get_key_and_content((child), &(key), &(content)); \ - \ - (child); \ - \ - xmlFree((xmlChar *)(content)), \ - (child) = lab_xml_skip_text((child)->next), \ - lab_xml_get_key_and_content((child), &(key), &(content))) - -#endif /* LABWC_XML_H */ diff --git a/include/config/default-bindings.h b/include/config/default-bindings.h index 9d42d237..98c0b206 100644 --- a/include/config/default-bindings.h +++ b/include/config/default-bindings.h @@ -35,10 +35,6 @@ static struct key_combos { .name = "direction", .value = "left", }, - .attributes[1] = { - .name = "combine", - .value = "yes", - }, }, { .binding = "W-Right", .action = "SnapToEdge", @@ -46,10 +42,6 @@ static struct key_combos { .name = "direction", .value = "right", }, - .attributes[1] = { - .name = "combine", - .value = "yes", - }, }, { .binding = "W-Up", .action = "SnapToEdge", @@ -57,10 +49,6 @@ static struct key_combos { .name = "direction", .value = "up", }, - .attributes[1] = { - .name = "combine", - .value = "yes", - }, }, { .binding = "W-Down", .action = "SnapToEdge", @@ -68,10 +56,6 @@ static struct key_combos { .name = "direction", .value = "down", }, - .attributes[1] = { - .name = "combine", - .value = "yes", - }, }, { .binding = "A-Space", .action = "ShowMenu", @@ -159,17 +143,42 @@ static struct mouse_combos { const char *name, *value; } attributes[2]; } mouse_combos[] = { { - .context = "Border", + .context = "Left", .button = "Left", - .event = "Press", - .action = "Focus", + .event = "Drag", + .action = "Resize", }, { - .context = "Border", + .context = "Top", .button = "Left", - .event = "Press", - .action = "Raise", + .event = "Drag", + .action = "Resize", }, { - .context = "Border", + .context = "Bottom", + .button = "Left", + .event = "Drag", + .action = "Resize", + }, { + .context = "Right", + .button = "Left", + .event = "Drag", + .action = "Resize", + }, { + .context = "TLCorner", + .button = "Left", + .event = "Drag", + .action = "Resize", + }, { + .context = "TRCorner", + .button = "Left", + .event = "Drag", + .action = "Resize", + }, { + .context = "BRCorner", + .button = "Left", + .event = "Drag", + .action = "Resize", + }, { + .context = "BLCorner", .button = "Left", .event = "Drag", .action = "Resize", diff --git a/include/config/keybind.h b/include/config/keybind.h index bef3e189..386839ee 100644 --- a/include/config/keybind.h +++ b/include/config/keybind.h @@ -2,8 +2,7 @@ #ifndef LABWC_KEYBIND_H #define LABWC_KEYBIND_H -#include -#include +#include #include #define MAX_KEYSYMS 32 @@ -42,8 +41,5 @@ uint32_t parse_modifier(const char *symname); 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); #endif /* LABWC_KEYBIND_H */ diff --git a/include/config/libinput.h b/include/config/libinput.h index 80b3fc10..9de8f252 100644 --- a/include/config/libinput.h +++ b/include/config/libinput.h @@ -38,7 +38,6 @@ struct libinput_category { }; enum lab_libinput_device_type get_device_type(const char *s); -const char *libinput_device_type_name(enum lab_libinput_device_type type); struct libinput_category *libinput_category_create(void); struct libinput_category *libinput_category_get_default(void); diff --git a/include/config/mousebind.h b/include/config/mousebind.h index b86ed4e7..0d78973b 100644 --- a/include/config/mousebind.h +++ b/include/config/mousebind.h @@ -2,9 +2,9 @@ #ifndef LABWC_MOUSEBIND_H #define LABWC_MOUSEBIND_H -#include #include -#include "common/node-type.h" +#include "ssd.h" +#include "config/keybind.h" enum mouse_event { MOUSE_ACTION_NONE = 0, @@ -25,7 +25,7 @@ enum direction { }; struct mousebind { - enum lab_node_type context; + enum ssd_part_type context; /* ex: BTN_LEFT, BTN_RIGHT from linux/input_event_codes.h */ uint32_t button; diff --git a/include/config/rcxml.h b/include/config/rcxml.h index 50874571..05f75098 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -3,19 +3,28 @@ #define LABWC_RCXML_H #include +#include #include -#include -#include #include "common/border.h" +#include "common/buf.h" #include "common/font.h" -#include "common/node-type.h" -#include "config/types.h" +#include "common/three-state.h" +#include "config/touch.h" +#include "config/tablet.h" +#include "config/tablet-tool.h" +#include "config/libinput.h" +#include "resize-indicator.h" +#include "ssd.h" +#include "theme.h" -#define BUTTON_MAP_MAX 16 - -/* max of one button of each type (no repeats) */ -#define TITLE_BUTTONS_MAX ((LAB_NODE_BUTTON_LAST + 1) - LAB_NODE_BUTTON_FIRST) +enum view_placement_policy { + LAB_PLACE_INVALID = 0, + LAB_PLACE_CENTER, + LAB_PLACE_CURSOR, + LAB_PLACE_AUTOMATIC, + LAB_PLACE_CASCADE, +}; enum adaptive_sync_mode { LAB_ADAPTIVE_SYNC_DISABLED, @@ -23,12 +32,6 @@ enum adaptive_sync_mode { LAB_ADAPTIVE_SYNC_FULLSCREEN, }; -enum resize_indicator_mode { - LAB_RESIZE_INDICATOR_NEVER = 0, - LAB_RESIZE_INDICATOR_ALWAYS, - LAB_RESIZE_INDICATOR_NON_PIXEL -}; - enum tearing_mode { LAB_TEARING_DISABLED = 0, LAB_TEARING_ENABLED, @@ -44,11 +47,9 @@ enum tiling_events_mode { (LAB_TILING_EVENTS_REGION | LAB_TILING_EVENTS_EDGE), }; -struct buf; - -struct button_map_entry { - uint32_t from; - uint32_t to; +struct title_button { + enum ssd_part_type type; + struct wl_list link; }; struct usable_area_override { @@ -65,18 +66,14 @@ struct rcxml { /* core */ bool xdg_shell_server_side_deco; - bool hide_maximized_window_titlebar; int gap; enum adaptive_sync_mode adaptive_sync; enum tearing_mode allow_tearing; bool auto_enable_outputs; bool reuse_output_mode; + enum view_placement_policy placement_policy; bool xwayland_persistence; bool primary_selection; - char *prompt_command; - - /* placement */ - enum lab_placement_policy placement_policy; int placement_cascade_offset_x; int placement_cascade_offset_y; @@ -89,12 +86,8 @@ struct rcxml { char *theme_name; char *icon_theme_name; char *fallback_app_icon_name; - - enum lab_node_type title_buttons_left[TITLE_BUTTONS_MAX]; - int nr_title_buttons_left; - enum lab_node_type title_buttons_right[TITLE_BUTTONS_MAX]; - int nr_title_buttons_right; - + struct wl_list title_buttons_left; + struct wl_list title_buttons_right; int corner_radius; bool show_title; bool title_layout_loaded; @@ -116,7 +109,7 @@ struct rcxml { /* keyboard */ int repeat_rate; int repeat_delay; - enum lab_tristate kb_numlock_enable; + enum three_state kb_numlock_enable; bool kb_layout_per_window; struct wl_list keybinds; /* struct keybind.link */ @@ -132,12 +125,12 @@ struct rcxml { bool force_mouse_emulation; char *output_name; struct wlr_fbox box; - enum lab_rotation rotation; + enum rotation rotation; uint16_t button_map_count; struct button_map_entry button_map[BUTTON_MAP_MAX]; } tablet; struct tablet_tool_config { - enum lab_motion motion; + enum motion motion; double relative_motion_sensitivity; } tablet_tool; @@ -152,7 +145,6 @@ struct rcxml { /* window snapping */ int snap_edge_range; - int snap_edge_corner_range; bool snap_overlay_enabled; int snap_overlay_delay_inner; int snap_overlay_delay_outer; @@ -179,10 +171,8 @@ struct rcxml { bool show; bool preview; bool outlines; - bool unshade; - enum lab_view_criteria criteria; + uint32_t criteria; struct wl_list fields; /* struct window_switcher_field.link */ - enum window_switcher_style style; } window_switcher; struct wl_list window_rules; /* struct window_rule.link */ @@ -201,13 +191,8 @@ struct rcxml { extern struct rcxml rc; +void rcxml_parse_xml(struct buf *b); void rcxml_read(const char *filename); void rcxml_finish(void); -/* - * Parse the child nodes and append them to the list. - * FIXME: move this function to somewhere else. - */ -void append_parsed_actions(xmlNode *node, struct wl_list *list); - #endif /* LABWC_RCXML_H */ diff --git a/include/config/tablet-tool.h b/include/config/tablet-tool.h index 62aec1c1..bd1943af 100644 --- a/include/config/tablet-tool.h +++ b/include/config/tablet-tool.h @@ -2,8 +2,13 @@ #ifndef LABWC_TABLET_TOOL_CONFIG_H #define LABWC_TABLET_TOOL_CONFIG_H -#include "config/types.h" +#include -enum lab_motion tablet_parse_motion(const char *name); +enum motion { + LAB_TABLET_MOTION_ABSOLUTE = 0, + LAB_TABLET_MOTION_RELATIVE, +}; + +enum motion tablet_parse_motion(const char *name); #endif /* LABWC_TABLET_TOOL_CONFIG_H */ diff --git a/include/config/tablet.h b/include/config/tablet.h index ae3772b5..8e0c7c67 100644 --- a/include/config/tablet.h +++ b/include/config/tablet.h @@ -3,10 +3,22 @@ #define LABWC_TABLET_CONFIG_H #include -#include "config/types.h" + +enum rotation { + LAB_ROTATE_NONE = 0, + LAB_ROTATE_90, + LAB_ROTATE_180, + LAB_ROTATE_270, +}; + +#define BUTTON_MAP_MAX 16 +struct button_map_entry { + uint32_t from; + uint32_t to; +}; double tablet_get_dbl_if_positive(const char *content, const char *name); -enum lab_rotation tablet_parse_rotation(int value); +enum rotation tablet_parse_rotation(int value); uint32_t tablet_button_from_str(const char *button); void tablet_button_mapping_add(uint32_t from, uint32_t to); void tablet_load_default_button_mappings(void); diff --git a/include/config/touch.h b/include/config/touch.h index 1a05670e..12f7b032 100644 --- a/include/config/touch.h +++ b/include/config/touch.h @@ -2,7 +2,7 @@ #ifndef LABWC_TOUCH_CONFIG_H #define LABWC_TOUCH_CONFIG_H -#include +#include #include struct touch_config_entry { diff --git a/include/config/types.h b/include/config/types.h deleted file mode 100644 index e832a658..00000000 --- a/include/config/types.h +++ /dev/null @@ -1,115 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef LABWC_CONFIG_TYPES_H -#define LABWC_CONFIG_TYPES_H - -/* - * Shared (basic) types related to user configuration. - * - * Please try to keep dependencies on other headers minimal, - * since config/types.h gets included in many source files. - * - * For the full config struct, see config/rcxml.h. - */ - -/** - * Indicates whether tablet tool motion events should be reported using - * absolute or relative coordinates - */ -enum lab_motion { - LAB_MOTION_ABSOLUTE = 0, - LAB_MOTION_RELATIVE, -}; - -enum lab_placement_policy { - LAB_PLACE_INVALID = 0, - LAB_PLACE_CENTER, - LAB_PLACE_CURSOR, - LAB_PLACE_AUTOMATIC, - LAB_PLACE_CASCADE, -}; - -enum lab_rotation { - LAB_ROTATE_NONE = 0, - LAB_ROTATE_90, - LAB_ROTATE_180, - LAB_ROTATE_270, -}; - -enum lab_ssd_mode { - LAB_SSD_MODE_NONE = 0, - LAB_SSD_MODE_BORDER, - LAB_SSD_MODE_FULL, - LAB_SSD_MODE_INVALID, -}; - -enum lab_tristate { - LAB_STATE_UNSPECIFIED = 0, - LAB_STATE_ENABLED, - LAB_STATE_DISABLED -}; - -/* - * This enum type is a set of bit flags where each set bit makes the - * criteria more restrictive. For example: - * - * (LAB_VIEW_CRITERIA_FULLSCREEN | LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) - * matches only fullscreen views on the current workspace, while - * - * (LAB_VIEW_CRITERIA_ALWAYS_ON_TOP | LAB_VIEW_CRITERIA_NO_ALWAYS_ON_TOP) - * would be contradictory and match nothing at all. - */ -enum lab_view_criteria { - /* No filter -> all focusable views */ - LAB_VIEW_CRITERIA_NONE = 0, - - /* - * Includes always-on-top views, e.g. - * what is visible on the current workspace - */ - LAB_VIEW_CRITERIA_CURRENT_WORKSPACE = 1 << 0, - - /* Positive criteria */ - LAB_VIEW_CRITERIA_FULLSCREEN = 1 << 1, - LAB_VIEW_CRITERIA_ALWAYS_ON_TOP = 1 << 2, - LAB_VIEW_CRITERIA_ROOT_TOPLEVEL = 1 << 3, - - /* Negative criteria */ - LAB_VIEW_CRITERIA_NO_ALWAYS_ON_TOP = 1 << 6, - LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER = 1 << 7, - LAB_VIEW_CRITERIA_NO_OMNIPRESENT = 1 << 8, -}; - -/* - * Window types are based on the NET_WM constants from X11. See: - * https://specifications.freedesktop.org/wm-spec/1.4/ar01s05.html#id-1.6.7 - * - * The enum constants are intended to match wlr_xwayland_net_wm_window_type. - * Redefining the same constants here may seem redundant, but is necessary - * to make them available even in builds with xwayland support disabled. - */ -enum lab_window_type { - LAB_WINDOW_TYPE_INVALID = -1, - LAB_WINDOW_TYPE_DESKTOP = 0, - LAB_WINDOW_TYPE_DOCK, - LAB_WINDOW_TYPE_TOOLBAR, - LAB_WINDOW_TYPE_MENU, - LAB_WINDOW_TYPE_UTILITY, - LAB_WINDOW_TYPE_SPLASH, - LAB_WINDOW_TYPE_DIALOG, - LAB_WINDOW_TYPE_DROPDOWN_MENU, - LAB_WINDOW_TYPE_POPUP_MENU, - LAB_WINDOW_TYPE_TOOLTIP, - LAB_WINDOW_TYPE_NOTIFICATION, - LAB_WINDOW_TYPE_COMBO, - LAB_WINDOW_TYPE_DND, - LAB_WINDOW_TYPE_NORMAL, - - LAB_WINDOW_TYPE_LEN -}; - -enum window_switcher_style { - WINDOW_SWITCHER_CLASSIC, - WINDOW_SWITCHER_THUMBNAIL, -}; - -#endif /* LABWC_CONFIG_TYPES_H */ diff --git a/include/edges.h b/include/edges.h index 439b5a34..47dd6f1b 100644 --- a/include/edges.h +++ b/include/edges.h @@ -4,7 +4,7 @@ #include #include -#include "common/edge.h" +#include #include "common/macros.h" struct border; @@ -101,6 +101,9 @@ typedef void (*edge_validator_t)(int *best, struct edge current, void edges_initialize(struct border *edges); +void edges_adjust_geom(struct view *view, struct border edges, + uint32_t resize_edges, struct wlr_box *geom); + void edges_find_neighbors(struct border *nearest_edges, struct view *view, struct wlr_box origin, struct wlr_box target, struct output *output, edge_validator_t validator, bool ignore_hidden); @@ -113,10 +116,9 @@ void edges_adjust_move_coords(struct view *view, struct border edges, int *x, int *y, bool use_pending); void edges_adjust_resize_geom(struct view *view, struct border edges, - enum lab_edge resize_edges, struct wlr_box *geom, bool use_pending); + uint32_t resize_edges, struct wlr_box *geom, bool use_pending); bool edges_traverse_edge(struct edge current, struct edge target, struct edge edge); void edges_calculate_visibility(struct server *server, struct view *ignored_view); - #endif /* LABWC_EDGES_H */ diff --git a/include/foreign-toplevel-internal.h b/include/foreign-toplevel-internal.h new file mode 100644 index 00000000..7be87b3d --- /dev/null +++ b/include/foreign-toplevel-internal.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_FOREIGN_TOPLEVEL_INTERNAL_H +#define LABWC_FOREIGN_TOPLEVEL_INTERNAL_H + +#include +#include +#include "foreign-toplevel.h" + +struct foreign_toplevel { + struct view *view; + + /* *-toplevel implementations */ + struct wlr_foreign_toplevel { + struct wlr_foreign_toplevel_handle_v1 *handle; + + /* Client side events */ + struct { + struct wl_listener request_maximize; + struct wl_listener request_minimize; + struct wl_listener request_fullscreen; + struct wl_listener request_activate; + struct wl_listener request_close; + struct wl_listener handle_destroy; + } on; + + /* Compositor side state updates */ + struct { + struct wl_listener new_app_id; + struct wl_listener new_title; + struct wl_listener new_outputs; + struct wl_listener maximized; + struct wl_listener minimized; + struct wl_listener fullscreened; + struct wl_listener activated; + } on_view; + + /* Internal signals */ + struct { + struct wl_listener toplevel_parent; + struct wl_listener toplevel_destroy; + } on_foreign_toplevel; + + } wlr_toplevel; + + struct ext_foreign_toplevel { + struct wlr_ext_foreign_toplevel_handle_v1 *handle; + + /* Client side events */ + struct { + struct wl_listener handle_destroy; + } on; + + /* Compositor side state updates */ + struct { + struct wl_listener new_app_id; + struct wl_listener new_title; + } on_view; + + /* Internal signals */ + struct { + struct wl_listener toplevel_destroy; + } on_foreign_toplevel; + + } ext_toplevel; + + /* TODO: add struct xdg_x11_mapped_toplevel at some point */ + + struct { + struct wl_signal toplevel_parent; /* struct view *parent */ + struct wl_signal toplevel_destroy; + } events; +}; + +void ext_foreign_toplevel_init(struct foreign_toplevel *toplevel); +void wlr_foreign_toplevel_init(struct foreign_toplevel *toplevel); + +void foreign_request_minimize(struct foreign_toplevel *toplevel, bool minimized); +void foreign_request_maximize(struct foreign_toplevel *toplevel, enum view_axis axis); +void foreign_request_fullscreen(struct foreign_toplevel *toplevel, bool fullscreen); +void foreign_request_activate(struct foreign_toplevel *toplevel); +void foreign_request_close(struct foreign_toplevel *toplevel); + +#endif /* LABWC_FOREIGN_TOPLEVEL_INTERNAL_H */ diff --git a/include/foreign-toplevel/foreign.h b/include/foreign-toplevel.h similarity index 100% rename from include/foreign-toplevel/foreign.h rename to include/foreign-toplevel.h diff --git a/include/foreign-toplevel/ext-foreign.h b/include/foreign-toplevel/ext-foreign.h deleted file mode 100644 index d9442c02..00000000 --- a/include/foreign-toplevel/ext-foreign.h +++ /dev/null @@ -1,27 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef LABWC_EXT_FOREIGN_TOPLEVEL_H -#define LABWC_EXT_FOREIGN_TOPLEVEL_H - -#include - -struct ext_foreign_toplevel { - struct view *view; - struct wlr_ext_foreign_toplevel_handle_v1 *handle; - - /* Client side events */ - struct { - struct wl_listener handle_destroy; - } on; - - /* Compositor side state updates */ - struct { - struct wl_listener new_app_id; - struct wl_listener new_title; - } on_view; -}; - -void ext_foreign_toplevel_init(struct ext_foreign_toplevel *ext_toplevel, - struct view *view); -void ext_foreign_toplevel_finish(struct ext_foreign_toplevel *ext_toplevel); - -#endif /* LABWC_EXT_FOREIGN_TOPLEVEL_H */ diff --git a/include/foreign-toplevel/wlr-foreign.h b/include/foreign-toplevel/wlr-foreign.h deleted file mode 100644 index 2da44752..00000000 --- a/include/foreign-toplevel/wlr-foreign.h +++ /dev/null @@ -1,39 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef LABWC_WLR_FOREIGN_TOPLEVEL_H -#define LABWC_WLR_FOREIGN_TOPLEVEL_H - -#include - -struct wlr_foreign_toplevel { - struct view *view; - struct wlr_foreign_toplevel_handle_v1 *handle; - - /* Client side events */ - struct { - struct wl_listener request_maximize; - struct wl_listener request_minimize; - struct wl_listener request_fullscreen; - struct wl_listener request_activate; - struct wl_listener request_close; - struct wl_listener handle_destroy; - } on; - - /* Compositor side state updates */ - struct { - struct wl_listener new_app_id; - struct wl_listener new_title; - struct wl_listener new_outputs; - struct wl_listener maximized; - struct wl_listener minimized; - struct wl_listener fullscreened; - struct wl_listener activated; - } on_view; -}; - -void wlr_foreign_toplevel_init(struct wlr_foreign_toplevel *wlr_toplevel, - struct view *view); -void wlr_foreign_toplevel_set_parent(struct wlr_foreign_toplevel *wlr_toplevel, - struct wlr_foreign_toplevel *parent); -void wlr_foreign_toplevel_finish(struct wlr_foreign_toplevel *wlr_toplevel); - -#endif /* LABWC_WLR_FOREIGN_TOPLEVEL_H */ diff --git a/include/idle.h b/include/idle.h index 845fdf41..50a660c2 100644 --- a/include/idle.h +++ b/include/idle.h @@ -5,7 +5,7 @@ struct wl_display; struct wlr_seat; -void idle_manager_create(struct wl_display *display); +void idle_manager_create(struct wl_display *display, struct wlr_seat *wlr_seat); void idle_manager_notify_activity(struct wlr_seat *seat); #endif /* LABWC_IDLE_H */ diff --git a/include/img/img.h b/include/img/img.h index 82c695b6..f299335e 100644 --- a/include/img/img.h +++ b/include/img/img.h @@ -4,6 +4,7 @@ #include #include +#include #include enum lab_img_type { diff --git a/include/input/cursor.h b/include/input/cursor.h index 13ab72d1..c49606e2 100644 --- a/include/input/cursor.h +++ b/include/input/cursor.h @@ -2,17 +2,16 @@ #ifndef LABWC_CURSOR_H #define LABWC_CURSOR_H -#include -#include "common/edge.h" -#include "common/node-type.h" +#include +#include +#include "ssd.h" struct view; struct seat; struct server; -struct wlr_input_device; -struct wlr_cursor; struct wlr_surface; struct wlr_scene_node; +enum wl_pointer_button_state; /* Cursors used internally by labwc */ enum lab_cursors { @@ -34,25 +33,35 @@ struct cursor_context { struct view *view; struct wlr_scene_node *node; struct wlr_surface *surface; - enum lab_node_type type; + enum ssd_part_type type; double sx, sy; }; /** - * get_cursor_context - find view, surface and scene_node at cursor + * get_cursor_context - find view and scene_node at cursor * - * If the cursor is on a client-drawn surface: - * - ctx.{surface,node} points to the surface, which may be a subsurface. - * - ctx.view is set if the node is associated to a xdg/x11 window. - * - ctx.type is LAYER_SURFACE or UNMANAGED if the node is a layer-shell - * surface or an X11 unmanaged surface. Otherwise, CLIENT is set. + * Behavior if node points to a surface: + * - If surface is a layer-surface, type will be + * set to LAB_SSD_LAYER_SURFACE and view will be NULL. * - * If the cursor is on a server-side component (SSD part and menu item): - * - ctx.node points to the root node of that component - * - ctx.view is set if the component is a SSD part - * - ctx.type specifies the component (e.g. MENU_ITEM, BORDER_TOP, BUTTON_ICONIFY) + * - If surface is a 'lost' unmanaged xsurface (one + * with a never-mapped parent view), type will + * be set to LAB_SSD_UNMANAGED and view will be NULL. + * + * 'Lost' unmanaged xsurfaces are usually caused by + * X11 applications opening popups without setting + * the main window as parent. Example: VLC submenus. + * + * - Any other surface will cause type to be set to + * LAB_SSD_CLIENT and return the attached view. + * + * Behavior if node points to internal elements: + * - type will be set to the appropriate enum value + * and view will be NULL if the node is not part of the SSD. + * + * If no node is found for the given layout coordinates, + * type will be set to LAB_SSD_ROOT and view will be NULL. * - * If no node is found at cursor, ctx.type is set to ROOT. */ struct cursor_context get_cursor_context(struct server *server); @@ -76,19 +85,20 @@ void cursor_set_visible(struct seat *seat, bool visible); * This is mostly important when either resizing a window using a * keyboard modifier or when using the Resize action from a keybind. */ -enum lab_edge cursor_get_resize_edges(struct wlr_cursor *cursor, +uint32_t cursor_get_resize_edges(struct wlr_cursor *cursor, struct cursor_context *ctx); /** - * cursor_get_from_edge - translate lab_edge enum to lab_cursor enum - * @resize_edges - edge(s) being resized + * cursor_get_from_edge - translate wlroots edge enum to lab_cursor enum + * @resize_edges - WLR_EDGE_ combination like WLR_EDGE_TOP | WLR_EDGE_RIGHT * + * Returns LAB_CURSOR_DEFAULT on WLR_EDGE_NONE * Returns the appropriate lab_cursors enum if @resize_edges * is one of the 4 corners or one of the 4 edges. * - * Returns LAB_CURSOR_DEFAULT on any other value. + * Asserts on invalid edge combinations like WLR_EDGE_LEFT | WLR_EDGE_RIGHT */ -enum lab_cursors cursor_get_from_edge(enum lab_edge resize_edges); +enum lab_cursors cursor_get_from_edge(uint32_t resize_edges); /** * cursor_update_focus - update cursor focus, may update the cursor icon diff --git a/include/input/ime.h b/include/input/ime.h index 046e0926..fcd292ce 100644 --- a/include/input/ime.h +++ b/include/input/ime.h @@ -3,10 +3,11 @@ #ifndef LABWC_IME_H #define LABWC_IME_H -#include +#include +#include +#include "labwc.h" struct keyboard; -struct wlr_keyboard_key_event; /* * The relay structure manages the relationship between text-inputs and diff --git a/include/input/input.h b/include/input/input.h index 520216b6..37758e0e 100644 --- a/include/input/input.h +++ b/include/input/input.h @@ -2,16 +2,7 @@ #ifndef LABWC_INPUT_H #define LABWC_INPUT_H -#include - -struct input { - struct wlr_input_device *wlr_input_device; - struct seat *seat; - /* Set for pointer/touch devices */ - double scroll_factor; - struct wl_listener destroy; - struct wl_list link; /* seat.inputs */ -}; +struct seat; void input_handlers_init(struct seat *seat); void input_handlers_finish(struct seat *seat); diff --git a/include/input/key-state.h b/include/input/key-state.h index d9971b74..c53a4b10 100644 --- a/include/input/key-state.h +++ b/include/input/key-state.h @@ -24,5 +24,6 @@ void key_state_store_pressed_key_as_bound(uint32_t keycode); bool key_state_corresponding_press_event_was_bound(uint32_t keycode); void key_state_bound_key_remove(uint32_t keycode); int key_state_nr_bound_keys(void); +int key_state_nr_pressed_keys(void); #endif /* LABWC_KEY_STATE_H */ diff --git a/include/input/keyboard.h b/include/input/keyboard.h index 6836d9bc..d080d30a 100644 --- a/include/input/keyboard.h +++ b/include/input/keyboard.h @@ -4,25 +4,10 @@ #include #include -#include "input/input.h" -/* - * Virtual keyboards should not belong to seat->keyboard_group. As a result we - * need to be able to ascertain which wlr_keyboard key/modifier events come from - * and we achieve that by using `struct keyboard` which inherits `struct input` - * and adds keyboard specific listeners and a wlr_keyboard pointer. - */ -struct keyboard { - struct input base; - struct wlr_keyboard *wlr_keyboard; - bool is_virtual; - struct wl_listener modifiers; - struct wl_listener key; - /* key repeat for compositor keybinds */ - uint32_t keybind_repeat_keycode; - int32_t keybind_repeat_rate; - struct wl_event_source *keybind_repeat; -}; +struct seat; +struct keyboard; +struct wlr_keyboard; void keyboard_reset_current_keybind(void); void keyboard_configure(struct seat *seat, struct wlr_keyboard *kb, diff --git a/include/input/tablet-pad.h b/include/input/tablet-pad.h index e8be74f5..3ada7634 100644 --- a/include/input/tablet-pad.h +++ b/include/input/tablet-pad.h @@ -3,6 +3,7 @@ #define LABWC_TABLET_PAD_H #include +#include struct seat; struct wlr_device; diff --git a/include/input/tablet.h b/include/input/tablet.h index eb3234f4..29aeb72b 100644 --- a/include/input/tablet.h +++ b/include/input/tablet.h @@ -3,7 +3,7 @@ #define LABWC_TABLET_H #include -#include "config/types.h" +#include struct seat; struct wlr_device; @@ -20,7 +20,7 @@ struct drawing_tablet_tool { */ bool force_mouse_emulation; - enum lab_motion motion_mode; + enum motion motion_mode; double x, y, dx, dy; double distance; double pressure; diff --git a/include/labwc.h b/include/labwc.h index d853c01d..cff78588 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -2,17 +2,64 @@ #ifndef LABWC_H #define LABWC_H #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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include "common/set.h" +#include "config/keybind.h" +#include "config/rcxml.h" #include "input/cursor.h" #include "overlay.h" +#include "regions.h" +#include "session-lock.h" +#if HAVE_NLS +#include +#include +#define _ gettext +#else +#define _(s) (s) +#endif #define XCURSOR_DEFAULT "left_ptr" #define XCURSOR_SIZE 24 -struct wlr_xdg_popup; - enum input_mode { LAB_INPUT_STATE_PASSTHROUGH = 0, LAB_INPUT_STATE_MOVE, @@ -21,6 +68,33 @@ enum input_mode { LAB_INPUT_STATE_WINDOW_SWITCHER, }; +struct input { + struct wlr_input_device *wlr_input_device; + struct seat *seat; + /* Set for pointer/touch devices */ + double scroll_factor; + struct wl_listener destroy; + struct wl_list link; /* seat.inputs */ +}; + +/* + * Virtual keyboards should not belong to seat->keyboard_group. As a result we + * need to be able to ascertain which wlr_keyboard key/modifier events come from + * and we achieve that by using `struct keyboard` which inherits `struct input` + * and adds keyboard specific listeners and a wlr_keyboard pointer. + */ +struct keyboard { + struct input base; + struct wlr_keyboard *wlr_keyboard; + bool is_virtual; + struct wl_listener modifiers; + struct wl_listener key; + /* key repeat for compositor keybinds */ + uint32_t keybind_repeat_keycode; + int32_t keybind_repeat_rate; + struct wl_event_source *keybind_repeat; +}; + struct seat { struct wlr_seat *seat; struct server *server; @@ -37,10 +111,9 @@ struct seat { bool cursor_visible; struct wlr_cursor *cursor; struct wlr_xcursor_manager *xcursor_manager; - struct accumulated_scroll { - double delta; - double delta_discrete; - } accumulated_scrolls[2]; /* indexed by wl_pointer_axis */ + struct { + double x, y; + } smooth_scroll_offset; bool cursor_scroll_wheel_emulation; /* @@ -55,6 +128,9 @@ struct seat { struct wlr_pointer_constraint_v1 *current_constraint; + /* In support for ToggleKeybinds */ + uint32_t nr_inhibited_keybind_views; + /* Used to hide the workspace OSD after switching workspaces */ struct wl_event_source *workspace_osd_timer; bool workspace_osd_shown_by_modifier; @@ -142,12 +218,21 @@ struct seat { struct wl_listener pressed_surface_destroy; struct wlr_virtual_pointer_manager_v1 *virtual_pointer; - struct wl_listener new_virtual_pointer; + struct wl_listener virtual_pointer_new; struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard; struct wl_listener new_virtual_keyboard; }; +struct lab_data_buffer; +struct workspace; + +enum lab_cycle_dir { + LAB_CYCLE_DIR_NONE, + LAB_CYCLE_DIR_FORWARD, + LAB_CYCLE_DIR_BACKWARD, +}; + struct server { struct wl_display *wl_display; struct wl_event_loop *wl_event_loop; /* Can be used for timer events */ @@ -203,7 +288,7 @@ struct server { double grab_x, grab_y; /* View geometry when interactive move/resize is requested */ struct wlr_box grab_box; - enum lab_edge resize_edges; + uint32_t resize_edges; /* * 'active_view' is generally the view with keyboard-focus, updated with @@ -219,7 +304,7 @@ struct server { */ struct view *active_view; - struct ssd_button *hovered_button; + struct ssd_hover_state *ssd_hover_state; /* Tree for all non-layer xdg/xwayland-shell surfaces */ struct wlr_scene_tree *view_tree; @@ -303,7 +388,6 @@ struct server { /* Set when in cycle (alt-tab) mode */ struct osd_state { struct view *cycle_view; - bool preview_was_shaded; bool preview_was_enabled; struct wlr_scene_node *preview_node; struct wlr_scene_tree *preview_parent; @@ -321,6 +405,45 @@ struct server { pid_t primary_client_pid; }; +#define LAB_NR_LAYERS (4) + +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; + struct wlr_scene_tree *layer_tree[LAB_NR_LAYERS]; + struct wlr_scene_tree *layer_popup_tree; + struct wlr_scene_tree *osd_tree; + struct wlr_scene_tree *session_lock_tree; + struct wlr_scene_buffer *workspace_osd; + + struct osd_scene { + struct wl_array items; /* struct osd_scene_item */ + struct wlr_scene_tree *tree; + } osd_scene; + + /* In output-relative scene coordinates */ + struct wlr_box usable_area; + + struct wl_list regions; /* struct region.link */ + + struct wl_listener destroy; + struct wl_listener frame; + struct wl_listener request_state; + + bool gamma_lut_changed; +}; + +#undef LAB_NR_LAYERS + +struct constraint { + struct seat *seat; + struct wlr_pointer_constraint_v1 *constraint; + struct wl_listener destroy; +}; + 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); @@ -360,6 +483,7 @@ void desktop_focus_view_or_surface(struct seat *seat, struct view *view, void desktop_arrange_all_views(struct server *server); void desktop_focus_output(struct output *output); +struct view *desktop_topmost_focusable_view(struct server *server); /** * Toggles the (output local) visibility of the layershell top layer @@ -420,20 +544,50 @@ void seat_focus_override_end(struct seat *seat); */ void interactive_anchor_to_cursor(struct server *server, struct wlr_box *geo); -void interactive_begin(struct view *view, enum input_mode mode, - enum lab_edge edges); +void interactive_begin(struct view *view, enum input_mode mode, uint32_t edges); void interactive_finish(struct view *view); void interactive_cancel(struct view *view); +/* Possibly returns VIEW_EDGE_CENTER if is yes */ +enum view_edge edge_from_cursor(struct seat *seat, struct output **dest_output); + +void output_init(struct server *server); +void output_finish(struct server *server); +void output_manager_init(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); /** - * Returns the edge to snap a window to. - * For example, if the output-relative cursor position (x,y) fulfills - * x <= () and y <= (), - * then edge1=LAB_EDGE_TOP and edge2=LAB_EDGE_LEFT. - * The value of (edge1|edge2) can be passed to view_snap_to_edge(). + * output_get_adjacent() - get next output, in a given direction, + * from a given output + * + * @output: reference output + * @edge: direction in which to look for the nearest output + * @wrap: if true, wrap around at layout edge + * + * Note: if output is NULL, the output nearest the cursor will be used as the + * reference instead. */ -bool edge_from_cursor(struct seat *seat, struct output **dest_output, - enum lab_edge *edge1, enum lab_edge *edge2); +struct output *output_get_adjacent(struct output *output, + enum view_edge edge, bool wrap); + +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); +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); + +/** + * output_max_scale() - get maximum scale factor of all usable outputs. + * Used when loading/rendering resources (e.g. icons) that may be + * displayed on any output. + */ +float output_max_scale(struct server *server); void handle_tearing_new_object(struct wl_listener *listener, void *data); diff --git a/include/layers.h b/include/layers.h index da1bdb86..b995fc8b 100644 --- a/include/layers.h +++ b/include/layers.h @@ -1,9 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0-only */ #ifndef LABWC_LAYERS_H #define LABWC_LAYERS_H - -#include -#include +#include +#include struct server; struct output; diff --git a/include/menu/menu.h b/include/menu/menu.h index f65ff9a3..a4d236d6 100644 --- a/include/menu/menu.h +++ b/include/menu/menu.h @@ -100,6 +100,18 @@ void menu_open_root(struct menu *menu, int x, int y); */ void menu_process_cursor_motion(struct wlr_scene_node *node); +/** + * menu_call_actions - call actions associated with a menu node + * + * If menuitem connected to @node does not just open a submenu: + * - associated actions will be called + * - server->menu_current will be closed + * - server->menu_current will be set to NULL + * + * Returns true if actions have actually been executed + */ +bool menu_call_actions(struct wlr_scene_node *node); + /** * menu_close_root- close root menu * diff --git a/include/node.h b/include/node.h index bfad1373..80d03e9f 100644 --- a/include/node.h +++ b/include/node.h @@ -1,15 +1,31 @@ /* SPDX-License-Identifier: GPL-2.0-only */ #ifndef LABWC_NODE_DESCRIPTOR_H #define LABWC_NODE_DESCRIPTOR_H +#include -#include -#include "common/node-type.h" +struct view; +struct lab_layer_surface; +struct lab_layer_popup; +struct menuitem; +struct ssd_button; +struct scaled_scene_buffer; -struct wlr_scene_node; +enum node_descriptor_type { + LAB_NODE_DESC_NODE = 0, + LAB_NODE_DESC_VIEW, + LAB_NODE_DESC_XDG_POPUP, + LAB_NODE_DESC_LAYER_SURFACE, + LAB_NODE_DESC_LAYER_POPUP, + LAB_NODE_DESC_SESSION_LOCK_SURFACE, + LAB_NODE_DESC_IME_POPUP, + LAB_NODE_DESC_MENUITEM, + LAB_NODE_DESC_TREE, + LAB_NODE_DESC_SCALED_SCENE_BUFFER, + LAB_NODE_DESC_SSD_BUTTON, +}; struct node_descriptor { - enum lab_node_type type; - struct view *view; + enum node_descriptor_type type; void *data; struct wl_listener destroy; }; @@ -22,15 +38,16 @@ struct node_descriptor { * * @scene_node: wlr_scene_node to attached node_descriptor to * @type: node descriptor type - * @view: associated view * @data: struct to point to as follows: - * - LAB_NODE_LAYER_SURFACE struct lab_layer_surface - * - LAB_NODE_LAYER_POPUP struct lab_layer_popup - * - LAB_NODE_MENUITEM struct menuitem - * - LAB_NODE_BUTTON_* struct ssd_button + * - LAB_NODE_DESC_VIEW struct view + * - LAB_NODE_DESC_XDG_POPUP struct view + * - LAB_NODE_DESC_LAYER_SURFACE struct lab_layer_surface + * - LAB_NODE_DESC_LAYER_POPUP struct lab_layer_popup + * - LAB_NODE_DESC_MENUITEM struct menuitem + * - LAB_NODE_DESC_SSD_BUTTON struct ssd_button */ void node_descriptor_create(struct wlr_scene_node *scene_node, - enum lab_node_type type, struct view *view, void *data); + enum node_descriptor_type type, void *data); /** * node_view_from_node - return view struct from node @@ -45,6 +62,13 @@ struct view *node_view_from_node(struct wlr_scene_node *wlr_scene_node); struct lab_layer_surface *node_layer_surface_from_node( struct wlr_scene_node *wlr_scene_node); +/** + * node_layer_popup_from_node - return lab_layer_popup struct from node + * @wlr_scene_node: wlr_scene_node from which to return data + */ +struct lab_layer_popup *node_layer_popup_from_node( + struct wlr_scene_node *wlr_scene_node); + /** * node_menuitem_from_node - return menuitem struct from node * @wlr_scene_node: wlr_scene_node from which to return data @@ -53,10 +77,17 @@ struct menuitem *node_menuitem_from_node( struct wlr_scene_node *wlr_scene_node); /** - * node_try_ssd_button_from_node - return ssd_button or NULL from node + * node_ssd_button_from_node - return ssd_button struct from node * @wlr_scene_node: wlr_scene_node from which to return data */ -struct ssd_button *node_try_ssd_button_from_node( +struct ssd_button *node_ssd_button_from_node( + struct wlr_scene_node *wlr_scene_node); + +/** + * node_scaled_scene_buffer_from_node - return scaled_scene_buffer from node + * @wlr_scene_node: wlr_scene_node from which to return data + */ +struct scaled_scene_buffer *node_scaled_scene_buffer_from_node( struct wlr_scene_node *wlr_scene_node); #endif /* LABWC_NODE_DESCRIPTOR_H */ diff --git a/include/osd.h b/include/osd.h index a5c2a4cc..806b88d4 100644 --- a/include/osd.h +++ b/include/osd.h @@ -5,14 +5,6 @@ #include #include -struct output; - -enum lab_cycle_dir { - LAB_CYCLE_DIR_NONE, - LAB_CYCLE_DIR_FORWARD, - LAB_CYCLE_DIR_BACKWARD, -}; - /* TODO: add field with keyboard layout? */ enum window_switcher_field_content { LAB_FIELD_NONE = 0, @@ -45,6 +37,7 @@ struct window_switcher_field { struct buf; struct view; struct server; +enum lab_cycle_dir; /* Begin window switcher */ void osd_begin(struct server *server, enum lab_cycle_dir direction); @@ -63,26 +56,10 @@ void osd_field_get_content(struct window_switcher_field *field, struct buf *buf, struct view *view); /* Used by rcxml.c when parsing the config */ +struct window_switcher_field *osd_field_create(void); void osd_field_arg_from_xml_node(struct window_switcher_field *field, const char *nodename, const char *content); bool osd_field_is_valid(struct window_switcher_field *field); void osd_field_free(struct window_switcher_field *field); -/* Internal API */ -struct osd_impl { - /* - * Create a scene-tree of OSD for an output. - * This sets output->osd_scene.{items,tree}. - */ - void (*create)(struct output *output, struct wl_array *views); - /* - * Update output->osd_scene.tree to highlight - * server->osd_state.cycle_view. - */ - void (*update)(struct output *output); -}; - -extern struct osd_impl osd_classic_impl; -extern struct osd_impl osd_thumbnail_impl; - #endif // LABWC_OSD_H diff --git a/include/output.h b/include/output.h deleted file mode 100644 index 888f62c7..00000000 --- a/include/output.h +++ /dev/null @@ -1,72 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef LABWC_OUTPUT_H -#define LABWC_OUTPUT_H - -#include -#include "common/edge.h" - -#define LAB_NR_LAYERS (4) - -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; - struct wlr_scene_tree *layer_tree[LAB_NR_LAYERS]; - struct wlr_scene_tree *layer_popup_tree; - struct wlr_scene_tree *osd_tree; - struct wlr_scene_tree *session_lock_tree; - struct wlr_scene_buffer *workspace_osd; - - struct osd_scene { - struct wl_array items; /* struct osd_scene_item */ - struct wlr_scene_tree *tree; - } osd_scene; - - /* In output-relative scene coordinates */ - struct wlr_box usable_area; - - struct wl_list regions; /* struct region.link */ - - struct wl_listener destroy; - struct wl_listener frame; - struct wl_listener request_state; - - 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); - -/** - * output_get_adjacent() - get next output, in a given direction, - * from a given output - * - * @output: reference output - * @edge: direction in which to look for the nearest output - * @wrap: if true, wrap around at layout edge - * - * Note: if output is NULL, the output nearest the cursor will be used as the - * reference instead. - */ -struct output *output_get_adjacent(struct output *output, - enum lab_edge edge, bool wrap); - -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); -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); - -#endif // LABWC_OUTPUT_H diff --git a/include/overlay.h b/include/overlay.h index a7911f3a..6a0ee020 100644 --- a/include/overlay.h +++ b/include/overlay.h @@ -2,12 +2,24 @@ #ifndef LABWC_OVERLAY_H #define LABWC_OVERLAY_H -#include "common/edge.h" +#include +#include "common/graphic-helpers.h" +#include "regions.h" +#include "view.h" -struct seat; +/* TODO: replace this with single lab_scene_rect */ +struct overlay_rect { + struct wlr_scene_tree *tree; + + bool bg_enabled; + struct wlr_scene_rect *bg_rect; + + bool border_enabled; + struct lab_scene_rect *border_rect; +}; struct overlay { - struct lab_scene_rect *rect; + struct overlay_rect region_rect, edge_rect; /* Represents currently shown or delayed overlay */ struct { @@ -15,7 +27,7 @@ struct overlay { struct region *region; /* Snap-to-edge overlay */ - enum lab_edge edge; + enum view_edge edge; struct output *output; } active; @@ -23,13 +35,15 @@ struct overlay { struct wl_event_source *timer; }; -/* - * Shows or updates an overlay when the grabbed window can be snapped to - * a region or an output edge. Calls overlay_finish() otherwise. - */ +void overlay_reconfigure(struct seat *seat); + +/* Calls overlay_hide() internally if there's no overlay to show */ void overlay_update(struct seat *seat); -/* Destroys the overlay if it exists */ +/* This function must be called when server->grabbed_view is destroyed */ +void overlay_hide(struct seat *seat); + +/* This function is called to clean up the timer on exit */ void overlay_finish(struct seat *seat); #endif diff --git a/include/placement.h b/include/placement.h index 5210117e..99f1ca28 100644 --- a/include/placement.h +++ b/include/placement.h @@ -4,8 +4,7 @@ #include #include - -struct view; +#include "view.h" bool placement_find_best(struct view *view, struct wlr_box *geometry); diff --git a/include/resistance.h b/include/resistance.h index 0a6c2783..6a155ceb 100644 --- a/include/resistance.h +++ b/include/resistance.h @@ -1,11 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ #ifndef LABWC_RESISTANCE_H #define LABWC_RESISTANCE_H - -#include -#include - -struct view; +#include "labwc.h" /** * resistance_unsnap_apply() - Apply resistance when dragging a diff --git a/include/resize-indicator.h b/include/resize-indicator.h index e8fe8480..36e7b0d1 100644 --- a/include/resize-indicator.h +++ b/include/resize-indicator.h @@ -5,6 +5,12 @@ struct server; struct view; +enum resize_indicator_mode { + LAB_RESIZE_INDICATOR_NEVER = 0, + LAB_RESIZE_INDICATOR_ALWAYS, + LAB_RESIZE_INDICATOR_NON_PIXEL +}; + void resize_indicator_reconfigure(struct server *server); void resize_indicator_show(struct view *view); void resize_indicator_update(struct view *view); diff --git a/include/session-lock.h b/include/session-lock.h index 207f0393..715c859f 100644 --- a/include/session-lock.h +++ b/include/session-lock.h @@ -2,7 +2,7 @@ #ifndef LABWC_SESSION_LOCK_H #define LABWC_SESSION_LOCK_H -#include +#include struct output; struct server; diff --git a/include/snap-constraints.h b/include/snap-constraints.h index c7ed7f8a..8ad32a1b 100644 --- a/include/snap-constraints.h +++ b/include/snap-constraints.h @@ -2,19 +2,21 @@ #ifndef LABWC_SNAP_CONSTRAINTS_H #define LABWC_SNAP_CONSTRAINTS_H -#include "common/edge.h" +#include + +#include "common/border.h" +#include "view.h" -struct view; struct wlr_box; -void snap_constraints_set(struct view *view, enum lab_edge direction, - struct wlr_box geom); +void snap_constraints_set(struct view *view, + enum wlr_edges direction, struct wlr_box geom); void snap_constraints_invalidate(struct view *view); void snap_constraints_update(struct view *view); struct wlr_box snap_constraints_effective(struct view *view, - enum lab_edge direction, bool use_pending); + enum wlr_edges direction, bool use_pending); #endif /* LABWC_SNAP_CONSTRAINTS_H */ diff --git a/include/snap.h b/include/snap.h index 09bf6baf..4ac8025d 100644 --- a/include/snap.h +++ b/include/snap.h @@ -2,19 +2,19 @@ #ifndef LABWC_SNAP_H #define LABWC_SNAP_H -#include "common/edge.h" +#include "common/border.h" +#include "view.h" -struct view; struct wlr_box; void snap_move_to_edge(struct view *view, - enum lab_edge direction, bool snap_to_windows, int *dx, int *dy); + enum view_edge direction, bool snap_to_windows, int *dx, int *dy); void snap_grow_to_next_edge(struct view *view, - enum lab_edge direction, struct wlr_box *geo); + enum view_edge direction, struct wlr_box *geo); void snap_shrink_to_next_edge(struct view *view, - enum lab_edge direction, struct wlr_box *geo); + enum view_edge direction, struct wlr_box *geo); void snap_invalidate_edge_cache(struct view *view); void snap_update_cache_geometry(struct view *view); diff --git a/include/ssd-internal.h b/include/ssd-internal.h index 600b9076..ec64311e 100644 --- a/include/ssd-internal.h +++ b/include/ssd-internal.h @@ -3,54 +3,51 @@ #define LABWC_SSD_INTERNAL_H #include -#include "common/border.h" -#include "theme.h" +#include "common/macros.h" +#include "ssd.h" #include "view.h" +#define FOR_EACH(tmp, ...) \ +{ \ + __typeof__(tmp) _x[] = { __VA_ARGS__, NULL }; \ + size_t _i = 0; \ + for ((tmp) = _x[_i]; _i < ARRAY_SIZE(_x) - 1; (tmp) = _x[++_i]) + +#define FOR_EACH_END } + +struct ssd_button { + struct view *view; + enum ssd_part_type type; + /* + * Bitmap of lab_button_state that represents a combination of + * hover/toggled/rounded states. + */ + uint8_t state_set; + /* + * Image buffers for each combination of hover/toggled/rounded states. + * img_buffers[state_set] is displayed. Some of these can be NULL + * (e.g. img_buffers[LAB_BS_ROUNDED] is set only for corner buttons). + * + * When "type" is LAB_SSD_BUTTON_WINDOW_ICON, these are all NULL and + * window_icon is used instead. + */ + struct scaled_img_buffer *img_buffers[LAB_BS_ALL + 1]; + + struct scaled_icon_buffer *window_icon; + + struct wl_listener destroy; +}; + +struct ssd_sub_tree { + struct wlr_scene_tree *tree; + struct wl_list parts; /* ssd_part.link */ +}; + struct ssd_state_title_width { int width; bool truncated; }; -/* - * The scene-graph of SSD looks like below. The parentheses indicate the - * type of each node (enum lab_node_type, stored in the node_descriptor - * attached to the wlr_scene_node). - * - * ssd->tree (LAB_NODE_SSD_ROOT) - * +--titlebar (LAB_NODE_TITLEBAR) - * | +--inactive - * | | +--background bar - * | | +--left corner - * | | +--right corner - * | | +--title (LAB_NODE_TITLE) - * | | +--iconify button (LAB_NODE_BUTTON_ICONIFY) - * | | | +--normal close icon image - * | | | +--hovered close icon image - * | | | +--... - * | | +--window icon (LAB_NODE_BUTTON_WINDOW_ICON) - * | | | +--window icon image - * | | +--... - * | +--active - * | +--... - * +--border - * | +--inactive - * | | +--top - * | | +--... - * | +--active - * | +--top - * | +--... - * +--shadow - * | +--inactive - * | | +--top - * | | +--... - * | +--active - * | +--top - * | +--... - * +--extents - * +--top - * +--... - */ struct ssd { struct view *view; struct wlr_scene_tree *tree; @@ -83,48 +80,33 @@ struct ssd { struct wlr_box geometry; struct ssd_state_title { char *text; - /* indexed by enum ssd_active_state */ - struct ssd_state_title_width dstates[2]; + struct ssd_state_title_width active; + struct ssd_state_title_width inactive; } title; } state; /* An invisible area around the view which allows resizing */ - struct ssd_extents_scene { - struct wlr_scene_tree *tree; - struct wlr_scene_rect *top, *bottom, *left, *right; - } extents; + struct ssd_sub_tree extents; /* The top of the view, containing buttons, title, .. */ - struct ssd_titlebar_scene { + struct { int height; struct wlr_scene_tree *tree; - struct ssd_titlebar_subtree { - struct wlr_scene_tree *tree; - struct wlr_scene_buffer *corner_left; - struct wlr_scene_buffer *corner_right; - struct wlr_scene_buffer *bar; - struct scaled_font_buffer *title; - struct wl_list buttons_left; /* ssd_button.link */ - struct wl_list buttons_right; /* ssd_button.link */ - } subtrees[2]; /* indexed by enum ssd_active_state */ + struct ssd_sub_tree active; + struct ssd_sub_tree inactive; } titlebar; /* Borders allow resizing as well */ - struct ssd_border_scene { + struct { struct wlr_scene_tree *tree; - struct ssd_border_subtree { - struct wlr_scene_tree *tree; - struct wlr_scene_rect *top, *bottom, *left, *right; - } subtrees[2]; /* indexed by enum ssd_active_state */ + struct ssd_sub_tree active; + struct ssd_sub_tree inactive; } border; - struct ssd_shadow_scene { + struct { struct wlr_scene_tree *tree; - struct ssd_shadow_subtree { - struct wlr_scene_tree *tree; - struct wlr_scene_buffer *top, *bottom, *left, *right, - *top_left, *top_right, *bottom_left, *bottom_right; - } subtrees[2]; /* indexed by enum ssd_active_state */ + struct ssd_sub_tree active; + struct ssd_sub_tree inactive; } shadow; /* @@ -135,39 +117,47 @@ struct ssd { struct border margin; }; -struct ssd_button { +struct ssd_part { + enum ssd_part_type type; + + /* Buffer pointer. May be NULL */ + struct scaled_font_buffer *buffer; + + /* This part represented in scene graph */ struct wlr_scene_node *node; - enum lab_node_type type; - /* - * Bitmap of lab_button_state that represents a combination of - * hover/toggled/rounded states. - */ - uint8_t state_set; - /* - * Image buffers for each combination of hover/toggled/rounded states. - * img_buffers[state_set] is displayed. Some of these can be NULL - * (e.g. img_buffers[LAB_BS_ROUNDED] is set only for corner buttons). - * - * When the button type is LAB_NODE_BUTTON_WINDOW_ICON, - * these are all NULL and window_icon is used instead. - */ - struct scaled_img_buffer *img_buffers[LAB_BS_ALL + 1]; + struct wl_list link; +}; - struct scaled_icon_buffer *window_icon; - - struct wl_list link; /* ssd_titlebar_subtree.buttons_{left,right} */ +struct ssd_hover_state { + struct view *view; + struct ssd_button *button; }; struct wlr_buffer; struct wlr_scene_tree; /* SSD internal helpers to create various SSD elements */ -struct ssd_button *attach_ssd_button(struct wl_list *button_parts, - enum lab_node_type type, struct wlr_scene_tree *parent, - struct lab_img *imgs[LAB_BS_ALL + 1], int x, int y, +/* TODO: Replace some common args with a struct */ +struct ssd_part *add_scene_part( + struct wl_list *part_list, enum ssd_part_type type); +struct ssd_part *add_scene_rect( + struct wl_list *list, enum ssd_part_type type, + struct wlr_scene_tree *parent, int width, int height, int x, int y, + float color[4]); +struct ssd_part *add_scene_buffer( + struct wl_list *list, enum ssd_part_type type, + struct wlr_scene_tree *parent, struct wlr_buffer *buffer, int x, int y); +struct ssd_part *add_scene_button(struct wl_list *part_list, + enum ssd_part_type type, struct wlr_scene_tree *parent, + struct lab_img *buffers[LAB_BS_ALL + 1], int x, int y, struct view *view); +/* SSD internal helpers */ +struct ssd_part *ssd_get_part( + struct wl_list *part_list, enum ssd_part_type type); +void ssd_destroy_parts(struct wl_list *list); + /* SSD internal */ void ssd_titlebar_create(struct ssd *ssd); void ssd_titlebar_update(struct ssd *ssd); diff --git a/include/ssd.h b/include/ssd.h index 1340c8dc..eda0370e 100644 --- a/include/ssd.h +++ b/include/ssd.h @@ -2,15 +2,8 @@ #ifndef LABWC_SSD_H #define LABWC_SSD_H -#include "common/node-type.h" -#include "config/types.h" - -enum ssd_active_state { - SSD_INACTIVE = 0, - SSD_ACTIVE = 1, -}; - -#define FOR_EACH_ACTIVE_STATE(active) for (active = SSD_INACTIVE; active <= SSD_ACTIVE; active++) +#include +#include "common/border.h" struct wlr_cursor; @@ -21,10 +14,64 @@ struct wlr_cursor; */ #define SSD_SHADOW_INSET 0.3 +/* + * Sequence these according to the order they should be processed for + * press and hover events. Bear in mind that some of their respective + * interactive areas overlap, so for example buttons need to come before title. + */ +enum ssd_part_type { + LAB_SSD_NONE = 0, + + LAB_SSD_BUTTON_CLOSE = 1, + LAB_SSD_BUTTON_MAXIMIZE, + LAB_SSD_BUTTON_ICONIFY, + LAB_SSD_BUTTON_WINDOW_ICON, + LAB_SSD_BUTTON_WINDOW_MENU, + LAB_SSD_BUTTON_SHADE, + LAB_SSD_BUTTON_OMNIPRESENT, + /* only for internal use */ + LAB_SSD_BUTTON_FIRST = LAB_SSD_BUTTON_CLOSE, + LAB_SSD_BUTTON_LAST = LAB_SSD_BUTTON_OMNIPRESENT, + LAB_SSD_BUTTON, + + LAB_SSD_PART_TITLEBAR, + LAB_SSD_PART_TITLEBAR_CORNER_RIGHT, + LAB_SSD_PART_TITLEBAR_CORNER_LEFT, + LAB_SSD_PART_TITLE, + + /* shared by shadows, borders and extents */ + LAB_SSD_PART_CORNER_TOP_LEFT, + LAB_SSD_PART_CORNER_TOP_RIGHT, + LAB_SSD_PART_CORNER_BOTTOM_RIGHT, + LAB_SSD_PART_CORNER_BOTTOM_LEFT, + LAB_SSD_PART_TOP, + LAB_SSD_PART_RIGHT, + LAB_SSD_PART_BOTTOM, + LAB_SSD_PART_LEFT, + + LAB_SSD_CLIENT, + LAB_SSD_FRAME, + LAB_SSD_ROOT, + LAB_SSD_MENU, + LAB_SSD_OSD, + LAB_SSD_LAYER_SURFACE, + LAB_SSD_LAYER_SUBSURFACE, + LAB_SSD_UNMANAGED, + LAB_SSD_ALL, + LAB_SSD_END_MARKER +}; + +enum ssd_mode { + LAB_SSD_MODE_INVALID, + LAB_SSD_MODE_NONE, + LAB_SSD_MODE_BORDER, + LAB_SSD_MODE_FULL, +}; + /* Forward declare arguments */ -struct server; struct ssd; struct ssd_button; +struct ssd_hover_state; struct view; struct wlr_scene; struct wlr_scene_node; @@ -51,20 +98,19 @@ 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); +struct ssd_hover_state *ssd_hover_state_new(void); +void ssd_update_button_hover(struct wlr_scene_node *node, + struct ssd_hover_state *hover_state); -void ssd_button_free(struct ssd_button *button); +enum ssd_part_type ssd_button_get_type(const struct ssd_button *button); +struct view *ssd_button_get_view(const struct ssd_button *button); /* Public SSD helpers */ - -/* - * Returns a part type that represents a mouse context like "Top", "Left" and - * "TRCorner" when the cursor is on the window border or resizing handle. - */ -enum lab_node_type ssd_get_resizing_type(const struct ssd *ssd, - struct wlr_cursor *cursor); -enum lab_ssd_mode ssd_mode_parse(const char *mode); +enum ssd_part_type ssd_get_part_type(const struct ssd *ssd, + struct wlr_scene_node *node, struct wlr_cursor *cursor); +uint32_t ssd_resize_edges(enum ssd_part_type type); +bool ssd_part_contains(enum ssd_part_type whole, enum ssd_part_type candidate); +enum ssd_mode ssd_mode_parse(const char *mode); /* TODO: clean up / update */ struct border ssd_thickness(struct view *view); diff --git a/include/theme.h b/include/theme.h index 58ea3e13..92d4a99e 100644 --- a/include/theme.h +++ b/include/theme.h @@ -9,8 +9,8 @@ #define LABWC_THEME_H #include -#include -#include "common/node-type.h" +#include +#include "ssd.h" struct lab_img; @@ -41,11 +41,11 @@ struct theme_snapping_overlay { enum lab_button_state { LAB_BS_DEFAULT = 0, - LAB_BS_HOVERED = 1 << 0, + LAB_BS_HOVERD = 1 << 0, LAB_BS_TOGGLED = 1 << 1, LAB_BS_ROUNDED = 1 << 2, - LAB_BS_ALL = LAB_BS_HOVERED | LAB_BS_TOGGLED | LAB_BS_ROUNDED, + LAB_BS_ALL = LAB_BS_HOVERD | LAB_BS_TOGGLED | LAB_BS_ROUNDED, }; struct theme_background { @@ -83,14 +83,14 @@ struct theme { /* * Themes/textures for each active/inactive window. Indexed by - * ssd_active_state. + * THEME_INACTIVE and THEME_ACTIVE. */ struct { /* title background pattern (solid or gradient) */ struct theme_background title_bg; /* TODO: add toggled/hover/pressed/disabled colors for buttons */ - float button_colors[LAB_NODE_BUTTON_LAST + 1][4]; + float button_colors[LAB_SSD_BUTTON_LAST + 1][4]; float border_color[4]; float toggled_keybinds_color[4]; @@ -104,12 +104,12 @@ struct theme { * The texture of a window buttons for each hover/toggled/rounded * state. This can be accessed like: * - * buttons[LAB_NODE_BUTTON_ICONIFY][LAB_BS_HOVERED | LAB_BS_TOGGLED] + * buttons[LAB_SSD_BUTTON_ICONIFY][LAB_BS_HOVERD | LAB_BS_TOGGLED] * - * Elements in buttons[0] are all NULL since LAB_NODE_BUTTON_FIRST is 1. + * Elements in buttons[0] are all NULL since LAB_SSD_BUTTON_FIRST is 1. */ struct lab_img *button_imgs - [LAB_NODE_BUTTON_LAST + 1][LAB_BS_ALL + 1]; + [LAB_SSD_BUTTON_LAST + 1][LAB_BS_ALL + 1]; /* * The titlebar background is specified as a cairo_pattern @@ -164,39 +164,13 @@ struct theme { float osd_border_color[4]; float osd_label_text_color[4]; - struct window_switcher_classic_theme { - int width; - int padding; - int item_padding_x; - int item_padding_y; - int item_active_border_width; - float item_active_border_color[4]; - float item_active_bg_color[4]; - int item_icon_size; - bool width_is_percent; - - /* - * Not set in rc.xml/themerc, but derived from the tallest - * titlebar object plus 2 * window_titlebar_padding_height - */ - int item_height; - } osd_window_switcher_classic; - - struct window_switcher_thumbnail_theme { - int max_width; - int padding; - int item_width; - int item_height; - int item_padding; - int item_active_border_width; - float item_active_border_color[4]; - float item_active_bg_color[4]; - int item_icon_size; - bool max_width_is_percent; - - int title_height; - } osd_window_switcher_thumbnail; - + int osd_window_switcher_width; + int osd_window_switcher_padding; + int osd_window_switcher_item_padding_x; + int osd_window_switcher_item_padding_y; + int osd_window_switcher_item_active_border_width; + int osd_window_switcher_item_icon_size; + bool osd_window_switcher_width_is_percent; int osd_window_switcher_preview_border_width; float osd_window_switcher_preview_border_color[3][4]; @@ -207,11 +181,20 @@ struct theme { struct theme_snapping_overlay snapping_overlay_region, snapping_overlay_edge; + /* + * Not set in rc.xml/themerc, but derived from the tallest titlebar + * object plus 2 * window_titlebar_padding_height + */ + int osd_window_switcher_item_height; + /* magnifier */ float mag_border_color[4]; int mag_border_width; }; +#define THEME_INACTIVE 0 +#define THEME_ACTIVE 1 + struct server; /** diff --git a/include/translate.h b/include/translate.h deleted file mode 100644 index f7fc51e2..00000000 --- a/include/translate.h +++ /dev/null @@ -1,14 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef LABWC_TRANSLATE_H -#define LABWC_TRANSLATE_H -#include "config.h" - -#if HAVE_NLS -#include -#include -#define _ gettext -#else -#define _(s) (s) -#endif - -#endif /* LABWC_TRANSLATE_H */ diff --git a/include/view.h b/include/view.h index a8456de2..2ea83cd7 100644 --- a/include/view.h +++ b/include/view.h @@ -2,20 +2,16 @@ #ifndef LABWC_VIEW_H #define LABWC_VIEW_H +#include "config/rcxml.h" +#include "config.h" +#include "ssd.h" #include #include #include #include #include -#include "common/edge.h" -#include "config.h" -#include "config/types.h" +#include "common/three-state.h" -/* - * Default minimal window size. Clients can explicitly set smaller values via - * e.g. xdg_toplevel::set_min_size. - */ -#define LAB_MIN_VIEW_WIDTH 100 #define LAB_MIN_VIEW_HEIGHT 60 /* @@ -32,6 +28,7 @@ * In labwc, a view is a container for surfaces which can be moved around by * the user. In practice this means XDG toplevel and XWayland windows. */ + enum view_type { LAB_XDG_SHELL_VIEW, #if HAVE_XWAYLAND @@ -62,6 +59,16 @@ enum view_axis { VIEW_AXIS_INVALID = (1 << 2), }; +enum view_edge { + VIEW_EDGE_INVALID = 0, + + VIEW_EDGE_LEFT, + VIEW_EDGE_RIGHT, + VIEW_EDGE_UP, + VIEW_EDGE_DOWN, + VIEW_EDGE_CENTER, +}; + enum view_wants_focus { /* View does not want focus */ VIEW_WANTS_FOCUS_NEVER = 0, @@ -82,6 +89,33 @@ enum view_wants_focus { VIEW_WANTS_FOCUS_UNLIKELY, }; +/* + * Window types are based on the NET_WM constants from X11. See: + * https://specifications.freedesktop.org/wm-spec/1.4/ar01s05.html#id-1.6.7 + * + * The enum constants are intended to match wlr_xwayland_net_wm_window_type. + * Redefining the same constants here may seem redundant, but is necessary + * to make them available even in builds with xwayland support disabled. + */ +enum window_type { + NET_WM_WINDOW_TYPE_DESKTOP = 0, + NET_WM_WINDOW_TYPE_DOCK, + NET_WM_WINDOW_TYPE_TOOLBAR, + NET_WM_WINDOW_TYPE_MENU, + NET_WM_WINDOW_TYPE_UTILITY, + NET_WM_WINDOW_TYPE_SPLASH, + NET_WM_WINDOW_TYPE_DIALOG, + NET_WM_WINDOW_TYPE_DROPDOWN_MENU, + NET_WM_WINDOW_TYPE_POPUP_MENU, + NET_WM_WINDOW_TYPE_TOOLTIP, + NET_WM_WINDOW_TYPE_NOTIFICATION, + NET_WM_WINDOW_TYPE_COMBO, + NET_WM_WINDOW_TYPE_DND, + NET_WM_WINDOW_TYPE_NORMAL, + + WINDOW_TYPE_LEN +}; + struct view; struct wlr_surface; struct foreign_toplevel; @@ -106,6 +140,7 @@ struct view_size_hints { struct view_impl { void (*configure)(struct view *view, struct wlr_box geo); void (*close)(struct view *view); + const char *(*get_string_prop)(struct view *view, const char *prop); void (*map)(struct view *view); void (*set_activated)(struct view *view, bool activated); void (*set_fullscreen)(struct view *view, bool fullscreen); @@ -130,7 +165,7 @@ struct view_impl { bool (*has_strut_partial)(struct view *self); /* returns true if view declared itself a window type */ bool (*contains_window_type)(struct view *view, - enum lab_window_type window_type); + enum window_type window_type); /* returns the client pid that this view belongs to */ pid_t (*get_pid)(struct view *view); }; @@ -172,24 +207,21 @@ struct view { struct wlr_scene_tree *scene_tree; struct wlr_scene_tree *content_tree; - /* These are never NULL and an empty string is set instead. */ - char *title; - char *app_id; /* WM_CLASS for xwayland windows */ - bool mapped; bool been_mapped; - enum lab_ssd_mode ssd_mode; + bool ssd_enabled; + bool ssd_titlebar_hidden; enum ssd_preference ssd_preference; bool shaded; bool minimized; enum view_axis maximized; bool fullscreen; bool tearing_hint; - enum lab_tristate force_tearing; + enum three_state force_tearing; bool visible_on_all_workspaces; - enum lab_edge tiled; - enum lab_edge edges_visible; - bool inhibits_keybinds; /* also inhibits mousebinds */ + enum view_edge tiled; + uint32_t edges_visible; /* enum wlr_edges bitset */ + bool inhibits_keybinds; xkb_layout_index_t keyboard_layout; /* Pointer to an output owned struct region, may be NULL */ @@ -282,18 +314,18 @@ struct view_query { struct wl_list link; char *identifier; char *title; - enum lab_window_type window_type; + int window_type; char *sandbox_engine; char *sandbox_app_id; - enum lab_tristate shaded; + enum three_state shaded; enum view_axis maximized; - enum lab_tristate iconified; - enum lab_tristate focused; - enum lab_tristate omnipresent; - enum lab_edge tiled; + enum three_state iconified; + enum three_state focused; + enum three_state omnipresent; + enum view_edge tiled; char *tiled_region; char *desktop; - enum lab_ssd_mode decoration; + enum ssd_mode decoration; char *monitor; }; @@ -307,6 +339,28 @@ struct xdg_toplevel_view { struct wl_listener new_popup; }; +/* All criteria is applied in AND logic */ +enum lab_view_criteria { + /* No filter -> all focusable views */ + LAB_VIEW_CRITERIA_NONE = 0, + + /* + * Includes always-on-top views, e.g. + * what is visible on the current workspace + */ + LAB_VIEW_CRITERIA_CURRENT_WORKSPACE = 1 << 0, + + /* Positive criteria */ + LAB_VIEW_CRITERIA_FULLSCREEN = 1 << 1, + LAB_VIEW_CRITERIA_ALWAYS_ON_TOP = 1 << 2, + LAB_VIEW_CRITERIA_ROOT_TOPLEVEL = 1 << 3, + + /* Negative criteria */ + LAB_VIEW_CRITERIA_NO_ALWAYS_ON_TOP = 1 << 6, + LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER = 1 << 7, + LAB_VIEW_CRITERIA_NO_OMNIPRESENT = 1 << 8, +}; + /** * view_from_wlr_surface() - returns the view associated with a * wlr_surface, or NULL if the surface has no associated view. @@ -428,11 +482,16 @@ void view_array_append(struct server *server, struct wl_array *views, enum lab_view_criteria criteria); enum view_wants_focus view_wants_focus(struct view *view); +bool view_contains_window_type(struct view *view, enum window_type window_type); -/* If view is NULL, the size of SSD is not considered */ -struct wlr_box view_get_edge_snap_box(struct view *view, struct output *output, - enum lab_edge edge); -struct wlr_box view_get_region_snap_box(struct view *view, struct region *region); +/** + * view_edge_invert() - select the opposite of a provided edge + * + * VIEW_EDGE_CENTER and VIEW_EDGE_INVALID both map to VIEW_EDGE_INVALID. + * + * @edge: edge to be inverted + */ +enum view_edge view_edge_invert(enum view_edge edge); /** * view_is_focusable() - Check whether or not a view can be focused @@ -454,15 +513,11 @@ bool view_is_focusable(struct view *view); */ void view_offer_focus(struct view *view); -struct wlr_box view_get_edge_snap_box(struct view *view, struct output *output, - enum lab_edge edge); - void mappable_connect(struct mappable *mappable, struct wlr_surface *surface, wl_notify_func_t notify_map, wl_notify_func_t notify_unmap); void mappable_disconnect(struct mappable *mappable); void view_toggle_keybinds(struct view *view); -bool view_inhibits_actions(struct view *view, struct wl_list *actions); void view_set_activated(struct view *view, bool activated); void view_set_output(struct view *view, struct output *output); @@ -486,7 +541,7 @@ void view_moved(struct view *view); void view_minimize(struct view *view, bool minimized); bool view_compute_centered_position(struct view *view, const struct wlr_box *ref, int w, int h, int *x, int *y); -struct wlr_box view_get_fallback_natural_geometry(struct view *view); +void view_set_fallback_natural_geometry(struct view *view); void view_store_natural_geometry(struct view *view); /** @@ -517,10 +572,10 @@ void view_center(struct view *view, const struct wlr_box *ref); * @policy: placement policy to apply */ void view_place_by_policy(struct view *view, bool allow_cursor, - enum lab_placement_policy policy); + enum view_placement_policy policy); void view_constrain_size_to_that_of_usable_area(struct view *view); -void view_set_maximized(struct view *view, enum view_axis maximized); +void view_restore_to(struct view *view, struct wlr_box geometry); void view_set_untiled(struct view *view); void view_maximize(struct view *view, enum view_axis axis, bool store_natural_geometry); @@ -540,22 +595,24 @@ bool view_is_tiled(struct view *view); bool view_is_tiled_and_notify_tiled(struct view *view); bool view_is_floating(struct view *view); void view_move_to_workspace(struct view *view, struct workspace *workspace); -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); +enum ssd_mode view_get_ssd_mode(struct view *view); +void view_set_ssd_mode(struct view *view, enum ssd_mode mode); +void view_set_decorations(struct view *view, enum ssd_mode mode, bool force_ssd); void view_toggle_fullscreen(struct view *view); void view_invalidate_last_layout_geometry(struct view *view); 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); -void view_snap_to_edge(struct view *view, enum lab_edge direction, - bool across_outputs, bool combine, bool store_natural_geometry); +void view_move_to_edge(struct view *view, enum view_edge direction, bool snap_to_windows); +void view_grow_to_edge(struct view *view, enum view_edge direction); +void view_shrink_to_edge(struct view *view, enum view_edge direction); +void view_snap_to_edge(struct view *view, enum view_edge direction, + bool across_outputs, bool store_natural_geometry); void view_snap_to_region(struct view *view, struct region *region, bool store_natural_geometry); void view_move_to_output(struct view *view, struct output *output); void view_move_to_front(struct view *view); void view_move_to_back(struct view *view); +struct view *view_get_root(struct view *view); +void view_append_children(struct view *view, struct wl_array *children); /** * view_get_modal_dialog() - returns any modal dialog found among this @@ -574,9 +631,11 @@ bool view_on_output(struct view *view, struct output *output); */ bool view_has_strut_partial(struct view *view); -void view_set_title(struct view *view, const char *title); -void view_set_app_id(struct view *view, const char *app_id); +const char *view_get_string_prop(struct view *view, const char *prop); +void view_update_title(struct view *view); +void view_update_app_id(struct view *view); void view_reload_ssd(struct view *view); +int view_get_min_width(void); void view_set_shade(struct view *view, bool shaded); @@ -595,7 +654,8 @@ void view_init(struct view *view); void view_destroy(struct view *view); enum view_axis view_axis_parse(const char *direction); -enum lab_placement_policy view_placement_parse(const char *policy); +enum view_edge view_edge_parse(const char *direction); +enum view_placement_policy view_placement_parse(const char *policy); /* xdg.c */ struct wlr_xdg_surface *xdg_surface_from_view(struct view *view); diff --git a/include/window-rules.h b/include/window-rules.h index 1bee4c09..4b7f6673 100644 --- a/include/window-rules.h +++ b/include/window-rules.h @@ -4,7 +4,6 @@ #include #include -#include "config/types.h" enum window_rule_event { LAB_WINDOW_RULE_EVENT_ON_FIRST_MAP = 0, @@ -25,7 +24,7 @@ enum property { struct window_rule { char *identifier; char *title; - enum lab_window_type window_type; + int window_type; char *sandbox_engine; char *sandbox_app_id; bool match_once; diff --git a/include/xwayland.h b/include/xwayland.h index 5fa20e11..b5bd2623 100644 --- a/include/xwayland.h +++ b/include/xwayland.h @@ -65,6 +65,8 @@ void xwayland_unmanaged_create(struct server *server, void xwayland_view_create(struct server *server, struct wlr_xwayland_surface *xsurface, bool mapped); +struct wlr_xwayland_surface *xwayland_surface_from_view(struct view *view); + void xwayland_server_init(struct server *server, struct wlr_compositor *compositor); void xwayland_server_finish(struct server *server); diff --git a/meson.build b/meson.build index 335bf629..3172c7e5 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'labwc', 'c', - version: '0.9.2', + version: '0.9.0', license: 'GPL-2.0-only', meson_version: '>=0.59.0', default_options: [ @@ -127,24 +127,13 @@ conf_data.set10('HAVE_RSVG', have_rsvg) conf_data.set10('HAVE_LIBSFDO', have_libsfdo) foreach sym : ['LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY', 'LIBINPUT_CONFIG_3FG_DRAG_ENABLED_3FG'] - has_sym = input.type_name() != 'internal' \ - and cc.has_header_symbol('libinput.h', sym, dependencies: input) - conf_data.set10('HAVE_' + sym, has_sym) + conf_data.set10('HAVE_' + sym, cc.has_header_symbol('libinput.h', sym, dependencies: input)) endforeach if get_option('static_analyzer').enabled() add_project_arguments(['-fanalyzer'], language: 'c') endif -link_args = [] -if get_option('sections').enabled() - add_project_arguments(['-ffunction-sections'], language: 'c') - link_args += [ - '-Wl,--gc-sections', - '-Wl,--print-gc-sections', - ] -endif - msgfmt = find_program('msgfmt', required: get_option('nls')) if msgfmt.found() source_root = meson.current_source_dir() @@ -191,7 +180,6 @@ endif subdir('include') subdir('src') subdir('docs') -subdir('clients') dep_cmocka = dependency('cmocka', required: get_option('test')) if dep_cmocka.found() @@ -204,13 +192,16 @@ executable( include_directories: [labwc_inc], dependencies: labwc_deps, install: true, - link_args: link_args, ) install_data('data/labwc.desktop', install_dir: get_option('datadir') / 'wayland-sessions') install_data('data/labwc-portals.conf', install_dir: get_option('datadir') / 'xdg-desktop-portal') +# TODO: move this to clients/meson.build after the labnag PR +clients = files('clients/lab-sensible-terminal') +install_data(clients, install_dir: get_option('bindir')) + 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..ec3fe85d 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -5,4 +5,3 @@ option('icon', type: 'feature', value: 'enabled', description: 'Enable window ic option('nls', type: 'feature', value: 'auto', description: 'Enable native language support') 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 23312931..2878ab89 100644 --- a/po/LINGUAS +++ b/po/LINGUAS @@ -1 +1 @@ -ar ca cs da de el es et eu fa fi fr gl he hu id it ja ka 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 hu id it ja ka ko lt ms nl pa pl pt pt_BR ru sk sv tr uk zh_CN diff --git a/po/ar.po b/po/ar.po index 46ec2870..94e860f8 100644 --- a/po/ar.po +++ b/po/ar.po @@ -8,16 +8,14 @@ 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-07-17 07:22+0000\n" +"PO-Revision-Date: 2024-09-23 20:01+0000\n" "Last-Translator: Abdullah Albaroty \n" -"Language-Team: Arabic \n" +"Language-Team: Arabic \n" "Language: ar\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " -"&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n" +"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n" "X-Generator: Weblate 4.2.1\n" #: src/menu/menu.c:1016 @@ -26,7 +24,7 @@ msgstr "اذهب هناك..." #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "الطرفية" +msgstr "" #: src/menu/menu.c:1040 msgid "Reconfigure" diff --git a/po/ca.po b/po/ca.po index 7bc442bc..f9c6975e 100644 --- a/po/ca.po +++ b/po/ca.po @@ -8,10 +8,9 @@ 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-10-11 20:01+0000\n" -"Last-Translator: alvaroelpob \n" -"Language-Team: Catalan \n" +"PO-Revision-Date: 2025-03-29 11:25+0000\n" +"Last-Translator: Davidmp \n" +"Language-Team: Catalan \n" "Language: ca\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -25,7 +24,7 @@ msgstr "Ves-hi..." #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "Terminal" +msgstr "" #: src/menu/menu.c:1040 msgid "Reconfigure" diff --git a/po/cs.po b/po/cs.po index 11ff25ce..27fe6a68 100644 --- a/po/cs.po +++ b/po/cs.po @@ -8,32 +8,29 @@ 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-10-11 20:01+0000\n" -"Last-Translator: p-bo \n" -"Language-Team: Czech \n" +"PO-Revision-Date: 2024-03-02 02:00+0100\n" +"Last-Translator: zenobit \n" +"Language-Team: Czech \n" "Language: cs\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==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" -"X-Generator: Weblate 4.2.1\n" #: src/menu/menu.c:1016 msgid "Go there..." -msgstr "Přejít tam..." +msgstr "" #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "Terminál" +msgstr "" #: src/menu/menu.c:1040 msgid "Reconfigure" -msgstr "Přenastavit" +msgstr "Překonfigurovat" #: src/menu/menu.c:1042 msgid "Exit" -msgstr "Ukončit" +msgstr "Odejít" #: src/menu/menu.c:1056 msgid "Minimize" @@ -49,7 +46,7 @@ msgstr "Na celou obrazovku" #: src/menu/menu.c:1062 msgid "Roll Up/Down" -msgstr "Posouvat nahoru/dolů" +msgstr "Rolovat nahoru/dolů" #: src/menu/menu.c:1064 msgid "Decorations" @@ -69,11 +66,11 @@ msgstr "Posunout doprava" #: src/menu/menu.c:1083 msgid "Always on Visible Workspace" -msgstr "Vždy na viditelné Pracovní ploše" +msgstr "Vždy na viditelné Pracovní Ploše" #: src/menu/menu.c:1086 msgid "Workspace" -msgstr "Pracovní plocha" +msgstr "Pracovní Plocha" #: src/menu/menu.c:1089 msgid "Close" diff --git a/po/de.po b/po/de.po index 5841f3f6..3d68fcbe 100644 --- a/po/de.po +++ b/po/de.po @@ -8,10 +8,9 @@ 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" +"PO-Revision-Date: 2024-11-05 21:01+0000\n" "Last-Translator: Ettore Atalan \n" -"Language-Team: German \n" +"Language-Team: German \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -25,7 +24,7 @@ msgstr "Dorthin gehen..." #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "Terminal" +msgstr "" #: src/menu/menu.c:1040 msgid "Reconfigure" diff --git a/po/el.po b/po/el.po index 8dcaab37..29e1d28f 100644 --- a/po/el.po +++ b/po/el.po @@ -8,10 +8,9 @@ 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-02 08:01+0000\n" -"Last-Translator: Dimitrios Glentadakis \n" -"Language-Team: Greek \n" +"PO-Revision-Date: 2024-09-25 20:01+0000\n" +"Last-Translator: Giannis Antypas \n" +"Language-Team: Greek \n" "Language: el\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -25,7 +24,7 @@ msgstr "Πήγαινε εκεί..." #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "Τερματικό" +msgstr "" #: src/menu/menu.c:1040 msgid "Reconfigure" diff --git a/po/fi.po b/po/fi.po index 8aa26976..b1a6c9d1 100644 --- a/po/fi.po +++ b/po/fi.po @@ -8,10 +8,9 @@ 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-10-23 23:27+0000\n" -"Last-Translator: sudoasd \n" -"Language-Team: Finnish \n" +"PO-Revision-Date: 2024-04-20 15:23+0000\n" +"Last-Translator: Jouni Järvinen \n" +"Language-Team: Finnish \n" "Language: fi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -21,11 +20,11 @@ msgstr "" #: src/menu/menu.c:1016 msgid "Go there..." -msgstr "Mene..." +msgstr "" #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "Pääte" +msgstr "" #: src/menu/menu.c:1040 msgid "Reconfigure" @@ -57,7 +56,7 @@ msgstr "Kehykset" #: src/menu/menu.c:1066 msgid "Always on Top" -msgstr "Aina päällimmäisenä" +msgstr "Aina ylimpänä" #: src/menu/menu.c:1071 msgid "Move Left" diff --git a/po/fr.po b/po/fr.po index 1b9c8879..78b8bfca 100644 --- a/po/fr.po +++ b/po/fr.po @@ -8,10 +8,9 @@ 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-11 00:27+0000\n" -"Last-Translator: Oliver Chiasson \n" -"Language-Team: French \n" +"PO-Revision-Date: 2024-12-15 01:13+0000\n" +"Last-Translator: vTT \n" +"Language-Team: French \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -25,7 +24,7 @@ msgstr "Y aller…" #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "Terminal" +msgstr "" #: src/menu/menu.c:1040 msgid "Reconfigure" diff --git a/po/he.po b/po/he.po deleted file mode 100644 index 08f301d9..00000000 --- a/po/he.po +++ /dev/null @@ -1,81 +0,0 @@ -# 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: 2025-10-30 06:38+0000\n" -"Last-Translator: Yaron Shahrabani \n" -"Language-Team: Hebrew \n" -"Language: he\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=4; plural=(n == 1) ? 0 : ((n == 2) ? 1 : ((n > 10 && " -"n % 10 == 0) ? 2 : 3));\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 "מסוף" - -#: src/menu/menu.c:1040 -msgid "Reconfigure" -msgstr "הגדרה מחדש" - -#: src/menu/menu.c:1042 -msgid "Exit" -msgstr "יציאה" - -#: src/menu/menu.c:1056 -msgid "Minimize" -msgstr "מזעור" - -#: src/menu/menu.c:1058 -msgid "Maximize" -msgstr "הגדלה" - -#: src/menu/menu.c:1060 -msgid "Fullscreen" -msgstr "מסך מלא" - -#: 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 "סגירה" diff --git a/po/ko.po b/po/ko.po index 8af99f83..bacefdb9 100644 --- a/po/ko.po +++ b/po/ko.po @@ -8,10 +8,9 @@ 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-02 03:27+0000\n" +"PO-Revision-Date: 2024-12-10 02:04+0000\n" "Last-Translator: 이정희 \n" -"Language-Team: Korean \n" +"Language-Team: Korean \n" "Language: ko\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -25,7 +24,7 @@ msgstr "빨리 가기..." #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "터미널" +msgstr "" #: src/menu/menu.c:1040 msgid "Reconfigure" diff --git a/po/lt.po b/po/lt.po index 3f9d6113..9c734b53 100644 --- a/po/lt.po +++ b/po/lt.po @@ -8,17 +8,14 @@ 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-07-19 18:47+0000\n" +"PO-Revision-Date: 2024-11-13 21:01+0000\n" "Last-Translator: Moo \n" -"Language-Team: Lithuanian \n" +"Language-Team: Lithuanian \n" "Language: lt\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 || n % 100 > " -"19)) ? 0 : ((n % 10 >= 2 && n % 10 <= 9 && (n % 100 < 11 || n % 100 > 19)) ? " -"1 : 2);\n" +"Plural-Forms: nplurals=3; plural=(n % 10 == 1 && (n % 100 < 11 || n % 100 > 19)) ? 0 : ((n % 10 >= 2 && n % 10 <= 9 && (n % 100 < 11 || n % 100 > 19)) ? 1 : 2);\n" "X-Generator: Weblate 4.2.1\n" #: src/menu/menu.c:1016 @@ -27,7 +24,7 @@ msgstr "Eiti ten..." #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "Terminalas" +msgstr "" #: src/menu/menu.c:1040 msgid "Reconfigure" diff --git a/po/ms.po b/po/ms.po index 95ac65d1..5882f8a6 100644 --- a/po/ms.po +++ b/po/ms.po @@ -8,10 +8,9 @@ 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-18 20:01+0000\n" +"PO-Revision-Date: 2025-04-08 05:25+0000\n" "Last-Translator: Qayyum Yazid \n" -"Language-Team: Malay \n" +"Language-Team: Malay \n" "Language: ms\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -25,7 +24,7 @@ msgstr "Pergi sana..." #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "Terminal" +msgstr "" #: src/menu/menu.c:1040 msgid "Reconfigure" diff --git a/po/pt.po b/po/pt.po index 6d70076d..0bb6ff3f 100644 --- a/po/pt.po +++ b/po/pt.po @@ -8,10 +8,9 @@ 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-07-13 08:01+0000\n" +"PO-Revision-Date: 2024-09-21 20:01+0000\n" "Last-Translator: Hugo Carvalho \n" -"Language-Team: Portuguese \n" +"Language-Team: Portuguese \n" "Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -25,7 +24,7 @@ msgstr "Ir lá..." #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "Terminal" +msgstr "" #: src/menu/menu.c:1040 msgid "Reconfigure" diff --git a/po/pt_BR.po b/po/pt_BR.po index 89a23170..678915b6 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -8,10 +8,9 @@ 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-09-06 04:27+0000\n" -"Last-Translator: virtual-hand \n" -"Language-Team: Portuguese (Brazil) \n" +"PO-Revision-Date: 2025-02-04 21:01+0000\n" +"Last-Translator: EggSupernova \n" +"Language-Team: Portuguese (Brazil) \n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -25,7 +24,7 @@ msgstr "Vá para..." #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "Terminal" +msgstr "" #: src/menu/menu.c:1040 msgid "Reconfigure" diff --git a/po/ru.po b/po/ru.po index fc6f4745..88c97b55 100644 --- a/po/ru.po +++ b/po/ru.po @@ -8,16 +8,14 @@ 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-07-30 08:01+0000\n" -"Last-Translator: Valera \n" -"Language-Team: Russian \n" +"PO-Revision-Date: 2024-12-08 21:01+0000\n" +"Last-Translator: unabomberlive <7alinchik@mail.ru>\n" +"Language-Team: Russian \n" "Language: ru\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" +"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 @@ -26,7 +24,7 @@ msgstr "Перейти..." #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "Терминал" +msgstr "" #: src/menu/menu.c:1040 msgid "Reconfigure" diff --git a/po/uk.po b/po/uk.po index 2be3a417..aa481873 100644 --- a/po/uk.po +++ b/po/uk.po @@ -8,16 +8,14 @@ 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-16 20:01+0000\n" +"PO-Revision-Date: 2024-09-24 08:01+0000\n" "Last-Translator: Ihor Hordiichuk \n" -"Language-Team: Ukrainian \n" +"Language-Team: Ukrainian \n" "Language: uk\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" +"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 @@ -26,7 +24,7 @@ msgstr "Перейти до..." #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "Термінал" +msgstr "" #: src/menu/menu.c:1040 msgid "Reconfigure" diff --git a/po/vi.po b/po/vi.po deleted file mode 100644 index bdef57cd..00000000 --- a/po/vi.po +++ /dev/null @@ -1,80 +0,0 @@ -# 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: 2025-10-25 04:27+0000\n" -"Last-Translator: zenfas \n" -"Language-Team: Vietnamese \n" -"Language: vi\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 4.2.1\n" - -#: src/menu/menu.c:1016 -msgid "Go there..." -msgstr "Chuyển đến đó…" - -#: src/menu/menu.c:1034 -msgid "Terminal" -msgstr "Cửa sổ dòng lệnh" - -#: src/menu/menu.c:1040 -msgid "Reconfigure" -msgstr "Cấu hình lại" - -#: src/menu/menu.c:1042 -msgid "Exit" -msgstr "Thoát" - -#: src/menu/menu.c:1056 -msgid "Minimize" -msgstr "Thu nhỏ" - -#: src/menu/menu.c:1058 -msgid "Maximize" -msgstr "Phóng to" - -#: src/menu/menu.c:1060 -msgid "Fullscreen" -msgstr "Toàn màn hình" - -#: src/menu/menu.c:1062 -msgid "Roll Up/Down" -msgstr "Cuộn lên/xuống" - -#: src/menu/menu.c:1064 -msgid "Decorations" -msgstr "Trang trí" - -#: src/menu/menu.c:1066 -msgid "Always on Top" -msgstr "Luôn ở trên cùng" - -#: src/menu/menu.c:1071 -msgid "Move Left" -msgstr "Chuyển sang trái" - -#: src/menu/menu.c:1078 -msgid "Move Right" -msgstr "Chuyển sang phải" - -#: src/menu/menu.c:1083 -msgid "Always on Visible Workspace" -msgstr "Luôn hiển thị trên không gian làm việc hiện tại" - -#: src/menu/menu.c:1086 -msgid "Workspace" -msgstr "Không gian làm việc" - -#: src/menu/menu.c:1089 -msgid "Close" -msgstr "Đóng" diff --git a/po/zh_CN.po b/po/zh_CN.po index 7662cecb..c872c2d6 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -8,10 +8,9 @@ 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-07-15 05:27+0000\n" -"Last-Translator: kmephistoh \n" -"Language-Team: Chinese (Simplified) \n" +"PO-Revision-Date: 2025-04-30 08:01+0000\n" +"Last-Translator: knm100 \n" +"Language-Team: Chinese (Simplified) \n" "Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -25,7 +24,7 @@ msgstr "前往..." #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "终端" +msgstr "" #: src/menu/menu.c:1040 msgid "Reconfigure" diff --git a/po/zh_TW.po b/po/zh_TW.po deleted file mode 100644 index e11c45ab..00000000 --- a/po/zh_TW.po +++ /dev/null @@ -1,80 +0,0 @@ -# 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: 2025-07-30 08:01+0000\n" -"Last-Translator: BigELK176 ≡ \n" -"Language-Team: Chinese (Traditional) \n" -"Language: zh_TW\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=1; plural=0;\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 "終端" - -#: src/menu/menu.c:1040 -msgid "Reconfigure" -msgstr "重新設定" - -#: src/menu/menu.c:1042 -msgid "Exit" -msgstr "退出" - -#: src/menu/menu.c:1056 -msgid "Minimize" -msgstr "最小化" - -#: src/menu/menu.c:1058 -msgid "Maximize" -msgstr "最大化" - -#: src/menu/menu.c:1060 -msgid "Fullscreen" -msgstr "全螢幕" - -#: 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 "關閉" diff --git a/scripts/.gitignore b/scripts/.gitignore index dcd0305c..ed76aa70 100644 --- a/scripts/.gitignore +++ b/scripts/.gitignore @@ -1,3 +1,2 @@ *.o find-banned -rip diff --git a/scripts/check b/scripts/check index 473fcfdc..9e108666 100755 --- a/scripts/check +++ b/scripts/check @@ -19,7 +19,7 @@ run_checks () { return $? fi - find src/ include/ clients/ t/ \( -name "*.c" -o -name "*.h" \) -type f -print0 | + find src/ include/ \( -name "*.c" -o -name "*.h" \) -type f -print0 | nice xargs -0 --max-args 1 --max-procs $(nproc) \ scripts/checkpatch.pl --terse --no-tree --strict --file return $? diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 217536a6..6f835f22 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -5532,7 +5532,6 @@ sub process { if ($starts_with_if_while_etc && !length($s) && $filename ne "include/view.h" && $filename ne "include/common/array.h" - && $filename ne "include/common/xml.h" && $filename ne "include/ssd-internal.h") { CHK("BRACES", "[labwc-custom] open brace { expected after if/while/for/switch - even with single statement blocks"); } @@ -5659,9 +5658,7 @@ sub process { $var !~ /^(?:_?Pango\w+)/ && $var !~ /^(?:xml\w+)/ && $var !~ /^(?:GString|GError|GHashTable)/ && - $var !~ /^(?:__FreeBSD__)/ && $var !~ /^(?:RsvgRectangle|RsvgHandle)/ && - $var !~ /^(?:CMUnitTest)/ && $var !~ /^(?:XKB_KEY_XF86Switch_VT_1)/ && #Ignore SI style variants like nS, mV and dB diff --git a/scripts/rip.c b/scripts/rip.c index 832b87a2..d7600b95 100644 --- a/scripts/rip.c +++ b/scripts/rip.c @@ -65,7 +65,7 @@ int main(int argc, char **argv) *p = '\0'; } /* Do not process the references at the bottom of NEWS.md */ - if (!strncmp(line, "[0.1.0-commits]", 15)) { + if (!strncmp(line, "[0.1.0]", 7)) { break; } process_line(line); diff --git a/src/action.c b/src/action.c index a351160d..a88ce309 100644 --- a/src/action.c +++ b/src/action.c @@ -1,37 +1,29 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "action.h" #include #include #include #include #include -#include -#include #include -#include "action-prompt-codes.h" -#include "common/buf.h" +#include "action.h" #include "common/macros.h" #include "common/list.h" #include "common/mem.h" #include "common/parse-bool.h" #include "common/spawn.h" #include "common/string-helpers.h" -#include "config/rcxml.h" #include "debug.h" -#include "input/keyboard.h" #include "labwc.h" #include "magnifier.h" #include "menu/menu.h" #include "osd.h" -#include "output.h" #include "output-virtual.h" #include "regions.h" #include "ssd.h" -#include "theme.h" -#include "translate.h" #include "view.h" #include "workspaces.h" +#include "input/keyboard.h" enum action_arg_type { LAB_ACTION_ARG_STR = 0, @@ -282,7 +274,7 @@ action_get_arg(struct action *action, const char *key, enum action_arg_type type return NULL; } -const char * +static const char * action_get_str(struct action *action, const char *key, const char *default_value) { struct action_arg_str *arg = action_get_arg(action, key, LAB_ACTION_ARG_STR); @@ -338,15 +330,21 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char } break; case ACTION_TYPE_MOVE_TO_EDGE: + if (!strcasecmp(argument, "snapWindows")) { + action_arg_add_bool(action, argument, parse_bool(content, true)); + goto cleanup; + } + /* Falls through */ case ACTION_TYPE_TOGGLE_SNAP_TO_EDGE: case ACTION_TYPE_SNAP_TO_EDGE: case ACTION_TYPE_GROW_TO_EDGE: case ACTION_TYPE_SHRINK_TO_EDGE: if (!strcmp(argument, "direction")) { - bool tiled = (action->type == ACTION_TYPE_TOGGLE_SNAP_TO_EDGE - || action->type == ACTION_TYPE_SNAP_TO_EDGE); - enum lab_edge edge = lab_edge_parse(content, tiled, /*any*/ false); - if (edge == LAB_EDGE_NONE) { + enum view_edge edge = view_edge_parse(content); + bool allow_center = action->type == ACTION_TYPE_TOGGLE_SNAP_TO_EDGE + || action->type == ACTION_TYPE_SNAP_TO_EDGE; + if ((edge == VIEW_EDGE_CENTER && !allow_center) + || edge == VIEW_EDGE_INVALID) { wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", action_names[action->type], argument, content); } else { @@ -354,17 +352,6 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char } goto cleanup; } - if (action->type == ACTION_TYPE_MOVE_TO_EDGE - && !strcasecmp(argument, "snapWindows")) { - action_arg_add_bool(action, argument, parse_bool(content, true)); - goto cleanup; - } - if ((action->type == ACTION_TYPE_SNAP_TO_EDGE - || action->type == ACTION_TYPE_TOGGLE_SNAP_TO_EDGE) - && !strcasecmp(argument, "combine")) { - action_arg_add_bool(action, argument, parse_bool(content, false)); - goto cleanup; - } break; case ACTION_TYPE_SHOW_MENU: if (!strcmp(argument, "menu")) { @@ -400,7 +387,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char break; case ACTION_TYPE_SET_DECORATIONS: if (!strcmp(argument, "decorations")) { - enum lab_ssd_mode mode = ssd_mode_parse(content); + enum ssd_mode mode = ssd_mode_parse(content); if (mode != LAB_SSD_MODE_INVALID) { action_arg_add_int(action, argument, mode); } else { @@ -449,11 +436,6 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char action_arg_add_bool(action, argument, parse_bool(content, true)); goto cleanup; } - if (!strcmp(argument, "toggle")) { - action_arg_add_bool( - action, argument, parse_bool(content, false)); - goto cleanup; - } break; case ACTION_TYPE_TOGGLE_SNAP_TO_REGION: case ACTION_TYPE_SNAP_TO_REGION: @@ -469,9 +451,8 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char goto cleanup; } if (!strcmp(argument, "direction")) { - enum lab_edge edge = lab_edge_parse(content, - /*tiled*/ false, /*any*/ false); - if (edge == LAB_EDGE_NONE) { + enum view_edge edge = view_edge_parse(content); + if (edge == VIEW_EDGE_CENTER) { wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", action_names[action->type], argument, content); } else { @@ -493,7 +474,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char break; case ACTION_TYPE_AUTO_PLACE: if (!strcmp(argument, "policy")) { - enum lab_placement_policy policy = + enum view_placement_policy policy = view_placement_parse(content); if (policy == LAB_PLACE_INVALID) { wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", @@ -510,11 +491,6 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char goto cleanup; } break; - case ACTION_TYPE_IF: - if (!strcmp(argument, "message.prompt")) { - action_arg_add_str(action, "message.prompt", content); - } - goto cleanup; } wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s'", @@ -716,8 +692,8 @@ show_menu(struct server *server, struct view *view, struct cursor_context *ctx, x = extent.x; y = view->current.y; /* Push the client menu underneath the button */ - if (is_client_menu && node_type_contains( - LAB_NODE_BUTTON, ctx->type)) { + if (is_client_menu && ssd_part_contains( + LAB_SSD_BUTTON, ctx->type)) { assert(ctx->node); int lx, ly; wlr_scene_node_coords(ctx->node, &lx, &ly); @@ -797,180 +773,30 @@ view_for_action(struct view *activator, struct server *server, } } -struct action_prompt { - /* Set when created */ - struct server *server; - struct action *action; - struct view *view; - - /* Set when executed */ - pid_t pid; - - struct { - struct wl_listener destroy; - } on_view; - struct wl_list link; -}; - -static struct wl_list prompts = WL_LIST_INIT(&prompts); - -static void -action_prompt_destroy(struct action_prompt *prompt) -{ - wl_list_remove(&prompt->on_view.destroy.link); - wl_list_remove(&prompt->link); - free(prompt); -} - -static void -handle_view_destroy(struct wl_listener *listener, void *data) -{ - struct action_prompt *prompt = wl_container_of(listener, prompt, on_view.destroy); - wl_list_remove(&prompt->on_view.destroy.link); - wl_list_init(&prompt->on_view.destroy.link); - prompt->view = NULL; -} - -static void -print_prompt_command(struct buf *buf, const char *format, - struct action *action, struct theme *theme) -{ - assert(format); - - for (const char *p = format; *p; p++) { - /* - * If we're not on a conversion specifier (like %m) then just - * keep adding it to the buffer - */ - if (*p != '%') { - buf_add_char(buf, *p); - continue; - } - - /* Process the %* conversion specifier */ - ++p; - - switch (*p) { - case 'm': - buf_add(buf, action_get_str(action, - "message.prompt", "Choose wisely")); - break; - case 'n': - buf_add(buf, _("No")); - break; - case 'y': - buf_add(buf, _("Yes")); - break; - case 'b': - buf_add_hex_color(buf, theme->osd_bg_color); - break; - case 't': - buf_add_hex_color(buf, theme->osd_label_text_color); - break; - default: - wlr_log(WLR_ERROR, - "invalid prompt command conversion specifier '%c'", *p); - break; - } - } -} - -static void -action_prompt_create(struct view *view, struct server *server, struct action *action) -{ - struct buf command = BUF_INIT; - print_prompt_command(&command, rc.prompt_command, action, rc.theme); - - wlr_log(WLR_INFO, "prompt command: '%s'", command.data); - - int pipe_fd; - pid_t prompt_pid = spawn_piped(command.data, &pipe_fd); - if (prompt_pid < 0) { - wlr_log(WLR_ERROR, "Failed to create action prompt"); - goto cleanup; - } - /* FIXME: closing stdout might confuse clients */ - close(pipe_fd); - - struct action_prompt *prompt = znew(*prompt); - prompt->server = server; - prompt->action = action; - prompt->view = view; - prompt->pid = prompt_pid; - if (view) { - prompt->on_view.destroy.notify = handle_view_destroy; - wl_signal_add(&view->events.destroy, &prompt->on_view.destroy); - } else { - /* Allows removing during destroy */ - wl_list_init(&prompt->on_view.destroy.link); - } - - wl_list_insert(&prompts, &prompt->link); - -cleanup: - buf_reset(&command); -} - -void -action_prompts_destroy(void) -{ - struct action_prompt *prompt, *tmp; - wl_list_for_each_safe(prompt, tmp, &prompts, link) { - action_prompt_destroy(prompt); - } -} - -bool -action_check_prompt_result(pid_t pid, int exit_code) -{ - struct action_prompt *prompt, *tmp; - wl_list_for_each_safe(prompt, tmp, &prompts, link) { - if (prompt->pid != pid) { - continue; - } - - wlr_log(WLR_INFO, "Found pending prompt for exit code %d", exit_code); - struct wl_list *actions = NULL; - if (exit_code == LAB_EXIT_SUCCESS) { - wlr_log(WLR_INFO, "Selected the 'then' branch"); - actions = action_get_actionlist(prompt->action, "then"); - } else if (exit_code == LAB_EXIT_CANCELLED) { - /* no-op */ - } else { - wlr_log(WLR_INFO, "Selected the 'else' branch"); - actions = action_get_actionlist(prompt->action, "else"); - } - if (actions) { - wlr_log(WLR_INFO, "Running actions"); - actions_run(prompt->view, prompt->server, - actions, /*cursor_ctx*/ NULL); - } else { - wlr_log(WLR_INFO, "No actions for selected branch"); - } - action_prompt_destroy(prompt); - return true; - } - return false; -} - static bool -match_queries(struct view *view, struct action *action) +run_if_action(struct view *view, struct server *server, struct action *action) { - assert(view); - - struct wl_list *queries = action_get_querylist(action, "query"); - if (!queries) { - return true; - } - - /* All queries are OR'ed */ struct view_query *query; - wl_list_for_each(query, queries, link) { - if (view_matches_query(view, query)) { - return true; + struct wl_list *queries, *actions; + const char *branch = "then"; + + queries = action_get_querylist(action, "query"); + if (queries) { + branch = "else"; + /* All queries are OR'ed */ + wl_list_for_each(query, queries, link) { + if (view_matches_query(view, query)) { + branch = "then"; + break; + } } } - return false; + + actions = action_get_actionlist(action, branch); + if (actions) { + actions_run(view, server, actions, NULL); + } + return !strcmp(branch, "then"); } static struct output * @@ -983,8 +809,8 @@ get_target_output(struct output *output, struct server *server, if (output_name) { target = output_from_name(server, output_name); } else { - enum lab_edge edge = - action_get_int(action, "direction", LAB_EDGE_NONE); + enum view_edge edge = + action_get_int(action, "direction", VIEW_EDGE_INVALID); bool wrap = action_get_bool(action, "wrap", false); target = output_get_adjacent(output, edge, wrap); } @@ -997,9 +823,8 @@ 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, struct output *output, const char *to, const char *x, const char *y) { - struct output *output = output_nearest_to_cursor(server); struct wlr_box target_area = {0}; int goto_x; int goto_y; @@ -1030,524 +855,8 @@ 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); -} - -static void -run_action(struct view *view, struct server *server, struct action *action, - struct cursor_context *ctx) -{ - switch (action->type) { - case ACTION_TYPE_CLOSE: - if (view) { - view_close(view); - } - break; - case ACTION_TYPE_KILL: - if (view) { - /* Send SIGTERM to the process associated with the surface */ - assert(view->impl->get_pid); - pid_t pid = view->impl->get_pid(view); - if (pid == getpid()) { - wlr_log(WLR_ERROR, "Preventing sending SIGTERM to labwc"); - } else if (pid > 0) { - kill(pid, SIGTERM); - } - } - break; - case ACTION_TYPE_DEBUG: - debug_dump_scene(server); - break; - case ACTION_TYPE_EXECUTE: { - struct buf cmd = BUF_INIT; - buf_add(&cmd, action_get_str(action, "command", NULL)); - buf_expand_tilde(&cmd); - spawn_async_no_shell(cmd.data); - buf_reset(&cmd); - break; - } - case ACTION_TYPE_EXIT: - wl_display_terminate(server->wl_display); - break; - case ACTION_TYPE_MOVE_TO_EDGE: - if (view) { - /* Config parsing makes sure that direction is a valid direction */ - enum lab_edge edge = action_get_int(action, "direction", 0); - bool snap_to_windows = action_get_bool(action, "snapWindows", true); - view_move_to_edge(view, edge, snap_to_windows); - } - break; - case ACTION_TYPE_TOGGLE_SNAP_TO_EDGE: - case ACTION_TYPE_SNAP_TO_EDGE: - if (view) { - /* Config parsing makes sure that direction is a valid direction */ - enum lab_edge edge = action_get_int(action, "direction", 0); - if (action->type == ACTION_TYPE_TOGGLE_SNAP_TO_EDGE - && view->maximized == VIEW_AXIS_NONE - && !view->fullscreen - && view_is_tiled(view) - && view->tiled == edge) { - view_set_untiled(view); - view_apply_natural_geometry(view); - break; - } - bool combine = action_get_bool(action, "combine", false); - view_snap_to_edge(view, edge, /*across_outputs*/ true, - combine, /*store_natural_geometry*/ true); - } - break; - case ACTION_TYPE_GROW_TO_EDGE: - if (view) { - /* Config parsing makes sure that direction is a valid direction */ - enum lab_edge edge = action_get_int(action, "direction", 0); - view_grow_to_edge(view, edge); - } - break; - case ACTION_TYPE_SHRINK_TO_EDGE: - if (view) { - /* Config parsing makes sure that direction is a valid direction */ - enum lab_edge edge = action_get_int(action, "direction", 0); - view_shrink_to_edge(view, edge); - } - break; - case ACTION_TYPE_NEXT_WINDOW: - if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) { - osd_cycle(server, LAB_CYCLE_DIR_FORWARD); - } else { - osd_begin(server, LAB_CYCLE_DIR_FORWARD); - } - break; - case ACTION_TYPE_PREVIOUS_WINDOW: - if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) { - osd_cycle(server, LAB_CYCLE_DIR_BACKWARD); - } else { - osd_begin(server, LAB_CYCLE_DIR_BACKWARD); - } - break; - case ACTION_TYPE_RECONFIGURE: - kill(getpid(), SIGHUP); - break; - case ACTION_TYPE_SHOW_MENU: - show_menu(server, view, ctx, - action_get_str(action, "menu", NULL), - action_get_bool(action, "atCursor", true), - action_get_str(action, "x.position", NULL), - action_get_str(action, "y.position", NULL)); - break; - case ACTION_TYPE_TOGGLE_MAXIMIZE: - if (view) { - enum view_axis axis = action_get_int(action, - "direction", VIEW_AXIS_BOTH); - view_toggle_maximize(view, axis); - } - break; - case ACTION_TYPE_MAXIMIZE: - if (view) { - enum view_axis axis = action_get_int(action, - "direction", VIEW_AXIS_BOTH); - view_maximize(view, axis, - /*store_natural_geometry*/ true); - } - break; - case ACTION_TYPE_UNMAXIMIZE: - if (view) { - enum view_axis axis = action_get_int(action, - "direction", VIEW_AXIS_BOTH); - view_maximize(view, view->maximized & ~axis, - /*store_natural_geometry*/ true); - } - break; - case ACTION_TYPE_TOGGLE_FULLSCREEN: - if (view) { - view_toggle_fullscreen(view); - } - break; - case ACTION_TYPE_SET_DECORATIONS: - if (view) { - enum lab_ssd_mode mode = action_get_int(action, - "decorations", LAB_SSD_MODE_FULL); - bool force_ssd = action_get_bool(action, - "forceSSD", false); - view_set_decorations(view, mode, force_ssd); - } - break; - case ACTION_TYPE_TOGGLE_DECORATIONS: - if (view) { - view_toggle_decorations(view); - } - break; - case ACTION_TYPE_TOGGLE_ALWAYS_ON_TOP: - if (view) { - view_toggle_always_on_top(view); - } - break; - case ACTION_TYPE_TOGGLE_ALWAYS_ON_BOTTOM: - if (view) { - view_toggle_always_on_bottom(view); - } - break; - case ACTION_TYPE_TOGGLE_OMNIPRESENT: - if (view) { - view_toggle_visible_on_all_workspaces(view); - } - break; - case ACTION_TYPE_FOCUS: - if (view) { - desktop_focus_view(view, /*raise*/ false); - } - break; - case ACTION_TYPE_UNFOCUS: - seat_focus_surface(&server->seat, NULL); - break; - case ACTION_TYPE_ICONIFY: - if (view) { - view_minimize(view, true); - } - break; - case ACTION_TYPE_MOVE: - if (view) { - interactive_begin(view, LAB_INPUT_STATE_MOVE, - LAB_EDGE_NONE); - } - break; - case ACTION_TYPE_RAISE: - if (view) { - view_move_to_front(view); - } - break; - case ACTION_TYPE_LOWER: - if (view) { - view_move_to_back(view); - } - break; - case ACTION_TYPE_RESIZE: - if (view) { - enum lab_edge resize_edges = cursor_get_resize_edges( - server->seat.cursor, ctx); - interactive_begin(view, LAB_INPUT_STATE_RESIZE, - resize_edges); - } - break; - case ACTION_TYPE_RESIZE_RELATIVE: - if (view) { - int left = action_get_int(action, "left", 0); - int right = action_get_int(action, "right", 0); - int top = action_get_int(action, "top", 0); - int bottom = action_get_int(action, "bottom", 0); - view_resize_relative(view, left, right, top, bottom); - } - break; - case ACTION_TYPE_MOVETO: - if (view) { - int x = action_get_int(action, "x", 0); - int y = action_get_int(action, "y", 0); - struct border margin = ssd_thickness(view); - view_move(view, x + margin.left, y + margin.top); - } - break; - case ACTION_TYPE_RESIZETO: - if (view) { - int width = action_get_int(action, "width", 0); - int height = action_get_int(action, "height", 0); - - /* - * To support only setting one of width/height - * in - * we fall back to current dimension when unset. - */ - struct wlr_box box = { - .x = view->pending.x, - .y = view->pending.y, - .width = width ? : view->pending.width, - .height = height ? : view->pending.height, - }; - view_set_shade(view, false); - view_move_resize(view, box); - } - break; - case ACTION_TYPE_MOVE_RELATIVE: - if (view) { - int x = action_get_int(action, "x", 0); - int y = action_get_int(action, "y", 0); - view_move_relative(view, x, y); - } - break; - case ACTION_TYPE_MOVETO_CURSOR: - wlr_log(WLR_ERROR, - "Action MoveToCursor is deprecated. To ensure your config works in future labwc " - "releases, please use "); - if (view) { - view_move_to_cursor(view); - } - break; - case ACTION_TYPE_SEND_TO_DESKTOP: - if (!view) { - break; - } - /* Falls through to GoToDesktop */ - case ACTION_TYPE_GO_TO_DESKTOP: { - bool follow = true; - bool wrap = action_get_bool(action, "wrap", true); - const char *to = action_get_str(action, "to", NULL); - /* - * `to` is always != NULL here because otherwise we would have - * removed the action during the initial parsing step as it is - * a required argument for both SendToDesktop and GoToDesktop. - */ - struct workspace *target_workspace = workspaces_find( - 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 - && toggle) { - target_workspace = server->workspaces.last; - } - } - if (!target_workspace) { - break; - } - if (action->type == ACTION_TYPE_SEND_TO_DESKTOP) { - view_move_to_workspace(view, target_workspace); - 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) { - workspaces_switch_to(target_workspace, - /*update_focus*/ true); - } - break; - } - case ACTION_TYPE_MOVE_TO_OUTPUT: { - if (!view) { - break; - } - struct output *target_output = - get_target_output(view->output, server, action); - if (target_output) { - view_move_to_output(view, target_output); - } - break; - } - case ACTION_TYPE_FIT_TO_OUTPUT: - if (!view) { - break; - } - view_constrain_size_to_that_of_usable_area(view); - break; - case ACTION_TYPE_TOGGLE_SNAP_TO_REGION: - case ACTION_TYPE_SNAP_TO_REGION: { - if (!view) { - break; - } - struct output *output = view->output; - if (!output) { - break; - } - const char *region_name = action_get_str(action, "region", NULL); - struct region *region = regions_from_name(region_name, output); - if (region) { - if (action->type == ACTION_TYPE_TOGGLE_SNAP_TO_REGION - && view->maximized == VIEW_AXIS_NONE - && !view->fullscreen - && view_is_tiled(view) - && view->tiled_region == region) { - view_set_untiled(view); - view_apply_natural_geometry(view); - break; - } - view_snap_to_region(view, region, - /*store_natural_geometry*/ true); - } else { - wlr_log(WLR_ERROR, "Invalid SnapToRegion id: '%s'", region_name); - } - break; - } - case ACTION_TYPE_UNSNAP: - if (view && !view->fullscreen && !view_is_floating(view)) { - view_maximize(view, VIEW_AXIS_NONE, - /* store_natural_geometry */ false); - view_set_untiled(view); - view_apply_natural_geometry(view); - } - break; - case ACTION_TYPE_TOGGLE_KEYBINDS: - if (view) { - view_toggle_keybinds(view); - } - break; - case ACTION_TYPE_FOCUS_OUTPUT: { - struct output *output = output_nearest_to_cursor(server); - struct output *target_output = - get_target_output(output, server, action); - if (target_output) { - desktop_focus_output(target_output); - } - break; - } - case ACTION_TYPE_IF: { - /* At least one of the queries was matched or there was no query */ - if (action_get_str(action, "message.prompt", NULL)) { - /* - * We delay the selection and execution of the - * branch until we get a response from the user. - */ - action_prompt_create(view, server, action); - } else if (view) { - struct wl_list *actions; - if (match_queries(view, action)) { - actions = action_get_actionlist(action, "then"); - } else { - actions = action_get_actionlist(action, "else"); - } - if (actions) { - actions_run(view, server, actions, ctx); - } - } - break; - } - case ACTION_TYPE_FOR_EACH: { - struct wl_list *actions = NULL; - bool matches = false; - - struct wl_array views; - wl_array_init(&views); - view_array_append(server, &views, LAB_VIEW_CRITERIA_NONE); - - struct view **item; - wl_array_for_each(item, &views) { - if (match_queries(*item, action)) { - matches = true; - actions = action_get_actionlist(action, "then"); - } else { - actions = action_get_actionlist(action, "else"); - } - if (actions) { - actions_run(*item, server, actions, ctx); - } - } - wl_array_release(&views); - if (!matches) { - actions = action_get_actionlist(action, "none"); - if (actions) { - actions_run(view, server, actions, NULL); - } - } - break; - } - case ACTION_TYPE_VIRTUAL_OUTPUT_ADD: { - /* TODO: rename this argument to "outputName" */ - const char *output_name = - action_get_str(action, "output_name", NULL); - output_virtual_add(server, output_name, - /*store_wlr_output*/ NULL); - break; - } - case ACTION_TYPE_VIRTUAL_OUTPUT_REMOVE: { - /* TODO: rename this argument to "outputName" */ - const char *output_name = - action_get_str(action, "output_name", NULL); - output_virtual_remove(server, output_name); - break; - } - case ACTION_TYPE_AUTO_PLACE: - if (view) { - enum lab_placement_policy policy = - action_get_int(action, "policy", LAB_PLACE_AUTOMATIC); - view_place_by_policy(view, - /* allow_cursor */ true, policy); - } - break; - case ACTION_TYPE_TOGGLE_TEARING: - if (view) { - switch (view->force_tearing) { - case LAB_STATE_UNSPECIFIED: - view->force_tearing = - output_get_tearing_allowance(view->output) - ? LAB_STATE_DISABLED : LAB_STATE_ENABLED; - break; - case LAB_STATE_DISABLED: - view->force_tearing = LAB_STATE_ENABLED; - break; - case LAB_STATE_ENABLED: - view->force_tearing = LAB_STATE_DISABLED; - break; - } - wlr_log(WLR_ERROR, "force tearing %sabled", - view->force_tearing == LAB_STATE_ENABLED - ? "en" : "dis"); - } - break; - case ACTION_TYPE_TOGGLE_SHADE: - if (view) { - view_set_shade(view, !view->shaded); - } - break; - case ACTION_TYPE_SHADE: - if (view) { - view_set_shade(view, true); - } - break; - case ACTION_TYPE_UNSHADE: - if (view) { - view_set_shade(view, false); - } - break; - case ACTION_TYPE_ENABLE_SCROLL_WHEEL_EMULATION: - server->seat.cursor_scroll_wheel_emulation = true; - break; - case ACTION_TYPE_DISABLE_SCROLL_WHEEL_EMULATION: - 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; - break; - case ACTION_TYPE_ENABLE_TABLET_MOUSE_EMULATION: - rc.tablet.force_mouse_emulation = true; - break; - case ACTION_TYPE_DISABLE_TABLET_MOUSE_EMULATION: - rc.tablet.force_mouse_emulation = false; - break; - case ACTION_TYPE_TOGGLE_TABLET_MOUSE_EMULATION: - rc.tablet.force_mouse_emulation = !rc.tablet.force_mouse_emulation; - break; - case ACTION_TYPE_TOGGLE_MAGNIFY: - magnifier_toggle(server); - break; - case ACTION_TYPE_ZOOM_IN: - magnifier_set_scale(server, MAGNIFY_INCREASE); - break; - case ACTION_TYPE_ZOOM_OUT: - magnifier_set_scale(server, MAGNIFY_DECREASE); - 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); - break; - } - case ACTION_TYPE_HIDE_CURSOR: - cursor_set_visible(&server->seat, false); - break; - case ACTION_TYPE_INVALID: - wlr_log(WLR_ERROR, "Not executing unknown action"); - break; - default: - /* - * If we get here it must be a BUG caused most likely by - * action_names and action_type being out of sync or by - * adding a new action without installing a handler here. - */ - wlr_log(WLR_ERROR, - "Not executing invalid action (%u)" - " This is a BUG. Please report.", action->type); - } + wlr_cursor_warp(output->server->seat.cursor, NULL, goto_x, goto_y); + cursor_update_focus(output->server); } void @@ -1586,6 +895,483 @@ actions_run(struct view *activator, struct server *server, */ struct view *view = view_for_action(activator, server, action, &ctx); - run_action(view, server, action, &ctx); + switch (action->type) { + case ACTION_TYPE_CLOSE: + if (view) { + view_close(view); + } + break; + case ACTION_TYPE_KILL: + if (view) { + /* Send SIGTERM to the process associated with the surface */ + assert(view->impl->get_pid); + pid_t pid = view->impl->get_pid(view); + if (pid == getpid()) { + wlr_log(WLR_ERROR, "Preventing sending SIGTERM to labwc"); + } else if (pid > 0) { + kill(pid, SIGTERM); + } + } + break; + case ACTION_TYPE_DEBUG: + debug_dump_scene(server); + break; + case ACTION_TYPE_EXECUTE: { + struct buf cmd = BUF_INIT; + buf_add(&cmd, action_get_str(action, "command", NULL)); + buf_expand_tilde(&cmd); + spawn_async_no_shell(cmd.data); + buf_reset(&cmd); + break; + } + case ACTION_TYPE_EXIT: + wl_display_terminate(server->wl_display); + break; + case ACTION_TYPE_MOVE_TO_EDGE: + if (view) { + /* Config parsing makes sure that direction is a valid direction */ + enum view_edge edge = action_get_int(action, "direction", 0); + bool snap_to_windows = action_get_bool(action, "snapWindows", true); + view_move_to_edge(view, edge, snap_to_windows); + } + break; + case ACTION_TYPE_TOGGLE_SNAP_TO_EDGE: + case ACTION_TYPE_SNAP_TO_EDGE: + if (view) { + /* Config parsing makes sure that direction is a valid direction */ + enum view_edge edge = action_get_int(action, "direction", 0); + if (action->type == ACTION_TYPE_TOGGLE_SNAP_TO_EDGE + && view->maximized == VIEW_AXIS_NONE + && !view->fullscreen + && view_is_tiled(view) + && view->tiled == edge) { + view_set_untiled(view); + view_apply_natural_geometry(view); + break; + } + view_snap_to_edge(view, edge, + /*across_outputs*/ true, + /*store_natural_geometry*/ true); + } + break; + case ACTION_TYPE_GROW_TO_EDGE: + if (view) { + /* Config parsing makes sure that direction is a valid direction */ + enum view_edge edge = action_get_int(action, "direction", 0); + view_grow_to_edge(view, edge); + } + break; + case ACTION_TYPE_SHRINK_TO_EDGE: + if (view) { + /* Config parsing makes sure that direction is a valid direction */ + enum view_edge edge = action_get_int(action, "direction", 0); + view_shrink_to_edge(view, edge); + } + break; + case ACTION_TYPE_NEXT_WINDOW: + if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) { + osd_cycle(server, LAB_CYCLE_DIR_FORWARD); + } else { + osd_begin(server, LAB_CYCLE_DIR_FORWARD); + } + break; + case ACTION_TYPE_PREVIOUS_WINDOW: + if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) { + osd_cycle(server, LAB_CYCLE_DIR_BACKWARD); + } else { + osd_begin(server, LAB_CYCLE_DIR_BACKWARD); + } + break; + case ACTION_TYPE_RECONFIGURE: + kill(getpid(), SIGHUP); + break; + case ACTION_TYPE_SHOW_MENU: + show_menu(server, view, &ctx, + action_get_str(action, "menu", NULL), + action_get_bool(action, "atCursor", true), + action_get_str(action, "x.position", NULL), + action_get_str(action, "y.position", NULL)); + break; + case ACTION_TYPE_TOGGLE_MAXIMIZE: + if (view) { + enum view_axis axis = action_get_int(action, + "direction", VIEW_AXIS_BOTH); + view_toggle_maximize(view, axis); + } + break; + case ACTION_TYPE_MAXIMIZE: + if (view) { + enum view_axis axis = action_get_int(action, + "direction", VIEW_AXIS_BOTH); + view_maximize(view, axis, + /*store_natural_geometry*/ true); + } + break; + case ACTION_TYPE_UNMAXIMIZE: + if (view) { + enum view_axis axis = action_get_int(action, + "direction", VIEW_AXIS_BOTH); + view_maximize(view, view->maximized & ~axis, + /*store_natural_geometry*/ true); + } + break; + case ACTION_TYPE_TOGGLE_FULLSCREEN: + if (view) { + view_toggle_fullscreen(view); + } + break; + case ACTION_TYPE_SET_DECORATIONS: + if (view) { + enum ssd_mode mode = action_get_int(action, + "decorations", LAB_SSD_MODE_FULL); + bool force_ssd = action_get_bool(action, + "forceSSD", false); + view_set_decorations(view, mode, force_ssd); + } + break; + case ACTION_TYPE_TOGGLE_DECORATIONS: + if (view) { + view_toggle_decorations(view); + } + break; + case ACTION_TYPE_TOGGLE_ALWAYS_ON_TOP: + if (view) { + view_toggle_always_on_top(view); + } + break; + case ACTION_TYPE_TOGGLE_ALWAYS_ON_BOTTOM: + if (view) { + view_toggle_always_on_bottom(view); + } + break; + case ACTION_TYPE_TOGGLE_OMNIPRESENT: + if (view) { + view_toggle_visible_on_all_workspaces(view); + } + break; + case ACTION_TYPE_FOCUS: + if (view) { + desktop_focus_view(view, /*raise*/ false); + } + break; + case ACTION_TYPE_UNFOCUS: + seat_focus_surface(&server->seat, NULL); + break; + case ACTION_TYPE_ICONIFY: + if (view) { + view_minimize(view, true); + } + break; + case ACTION_TYPE_MOVE: + if (view) { + interactive_begin(view, LAB_INPUT_STATE_MOVE, 0); + } + break; + case ACTION_TYPE_RAISE: + if (view) { + view_move_to_front(view); + } + break; + case ACTION_TYPE_LOWER: + if (view) { + view_move_to_back(view); + } + break; + case ACTION_TYPE_RESIZE: + if (view) { + uint32_t resize_edges = cursor_get_resize_edges( + server->seat.cursor, &ctx); + interactive_begin(view, LAB_INPUT_STATE_RESIZE, + resize_edges); + } + break; + case ACTION_TYPE_RESIZE_RELATIVE: + if (view) { + int left = action_get_int(action, "left", 0); + int right = action_get_int(action, "right", 0); + int top = action_get_int(action, "top", 0); + int bottom = action_get_int(action, "bottom", 0); + view_resize_relative(view, left, right, top, bottom); + } + break; + case ACTION_TYPE_MOVETO: + if (view) { + int x = action_get_int(action, "x", 0); + int y = action_get_int(action, "y", 0); + struct border margin = ssd_thickness(view); + view_move(view, x + margin.left, y + margin.top); + } + break; + case ACTION_TYPE_RESIZETO: + if (view) { + int width = action_get_int(action, "width", 0); + int height = action_get_int(action, "height", 0); + + /* + * To support only setting one of width/height + * in + * we fall back to current dimension when unset. + */ + struct wlr_box box = { + .x = view->pending.x, + .y = view->pending.y, + .width = width ? : view->pending.width, + .height = height ? : view->pending.height, + }; + view_set_shade(view, false); + view_move_resize(view, box); + } + break; + case ACTION_TYPE_MOVE_RELATIVE: + if (view) { + int x = action_get_int(action, "x", 0); + int y = action_get_int(action, "y", 0); + view_move_relative(view, x, y); + } + break; + case ACTION_TYPE_MOVETO_CURSOR: + wlr_log(WLR_ERROR, + "Action MoveToCursor is deprecated. To ensure your config works in future labwc " + "releases, please use "); + if (view) { + view_move_to_cursor(view); + } + break; + case ACTION_TYPE_SEND_TO_DESKTOP: + if (!view) { + break; + } + /* Falls through to GoToDesktop */ + case ACTION_TYPE_GO_TO_DESKTOP: { + bool follow = true; + bool wrap = action_get_bool(action, "wrap", true); + const char *to = action_get_str(action, "to", NULL); + /* + * `to` is always != NULL here because otherwise we would have + * removed the action during the initial parsing step as it is + * a required argument for both SendToDesktop and GoToDesktop. + */ + struct workspace *target_workspace = workspaces_find( + server->workspaces.current, to, wrap); + if (!target_workspace) { + break; + } + if (action->type == ACTION_TYPE_SEND_TO_DESKTOP) { + view_move_to_workspace(view, target_workspace); + 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) { + workspaces_switch_to(target_workspace, + /*update_focus*/ true); + } + break; + } + case ACTION_TYPE_MOVE_TO_OUTPUT: { + if (!view) { + break; + } + struct output *target_output = + get_target_output(view->output, server, action); + if (target_output) { + view_move_to_output(view, target_output); + } + break; + } + case ACTION_TYPE_FIT_TO_OUTPUT: + if (!view) { + break; + } + view_constrain_size_to_that_of_usable_area(view); + break; + case ACTION_TYPE_TOGGLE_SNAP_TO_REGION: + case ACTION_TYPE_SNAP_TO_REGION: { + if (!view) { + break; + } + struct output *output = view->output; + if (!output) { + break; + } + const char *region_name = action_get_str(action, "region", NULL); + struct region *region = regions_from_name(region_name, output); + if (region) { + if (action->type == ACTION_TYPE_TOGGLE_SNAP_TO_REGION + && view->maximized == VIEW_AXIS_NONE + && !view->fullscreen + && view_is_tiled(view) + && view->tiled_region == region) { + view_set_untiled(view); + view_apply_natural_geometry(view); + break; + } + view_snap_to_region(view, region, + /*store_natural_geometry*/ true); + } else { + wlr_log(WLR_ERROR, "Invalid SnapToRegion id: '%s'", region_name); + } + break; + } + case ACTION_TYPE_UNSNAP: + if (view && !view->fullscreen && !view_is_floating(view)) { + view_maximize(view, VIEW_AXIS_NONE, + /* store_natural_geometry */ false); + view_set_untiled(view); + view_apply_natural_geometry(view); + } + break; + case ACTION_TYPE_TOGGLE_KEYBINDS: + if (view) { + view_toggle_keybinds(view); + } + break; + case ACTION_TYPE_FOCUS_OUTPUT: { + struct output *output = output_nearest_to_cursor(server); + struct output *target_output = + get_target_output(output, server, action); + if (target_output) { + desktop_focus_output(target_output); + } + break; + } + case ACTION_TYPE_IF: + if (view) { + run_if_action(view, server, action); + } + break; + case ACTION_TYPE_FOR_EACH: { + struct wl_array views; + struct view **item; + bool matches = false; + wl_array_init(&views); + view_array_append(server, &views, LAB_VIEW_CRITERIA_NONE); + wl_array_for_each(item, &views) { + matches |= run_if_action(*item, server, action); + } + wl_array_release(&views); + if (!matches) { + struct wl_list *child_actions; + child_actions = action_get_actionlist(action, "none"); + if (child_actions) { + actions_run(view, server, child_actions, NULL); + } + } + break; + } + case ACTION_TYPE_VIRTUAL_OUTPUT_ADD: { + /* TODO: rename this argument to "outputName" */ + const char *output_name = + action_get_str(action, "output_name", NULL); + output_virtual_add(server, output_name, + /*store_wlr_output*/ NULL); + break; + } + case ACTION_TYPE_VIRTUAL_OUTPUT_REMOVE: { + /* TODO: rename this argument to "outputName" */ + const char *output_name = + action_get_str(action, "output_name", NULL); + output_virtual_remove(server, output_name); + break; + } + case ACTION_TYPE_AUTO_PLACE: + if (view) { + enum view_placement_policy policy = + action_get_int(action, "policy", LAB_PLACE_AUTOMATIC); + view_place_by_policy(view, + /* allow_cursor */ true, policy); + } + break; + case ACTION_TYPE_TOGGLE_TEARING: + if (view) { + switch (view->force_tearing) { + case LAB_STATE_UNSPECIFIED: + view->force_tearing = + output_get_tearing_allowance(view->output) + ? LAB_STATE_DISABLED : LAB_STATE_ENABLED; + break; + case LAB_STATE_DISABLED: + view->force_tearing = LAB_STATE_ENABLED; + break; + case LAB_STATE_ENABLED: + view->force_tearing = LAB_STATE_DISABLED; + break; + } + wlr_log(WLR_ERROR, "force tearing %sabled", + view->force_tearing == LAB_STATE_ENABLED + ? "en" : "dis"); + } + break; + case ACTION_TYPE_TOGGLE_SHADE: + if (view) { + view_set_shade(view, !view->shaded); + } + break; + case ACTION_TYPE_SHADE: + if (view) { + view_set_shade(view, true); + } + break; + case ACTION_TYPE_UNSHADE: + if (view) { + view_set_shade(view, false); + } + break; + case ACTION_TYPE_ENABLE_SCROLL_WHEEL_EMULATION: + server->seat.cursor_scroll_wheel_emulation = true; + break; + case ACTION_TYPE_DISABLE_SCROLL_WHEEL_EMULATION: + 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; + break; + case ACTION_TYPE_ENABLE_TABLET_MOUSE_EMULATION: + rc.tablet.force_mouse_emulation = true; + break; + case ACTION_TYPE_DISABLE_TABLET_MOUSE_EMULATION: + rc.tablet.force_mouse_emulation = false; + break; + case ACTION_TYPE_TOGGLE_TABLET_MOUSE_EMULATION: + rc.tablet.force_mouse_emulation = !rc.tablet.force_mouse_emulation; + break; + case ACTION_TYPE_TOGGLE_MAGNIFY: + magnifier_toggle(server); + break; + case ACTION_TYPE_ZOOM_IN: + magnifier_set_scale(server, MAGNIFY_INCREASE); + break; + case ACTION_TYPE_ZOOM_OUT: + magnifier_set_scale(server, MAGNIFY_DECREASE); + 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"); + struct output *output = output_nearest_to_cursor(server); + + warp_cursor(view, output, to, x, y); + break; + } + case ACTION_TYPE_HIDE_CURSOR: + cursor_set_visible(&server->seat, false); + break; + case ACTION_TYPE_INVALID: + wlr_log(WLR_ERROR, "Not executing unknown action"); + break; + default: + /* + * If we get here it must be a BUG caused most likely by + * action_names and action_type being out of sync or by + * adding a new action without installing a handler here. + */ + wlr_log(WLR_ERROR, + "Not executing invalid action (%u)" + " This is a BUG. Please report.", action->type); + } } } diff --git a/src/buffer.c b/src/buffer.c index 04ca4328..1faa4620 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -24,17 +24,23 @@ * SOFTWARE. */ -#include "buffer.h" #include #include #include #include #include +#include "buffer.h" #include "common/box.h" #include "common/mem.h" -static struct lab_data_buffer *data_buffer_from_buffer( - struct wlr_buffer *buffer); +static const struct wlr_buffer_impl data_buffer_impl; + +static struct lab_data_buffer * +data_buffer_from_buffer(struct wlr_buffer *buffer) +{ + assert(buffer->impl == &data_buffer_impl); + return (struct lab_data_buffer *)buffer; +} static void data_buffer_destroy(struct wlr_buffer *wlr_buffer) @@ -74,13 +80,6 @@ static const struct wlr_buffer_impl data_buffer_impl = { .end_data_ptr_access = data_buffer_end_data_ptr_access, }; -static struct lab_data_buffer * -data_buffer_from_buffer(struct wlr_buffer *buffer) -{ - assert(buffer->impl == &data_buffer_impl); - return (struct lab_data_buffer *)buffer; -} - struct lab_data_buffer * buffer_adopt_cairo_surface(cairo_surface_t *surface) { diff --git a/src/common/box.c b/src/common/box.c index 2520f733..0b1c0b44 100644 --- a/src/common/box.c +++ b/src/common/box.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +#include #include "common/box.h" #include "common/macros.h" diff --git a/src/common/buf.c b/src/common/buf.c index c141b62e..1e103fae 100644 --- a/src/common/buf.c +++ b/src/common/buf.c @@ -1,12 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "common/buf.h" #include #include #include #include #include #include -#include +#include "common/buf.h" #include "common/macros.h" #include "common/mem.h" #include "common/string-helpers.h" @@ -14,15 +13,15 @@ void buf_expand_tilde(struct buf *s) { - struct buf tmp = BUF_INIT; + struct buf new = BUF_INIT; for (int i = 0 ; i < s->len ; i++) { if (s->data[i] == '~') { - buf_add(&tmp, getenv("HOME")); + buf_add(&new, getenv("HOME")); } else { - buf_add_char(&tmp, s->data[i]); + buf_add_char(&new, s->data[i]); } } - buf_move(s, &tmp); + buf_move(s, &new); } static void @@ -46,7 +45,7 @@ isvalid(char p) void buf_expand_shell_variables(struct buf *s) { - struct buf tmp = BUF_INIT; + struct buf new = BUF_INIT; struct buf environment_variable = BUF_INIT; for (int i = 0 ; i < s->len ; i++) { @@ -63,14 +62,14 @@ buf_expand_shell_variables(struct buf *s) strip_curly_braces(environment_variable.data); p = getenv(environment_variable.data); if (p) { - buf_add(&tmp, p); + buf_add(&new, p); } } else { - buf_add_char(&tmp, s->data[i]); + buf_add_char(&new, s->data[i]); } } buf_reset(&environment_variable); - buf_move(s, &tmp); + buf_move(s, &new); } static void @@ -129,30 +128,6 @@ buf_add_fmt(struct buf *s, const char *fmt, ...) s->data[s->len] = 0; } -void -buf_add_hex_color(struct buf *s, float color[4]) -{ - /* - * In theme.c parse_hexstr() colors are pre-multiplied (by alpha) as - * expected by wlr_scene(). We therefore need to reverse that here. - * - * For details, see https://github.com/labwc/labwc/pull/1685 - */ - float alpha = color[3]; - - /* Avoid division by zero */ - if (alpha == 0.0f) { - buf_add(s, "#00000000"); - return; - } - - buf_add_fmt(s, "#%02x%02x%02x%02x", - (int)(color[0] / alpha * 255), - (int)(color[1] / alpha * 255), - (int)(color[2] / alpha * 255), - (int)(alpha * 255)); -} - void buf_add(struct buf *s, const char *data) { @@ -204,37 +179,3 @@ buf_move(struct buf *dst, struct buf *src) *dst = *src; *src = BUF_INIT; } - -struct buf -buf_from_file(const char *filename) -{ - struct buf buf = BUF_INIT; - FILE *stream = fopen(filename, "r"); - if (!stream) { - return buf; - } - - if (fseek(stream, 0, SEEK_END) == -1) { - wlr_log_errno(WLR_ERROR, "fseek(%s)", filename); - fclose(stream); - return buf; - } - long size = ftell(stream); - if (size == -1) { - wlr_log_errno(WLR_ERROR, "ftell(%s)", filename); - fclose(stream); - return buf; - } - rewind(stream); - - buf_expand(&buf, size + 1); - if (fread(buf.data, 1, size, stream) == (size_t)size) { - buf.len = size; - buf.data[size] = '\0'; - } else { - wlr_log_errno(WLR_ERROR, "fread(%s)", filename); - buf_reset(&buf); - } - fclose(stream); - return buf; -} diff --git a/src/common/dir.c b/src/common/dir.c index 76b4e65e..2b0016d7 100644 --- a/src/common/dir.c +++ b/src/common/dir.c @@ -4,16 +4,18 @@ * * Copyright Johan Malm 2020 */ -#include "common/dir.h" #include #include +#include #include #include +#include +#include "common/dir.h" #include "common/buf.h" #include "common/list.h" #include "common/mem.h" #include "common/string-helpers.h" -#include "config/rcxml.h" +#include "labwc.h" struct dir { const char *prefix; diff --git a/src/common/direction.c b/src/common/direction.c new file mode 100644 index 00000000..fa88eee4 --- /dev/null +++ b/src/common/direction.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include "common/direction.h" +#include "view.h" + +enum wlr_direction +direction_from_view_edge(enum view_edge edge) +{ + switch (edge) { + case VIEW_EDGE_LEFT: + return WLR_DIRECTION_LEFT; + case VIEW_EDGE_RIGHT: + return WLR_DIRECTION_RIGHT; + case VIEW_EDGE_UP: + return WLR_DIRECTION_UP; + case VIEW_EDGE_DOWN: + return WLR_DIRECTION_DOWN; + case VIEW_EDGE_CENTER: + case VIEW_EDGE_INVALID: + default: + return WLR_DIRECTION_UP; + } +} + +enum wlr_direction +direction_get_opposite(enum wlr_direction direction) +{ + switch (direction) { + case WLR_DIRECTION_RIGHT: + return WLR_DIRECTION_LEFT; + case WLR_DIRECTION_LEFT: + return WLR_DIRECTION_RIGHT; + case WLR_DIRECTION_DOWN: + return WLR_DIRECTION_UP; + case WLR_DIRECTION_UP: + return WLR_DIRECTION_DOWN; + default: + assert(0); /* Unreachable */ + return WLR_DIRECTION_UP; + } +} diff --git a/src/common/edge.c b/src/common/edge.c deleted file mode 100644 index c241396b..00000000 --- a/src/common/edge.c +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include "common/edge.h" -#include -#include -#include -#include - -static_assert((int)LAB_EDGE_TOP == (int)WLR_EDGE_TOP - && (int)LAB_EDGE_BOTTOM == (int)WLR_EDGE_BOTTOM - && (int)LAB_EDGE_LEFT == (int)WLR_EDGE_LEFT - && (int)LAB_EDGE_RIGHT == (int)WLR_EDGE_RIGHT, - "enum lab_edge does not match enum wlr_edges"); - -static_assert((int)LAB_EDGE_TOP == (int)WLR_DIRECTION_UP - && (int)LAB_EDGE_BOTTOM == (int)WLR_DIRECTION_DOWN - && (int)LAB_EDGE_LEFT == (int)WLR_DIRECTION_LEFT - && (int)LAB_EDGE_RIGHT == (int)WLR_DIRECTION_RIGHT, - "enum lab_edge does not match enum wlr_direction"); - -enum lab_edge -lab_edge_parse(const char *direction, bool tiled, bool any) -{ - if (!direction) { - return LAB_EDGE_NONE; - } - if (!strcasecmp(direction, "left")) { - return LAB_EDGE_LEFT; - } else if (!strcasecmp(direction, "up")) { - return LAB_EDGE_TOP; - } else if (!strcasecmp(direction, "right")) { - return LAB_EDGE_RIGHT; - } else if (!strcasecmp(direction, "down")) { - return LAB_EDGE_BOTTOM; - } - - if (any) { - if (!strcasecmp(direction, "any")) { - return LAB_EDGE_ANY; - } - } - - if (tiled) { - if (!strcasecmp(direction, "center")) { - return LAB_EDGE_CENTER; - } else if (!strcasecmp(direction, "up-left")) { - return LAB_EDGES_TOP_LEFT; - } else if (!strcasecmp(direction, "up-right")) { - return LAB_EDGES_TOP_RIGHT; - } else if (!strcasecmp(direction, "down-left")) { - return LAB_EDGES_BOTTOM_LEFT; - } else if (!strcasecmp(direction, "down-right")) { - return LAB_EDGES_BOTTOM_RIGHT; - } - } - - return LAB_EDGE_NONE; -} - -bool -lab_edge_is_cardinal(enum lab_edge edge) -{ - switch (edge) { - case LAB_EDGE_TOP: - case LAB_EDGE_BOTTOM: - case LAB_EDGE_LEFT: - case LAB_EDGE_RIGHT: - return true; - default: - return false; - } -} - -enum lab_edge -lab_edge_invert(enum lab_edge edge) -{ - switch (edge) { - case LAB_EDGE_LEFT: - return LAB_EDGE_RIGHT; - case LAB_EDGE_RIGHT: - return LAB_EDGE_LEFT; - case LAB_EDGE_TOP: - return LAB_EDGE_BOTTOM; - case LAB_EDGE_BOTTOM: - return LAB_EDGE_TOP; - default: - return LAB_EDGE_NONE; - } -} diff --git a/src/common/fd-util.c b/src/common/fd-util.c index 883e2c84..672f467e 100644 --- a/src/common/fd-util.c +++ b/src/common/fd-util.c @@ -1,10 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "common/fd-util.h" #include #include +#include "common/fd-util.h" + static struct rlimit original_nofile_rlimit = {0}; void diff --git a/src/common/file-helpers.c b/src/common/file-helpers.c index 14dc6e6f..2361b546 100644 --- a/src/common/file-helpers.c +++ b/src/common/file-helpers.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "common/file-helpers.h" #include +#include "common/file-helpers.h" bool file_exists(const char *filename) diff --git a/src/common/font.c b/src/common/font.c index b307729c..9599c846 100644 --- a/src/common/font.c +++ b/src/common/font.c @@ -1,10 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "common/font.h" #include +#include #include +#include +#include #include +#include "common/font.h" #include "common/graphic-helpers.h" #include "common/string-helpers.h" +#include "labwc.h" #include "buffer.h" PangoFontDescription * @@ -22,7 +26,7 @@ static PangoRectangle font_extents(struct font *font, const char *string) { PangoRectangle rect = { 0 }; - if (string_null_or_empty(string)) { + if (!string) { return rect; } cairo_surface_t *surface; @@ -43,6 +47,10 @@ font_extents(struct font *font, const char *string) pango_layout_get_extents(layout, NULL, &rect); pango_extents_to_pixels(&rect, NULL); + /* we put a 2 px edge on each side - because Openbox does it :) */ + /* TODO: remove the 4 pixel addition and always do the padding by the caller */ + rect.width += 4; + cairo_destroy(c); cairo_surface_destroy(surface); pango_font_description_free(desc); diff --git a/src/common/grab-file.c b/src/common/grab-file.c new file mode 100644 index 00000000..9c90045c --- /dev/null +++ b/src/common/grab-file.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Read file into memory + * + * Copyright Johan Malm 2020 + */ + +#define _POSIX_C_SOURCE 200809L +#include "common/grab-file.h" +#include "common/buf.h" + +#include +#include +#include + +struct buf +grab_file(const char *filename) +{ + char *line = NULL; + size_t len = 0; + FILE *stream = fopen(filename, "r"); + if (!stream) { + return BUF_INIT; + } + struct buf buffer = BUF_INIT; + while ((getline(&line, &len, stream) != -1)) { + char *p = strrchr(line, '\n'); + if (p) { + *p = '\0'; + } + buf_add(&buffer, line); + } + free(line); + fclose(stream); + return buffer; +} diff --git a/src/common/graphic-helpers.c b/src/common/graphic-helpers.c index 4371c9e5..7a8af89a 100644 --- a/src/common/graphic-helpers.c +++ b/src/common/graphic-helpers.c @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "common/graphic-helpers.h" #include #include /* g_ascii_strcasecmp */ -#include +#include +#include "common/graphic-helpers.h" #include "common/macros.h" #include "xcolor-table.h" @@ -27,34 +27,34 @@ draw_cairo_border(cairo_t *cairo, struct wlr_fbox fbox, double line_width) /* Sets the cairo color. Splits the single color channels */ void -set_cairo_color(cairo_t *cairo, const float *color) +set_cairo_color(cairo_t *cairo, const float *c) { /* * We are dealing with pre-multiplied colors * but cairo expects unmultiplied colors here */ - float alpha = color[3]; + float alpha = c[3]; if (alpha == 0.0f) { cairo_set_source_rgba(cairo, 0, 0, 0, 0); return; } - cairo_set_source_rgba(cairo, color[0] / alpha, color[1] / alpha, - color[2] / alpha, alpha); + cairo_set_source_rgba(cairo, c[0] / alpha, c[1] / alpha, + c[2] / alpha, alpha); } cairo_pattern_t * -color_to_pattern(const float *color) +color_to_pattern(const float *c) { - float alpha = color[3]; + float alpha = c[3]; if (alpha == 0.0f) { return cairo_pattern_create_rgba(0, 0, 0, 0); } - return cairo_pattern_create_rgba(color[0] / alpha, color[1] / alpha, - color[2] / alpha, alpha); + return cairo_pattern_create_rgba( + c[0] / alpha, c[1] / alpha, c[2] / alpha, alpha); } /* diff --git a/src/common/lab-scene-rect.c b/src/common/lab-scene-rect.c index 2d0c7408..bc23422d 100644 --- a/src/common/lab-scene-rect.c +++ b/src/common/lab-scene-rect.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "common/lab-scene-rect.h" #include #include +#include "common/lab-scene-rect.h" #include "common/mem.h" struct border_scene { diff --git a/src/common/match.c b/src/common/match.c index 2293a71c..9bc3cf8b 100644 --- a/src/common/match.c +++ b/src/common/match.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "common/match.h" #include +#include "common/match.h" bool match_glob(const char *pattern, const char *string) diff --git a/src/common/mem.c b/src/common/mem.c index 72ba3324..e63987b5 100644 --- a/src/common/mem.c +++ b/src/common/mem.c @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "common/mem.h" #include #include #include +#include "common/mem.h" -void +static void die_if_null(void *ptr) { if (!ptr) { diff --git a/src/common/meson.build b/src/common/meson.build index 4cf52023..1e226297 100644 --- a/src/common/meson.build +++ b/src/common/meson.build @@ -1,22 +1,26 @@ labwc_sources += files( + 'direction.c', 'box.c', 'buf.c', 'dir.c', - 'edge.c', 'fd-util.c', 'file-helpers.c', 'font.c', + 'grab-file.c', 'graphic-helpers.c', 'lab-scene-rect.c', 'match.c', 'mem.c', 'nodename.c', - 'node-type.c', 'parse-bool.c', 'parse-double.c', + 'scaled-font-buffer.c', + 'scaled-icon-buffer.c', + 'scaled-img-buffer.c', + 'scaled-scene-buffer.c', 'scene-helpers.c', 'set.c', + 'surface-helpers.c', 'spawn.c', 'string-helpers.c', - 'xml.c', ) diff --git a/src/common/node-type.c b/src/common/node-type.c deleted file mode 100644 index 67958dd4..00000000 --- a/src/common/node-type.c +++ /dev/null @@ -1,129 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include "common/node-type.h" -#include -#include - -enum lab_node_type -node_type_parse(const char *context) -{ - if (!strcasecmp(context, "Close")) { - return LAB_NODE_BUTTON_CLOSE; - } else if (!strcasecmp(context, "Maximize")) { - return LAB_NODE_BUTTON_MAXIMIZE; - } else if (!strcasecmp(context, "Iconify")) { - return LAB_NODE_BUTTON_ICONIFY; - } else if (!strcasecmp(context, "WindowMenu")) { - return LAB_NODE_BUTTON_WINDOW_MENU; - } else if (!strcasecmp(context, "Icon")) { - return LAB_NODE_BUTTON_WINDOW_ICON; - } else if (!strcasecmp(context, "Shade")) { - return LAB_NODE_BUTTON_SHADE; - } else if (!strcasecmp(context, "AllDesktops")) { - return LAB_NODE_BUTTON_OMNIPRESENT; - } else if (!strcasecmp(context, "Titlebar")) { - return LAB_NODE_TITLEBAR; - } else if (!strcasecmp(context, "Title")) { - return LAB_NODE_TITLE; - } else if (!strcasecmp(context, "TLCorner")) { - return LAB_NODE_CORNER_TOP_LEFT; - } else if (!strcasecmp(context, "TRCorner")) { - return LAB_NODE_CORNER_TOP_RIGHT; - } else if (!strcasecmp(context, "BRCorner")) { - return LAB_NODE_CORNER_BOTTOM_RIGHT; - } else if (!strcasecmp(context, "BLCorner")) { - return LAB_NODE_CORNER_BOTTOM_LEFT; - } else if (!strcasecmp(context, "Border")) { - return LAB_NODE_BORDER; - } else if (!strcasecmp(context, "Top")) { - return LAB_NODE_BORDER_TOP; - } else if (!strcasecmp(context, "Right")) { - return LAB_NODE_BORDER_RIGHT; - } else if (!strcasecmp(context, "Bottom")) { - return LAB_NODE_BORDER_BOTTOM; - } else if (!strcasecmp(context, "Left")) { - return LAB_NODE_BORDER_LEFT; - } else if (!strcasecmp(context, "Frame")) { - return LAB_NODE_FRAME; - } else if (!strcasecmp(context, "Client")) { - return LAB_NODE_CLIENT; - } else if (!strcasecmp(context, "Desktop")) { - return LAB_NODE_ROOT; - } else if (!strcasecmp(context, "Root")) { - return LAB_NODE_ROOT; - } else if (!strcasecmp(context, "All")) { - return LAB_NODE_ALL; - } - wlr_log(WLR_ERROR, "unknown mouse context (%s)", context); - return LAB_NODE_NONE; -} - -bool -node_type_contains(enum lab_node_type whole, enum lab_node_type part) -{ - if (whole == part || whole == LAB_NODE_ALL) { - return true; - } - if (whole == LAB_NODE_BUTTON) { - return part >= LAB_NODE_BUTTON_FIRST - && part <= LAB_NODE_BUTTON_LAST; - } - if (whole == LAB_NODE_TITLEBAR) { - return part >= LAB_NODE_BUTTON_FIRST - && part <= LAB_NODE_TITLE; - } - if (whole == LAB_NODE_TITLE) { - /* "Title" includes blank areas of "Titlebar" as well */ - return part >= LAB_NODE_TITLEBAR - && part <= LAB_NODE_TITLE; - } - if (whole == LAB_NODE_FRAME) { - return part >= LAB_NODE_BUTTON_FIRST - && part <= LAB_NODE_CLIENT; - } - if (whole == LAB_NODE_BORDER) { - return part >= LAB_NODE_CORNER_TOP_LEFT - && part <= LAB_NODE_BORDER_LEFT; - } - if (whole == LAB_NODE_BORDER_TOP) { - return part == LAB_NODE_CORNER_TOP_LEFT - || part == LAB_NODE_CORNER_TOP_RIGHT; - } - if (whole == LAB_NODE_BORDER_RIGHT) { - return part == LAB_NODE_CORNER_TOP_RIGHT - || part == LAB_NODE_CORNER_BOTTOM_RIGHT; - } - if (whole == LAB_NODE_BORDER_BOTTOM) { - return part == LAB_NODE_CORNER_BOTTOM_RIGHT - || part == LAB_NODE_CORNER_BOTTOM_LEFT; - } - if (whole == LAB_NODE_BORDER_LEFT) { - return part == LAB_NODE_CORNER_TOP_LEFT - || part == LAB_NODE_CORNER_BOTTOM_LEFT; - } - return false; -} - -enum lab_edge -node_type_to_edges(enum lab_node_type type) -{ - switch (type) { - case LAB_NODE_BORDER_TOP: - return LAB_EDGE_TOP; - case LAB_NODE_BORDER_RIGHT: - return LAB_EDGE_RIGHT; - case LAB_NODE_BORDER_BOTTOM: - return LAB_EDGE_BOTTOM; - case LAB_NODE_BORDER_LEFT: - return LAB_EDGE_LEFT; - case LAB_NODE_CORNER_TOP_LEFT: - return LAB_EDGES_TOP_LEFT; - case LAB_NODE_CORNER_TOP_RIGHT: - return LAB_EDGES_TOP_RIGHT; - case LAB_NODE_CORNER_BOTTOM_RIGHT: - return LAB_EDGES_BOTTOM_RIGHT; - case LAB_NODE_CORNER_BOTTOM_LEFT: - return LAB_EDGES_BOTTOM_LEFT; - default: - return LAB_EDGE_NONE; - } -} diff --git a/src/common/nodename.c b/src/common/nodename.c index 269e671c..86dfd620 100644 --- a/src/common/nodename.c +++ b/src/common/nodename.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "common/nodename.h" #include #include +#include "common/nodename.h" char * nodename(xmlNode *node, char *buf, int len) diff --git a/src/common/parse-bool.c b/src/common/parse-bool.c index 30890315..dc6780f5 100644 --- a/src/common/parse-bool.c +++ b/src/common/parse-bool.c @@ -1,10 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "common/parse-bool.h" #include #include +#include "common/parse-bool.h" -enum lab_tristate -parse_tristate(const char *str) +enum three_state +parse_three_state(const char *str) { if (!str) { goto error_not_a_boolean; @@ -33,7 +33,7 @@ error_not_a_boolean: int parse_bool(const char *str, int default_value) { - enum lab_tristate val = parse_tristate(str); + enum three_state val = parse_three_state(str); if (val == LAB_STATE_UNSPECIFIED) { return default_value; } diff --git a/src/common/parse-double.c b/src/common/parse-double.c index a41e57f2..db440d0e 100644 --- a/src/common/parse-double.c +++ b/src/common/parse-double.c @@ -1,9 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "common/parse-double.h" #include #include +#include #include #include "common/mem.h" +#include "common/parse-double.h" struct dec_separator { int index; diff --git a/src/scaled-buffer/scaled-font-buffer.c b/src/common/scaled-font-buffer.c similarity index 78% rename from src/scaled-buffer/scaled-font-buffer.c rename to src/common/scaled-font-buffer.c index bb93fc67..64584b03 100644 --- a/src/scaled-buffer/scaled-font-buffer.c +++ b/src/common/scaled-font-buffer.c @@ -1,18 +1,20 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "scaled-buffer/scaled-font-buffer.h" #include #include #include +#include #include +#include "buffer.h" #include "common/font.h" #include "common/graphic-helpers.h" #include "common/mem.h" +#include "common/scaled-scene-buffer.h" +#include "common/scaled-font-buffer.h" #include "common/string-helpers.h" -#include "scaled-buffer/scaled-buffer.h" static struct lab_data_buffer * -_create_buffer(struct scaled_buffer *scaled_buffer, double scale) +_create_buffer(struct scaled_scene_buffer *scaled_buffer, double scale) { struct lab_data_buffer *buffer = NULL; struct scaled_font_buffer *self = scaled_buffer->data; @@ -37,7 +39,7 @@ _create_buffer(struct scaled_buffer *scaled_buffer, double scale) } static void -_destroy(struct scaled_buffer *scaled_buffer) +_destroy(struct scaled_scene_buffer *scaled_buffer) { struct scaled_font_buffer *self = scaled_buffer->data; scaled_buffer->data = NULL; @@ -49,8 +51,8 @@ _destroy(struct scaled_buffer *scaled_buffer) } static bool -_equal(struct scaled_buffer *scaled_buffer_a, - struct scaled_buffer *scaled_buffer_b) +_equal(struct scaled_scene_buffer *scaled_buffer_a, + struct scaled_scene_buffer *scaled_buffer_b) { struct scaled_font_buffer *a = scaled_buffer_a->data; struct scaled_font_buffer *b = scaled_buffer_b->data; @@ -67,7 +69,7 @@ _equal(struct scaled_buffer *scaled_buffer_a, && a->bg_pattern == b->bg_pattern; } -static const struct scaled_buffer_impl impl = { +static const struct scaled_scene_buffer_impl impl = { .create_buffer = _create_buffer, .destroy = _destroy, .equal = _equal, @@ -79,7 +81,7 @@ scaled_font_buffer_create(struct wlr_scene_tree *parent) { assert(parent); struct scaled_font_buffer *self = znew(*self); - struct scaled_buffer *scaled_buffer = scaled_buffer_create( + struct scaled_scene_buffer *scaled_buffer = scaled_scene_buffer_create( parent, &impl, /* drop_buffer */ true); if (!scaled_buffer) { free(self); @@ -136,6 +138,20 @@ scaled_font_buffer_update(struct scaled_font_buffer *self, const char *text, &self->width, &computed_height); self->height = (self->fixed_height > 0) ? self->fixed_height : computed_height; - scaled_buffer_request_update(self->scaled_buffer, + scaled_scene_buffer_request_update(self->scaled_buffer, + self->width, self->height); +} + +void +scaled_font_buffer_set_max_width(struct scaled_font_buffer *self, int max_width) +{ + self->max_width = max_width; + + int computed_height; + font_get_buffer_size(self->max_width, self->text, &self->font, + &self->width, &computed_height); + self->height = (self->fixed_height > 0) ? + self->fixed_height : computed_height; + scaled_scene_buffer_request_update(self->scaled_buffer, self->width, self->height); } diff --git a/src/scaled-buffer/scaled-icon-buffer.c b/src/common/scaled-icon-buffer.c similarity index 89% rename from src/scaled-buffer/scaled-icon-buffer.c rename to src/common/scaled-icon-buffer.c index ceab0509..bb9ca9eb 100644 --- a/src/scaled-buffer/scaled-icon-buffer.c +++ b/src/common/scaled-icon-buffer.c @@ -1,18 +1,19 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "scaled-buffer/scaled-icon-buffer.h" #include #include #include #include "buffer.h" +#include "common/macros.h" #include "common/mem.h" +#include "common/scaled-icon-buffer.h" +#include "common/scaled-scene-buffer.h" #include "common/string-helpers.h" #include "config.h" #include "config/rcxml.h" #include "desktop-entry.h" #include "img/img.h" #include "node.h" -#include "scaled-buffer/scaled-buffer.h" #include "view.h" #include "window-rules.h" @@ -21,7 +22,7 @@ static struct lab_data_buffer * choose_best_icon_buffer(struct scaled_icon_buffer *self, int icon_size, double scale) { - int best_dist = -INT_MAX; + int best_dist = INT_MIN; struct lab_data_buffer *best_buffer = NULL; struct lab_data_buffer **buffer; @@ -44,7 +45,7 @@ choose_best_icon_buffer(struct scaled_icon_buffer *self, int icon_size, double s } static struct lab_data_buffer * -img_to_buffer(struct lab_img *img, int width, int height, double scale) +img_to_buffer(struct lab_img *img, int width, int height, int scale) { struct lab_data_buffer *buffer = lab_img_render(img, width, height, scale); lab_img_destroy(img); @@ -96,7 +97,7 @@ load_server_icon(struct scaled_icon_buffer *self, int icon_size, double scale) #endif /* HAVE_LIBSFDO */ static struct lab_data_buffer * -_create_buffer(struct scaled_buffer *scaled_buffer, double scale) +_create_buffer(struct scaled_scene_buffer *scaled_buffer, double scale) { #if HAVE_LIBSFDO struct scaled_icon_buffer *self = scaled_buffer->data; @@ -167,7 +168,7 @@ set_icon_buffers(struct scaled_icon_buffer *self, struct wl_array *buffers) } static void -_destroy(struct scaled_buffer *scaled_buffer) +_destroy(struct scaled_scene_buffer *scaled_buffer) { struct scaled_icon_buffer *self = scaled_buffer->data; if (self->view) { @@ -193,8 +194,8 @@ icon_buffers_equal(struct wl_array *a, struct wl_array *b) } static bool -_equal(struct scaled_buffer *scaled_buffer_a, - struct scaled_buffer *scaled_buffer_b) +_equal(struct scaled_scene_buffer *scaled_buffer_a, + struct scaled_scene_buffer *scaled_buffer_b) { struct scaled_icon_buffer *a = scaled_buffer_a->data; struct scaled_icon_buffer *b = scaled_buffer_b->data; @@ -208,7 +209,7 @@ _equal(struct scaled_buffer *scaled_buffer_a, && a->height == b->height; } -static struct scaled_buffer_impl impl = { +static struct scaled_scene_buffer_impl impl = { .create_buffer = _create_buffer, .destroy = _destroy, .equal = _equal, @@ -221,7 +222,7 @@ scaled_icon_buffer_create(struct wlr_scene_tree *parent, struct server *server, assert(parent); assert(width >= 0 && height >= 0); - struct scaled_buffer *scaled_buffer = scaled_buffer_create( + struct scaled_scene_buffer *scaled_buffer = scaled_scene_buffer_create( parent, &impl, /* drop_buffer */ true); struct scaled_icon_buffer *self = znew(*self); self->scaled_buffer = scaled_buffer; @@ -255,7 +256,7 @@ handle_view_set_icon(struct wl_listener *listener, void *data) } set_icon_buffers(self, &self->view->icon.buffers); - scaled_buffer_request_update(self->scaled_buffer, + scaled_scene_buffer_request_update(self->scaled_buffer, self->width, self->height); } @@ -271,7 +272,7 @@ handle_view_new_title(struct wl_listener *listener, void *data) return; } self->view_icon_prefer_client = prefer_client; - scaled_buffer_request_update(self->scaled_buffer, + scaled_scene_buffer_request_update(self->scaled_buffer, self->width, self->height); } @@ -281,7 +282,7 @@ handle_view_new_app_id(struct wl_listener *listener, void *data) struct scaled_icon_buffer *self = wl_container_of(listener, self, on_view.new_app_id); - const char *app_id = self->view->app_id; + const char *app_id = view_get_string_prop(self->view, "app_id"); if (str_equal(app_id, self->view_app_id)) { return; } @@ -289,7 +290,7 @@ handle_view_new_app_id(struct wl_listener *listener, void *data) xstrdup_replace(self->view_app_id, app_id); self->view_icon_prefer_client = window_rules_get_property( self->view, "iconPreferClient") == LAB_PROP_TRUE; - scaled_buffer_request_update(self->scaled_buffer, + scaled_scene_buffer_request_update(self->scaled_buffer, self->width, self->height); } @@ -347,5 +348,14 @@ scaled_icon_buffer_set_icon_name(struct scaled_icon_buffer *self, return; } xstrdup_replace(self->icon_name, icon_name); - scaled_buffer_request_update(self->scaled_buffer, self->width, self->height); + scaled_scene_buffer_request_update(self->scaled_buffer, self->width, self->height); +} + +struct scaled_icon_buffer * +scaled_icon_buffer_from_node(struct wlr_scene_node *node) +{ + struct scaled_scene_buffer *scaled_buffer = + node_scaled_scene_buffer_from_node(node); + assert(scaled_buffer->impl == &impl); + return scaled_buffer->data; } diff --git a/src/scaled-buffer/scaled-img-buffer.c b/src/common/scaled-img-buffer.c similarity index 60% rename from src/scaled-buffer/scaled-img-buffer.c rename to src/common/scaled-img-buffer.c index 50baa961..817e222c 100644 --- a/src/scaled-buffer/scaled-img-buffer.c +++ b/src/common/scaled-img-buffer.c @@ -1,14 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "scaled-buffer/scaled-img-buffer.h" #include +#include +#include +#include "buffer.h" #include "common/mem.h" +#include "common/scaled-img-buffer.h" +#include "common/scaled-scene-buffer.h" #include "img/img.h" #include "node.h" -#include "scaled-buffer/scaled-buffer.h" static struct lab_data_buffer * -_create_buffer(struct scaled_buffer *scaled_buffer, double scale) +_create_buffer(struct scaled_scene_buffer *scaled_buffer, double scale) { struct scaled_img_buffer *self = scaled_buffer->data; struct lab_data_buffer *buffer = lab_img_render(self->img, @@ -17,7 +20,7 @@ _create_buffer(struct scaled_buffer *scaled_buffer, double scale) } static void -_destroy(struct scaled_buffer *scaled_buffer) +_destroy(struct scaled_scene_buffer *scaled_buffer) { struct scaled_img_buffer *self = scaled_buffer->data; lab_img_destroy(self->img); @@ -25,8 +28,8 @@ _destroy(struct scaled_buffer *scaled_buffer) } static bool -_equal(struct scaled_buffer *scaled_buffer_a, - struct scaled_buffer *scaled_buffer_b) +_equal(struct scaled_scene_buffer *scaled_buffer_a, + struct scaled_scene_buffer *scaled_buffer_b) { struct scaled_img_buffer *a = scaled_buffer_a->data; struct scaled_img_buffer *b = scaled_buffer_b->data; @@ -36,7 +39,7 @@ _equal(struct scaled_buffer *scaled_buffer_a, && a->height == b->height; } -static struct scaled_buffer_impl impl = { +static struct scaled_scene_buffer_impl impl = { .create_buffer = _create_buffer, .destroy = _destroy, .equal = _equal, @@ -50,7 +53,7 @@ scaled_img_buffer_create(struct wlr_scene_tree *parent, struct lab_img *img, assert(img); assert(width >= 0 && height >= 0); - struct scaled_buffer *scaled_buffer = scaled_buffer_create( + struct scaled_scene_buffer *scaled_buffer = scaled_scene_buffer_create( parent, &impl, /* drop_buffer */ true); struct scaled_img_buffer *self = znew(*self); self->scaled_buffer = scaled_buffer; @@ -61,7 +64,16 @@ scaled_img_buffer_create(struct wlr_scene_tree *parent, struct lab_img *img, scaled_buffer->data = self; - scaled_buffer_request_update(scaled_buffer, width, height); + scaled_scene_buffer_request_update(scaled_buffer, width, height); return self; } + +struct scaled_img_buffer * +scaled_img_buffer_from_node(struct wlr_scene_node *node) +{ + struct scaled_scene_buffer *scaled_buffer = + node_scaled_scene_buffer_from_node(node); + assert(scaled_buffer->impl == &impl); + return scaled_buffer->data; +} diff --git a/src/scaled-buffer/scaled-buffer.c b/src/common/scaled-scene-buffer.c similarity index 83% rename from src/scaled-buffer/scaled-buffer.c rename to src/common/scaled-scene-buffer.c index 8bef0a75..39ac44dc 100644 --- a/src/scaled-buffer/scaled-buffer.c +++ b/src/common/scaled-scene-buffer.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "scaled-buffer/scaled-buffer.h" #include #include #include @@ -12,17 +11,18 @@ #include "common/list.h" #include "common/macros.h" #include "common/mem.h" +#include "common/scaled-scene-buffer.h" #include "node.h" /* - * This holds all the scaled_buffers from all the implementers. + * This holds all the scaled_scene_buffers from all the implementers. * This is used to share visually duplicated buffers found via impl->equal(). */ static struct wl_list all_scaled_buffers = WL_LIST_INIT(&all_scaled_buffers); /* Internal API */ static void -_cache_entry_destroy(struct scaled_buffer_cache_entry *cache_entry, bool drop_buffer) +_cache_entry_destroy(struct scaled_scene_buffer_cache_entry *cache_entry, bool drop_buffer) { wl_list_remove(&cache_entry->link); if (cache_entry->buffer) { @@ -35,10 +35,10 @@ _cache_entry_destroy(struct scaled_buffer_cache_entry *cache_entry, bool drop_bu free(cache_entry); } -static struct scaled_buffer_cache_entry * -find_cache_for_scale(struct scaled_buffer *scene_buffer, double scale) +static struct scaled_scene_buffer_cache_entry * +find_cache_for_scale(struct scaled_scene_buffer *scene_buffer, double scale) { - struct scaled_buffer_cache_entry *cache_entry; + struct scaled_scene_buffer_cache_entry *cache_entry; wl_list_for_each(cache_entry, &scene_buffer->cache, link) { if (cache_entry->scale == scale) { return cache_entry; @@ -48,12 +48,12 @@ find_cache_for_scale(struct scaled_buffer *scene_buffer, double scale) } static void -_update_buffer(struct scaled_buffer *self, double scale) +_update_buffer(struct scaled_scene_buffer *self, double scale) { self->active_scale = scale; /* Search for cached buffer of specified scale */ - struct scaled_buffer_cache_entry *cache_entry = + struct scaled_scene_buffer_cache_entry *cache_entry = find_cache_for_scale(self, scale); if (cache_entry) { /* LRU cache, recently used in front */ @@ -71,8 +71,8 @@ _update_buffer(struct scaled_buffer *self, double scale) struct wlr_buffer *wlr_buffer = NULL; if (self->impl->equal) { - /* Search from other cached scaled-buffers */ - struct scaled_buffer *scene_buffer; + /* Search from other cached scaled-scene-buffers */ + struct scaled_scene_buffer *scene_buffer; wl_list_for_each(scene_buffer, &all_scaled_buffers, link) { if (scene_buffer == self) { continue; @@ -146,8 +146,8 @@ _update_buffer(struct scaled_buffer *self, double scale) static void _handle_node_destroy(struct wl_listener *listener, void *data) { - struct scaled_buffer_cache_entry *cache_entry, *cache_entry_tmp; - struct scaled_buffer *self = wl_container_of(listener, self, destroy); + struct scaled_scene_buffer_cache_entry *cache_entry, *cache_entry_tmp; + struct scaled_scene_buffer *self = wl_container_of(listener, self, destroy); wl_list_remove(&self->destroy.link); wl_list_remove(&self->outputs_update.link); @@ -167,7 +167,7 @@ _handle_node_destroy(struct wl_listener *listener, void *data) static void _handle_outputs_update(struct wl_listener *listener, void *data) { - struct scaled_buffer *self = + struct scaled_scene_buffer *self = wl_container_of(listener, self, outputs_update); double max_scale = 0; @@ -181,22 +181,24 @@ _handle_outputs_update(struct wl_listener *listener, void *data) } /* Public API */ -struct scaled_buffer * -scaled_buffer_create(struct wlr_scene_tree *parent, - const struct scaled_buffer_impl *impl, +struct scaled_scene_buffer * +scaled_scene_buffer_create(struct wlr_scene_tree *parent, + const struct scaled_scene_buffer_impl *impl, bool drop_buffer) { assert(parent); assert(impl); assert(impl->create_buffer); - struct scaled_buffer *self = znew(*self); + struct scaled_scene_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; } + node_descriptor_create(&self->scene_buffer->node, + LAB_NODE_DESC_SCALED_SCENE_BUFFER, self); self->impl = impl; /* @@ -221,14 +223,14 @@ scaled_buffer_create(struct wlr_scene_tree *parent, } void -scaled_buffer_request_update(struct scaled_buffer *self, +scaled_scene_buffer_request_update(struct scaled_scene_buffer *self, int width, int height) { assert(self); assert(width >= 0); assert(height >= 0); - struct scaled_buffer_cache_entry *cache_entry, *cache_entry_tmp; + struct scaled_scene_buffer_cache_entry *cache_entry, *cache_entry_tmp; wl_list_for_each_safe(cache_entry, cache_entry_tmp, &self->cache, link) { _cache_entry_destroy(cache_entry, self->drop_buffer); } @@ -254,9 +256,9 @@ scaled_buffer_request_update(struct scaled_buffer *self, } void -scaled_buffer_invalidate_sharing(void) +scaled_scene_buffer_invalidate_sharing(void) { - struct scaled_buffer *scene_buffer, *tmp; + struct scaled_scene_buffer *scene_buffer, *tmp; wl_list_for_each_safe(scene_buffer, tmp, &all_scaled_buffers, link) { wl_list_remove(&scene_buffer->link); wl_list_init(&scene_buffer->link); diff --git a/src/common/scene-helpers.c b/src/common/scene-helpers.c index 517789c0..e40e41cc 100644 --- a/src/common/scene-helpers.c +++ b/src/common/scene-helpers.c @@ -1,12 +1,15 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "common/scene-helpers.h" #include #include #include #include +#include +#include +#include "common/scene-helpers.h" +#include "labwc.h" #include "magnifier.h" -#include "output.h" +#include "output-state.h" struct wlr_surface * lab_wlr_surface_from_node(struct wlr_scene_node *node) diff --git a/src/common/set.c b/src/common/set.c index dffc9a86..64df2717 100644 --- a/src/common/set.c +++ b/src/common/set.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "common/set.h" #include +#include "common/set.h" bool lab_set_contains(struct lab_set *set, uint32_t value) diff --git a/src/common/spawn.c b/src/common/spawn.c index 898c4800..6e8e7c51 100644 --- a/src/common/spawn.c +++ b/src/common/spawn.c @@ -1,14 +1,15 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "common/spawn.h" #include #include #include #include #include +#include #include #include #include +#include "common/spawn.h" #include "common/fd-util.h" static void diff --git a/src/common/string-helpers.c b/src/common/string-helpers.c index 13fe6f68..f753d1a1 100644 --- a/src/common/string-helpers.c +++ b/src/common/string-helpers.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "common/string-helpers.h" #include #include #include @@ -8,6 +7,7 @@ #include #include #include "common/mem.h" +#include "common/string-helpers.h" enum str_flags { STR_FLAG_NONE = 0, @@ -20,6 +20,15 @@ string_null_or_empty(const char *s) return !s || !*s; } +void +trim_last_field(char *buf, char delim) +{ + char *p = strrchr(buf, delim); + if (p) { + *p = '\0'; + } +} + static void rtrim(char *s) { @@ -84,7 +93,8 @@ strdup_printf(const char *fmt, ...) } char * -str_join(const char *const parts[], const char *fmt, const char *sep) +str_join(const char *const parts[], + const char *restrict fmt, const char *restrict sep) { assert(parts); @@ -195,14 +205,3 @@ str_equal(const char *a, const char *b) { return a == b || (a && b && !strcmp(a, b)); } - -bool -str_space_only(const char *s) -{ - for (; *s; s++) { - if (!isspace(*s)) { - return false; - } - } - return true; -} diff --git a/src/common/surface-helpers.c b/src/common/surface-helpers.c new file mode 100644 index 00000000..e173aa10 --- /dev/null +++ b/src/common/surface-helpers.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include "common/surface-helpers.h" + +struct wlr_layer_surface_v1 * +subsurface_parent_layer(struct wlr_surface *wlr_surface) +{ + struct wlr_subsurface *subsurface = + wlr_subsurface_try_from_wlr_surface(wlr_surface); + if (!subsurface) { + return NULL; + } + struct wlr_surface *parent = subsurface->parent; + if (!parent) { + wlr_log(WLR_DEBUG, "subsurface %p has no parent", subsurface); + return NULL; + } + struct wlr_layer_surface_v1 *wlr_layer_surface = + wlr_layer_surface_v1_try_from_wlr_surface(parent); + if (wlr_layer_surface) { + return wlr_layer_surface; + } + /* Recurse in case there are nested sub-surfaces */ + return subsurface_parent_layer(parent); +} diff --git a/src/common/xml.c b/src/common/xml.c deleted file mode 100644 index 0185d9a3..00000000 --- a/src/common/xml.c +++ /dev/null @@ -1,195 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include -#include -#include -#include -#include "common/xml.h" -#include "common/parse-bool.h" - -/* - * Converts an attribute A.B.C="X" into X - */ -static xmlNode * -create_attribute_tree(const xmlAttr *attr) -{ - gchar **parts = g_strsplit((char *)attr->name, ".", -1); - int length = g_strv_length(parts); - xmlNode *root_node = NULL; - xmlNode *parent_node = NULL; - xmlNode *current_node = NULL; - - for (int i = length - 1; i >= 0; i--) { - gchar *part = parts[i]; - if (!*part) { - /* Ignore empty string */ - continue; - } - current_node = xmlNewNode(NULL, (xmlChar *)part); - if (parent_node) { - xmlAddChild(parent_node, current_node); - } else { - root_node = current_node; - } - parent_node = current_node; - } - - /* - * Note: empty attributes or attributes with only dots are forbidden - * and root_node becomes never NULL here. - */ - assert(root_node); - - xmlChar *content = xmlNodeGetContent(attr->children); - xmlNodeSetContent(current_node, content); - xmlFree(content); - - g_strfreev(parts); - return root_node; -} - -/* - * Consider . - * These three attributes are represented by following trees. - * action(dst)---name - * action(src)---position---x - * action--------position---y - * When we call merge_two_trees(dst, src) for the first 2 trees above, we walk - * over the trees from their roots towards leaves, and merge the identical - * node 'action' like: - * action(dst)---name - * \--------position---x - * action(src)---position---y - * And when we call merge_two_trees(dst, src) again, we walk over the dst tree - * like 'action'->'position'->'x' and the src tree like 'action'->'position'->'y'. - * First, we merge the identical node 'action' again like: - * action---name - * \---position(dst)---x - * \--position(src)---y - * Next, we merge the identical node 'position' like: - * action---name - * \---position---x - * \----y - */ -static bool -merge_two_trees(xmlNode *dst, xmlNode *src) -{ - bool merged = false; - - while (dst && src && src->children - && !strcasecmp((char *)dst->name, (char *)src->name)) { - xmlNode *next_dst = dst->last; - xmlNode *next_src = src->children; - xmlUnlinkNode(next_src); - xmlAddChild(dst, next_src); - xmlUnlinkNode(src); - xmlFreeNode(src); - src = next_src; - dst = next_dst; - merged = true; - } - - return merged; -} - -void -lab_xml_expand_dotted_attributes(xmlNode *parent) -{ - xmlNode *old_first_child = parent->children; - xmlNode *prev_tree = NULL; - - if (parent->type != XML_ELEMENT_NODE) { - return; - } - - for (xmlAttr *attr = parent->properties; attr;) { - /* Convert the attribute with dots into an xml tree */ - xmlNode *tree = create_attribute_tree(attr); - if (!tree) { - /* The attribute doesn't contain dots */ - prev_tree = NULL; - attr = attr->next; - continue; - } - - /* Try to merge the tree with the previous one */ - if (!merge_two_trees(prev_tree, tree)) { - /* If not merged, add the tree as a new child */ - if (old_first_child) { - xmlAddPrevSibling(old_first_child, tree); - } else { - xmlAddChild(parent, tree); - } - prev_tree = tree; - } - - xmlAttr *next_attr = attr->next; - xmlRemoveProp(attr); - attr = next_attr; - } - - for (xmlNode *node = parent->children; node; node = node->next) { - lab_xml_expand_dotted_attributes(node); - } -} - -bool -lab_xml_node_is_leaf(xmlNode *node) -{ - if (node->type != XML_ELEMENT_NODE) { - return false; - } - for (xmlNode *child = node->children; child; child = child->next) { - if (child->type != XML_TEXT_NODE - && child->type != XML_CDATA_SECTION_NODE) { - return false; - } - } - return true; -} - -static bool -get_node(xmlNode *node, const char *key, xmlNode **dst_node, bool leaf_only) -{ - for (xmlNode *child = node->last; child; child = child->prev) { - if (child->type != XML_ELEMENT_NODE) { - continue; - } - if (leaf_only && !lab_xml_node_is_leaf(child)) { - continue; - } - if (!strcasecmp((char *)child->name, key)) { - *dst_node = child; - return true; - } - } - return false; -} - -bool -lab_xml_get_string(xmlNode *node, const char *key, char *s, size_t len) -{ - xmlNode *child; - if (get_node(node, key, &child, /* leaf_only */ true)) { - xmlChar *content = xmlNodeGetContent(child); - g_strlcpy(s, (char *)content, len); - xmlFree(content); - return true; - } - return false; -} - -bool -lab_xml_get_bool(xmlNode *node, const char *key, bool *b) -{ - xmlNode *child; - if (get_node(node, key, &child, /* leaf_only */ true)) { - char *s = (char *)xmlNodeGetContent(child); - int ret = parse_bool(s, -1); - xmlFree(s); - if (ret >= 0) { - *b = ret; - return true; - } - } - return false; -} diff --git a/src/config/keybind.c b/src/config/keybind.c index 1705983a..5e5002f7 100644 --- a/src/config/keybind.c +++ b/src/config/keybind.c @@ -1,14 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "config/keybind.h" #include #include +#include #include #include -#include #include #include "common/list.h" #include "common/mem.h" +#include "config/keybind.h" #include "config/rcxml.h" #include "labwc.h" @@ -48,42 +48,6 @@ keybind_the_same(struct keybind *a, struct keybind *b) return true; } -bool -keybind_contains_keycode(struct keybind *keybind, xkb_keycode_t keycode) -{ - assert(keybind); - for (size_t i = 0; i < keybind->keycodes_len; i++) { - if (keybind->keycodes[i] == keycode) { - return true; - } - } - return false; -} - -bool -keybind_contains_keysym(struct keybind *keybind, xkb_keysym_t keysym) -{ - assert(keybind); - for (size_t i = 0; i < keybind->keysyms_len; i++) { - if (keybind->keysyms[i] == keysym) { - return true; - } - } - return false; -} - -static bool -keybind_contains_any_keysym(struct keybind *keybind, - const xkb_keysym_t *syms, int nr_syms) -{ - for (int i = 0; i < nr_syms; i++) { - if (keybind_contains_keysym(keybind, syms[i])) { - return true; - } - } - return false; -} - static void update_keycodes_iter(struct xkb_keymap *keymap, xkb_keycode_t key, void *data) { @@ -103,19 +67,32 @@ update_keycodes_iter(struct xkb_keymap *keymap, xkb_keycode_t key, void *data) if (keybind->use_syms_only) { continue; } - if (keybind_contains_any_keysym(keybind, syms, nr_syms)) { - if (keybind_contains_keycode(keybind, key)) { - /* Prevent storing the same keycode twice */ - continue; + for (int i = 0; i < nr_syms; i++) { + xkb_keysym_t sym = syms[i]; + for (size_t j = 0; j < keybind->keysyms_len; j++) { + if (sym != keybind->keysyms[j]) { + continue; + } + /* Found keycode for sym */ + if (keybind->keycodes_len == MAX_KEYCODES) { + wlr_log(WLR_ERROR, + "Already stored %lu keycodes for keybind", + keybind->keycodes_len); + break; + } + bool keycode_exists = false; + for (size_t k = 0; k < keybind->keycodes_len; k++) { + if (keybind->keycodes[k] == key) { + keycode_exists = true; + break; + } + } + if (keycode_exists) { + continue; + } + keybind->keycodes[keybind->keycodes_len++] = key; + keybind->keycodes_layout = layout; } - if (keybind->keycodes_len == MAX_KEYCODES) { - wlr_log(WLR_ERROR, - "Already stored %lu keycodes for keybind", - keybind->keycodes_len); - continue; - } - keybind->keycodes[keybind->keycodes_len++] = key; - keybind->keycodes_layout = layout; } } } @@ -146,7 +123,7 @@ keybind_create(const char *keybind) xkb_keysym_t keysyms[MAX_KEYSYMS]; gchar **symnames = g_strsplit(keybind, "-", -1); for (size_t i = 0; symnames[i]; i++) { - const char *symname = symnames[i]; + char *symname = symnames[i]; /* * Since "-" is used as a separator, a keybind string like "W--" * becomes "W", "", "". This means that it is impossible to bind diff --git a/src/config/libinput.c b/src/config/libinput.c index 1209d267..c76a9ff9 100644 --- a/src/config/libinput.c +++ b/src/config/libinput.c @@ -1,10 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "config/libinput.h" #include #include #include "common/mem.h" #include "common/list.h" #include "common/string-helpers.h" +#include "config/libinput.h" #include "config/rcxml.h" static void @@ -51,25 +51,6 @@ get_device_type(const char *s) return LAB_LIBINPUT_DEVICE_NONE; } -const char * -libinput_device_type_name(enum lab_libinput_device_type type) -{ - switch (type) { - case LAB_LIBINPUT_DEVICE_NONE: - break; - case LAB_LIBINPUT_DEVICE_DEFAULT: - return "default"; - case LAB_LIBINPUT_DEVICE_TOUCH: - return "touch"; - case LAB_LIBINPUT_DEVICE_TOUCHPAD: - return "touchpad"; - case LAB_LIBINPUT_DEVICE_NON_TOUCH: - return "non-touch"; - } - /* none/invalid */ - return "(none)"; -} - struct libinput_category * libinput_category_create(void) { diff --git a/src/config/mousebind.c b/src/config/mousebind.c index 6b5254f3..19169d55 100644 --- a/src/config/mousebind.c +++ b/src/config/mousebind.c @@ -1,13 +1,13 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "config/mousebind.h" #include #include #include +#include #include #include "common/list.h" #include "common/mem.h" -#include "config/keybind.h" +#include "config/mousebind.h" #include "config/rcxml.h" uint32_t @@ -103,6 +103,58 @@ mousebind_event_from_str(const char *str) return MOUSE_ACTION_NONE; } +static enum ssd_part_type +context_from_str(const char *str) +{ + if (!strcasecmp(str, "Close")) { + return LAB_SSD_BUTTON_CLOSE; + } else if (!strcasecmp(str, "Maximize")) { + return LAB_SSD_BUTTON_MAXIMIZE; + } else if (!strcasecmp(str, "Iconify")) { + return LAB_SSD_BUTTON_ICONIFY; + } else if (!strcasecmp(str, "WindowMenu")) { + return LAB_SSD_BUTTON_WINDOW_MENU; + } else if (!strcasecmp(str, "Icon")) { + return LAB_SSD_BUTTON_WINDOW_ICON; + } else if (!strcasecmp(str, "Shade")) { + return LAB_SSD_BUTTON_SHADE; + } else if (!strcasecmp(str, "AllDesktops")) { + return LAB_SSD_BUTTON_OMNIPRESENT; + } else if (!strcasecmp(str, "Titlebar")) { + return LAB_SSD_PART_TITLEBAR; + } else if (!strcasecmp(str, "Title")) { + return LAB_SSD_PART_TITLE; + } else if (!strcasecmp(str, "TLCorner")) { + return LAB_SSD_PART_CORNER_TOP_LEFT; + } else if (!strcasecmp(str, "TRCorner")) { + return LAB_SSD_PART_CORNER_TOP_RIGHT; + } else if (!strcasecmp(str, "BRCorner")) { + return LAB_SSD_PART_CORNER_BOTTOM_RIGHT; + } else if (!strcasecmp(str, "BLCorner")) { + return LAB_SSD_PART_CORNER_BOTTOM_LEFT; + } else if (!strcasecmp(str, "Top")) { + return LAB_SSD_PART_TOP; + } else if (!strcasecmp(str, "Right")) { + return LAB_SSD_PART_RIGHT; + } else if (!strcasecmp(str, "Bottom")) { + return LAB_SSD_PART_BOTTOM; + } else if (!strcasecmp(str, "Left")) { + return LAB_SSD_PART_LEFT; + } else if (!strcasecmp(str, "Frame")) { + return LAB_SSD_FRAME; + } else if (!strcasecmp(str, "Client")) { + return LAB_SSD_CLIENT; + } else if (!strcasecmp(str, "Desktop")) { + return LAB_SSD_ROOT; + } else if (!strcasecmp(str, "Root")) { + return LAB_SSD_ROOT; + } else if (!strcasecmp(str, "All")) { + return LAB_SSD_ALL; + } + wlr_log(WLR_ERROR, "unknown mouse context (%s)", str); + return LAB_SSD_NONE; +} + bool mousebind_the_same(struct mousebind *a, struct mousebind *b) { @@ -122,8 +174,8 @@ mousebind_create(const char *context) return NULL; } struct mousebind *m = znew(*m); - m->context = node_type_parse(context); - if (m->context != LAB_NODE_NONE) { + m->context = context_from_str(context); + if (m->context != LAB_SSD_NONE) { wl_list_append(&rc.mousebinds, &m->link); } wl_list_init(&m->actions); diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 6ed1522b..cc207603 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -1,18 +1,20 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "config/rcxml.h" #include +#include #include #include +#include #include #include #include #include #include +#include +#include #include #include #include "action.h" -#include "common/buf.h" #include "common/dir.h" #include "common/list.h" #include "common/macros.h" @@ -21,23 +23,49 @@ #include "common/parse-bool.h" #include "common/parse-double.h" #include "common/string-helpers.h" -#include "common/xml.h" +#include "common/three-state.h" #include "config/default-bindings.h" #include "config/keybind.h" #include "config/libinput.h" #include "config/mousebind.h" #include "config/tablet.h" -#include "config/tablet-tool.h" -#include "config/touch.h" +#include "config/rcxml.h" #include "labwc.h" #include "osd.h" #include "regions.h" -#include "ssd.h" -#include "translate.h" #include "view.h" #include "window-rules.h" #include "workspaces.h" +struct parser_state { + bool in_regions; + bool in_usable_area_override; + bool in_keybind; + bool in_mousebind; + bool in_touch; + bool in_libinput_category; + bool in_window_switcher_field; + bool in_window_rules; + bool in_action_query; + bool in_action_then_branch; + bool in_action_else_branch; + bool in_action_none_branch; + struct usable_area_override *current_usable_area_override; + struct keybind *current_keybind; + struct mousebind *current_mousebind; + struct touch_config_entry *current_touch; + struct libinput_category *current_libinput_category; + const char *current_mouse_context; + struct action *current_keybind_action; + struct action *current_mousebind_action; + struct region *current_region; + struct window_switcher_field *current_field; + struct window_rule *current_window_rule; + struct action *current_window_rule_action; + struct view_query *current_view_query; + struct action *current_child_action; +}; + /* for backward compatibility of */ static double mouse_scroll_factor = -1; @@ -55,42 +83,42 @@ enum font_place { static void load_default_key_bindings(void); static void load_default_mouse_bindings(void); -static enum lab_window_type +static int parse_window_type(const char *type) { if (!type) { - return LAB_WINDOW_TYPE_INVALID; + return -1; } if (!strcasecmp(type, "desktop")) { - return LAB_WINDOW_TYPE_DESKTOP; + return NET_WM_WINDOW_TYPE_DESKTOP; } else if (!strcasecmp(type, "dock")) { - return LAB_WINDOW_TYPE_DOCK; + return NET_WM_WINDOW_TYPE_DOCK; } else if (!strcasecmp(type, "toolbar")) { - return LAB_WINDOW_TYPE_TOOLBAR; + return NET_WM_WINDOW_TYPE_TOOLBAR; } else if (!strcasecmp(type, "menu")) { - return LAB_WINDOW_TYPE_MENU; + return NET_WM_WINDOW_TYPE_MENU; } else if (!strcasecmp(type, "utility")) { - return LAB_WINDOW_TYPE_UTILITY; + return NET_WM_WINDOW_TYPE_UTILITY; } else if (!strcasecmp(type, "splash")) { - return LAB_WINDOW_TYPE_SPLASH; + return NET_WM_WINDOW_TYPE_SPLASH; } else if (!strcasecmp(type, "dialog")) { - return LAB_WINDOW_TYPE_DIALOG; + return NET_WM_WINDOW_TYPE_DIALOG; } else if (!strcasecmp(type, "dropdown_menu")) { - return LAB_WINDOW_TYPE_DROPDOWN_MENU; + return NET_WM_WINDOW_TYPE_DROPDOWN_MENU; } else if (!strcasecmp(type, "popup_menu")) { - return LAB_WINDOW_TYPE_POPUP_MENU; + return NET_WM_WINDOW_TYPE_POPUP_MENU; } else if (!strcasecmp(type, "tooltip")) { - return LAB_WINDOW_TYPE_TOOLTIP; + return NET_WM_WINDOW_TYPE_TOOLTIP; } else if (!strcasecmp(type, "notification")) { - return LAB_WINDOW_TYPE_NOTIFICATION; + return NET_WM_WINDOW_TYPE_NOTIFICATION; } else if (!strcasecmp(type, "combo")) { - return LAB_WINDOW_TYPE_COMBO; + return NET_WM_WINDOW_TYPE_COMBO; } else if (!strcasecmp(type, "dnd")) { - return LAB_WINDOW_TYPE_DND; + return NET_WM_WINDOW_TYPE_DND; } else if (!strcasecmp(type, "normal")) { - return LAB_WINDOW_TYPE_NORMAL; + return NET_WM_WINDOW_TYPE_NORMAL; } else { - return LAB_WINDOW_TYPE_INVALID; + return -1; } } @@ -117,8 +145,7 @@ parse_window_type(const char *type) * desk D All-desktops toggle (aka omnipresent) */ static void -fill_section(const char *content, enum lab_node_type *buttons, int *count, - uint32_t *found_buttons /* bitmask */) +fill_section(const char *content, struct wl_list *list, uint32_t *found_buttons) { gchar **identifiers = g_strsplit(content, ",", -1); for (size_t i = 0; identifiers[i]; ++i) { @@ -126,36 +153,35 @@ fill_section(const char *content, enum lab_node_type *buttons, int *count, if (string_null_or_empty(identifier)) { continue; } - enum lab_node_type type = LAB_NODE_NONE; + enum ssd_part_type type = LAB_SSD_NONE; if (!strcmp(identifier, "icon")) { #if HAVE_LIBSFDO - type = LAB_NODE_BUTTON_WINDOW_ICON; + type = LAB_SSD_BUTTON_WINDOW_ICON; #else wlr_log(WLR_ERROR, "libsfdo is not linked. " "Replacing 'icon' in titlebar layout with 'menu'."); - type = LAB_NODE_BUTTON_WINDOW_MENU; + type = LAB_SSD_BUTTON_WINDOW_MENU; #endif } else if (!strcmp(identifier, "menu")) { - type = LAB_NODE_BUTTON_WINDOW_MENU; + type = LAB_SSD_BUTTON_WINDOW_MENU; } else if (!strcmp(identifier, "iconify")) { - type = LAB_NODE_BUTTON_ICONIFY; + type = LAB_SSD_BUTTON_ICONIFY; } else if (!strcmp(identifier, "max")) { - type = LAB_NODE_BUTTON_MAXIMIZE; + type = LAB_SSD_BUTTON_MAXIMIZE; } else if (!strcmp(identifier, "close")) { - type = LAB_NODE_BUTTON_CLOSE; + type = LAB_SSD_BUTTON_CLOSE; } else if (!strcmp(identifier, "shade")) { - type = LAB_NODE_BUTTON_SHADE; + type = LAB_SSD_BUTTON_SHADE; } else if (!strcmp(identifier, "desk")) { - type = LAB_NODE_BUTTON_OMNIPRESENT; + type = LAB_SSD_BUTTON_OMNIPRESENT; } else { wlr_log(WLR_ERROR, "invalid titleLayout identifier '%s'", identifier); continue; } - assert(type != LAB_NODE_NONE); + assert(type != LAB_SSD_NONE); - /* We no longer need this check, but let's keep it just in case */ if (*found_buttons & (1 << type)) { wlr_log(WLR_ERROR, "ignoring duplicated button type '%s'", identifier); @@ -164,8 +190,9 @@ fill_section(const char *content, enum lab_node_type *buttons, int *count, *found_buttons |= (1 << type); - assert(*count < TITLE_BUTTONS_MAX); - buttons[(*count)++] = type; + struct title_button *item = znew(*item); + item->type = type; + wl_list_append(list, &item->link); } g_strfreev(identifiers); } @@ -173,16 +200,28 @@ fill_section(const char *content, enum lab_node_type *buttons, int *count, static void clear_title_layout(void) { - rc.nr_title_buttons_left = 0; - rc.nr_title_buttons_right = 0; + struct title_button *button, *button_tmp; + wl_list_for_each_safe(button, button_tmp, &rc.title_buttons_left, link) { + wl_list_remove(&button->link); + zfree(button); + } + wl_list_for_each_safe(button, button_tmp, &rc.title_buttons_right, link) { + wl_list_remove(&button->link); + zfree(button); + } rc.title_layout_loaded = false; } static void -fill_title_layout(const char *content) +fill_title_layout(char *content) { clear_title_layout(); + struct wl_list *sections[] = { + &rc.title_buttons_left, + &rc.title_buttons_right, + }; + gchar **parts = g_strsplit(content, ":", -1); if (g_strv_length(parts) != 2) { @@ -191,10 +230,9 @@ fill_title_layout(const char *content) } uint32_t found_buttons = 0; - fill_section(parts[0], rc.title_buttons_left, - &rc.nr_title_buttons_left, &found_buttons); - fill_section(parts[1], rc.title_buttons_right, - &rc.nr_title_buttons_right, &found_buttons); + for (size_t i = 0; parts[i]; ++i) { + fill_section(parts[i], sections[i], &found_buttons); + } rc.title_layout_loaded = true; err: @@ -202,29 +240,32 @@ err: } static void -fill_usable_area_override(xmlNode *node) +fill_usable_area_override(char *nodename, char *content, struct parser_state *state) { - struct usable_area_override *usable_area_override = - znew(*usable_area_override); - wl_list_append(&rc.usable_area_overrides, &usable_area_override->link); - - xmlNode *child; - char *key, *content; - LAB_XML_FOR_EACH(node, child, key, content) { - if (!strcmp(key, "output")) { - xstrdup_replace(usable_area_override->output, content); - } else if (!strcmp(key, "left")) { - usable_area_override->margin.left = atoi(content); - } else if (!strcmp(key, "right")) { - usable_area_override->margin.right = atoi(content); - } else if (!strcmp(key, "top")) { - usable_area_override->margin.top = atoi(content); - } else if (!strcmp(key, "bottom")) { - usable_area_override->margin.bottom = atoi(content); - } else { - wlr_log(WLR_ERROR, "Unexpected data usable-area-override " - "parser: %s=\"%s\"", key, content); - } + if (!strcasecmp(nodename, "margin")) { + state->current_usable_area_override = znew(*state->current_usable_area_override); + wl_list_append(&rc.usable_area_overrides, + &state->current_usable_area_override->link); + return; + } + string_truncate_at_pattern(nodename, ".margin"); + if (!content) { + /* nop */ + } else if (!state->current_usable_area_override) { + wlr_log(WLR_ERROR, "no usable-area-override object"); + } else if (!strcmp(nodename, "output")) { + xstrdup_replace(state->current_usable_area_override->output, content); + } else if (!strcmp(nodename, "left")) { + state->current_usable_area_override->margin.left = atoi(content); + } else if (!strcmp(nodename, "right")) { + state->current_usable_area_override->margin.right = atoi(content); + } else if (!strcmp(nodename, "top")) { + state->current_usable_area_override->margin.top = atoi(content); + } else if (!strcmp(nodename, "bottom")) { + state->current_usable_area_override->margin.bottom = atoi(content); + } else { + wlr_log(WLR_ERROR, "Unexpected data usable-area-override parser: %s=\"%s\"", + nodename, content); } } @@ -244,79 +285,81 @@ set_property(const char *str, enum property *variable) } static void -fill_window_rule(xmlNode *node) +fill_window_rule(char *nodename, char *content, struct parser_state *state) { - struct window_rule *window_rule = znew(*window_rule); - window_rule->window_type = LAB_WINDOW_TYPE_INVALID; - wl_list_append(&rc.window_rules, &window_rule->link); - wl_list_init(&window_rule->actions); - - xmlNode *child; - char *key, *content; - LAB_XML_FOR_EACH(node, child, key, content) { - /* Criteria */ - if (!strcmp(key, "identifier")) { - xstrdup_replace(window_rule->identifier, content); - } else if (!strcmp(key, "title")) { - xstrdup_replace(window_rule->title, content); - } else if (!strcmp(key, "type")) { - window_rule->window_type = parse_window_type(content); - } else if (!strcasecmp(key, "matchOnce")) { - set_bool(content, &window_rule->match_once); - } else if (!strcasecmp(key, "sandboxEngine")) { - xstrdup_replace(window_rule->sandbox_engine, content); - } else if (!strcasecmp(key, "sandboxAppId")) { - xstrdup_replace(window_rule->sandbox_app_id, content); - - /* Event */ - } else if (!strcmp(key, "event")) { - /* - * This is just in readiness for adding any other types of - * events in the future. We default to onFirstMap anyway. - */ - if (!strcasecmp(content, "onFirstMap")) { - window_rule->event = LAB_WINDOW_RULE_EVENT_ON_FIRST_MAP; - } - - /* Properties */ - } else if (!strcasecmp(key, "serverDecoration")) { - set_property(content, &window_rule->server_decoration); - } else if (!strcasecmp(key, "iconPriority")) { - if (!strcasecmp(content, "client")) { - window_rule->icon_prefer_client = LAB_PROP_TRUE; - } else if (!strcasecmp(content, "server")) { - window_rule->icon_prefer_client = LAB_PROP_FALSE; - } else { - wlr_log(WLR_ERROR, - "Invalid value for window rule property 'iconPriority'"); - } - } else if (!strcasecmp(key, "skipTaskbar")) { - set_property(content, &window_rule->skip_taskbar); - } else if (!strcasecmp(key, "skipWindowSwitcher")) { - set_property(content, &window_rule->skip_window_switcher); - } else if (!strcasecmp(key, "ignoreFocusRequest")) { - set_property(content, &window_rule->ignore_focus_request); - } else if (!strcasecmp(key, "ignoreConfigureRequest")) { - set_property(content, &window_rule->ignore_configure_request); - } else if (!strcasecmp(key, "fixedPosition")) { - set_property(content, &window_rule->fixed_position); - } + if (!strcasecmp(nodename, "windowRule.windowRules")) { + state->current_window_rule = znew(*state->current_window_rule); + state->current_window_rule->window_type = -1; // Window types are >= 0 + wl_list_append(&rc.window_rules, &state->current_window_rule->link); + wl_list_init(&state->current_window_rule->actions); + return; } - append_parsed_actions(node, &window_rule->actions); -} + string_truncate_at_pattern(nodename, ".windowrule.windowrules"); + if (!content) { + /* nop */ + } else if (!state->current_window_rule) { + wlr_log(WLR_ERROR, "no window-rule"); -static void -fill_window_rules(xmlNode *node) -{ - /* TODO: make sure is empty here */ + /* Criteria */ + } else if (!strcmp(nodename, "identifier")) { + xstrdup_replace(state->current_window_rule->identifier, content); + } else if (!strcmp(nodename, "title")) { + xstrdup_replace(state->current_window_rule->title, content); + } else if (!strcmp(nodename, "type")) { + state->current_window_rule->window_type = parse_window_type(content); + } else if (!strcasecmp(nodename, "matchOnce")) { + set_bool(content, &state->current_window_rule->match_once); + } else if (!strcasecmp(nodename, "sandboxEngine")) { + xstrdup_replace(state->current_window_rule->sandbox_engine, content); + } else if (!strcasecmp(nodename, "sandboxAppId")) { + xstrdup_replace(state->current_window_rule->sandbox_app_id, content); - xmlNode *child; - char *key, *content; - LAB_XML_FOR_EACH(node, child, key, content) { - if (!strcasecmp(key, "windowRule")) { - fill_window_rule(child); + /* Event */ + } else if (!strcmp(nodename, "event")) { + /* + * This is just in readiness for adding any other types of + * events in the future. We default to onFirstMap anyway. + */ + if (!strcasecmp(content, "onFirstMap")) { + state->current_window_rule->event = LAB_WINDOW_RULE_EVENT_ON_FIRST_MAP; } + + /* Properties */ + } else if (!strcasecmp(nodename, "serverDecoration")) { + set_property(content, &state->current_window_rule->server_decoration); + } else if (!strcasecmp(nodename, "iconPriority")) { + if (!strcasecmp(content, "client")) { + state->current_window_rule->icon_prefer_client = LAB_PROP_TRUE; + } else if (!strcasecmp(content, "server")) { + state->current_window_rule->icon_prefer_client = LAB_PROP_FALSE; + } else { + wlr_log(WLR_ERROR, + "Invalid value for window rule property 'iconPriority'"); + } + } else if (!strcasecmp(nodename, "skipTaskbar")) { + set_property(content, &state->current_window_rule->skip_taskbar); + } else if (!strcasecmp(nodename, "skipWindowSwitcher")) { + set_property(content, &state->current_window_rule->skip_window_switcher); + } else if (!strcasecmp(nodename, "ignoreFocusRequest")) { + set_property(content, &state->current_window_rule->ignore_focus_request); + } else if (!strcasecmp(nodename, "ignoreConfigureRequest")) { + set_property(content, &state->current_window_rule->ignore_configure_request); + } else if (!strcasecmp(nodename, "fixedPosition")) { + set_property(content, &state->current_window_rule->fixed_position); + + /* Actions */ + } else if (!strcmp(nodename, "name.action")) { + state->current_window_rule_action = action_create(content); + if (state->current_window_rule_action) { + wl_list_append(&state->current_window_rule->actions, + &state->current_window_rule_action->link); + } + } else if (!state->current_window_rule_action) { + wlr_log(WLR_ERROR, "expect element first. " + "nodename: '%s' content: '%s'", nodename, content); + } else { + action_arg_from_xml_node(state->current_window_rule_action, nodename, content); } } @@ -331,244 +374,220 @@ clear_window_switcher_fields(void) } static void -fill_window_switcher_field(xmlNode *node) +fill_window_switcher_field(char *nodename, char *content, struct parser_state *state) { - struct window_switcher_field *field = znew(*field); - wl_list_append(&rc.window_switcher.fields, &field->link); - - xmlNode *child; - char *key, *content; - LAB_XML_FOR_EACH(node, child, key, content) { - osd_field_arg_from_xml_node(field, key, content); - } -} - -static void -fill_window_switcher_fields(xmlNode *node) -{ - clear_window_switcher_fields(); - - xmlNode *child; - char *key, *content; - LAB_XML_FOR_EACH(node, child, key, content) { - if (!strcasecmp(key, "field")) { - fill_window_switcher_field(child); - } - } -} - -static void -fill_region(xmlNode *node) -{ - struct region *region = znew(*region); - wl_list_append(&rc.regions, ®ion->link); - - xmlNode *child; - char *key, *content; - LAB_XML_FOR_EACH(node, child, key, content) { - if (!strcasecmp(key, "name")) { - xstrdup_replace(region->name, content); - } else if (strstr("xywidtheight", key) - && !strchr(content, '%')) { - wlr_log(WLR_ERROR, "Removing invalid region " - "'%s': %s='%s' misses a trailing %%", - region->name, key, content); - wl_list_remove(®ion->link); - zfree(region->name); - zfree(region); - return; - } else if (!strcmp(key, "x")) { - region->percentage.x = atoi(content); - } else if (!strcmp(key, "y")) { - region->percentage.y = atoi(content); - } else if (!strcmp(key, "width")) { - region->percentage.width = atoi(content); - } else if (!strcmp(key, "height")) { - region->percentage.height = atoi(content); - } else { - wlr_log(WLR_ERROR, "Unexpected data in region " - "parser: %s=\"%s\"", key, content); - } - } -} - -static void -fill_regions(xmlNode *node) -{ - /* TODO: make sure is empty here */ - - xmlNode *child; - char *key, *content; - LAB_XML_FOR_EACH(node, child, key, content) { - if (!strcasecmp(key, "region")) { - fill_region(child); - } - } -} - -static void -fill_action_query(struct action *action, xmlNode *node, struct view_query *query) -{ - xmlNode *child; - char *key, *content; - LAB_XML_FOR_EACH(node, child, key, content) { - if (!strcasecmp(key, "identifier")) { - xstrdup_replace(query->identifier, content); - } else if (!strcasecmp(key, "title")) { - xstrdup_replace(query->title, content); - } else if (!strcmp(key, "type")) { - query->window_type = parse_window_type(content); - } else if (!strcasecmp(key, "sandboxEngine")) { - xstrdup_replace(query->sandbox_engine, content); - } else if (!strcasecmp(key, "sandboxAppId")) { - xstrdup_replace(query->sandbox_app_id, content); - } else if (!strcasecmp(key, "shaded")) { - query->shaded = parse_tristate(content); - } else if (!strcasecmp(key, "maximized")) { - query->maximized = view_axis_parse(content); - } else if (!strcasecmp(key, "iconified")) { - query->iconified = parse_tristate(content); - } else if (!strcasecmp(key, "focused")) { - query->focused = parse_tristate(content); - } else if (!strcasecmp(key, "omnipresent")) { - query->omnipresent = parse_tristate(content); - } else if (!strcasecmp(key, "tiled")) { - query->tiled = lab_edge_parse(content, - /*tiled*/ true, /*any*/ true); - } else if (!strcasecmp(key, "tiled_region")) { - xstrdup_replace(query->tiled_region, content); - } else if (!strcasecmp(key, "desktop")) { - xstrdup_replace(query->desktop, content); - } else if (!strcasecmp(key, "decoration")) { - query->decoration = ssd_mode_parse(content); - } else if (!strcasecmp(key, "monitor")) { - xstrdup_replace(query->monitor, content); - } - } -} - -static void -parse_action_args(xmlNode *node, struct action *action) -{ - xmlNode *child; - char *key, *content; - LAB_XML_FOR_EACH(node, child, key, content) { - if (!strcasecmp(key, "query")) { - struct wl_list *querylist = - action_get_querylist(action, "query"); - if (!querylist) { - action_arg_add_querylist(action, "query"); - querylist = action_get_querylist(action, "query"); - } - struct view_query *query = view_query_create(); - fill_action_query(action, child, query); - wl_list_append(querylist, &query->link); - } else if (!strcasecmp(key, "then")) { - struct wl_list *actions = - action_get_actionlist(action, "then"); - if (!actions) { - action_arg_add_actionlist(action, "then"); - actions = action_get_actionlist(action, "then"); - } - append_parsed_actions(child, actions); - } else if (!strcasecmp(key, "else")) { - struct wl_list *actions = - action_get_actionlist(action, "else"); - if (!actions) { - action_arg_add_actionlist(action, "else"); - actions = action_get_actionlist(action, "else"); - } - append_parsed_actions(child, actions); - } else if (!strcasecmp(key, "none")) { - struct wl_list *actions = - action_get_actionlist(action, "none"); - if (!actions) { - action_arg_add_actionlist(action, "none"); - actions = action_get_actionlist(action, "none"); - } - append_parsed_actions(child, actions); - } else if (!strcasecmp(key, "name")) { - /* Ignore */ - } else if (lab_xml_node_is_leaf(child)) { - /* Handle normal action args */ - char buffer[256]; - char *node_name = nodename(child, buffer, sizeof(buffer)); - action_arg_from_xml_node(action, node_name, content); - } else { - /* Handle nested args like in ShowMenu */ - parse_action_args(child, action); - } - } -} - -static struct action * -parse_action(xmlNode *node) -{ - char name[256]; - struct action *action = NULL; - - if (lab_xml_get_string(node, "name", name, sizeof(name))) { - action = action_create(name); - } - if (!action) { - return NULL; - } - - parse_action_args(node, action); - return action; -} - -void -append_parsed_actions(xmlNode *node, struct wl_list *list) -{ - xmlNode *child; - char *key, *content; - LAB_XML_FOR_EACH(node, child, key, content) { - if (strcasecmp(key, "action")) { - continue; - } - if (lab_xml_node_is_leaf(child)) { - /* - * A mousebind contains two types of "action" nodes: - * - * - * - * The first node (action="Click") is skipped. - */ - continue; - } - struct action *action = parse_action(child); - if (action) { - wl_list_append(list, &action->link); - } - } -} - -static void -fill_keybind(xmlNode *node) -{ - struct keybind *keybind = NULL; - char keyname[256]; - - if (lab_xml_get_string(node, "key", keyname, sizeof(keyname))) { - keybind = keybind_create(keyname); - if (!keybind) { - wlr_log(WLR_ERROR, "Invalid keybind: %s", keyname); - } - } - if (!keybind) { + if (!strcasecmp(nodename, "field.fields.windowswitcher")) { + state->current_field = osd_field_create(); + wl_list_append(&rc.window_switcher.fields, &state->current_field->link); return; } - 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); - - append_parsed_actions(node, &keybind->actions); + string_truncate_at_pattern(nodename, ".field.fields.windowswitcher"); + if (!content) { + /* intentionally left empty */ + } else if (!state->current_field) { + wlr_log(WLR_ERROR, "no "); + } else { + osd_field_arg_from_xml_node(state->current_field, nodename, content); + } } static void -fill_mousebind(xmlNode *node, const char *context) +fill_region(char *nodename, char *content, struct parser_state *state) +{ + string_truncate_at_pattern(nodename, ".region.regions"); + + if (!strcasecmp(nodename, "region.regions")) { + state->current_region = znew(*state->current_region); + wl_list_append(&rc.regions, &state->current_region->link); + } else if (!content) { + /* intentionally left empty */ + } else if (!state->current_region) { + wlr_log(WLR_ERROR, "Expecting current_region->name) { + state->current_region->name = xstrdup(content); + } + } else if (strstr("xywidtheight", nodename) && !strchr(content, '%')) { + wlr_log(WLR_ERROR, "Removing invalid region '%s': %s='%s' misses" + " a trailing %%", state->current_region->name, nodename, content); + wl_list_remove(&state->current_region->link); + zfree(state->current_region->name); + zfree(state->current_region); + } else if (!strcmp(nodename, "x")) { + state->current_region->percentage.x = atoi(content); + } else if (!strcmp(nodename, "y")) { + state->current_region->percentage.y = atoi(content); + } else if (!strcmp(nodename, "width")) { + state->current_region->percentage.width = atoi(content); + } else if (!strcmp(nodename, "height")) { + state->current_region->percentage.height = atoi(content); + } else { + wlr_log(WLR_ERROR, "Unexpected data in region parser: %s=\"%s\"", + nodename, content); + } +} + +static void +fill_action_query(char *nodename, char *content, struct action *action, struct parser_state *state) +{ + if (!action) { + wlr_log(WLR_ERROR, "No parent action for query: %s=%s", nodename, content); + return; + } + + string_truncate_at_pattern(nodename, ".keybind.keyboard"); + string_truncate_at_pattern(nodename, ".mousebind.context.mouse"); + + if (!strcasecmp(nodename, "query.action")) { + state->current_view_query = NULL; + } + + string_truncate_at_pattern(nodename, ".query.action"); + + if (!content) { + return; + } + + if (!state->current_view_query) { + struct wl_list *queries = action_get_querylist(action, "query"); + if (!queries) { + action_arg_add_querylist(action, "query"); + queries = action_get_querylist(action, "query"); + } + state->current_view_query = view_query_create(); + wl_list_append(queries, &state->current_view_query->link); + } + + if (!strcasecmp(nodename, "identifier")) { + xstrdup_replace(state->current_view_query->identifier, content); + } else if (!strcasecmp(nodename, "title")) { + xstrdup_replace(state->current_view_query->title, content); + } else if (!strcmp(nodename, "type")) { + state->current_view_query->window_type = parse_window_type(content); + } else if (!strcasecmp(nodename, "sandboxEngine")) { + xstrdup_replace(state->current_view_query->sandbox_engine, content); + } else if (!strcasecmp(nodename, "sandboxAppId")) { + xstrdup_replace(state->current_view_query->sandbox_app_id, content); + } else if (!strcasecmp(nodename, "shaded")) { + state->current_view_query->shaded = parse_three_state(content); + } else if (!strcasecmp(nodename, "maximized")) { + state->current_view_query->maximized = view_axis_parse(content); + } else if (!strcasecmp(nodename, "iconified")) { + state->current_view_query->iconified = parse_three_state(content); + } else if (!strcasecmp(nodename, "focused")) { + state->current_view_query->focused = parse_three_state(content); + } else if (!strcasecmp(nodename, "omnipresent")) { + state->current_view_query->omnipresent = parse_three_state(content); + } else if (!strcasecmp(nodename, "tiled")) { + state->current_view_query->tiled = view_edge_parse(content); + } else if (!strcasecmp(nodename, "tiled_region")) { + xstrdup_replace(state->current_view_query->tiled_region, content); + } else if (!strcasecmp(nodename, "desktop")) { + xstrdup_replace(state->current_view_query->desktop, content); + } else if (!strcasecmp(nodename, "decoration")) { + state->current_view_query->decoration = ssd_mode_parse(content); + } else if (!strcasecmp(nodename, "monitor")) { + xstrdup_replace(state->current_view_query->monitor, content); + } +} + +static void +fill_child_action(char *nodename, char *content, struct action *parent, + const char *branch_name, struct parser_state *state) +{ + if (!parent) { + wlr_log(WLR_ERROR, "No parent action for branch: %s=%s", nodename, content); + return; + } + + string_truncate_at_pattern(nodename, ".keybind.keyboard"); + string_truncate_at_pattern(nodename, ".mousebind.context.mouse"); + string_truncate_at_pattern(nodename, ".then.action"); + string_truncate_at_pattern(nodename, ".else.action"); + string_truncate_at_pattern(nodename, ".none.action"); + + if (!strcasecmp(nodename, "action")) { + state->current_child_action = NULL; + } + + if (!content) { + return; + } + + struct wl_list *siblings = action_get_actionlist(parent, branch_name); + if (!siblings) { + action_arg_add_actionlist(parent, branch_name); + siblings = action_get_actionlist(parent, branch_name); + } + + if (!strcasecmp(nodename, "name.action")) { + if (!strcasecmp(content, "If") || !strcasecmp(content, "ForEach")) { + wlr_log(WLR_ERROR, "action '%s' cannot be a child action", content); + return; + } + state->current_child_action = action_create(content); + if (state->current_child_action) { + wl_list_append(siblings, &state->current_child_action->link); + } + } else if (!state->current_child_action) { + wlr_log(WLR_ERROR, "expect element first. " + "nodename: '%s' content: '%s'", nodename, content); + } else { + action_arg_from_xml_node(state->current_child_action, nodename, content); + } +} + +static void +fill_keybind(char *nodename, char *content, struct parser_state *state) +{ + if (!content) { + return; + } + string_truncate_at_pattern(nodename, ".keybind.keyboard"); + if (!strcmp(nodename, "key")) { + state->current_keybind = keybind_create(content); + state->current_keybind_action = NULL; + /* + * If an invalid keybind has been provided, + * keybind_create() complains. + */ + if (!state->current_keybind) { + wlr_log(WLR_ERROR, "Invalid keybind: %s", content); + return; + } + } else if (!state->current_keybind) { + wlr_log(WLR_ERROR, "expect element first. " + "nodename: '%s' content: '%s'", nodename, content); + } else if (!strcasecmp(nodename, "onRelease")) { + set_bool(content, &state->current_keybind->on_release); + } else if (!strcasecmp(nodename, "layoutDependent")) { + set_bool(content, &state->current_keybind->use_syms_only); + } else if (!strcasecmp(nodename, "allowWhenLocked")) { + set_bool(content, &state->current_keybind->allow_when_locked); + } else if (!strcmp(nodename, "name.action")) { + state->current_keybind_action = action_create(content); + if (state->current_keybind_action) { + wl_list_append(&state->current_keybind->actions, + &state->current_keybind_action->link); + } + } else if (!state->current_keybind_action) { + wlr_log(WLR_ERROR, "expect element first. " + "nodename: '%s' content: '%s'", nodename, content); + } else { + /* + * Here we deal with action sub-elements such as , , + * , and so on. This is common to key- and + * mousebinds. + */ + action_arg_from_xml_node(state->current_keybind_action, nodename, content); + } +} + +static void +fill_mousebind(char *nodename, char *content, struct parser_state *state) { /* * Example of what we are parsing: @@ -579,87 +598,72 @@ fill_mousebind(xmlNode *node, const char *context) * */ - wlr_log(WLR_INFO, "create mousebind for %s", context); - struct mousebind *mousebind = mousebind_create(context); + if (!state->current_mouse_context) { + wlr_log(WLR_ERROR, "expect element first. " + "nodename: '%s' content: '%s'", nodename, content); + return; + } else if (!strcmp(nodename, "mousebind.context.mouse")) { + wlr_log(WLR_INFO, "create mousebind for %s", + state->current_mouse_context); + state->current_mousebind = mousebind_create(state->current_mouse_context); + state->current_mousebind_action = NULL; + return; + } else if (!content) { + return; + } - char buf[256]; - if (lab_xml_get_string(node, "button", buf, sizeof(buf))) { - mousebind->button = mousebind_button_from_str( - buf, &mousebind->modifiers); - } - if (lab_xml_get_string(node, "direction", buf, sizeof(buf))) { - mousebind->direction = mousebind_direction_from_str( - buf, &mousebind->modifiers); - } - if (lab_xml_get_string(node, "action", buf, sizeof(buf))) { + string_truncate_at_pattern(nodename, ".mousebind.context.mouse"); + if (!state->current_mousebind) { + wlr_log(WLR_ERROR, + "expect element first. " + "nodename: '%s' content: '%s'", nodename, content); + } else if (!strcmp(nodename, "button")) { + state->current_mousebind->button = mousebind_button_from_str(content, + &state->current_mousebind->modifiers); + } else if (!strcmp(nodename, "direction")) { + state->current_mousebind->direction = mousebind_direction_from_str(content, + &state->current_mousebind->modifiers); + } else if (!strcmp(nodename, "action")) { /* */ - mousebind->mouse_event = mousebind_event_from_str(buf); - } - - append_parsed_actions(node, &mousebind->actions); -} - -static void -fill_mouse_context(xmlNode *node) -{ - char context_name[256]; - if (!lab_xml_get_string(node, "name", context_name, sizeof(context_name))) { - return; - } - - xmlNode *child; - char *key, *content; - LAB_XML_FOR_EACH(node, child, key, content) { - if (!strcasecmp(key, "mousebind")) { - fill_mousebind(child, context_name); + state->current_mousebind->mouse_event = + mousebind_event_from_str(content); + } else if (!strcmp(nodename, "name.action")) { + state->current_mousebind_action = action_create(content); + if (state->current_mousebind_action) { + wl_list_append(&state->current_mousebind->actions, + &state->current_mousebind_action->link); } + } else if (!state->current_mousebind_action) { + wlr_log(WLR_ERROR, "expect element first. " + "nodename: '%s' content: '%s'", nodename, content); + } else { + action_arg_from_xml_node(state->current_mousebind_action, nodename, content); } } static void -fill_touch(xmlNode *node) +fill_touch(char *nodename, char *content, struct parser_state *state) { - struct touch_config_entry *touch_config = znew(*touch_config); - wl_list_append(&rc.touch_configs, &touch_config->link); - - xmlNode *child; - char *key, *content; - LAB_XML_FOR_EACH(node, child, key, content) { - if (!strcasecmp(key, "deviceName")) { - xstrdup_replace(touch_config->device_name, content); - } else if (!strcasecmp(key, "mapToOutput")) { - xstrdup_replace(touch_config->output_name, content); - } else if (!strcasecmp(key, "mouseEmulation")) { - set_bool(content, &touch_config->force_mouse_emulation); - } else { - wlr_log(WLR_ERROR, "Unexpected data in touch parser: %s=\"%s\"", - key, content); - } - } -} - -static void -fill_tablet_button_map(xmlNode *node) -{ - uint32_t map_from; - uint32_t map_to; - char buf[256]; - - if (lab_xml_get_string(node, "button", buf, sizeof(buf))) { - map_from = tablet_button_from_str(buf); - } else { - wlr_log(WLR_ERROR, "Invalid 'button' argument for tablet button mapping"); + if (!strcasecmp(nodename, "touch")) { + state->current_touch = znew(*state->current_touch); + wl_list_append(&rc.touch_configs, &state->current_touch->link); return; } - if (lab_xml_get_string(node, "to", buf, sizeof(buf))) { - map_to = mousebind_button_from_str(buf, NULL); - } else { - wlr_log(WLR_ERROR, "Invalid 'to' argument for tablet button mapping"); + if (!content) { return; } - tablet_button_mapping_add(map_from, map_to); + if (!strcasecmp(nodename, "deviceName.touch")) { + xstrdup_replace(state->current_touch->device_name, content); + } else if (!strcasecmp(nodename, "mapToOutput.touch")) { + xstrdup_replace(state->current_touch->output_name, content); + } else if (!strcasecmp(nodename, "mouseEmulation.touch")) { + set_bool(content, &state->current_touch->force_mouse_emulation); + } else { + wlr_log(WLR_ERROR, "Unexpected data in touch parser: %s=\"%s\"", + nodename, content); + } } static int @@ -701,105 +705,112 @@ err: } static void -fill_libinput_category(xmlNode *node) +fill_libinput_category(char *nodename, char *content, struct parser_state *state) { /* * Create a new profile (libinput-category) on `` * so that the 'default' profile can be created without even providing a * category="" attribute (same as ...) */ - struct libinput_category *category = libinput_category_create(); + if (!strcmp(nodename, "device.libinput")) { + state->current_libinput_category = libinput_category_create(); + } - xmlNode *child; - char *key, *content; - LAB_XML_FOR_EACH(node, child, key, content) { - if (string_null_or_empty(content)) { - continue; + if (!content) { + return; + } + + if (!state->current_libinput_category) { + return; + } + + string_truncate_at_pattern(nodename, ".device.libinput"); + + if (!strcmp(nodename, "category")) { + /* + * First we try to get a type based on a number of pre-defined + * terms, for example: 'default', 'touch', 'touchpad' and + * 'non-touch' + */ + state->current_libinput_category->type = get_device_type(content); + + /* + * If we couldn't match against any of those terms, we use the + * provided value to define the device name that the settings + * should be applicable to. + */ + if (state->current_libinput_category->type == LAB_LIBINPUT_DEVICE_NONE) { + xstrdup_replace(state->current_libinput_category->name, content); } - if (!strcmp(key, "category")) { - /* - * First we try to get a type based on a number of - * pre-defined terms, for example: 'default', 'touch', - * 'touchpad' and 'non-touch' - */ - category->type = get_device_type(content); - - /* - * If we couldn't match against any of those terms, we - * use the provided value to define the device name - * that the settings should be applicable to. - */ - if (category->type == LAB_LIBINPUT_DEVICE_NONE) { - xstrdup_replace(category->name, content); - } - } else if (!strcasecmp(key, "naturalScroll")) { - set_bool_as_int(content, &category->natural_scroll); - } else if (!strcasecmp(key, "leftHanded")) { - set_bool_as_int(content, &category->left_handed); - } else if (!strcasecmp(key, "pointerSpeed")) { - set_float(content, &category->pointer_speed); - if (category->pointer_speed < -1) { - category->pointer_speed = -1; - } else if (category->pointer_speed > 1) { - category->pointer_speed = 1; - } - } else if (!strcasecmp(key, "tap")) { - int ret = parse_bool(content, -1); - if (ret < 0) { - continue; - } - category->tap = ret - ? LIBINPUT_CONFIG_TAP_ENABLED - : LIBINPUT_CONFIG_TAP_DISABLED; - } else if (!strcasecmp(key, "tapButtonMap")) { - if (!strcmp(content, "lrm")) { - category->tap_button_map = - LIBINPUT_CONFIG_TAP_MAP_LRM; - } else if (!strcmp(content, "lmr")) { - category->tap_button_map = - LIBINPUT_CONFIG_TAP_MAP_LMR; - } else { - wlr_log(WLR_ERROR, "invalid tapButtonMap"); - } - } else if (!strcasecmp(key, "tapAndDrag")) { - int ret = parse_bool(content, -1); - if (ret < 0) { - continue; - } - category->tap_and_drag = ret - ? LIBINPUT_CONFIG_DRAG_ENABLED - : LIBINPUT_CONFIG_DRAG_DISABLED; - } else if (!strcasecmp(key, "dragLock")) { - if (!strcasecmp(content, "timeout")) { - /* "timeout" enables drag-lock with timeout */ - category->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED; - continue; - } - int ret = parse_bool(content, -1); - if (ret < 0) { - continue; - } - /* "yes" enables drag-lock, without timeout if libinput >= 1.27 */ - int enabled = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED; + } else if (!strcasecmp(nodename, "naturalScroll")) { + set_bool_as_int(content, &state->current_libinput_category->natural_scroll); + } else if (!strcasecmp(nodename, "leftHanded")) { + set_bool_as_int(content, &state->current_libinput_category->left_handed); + } else if (!strcasecmp(nodename, "pointerSpeed")) { + set_float(content, &state->current_libinput_category->pointer_speed); + if (state->current_libinput_category->pointer_speed < -1) { + state->current_libinput_category->pointer_speed = -1; + } else if (state->current_libinput_category->pointer_speed > 1) { + state->current_libinput_category->pointer_speed = 1; + } + } else if (!strcasecmp(nodename, "tap")) { + int ret = parse_bool(content, -1); + if (ret < 0) { + return; + } + state->current_libinput_category->tap = ret + ? LIBINPUT_CONFIG_TAP_ENABLED + : LIBINPUT_CONFIG_TAP_DISABLED; + } else if (!strcasecmp(nodename, "tapButtonMap")) { + if (!strcmp(content, "lrm")) { + state->current_libinput_category->tap_button_map = + LIBINPUT_CONFIG_TAP_MAP_LRM; + } else if (!strcmp(content, "lmr")) { + state->current_libinput_category->tap_button_map = + LIBINPUT_CONFIG_TAP_MAP_LMR; + } else { + wlr_log(WLR_ERROR, "invalid tapButtonMap"); + } + } else if (!strcasecmp(nodename, "tapAndDrag")) { + int ret = parse_bool(content, -1); + if (ret < 0) { + return; + } + state->current_libinput_category->tap_and_drag = ret + ? LIBINPUT_CONFIG_DRAG_ENABLED + : LIBINPUT_CONFIG_DRAG_DISABLED; + } else if (!strcasecmp(nodename, "dragLock")) { + if (!strcasecmp(content, "timeout")) { + /* "timeout" enables drag-lock with timeout */ + state->current_libinput_category->drag_lock = + LIBINPUT_CONFIG_DRAG_LOCK_ENABLED; + return; + } + int ret = parse_bool(content, -1); + if (ret < 0) { + return; + } + /* "yes" enables drag-lock, without timeout if libinput >= 1.27 */ + int enabled = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED; #if HAVE_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY - enabled = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY; + enabled = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY; #endif - category->drag_lock = ret ? - enabled : LIBINPUT_CONFIG_DRAG_LOCK_DISABLED; - } else if (!strcasecmp(key, "threeFingerDrag")) { + state->current_libinput_category->drag_lock = ret ? + enabled : LIBINPUT_CONFIG_DRAG_LOCK_DISABLED; + } else if (!strcasecmp(nodename, "threeFingerDrag")) { #if HAVE_LIBINPUT_CONFIG_3FG_DRAG_ENABLED_3FG if (!strcmp(content, "3")) { - category->three_finger_drag = + state->current_libinput_category->three_finger_drag = LIBINPUT_CONFIG_3FG_DRAG_ENABLED_3FG; } else if (!strcmp(content, "4")) { - category->three_finger_drag = + state->current_libinput_category->three_finger_drag = LIBINPUT_CONFIG_3FG_DRAG_ENABLED_4FG; } else { int ret = parse_bool(content, -1); if (ret < 0) { - continue; + return; } - category->three_finger_drag = ret + state->current_libinput_category->three_finger_drag = ret ? LIBINPUT_CONFIG_3FG_DRAG_ENABLED_3FG : LIBINPUT_CONFIG_3FG_DRAG_DISABLED; } @@ -807,82 +818,80 @@ fill_libinput_category(xmlNode *node) wlr_log(WLR_ERROR, " is only" " supported in libinput >= 1.28"); #endif - } else if (!strcasecmp(key, "accelProfile")) { - category->accel_profile = - get_accel_profile(content); - } else if (!strcasecmp(key, "middleEmulation")) { - int ret = parse_bool(content, -1); - if (ret < 0) { - continue; - } - category->middle_emu = ret - ? LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED - : LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; - } else if (!strcasecmp(key, "disableWhileTyping")) { - int ret = parse_bool(content, -1); - if (ret < 0) { - continue; - } - category->dwt = ret - ? LIBINPUT_CONFIG_DWT_ENABLED - : LIBINPUT_CONFIG_DWT_DISABLED; - } else if (!strcasecmp(key, "clickMethod")) { - if (!strcasecmp(content, "none")) { - category->click_method = - LIBINPUT_CONFIG_CLICK_METHOD_NONE; - } else if (!strcasecmp(content, "clickfinger")) { - category->click_method = - LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; - } else if (!strcasecmp(content, "buttonAreas")) { - category->click_method = - LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; - } else { - wlr_log(WLR_ERROR, "invalid clickMethod"); - } - } else if (!strcasecmp(key, "scrollMethod")) { - if (!strcasecmp(content, "none")) { - category->scroll_method = - LIBINPUT_CONFIG_SCROLL_NO_SCROLL; - } else if (!strcasecmp(content, "edge")) { - category->scroll_method = - LIBINPUT_CONFIG_SCROLL_EDGE; - } else if (!strcasecmp(content, "twofinger")) { - category->scroll_method = - LIBINPUT_CONFIG_SCROLL_2FG; - } else { - wlr_log(WLR_ERROR, "invalid scrollMethod"); - } - } else if (!strcasecmp(key, "sendEventsMode")) { - category->send_events_mode = - get_send_events_mode(content); - } else if (!strcasecmp(key, "calibrationMatrix")) { - errno = 0; - category->have_calibration_matrix = true; - float *mat = category->calibration_matrix; - gchar **elements = g_strsplit(content, " ", -1); - guint i = 0; - for (; elements[i]; ++i) { - char *end_str = NULL; - mat[i] = strtof(elements[i], &end_str); - if (errno == ERANGE || *end_str != '\0' || i == 6 - || *elements[i] == '\0') { - wlr_log(WLR_ERROR, "invalid calibration " - "matrix element %s (index %d), " - "expect six floats", elements[i], i); - category->have_calibration_matrix = false; - errno = 0; - break; - } - } - if (i != 6 && category->have_calibration_matrix) { - wlr_log(WLR_ERROR, "wrong number of calibration " - "matrix elements, expected 6, got %d", i); - category->have_calibration_matrix = false; - } - g_strfreev(elements); - } else if (!strcasecmp(key, "scrollFactor")) { - set_double(content, &category->scroll_factor); + } else if (!strcasecmp(nodename, "accelProfile")) { + state->current_libinput_category->accel_profile = + get_accel_profile(content); + } else if (!strcasecmp(nodename, "middleEmulation")) { + int ret = parse_bool(content, -1); + if (ret < 0) { + return; } + state->current_libinput_category->middle_emu = ret + ? LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED + : LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; + } else if (!strcasecmp(nodename, "disableWhileTyping")) { + int ret = parse_bool(content, -1); + if (ret < 0) { + return; + } + state->current_libinput_category->dwt = ret + ? LIBINPUT_CONFIG_DWT_ENABLED + : LIBINPUT_CONFIG_DWT_DISABLED; + } else if (!strcasecmp(nodename, "clickMethod")) { + if (!strcasecmp(content, "none")) { + state->current_libinput_category->click_method = + LIBINPUT_CONFIG_CLICK_METHOD_NONE; + } else if (!strcasecmp(content, "clickfinger")) { + state->current_libinput_category->click_method = + LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; + } else if (!strcasecmp(content, "buttonAreas")) { + state->current_libinput_category->click_method = + LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; + } else { + wlr_log(WLR_ERROR, "invalid clickMethod"); + } + } else if (!strcasecmp(nodename, "scrollMethod")) { + if (!strcasecmp(content, "none")) { + state->current_libinput_category->scroll_method = + LIBINPUT_CONFIG_SCROLL_NO_SCROLL; + } else if (!strcasecmp(content, "edge")) { + state->current_libinput_category->scroll_method = + LIBINPUT_CONFIG_SCROLL_EDGE; + } else if (!strcasecmp(content, "twofinger")) { + state->current_libinput_category->scroll_method = + LIBINPUT_CONFIG_SCROLL_2FG; + } else { + wlr_log(WLR_ERROR, "invalid scrollMethod"); + } + } else if (!strcasecmp(nodename, "sendEventsMode")) { + state->current_libinput_category->send_events_mode = + get_send_events_mode(content); + } else if (!strcasecmp(nodename, "calibrationMatrix")) { + errno = 0; + state->current_libinput_category->have_calibration_matrix = true; + float *mat = state->current_libinput_category->calibration_matrix; + gchar **elements = g_strsplit(content, " ", -1); + guint i = 0; + for (; elements[i]; ++i) { + char *end_str = NULL; + mat[i] = strtof(elements[i], &end_str); + if (errno == ERANGE || *end_str != '\0' || i == 6 || *elements[i] == '\0') { + wlr_log(WLR_ERROR, "invalid calibration matrix element" + " %s (index %d), expect six floats", + elements[i], i); + state->current_libinput_category->have_calibration_matrix = false; + errno = 0; + break; + } + } + if (i != 6 && state->current_libinput_category->have_calibration_matrix) { + wlr_log(WLR_ERROR, "wrong number of calibration matrix elements," + " expected 6, got %d", i); + state->current_libinput_category->have_calibration_matrix = false; + } + g_strfreev(elements); + } else if (!strcasecmp(nodename, "scrollFactor")) { + set_double(content, &state->current_libinput_category->scroll_factor); } } @@ -930,6 +939,49 @@ set_font_attr(struct font *font, const char *nodename, const char *content) } } +static void +fill_font(char *nodename, char *content, enum font_place place) +{ + if (!content) { + return; + } + string_truncate_at_pattern(nodename, ".font.theme"); + + switch (place) { + case FONT_PLACE_NONE: + /* + * If is used without a place="" + * attribute, we set all font variables + */ + set_font_attr(&rc.font_activewindow, nodename, content); + set_font_attr(&rc.font_inactivewindow, nodename, content); + set_font_attr(&rc.font_menuheader, nodename, content); + set_font_attr(&rc.font_menuitem, nodename, content); + set_font_attr(&rc.font_osd, nodename, content); + break; + case FONT_PLACE_ACTIVEWINDOW: + set_font_attr(&rc.font_activewindow, nodename, content); + break; + case FONT_PLACE_INACTIVEWINDOW: + set_font_attr(&rc.font_inactivewindow, nodename, content); + break; + case FONT_PLACE_MENUHEADER: + set_font_attr(&rc.font_menuheader, nodename, content); + break; + case FONT_PLACE_MENUITEM: + set_font_attr(&rc.font_menuitem, nodename, content); + break; + case FONT_PLACE_OSD: + set_font_attr(&rc.font_osd, nodename, content); + break; + + /* TODO: implement for all font places */ + + default: + break; + } +} + static enum font_place enum_font_place(const char *place) { @@ -951,54 +1003,6 @@ enum_font_place(const char *place) return FONT_PLACE_UNKNOWN; } -static void -fill_font(xmlNode *node) -{ - enum font_place font_place = FONT_PLACE_NONE; - char buf[256]; - if (lab_xml_get_string(node, "place", buf, sizeof(buf))) { - font_place = enum_font_place(buf); - } - - xmlNode *child; - char *key, *content; - LAB_XML_FOR_EACH(node, child, key, content) { - switch (font_place) { - case FONT_PLACE_NONE: - /* - * If is used without a - * place="" attribute, we set all font variables - */ - set_font_attr(&rc.font_activewindow, key, content); - set_font_attr(&rc.font_inactivewindow, key, content); - set_font_attr(&rc.font_menuheader, key, content); - set_font_attr(&rc.font_menuitem, key, content); - set_font_attr(&rc.font_osd, key, content); - break; - case FONT_PLACE_ACTIVEWINDOW: - set_font_attr(&rc.font_activewindow, key, content); - break; - case FONT_PLACE_INACTIVEWINDOW: - set_font_attr(&rc.font_inactivewindow, key, content); - break; - case FONT_PLACE_MENUHEADER: - set_font_attr(&rc.font_menuheader, key, content); - break; - case FONT_PLACE_MENUITEM: - set_font_attr(&rc.font_menuitem, key, content); - break; - case FONT_PLACE_OSD: - set_font_attr(&rc.font_osd, key, content); - break; - - /* TODO: implement for all font places */ - - default: - break; - } - } -} - static void set_adaptive_sync_mode(const char *str, enum adaptive_sync_mode *variable) { @@ -1028,10 +1032,17 @@ set_tearing_mode(const char *str, enum tearing_mode *variable) } } -/* Returns true if the node's children should also be traversed */ -static bool -entry(xmlNode *node, char *nodename, char *content) +static void +entry(xmlNode *node, char *nodename, char *content, struct parser_state *state) { + /* current */ + static enum font_place font_place = FONT_PLACE_NONE; + + static uint32_t button_map_from; + + if (!nodename) { + return; + } string_truncate_at_pattern(nodename, ".openbox_config"); string_truncate_at_pattern(nodename, ".labwc_config"); @@ -1039,45 +1050,109 @@ entry(xmlNode *node, char *nodename, char *content) printf("%s: %s\n", nodename, content); } - /* handle nested nodes */ - if (!strcasecmp(nodename, "margin")) { - fill_usable_area_override(node); - } else if (!strcasecmp(nodename, "keybind.keyboard")) { - fill_keybind(node); - } else if (!strcasecmp(nodename, "context.mouse")) { - fill_mouse_context(node); - } else if (!strcasecmp(nodename, "touch")) { - fill_touch(node); - } else if (!strcasecmp(nodename, "device.libinput")) { - fill_libinput_category(node); - } else if (!strcasecmp(nodename, "regions")) { - fill_regions(node); - } else if (!strcasecmp(nodename, "fields.windowSwitcher")) { - fill_window_switcher_fields(node); - } else if (!strcasecmp(nodename, "windowRules")) { - fill_window_rules(node); - } else if (!strcasecmp(nodename, "font.theme")) { - fill_font(node); - } else if (!strcasecmp(nodename, "map.tablet")) { - fill_tablet_button_map(node); + if (state->in_usable_area_override) { + fill_usable_area_override(nodename, content, state); + } + if (state->in_keybind) { + if (state->in_action_query) { + fill_action_query(nodename, content, + state->current_keybind_action, state); + } else if (state->in_action_then_branch) { + fill_child_action(nodename, content, + state->current_keybind_action, "then", state); + } else if (state->in_action_else_branch) { + fill_child_action(nodename, content, + state->current_keybind_action, "else", state); + } else if (state->in_action_none_branch) { + fill_child_action(nodename, content, + state->current_keybind_action, "none", state); + } else { + fill_keybind(nodename, content, state); + } + } + if (state->in_mousebind) { + if (state->in_action_query) { + fill_action_query(nodename, content, + state->current_mousebind_action, state); + } else if (state->in_action_then_branch) { + fill_child_action(nodename, content, + state->current_mousebind_action, "then", state); + } else if (state->in_action_else_branch) { + fill_child_action(nodename, content, + state->current_mousebind_action, "else", state); + } else if (state->in_action_none_branch) { + fill_child_action(nodename, content, + state->current_mousebind_action, "none", state); + } else { + fill_mousebind(nodename, content, state); + } + } + if (state->in_touch) { + fill_touch(nodename, content, state); + return; + } + if (state->in_libinput_category) { + fill_libinput_category(nodename, content, state); + return; + } + if (state->in_regions) { + fill_region(nodename, content, state); + return; + } + if (state->in_window_switcher_field) { + fill_window_switcher_field(nodename, content, state); + return; + } + if (state->in_window_rules) { + fill_window_rule(nodename, content, state); + return; + } /* handle nodes without content, e.g. */ - } else if (!strcmp(nodename, "default.keyboard")) { + if (!strcmp(nodename, "default.keyboard")) { load_default_key_bindings(); - } else if (!strcmp(nodename, "default.mouse")) { + return; + } + if (!strcmp(nodename, "devault.mouse") + || !strcmp(nodename, "default.mouse")) { load_default_mouse_bindings(); - } else if (!strcasecmp(nodename, "prefix.desktops")) { - xstrdup_replace(rc.workspace_config.prefix, content); + return; + } - } else if (!lab_xml_node_is_leaf(node)) { - /* parse children of nested nodes other than above */ - return true; + if (!strcasecmp(nodename, "map.tablet")) { + button_map_from = UINT32_MAX; + return; + } - } else if (str_space_only(content)) { - /* ignore empty leaf nodes other than above */ + if (!strcasecmp(nodename, "prefix.desktops")) { + xstrdup_replace(rc.workspace_config.prefix, content ? content : ""); + return; + } - /* handle non-empty leaf nodes */ - } else if (!strcmp(nodename, "decoration.core")) { + /* + * Nodenames where we want to honour !content have to be parsed above + * this point. An example of this is: + * + * + * + * + * + * In the case of the element having content, the node will be + * processed twice; first for the element itself (with no content) and + * then the content itself. In this situation xstrdup_replace() is + * called twice, but the end result is the right one. + */ + if (!content) { + return; + } + if (!strcmp(nodename, "place.font.theme")) { + font_place = enum_font_place(content); + if (font_place == FONT_PLACE_UNKNOWN) { + wlr_log(WLR_ERROR, "invalid font place %s", content); + } + } + + if (!strcmp(nodename, "decoration.core")) { if (!strcmp(content, "client")) { rc.xdg_shell_server_side_deco = false; } else { @@ -1097,12 +1172,8 @@ entry(xmlNode *node, char *nodename, char *content) set_bool(content, &rc.xwayland_persistence); } else if (!strcasecmp(nodename, "primarySelection.core")) { set_bool(content, &rc.primary_selection); - - } else if (!strcasecmp(nodename, "promptCommand.core")) { - xstrdup_replace(rc.prompt_command, content); - } else if (!strcmp(nodename, "policy.placement")) { - enum lab_placement_policy policy = view_placement_parse(content); + enum view_placement_policy policy = view_placement_parse(content); if (policy != LAB_PLACE_INVALID) { rc.placement_policy = policy; } @@ -1124,16 +1195,18 @@ entry(xmlNode *node, char *nodename, char *content) rc.corner_radius = atoi(content); } else if (!strcasecmp(nodename, "keepBorder.theme")) { set_bool(content, &rc.ssd_keep_border); - } else if (!strcasecmp(nodename, "maximizedDecoration.theme")) { - if (!strcasecmp(content, "titlebar")) { - rc.hide_maximized_window_titlebar = false; - } else if (!strcasecmp(content, "none")) { - rc.hide_maximized_window_titlebar = true; - } } else if (!strcasecmp(nodename, "dropShadows.theme")) { set_bool(content, &rc.shadows_enabled); } else if (!strcasecmp(nodename, "dropShadowsOnTiled.theme")) { set_bool(content, &rc.shadows_on_tiled); + } else if (!strcmp(nodename, "name.font.theme")) { + fill_font(nodename, content, font_place); + } else if (!strcmp(nodename, "size.font.theme")) { + fill_font(nodename, content, font_place); + } else if (!strcmp(nodename, "slant.font.theme")) { + fill_font(nodename, content, font_place); + } else if (!strcmp(nodename, "weight.font.theme")) { + fill_font(nodename, content, font_place); } else if (!strcasecmp(nodename, "followMouse.focus")) { set_bool(content, &rc.focus_follow_mouse); } else if (!strcasecmp(nodename, "followMouseRequiresMovement.focus")) { @@ -1150,6 +1223,9 @@ entry(xmlNode *node, char *nodename, char *content) } else if (!strcasecmp(nodename, "scrollFactor.mouse")) { /* This is deprecated. Show an error message in post_processing() */ set_double(content, &mouse_scroll_factor); + } else if (!strcasecmp(nodename, "name.context.mouse")) { + state->current_mouse_context = content; + state->current_mousebind = NULL; } else if (!strcasecmp(nodename, "repeatRate.keyboard")) { rc.repeat_rate = atoi(content); @@ -1176,8 +1252,6 @@ entry(xmlNode *node, char *nodename, char *content) rc.unmaximize_threshold = atoi(content); } else if (!strcasecmp(nodename, "range.snapping")) { rc.snap_edge_range = atoi(content); - } else if (!strcasecmp(nodename, "cornerRange.snapping")) { - rc.snap_edge_corner_range = atoi(content); } else if (!strcasecmp(nodename, "enabled.overlay.snapping")) { set_bool(content, &rc.snap_overlay_enabled); } else if (!strcasecmp(nodename, "inner.delay.overlay.snapping")) { @@ -1202,12 +1276,6 @@ entry(xmlNode *node, char *nodename, char *content) /* */ } else if (!strcasecmp(nodename, "show.windowSwitcher")) { set_bool(content, &rc.window_switcher.show); - } else if (!strcasecmp(nodename, "style.windowSwitcher")) { - if (!strcasecmp(content, "classic")) { - rc.window_switcher.style = WINDOW_SWITCHER_CLASSIC; - } else if (!strcasecmp(content, "thumbnail")) { - rc.window_switcher.style = WINDOW_SWITCHER_THUMBNAIL; - } } else if (!strcasecmp(nodename, "preview.windowSwitcher")) { set_bool(content, &rc.window_switcher.preview); } else if (!strcasecmp(nodename, "outlines.windowSwitcher")) { @@ -1217,8 +1285,6 @@ entry(xmlNode *node, char *nodename, char *content) rc.window_switcher.criteria &= ~LAB_VIEW_CRITERIA_CURRENT_WORKSPACE; } - } else if (!strcasecmp(nodename, "unshade.windowSwitcher")) { - set_bool(content, &rc.window_switcher.unshade); /* Remove this long term - just a friendly warning for now */ } else if (strstr(nodename, "windowswitcher.core")) { @@ -1284,6 +1350,17 @@ entry(xmlNode *node, char *nodename, char *content) rc.tablet.box.width = tablet_get_dbl_if_positive(content, "width"); } else if (!strcasecmp(nodename, "height.area.tablet")) { rc.tablet.box.height = tablet_get_dbl_if_positive(content, "height"); + } else if (!strcasecmp(nodename, "button.map.tablet")) { + button_map_from = tablet_button_from_str(content); + } else if (!strcasecmp(nodename, "to.map.tablet")) { + if (button_map_from != UINT32_MAX) { + uint32_t button_map_to = mousebind_button_from_str(content, NULL); + if (button_map_to != UINT32_MAX) { + tablet_button_mapping_add(button_map_from, button_map_to); + } + } else { + wlr_log(WLR_ERROR, "Missing 'button' argument for tablet button mapping"); + } } else if (!strcasecmp(nodename, "motion.tabletTool")) { rc.tablet_tool.motion = tablet_parse_motion(content); } else if (!strcasecmp(nodename, "relativeMotionSensitivity.tabletTool")) { @@ -1306,26 +1383,123 @@ entry(xmlNode *node, char *nodename, char *content) } else if (!strcasecmp(nodename, "useFilter.magnifier")) { set_bool(content, &rc.mag_filter); } - - return false; } static void -traverse(xmlNode *node) +process_node(xmlNode *node, struct parser_state *state) { - xmlNode *child; - char *key, *content; - LAB_XML_FOR_EACH(node, child, key, content) { - (void)key; - char buffer[256]; - char *name = nodename(child, buffer, sizeof(buffer)); - if (entry(child, name, content)) { - traverse(child); + char *content; + static char buffer[256]; + char *name; + + content = (char *)node->content; + if (xmlIsBlankNode(node)) { + return; + } + name = nodename(node, buffer, sizeof(buffer)); + entry(node, name, content, state); +} + +static void xml_tree_walk(xmlNode *node, struct parser_state *state); + +static void +traverse(xmlNode *n, struct parser_state *state) +{ + xmlAttr *attr; + + process_node(n, state); + for (attr = n->properties; attr; attr = attr->next) { + xml_tree_walk(attr->children, state); + } + xml_tree_walk(n->children, state); +} + +static void +xml_tree_walk(xmlNode *node, struct parser_state *state) +{ + for (xmlNode *n = node; n && n->name; n = n->next) { + if (!strcasecmp((char *)n->name, "comment")) { + continue; } + if (!strcasecmp((char *)n->name, "margin")) { + state->in_usable_area_override = true; + traverse(n, state); + state->in_usable_area_override = false; + continue; + } + if (!strcasecmp((char *)n->name, "keybind")) { + state->in_keybind = true; + traverse(n, state); + state->in_keybind = false; + continue; + } + if (!strcasecmp((char *)n->name, "mousebind")) { + state->in_mousebind = true; + traverse(n, state); + state->in_mousebind = false; + continue; + } + if (!strcasecmp((char *)n->name, "touch")) { + state->in_touch = true; + traverse(n, state); + state->in_touch = false; + continue; + } + if (!strcasecmp((char *)n->name, "device")) { + state->in_libinput_category = true; + traverse(n, state); + state->in_libinput_category = false; + continue; + } + if (!strcasecmp((char *)n->name, "regions")) { + state->in_regions = true; + traverse(n, state); + state->in_regions = false; + continue; + } + if (!strcasecmp((char *)n->name, "fields")) { + clear_window_switcher_fields(); + state->in_window_switcher_field = true; + traverse(n, state); + state->in_window_switcher_field = false; + continue; + } + if (!strcasecmp((char *)n->name, "windowRules")) { + state->in_window_rules = true; + traverse(n, state); + state->in_window_rules = false; + continue; + } + if (!strcasecmp((char *)n->name, "query")) { + state->in_action_query = true; + traverse(n, state); + state->in_action_query = false; + continue; + } + if (!strcasecmp((char *)n->name, "then")) { + state->in_action_then_branch = true; + traverse(n, state); + state->in_action_then_branch = false; + continue; + } + if (!strcasecmp((char *)n->name, "else")) { + state->in_action_else_branch = true; + traverse(n, state); + state->in_action_else_branch = false; + continue; + } + if (!strcasecmp((char *)n->name, "none")) { + state->in_action_none_branch = true; + traverse(n, state); + state->in_action_none_branch = false; + continue; + } + traverse(n, state); } } -static void +/* Exposed in header file to allow unit tests to parse buffers */ +void rcxml_parse_xml(struct buf *b) { int options = 0; @@ -1334,11 +1508,8 @@ rcxml_parse_xml(struct buf *b) wlr_log(WLR_ERROR, "error parsing config file"); return; } - xmlNode *root = xmlDocGetRootElement(d); - - lab_xml_expand_dotted_attributes(root); - traverse(root); - + struct parser_state init_state = {0}; + xml_tree_walk(xmlDocGetRootElement(d), &init_state); xmlFreeDoc(d); xmlCleanupParser(); } @@ -1357,6 +1528,8 @@ rcxml_init(void) static bool has_run; if (!has_run) { + wl_list_init(&rc.title_buttons_left); + wl_list_init(&rc.title_buttons_right); wl_list_init(&rc.usable_area_overrides); wl_list_init(&rc.keybinds); wl_list_init(&rc.mousebinds); @@ -1374,7 +1547,6 @@ rcxml_init(void) rc.placement_cascade_offset_y = 0; rc.xdg_shell_server_side_deco = true; - rc.hide_maximized_window_titlebar = false; rc.show_title = true; rc.title_layout_loaded = false; rc.ssd_keep_border = true; @@ -1384,7 +1556,7 @@ rcxml_init(void) rc.gap = 0; rc.adaptive_sync = LAB_ADAPTIVE_SYNC_DISABLED; - rc.allow_tearing = LAB_TEARING_DISABLED; + rc.allow_tearing = false; rc.auto_enable_outputs = true; rc.reuse_output_mode = false; rc.xwayland_persistence = false; @@ -1404,10 +1576,10 @@ rcxml_init(void) rc.tablet.force_mouse_emulation = false; rc.tablet.output_name = NULL; - rc.tablet.rotation = LAB_ROTATE_NONE; + rc.tablet.rotation = 0; rc.tablet.box = (struct wlr_fbox){0}; tablet_load_default_button_mappings(); - rc.tablet_tool.motion = LAB_MOTION_ABSOLUTE; + rc.tablet_tool.motion = LAB_TABLET_MOTION_ABSOLUTE; rc.tablet_tool.relative_motion_sensitivity = 1.0; rc.repeat_rate = 25; @@ -1420,7 +1592,6 @@ rcxml_init(void) rc.unmaximize_threshold = 150; rc.snap_edge_range = 10; - rc.snap_edge_corner_range = 50; rc.snap_overlay_enabled = true; rc.snap_overlay_delay_inner = 500; rc.snap_overlay_delay_outer = 500; @@ -1428,10 +1599,8 @@ rcxml_init(void) rc.snap_tiling_events_mode = LAB_TILING_EVENTS_ALWAYS; rc.window_switcher.show = true; - rc.window_switcher.style = WINDOW_SWITCHER_CLASSIC; rc.window_switcher.preview = true; rc.window_switcher.outlines = true; - rc.window_switcher.unshade = true; rc.window_switcher.criteria = LAB_VIEW_CRITERIA_CURRENT_WORKSPACE | LAB_VIEW_CRITERIA_ROOT_TOPLEVEL | LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER; @@ -1631,24 +1800,6 @@ post_processing(void) load_default_mouse_bindings(); } - if (!rc.prompt_command) { - rc.prompt_command = - xstrdup("labnag " - "--message '%m' " - "--button-dismiss '%n' " - "--button-dismiss '%y' " - "--background-color '%b' " - "--text-color '%t' " - "--button-border-color '%t' " - "--border-bottom-color '%t' " - "--button-background-color '%b' " - "--button-text-color '%t' " - "--border-bottom-size 1 " - "--button-border-size 3 " - "--keyboard-focus on-demand " - "--layer overlay " - "--timeout 0"); - } if (!rc.fallback_app_icon_name) { rc.fallback_app_icon_name = xstrdup("labwc"); } @@ -1873,13 +2024,25 @@ rcxml_read(const char *filename) */ for (struct wl_list *elm = iter(&paths); elm != &paths; elm = iter(elm)) { struct path *path = wl_container_of(elm, path, link); - struct buf b = buf_from_file(path->string); - if (!b.len) { + FILE *stream = fopen(path->string, "r"); + if (!stream) { continue; } wlr_log(WLR_INFO, "read config file %s", path->string); + struct buf b = BUF_INIT; + char *line = NULL; + size_t len = 0; + while (getline(&line, &len, stream) != -1) { + char *p = strrchr(line, '\n'); + if (p) { + *p = '\0'; + } + buf_add(&b, line); + } + zfree(line); + fclose(stream); rcxml_parse_xml(&b); buf_reset(&b); if (!should_merge_config) { @@ -1899,7 +2062,6 @@ rcxml_finish(void) zfree(rc.font_menuheader.name); zfree(rc.font_menuitem.name); zfree(rc.font_osd.name); - zfree(rc.prompt_command); zfree(rc.theme_name); zfree(rc.icon_theme_name); zfree(rc.fallback_app_icon_name); diff --git a/src/config/session.c b/src/config/session.c index 515b6803..5408bbcb 100644 --- a/src/config/session.c +++ b/src/config/session.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "config/session.h" #include #include #include @@ -8,16 +7,18 @@ #include #include #include +#include #include #include #include #include "common/buf.h" #include "common/dir.h" #include "common/file-helpers.h" +#include "common/mem.h" #include "common/parse-bool.h" #include "common/spawn.h" #include "common/string-helpers.h" -#include "config/rcxml.h" +#include "config/session.h" #include "labwc.h" static const char *const env_vars[] = { diff --git a/src/config/tablet-tool.c b/src/config/tablet-tool.c index 228e4b53..7e171410 100644 --- a/src/config/tablet-tool.c +++ b/src/config/tablet-tool.c @@ -1,17 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "config/tablet-tool.h" #include #include +#include "config/tablet-tool.h" -enum lab_motion +enum motion tablet_parse_motion(const char *name) { if (!strcasecmp(name, "Absolute")) { - return LAB_MOTION_ABSOLUTE; + return LAB_TABLET_MOTION_ABSOLUTE; } else if (!strcasecmp(name, "Relative")) { - return LAB_MOTION_RELATIVE; + return LAB_TABLET_MOTION_RELATIVE; } wlr_log(WLR_ERROR, "Invalid value for tablet motion: %s", name); - return LAB_MOTION_ABSOLUTE; + return LAB_TABLET_MOTION_ABSOLUTE; } diff --git a/src/config/tablet.c b/src/config/tablet.c index 17435a9f..b7d16c24 100644 --- a/src/config/tablet.c +++ b/src/config/tablet.c @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "config/tablet.h" #include #include #include #include #include "common/parse-double.h" +#include "config/tablet.h" #include "config/rcxml.h" #include "input/tablet-pad.h" @@ -21,7 +21,7 @@ tablet_get_dbl_if_positive(const char *content, const char *name) return value; } -enum lab_rotation +enum rotation tablet_parse_rotation(int value) { switch (value) { diff --git a/src/config/touch.c b/src/config/touch.c index 05592a46..0ff530e8 100644 --- a/src/config/touch.c +++ b/src/config/touch.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "config/touch.h" #include #include +#include "common/list.h" #include "config/rcxml.h" static struct touch_config_entry * diff --git a/src/debug.c b/src/debug.c index 84b844d8..4123187b 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1,15 +1,13 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "debug.h" -#include #include #include #include "common/lab-scene-rect.h" #include "common/scene-helpers.h" #include "common/string-helpers.h" +#include "debug.h" #include "input/ime.h" #include "labwc.h" #include "node.h" -#include "output.h" #include "ssd.h" #include "view.h" #include "workspaces.h" @@ -22,7 +20,7 @@ #define IGNORE_SSD true #define IGNORE_MENU true #define IGNORE_OSD_PREVIEW_OUTLINE true -#define IGNORE_SNAPPING_OVERLAY true +#define IGNORE_SNAPPING_PREVIEW_OUTLINE true static struct view *last_view; @@ -71,10 +69,11 @@ get_view_part(struct view *view, struct wlr_scene_node *node) return NULL; } if (node == &view->scene_tree->node) { - if (string_null_or_empty(view->app_id)) { + const char *app_id = view_get_string_prop(view, "app_id"); + if (string_null_or_empty(app_id)) { return "view"; } - snprintf(view_name, sizeof(view_name), "view (%s)", view->app_id); + snprintf(view_name, sizeof(view_name), "view (%s)", app_id); return view_name; } if (node == &view->content_tree->node) { @@ -140,10 +139,15 @@ get_special(struct server *server, struct wlr_scene_node *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.region_rect.tree + && node == &server->seat.overlay.region_rect.tree->node) { /* Created on-demand */ - return "seat->overlay.rect"; + return "seat->overlay.region_rect"; + } + if (server->seat.overlay.edge_rect.tree + && node == &server->seat.overlay.edge_rect.tree->node) { + /* Created on-demand */ + return "seat->overlay.edge_rect"; } if (server->seat.input_method_relay->popup_tree && node == &server->seat.input_method_relay->popup_tree->node) { @@ -218,13 +222,21 @@ dump_tree(struct server *server, struct wlr_scene_node *node, struct lab_scene_rect *osd_preview_outline = server->osd_state.preview_outline; + struct lab_scene_rect *region_snapping_overlay_outline = + server->seat.overlay.region_rect.border_rect; + struct lab_scene_rect *edge_snapping_overlay_outline = + server->seat.overlay.edge_rect.border_rect; if ((IGNORE_MENU && node == &server->menu_tree->node) || (IGNORE_SSD && last_view && ssd_debug_is_root_node(last_view->ssd, node)) || (IGNORE_OSD_PREVIEW_OUTLINE && osd_preview_outline && node == &osd_preview_outline->tree->node) - || (IGNORE_SNAPPING_OVERLAY && server->seat.overlay.rect - && node == &server->seat.overlay.rect->tree->node)) { + || (IGNORE_SNAPPING_PREVIEW_OUTLINE + && region_snapping_overlay_outline + && node == ®ion_snapping_overlay_outline->tree->node) + || (IGNORE_SNAPPING_PREVIEW_OUTLINE + && edge_snapping_overlay_outline + && node == &edge_snapping_overlay_outline->tree->node)) { printf("%*c%s\n", pos + 4 + INDENT_SIZE, ' ', ""); return; } diff --git a/src/decorations/kde-deco.c b/src/decorations/kde-deco.c index bf746163..a615811b 100644 --- a/src/decorations/kde-deco.c +++ b/src/decorations/kde-deco.c @@ -1,10 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only #include #include -#include #include "common/list.h" #include "common/mem.h" -#include "config/rcxml.h" #include "decorations.h" #include "labwc.h" #include "view.h" diff --git a/src/decorations/xdg-deco.c b/src/decorations/xdg-deco.c index d9eb3918..d69808e6 100644 --- a/src/decorations/xdg-deco.c +++ b/src/decorations/xdg-deco.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #include #include "common/mem.h" -#include "config/rcxml.h" #include "decorations.h" #include "labwc.h" #include "view.h" diff --git a/src/desktop-entry.c b/src/desktop-entry.c index 7617fa26..05e0168a 100644 --- a/src/desktop-entry.c +++ b/src/desktop-entry.c @@ -1,6 +1,4 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "desktop-entry.h" -#include #include #include #include @@ -8,10 +6,12 @@ #include #include #include +#include "common/macros.h" #include "common/mem.h" #include "common/string-helpers.h" -#include "config/rcxml.h" +#include "desktop-entry.h" #include "img/img.h" + #include "labwc.h" static const char *debug_libsfdo; @@ -37,10 +37,9 @@ log_handler(enum sfdo_log_level level, const char *fmt, va_list args, void *tag) /* * To avoid logging issues with .desktop files as errors, all libsfdo - * error-logging is demoted to info level unless running with - * LABWC_DEBUG_LIBSFDO. + * error-logging is demoted to info level. */ - if (!debug_libsfdo && level == SFDO_LOG_LEVEL_ERROR) { + if (level == SFDO_LOG_LEVEL_ERROR) { level = SFDO_LOG_LEVEL_INFO; } @@ -96,37 +95,19 @@ desktop_entry_init(struct server *server) * We set some relaxed load options to accommodate delinquent themes in * the wild, namely: * - * - SFDO_ICON_THEME_LOAD_OPTION_RELAXED to "impose less restrictions - * on the format of icon theme files" + * - SFDO_ICON_THEME_LOAD_OPTION_ALLOW_MISSING to "impose less + * restrictions on the format of icon theme files" * - * - SFDO_ICON_THEME_LOAD_OPTION_ALLOW_MISSING to "continue loading - * even if it fails to find a theme or one of its dependencies." + * - SFDO_ICON_THEME_LOAD_OPTION_RELAXED to "continue loading even if it + * fails to find a theme or one of its dependencies." */ int load_options = SFDO_ICON_THEME_LOAD_OPTIONS_DEFAULT - | SFDO_ICON_THEME_LOAD_OPTION_RELAXED - | SFDO_ICON_THEME_LOAD_OPTION_ALLOW_MISSING; + | SFDO_ICON_THEME_LOAD_OPTION_ALLOW_MISSING + | SFDO_ICON_THEME_LOAD_OPTION_RELAXED; sfdo->icon_theme = sfdo_icon_theme_load( sfdo->icon_ctx, rc.icon_theme_name, load_options); - if (!sfdo->icon_theme) { - /* - * sfdo_icon_theme_load() falls back to hicolor theme with - * _ALLOW_MISSING flag when the theme is missing, but just - * fails when the theme is invalid. - * So manually call sfdo_icon_theme_load() again here. - */ - wlr_log(WLR_ERROR, "Failed to load icon theme %s, falling back to 'hicolor'", - rc.icon_theme_name); - - if (!debug_libsfdo) { - wlr_log(WLR_ERROR, "Further information is available by setting " - "the LABWC_DEBUG_LIBSFDO=1 env var before starting labwc"); - } - - sfdo->icon_theme = sfdo_icon_theme_load( - sfdo->icon_ctx, "hicolor", load_options); - } if (!sfdo->icon_theme) { goto err_icon_theme; } @@ -148,10 +129,6 @@ err_desktop_ctx: err_basedir_ctx: free(sfdo); wlr_log(WLR_ERROR, "Failed to initialize icon loader"); - if (!debug_libsfdo) { - wlr_log(WLR_ERROR, "Further information is available by setting " - "the LABWC_DEBUG_LIBSFDO=1 env var before starting labwc"); - } } void @@ -291,7 +268,7 @@ get_db_entry_by_id_fuzzy(struct sfdo_desktop_db *db, const char *app_id) int alen = strlen(app_id); int dlen = strlen(desktop_id_base); - if (!strncasecmp(app_id, desktop_id_base, alen > dlen ? dlen : alen)) { + if (!strncasecmp(app_id, desktop_id, alen > dlen ? dlen : alen)) { return entry; } } diff --git a/src/desktop.c b/src/desktop.c index 2d883445..28af6cc6 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -1,22 +1,18 @@ // SPDX-License-Identifier: GPL-2.0-only #include "config.h" #include -#include -#include -#include -#include -#include -#include -#include #include "common/scene-helpers.h" +#include "common/surface-helpers.h" #include "dnd.h" #include "labwc.h" #include "layers.h" #include "node.h" -#include "output.h" +#include "osd.h" #include "ssd.h" #include "view.h" +#include "window-rules.h" #include "workspaces.h" +#include "xwayland.h" #if HAVE_XWAYLAND #include @@ -132,7 +128,7 @@ desktop_focus_view_or_surface(struct seat *seat, struct view *view, } } -static struct view * +struct view * desktop_topmost_focusable_view(struct server *server) { struct view *view; @@ -242,44 +238,28 @@ desktop_update_top_layer_visibility(struct server *server) } } -/* - * Work around rounding issues in some clients (notably Qt apps) where - * cursor coordinates in the rightmost or bottom pixel are incorrectly - * rounded up, putting them outside the surface bounds. The effect is - * especially noticeable in right/bottom desktop panels, since driving - * the cursor to the edge of the screen no longer works. - * - * Under X11, such rounding issues went unnoticed since cursor positions - * were always integers (i.e. whole pixel boundaries) anyway. Until more - * clients/toolkits are fractional-pixel clean, limit surface cursor - * coordinates to (w - 1, h - 1) as a workaround. - */ -static void -avoid_edge_rounding_issues(struct cursor_context *ctx) +static struct wlr_surface * +get_surface_from_layer_node(struct wlr_scene_node *node) { - if (!ctx->surface) { - return; - } - - int w = ctx->surface->current.width; - int h = ctx->surface->current.height; - /* - * The cursor isn't expected to be outside the surface bounds - * here, but check (sx < w, sy < h) just in case. - */ - if (ctx->sx > w - 1 && ctx->sx < w) { - ctx->sx = w - 1; - } - if (ctx->sy > h - 1 && ctx->sy < h) { - ctx->sy = h - 1; + assert(node->data); + struct node_descriptor *desc = (struct node_descriptor *)node->data; + if (desc->type == LAB_NODE_DESC_LAYER_SURFACE) { + struct lab_layer_surface *surface; + surface = node_layer_surface_from_node(node); + return surface->scene_layer_surface->layer_surface->surface; + } else if (desc->type == LAB_NODE_DESC_LAYER_POPUP) { + struct lab_layer_popup *popup; + popup = node_layer_popup_from_node(node); + return popup->wlr_popup->base->surface; } + return NULL; } /* TODO: make this less big and scary */ struct cursor_context get_cursor_context(struct server *server) { - struct cursor_context ret = {.type = LAB_NODE_NONE}; + struct cursor_context ret = {.type = LAB_SSD_NONE}; struct wlr_cursor *cursor = server->seat.cursor; /* Prevent drag icons to be on top of the hitbox detection */ @@ -295,20 +275,18 @@ get_cursor_context(struct server *server) dnd_icons_show(&server->seat, true); } + ret.node = node; if (!node) { - ret.type = LAB_NODE_ROOT; + ret.type = LAB_SSD_ROOT; return ret; } - ret.node = node; - ret.surface = lab_wlr_surface_from_node(node); - - avoid_edge_rounding_issues(&ret); #if HAVE_XWAYLAND - /* TODO: attach LAB_NODE_UNMANAGED node-descriptor to unmanaged surfaces */ if (node->type == WLR_SCENE_NODE_BUFFER) { + struct wlr_surface *surface = lab_wlr_surface_from_node(node); if (node->parent == server->unmanaged_tree) { - ret.type = LAB_NODE_UNMANAGED; + ret.type = LAB_SSD_UNMANAGED; + ret.surface = surface; return ret; } } @@ -317,73 +295,77 @@ get_cursor_context(struct server *server) struct node_descriptor *desc = node->data; if (desc) { switch (desc->type) { - case LAB_NODE_VIEW: - case LAB_NODE_XDG_POPUP: - ret.view = desc->view; - if (ret.surface) { - ret.type = LAB_NODE_CLIENT; - } else { - /* e.g. when cursor is on resize-indicator */ - ret.type = LAB_NODE_NONE; + case LAB_NODE_DESC_VIEW: + case LAB_NODE_DESC_XDG_POPUP: + ret.view = desc->data; + ret.type = ssd_get_part_type( + ret.view->ssd, ret.node, cursor); + if (ret.type == LAB_SSD_CLIENT) { + ret.surface = lab_wlr_surface_from_node(ret.node); } return ret; - case LAB_NODE_LAYER_SURFACE: - ret.type = LAB_NODE_LAYER_SURFACE; + case LAB_NODE_DESC_SSD_BUTTON: { + /* + * Always return the top scene node for SSD + * buttons + */ + struct ssd_button *button = + node_ssd_button_from_node(node); + ret.node = node; + ret.type = ssd_button_get_type(button); + ret.view = ssd_button_get_view(button); return ret; - case LAB_NODE_LAYER_POPUP: - case LAB_NODE_SESSION_LOCK_SURFACE: - case LAB_NODE_IME_POPUP: - ret.type = LAB_NODE_CLIENT; + } + case LAB_NODE_DESC_LAYER_SURFACE: + ret.node = node; + ret.type = LAB_SSD_LAYER_SURFACE; + ret.surface = get_surface_from_layer_node(node); return ret; - case LAB_NODE_MENUITEM: + case LAB_NODE_DESC_LAYER_POPUP: + ret.node = node; + ret.type = LAB_SSD_CLIENT; + ret.surface = get_surface_from_layer_node(node); + return ret; + case LAB_NODE_DESC_SESSION_LOCK_SURFACE: /* fallthrough */ + case LAB_NODE_DESC_IME_POPUP: + ret.type = LAB_SSD_CLIENT; + ret.surface = lab_wlr_surface_from_node(ret.node); + return ret; + case LAB_NODE_DESC_MENUITEM: /* Always return the top scene node for menu items */ ret.node = node; - ret.type = LAB_NODE_MENUITEM; + ret.type = LAB_SSD_MENU; return ret; - case LAB_NODE_BUTTON_FIRST...LAB_NODE_BUTTON_LAST: - case LAB_NODE_SSD_ROOT: - case LAB_NODE_TITLE: - case LAB_NODE_TITLEBAR: - /* Always return the top scene node for ssd parts */ - ret.node = node; - ret.view = desc->view; - /* - * A node_descriptor attached to a ssd part - * must have an associated view. - */ - assert(ret.view); - - /* - * When cursor is on the ssd border or extents, - * desc->type is usually LAB_NODE_SSD_ROOT. - * But desc->type can also be LAB_NODE_TITLEBAR - * when cursor is on the curved border at the - * titlebar. - * - * ssd_get_resizing_type() overwrites both of - * them with LAB_NODE_{BORDER,CORNER}_* node - * types, which are mapped to mouse contexts - * like Left and TLCorner. - */ - ret.type = ssd_get_resizing_type(ret.view->ssd, cursor); - if (ret.type == LAB_NODE_NONE) { - /* - * If cursor is not on border/extents, - * just use desc->type which should be - * mapped to mouse contexts like Title, - * Titlebar and Iconify. - */ - ret.type = desc->type; - } - - return ret; - default: - /* Other node types are not attached a scene node */ - wlr_log(WLR_ERROR, "unexpected node type: %d", desc->type); + case LAB_NODE_DESC_NODE: + case LAB_NODE_DESC_TREE: + case LAB_NODE_DESC_SCALED_SCENE_BUFFER: break; } } + /* Edge-case nodes without node-descriptors */ + if (node->type == WLR_SCENE_NODE_BUFFER) { + struct wlr_surface *surface = lab_wlr_surface_from_node(node); + + /* + * Handle layer-shell subsurfaces + * + * These don't have node-descriptors, but need to be + * able to receive pointer actions so we have to process + * them here. + * + * Test by running `gtk-layer-demo -k exclusive`, then + * open the 'set margin' dialog and try setting the + * margin with the pointer. + */ + if (surface && wlr_subsurface_try_from_wlr_surface(surface) + && subsurface_parent_layer(surface)) { + ret.surface = surface; + ret.type = LAB_SSD_LAYER_SUBSURFACE; + return ret; + } + } + /* node->parent is always a *wlr_scene_tree */ node = node->parent ? &node->parent->node : NULL; } diff --git a/src/dnd.c b/src/dnd.c index 8ed7c311..eeca114a 100644 --- a/src/dnd.c +++ b/src/dnd.c @@ -1,10 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "dnd.h" #include #include #include #include -#include "config/rcxml.h" +#include "dnd.h" #include "input/cursor.h" #include "labwc.h" /* for struct seat */ #include "view.h" diff --git a/src/edges.c b/src/edges.c index 56fb8bfa..6d64c511 100644 --- a/src/edges.c +++ b/src/edges.c @@ -1,20 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "edges.h" #include #include #include -#include -#include +#include #include #include "common/border.h" #include "common/box.h" #include "common/macros.h" #include "config/rcxml.h" +#include "edges.h" #include "labwc.h" -#include "node.h" -#include "output.h" -#include "ssd.h" #include "view.h" +#include "node.h" static void edges_for_target_geometry(struct border *edges, struct view *view, @@ -42,32 +39,32 @@ edges_initialize(struct border *edges) } static inline struct edge -build_edge(struct border region, enum lab_edge direction, int pad) +build_edge(struct border region, enum wlr_edges direction, int pad) { struct edge edge = { 0 }; switch (direction) { - case LAB_EDGE_LEFT: + case WLR_EDGE_LEFT: edge.offset = clipped_sub(region.left, pad); edge.min = region.top; edge.max = region.bottom; break; - case LAB_EDGE_RIGHT: + case WLR_EDGE_RIGHT: edge.offset = clipped_add(region.right, pad); edge.min = region.top; edge.max = region.bottom; break; - case LAB_EDGE_TOP: + case WLR_EDGE_TOP: edge.offset = clipped_sub(region.top, pad); edge.min = region.left; edge.max = region.right; break; - case LAB_EDGE_BOTTOM: + case WLR_EDGE_BOTTOM: edge.offset = clipped_add(region.bottom, pad); edge.min = region.left; edge.max = region.right; break; - default: + case WLR_EDGE_NONE: /* Should never be reached */ wlr_log(WLR_ERROR, "invalid direction"); abort(); @@ -77,14 +74,14 @@ build_edge(struct border region, enum lab_edge direction, int pad) } static inline bool -is_lesser(enum lab_edge direction) +is_lesser(enum wlr_edges direction) { - return direction == LAB_EDGE_LEFT || direction == LAB_EDGE_TOP; + return direction == WLR_EDGE_LEFT || direction == WLR_EDGE_TOP; } static inline struct edge -build_visible_edge(struct border region, enum lab_edge direction, - int pad, enum lab_edge edges_visible) +build_visible_edge(struct border region, enum wlr_edges direction, + int pad, uint32_t edges_visible) { struct edge edge = build_edge(region, direction, pad); @@ -99,7 +96,7 @@ static void validate_single_region_edge(int *valid_edge, struct border view, struct border target, struct border region, edge_validator_t validator, - enum lab_edge direction, enum lab_edge edges_visible) + enum wlr_edges direction, uint32_t edges_visible) { /* * When a view snaps to another while moving to its target, it can do @@ -117,8 +114,26 @@ validate_single_region_edge(int *valid_edge, * the region borders for aligned edges only. */ - enum lab_edge opposing = lab_edge_invert(direction); - assert(opposing != LAB_EDGE_NONE); + enum wlr_edges opposing = WLR_EDGE_NONE; + + switch (direction) { + case WLR_EDGE_TOP: + opposing = WLR_EDGE_BOTTOM; + break; + case WLR_EDGE_BOTTOM: + opposing = WLR_EDGE_TOP; + break; + case WLR_EDGE_LEFT: + opposing = WLR_EDGE_RIGHT; + break; + case WLR_EDGE_RIGHT: + opposing = WLR_EDGE_LEFT; + break; + case WLR_EDGE_NONE: + /* Should never be reached */ + assert(false); + return; + } validator(valid_edge, build_edge(view, direction, 0), @@ -131,31 +146,31 @@ validate_single_region_edge(int *valid_edge, static void validate_edges(struct border *valid_edges, struct border view, struct border target, - struct border region, enum lab_edge edges_visible, + struct border region, uint32_t edges_visible, edge_validator_t validator) { /* Check for edges encountered during movement of left edge */ validate_single_region_edge(&valid_edges->left, - view, target, region, validator, LAB_EDGE_LEFT, edges_visible); + view, target, region, validator, WLR_EDGE_LEFT, edges_visible); /* Check for edges encountered during movement of right edge */ validate_single_region_edge(&valid_edges->right, - view, target, region, validator, LAB_EDGE_RIGHT, edges_visible); + view, target, region, validator, WLR_EDGE_RIGHT, edges_visible); /* Check for edges encountered during movement of top edge */ validate_single_region_edge(&valid_edges->top, - view, target, region, validator, LAB_EDGE_TOP, edges_visible); + view, target, region, validator, WLR_EDGE_TOP, edges_visible); /* Check for edges encountered during movement of bottom edge */ validate_single_region_edge(&valid_edges->bottom, - view, target, region, validator, LAB_EDGE_BOTTOM, edges_visible); + view, target, region, validator, WLR_EDGE_BOTTOM, edges_visible); } static void validate_single_output_edge(int *valid_edge, struct border view, struct border target, struct border region, edge_validator_t validator, - enum lab_edge direction) + enum wlr_edges direction) { static struct border unbounded = { .top = INT_MIN, @@ -210,24 +225,24 @@ validate_output_edges(struct border *valid_edges, /* Left edge encounters a half-infinite region to the left of the output */ validate_single_output_edge(&valid_edges->left, - view, target, output, validator, LAB_EDGE_LEFT); + view, target, output, validator, WLR_EDGE_LEFT); /* Right edge encounters a half-infinite region to the right of the output */ validate_single_output_edge(&valid_edges->right, - view, target, output, validator, LAB_EDGE_RIGHT); + view, target, output, validator, WLR_EDGE_RIGHT); /* Top edge encounters a half-infinite region above the output */ validate_single_output_edge(&valid_edges->top, - view, target, output, validator, LAB_EDGE_TOP); + view, target, output, validator, WLR_EDGE_TOP); /* Bottom edge encounters a half-infinite region below the output */ validate_single_output_edge(&valid_edges->bottom, - view, target, output, validator, LAB_EDGE_BOTTOM); + view, target, output, validator, WLR_EDGE_BOTTOM); } -static enum lab_edge +static uint32_t compute_edges_visible(const struct wlr_box *view_size, const pixman_box32_t *view_rect, const pixman_region32_t *available) @@ -242,19 +257,19 @@ compute_edges_visible(const struct wlr_box *view_size, const pixman_box32_t *rects = pixman_region32_rectangles(&intersection, &nrects); - enum lab_edge edges_visible = LAB_EDGE_NONE; + uint32_t edges_visible = 0; for (int i = 0; i < nrects; i++) { if (rects[i].x1 == view_rect->x1) { - edges_visible |= LAB_EDGE_LEFT; + edges_visible |= WLR_EDGE_LEFT; } if (rects[i].y1 == view_rect->y1) { - edges_visible |= LAB_EDGE_TOP; + edges_visible |= WLR_EDGE_TOP; } if (rects[i].x2 == view_rect->x2) { - edges_visible |= LAB_EDGE_RIGHT; + edges_visible |= WLR_EDGE_RIGHT; } if (rects[i].y2 == view_rect->y2) { - edges_visible |= LAB_EDGE_BOTTOM; + edges_visible |= WLR_EDGE_BOTTOM; } } pixman_region32_fini(&intersection); @@ -268,8 +283,8 @@ subtract_view_from_space(struct view *view, pixman_region32_t *available) struct wlr_box view_size = ssd_max_extents(view); pixman_box32_t view_rect = { .x1 = view_size.x, - .y1 = view_size.y, .x2 = view_size.x + view_size.width, + .y1 = view_size.y, .y2 = view_size.y + view_size.height }; @@ -278,10 +293,11 @@ subtract_view_from_space(struct view *view, pixman_region32_t *available) switch (overlap) { case PIXMAN_REGION_IN: - view->edges_visible = LAB_EDGES_ALL; + view->edges_visible = WLR_EDGE_TOP | WLR_EDGE_RIGHT + | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT; break; case PIXMAN_REGION_OUT: - view->edges_visible = LAB_EDGE_NONE; + view->edges_visible = 0; return; case PIXMAN_REGION_PART: view->edges_visible = compute_edges_visible( @@ -315,7 +331,7 @@ subtract_node_tree(struct wlr_scene_tree *tree, pixman_region32_t *available, } node_desc = node->data; - if (node_desc && node_desc->type == LAB_NODE_VIEW) { + if (node_desc && node_desc->type == LAB_NODE_DESC_VIEW) { view = node_view_from_node(node); if (view != ignored_view) { subtract_view_from_space(view, available); @@ -394,10 +410,11 @@ edges_find_neighbors(struct border *nearest_edges, struct view *view, continue; } - enum lab_edge edges_visible = - ignore_hidden ? v->edges_visible : LAB_EDGES_ALL; + uint32_t edges_visible = ignore_hidden ? v->edges_visible : + WLR_EDGE_TOP | WLR_EDGE_LEFT + | WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT; - if (edges_visible == LAB_EDGE_NONE) { + if (edges_visible == 0) { continue; } @@ -414,10 +431,10 @@ edges_find_neighbors(struct border *nearest_edges, struct view *view, struct border win_edges = { .top = v->current.y - border.top, - .right = v->current.x + v->current.width + border.right, + .left = v->current.x - border.left, .bottom = v->current.y + border.bottom + view_effective_height(v, /* use_pending */ false), - .left = v->current.x - border.left, + .right = v->current.x + v->current.width + border.right, }; validate_edges(nearest_edges, view_edges, @@ -525,8 +542,7 @@ edges_adjust_move_coords(struct view *view, struct border edges, void edges_adjust_resize_geom(struct view *view, struct border edges, - enum lab_edge resize_edges, struct wlr_box *geom, - bool use_pending) + uint32_t resize_edges, struct wlr_box *geom, bool use_pending) { assert(view); @@ -539,24 +555,24 @@ edges_adjust_resize_geom(struct view *view, struct border edges, * any valid nearest edge in the corresponding direction. */ - if (resize_edges & LAB_EDGE_LEFT) { + if (resize_edges & WLR_EDGE_LEFT) { if (BOUNDED_INT(edges.left)) { geom->x = edges.left + border.left + rc.gap; geom->width = view_geom->width + view_geom->x - geom->x; } - } else if (resize_edges & LAB_EDGE_RIGHT) { + } else if (resize_edges & WLR_EDGE_RIGHT) { if (BOUNDED_INT(edges.right)) { geom->width = edges.right - view_geom->x - border.right - rc.gap; } } - if (resize_edges & LAB_EDGE_TOP) { + if (resize_edges & WLR_EDGE_TOP) { if (BOUNDED_INT(edges.top)) { geom->y = edges.top + border.top + rc.gap; geom->height = view_geom->height + view_geom->y - geom->y; } - } else if (resize_edges & LAB_EDGE_BOTTOM) { + } else if (resize_edges & WLR_EDGE_BOTTOM) { if (BOUNDED_INT(edges.bottom)) { geom->height = edges.bottom - view_geom->y - border.bottom - rc.gap; diff --git a/src/foreign-toplevel/ext-foreign.c b/src/foreign-toplevel/ext-foreign.c index 748050fa..e959b626 100644 --- a/src/foreign-toplevel/ext-foreign.c +++ b/src/foreign-toplevel/ext-foreign.c @@ -1,10 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "foreign-toplevel/ext-foreign.h" #include #include #include "common/macros.h" #include "labwc.h" #include "view.h" +#include "foreign-toplevel-internal.h" /* ext signals */ static void @@ -15,11 +15,6 @@ handle_handle_destroy(struct wl_listener *listener, void *data) /* Client side requests */ wl_list_remove(&ext_toplevel->on.handle_destroy.link); - - /* Compositor side state changes */ - wl_list_remove(&ext_toplevel->on_view.new_app_id.link); - wl_list_remove(&ext_toplevel->on_view.new_title.link); - ext_toplevel->handle = NULL; } @@ -27,51 +22,73 @@ handle_handle_destroy(struct wl_listener *listener, void *data) static void handle_new_app_id(struct wl_listener *listener, void *data) { - struct ext_foreign_toplevel *ext_toplevel = - wl_container_of(listener, ext_toplevel, on_view.new_app_id); - assert(ext_toplevel->handle); + struct foreign_toplevel *toplevel = + wl_container_of(listener, toplevel, ext_toplevel.on_view.new_app_id); + assert(toplevel->ext_toplevel.handle); struct wlr_ext_foreign_toplevel_handle_v1_state state = { - .title = ext_toplevel->view->title, - .app_id = ext_toplevel->view->app_id, + .title = view_get_string_prop(toplevel->view, "title"), + .app_id = view_get_string_prop(toplevel->view, "app_id") }; - wlr_ext_foreign_toplevel_handle_v1_update_state(ext_toplevel->handle, - &state); + wlr_ext_foreign_toplevel_handle_v1_update_state( + toplevel->ext_toplevel.handle, &state); } static void handle_new_title(struct wl_listener *listener, void *data) { - struct ext_foreign_toplevel *ext_toplevel = - wl_container_of(listener, ext_toplevel, on_view.new_title); - assert(ext_toplevel->handle); + struct foreign_toplevel *toplevel = + wl_container_of(listener, toplevel, ext_toplevel.on_view.new_title); + assert(toplevel->ext_toplevel.handle); struct wlr_ext_foreign_toplevel_handle_v1_state state = { - .title = ext_toplevel->view->title, - .app_id = ext_toplevel->view->app_id, + .title = view_get_string_prop(toplevel->view, "title"), + .app_id = view_get_string_prop(toplevel->view, "app_id") }; - wlr_ext_foreign_toplevel_handle_v1_update_state(ext_toplevel->handle, - &state); + wlr_ext_foreign_toplevel_handle_v1_update_state( + toplevel->ext_toplevel.handle, &state); +} + +/* Internal signals */ +static void +handle_toplevel_destroy(struct wl_listener *listener, void *data) +{ + struct ext_foreign_toplevel *ext_toplevel = + wl_container_of(listener, ext_toplevel, on_foreign_toplevel.toplevel_destroy); + + if (!ext_toplevel->handle) { + return; + } + + wlr_ext_foreign_toplevel_handle_v1_destroy(ext_toplevel->handle); + + /* Compositor side state changes */ + wl_list_remove(&ext_toplevel->on_view.new_app_id.link); + wl_list_remove(&ext_toplevel->on_view.new_title.link); + + /* Internal signals */ + wl_list_remove(&ext_toplevel->on_foreign_toplevel.toplevel_destroy.link); } /* Internal API */ void -ext_foreign_toplevel_init(struct ext_foreign_toplevel *ext_toplevel, - struct view *view) +ext_foreign_toplevel_init(struct foreign_toplevel *toplevel) { + struct ext_foreign_toplevel *ext_toplevel = &toplevel->ext_toplevel; + struct view *view = toplevel->view; + assert(view->server->foreign_toplevel_list); - ext_toplevel->view = view; struct wlr_ext_foreign_toplevel_handle_v1_state state = { - .title = view->title, - .app_id = view->app_id, + .title = view_get_string_prop(toplevel->view, "title"), + .app_id = view_get_string_prop(toplevel->view, "app_id") }; ext_toplevel->handle = wlr_ext_foreign_toplevel_handle_v1_create( view->server->foreign_toplevel_list, &state); if (!ext_toplevel->handle) { wlr_log(WLR_ERROR, "cannot create ext toplevel handle for (%s)", - view->title); + view_get_string_prop(view, "title")); return; } @@ -82,16 +99,7 @@ ext_foreign_toplevel_init(struct ext_foreign_toplevel *ext_toplevel, /* Compositor side state changes */ CONNECT_SIGNAL(view, &ext_toplevel->on_view, new_app_id); CONNECT_SIGNAL(view, &ext_toplevel->on_view, new_title); -} -void -ext_foreign_toplevel_finish(struct ext_foreign_toplevel *ext_toplevel) -{ - if (!ext_toplevel->handle) { - return; - } - - /* invokes handle_handle_destroy() which does more cleanup */ - wlr_ext_foreign_toplevel_handle_v1_destroy(ext_toplevel->handle); - assert(!ext_toplevel->handle); + /* Internal signals */ + CONNECT_SIGNAL(toplevel, &ext_toplevel->on_foreign_toplevel, toplevel_destroy); } diff --git a/src/foreign-toplevel/foreign.c b/src/foreign-toplevel/foreign.c index 14563431..a469305c 100644 --- a/src/foreign-toplevel/foreign.c +++ b/src/foreign-toplevel/foreign.c @@ -1,19 +1,44 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "foreign-toplevel/foreign.h" #include +#include +#include "common/macros.h" #include "common/mem.h" -#include "foreign-toplevel/ext-foreign.h" -#include "foreign-toplevel/wlr-foreign.h" +#include "labwc.h" #include "view.h" +#include "foreign-toplevel-internal.h" -struct foreign_toplevel { - /* *-toplevel implementations */ - struct wlr_foreign_toplevel wlr_toplevel; - struct ext_foreign_toplevel ext_toplevel; +/* Internal API */ +void +foreign_request_minimize(struct foreign_toplevel *toplevel, bool minimized) +{ + view_minimize(toplevel->view, minimized); +} - /* TODO: add struct xdg_x11_mapped_toplevel at some point */ -}; +void +foreign_request_maximize(struct foreign_toplevel *toplevel, enum view_axis axis) +{ + view_maximize(toplevel->view, axis, /*store_natural_geometry*/ true); +} +void +foreign_request_fullscreen(struct foreign_toplevel *toplevel, bool fullscreen) +{ + view_set_fullscreen(toplevel->view, fullscreen); +} + +void +foreign_request_activate(struct foreign_toplevel *toplevel) +{ + desktop_focus_view(toplevel->view, /*raise*/ true); +} + +void +foreign_request_close(struct foreign_toplevel *toplevel) +{ + view_close(toplevel->view); +} + +/* Public API */ struct foreign_toplevel * foreign_toplevel_create(struct view *view) { @@ -21,8 +46,13 @@ foreign_toplevel_create(struct view *view) assert(view->mapped); struct foreign_toplevel *toplevel = znew(*toplevel); - wlr_foreign_toplevel_init(&toplevel->wlr_toplevel, view); - ext_foreign_toplevel_init(&toplevel->ext_toplevel, view); + toplevel->view = view; + + wl_signal_init(&toplevel->events.toplevel_parent); + wl_signal_init(&toplevel->events.toplevel_destroy); + + wlr_foreign_toplevel_init(toplevel); + ext_foreign_toplevel_init(toplevel); return toplevel; } @@ -31,15 +61,15 @@ void foreign_toplevel_set_parent(struct foreign_toplevel *toplevel, struct foreign_toplevel *parent) { assert(toplevel); - wlr_foreign_toplevel_set_parent(&toplevel->wlr_toplevel, - parent ? &parent->wlr_toplevel : NULL); + wl_signal_emit_mutable(&toplevel->events.toplevel_parent, parent); } void foreign_toplevel_destroy(struct foreign_toplevel *toplevel) { assert(toplevel); - wlr_foreign_toplevel_finish(&toplevel->wlr_toplevel); - ext_foreign_toplevel_finish(&toplevel->ext_toplevel); + wl_signal_emit_mutable(&toplevel->events.toplevel_destroy, NULL); + assert(!toplevel->wlr_toplevel.handle); + assert(!toplevel->ext_toplevel.handle); free(toplevel); } diff --git a/src/foreign-toplevel/wlr-foreign.c b/src/foreign-toplevel/wlr-foreign.c index a84775f5..76eb01e5 100644 --- a/src/foreign-toplevel/wlr-foreign.c +++ b/src/foreign-toplevel/wlr-foreign.c @@ -1,63 +1,61 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "foreign-toplevel/wlr-foreign.h" #include #include #include "common/macros.h" #include "labwc.h" -#include "output.h" #include "view.h" +#include "foreign-toplevel-internal.h" /* wlr signals */ static void handle_request_minimize(struct wl_listener *listener, void *data) { - struct wlr_foreign_toplevel *wlr_toplevel = - wl_container_of(listener, wlr_toplevel, on.request_minimize); + struct foreign_toplevel *toplevel = wl_container_of( + listener, toplevel, wlr_toplevel.on.request_minimize); struct wlr_foreign_toplevel_handle_v1_minimized_event *event = data; - view_minimize(wlr_toplevel->view, event->minimized); + foreign_request_minimize(toplevel, event->minimized); } static void handle_request_maximize(struct wl_listener *listener, void *data) { - struct wlr_foreign_toplevel *wlr_toplevel = - wl_container_of(listener, wlr_toplevel, on.request_maximize); + struct foreign_toplevel *toplevel = wl_container_of( + listener, toplevel, wlr_toplevel.on.request_maximize); struct wlr_foreign_toplevel_handle_v1_maximized_event *event = data; - view_maximize(wlr_toplevel->view, - event->maximized ? VIEW_AXIS_BOTH : VIEW_AXIS_NONE, - /*store_natural_geometry*/ true); + foreign_request_maximize(toplevel, + event->maximized ? VIEW_AXIS_BOTH : VIEW_AXIS_NONE); } static void handle_request_fullscreen(struct wl_listener *listener, void *data) { - struct wlr_foreign_toplevel *wlr_toplevel = - wl_container_of(listener, wlr_toplevel, on.request_fullscreen); + struct foreign_toplevel *toplevel = wl_container_of( + listener, toplevel, wlr_toplevel.on.request_fullscreen); struct wlr_foreign_toplevel_handle_v1_fullscreen_event *event = data; /* TODO: This ignores event->output */ - view_set_fullscreen(wlr_toplevel->view, event->fullscreen); + foreign_request_fullscreen(toplevel, event->fullscreen); } static void handle_request_activate(struct wl_listener *listener, void *data) { - struct wlr_foreign_toplevel *wlr_toplevel = - wl_container_of(listener, wlr_toplevel, on.request_activate); + struct foreign_toplevel *toplevel = wl_container_of( + listener, toplevel, wlr_toplevel.on.request_activate); /* In a multi-seat world we would select seat based on event->seat here. */ - desktop_focus_view(wlr_toplevel->view, /*raise*/ true); + foreign_request_activate(toplevel); } static void handle_request_close(struct wl_listener *listener, void *data) { - struct wlr_foreign_toplevel *wlr_toplevel = - wl_container_of(listener, wlr_toplevel, on.request_close); + struct foreign_toplevel *toplevel = wl_container_of( + listener, toplevel, wlr_toplevel.on.request_close); - view_close(wlr_toplevel->view); + foreign_request_close(toplevel); } static void @@ -73,6 +71,138 @@ handle_handle_destroy(struct wl_listener *listener, void *data) wl_list_remove(&wlr_toplevel->on.request_activate.link); wl_list_remove(&wlr_toplevel->on.request_close.link); wl_list_remove(&wlr_toplevel->on.handle_destroy.link); + wlr_toplevel->handle = NULL; +} + +/* Compositor signals */ +static void +handle_new_app_id(struct wl_listener *listener, void *data) +{ + struct foreign_toplevel *toplevel = + wl_container_of(listener, toplevel, wlr_toplevel.on_view.new_app_id); + assert(toplevel->wlr_toplevel.handle); + + const char *app_id = view_get_string_prop(toplevel->view, "app_id"); + const char *wlr_app_id = toplevel->wlr_toplevel.handle->app_id; + if (app_id && wlr_app_id && !strcmp(app_id, wlr_app_id)) { + /* Don't send app_id if they are the same */ + return; + } + wlr_foreign_toplevel_handle_v1_set_app_id(toplevel->wlr_toplevel.handle, app_id); +} + +static void +handle_new_title(struct wl_listener *listener, void *data) +{ + struct foreign_toplevel *toplevel = + wl_container_of(listener, toplevel, wlr_toplevel.on_view.new_title); + assert(toplevel->wlr_toplevel.handle); + + const char *title = view_get_string_prop(toplevel->view, "title"); + const char *wlr_title = toplevel->wlr_toplevel.handle->title; + if (title && wlr_title && !strcmp(title, wlr_title)) { + /* Don't send title if they are the same */ + return; + } + wlr_foreign_toplevel_handle_v1_set_title(toplevel->wlr_toplevel.handle, title); +} + +static void +handle_new_outputs(struct wl_listener *listener, void *data) +{ + struct foreign_toplevel *toplevel = + wl_container_of(listener, toplevel, wlr_toplevel.on_view.new_outputs); + assert(toplevel->wlr_toplevel.handle); + + /* + * Loop over all outputs and notify foreign_toplevel clients about changes. + * wlr_foreign_toplevel_handle_v1_output_xxx() keeps track of the active + * outputs internally and merges the events. It also listens to output + * destroy events so its fine to just relay the current state and let + * wlr_foreign_toplevel handle the rest. + */ + struct output *output; + wl_list_for_each(output, &toplevel->view->server->outputs, link) { + if (view_on_output(toplevel->view, output)) { + wlr_foreign_toplevel_handle_v1_output_enter( + toplevel->wlr_toplevel.handle, output->wlr_output); + } else { + wlr_foreign_toplevel_handle_v1_output_leave( + toplevel->wlr_toplevel.handle, output->wlr_output); + } + } +} + +static void +handle_maximized(struct wl_listener *listener, void *data) +{ + struct foreign_toplevel *toplevel = + wl_container_of(listener, toplevel, wlr_toplevel.on_view.maximized); + assert(toplevel->wlr_toplevel.handle); + + wlr_foreign_toplevel_handle_v1_set_maximized( + toplevel->wlr_toplevel.handle, + toplevel->view->maximized == VIEW_AXIS_BOTH); +} + +static void +handle_minimized(struct wl_listener *listener, void *data) +{ + struct foreign_toplevel *toplevel = + wl_container_of(listener, toplevel, wlr_toplevel.on_view.minimized); + assert(toplevel->wlr_toplevel.handle); + + wlr_foreign_toplevel_handle_v1_set_minimized( + toplevel->wlr_toplevel.handle, toplevel->view->minimized); +} + +static void +handle_fullscreened(struct wl_listener *listener, void *data) +{ + struct foreign_toplevel *toplevel = + wl_container_of(listener, toplevel, wlr_toplevel.on_view.fullscreened); + assert(toplevel->wlr_toplevel.handle); + + wlr_foreign_toplevel_handle_v1_set_fullscreen( + toplevel->wlr_toplevel.handle, toplevel->view->fullscreen); +} + +static void +handle_activated(struct wl_listener *listener, void *data) +{ + struct foreign_toplevel *toplevel = + wl_container_of(listener, toplevel, wlr_toplevel.on_view.activated); + assert(toplevel->wlr_toplevel.handle); + + bool *activated = data; + wlr_foreign_toplevel_handle_v1_set_activated( + toplevel->wlr_toplevel.handle, *activated); +} + +/* Internal signals */ +static void +handle_toplevel_parent(struct wl_listener *listener, void *data) +{ + struct wlr_foreign_toplevel *wlr_toplevel = wl_container_of( + listener, wlr_toplevel, on_foreign_toplevel.toplevel_parent); + struct foreign_toplevel *parent = data; + + assert(wlr_toplevel->handle); + + /* The wlroots wlr-foreign-toplevel impl ensures parent is reset to NULL on destroy */ + wlr_foreign_toplevel_handle_v1_set_parent(wlr_toplevel->handle, parent + ? parent->wlr_toplevel.handle + : NULL); +} + +static void +handle_toplevel_destroy(struct wl_listener *listener, void *data) +{ + struct wlr_foreign_toplevel *wlr_toplevel = wl_container_of( + listener, wlr_toplevel, on_foreign_toplevel.toplevel_destroy); + + assert(wlr_toplevel->handle); + wlr_foreign_toplevel_handle_v1_destroy(wlr_toplevel->handle); /* Compositor side state changes */ wl_list_remove(&wlr_toplevel->on_view.new_app_id.link); @@ -83,116 +213,25 @@ handle_handle_destroy(struct wl_listener *listener, void *data) wl_list_remove(&wlr_toplevel->on_view.fullscreened.link); wl_list_remove(&wlr_toplevel->on_view.activated.link); - wlr_toplevel->handle = NULL; -} - -/* Compositor signals */ -static void -handle_new_app_id(struct wl_listener *listener, void *data) -{ - struct wlr_foreign_toplevel *wlr_toplevel = - wl_container_of(listener, wlr_toplevel, on_view.new_app_id); - assert(wlr_toplevel->handle); - - wlr_foreign_toplevel_handle_v1_set_app_id(wlr_toplevel->handle, - wlr_toplevel->view->app_id); -} - -static void -handle_new_title(struct wl_listener *listener, void *data) -{ - struct wlr_foreign_toplevel *wlr_toplevel = - wl_container_of(listener, wlr_toplevel, on_view.new_title); - assert(wlr_toplevel->handle); - - wlr_foreign_toplevel_handle_v1_set_title(wlr_toplevel->handle, - wlr_toplevel->view->title); -} - -static void -handle_new_outputs(struct wl_listener *listener, void *data) -{ - struct wlr_foreign_toplevel *wlr_toplevel = - wl_container_of(listener, wlr_toplevel, on_view.new_outputs); - assert(wlr_toplevel->handle); - - /* - * Loop over all outputs and notify foreign_toplevel clients about changes. - * wlr_foreign_toplevel_handle_v1_output_xxx() keeps track of the active - * outputs internally and merges the events. It also listens to output - * destroy events so its fine to just relay the current state and let - * wlr_foreign_toplevel handle the rest. - */ - struct output *output; - wl_list_for_each(output, &wlr_toplevel->view->server->outputs, link) { - if (view_on_output(wlr_toplevel->view, output)) { - wlr_foreign_toplevel_handle_v1_output_enter( - wlr_toplevel->handle, output->wlr_output); - } else { - wlr_foreign_toplevel_handle_v1_output_leave( - wlr_toplevel->handle, output->wlr_output); - } - } -} - -static void -handle_maximized(struct wl_listener *listener, void *data) -{ - struct wlr_foreign_toplevel *wlr_toplevel = - wl_container_of(listener, wlr_toplevel, on_view.maximized); - assert(wlr_toplevel->handle); - - wlr_foreign_toplevel_handle_v1_set_maximized(wlr_toplevel->handle, - wlr_toplevel->view->maximized == VIEW_AXIS_BOTH); -} - -static void -handle_minimized(struct wl_listener *listener, void *data) -{ - struct wlr_foreign_toplevel *wlr_toplevel = - wl_container_of(listener, wlr_toplevel, on_view.minimized); - assert(wlr_toplevel->handle); - - wlr_foreign_toplevel_handle_v1_set_minimized(wlr_toplevel->handle, - wlr_toplevel->view->minimized); -} - -static void -handle_fullscreened(struct wl_listener *listener, void *data) -{ - struct wlr_foreign_toplevel *wlr_toplevel = - wl_container_of(listener, wlr_toplevel, on_view.fullscreened); - assert(wlr_toplevel->handle); - - wlr_foreign_toplevel_handle_v1_set_fullscreen(wlr_toplevel->handle, - wlr_toplevel->view->fullscreen); -} - -static void -handle_activated(struct wl_listener *listener, void *data) -{ - struct wlr_foreign_toplevel *wlr_toplevel = - wl_container_of(listener, wlr_toplevel, on_view.activated); - assert(wlr_toplevel->handle); - - bool *activated = data; - wlr_foreign_toplevel_handle_v1_set_activated(wlr_toplevel->handle, - *activated); + /* Internal signals */ + wl_list_remove(&wlr_toplevel->on_foreign_toplevel.toplevel_parent.link); + wl_list_remove(&wlr_toplevel->on_foreign_toplevel.toplevel_destroy.link); } /* Internal API */ void -wlr_foreign_toplevel_init(struct wlr_foreign_toplevel *wlr_toplevel, - struct view *view) +wlr_foreign_toplevel_init(struct foreign_toplevel *toplevel) { + struct wlr_foreign_toplevel *wlr_toplevel = &toplevel->wlr_toplevel; + struct view *view = toplevel->view; + assert(view->server->foreign_toplevel_manager); - wlr_toplevel->view = view; wlr_toplevel->handle = wlr_foreign_toplevel_handle_v1_create( view->server->foreign_toplevel_manager); if (!wlr_toplevel->handle) { wlr_log(WLR_ERROR, "cannot create wlr foreign toplevel handle for (%s)", - view->title); + view_get_string_prop(view, "title")); return; } @@ -220,29 +259,8 @@ wlr_foreign_toplevel_init(struct wlr_foreign_toplevel *wlr_toplevel, CONNECT_SIGNAL(view, &wlr_toplevel->on_view, minimized); CONNECT_SIGNAL(view, &wlr_toplevel->on_view, fullscreened); CONNECT_SIGNAL(view, &wlr_toplevel->on_view, activated); -} - -void -wlr_foreign_toplevel_set_parent(struct wlr_foreign_toplevel *wlr_toplevel, - struct wlr_foreign_toplevel *parent) -{ - if (!wlr_toplevel->handle) { - return; - } - - /* The wlroots wlr-foreign-toplevel impl ensures parent is reset to NULL on destroy */ - wlr_foreign_toplevel_handle_v1_set_parent(wlr_toplevel->handle, - parent ? parent->handle : NULL); -} - -void -wlr_foreign_toplevel_finish(struct wlr_foreign_toplevel *wlr_toplevel) -{ - if (!wlr_toplevel->handle) { - return; - } - - /* invokes handle_handle_destroy() which does more cleanup */ - wlr_foreign_toplevel_handle_v1_destroy(wlr_toplevel->handle); - assert(!wlr_toplevel->handle); + + /* Internal signals */ + CONNECT_SIGNAL(toplevel, &wlr_toplevel->on_foreign_toplevel, toplevel_parent); + CONNECT_SIGNAL(toplevel, &wlr_toplevel->on_foreign_toplevel, toplevel_destroy); } diff --git a/src/idle.c b/src/idle.c index b8e96c2d..568b680f 100644 --- a/src/idle.c +++ b/src/idle.c @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "idle.h" #include #include #include #include #include "common/mem.h" +#include "idle.h" struct lab_idle_inhibitor { struct wlr_idle_inhibitor_v1 *wlr_inhibitor; @@ -19,6 +19,7 @@ struct lab_idle_manager { struct wl_listener on_new_inhibitor; struct wl_listener on_destroy; } inhibitor; + struct wlr_seat *wlr_seat; }; static struct lab_idle_manager *manager; @@ -66,10 +67,11 @@ handle_inhibitor_manager_destroy(struct wl_listener *listener, void *data) } void -idle_manager_create(struct wl_display *display) +idle_manager_create(struct wl_display *display, struct wlr_seat *wlr_seat) { assert(!manager); manager = znew(*manager); + manager->wlr_seat = wlr_seat; manager->ext = wlr_idle_notifier_v1_create(display); diff --git a/src/img/img-png.c b/src/img/img-png.c index 29eba44d..79aeafff 100644 --- a/src/img/img-png.c +++ b/src/img/img-png.c @@ -3,14 +3,16 @@ * Copyright (C) Johan Malm 2023 */ #define _POSIX_C_SOURCE 200809L -#include "img/img-png.h" +#include #include #include #include #include +#include #include #include "buffer.h" #include "common/string-helpers.h" +#include "img/img-png.h" /* * cairo_image_surface_create_from_png() does not gracefully handle non-png diff --git a/src/img/img-svg.c b/src/img/img-svg.c index 1c12bde3..b5bf8670 100644 --- a/src/img/img-svg.c +++ b/src/img/img-svg.c @@ -3,13 +3,16 @@ * Copyright (C) Johan Malm 2023 */ #define _POSIX_C_SOURCE 200809L -#include "img/img-svg.h" #include #include +#include #include +#include #include #include "buffer.h" #include "common/string-helpers.h" +#include "img/img-svg.h" +#include "labwc.h" RsvgHandle * img_svg_load(const char *filename) @@ -62,5 +65,6 @@ img_svg_render(RsvgHandle *svg, int w, int h, double scale) error: wlr_buffer_drop(&buffer->base); cairo_destroy(cr); + g_object_unref(svg); return NULL; } diff --git a/src/img/img-xbm.c b/src/img/img-xbm.c index 91269f64..f01b689b 100644 --- a/src/img/img-xbm.c +++ b/src/img/img-xbm.c @@ -6,13 +6,16 @@ */ #define _POSIX_C_SOURCE 200809L -#include "img/img-xbm.h" #include +#include #include #include #include #include -#include "common/buf.h" +#include +#include "img/img.h" +#include "img/img-xbm.h" +#include "common/grab-file.h" #include "common/mem.h" #include "common/string-helpers.h" #include "buffer.h" @@ -138,13 +141,12 @@ tokenize_xbm(char *buffer) add_token(&ctx, TOKEN_IDENT); get_identifier_token(&ctx); continue; - case '0' ... '9': { + case '0' ... '9': add_token(&ctx, TOKEN_INT); get_number_token(&ctx); struct token *token = ctx.tokens + ctx.nr_tokens - 1; token->value = (int)strtol(token->name, NULL, 0); continue; - } case '{': add_token(&ctx, TOKEN_SPECIAL); get_special_char_token(&ctx); @@ -248,7 +250,7 @@ parse_xbm_builtin(const char *button, int size, uint32_t color) t[i].value = button[i]; t[i].type = TOKEN_INT; } - t[size].type = TOKEN_NONE; + t[size].type = 0; process_bytes(&pixmap, t, color); return pixmap; } @@ -273,7 +275,7 @@ img_xbm_load(const char *filename, float *rgba) uint32_t color = argb32(rgba); /* Read file into memory as it's easier to tokenize that way */ - struct buf token_buf = buf_from_file(filename); + struct buf token_buf = grab_file(filename); if (token_buf.len) { struct token *tokens = tokenize_xbm(token_buf.data); pixmap = parse_xbm_tokens(tokens, color); diff --git a/src/img/img-xpm.c b/src/img/img-xpm.c index 0d251b49..ad50bc5e 100644 --- a/src/img/img-xpm.c +++ b/src/img/img-xpm.c @@ -11,7 +11,6 @@ * Adapted for labwc by John Lindgren, 2024 */ -#include "img/img-xpm.h" #include #include #include @@ -22,6 +21,7 @@ #include "common/buf.h" #include "common/graphic-helpers.h" #include "common/mem.h" +#include "img/img-xpm.h" enum buf_op { op_header, op_cmap, op_body }; diff --git a/src/img/img.c b/src/img/img.c index 41bda9e4..03b9130a 100644 --- a/src/img/img.c +++ b/src/img/img.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "img/img.h" #include +#include #include "buffer.h" #include "config.h" #include "common/box.h" @@ -9,6 +9,7 @@ #include "common/macros.h" #include "common/mem.h" #include "common/string-helpers.h" +#include "img/img.h" #include "img/img-png.h" #if HAVE_RSVG #include "img/img-svg.h" diff --git a/src/input/cursor.c b/src/input/cursor.c index fe694db4..c5f9e4ae 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -1,26 +1,20 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "input/cursor.h" #include +#include +#include #include #include -#include #include -#include -#include -#include #include -#include -#include -#include -#include -#include #include #include "action.h" #include "common/macros.h" #include "common/mem.h" +#include "common/scene-helpers.h" +#include "common/surface-helpers.h" #include "config/mousebind.h" -#include "config/rcxml.h" +#include "config/tablet-tool.h" #include "dnd.h" #include "idle.h" #include "input/gestures.h" @@ -30,7 +24,7 @@ #include "labwc.h" #include "layers.h" #include "menu/menu.h" -#include "output.h" +#include "regions.h" #include "resistance.h" #include "resize-outlines.h" #include "ssd.h" @@ -39,12 +33,6 @@ #define LAB_CURSOR_SHAPE_V1_VERSION 1 -struct constraint { - struct seat *seat; - struct wlr_pointer_constraint_v1 *constraint; - struct wl_listener destroy; -}; - static const char * const *cursor_names = NULL; /* Usual cursor names */ @@ -85,34 +73,40 @@ static_assert( "X11 cursor names are out of sync"); enum lab_cursors -cursor_get_from_edge(enum lab_edge resize_edges) +cursor_get_from_edge(uint32_t resize_edges) { switch (resize_edges) { - case LAB_EDGES_TOP_LEFT: + case WLR_EDGE_NONE: + return LAB_CURSOR_DEFAULT; + case WLR_EDGE_TOP | WLR_EDGE_LEFT: return LAB_CURSOR_RESIZE_NW; - case LAB_EDGE_TOP: + case WLR_EDGE_TOP: return LAB_CURSOR_RESIZE_N; - case LAB_EDGES_TOP_RIGHT: + case WLR_EDGE_TOP | WLR_EDGE_RIGHT: return LAB_CURSOR_RESIZE_NE; - case LAB_EDGE_RIGHT: + case WLR_EDGE_RIGHT: return LAB_CURSOR_RESIZE_E; - case LAB_EDGES_BOTTOM_RIGHT: + case WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT: return LAB_CURSOR_RESIZE_SE; - case LAB_EDGE_BOTTOM: + case WLR_EDGE_BOTTOM: return LAB_CURSOR_RESIZE_S; - case LAB_EDGES_BOTTOM_LEFT: + case WLR_EDGE_BOTTOM | WLR_EDGE_LEFT: return LAB_CURSOR_RESIZE_SW; - case LAB_EDGE_LEFT: + case WLR_EDGE_LEFT: return LAB_CURSOR_RESIZE_W; default: - return LAB_CURSOR_DEFAULT; + wlr_log(WLR_ERROR, + "Failed to resolve wlroots edge %u to cursor name", resize_edges); + assert(false); } + + return LAB_CURSOR_DEFAULT; } static enum lab_cursors -cursor_get_from_ssd(enum lab_node_type view_area) +cursor_get_from_ssd(enum ssd_part_type view_area) { - enum lab_edge resize_edges = node_type_to_edges(view_area); + uint32_t resize_edges = ssd_resize_edges(view_area); return cursor_get_from_edge(resize_edges); } @@ -288,9 +282,8 @@ process_cursor_move(struct server *server, uint32_t time) interactive_anchor_to_cursor(server, &new_geo); /* Shaded clients will not process resize events until unshaded */ view_set_shade(view, false); - view_set_maximized(view, VIEW_AXIS_NONE); view_set_untiled(view); - view_move_resize(view, new_geo); + view_restore_to(view, new_geo); x = new_geo.x; y = new_geo.y; } @@ -309,7 +302,6 @@ process_cursor_resize(struct server *server, uint32_t time) 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) { int32_t refresh = 0; if (output_is_usable(last_resize_view->output)) { @@ -334,32 +326,32 @@ process_cursor_resize(struct server *server, uint32_t time) 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 & WLR_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) { + } else if (server->resize_edges & WLR_EDGE_BOTTOM) { new_view_geo.height = server->grab_box.height + dy; } - if (server->resize_edges & LAB_EDGE_LEFT) { + if (server->resize_edges & WLR_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) { + } else if (server->resize_edges & WLR_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 & WLR_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; } - if (server->resize_edges & LAB_EDGE_LEFT) { + if (server->resize_edges & WLR_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; @@ -512,7 +504,7 @@ cursor_update_common(struct server *server, struct cursor_context *ctx, struct seat *seat = &server->seat; struct wlr_seat *wlr_seat = seat->seat; - ssd_update_hovered_button(server, ctx->node); + ssd_update_button_hover(ctx->node, server->ssd_hover_state); if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) { /* @@ -574,18 +566,18 @@ cursor_update_common(struct server *server, struct cursor_context *ctx, return false; } -enum lab_edge +uint32_t cursor_get_resize_edges(struct wlr_cursor *cursor, struct cursor_context *ctx) { - enum lab_edge resize_edges = node_type_to_edges(ctx->type); + uint32_t resize_edges = ssd_resize_edges(ctx->type); if (ctx->view && !resize_edges) { struct wlr_box box = ctx->view->current; resize_edges |= (int)cursor->x < box.x + box.width / 2 ? - LAB_EDGE_LEFT : LAB_EDGE_RIGHT; + WLR_EDGE_LEFT : WLR_EDGE_RIGHT; resize_edges |= (int)cursor->y < box.y + box.height / 2 ? - LAB_EDGE_TOP : LAB_EDGE_BOTTOM; + WLR_EDGE_TOP : WLR_EDGE_BOTTOM; } return resize_edges; } @@ -606,7 +598,7 @@ cursor_process_motion(struct server *server, uint32_t time, double *sx, double * struct cursor_context ctx = get_cursor_context(server); struct seat *seat = &server->seat; - if (ctx.type == LAB_NODE_MENUITEM) { + if (ctx.type == LAB_SSD_MENU) { menu_process_cursor_motion(ctx.node); cursor_set(&server->seat, LAB_CURSOR_DEFAULT); return false; @@ -618,10 +610,6 @@ cursor_process_motion(struct server *server, uint32_t time, double *sx, double * struct mousebind *mousebind; wl_list_for_each(mousebind, &rc.mousebinds, link) { - if (ctx.type == LAB_NODE_CLIENT - && view_inhibits_actions(ctx.view, &mousebind->actions)) { - continue; - } if (mousebind->mouse_event == MOUSE_ACTION_DRAG && mousebind->pressed_in_context) { /* @@ -718,7 +706,7 @@ handle_constraint_commit(struct wl_listener *listener, void *data) struct wlr_pointer_constraint_v1 *constraint = seat->current_constraint; /* Prevents unused variable warning when compiled without asserts */ (void)constraint; - assert(constraint->surface == data); + assert(constraint->surface = data); } static void @@ -880,7 +868,7 @@ handle_motion(struct wl_listener *listener, void *data) cursor_set_visible(seat, /* visible */ true); if (seat->cursor_scroll_wheel_emulation) { - enum wl_pointer_axis orientation; + uint32_t orientation; double delta; if (fabs(event->delta_x) > fabs(event->delta_y)) { orientation = WL_POINTER_AXIS_HORIZONTAL_SCROLL; @@ -961,11 +949,7 @@ process_release_mousebinding(struct server *server, uint32_t modifiers = keyboard_get_all_modifiers(&server->seat); wl_list_for_each(mousebind, &rc.mousebinds, link) { - if (ctx->type == LAB_NODE_CLIENT - && view_inhibits_actions(ctx->view, &mousebind->actions)) { - continue; - } - if (node_type_contains(mousebind->context, ctx->type) + if (ssd_part_contains(mousebind->context, ctx->type) && mousebind->button == button && modifiers == mousebind->modifiers) { switch (mousebind->mouse_event) { @@ -988,7 +972,7 @@ static bool is_double_click(long double_click_speed, uint32_t button, struct cursor_context *ctx) { - static enum lab_node_type last_type; + static enum ssd_part_type last_type; static uint32_t last_button; static struct view *last_view; static struct timespec last_click; @@ -1012,7 +996,7 @@ is_double_click(long double_click_speed, uint32_t button, */ last_button = 0; last_view = NULL; - last_type = LAB_NODE_NONE; + last_type = 0; return true; } return false; @@ -1032,11 +1016,7 @@ process_press_mousebinding(struct server *server, struct cursor_context *ctx, uint32_t modifiers = keyboard_get_all_modifiers(&server->seat); wl_list_for_each(mousebind, &rc.mousebinds, link) { - if (ctx->type == LAB_NODE_CLIENT - && view_inhibits_actions(ctx->view, &mousebind->actions)) { - continue; - } - if (node_type_contains(mousebind->context, ctx->type) + if (ssd_part_contains(mousebind->context, ctx->type) && mousebind->button == button && modifiers == mousebind->modifiers) { switch (mousebind->mouse_event) { @@ -1050,9 +1030,9 @@ process_press_mousebinding(struct server *server, struct cursor_context *ctx, if (!double_click) { /* Swallow the press event */ consumed_by_frame_context |= - mousebind->context == LAB_NODE_FRAME; + mousebind->context == LAB_SSD_FRAME; consumed_by_frame_context |= - mousebind->context == LAB_NODE_ALL; + mousebind->context == LAB_SSD_ALL; mousebind->pressed_in_context = true; } continue; @@ -1066,33 +1046,14 @@ process_press_mousebinding(struct server *server, struct cursor_context *ctx, default: continue; } - consumed_by_frame_context |= mousebind->context == LAB_NODE_FRAME; - consumed_by_frame_context |= mousebind->context == LAB_NODE_ALL; + consumed_by_frame_context |= mousebind->context == LAB_SSD_FRAME; + consumed_by_frame_context |= mousebind->context == LAB_SSD_ALL; actions_run(ctx->view, server, &mousebind->actions, ctx); } } return consumed_by_frame_context; } -static struct wlr_layer_surface_v1 * -get_root_layer(struct wlr_surface *wlr_surface) -{ - assert(wlr_surface); - struct wlr_subsurface *subsurface = - wlr_subsurface_try_from_wlr_surface(wlr_surface); - if (subsurface) { - if (subsurface->parent) { - return get_root_layer(subsurface->parent); - } else { - /* never reached? */ - wlr_log(WLR_ERROR, "subsurface without parent"); - return NULL; - } - } else { - return wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface); - } -} - static uint32_t press_msec; bool @@ -1124,20 +1085,28 @@ cursor_process_button_press(struct seat *seat, uint32_t button, uint32_t time_ms * Action processing does not run for these surfaces and thus * the Focus action (used for normal views) does not work. */ - if (ctx.type == LAB_NODE_LAYER_SURFACE) { - wlr_log(WLR_DEBUG, "press on layer-(sub)surface"); - struct wlr_layer_surface_v1 *layer = get_root_layer(ctx.surface); + if (ctx.type == LAB_SSD_LAYER_SURFACE) { + wlr_log(WLR_DEBUG, "press on layer-surface"); + struct wlr_layer_surface_v1 *layer = + wlr_layer_surface_v1_try_from_wlr_surface(ctx.surface); + if (layer && layer->current.keyboard_interactive) { + layer_try_set_focus(seat, layer); + } + } else if (ctx.type == LAB_SSD_LAYER_SUBSURFACE) { + wlr_log(WLR_DEBUG, "press on layer-subsurface"); + struct wlr_layer_surface_v1 *layer = + subsurface_parent_layer(ctx.surface); if (layer && layer->current.keyboard_interactive) { layer_try_set_focus(seat, layer); } #ifdef HAVE_XWAYLAND - } else if (ctx.type == LAB_NODE_UNMANAGED) { + } else if (ctx.type == LAB_SSD_UNMANAGED) { desktop_focus_view_or_surface(seat, NULL, ctx.surface, /*raise*/ false); #endif } - if (ctx.type != LAB_NODE_CLIENT && ctx.type != LAB_NODE_LAYER_SURFACE + if (ctx.type != LAB_SSD_CLIENT && ctx.type != LAB_SSD_LAYER_SUBSURFACE && wlr_seat_pointer_has_grab(seat->seat)) { /* * If we have an active popup grab (an open popup) we want to @@ -1181,7 +1150,7 @@ cursor_process_button_release(struct seat *seat, uint32_t button, 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) { + if (ctx.type == LAB_SSD_MENU) { menu_call_selected_actions(server); } else { menu_close_root(server); @@ -1276,44 +1245,38 @@ struct scroll_info { }; static struct scroll_info -compare_delta(double delta, double delta_discrete, struct accumulated_scroll *accum) +compare_delta(double delta, double delta_discrete, double *accum) { + /* + * Smooth scroll deltas are in surface space, so treating each unit as a + * scroll event would result in too-fast scrolling. + * + * This fudge factor (inherited from various historic projects, incl. Weston) + * produces events at a more reasonable rate. + * + * For historic context, see: + * https://lists.freedesktop.org/archives/wayland-devel/2019-April/040377.html + */ + const double SCROLL_THRESHOLD = 10.0; struct scroll_info info = {0}; - if (delta_discrete) { - /* mice */ - info.direction = delta_discrete > 0 ? 1 : -1; - accum->delta_discrete += delta_discrete; - /* - * Non-hi-res mice produce delta_discrete of ±120 for every - * "click", so it always triggers actions. But for hi-res mice - * that produce smaller delta_discrete, we accumulate it and - * run actions after it exceeds 120(= 1 click). - */ - if (fabs(accum->delta_discrete) >= 120.0) { - accum->delta_discrete = fmod(accum->delta_discrete, 120.0); - info.run_action = true; - } + if (delta_discrete < 0 || delta < 0) { + info.direction = -1; + } else if (delta_discrete > 0 || delta > 0) { + info.direction = 1; + } + + if (delta == 0.0) { + /* Delta 0 marks the end of a scroll */ + *accum = 0.0; } else { - /* 2-finger scrolling on touchpads */ - if (delta == 0) { - /* delta=0 marks the end of a scroll */ - accum->delta = 0; - return info; - } - info.direction = delta > 0 ? 1 : -1; - accum->delta += delta; - /* - * The threshold of 10 is inherited from various historic - * projects including weston. - * - * For historic context, see: - * https://lists.freedesktop.org/archives/wayland-devel/2019-April/040377.html - */ - if (fabs(accum->delta) >= 10.0) { - accum->delta = fmod(accum->delta, 10.0); - info.run_action = true; - } + /* Accumulate smooth scrolling until we hit threshold */ + *accum += delta; + } + + if (delta_discrete != 0 || fabs(*accum) > SCROLL_THRESHOLD) { + *accum = fmod(*accum, SCROLL_THRESHOLD); + info.run_action = true; } return info; @@ -1327,16 +1290,21 @@ process_cursor_axis(struct server *server, enum wl_pointer_axis orientation, 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]); + struct scroll_info info = {0}; if (orientation == WL_POINTER_AXIS_HORIZONTAL_SCROLL) { + info = compare_delta(delta, delta_discrete, + &server->seat.smooth_scroll_offset.x); + if (info.direction < 0) { direction = LAB_DIRECTION_LEFT; } else if (info.direction > 0) { direction = LAB_DIRECTION_RIGHT; } } else if (orientation == WL_POINTER_AXIS_VERTICAL_SCROLL) { + info = compare_delta(delta, delta_discrete, + &server->seat.smooth_scroll_offset.y); + if (info.direction < 0) { direction = LAB_DIRECTION_UP; } else if (info.direction > 0) { @@ -1346,23 +1314,18 @@ process_cursor_axis(struct server *server, enum wl_pointer_axis orientation, wlr_log(WLR_DEBUG, "Failed to handle cursor axis event"); } - bool consumed = false; + bool handled = false; if (direction != LAB_DIRECTION_INVALID) { struct mousebind *mousebind; wl_list_for_each(mousebind, &rc.mousebinds, link) { - if (ctx.type == LAB_NODE_CLIENT - && view_inhibits_actions(ctx.view, &mousebind->actions)) { - continue; - } - if (node_type_contains(mousebind->context, ctx.type) + if (ssd_part_contains(mousebind->context, ctx.type) && mousebind->direction == direction && modifiers == mousebind->modifiers && mousebind->mouse_event == MOUSE_ACTION_SCROLL) { - consumed |= mousebind->context == LAB_NODE_FRAME; - consumed |= mousebind->context == LAB_NODE_ALL; + handled = true; /* - * Action may not be executed if the accumulated scroll delta - * on touchpads or hi-res mice doesn't exceed the threshold + * Action may not be executed if the accumulated scroll + * delta on touchpads doesn't exceed the threshold */ if (info.run_action) { actions_run(ctx.view, server, &mousebind->actions, &ctx); @@ -1372,7 +1335,7 @@ process_cursor_axis(struct server *server, enum wl_pointer_axis orientation, } /* Bindings swallow mouse events if activated */ - if (ctx.surface && !consumed) { + if (ctx.surface && !handled) { /* Make sure we are sending the events to the surface under the cursor */ double sx, sy; cursor_update_common(server, &ctx, /*cursor_has_moved*/ false, &sx, &sy); diff --git a/src/input/gestures.c b/src/input/gestures.c index b40da82c..4cb94c6f 100644 --- a/src/input/gestures.c +++ b/src/input/gestures.c @@ -1,8 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "input/gestures.h" -#include #include #include "common/macros.h" +#include "input/gestures.h" #include "labwc.h" #include "idle.h" diff --git a/src/input/ime.c b/src/input/ime.c index 92d88ffe..ded28f5c 100644 --- a/src/input/ime.c +++ b/src/input/ime.c @@ -1,21 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-only /* Based on Sway (https://github.com/swaywm/sway) */ -#include "input/ime.h" #include -#include -#include -#include -#include -#include -#include -#include -#include #include "common/mem.h" -#include "input/keyboard.h" -#include "labwc.h" +#include "input/ime.h" #include "node.h" -#include "output.h" +#include "view.h" #define SAME_CLIENT(wlr_obj1, wlr_obj2) \ (wl_resource_get_client((wlr_obj1)->resource) \ @@ -234,13 +224,13 @@ update_popup_position(struct input_method_popup *popup) .anchor_rect = cursor_rect, .anchor = XDG_POSITIONER_ANCHOR_BOTTOM_LEFT, .gravity = XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT, - .constraint_adjustment = - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y - | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X, .size = { .width = popup->popup_surface->surface->current.width, .height = popup->popup_surface->surface->current.height, }, + .constraint_adjustment = + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y + | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X, }; struct wlr_box popup_box; @@ -275,7 +265,8 @@ handle_input_method_commit(struct wl_listener *listener, void *data) { struct input_method_relay *relay = wl_container_of(listener, relay, input_method_commit); - struct wlr_input_method_v2 *input_method = relay->input_method; + struct wlr_input_method_v2 *input_method = data; + assert(relay->input_method == input_method); struct text_input *text_input = relay->active_text_input; if (!text_input) { @@ -310,8 +301,6 @@ 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; - assert(keyboard_grab); - wl_list_remove(&relay->keyboard_grab_destroy.link); if (keyboard_grab->keyboard) { @@ -349,6 +338,7 @@ handle_input_method_destroy(struct wl_listener *listener, void *data) { struct input_method_relay *relay = wl_container_of(listener, relay, input_method_destroy); + assert(relay->input_method == data); wl_list_remove(&relay->input_method_commit.link); wl_list_remove(&relay->input_method_grab_keyboard.link); wl_list_remove(&relay->input_method_new_popup_surface.link); @@ -398,8 +388,7 @@ 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); - node_descriptor_create(&popup->tree->node, LAB_NODE_IME_POPUP, - /*view*/ NULL, /*data*/ NULL); + node_descriptor_create(&popup->tree->node, LAB_NODE_DESC_IME_POPUP, NULL); wl_list_insert(&relay->popups, &popup->link); diff --git a/src/input/input.c b/src/input/input.c index 61afdc6d..d78dca1a 100644 --- a/src/input/input.c +++ b/src/input/input.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "input/input.h" #include "input/cursor.h" +#include "input/input.h" #include "input/keyboard.h" void diff --git a/src/input/key-state.c b/src/input/key-state.c index 492f7b0f..24a3f606 100644 --- a/src/input/key-state.c +++ b/src/input/key-state.c @@ -1,10 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "input/key-state.h" #include #include #include #include +#include +#include #include "common/set.h" +#include "input/key-state.h" static struct lab_set pressed, bound, pressed_sent; @@ -84,3 +86,9 @@ key_state_nr_bound_keys(void) { return bound.size; } + +int +key_state_nr_pressed_keys(void) +{ + return pressed.size; +} diff --git a/src/input/keyboard.c b/src/input/keyboard.c index 8f7a3e81..4c53b580 100644 --- a/src/input/keyboard.c +++ b/src/input/keyboard.c @@ -1,23 +1,21 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "input/keyboard.h" #include #include +#include #include #include -#include -#include #include "action.h" #include "common/macros.h" -#include "config/keybind.h" -#include "config/rcxml.h" +#include "common/three-state.h" #include "idle.h" #include "input/ime.h" +#include "input/keyboard.h" #include "input/key-state.h" #include "labwc.h" #include "menu/menu.h" #include "osd.h" -#include "session-lock.h" +#include "regions.h" #include "view.h" #include "workspaces.h" @@ -96,9 +94,6 @@ end_cycling(struct server *server) /* FIXME: osd_finish() transiently sets focus to the old surface */ osd_finish(server); /* Note that server->osd_state.cycle_view is cleared at this point */ - if (rc.window_switcher.unshade) { - view_set_shade(cycle_view, false); - } desktop_focus_view(cycle_view, /*raise*/ true); } @@ -216,19 +211,25 @@ match_keybinding_for_sym(struct server *server, uint32_t modifiers, if (modifiers ^ keybind->modifiers) { continue; } - if (view_inhibits_actions(server->active_view, &keybind->actions)) { + if (server->seat.nr_inhibited_keybind_views + && server->active_view + && server->active_view->inhibits_keybinds + && !actions_contain_toggle_keybinds(&keybind->actions)) { continue; } if (sym == XKB_KEY_NoSymbol) { /* Use keycodes */ - if (keybind_contains_keycode(keybind, xkb_keycode)) { - return keybind; + for (size_t i = 0; i < keybind->keycodes_len; i++) { + if (keybind->keycodes[i] == xkb_keycode) { + return keybind; + } } } else { /* Use syms */ - if (keybind_contains_keysym(keybind, - xkb_keysym_to_lower(sym))) { - return keybind; + for (size_t i = 0; i < keybind->keysyms_len; i++) { + if (xkb_keysym_to_lower(sym) == keybind->keysyms[i]) { + return keybind; + } } } } @@ -264,21 +265,23 @@ static struct keybind * match_keybinding(struct server *server, struct keyinfo *keyinfo, bool is_virtual) { - if (!is_virtual) { - /* First try keycodes */ - struct keybind *keybind = match_keybinding_for_sym(server, - keyinfo->modifiers, XKB_KEY_NoSymbol, keyinfo->xkb_keycode); - if (keybind) { - wlr_log(WLR_DEBUG, "keycode matched"); - return keybind; - } + if (is_virtual) { + goto process_syms; } + /* First try keycodes */ + struct keybind *keybind = match_keybinding_for_sym(server, + keyinfo->modifiers, XKB_KEY_NoSymbol, keyinfo->xkb_keycode); + if (keybind) { + wlr_log(WLR_DEBUG, "keycode matched"); + return keybind; + } + +process_syms: /* 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, - keyinfo->translated.syms[i], keyinfo->xkb_keycode); + keybind = match_keybinding_for_sym(server, keyinfo->modifiers, + keyinfo->translated.syms[i], keyinfo->xkb_keycode); if (keybind) { wlr_log(WLR_DEBUG, "translated keysym matched"); return keybind; @@ -287,9 +290,8 @@ 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, - keyinfo->raw.syms[i], keyinfo->xkb_keycode); + keybind = match_keybinding_for_sym(server, keyinfo->modifiers, + keyinfo->raw.syms[i], keyinfo->xkb_keycode); if (keybind) { wlr_log(WLR_DEBUG, "raw keysym matched"); return keybind; @@ -385,7 +387,7 @@ get_keyinfo(struct wlr_keyboard *wlr_keyboard, uint32_t evdev_keycode) return keyinfo; } -static enum lab_key_handled +static bool handle_key_release(struct server *server, uint32_t evdev_keycode) { /* @@ -393,7 +395,7 @@ handle_key_release(struct server *server, uint32_t evdev_keycode) * forwarded to clients to avoid stuck keys. */ if (!key_state_corresponding_press_event_was_bound(evdev_keycode)) { - return LAB_KEY_HANDLED_FALSE; + return false; } /* @@ -414,7 +416,7 @@ handle_key_release(struct server *server, uint32_t evdev_keycode) * not forward the corresponding release event to clients. */ key_state_bound_key_remove(evdev_keycode); - return LAB_KEY_HANDLED_TRUE; + return true; } static bool @@ -516,10 +518,10 @@ handle_compositor_keybindings(struct keyboard *keyboard, key_state_bound_key_remove(event->keycode); if (locked && !cur_keybind->allow_when_locked) { cur_keybind = NULL; - return LAB_KEY_HANDLED_TRUE; + return true; } actions_run(NULL, server, &cur_keybind->actions, NULL); - return LAB_KEY_HANDLED_TRUE; + return true; } else { return handle_key_release(server, event->keycode); } @@ -540,11 +542,11 @@ handle_compositor_keybindings(struct keyboard *keyboard, if (server->input_mode == LAB_INPUT_STATE_MENU) { key_state_store_pressed_key_as_bound(event->keycode); handle_menu_keys(server, &keyinfo.translated); - return LAB_KEY_HANDLED_TRUE; + return true; } else if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) { if (handle_cycle_view_key(server, &keyinfo)) { key_state_store_pressed_key_as_bound(event->keycode); - return LAB_KEY_HANDLED_TRUE; + return true; } } } @@ -563,10 +565,10 @@ handle_compositor_keybindings(struct keyboard *keyboard, if (!cur_keybind->on_release) { actions_run(NULL, server, &cur_keybind->actions, NULL); } - return LAB_KEY_HANDLED_TRUE; + return true; } - return LAB_KEY_HANDLED_FALSE; + return false; } static int diff --git a/src/input/tablet-pad.c b/src/input/tablet-pad.c index 230b6e6b..2453be96 100644 --- a/src/input/tablet-pad.c +++ b/src/input/tablet-pad.c @@ -1,16 +1,15 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "input/tablet-pad.h" +#include #include #include -#include #include -#include +#include #include #include "common/macros.h" #include "common/mem.h" #include "config/rcxml.h" -#include "config/tablet.h" #include "input/cursor.h" +#include "input/tablet-pad.h" #include "input/tablet.h" #include "labwc.h" diff --git a/src/input/tablet.c b/src/input/tablet.c index 319c06b3..a4ec328a 100644 --- a/src/input/tablet.c +++ b/src/input/tablet.c @@ -1,10 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "input/tablet.h" +#include #include #include -#include +#include #include -#include #include #include #include "common/macros.h" @@ -12,13 +11,12 @@ #include "common/scene-helpers.h" #include "config/rcxml.h" #include "config/mousebind.h" -#include "config/tablet.h" #include "input/cursor.h" +#include "input/tablet.h" #include "input/tablet-pad.h" #include "labwc.h" #include "idle.h" #include "action.h" -#include "view.h" bool tablet_tool_has_focused_surface(struct seat *seat) @@ -94,8 +92,8 @@ tablet_tool_create(struct seat *seat, return tool; } -static enum lab_motion -tool_motion_mode(enum lab_motion motion, struct wlr_tablet_tool *tool) +static enum motion +tool_motion_mode(enum motion motion, struct wlr_tablet_tool *tool) { /* * Absolute positioning doesn't make sense @@ -104,7 +102,7 @@ tool_motion_mode(enum lab_motion motion, struct wlr_tablet_tool *tool) switch (tool->type) { case WLR_TABLET_TOOL_TYPE_MOUSE: case WLR_TABLET_TOOL_TYPE_LENS: - return LAB_MOTION_RELATIVE; + return LAB_TABLET_MOTION_RELATIVE; default: return motion; } @@ -139,7 +137,7 @@ adjust_for_tablet_area(double tablet_width, double tablet_height, } static void -adjust_for_rotation(enum lab_rotation rotation, double *x, double *y) +adjust_for_rotation(enum rotation rotation, double *x, double *y) { double tmp; switch (rotation) { @@ -163,7 +161,7 @@ adjust_for_rotation(enum lab_rotation rotation, double *x, double *y) } static void -adjust_for_rotation_relative(enum lab_rotation rotation, double *dx, double *dy) +adjust_for_rotation_relative(enum rotation rotation, double *dx, double *dy) { double tmp; switch (rotation) { @@ -222,11 +220,11 @@ tablet_get_coords(struct drawing_tablet *tablet, struct drawing_tablet_tool *too /* initialize here to avoid a maybe-uninitialized compiler warning */ double lx = -1, ly = -1; switch (tool->motion_mode) { - case LAB_MOTION_ABSOLUTE: + case LAB_TABLET_MOTION_ABSOLUTE: wlr_cursor_absolute_to_layout_coords(tablet->seat->cursor, tablet->wlr_input_device, *x, *y, &lx, &ly); break; - case LAB_MOTION_RELATIVE: + case LAB_TABLET_MOTION_RELATIVE: /* * Deltas dx,dy will be directly passed into wlr_cursor_move, * so we can add those directly here to determine our future @@ -264,11 +262,11 @@ notify_motion(struct drawing_tablet *tablet, struct drawing_tablet_tool *tool, } switch (tool->motion_mode) { - case LAB_MOTION_ABSOLUTE: + case LAB_TABLET_MOTION_ABSOLUTE: wlr_cursor_warp_absolute(tablet->seat->cursor, tablet->wlr_input_device, x, y); break; - case LAB_MOTION_RELATIVE: + case LAB_TABLET_MOTION_RELATIVE: wlr_cursor_move(tablet->seat->cursor, tablet->wlr_input_device, dx, dy); break; @@ -502,12 +500,12 @@ handle_tablet_tool_axis(struct wl_listener *listener, void *data) } switch (tool->motion_mode) { - case LAB_MOTION_ABSOLUTE: + case LAB_TABLET_MOTION_ABSOLUTE: cursor_emulate_move_absolute(tablet->seat, &ev->tablet->base, x, y, ev->time_msec); break; - case LAB_MOTION_RELATIVE: + case LAB_TABLET_MOTION_RELATIVE: cursor_emulate_move(tablet->seat, &ev->tablet->base, dx, dy, ev->time_msec); @@ -666,7 +664,7 @@ handle_tablet_tool_button(struct wl_listener *listener, void *data) wl_list_for_each(mousebind, &rc.mousebinds, link) { if (mousebind->mouse_event == MOUSE_ACTION_PRESS && mousebind->button == button - && mousebind->context == LAB_NODE_CLIENT) { + && mousebind->context == LAB_SSD_CLIENT) { actions_run(view, tool->seat->server, &mousebind->actions, NULL); } diff --git a/src/input/touch.c b/src/input/touch.c index d6557014..ec6a2bf7 100644 --- a/src/input/touch.c +++ b/src/input/touch.c @@ -1,22 +1,15 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "input/touch.h" #include -#include -#include -#include #include #include -#include "action.h" #include "common/macros.h" #include "common/mem.h" #include "common/scene-helpers.h" -#include "config/mousebind.h" -#include "config/rcxml.h" -#include "config/touch.h" #include "idle.h" +#include "input/touch.h" #include "labwc.h" -#include "ssd.h" -#include "view.h" +#include "config/mousebind.h" +#include "action.h" /* Holds layout -> surface offsets to report motion events in relative coords */ struct touch_point { @@ -168,7 +161,7 @@ handle_touch_down(struct wl_listener *listener, void *data) wl_list_for_each(mousebind, &rc.mousebinds, link) { if (mousebind->mouse_event == MOUSE_ACTION_PRESS && mousebind->button == BTN_LEFT - && mousebind->context == LAB_NODE_CLIENT) { + && mousebind->context == LAB_SSD_CLIENT) { actions_run(view, seat->server, &mousebind->actions, NULL); } } @@ -207,7 +200,7 @@ handle_touch_up(struct wl_listener *listener, void *data) } else { cursor_emulate_button(seat, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED, event->time_msec); - ssd_update_hovered_button(seat->server, NULL); + ssd_update_button_hover(NULL, seat->server->ssd_hover_state); } wl_list_remove(&touch_point->link); zfree(touch_point); diff --git a/src/interactive.c b/src/interactive.c index 8ca258c1..bd620edb 100644 --- a/src/interactive.c +++ b/src/interactive.c @@ -1,13 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-only #include -#include -#include "config/rcxml.h" #include "edges.h" #include "input/keyboard.h" #include "labwc.h" -#include "output.h" #include "regions.h" #include "resize-indicator.h" +#include "snap.h" #include "view.h" #include "window-rules.h" @@ -55,7 +53,7 @@ interactive_anchor_to_cursor(struct server *server, struct wlr_box *geo) } void -interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges) +interactive_begin(struct view *view, enum input_mode mode, uint32_t edges) { /* * This function sets up an interactive move or resize operation, where @@ -90,9 +88,9 @@ interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges) return; } - /* Store natural geometry at start of move */ - view_store_natural_geometry(view); if (view_is_floating(view)) { + /* Store natural geometry at start of move */ + view_store_natural_geometry(view); view_invalidate_last_layout_geometry(view); } @@ -101,7 +99,7 @@ interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges) cursor_shape = LAB_CURSOR_GRAB; break; - case LAB_INPUT_STATE_RESIZE: { + case LAB_INPUT_STATE_RESIZE: if (view->shaded || view->fullscreen || view->maximized == VIEW_AXIS_BOTH) { /* @@ -119,21 +117,13 @@ interactive_begin(struct view *view, enum input_mode mode, enum lab_edge 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. + * maximized/tiled state but keep the same geometry as + * the starting point for the resize. */ - enum view_axis maximized = view->maximized; - if (edges & LAB_EDGES_LEFT_RIGHT) { - maximized &= ~VIEW_AXIS_HORIZONTAL; - } - if (edges & LAB_EDGES_TOP_BOTTOM) { - maximized &= ~VIEW_AXIS_VERTICAL; - } - view_set_maximized(view, maximized); view_set_untiled(view); + view_restore_to(view, view->pending); cursor_shape = cursor_get_from_edge(edges); break; - } default: /* Should not be reached */ return; @@ -161,9 +151,8 @@ interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges) interactive_anchor_to_cursor(server, &natural_geo); /* Shaded clients will not process resize events until unshaded */ view_set_shade(view, false); - view_set_maximized(view, VIEW_AXIS_NONE); view_set_untiled(view); - view_move_resize(view, natural_geo); + view_restore_to(view, natural_geo); } if (rc.resize_indicator) { @@ -174,26 +163,22 @@ interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges) } } -bool -edge_from_cursor(struct seat *seat, struct output **dest_output, - enum lab_edge *edge1, enum lab_edge *edge2) +enum view_edge +edge_from_cursor(struct seat *seat, struct output **dest_output) { - *dest_output = NULL; - *edge1 = LAB_EDGE_NONE; - *edge2 = LAB_EDGE_NONE; - if (!view_is_floating(seat->server->grabbed_view)) { - return false; + return VIEW_EDGE_INVALID; } - if (rc.snap_edge_range == 0) { - return false; + int snap_range = rc.snap_edge_range; + if (!snap_range) { + return VIEW_EDGE_INVALID; } struct output *output = output_nearest_to_cursor(seat->server); if (!output_is_usable(output)) { wlr_log(WLR_ERROR, "output at cursor is unusable"); - return false; + return VIEW_EDGE_INVALID; } *dest_output = output; @@ -204,39 +189,22 @@ edge_from_cursor(struct seat *seat, struct output **dest_output, output->wlr_output, &cursor_x, &cursor_y); struct wlr_box *area = &output->usable_area; - - int top = cursor_y - area->y; - int bottom = area->y + area->height - cursor_y; - int left = cursor_x - area->x; - int right = area->x + area->width - cursor_x; - - if (top < rc.snap_edge_range) { - *edge1 = LAB_EDGE_TOP; - } else if (bottom < rc.snap_edge_range) { - *edge1 = LAB_EDGE_BOTTOM; - } else if (left < rc.snap_edge_range) { - *edge1 = LAB_EDGE_LEFT; - } else if (right < rc.snap_edge_range) { - *edge1 = LAB_EDGE_RIGHT; + if (cursor_x <= area->x + snap_range) { + return VIEW_EDGE_LEFT; + } else if (cursor_x >= area->x + area->width - snap_range) { + return VIEW_EDGE_RIGHT; + } else if (cursor_y <= area->y + snap_range) { + if (rc.snap_top_maximize) { + return VIEW_EDGE_CENTER; + } else { + return VIEW_EDGE_UP; + } + } else if (cursor_y >= area->y + area->height - snap_range) { + return VIEW_EDGE_DOWN; } else { - return false; + /* Not close to any edge */ + return VIEW_EDGE_INVALID; } - - if (*edge1 == LAB_EDGE_TOP || *edge1 == LAB_EDGE_BOTTOM) { - if (left < rc.snap_edge_corner_range) { - *edge2 = LAB_EDGE_LEFT; - } else if (right < rc.snap_edge_corner_range) { - *edge2 = LAB_EDGE_RIGHT; - } - } else if (*edge1 == LAB_EDGE_LEFT || *edge1 == LAB_EDGE_RIGHT) { - if (top < rc.snap_edge_corner_range) { - *edge2 = LAB_EDGE_TOP; - } else if (bottom < rc.snap_edge_corner_range) { - *edge2 = LAB_EDGE_BOTTOM; - } - } - - return true; } /* Returns true if view was snapped to any edge */ @@ -244,24 +212,24 @@ static bool snap_to_edge(struct view *view) { struct output *output; - enum lab_edge edge1, edge2; - if (!edge_from_cursor(&view->server->seat, &output, &edge1, &edge2)) { + enum view_edge edge = edge_from_cursor(&view->server->seat, &output); + if (edge == VIEW_EDGE_INVALID) { return false; } - enum lab_edge edge = edge1 | edge2; view_set_output(view, output); /* * Don't store natural geometry here (it was * stored already in interactive_begin()) */ - if (edge == LAB_EDGE_TOP && rc.snap_top_maximize) { + if (edge == VIEW_EDGE_CENTER) { /* */ view_maximize(view, VIEW_AXIS_BOTH, /*store_natural_geometry*/ false); } else { - view_snap_to_edge(view, edge, /*across_outputs*/ false, - /*combine*/ false, /*store_natural_geometry*/ false); + view_snap_to_edge(view, edge, + /*across_outputs*/ false, + /*store_natural_geometry*/ false); } return true; @@ -311,7 +279,7 @@ interactive_cancel(struct view *view) return; } - overlay_finish(&view->server->seat); + overlay_hide(&view->server->seat); resize_indicator_hide(view); diff --git a/src/layers.c b/src/layers.c index f26208ef..2ae560df 100644 --- a/src/layers.c +++ b/src/layers.c @@ -6,23 +6,21 @@ * Copyright (C) 2019 Drew DeVault and Sway developers */ -#include "layers.h" #include #include #include +#include #include -#include +#include #include #include -#include -#include #include #include "common/macros.h" #include "common/mem.h" #include "config/rcxml.h" +#include "layers.h" #include "labwc.h" #include "node.h" -#include "output.h" #define LAB_LAYERSHELL_VERSION 4 @@ -473,7 +471,7 @@ create_popup(struct server *server, struct wlr_xdg_popup *wlr_popup, wlr_popup->base->surface->data = popup->scene_tree; node_descriptor_create(&popup->scene_tree->node, - LAB_NODE_LAYER_POPUP, /*view*/ NULL, popup); + LAB_NODE_DESC_LAYER_POPUP, popup); popup->destroy.notify = handle_popup_destroy; wl_signal_add(&wlr_popup->events.destroy, &popup->destroy); @@ -624,7 +622,7 @@ handle_new_layer_surface(struct wl_listener *listener, void *data) layer_surface->surface->data = surface->scene_layer_surface->tree; node_descriptor_create(&surface->scene_layer_surface->tree->node, - LAB_NODE_LAYER_SURFACE, /*view*/ NULL, surface); + LAB_NODE_DESC_LAYER_SURFACE, surface); surface->server = server; surface->scene_layer_surface->layer_surface = layer_surface; diff --git a/src/magnifier.c b/src/magnifier.c index 424865c9..0846e02a 100644 --- a/src/magnifier.c +++ b/src/magnifier.c @@ -1,17 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "magnifier.h" #include -#include #include -#include #include -#include #include #include "common/box.h" -#include "config/rcxml.h" #include "labwc.h" -#include "output.h" +#include "magnifier.h" #include "theme.h" static bool magnify_on; diff --git a/src/main.c b/src/main.c index 755a0a0c..ef093112 100644 --- a/src/main.c +++ b/src/main.c @@ -1,17 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include #include #include +#include #include +#include "common/dir.h" #include "common/fd-util.h" #include "common/font.h" +#include "common/mem.h" #include "common/spawn.h" -#include "config/rcxml.h" #include "config/session.h" #include "labwc.h" #include "theme.h" -#include "translate.h" #include "menu/menu.h" struct rcxml rc = { 0 }; diff --git a/src/menu/menu.c b/src/menu/menu.c index bc0fdcad..c89c2b88 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -1,15 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "menu/menu.h" #include #include +#include #include #include #include #include #include -#include -#include +#include #include #include #include "action.h" @@ -18,26 +17,33 @@ #include "common/font.h" #include "common/lab-scene-rect.h" #include "common/list.h" +#include "common/macros.h" #include "common/mem.h" +#include "common/nodename.h" +#include "common/scaled-font-buffer.h" +#include "common/scaled-icon-buffer.h" +#include "common/scene-helpers.h" #include "common/spawn.h" #include "common/string-helpers.h" -#include "common/xml.h" -#include "config/rcxml.h" #include "labwc.h" -#include "node.h" -#include "output.h" -#include "scaled-buffer/scaled-font-buffer.h" -#include "scaled-buffer/scaled-icon-buffer.h" -#include "theme.h" -#include "translate.h" -#include "view.h" +#include "menu/menu.h" #include "workspaces.h" +#include "view.h" +#include "node.h" +#include "theme.h" #define PIPEMENU_MAX_BUF_SIZE 1048576 /* 1 MiB */ #define PIPEMENU_TIMEOUT_IN_MS 4000 /* 4 seconds */ #define ICON_SIZE (rc.theme->menu_item_height - 2 * rc.theme->menu_items_padding_y) +/* state-machine variables for processing */ +static bool in_item; +static struct menuitem *current_item; +static struct action *current_item_action; + +static struct menu *current_menu; + static bool waiting_for_pipe_menu; static struct menuitem *selected_item; @@ -66,8 +72,7 @@ is_unique_id(struct server *server, const char *id) } static struct menu * -menu_create(struct server *server, struct menu *parent, const char *id, - const char *label) +menu_create(struct server *server, const char *id, const char *label) { if (!is_unique_id(server, id)) { wlr_log(WLR_ERROR, "menu id %s already exists", id); @@ -79,7 +84,7 @@ menu_create(struct server *server, struct menu *parent, const char *id, wl_list_init(&menu->menuitems); menu->id = xstrdup(id); menu->label = xstrdup(label ? label : id); - menu->parent = parent; + menu->parent = current_menu; menu->server = server; menu->is_pipemenu_child = waiting_for_pipe_menu; return menu; @@ -131,12 +136,11 @@ validate(struct server *server) } static struct menuitem * -item_create(struct menu *menu, const char *text, const char *icon_name, bool show_arrow) +item_create(struct menu *menu, const char *text, bool show_arrow) { assert(menu); assert(text); - struct theme *theme = menu->server->theme; struct menuitem *menuitem = znew(*menuitem); menuitem->parent = menu; menuitem->selectable = true; @@ -144,17 +148,9 @@ item_create(struct menu *menu, const char *text, const char *icon_name, bool sho menuitem->text = xstrdup(text); menuitem->arrow = show_arrow ? "›" : NULL; -#if HAVE_LIBSFDO - if (rc.menu_show_icons && !string_null_or_empty(icon_name)) { - menuitem->icon_name = xstrdup(icon_name); - menu->has_icons = true; - } -#endif - menuitem->native_width = font_width(&rc.font_menuitem, text); if (menuitem->arrow) { - menuitem->native_width += font_width(&rc.font_menuitem, menuitem->arrow) - + theme->menu_items_padding_x; + menuitem->native_width += font_width(&rc.font_menuitem, menuitem->arrow); } wl_list_append(&menu->menuitems, &menuitem->link); @@ -180,7 +176,7 @@ item_create_scene_for_state(struct menuitem *item, float *text_color, int bg_width = menu->size.width - 2 * theme->menu_border_width; int arrow_width = item->arrow ? - font_width(&rc.font_menuitem, item->arrow) + theme->menu_items_padding_x : 0; + font_width(&rc.font_menuitem, item->arrow) : 0; int label_max_width = bg_width - 2 * theme->menu_items_padding_x - arrow_width - icon_width; @@ -230,7 +226,7 @@ item_create_scene_for_state(struct menuitem *item, float *text_color, scaled_font_buffer_update(arrow_buffer, item->arrow, -1, &rc.font_menuitem, text_color, bg_color); /* Vertically center and right-align arrow */ - x += label_max_width + theme->menu_items_padding_x; + x += label_max_width; y = (theme->menu_item_height - label_buffer->height) / 2; wlr_scene_node_set_position(&arrow_buffer->scene_buffer->node, x, y); @@ -247,8 +243,8 @@ item_create_scene(struct menuitem *menuitem, int *item_y) /* Menu item root node */ menuitem->tree = wlr_scene_tree_create(menu->scene_tree); - node_descriptor_create(&menuitem->tree->node, LAB_NODE_MENUITEM, - /*view*/ NULL, menuitem); + node_descriptor_create(&menuitem->tree->node, + LAB_NODE_DESC_MENUITEM, menuitem); /* Create scenes for unselected/selected states */ menuitem->normal_tree = item_create_scene_for_state(menuitem, @@ -269,8 +265,6 @@ item_create_scene(struct menuitem *menuitem, int *item_y) static struct menuitem * separator_create(struct menu *menu, const char *label) { - assert(menu); - struct menuitem *menuitem = znew(*menuitem); menuitem->parent = menu; menuitem->selectable = false; @@ -296,8 +290,8 @@ separator_create_scene(struct menuitem *menuitem, int *item_y) /* Menu item root node */ menuitem->tree = wlr_scene_tree_create(menu->scene_tree); - node_descriptor_create(&menuitem->tree->node, LAB_NODE_MENUITEM, - /*view*/ NULL, menuitem); + node_descriptor_create(&menuitem->tree->node, + LAB_NODE_DESC_MENUITEM, menuitem); /* Tree to hold background and line buffer */ menuitem->normal_tree = wlr_scene_tree_create(menuitem->tree); @@ -344,8 +338,8 @@ title_create_scene(struct menuitem *menuitem, int *item_y) /* Menu item root node */ menuitem->tree = wlr_scene_tree_create(menu->scene_tree); - node_descriptor_create(&menuitem->tree->node, LAB_NODE_MENUITEM, - /*view*/ NULL, menuitem); + node_descriptor_create(&menuitem->tree->node, + LAB_NODE_DESC_MENUITEM, menuitem); /* Tree to hold background and text buffer */ menuitem->normal_tree = wlr_scene_tree_create(menuitem->tree); @@ -473,22 +467,42 @@ menu_create_scene(struct menu *menu) * */ static void -fill_item(struct menu *menu, xmlNode *node) +fill_item(char *nodename, char *content) { - char *label = (char *)xmlGetProp(node, (xmlChar *)"label"); - char *icon_name = (char *)xmlGetProp(node, (xmlChar *)"icon"); - if (!label) { - wlr_log(WLR_ERROR, "missing label in "); - goto out; + /* + * Nodenames for most menu-items end with '.item.menu' but top-level + * pipemenu items do not have the associated element so merely + * end with a '.item' + */ + string_truncate_at_pattern(nodename, ".item.menu"); + string_truncate_at_pattern(nodename, ".item"); + + /* defines the start of a new item */ + if (!strcmp(nodename, "label")) { + current_item = item_create(current_menu, content, false); + current_item_action = NULL; + } else if (!current_item) { + wlr_log(WLR_ERROR, "expect element first. " + "nodename: '%s' content: '%s'", nodename, content); + } else if (!strcmp(nodename, "icon")) { +#if HAVE_LIBSFDO + if (rc.menu_show_icons && !string_null_or_empty(content)) { + xstrdup_replace(current_item->icon_name, content); + current_menu->has_icons = true; + } +#endif + } else if (!strcmp(nodename, "name.action")) { + current_item_action = action_create(content); + if (current_item_action) { + wl_list_append(¤t_item->actions, + ¤t_item_action->link); + } + } else if (!current_item_action) { + wlr_log(WLR_ERROR, "expect element first. " + "nodename: '%s' content: '%s'", nodename, content); + } else { + action_arg_from_xml_node(current_item_action, nodename, content); } - - struct menuitem *item = item_create(menu, label, icon_name, false); - lab_xml_expand_dotted_attributes(node); - append_parsed_actions(node, &item->actions); - -out: - xmlFree(label); - xmlFree(icon_name); } static void @@ -504,10 +518,95 @@ item_destroy(struct menuitem *item) free(item); } -static bool parse_buf(struct server *server, struct menu *menu, struct buf *buf); +/* + * We support XML CDATA for in menu.xml in order to provide backward + * compatibility with obmenu-generator. For example: + * + * + * + * + * + * + * + * + * + * is an old, deprecated openbox variety of . We support it + * for backward compatibility with old openbox-menu generators. It has the same + * function and + * + * The following nodenames support CDATA. + * - command.action.item.*menu.openbox_menu + * - execute.action.item.*menu.openbox_menu + * - command.action.item.openbox_pipe_menu + * - execute.action.item.openbox_pipe_menu + * - command.action.item.*menu.openbox_pipe_menu + * - execute.action.item.*menu.openbox_pipe_menu + * + * The *menu allows nested menus with nodenames such as ...menu.menu... or + * ...menu.menu.menu... and so on. We could use match_glob() for all of the + * above but it seems simpler to just check the first three fields. + */ +static bool +nodename_supports_cdata(char *nodename) +{ + return !strncmp("command.action.", nodename, 15) + || !strncmp("execute.action.", nodename, 15); +} + +static void +entry(xmlNode *node, char *nodename, char *content) +{ + if (!nodename) { + return; + } + xmlChar *cdata = NULL; + if (!content && nodename_supports_cdata(nodename)) { + cdata = xmlNodeGetContent(node); + } + if (!content && !cdata) { + return; + } + string_truncate_at_pattern(nodename, ".openbox_menu"); + string_truncate_at_pattern(nodename, ".openbox_pipe_menu"); + if (getenv("LABWC_DEBUG_MENU_NODENAMES")) { + printf("%s: %s\n", nodename, content ? content : (char *)cdata); + } + if (in_item) { + fill_item(nodename, content ? content : (char *)cdata); + } + xmlFree(cdata); +} + +static void +process_node(xmlNode *node) +{ + static char buffer[256]; + + char *content = (char *)node->content; + if (xmlIsBlankNode(node)) { + return; + } + char *name = nodename(node, buffer, sizeof(buffer)); + entry(node, name, content); +} + +static void xml_tree_walk(xmlNode *node, struct server *server); + +static void +traverse(xmlNode *n, struct server *server) +{ + xmlAttr *attr; + + process_node(n); + for (attr = n->properties; attr; attr = attr->next) { + xml_tree_walk(attr->children, server); + } + xml_tree_walk(n->children, server); +} + +static bool parse_buf(struct server *server, 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); /* * elements have three different roles: @@ -516,7 +615,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) +handle_menu_element(xmlNode *n, struct server *server) { char *label = (char *)xmlGetProp(n, (const xmlChar *)"label"); char *icon_name = (char *)xmlGetProp(n, (const xmlChar *)"icon"); @@ -531,9 +630,9 @@ 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(server, id, label); pipemenu->execute = xstrdup(execute); - if (!parent) { + if (!current_menu) { /* * A pipemenu may not have its parent like: * @@ -543,16 +642,18 @@ fill_menu(struct server *server, struct menu *parent, xmlNode *n) * */ } else { - struct menuitem *item = item_create(parent, label, - icon_name, /* arrow */ true); - item->submenu = pipemenu; + current_item = item_create(current_menu, label, + /* arrow */ true); + fill_item("icon", icon_name); + current_item_action = NULL; + current_item->submenu = pipemenu; } - } else if ((label && parent) || !parent) { + } else if ((label && current_menu) || !current_menu) { /* - * (label && parent) refers to + * (label && current_menu) refers to * which is an nested (inline) menu definition. * - * (!parent) catches: + * (!current_menu) catches: * * * @@ -568,20 +669,22 @@ 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 *parent_menu = current_menu; + current_menu = menu_create(server, id, label); if (icon_name) { - menu->icon_name = xstrdup(icon_name); + current_menu->icon_name = xstrdup(icon_name); } - if (label && parent) { + if (label && parent_menu) { /* * In a nested (inline) menu definition we need to * create an item pointing to the new submenu */ - struct menuitem *item = item_create(parent, label, - icon_name, true); - item->submenu = menu; + current_item = item_create(parent_menu, label, true); + fill_item("icon", icon_name); + current_item->submenu = current_menu; } - fill_menu_children(server, menu, n); + traverse(n, server); + current_menu = parent_menu; } else { /* * (when inside another element) creates an @@ -604,7 +707,7 @@ fill_menu(struct server *server, struct menu *parent, xmlNode *n) goto error; } - struct menu *iter = parent; + struct menu *iter = current_menu; while (iter) { if (iter == menu) { wlr_log(WLR_ERROR, "menus with the same id '%s' " @@ -614,55 +717,58 @@ fill_menu(struct server *server, struct menu *parent, xmlNode *n) iter = iter->parent; } - struct menuitem *item = item_create(parent, menu->label, - icon_name ? icon_name : menu->icon_name, true); - item->submenu = menu; + current_item = item_create(current_menu, menu->label, true); + fill_item("icon", menu->icon_name); + current_item->submenu = menu; } error: - xmlFree(label); - xmlFree(icon_name); - xmlFree(execute); - xmlFree(id); + free(label); + free(icon_name); + free(execute); + free(id); } /* This can be one of and */ static void -fill_separator(struct menu *menu, xmlNode *n) +handle_separator_element(xmlNode *n) { char *label = (char *)xmlGetProp(n, (const xmlChar *)"label"); - separator_create(menu, label); - xmlFree(label); + current_item = separator_create(current_menu, label); + free(label); } -/* parent==NULL when processing toplevel menus in menu.xml */ static void -fill_menu_children(struct server *server, struct menu *parent, xmlNode *n) +xml_tree_walk(xmlNode *node, struct server *server) { - xmlNode *child; - char *key, *content; - LAB_XML_FOR_EACH(n, child, key, content) { - if (!strcasecmp(key, "menu")) { - fill_menu(server, parent, child); - } else if (!strcasecmp(key, "separator")) { - if (!parent) { - wlr_log(WLR_ERROR, - "ignoring without parent "); - continue; - } - fill_separator(parent, child); - } else if (!strcasecmp(key, "item")) { - if (!parent) { + for (xmlNode *n = node; n && n->name; n = n->next) { + if (!strcasecmp((char *)n->name, "comment")) { + continue; + } + if (!strcasecmp((char *)n->name, "menu")) { + handle_menu_element(n, server); + continue; + } + if (!strcasecmp((char *)n->name, "separator")) { + handle_separator_element(n); + continue; + } + if (!strcasecmp((char *)n->name, "item")) { + if (!current_menu) { wlr_log(WLR_ERROR, "ignoring without parent "); continue; } - fill_item(parent, child); + in_item = true; + traverse(n, server); + in_item = false; + continue; } + traverse(n, server); } } static bool -parse_buf(struct server *server, struct menu *parent, struct buf *buf) +parse_buf(struct server *server, struct buf *buf) { int options = 0; xmlDoc *d = xmlReadMemory(buf->data, buf->len, NULL, NULL, options); @@ -670,15 +776,36 @@ parse_buf(struct server *server, struct menu *parent, struct buf *buf) wlr_log(WLR_ERROR, "xmlParseMemory()"); return false; } - - xmlNode *root = xmlDocGetRootElement(d); - fill_menu_children(server, parent, root); - + xml_tree_walk(xmlDocGetRootElement(d), server); xmlFreeDoc(d); xmlCleanupParser(); return true; } +/* + * @stream can come from either of the following: + * - fopen() in the case of reading a file such as menu.xml + * - popen() when processing pipemenus + */ +static void +parse_stream(struct server *server, FILE *stream) +{ + char *line = NULL; + size_t len = 0; + struct buf b = BUF_INIT; + + while (getline(&line, &len, stream) != -1) { + char *p = strrchr(line, '\n'); + if (p) { + *p = '\0'; + } + buf_add(&b, line); + } + free(line); + parse_buf(server, &b); + buf_reset(&b); +} + static void parse_xml(const char *filename, struct server *server) { @@ -691,13 +818,13 @@ parse_xml(const char *filename, struct server *server) for (struct wl_list *elm = iter(&paths); elm != &paths; elm = iter(elm)) { struct path *path = wl_container_of(elm, path, link); - struct buf buf = buf_from_file(path->string); - if (!buf.len) { + FILE *stream = fopen(path->string, "r"); + if (!stream) { continue; } wlr_log(WLR_INFO, "read menu file %s", path->string); - parse_buf(server, /*parent*/ NULL, &buf); - buf_reset(&buf); + parse_stream(server, stream); + fclose(stream); if (!should_merge_config) { break; } @@ -792,12 +919,11 @@ menu_hide_submenu(struct server *server, const char *id) } } -static struct action * -item_add_action(struct menuitem *item, const char *action_name) +static void +init_client_send_to_menu(struct server *server) { - struct action *action = action_create(action_name); - wl_list_append(&item->actions, &action->link); - return action; + /* Just create placeholder. Contents will be created when launched */ + menu_create(server, "client-send-to-menu", ""); } /* @@ -818,35 +944,29 @@ update_client_send_to_menu(struct server *server) struct workspace *workspace; - /* - * is true by default so - * 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) { - buf_add_fmt(&buf, ">%s<", workspace->name); + char *label = strdup_printf(">%s<", workspace->name); + current_item = item_create(menu, label, + /*show arrow*/ false); + free(label); } else { - buf_add(&buf, workspace->name); + current_item = item_create(menu, workspace->name, /*show arrow*/ false); } - struct menuitem *item = item_create(menu, buf.data, - NULL, /*show arrow*/ false); - - struct action *action = item_add_action(item, "SendToDesktop"); - action_arg_add_str(action, "to", workspace->name); - - buf_clear(&buf); + fill_item("name.action", "SendToDesktop"); + fill_item("to.action", workspace->name); } - buf_reset(&buf); - - separator_create(menu, ""); - struct menuitem *item = item_create(menu, - _("Always on Visible Workspace"), NULL, false); - item_add_action(item, "ToggleOmnipresent"); menu_create_scene(menu); } +static void +init_client_list_combined_menu(struct server *server) +{ + /* Just create placeholder. Contents will be created when launched */ + menu_create(server, "client-list-combined-menu", ""); +} + /* * This is client-list-combined-menu an internal menu similar to root-menu and * client-menu. @@ -863,7 +983,6 @@ update_client_list_combined_menu(struct server *server) reset_menu(menu); - struct menuitem *item; struct workspace *workspace; struct view *view; struct buf buffer = BUF_INIT; @@ -871,37 +990,32 @@ update_client_list_combined_menu(struct server *server) 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); + current_item = separator_create(menu, buffer.data); buf_clear(&buffer); wl_list_for_each(view, &server->views, link) { if (view->workspace == workspace) { - if (!view->foreign_toplevel - || string_null_or_empty(view->title)) { + const char *title = view_get_string_prop(view, "title"); + if (!view->foreign_toplevel || string_null_or_empty(title)) { continue; } if (view == server->active_view) { buf_add(&buffer, "*"); } - if (view->minimized) { - buf_add_fmt(&buffer, "(%s)", view->title); - } else { - buf_add(&buffer, view->title); - } - item = item_create(menu, buffer.data, NULL, - /*show arrow*/ false); - item->client_list_view = view; - item_add_action(item, "Focus"); - item_add_action(item, "Raise"); + buf_add(&buffer, title); + + current_item = item_create(menu, buffer.data, /*show arrow*/ false); + current_item->client_list_view = view; + fill_item("name.action", "Focus"); + fill_item("name.action", "Raise"); buf_clear(&buffer); menu->has_icons = true; } } - item = item_create(menu, _("Go there..."), NULL, - /*show arrow*/ false); - struct action *action = item_add_action(item, "GoToDesktop"); - action_arg_add_str(action, "to", workspace->name); + current_item = item_create(menu, _("Go there..."), /*show arrow*/ false); + fill_item("name.action", "GoToDesktop"); + fill_item("to.action", workspace->name); } buf_reset(&buffer); menu_create_scene(menu); @@ -911,22 +1025,22 @@ static void init_rootmenu(struct server *server) { struct menu *menu = menu_get_by_id(server, "root-menu"); - struct menuitem *item; /* Default menu if no menu.xml found */ if (!menu) { - menu = menu_create(server, NULL, "root-menu", ""); + current_menu = NULL; + menu = menu_create(server, "root-menu", ""); - item = item_create(menu, _("Terminal"), NULL, false); - struct action *action = item_add_action(item, "Execute"); - action_arg_add_str(action, "command", "lab-sensible-terminal"); + current_item = item_create(menu, _("Terminal"), false); + fill_item("name.action", "Execute"); + fill_item("command.action", "lab-sensible-terminal"); - separator_create(menu, NULL); + current_item = separator_create(menu, NULL); - item = item_create(menu, _("Reconfigure"), NULL, false); - item_add_action(item, "Reconfigure"); - item = item_create(menu, _("Exit"), NULL, false); - item_add_action(item, "Exit"); + current_item = item_create(menu, _("Reconfigure"), false); + fill_item("name.action", "Reconfigure"); + current_item = item_create(menu, _("Exit"), false); + fill_item("name.action", "Exit"); } } @@ -934,30 +1048,46 @@ static void init_windowmenu(struct server *server) { struct menu *menu = menu_get_by_id(server, "client-menu"); - struct menuitem *item; /* Default menu if no menu.xml found */ if (!menu) { - menu = menu_create(server, NULL, "client-menu", ""); - item = item_create(menu, _("Minimize"), NULL, false); - item_add_action(item, "Iconify"); - item = item_create(menu, _("Maximize"), NULL, false); - item_add_action(item, "ToggleMaximize"); - item = item_create(menu, _("Fullscreen"), NULL, false); - item_add_action(item, "ToggleFullscreen"); - item = item_create(menu, _("Roll Up/Down"), NULL, false); - item_add_action(item, "ToggleShade"); - item = item_create(menu, _("Decorations"), NULL, false); - item_add_action(item, "ToggleDecorations"); - item = item_create(menu, _("Always on Top"), NULL, false); - item_add_action(item, "ToggleAlwaysOnTop"); + current_menu = NULL; + menu = menu_create(server, "client-menu", ""); + current_item = item_create(menu, _("Minimize"), false); + fill_item("name.action", "Iconify"); + current_item = item_create(menu, _("Maximize"), false); + fill_item("name.action", "ToggleMaximize"); + current_item = item_create(menu, _("Fullscreen"), false); + fill_item("name.action", "ToggleFullscreen"); + current_item = item_create(menu, _("Roll Up/Down"), false); + fill_item("name.action", "ToggleShade"); + current_item = item_create(menu, _("Decorations"), false); + fill_item("name.action", "ToggleDecorations"); + current_item = item_create(menu, _("Always on Top"), false); + fill_item("name.action", "ToggleAlwaysOnTop"); /* Workspace sub-menu */ - item = item_create(menu, _("Workspace"), NULL, true); - item->submenu = menu_get_by_id(server, "client-send-to-menu"); + struct menu *workspace_menu = menu_create(server, "workspaces", ""); + current_item = item_create(workspace_menu, _("Move Left"), false); + /* + * is true by default so + * GoToDesktop will be called as part of the action. + */ + fill_item("name.action", "SendToDesktop"); + fill_item("to.action", "left"); + current_item = item_create(workspace_menu, _("Move Right"), false); + fill_item("name.action", "SendToDesktop"); + fill_item("to.action", "right"); + current_item = separator_create(workspace_menu, ""); + current_item = item_create(workspace_menu, + _("Always on Visible Workspace"), false); + fill_item("name.action", "ToggleOmnipresent"); - item = item_create(menu, _("Close"), NULL, false); - item_add_action(item, "Close"); + current_item = item_create(menu, _("Workspace"), true); + current_item->submenu = workspace_menu; + + current_item = item_create(menu, _("Close"), false); + fill_item("name.action", "Close"); } if (wl_list_length(&rc.workspace_config.workspaces) == 1) { @@ -969,14 +1099,11 @@ void menu_init(struct server *server) { 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")); - parse_xml("menu.xml", server); init_rootmenu(server); init_windowmenu(server); + init_client_list_combined_menu(server); + init_client_send_to_menu(server); validate(server); } @@ -1000,10 +1127,6 @@ nullify_item_pointing_to_this_menu(struct menu *menu) if (iter->parent == menu) { iter->parent = NULL; } - - if (iter->selection.menu == menu) { - iter->selection.menu = NULL; - } } } @@ -1051,6 +1174,11 @@ menu_finish(struct server *server) wl_list_for_each_safe(menu, tmp_menu, &server->menus, link) { menu_free(menu); } + + /* Reset state vars for starting fresh when Reload is triggered */ + current_item = NULL; + current_item_action = NULL; + current_menu = NULL; } void @@ -1206,14 +1334,19 @@ static void create_pipe_menu(struct menu_pipe_context *ctx) { struct server *server = ctx->pipemenu->server; - if (!parse_buf(server, ctx->pipemenu, &ctx->buf)) { - return; + struct menu *old_current_menu = current_menu; + current_menu = ctx->pipemenu; + if (!parse_buf(server, &ctx->buf)) { + goto restore_menus; } /* TODO: apply validate() only for generated pipemenus */ validate(server); /* Finally open the new submenu tree */ open_menu(ctx->pipemenu, ctx->anchor_rect); + +restore_menus: + current_menu = old_current_menu; } static void @@ -1521,6 +1654,15 @@ menu_process_cursor_motion(struct wlr_scene_node *node) menu_process_item_selection(item); } +bool +menu_call_actions(struct wlr_scene_node *node) +{ + assert(node && node->data); + struct menuitem *item = node_menuitem_from_node(node); + + return menu_execute_item(item); +} + void menu_close_root(struct server *server) { diff --git a/src/meson.build b/src/meson.build index 330b5daf..40cf0f69 100644 --- a/src/meson.build +++ b/src/meson.build @@ -11,6 +11,8 @@ labwc_sources = files( 'magnifier.c', 'main.c', 'node.c', + 'osd.c', + 'osd-field.c', 'output.c', 'output-state.c', 'output-virtual.c', @@ -47,14 +49,12 @@ if have_libsfdo ) endif +subdir('img') subdir('common') subdir('config') subdir('decorations') subdir('foreign-toplevel') -subdir('img') subdir('input') subdir('menu') -subdir('osd') -subdir('protocols') -subdir('scaled-buffer') subdir('ssd') +subdir('protocols') diff --git a/src/node.c b/src/node.c index ce4fb040..10d79a23 100644 --- a/src/node.c +++ b/src/node.c @@ -1,32 +1,33 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "node.h" #include #include -#include #include "common/mem.h" -#include "ssd.h" +#include "node.h" + +static void +descriptor_destroy(struct node_descriptor *node_descriptor) +{ + if (!node_descriptor) { + return; + } + wl_list_remove(&node_descriptor->destroy.link); + free(node_descriptor); +} static void handle_node_destroy(struct wl_listener *listener, void *data) { struct node_descriptor *node_descriptor = wl_container_of(listener, node_descriptor, destroy); - - if (node_type_contains(LAB_NODE_BUTTON, node_descriptor->type)) { - ssd_button_free(node_descriptor->data); - } - - wl_list_remove(&node_descriptor->destroy.link); - free(node_descriptor); + descriptor_destroy(node_descriptor); } void node_descriptor_create(struct wlr_scene_node *scene_node, - enum lab_node_type type, struct view *view, void *data) + enum node_descriptor_type type, void *data) { struct node_descriptor *node_descriptor = znew(*node_descriptor); node_descriptor->type = type; - node_descriptor->view = view; node_descriptor->data = data; node_descriptor->destroy.notify = handle_node_destroy; wl_signal_add(&scene_node->events.destroy, &node_descriptor->destroy); @@ -38,7 +39,9 @@ node_view_from_node(struct wlr_scene_node *wlr_scene_node) { assert(wlr_scene_node->data); struct node_descriptor *node_descriptor = wlr_scene_node->data; - return node_descriptor->view; + assert(node_descriptor->type == LAB_NODE_DESC_VIEW + || node_descriptor->type == LAB_NODE_DESC_XDG_POPUP); + return (struct view *)node_descriptor->data; } struct lab_layer_surface * @@ -46,28 +49,42 @@ node_layer_surface_from_node(struct wlr_scene_node *wlr_scene_node) { assert(wlr_scene_node->data); struct node_descriptor *node_descriptor = wlr_scene_node->data; - assert(node_descriptor->type == LAB_NODE_LAYER_SURFACE); + assert(node_descriptor->type == LAB_NODE_DESC_LAYER_SURFACE); return (struct lab_layer_surface *)node_descriptor->data; } +struct lab_layer_popup * +node_layer_popup_from_node(struct wlr_scene_node *wlr_scene_node) +{ + assert(wlr_scene_node->data); + struct node_descriptor *node_descriptor = wlr_scene_node->data; + assert(node_descriptor->type == LAB_NODE_DESC_LAYER_POPUP); + return (struct lab_layer_popup *)node_descriptor->data; +} + struct menuitem * node_menuitem_from_node(struct wlr_scene_node *wlr_scene_node) { assert(wlr_scene_node->data); struct node_descriptor *node_descriptor = wlr_scene_node->data; - assert(node_descriptor->type == LAB_NODE_MENUITEM); + assert(node_descriptor->type == LAB_NODE_DESC_MENUITEM); return (struct menuitem *)node_descriptor->data; } struct ssd_button * -node_try_ssd_button_from_node(struct wlr_scene_node *wlr_scene_node) +node_ssd_button_from_node(struct wlr_scene_node *wlr_scene_node) { assert(wlr_scene_node->data); struct node_descriptor *node_descriptor = wlr_scene_node->data; - - if (node_type_contains(LAB_NODE_BUTTON, node_descriptor->type)) { - return (struct ssd_button *)node_descriptor->data; - } - - return NULL; + assert(node_descriptor->type == LAB_NODE_DESC_SSD_BUTTON); + return (struct ssd_button *)node_descriptor->data; +} + +struct scaled_scene_buffer * +node_scaled_scene_buffer_from_node(struct wlr_scene_node *wlr_scene_node) +{ + assert(wlr_scene_node->data); + struct node_descriptor *node_descriptor = wlr_scene_node->data; + assert(node_descriptor->type == LAB_NODE_DESC_SCALED_SCENE_BUFFER); + return (struct scaled_scene_buffer *)node_descriptor->data; } diff --git a/src/osd/osd-field.c b/src/osd-field.c similarity index 89% rename from src/osd/osd-field.c rename to src/osd-field.c index 01c4e48b..9eec0837 100644 --- a/src/osd/osd-field.c +++ b/src/osd-field.c @@ -2,7 +2,6 @@ #include #include #include -#include "common/buf.h" #include "common/mem.h" #include "config/rcxml.h" #include "view.h" @@ -10,7 +9,6 @@ #include "labwc.h" #include "desktop-entry.h" #include "osd.h" -#include "output.h" /* includes '%', terminating 's' and NULL byte, 8 is enough for %-9999s */ #define LAB_FIELD_SINGLE_FMT_MAX_LEN 8 @@ -23,16 +21,22 @@ struct field_converter { field_conversion_type *fn; }; +static const struct field_converter field_converter[]; + /* Internal helpers */ static const char * -get_identifier(struct view *view, bool trim) +get_app_id_or_class(struct view *view, bool trim) { - const char *identifier = view->app_id; + /* + * XWayland clients return WM_CLASS for 'app_id' so we don't need a + * special case for that here. + */ + const char *identifier = view_get_string_prop(view, "app_id"); /* remove the first two nodes of 'org.' strings */ if (trim && !strncmp(identifier, "org.", 4)) { - const char *p = identifier + 4; + char *p = (char *)identifier + 4; p = strchr(p, '.'); if (p) { return ++p; @@ -45,13 +49,14 @@ 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 *app_id = view_get_string_prop(view, "app_id"); + const char *name = desktop_entry_name_lookup(view->server, app_id); if (name) { return name; } #endif - return get_identifier(view, /* trim */ true); + return get_app_id_or_class(view, /* trim */ true); } static const char * @@ -68,12 +73,21 @@ get_type(struct view *view, bool short_form) return "???"; } +static const char * +get_title(struct view *view) +{ + return view_get_string_prop(view, "title"); +} + static const char * get_title_if_different(struct view *view) { - const char *identifier = get_identifier(view, /*trim*/ false); - const char *title = view->title; - return !strcmp(identifier, title) ? NULL : title; + const char *identifier = get_app_id_or_class(view, /*trim*/ false); + const char *title = get_title(view); + if (!identifier) { + return title; + } + return (!title || !strcmp(identifier, title)) ? NULL : title; } /* Field handlers */ @@ -111,12 +125,10 @@ static void field_set_win_state(struct buf *buf, struct view *view, const char *format) { /* custom type conversion-specifier: s */ - if (view->minimized) { - buf_add(buf, "m"); - } else if (view->shaded) { - buf_add(buf, "s"); - } else if (view->maximized) { + if (view->maximized) { buf_add(buf, "M"); + } else if (view->minimized) { + buf_add(buf, "m"); } else if (view->fullscreen) { buf_add(buf, "F"); } else { @@ -129,7 +141,6 @@ field_set_win_state_all(struct buf *buf, struct view *view, const char *format) { /* custom type conversion-specifier: S */ buf_add(buf, view->minimized ? "m" : " "); - buf_add(buf, view->shaded ? "s" : " "); buf_add(buf, view->maximized ? "M" : " "); buf_add(buf, view->fullscreen ? "F" : " "); /* TODO: add always-on-top and omnipresent ? */ @@ -158,14 +169,14 @@ static void field_set_identifier(struct buf *buf, struct view *view, const char *format) { /* custom type conversion-specifier: I */ - buf_add(buf, get_identifier(view, /*trim*/ false)); + buf_add(buf, get_app_id_or_class(view, /*trim*/ false)); } static void field_set_identifier_trimmed(struct buf *buf, struct view *view, const char *format) { /* custom type conversion-specifier: i */ - buf_add(buf, get_identifier(view, /*trim*/ true)); + buf_add(buf, get_app_id_or_class(view, /*trim*/ true)); } static void @@ -179,7 +190,7 @@ static void field_set_title(struct buf *buf, struct view *view, const char *format) { /* custom type conversion-specifier: T */ - buf_add(buf, view->title); + buf_add(buf, get_title(view)); } static void @@ -189,27 +200,6 @@ field_set_title_short(struct buf *buf, struct view *view, const char *format) buf_add(buf, get_title_if_different(view)); } -static void field_set_custom(struct buf *buf, struct view *view, - const char *format); - -static const struct field_converter field_converter[LAB_FIELD_COUNT] = { - [LAB_FIELD_TYPE] = { 'B', field_set_type }, - [LAB_FIELD_TYPE_SHORT] = { 'b', field_set_type_short }, - [LAB_FIELD_WIN_STATE_ALL] = { 'S', field_set_win_state_all }, - [LAB_FIELD_WIN_STATE] = { 's', field_set_win_state }, - [LAB_FIELD_IDENTIFIER] = { 'I', field_set_identifier }, - [LAB_FIELD_TRIMMED_IDENTIFIER] = { 'i', field_set_identifier_trimmed }, - [LAB_FIELD_DESKTOP_ENTRY_NAME] = { 'n', field_set_desktop_entry_name}, - [LAB_FIELD_WORKSPACE] = { 'W', field_set_workspace }, - [LAB_FIELD_WORKSPACE_SHORT] = { 'w', field_set_workspace_short }, - [LAB_FIELD_OUTPUT] = { 'O', field_set_output }, - [LAB_FIELD_OUTPUT_SHORT] = { 'o', field_set_output_short }, - [LAB_FIELD_TITLE] = { 'T', field_set_title }, - [LAB_FIELD_TITLE_SHORT] = { 't', field_set_title_short }, - /* fmt_char can never be matched so prevents LAB_FIELD_CUSTOM recursion */ - [LAB_FIELD_CUSTOM] = { '\0', field_set_custom }, -}; - static void field_set_custom(struct buf *buf, struct view *view, const char *format) { @@ -288,6 +278,31 @@ reset_format: buf_reset(&field_result); } +static const struct field_converter field_converter[LAB_FIELD_COUNT] = { + [LAB_FIELD_TYPE] = { 'B', field_set_type }, + [LAB_FIELD_TYPE_SHORT] = { 'b', field_set_type_short }, + [LAB_FIELD_WIN_STATE_ALL] = { 'S', field_set_win_state_all }, + [LAB_FIELD_WIN_STATE] = { 's', field_set_win_state }, + [LAB_FIELD_IDENTIFIER] = { 'I', field_set_identifier }, + [LAB_FIELD_TRIMMED_IDENTIFIER] = { 'i', field_set_identifier_trimmed }, + [LAB_FIELD_DESKTOP_ENTRY_NAME] = { 'n', field_set_desktop_entry_name}, + [LAB_FIELD_WORKSPACE] = { 'W', field_set_workspace }, + [LAB_FIELD_WORKSPACE_SHORT] = { 'w', field_set_workspace_short }, + [LAB_FIELD_OUTPUT] = { 'O', field_set_output }, + [LAB_FIELD_OUTPUT_SHORT] = { 'o', field_set_output_short }, + [LAB_FIELD_TITLE] = { 'T', field_set_title }, + [LAB_FIELD_TITLE_SHORT] = { 't', field_set_title_short }, + /* fmt_char can never be matched so prevents LAB_FIELD_CUSTOM recursion */ + [LAB_FIELD_CUSTOM] = { '\0', field_set_custom }, +}; + +struct window_switcher_field * +osd_field_create(void) +{ + struct window_switcher_field *field = znew(*field); + return field; +} + void osd_field_arg_from_xml_node(struct window_switcher_field *field, const char *nodename, const char *content) diff --git a/src/osd/osd.c b/src/osd.c similarity index 54% rename from src/osd/osd.c rename to src/osd.c index e436c19e..9213a1b3 100644 --- a/src/osd/osd.c +++ b/src/osd.c @@ -1,21 +1,30 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "osd.h" #include -#include -#include +#include #include +#include #include "common/array.h" +#include "common/buf.h" +#include "common/font.h" #include "common/lab-scene-rect.h" +#include "common/macros.h" +#include "common/scaled-font-buffer.h" +#include "common/scaled-icon-buffer.h" #include "common/scene-helpers.h" +#include "common/string-helpers.h" #include "config/rcxml.h" #include "labwc.h" #include "node.h" -#include "output.h" -#include "scaled-buffer/scaled-font-buffer.h" -#include "scaled-buffer/scaled-icon-buffer.h" -#include "ssd.h" +#include "osd.h" #include "theme.h" #include "view.h" +#include "window-rules.h" +#include "workspaces.h" + +struct osd_scene_item { + struct view *view; + struct wlr_scene_node *highlight_outline; +}; static void update_osd(struct server *server); @@ -24,10 +33,9 @@ destroy_osd_scenes(struct server *server) { struct output *output; wl_list_for_each(output, &server->outputs, link) { - if (output->osd_scene.tree) { - wlr_scene_node_destroy(&output->osd_scene.tree->node); - output->osd_scene.tree = NULL; - } + wlr_scene_node_destroy(&output->osd_scene.tree->node); + output->osd_scene.tree = NULL; + wl_array_release(&output->osd_scene.items); wl_array_init(&output->osd_scene.items); } @@ -161,14 +169,9 @@ restore_preview_node(struct server *server) if (!osd_state->preview_was_enabled) { wlr_scene_node_set_enabled(osd_state->preview_node, false); } - if (osd_state->preview_was_shaded) { - struct view *view = node_view_from_node(osd_state->preview_node); - view_set_shade(view, true); - } osd_state->preview_node = NULL; osd_state->preview_parent = NULL; osd_state->preview_anchor = NULL; - osd_state->preview_was_shaded = false; } } @@ -209,7 +212,6 @@ osd_finish(struct server *server) server->osd_state.preview_node = NULL; server->osd_state.preview_anchor = NULL; server->osd_state.cycle_view = NULL; - server->osd_state.preview_was_shaded = false; destroy_osd_scenes(server); @@ -251,10 +253,6 @@ preview_cycled_view(struct view *view) if (!osd_state->preview_was_enabled) { wlr_scene_node_set_enabled(osd_state->preview_node, true); } - if (rc.window_switcher.unshade && view->shaded) { - view_set_shade(view, false); - osd_state->preview_was_shaded = true; - } /* * FIXME: This abuses an implementation detail of the always-on-top tree. @@ -268,6 +266,190 @@ preview_cycled_view(struct view *view) wlr_scene_node_raise_to_top(osd_state->preview_node); } +static void +create_osd_scene(struct output *output, struct wl_array *views) +{ + struct server *server = output->server; + struct theme *theme = server->theme; + bool show_workspace = wl_list_length(&rc.workspace_config.workspaces) > 1; + const char *workspace_name = server->workspaces.current->name; + + int w = theme->osd_window_switcher_width; + if (theme->osd_window_switcher_width_is_percent) { + w = output->wlr_output->width + * theme->osd_window_switcher_width / 100; + } + int h = wl_array_len(views) * rc.theme->osd_window_switcher_item_height + + 2 * rc.theme->osd_border_width + + 2 * rc.theme->osd_window_switcher_padding; + if (show_workspace) { + /* workspace indicator */ + h += theme->osd_window_switcher_item_height; + } + + output->osd_scene.tree = wlr_scene_tree_create(output->osd_tree); + + float *text_color = theme->osd_label_text_color; + float *bg_color = theme->osd_bg_color; + + /* Draw background */ + struct lab_scene_rect_options bg_opts = { + .border_colors = (float *[1]) {theme->osd_border_color}, + .nr_borders = 1, + .border_width = theme->osd_border_width, + .bg_color = bg_color, + .width = w, + .height = h, + }; + lab_scene_rect_create(output->osd_scene.tree, &bg_opts); + + int y = theme->osd_border_width + theme->osd_window_switcher_padding; + + /* Draw workspace indicator */ + if (show_workspace) { + struct font font = rc.font_osd; + font.weight = PANGO_WEIGHT_BOLD; + + /* Center workspace indicator on the x axis */ + int x = (w - font_width(&font, workspace_name)) / 2; + if (x < 0) { + wlr_log(WLR_ERROR, + "not enough space for workspace name in osd"); + goto error; + } + + struct scaled_font_buffer *font_buffer = + scaled_font_buffer_create(output->osd_scene.tree); + wlr_scene_node_set_position(&font_buffer->scene_buffer->node, + x, y + (theme->osd_window_switcher_item_height + - font_height(&font)) / 2); + scaled_font_buffer_update(font_buffer, workspace_name, 0, + &font, text_color, bg_color); + y += theme->osd_window_switcher_item_height; + } + + struct buf buf = BUF_INIT; + int nr_fields = wl_list_length(&rc.window_switcher.fields); + + /* This is the width of the area available for text fields */ + int field_widths_sum = w - 2 * theme->osd_border_width + - 2 * theme->osd_window_switcher_padding + - 2 * theme->osd_window_switcher_item_active_border_width + - (nr_fields + 1) * theme->osd_window_switcher_item_padding_x; + if (field_widths_sum <= 0) { + wlr_log(WLR_ERROR, "Not enough spaces for osd contents"); + goto error; + } + + /* Draw text for each node */ + struct view **view; + wl_array_for_each(view, views) { + struct osd_scene_item *item = + wl_array_add(&output->osd_scene.items, sizeof(*item)); + item->view = *view; + /* + * OSD border + * +---------------------------------+ + * | | + * | item border | + * |+-------------------------------+| + * || || + * ||padding between each field || + * ||| field-1 | field-2 | field-n ||| + * || || + * || || + * |+-------------------------------+| + * | | + * | | + * +---------------------------------+ + */ + int x = theme->osd_border_width + + theme->osd_window_switcher_padding + + theme->osd_window_switcher_item_active_border_width + + theme->osd_window_switcher_item_padding_x; + struct wlr_scene_tree *item_root = + wlr_scene_tree_create(output->osd_scene.tree); + + struct window_switcher_field *field; + wl_list_for_each(field, &rc.window_switcher.fields, link) { + int field_width = field_widths_sum * field->width / 100.0; + struct wlr_scene_node *node = NULL; + int height = -1; + + if (field->content == LAB_FIELD_ICON) { + int icon_size = MIN(field_width, + theme->osd_window_switcher_item_icon_size); + struct scaled_icon_buffer *icon_buffer = + scaled_icon_buffer_create(item_root, + server, icon_size, icon_size); + scaled_icon_buffer_set_view(icon_buffer, *view); + node = &icon_buffer->scene_buffer->node; + height = icon_size; + } else { + buf_clear(&buf); + osd_field_get_content(field, &buf, *view); + + if (!string_null_or_empty(buf.data)) { + struct scaled_font_buffer *font_buffer = + scaled_font_buffer_create(item_root); + scaled_font_buffer_update(font_buffer, + buf.data, field_width, + &rc.font_osd, text_color, bg_color); + node = &font_buffer->scene_buffer->node; + height = font_height(&rc.font_osd); + } + } + + if (node) { + int item_height = + theme->osd_window_switcher_item_height; + wlr_scene_node_set_position(node, + x, y + (item_height - height) / 2); + } + x += field_width + theme->osd_window_switcher_item_padding_x; + } + + /* Highlight around selected window's item */ + int highlight_x = theme->osd_border_width + + theme->osd_window_switcher_padding; + struct lab_scene_rect_options highlight_opts = { + .border_colors = (float *[1]) {text_color}, + .nr_borders = 1, + .border_width = + theme->osd_window_switcher_item_active_border_width, + .width = w - 2 * theme->osd_border_width + - 2 * theme->osd_window_switcher_padding, + .height = theme->osd_window_switcher_item_height, + }; + + struct lab_scene_rect *highlight_rect = lab_scene_rect_create( + output->osd_scene.tree, &highlight_opts); + item->highlight_outline = &highlight_rect->tree->node; + wlr_scene_node_set_position(item->highlight_outline, highlight_x, y); + wlr_scene_node_set_enabled(item->highlight_outline, false); + + y += theme->osd_window_switcher_item_height; + } + buf_reset(&buf); + +error:; + /* Center OSD */ + struct wlr_box usable = output_usable_area_in_layout_coords(output); + wlr_scene_node_set_position(&output->osd_scene.tree->node, + usable.x + usable.width / 2 - w / 2, + usable.y + usable.height / 2 - h / 2); +} + +static void +update_item_highlight(struct output *output) +{ + struct osd_scene_item *item; + wl_array_for_each(item, &output->osd_scene.items) { + wlr_scene_node_set_enabled(item->highlight_outline, + item->view == output->server->osd_state.cycle_view); + } +} + static void update_osd(struct server *server) { @@ -275,22 +457,12 @@ update_osd(struct server *server) wl_array_init(&views); view_array_append(server, &views, rc.window_switcher.criteria); - struct osd_impl *osd_impl = NULL; - switch (rc.window_switcher.style) { - case WINDOW_SWITCHER_CLASSIC: - osd_impl = &osd_classic_impl; - break; - case WINDOW_SWITCHER_THUMBNAIL: - osd_impl = &osd_thumbnail_impl; - break; - } - if (!wl_array_len(&views) || !server->osd_state.cycle_view) { osd_finish(server); goto out; } - if (rc.window_switcher.show) { + if (rc.window_switcher.show && rc.theme->osd_window_switcher_width > 0) { /* Display the actual OSD */ struct output *output; wl_list_for_each(output, &server->outputs, link) { @@ -298,17 +470,13 @@ update_osd(struct server *server) continue; } if (!output->osd_scene.tree) { - osd_impl->create(output, &views); + create_osd_scene(output, &views); assert(output->osd_scene.tree); } - osd_impl->update(output); + update_item_highlight(output); } } - if (rc.window_switcher.preview) { - preview_cycled_view(server->osd_state.cycle_view); - } - /* Outline current window */ if (rc.window_switcher.outlines) { if (view_is_focusable(server->osd_state.cycle_view)) { @@ -316,6 +484,9 @@ update_osd(struct server *server) } } + if (rc.window_switcher.preview) { + preview_cycled_view(server->osd_state.cycle_view); + } out: wl_array_release(&views); } diff --git a/src/osd/meson.build b/src/osd/meson.build deleted file mode 100644 index 17b4bf51..00000000 --- a/src/osd/meson.build +++ /dev/null @@ -1,6 +0,0 @@ -labwc_sources += files( - 'osd.c', - 'osd-classic.c', - 'osd-field.c', - 'osd-thumbnail.c', -) diff --git a/src/osd/osd-classic.c b/src/osd/osd-classic.c deleted file mode 100644 index 7af72823..00000000 --- a/src/osd/osd-classic.c +++ /dev/null @@ -1,237 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include -#include -#include -#include -#include "common/array.h" -#include "common/buf.h" -#include "common/font.h" -#include "common/lab-scene-rect.h" -#include "common/string-helpers.h" -#include "config/rcxml.h" -#include "labwc.h" -#include "osd.h" -#include "output.h" -#include "scaled-buffer/scaled-font-buffer.h" -#include "scaled-buffer/scaled-icon-buffer.h" -#include "theme.h" -#include "workspaces.h" - -struct osd_classic_scene_item { - struct view *view; - struct wlr_scene_tree *normal_tree, *active_tree; -}; - -static void -create_fields_scene(struct server *server, 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 window_switcher_classic_theme *switcher_theme = - &theme->osd_window_switcher_classic; - - struct window_switcher_field *field; - wl_list_for_each(field, &rc.window_switcher.fields, link) { - int field_width = field_widths_sum * field->width / 100.0; - struct wlr_scene_node *node = NULL; - int height = -1; - - if (field->content == LAB_FIELD_ICON) { - 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_set_view(icon_buffer, view); - node = &icon_buffer->scene_buffer->node; - height = icon_size; - } else { - struct buf buf = BUF_INIT; - osd_field_get_content(field, &buf, view); - - if (!string_null_or_empty(buf.data)) { - struct scaled_font_buffer *font_buffer = - scaled_font_buffer_create(parent); - scaled_font_buffer_update(font_buffer, - buf.data, field_width, - &rc.font_osd, text_color, bg_color); - node = &font_buffer->scene_buffer->node; - height = font_height(&rc.font_osd); - } - - buf_reset(&buf); - } - - if (node) { - int item_height = switcher_theme->item_height; - wlr_scene_node_set_position(node, - x, y + (item_height - height) / 2); - } - x += field_width + switcher_theme->item_padding_x; - } -} - -static void -osd_classic_create(struct output *output, struct wl_array *views) -{ - assert(!output->osd_scene.tree); - - struct server *server = output->server; - struct theme *theme = server->theme; - struct window_switcher_classic_theme *switcher_theme = - &theme->osd_window_switcher_classic; - bool show_workspace = wl_list_length(&rc.workspace_config.workspaces) > 1; - const char *workspace_name = server->workspaces.current->name; - - int output_width, output_height; - wlr_output_effective_resolution(output->wlr_output, - &output_width, &output_height); - - int w = switcher_theme->width; - if (switcher_theme->width_is_percent) { - w = output_width * switcher_theme->width / 100; - } - int h = wl_array_len(views) * switcher_theme->item_height - + 2 * rc.theme->osd_border_width + 2 * switcher_theme->padding; - if (show_workspace) { - /* workspace indicator */ - h += switcher_theme->item_height; - } - - output->osd_scene.tree = wlr_scene_tree_create(output->osd_tree); - - float *text_color = theme->osd_label_text_color; - float *bg_color = theme->osd_bg_color; - - /* Draw background */ - struct lab_scene_rect_options bg_opts = { - .border_colors = (float *[1]) {theme->osd_border_color}, - .nr_borders = 1, - .border_width = theme->osd_border_width, - .bg_color = bg_color, - .width = w, - .height = h, - }; - lab_scene_rect_create(output->osd_scene.tree, &bg_opts); - - int y = theme->osd_border_width + switcher_theme->padding; - - /* Draw workspace indicator */ - if (show_workspace) { - struct font font = rc.font_osd; - font.weight = PANGO_WEIGHT_BOLD; - - /* Center workspace indicator on the x axis */ - int x = (w - font_width(&font, workspace_name)) / 2; - if (x < 0) { - wlr_log(WLR_ERROR, - "not enough space for workspace name in osd"); - goto error; - } - - struct scaled_font_buffer *font_buffer = - scaled_font_buffer_create(output->osd_scene.tree); - wlr_scene_node_set_position(&font_buffer->scene_buffer->node, - x, y + (switcher_theme->item_height - font_height(&font)) / 2); - scaled_font_buffer_update(font_buffer, workspace_name, 0, - &font, text_color, bg_color); - y += switcher_theme->item_height; - } - - struct buf buf = BUF_INIT; - int nr_fields = wl_list_length(&rc.window_switcher.fields); - - /* This is the width of the area available for text fields */ - int field_widths_sum = w - 2 * theme->osd_border_width - - 2 * switcher_theme->padding - - 2 * switcher_theme->item_active_border_width - - (nr_fields + 1) * switcher_theme->item_padding_x; - if (field_widths_sum <= 0) { - wlr_log(WLR_ERROR, "Not enough spaces for osd contents"); - goto error; - } - - /* Draw text for each node */ - struct view **view; - wl_array_for_each(view, views) { - struct osd_classic_scene_item *item = - wl_array_add(&output->osd_scene.items, sizeof(*item)); - item->view = *view; - /* - * OSD border - * +---------------------------------+ - * | | - * | item border | - * |+-------------------------------+| - * || || - * ||padding between each field || - * ||| field-1 | field-2 | field-n ||| - * || || - * || || - * |+-------------------------------+| - * | | - * | | - * +---------------------------------+ - */ - int x = theme->osd_border_width - + switcher_theme->padding - + switcher_theme->item_active_border_width - + switcher_theme->item_padding_x; - struct wlr_scene_tree *item_root = - wlr_scene_tree_create(output->osd_scene.tree); - item->normal_tree = wlr_scene_tree_create(item_root); - item->active_tree = wlr_scene_tree_create(item_root); - wlr_scene_node_set_enabled(&item->active_tree->node, false); - - float *active_bg_color = switcher_theme->item_active_bg_color; - float *active_border_color = switcher_theme->item_active_border_color; - - /* Highlight around selected window's item */ - int highlight_x = theme->osd_border_width - + switcher_theme->padding; - struct lab_scene_rect_options highlight_opts = { - .border_colors = (float *[1]) {active_border_color}, - .bg_color = active_bg_color, - .nr_borders = 1, - .border_width = switcher_theme->item_active_border_width, - .width = w - 2 * theme->osd_border_width - - 2 * switcher_theme->padding, - .height = switcher_theme->item_height, - }; - struct lab_scene_rect *highlight_rect = lab_scene_rect_create( - item->active_tree, &highlight_opts); - wlr_scene_node_set_position(&highlight_rect->tree->node, highlight_x, y); - - create_fields_scene(server, *view, item->normal_tree, - text_color, bg_color, field_widths_sum, x, y); - create_fields_scene(server, *view, item->active_tree, - text_color, active_bg_color, field_widths_sum, x, y); - - y += switcher_theme->item_height; - } - buf_reset(&buf); - -error:; - /* Center OSD */ - struct wlr_box usable = output_usable_area_in_layout_coords(output); - wlr_scene_node_set_position(&output->osd_scene.tree->node, - usable.x + usable.width / 2 - w / 2, - usable.y + usable.height / 2 - h / 2); -} - -static void -osd_classic_update(struct output *output) -{ - struct osd_classic_scene_item *item; - wl_array_for_each(item, &output->osd_scene.items) { - bool active = item->view == output->server->osd_state.cycle_view; - wlr_scene_node_set_enabled(&item->normal_tree->node, !active); - wlr_scene_node_set_enabled(&item->active_tree->node, active); - } -} - -struct osd_impl osd_classic_impl = { - .create = osd_classic_create, - .update = osd_classic_update, -}; diff --git a/src/osd/osd-thumbnail.c b/src/osd/osd-thumbnail.c deleted file mode 100644 index 1ba65a79..00000000 --- a/src/osd/osd-thumbnail.c +++ /dev/null @@ -1,278 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include -#include -#include -#include -#include "config/rcxml.h" -#include "common/array.h" -#include "common/box.h" -#include "common/lab-scene-rect.h" -#include "labwc.h" -#include "osd.h" -#include "output.h" -#include "scaled-buffer/scaled-font-buffer.h" -#include "scaled-buffer/scaled-icon-buffer.h" -#include "theme.h" -#include "view.h" - -struct osd_thumbnail_scene_item { - struct view *view; - struct wlr_scene_tree *tree; - struct scaled_font_buffer *normal_title; - struct scaled_font_buffer *active_title; - struct lab_scene_rect *active_bg; -}; - -static void -render_node(struct server *server, struct wlr_render_pass *pass, - struct wlr_scene_node *node, int x, int y) -{ - switch (node->type) { - case WLR_SCENE_NODE_TREE: { - 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); - } - break; - } - case WLR_SCENE_NODE_BUFFER: { - struct wlr_scene_buffer *scene_buffer = - wlr_scene_buffer_from_node(node); - if (!scene_buffer->buffer) { - break; - } - struct wlr_texture *texture = wlr_texture_from_buffer( - server->renderer, scene_buffer->buffer); - if (!texture) { - break; - } - wlr_render_pass_add_texture(pass, &(struct wlr_render_texture_options){ - .texture = texture, - .src_box = scene_buffer->src_box, - .dst_box = { - .x = x, - .y = y, - .width = scene_buffer->dst_width, - .height = scene_buffer->dst_height, - }, - .transform = scene_buffer->transform, - }); - wlr_texture_destroy(texture); - break; - } - case WLR_SCENE_NODE_RECT: - /* should be unreached */ - wlr_log(WLR_ERROR, "ignoring rect"); - break; - } -} - -static struct wlr_buffer * -render_thumb(struct output *output, struct view *view) -{ - struct server *server = output->server; - struct wlr_buffer *buffer = wlr_allocator_create_buffer(server->allocator, - view->current.width, view->current.height, - &output->wlr_output->swapchain->format); - struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass( - server->renderer, buffer, NULL); - render_node(server, 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); - return NULL; - } - return buffer; -} - -static struct scaled_font_buffer * -create_title(struct wlr_scene_tree *parent, - struct window_switcher_thumbnail_theme *switcher_theme, - const char *title, const float *title_color, - const float *bg_color, int y) -{ - struct scaled_font_buffer *buffer = - scaled_font_buffer_create(parent); - scaled_font_buffer_update(buffer, title, - switcher_theme->item_width - 2 * switcher_theme->item_padding, - &rc.font_osd, title_color, bg_color); - wlr_scene_node_set_position(&buffer->scene_buffer->node, - (switcher_theme->item_width - buffer->width) / 2, y); - return buffer; -} - -static struct osd_thumbnail_scene_item * -create_item_scene(struct wlr_scene_tree *parent, struct view *view, - struct output *output) -{ - struct server *server = output->server; - struct theme *theme = server->theme; - struct window_switcher_thumbnail_theme *switcher_theme = - &theme->osd_window_switcher_thumbnail; - int padding = theme->border_width + switcher_theme->item_padding; - int title_y = switcher_theme->item_height - padding - switcher_theme->title_height; - struct wlr_box thumb_bounds = { - .x = padding, - .y = padding, - .width = switcher_theme->item_width - 2 * padding, - .height = title_y - 2 * padding, - }; - if (thumb_bounds.width <= 0 || thumb_bounds.height <= 0) { - wlr_log(WLR_ERROR, "too small thumbnail area"); - return NULL; - } - - struct osd_thumbnail_scene_item *item = - wl_array_add(&output->osd_scene.items, sizeof(*item)); - item->tree = wlr_scene_tree_create(parent); - item->view = view; - - /* background for selected item */ - struct lab_scene_rect_options opts = { - .border_colors = (float *[1]) { switcher_theme->item_active_border_color }, - .nr_borders = 1, - .border_width = switcher_theme->item_active_border_width, - .bg_color = switcher_theme->item_active_bg_color, - .width = switcher_theme->item_width, - .height = switcher_theme->item_height, - }; - item->active_bg = lab_scene_rect_create(item->tree, &opts); - - /* thumbnail */ - struct wlr_buffer *thumb_buffer = render_thumb(output, view); - if (thumb_buffer) { - struct wlr_scene_buffer *thumb_scene_buffer = - wlr_scene_buffer_create(item->tree, thumb_buffer); - wlr_buffer_drop(thumb_buffer); - struct wlr_box thumb_box = box_fit_within( - thumb_buffer->width, thumb_buffer->height, - &thumb_bounds); - wlr_scene_buffer_set_dest_size(thumb_scene_buffer, - thumb_box.width, thumb_box.height); - wlr_scene_node_set_position(&thumb_scene_buffer->node, - thumb_box.x, thumb_box.y); - } - - /* title */ - item->normal_title = create_title(item->tree, switcher_theme, - view->title, theme->osd_label_text_color, - theme->osd_bg_color, title_y); - item->active_title = create_title(item->tree, switcher_theme, - view->title, theme->osd_label_text_color, - switcher_theme->item_active_bg_color, title_y); - - /* icon */ - int icon_size = switcher_theme->item_icon_size; - struct scaled_icon_buffer *icon_buffer = scaled_icon_buffer_create( - item->tree, server, 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 */ - wlr_scene_node_set_position(&icon_buffer->scene_buffer->node, x, y); - - return item; -} - -static void -get_items_geometry(struct output *output, struct theme *theme, - int nr_thumbs, int *nr_rows, int *nr_cols) -{ - struct window_switcher_thumbnail_theme *switcher_theme = - &theme->osd_window_switcher_thumbnail; - int output_width, output_height; - wlr_output_effective_resolution(output->wlr_output, - &output_width, &output_height); - int padding = theme->osd_border_width + switcher_theme->padding; - - int max_bg_width = switcher_theme->max_width; - if (switcher_theme->max_width_is_percent) { - max_bg_width = output_width * switcher_theme->max_width / 100; - } - - *nr_rows = 1; - *nr_cols = nr_thumbs; - while (1) { - assert(*nr_rows <= nr_thumbs); - int bg_width = *nr_cols * switcher_theme->item_width + 2 * padding; - if (bg_width < max_bg_width) { - break; - } - if (*nr_rows >= nr_thumbs) { - break; - } - (*nr_rows)++; - *nr_cols = ceilf((float)nr_thumbs / *nr_rows); - } -} - -static void -osd_thumbnail_create(struct output *output, struct wl_array *views) -{ - assert(!output->osd_scene.tree); - - struct theme *theme = output->server->theme; - struct window_switcher_thumbnail_theme *switcher_theme = - &theme->osd_window_switcher_thumbnail; - int padding = theme->osd_border_width + switcher_theme->padding; - - output->osd_scene.tree = wlr_scene_tree_create(output->osd_tree); - - int nr_views = wl_array_len(views); - assert(nr_views > 0); - int nr_rows, nr_cols; - get_items_geometry(output, theme, nr_views, &nr_rows, &nr_cols); - - /* items */ - struct view **view; - int index = 0; - wl_array_for_each(view, views) { - struct osd_thumbnail_scene_item *item = create_item_scene( - output->osd_scene.tree, *view, output); - if (!item) { - break; - } - int x = (index % nr_cols) * switcher_theme->item_width + padding; - int y = (index / nr_cols) * switcher_theme->item_height + padding; - wlr_scene_node_set_position(&item->tree->node, x, y); - index++; - } - - /* background */ - struct lab_scene_rect_options bg_opts = { - .border_colors = (float *[1]) { theme->osd_border_color }, - .nr_borders = 1, - .border_width = theme->osd_border_width, - .bg_color = theme->osd_bg_color, - .width = nr_cols * switcher_theme->item_width + 2 * padding, - .height = nr_rows * switcher_theme->item_height + 2 * padding, - }; - struct lab_scene_rect *bg = - lab_scene_rect_create(output->osd_scene.tree, &bg_opts); - wlr_scene_node_lower_to_bottom(&bg->tree->node); - - /* center */ - struct wlr_box usable = output_usable_area_in_layout_coords(output); - int lx = usable.x + (usable.width - bg_opts.width) / 2; - int ly = usable.y + (usable.height - bg_opts.height) / 2; - wlr_scene_node_set_position(&output->osd_scene.tree->node, lx, ly); -} - -static void -osd_thumbnail_update(struct output *output) -{ - struct osd_thumbnail_scene_item *item; - wl_array_for_each(item, &output->osd_scene.items) { - bool active = (item->view == output->server->osd_state.cycle_view); - wlr_scene_node_set_enabled(&item->active_bg->tree->node, active); - wlr_scene_node_set_enabled( - &item->active_title->scene_buffer->node, active); - wlr_scene_node_set_enabled( - &item->normal_title->scene_buffer->node, !active); - } -} - -struct osd_impl osd_thumbnail_impl = { - .create = osd_thumbnail_create, - .update = osd_thumbnail_update, -}; diff --git a/src/output-state.c b/src/output-state.c index 7da1108b..62e2c4d1 100644 --- a/src/output-state.c +++ b/src/output-state.c @@ -1,10 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "output-state.h" #include #include -#include -#include "output.h" +#include "labwc.h" +#include "output-state.h" void output_state_init(struct output *output) diff --git a/src/output-virtual.c b/src/output-virtual.c index f3a40e10..3853c9ef 100644 --- a/src/output-virtual.c +++ b/src/output-virtual.c @@ -1,13 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "output-virtual.h" #include #include #include -#include #include "common/string-helpers.h" #include "labwc.h" -#include "output.h" +#include "output-virtual.h" static struct wlr_output *fallback_output = NULL; diff --git a/src/output.c b/src/output.c index adee3f9d..3b4a26ca 100644 --- a/src/output.c +++ b/src/output.c @@ -7,25 +7,21 @@ */ #define _POSIX_C_SOURCE 200809L -#include "output.h" #include #include #include #include -#include +#include #include -#include #include -#include -#include -#include #include #include +#include #include +#include "common/direction.h" #include "common/macros.h" #include "common/mem.h" #include "common/scene-helpers.h" -#include "config/rcxml.h" #include "labwc.h" #include "layers.h" #include "node.h" @@ -34,7 +30,6 @@ #include "protocols/cosmic-workspaces.h" #include "protocols/ext-workspace.h" #include "regions.h" -#include "session-lock.h" #include "view.h" #include "xwayland.h" @@ -170,7 +165,7 @@ handle_output_destroy(struct wl_listener *listener, void *data) regions_evacuate_output(output); regions_destroy(seat, &output->regions); if (seat->overlay.active.output == output) { - overlay_finish(seat); + overlay_hide(seat); } wl_list_remove(&output->link); wl_list_remove(&output->frame.link); @@ -509,10 +504,18 @@ 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); + node_descriptor_create(&output->layer_tree[i]->node, + LAB_NODE_DESC_TREE, NULL); } output->layer_popup_tree = wlr_scene_tree_create(&server->scene->tree); + node_descriptor_create(&output->layer_popup_tree->node, + LAB_NODE_DESC_TREE, NULL); output->osd_tree = wlr_scene_tree_create(&server->scene->tree); + node_descriptor_create(&output->osd_tree->node, + LAB_NODE_DESC_TREE, NULL); output->session_lock_tree = wlr_scene_tree_create(&server->scene->tree); + node_descriptor_create(&output->session_lock_tree->node, + LAB_NODE_DESC_TREE, NULL); /* * Set the z-positions to achieve the following order (from top to @@ -538,21 +541,13 @@ handle_new_output(struct wl_listener *listener, void *data) wlr_scene_node_raise_to_top(&output->osd_tree->node); wlr_scene_node_raise_to_top(&output->session_lock_tree->node); - /* - * autoEnableOutputs=no only makes sense for outputs that can be - * hotplugged - currently only drm outputs. With wl/x11/headless - * it would result in no outputs being enabled at all. This check - * might need tweaking if wlroots adds other output backends. - */ - if (rc.auto_enable_outputs || !wlr_output_is_drm(wlr_output)) { + if (rc.auto_enable_outputs) { configure_new_output(server, output); } do_output_layout_change(server); } -static void output_manager_init(struct server *server); - void output_init(struct server *server) { @@ -892,7 +887,7 @@ handle_gamma_control_set_gamma(struct wl_listener *listener, void *data) wlr_output_schedule_frame(output->wlr_output); } -static void +void output_manager_init(struct server *server) { server->output_manager = wlr_output_manager_v1_create(server->wl_display); @@ -970,7 +965,7 @@ output_nearest_to_cursor(struct server *server) } struct output * -output_get_adjacent(struct output *output, enum lab_edge edge, bool wrap) +output_get_adjacent(struct output *output, enum view_edge edge, bool wrap) { if (!output_is_usable(output)) { wlr_log(WLR_ERROR, @@ -978,11 +973,6 @@ output_get_adjacent(struct output *output, enum lab_edge edge, bool wrap) return NULL; } - /* Allow only up/down/left/right */ - if (!lab_edge_is_cardinal(edge)) { - return NULL; - } - struct wlr_box box = output_usable_area_in_layout_coords(output); int lx = box.x + box.width / 2; int ly = box.y + box.height / 2; @@ -991,18 +981,17 @@ output_get_adjacent(struct output *output, enum lab_edge edge, bool wrap) struct wlr_output *new_output = NULL; struct wlr_output *current_output = output->wlr_output; struct wlr_output_layout *layout = output->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); + enum wlr_direction direction = direction_from_view_edge(edge); + new_output = wlr_output_layout_adjacent_output(layout, direction, + current_output, lx, ly); /* * Optionally wrap around from top-to-bottom or left-to-right, and vice * versa. */ if (wrap && !new_output) { - enum lab_edge opposite = lab_edge_invert(edge); new_output = wlr_output_layout_farthest_output(layout, - (enum wlr_direction)opposite, current_output, lx, ly); + direction_get_opposite(direction), current_output, lx, ly); } /* @@ -1144,3 +1133,17 @@ output_enable_adaptive_sync(struct output *output, bool enabled) enabled ? "en" : "dis", output->wlr_output->name); } } + +float +output_max_scale(struct server *server) +{ + /* Never return less than 1, in case outputs are disabled */ + float scale = 1; + struct output *output; + wl_list_for_each(output, &server->outputs, link) { + if (output_is_usable(output)) { + scale = MAX(scale, output->wlr_output->scale); + } + } + return scale; +} diff --git a/src/overlay.c b/src/overlay.c index 508a531a..354f5ad9 100644 --- a/src/overlay.c +++ b/src/overlay.c @@ -1,52 +1,103 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "overlay.h" #include -#include -#include -#include #include "common/lab-scene-rect.h" -#include "config/rcxml.h" #include "labwc.h" -#include "output.h" -#include "regions.h" -#include "theme.h" +#include "overlay.h" #include "view.h" +#include "theme.h" static void -show_overlay(struct seat *seat, struct theme_snapping_overlay *overlay_theme, - struct wlr_box *box) +create_overlay_rect(struct seat *seat, struct overlay_rect *rect, + struct theme_snapping_overlay *theme) +{ + struct server *server = seat->server; + + rect->bg_enabled = theme->bg_enabled; + rect->border_enabled = theme->border_enabled; + rect->tree = wlr_scene_tree_create(&server->scene->tree); + + if (rect->bg_enabled) { + /* Create a filled rectangle */ + rect->bg_rect = wlr_scene_rect_create( + rect->tree, 0, 0, theme->bg_color); + } + + if (rect->border_enabled) { + /* Create outlines */ + struct lab_scene_rect_options opts = { + .border_colors = (float *[3]) { + theme->border_color[0], + theme->border_color[1], + theme->border_color[2], + }, + .nr_borders = 3, + .border_width = theme->border_width, + }; + rect->border_rect = lab_scene_rect_create(rect->tree, &opts); + } + + wlr_scene_node_set_enabled(&rect->tree->node, false); +} + +void overlay_reconfigure(struct seat *seat) +{ + if (seat->overlay.region_rect.tree) { + wlr_scene_node_destroy(&seat->overlay.region_rect.tree->node); + } + if (seat->overlay.edge_rect.tree) { + wlr_scene_node_destroy(&seat->overlay.edge_rect.tree->node); + } + + struct theme *theme = seat->server->theme; + create_overlay_rect(seat, &seat->overlay.region_rect, + &theme->snapping_overlay_region); + create_overlay_rect(seat, &seat->overlay.edge_rect, + &theme->snapping_overlay_edge); +} + +static void +show_overlay(struct seat *seat, struct overlay_rect *rect, struct wlr_box *box) { struct server *server = seat->server; struct view *view = server->grabbed_view; assert(view); - assert(!seat->overlay.rect); - struct lab_scene_rect_options opts = { - .width = box->width, - .height = box->height, - }; - if (overlay_theme->bg_enabled) { - /* Create a filled rectangle */ - opts.bg_color = overlay_theme->bg_color; - } - float *border_colors[3] = { - overlay_theme->border_color[0], - overlay_theme->border_color[1], - overlay_theme->border_color[2], - }; - if (overlay_theme->border_enabled) { - /* Create outlines */ - opts.border_colors = border_colors; - opts.nr_borders = 3; - opts.border_width = overlay_theme->border_width; + if (!rect->tree) { + overlay_reconfigure(seat); + assert(rect->tree); } - seat->overlay.rect = - lab_scene_rect_create(view->scene_tree->node.parent, &opts); + if (rect->bg_enabled) { + wlr_scene_rect_set_size(rect->bg_rect, box->width, box->height); + } + if (rect->border_enabled) { + lab_scene_rect_set_size(rect->border_rect, box->width, box->height); + } - struct wlr_scene_node *node = &seat->overlay.rect->tree->node; + struct wlr_scene_node *node = &rect->tree->node; + wlr_scene_node_reparent(node, view->scene_tree->node.parent); wlr_scene_node_place_below(node, &view->scene_tree->node); wlr_scene_node_set_position(node, box->x, box->y); + wlr_scene_node_set_enabled(node, true); +} + +static void +inactivate_overlay(struct overlay *overlay) +{ + if (overlay->region_rect.tree) { + wlr_scene_node_set_enabled( + &overlay->region_rect.tree->node, false); + } + if (overlay->edge_rect.tree) { + wlr_scene_node_set_enabled( + &overlay->edge_rect.tree->node, false); + } + overlay->active.region = NULL; + overlay->active.edge = VIEW_EDGE_INVALID; + overlay->active.output = NULL; + if (overlay->timer) { + wl_event_source_timer_update(overlay->timer, 0); + } } static void @@ -55,67 +106,97 @@ show_region_overlay(struct seat *seat, struct region *region) if (region == seat->overlay.active.region) { return; } - overlay_finish(seat); + inactivate_overlay(&seat->overlay); seat->overlay.active.region = region; - struct wlr_box geo = view_get_region_snap_box(NULL, region); - show_overlay(seat, &rc.theme->snapping_overlay_region, &geo); + show_overlay(seat, &seat->overlay.region_rect, ®ion->geo); } -static struct wlr_box -get_edge_snap_box(enum lab_edge edge, struct output *output) +/* TODO: share logic with view_get_edge_snap_box() */ +static struct wlr_box get_edge_snap_box(enum view_edge edge, struct output *output) { - if (edge == LAB_EDGE_TOP && rc.snap_top_maximize) { - return output_usable_area_in_layout_coords(output); - } else { - return view_get_edge_snap_box(NULL, output, edge); + struct wlr_box box = output_usable_area_in_layout_coords(output); + switch (edge) { + case VIEW_EDGE_RIGHT: + box.x += box.width / 2; + /* fallthrough */ + case VIEW_EDGE_LEFT: + box.width /= 2; + break; + case VIEW_EDGE_DOWN: + box.y += box.height / 2; + /* fallthrough */ + case VIEW_EDGE_UP: + box.height /= 2; + break; + case VIEW_EDGE_CENTER: + /* */ + break; + default: + /* not reached */ + assert(false); } + return box; } static int handle_edge_overlay_timeout(void *data) { struct seat *seat = data; - assert(seat->overlay.active.edge != LAB_EDGE_NONE + assert(seat->overlay.active.edge != VIEW_EDGE_INVALID && seat->overlay.active.output); struct wlr_box box = get_edge_snap_box(seat->overlay.active.edge, seat->overlay.active.output); - show_overlay(seat, &rc.theme->snapping_overlay_edge, &box); + show_overlay(seat, &seat->overlay.edge_rect, &box); return 0; } +static enum wlr_direction +get_wlr_direction(enum view_edge edge) +{ + switch (edge) { + case VIEW_EDGE_LEFT: + return WLR_DIRECTION_LEFT; + case VIEW_EDGE_RIGHT: + return WLR_DIRECTION_RIGHT; + case VIEW_EDGE_UP: + case VIEW_EDGE_CENTER: + return WLR_DIRECTION_UP; + case VIEW_EDGE_DOWN: + return WLR_DIRECTION_DOWN; + default: + /* not reached */ + assert(false); + return 0; + } +} + static bool edge_has_adjacent_output_from_cursor(struct seat *seat, struct output *output, - enum lab_edge edge) + enum view_edge edge) { - /* Allow only up/down/left/right */ - if (!lab_edge_is_cardinal(edge)) { - return false; - } - /* 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, + seat->server->output_layout, get_wlr_direction(edge), output->wlr_output, seat->cursor->x, seat->cursor->y); } static void -show_edge_overlay(struct seat *seat, enum lab_edge edge1, enum lab_edge edge2, +show_edge_overlay(struct seat *seat, enum view_edge edge, struct output *output) { if (!rc.snap_overlay_enabled) { return; } - enum lab_edge edge = edge1 | edge2; if (seat->overlay.active.edge == edge && seat->overlay.active.output == output) { return; } - overlay_finish(seat); + inactivate_overlay(&seat->overlay); seat->overlay.active.edge = edge; seat->overlay.active.output = output; int delay; - if (edge_has_adjacent_output_from_cursor(seat, output, edge1)) { + if (edge_has_adjacent_output_from_cursor(seat, output, edge)) { delay = rc.snap_overlay_delay_inner; } else { delay = rc.snap_overlay_delay_outer; @@ -131,7 +212,9 @@ show_edge_overlay(struct seat *seat, enum lab_edge edge1, enum lab_edge edge2, wl_event_source_timer_update(seat->overlay.timer, delay); } else { /* Show overlay now */ - handle_edge_overlay_timeout(seat); + struct wlr_box box = get_edge_snap_box(seat->overlay.active.edge, + seat->overlay.active.output); + show_overlay(seat, &seat->overlay.edge_rect, &box); } } @@ -151,23 +234,42 @@ overlay_update(struct seat *seat) /* Edge-snapping overlay */ struct output *output; - enum lab_edge edge1, edge2; - if (edge_from_cursor(seat, &output, &edge1, &edge2)) { - show_edge_overlay(seat, edge1, edge2, output); + enum view_edge edge = edge_from_cursor(seat, &output); + if (edge != VIEW_EDGE_INVALID) { + show_edge_overlay(seat, edge, output); return; } - overlay_finish(seat); + overlay_hide(seat); +} + +void +overlay_hide(struct seat *seat) +{ + struct overlay *overlay = &seat->overlay; + struct server *server = seat->server; + + inactivate_overlay(overlay); + + /* + * Reparent the rectangle nodes to server's scene-tree so they don't + * get destroyed on view destruction + */ + if (overlay->region_rect.tree) { + wlr_scene_node_reparent(&overlay->region_rect.tree->node, + &server->scene->tree); + } + if (overlay->edge_rect.tree) { + wlr_scene_node_reparent(&overlay->edge_rect.tree->node, + &server->scene->tree); + } } void overlay_finish(struct seat *seat) { - if (seat->overlay.rect) { - wlr_scene_node_destroy(&seat->overlay.rect->tree->node); - } if (seat->overlay.timer) { wl_event_source_remove(seat->overlay.timer); + seat->overlay.timer = NULL; } - seat->overlay = (struct overlay){0}; } diff --git a/src/placement.c b/src/placement.c index 2b5cdf61..1e4fd937 100644 --- a/src/placement.c +++ b/src/placement.c @@ -1,12 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "placement.h" #include #include #include +#include +#include "common/macros.h" #include "common/mem.h" -#include "config/rcxml.h" #include "labwc.h" -#include "output.h" +#include "placement.h" #include "ssd.h" #include "view.h" diff --git a/src/protocols/cosmic_workspaces/cosmic-workspaces.c b/src/protocols/cosmic_workspaces/cosmic-workspaces.c index d12611cb..b39e2d17 100644 --- a/src/protocols/cosmic_workspaces/cosmic-workspaces.c +++ b/src/protocols/cosmic_workspaces/cosmic-workspaces.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #include +#include #include #include "common/array.h" #include "common/mem.h" @@ -322,12 +323,11 @@ manager_handle_commit(struct wl_client *client, struct wl_resource *resource) 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: { + 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); diff --git a/src/protocols/ext-workspace/ext-workspace.c b/src/protocols/ext-workspace/ext-workspace.c index 30875811..92032375 100644 --- a/src/protocols/ext-workspace/ext-workspace.c +++ b/src/protocols/ext-workspace/ext-workspace.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only #include +#include #include +#include "common/array.h" #include "common/mem.h" #include "common/list.h" #include "ext-workspace-v1-protocol.h" @@ -293,12 +295,11 @@ manager_handle_commit(struct wl_client *client, struct wl_resource *resource) 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: { + 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); diff --git a/src/protocols/transaction-addon.c b/src/protocols/transaction-addon.c index 808a2096..c4678497 100644 --- a/src/protocols/transaction-addon.c +++ b/src/protocols/transaction-addon.c @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "protocols/transaction-addon.h" #include #include #include "common/list.h" #include "common/mem.h" +#include "protocols/transaction-addon.h" void lab_transaction_op_destroy(struct lab_transaction_op *trans_op) diff --git a/src/regions.c b/src/regions.c index a5170d4f..112cc6e3 100644 --- a/src/regions.c +++ b/src/regions.c @@ -1,19 +1,18 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "regions.h" #include #include #include #include -#include +#include #include +#include #include "common/list.h" #include "common/mem.h" -#include "config/rcxml.h" #include "input/keyboard.h" #include "labwc.h" -#include "output.h" +#include "regions.h" #include "view.h" bool @@ -165,7 +164,7 @@ regions_destroy(struct seat *seat, struct wl_list *regions) wl_list_for_each_safe(region, region_tmp, regions, link) { wl_list_remove(®ion->link); if (seat && seat->overlay.active.region == region) { - overlay_finish(seat); + overlay_hide(seat); } zfree(region->name); zfree(region); diff --git a/src/resistance.c b/src/resistance.c index 12cbec90..bc1bf8ad 100644 --- a/src/resistance.c +++ b/src/resistance.c @@ -1,10 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "resistance.h" #include +#include #include "common/border.h" +#include "common/macros.h" #include "config/rcxml.h" #include "edges.h" #include "labwc.h" +#include "resistance.h" #include "snap-constraints.h" #include "view.h" @@ -165,7 +167,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 wlr_edges resize_edges = view->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..9186bc6f 100644 --- a/src/resize-outlines.c +++ b/src/resize-outlines.c @@ -1,14 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "resize-outlines.h" #include -#include "common/border.h" #include "common/lab-scene-rect.h" -#include "labwc.h" -#include "resize-indicator.h" #include "ssd.h" -#include "theme.h" -#include "view.h" +#include "resize-outlines.h" +#include "labwc.h" bool resize_outlines_enabled(struct view *view) diff --git a/src/scaled-buffer/meson.build b/src/scaled-buffer/meson.build deleted file mode 100644 index 7bd8fa8b..00000000 --- a/src/scaled-buffer/meson.build +++ /dev/null @@ -1,6 +0,0 @@ -labwc_sources += files( - 'scaled-font-buffer.c', - 'scaled-icon-buffer.c', - 'scaled-img-buffer.c', - 'scaled-buffer.c', -) diff --git a/src/seat.c b/src/seat.c index 7e882a9b..0beaf487 100644 --- a/src/seat.c +++ b/src/seat.c @@ -3,23 +3,13 @@ #include #include #include -#include #include #include -#include -#include #include -#include #include -#include -#include -#include #include #include "common/macros.h" #include "common/mem.h" -#include "config/libinput.h" -#include "config/rcxml.h" -#include "config/touch.h" #include "input/ime.h" #include "input/tablet.h" #include "input/tablet-pad.h" @@ -27,8 +17,6 @@ #include "input/keyboard.h" #include "input/key-state.h" #include "labwc.h" -#include "output.h" -#include "session-lock.h" #include "view.h" static void @@ -150,148 +138,100 @@ configure_libinput(struct wlr_input_device *wlr_input_device) */ assert(dc); - wlr_log(WLR_INFO, "configuring input device %s (%s)", - libinput_device_get_name(libinput_dev), - libinput_device_get_sysname(libinput_dev)); - - wlr_log(WLR_INFO, "matched category: %s", - dc->name ? dc->name : libinput_device_type_name(dc->type)); - - libinput_device_config_tap_set_enabled(libinput_dev, - libinput_device_config_tap_get_default_enabled(libinput_dev)); - libinput_device_config_tap_set_button_map(libinput_dev, - libinput_device_config_tap_get_default_button_map(libinput_dev)); if (libinput_device_config_tap_get_finger_count(libinput_dev) <= 0) { wlr_log(WLR_INFO, "tap unavailable"); } else { - wlr_log(WLR_INFO, "tap configured (tap=%d, button_map=%d)", - dc->tap, dc->tap_button_map); + wlr_log(WLR_INFO, "tap configured"); libinput_device_config_tap_set_enabled(libinput_dev, dc->tap); libinput_device_config_tap_set_button_map(libinput_dev, dc->tap_button_map); } - libinput_device_config_tap_set_drag_enabled(libinput_dev, - libinput_device_config_tap_get_default_drag_enabled(libinput_dev)); if (libinput_device_config_tap_get_finger_count(libinput_dev) <= 0 || dc->tap_and_drag < 0) { wlr_log(WLR_INFO, "tap-and-drag not configured"); } else { - wlr_log(WLR_INFO, "tap-and-drag configured (%d)", - dc->tap_and_drag); + wlr_log(WLR_INFO, "tap-and-drag configured"); libinput_device_config_tap_set_drag_enabled( libinput_dev, dc->tap_and_drag); } - libinput_device_config_tap_set_drag_lock_enabled(libinput_dev, - libinput_device_config_tap_get_default_drag_lock_enabled(libinput_dev)); if (libinput_device_config_tap_get_finger_count(libinput_dev) <= 0 || dc->drag_lock < 0) { wlr_log(WLR_INFO, "drag lock not configured"); } else { - wlr_log(WLR_INFO, "drag lock configured (%d)", dc->drag_lock); + wlr_log(WLR_INFO, "drag lock configured"); libinput_device_config_tap_set_drag_lock_enabled( libinput_dev, dc->drag_lock); } #if HAVE_LIBINPUT_CONFIG_3FG_DRAG_ENABLED_3FG - libinput_device_config_3fg_drag_set_enabled(libinput_dev, - libinput_device_config_3fg_drag_get_default_enabled(libinput_dev)); if (libinput_device_config_tap_get_finger_count(libinput_dev) <= 0 || dc->three_finger_drag < 0) { wlr_log(WLR_INFO, "three-finger drag not configured"); } else { - wlr_log(WLR_INFO, "three-finger drag configured (%d)", - dc->three_finger_drag); + wlr_log(WLR_INFO, "three-finger drag configured"); libinput_device_config_3fg_drag_set_enabled( libinput_dev, dc->three_finger_drag); } #endif - libinput_device_config_scroll_set_natural_scroll_enabled(libinput_dev, - libinput_device_config_scroll_get_default_natural_scroll_enabled(libinput_dev)); if (libinput_device_config_scroll_has_natural_scroll(libinput_dev) <= 0 || dc->natural_scroll < 0) { wlr_log(WLR_INFO, "natural scroll not configured"); } else { - wlr_log(WLR_INFO, "natural scroll configured (%d)", - dc->natural_scroll); + wlr_log(WLR_INFO, "natural scroll configured"); libinput_device_config_scroll_set_natural_scroll_enabled( libinput_dev, dc->natural_scroll); } - libinput_device_config_left_handed_set(libinput_dev, - libinput_device_config_left_handed_get_default(libinput_dev)); if (libinput_device_config_left_handed_is_available(libinput_dev) <= 0 || dc->left_handed < 0) { wlr_log(WLR_INFO, "left-handed mode not configured"); } else { - wlr_log(WLR_INFO, "left-handed mode configured (%d)", - dc->left_handed); + wlr_log(WLR_INFO, "left-handed mode configured"); libinput_device_config_left_handed_set(libinput_dev, dc->left_handed); } - libinput_device_config_accel_set_speed(libinput_dev, - libinput_device_config_accel_get_default_speed(libinput_dev)); - libinput_device_config_accel_set_profile(libinput_dev, - libinput_device_config_accel_get_default_profile(libinput_dev)); if (libinput_device_config_accel_is_available(libinput_dev) == 0) { wlr_log(WLR_INFO, "pointer acceleration unavailable"); } else { + wlr_log(WLR_INFO, "pointer acceleration configured"); if (dc->pointer_speed >= -1) { - wlr_log(WLR_INFO, "pointer speed configured (%g)", - dc->pointer_speed); libinput_device_config_accel_set_speed(libinput_dev, dc->pointer_speed); - } else { - wlr_log(WLR_INFO, "pointer speed not configured"); } - if (dc->accel_profile > 0) { - wlr_log(WLR_INFO, - "pointer accel profile configured (%d)", - dc->accel_profile); libinput_device_config_accel_set_profile(libinput_dev, dc->accel_profile); - } else { - wlr_log(WLR_INFO, - "pointer accel profile not configured"); } } - libinput_device_config_middle_emulation_set_enabled(libinput_dev, - libinput_device_config_middle_emulation_get_default_enabled(libinput_dev)); if (libinput_device_config_middle_emulation_is_available(libinput_dev) == 0 || dc->middle_emu < 0) { wlr_log(WLR_INFO, "middle emulation not configured"); } else { - wlr_log(WLR_INFO, "middle emulation configured (%d)", - dc->middle_emu); + wlr_log(WLR_INFO, "middle emulation configured"); libinput_device_config_middle_emulation_set_enabled( libinput_dev, dc->middle_emu); } - libinput_device_config_dwt_set_enabled(libinput_dev, - libinput_device_config_dwt_get_default_enabled(libinput_dev)); if (libinput_device_config_dwt_is_available(libinput_dev) == 0 || dc->dwt < 0) { wlr_log(WLR_INFO, "dwt not configured"); } else { - wlr_log(WLR_INFO, "dwt configured (%d)", dc->dwt); + wlr_log(WLR_INFO, "dwt configured"); libinput_device_config_dwt_set_enabled(libinput_dev, dc->dwt); } - libinput_device_config_click_set_method(libinput_dev, - libinput_device_config_click_get_default_method(libinput_dev)); if ((dc->click_method != LIBINPUT_CONFIG_CLICK_METHOD_NONE && (libinput_device_config_click_get_methods(libinput_dev) & dc->click_method) == 0) || dc->click_method < 0) { wlr_log(WLR_INFO, "click method not configured"); } else { - wlr_log(WLR_INFO, "click method configured (%d)", - dc->click_method); + wlr_log(WLR_INFO, "click method configured"); /* * Note, the documentation claims that: @@ -305,8 +245,6 @@ configure_libinput(struct wlr_input_device *wlr_input_device) libinput_device_config_click_set_method(libinput_dev, dc->click_method); } - libinput_device_config_scroll_set_method(libinput_dev, - libinput_device_config_scroll_get_default_method(libinput_dev)); if (dc->scroll_method < 0) { wlr_log(WLR_INFO, "scroll method not configured"); } else if (dc->scroll_method != LIBINPUT_CONFIG_SCROLL_NO_SCROLL @@ -314,27 +252,20 @@ configure_libinput(struct wlr_input_device *wlr_input_device) & dc->scroll_method) == 0) { wlr_log(WLR_INFO, "scroll method not supported"); } else { - wlr_log(WLR_INFO, "scroll method configured (%d)", - dc->scroll_method); + wlr_log(WLR_INFO, "scroll method configured"); libinput_device_config_scroll_set_method(libinput_dev, dc->scroll_method); } - 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 && (libinput_device_config_send_events_get_modes(libinput_dev) & dc->send_events_mode) == 0) || dc->send_events_mode < 0) { wlr_log(WLR_INFO, "send events mode not configured"); } else { - wlr_log(WLR_INFO, "send events mode configured (%d)", - dc->send_events_mode); + wlr_log(WLR_INFO, "send events mode configured"); libinput_device_config_send_events_set_mode(libinput_dev, dc->send_events_mode); } - float default_matrix[6]; - libinput_device_config_calibration_get_default_matrix(libinput_dev, default_matrix); - libinput_device_config_calibration_set_matrix(libinput_dev, default_matrix); /* Non-zero if the device can be calibrated, zero otherwise. */ if (libinput_device_config_calibration_has_matrix(libinput_dev) == 0 || !dc->have_calibration_matrix) { @@ -344,7 +275,7 @@ configure_libinput(struct wlr_input_device *wlr_input_device) libinput_device_config_calibration_set_matrix(libinput_dev, dc->calibration_matrix); } - wlr_log(WLR_INFO, "scroll factor configured (%g)", dc->scroll_factor); + wlr_log(WLR_INFO, "scroll factor configured"); input->scroll_factor = dc->scroll_factor; } @@ -557,9 +488,9 @@ handle_new_input(struct wl_listener *listener, void *data) } static void -handle_new_virtual_pointer(struct wl_listener *listener, void *data) +new_virtual_pointer(struct wl_listener *listener, void *data) { - struct seat *seat = wl_container_of(listener, seat, new_virtual_pointer); + struct seat *seat = wl_container_of(listener, seat, virtual_pointer_new); struct wlr_virtual_pointer_v1_new_pointer_event *event = data; struct wlr_virtual_pointer_v1 *pointer = event->new_pointer; struct wlr_input_device *device = &pointer->pointer.base; @@ -643,7 +574,9 @@ seat_init(struct server *server) seat->virtual_pointer = wlr_virtual_pointer_manager_v1_create( server->wl_display); - CONNECT_SIGNAL(seat->virtual_pointer, seat, new_virtual_pointer); + wl_signal_add(&seat->virtual_pointer->events.new_virtual_pointer, + &seat->virtual_pointer_new); + seat->virtual_pointer_new.notify = new_virtual_pointer; seat->virtual_keyboard = wlr_virtual_keyboard_manager_v1_create( server->wl_display); @@ -673,7 +606,7 @@ seat_finish(struct server *server) 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); + wl_list_remove(&seat->virtual_pointer_new.link); wl_list_remove(&seat->new_virtual_keyboard.link); struct input *input, *next; @@ -730,7 +663,7 @@ seat_reconfigure(struct server *server) struct seat *seat = &server->seat; struct input *input; cursor_reload(seat); - overlay_finish(seat); + overlay_reconfigure(seat); keyboard_reset_current_keybind(); wl_list_for_each(input, &seat->inputs, link) { switch (input->wlr_input_device->type) { diff --git a/src/server.c b/src/server.c index abaaaf0b..214885b1 100644 --- a/src/server.c +++ b/src/server.c @@ -6,35 +6,25 @@ #include #include #include -#include #include #include -#include -#include -#include -#include #include +#include +#include #include -#include -#include #include #include -#include +#include #include -#include -#include #include #include -#include -#include #include +#include +#include #include #include -#include -#include -#include -#include #include +#include #include #include #include @@ -44,8 +34,9 @@ #include "xwayland-shell-v1-protocol.h" #endif -#include "action.h" +#include "drm-lease-v1-protocol.h" #include "common/macros.h" +#include "common/scaled-scene-buffer.h" #include "config/rcxml.h" #include "config/session.h" #include "decorations.h" @@ -56,13 +47,10 @@ #include "layers.h" #include "magnifier.h" #include "menu/menu.h" -#include "output.h" +#include "output-state.h" #include "output-virtual.h" #include "regions.h" #include "resize-indicator.h" -#include "scaled-buffer/scaled-buffer.h" -#include "session-lock.h" -#include "ssd.h" #include "theme.h" #include "view.h" #include "workspaces.h" @@ -78,10 +66,7 @@ static void reload_config_and_theme(struct server *server) { - /* Avoid UAF when dialog client is used during reconfigure */ - action_prompts_destroy(); - - scaled_buffer_invalidate_sharing(); + scaled_scene_buffer_invalidate_sharing(); rcxml_finish(); rcxml_read(rc.config_file); theme_finish(server->theme); @@ -162,11 +147,9 @@ handle_sigchld(int signal, void *data) const char *signame; switch (info.si_code) { case CLD_EXITED: - if (!action_check_prompt_result(info.si_pid, info.si_status)) { - wlr_log(info.si_status == 0 ? WLR_DEBUG : WLR_ERROR, - "spawned child %ld exited with %d", - (long)info.si_pid, info.si_status); - } + wlr_log(info.si_status == 0 ? WLR_DEBUG : WLR_ERROR, + "spawned child %ld exited with %d", + (long)info.si_pid, info.si_status); break; case CLD_KILLED: case CLD_DUMPED: @@ -175,8 +158,6 @@ handle_sigchld(int signal, void *data) "spawned child %ld terminated with signal %d (%s)", (long)info.si_pid, info.si_status, signame ? signame : "unknown"); - /* Allow cleanup of killed prompt */ - action_check_prompt_result(info.si_pid, -info.si_status); break; default: wlr_log(WLR_ERROR, @@ -276,7 +257,6 @@ 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", /* plus */ "wp_alpha_modifier_v1", "wp_linux_drm_syncobj_manager_v1", @@ -550,6 +530,8 @@ server_init(struct server *server) wl_list_init(&server->views); wl_list_init(&server->unmanaged_surfaces); + server->ssd_hover_state = ssd_hover_state_new(); + server->scene = wlr_scene_create(); if (!server->scene) { wlr_log(WLR_ERROR, "unable to create scene"); @@ -662,7 +644,7 @@ server_init(struct server *server) 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->seat.seat); server->relative_pointer_manager = wlr_relative_pointer_manager_v1_create( server->wl_display); @@ -794,4 +776,5 @@ server_finish(struct server *server) wlr_scene_node_destroy(&server->scene->tree.node); wl_display_destroy(server->wl_display); + free(server->ssd_hover_state); } diff --git a/src/session-lock.c b/src/session-lock.c index a370241e..edad8f64 100644 --- a/src/session-lock.c +++ b/src/session-lock.c @@ -1,14 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "session-lock.h" #include -#include -#include -#include #include "common/mem.h" #include "labwc.h" #include "node.h" -#include "output.h" struct session_lock_output { struct wlr_scene_tree *tree; @@ -138,7 +133,7 @@ handle_new_surface(struct wl_listener *listener, void *data) struct wlr_scene_tree *surface_tree = wlr_scene_subsurface_tree_create(lock_output->tree, lock_surface->surface); node_descriptor_create(&surface_tree->node, - LAB_NODE_SESSION_LOCK_SURFACE, /*view*/ NULL, /*data*/ NULL); + LAB_NODE_DESC_SESSION_LOCK_SURFACE, NULL); lock_output->surface_destroy.notify = handle_surface_destroy; wl_signal_add(&lock_surface->events.destroy, &lock_output->surface_destroy); @@ -221,7 +216,7 @@ session_lock_output_create(struct session_lock_manager *manager, struct output * * all outputs with an opaque color such that their normal content is * fully hidden */ - float black[4] = { 0.f, 0.f, 0.f, 1.f }; + float *black = (float[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()"); diff --git a/src/snap-constraints.c b/src/snap-constraints.c index fd1f09cd..bc1ffd14 100644 --- a/src/snap-constraints.c +++ b/src/snap-constraints.c @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "snap-constraints.h" #include -#include #include #include "common/macros.h" +#include "labwc.h" +#include "snap-constraints.h" #include "view.h" /* @@ -39,7 +39,7 @@ static struct { bool pending; int vertical_offset; int horizontal_offset; - enum lab_edge resize_edges; + enum wlr_edges resize_edges; struct wlr_box geom; } last_snap_hit; @@ -50,12 +50,12 @@ snap_constraints_reset(void) last_snap_hit.pending = false; last_snap_hit.horizontal_offset = INT_MIN; last_snap_hit.vertical_offset = INT_MIN; - last_snap_hit.resize_edges = LAB_EDGE_NONE; + last_snap_hit.resize_edges = WLR_EDGE_NONE; memset(&last_snap_hit.geom, 0, sizeof(last_snap_hit.geom)); } static bool -snap_constraints_are_valid(struct view *view, enum lab_edge resize_edges) +snap_constraints_are_valid(struct view *view, enum wlr_edges resize_edges) { assert(view); @@ -70,20 +70,20 @@ snap_constraints_are_valid(struct view *view, enum lab_edge resize_edges) } /* Cache is not valid if edge offsets are invalid */ - if (resize_edges & LAB_EDGES_LEFT_RIGHT) { + if (resize_edges & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)) { if (!BOUNDED_INT(last_snap_hit.horizontal_offset)) { return false; } - if ((resize_edges & LAB_EDGE_LEFT) && (resize_edges & LAB_EDGE_RIGHT)) { + if ((resize_edges & WLR_EDGE_LEFT) && (resize_edges & WLR_EDGE_RIGHT)) { return false; } - } else if (resize_edges & LAB_EDGES_TOP_BOTTOM) { + } else if (resize_edges & (WLR_EDGE_TOP | WLR_EDGE_BOTTOM)) { if (!BOUNDED_INT(last_snap_hit.vertical_offset)) { return false; } - if ((resize_edges & LAB_EDGE_TOP) && (resize_edges & LAB_EDGE_BOTTOM)) { + if ((resize_edges & WLR_EDGE_TOP) && (resize_edges & WLR_EDGE_BOTTOM)) { return false; } } else { @@ -95,24 +95,24 @@ snap_constraints_are_valid(struct view *view, enum lab_edge resize_edges) } void -snap_constraints_set(struct view *view, enum lab_edge resize_edges, - struct wlr_box geom) +snap_constraints_set(struct view *view, + enum wlr_edges resize_edges, struct wlr_box geom) { assert(view); /* Set horizontal offset when resizing horizontal edges */ last_snap_hit.horizontal_offset = INT_MIN; - if (resize_edges & LAB_EDGE_LEFT) { + if (resize_edges & WLR_EDGE_LEFT) { last_snap_hit.horizontal_offset = geom.x; - } else if (resize_edges & LAB_EDGE_RIGHT) { + } else if (resize_edges & WLR_EDGE_RIGHT) { last_snap_hit.horizontal_offset = geom.x + geom.width; } /* Set vertical offset when resizing vertical edges */ last_snap_hit.vertical_offset = INT_MIN; - if (resize_edges & LAB_EDGE_TOP) { + if (resize_edges & WLR_EDGE_TOP) { last_snap_hit.vertical_offset = geom.y; - } else if (resize_edges & LAB_EDGE_BOTTOM) { + } else if (resize_edges & WLR_EDGE_BOTTOM) { last_snap_hit.vertical_offset = geom.y + geom.height; } @@ -166,8 +166,8 @@ snap_constraints_update(struct view *view) } struct wlr_box -snap_constraints_effective(struct view *view, enum lab_edge resize_edges, - bool use_pending) +snap_constraints_effective(struct view *view, + enum wlr_edges resize_edges, bool use_pending) { assert(view); @@ -181,15 +181,15 @@ snap_constraints_effective(struct view *view, enum lab_edge resize_edges, /* Override changing edge with constrained value */ struct wlr_box geom = real_geom; - if (resize_edges & LAB_EDGE_LEFT) { + if (resize_edges & WLR_EDGE_LEFT) { geom.x = last_snap_hit.horizontal_offset; - } else if (resize_edges & LAB_EDGE_RIGHT) { + } else if (resize_edges & WLR_EDGE_RIGHT) { geom.width = last_snap_hit.horizontal_offset - geom.x; } - if (resize_edges & LAB_EDGE_TOP) { + if (resize_edges & WLR_EDGE_TOP) { geom.y = last_snap_hit.vertical_offset; - } else if (resize_edges & LAB_EDGE_BOTTOM) { + } else if (resize_edges & WLR_EDGE_BOTTOM) { geom.height = last_snap_hit.vertical_offset - geom.y; } diff --git a/src/snap.c b/src/snap.c index 3da5f701..228572fa 100644 --- a/src/snap.c +++ b/src/snap.c @@ -1,14 +1,13 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "snap.h" #include +#include #include -#include #include "common/border.h" -#include "config/rcxml.h" +#include "common/macros.h" #include "edges.h" -#include "output.h" +#include "labwc.h" #include "snap-constraints.h" -#include "ssd.h" +#include "snap.h" #include "view.h" static void @@ -52,7 +51,7 @@ check_edge(int *next, struct edge current, struct edge target, } void -snap_move_to_edge(struct view *view, enum lab_edge direction, +snap_move_to_edge(struct view *view, enum view_edge direction, bool snap_to_windows, int *dx, int *dy) { assert(view); @@ -81,26 +80,26 @@ snap_move_to_edge(struct view *view, enum lab_edge direction, * needs no further consideration. */ switch (direction) { - case LAB_EDGE_LEFT: + case VIEW_EDGE_LEFT: target.x = usable.x + ssd.left + rc.gap; if (target.x >= view->pending.x) { return; } break; - case LAB_EDGE_RIGHT: + case VIEW_EDGE_RIGHT: target.x = usable.x + usable.width - rc.gap - target.width - ssd.right; if (target.x <= view->pending.x) { return; } break; - case LAB_EDGE_TOP: + case VIEW_EDGE_UP: target.y = usable.y + ssd.top + rc.gap; if (target.y >= view->pending.y) { return; } break; - case LAB_EDGE_BOTTOM: + case VIEW_EDGE_DOWN: target.y = usable.y + usable.height - rc.gap - ssd.bottom - view_effective_height(view, /* use_pending */ true); if (target.y <= view->pending.y) { @@ -135,7 +134,7 @@ snap_move_to_edge(struct view *view, enum lab_edge direction, void snap_grow_to_next_edge(struct view *view, - enum lab_edge direction, struct wlr_box *geo) + enum view_edge direction, struct wlr_box *geo) { assert(view); assert(!view->shaded); @@ -150,29 +149,29 @@ snap_grow_to_next_edge(struct view *view, struct border ssd = ssd_thickness(view); struct wlr_box usable = output_usable_area_in_layout_coords(output); - enum lab_edge resize_edges; + uint32_t resize_edges; /* First try to grow the view to the relevant edge of its output. */ switch (direction) { - case LAB_EDGE_LEFT: + case VIEW_EDGE_LEFT: geo->x = usable.x + ssd.left + rc.gap; geo->width = view->pending.x + view->pending.width - geo->x; - resize_edges = LAB_EDGE_LEFT; + resize_edges = WLR_EDGE_LEFT; break; - case LAB_EDGE_RIGHT: + case VIEW_EDGE_RIGHT: geo->width = usable.x + usable.width - rc.gap - ssd.right - view->pending.x; - resize_edges = LAB_EDGE_RIGHT; + resize_edges = WLR_EDGE_RIGHT; break; - case LAB_EDGE_TOP: + case VIEW_EDGE_UP: geo->y = usable.y + ssd.top + rc.gap; geo->height = view->pending.y + view->pending.height - geo->y; - resize_edges = LAB_EDGE_TOP; + resize_edges = WLR_EDGE_TOP; break; - case LAB_EDGE_BOTTOM: + case VIEW_EDGE_DOWN: geo->height = usable.y + usable.height - rc.gap - ssd.bottom - view->pending.y; - resize_edges = LAB_EDGE_BOTTOM; + resize_edges = WLR_EDGE_BOTTOM; break; default: return; @@ -215,13 +214,14 @@ snap_grow_to_next_edge(struct view *view, void snap_shrink_to_next_edge(struct view *view, - enum lab_edge direction, struct wlr_box *geo) + enum view_edge direction, struct wlr_box *geo) { assert(view); assert(!view->shaded); *geo = view->pending; - enum lab_edge resize_edges; + uint32_t resize_edges; + int min_width = view_get_min_width(); /* * First shrink the view along the relevant edge. The maximum shrink @@ -229,23 +229,23 @@ snap_shrink_to_next_edge(struct view *view, * minimum size requirements. */ switch (direction) { - case LAB_EDGE_RIGHT: - geo->width = MAX(geo->width / 2, LAB_MIN_VIEW_WIDTH); + case VIEW_EDGE_RIGHT: + geo->width = MAX(geo->width / 2, min_width); geo->x = view->pending.x + view->pending.width - geo->width; - resize_edges = LAB_EDGE_LEFT; + resize_edges = WLR_EDGE_LEFT; break; - case LAB_EDGE_LEFT: - geo->width = MAX(geo->width / 2, LAB_MIN_VIEW_WIDTH); - resize_edges = LAB_EDGE_RIGHT; + case VIEW_EDGE_LEFT: + geo->width = MAX(geo->width / 2, min_width); + resize_edges = WLR_EDGE_RIGHT; break; - case LAB_EDGE_BOTTOM: + case VIEW_EDGE_DOWN: geo->height = MAX(geo->height / 2, LAB_MIN_VIEW_HEIGHT); geo->y = view->pending.y + view->pending.height - geo->height; - resize_edges = LAB_EDGE_TOP; + resize_edges = WLR_EDGE_TOP; break; - case LAB_EDGE_TOP: + case VIEW_EDGE_UP: geo->height = MAX(geo->height / 2, LAB_MIN_VIEW_HEIGHT); - resize_edges = LAB_EDGE_BOTTOM; + resize_edges = WLR_EDGE_BOTTOM; break; default: return; diff --git a/src/ssd/meson.build b/src/ssd/meson.build index a316409b..c37eef45 100644 --- a/src/ssd/meson.build +++ b/src/ssd/meson.build @@ -1,7 +1,7 @@ labwc_sources += files( 'resize-indicator.c', 'ssd.c', - 'ssd-button.c', + 'ssd-part.c', 'ssd-titlebar.c', 'ssd-border.c', 'ssd-extents.c', diff --git a/src/ssd/resize-indicator.c b/src/ssd/resize-indicator.c index 3a635edc..c30900cf 100644 --- a/src/ssd/resize-indicator.c +++ b/src/ssd/resize-indicator.c @@ -3,17 +3,13 @@ #include #include #include -#include "config/rcxml.h" +#include "common/macros.h" +#include "common/scaled-font-buffer.h" #include "labwc.h" #include "resize-indicator.h" #include "resize-outlines.h" -#include "scaled-buffer/scaled-font-buffer.h" -#include "ssd.h" -#include "theme.h" #include "view.h" -#define PADDING rc.theme->osd_window_switcher_classic.padding - static void resize_indicator_reconfigure_view(struct resize_indicator *indicator) { @@ -21,7 +17,7 @@ resize_indicator_reconfigure_view(struct resize_indicator *indicator) struct theme *theme = rc.theme; indicator->height = font_height(&rc.font_osd) - + 2 * PADDING + + 2 * theme->osd_window_switcher_padding + 2 * theme->osd_border_width; /* Static positions */ @@ -29,8 +25,8 @@ resize_indicator_reconfigure_view(struct resize_indicator *indicator) theme->osd_border_width, theme->osd_border_width); wlr_scene_node_set_position(&indicator->text->scene_buffer->node, - theme->osd_border_width + PADDING, - theme->osd_border_width + PADDING); + theme->osd_border_width + theme->osd_window_switcher_padding, + theme->osd_border_width + theme->osd_window_switcher_padding); /* Colors */ wlr_scene_rect_set_color(indicator->border, theme->osd_border_color); @@ -109,7 +105,7 @@ resize_indicator_set_size(struct resize_indicator *indicator, int width) /* We are not using a width-cache-early-out here to allow for theme changes */ indicator->width = width - + 2 * PADDING + + 2 * rc.theme->osd_window_switcher_padding + 2 * rc.theme->osd_border_width; wlr_scene_rect_set_size(indicator->border, indicator->width, indicator->height); @@ -193,6 +189,9 @@ resize_indicator_update(struct view *view) /* Let the indicator change width as required by the content */ int width = font_width(&rc.font_osd, text); + /* font_extents() adds 4 pixels to the calculated width */ + width -= 4; + resize_indicator_set_size(indicator, width); /* Center the indicator in the window */ diff --git a/src/ssd/ssd-border.c b/src/ssd/ssd-border.c index f0db4fff..e96c65c6 100644 --- a/src/ssd/ssd-border.c +++ b/src/ssd/ssd-border.c @@ -1,14 +1,16 @@ // SPDX-License-Identifier: GPL-2.0-only #include -#include -#include "common/macros.h" +#include "common/scene-helpers.h" #include "labwc.h" -#include "ssd.h" #include "ssd-internal.h" #include "theme.h" #include "view.h" +#define FOR_EACH_STATE(ssd, tmp) FOR_EACH(tmp, \ + &(ssd)->border.active, \ + &(ssd)->border.inactive) + void ssd_border_create(struct ssd *ssd) { @@ -22,37 +24,35 @@ ssd_border_create(struct ssd *ssd) int full_width = width + 2 * theme->border_width; int corner_width = ssd_get_corner_width(); + float *color; + struct wlr_scene_tree *parent; + struct ssd_sub_tree *subtree; + int active; + ssd->border.tree = 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]; + FOR_EACH_STATE(ssd, subtree) { subtree->tree = wlr_scene_tree_create(ssd->border.tree); - struct wlr_scene_tree *parent = subtree->tree; + parent = subtree->tree; + active = (subtree == &ssd->border.active) ? + THEME_ACTIVE : THEME_INACTIVE; wlr_scene_node_set_enabled(&parent->node, active); - float *color = theme->window[active].border_color; + color = theme->window[active].border_color; - subtree->left = 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, - 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, - full_width, theme->border_width, color); - wlr_scene_node_set_position(&subtree->bottom->node, - 0, height); - - subtree->top = wlr_scene_rect_create(parent, - MAX(width - 2 * corner_width, 0), theme->border_width, color); - wlr_scene_node_set_position(&subtree->top->node, + wl_list_init(&subtree->parts); + add_scene_rect(&subtree->parts, LAB_SSD_PART_LEFT, parent, + theme->border_width, height, 0, 0, color); + add_scene_rect(&subtree->parts, LAB_SSD_PART_RIGHT, parent, + theme->border_width, height, + theme->border_width + width, 0, color); + add_scene_rect(&subtree->parts, LAB_SSD_PART_BOTTOM, parent, + full_width, theme->border_width, 0, height, color); + add_scene_rect(&subtree->parts, LAB_SSD_PART_TOP, parent, + MAX(width - 2 * corner_width, 0), theme->border_width, theme->border_width + corner_width, - -(ssd->titlebar.height + theme->border_width)); - } + -(ssd->titlebar.height + theme->border_width), color); + } FOR_EACH_END if (view->maximized == VIEW_AXIS_BOTH) { wlr_scene_node_set_enabled(&ssd->border.tree->node, false); @@ -128,30 +128,50 @@ ssd_border_update(struct ssd *ssd) ? 0 : theme->border_width + corner_width; - enum ssd_active_state active; - FOR_EACH_ACTIVE_STATE(active) { - struct ssd_border_subtree *subtree = &ssd->border.subtrees[active]; - - wlr_scene_rect_set_size(subtree->left, - theme->border_width, side_height); - wlr_scene_node_set_position(&subtree->left->node, - 0, side_y); - - wlr_scene_rect_set_size(subtree->right, - theme->border_width, side_height); - wlr_scene_node_set_position(&subtree->right->node, - theme->border_width + width, side_y); - - wlr_scene_rect_set_size(subtree->bottom, - full_width, theme->border_width); - wlr_scene_node_set_position(&subtree->bottom->node, - 0, height); - - wlr_scene_rect_set_size(subtree->top, - top_width, theme->border_width); - wlr_scene_node_set_position(&subtree->top->node, - top_x, -(ssd->titlebar.height + theme->border_width)); - } + struct ssd_part *part; + struct wlr_scene_rect *rect; + struct ssd_sub_tree *subtree; + FOR_EACH_STATE(ssd, subtree) { + wl_list_for_each(part, &subtree->parts, link) { + rect = wlr_scene_rect_from_node(part->node); + switch (part->type) { + case LAB_SSD_PART_LEFT: + wlr_scene_rect_set_size(rect, + theme->border_width, + side_height); + wlr_scene_node_set_position(part->node, + 0, + side_y); + continue; + case LAB_SSD_PART_RIGHT: + wlr_scene_rect_set_size(rect, + theme->border_width, + side_height); + wlr_scene_node_set_position(part->node, + theme->border_width + width, + side_y); + continue; + case LAB_SSD_PART_BOTTOM: + wlr_scene_rect_set_size(rect, + full_width, + theme->border_width); + wlr_scene_node_set_position(part->node, + 0, + height); + continue; + case LAB_SSD_PART_TOP: + wlr_scene_rect_set_size(rect, + top_width, + theme->border_width); + wlr_scene_node_set_position(part->node, + top_x, + -(ssd->titlebar.height + theme->border_width)); + continue; + default: + continue; + } + } + } FOR_EACH_END } void @@ -160,6 +180,15 @@ ssd_border_destroy(struct ssd *ssd) assert(ssd); assert(ssd->border.tree); + struct ssd_sub_tree *subtree; + FOR_EACH_STATE(ssd, subtree) { + ssd_destroy_parts(&subtree->parts); + wlr_scene_node_destroy(&subtree->tree->node); + subtree->tree = NULL; + } FOR_EACH_END + wlr_scene_node_destroy(&ssd->border.tree->node); - ssd->border = (struct ssd_border_scene){0}; + ssd->border.tree = NULL; } + +#undef FOR_EACH_STATE diff --git a/src/ssd/ssd-button.c b/src/ssd/ssd-button.c deleted file mode 100644 index 50131a12..00000000 --- a/src/ssd/ssd-button.c +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only - -#include -#include -#include "config/rcxml.h" -#include "common/list.h" -#include "common/mem.h" -#include "node.h" -#include "scaled-buffer/scaled-icon-buffer.h" -#include "scaled-buffer/scaled-img-buffer.h" -#include "ssd.h" -#include "ssd-internal.h" - -/* Internal API */ - -struct ssd_button * -attach_ssd_button(struct wl_list *button_parts, enum lab_node_type type, - struct wlr_scene_tree *parent, - 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); - wlr_scene_node_set_position(&root->node, x, y); - - assert(node_type_contains(LAB_NODE_BUTTON, type)); - struct ssd_button *button = znew(*button); - button->node = &root->node; - button->type = type; - node_descriptor_create(&root->node, type, view, button); - wl_list_append(button_parts, &button->link); - - /* Hitbox */ - float invisible[4] = { 0, 0, 0, 0 }; - wlr_scene_rect_create(root, rc.theme->window_button_width, - rc.theme->window_button_height, invisible); - - /* Icons */ - int button_width = rc.theme->window_button_width; - int button_height = rc.theme->window_button_height; - /* - * Ensure a small amount of horizontal padding within the button - * area (2px on each side with the default 26px button width). - * A new theme setting could be added to configure this. Using - * an existing setting (padding.width or window.button.spacing) - * was considered, but these settings have distinct purposes - * already and are zero by default. - */ - int icon_padding = button_width / 10; - - 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); - assert(icon_buffer); - struct wlr_scene_node *icon_node = &icon_buffer->scene_buffer->node; - scaled_icon_buffer_set_view(icon_buffer, view); - wlr_scene_node_set_position(icon_node, icon_padding, 0); - button->window_icon = icon_buffer; - } else { - for (uint8_t state_set = LAB_BS_DEFAULT; - state_set <= LAB_BS_ALL; state_set++) { - if (!imgs[state_set]) { - continue; - } - struct scaled_img_buffer *img_buffer = scaled_img_buffer_create( - root, imgs[state_set], rc.theme->window_button_width, - rc.theme->window_button_height); - assert(img_buffer); - struct wlr_scene_node *icon_node = &img_buffer->scene_buffer->node; - wlr_scene_node_set_enabled(icon_node, false); - button->img_buffers[state_set] = img_buffer; - } - /* Initially show non-hover, non-toggled, unrounded variant */ - wlr_scene_node_set_enabled( - &button->img_buffers[LAB_BS_DEFAULT]->scene_buffer->node, true); - } - - return button; -} - -/* called from node descriptor destroy */ -void ssd_button_free(struct ssd_button *button) -{ - wl_list_remove(&button->link); - free(button); -} diff --git a/src/ssd/ssd-extents.c b/src/ssd/ssd-extents.c index bc1ed9f7..c2ff6d0c 100644 --- a/src/ssd/ssd-extents.c +++ b/src/ssd/ssd-extents.c @@ -1,19 +1,31 @@ // SPDX-License-Identifier: GPL-2.0-only +#include #include -#include +#include "common/mem.h" +#include "common/scene-helpers.h" #include "config/rcxml.h" #include "labwc.h" -#include "output.h" #include "ssd-internal.h" #include "theme.h" #include "view.h" +static struct ssd_part * +add_extent(struct wl_list *part_list, enum ssd_part_type type, + struct wlr_scene_tree *parent) +{ + float invisible[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + struct ssd_part *part = add_scene_part(part_list, type); + part->node = &wlr_scene_rect_create(parent, 0, 0, invisible)->node; + return part; +} + void ssd_extents_create(struct ssd *ssd) { struct view *view = ssd->view; struct theme *theme = view->server->theme; + struct wl_list *part_list = &ssd->extents.parts; int border_width = MAX(0, MAX(rc.resize_minimum_area, theme->border_width)); @@ -22,67 +34,19 @@ ssd_extents_create(struct ssd *ssd) if (view->fullscreen || view->maximized == VIEW_AXIS_BOTH) { wlr_scene_node_set_enabled(&parent->node, false); } + wl_list_init(&ssd->extents.parts); wlr_scene_node_set_position(&parent->node, -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); + add_extent(part_list, LAB_SSD_PART_TOP, parent); + add_extent(part_list, LAB_SSD_PART_LEFT, parent); + add_extent(part_list, LAB_SSD_PART_RIGHT, parent); + add_extent(part_list, LAB_SSD_PART_BOTTOM, parent); /* Initial manual update to keep X11 applications happy */ ssd_extents_update(ssd); } -static void -resize_extent_within_usable(struct wlr_scene_rect *rect, - pixman_region32_t *usable, int x, int y, int w, int h) -{ - pixman_region32_t intersection; - pixman_region32_init(&intersection); - /* Constrain part to output->usable_area */ - pixman_region32_intersect_rect(&intersection, usable, x, y, w, h); - int nrects; - const pixman_box32_t *inter_rects = - pixman_region32_rectangles(&intersection, &nrects); - - if (nrects == 0) { - /* Not visible */ - wlr_scene_node_set_enabled(&rect->node, false); - goto out; - } - - /* - * For each edge, the invisible grab area is resized - * to not cover layer-shell clients such as panels. - * However, only one resize operation is used per edge, - * so if a window is in the unlikely position that it - * is near a panel but also overspills onto another screen, - * the invisible grab-area on the other screen would be - * smaller than would normally be the case. - * - * Thus only use the first intersecting rect, this is - * a compromise as it doesn't require us to create - * multiple scene rects for a given extent edge - * and still works in 95% of the cases. - */ - struct wlr_box result_box = (struct wlr_box) { - .x = inter_rects[0].x1, - .y = inter_rects[0].y1, - .width = inter_rects[0].x2 - inter_rects[0].x1, - .height = inter_rects[0].y2 - inter_rects[0].y1 - }; - - wlr_scene_node_set_enabled(&rect->node, true); - - wlr_scene_node_set_position(&rect->node, result_box.x, result_box.y); - wlr_scene_rect_set_size(rect, result_box.width, result_box.height); - -out: - pixman_region32_fini(&intersection); -} - void ssd_extents_update(struct ssd *ssd) { @@ -108,6 +72,12 @@ ssd_extents_update(struct ssd *ssd) int border_width = MAX(rc.resize_minimum_area, theme->border_width); int extended_area = MAX(0, rc.resize_minimum_area - theme->border_width); + struct wlr_box part_box; + struct wlr_box result_box; + struct ssd_part *part; + struct wlr_scene_rect *rect; + struct wlr_box target; + /* Make sure we update the y offset based on titlebar shown / hidden */ wlr_scene_node_set_position(&ssd->extents.tree->node, -border_width, -(ssd->titlebar.height + border_width)); @@ -116,8 +86,11 @@ ssd_extents_update(struct ssd *ssd) * Convert all output usable areas that the * view is currently on into a pixman region */ + int nrects; pixman_region32_t usable; + pixman_region32_t intersection; pixman_region32_init(&usable); + pixman_region32_init(&intersection); struct output *output; wl_list_for_each(output, &view->server->outputs, link) { if (!view_on_output(view, output)) { @@ -129,24 +102,103 @@ ssd_extents_update(struct ssd *ssd) usable_area.y, usable_area.width, usable_area.height); } - /* Move usable area to the coordinate system of extents */ + /* Remember base layout coordinates */ int base_x, base_y; wlr_scene_node_coords(&ssd->extents.tree->node, &base_x, &base_y); - pixman_region32_translate(&usable, -base_x, -base_y); - resize_extent_within_usable(ssd->extents.top, &usable, - 0, 0, - full_width + extended_area * 2, extended_area); - resize_extent_within_usable(ssd->extents.left, &usable, - 0, extended_area, - extended_area, full_height); - resize_extent_within_usable(ssd->extents.right, &usable, - extended_area + full_width, extended_area, - extended_area, full_height); - resize_extent_within_usable(ssd->extents.bottom, &usable, - 0, extended_area + full_height, - full_width + extended_area * 2, extended_area); + wl_list_for_each(part, &ssd->extents.parts, link) { + rect = wlr_scene_rect_from_node(part->node); + switch (part->type) { + case LAB_SSD_PART_TOP: + target.x = 0; + target.y = 0; + target.width = full_width + extended_area * 2; + target.height = extended_area; + break; + case LAB_SSD_PART_LEFT: + target.x = 0; + target.y = extended_area; + target.width = extended_area; + target.height = full_height; + break; + case LAB_SSD_PART_RIGHT: + target.x = extended_area + full_width; + target.y = extended_area; + target.width = extended_area; + target.height = full_height; + break; + case LAB_SSD_PART_BOTTOM: + target.x = 0; + target.y = extended_area + full_height; + target.width = full_width + extended_area * 2; + target.height = extended_area; + break; + default: + /* not reached */ + assert(false); + /* suppress warnings with NDEBUG */ + target = (struct wlr_box){0}; + } + /* Get layout geometry of what the part *should* be */ + part_box.x = base_x + target.x; + part_box.y = base_y + target.y; + part_box.width = target.width; + part_box.height = target.height; + + /* Constrain part to output->usable_area */ + pixman_region32_clear(&intersection); + pixman_region32_intersect_rect(&intersection, &usable, + part_box.x, part_box.y, part_box.width, part_box.height); + const pixman_box32_t *inter_rects = + pixman_region32_rectangles(&intersection, &nrects); + + if (nrects == 0) { + /* Not visible */ + wlr_scene_node_set_enabled(part->node, false); + continue; + } + + /* + * For each edge, the invisible grab area is resized + * to not cover layer-shell clients such as panels. + * However, only one resize operation is used per edge, + * so if a window is in the unlikely position that it + * is near a panel but also overspills onto another screen, + * the invisible grab-area on the other screen would be + * smaller than would normally be the case. + * + * Thus only use the first intersecting rect, this is + * a compromise as it doesn't require us to create + * multiple scene rects for a given extent edge + * and still works in 95% of the cases. + */ + result_box = (struct wlr_box) { + .x = inter_rects[0].x1, + .y = inter_rects[0].y1, + .width = inter_rects[0].x2 - inter_rects[0].x1, + .height = inter_rects[0].y2 - inter_rects[0].y1 + }; + + if (!part->node->enabled) { + wlr_scene_node_set_enabled(part->node, true); + } + + if (part_box.width != result_box.width + || part_box.height != result_box.height) { + /* Partly visible */ + wlr_scene_rect_set_size(rect, result_box.width, + result_box.height); + wlr_scene_node_set_position(part->node, + target.x + (result_box.x - part_box.x), + target.y + (result_box.y - part_box.y)); + } else { + /* Fully visible */ + wlr_scene_node_set_position(part->node, target.x, target.y); + wlr_scene_rect_set_size(rect, target.width, target.height); + } + } + pixman_region32_fini(&intersection); pixman_region32_fini(&usable); } @@ -157,6 +209,7 @@ ssd_extents_destroy(struct ssd *ssd) return; } + ssd_destroy_parts(&ssd->extents.parts); wlr_scene_node_destroy(&ssd->extents.tree->node); - ssd->extents = (struct ssd_extents_scene){0}; + ssd->extents.tree = NULL; } diff --git a/src/ssd/ssd-part.c b/src/ssd/ssd-part.c new file mode 100644 index 00000000..1efc7cad --- /dev/null +++ b/src/ssd/ssd-part.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include "buffer.h" +#include "common/box.h" +#include "common/list.h" +#include "common/mem.h" +#include "common/scaled-icon-buffer.h" +#include "common/scaled-img-buffer.h" +#include "labwc.h" +#include "node.h" +#include "ssd-internal.h" + +/* Internal helpers */ +static void +handle_button_node_destroy(struct wl_listener *listener, void *data) +{ + struct ssd_button *button = wl_container_of(listener, button, destroy); + wl_list_remove(&button->destroy.link); + free(button); +} + +/* + * Create a new node_descriptor containing a link to a new ssd_button struct. + * Both will be destroyed automatically once the scene_node they are attached + * to is destroyed. + */ +static struct ssd_button * +ssd_button_descriptor_create(struct wlr_scene_node *node) +{ + /* Create new ssd_button */ + struct ssd_button *button = znew(*button); + + /* Let it destroy automatically when the scene node destroys */ + button->destroy.notify = handle_button_node_destroy; + wl_signal_add(&node->events.destroy, &button->destroy); + + /* And finally attach the ssd_button to a node descriptor */ + node_descriptor_create(node, LAB_NODE_DESC_SSD_BUTTON, button); + return button; +} + +/* Internal API */ +struct ssd_part * +add_scene_part(struct wl_list *part_list, enum ssd_part_type type) +{ + struct ssd_part *part = znew(*part); + part->type = type; + wl_list_append(part_list, &part->link); + return part; +} + +struct ssd_part * +add_scene_rect(struct wl_list *list, enum ssd_part_type type, + struct wlr_scene_tree *parent, int width, int height, + int x, int y, float color[4]) +{ + assert(width >= 0 && height >= 0); + struct ssd_part *part = add_scene_part(list, type); + part->node = &wlr_scene_rect_create( + parent, width, height, color)->node; + wlr_scene_node_set_position(part->node, x, y); + return part; +} + +struct ssd_part * +add_scene_buffer(struct wl_list *list, enum ssd_part_type type, + struct wlr_scene_tree *parent, struct wlr_buffer *buffer, + int x, int y) +{ + struct ssd_part *part = add_scene_part(list, type); + part->node = &wlr_scene_buffer_create(parent, buffer)->node; + wlr_scene_node_set_position(part->node, x, y); + return part; +} + +struct ssd_part * +add_scene_button(struct wl_list *part_list, enum ssd_part_type type, + struct wlr_scene_tree *parent, + struct lab_img *imgs[LAB_BS_ALL + 1], + int x, int y, struct view *view) +{ + struct ssd_part *button_root = add_scene_part(part_list, type); + parent = wlr_scene_tree_create(parent); + button_root->node = &parent->node; + wlr_scene_node_set_position(button_root->node, x, y); + + struct ssd_button *button = ssd_button_descriptor_create(button_root->node); + button->type = type; + button->view = view; + + /* Hitbox */ + float invisible[4] = { 0, 0, 0, 0 }; + add_scene_rect(part_list, type, parent, + rc.theme->window_button_width, rc.theme->window_button_height, 0, 0, + invisible); + + /* Icons */ + int button_width = rc.theme->window_button_width; + int button_height = rc.theme->window_button_height; + /* + * Ensure a small amount of horizontal padding within the button + * area (2px on each side with the default 26px button width). + * A new theme setting could be added to configure this. Using + * an existing setting (padding.width or window.button.spacing) + * was considered, but these settings have distinct purposes + * already and are zero by default. + */ + int icon_padding = button_width / 10; + + if (type == LAB_SSD_BUTTON_WINDOW_ICON) { + struct ssd_part *icon_part = add_scene_part(part_list, type); + struct scaled_icon_buffer *icon_buffer = + scaled_icon_buffer_create(parent, view->server, + button_width - 2 * icon_padding, button_height); + scaled_icon_buffer_set_view(icon_buffer, view); + assert(icon_buffer); + icon_part->node = &icon_buffer->scene_buffer->node; + wlr_scene_node_set_position(icon_part->node, icon_padding, 0); + button->window_icon = icon_buffer; + } else { + for (uint8_t state_set = LAB_BS_DEFAULT; + state_set <= LAB_BS_ALL; state_set++) { + if (!imgs[state_set]) { + continue; + } + struct ssd_part *icon_part = add_scene_part(part_list, type); + struct scaled_img_buffer *img_buffer = scaled_img_buffer_create( + parent, imgs[state_set], rc.theme->window_button_width, + rc.theme->window_button_height); + assert(img_buffer); + icon_part->node = &img_buffer->scene_buffer->node; + wlr_scene_node_set_enabled(icon_part->node, false); + button->img_buffers[state_set] = img_buffer; + } + /* Initially show non-hover, non-toggled, unrounded variant */ + wlr_scene_node_set_enabled( + &button->img_buffers[LAB_BS_DEFAULT]->scene_buffer->node, true); + } + + return button_root; +} + +struct ssd_part * +ssd_get_part(struct wl_list *part_list, enum ssd_part_type type) +{ + struct ssd_part *part; + wl_list_for_each(part, part_list, link) { + if (part->type == type) { + return part; + } + } + return NULL; +} + +void +ssd_destroy_parts(struct wl_list *list) +{ + struct ssd_part *part, *tmp; + wl_list_for_each_reverse_safe(part, tmp, list, link) { + if (part->node) { + wlr_scene_node_destroy(part->node); + part->node = NULL; + } + /* part->buffer will free itself along the scene_buffer node */ + part->buffer = NULL; + wl_list_remove(&part->link); + free(part); + } + assert(wl_list_empty(list)); +} diff --git a/src/ssd/ssd-shadow.c b/src/ssd/ssd-shadow.c index 2ead17ce..29ee1ca2 100644 --- a/src/ssd/ssd-shadow.c +++ b/src/ssd/ssd-shadow.c @@ -1,14 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-only #include -#include -#include "buffer.h" -#include "config/rcxml.h" +#include "common/scene-helpers.h" #include "labwc.h" -#include "ssd.h" +#include "buffer.h" #include "ssd-internal.h" #include "theme.h" #include "view.h" +#include + +#define FOR_EACH_STATE(ssd, tmp) FOR_EACH(tmp, \ + &(ssd)->shadow.active, \ + &(ssd)->shadow.inactive) /* * Implements point_accepts_input for a buffer which never accepts input @@ -46,10 +49,12 @@ corner_scale_crop(struct wlr_scene_buffer *buffer, int horizontal_overlap, * drop-shadow. */ static void -set_shadow_parts_geometry(struct ssd_shadow_subtree *subtree, - int width, int height, int titlebar_height, int corner_size, - int inset, int visible_shadow_width) +set_shadow_part_geometry(struct ssd_part *part, int width, int height, + int titlebar_height, int corner_size, int inset, + int visible_shadow_width) { + struct wlr_scene_buffer *scene_buf = + wlr_scene_buffer_from_node(part->node); /* * If the shadow inset is greater than half the overall window height * or width (eg. because the window is shaded or because we have a @@ -75,69 +80,83 @@ set_shadow_parts_geometry(struct ssd_shadow_subtree *subtree, * the top-left and bottom-right corners one pixel wider (if the width * is odd) or taller (if the height is odd). */ - int horizontal_overlap_downsized = horizontal_overlap; - if (horizontal_overlap > 0) { - horizontal_overlap_downsized -= width % 2; - } - int vertical_overlap_downsized = vertical_overlap; - if (vertical_overlap > 0) { - vertical_overlap_downsized -= height % 2; + if (part->type == LAB_SSD_PART_CORNER_TOP_LEFT + || part->type == LAB_SSD_PART_CORNER_BOTTOM_RIGHT) { + if (horizontal_overlap > 0) { + horizontal_overlap -= width % 2; + } + if (vertical_overlap > 0) { + vertical_overlap -= height % 2; + } } int x; int y; - x = width - inset + horizontal_overlap_downsized; - y = -titlebar_height + height - inset + vertical_overlap_downsized; - wlr_scene_node_set_position(&subtree->bottom_right->node, x, y); - corner_scale_crop(subtree->bottom_right, horizontal_overlap_downsized, - vertical_overlap_downsized, corner_size); - - x = -visible_shadow_width; - y = -titlebar_height + height - inset + vertical_overlap; - wlr_scene_node_set_position(&subtree->bottom_left->node, x, y); - corner_scale_crop(subtree->bottom_left, horizontal_overlap, - vertical_overlap, corner_size); - - x = -visible_shadow_width; - y = -titlebar_height - visible_shadow_width; - wlr_scene_node_set_position(&subtree->top_left->node, x, y); - corner_scale_crop(subtree->top_left, horizontal_overlap_downsized, - vertical_overlap_downsized, corner_size); - - x = width - inset + horizontal_overlap; - y = -titlebar_height - visible_shadow_width; - wlr_scene_node_set_position(&subtree->top_right->node, x, y); - corner_scale_crop(subtree->top_right, horizontal_overlap, - vertical_overlap, corner_size); - - x = width; - y = -titlebar_height + inset; - wlr_scene_node_set_position(&subtree->right->node, x, y); - wlr_scene_buffer_set_dest_size(subtree->right, - visible_shadow_width, MAX(height - 2 * inset, 0)); - wlr_scene_node_set_enabled(&subtree->right->node, show_sides); - - x = inset; - y = -titlebar_height + height; - wlr_scene_node_set_position(&subtree->bottom->node, x, y); - wlr_scene_buffer_set_dest_size(subtree->bottom, - MAX(width - 2 * inset, 0), visible_shadow_width); - wlr_scene_node_set_enabled(&subtree->bottom->node, show_topbottom); - - x = -visible_shadow_width; - y = -titlebar_height + inset; - wlr_scene_node_set_position(&subtree->left->node, x, y); - wlr_scene_buffer_set_dest_size(subtree->left, - visible_shadow_width, MAX(height - 2 * inset, 0)); - wlr_scene_node_set_enabled(&subtree->left->node, show_sides); - - x = inset; - y = -titlebar_height - visible_shadow_width; - wlr_scene_node_set_position(&subtree->top->node, x, y); - wlr_scene_buffer_set_dest_size(subtree->top, - MAX(width - 2 * inset, 0), visible_shadow_width); - wlr_scene_node_set_enabled(&subtree->top->node, show_topbottom); + switch (part->type) { + case LAB_SSD_PART_CORNER_BOTTOM_RIGHT: + x = width - inset + horizontal_overlap; + y = -titlebar_height + height - inset + vertical_overlap; + wlr_scene_node_set_position(part->node, x, y); + corner_scale_crop(scene_buf, horizontal_overlap, + vertical_overlap, corner_size); + break; + case LAB_SSD_PART_CORNER_BOTTOM_LEFT: + x = -visible_shadow_width; + y = -titlebar_height + height - inset + vertical_overlap; + wlr_scene_node_set_position(part->node, x, y); + corner_scale_crop(scene_buf, horizontal_overlap, + vertical_overlap, corner_size); + break; + case LAB_SSD_PART_CORNER_TOP_LEFT: + x = -visible_shadow_width; + y = -titlebar_height - visible_shadow_width; + wlr_scene_node_set_position(part->node, x, y); + corner_scale_crop(scene_buf, horizontal_overlap, + vertical_overlap, corner_size); + break; + case LAB_SSD_PART_CORNER_TOP_RIGHT: + x = width - inset + horizontal_overlap; + y = -titlebar_height - visible_shadow_width; + wlr_scene_node_set_position(part->node, x, y); + corner_scale_crop(scene_buf, horizontal_overlap, + vertical_overlap, corner_size); + break; + case LAB_SSD_PART_RIGHT: + x = width; + y = -titlebar_height + inset; + wlr_scene_node_set_position(part->node, x, y); + wlr_scene_buffer_set_dest_size( + scene_buf, visible_shadow_width, MAX(height - 2 * inset, 0)); + wlr_scene_node_set_enabled(part->node, show_sides); + break; + case LAB_SSD_PART_BOTTOM: + x = inset; + y = -titlebar_height + height; + wlr_scene_node_set_position(part->node, x, y); + wlr_scene_buffer_set_dest_size( + scene_buf, MAX(width - 2 * inset, 0), visible_shadow_width); + wlr_scene_node_set_enabled(part->node, show_topbottom); + break; + case LAB_SSD_PART_LEFT: + x = -visible_shadow_width; + y = -titlebar_height + inset; + wlr_scene_node_set_position(part->node, x, y); + wlr_scene_buffer_set_dest_size( + scene_buf, visible_shadow_width, MAX(height - 2 * inset, 0)); + wlr_scene_node_set_enabled(part->node, show_sides); + break; + case LAB_SSD_PART_TOP: + x = inset; + y = -titlebar_height - visible_shadow_width; + wlr_scene_node_set_position(part->node, x, y); + wlr_scene_buffer_set_dest_size( + scene_buf, MAX(width - 2 * inset, 0), visible_shadow_width); + wlr_scene_node_set_enabled(part->node, show_topbottom); + break; + default: + break; + } } static void @@ -149,14 +168,17 @@ set_shadow_geometry(struct ssd *ssd) int width = view->current.width; int height = view_effective_height(view, false) + titlebar_height; - enum ssd_active_state active; - FOR_EACH_ACTIVE_STATE(active) { - struct ssd_shadow_subtree *subtree = &ssd->shadow.subtrees[active]; + struct ssd_part *part; + struct ssd_sub_tree *subtree; + + FOR_EACH_STATE(ssd, subtree) { if (!subtree->tree) { /* Looks like this type of shadow is disabled */ continue; } + int active = (subtree == &ssd->shadow.active) ? + THEME_ACTIVE : THEME_INACTIVE; int visible_shadow_width = theme->window[active].shadow_size; /* inset as a proportion of shadow width */ double inset_proportion = SSD_SHADOW_INSET; @@ -171,19 +193,23 @@ set_shadow_geometry(struct ssd *ssd) int corner_size = theme->window[active].shadow_corner_top->logical_height; - set_shadow_parts_geometry(subtree, width, height, - titlebar_height, corner_size, inset, - visible_shadow_width); - } + wl_list_for_each(part, &subtree->parts, link) { + set_shadow_part_geometry(part, width, height, + titlebar_height, corner_size, inset, + visible_shadow_width); + } + } FOR_EACH_END } -static struct wlr_scene_buffer * -make_shadow(struct view *view, +static void +make_shadow(struct wl_list *parts, enum ssd_part_type type, struct wlr_scene_tree *parent, struct wlr_buffer *buf, enum wl_output_transform tx) { + struct ssd_part *part = add_scene_buffer( + parts, type, parent, buf, 0, 0); struct wlr_scene_buffer *scene_buf = - wlr_scene_buffer_create(parent, buf); + wlr_scene_buffer_from_node(part->node); wlr_scene_buffer_set_transform(scene_buf, tx); scene_buf->point_accepts_input = never_accepts_input; /* @@ -191,7 +217,6 @@ make_shadow(struct view *view, * pixel wide/tall. Use nearest-neighbour scaling to workaround. */ scene_buf->filter_mode = WLR_SCALE_FILTER_NEAREST; - return scene_buf; } void @@ -203,47 +228,51 @@ ssd_shadow_create(struct ssd *ssd) ssd->shadow.tree = wlr_scene_tree_create(ssd->tree); struct theme *theme = ssd->view->server->theme; - struct view *view = ssd->view; + struct wlr_buffer *corner_top_buffer; + struct wlr_buffer *corner_bottom_buffer; + struct wlr_buffer *edge_buffer; + struct ssd_sub_tree *subtree; + struct wlr_scene_tree *parent; + int active; - enum ssd_active_state active; - FOR_EACH_ACTIVE_STATE(active) { - struct ssd_shadow_subtree *subtree = &ssd->shadow.subtrees[active]; + FOR_EACH_STATE(ssd, subtree) { + wl_list_init(&subtree->parts); if (!rc.shadows_enabled) { /* Shadows are globally disabled */ continue; } + active = (subtree == &ssd->shadow.active) ? + THEME_ACTIVE : THEME_INACTIVE; if (theme->window[active].shadow_size == 0) { /* Window shadows are disabled */ continue; } subtree->tree = 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; - struct wlr_buffer *corner_bottom_buffer = - &theme->window[active].shadow_corner_bottom->base; - struct wlr_buffer *edge_buffer = - &theme->window[active].shadow_edge->base; + parent = subtree->tree; + corner_top_buffer = &theme->window[active].shadow_corner_top->base; + corner_bottom_buffer = &theme->window[active].shadow_corner_bottom->base; + edge_buffer = &theme->window[active].shadow_edge->base; - subtree->bottom_right = make_shadow(view, parent, - corner_bottom_buffer, WL_OUTPUT_TRANSFORM_NORMAL); - subtree->bottom_left = make_shadow(view, parent, - corner_bottom_buffer, WL_OUTPUT_TRANSFORM_FLIPPED); - subtree->top_left = make_shadow(view, parent, - corner_top_buffer, WL_OUTPUT_TRANSFORM_180); - subtree->top_right = make_shadow(view, parent, - corner_top_buffer, WL_OUTPUT_TRANSFORM_FLIPPED_180); - subtree->right = make_shadow(view, parent, + make_shadow(&subtree->parts, LAB_SSD_PART_CORNER_BOTTOM_RIGHT, + parent, corner_bottom_buffer, WL_OUTPUT_TRANSFORM_NORMAL); + make_shadow(&subtree->parts, LAB_SSD_PART_CORNER_BOTTOM_LEFT, + parent, corner_bottom_buffer, WL_OUTPUT_TRANSFORM_FLIPPED); + make_shadow(&subtree->parts, LAB_SSD_PART_CORNER_TOP_LEFT, + parent, corner_top_buffer, WL_OUTPUT_TRANSFORM_180); + make_shadow(&subtree->parts, LAB_SSD_PART_CORNER_TOP_RIGHT, + parent, corner_top_buffer, WL_OUTPUT_TRANSFORM_FLIPPED_180); + make_shadow(&subtree->parts, LAB_SSD_PART_RIGHT, parent, edge_buffer, WL_OUTPUT_TRANSFORM_NORMAL); - subtree->bottom = make_shadow(view, parent, + make_shadow(&subtree->parts, LAB_SSD_PART_BOTTOM, parent, edge_buffer, WL_OUTPUT_TRANSFORM_90); - subtree->left = make_shadow(view, parent, + make_shadow(&subtree->parts, LAB_SSD_PART_LEFT, parent, edge_buffer, WL_OUTPUT_TRANSFORM_180); - subtree->top = make_shadow(view, parent, + make_shadow(&subtree->parts, LAB_SSD_PART_TOP, parent, edge_buffer, WL_OUTPUT_TRANSFORM_270); - } + + } FOR_EACH_END ssd_shadow_update(ssd); } @@ -259,8 +288,8 @@ ssd_shadow_update(struct ssd *ssd) bool maximized = view->maximized == VIEW_AXIS_BOTH; bool tiled_shadows = false; if (rc.shadows_on_tiled) { - if (rc.gap >= theme->window[SSD_ACTIVE].shadow_size - && rc.gap >= theme->window[SSD_INACTIVE].shadow_size) { + if (rc.gap >= theme->window[THEME_ACTIVE].shadow_size + && rc.gap >= theme->window[THEME_INACTIVE].shadow_size) { tiled_shadows = true; } else { wlr_log(WLR_INFO, "gap size < shadow_size, ignore rc.shadows_ontiled"); @@ -280,6 +309,16 @@ ssd_shadow_destroy(struct ssd *ssd) assert(ssd); assert(ssd->shadow.tree); + struct ssd_sub_tree *subtree; + FOR_EACH_STATE(ssd, subtree) { + ssd_destroy_parts(&subtree->parts); + /* + * subtree->tree will be destroyed when its + * parent (ssd->shadow.tree) is destroyed. + */ + subtree->tree = NULL; + } FOR_EACH_END + wlr_scene_node_destroy(&ssd->shadow.tree->node); - ssd->shadow = (struct ssd_shadow_scene){0}; + ssd->shadow.tree = NULL; } diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 09f5362a..d44adf41 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -4,23 +4,28 @@ #include #include #include -#include #include "buffer.h" +#include "config.h" #include "common/mem.h" +#include "common/scaled-font-buffer.h" +#include "common/scaled-icon-buffer.h" +#include "common/scaled-img-buffer.h" +#include "common/scene-helpers.h" #include "common/string-helpers.h" -#include "config/rcxml.h" +#include "desktop-entry.h" +#include "img/img.h" #include "labwc.h" #include "node.h" -#include "scaled-buffer/scaled-font-buffer.h" -#include "scaled-buffer/scaled-icon-buffer.h" -#include "scaled-buffer/scaled-img-buffer.h" -#include "ssd.h" #include "ssd-internal.h" #include "theme.h" #include "view.h" +#define FOR_EACH_STATE(ssd, tmp) FOR_EACH(tmp, \ + &(ssd)->titlebar.active, \ + &(ssd)->titlebar.inactive) + static void set_squared_corners(struct ssd *ssd, bool enable); -static void set_alt_button_icon(struct ssd *ssd, enum lab_node_type type, bool enable); +static void set_alt_button_icon(struct ssd *ssd, enum ssd_part_type type, bool enable); static void update_visible_buttons(struct ssd *ssd); void @@ -31,27 +36,30 @@ ssd_titlebar_create(struct ssd *ssd) int width = view->current.width; int corner_width = ssd_get_corner_width(); - ssd->titlebar.tree = wlr_scene_tree_create(ssd->tree); - node_descriptor_create(&ssd->titlebar.tree->node, - LAB_NODE_TITLEBAR, view, /*data*/ NULL); + struct wlr_scene_tree *parent; + struct wlr_buffer *titlebar_fill; + struct wlr_buffer *corner_top_left; + struct wlr_buffer *corner_top_right; + int active; - enum ssd_active_state active; - FOR_EACH_ACTIVE_STATE(active) { - struct ssd_titlebar_subtree *subtree = &ssd->titlebar.subtrees[active]; + ssd->titlebar.tree = wlr_scene_tree_create(ssd->tree); + + struct ssd_sub_tree *subtree; + FOR_EACH_STATE(ssd, subtree) { subtree->tree = wlr_scene_tree_create(ssd->titlebar.tree); - struct wlr_scene_tree *parent = subtree->tree; + parent = subtree->tree; + active = (subtree == &ssd->titlebar.active) ? + THEME_ACTIVE : THEME_INACTIVE; + titlebar_fill = &theme->window[active].titlebar_fill->base; + corner_top_left = &theme->window[active].corner_top_left_normal->base; + corner_top_right = &theme->window[active].corner_top_right_normal->base; wlr_scene_node_set_enabled(&parent->node, active); wlr_scene_node_set_position(&parent->node, 0, -theme->titlebar_height); - - struct wlr_buffer *titlebar_fill = - &theme->window[active].titlebar_fill->base; - struct wlr_buffer *corner_top_left = - &theme->window[active].corner_top_left_normal->base; - struct wlr_buffer *corner_top_right = - &theme->window[active].corner_top_right_normal->base; + wl_list_init(&subtree->parts); /* Background */ - subtree->bar = wlr_scene_buffer_create(parent, titlebar_fill); + struct wlr_scene_buffer *bg_scene_buffer = + wlr_scene_buffer_create(parent, titlebar_fill); /* * Work around the wlroots/pixman bug that widened 1px buffer * becomes translucent when bilinear filtering is used. @@ -60,54 +68,43 @@ ssd_titlebar_create(struct ssd *ssd) */ if (wlr_renderer_is_pixman(view->server->renderer)) { wlr_scene_buffer_set_filter_mode( - subtree->bar, WLR_SCALE_FILTER_NEAREST); + bg_scene_buffer, WLR_SCALE_FILTER_NEAREST); } - wlr_scene_node_set_position(&subtree->bar->node, corner_width, 0); + struct ssd_part *bg_part = + add_scene_part(&subtree->parts, LAB_SSD_PART_TITLEBAR); + bg_part->node = &bg_scene_buffer->node; + wlr_scene_node_set_position(bg_part->node, corner_width, 0); - subtree->corner_left = 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); - wlr_scene_node_set_position(&subtree->corner_right->node, - width - corner_width, -rc.theme->border_width); - - /* Title */ - subtree->title = scaled_font_buffer_create_for_titlebar( - subtree->tree, theme->titlebar_height, - theme->window[active].titlebar_pattern); - assert(subtree->title); - node_descriptor_create(&subtree->title->scene_buffer->node, - LAB_NODE_TITLE, view, /*data*/ NULL); + add_scene_buffer(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_LEFT, parent, + corner_top_left, -rc.theme->border_width, -rc.theme->border_width); + add_scene_buffer(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_RIGHT, parent, + corner_top_right, width - corner_width, + -rc.theme->border_width); /* Buttons */ + struct title_button *b; int x = theme->window_titlebar_padding_width; /* Center vertically within titlebar */ int y = (theme->titlebar_height - theme->window_button_height) / 2; - wl_list_init(&subtree->buttons_left); - wl_list_init(&subtree->buttons_right); - - for (int b = 0; b < rc.nr_title_buttons_left; b++) { - enum lab_node_type type = rc.title_buttons_left[b]; + wl_list_for_each(b, &rc.title_buttons_left, link) { struct lab_img **imgs = - theme->window[active].button_imgs[type]; - attach_ssd_button(&subtree->buttons_left, type, parent, + theme->window[active].button_imgs[b->type]; + add_scene_button(&subtree->parts, b->type, parent, imgs, x, y, view); x += theme->window_button_width + theme->window_button_spacing; } x = width - theme->window_titlebar_padding_width + theme->window_button_spacing; - for (int b = rc.nr_title_buttons_right - 1; b >= 0; b--) { + wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { x -= theme->window_button_width + theme->window_button_spacing; - enum lab_node_type type = rc.title_buttons_right[b]; struct lab_img **imgs = - theme->window[active].button_imgs[type]; - attach_ssd_button(&subtree->buttons_right, type, parent, + theme->window[active].button_imgs[b->type]; + add_scene_button(&subtree->parts, b->type, parent, imgs, x, y, view); } - } + } FOR_EACH_END update_visible_buttons(ssd); @@ -116,7 +113,7 @@ ssd_titlebar_create(struct ssd *ssd) bool maximized = view->maximized == VIEW_AXIS_BOTH; bool squared = ssd_should_be_squared(ssd); if (maximized) { - set_alt_button_icon(ssd, LAB_NODE_BUTTON_MAXIMIZE, true); + set_alt_button_icon(ssd, LAB_SSD_BUTTON_MAXIMIZE, true); ssd->state.was_maximized = true; } if (squared) { @@ -125,11 +122,11 @@ ssd_titlebar_create(struct ssd *ssd) set_squared_corners(ssd, maximized || squared); if (view->shaded) { - set_alt_button_icon(ssd, LAB_NODE_BUTTON_SHADE, true); + set_alt_button_icon(ssd, LAB_SSD_BUTTON_SHADE, true); } if (view->visible_on_all_workspaces) { - set_alt_button_icon(ssd, LAB_NODE_BUTTON_OMNIPRESENT, true); + set_alt_button_icon(ssd, LAB_SSD_BUTTON_OMNIPRESENT, true); } } @@ -162,54 +159,56 @@ set_squared_corners(struct ssd *ssd, bool enable) int corner_width = ssd_get_corner_width(); struct theme *theme = view->server->theme; + struct ssd_part *part; + struct ssd_sub_tree *subtree; int x = enable ? 0 : corner_width; - enum ssd_active_state active; - FOR_EACH_ACTIVE_STATE(active) { - struct ssd_titlebar_subtree *subtree = &ssd->titlebar.subtrees[active]; - - wlr_scene_node_set_position(&subtree->bar->node, x, 0); - wlr_scene_buffer_set_dest_size(subtree->bar, + FOR_EACH_STATE(ssd, subtree) { + part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR); + wlr_scene_node_set_position(part->node, x, 0); + wlr_scene_buffer_set_dest_size( + wlr_scene_buffer_from_node(part->node), MAX(width - 2 * x, 0), theme->titlebar_height); - wlr_scene_node_set_enabled(&subtree->corner_left->node, !enable); + part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_LEFT); + wlr_scene_node_set_enabled(part->node, !enable); - wlr_scene_node_set_enabled(&subtree->corner_right->node, !enable); + part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_RIGHT); + wlr_scene_node_set_enabled(part->node, !enable); /* (Un)round the corner buttons */ - struct ssd_button *button; - wl_list_for_each(button, &subtree->buttons_left, link) { + struct title_button *title_button; + wl_list_for_each(title_button, &rc.title_buttons_left, link) { + part = ssd_get_part(&subtree->parts, title_button->type); + struct ssd_button *button = node_ssd_button_from_node(part->node); update_button_state(button, LAB_BS_ROUNDED, !enable); break; } - wl_list_for_each(button, &subtree->buttons_right, link) { + wl_list_for_each_reverse(title_button, &rc.title_buttons_right, link) { + part = ssd_get_part(&subtree->parts, title_button->type); + struct ssd_button *button = node_ssd_button_from_node(part->node); update_button_state(button, LAB_BS_ROUNDED, !enable); break; } - } + } FOR_EACH_END } static void -set_alt_button_icon(struct ssd *ssd, enum lab_node_type type, bool enable) +set_alt_button_icon(struct ssd *ssd, enum ssd_part_type type, bool enable) { - enum ssd_active_state active; - FOR_EACH_ACTIVE_STATE(active) { - struct ssd_titlebar_subtree *subtree = &ssd->titlebar.subtrees[active]; + struct ssd_part *part; + struct ssd_button *button; + struct ssd_sub_tree *subtree; - struct ssd_button *button; - wl_list_for_each(button, &subtree->buttons_left, link) { - if (button->type == type) { - update_button_state(button, - LAB_BS_TOGGLED, enable); - } + FOR_EACH_STATE(ssd, subtree) { + part = ssd_get_part(&subtree->parts, type); + if (!part) { + return; } - wl_list_for_each(button, &subtree->buttons_right, link) { - if (button->type == type) { - update_button_state(button, - LAB_BS_TOGGLED, enable); - } - } - } + + button = node_ssd_button_from_node(part->node); + update_button_state(button, LAB_BS_TOGGLED, enable); + } FOR_EACH_END } /* @@ -224,8 +223,8 @@ update_visible_buttons(struct ssd *ssd) 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; - int button_count_left = rc.nr_title_buttons_left; - int button_count_right = rc.nr_title_buttons_right; + int button_count_left = wl_list_length(&rc.title_buttons_left); + int button_count_right = wl_list_length(&rc.title_buttons_right); /* Make sure infinite loop never occurs */ assert(button_width > 0); @@ -246,25 +245,27 @@ update_visible_buttons(struct ssd *ssd) } } - enum ssd_active_state active; - FOR_EACH_ACTIVE_STATE(active) { - struct ssd_titlebar_subtree *subtree = &ssd->titlebar.subtrees[active]; - int button_count = 0; - - struct ssd_button *button; - wl_list_for_each(button, &subtree->buttons_left, link) { - wlr_scene_node_set_enabled(button->node, + int button_count; + struct ssd_part *part; + struct ssd_sub_tree *subtree; + struct title_button *b; + FOR_EACH_STATE(ssd, subtree) { + button_count = 0; + wl_list_for_each(b, &rc.title_buttons_left, link) { + part = ssd_get_part(&subtree->parts, b->type); + wlr_scene_node_set_enabled(part->node, button_count < button_count_left); button_count++; } button_count = 0; - wl_list_for_each(button, &subtree->buttons_right, link) { - wlr_scene_node_set_enabled(button->node, + wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { + part = ssd_get_part(&subtree->parts, b->type); + wlr_scene_node_set_enabled(part->node, button_count < button_count_right); button_count++; } - } + } FOR_EACH_END } void @@ -282,19 +283,19 @@ ssd_titlebar_update(struct ssd *ssd) || ssd->state.was_squared != squared) { set_squared_corners(ssd, maximized || squared); if (ssd->state.was_maximized != maximized) { - set_alt_button_icon(ssd, LAB_NODE_BUTTON_MAXIMIZE, maximized); + set_alt_button_icon(ssd, LAB_SSD_BUTTON_MAXIMIZE, maximized); } ssd->state.was_maximized = maximized; ssd->state.was_squared = squared; } if (ssd->state.was_shaded != view->shaded) { - set_alt_button_icon(ssd, LAB_NODE_BUTTON_SHADE, view->shaded); + set_alt_button_icon(ssd, LAB_SSD_BUTTON_SHADE, view->shaded); ssd->state.was_shaded = view->shaded; } if (ssd->state.was_omnipresent != view->visible_on_all_workspaces) { - set_alt_button_icon(ssd, LAB_NODE_BUTTON_OMNIPRESENT, + set_alt_button_icon(ssd, LAB_SSD_BUTTON_OMNIPRESENT, view->visible_on_all_workspaces); ssd->state.was_omnipresent = view->visible_on_all_workspaces; } @@ -308,31 +309,34 @@ ssd_titlebar_update(struct ssd *ssd) /* Center buttons vertically within titlebar */ int y = (theme->titlebar_height - theme->window_button_height) / 2; int x; + struct ssd_part *part; + struct ssd_sub_tree *subtree; + struct title_button *b; int bg_offset = maximized || squared ? 0 : corner_width; - - enum ssd_active_state active; - FOR_EACH_ACTIVE_STATE(active) { - struct ssd_titlebar_subtree *subtree = &ssd->titlebar.subtrees[active]; - wlr_scene_buffer_set_dest_size(subtree->bar, + FOR_EACH_STATE(ssd, subtree) { + part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR); + wlr_scene_buffer_set_dest_size( + wlr_scene_buffer_from_node(part->node), MAX(width - bg_offset * 2, 0), theme->titlebar_height); x = theme->window_titlebar_padding_width; - struct ssd_button *button; - wl_list_for_each(button, &subtree->buttons_left, link) { - wlr_scene_node_set_position(button->node, x, y); + wl_list_for_each(b, &rc.title_buttons_left, link) { + part = ssd_get_part(&subtree->parts, b->type); + wlr_scene_node_set_position(part->node, x, y); x += theme->window_button_width + theme->window_button_spacing; } x = width - corner_width; - wlr_scene_node_set_position(&subtree->corner_right->node, - x, -rc.theme->border_width); + part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_RIGHT); + wlr_scene_node_set_position(part->node, x, -rc.theme->border_width); x = width - theme->window_titlebar_padding_width + theme->window_button_spacing; - wl_list_for_each(button, &subtree->buttons_right, link) { + wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { + part = ssd_get_part(&subtree->parts, b->type); x -= theme->window_button_width + theme->window_button_spacing; - wlr_scene_node_set_position(button->node, x, y); + wlr_scene_node_set_position(part->node, x, y); } - } + } FOR_EACH_END ssd_update_title(ssd); } @@ -344,9 +348,19 @@ ssd_titlebar_destroy(struct ssd *ssd) return; } - zfree(ssd->state.title.text); + struct ssd_sub_tree *subtree; + FOR_EACH_STATE(ssd, subtree) { + ssd_destroy_parts(&subtree->parts); + wlr_scene_node_destroy(&subtree->tree->node); + subtree->tree = NULL; + } FOR_EACH_END + + if (ssd->state.title.text) { + zfree(ssd->state.title.text); + } + wlr_scene_node_destroy(&ssd->titlebar.tree->node); - ssd->titlebar = (struct ssd_titlebar_scene){0}; + ssd->titlebar.tree = NULL; } /* @@ -369,40 +383,48 @@ ssd_update_title_positions(struct ssd *ssd, int offset_left, int offset_right) int width = view->current.width; int title_bg_width = width - offset_left - offset_right; - enum ssd_active_state active; - FOR_EACH_ACTIVE_STATE(active) { - struct ssd_titlebar_subtree *subtree = &ssd->titlebar.subtrees[active]; - struct scaled_font_buffer *title = subtree->title; - int x, y; - - x = offset_left; - y = (theme->titlebar_height - title->height) / 2; - - if (title_bg_width <= 0) { - wlr_scene_node_set_enabled(&title->scene_buffer->node, false); + int x, y; + int buffer_height, buffer_width; + struct ssd_part *part; + struct ssd_sub_tree *subtree; + FOR_EACH_STATE(ssd, subtree) { + part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLE); + if (!part || !part->node) { + /* view->surface never been mapped */ + /* Or we somehow failed to allocate a scaled titlebar buffer */ continue; } - wlr_scene_node_set_enabled(&title->scene_buffer->node, true); + + buffer_width = part->buffer ? part->buffer->width : 0; + buffer_height = part->buffer ? part->buffer->height : 0; + x = offset_left; + y = (theme->titlebar_height - buffer_height) / 2; + + if (title_bg_width <= 0) { + wlr_scene_node_set_enabled(part->node, false); + continue; + } + wlr_scene_node_set_enabled(part->node, true); if (theme->window_label_text_justify == LAB_JUSTIFY_CENTER) { - if (title->width + MAX(offset_left, offset_right) * 2 <= width) { + if (buffer_width + MAX(offset_left, offset_right) * 2 <= width) { /* Center based on the full width */ - x = (width - title->width) / 2; + x = (width - buffer_width) / 2; } else { /* * Center based on the width between the buttons. * Title jumps around once this is hit but its still * better than to hide behind the buttons on the right. */ - x += (title_bg_width - title->width) / 2; + x += (title_bg_width - buffer_width) / 2; } } else if (theme->window_label_text_justify == LAB_JUSTIFY_RIGHT) { - x += title_bg_width - title->width; + x += title_bg_width - buffer_width; } else if (theme->window_label_text_justify == LAB_JUSTIFY_LEFT) { /* TODO: maybe add some theme x padding here? */ } - wlr_scene_node_set_position(&title->scene_buffer->node, x, y); - } + wlr_scene_node_set_position(part->node, x, y); + } FOR_EACH_END } /* @@ -412,21 +434,23 @@ ssd_update_title_positions(struct ssd *ssd, int offset_left, int offset_right) static void get_title_offsets(struct ssd *ssd, int *offset_left, int *offset_right) { - struct ssd_titlebar_subtree *subtree = &ssd->titlebar.subtrees[SSD_ACTIVE]; + struct ssd_sub_tree *subtree = &ssd->titlebar.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; *offset_left = padding_width; *offset_right = padding_width; - struct ssd_button *button; - wl_list_for_each(button, &subtree->buttons_left, link) { - if (button->node->enabled) { + struct title_button *b; + wl_list_for_each(b, &rc.title_buttons_left, link) { + struct ssd_part *part = ssd_get_part(&subtree->parts, b->type); + if (part->node->enabled) { *offset_left += button_width + button_spacing; } } - wl_list_for_each(button, &subtree->buttons_right, link) { - if (button->node->enabled) { + wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { + struct ssd_part *part = ssd_get_part(&subtree->parts, b->type); + if (part->node->enabled) { *offset_right += button_width + button_spacing; } } @@ -440,25 +464,33 @@ ssd_update_title(struct ssd *ssd) } struct view *view = ssd->view; - if (string_null_or_empty(view->title)) { + char *title = (char *)view_get_string_prop(view, "title"); + if (string_null_or_empty(title)) { return; } struct theme *theme = view->server->theme; struct ssd_state_title *state = &ssd->state.title; - bool title_unchanged = state->text && !strcmp(view->title, state->text); + bool title_unchanged = state->text && !strcmp(title, state->text); + + const float *text_color; + const float bg_color[4] = {0, 0, 0, 0}; /* ignored */ + struct font *font = NULL; + struct ssd_part *part; + struct ssd_sub_tree *subtree; + struct ssd_state_title_width *dstate; + int active; int offset_left, offset_right; get_title_offsets(ssd, &offset_left, &offset_right); int title_bg_width = view->current.width - offset_left - offset_right; - enum ssd_active_state active; - FOR_EACH_ACTIVE_STATE(active) { - struct ssd_titlebar_subtree *subtree = &ssd->titlebar.subtrees[active]; - struct ssd_state_title_width *dstate = &state->dstates[active]; - const float *text_color = theme->window[active].label_text_color; - struct font *font = active ? - &rc.font_activewindow : &rc.font_inactivewindow; + FOR_EACH_STATE(ssd, subtree) { + active = (subtree == &ssd->titlebar.active) ? + THEME_ACTIVE : THEME_INACTIVE; + dstate = active ? &state->active : &state->inactive; + text_color = theme->window[active].label_text_color; + font = active ? &rc.font_activewindow : &rc.font_inactivewindow; if (title_bg_width <= 0) { dstate->truncated = true; @@ -471,42 +503,69 @@ ssd_update_title(struct ssd *ssd) continue; } - const float bg_color[4] = {0, 0, 0, 0}; /* ignored */ - scaled_font_buffer_update(subtree->title, view->title, - title_bg_width, font, - text_color, bg_color); + part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLE); + if (!part) { + /* Initialize part and wlr_scene_buffer without attaching a buffer */ + part = add_scene_part(&subtree->parts, LAB_SSD_PART_TITLE); + part->buffer = scaled_font_buffer_create_for_titlebar( + subtree->tree, theme->titlebar_height, + theme->window[active].titlebar_pattern); + if (part->buffer) { + part->node = &part->buffer->scene_buffer->node; + } else { + wlr_log(WLR_ERROR, "Failed to create title node"); + } + } + + if (part->buffer) { + scaled_font_buffer_update(part->buffer, title, + title_bg_width, font, + text_color, bg_color); + } /* And finally update the cache */ - dstate->width = subtree->title->width; + dstate->width = part->buffer ? part->buffer->width : 0; dstate->truncated = title_bg_width <= dstate->width; - } + + } FOR_EACH_END if (!title_unchanged) { - xstrdup_replace(state->text, view->title); + if (state->text) { + free(state->text); + } + state->text = xstrdup(title); } ssd_update_title_positions(ssd, offset_left, offset_right); } void -ssd_update_hovered_button(struct server *server, struct wlr_scene_node *node) +ssd_update_button_hover(struct wlr_scene_node *node, + struct ssd_hover_state *hover_state) { struct ssd_button *button = NULL; + if (!node || !node->data) { + goto disable_old_hover; + } - if (node && node->data) { - button = node_try_ssd_button_from_node(node); - if (button == server->hovered_button) { + struct node_descriptor *desc = node->data; + if (desc->type == LAB_NODE_DESC_SSD_BUTTON) { + button = node_ssd_button_from_node(node); + if (button == hover_state->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); +disable_old_hover: + if (hover_state->button) { + update_button_state(hover_state->button, LAB_BS_HOVERD, false); + hover_state->view = NULL; + hover_state->button = NULL; } - server->hovered_button = button; if (button) { - update_button_state(button, LAB_BS_HOVERED, true); + update_button_state(button, LAB_BS_HOVERD, true); + hover_state->view = button->view; + hover_state->button = button; } } @@ -520,3 +579,5 @@ ssd_should_be_squared(struct ssd *ssd) || view->current.width < corner_width * 2) && view->maximized != VIEW_AXIS_BOTH; } + +#undef FOR_EACH_STATE diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index d1381c1a..65416ca3 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -6,15 +6,11 @@ * Copyright (C) Johan Malm 2020-2021 */ -#include "ssd.h" #include #include -#include -#include #include "common/mem.h" -#include "config/rcxml.h" +#include "common/scene-helpers.h" #include "labwc.h" -#include "node.h" #include "ssd-internal.h" #include "theme.h" #include "view.h" @@ -34,7 +30,7 @@ ssd_thickness(struct view *view) * in border-only deco mode as view->ssd would only be set * after ssd_create() returns. */ - if (!view->ssd_mode || view->fullscreen) { + if (!view->ssd_enabled || view->fullscreen) { return (struct border){ 0 }; } @@ -42,7 +38,7 @@ ssd_thickness(struct view *view) if (view->maximized == VIEW_AXIS_BOTH) { struct border thickness = { 0 }; - if (view_titlebar_visible(view)) { + if (!view->ssd_titlebar_hidden) { thickness.top += theme->titlebar_height; } return thickness; @@ -50,12 +46,12 @@ ssd_thickness(struct view *view) struct border thickness = { .top = theme->titlebar_height + theme->border_width, - .right = theme->border_width, .bottom = theme->border_width, .left = theme->border_width, + .right = theme->border_width, }; - if (!view_titlebar_visible(view)) { + if (view->ssd_titlebar_hidden) { thickness.top -= theme->titlebar_height; } return thickness; @@ -87,18 +83,18 @@ ssd_max_extents(struct view *view) * (generally rc.resize_corner_range, but clipped to view size) of the view * bounds, so check the cursor against the view here. */ -enum lab_node_type -ssd_get_resizing_type(const struct ssd *ssd, struct wlr_cursor *cursor) +static enum ssd_part_type +get_resizing_type(const struct ssd *ssd, struct wlr_cursor *cursor) { struct view *view = ssd ? ssd->view : NULL; - if (!view || !cursor || !view->ssd_mode || view->fullscreen) { - return LAB_NODE_NONE; + if (!view || !cursor || !view->ssd_enabled || view->fullscreen) { + return LAB_SSD_NONE; } struct wlr_box view_box = view->current; view_box.height = view_effective_height(view, /* use_pending */ false); - if (view_titlebar_visible(view)) { + if (!view->ssd_titlebar_hidden) { /* If the titlebar is visible, consider it part of the view */ int titlebar_height = view->server->theme->titlebar_height; view_box.y -= titlebar_height; @@ -107,7 +103,7 @@ ssd_get_resizing_type(const struct ssd *ssd, struct wlr_cursor *cursor) if (wlr_box_contains_point(&view_box, cursor->x, cursor->y)) { /* A cursor in bounds of the view is never in an SSD context */ - return LAB_NODE_NONE; + return LAB_SSD_NONE; } int corner_height = MAX(0, MIN(rc.resize_corner_range, view_box.height / 2)); @@ -118,24 +114,118 @@ ssd_get_resizing_type(const struct ssd *ssd, struct wlr_cursor *cursor) bool bottom = cursor->y > view_box.y + view_box.height - corner_height; if (top && left) { - return LAB_NODE_CORNER_TOP_LEFT; + return LAB_SSD_PART_CORNER_TOP_LEFT; } else if (top && right) { - return LAB_NODE_CORNER_TOP_RIGHT; + return LAB_SSD_PART_CORNER_TOP_RIGHT; } else if (bottom && left) { - return LAB_NODE_CORNER_BOTTOM_LEFT; + return LAB_SSD_PART_CORNER_BOTTOM_LEFT; } else if (bottom && right) { - return LAB_NODE_CORNER_BOTTOM_RIGHT; + return LAB_SSD_PART_CORNER_BOTTOM_RIGHT; } else if (top) { - return LAB_NODE_BORDER_TOP; + return LAB_SSD_PART_TOP; } else if (bottom) { - return LAB_NODE_BORDER_BOTTOM; + return LAB_SSD_PART_BOTTOM; } else if (left) { - return LAB_NODE_BORDER_LEFT; + return LAB_SSD_PART_LEFT; } else if (right) { - return LAB_NODE_BORDER_RIGHT; + return LAB_SSD_PART_RIGHT; } - return LAB_NODE_NONE; + return LAB_SSD_NONE; +} + +enum ssd_part_type +ssd_get_part_type(const struct ssd *ssd, struct wlr_scene_node *node, + struct wlr_cursor *cursor) +{ + if (!node) { + return LAB_SSD_NONE; + } else if (node->type == WLR_SCENE_NODE_BUFFER + && lab_wlr_surface_from_node(node)) { + return LAB_SSD_CLIENT; + } else if (!ssd) { + return LAB_SSD_NONE; + } + + const struct wl_list *part_list = NULL; + struct wlr_scene_tree *grandparent = + node->parent ? node->parent->node.parent : NULL; + struct wlr_scene_tree *greatgrandparent = + grandparent ? grandparent->node.parent : NULL; + + /* active titlebar */ + if (node->parent == ssd->titlebar.active.tree) { + part_list = &ssd->titlebar.active.parts; + } else if (grandparent == ssd->titlebar.active.tree) { + part_list = &ssd->titlebar.active.parts; + } else if (greatgrandparent == ssd->titlebar.active.tree) { + part_list = &ssd->titlebar.active.parts; + + /* extents */ + } else if (node->parent == ssd->extents.tree) { + part_list = &ssd->extents.parts; + + /* active border */ + } else if (node->parent == ssd->border.active.tree) { + part_list = &ssd->border.active.parts; + + /* inactive titlebar */ + } else if (node->parent == ssd->titlebar.inactive.tree) { + part_list = &ssd->titlebar.inactive.parts; + } else if (grandparent == ssd->titlebar.inactive.tree) { + part_list = &ssd->titlebar.inactive.parts; + } else if (greatgrandparent == ssd->titlebar.inactive.tree) { + part_list = &ssd->titlebar.inactive.parts; + + /* inactive border */ + } else if (node->parent == ssd->border.inactive.tree) { + part_list = &ssd->border.inactive.parts; + } + + enum ssd_part_type part_type = LAB_SSD_NONE; + + if (part_list) { + struct ssd_part *part; + wl_list_for_each(part, part_list, link) { + if (node == part->node) { + part_type = part->type; + break; + } + } + } + + if (part_type == LAB_SSD_NONE) { + return part_type; + } + + /* Perform cursor-based context checks */ + enum ssd_part_type resizing_type = get_resizing_type(ssd, cursor); + return resizing_type != LAB_SSD_NONE ? resizing_type : part_type; +} + +uint32_t +ssd_resize_edges(enum ssd_part_type type) +{ + switch (type) { + case LAB_SSD_PART_TOP: + return WLR_EDGE_TOP; + case LAB_SSD_PART_RIGHT: + return WLR_EDGE_RIGHT; + case LAB_SSD_PART_BOTTOM: + return WLR_EDGE_BOTTOM; + case LAB_SSD_PART_LEFT: + return WLR_EDGE_LEFT; + case LAB_SSD_PART_CORNER_TOP_LEFT: + return WLR_EDGE_TOP | WLR_EDGE_LEFT; + case LAB_SSD_PART_CORNER_TOP_RIGHT: + return WLR_EDGE_RIGHT | WLR_EDGE_TOP; + case LAB_SSD_PART_CORNER_BOTTOM_RIGHT: + return WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT; + case LAB_SSD_PART_CORNER_BOTTOM_LEFT: + return WLR_EDGE_BOTTOM | WLR_EDGE_LEFT; + default: + return WLR_EDGE_NONE; + } } struct ssd * @@ -146,14 +236,6 @@ ssd_create(struct view *view, bool active) ssd->view = view; ssd->tree = wlr_scene_tree_create(view->scene_tree); - - /* - * Attach node_descriptor to the root node so that get_cursor_context() - * detect cursor hovering on borders and extents. - */ - node_descriptor_create(&ssd->tree->node, - 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_shadow_create(ssd); @@ -166,7 +248,7 @@ ssd_create(struct view *view, bool active) */ ssd_titlebar_create(ssd); ssd_border_create(ssd); - if (!view_titlebar_visible(view)) { + if (view->ssd_titlebar_hidden) { /* Ensure we keep the old state on Reconfigure or when exiting fullscreen */ ssd_set_titlebar(ssd, false); } @@ -228,12 +310,6 @@ ssd_update_geometry(struct ssd *ssd) || ssd->state.was_squared != squared || ssd->state.was_omnipresent != view->visible_on_all_workspaces; - /* - * (Un)maximization updates titlebar visibility with - * maximizedDecoration=none - */ - ssd_set_titlebar(ssd, view_titlebar_visible(view)); - if (update_extents) { ssd_extents_update(ssd); } @@ -272,10 +348,11 @@ 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; + struct ssd_hover_state *hover_state; + hover_state = view->server->ssd_hover_state; + if (hover_state->view == view) { + hover_state->view = NULL; + hover_state->button = NULL; } /* Destroy subcomponents */ @@ -288,7 +365,49 @@ ssd_destroy(struct ssd *ssd) free(ssd); } -enum lab_ssd_mode +bool +ssd_part_contains(enum ssd_part_type whole, enum ssd_part_type candidate) +{ + if (whole == candidate || whole == LAB_SSD_ALL) { + return true; + } + if (whole == LAB_SSD_BUTTON) { + return candidate >= LAB_SSD_BUTTON_CLOSE + && candidate <= LAB_SSD_BUTTON_OMNIPRESENT; + } + if (whole == LAB_SSD_PART_TITLEBAR) { + return candidate >= LAB_SSD_BUTTON_CLOSE + && candidate <= LAB_SSD_PART_TITLE; + } + if (whole == LAB_SSD_PART_TITLE) { + /* "Title" includes blank areas of "Titlebar" as well */ + return candidate >= LAB_SSD_PART_TITLEBAR + && candidate <= LAB_SSD_PART_TITLE; + } + if (whole == LAB_SSD_FRAME) { + return candidate >= LAB_SSD_BUTTON_CLOSE + && candidate <= LAB_SSD_CLIENT; + } + if (whole == LAB_SSD_PART_TOP) { + return candidate == LAB_SSD_PART_CORNER_TOP_LEFT + || candidate == LAB_SSD_PART_CORNER_TOP_RIGHT; + } + if (whole == LAB_SSD_PART_RIGHT) { + return candidate == LAB_SSD_PART_CORNER_TOP_RIGHT + || candidate == LAB_SSD_PART_CORNER_BOTTOM_RIGHT; + } + if (whole == LAB_SSD_PART_BOTTOM) { + return candidate == LAB_SSD_PART_CORNER_BOTTOM_RIGHT + || candidate == LAB_SSD_PART_CORNER_BOTTOM_LEFT; + } + if (whole == LAB_SSD_PART_LEFT) { + return candidate == LAB_SSD_PART_CORNER_TOP_LEFT + || candidate == LAB_SSD_PART_CORNER_BOTTOM_LEFT; + } + return false; +} + +enum ssd_mode ssd_mode_parse(const char *mode) { if (!mode) { @@ -311,19 +430,17 @@ ssd_set_active(struct ssd *ssd, bool active) if (!ssd) { return; } - enum ssd_active_state active_state; - FOR_EACH_ACTIVE_STATE(active_state) { + wlr_scene_node_set_enabled(&ssd->border.active.tree->node, active); + wlr_scene_node_set_enabled(&ssd->titlebar.active.tree->node, active); + if (ssd->shadow.active.tree) { wlr_scene_node_set_enabled( - &ssd->border.subtrees[active_state].tree->node, - active == active_state); + &ssd->shadow.active.tree->node, active); + } + wlr_scene_node_set_enabled(&ssd->border.inactive.tree->node, !active); + wlr_scene_node_set_enabled(&ssd->titlebar.inactive.tree->node, !active); + if (ssd->shadow.inactive.tree) { wlr_scene_node_set_enabled( - &ssd->titlebar.subtrees[active_state].tree->node, - active == active_state); - if (ssd->shadow.subtrees[active_state].tree) { - wlr_scene_node_set_enabled( - &ssd->shadow.subtrees[active_state].tree->node, - active == active_state); - } + &ssd->shadow.inactive.tree->node, !active); } } @@ -348,8 +465,29 @@ ssd_enable_keybind_inhibit_indicator(struct ssd *ssd, bool enable) float *color = enable ? rc.theme->window_toggled_keybinds_color - : rc.theme->window[SSD_ACTIVE].border_color; - wlr_scene_rect_set_color(ssd->border.subtrees[SSD_ACTIVE].top, color); + : rc.theme->window[THEME_ACTIVE].border_color; + + struct ssd_part *part = ssd_get_part(&ssd->border.active.parts, LAB_SSD_PART_TOP); + struct wlr_scene_rect *rect = wlr_scene_rect_from_node(part->node); + wlr_scene_rect_set_color(rect, color); +} + +struct ssd_hover_state * +ssd_hover_state_new(void) +{ + return znew(struct ssd_hover_state); +} + +enum ssd_part_type +ssd_button_get_type(const struct ssd_button *button) +{ + return button ? button->type : LAB_SSD_NONE; +} + +struct view * +ssd_button_get_view(const struct ssd_button *button) +{ + return button ? button->view : NULL; } bool @@ -370,16 +508,16 @@ ssd_debug_get_node_name(const struct ssd *ssd, struct wlr_scene_node *node) if (node == &ssd->tree->node) { return "view->ssd"; } - if (node == &ssd->titlebar.subtrees[SSD_ACTIVE].tree->node) { + if (node == &ssd->titlebar.active.tree->node) { return "titlebar.active"; } - if (node == &ssd->titlebar.subtrees[SSD_INACTIVE].tree->node) { + if (node == &ssd->titlebar.inactive.tree->node) { return "titlebar.inactive"; } - if (node == &ssd->border.subtrees[SSD_ACTIVE].tree->node) { + if (node == &ssd->border.active.tree->node) { return "border.active"; } - if (node == &ssd->border.subtrees[SSD_INACTIVE].tree->node) { + if (node == &ssd->border.inactive.tree->node) { return "border.inactive"; } if (node == &ssd->extents.tree->node) { diff --git a/src/tearing.c b/src/tearing.c index eaba51a0..50b0611c 100644 --- a/src/tearing.c +++ b/src/tearing.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -#include #include "common/mem.h" #include "labwc.h" #include "view.h" diff --git a/src/theme.c b/src/theme.c index f6684ee9..5671c195 100644 --- a/src/theme.c +++ b/src/theme.c @@ -6,7 +6,7 @@ */ #define _POSIX_C_SOURCE 200809L -#include "theme.h" +#include "config.h" #include #include #include @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -31,6 +30,7 @@ #include "config/rcxml.h" #include "img/img.h" #include "labwc.h" +#include "theme.h" #include "buffer.h" #include "ssd.h" @@ -38,13 +38,14 @@ struct button { const char *name; const char *alt_name; const char *fallback_button; /* built-in 6x6 button */ - enum lab_node_type type; + enum ssd_part_type type; uint8_t state_set; }; -enum rounded_corner { - ROUNDED_CORNER_TOP_LEFT, - ROUNDED_CORNER_TOP_RIGHT +enum corner { + LAB_CORNER_UNKNOWN = 0, + LAB_CORNER_TOP_LEFT, + LAB_CORNER_TOP_RIGHT, }; struct rounded_corner_ctx { @@ -53,7 +54,7 @@ struct rounded_corner_ctx { double line_width; cairo_pattern_t *fill_pattern; float *border_color; - enum rounded_corner corner; + enum corner corner; }; #define zero_array(arr) memset(arr, 0, sizeof(arr)) @@ -162,7 +163,7 @@ get_button_filename(char *buf, size_t len, const char *name, const char *postfix } static void -load_button(struct theme *theme, struct button *b, enum ssd_active_state active) +load_button(struct theme *theme, struct button *b, int active) { struct lab_img *(*button_imgs)[LAB_BS_ALL + 1] = theme->window[active].button_imgs; @@ -216,9 +217,9 @@ load_button(struct theme *theme, struct button *b, enum ssd_active_state active) * If hover-icons do not exist, add fallbacks by copying the non-hover * variant and then adding an overlay. */ - if (!*img && (b->state_set & LAB_BS_HOVERED)) { + if (!*img && (b->state_set & LAB_BS_HOVERD)) { struct lab_img *non_hover_img = - button_imgs[b->type][b->state_set & ~LAB_BS_HOVERED]; + button_imgs[b->type][b->state_set & ~LAB_BS_HOVERD]; *img = lab_img_copy(non_hover_img); lab_img_add_modifier(*img, draw_hover_overlay_on_button); @@ -231,16 +232,25 @@ load_button(struct theme *theme, struct button *b, enum ssd_active_state active) struct lab_img **rounded_img = &button_imgs[b->type][b->state_set | LAB_BS_ROUNDED]; - if (rc.nr_title_buttons_left > 0 - && b->type == rc.title_buttons_left[0]) { - *rounded_img = lab_img_copy(*img); - lab_img_add_modifier(*rounded_img, round_left_corner_button); + struct title_button *leftmost_button; + wl_list_for_each(leftmost_button, + &rc.title_buttons_left, link) { + if (leftmost_button->type == b->type) { + *rounded_img = lab_img_copy(*img); + lab_img_add_modifier(*rounded_img, + round_left_corner_button); + } + break; } - if (rc.nr_title_buttons_right > 0 - && b->type == rc.title_buttons_right - [rc.nr_title_buttons_right - 1]) { - *rounded_img = lab_img_copy(*img); - lab_img_add_modifier(*rounded_img, round_right_corner_button); + struct title_button *rightmost_button; + wl_list_for_each_reverse(rightmost_button, + &rc.title_buttons_right, link) { + if (rightmost_button->type == b->type) { + *rounded_img = lab_img_copy(*img); + lab_img_add_modifier(*rounded_img, + round_right_corner_button); + } + break; } } @@ -279,103 +289,103 @@ load_buttons(struct theme *theme) { struct button buttons[] = { { .name = "menu", - .fallback_button = (const char[]){ 0x00, 0x21, 0x33, 0x1E, 0x0C, 0x00 }, - .type = LAB_NODE_BUTTON_WINDOW_MENU, + .type = LAB_SSD_BUTTON_WINDOW_MENU, .state_set = 0, + .fallback_button = (const char[]){ 0x00, 0x21, 0x33, 0x1E, 0x0C, 0x00 }, }, { .name = "iconify", - .fallback_button = (const char[]){ 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f }, - .type = LAB_NODE_BUTTON_ICONIFY, + .type = LAB_SSD_BUTTON_ICONIFY, .state_set = 0, + .fallback_button = (const char[]){ 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f }, }, { .name = "max", - .fallback_button = (const char[]){ 0x3f, 0x3f, 0x21, 0x21, 0x21, 0x3f }, - .type = LAB_NODE_BUTTON_MAXIMIZE, + .type = LAB_SSD_BUTTON_MAXIMIZE, .state_set = 0, + .fallback_button = (const char[]){ 0x3f, 0x3f, 0x21, 0x21, 0x21, 0x3f }, }, { .name = "max_toggled", - .fallback_button = (const char[]){ 0x3e, 0x22, 0x2f, 0x29, 0x39, 0x0f }, - .type = LAB_NODE_BUTTON_MAXIMIZE, + .type = LAB_SSD_BUTTON_MAXIMIZE, .state_set = LAB_BS_TOGGLED, + .fallback_button = (const char[]){ 0x3e, 0x22, 0x2f, 0x29, 0x39, 0x0f }, }, { .name = "shade", - .fallback_button = (const char[]){ 0x3f, 0x3f, 0x00, 0x0c, 0x1e, 0x3f }, - .type = LAB_NODE_BUTTON_SHADE, + .type = LAB_SSD_BUTTON_SHADE, .state_set = 0, + .fallback_button = (const char[]){ 0x3f, 0x3f, 0x00, 0x0c, 0x1e, 0x3f }, }, { .name = "shade_toggled", - .fallback_button = (const char[]){ 0x3f, 0x3f, 0x00, 0x3f, 0x1e, 0x0c }, - .type = LAB_NODE_BUTTON_SHADE, + .type = LAB_SSD_BUTTON_SHADE, .state_set = LAB_BS_TOGGLED, + .fallback_button = (const char[]){ 0x3f, 0x3f, 0x00, 0x3f, 0x1e, 0x0c }, }, { .name = "desk", - .fallback_button = (const char[]){ 0x33, 0x33, 0x00, 0x00, 0x33, 0x33 }, - .type = LAB_NODE_BUTTON_OMNIPRESENT, + .type = LAB_SSD_BUTTON_OMNIPRESENT, .state_set = 0, + .fallback_button = (const char[]){ 0x33, 0x33, 0x00, 0x00, 0x33, 0x33 }, }, { .name = "desk_toggled", - .fallback_button = (const char[]){ 0x00, 0x1e, 0x1a, 0x16, 0x1e, 0x00 }, - .type = LAB_NODE_BUTTON_OMNIPRESENT, + .type = LAB_SSD_BUTTON_OMNIPRESENT, .state_set = LAB_BS_TOGGLED, + .fallback_button = (const char[]){ 0x00, 0x1e, 0x1a, 0x16, 0x1e, 0x00 }, }, { .name = "close", - .fallback_button = (const char[]){ 0x33, 0x3f, 0x1e, 0x1e, 0x3f, 0x33 }, - .type = LAB_NODE_BUTTON_CLOSE, + .type = LAB_SSD_BUTTON_CLOSE, .state_set = 0, + .fallback_button = (const char[]){ 0x33, 0x3f, 0x1e, 0x1e, 0x3f, 0x33 }, }, { .name = "menu_hover", - .type = LAB_NODE_BUTTON_WINDOW_MENU, - .state_set = LAB_BS_HOVERED, + .type = LAB_SSD_BUTTON_WINDOW_MENU, + .state_set = LAB_BS_HOVERD, /* no fallback (non-hover variant is used instead) */ }, { .name = "iconify_hover", - .type = LAB_NODE_BUTTON_ICONIFY, - .state_set = LAB_BS_HOVERED, + .type = LAB_SSD_BUTTON_ICONIFY, + .state_set = LAB_BS_HOVERD, /* no fallback (non-hover variant is used instead) */ }, { .name = "max_hover", - .type = LAB_NODE_BUTTON_MAXIMIZE, - .state_set = LAB_BS_HOVERED, + .type = LAB_SSD_BUTTON_MAXIMIZE, + .state_set = LAB_BS_HOVERD, /* no fallback (non-hover variant is used instead) */ }, { .name = "max_toggled_hover", .alt_name = "max_hover_toggled", - .type = LAB_NODE_BUTTON_MAXIMIZE, - .state_set = LAB_BS_TOGGLED | LAB_BS_HOVERED, + .type = LAB_SSD_BUTTON_MAXIMIZE, + .state_set = LAB_BS_TOGGLED | LAB_BS_HOVERD, /* no fallback (non-hover variant is used instead) */ }, { .name = "shade_hover", - .type = LAB_NODE_BUTTON_SHADE, - .state_set = LAB_BS_HOVERED, + .type = LAB_SSD_BUTTON_SHADE, + .state_set = LAB_BS_HOVERD, /* no fallback (non-hover variant is used instead) */ }, { .name = "shade_toggled_hover", .alt_name = "shade_hover_toggled", - .type = LAB_NODE_BUTTON_SHADE, - .state_set = LAB_BS_TOGGLED | LAB_BS_HOVERED, + .type = LAB_SSD_BUTTON_SHADE, + .state_set = LAB_BS_TOGGLED | LAB_BS_HOVERD, /* no fallback (non-hover variant is used instead) */ }, { .name = "desk_hover", /* no fallback (non-hover variant is used instead) */ - .type = LAB_NODE_BUTTON_OMNIPRESENT, - .state_set = LAB_BS_HOVERED, + .type = LAB_SSD_BUTTON_OMNIPRESENT, + .state_set = LAB_BS_HOVERD, }, { .name = "desk_toggled_hover", .alt_name = "desk_hover_toggled", - .type = LAB_NODE_BUTTON_OMNIPRESENT, - .state_set = LAB_BS_TOGGLED | LAB_BS_HOVERED, + .type = LAB_SSD_BUTTON_OMNIPRESENT, + .state_set = LAB_BS_TOGGLED | LAB_BS_HOVERD, /* no fallback (non-hover variant is used instead) */ }, { .name = "close_hover", - .type = LAB_NODE_BUTTON_CLOSE, - .state_set = LAB_BS_HOVERED, + .type = LAB_SSD_BUTTON_CLOSE, + .state_set = LAB_BS_HOVERD, /* no fallback (non-hover variant is used instead) */ }, }; for (size_t i = 0; i < ARRAY_SIZE(buttons); ++i) { struct button *b = &buttons[i]; - load_button(theme, b, SSD_INACTIVE); - load_button(theme, b, SSD_ACTIVE); + load_button(theme, b, THEME_INACTIVE); + load_button(theme, b, THEME_ACTIVE); } } @@ -537,24 +547,24 @@ theme_builtin(struct theme *theme, struct server *server) theme->window_titlebar_padding_height = 0; theme->window_titlebar_padding_width = 0; - parse_hexstr("#aaaaaa", theme->window[SSD_ACTIVE].border_color); - parse_hexstr("#aaaaaa", theme->window[SSD_INACTIVE].border_color); + parse_hexstr("#aaaaaa", theme->window[THEME_ACTIVE].border_color); + parse_hexstr("#aaaaaa", theme->window[THEME_INACTIVE].border_color); parse_hexstr("#ff0000", theme->window_toggled_keybinds_color); - theme->window[SSD_ACTIVE].title_bg.gradient = LAB_GRADIENT_NONE; - theme->window[SSD_INACTIVE].title_bg.gradient = LAB_GRADIENT_NONE; - parse_hexstr("#e1dedb", theme->window[SSD_ACTIVE].title_bg.color); - parse_hexstr("#f6f5f4", theme->window[SSD_INACTIVE].title_bg.color); - theme->window[SSD_ACTIVE].title_bg.color_split_to[0] = FLT_MIN; - theme->window[SSD_INACTIVE].title_bg.color_split_to[0] = FLT_MIN; - theme->window[SSD_ACTIVE].title_bg.color_to[0] = FLT_MIN; - theme->window[SSD_INACTIVE].title_bg.color_to[0] = FLT_MIN; - theme->window[SSD_ACTIVE].title_bg.color_to_split_to[0] = FLT_MIN; - theme->window[SSD_INACTIVE].title_bg.color_to_split_to[0] = FLT_MIN; + theme->window[THEME_ACTIVE].title_bg.gradient = LAB_GRADIENT_NONE; + theme->window[THEME_INACTIVE].title_bg.gradient = LAB_GRADIENT_NONE; + parse_hexstr("#e1dedb", theme->window[THEME_ACTIVE].title_bg.color); + parse_hexstr("#f6f5f4", theme->window[THEME_INACTIVE].title_bg.color); + theme->window[THEME_ACTIVE].title_bg.color_split_to[0] = FLT_MIN; + theme->window[THEME_INACTIVE].title_bg.color_split_to[0] = FLT_MIN; + theme->window[THEME_ACTIVE].title_bg.color_to[0] = FLT_MIN; + theme->window[THEME_INACTIVE].title_bg.color_to[0] = FLT_MIN; + theme->window[THEME_ACTIVE].title_bg.color_to_split_to[0] = FLT_MIN; + theme->window[THEME_INACTIVE].title_bg.color_to_split_to[0] = FLT_MIN; - parse_hexstr("#000000", theme->window[SSD_ACTIVE].label_text_color); - parse_hexstr("#000000", theme->window[SSD_INACTIVE].label_text_color); + parse_hexstr("#000000", theme->window[THEME_ACTIVE].label_text_color); + parse_hexstr("#000000", theme->window[THEME_INACTIVE].label_text_color); theme->window_label_text_justify = parse_justification("Center"); theme->window_button_width = 26; @@ -562,18 +572,18 @@ theme_builtin(struct theme *theme, struct server *server) theme->window_button_spacing = 0; theme->window_button_hover_bg_corner_radius = 0; - for (enum lab_node_type type = LAB_NODE_BUTTON_FIRST; - type <= LAB_NODE_BUTTON_LAST; type++) { + for (enum ssd_part_type type = LAB_SSD_BUTTON_FIRST; + type <= LAB_SSD_BUTTON_LAST; type++) { parse_hexstr("#000000", - theme->window[SSD_INACTIVE].button_colors[type]); + theme->window[THEME_INACTIVE].button_colors[type]); parse_hexstr("#000000", - theme->window[SSD_ACTIVE].button_colors[type]); + theme->window[THEME_ACTIVE].button_colors[type]); } - theme->window[SSD_ACTIVE].shadow_size = 60; - theme->window[SSD_INACTIVE].shadow_size = 40; - parse_hexstr("#00000060", theme->window[SSD_ACTIVE].shadow_color); - parse_hexstr("#00000040", theme->window[SSD_INACTIVE].shadow_color); + theme->window[THEME_ACTIVE].shadow_size = 60; + theme->window[THEME_INACTIVE].shadow_size = 40; + parse_hexstr("#00000060", theme->window[THEME_ACTIVE].shadow_color); + parse_hexstr("#00000040", theme->window[THEME_INACTIVE].shadow_color); theme->menu_overlap_x = 0; theme->menu_overlap_y = 0; @@ -598,26 +608,13 @@ theme_builtin(struct theme *theme, struct server *server) theme->menu_title_text_justify = parse_justification("Center"); parse_hexstr("#ffffff", theme->menu_title_text_color); - theme->osd_window_switcher_classic.width = 600; - theme->osd_window_switcher_classic.width_is_percent = false; - theme->osd_window_switcher_classic.padding = 4; - theme->osd_window_switcher_classic.item_padding_x = 10; - theme->osd_window_switcher_classic.item_padding_y = 1; - theme->osd_window_switcher_classic.item_active_border_width = 2; - theme->osd_window_switcher_classic.item_active_border_color[0] = FLT_MIN; - theme->osd_window_switcher_classic.item_active_bg_color[0] = FLT_MIN; - theme->osd_window_switcher_classic.item_icon_size = -1; - - theme->osd_window_switcher_thumbnail.max_width = 80; - theme->osd_window_switcher_thumbnail.max_width_is_percent = true; - theme->osd_window_switcher_thumbnail.padding = 4; - theme->osd_window_switcher_thumbnail.item_width = 300; - theme->osd_window_switcher_thumbnail.item_height = 250; - theme->osd_window_switcher_thumbnail.item_padding = 10; - theme->osd_window_switcher_thumbnail.item_active_border_width = 2; - theme->osd_window_switcher_thumbnail.item_active_border_color[0] = FLT_MIN; - theme->osd_window_switcher_thumbnail.item_active_bg_color[0] = FLT_MIN; - theme->osd_window_switcher_thumbnail.item_icon_size = 60; + theme->osd_window_switcher_width = 600; + theme->osd_window_switcher_width_is_percent = false; + theme->osd_window_switcher_padding = 4; + theme->osd_window_switcher_item_padding_x = 10; + theme->osd_window_switcher_item_padding_y = 1; + theme->osd_window_switcher_item_active_border_width = 2; + theme->osd_window_switcher_item_icon_size = -1; /* inherit settings in post_processing() if not set elsewhere */ theme->osd_window_switcher_preview_border_width = INT_MIN; @@ -682,11 +679,6 @@ entry(struct theme *theme, const char *key, const char *value) return; } - struct window_switcher_classic_theme *switcher_classic_theme = - &theme->osd_window_switcher_classic; - struct window_switcher_thumbnail_theme *switcher_thumb_theme = - &theme->osd_window_switcher_thumbnail; - /* * Note that in order for the pattern match to apply to more than just * the first instance, "else if" cannot be used throughout this function @@ -711,15 +703,15 @@ entry(struct theme *theme, const char *key, const char *value) } if (match_glob(key, "window.active.border.color")) { - parse_color(value, theme->window[SSD_ACTIVE].border_color); + parse_color(value, theme->window[THEME_ACTIVE].border_color); } if (match_glob(key, "window.inactive.border.color")) { - parse_color(value, theme->window[SSD_INACTIVE].border_color); + parse_color(value, theme->window[THEME_INACTIVE].border_color); } /* border.color is obsolete, but handled for backward compatibility */ if (match_glob(key, "border.color")) { - parse_color(value, theme->window[SSD_ACTIVE].border_color); - parse_color(value, theme->window[SSD_INACTIVE].border_color); + parse_color(value, theme->window[THEME_ACTIVE].border_color); + parse_color(value, theme->window[THEME_INACTIVE].border_color); } if (match_glob(key, "window.active.indicator.toggled-keybind.color")) { @@ -727,41 +719,41 @@ entry(struct theme *theme, const char *key, const char *value) } if (match_glob(key, "window.active.title.bg")) { - theme->window[SSD_ACTIVE].title_bg.gradient = parse_gradient(value); + theme->window[THEME_ACTIVE].title_bg.gradient = parse_gradient(value); } if (match_glob(key, "window.inactive.title.bg")) { - theme->window[SSD_INACTIVE].title_bg.gradient = parse_gradient(value); + theme->window[THEME_INACTIVE].title_bg.gradient = parse_gradient(value); } if (match_glob(key, "window.active.title.bg.color")) { - parse_color(value, theme->window[SSD_ACTIVE].title_bg.color); + parse_color(value, theme->window[THEME_ACTIVE].title_bg.color); } if (match_glob(key, "window.inactive.title.bg.color")) { - parse_color(value, theme->window[SSD_INACTIVE].title_bg.color); + parse_color(value, theme->window[THEME_INACTIVE].title_bg.color); } if (match_glob(key, "window.active.title.bg.color.splitTo")) { - parse_color(value, theme->window[SSD_ACTIVE].title_bg.color_split_to); + parse_color(value, theme->window[THEME_ACTIVE].title_bg.color_split_to); } if (match_glob(key, "window.inactive.title.bg.color.splitTo")) { - parse_color(value, theme->window[SSD_INACTIVE].title_bg.color_split_to); + parse_color(value, theme->window[THEME_INACTIVE].title_bg.color_split_to); } if (match_glob(key, "window.active.title.bg.colorTo")) { - parse_color(value, theme->window[SSD_ACTIVE].title_bg.color_to); + parse_color(value, theme->window[THEME_ACTIVE].title_bg.color_to); } if (match_glob(key, "window.inactive.title.bg.colorTo")) { - parse_color(value, theme->window[SSD_INACTIVE].title_bg.color_to); + parse_color(value, theme->window[THEME_INACTIVE].title_bg.color_to); } if (match_glob(key, "window.active.title.bg.colorTo.splitTo")) { - parse_color(value, theme->window[SSD_ACTIVE].title_bg.color_to_split_to); + parse_color(value, theme->window[THEME_ACTIVE].title_bg.color_to_split_to); } if (match_glob(key, "window.inactive.title.bg.colorTo.splitTo")) { - parse_color(value, theme->window[SSD_INACTIVE].title_bg.color_to_split_to); + parse_color(value, theme->window[THEME_INACTIVE].title_bg.color_to_split_to); } if (match_glob(key, "window.active.label.text.color")) { - parse_color(value, theme->window[SSD_ACTIVE].label_text_color); + parse_color(value, theme->window[THEME_ACTIVE].label_text_color); } if (match_glob(key, "window.inactive.label.text.color")) { - parse_color(value, theme->window[SSD_INACTIVE].label_text_color); + parse_color(value, theme->window[THEME_INACTIVE].label_text_color); } if (match_glob(key, "window.label.text.justify")) { theme->window_label_text_justify = parse_justification(value); @@ -794,88 +786,88 @@ entry(struct theme *theme, const char *key, const char *value) /* universal button */ if (match_glob(key, "window.active.button.unpressed.image.color")) { - for (enum lab_node_type type = LAB_NODE_BUTTON_FIRST; - type <= LAB_NODE_BUTTON_LAST; type++) { + for (enum ssd_part_type type = LAB_SSD_BUTTON_FIRST; + type <= LAB_SSD_BUTTON_LAST; type++) { parse_color(value, - theme->window[SSD_ACTIVE].button_colors[type]); + theme->window[THEME_ACTIVE].button_colors[type]); } } if (match_glob(key, "window.inactive.button.unpressed.image.color")) { - for (enum lab_node_type type = LAB_NODE_BUTTON_FIRST; - type <= LAB_NODE_BUTTON_LAST; type++) { + for (enum ssd_part_type type = LAB_SSD_BUTTON_FIRST; + type <= LAB_SSD_BUTTON_LAST; type++) { parse_color(value, - theme->window[SSD_INACTIVE].button_colors[type]); + theme->window[THEME_INACTIVE].button_colors[type]); } } /* individual buttons */ if (match_glob(key, "window.active.button.menu.unpressed.image.color")) { - parse_color(value, theme->window[SSD_ACTIVE] - .button_colors[LAB_NODE_BUTTON_WINDOW_MENU]); - parse_color(value, theme->window[SSD_ACTIVE] - .button_colors[LAB_NODE_BUTTON_WINDOW_ICON]); + parse_color(value, theme->window[THEME_ACTIVE] + .button_colors[LAB_SSD_BUTTON_WINDOW_MENU]); + parse_color(value, theme->window[THEME_ACTIVE] + .button_colors[LAB_SSD_BUTTON_WINDOW_ICON]); } if (match_glob(key, "window.active.button.iconify.unpressed.image.color")) { - parse_color(value, theme->window[SSD_ACTIVE] - .button_colors[LAB_NODE_BUTTON_ICONIFY]); + parse_color(value, theme->window[THEME_ACTIVE] + .button_colors[LAB_SSD_BUTTON_ICONIFY]); } if (match_glob(key, "window.active.button.max.unpressed.image.color")) { - parse_color(value, theme->window[SSD_ACTIVE] - .button_colors[LAB_NODE_BUTTON_MAXIMIZE]); + parse_color(value, theme->window[THEME_ACTIVE] + .button_colors[LAB_SSD_BUTTON_MAXIMIZE]); } if (match_glob(key, "window.active.button.shade.unpressed.image.color")) { - parse_color(value, theme->window[SSD_ACTIVE] - .button_colors[LAB_NODE_BUTTON_SHADE]); + parse_color(value, theme->window[THEME_ACTIVE] + .button_colors[LAB_SSD_BUTTON_SHADE]); } if (match_glob(key, "window.active.button.desk.unpressed.image.color")) { - parse_color(value, theme->window[SSD_ACTIVE] - .button_colors[LAB_NODE_BUTTON_OMNIPRESENT]); + parse_color(value, theme->window[THEME_ACTIVE] + .button_colors[LAB_SSD_BUTTON_OMNIPRESENT]); } if (match_glob(key, "window.active.button.close.unpressed.image.color")) { - parse_color(value, theme->window[SSD_ACTIVE] - .button_colors[LAB_NODE_BUTTON_CLOSE]); + parse_color(value, theme->window[THEME_ACTIVE] + .button_colors[LAB_SSD_BUTTON_CLOSE]); } if (match_glob(key, "window.inactive.button.menu.unpressed.image.color")) { - parse_color(value, theme->window[SSD_INACTIVE] - .button_colors[LAB_NODE_BUTTON_WINDOW_MENU]); - parse_color(value, theme->window[SSD_INACTIVE] - .button_colors[LAB_NODE_BUTTON_WINDOW_ICON]); + parse_color(value, theme->window[THEME_INACTIVE] + .button_colors[LAB_SSD_BUTTON_WINDOW_MENU]); + parse_color(value, theme->window[THEME_INACTIVE] + .button_colors[LAB_SSD_BUTTON_WINDOW_ICON]); } if (match_glob(key, "window.inactive.button.iconify.unpressed.image.color")) { - parse_color(value, theme->window[SSD_INACTIVE] - .button_colors[LAB_NODE_BUTTON_ICONIFY]); + parse_color(value, theme->window[THEME_INACTIVE] + .button_colors[LAB_SSD_BUTTON_ICONIFY]); } if (match_glob(key, "window.inactive.button.max.unpressed.image.color")) { - parse_color(value, theme->window[SSD_INACTIVE] - .button_colors[LAB_NODE_BUTTON_MAXIMIZE]); + parse_color(value, theme->window[THEME_INACTIVE] + .button_colors[LAB_SSD_BUTTON_MAXIMIZE]); } if (match_glob(key, "window.inactive.button.shade.unpressed.image.color")) { - parse_color(value, theme->window[SSD_INACTIVE] - .button_colors[LAB_NODE_BUTTON_SHADE]); + parse_color(value, theme->window[THEME_INACTIVE] + .button_colors[LAB_SSD_BUTTON_SHADE]); } if (match_glob(key, "window.inactive.button.desk.unpressed.image.color")) { - parse_color(value, theme->window[SSD_INACTIVE] - .button_colors[LAB_NODE_BUTTON_OMNIPRESENT]); + parse_color(value, theme->window[THEME_INACTIVE] + .button_colors[LAB_SSD_BUTTON_OMNIPRESENT]); } if (match_glob(key, "window.inactive.button.close.unpressed.image.color")) { - parse_color(value, theme->window[SSD_INACTIVE] - .button_colors[LAB_NODE_BUTTON_CLOSE]); + parse_color(value, theme->window[THEME_INACTIVE] + .button_colors[LAB_SSD_BUTTON_CLOSE]); } /* window drop-shadows */ if (match_glob(key, "window.active.shadow.size")) { - theme->window[SSD_ACTIVE].shadow_size = get_int_if_positive( + theme->window[THEME_ACTIVE].shadow_size = get_int_if_positive( value, "window.active.shadow.size"); } if (match_glob(key, "window.inactive.shadow.size")) { - theme->window[SSD_INACTIVE].shadow_size = get_int_if_positive( + theme->window[THEME_INACTIVE].shadow_size = get_int_if_positive( value, "window.inactive.shadow.size"); } if (match_glob(key, "window.active.shadow.color")) { - parse_color(value, theme->window[SSD_ACTIVE].shadow_color); + parse_color(value, theme->window[THEME_ACTIVE].shadow_color); } if (match_glob(key, "window.inactive.shadow.color")) { - parse_color(value, theme->window[SSD_INACTIVE].shadow_color); + parse_color(value, theme->window[THEME_INACTIVE].shadow_color); } if (match_glob(key, "menu.overlap.x")) { @@ -957,93 +949,39 @@ entry(struct theme *theme, const char *key, const char *value) if (match_glob(key, "osd.border.color")) { parse_color(value, theme->osd_border_color); } - /* classic window switcher */ - if (match_glob(key, "osd.window-switcher.style-classic.width") - || match_glob(key, "osd.window-switcher.width")) { + if (match_glob(key, "osd.window-switcher.width")) { if (strrchr(value, '%')) { - switcher_classic_theme->width_is_percent = true; + theme->osd_window_switcher_width_is_percent = true; } else { - switcher_classic_theme->width_is_percent = false; + theme->osd_window_switcher_width_is_percent = false; } - switcher_classic_theme->width = get_int_if_positive(value, - "osd.window-switcher.style-classic.width"); + theme->osd_window_switcher_width = get_int_if_positive( + value, "osd.window-switcher.width"); } - if (match_glob(key, "osd.window-switcher.style-classic.padding") - || match_glob(key, "osd.window-switcher.padding")) { - switcher_classic_theme->padding = get_int_if_positive(value, - "osd.window-switcher.style-classic.padding"); + if (match_glob(key, "osd.window-switcher.padding")) { + theme->osd_window_switcher_padding = get_int_if_positive( + value, "osd.window-switcher.padding"); } - if (match_glob(key, "osd.window-switcher.style-classic.item.padding.x") - || match_glob(key, "osd.window-switcher.item.padding.x")) { - switcher_classic_theme->item_padding_x = - get_int_if_positive(value, - "osd.window-switcher.style-classic.item.padding.x"); + if (match_glob(key, "osd.window-switcher.item.padding.x")) { + theme->osd_window_switcher_item_padding_x = + get_int_if_positive( + value, "osd.window-switcher.item.padding.x"); } - if (match_glob(key, "osd.window-switcher.style-classic.item.padding.y") - || match_glob(key, "osd.window-switcher.item.padding.y")) { - switcher_classic_theme->item_padding_y = - get_int_if_positive(value, - "osd.window-switcher.style-classic.item.padding.y"); + if (match_glob(key, "osd.window-switcher.item.padding.y")) { + theme->osd_window_switcher_item_padding_y = + get_int_if_positive( + value, "osd.window-switcher.item.padding.y"); } - if (match_glob(key, "osd.window-switcher.style-classic.item.active.border.width") - || match_glob(key, "osd.window-switcher.item.active.border.width")) { - switcher_classic_theme->item_active_border_width = - get_int_if_positive(value, - "osd.window-switcher.style-classic.item.active.border.width"); + if (match_glob(key, "osd.window-switcher.item.active.border.width")) { + theme->osd_window_switcher_item_active_border_width = + get_int_if_positive( + value, "osd.window-switcher.item.active.border.width"); } - if (match_glob(key, "osd.window-switcher.style-classic.item.active.border.color")) { - parse_color(value, switcher_classic_theme->item_active_border_color); + if (match_glob(key, "osd.window-switcher.item.icon.size")) { + theme->osd_window_switcher_item_icon_size = + get_int_if_positive( + value, "osd.window-switcher.item.icon.size"); } - if (match_glob(key, "osd.window-switcher.style-classic.item.active.bg.color")) { - parse_color(value, switcher_classic_theme->item_active_bg_color); - } - if (match_glob(key, "osd.window-switcher.style-classic.item.icon.size") - || match_glob(key, "osd.window-switcher.item.icon.size")) { - switcher_classic_theme->item_icon_size = - get_int_if_positive(value, - "osd.window-switcher.style-classic.item.icon.size"); - } - /* thumbnail window switcher */ - if (match_glob(key, "osd.window-switcher.style-thumbnail.width.max")) { - if (strrchr(value, '%')) { - switcher_thumb_theme->max_width_is_percent = true; - } else { - switcher_thumb_theme->max_width_is_percent = false; - } - switcher_thumb_theme->max_width = get_int_if_positive( - value, "osd.window-switcher.style-thumbnail.width.max"); - } - if (match_glob(key, "osd.window-switcher.style-thumbnail.padding")) { - switcher_thumb_theme->padding = get_int_if_positive( - value, "osd.window-switcher.style-thumbnail.padding"); - } - if (match_glob(key, "osd.window-switcher.style-thumbnail.item.width")) { - switcher_thumb_theme->item_width = get_int_if_positive( - value, "osd.window-switcher.style-thumbnail.item.width"); - } - if (match_glob(key, "osd.window-switcher.style-thumbnail.item.height")) { - switcher_thumb_theme->item_height = get_int_if_positive( - value, "osd.window-switcher.style-thumbnail.item.height"); - } - if (match_glob(key, "osd.window-switcher.style-thumbnail.item.padding")) { - switcher_thumb_theme->item_padding = get_int_if_positive( - value, "osd.window-switcher.style-thumbnail.item.padding"); - } - if (match_glob(key, "osd.window-switcher.style-thumbnail.item.active.border.width")) { - switcher_thumb_theme->item_active_border_width = get_int_if_positive( - value, "osd.window-switcher.style-thumbnail.item.active.border.width"); - } - if (match_glob(key, "osd.window-switcher.style-thumbnail.item.active.border.color")) { - parse_color(value, switcher_thumb_theme->item_active_border_color); - } - if (match_glob(key, "osd.window-switcher.style-thumbnail.item.active.bg.color")) { - parse_color(value, switcher_thumb_theme->item_active_bg_color); - } - if (match_glob(key, "osd.window-switcher.style-thumbnail.item.icon.size")) { - switcher_thumb_theme->item_icon_size = get_int_if_positive( - value, "osd.window-switcher.style-thumbnail.item.icon.size"); - } - if (match_glob(key, "osd.window-switcher.preview.border.width")) { theme->osd_window_switcher_preview_border_width = get_int_if_positive( @@ -1171,6 +1109,10 @@ theme_read(struct theme *theme, struct wl_list *paths) static struct lab_data_buffer * rounded_rect(struct rounded_corner_ctx *ctx) { + if (ctx->corner == LAB_CORNER_UNKNOWN) { + return NULL; + } + double w = ctx->box->width; double h = ctx->box->height; double r = ctx->radius; @@ -1204,18 +1146,20 @@ rounded_rect(struct rounded_corner_ctx *ctx) cairo_set_line_width(cairo, 0.0); cairo_new_sub_path(cairo); switch (ctx->corner) { - case ROUNDED_CORNER_TOP_LEFT: + case LAB_CORNER_TOP_LEFT: cairo_arc(cairo, r, r, r, 180 * deg, 270 * deg); cairo_line_to(cairo, w, 0); cairo_line_to(cairo, w, h); cairo_line_to(cairo, 0, h); break; - case ROUNDED_CORNER_TOP_RIGHT: + case LAB_CORNER_TOP_RIGHT: cairo_arc(cairo, w - r, r, r, -90 * deg, 0 * deg); cairo_line_to(cairo, w, h); cairo_line_to(cairo, 0, h); cairo_line_to(cairo, 0, 0); break; + default: + wlr_log(WLR_ERROR, "unknown corner type"); } cairo_close_path(cairo); cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); @@ -1258,18 +1202,20 @@ rounded_rect(struct rounded_corner_ctx *ctx) cairo_set_line_width(cairo, ctx->line_width); double half_line_width = ctx->line_width / 2.0; switch (ctx->corner) { - case ROUNDED_CORNER_TOP_LEFT: + case LAB_CORNER_TOP_LEFT: cairo_move_to(cairo, half_line_width, h); cairo_line_to(cairo, half_line_width, r); cairo_move_to(cairo, r, half_line_width); cairo_line_to(cairo, w, half_line_width); break; - case ROUNDED_CORNER_TOP_RIGHT: + case LAB_CORNER_TOP_RIGHT: cairo_move_to(cairo, 0, half_line_width); cairo_line_to(cairo, w - r, half_line_width); cairo_move_to(cairo, w - half_line_width, r); cairo_line_to(cairo, w - half_line_width, h); break; + default: + wlr_log(WLR_ERROR, "unknown corner type"); } cairo_stroke(cairo); @@ -1317,14 +1263,16 @@ rounded_rect(struct rounded_corner_ctx *ctx) cairo_set_line_width(cairo, line_width); half_line_width = line_width / 2.0; switch (ctx->corner) { - case ROUNDED_CORNER_TOP_LEFT: + case LAB_CORNER_TOP_LEFT: cairo_move_to(cairo, half_line_width, r); cairo_arc(cairo, r, r, r - half_line_width, 180 * deg, 270 * deg); break; - case ROUNDED_CORNER_TOP_RIGHT: + case LAB_CORNER_TOP_RIGHT: cairo_move_to(cairo, w - r, half_line_width); cairo_arc(cairo, w - r, r, r - half_line_width, -90 * deg, 0 * deg); break; + default: + break; } cairo_stroke(cairo); @@ -1396,8 +1344,7 @@ create_titlebar_fill(cairo_pattern_t *pattern, int height) static void create_backgrounds(struct theme *theme) { - enum ssd_active_state active; - FOR_EACH_ACTIVE_STATE(active) { + for (int active = THEME_INACTIVE; active <= THEME_ACTIVE; active++) { theme->window[active].titlebar_pattern = create_titlebar_pattern( &theme->window[active].title_bg, theme->titlebar_height); @@ -1419,18 +1366,17 @@ create_corners(struct theme *theme) .height = theme->titlebar_height + theme->border_width, }; - enum ssd_active_state active; - FOR_EACH_ACTIVE_STATE(active) { + for (int active = THEME_INACTIVE; active <= THEME_ACTIVE; active++) { struct rounded_corner_ctx ctx = { .box = &box, .radius = rc.corner_radius, .line_width = theme->border_width, .fill_pattern = theme->window[active].titlebar_pattern, .border_color = theme->window[active].border_color, - .corner = ROUNDED_CORNER_TOP_LEFT, + .corner = LAB_CORNER_TOP_LEFT, }; theme->window[active].corner_top_left_normal = rounded_rect(&ctx); - ctx.corner = ROUNDED_CORNER_TOP_RIGHT; + ctx.corner = LAB_CORNER_TOP_RIGHT; theme->window[active].corner_top_right_normal = rounded_rect(&ctx); } } @@ -1550,7 +1496,7 @@ shadow_corner_gradient(struct lab_data_buffer *buffer, int visible_size, } static void -create_shadow(struct theme *theme, enum ssd_active_state active) +create_shadow(struct theme *theme, int active) { /* Size of shadow visible extending beyond the window */ int visible_size = theme->window[active].shadow_size; @@ -1592,8 +1538,8 @@ create_shadow(struct theme *theme, enum ssd_active_state active) static void create_shadows(struct theme *theme) { - create_shadow(theme, SSD_INACTIVE); - create_shadow(theme, SSD_ACTIVE); + create_shadow(theme, THEME_INACTIVE); + create_shadow(theme, THEME_ACTIVE); } static void @@ -1643,43 +1589,13 @@ get_titlebar_height(struct theme *theme) return h; } -/* Blend foreground color (with new alpha) with background color */ -static void -blend_color_with_bg(float *dst, float *fg, float fg_a, float *bg) -{ - /* Guard against zero division */ - if (fg[3] <= 0.0f) { - memset(dst, 0, sizeof(float) * 4); - return; - } - - /* Redo premultiplication to update foreground alpha */ - float new_fg[4] = { - fg[0] / fg[3] * fg_a, - fg[1] / fg[3] * fg_a, - fg[2] / fg[3] * fg_a, - fg_a, - }; - - /* Blend colors */ - dst[0] = new_fg[0] + bg[0] * (1.0f - new_fg[3]); - dst[1] = new_fg[1] + bg[1] * (1.0f - new_fg[3]); - dst[2] = new_fg[2] + bg[2] * (1.0f - new_fg[3]); - dst[3] = new_fg[3] + bg[3] * (1.0f - new_fg[3]); -} - static void post_processing(struct theme *theme) { - struct window_switcher_classic_theme *switcher_classic_theme = - &theme->osd_window_switcher_classic; - struct window_switcher_thumbnail_theme *switcher_thumb_theme = - &theme->osd_window_switcher_thumbnail; - theme->titlebar_height = get_titlebar_height(theme); - fill_background_colors(&theme->window[SSD_INACTIVE].title_bg); - fill_background_colors(&theme->window[SSD_ACTIVE].title_bg); + fill_background_colors(&theme->window[THEME_INACTIVE].title_bg); + fill_background_colors(&theme->window[THEME_ACTIVE].title_bg); theme->menu_item_height = font_height(&rc.font_menuitem) + 2 * theme->menu_items_padding_y; @@ -1688,15 +1604,14 @@ post_processing(struct theme *theme) + 2 * theme->menu_items_padding_y; int osd_font_height = font_height(&rc.font_osd); - switcher_thumb_theme->title_height = osd_font_height; - if (switcher_classic_theme->item_icon_size <= 0) { - switcher_classic_theme->item_icon_size = osd_font_height; + if (theme->osd_window_switcher_item_icon_size <= 0) { + theme->osd_window_switcher_item_icon_size = osd_font_height; } int osd_field_height = - MAX(osd_font_height, switcher_classic_theme->item_icon_size); - switcher_classic_theme->item_height = osd_field_height - + 2 * switcher_classic_theme->item_padding_y - + 2 * switcher_classic_theme->item_active_border_width; + MAX(osd_font_height, theme->osd_window_switcher_item_icon_size); + theme->osd_window_switcher_item_height = osd_field_height + + 2 * theme->osd_window_switcher_item_padding_y + + 2 * theme->osd_window_switcher_item_active_border_width; if (rc.corner_radius >= theme->titlebar_height) { rc.corner_radius = theme->titlebar_height - 1; @@ -1724,14 +1639,14 @@ post_processing(struct theme *theme) } if (theme->menu_border_color[0] == FLT_MIN) { memcpy(theme->menu_border_color, - theme->window[SSD_ACTIVE].border_color, + theme->window[THEME_ACTIVE].border_color, sizeof(theme->menu_border_color)); } /* Inherit OSD settings if not set */ if (theme->osd_bg_color[0] == FLT_MIN) { memcpy(theme->osd_bg_color, - theme->window[SSD_ACTIVE].title_bg.color, + theme->window[THEME_ACTIVE].title_bg.color, sizeof(theme->osd_bg_color)); } if (theme->osd_border_width == INT_MIN) { @@ -1739,7 +1654,7 @@ post_processing(struct theme *theme) } if (theme->osd_label_text_color[0] == FLT_MIN) { memcpy(theme->osd_label_text_color, - theme->window[SSD_ACTIVE].label_text_color, + theme->window[THEME_ACTIVE].label_text_color, sizeof(theme->osd_label_text_color)); } if (theme->osd_border_color[0] == FLT_MIN) { @@ -1756,31 +1671,15 @@ post_processing(struct theme *theme) memcpy(theme->osd_border_color, theme->osd_label_text_color, sizeof(theme->osd_border_color)); } - if (switcher_classic_theme->item_active_border_color[0] == FLT_MIN) { - blend_color_with_bg(switcher_classic_theme->item_active_border_color, - theme->osd_label_text_color, 0.50, theme->osd_bg_color); - } - if (switcher_classic_theme->item_active_bg_color[0] == FLT_MIN) { - blend_color_with_bg(switcher_classic_theme->item_active_bg_color, - theme->osd_label_text_color, 0.15, theme->osd_bg_color); - } - if (switcher_thumb_theme->item_active_border_color[0] == FLT_MIN) { - blend_color_with_bg(switcher_thumb_theme->item_active_border_color, - theme->osd_label_text_color, 0.50, theme->osd_bg_color); - } - if (switcher_thumb_theme->item_active_bg_color[0] == FLT_MIN) { - blend_color_with_bg(switcher_thumb_theme->item_active_bg_color, - theme->osd_label_text_color, 0.15, theme->osd_bg_color); - } if (theme->osd_workspace_switcher_boxes_width == 0) { theme->osd_workspace_switcher_boxes_height = 0; } if (theme->osd_workspace_switcher_boxes_height == 0) { theme->osd_workspace_switcher_boxes_width = 0; } - if (switcher_classic_theme->width_is_percent) { - switcher_classic_theme->width = - MIN(switcher_classic_theme->width, 100); + if (theme->osd_window_switcher_width_is_percent) { + theme->osd_window_switcher_width = + MIN(theme->osd_window_switcher_width, 100); } if (theme->osd_window_switcher_preview_border_width == INT_MIN) { theme->osd_window_switcher_preview_border_width = @@ -1852,19 +1751,18 @@ static void destroy_img(struct lab_img **img) void theme_finish(struct theme *theme) { - for (enum lab_node_type type = LAB_NODE_BUTTON_FIRST; - type <= LAB_NODE_BUTTON_LAST; type++) { + for (enum ssd_part_type type = LAB_SSD_BUTTON_FIRST; + type <= LAB_SSD_BUTTON_LAST; type++) { for (uint8_t state_set = LAB_BS_DEFAULT; state_set <= LAB_BS_ALL; state_set++) { - destroy_img(&theme->window[SSD_INACTIVE] + destroy_img(&theme->window[THEME_INACTIVE] .button_imgs[type][state_set]); - destroy_img(&theme->window[SSD_ACTIVE] + destroy_img(&theme->window[THEME_ACTIVE] .button_imgs[type][state_set]); } } - enum ssd_active_state active; - FOR_EACH_ACTIVE_STATE(active) { + for (int active = THEME_INACTIVE; active <= THEME_ACTIVE; active++) { zfree_pattern(theme->window[active].titlebar_pattern); zdrop(&theme->window[active].titlebar_fill); zdrop(&theme->window[active].corner_top_left_normal); diff --git a/src/view-impl-common.c b/src/view-impl-common.c index df72fe62..3357367b 100644 --- a/src/view-impl-common.c +++ b/src/view-impl-common.c @@ -1,15 +1,19 @@ // SPDX-License-Identifier: GPL-2.0-only /* view-impl-common.c: common code for shell view->impl functions */ -#include "view-impl-common.h" -#include "foreign-toplevel/foreign.h" +#include +#include +#include "foreign-toplevel.h" #include "labwc.h" #include "view.h" +#include "view-impl-common.h" #include "window-rules.h" void view_impl_map(struct view *view) { desktop_focus_view(view, /*raise*/ true); + view_update_title(view); + view_update_app_id(view); if (!view->been_mapped) { window_rules_apply(view, LAB_WINDOW_RULE_EVENT_ON_FIRST_MAP); } @@ -34,7 +38,8 @@ view_impl_map(struct view *view) desktop_update_top_layer_visibility(view->server); wlr_log(WLR_DEBUG, "[map] identifier=%s, title=%s", - view->app_id, view->title); + view_get_string_prop(view, "app_id"), + view_get_string_prop(view, "title")); } void @@ -55,7 +60,7 @@ view_impl_unmap(struct view *view) } static bool -resizing_edge(struct view *view, enum lab_edge edge) +resizing_edge(struct view *view, uint32_t edge) { struct server *server = view->server; return server->input_mode == LAB_INPUT_STATE_RESIZE @@ -82,7 +87,7 @@ view_impl_apply_geometry(struct view *view, int w, int h) * reliable on its own. The combination of the two methods * should catch 99% of resize cases that we care about. */ - bool resizing_left_edge = resizing_edge(view, LAB_EDGE_LEFT); + bool resizing_left_edge = resizing_edge(view, WLR_EDGE_LEFT); if (resizing_left_edge || (current->x != pending->x && current->x + current->width == pending->x + pending->width)) { @@ -92,7 +97,7 @@ view_impl_apply_geometry(struct view *view, int w, int h) } /* Anchor bottom edge if resizing via top edge */ - bool resizing_top_edge = resizing_edge(view, LAB_EDGE_TOP); + bool resizing_top_edge = resizing_edge(view, WLR_EDGE_TOP); if (resizing_top_edge || (current->y != pending->y && current->y + current->height == pending->y + pending->height)) { diff --git a/src/view.c b/src/view.c index 87ef8566..59c32e27 100644 --- a/src/view.c +++ b/src/view.c @@ -1,38 +1,34 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "view.h" #include +#include #include -#include -#include #include -#include #include -#include -#include "action.h" #include "buffer.h" #include "common/box.h" #include "common/list.h" +#include "common/macros.h" #include "common/match.h" #include "common/mem.h" -#include "config/rcxml.h" -#include "foreign-toplevel/foreign.h" +#include "common/parse-bool.h" +#include "common/scene-helpers.h" +#include "foreign-toplevel.h" #include "input/keyboard.h" #include "labwc.h" #include "menu/menu.h" #include "osd.h" -#include "output.h" #include "output-state.h" #include "placement.h" #include "regions.h" #include "resize-indicator.h" -#include "session-lock.h" #include "snap-constraints.h" #include "snap.h" #include "ssd.h" -#include "theme.h" +#include "view.h" #include "window-rules.h" #include "wlr/util/log.h" #include "workspaces.h" +#include "xwayland.h" #if HAVE_XWAYLAND #include @@ -77,10 +73,8 @@ struct view_query * view_query_create(void) { struct view_query *query = znew(*query); - /* Must be synced with view_matches_criteria() in window-rules.c */ - query->window_type = LAB_WINDOW_TYPE_INVALID; + query->window_type = -1; query->maximized = VIEW_AXIS_INVALID; - query->decoration = LAB_SSD_MODE_INVALID; return query; } @@ -99,7 +93,7 @@ view_query_free(struct view_query *query) } static bool -query_tristate_match(enum lab_tristate desired, bool actual) +query_tristate_match(enum three_state desired, bool actual) { switch (desired) { case LAB_STATE_ENABLED: @@ -120,29 +114,18 @@ query_str_match(const char *condition, const char *value) return value && match_glob(condition, value); } -static bool -view_contains_window_type(struct view *view, enum lab_window_type window_type) -{ - assert(view); - if (view->impl->contains_window_type) { - return view->impl->contains_window_type(view, window_type); - } - return false; -} - bool view_matches_query(struct view *view, struct view_query *query) { - if (!query_str_match(query->identifier, view->app_id)) { + if (!query_str_match(query->identifier, view_get_string_prop(view, "app_id"))) { return false; } - if (!query_str_match(query->title, view->title)) { + if (!query_str_match(query->title, view_get_string_prop(view, "title"))) { return false; } - if (query->window_type != LAB_WINDOW_TYPE_INVALID - && !view_contains_window_type(view, query->window_type)) { + if (query->window_type >= 0 && !view_contains_window_type(view, query->window_type)) { return false; } @@ -183,14 +166,8 @@ view_matches_query(struct view *view, struct view_query *query) return false; } - if (query->tiled == LAB_EDGE_ANY) { - if (!view->tiled) { - return false; - } - } else if (query->tiled != LAB_EDGE_NONE) { - if (query->tiled != view->tiled) { - return false; - } + if (query->tiled != VIEW_EDGE_INVALID && query->tiled != view->tiled) { + return false; } const char *tiled_region = @@ -218,48 +195,33 @@ view_matches_query(struct view *view, struct view_query *query) } } - if (query->decoration != LAB_SSD_MODE_INVALID - && query->decoration != view->ssd_mode) { + enum ssd_mode decor = view_get_ssd_mode(view); + if (query->decoration != LAB_SSD_MODE_INVALID && query->decoration != decor) { return false; } if (query->monitor) { struct output *current = output_nearest_to_cursor(view->server); - if (!strcasecmp(query->monitor, "current")) { - if (current != view->output) { - return false; - } - } else if (!strcasecmp(query->monitor, "left")) { - if (output_get_adjacent(current, LAB_EDGE_LEFT, false) - != view->output) { - return false; - } - } else if (!strcasecmp(query->monitor, "right")) { - if (output_get_adjacent(current, LAB_EDGE_RIGHT, false) - != view->output) { - return false; - } - } else { - if (output_from_name(view->server, query->monitor) - != view->output) { - return false; - } + + if (!strcasecmp(query->monitor, "current") && current != view->output) { + return false; + } + if (!strcasecmp(query->monitor, "left") && + output_get_adjacent(current, VIEW_EDGE_LEFT, false) != view->output) { + return false; + } + if (!strcasecmp(query->monitor, "right") && + output_get_adjacent(current, VIEW_EDGE_RIGHT, false) != view->output) { + return false; + } + if (output_from_name(view->server, query->monitor) != view->output) { + return false; } } return true; } -static struct view * -view_get_root(struct view *view) -{ - assert(view); - if (view->impl->get_root) { - return view->impl->get_root(view); - } - return view; -} - static bool matches_criteria(struct view *view, enum lab_view_criteria criteria) { @@ -413,6 +375,16 @@ view_wants_focus(struct view *view) return VIEW_WANTS_FOCUS_ALWAYS; } +bool +view_contains_window_type(struct view *view, enum window_type window_type) +{ + assert(view); + if (view->impl->contains_window_type) { + return view->impl->contains_window_type(view, window_type); + } + return false; +} + bool view_is_focusable(struct view *view) { @@ -446,44 +418,62 @@ view_offer_focus(struct view *view) * They may be called repeatably during output layout changes. */ -struct wlr_box +enum view_edge +view_edge_invert(enum view_edge edge) +{ + switch (edge) { + case VIEW_EDGE_LEFT: + return VIEW_EDGE_RIGHT; + case VIEW_EDGE_RIGHT: + return VIEW_EDGE_LEFT; + case VIEW_EDGE_UP: + return VIEW_EDGE_DOWN; + case VIEW_EDGE_DOWN: + return VIEW_EDGE_UP; + case VIEW_EDGE_CENTER: + case VIEW_EDGE_INVALID: + default: + return VIEW_EDGE_INVALID; + } +} + +static struct wlr_box view_get_edge_snap_box(struct view *view, struct output *output, - enum lab_edge edge) + enum view_edge edge) { struct wlr_box usable = output_usable_area_in_layout_coords(output); - int x1 = rc.gap; - int y1 = rc.gap; - int x2 = usable.width - rc.gap; - int y2 = usable.height - rc.gap; + int x_offset = edge == VIEW_EDGE_RIGHT + ? (usable.width + rc.gap) / 2 : rc.gap; + int y_offset = edge == VIEW_EDGE_DOWN + ? (usable.height + rc.gap) / 2 : rc.gap; - if (edge & LAB_EDGE_RIGHT) { - x1 = (usable.width + rc.gap) / 2; - } - if (edge & LAB_EDGE_LEFT) { - x2 = (usable.width - rc.gap) / 2; - } - if (edge & LAB_EDGE_BOTTOM) { - y1 = (usable.height + rc.gap) / 2; - } - if (edge & LAB_EDGE_TOP) { - y2 = (usable.height - rc.gap) / 2; + int base_width, base_height; + switch (edge) { + case VIEW_EDGE_LEFT: + case VIEW_EDGE_RIGHT: + base_width = (usable.width - 3 * rc.gap) / 2; + base_height = usable.height - 2 * rc.gap; + break; + case VIEW_EDGE_UP: + case VIEW_EDGE_DOWN: + base_width = usable.width - 2 * rc.gap; + base_height = (usable.height - 3 * rc.gap) / 2; + break; + default: + case VIEW_EDGE_CENTER: + base_width = usable.width - 2 * rc.gap; + base_height = usable.height - 2 * rc.gap; + break; } + struct border margin = ssd_get_margin(view->ssd); struct wlr_box dst = { - .x = x1 + usable.x, - .y = y1 + usable.y, - .width = x2 - x1, - .height = y2 - y1, + .x = x_offset + usable.x + margin.left, + .y = y_offset + usable.y + margin.top, + .width = base_width - margin.left - margin.right, + .height = base_height - margin.top - margin.bottom, }; - if (view) { - struct border margin = ssd_get_margin(view->ssd); - dst.x += margin.left; - dst.y += margin.top; - dst.width -= margin.left + margin.right; - dst.height -= margin.top + margin.bottom; - } - return dst; } @@ -674,7 +664,7 @@ view_move_relative(struct view *view, int x, int y) view_maximize(view, VIEW_AXIS_NONE, /*store_natural_geometry*/ false); if (view_is_tiled(view)) { view_set_untiled(view); - view_move_resize(view, view->natural_geometry); + view_restore_to(view, view->natural_geometry); } view_move(view, view->pending.x + x, view->pending.y + y); } @@ -692,7 +682,7 @@ view_move_to_cursor(struct view *view) view_maximize(view, VIEW_AXIS_NONE, /*store_natural_geometry*/ false); if (view_is_tiled(view)) { view_set_untiled(view); - view_move_resize(view, view->natural_geometry); + view_restore_to(view, view->natural_geometry); } struct border margin = ssd_thickness(view); @@ -758,6 +748,7 @@ view_adjust_size(struct view *view, int *w, int *h) { assert(view); struct view_size_hints hints = view_get_size_hints(view); + int min_width = view_get_min_width(); /* * "If a base size is not provided, the minimum size is to be @@ -775,9 +766,12 @@ view_adjust_size(struct view *view, int *w, int *h) *w = round_to_increment(*w, hints.base_width, hints.width_inc); *h = round_to_increment(*h, hints.base_height, hints.height_inc); - /* If a minimum width/height was not set, then use default */ + /* + * If a minimum width/height was not set, then use default. + * This is currently always the case for xdg-shell views. + */ if (hints.min_width < 1) { - hints.min_width = LAB_MIN_VIEW_WIDTH; + hints.min_width = min_width; } if (hints.min_height < 1) { hints.min_height = LAB_MIN_VIEW_HEIGHT; @@ -808,15 +802,6 @@ _minimize(struct view *view, bool minimized) } } -static void -view_append_children(struct view *view, struct wl_array *children) -{ - assert(view); - if (view->impl->append_children) { - view->impl->append_children(view, children); - } -} - static void minimize_sub_views(struct view *view, bool minimized) { @@ -976,27 +961,24 @@ adjust_floating_geometry(struct view *view, struct wlr_box *geometry, &geometry->x, &geometry->y); } -struct wlr_box -view_get_fallback_natural_geometry(struct view *view) +void +view_set_fallback_natural_geometry(struct view *view) { - struct wlr_box box = { - .width = VIEW_FALLBACK_WIDTH, - .height = VIEW_FALLBACK_HEIGHT, - }; + view->natural_geometry.width = VIEW_FALLBACK_WIDTH; + view->natural_geometry.height = VIEW_FALLBACK_HEIGHT; view_compute_centered_position(view, NULL, - box.width, box.height, &box.x, &box.y); - return box; + view->natural_geometry.width, + view->natural_geometry.height, + &view->natural_geometry.x, + &view->natural_geometry.y); } void view_store_natural_geometry(struct view *view) { assert(view); - /* - * Do not overwrite the stored geometry if fullscreen or tiled. - * Maximized views are handled on a per-axis basis (see below). - */ - if (view->fullscreen || view_is_tiled(view)) { + if (!view_is_floating(view)) { + /* Do not overwrite the stored geometry with special cases */ return; } @@ -1007,14 +989,7 @@ view_store_natural_geometry(struct view *view) * xdg-toplevel configure event, which means the application should * choose its own size. */ - if (!(view->maximized & VIEW_AXIS_HORIZONTAL)) { - view->natural_geometry.x = view->pending.x; - view->natural_geometry.width = view->pending.width; - } - if (!(view->maximized & VIEW_AXIS_VERTICAL)) { - view->natural_geometry.y = view->pending.y; - view->natural_geometry.height = view->pending.height; - } + view->natural_geometry = view->pending; } int @@ -1130,7 +1105,7 @@ view_cascade(struct view *view) void view_place_by_policy(struct view *view, bool allow_cursor, - enum lab_placement_policy policy) + enum view_placement_policy policy) { if (allow_cursor && policy == LAB_PLACE_CURSOR) { view_move_to_cursor(view); @@ -1210,54 +1185,6 @@ view_apply_natural_geometry(struct view *view) view_move_resize(view, geometry); } -struct wlr_box -view_get_region_snap_box(struct view *view, struct region *region) -{ - struct wlr_box geo = region->geo; - - /* Adjust for rc.gap */ - if (rc.gap) { - double half_gap = rc.gap / 2.0; - struct wlr_fbox offset = { - .x = half_gap, - .y = half_gap, - .width = -rc.gap, - .height = -rc.gap - }; - struct wlr_box usable = - output_usable_area_in_layout_coords(region->output); - if (geo.x == usable.x) { - offset.x += half_gap; - offset.width -= half_gap; - } - if (geo.y == usable.y) { - offset.y += half_gap; - offset.height -= half_gap; - } - if (geo.x + geo.width == usable.x + usable.width) { - offset.width -= half_gap; - } - if (geo.y + geo.height == usable.y + usable.height) { - offset.height -= half_gap; - } - geo.x += offset.x; - geo.y += offset.y; - geo.width += offset.width; - geo.height += offset.height; - } - - /* And adjust for current view */ - if (view) { - struct border margin = ssd_get_margin(view->ssd); - geo.x += margin.left; - geo.y += margin.top; - geo.width -= margin.left + margin.right; - geo.height -= margin.top + margin.bottom; - } - - return geo; -} - static void view_apply_region_geometry(struct view *view) { @@ -1283,7 +1210,47 @@ view_apply_region_geometry(struct view *view) } } - struct wlr_box geo = view_get_region_snap_box(view, view->tiled_region); + /* Create a copy of the original region geometry */ + struct wlr_box geo = view->tiled_region->geo; + + /* Adjust for rc.gap */ + if (rc.gap) { + double half_gap = rc.gap / 2.0; + struct wlr_fbox offset = { + .x = half_gap, + .y = half_gap, + .width = -rc.gap, + .height = -rc.gap + }; + struct wlr_box usable = + output_usable_area_in_layout_coords(output); + if (geo.x == usable.x) { + offset.x += half_gap; + offset.width -= half_gap; + } + if (geo.y == usable.y) { + offset.y += half_gap; + offset.height -= half_gap; + } + if (geo.x + geo.width == usable.x + usable.width) { + offset.width -= half_gap; + } + if (geo.y + geo.height == usable.y + usable.height) { + offset.height -= half_gap; + } + geo.x += offset.x; + geo.y += offset.y; + geo.width += offset.width; + geo.height += offset.height; + } + + /* And adjust for current view */ + struct border margin = ssd_get_margin(view->ssd); + geo.x += margin.left; + geo.y += margin.top; + geo.width -= margin.left + margin.right; + geo.height -= margin.top + margin.bottom; + view_move_resize(view, geo); } @@ -1325,6 +1292,14 @@ view_apply_maximized_geometry(struct view *view) assert(output_is_usable(output)); struct wlr_box box = output_usable_area_in_layout_coords(output); + if (box.height == output->wlr_output->height + && output->wlr_output->scale != 1) { + box.height /= output->wlr_output->scale; + } + if (box.width == output->wlr_output->width + && output->wlr_output->scale != 1) { + box.width /= output->wlr_output->scale; + } /* * If one axis (horizontal or vertical) is unmaximized, it @@ -1340,7 +1315,7 @@ view_apply_maximized_geometry(struct view *view) &natural.x, &natural.y); } - if (view->ssd_mode) { + if (view->ssd_enabled) { struct border border = ssd_thickness(view); box.x += border.left; box.y += border.top; @@ -1382,18 +1357,10 @@ view_apply_special_geometry(struct view *view) } } -/* - * Sets maximized state without updating geometry. Used in interactive - * move/resize. In most other cases, use view_maximize() instead. - */ -void -view_set_maximized(struct view *view, enum view_axis maximized) +/* For internal use only. Does not update geometry. */ +static void +set_maximized(struct view *view, enum view_axis maximized) { - assert(view); - if (view->maximized == maximized) { - return; - } - if (view->impl->maximize) { view->impl->maximize(view, maximized); } @@ -1409,6 +1376,23 @@ view_set_maximized(struct view *view, enum view_axis maximized) ssd_update_margin(view->ssd); } +/* + * Un-maximize view and move it to specific geometry. Does not reset + * tiled state (use view_set_untiled() if you want that). + */ +void +view_restore_to(struct view *view, struct wlr_box geometry) +{ + assert(view); + if (view->fullscreen) { + return; + } + if (view->maximized != VIEW_AXIS_NONE) { + set_maximized(view, VIEW_AXIS_NONE); + } + view_move_resize(view, geometry); +} + bool view_is_tiled(struct view *view) { @@ -1456,7 +1440,7 @@ void view_set_untiled(struct view *view) { assert(view); - view->tiled = LAB_EDGE_NONE; + view->tiled = VIEW_EDGE_INVALID; view->tiled_region = NULL; zfree(view->tiled_region_evacuate); view_notify_tiled(view); @@ -1486,20 +1470,11 @@ view_maximize(struct view *view, enum view_axis axis, */ interactive_cancel(view); if (store_natural_geometry && view_is_floating(view)) { + view_store_natural_geometry(view); view_invalidate_last_layout_geometry(view); } } - /* - * Update natural geometry for any axis that wasn't already - * maximized. This is needed even when unmaximizing, because in - * single-axis cases the client may have resized the other axis - * while one axis was maximized. - */ - if (store_natural_geometry) { - view_store_natural_geometry(view); - } - /* * When natural geometry is unknown (0x0) for an xdg-shell view, * we normally send a configure event of 0x0 to get the client's @@ -1508,10 +1483,10 @@ view_maximize(struct view *view, enum view_axis axis, */ if ((axis == VIEW_AXIS_HORIZONTAL || axis == VIEW_AXIS_VERTICAL) && wlr_box_empty(&view->natural_geometry)) { - view->natural_geometry = view_get_fallback_natural_geometry(view); + view_set_fallback_natural_geometry(view); } - view_set_maximized(view, axis); + set_maximized(view, axis); if (view_is_floating(view)) { view_apply_natural_geometry(view); } else { @@ -1576,12 +1551,12 @@ view_wants_decorations(struct view *view) } void -view_set_decorations(struct view *view, enum lab_ssd_mode mode, bool force_ssd) +view_set_decorations(struct view *view, enum ssd_mode mode, bool force_ssd) { assert(view); if (force_ssd || view_wants_decorations(view) - || mode < view->ssd_mode) { + || mode < view_get_ssd_mode(view)) { view_set_ssd_mode(view, mode); } } @@ -1591,9 +1566,10 @@ view_toggle_decorations(struct view *view) { assert(view); - if (rc.ssd_keep_border && view->ssd_mode == LAB_SSD_MODE_FULL) { + enum ssd_mode mode = view_get_ssd_mode(view); + if (rc.ssd_keep_border && mode == LAB_SSD_MODE_FULL) { view_set_ssd_mode(view, LAB_SSD_MODE_BORDER); - } else if (view->ssd_mode != LAB_SSD_MODE_NONE) { + } else if (mode != LAB_SSD_MODE_NONE) { view_set_ssd_mode(view, LAB_SSD_MODE_NONE); } else { view_set_ssd_mode(view, LAB_SSD_MODE_FULL); @@ -1680,23 +1656,27 @@ undecorate(struct view *view) view->ssd = NULL; } -bool -view_titlebar_visible(struct view *view) +enum ssd_mode +view_get_ssd_mode(struct view *view) { - if (view->maximized == VIEW_AXIS_BOTH - && rc.hide_maximized_window_titlebar) { - return false; + assert(view); + + if (!view->ssd_enabled) { + return LAB_SSD_MODE_NONE; + } else if (view->ssd_titlebar_hidden) { + return LAB_SSD_MODE_BORDER; + } else { + return LAB_SSD_MODE_FULL; } - return view->ssd_mode == LAB_SSD_MODE_FULL; } void -view_set_ssd_mode(struct view *view, enum lab_ssd_mode mode) +view_set_ssd_mode(struct view *view, enum ssd_mode mode) { assert(view); if (view->shaded || view->fullscreen - || mode == view->ssd_mode) { + || mode == view_get_ssd_mode(view)) { return; } @@ -1704,11 +1684,12 @@ view_set_ssd_mode(struct view *view, enum lab_ssd_mode mode) * Set these first since they are referenced * within the call tree of ssd_create() and ssd_thickness() */ - view->ssd_mode = mode; + view->ssd_enabled = mode != LAB_SSD_MODE_NONE; + view->ssd_titlebar_hidden = mode != LAB_SSD_MODE_FULL; - if (mode) { + if (view->ssd_enabled) { decorate(view); - ssd_set_titlebar(view->ssd, view_titlebar_visible(view)); + ssd_set_titlebar(view->ssd, !view->ssd_titlebar_hidden); } else { undecorate(view); } @@ -1736,7 +1717,7 @@ set_fullscreen(struct view *view, bool fullscreen) } /* Hide decorations when going fullscreen */ - if (fullscreen && view->ssd_mode) { + if (fullscreen && view->ssd_enabled) { undecorate(view); } @@ -1748,7 +1729,7 @@ set_fullscreen(struct view *view, bool fullscreen) wl_signal_emit_mutable(&view->events.fullscreened, NULL); /* Re-show decorations when no longer fullscreen */ - if (!fullscreen && view->ssd_mode) { + if (!fullscreen && view->ssd_enabled) { decorate(view); } @@ -1812,12 +1793,8 @@ update_last_layout_geometry(struct view *view) if (view_is_floating(view)) { view->last_layout_geometry = view->pending; - } else if (!wlr_box_empty(&view->natural_geometry)) { - view->last_layout_geometry = view->natural_geometry; } else { - /* e.g. initially-maximized window */ - view->last_layout_geometry = - view_get_fallback_natural_geometry(view); + view->last_layout_geometry = view->natural_geometry; } } @@ -1979,7 +1956,7 @@ shift_view_to_usable_1d(int size, } void -view_move_to_edge(struct view *view, enum lab_edge direction, bool snap_to_windows) +view_move_to_edge(struct view *view, enum view_edge direction, bool snap_to_windows) { assert(view); if (!output_is_usable(view->output)) { @@ -2023,18 +2000,18 @@ view_move_to_edge(struct view *view, enum lab_edge direction, bool snap_to_windo int destination_y = view->pending.y; /* Compute the new position in the direction of motion */ - direction = lab_edge_invert(direction); + direction = view_edge_invert(direction); switch (direction) { - case LAB_EDGE_LEFT: + case VIEW_EDGE_LEFT: destination_x = left; break; - case LAB_EDGE_RIGHT: + case VIEW_EDGE_RIGHT: destination_x = right - view->pending.width; break; - case LAB_EDGE_TOP: + case VIEW_EDGE_UP: destination_y = top; break; - case LAB_EDGE_BOTTOM: + case VIEW_EDGE_DOWN: destination_y = bottom - view_effective_height(view, /* use_pending */ true); break; @@ -2062,7 +2039,7 @@ view_move_to_edge(struct view *view, enum lab_edge direction, bool snap_to_windo } void -view_grow_to_edge(struct view *view, enum lab_edge direction) +view_grow_to_edge(struct view *view, enum view_edge direction) { assert(view); /* TODO: allow grow to edge if maximized along the other axis */ @@ -2083,7 +2060,7 @@ view_grow_to_edge(struct view *view, enum lab_edge direction) } void -view_shrink_to_edge(struct view *view, enum lab_edge direction) +view_shrink_to_edge(struct view *view, enum view_edge direction) { assert(view); @@ -2123,7 +2100,28 @@ view_axis_parse(const char *direction) } } -enum lab_placement_policy +enum view_edge +view_edge_parse(const char *direction) +{ + if (!direction) { + return VIEW_EDGE_INVALID; + } + if (!strcasecmp(direction, "left")) { + return VIEW_EDGE_LEFT; + } else if (!strcasecmp(direction, "up")) { + return VIEW_EDGE_UP; + } else if (!strcasecmp(direction, "right")) { + return VIEW_EDGE_RIGHT; + } else if (!strcasecmp(direction, "down")) { + return VIEW_EDGE_DOWN; + } else if (!strcasecmp(direction, "center")) { + return VIEW_EDGE_CENTER; + } else { + return VIEW_EDGE_INVALID; + } +} + +enum view_placement_policy view_placement_parse(const char *policy) { if (!policy) { @@ -2144,8 +2142,8 @@ view_placement_parse(const char *policy) } void -view_snap_to_edge(struct view *view, enum lab_edge edge, - bool across_outputs, bool combine, bool store_natural_geometry) +view_snap_to_edge(struct view *view, enum view_edge edge, + bool across_outputs, bool store_natural_geometry) { assert(view); @@ -2161,46 +2159,25 @@ view_snap_to_edge(struct view *view, enum lab_edge edge, view_set_shade(view, false); - if (lab_edge_is_cardinal(edge) && view->maximized == VIEW_AXIS_NONE - && view->tiled != LAB_EDGE_CENTER) { - enum lab_edge invert_edge = lab_edge_invert(edge); - /* Represents axis of snapping direction */ - enum lab_edge parallel_mask = edge | invert_edge; - /* - * The vector view->tiled is split to components - * parallel/orthogonal to snapping direction. For example, - * view->tiled=TOP_LEFT is split to parallel_tiled=TOP and - * orthogonal_tiled=LEFT when edge=TOP or edge=BOTTOM. - */ - enum lab_edge parallel_tiled = view->tiled & parallel_mask; - enum lab_edge orthogonal_tiled = view->tiled & ~parallel_mask; + if (across_outputs && view->tiled == edge && view->maximized == VIEW_AXIS_NONE) { + /* We are already tiled for this edge; try to switch outputs */ + output = output_get_adjacent(view->output, edge, /* wrap */ false); - if (across_outputs && view->tiled == edge) { + if (!output) { /* - * E.g. when window is tiled to up and being snapped - * to up again, move it to the output above and tile - * it to down. + * No more output to move to + * + * We re-apply the tiled geometry without changing any + * state because the window might have been moved away + * (and thus got untiled) and then snapped back to the + * original edge. */ - output = output_get_adjacent(view->output, edge, - /* wrap */ false); - if (!output) { - return; - } - edge = invert_edge; - } else if (combine && parallel_tiled == invert_edge - && orthogonal_tiled != LAB_EDGE_NONE) { - /* - * E.g. when window is tiled to downleft/downright and - * being snapped to up, tile it to left/right. - */ - edge = view->tiled & ~parallel_mask; - } else if (combine && parallel_tiled == LAB_EDGE_NONE) { - /* - * E.g. when window is tiled to left/right and being - * snapped to up, tile it to upleft/upright. - */ - edge = view->tiled | edge; + view_apply_tiled_geometry(view); + return; } + + /* When switching outputs, jump to the opposite edge */ + edge = view_edge_invert(edge); } if (view->maximized != VIEW_AXIS_NONE) { @@ -2347,6 +2324,25 @@ view_move_to_back(struct view *view) desktop_update_top_layer_visibility(view->server); } +struct view * +view_get_root(struct view *view) +{ + assert(view); + if (view->impl->get_root) { + return view->impl->get_root(view); + } + return view; +} + +void +view_append_children(struct view *view, struct wl_array *children) +{ + assert(view); + if (view->impl->append_children) { + view->impl->append_children(view, children); + } +} + struct view * view_get_modal_dialog(struct view *view) { @@ -2385,36 +2381,31 @@ view_has_strut_partial(struct view *view) view->impl->has_strut_partial(view); } -void -view_set_title(struct view *view, const char *title) +/* Note: It is safe to assume that this function never returns NULL */ +const char * +view_get_string_prop(struct view *view, const char *prop) { assert(view); - if (!title) { - title = ""; + assert(prop); + if (view->impl->get_string_prop) { + const char *ret = view->impl->get_string_prop(view, prop); + return ret ? ret : ""; } + return ""; +} - if (!strcmp(view->title, title)) { - return; - } - xstrdup_replace(view->title, title); - +void +view_update_title(struct view *view) +{ + assert(view); ssd_update_title(view->ssd); wl_signal_emit_mutable(&view->events.new_title, NULL); } void -view_set_app_id(struct view *view, const char *app_id) +view_update_app_id(struct view *view) { assert(view); - if (!app_id) { - app_id = ""; - } - - if (!strcmp(view->app_id, app_id)) { - return; - } - xstrdup_replace(view->app_id, app_id); - wl_signal_emit_mutable(&view->events.new_app_id, NULL); } @@ -2422,30 +2413,40 @@ void view_reload_ssd(struct view *view) { assert(view); - if (view->ssd_mode && !view->fullscreen) { + if (view->ssd_enabled && !view->fullscreen) { undecorate(view); decorate(view); } } +int +view_get_min_width(void) +{ + int button_count_left = wl_list_length(&rc.title_buttons_left); + int button_count_right = wl_list_length(&rc.title_buttons_right); + return (rc.theme->window_button_width * (button_count_left + button_count_right)) + + (rc.theme->window_button_spacing * MAX((button_count_right - 1), 0)) + + (rc.theme->window_button_spacing * MAX((button_count_left - 1), 0)) + + (2 * rc.theme->window_titlebar_padding_width); +} + void view_toggle_keybinds(struct view *view) { assert(view); view->inhibits_keybinds = !view->inhibits_keybinds; + if (view->inhibits_keybinds) { + view->server->seat.nr_inhibited_keybind_views++; + } else { + view->server->seat.nr_inhibited_keybind_views--; + } - if (view->ssd_mode) { + if (view->ssd_enabled) { ssd_enable_keybind_inhibit_indicator(view->ssd, view->inhibits_keybinds); } } -bool -view_inhibits_actions(struct view *view, struct wl_list *actions) -{ - return view && view->inhibits_keybinds && !actions_contain_toggle_keybinds(actions); -} - void mappable_connect(struct mappable *mappable, struct wlr_surface *surface, wl_notify_func_t notify_map, wl_notify_func_t notify_unmap) @@ -2516,7 +2517,7 @@ view_set_shade(struct view *view, bool shaded) } /* Views without a title-bar or SSD cannot be shaded */ - if (shaded && (!view->ssd || !view_titlebar_visible(view))) { + if (shaded && (!view->ssd || view->ssd_titlebar_hidden)) { return; } @@ -2567,9 +2568,6 @@ view_init(struct view *view) wl_signal_init(&view->events.activated); wl_signal_init(&view->events.set_icon); wl_signal_init(&view->events.destroy); - - view->title = xstrdup(""); - view->app_id = xstrdup(""); } void @@ -2593,9 +2591,6 @@ view_destroy(struct view *view) wl_list_remove(&view->set_title.link); wl_list_remove(&view->destroy.link); - zfree(view->title); - zfree(view->app_id); - if (view->foreign_toplevel) { foreign_toplevel_destroy(view->foreign_toplevel); view->foreign_toplevel = NULL; @@ -2622,6 +2617,11 @@ view_destroy(struct view *view) zfree(view->tiled_region_evacuate); } + if (view->inhibits_keybinds) { + view->inhibits_keybinds = false; + server->seat.nr_inhibited_keybind_views--; + } + osd_on_view_destroy(view); undecorate(view); @@ -2652,16 +2652,6 @@ view_destroy(struct view *view) view->scene_tree = NULL; } - assert(wl_list_empty(&view->events.new_app_id.listener_list)); - assert(wl_list_empty(&view->events.new_title.listener_list)); - assert(wl_list_empty(&view->events.new_outputs.listener_list)); - assert(wl_list_empty(&view->events.maximized.listener_list)); - 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.set_icon.listener_list)); - assert(wl_list_empty(&view->events.destroy.listener_list)); - /* Remove view from server->views */ wl_list_remove(&view->link); free(view); diff --git a/src/window-rules.c b/src/window-rules.c index 0b8f1101..fa21f749 100644 --- a/src/window-rules.c +++ b/src/window-rules.c @@ -1,13 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "window-rules.h" #include #include +#include +#include #include +#include #include "action.h" +#include "common/match.h" #include "config/rcxml.h" #include "labwc.h" #include "view.h" +#include "window-rules.h" static bool other_instances_exist(struct view *self, struct view_query *query) @@ -32,9 +36,7 @@ view_matches_criteria(struct window_rule *rule, struct view *view) .window_type = rule->window_type, .sandbox_engine = rule->sandbox_engine, .sandbox_app_id = rule->sandbox_app_id, - /* Must be synced with view_query_create() */ .maximized = VIEW_AXIS_INVALID, - .decoration = LAB_SSD_MODE_INVALID, }; if (rule->match_once && other_instances_exist(view, &query)) { diff --git a/src/workspaces.c b/src/workspaces.c index 43f19b18..45b2f918 100644 --- a/src/workspaces.c +++ b/src/workspaces.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "workspaces.h" #include #include #include @@ -8,21 +7,19 @@ #include #include #include -#include -#include +#include "config.h" #include "buffer.h" #include "common/font.h" #include "common/graphic-helpers.h" #include "common/list.h" #include "common/mem.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 "theme.h" #include "view.h" +#include "workspaces.h" +#include "xwayland.h" #define COSMIC_WORKSPACES_VERSION 1 #define EXT_WORKSPACES_VERSION 1 @@ -603,7 +600,7 @@ workspaces_reconfigure(struct server *server) } /* # of configured workspaces decreased */ - overlay_finish(&server->seat); + overlay_hide(&server->seat); struct workspace *first_workspace = wl_container_of(server->workspaces.all.next, first_workspace, link); diff --git a/src/xdg-popup.c b/src/xdg-popup.c index aded80b5..d4daadd6 100644 --- a/src/xdg-popup.c +++ b/src/xdg-popup.c @@ -7,13 +7,10 @@ * - keeping non-layer-shell xdg-popups outside the layers.c code */ -#include -#include #include "common/macros.h" #include "common/mem.h" #include "labwc.h" #include "node.h" -#include "output.h" #include "view.h" struct xdg_popup { @@ -165,5 +162,5 @@ xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup) wlr_popup->base->surface->data = wlr_scene_xdg_surface_create(parent_tree, wlr_popup->base); node_descriptor_create(wlr_popup->base->surface->data, - LAB_NODE_XDG_POPUP, view, /*data*/ NULL); + LAB_NODE_DESC_XDG_POPUP, view); } diff --git a/src/xdg.c b/src/xdg.c index 83ab9bb9..a2fbf1b8 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -1,24 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-only #include -#include #include -#include -#include -#include -#include #include #include "buffer.h" #include "common/array.h" #include "common/macros.h" #include "common/mem.h" -#include "config/rcxml.h" #include "decorations.h" -#include "foreign-toplevel/foreign.h" +#include "foreign-toplevel.h" #include "labwc.h" #include "menu/menu.h" #include "node.h" -#include "output.h" #include "snap-constraints.h" #include "view.h" #include "view-impl-common.h" @@ -70,7 +63,7 @@ xdg_toplevel_view_get_size_hints(struct view *view) static bool xdg_toplevel_view_contains_window_type(struct view *view, - enum lab_window_type window_type) + enum window_type window_type) { assert(view); @@ -82,9 +75,9 @@ xdg_toplevel_view_contains_window_type(struct view *view, || toplevel->parent; switch (window_type) { - case LAB_WINDOW_TYPE_NORMAL: + case NET_WM_WINDOW_TYPE_NORMAL: return !is_dialog; - case LAB_WINDOW_TYPE_DIALOG: + case NET_WM_WINDOW_TYPE_DIALOG: return is_dialog; default: return false; @@ -217,7 +210,7 @@ handle_commit(struct wl_listener *listener, void *data) && extent.height == view->pending.height) { wlr_log(WLR_DEBUG, "window geometry for client (%s) " "appears to be incorrect - ignoring", - view->app_id); + view_get_string_prop(view, "app_id")); size = extent; /* Use surface extent instead */ } } @@ -284,21 +277,14 @@ handle_configure_timeout(void *data) assert(view->pending_configure_serial > 0); assert(view->pending_configure_timeout); + const char *app_id = view_get_string_prop(view, "app_id"); wlr_log(WLR_INFO, "client (%s) did not respond to configure request " - "in %d ms", view->app_id, CONFIGURE_TIMEOUT_MS); + "in %d ms", app_id, CONFIGURE_TIMEOUT_MS); wl_event_source_remove(view->pending_configure_timeout); view->pending_configure_serial = 0; view->pending_configure_timeout = NULL; - /* - * No need to do anything else if the view is just being slow to - * map - the map handler will take care of the positioning. - */ - if (!view->mapped) { - return 0; /* ignored per wl_event_loop docs */ - } - bool empty_pending = wlr_box_empty(&view->pending); if (empty_pending || view->pending.x != view->current.x || view->pending.y != view->current.y) { @@ -325,13 +311,6 @@ handle_configure_timeout(void *data) wlr_log(WLR_INFO, "using fallback position"); view->pending.x = VIEW_FALLBACK_X; view->pending.y = VIEW_FALLBACK_Y; - /* At least try to keep it on the same output */ - if (output_is_usable(view->output)) { - struct wlr_box box = - output_usable_area_in_layout_coords(view->output); - view->pending.x += box.x; - view->pending.y += box.y; - } } view->current.x = view->pending.x; view->current.y = view->pending.y; @@ -401,7 +380,7 @@ handle_request_move(struct wl_listener *listener, void *data) */ struct view *view = wl_container_of(listener, view, request_move); if (view == view->server->seat.pressed.view) { - interactive_begin(view, LAB_INPUT_STATE_MOVE, LAB_EDGE_NONE); + interactive_begin(view, LAB_INPUT_STATE_MOVE, 0); } } @@ -492,9 +471,7 @@ static void handle_set_title(struct wl_listener *listener, void *data) { struct view *view = wl_container_of(listener, view, set_title); - struct wlr_xdg_toplevel *toplevel = xdg_toplevel_from_view(view); - - view_set_title(view, toplevel->title); + view_update_title(view); } static void @@ -503,9 +480,7 @@ handle_set_app_id(struct wl_listener *listener, void *data) struct xdg_toplevel_view *xdg_toplevel_view = wl_container_of(listener, xdg_toplevel_view, set_app_id); struct view *view = &xdg_toplevel_view->base; - struct wlr_xdg_toplevel *toplevel = xdg_toplevel_from_view(view); - - view_set_app_id(view, toplevel->app_id); + view_update_app_id(view); } static void @@ -513,8 +488,6 @@ xdg_toplevel_view_configure(struct view *view, struct wlr_box geo) { uint32_t serial = 0; - struct wlr_xdg_toplevel *toplevel = xdg_toplevel_from_view(view); - /* * We do not need to send a configure request unless the size * changed (wayland has no notion of a global position). If the @@ -523,20 +496,8 @@ 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); - } else { - /* - * This may happen, for example, when a panel resizes because a - * foreign-toplevel has been destroyed. This would then trigger - * a call to desktop_arrange_all_views() which in turn explicitly - * also tries to configure unmapped surfaces. This is fine when - * trying to resize surfaces before they are mapped but it will - * also try to resize surfaces which have been unmapped but their - * associated struct view has not been destroyed yet. - */ - wlr_log(WLR_DEBUG, "Preventing configure of uninitialized surface"); - } + serial = wlr_xdg_toplevel_set_size(xdg_toplevel_from_view(view), + geo.width, geo.height); } view->pending = geo; @@ -558,10 +519,6 @@ xdg_toplevel_view_close(struct view *view) static void xdg_toplevel_view_maximize(struct view *view, enum view_axis maximized) { - if (!xdg_toplevel_from_view(view)->base->initialized) { - wlr_log(WLR_DEBUG, "Prevented maximize notification for a non-intialized view"); - return; - } uint32_t serial = wlr_xdg_toplevel_set_maximized( xdg_toplevel_from_view(view), maximized == VIEW_AXIS_BOTH); if (serial > 0) { @@ -600,7 +557,8 @@ 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, &self->server->views, link) + { if (view == self) { continue; } @@ -618,25 +576,9 @@ xdg_toplevel_view_append_children(struct view *self, struct wl_array *children) } } -static bool -xdg_toplevel_view_is_modal_dialog(struct view *view) -{ - struct wlr_xdg_toplevel *toplevel = xdg_toplevel_from_view(view); - struct wlr_xdg_dialog_v1 *dialog = - wlr_xdg_dialog_v1_try_from_wlr_xdg_toplevel(toplevel); - if (!dialog) { - return false; - } - return dialog->modal; -} - static void xdg_toplevel_view_set_activated(struct view *view, bool activated) { - if (!xdg_toplevel_from_view(view)->base->initialized) { - wlr_log(WLR_DEBUG, "Prevented activating a non-intialized view"); - return; - } uint32_t serial = wlr_xdg_toplevel_set_activated( xdg_toplevel_from_view(view), activated); if (serial > 0) { @@ -647,10 +589,6 @@ xdg_toplevel_view_set_activated(struct view *view, bool activated) static void xdg_toplevel_view_set_fullscreen(struct view *view, bool fullscreen) { - if (!xdg_toplevel_from_view(view)->base->initialized) { - wlr_log(WLR_DEBUG, "Prevented fullscreening a non-intialized view"); - return; - } uint32_t serial = wlr_xdg_toplevel_set_fullscreen( xdg_toplevel_from_view(view), fullscreen); if (serial > 0) { @@ -666,12 +604,7 @@ xdg_toplevel_view_notify_tiled(struct view *view) return; } - if (!xdg_toplevel_from_view(view)->base->initialized) { - wlr_log(WLR_DEBUG, "Prevented tiling notification for a non-intialized view"); - return; - } - - enum lab_edge edge = LAB_EDGE_NONE; + enum wlr_edges edge = WLR_EDGE_NONE; bool want_edge = rc.snap_tiling_events_mode & LAB_TILING_EVENTS_EDGE; bool want_region = rc.snap_tiling_events_mode & LAB_TILING_EVENTS_REGION; @@ -682,33 +615,27 @@ xdg_toplevel_view_notify_tiled(struct view *view) */ if (want_edge) { switch (view->tiled) { - case LAB_EDGE_LEFT: - edge = LAB_EDGES_EXCEPT_RIGHT; + case VIEW_EDGE_LEFT: + edge = WLR_EDGE_LEFT | WLR_EDGE_TOP | WLR_EDGE_BOTTOM; break; - case LAB_EDGE_RIGHT: - edge = LAB_EDGES_EXCEPT_LEFT; + case VIEW_EDGE_RIGHT: + edge = WLR_EDGE_RIGHT | WLR_EDGE_TOP | WLR_EDGE_BOTTOM; break; - case LAB_EDGE_TOP: - edge = LAB_EDGES_EXCEPT_BOTTOM; + case VIEW_EDGE_UP: + edge = WLR_EDGE_TOP | WLR_EDGE_LEFT | WLR_EDGE_RIGHT; break; - case LAB_EDGE_BOTTOM: - edge = LAB_EDGES_EXCEPT_TOP; + case VIEW_EDGE_DOWN: + edge = WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT; break; - case LAB_EDGES_TOP_LEFT: - case LAB_EDGES_TOP_RIGHT: - case LAB_EDGES_BOTTOM_LEFT: - case LAB_EDGES_BOTTOM_RIGHT: - edge = view->tiled; - break; - /* TODO: LAB_EDGE_CENTER? */ default: - edge = LAB_EDGE_NONE; + edge = WLR_EDGE_NONE; } } if (want_region && view->tiled_region) { /* Region-snapped views are considered tiled on all edges */ - edge = LAB_EDGES_ALL; + edge = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | + WLR_EDGE_TOP | WLR_EDGE_BOTTOM; } uint32_t serial = @@ -756,6 +683,31 @@ set_initial_position(struct view *view) view_place_by_policy(view, /* allow_cursor */ true, rc.placement_policy); } +static const char * +xdg_toplevel_view_get_string_prop(struct view *view, const char *prop) +{ + struct xdg_toplevel_view *xdg_view = xdg_toplevel_view_from_view(view); + struct wlr_xdg_toplevel *xdg_toplevel = xdg_view->xdg_surface + ? xdg_view->xdg_surface->toplevel + : NULL; + if (!xdg_toplevel) { + /* + * This may happen due to a matchOnce rule when + * a view is destroyed while A-Tab is open. See + * https://github.com/labwc/labwc/issues/1082#issuecomment-1716137180 + */ + return ""; + } + + if (!strcmp(prop, "title")) { + return xdg_toplevel->title ? xdg_toplevel->title : ""; + } + if (!strcmp(prop, "app_id")) { + return xdg_toplevel->app_id ? xdg_toplevel->app_id : ""; + } + return ""; +} + static void init_foreign_toplevel(struct view *view) { @@ -875,6 +827,7 @@ xdg_view_get_pid(struct view *view) static const struct view_impl xdg_toplevel_view_impl = { .configure = xdg_toplevel_view_configure, .close = xdg_toplevel_view_close, + .get_string_prop = xdg_toplevel_view_get_string_prop, .map = xdg_toplevel_view_map, .set_activated = xdg_toplevel_view_set_activated, .set_fullscreen = xdg_toplevel_view_set_fullscreen, @@ -884,7 +837,6 @@ static const struct view_impl xdg_toplevel_view_impl = { .minimize = xdg_toplevel_view_minimize, .get_root = xdg_toplevel_view_get_root, .append_children = xdg_toplevel_view_append_children, - .is_modal_dialog = xdg_toplevel_view_is_modal_dialog, .get_size_hints = xdg_toplevel_view_get_size_hints, .contains_window_type = xdg_toplevel_view_contains_window_type, .get_pid = xdg_view_get_pid, @@ -1016,7 +968,7 @@ handle_new_xdg_toplevel(struct wl_listener *listener, void *data) } view->content_tree = tree; node_descriptor_create(&view->scene_tree->node, - LAB_NODE_VIEW, view, /*data*/ NULL); + LAB_NODE_DESC_VIEW, view); /* * The xdg_toplevel_decoration and kde_server_decoration protocols @@ -1072,6 +1024,9 @@ static void handle_xdg_toplevel_icon_set_icon(struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel_icon_manager_v1_set_icon_event *event = data; + + struct server *server = + wl_container_of(listener, server, xdg_toplevel_icon_set_icon); struct wlr_xdg_surface *xdg_surface = event->toplevel->base; struct view *view = xdg_surface->data; assert(view); @@ -1130,8 +1085,6 @@ xdg_shell_init(struct server *server) 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); } void diff --git a/src/xwayland-unmanaged.c b/src/xwayland-unmanaged.c index b003e4ec..2fc15cfe 100644 --- a/src/xwayland-unmanaged.c +++ b/src/xwayland-unmanaged.c @@ -1,8 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only #include -#include -#include -#include #include #include "common/list.h" #include "common/macros.h" diff --git a/src/xwayland.c b/src/xwayland.c index 6b408a78..a648877e 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -1,13 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "xwayland.h" #include #include -#include -#include -#include -#include -#include #include #include "buffer.h" #include "common/array.h" @@ -15,14 +9,15 @@ #include "common/mem.h" #include "config/rcxml.h" #include "config/session.h" -#include "foreign-toplevel/foreign.h" +#include "foreign-toplevel.h" #include "labwc.h" #include "node.h" -#include "output.h" +#include "ssd.h" #include "view.h" #include "view-impl-common.h" #include "window-rules.h" #include "workspaces.h" +#include "xwayland.h" enum atoms { ATOM_NET_WM_ICON = 0, @@ -40,57 +35,42 @@ static xcb_atom_t atoms[ATOM_COUNT] = {0}; static void xwayland_view_unmap(struct view *view, bool client_request); -static struct xwayland_view * -xwayland_view_from_view(struct view *view) -{ - assert(view->type == LAB_XWAYLAND_VIEW); - return (struct xwayland_view *)view; -} - -static struct wlr_xwayland_surface * -xwayland_surface_from_view(struct view *view) -{ - struct xwayland_view *xwayland_view = xwayland_view_from_view(view); - assert(xwayland_view->xwayland_surface); - return xwayland_view->xwayland_surface; -} - static bool xwayland_view_contains_window_type(struct view *view, - enum lab_window_type window_type) + enum window_type window_type) { /* Compile-time check that the enum types match */ - static_assert(LAB_WINDOW_TYPE_DESKTOP == + static_assert(NET_WM_WINDOW_TYPE_DESKTOP == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DESKTOP - && LAB_WINDOW_TYPE_DOCK == + && NET_WM_WINDOW_TYPE_DOCK == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DOCK - && LAB_WINDOW_TYPE_TOOLBAR == + && NET_WM_WINDOW_TYPE_TOOLBAR == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_TOOLBAR - && LAB_WINDOW_TYPE_MENU == + && NET_WM_WINDOW_TYPE_MENU == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_MENU - && LAB_WINDOW_TYPE_UTILITY == + && NET_WM_WINDOW_TYPE_UTILITY == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_UTILITY - && LAB_WINDOW_TYPE_SPLASH == + && NET_WM_WINDOW_TYPE_SPLASH == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_SPLASH - && LAB_WINDOW_TYPE_DIALOG == + && NET_WM_WINDOW_TYPE_DIALOG == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DIALOG - && LAB_WINDOW_TYPE_DROPDOWN_MENU == + && NET_WM_WINDOW_TYPE_DROPDOWN_MENU == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DROPDOWN_MENU - && LAB_WINDOW_TYPE_POPUP_MENU == + && NET_WM_WINDOW_TYPE_POPUP_MENU == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_POPUP_MENU - && LAB_WINDOW_TYPE_TOOLTIP == + && NET_WM_WINDOW_TYPE_TOOLTIP == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_TOOLTIP - && LAB_WINDOW_TYPE_NOTIFICATION == + && NET_WM_WINDOW_TYPE_NOTIFICATION == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_NOTIFICATION - && LAB_WINDOW_TYPE_COMBO == + && NET_WM_WINDOW_TYPE_COMBO == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_COMBO - && LAB_WINDOW_TYPE_DND == + && NET_WM_WINDOW_TYPE_DND == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DND - && LAB_WINDOW_TYPE_NORMAL == + && NET_WM_WINDOW_TYPE_NORMAL == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_NORMAL - && LAB_WINDOW_TYPE_LEN == + && WINDOW_TYPE_LEN == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_NORMAL + 1, - "lab_window_type does not match wlr_xwayland_net_wm_window_type"); + "enum window_type does not match wlr_xwayland_net_wm_window_type"); assert(view); struct wlr_xwayland_surface *surface = xwayland_surface_from_view(view); @@ -201,6 +181,21 @@ top_parent_of(struct view *view) return s; } +static struct xwayland_view * +xwayland_view_from_view(struct view *view) +{ + assert(view->type == LAB_XWAYLAND_VIEW); + return (struct xwayland_view *)view; +} + +struct wlr_xwayland_surface * +xwayland_surface_from_view(struct view *view) +{ + struct xwayland_view *xwayland_view = xwayland_view_from_view(view); + assert(xwayland_view->xwayland_surface); + return xwayland_view->xwayland_surface; +} + static void ensure_initial_geometry_and_output(struct view *view) { @@ -281,7 +276,7 @@ handle_request_move(struct wl_listener *listener, void *data) */ struct view *view = wl_container_of(listener, view, request_move); if (view == view->server->seat.pressed.view) { - interactive_begin(view, LAB_INPUT_STATE_MOVE, LAB_EDGE_NONE); + interactive_begin(view, LAB_INPUT_STATE_MOVE, 0); } } @@ -498,8 +493,7 @@ static void handle_set_title(struct wl_listener *listener, void *data) { struct view *view = wl_container_of(listener, view, set_title); - struct xwayland_view *xwayland_view = xwayland_view_from_view(view); - view_set_title(view, xwayland_view->xwayland_surface->title); + view_update_title(view); } static void @@ -508,7 +502,35 @@ handle_set_class(struct wl_listener *listener, void *data) struct xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, set_class); struct view *view = &xwayland_view->base; + view_update_app_id(view); +} +static void +xwayland_view_close(struct view *view) +{ + wlr_xwayland_surface_close(xwayland_surface_from_view(view)); +} + +static const char * +xwayland_view_get_string_prop(struct view *view, const char *prop) +{ + struct xwayland_view *xwayland_view = xwayland_view_from_view(view); + struct wlr_xwayland_surface *xwayland_surface = xwayland_view->xwayland_surface; + if (!xwayland_surface) { + /* + * This may happen due to a matchOnce rule when + * a view is destroyed while A-Tab is open. See + * https://github.com/labwc/labwc/issues/1082#issuecomment-1716137180 + */ + return ""; + } + + if (!strcmp(prop, "title")) { + return xwayland_surface->title ? xwayland_surface->title : ""; + } + if (!strcmp(prop, "class")) { + return xwayland_surface->class ? xwayland_surface->class : ""; + } /* * Use the WM_CLASS 'instance' (1st string) for the app_id. Per * ICCCM, this is usually "the trailing part of the name used to @@ -518,13 +540,10 @@ handle_set_class(struct wl_listener *listener, void *data) * 'instance' except for being capitalized. We want lowercase * here since we use the app_id for icon lookups. */ - view_set_app_id(view, xwayland_view->xwayland_surface->instance); -} - -static void -xwayland_view_close(struct view *view) -{ - wlr_xwayland_surface_close(xwayland_surface_from_view(view)); + if (!strcmp(prop, "app_id")) { + return xwayland_surface->instance ? xwayland_surface->instance : ""; + } + return ""; } static void @@ -605,8 +624,8 @@ update_icon(struct xwayland_view *xwayland_view) struct wl_array buffers; wl_array_init(&buffers); for (; iter.rem; xcb_ewmh_get_wm_icon_next(&iter)) { - size_t stride = iter.width * 4; - uint32_t *buf = xzalloc(iter.height * stride); + size_t stride = iter.height * 4; + uint32_t *buf = xzalloc(iter.width * stride); /* Pre-multiply alpha */ for (uint32_t y = 0; y < iter.height; y++) { @@ -725,15 +744,17 @@ handle_map_request(struct wl_listener *listener, void *data) static void check_natural_geometry(struct view *view) { + int min_width = view_get_min_width(); + /* * Some applications (example: Thonny) don't set a reasonable * un-maximized size when started maximized. Try to detect this * and set a fallback size. */ if (!view_is_floating(view) - && (view->natural_geometry.width < LAB_MIN_VIEW_WIDTH + && (view->natural_geometry.width < min_width || view->natural_geometry.height < LAB_MIN_VIEW_HEIGHT)) { - view->natural_geometry = view_get_fallback_natural_geometry(view); + view_set_fallback_natural_geometry(view); } } @@ -959,7 +980,8 @@ 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, &self->server->views, link) + { if (view == self) { continue; } @@ -1026,6 +1048,7 @@ xwayland_view_get_pid(struct view *view) static const struct view_impl xwayland_view_impl = { .configure = xwayland_view_configure, .close = xwayland_view_close, + .get_string_prop = xwayland_view_get_string_prop, .map = xwayland_view_map, .set_activated = xwayland_view_set_activated, .set_fullscreen = xwayland_view_set_fullscreen, @@ -1067,8 +1090,7 @@ xwayland_view_create(struct server *server, view->workspace = server->workspaces.current; view->scene_tree = wlr_scene_tree_create(view->workspace->tree); - node_descriptor_create(&view->scene_tree->node, - LAB_NODE_VIEW, view, /*data*/ NULL); + node_descriptor_create(&view->scene_tree->node, LAB_NODE_DESC_VIEW, view); CONNECT_SIGNAL(xsurface, view, destroy); CONNECT_SIGNAL(xsurface, view, request_minimize); diff --git a/subprojects/libsfdo.wrap b/subprojects/libsfdo.wrap index d05264db..22df1d6a 100644 --- a/subprojects/libsfdo.wrap +++ b/subprojects/libsfdo.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://gitlab.freedesktop.org/vyivel/libsfdo.git -revision = v0.1.4 +revision = v0.1.3 [provide] dependency_names = libsfdo-basedir, libsfdo-desktop, libsfdo-icon diff --git a/t/buf-simple.c b/t/buf-simple.c index d3ebaeb9..ff22aa10 100644 --- a/t/buf-simple.c +++ b/t/buf-simple.c @@ -12,6 +12,8 @@ static void test_expand_title(void **state) { + (void)state; + struct buf s = BUF_INIT; char TEMPLATE[] = "foo ~/bar"; @@ -56,6 +58,8 @@ test_expand_title(void **state) static void test_buf_add_fmt(void **state) { + (void)state; + struct buf s = BUF_INIT; buf_add(&s, "foo"); @@ -68,12 +72,9 @@ test_buf_add_fmt(void **state) static void test_buf_add_char(void **state) { - static const char long_string[] = - "123456789012345678901234567890123456789012345678901234567890 " - "123456789012345678901234567890123456789012345678901234567890 " - "123456789012345678901234567890123456789012345678901234567890 " - "123456789012345678901234567890123456789012345678901234567890 " - "123456789012345678901234567890123456789012345678901234567890 "; + (void)state; + + const char long_string[] = "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; size_t len = strlen(long_string); /* diff --git a/t/meson.build b/t/meson.build index 07d55a1f..eb997fe2 100644 --- a/t/meson.build +++ b/t/meson.build @@ -1,27 +1,17 @@ -test_deps = [ - dep_cmocka, - glib, - xml2, - wlroots, -] - test_lib = static_library( 'test_lib', sources: files( '../src/common/buf.c', '../src/common/mem.c', - '../src/common/string-helpers.c', - '../src/common/xml.c', - '../src/common/parse-bool.c', + '../src/common/string-helpers.c' ), include_directories: [labwc_inc], - dependencies: test_deps, + dependencies: [dep_cmocka], ) tests = [ 'buf-simple', 'str', - 'xml', ] foreach t : tests @@ -32,7 +22,6 @@ foreach t : tests sources: '@0@.c'.format(t), include_directories: [labwc_inc], link_with: [test_lib], - dependencies: test_deps, ), is_parallel: false, ) diff --git a/t/str.c b/t/str.c index 159f95d3..79debb1c 100644 --- a/t/str.c +++ b/t/str.c @@ -11,6 +11,8 @@ static void test_str_starts_with(void **state) { + (void)state; + assert_true(str_starts_with(" foo", 'f', " \t\r\n")); assert_true(str_starts_with("f", 'f', " \t\r\n")); assert_false(str_starts_with(" foo", '<', " ")); diff --git a/t/xml.c b/t/xml.c deleted file mode 100644 index f4c70744..00000000 --- a/t/xml.c +++ /dev/null @@ -1,126 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#define _POSIX_C_SOURCE 200809L -#include -#include -#include -#include -#include -#include "common/macros.h" -#include "common/xml.h" - -struct test_case { - const char *before, *after; -} test_cases[] = {{ - "", - - "" - "" - "ShowMenu" - "root-menu" - "" - "1" - "2" - "" - "" - "" -}, { - "", - - "" - "111" - "222" - "" -}, { - "", - - "" - "111" - "222" - "333" - "" -}, { - "", - - "" - "111" - "222" - "333" - "" -}, { - "", - - "" - "111" - "222" - "" -}, { - "", - - "" - "111" - "222" - "333" - "", -}, { - "" - "" - "" - "", - - "" - "111" - "111" - "", -}, { - "" - "222" - "", - - "" - "111" - "222" - "", -}, { - "" - "111" - "111" - "", - - "" - "111" - "111" - "", -}, { - "", - - "111" -}}; - -static void -test_lab_xml_expand_dotted_attributes(void **state) -{ - for (size_t i = 0; i < ARRAY_SIZE(test_cases); i++) { - xmlDoc *doc = xmlReadDoc((xmlChar *)test_cases[i].before, - NULL, NULL, 0); - xmlNode *root = xmlDocGetRootElement(doc); - - lab_xml_expand_dotted_attributes(root); - - xmlBuffer *buf = xmlBufferCreate(); - xmlNodeDump(buf, root->doc, root, 0, 0); - assert_string_equal(test_cases[i].after, (char *)xmlBufferContent(buf)); - xmlBufferFree(buf); - - xmlFreeDoc(doc); - } -} - -int main(int argc, char **argv) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_lab_xml_expand_dotted_attributes), - }; - - return cmocka_run_group_tests(tests, NULL, NULL); -}