Compare commits

..

No commits in common. "master" and "0.9.3" have entirely different histories.

74 changed files with 1111 additions and 2049 deletions

View file

@ -251,7 +251,7 @@ jobs:
cd "$GITHUB_WORKSPACE"
export CC=gcc
meson setup build-gcc-leak -Dxwayland=enabled -Db_sanitize=address,undefined \
--werror --force-fallback-for=wlroots -Dwlroots:c_std=c11
--werror --force-fallback-for=wlroots
meson compile -C build-gcc-leak
LABWC_LEAK_TEST=1 scripts/ci/smoke-test.sh build-gcc-leak
' | $TARGET
@ -263,7 +263,7 @@ jobs:
cd "$GITHUB_WORKSPACE"
export CC=clang
meson setup build-clang-leak -Dxwayland=enabled -Db_sanitize=address,undefined \
--werror --force-fallback-for=wlroots -Dwlroots:c_std=c11
--werror --force-fallback-for=wlroots
meson compile -C build-clang-leak
LABWC_LEAK_TEST=1 scripts/ci/smoke-test.sh build-clang-leak
' | $TARGET
@ -280,18 +280,14 @@ jobs:
LABWC_RUNS=2 scripts/ci/smoke-test.sh build-gcc-gdb
' | $TARGET
# Void made the foot package depend on the font.
# As this test uses both and the feature itself
# seems to work well lets just skip it for now
#
#- name: Build with gcc - catch no font installed case
# if: matrix.name == 'Void-musl'
# run: |
# echo '
# cd "$GITHUB_WORKSPACE"
# xbps-remove -y dejavu-fonts-ttf
# export CC=gcc
# meson setup build-gcc-nofont -Dxwayland=enabled --werror
# meson compile -C build-gcc-nofont
# LABWC_EXPECT_RETURNCODE=1 scripts/ci/smoke-test.sh build-gcc-nofont
# ' | $TARGET
- name: Build with gcc - catch no font installed case
if: matrix.name == 'Void-musl'
run: |
echo '
cd "$GITHUB_WORKSPACE"
xbps-remove -y dejavu-fonts-ttf
export CC=gcc
meson setup build-gcc-nofont -Dxwayland=enabled --werror
meson compile -C build-gcc-nofont
LABWC_EXPECT_RETURNCODE=1 scripts/ci/smoke-test.sh build-gcc-nofont
' | $TARGET

180
NEWS.md
View file

@ -9,10 +9,6 @@ The format is based on [Keep a Changelog]
| Date | All Changes | wlroots version | lines-of-code |
|------------|---------------|-----------------|---------------|
| 2026-03-15 | [unreleased] | 0.19.2 | 29244 |
| 2026-03-15 | [0.9.6] | 0.19.2 | 29271 |
| 2026-03-04 | [0.9.5] | 0.19.2 | 29251 |
| 2026-02-27 | [0.9.4] | 0.19.2 | 29225 |
| 2025-12-19 | [0.9.3] | 0.19.2 | 28968 |
| 2025-10-10 | [0.9.2] | 0.19.1 | 28818 |
| 2025-08-02 | [0.9.1] | 0.19.0 | 28605 |
@ -44,9 +40,6 @@ The format is based on [Keep a Changelog]
| 2021-03-05 | [0.1.0] | 0.12.0 | 4627 |
[unreleased]: NEWS.md#unreleased
[0.9.6]: NEWS.md#096---2026-03-15
[0.9.5]: NEWS.md#095---2026-03-04
[0.9.4]: NEWS.md#094---2026-02-27
[0.9.3]: NEWS.md#093---2025-12-19
[0.9.2]: NEWS.md#092---2025-10-10
[0.9.1]: NEWS.md#091---2025-08-02
@ -115,138 +108,6 @@ There are some regression warnings worth noting for the switch to wlroots 0.19:
[unreleased-commits]
### Fixed
- Update titlebar title when set to empty and fix an associated issue causing
the title to be misplaced outside of the titlebar when the window is resized.
[#3443] @tokyo4j
- When running nested, exit compositor when last output is destroyed because
in this situation, each output corresponds to a window in the parent
compositor and, unlike DRM outputs, these cannot be reconnected after being
destroyed. [#3440] @marler8997
- Allow policy-based placement to apply when an initially-maximized/fullscreen
view is restored to floating geometry. [#3387] @jlindgren90
## 0.9.6 - 2026-03-15
[0.9.6-commits]
This is an earlier-than-usual release containing bug fixes only. It has been
done on a separate branch (0.9.5-maintenance) to avoid the inclusion of
refactoring and new features.
```
0.9.6 <--- bug-fixes only
/
/
0.9.4--------0.9.5-------- <-- master
```
### Fixed
- Disable outputs where all modes fail [#3428] [#3429] @Consolatis @kode54
- Fix regression in `0.9.4` that causes `NextWindow` action to segfault when
no outputs are connected. [#3425] [#3430] @Consolatis
- Fix typo to allow `xdg-dialog-v1` global [#3426] @xi
### Changed
- Disallow X11 window always-on-top requests by default to fix an issue whereby
Alt+Tab cannot be used to switch to other windows when using some XWayland
Wine games [#3441]. Add window-rule property `allowAlwaysOnTop` to optionally
allow this always-on-top requests. Add the snippet below to the `rc.xml` file
to restore the previous behaviour. [#3445] @Consolatis
```
<windowRules>
<windowRule identifier="*" allowAlwaysOnTop="yes" />
</windowRules>
```
## 0.9.5 - 2026-03-04
[0.9.5-commits]
This is a small bug-fix release.
### Added
- Support XWayland client requests for `always-on-top` and `close` [#3406]
[#3412] @jlindgren90
### Fixed
- Allow layer-shell surfaces on disabled outputs to fix a memory leak in some
clients (e.g. mako) when using `wlopm --off`. [#3410] [#3411] @Consolatis
## 0.9.4 - 2026-02-27
[0.9.4-commits]
Note to package maintainers: This release requires wayland version >=1.22.90
### Added
- Add theme option window.button.hover.bg.color [#3365] @johanmalm
- Implement scrollable window-switcher OSD [#3291] @tokyo4j
- Support the `NextWindow` options listed below [#3271] @tokyo4j
- `<action name="NextWindow" workspace="current|all"/>`
- `<action name="NextWindow" output="all|focused|cursor"/>`
- `<action name="NextWindow" identifier="all|current"/>`
- Add config option `*<desktops><initial>` for setting the active workspace on
startup. [#3265] @5trixs0f
### Fixed
- Increase max client buffer size to 1 MiB to allows slow clients to better deal
with large amounts of events coming in from labwc like from high refresh rate
mice. Fixes issue with clients getting disconnected because the labwc side
client write buffer is full. [#3400] @Consolatis
- Fix two minor cursor position issues during interactive move/resize.
[#3372] @jlindgren90
- Flush X11 connection after focus/activate to mitigate a race where the
XWayland server may generate an unwanted FocusOut event for the newly
activated window, if it receives pointer events over the parallel wayland
connection first. In particular, this fixes an issue with certain fullscreen
applications (such as Minecraft) that self-minimize when receiving FocusOut.
[#3344] [#3362] @jlindgren90
- Cancel interactive move/resize when a window is minimized/unmapped. This is
probably not a common occurrence but it seems cleaner to handle it properly.
[#3350] @jlindgren90
- Fix spurious focus change when window-switcher finishes [#3346] @tokyo4j
- Improve logic for restoring view positions after output disconnect and
reconnect [#3309] [#3310] @jlindgren90 @tokyo4j
- Avoid restacking when a window is already in front; and avoid repeated focus
changes when unminimizing a window with child windows. These changes are not
believed to be visible to the user, but are mentioned here for completeness.
[#3323] [#3325] @jlindgren90
- Do not try to focus an XWayland window that is already in focus. This fixes an
issue with old versions of Minecraft (<=1.5.2). [#3316] @ventureoo
- Halt window-switcher on reconfigure to avoid invalid memory access in some
circumstances. [#3297] @tokyo4j
### Changed
- Decouple the window states always-on-{top,bottom} and omnipresent. Previously
always-on-{top,bottom} windows were always visible on all workspaces
(omnipresent).[#3356] @tokyo4j
- Change the following layer-shell focus semantics:
- Do not allow clients with on-demand keyboard interactivity in the background
and bottom layers steal focus. The reason for this is to avoid desktop
components like `pcmanfm-qt --desktop` from stealing keyboard focus if they
are re-started. [#3167] [#3373] @johanmalm
- Give keyboard focus to xdg-popups of unfocused layer-shell clients in
support of enabling panel menus to be opened by keyboard shortcuts
and get keyboard focus so that they can be operated with the keyboard.
An example use-case is the xfce4-panel applications-menu being opened by
the command xfce4-popup-applicationsmenu. [#3165] @johanmalm
- On popup destory, return focus to whoever had it before the popop [#3165]
@johanmalm @tokyo4j
- Unshade window if selected from client-list-combined-menu [#3345] @Amodio
- Show non-dialog child windows in window-switcher [#3339] @tokyo4j
- `<windowSwitcher allWorkspaces="yes|no">` is deprecated. Instead, use:
`<action name="NextWindow" workspace="current|all">`. [#3271] @tokyo4j
## 0.9.3 - 2025-12-19
[0.9.3-commits]
@ -2658,10 +2519,7 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16
ShowMenu
[Keep a Changelog]: https://keepachangelog.com/en/1.0.0/
[unreleased-commits]: https://github.com/labwc/labwc/compare/0.9.5...HEAD
[0.9.6-commits]: https://github.com/labwc/labwc/compare/0.9.5...0.9.6
[0.9.5-commits]: https://github.com/labwc/labwc/compare/0.9.4...0.9.5
[0.9.4-commits]: https://github.com/labwc/labwc/compare/0.9.3...0.9.4
[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
@ -3143,8 +3001,6 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16
[#3153]: https://github.com/labwc/labwc/pull/3153
[#3157]: https://github.com/labwc/labwc/pull/3157
[#3158]: https://github.com/labwc/labwc/pull/3158
[#3165]: https://github.com/labwc/labwc/pull/3165
[#3167]: https://github.com/labwc/labwc/pull/3167
[#3168]: https://github.com/labwc/labwc/pull/3168
[#3175]: https://github.com/labwc/labwc/pull/3175
[#3176]: https://github.com/labwc/labwc/pull/3176
@ -3167,38 +3023,4 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16
[#3249]: https://github.com/labwc/labwc/pull/3249
[#3251]: https://github.com/labwc/labwc/pull/3251
[#3252]: https://github.com/labwc/labwc/pull/3252
[#3265]: https://github.com/labwc/labwc/pull/3265
[#3267]: https://github.com/labwc/labwc/pull/3267
[#3271]: https://github.com/labwc/labwc/pull/3271
[#3291]: https://github.com/labwc/labwc/pull/3291
[#3297]: https://github.com/labwc/labwc/pull/3297
[#3309]: https://github.com/labwc/labwc/pull/3309
[#3310]: https://github.com/labwc/labwc/pull/3310
[#3316]: https://github.com/labwc/labwc/pull/3316
[#3323]: https://github.com/labwc/labwc/pull/3323
[#3325]: https://github.com/labwc/labwc/pull/3325
[#3339]: https://github.com/labwc/labwc/pull/3339
[#3344]: https://github.com/labwc/labwc/pull/3344
[#3345]: https://github.com/labwc/labwc/pull/3345
[#3346]: https://github.com/labwc/labwc/pull/3346
[#3350]: https://github.com/labwc/labwc/pull/3350
[#3356]: https://github.com/labwc/labwc/pull/3356
[#3362]: https://github.com/labwc/labwc/pull/3362
[#3365]: https://github.com/labwc/labwc/pull/3365
[#3372]: https://github.com/labwc/labwc/pull/3372
[#3373]: https://github.com/labwc/labwc/pull/3373
[#3387]: https://github.com/labwc/labwc/pull/3387
[#3400]: https://github.com/labwc/labwc/pull/3400
[#3406]: https://github.com/labwc/labwc/pull/3406
[#3410]: https://github.com/labwc/labwc/pull/3410
[#3411]: https://github.com/labwc/labwc/pull/3411
[#3412]: https://github.com/labwc/labwc/pull/3412
[#3425]: https://github.com/labwc/labwc/pull/3425
[#3426]: https://github.com/labwc/labwc/pull/3426
[#3428]: https://github.com/labwc/labwc/pull/3428
[#3429]: https://github.com/labwc/labwc/pull/3429
[#3430]: https://github.com/labwc/labwc/pull/3430
[#3440]: https://github.com/labwc/labwc/pull/3440
[#3441]: https://github.com/labwc/labwc/pull/3441
[#3443]: https://github.com/labwc/labwc/pull/3443
[#3445]: https://github.com/labwc/labwc/pull/3445

View file

@ -107,14 +107,12 @@ See [scope] for full details on implemented features.
| video link | date | duration
| ------------------------- | ------------| -------
| [0.9.4-release-video] | 27-Feb-2026 | 3:19
| [0.8.3-release-video] | 03-Mar-2025 | 2:35
| [0.7.2-release-video] | 24-May-2024 | 3:17
| [0.6.0-release-video] | 31-Oct-2022 | 2:48
| [0.3.0-release-video] | 05-Aug-2021 | 1:10
| [pre-0.1.0-release-video] | 25-Feb-2021 | 3:42
[0.9.4-release-video]: https://youtu.be/BY7AFh-MnOk
[0.8.3-release-video]: https://youtu.be/HGzqci_THwA
[0.7.2-release-video]: https://youtu.be/gNIj6VU-IH8
[0.6.0-release-video]: https://youtu.be/guBnx18EQiA

View file

@ -1388,7 +1388,6 @@ nag_setup(struct nag *nag)
}
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGTERM);
sigprocmask(SIG_BLOCK, &mask, NULL);

View file

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

View file

@ -125,25 +125,13 @@ 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" workspace="current" output="all" identifier="all" />*++
*<action name="PreviousWindow" workspace="current" output="all" identifier="all" />*
*<action name="NextWindow" />*++
*<action name="PreviousWindow" />*
Cycle focus to next/previous window, respectively.
Default keybinds for NextWindow and PreviousWindow are Alt-Tab and
Shift-Alt-Tab. While cycling through windows, the arrow keys move the
selected window forwards/backwards and the escape key halts the cycling.
Default keybind for NextWindow is Alt-Tab.
*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".
The arrow keys are used to move forwards/backwards while cycling.
*<action name="Reconfigure" />*
Re-load configuration and theme files.

View file

@ -223,7 +223,7 @@ this is for compatibility with Openbox.
unless an external tool such as `wlr-randr` or `kanshi` is used to
manage outputs.
The reason for the existence of this option is that after losing signal
The reason for the existance of this option is that after losing signal
from the PC (e.g. by `wlopm -off`), some monitors do an input detection
that makes it appear (from the PC side) to disconnect and reconnect a
few seconds later, causing the monitor to turn back on again (as labwc
@ -257,7 +257,7 @@ this is for compatibility with Openbox.
*<core><primarySelection>* [yes|no]
Enable or disable the primary selection clipboard. May only be
configured at launch. This enables autoscroll (middle-click to scroll
up/down) in Chromium and electron based clients without inadvertently
up/down) in Chromium and electron based clients without inadvertantly
pasting the primary clipboard. Default is yes.
*<core><promptCommand>*
@ -339,7 +339,7 @@ this is for compatibility with Openbox.
## WINDOW SWITCHER
```
<windowSwitcher preview="yes" outlines="yes">
<windowSwitcher preview="yes" outlines="yes" allWorkspaces="no">
<osd show="yes" style="classic" output="all" thumbnailLabelFormat="%T" />
<fields>
<field content="icon" width="5%" />
@ -349,13 +349,17 @@ this is for compatibility with Openbox.
</windowSwitcher>
```
*<windowSwitcher preview="" outlines="" unshade="" order="">*
*<windowSwitcher preview="" outlines="" allWorkspaces="" 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.
@ -512,7 +516,7 @@ extending outward from the snapped edge.
*<range><inner>* and *<range><outer>*, and 50 for *<cornerRange>*.
*<snapping><overlay><enabled>* [yes|no]
Show an overlay when snapping a window to an output edge. Default is yes.
Show an overlay when snapping to a window to an edge. Default is yes.
*<snapping><overlay><delay><inner>*++
*<snapping><overlay><delay><outer>*
@ -571,11 +575,6 @@ 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.
@ -1354,13 +1353,6 @@ situation.
*ignoreConfigureRequest* prevents a X11 window to position and size
itself.
*<windowRules><windowRule allowAlwaysOnTop="">* [yes|no|default]
*allowAlwaysOnTop* allows a X11 window to control its always-on-top
state ('above' in X11 terms).
Note: X11 window always-on-top requests are disallowed by default.
This window rule offers a means of allowing it.
*<windowRules><windowRule fixedPosition="">* [yes|no|default]
*fixedPosition* disallows interactive move/resize and prevents
re-positioning in response to changes in reserved output space, which
@ -1436,25 +1428,9 @@ situation.
## ENVIRONMENT VARIABLES
*XCURSOR_PATH*
Specify a colon-separated list of paths to look for mouse cursors in.
Default
~/.local/share/icons:
~/.icons:
/usr/share/icons:
/usr/share/pixmaps:
~/.cursors:
/usr/share/cursors/xorg-x11:
/usr/X11R6/lib/X11/icons:
*XCURSOR_SIZE*
Specify an alternative mouse cursor size in pixels. Requires
XCURSOR_THEME to be set also. Default 24.
*XCURSOR_THEME*
Specify a mouse cursor theme within XCURSOR_PATH.
System cursor themes can typically be found with a command such as:
*XCURSOR_THEME* and *XCURSOR_SIZE* are supported to set cursor theme
and size respectively. The default size is 24. System cursor themes can
typically be found with a command such as:
```
find /usr/share/icons/ -type d -name "cursors"

View file

@ -173,21 +173,6 @@ window.*.title.bg.colorTo.splitTo: #557485
Space between titlebar buttons, in pixels.
Default is 0.
Note: The *window.button.hover* namespace (below) relates to button hover
overlay effects which work with all buttons including svg, png and xbm.
The *window.button.hover* effects are only shown if there is no corresponding
hover icon, like *close_hover-active.svg*.
Openbox only supports hover effects rendered behind buttons which makes sense
for the xbm format, but not for others. For reference, these are defined by
*window.active|inactive.button.STATE.bg* where *STATE* can be any of *pressed*,
*hover* and *disabled*. These are not (yet) supported by labwc and are mentioned
here for comparison only.
*window.button.hover.bg.color*
Color of the hover effect of a titlebar button. Default is #80808020.
*window.button.hover.bg.corner-radius*
Radius of the hover effect of a titlebar button, in pixels.
Default is 0.

View file

@ -141,7 +141,7 @@ example: *LABWC_DEBUG_FOO=1 labwc*.
*LABWC_DEBUG_KEY_STATE*
Enable logging of press and release events for bound keys (generally
key-combinations like *Ctrl-Alt-t*).
key-combinations like *Ctrl-Alt-t*)
# SEE ALSO

View file

@ -77,7 +77,7 @@
</font>
</theme>
<windowSwitcher preview="yes" outlines="yes" unshade="yes">
<windowSwitcher preview="yes" outlines="yes" allWorkspaces="no" 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">
<windowSwitcher preview="no" outlines="no" allWorkspaces="yes">
<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">
<windowSwitcher preview="no" outlines="no" allWorkspaces="yes">
<osd show="yes" />
<fields>
<field content="custom" format="foobar %b %3s %-10o %-20W %-10i %t" width="100%" />
@ -175,7 +175,6 @@
Workspaces can be configured like this:
<desktops>
<popupTime>1000</popupTime>
<initial>Workspace 1</initial>
<names>
<name>Workspace 1</name>
<name>Workspace 2</name>

View file

@ -44,8 +44,7 @@ window.button.width: 26
window.button.height: 26
window.button.spacing: 0
# window button hover overlay
window.button.hover.bg.color: #80808020
# window button hover effect
window.button.hover.bg.corner-radius: 0
# window buttons

View file

@ -4,7 +4,6 @@
#include <stdbool.h>
struct wlr_buffer;
struct wlr_scene_node;
struct wlr_surface;
struct wlr_scene_output;
@ -12,13 +11,6 @@ struct wlr_output_state;
struct wlr_surface *lab_wlr_surface_from_node(struct wlr_scene_node *node);
/* variants of wlr_scene_*_create() functions that don't return NULL */
struct wlr_scene_tree *lab_wlr_scene_tree_create(struct wlr_scene_tree *parent);
struct wlr_scene_rect *lab_wlr_scene_rect_create(struct wlr_scene_tree *parent,
int width, int height, const float color[static 4]);
struct wlr_scene_buffer *lab_wlr_scene_buffer_create(
struct wlr_scene_tree *parent, struct wlr_buffer *buffer);
/**
* lab_get_prev_node - return previous (sibling) node
* @node: node to find the previous node from

View file

@ -90,8 +90,8 @@ bool str_starts_with(const char *s, char needle, const char *ignore_chars);
/**
* str_equal - indicate whether two strings are identical
* @a: first string to compare (can be NULL)
* @b: second string to compare (can be NULL)
* @a: first string to compare
* @b: second string to compare
*
* If both strings are NULL, returns true.
*/

View file

@ -57,11 +57,6 @@ struct usable_area_override {
struct wl_list link; /* struct rcxml.usable_area_overrides */
};
struct workspace_config {
struct wl_list link; /* struct rcxml.workspace_config.workspaces */
char *name;
};
struct rcxml {
/* from command line */
char *config_dir;
@ -173,9 +168,8 @@ struct rcxml {
struct {
int popuptime;
int min_nr_workspaces;
char *initial_workspace_name;
char *prefix;
struct wl_list workspaces; /* struct workspace_config.link */
struct wl_list workspaces; /* struct workspace.link */
} workspace_config;
/* Regions */
@ -183,18 +177,16 @@ struct rcxml {
/* Window Switcher */
struct {
bool show;
bool preview;
bool outlines;
bool unshade;
enum window_switcher_order order;
enum cycle_workspace_filter workspace_filter; /* deprecated */
struct {
bool show;
enum lab_view_criteria criteria;
struct wl_list fields; /* struct window_switcher_field.link */
enum cycle_osd_style style;
enum cycle_output_filter output_filter;
enum cycle_osd_output_criteria output_criteria;
char *thumbnail_label_format;
struct wl_list fields; /* struct cycle_osd_field.link */
} osd;
enum window_switcher_order order;
} window_switcher;
struct wl_list window_rules; /* struct window_rule.link */

View file

@ -62,15 +62,18 @@ enum lab_view_criteria {
/* No filter -> all focusable views */
LAB_VIEW_CRITERIA_NONE = 0,
/* Includes omnipresent (visible on all desktops) views */
/*
* Includes always-on-top views, e.g.
* what is visible on the current workspace
*/
LAB_VIEW_CRITERIA_CURRENT_WORKSPACE = 1 << 0,
/* Positive criteria */
LAB_VIEW_CRITERIA_FULLSCREEN = 1 << 1,
LAB_VIEW_CRITERIA_ALWAYS_ON_TOP = 1 << 2,
LAB_VIEW_CRITERIA_ROOT_TOPLEVEL = 1 << 3,
/* Negative criteria */
LAB_VIEW_CRITERIA_NO_DIALOG = 1 << 5,
LAB_VIEW_CRITERIA_NO_ALWAYS_ON_TOP = 1 << 6,
LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER = 1 << 7,
LAB_VIEW_CRITERIA_NO_OMNIPRESENT = 1 << 8,
@ -114,20 +117,10 @@ enum cycle_osd_style {
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,
enum cycle_osd_output_criteria {
CYCLE_OSD_OUTPUT_ALL,
CYCLE_OSD_OUTPUT_CURSOR,
CYCLE_OSD_OUTPUT_FOCUSED,
};
#endif /* LABWC_CONFIG_TYPES_H */

View file

@ -4,11 +4,8 @@
#include <stdbool.h>
#include <wayland-server-core.h>
#include <wlr/util/box.h>
#include "config/types.h"
struct output;
struct wlr_box;
enum lab_cycle_dir {
LAB_CYCLE_DIR_NONE,
@ -42,47 +39,7 @@ 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 cycle_state {
struct view *selected_view;
struct wl_list views;
struct wl_list osd_outputs; /* struct cycle_osd_output.link */
bool preview_was_shaded;
bool preview_was_enabled;
struct wlr_scene_node *preview_node;
struct wlr_scene_node *preview_dummy;
struct lab_scene_rect *preview_outline;
struct cycle_filter filter;
};
struct cycle_osd_output {
struct wl_list link; /* struct cycle_state.osd_outputs */
struct output *output;
struct wl_listener tree_destroy;
/* set by cycle_osd_impl->init() */
struct wl_list items; /* struct cycle_osd_item.link */
struct wlr_scene_tree *tree;
/* set by cycle_osd_impl->init() and moved by cycle_osd_scroll_update() */
struct wlr_scene_tree *items_tree;
/* used in osd-scroll.c */
struct cycle_osd_scroll_context {
int top_row_idx;
int nr_rows, nr_cols, nr_visible_rows;
int delta_y;
struct wlr_box bar_area;
struct wlr_scene_tree *bar_tree;
struct lab_scene_rect *bar;
} scroll;
struct wl_list link; /* struct rcxml.window_switcher.fields */
};
struct buf;
@ -91,8 +48,7 @@ struct server;
struct wlr_scene_node;
/* Begin window switcher */
void cycle_begin(struct server *server, enum lab_cycle_dir direction,
struct cycle_filter filter);
void cycle_begin(struct server *server, enum lab_cycle_dir direction);
/* Cycle the selected view in the window switcher */
void cycle_step(struct server *server, enum lab_cycle_dir direction);
@ -128,38 +84,17 @@ struct cycle_osd_item {
struct cycle_osd_impl {
/*
* Create a scene-tree of OSD for an output and fill
* osd_output->items.
* Create a scene-tree of OSD for an output.
* This sets output->cycle_osd.{items,tree}.
*/
void (*init)(struct cycle_osd_output *osd_output);
void (*create)(struct output *output);
/*
* Update the OSD to highlight server->cycle.selected_view.
* Update output->cycle_osd.tree to highlight
* server->cycle_state.selected_view.
*/
void (*update)(struct cycle_osd_output *osd_output);
void (*update)(struct output *output);
};
#define SCROLLBAR_W 10
/**
* Initialize the context and scene for scrolling OSD items.
*
* @output: Output of the OSD
* @bar_area: Area where the scrollbar is drawn
* @delta_y: The vertical delta by which items are scrolled (usually item height)
* @nr_cols: Number of columns in the OSD
* @nr_rows: Number of rows in the OSD
* @nr_visible_rows: Number of visible rows in the OSD
* @border_color: Border color of the scrollbar
* @bg_color: Background color of the scrollbar
*/
void cycle_osd_scroll_init(struct cycle_osd_output *osd_output,
struct wlr_box bar_area, int delta_y,
int nr_cols, int nr_rows, int nr_visible_rows,
float *border_color, float *bg_color);
/* Scroll the OSD to show server->cycle.selected_view if needed */
void cycle_osd_scroll_update(struct cycle_osd_output *osd_output);
extern struct cycle_osd_impl cycle_osd_classic_impl;
extern struct cycle_osd_impl cycle_osd_thumbnail_impl;

View file

@ -92,7 +92,7 @@ void cursor_context_save(struct cursor_context_saved *saved_ctx,
* keyboard modifier or when using the Resize action from a keybind.
*/
enum lab_edge cursor_get_resize_edges(struct wlr_cursor *cursor,
const struct cursor_context *ctx);
struct cursor_context *ctx);
/**
* cursor_get_from_edge - translate lab_edge enum to lab_cursor enum

View file

@ -5,7 +5,6 @@
#include <wlr/util/box.h>
#include <wlr/util/log.h>
#include "common/set.h"
#include "cycle.h"
#include "input/cursor.h"
#include "overlay.h"
@ -188,7 +187,6 @@ struct server {
struct wlr_xdg_toplevel_icon_manager_v1 *xdg_toplevel_icon_manager;
struct wl_listener xdg_toplevel_icon_set_icon;
/* front to back order */
struct wl_list views;
uint64_t next_view_creation_id;
struct wl_list unmanaged_surfaces;
@ -224,18 +222,21 @@ struct server {
struct ssd_button *hovered_button;
/* Tree for all non-layer xdg/xwayland-shell surfaces */
struct wlr_scene_tree *workspace_tree;
struct wlr_scene_tree *view_tree;
/*
* Popups need to be rendered above always-on-top views, so we reparent
* them to this dedicated tree
*/
struct wlr_scene_tree *xdg_popup_tree;
/* Tree for all non-layer xdg/xwayland-shell surfaces with always-on-top/below */
struct wlr_scene_tree *view_tree_always_on_top;
struct wlr_scene_tree *view_tree_always_on_bottom;
#if HAVE_XWAYLAND
/* Tree for unmanaged xsurfaces without initialized view (usually popups) */
struct wlr_scene_tree *unmanaged_tree;
#endif
struct wlr_scene_tree *cycle_preview_tree;
/* Tree for built in menu */
struct wlr_scene_tree *menu_tree;
@ -301,7 +302,15 @@ struct server {
struct wlr_security_context_manager_v1 *security_context_manager_v1;
/* Set when in cycle (alt-tab) mode */
struct cycle_state cycle;
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_node *preview_dummy;
struct lab_scene_rect *preview_outline;
} cycle;
struct theme *theme;
@ -372,7 +381,6 @@ void desktop_focus_topmost_view(struct server *server);
void seat_init(struct server *server);
void seat_finish(struct server *server);
void seat_reconfigure(struct server *server);
void seat_force_focus_surface(struct seat *seat, struct wlr_surface *surface);
void seat_focus_surface(struct seat *seat, struct wlr_surface *surface);
void seat_pointer_end_grab(struct seat *seat, struct wlr_surface *surface);
@ -395,10 +403,10 @@ void seat_output_layout_changed(struct seat *seat);
void seat_focus_override_begin(struct seat *seat, enum input_mode input_mode,
enum lab_cursors cursor_shape);
/*
* If restore_focus=true, restore the pointer/keyboard focus which was cleared
* in seat_focus_override_begin().
* Restore the pointer/keyboard focus which was cleared in
* seat_focus_override_begin().
*/
void seat_focus_override_end(struct seat *seat, bool restore_focus);
void seat_focus_override_end(struct seat *seat);
/**
* interactive_anchor_to_cursor() - repositions the geometry to remain
@ -411,8 +419,6 @@ void seat_focus_override_end(struct seat *seat, bool restore_focus);
*/
void interactive_anchor_to_cursor(struct server *server, struct wlr_box *geo);
void interactive_set_grab_context(struct server *server,
const struct cursor_context *ctx);
void interactive_begin(struct view *view, enum input_mode mode,
enum lab_edge edges);
void interactive_finish(struct view *view);

View file

@ -30,7 +30,6 @@ struct lab_layer_popup {
struct wlr_xdg_popup *wlr_popup;
struct wlr_scene_tree *scene_tree;
struct server *server;
bool parent_was_focused;
/* To simplify moving popup nodes from the bottom to the top layer */
struct wlr_box output_toplevel_sx_box;

View file

@ -19,6 +19,11 @@ struct output {
struct wlr_scene_tree *session_lock_tree;
struct wlr_scene_buffer *workspace_osd;
struct cycle_osd_scene {
struct wl_list items; /* struct cycle_osd_item */
struct wlr_scene_tree *tree;
} cycle_osd;
/* In output-relative scene coordinates */
struct wlr_box usable_area;

View file

@ -78,8 +78,7 @@ struct theme {
int window_button_height;
int window_button_spacing;
/* button hover effect */
float window_button_hover_bg_color[4];
/* the corner radius of the hover effect */
int window_button_hover_bg_corner_radius;
/*

View file

@ -82,12 +82,6 @@ enum view_wants_focus {
VIEW_WANTS_FOCUS_UNLIKELY,
};
enum view_layer {
VIEW_LAYER_NORMAL = 0,
VIEW_LAYER_ALWAYS_ON_TOP,
VIEW_LAYER_ALWAYS_ON_BOTTOM,
};
struct view;
struct wlr_surface;
struct foreign_toplevel;
@ -189,7 +183,6 @@ struct view {
bool fullscreen;
bool tearing_hint;
enum lab_tristate force_tearing;
enum view_layer layer;
bool visible_on_all_workspaces;
enum lab_edge tiled;
enum lab_edge edges_visible;
@ -221,20 +214,12 @@ struct view {
*/
struct wlr_box natural_geometry;
/*
* last_placement represents the last view position set by the user.
* output_name and relative_geo are used to keep or restore the view
* position relative to the output and layout_geo is used to keep the
* global position when the output is lost.
* Whenever an output layout change triggers a view relocation, the
* last pending position (or natural geometry) will be saved so the
* view may be restored to its original location on a subsequent layout
* change.
*/
struct {
char *output_name;
/* view geometry in output-relative coordinates */
struct wlr_box relative_geo;
/* view geometry in layout coordinates */
struct wlr_box layout_geo;
} last_placement;
/* Set temporarily when moving view due to layout change */
bool adjusting_for_layout_change;
struct wlr_box last_layout_geometry;
/* used by xdg-shell views */
uint32_t pending_configure_serial;
@ -280,7 +265,6 @@ struct view {
struct wl_signal minimized;
struct wl_signal fullscreened;
struct wl_signal activated; /* bool *activated */
struct wl_signal always_on_top;
/*
* This is emitted when app_id, or icon set via xdg_toplevel_icon
* is updated. This is listened by scaled_icon_buffer.
@ -352,7 +336,7 @@ void view_query_free(struct view_query *view);
bool view_matches_query(struct view *view, struct view_query *query);
/**
* for_each_view() - iterate over all views which match criteria (front to back)
* for_each_view() - iterate over all views which match criteria
* @view: Iterator.
* @head: Head of list to iterate over.
* @criteria: Criteria to match against.
@ -368,7 +352,7 @@ bool view_matches_query(struct view *view, struct view_query *query);
view = view_next(head, view, criteria))
/**
* for_each_view_reverse() - iterate over all views which match criteria (back to front)
* for_each_view_reverse() - iterate over all views which match criteria
* @view: Iterator.
* @head: Head of list to iterate over.
* @criteria: Criteria to match against.
@ -487,6 +471,7 @@ void view_resize_relative(struct view *view,
int left, int right, int top, int bottom);
void view_move_relative(struct view *view, int x, int y);
void view_move(struct view *view, int x, int y);
void view_move_to_cursor(struct view *view);
void view_moved(struct view *view);
void view_minimize(struct view *view, bool minimized);
bool view_compute_centered_position(struct view *view,
@ -515,30 +500,11 @@ int view_effective_height(struct view *view, bool use_pending);
*/
void view_center(struct view *view, const struct wlr_box *ref);
/**
* view_compute_position_by_policy() - compute view placement
* @view: view to be placed
* @geom: floating view geometry to update (in/out)
* @allow_cursor: set to false to ignore center-on-cursor policy
* @policy: placement policy to apply
*
* Computes floating view placement according to configured strategy.
* Unlike view_place_by_policy(), this function doesn't actually move
* the view. It returns false if position could not be computed (e.g.
* if no outputs are connected). In that case, @geom is not modified.
*/
bool view_compute_position_by_policy(struct view *view, struct wlr_box *geom,
bool allow_cursor, enum lab_placement_policy policy);
/**
* view_place_by_policy - apply placement strategy to view
* @view: view to be placed
* @allow_cursor: set to false to ignore center-on-cursor policy
* @policy: placement policy to apply
*
* Places a floating view according to configured placement strategy.
* Clears any maximized/fullscreen/tiled state and restores natural
* geometry of the view before positioning.
*/
void view_place_by_policy(struct view *view, bool allow_cursor,
enum lab_placement_policy policy);
@ -546,13 +512,16 @@ void view_constrain_size_to_that_of_usable_area(struct view *view);
void view_set_maximized(struct view *view, enum view_axis maximized);
void view_set_untiled(struct view *view);
void view_maximize(struct view *view, enum view_axis axis);
void view_maximize(struct view *view, enum view_axis axis,
bool store_natural_geometry);
void view_set_fullscreen(struct view *view, bool fullscreen);
void view_toggle_maximize(struct view *view, enum view_axis axis);
bool view_wants_decorations(struct view *view);
void view_toggle_decorations(struct view *view);
void view_set_layer(struct view *view, enum view_layer layer);
bool view_is_always_on_top(struct view *view);
bool view_is_always_on_bottom(struct view *view);
bool view_is_omnipresent(struct view *view);
void view_toggle_always_on_top(struct view *view);
void view_toggle_always_on_bottom(struct view *view);
void view_toggle_visible_on_all_workspaces(struct view *view);
@ -565,28 +534,19 @@ bool view_titlebar_visible(struct view *view);
void view_set_ssd_mode(struct view *view, enum lab_ssd_mode mode);
void view_set_decorations(struct view *view, enum lab_ssd_mode mode, bool force_ssd);
void view_toggle_fullscreen(struct view *view);
/*
* Saves the window position in view->last_placement. This should be called
* when a view is first mapped or manually moved by the user.
*/
void view_save_last_placement(struct view *view);
/* Restores and adjusts the view's position from the view->last_placement */
void view_invalidate_last_layout_geometry(struct view *view);
void view_adjust_for_layout_change(struct view *view);
void view_move_to_edge(struct view *view, enum lab_edge direction, bool snap_to_windows);
void view_grow_to_edge(struct view *view, enum lab_edge direction);
void view_shrink_to_edge(struct view *view, enum lab_edge direction);
void view_snap_to_edge(struct view *view, enum lab_edge direction,
bool across_outputs, bool combine);
void view_snap_to_region(struct view *view, struct region *region);
bool across_outputs, bool combine, bool store_natural_geometry);
void view_snap_to_region(struct view *view, struct region *region, bool store_natural_geometry);
void view_move_to_output(struct view *view, struct output *output);
void view_move_to_front(struct view *view);
void view_move_to_back(struct view *view);
bool view_is_modal_dialog(struct view *view);
/**
* view_get_modal_dialog() - returns any modal dialog found among this
* view's children or siblings (or possibly this view itself). Applies

View file

@ -40,7 +40,6 @@ struct window_rule {
enum property ignore_configure_request;
enum property fixed_position;
enum property icon_prefer_client;
enum property allow_always_on_top;
struct wl_list link; /* struct rcxml.window_rules */
};

View file

@ -10,13 +10,16 @@ struct seat;
struct server;
struct wlr_scene_tree;
/* Double use: as config in config/rcxml.c and as instance in workspaces.c */
struct workspace {
struct wl_list link; /* struct server.workspaces */
struct wl_list link; /*
* struct server.workspaces
* struct rcxml.workspace_config.workspaces
*/
struct server *server;
char *name;
struct wlr_scene_tree *tree;
struct wlr_scene_tree *view_trees[3];
struct lab_cosmic_workspace *cosmic_workspace;
struct {

View file

@ -44,9 +44,7 @@ struct xwayland_view {
/* Events unique to XWayland views */
struct wl_listener associate;
struct wl_listener dissociate;
struct wl_listener request_above;
struct wl_listener request_activate;
struct wl_listener request_close;
struct wl_listener request_configure;
struct wl_listener set_class;
struct wl_listener set_decorations;
@ -59,12 +57,6 @@ struct xwayland_view {
/* Not (yet) implemented */
/* struct wl_listener set_role; */
/* struct wl_listener set_hints; */
/* Events coming in from the view itself */
struct {
struct wl_listener always_on_top;
} on_view;
};
void xwayland_unmanaged_create(struct server *server,

View file

@ -1,7 +1,7 @@
project(
'labwc',
'c',
version: '0.9.5',
version: '0.9.3',
license: 'GPL-2.0-only',
meson_version: '>=0.59.0',
default_options: [
@ -59,7 +59,7 @@ wlroots = dependency(
wlroots_has_xwayland = wlroots.get_variable('have_xwayland') == 'true'
have_libsfdo = not get_option('icon').disabled()
wayland_server = dependency('wayland-server', version: '>=1.22.90')
wayland_server = dependency('wayland-server', version: '>=1.19.0')
wayland_protos = dependency('wayland-protocols', version: '>=1.39')
xkbcommon = dependency('xkbcommon')
xcb = dependency('xcb', required: get_option('xwayland'))

View file

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

View file

@ -8,10 +8,9 @@ msgstr ""
"Project-Id-Version: labwc\n"
"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n"
"POT-Creation-Date: 2024-09-19 21:09+1000\n"
"PO-Revision-Date: 2026-02-22 21:01+0000\n"
"PO-Revision-Date: 2024-10-27 12:24+0000\n"
"Last-Translator: Peter Jespersen <flywheel@illogical.dk>\n"
"Language-Team: Danish <https://translate.lxqt-project.org/projects/labwc/"
"labwc/da/>\n"
"Language-Team: Danish <https://translate.lxqt-project.org/projects/labwc/labwc/da/>\n"
"Language: da\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -25,7 +24,7 @@ msgstr "Gå derhen..."
#: src/menu/menu.c:1034
msgid "Terminal"
msgstr "Terminal"
msgstr ""
#: src/menu/menu.c:1040
msgid "Reconfigure"

View file

@ -1,81 +0,0 @@
# Labwc pot file
# Copyright (C) 2024
# This file is distributed under the same license as the labwc package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: labwc\n"
"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n"
"POT-Creation-Date: 2024-09-19 21:09+1000\n"
"PO-Revision-Date: 2026-01-18 21:01+0000\n"
"Last-Translator: milotype <mail@milotype.de>\n"
"Language-Team: Croatian <https://translate.lxqt-project.org/projects/labwc/"
"labwc/hr/>\n"
"Language: hr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Generator: Weblate 4.2.1\n"
#: src/menu/menu.c:1016
msgid "Go there..."
msgstr "Idi tamo …"
#: src/menu/menu.c:1034
msgid "Terminal"
msgstr "Terminal"
#: src/menu/menu.c:1040
msgid "Reconfigure"
msgstr "Konfiguriraj ponovo"
#: src/menu/menu.c:1042
msgid "Exit"
msgstr "Zatvori"
#: src/menu/menu.c:1056
msgid "Minimize"
msgstr "Smanji prozor"
#: src/menu/menu.c:1058
msgid "Maximize"
msgstr "Maks. raširi prozor"
#: src/menu/menu.c:1060
msgid "Fullscreen"
msgstr "Cjeloekranski prikaz"
#: src/menu/menu.c:1062
msgid "Roll Up/Down"
msgstr "Pomiči se prema gore/dolje"
#: src/menu/menu.c:1064
msgid "Decorations"
msgstr "Grafički elementi"
#: src/menu/menu.c:1066
msgid "Always on Top"
msgstr "Uvijek gore"
#: src/menu/menu.c:1071
msgid "Move Left"
msgstr "Pomakni ulijevo"
#: src/menu/menu.c:1078
msgid "Move Right"
msgstr "Pomakni udesno"
#: src/menu/menu.c:1083
msgid "Always on Visible Workspace"
msgstr "Uvijek na vidljivom radnom prostoru"
#: src/menu/menu.c:1086
msgid "Workspace"
msgstr "Radni prostor"
#: src/menu/menu.c:1089
msgid "Close"
msgstr "Zatvori"

View file

@ -1,80 +0,0 @@
# Labwc pot file
# Copyright (C) 2024
# This file is distributed under the same license as the labwc package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: labwc\n"
"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n"
"POT-Creation-Date: 2024-09-19 21:09+1000\n"
"PO-Revision-Date: 2026-01-18 21:01+0000\n"
"Last-Translator: Baurzhan Muftakhidinov <baurthefirst@gmail.com>\n"
"Language-Team: Kazakh <https://translate.lxqt-project.org/projects/labwc/"
"labwc/kk/>\n"
"Language: kk\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.2.1\n"
#: src/menu/menu.c:1016
msgid "Go there..."
msgstr "Онда бару..."
#: src/menu/menu.c:1034
msgid "Terminal"
msgstr "Терминал"
#: src/menu/menu.c:1040
msgid "Reconfigure"
msgstr "Қайта баптау"
#: src/menu/menu.c:1042
msgid "Exit"
msgstr "Шығу"
#: src/menu/menu.c:1056
msgid "Minimize"
msgstr "Қайыру"
#: src/menu/menu.c:1058
msgid "Maximize"
msgstr "Жазық қылу"
#: src/menu/menu.c:1060
msgid "Fullscreen"
msgstr "Толық экран"
#: src/menu/menu.c:1062
msgid "Roll Up/Down"
msgstr "Жоғары/Төмен жинау"
#: src/menu/menu.c:1064
msgid "Decorations"
msgstr "Безендірулер"
#: src/menu/menu.c:1066
msgid "Always on Top"
msgstr "Әрқашан жоғарыда"
#: src/menu/menu.c:1071
msgid "Move Left"
msgstr "Солға жылжыту"
#: src/menu/menu.c:1078
msgid "Move Right"
msgstr "Оңға жылжыту"
#: src/menu/menu.c:1083
msgid "Always on Visible Workspace"
msgstr "Әрқашан көрінетін жұмыс орнында"
#: src/menu/menu.c:1086
msgid "Workspace"
msgstr "Жұмыс орны"
#: src/menu/menu.c:1089
msgid "Close"
msgstr "Жабу"

View file

@ -27,6 +27,7 @@ server_protocols = [
wl_protocol_dir / 'staging/ext-image-copy-capture/ext-image-copy-capture-v1.xml',
'cosmic-workspace-unstable-v1.xml',
'wlr-layer-shell-unstable-v1.xml',
'wlr-input-inhibitor-unstable-v1.xml',
'wlr-output-power-management-unstable-v1.xml',
]

View file

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="wlr_input_inhibit_unstable_v1">
<copyright>
Copyright © 2018 Drew DeVault
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that copyright notice and this permission
notice appear in supporting documentation, and that the name of
the copyright holders not be used in advertising or publicity
pertaining to distribution of the software without specific,
written prior permission. The copyright holders make no
representations about the suitability of this software for any
purpose. It is provided "as is" without express or implied
warranty.
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
</copyright>
<interface name="zwlr_input_inhibit_manager_v1" version="1">
<description summary="inhibits input events to other clients">
Clients can use this interface to prevent input events from being sent to
any surfaces but its own, which is useful for example in lock screen
software. It is assumed that access to this interface will be locked down
to whitelisted clients by the compositor.
</description>
<request name="get_inhibitor">
<description summary="inhibit input to other clients">
Activates the input inhibitor. As long as the inhibitor is active, the
compositor will not send input events to other clients.
</description>
<arg name="id" type="new_id" interface="zwlr_input_inhibitor_v1"/>
</request>
<enum name="error">
<entry name="already_inhibited" value="0" summary="an input inhibitor is already in use on the compositor"/>
</enum>
</interface>
<interface name="zwlr_input_inhibitor_v1" version="1">
<description summary="inhibits input to other clients">
While this resource exists, input to clients other than the owner of the
inhibitor resource will not receive input events. The client that owns
this resource will receive all input events normally. The compositor will
also disable all of its own input processing (such as keyboard shortcuts)
while the inhibitor is active.
The compositor may continue to send input events to selected clients,
such as an on-screen keyboard (via the input-method protocol).
</description>
<request name="destroy" type="destructor">
<description summary="destroy the input inhibitor object">
Destroy the inhibitor and allow other clients to receive input.
</description>
</request>
</interface>
</protocol>

View file

@ -20,7 +20,7 @@ run_checks () {
fi
find src/ include/ clients/ t/ \( -name "*.c" -o -name "*.h" \) -type f -print0 |
nice xargs -0 --max-args 16 --max-procs $(nproc) \
nice xargs -0 --max-args 1 --max-procs $(nproc) \
scripts/checkpatch.pl --terse --no-tree --strict --file
return $?
}

View file

@ -68,7 +68,6 @@ struct action_arg_list {
struct wl_list value;
};
/* Warning: Any change in this enum needs to be reflected in the action_names[] array below */
enum action_type {
ACTION_TYPE_INVALID = 0,
ACTION_TYPE_NONE,
@ -98,9 +97,9 @@ enum action_type {
ACTION_TYPE_FOCUS,
ACTION_TYPE_UNFOCUS,
ACTION_TYPE_ICONIFY,
ACTION_TYPE_MOVE,
ACTION_TYPE_RAISE,
ACTION_TYPE_LOWER,
ACTION_TYPE_MOVE,
ACTION_TYPE_RESIZE,
ACTION_TYPE_RESIZE_RELATIVE,
ACTION_TYPE_MOVETO,
@ -138,7 +137,6 @@ enum action_type {
ACTION_TYPE_HIDE_CURSOR,
};
/* Warning: Any change in this array needs to be reflected in the action_type enum above */
const char *action_names[] = {
"INVALID",
"None",
@ -168,9 +166,9 @@ const char *action_names[] = {
"Focus",
"Unfocus",
"Iconify",
"Move",
"Raise",
"Lower",
"Move",
"Resize",
"ResizeRelative",
"MoveTo",
@ -368,44 +366,6 @@ 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);
@ -1148,7 +1108,7 @@ run_action(struct view *view, struct server *server, struct action *action,
}
bool combine = action_get_bool(action, "combine", false);
view_snap_to_edge(view, edge, /*across_outputs*/ true,
combine);
combine, /*store_natural_geometry*/ true);
}
break;
case ACTION_TYPE_GROW_TO_EDGE:
@ -1166,24 +1126,19 @@ run_action(struct view *view, struct server *server, struct action *action,
}
break;
case ACTION_TYPE_NEXT_WINDOW:
case ACTION_TYPE_PREVIOUS_WINDOW: {
enum lab_cycle_dir dir = (action->type == ACTION_TYPE_NEXT_WINDOW) ?
LAB_CYCLE_DIR_FORWARD : LAB_CYCLE_DIR_BACKWARD;
struct cycle_filter filter = {
.workspace = action_get_int(action, "workspace",
rc.window_switcher.workspace_filter),
.output = action_get_int(action, "output",
CYCLE_OUTPUT_ALL),
.app_id = action_get_int(action, "identifier",
CYCLE_APP_ID_ALL),
};
if (server->input_mode == LAB_INPUT_STATE_CYCLE) {
cycle_step(server, dir);
cycle_step(server, LAB_CYCLE_DIR_FORWARD);
} else {
cycle_begin(server, dir, filter);
cycle_begin(server, LAB_CYCLE_DIR_FORWARD);
}
break;
case ACTION_TYPE_PREVIOUS_WINDOW:
if (server->input_mode == LAB_INPUT_STATE_CYCLE) {
cycle_step(server, LAB_CYCLE_DIR_BACKWARD);
} else {
cycle_begin(server, LAB_CYCLE_DIR_BACKWARD);
}
break;
case ACTION_TYPE_RECONFIGURE:
kill(getpid(), SIGHUP);
break;
@ -1205,14 +1160,16 @@ run_action(struct view *view, struct server *server, struct action *action,
if (view) {
enum view_axis axis = action_get_int(action,
"direction", VIEW_AXIS_BOTH);
view_maximize(view, axis);
view_maximize(view, axis,
/*store_natural_geometry*/ true);
}
break;
case ACTION_TYPE_UNMAXIMIZE:
if (view) {
enum view_axis axis = action_get_int(action,
"direction", VIEW_AXIS_BOTH);
view_maximize(view, view->maximized & ~axis);
view_maximize(view, view->maximized & ~axis,
/*store_natural_geometry*/ true);
}
break;
case ACTION_TYPE_TOGGLE_FULLSCREEN:
@ -1262,6 +1219,12 @@ run_action(struct view *view, struct server *server, struct action *action,
view_minimize(view, true);
}
break;
case ACTION_TYPE_MOVE:
if (view) {
interactive_begin(view, LAB_INPUT_STATE_MOVE,
LAB_EDGE_NONE);
}
break;
case ACTION_TYPE_RAISE:
if (view) {
view_move_to_front(view);
@ -1272,20 +1235,6 @@ run_action(struct view *view, struct server *server, struct action *action,
view_move_to_back(view);
}
break;
case ACTION_TYPE_MOVE:
if (view) {
/*
* If triggered by mousebind, grab context was already
* set by button press handling. For keybind-triggered
* Move, set it now from current cursor position.
*/
if (view != server->seat.pressed.ctx.view) {
interactive_set_grab_context(server, ctx);
}
interactive_begin(view, LAB_INPUT_STATE_MOVE,
LAB_EDGE_NONE);
}
break;
case ACTION_TYPE_RESIZE:
if (view) {
/*
@ -1295,13 +1244,9 @@ run_action(struct view *view, struct server *server, struct action *action,
*/
enum lab_edge resize_edges =
action_get_int(action, "direction", LAB_EDGE_NONE);
/*
* If triggered by mousebind, grab context was already
* set by button press handling. For keybind-triggered
* Resize, set it now from current cursor position.
*/
if (view != server->seat.pressed.ctx.view) {
interactive_set_grab_context(server, ctx);
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);
@ -1356,8 +1301,7 @@ run_action(struct view *view, struct server *server, struct action *action,
"Action MoveToCursor is deprecated. To ensure your config works in future labwc "
"releases, please use <action name=\"AutoPlace\" policy=\"cursor\">");
if (view) {
view_place_by_policy(view, /* allow_cursor */ true,
LAB_PLACE_CURSOR);
view_move_to_cursor(view);
}
break;
case ACTION_TYPE_SEND_TO_DESKTOP:
@ -1424,7 +1368,7 @@ run_action(struct view *view, struct server *server, struct action *action,
break;
}
struct output *output = view->output;
if (!output_is_usable(output)) {
if (!output) {
break;
}
const char *region_name = action_get_str(action, "region", NULL);
@ -1439,7 +1383,8 @@ run_action(struct view *view, struct server *server, struct action *action,
view_apply_natural_geometry(view);
break;
}
view_snap_to_region(view, region);
view_snap_to_region(view, region,
/*store_natural_geometry*/ true);
} else {
wlr_log(WLR_ERROR, "Invalid SnapToRegion id: '%s'", region_name);
}
@ -1447,7 +1392,8 @@ run_action(struct view *view, struct server *server, struct action *action,
}
case ACTION_TYPE_UNSNAP:
if (view && !view->fullscreen && !view_is_floating(view)) {
view_maximize(view, VIEW_AXIS_NONE);
view_maximize(view, VIEW_AXIS_NONE,
/* store_natural_geometry */ false);
view_set_untiled(view);
view_apply_natural_geometry(view);
}

View file

@ -3,7 +3,6 @@
#include <assert.h>
#include <wlr/types/wlr_scene.h>
#include "common/mem.h"
#include "common/scene-helpers.h"
struct border_scene {
struct wlr_scene_tree *tree;
@ -27,20 +26,20 @@ lab_scene_rect_create(struct wlr_scene_tree *parent,
rect->border_width = opts->border_width;
rect->nr_borders = opts->nr_borders;
rect->borders = znew_n(rect->borders[0], opts->nr_borders);
rect->tree = lab_wlr_scene_tree_create(parent);
rect->tree = wlr_scene_tree_create(parent);
if (opts->bg_color) {
rect->fill = lab_wlr_scene_rect_create(rect->tree, 0, 0, opts->bg_color);
rect->fill = wlr_scene_rect_create(rect->tree, 0, 0, opts->bg_color);
}
for (int i = 0; i < rect->nr_borders; i++) {
struct border_scene *border = &rect->borders[i];
float *color = opts->border_colors[i];
border->tree = lab_wlr_scene_tree_create(rect->tree);
border->top = lab_wlr_scene_rect_create(border->tree, 0, 0, color);
border->right = lab_wlr_scene_rect_create(border->tree, 0, 0, color);
border->bottom = lab_wlr_scene_rect_create(border->tree, 0, 0, color);
border->left = lab_wlr_scene_rect_create(border->tree, 0, 0, color);
border->tree = wlr_scene_tree_create(rect->tree);
border->top = wlr_scene_rect_create(border->tree, 0, 0, color);
border->right = wlr_scene_rect_create(border->tree, 0, 0, color);
border->bottom = wlr_scene_rect_create(border->tree, 0, 0, color);
border->left = wlr_scene_rect_create(border->tree, 0, 0, color);
}
rect->node_destroy.notify = handle_node_destroy;

View file

@ -5,7 +5,6 @@
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/util/log.h>
#include "common/mem.h"
#include "magnifier.h"
#include "output.h"
@ -25,34 +24,6 @@ lab_wlr_surface_from_node(struct wlr_scene_node *node)
return NULL;
}
struct wlr_scene_tree *
lab_wlr_scene_tree_create(struct wlr_scene_tree *parent)
{
struct wlr_scene_tree *tree = wlr_scene_tree_create(parent);
die_if_null(tree);
return tree;
}
struct wlr_scene_rect *
lab_wlr_scene_rect_create(struct wlr_scene_tree *parent,
int width, int height, const float color[static 4])
{
struct wlr_scene_rect *rect =
wlr_scene_rect_create(parent, width, height, color);
die_if_null(rect);
return rect;
}
struct wlr_scene_buffer *
lab_wlr_scene_buffer_create(struct wlr_scene_tree *parent,
struct wlr_buffer *buffer)
{
struct wlr_scene_buffer *scene_buffer =
wlr_scene_buffer_create(parent, buffer);
die_if_null(scene_buffer);
return scene_buffer;
}
struct wlr_scene_node *
lab_wlr_scene_get_prev_node(struct wlr_scene_node *node)
{

View file

@ -300,8 +300,6 @@ fill_window_rule(xmlNode *node)
set_property(content, &window_rule->ignore_configure_request);
} else if (!strcasecmp(key, "fixedPosition")) {
set_property(content, &window_rule->fixed_position);
} else if (!strcasecmp(key, "allowAlwaysOnTop")) {
set_property(content, &window_rule->allow_always_on_top);
}
}
@ -326,7 +324,7 @@ static void
clear_window_switcher_fields(void)
{
struct cycle_osd_field *field, *field_tmp;
wl_list_for_each_safe(field, field_tmp, &rc.window_switcher.osd.fields, link) {
wl_list_for_each_safe(field, field_tmp, &rc.window_switcher.fields, link) {
wl_list_remove(&field->link);
cycle_osd_field_free(field);
}
@ -336,7 +334,7 @@ static void
fill_window_switcher_field(xmlNode *node)
{
struct cycle_osd_field *field = znew(*field);
wl_list_append(&rc.window_switcher.osd.fields, &field->link);
wl_list_append(&rc.window_switcher.fields, &field->link);
xmlNode *child;
char *key, *content;
@ -686,10 +684,6 @@ 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
@ -697,6 +691,10 @@ 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;
@ -1073,7 +1071,7 @@ entry(xmlNode *node, char *nodename, char *content)
} else if (!strcasecmp(nodename, "prefix.desktops")) {
xstrdup_replace(rc.workspace_config.prefix, content);
} else if (!strcasecmp(nodename, "thumbnailLabelFormat.osd.windowSwitcher")) {
xstrdup_replace(rc.window_switcher.osd.thumbnail_label_format, content);
xstrdup_replace(rc.window_switcher.thumbnail_label_format, content);
} else if (!lab_xml_node_is_leaf(node)) {
/* parse children of nested nodes other than above */
@ -1221,23 +1219,23 @@ 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.osd.show);
set_bool(content, &rc.window_switcher.show);
} else if (!strcasecmp(nodename, "style.osd.windowSwitcher")) {
if (!strcasecmp(content, "classic")) {
rc.window_switcher.osd.style = CYCLE_OSD_STYLE_CLASSIC;
rc.window_switcher.style = CYCLE_OSD_STYLE_CLASSIC;
} else if (!strcasecmp(content, "thumbnail")) {
rc.window_switcher.osd.style = CYCLE_OSD_STYLE_THUMBNAIL;
rc.window_switcher.style = CYCLE_OSD_STYLE_THUMBNAIL;
} else {
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.osd.output_filter = CYCLE_OUTPUT_ALL;
rc.window_switcher.output_criteria = CYCLE_OSD_OUTPUT_ALL;
} else if (!strcasecmp(content, "cursor")) {
rc.window_switcher.osd.output_filter = CYCLE_OUTPUT_CURSOR;
rc.window_switcher.output_criteria = CYCLE_OSD_OUTPUT_CURSOR;
} else if (!strcasecmp(content, "focused")) {
rc.window_switcher.osd.output_filter = CYCLE_OUTPUT_FOCUSED;
rc.window_switcher.output_criteria = CYCLE_OSD_OUTPUT_FOCUSED;
} else {
wlr_log(WLR_ERROR, "Invalid windowSwitcher output '%s': "
"should be one of all|focused|cursor", content);
@ -1254,14 +1252,14 @@ entry(xmlNode *node, char *nodename, char *content)
/* The following two are for backward compatibility only. */
} else if (!strcasecmp(nodename, "show.windowSwitcher")) {
set_bool(content, &rc.window_switcher.osd.show);
set_bool(content, &rc.window_switcher.show);
wlr_log(WLR_ERROR, "<windowSwitcher show=\"\" /> is deprecated."
" Use <windowSwitcher><osd show=\"\" />");
} else if (!strcasecmp(nodename, "style.windowSwitcher")) {
if (!strcasecmp(content, "classic")) {
rc.window_switcher.osd.style = CYCLE_OSD_STYLE_CLASSIC;
rc.window_switcher.style = CYCLE_OSD_STYLE_CLASSIC;
} else if (!strcasecmp(content, "thumbnail")) {
rc.window_switcher.osd.style = CYCLE_OSD_STYLE_THUMBNAIL;
rc.window_switcher.style = CYCLE_OSD_STYLE_THUMBNAIL;
}
wlr_log(WLR_ERROR, "<windowSwitcher style=\"\" /> is deprecated."
" Use <windowSwitcher><osd style=\"\" />");
@ -1271,16 +1269,10 @@ entry(xmlNode *node, char *nodename, char *content)
} else if (!strcasecmp(nodename, "outlines.windowSwitcher")) {
set_bool(content, &rc.window_switcher.outlines);
} else if (!strcasecmp(nodename, "allWorkspaces.windowSwitcher")) {
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;
if (parse_bool(content, -1) == true) {
rc.window_switcher.criteria &=
~LAB_VIEW_CRITERIA_CURRENT_WORKSPACE;
}
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);
@ -1290,7 +1282,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.osd.show);
set_bool(content, &rc.window_switcher.show);
} else if (!strcasecmp(nodename, "preview.windowSwitcher.core")) {
set_bool(content, &rc.window_switcher.preview);
} else if (!strcasecmp(nodename, "outlines.windowSwitcher.core")) {
@ -1298,7 +1290,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.osd.show);
set_bool(content, &rc.window_switcher.show);
wlr_log(WLR_ERROR, "<cycleViewOSD> is deprecated."
" Use <windowSwitcher show=\"\" />");
} else if (!strcasecmp(nodename, "cycleViewPreview.core")) {
@ -1311,13 +1303,11 @@ entry(xmlNode *node, char *nodename, char *content)
" Use <windowSwitcher outlines=\"\" />");
} else if (!strcasecmp(nodename, "name.names.desktops")) {
struct workspace_config *conf = znew(*conf);
conf->name = xstrdup(content);
wl_list_append(&rc.workspace_config.workspaces, &conf->link);
struct workspace *workspace = znew(*workspace);
workspace->name = xstrdup(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")) {
@ -1429,7 +1419,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.osd.fields);
wl_list_init(&rc.window_switcher.fields);
wl_list_init(&rc.window_rules);
wl_list_init(&rc.touch_configs);
}
@ -1494,14 +1484,16 @@ rcxml_init(void)
rc.snap_top_maximize = true;
rc.snap_tiling_events_mode = LAB_TILING_EVENTS_ALWAYS;
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.show = true;
rc.window_switcher.style = CYCLE_OSD_STYLE_CLASSIC;
rc.window_switcher.output_criteria = CYCLE_OSD_OUTPUT_ALL;
rc.window_switcher.thumbnail_label_format = xstrdup("%T");
rc.window_switcher.preview = true;
rc.window_switcher.outlines = true;
rc.window_switcher.unshade = true;
rc.window_switcher.workspace_filter = CYCLE_WORKSPACE_CURRENT;
rc.window_switcher.criteria = LAB_VIEW_CRITERIA_CURRENT_WORKSPACE
| LAB_VIEW_CRITERIA_ROOT_TOPLEVEL
| LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER;
rc.window_switcher.order = WINDOW_SWITCHER_ORDER_FOCUS;
rc.resize_indicator = LAB_RESIZE_INDICATOR_NEVER;
@ -1681,7 +1673,7 @@ load_default_window_switcher_fields(void)
struct cycle_osd_field *field = znew(*field);
field->content = fields[i].content;
field->width = fields[i].width;
wl_list_append(&rc.window_switcher.osd.fields, &field->link);
wl_list_append(&rc.window_switcher.fields, &field->link);
}
}
@ -1786,14 +1778,15 @@ post_processing(void)
}
struct buf b = BUF_INIT;
struct workspace *workspace;
for (int i = nr_workspaces; i < rc.workspace_config.min_nr_workspaces; i++) {
struct workspace_config *conf = znew(*conf);
workspace = znew(*workspace);
if (!string_null_or_empty(rc.workspace_config.prefix)) {
buf_add_fmt(&b, "%s ", rc.workspace_config.prefix);
}
buf_add_fmt(&b, "%d", i + 1);
conf->name = xstrdup(b.data);
wl_list_append(&rc.workspace_config.workspaces, &conf->link);
workspace->name = xstrdup(b.data);
wl_list_append(&rc.workspace_config.workspaces, &workspace->link);
buf_clear(&b);
}
buf_reset(&b);
@ -1801,7 +1794,7 @@ post_processing(void)
if (rc.workspace_config.popuptime == INT_MIN) {
rc.workspace_config.popuptime = 1000;
}
if (!wl_list_length(&rc.window_switcher.osd.fields)) {
if (!wl_list_length(&rc.window_switcher.fields)) {
wlr_log(WLR_INFO, "load default window switcher fields");
load_default_window_switcher_fields();
}
@ -1895,7 +1888,7 @@ validate(void)
/* OSD fields */
int field_width_sum = 0;
struct cycle_osd_field *field, *field_tmp;
wl_list_for_each_safe(field, field_tmp, &rc.window_switcher.osd.fields, link) {
wl_list_for_each_safe(field, field_tmp, &rc.window_switcher.fields, link) {
field_width_sum += field->width;
if (!cycle_osd_field_is_valid(field) || field_width_sum > 100) {
wlr_log(WLR_ERROR, "Deleting invalid window switcher field %p", field);
@ -1970,9 +1963,8 @@ 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.osd.thumbnail_label_format);
zfree(rc.window_switcher.thumbnail_label_format);
clear_title_layout();
@ -2012,7 +2004,7 @@ rcxml_finish(void)
zfree(l);
}
struct workspace_config *w, *w_tmp;
struct workspace *w, *w_tmp;
wl_list_for_each_safe(w, w_tmp, &rc.workspace_config.workspaces, link) {
wl_list_remove(&w->link);
zfree(w->name);

View file

@ -6,7 +6,6 @@
#include <wlr/util/log.h>
#include "common/lab-scene-rect.h"
#include "common/list.h"
#include "common/mem.h"
#include "common/scene-helpers.h"
#include "config/rcxml.h"
#include "labwc.h"
@ -18,7 +17,7 @@
#include "theme.h"
#include "view.h"
static bool init_cycle(struct server *server, struct cycle_filter filter);
static bool init_cycle(struct server *server);
static void update_cycle(struct server *server);
static void destroy_cycle(struct server *server);
@ -40,8 +39,7 @@ update_preview_outlines(struct view *view)
.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->cycle_preview_tree->node);
wlr_scene_node_place_above(&rect->tree->node, &server->menu_tree->node);
server->cycle.preview_outline = rect;
}
@ -95,10 +93,9 @@ cycle_reinitialize(struct server *server)
struct view *selected_view = cycle->selected_view;
struct view *selected_view_prev =
get_next_selected_view(server, LAB_CYCLE_DIR_BACKWARD);
struct cycle_filter filter = cycle->filter;
destroy_cycle(server);
if (init_cycle(server, filter)) {
if (init_cycle(server)) {
/*
* Preserve the selected view (or its previous view) if it's
* still in the cycle list
@ -155,14 +152,13 @@ restore_preview_node(struct server *server)
}
void
cycle_begin(struct server *server, enum lab_cycle_dir direction,
struct cycle_filter filter)
cycle_begin(struct server *server, enum lab_cycle_dir direction)
{
if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
return;
}
if (!init_cycle(server, filter)) {
if (!init_cycle(server)) {
return;
}
@ -204,7 +200,8 @@ cycle_finish(struct server *server, bool switch_focus)
struct view *selected_view = server->cycle.selected_view;
destroy_cycle(server);
seat_focus_override_end(&server->seat, /*restore_focus*/ false);
/* 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);
@ -231,7 +228,7 @@ preview_selected_view(struct view *view)
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 = lab_wlr_scene_rect_create(
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);
@ -245,8 +242,13 @@ preview_selected_view(struct view *view)
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->cycle_preview_tree);
view->server->view_tree_always_on_top);
/* Finally raise selected node to the top */
wlr_scene_node_raise_to_top(cycle->preview_node);
@ -255,7 +257,7 @@ preview_selected_view(struct view *view)
static struct cycle_osd_impl *
get_osd_impl(void)
{
switch (rc.window_switcher.osd.style) {
switch (rc.window_switcher.style) {
case CYCLE_OSD_STYLE_CLASSIC:
return &cycle_osd_classic_impl;
case CYCLE_OSD_STYLE_THUMBNAIL:
@ -264,36 +266,14 @@ get_osd_impl(void)
return NULL;
}
static uint64_t
get_outputs_by_filter(struct server *server,
enum cycle_output_filter output_filter)
static void
create_osd_on_output(struct output *output)
{
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;
if (!output_is_usable(output)) {
return;
}
get_osd_impl()->create(output);
assert(output->cycle_osd.tree);
}
static void
@ -310,51 +290,12 @@ insert_view_ordered_by_age(struct wl_list *views, struct view *new_view)
wl_list_insert(link, &new_view->cycle_link);
}
static void
handle_osd_tree_destroy(struct wl_listener *listener, void *data)
{
struct cycle_osd_output *osd_output =
wl_container_of(listener, osd_output, tree_destroy);
struct cycle_osd_item *item, *tmp;
wl_list_for_each_safe(item, tmp, &osd_output->items, link) {
wl_list_remove(&item->link);
free(item);
}
wl_list_remove(&osd_output->tree_destroy.link);
wl_list_remove(&osd_output->link);
free(osd_output);
}
/* Return false on failure */
static bool
init_cycle(struct server *server, struct cycle_filter filter)
init_cycle(struct server *server)
{
enum lab_view_criteria criteria =
LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER
| LAB_VIEW_CRITERIA_NO_DIALOG;
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 (filter.output != CYCLE_OUTPUT_ALL) {
if (!view->output || !(cycle_outputs & view->output->id_bit)) {
continue;
}
}
if (cycle_app_id && strcmp(view->app_id, cycle_app_id) != 0) {
continue;
}
for_each_view(view, &server->views, rc.window_switcher.criteria) {
if (rc.window_switcher.order == WINDOW_SWITCHER_ORDER_AGE) {
insert_view_ordered_by_age(&server->cycle.views, view);
} else {
@ -365,31 +306,31 @@ init_cycle(struct server *server, struct cycle_filter filter)
wlr_log(WLR_DEBUG, "no views to switch between");
return false;
}
server->cycle.filter = filter;
if (rc.window_switcher.osd.show) {
if (rc.window_switcher.show) {
/* Create OSD */
uint64_t osd_outputs = get_outputs_by_filter(server,
rc.window_switcher.osd.output_filter);
switch (rc.window_switcher.output_criteria) {
case CYCLE_OSD_OUTPUT_ALL: {
struct output *output;
wl_list_for_each(output, &server->outputs, link) {
if (!(osd_outputs & output->id_bit)) {
continue;
create_osd_on_output(output);
}
if (!output_is_usable(output)) {
continue;
break;
}
case CYCLE_OSD_OUTPUT_CURSOR:
create_osd_on_output(output_nearest_to_cursor(server));
break;
case CYCLE_OSD_OUTPUT_FOCUSED: {
struct output *output;
if (server->active_view) {
output = server->active_view->output;
} else {
/* Fallback to pointer, if there is no active_view */
output = output_nearest_to_cursor(server);
}
create_osd_on_output(output);
break;
}
struct cycle_osd_output *osd_output = znew(*osd_output);
wl_list_append(&server->cycle.osd_outputs, &osd_output->link);
osd_output->output = output;
wl_list_init(&osd_output->items);
get_osd_impl()->init(osd_output);
osd_output->tree_destroy.notify = handle_osd_tree_destroy;
wl_signal_add(&osd_output->tree->node.events.destroy,
&osd_output->tree_destroy);
}
}
@ -401,10 +342,12 @@ update_cycle(struct server *server)
{
struct cycle_state *cycle = &server->cycle;
if (rc.window_switcher.osd.show) {
struct cycle_osd_output *osd_output;
wl_list_for_each(osd_output, &cycle->osd_outputs, link) {
get_osd_impl()->update(osd_output);
if (rc.window_switcher.show) {
struct output *output;
wl_list_for_each(output, &server->outputs, link) {
if (output->cycle_osd.tree) {
get_osd_impl()->update(output);
}
}
}
@ -414,8 +357,8 @@ update_cycle(struct server *server)
/* Outline current window */
if (rc.window_switcher.outlines) {
if (view_is_focusable(cycle->selected_view)) {
update_preview_outlines(cycle->selected_view);
if (view_is_focusable(server->cycle.selected_view)) {
update_preview_outlines(server->cycle.selected_view);
}
}
}
@ -424,25 +367,31 @@ update_cycle(struct server *server)
static void
destroy_cycle(struct server *server)
{
struct cycle_osd_output *osd_output, *tmp;
wl_list_for_each_safe(osd_output, tmp, &server->cycle.osd_outputs, link) {
/* calls handle_osd_tree_destroy() */
wlr_scene_node_destroy(&osd_output->tree->node);
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, *tmp2;
wl_list_for_each_safe(view, tmp2, &server->cycle.views, cycle_link) {
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 = (struct cycle_state){0};
wl_list_init(&server->cycle.views);
wl_list_init(&server->cycle.osd_outputs);
server->cycle.selected_view = NULL;
}

View file

@ -2,6 +2,5 @@ labwc_sources += files(
'cycle.c',
'osd-classic.c',
'osd-field.c',
'osd-scroll.c',
'osd-thumbnail.c',
)

View file

@ -9,7 +9,6 @@
#include "common/lab-scene-rect.h"
#include "common/list.h"
#include "common/mem.h"
#include "common/scene-helpers.h"
#include "common/string-helpers.h"
#include "config/rcxml.h"
#include "cycle.h"
@ -37,7 +36,7 @@ create_fields_scene(struct server *server, struct view *view,
&theme->osd_window_switcher_classic;
struct cycle_osd_field *field;
wl_list_for_each(field, &rc.window_switcher.osd.fields, link) {
wl_list_for_each(field, &rc.window_switcher.fields, link) {
int field_width = field_widths_sum * field->width / 100.0;
struct wlr_scene_node *node = NULL;
int height = -1;
@ -78,9 +77,10 @@ create_fields_scene(struct server *server, struct view *view,
}
static void
cycle_osd_classic_init(struct cycle_osd_output *osd_output)
cycle_osd_classic_create(struct output *output)
{
struct output *output = osd_output->output;
assert(!output->cycle_osd.tree && wl_list_empty(&output->cycle_osd.items));
struct server *server = output->server;
struct theme *theme = server->theme;
struct window_switcher_classic_theme *switcher_theme =
@ -98,18 +98,13 @@ cycle_osd_classic_init(struct cycle_osd_output *osd_output)
if (switcher_theme->width_is_percent) {
w = output_box.width * switcher_theme->width / 100;
}
int workspace_name_h = 0;
int h = nr_views * switcher_theme->item_height + 2 * padding;
if (show_workspace) {
/* workspace indicator */
workspace_name_h = switcher_theme->item_height;
h += switcher_theme->item_height;
}
int nr_visible_views = (output_box.height - workspace_name_h - 2 * padding)
/ switcher_theme->item_height;
nr_visible_views = MIN(nr_visible_views, nr_views);
int h = workspace_name_h + nr_visible_views * switcher_theme->item_height
+ 2 * padding;
osd_output->tree = lab_wlr_scene_tree_create(output->cycle_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;
@ -123,7 +118,7 @@ cycle_osd_classic_init(struct cycle_osd_output *osd_output)
.width = w,
.height = h,
};
lab_scene_rect_create(osd_output->tree, &bg_opts);
lab_scene_rect_create(output->cycle_osd.tree, &bg_opts);
int y = padding;
@ -141,7 +136,7 @@ cycle_osd_classic_init(struct cycle_osd_output *osd_output)
}
struct scaled_font_buffer *font_buffer =
scaled_font_buffer_create(osd_output->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,
@ -149,7 +144,7 @@ cycle_osd_classic_init(struct cycle_osd_output *osd_output)
y += switcher_theme->item_height;
}
int nr_fields = wl_list_length(&rc.window_switcher.osd.fields);
int nr_fields = wl_list_length(&rc.window_switcher.fields);
/* This is the width of the area available for text fields */
int field_widths_sum = w - 2 * padding
@ -160,17 +155,13 @@ cycle_osd_classic_init(struct cycle_osd_output *osd_output)
goto error;
}
float *active_bg_color = switcher_theme->item_active_bg_color;
float *active_border_color = switcher_theme->item_active_border_color;
osd_output->items_tree = lab_wlr_scene_tree_create(osd_output->tree);
/* Draw text for each node */
struct view *view;
wl_list_for_each(view, &server->cycle.views, cycle_link) {
struct cycle_osd_classic_item *item = znew(*item);
wl_list_append(&osd_output->items, &item->base.link);
wl_list_append(&output->cycle_osd.items, &item->base.link);
item->base.view = view;
item->base.tree = lab_wlr_scene_tree_create(osd_output->items_tree);
item->base.tree = wlr_scene_tree_create(output->cycle_osd.tree);
node_descriptor_create(&item->base.tree->node,
LAB_NODE_CYCLE_OSD_ITEM, NULL, item);
/*
@ -192,10 +183,13 @@ cycle_osd_classic_init(struct cycle_osd_output *osd_output)
int x = padding
+ switcher_theme->item_active_border_width
+ switcher_theme->item_padding_x;
item->normal_tree = lab_wlr_scene_tree_create(item->base.tree);
item->active_tree = lab_wlr_scene_tree_create(item->base.tree);
item->normal_tree = wlr_scene_tree_create(item->base.tree);
item->active_tree = wlr_scene_tree_create(item->base.tree);
wlr_scene_node_set_enabled(&item->active_tree->node, false);
float *active_bg_color = switcher_theme->item_active_bg_color;
float *active_border_color = switcher_theme->item_active_border_color;
/* Highlight around selected window's item */
struct lab_scene_rect_options highlight_opts = {
.border_colors = (float *[1]) {active_border_color},
@ -210,7 +204,7 @@ cycle_osd_classic_init(struct cycle_osd_output *osd_output)
wlr_scene_node_set_position(&highlight_rect->tree->node, padding, y);
/* hitbox for mouse clicks */
struct wlr_scene_rect *hitbox = lab_wlr_scene_rect_create(item->base.tree,
struct wlr_scene_rect *hitbox = wlr_scene_rect_create(item->base.tree,
w - 2 * padding, switcher_theme->item_height, (float[4]) {0});
wlr_scene_node_set_position(&hitbox->node, padding, y);
@ -222,39 +216,25 @@ cycle_osd_classic_init(struct cycle_osd_output *osd_output)
y += switcher_theme->item_height;
}
struct wlr_box scrollbar_area = {
.x = w - padding - SCROLLBAR_W,
.y = padding,
.width = SCROLLBAR_W,
.height = h - 2 * padding,
};
cycle_osd_scroll_init(osd_output, scrollbar_area,
switcher_theme->item_height,
/*nr_cols*/ 1, /*nr_rows*/ nr_views, nr_visible_views,
active_border_color, active_bg_color);
error:;
/* Center OSD */
wlr_scene_node_set_position(&osd_output->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
cycle_osd_classic_update(struct cycle_osd_output *osd_output)
cycle_osd_classic_update(struct output *output)
{
struct server *server = osd_output->output->server;
cycle_osd_scroll_update(osd_output);
struct cycle_osd_classic_item *item;
wl_list_for_each(item, &osd_output->items, base.link) {
bool active = item->base.view == server->cycle.selected_view;
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 cycle_osd_impl cycle_osd_classic_impl = {
.init = cycle_osd_classic_init,
.create = cycle_osd_classic_create,
.update = cycle_osd_classic_update,
};

View file

@ -1,96 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <assert.h>
#include <wlr/types/wlr_scene.h>
#include "common/lab-scene-rect.h"
#include "common/scene-helpers.h"
#include "labwc.h"
#include "cycle.h"
#include "output.h"
void
cycle_osd_scroll_init(struct cycle_osd_output *osd_output, struct wlr_box bar_area,
int delta_y, int nr_cols, int nr_rows, int nr_visible_rows,
float *border_color, float *bg_color)
{
if (nr_visible_rows >= nr_rows) {
/* OSD doesn't have so many windows to scroll through */
return;
}
struct cycle_osd_scroll_context *scroll = &osd_output->scroll;
scroll->nr_cols = nr_cols;
scroll->nr_rows = nr_rows;
scroll->nr_visible_rows = nr_visible_rows;
scroll->top_row_idx = 0;
scroll->bar_area = bar_area;
scroll->delta_y = delta_y;
scroll->bar_tree = lab_wlr_scene_tree_create(osd_output->tree);
wlr_scene_node_set_position(&scroll->bar_tree->node,
bar_area.x, bar_area.y);
struct lab_scene_rect_options scrollbar_opts = {
.border_colors = (float *[1]) { border_color },
.nr_borders = 1,
.border_width = 1,
.bg_color = bg_color,
.width = bar_area.width,
.height = bar_area.height * nr_visible_rows / nr_rows,
};
scroll->bar = lab_scene_rect_create(scroll->bar_tree, &scrollbar_opts);
}
static int
get_cycle_idx(struct cycle_osd_output *osd_output)
{
struct server *server = osd_output->output->server;
int idx = 0;
struct cycle_osd_item *item;
wl_list_for_each(item, &osd_output->items, link) {
if (item->view == server->cycle.selected_view) {
return idx;
}
idx++;
}
assert(false && "selected view not found in items");
return -1;
}
void
cycle_osd_scroll_update(struct cycle_osd_output *osd_output)
{
struct cycle_osd_scroll_context *scroll = &osd_output->scroll;
if (!scroll->bar) {
return;
}
int cycle_idx = get_cycle_idx(osd_output);
/* Update the range of visible rows */
int bottom_row_idx = scroll->top_row_idx + scroll->nr_visible_rows;
while (cycle_idx < scroll->top_row_idx * scroll->nr_cols) {
scroll->top_row_idx--;
bottom_row_idx--;
}
while (cycle_idx >= bottom_row_idx * scroll->nr_cols) {
scroll->top_row_idx++;
bottom_row_idx++;
}
/* Vertically move scrollbar by (bar height) / (# of total rows) */
wlr_scene_node_set_position(&scroll->bar->tree->node, 0,
scroll->bar_area.height * scroll->top_row_idx / scroll->nr_rows);
/* Vertically move items */
wlr_scene_node_set_position(&osd_output->items_tree->node, 0,
-scroll->delta_y * scroll->top_row_idx);
/* Hide items outside of visible area */
int idx = 0;
struct cycle_osd_item *item;
wl_list_for_each(item, &osd_output->items, link) {
bool visible = idx >= scroll->top_row_idx * scroll->nr_cols
&& idx < bottom_row_idx * scroll->nr_cols;
wlr_scene_node_set_enabled(&item->tree->node, visible);
idx++;
}
}

View file

@ -10,7 +10,6 @@
#include "common/lab-scene-rect.h"
#include "common/list.h"
#include "common/mem.h"
#include "common/scene-helpers.h"
#include "cycle.h"
#include "labwc.h"
#include "node.h"
@ -104,7 +103,7 @@ create_label(struct wlr_scene_tree *parent, struct view *view,
{
struct buf buf = BUF_INIT;
cycle_osd_field_set_custom(&buf, view,
rc.window_switcher.osd.thumbnail_label_format);
rc.window_switcher.thumbnail_label_format);
struct scaled_font_buffer *buffer =
scaled_font_buffer_create(parent);
scaled_font_buffer_update(buffer, buf.data,
@ -118,9 +117,9 @@ create_label(struct wlr_scene_tree *parent, struct view *view,
static struct cycle_osd_thumbnail_item *
create_item_scene(struct wlr_scene_tree *parent, struct view *view,
struct cycle_osd_output *osd_output)
struct output *output)
{
struct server *server = osd_output->output->server;
struct server *server = output->server;
struct theme *theme = server->theme;
struct window_switcher_thumbnail_theme *switcher_theme =
&theme->osd_window_switcher_thumbnail;
@ -138,8 +137,8 @@ create_item_scene(struct wlr_scene_tree *parent, struct view *view,
}
struct cycle_osd_thumbnail_item *item = znew(*item);
wl_list_append(&osd_output->items, &item->base.link);
struct wlr_scene_tree *tree = lab_wlr_scene_tree_create(parent);
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_CYCLE_OSD_ITEM, NULL, item);
item->base.tree = tree;
item->base.view = view;
@ -156,14 +155,14 @@ create_item_scene(struct wlr_scene_tree *parent, struct view *view,
item->active_bg = lab_scene_rect_create(tree, &opts);
/* hitbox for mouse clicks */
lab_wlr_scene_rect_create(tree, switcher_theme->item_width,
wlr_scene_rect_create(tree, switcher_theme->item_width,
switcher_theme->item_height, (float[4]) {0});
/* thumbnail */
struct wlr_buffer *thumb_buffer = render_thumb(osd_output->output, view);
struct wlr_buffer *thumb_buffer = render_thumb(output, view);
if (thumb_buffer) {
struct wlr_scene_buffer *thumb_scene_buffer =
lab_wlr_scene_buffer_create(tree, thumb_buffer);
wlr_scene_buffer_create(tree, thumb_buffer);
wlr_buffer_drop(thumb_buffer);
struct wlr_box thumb_box = box_fit_within(
thumb_buffer->width, thumb_buffer->height,
@ -195,10 +194,9 @@ create_item_scene(struct wlr_scene_tree *parent, struct view *view,
}
static void
get_items_geometry(struct output *output, int nr_thumbs,
int *nr_cols, int *nr_rows, int *nr_visible_rows)
get_items_geometry(struct output *output, struct theme *theme,
int nr_thumbs, int *nr_rows, int *nr_cols)
{
struct theme *theme = output->server->theme;
struct window_switcher_thumbnail_theme *switcher_theme =
&theme->osd_window_switcher_thumbnail;
int output_width, output_height;
@ -225,35 +223,32 @@ get_items_geometry(struct output *output, int nr_thumbs,
(*nr_rows)++;
*nr_cols = ceilf((float)nr_thumbs / *nr_rows);
}
*nr_visible_rows = MIN(*nr_rows,
(output_height - 2 * padding) / switcher_theme->item_height);
}
static void
cycle_osd_thumbnail_init(struct cycle_osd_output *osd_output)
cycle_osd_thumbnail_create(struct output *output)
{
struct output *output = osd_output->output;
assert(!output->cycle_osd.tree && wl_list_empty(&output->cycle_osd.items));
struct server *server = output->server;
struct theme *theme = server->theme;
struct window_switcher_thumbnail_theme *switcher_theme =
&theme->osd_window_switcher_thumbnail;
int padding = theme->osd_border_width + switcher_theme->padding;
osd_output->tree = lab_wlr_scene_tree_create(output->cycle_osd_tree);
osd_output->items_tree = lab_wlr_scene_tree_create(osd_output->tree);
output->cycle_osd.tree = wlr_scene_tree_create(output->cycle_osd_tree);
int nr_views = wl_list_length(&server->cycle.views);
assert(nr_views > 0);
int nr_cols, nr_rows, nr_visible_rows;
get_items_geometry(output, nr_views, &nr_cols, &nr_rows, &nr_visible_rows);
int nr_rows, nr_cols;
get_items_geometry(output, theme, nr_views, &nr_rows, &nr_cols);
/* items */
struct view *view;
int index = 0;
wl_list_for_each(view, &server->cycle.views, cycle_link) {
struct cycle_osd_thumbnail_item *item = create_item_scene(
osd_output->items_tree, view, osd_output);
output->cycle_osd.tree, view, output);
if (!item) {
break;
}
@ -263,31 +258,17 @@ cycle_osd_thumbnail_init(struct cycle_osd_output *osd_output)
index++;
}
int items_width = switcher_theme->item_width * nr_cols;
int items_height = switcher_theme->item_height * nr_visible_rows;
struct wlr_box scrollbar_area = {
.x = padding + items_width - SCROLLBAR_W,
.y = padding,
.width = SCROLLBAR_W,
.height = items_height,
};
cycle_osd_scroll_init(osd_output, scrollbar_area,
switcher_theme->item_height, nr_cols, nr_rows, nr_visible_rows,
switcher_theme->item_active_border_color,
switcher_theme->item_active_bg_color);
/* background */
struct lab_scene_rect_options bg_opts = {
.border_colors = (float *[1]) { theme->osd_border_color },
.nr_borders = 1,
.border_width = theme->osd_border_width,
.bg_color = theme->osd_bg_color,
.width = items_width + 2 * padding,
.height = items_height + 2 * padding,
.width = nr_cols * switcher_theme->item_width + 2 * padding,
.height = nr_rows * switcher_theme->item_height + 2 * padding,
};
struct lab_scene_rect *bg =
lab_scene_rect_create(osd_output->tree, &bg_opts);
lab_scene_rect_create(output->cycle_osd.tree, &bg_opts);
wlr_scene_node_lower_to_bottom(&bg->tree->node);
/* center */
@ -296,18 +277,15 @@ cycle_osd_thumbnail_init(struct cycle_osd_output *osd_output)
&output_box);
int lx = output_box.x + (output_box.width - bg_opts.width) / 2;
int ly = output_box.y + (output_box.height - bg_opts.height) / 2;
wlr_scene_node_set_position(&osd_output->tree->node, lx, ly);
wlr_scene_node_set_position(&output->cycle_osd.tree->node, lx, ly);
}
static void
cycle_osd_thumbnail_update(struct cycle_osd_output *osd_output)
cycle_osd_thumbnail_update(struct output *output)
{
struct server *server = osd_output->output->server;
cycle_osd_scroll_update(osd_output);
struct cycle_osd_thumbnail_item *item;
wl_list_for_each(item, &osd_output->items, base.link) {
bool active = (item->base.view == server->cycle.selected_view);
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);
@ -317,6 +295,6 @@ cycle_osd_thumbnail_update(struct cycle_osd_output *osd_output)
}
struct cycle_osd_impl cycle_osd_thumbnail_impl = {
.init = cycle_osd_thumbnail_init,
.create = cycle_osd_thumbnail_create,
.update = cycle_osd_thumbnail_update,
};

View file

@ -88,56 +88,33 @@ get_view_part(struct view *view, struct wlr_scene_node *node)
return ssd_debug_get_node_name(view->ssd, node);
}
static struct workspace *
get_workspace_from_node(struct server *server, struct wlr_scene_node *node)
{
struct workspace *workspace;
wl_list_for_each(workspace, &server->workspaces.all, link) {
if (&workspace->tree->node == node) {
return workspace;
}
}
return NULL;
}
static const char *
get_special(struct server *server, struct wlr_scene_node *node)
{
struct wlr_scene_tree *grand_parent =
node->parent ? node->parent->node.parent : NULL;
struct wlr_scene_tree *grand_grand_parent =
grand_parent ? grand_parent->node.parent : NULL;
if (node == &server->scene->tree.node) {
return "server->scene";
}
if (node == &server->menu_tree->node) {
return "server->menu_tree";
}
if (node == &server->workspace_tree->node) {
return "server->workspace_tree";
if (node == &server->view_tree->node) {
return "server->view_tree";
}
if (node->parent == server->workspace_tree) {
struct workspace *workspace = get_workspace_from_node(server, node);
if (workspace) {
if (node == &server->view_tree_always_on_bottom->node) {
return "server->always_on_bottom";
}
if (node == &server->view_tree_always_on_top->node) {
return "server->always_on_top";
}
if (node->parent == server->view_tree) {
struct workspace *workspace;
wl_list_for_each(workspace, &server->workspaces.all, link) {
if (&workspace->tree->node == node) {
return workspace->name;
}
}
return "unknown workspace";
}
if (grand_parent == server->workspace_tree) {
struct workspace *workspace =
get_workspace_from_node(server, &node->parent->node);
if (workspace) {
struct wlr_scene_tree **trees = workspace->view_trees;
if (node == &trees[VIEW_LAYER_NORMAL]->node) {
return "normal";
} else if (node == &trees[VIEW_LAYER_ALWAYS_ON_TOP]->node) {
return "always_on_top";
} else if (node == &trees[VIEW_LAYER_ALWAYS_ON_BOTTOM]->node) {
return "always_on_bottom";
}
}
return "unknown tree";
}
if (node->parent == &server->scene->tree) {
struct output *output;
wl_list_for_each(output, &server->outputs, link) {
@ -183,7 +160,12 @@ get_special(struct server *server, struct wlr_scene_node *node)
return "server->unmanaged_tree";
}
#endif
if (grand_grand_parent == server->workspace_tree && node->data) {
struct wlr_scene_tree *grand_parent =
node->parent ? node->parent->node.parent : NULL;
if (grand_parent == server->view_tree && node->data) {
last_view = node_view_from_node(node);
}
if (node->parent == server->view_tree_always_on_top && node->data) {
last_view = node_view_from_node(node);
}
const char *view_part = get_view_part(last_view, node);

View file

@ -55,9 +55,7 @@ set_or_offer_focus(struct view *view)
break;
case VIEW_WANTS_FOCUS_LIKELY:
case VIEW_WANTS_FOCUS_UNLIKELY:
if (view->surface != seat->seat->keyboard_state.focused_surface) {
view_offer_focus(view);
}
break;
case VIEW_WANTS_FOCUS_NEVER:
break;
@ -96,9 +94,9 @@ desktop_focus_view(struct view *view, bool raise)
/*
* Switch workspace if necessary to make the view visible
* (unnecessary for omnipresent views).
* (unnecessary for "always on {top,bottom}" views).
*/
if (!view->visible_on_all_workspaces) {
if (!view_is_always_on_top(view) && !view_is_always_on_bottom(view)) {
workspaces_switch_to(view->workspace, /*update_focus*/ false);
}
@ -138,9 +136,16 @@ static struct view *
desktop_topmost_focusable_view(struct server *server)
{
struct view *view;
for_each_view(view, &server->views,
LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) {
if (!view->minimized) {
struct wl_list *node_list;
struct wlr_scene_node *node;
node_list = &server->workspaces.current->tree->children;
wl_list_for_each_reverse(node, node_list, link) {
if (!node->data) {
/* We found some non-view, most likely the region overlay */
continue;
}
view = node_view_from_node(node);
if (view_is_focusable(view) && !view->minimized) {
return view;
}
}
@ -165,31 +170,41 @@ desktop_focus_topmost_view(struct server *server)
void
desktop_focus_output(struct output *output)
{
struct server *server = output->server;
if (!output_is_usable(output) || server->input_mode
if (!output_is_usable(output) || output->server->input_mode
!= LAB_INPUT_STATE_PASSTHROUGH) {
return;
}
struct view *view;
for_each_view(view, &server->views, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) {
if (view->outputs & output->id_bit) {
struct wlr_scene_node *node;
struct wlr_output_layout *layout = output->server->output_layout;
struct wl_list *list_head =
&output->server->workspaces.current->tree->children;
wl_list_for_each_reverse(node, list_head, link) {
if (!node->data) {
continue;
}
view = node_view_from_node(node);
if (!view_is_focusable(view)) {
continue;
}
if (wlr_output_layout_intersects(layout,
output->wlr_output, &view->current)) {
desktop_focus_view(view, /*raise*/ false);
wlr_cursor_warp(server->seat.cursor, NULL,
wlr_cursor_warp(view->server->seat.cursor, NULL,
view->current.x + view->current.width / 2,
view->current.y + view->current.height / 2);
cursor_update_focus(server);
cursor_update_focus(view->server);
return;
}
}
/* No view found on desired output */
struct wlr_box layout_box;
wlr_output_layout_get_box(server->output_layout,
wlr_output_layout_get_box(output->server->output_layout,
output->wlr_output, &layout_box);
wlr_cursor_warp(server->seat.cursor, NULL,
wlr_cursor_warp(output->server->seat.cursor, NULL,
layout_box.x + output->usable_area.x + output->usable_area.width / 2,
layout_box.y + output->usable_area.y + output->usable_area.height / 2);
cursor_update_focus(server);
cursor_update_focus(output->server);
}
void

View file

@ -4,7 +4,6 @@
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/util/log.h>
#include "common/scene-helpers.h"
#include "config/rcxml.h"
#include "input/cursor.h"
#include "labwc.h" /* for struct seat */
@ -84,7 +83,7 @@ handle_drag_destroy(struct wl_listener *listener, void *data)
void
dnd_init(struct seat *seat)
{
seat->drag.icons = lab_wlr_scene_tree_create(&seat->server->scene->tree);
seat->drag.icons = wlr_scene_tree_create(&seat->server->scene->tree);
wlr_scene_node_set_enabled(&seat->drag.icons->node, false);
seat->drag.events.request.notify = handle_drag_request;

View file

@ -26,7 +26,8 @@ handle_request_maximize(struct wl_listener *listener, void *data)
struct wlr_foreign_toplevel_handle_v1_maximized_event *event = data;
view_maximize(wlr_toplevel->view,
event->maximized ? VIEW_AXIS_BOTH : VIEW_AXIS_NONE);
event->maximized ? VIEW_AXIS_BOTH : VIEW_AXIS_NONE,
/*store_natural_geometry*/ true);
}
static void

View file

@ -604,7 +604,7 @@ cursor_update_common(struct server *server, const struct cursor_context *ctx,
}
enum lab_edge
cursor_get_resize_edges(struct wlr_cursor *cursor, const struct cursor_context *ctx)
cursor_get_resize_edges(struct wlr_cursor *cursor, struct cursor_context *ctx)
{
enum lab_edge resize_edges = node_type_to_edges(ctx->type);
if (ctx->view && !resize_edges) {
@ -1147,7 +1147,6 @@ 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 */
cursor_context_save(&seat->pressed, &ctx);
interactive_set_grab_context(server, &ctx);
}
if (server->input_mode == LAB_INPUT_STATE_MENU) {
@ -1278,9 +1277,6 @@ cursor_finish_button_release(struct seat *seat, uint32_t button)
/* Exit interactive move/resize mode */
interactive_finish(server->grabbed_view);
return true;
} else if (server->grabbed_view) {
/* Button was released without starting move/resize */
interactive_cancel(server->grabbed_view);
}
return false;

View file

@ -12,7 +12,6 @@
#include <wlr/types/wlr_virtual_keyboard_v1.h>
#include <wlr/types/wlr_xdg_shell.h>
#include "common/mem.h"
#include "common/scene-helpers.h"
#include "input/keyboard.h"
#include "labwc.h"
#include "node.h"
@ -399,8 +398,6 @@ handle_input_method_new_popup_surface(struct wl_listener *listener, void *data)
popup->tree = wlr_scene_subsurface_tree_create(
relay->popup_tree, popup->popup_surface->surface);
die_if_null(popup->tree);
node_descriptor_create(&popup->tree->node, LAB_NODE_IME_POPUP,
/*view*/ NULL, /*data*/ NULL);
@ -583,7 +580,7 @@ input_method_relay_create(struct seat *seat)
relay->seat = seat;
wl_list_init(&relay->text_inputs);
wl_list_init(&relay->popups);
relay->popup_tree = lab_wlr_scene_tree_create(&seat->server->scene->tree);
relay->popup_tree = wlr_scene_tree_create(&seat->server->scene->tree);
relay->new_text_input.notify = handle_new_text_input;
wl_signal_add(&seat->server->text_input_manager->events.text_input,

View file

@ -54,33 +54,9 @@ interactive_anchor_to_cursor(struct server *server, struct wlr_box *geo)
geo->y = server->grab_box.y + (server->seat.cursor->y - server->grab_y);
}
/*
* Called before interactive_begin() to set the initial grab parameters
* (cursor position and view geometry). Once the cursor actually moves,
* then interactive_begin() is called.
*/
void
interactive_set_grab_context(struct server *server, const struct cursor_context *ctx)
{
if (!ctx->view) {
return;
}
if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
return;
}
server->grabbed_view = ctx->view;
server->grab_x = server->seat.cursor->x;
server->grab_y = server->seat.cursor->y;
server->grab_box = ctx->view->current;
server->resize_edges =
cursor_get_resize_edges(server->seat.cursor, ctx);
}
void
interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges)
{
assert(view);
/*
* This function sets up an interactive move or resize operation, where
* the compositor stops propagating pointer events to clients and
@ -89,8 +65,7 @@ interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges)
struct server *server = view->server;
struct seat *seat = &server->seat;
if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH
|| view != server->grabbed_view) {
if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
return;
}
@ -117,6 +92,9 @@ interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges)
/* Store natural geometry at start of move */
view_store_natural_geometry(view);
if (view_is_floating(view)) {
view_invalidate_last_layout_geometry(view);
}
/* Prevent region snapping when just moving via A-Left mousebind */
seat->region_prevent_snap = keyboard_get_all_modifiers(seat);
@ -134,12 +112,10 @@ interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges)
}
/*
* Override resize edges if specified explicitly.
* Otherwise, they were set already from cursor context.
* Resizing overrides any attempt to restore window
* geometries altered by layout changes.
*/
if (edges != LAB_EDGE_NONE) {
server->resize_edges = edges;
}
view_invalidate_last_layout_geometry(view);
/*
* If tiled or maximized in only one direction, reset
@ -147,15 +123,15 @@ interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges)
* keep the same geometry as the starting point.
*/
enum view_axis maximized = view->maximized;
if (server->resize_edges & LAB_EDGES_LEFT_RIGHT) {
if (edges & LAB_EDGES_LEFT_RIGHT) {
maximized &= ~VIEW_AXIS_HORIZONTAL;
}
if (server->resize_edges & LAB_EDGES_TOP_BOTTOM) {
if (edges & LAB_EDGES_TOP_BOTTOM) {
maximized &= ~VIEW_AXIS_VERTICAL;
}
view_set_maximized(view, maximized);
view_set_untiled(view);
cursor_shape = cursor_get_from_edge(server->resize_edges);
cursor_shape = cursor_get_from_edge(edges);
break;
}
default:
@ -163,6 +139,13 @@ interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges)
return;
}
server->grabbed_view = view;
/* Remember view and cursor positions at start of move/resize */
server->grab_x = seat->cursor->x;
server->grab_y = seat->cursor->y;
server->grab_box = view->current;
server->resize_edges = edges;
seat_focus_override_begin(seat, mode, cursor_shape);
/*
@ -290,12 +273,17 @@ snap_to_edge(struct view *view)
enum lab_edge edge = edge1 | edge2;
view_set_output(view, output);
/*
* Don't store natural geometry here (it was
* stored already in interactive_begin())
*/
if (edge == LAB_EDGE_TOP && rc.snap_top_maximize) {
/* <topMaximize> */
view_maximize(view, VIEW_AXIS_BOTH);
view_maximize(view, VIEW_AXIS_BOTH,
/*store_natural_geometry*/ false);
} else {
view_snap_to_edge(view, edge, /*across_outputs*/ false,
/*combine*/ false);
/*combine*/ false, /*store_natural_geometry*/ false);
}
return true;
@ -310,7 +298,8 @@ snap_to_region(struct view *view)
struct region *region = regions_from_cursor(view->server);
if (region) {
view_snap_to_region(view, region);
view_snap_to_region(view, region,
/*store_natural_geometry*/ false);
return true;
}
return false;
@ -319,8 +308,6 @@ snap_to_region(struct view *view)
void
interactive_finish(struct view *view)
{
assert(view);
if (view->server->grabbed_view != view) {
return;
}
@ -342,27 +329,16 @@ interactive_finish(struct view *view)
void
interactive_cancel(struct view *view)
{
assert(view);
if (view->server->grabbed_view != view) {
return;
}
view->server->grabbed_view = NULL;
/*
* It's possible that grabbed_view was set but interactive_begin()
* wasn't called yet. In that case, we are done.
*/
if (view->server->input_mode != LAB_INPUT_STATE_MOVE
&& view->server->input_mode != LAB_INPUT_STATE_RESIZE) {
return;
}
overlay_finish(&view->server->seat);
resize_indicator_hide(view);
view->server->grabbed_view = NULL;
/* Restore keyboard/pointer focus */
seat_focus_override_end(&view->server->seat, /*restore_focus*/ true);
seat_focus_override_end(&view->server->seat);
}

View file

@ -145,7 +145,7 @@ try_to_focus_next_layer_or_toplevel(struct server *server)
{
struct seat *seat = &server->seat;
struct output *output = output_nearest_to_cursor(server);
if (!output_is_usable(output)) {
if (!output) {
goto no_output;
}
@ -378,24 +378,16 @@ handle_unmap(struct wl_listener *listener, void *data)
if (seat->focused_layer == layer_surface) {
try_to_focus_next_layer_or_toplevel(layer->server);
}
cursor_update_focus(layer->server);
layer->being_unmapped = false;
}
static bool
is_above_toplevels(struct wlr_layer_surface_v1 *layer_surface)
{
return layer_surface->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP;
}
static void
handle_map(struct wl_listener *listener, void *data)
{
struct lab_layer_surface *layer = wl_container_of(listener, layer, map);
struct wlr_layer_surface_v1 *layer_surface =
layer->scene_layer_surface->layer_surface;
struct wlr_output *wlr_output = layer_surface->output;
struct wlr_output *wlr_output =
layer->scene_layer_surface->layer_surface->output;
if (wlr_output) {
output_update_usable_area(wlr_output->data);
}
@ -407,39 +399,15 @@ handle_map(struct wl_listener *listener, void *data)
* the scene. See wlr_scene_surface_create() documentation.
*/
/*
* Layer-shell clients with exclusive interactivity always get focus,
* whereas on-demand ones only get it when above toplevels (i.e. in the
* top or overlay layers). We could make this configurable, but for the
* time being this default behaviour strikes a balance between:
*
* 1. Giving top/overlay, on-demand clients (like labnag and
* lxqt-runner) keyboard focus.
* 2. Preventing desktop components like desktops from stealing
* keyboard focus on re-start. On compositor start, this is not
* really a problem, but if any such client restarts later,
* focus-stealing is unlikely to be desirable.
*/
if (!is_above_toplevels(layer_surface) && is_on_demand(layer_surface)) {
return;
}
struct seat *seat = &layer->server->seat;
layer_try_set_focus(seat, layer->scene_layer_surface->layer_surface);
}
static bool
surface_is_focused(struct seat *seat, struct wlr_surface *surface)
{
return seat->seat->keyboard_state.focused_surface == surface;
}
static void
handle_popup_destroy(struct wl_listener *listener, void *data)
{
struct lab_layer_popup *popup =
wl_container_of(listener, popup, destroy);
struct seat *seat = &popup->server->seat;
struct wlr_xdg_popup *_popup, *tmp;
wl_list_for_each_safe(_popup, tmp, &popup->wlr_popup->base->popups, link) {
@ -455,65 +423,11 @@ handle_popup_destroy(struct wl_listener *listener, void *data)
wl_list_remove(&popup->commit.link);
}
/* TODO: do this on unmap instead? */
if (surface_is_focused(seat, popup->wlr_popup->base->surface)) {
/* Give focus back to whoever had it before the popup */
if (popup->parent_was_focused && popup->wlr_popup->parent) {
seat_force_focus_surface(seat, popup->wlr_popup->parent);
} else {
desktop_focus_topmost_view(popup->server);
}
}
cursor_update_focus(popup->server);
free(popup);
}
/*
* When a popup is opened by a client without keyboard focus we need to force
* focus it so that it can be operated by the keyboard. An example of a use-case
* is the xfce4-panel start menu which can be opened by a keyboard shortcut
* linked to `xfce4-popup-applicationsmenu`, and the same for lxqt-panel with
* `lxqt-qdbus openmenu`.
*
* We check wlr_popup->seat here to make sure that the popup requested a grab,
* so that we do not give keyboard-focus to tooltips and the like. The
* wlr_popup->seat check works because it is set by wlroots only when a
* popup-grab has been handled. See xdg_popup_handle_grab() in
* types/xdg_shell/wlr_xdg_popup.c
*
* From a technical perspective it would seem nicer to explicitly set the focus
* on catching a xdg_popup.grab event but this is not possible for two reasons:
*
* 1. The xdg_popup.grab event is not emitted from wlroots in such a way
* that it can be differentiated from dnd grabs (see #3375).
* 2. There is a sequencing issue with some clients (notably layer-shell-qt
* ones) that means this cannot be managed from the labwc side.
* Specifically, the grab event takes place before the get_popup one
* which means that the lab_layer_popup object has not yet been created
* when the grab is requested.
*
* WAYLAND_DEBUG=1 lxqt-panel 2>&1 | grep 'grab\|get_popup'
* ...xdg_surface#50.get_popup(new id xdg_popup#52, nil, xdg_positioner#51)
* ...xdg_popup#52.grab(wl_seat#11, 205)
* ...zwlr_layer_surface_v1#38.get_popup(xdg_popup#52)
*
* WAYLAND_DEBUG=1 xfce4-panel 2>&1 | grep 'grab\|get_popup'
* ...xdg_surface#50.get_popup(new id xdg_popup#51, nil, xdg_positioner#49)
* ...zwlr_layer_surface_v1#41.get_popup(xdg_popup#51)
* ...xdg_popup#51.grab(wl_seat#23, 540)
*
* So for the time being we make {xfce4,lxqt}-panel work by just checking
* wlr_popup->seat and then setting the focus in handle_popup_commit()
*
* Ref:
* - https://github.com/labwc/labwc/pull/3165
* - https://github.com/labwc/labwc/pull/3375
* - https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5265
* - https://github.com/labwc/labwc/issues/2467#issuecomment-2585927886
* - https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3689
* - https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4950
*/
static void
handle_popup_commit(struct wl_listener *listener, void *data)
{
@ -527,17 +441,6 @@ handle_popup_commit(struct wl_listener *listener, void *data)
/* Prevent getting called over and over again */
wl_list_remove(&popup->commit.link);
popup->commit.notify = NULL;
/* Force focus when popup was triggered by IPC */
struct server *server = popup->server;
struct seat *seat = &server->seat;
bool requesting_grab = !!popup->wlr_popup->seat;
if (requesting_grab) {
if (surface_is_focused(seat, popup->wlr_popup->parent)) {
popup->parent_was_focused = true;
}
seat_force_focus_surface(seat, popup->wlr_popup->base->surface);
}
}
}
@ -561,7 +464,10 @@ create_popup(struct server *server, struct wlr_xdg_popup *wlr_popup,
popup->wlr_popup = wlr_popup;
popup->scene_tree =
wlr_scene_xdg_surface_create(parent, wlr_popup->base);
die_if_null(popup->scene_tree);
if (!popup->scene_tree) {
free(popup);
return NULL;
}
/* In support of IME popup */
wlr_popup->base->surface->data = popup->scene_tree;
@ -595,6 +501,12 @@ handle_popup_new_popup(struct wl_listener *listener, void *data)
lab_layer_popup->server, wlr_popup,
lab_layer_popup->scene_tree);
if (!new_popup) {
wl_resource_post_no_memory(wlr_popup->resource);
wlr_xdg_popup_destroy(wlr_popup);
return;
}
new_popup->output_toplevel_sx_box =
lab_layer_popup->output_toplevel_sx_box;
}
@ -655,6 +567,12 @@ handle_new_popup(struct wl_listener *listener, void *data)
};
struct lab_layer_popup *popup =
create_popup(server, wlr_popup, surface->tree);
if (!popup) {
wl_resource_post_no_memory(wlr_popup->resource);
wlr_xdg_popup_destroy(wlr_popup);
return;
}
popup->output_toplevel_sx_box = output_toplevel_sx_box;
if (surface->layer_surface->current.layer
@ -671,21 +589,16 @@ handle_new_layer_surface(struct wl_listener *listener, void *data)
struct wlr_layer_surface_v1 *layer_surface = data;
if (!layer_surface->output) {
struct output *output = output_nearest_to_cursor(server);
if (!output || !output->scene_output) {
/*
* We are not using output_is_usable() here because
* it also checks if the output is enabled which is
* not the case when wlopm is used. Thus we allow
* layer surfaces to spawn on disabled outputs as
* long as it is part of the scene layout.
*/
struct wlr_output *output = wlr_output_layout_output_at(
server->output_layout, server->seat.cursor->x,
server->seat.cursor->y);
if (!output) {
wlr_log(WLR_INFO,
"No output available to assign layer surface");
wlr_layer_surface_v1_destroy(layer_surface);
return;
}
layer_surface->output = output->wlr_output;
layer_surface->output = output;
}
struct lab_layer_surface *surface = znew(*surface);
@ -701,7 +614,11 @@ handle_new_layer_surface(struct wl_listener *listener, void *data)
surface->scene_layer_surface = wlr_scene_layer_surface_v1_create(
selected_layer, layer_surface);
die_if_null(surface->scene_layer_surface);
if (!surface->scene_layer_surface) {
wlr_layer_surface_v1_destroy(layer_surface);
wlr_log(WLR_ERROR, "could not create layer surface");
return;
}
/* In support of IME popup */
layer_surface->surface->data = surface->scene_layer_surface->tree;

View file

@ -4,6 +4,7 @@
#include <assert.h>
#include <libxml/parser.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
@ -18,7 +19,6 @@
#include "common/lab-scene-rect.h"
#include "common/list.h"
#include "common/mem.h"
#include "common/scene-helpers.h"
#include "common/spawn.h"
#include "common/string-helpers.h"
#include "common/xml.h"
@ -170,7 +170,7 @@ item_create_scene_for_state(struct menuitem *item, float *text_color,
struct theme *theme = menu->server->theme;
/* Tree to hold background and label buffers */
struct wlr_scene_tree *tree = lab_wlr_scene_tree_create(item->tree);
struct wlr_scene_tree *tree = wlr_scene_tree_create(item->tree);
int icon_width = 0;
int icon_size = ICON_SIZE;
@ -190,7 +190,7 @@ item_create_scene_for_state(struct menuitem *item, float *text_color,
}
/* Create background */
lab_wlr_scene_rect_create(tree, bg_width, theme->menu_item_height, bg_color);
wlr_scene_rect_create(tree, bg_width, theme->menu_item_height, bg_color);
/* Create icon */
bool show_app_icon = !strcmp(item->parent->id, "client-list-combined-menu")
@ -246,7 +246,7 @@ item_create_scene(struct menuitem *menuitem, int *item_y)
struct theme *theme = menu->server->theme;
/* Menu item root node */
menuitem->tree = lab_wlr_scene_tree_create(menu->scene_tree);
menuitem->tree = wlr_scene_tree_create(menu->scene_tree);
node_descriptor_create(&menuitem->tree->node, LAB_NODE_MENUITEM,
/*view*/ NULL, menuitem);
@ -295,12 +295,12 @@ separator_create_scene(struct menuitem *menuitem, int *item_y)
struct theme *theme = menu->server->theme;
/* Menu item root node */
menuitem->tree = lab_wlr_scene_tree_create(menu->scene_tree);
menuitem->tree = wlr_scene_tree_create(menu->scene_tree);
node_descriptor_create(&menuitem->tree->node, LAB_NODE_MENUITEM,
/*view*/ NULL, menuitem);
/* Tree to hold background and line buffer */
menuitem->normal_tree = lab_wlr_scene_tree_create(menuitem->tree);
menuitem->normal_tree = wlr_scene_tree_create(menuitem->tree);
int bg_height = theme->menu_separator_line_thickness
+ 2 * theme->menu_separator_padding_height;
@ -313,11 +313,11 @@ separator_create_scene(struct menuitem *menuitem, int *item_y)
}
/* Item background nodes */
lab_wlr_scene_rect_create(menuitem->normal_tree, bg_width, bg_height,
wlr_scene_rect_create(menuitem->normal_tree, bg_width, bg_height,
theme->menu_items_bg_color);
/* Draw separator line */
struct wlr_scene_rect *line_rect = lab_wlr_scene_rect_create(
struct wlr_scene_rect *line_rect = wlr_scene_rect_create(
menuitem->normal_tree, line_width,
theme->menu_separator_line_thickness,
theme->menu_separator_color);
@ -343,12 +343,12 @@ title_create_scene(struct menuitem *menuitem, int *item_y)
float *text_color = theme->menu_title_text_color;
/* Menu item root node */
menuitem->tree = lab_wlr_scene_tree_create(menu->scene_tree);
menuitem->tree = wlr_scene_tree_create(menu->scene_tree);
node_descriptor_create(&menuitem->tree->node, LAB_NODE_MENUITEM,
/*view*/ NULL, menuitem);
/* Tree to hold background and text buffer */
menuitem->normal_tree = lab_wlr_scene_tree_create(menuitem->tree);
menuitem->normal_tree = wlr_scene_tree_create(menuitem->tree);
int bg_width = menu->size.width - 2 * theme->menu_border_width;
int text_width = bg_width - 2 * theme->menu_items_padding_x;
@ -359,7 +359,7 @@ title_create_scene(struct menuitem *menuitem, int *item_y)
}
/* Background */
lab_wlr_scene_rect_create(menuitem->normal_tree,
wlr_scene_rect_create(menuitem->normal_tree,
bg_width, theme->menu_header_height, bg_color);
/* Draw separator title */
@ -416,7 +416,7 @@ menu_create_scene(struct menu *menu)
assert(!menu->scene_tree);
menu->scene_tree = lab_wlr_scene_tree_create(menu->server->menu_tree);
menu->scene_tree = wlr_scene_tree_create(menu->server->menu_tree);
wlr_scene_node_set_enabled(&menu->scene_tree->node, false);
/* Menu width is the maximum item width, capped by menu.width.{min,max} */
@ -731,7 +731,7 @@ menu_reposition(struct menu *menu, struct wlr_box anchor_rect)
/* Get output usable area to place the menu within */
struct output *output = output_nearest_to(menu->server,
anchor_rect.x, anchor_rect.y);
if (!output_is_usable(output)) {
if (!output) {
wlr_log(WLR_ERROR, "no output found around (%d,%d)",
anchor_rect.x, anchor_rect.y);
return;
@ -1430,7 +1430,7 @@ menu_execute_item(struct menuitem *item)
struct server *server = item->parent->server;
menu_close(server->menu_current);
server->menu_current = NULL;
seat_focus_override_end(&server->seat, /*restore_focus*/ true);
seat_focus_override_end(&server->seat);
/*
* We call the actions after closing the menu so that virtual keyboard
@ -1443,9 +1443,6 @@ menu_execute_item(struct menuitem *item)
*/
if (!strcmp(item->parent->id, "client-list-combined-menu")
&& item->client_list_view) {
if (item->client_list_view->shaded) {
view_set_shade(item->client_list_view, false);
}
actions_run(item->client_list_view, server, &item->actions, NULL);
} else {
actions_run(item->parent->triggered_by_view, server,
@ -1533,7 +1530,7 @@ menu_close_root(struct server *server)
menu_close(server->menu_current);
server->menu_current = NULL;
reset_pipemenus(server);
seat_focus_override_end(&server->seat, /*restore_focus*/ true);
seat_focus_override_end(&server->seat);
}
void

View file

@ -12,7 +12,6 @@
#include <strings.h>
#include <wlr/backend/drm.h>
#include <wlr/backend/wayland.h>
#include <wlr/config.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_drm_lease_v1.h>
#include <wlr/types/wlr_gamma_control_v1.h>
@ -39,10 +38,6 @@
#include "view.h"
#include "xwayland.h"
#if WLR_HAS_X11_BACKEND
#include <wlr/backend/x11.h>
#endif
bool
output_get_tearing_allowance(struct output *output)
{
@ -133,6 +128,17 @@ handle_output_frame(struct wl_listener *listener, void *data)
return;
}
if (!output->scene_output) {
/*
* TODO: This is a short term fix for issue #1667,
* a proper fix would require restructuring
* the life cycle of scene outputs, e.g.
* creating them on handle_new_output() only.
*/
wlr_log(WLR_INFO, "Failed to render new frame: no scene-output");
return;
}
if (output->gamma_lut_changed) {
/*
* We are not mixing the gamma state with
@ -160,8 +166,7 @@ static void
handle_output_destroy(struct wl_listener *listener, void *data)
{
struct output *output = wl_container_of(listener, output, destroy);
struct server *server = output->server;
struct seat *seat = &server->seat;
struct seat *seat = &output->server->seat;
regions_evacuate_output(output);
regions_destroy(seat, &output->regions);
if (seat->overlay.active.output == output) {
@ -185,6 +190,7 @@ handle_output_destroy(struct wl_listener *listener, void *data)
}
struct view *view;
struct server *server = output->server;
wl_list_for_each(view, &server->views, link) {
if (view->output == output) {
view_on_output_destroy(view);
@ -200,20 +206,6 @@ handle_output_destroy(struct wl_listener *listener, void *data)
*/
output->wlr_output->data = NULL;
/*
* On nested backends (X11/Wayland), outputs correspond to
* windows and cannot be reconnected. Exit the compositor
* when the last one is destroyed.
*/
if (wl_list_empty(&server->outputs) && (
wlr_output_is_wl(output->wlr_output)
#if WLR_HAS_X11_BACKEND
|| wlr_output_is_x11(output->wlr_output)
#endif
)) {
wl_display_terminate(server->wl_display);
}
/*
* output->scene_output (if still around at this point) is
* destroyed automatically when the wlr_output is destroyed
@ -381,10 +373,8 @@ output_test_auto(struct wlr_output *wlr_output, struct wlr_output_state *state,
}
/* Reset mode if none worked (we may still try to commit) */
wlr_log(WLR_DEBUG, "no working fixed mode found for output %s", wlr_output->name);
state->committed &= ~WLR_OUTPUT_STATE_MODE;
return wlr_output_test_state(wlr_output, state);
wlr_output_state_set_mode(state, NULL);
return false;
}
static void
@ -399,8 +389,10 @@ configure_new_output(struct server *server, struct output *output)
/* is_client_request */ false)) {
wlr_log(WLR_INFO, "mode test failed for output %s",
wlr_output->name);
wlr_output_state_set_enabled(&output->pending, false);
return;
/*
* Continue anyway. For some reason, the test fails when
* running nested, yet the following commit succeeds.
*/
}
if (rc.adaptive_sync == LAB_ADAPTIVE_SYNC_ENABLED) {
@ -550,6 +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->cycle_osd.items);
/*
* Create layer-trees (background, bottom, top and overlay) and
@ -557,11 +550,11 @@ handle_new_output(struct wl_listener *listener, void *data)
*/
for (size_t i = 0; i < ARRAY_SIZE(output->layer_tree); i++) {
output->layer_tree[i] =
lab_wlr_scene_tree_create(&server->scene->tree);
wlr_scene_tree_create(&server->scene->tree);
}
output->layer_popup_tree = lab_wlr_scene_tree_create(&server->scene->tree);
output->cycle_osd_tree = lab_wlr_scene_tree_create(&server->scene->tree);
output->session_lock_tree = lab_wlr_scene_tree_create(&server->scene->tree);
output->layer_popup_tree = wlr_scene_tree_create(&server->scene->tree);
output->cycle_osd_tree = wlr_scene_tree_create(&server->scene->tree);
output->session_lock_tree = wlr_scene_tree_create(&server->scene->tree);
/*
* Set the z-positions to achieve the following order (from top to
@ -884,9 +877,14 @@ wlr_output_configuration_v1 *create_output_config(struct server *server)
wlr_output_configuration_v1_destroy(config);
return NULL;
}
if (output_is_usable(output)) {
head->state.x = output->scene_output->x;
head->state.y = output->scene_output->y;
struct wlr_box box;
wlr_output_layout_get_box(server->output_layout,
output->wlr_output, &box);
if (!wlr_box_empty(&box)) {
head->state.x = box.x;
head->state.y = box.y;
} else {
wlr_log(WLR_ERROR, "failed to get output layout box");
}
}
return config;
@ -1070,14 +1068,8 @@ output_get_adjacent(struct output *output, enum lab_edge edge, bool wrap)
bool
output_is_usable(struct output *output)
{
/*
* output_is_usable(NULL) is safe and returns false.
*
* Checking output->scene_output != NULL is necessary in case the
* wlr_output was initially enabled but hasn't been configured yet
* (occurs with autoEnableOutputs=no).
*/
return output && output->wlr_output->enabled && output->scene_output;
/* output_is_usable(NULL) is safe and returns false */
return output && output->wlr_output->enabled;
}
/* returns true if usable area changed */
@ -1137,7 +1129,7 @@ output_update_all_usable_areas(struct server *server, bool layout_changed)
struct wlr_box
output_usable_area_in_layout_coords(struct output *output)
{
if (!output_is_usable(output)) {
if (!output) {
return (struct wlr_box){0};
}
struct wlr_box box = output->usable_area;

View file

@ -53,7 +53,7 @@ regions_from_cursor(struct server *server)
struct wlr_output *wlr_output = wlr_output_layout_output_at(
server->output_layout, lx, ly);
struct output *output = output_from_wlr_output(server, wlr_output);
if (!output_is_usable(output)) {
if (!output) {
return NULL;
}

View file

@ -12,7 +12,6 @@
#include "common/list.h"
#include "common/macros.h"
#include "common/mem.h"
#include "common/scene-helpers.h"
#include "node.h"
/*
@ -192,7 +191,12 @@ scaled_buffer_create(struct wlr_scene_tree *parent,
assert(impl->create_buffer);
struct scaled_buffer *self = znew(*self);
self->scene_buffer = lab_wlr_scene_buffer_create(parent, NULL);
self->scene_buffer = wlr_scene_buffer_create(parent, NULL);
if (!self->scene_buffer) {
wlr_log(WLR_ERROR, "Failed to create scene buffer");
free(self);
return NULL;
}
self->impl = impl;
/*

View file

@ -759,20 +759,6 @@ seat_reconfigure(struct server *server)
}
}
void
seat_force_focus_surface(struct seat *seat, struct wlr_surface *surface)
{
if (seat->server->session_lock_manager->locked) {
return;
}
uint32_t *pressed_sent_keycodes = key_state_pressed_sent_keycodes();
int nr_pressed_sent_keycodes = key_state_nr_pressed_sent_keycodes();
struct wlr_keyboard *kb = &seat->keyboard_group->keyboard;
wlr_seat_keyboard_enter(seat->seat, surface,
pressed_sent_keycodes, nr_pressed_sent_keycodes, &kb->modifiers);
}
static void
seat_focus(struct seat *seat, struct wlr_surface *surface,
bool replace_exclusive_layer, bool is_lock_surface)
@ -916,12 +902,12 @@ seat_focus_override_begin(struct seat *seat, enum input_mode input_mode,
}
void
seat_focus_override_end(struct seat *seat, bool restore_focus)
seat_focus_override_end(struct seat *seat)
{
seat->server->input_mode = LAB_INPUT_STATE_PASSTHROUGH;
if (seat->focus_override.surface) {
if (restore_focus) {
if (!seat->seat->keyboard_state.focused_surface) {
seat_focus(seat, seat->focus_override.surface,
/*replace_exclusive_layer*/ false,
/*is_lock_surface*/ false);
@ -930,7 +916,5 @@ seat_focus_override_end(struct seat *seat, bool restore_focus)
seat->focus_override.surface = NULL;
}
if (restore_focus) {
cursor_update_focus(seat->server);
}
}

View file

@ -46,8 +46,6 @@
#include "action.h"
#include "common/macros.h"
#include "common/mem.h"
#include "common/scene-helpers.h"
#include "config/rcxml.h"
#include "config/session.h"
#include "decorations.h"
@ -99,7 +97,6 @@ reload_config_and_theme(struct server *server)
view_reload_ssd(view);
}
cycle_finish(server, /*switch_focus*/ false);
menu_reconfigure(server);
seat_reconfigure(server);
regions_reconfigure(server);
@ -279,7 +276,7 @@ allow_for_sandbox(const struct wlr_security_context_v1_state *security_state,
"zxdg_importer_v1",
"zxdg_importer_v2",
"xdg_toplevel_icon_manager_v1",
"xdg_wm_dialog_v1",
"xdg_dialog_v1",
/* plus */
"wp_alpha_modifier_v1",
"wp_linux_drm_syncobj_manager_v1",
@ -433,10 +430,9 @@ server_init(struct server *server)
wlr_log(WLR_ERROR, "cannot allocate a wayland display");
exit(EXIT_FAILURE);
}
/* Increase max client buffer size to make slow clients less likely to terminate */
wl_display_set_default_max_buffer_size(server->wl_display, 1024 * 1024);
wl_display_set_global_filter(server->wl_display, server_global_filter, server);
server->wl_event_loop = wl_display_get_event_loop(server->wl_display);
/* Catch signals */
@ -554,11 +550,12 @@ server_init(struct server *server)
wl_list_init(&server->views);
wl_list_init(&server->unmanaged_surfaces);
wl_list_init(&server->cycle.views);
wl_list_init(&server->cycle.osd_outputs);
server->scene = wlr_scene_create();
die_if_null(server->scene);
if (!server->scene) {
wlr_log(WLR_ERROR, "unable to create scene");
exit(EXIT_FAILURE);
}
server->direct_scanout_enabled = server->scene->WLR_PRIVATE.direct_scanout;
/*
@ -566,33 +563,31 @@ 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
*
* | Scene Tree | Description
* | ---------------------------------- | -------------------------------------
* | output->session_lock_tree | session lock surfaces (e.g. swaylock)
* | output->cycle_osd_tree | window switcher's on-screen display
* | server->cycle_preview_tree | window switcher's previewed window
* | server->menu_tree | labwc's server-side menus
* | output->layer_popup_tree | xdg popups on layer surfaces
* | output->layer_tree[3] | overlay layer surfaces (e.g. rofi)
* | output->layer_tree[2] | top layer surfaces (e.g. waybar)
* | server->unmanaged_tree | unmanaged X11 surfaces (e.g. dmenu)
* | server->xdg_popup_tree | xdg popups on xdg windows
* | server->workspace_tree |
* | + workspace->tree |
* | + workspace->view_trees[1] | always-on-top xdg/X11 windows
* | + workspace->view_trees[0] | normal xdg/X11 windows (e.g. firefox)
* | + workspace->view_trees[2] | always-on-bottom xdg/X11 windows
* | output->layer_tree[1] | bottom layer surfaces
* | output->layer_tree[0] | background layer surfaces (e.g. 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
*/
server->workspace_tree = lab_wlr_scene_tree_create(&server->scene->tree);
server->xdg_popup_tree = lab_wlr_scene_tree_create(&server->scene->tree);
server->view_tree_always_on_bottom = wlr_scene_tree_create(&server->scene->tree);
server->view_tree = wlr_scene_tree_create(&server->scene->tree);
server->view_tree_always_on_top = wlr_scene_tree_create(&server->scene->tree);
server->xdg_popup_tree = wlr_scene_tree_create(&server->scene->tree);
#if HAVE_XWAYLAND
server->unmanaged_tree = lab_wlr_scene_tree_create(&server->scene->tree);
server->unmanaged_tree = wlr_scene_tree_create(&server->scene->tree);
#endif
server->menu_tree = lab_wlr_scene_tree_create(&server->scene->tree);
server->cycle_preview_tree = lab_wlr_scene_tree_create(&server->scene->tree);
server->menu_tree = wlr_scene_tree_create(&server->scene->tree);
workspaces_init(server);

View file

@ -6,7 +6,6 @@
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_session_lock_v1.h>
#include "common/mem.h"
#include "common/scene-helpers.h"
#include "labwc.h"
#include "node.h"
#include "output.h"
@ -136,11 +135,8 @@ handle_new_surface(struct wl_listener *listener, void *data)
}
lock_output->surface = lock_surface;
struct wlr_scene_tree *surface_tree =
wlr_scene_subsurface_tree_create(lock_output->tree, lock_surface->surface);
die_if_null(surface_tree);
node_descriptor_create(&surface_tree->node,
LAB_NODE_SESSION_LOCK_SURFACE, /*view*/ NULL, /*data*/ NULL);
@ -213,7 +209,12 @@ session_lock_output_create(struct session_lock_manager *manager, struct output *
struct session_lock_output *lock_output = znew(*lock_output);
struct wlr_scene_tree *tree = lab_wlr_scene_tree_create(output->session_lock_tree);
struct wlr_scene_tree *tree = wlr_scene_tree_create(output->session_lock_tree);
if (!tree) {
wlr_log(WLR_ERROR, "session-lock: wlr_scene_tree_create()");
free(lock_output);
goto exit_session;
}
/*
* The ext-session-lock protocol says that the compositor should blank
@ -221,7 +222,13 @@ session_lock_output_create(struct session_lock_manager *manager, struct output *
* fully hidden
*/
float black[4] = { 0.f, 0.f, 0.f, 1.f };
struct wlr_scene_rect *background = lab_wlr_scene_rect_create(tree, 0, 0, black);
struct wlr_scene_rect *background = wlr_scene_rect_create(tree, 0, 0, black);
if (!background) {
wlr_log(WLR_ERROR, "session-lock: wlr_scene_rect_create()");
wlr_scene_node_destroy(&tree->node);
free(lock_output);
goto exit_session;
}
/*
* Delay blanking output by 100ms to prevent flicker. If the session is
@ -251,6 +258,12 @@ session_lock_output_create(struct session_lock_manager *manager, struct output *
lock_output_reconfigure(lock_output);
wl_list_insert(&manager->lock_outputs, &lock_output->link);
return;
exit_session:
/* TODO: Consider a better - but secure - way to deal with this */
wlr_log(WLR_ERROR, "out of memory");
exit(EXIT_FAILURE);
}
static void

View file

@ -3,7 +3,6 @@
#include <wlr/types/wlr_scene.h>
#include <wlr/util/box.h>
#include <wlr/util/log.h>
#include "common/scene-helpers.h"
#include "config/rcxml.h"
#include "labwc.h"
#include "resize-indicator.h"
@ -45,10 +44,10 @@ resize_indicator_init(struct view *view)
struct resize_indicator *indicator = &view->resize_indicator;
assert(!indicator->tree);
indicator->tree = lab_wlr_scene_tree_create(view->scene_tree);
indicator->border = lab_wlr_scene_rect_create(
indicator->tree = wlr_scene_tree_create(view->scene_tree);
indicator->border = wlr_scene_rect_create(
indicator->tree, 0, 0, rc.theme->osd_border_color);
indicator->background = lab_wlr_scene_rect_create(
indicator->background = wlr_scene_rect_create(
indicator->tree, 0, 0, rc.theme->osd_bg_color);
indicator->text = scaled_font_buffer_create(indicator->tree);

View file

@ -3,7 +3,6 @@
#include <assert.h>
#include <wlr/types/wlr_scene.h>
#include "common/macros.h"
#include "common/scene-helpers.h"
#include "labwc.h"
#include "ssd.h"
#include "ssd-internal.h"
@ -23,32 +22,32 @@ ssd_border_create(struct ssd *ssd)
int full_width = width + 2 * theme->border_width;
int corner_width = ssd_get_corner_width();
ssd->border.tree = lab_wlr_scene_tree_create(ssd->tree);
ssd->border.tree = wlr_scene_tree_create(ssd->tree);
wlr_scene_node_set_position(&ssd->border.tree->node, -theme->border_width, 0);
enum ssd_active_state active;
FOR_EACH_ACTIVE_STATE(active) {
struct ssd_border_subtree *subtree = &ssd->border.subtrees[active];
subtree->tree = lab_wlr_scene_tree_create(ssd->border.tree);
subtree->tree = wlr_scene_tree_create(ssd->border.tree);
struct wlr_scene_tree *parent = subtree->tree;
wlr_scene_node_set_enabled(&parent->node, active);
float *color = theme->window[active].border_color;
subtree->left = lab_wlr_scene_rect_create(parent,
subtree->left = wlr_scene_rect_create(parent,
theme->border_width, height, color);
wlr_scene_node_set_position(&subtree->left->node, 0, 0);
subtree->right = lab_wlr_scene_rect_create(parent,
subtree->right = wlr_scene_rect_create(parent,
theme->border_width, height, color);
wlr_scene_node_set_position(&subtree->right->node,
theme->border_width + width, 0);
subtree->bottom = lab_wlr_scene_rect_create(parent,
subtree->bottom = wlr_scene_rect_create(parent,
full_width, theme->border_width, color);
wlr_scene_node_set_position(&subtree->bottom->node,
0, height);
subtree->top = lab_wlr_scene_rect_create(parent,
subtree->top = wlr_scene_rect_create(parent,
MAX(width - 2 * corner_width, 0), theme->border_width, color);
wlr_scene_node_set_position(&subtree->top->node,
theme->border_width + corner_width,

View file

@ -5,7 +5,6 @@
#include "config/rcxml.h"
#include "common/list.h"
#include "common/mem.h"
#include "common/scene-helpers.h"
#include "node.h"
#include "scaled-buffer/scaled-icon-buffer.h"
#include "scaled-buffer/scaled-img-buffer.h"
@ -20,7 +19,7 @@ attach_ssd_button(struct wl_list *button_parts, enum lab_node_type type,
struct lab_img *imgs[LAB_BS_ALL + 1],
int x, int y, struct view *view)
{
struct wlr_scene_tree *root = lab_wlr_scene_tree_create(parent);
struct wlr_scene_tree *root = wlr_scene_tree_create(parent);
wlr_scene_node_set_position(&root->node, x, y);
assert(node_type_contains(LAB_NODE_BUTTON, type));
@ -32,7 +31,7 @@ attach_ssd_button(struct wl_list *button_parts, enum lab_node_type type,
/* Hitbox */
float invisible[4] = { 0, 0, 0, 0 };
lab_wlr_scene_rect_create(root, rc.theme->window_button_width,
wlr_scene_rect_create(root, rc.theme->window_button_width,
rc.theme->window_button_height, invisible);
/* Icons */

View file

@ -2,7 +2,6 @@
#include <pixman.h>
#include <wlr/types/wlr_scene.h>
#include "common/scene-helpers.h"
#include "config/rcxml.h"
#include "labwc.h"
#include "output.h"
@ -18,7 +17,7 @@ ssd_extents_create(struct ssd *ssd)
int border_width = MAX(0, MAX(rc.resize_minimum_area, theme->border_width));
ssd->extents.tree = lab_wlr_scene_tree_create(ssd->tree);
ssd->extents.tree = wlr_scene_tree_create(ssd->tree);
struct wlr_scene_tree *parent = ssd->extents.tree;
if (view->fullscreen || view->maximized == VIEW_AXIS_BOTH) {
wlr_scene_node_set_enabled(&parent->node, false);
@ -27,10 +26,10 @@ ssd_extents_create(struct ssd *ssd)
-border_width, -(ssd->titlebar.height + border_width));
float invisible[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
ssd->extents.top = lab_wlr_scene_rect_create(parent, 0, 0, invisible);
ssd->extents.left = lab_wlr_scene_rect_create(parent, 0, 0, invisible);
ssd->extents.right = lab_wlr_scene_rect_create(parent, 0, 0, invisible);
ssd->extents.bottom = lab_wlr_scene_rect_create(parent, 0, 0, invisible);
ssd->extents.top = wlr_scene_rect_create(parent, 0, 0, invisible);
ssd->extents.left = wlr_scene_rect_create(parent, 0, 0, invisible);
ssd->extents.right = wlr_scene_rect_create(parent, 0, 0, invisible);
ssd->extents.bottom = wlr_scene_rect_create(parent, 0, 0, invisible);
/* Initial manual update to keep X11 applications happy */
ssd_extents_update(ssd);
@ -96,7 +95,7 @@ ssd_extents_update(struct ssd *ssd)
wlr_scene_node_set_enabled(&ssd->extents.tree->node, true);
}
if (!output_is_usable(view->output)) {
if (!view->output) {
return;
}

View file

@ -3,7 +3,6 @@
#include <assert.h>
#include <wlr/types/wlr_scene.h>
#include "buffer.h"
#include "common/scene-helpers.h"
#include "config/rcxml.h"
#include "labwc.h"
#include "ssd.h"
@ -184,7 +183,7 @@ make_shadow(struct view *view,
enum wl_output_transform tx)
{
struct wlr_scene_buffer *scene_buf =
lab_wlr_scene_buffer_create(parent, buf);
wlr_scene_buffer_create(parent, buf);
wlr_scene_buffer_set_transform(scene_buf, tx);
scene_buf->point_accepts_input = never_accepts_input;
/*
@ -201,7 +200,7 @@ ssd_shadow_create(struct ssd *ssd)
assert(ssd);
assert(!ssd->shadow.tree);
ssd->shadow.tree = lab_wlr_scene_tree_create(ssd->tree);
ssd->shadow.tree = wlr_scene_tree_create(ssd->tree);
struct theme *theme = ssd->view->server->theme;
struct view *view = ssd->view;
@ -219,7 +218,7 @@ ssd_shadow_create(struct ssd *ssd)
continue;
}
subtree->tree = lab_wlr_scene_tree_create(ssd->shadow.tree);
subtree->tree = wlr_scene_tree_create(ssd->shadow.tree);
struct wlr_scene_tree *parent = subtree->tree;
struct wlr_buffer *corner_top_buffer =
&theme->window[active].shadow_corner_top->base;

View file

@ -7,7 +7,7 @@
#include <wlr/types/wlr_scene.h>
#include "buffer.h"
#include "common/mem.h"
#include "common/scene-helpers.h"
#include "common/string-helpers.h"
#include "config/rcxml.h"
#include "labwc.h"
#include "node.h"
@ -31,14 +31,14 @@ ssd_titlebar_create(struct ssd *ssd)
int width = view->current.width;
int corner_width = ssd_get_corner_width();
ssd->titlebar.tree = lab_wlr_scene_tree_create(ssd->tree);
ssd->titlebar.tree = wlr_scene_tree_create(ssd->tree);
node_descriptor_create(&ssd->titlebar.tree->node,
LAB_NODE_TITLEBAR, view, /*data*/ NULL);
enum ssd_active_state active;
FOR_EACH_ACTIVE_STATE(active) {
struct ssd_titlebar_subtree *subtree = &ssd->titlebar.subtrees[active];
subtree->tree = lab_wlr_scene_tree_create(ssd->titlebar.tree);
subtree->tree = wlr_scene_tree_create(ssd->titlebar.tree);
struct wlr_scene_tree *parent = subtree->tree;
wlr_scene_node_set_enabled(&parent->node, active);
wlr_scene_node_set_position(&parent->node, 0, -theme->titlebar_height);
@ -51,7 +51,7 @@ ssd_titlebar_create(struct ssd *ssd)
&theme->window[active].corner_top_right_normal->base;
/* Background */
subtree->bar = lab_wlr_scene_buffer_create(parent, titlebar_fill);
subtree->bar = wlr_scene_buffer_create(parent, titlebar_fill);
/*
* Work around the wlroots/pixman bug that widened 1px buffer
* becomes translucent when bilinear filtering is used.
@ -64,11 +64,11 @@ ssd_titlebar_create(struct ssd *ssd)
}
wlr_scene_node_set_position(&subtree->bar->node, corner_width, 0);
subtree->corner_left = lab_wlr_scene_buffer_create(parent, corner_top_left);
subtree->corner_left = wlr_scene_buffer_create(parent, corner_top_left);
wlr_scene_node_set_position(&subtree->corner_left->node,
-rc.theme->border_width, -rc.theme->border_width);
subtree->corner_right = lab_wlr_scene_buffer_create(parent, corner_top_right);
subtree->corner_right = wlr_scene_buffer_create(parent, corner_top_right);
wlr_scene_node_set_position(&subtree->corner_right->node,
width - corner_width, -rc.theme->border_width);
@ -440,8 +440,9 @@ ssd_update_title(struct ssd *ssd)
}
struct view *view = ssd->view;
/* view->title is never NULL (instead it can be an empty string) */
assert(view->title);
if (string_null_or_empty(view->title)) {
return;
}
struct theme *theme = view->server->theme;
struct ssd_state_title *state = &ssd->state.title;

View file

@ -12,7 +12,6 @@
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_scene.h>
#include "common/mem.h"
#include "common/scene-helpers.h"
#include "config/rcxml.h"
#include "labwc.h"
#include "node.h"
@ -146,7 +145,7 @@ ssd_create(struct view *view, bool active)
struct ssd *ssd = znew(*ssd);
ssd->view = view;
ssd->tree = lab_wlr_scene_tree_create(view->scene_tree);
ssd->tree = wlr_scene_tree_create(view->scene_tree);
/*
* Attach node_descriptor to the root node so that get_cursor_context()

View file

@ -76,7 +76,9 @@ zdrop(struct lab_data_buffer **buffer)
static void
draw_hover_overlay_on_button(cairo_t *cairo, int w, int h)
{
set_cairo_color(cairo, rc.theme->window_button_hover_bg_color);
/* Overlay (pre-multiplied alpha) */
float overlay_color[4] = { 0.15f, 0.15f, 0.15f, 0.3f};
set_cairo_color(cairo, overlay_color);
int r = rc.theme->window_button_hover_bg_corner_radius;
cairo_new_sub_path(cairo);
@ -558,8 +560,6 @@ theme_builtin(struct theme *theme, struct server *server)
theme->window_button_width = 26;
theme->window_button_height = 26;
theme->window_button_spacing = 0;
parse_hexstr("#80808020", theme->window_button_hover_bg_color);
theme->window_button_hover_bg_corner_radius = 0;
for (enum lab_node_type type = LAB_NODE_BUTTON_FIRST;
@ -787,11 +787,6 @@ entry(struct theme *theme, const char *key, const char *value)
theme->window_button_spacing = get_int_if_positive(
value, "window.button.spacing");
}
/* botton hover overlay */
if (match_glob(key, "window.button.hover.bg.color")) {
parse_color(value, theme->window_button_hover_bg_color);
}
if (match_glob(key, "window.button.hover.bg.corner-radius")) {
theme->window_button_hover_bg_corner_radius = get_int_if_positive(
value, "window.button.hover.bg.corner-radius");

View file

@ -11,11 +11,6 @@ view_impl_map(struct view *view)
{
view_update_visibility(view);
/* Leave minimized, if minimized before map */
if (!view->minimized) {
desktop_focus_view(view, /* raise */ true);
}
if (!view->been_mapped) {
window_rules_apply(view, LAB_WINDOW_RULE_EVENT_ON_FIRST_MAP);
}
@ -46,19 +41,6 @@ view_impl_unmap(struct view *view)
{
view_update_visibility(view);
/*
* When exiting an xwayland application with multiple views
* mapped, a race condition can occur: after the topmost view
* is unmapped, the next view under it is offered focus, but is
* also unmapped before accepting focus (so server->active_view
* remains NULL). To avoid being left with no active view at
* all, check for that case also.
*/
struct server *server = view->server;
if (view == server->active_view || !server->active_view) {
desktop_focus_topmost_view(server);
}
/*
* Destroy the foreign toplevel handle so the unmapped view
* doesn't show up in panels and the like.

View file

@ -14,7 +14,6 @@
#include "common/list.h"
#include "common/match.h"
#include "common/mem.h"
#include "common/string-helpers.h"
#include "config/rcxml.h"
#include "cycle.h"
#include "foreign-toplevel/foreign.h"
@ -268,7 +267,13 @@ matches_criteria(struct view *view, enum lab_view_criteria criteria)
return false;
}
if (criteria & LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) {
if (view->workspace != view->server->workspaces.current) {
/*
* Always-on-top views are always on the current desktop and are
* special in that they live in a different tree.
*/
struct server *server = view->server;
if (view->scene_tree->node.parent != server->workspaces.current->tree
&& !view_is_always_on_top(view)) {
return false;
}
}
@ -278,17 +283,17 @@ matches_criteria(struct view *view, enum lab_view_criteria criteria)
}
}
if (criteria & LAB_VIEW_CRITERIA_ALWAYS_ON_TOP) {
if (view->layer != VIEW_LAYER_ALWAYS_ON_TOP) {
if (!view_is_always_on_top(view)) {
return false;
}
}
if (criteria & LAB_VIEW_CRITERIA_NO_DIALOG) {
if (view_is_modal_dialog(view)) {
if (criteria & LAB_VIEW_CRITERIA_ROOT_TOPLEVEL) {
if (view != view_get_root(view)) {
return false;
}
}
if (criteria & LAB_VIEW_CRITERIA_NO_ALWAYS_ON_TOP) {
if (view->layer == VIEW_LAYER_ALWAYS_ON_TOP) {
if (view_is_always_on_top(view)) {
return false;
}
}
@ -298,7 +303,11 @@ matches_criteria(struct view *view, enum lab_view_criteria criteria)
}
}
if (criteria & LAB_VIEW_CRITERIA_NO_OMNIPRESENT) {
if (view->visible_on_all_workspaces) {
/*
* TODO: Once always-on-top views use a per-workspace
* sub-tree we can remove the check from this condition.
*/
if (view->visible_on_all_workspaces || view_is_always_on_top(view)) {
return false;
}
}
@ -452,6 +461,10 @@ view_discover_output(struct view *view, struct wlr_box *geometry)
if (output && output != view->output) {
view->output = output;
/* Show fullscreen views above top-layer */
if (view->fullscreen) {
desktop_update_top_layer_visibility(view->server);
}
return true;
}
@ -575,20 +588,6 @@ view_move_resize(struct view *view, struct wlr_box geo)
if (view->impl->configure) {
view->impl->configure(view, geo);
}
/*
* If the move/resize was user-initiated (rather than due to
* output layout change), then update the last placement info.
*
* TODO: consider also updating view->output here for floating
* views (based on view->pending) rather than waiting until
* view_moved(). This might eliminate some race conditions with
* view_adjust_for_layout_change(), which uses view->pending.
* Not sure if it might have other side-effects though.
*/
if (!view->adjusting_for_layout_change) {
view_save_last_placement(view);
}
}
void
@ -615,7 +614,7 @@ view_move_relative(struct view *view, int x, int y)
if (view->fullscreen) {
return;
}
view_maximize(view, VIEW_AXIS_NONE);
view_maximize(view, VIEW_AXIS_NONE, /*store_natural_geometry*/ false);
if (view_is_tiled(view)) {
view_set_untiled(view);
view_move_resize(view, view->natural_geometry);
@ -623,12 +622,30 @@ view_move_relative(struct view *view, int x, int y)
view_move(view, view->pending.x + x, view->pending.y + y);
}
static bool
view_compute_near_cursor_position(struct view *view, struct wlr_box *geom)
void
view_move_to_cursor(struct view *view)
{
assert(view);
struct output *pending_output = output_nearest_to_cursor(view->server);
if (!output_is_usable(pending_output)) {
return;
}
view_set_fullscreen(view, false);
view_maximize(view, VIEW_AXIS_NONE, /*store_natural_geometry*/ false);
if (view_is_tiled(view)) {
view_set_untiled(view);
view_move_resize(view, view->natural_geometry);
}
struct border margin = ssd_thickness(view);
struct wlr_box geo = view->pending;
geo.width += margin.left + margin.right;
geo.height += margin.top + margin.bottom;
int x = view->server->seat.cursor->x - (geo.width / 2);
int y = view->server->seat.cursor->y - (geo.height / 2);
struct wlr_box usable = output_usable_area_in_layout_coords(pending_output);
/* Limit usable region to account for gap */
@ -637,30 +654,17 @@ view_compute_near_cursor_position(struct view *view, struct wlr_box *geom)
usable.width -= 2 * rc.gap;
usable.height -= 2 * rc.gap;
if (wlr_box_empty(geom) || wlr_box_empty(&usable)) {
return false;
if (x + geo.width > usable.x + usable.width) {
x = usable.x + usable.width - geo.width;
}
x = MAX(x, usable.x) + margin.left;
struct border margin = ssd_thickness(view);
struct seat *seat = &view->server->seat;
if (y + geo.height > usable.y + usable.height) {
y = usable.y + usable.height - geo.height;
}
y = MAX(y, usable.y) + margin.top;
int total_width = geom->width + margin.left + margin.right;
int total_height = geom->height + margin.top + margin.bottom;
int x = (int)seat->cursor->x - (total_width / 2);
int y = (int)seat->cursor->y - (total_height / 2);
/*
* Order of MIN/MAX is significant here (so that the top-left
* corner of the view remains visible even if the view is larger
* than the usable output area)
*/
x = MIN(x, usable.x + usable.width - total_width);
geom->x = MAX(x, usable.x) + margin.left;
y = MIN(y, usable.y + usable.height - total_height);
geom->y = MAX(y, usable.y) + margin.top;
return true;
view_move(view, x, y);
}
struct view_size_hints
@ -726,7 +730,7 @@ view_adjust_size(struct view *view, int *w, int *h)
}
static void
_minimize(struct view *view, bool minimized, bool *need_refocus)
_minimize(struct view *view, bool minimized)
{
assert(view);
if (view->minimized == minimized) {
@ -739,15 +743,8 @@ _minimize(struct view *view, bool minimized, bool *need_refocus)
view->minimized = minimized;
wl_signal_emit_mutable(&view->events.minimized, NULL);
view_update_visibility(view);
/*
* Need to focus a different view when:
* - minimizing the active view
* - unminimizing any mapped view
*/
*need_refocus |= (minimized ?
(view == view->server->active_view) : view->mapped);
view_update_visibility(view);
}
static void
@ -760,7 +757,7 @@ view_append_children(struct view *view, struct wl_array *children)
}
static void
minimize_sub_views(struct view *view, bool minimized, bool *need_refocus)
minimize_sub_views(struct view *view, bool minimized)
{
struct view **child;
struct wl_array children;
@ -768,8 +765,8 @@ minimize_sub_views(struct view *view, bool minimized, bool *need_refocus)
wl_array_init(&children);
view_append_children(view, &children);
wl_array_for_each(child, &children) {
_minimize(*child, minimized, need_refocus);
minimize_sub_views(*child, minimized, need_refocus);
_minimize(*child, minimized);
minimize_sub_views(*child, minimized);
}
wl_array_release(&children);
}
@ -784,10 +781,8 @@ void
view_minimize(struct view *view, bool minimized)
{
assert(view);
struct server *server = view->server;
bool need_refocus = false;
if (server->input_mode == LAB_INPUT_STATE_CYCLE) {
if (view->server->input_mode == LAB_INPUT_STATE_CYCLE) {
wlr_log(WLR_ERROR, "not minimizing window while window switching");
return;
}
@ -798,20 +793,8 @@ view_minimize(struct view *view, bool minimized)
* 'open file' dialog), so it saves trying to unmap them twice
*/
struct view *root = view_get_root(view);
_minimize(root, minimized, &need_refocus);
minimize_sub_views(root, minimized, &need_refocus);
/*
* Update focus only at the end to avoid repeated focus changes.
* desktop_focus_view() will raise all sibling views together.
*/
if (need_refocus) {
if (minimized) {
desktop_focus_topmost_view(server);
} else {
desktop_focus_view(view, /* raise */ true);
}
}
_minimize(root, minimized);
minimize_sub_views(root, minimized);
}
bool
@ -842,7 +825,6 @@ view_compute_centered_position(struct view *view, const struct wlr_box *ref,
return true;
}
/* Make sure the passed-in view geometry is visible in view->output */
static bool
adjust_floating_geometry(struct view *view, struct wlr_box *geometry,
bool midpoint_visibility)
@ -900,8 +882,16 @@ adjust_floating_geometry(struct view *view, struct wlr_box *geometry,
}
/* Reposition offscreen automatically if configured to do so */
return view_compute_position_by_policy(view, geometry,
/* allow_cursor */ true, rc.placement_policy);
if (rc.placement_policy == LAB_PLACE_AUTOMATIC) {
if (placement_find_best(view, geometry)) {
return true;
}
}
/* If automatic placement failed or was not enabled, just center */
return view_compute_centered_position(view, NULL,
geometry->width, geometry->height,
&geometry->x, &geometry->y);
}
struct wlr_box
@ -911,8 +901,8 @@ view_get_fallback_natural_geometry(struct view *view)
.width = VIEW_FALLBACK_WIDTH,
.height = VIEW_FALLBACK_HEIGHT,
};
view_compute_position_by_policy(view, &box,
/* allow_cursor */ true, rc.placement_policy);
view_compute_centered_position(view, NULL,
box.width, box.height, &box.x, &box.y);
return box;
}
@ -972,16 +962,13 @@ view_center(struct view *view, const struct wlr_box *ref)
* Algorithm based on KWin's implementation:
* https://github.com/KDE/kwin/blob/df9f8f8346b5b7645578e37365dabb1a7b02ca5a/src/placement.cpp#L589
*/
static bool
view_compute_cascaded_position(struct view *view, struct wlr_box *geom)
static void
view_cascade(struct view *view)
{
/* "cascade" policy places a new view at center by default */
struct wlr_box center = *geom;
if (!view_compute_centered_position(view, NULL, center.width,
center.height, &center.x, &center.y)) {
return false;
}
struct wlr_box center = view->pending;
view_compute_centered_position(view, NULL,
center.width, center.height, &center.x, &center.y);
struct border margin = ssd_get_margin(view->ssd);
center.x -= margin.left;
center.y -= margin.top;
@ -1056,48 +1043,34 @@ view_compute_cascaded_position(struct view *view, struct wlr_box *geom)
}
}
geom->x = candidate.x + margin.left;
geom->y = candidate.y + margin.top;
return true;
}
bool
view_compute_position_by_policy(struct view *view, struct wlr_box *geom,
bool allow_cursor, enum lab_placement_policy policy)
{
if (allow_cursor && policy == LAB_PLACE_CURSOR) {
return view_compute_near_cursor_position(view, geom);
} else if (policy == LAB_PLACE_AUTOMATIC) {
return placement_find_best(view, geom);
} else if (policy == LAB_PLACE_CASCADE) {
return view_compute_cascaded_position(view, geom);
} else {
return view_compute_centered_position(view, NULL,
geom->width, geom->height, &geom->x, &geom->y);
}
view_move(view, candidate.x + margin.left, candidate.y + margin.top);
}
void
view_place_by_policy(struct view *view, bool allow_cursor,
enum lab_placement_policy policy)
{
view_set_fullscreen(view, false);
view_maximize(view, VIEW_AXIS_NONE);
if (view_is_tiled(view)) {
view_set_untiled(view);
view_move_resize(view, view->natural_geometry);
if (allow_cursor && policy == LAB_PLACE_CURSOR) {
view_move_to_cursor(view);
return;
} else if (policy == LAB_PLACE_AUTOMATIC) {
struct wlr_box geometry = view->pending;
if (placement_find_best(view, &geometry)) {
view_move(view, geometry.x, geometry.y);
return;
}
} else if (policy == LAB_PLACE_CASCADE) {
view_cascade(view);
return;
}
struct wlr_box geom = view->pending;
if (view_compute_position_by_policy(view, &geom, allow_cursor, policy)) {
view_move(view, geom.x, geom.y);
}
view_center(view, NULL);
}
void
view_constrain_size_to_that_of_usable_area(struct view *view)
{
if (!view || !output_is_usable(view->output) || view->fullscreen) {
if (!view || !view->output || view->fullscreen) {
return;
}
@ -1402,15 +1375,9 @@ view_set_untiled(struct view *view)
view_notify_tiled(view);
}
static bool
in_interactive_move(struct view *view)
{
return (view->server->input_mode == LAB_INPUT_STATE_MOVE
&& view->server->grabbed_view == view);
}
void
view_maximize(struct view *view, enum view_axis axis)
view_maximize(struct view *view, enum view_axis axis,
bool store_natural_geometry)
{
assert(view);
@ -1422,14 +1389,19 @@ view_maximize(struct view *view, enum view_axis axis)
return;
}
bool store_natural_geometry = !in_interactive_move(view);
view_set_shade(view, false);
if (axis != VIEW_AXIS_NONE) {
/*
* Maximize/unmaximize via keybind or client request cancels
* interactive move/resize.
* Maximize via keybind or client request cancels
* interactive move/resize since we can't move/resize
* a maximized view.
*/
interactive_cancel(view);
if (store_natural_geometry && view_is_floating(view)) {
view_invalidate_last_layout_geometry(view);
}
}
/*
* Update natural geometry for any axis that wasn't already
@ -1468,7 +1440,8 @@ view_toggle_maximize(struct view *view, enum view_axis axis)
case VIEW_AXIS_HORIZONTAL:
case VIEW_AXIS_VERTICAL:
/* Toggle one axis (XOR) */
view_maximize(view, view->maximized ^ axis);
view_maximize(view, view->maximized ^ axis,
/*store_natural_geometry*/ true);
break;
case VIEW_AXIS_BOTH:
/*
@ -1476,7 +1449,8 @@ view_toggle_maximize(struct view *view, enum view_axis axis)
* maximized, otherwise unmaximize.
*/
view_maximize(view, (view->maximized == VIEW_AXIS_BOTH) ?
VIEW_AXIS_NONE : VIEW_AXIS_BOTH);
VIEW_AXIS_NONE : VIEW_AXIS_BOTH,
/*store_natural_geometry*/ true);
break;
default:
break;
@ -1539,39 +1513,47 @@ view_toggle_decorations(struct view *view)
}
}
void
view_set_layer(struct view *view, enum view_layer layer)
bool
view_is_always_on_top(struct view *view)
{
assert(view);
if (view->layer == layer) {
return;
}
view->layer = layer;
wlr_scene_node_reparent(&view->scene_tree->node,
view->workspace->view_trees[layer]);
wl_signal_emit_mutable(&view->events.always_on_top, NULL);
return view->scene_tree->node.parent ==
view->server->view_tree_always_on_top;
}
void
view_toggle_always_on_top(struct view *view)
{
assert(view);
if (view->layer == VIEW_LAYER_ALWAYS_ON_TOP) {
view_set_layer(view, VIEW_LAYER_NORMAL);
if (view_is_always_on_top(view)) {
view->workspace = view->server->workspaces.current;
wlr_scene_node_reparent(&view->scene_tree->node,
view->workspace->tree);
} else {
view_set_layer(view, VIEW_LAYER_ALWAYS_ON_TOP);
wlr_scene_node_reparent(&view->scene_tree->node,
view->server->view_tree_always_on_top);
}
}
bool
view_is_always_on_bottom(struct view *view)
{
assert(view);
return view->scene_tree->node.parent ==
view->server->view_tree_always_on_bottom;
}
void
view_toggle_always_on_bottom(struct view *view)
{
assert(view);
if (view->layer == VIEW_LAYER_ALWAYS_ON_BOTTOM) {
view_set_layer(view, VIEW_LAYER_NORMAL);
if (view_is_always_on_bottom(view)) {
view->workspace = view->server->workspaces.current;
wlr_scene_node_reparent(&view->scene_tree->node,
view->workspace->tree);
} else {
view_set_layer(view, VIEW_LAYER_ALWAYS_ON_BOTTOM);
wlr_scene_node_reparent(&view->scene_tree->node,
view->server->view_tree_always_on_bottom);
}
}
@ -1591,7 +1573,7 @@ view_move_to_workspace(struct view *view, struct workspace *workspace)
if (view->workspace != workspace) {
view->workspace = workspace;
wlr_scene_node_reparent(&view->scene_tree->node,
workspace->view_trees[view->layer]);
workspace->tree);
}
}
@ -1708,6 +1690,7 @@ view_set_fullscreen(struct view *view, bool fullscreen)
*/
interactive_cancel(view);
view_store_natural_geometry(view);
view_invalidate_last_layout_geometry(view);
}
set_fullscreen(view, fullscreen);
@ -1725,80 +1708,139 @@ view_set_fullscreen(struct view *view, bool fullscreen)
cursor_update_focus(view->server);
}
void
view_save_last_placement(struct view *view)
static bool
last_layout_geometry_is_valid(struct view *view)
{
assert(view);
struct output *output = view->output;
if (!output_is_usable(output)) {
wlr_log(WLR_ERROR, "cannot save last placement in unusable output");
return;
}
if (!str_equal(view->last_placement.output_name, output->wlr_output->name)) {
xstrdup_replace(view->last_placement.output_name,
output->wlr_output->name);
}
view->last_placement.layout_geo = view->pending;
view->last_placement.relative_geo = view->pending;
view->last_placement.relative_geo.x -= output->scene_output->x;
view->last_placement.relative_geo.y -= output->scene_output->y;
return view->last_layout_geometry.width > 0
&& view->last_layout_geometry.height > 0;
}
static void
clear_last_placement(struct view *view)
update_last_layout_geometry(struct view *view)
{
/*
* Only update an invalid last-layout geometry to prevent a series of
* successive layout changes from continually replacing the "preferred"
* location with whatever location the view currently holds. The
* "preferred" location should be whatever state was set by user
* interaction, not automatic responses to layout changes.
*/
if (last_layout_geometry_is_valid(view)) {
return;
}
if (view_is_floating(view)) {
view->last_layout_geometry = view->pending;
} else if (!wlr_box_empty(&view->natural_geometry)) {
view->last_layout_geometry = view->natural_geometry;
} else {
/* e.g. initially-maximized window */
view->last_layout_geometry =
view_get_fallback_natural_geometry(view);
}
}
static bool
apply_last_layout_geometry(struct view *view, bool force_update)
{
/* Only apply a valid last-layout geometry */
if (!last_layout_geometry_is_valid(view)) {
return false;
}
/*
* Unless forced, the last-layout geometry is only applied
* when the relevant view geometry is distinct.
*/
if (!force_update) {
struct wlr_box *relevant = view_is_floating(view) ?
&view->pending : &view->natural_geometry;
if (wlr_box_equal(relevant, &view->last_layout_geometry)) {
return false;
}
}
view->natural_geometry = view->last_layout_geometry;
adjust_floating_geometry(view, &view->natural_geometry,
/* midpoint_visibility */ true);
return true;
}
void
view_invalidate_last_layout_geometry(struct view *view)
{
assert(view);
zfree(view->last_placement.output_name);
view->last_placement.relative_geo = (struct wlr_box){0};
view->last_placement.layout_geo = (struct wlr_box){0};
view->last_layout_geometry.width = 0;
view->last_layout_geometry.height = 0;
}
void
view_adjust_for_layout_change(struct view *view)
{
assert(view);
if (wlr_box_empty(&view->last_placement.layout_geo)) {
/* Not using assert() just in case */
wlr_log(WLR_ERROR, "view has no last placement info");
return;
bool is_floating = view_is_floating(view);
bool use_natural = false;
if (!output_is_usable(view->output)) {
/* A view losing an output should have a last-layout geometry */
update_last_layout_geometry(view);
}
view->adjusting_for_layout_change = true;
struct wlr_box new_geo;
struct output *output = output_from_name(view->server,
view->last_placement.output_name);
if (output_is_usable(output)) {
/*
* When the previous output (which might have been reconnected
* or relocated) is available, keep the relative position on it.
*/
new_geo = view->last_placement.relative_geo;
new_geo.x += output->scene_output->x;
new_geo.y += output->scene_output->y;
view->output = output;
} else {
/*
* Otherwise, evacuate the view to another output. Use the last
* layout geometry so that the view position is kept when the
* user reconnects the previous output in a different connector
* or the reconnected output somehow gets a different name.
*/
view_discover_output(view, &view->last_placement.layout_geo);
new_geo = view->last_placement.layout_geo;
/* Capture a pointer to the last-layout geometry (only if valid) */
struct wlr_box *last_geometry = NULL;
if (last_layout_geometry_is_valid(view)) {
last_geometry = &view->last_layout_geometry;
}
if (!view_is_floating(view)) {
/*
* Check if an output change is required:
* - Floating views are always mapped to the nearest output
* - Any view without a usable output needs to be repositioned
* - Any view with a valid last-layout geometry might be better
* positioned on another output
*/
if (is_floating || last_geometry || !output_is_usable(view->output)) {
/* Move the view to an appropriate output, if needed */
bool output_changed = view_discover_output(view, last_geometry);
/*
* Try to apply the last-layout to the natural geometry
* (adjusting to ensure that it fits on the screen). This is
* forced if the output has changed, but will be done
* opportunistically even on the same output if the last-layout
* geometry is different from the view's governing geometry.
*/
if (apply_last_layout_geometry(view, output_changed)) {
use_natural = true;
}
/*
* Whether or not the view has moved, the layout has changed.
* Ensure that the view now has a valid last-layout geometry.
*/
update_last_layout_geometry(view);
}
if (!is_floating) {
view_apply_special_geometry(view);
} else if (use_natural) {
/*
* Move the window to its natural location, because
* we are trying to restore a prior layout.
*/
view_apply_natural_geometry(view);
} else {
/* Ensure view is on-screen */
adjust_floating_geometry(view, &new_geo,
/* midpoint_visibility */ true);
view_move_resize(view, new_geo);
/* Otherwise, just ensure the view is on screen. */
struct wlr_box geometry = view->pending;
if (adjust_floating_geometry(view, &geometry,
/* midpoint_visibility */ true)) {
view_move_resize(view, geometry);
}
}
view_update_outputs(view);
view->adjusting_for_layout_change = false;
}
void
@ -1881,7 +1923,7 @@ view_move_to_edge(struct view *view, enum lab_edge direction, bool snap_to_windo
/* Otherwise, move to edge of next adjacent display, if possible */
struct output *output =
output_get_adjacent(view->output, direction, /* wrap */ false);
if (!output_is_usable(output)) {
if (!output) {
return;
}
@ -2022,7 +2064,7 @@ view_placement_parse(const char *policy)
void
view_snap_to_edge(struct view *view, enum lab_edge edge,
bool across_outputs, bool combine)
bool across_outputs, bool combine, bool store_natural_geometry)
{
assert(view);
@ -2036,7 +2078,6 @@ view_snap_to_edge(struct view *view, enum lab_edge edge,
return;
}
bool store_natural_geometry = !in_interactive_move(view);
view_set_shade(view, false);
if (lab_edge_is_cardinal(edge) && view->maximized == VIEW_AXIS_NONE
@ -2061,7 +2102,7 @@ view_snap_to_edge(struct view *view, enum lab_edge edge,
*/
output = output_get_adjacent(view->output, edge,
/* wrap */ false);
if (!output_is_usable(output)) {
if (!output) {
return;
}
edge = invert_edge;
@ -2083,10 +2124,12 @@ view_snap_to_edge(struct view *view, enum lab_edge edge,
if (view->maximized != VIEW_AXIS_NONE) {
/* Unmaximize + keep using existing natural_geometry */
view_maximize(view, VIEW_AXIS_NONE);
view_maximize(view, VIEW_AXIS_NONE,
/*store_natural_geometry*/ false);
} else if (store_natural_geometry) {
/* store current geometry as new natural_geometry */
view_store_natural_geometry(view);
view_invalidate_last_layout_geometry(view);
}
view_set_untiled(view);
view_set_output(view, output);
@ -2096,7 +2139,8 @@ view_snap_to_edge(struct view *view, enum lab_edge edge,
}
void
view_snap_to_region(struct view *view, struct region *region)
view_snap_to_region(struct view *view, struct region *region,
bool store_natural_geometry)
{
assert(view);
assert(region);
@ -2111,15 +2155,16 @@ view_snap_to_region(struct view *view, struct region *region)
return;
}
bool store_natural_geometry = !in_interactive_move(view);
view_set_shade(view, false);
if (view->maximized != VIEW_AXIS_NONE) {
/* Unmaximize + keep using existing natural_geometry */
view_maximize(view, VIEW_AXIS_NONE);
view_maximize(view, VIEW_AXIS_NONE,
/*store_natural_geometry*/ false);
} else if (store_natural_geometry) {
/* store current geometry as new natural_geometry */
view_store_natural_geometry(view);
view_invalidate_last_layout_geometry(view);
}
view_set_untiled(view);
view->tiled_region = region;
@ -2132,6 +2177,7 @@ view_move_to_output(struct view *view, struct output *output)
{
assert(view);
view_invalidate_last_layout_geometry(view);
view_set_output(view, output);
if (view_is_floating(view)) {
struct wlr_box output_area = output_usable_area_in_layout_coords(output);
@ -2147,7 +2193,7 @@ view_move_to_output(struct view *view, struct output *output)
view_apply_tiled_geometry(view);
} else if (view->tiled_region) {
struct region *region = regions_from_name(view->tiled_region->name, output);
view_snap_to_region(view, region);
view_snap_to_region(view, region, /*store_natural_geometry*/ false);
}
}
@ -2191,18 +2237,6 @@ void
view_move_to_front(struct view *view)
{
assert(view);
struct server *server = view->server;
assert(!wl_list_empty(&server->views));
/*
* Check whether the view is already in front, or is the root
* parent of the view in front (in which case we don't want to
* raise it in front of its sub-view).
*/
struct view *front = wl_container_of(server->views.next, front, link);
if (view == front || view == view_get_root(front)) {
return;
}
struct view *root = view_get_root(view);
assert(root);
@ -2223,9 +2257,7 @@ view_move_to_front(struct view *view)
* to an incorrect X window depending on timing. To mitigate the
* race, perform an explicit flush after restacking.
*/
if (view->type == LAB_XWAYLAND_VIEW) {
xwayland_flush(view->server);
}
#endif
cursor_update_focus(view->server);
desktop_update_top_layer_visibility(view->server);
@ -2245,20 +2277,15 @@ view_move_to_back(struct view *view)
desktop_update_top_layer_visibility(view->server);
}
bool
view_is_modal_dialog(struct view *view)
{
assert(view);
assert(view->impl->is_modal_dialog);
return view->impl->is_modal_dialog(view);
}
struct view *
view_get_modal_dialog(struct view *view)
{
assert(view);
if (!view->impl->is_modal_dialog) {
return NULL;
}
/* check view itself first */
if (view_is_modal_dialog(view)) {
if (view->impl->is_modal_dialog(view)) {
return view;
}
@ -2271,7 +2298,7 @@ view_get_modal_dialog(struct view *view)
wl_array_init(&children);
view_append_children(root, &children);
wl_array_for_each(child, &children) {
if (view_is_modal_dialog(*child)) {
if (view->impl->is_modal_dialog(*child)) {
dialog = *child;
break;
}
@ -2382,12 +2409,30 @@ view_update_visibility(struct view *view)
}
wlr_scene_node_set_enabled(&view->scene_tree->node, visible);
struct server *server = view->server;
if (visible) {
desktop_focus_view(view, /*raise*/ true);
} else {
/*
* When exiting an xwayland application with multiple
* views mapped, a race condition can occur: after the
* topmost view is unmapped, the next view under it is
* offered focus, but is also unmapped before accepting
* focus (so server->active_view remains NULL). To avoid
* being left with no active view at all, check for that
* case also.
*/
if (view == server->active_view || !server->active_view) {
desktop_focus_topmost_view(server);
}
}
/*
* Show top layer when a fullscreen view is hidden.
* Hide it if a fullscreen view is shown (or uncovered).
*/
desktop_update_top_layer_visibility(view->server);
desktop_update_top_layer_visibility(server);
/*
* We may need to disable adaptive sync if view was fullscreen.
@ -2402,12 +2447,7 @@ view_update_visibility(struct view *view)
/* Update usable area to account for XWayland "struts" (panels) */
if (view_has_strut_partial(view)) {
output_update_all_usable_areas(view->server, false);
}
/* View might have been unmapped/minimized during move/resize */
if (!visible) {
interactive_cancel(view);
output_update_all_usable_areas(server, false);
}
}
@ -2478,7 +2518,6 @@ view_init(struct view *view)
wl_signal_init(&view->events.minimized);
wl_signal_init(&view->events.fullscreened);
wl_signal_init(&view->events.activated);
wl_signal_init(&view->events.always_on_top);
wl_signal_init(&view->events.set_icon);
wl_signal_init(&view->events.destroy);
@ -2507,16 +2546,16 @@ view_destroy(struct view *view)
wl_list_remove(&view->set_title.link);
wl_list_remove(&view->destroy.link);
zfree(view->title);
zfree(view->app_id);
if (view->foreign_toplevel) {
foreign_toplevel_destroy(view->foreign_toplevel);
view->foreign_toplevel = NULL;
}
/*
* This check is (in theory) redundant since interactive_cancel()
* is called at unmap. Leaving it here just to be sure.
*/
if (server->grabbed_view == view) {
/* Application got killed while moving around */
interactive_cancel(view);
}
@ -2537,7 +2576,6 @@ view_destroy(struct view *view)
undecorate(view);
clear_last_placement(view);
view_set_icon(view, NULL, NULL);
menu_on_view_destroy(view);
@ -2558,13 +2596,9 @@ view_destroy(struct view *view)
assert(wl_list_empty(&view->events.minimized.listener_list));
assert(wl_list_empty(&view->events.fullscreened.listener_list));
assert(wl_list_empty(&view->events.activated.listener_list));
assert(wl_list_empty(&view->events.always_on_top.listener_list));
assert(wl_list_empty(&view->events.set_icon.listener_list));
assert(wl_list_empty(&view->events.destroy.listener_list));
zfree(view->title);
zfree(view->app_id);
/* Remove view from server->views */
wl_list_remove(&view->link);
free(view);

View file

@ -110,10 +110,6 @@ window_rules_get_property(struct view *view, const char *property)
&& !strcasecmp(property, "iconPreferClient")) {
return rule->icon_prefer_client;
}
if (rule->allow_always_on_top
&& !strcasecmp(property, "allowAlwaysOnTop")) {
return rule->allow_always_on_top;
}
}
}
return LAB_PROP_UNSPECIFIED;

View file

@ -15,7 +15,6 @@
#include "common/graphic-helpers.h"
#include "common/list.h"
#include "common/mem.h"
#include "common/scene-helpers.h"
#include "config/rcxml.h"
#include "input/keyboard.h"
#include "labwc.h"
@ -164,7 +163,7 @@ _osd_update(struct server *server)
cairo_destroy(cairo);
if (!output->workspace_osd) {
output->workspace_osd = lab_wlr_scene_buffer_create(
output->workspace_osd = wlr_scene_buffer_create(
&server->scene->tree, NULL);
}
/* Position the whole thing */
@ -183,33 +182,6 @@ _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)
@ -235,19 +207,20 @@ add_workspace(struct server *server, const char *name)
struct workspace *workspace = znew(*workspace);
workspace->server = server;
workspace->name = xstrdup(name);
workspace->tree = lab_wlr_scene_tree_create(server->workspace_tree);
workspace->view_trees[VIEW_LAYER_ALWAYS_ON_BOTTOM] =
lab_wlr_scene_tree_create(workspace->tree);
workspace->view_trees[VIEW_LAYER_NORMAL] =
lab_wlr_scene_tree_create(workspace->tree);
workspace->view_trees[VIEW_LAYER_ALWAYS_ON_TOP] =
lab_wlr_scene_tree_create(workspace->tree);
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;
/* 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,
@ -258,6 +231,7 @@ 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,
@ -420,31 +394,10 @@ workspaces_init(struct server *server)
wl_list_init(&server->workspaces.all);
struct workspace_config *conf;
struct workspace *conf;
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);
}
/*
@ -470,13 +423,11 @@ workspaces_switch_to(struct workspace *target, bool update_focus)
lab_ext_workspace_set_active(
server->workspaces.current->ext_workspace, false);
/*
* Move Omnipresent views to new workspace.
* Not using for_each_view() since it skips views that
* view_is_focusable() returns false (e.g. Conky).
*/
/* Move Omnipresent views to new workspace */
struct view *view;
wl_list_for_each_reverse(view, &server->views, link) {
enum lab_view_criteria criteria =
LAB_VIEW_CRITERIA_CURRENT_WORKSPACE;
for_each_view_reverse(view, &server->views, criteria) {
if (view->visible_on_all_workspaces) {
view_move_to_workspace(view, target);
}
@ -492,17 +443,24 @@ workspaces_switch_to(struct workspace *target, bool update_focus)
server->workspaces.current = target;
struct view *grabbed_view = server->grabbed_view;
if (grabbed_view) {
if (grabbed_view && !view_is_always_on_top(grabbed_view)) {
view_move_to_workspace(grabbed_view, target);
}
/*
* Make sure we are focusing what the user sees. Only refocus if
* the focus is not already on an omnipresent view.
* the focus is not already on an omnipresent or always-on-top view.
*
* TODO: Decouple always-on-top views from the omnipresent state.
* One option for that would be to create a new scene tree
* as child of every workspace tree and then reparent a-o-t
* windows to that one. Combined with adjusting the condition
* below that should take care of the issue.
*/
if (update_focus) {
struct view *active_view = server->active_view;
if (!(active_view && active_view->visible_on_all_workspaces)) {
if (!active_view || (!active_view->visible_on_all_workspaces
&& !view_is_always_on_top(active_view))) {
desktop_focus_topmost_view(server);
}
}
@ -549,13 +507,21 @@ workspaces_find(struct workspace *anchor, const char *name, bool wrap)
if (!name) {
return NULL;
}
struct server *server = anchor->server;
struct wl_list *workspaces = &server->workspaces.all;
size_t index = 0;
struct workspace *target;
size_t wants_index = parse_workspace_index(name);
struct wl_list *workspaces = &anchor->server->workspaces.all;
if (!strcasecmp(name, "current")) {
if (wants_index) {
wl_list_for_each(target, workspaces, link) {
if (wants_index == ++index) {
return target;
}
}
} else if (!strcasecmp(name, "current")) {
return anchor;
} else if (!strcasecmp(name, "last")) {
return server->workspaces.last;
return anchor->server->workspaces.last;
} else if (!strcasecmp(name, "left")) {
return get_prev(anchor, workspaces, wrap);
} else if (!strcasecmp(name, "right")) {
@ -564,8 +530,15 @@ 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;
}
return workspace_find_by_name(server, name);
}
}
wlr_log(WLR_ERROR, "Workspace '%s' not found", name);
return NULL;
}
static void
@ -592,34 +565,36 @@ workspaces_reconfigure(struct server *server)
* - Destroy workspaces if fewer workspace are desired
*/
struct wl_list *workspace_link = server->workspaces.all.next;
struct wl_list *actual_workspace_link = server->workspaces.all.next;
struct workspace_config *conf;
wl_list_for_each(conf, &rc.workspace_config.workspaces, link) {
struct workspace *workspace = wl_container_of(
workspace_link, workspace, link);
struct workspace *configured_workspace;
wl_list_for_each(configured_workspace,
&rc.workspace_config.workspaces, link) {
struct workspace *actual_workspace = wl_container_of(
actual_workspace_link, actual_workspace, link);
if (workspace_link == &server->workspaces.all) {
if (actual_workspace_link == &server->workspaces.all) {
/* # of configured workspaces increased */
wlr_log(WLR_DEBUG, "Adding workspace \"%s\"",
conf->name);
add_workspace(server, conf->name);
configured_workspace->name);
add_workspace(server, configured_workspace->name);
continue;
}
if (strcmp(workspace->name, conf->name)) {
if (strcmp(actual_workspace->name, configured_workspace->name)) {
/* Workspace is renamed */
wlr_log(WLR_DEBUG, "Renaming workspace \"%s\" to \"%s\"",
workspace->name, conf->name);
xstrdup_replace(workspace->name, conf->name);
actual_workspace->name, configured_workspace->name);
free(actual_workspace->name);
actual_workspace->name = xstrdup(configured_workspace->name);
lab_cosmic_workspace_set_name(
workspace->cosmic_workspace, workspace->name);
actual_workspace->cosmic_workspace, actual_workspace->name);
lab_ext_workspace_set_name(
workspace->ext_workspace, workspace->name);
actual_workspace->ext_workspace, actual_workspace->name);
}
workspace_link = workspace_link->next;
actual_workspace_link = actual_workspace_link->next;
}
if (workspace_link == &server->workspaces.all) {
if (actual_workspace_link == &server->workspaces.all) {
return;
}
@ -628,30 +603,30 @@ workspaces_reconfigure(struct server *server)
struct workspace *first_workspace =
wl_container_of(server->workspaces.all.next, first_workspace, link);
while (workspace_link != &server->workspaces.all) {
struct workspace *workspace = wl_container_of(
workspace_link, workspace, link);
while (actual_workspace_link != &server->workspaces.all) {
struct workspace *actual_workspace = wl_container_of(
actual_workspace_link, actual_workspace, link);
wlr_log(WLR_DEBUG, "Destroying workspace \"%s\"",
workspace->name);
actual_workspace->name);
struct view *view;
wl_list_for_each(view, &server->views, link) {
if (view->workspace == workspace) {
if (view->workspace == actual_workspace) {
view_move_to_workspace(view, first_workspace);
}
}
if (server->workspaces.current == workspace) {
if (server->workspaces.current == actual_workspace) {
workspaces_switch_to(first_workspace,
/* update_focus */ true);
}
if (server->workspaces.last == workspace) {
if (server->workspaces.last == actual_workspace) {
server->workspaces.last = first_workspace;
}
workspace_link = workspace_link->next;
destroy_workspace(workspace);
actual_workspace_link = actual_workspace_link->next;
destroy_workspace(actual_workspace);
}
}

View file

@ -164,8 +164,6 @@ xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup)
}
wlr_popup->base->surface->data =
wlr_scene_xdg_surface_create(parent_tree, wlr_popup->base);
die_if_null(wlr_popup->base->surface->data);
node_descriptor_create(wlr_popup->base->surface->data,
LAB_NODE_XDG_POPUP, view, /*data*/ NULL);
}

151
src/xdg.c
View file

@ -13,7 +13,6 @@
#include "common/box.h"
#include "common/macros.h"
#include "common/mem.h"
#include "common/scene-helpers.h"
#include "config/rcxml.h"
#include "decorations.h"
#include "foreign-toplevel/foreign.h"
@ -56,13 +55,6 @@ xdg_toplevel_from_view(struct view *view)
return xdg_surface->toplevel;
}
static struct view *
xdg_toplevel_view_get_parent(struct view *view)
{
struct wlr_xdg_toplevel *toplevel = xdg_toplevel_from_view(view);
return toplevel->parent ? toplevel->parent->base->data : NULL;
}
static struct view_size_hints
xdg_toplevel_view_get_size_hints(struct view *view)
{
@ -122,35 +114,20 @@ set_fullscreen_from_request(struct view *view,
view_set_fullscreen(view, requested->fullscreen);
}
/* Called from commit handler and updates view->pending.x/y directly */
static void
set_initial_position(struct view *view)
do_late_positioning(struct view *view)
{
if (!view_is_floating(view)) {
return;
}
view_constrain_size_to_that_of_usable_area(view);
struct server *server = view->server;
if (server->input_mode == LAB_INPUT_STATE_MOVE
&& view == server->grabbed_view) {
/* Reposition the view while anchoring it to cursor */
interactive_anchor_to_cursor(server, &view->pending);
} else {
struct view *parent = xdg_toplevel_view_get_parent(view);
if (parent) {
/* Center relative to parent view */
view_compute_centered_position(view, &parent->pending,
/* TODO: smart placement? */
view_compute_centered_position(view, NULL,
view->pending.width, view->pending.height,
&view->pending.x, &view->pending.y);
} else {
view_compute_position_by_policy(view, &view->pending,
/* allow_cursor */ true, rc.placement_policy);
}
}
view_save_last_placement(view);
}
static void
@ -194,7 +171,7 @@ center_fullscreen_if_needed(struct view *view)
if (!xdg_view->fullscreen_bg) {
const float black[4] = {0, 0, 0, 1};
xdg_view->fullscreen_bg =
lab_wlr_scene_rect_create(view->scene_tree, 0, 0, black);
wlr_scene_rect_create(view->scene_tree, 0, 0, black);
wlr_scene_node_lower_to_bottom(&xdg_view->fullscreen_bg->node);
}
@ -229,15 +206,7 @@ handle_commit(struct wl_listener *listener, void *data)
| WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE;
wlr_xdg_toplevel_set_wm_capabilities(toplevel, wm_caps);
/* Put view on the same output as its parent if possible */
struct view *parent = xdg_toplevel_view_get_parent(view);
if (parent && output_is_usable(parent->output)) {
view_set_output(view, parent->output);
} else {
view_set_output(view, output_nearest_to_cursor(view->server));
}
if (output_is_usable(view->output)) {
if (view->output) {
wlr_xdg_toplevel_set_bounds(toplevel,
view->output->usable_area.width,
view->output->usable_area.height);
@ -256,7 +225,8 @@ handle_commit(struct wl_listener *listener, void *data)
set_fullscreen_from_request(view, &toplevel->requested);
}
if (toplevel->requested.maximized) {
view_maximize(view, VIEW_AXIS_BOTH);
view_maximize(view, VIEW_AXIS_BOTH,
/*store_natural_geometry*/ true);
}
return;
}
@ -265,16 +235,15 @@ handle_commit(struct wl_listener *listener, void *data)
bool update_required = false;
/*
* The pending size will be empty in two cases:
* (1) when the view is first mapped
* (2) when leaving fullscreen or un-maximizing, if the view
* was initially fullscreen/maximized and the natural
* geometry isn't known yet
* If we didn't know the natural size when leaving fullscreen or
* unmaximizing, then the pending size will be 0x0. In this case,
* the pending x/y is also unset and we still need to position
* the window.
*/
if (wlr_box_empty(&view->pending) && !wlr_box_empty(&size)) {
view->pending.width = size.width;
view->pending.height = size.height;
set_initial_position(view);
do_late_positioning(view);
update_required = true;
}
@ -486,11 +455,11 @@ handle_request_move(struct wl_listener *listener, void *data)
* the provided serial against a list of button press serials sent to
* this client, to prevent the client from requesting this whenever they
* want.
*
* Note: interactive_begin() checks that view == server->grabbed_view.
*/
struct view *view = wl_container_of(listener, view, request_move);
if (view == view->server->seat.pressed.ctx.view) {
interactive_begin(view, LAB_INPUT_STATE_MOVE, LAB_EDGE_NONE);
}
}
static void
@ -503,12 +472,12 @@ handle_request_resize(struct wl_listener *listener, void *data)
* the provided serial against a list of button press serials sent to
* this client, to prevent the client from requesting this whenever they
* want.
*
* Note: interactive_begin() checks that view == server->grabbed_view.
*/
struct wlr_xdg_toplevel_resize_event *event = data;
struct view *view = wl_container_of(listener, view, request_resize);
if (view == view->server->seat.pressed.ctx.view) {
interactive_begin(view, LAB_INPUT_STATE_RESIZE, event->edges);
}
}
static void
@ -532,8 +501,12 @@ handle_request_maximize(struct wl_listener *listener, void *data)
return;
}
if (!view->mapped && !view->output) {
view_set_output(view, output_nearest_to_cursor(view->server));
}
bool maximized = toplevel->requested.maximized;
view_maximize(view, maximized ? VIEW_AXIS_BOTH : VIEW_AXIS_NONE);
view_maximize(view, maximized ? VIEW_AXIS_BOTH : VIEW_AXIS_NONE,
/*store_natural_geometry*/ true);
}
static void
@ -550,6 +523,9 @@ handle_request_fullscreen(struct wl_listener *listener, void *data)
return;
}
if (!view->mapped && !view->output) {
view_set_output(view, output_nearest_to_cursor(view->server));
}
set_fullscreen_from_request(view,
&xdg_toplevel_from_view(view)->requested);
}
@ -662,6 +638,14 @@ xdg_toplevel_view_minimize(struct view *view, bool minimized)
/* noop */
}
static struct view *
xdg_toplevel_view_get_parent(struct view *view)
{
struct wlr_xdg_toplevel *toplevel = xdg_toplevel_from_view(view);
return toplevel->parent ?
(struct view *)toplevel->parent->base->data : NULL;
}
static struct wlr_xdg_toplevel *
top_parent_of(struct view *view)
{
@ -808,6 +792,23 @@ xdg_toplevel_view_notify_tiled(struct view *view)
}
}
static void
set_initial_position(struct view *view)
{
view_constrain_size_to_that_of_usable_area(view);
struct view *parent = xdg_toplevel_view_get_parent(view);
if (parent) {
/* Child views are center-aligned relative to their parents */
view_set_output(view, parent->output);
view_center(view, &parent->pending);
return;
}
/* All other views are placed according to a configured strategy */
view_place_by_policy(view, /* allow_cursor */ true, rc.placement_policy);
}
static void
handle_map(struct wl_listener *listener, void *data)
{
@ -816,6 +817,14 @@ handle_map(struct wl_listener *listener, void *data)
return;
}
/*
* An output should have been chosen when the surface was first
* created, but take one more opportunity to assign an output if not.
*/
if (!view->output) {
view_set_output(view, output_nearest_to_cursor(view->server));
}
view->mapped = true;
if (!view->been_mapped) {
@ -824,6 +833,36 @@ handle_map(struct wl_listener *listener, void *data)
} else {
view_set_ssd_mode(view, LAB_SSD_MODE_NONE);
}
/*
* Set initial "pending" dimensions. "Current"
* dimensions remain zero until handle_commit().
*/
if (wlr_box_empty(&view->pending)) {
struct wlr_xdg_surface *xdg_surface =
xdg_surface_from_view(view);
view->pending.width = xdg_surface->geometry.width;
view->pending.height = xdg_surface->geometry.height;
}
/*
* Set initial "pending" position for floating views.
*/
if (view_is_floating(view)) {
set_initial_position(view);
}
/* Disable background fill at map (paranoid?) */
disable_fullscreen_bg(view);
/*
* Set initial "current" position directly before
* calling view_moved() to reduce flicker
*/
view->current.x = view->pending.x;
view->current.y = view->pending.y;
view_moved(view);
}
view_impl_map(view);
@ -974,25 +1013,25 @@ handle_new_xdg_toplevel(struct wl_listener *listener, void *data)
* Pick an output for the surface as soon as its created, so that the
* client can be notified about any fractional scale before it is given
* the chance to configure itself (and possibly pick its dimensions).
*
* FIXME: this may be the wrong output since the parent view isn't
* known yet. The correct output will be set at initial commit.
*/
view_set_output(view, output_nearest_to_cursor(server));
if (output_is_usable(view->output)) {
if (view->output) {
wlr_fractional_scale_v1_notify_scale(xdg_surface->surface,
view->output->wlr_output->scale);
}
view->workspace = server->workspaces.current;
view->scene_tree = lab_wlr_scene_tree_create(
view->workspace->view_trees[VIEW_LAYER_NORMAL]);
view->scene_tree = wlr_scene_tree_create(view->workspace->tree);
wlr_scene_node_set_enabled(&view->scene_tree->node, false);
struct wlr_scene_tree *tree = wlr_scene_xdg_surface_create(
view->scene_tree, xdg_surface);
die_if_null(tree);
if (!tree) {
/* TODO: might need further clean up */
wl_resource_post_no_memory(xdg_surface->resource);
free(xdg_toplevel_view);
return;
}
view->content_tree = tree;
node_descriptor_create(&view->scene_tree->node,
LAB_NODE_VIEW, view, /*data*/ NULL);

View file

@ -68,11 +68,9 @@ handle_map(struct wl_listener *listener, void *data)
seat_focus_surface(&unmanaged->server->seat, xsurface->surface);
}
struct wlr_scene_surface *scene_surface = wlr_scene_surface_create(
unmanaged->server->unmanaged_tree, xsurface->surface);
die_if_null(scene_surface);
unmanaged->node = &scene_surface->buffer->node;
unmanaged->node = &wlr_scene_surface_create(
unmanaged->server->unmanaged_tree,
xsurface->surface)->buffer->node;
wlr_scene_node_set_position(unmanaged->node, xsurface->x, xsurface->y);
cursor_update_focus(unmanaged->server);
}

View file

@ -13,7 +13,6 @@
#include "common/array.h"
#include "common/macros.h"
#include "common/mem.h"
#include "common/scene-helpers.h"
#include "config/rcxml.h"
#include "config/session.h"
#include "foreign-toplevel/foreign.h"
@ -229,7 +228,7 @@ ensure_initial_geometry_and_output(struct view *view)
view->pending = view->current;
}
}
if (!output_is_usable(view->output)) {
if (!view->output) {
/*
* Just use the cursor output since we don't know yet
* whether the surface position is meaningful.
@ -289,11 +288,11 @@ handle_request_move(struct wl_listener *listener, void *data)
* the provided serial against a list of button press serials sent to
* this client, to prevent the client from requesting this whenever they
* want.
*
* Note: interactive_begin() checks that view == server->grabbed_view.
*/
struct view *view = wl_container_of(listener, view, request_move);
if (view == view->server->seat.pressed.ctx.view) {
interactive_begin(view, LAB_INPUT_STATE_MOVE, LAB_EDGE_NONE);
}
}
static void
@ -306,12 +305,12 @@ handle_request_resize(struct wl_listener *listener, void *data)
* the provided serial against a list of button press serials sent to
* this client, to prevent the client from requesting this whenever they
* want.
*
* Note: interactive_begin() checks that view == server->grabbed_view.
*/
struct wlr_xwayland_resize_event *event = data;
struct view *view = wl_container_of(listener, view, request_resize);
if (view == view->server->seat.pressed.ctx.view) {
interactive_begin(view, LAB_INPUT_STATE_RESIZE, event->edges);
}
}
static void
@ -356,9 +355,7 @@ handle_destroy(struct wl_listener *listener, void *data)
/* Remove XWayland view specific listeners */
wl_list_remove(&xwayland_view->associate.link);
wl_list_remove(&xwayland_view->dissociate.link);
wl_list_remove(&xwayland_view->request_above.link);
wl_list_remove(&xwayland_view->request_activate.link);
wl_list_remove(&xwayland_view->request_close.link);
wl_list_remove(&xwayland_view->request_configure.link);
wl_list_remove(&xwayland_view->set_class.link);
wl_list_remove(&xwayland_view->set_decorations.link);
@ -368,8 +365,6 @@ handle_destroy(struct wl_listener *listener, void *data)
wl_list_remove(&xwayland_view->focus_in.link);
wl_list_remove(&xwayland_view->map_request.link);
wl_list_remove(&xwayland_view->on_view.always_on_top.link);
view_destroy(view);
}
@ -427,22 +422,6 @@ handle_request_configure(struct wl_listener *listener, void *data)
}
}
static void
handle_request_above(struct wl_listener *listener, void *data)
{
struct xwayland_view *xwayland_view =
wl_container_of(listener, xwayland_view, request_above);
struct view *view = &xwayland_view->base;
if (window_rules_get_property(view, "allowAlwaysOnTop") != LAB_PROP_TRUE) {
wlr_log(WLR_INFO, "X11 client side always on top request rejected");
return;
}
view_set_layer(view, xwayland_view->xwayland_surface->above
? VIEW_LAYER_ALWAYS_ON_TOP : VIEW_LAYER_NORMAL);
}
static void
handle_request_activate(struct wl_listener *listener, void *data)
{
@ -458,15 +437,6 @@ handle_request_activate(struct wl_listener *listener, void *data)
desktop_focus_view(view, /*raise*/ true);
}
static void
handle_request_close(struct wl_listener *listener, void *data)
{
struct xwayland_view *xwayland_view =
wl_container_of(listener, xwayland_view, request_close);
view_close(&xwayland_view->base);
}
static void
handle_request_minimize(struct wl_listener *listener, void *data)
{
@ -500,7 +470,7 @@ handle_request_maximize(struct wl_listener *listener, void *data)
if (surf->maximized_horz) {
maximize |= VIEW_AXIS_HORIZONTAL;
}
view_maximize(view, maximize);
view_maximize(view, maximize, /*store_natural_geometry*/ true);
}
static void
@ -734,12 +704,7 @@ handle_map_request(struct wl_listener *listener, void *data)
if (xsurface->maximized_vert) {
axis |= VIEW_AXIS_VERTICAL;
}
view_maximize(view, axis);
if (window_rules_get_property(view, "allowAlwaysOnTop") == LAB_PROP_TRUE) {
view_set_layer(view, xsurface->above
? VIEW_LAYER_ALWAYS_ON_TOP : VIEW_LAYER_NORMAL);
}
view_maximize(view, axis, /*store_natural_geometry*/ true);
/*
* We could also call set_initial_position() here, but it's not
* really necessary until the view is actually mapped (and at
@ -780,26 +745,18 @@ set_initial_position(struct view *view,
/* allow_cursor */ true, rc.placement_policy);
} else {
/*
* View is maximized/fullscreen. Place the
* View is maximized/fullscreen. Center the
* stored natural geometry without actually
* moving the view.
*
* FIXME: this positioning will be slightly off
* since it uses border widths computed for the
* current (non-floating) state of the view.
* Possible fixes would be (1) adjust the natural
* geometry earlier, while still floating, or
* (2) add a variant of ssd_thickness() that
* disregards the current view state.
*/
view_compute_position_by_policy(view, &view->natural_geometry,
/* allow_cursor */ true, rc.placement_policy);
view_compute_centered_position(view, NULL,
view->natural_geometry.width,
view->natural_geometry.height,
&view->natural_geometry.x,
&view->natural_geometry.y);
}
}
/* view->last_placement is still unset if has_position=true */
view_save_last_placement(view);
/*
* Always make sure the view is onscreen and adjusted for any
* layout changes that could have occurred between map_request
@ -853,7 +810,11 @@ handle_map(struct wl_listener *listener, void *data)
if (!view->content_tree) {
view->content_tree = wlr_scene_subsurface_tree_create(
view->scene_tree, view->surface);
die_if_null(view->content_tree);
if (!view->content_tree) {
/* TODO: might need further clean up */
wl_resource_post_no_memory(view->surface->resource);
return;
}
}
wlr_scene_node_set_enabled(&view->content_tree->node, !view->shaded);
@ -985,14 +946,6 @@ xwayland_view_set_activated(struct view *view, bool activated)
}
wlr_xwayland_surface_activate(xwayland_surface, activated);
/*
* Make sure that the X11-protocol messages (SetInputFocus etc.)
* are sent immediately. This mitigates a race where the XWayland
* server may generate an unwanted FocusOut event for the newly
* activated window, if it receives mouse/pointer events over the
* parallel wayland connection first.
*/
xwayland_flush(view->server);
}
static void
@ -1034,15 +987,6 @@ static const struct view_impl xwayland_view_impl = {
.get_pid = xwayland_view_get_pid,
};
static void
handle_always_on_top(struct wl_listener *listener, void *data)
{
struct xwayland_view *xwayland_view =
wl_container_of(listener, xwayland_view, on_view.always_on_top);
wlr_xwayland_surface_set_above(xwayland_view->xwayland_surface,
xwayland_view->base.layer == VIEW_LAYER_ALWAYS_ON_TOP);
}
void
xwayland_view_create(struct server *server,
struct wlr_xwayland_surface *xsurface, bool mapped)
@ -1066,8 +1010,7 @@ xwayland_view_create(struct server *server,
xsurface->data = view;
view->workspace = server->workspaces.current;
view->scene_tree = lab_wlr_scene_tree_create(
view->workspace->view_trees[VIEW_LAYER_NORMAL]);
view->scene_tree = wlr_scene_tree_create(view->workspace->tree);
node_descriptor_create(&view->scene_tree->node,
LAB_NODE_VIEW, view, /*data*/ NULL);
@ -1082,9 +1025,7 @@ xwayland_view_create(struct server *server,
/* Events specific to XWayland views */
CONNECT_SIGNAL(xsurface, xwayland_view, associate);
CONNECT_SIGNAL(xsurface, xwayland_view, dissociate);
CONNECT_SIGNAL(xsurface, xwayland_view, request_above);
CONNECT_SIGNAL(xsurface, xwayland_view, request_activate);
CONNECT_SIGNAL(xsurface, xwayland_view, request_close);
CONNECT_SIGNAL(xsurface, xwayland_view, request_configure);
CONNECT_SIGNAL(xsurface, xwayland_view, set_class);
CONNECT_SIGNAL(xsurface, xwayland_view, set_decorations);
@ -1094,24 +1035,11 @@ xwayland_view_create(struct server *server,
CONNECT_SIGNAL(xsurface, xwayland_view, focus_in);
CONNECT_SIGNAL(xsurface, xwayland_view, map_request);
/* Events from the view itself */
CONNECT_SIGNAL(view, &xwayland_view->on_view, always_on_top);
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);
/*
* If a surface is already associated, then we've
* missed the various initial set_* events as well.
*
* TODO: update_icon() -> handle_set_icon() after
* https://github.com/labwc/labwc/pull/2760
*/
handle_set_title(&view->set_title, NULL);
handle_set_class(&xwayland_view->set_class, NULL);
update_icon(xwayland_view);
}
if (mapped) {
handle_map(&xwayland_view->base.mappable.map, NULL);