mirror of
https://github.com/labwc/labwc.git
synced 2026-02-07 04:07:42 -05:00
Merge remote-tracking branch 'upstream/master' into chase/0.20
This commit is contained in:
commit
c93c3ad6f4
49 changed files with 1552 additions and 1040 deletions
103
NEWS.md
103
NEWS.md
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"`
|
||||
|
|
|
|||
|
|
@ -140,4 +140,3 @@ labnag \\
|
|||
--button-border-size 2\\
|
||||
-t 60
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -51,9 +51,13 @@ Actions are used in menus and keyboard/mouse bindings.
|
|||
another window or screen edge. If set to "no", only move to
|
||||
the next screen edge. Default is yes.
|
||||
|
||||
*<action name="Resize" />*
|
||||
*<action name="Resize" direction="value" />*
|
||||
Begin interactive resize of window under cursor.
|
||||
|
||||
*direction* [up|down|left|right|up-left|up-right|down-left|down-right]
|
||||
Edge or corner from which to start resizing. If this is not provided,
|
||||
the direction is inferred from the cursor position.
|
||||
|
||||
*<action name="ResizeRelative" left="" right="" top="" bottom="" />*
|
||||
Resize window relative to its current size. Values of left, right,
|
||||
top or bottom tell how much to resize on that edge of window,
|
||||
|
|
@ -121,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.
|
||||
|
|
@ -375,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
|
||||
|
|
|
|||
|
|
@ -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,37 +349,39 @@ this is for compatibility with Openbox.
|
|||
</windowSwitcher>
|
||||
```
|
||||
|
||||
*<windowSwitcher preview="" outlines="" allWorkspaces="" unshade="">*
|
||||
*<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.
|
||||
|
||||
*<osd show="" style="" output="" thumbnailLabelFormat="" />*
|
||||
*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 doesn’t 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|pointer|keyboard] Configures which monitor(s) show the OSD.
|
||||
*output* [all|focused|cursor] Configures which monitor(s) show the OSD.
|
||||
"all" displays the OSD on all monitors.
|
||||
"pointer" displays the OSD on the monitor containing the mouse pointer.
|
||||
"keyboard" displays the OSD on the monitor with keyboard focus.
|
||||
"focused" displays the OSD on the monitor with keyboard focus.
|
||||
"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" />*.
|
||||
|
|
@ -498,13 +500,16 @@ activated with SnapToEdge actions or, optionally, by dragging windows to the
|
|||
edges of an output. Edge snapping causes a window to occupy half of its output,
|
||||
extending outward from the snapped edge.
|
||||
|
||||
*<snapping><range>*++
|
||||
*<snapping><range><inner>*++
|
||||
*<snapping><range><outer>*++
|
||||
*<snapping><cornerRange>*
|
||||
If an interactive move ends with the cursor within *<range>* pixels of an
|
||||
output edge, the window is snapped to the edge. If it's also within
|
||||
*<cornerRange>* pixels of an output corner, the window is snapped to the
|
||||
corner instead. A *<range>* of 0 disables snapping.
|
||||
Default is 10 for *<range>* 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.
|
||||
|
|
@ -525,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.
|
||||
|
|
@ -566,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.
|
||||
|
|
@ -616,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.
|
||||
|
||||
|
|
@ -700,6 +711,45 @@ extending outward from the snapped edge.
|
|||
invisible zones just beyond the window that serve as click targets for
|
||||
mouse actions. Default is 8.
|
||||
|
||||
# INPUT CONFIGURATION
|
||||
|
||||
This section describes configuration of input devices including:
|
||||
|
||||
- Keyboards
|
||||
- Mice
|
||||
- Touchpads (sometimes referred to as laptop trackpads)
|
||||
- Touchscreens
|
||||
- Tablets
|
||||
- Tablet tools (like stylus pens)
|
||||
|
||||
It aims to clarify related terminology and separation of concerns.
|
||||
|
||||
Keyboards are configured in the *<keyboard>* section below and are simple in
|
||||
this regard.
|
||||
|
||||
Touchpads and mice are harder. They are both considered to be *pointer* devices
|
||||
by the compositor, and can be configured in the *<mouse>* and *<libinput>*
|
||||
sections. Any setting that is supported by libinput is configured in the
|
||||
*<libinput>* section, and anything else is in *<mouse>*. Touchpad devices can
|
||||
generate gesture events, like swipe and pinch. There are some related settings
|
||||
(e.g. *threeFingerDrag* and *twoFingerScroll*) in the *<libinput>* section.
|
||||
|
||||
In the Wayland Compositor domain, events associated with touchscreens are
|
||||
sometimes simply referred to as *touch* events. Touchscreens can be configured
|
||||
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
|
||||
client (application) for any interpretation of gestures.
|
||||
|
||||
Tablets are considered special by libinput although in the eyes of the Wayland
|
||||
protocol they are merely a *touch* capability. Tablets and associated tablet
|
||||
tools are configured in the *<tablet>*, *<tablettool>* and *<libinput>*
|
||||
sections. Note that the term *tablet* in libinput (and labwc) refers to graphics
|
||||
tablets only (e.g. Wacom Intuos), not to tablet devices like the Apple iPad.
|
||||
|
||||
References:
|
||||
- https://wayland.freedesktop.org/libinput/doc/latest/tablet-support.html
|
||||
- https://wayland.freedesktop.org/libinput/doc/latest/gestures.html
|
||||
|
||||
## KEYBOARD
|
||||
|
||||
*<keyboard><numlock>* [on|off]
|
||||
|
|
@ -808,6 +858,9 @@ extending outward from the snapped edge.
|
|||
|
||||
## MOUSE
|
||||
|
||||
This section relates to mice and touchpads - which are both considered pointer
|
||||
input-devices by the Wayland protocol.
|
||||
|
||||
*<mouse><doubleClickTime>*
|
||||
Set double click time in milliseconds. Default is 500.
|
||||
|
||||
|
|
@ -918,6 +971,11 @@ extending outward from the snapped edge.
|
|||
|
||||
## TOUCH
|
||||
|
||||
This section relates to touchscreens and *not* touchpads.
|
||||
|
||||
Note: To rotate touch events with output rotation, use the libinput
|
||||
*calibrationMatrix* setting.
|
||||
|
||||
```
|
||||
<touch deviceName="" mapToOutput="" mouseEmulation="no"/>
|
||||
```
|
||||
|
|
@ -993,7 +1051,7 @@ extending outward from the snapped edge.
|
|||
|
||||
*<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.
|
||||
|
|
@ -1012,10 +1070,10 @@ extending outward from the snapped edge.
|
|||
|
||||
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:
|
||||
|
|
@ -1080,14 +1138,9 @@ extending outward from the snapped edge.
|
|||
attribute is provided, a 'default' device profile will created that will
|
||||
act as the fallback for all libinput devices. Category can be set to any
|
||||
of the following types:
|
||||
- *touch* - Devices which have a defined width/height, but do not
|
||||
support multitouch (i.e. they cannot track multiple locations where
|
||||
the screen has been touched). Drawing tablets typically fall into this
|
||||
type.
|
||||
- *touchpad* - Same as 'touch' but support multitouch. This typically
|
||||
includes laptop track pads with two-finger scroll and swipe gestures.
|
||||
- *non-touch* - Anything not described above, for example traditional
|
||||
mouse pointers.
|
||||
- *touch* - Includes touchscreens and drawing-tablets.
|
||||
- *touchpad* - Includes touchpads (also known as laptop trackpads)
|
||||
- *non-touch* - Includes traditional mice
|
||||
- *default* - Defines a device-category applicable to all devices not
|
||||
matched by anything else. This can be useful for a fallback, or if you
|
||||
want the same settings to be applied to all devices.
|
||||
|
|
@ -1130,11 +1183,12 @@ extending outward from the snapped edge.
|
|||
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]
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<labwc_config>
|
||||
|
||||
<theme>
|
||||
<name></name>
|
||||
<name>Clearlooks-3.4</name>
|
||||
<cornerRadius>8</cornerRadius>
|
||||
<font name="sans" size="10" />
|
||||
</theme>
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@
|
|||
|
||||
<!-- <font><theme> can be defined without an attribute to set all places -->
|
||||
<theme>
|
||||
<name></name>
|
||||
<icon></icon>
|
||||
<!-- <name>Numix</name> -->
|
||||
<!-- <icon>breeze</icon> -->
|
||||
<fallbackAppIcon>labwc</fallbackAppIcon>
|
||||
<titlebar>
|
||||
<layout>icon:iconify,max,close</layout>
|
||||
|
|
@ -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%" />
|
||||
|
|
@ -161,8 +161,8 @@
|
|||
</focus>
|
||||
|
||||
<snapping>
|
||||
<!-- Set range to 0 to disable window snapping completely -->
|
||||
<range>10</range>
|
||||
<!-- Set inner and outer range to 0 to disable window snapping completely -->
|
||||
<range inner="10" outer="10" />
|
||||
<cornerRange>50</cornerRange>
|
||||
<overlay enabled="yes">
|
||||
<delay inner="500" outer="500" />
|
||||
|
|
@ -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>
|
||||
|
|
@ -218,9 +219,9 @@
|
|||
space automatically, so <margin> is only intended for other, specialist
|
||||
cases.
|
||||
|
||||
If output is left empty, the margin will be applied to all outputs.
|
||||
If 'output' is not provided, the margin will be applied to all outputs.
|
||||
|
||||
<margin top="" bottom="" left="" right="" output="" />
|
||||
<margin top="10" bottom="10" left="10" right="10" output="HDMI-A-1" />
|
||||
-->
|
||||
|
||||
<!-- Percent based regions based on output usable area, % char is required -->
|
||||
|
|
@ -525,7 +526,11 @@
|
|||
If mouseEmulation is enabled, all touch up/down/motion events are
|
||||
translated to mouse button and motion events.
|
||||
-->
|
||||
<touch deviceName="" mapToOutput="" mouseEmulation="no"/>
|
||||
<touch>
|
||||
<!-- <deviceName>ELAN2514:00 04F3:2AF1<deviceName> -->
|
||||
<!-- <mapToOutput>HDMI-A-1</mapToOutput> -->
|
||||
<mouseEmulation>no</mouseEmulation>
|
||||
</touch>
|
||||
|
||||
<!--
|
||||
The tablet cursor movement can be restricted to a single output.
|
||||
|
|
@ -552,7 +557,8 @@
|
|||
When using mouse emulation, the pen tip [tip] and the stylus buttons
|
||||
can be set to any available mouse button [Left|Right|Middle|..|Task].
|
||||
-->
|
||||
<tablet mapToOutput="" rotate="0" mouseEmulation="no">
|
||||
<tablet rotate="0" mouseEmulation="no">
|
||||
<!-- <mapToOutput>HDMI-A-1</mapToOutput> -->
|
||||
<!-- Active area dimensions are in mm -->
|
||||
<area top="0.0" left="0.0" width="0.0" height="0.0" />
|
||||
<map button="Tip" to="Left" />
|
||||
|
|
@ -583,10 +589,11 @@
|
|||
- accelProfile [flat|adaptive]
|
||||
- tapButtonMap [lrm|lmr]
|
||||
- clickMethod [none|buttonAreas|clickfinger]
|
||||
- scrollMethod [twoFinger|edge|none]
|
||||
- sendEventsMode [yes|no|disabledOnExternalMouse]
|
||||
- calibrationMatrix [six float values split by space]
|
||||
- scrollFactor [float]
|
||||
|
||||
|
||||
The following <libinput>...</libinput> block may not be complete for
|
||||
your requirements. Default values are device specific. Only set an option
|
||||
if you require to override the default. Valid values must be inserted.
|
||||
|
|
@ -595,21 +602,21 @@
|
|||
|
||||
<libinput>
|
||||
<device category="default">
|
||||
<naturalScroll></naturalScroll>
|
||||
<leftHanded></leftHanded>
|
||||
<pointerSpeed></pointerSpeed>
|
||||
<accelProfile></accelProfile>
|
||||
<!-- <naturalScroll>no</naturalScroll> -->
|
||||
<!-- <leftHanded>no</leftHanded> -->
|
||||
<!-- <pointerSpeed>0.0</pointerSpeed> -->
|
||||
<!-- <accelProfile>adaptive</accelProfile> -->
|
||||
<tap>yes</tap>
|
||||
<tapButtonMap></tapButtonMap>
|
||||
<tapAndDrag></tapAndDrag>
|
||||
<dragLock></dragLock>
|
||||
<threeFingerDrag></threeFingerDrag>
|
||||
<middleEmulation></middleEmulation>
|
||||
<disableWhileTyping></disableWhileTyping>
|
||||
<clickMethod></clickMethod>
|
||||
<scrollMethod></scrollMethod>
|
||||
<sendEventsMode></sendEventsMode>
|
||||
<calibrationMatrix></calibrationMatrix>
|
||||
<!-- <tapButtonMap>lrm</tapButtonMap> -->
|
||||
<!-- <tapAndDrag>yes</tapAndDrag> -->
|
||||
<!-- <dragLock>yes</dragLock> -->
|
||||
<!-- <threeFingerDrag>yes</threeFingerDrag> -->
|
||||
<!-- <middleEmulation>no</middleEmulation> -->
|
||||
<!-- <disableWhileTyping>yes</disableWhileTyping> -->
|
||||
<!-- <clickMethod>buttonAreas</clickMethod> -->
|
||||
<!-- <scrollMethod>twofinger</scrollMethod> -->
|
||||
<!-- <sendEventsMode>yes</sendEventsMode> -->
|
||||
<!-- <calibrationMatrix>1 0 0 0 1 0</calibrationMatrix> -->
|
||||
<scrollFactor>1.0</scrollFactor>
|
||||
</device>
|
||||
</libinput>
|
||||
|
|
@ -624,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>
|
||||
|
|
@ -678,7 +685,7 @@
|
|||
<height>400</height>
|
||||
<initScale>2.0</initScale>
|
||||
<increment>0.2</increment>
|
||||
<useFilter>true</useFilter>
|
||||
<useFilter>yes</useFilter>
|
||||
</magnifier>
|
||||
|
||||
</labwc_config>
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -10,6 +10,17 @@ bool box_intersects(struct wlr_box *box_a, struct wlr_box *box_b);
|
|||
void box_union(struct wlr_box *box_dest, struct wlr_box *box_a,
|
||||
struct wlr_box *box_b);
|
||||
|
||||
/*
|
||||
* Centers a content box (width & height) within a reference box,
|
||||
* limiting it (if possible) to not extend outside a bounding box.
|
||||
*
|
||||
* The reference box and bounding box are often the same but could be
|
||||
* different (e.g. when centering a view within its parent but limiting
|
||||
* to usable output area).
|
||||
*/
|
||||
void box_center(int width, int height, const struct wlr_box *ref,
|
||||
const struct wlr_box *bound, int *x, int *y);
|
||||
|
||||
/*
|
||||
* Fits and centers a content box (width & height) within a bounding box.
|
||||
* The content box is downscaled if necessary (preserving aspect ratio) but
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ enum lab_node_type {
|
|||
LAB_NODE_FRAME,
|
||||
LAB_NODE_ROOT,
|
||||
LAB_NODE_MENUITEM,
|
||||
LAB_NODE_OSD_ITEM,
|
||||
LAB_NODE_CYCLE_OSD_ITEM,
|
||||
LAB_NODE_LAYER_SURFACE,
|
||||
LAB_NODE_UNMANAGED,
|
||||
LAB_NODE_ALL,
|
||||
|
|
|
|||
|
|
@ -151,7 +151,8 @@ struct rcxml {
|
|||
int unmaximize_threshold;
|
||||
|
||||
/* window snapping */
|
||||
int snap_edge_range;
|
||||
int snap_edge_range_inner;
|
||||
int snap_edge_range_outer;
|
||||
int snap_edge_corner_range;
|
||||
bool snap_overlay_enabled;
|
||||
int snap_overlay_delay_inner;
|
||||
|
|
@ -167,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;
|
||||
|
|
@ -176,15 +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 window_switcher_style style;
|
||||
enum 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 */
|
||||
|
|
|
|||
|
|
@ -107,15 +107,30 @@ enum lab_window_type {
|
|||
LAB_WINDOW_TYPE_LEN
|
||||
};
|
||||
|
||||
enum window_switcher_style {
|
||||
WINDOW_SWITCHER_CLASSIC,
|
||||
WINDOW_SWITCHER_THUMBNAIL,
|
||||
enum window_switcher_order {
|
||||
WINDOW_SWITCHER_ORDER_FOCUS,
|
||||
WINDOW_SWITCHER_ORDER_AGE,
|
||||
};
|
||||
|
||||
enum osd_output_criteria {
|
||||
OSD_OUTPUT_ALL,
|
||||
OSD_OUTPUT_POINTER,
|
||||
OSD_OUTPUT_KEYBOARD,
|
||||
enum cycle_osd_style {
|
||||
CYCLE_OSD_STYLE_CLASSIC,
|
||||
CYCLE_OSD_STYLE_THUMBNAIL,
|
||||
};
|
||||
|
||||
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 */
|
||||
|
|
|
|||
109
include/cycle.h
Normal file
109
include/cycle.h
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
#ifndef LABWC_CYCLE_H
|
||||
#define LABWC_CYCLE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include "config/types.h"
|
||||
|
||||
struct output;
|
||||
|
||||
enum lab_cycle_dir {
|
||||
LAB_CYCLE_DIR_NONE,
|
||||
LAB_CYCLE_DIR_FORWARD,
|
||||
LAB_CYCLE_DIR_BACKWARD,
|
||||
};
|
||||
|
||||
/* TODO: add field with keyboard layout? */
|
||||
enum cycle_osd_field_content {
|
||||
LAB_FIELD_NONE = 0,
|
||||
LAB_FIELD_TYPE,
|
||||
LAB_FIELD_TYPE_SHORT,
|
||||
LAB_FIELD_IDENTIFIER,
|
||||
LAB_FIELD_TRIMMED_IDENTIFIER,
|
||||
LAB_FIELD_ICON,
|
||||
LAB_FIELD_DESKTOP_ENTRY_NAME,
|
||||
LAB_FIELD_TITLE,
|
||||
LAB_FIELD_TITLE_SHORT,
|
||||
LAB_FIELD_WORKSPACE,
|
||||
LAB_FIELD_WORKSPACE_SHORT,
|
||||
LAB_FIELD_WIN_STATE,
|
||||
LAB_FIELD_WIN_STATE_ALL,
|
||||
LAB_FIELD_OUTPUT,
|
||||
LAB_FIELD_OUTPUT_SHORT,
|
||||
LAB_FIELD_CUSTOM,
|
||||
|
||||
LAB_FIELD_COUNT
|
||||
};
|
||||
|
||||
struct cycle_osd_field {
|
||||
enum cycle_osd_field_content content;
|
||||
int width;
|
||||
char *format;
|
||||
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;
|
||||
struct view;
|
||||
struct server;
|
||||
struct wlr_scene_node;
|
||||
|
||||
/* Begin window switcher */
|
||||
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);
|
||||
|
||||
/* Closes the OSD */
|
||||
void cycle_finish(struct server *server, bool switch_focus);
|
||||
|
||||
/* Re-initialize the window switcher */
|
||||
void cycle_reinitialize(struct server *server);
|
||||
|
||||
/* Focus the clicked window and close OSD */
|
||||
void cycle_on_cursor_release(struct server *server, struct wlr_scene_node *node);
|
||||
|
||||
/* Used by osd.c internally to render window switcher fields */
|
||||
void cycle_osd_field_get_content(struct cycle_osd_field *field,
|
||||
struct buf *buf, struct view *view);
|
||||
/* Sets view info to buf according to format */
|
||||
void cycle_osd_field_set_custom(struct buf *buf, struct view *view,
|
||||
const char *format);
|
||||
|
||||
/* Used by rcxml.c when parsing the config */
|
||||
void cycle_osd_field_arg_from_xml_node(struct cycle_osd_field *field,
|
||||
const char *nodename, const char *content);
|
||||
bool cycle_osd_field_is_valid(struct cycle_osd_field *field);
|
||||
void cycle_osd_field_free(struct cycle_osd_field *field);
|
||||
|
||||
/* Internal API */
|
||||
struct cycle_osd_item {
|
||||
struct view *view;
|
||||
struct wlr_scene_tree *tree;
|
||||
struct wl_list link;
|
||||
};
|
||||
|
||||
struct cycle_osd_impl {
|
||||
/*
|
||||
* Create a scene-tree of OSD for an output.
|
||||
* This sets output->cycle_osd.{items,tree}.
|
||||
*/
|
||||
void (*create)(struct output *output);
|
||||
/*
|
||||
* Update output->cycle_osd.tree to highlight
|
||||
* server->cycle_state.selected_view.
|
||||
*/
|
||||
void (*update)(struct output *output);
|
||||
};
|
||||
|
||||
extern struct cycle_osd_impl cycle_osd_classic_impl;
|
||||
extern struct cycle_osd_impl cycle_osd_thumbnail_impl;
|
||||
|
||||
#endif // LABWC_CYCLE_H
|
||||
|
|
@ -38,6 +38,14 @@ struct cursor_context {
|
|||
double sx, sy;
|
||||
};
|
||||
|
||||
/* Used to persistently store cursor context (e.g. in seat->pressed) */
|
||||
struct cursor_context_saved {
|
||||
struct cursor_context ctx;
|
||||
struct wl_listener view_destroy;
|
||||
struct wl_listener node_destroy;
|
||||
struct wl_listener surface_destroy;
|
||||
};
|
||||
|
||||
/**
|
||||
* get_cursor_context - find view, surface and scene_node at cursor
|
||||
*
|
||||
|
|
@ -65,6 +73,13 @@ void cursor_set(struct seat *seat, enum lab_cursors cursor);
|
|||
|
||||
void cursor_set_visible(struct seat *seat, bool visible);
|
||||
|
||||
/*
|
||||
* Safely store a cursor context to saved_ctx. saved_ctx is cleared when either
|
||||
* of its node, surface and view is destroyed.
|
||||
*/
|
||||
void cursor_context_save(struct cursor_context_saved *saved_ctx,
|
||||
const struct cursor_context *ctx);
|
||||
|
||||
/**
|
||||
* cursor_get_resize_edges - calculate resize edge based on cursor position
|
||||
* @cursor - the current cursor (usually server->seat.cursor)
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
@ -18,7 +19,7 @@ enum input_mode {
|
|||
LAB_INPUT_STATE_MOVE,
|
||||
LAB_INPUT_STATE_RESIZE,
|
||||
LAB_INPUT_STATE_MENU,
|
||||
LAB_INPUT_STATE_WINDOW_SWITCHER,
|
||||
LAB_INPUT_STATE_CYCLE, /* a.k.a. window switching */
|
||||
};
|
||||
|
||||
struct seat {
|
||||
|
|
@ -65,8 +66,7 @@ struct seat {
|
|||
struct input_method_relay *input_method_relay;
|
||||
|
||||
/**
|
||||
* This is usually zeroed and is only set on button press while the
|
||||
* mouse is over a view or surface, and zeroed on button release.
|
||||
* Cursor context saved when a mouse button is pressed on a view/surface.
|
||||
* It is used to send cursor motion events to a surface even though
|
||||
* the cursor has left the surface in the meantime.
|
||||
*
|
||||
|
|
@ -76,10 +76,11 @@ struct seat {
|
|||
* It is also used to:
|
||||
* - determine the target view for action in "Drag" mousebind
|
||||
* - validate view move/resize requests from CSD clients
|
||||
*
|
||||
* Both (view && !surface) and (surface && !view) are possible.
|
||||
*/
|
||||
struct cursor_context pressed;
|
||||
struct cursor_context_saved pressed;
|
||||
|
||||
/* Cursor context of the last cursor motion */
|
||||
struct cursor_context_saved last_cursor_ctx;
|
||||
|
||||
struct lab_set bound_buttons;
|
||||
|
||||
|
|
@ -139,7 +140,6 @@ struct seat {
|
|||
struct wl_list tablet_pads;
|
||||
|
||||
struct wl_listener constraint_commit;
|
||||
struct wl_listener pressed_surface_destroy;
|
||||
|
||||
struct wlr_virtual_pointer_manager_v1 *virtual_pointer;
|
||||
struct wl_listener new_virtual_pointer;
|
||||
|
|
@ -189,6 +189,7 @@ struct server {
|
|||
struct wl_listener xdg_toplevel_icon_set_icon;
|
||||
|
||||
struct wl_list views;
|
||||
uint64_t next_view_creation_id;
|
||||
struct wl_list unmanaged_surfaces;
|
||||
|
||||
struct seat seat;
|
||||
|
|
@ -302,15 +303,16 @@ struct server {
|
|||
struct wlr_security_context_manager_v1 *security_context_manager_v1;
|
||||
|
||||
/* Set when in cycle (alt-tab) mode */
|
||||
struct osd_state {
|
||||
struct view *cycle_view;
|
||||
struct cycle_state {
|
||||
struct view *selected_view;
|
||||
struct wl_list views;
|
||||
bool preview_was_shaded;
|
||||
bool preview_was_enabled;
|
||||
struct wlr_scene_node *preview_node;
|
||||
struct wlr_scene_tree *preview_parent;
|
||||
struct wlr_scene_node *preview_anchor;
|
||||
struct wlr_scene_node *preview_dummy;
|
||||
struct lab_scene_rect *preview_outline;
|
||||
} osd_state;
|
||||
struct cycle_filter filter;
|
||||
} cycle;
|
||||
|
||||
struct theme *theme;
|
||||
|
||||
|
|
@ -392,8 +394,6 @@ void seat_pointer_end_grab(struct seat *seat, struct wlr_surface *surface);
|
|||
void seat_focus_lock_surface(struct seat *seat, struct wlr_surface *surface);
|
||||
|
||||
void seat_set_focus_layer(struct seat *seat, struct wlr_layer_surface_v1 *layer);
|
||||
void seat_set_pressed(struct seat *seat, struct cursor_context *ctx);
|
||||
void seat_reset_pressed(struct seat *seat);
|
||||
void seat_output_layout_changed(struct seat *seat);
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ struct node_descriptor {
|
|||
* @type: node descriptor type
|
||||
* @view: associated view
|
||||
* @data: struct to point to as follows:
|
||||
* - LAB_NODE_CYCLE_OSD_ITEM struct cycle_osd_item
|
||||
* - LAB_NODE_LAYER_SURFACE struct lab_layer_surface
|
||||
* - LAB_NODE_LAYER_POPUP struct lab_layer_popup
|
||||
* - LAB_NODE_MENUITEM struct menuitem
|
||||
|
|
@ -53,10 +54,10 @@ struct menuitem *node_menuitem_from_node(
|
|||
struct wlr_scene_node *wlr_scene_node);
|
||||
|
||||
/**
|
||||
* node_osd_item_from_node - return osd item struct from node
|
||||
* node_cycle_osd_item_from_node - return cycle OSD item struct from node
|
||||
* @wlr_scene_node: wlr_scene_node from which to return data
|
||||
*/
|
||||
struct osd_item *node_osd_item_from_node(
|
||||
struct cycle_osd_item *node_cycle_osd_item_from_node(
|
||||
struct wlr_scene_node *wlr_scene_node);
|
||||
|
||||
/**
|
||||
|
|
|
|||
101
include/osd.h
101
include/osd.h
|
|
@ -1,101 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
#ifndef LABWC_OSD_H
|
||||
#define LABWC_OSD_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <wayland-server-core.h>
|
||||
|
||||
struct output;
|
||||
|
||||
enum lab_cycle_dir {
|
||||
LAB_CYCLE_DIR_NONE,
|
||||
LAB_CYCLE_DIR_FORWARD,
|
||||
LAB_CYCLE_DIR_BACKWARD,
|
||||
};
|
||||
|
||||
/* TODO: add field with keyboard layout? */
|
||||
enum window_switcher_field_content {
|
||||
LAB_FIELD_NONE = 0,
|
||||
LAB_FIELD_TYPE,
|
||||
LAB_FIELD_TYPE_SHORT,
|
||||
LAB_FIELD_IDENTIFIER,
|
||||
LAB_FIELD_TRIMMED_IDENTIFIER,
|
||||
LAB_FIELD_ICON,
|
||||
LAB_FIELD_DESKTOP_ENTRY_NAME,
|
||||
LAB_FIELD_TITLE,
|
||||
LAB_FIELD_TITLE_SHORT,
|
||||
LAB_FIELD_WORKSPACE,
|
||||
LAB_FIELD_WORKSPACE_SHORT,
|
||||
LAB_FIELD_WIN_STATE,
|
||||
LAB_FIELD_WIN_STATE_ALL,
|
||||
LAB_FIELD_OUTPUT,
|
||||
LAB_FIELD_OUTPUT_SHORT,
|
||||
LAB_FIELD_CUSTOM,
|
||||
|
||||
LAB_FIELD_COUNT
|
||||
};
|
||||
|
||||
struct window_switcher_field {
|
||||
enum window_switcher_field_content content;
|
||||
int width;
|
||||
char *format;
|
||||
struct wl_list link; /* struct rcxml.window_switcher.fields */
|
||||
};
|
||||
|
||||
struct buf;
|
||||
struct view;
|
||||
struct server;
|
||||
struct wlr_scene_node;
|
||||
|
||||
/* Begin window switcher */
|
||||
void osd_begin(struct server *server, enum lab_cycle_dir direction);
|
||||
|
||||
/* Cycle the selected view in the window switcher */
|
||||
void osd_cycle(struct server *server, enum lab_cycle_dir direction);
|
||||
|
||||
/* Closes the OSD */
|
||||
void osd_finish(struct server *server, bool switch_focus);
|
||||
|
||||
/* Notify OSD about a destroying view */
|
||||
void osd_on_view_destroy(struct view *view);
|
||||
|
||||
/* Focus the clicked window and close OSD */
|
||||
void osd_on_cursor_release(struct server *server, struct wlr_scene_node *node);
|
||||
|
||||
/* Used by osd.c internally to render window switcher fields */
|
||||
void osd_field_get_content(struct window_switcher_field *field,
|
||||
struct buf *buf, struct view *view);
|
||||
/* Sets view info to buf according to format */
|
||||
void osd_field_set_custom(struct buf *buf, struct view *view,
|
||||
const char *format);
|
||||
|
||||
/* Used by rcxml.c when parsing the config */
|
||||
void osd_field_arg_from_xml_node(struct window_switcher_field *field,
|
||||
const char *nodename, const char *content);
|
||||
bool osd_field_is_valid(struct window_switcher_field *field);
|
||||
void osd_field_free(struct window_switcher_field *field);
|
||||
|
||||
/* Internal API */
|
||||
struct osd_item {
|
||||
struct view *view;
|
||||
struct wlr_scene_tree *tree;
|
||||
struct wl_list link;
|
||||
};
|
||||
|
||||
struct osd_impl {
|
||||
/*
|
||||
* Create a scene-tree of OSD for an output.
|
||||
* This sets output->osd_scene.{items,tree}.
|
||||
*/
|
||||
void (*create)(struct output *output, struct wl_array *views);
|
||||
/*
|
||||
* Update output->osd_scene.tree to highlight
|
||||
* server->osd_state.cycle_view.
|
||||
*/
|
||||
void (*update)(struct output *output);
|
||||
};
|
||||
|
||||
extern struct osd_impl osd_classic_impl;
|
||||
extern struct osd_impl osd_thumbnail_impl;
|
||||
|
||||
#endif // LABWC_OSD_H
|
||||
|
|
@ -15,14 +15,14 @@ struct output {
|
|||
struct wlr_scene_output *scene_output;
|
||||
struct wlr_scene_tree *layer_tree[LAB_NR_LAYERS];
|
||||
struct wlr_scene_tree *layer_popup_tree;
|
||||
struct wlr_scene_tree *osd_tree;
|
||||
struct wlr_scene_tree *cycle_osd_tree;
|
||||
struct wlr_scene_tree *session_lock_tree;
|
||||
struct wlr_scene_buffer *workspace_osd;
|
||||
|
||||
struct osd_scene {
|
||||
struct wl_list items; /* struct osd_item */
|
||||
struct cycle_osd_scene {
|
||||
struct wl_list items; /* struct cycle_osd_item */
|
||||
struct wlr_scene_tree *tree;
|
||||
} osd_scene;
|
||||
} cycle_osd;
|
||||
|
||||
/* In output-relative scene coordinates */
|
||||
struct wlr_box usable_area;
|
||||
|
|
|
|||
|
|
@ -134,6 +134,9 @@ struct view {
|
|||
const struct view_impl *impl;
|
||||
struct wl_list link;
|
||||
|
||||
/* This is cleared when the view is not in the cycle list */
|
||||
struct wl_list cycle_link;
|
||||
|
||||
/*
|
||||
* The primary output that the view is displayed on. Specifically:
|
||||
*
|
||||
|
|
@ -171,6 +174,7 @@ struct view {
|
|||
|
||||
bool mapped;
|
||||
bool been_mapped;
|
||||
uint64_t creation_id;
|
||||
enum lab_ssd_mode ssd_mode;
|
||||
enum ssd_preference ssd_preference;
|
||||
bool shaded;
|
||||
|
|
@ -293,6 +297,9 @@ struct xdg_toplevel_view {
|
|||
struct view base;
|
||||
struct wlr_xdg_surface *xdg_surface;
|
||||
|
||||
/* Optional black background fill behind fullscreen view */
|
||||
struct wlr_scene_rect *fullscreen_bg;
|
||||
|
||||
/* Events unique to xdg-toplevel views */
|
||||
struct wl_listener set_app_id;
|
||||
struct wl_listener request_show_window_menu;
|
||||
|
|
@ -384,15 +391,6 @@ struct view *view_next(struct wl_list *head, struct view *view,
|
|||
struct view *view_prev(struct wl_list *head, struct view *view,
|
||||
enum lab_view_criteria criteria);
|
||||
|
||||
/*
|
||||
* Same as `view_next()` except that they iterate one whole cycle rather than
|
||||
* stopping at the list-head
|
||||
*/
|
||||
struct view *view_next_no_head_stop(struct wl_list *head, struct view *from,
|
||||
enum lab_view_criteria criteria);
|
||||
struct view *view_prev_no_head_stop(struct wl_list *head, struct view *from,
|
||||
enum lab_view_criteria criteria);
|
||||
|
||||
/**
|
||||
* view_array_append() - Append views that match criteria to array
|
||||
* @server: server context
|
||||
|
|
|
|||
|
|
@ -77,5 +77,7 @@ void xwayland_update_workarea(struct server *server);
|
|||
|
||||
void xwayland_reset_cursor(struct server *server);
|
||||
|
||||
void xwayland_flush(struct server *server);
|
||||
|
||||
#endif /* HAVE_XWAYLAND */
|
||||
#endif /* LABWC_XWAYLAND_H */
|
||||
|
|
|
|||
|
|
@ -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: [
|
||||
|
|
|
|||
94
src/action.c
94
src/action.c
|
|
@ -18,12 +18,12 @@
|
|||
#include "common/spawn.h"
|
||||
#include "common/string-helpers.h"
|
||||
#include "config/rcxml.h"
|
||||
#include "cycle.h"
|
||||
#include "debug.h"
|
||||
#include "input/keyboard.h"
|
||||
#include "labwc.h"
|
||||
#include "magnifier.h"
|
||||
#include "menu/menu.h"
|
||||
#include "osd.h"
|
||||
#include "output.h"
|
||||
#include "output-virtual.h"
|
||||
#include "regions.h"
|
||||
|
|
@ -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);
|
||||
|
|
@ -414,6 +452,20 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
|
|||
goto cleanup;
|
||||
}
|
||||
break;
|
||||
case ACTION_TYPE_RESIZE:
|
||||
if (!strcmp(argument, "direction")) {
|
||||
enum lab_edge edge = lab_edge_parse(content,
|
||||
/*tiled*/ true, /*any*/ false);
|
||||
if (edge == LAB_EDGE_NONE || edge == LAB_EDGE_CENTER) {
|
||||
wlr_log(WLR_ERROR,
|
||||
"Invalid argument for action %s: '%s' (%s)",
|
||||
action_names[action->type], argument, content);
|
||||
} else {
|
||||
action_arg_add_int(action, argument, edge);
|
||||
}
|
||||
goto cleanup;
|
||||
}
|
||||
break;
|
||||
case ACTION_TYPE_RESIZE_RELATIVE:
|
||||
if (!strcmp(argument, "left") || !strcmp(argument, "right") ||
|
||||
!strcmp(argument, "top") || !strcmp(argument, "bottom")) {
|
||||
|
|
@ -1112,19 +1164,24 @@ run_action(struct view *view, struct server *server, struct action *action,
|
|||
}
|
||||
break;
|
||||
case ACTION_TYPE_NEXT_WINDOW:
|
||||
if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) {
|
||||
osd_cycle(server, LAB_CYCLE_DIR_FORWARD);
|
||||
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, dir);
|
||||
} else {
|
||||
osd_begin(server, LAB_CYCLE_DIR_FORWARD);
|
||||
}
|
||||
break;
|
||||
case ACTION_TYPE_PREVIOUS_WINDOW:
|
||||
if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) {
|
||||
osd_cycle(server, LAB_CYCLE_DIR_BACKWARD);
|
||||
} else {
|
||||
osd_begin(server, LAB_CYCLE_DIR_BACKWARD);
|
||||
cycle_begin(server, dir, filter);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ACTION_TYPE_RECONFIGURE:
|
||||
kill(getpid(), SIGHUP);
|
||||
break;
|
||||
|
|
@ -1223,8 +1280,17 @@ run_action(struct view *view, struct server *server, struct action *action,
|
|||
break;
|
||||
case ACTION_TYPE_RESIZE:
|
||||
if (view) {
|
||||
enum lab_edge resize_edges = cursor_get_resize_edges(
|
||||
server->seat.cursor, ctx);
|
||||
/*
|
||||
* If a direction was specified in the config, honour it.
|
||||
* Otherwise, fall back to determining the resize edges from
|
||||
* the current cursor position (existing behaviour).
|
||||
*/
|
||||
enum lab_edge resize_edges =
|
||||
action_get_int(action, "direction", LAB_EDGE_NONE);
|
||||
if (resize_edges == LAB_EDGE_NONE) {
|
||||
resize_edges = cursor_get_resize_edges(
|
||||
server->seat.cursor, ctx);
|
||||
}
|
||||
interactive_begin(view, LAB_INPUT_STATE_RESIZE,
|
||||
resize_edges);
|
||||
}
|
||||
|
|
@ -1569,7 +1635,7 @@ actions_run(struct view *activator, struct server *server,
|
|||
|
||||
struct action *action;
|
||||
wl_list_for_each(action, actions, link) {
|
||||
if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER
|
||||
if (server->input_mode == LAB_INPUT_STATE_CYCLE
|
||||
&& action->type != ACTION_TYPE_NEXT_WINDOW
|
||||
&& action->type != ACTION_TYPE_PREVIOUS_WINDOW) {
|
||||
wlr_log(WLR_INFO, "Only NextWindow or PreviousWindow "
|
||||
|
|
|
|||
|
|
@ -35,6 +35,25 @@ box_union(struct wlr_box *box_dest, struct wlr_box *box_a, struct wlr_box *box_b
|
|||
box_dest->height = y2 - y1;
|
||||
}
|
||||
|
||||
void
|
||||
box_center(int width, int height, const struct wlr_box *ref,
|
||||
const struct wlr_box *bound, int *x, int *y)
|
||||
{
|
||||
*x = ref->x + (ref->width - width) / 2;
|
||||
*y = ref->y + (ref->height - height) / 2;
|
||||
|
||||
if (*x < bound->x) {
|
||||
*x = bound->x;
|
||||
} else if (*x + width > bound->x + bound->width) {
|
||||
*x = bound->x + bound->width - width;
|
||||
}
|
||||
if (*y < bound->y) {
|
||||
*y = bound->y;
|
||||
} else if (*y + height > bound->y + bound->height) {
|
||||
*y = bound->y + bound->height - height;
|
||||
}
|
||||
}
|
||||
|
||||
struct wlr_box
|
||||
box_fit_within(int width, int height, struct wlr_box *bound)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@
|
|||
#include "config/tablet.h"
|
||||
#include "config/tablet-tool.h"
|
||||
#include "config/touch.h"
|
||||
#include "cycle.h"
|
||||
#include "labwc.h"
|
||||
#include "osd.h"
|
||||
#include "regions.h"
|
||||
#include "ssd.h"
|
||||
#include "translate.h"
|
||||
|
|
@ -323,23 +323,23 @@ fill_window_rules(xmlNode *node)
|
|||
static void
|
||||
clear_window_switcher_fields(void)
|
||||
{
|
||||
struct window_switcher_field *field, *field_tmp;
|
||||
wl_list_for_each_safe(field, field_tmp, &rc.window_switcher.fields, link) {
|
||||
struct cycle_osd_field *field, *field_tmp;
|
||||
wl_list_for_each_safe(field, field_tmp, &rc.window_switcher.osd.fields, link) {
|
||||
wl_list_remove(&field->link);
|
||||
osd_field_free(field);
|
||||
cycle_osd_field_free(field);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fill_window_switcher_field(xmlNode *node)
|
||||
{
|
||||
struct window_switcher_field *field = znew(*field);
|
||||
wl_list_append(&rc.window_switcher.fields, &field->link);
|
||||
struct cycle_osd_field *field = znew(*field);
|
||||
wl_list_append(&rc.window_switcher.osd.fields, &field->link);
|
||||
|
||||
xmlNode *child;
|
||||
char *key, *content;
|
||||
LAB_XML_FOR_EACH(node, child, key, content) {
|
||||
osd_field_arg_from_xml_node(field, key, content);
|
||||
cycle_osd_field_arg_from_xml_node(field, 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;
|
||||
|
|
@ -714,6 +714,8 @@ fill_libinput_category(xmlNode *node)
|
|||
char *key, *content;
|
||||
LAB_XML_FOR_EACH(node, child, key, content) {
|
||||
if (string_null_or_empty(content)) {
|
||||
wlr_log(WLR_ERROR, "Empty string is not allowed for "
|
||||
"<libinput><device><%s>. Ignoring.", key);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(key, "category")) {
|
||||
|
|
@ -1069,14 +1071,15 @@ 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 */
|
||||
return true;
|
||||
|
||||
} else if (str_space_only(content)) {
|
||||
/* ignore empty leaf nodes other than above */
|
||||
wlr_log(WLR_ERROR, "Empty string is not allowed for %s. "
|
||||
"Ignoring.", nodename);
|
||||
|
||||
/* handle non-empty leaf nodes */
|
||||
} else if (!strcmp(nodename, "decoration.core")) {
|
||||
|
|
@ -1177,7 +1180,14 @@ entry(xmlNode *node, char *nodename, char *content)
|
|||
} else if (!strcasecmp(nodename, "unMaximizeThreshold.resistance")) {
|
||||
rc.unmaximize_threshold = atoi(content);
|
||||
} else if (!strcasecmp(nodename, "range.snapping")) {
|
||||
rc.snap_edge_range = atoi(content);
|
||||
rc.snap_edge_range_inner = atoi(content);
|
||||
rc.snap_edge_range_outer = atoi(content);
|
||||
wlr_log(WLR_ERROR, "<snapping><range> is deprecated. "
|
||||
"Use <snapping><range inner=\"\" outer=\"\"> instead.");
|
||||
} else if (!strcasecmp(nodename, "inner.range.snapping")) {
|
||||
rc.snap_edge_range_inner = atoi(content);
|
||||
} else if (!strcasecmp(nodename, "outer.range.snapping")) {
|
||||
rc.snap_edge_range_outer = atoi(content);
|
||||
} else if (!strcasecmp(nodename, "cornerRange.snapping")) {
|
||||
rc.snap_edge_corner_range = atoi(content);
|
||||
} else if (!strcasecmp(nodename, "enabled.overlay.snapping")) {
|
||||
|
|
@ -1209,51 +1219,66 @@ 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 = WINDOW_SWITCHER_CLASSIC;
|
||||
rc.window_switcher.osd.style = CYCLE_OSD_STYLE_CLASSIC;
|
||||
} else if (!strcasecmp(content, "thumbnail")) {
|
||||
rc.window_switcher.style = WINDOW_SWITCHER_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 = OSD_OUTPUT_ALL;
|
||||
} else if (!strcasecmp(content, "pointer")) {
|
||||
rc.window_switcher.output_criteria = OSD_OUTPUT_POINTER;
|
||||
} else if (!strcasecmp(content, "keyboard")) {
|
||||
rc.window_switcher.output_criteria = OSD_OUTPUT_KEYBOARD;
|
||||
rc.window_switcher.osd.output_filter = CYCLE_OUTPUT_ALL;
|
||||
} else if (!strcasecmp(content, "cursor")) {
|
||||
rc.window_switcher.osd.output_filter = CYCLE_OUTPUT_CURSOR;
|
||||
} else if (!strcasecmp(content, "focused")) {
|
||||
rc.window_switcher.osd.output_filter = CYCLE_OUTPUT_FOCUSED;
|
||||
} else {
|
||||
wlr_log(WLR_ERROR, "Invalid windowSwitcher output %s: "
|
||||
"should be one of all|pointer|keyboard", content);
|
||||
wlr_log(WLR_ERROR, "Invalid windowSwitcher output '%s': "
|
||||
"should be one of all|focused|cursor", content);
|
||||
}
|
||||
} else if (!strcasecmp(nodename, "order.windowSwitcher")) {
|
||||
if (!strcasecmp(content, "focus")) {
|
||||
rc.window_switcher.order = WINDOW_SWITCHER_ORDER_FOCUS;
|
||||
} else if (!strcasecmp(content, "age")) {
|
||||
rc.window_switcher.order = WINDOW_SWITCHER_ORDER_AGE;
|
||||
} else {
|
||||
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 <osd show=\"\" />");
|
||||
" Use <windowSwitcher><osd show=\"\" />");
|
||||
} else if (!strcasecmp(nodename, "style.windowSwitcher")) {
|
||||
if (!strcasecmp(content, "classic")) {
|
||||
rc.window_switcher.style = WINDOW_SWITCHER_CLASSIC;
|
||||
rc.window_switcher.osd.style = CYCLE_OSD_STYLE_CLASSIC;
|
||||
} else if (!strcasecmp(content, "thumbnail")) {
|
||||
rc.window_switcher.style = WINDOW_SWITCHER_THUMBNAIL;
|
||||
rc.window_switcher.osd.style = CYCLE_OSD_STYLE_THUMBNAIL;
|
||||
}
|
||||
wlr_log(WLR_ERROR, "<windowSwitcher style=\"\" /> is deprecated."
|
||||
" Use <osd style=\"\" />");
|
||||
" Use <windowSwitcher><osd style=\"\" />");
|
||||
|
||||
} else if (!strcasecmp(nodename, "preview.windowSwitcher")) {
|
||||
set_bool(content, &rc.window_switcher.preview);
|
||||
} 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);
|
||||
|
||||
|
|
@ -1263,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")) {
|
||||
|
|
@ -1271,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")) {
|
||||
|
|
@ -1289,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")) {
|
||||
|
|
@ -1400,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);
|
||||
}
|
||||
|
|
@ -1456,7 +1483,8 @@ rcxml_init(void)
|
|||
rc.unsnap_threshold = 20;
|
||||
rc.unmaximize_threshold = 150;
|
||||
|
||||
rc.snap_edge_range = 10;
|
||||
rc.snap_edge_range_inner = 10;
|
||||
rc.snap_edge_range_outer = 10;
|
||||
rc.snap_edge_corner_range = 50;
|
||||
rc.snap_overlay_enabled = true;
|
||||
rc.snap_overlay_delay_inner = 500;
|
||||
|
|
@ -1464,16 +1492,15 @@ 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 = WINDOW_SWITCHER_CLASSIC;
|
||||
rc.window_switcher.output_criteria = 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;
|
||||
rc.resize_draw_contents = true;
|
||||
|
|
@ -1635,7 +1662,7 @@ static void
|
|||
load_default_window_switcher_fields(void)
|
||||
{
|
||||
static const struct {
|
||||
enum window_switcher_field_content content;
|
||||
enum cycle_osd_field_content content;
|
||||
int width;
|
||||
} fields[] = {
|
||||
#if HAVE_LIBSFDO
|
||||
|
|
@ -1648,12 +1675,11 @@ load_default_window_switcher_fields(void)
|
|||
#endif
|
||||
};
|
||||
|
||||
struct window_switcher_field *field;
|
||||
for (size_t i = 0; i < ARRAY_SIZE(fields); i++) {
|
||||
field = znew(*field);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1774,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();
|
||||
}
|
||||
|
|
@ -1867,13 +1893,13 @@ validate(void)
|
|||
|
||||
/* OSD fields */
|
||||
int field_width_sum = 0;
|
||||
struct window_switcher_field *field, *field_tmp;
|
||||
wl_list_for_each_safe(field, field_tmp, &rc.window_switcher.fields, link) {
|
||||
struct cycle_osd_field *field, *field_tmp;
|
||||
wl_list_for_each_safe(field, field_tmp, &rc.window_switcher.osd.fields, link) {
|
||||
field_width_sum += field->width;
|
||||
if (!osd_field_is_valid(field) || field_width_sum > 100) {
|
||||
if (!cycle_osd_field_is_valid(field) || field_width_sum > 100) {
|
||||
wlr_log(WLR_ERROR, "Deleting invalid window switcher field %p", field);
|
||||
wl_list_remove(&field->link);
|
||||
osd_field_free(field);
|
||||
cycle_osd_field_free(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1943,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();
|
||||
|
||||
|
|
|
|||
435
src/cycle/cycle.c
Normal file
435
src/cycle/cycle.c
Normal file
|
|
@ -0,0 +1,435 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#include "cycle.h"
|
||||
#include <assert.h>
|
||||
#include <wlr/types/wlr_scene.h>
|
||||
#include <wlr/util/box.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "common/lab-scene-rect.h"
|
||||
#include "common/list.h"
|
||||
#include "common/scene-helpers.h"
|
||||
#include "config/rcxml.h"
|
||||
#include "labwc.h"
|
||||
#include "node.h"
|
||||
#include "output.h"
|
||||
#include "scaled-buffer/scaled-font-buffer.h"
|
||||
#include "scaled-buffer/scaled-icon-buffer.h"
|
||||
#include "ssd.h"
|
||||
#include "theme.h"
|
||||
#include "view.h"
|
||||
|
||||
static bool init_cycle(struct server *server, struct cycle_filter filter);
|
||||
static void update_cycle(struct server *server);
|
||||
static void destroy_cycle(struct server *server);
|
||||
|
||||
static void
|
||||
update_preview_outlines(struct view *view)
|
||||
{
|
||||
/* Create / Update preview outline tree */
|
||||
struct server *server = view->server;
|
||||
struct theme *theme = server->theme;
|
||||
struct lab_scene_rect *rect = view->server->cycle.preview_outline;
|
||||
if (!rect) {
|
||||
struct lab_scene_rect_options opts = {
|
||||
.border_colors = (float *[3]) {
|
||||
theme->osd_window_switcher_preview_border_color[0],
|
||||
theme->osd_window_switcher_preview_border_color[1],
|
||||
theme->osd_window_switcher_preview_border_color[2],
|
||||
},
|
||||
.nr_borders = 3,
|
||||
.border_width = theme->osd_window_switcher_preview_border_width,
|
||||
};
|
||||
rect = lab_scene_rect_create(&server->scene->tree, &opts);
|
||||
wlr_scene_node_place_above(&rect->tree->node, &server->menu_tree->node);
|
||||
server->cycle.preview_outline = rect;
|
||||
}
|
||||
|
||||
struct wlr_box geo = ssd_max_extents(view);
|
||||
lab_scene_rect_set_size(rect, geo.width, geo.height);
|
||||
wlr_scene_node_set_position(&rect->tree->node, geo.x, geo.y);
|
||||
}
|
||||
|
||||
/* Returns the view to select next in the window switcher. */
|
||||
static struct view *
|
||||
get_next_selected_view(struct server *server, enum lab_cycle_dir dir)
|
||||
{
|
||||
struct cycle_state *cycle = &server->cycle;
|
||||
assert(cycle->selected_view);
|
||||
assert(!wl_list_empty(&server->cycle.views));
|
||||
|
||||
struct wl_list *link;
|
||||
if (dir == LAB_CYCLE_DIR_FORWARD) {
|
||||
link = cycle->selected_view->cycle_link.next;
|
||||
if (link == &server->cycle.views) {
|
||||
link = link->next;
|
||||
}
|
||||
} else {
|
||||
link = cycle->selected_view->cycle_link.prev;
|
||||
if (link == &server->cycle.views) {
|
||||
link = link->prev;
|
||||
}
|
||||
}
|
||||
struct view *view = wl_container_of(link, view, cycle_link);
|
||||
return view;
|
||||
}
|
||||
|
||||
static struct view *
|
||||
get_first_view(struct wl_list *views)
|
||||
{
|
||||
assert(!wl_list_empty(views));
|
||||
struct view *view = wl_container_of(views->next, view, cycle_link);
|
||||
return view;
|
||||
}
|
||||
|
||||
void
|
||||
cycle_reinitialize(struct server *server)
|
||||
{
|
||||
struct cycle_state *cycle = &server->cycle;
|
||||
|
||||
if (server->input_mode != LAB_INPUT_STATE_CYCLE) {
|
||||
/* OSD not active, no need for clean up */
|
||||
return;
|
||||
}
|
||||
|
||||
struct view *selected_view = cycle->selected_view;
|
||||
struct view *selected_view_prev =
|
||||
get_next_selected_view(server, LAB_CYCLE_DIR_BACKWARD);
|
||||
struct cycle_filter filter = cycle->filter;
|
||||
|
||||
destroy_cycle(server);
|
||||
if (init_cycle(server, filter)) {
|
||||
/*
|
||||
* Preserve the selected view (or its previous view) if it's
|
||||
* still in the cycle list
|
||||
*/
|
||||
if (selected_view->cycle_link.next) {
|
||||
cycle->selected_view = selected_view;
|
||||
} else if (selected_view_prev->cycle_link.next) {
|
||||
cycle->selected_view = selected_view_prev;
|
||||
} else {
|
||||
/* should be unreachable */
|
||||
wlr_log(WLR_ERROR, "could not find view to select");
|
||||
cycle->selected_view = get_first_view(&server->cycle.views);
|
||||
}
|
||||
update_cycle(server);
|
||||
} else {
|
||||
/* Failed to re-init window switcher, exit */
|
||||
cycle_finish(server, /*switch_focus*/ false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
cycle_on_cursor_release(struct server *server, struct wlr_scene_node *node)
|
||||
{
|
||||
assert(server->input_mode == LAB_INPUT_STATE_CYCLE);
|
||||
|
||||
struct cycle_osd_item *item = node_cycle_osd_item_from_node(node);
|
||||
server->cycle.selected_view = item->view;
|
||||
cycle_finish(server, /*switch_focus*/ true);
|
||||
}
|
||||
|
||||
static void
|
||||
restore_preview_node(struct server *server)
|
||||
{
|
||||
if (server->cycle.preview_node) {
|
||||
wlr_scene_node_reparent(server->cycle.preview_node,
|
||||
server->cycle.preview_dummy->parent);
|
||||
wlr_scene_node_place_above(server->cycle.preview_node,
|
||||
server->cycle.preview_dummy);
|
||||
wlr_scene_node_destroy(server->cycle.preview_dummy);
|
||||
|
||||
/* Node was disabled / minimized before, disable again */
|
||||
if (!server->cycle.preview_was_enabled) {
|
||||
wlr_scene_node_set_enabled(server->cycle.preview_node, false);
|
||||
}
|
||||
if (server->cycle.preview_was_shaded) {
|
||||
struct view *view = node_view_from_node(server->cycle.preview_node);
|
||||
view_set_shade(view, true);
|
||||
}
|
||||
server->cycle.preview_node = NULL;
|
||||
server->cycle.preview_dummy = NULL;
|
||||
server->cycle.preview_was_enabled = false;
|
||||
server->cycle.preview_was_shaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
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, filter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct view *active_view = server->active_view;
|
||||
if (active_view && active_view->cycle_link.next) {
|
||||
/* Select the active view it's in the cycle list */
|
||||
server->cycle.selected_view = active_view;
|
||||
} else {
|
||||
/* Otherwise, select the first view in the cycle list */
|
||||
server->cycle.selected_view = get_first_view(&server->cycle.views);
|
||||
}
|
||||
/* Pre-select the next view in the given direction */
|
||||
server->cycle.selected_view = get_next_selected_view(server, direction);
|
||||
|
||||
seat_focus_override_begin(&server->seat,
|
||||
LAB_INPUT_STATE_CYCLE, LAB_CURSOR_DEFAULT);
|
||||
update_cycle(server);
|
||||
|
||||
/* Update cursor, in case it is within the area covered by OSD */
|
||||
cursor_update_focus(server);
|
||||
}
|
||||
|
||||
void
|
||||
cycle_step(struct server *server, enum lab_cycle_dir direction)
|
||||
{
|
||||
assert(server->input_mode == LAB_INPUT_STATE_CYCLE);
|
||||
|
||||
server->cycle.selected_view = get_next_selected_view(server, direction);
|
||||
update_cycle(server);
|
||||
}
|
||||
|
||||
void
|
||||
cycle_finish(struct server *server, bool switch_focus)
|
||||
{
|
||||
if (server->input_mode != LAB_INPUT_STATE_CYCLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct view *selected_view = server->cycle.selected_view;
|
||||
destroy_cycle(server);
|
||||
|
||||
/* FIXME: this sets focus to the old surface even with switch_focus=true */
|
||||
seat_focus_override_end(&server->seat);
|
||||
|
||||
/* Hiding OSD may need a cursor change */
|
||||
cursor_update_focus(server);
|
||||
|
||||
if (switch_focus && selected_view) {
|
||||
if (rc.window_switcher.unshade) {
|
||||
view_set_shade(selected_view, false);
|
||||
}
|
||||
desktop_focus_view(selected_view, /*raise*/ true);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
preview_selected_view(struct view *view)
|
||||
{
|
||||
assert(view);
|
||||
assert(view->scene_tree);
|
||||
struct server *server = view->server;
|
||||
struct cycle_state *cycle = &server->cycle;
|
||||
|
||||
/* Move previous selected node back to its original place */
|
||||
restore_preview_node(server);
|
||||
|
||||
cycle->preview_node = &view->scene_tree->node;
|
||||
|
||||
/* Create a dummy node at the original place of the previewed window */
|
||||
struct wlr_scene_rect *dummy_rect = wlr_scene_rect_create(
|
||||
cycle->preview_node->parent, 0, 0, (float [4]) {0});
|
||||
wlr_scene_node_place_below(&dummy_rect->node, cycle->preview_node);
|
||||
wlr_scene_node_set_enabled(&dummy_rect->node, false);
|
||||
cycle->preview_dummy = &dummy_rect->node;
|
||||
|
||||
/* Store node enabled / minimized state and force-enable if disabled */
|
||||
cycle->preview_was_enabled = cycle->preview_node->enabled;
|
||||
wlr_scene_node_set_enabled(cycle->preview_node, true);
|
||||
if (rc.window_switcher.unshade && view->shaded) {
|
||||
view_set_shade(view, false);
|
||||
cycle->preview_was_shaded = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: This abuses an implementation detail of the always-on-top tree.
|
||||
* Create a permanent server->osd_preview_tree instead that can
|
||||
* also be used as parent for the preview outlines.
|
||||
*/
|
||||
wlr_scene_node_reparent(cycle->preview_node,
|
||||
view->server->view_tree_always_on_top);
|
||||
|
||||
/* Finally raise selected node to the top */
|
||||
wlr_scene_node_raise_to_top(cycle->preview_node);
|
||||
}
|
||||
|
||||
static struct cycle_osd_impl *
|
||||
get_osd_impl(void)
|
||||
{
|
||||
switch (rc.window_switcher.osd.style) {
|
||||
case CYCLE_OSD_STYLE_CLASSIC:
|
||||
return &cycle_osd_classic_impl;
|
||||
case CYCLE_OSD_STYLE_THUMBNAIL:
|
||||
return &cycle_osd_thumbnail_impl;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
get_outputs_by_filter(struct server *server,
|
||||
enum cycle_output_filter output_filter)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
insert_view_ordered_by_age(struct wl_list *views, struct view *new_view)
|
||||
{
|
||||
struct wl_list *link = views;
|
||||
struct view *view;
|
||||
wl_list_for_each(view, views, cycle_link) {
|
||||
if (view->creation_id >= new_view->creation_id) {
|
||||
break;
|
||||
}
|
||||
link = &view->cycle_link;
|
||||
}
|
||||
wl_list_insert(link, &new_view->cycle_link);
|
||||
}
|
||||
|
||||
/* Return false on failure */
|
||||
static bool
|
||||
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, 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 {
|
||||
wl_list_append(&server->cycle.views, &view->cycle_link);
|
||||
}
|
||||
}
|
||||
if (wl_list_empty(&server->cycle.views)) {
|
||||
wlr_log(WLR_DEBUG, "no views to switch between");
|
||||
return false;
|
||||
}
|
||||
server->cycle.filter = filter;
|
||||
|
||||
if (rc.window_switcher.osd.show) {
|
||||
/* Create OSD */
|
||||
uint64_t osd_outputs = get_outputs_by_filter(server,
|
||||
rc.window_switcher.osd.output_filter);
|
||||
struct output *output;
|
||||
wl_list_for_each(output, &server->outputs, link) {
|
||||
if (!(osd_outputs & output->id_bit)) {
|
||||
continue;
|
||||
}
|
||||
if (!output_is_usable(output)) {
|
||||
continue;
|
||||
}
|
||||
get_osd_impl()->create(output);
|
||||
assert(output->cycle_osd.tree);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
update_cycle(struct server *server)
|
||||
{
|
||||
struct cycle_state *cycle = &server->cycle;
|
||||
|
||||
if (rc.window_switcher.osd.show) {
|
||||
struct output *output;
|
||||
wl_list_for_each(output, &server->outputs, link) {
|
||||
if (output->cycle_osd.tree) {
|
||||
get_osd_impl()->update(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rc.window_switcher.preview) {
|
||||
preview_selected_view(cycle->selected_view);
|
||||
}
|
||||
|
||||
/* Outline current window */
|
||||
if (rc.window_switcher.outlines) {
|
||||
if (view_is_focusable(server->cycle.selected_view)) {
|
||||
update_preview_outlines(server->cycle.selected_view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Resets all the states in server->cycle */
|
||||
static void
|
||||
destroy_cycle(struct server *server)
|
||||
{
|
||||
struct output *output;
|
||||
wl_list_for_each(output, &server->outputs, link) {
|
||||
struct cycle_osd_item *item, *tmp;
|
||||
wl_list_for_each_safe(item, tmp, &output->cycle_osd.items, link) {
|
||||
wl_list_remove(&item->link);
|
||||
free(item);
|
||||
}
|
||||
if (output->cycle_osd.tree) {
|
||||
wlr_scene_node_destroy(&output->cycle_osd.tree->node);
|
||||
output->cycle_osd.tree = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
restore_preview_node(server);
|
||||
|
||||
if (server->cycle.preview_outline) {
|
||||
wlr_scene_node_destroy(&server->cycle.preview_outline->tree->node);
|
||||
server->cycle.preview_outline = NULL;
|
||||
}
|
||||
|
||||
struct view *view, *tmp;
|
||||
wl_list_for_each_safe(view, tmp, &server->cycle.views, cycle_link) {
|
||||
wl_list_remove(&view->cycle_link);
|
||||
view->cycle_link = (struct wl_list){0};
|
||||
}
|
||||
|
||||
server->cycle.selected_view = NULL;
|
||||
server->cycle.filter = (struct cycle_filter){0};
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
labwc_sources += files(
|
||||
'osd.c',
|
||||
'cycle.c',
|
||||
'osd-classic.c',
|
||||
'osd-field.c',
|
||||
'osd-thumbnail.c',
|
||||
|
|
@ -4,24 +4,25 @@
|
|||
#include <wlr/types/wlr_scene.h>
|
||||
#include <wlr/util/box.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "common/array.h"
|
||||
#include "common/buf.h"
|
||||
#include "common/font.h"
|
||||
#include "common/lab-scene-rect.h"
|
||||
#include "common/list.h"
|
||||
#include "common/mem.h"
|
||||
#include "common/string-helpers.h"
|
||||
#include "config/rcxml.h"
|
||||
#include "cycle.h"
|
||||
#include "labwc.h"
|
||||
#include "node.h"
|
||||
#include "osd.h"
|
||||
#include "output.h"
|
||||
#include "scaled-buffer/scaled-font-buffer.h"
|
||||
#include "scaled-buffer/scaled-icon-buffer.h"
|
||||
#include "theme.h"
|
||||
#include "view.h"
|
||||
#include "workspaces.h"
|
||||
|
||||
struct osd_classic_item {
|
||||
struct osd_item base;
|
||||
struct cycle_osd_classic_item {
|
||||
struct cycle_osd_item base;
|
||||
struct wlr_scene_tree *normal_tree, *active_tree;
|
||||
};
|
||||
|
||||
|
|
@ -34,8 +35,8 @@ create_fields_scene(struct server *server, struct view *view,
|
|||
struct window_switcher_classic_theme *switcher_theme =
|
||||
&theme->osd_window_switcher_classic;
|
||||
|
||||
struct window_switcher_field *field;
|
||||
wl_list_for_each(field, &rc.window_switcher.fields, link) {
|
||||
struct cycle_osd_field *field;
|
||||
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;
|
||||
|
|
@ -51,7 +52,7 @@ create_fields_scene(struct server *server, struct view *view,
|
|||
height = icon_size;
|
||||
} else {
|
||||
struct buf buf = BUF_INIT;
|
||||
osd_field_get_content(field, &buf, view);
|
||||
cycle_osd_field_get_content(field, &buf, view);
|
||||
|
||||
if (!string_null_or_empty(buf.data)) {
|
||||
struct scaled_font_buffer *font_buffer =
|
||||
|
|
@ -76,9 +77,9 @@ create_fields_scene(struct server *server, struct view *view,
|
|||
}
|
||||
|
||||
static void
|
||||
osd_classic_create(struct output *output, struct wl_array *views)
|
||||
cycle_osd_classic_create(struct output *output)
|
||||
{
|
||||
assert(!output->osd_scene.tree && wl_list_empty(&output->osd_scene.items));
|
||||
assert(!output->cycle_osd.tree && wl_list_empty(&output->cycle_osd.items));
|
||||
|
||||
struct server *server = output->server;
|
||||
struct theme *theme = server->theme;
|
||||
|
|
@ -87,6 +88,7 @@ osd_classic_create(struct output *output, struct wl_array *views)
|
|||
int padding = theme->osd_border_width + switcher_theme->padding;
|
||||
bool show_workspace = wl_list_length(&rc.workspace_config.workspaces) > 1;
|
||||
const char *workspace_name = server->workspaces.current->name;
|
||||
int nr_views = wl_list_length(&server->cycle.views);
|
||||
|
||||
struct wlr_box output_box;
|
||||
wlr_output_layout_get_box(server->output_layout, output->wlr_output,
|
||||
|
|
@ -96,13 +98,13 @@ osd_classic_create(struct output *output, struct wl_array *views)
|
|||
if (switcher_theme->width_is_percent) {
|
||||
w = output_box.width * switcher_theme->width / 100;
|
||||
}
|
||||
int h = wl_array_len(views) * switcher_theme->item_height + 2 * padding;
|
||||
int h = nr_views * switcher_theme->item_height + 2 * padding;
|
||||
if (show_workspace) {
|
||||
/* workspace indicator */
|
||||
h += switcher_theme->item_height;
|
||||
}
|
||||
|
||||
output->osd_scene.tree = wlr_scene_tree_create(output->osd_tree);
|
||||
output->cycle_osd.tree = wlr_scene_tree_create(output->cycle_osd_tree);
|
||||
|
||||
float *text_color = theme->osd_label_text_color;
|
||||
float *bg_color = theme->osd_bg_color;
|
||||
|
|
@ -116,7 +118,7 @@ osd_classic_create(struct output *output, struct wl_array *views)
|
|||
.width = w,
|
||||
.height = h,
|
||||
};
|
||||
lab_scene_rect_create(output->osd_scene.tree, &bg_opts);
|
||||
lab_scene_rect_create(output->cycle_osd.tree, &bg_opts);
|
||||
|
||||
int y = padding;
|
||||
|
||||
|
|
@ -134,7 +136,7 @@ osd_classic_create(struct output *output, struct wl_array *views)
|
|||
}
|
||||
|
||||
struct scaled_font_buffer *font_buffer =
|
||||
scaled_font_buffer_create(output->osd_scene.tree);
|
||||
scaled_font_buffer_create(output->cycle_osd.tree);
|
||||
wlr_scene_node_set_position(&font_buffer->scene_buffer->node,
|
||||
x, y + (switcher_theme->item_height - font_height(&font)) / 2);
|
||||
scaled_font_buffer_update(font_buffer, workspace_name, 0,
|
||||
|
|
@ -142,8 +144,7 @@ osd_classic_create(struct output *output, struct wl_array *views)
|
|||
y += switcher_theme->item_height;
|
||||
}
|
||||
|
||||
struct buf buf = BUF_INIT;
|
||||
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
|
||||
|
|
@ -155,14 +156,14 @@ osd_classic_create(struct output *output, struct wl_array *views)
|
|||
}
|
||||
|
||||
/* Draw text for each node */
|
||||
struct view **view;
|
||||
wl_array_for_each(view, views) {
|
||||
struct osd_classic_item *item = znew(*item);
|
||||
wl_list_append(&output->osd_scene.items, &item->base.link);
|
||||
item->base.view = *view;
|
||||
item->base.tree = wlr_scene_tree_create(output->osd_scene.tree);
|
||||
struct view *view;
|
||||
wl_list_for_each(view, &server->cycle.views, cycle_link) {
|
||||
struct cycle_osd_classic_item *item = znew(*item);
|
||||
wl_list_append(&output->cycle_osd.items, &item->base.link);
|
||||
item->base.view = view;
|
||||
item->base.tree = wlr_scene_tree_create(output->cycle_osd.tree);
|
||||
node_descriptor_create(&item->base.tree->node,
|
||||
LAB_NODE_OSD_ITEM, NULL, item);
|
||||
LAB_NODE_CYCLE_OSD_ITEM, NULL, item);
|
||||
/*
|
||||
* OSD border
|
||||
* +---------------------------------+
|
||||
|
|
@ -192,9 +193,9 @@ osd_classic_create(struct output *output, struct wl_array *views)
|
|||
/* Highlight around selected window's item */
|
||||
struct lab_scene_rect_options highlight_opts = {
|
||||
.border_colors = (float *[1]) {active_border_color},
|
||||
.bg_color = active_bg_color,
|
||||
.nr_borders = 1,
|
||||
.border_width = switcher_theme->item_active_border_width,
|
||||
.bg_color = active_bg_color,
|
||||
.width = w - 2 * padding,
|
||||
.height = switcher_theme->item_height,
|
||||
};
|
||||
|
|
@ -207,34 +208,33 @@ osd_classic_create(struct output *output, struct wl_array *views)
|
|||
w - 2 * padding, switcher_theme->item_height, (float[4]) {0});
|
||||
wlr_scene_node_set_position(&hitbox->node, padding, y);
|
||||
|
||||
create_fields_scene(server, *view, item->normal_tree,
|
||||
create_fields_scene(server, view, item->normal_tree,
|
||||
text_color, bg_color, field_widths_sum, x, y);
|
||||
create_fields_scene(server, *view, item->active_tree,
|
||||
create_fields_scene(server, view, item->active_tree,
|
||||
text_color, active_bg_color, field_widths_sum, x, y);
|
||||
|
||||
y += switcher_theme->item_height;
|
||||
}
|
||||
buf_reset(&buf);
|
||||
|
||||
error:;
|
||||
/* Center OSD */
|
||||
wlr_scene_node_set_position(&output->osd_scene.tree->node,
|
||||
wlr_scene_node_set_position(&output->cycle_osd.tree->node,
|
||||
output_box.x + (output_box.width - w) / 2,
|
||||
output_box.y + (output_box.height - h) / 2);
|
||||
}
|
||||
|
||||
static void
|
||||
osd_classic_update(struct output *output)
|
||||
cycle_osd_classic_update(struct output *output)
|
||||
{
|
||||
struct osd_classic_item *item;
|
||||
wl_list_for_each(item, &output->osd_scene.items, base.link) {
|
||||
bool active = item->base.view == output->server->osd_state.cycle_view;
|
||||
struct cycle_osd_classic_item *item;
|
||||
wl_list_for_each(item, &output->cycle_osd.items, base.link) {
|
||||
bool active = item->base.view == output->server->cycle.selected_view;
|
||||
wlr_scene_node_set_enabled(&item->normal_tree->node, !active);
|
||||
wlr_scene_node_set_enabled(&item->active_tree->node, active);
|
||||
}
|
||||
}
|
||||
|
||||
struct osd_impl osd_classic_impl = {
|
||||
.create = osd_classic_create,
|
||||
.update = osd_classic_update,
|
||||
struct cycle_osd_impl cycle_osd_classic_impl = {
|
||||
.create = cycle_osd_classic_create,
|
||||
.update = cycle_osd_classic_update,
|
||||
};
|
||||
|
|
@ -5,11 +5,11 @@
|
|||
#include "common/buf.h"
|
||||
#include "common/mem.h"
|
||||
#include "config/rcxml.h"
|
||||
#include "cycle.h"
|
||||
#include "view.h"
|
||||
#include "workspaces.h"
|
||||
#include "labwc.h"
|
||||
#include "desktop-entry.h"
|
||||
#include "osd.h"
|
||||
#include "output.h"
|
||||
|
||||
/* includes '%', terminating 's' and NULL byte, 8 is enough for %-9999s */
|
||||
|
|
@ -204,11 +204,11 @@ static const struct field_converter field_converter[LAB_FIELD_COUNT] = {
|
|||
[LAB_FIELD_TITLE] = { 'T', field_set_title },
|
||||
[LAB_FIELD_TITLE_SHORT] = { 't', field_set_title_short },
|
||||
/* fmt_char can never be matched so prevents LAB_FIELD_CUSTOM recursion */
|
||||
[LAB_FIELD_CUSTOM] = { '\0', osd_field_set_custom },
|
||||
[LAB_FIELD_CUSTOM] = { '\0', cycle_osd_field_set_custom },
|
||||
};
|
||||
|
||||
void
|
||||
osd_field_set_custom(struct buf *buf, struct view *view, const char *format)
|
||||
cycle_osd_field_set_custom(struct buf *buf, struct view *view, const char *format)
|
||||
{
|
||||
if (!format) {
|
||||
wlr_log(WLR_ERROR, "Missing format for custom window switcher field");
|
||||
|
|
@ -286,7 +286,7 @@ reset_format:
|
|||
}
|
||||
|
||||
void
|
||||
osd_field_arg_from_xml_node(struct window_switcher_field *field,
|
||||
cycle_osd_field_arg_from_xml_node(struct cycle_osd_field *field,
|
||||
const char *nodename, const char *content)
|
||||
{
|
||||
if (!strcmp(nodename, "content")) {
|
||||
|
|
@ -332,7 +332,7 @@ osd_field_arg_from_xml_node(struct window_switcher_field *field,
|
|||
}
|
||||
|
||||
bool
|
||||
osd_field_is_valid(struct window_switcher_field *field)
|
||||
cycle_osd_field_is_valid(struct cycle_osd_field *field)
|
||||
{
|
||||
if (field->content == LAB_FIELD_NONE) {
|
||||
wlr_log(WLR_ERROR, "Invalid OSD field: no content set");
|
||||
|
|
@ -350,7 +350,7 @@ osd_field_is_valid(struct window_switcher_field *field)
|
|||
}
|
||||
|
||||
void
|
||||
osd_field_get_content(struct window_switcher_field *field,
|
||||
cycle_osd_field_get_content(struct cycle_osd_field *field,
|
||||
struct buf *buf, struct view *view)
|
||||
{
|
||||
if (field->content == LAB_FIELD_NONE) {
|
||||
|
|
@ -363,7 +363,7 @@ osd_field_get_content(struct window_switcher_field *field,
|
|||
}
|
||||
|
||||
void
|
||||
osd_field_free(struct window_switcher_field *field)
|
||||
cycle_osd_field_free(struct cycle_osd_field *field)
|
||||
{
|
||||
zfree(field->format);
|
||||
zfree(field);
|
||||
|
|
@ -5,22 +5,22 @@
|
|||
#include <wlr/types/wlr_output_layout.h>
|
||||
#include <wlr/types/wlr_scene.h>
|
||||
#include "config/rcxml.h"
|
||||
#include "common/array.h"
|
||||
#include "common/box.h"
|
||||
#include "common/buf.h"
|
||||
#include "common/lab-scene-rect.h"
|
||||
#include "common/list.h"
|
||||
#include "common/mem.h"
|
||||
#include "cycle.h"
|
||||
#include "labwc.h"
|
||||
#include "node.h"
|
||||
#include "osd.h"
|
||||
#include "output.h"
|
||||
#include "scaled-buffer/scaled-font-buffer.h"
|
||||
#include "scaled-buffer/scaled-icon-buffer.h"
|
||||
#include "theme.h"
|
||||
#include "view.h"
|
||||
|
||||
struct osd_thumbnail_item {
|
||||
struct osd_item base;
|
||||
struct cycle_osd_thumbnail_item {
|
||||
struct cycle_osd_item base;
|
||||
struct scaled_font_buffer *normal_label;
|
||||
struct scaled_font_buffer *active_label;
|
||||
struct lab_scene_rect *active_bg;
|
||||
|
|
@ -102,8 +102,8 @@ create_label(struct wlr_scene_tree *parent, struct view *view,
|
|||
const float *text_color, const float *bg_color, int y)
|
||||
{
|
||||
struct buf buf = BUF_INIT;
|
||||
osd_field_set_custom(&buf, view,
|
||||
rc.window_switcher.thumbnail_label_format);
|
||||
cycle_osd_field_set_custom(&buf, view,
|
||||
rc.window_switcher.osd.thumbnail_label_format);
|
||||
struct scaled_font_buffer *buffer =
|
||||
scaled_font_buffer_create(parent);
|
||||
scaled_font_buffer_update(buffer, buf.data,
|
||||
|
|
@ -115,7 +115,7 @@ create_label(struct wlr_scene_tree *parent, struct view *view,
|
|||
return buffer;
|
||||
}
|
||||
|
||||
static struct osd_thumbnail_item *
|
||||
static struct cycle_osd_thumbnail_item *
|
||||
create_item_scene(struct wlr_scene_tree *parent, struct view *view,
|
||||
struct output *output)
|
||||
{
|
||||
|
|
@ -136,10 +136,10 @@ create_item_scene(struct wlr_scene_tree *parent, struct view *view,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
struct osd_thumbnail_item *item = znew(*item);
|
||||
wl_list_append(&output->osd_scene.items, &item->base.link);
|
||||
struct cycle_osd_thumbnail_item *item = znew(*item);
|
||||
wl_list_append(&output->cycle_osd.items, &item->base.link);
|
||||
struct wlr_scene_tree *tree = wlr_scene_tree_create(parent);
|
||||
node_descriptor_create(&tree->node, LAB_NODE_OSD_ITEM, NULL, item);
|
||||
node_descriptor_create(&tree->node, LAB_NODE_CYCLE_OSD_ITEM, NULL, item);
|
||||
item->base.tree = tree;
|
||||
item->base.view = view;
|
||||
|
||||
|
|
@ -226,9 +226,9 @@ get_items_geometry(struct output *output, struct theme *theme,
|
|||
}
|
||||
|
||||
static void
|
||||
osd_thumbnail_create(struct output *output, struct wl_array *views)
|
||||
cycle_osd_thumbnail_create(struct output *output)
|
||||
{
|
||||
assert(!output->osd_scene.tree && wl_list_empty(&output->osd_scene.items));
|
||||
assert(!output->cycle_osd.tree && wl_list_empty(&output->cycle_osd.items));
|
||||
|
||||
struct server *server = output->server;
|
||||
struct theme *theme = server->theme;
|
||||
|
|
@ -236,19 +236,19 @@ osd_thumbnail_create(struct output *output, struct wl_array *views)
|
|||
&theme->osd_window_switcher_thumbnail;
|
||||
int padding = theme->osd_border_width + switcher_theme->padding;
|
||||
|
||||
output->osd_scene.tree = wlr_scene_tree_create(output->osd_tree);
|
||||
output->cycle_osd.tree = wlr_scene_tree_create(output->cycle_osd_tree);
|
||||
|
||||
int nr_views = wl_array_len(views);
|
||||
int nr_views = wl_list_length(&server->cycle.views);
|
||||
assert(nr_views > 0);
|
||||
int nr_rows, nr_cols;
|
||||
get_items_geometry(output, theme, nr_views, &nr_rows, &nr_cols);
|
||||
|
||||
/* items */
|
||||
struct view **view;
|
||||
struct view *view;
|
||||
int index = 0;
|
||||
wl_array_for_each(view, views) {
|
||||
struct osd_thumbnail_item *item = create_item_scene(
|
||||
output->osd_scene.tree, *view, output);
|
||||
wl_list_for_each(view, &server->cycle.views, cycle_link) {
|
||||
struct cycle_osd_thumbnail_item *item = create_item_scene(
|
||||
output->cycle_osd.tree, view, output);
|
||||
if (!item) {
|
||||
break;
|
||||
}
|
||||
|
|
@ -268,7 +268,7 @@ osd_thumbnail_create(struct output *output, struct wl_array *views)
|
|||
.height = nr_rows * switcher_theme->item_height + 2 * padding,
|
||||
};
|
||||
struct lab_scene_rect *bg =
|
||||
lab_scene_rect_create(output->osd_scene.tree, &bg_opts);
|
||||
lab_scene_rect_create(output->cycle_osd.tree, &bg_opts);
|
||||
wlr_scene_node_lower_to_bottom(&bg->tree->node);
|
||||
|
||||
/* center */
|
||||
|
|
@ -277,15 +277,15 @@ osd_thumbnail_create(struct output *output, struct wl_array *views)
|
|||
&output_box);
|
||||
int lx = output_box.x + (output_box.width - bg_opts.width) / 2;
|
||||
int ly = output_box.y + (output_box.height - bg_opts.height) / 2;
|
||||
wlr_scene_node_set_position(&output->osd_scene.tree->node, lx, ly);
|
||||
wlr_scene_node_set_position(&output->cycle_osd.tree->node, lx, ly);
|
||||
}
|
||||
|
||||
static void
|
||||
osd_thumbnail_update(struct output *output)
|
||||
cycle_osd_thumbnail_update(struct output *output)
|
||||
{
|
||||
struct osd_thumbnail_item *item;
|
||||
wl_list_for_each(item, &output->osd_scene.items, base.link) {
|
||||
bool active = (item->base.view == output->server->osd_state.cycle_view);
|
||||
struct cycle_osd_thumbnail_item *item;
|
||||
wl_list_for_each(item, &output->cycle_osd.items, base.link) {
|
||||
bool active = (item->base.view == output->server->cycle.selected_view);
|
||||
wlr_scene_node_set_enabled(&item->active_bg->tree->node, active);
|
||||
wlr_scene_node_set_enabled(
|
||||
&item->active_label->scene_buffer->node, active);
|
||||
|
|
@ -294,7 +294,7 @@ osd_thumbnail_update(struct output *output)
|
|||
}
|
||||
}
|
||||
|
||||
struct osd_impl osd_thumbnail_impl = {
|
||||
.create = osd_thumbnail_create,
|
||||
.update = osd_thumbnail_update,
|
||||
struct cycle_osd_impl cycle_osd_thumbnail_impl = {
|
||||
.create = cycle_osd_thumbnail_create,
|
||||
.update = cycle_osd_thumbnail_update,
|
||||
};
|
||||
16
src/debug.c
16
src/debug.c
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
#define IGNORE_SSD true
|
||||
#define IGNORE_MENU true
|
||||
#define IGNORE_OSD_PREVIEW_OUTLINE true
|
||||
#define IGNORE_CYCLE_PREVIEW_OUTLINE true
|
||||
#define IGNORE_SNAPPING_OVERLAY true
|
||||
|
||||
static struct view *last_view;
|
||||
|
|
@ -118,7 +118,7 @@ get_special(struct server *server, struct wlr_scene_node *node)
|
|||
if (node->parent == &server->scene->tree) {
|
||||
struct output *output;
|
||||
wl_list_for_each(output, &server->outputs, link) {
|
||||
if (node == &output->osd_tree->node) {
|
||||
if (node == &output->cycle_osd_tree->node) {
|
||||
return "output->osd_tree";
|
||||
}
|
||||
if (node == &output->layer_popup_tree->node) {
|
||||
|
|
@ -150,10 +150,10 @@ get_special(struct server *server, struct wlr_scene_node *node)
|
|||
/* Created on-demand */
|
||||
return "seat->im_relay->popup_tree";
|
||||
}
|
||||
if (server->osd_state.preview_outline
|
||||
&& node == &server->osd_state.preview_outline->tree->node) {
|
||||
if (server->cycle.preview_outline
|
||||
&& node == &server->cycle.preview_outline->tree->node) {
|
||||
/* Created on-demand */
|
||||
return "osd_state->preview_outline";
|
||||
return "cycle_state->preview_outline";
|
||||
}
|
||||
#if HAVE_XWAYLAND
|
||||
if (node == &server->unmanaged_tree->node) {
|
||||
|
|
@ -216,13 +216,11 @@ dump_tree(struct server *server, struct wlr_scene_node *node,
|
|||
}
|
||||
printf("%.*s %*c %4d %4d [%p]\n", max_width - 1, type, padding, ' ', x, y, node);
|
||||
|
||||
struct lab_scene_rect *osd_preview_outline =
|
||||
server->osd_state.preview_outline;
|
||||
if ((IGNORE_MENU && node == &server->menu_tree->node)
|
||||
|| (IGNORE_SSD && last_view
|
||||
&& ssd_debug_is_root_node(last_view->ssd, node))
|
||||
|| (IGNORE_OSD_PREVIEW_OUTLINE && osd_preview_outline
|
||||
&& node == &osd_preview_outline->tree->node)
|
||||
|| (IGNORE_CYCLE_PREVIEW_OUTLINE && server->cycle.preview_outline
|
||||
&& node == &server->cycle.preview_outline->tree->node)
|
||||
|| (IGNORE_SNAPPING_OVERLAY && server->seat.overlay.rect
|
||||
&& node == &server->seat.overlay.rect->tree->node)) {
|
||||
printf("%*c%s\n", pos + 4 + INDENT_SIZE, ' ', "<skipping children>");
|
||||
|
|
|
|||
|
|
@ -251,6 +251,9 @@ err:
|
|||
* (e.g. "thunderbird" matches "org.mozilla.Thunderbird.desktop"
|
||||
* and "XTerm" matches "xterm.desktop"). This is not per any spec
|
||||
* but is needed to find icons for existing applications.
|
||||
*
|
||||
* The second loop tries to match more partial strings, for
|
||||
* example "gimp-2.0" would match "org.something.gimp.desktop".
|
||||
*/
|
||||
static struct sfdo_desktop_entry *
|
||||
get_db_entry_by_id_fuzzy(struct sfdo_desktop_db *db, const char *app_id)
|
||||
|
|
@ -258,6 +261,7 @@ get_db_entry_by_id_fuzzy(struct sfdo_desktop_db *db, const char *app_id)
|
|||
size_t n_entries;
|
||||
struct sfdo_desktop_entry **entries = sfdo_desktop_db_get_entries(db, &n_entries);
|
||||
|
||||
/* Would match "org.foobar.xterm" when given app-id "XTerm" */
|
||||
for (size_t i = 0; i < n_entries; i++) {
|
||||
struct sfdo_desktop_entry *entry = entries[i];
|
||||
const char *desktop_id = sfdo_desktop_entry_get_id(entry, NULL);
|
||||
|
|
@ -266,6 +270,8 @@ get_db_entry_by_id_fuzzy(struct sfdo_desktop_db *db, const char *app_id)
|
|||
const char *desktop_id_base = dot ? (dot + 1) : desktop_id;
|
||||
|
||||
if (!strcasecmp(app_id, desktop_id_base)) {
|
||||
wlr_log(WLR_DEBUG, "'%s' to '%s.desktop' via case-insensitive match",
|
||||
app_id, desktop_id);
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
|
@ -278,20 +284,31 @@ get_db_entry_by_id_fuzzy(struct sfdo_desktop_db *db, const char *app_id)
|
|||
const char *wm_class =
|
||||
sfdo_desktop_entry_get_startup_wm_class(entry, NULL);
|
||||
if (wm_class && !strcasecmp(app_id, wm_class)) {
|
||||
wlr_log(WLR_DEBUG, "'%s' to '%s.desktop' via StartupWMClass",
|
||||
app_id, desktop_id);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try matching partial strings - catches GIMP, among others */
|
||||
/* Would match "org.foobar.xterm-unicode" when given app-id "XTerm" */
|
||||
const int app_id_len = strlen(app_id);
|
||||
for (size_t i = 0; i < n_entries; i++) {
|
||||
struct sfdo_desktop_entry *entry = entries[i];
|
||||
const char *desktop_id = sfdo_desktop_entry_get_id(entry, NULL);
|
||||
const char *dot = strrchr(desktop_id, '.');
|
||||
const char *desktop_id_base = dot ? (dot + 1) : desktop_id;
|
||||
int alen = strlen(app_id);
|
||||
int dlen = strlen(desktop_id_base);
|
||||
|
||||
if (!strncasecmp(app_id, desktop_id_base, alen > dlen ? dlen : alen)) {
|
||||
const int dlen = strlen(desktop_id_base);
|
||||
const int cmp_len = MIN(app_id_len, dlen);
|
||||
if (cmp_len < 3) {
|
||||
/*
|
||||
* Without this check, app-id "foot" would match
|
||||
* "something.f" and any app-id would match "R.E.P.O."
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
if (!strncasecmp(app_id, desktop_id_base, cmp_len)) {
|
||||
wlr_log(WLR_DEBUG, "'%s' to '%s.desktop' via partial match",
|
||||
app_id, desktop_id);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
|
@ -304,10 +321,14 @@ get_desktop_entry(struct sfdo *sfdo, const char *app_id)
|
|||
{
|
||||
struct sfdo_desktop_entry *entry = sfdo_desktop_db_get_entry_by_id(
|
||||
sfdo->desktop_db, app_id, SFDO_NT);
|
||||
if (!entry) {
|
||||
entry = get_db_entry_by_id_fuzzy(sfdo->desktop_db, app_id);
|
||||
if (entry) {
|
||||
wlr_log(WLR_DEBUG, "matched '%s.desktop' via exact match", app_id);
|
||||
return entry;
|
||||
}
|
||||
entry = get_db_entry_by_id_fuzzy(sfdo->desktop_db, app_id);
|
||||
if (!entry) {
|
||||
wlr_log(WLR_DEBUG, "failed to find .desktop file for '%s'", app_id);
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ desktop_focus_view(struct view *view, bool raise)
|
|||
return;
|
||||
}
|
||||
|
||||
if (view->server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) {
|
||||
if (view->server->input_mode == LAB_INPUT_STATE_CYCLE) {
|
||||
wlr_log(WLR_DEBUG, "not focusing window while window switching");
|
||||
return;
|
||||
}
|
||||
|
|
@ -340,10 +340,10 @@ get_cursor_context(struct server *server)
|
|||
ret.node = node;
|
||||
ret.type = LAB_NODE_MENUITEM;
|
||||
return ret;
|
||||
case LAB_NODE_OSD_ITEM:
|
||||
case LAB_NODE_CYCLE_OSD_ITEM:
|
||||
/* Always return the top scene node for osd items */
|
||||
ret.node = node;
|
||||
ret.type = LAB_NODE_OSD_ITEM;
|
||||
ret.type = LAB_NODE_CYCLE_OSD_ITEM;
|
||||
return ret;
|
||||
case LAB_NODE_BUTTON_FIRST...LAB_NODE_BUTTON_LAST:
|
||||
case LAB_NODE_SSD_ROOT:
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ handle_drag_start(struct wl_listener *listener, void *data)
|
|||
struct wlr_drag *drag = data;
|
||||
|
||||
seat->drag.active = true;
|
||||
seat_reset_pressed(seat);
|
||||
cursor_context_save(&seat->pressed, NULL);
|
||||
if (drag->icon) {
|
||||
/* Cleans up automatically on drag->icon->events.destroy */
|
||||
wlr_scene_drag_icon_create(seat->drag.icons, drag->icon);
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include "common/mem.h"
|
||||
#include "config/mousebind.h"
|
||||
#include "config/rcxml.h"
|
||||
#include "cycle.h"
|
||||
#include "dnd.h"
|
||||
#include "idle.h"
|
||||
#include "input/gestures.h"
|
||||
|
|
@ -30,7 +31,6 @@
|
|||
#include "labwc.h"
|
||||
#include "layers.h"
|
||||
#include "menu/menu.h"
|
||||
#include "osd.h"
|
||||
#include "output.h"
|
||||
#include "resistance.h"
|
||||
#include "resize-outlines.h"
|
||||
|
|
@ -437,8 +437,72 @@ cursor_update_image(struct seat *seat)
|
|||
cursor_names[cursor]);
|
||||
}
|
||||
|
||||
static void
|
||||
clear_cursor_context(struct cursor_context_saved *saved_ctx)
|
||||
{
|
||||
if (saved_ctx->node_destroy.notify) {
|
||||
wl_list_remove(&saved_ctx->node_destroy.link);
|
||||
}
|
||||
if (saved_ctx->surface_destroy.notify) {
|
||||
wl_list_remove(&saved_ctx->surface_destroy.link);
|
||||
}
|
||||
if (saved_ctx->view_destroy.notify) {
|
||||
wl_list_remove(&saved_ctx->view_destroy.link);
|
||||
}
|
||||
*saved_ctx = (struct cursor_context_saved) {0};
|
||||
}
|
||||
|
||||
static void
|
||||
handle_ctx_node_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct cursor_context_saved *saved_ctx =
|
||||
wl_container_of(listener, saved_ctx, node_destroy);
|
||||
clear_cursor_context(saved_ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_ctx_surface_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct cursor_context_saved *saved_ctx =
|
||||
wl_container_of(listener, saved_ctx, surface_destroy);
|
||||
clear_cursor_context(saved_ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_ctx_view_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct cursor_context_saved *saved_ctx =
|
||||
wl_container_of(listener, saved_ctx, view_destroy);
|
||||
clear_cursor_context(saved_ctx);
|
||||
}
|
||||
|
||||
void
|
||||
cursor_context_save(struct cursor_context_saved *saved_ctx,
|
||||
const struct cursor_context *ctx)
|
||||
{
|
||||
assert(saved_ctx);
|
||||
|
||||
clear_cursor_context(saved_ctx);
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
saved_ctx->ctx = *ctx;
|
||||
if (ctx->node) {
|
||||
saved_ctx->node_destroy.notify = handle_ctx_node_destroy;
|
||||
wl_signal_add(&ctx->node->events.destroy, &saved_ctx->node_destroy);
|
||||
}
|
||||
if (ctx->surface) {
|
||||
saved_ctx->surface_destroy.notify = handle_ctx_surface_destroy;
|
||||
wl_signal_add(&ctx->surface->events.destroy, &saved_ctx->surface_destroy);
|
||||
}
|
||||
if (ctx->view) {
|
||||
saved_ctx->view_destroy.notify = handle_ctx_view_destroy;
|
||||
wl_signal_add(&ctx->view->events.destroy, &saved_ctx->view_destroy);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
update_pressed_surface(struct seat *seat, struct cursor_context *ctx)
|
||||
update_pressed_surface(struct seat *seat, const struct cursor_context *ctx)
|
||||
{
|
||||
/*
|
||||
* In most cases, we don't want to leave one surface and enter
|
||||
|
|
@ -454,10 +518,10 @@ update_pressed_surface(struct seat *seat, struct cursor_context *ctx)
|
|||
if (!wlr_seat_pointer_has_grab(seat->seat)) {
|
||||
return false;
|
||||
}
|
||||
if (seat->pressed.surface && ctx->surface != seat->pressed.surface) {
|
||||
if (seat->pressed.ctx.surface && ctx->surface != seat->pressed.ctx.surface) {
|
||||
struct wlr_surface *toplevel = get_toplevel(ctx->surface);
|
||||
if (toplevel && toplevel == get_toplevel(seat->pressed.surface)) {
|
||||
seat_set_pressed(seat, ctx);
|
||||
if (toplevel && toplevel == get_toplevel(seat->pressed.ctx.surface)) {
|
||||
cursor_context_save(&seat->pressed, ctx);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -466,11 +530,11 @@ update_pressed_surface(struct seat *seat, struct cursor_context *ctx)
|
|||
|
||||
/*
|
||||
* Common logic shared by cursor_update_focus(), process_cursor_motion()
|
||||
* and cursor_axis()
|
||||
* and process_cursor_axis()
|
||||
*/
|
||||
static bool
|
||||
cursor_update_common(struct server *server, struct cursor_context *ctx,
|
||||
bool cursor_has_moved, double *sx, double *sy)
|
||||
static void
|
||||
cursor_update_common(struct server *server, const struct cursor_context *ctx,
|
||||
struct cursor_context *notified_ctx)
|
||||
{
|
||||
struct seat *seat = &server->seat;
|
||||
struct wlr_seat *wlr_seat = seat->seat;
|
||||
|
|
@ -483,14 +547,14 @@ cursor_update_common(struct server *server, struct cursor_context *ctx,
|
|||
* interactive move/resize, window switcher and
|
||||
* menu interaction.
|
||||
*/
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO: verify drag_icon logic */
|
||||
if (seat->pressed.surface && ctx->surface != seat->pressed.surface
|
||||
if (seat->pressed.ctx.surface && ctx->surface != seat->pressed.ctx.surface
|
||||
&& !update_pressed_surface(seat, ctx)
|
||||
&& !seat->drag.active) {
|
||||
if (cursor_has_moved) {
|
||||
if (notified_ctx) {
|
||||
/*
|
||||
* Button has been pressed while over another
|
||||
* surface and is still held down. Just send
|
||||
|
|
@ -499,12 +563,16 @@ cursor_update_common(struct server *server, struct cursor_context *ctx,
|
|||
* if the cursor moves outside of the surface.
|
||||
*/
|
||||
int lx, ly;
|
||||
wlr_scene_node_coords(seat->pressed.node, &lx, &ly);
|
||||
*sx = server->seat.cursor->x - lx;
|
||||
*sy = server->seat.cursor->y - ly;
|
||||
return true;
|
||||
wlr_scene_node_coords(seat->pressed.ctx.node, &lx, &ly);
|
||||
*notified_ctx = seat->pressed.ctx;
|
||||
notified_ctx->sx = server->seat.cursor->x - lx;
|
||||
notified_ctx->sy = server->seat.cursor->y - ly;
|
||||
}
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (notified_ctx) {
|
||||
*notified_ctx = *ctx;
|
||||
}
|
||||
|
||||
if (ctx->surface) {
|
||||
|
|
@ -516,11 +584,6 @@ cursor_update_common(struct server *server, struct cursor_context *ctx,
|
|||
wlr_seat_pointer_notify_enter(wlr_seat, ctx->surface,
|
||||
ctx->sx, ctx->sy);
|
||||
seat->server_cursor = LAB_CURSOR_CLIENT;
|
||||
if (cursor_has_moved) {
|
||||
*sx = ctx->sx;
|
||||
*sy = ctx->sy;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Cursor is over a server (labwc) surface. Clear focus
|
||||
|
|
@ -538,7 +601,6 @@ cursor_update_common(struct server *server, struct cursor_context *ctx,
|
|||
cursor_set(seat, cursor);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
enum lab_edge
|
||||
|
|
@ -597,32 +659,45 @@ cursor_process_motion(struct server *server, uint32_t time, double *sx, double *
|
|||
* moving/resizing the wrong view
|
||||
*/
|
||||
mousebind->pressed_in_context = false;
|
||||
actions_run(seat->pressed.view, server,
|
||||
&mousebind->actions, &seat->pressed);
|
||||
actions_run(seat->pressed.ctx.view, server,
|
||||
&mousebind->actions, &seat->pressed.ctx);
|
||||
}
|
||||
}
|
||||
|
||||
struct wlr_surface *old_focused_surface =
|
||||
seat->seat->pointer_state.focused_surface;
|
||||
/*
|
||||
* Cursor context that is actually interacting with cursor and should
|
||||
* be notified to the client. E.g. it is cleared when menu is open,
|
||||
* and the pressed view is set while out-of-surface dragging.
|
||||
*/
|
||||
struct cursor_context notified_ctx = {0};
|
||||
cursor_update_common(server, &ctx, ¬ified_ctx);
|
||||
|
||||
bool notify = cursor_update_common(server, &ctx,
|
||||
/* cursor_has_moved */ true, sx, sy);
|
||||
|
||||
struct wlr_surface *new_focused_surface =
|
||||
seat->seat->pointer_state.focused_surface;
|
||||
|
||||
if (rc.focus_follow_mouse && new_focused_surface
|
||||
&& old_focused_surface != new_focused_surface) {
|
||||
if (rc.focus_follow_mouse) {
|
||||
/*
|
||||
* If followMouse=yes, update the keyboard focus when the
|
||||
* cursor enters a surface
|
||||
* If followMouse=yes, entering a surface or view updates
|
||||
* keyboard focus. Note that moving the cursor between a
|
||||
* surface and a SSD within the same view doesn't update
|
||||
* keyboard focus, and that entering a surface/view doesn't
|
||||
* update keyboard focus if implicit grab is active.
|
||||
*/
|
||||
desktop_focus_view_or_surface(seat,
|
||||
view_from_wlr_surface(new_focused_surface),
|
||||
new_focused_surface, rc.raise_on_focus);
|
||||
bool entering = false;
|
||||
if (notified_ctx.view) {
|
||||
entering = notified_ctx.view
|
||||
!= seat->last_cursor_ctx.ctx.view;
|
||||
} else if (notified_ctx.surface) {
|
||||
entering = notified_ctx.surface
|
||||
!= seat->last_cursor_ctx.ctx.surface;
|
||||
}
|
||||
if (entering) {
|
||||
desktop_focus_view_or_surface(seat, notified_ctx.view,
|
||||
notified_ctx.surface, rc.raise_on_focus);
|
||||
}
|
||||
}
|
||||
cursor_context_save(&seat->last_cursor_ctx, ¬ified_ctx);
|
||||
|
||||
return notify;
|
||||
*sx = notified_ctx.sx;
|
||||
*sy = notified_ctx.sy;
|
||||
return notified_ctx.surface;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -641,8 +716,7 @@ _cursor_update_focus(struct server *server)
|
|||
ctx.surface, rc.raise_on_focus);
|
||||
}
|
||||
|
||||
double sx, sy;
|
||||
cursor_update_common(server, &ctx, /*cursor_has_moved*/ false, &sx, &sy);
|
||||
cursor_update_common(server, &ctx, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -766,10 +840,12 @@ apply_constraint(struct seat *seat, struct wlr_pointer *pointer, double *x, doub
|
|||
if (!seat->server->active_view) {
|
||||
return;
|
||||
}
|
||||
if (!seat->current_constraint || pointer->base.type != WLR_INPUT_DEVICE_POINTER) {
|
||||
if (!seat->current_constraint
|
||||
|| pointer->base.type != WLR_INPUT_DEVICE_POINTER
|
||||
|| seat->current_constraint->type
|
||||
!= WLR_POINTER_CONSTRAINT_V1_CONFINED) {
|
||||
return;
|
||||
}
|
||||
assert(seat->current_constraint->type == WLR_POINTER_CONSTRAINT_V1_CONFINED);
|
||||
|
||||
double sx = seat->cursor->x;
|
||||
double sy = seat->cursor->y;
|
||||
|
|
@ -792,7 +868,9 @@ cursor_locked(struct seat *seat, struct wlr_pointer *pointer)
|
|||
{
|
||||
return seat->current_constraint
|
||||
&& pointer->base.type == WLR_INPUT_DEVICE_POINTER
|
||||
&& seat->current_constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED;
|
||||
&& seat->current_constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED
|
||||
&& seat->current_constraint->surface
|
||||
== seat->seat->pointer_state.focused_surface;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -907,11 +985,6 @@ handle_motion_absolute(struct wl_listener *listener, void *data)
|
|||
double dx = lx - seat->cursor->x;
|
||||
double dy = ly - seat->cursor->y;
|
||||
|
||||
wlr_relative_pointer_manager_v1_send_relative_motion(
|
||||
seat->server->relative_pointer_manager,
|
||||
seat->seat, (uint64_t)event->time_msec * 1000,
|
||||
dx, dy, dx, dy);
|
||||
|
||||
preprocess_cursor_motion(seat, event->pointer,
|
||||
event->time_msec, dx, dy);
|
||||
}
|
||||
|
|
@ -920,7 +993,7 @@ static void
|
|||
process_release_mousebinding(struct server *server,
|
||||
struct cursor_context *ctx, uint32_t button)
|
||||
{
|
||||
if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) {
|
||||
if (server->input_mode == LAB_INPUT_STATE_CYCLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -989,7 +1062,7 @@ static bool
|
|||
process_press_mousebinding(struct server *server, struct cursor_context *ctx,
|
||||
uint32_t button)
|
||||
{
|
||||
if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) {
|
||||
if (server->input_mode == LAB_INPUT_STATE_CYCLE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1073,7 +1146,7 @@ cursor_process_button_press(struct seat *seat, uint32_t button, uint32_t time_ms
|
|||
|
||||
if (ctx.view || ctx.surface) {
|
||||
/* Store cursor context for later action processing */
|
||||
seat_set_pressed(seat, &ctx);
|
||||
cursor_context_save(&seat->pressed, &ctx);
|
||||
}
|
||||
|
||||
if (server->input_mode == LAB_INPUT_STATE_MENU) {
|
||||
|
|
@ -1138,12 +1211,12 @@ cursor_process_button_release(struct seat *seat, uint32_t button,
|
|||
{
|
||||
struct server *server = seat->server;
|
||||
struct cursor_context ctx = get_cursor_context(server);
|
||||
struct wlr_surface *pressed_surface = seat->pressed.surface;
|
||||
struct wlr_surface *pressed_surface = seat->pressed.ctx.surface;
|
||||
|
||||
/* Always notify button release event when it's not bound */
|
||||
const bool notify = !lab_set_contains(&seat->bound_buttons, button);
|
||||
|
||||
seat_reset_pressed(seat);
|
||||
cursor_context_save(&seat->pressed, NULL);
|
||||
|
||||
if (server->input_mode == LAB_INPUT_STATE_MENU) {
|
||||
/* TODO: take into account overflow of time_msec */
|
||||
|
|
@ -1157,9 +1230,9 @@ cursor_process_button_release(struct seat *seat, uint32_t button,
|
|||
}
|
||||
return notify;
|
||||
}
|
||||
if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) {
|
||||
if (ctx.type == LAB_NODE_OSD_ITEM) {
|
||||
osd_on_cursor_release(server, ctx.node);
|
||||
if (server->input_mode == LAB_INPUT_STATE_CYCLE) {
|
||||
if (ctx.type == LAB_NODE_CYCLE_OSD_ITEM) {
|
||||
cycle_on_cursor_release(server, ctx.node);
|
||||
}
|
||||
return notify;
|
||||
}
|
||||
|
|
@ -1347,8 +1420,7 @@ process_cursor_axis(struct server *server, enum wl_pointer_axis orientation,
|
|||
/* Bindings swallow mouse events if activated */
|
||||
if (ctx.surface && !consumed) {
|
||||
/* Make sure we are sending the events to the surface under the cursor */
|
||||
double sx, sy;
|
||||
cursor_update_common(server, &ctx, /*cursor_has_moved*/ false, &sx, &sy);
|
||||
cursor_update_common(server, &ctx, NULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@
|
|||
#include "common/macros.h"
|
||||
#include "config/keybind.h"
|
||||
#include "config/rcxml.h"
|
||||
#include "cycle.h"
|
||||
#include "idle.h"
|
||||
#include "input/ime.h"
|
||||
#include "input/key-state.h"
|
||||
#include "labwc.h"
|
||||
#include "menu/menu.h"
|
||||
#include "osd.h"
|
||||
#include "session-lock.h"
|
||||
#include "view.h"
|
||||
#include "workspaces.h"
|
||||
|
|
@ -141,17 +141,16 @@ handle_modifiers(struct wl_listener *listener, void *data)
|
|||
overlay_update(seat);
|
||||
}
|
||||
|
||||
bool window_switcher_active = server->input_mode
|
||||
== LAB_INPUT_STATE_WINDOW_SWITCHER;
|
||||
bool cycling = server->input_mode == LAB_INPUT_STATE_CYCLE;
|
||||
|
||||
if ((window_switcher_active || seat->workspace_osd_shown_by_modifier)
|
||||
if ((cycling || seat->workspace_osd_shown_by_modifier)
|
||||
&& !keyboard_get_all_modifiers(seat)) {
|
||||
if (window_switcher_active) {
|
||||
if (cycling) {
|
||||
if (key_state_nr_bound_keys()) {
|
||||
should_cancel_cycling_on_next_key_release = true;
|
||||
} else {
|
||||
should_cancel_cycling_on_next_key_release = false;
|
||||
osd_finish(server, /*switch_focus*/ true);
|
||||
cycle_finish(server, /*switch_focus*/ true);
|
||||
}
|
||||
}
|
||||
if (seat->workspace_osd_shown_by_modifier) {
|
||||
|
|
@ -388,7 +387,7 @@ handle_key_release(struct server *server, uint32_t evdev_keycode)
|
|||
*/
|
||||
if (should_cancel_cycling_on_next_key_release) {
|
||||
should_cancel_cycling_on_next_key_release = false;
|
||||
osd_finish(server, /*switch_focus*/ true);
|
||||
cycle_finish(server, /*switch_focus*/ true);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -461,19 +460,19 @@ handle_cycle_view_key(struct server *server, struct keyinfo *keyinfo)
|
|||
for (int i = 0; i < keyinfo->translated.nr_syms; i++) {
|
||||
if (keyinfo->translated.syms[i] == XKB_KEY_Escape) {
|
||||
/* Esc deactivates window switcher */
|
||||
osd_finish(server, /*switch_focus*/ false);
|
||||
cycle_finish(server, /*switch_focus*/ false);
|
||||
return true;
|
||||
}
|
||||
if (keyinfo->translated.syms[i] == XKB_KEY_Up
|
||||
|| keyinfo->translated.syms[i] == XKB_KEY_Left) {
|
||||
/* Up/Left cycles the window backward */
|
||||
osd_cycle(server, LAB_CYCLE_DIR_BACKWARD);
|
||||
cycle_step(server, LAB_CYCLE_DIR_BACKWARD);
|
||||
return true;
|
||||
}
|
||||
if (keyinfo->translated.syms[i] == XKB_KEY_Down
|
||||
|| keyinfo->translated.syms[i] == XKB_KEY_Right) {
|
||||
/* Down/Right cycles the window forward */
|
||||
osd_cycle(server, LAB_CYCLE_DIR_FORWARD);
|
||||
cycle_step(server, LAB_CYCLE_DIR_FORWARD);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -523,7 +522,7 @@ handle_compositor_keybindings(struct keyboard *keyboard,
|
|||
key_state_store_pressed_key_as_bound(event->keycode);
|
||||
handle_menu_keys(server, &keyinfo.translated);
|
||||
return LAB_KEY_HANDLED_TRUE;
|
||||
} else if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) {
|
||||
} else if (server->input_mode == LAB_INPUT_STATE_CYCLE) {
|
||||
if (handle_cycle_view_key(server, &keyinfo)) {
|
||||
key_state_store_pressed_key_as_bound(event->keycode);
|
||||
return LAB_KEY_HANDLED_TRUE;
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ interactive_anchor_to_cursor(struct server *server, struct wlr_box *geo)
|
|||
if (wlr_box_empty(geo)) {
|
||||
return;
|
||||
}
|
||||
/* Resize grab_box while anchoring it to grab_box.{x,y} */
|
||||
/* Resize grab_box while anchoring it to grab_{x,y} */
|
||||
server->grab_box.x = max_move_scale(server->grab_x, server->grab_box.x,
|
||||
server->grab_box.width, geo->width);
|
||||
server->grab_box.y = max_move_scale(server->grab_y, server->grab_box.y,
|
||||
|
|
@ -186,7 +186,7 @@ edge_from_cursor(struct seat *seat, struct output **dest_output,
|
|||
return false;
|
||||
}
|
||||
|
||||
if (rc.snap_edge_range == 0) {
|
||||
if (rc.snap_edge_range_inner == 0 && rc.snap_edge_range_outer == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -197,9 +197,31 @@ edge_from_cursor(struct seat *seat, struct output **dest_output,
|
|||
}
|
||||
*dest_output = output;
|
||||
|
||||
/* Translate into output local coordinates */
|
||||
double cursor_x = seat->cursor->x;
|
||||
double cursor_y = seat->cursor->y;
|
||||
|
||||
int top_range = rc.snap_edge_range_outer;
|
||||
int bottom_range = rc.snap_edge_range_outer;
|
||||
int left_range = rc.snap_edge_range_outer;
|
||||
int right_range = rc.snap_edge_range_outer;
|
||||
if (wlr_output_layout_adjacent_output(seat->server->output_layout, WLR_DIRECTION_UP,
|
||||
output->wlr_output, cursor_x, cursor_y)) {
|
||||
top_range = rc.snap_edge_range_inner;
|
||||
}
|
||||
if (wlr_output_layout_adjacent_output(seat->server->output_layout, WLR_DIRECTION_DOWN,
|
||||
output->wlr_output, cursor_x, cursor_y)) {
|
||||
bottom_range = rc.snap_edge_range_inner;
|
||||
}
|
||||
if (wlr_output_layout_adjacent_output(seat->server->output_layout, WLR_DIRECTION_LEFT,
|
||||
output->wlr_output, cursor_x, cursor_y)) {
|
||||
left_range = rc.snap_edge_range_inner;
|
||||
}
|
||||
if (wlr_output_layout_adjacent_output(seat->server->output_layout, WLR_DIRECTION_RIGHT,
|
||||
output->wlr_output, cursor_x, cursor_y)) {
|
||||
right_range = rc.snap_edge_range_inner;
|
||||
}
|
||||
|
||||
/* Translate into output local coordinates */
|
||||
wlr_output_layout_output_coords(seat->server->output_layout,
|
||||
output->wlr_output, &cursor_x, &cursor_y);
|
||||
|
||||
|
|
@ -210,13 +232,13 @@ edge_from_cursor(struct seat *seat, struct output **dest_output,
|
|||
int left = cursor_x - area->x;
|
||||
int right = area->x + area->width - cursor_x;
|
||||
|
||||
if (top < rc.snap_edge_range) {
|
||||
if (top < top_range) {
|
||||
*edge1 = LAB_EDGE_TOP;
|
||||
} else if (bottom < rc.snap_edge_range) {
|
||||
} else if (bottom < bottom_range) {
|
||||
*edge1 = LAB_EDGE_BOTTOM;
|
||||
} else if (left < rc.snap_edge_range) {
|
||||
} else if (left < left_range) {
|
||||
*edge1 = LAB_EDGE_LEFT;
|
||||
} else if (right < rc.snap_edge_range) {
|
||||
} else if (right < right_range) {
|
||||
*edge1 = LAB_EDGE_RIGHT;
|
||||
} else {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -49,12 +49,12 @@ endif
|
|||
|
||||
subdir('common')
|
||||
subdir('config')
|
||||
subdir('cycle')
|
||||
subdir('decorations')
|
||||
subdir('foreign-toplevel')
|
||||
subdir('img')
|
||||
subdir('input')
|
||||
subdir('menu')
|
||||
subdir('osd')
|
||||
subdir('protocols')
|
||||
subdir('scaled-buffer')
|
||||
subdir('ssd')
|
||||
|
|
|
|||
|
|
@ -59,13 +59,13 @@ node_menuitem_from_node(struct wlr_scene_node *wlr_scene_node)
|
|||
return (struct menuitem *)node_descriptor->data;
|
||||
}
|
||||
|
||||
struct osd_item *
|
||||
node_osd_item_from_node(struct wlr_scene_node *wlr_scene_node)
|
||||
struct cycle_osd_item *
|
||||
node_cycle_osd_item_from_node(struct wlr_scene_node *wlr_scene_node)
|
||||
{
|
||||
assert(wlr_scene_node->data);
|
||||
struct node_descriptor *node_descriptor = wlr_scene_node->data;
|
||||
assert(node_descriptor->type == LAB_NODE_OSD_ITEM);
|
||||
return (struct osd_item *)node_descriptor->data;
|
||||
assert(node_descriptor->type == LAB_NODE_CYCLE_OSD_ITEM);
|
||||
return (struct cycle_osd_item *)node_descriptor->data;
|
||||
}
|
||||
|
||||
struct ssd_button *
|
||||
|
|
|
|||
374
src/osd/osd.c
374
src/osd/osd.c
|
|
@ -1,374 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#include "osd.h"
|
||||
#include <assert.h>
|
||||
#include <wlr/types/wlr_scene.h>
|
||||
#include <wlr/util/box.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "common/array.h"
|
||||
#include "common/lab-scene-rect.h"
|
||||
#include "common/scene-helpers.h"
|
||||
#include "config/rcxml.h"
|
||||
#include "labwc.h"
|
||||
#include "node.h"
|
||||
#include "output.h"
|
||||
#include "scaled-buffer/scaled-font-buffer.h"
|
||||
#include "scaled-buffer/scaled-icon-buffer.h"
|
||||
#include "ssd.h"
|
||||
#include "theme.h"
|
||||
#include "view.h"
|
||||
|
||||
static void update_osd(struct server *server);
|
||||
|
||||
static void
|
||||
destroy_osd_scenes(struct server *server)
|
||||
{
|
||||
struct output *output;
|
||||
wl_list_for_each(output, &server->outputs, link) {
|
||||
struct osd_item *item, *tmp;
|
||||
wl_list_for_each_safe(item, tmp, &output->osd_scene.items, link) {
|
||||
wl_list_remove(&item->link);
|
||||
free(item);
|
||||
}
|
||||
if (output->osd_scene.tree) {
|
||||
wlr_scene_node_destroy(&output->osd_scene.tree->node);
|
||||
output->osd_scene.tree = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
osd_update_preview_outlines(struct view *view)
|
||||
{
|
||||
/* Create / Update preview outline tree */
|
||||
struct server *server = view->server;
|
||||
struct theme *theme = server->theme;
|
||||
struct lab_scene_rect *rect = view->server->osd_state.preview_outline;
|
||||
if (!rect) {
|
||||
struct lab_scene_rect_options opts = {
|
||||
.border_colors = (float *[3]) {
|
||||
theme->osd_window_switcher_preview_border_color[0],
|
||||
theme->osd_window_switcher_preview_border_color[1],
|
||||
theme->osd_window_switcher_preview_border_color[2],
|
||||
},
|
||||
.nr_borders = 3,
|
||||
.border_width = theme->osd_window_switcher_preview_border_width,
|
||||
};
|
||||
rect = lab_scene_rect_create(&server->scene->tree, &opts);
|
||||
wlr_scene_node_place_above(&rect->tree->node, &server->menu_tree->node);
|
||||
server->osd_state.preview_outline = rect;
|
||||
}
|
||||
|
||||
struct wlr_box geo = ssd_max_extents(view);
|
||||
lab_scene_rect_set_size(rect, geo.width, geo.height);
|
||||
wlr_scene_node_set_position(&rect->tree->node, geo.x, geo.y);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the view to select next in the window switcher.
|
||||
* If !start_view, the second focusable view is returned.
|
||||
*/
|
||||
static struct view *
|
||||
get_next_cycle_view(struct server *server, struct view *start_view,
|
||||
enum lab_cycle_dir dir)
|
||||
{
|
||||
struct view *(*iter)(struct wl_list *head, struct view *view,
|
||||
enum lab_view_criteria criteria);
|
||||
bool forwards = dir == LAB_CYCLE_DIR_FORWARD;
|
||||
iter = forwards ? view_next_no_head_stop : view_prev_no_head_stop;
|
||||
|
||||
enum lab_view_criteria criteria = rc.window_switcher.criteria;
|
||||
|
||||
/*
|
||||
* Views are listed in stacking order, topmost first. Usually the
|
||||
* topmost view is already focused, so when iterating in the forward
|
||||
* direction we pre-select the view second from the top:
|
||||
*
|
||||
* View #1 (on top, currently focused)
|
||||
* View #2 (pre-selected)
|
||||
* View #3
|
||||
* ...
|
||||
*/
|
||||
if (!start_view && forwards) {
|
||||
start_view = iter(&server->views, NULL, criteria);
|
||||
}
|
||||
|
||||
return iter(&server->views, start_view, criteria);
|
||||
}
|
||||
|
||||
void
|
||||
osd_on_view_destroy(struct view *view)
|
||||
{
|
||||
assert(view);
|
||||
struct osd_state *osd_state = &view->server->osd_state;
|
||||
|
||||
if (view->server->input_mode != LAB_INPUT_STATE_WINDOW_SWITCHER) {
|
||||
/* OSD not active, no need for clean up */
|
||||
return;
|
||||
}
|
||||
|
||||
if (osd_state->cycle_view == view) {
|
||||
/*
|
||||
* If we are the current OSD selected view, cycle
|
||||
* to the next because we are dying.
|
||||
*/
|
||||
|
||||
/* Also resets preview node */
|
||||
osd_state->cycle_view = get_next_cycle_view(view->server,
|
||||
osd_state->cycle_view, LAB_CYCLE_DIR_BACKWARD);
|
||||
|
||||
/*
|
||||
* If we cycled back to ourselves, then we have no more windows.
|
||||
* Just close the OSD for good.
|
||||
*/
|
||||
if (osd_state->cycle_view == view || !osd_state->cycle_view) {
|
||||
/* osd_finish() additionally resets cycle_view to NULL */
|
||||
osd_finish(view->server, /*switch_focus*/ false);
|
||||
}
|
||||
}
|
||||
|
||||
if (osd_state->cycle_view) {
|
||||
/* Recreate the OSD to reflect the view has now gone. */
|
||||
destroy_osd_scenes(view->server);
|
||||
update_osd(view->server);
|
||||
}
|
||||
|
||||
if (view->scene_tree) {
|
||||
struct wlr_scene_node *node = &view->scene_tree->node;
|
||||
if (osd_state->preview_anchor == node) {
|
||||
/*
|
||||
* If we are the anchor for the current OSD selected view,
|
||||
* replace the anchor with the node before us.
|
||||
*/
|
||||
osd_state->preview_anchor = lab_wlr_scene_get_prev_node(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
osd_on_cursor_release(struct server *server, struct wlr_scene_node *node)
|
||||
{
|
||||
assert(server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER);
|
||||
|
||||
struct osd_item *item = node_osd_item_from_node(node);
|
||||
server->osd_state.cycle_view = item->view;
|
||||
osd_finish(server, /*switch_focus*/ true);
|
||||
}
|
||||
|
||||
static void
|
||||
restore_preview_node(struct server *server)
|
||||
{
|
||||
struct osd_state *osd_state = &server->osd_state;
|
||||
if (osd_state->preview_node) {
|
||||
wlr_scene_node_reparent(osd_state->preview_node,
|
||||
osd_state->preview_parent);
|
||||
|
||||
if (osd_state->preview_anchor) {
|
||||
wlr_scene_node_place_above(osd_state->preview_node,
|
||||
osd_state->preview_anchor);
|
||||
} else {
|
||||
/* Selected view was the first node */
|
||||
wlr_scene_node_lower_to_bottom(osd_state->preview_node);
|
||||
}
|
||||
|
||||
/* Node was disabled / minimized before, disable again */
|
||||
if (!osd_state->preview_was_enabled) {
|
||||
wlr_scene_node_set_enabled(osd_state->preview_node, false);
|
||||
}
|
||||
if (osd_state->preview_was_shaded) {
|
||||
struct view *view = node_view_from_node(osd_state->preview_node);
|
||||
view_set_shade(view, true);
|
||||
}
|
||||
osd_state->preview_node = NULL;
|
||||
osd_state->preview_parent = NULL;
|
||||
osd_state->preview_anchor = NULL;
|
||||
osd_state->preview_was_shaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
osd_begin(struct server *server, enum lab_cycle_dir direction)
|
||||
{
|
||||
if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
|
||||
return;
|
||||
}
|
||||
|
||||
server->osd_state.cycle_view = get_next_cycle_view(server,
|
||||
server->osd_state.cycle_view, direction);
|
||||
|
||||
seat_focus_override_begin(&server->seat,
|
||||
LAB_INPUT_STATE_WINDOW_SWITCHER, LAB_CURSOR_DEFAULT);
|
||||
update_osd(server);
|
||||
|
||||
/* Update cursor, in case it is within the area covered by OSD */
|
||||
cursor_update_focus(server);
|
||||
}
|
||||
|
||||
void
|
||||
osd_cycle(struct server *server, enum lab_cycle_dir direction)
|
||||
{
|
||||
assert(server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER);
|
||||
|
||||
server->osd_state.cycle_view = get_next_cycle_view(server,
|
||||
server->osd_state.cycle_view, direction);
|
||||
update_osd(server);
|
||||
}
|
||||
|
||||
void
|
||||
osd_finish(struct server *server, bool switch_focus)
|
||||
{
|
||||
if (server->input_mode != LAB_INPUT_STATE_WINDOW_SWITCHER) {
|
||||
return;
|
||||
}
|
||||
|
||||
restore_preview_node(server);
|
||||
/* FIXME: this sets focus to the old surface even with switch_focus=true */
|
||||
seat_focus_override_end(&server->seat);
|
||||
|
||||
struct view *cycle_view = server->osd_state.cycle_view;
|
||||
server->osd_state.preview_node = NULL;
|
||||
server->osd_state.preview_anchor = NULL;
|
||||
server->osd_state.cycle_view = NULL;
|
||||
server->osd_state.preview_was_shaded = false;
|
||||
|
||||
destroy_osd_scenes(server);
|
||||
|
||||
if (server->osd_state.preview_outline) {
|
||||
/* Destroy the whole multi_rect so we can easily react to new themes */
|
||||
wlr_scene_node_destroy(&server->osd_state.preview_outline->tree->node);
|
||||
server->osd_state.preview_outline = NULL;
|
||||
}
|
||||
|
||||
/* Hiding OSD may need a cursor change */
|
||||
cursor_update_focus(server);
|
||||
|
||||
if (switch_focus && cycle_view) {
|
||||
if (rc.window_switcher.unshade) {
|
||||
view_set_shade(cycle_view, false);
|
||||
}
|
||||
desktop_focus_view(cycle_view, /*raise*/ true);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
preview_cycled_view(struct view *view)
|
||||
{
|
||||
assert(view);
|
||||
assert(view->scene_tree);
|
||||
struct osd_state *osd_state = &view->server->osd_state;
|
||||
|
||||
/* Move previous selected node back to its original place */
|
||||
restore_preview_node(view->server);
|
||||
|
||||
/* Store some pointers so we can reset the preview later on */
|
||||
osd_state->preview_node = &view->scene_tree->node;
|
||||
osd_state->preview_parent = view->scene_tree->node.parent;
|
||||
|
||||
/* Remember the sibling right before the selected node */
|
||||
osd_state->preview_anchor = lab_wlr_scene_get_prev_node(
|
||||
osd_state->preview_node);
|
||||
while (osd_state->preview_anchor && !osd_state->preview_anchor->data) {
|
||||
/* Ignore non-view nodes */
|
||||
osd_state->preview_anchor = lab_wlr_scene_get_prev_node(
|
||||
osd_state->preview_anchor);
|
||||
}
|
||||
|
||||
/* Store node enabled / minimized state and force-enable if disabled */
|
||||
osd_state->preview_was_enabled = osd_state->preview_node->enabled;
|
||||
if (!osd_state->preview_was_enabled) {
|
||||
wlr_scene_node_set_enabled(osd_state->preview_node, true);
|
||||
}
|
||||
if (rc.window_switcher.unshade && view->shaded) {
|
||||
view_set_shade(view, false);
|
||||
osd_state->preview_was_shaded = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: This abuses an implementation detail of the always-on-top tree.
|
||||
* Create a permanent server->osd_preview_tree instead that can
|
||||
* also be used as parent for the preview outlines.
|
||||
*/
|
||||
wlr_scene_node_reparent(osd_state->preview_node,
|
||||
view->server->view_tree_always_on_top);
|
||||
|
||||
/* Finally raise selected node to the top */
|
||||
wlr_scene_node_raise_to_top(osd_state->preview_node);
|
||||
}
|
||||
|
||||
static void
|
||||
update_osd_on_output(struct server *server, struct output *output,
|
||||
struct osd_impl *osd_impl, struct wl_array *views)
|
||||
{
|
||||
if (!output_is_usable(output)) {
|
||||
return;
|
||||
}
|
||||
if (!output->osd_scene.tree) {
|
||||
osd_impl->create(output, views);
|
||||
assert(output->osd_scene.tree);
|
||||
}
|
||||
osd_impl->update(output);
|
||||
}
|
||||
|
||||
static void
|
||||
update_osd(struct server *server)
|
||||
{
|
||||
struct wl_array views;
|
||||
wl_array_init(&views);
|
||||
view_array_append(server, &views, rc.window_switcher.criteria);
|
||||
|
||||
struct osd_impl *osd_impl = NULL;
|
||||
switch (rc.window_switcher.style) {
|
||||
case WINDOW_SWITCHER_CLASSIC:
|
||||
osd_impl = &osd_classic_impl;
|
||||
break;
|
||||
case WINDOW_SWITCHER_THUMBNAIL:
|
||||
osd_impl = &osd_thumbnail_impl;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!wl_array_len(&views) || !server->osd_state.cycle_view) {
|
||||
osd_finish(server, /*switch_focus*/ false);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (rc.window_switcher.show) {
|
||||
/* Display the actual OSD */
|
||||
switch (rc.window_switcher.output_criteria) {
|
||||
case OSD_OUTPUT_ALL: {
|
||||
struct output *output;
|
||||
wl_list_for_each(output, &server->outputs, link) {
|
||||
update_osd_on_output(server, output, osd_impl, &views);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OSD_OUTPUT_POINTER:
|
||||
update_osd_on_output(server,
|
||||
output_nearest_to_cursor(server), osd_impl, &views);
|
||||
break;
|
||||
case OSD_OUTPUT_KEYBOARD: {
|
||||
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);
|
||||
}
|
||||
update_osd_on_output(server, output, osd_impl, &views);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rc.window_switcher.preview) {
|
||||
preview_cycled_view(server->osd_state.cycle_view);
|
||||
}
|
||||
|
||||
/* Outline current window */
|
||||
if (rc.window_switcher.outlines) {
|
||||
if (view_is_focusable(server->osd_state.cycle_view)) {
|
||||
osd_update_preview_outlines(server->osd_state.cycle_view);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
wl_array_release(&views);
|
||||
}
|
||||
|
|
@ -182,7 +182,7 @@ handle_output_destroy(struct wl_listener *listener, void *data)
|
|||
wlr_scene_node_destroy(&output->layer_tree[i]->node);
|
||||
}
|
||||
wlr_scene_node_destroy(&output->layer_popup_tree->node);
|
||||
wlr_scene_node_destroy(&output->osd_tree->node);
|
||||
wlr_scene_node_destroy(&output->cycle_osd_tree->node);
|
||||
wlr_scene_node_destroy(&output->session_lock_tree->node);
|
||||
if (output->workspace_osd) {
|
||||
wlr_scene_node_destroy(&output->workspace_osd->node);
|
||||
|
|
@ -542,7 +542,7 @@ handle_new_output(struct wl_listener *listener, void *data)
|
|||
wl_signal_add(&wlr_output->events.request_state, &output->request_state);
|
||||
|
||||
wl_list_init(&output->regions);
|
||||
wl_list_init(&output->osd_scene.items);
|
||||
wl_list_init(&output->cycle_osd.items);
|
||||
|
||||
/*
|
||||
* Create layer-trees (background, bottom, top and overlay) and
|
||||
|
|
@ -553,7 +553,7 @@ handle_new_output(struct wl_listener *listener, void *data)
|
|||
wlr_scene_tree_create(&server->scene->tree);
|
||||
}
|
||||
output->layer_popup_tree = wlr_scene_tree_create(&server->scene->tree);
|
||||
output->osd_tree = wlr_scene_tree_create(&server->scene->tree);
|
||||
output->cycle_osd_tree = wlr_scene_tree_create(&server->scene->tree);
|
||||
output->session_lock_tree = wlr_scene_tree_create(&server->scene->tree);
|
||||
|
||||
/*
|
||||
|
|
@ -577,7 +577,7 @@ handle_new_output(struct wl_listener *listener, void *data)
|
|||
wlr_scene_node_place_below(&output->layer_tree[3]->node, menu_node);
|
||||
wlr_scene_node_place_below(&output->layer_popup_tree->node, menu_node);
|
||||
|
||||
wlr_scene_node_raise_to_top(&output->osd_tree->node);
|
||||
wlr_scene_node_raise_to_top(&output->cycle_osd_tree->node);
|
||||
wlr_scene_node_raise_to_top(&output->session_lock_tree->node);
|
||||
|
||||
/*
|
||||
|
|
|
|||
40
src/seat.c
40
src/seat.c
|
|
@ -848,46 +848,6 @@ seat_set_focus_layer(struct seat *seat, struct wlr_layer_surface_v1 *layer)
|
|||
seat->focused_layer = layer;
|
||||
}
|
||||
|
||||
static void
|
||||
pressed_surface_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct seat *seat = wl_container_of(listener, seat,
|
||||
pressed_surface_destroy);
|
||||
|
||||
/*
|
||||
* Using data directly prevents 'unused variable'
|
||||
* warning when compiling without asserts
|
||||
*/
|
||||
assert(data == seat->pressed.surface);
|
||||
|
||||
seat_reset_pressed(seat);
|
||||
}
|
||||
|
||||
void
|
||||
seat_set_pressed(struct seat *seat, struct cursor_context *ctx)
|
||||
{
|
||||
assert(ctx);
|
||||
assert(ctx->view || ctx->surface);
|
||||
seat_reset_pressed(seat);
|
||||
|
||||
seat->pressed = *ctx;
|
||||
|
||||
if (ctx->surface) {
|
||||
seat->pressed_surface_destroy.notify = pressed_surface_destroy;
|
||||
wl_signal_add(&ctx->surface->events.destroy,
|
||||
&seat->pressed_surface_destroy);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
seat_reset_pressed(struct seat *seat)
|
||||
{
|
||||
if (seat->pressed.surface) {
|
||||
wl_list_remove(&seat->pressed_surface_destroy.link);
|
||||
}
|
||||
seat->pressed = (struct cursor_context){0};
|
||||
}
|
||||
|
||||
void
|
||||
seat_output_layout_changed(struct seat *seat)
|
||||
{
|
||||
|
|
|
|||
31
src/server.c
31
src/server.c
|
|
@ -555,6 +555,7 @@ server_init(struct server *server)
|
|||
|
||||
wl_list_init(&server->views);
|
||||
wl_list_init(&server->unmanaged_surfaces);
|
||||
wl_list_init(&server->cycle.views);
|
||||
|
||||
server->scene = wlr_scene_create();
|
||||
if (!server->scene) {
|
||||
|
|
@ -568,21 +569,21 @@ server_init(struct server *server)
|
|||
* z-order for nodes which cover the whole work-area. For per-output
|
||||
* scene-trees, see handle_new_output() in src/output.c
|
||||
*
|
||||
* | Type | Scene Tree | Per Output | Example
|
||||
* | ----------------- | ---------------- | ---------- | -------
|
||||
* | ext-session | lock-screen | Yes | swaylock
|
||||
* | osd | osd_tree | Yes |
|
||||
* | compositor-menu | menu_tree | No | root-menu
|
||||
* | layer-shell | layer-popups | Yes |
|
||||
* | layer-shell | overlay-layer | Yes |
|
||||
* | layer-shell | top-layer | Yes | waybar
|
||||
* | xwayland-OR | unmanaged | No | dmenu
|
||||
* | xdg-popups | xdg-popups | No |
|
||||
* | toplevels windows | always-on-top | No |
|
||||
* | toplevels windows | normal | No | firefox
|
||||
* | toplevels windows | always-on-bottom | No | pcmanfm-qt --desktop
|
||||
* | layer-shell | bottom-layer | Yes | waybar
|
||||
* | layer-shell | background-layer | Yes | swaybg
|
||||
* | Type | Scene Tree | Per Output | Example
|
||||
* | ------------------- | ---------------- | ---------- | -------
|
||||
* | ext-session | lock-screen | Yes | swaylock
|
||||
* | window switcher OSD | cycle_osd_tree | Yes |
|
||||
* | compositor-menu | menu_tree | No | root-menu
|
||||
* | layer-shell | layer-popups | Yes |
|
||||
* | layer-shell | overlay-layer | Yes |
|
||||
* | layer-shell | top-layer | Yes | waybar
|
||||
* | xwayland-OR | unmanaged | No | dmenu
|
||||
* | xdg-popups | xdg-popups | No |
|
||||
* | toplevels windows | always-on-top | No |
|
||||
* | toplevels windows | normal | No | firefox
|
||||
* | toplevels windows | always-on-bottom | No | pcmanfm-qt --desktop
|
||||
* | layer-shell | bottom-layer | Yes | waybar
|
||||
* | layer-shell | background-layer | Yes | swaybg
|
||||
*/
|
||||
|
||||
if (server->renderer->features.input_color_transform) {
|
||||
|
|
|
|||
|
|
@ -65,7 +65,6 @@ view_impl_apply_geometry(struct view *view, int w, int h)
|
|||
{
|
||||
struct wlr_box *current = &view->current;
|
||||
struct wlr_box *pending = &view->pending;
|
||||
struct wlr_box old = *current;
|
||||
|
||||
/*
|
||||
* Anchor right edge if resizing via left edge.
|
||||
|
|
@ -100,8 +99,4 @@ view_impl_apply_geometry(struct view *view, int w, int h)
|
|||
|
||||
current->width = w;
|
||||
current->height = h;
|
||||
|
||||
if (!wlr_box_equal(current, &old)) {
|
||||
view_moved(view);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
99
src/view.c
99
src/view.c
|
|
@ -15,11 +15,11 @@
|
|||
#include "common/match.h"
|
||||
#include "common/mem.h"
|
||||
#include "config/rcxml.h"
|
||||
#include "cycle.h"
|
||||
#include "foreign-toplevel/foreign.h"
|
||||
#include "input/keyboard.h"
|
||||
#include "labwc.h"
|
||||
#include "menu/menu.h"
|
||||
#include "osd.h"
|
||||
#include "output.h"
|
||||
#include "placement.h"
|
||||
#include "regions.h"
|
||||
|
|
@ -35,6 +35,7 @@
|
|||
|
||||
#if HAVE_XWAYLAND
|
||||
#include <wlr/xwayland.h>
|
||||
#include "xwayland.h"
|
||||
#endif
|
||||
|
||||
struct view *
|
||||
|
|
@ -345,48 +346,6 @@ view_prev(struct wl_list *head, struct view *view, enum lab_view_criteria criter
|
|||
return NULL;
|
||||
}
|
||||
|
||||
struct view *
|
||||
view_next_no_head_stop(struct wl_list *head, struct view *from,
|
||||
enum lab_view_criteria criteria)
|
||||
{
|
||||
assert(head);
|
||||
|
||||
struct wl_list *elm = from ? &from->link : head;
|
||||
|
||||
struct wl_list *end = elm;
|
||||
for (elm = elm->next; elm != end; elm = elm->next) {
|
||||
if (elm == head) {
|
||||
continue;
|
||||
}
|
||||
struct view *view = wl_container_of(elm, view, link);
|
||||
if (matches_criteria(view, criteria)) {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
return from;
|
||||
}
|
||||
|
||||
struct view *
|
||||
view_prev_no_head_stop(struct wl_list *head, struct view *from,
|
||||
enum lab_view_criteria criteria)
|
||||
{
|
||||
assert(head);
|
||||
|
||||
struct wl_list *elm = from ? &from->link : head;
|
||||
|
||||
struct wl_list *end = elm;
|
||||
for (elm = elm->prev; elm != end; elm = elm->prev) {
|
||||
if (elm == head) {
|
||||
continue;
|
||||
}
|
||||
struct view *view = wl_container_of(elm, view, link);
|
||||
if (matches_criteria(view, criteria)) {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
return from;
|
||||
}
|
||||
|
||||
void
|
||||
view_array_append(struct server *server, struct wl_array *views,
|
||||
enum lab_view_criteria criteria)
|
||||
|
|
@ -823,7 +782,7 @@ view_minimize(struct view *view, bool minimized)
|
|||
{
|
||||
assert(view);
|
||||
|
||||
if (view->server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) {
|
||||
if (view->server->input_mode == LAB_INPUT_STATE_CYCLE) {
|
||||
wlr_log(WLR_ERROR, "not minimizing window while window switching");
|
||||
return;
|
||||
}
|
||||
|
|
@ -858,23 +817,7 @@ view_compute_centered_position(struct view *view, const struct wlr_box *ref,
|
|||
int height = h + margin.top + margin.bottom;
|
||||
|
||||
/* If reference box is NULL then center to usable area */
|
||||
if (!ref) {
|
||||
ref = &usable;
|
||||
}
|
||||
*x = ref->x + (ref->width - width) / 2;
|
||||
*y = ref->y + (ref->height - height) / 2;
|
||||
|
||||
/* Fit the view within the usable area */
|
||||
if (*x < usable.x) {
|
||||
*x = usable.x;
|
||||
} else if (*x + width > usable.x + usable.width) {
|
||||
*x = usable.x + usable.width - width;
|
||||
}
|
||||
if (*y < usable.y) {
|
||||
*y = usable.y;
|
||||
} else if (*y + height > usable.y + usable.height) {
|
||||
*y = usable.y + usable.height - height;
|
||||
}
|
||||
box_center(width, height, ref ? ref : &usable, &usable, x, y);
|
||||
|
||||
*x += margin.left;
|
||||
*y += margin.top;
|
||||
|
|
@ -1281,13 +1224,8 @@ view_apply_fullscreen_geometry(struct view *view)
|
|||
assert(output_is_usable(view->output));
|
||||
|
||||
struct wlr_box box = { 0 };
|
||||
wlr_output_effective_resolution(view->output->wlr_output,
|
||||
&box.width, &box.height);
|
||||
double ox = 0, oy = 0;
|
||||
wlr_output_layout_output_coords(view->server->output_layout,
|
||||
view->output->wlr_output, &ox, &oy);
|
||||
box.x -= ox;
|
||||
box.y -= oy;
|
||||
wlr_output_layout_get_box(view->server->output_layout,
|
||||
view->output->wlr_output, &box);
|
||||
view_move_resize(view, box);
|
||||
}
|
||||
|
||||
|
|
@ -1762,6 +1700,12 @@ view_set_fullscreen(struct view *view, bool fullscreen)
|
|||
view_apply_special_geometry(view);
|
||||
}
|
||||
output_set_has_fullscreen_view(view->output, view->fullscreen);
|
||||
/*
|
||||
* Entering/leaving fullscreen might result in a different
|
||||
* scene node ending up under the cursor even if view_moved()
|
||||
* isn't called. Update cursor focus explicitly for that case.
|
||||
*/
|
||||
cursor_update_focus(view->server);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
@ -2304,6 +2248,17 @@ view_move_to_front(struct view *view)
|
|||
move_to_front(view);
|
||||
}
|
||||
|
||||
#if HAVE_XWAYLAND
|
||||
/*
|
||||
* view_move_to_front() is typically called on each mouse press
|
||||
* via Raise action. This means we are restacking windows just
|
||||
* about at the same time we send the mouse press input to the
|
||||
* X server, and creates a race where the mouse press could go
|
||||
* to an incorrect X window depending on timing. To mitigate the
|
||||
* race, perform an explicit flush after restacking.
|
||||
*/
|
||||
xwayland_flush(view->server);
|
||||
#endif
|
||||
cursor_update_focus(view->server);
|
||||
desktop_update_top_layer_visibility(view->server);
|
||||
}
|
||||
|
|
@ -2612,15 +2567,13 @@ view_destroy(struct view *view)
|
|||
server->session_lock_manager->last_active_view = NULL;
|
||||
}
|
||||
|
||||
if (server->seat.pressed.view == view) {
|
||||
seat_reset_pressed(&server->seat);
|
||||
}
|
||||
|
||||
if (view->tiled_region_evacuate) {
|
||||
zfree(view->tiled_region_evacuate);
|
||||
}
|
||||
|
||||
osd_on_view_destroy(view);
|
||||
/* TODO: call this on map/unmap instead */
|
||||
cycle_reinitialize(server);
|
||||
|
||||
undecorate(view);
|
||||
|
||||
view_set_icon(view, NULL, NULL);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
79
src/xdg.c
79
src/xdg.c
|
|
@ -10,6 +10,7 @@
|
|||
#include <wlr/types/wlr_xdg_toplevel_icon_v1.h>
|
||||
#include "buffer.h"
|
||||
#include "common/array.h"
|
||||
#include "common/box.h"
|
||||
#include "common/macros.h"
|
||||
#include "common/mem.h"
|
||||
#include "config/rcxml.h"
|
||||
|
|
@ -129,6 +130,58 @@ do_late_positioning(struct view *view)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
disable_fullscreen_bg(struct view *view)
|
||||
{
|
||||
struct xdg_toplevel_view *xdg_view = xdg_toplevel_view_from_view(view);
|
||||
if (xdg_view->fullscreen_bg) {
|
||||
wlr_scene_node_set_enabled(&xdg_view->fullscreen_bg->node, false);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Centers any fullscreen view smaller than the full output size.
|
||||
* This should be called immediately before view_moved().
|
||||
*/
|
||||
static void
|
||||
center_fullscreen_if_needed(struct view *view)
|
||||
{
|
||||
if (!view->fullscreen || !output_is_usable(view->output)) {
|
||||
disable_fullscreen_bg(view);
|
||||
return;
|
||||
}
|
||||
|
||||
struct wlr_box output_box = {0};
|
||||
wlr_output_layout_get_box(view->server->output_layout,
|
||||
view->output->wlr_output, &output_box);
|
||||
box_center(view->current.width, view->current.height, &output_box,
|
||||
&output_box, &view->current.x, &view->current.y);
|
||||
|
||||
/* Reset pending x/y to computed position also */
|
||||
view->pending.x = view->current.x;
|
||||
view->pending.y = view->current.y;
|
||||
|
||||
if (view->current.width >= output_box.width
|
||||
&& view->current.width >= output_box.height) {
|
||||
disable_fullscreen_bg(view);
|
||||
return;
|
||||
}
|
||||
|
||||
struct xdg_toplevel_view *xdg_view = xdg_toplevel_view_from_view(view);
|
||||
if (!xdg_view->fullscreen_bg) {
|
||||
const float black[4] = {0, 0, 0, 1};
|
||||
xdg_view->fullscreen_bg =
|
||||
wlr_scene_rect_create(view->scene_tree, 0, 0, black);
|
||||
wlr_scene_node_lower_to_bottom(&xdg_view->fullscreen_bg->node);
|
||||
}
|
||||
|
||||
wlr_scene_node_set_position(&xdg_view->fullscreen_bg->node,
|
||||
output_box.x - view->current.x, output_box.y - view->current.y);
|
||||
wlr_scene_rect_set_size(xdg_view->fullscreen_bg,
|
||||
output_box.width, output_box.height);
|
||||
wlr_scene_node_set_enabled(&xdg_view->fullscreen_bg->node, true);
|
||||
}
|
||||
|
||||
/* TODO: reorder so this forward declaration isn't needed */
|
||||
static void set_pending_configure_serial(struct view *view, uint32_t serial);
|
||||
|
||||
|
|
@ -238,6 +291,8 @@ handle_commit(struct wl_listener *listener, void *data)
|
|||
|
||||
if (update_required) {
|
||||
view_impl_apply_geometry(view, size.width, size.height);
|
||||
center_fullscreen_if_needed(view);
|
||||
view_moved(view);
|
||||
|
||||
/*
|
||||
* Some views (e.g., terminals that scale as multiples of rows
|
||||
|
|
@ -335,9 +390,11 @@ handle_configure_timeout(void *data)
|
|||
}
|
||||
view->current.x = view->pending.x;
|
||||
view->current.y = view->pending.y;
|
||||
view_moved(view);
|
||||
}
|
||||
|
||||
center_fullscreen_if_needed(view);
|
||||
view_moved(view);
|
||||
|
||||
/* Re-sync pending view with current state */
|
||||
snap_constraints_update(view);
|
||||
view->pending = view->current;
|
||||
|
|
@ -400,7 +457,7 @@ handle_request_move(struct wl_listener *listener, void *data)
|
|||
* want.
|
||||
*/
|
||||
struct view *view = wl_container_of(listener, view, request_move);
|
||||
if (view == view->server->seat.pressed.view) {
|
||||
if (view == view->server->seat.pressed.ctx.view) {
|
||||
interactive_begin(view, LAB_INPUT_STATE_MOVE, LAB_EDGE_NONE);
|
||||
}
|
||||
}
|
||||
|
|
@ -418,7 +475,7 @@ handle_request_resize(struct wl_listener *listener, void *data)
|
|||
*/
|
||||
struct wlr_xdg_toplevel_resize_event *event = data;
|
||||
struct view *view = wl_container_of(listener, view, request_resize);
|
||||
if (view == view->server->seat.pressed.view) {
|
||||
if (view == view->server->seat.pressed.ctx.view) {
|
||||
interactive_begin(view, LAB_INPUT_STATE_RESIZE, event->edges);
|
||||
}
|
||||
}
|
||||
|
|
@ -545,6 +602,12 @@ xdg_toplevel_view_configure(struct view *view, struct wlr_box geo)
|
|||
} else if (view->pending_configure_serial == 0) {
|
||||
view->current.x = geo.x;
|
||||
view->current.y = geo.y;
|
||||
/*
|
||||
* It's a bit difficult to think of a corner case where
|
||||
* center_fullscreen_if_needed() would actually be needed
|
||||
* here, but including it anyway for completeness.
|
||||
*/
|
||||
center_fullscreen_if_needed(view);
|
||||
view_moved(view);
|
||||
}
|
||||
}
|
||||
|
|
@ -663,6 +726,10 @@ xdg_toplevel_view_set_fullscreen(struct view *view, bool fullscreen)
|
|||
if (serial > 0) {
|
||||
set_pending_configure_serial(view, serial);
|
||||
}
|
||||
/* Disable background fill immediately on leaving fullscreen */
|
||||
if (!fullscreen) {
|
||||
disable_fullscreen_bg(view);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -785,6 +852,9 @@ handle_map(struct wl_listener *listener, void *data)
|
|||
set_initial_position(view);
|
||||
}
|
||||
|
||||
/* Disable background fill at map (paranoid?) */
|
||||
disable_fullscreen_bg(view);
|
||||
|
||||
/*
|
||||
* Set initial "current" position directly before
|
||||
* calling view_moved() to reduce flicker
|
||||
|
|
@ -929,8 +999,6 @@ handle_new_xdg_toplevel(struct wl_listener *listener, void *data)
|
|||
|
||||
assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL);
|
||||
|
||||
wlr_xdg_surface_ping(xdg_surface);
|
||||
|
||||
struct xdg_toplevel_view *xdg_toplevel_view = znew(*xdg_toplevel_view);
|
||||
struct view *view = &xdg_toplevel_view->base;
|
||||
|
||||
|
|
@ -1017,6 +1085,7 @@ handle_new_xdg_toplevel(struct wl_listener *listener, void *data)
|
|||
CONNECT_SIGNAL(xdg_surface, xdg_toplevel_view, new_popup);
|
||||
|
||||
wl_list_insert(&server->views, &view->link);
|
||||
view->creation_id = server->next_view_creation_id++;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
|||
|
|
@ -274,6 +274,7 @@ handle_commit(struct wl_listener *listener, void *data)
|
|||
*/
|
||||
if (current->width != state->width || current->height != state->height) {
|
||||
view_impl_apply_geometry(view, state->width, state->height);
|
||||
view_moved(view);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -289,7 +290,7 @@ handle_request_move(struct wl_listener *listener, void *data)
|
|||
* want.
|
||||
*/
|
||||
struct view *view = wl_container_of(listener, view, request_move);
|
||||
if (view == view->server->seat.pressed.view) {
|
||||
if (view == view->server->seat.pressed.ctx.view) {
|
||||
interactive_begin(view, LAB_INPUT_STATE_MOVE, LAB_EDGE_NONE);
|
||||
}
|
||||
}
|
||||
|
|
@ -307,7 +308,7 @@ handle_request_resize(struct wl_listener *listener, void *data)
|
|||
*/
|
||||
struct wlr_xwayland_resize_event *event = data;
|
||||
struct view *view = wl_container_of(listener, view, request_resize);
|
||||
if (view == view->server->seat.pressed.view) {
|
||||
if (view == view->server->seat.pressed.ctx.view) {
|
||||
interactive_begin(view, LAB_INPUT_STATE_RESIZE, event->edges);
|
||||
}
|
||||
}
|
||||
|
|
@ -1035,6 +1036,7 @@ xwayland_view_create(struct server *server,
|
|||
CONNECT_SIGNAL(xsurface, xwayland_view, map_request);
|
||||
|
||||
wl_list_insert(&view->server->views, &view->link);
|
||||
view->creation_id = view->server->next_view_creation_id++;
|
||||
|
||||
if (xsurface->surface) {
|
||||
handle_associate(&xwayland_view->associate, NULL);
|
||||
|
|
@ -1050,7 +1052,6 @@ handle_new_surface(struct wl_listener *listener, void *data)
|
|||
struct server *server =
|
||||
wl_container_of(listener, server, xwayland_new_surface);
|
||||
struct wlr_xwayland_surface *xsurface = data;
|
||||
wlr_xwayland_surface_ping(xsurface);
|
||||
|
||||
/*
|
||||
* We do not create 'views' for xwayland override_redirect surfaces,
|
||||
|
|
@ -1438,3 +1439,13 @@ xwayland_update_workarea(struct server *server)
|
|||
};
|
||||
wlr_xwayland_set_workareas(server->xwayland, &workarea, 1);
|
||||
}
|
||||
|
||||
void
|
||||
xwayland_flush(struct server *server)
|
||||
{
|
||||
if (!server->xwayland || !server->xwayland->xwm) {
|
||||
return;
|
||||
}
|
||||
|
||||
xcb_flush(wlr_xwayland_get_xwm_connection(server->xwayland));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue