Merge branch 'master' into master

This commit is contained in:
Jack Zeal 2026-05-27 03:36:48 +00:00 committed by GitHub
commit 6885d5b887
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
70 changed files with 1979 additions and 321 deletions

View file

@ -20,6 +20,7 @@ on:
- 'clients/**' - 'clients/**'
- 't/**' - 't/**'
- 'scripts/**' - 'scripts/**'
- 'docs/**'
- '.github/workflows/**' - '.github/workflows/**'
jobs: jobs:
@ -121,7 +122,7 @@ jobs:
xbps-install -Syu xbps-install -Syu
xbps-install -y git meson gcc clang pkg-config scdoc \ xbps-install -y git meson gcc clang pkg-config scdoc \
cairo-devel glib-devel libpng-devel librsvg-devel libxml2-devel \ cairo-devel glib-devel libpng-devel librsvg-devel libxml2-devel \
pango-devel wlroots0.19-devel gdb bash xorg-server-xwayland \ pango-devel wlroots0.20-devel gdb bash xorg-server-xwayland \
dejavu-fonts-ttf libsfdo-devel foot hwids dejavu-fonts-ttf libsfdo-devel foot hwids
# These builds are executed on all runners # These builds are executed on all runners

260
NEWS.md
View file

@ -9,7 +9,8 @@ The format is based on [Keep a Changelog]
| Date | All Changes | wlroots version | lines-of-code | | Date | All Changes | wlroots version | lines-of-code |
|------------|---------------|-----------------|---------------| |------------|---------------|-----------------|---------------|
| 2026-03-31 | [unreleased] | 0.20.0 | 27402 | | 2026-05-25 | [0.20.0] | 0.20.1 | 28313 |
| 2026-04-17 | [0.9.7] | 0.19.2 | 29277 |
| 2026-03-15 | [0.9.6] | 0.19.2 | 29271 | | 2026-03-15 | [0.9.6] | 0.19.2 | 29271 |
| 2026-03-04 | [0.9.5] | 0.19.2 | 29251 | | 2026-03-04 | [0.9.5] | 0.19.2 | 29251 |
| 2026-02-27 | [0.9.4] | 0.19.2 | 29225 | | 2026-02-27 | [0.9.4] | 0.19.2 | 29225 |
@ -44,6 +45,8 @@ The format is based on [Keep a Changelog]
| 2021-03-05 | [0.1.0] | 0.12.0 | 4627 | | 2021-03-05 | [0.1.0] | 0.12.0 | 4627 |
[unreleased]: NEWS.md#unreleased [unreleased]: NEWS.md#unreleased
[0.20.0]: NEWS.md#0200---2026-05-25
[0.9.7]: NEWS.md#097---2026-04-17
[0.9.6]: NEWS.md#096---2026-03-15 [0.9.6]: NEWS.md#096---2026-03-15
[0.9.5]: NEWS.md#095---2026-03-04 [0.9.5]: NEWS.md#095---2026-03-04
[0.9.4]: NEWS.md#094---2026-02-27 [0.9.4]: NEWS.md#094---2026-02-27
@ -77,6 +80,109 @@ The format is based on [Keep a Changelog]
[0.2.0]: NEWS.md#020---2021-04-15 [0.2.0]: NEWS.md#020---2021-04-15
[0.1.0]: NEWS.md#010---2021-03-05 [0.1.0]: NEWS.md#010---2021-03-05
## unreleased
[unreleased-commits]
## 0.20.0 - 2026-05-25
[0.20.0-commits]
This is the first release using wlroots-0.20 and therefore has an increased risk
of teething issues. Many thanks to @Consolatis for leading the effort to port
across [#2956].
In terms of new features, the most noteworthy ones include: (i) the frequently
requested show-desktop action; (ii) initial toplevel capture support to
screenshot specific windows; (iii) menu accelerators/shortcuts; and (iv) HDR10
support when running with the Vulkan renderer option.
The eagle-eyed amongst you will have noticed the sudden jump from labwc `0.9.7`
to `0.20.0`. The reason for this is to align the minor number to that of the
wlroots version against which the compositor is linked.
The 0.9.x series has turned into a maintenance branch named v0.9 with bug fixes
only, for anyone preferring to build with wlroots-0.19.
Note to maintainers:
- libinput >=1.26 is required in support of tablet tool pressure range
configuration.
- wlroots >=0.20.1 is required to avoid some bugs that we do not want labwc to
ship with. For details, see:
https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5325
### Added
- Partially support toplevel-capture. Note that the following are not yet
implemented: (i) XWayland child windows; (ii) XWayland unmanaged windows
(e.g. popups); (iii) xdg child window positioning; (iv) xdg subsurfaces;
and (v) xdg popup positioning. [#2968] @Consolatis
- Add command line option -t|--title to set the labwc window title when running
nested [#3577] @mdsib
- Add support for HDR10 output @kode54 @Consolatis [#3424]
- Include wlroots version in --version string @Consolatis [#3567] [#3581]
- Implement menu accelerators (one-letter mnemonics to quickly select/exec
items from the current menu) @ch3rn1ka [#3505]
- Add Next/PreviousWindowImmediate actions @elviosak @johanmalm [#3547]
- Add labnag options `--details-border-color` and `--details-margin`
@st0rm-shad0w [#3527]
- Add config option `<focus><raiseOnFocusDelay>` to defer raise-on-focus by a
small amount when `raiseOnFocus` is enabled @joske [#3513]
- Install `labwc-session.target` systemd user unit when the systemd dependency
is available @joske [#3534]
- Add `onbutton` to config option `<libinput><device><scrollMethod>`. Also add
associated option `<libinput><device><scrollButton>`. @diniamo [#3540]
- Add `overrideInhibition` option to `<keybind>` [#3507] @drougas
- Add action `ToggleShowDesktop` to hide/unhide windows, and default keybind
`Super-d` to trigger this action [#3500] [#3595] @johanmalm
- Add `<privilegedInterfaces>` config option so that privileged protocols can be
restricted [#3493] @xi
- Add action `DebugToggleKeyStateIndicator` to show a key-state on-screen
display (OSD) for debugging. [#3499] @johanmalm @tokyo4j
- Add support for `color-management-v1` and `color-representation-manager-v1`
protocols [#3469] @ManuLinares
- Add configuration option `<tabletTool minPressure="0.0" maxPressure="1.0" />`
to enable tablet tool pressure range libinput settings [#2916] @jp7677
- Add `wl_fixes` interface [#2956] @kode54
### Fixed
- Enable labnag long option --exclusive-zone [#3576] @st0rm-shad0w
- Position chromium popup correctly when a window is maximized on a multi-
output setup @elviosak [#3547]
- Run session activation environment update synchronously to avoid a race
condition with the autostart script [#3543] @joske
- Allow interactive resize on fully maximized windows so that a resize
initiated by modifier plus right-mouse-button-drag is not ignored [#3525]
@bjorn
- Gracefully handle missing XWayland packages, so that a labwc compositor which
has been built with XWayland support (which is optional) can be run even if
XWayland is not installed. [#3401] @quite
- Rework how XWayland window initial geometry is set to ensure that the natural
geometry does not exceed the usable output area when handling initial
maximize/fullscreen requests. [#3439] @jlindgren90.
- For XWayland windows, sync always-on-top state back to X.Org Server. This
makes `wmctrl -b toggle,above` work. [#3446] @jlindgren90
- Fix missing title and icon with XWayland client override-redirect toggle.
There are no known issues with clients, so this is purely for preventative
purposes. [#3450] @jlindgren90
- Update titlebar title when set to empty and fix an associated issue causing
the title to be misplaced outside of the titlebar when the window is resized.
[#3443] @tokyo4j
- When running nested, exit compositor when last output is destroyed because
in this situation, each output corresponds to a window in the parent
compositor and, unlike DRM outputs, these cannot be reconnected after being
destroyed. [#3440] @marler8997
- Allow policy-based placement to apply when an initially-maximized/fullscreen
view is restored to floating geometry. [#3387] [#3502] @jlindgren90
### Changed
- Change the default keybinds for XF86Audio{LowerVolume,RaiseVolume,Mute} to use
pactl instead of amixer [#3484] @danielrrrr
- Drop cosmic-workspace protocol [#3031] @tokyo4j
## Notes on wlroots-0.19 ## Notes on wlroots-0.19
There are some regression warnings worth noting for the switch to wlroots 0.19: There are some regression warnings worth noting for the switch to wlroots 0.19:
@ -111,52 +217,35 @@ There are some regression warnings worth noting for the switch to wlroots 0.19:
[wlroots-5098]:https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5098 [wlroots-5098]:https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5098
[gtk-8792]: https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/8792 [gtk-8792]: https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/8792
## unreleased ## 0.9.7 - 2026-04-17
[unreleased-commits] [0.9.7-commits]
The codebase has been ported to wlroots 0.20 [#2956] @Consolatis This is a small bug fix release.
### Added ```
0.9.6--------0.9.7 <--- v0.9 branch with bug-fixes only
/
/
0.9.4--------0.9.5-------- <-- master (built with wlroots-0.20)
```
- Add configuration option `<tabletTool minPressure="0.0" maxPressure="1.0" />` ## Fixed
to enable tablet tool pressure range libinput settings [#2916] @jp7677
- Add `wl_fixes` interface [#2956] @kode54
### Fixed - Fix intermittent failed-to-get-texture issue with some clients (e.g. foot)
when using the window-switcher in the thumbnail mode. [#3511] @yuiiio
- Gracefully handle missing XWayland packages, so that a labwc compositor which - Fix tablet tool tilt motion. [#3494] @jp7677
has been built with XWayland support (which is optional) can be run even if - Handle window-switcher buffer allocation failure when in 'thumbnail' mode.
XWayland is not installed. [#3401] @quite This is believed to be very unlikely to happen, but has been reported by one
- Rework how XWayland window initial geometry is set to ensure that the natural user and is believed to be GPU driver related. [#3490] @Consolatis
geometry does not exceed the usable output area when handling initial
maximize/fullscreen requests. [#3439] @jlindgren90.
- For XWayland windows, sync always-on-top state back to X.Org Server. This
makes `wmctrl -b toggle,above` work. [#3446] @jlindgren90
- Fix missing title and icon with XWayland client override-redirect toggle.
There are no known issues with clients, so this is purely for preventative
purposes. [#3450] @jlindgren90
- Update titlebar title when set to empty and fix an associated issue causing
the title to be misplaced outside of the titlebar when the window is resized.
[#3443] @tokyo4j
- When running nested, exit compositor when last output is destroyed because
in this situation, each output corresponds to a window in the parent
compositor and, unlike DRM outputs, these cannot be reconnected after being
destroyed. [#3440] @marler8997
- Allow policy-based placement to apply when an initially-maximized/fullscreen
view is restored to floating geometry. [#3387] @jlindgren90
### Changed
- Drop cosmic-workspace protocol [#3031] @tokyo4j
## 0.9.6 - 2026-03-15 ## 0.9.6 - 2026-03-15
[0.9.6-commits] [0.9.6-commits]
This is an earlier-than-usual release containing bug fixes only. It has been This is an earlier-than-usual release containing bug fixes only. It has been
done on a separate branch (0.9.5-maintenance) to avoid the inclusion of done on a separate branch (v0.9) to avoid the inclusion of refactoring and new
refactoring and new features. features.
``` ```
0.9.6 <--- bug-fixes only 0.9.6 <--- bug-fixes only
@ -213,9 +302,9 @@ Note to package maintainers: This release requires wayland version >=1.22.90
- Add theme option window.button.hover.bg.color [#3365] @johanmalm - Add theme option window.button.hover.bg.color [#3365] @johanmalm
- Implement scrollable window-switcher OSD [#3291] @tokyo4j - Implement scrollable window-switcher OSD [#3291] @tokyo4j
- Support the `NextWindow` options listed below [#3271] @tokyo4j - Support the `NextWindow` options listed below [#3271] @tokyo4j
- `<action name="NextWindow" workspace="current|all"/>` - `<action name="NextWindow" workspace="current|all" />`
- `<action name="NextWindow" output="all|focused|cursor"/>` - `<action name="NextWindow" output="all|focused|cursor" />`
- `<action name="NextWindow" identifier="all|current"/>` - `<action name="NextWindow" identifier="all|current" />`
- Add config option `*<desktops><initial>` for setting the active workspace on - Add config option `*<desktops><initial>` for setting the active workspace on
startup. [#3265] @5trixs0f startup. [#3265] @5trixs0f
@ -263,7 +352,7 @@ Note to package maintainers: This release requires wayland version >=1.22.90
and get keyboard focus so that they can be operated with the keyboard. and get keyboard focus so that they can be operated with the keyboard.
An example use-case is the xfce4-panel applications-menu being opened by An example use-case is the xfce4-panel applications-menu being opened by
the command xfce4-popup-applicationsmenu. [#3165] @johanmalm the command xfce4-popup-applicationsmenu. [#3165] @johanmalm
- On popup destory, return focus to whoever had it before the popop [#3165] - On popup destroy, return focus to whoever had it before the popop [#3165]
@johanmalm @tokyo4j @johanmalm @tokyo4j
- Unshade window if selected from client-list-combined-menu [#3345] @Amodio - Unshade window if selected from client-list-combined-menu [#3345] @Amodio
- Show non-dialog child windows in window-switcher [#3339] @tokyo4j - Show non-dialog child windows in window-switcher [#3339] @tokyo4j
@ -297,7 +386,7 @@ A big thank you to all involved in this release.
### Added ### Added
- Add `<windowSwitcher order="focus|age"/>` to optionally order windows by age - Add `<windowSwitcher order="focus|age" />` to optionally order windows by age
rather than most recent focus. @mbroemme [#3229] rather than most recent focus. @mbroemme [#3229]
- Replace `<snapping><range>` with `<snapping><range inner="" outer="">` to - Replace `<snapping><range>` with `<snapping><range inner="" outer="">` to
provide more granular control when configuring the size of snapping areas provide more granular control when configuring the size of snapping areas
@ -328,7 +417,7 @@ A big thank you to all involved in this release.
[#3134] [#3134]
- labnag: add --keyboard-focus option @tokyo4j [#3120] - labnag: add --keyboard-focus option @tokyo4j [#3120]
- Allow window switcher to temporarily unshade windows using config option - Allow window switcher to temporarily unshade windows using config option
`<windowSwitcher unshade="yes|no"/>` @Amodio @Consolatis [#3124] `<windowSwitcher unshade="yes|no" />` @Amodio @Consolatis [#3124]
- For the 'classic' style window-switcher, add the following theme options: - 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.border.color`
- `osd.window-switcher.style-classic.item.active.bg.color` - `osd.window-switcher.style-classic.item.active.bg.color`
@ -358,7 +447,7 @@ A big thank you to all involved in this release.
client surface. Fixes a regression in 885919f. @tokyo4j [#3211] client surface. Fixes a regression in 885919f. @tokyo4j [#3211]
- Set all foreign-toplevel initial states correctly. This is not believed to fix - Set all foreign-toplevel initial states correctly. This is not believed to fix
any particular user-issue, but just feels safer. @jlindgren90 [#3217] any particular user-issue, but just feels safer. @jlindgren90 [#3217]
- Update layer-shell client top layer visiblity on unmap instead of destroy - Update layer-shell client top layer visibility on unmap instead of destroy
because it is possible for fullscreen xwayland windows to be unmapped without because it is possible for fullscreen xwayland windows to be unmapped without
being destroyed, and in this case the top layer visibility needs to be updated being destroyed, and in this case the top layer visibility needs to be updated
to unhide other layer-shell clients like panels. @jlindgren90 [#3199] to unhide other layer-shell clients like panels. @jlindgren90 [#3199]
@ -370,7 +459,7 @@ A big thank you to all involved in this release.
@elviosak [#3146] [#3168] @elviosak [#3146] [#3168]
- Work around client-side rounding issues at right/bottom pixel. This fixes an - Work around client-side rounding issues at right/bottom pixel. This fixes an
issue with some clients (notably Qt ones) where cursor coordinates in the issue with some clients (notably Qt ones) where cursor coordinates in the
rightmost or bottom fixel are incorrectly rounded up putting them outside the rightmost or bottom pixel are incorrectly rounded up putting them outside the
surface bounds. The issue has been particularly noticeable with layer-shell surface bounds. The issue has been particularly noticeable with layer-shell
clients like lxqt-panel. @jlindgren90 [#3157] [#2379] [#3099] clients like lxqt-panel. @jlindgren90 [#3157] [#2379] [#3099]
Note: This also avoids a similar server-side rounding issue with some Note: This also avoids a similar server-side rounding issue with some
@ -418,7 +507,7 @@ A big thank you to all involved in this release.
when a window is using fullscreen mode. @johanmalm [#3158] when a window is using fullscreen mode. @johanmalm [#3158]
- Call labnag with on-demand keyboard interactivity by default @tokyo4j [#3120] - Call labnag with on-demand keyboard interactivity by default @tokyo4j [#3120]
- Temporarily unshade windows when switching windows. Restore old behaviour with - Temporarily unshade windows when switching windows. Restore old behaviour with
`<windowSwitcher unshade="no"/>` @Amodio @Consolatis [#3124] `<windowSwitcher unshade="no" />` @Amodio @Consolatis [#3124]
- In the classic style window-switcher, the default color of the selected window - 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 item has been changed to inherit the border color but with 15% opacity
@tokyo4j [#3118] @tokyo4j [#3118]
@ -437,7 +526,7 @@ A big thank you to all involved in this release.
`<windowSwitcher style="thumbnail">`. @tokyo4j [#2981] `<windowSwitcher style="thumbnail">`. @tokyo4j [#2981]
- Add `toggle` option to `GoToDesktop` action. This has the effect of going back - Add `toggle` option to `GoToDesktop` action. This has the effect of going back
to the last desktop if already on the target. @RainerKuemmerle [#3024] to the last desktop if already on the target. @RainerKuemmerle [#3024]
- Add `<theme maximizedDecoration="titlebar|none"/>` to allow hiding titlebar - Add `<theme maximizedDecoration="titlebar|none" />` to allow hiding titlebar
when window is maximized. @CosmicFusion @tokyo4j [#3015] when window is maximized. @CosmicFusion @tokyo4j [#3015]
- Use client-send-to-menu as 'Workspace' submenu in built-in client-menu - Use client-send-to-menu as 'Workspace' submenu in built-in client-menu
@johanmalm [#2995] @johanmalm [#2995]
@ -488,7 +577,7 @@ A big thank you to all involved in this release.
- Change default keybind `W-<arrow>` to combine cardinal directions to support - Change default keybind `W-<arrow>` to combine cardinal directions to support
resizing of windows to fill a quarter of an output. This only affects users 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 who do not use an `rc.xml` (thereby using default keybinds) or use the
`<keyboard><default/>` option. Previous behavior can be restored by setting `<keyboard><default />` option. Previous behavior can be restored by setting
`combine="no"` as shown below. [#3081] @tokyo4j `combine="no"` as shown below. [#3081] @tokyo4j
``` ```
@ -587,7 +676,7 @@ window.*.title.bg.colorTo.splitTo:
window rule to enable this. @Consolatis @tokyo4j [#2840] window rule to enable this. @Consolatis @tokyo4j [#2840]
- Add config option `<core><primarySelection>`. This enables autoscroll - Add config option `<core><primarySelection>`. This enables autoscroll
(middle-click to scroll up/down) in Chromium and electron based clients (middle-click to scroll up/down) in Chromium and electron based clients
without inadvertantly pasting the primary clipboard. @johanmalm [#2832] without inadvertently pasting the primary clipboard. @johanmalm [#2832]
- Bump `xdg_shell` version from 3 to 6 @tokyo4j [#2814] - Bump `xdg_shell` version from 3 to 6 @tokyo4j [#2814]
- Bump `wl_compositor` version from 5 to 6 @tokyo4j [#2812] - Bump `wl_compositor` version from 5 to 6 @tokyo4j [#2812]
- Support tablet tool mouse buttons @jp7677 [#2778] - Support tablet tool mouse buttons @jp7677 [#2778]
@ -664,7 +753,7 @@ window.*.title.bg.colorTo.splitTo:
agnostic on choice of launcher. agnostic on choice of launcher.
- `A-<arrow>` for `MoveToEdge` because `Alt-` keybinds should be for clients - `A-<arrow>` for `MoveToEdge` because `Alt-` keybinds should be for clients
to use and this one results in frequent user complaints because it prevents to use and this one results in frequent user complaints because it prevents
some common usage patterns like alt-left/right in web browers. some common usage patterns like alt-left/right in web browsers.
- Change default titlebar menu button from a dot to an arrow @johanmalm [#2844] - Change default titlebar menu button from a dot to an arrow @johanmalm [#2844]
- When `dragLock` is set to `yes`, the drag no longer expires after a short - When `dragLock` is set to `yes`, the drag no longer expires after a short
delay (known as `Sticky` mode) as recommended by libinput [#2803]. The timeout delay (known as `Sticky` mode) as recommended by libinput [#2803]. The timeout
@ -699,7 +788,7 @@ release.
- Localize desktop-entry application names used by the window switcher via - Localize desktop-entry application names used by the window switcher via
`desktop_entry_name` or the `%n` specifier @tokyo4j [#2653] `desktop_entry_name` or the `%n` specifier @tokyo4j [#2653]
- Add `HideCursor` action @jp7677 [#2633] - Add `HideCursor` action @jp7677 [#2633]
- Support application icons in window-switcher using `<field content="icon"/>` - Support application icons in window-switcher using `<field content="icon" />`
and use this by default. @tokyo4j [#2621] and use this by default. @tokyo4j [#2621]
- Support application icons in client-list-combined-menu @tokyo4j [#2617] - Support application icons in client-list-combined-menu @tokyo4j [#2617]
- Support the use of the keypad-enter key when using menu. @zeusgoose [#2610] - Support the use of the keypad-enter key when using menu. @zeusgoose [#2610]
@ -836,7 +925,7 @@ Notes to package maintainers:
closing a popup did not move the pointer focus to the main toplevel until the closing a popup did not move the pointer focus to the main toplevel until the
cursor was moved. [#2443] cursor was moved. [#2443]
- Improve algorithm for menu placement with xdg-positioner [#2408] - Improve algorithm for menu placement with xdg-positioner [#2408]
- Do not forward IME key-release without correspinding key-press to avoid stuck - Do not forward IME key-release without corresponding key-press to avoid stuck
keys [#2437] keys [#2437]
### Changed ### Changed
@ -898,7 +987,7 @@ Notes to package maintainers:
```xml ```xml
<windowRules> <windowRules>
<windowRule identifier="blender" wantAbsorbedModifierReleaseEvents="yes"/> <windowRule identifier="blender" wantAbsorbedModifierReleaseEvents="yes" />
</windowRules> </windowRules>
``` ```
@ -915,8 +1004,8 @@ menu.border.color: #aaaaaa
```xml ```xml
<windowSwitcher> <windowSwitcher>
<fields> <fields>
<field content="custom" format="%n" width="25%"/> <field content="custom" format="%n" width="25%" />
<field content="title" width="75%"/> <field content="title" width="75%" />
</fields> </fields>
</windowSwitcher> </windowSwitcher>
``` ```
@ -1046,7 +1135,7 @@ Notes to package maintainers:
- Support the openbox style menus listed below. Written-by: @droc12345 - Support the openbox style menus listed below. Written-by: @droc12345
1. `client-list-combined-menu` shows windows across all workspaces. This can 1. `client-list-combined-menu` shows windows across all workspaces. This can
be used with a mouse/key bind using: be used with a mouse/key bind using:
`<action name="ShowMenu" menu="client-list-combined-menu"/>` [#2101] `<action name="ShowMenu" menu="client-list-combined-menu" />` [#2101]
2. `client-send-to` shows all workspaces that the current window can be sent 2. `client-send-to` shows all workspaces that the current window can be sent
to. This can additional be used within a client menu using: to. This can additional be used within a client menu using:
`<menu id="client-send-to-menu" label="Send to Workspace..." />` [#2152] `<menu id="client-send-to-menu" label="Send to Workspace..." />` [#2152]
@ -1205,14 +1294,14 @@ have been attributed with a 'Written-by' against each relevant log entry.
```xml ```xml
<placement> <placement>
<policy>cascade</policy> <policy>cascade</policy>
<cascadeOffset x="40" y="30"/> <cascadeOffset x="40" y="30" />
</placement> </placement>
``` ```
- Support relative tablet motion. Written-by: @jp7677 [#1962] - Support relative tablet motion. Written-by: @jp7677 [#1962]
```xml ```xml
<tabletTool motion="absolute|relative" relativeMotionSensitivity="1.0"/> <tabletTool motion="absolute|relative" relativeMotionSensitivity="1.0" />
``` ```
### Fixed ### Fixed
@ -1296,7 +1385,7 @@ joint effort by @spl237 and @Consolatis.
- Respect `menu.overlap.x` when using pipemenus. [#1940] - Respect `menu.overlap.x` when using pipemenus. [#1940]
- Do not try to restore windows to very small width/height on unmaximize. - Do not try to restore windows to very small width/height on unmaximize.
This fixes a bug with Thonny (Python IDE made with Tk). [#1938] This fixes a bug with Thonny (Python IDE made with Tk). [#1938]
- Conditially set squared server-side decoration (SSD) corners when a view is - Conditionally set squared server-side decoration (SSD) corners when a view is
tiled. Written-by: @jp7677 [#1926] tiled. Written-by: @jp7677 [#1926]
- Remember initial direction when starting window-cycling with `PreviousView`. - Remember initial direction when starting window-cycling with `PreviousView`.
Also make the toggling of direction when shift is pressed relative to the Also make the toggling of direction when shift is pressed relative to the
@ -1320,7 +1409,7 @@ joint effort by @spl237 and @Consolatis.
Chromium and Steam. [#1861] Chromium and Steam. [#1861]
- Session-lock: fix flashing & update cursor shape. [#1858] - Session-lock: fix flashing & update cursor shape. [#1858]
- Remove tearing-controller listeners on destroy. [#1853] - Remove tearing-controller listeners on destroy. [#1853]
- Handle invalid `ForEach` and `If` action cofigs. [#1838] - Handle invalid `ForEach` and `If` action configs. [#1838]
- Delay startup of applications until event loop is ready. This avoids race - Delay startup of applications until event loop is ready. This avoids race
conditions when using autostart scripts that trigger a labwc SIGHUP. [#1588] conditions when using autostart scripts that trigger a labwc SIGHUP. [#1588]
- With `SendToDesktop` action follow=no option, ensure the topmost window is - With `SendToDesktop` action follow=no option, ensure the topmost window is
@ -1337,7 +1426,7 @@ joint effort by @spl237 and @Consolatis.
- Remove subprojects/seatd.wrap as no longer needed - Remove subprojects/seatd.wrap as no longer needed
- Action `MoveToCursor` is deprecated in favour of: - Action `MoveToCursor` is deprecated in favour of:
`<action name="AutoPlace" policy="cursor"/>`. `<action name="AutoPlace" policy="cursor" />`.
## 0.7.2 - 2024-05-10 ## 0.7.2 - 2024-05-10
@ -1354,7 +1443,7 @@ contributions from others as noted in the log.
### Added ### Added
- Add `<menu><ignoreButtonReleasePeriod>` to prevent clicks with small movements - Add `<menu><ignoreButtonReleasePeriod>` to prevent clicks with small movements
from inadvertantly closing a menu or selecting a menu item. This is the from inadvertently closing a menu or selecting a menu item. This is the
equivalent of `<menu><hideDelay>` on Openbox. [#1760] equivalent of `<menu><hideDelay>` on Openbox. [#1760]
- Support drop-shadows (disabled by default) for windows using server-side - Support drop-shadows (disabled by default) for windows using server-side
decorations. Written-by: @cillian64 decorations. Written-by: @cillian64
@ -1382,7 +1471,7 @@ window.inactive.shadow.color: #00000040
```xml ```xml
<action name="ForEach"> <action name="ForEach">
<query identifier="foo"/> <query identifier="foo" />
<then> <then>
<!-- carry out some action on match --> <!-- carry out some action on match -->
</then> </then>
@ -1424,7 +1513,7 @@ osd.window-switcher.width: 75%
<snapping> <snapping>
<overlay> <overlay>
<enabled>yes|no</enabled> <enabled>yes|no</enabled>
<delay inner="500" outer="500"/> <delay inner="500" outer="500" />
</overlay> </overlay>
</snapping> </snapping>
``` ```
@ -1587,7 +1676,7 @@ osd.window-switcher.preview.border.color: #ffffff,#00a2ff,#ffffff
```xml ```xml
<keybind key="A-Space"> <keybind key="A-Space">
<action name="ShowMenu" menu="client-menu" atCursor="No"/> <action name="ShowMenu" menu="client-menu" atCursor="No" />
</keybind> </keybind>
``` ```
@ -1595,7 +1684,7 @@ osd.window-switcher.preview.border.color: #ffffff,#00a2ff,#ffffff
is already used by the action itself). [#1589] is already used by the action itself). [#1589]
```xml ```xml
<action name="MoveToOutput" output="HDMI-A-1"/> <action name="MoveToOutput" output="HDMI-A-1" />
``` ```
- Do not deactivate window when giving keyboard focus to a non-view - Do not deactivate window when giving keyboard focus to a non-view
@ -1660,8 +1749,8 @@ osd.window-switcher.preview.border.color: #ffffff,#00a2ff,#ffffff
Written-by: @jp7677 Written-by: @jp7677
```xml ```xml
<touch mapToOutput=""/> <touch mapToOutput="" />
<touch deviceName="" mapToOutput=""/> <touch deviceName="" mapToOutput="" />
``` ```
- Add tablet support including: - Add tablet support including:
@ -1874,7 +1963,7 @@ relating to surface focus and keyboard issues, amongst others.
- Allow referencing the current workspace in actions, for example: - Allow referencing the current workspace in actions, for example:
```xml ```xml
<action name="SendToDesktop" to="current"/> <action name="SendToDesktop" to="current" />
``` ```
### Fixed ### Fixed
@ -2030,7 +2119,7 @@ relating to surface focus and keyboard issues, amongst others.
```xml ```xml
<windowSwitcher> <windowSwitcher>
<fields> <fields>
<field content="identifier" width="25%"/> <field content="identifier" width="25%" />
</fields> </fields>
</windowSwithcer> </windowSwithcer>
``` ```
@ -2097,9 +2186,9 @@ relating to surface focus and keyboard issues, amongst others.
```xml ```xml
<windowRules> <windowRules>
<windowRule identifier="some-application"> <windowRule identifier="some-application">
<action name="Maximize"/> <action name="Maximize" />
</windowRule> </windowRule>
<windowRule identifier="foo*" serverDecoration="yes|no"/> <windowRule identifier="foo*" serverDecoration="yes|no" />
</windowRules> </windowRules>
``` ```
@ -2184,7 +2273,7 @@ Unless otherwise stated all contributions are by the core-devs
```xml ```xml
<keyboard> <keyboard>
<default/> <default />
<keybind key="A-Left"><action name="None" /></keybind> <keybind key="A-Left"><action name="None" /></keybind>
<keybind key="A-Right"><action name="None" /></keybind> <keybind key="A-Right"><action name="None" /></keybind>
</keyboard> </keyboard>
@ -2448,7 +2537,7 @@ reported, tested and fixed issues. Particular mentions go to @bi4k8,
actions to be de-coupled from buttons. As a result, "Drag" and actions to be de-coupled from buttons. As a result, "Drag" and
"DoubleClick" actions previously defined against "TitleBar" should now "DoubleClick" actions previously defined against "TitleBar" should now
come under the "Title" context, for example: come under the "Title" context, for example:
`<mousebind button="Left" action="Drag"><action name="Move"/></mousebind>` `<mousebind button="Left" action="Drag"><action name="Move" /></mousebind>`
- Remove default alt-escape keybind for Exit because too many people have - Remove default alt-escape keybind for Exit because too many people have
exited the compositor by mistake trying to get out of alt-tab cycling exited the compositor by mistake trying to get out of alt-tab cycling
or similar. or similar.
@ -2681,7 +2770,9 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16
ShowMenu ShowMenu
[Keep a Changelog]: https://keepachangelog.com/en/1.0.0/ [Keep a Changelog]: https://keepachangelog.com/en/1.0.0/
[unreleased-commits]: https://github.com/labwc/labwc/compare/0.9.5...HEAD [unreleased-commits]: https://github.com/labwc/labwc/compare/0.20.0...HEAD
[0.20.0-commits]: https://github.com/labwc/labwc/compare/0.9.5..0.20.0
[0.9.7-commits]: https://github.com/labwc/labwc/compare/0.9.6...0.9.7
[0.9.6-commits]: https://github.com/labwc/labwc/compare/0.9.5...0.9.6 [0.9.6-commits]: https://github.com/labwc/labwc/compare/0.9.5...0.9.6
[0.9.5-commits]: https://github.com/labwc/labwc/compare/0.9.4...0.9.5 [0.9.5-commits]: https://github.com/labwc/labwc/compare/0.9.4...0.9.5
[0.9.4-commits]: https://github.com/labwc/labwc/compare/0.9.3...0.9.4 [0.9.4-commits]: https://github.com/labwc/labwc/compare/0.9.3...0.9.4
@ -3220,6 +3311,7 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16
[#3410]: https://github.com/labwc/labwc/pull/3410 [#3410]: https://github.com/labwc/labwc/pull/3410
[#3411]: https://github.com/labwc/labwc/pull/3411 [#3411]: https://github.com/labwc/labwc/pull/3411
[#3412]: https://github.com/labwc/labwc/pull/3412 [#3412]: https://github.com/labwc/labwc/pull/3412
[#3424]: https://github.com/labwc/labwc/pull/3424
[#3425]: https://github.com/labwc/labwc/pull/3425 [#3425]: https://github.com/labwc/labwc/pull/3425
[#3426]: https://github.com/labwc/labwc/pull/3426 [#3426]: https://github.com/labwc/labwc/pull/3426
[#3428]: https://github.com/labwc/labwc/pull/3428 [#3428]: https://github.com/labwc/labwc/pull/3428
@ -3232,3 +3324,23 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16
[#3445]: https://github.com/labwc/labwc/pull/3445 [#3445]: https://github.com/labwc/labwc/pull/3445
[#3446]: https://github.com/labwc/labwc/pull/3446 [#3446]: https://github.com/labwc/labwc/pull/3446
[#3450]: https://github.com/labwc/labwc/pull/3450 [#3450]: https://github.com/labwc/labwc/pull/3450
[#3469]: https://github.com/labwc/labwc/pull/3469
[#3484]: https://github.com/labwc/labwc/pull/3484
[#3490]: https://github.com/labwc/labwc/pull/3490
[#3493]: https://github.com/labwc/labwc/pull/3493
[#3494]: https://github.com/labwc/labwc/pull/3494
[#3499]: https://github.com/labwc/labwc/pull/3499
[#3500]: https://github.com/labwc/labwc/pull/3500
[#3502]: https://github.com/labwc/labwc/pull/3502
[#3505]: https://github.com/labwc/labwc/pull/3505
[#3507]: https://github.com/labwc/labwc/pull/3507
[#3511]: https://github.com/labwc/labwc/pull/3511
[#3513]: https://github.com/labwc/labwc/pull/3513
[#3525]: https://github.com/labwc/labwc/pull/3525
[#3527]: https://github.com/labwc/labwc/pull/3527
[#3534]: https://github.com/labwc/labwc/pull/3534
[#3540]: https://github.com/labwc/labwc/pull/3540
[#3543]: https://github.com/labwc/labwc/pull/3543
[#3547]: https://github.com/labwc/labwc/pull/3547
[#3567]: https://github.com/labwc/labwc/pull/3567
[#3595]: https://github.com/labwc/labwc/pull/3595

View file

@ -213,15 +213,16 @@ If you have not created an rc.xml config file, default bindings will be:
| `super`-`return` | lab-sensible-terminal | `super`-`return` | lab-sensible-terminal
| `alt`-`F4` | close window | `alt`-`F4` | close window
| `super`-`a` | toggle maximize | `super`-`a` | toggle maximize
| `super`-`d` | toggle show-desktop
| `super`-`mouse-left` | move window | `super`-`mouse-left` | move window
| `super`-`mouse-right` | resize window | `super`-`mouse-right` | resize window
| `super`-`arrow` | resize window to fill half the output | `super`-`arrow` | resize window to fill half the output
| `alt`-`space` | show the window menu | `alt`-`space` | show the window menu
| `XF86_AudioLowerVolume` | amixer sset Master 5%- | `XF86AudioLowerVolume` | pactl set-sink-volume @DEFAULT_SINK@ -5%
| `XF86_AudioRaiseVolume` | amixer sset Master 5%+ | `XF86AudioRaiseVolume` | pactl set-sink-volume @DEFAULT_SINK@ +5%
| `XF86_AudioMute` | amixer sset Master toggle | `XF86AudioMute` | pactl set-sink-mute @DEFAULT_SINK@ toggle
| `XF86_MonBrightnessUp` | brightnessctl set +10% | `XF86MonBrightnessUp` | brightnessctl set +10%
| `XF86_MonBrightnessDown` | brightnessctl set 10%- | `XF86MonBrightnessDown` | brightnessctl set 10%-
A root-menu can be opened by clicking on the desktop. A root-menu can be opened by clicking on the desktop.

View file

@ -46,6 +46,7 @@ struct conf {
uint32_t button_text; uint32_t button_text;
uint32_t button_background; uint32_t button_background;
uint32_t details_background; uint32_t details_background;
uint32_t details_border_color;
uint32_t background; uint32_t background;
uint32_t text; uint32_t text;
uint32_t button_border; uint32_t button_border;
@ -60,6 +61,7 @@ struct conf {
ssize_t button_gap_close; ssize_t button_gap_close;
ssize_t button_margin_right; ssize_t button_margin_right;
ssize_t button_padding; ssize_t button_padding;
ssize_t details_margin;
}; };
struct pointer { struct pointer {
@ -300,10 +302,10 @@ render_details_scroll_button(cairo_t *cairo, struct nag *nag,
get_text_size(cairo, nag->conf->font_description, &text_width, get_text_size(cairo, nag->conf->font_description, &text_width,
&text_height, NULL, 1, true, "%s", button->text); &text_height, NULL, 1, true, "%s", button->text);
int border = nag->conf->button_border_thickness; int border = nag->conf->details_border_thickness;
int padding = nag->conf->button_padding; int padding = (nag->conf->button_padding / 3) + 2;
cairo_set_source_u32(cairo, nag->conf->details_background); cairo_set_source_u32(cairo, nag->conf->details_border_color);
cairo_rectangle(cairo, button->x, button->y, cairo_rectangle(cairo, button->x, button->y,
button->width, button->height); button->width, button->height);
cairo_fill(cairo); cairo_fill(cairo);
@ -316,7 +318,7 @@ render_details_scroll_button(cairo_t *cairo, struct nag *nag,
cairo_set_source_u32(cairo, nag->conf->button_text); cairo_set_source_u32(cairo, nag->conf->button_text);
cairo_move_to(cairo, button->x + border + padding, cairo_move_to(cairo, button->x + border + padding,
button->y + border + (button->height - text_height) / 2); button->y + (button->height - text_height) / 2);
render_text(cairo, nag->conf->font_description, 1, true, render_text(cairo, nag->conf->font_description, 1, true,
"%s", button->text); "%s", button->text);
} }
@ -331,8 +333,8 @@ get_detailed_scroll_button_width(cairo_t *cairo, struct nag *nag)
NULL, 1, true, "%s", nag->details.button_down.text); NULL, 1, true, "%s", nag->details.button_down.text);
int text_width = up_width > down_width ? up_width : down_width; int text_width = up_width > down_width ? up_width : down_width;
int border = nag->conf->button_border_thickness; int border = nag->conf->details_border_thickness;
int padding = nag->conf->button_padding; int padding = (nag->conf->button_padding / 3) + 2;
return text_width + border * 2 + padding * 2; return text_width + border * 2 + padding * 2;
} }
@ -343,8 +345,9 @@ render_detailed(cairo_t *cairo, struct nag *nag, uint32_t y)
uint32_t width = nag->width; uint32_t width = nag->width;
int border = nag->conf->details_border_thickness; int border = nag->conf->details_border_thickness;
int margin = nag->conf->details_margin;
int padding = nag->conf->message_padding; int padding = nag->conf->message_padding;
int decor = padding + border; int decor = margin + border;
nag->details.x = decor; nag->details.x = decor;
nag->details.y = y + decor; nag->details.y = y + decor;
@ -372,7 +375,7 @@ render_detailed(cairo_t *cairo, struct nag *nag, uint32_t y)
bool show_buttons = nag->details.offset > 0; bool show_buttons = nag->details.offset > 0;
int button_width = get_detailed_scroll_button_width(cairo, nag); int button_width = get_detailed_scroll_button_width(cairo, nag);
if (show_buttons) { if (show_buttons) {
nag->details.width -= button_width; nag->details.width += border - button_width;
pango_layout_set_width(layout, pango_layout_set_width(layout,
(nag->details.width - padding * 2) * PANGO_SCALE); (nag->details.width - padding * 2) * PANGO_SCALE);
} }
@ -385,7 +388,7 @@ render_detailed(cairo_t *cairo, struct nag *nag, uint32_t y)
if (!show_buttons) { if (!show_buttons) {
show_buttons = true; show_buttons = true;
nag->details.width -= button_width; nag->details.width += border - button_width;
pango_layout_set_width(layout, pango_layout_set_width(layout,
(nag->details.width - padding * 2) * PANGO_SCALE); (nag->details.width - padding * 2) * PANGO_SCALE);
} }
@ -401,21 +404,29 @@ render_detailed(cairo_t *cairo, struct nag *nag, uint32_t y)
nag->details.visible_lines = pango_layout_get_line_count(layout); nag->details.visible_lines = pango_layout_get_line_count(layout);
int border_rect_height = nag->details.height + 2 * border;
if (show_buttons) { if (show_buttons) {
nag->details.button_up.x = nag->details.x + nag->details.width; nag->details.button_up.x = nag->details.x + nag->details.width;
nag->details.button_up.y = nag->details.y; nag->details.button_up.y = nag->details.y - border;
nag->details.button_up.width = button_width; nag->details.button_up.width = button_width;
nag->details.button_up.height = nag->details.height / 2; nag->details.button_up.height = (border_rect_height + border) / 2;
render_details_scroll_button(cairo, nag, &nag->details.button_up); 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.x = nag->details.x + nag->details.width;
nag->details.button_down.y = nag->details.button_down.y =
nag->details.button_up.y + nag->details.button_up.height; nag->details.button_up.y + nag->details.button_up.height - border;
nag->details.button_down.width = button_width; nag->details.button_down.width = button_width;
nag->details.button_down.height = nag->details.height / 2; nag->details.button_down.height =
border_rect_height - nag->details.button_up.height + border;
render_details_scroll_button(cairo, nag, &nag->details.button_down); render_details_scroll_button(cairo, nag, &nag->details.button_down);
} }
cairo_set_source_u32(cairo, nag->conf->details_border_color);
cairo_rectangle(cairo, margin, nag->details.y - border,
nag->details.width + 2 * border, border_rect_height);
cairo_fill(cairo);
cairo_set_source_u32(cairo, nag->conf->details_background); cairo_set_source_u32(cairo, nag->conf->details_background);
cairo_rectangle(cairo, nag->details.x, nag->details.y, cairo_rectangle(cairo, nag->details.x, nag->details.y,
nag->details.width, nag->details.height); nag->details.width, nag->details.height);
@ -447,7 +458,7 @@ render_button(cairo_t *cairo, struct nag *nag, struct button *button,
} }
button->x = *x - border - text_width - padding * 2 + 1; button->x = *x - border - text_width - padding * 2 + 1;
button->y = (int)(ideal_height - text_height) / 2 - padding + 1; button->y = (int)(ideal_height - text_height) / 2 - padding;
button->width = text_width + padding * 2; button->width = text_width + padding * 2;
button->height = text_height + padding * 2; button->height = text_height + padding * 2;
@ -1464,14 +1475,16 @@ conf_init(struct conf *conf)
conf->keyboard_focus = ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE; conf->keyboard_focus = ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE;
conf->bar_border_thickness = 2; conf->bar_border_thickness = 2;
conf->message_padding = 8; conf->message_padding = 8;
conf->details_border_thickness = 3;
conf->button_border_thickness = 3; conf->button_border_thickness = 3;
conf->button_gap = 20; conf->button_gap = 20;
conf->button_gap_close = 15; conf->button_gap_close = 15;
conf->button_margin_right = 2; conf->button_margin_right = 2;
conf->button_padding = 3; conf->button_padding = 3;
conf->button_background = 0x680A0AFF; conf->button_background = 0x680A0AFF;
conf->details_margin = 11;
conf->details_border_thickness = 3;
conf->details_background = 0x680A0AFF; conf->details_background = 0x680A0AFF;
conf->details_border_color = 0x680A0AFF;
conf->background = 0x900000FF; conf->background = 0x900000FF;
conf->text = 0xFFFFFFFF; conf->text = 0xFFFFFFFF;
conf->button_text = 0xFFFFFFFF; conf->button_text = 0xFFFFFFFF;
@ -1551,16 +1564,18 @@ nag_parse_options(int argc, char **argv, struct nag *nag,
TO_COLOR_BORDER_BOTTOM, TO_COLOR_BORDER_BOTTOM,
TO_COLOR_BUTTON_BG, TO_COLOR_BUTTON_BG,
TO_COLOR_DETAILS, TO_COLOR_DETAILS,
TO_COLOR_DETAILS_BORDER,
TO_COLOR_TEXT, TO_COLOR_TEXT,
TO_COLOR_BUTTON_TEXT, TO_COLOR_BUTTON_TEXT,
TO_THICK_BAR_BORDER, TO_THICK_BAR_BORDER,
TO_PADDING_MESSAGE, TO_PADDING_MESSAGE,
TO_THICK_DET_BORDER, TO_THICK_DETAILS_BORDER,
TO_THICK_BTN_BORDER, TO_THICK_BTN_BORDER,
TO_GAP_BTN, TO_GAP_BTN,
TO_GAP_BTN_DISMISS, TO_GAP_BTN_DISMISS,
TO_MARGIN_BTN_RIGHT, TO_MARGIN_BTN_RIGHT,
TO_PADDING_BTN, TO_PADDING_BTN,
TO_MARGIN_DETAILS,
}; };
static const struct option opts[] = { static const struct option opts[] = {
@ -1577,6 +1592,7 @@ nag_parse_options(int argc, char **argv, struct nag *nag,
{"message", required_argument, NULL, 'm'}, {"message", required_argument, NULL, 'm'},
{"output", required_argument, NULL, 'o'}, {"output", required_argument, NULL, 'o'},
{"timeout", required_argument, NULL, 't'}, {"timeout", required_argument, NULL, 't'},
{"exclusive-zone", no_argument, NULL, 'x'},
{"version", no_argument, NULL, 'v'}, {"version", no_argument, NULL, 'v'},
{"background-color", required_argument, NULL, TO_COLOR_BACKGROUND}, {"background-color", required_argument, NULL, TO_COLOR_BACKGROUND},
@ -1587,8 +1603,10 @@ nag_parse_options(int argc, char **argv, struct nag *nag,
{"button-text-color", required_argument, NULL, TO_COLOR_BUTTON_TEXT}, {"button-text-color", required_argument, NULL, TO_COLOR_BUTTON_TEXT},
{"border-bottom-size", required_argument, NULL, TO_THICK_BAR_BORDER}, {"border-bottom-size", required_argument, NULL, TO_THICK_BAR_BORDER},
{"message-padding", required_argument, NULL, TO_PADDING_MESSAGE}, {"message-padding", required_argument, NULL, TO_PADDING_MESSAGE},
{"details-border-size", required_argument, NULL, TO_THICK_DET_BORDER}, {"details-border-size", required_argument, NULL, TO_THICK_DETAILS_BORDER},
{"details-background-color", required_argument, NULL, TO_COLOR_DETAILS}, {"details-background-color", required_argument, NULL, TO_COLOR_DETAILS},
{"details-border-color", required_argument, NULL, TO_COLOR_DETAILS_BORDER},
{"details-margin", required_argument, NULL, TO_MARGIN_DETAILS},
{"button-border-size", required_argument, NULL, TO_THICK_BTN_BORDER}, {"button-border-size", required_argument, NULL, TO_THICK_BTN_BORDER},
{"button-gap", required_argument, NULL, TO_GAP_BTN}, {"button-gap", required_argument, NULL, TO_GAP_BTN},
{"button-dismiss-gap", required_argument, NULL, TO_GAP_BTN_DISMISS}, {"button-dismiss-gap", required_argument, NULL, TO_GAP_BTN_DISMISS},
@ -1633,6 +1651,9 @@ nag_parse_options(int argc, char **argv, struct nag *nag,
" --details-border-size size Thickness for the details border.\n" " --details-border-size size Thickness for the details border.\n"
" --details-background-color RRGGBB[AA]\n" " --details-background-color RRGGBB[AA]\n"
" Details background color.\n" " Details background color.\n"
" --details-border-color RRGGBB[AA]\n"
" Details border color.\n"
" --details-margin margin Margin for the details.\n"
" --button-border-size size Thickness for the button border.\n" " --button-border-size size Thickness for the button border.\n"
" --button-gap gap Size of the gap between buttons\n" " --button-gap gap Size of the gap between buttons\n"
" --button-dismiss-gap gap Size of the gap for dismiss button.\n" " --button-dismiss-gap gap Size of the gap for dismiss button.\n"
@ -1769,6 +1790,11 @@ nag_parse_options(int argc, char **argv, struct nag *nag,
fprintf(stderr, "Invalid details background color: %s\n", optarg); fprintf(stderr, "Invalid details background color: %s\n", optarg);
} }
break; break;
case TO_COLOR_DETAILS_BORDER:
if (!parse_color(optarg, &conf->details_border_color)) {
fprintf(stderr, "Invalid details border color: %s\n", optarg);
}
break;
case TO_COLOR_TEXT: /* Text color */ case TO_COLOR_TEXT: /* Text color */
if (!parse_color(optarg, &conf->text)) { if (!parse_color(optarg, &conf->text)) {
fprintf(stderr, "Invalid text color: %s\n", optarg); fprintf(stderr, "Invalid text color: %s\n", optarg);
@ -1785,7 +1811,7 @@ nag_parse_options(int argc, char **argv, struct nag *nag,
case TO_PADDING_MESSAGE: /* Message padding */ case TO_PADDING_MESSAGE: /* Message padding */
conf->message_padding = strtol(optarg, NULL, 0); conf->message_padding = strtol(optarg, NULL, 0);
break; break;
case TO_THICK_DET_BORDER: /* Details border thickness */ case TO_THICK_DETAILS_BORDER: /* Details border thickness */
conf->details_border_thickness = strtol(optarg, NULL, 0); conf->details_border_thickness = strtol(optarg, NULL, 0);
break; break;
case TO_THICK_BTN_BORDER: /* Button border thickness */ case TO_THICK_BTN_BORDER: /* Button border thickness */
@ -1803,6 +1829,9 @@ nag_parse_options(int argc, char **argv, struct nag *nag,
case TO_PADDING_BTN: /* Padding for the button text */ case TO_PADDING_BTN: /* Padding for the button text */
conf->button_padding = strtol(optarg, NULL, 0); conf->button_padding = strtol(optarg, NULL, 0);
break; break;
case TO_MARGIN_DETAILS:
conf->details_margin = strtol(optarg, NULL, 0);
break;
default: /* Help or unknown flag */ default: /* Help or unknown flag */
fprintf(c == 'h' ? stdout : stderr, "%s", usage); fprintf(c == 'h' ? stdout : stderr, "%s", usage);
return LAB_EXIT_FAILURE; return LAB_EXIT_FAILURE;

View file

@ -0,0 +1,6 @@
[Unit]
Description=labwc session
Documentation=man:labwc(1) man:systemd.special(7)
BindsTo=graphical-session.target
Wants=graphical-session-pre.target
After=graphical-session-pre.target

View file

@ -1,10 +1,18 @@
# Example autostart file # Example autostart file
# When running under systemd, uncomment the systemctl line below to pull in
# graphical-session.target via labwc-session.target. This lets systemd user
# services declaring WantedBy=graphical-session.target (panels, portals,
# notification daemons, etc.) start in sync with the labwc session. Enable
# individual services with: systemctl --user enable <unit>
#
# systemctl --user --no-block start labwc-session.target
# Set background color. # Set background color.
swaybg -c '#113344' >/dev/null 2>&1 & swaybg -c '#113344' >/dev/null 2>&1 &
# Configure output directives such as mode, position, scale and transform. # Configure output directives such as mode, position, scale and transform.
# Use wlr-randr to get your output names # Use wlr-randr to get your output names.
# Example ~/.config/kanshi/config below: # Example ~/.config/kanshi/config below:
# profile { # profile {
# output HDMI-A-1 position 1366,0 # output HDMI-A-1 position 1366,0

View file

@ -95,6 +95,12 @@ _labnag_ [options...]
*--details-border-size* <size> *--details-border-size* <size>
Set the thickness for the details border. Set the thickness for the details border.
*--details-border-color* <RRGGBB[AA]>
Set the color of the details border.
*--details-margin* <margin>
Set the margin for the details.
*--button-border-size* <size> *--button-border-size* <size>
Set the thickness for the button border. Set the thickness for the button border.

View file

@ -16,7 +16,7 @@ Actions are used in menus and keyboard/mouse bindings.
SIGTERM signal. SIGTERM signal.
*<action name="Execute" command="value" />* *<action name="Execute" command="value" />*
Execute command. Note that in the interest of backward compatibility, Execute command. Note that in the interest of backward compatibility,
labwc supports <execute> as an alternative to <command> even though labwc supports <execute> as an alternative to <command> even though
openbox documentation states that it is deprecated. openbox documentation states that it is deprecated.
Note: Tilde (~) is expanded in the command before passing to execvp(). Note: Tilde (~) is expanded in the command before passing to execvp().
@ -126,30 +126,34 @@ Actions are used in menus and keyboard/mouse bindings.
position if it had been maximized or tiled to a direction or region. position if it had been maximized or tiled to a direction or region.
*<action name="NextWindow" workspace="current" output="all" identifier="all" />*++ *<action name="NextWindow" workspace="current" output="all" identifier="all" />*++
*<action name="PreviousWindow" workspace="current" output="all" identifier="all" />* *<action name="PreviousWindow" workspace="current" output="all" identifier="all" />*++
*<action name="NextWindowImmediate" workspace="current" output="all" identifier="all" />*++
*<action name="PreviousWindowImmediate" workspace="current" output="all" identifier="all" />*++
Cycle focus to next/previous window, respectively. Cycle focus to next/previous window, respectively.
Default keybinds for NextWindow and PreviousWindow are Alt-Tab and Default keybinds for NextWindow and PreviousWindow are Alt-Tab and
Shift-Alt-Tab. While cycling through windows, the arrow keys move the Shift-Alt-Tab. While cycling through windows, the arrow keys move the
selected window forwards/backwards and the escape key halts the cycling. selected window forwards/backwards and the escape key halts the cycling.
NextWindowImmediate and PreviousWindowImmediate skip the Window Switcher
and OSD, useful for binding to keys without modifiers.
*workspace* [all|current] *workspace* [all|current]
This determines whether to cycle through windows on all workspaces or the This determines whether to cycle through windows on all workspaces or
current workspace. Default is "current". the current workspace. Default is "current".
*output* [all|focused|cursor] *output* [all|focused|cursor]
This determines whether to cycle through windows on all outputs, the focused This determines whether to cycle through windows on all outputs, the
output, or the output under the cursor. Default is "all". focused output, or the output under the cursor. Default is "all".
*identifier* [all|current] *identifier* [all|current]
This determines whether to cycle through all windows or only windows of the This determines whether to cycle through all windows or only windows of
same application as the currently focused window. Default is "all". the same application as the currently focused window. Default is "all".
*<action name="Reconfigure" />* *<action name="Reconfigure" />*
Re-load configuration and theme files. Re-load configuration and theme files.
*<action name="ShowMenu" menu="root-menu"/>* *<action name="ShowMenu" menu="root-menu" />*
Show a menu. Show a menu.
``` ```
@ -295,7 +299,7 @@ Actions are used in menus and keyboard/mouse bindings.
(if one exists). (if one exists).
*wrap* [yes|no] When using the direction attribute, wrap around from *wrap* [yes|no] When using the direction attribute, wrap around from
right-to-left or top-to-bottom, and vice versa. Default no. right-to-left or top-to-bottom, and vice versa. Default is no.
*<action name="FitToOutput" />* *<action name="FitToOutput" />*
Resizes active window size to width and height of the output when the Resizes active window size to width and height of the output when the
@ -309,10 +313,10 @@ Actions are used in menus and keyboard/mouse bindings.
workspace or its index (starting at 1) as configured in rc.xml. workspace or its index (starting at 1) as configured in rc.xml.
*wrap* [yes|no] Wrap around from last desktop to first, and vice *wrap* [yes|no] Wrap around from last desktop to first, and vice
versa. Default yes. versa. Default is yes.
*toggle* [yes|no] Toggle to “last” if already on the workspace that *toggle* [yes|no] Toggle to “last” if already on the workspace that
would be the actual destination. Default no. would be the actual destination. Default is no.
*<action name="SendToDesktop" to="value" follow="yes" wrap="yes" />* *<action name="SendToDesktop" to="value" follow="yes" wrap="yes" />*
Send active window to workspace. Send active window to workspace.
@ -320,10 +324,11 @@ Actions are used in menus and keyboard/mouse bindings.
*to* The workspace to send the window to. Supported values are the same *to* The workspace to send the window to. Supported values are the same
as for GoToDesktop. as for GoToDesktop.
*follow* [yes|no] Also switch to the specified workspace. Default yes. *follow* [yes|no] Also switch to the specified workspace.
Default is yes.
*wrap* [yes|no] Wrap around from last desktop to first, and vice *wrap* [yes|no] Wrap around from last desktop to first, and vice
versa. Default yes. versa. Default is yes.
*<action name="VirtualOutputAdd" output_name="value" />* *<action name="VirtualOutputAdd" output_name="value" />*
Add virtual output (headless backend). Add virtual output (headless backend).
@ -341,11 +346,11 @@ Actions are used in menus and keyboard/mouse bindings.
``` ```
<keybind key="W-v"> <keybind key="W-v">
<action name="VirtualOutputAdd" output_name="ScreenCasting"/> <action name="VirtualOutputAdd" output_name="ScreenCasting" />
<action name="Execute" command='sh -c "wlr-randr --output ScreenCasting --pos 0,0 --scale 2 --custom-mode 3840x2110; wlr-randr --output eDP-1 --pos 0,0 --scale 2 --mode 3840x2160"'/> <action name="Execute" command='sh -c "wlr-randr --output ScreenCasting --pos 0,0 --scale 2 --custom-mode 3840x2110; wlr-randr --output eDP-1 --pos 0,0 --scale 2 --mode 3840x2160"' />
</keybind> </keybind>
<keybind key="W-c"> <keybind key="W-c">
<action name="VirtualOutputRemove"/> <action name="VirtualOutputRemove" />
</keybind> </keybind>
``` ```
@ -366,7 +371,7 @@ Actions are used in menus and keyboard/mouse bindings.
*output_name* The name of virtual output. If not supplied, will remove *output_name* The name of virtual output. If not supplied, will remove
the last virtual output added. the last virtual output added.
*<action name="AutoPlace" policy="value"/>* *<action name="AutoPlace" policy="value" />*
Reposition the window according to the desired placement policy. Reposition the window according to the desired placement policy.
*policy* [automatic|cursor|center|cascade] Use the specified policy, *policy* [automatic|cursor|center|cascade] Use the specified policy,
@ -424,6 +429,13 @@ Actions are used in menus and keyboard/mouse bindings.
Toggle the screen magnifier on or off at the last magnification level Toggle the screen magnifier on or off at the last magnification level
used. used.
*<action name="ToggleShowDesktop" />*
Minimize all windows in the current workspace so that the desktop
becomes visible. On calling the action again the hidden windows are
unminimized, provided that - since the initial `ShowDesktop` - (a) no
windows have been unminimized; (b) workspaces have not been switched;
and (c) no new applications have been started.
*<action name="ZoomIn">*++ *<action name="ZoomIn">*++
*<action name="ZoomOut">* *<action name="ZoomOut">*
Increase or decrease the magnification level for the screen magnifier. Increase or decrease the magnification level for the screen magnifier.
@ -436,6 +448,10 @@ Actions are used in menus and keyboard/mouse bindings.
If used as the only action for a binding: clear an earlier defined If used as the only action for a binding: clear an earlier defined
binding. binding.
*<action name="DebugToggleKeyStateIndicator" />*
Toggle visibility of key-state on-screen display (OSD). Note: This is
for debugging purposes only.
# CONDITIONAL ACTIONS # CONDITIONAL ACTIONS
Actions that execute other actions. Used in keyboard/mouse bindings. Actions that execute other actions. Used in keyboard/mouse bindings.
@ -448,10 +464,10 @@ Actions that execute other actions. Used in keyboard/mouse bindings.
``` ```
<action name="If"> <action name="If">
<query/> <query />
<prompt message=""/> <prompt message="" />
<then><action/></then> <then><action /></then>
<else><action/></else> <else><action /></else>
</action> </action>
``` ```
@ -503,7 +519,7 @@ Actions that execute other actions. Used in keyboard/mouse bindings.
"right-occupied" directions will not wrap. "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|up-left|up-right|down-left|down-right|center|any]
Whether the client is tiled (snapped) along the the Whether the client is tiled (snapped) along the
indicated screen edge. indicated screen edge.
*tiled_region* *tiled_region*
@ -533,9 +549,9 @@ Actions that execute other actions. Used in keyboard/mouse bindings.
``` ```
<keybind key="W-q"> <keybind key="W-q">
<action name="If"> <action name="If">
<prompt message="Quit?"/> <prompt message="Quit?" />
<then> <then>
<action name="Exit"/> <action name="Exit" />
</then> </then>
</action> </action>
</keybind> </keybind>

View file

@ -25,7 +25,7 @@ The XDG Base Directory Specification does not specify whether or not programs
should (a) allow the first-identified configuration file to supersede any should (a) allow the first-identified configuration file to supersede any
others, or (b) define rules for merging the information from more than one file. others, or (b) define rules for merging the information from more than one file.
By default, labwc uses option (a), reading only the first file identified. With By default, labwc uses option (a), reading only the first file identified. With
the --merge-config option, the search order is reversed, but every configuration the --merge-config option, the search order is reversed, but every configuration
file encountered is processed in turn. Thus, user-specific files will augment file encountered is processed in turn. Thus, user-specific files will augment
system-wide configurations, with conflicts favoring the user-specific system-wide configurations, with conflicts favoring the user-specific
@ -175,6 +175,7 @@ this is for compatibility with Openbox.
<adaptiveSync>no</adaptiveSync> <adaptiveSync>no</adaptiveSync>
<allowTearing>no</allowTearing> <allowTearing>no</allowTearing>
<autoEnableOutputs>yes</autoEnableOutputs> <autoEnableOutputs>yes</autoEnableOutputs>
<hdr>no</hdr>
<reuseOutputMode>no</reuseOutputMode> <reuseOutputMode>no</reuseOutputMode>
<xwaylandPersistence>no</xwaylandPersistence> <xwaylandPersistence>no</xwaylandPersistence>
<primarySelection>yes</primarySelection> <primarySelection>yes</primarySelection>
@ -240,6 +241,12 @@ this is for compatibility with Openbox.
'pkill kanshi ; wlopm --off \*' resume 'kanshi & wlopm --on \*' 'pkill kanshi ; wlopm --off \*' resume 'kanshi & wlopm --on \*'
``` ```
*<core><hdr>* [yes|no]
Automatically enable HDR support on outputs when configuring them,
where supported by the particular output device and display. Default
is no. Additionally requires the Vulkan backend. Can be set
via `WLR_RENDERER=vulkan` in `~/.config/labwc/environment`.
*<core><reuseOutputMode>* [yes|no] *<core><reuseOutputMode>* [yes|no]
Try to re-use the existing output mode (resolution / refresh rate). Try to re-use the existing output mode (resolution / refresh rate).
This may prevent unnecessary screenblank delays when starting labwc This may prevent unnecessary screenblank delays when starting labwc
@ -493,6 +500,13 @@ this is for compatibility with Openbox.
*<focus><raiseOnFocus>* [yes|no] *<focus><raiseOnFocus>* [yes|no]
Raise window to top when focused. Default is no. Raise window to top when focused. Default is no.
*<focus><raiseOnFocusDelay>* [milliseconds]
When raiseOnFocus is enabled, delay the actual raise by this many
milliseconds. Default is 0 (raise immediately). A subsequent focus
change before the timer elapses restarts or cancels the pending raise.
Useful together with followMouse to avoid brief passes of the cursor
stacking up z-order changes.
## WINDOW SNAPPING ## WINDOW SNAPPING
Windows may be "snapped" to an edge or user-defined region of an output when Windows may be "snapped" to an edge or user-defined region of an output when
@ -512,7 +526,8 @@ extending outward from the snapped edge.
*<range><inner>* and *<range><outer>*, and 50 for *<cornerRange>*. *<range><inner>* and *<range><outer>*, and 50 for *<cornerRange>*.
*<snapping><overlay><enabled>* [yes|no] *<snapping><overlay><enabled>* [yes|no]
Show an overlay when snapping a window to an output edge. Default is yes. Show an overlay when snapping a window to an output edge.
Default is yes.
*<snapping><overlay><delay><inner>*++ *<snapping><overlay><delay><inner>*++
*<snapping><overlay><delay><outer>* *<snapping><overlay><delay><outer>*
@ -581,7 +596,7 @@ extending outward from the snapped edge.
A setting of 0 disables the OSD. Default is 1000 ms. A setting of 0 disables the OSD. Default is 1000 ms.
*<desktops><prefix>* *<desktops><prefix>*
Set the prefix to use when using "number" above. Default is "Workspace" Set the prefix to use when using "number" above. Default is "Workspace".
## THEME ## THEME
@ -735,7 +750,7 @@ generate gesture events, like swipe and pinch. There are some related settings
(e.g. *threeFingerDrag* and *twoFingerScroll*) in the *<libinput>* section. (e.g. *threeFingerDrag* and *twoFingerScroll*) in the *<libinput>* section.
In the Wayland Compositor domain, events associated with touchscreens are In the Wayland Compositor domain, events associated with touchscreens are
sometimes simply referred to as *touch* events. Touchscreens can be configured sometimes simply referred to as *touch* events. Touchscreens can be configured
in both the *<touch>* and *<libinput>* sections. Note that touchscreen gestures in both the *<touch>* and *<libinput>* sections. Note that touchscreen gestures
are not interpreted by libinput, nor labwc. Any touch point is passed to the are not interpreted by libinput, nor labwc. Any touch point is passed to the
client (application) for any interpretation of gestures. client (application) for any interpretation of gestures.
@ -760,7 +775,8 @@ References:
Stores the keyboard layout either globally or per window and restores Stores the keyboard layout either globally or per window and restores
it when switching back to the window. Default is global. it when switching back to the window. Default is global.
*<keyboard><keybind key="" layoutDependent="" onRelease="" allowWhenLocked="">* *<keyboard><keybind key="" layoutDependent="" onRelease="" allowWhenLocked=""
overrideInhibition="">*
Define a *key* binding in the format *modifier-key*, where supported Define a *key* binding in the format *modifier-key*, where supported
modifiers are: modifiers are:
- S (shift) - S (shift)
@ -808,6 +824,12 @@ References:
*allowWhenLocked* [yes|no] *allowWhenLocked* [yes|no]
Make this keybind work even if the screen is locked. Default is no. Make this keybind work even if the screen is locked. Default is no.
*overrideInhibition* [yes|no]
Make this keybind work even if the view inhibits keybinds.
This can be used to prevent W-Tab and similar keybinds from being
delivered to Virtual Machines, VNC clients or nested compositors.
Default is no.
*onRelease* [yes|no] *onRelease* [yes|no]
When yes, fires the keybind action when the key or key When yes, fires the keybind action when the key or key
combination is released, rather than first pressed. This is useful to combination is released, rather than first pressed. This is useful to
@ -820,7 +842,7 @@ References:
``` ```
<keybind key="Super_L" onRelease="yes"> <keybind key="Super_L" onRelease="yes">
<action name="Execute" command="rofi -show drun"/> <action name="Execute" command="rofi -show drun" />
</keybind> </keybind>
``` ```
@ -841,11 +863,12 @@ References:
W-Return - lab-sensible-terminal W-Return - lab-sensible-terminal
A-F4 - close window A-F4 - close window
W-a - toggle maximize W-a - toggle maximize
W-d - toggle show-desktop
W-<arrow> - resize window to fill half or quarter of the output W-<arrow> - resize window to fill half or quarter of the output
A-Space - show window menu A-Space - show window menu
``` ```
Audio and MonBrightness keys are also bound to amixer and Audio and MonBrightness keys are also bound to pactl and
brightnessctl, respectively. brightnessctl, respectively.
*<keyboard><repeatRate>* *<keyboard><repeatRate>*
@ -880,7 +903,7 @@ input-devices by the Wayland protocol.
- Shade: A button that, by default, toggles window shading. - Shade: A button that, by default, toggles window shading.
- AllDesktops: A button that, by default, toggles omnipresence of a - AllDesktops: A button that, by default, toggles omnipresence of a
window. window.
- Close: A button that, by default, closses a window. - Close: A button that, by default, closes a window.
- Border: The window's border including Top...BRCorner below. - Border: The window's border including Top...BRCorner below.
- Top: The top edge of the window's border. - Top: The top edge of the window's border.
- Bottom: The bottom edge of the window's border. - Bottom: The bottom edge of the window's border.
@ -954,10 +977,10 @@ input-devices by the Wayland protocol.
``` ```
<mouse> <mouse>
<default/> <default />
<context name="Frame"> <context name="Frame">
<mousebind button="W-Left" action="Press"/> <mousebind button="W-Left" action="Press" />
<mousebind button="W-Left" action="Drag"/> <mousebind button="W-Left" action="Drag" />
</context> </context>
</mouse> </mouse>
``` ```
@ -965,7 +988,7 @@ input-devices by the Wayland protocol.
*<mouse><default />* *<mouse><default />*
Load default mousebinds. This is an addition to the openbox Load default mousebinds. This is an addition to the openbox
specification and provides a way to keep config files simpler whilst specification and provides a way to keep config files simpler whilst
allowing user specific binds. Note that if no rc.xml is found, or if no allowing user specific binds. Note that if no rc.xml is found, or if no
<mouse><mousebind> entries exist, the same default mousebinds will be <mouse><mousebind> entries exist, the same default mousebinds will be
loaded even if the <default /> element is not provided. loaded even if the <default /> element is not provided.
@ -977,7 +1000,7 @@ Note: To rotate touch events with output rotation, use the libinput
*calibrationMatrix* setting. *calibrationMatrix* setting.
``` ```
<touch deviceName="" mapToOutput="" mouseEmulation="no"/> <touch deviceName="" mapToOutput="" mouseEmulation="no" />
``` ```
*<touch deviceName="" />* *<touch deviceName="" />*
@ -1134,6 +1157,7 @@ Note: To rotate touch events with output rotation, use the libinput
<disableWhileTyping></disableWhileTyping> <disableWhileTyping></disableWhileTyping>
<clickMethod></clickMethod> <clickMethod></clickMethod>
<scrollMethod></scrollMethod> <scrollMethod></scrollMethod>
<scrollButton></scrollButton>
<sendEventsMode></sendEventsMode> <sendEventsMode></sendEventsMode>
<calibrationMatrix></calibrationMatrix> <calibrationMatrix></calibrationMatrix>
<scrollFactor>1.0</scrollFactor> <scrollFactor>1.0</scrollFactor>
@ -1238,19 +1262,26 @@ Note: To rotate touch events with output rotation, use the libinput
The default method depends on the touchpad hardware. The default method depends on the touchpad hardware.
*<libinput><device><scrollMethod>* [none|twofinger|edge] *<libinput><device><scrollMethod>* [none|twofinger|edge|onbutton]
Configure the method by which physical movements on a touchpad are Configure the method by which physical movements are mapped to scroll
mapped to scroll events. events.
The scroll methods available are: The scroll methods available are:
- *twofinger* - Scroll by two fingers being placed on the surface of the - *twofinger* - Scroll by two fingers being placed on the surface of the
touchpad, then moving those fingers vertically or horizontally. touchpad, then moving those fingers vertically or horizontally.
- *edge* - Scroll by moving a single finger along the right edge - *edge* - Scroll by moving a single finger along the right edge
(vertical scroll) or bottom edge (horizontal scroll). (vertical scroll) or bottom edge (horizontal scroll).
- *onbutton* - Scroll by pressing a button.
- *none* - No scroll events will be produced. - *none* - No scroll events will be produced.
The default method depends on the touchpad hardware. The default method depends on the touchpad hardware.
*<libinput><device><scrollButton>* [button]
Set the button used for the *onbutton* scroll method.
*button* is the decimal form of a value
from `linux/input-event-codes.h`.
*<libinput><device><sendEventsMode>* [yes|no|disabledOnExternalMouse] *<libinput><device><sendEventsMode>* [yes|no|disabledOnExternalMouse]
Optionally enable or disable sending any device events. Optionally enable or disable sending any device events.
@ -1298,7 +1329,7 @@ defined as shown below.
<!-- Action --> <!-- Action -->
<windowRule identifier="" title="" type=""> <windowRule identifier="" title="" type="">
<action name=""/> <action name="" />
</windowRule> </windowRule>
<!-- Property --> <!-- Property -->
@ -1445,11 +1476,57 @@ situation.
Whether to apply a bilinear filter to the magnified image, or Whether to apply a bilinear filter to the magnified image, or
just to use nearest-neighbour. Default is true - bilinear filtered. just to use nearest-neighbour. Default is true - bilinear filtered.
## PRIVILEGED INTERFACES
Labwc supports a small set of privileged wayland interfaces. All of these
interfaces are enabled by default for applications unless they are running
via a sandbox environment supporting the security-context-v1 protocol.
Security conscious users are suggested to use a sandbox framework to run
potentially untrusted applications as it additionally limits access to the
filesystem (including labwc configuration) and other services like dbus.
In addition to that, privileged protocols can be restricted for non-sandboxed
clients by defining a `<privilegedInterfaces>` block:
```
<privilegedInterfaces>
<allow>zwlr_layer_shell_v1</allow>
<allow>zwlr_virtual_pointer_manager_v1</allow>
</privilegedInterfaces>
```
*<privilegedInterfaces><allow>*
Name of the interface that should be allowed.
This is the full list of interfaces that can be controlled with this mechanism:
- `wp_drm_lease_device_v1`
- `zwlr_gamma_control_manager_v1`
- `zwlr_output_manager_v1`
- `zwlr_output_power_manager_v1`
- `zwp_input_method_manager_v2`
- `zwlr_virtual_pointer_manager_v1`
- `zwp_virtual_keyboard_manager_v1`
- `zwlr_export_dmabuf_manager_v1`
- `zwlr_screencopy_manager_v1`
- `ext_data_control_manager_v1`
- `zwlr_data_control_manager_v1`
- `wp_security_context_manager_v1`
- `ext_idle_notifier_v1`
- `zwlr_foreign_toplevel_manager_v1`
- `ext_foreign_toplevel_list_v1`
- `ext_session_lock_manager_v1`
- `zwlr_layer_shell_v1`
- `ext_workspace_manager_v1`
- `ext_image_copy_capture_manager_v1`
- `ext_output_image_capture_source_manager_v1`
## ENVIRONMENT VARIABLES ## ENVIRONMENT VARIABLES
*XCURSOR_PATH* *XCURSOR_PATH*
Specify a colon-separated list of paths to look for mouse cursors in. Specify a colon-separated list of paths to look for mouse cursors in.
Default Default is
~/.local/share/icons: ~/.local/share/icons:
~/.icons: ~/.icons:
/usr/share/icons: /usr/share/icons:
@ -1460,7 +1537,7 @@ situation.
*XCURSOR_SIZE* *XCURSOR_SIZE*
Specify an alternative mouse cursor size in pixels. Requires Specify an alternative mouse cursor size in pixels. Requires
XCURSOR_THEME to be set also. Default 24. XCURSOR_THEME to be set also. Default is 24.
*XCURSOR_THEME* *XCURSOR_THEME*
Specify a mouse cursor theme within XCURSOR_PATH. Specify a mouse cursor theme within XCURSOR_PATH.

View file

@ -12,7 +12,7 @@ Static menus are built based on the menu.xml file located at
# SYNTAX # SYNTAX
The menu file must be entirely enclosed within <openbox_menu> and The menu file must be entirely enclosed within <openbox_menu> and
</openbox_menu> tags. Inside these tags, menus are specified as follows: </openbox_menu> tags. Inside these tags, menus are specified as follows:
``` ```
<!-- A toplevel menu --> <!-- A toplevel menu -->
@ -111,7 +111,7 @@ Pipe menus are menus generated dynamically based on output of scripts or
binaries. They are so-called because the output of the executable is piped to binaries. They are so-called because the output of the executable is piped to
the labwc menu. the labwc menu.
For any *<menu id="" label="" execute="COMMAND"/>* entry in menu.xml, the For any *<menu id="" label="" execute="COMMAND" />* entry in menu.xml, the
COMMAND will be executed the first time the item is selected (for example by COMMAND will be executed the first time the item is selected (for example by
cursor or keyboard input). The XML output of the command will be parsed and cursor or keyboard input). The XML output of the command will be parsed and
shown as a submenu. The content of pipemenus is cached until the whole menu shown as a submenu. The content of pipemenus is cached until the whole menu
@ -124,7 +124,7 @@ menus, for example:
``` ```
<openbox_pipe_menu> <openbox_pipe_menu>
<item label="Terminal"> <item label="Terminal">
<action name="Execute" command="xterm"/> <action name="Execute" command="xterm" />
</item> </item>
</openbox_pipe_menu> </openbox_pipe_menu>
``` ```
@ -144,10 +144,25 @@ obmenu-generator with the menu generator of your choice):
``` ```
<?xml version="1.0"?> <?xml version="1.0"?>
<openbox_menu> <openbox_menu>
<menu id="root-menu" label="" execute="obmenu-generator"/> <menu id="root-menu" label="" execute="obmenu-generator" />
</openbox_menu> </openbox_menu>
``` ```
# ACCELERATORS / MNEMONICS
Menu accelerators are one-letter mnemonics to quickly select/exec items from
the current menu. For each menu item, the accelerator is defined as the first
character of the item label, converted to lowercase. A different accelerator
can be explicitly defined in menu.xml with the special '\_' character before the
target letter. Accelerators can be any unicode character and are not limited to
ASCII. A usual underscore can be shown by duplicating it.
If the menu only contains a single instance of the pressed accelerator the item
will be executed directly. Otherwise, all matching items are cycled through.
Example:
The accelerator for an item with the label "e_Macs" is 'm'.
# LOCALISATION # LOCALISATION
Available localisation for the default "client-menu" is only shown if no Available localisation for the default "client-menu" is only shown if no

View file

@ -246,7 +246,7 @@ osd.bg.bevel-width:2
*window.active.title.bg* *window.active.title.bg*
Texture for the focused window's titlebar. See texture section above. Texture for the focused window's titlebar. See texture section above.
Default is *Solid* Default is *Solid*.
*window.active.title.bg.width* *window.active.title.bg.width*
Used with beveled textures. Used with beveled textures.
@ -265,7 +265,7 @@ osd.bg.bevel-width:2
*window.inactive.title.bg* *window.inactive.title.bg*
Texture for non-focused windows' titlebars. See texture section above. Texture for non-focused windows' titlebars. See texture section above.
Default is *Solid* Default is *Solid*.
*window.inactive.title.bg.width* *window.inactive.title.bg.width*
Used with beveled textures. Used with beveled textures.
@ -685,7 +685,7 @@ all are supported.
Width of magnifier window border in pixels. Default is 1. Width of magnifier window border in pixels. Default is 1.
*magnifier.border.color* *magnifier.border.color*
Color of the magnfier window border. Default is #ff0000 (red). Color of the magnifier window border. Default is #ff0000 (red).
# BUTTONS # BUTTONS

View file

@ -66,10 +66,18 @@ the `--exit` and `--reconfigure` options use.
Manager, or the Window Manager can be launched independently first. On Manager, or the Window Manager can be launched independently first. On
Wayland, the Compositor is both Display Server and Window Manager, so Wayland, the Compositor is both Display Server and Window Manager, so
the described session management mechanisms do not work because the the described session management mechanisms do not work because the
Compositor needs to be running before the session can function. As some Compositor needs to be running before the session can function. As some
session clients support both X11 and Wayland, this command line option session clients support both X11 and Wayland, this command line option
avoids re-writes and fragmentation. avoids re-writes and fragmentation.
*-t, --title* <fmtstr>
Set the window title for labwc to use when it is running in a window
(i.e. nested in a compositor). <fmtstr> is a format string to be used as
the window title, replacing `%o` with the name of the output
region. This is useful when simulating multiple screens, such as with
running labwc with the environment variable `WLR_WL_OUTPUTS=2`. In this
case, `%o` will be unique per simulated screen.
*-v, --version* *-v, --version*
Show the version number and quit Show the version number and quit
@ -118,6 +126,25 @@ this is accomplished by setting the session variables to empty strings. For
systemd, the command `systemctl --user unset-environment` will be invoked to systemd, the command `systemctl --user unset-environment` will be invoked to
actually remove the variables from the activation environment. actually remove the variables from the activation environment.
A systemd user unit named `labwc-session.target` is also shipped alongside
the compositor for users who want to integrate labwc with systemd. It binds
to the standard `graphical-session.target`, so systemd user services can
start and stop in sync with the labwc session when they declare a WantedBy
or PartOf relationship to that target. Labwc does not activate the target
itself; users opt in by adding lines like the following to their
*autostart* and *shutdown* files:
```
systemctl --user --no-block start labwc-session.target
systemctl --user stop graphical-session.target
```
The example *autostart* and *shutdown* files shipped with labwc include
these commented out. To have a user service automatically started with
the session, enable it so the corresponding symlink under the
graphical-session.target.wants directory exists, for example by running
"systemctl --user enable dms.service".
# ENVIRONMENT VARIABLES # ENVIRONMENT VARIABLES
Set the environment variables listed below to enable specific debug options. Set the environment variables listed below to enable specific debug options.

View file

@ -23,7 +23,7 @@
Any menu with the id "workspaces" will be hidden Any menu with the id "workspaces" will be hidden
if there is only a single workspace available. if there is only a single workspace available.
--> -->
<menu id="client-send-to-menu"/> <menu id="client-send-to-menu" />
<!-- <!--
openbox default workspace selector openbox default workspace selector
to use replace above workspace menu with the example below to use replace above workspace menu with the example below
@ -56,9 +56,9 @@
# A prompt can be used as follows: # A prompt can be used as follows:
<item label="Exit"> <item label="Exit">
<action name="If"> <action name="If">
<prompt message="Do you really want to exit the compositor?"/> <prompt message="Do you really want to exit the compositor?" />
<then> <then>
<action name="Exit"/> <action name="Exit" />
</then> </then>
</action> </action>
</item> </item>

View file

@ -13,6 +13,7 @@
<adaptiveSync>no</adaptiveSync> <adaptiveSync>no</adaptiveSync>
<allowTearing>no</allowTearing> <allowTearing>no</allowTearing>
<autoEnableOutputs>yes</autoEnableOutputs> <autoEnableOutputs>yes</autoEnableOutputs>
<hdr>no</hdr>
<reuseOutputMode>no</reuseOutputMode> <reuseOutputMode>no</reuseOutputMode>
<xwaylandPersistence>no</xwaylandPersistence> <xwaylandPersistence>no</xwaylandPersistence>
<primarySelection>yes</primarySelection> <primarySelection>yes</primarySelection>
@ -158,6 +159,8 @@
<followMouse>no</followMouse> <followMouse>no</followMouse>
<followMouseRequiresMovement>yes</followMouseRequiresMovement> <followMouseRequiresMovement>yes</followMouseRequiresMovement>
<raiseOnFocus>no</raiseOnFocus> <raiseOnFocus>no</raiseOnFocus>
<!-- Delay (ms) before applying raise-on-focus. 0 = immediate. -->
<raiseOnFocusDelay>0</raiseOnFocusDelay>
</focus> </focus>
<snapping> <snapping>
@ -276,6 +279,9 @@
<keybind key="W-a"> <keybind key="W-a">
<action name="ToggleMaximize" /> <action name="ToggleMaximize" />
</keybind> </keybind>
<keybind key="W-d">
<action name="ToggleShowDesktop" />
</keybind>
<keybind key="W-Left"> <keybind key="W-Left">
<action name="SnapToEdge" direction="left" combine="yes" /> <action name="SnapToEdge" direction="left" combine="yes" />
</keybind> </keybind>
@ -291,19 +297,19 @@
<keybind key="A-Space"> <keybind key="A-Space">
<action name="ShowMenu" menu="client-menu" atCursor="no" /> <action name="ShowMenu" menu="client-menu" atCursor="no" />
</keybind> </keybind>
<keybind key="XF86_AudioLowerVolume"> <keybind key="XF86AudioLowerVolume">
<action name="Execute" command="amixer sset Master 5%-" /> <action name="Execute" command="pactl set-sink-volume @DEFAULT_SINK@ -5%" />
</keybind> </keybind>
<keybind key="XF86_AudioRaiseVolume"> <keybind key="XF86AudioRaiseVolume">
<action name="Execute" command="amixer sset Master 5%+" /> <action name="Execute" command="pactl set-sink-volume @DEFAULT_SINK@ +5%" />
</keybind> </keybind>
<keybind key="XF86_AudioMute"> <keybind key="XF86AudioMute">
<action name="Execute" command="amixer sset Master toggle" /> <action name="Execute" command="pactl set-sink-mute @DEFAULT_SINK@ toggle" />
</keybind> </keybind>
<keybind key="XF86_MonBrightnessUp"> <keybind key="XF86MonBrightnessUp">
<action name="Execute" command="brightnessctl set +10%" /> <action name="Execute" command="brightnessctl set +10%" />
</keybind> </keybind>
<keybind key="XF86_MonBrightnessDown"> <keybind key="XF86MonBrightnessDown">
<action name="Execute" command="brightnessctl set 10%-" /> <action name="Execute" command="brightnessctl set 10%-" />
</keybind> </keybind>
<!-- SnapToRegion via W-Numpad --> <!-- SnapToRegion via W-Numpad -->
@ -592,7 +598,7 @@
- accelProfile [flat|adaptive] - accelProfile [flat|adaptive]
- tapButtonMap [lrm|lmr] - tapButtonMap [lrm|lmr]
- clickMethod [none|buttonAreas|clickfinger] - clickMethod [none|buttonAreas|clickfinger]
- scrollMethod [twoFinger|edge|none] - scrollMethod [twoFinger|edge|onbutton|none]
- sendEventsMode [yes|no|disabledOnExternalMouse] - sendEventsMode [yes|no|disabledOnExternalMouse]
- calibrationMatrix [six float values split by space] - calibrationMatrix [six float values split by space]
- scrollFactor [float] - scrollFactor [float]
@ -618,6 +624,7 @@
<!-- <disableWhileTyping>yes</disableWhileTyping> --> <!-- <disableWhileTyping>yes</disableWhileTyping> -->
<!-- <clickMethod>buttonAreas</clickMethod> --> <!-- <clickMethod>buttonAreas</clickMethod> -->
<!-- <scrollMethod>twofinger</scrollMethod> --> <!-- <scrollMethod>twofinger</scrollMethod> -->
<!-- <scrollButton>274</scrollButton> -->
<!-- <sendEventsMode>yes</sendEventsMode> --> <!-- <sendEventsMode>yes</sendEventsMode> -->
<!-- <calibrationMatrix>1 0 0 0 1 0</calibrationMatrix> --> <!-- <calibrationMatrix>1 0 0 0 1 0</calibrationMatrix> -->
<scrollFactor>1.0</scrollFactor> <scrollFactor>1.0</scrollFactor>
@ -638,10 +645,10 @@
# string and '?' matches any single character. # string and '?' matches any single character.
<windowRules> <windowRules>
<windowRule identifier="*"><action name="Maximize"/></windowRule> <windowRule identifier="*"><action name="Maximize" /></windowRule>
<windowRule identifier="foo" serverDecoration="yes"/> <windowRule identifier="foo" serverDecoration="yes" />
<windowRule title="bar" serverDecoration="yes"/> <windowRule title="bar" serverDecoration="yes" />
<windowRule identifier="baz" title="quax" serverDecoration="yes"/> <windowRule identifier="baz" title="quax" serverDecoration="yes" />
</windowRules> </windowRules>
# Example below for `lxqt-panel` and `pcmanfm-qt \-\-desktop` # Example below for `lxqt-panel` and `pcmanfm-qt \-\-desktop`
@ -652,18 +659,18 @@
<windowRule identifier="lxqt-panel" matchOnce="true"> <windowRule identifier="lxqt-panel" matchOnce="true">
<skipTaskbar>yes</skipTaskbar> <skipTaskbar>yes</skipTaskbar>
<action name="MoveTo" x="0" y="0" /> <action name="MoveTo" x="0" y="0" />
<action name="ToggleAlwaysOnTop"/> <action name="ToggleAlwaysOnTop" />
</windowRule> </windowRule>
<windowRule title="pcmanfm-desktop*"> <windowRule title="pcmanfm-desktop*">
<skipTaskbar>yes</skipTaskbar> <skipTaskbar>yes</skipTaskbar>
<skipWindowSwitcher>yes</skipWindowSwitcher> <skipWindowSwitcher>yes</skipWindowSwitcher>
<fixedPosition>yes</fixedPosition> <fixedPosition>yes</fixedPosition>
<action name="MoveTo" x="0" y="0" /> <action name="MoveTo" x="0" y="0" />
<action name="ToggleAlwaysOnBottom"/> <action name="ToggleAlwaysOnBottom" />
</windowRule> </windowRule>
<windowRule identifier="org.qutebrowser.qutebrowser"> <windowRule identifier="org.qutebrowser.qutebrowser">
<action name="ResizeTo" width="1024" height="800" /> <action name="ResizeTo" width="1024" height="800" />
<action name="AutoPlace"/> <action name="AutoPlace" />
</windowRule> </windowRule>
</windowRules> </windowRules>
--> -->

View file

@ -3,3 +3,11 @@
# This file is executed as a shell script when labwc is preparing to terminate # This file is executed as a shell script when labwc is preparing to terminate
# itself. # itself.
# For further details see labwc-config(5). # For further details see labwc-config(5).
# When running under systemd, uncomment the systemctl line below to tear down
# graphical-session.target (which cascades to labwc-session.target via
# BindsTo, and to any service declaring PartOf=graphical-session.target).
# Running synchronously here ensures those services are stopped before the
# Wayland socket goes away, avoiding "Broken pipe" failures on teardown.
#
# systemctl --user stop graphical-session.target

View file

@ -4,6 +4,7 @@
#include <cairo.h> #include <cairo.h>
#include <pango/pango-font.h> #include <pango/pango-font.h>
#include <stdbool.h>
struct lab_data_buffer; struct lab_data_buffer;
@ -43,10 +44,11 @@ void font_get_buffer_size(int max_width, const char *text, struct font *font,
* @font: font description * @font: font description
* @color: foreground color in rgba format * @color: foreground color in rgba format
* @bg_pattern: background pattern * @bg_pattern: background pattern
* @use_markup: flag to render pango markup
*/ */
void font_buffer_create(struct lab_data_buffer **buffer, int max_width, void font_buffer_create(struct lab_data_buffer **buffer, int max_width,
int height, const char *text, struct font *font, const float *color, int height, const char *text, struct font *font, const float *color,
cairo_pattern_t *bg_pattern, double scale); cairo_pattern_t *bg_pattern, double scale, bool use_markup);
/** /**
* font_finish - free some font related resources * font_finish - free some font related resources

View file

@ -62,8 +62,12 @@
#define BOUNDED_INT(a) ((a) < INT_MAX && (a) > INT_MIN) #define BOUNDED_INT(a) ((a) < INT_MAX && (a) > INT_MIN)
#endif #endif
#define LAB_WLR_VERSION_AT_LEAST(major, minor, micro) \ #define _LAB_CALC_WLR_VERSION_NUM(major, minor, micro) (((major) << 16) | ((minor) << 8) | (micro))
(WLR_VERSION_NUM >= (((major) << 16) | ((minor) << 8) | (micro)))
#define LAB_WLR_VERSION_AT_LEAST(major, minor, micro) ( \
server.wlr_version >= _LAB_CALC_WLR_VERSION_NUM(major, minor, micro))
#define LAB_WLR_VERSION_LOWER(major, minor, micro) (!LAB_WLR_VERSION_AT_LEAST(major, minor, micro))
/** /**
* PIXEL () - calculate pixel offset in an array * PIXEL () - calculate pixel offset in an array

View file

@ -16,6 +16,12 @@ pid_t spawn_primary_client(const char *command);
*/ */
void spawn_async_no_shell(char const *command); void spawn_async_no_shell(char const *command);
/**
* spawn_sync_no_shell - execute synchronously
* @command: command to be executed
*/
void spawn_sync_no_shell(char const *command);
/** /**
* spawn_piped - execute asynchronously * spawn_piped - execute asynchronously
* @command: command to be executed * @command: command to be executed

View file

@ -28,6 +28,9 @@ static struct key_combos {
}, { }, {
.binding = "W-a", .binding = "W-a",
.action = "ToggleMaximize", .action = "ToggleMaximize",
}, {
.binding = "W-d",
.action = "ToggleShowDesktop",
}, { }, {
.binding = "W-Left", .binding = "W-Left",
.action = "SnapToEdge", .action = "SnapToEdge",
@ -84,35 +87,35 @@ static struct key_combos {
.value = "no", .value = "no",
}, },
}, { }, {
.binding = "XF86_AudioLowerVolume", .binding = "XF86AudioLowerVolume",
.action = "Execute", .action = "Execute",
.attributes[0] = { .attributes[0] = {
.name = "command", .name = "command",
.value = "amixer sset Master 5%-", .value = "pactl set-sink-volume @DEFAULT_SINK@ -5%",
}, },
}, { }, {
.binding = "XF86_AudioRaiseVolume", .binding = "XF86AudioRaiseVolume",
.action = "Execute", .action = "Execute",
.attributes[0] = { .attributes[0] = {
.name = "command", .name = "command",
.value = "amixer sset Master 5%+", .value = "pactl set-sink-volume @DEFAULT_SINK@ +5%",
}, },
}, { }, {
.binding = "XF86_AudioMute", .binding = "XF86AudioMute",
.action = "Execute", .action = "Execute",
.attributes[0] = { .attributes[0] = {
.name = "command", .name = "command",
.value = "amixer sset Master toggle", .value = "pactl set-sink-mute @DEFAULT_SINK@ toggle",
}, },
}, { }, {
.binding = "XF86_MonBrightnessUp", .binding = "XF86MonBrightnessUp",
.action = "Execute", .action = "Execute",
.attributes[0] = { .attributes[0] = {
.name = "command", .name = "command",
.value = "brightnessctl set +10%", .value = "brightnessctl set +10%",
}, },
}, { }, {
.binding = "XF86_MonBrightnessDown", .binding = "XF86MonBrightnessDown",
.action = "Execute", .action = "Execute",
.attributes[0] = { .attributes[0] = {
.name = "command", .name = "command",
@ -141,14 +144,14 @@ static struct key_combos {
* <mouse> * <mouse>
* <context name="Maximize"> * <context name="Maximize">
* <mousebind button="Left" action="Click"> * <mousebind button="Left" action="Click">
* <action name="Focus"/> * <action name="Focus" />
* <action name="Raise"/> * <action name="Raise" />
* <action name="ToggleMaximize"/> * <action name="ToggleMaximize" />
* </mousebind> * </mousebind>
* </context> * </context>
* <context name="Root"> * <context name="Root">
* <mousebind direction="Up" action="Scroll"> * <mousebind direction="Up" action="Scroll">
* <action name="GoToDesktop" to="left" wrap="yes"/> * <action name="GoToDesktop" to="left" wrap="yes" />
* </mousebind> * </mousebind>
* </context> * </context>
* </mouse> * </mouse>

View file

@ -23,6 +23,7 @@ struct keybind {
struct wl_list actions; /* struct action.link */ struct wl_list actions; /* struct action.link */
struct wl_list link; /* struct rcxml.keybinds */ struct wl_list link; /* struct rcxml.keybinds */
bool on_release; bool on_release;
bool override_inhibition;
}; };
/** /**

View file

@ -31,6 +31,7 @@ struct libinput_category {
int dwt; /* -1 or libinput_config_dwt_state */ int dwt; /* -1 or libinput_config_dwt_state */
int click_method; /* -1 or libinput_config_click_method */ int click_method; /* -1 or libinput_config_click_method */
int scroll_method; /* -1 or libinput_config_scroll_method */ int scroll_method; /* -1 or libinput_config_scroll_method */
int scroll_button; /* -1 or a button from linux/input_event_codes.h */
int send_events_mode; /* -1 or libinput_config_send_events_mode */ int send_events_mode; /* -1 or libinput_config_send_events_mode */
bool have_calibration_matrix; bool have_calibration_matrix;
double scroll_factor; double scroll_factor;

View file

@ -36,6 +36,12 @@ enum tearing_mode {
LAB_TEARING_FULLSCREEN_FORCED, LAB_TEARING_FULLSCREEN_FORCED,
}; };
enum render_bit_depth {
LAB_RENDER_BIT_DEPTH_DEFAULT = 0,
LAB_RENDER_BIT_DEPTH_8,
LAB_RENDER_BIT_DEPTH_10,
};
enum tiling_events_mode { enum tiling_events_mode {
LAB_TILING_EVENTS_NEVER = 0, LAB_TILING_EVENTS_NEVER = 0,
LAB_TILING_EVENTS_REGION = 1 << 0, LAB_TILING_EVENTS_REGION = 1 << 0,
@ -74,8 +80,10 @@ struct rcxml {
int gap; int gap;
enum adaptive_sync_mode adaptive_sync; enum adaptive_sync_mode adaptive_sync;
enum tearing_mode allow_tearing; enum tearing_mode allow_tearing;
enum render_bit_depth target_render_depth;
bool auto_enable_outputs; bool auto_enable_outputs;
bool reuse_output_mode; bool reuse_output_mode;
uint32_t allowed_interfaces;
bool xwayland_persistence; bool xwayland_persistence;
bool primary_selection; bool primary_selection;
char *prompt_command; char *prompt_command;
@ -89,6 +97,7 @@ struct rcxml {
bool focus_follow_mouse; bool focus_follow_mouse;
bool focus_follow_mouse_requires_movement; bool focus_follow_mouse_requires_movement;
bool raise_on_focus; bool raise_on_focus;
uint32_t raise_on_focus_delay_ms;
/* theme */ /* theme */
char *theme_name; char *theme_name;
@ -225,4 +234,6 @@ void rcxml_finish(void);
*/ */
void append_parsed_actions(xmlNode *node, struct wl_list *list); void append_parsed_actions(xmlNode *node, struct wl_list *list);
uint32_t parse_privileged_interface(const char *name);
#endif /* LABWC_RCXML_H */ #endif /* LABWC_RCXML_H */

View file

@ -103,6 +103,10 @@ void cycle_finish(bool switch_focus);
/* Re-initialize the window switcher */ /* Re-initialize the window switcher */
void cycle_reinitialize(void); void cycle_reinitialize(void);
/* Immediately cycle to next/previous window */
void cycle_immediate(enum lab_cycle_dir direction,
struct cycle_filter filter);
/* Focus the clicked window and close OSD */ /* Focus the clicked window and close OSD */
void cycle_on_cursor_release(struct wlr_scene_node *node); void cycle_on_cursor_release(struct wlr_scene_node *node);

View file

@ -5,12 +5,17 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
struct seat;
/* /*
* All keycodes in these functions are (Linux) libinput evdev scancodes which is * All keycodes in these functions are (Linux) libinput evdev scancodes which is
* what 'wlr_keyboard' uses (e.g. 'seat->keyboard_group->keyboard->keycodes'). * what 'wlr_keyboard' uses (e.g. 'seat->keyboard_group->keyboard->keycodes').
* Note: These keycodes are different to XKB scancodes by a value of 8. * Note: These keycodes are different to XKB scancodes by a value of 8.
*/ */
void key_state_indicator_update(struct seat *seat);
void key_state_indicator_toggle(void);
/** /**
* key_state_pressed_sent_keycodes - generate array of pressed+sent keys * key_state_pressed_sent_keycodes - generate array of pressed+sent keys
* Note: The array is generated by subtracting any bound keys from _all_ pressed * Note: The array is generated by subtracting any bound keys from _all_ pressed

View file

@ -148,8 +148,15 @@ struct seat {
}; };
struct server { struct server {
uint32_t wlr_version;
struct wl_display *wl_display; struct wl_display *wl_display;
struct wl_event_loop *wl_event_loop; /* Can be used for timer events */ struct wl_event_loop *wl_event_loop; /* Can be used for timer events */
/* Pending auto-raise timer (used when rc.raise_on_focus_delay_ms > 0) */
struct view *pending_auto_raise_view;
struct wl_event_source *pending_auto_raise_timer;
struct wlr_renderer *renderer; struct wlr_renderer *renderer;
struct wlr_allocator *allocator; struct wlr_allocator *allocator;
struct wlr_backend *backend; struct wlr_backend *backend;
@ -187,6 +194,13 @@ struct server {
struct wlr_xdg_toplevel_icon_manager_v1 *xdg_toplevel_icon_manager; struct wlr_xdg_toplevel_icon_manager_v1 *xdg_toplevel_icon_manager;
struct wl_listener xdg_toplevel_icon_set_icon; struct wl_listener xdg_toplevel_icon_set_icon;
struct {
struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 *manager;
struct {
struct wl_listener new_request;
} on;
} toplevel_capture;
/* front to back order */ /* front to back order */
struct wl_list views; struct wl_list views;
uint64_t next_view_creation_id; uint64_t next_view_creation_id;
@ -308,6 +322,8 @@ struct server {
struct sfdo *sfdo; struct sfdo *sfdo;
pid_t primary_client_pid; pid_t primary_client_pid;
char *title_fmt;
}; };
/* defined in main.c */ /* defined in main.c */
@ -343,6 +359,13 @@ void xdg_shell_finish(void);
*/ */
void desktop_focus_view(struct view *view, bool raise); void desktop_focus_view(struct view *view, bool raise);
/**
* desktop_cancel_pending_auto_raise() - cancel any pending delayed auto-raise
* (from raiseOnFocusDelay). Called when a view is being destroyed, on config
* reload, or when a new focus change with raise=false supersedes the pending.
*/
void desktop_cancel_pending_auto_raise(void);
/** /**
* desktop_focus_view_or_surface() - like desktop_focus_view() but can * desktop_focus_view_or_surface() - like desktop_focus_view() but can
* also focus other (e.g. xwayland-unmanaged) surfaces * also focus other (e.g. xwayland-unmanaged) surfaces

View file

@ -23,9 +23,11 @@ struct menuitem {
char *text; char *text;
char *icon_name; char *icon_name;
const char *arrow; const char *arrow;
uint32_t accelerator;
struct menu *parent; struct menu *parent;
struct menu *submenu; struct menu *submenu;
bool selectable; bool selectable;
bool use_markup;
enum menuitem_type type; enum menuitem_type type;
int native_width; int native_width;
struct wlr_scene_tree *tree; struct wlr_scene_tree *tree;
@ -66,6 +68,19 @@ struct menu {
/* For keyboard support */ /* For keyboard support */
void menu_item_select_next(void); void menu_item_select_next(void);
void menu_item_select_previous(void); void menu_item_select_previous(void);
/**
* menu_item_select_by_accelerator - selects the next menu item with
* a matching accelerator, starting after the current selection
*
* @accelerator a shortcut to quickly select/open an item, defined in menu.xml
* with an underscore in the item label before the target letter.
*
* Return: a boolean value that represents whether the newly selected item
* needs to be executed.
*/
bool menu_item_select_by_accelerator(uint32_t accelerator);
void menu_submenu_enter(void); void menu_submenu_enter(void);
void menu_submenu_leave(void); void menu_submenu_leave(void);
bool menu_call_selected_actions(void); bool menu_call_selected_actions(void);
@ -100,7 +115,7 @@ void menu_open_root(struct menu *menu, int x, int y);
void menu_process_cursor_motion(struct wlr_scene_node *node); void menu_process_cursor_motion(struct wlr_scene_node *node);
/** /**
* menu_close_root- close root menu * menu_close_root - close root menu
* *
* This function will close server.menu_current and set it to NULL. * This function will close server.menu_current and set it to NULL.
* Asserts that server.input_mode is set to LAB_INPUT_STATE_MENU. * Asserts that server.input_mode is set to LAB_INPUT_STATE_MENU.

View file

@ -71,6 +71,9 @@ struct wlr_box output_usable_area_in_layout_coords(struct output *output);
void handle_output_power_manager_set_mode(struct wl_listener *listener, void handle_output_power_manager_set_mode(struct wl_listener *listener,
void *data); void *data);
void output_enable_adaptive_sync(struct output *output, bool enabled); void output_enable_adaptive_sync(struct output *output, bool enabled);
void output_enable_hdr(struct output *output, struct wlr_output_state *os,
bool enabled, bool silent);
void output_state_setup_hdr(struct output *output, bool silent);
/** /**
* Notifies whether a fullscreen view is displayed on the given output. * Notifies whether a fullscreen view is displayed on the given output.

View file

@ -130,7 +130,7 @@ void scaled_buffer_request_update(struct scaled_buffer *self,
/** /**
* scaled_buffer_invalidate_sharing - clear the list of entire cached * scaled_buffer_invalidate_sharing - clear the list of entire cached
* scaled_buffers used to share visually dupliated buffers. This should * scaled_buffers used to share visually duplicated buffers. This should
* be called on Reconfigure to force updates of newly created * be called on Reconfigure to force updates of newly created
* scaled_buffers rather than reusing ones created before Reconfigure. * scaled_buffers rather than reusing ones created before Reconfigure.
*/ */

View file

@ -15,6 +15,7 @@ struct scaled_font_buffer {
/* Private */ /* Private */
char *text; char *text;
bool use_markup;
int max_width; int max_width;
float color[4]; float color[4];
float bg_color[4]; float bg_color[4];
@ -69,8 +70,18 @@ scaled_font_buffer_create_for_titlebar(struct wlr_scene_tree *parent,
* bg_color is ignored for font buffers created with * bg_color is ignored for font buffers created with
* scaled_font_buffer_create_for_titlebar(). * scaled_font_buffer_create_for_titlebar().
*/ */
void scaled_font_buffer_update(struct scaled_font_buffer *self, const char *text, void scaled_font_buffer_update(struct scaled_font_buffer *self,
int max_width, struct font *font, const float *color, const char *text, int max_width, struct font *font, const float *color,
const float *bg_color); const float *bg_color);
/**
* Update an existing auto scaling font buffer allowing the use of pango markup.
*
* Behaves identically to scaled_font_buffer_update(), but allows customization
* of the `use_markup` field of the @self struct via @use_markup.
*/
void scaled_font_buffer_update_markup(struct scaled_font_buffer *self,
const char *text, int max_width, struct font *font, const float *color,
const float *bg_color, bool use_markup);
#endif /* LABWC_SCALED_FONT_BUFFER_H */ #endif /* LABWC_SCALED_FONT_BUFFER_H */

8
include/show-desktop.h Normal file
View file

@ -0,0 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef LABWC_SHOW_DESKTOP_H
#define LABWC_SHOW_DESKTOP_H
void show_desktop_toggle(void);
void show_desktop_reset(void);
#endif /* LABWC_SHOW_DESKTOP_H */

View file

@ -177,6 +177,12 @@ struct view {
char *title; char *title;
char *app_id; /* WM_CLASS for xwayland windows */ char *app_id; /* WM_CLASS for xwayland windows */
struct {
struct wlr_scene *scene;
struct wlr_ext_image_capture_source_v1 *source;
struct wl_listener on_capture_source_destroy;
} capture;
bool mapped; bool mapped;
bool been_mapped; bool been_mapped;
uint64_t creation_id; uint64_t creation_id;
@ -184,6 +190,7 @@ struct view {
enum ssd_preference ssd_preference; enum ssd_preference ssd_preference;
bool shaded; bool shaded;
bool minimized; bool minimized;
bool was_minimized_by_show_desktop_action;
enum view_axis maximized; enum view_axis maximized;
bool fullscreen; bool fullscreen;
bool tearing_hint; bool tearing_hint;
@ -319,6 +326,7 @@ struct xdg_toplevel_view {
/* Events unique to xdg-toplevel views */ /* Events unique to xdg-toplevel views */
struct wl_listener set_app_id; struct wl_listener set_app_id;
struct wl_listener request_show_window_menu; struct wl_listener request_show_window_menu;
struct wl_listener set_parent;
struct wl_listener new_popup; struct wl_listener new_popup;
}; };
@ -630,4 +638,6 @@ enum lab_placement_policy view_placement_parse(const char *policy);
/* xdg.c */ /* xdg.c */
struct wlr_xdg_surface *xdg_surface_from_view(struct view *view); struct wlr_xdg_surface *xdg_surface_from_view(struct view *view);
bool view_matches_criteria(struct view *view, enum lab_view_criteria criteria);
#endif /* LABWC_VIEW_H */ #endif /* LABWC_VIEW_H */

View file

@ -1,7 +1,7 @@
project( project(
'labwc', 'labwc',
'c', 'c',
version: '0.9.5', version: '0.20.0',
license: 'GPL-2.0-only', license: 'GPL-2.0-only',
meson_version: '>=0.59.0', meson_version: '>=0.59.0',
default_options: [ default_options: [
@ -53,7 +53,7 @@ add_project_arguments('-DLABWC_VERSION=@0@'.format(version), language: 'c')
wlroots = dependency( wlroots = dependency(
'wlroots-0.20', 'wlroots-0.20',
default_options: ['default_library=static', 'examples=false'], default_options: ['default_library=static', 'examples=false'],
version: ['>=0.20.0', '<0.21.0'], version: ['>=0.20.1', '<0.21.0'],
) )
wlroots_has_xwayland = wlroots.get_variable('have_xwayland') == 'true' wlroots_has_xwayland = wlroots.get_variable('have_xwayland') == 'true'
@ -211,6 +211,17 @@ install_data('data/labwc.desktop', install_dir: get_option('datadir') / 'wayland
install_data('data/labwc-portals.conf', install_dir: get_option('datadir') / 'xdg-desktop-portal') install_data('data/labwc-portals.conf', install_dir: get_option('datadir') / 'xdg-desktop-portal')
# Install labwc-session.target so that systemd user services with
# WantedBy=graphical-session.target can be started and stopped in sync
# with a labwc session (see labwc(1) SESSION MANAGEMENT for the opt-in
# autostart/shutdown snippet).
systemd_feat = get_option('systemd-session')
systemd = dependency('systemd', required: systemd_feat)
if systemd.found()
install_data('data/labwc-session.target',
install_dir: systemd.get_variable('systemduserunitdir'))
endif
icons = ['labwc-symbolic.svg', 'labwc.svg'] icons = ['labwc-symbolic.svg', 'labwc.svg']
foreach icon : icons foreach icon : icons
icon_path = join_paths('data', icon) icon_path = join_paths('data', icon)

View file

@ -4,6 +4,7 @@ option('svg', type: 'feature', value: 'enabled', description: 'Enable svg window
option('icon', type: 'feature', value: 'enabled', description: 'Enable window icons') option('icon', type: 'feature', value: 'enabled', description: 'Enable window icons')
option('labnag', type: 'feature', value: 'auto', description: 'Build labnag notification daemon') option('labnag', type: 'feature', value: 'auto', description: 'Build labnag notification daemon')
option('nls', type: 'feature', value: 'auto', description: 'Enable native language support') option('nls', type: 'feature', value: 'auto', description: 'Enable native language support')
option('systemd-session', type: 'feature', value: 'auto', description: 'Install labwc-session.target systemd user unit')
option('static_analyzer', type: 'feature', value: 'disabled', description: 'Run gcc static analyzer') option('static_analyzer', type: 'feature', value: 'disabled', description: 'Run gcc static analyzer')
option('test', type: 'feature', value: 'disabled', description: 'Run tests') option('test', type: 'feature', value: 'disabled', description: 'Run tests')
option('sections', type: 'feature', value: 'disabled', description: 'Show unused functions') option('sections', type: 'feature', value: 'disabled', description: 'Show unused functions')

View file

@ -1 +1 @@
ar ca cs da de el es et eu fa fi fr gl he hr hu id it ja ka kk ko lt ms nl pa pl pt pt_BR ru sk sv tr uk vi zh_CN zh_TW ar ca cs da de el es et eu fa fi fr gl he hr hu id it ja ka kab kk ko lt ms nl pa pl pt pt_BR ru sk sr_Latn sv tr uk vi zh_CN zh_TW

View file

@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: labwc\n" "Project-Id-Version: labwc\n"
"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" "Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n"
"POT-Creation-Date: 2024-09-19 21:09+1000\n" "POT-Creation-Date: 2024-09-19 21:09+1000\n"
"PO-Revision-Date: 2025-08-15 20:01+0000\n" "PO-Revision-Date: 2026-05-18 18:28+0000\n"
"Last-Translator: Ettore Atalan <atalanttore@googlemail.com>\n" "Last-Translator: Demian <Demian@gmx.co.uk>\n"
"Language-Team: German <https://translate.lxqt-project.org/projects/labwc/" "Language-Team: German <https://translate.lxqt-project.org/projects/labwc/"
"labwc/de/>\n" "labwc/de/>\n"
"Language: de\n" "Language: de\n"
@ -21,7 +21,7 @@ msgstr ""
#: src/menu/menu.c:1016 #: src/menu/menu.c:1016
msgid "Go there..." msgid "Go there..."
msgstr "Dorthin gehen..." msgstr "Dorthin wechseln ..."
#: src/menu/menu.c:1034 #: src/menu/menu.c:1034
msgid "Terminal" msgid "Terminal"
@ -61,11 +61,11 @@ msgstr "Immer im Vordergrund"
#: src/menu/menu.c:1071 #: src/menu/menu.c:1071
msgid "Move Left" msgid "Move Left"
msgstr "nach links" msgstr "Nach links"
#: src/menu/menu.c:1078 #: src/menu/menu.c:1078
msgid "Move Right" msgid "Move Right"
msgstr "nach rechts" msgstr "Nach rechts"
#: src/menu/menu.c:1083 #: src/menu/menu.c:1083
msgid "Always on Visible Workspace" msgid "Always on Visible Workspace"

80
po/kab.po Normal file
View file

@ -0,0 +1,80 @@
# Labwc pot file
# Copyright (C) 2024
# This file is distributed under the same license as the labwc package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: labwc\n"
"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n"
"POT-Creation-Date: 2024-09-19 21:09+1000\n"
"PO-Revision-Date: 2026-03-31 08:01+0000\n"
"Last-Translator: ButterflyOfFire <butterflyoffire@protonmail.com>\n"
"Language-Team: Kabyle <https://translate.lxqt-project.org/projects/labwc/"
"labwc/kab/>\n"
"Language: kab\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 4.2.1\n"
#: src/menu/menu.c:1016
msgid "Go there..."
msgstr ""
#: src/menu/menu.c:1034
msgid "Terminal"
msgstr "Ixf"
#: src/menu/menu.c:1040
msgid "Reconfigure"
msgstr ""
#: src/menu/menu.c:1042
msgid "Exit"
msgstr "Ffeɣ"
#: src/menu/menu.c:1056
msgid "Minimize"
msgstr ""
#: src/menu/menu.c:1058
msgid "Maximize"
msgstr ""
#: src/menu/menu.c:1060
msgid "Fullscreen"
msgstr "Agdil aččuran"
#: src/menu/menu.c:1062
msgid "Roll Up/Down"
msgstr ""
#: src/menu/menu.c:1064
msgid "Decorations"
msgstr ""
#: src/menu/menu.c:1066
msgid "Always on Top"
msgstr ""
#: src/menu/menu.c:1071
msgid "Move Left"
msgstr ""
#: src/menu/menu.c:1078
msgid "Move Right"
msgstr ""
#: src/menu/menu.c:1083
msgid "Always on Visible Workspace"
msgstr ""
#: src/menu/menu.c:1086
msgid "Workspace"
msgstr ""
#: src/menu/menu.c:1089
msgid "Close"
msgstr "Mdel"

81
po/sr_Latn.po Normal file
View file

@ -0,0 +1,81 @@
# Labwc pot file
# Copyright (C) 2024
# This file is distributed under the same license as the labwc package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: labwc\n"
"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n"
"POT-Creation-Date: 2024-09-19 21:09+1000\n"
"PO-Revision-Date: 2026-05-05 08:28+0000\n"
"Last-Translator: Tobias Si <dragoslav123@protonmail.com>\n"
"Language-Team: Serbian (latin) <https://translate.lxqt-project.org/projects/"
"labwc/labwc/sr_Latn/>\n"
"Language: sr_Latn\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Generator: Weblate 4.2.1\n"
#: src/menu/menu.c:1016
msgid "Go there..."
msgstr "Idi tamo..."
#: src/menu/menu.c:1034
msgid "Terminal"
msgstr "Terminal"
#: src/menu/menu.c:1040
msgid "Reconfigure"
msgstr "Podesi"
#: src/menu/menu.c:1042
msgid "Exit"
msgstr "Izlaz"
#: src/menu/menu.c:1056
msgid "Minimize"
msgstr "Minimiziraj"
#: src/menu/menu.c:1058
msgid "Maximize"
msgstr "Maksimiziraj"
#: src/menu/menu.c:1060
msgid "Fullscreen"
msgstr "Prikaz preko celog ekrana"
#: src/menu/menu.c:1062
msgid "Roll Up/Down"
msgstr "Prevuci Gore/Dole"
#: src/menu/menu.c:1064
msgid "Decorations"
msgstr "Dekoracije"
#: src/menu/menu.c:1066
msgid "Always on Top"
msgstr "Uvek na vrhu"
#: src/menu/menu.c:1071
msgid "Move Left"
msgstr "Pomeri levo"
#: src/menu/menu.c:1078
msgid "Move Right"
msgstr "Pomeri desno"
#: src/menu/menu.c:1083
msgid "Always on Visible Workspace"
msgstr "Uvek na vidljivom radnom prostoru"
#: src/menu/menu.c:1086
msgid "Workspace"
msgstr "Radni prostor"
#: src/menu/menu.c:1089
msgid "Close"
msgstr "Zatvori"

View file

@ -21,12 +21,14 @@
#include "cycle.h" #include "cycle.h"
#include "debug.h" #include "debug.h"
#include "input/keyboard.h" #include "input/keyboard.h"
#include "input/key-state.h"
#include "labwc.h" #include "labwc.h"
#include "magnifier.h" #include "magnifier.h"
#include "menu/menu.h" #include "menu/menu.h"
#include "output.h" #include "output.h"
#include "output-virtual.h" #include "output-virtual.h"
#include "regions.h" #include "regions.h"
#include "show-desktop.h"
#include "ssd.h" #include "ssd.h"
#include "theme.h" #include "theme.h"
#include "translate.h" #include "translate.h"
@ -82,6 +84,8 @@ struct action_arg_list {
X(SHRINK_TO_EDGE, "ShrinkToEdge") \ X(SHRINK_TO_EDGE, "ShrinkToEdge") \
X(NEXT_WINDOW, "NextWindow") \ X(NEXT_WINDOW, "NextWindow") \
X(PREVIOUS_WINDOW, "PreviousWindow") \ X(PREVIOUS_WINDOW, "PreviousWindow") \
X(NEXT_WINDOW_IMMEDIATE, "NextWindowImmediate") \
X(PREVIOUS_WINDOW_IMMEDIATE, "PreviousWindowImmediate") \
X(RECONFIGURE, "Reconfigure") \ X(RECONFIGURE, "Reconfigure") \
X(SHOW_MENU, "ShowMenu") \ X(SHOW_MENU, "ShowMenu") \
X(TOGGLE_MAXIMIZE, "ToggleMaximize") \ X(TOGGLE_MAXIMIZE, "ToggleMaximize") \
@ -132,14 +136,16 @@ struct action_arg_list {
X(TOGGLE_MAGNIFY, "ToggleMagnify") \ X(TOGGLE_MAGNIFY, "ToggleMagnify") \
X(ZOOM_IN, "ZoomIn") \ X(ZOOM_IN, "ZoomIn") \
X(ZOOM_OUT, "ZoomOut") \ X(ZOOM_OUT, "ZoomOut") \
X(TOGGLE_SHOW_DESKTOP, "ToggleShowDesktop") \
X(WARP_CURSOR, "WarpCursor") \ X(WARP_CURSOR, "WarpCursor") \
X(HIDE_CURSOR, "HideCursor") X(HIDE_CURSOR, "HideCursor") \
X(DEBUG_TOGGLE_KEY_STATE_INDICATOR, "DebugToggleKeyStateIndicator")
/* /*
* Will expand to: * Will expand to:
* *
* enum action_type { * enum action_type {
* ACTION_TYPE_INVALID, * ACTION_TYPE_INVALID = 0,
* ACTION_TYPE_NONE, * ACTION_TYPE_NONE,
* ACTION_TYPE_CLOSE, * ACTION_TYPE_CLOSE,
* ACTION_TYPE_KILL, * ACTION_TYPE_KILL,
@ -333,6 +339,8 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
break; break;
case ACTION_TYPE_NEXT_WINDOW: case ACTION_TYPE_NEXT_WINDOW:
case ACTION_TYPE_PREVIOUS_WINDOW: case ACTION_TYPE_PREVIOUS_WINDOW:
case ACTION_TYPE_NEXT_WINDOW_IMMEDIATE:
case ACTION_TYPE_PREVIOUS_WINDOW_IMMEDIATE:
if (!strcasecmp(argument, "workspace")) { if (!strcasecmp(argument, "workspace")) {
if (!strcasecmp(content, "all")) { if (!strcasecmp(content, "all")) {
action_arg_add_int(action, argument, CYCLE_WORKSPACE_ALL); action_arg_add_int(action, argument, CYCLE_WORKSPACE_ALL);
@ -1142,6 +1150,21 @@ run_action(struct view *view, struct action *action,
} }
break; break;
} }
case ACTION_TYPE_NEXT_WINDOW_IMMEDIATE:
case ACTION_TYPE_PREVIOUS_WINDOW_IMMEDIATE: {
enum lab_cycle_dir dir = (action->type == ACTION_TYPE_NEXT_WINDOW_IMMEDIATE) ?
LAB_CYCLE_DIR_FORWARD : LAB_CYCLE_DIR_BACKWARD;
struct cycle_filter filter = {
.workspace = action_get_int(action, "workspace",
rc.window_switcher.workspace_filter),
.output = action_get_int(action, "output",
CYCLE_OUTPUT_ALL),
.app_id = action_get_int(action, "identifier",
CYCLE_APP_ID_ALL),
};
cycle_immediate(dir, filter);
break;
}
case ACTION_TYPE_RECONFIGURE: case ACTION_TYPE_RECONFIGURE:
kill(getpid(), SIGHUP); kill(getpid(), SIGHUP);
break; break;
@ -1289,7 +1312,7 @@ run_action(struct view *view, struct action *action,
/* /*
* To support only setting one of width/height * To support only setting one of width/height
* in <action name="ResizeTo" width="" height=""/> * in <action name="ResizeTo" width="" height="" />
* we fall back to current dimension when unset. * we fall back to current dimension when unset.
*/ */
struct wlr_box box = { struct wlr_box box = {
@ -1560,6 +1583,9 @@ run_action(struct view *view, struct action *action,
case ACTION_TYPE_ZOOM_OUT: case ACTION_TYPE_ZOOM_OUT:
magnifier_set_scale(MAGNIFY_DECREASE); magnifier_set_scale(MAGNIFY_DECREASE);
break; break;
case ACTION_TYPE_TOGGLE_SHOW_DESKTOP:
show_desktop_toggle();
break;
case ACTION_TYPE_WARP_CURSOR: { case ACTION_TYPE_WARP_CURSOR: {
const char *to = action_get_str(action, "to", "output"); const char *to = action_get_str(action, "to", "output");
const char *x = action_get_str(action, "x", "center"); const char *x = action_get_str(action, "x", "center");
@ -1570,6 +1596,9 @@ run_action(struct view *view, struct action *action,
case ACTION_TYPE_HIDE_CURSOR: case ACTION_TYPE_HIDE_CURSOR:
cursor_set_visible(&server.seat, false); cursor_set_visible(&server.seat, false);
break; break;
case ACTION_TYPE_DEBUG_TOGGLE_KEY_STATE_INDICATOR:
key_state_indicator_toggle();
break;
case ACTION_TYPE_INVALID: case ACTION_TYPE_INVALID:
wlr_log(WLR_ERROR, "Not executing unknown action"); wlr_log(WLR_ERROR, "Not executing unknown action");
break; break;

View file

@ -53,15 +53,14 @@ buf_expand_shell_variables(struct buf *s)
if (s->data[i] == '$' && isvalid(s->data[i+1])) { if (s->data[i] == '$' && isvalid(s->data[i+1])) {
/* expand environment variable */ /* expand environment variable */
buf_clear(&environment_variable); buf_clear(&environment_variable);
buf_add(&environment_variable, s->data + i + 1); int len = 0;
char *p = environment_variable.data; while (isvalid(s->data[i + 1 + len])) {
while (isvalid(*p)) { buf_add_char(&environment_variable, s->data[i + 1 + len]);
++p; ++len;
} }
*p = '\0'; i += len;
i += strlen(environment_variable.data);
strip_curly_braces(environment_variable.data); strip_curly_braces(environment_variable.data);
p = getenv(environment_variable.data); char *p = getenv(environment_variable.data);
if (p) { if (p) {
buf_add(&tmp, p); buf_add(&tmp, p);
} }

View file

@ -79,7 +79,7 @@ font_get_buffer_size(int max_width, const char *text, struct font *font,
void void
font_buffer_create(struct lab_data_buffer **buffer, int max_width, font_buffer_create(struct lab_data_buffer **buffer, int max_width,
int height, const char *text, struct font *font, const float *color, int height, const char *text, struct font *font, const float *color,
cairo_pattern_t *bg_pattern, double scale) cairo_pattern_t *bg_pattern, double scale, bool use_markup)
{ {
if (string_null_or_empty(text)) { if (string_null_or_empty(text)) {
return; return;
@ -123,7 +123,13 @@ font_buffer_create(struct lab_data_buffer **buffer, int max_width,
PangoLayout *layout = pango_cairo_create_layout(cairo); PangoLayout *layout = pango_cairo_create_layout(cairo);
pango_context_set_round_glyph_positions(pango_layout_get_context(layout), false); pango_context_set_round_glyph_positions(pango_layout_get_context(layout), false);
pango_layout_set_width(layout, width * PANGO_SCALE); pango_layout_set_width(layout, width * PANGO_SCALE);
pango_layout_set_text(layout, text, -1);
if (use_markup) {
pango_layout_set_markup(layout, text, -1);
} else {
pango_layout_set_text(layout, text, -1);
}
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
if (!opaque_bg) { if (!opaque_bg) {

View file

@ -89,6 +89,37 @@ out:
g_strfreev(argv); g_strfreev(argv);
} }
void
spawn_sync_no_shell(char const *command)
{
GError *err = NULL;
gchar **argv = NULL;
assert(command);
g_shell_parse_argv((gchar *)command, NULL, &argv, &err);
if (err) {
g_message("%s", err->message);
g_error_free(err);
return;
}
pid_t child = fork();
switch (child) {
case -1:
wlr_log(WLR_ERROR, "unable to fork()");
break;
case 0:
reset_signals_and_limits();
execvp(argv[0], argv);
_exit(1);
default:
waitpid(child, NULL, 0);
break;
}
g_strfreev(argv);
}
pid_t pid_t
spawn_primary_client(const char *command) spawn_primary_client(const char *command)
{ {

View file

@ -25,6 +25,7 @@ libinput_category_init(struct libinput_category *l)
l->dwt = -1; l->dwt = -1;
l->click_method = -1; l->click_method = -1;
l->scroll_method = -1; l->scroll_method = -1;
l->scroll_button = -1;
l->send_events_mode = -1; l->send_events_mode = -1;
l->have_calibration_matrix = false; l->have_calibration_matrix = false;
l->scroll_factor = 1.0; l->scroll_factor = 1.0;

View file

@ -94,6 +94,43 @@ parse_window_type(const char *type)
} }
} }
uint32_t
parse_privileged_interface(const char *name)
{
static const char * const ifaces[] = {
"wp_drm_lease_device_v1",
"zwlr_gamma_control_manager_v1",
"zwlr_output_manager_v1",
"zwlr_output_power_manager_v1",
"zwp_input_method_manager_v2",
"zwlr_virtual_pointer_manager_v1",
"zwp_virtual_keyboard_manager_v1",
"zwlr_export_dmabuf_manager_v1",
"zwlr_screencopy_manager_v1",
"ext_data_control_manager_v1",
"zwlr_data_control_manager_v1",
"wp_security_context_manager_v1",
"ext_idle_notifier_v1",
"zwlr_foreign_toplevel_manager_v1",
"ext_foreign_toplevel_list_v1",
"ext_session_lock_manager_v1",
"zwlr_layer_shell_v1",
"ext_workspace_manager_v1",
"ext_image_copy_capture_manager_v1",
"ext_output_image_capture_source_manager_v1",
};
static_assert(ARRAY_SIZE(ifaces) <= 32,
"return type too small for amount of privileged protocols");
for (size_t i = 0; i < ARRAY_SIZE(ifaces); i++) {
if (!strcmp(name, ifaces[i])) {
return 1 << i;
}
}
return 0;
}
/* /*
* Openbox/labwc comparison * Openbox/labwc comparison
* *
@ -565,6 +602,7 @@ fill_keybind(xmlNode *node)
lab_xml_get_bool(node, "onRelease", &keybind->on_release); lab_xml_get_bool(node, "onRelease", &keybind->on_release);
lab_xml_get_bool(node, "layoutDependent", &keybind->use_syms_only); lab_xml_get_bool(node, "layoutDependent", &keybind->use_syms_only);
lab_xml_get_bool(node, "allowWhenLocked", &keybind->allow_when_locked); lab_xml_get_bool(node, "allowWhenLocked", &keybind->allow_when_locked);
lab_xml_get_bool(node, "overrideInhibition", &keybind->override_inhibition);
append_parsed_actions(node, &keybind->actions); append_parsed_actions(node, &keybind->actions);
} }
@ -575,9 +613,9 @@ fill_mousebind(xmlNode *node, const char *context)
/* /*
* Example of what we are parsing: * Example of what we are parsing:
* <mousebind button="Left" action="DoubleClick"> * <mousebind button="Left" action="DoubleClick">
* <action name="Focus"/> * <action name="Focus" />
* <action name="Raise"/> * <action name="Raise" />
* <action name="ToggleMaximize"/> * <action name="ToggleMaximize" />
* </mousebind> * </mousebind>
*/ */
@ -853,9 +891,19 @@ fill_libinput_category(xmlNode *node)
} else if (!strcasecmp(content, "twofinger")) { } else if (!strcasecmp(content, "twofinger")) {
category->scroll_method = category->scroll_method =
LIBINPUT_CONFIG_SCROLL_2FG; LIBINPUT_CONFIG_SCROLL_2FG;
} else if (!strcasecmp(content, "onbutton")) {
category->scroll_method =
LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
} else { } else {
wlr_log(WLR_ERROR, "invalid scrollMethod"); wlr_log(WLR_ERROR, "invalid scrollMethod");
} }
} else if (!strcasecmp(key, "scrollButton")) {
int button = atoi(content);
if (button != 0) {
category->scroll_button = button;
} else {
wlr_log(WLR_ERROR, "invalid scrollButton");
}
} else if (!strcasecmp(key, "sendEventsMode")) { } else if (!strcasecmp(key, "sendEventsMode")) {
category->send_events_mode = category->send_events_mode =
get_send_events_mode(content); get_send_events_mode(content);
@ -1032,6 +1080,16 @@ set_tearing_mode(const char *str, enum tearing_mode *variable)
} }
} }
static void
set_hdr_mode(const char *str, enum render_bit_depth *variable)
{
if (parse_bool(str, -1) == 1) {
*variable = LAB_RENDER_BIT_DEPTH_10;
} else {
*variable = LAB_RENDER_BIT_DEPTH_8;
}
}
/* Returns true if the node's children should also be traversed */ /* Returns true if the node's children should also be traversed */
static bool static bool
entry(xmlNode *node, char *nodename, char *content) entry(xmlNode *node, char *nodename, char *content)
@ -1096,6 +1154,8 @@ entry(xmlNode *node, char *nodename, char *content)
set_adaptive_sync_mode(content, &rc.adaptive_sync); set_adaptive_sync_mode(content, &rc.adaptive_sync);
} else if (!strcasecmp(nodename, "allowTearing.core")) { } else if (!strcasecmp(nodename, "allowTearing.core")) {
set_tearing_mode(content, &rc.allow_tearing); set_tearing_mode(content, &rc.allow_tearing);
} else if (!strcasecmp(nodename, "Hdr.core")) {
set_hdr_mode(content, &rc.target_render_depth);
} else if (!strcasecmp(nodename, "autoEnableOutputs.core")) { } else if (!strcasecmp(nodename, "autoEnableOutputs.core")) {
set_bool(content, &rc.auto_enable_outputs); set_bool(content, &rc.auto_enable_outputs);
} else if (!strcasecmp(nodename, "reuseOutputMode.core")) { } else if (!strcasecmp(nodename, "reuseOutputMode.core")) {
@ -1147,6 +1207,9 @@ entry(xmlNode *node, char *nodename, char *content)
set_bool(content, &rc.focus_follow_mouse_requires_movement); set_bool(content, &rc.focus_follow_mouse_requires_movement);
} else if (!strcasecmp(nodename, "raiseOnFocus.focus")) { } else if (!strcasecmp(nodename, "raiseOnFocus.focus")) {
set_bool(content, &rc.raise_on_focus); set_bool(content, &rc.raise_on_focus);
} else if (!strcasecmp(nodename, "raiseOnFocusDelay.focus")) {
long val = strtol(content, NULL, 10);
rc.raise_on_focus_delay_ms = val > 0 ? (uint32_t)val : 0;
} else if (!strcasecmp(nodename, "doubleClickTime.mouse")) { } else if (!strcasecmp(nodename, "doubleClickTime.mouse")) {
long doubleclick_time_parsed = strtol(content, NULL, 10); long doubleclick_time_parsed = strtol(content, NULL, 10);
if (doubleclick_time_parsed > 0) { if (doubleclick_time_parsed > 0) {
@ -1377,6 +1440,16 @@ entry(xmlNode *node, char *nodename, char *content)
rc.mag_increment = MAX(0, rc.mag_increment); rc.mag_increment = MAX(0, rc.mag_increment);
} else if (!strcasecmp(nodename, "useFilter.magnifier")) { } else if (!strcasecmp(nodename, "useFilter.magnifier")) {
set_bool(content, &rc.mag_filter); set_bool(content, &rc.mag_filter);
} else if (!strcasecmp(nodename, "privilegedInterfaces")) {
rc.allowed_interfaces = 0;
} else if (!strcasecmp(nodename, "allow.privilegedInterfaces")) {
uint32_t iface_id = parse_privileged_interface(content);
if (iface_id) {
rc.allowed_interfaces |= iface_id;
} else {
wlr_log(WLR_ERROR, "invalid value for "
"<privilegedInterfaces><allow>");
}
} }
return false; return false;
@ -1457,8 +1530,10 @@ rcxml_init(void)
rc.gap = 0; rc.gap = 0;
rc.adaptive_sync = LAB_ADAPTIVE_SYNC_DISABLED; rc.adaptive_sync = LAB_ADAPTIVE_SYNC_DISABLED;
rc.allow_tearing = LAB_TEARING_DISABLED; rc.allow_tearing = LAB_TEARING_DISABLED;
rc.target_render_depth = LAB_RENDER_BIT_DEPTH_DEFAULT;
rc.auto_enable_outputs = true; rc.auto_enable_outputs = true;
rc.reuse_output_mode = false; rc.reuse_output_mode = false;
rc.allowed_interfaces = UINT32_MAX;
rc.xwayland_persistence = false; rc.xwayland_persistence = false;
rc.primary_selection = true; rc.primary_selection = true;
@ -1471,6 +1546,7 @@ rcxml_init(void)
rc.focus_follow_mouse = false; rc.focus_follow_mouse = false;
rc.focus_follow_mouse_requires_movement = true; rc.focus_follow_mouse_requires_movement = true;
rc.raise_on_focus = false; rc.raise_on_focus = false;
rc.raise_on_focus_delay_ms = 0;
rc.doubleclick_time = 500; rc.doubleclick_time = 500;
@ -1658,6 +1734,8 @@ deduplicate_key_bindings(void)
wl_list_remove(&current->link); wl_list_remove(&current->link);
keybind_destroy(current); keybind_destroy(current);
cleared++; cleared++;
} else if (actions_contain_toggle_keybinds(&current->actions)) {
current->override_inhibition = true;
} }
} }
if (replaced) { if (replaced) {

View file

@ -219,12 +219,12 @@ execute_update(const char *env_keys, const char *env_unset_keys, bool initialize
char *cmd = char *cmd =
strdup_printf("dbus-update-activation-environment %s", strdup_printf("dbus-update-activation-environment %s",
initialize ? env_keys : env_unset_keys); initialize ? env_keys : env_unset_keys);
spawn_async_no_shell(cmd); spawn_sync_no_shell(cmd);
free(cmd); free(cmd);
cmd = strdup_printf("systemctl --user %s %s", cmd = strdup_printf("systemctl --user %s %s",
initialize ? "import-environment" : "unset-environment", env_keys); initialize ? "import-environment" : "unset-environment", env_keys);
spawn_async_no_shell(cmd); spawn_sync_no_shell(cmd);
free(cmd); free(cmd);
} }

View file

@ -9,6 +9,7 @@
#include "common/mem.h" #include "common/mem.h"
#include "common/scene-helpers.h" #include "common/scene-helpers.h"
#include "config/rcxml.h" #include "config/rcxml.h"
#include "config/types.h"
#include "labwc.h" #include "labwc.h"
#include "node.h" #include "node.h"
#include "output.h" #include "output.h"
@ -171,7 +172,7 @@ cycle_begin(enum lab_cycle_dir direction,
struct view *active_view = server.active_view; struct view *active_view = server.active_view;
if (active_view && active_view->cycle_link.next) { if (active_view && active_view->cycle_link.next) {
/* Select the active view it's in the cycle list */ /* Select the active view if it's in the cycle list */
server.cycle.selected_view = active_view; server.cycle.selected_view = active_view;
} else { } else {
/* Otherwise, select the first view in the cycle list */ /* Otherwise, select the first view in the cycle list */
@ -326,24 +327,82 @@ handle_osd_tree_destroy(struct wl_listener *listener, void *data)
free(osd_output); free(osd_output);
} }
/* Return false on failure */ static enum lab_view_criteria
static bool get_view_criteria(struct cycle_filter *filter)
init_cycle(struct cycle_filter filter)
{ {
enum lab_view_criteria criteria = enum lab_view_criteria criteria =
LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER
| LAB_VIEW_CRITERIA_NO_DIALOG; | LAB_VIEW_CRITERIA_NO_DIALOG;
if (filter.workspace == CYCLE_WORKSPACE_CURRENT) { if (filter->workspace == CYCLE_WORKSPACE_CURRENT) {
criteria |= LAB_VIEW_CRITERIA_CURRENT_WORKSPACE; criteria |= LAB_VIEW_CRITERIA_CURRENT_WORKSPACE;
} }
return criteria;
}
uint64_t cycle_outputs = static const char *
get_outputs_by_filter(filter.output); get_cycle_app_id(struct cycle_filter *filter)
{
const char *cycle_app_id = NULL; if (filter->app_id == CYCLE_APP_ID_CURRENT && server.active_view) {
if (filter.app_id == CYCLE_APP_ID_CURRENT && server.active_view) { return server.active_view->app_id;
cycle_app_id = server.active_view->app_id;
} }
return NULL;
}
static struct wl_list *prev(struct wl_list *elm) { return elm->prev; }
static struct wl_list *next(struct wl_list *elm) { return elm->next; }
void
cycle_immediate(enum lab_cycle_dir direction, struct cycle_filter filter)
{
if (wl_list_empty(&server.views)) {
return;
}
enum lab_view_criteria criteria = get_view_criteria(&filter);
uint64_t cycle_outputs = get_outputs_by_filter(filter.output);
const char *cycle_app_id = get_cycle_app_id(&filter);
struct wl_list *head = &server.views;
struct wl_list *(*iter)(struct wl_list *list);
iter = direction == LAB_CYCLE_DIR_FORWARD ? next : prev;
struct wl_list *from = (direction == LAB_CYCLE_DIR_FORWARD) && server.active_view
? &server.active_view->link : head;
for (struct wl_list *elm = iter(from); elm != head; elm = iter(elm)) {
struct view *view = wl_container_of(elm, view, link);
if (!view_matches_criteria(view, criteria)) {
continue;
}
if (filter.output != CYCLE_OUTPUT_ALL) {
if (!view->output || !(cycle_outputs & view->output->id_bit)) {
continue;
}
}
if (cycle_app_id && strcmp(view->app_id, cycle_app_id) != 0) {
continue;
}
if (server.active_view && direction == LAB_CYCLE_DIR_FORWARD) {
/*
* When cycling forward, the current active view needs to be
* sent to back to keep the same sequence and avoid getting
* stuck in the 2 topmost views.
*/
view_move_to_back(server.active_view);
}
desktop_focus_view(view, true);
break;
}
cursor_update_focus();
}
/* Return false on failure */
static bool
init_cycle(struct cycle_filter filter)
{
enum lab_view_criteria criteria = get_view_criteria(&filter);
uint64_t cycle_outputs = get_outputs_by_filter(filter.output);
const char *cycle_app_id = get_cycle_app_id(&filter);
struct view *view; struct view *view;
for_each_view(view, &server.views, criteria) { for_each_view(view, &server.views, criteria) {

View file

@ -2,6 +2,7 @@
#include <assert.h> #include <assert.h>
#include <wlr/render/allocator.h> #include <wlr/render/allocator.h>
#include <wlr/render/swapchain.h> #include <wlr/render/swapchain.h>
#include <wlr/types/wlr_buffer.h>
#include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_scene.h> #include <wlr/types/wlr_scene.h>
#include "config/rcxml.h" #include "config/rcxml.h"
@ -46,8 +47,12 @@ render_node(struct wlr_render_pass *pass,
if (!scene_buffer->buffer) { if (!scene_buffer->buffer) {
break; break;
} }
struct wlr_texture *texture = wlr_texture_from_buffer( struct wlr_texture *texture = NULL;
server.renderer, scene_buffer->buffer); struct wlr_client_buffer *client_buffer =
wlr_client_buffer_get(scene_buffer->buffer);
if (client_buffer) {
texture = client_buffer->texture;
}
if (!texture) { if (!texture) {
break; break;
} }
@ -62,7 +67,6 @@ render_node(struct wlr_render_pass *pass,
}, },
.transform = scene_buffer->transform, .transform = scene_buffer->transform,
}); });
wlr_texture_destroy(texture);
break; break;
} }
case WLR_SCENE_NODE_RECT: case WLR_SCENE_NODE_RECT:
@ -85,6 +89,10 @@ render_thumb(struct output *output, struct view *view)
struct wlr_buffer *buffer = wlr_allocator_create_buffer(server.allocator, struct wlr_buffer *buffer = wlr_allocator_create_buffer(server.allocator,
view->current.width, view->current.height, view->current.width, view->current.height,
&output->wlr_output->swapchain->format); &output->wlr_output->swapchain->format);
if (!buffer) {
wlr_log(WLR_ERROR, "failed to allocate buffer for thumbnail");
return NULL;
}
struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass( struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass(
server.renderer, buffer, NULL); server.renderer, buffer, NULL);
render_node(pass, &view->content_tree->node, 0, 0); render_node(pass, &view->content_tree->node, 0, 0);

View file

@ -9,11 +9,13 @@
#include <wlr/types/wlr_subcompositor.h> #include <wlr/types/wlr_subcompositor.h>
#include <wlr/types/wlr_xdg_shell.h> #include <wlr/types/wlr_xdg_shell.h>
#include "common/scene-helpers.h" #include "common/scene-helpers.h"
#include "config/rcxml.h"
#include "dnd.h" #include "dnd.h"
#include "labwc.h" #include "labwc.h"
#include "layers.h" #include "layers.h"
#include "node.h" #include "node.h"
#include "output.h" #include "output.h"
#include "show-desktop.h"
#include "ssd.h" #include "ssd.h"
#include "view.h" #include "view.h"
#include "workspaces.h" #include "workspaces.h"
@ -64,8 +66,51 @@ set_or_offer_focus(struct view *view)
} }
} }
static int
handle_auto_raise_timer(void *data)
{
(void)data;
struct view *view = server.pending_auto_raise_view;
server.pending_auto_raise_view = NULL;
if (view && view->mapped) {
view_move_to_front(view);
}
return 0; /* ignored per wl_event_loop docs */
}
void void
desktop_focus_view(struct view *view, bool raise) desktop_cancel_pending_auto_raise(void)
{
server.pending_auto_raise_view = NULL;
if (server.pending_auto_raise_timer) {
/* Disarm by setting to 0 ms */
wl_event_source_timer_update(server.pending_auto_raise_timer, 0);
}
}
static void
schedule_delayed_auto_raise(struct view *view)
{
server.pending_auto_raise_view = view;
if (!server.pending_auto_raise_timer) {
server.pending_auto_raise_timer =
wl_event_loop_add_timer(server.wl_event_loop,
handle_auto_raise_timer, NULL);
}
wl_event_source_timer_update(server.pending_auto_raise_timer,
rc.raise_on_focus_delay_ms);
}
/*
* The raise_on_focus_delay is only meant to dampen z-order churn from
* focus-follows-mouse cursor passes. Explicit focus changes (alt-tab,
* Focus action, xdg/xwayland activation, etc.) should raise immediately.
* allow_delay is therefore only set when the caller is the sloppy-focus
* path in desktop_focus_view_or_surface().
*/
static void
desktop_focus_view_internal(struct view *view, bool raise, bool allow_delay)
{ {
assert(view); assert(view);
/* /*
@ -102,8 +147,17 @@ desktop_focus_view(struct view *view, bool raise)
workspaces_switch_to(view->workspace, /*update_focus*/ false); workspaces_switch_to(view->workspace, /*update_focus*/ false);
} }
/*
* A new focus change supersedes any pending auto-raise from a
* previous focus event, regardless of whether we raise now.
*/
desktop_cancel_pending_auto_raise();
if (raise) { if (raise) {
view_move_to_front(view); if (allow_delay && rc.raise_on_focus_delay_ms > 0) {
schedule_delayed_auto_raise(view);
} else {
view_move_to_front(view);
}
} }
/* /*
@ -113,6 +167,14 @@ desktop_focus_view(struct view *view, bool raise)
*/ */
struct view *dialog = view_get_modal_dialog(view); struct view *dialog = view_get_modal_dialog(view);
set_or_offer_focus(dialog ? dialog : view); set_or_offer_focus(dialog ? dialog : view);
show_desktop_reset();
}
void
desktop_focus_view(struct view *view, bool raise)
{
desktop_focus_view_internal(view, raise, /*allow_delay*/ false);
} }
/* TODO: focus layer-shell surfaces also? */ /* TODO: focus layer-shell surfaces also? */
@ -122,7 +184,7 @@ desktop_focus_view_or_surface(struct seat *seat, struct view *view,
{ {
assert(view || surface); assert(view || surface);
if (view) { if (view) {
desktop_focus_view(view, raise); desktop_focus_view_internal(view, raise, /*allow_delay*/ true);
#if HAVE_XWAYLAND #if HAVE_XWAYLAND
} else { } else {
struct wlr_xwayland_surface *xsurface = struct wlr_xwayland_surface *xsurface =

View file

@ -75,6 +75,9 @@ ext_foreign_toplevel_init(struct ext_foreign_toplevel *ext_toplevel,
return; return;
} }
/* In support for ext-toplevel-capture */
ext_toplevel->handle->data = view;
/* Client side requests */ /* Client side requests */
ext_toplevel->on.handle_destroy.notify = handle_handle_destroy; ext_toplevel->on.handle_destroy.notify = handle_handle_destroy;
wl_signal_add(&ext_toplevel->handle->events.destroy, &ext_toplevel->on.handle_destroy); wl_signal_add(&ext_toplevel->handle->events.destroy, &ext_toplevel->on.handle_destroy);

View file

@ -226,7 +226,7 @@ out:
/* /*
* Openbox built-in icons are not bigger than 8x8, so have only written this * Openbox built-in icons are not bigger than 8x8, so have only written this
* function to cope wit that max size * function to cope with that max size
*/ */
#define LABWC_BUILTIN_ICON_MAX_SIZE (8) #define LABWC_BUILTIN_ICON_MAX_SIZE (8)

View file

@ -351,7 +351,7 @@ xpm_load_to_surface(struct file_handle *handle)
goto out; goto out;
} }
for (int n = 0, xcnt = 0; n < wbytes; n += cpp, xcnt++) { for (int n = 0; n < wbytes; n += cpp) {
g_strlcpy(pixel_str, &buffer[n], cpp + 1); g_strlcpy(pixel_str, &buffer[n], cpp + 1);
struct xpm_color *color = struct xpm_color *color =

View file

@ -4,10 +4,171 @@
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_scene.h>
#include <xkbcommon/xkbcommon.h>
#include "config/rcxml.h"
#include "common/buf.h"
#include "common/set.h" #include "common/set.h"
#include "input/keyboard.h"
#include "labwc.h"
#include "scaled-buffer/scaled-font-buffer.h"
static struct lab_set pressed, bound, pressed_sent; static struct lab_set pressed, bound, pressed_sent;
static bool show_debug_indicator;
static struct indicator_state {
struct wlr_scene_tree *tree;
struct scaled_font_buffer *sfb_pressed;
struct scaled_font_buffer *sfb_bound;
struct scaled_font_buffer *sfb_pressed_sent;
struct scaled_font_buffer *sfb_modifiers;
struct xkb_keymap *keymap;
} indicator_state;
static const char *
keycode_to_keyname(struct xkb_keymap *keymap, uint32_t keycode)
{
const xkb_keysym_t *syms;
int syms_len = xkb_keymap_key_get_syms_by_level(keymap, keycode + 8, 0, 0, &syms);
if (!syms_len) {
return NULL;
}
static char buf[256];
if (!xkb_keysym_get_name(syms[0], buf, sizeof(buf))) {
return NULL;
}
return buf;
}
static const char *
modifier_to_name(uint32_t modifier)
{
switch (modifier) {
case WLR_MODIFIER_SHIFT:
return "S";
case WLR_MODIFIER_CAPS:
return "caps";
case WLR_MODIFIER_CTRL:
return "C";
case WLR_MODIFIER_ALT:
return "A";
case WLR_MODIFIER_MOD2:
return "numlock";
case WLR_MODIFIER_MOD3:
return "H";
case WLR_MODIFIER_LOGO:
return "W";
case WLR_MODIFIER_MOD5:
return "M";
default:
return "?";
}
}
static void
init_indicator(struct indicator_state *state)
{
state->tree = wlr_scene_tree_create(&server.scene->tree);
wlr_scene_node_set_enabled(&state->tree->node, false);
state->sfb_pressed = scaled_font_buffer_create(state->tree);
wlr_scene_node_set_position(&state->sfb_pressed->scene_buffer->node, 0, 0);
state->sfb_bound = scaled_font_buffer_create(state->tree);
wlr_scene_node_set_position(&state->sfb_bound->scene_buffer->node, 0, 20);
state->sfb_pressed_sent = scaled_font_buffer_create(state->tree);
wlr_scene_node_set_position(&state->sfb_pressed_sent->scene_buffer->node, 0, 40);
state->sfb_modifiers = scaled_font_buffer_create(state->tree);
wlr_scene_node_set_position(&state->sfb_modifiers->scene_buffer->node, 0, 60);
struct xkb_rule_names rules = { 0 };
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
state->keymap = xkb_map_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
}
static void
update_key_indicator_callback(void *data)
{
struct seat *seat = data;
uint32_t all_modifiers = keyboard_get_all_modifiers(seat);
float black[4] = {0, 0, 0, 1};
float white[4] = {1, 1, 1, 1};
if (!indicator_state.tree) {
init_indicator(&indicator_state);
}
if (show_debug_indicator) {
wlr_scene_node_set_enabled(&indicator_state.tree->node, true);
} else {
wlr_scene_node_set_enabled(&indicator_state.tree->node, false);
return;
}
struct buf buf = BUF_INIT;
buf_add(&buf, "pressed=");
for (int i = 0; i < pressed.size; i++) {
const char *keyname = keycode_to_keyname(indicator_state.keymap,
pressed.values[i]);
buf_add_fmt(&buf, "%s (%d), ", keyname, pressed.values[i]);
}
scaled_font_buffer_update(indicator_state.sfb_pressed, buf.data,
-1, &rc.font_osd, black, white);
buf_clear(&buf);
buf_add(&buf, "bound=");
for (int i = 0; i < bound.size; i++) {
const char *keyname = keycode_to_keyname(indicator_state.keymap,
bound.values[i]);
buf_add_fmt(&buf, "%s (%d), ", keyname, bound.values[i]);
}
scaled_font_buffer_update(indicator_state.sfb_bound, buf.data,
-1, &rc.font_osd, black, white);
buf_clear(&buf);
buf_add(&buf, "pressed_sent=");
for (int i = 0; i < pressed_sent.size; i++) {
const char *keyname = keycode_to_keyname(indicator_state.keymap,
pressed_sent.values[i]);
buf_add_fmt(&buf, "%s (%d), ", keyname, pressed_sent.values[i]);
}
scaled_font_buffer_update(indicator_state.sfb_pressed_sent, buf.data, -1,
&rc.font_osd, black, white);
buf_clear(&buf);
buf_add(&buf, "modifiers=");
for (int i = 0; i <= 7; i++) {
uint32_t mod = 1 << i;
if (all_modifiers & mod) {
buf_add_fmt(&buf, "%s, ", modifier_to_name(mod));
}
}
buf_add_fmt(&buf, "(%d)", all_modifiers);
scaled_font_buffer_update(indicator_state.sfb_modifiers, buf.data, -1,
&rc.font_osd, black, white);
buf_reset(&buf);
}
void
key_state_indicator_update(struct seat *seat)
{
if (!show_debug_indicator) {
return;
}
wl_event_loop_add_idle(server.wl_event_loop,
update_key_indicator_callback, seat);
}
void
key_state_indicator_toggle(void)
{
show_debug_indicator = !show_debug_indicator;
}
static void static void
report(struct lab_set *key_set, const char *msg) report(struct lab_set *key_set, const char *msg)
{ {

View file

@ -139,6 +139,8 @@ handle_modifiers(struct wl_listener *listener, void *data)
struct seat *seat = keyboard->base.seat; struct seat *seat = keyboard->base.seat;
struct wlr_keyboard *wlr_keyboard = keyboard->wlr_keyboard; struct wlr_keyboard *wlr_keyboard = keyboard->wlr_keyboard;
key_state_indicator_update(seat);
if (server.input_mode == LAB_INPUT_STATE_MOVE) { if (server.input_mode == LAB_INPUT_STATE_MOVE) {
/* Any change to the modifier state re-enable region snap */ /* Any change to the modifier state re-enable region snap */
seat->region_prevent_snap = false; seat->region_prevent_snap = false;
@ -201,8 +203,10 @@ match_keybinding_for_sym(uint32_t modifiers,
if (modifiers ^ keybind->modifiers) { if (modifiers ^ keybind->modifiers) {
continue; continue;
} }
if (view_inhibits_actions(server.active_view, &keybind->actions)) { if (!(keybind->override_inhibition)) {
continue; if (view_inhibits_actions(server.active_view, &keybind->actions)) {
continue;
}
} }
if (sym == XKB_KEY_NoSymbol) { if (sym == XKB_KEY_NoSymbol) {
/* Use keycodes */ /* Use keycodes */
@ -440,15 +444,24 @@ handle_menu_keys(struct keysyms *syms)
break; break;
case XKB_KEY_Return: case XKB_KEY_Return:
case XKB_KEY_KP_Enter: case XKB_KEY_KP_Enter:
menu_call_selected_actions(); if (!menu_call_selected_actions()) {
menu_submenu_enter();
};
break; break;
case XKB_KEY_Escape: case XKB_KEY_Escape:
menu_close_root(); menu_close_root();
cursor_update_focus(); cursor_update_focus();
break; break;
default: default: {
continue; uint32_t accelerator = xkb_keysym_to_utf32(syms->syms[i]);
} if (accelerator == 0) {
continue;
}
if (menu_item_select_by_accelerator(accelerator)) {
menu_call_selected_actions();
}
break;
}}
break; break;
} }
} }
@ -622,6 +635,9 @@ handle_key(struct wl_listener *listener, void *data)
struct seat *seat = keyboard->base.seat; struct seat *seat = keyboard->base.seat;
struct wlr_keyboard_key_event *event = data; struct wlr_keyboard_key_event *event = data;
struct wlr_seat *wlr_seat = seat->wlr_seat; struct wlr_seat *wlr_seat = seat->wlr_seat;
key_state_indicator_update(seat);
idle_manager_notify_activity(seat->wlr_seat); idle_manager_notify_activity(seat->wlr_seat);
/* any new press/release cancels current keybind repeat */ /* any new press/release cancels current keybind repeat */

View file

@ -431,8 +431,6 @@ handle_tablet_tool_axis(struct wl_listener *listener, void *data)
*/ */
tool->dx = 0; tool->dx = 0;
tool->dy = 0; tool->dy = 0;
tool->tilt_x = 0;
tool->tilt_y = 0;
if (ev->updated_axes & WLR_TABLET_TOOL_AXIS_X) { if (ev->updated_axes & WLR_TABLET_TOOL_AXIS_X) {
tool->x = ev->x; tool->x = ev->x;

View file

@ -123,11 +123,12 @@ interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges)
cursor_shape = LAB_CURSOR_GRAB; cursor_shape = LAB_CURSOR_GRAB;
break; break;
case LAB_INPUT_STATE_RESIZE: { case LAB_INPUT_STATE_RESIZE: {
if (view->shaded || view->fullscreen || if (view->shaded || view->fullscreen) {
view->maximized == VIEW_AXIS_BOTH) {
/* /*
* We don't allow resizing while shaded, * We don't allow resizing while shaded or fullscreen.
* fullscreen or maximized in both directions. * Maximized views are handled below by un-maximizing
* the axes being resized while keeping the current
* geometry as the starting point.
*/ */
return; return;
} }
@ -141,9 +142,9 @@ interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges)
} }
/* /*
* If tiled or maximized in only one direction, reset * If tiled or maximized, reset tiled state and un-maximize
* tiled state and un-maximize the relevant axes, but * the axes that are being resized, but keep the same
* keep the same geometry as the starting point. * geometry as the starting point.
*/ */
enum view_axis maximized = view->maximized; enum view_axis maximized = view->maximized;
if (server.resize_edges & LAB_EDGES_LEFT_RIGHT) { if (server.resize_edges & LAB_EDGES_LEFT_RIGHT) {

View file

@ -4,9 +4,12 @@
#include <pango/pangocairo.h> #include <pango/pangocairo.h>
#include <signal.h> #include <signal.h>
#include <unistd.h> #include <unistd.h>
#include <wlr/version.h>
#include "common/fd-util.h" #include "common/fd-util.h"
#include "common/font.h" #include "common/font.h"
#include "common/macros.h"
#include "common/spawn.h" #include "common/spawn.h"
#include "common/string-helpers.h"
#include "config/rcxml.h" #include "config/rcxml.h"
#include "config/session.h" #include "config/session.h"
#include "labwc.h" #include "labwc.h"
@ -36,6 +39,7 @@ static const struct option long_options[] = {
{"reconfigure", no_argument, NULL, 'r'}, {"reconfigure", no_argument, NULL, 'r'},
{"startup", required_argument, NULL, 's'}, {"startup", required_argument, NULL, 's'},
{"session", required_argument, NULL, 'S'}, {"session", required_argument, NULL, 'S'},
{"title", required_argument, NULL, 't'},
{"version", no_argument, NULL, 'v'}, {"version", no_argument, NULL, 'v'},
{"verbose", no_argument, NULL, 'V'}, {"verbose", no_argument, NULL, 'V'},
{0, 0, 0, 0} {0, 0, 0, 0}
@ -54,6 +58,7 @@ static const char labwc_usage[] =
" -r, --reconfigure Reload the compositor configuration\n" " -r, --reconfigure Reload the compositor configuration\n"
" -s, --startup <command> Run command on startup\n" " -s, --startup <command> Run command on startup\n"
" -S, --session <command> Run command on startup and terminate on exit\n" " -S, --session <command> Run command on startup and terminate on exit\n"
" -t, --title <fmtstr> Specify title to use when running in a window\n"
" -v, --version Show version number and quit\n" " -v, --version Show version number and quit\n"
" -V, --verbose Enable more verbose logging\n"; " -V, --verbose Enable more verbose logging\n";
@ -68,12 +73,15 @@ static void
print_version(void) print_version(void)
{ {
#define FEATURE_ENABLED(feature) (HAVE_##feature ? "+" : "-") #define FEATURE_ENABLED(feature) (HAVE_##feature ? "+" : "-")
printf("labwc %s (%sxwayland %snls %srsvg %slibsfdo)\n", printf("labwc %s (%sxwayland %snls %srsvg %slibsfdo) wlroots-%d.%d.%d\n",
LABWC_VERSION, LABWC_VERSION,
FEATURE_ENABLED(XWAYLAND), FEATURE_ENABLED(XWAYLAND),
FEATURE_ENABLED(NLS), FEATURE_ENABLED(NLS),
FEATURE_ENABLED(RSVG), FEATURE_ENABLED(RSVG),
FEATURE_ENABLED(LIBSFDO) FEATURE_ENABLED(LIBSFDO),
wlr_version_get_major(),
wlr_version_get_minor(),
wlr_version_get_micro()
); );
#undef FEATURE_ENABLED #undef FEATURE_ENABLED
} }
@ -167,10 +175,16 @@ main(int argc, char *argv[])
char *primary_client = NULL; char *primary_client = NULL;
enum wlr_log_importance verbosity = WLR_ERROR; enum wlr_log_importance verbosity = WLR_ERROR;
server.wlr_version = _LAB_CALC_WLR_VERSION_NUM(
wlr_version_get_major(),
wlr_version_get_minor(),
wlr_version_get_micro()
);
int c; int c;
while (1) { while (1) {
int index = 0; int index = 0;
c = getopt_long(argc, argv, "c:C:dehmrs:S:vV", long_options, &index); c = getopt_long(argc, argv, "c:C:dehmrs:S:t:vV", long_options, &index);
if (c == -1) { if (c == -1) {
break; break;
} }
@ -199,6 +213,9 @@ main(int argc, char *argv[])
case 'S': case 'S':
primary_client = optarg; primary_client = optarg;
break; break;
case 't':
server.title_fmt = optarg;
break;
case 'v': case 'v':
print_version(); print_version();
exit(0); exit(0);
@ -257,6 +274,10 @@ main(int argc, char *argv[])
increase_nofile_limit(); increase_nofile_limit();
if (string_null_or_empty(server.title_fmt)) {
server.title_fmt = "labwc - %o";
}
server_init(); server_init();
server_start(); server_start();

View file

@ -2,8 +2,10 @@
#define _POSIX_C_SOURCE 200809L #define _POSIX_C_SOURCE 200809L
#include "menu/menu.h" #include "menu/menu.h"
#include <assert.h> #include <assert.h>
#include <ctype.h>
#include <libxml/parser.h> #include <libxml/parser.h>
#include <signal.h> #include <signal.h>
#include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <strings.h> #include <strings.h>
@ -131,6 +133,92 @@ validate(void)
} }
} }
/* Read a single Unicode codepoint and convert it to lowercase */
static uint32_t
read_unicode_char_lowercase(const char *first_byte, size_t *bytes_read)
{
assert(bytes_read);
if (string_null_or_empty(first_byte)) {
*bytes_read = 0;
return 0;
}
gunichar codepoint = g_utf8_get_char_validated(first_byte, -1);
bool partial_read = (codepoint == (gunichar)-2);
bool failed_read = (codepoint == (gunichar)-1);
if (partial_read || failed_read) {
/* Read only the first byte */
*bytes_read = 1;
return (uint32_t)(unsigned char)first_byte[0];
}
*bytes_read = (size_t)(g_utf8_next_char(first_byte) - first_byte);
return (uint32_t)g_unichar_tolower(codepoint);
}
/* Retrieve the accelerator from an item label */
static void
item_parse_accelerator(struct menuitem *item, const char *text)
{
const char *accel_ptr = NULL;
char *underscore = strchr(text, '_');
while (underscore) {
if (underscore[1] == '_') {
/* Ignore escaped underscores */
underscore = strchr(underscore + 2, '_');
} else if (underscore[1] != '\0') {
/* Found a valid accelerator */
accel_ptr = underscore + 1;
break;
} else {
/* Ignore empty accelerator */
break;
}
}
size_t bytes_read = 0;
if (!accel_ptr) {
/* Default to the first char */
item->text = xstrdup(text);
item->accelerator = read_unicode_char_lowercase(text, &bytes_read);
} else {
/* Set the accelerator and remove the preceding underscore */
item->use_markup = true;
item->accelerator = read_unicode_char_lowercase(accel_ptr, &bytes_read);
item->text = strdup_printf("%.*s<u>%.*s</u>%s",
/* Prefix length + prefix */
(int)(accel_ptr - 1 - text), text,
/* Accelerator (utf-8 byte) length + accelerator */
(int)bytes_read, accel_ptr,
/* Remainder */
accel_ptr + bytes_read);
}
}
/* Remove underscores used for escaping other underscores from a string */
static void
unescape_underscores(char *text)
{
if (!text) {
return;
}
char *src = text;
char *dst = text;
while (*src) {
if (*src == '_' && *(src + 1) == '_') {
*dst++ = '_';
src += 2;
} else {
*dst++ = *src++;
}
}
*dst = '\0';
}
static struct menuitem * 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, const char *icon_name, bool show_arrow)
{ {
@ -142,8 +230,9 @@ item_create(struct menu *menu, const char *text, const char *icon_name, bool sho
menuitem->parent = menu; menuitem->parent = menu;
menuitem->selectable = true; menuitem->selectable = true;
menuitem->type = LAB_MENU_ITEM; menuitem->type = LAB_MENU_ITEM;
menuitem->text = xstrdup(text);
menuitem->arrow = show_arrow ? "" : NULL; menuitem->arrow = show_arrow ? "" : NULL;
item_parse_accelerator(menuitem, text);
unescape_underscores(menuitem->text);
#if HAVE_LIBSFDO #if HAVE_LIBSFDO
if (rc.menu_show_icons && !string_null_or_empty(icon_name)) { if (rc.menu_show_icons && !string_null_or_empty(icon_name)) {
@ -250,8 +339,8 @@ item_create_scene_for_state(struct menuitem *item, float *text_color,
/* Create label */ /* Create label */
struct scaled_font_buffer *label_buffer = scaled_font_buffer_create(tree); struct scaled_font_buffer *label_buffer = scaled_font_buffer_create(tree);
assert(label_buffer); assert(label_buffer);
scaled_font_buffer_update(label_buffer, item->text, label_max_width, scaled_font_buffer_update_markup(label_buffer, item->text, label_max_width,
&rc.font_menuitem, text_color, bg_color); &rc.font_menuitem, text_color, bg_color, item->use_markup);
/* Vertically center and left-align label */ /* Vertically center and left-align label */
int x = theme->menu_items_padding_x + icon_width; int x = theme->menu_items_padding_x + icon_width;
int y = (theme->menu_item_height - label_buffer->height) / 2; int y = (theme->menu_item_height - label_buffer->height) / 2;
@ -609,7 +698,7 @@ fill_menu(struct menu *parent, xmlNode *n)
* *
* <?xml version="1.0" encoding="UTF-8"?> * <?xml version="1.0" encoding="UTF-8"?>
* <openbox_menu> * <openbox_menu>
* <menu id="root-menu" label="foo" execute="bar"/> * <menu id="root-menu" label="foo" execute="bar" />
* </openbox_menu> * </openbox_menu>
*/ */
} else { } else {
@ -1531,6 +1620,60 @@ menu_item_select_previous(void)
menu_item_select(/* forward */ false); menu_item_select(/* forward */ false);
} }
bool
menu_item_select_by_accelerator(uint32_t accelerator)
{
struct menu *menu = get_selection_leaf();
if (!menu || wl_list_empty(&menu->menuitems)) {
return false;
}
bool needs_exec = false;
bool matched = false;
struct menuitem *selection = menu->selection.item;
struct wl_list *start = selection ? &selection->link : &menu->menuitems;
struct wl_list *current = start;
struct menuitem *item = NULL;
struct menuitem *next_selection = NULL;
do {
current = current->next;
if (current == &menu->menuitems) {
/* Allow wrap around */
continue;
}
item = wl_container_of(current, item, link);
if (item->accelerator == accelerator) {
if (!matched) {
/* Found first match */
next_selection = item;
needs_exec = true;
matched = true;
} else {
/*
* Found another match,
* cycle selection instead of executing
*/
needs_exec = false;
break;
}
}
} while (current != start);
if (!next_selection) {
return false;
}
menu_process_item_selection(next_selection);
if (needs_exec && next_selection->submenu) {
/* Since we can't execute a submenu, enter it */
needs_exec = false;
menu_submenu_enter();
}
return needs_exec;
}
bool bool
menu_call_selected_actions(void) menu_call_selected_actions(void)
{ {

View file

@ -22,6 +22,7 @@ labwc_sources = files(
'seat.c', 'seat.c',
'server.c', 'server.c',
'session-lock.c', 'session-lock.c',
'show-desktop.c',
'snap-constraints.c', 'snap-constraints.c',
'snap.c', 'snap.c',
'tearing.c', 'tearing.c',

View file

@ -24,6 +24,7 @@ output_state_init(struct output *output)
backup_config, output->wlr_output); backup_config, output->wlr_output);
wlr_output_head_v1_state_apply(&backup_head->state, &output->pending); wlr_output_head_v1_state_apply(&backup_head->state, &output->pending);
wlr_output_configuration_v1_destroy(backup_config); wlr_output_configuration_v1_destroy(backup_config);
} }

View file

@ -9,6 +9,8 @@
#define _POSIX_C_SOURCE 200809L #define _POSIX_C_SOURCE 200809L
#include "output.h" #include "output.h"
#include <assert.h> #include <assert.h>
#include <drm_fourcc.h>
#include <glib.h>
#include <strings.h> #include <strings.h>
#include <wlr/backend/wayland.h> #include <wlr/backend/wayland.h>
#include <wlr/config.h> #include <wlr/config.h>
@ -25,6 +27,7 @@
#include "common/macros.h" #include "common/macros.h"
#include "common/mem.h" #include "common/mem.h"
#include "common/scene-helpers.h" #include "common/scene-helpers.h"
#include "common/string-helpers.h"
#include "config/rcxml.h" #include "config/rcxml.h"
#include "labwc.h" #include "labwc.h"
#include "layers.h" #include "layers.h"
@ -51,6 +54,150 @@
#include <wlr/backend/session.h> #include <wlr/backend/session.h>
#endif #endif
static uint32_t output_formats_8bit[] = {
/* 32 bpp RGB with 8 bit component width */
DRM_FORMAT_XRGB8888,
DRM_FORMAT_XBGR8888,
DRM_FORMAT_RGBX8888,
DRM_FORMAT_BGRX8888,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_ABGR8888,
DRM_FORMAT_RGBA8888,
DRM_FORMAT_BGRA8888,
/* 24 bpp RGB */
DRM_FORMAT_RGB888,
DRM_FORMAT_BGR888,
};
uint32_t output_formats_10bit[] = {
/* 32 bpp RGB with 10 bit component width */
DRM_FORMAT_XRGB2101010,
DRM_FORMAT_XBGR2101010,
DRM_FORMAT_RGBX1010102,
DRM_FORMAT_BGRX1010102,
DRM_FORMAT_ARGB2101010,
DRM_FORMAT_ABGR2101010,
DRM_FORMAT_RGBA1010102,
DRM_FORMAT_BGRA1010102,
};
static bool
output_set_render_format(struct output *output, uint32_t candidates[], size_t count)
{
for (size_t i = 0; i < count; i++) {
wlr_output_state_set_render_format(&output->pending, candidates[i]);
if (wlr_output_test_state(output->wlr_output, &output->pending)) {
return true;
}
}
return false;
}
static bool
output_format_in_candidates(uint32_t render_format, uint32_t candidates[], size_t count)
{
for (size_t i = 0; i < count; i++) {
if (candidates[i] == render_format) {
return true;
}
}
return false;
}
static enum render_bit_depth
bit_depth_from_format(uint32_t render_format)
{
if (output_format_in_candidates(render_format, output_formats_10bit,
ARRAY_SIZE(output_formats_10bit))) {
return LAB_RENDER_BIT_DEPTH_10;
} else if (output_format_in_candidates(render_format, output_formats_8bit,
ARRAY_SIZE(output_formats_8bit))) {
return LAB_RENDER_BIT_DEPTH_8;
}
return LAB_RENDER_BIT_DEPTH_DEFAULT;
}
static enum render_bit_depth
get_config_render_bit_depth(void)
{
return rc.target_render_depth;
}
static bool
output_supports_hdr(const struct wlr_output *output, const char **unsupported_reason_ptr)
{
const char *unsupported_reason = NULL;
if (!(output->supported_primaries & WLR_COLOR_NAMED_PRIMARIES_BT2020)) {
unsupported_reason = "BT2020 primaries not supported by output";
} else if (!(output->supported_transfer_functions &
WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ)) {
unsupported_reason = "PQ transfer function not supported by output";
} else if (!server.renderer->features.output_color_transform) {
unsupported_reason = "renderer doesn't support output color transforms";
}
if (unsupported_reason_ptr) {
*unsupported_reason_ptr = unsupported_reason;
}
return !unsupported_reason;
}
void
output_state_setup_hdr(struct output *output, bool silent)
{
uint32_t render_format = output->wlr_output->render_format;
const char *unsupported_reason = NULL;
bool hdr_supported = output_supports_hdr(output->wlr_output,
&unsupported_reason);
bool hdr_succeeded = false;
enum render_bit_depth render_bit_depth = get_config_render_bit_depth();
if (render_bit_depth == LAB_RENDER_BIT_DEPTH_DEFAULT) {
render_bit_depth = bit_depth_from_format(render_format);
}
if (!hdr_supported && render_bit_depth == LAB_RENDER_BIT_DEPTH_10) {
if (!silent) {
wlr_log(WLR_INFO, "Cannot enable HDR on output %s: %s",
output->wlr_output->name, unsupported_reason);
}
render_bit_depth = LAB_RENDER_BIT_DEPTH_8;
}
if (render_bit_depth == LAB_RENDER_BIT_DEPTH_10 &&
bit_depth_from_format(render_format) == render_bit_depth) {
/* 10-bit was set successfully before, try to save some
* tests by reusing the format
*/
hdr_succeeded = true;
} else if (render_bit_depth == LAB_RENDER_BIT_DEPTH_10) {
hdr_succeeded = output_set_render_format(output, output_formats_10bit,
ARRAY_SIZE(output_formats_10bit));
if (!hdr_succeeded) {
if (!silent) {
wlr_log(WLR_INFO, "No 10 bit color formats"
" supported, HDR disabled.");
}
if (!output_set_render_format(output, output_formats_8bit,
ARRAY_SIZE(output_formats_8bit))) {
if (!silent) {
wlr_log(WLR_ERROR, "No 8 bit color formats"
" supported either!");
}
}
}
} else {
if (!output_set_render_format(output, output_formats_8bit,
ARRAY_SIZE(output_formats_8bit)) && !silent) {
wlr_log(WLR_ERROR, "No 8 bit color formats supported!");
}
}
output_enable_hdr(output, &output->pending, hdr_succeeded, silent);
}
bool bool
output_get_tearing_allowance(struct output *output) output_get_tearing_allowance(struct output *output)
{ {
@ -371,10 +518,7 @@ configure_new_output(struct output *output)
output_enable_adaptive_sync(output, true); output_enable_adaptive_sync(output, true);
} }
output_state_commit(output); output_state_setup_hdr(output, false);
wlr_output_effective_resolution(wlr_output,
&output->usable_area.width, &output->usable_area.height);
/* /*
* Wait until wlr_output_layout_add_auto() returns before * Wait until wlr_output_layout_add_auto() returns before
@ -384,6 +528,19 @@ configure_new_output(struct output *output)
server.pending_output_layout_change++; server.pending_output_layout_change++;
add_output_to_layout(output); add_output_to_layout(output);
server.pending_output_layout_change--; server.pending_output_layout_change--;
/*
* Commit the output this way instead, HDR needs a buffer, and
* this commit must be called after the output is added to the
* layout above.
*/
lab_wlr_scene_output_commit(output->scene_output, &output->pending);
/*
* Collect the effective resolution after the final commit.
*/
wlr_output_effective_resolution(wlr_output,
&output->usable_area.width, &output->usable_area.height);
} }
static uint64_t static uint64_t
@ -450,9 +607,10 @@ handle_new_output(struct wl_listener *listener, void *data)
} }
if (wlr_output_is_wl(wlr_output)) { if (wlr_output_is_wl(wlr_output)) {
char title[64]; GString *title = g_string_new(server.title_fmt);
snprintf(title, sizeof(title), "%s - %s", "labwc", wlr_output->name); g_string_replace(title, "%o", wlr_output->name, 0);
wlr_wl_output_set_title(wlr_output, title); wlr_wl_output_set_title(wlr_output, title->str);
g_string_free(title, TRUE);
wlr_wl_output_set_app_id(wlr_output, "labwc"); wlr_wl_output_set_app_id(wlr_output, "labwc");
} }
@ -653,6 +811,7 @@ output_config_apply(struct wlr_output_configuration_v1 *config)
wlr_output_state_set_transform(os, head->state.transform); wlr_output_state_set_transform(os, head->state.transform);
output_enable_adaptive_sync(output, output_enable_adaptive_sync(output,
head->state.adaptive_sync_enabled); head->state.adaptive_sync_enabled);
output_state_setup_hdr(output, false);
} }
if (!output_state_commit(output)) { if (!output_state_commit(output)) {
/* /*
@ -666,6 +825,19 @@ output_config_apply(struct wlr_output_configuration_v1 *config)
break; break;
} }
if (output_enabled) {
/*
* The above commit was likely made without an image
* buffer attached. This will break applying HDR color
* transformation, since image descriptions must be
* committed with a buffer attached. Queue the HDR mode
* again if output is enabled, but make it silent,
* since it would have emitted messages already when
* called above.
*/
output_state_setup_hdr(output, true);
}
/* /*
* Add or remove output from layout only if the commit went * Add or remove output from layout only if the commit went
* through. Note that at startup, the output may have already * through. Note that at startup, the output may have already
@ -1136,3 +1308,34 @@ output_set_has_fullscreen_view(struct output *output, bool has_fullscreen_view)
output_enable_adaptive_sync(output, has_fullscreen_view); output_enable_adaptive_sync(output, has_fullscreen_view);
output_state_commit(output); output_state_commit(output);
} }
void
output_enable_hdr(struct output *output, struct wlr_output_state *os,
bool enabled, bool silent)
{
if (enabled && !output_supports_hdr(output->wlr_output, NULL)) {
enabled = false;
}
if (!enabled) {
if (output->wlr_output->supported_primaries != 0 ||
output->wlr_output->supported_transfer_functions != 0) {
if (!silent) {
wlr_log(WLR_DEBUG, "Disabling HDR on output %s",
output->wlr_output->name);
}
wlr_output_state_set_image_description(os, NULL);
}
return;
}
if (!silent) {
wlr_log(WLR_DEBUG, "Enabling HDR on output %s",
output->wlr_output->name);
}
const struct wlr_output_image_description image_desc = {
.primaries = WLR_COLOR_NAMED_PRIMARIES_BT2020,
.transfer_function = WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ,
};
wlr_output_state_set_image_description(os, &image_desc);
}

View file

@ -26,7 +26,7 @@ _create_buffer(struct scaled_buffer *scaled_buffer, double scale)
/* Buffer gets free'd automatically along the backing wlr_buffer */ /* Buffer gets free'd automatically along the backing wlr_buffer */
font_buffer_create(&buffer, self->max_width, self->height, self->text, font_buffer_create(&buffer, self->max_width, self->height, self->text,
&self->font, self->color, bg_pattern, scale); &self->font, self->color, bg_pattern, scale, self->use_markup);
if (!buffer) { if (!buffer) {
wlr_log(WLR_ERROR, "font_buffer_create() failed"); wlr_log(WLR_ERROR, "font_buffer_create() failed");
@ -56,6 +56,7 @@ _equal(struct scaled_buffer *scaled_buffer_a,
struct scaled_font_buffer *b = scaled_buffer_b->data; struct scaled_font_buffer *b = scaled_buffer_b->data;
return str_equal(a->text, b->text) return str_equal(a->text, b->text)
&& a->use_markup == b->use_markup
&& a->max_width == b->max_width && a->max_width == b->max_width
&& str_equal(a->font.name, b->font.name) && str_equal(a->font.name, b->font.name)
&& a->font.size == b->font.size && a->font.size == b->font.size
@ -104,10 +105,10 @@ scaled_font_buffer_create_for_titlebar(struct wlr_scene_tree *parent,
return self; return self;
} }
void static void
scaled_font_buffer_update(struct scaled_font_buffer *self, const char *text, _scaled_font_buffer_update(struct scaled_font_buffer *self, const char *text,
int max_width, struct font *font, const float *color, int max_width, struct font *font, const float *color,
const float *bg_color) const float *bg_color, bool use_markup)
{ {
assert(self); assert(self);
assert(text); assert(text);
@ -120,6 +121,7 @@ scaled_font_buffer_update(struct scaled_font_buffer *self, const char *text,
/* Update internal state */ /* Update internal state */
self->text = xstrdup(text); self->text = xstrdup(text);
self->use_markup = use_markup;
self->max_width = max_width; self->max_width = max_width;
if (font->name) { if (font->name) {
self->font.name = xstrdup(font->name); self->font.name = xstrdup(font->name);
@ -139,3 +141,19 @@ scaled_font_buffer_update(struct scaled_font_buffer *self, const char *text,
scaled_buffer_request_update(self->scaled_buffer, scaled_buffer_request_update(self->scaled_buffer,
self->width, self->height); self->width, self->height);
} }
void
scaled_font_buffer_update(struct scaled_font_buffer *self, const char *text,
int max_width, struct font *font, const float *color,
const float *bg_color)
{
_scaled_font_buffer_update(self, text, max_width, font, color, bg_color, false);
}
void
scaled_font_buffer_update_markup(struct scaled_font_buffer *self, const char *text,
int max_width, struct font *font, const float *color,
const float *bg_color, bool use_markup)
{
_scaled_font_buffer_update(self, text, max_width, font, color, bg_color, use_markup);
}

View file

@ -328,6 +328,16 @@ configure_libinput(struct wlr_input_device *wlr_input_device)
libinput_device_config_scroll_set_method(libinput_dev, dc->scroll_method); libinput_device_config_scroll_set_method(libinput_dev, dc->scroll_method);
} }
libinput_device_config_scroll_set_button(libinput_dev,
libinput_device_config_scroll_get_default_button(libinput_dev));
if (dc->scroll_button < 0) {
wlr_log(WLR_INFO, "scroll button not configured");
} else {
wlr_log(WLR_INFO, "scroll button configured (%d)",
dc->scroll_button);
libinput_device_config_scroll_set_button(libinput_dev, dc->scroll_button);
}
libinput_device_config_send_events_set_mode(libinput_dev, libinput_device_config_send_events_set_mode(libinput_dev,
libinput_device_config_send_events_get_default_mode(libinput_dev)); libinput_device_config_send_events_get_default_mode(libinput_dev));
if ((dc->send_events_mode != LIBINPUT_CONFIG_SEND_EVENTS_ENABLED if ((dc->send_events_mode != LIBINPUT_CONFIG_SEND_EVENTS_ENABLED

View file

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
#define _POSIX_C_SOURCE 200809L #define _POSIX_C_SOURCE 200809L
#include "config.h" #include "config.h"
#include <assert.h>
#include <signal.h> #include <signal.h>
#include <string.h> #include <string.h>
#include <sys/wait.h> #include <sys/wait.h>
@ -9,6 +10,8 @@
#include <wlr/config.h> #include <wlr/config.h>
#include <wlr/render/allocator.h> #include <wlr/render/allocator.h>
#include <wlr/types/wlr_alpha_modifier_v1.h> #include <wlr/types/wlr_alpha_modifier_v1.h>
#include <wlr/types/wlr_color_management_v1.h>
#include <wlr/types/wlr_color_representation_v1.h>
#include <wlr/types/wlr_data_control_v1.h> #include <wlr/types/wlr_data_control_v1.h>
#include <wlr/types/wlr_data_device.h> #include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_drm.h> #include <wlr/types/wlr_drm.h>
@ -88,6 +91,12 @@ reload_config_and_theme(void)
/* Avoid UAF when dialog client is used during reconfigure */ /* Avoid UAF when dialog client is used during reconfigure */
action_prompts_destroy(); action_prompts_destroy();
/*
* Cancel any pending auto-raise before reloading config in case the
* raiseOnFocusDelay option was disabled or changed.
*/
desktop_cancel_pending_auto_raise();
scaled_buffer_invalidate_sharing(); scaled_buffer_invalidate_sharing();
rcxml_finish(); rcxml_finish();
clearborder_cache(border_cache); clearborder_cache(border_cache);
@ -214,39 +223,6 @@ handle_drm_lease_request(struct wl_listener *listener, void *data)
} }
#endif #endif
static bool
protocol_is_privileged(const struct wl_interface *iface)
{
static const char * const rejected[] = {
"wp_drm_lease_device_v1",
"zwlr_gamma_control_manager_v1",
"zwlr_output_manager_v1",
"zwlr_output_power_manager_v1",
"zwp_input_method_manager_v2",
"zwlr_virtual_pointer_manager_v1",
"zwp_virtual_keyboard_manager_v1",
"zwlr_export_dmabuf_manager_v1",
"zwlr_screencopy_manager_v1",
"ext_data_control_manager_v1",
"zwlr_data_control_manager_v1",
"wp_security_context_manager_v1",
"ext_idle_notifier_v1",
"zwlr_foreign_toplevel_manager_v1",
"ext_foreign_toplevel_list_v1",
"ext_session_lock_manager_v1",
"zwlr_layer_shell_v1",
"ext_workspace_manager_v1",
"ext_image_copy_capture_manager_v1",
"ext_output_image_capture_source_manager_v1",
};
for (size_t i = 0; i < ARRAY_SIZE(rejected); i++) {
if (!strcmp(iface->name, rejected[i])) {
return true;
}
}
return false;
}
static bool static bool
allow_for_sandbox(const struct wlr_security_context_v1_state *security_state, allow_for_sandbox(const struct wlr_security_context_v1_state *security_state,
const struct wl_interface *iface) const struct wl_interface *iface)
@ -289,6 +265,8 @@ allow_for_sandbox(const struct wlr_security_context_v1_state *security_state,
"xdg_wm_dialog_v1", "xdg_wm_dialog_v1",
/* plus */ /* plus */
"wp_alpha_modifier_v1", "wp_alpha_modifier_v1",
"wp_color_manager_v1",
"wp_color_representation_manager_v1",
"wp_linux_drm_syncobj_manager_v1", "wp_linux_drm_syncobj_manager_v1",
"zxdg_exporter_v1", "zxdg_exporter_v1",
"zxdg_exporter_v2", "zxdg_exporter_v2",
@ -327,6 +305,11 @@ server_global_filter(const struct wl_client *client, const struct wl_global *glo
} }
#endif #endif
uint32_t iface_id = parse_privileged_interface(iface->name);
if (iface_id && !(iface_id & rc.allowed_interfaces)) {
return false;
}
/* Do not allow security_context_manager_v1 to clients with a security context attached */ /* Do not allow security_context_manager_v1 to clients with a security context attached */
const struct wlr_security_context_v1_state *security_context = const struct wlr_security_context_v1_state *security_context =
wlr_security_context_manager_v1_lookup_client( wlr_security_context_manager_v1_lookup_client(
@ -342,11 +325,11 @@ server_global_filter(const struct wl_client *client, const struct wl_global *glo
/* /*
* TODO: The following call is basically useless right now * TODO: The following call is basically useless right now
* and should be replaced with * and should be replaced with
* assert(allow || protocol_is_privileged(iface)); * assert(allow || iface_id);
* This ensures that our lists are in sync with what * This ensures that our lists are in sync with what
* protocols labwc supports. * protocols labwc supports.
*/ */
if (!allow && !protocol_is_privileged(iface)) { if (!allow && !iface_id) {
wlr_log(WLR_ERROR, "Blocking unknown protocol %s", iface->name); wlr_log(WLR_ERROR, "Blocking unknown protocol %s", iface->name);
} else if (!allow) { } else if (!allow) {
wlr_log(WLR_DEBUG, "Blocking %s for security context %s->%s->%s", wlr_log(WLR_DEBUG, "Blocking %s for security context %s->%s->%s",
@ -432,6 +415,39 @@ handle_renderer_lost(struct wl_listener *listener, void *data)
wlr_renderer_destroy(old_renderer); wlr_renderer_destroy(old_renderer);
} }
static void
handle_toplevel_capture_source_destroy(struct wl_listener *listener, void *data)
{
struct view *view = wl_container_of(listener, view, capture.on_capture_source_destroy);
assert(view->capture.source);
view->capture.source = NULL;
wl_list_remove(&listener->link);
wl_list_init(&listener->link);
}
static void
handle_toplevel_capture_request(struct wl_listener *listener, void *data)
{
struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request *request = data;
struct view *view = request->toplevel_handle->data;
assert(view);
wlr_log(WLR_INFO, "Capturing toplevel %s", view->app_id);
if (!view->capture.source) {
view->capture.source = wlr_ext_image_capture_source_v1_create_with_scene_node(
&view->capture.scene->tree.node, server.wl_event_loop,
server.allocator, server.renderer);
assert(view->capture.source);
view->capture.on_capture_source_destroy.notify =
handle_toplevel_capture_source_destroy;
wl_signal_add(&view->capture.source->events.destroy,
&view->capture.on_capture_source_destroy);
}
wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request_accept(
request, view->capture.source);
}
void void
server_init(void) server_init(void)
{ {
@ -596,6 +612,47 @@ server_init(void)
* | output->layer_tree[0] | background layer surfaces (e.g. swaybg) * | output->layer_tree[0] | background layer surfaces (e.g. swaybg)
*/ */
if (server.renderer->features.input_color_transform) {
const enum wp_color_manager_v1_render_intent render_intents[] = {
WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL,
};
size_t transfer_functions_len = 0;
enum wp_color_manager_v1_transfer_function *transfer_functions =
wlr_color_manager_v1_transfer_function_list_from_renderer(
server.renderer, &transfer_functions_len);
size_t primaries_len = 0;
enum wp_color_manager_v1_primaries *primaries =
wlr_color_manager_v1_primaries_list_from_renderer(
server.renderer, &primaries_len);
struct wlr_color_manager_v1 *cm = wlr_color_manager_v1_create(
server.wl_display, 2, &(struct wlr_color_manager_v1_options){
.features = {
.parametric = true,
.set_mastering_display_primaries = true,
},
.render_intents = render_intents,
.render_intents_len = ARRAY_SIZE(render_intents),
.transfer_functions = transfer_functions,
.transfer_functions_len = transfer_functions_len,
.primaries = primaries,
.primaries_len = primaries_len,
});
free(transfer_functions);
free(primaries);
if (cm) {
wlr_scene_set_color_manager_v1(server.scene, cm);
} else {
wlr_log(WLR_ERROR, "unable to create color manager");
}
}
wlr_color_representation_manager_v1_create_with_renderer(
server.wl_display, 1, server.renderer);
server.workspace_tree = lab_wlr_scene_tree_create(&server.scene->tree); server.workspace_tree = lab_wlr_scene_tree_create(&server.scene->tree);
server.xdg_popup_tree = lab_wlr_scene_tree_create(&server.scene->tree); server.xdg_popup_tree = lab_wlr_scene_tree_create(&server.scene->tree);
#if HAVE_XWAYLAND #if HAVE_XWAYLAND
@ -670,6 +727,19 @@ server_init(void)
wlr_screencopy_manager_v1_create(server.wl_display); wlr_screencopy_manager_v1_create(server.wl_display);
wlr_ext_image_copy_capture_manager_v1_create(server.wl_display, 1); wlr_ext_image_copy_capture_manager_v1_create(server.wl_display, 1);
wlr_ext_output_image_capture_source_manager_v1_create(server.wl_display, 1); wlr_ext_output_image_capture_source_manager_v1_create(server.wl_display, 1);
server.toplevel_capture.manager =
wlr_ext_foreign_toplevel_image_capture_source_manager_v1_create(
server.wl_display, 1);
if (server.toplevel_capture.manager) {
server.toplevel_capture.on.new_request.notify = handle_toplevel_capture_request;
wl_signal_add(&server.toplevel_capture.manager->events.new_request,
&server.toplevel_capture.on.new_request);
} else {
/* Allow safe removal on shutdown */
wl_list_init(&server.toplevel_capture.on.new_request.link);
}
wlr_data_control_manager_v1_create(server.wl_display); wlr_data_control_manager_v1_create(server.wl_display);
wlr_ext_data_control_manager_v1_create(server.wl_display, wlr_ext_data_control_manager_v1_create(server.wl_display,
LAB_EXT_DATA_CONTROL_VERSION); LAB_EXT_DATA_CONTROL_VERSION);
@ -804,6 +874,8 @@ server_finish(void)
server.drm_lease_request.notify = NULL; server.drm_lease_request.notify = NULL;
} }
wl_list_remove(&server.toplevel_capture.on.new_request.link);
wlr_backend_destroy(server.backend); wlr_backend_destroy(server.backend);
wlr_allocator_destroy(server.allocator); wlr_allocator_destroy(server.allocator);

78
src/show-desktop.c Normal file
View file

@ -0,0 +1,78 @@
// SPDX-License-Identifier: GPL-2.0-only
#include "show-desktop.h"
#include <wlr/types/wlr_seat.h>
#include "common/array.h"
#include "config/types.h"
#include "labwc.h"
#include "view.h"
static bool is_showing_desktop;
static void
minimize_views(struct wl_array *views, bool minimize)
{
struct view **view;
wl_array_for_each_reverse(view, views) {
view_minimize(*view, minimize);
}
}
static void
show(void)
{
static struct wl_array views;
wl_array_init(&views);
/* Build array first as minimize changes server.views */
struct view *view;
for_each_view(view, &server.views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) {
if (view->minimized) {
continue;
}
view->was_minimized_by_show_desktop_action = true;
array_add(&views, view);
}
minimize_views(&views, true);
is_showing_desktop = true;
wl_array_release(&views);
}
static void
restore(void)
{
static struct wl_array views;
wl_array_init(&views);
struct view *view;
for_each_view(view, &server.views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) {
if (view->was_minimized_by_show_desktop_action) {
array_add(&views, view);
}
}
minimize_views(&views, false);
show_desktop_reset();
wl_array_release(&views);
}
void
show_desktop_toggle(void)
{
if (is_showing_desktop) {
restore();
} else {
show();
}
}
void
show_desktop_reset(void)
{
is_showing_desktop = false;
struct view *view;
for_each_view(view, &server.views, LAB_VIEW_CRITERIA_NONE) {
view->was_minimized_by_show_desktop_action = false;
}
}

View file

@ -1103,7 +1103,7 @@ entry(struct theme *theme, const char *key, const char *value)
parse_color(value, theme->window[SSD_ACTIVE].button_hover_border_color); parse_color(value, theme->window[SSD_ACTIVE].button_hover_border_color);
} }
/* botton hover overlay */ /* button hover overlay */
if (match_glob(key, "window.button.hover.bg.color")) { if (match_glob(key, "window.button.hover.bg.color")) {
parse_color(value, theme->window_button_hover_bg_color); parse_color(value, theme->window_button_hover_bg_color);
} }

View file

@ -80,7 +80,7 @@ struct view_query *
view_query_create(void) view_query_create(void)
{ {
struct view_query *query = znew(*query); struct view_query *query = znew(*query);
/* Must be synced with view_matches_criteria() in window-rules.c */ /* Must be synced with view_matches_rule() in window-rules.c */
query->window_type = LAB_WINDOW_TYPE_INVALID; query->window_type = LAB_WINDOW_TYPE_INVALID;
query->maximized = VIEW_AXIS_INVALID; query->maximized = VIEW_AXIS_INVALID;
query->decoration = LAB_SSD_MODE_INVALID; query->decoration = LAB_SSD_MODE_INVALID;
@ -263,8 +263,8 @@ view_get_root(struct view *view)
return view; return view;
} }
static bool bool
matches_criteria(struct view *view, enum lab_view_criteria criteria) view_matches_criteria(struct view *view, enum lab_view_criteria criteria)
{ {
if (!view_is_focusable(view)) { if (!view_is_focusable(view)) {
return false; return false;
@ -316,7 +316,7 @@ view_next(struct wl_list *head, struct view *view, enum lab_view_criteria criter
for (elm = elm->next; elm != head; elm = elm->next) { for (elm = elm->next; elm != head; elm = elm->next) {
view = wl_container_of(elm, view, link); view = wl_container_of(elm, view, link);
if (matches_criteria(view, criteria)) { if (view_matches_criteria(view, criteria)) {
return view; return view;
} }
} }
@ -332,7 +332,7 @@ view_prev(struct wl_list *head, struct view *view, enum lab_view_criteria criter
for (elm = elm->prev; elm != head; elm = elm->prev) { for (elm = elm->prev; elm != head; elm = elm->prev) {
view = wl_container_of(elm, view, link); view = wl_container_of(elm, view, link);
if (matches_criteria(view, criteria)) { if (view_matches_criteria(view, criteria)) {
return view; return view;
} }
} }
@ -2482,6 +2482,10 @@ view_init(struct view *view)
view->title = xstrdup(""); view->title = xstrdup("");
view->app_id = xstrdup(""); view->app_id = xstrdup("");
view->capture.scene = wlr_scene_create();
view->capture.scene->restack_xwayland_surfaces = false;
wl_list_init(&view->capture.on_capture_source_destroy.link);
} }
void void
@ -2503,6 +2507,9 @@ view_destroy(struct view *view)
wl_list_remove(&view->request_fullscreen.link); wl_list_remove(&view->request_fullscreen.link);
wl_list_remove(&view->set_title.link); wl_list_remove(&view->set_title.link);
wl_list_remove(&view->destroy.link); wl_list_remove(&view->destroy.link);
wl_list_remove(&view->capture.on_capture_source_destroy.link);
wlr_scene_node_destroy(&view->capture.scene->tree.node);
if (view->foreign_toplevel) { if (view->foreign_toplevel) {
foreign_toplevel_destroy(view->foreign_toplevel); foreign_toplevel_destroy(view->foreign_toplevel);
@ -2521,6 +2528,10 @@ view_destroy(struct view *view)
server.active_view = NULL; server.active_view = NULL;
} }
if (server.pending_auto_raise_view == view) {
desktop_cancel_pending_auto_raise();
}
if (server.session_lock_manager->last_active_view == view) { if (server.session_lock_manager->last_active_view == view) {
server.session_lock_manager->last_active_view = NULL; server.session_lock_manager->last_active_view = NULL;
} }

View file

@ -24,7 +24,7 @@ other_instances_exist(struct view *self, struct view_query *query)
} }
static bool static bool
view_matches_criteria(struct window_rule *rule, struct view *view) view_matches_rule(struct window_rule *rule, struct view *view)
{ {
struct view_query query = { struct view_query query = {
.identifier = rule->identifier, .identifier = rule->identifier,
@ -52,7 +52,7 @@ window_rules_apply(struct view *view, enum window_rule_event event)
if (rule->event != event) { if (rule->event != event) {
continue; continue;
} }
if (view_matches_criteria(rule, view)) { if (view_matches_rule(rule, view)) {
actions_run(view, &rule->actions, NULL); actions_run(view, &rule->actions, NULL);
} }
} }
@ -69,8 +69,8 @@ window_rules_get_property(struct view *view, const char *property)
* for foot's "serverDecoration" property to be "default". * for foot's "serverDecoration" property to be "default".
* *
* <windowRules> * <windowRules>
* <windowRule identifier="*" serverDecoration="no"/> * <windowRule identifier="*" serverDecoration="no" />
* <windowRule identifier="foot" serverDecoration="default"/> * <windowRule identifier="foot" serverDecoration="default" />
* </windowRules> * </windowRules>
*/ */
struct window_rule *rule; struct window_rule *rule;
@ -81,7 +81,7 @@ window_rules_get_property(struct view *view, const char *property)
* attribute would still return here if that property was asked * attribute would still return here if that property was asked
* for. * for.
*/ */
if (view_matches_criteria(rule, view)) { if (view_matches_rule(rule, view)) {
if (rule->server_decoration if (rule->server_decoration
&& !strcasecmp(property, "serverDecoration")) { && !strcasecmp(property, "serverDecoration")) {
return rule->server_decoration; return rule->server_decoration;

View file

@ -22,6 +22,7 @@
#include "common/borderset.h" #include "common/borderset.h"
#include "labwc.h" #include "labwc.h"
#include "output.h" #include "output.h"
#include "show-desktop.h"
#include "theme.h" #include "theme.h"
#include "view.h" #include "view.h"
@ -552,6 +553,8 @@ workspaces_switch_to(struct workspace *target, bool update_focus)
desktop_update_top_layer_visibility(); desktop_update_top_layer_visibility();
wlr_ext_workspace_handle_v1_set_active(target->ext_workspace, true); wlr_ext_workspace_handle_v1_set_active(target->ext_workspace, true);
show_desktop_reset();
} }
void void

View file

@ -43,10 +43,13 @@ popup_unconstrain(struct xdg_popup *popup)
* than zero, typically with Qt apps. We therefore clamp it to avoid for * than zero, typically with Qt apps. We therefore clamp it to avoid for
* example the 'File' menu of a maximized window to end up on an another * example the 'File' menu of a maximized window to end up on an another
* output. * output.
* Also some apps open the menu exactly at the right border when maximized,
* causing popup_box->x (or y?) to be in the next output. We subtract one
* inside MAX to avoid the problem mentioned above.
*/ */
struct wlr_box *popup_box = &popup->wlr_popup->scheduled.geometry; struct wlr_box *popup_box = &popup->wlr_popup->scheduled.geometry;
struct output *output = output_nearest_to(parent_lx + MAX(popup_box->x, 0), struct output *output = output_nearest_to(parent_lx + MAX(popup_box->x - 1, 0),
parent_ly + MAX(popup_box->y, 0)); parent_ly + MAX(popup_box->y - 1, 0));
struct wlr_box usable = output_usable_area_in_layout_coords(output); struct wlr_box usable = output_usable_area_in_layout_coords(output);
/* Get offset of toplevel window from its surface */ /* Get offset of toplevel window from its surface */
@ -166,4 +169,6 @@ xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup)
node_descriptor_create(wlr_popup->base->surface->data, node_descriptor_create(wlr_popup->base->surface->data,
LAB_NODE_XDG_POPUP, view, /*data*/ NULL); LAB_NODE_XDG_POPUP, view, /*data*/ NULL);
wlr_scene_xdg_surface_create(&view->capture.scene->tree, wlr_popup->base);
} }

View file

@ -30,6 +30,8 @@
#define LAB_XDG_SHELL_VERSION 6 #define LAB_XDG_SHELL_VERSION 6
#define CONFIGURE_TIMEOUT_MS 100 #define CONFIGURE_TIMEOUT_MS 100
static struct view *xdg_toplevel_view_get_root(struct view *view);
static struct xdg_toplevel_view * static struct xdg_toplevel_view *
xdg_toplevel_view_from_view(struct view *view) xdg_toplevel_view_from_view(struct view *view)
{ {
@ -121,7 +123,7 @@ set_fullscreen_from_request(struct view *view,
view_set_fullscreen(view, requested->fullscreen); view_set_fullscreen(view, requested->fullscreen);
} }
/* Called from commit handler and updates view->pending.x/y directly */ /* Called from map/commit handler and updates view->pending.x/y directly */
static void static void
set_initial_position(struct view *view) set_initial_position(struct view *view)
{ {
@ -263,11 +265,10 @@ handle_commit(struct wl_listener *listener, void *data)
bool update_required = false; bool update_required = false;
/* /*
* The pending size will be empty in two cases: * If we didn't know the natural size when leaving fullscreen or
* (1) when the view is first mapped * unmaximizing, then the pending size will be 0x0. In this case,
* (2) when leaving fullscreen or un-maximizing, if the view * the pending x/y is also unset and we still need to position
* was initially fullscreen/maximized and the natural * the window.
* geometry isn't known yet
*/ */
if (wlr_box_empty(&view->pending) && !wlr_box_empty(&size)) { if (wlr_box_empty(&view->pending) && !wlr_box_empty(&size)) {
view->pending.width = size.width; view->pending.width = size.width;
@ -463,6 +464,7 @@ handle_destroy(struct wl_listener *listener, void *data)
/* Remove xdg-shell view specific listeners */ /* Remove xdg-shell view specific listeners */
wl_list_remove(&xdg_toplevel_view->set_app_id.link); wl_list_remove(&xdg_toplevel_view->set_app_id.link);
wl_list_remove(&xdg_toplevel_view->request_show_window_menu.link); wl_list_remove(&xdg_toplevel_view->request_show_window_menu.link);
wl_list_remove(&xdg_toplevel_view->set_parent.link);
wl_list_remove(&xdg_toplevel_view->new_popup.link); wl_list_remove(&xdg_toplevel_view->new_popup.link);
wl_list_remove(&view->commit.link); wl_list_remove(&view->commit.link);
@ -566,6 +568,23 @@ handle_request_show_window_menu(struct wl_listener *listener, void *data)
menu_open_root(menu, cursor->x, cursor->y); menu_open_root(menu, cursor->x, cursor->y);
} }
static void
handle_set_parent(struct wl_listener *listener, void *data)
{
struct xdg_toplevel_view *xdg_toplevel_view = wl_container_of(
listener, xdg_toplevel_view, set_parent);
struct view *view = &xdg_toplevel_view->base;
struct view *view_root = xdg_toplevel_view_get_root(view);
if (view_root == view) {
return;
}
struct wlr_scene_node *node, *tmp;
wl_list_for_each_safe(node, tmp, &view->capture.scene->tree.children, link) {
wlr_log(WLR_DEBUG, "moving capture scene node to view_root");
wlr_scene_node_reparent(node, &view_root->capture.scene->tree);
}
}
static void static void
handle_set_title(struct wl_listener *listener, void *data) handle_set_title(struct wl_listener *listener, void *data)
{ {
@ -821,6 +840,29 @@ handle_map(struct wl_listener *listener, void *data)
} else { } else {
view_set_ssd_mode(view, LAB_SSD_MODE_NONE); view_set_ssd_mode(view, LAB_SSD_MODE_NONE);
} }
/*
* Set initial "pending" dimensions. "Current"
* dimensions remain zero until handle_commit().
* Note: this must be done before view_impl_map()
* for window rules to work correctly.
*/
if (wlr_box_empty(&view->pending)) {
struct wlr_xdg_surface *xdg_surface =
xdg_surface_from_view(view);
view->pending.width = xdg_surface->geometry.width;
view->pending.height = xdg_surface->geometry.height;
set_initial_position(view);
}
/*
* Set initial "current" position directly before
* calling view_moved() to reduce flicker
*/
view->current.x = view->pending.x;
view->current.y = view->pending.y;
view_moved(view);
} }
view_impl_map(view); view_impl_map(view);
@ -1024,6 +1066,9 @@ handle_new_xdg_toplevel(struct wl_listener *listener, void *data)
mappable_connect(&view->mappable, xdg_surface->surface, mappable_connect(&view->mappable, xdg_surface->surface,
handle_map, handle_unmap); handle_map, handle_unmap);
struct view *root_view = xdg_toplevel_view_get_root(view);
wlr_scene_xdg_surface_create(&root_view->capture.scene->tree, xdg_surface);
struct wlr_xdg_toplevel *toplevel = xdg_surface->toplevel; struct wlr_xdg_toplevel *toplevel = xdg_surface->toplevel;
CONNECT_SIGNAL(toplevel, view, destroy); CONNECT_SIGNAL(toplevel, view, destroy);
CONNECT_SIGNAL(toplevel, view, request_move); CONNECT_SIGNAL(toplevel, view, request_move);
@ -1037,6 +1082,7 @@ handle_new_xdg_toplevel(struct wl_listener *listener, void *data)
/* Events specific to XDG toplevel views */ /* Events specific to XDG toplevel views */
CONNECT_SIGNAL(toplevel, xdg_toplevel_view, set_app_id); CONNECT_SIGNAL(toplevel, xdg_toplevel_view, set_app_id);
CONNECT_SIGNAL(toplevel, xdg_toplevel_view, request_show_window_menu); CONNECT_SIGNAL(toplevel, xdg_toplevel_view, request_show_window_menu);
CONNECT_SIGNAL(toplevel, xdg_toplevel_view, set_parent);
CONNECT_SIGNAL(xdg_surface, xdg_toplevel_view, new_popup); CONNECT_SIGNAL(xdg_surface, xdg_toplevel_view, new_popup);
wl_list_insert(&server.views, &view->link); wl_list_insert(&server.views, &view->link);

View file

@ -783,6 +783,7 @@ handle_map(struct wl_listener *listener, void *data)
view->content_tree = wlr_scene_subsurface_tree_create( view->content_tree = wlr_scene_subsurface_tree_create(
view->scene_tree, view->surface); view->scene_tree, view->surface);
die_if_null(view->content_tree); die_if_null(view->content_tree);
wlr_scene_subsurface_tree_create(&view->capture.scene->tree, view->surface);
} }
wlr_scene_node_set_enabled(&view->content_tree->node, !view->shaded); wlr_scene_node_set_enabled(&view->content_tree->node, !view->shaded);