Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Manuel Barrio Linares 2025-12-26 19:36:37 -03:00
commit ed64c4a2ca
20 changed files with 430 additions and 196 deletions

103
NEWS.md
View file

@ -9,7 +9,7 @@ The format is based on [Keep a Changelog]
| Date | All Changes | wlroots version | lines-of-code |
|------------|---------------|-----------------|---------------|
| 2025-11-15 | [unreleased] | 0.19.2 | 28825 |
| 2025-12-19 | [0.9.3] | 0.19.2 | 28968 |
| 2025-10-10 | [0.9.2] | 0.19.1 | 28818 |
| 2025-08-02 | [0.9.1] | 0.19.0 | 28605 |
| 2025-07-11 | [0.9.0] | 0.19.0 | 28586 |
@ -40,6 +40,7 @@ The format is based on [Keep a Changelog]
| 2021-03-05 | [0.1.0] | 0.12.0 | 4627 |
[unreleased]: NEWS.md#unreleased
[0.9.3]: NEWS.md#093---2025-12-19
[0.9.2]: NEWS.md#092---2025-10-10
[0.9.1]: NEWS.md#091---2025-08-02
[0.9.0]: NEWS.md#090---2025-07-11
@ -89,8 +90,6 @@ There are some regression warnings worth noting for the switch to wlroots 0.19:
with the environment variable `LABWC_FALLBACK_OUTPUT=NOOP-fallback` to
temporarily create a fallback-output when the last physical display
disconnects. [#2914] [#2939] [wlroots-4878] [gtk-8792]
- Due to a single-pixel protocol issue, `waylock` and `chayang` do not work.
This will be fixed in `wlroots-0.19.1`. [#2943] [wlroots-5098]
- Menu item can no longer be activated in any Gtk applications with a single
press-drag-release mouse action. For context: This is due to ambiguity in the
specifications and contrary implementations. For example, Gtk applications are
@ -98,26 +97,53 @@ There are some regression warnings worth noting for the switch to wlroots 0.19:
other compositors like Weston, Mutter and labwc. It has been decided not to
block the release due to this regression as it is an eco-system wide issue
that has existed for a long time. [#2787]
- VR headset support is disabled when compiled with wlroots `0.19.0` to work
around a bug on the wlroots side which is expected to be fixed in wlroots
`0.19.1` [#2887]
With wlroots compiled with libwayland (>= 1.24.0), there is an invisible margin
preventing pointer focus on some layer-shell surfaces including those created by
Gtk. In simple words, this is because libwayland now rounds floats a bit
differently [#3099]. There is a pending fix [wlroots-5159].
- It is strongly recommended to use at least wlroots 0.19.1 [#2943]
[wlroots-5098] [#2887]
[wlroots-4878]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4878
[wlroots-5098]:https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5098
[wlroots-5159]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5159
[gtk-8792]: https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/8792
## unreleased
[unreleased-commits]
## 0.9.3 - 2025-12-19
[0.9.3-commits]
This release contains a good amount of bug-fixes, code simplification and
small usability improvements.
With the stability that comes with having tracked `wlroots 0.19` for a decent
length of time, this feels like the best version of labwc so far.
In terms of new features, it is worth drawing attention to the click support in
the window-switcher on-screen-display by @tokyo4j [#3186] which has frequently
been requested by users.
As a general note to users, we discourage the use of empty strings in the
`rc.xml` configuration file, for example `<theme><name></name></theme>`. There
are only a few areas left where empty string are ignored (like under
`<libinput>`) but the intent for future releases is to consistently read empty
strings as empty strings. As a preparation, this release has added some warnings
for empty strings that are currently ignored, so that users can take action.
Also, the example `docs/rc.xml.all` has been updated to remove poor examples in
this regard.
A big thank you to all involved in this release.
### Added
- Add `<windowSwitcher order="focus|age"/>` to optionally order windows by age
rather than most recent focus. @mbroemme [#3229]
- Replace `<snapping><range>` with `<snapping><range inner="" outer="">` to
provide more granular control when configuring the size of snapping areas
(including `<topMaximize>`) on output edges with and without adjacent outputs.
@elviosak [#3241]
- Add `direction` option to `Resize` action supporting the values `up-left`,
`up`, `up-right`, `left`, `right`, `down-left`, `down`, `down-right`. This
mirrors Fluxbox's `StartResizing [corner]` behavior. @mbroemme [#3239]
- Allow the use of the `sendEventsMode` configuration option on keyboards in
order to disable keyboard input. @cillian64 [#3208]
@ -131,8 +157,8 @@ differently [#3099]. There is a pending fix [wlroots-5159].
- Support the following new `<windowSwitcher>` configuration options:
- `<osd thumbnailLabelFormat="%T">` to specify the label text in each item in
the thumbnail style window-switcher. @elviosak [#3187]
- `<osd output="all|pointer|keyboard">` to specify which monitor(s) to show
the OSD(s) on. @dntxi [#3201]
- `<osd output="all|focused|cursor">` to specify which monitor(s) to show
the OSD(s) on. @dntxi [#3201] [#3248]
- Support window-switcher OSD item click to focus window @tokyo4j [#3186]
- With the window-switcher custom field state specifiers 's' and 'S', show 's'
for shaded window @domo141 [#2895]
@ -148,6 +174,28 @@ differently [#3099]. There is a pending fix [wlroots-5159].
### Fixed
- Handle desktop files with dots in their names better @Consolatis [#3267]
- Do not synthesize cursor relative motion events from absolute events to fix a
couple of problems: Firstly to avoid unexpectedly large relative motion deltas
with multiple input devices or in nested/VM scenarios, and secondly to fix
erratic mouse behavior in applications that use relative events whilst locking
with pointer constraints. @jlindgren90 [#3251]
- Allow cursor movement until entering constraint surface, to fix an issue where
the cursor would get stuck (immovable) outside the window of a Wine/Wayland
game, if it was already outside when the game started (which is common with
4:3 games on a 16:9 screen). @jlindgren90 [#3252]
- Flush XCB connection to mitigate race between Raise and input. @jlindgren90
[#3249]
- Fix disappearing XWayland popups with some (less commonly used) clients like
Imagemagick's `display` command, `xshogi`, `xedit` and `xfig` caused by
too many surface-pings. @jlindgren90 [#3152] [#3246]
- Center small fullscreen xdg-shell windows and add black background fill. This
increases spec compliance and improves the user experience with games like
SWAT4, Quake III and Splinter Cell 3. @jlindgren90 [#3233]
- When followMouse=yes, update focus on cursor entering SSD rather than just the
client surface. Fixes a regression in 885919f. @tokyo4j [#3211]
- Set all foreign-toplevel initial states correctly. This is not believed to fix
any particular user-issue, but just feels safer. @jlindgren90 [#3217]
- Update layer-shell client top layer visiblity on unmap instead of destroy
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
@ -181,6 +229,16 @@ differently [#3099]. There is a pending fix [wlroots-5159].
### Changed
- `<snapping><range>` is deprecated. Use `<snapping><range inner="" outer="">`
instead. @elviosak [#3241]
- When cycling through windows (typically with Alt-Tab) there are two minor
user-visible changes. For most users these will not be noticeable, but are
mentioned here for completeness.
- The initially selected window will now be the one that previously had
keyboard focus when cycling commenced rather than the second topmost one.
@tokyo4j [#3236]
- Windows that are spawned whilst cycling can no longer be cycled through. The
intent is to fix this in future releases. @tokyo4j [#3236]
- Refactor window switcher configuration to put attributes `show` and `style`
under `<windowSwitcher><osd>` rather than directly under `<windowSwitcher>`.
The old configuration syntax will remain supported for at least one release.
@ -2461,7 +2519,8 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16
ShowMenu
[Keep a Changelog]: https://keepachangelog.com/en/1.0.0/
[unreleased-commits]: https://github.com/labwc/labwc/compare/0.9.2...HEAD
[unreleased-commits]: https://github.com/labwc/labwc/compare/0.9.3...HEAD
[0.9.3-commits]: https://github.com/labwc/labwc/compare/0.9.2...0.9.3
[0.9.2-commits]: https://github.com/labwc/labwc/compare/0.9.1...0.9.2
[0.9.1-commits]: https://github.com/labwc/labwc/compare/0.9.0...0.9.1
[0.9.0-commits]: https://github.com/labwc/labwc/compare/0.8.4...0.9.0
@ -2938,6 +2997,7 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16
[#3145]: https://github.com/labwc/labwc/pull/3145
[#3146]: https://github.com/labwc/labwc/pull/3146
[#3148]: https://github.com/labwc/labwc/pull/3148
[#3152]: https://github.com/labwc/labwc/pull/3152
[#3153]: https://github.com/labwc/labwc/pull/3153
[#3157]: https://github.com/labwc/labwc/pull/3157
[#3158]: https://github.com/labwc/labwc/pull/3158
@ -2951,3 +3011,16 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16
[#3199]: https://github.com/labwc/labwc/pull/3199
[#3201]: https://github.com/labwc/labwc/pull/3201
[#3208]: https://github.com/labwc/labwc/pull/3208
[#3211]: https://github.com/labwc/labwc/pull/3211
[#3217]: https://github.com/labwc/labwc/pull/3217
[#3229]: https://github.com/labwc/labwc/pull/3229
[#3233]: https://github.com/labwc/labwc/pull/3233
[#3236]: https://github.com/labwc/labwc/pull/3236
[#3239]: https://github.com/labwc/labwc/pull/3239
[#3241]: https://github.com/labwc/labwc/pull/3241
[#3246]: https://github.com/labwc/labwc/pull/3246
[#3248]: https://github.com/labwc/labwc/pull/3248
[#3249]: https://github.com/labwc/labwc/pull/3249
[#3251]: https://github.com/labwc/labwc/pull/3251
[#3252]: https://github.com/labwc/labwc/pull/3252
[#3267]: https://github.com/labwc/labwc/pull/3267

View file

@ -22,6 +22,13 @@
# XKB_DEFAULT_OPTIONS=grp:alt_shift_toggle
# XKB_DEFAULT_OPTIONS=grp:shift_caps_toggle
## GTK4 started to require input methods like fcitx5 or ibus to handle
## simple compose sequences. If you do not use input methods, uncomment
## the following line to force GTK4 internal composing. For further
## information see https://labwc.github.io/integration.html#gtk
##
# GTK_IM_MODULE=simple
##
## Set cursor theme and size. Find system icons themes with:
## `find /usr/share/icons/ -type d -name "cursors"`

View file

@ -140,4 +140,3 @@ labnag \\
--button-border-size 2\\
-t 60
```

View file

@ -125,13 +125,25 @@ Actions are used in menus and keyboard/mouse bindings.
Resize and move the active window back to its untiled or unmaximized
position if it had been maximized or tiled to a direction or region.
*<action name="NextWindow" />*++
*<action name="PreviousWindow" />*
*<action name="NextWindow" workspace="current" output="all" identifier="all" />*++
*<action name="PreviousWindow" workspace="current" output="all" identifier="all" />*
Cycle focus to next/previous window, respectively.
Default keybind for NextWindow is Alt-Tab.
Default keybinds for NextWindow and PreviousWindow are Alt-Tab and
Shift-Alt-Tab. While cycling through windows, the arrow keys move the
selected window forwards/backwards and the escape key halts the cycling.
The arrow keys are used to move forwards/backwards while cycling.
*workspace* [all|current]
This determines whether to cycle through windows on all workspaces or the
current workspace. Default is "current".
*output* [all|focused|cursor]
This determines whether to cycle through windows on all outputs, the focused
output, or the output under the cursor. Default is "all".
*identifier* [all|current]
This determines whether to cycle through all windows or only windows of the
same application as the currently focused window. Default is "all".
*<action name="Reconfigure" />*
Re-load configuration and theme files.
@ -379,7 +391,7 @@ Actions are used in menus and keyboard/mouse bindings.
*x* [center|value] Specifies the horizontal warp position within the
target area. "center": Moves the cursor to the horizontal center of the
target area. Positive or negative integers warp the cursor to a position
offset by the specified number of pixels from the left or right edge of
offset by the specified number of pixels from the left or right edge of
the target area, respectively. Default is "center"
*y* [center|value] Equivalent for the vertical warp position within the

View file

@ -26,7 +26,7 @@ should (a) allow the first-identified configuration file to supersede any
others, or (b) define rules for merging the information from more than one file.
By default, labwc uses option (a), reading only the first file identified. With
the --merge-config option, the search order is reserved, 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
system-wide configurations, with conflicts favoring the user-specific
alternative.
@ -339,7 +339,7 @@ this is for compatibility with Openbox.
## WINDOW SWITCHER
```
<windowSwitcher preview="yes" outlines="yes" allWorkspaces="no">
<windowSwitcher preview="yes" outlines="yes">
<osd show="yes" style="classic" output="all" thumbnailLabelFormat="%T" />
<fields>
<field content="icon" width="5%" />
@ -349,31 +349,28 @@ this is for compatibility with Openbox.
</windowSwitcher>
```
*<windowSwitcher preview="" outlines="" allWorkspaces="" unshade="" order="">*
*<windowSwitcher preview="" outlines="" unshade="" order="">*
*preview* [yes|no] Preview the contents of the selected window when
switching between windows. Default is yes.
*outlines* [yes|no] Draw an outline around the selected window when
switching between windows. Default is yes.
*allWorkspaces* [yes|no] Show windows regardless of what workspace
they are on. Default no (that is only windows on the current workspace
are shown).
*unshade* [yes|no] Temporarily unshade windows when switching between
them and permanently unshade on the final selection. Default is yes.
*order* [focus|age] The order in which windows are cycled. *focus* cycles by
recent focus history, starting with the previously focused window. *age* cycles
by creation/open order, a stable taskbar-style ordering that doesnt change on
focus. Default is *focus*.
*order* [focus|age] The order in which windows are cycled. *focus*
cycles by recent focus history, starting with the previously focused
window. *age* cycles by creation/open order, a stable taskbar-style
ordering that doesnt change on focus. Default is *focus*.
*<windowSwitcher><osd show="" style="" output="" thumbnailLabelFormat="" />*
*show* [yes|no] Draw the OnScreenDisplay when switching between
windows. Default is yes.
*style* [classic|thumbnail] Configures the style of the OSD.
"classic" displays window information like icons and titles in a vertical list.
"classic" displays window information like icons and titles in a
vertical list.
"thumbnail" shows window thumbnail, icon and title in grids.
*output* [all|focused|cursor] Configures which monitor(s) show the OSD.
@ -382,9 +379,9 @@ this is for compatibility with Openbox.
"cursor" displays the OSD on the monitor containing the mouse pointer.
Default is "all".
*thumbnailLabelFormat* Format to be used for the thumbnail label according to *custom*
field below, only applied when using *<osd style="thumbnail" />*.
Default is "%T".
*thumbnailLabelFormat* Format to be used for the thumbnail label
according to *custom* field below, only applied when using
*<osd style="thumbnail" />*. Default is "%T".
*<windowSwitcher><fields><field content="" width="%">*
Define window switcher fields when using *<osd style="classic" />*.
@ -506,13 +503,13 @@ extending outward from the snapped edge.
*<snapping><range><inner>*++
*<snapping><range><outer>*++
*<snapping><cornerRange>*
If an interactive move ends with the cursor within *inner* or *outer* pixels
of an output edge, the window is snapped to the edge. *inner* edges are edges
with an adjacent output and *outer* edges are edges without an adjacent output.
If it's also within *<cornerRange>* pixels of an output corner, the window is
snapped to the corner instead.
If *inner* and *outer* is 0, snapping is disabled.
Default is 10 for *<range><inner>* and *<range><outer>*, and 50 for *<cornerRange>*.
If an interactive move ends with the cursor within *inner* or *outer*
pixels of an output edge, the window is snapped to the edge. *inner*
edges are edges with an adjacent output and *outer* edges are edges
without an adjacent output. If it's also within *<cornerRange>* pixels
of an output corner, the window is snapped to the corner instead.
If *inner* and *outer* is 0, snapping is disabled. Default is 10 for
*<range><inner>* and *<range><outer>*, and 50 for *<cornerRange>*.
*<snapping><overlay><enabled>* [yes|no]
Show an overlay when snapping to a window to an edge. Default is yes.
@ -533,8 +530,8 @@ extending outward from the snapped edge.
*<snapping><notifyClient>* [always|region|edge|never]
Snapping windows can trigger corresponding tiling events for native
Wayland clients. Clients may use these events to alter their rendering
based on knowledge that some edges of the window are confined to edges of
a snapping region or output. For example, rounded corners may become
based on knowledge that some edges of the window are confined to edges
of a snapping region or output. For example, rounded corners may become
square when tiled, or media players may letter-box or pillar-box video
rather than imposing rigid aspect ratios on windows that will violate
the constraints of window snapping.
@ -574,6 +571,11 @@ extending outward from the snapped edge.
is 1. The number attribute is optional. If the number attribute is
specified, names.name is not required.
*<desktops><initial>*
Define the initial starting workspace. This must match one of the names
defined in <names> or must be an index equal to or lower than <number>.
If not set, the first workspace is used.
*<desktops><popupTime>*
Define the timeout after which to hide the workspace OSD.
A setting of 0 disables the OSD. Default is 1000 ms.
@ -624,15 +626,16 @@ extending outward from the snapped edge.
*<theme><maximizedDecoration>* [titlebar|none]
Specify how server side decorations are shown for maximized windows.
*titlebar* shows titlebar above a maximized window. *none* shows no server
side decorations around a maximized window. Default is titlebar.
*titlebar* shows titlebar above a maximized window. *none* shows no
server side decorations around a maximized window. Default is titlebar.
*<theme><dropShadows>* [yes|no]
Should drop-shadows be rendered behind windows. Default is no.
*<theme><dropShadowsOnTiled>* [yes|no]
Should drop-shadows be rendered behind tiled windows. This won't take
effect if <core><gap> is smaller than window.active.shadow.size in theme.
effect if <core><gap> is smaller than window.active.shadow.size in
theme.
Default is no.
@ -1048,7 +1051,7 @@ Note: To rotate touch events with output rotation, use the libinput
*<tablet><map button="" to="" />*
Pen and pad buttons behave like regular mouse buttons.With mouse
emulation set to "no", which is the default, and if not specified
emulation set to "no", which is the default, and if not specified
otherwise, the first pen button is mapped to the right mouse button,
the second pen button to the middle mouse button and a third pen
button is mapped to the side mouse button.
@ -1067,10 +1070,10 @@ Note: To rotate touch events with output rotation, use the libinput
When using mouse emulation, all pen buttons emulate regular mouse
buttons. The tip, stylus and pad buttons can be mapped to all
available mouse buttons. If not specified otherwise, the tip is
available mouse buttons. If not specified otherwise, the tip is
mapped to left mouse click, the first pen button (Stylus) is mapped
to right mouse button click and the second pen button (Stylus2)
emulates a middle mouse button click. Buttons of a tablet tool mouse
emulates a middle mouse button click. Buttons of a tablet tool mouse
are by default mapped to their (regular) mouse counterparts.
Supported map *buttons* for mouse emulation are:
@ -1180,11 +1183,12 @@ Note: To rotate touch events with output rotation, use the libinput
a tap immediately followed by a finger down as the start of a drag.
*<libinput><device><dragLock>* [yes|no|timeout]
Enable or disable drag lock for this category. Drag lock ignores a temporary
release of a finger during tap-and-dragging.
Enable or disable drag lock for this category. Drag lock ignores a
temporary release of a finger during tap-and-dragging.
*timeout* also enables drag lock, but with a timeout: if your fingers are
released for a certain amount of time, the drag gesture is cancelled.
*timeout* also enables drag lock, but with a timeout: if your fingers
are released for a certain amount of time, the drag gesture is
cancelled.
In libinput < 1.27, the behavior of *yes* is equivalent to *timeout*.
*<libinput><device><threeFingerDrag>* [yes|no|3|4]

View file

@ -308,8 +308,8 @@ all are supported.
See below for details.
*osd.window-switcher.style-classic.width*
Width of window switcher in pixels. Width can also be a percentage of the
monitor width by adding '%' as suffix (e.g. 70%). Default is 600.
Width of window switcher in pixels. Width can also be a percentage of
the monitor width by adding '%' as suffix (e.g. 70%). Default is 600.
*osd.window-switcher.style-classic.padding*
Padding of window switcher in pixels. This is the space between the
@ -337,16 +337,17 @@ all are supported.
*osd.window-switcher.style-classic.item.icon.size*
Size of the icon in window switcher, in pixels.
If not set, the font size derived from <theme><font place="OnScreenDisplay">
is used.
If not set, the font size derived from
<theme><font place="OnScreenDisplay"> is used.
*osd.window-switcher.style-thumbnail*
Theme for window switcher when using <windowSwitcher style="thumbnail" />.
See below for details.
Theme for window switcher when using
<windowSwitcher style="thumbnail" />. See below for details.
*osd.window-switcher.style-thumbnail.width.max*
Maximum width of window switcher in pixels. Width can also be a percentage of
the monitor width by adding '%' as suffix (e.g. 70%). Default is 80%.
Maximum width of window switcher in pixels. Width can also be a
percentage of the monitor width by adding '%' as suffix (e.g. 70%).
Default is 80%.
*osd.window-switcher.style-thumbnail.padding*
Padding of window switcher in pixels. This is the space between the
@ -359,8 +360,8 @@ all are supported.
Height of window switcher items in pixels. Default is 250.
*osd.window-switcher.style-thumbnail.item.padding*
Padding of window switcher items in pixels. This is the space between the
border around selected items and window thumbnail. Default is 2.
Padding of window switcher items in pixels. This is the space between
the border around selected items and window thumbnail. Default is 2.
*osd.window-switcher.style-thumbnail.item.active.border.width*
Border width of selected window switcher items in pixels. Default is 2.

View file

@ -77,7 +77,7 @@
</font>
</theme>
<windowSwitcher preview="yes" outlines="yes" allWorkspaces="no" unshade="yes">
<windowSwitcher preview="yes" outlines="yes" unshade="yes">
<osd show="yes" style="classic" output="all" thumbnailLabelFormat="%T" />
<fields>
<field content="icon" width="5%" />
@ -98,7 +98,7 @@
Some contents are fixed-length and others are variable-length.
See "man 5 labwc-config" for details.
<windowSwitcher preview="no" outlines="no" allWorkspaces="yes">
<windowSwitcher preview="no" outlines="no">
<osd show="yes" />
<fields>
<field content="workspace" width="5%" />
@ -119,7 +119,7 @@
then workspace name, then identifier/app-id, then the window title.
It uses 100% of OSD window width.
<windowSwitcher preview="no" outlines="no" allWorkspaces="yes">
<windowSwitcher preview="no" outlines="no">
<osd show="yes" />
<fields>
<field content="custom" format="foobar %b %3s %-10o %-20W %-10i %t" width="100%" />
@ -175,6 +175,7 @@
Workspaces can be configured like this:
<desktops>
<popupTime>1000</popupTime>
<initial>Workspace 1</initial>
<names>
<name>Workspace 1</name>
<name>Workspace 2</name>
@ -630,8 +631,8 @@
# must only apply to the first instance of the window with that
# particular 'identifier' or 'title'.
# - Matching is case-insensitive and is performed using shell wildcard
# patterns (see glob(7)) so '\*' (not between brackets) matches any string
# and '?' matches any single character.
# patterns (see glob(7)) so '\*' (not between brackets) matches any
# string and '?' matches any single character.
<windowRules>
<windowRule identifier="*"><action name="Maximize"/></windowRule>

View file

@ -1,4 +1,5 @@
# Example shutdown file
# This file is executed as a shell script when labwc is preparing to terminate itself.
# This file is executed as a shell script when labwc is preparing to terminate
# itself.
# For further details see labwc-config(5).

View file

@ -168,6 +168,7 @@ struct rcxml {
struct {
int popuptime;
int min_nr_workspaces;
char *initial_workspace_name;
char *prefix;
struct wl_list workspaces; /* struct workspace.link */
} workspace_config;
@ -177,16 +178,18 @@ struct rcxml {
/* Window Switcher */
struct {
bool show;
bool preview;
bool outlines;
bool unshade;
enum lab_view_criteria criteria;
struct wl_list fields; /* struct window_switcher_field.link */
enum cycle_osd_style style;
enum cycle_osd_output_criteria output_criteria;
char *thumbnail_label_format;
enum window_switcher_order order;
enum cycle_workspace_filter workspace_filter; /* deprecated */
struct {
bool show;
enum cycle_osd_style style;
enum cycle_output_filter output_filter;
char *thumbnail_label_format;
struct wl_list fields; /* struct cycle_osd_field.link */
} osd;
} window_switcher;
struct wl_list window_rules; /* struct window_rule.link */

View file

@ -117,10 +117,20 @@ enum cycle_osd_style {
CYCLE_OSD_STYLE_THUMBNAIL,
};
enum cycle_osd_output_criteria {
CYCLE_OSD_OUTPUT_ALL,
CYCLE_OSD_OUTPUT_CURSOR,
CYCLE_OSD_OUTPUT_FOCUSED,
enum cycle_workspace_filter {
CYCLE_WORKSPACE_ALL,
CYCLE_WORKSPACE_CURRENT,
};
enum cycle_output_filter {
CYCLE_OUTPUT_ALL,
CYCLE_OUTPUT_CURSOR,
CYCLE_OUTPUT_FOCUSED,
};
enum cycle_app_id_filter {
CYCLE_APP_ID_ALL,
CYCLE_APP_ID_CURRENT,
};
#endif /* LABWC_CONFIG_TYPES_H */

View file

@ -4,6 +4,7 @@
#include <stdbool.h>
#include <wayland-server-core.h>
#include "config/types.h"
struct output;
@ -39,7 +40,13 @@ struct cycle_osd_field {
enum cycle_osd_field_content content;
int width;
char *format;
struct wl_list link; /* struct rcxml.window_switcher.fields */
struct wl_list link; /* struct rcxml.window_switcher.osd.fields */
};
struct cycle_filter {
enum cycle_workspace_filter workspace;
enum cycle_output_filter output;
enum cycle_app_id_filter app_id;
};
struct buf;
@ -48,7 +55,8 @@ struct server;
struct wlr_scene_node;
/* Begin window switcher */
void cycle_begin(struct server *server, enum lab_cycle_dir direction);
void cycle_begin(struct server *server, enum lab_cycle_dir direction,
struct cycle_filter filter);
/* Cycle the selected view in the window switcher */
void cycle_step(struct server *server, enum lab_cycle_dir direction);

View file

@ -5,6 +5,7 @@
#include <wlr/util/box.h>
#include <wlr/util/log.h>
#include "common/set.h"
#include "cycle.h"
#include "input/cursor.h"
#include "overlay.h"
@ -310,6 +311,7 @@ struct server {
struct wlr_scene_node *preview_node;
struct wlr_scene_node *preview_dummy;
struct lab_scene_rect *preview_outline;
struct cycle_filter filter;
} cycle;
struct theme *theme;

View file

@ -1,7 +1,7 @@
project(
'labwc',
'c',
version: '0.9.2',
version: '0.9.3',
license: 'GPL-2.0-only',
meson_version: '>=0.59.0',
default_options: [

View file

@ -366,6 +366,44 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
goto cleanup;
}
break;
case ACTION_TYPE_NEXT_WINDOW:
case ACTION_TYPE_PREVIOUS_WINDOW:
if (!strcasecmp(argument, "workspace")) {
if (!strcasecmp(content, "all")) {
action_arg_add_int(action, argument, CYCLE_WORKSPACE_ALL);
} else if (!strcasecmp(content, "current")) {
action_arg_add_int(action, argument, CYCLE_WORKSPACE_CURRENT);
} else {
wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
action_names[action->type], argument, content);
}
goto cleanup;
}
if (!strcasecmp(argument, "output")) {
if (!strcasecmp(content, "all")) {
action_arg_add_int(action, argument, CYCLE_OUTPUT_ALL);
} else if (!strcasecmp(content, "cursor")) {
action_arg_add_int(action, argument, CYCLE_OUTPUT_CURSOR);
} else if (!strcasecmp(content, "focused")) {
action_arg_add_int(action, argument, CYCLE_OUTPUT_FOCUSED);
} else {
wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
action_names[action->type], argument, content);
}
goto cleanup;
}
if (!strcasecmp(argument, "identifier")) {
if (!strcasecmp(content, "all")) {
action_arg_add_int(action, argument, CYCLE_APP_ID_ALL);
} else if (!strcasecmp(content, "current")) {
action_arg_add_int(action, argument, CYCLE_APP_ID_CURRENT);
} else {
wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
action_names[action->type], argument, content);
}
goto cleanup;
}
break;
case ACTION_TYPE_SHOW_MENU:
if (!strcmp(argument, "menu")) {
action_arg_add_str(action, argument, content);
@ -1126,19 +1164,24 @@ run_action(struct view *view, struct server *server, struct action *action,
}
break;
case ACTION_TYPE_NEXT_WINDOW:
case ACTION_TYPE_PREVIOUS_WINDOW: {
enum lab_cycle_dir dir = (action->type == ACTION_TYPE_NEXT_WINDOW) ?
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),
};
if (server->input_mode == LAB_INPUT_STATE_CYCLE) {
cycle_step(server, LAB_CYCLE_DIR_FORWARD);
cycle_step(server, dir);
} else {
cycle_begin(server, LAB_CYCLE_DIR_FORWARD);
}
break;
case ACTION_TYPE_PREVIOUS_WINDOW:
if (server->input_mode == LAB_INPUT_STATE_CYCLE) {
cycle_step(server, LAB_CYCLE_DIR_BACKWARD);
} else {
cycle_begin(server, LAB_CYCLE_DIR_BACKWARD);
cycle_begin(server, dir, filter);
}
break;
}
case ACTION_TYPE_RECONFIGURE:
kill(getpid(), SIGHUP);
break;

View file

@ -324,7 +324,7 @@ static void
clear_window_switcher_fields(void)
{
struct cycle_osd_field *field, *field_tmp;
wl_list_for_each_safe(field, field_tmp, &rc.window_switcher.fields, link) {
wl_list_for_each_safe(field, field_tmp, &rc.window_switcher.osd.fields, link) {
wl_list_remove(&field->link);
cycle_osd_field_free(field);
}
@ -334,7 +334,7 @@ static void
fill_window_switcher_field(xmlNode *node)
{
struct cycle_osd_field *field = znew(*field);
wl_list_append(&rc.window_switcher.fields, &field->link);
wl_list_append(&rc.window_switcher.osd.fields, &field->link);
xmlNode *child;
char *key, *content;
@ -684,6 +684,10 @@ get_send_events_mode(const char *s)
goto err;
}
if (!strcasecmp(s, "disabledOnExternalMouse")) {
return LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
}
int ret = parse_bool(s, -1);
if (ret >= 0) {
return ret
@ -691,10 +695,6 @@ get_send_events_mode(const char *s)
: LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
}
if (!strcasecmp(s, "disabledOnExternalMouse")) {
return LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
}
err:
wlr_log(WLR_INFO, "Not a recognised send events mode");
return -1;
@ -1071,7 +1071,7 @@ entry(xmlNode *node, char *nodename, char *content)
} else if (!strcasecmp(nodename, "prefix.desktops")) {
xstrdup_replace(rc.workspace_config.prefix, content);
} else if (!strcasecmp(nodename, "thumbnailLabelFormat.osd.windowSwitcher")) {
xstrdup_replace(rc.window_switcher.thumbnail_label_format, content);
xstrdup_replace(rc.window_switcher.osd.thumbnail_label_format, content);
} else if (!lab_xml_node_is_leaf(node)) {
/* parse children of nested nodes other than above */
@ -1219,25 +1219,25 @@ entry(xmlNode *node, char *nodename, char *content)
* thumnailLabelFormat is handled above to allow for an empty value
*/
} else if (!strcasecmp(nodename, "show.osd.windowSwitcher")) {
set_bool(content, &rc.window_switcher.show);
set_bool(content, &rc.window_switcher.osd.show);
} else if (!strcasecmp(nodename, "style.osd.windowSwitcher")) {
if (!strcasecmp(content, "classic")) {
rc.window_switcher.style = CYCLE_OSD_STYLE_CLASSIC;
rc.window_switcher.osd.style = CYCLE_OSD_STYLE_CLASSIC;
} else if (!strcasecmp(content, "thumbnail")) {
rc.window_switcher.style = CYCLE_OSD_STYLE_THUMBNAIL;
rc.window_switcher.osd.style = CYCLE_OSD_STYLE_THUMBNAIL;
} else {
wlr_log(WLR_ERROR, "Invalid windowSwitcher style %s: "
wlr_log(WLR_ERROR, "Invalid windowSwitcher style '%s': "
"should be one of classic|thumbnail", content);
}
} else if (!strcasecmp(nodename, "output.osd.windowSwitcher")) {
if (!strcasecmp(content, "all")) {
rc.window_switcher.output_criteria = CYCLE_OSD_OUTPUT_ALL;
rc.window_switcher.osd.output_filter = CYCLE_OUTPUT_ALL;
} else if (!strcasecmp(content, "cursor")) {
rc.window_switcher.output_criteria = CYCLE_OSD_OUTPUT_CURSOR;
rc.window_switcher.osd.output_filter = CYCLE_OUTPUT_CURSOR;
} else if (!strcasecmp(content, "focused")) {
rc.window_switcher.output_criteria = CYCLE_OSD_OUTPUT_FOCUSED;
rc.window_switcher.osd.output_filter = CYCLE_OUTPUT_FOCUSED;
} else {
wlr_log(WLR_ERROR, "Invalid windowSwitcher output %s: "
wlr_log(WLR_ERROR, "Invalid windowSwitcher output '%s': "
"should be one of all|focused|cursor", content);
}
} else if (!strcasecmp(nodename, "order.windowSwitcher")) {
@ -1246,20 +1246,20 @@ entry(xmlNode *node, char *nodename, char *content)
} else if (!strcasecmp(content, "age")) {
rc.window_switcher.order = WINDOW_SWITCHER_ORDER_AGE;
} else {
wlr_log(WLR_ERROR, "Invalid windowSwitcher order %s: "
wlr_log(WLR_ERROR, "Invalid windowSwitcher order '%s': "
"should be one of focus|age", content);
}
/* The following two are for backward compatibility only. */
} else if (!strcasecmp(nodename, "show.windowSwitcher")) {
set_bool(content, &rc.window_switcher.show);
set_bool(content, &rc.window_switcher.osd.show);
wlr_log(WLR_ERROR, "<windowSwitcher show=\"\" /> is deprecated."
" Use <windowSwitcher><osd show=\"\" />");
} else if (!strcasecmp(nodename, "style.windowSwitcher")) {
if (!strcasecmp(content, "classic")) {
rc.window_switcher.style = CYCLE_OSD_STYLE_CLASSIC;
rc.window_switcher.osd.style = CYCLE_OSD_STYLE_CLASSIC;
} else if (!strcasecmp(content, "thumbnail")) {
rc.window_switcher.style = CYCLE_OSD_STYLE_THUMBNAIL;
rc.window_switcher.osd.style = CYCLE_OSD_STYLE_THUMBNAIL;
}
wlr_log(WLR_ERROR, "<windowSwitcher style=\"\" /> is deprecated."
" Use <windowSwitcher><osd style=\"\" />");
@ -1269,10 +1269,16 @@ entry(xmlNode *node, char *nodename, char *content)
} else if (!strcasecmp(nodename, "outlines.windowSwitcher")) {
set_bool(content, &rc.window_switcher.outlines);
} else if (!strcasecmp(nodename, "allWorkspaces.windowSwitcher")) {
if (parse_bool(content, -1) == true) {
rc.window_switcher.criteria &=
~LAB_VIEW_CRITERIA_CURRENT_WORKSPACE;
int ret = parse_bool(content, -1);
if (ret < 0) {
wlr_log(WLR_ERROR, "Invalid value for <windowSwitcher"
" allWorkspaces=\"\">: '%s'", content);
} else {
rc.window_switcher.workspace_filter = ret ?
CYCLE_WORKSPACE_ALL : CYCLE_WORKSPACE_CURRENT;
}
wlr_log(WLR_ERROR, "<windowSwitcher allWorkspaces=\"\" /> is deprecated."
" Use <action name=\"NextWindow\" workspace=\"\"> instead.");
} else if (!strcasecmp(nodename, "unshade.windowSwitcher")) {
set_bool(content, &rc.window_switcher.unshade);
@ -1282,7 +1288,7 @@ entry(xmlNode *node, char *nodename, char *content)
/* The following three are for backward compatibility only */
} else if (!strcasecmp(nodename, "show.windowSwitcher.core")) {
set_bool(content, &rc.window_switcher.show);
set_bool(content, &rc.window_switcher.osd.show);
} else if (!strcasecmp(nodename, "preview.windowSwitcher.core")) {
set_bool(content, &rc.window_switcher.preview);
} else if (!strcasecmp(nodename, "outlines.windowSwitcher.core")) {
@ -1290,7 +1296,7 @@ entry(xmlNode *node, char *nodename, char *content)
/* The following three are for backward compatibility only */
} else if (!strcasecmp(nodename, "cycleViewOSD.core")) {
set_bool(content, &rc.window_switcher.show);
set_bool(content, &rc.window_switcher.osd.show);
wlr_log(WLR_ERROR, "<cycleViewOSD> is deprecated."
" Use <windowSwitcher show=\"\" />");
} else if (!strcasecmp(nodename, "cycleViewPreview.core")) {
@ -1308,6 +1314,8 @@ entry(xmlNode *node, char *nodename, char *content)
wl_list_append(&rc.workspace_config.workspaces, &workspace->link);
} else if (!strcasecmp(nodename, "popupTime.desktops")) {
rc.workspace_config.popuptime = atoi(content);
} else if (!strcasecmp(nodename, "initial.desktops")) {
xstrdup_replace(rc.workspace_config.initial_workspace_name, content);
} else if (!strcasecmp(nodename, "number.desktops")) {
rc.workspace_config.min_nr_workspaces = MAX(1, atoi(content));
} else if (!strcasecmp(nodename, "popupShow.resize")) {
@ -1419,7 +1427,7 @@ rcxml_init(void)
wl_list_init(&rc.libinput_categories);
wl_list_init(&rc.workspace_config.workspaces);
wl_list_init(&rc.regions);
wl_list_init(&rc.window_switcher.fields);
wl_list_init(&rc.window_switcher.osd.fields);
wl_list_init(&rc.window_rules);
wl_list_init(&rc.touch_configs);
}
@ -1484,16 +1492,14 @@ rcxml_init(void)
rc.snap_top_maximize = true;
rc.snap_tiling_events_mode = LAB_TILING_EVENTS_ALWAYS;
rc.window_switcher.show = true;
rc.window_switcher.style = CYCLE_OSD_STYLE_CLASSIC;
rc.window_switcher.output_criteria = CYCLE_OSD_OUTPUT_ALL;
rc.window_switcher.thumbnail_label_format = xstrdup("%T");
rc.window_switcher.osd.show = true;
rc.window_switcher.osd.style = CYCLE_OSD_STYLE_CLASSIC;
rc.window_switcher.osd.output_filter = CYCLE_OUTPUT_ALL;
rc.window_switcher.osd.thumbnail_label_format = xstrdup("%T");
rc.window_switcher.preview = true;
rc.window_switcher.outlines = true;
rc.window_switcher.unshade = true;
rc.window_switcher.criteria = LAB_VIEW_CRITERIA_CURRENT_WORKSPACE
| LAB_VIEW_CRITERIA_ROOT_TOPLEVEL
| LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER;
rc.window_switcher.workspace_filter = CYCLE_WORKSPACE_CURRENT;
rc.window_switcher.order = WINDOW_SWITCHER_ORDER_FOCUS;
rc.resize_indicator = LAB_RESIZE_INDICATOR_NEVER;
@ -1673,7 +1679,7 @@ load_default_window_switcher_fields(void)
struct cycle_osd_field *field = znew(*field);
field->content = fields[i].content;
field->width = fields[i].width;
wl_list_append(&rc.window_switcher.fields, &field->link);
wl_list_append(&rc.window_switcher.osd.fields, &field->link);
}
}
@ -1794,7 +1800,7 @@ post_processing(void)
if (rc.workspace_config.popuptime == INT_MIN) {
rc.workspace_config.popuptime = 1000;
}
if (!wl_list_length(&rc.window_switcher.fields)) {
if (!wl_list_length(&rc.window_switcher.osd.fields)) {
wlr_log(WLR_INFO, "load default window switcher fields");
load_default_window_switcher_fields();
}
@ -1888,7 +1894,7 @@ validate(void)
/* OSD fields */
int field_width_sum = 0;
struct cycle_osd_field *field, *field_tmp;
wl_list_for_each_safe(field, field_tmp, &rc.window_switcher.fields, link) {
wl_list_for_each_safe(field, field_tmp, &rc.window_switcher.osd.fields, link) {
field_width_sum += field->width;
if (!cycle_osd_field_is_valid(field) || field_width_sum > 100) {
wlr_log(WLR_ERROR, "Deleting invalid window switcher field %p", field);
@ -1963,8 +1969,9 @@ rcxml_finish(void)
zfree(rc.icon_theme_name);
zfree(rc.fallback_app_icon_name);
zfree(rc.workspace_config.prefix);
zfree(rc.workspace_config.initial_workspace_name);
zfree(rc.tablet.output_name);
zfree(rc.window_switcher.thumbnail_label_format);
zfree(rc.window_switcher.osd.thumbnail_label_format);
clear_title_layout();

View file

@ -17,7 +17,7 @@
#include "theme.h"
#include "view.h"
static bool init_cycle(struct server *server);
static bool init_cycle(struct server *server, struct cycle_filter filter);
static void update_cycle(struct server *server);
static void destroy_cycle(struct server *server);
@ -93,9 +93,10 @@ cycle_reinitialize(struct server *server)
struct view *selected_view = cycle->selected_view;
struct view *selected_view_prev =
get_next_selected_view(server, LAB_CYCLE_DIR_BACKWARD);
struct cycle_filter filter = cycle->filter;
destroy_cycle(server);
if (init_cycle(server)) {
if (init_cycle(server, filter)) {
/*
* Preserve the selected view (or its previous view) if it's
* still in the cycle list
@ -152,13 +153,14 @@ restore_preview_node(struct server *server)
}
void
cycle_begin(struct server *server, enum lab_cycle_dir direction)
cycle_begin(struct server *server, enum lab_cycle_dir direction,
struct cycle_filter filter)
{
if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
return;
}
if (!init_cycle(server)) {
if (!init_cycle(server, filter)) {
return;
}
@ -257,7 +259,7 @@ preview_selected_view(struct view *view)
static struct cycle_osd_impl *
get_osd_impl(void)
{
switch (rc.window_switcher.style) {
switch (rc.window_switcher.osd.style) {
case CYCLE_OSD_STYLE_CLASSIC:
return &cycle_osd_classic_impl;
case CYCLE_OSD_STYLE_THUMBNAIL:
@ -266,14 +268,36 @@ get_osd_impl(void)
return NULL;
}
static void
create_osd_on_output(struct output *output)
static uint64_t
get_outputs_by_filter(struct server *server,
enum cycle_output_filter output_filter)
{
if (!output_is_usable(output)) {
return;
struct output *output = NULL;
switch (output_filter) {
case CYCLE_OUTPUT_ALL:
break;
case CYCLE_OUTPUT_CURSOR:
output = output_nearest_to_cursor(server);
break;
case CYCLE_OUTPUT_FOCUSED: {
struct view *view = server->active_view;
if (view && output_is_usable(view->output)) {
output = view->output;
} else {
/* Fallback to pointer */
output = output_nearest_to_cursor(server);
}
break;
}
}
if (output) {
return output->id_bit;
} else {
/* bitmask for all outputs */
return UINT64_MAX;
}
get_osd_impl()->create(output);
assert(output->cycle_osd.tree);
}
static void
@ -292,10 +316,32 @@ insert_view_ordered_by_age(struct wl_list *views, struct view *new_view)
/* Return false on failure */
static bool
init_cycle(struct server *server)
init_cycle(struct server *server, struct cycle_filter filter)
{
enum lab_view_criteria criteria =
LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER
| LAB_VIEW_CRITERIA_ROOT_TOPLEVEL;
if (filter.workspace == CYCLE_WORKSPACE_CURRENT) {
criteria |= LAB_VIEW_CRITERIA_CURRENT_WORKSPACE;
}
uint64_t cycle_outputs =
get_outputs_by_filter(server, filter.output);
const char *cycle_app_id = NULL;
if (filter.app_id == CYCLE_APP_ID_CURRENT && server->active_view) {
cycle_app_id = server->active_view->app_id;
}
struct view *view;
for_each_view(view, &server->views, rc.window_switcher.criteria) {
for_each_view(view, &server->views, criteria) {
if (!(cycle_outputs & view->output->id_bit)) {
continue;
}
if (cycle_app_id && strcmp(view->app_id, cycle_app_id) != 0) {
continue;
}
if (rc.window_switcher.order == WINDOW_SWITCHER_ORDER_AGE) {
insert_view_ordered_by_age(&server->cycle.views, view);
} else {
@ -306,31 +352,22 @@ init_cycle(struct server *server)
wlr_log(WLR_DEBUG, "no views to switch between");
return false;
}
server->cycle.filter = filter;
if (rc.window_switcher.show) {
if (rc.window_switcher.osd.show) {
/* Create OSD */
switch (rc.window_switcher.output_criteria) {
case CYCLE_OSD_OUTPUT_ALL: {
struct output *output;
wl_list_for_each(output, &server->outputs, link) {
create_osd_on_output(output);
uint64_t osd_outputs = get_outputs_by_filter(server,
rc.window_switcher.osd.output_filter);
struct output *output;
wl_list_for_each(output, &server->outputs, link) {
if (!(osd_outputs & output->id_bit)) {
continue;
}
break;
}
case CYCLE_OSD_OUTPUT_CURSOR:
create_osd_on_output(output_nearest_to_cursor(server));
break;
case CYCLE_OSD_OUTPUT_FOCUSED: {
struct output *output;
if (server->active_view) {
output = server->active_view->output;
} else {
/* Fallback to pointer, if there is no active_view */
output = output_nearest_to_cursor(server);
if (!output_is_usable(output)) {
continue;
}
create_osd_on_output(output);
break;
}
get_osd_impl()->create(output);
assert(output->cycle_osd.tree);
}
}
@ -342,7 +379,7 @@ update_cycle(struct server *server)
{
struct cycle_state *cycle = &server->cycle;
if (rc.window_switcher.show) {
if (rc.window_switcher.osd.show) {
struct output *output;
wl_list_for_each(output, &server->outputs, link) {
if (output->cycle_osd.tree) {
@ -394,4 +431,5 @@ destroy_cycle(struct server *server)
}
server->cycle.selected_view = NULL;
server->cycle.filter = (struct cycle_filter){0};
}

View file

@ -36,7 +36,7 @@ create_fields_scene(struct server *server, struct view *view,
&theme->osd_window_switcher_classic;
struct cycle_osd_field *field;
wl_list_for_each(field, &rc.window_switcher.fields, link) {
wl_list_for_each(field, &rc.window_switcher.osd.fields, link) {
int field_width = field_widths_sum * field->width / 100.0;
struct wlr_scene_node *node = NULL;
int height = -1;
@ -144,7 +144,7 @@ cycle_osd_classic_create(struct output *output)
y += switcher_theme->item_height;
}
int nr_fields = wl_list_length(&rc.window_switcher.fields);
int nr_fields = wl_list_length(&rc.window_switcher.osd.fields);
/* This is the width of the area available for text fields */
int field_widths_sum = w - 2 * padding

View file

@ -103,7 +103,7 @@ create_label(struct wlr_scene_tree *parent, struct view *view,
{
struct buf buf = BUF_INIT;
cycle_osd_field_set_custom(&buf, view,
rc.window_switcher.thumbnail_label_format);
rc.window_switcher.osd.thumbnail_label_format);
struct scaled_font_buffer *buffer =
scaled_font_buffer_create(parent);
scaled_font_buffer_update(buffer, buf.data,

View file

@ -182,6 +182,33 @@ _osd_update(struct server *server)
}
}
static struct workspace *
workspace_find_by_name(struct server *server, const char *name)
{
struct workspace *workspace;
/* by index */
size_t parsed_index = parse_workspace_index(name);
if (parsed_index) {
size_t index = 0;
wl_list_for_each(workspace, &server->workspaces.all, link) {
if (parsed_index == ++index) {
return workspace;
}
}
}
/* by name */
wl_list_for_each(workspace, &server->workspaces.all, link) {
if (!strcmp(workspace->name, name)) {
return workspace;
}
}
wlr_log(WLR_ERROR, "Workspace '%s' not found", name);
return NULL;
}
/* cosmic workspace handlers */
static void
handle_cosmic_workspace_activate(struct wl_listener *listener, void *data)
@ -209,18 +236,11 @@ add_workspace(struct server *server, const char *name)
workspace->name = xstrdup(name);
workspace->tree = wlr_scene_tree_create(server->view_tree);
wl_list_append(&server->workspaces.all, &workspace->link);
if (!server->workspaces.current) {
server->workspaces.current = workspace;
} else {
wlr_scene_node_set_enabled(&workspace->tree->node, false);
}
bool active = server->workspaces.current == workspace;
wlr_scene_node_set_enabled(&workspace->tree->node, false);
/* cosmic */
workspace->cosmic_workspace = lab_cosmic_workspace_create(server->workspaces.cosmic_group);
lab_cosmic_workspace_set_name(workspace->cosmic_workspace, name);
lab_cosmic_workspace_set_active(workspace->cosmic_workspace, active);
workspace->on_cosmic.activate.notify = handle_cosmic_workspace_activate;
wl_signal_add(&workspace->cosmic_workspace->events.activate,
@ -231,7 +251,6 @@ add_workspace(struct server *server, const char *name)
server->workspaces.ext_manager, /*id*/ NULL);
lab_ext_workspace_assign_to_group(workspace->ext_workspace, server->workspaces.ext_group);
lab_ext_workspace_set_name(workspace->ext_workspace, name);
lab_ext_workspace_set_active(workspace->ext_workspace, active);
workspace->on_ext.activate.notify = handle_ext_workspace_activate;
wl_signal_add(&workspace->ext_workspace->events.activate,
@ -398,6 +417,27 @@ workspaces_init(struct server *server)
wl_list_for_each(conf, &rc.workspace_config.workspaces, link) {
add_workspace(server, conf->name);
}
/*
* After adding workspaces, check if there is an initial workspace
* selected and set that as the initial workspace.
*/
char *initial_name = rc.workspace_config.initial_workspace_name;
struct workspace *initial = NULL;
struct workspace *first = wl_container_of(
server->workspaces.all.next, first, link);
if (initial_name) {
initial = workspace_find_by_name(server, initial_name);
}
if (!initial) {
initial = first;
}
server->workspaces.current = initial;
wlr_scene_node_set_enabled(&initial->tree->node, true);
lab_cosmic_workspace_set_active(initial->cosmic_workspace, true);
lab_ext_workspace_set_active(initial->ext_workspace, true);
}
/*
@ -507,21 +547,13 @@ workspaces_find(struct workspace *anchor, const char *name, bool wrap)
if (!name) {
return NULL;
}
size_t index = 0;
struct workspace *target;
size_t wants_index = parse_workspace_index(name);
struct wl_list *workspaces = &anchor->server->workspaces.all;
struct server *server = anchor->server;
struct wl_list *workspaces = &server->workspaces.all;
if (wants_index) {
wl_list_for_each(target, workspaces, link) {
if (wants_index == ++index) {
return target;
}
}
} else if (!strcasecmp(name, "current")) {
if (!strcasecmp(name, "current")) {
return anchor;
} else if (!strcasecmp(name, "last")) {
return anchor->server->workspaces.last;
return server->workspaces.last;
} else if (!strcasecmp(name, "left")) {
return get_prev(anchor, workspaces, wrap);
} else if (!strcasecmp(name, "right")) {
@ -530,15 +562,8 @@ workspaces_find(struct workspace *anchor, const char *name, bool wrap)
return get_prev_occupied(anchor, workspaces, wrap);
} else if (!strcasecmp(name, "right-occupied")) {
return get_next_occupied(anchor, workspaces, wrap);
} else {
wl_list_for_each(target, workspaces, link) {
if (!strcasecmp(target->name, name)) {
return target;
}
}
}
wlr_log(WLR_ERROR, "Workspace '%s' not found", name);
return NULL;
return workspace_find_by_name(server, name);
}
static void