Merge branch 'master' into master

This commit is contained in:
Jack Zeal 2026-04-01 05:27:23 +00:00 committed by GitHub
commit 6ae186edc5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
44 changed files with 512 additions and 3259 deletions

View file

@ -82,7 +82,7 @@ jobs:
run: | run: |
pacman-key --init pacman-key --init
pacman -Syu --noconfirm pacman -Syu --noconfirm
pacman -S --noconfirm git meson clang wlroots0.19 libdrm libinput \ pacman -S --noconfirm git meson clang wlroots0.20 libdrm libinput \
wayland-protocols cairo pango libxml2 xorg-xwayland librsvg \ wayland-protocols cairo pango libxml2 xorg-xwayland librsvg \
libdisplay-info gdb ttf-dejavu foot libsfdo cmocka libdisplay-info gdb ttf-dejavu foot libsfdo cmocka
@ -95,6 +95,7 @@ jobs:
apt-get install -y git gcc clang gdb xwayland apt-get install -y git gcc clang gdb xwayland
apt-get build-dep -y labwc apt-get build-dep -y labwc
apt-get build-dep -y libwlroots-0.19-dev apt-get build-dep -y libwlroots-0.19-dev
apt-get build-dep -y libxkbcommon-dev
- name: Install FreeBSD dependencies - name: Install FreeBSD dependencies
if: matrix.name == 'FreeBSD' if: matrix.name == 'FreeBSD'
@ -106,7 +107,7 @@ jobs:
pkg set -yn pkg:mesa-dri # hack to skip llvm dependency pkg set -yn pkg:mesa-dri # hack to skip llvm dependency
pkg install -y git pcre2 meson gcc pkgconf cairo pango evdev-proto \ pkg install -y git pcre2 meson gcc pkgconf cairo pango evdev-proto \
hwdata wayland-protocols libdisplay-info libepoll-shim \ hwdata wayland-protocols libdisplay-info libepoll-shim \
wlroots019 wlroots020
run: echo "setup done" run: echo "setup done"
- name: Install Void Linux dependencies - name: Install Void Linux dependencies
@ -121,7 +122,7 @@ jobs:
xbps-install -y git meson gcc clang pkg-config scdoc \ xbps-install -y git meson gcc clang pkg-config scdoc \
cairo-devel glib-devel libpng-devel librsvg-devel libxml2-devel \ cairo-devel glib-devel libpng-devel librsvg-devel libxml2-devel \
pango-devel wlroots0.19-devel gdb bash xorg-server-xwayland \ pango-devel wlroots0.19-devel gdb bash xorg-server-xwayland \
dejavu-fonts-ttf libsfdo-devel foot dejavu-fonts-ttf libsfdo-devel foot hwids
# These builds are executed on all runners # These builds are executed on all runners
- name: Build with gcc - name: Build with gcc
@ -209,6 +210,19 @@ jobs:
meson compile -C build-gcc-no-feature meson compile -C build-gcc-no-feature
' | $TARGET ' | $TARGET
# No backend build, run on Arch only
- name: Build with gcc - no-backends test
if: matrix.name == 'Arch'
run: |
echo '
cd "$GITHUB_WORKSPACE"
export CC=gcc
meson setup build-gcc-no-backends \
-Dwlroots:backends= -Dwlroots:session=disabled --werror \
--force-fallback-for=wlroots
meson compile -C build-gcc-no-backends
' | $TARGET
# Unit tests, run on Arch only # Unit tests, run on Arch only
- name: Build with gcc - unit test - name: Build with gcc - unit test
if: matrix.name == 'Arch' if: matrix.name == 'Arch'

32
NEWS.md
View file

@ -9,7 +9,7 @@ The format is based on [Keep a Changelog]
| Date | All Changes | wlroots version | lines-of-code | | Date | All Changes | wlroots version | lines-of-code |
|------------|---------------|-----------------|---------------| |------------|---------------|-----------------|---------------|
| 2026-03-15 | [unreleased] | 0.19.2 | 29244 | | 2026-03-31 | [unreleased] | 0.20.0 | 27402 |
| 2026-03-15 | [0.9.6] | 0.19.2 | 29271 | | 2026-03-15 | [0.9.6] | 0.19.2 | 29271 |
| 2026-03-04 | [0.9.5] | 0.19.2 | 29251 | | 2026-03-04 | [0.9.5] | 0.19.2 | 29251 |
| 2026-02-27 | [0.9.4] | 0.19.2 | 29225 | | 2026-02-27 | [0.9.4] | 0.19.2 | 29225 |
@ -115,8 +115,27 @@ There are some regression warnings worth noting for the switch to wlroots 0.19:
[unreleased-commits] [unreleased-commits]
The codebase has been ported to wlroots 0.20 [#2956] @Consolatis
### Added
- Add configuration option `<tabletTool minPressure="0.0" maxPressure="1.0" />`
to enable tablet tool pressure range libinput settings [#2916] @jp7677
- Add `wl_fixes` interface [#2956] @kode54
### Fixed ### Fixed
- Gracefully handle missing XWayland packages, so that a labwc compositor which
has been built with XWayland support (which is optional) can be run even if
XWayland is not installed. [#3401] @quite
- Rework how XWayland window initial geometry is set to ensure that the natural
geometry does not exceed the usable output area when handling initial
maximize/fullscreen requests. [#3439] @jlindgren90.
- For XWayland windows, sync always-on-top state back to X.Org Server. This
makes `wmctrl -b toggle,above` work. [#3446] @jlindgren90
- Fix missing title and icon with XWayland client override-redirect toggle.
There are no known issues with clients, so this is purely for preventative
purposes. [#3450] @jlindgren90
- Update titlebar title when set to empty and fix an associated issue causing - 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. the title to be misplaced outside of the titlebar when the window is resized.
[#3443] @tokyo4j [#3443] @tokyo4j
@ -127,6 +146,10 @@ There are some regression warnings worth noting for the switch to wlroots 0.19:
- Allow policy-based placement to apply when an initially-maximized/fullscreen - Allow policy-based placement to apply when an initially-maximized/fullscreen
view is restored to floating geometry. [#3387] @jlindgren90 view is restored to floating geometry. [#3387] @jlindgren90
### Changed
- Drop cosmic-workspace protocol [#3031] @tokyo4j
## 0.9.6 - 2026-03-15 ## 0.9.6 - 2026-03-15
[0.9.6-commits] [0.9.6-commits]
@ -3098,6 +3121,7 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16
[#2909]: https://github.com/labwc/labwc/pull/2909 [#2909]: https://github.com/labwc/labwc/pull/2909
[#2910]: https://github.com/labwc/labwc/pull/2910 [#2910]: https://github.com/labwc/labwc/pull/2910
[#2914]: https://github.com/labwc/labwc/pull/2914 [#2914]: https://github.com/labwc/labwc/pull/2914
[#2916]: https://github.com/labwc/labwc/pull/2916
[#2933]: https://github.com/labwc/labwc/pull/2933 [#2933]: https://github.com/labwc/labwc/pull/2933
[#2937]: https://github.com/labwc/labwc/pull/2937 [#2937]: https://github.com/labwc/labwc/pull/2937
[#2939]: https://github.com/labwc/labwc/pull/2939 [#2939]: https://github.com/labwc/labwc/pull/2939
@ -3105,6 +3129,7 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16
[#2943]: https://github.com/labwc/labwc/pull/2943 [#2943]: https://github.com/labwc/labwc/pull/2943
[#2944]: https://github.com/labwc/labwc/pull/2944 [#2944]: https://github.com/labwc/labwc/pull/2944
[#2948]: https://github.com/labwc/labwc/pull/2948 [#2948]: https://github.com/labwc/labwc/pull/2948
[#2956]: https://github.com/labwc/labwc/pull/2956
[#2965]: https://github.com/labwc/labwc/pull/2965 [#2965]: https://github.com/labwc/labwc/pull/2965
[#2967]: https://github.com/labwc/labwc/pull/2967 [#2967]: https://github.com/labwc/labwc/pull/2967
[#2970]: https://github.com/labwc/labwc/pull/2970 [#2970]: https://github.com/labwc/labwc/pull/2970
@ -3122,6 +3147,7 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16
[#3020]: https://github.com/labwc/labwc/pull/3020 [#3020]: https://github.com/labwc/labwc/pull/3020
[#3024]: https://github.com/labwc/labwc/pull/3024 [#3024]: https://github.com/labwc/labwc/pull/3024
[#3028]: https://github.com/labwc/labwc/pull/3028 [#3028]: https://github.com/labwc/labwc/pull/3028
[#3031]: https://github.com/labwc/labwc/pull/3031
[#3033]: https://github.com/labwc/labwc/pull/3033 [#3033]: https://github.com/labwc/labwc/pull/3033
[#3039]: https://github.com/labwc/labwc/pull/3039 [#3039]: https://github.com/labwc/labwc/pull/3039
[#3042]: https://github.com/labwc/labwc/pull/3042 [#3042]: https://github.com/labwc/labwc/pull/3042
@ -3189,6 +3215,7 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16
[#3373]: https://github.com/labwc/labwc/pull/3373 [#3373]: https://github.com/labwc/labwc/pull/3373
[#3387]: https://github.com/labwc/labwc/pull/3387 [#3387]: https://github.com/labwc/labwc/pull/3387
[#3400]: https://github.com/labwc/labwc/pull/3400 [#3400]: https://github.com/labwc/labwc/pull/3400
[#3401]: https://github.com/labwc/labwc/pull/3401
[#3406]: https://github.com/labwc/labwc/pull/3406 [#3406]: https://github.com/labwc/labwc/pull/3406
[#3410]: https://github.com/labwc/labwc/pull/3410 [#3410]: https://github.com/labwc/labwc/pull/3410
[#3411]: https://github.com/labwc/labwc/pull/3411 [#3411]: https://github.com/labwc/labwc/pull/3411
@ -3198,7 +3225,10 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16
[#3428]: https://github.com/labwc/labwc/pull/3428 [#3428]: https://github.com/labwc/labwc/pull/3428
[#3429]: https://github.com/labwc/labwc/pull/3429 [#3429]: https://github.com/labwc/labwc/pull/3429
[#3430]: https://github.com/labwc/labwc/pull/3430 [#3430]: https://github.com/labwc/labwc/pull/3430
[#3439]: https://github.com/labwc/labwc/pull/3439
[#3440]: https://github.com/labwc/labwc/pull/3440 [#3440]: https://github.com/labwc/labwc/pull/3440
[#3441]: https://github.com/labwc/labwc/pull/3441 [#3441]: https://github.com/labwc/labwc/pull/3441
[#3443]: https://github.com/labwc/labwc/pull/3443 [#3443]: https://github.com/labwc/labwc/pull/3443
[#3445]: https://github.com/labwc/labwc/pull/3445 [#3445]: https://github.com/labwc/labwc/pull/3445
[#3446]: https://github.com/labwc/labwc/pull/3446
[#3450]: https://github.com/labwc/labwc/pull/3450

View file

@ -206,6 +206,16 @@ static void
get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height, get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height,
int *baseline, double scale, bool markup, const char *fmt, ...) int *baseline, double scale, bool markup, const char *fmt, ...)
{ {
if (width) {
*width = 0;
}
if (height) {
*height = 0;
}
if (baseline) {
*baseline = 0;
}
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
gchar *buf = g_strdup_vprintf(fmt, args); gchar *buf = g_strdup_vprintf(fmt, args);

View file

@ -1,21 +1,22 @@
wayland_client = dependency('wayland-client') if get_option('labnag').allowed()
wayland_cursor = dependency('wayland-cursor') wayland_client = dependency('wayland-client')
wayland_cursor = dependency('wayland-cursor')
nag_sources = files( nag_sources = files(
'labnag.c', 'labnag.c',
'pool-buffer.c', 'pool-buffer.c',
) )
wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') wl_protocol_dir = wayland_protos.get_variable('pkgdatadir')
protocols = [ protocols = [
wl_protocol_dir / 'stable/tablet/tablet-v2.xml', wl_protocol_dir / 'stable/tablet/tablet-v2.xml',
wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml', wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml',
wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml',
'../protocols/wlr-layer-shell-unstable-v1.xml', '../protocols/wlr-layer-shell-unstable-v1.xml',
] ]
foreach xml : protocols foreach xml : protocols
nag_sources += custom_target( nag_sources += custom_target(
xml.underscorify() + '_c', xml.underscorify() + '_c',
input: xml, input: xml,
@ -28,16 +29,16 @@ foreach xml : protocols
output: '@BASENAME@-client-protocol.h', output: '@BASENAME@-client-protocol.h',
command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'], command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'],
) )
endforeach endforeach
if host_machine.system() in ['freebsd', 'openbsd'] if host_machine.system() in ['freebsd', 'openbsd']
# For signalfd() # For signalfd()
epoll_dep = dependency('epoll-shim') epoll_dep = dependency('epoll-shim')
else else
epoll_dep = [] epoll_dep = []
endif endif
executable( executable(
'labnag', 'labnag',
nag_sources, nag_sources,
dependencies: [ dependencies: [
@ -53,7 +54,8 @@ executable(
], ],
include_directories: [labwc_inc], include_directories: [labwc_inc],
install: true, install: true,
) )
endif
clients = files('lab-sensible-terminal') clients = files('lab-sensible-terminal')
install_data(clients, install_dir: get_option('bindir')) install_data(clients, install_dir: get_option('bindir'))

View file

@ -1090,7 +1090,8 @@ Note: To rotate touch events with output rotation, use the libinput
## TABLET TOOL ## TABLET TOOL
``` ```
<tabletTool motion="absolute" relativeMotionSensitivity="1" /> <tabletTool motion="absolute" relativeMotionSensitivity="1.0"
minPressure="0.0" maxPressure="1.0" />
``` ```
*<tabletTool motion="">* [absolute|relative] *<tabletTool motion="">* [absolute|relative]
@ -1105,6 +1106,16 @@ Note: To rotate touch events with output rotation, use the libinput
speed, using a value greater than 1.0 increases the speed of the speed, using a value greater than 1.0 increases the speed of the
cursor. The default is "1.0". cursor. The default is "1.0".
*<tabletTool minPressure="">*
*<tabletTool maxPressure="">*
The pressure range of a tablet tool can be controlled by adjusting
*minPressure* and *maxPressure*. Setting the minimum pressure to
a value greater than zero requires more pressure for the tip
threshold, setting the maximum pressure to a value less than 1.0
requires less pressure for the user before the maximum is reached.
The default is 0 for the minimum pressure and 1.0 for the maximum
pressure.
## LIBINPUT ## LIBINPUT
``` ```

View file

@ -573,8 +573,11 @@
*relativeMotionSensitivity* controls the speed of the cursor. Using *relativeMotionSensitivity* controls the speed of the cursor. Using
a value lower than 1.0 decreases the speed, using a value greater than a value lower than 1.0 decreases the speed, using a value greater than
1.0 increases the speed of the cursor. 1.0 increases the speed of the cursor.
The pressure range of a tablet tool can be controlled by adjusting
*minPressure* and *maxPressure*.
--> -->
<tabletTool motion="absolute" relativeMotionSensitivity="1.0" /> <tabletTool motion="absolute" relativeMotionSensitivity="1.0"
minPressure="0.0" maxPressure="1.0" />
<!-- <!--
The *category* attribute is optional and can be set to touch, touchpad, The *category* attribute is optional and can be set to touch, touchpad,

View file

@ -144,6 +144,8 @@ struct rcxml {
struct tablet_tool_config { struct tablet_tool_config {
enum lab_motion motion; enum lab_motion motion;
double relative_motion_sensitivity; double relative_motion_sensitivity;
double min_pressure;
double max_pressure;
} tablet_tool; } tablet_tool;
/* libinput */ /* libinput */

View file

@ -243,13 +243,14 @@ struct server {
struct wl_list all; /* struct workspace.link */ struct wl_list all; /* struct workspace.link */
struct workspace *current; struct workspace *current;
struct workspace *last; struct workspace *last;
struct lab_cosmic_workspace_manager *cosmic_manager; struct wlr_ext_workspace_manager_v1 *ext_manager;
struct lab_cosmic_workspace_group *cosmic_group; struct wlr_ext_workspace_group_handle_v1 *ext_group;
struct lab_ext_workspace_manager *ext_manager;
struct lab_ext_workspace_group *ext_group;
struct { struct {
struct wl_listener layout_output_added; struct wl_listener layout_output_added;
} on; } on;
struct {
struct wl_listener commit;
} on_ext_manager;
} workspaces; } workspaces;
struct wl_list outputs; struct wl_list outputs;
@ -273,7 +274,6 @@ struct server {
struct wl_listener renderer_lost; struct wl_listener renderer_lost;
struct wlr_gamma_control_manager_v1 *gamma_control_manager_v1; struct wlr_gamma_control_manager_v1 *gamma_control_manager_v1;
struct wl_listener gamma_control_set_gamma;
struct session_lock_manager *session_lock_manager; struct session_lock_manager *session_lock_manager;

View file

@ -38,8 +38,6 @@ struct output {
* disconnected and connected again. * disconnected and connected again.
*/ */
uint64_t id_bit; uint64_t id_bit;
bool gamma_lut_changed;
}; };
#undef LAB_NR_LAYERS #undef LAB_NR_LAYERS

View file

@ -1,24 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef LABWC_PROTOCOLS_COSMIC_WORKSPACES_INTERNAL_H
#define LABWC_PROTOCOLS_COSMIC_WORKSPACES_INTERNAL_H
struct lab_cosmic_workspace;
struct lab_cosmic_workspace_group;
struct lab_cosmic_workspace_manager;
enum pending_change {
/* group events */
CW_PENDING_WS_CREATE = 1 << 0,
/* ws events*/
CW_PENDING_WS_ACTIVATE = 1 << 1,
CW_PENDING_WS_DEACTIVATE = 1 << 2,
CW_PENDING_WS_REMOVE = 1 << 3,
};
void cosmic_group_output_send_initial_state(struct lab_cosmic_workspace_group *group,
struct wl_resource *group_resource);
void cosmic_manager_schedule_done_event(struct lab_cosmic_workspace_manager *manager);
#endif /* LABWC_PROTOCOLS_COSMIC_WORKSPACES_INTERNAL_H */

View file

@ -1,94 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef LABWC_PROTOCOLS_COSMIC_WORKSPACES_H
#define LABWC_PROTOCOLS_COSMIC_WORKSPACES_H
#include <stdbool.h>
#include <wayland-server-core.h>
struct wlr_output;
struct lab_cosmic_workspace_manager {
struct wl_global *global;
struct wl_list groups;
uint32_t caps;
struct wl_event_source *idle_source;
struct wl_event_loop *event_loop;
struct {
struct wl_listener display_destroy;
} on;
struct wl_list resources;
};
struct lab_cosmic_workspace_group {
struct lab_cosmic_workspace_manager *manager;
struct wl_list workspaces;
struct wl_array capabilities;
struct {
struct wl_signal create_workspace;
struct wl_signal destroy;
} events;
struct wl_list link;
struct wl_list outputs;
struct wl_list resources;
};
struct lab_cosmic_workspace {
struct lab_cosmic_workspace_group *group;
char *name;
struct wl_array coordinates;
struct wl_array capabilities;
uint32_t state; /* enum lab_cosmic_workspace_state */
uint32_t state_pending; /* enum lab_cosmic_workspace_state */
struct {
struct wl_signal activate;
struct wl_signal deactivate;
struct wl_signal remove;
struct wl_signal destroy;
} events;
struct wl_list link;
struct wl_list resources;
};
enum lab_cosmic_workspace_caps {
CW_CAP_NONE = 0,
CW_CAP_GRP_ALL = 0x000000ff,
CW_CAP_WS_ALL = 0x0000ff00,
/* group caps */
CW_CAP_GRP_WS_CREATE = 1 << 0,
/* workspace caps */
CW_CAP_WS_ACTIVATE = 1 << 8,
CW_CAP_WS_DEACTIVATE = 1 << 9,
CW_CAP_WS_REMOVE = 1 << 10,
};
struct lab_cosmic_workspace_manager *lab_cosmic_workspace_manager_create(
struct wl_display *display, uint32_t caps, uint32_t version);
struct lab_cosmic_workspace_group *lab_cosmic_workspace_group_create(
struct lab_cosmic_workspace_manager *manager);
void lab_cosmic_workspace_group_output_enter(
struct lab_cosmic_workspace_group *group, struct wlr_output *output);
void lab_cosmic_workspace_group_output_leave(
struct lab_cosmic_workspace_group *group, struct wlr_output *output);
void lab_cosmic_workspace_group_destroy(struct lab_cosmic_workspace_group *group);
struct lab_cosmic_workspace *lab_cosmic_workspace_create(struct lab_cosmic_workspace_group *group);
void lab_cosmic_workspace_set_name(struct lab_cosmic_workspace *workspace, const char *name);
void lab_cosmic_workspace_set_active(struct lab_cosmic_workspace *workspace, bool enabled);
void lab_cosmic_workspace_set_urgent(struct lab_cosmic_workspace *workspace, bool enabled);
void lab_cosmic_workspace_set_hidden(struct lab_cosmic_workspace *workspace, bool enabled);
void lab_cosmic_workspace_set_coordinates(struct lab_cosmic_workspace *workspace,
struct wl_array *coordinates);
void lab_cosmic_workspace_destroy(struct lab_cosmic_workspace *workspace);
#endif /* LABWC_PROTOCOLS_COSMIC_WORKSPACES_H */

View file

@ -1,25 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef LABWC_PROTOCOLS_EXT_WORKSPACES_INTERNAL_H
#define LABWC_PROTOCOLS_EXT_WORKSPACES_INTERNAL_H
struct wl_resource;
struct lab_ext_workspace_group;
struct lab_ext_workspace_manager;
enum pending_ext_workspaces_change {
/* group events */
WS_PENDING_WS_CREATE = 1 << 0,
/* ws events*/
WS_PENDING_WS_ACTIVATE = 1 << 1,
WS_PENDING_WS_DEACTIVATE = 1 << 2,
WS_PENDING_WS_REMOVE = 1 << 3,
WS_PENDING_WS_ASSIGN = 1 << 4,
};
void ext_group_output_send_initial_state(struct lab_ext_workspace_group *group,
struct wl_resource *group_resource);
void ext_manager_schedule_done_event(struct lab_ext_workspace_manager *manager);
#endif /* LABWC_PROTOCOLS_EXT_WORKSPACES_INTERNAL_H */

View file

@ -1,109 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef LABWC_PROTOCOLS_EXT_WORKSPACES_H
#define LABWC_PROTOCOLS_EXT_WORKSPACES_H
#include <stdbool.h>
#include <wayland-server-core.h>
struct wlr_output;
struct lab_ext_workspace_manager {
struct wl_global *global;
struct wl_list groups;
struct wl_list workspaces;
uint32_t caps;
struct wl_event_source *idle_source;
struct wl_event_loop *event_loop;
struct {
struct wl_listener display_destroy;
} on;
struct wl_list resources;
};
struct lab_ext_workspace_group {
struct lab_ext_workspace_manager *manager;
uint32_t capabilities;
struct {
struct wl_signal create_workspace;
struct wl_signal destroy;
} events;
struct wl_list link;
struct wl_list outputs;
struct wl_list resources;
};
struct lab_ext_workspace {
struct lab_ext_workspace_manager *manager;
struct lab_ext_workspace_group *group;
char *id;
char *name;
struct wl_array coordinates;
uint32_t capabilities;
uint32_t state; /* enum lab_ext_workspace_state */
uint32_t state_pending; /* enum lab_ext_workspace_state */
struct {
struct wl_signal activate;
struct wl_signal deactivate;
struct wl_signal remove;
struct wl_signal assign;
struct wl_signal destroy;
} events;
struct wl_list link;
struct wl_list resources;
};
enum lab_ext_workspace_caps {
WS_CAP_NONE = 0,
WS_CAP_GRP_ALL = 0x0000ffff,
WS_CAP_WS_ALL = 0xffff0000,
/* group caps */
WS_CAP_GRP_WS_CREATE = 1 << 0,
/* workspace caps */
WS_CAP_WS_ACTIVATE = 1 << 16,
WS_CAP_WS_DEACTIVATE = 1 << 17,
WS_CAP_WS_REMOVE = 1 << 18,
WS_CAP_WS_ASSIGN = 1 << 19,
};
struct lab_ext_workspace_manager *lab_ext_workspace_manager_create(
struct wl_display *display, uint32_t caps, uint32_t version);
struct lab_ext_workspace_group *lab_ext_workspace_group_create(
struct lab_ext_workspace_manager *manager);
void lab_ext_workspace_group_output_enter(
struct lab_ext_workspace_group *group, struct wlr_output *output);
void lab_ext_workspace_group_output_leave(
struct lab_ext_workspace_group *group, struct wlr_output *output);
void lab_ext_workspace_group_destroy(struct lab_ext_workspace_group *group);
/* Create a new workspace, id may be NULL */
struct lab_ext_workspace *lab_ext_workspace_create(
struct lab_ext_workspace_manager *manager, const char *id);
void lab_ext_workspace_assign_to_group(struct lab_ext_workspace *workspace,
struct lab_ext_workspace_group *group);
void lab_ext_workspace_set_name(struct lab_ext_workspace *workspace, const char *name);
void lab_ext_workspace_set_active(struct lab_ext_workspace *workspace, bool enabled);
void lab_ext_workspace_set_urgent(struct lab_ext_workspace *workspace, bool enabled);
void lab_ext_workspace_set_hidden(struct lab_ext_workspace *workspace, bool enabled);
void lab_ext_workspace_set_coordinates(struct lab_ext_workspace *workspace,
struct wl_array *coordinates);
void lab_ext_workspace_destroy(struct lab_ext_workspace *workspace);
#endif /* LABWC_PROTOCOLS_EXT_WORKSPACES_H */

View file

@ -1,84 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef LABWC_PROTOCOLS_TRANSACTION_ADDON_H
#define LABWC_PROTOCOLS_TRANSACTION_ADDON_H
#include <wayland-server-core.h>
struct lab_transaction_op {
uint32_t change;
void *src;
void *data;
struct {
struct wl_signal destroy;
} events;
// Private
struct wl_list link;
};
struct lab_transaction_session_context {
int ref_count;
struct wl_list transaction_ops;
};
struct lab_wl_resource_addon {
struct lab_transaction_session_context *ctx;
void *data;
};
/*
* Creates a new addon which can be attached to a wl_resource via
* wl_resource_set_user_data() and retrieved via wl_resource_get_user_data().
*
* Usually the ctx argument should be addon->ctx of the parent wl_resource.
* If it is NULL it will be created automatically which can be used for top
* level wl_resources (when a client binds a wl_global from the registry).
*
* The context refcount is increased by one after this call.
*/
struct lab_wl_resource_addon *lab_resource_addon_create(
struct lab_transaction_session_context *ctx);
/*
* A generic transaction operation attached to
* a session context transaction operation list.
*
* All arguments other than the context are user defined.
* Use of an enum for pending_change is suggested.
*
* The client is responsible for eventually freeing the data
* passed in the void *src and *data arguments by listening
* to the events.destroy signal. The transaction operations can be
* looped through by using lab_transaction_for_each(trans_op, ctx).
*/
struct lab_transaction_op *lab_transaction_op_add(
struct lab_transaction_session_context *ctx,
uint32_t pending_change, void *src, void *data);
/*
* Removes the transaction operation from the ctx list and frees it.
*
* Does *not* free any passed in src or data arguments.
* Use the events.destroy signal for that if necessary.
*/
void lab_transaction_op_destroy(struct lab_transaction_op *transaction_op);
/*
* Destroys the addon.
*
* The context refcount is decreased by one. If it reaches
* zero the context will be free'd alongside the addon itself.
* If the context is destroyed all pending transaction operations
* are destroyed as well.
*/
void lab_resource_addon_destroy(struct lab_wl_resource_addon *addon);
/* Convenience wrappers for looping through the pending transaction ops of a ctx */
#define lab_transaction_for_each(transaction_op, ctx) \
wl_list_for_each(transaction_op, &(ctx)->transaction_ops, link)
#define lab_transaction_for_each_safe(trans_op, trans_op_tmp, ctx) \
wl_list_for_each_safe(trans_op, trans_op_tmp, &(ctx)->transaction_ops, link)
#endif /* LABWC_PROTOCOLS_TRANSACTIONS_ADDON_H */

View file

@ -17,20 +17,7 @@ struct workspace {
struct wlr_scene_tree *tree; struct wlr_scene_tree *tree;
struct wlr_scene_tree *view_trees[3]; struct wlr_scene_tree *view_trees[3];
struct lab_cosmic_workspace *cosmic_workspace; struct wlr_ext_workspace_handle_v1 *ext_workspace;
struct {
struct wl_listener activate;
struct wl_listener deactivate;
struct wl_listener remove;
} on_cosmic;
struct lab_ext_workspace *ext_workspace;
struct {
struct wl_listener activate;
struct wl_listener deactivate;
struct wl_listener assign;
struct wl_listener remove;
} on_ext;
}; };
void workspaces_init(void); void workspaces_init(void);

View file

@ -53,8 +53,8 @@ struct xwayland_view {
struct wl_listener set_override_redirect; struct wl_listener set_override_redirect;
struct wl_listener set_strut_partial; struct wl_listener set_strut_partial;
struct wl_listener set_window_type; struct wl_listener set_window_type;
struct wl_listener set_icon;
struct wl_listener focus_in; struct wl_listener focus_in;
struct wl_listener map_request;
/* Not (yet) implemented */ /* Not (yet) implemented */
/* struct wl_listener set_role; */ /* struct wl_listener set_role; */
@ -80,8 +80,6 @@ void xwayland_adjust_usable_area(struct view *view,
void xwayland_update_workarea(void); void xwayland_update_workarea(void);
void xwayland_reset_cursor(void);
void xwayland_flush(void); void xwayland_flush(void);
#endif /* HAVE_XWAYLAND */ #endif /* HAVE_XWAYLAND */

View file

@ -51,9 +51,9 @@ endif
add_project_arguments('-DLABWC_VERSION=@0@'.format(version), language: 'c') add_project_arguments('-DLABWC_VERSION=@0@'.format(version), language: 'c')
wlroots = dependency( wlroots = dependency(
'wlroots-0.19', 'wlroots-0.20',
default_options: ['default_library=static', 'examples=false'], default_options: ['default_library=static', 'examples=false'],
version: ['>=0.19.0', '<0.20.0'], version: ['>=0.20.0', '<0.21.0'],
) )
wlroots_has_xwayland = wlroots.get_variable('have_xwayland') == 'true' wlroots_has_xwayland = wlroots.get_variable('have_xwayland') == 'true'
@ -65,13 +65,13 @@ xkbcommon = dependency('xkbcommon')
xcb = dependency('xcb', required: get_option('xwayland')) xcb = dependency('xcb', required: get_option('xwayland'))
xcb_ewmh = dependency('xcb-ewmh', required: get_option('xwayland')) xcb_ewmh = dependency('xcb-ewmh', required: get_option('xwayland'))
xcb_icccm = dependency('xcb-icccm', required: get_option('xwayland')) xcb_icccm = dependency('xcb-icccm', required: get_option('xwayland'))
drm_full = dependency('libdrm') drm_full = dependency('libdrm', required: wlroots.get_variable('have_drm_backend') == 'true')
drm = drm_full.partial_dependency(compile_args: true, includes: true) drm = drm_full.partial_dependency(compile_args: true, includes: true)
xml2 = dependency('libxml-2.0') xml2 = dependency('libxml-2.0')
glib = dependency('glib-2.0') glib = dependency('glib-2.0')
cairo = dependency('cairo') cairo = dependency('cairo')
pangocairo = dependency('pangocairo') pangocairo = dependency('pangocairo')
input = dependency('libinput', version: '>=1.14') input = dependency('libinput', version: '>=1.26', required: wlroots.get_variable('have_libinput_backend') == 'true')
pixman = dependency('pixman-1') pixman = dependency('pixman-1')
math = cc.find_library('m') math = cc.find_library('m')
png = dependency('libpng') png = dependency('libpng')

View file

@ -2,6 +2,7 @@ option('man-pages', type: 'feature', value: 'auto', description: 'Generate and i
option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications') option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications')
option('svg', type: 'feature', value: 'enabled', description: 'Enable svg window buttons') option('svg', type: 'feature', value: 'enabled', description: 'Enable svg window buttons')
option('icon', type: 'feature', value: 'enabled', description: 'Enable window icons') option('icon', type: 'feature', value: 'enabled', description: 'Enable window icons')
option('labnag', type: 'feature', value: 'auto', description: 'Build labnag notification daemon')
option('nls', type: 'feature', value: 'auto', description: 'Enable native language support') option('nls', type: 'feature', value: 'auto', description: 'Enable native language support')
option('static_analyzer', type: 'feature', value: 'disabled', description: 'Run gcc static analyzer') option('static_analyzer', type: 'feature', value: 'disabled', description: 'Run gcc static analyzer')
option('test', type: 'feature', value: 'disabled', description: 'Run tests') option('test', type: 'feature', value: 'disabled', description: 'Run tests')

View file

@ -1,364 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="cosmic_workspace_unstable_v1">
<copyright>
Copyright © 2019 Christopher Billington
Copyright © 2020 Ilia Bozhinov
Copyright © 2022 Victoria Brekenfeld
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="zcosmic_workspace_manager_v1" version="1">
<description summary="list and control workspaces">
Workspaces, also called virtual desktops, are groups of surfaces. A
compositor with a concept of workspaces may only show some such groups of
surfaces (those of 'active' workspaces) at a time. 'Activating' a
workspace is a request for the compositor to display that workspace's
surfaces as normal, whereas the compositor may hide or otherwise
de-emphasise surfaces that are associated only with 'inactive' workspaces.
Workspaces are grouped by which sets of outputs they correspond to, and
may contain surfaces only from those outputs. In this way, it is possible
for each output to have its own set of workspaces, or for all outputs (or
any other arbitrary grouping) to share workspaces. Compositors may
optionally conceptually arrange each group of workspaces in an
N-dimensional grid.
The purpose of this protocol is to enable the creation of taskbars and
docks by providing them with a list of workspaces and their properties,
and allowing them to activate and deactivate workspaces.
After a client binds the zcosmic_workspace_manager_v1, each workspace will be
sent via the workspace event.
</description>
<event name="workspace_group">
<description summary="a workspace group has been created">
This event is emitted whenever a new workspace group has been created.
All initial details of the workspace group (workspaces, outputs) will be
sent immediately after this event via the corresponding events in
zcosmic_workspace_group_handle_v1.
</description>
<arg name="workspace_group" type="new_id" interface="zcosmic_workspace_group_handle_v1"/>
</event>
<request name="commit">
<description summary="all requests about the workspaces have been sent">
The client must send this request after it has finished sending other
requests. The compositor must process a series of requests preceding a
commit request atomically.
This allows changes to the workspace properties to be seen as atomic,
even if they happen via multiple events, and even if they involve
multiple zcosmic_workspace_handle_v1 objects, for example, deactivating one
workspace and activating another.
</description>
</request>
<event name="done">
<description summary="all information about the workspace groups has been sent">
This event is sent after all changes in all workspace groups have been
sent.
This allows changes to one or more zcosmic_workspace_group_handle_v1
properties and zcosmic_workspace_handle_v1 properties to be seen as atomic,
even if they happen via multiple events.
In particular, an output moving from one workspace group to
another sends an output_enter event and an output_leave event to the two
zcosmic_workspace_group_handle_v1 objects in question. The compositor sends
the done event only after updating the output information in both
workspace groups.
</description>
</event>
<event name="finished">
<description summary="the compositor has finished with the workspace_manager">
This event indicates that the compositor is done sending events to the
zcosmic_workspace_manager_v1. The server will destroy the object
immediately after sending this request, so it will become invalid and
the client should free any resources associated with it.
</description>
</event>
<request name="stop">
<description summary="stop sending events">
Indicates the client no longer wishes to receive events for new
workspace groups. However the compositor may emit further workspace
events, until the finished event is emitted.
The client must not send any more requests after this one.
</description>
</request>
</interface>
<interface name="zcosmic_workspace_group_handle_v1" version="1">
<description summary="a workspace group assigned to a set of outputs">
A zcosmic_workspace_group_handle_v1 object represents a a workspace group
that is assigned a set of outputs and contains a number of workspaces.
The set of outputs assigned to the workspace group is conveyed to the client via
output_enter and output_leave events, and its workspaces are conveyed with
workspace events.
For example, a compositor which has a set of workspaces for each output may
advertise a workspace group (and its workspaces) per output, whereas a compositor
where a workspace spans all outputs may advertise a single workspace group for all
outputs.
</description>
<enum name="zcosmic_workspace_group_capabilities_v1">
<entry name="create_workspace" value="1" summary="create_workspace request is available"/>
</enum>
<event name="capabilities">
<description summary="compositor capabilities">
This event advertises the capabilities supported by the compositor. If
a capability isn't supported, clients should hide or disable the UI
elements that expose this functionality. For instance, if the
compositor doesn't advertise support for creating workspaces, a button
triggering the create_workspace request should not be displayed.
The compositor will ignore requests it doesn't support. For instance,
a compositor which doesn't advertise support for creating workspaces will ignore
create_workspace requests.
Compositors must send this event once after creation of an
zcosmic_workspace_group_handle_v1 . When the capabilities change, compositors
must send this event again.
The capabilities are sent as an array of 32-bit unsigned integers in
native endianness.
</description>
<arg name="capabilities" type="array" summary="array of 32-bit capabilities"/>
</event>
<event name="output_enter">
<description summary="output assigned to workspace group">
This event is emitted whenever an output is assigned to the workspace
group.
</description>
<arg name="output" type="object" interface="wl_output"/>
</event>
<event name="output_leave">
<description summary="output removed from workspace group">
This event is emitted whenever an output is removed from the workspace
group.
</description>
<arg name="output" type="object" interface="wl_output"/>
</event>
<event name="workspace">
<description summary="workspace added to workspace group">
This event is emitted whenever a new workspace has been created.
A workspace can only be a member of a single workspace group and cannot
be re-assigned.
All initial details of the workspace (name, coordinates, state) will
be sent immediately after this event via the corresponding events in
zcosmic_workspace_handle_v1.
</description>
<arg name="workspace" type="new_id" interface="zcosmic_workspace_handle_v1"/>
</event>
<event name="remove">
<description summary="this workspace group has been destroyed">
This event means the zcosmic_workspace_group_handle_v1 has been destroyed.
It is guaranteed there won't be any more events for this
zcosmic_workspace_group_handle_v1. The zext_workspace_group_handle_v1 becomes
inert so any requests will be ignored except the destroy request.
The compositor must remove all workspaces belonging to a workspace group
before removing the workspace group.
</description>
</event>
<request name="create_workspace">
<description summary="create a new workspace">
Request that the compositor create a new workspace with the given name.
There is no guarantee that the compositor will create a new workspace,
or that the created workspace will have the provided name.
</description>
<arg name="workspace" type="string"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy the zcosmic_workspace_group_handle_v1 object">
Destroys the zcosmic_workspace_group_handle_v1 object.
This request should be called either when the client does not want to
use the workspace object any more or after the remove event to finalize
the destruction of the object.
</description>
</request>
</interface>
<interface name="zcosmic_workspace_handle_v1" version="1">
<description summary="a workspace handing a group of surfaces">
A zcosmic_workspace_handle_v1 object represents a a workspace that handles a
group of surfaces.
Each workspace has a name, conveyed to the client with the name event; a
list of states, conveyed to the client with the state event; and
optionally a set of coordinates, conveyed to the client with the
coordinates event. The client may request that the compositor activate or
deactivate the workspace.
Each workspace can belong to only a single workspace group.
Depepending on the compositor policy, there might be workspaces with
the same name in different workspace groups, but these workspaces are still
separate (e.g. one of them might be active while the other is not).
</description>
<event name="name">
<description summary="workspace name changed">
This event is emitted immediately after the zcosmic_workspace_handle_v1 is
created and whenever the name of the workspace changes.
</description>
<arg name="name" type="string"/>
</event>
<event name="coordinates">
<description summary="workspace coordinates changed">
This event is used to organize workspaces into an N-dimensional grid
within a workspace group, and if supported, is emitted immediately after
the zcosmic_workspace_handle_v1 is created and whenever the coordinates of
the workspace change. Compositors may not send this event if they do not
conceptually arrange workspaces in this way. If compositors simply
number workspaces, without any geometric interpretation, they may send
1D coordinates, which clients should not interpret as implying any
geometry. Sending an empty array means that the compositor no longer
orders the workspace geometrically.
Coordinates have an arbitrary number of dimensions N with an uint32
position along each dimension. By convention if N > 1, the first
dimension is X, the second Y, the third Z, and so on. The compositor may
chose to utilize these events for a more novel workspace layout
convention, however. No guarantee is made about the grid being filled or
bounded; there may be a workspace at coordinate 1 and another at
coordinate 1000 and none in between. Within a workspace group, however,
workspaces must have unique coordinates of equal dimensionality.
</description>
<arg name="coordinates" type="array"/>
</event>
<event name="state">
<description summary="the state of the workspace changed">
This event is emitted immediately after the zcosmic_workspace_handle_v1 is
created and each time the workspace state changes, either because of a
compositor action or because of a request in this protocol.
</description>
<arg name="state" type="array"/>
</event>
<enum name="state">
<description summary="types of states on the workspace">
The different states that a workspace can have.
</description>
<entry name="active" value="0" summary="the workspace is active"/>
<entry name="urgent" value="1" summary="the workspace requests attention"/>
<entry name="hidden" value="2">
<description summary="the workspace is not visible">
The workspace is not visible in its workspace group, and clients
attempting to visualize the compositor workspace state should not
display such workspaces.
</description>
</entry>
</enum>
<enum name="zcosmic_workspace_capabilities_v1">
<entry name="activate" value="1" summary="activate request is available"/>
<entry name="deactivate" value="2" summary="deactivate request is available"/>
<entry name="remove" value="3" summary="remove request is available"/>
</enum>
<event name="capabilities">
<description summary="compositor capabilities">
This event advertises the capabilities supported by the compositor. If
a capability isn't supported, clients should hide or disable the UI
elements that expose this functionality. For instance, if the
compositor doesn't advertise support for removing workspaces, a button
triggering the remove request should not be displayed.
The compositor will ignore requests it doesn't support. For instance,
a compositor which doesn't advertise support for remove will ignore
remove requests.
Compositors must send this event once after creation of an
zcosmic_workspace_handle_v1 . When the capabilities change, compositors
must send this event again.
The capabilities are sent as an array of 32-bit unsigned integers in
native endianness.
</description>
<arg name="capabilities" type="array" summary="array of 32-bit capabilities"/>
</event>
<event name="remove">
<description summary="this workspace has been destroyed">
This event means the zcosmic_workspace_handle_v1 has been destroyed. It is
guaranteed there won't be any more events for this
zcosmic_workspace_handle_v1. The zext_workspace_handle_v1 becomes inert so
any requests will be ignored except the destroy request.
</description>
</event>
<request name="destroy" type="destructor">
<description summary="destroy the zcosmic_workspace_handle_v1 object">
Destroys the zcosmic_workspace_handle_v1 object.
This request should be called either when the client does not want to
use the workspace object any more or after the remove event to finalize
the destruction of the object.
</description>
</request>
<request name="activate">
<description summary="activate the workspace">
Request that this workspace be activated.
There is no guarantee the workspace will be actually activated, and
behaviour may be compositor-dependent. For example, activating a
workspace may or may not deactivate all other workspaces in the same
group.
</description>
</request>
<request name="deactivate">
<description summary="activate the workspace">
Request that this workspace be deactivated.
There is no guarantee the workspace will be actually deactivated.
</description>
</request>
<request name="remove">
<description summary="remove the workspace">
Request that this workspace be removed.
There is no guarantee the workspace will be actually removed.
</description>
</request>
</interface>
</protocol>

View file

@ -14,18 +14,6 @@ wayland_scanner_server = generator(
) )
server_protocols = [ server_protocols = [
wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml',
wl_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml',
wl_protocol_dir / 'stable/tablet/tablet-v2.xml',
wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml',
wl_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml',
wl_protocol_dir / 'staging/xwayland-shell/xwayland-shell-v1.xml',
wl_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml',
wl_protocol_dir / 'staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml',
wl_protocol_dir / 'staging/ext-workspace/ext-workspace-v1.xml',
wl_protocol_dir / 'staging/ext-image-capture-source/ext-image-capture-source-v1.xml',
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-layer-shell-unstable-v1.xml',
'wlr-output-power-management-unstable-v1.xml', 'wlr-output-power-management-unstable-v1.xml',
] ]

View file

@ -68,147 +68,110 @@ struct action_arg_list {
struct wl_list value; struct wl_list value;
}; };
/* Warning: Any change in this enum needs to be reflected in the action_names[] array below */ #define ACTION_TYPE_LIST(X) \
X(NONE, "None") \
X(CLOSE, "Close") \
X(KILL, "Kill") \
X(DEBUG, "Debug") \
X(EXECUTE, "Execute") \
X(EXIT, "Exit") \
X(MOVE_TO_EDGE, "MoveToEdge") \
X(TOGGLE_SNAP_TO_EDGE, "ToggleSnapToEdge") \
X(SNAP_TO_EDGE, "SnapToEdge") \
X(GROW_TO_EDGE, "GrowToEdge") \
X(SHRINK_TO_EDGE, "ShrinkToEdge") \
X(NEXT_WINDOW, "NextWindow") \
X(PREVIOUS_WINDOW, "PreviousWindow") \
X(RECONFIGURE, "Reconfigure") \
X(SHOW_MENU, "ShowMenu") \
X(TOGGLE_MAXIMIZE, "ToggleMaximize") \
X(MAXIMIZE, "Maximize") \
X(UNMAXIMIZE, "UnMaximize") \
X(TOGGLE_FULLSCREEN, "ToggleFullscreen") \
X(SET_DECORATIONS, "SetDecorations") \
X(TOGGLE_DECORATIONS, "ToggleDecorations") \
X(TOGGLE_ALWAYS_ON_TOP, "ToggleAlwaysOnTop") \
X(TOGGLE_ALWAYS_ON_BOTTOM, "ToggleAlwaysOnBottom") \
X(TOGGLE_OMNIPRESENT, "ToggleOmnipresent") \
X(FOCUS, "Focus") \
X(UNFOCUS, "Unfocus") \
X(ICONIFY, "Iconify") \
X(RAISE, "Raise") \
X(LOWER, "Lower") \
X(MOVE, "Move") \
X(RESIZE, "Resize") \
X(RESIZE_RELATIVE, "ResizeRelative") \
X(MOVETO, "MoveTo") \
X(RESIZETO, "ResizeTo") \
X(MOVETO_CURSOR, "MoveToCursor") \
X(MOVE_RELATIVE, "MoveRelative") \
X(SEND_TO_DESKTOP, "SendToDesktop") \
X(GO_TO_DESKTOP, "GoToDesktop") \
X(TOGGLE_SNAP_TO_REGION, "ToggleSnapToRegion") \
X(SNAP_TO_REGION, "SnapToRegion") \
X(UNSNAP, "UnSnap") \
X(TOGGLE_KEYBINDS, "ToggleKeybinds") \
X(FOCUS_OUTPUT, "FocusOutput") \
X(MOVE_TO_OUTPUT, "MoveToOutput") \
X(FIT_TO_OUTPUT, "FitToOutput") \
X(IF, "If") \
X(FOR_EACH, "ForEach") \
X(VIRTUAL_OUTPUT_ADD, "VirtualOutputAdd") \
X(VIRTUAL_OUTPUT_REMOVE, "VirtualOutputRemove") \
X(AUTO_PLACE, "AutoPlace") \
X(TOGGLE_TEARING, "ToggleTearing") \
X(SHADE, "Shade") \
X(UNSHADE, "Unshade") \
X(TOGGLE_SHADE, "ToggleShade") \
X(ENABLE_SCROLL_WHEEL_EMULATION, "EnableScrollWheelEmulation") \
X(DISABLE_SCROLL_WHEEL_EMULATION, "DisableScrollWheelEmulation") \
X(TOGGLE_SCROLL_WHEEL_EMULATION, "ToggleScrollWheelEmulation") \
X(ENABLE_TABLET_MOUSE_EMULATION, "EnableTabletMouseEmulation") \
X(DISABLE_TABLET_MOUSE_EMULATION, "DisableTabletMouseEmulation") \
X(TOGGLE_TABLET_MOUSE_EMULATION, "ToggleTabletMouseEmulation") \
X(TOGGLE_MAGNIFY, "ToggleMagnify") \
X(ZOOM_IN, "ZoomIn") \
X(ZOOM_OUT, "ZoomOut") \
X(WARP_CURSOR, "WarpCursor") \
X(HIDE_CURSOR, "HideCursor")
/*
* Will expand to:
*
* enum action_type {
* ACTION_TYPE_INVALID,
* ACTION_TYPE_NONE,
* ACTION_TYPE_CLOSE,
* ACTION_TYPE_KILL,
* etc...
*/
enum action_type { enum action_type {
ACTION_TYPE_INVALID = 0, ACTION_TYPE_INVALID = 0,
ACTION_TYPE_NONE, #define X(name, str) ACTION_TYPE_##name,
ACTION_TYPE_CLOSE, ACTION_TYPE_LIST(X)
ACTION_TYPE_KILL, #undef X
ACTION_TYPE_DEBUG,
ACTION_TYPE_EXECUTE,
ACTION_TYPE_EXIT,
ACTION_TYPE_MOVE_TO_EDGE,
ACTION_TYPE_TOGGLE_SNAP_TO_EDGE,
ACTION_TYPE_SNAP_TO_EDGE,
ACTION_TYPE_GROW_TO_EDGE,
ACTION_TYPE_SHRINK_TO_EDGE,
ACTION_TYPE_NEXT_WINDOW,
ACTION_TYPE_PREVIOUS_WINDOW,
ACTION_TYPE_RECONFIGURE,
ACTION_TYPE_SHOW_MENU,
ACTION_TYPE_TOGGLE_MAXIMIZE,
ACTION_TYPE_MAXIMIZE,
ACTION_TYPE_UNMAXIMIZE,
ACTION_TYPE_TOGGLE_FULLSCREEN,
ACTION_TYPE_SET_DECORATIONS,
ACTION_TYPE_TOGGLE_DECORATIONS,
ACTION_TYPE_TOGGLE_ALWAYS_ON_TOP,
ACTION_TYPE_TOGGLE_ALWAYS_ON_BOTTOM,
ACTION_TYPE_TOGGLE_OMNIPRESENT,
ACTION_TYPE_FOCUS,
ACTION_TYPE_UNFOCUS,
ACTION_TYPE_ICONIFY,
ACTION_TYPE_RAISE,
ACTION_TYPE_LOWER,
ACTION_TYPE_MOVE,
ACTION_TYPE_RESIZE,
ACTION_TYPE_RESIZE_RELATIVE,
ACTION_TYPE_MOVETO,
ACTION_TYPE_RESIZETO,
ACTION_TYPE_MOVETO_CURSOR,
ACTION_TYPE_MOVE_RELATIVE,
ACTION_TYPE_SEND_TO_DESKTOP,
ACTION_TYPE_GO_TO_DESKTOP,
ACTION_TYPE_TOGGLE_SNAP_TO_REGION,
ACTION_TYPE_SNAP_TO_REGION,
ACTION_TYPE_UNSNAP,
ACTION_TYPE_TOGGLE_KEYBINDS,
ACTION_TYPE_FOCUS_OUTPUT,
ACTION_TYPE_MOVE_TO_OUTPUT,
ACTION_TYPE_FIT_TO_OUTPUT,
ACTION_TYPE_IF,
ACTION_TYPE_FOR_EACH,
ACTION_TYPE_VIRTUAL_OUTPUT_ADD,
ACTION_TYPE_VIRTUAL_OUTPUT_REMOVE,
ACTION_TYPE_AUTO_PLACE,
ACTION_TYPE_TOGGLE_TEARING,
ACTION_TYPE_SHADE,
ACTION_TYPE_UNSHADE,
ACTION_TYPE_TOGGLE_SHADE,
ACTION_TYPE_ENABLE_SCROLL_WHEEL_EMULATION,
ACTION_TYPE_DISABLE_SCROLL_WHEEL_EMULATION,
ACTION_TYPE_TOGGLE_SCROLL_WHEEL_EMULATION,
ACTION_TYPE_ENABLE_TABLET_MOUSE_EMULATION,
ACTION_TYPE_DISABLE_TABLET_MOUSE_EMULATION,
ACTION_TYPE_TOGGLE_TABLET_MOUSE_EMULATION,
ACTION_TYPE_TOGGLE_MAGNIFY,
ACTION_TYPE_ZOOM_IN,
ACTION_TYPE_ZOOM_OUT,
ACTION_TYPE_WARP_CURSOR,
ACTION_TYPE_HIDE_CURSOR,
}; };
/* Warning: Any change in this array needs to be reflected in the action_type enum above */ /*
* Will expand to:
*
* const char *action_names[] = {
* [ACTION_TYPE_INVALID] = "INVALID",
* [ACTION_TYPE_NONE] = "None",
* [ACTION_TYPE_CLOSE] = "Close",
* [ACTION_TYPE_KILL] = "Kill",
* etc...
*/
const char *action_names[] = { const char *action_names[] = {
"INVALID", [ACTION_TYPE_INVALID] = "INVALID",
"None", #define X(name, str)[ACTION_TYPE_##name] = str,
"Close", ACTION_TYPE_LIST(X)
"Kill", #undef X
"Debug",
"Execute",
"Exit",
"MoveToEdge",
"ToggleSnapToEdge",
"SnapToEdge",
"GrowToEdge",
"ShrinkToEdge",
"NextWindow",
"PreviousWindow",
"Reconfigure",
"ShowMenu",
"ToggleMaximize",
"Maximize",
"UnMaximize",
"ToggleFullscreen",
"SetDecorations",
"ToggleDecorations",
"ToggleAlwaysOnTop",
"ToggleAlwaysOnBottom",
"ToggleOmnipresent",
"Focus",
"Unfocus",
"Iconify",
"Raise",
"Lower",
"Move",
"Resize",
"ResizeRelative",
"MoveTo",
"ResizeTo",
"MoveToCursor",
"MoveRelative",
"SendToDesktop",
"GoToDesktop",
"ToggleSnapToRegion",
"SnapToRegion",
"UnSnap",
"ToggleKeybinds",
"FocusOutput",
"MoveToOutput",
"FitToOutput",
"If",
"ForEach",
"VirtualOutputAdd",
"VirtualOutputRemove",
"AutoPlace",
"ToggleTearing",
"Shade",
"Unshade",
"ToggleShade",
"EnableScrollWheelEmulation",
"DisableScrollWheelEmulation",
"ToggleScrollWheelEmulation",
"EnableTabletMouseEmulation",
"DisableTabletMouseEmulation",
"ToggleTabletMouseEmulation",
"ToggleMagnify",
"ZoomIn",
"ZoomOut",
"WarpCursor",
"HideCursor",
NULL NULL
}; };
#undef ACTION_TYPE_LIST
void void
action_arg_add_str(struct action *action, const char *key, const char *value) action_arg_add_str(struct action *action, const char *key, const char *value)
{ {

View file

@ -1355,6 +1355,12 @@ entry(xmlNode *node, char *nodename, char *content)
} else if (!strcasecmp(nodename, "relativeMotionSensitivity.tabletTool")) { } else if (!strcasecmp(nodename, "relativeMotionSensitivity.tabletTool")) {
rc.tablet_tool.relative_motion_sensitivity = rc.tablet_tool.relative_motion_sensitivity =
tablet_get_dbl_if_positive(content, "relativeMotionSensitivity"); tablet_get_dbl_if_positive(content, "relativeMotionSensitivity");
} else if (!strcasecmp(nodename, "minPressure.tabletTool")) {
rc.tablet_tool.min_pressure =
tablet_get_dbl_if_positive(content, "minPressure");
} else if (!strcasecmp(nodename, "maxPressure.tabletTool")) {
rc.tablet_tool.max_pressure =
tablet_get_dbl_if_positive(content, "maxPressure");
} else if (!strcasecmp(nodename, "ignoreButtonReleasePeriod.menu")) { } else if (!strcasecmp(nodename, "ignoreButtonReleasePeriod.menu")) {
rc.menu_ignore_button_release_period = atoi(content); rc.menu_ignore_button_release_period = atoi(content);
} else if (!strcasecmp(nodename, "showIcons.menu")) { } else if (!strcasecmp(nodename, "showIcons.menu")) {
@ -1475,6 +1481,8 @@ rcxml_init(void)
tablet_load_default_button_mappings(); tablet_load_default_button_mappings();
rc.tablet_tool.motion = LAB_MOTION_ABSOLUTE; rc.tablet_tool.motion = LAB_MOTION_ABSOLUTE;
rc.tablet_tool.relative_motion_sensitivity = 1.0; rc.tablet_tool.relative_motion_sensitivity = 1.0;
rc.tablet_tool.min_pressure = 0.0;
rc.tablet_tool.max_pressure = 1.0;
rc.repeat_rate = 25; rc.repeat_rate = 25;
rc.repeat_delay = 600; rc.repeat_delay = 600;

View file

@ -8,8 +8,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <wlr/backend/drm.h>
#include <wlr/backend/multi.h> #include <wlr/backend/multi.h>
#include <wlr/config.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include "common/buf.h" #include "common/buf.h"
#include "common/dir.h" #include "common/dir.h"
@ -20,8 +20,13 @@
#include "config/rcxml.h" #include "config/rcxml.h"
#include "labwc.h" #include "labwc.h"
#if WLR_HAS_DRM_BACKEND
#include <wlr/backend/drm.h>
#else
#define wlr_backend_is_drm(backend) (false)
#endif
static const char *const env_vars[] = { static const char *const env_vars[] = {
"DISPLAY",
"WAYLAND_DISPLAY", "WAYLAND_DISPLAY",
"XDG_CURRENT_DESKTOP", "XDG_CURRENT_DESKTOP",
"XCURSOR_SIZE", "XCURSOR_SIZE",
@ -208,6 +213,21 @@ should_update_activation(void)
return have_drm; return have_drm;
} }
static void
execute_update(const char *env_keys, const char *env_unset_keys, bool initialize)
{
char *cmd =
strdup_printf("dbus-update-activation-environment %s",
initialize ? env_keys : env_unset_keys);
spawn_async_no_shell(cmd);
free(cmd);
cmd = strdup_printf("systemctl --user %s %s",
initialize ? "import-environment" : "unset-environment", env_keys);
spawn_async_no_shell(cmd);
free(cmd);
}
static void static void
update_activation_env(bool initialize) update_activation_env(bool initialize)
{ {
@ -227,19 +247,18 @@ update_activation_env(bool initialize)
char *env_keys = str_join(env_vars, "%s", " "); char *env_keys = str_join(env_vars, "%s", " ");
char *env_unset_keys = initialize ? NULL : str_join(env_vars, "%s=", " "); char *env_unset_keys = initialize ? NULL : str_join(env_vars, "%s=", " ");
char *cmd = execute_update(env_keys, env_unset_keys, initialize);
strdup_printf("dbus-update-activation-environment %s",
initialize ? env_keys : env_unset_keys);
spawn_async_no_shell(cmd);
free(cmd);
cmd = strdup_printf("systemctl --user %s %s",
initialize ? "import-environment" : "unset-environment", env_keys);
spawn_async_no_shell(cmd);
free(cmd);
free(env_keys); free(env_keys);
free(env_unset_keys); free(env_unset_keys);
#if HAVE_XWAYLAND
if (server.xwayland) {
// DISPLAY is only set if xwayland was initialized successfully,
// so we only update the env in that case
execute_update("DISPLAY", "DISPLAY=", initialize);
}
#endif
} }
void void

View file

@ -3,7 +3,7 @@
#include "input/cursor.h" #include "input/cursor.h"
#include <assert.h> #include <assert.h>
#include <time.h> #include <time.h>
#include <wlr/backend/libinput.h> #include <wlr/config.h>
#include <wlr/types/wlr_cursor.h> #include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_cursor_shape_v1.h> #include <wlr/types/wlr_cursor_shape_v1.h>
#include <wlr/types/wlr_data_device.h> #include <wlr/types/wlr_data_device.h>
@ -38,6 +38,10 @@
#include "view.h" #include "view.h"
#include "xwayland.h" #include "xwayland.h"
#if WLR_HAS_LIBINPUT_BACKEND
#include <wlr/backend/libinput.h>
#endif
#define LAB_CURSOR_SHAPE_V1_VERSION 1 #define LAB_CURSOR_SHAPE_V1_VERSION 1
struct constraint { struct constraint {
@ -898,6 +902,7 @@ preprocess_cursor_motion(struct seat *seat, struct wlr_pointer *pointer,
static double get_natural_scroll_factor(struct wlr_input_device *wlr_input_device) static double get_natural_scroll_factor(struct wlr_input_device *wlr_input_device)
{ {
#if WLR_HAS_LIBINPUT_BACKEND
if (wlr_input_device_is_libinput(wlr_input_device)) { if (wlr_input_device_is_libinput(wlr_input_device)) {
struct libinput_device *libinput_device = struct libinput_device *libinput_device =
wlr_libinput_get_device_handle(wlr_input_device); wlr_libinput_get_device_handle(wlr_input_device);
@ -905,7 +910,7 @@ static double get_natural_scroll_factor(struct wlr_input_device *wlr_input_devic
return -1.0; return -1.0;
} }
} }
#endif
return 1.0; return 1.0;
} }
@ -1166,7 +1171,7 @@ cursor_process_button_press(struct seat *seat, uint32_t button, uint32_t time_ms
if (layer && layer->current.keyboard_interactive) { if (layer && layer->current.keyboard_interactive) {
layer_try_set_focus(seat, layer); layer_try_set_focus(seat, layer);
} }
#ifdef HAVE_XWAYLAND #if HAVE_XWAYLAND
} else if (ctx.type == LAB_NODE_UNMANAGED) { } else if (ctx.type == LAB_NODE_UNMANAGED) {
desktop_focus_view_or_surface(seat, NULL, ctx.surface, desktop_focus_view_or_surface(seat, NULL, ctx.surface,
/*raise*/ false); /*raise*/ false);
@ -1604,9 +1609,6 @@ void
cursor_reload(struct seat *seat) cursor_reload(struct seat *seat)
{ {
cursor_load(seat); cursor_load(seat);
#if HAVE_XWAYLAND
xwayland_reset_cursor();
#endif
cursor_update_image(seat); cursor_update_image(seat);
} }

View file

@ -309,7 +309,8 @@ handle_keyboard_grab_destroy(struct wl_listener *listener, void *data)
{ {
struct input_method_relay *relay = struct input_method_relay *relay =
wl_container_of(listener, relay, keyboard_grab_destroy); wl_container_of(listener, relay, keyboard_grab_destroy);
struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data; struct wlr_input_method_keyboard_grab_v2 *keyboard_grab =
relay->input_method->keyboard_grab;
assert(keyboard_grab); assert(keyboard_grab);
wl_list_remove(&relay->keyboard_grab_destroy.link); wl_list_remove(&relay->keyboard_grab_destroy.link);
@ -585,11 +586,11 @@ input_method_relay_create(struct seat *seat)
relay->popup_tree = lab_wlr_scene_tree_create(&server.scene->tree); relay->popup_tree = lab_wlr_scene_tree_create(&server.scene->tree);
relay->new_text_input.notify = handle_new_text_input; relay->new_text_input.notify = handle_new_text_input;
wl_signal_add(&server.text_input_manager->events.text_input, wl_signal_add(&server.text_input_manager->events.new_text_input,
&relay->new_text_input); &relay->new_text_input);
relay->new_input_method.notify = handle_new_input_method; relay->new_input_method.notify = handle_new_input_method;
wl_signal_add(&server.input_method_manager->events.input_method, wl_signal_add(&server.input_method_manager->events.new_input_method,
&relay->new_input_method); &relay->new_input_method);
relay->focused_surface_destroy.notify = handle_focused_surface_destroy; relay->focused_surface_destroy.notify = handle_focused_surface_destroy;

View file

@ -3,7 +3,7 @@
#include "input/keyboard.h" #include "input/keyboard.h"
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <wlr/backend/session.h> #include <wlr/config.h>
#include <wlr/interfaces/wlr_keyboard.h> #include <wlr/interfaces/wlr_keyboard.h>
#include <wlr/types/wlr_keyboard_group.h> #include <wlr/types/wlr_keyboard_group.h>
#include <wlr/types/wlr_seat.h> #include <wlr/types/wlr_seat.h>
@ -21,6 +21,10 @@
#include "view.h" #include "view.h"
#include "workspaces.h" #include "workspaces.h"
#if WLR_HAS_SESSION
#include <wlr/backend/session.h>
#endif
enum lab_key_handled { enum lab_key_handled {
LAB_KEY_HANDLED_FALSE = 0, LAB_KEY_HANDLED_FALSE = 0,
LAB_KEY_HANDLED_TRUE = 1, LAB_KEY_HANDLED_TRUE = 1,
@ -54,7 +58,9 @@ keyboard_reset_current_keybind(void)
static void static void
change_vt(unsigned int vt) change_vt(unsigned int vt)
{ {
#if WLR_HAS_SESSION
wlr_session_change_vt(server.session, vt); wlr_session_change_vt(server.session, vt);
#endif
} }
uint32_t uint32_t
@ -745,7 +751,18 @@ set_layout(struct wlr_keyboard *kb)
static bool fallback_mode; static bool fallback_mode;
struct xkb_rule_names rules = { 0 }; struct xkb_rule_names rules = { 0 };
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); enum xkb_context_flags ctx_flags = XKB_CONTEXT_NO_FLAGS;
#ifdef __ANDROID__
/*
* Android's bionic libc implements secure_getenv() as a function
* that always returns NULL (the app process has no AT_SECURE).
* This prevents xkbcommon from reading XKB_DEFAULT_LAYOUT and
* friends via secure_getenv(). Use the flag to fall back to
* regular getenv() which works fine on Android.
*/
ctx_flags |= XKB_CONTEXT_NO_SECURE_GETENV;
#endif
struct xkb_context *context = xkb_context_new(ctx_flags);
struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules, struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
XKB_KEYMAP_COMPILE_NO_FLAGS); XKB_KEYMAP_COMPILE_NO_FLAGS);

View file

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
#include "input/tablet-pad.h" #include "input/tablet-pad.h"
#include <stdlib.h> #include <stdlib.h>
#include <wlr/backend/libinput.h> #include <wlr/config.h>
#include <wlr/types/wlr_compositor.h> #include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_tablet_pad.h> #include <wlr/types/wlr_tablet_pad.h>
#include <wlr/types/wlr_tablet_v2.h> #include <wlr/types/wlr_tablet_v2.h>
@ -14,6 +14,12 @@
#include "input/tablet.h" #include "input/tablet.h"
#include "labwc.h" #include "labwc.h"
#if WLR_HAS_LIBINPUT_BACKEND
#include <wlr/backend/libinput.h>
#else
#define wlr_input_device_is_libinput(device) (false)
#endif
void void
tablet_pad_attach_tablet(struct seat *seat) tablet_pad_attach_tablet(struct seat *seat)
{ {
@ -34,7 +40,7 @@ tablet_pad_attach_tablet(struct seat *seat)
*/ */
continue; continue;
} }
#if WLR_HAS_LIBINPUT_BACKEND
struct libinput_device *tablet_device = struct libinput_device *tablet_device =
wlr_libinput_get_device_handle(tablet->wlr_input_device); wlr_libinput_get_device_handle(tablet->wlr_input_device);
struct libinput_device_group *tablet_group = struct libinput_device_group *tablet_group =
@ -55,6 +61,7 @@ tablet_pad_attach_tablet(struct seat *seat)
pad->tablet = tablet; pad->tablet = tablet;
} }
} }
#endif
} }
} }

View file

@ -2,11 +2,13 @@
#include "input/tablet.h" #include "input/tablet.h"
#include <stdlib.h> #include <stdlib.h>
#include <linux/input-event-codes.h> #include <linux/input-event-codes.h>
#include <wlr/config.h>
#include <wlr/types/wlr_cursor.h> #include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_tablet_tool.h> #include <wlr/types/wlr_tablet_tool.h>
#include <wlr/types/wlr_tablet_v2.h> #include <wlr/types/wlr_tablet_v2.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include <wlr/types/wlr_scene.h> #include <wlr/types/wlr_scene.h>
#include <wlr/util/log.h>
#include "common/macros.h" #include "common/macros.h"
#include "common/mem.h" #include "common/mem.h"
#include "common/scene-helpers.h" #include "common/scene-helpers.h"
@ -20,6 +22,10 @@
#include "action.h" #include "action.h"
#include "view.h" #include "view.h"
#if WLR_HAS_LIBINPUT_BACKEND
#include <wlr/backend/libinput.h>
#endif
bool bool
tablet_tool_has_focused_surface(struct seat *seat) tablet_tool_has_focused_surface(struct seat *seat)
{ {
@ -336,6 +342,29 @@ handle_tablet_tool_proximity(struct wl_listener *listener, void *data)
tool = tablet_tool_create(tablet->seat, ev->tool); tool = tablet_tool_create(tablet->seat, ev->tool);
} }
#if WLR_HAS_LIBINPUT_BACKEND
struct libinput_tablet_tool *libinput_tool =
wlr_libinput_get_tablet_tool_handle(tool->tool_v2->wlr_tool);
/*
* Configure tool pressure range using libinput. Note that a runtime change
* needs two proximity-in events. First one is for applying the pressure range
* here and second one until it is effectively applied, probably because of
* how lininput applies pressure range changes internally.
*/
if (libinput_tablet_tool_config_pressure_range_is_available(libinput_tool) > 0
&& rc.tablet_tool.min_pressure >= 0.0
&& rc.tablet_tool.max_pressure <= 1.0) {
double min = libinput_tablet_tool_config_pressure_range_get_minimum(libinput_tool);
double max = libinput_tablet_tool_config_pressure_range_get_maximum(libinput_tool);
if (min != rc.tablet_tool.min_pressure || max != rc.tablet_tool.max_pressure) {
wlr_log(WLR_INFO, "tablet tool pressure range configured");
libinput_tablet_tool_config_pressure_range_set(libinput_tool,
rc.tablet_tool.min_pressure, rc.tablet_tool.max_pressure);
}
}
#endif
/* /*
* Enforce mouse emulation when the current tool is a tablet mouse. * Enforce mouse emulation when the current tool is a tablet mouse.
* Client support for tablet mouses in tablet mode is often incomplete * Client support for tablet mouses in tablet mode is often incomplete

View file

@ -55,6 +55,5 @@ subdir('foreign-toplevel')
subdir('img') subdir('img')
subdir('input') subdir('input')
subdir('menu') subdir('menu')
subdir('protocols')
subdir('scaled-buffer') subdir('scaled-buffer')
subdir('ssd') subdir('ssd')

View file

@ -10,11 +10,10 @@
#include "output.h" #include "output.h"
#include <assert.h> #include <assert.h>
#include <strings.h> #include <strings.h>
#include <wlr/backend/drm.h>
#include <wlr/backend/wayland.h> #include <wlr/backend/wayland.h>
#include <wlr/config.h> #include <wlr/config.h>
#include <wlr/types/wlr_cursor.h> #include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_drm_lease_v1.h> #include <wlr/types/wlr_ext_workspace_v1.h>
#include <wlr/types/wlr_gamma_control_v1.h> #include <wlr/types/wlr_gamma_control_v1.h>
#include <wlr/types/wlr_output.h> #include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_output_management_v1.h> #include <wlr/types/wlr_output_management_v1.h>
@ -32,15 +31,24 @@
#include "node.h" #include "node.h"
#include "output-state.h" #include "output-state.h"
#include "output-virtual.h" #include "output-virtual.h"
#include "protocols/cosmic-workspaces.h"
#include "protocols/ext-workspace.h"
#include "regions.h" #include "regions.h"
#include "session-lock.h" #include "session-lock.h"
#include "view.h" #include "view.h"
#include "xwayland.h" #include "xwayland.h"
#if WLR_HAS_X11_BACKEND #if WLR_HAS_X11_BACKEND
#include <wlr/backend/x11.h> #include <wlr/backend/x11.h>
#endif
#if WLR_HAS_DRM_BACKEND
#include <wlr/backend/drm.h>
#include <wlr/types/wlr_drm_lease_v1.h>
#else
#define wlr_output_is_drm(output) (false)
#endif
#if WLR_HAS_SESSION
#include <wlr/backend/session.h>
#endif #endif
bool bool
@ -82,35 +90,6 @@ output_get_tearing_allowance(struct output *output)
return view->force_tearing == LAB_STATE_ENABLED; return view->force_tearing == LAB_STATE_ENABLED;
} }
static void
output_apply_gamma(struct output *output)
{
assert(output);
assert(output->gamma_lut_changed);
struct wlr_scene_output *scene_output = output->scene_output;
struct wlr_output_state pending;
wlr_output_state_init(&pending);
output->gamma_lut_changed = false;
struct wlr_gamma_control_v1 *gamma_control =
wlr_gamma_control_manager_v1_get_control(
server.gamma_control_manager_v1,
output->wlr_output);
if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) {
wlr_output_state_finish(&pending);
return;
}
if (!lab_wlr_scene_output_commit(scene_output, &pending)) {
wlr_gamma_control_v1_send_failed_and_destroy(gamma_control);
}
wlr_output_state_finish(&pending);
}
static void static void
handle_output_frame(struct wl_listener *listener, void *data) handle_output_frame(struct wl_listener *listener, void *data)
{ {
@ -123,30 +102,21 @@ handle_output_frame(struct wl_listener *listener, void *data)
return; return;
} }
#if WLR_HAS_SESSION
/* /*
* skip painting the session when it exists but is not active. * skip painting the session when it exists but is not active.
*/ */
if (server.session && !server.session->active) { if (server.session && !server.session->active) {
return; return;
} }
#endif
if (output->gamma_lut_changed) {
/*
* We are not mixing the gamma state with
* other pending output changes to make it
* easier to handle a failed output commit
* due to gamma without impacting other
* unrelated output changes.
*/
output_apply_gamma(output);
} else {
struct wlr_scene_output *scene_output = output->scene_output; struct wlr_scene_output *scene_output = output->scene_output;
struct wlr_output_state *pending = &output->pending; struct wlr_output_state *pending = &output->pending;
pending->tearing_page_flip = output_get_tearing_allowance(output); pending->tearing_page_flip = output_get_tearing_allowance(output);
lab_wlr_scene_output_commit(scene_output, pending); lab_wlr_scene_output_commit(scene_output, pending);
}
struct timespec now = { 0 }; struct timespec now = { 0 };
clock_gettime(CLOCK_MONOTONIC, &now); clock_gettime(CLOCK_MONOTONIC, &now);
@ -285,9 +255,7 @@ add_output_to_layout(struct output *output)
layout_output, output->scene_output); layout_output, output->scene_output);
} }
lab_cosmic_workspace_group_output_enter( wlr_ext_workspace_group_handle_v1_output_enter(
server.workspaces.cosmic_group, output->wlr_output);
lab_ext_workspace_group_output_enter(
server.workspaces.ext_group, output->wlr_output); server.workspaces.ext_group, output->wlr_output);
/* (Re-)create regions from config */ /* (Re-)create regions from config */
@ -495,14 +463,8 @@ handle_new_output(struct wl_listener *listener, void *data)
* This is also useful for debugging the DRM parts of * This is also useful for debugging the DRM parts of
* another compositor. * another compositor.
* *
* All drm leasing is disabled due to a UAF bug in wlroots.
* We assume that the fix will be backported to 0.19.1 and thus
* check for a version >= 0.19.1. See following link for the fix status:
* https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5104
*
* TODO: remove once labwc starts tracking 0.20.x and the fix has been merged.
*/ */
#if LAB_WLR_VERSION_AT_LEAST(0, 19, 1) #if WLR_HAS_DRM_BACKEND
if (server.drm_lease_manager && wlr_output_is_drm(wlr_output)) { if (server.drm_lease_manager && wlr_output_is_drm(wlr_output)) {
wlr_drm_lease_v1_manager_offer_output( wlr_drm_lease_v1_manager_offer_output(
server.drm_lease_manager, wlr_output); server.drm_lease_manager, wlr_output);
@ -601,6 +563,8 @@ output_init(void)
{ {
server.gamma_control_manager_v1 = server.gamma_control_manager_v1 =
wlr_gamma_control_manager_v1_create(server.wl_display); wlr_gamma_control_manager_v1_create(server.wl_display);
wlr_scene_set_gamma_control_manager_v1(server.scene,
server.gamma_control_manager_v1);
server.new_output.notify = handle_new_output; server.new_output.notify = handle_new_output;
wl_signal_add(&server.backend->events.new_output, &server.new_output); wl_signal_add(&server.backend->events.new_output, &server.new_output);
@ -729,9 +693,7 @@ output_config_apply(struct wlr_output_configuration_v1 *config)
} else if (was_in_layout) { } else if (was_in_layout) {
regions_evacuate_output(output); regions_evacuate_output(output);
lab_cosmic_workspace_group_output_leave( wlr_ext_workspace_group_handle_v1_output_leave(
server.workspaces.cosmic_group, output->wlr_output);
lab_ext_workspace_group_output_leave(
server.workspaces.ext_group, output->wlr_output); server.workspaces.ext_group, output->wlr_output);
/* /*
@ -912,19 +874,6 @@ handle_output_layout_change(struct wl_listener *listener, void *data)
do_output_layout_change(); do_output_layout_change();
} }
static void
handle_gamma_control_set_gamma(struct wl_listener *listener, void *data)
{
const struct wlr_gamma_control_manager_v1_set_gamma_event *event = data;
struct output *output = event->output->data;
if (!output_is_usable(output)) {
return;
}
output->gamma_lut_changed = true;
wlr_output_schedule_frame(output->wlr_output);
}
static void static void
output_manager_init(void) output_manager_init(void)
{ {
@ -941,10 +890,6 @@ output_manager_init(void)
server.output_manager_test.notify = handle_output_manager_test; server.output_manager_test.notify = handle_output_manager_test;
wl_signal_add(&server.output_manager->events.test, wl_signal_add(&server.output_manager->events.test,
&server.output_manager_test); &server.output_manager_test);
server.gamma_control_set_gamma.notify = handle_gamma_control_set_gamma;
wl_signal_add(&server.gamma_control_manager_v1->events.set_gamma,
&server.gamma_control_set_gamma);
} }
static void static void
@ -953,7 +898,6 @@ output_manager_finish(void)
wl_list_remove(&server.output_layout_change.link); wl_list_remove(&server.output_layout_change.link);
wl_list_remove(&server.output_manager_apply.link); wl_list_remove(&server.output_manager_apply.link);
wl_list_remove(&server.output_manager_test.link); wl_list_remove(&server.output_manager_test.link);
wl_list_remove(&server.gamma_control_set_gamma.link);
} }
struct output * struct output *

View file

@ -1,717 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <assert.h>
#include <wlr/util/log.h>
#include "common/array.h"
#include "common/mem.h"
#include "common/list.h"
#include "cosmic-workspace-unstable-v1-protocol.h"
#include "protocols/cosmic-workspaces.h"
#include "protocols/cosmic-workspaces-internal.h"
#include "protocols/transaction-addon.h"
/*
* .--------------------.
* | TODO |
* |--------------------|
* | - prevent empty |
* | done events |
* | - go through xml |
* | and verify impl |
* | - assert pub API |
* `--------------------´
*
*/
/* Only used within an assert() */
#ifndef NDEBUG
#define COSMIC_WORKSPACE_V1_VERSION 1
#endif
/* These are just *waaay* too long */
#define ZCOSMIC_CAP_WS_CREATE \
ZCOSMIC_WORKSPACE_GROUP_HANDLE_V1_ZCOSMIC_WORKSPACE_GROUP_CAPABILITIES_V1_CREATE_WORKSPACE
#define ZCOSMIC_CAP_WS_ACTIVATE \
ZCOSMIC_WORKSPACE_HANDLE_V1_ZCOSMIC_WORKSPACE_CAPABILITIES_V1_ACTIVATE
#define ZCOSMIC_CAP_WS_DEACTIVATE \
ZCOSMIC_WORKSPACE_HANDLE_V1_ZCOSMIC_WORKSPACE_CAPABILITIES_V1_DEACTIVATE
#define ZCOSMIC_CAP_WS_REMOVE \
ZCOSMIC_WORKSPACE_HANDLE_V1_ZCOSMIC_WORKSPACE_CAPABILITIES_V1_REMOVE
enum workspace_state {
CW_WS_STATE_ACTIVE = 1 << 0,
CW_WS_STATE_URGENT = 1 << 1,
CW_WS_STATE_HIDDEN = 1 << 2,
/*
* Set when creating a new workspace so we
* don't end up having to send the state twice.
*/
CW_WS_STATE_INVALID = 1 << 31,
};
struct ws_create_workspace_event {
char *name;
struct {
struct wl_listener transaction_op_destroy;
} on;
};
static void
add_caps(struct wl_array *caps_arr, uint32_t caps)
{
if (caps == CW_CAP_NONE) {
return;
}
if (caps & CW_CAP_GRP_WS_CREATE) {
array_add(caps_arr, ZCOSMIC_CAP_WS_CREATE);
}
if (caps & CW_CAP_WS_ACTIVATE) {
array_add(caps_arr, ZCOSMIC_CAP_WS_ACTIVATE);
}
if (caps & CW_CAP_WS_DEACTIVATE) {
array_add(caps_arr, ZCOSMIC_CAP_WS_DEACTIVATE);
}
if (caps & CW_CAP_WS_REMOVE) {
array_add(caps_arr, ZCOSMIC_CAP_WS_REMOVE);
}
}
/* Workspace */
static void
workspace_handle_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static void
workspace_handle_activate(struct wl_client *client, struct wl_resource *resource)
{
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
if (!addon) {
/* workspace was destroyed from the compositor side */
return;
}
struct lab_cosmic_workspace *workspace = addon->data;
lab_transaction_op_add(addon->ctx, CW_PENDING_WS_ACTIVATE,
workspace, /*data*/ NULL);
}
static void
workspace_handle_deactivate(struct wl_client *client, struct wl_resource *resource)
{
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
if (!addon) {
/* Workspace was destroyed from the compositor side */
return;
}
struct lab_cosmic_workspace *workspace = addon->data;
lab_transaction_op_add(addon->ctx, CW_PENDING_WS_DEACTIVATE,
workspace, /*data*/ NULL);
}
static void
workspace_handle_remove(struct wl_client *client, struct wl_resource *resource)
{
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
if (!addon) {
/* workspace was destroyed from the compositor side */
return;
}
struct lab_cosmic_workspace *workspace = addon->data;
lab_transaction_op_add(addon->ctx, CW_PENDING_WS_REMOVE,
workspace, /*data*/ NULL);
}
static const struct zcosmic_workspace_handle_v1_interface workspace_impl = {
.destroy = workspace_handle_destroy,
.activate = workspace_handle_activate,
.deactivate = workspace_handle_deactivate,
.remove = workspace_handle_remove,
};
static void
workspace_instance_resource_destroy(struct wl_resource *resource)
{
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
if (addon) {
lab_resource_addon_destroy(addon);
wl_resource_set_user_data(resource, NULL);
}
wl_list_remove(wl_resource_get_link(resource));
}
static struct wl_resource *
workspace_resource_create(struct lab_cosmic_workspace *workspace,
struct wl_resource *group_resource, struct lab_transaction_session_context *ctx)
{
struct wl_client *client = wl_resource_get_client(group_resource);
struct wl_resource *resource = wl_resource_create(client,
&zcosmic_workspace_handle_v1_interface,
wl_resource_get_version(group_resource), 0);
if (!resource) {
wl_client_post_no_memory(client);
return NULL;
}
struct lab_wl_resource_addon *addon = lab_resource_addon_create(ctx);
addon->data = workspace;
wl_resource_set_implementation(resource, &workspace_impl, addon,
workspace_instance_resource_destroy);
wl_list_insert(&workspace->resources, wl_resource_get_link(resource));
return resource;
}
/* Workspace internal helpers */
static void
workspace_send_state(struct lab_cosmic_workspace *workspace, struct wl_resource *target)
{
struct wl_array state;
wl_array_init(&state);
if (workspace->state & CW_WS_STATE_ACTIVE) {
array_add(&state, ZCOSMIC_WORKSPACE_HANDLE_V1_STATE_ACTIVE);
}
if (workspace->state & CW_WS_STATE_URGENT) {
array_add(&state, ZCOSMIC_WORKSPACE_HANDLE_V1_STATE_URGENT);
}
if (workspace->state & CW_WS_STATE_HIDDEN) {
array_add(&state, ZCOSMIC_WORKSPACE_HANDLE_V1_STATE_HIDDEN);
}
if (target) {
zcosmic_workspace_handle_v1_send_state(target, &state);
} else {
struct wl_resource *resource;
wl_resource_for_each(resource, &workspace->resources) {
zcosmic_workspace_handle_v1_send_state(resource, &state);
}
}
wl_array_release(&state);
}
static void
workspace_send_initial_state(struct lab_cosmic_workspace *workspace, struct wl_resource *resource)
{
zcosmic_workspace_handle_v1_send_capabilities(resource, &workspace->capabilities);
if (workspace->coordinates.size > 0) {
zcosmic_workspace_handle_v1_send_coordinates(resource, &workspace->coordinates);
}
if (workspace->name) {
zcosmic_workspace_handle_v1_send_name(resource, workspace->name);
}
}
static void
workspace_set_state(struct lab_cosmic_workspace *workspace,
enum workspace_state state, bool enabled)
{
if (!!(workspace->state_pending & state) == enabled) {
return;
}
if (enabled) {
workspace->state_pending |= state;
} else {
workspace->state_pending &= ~state;
}
cosmic_manager_schedule_done_event(workspace->group->manager);
}
/* Group */
static void
ws_create_workspace_handle_transaction_op_destroy(struct wl_listener *listener, void *data)
{
struct ws_create_workspace_event *ev =
wl_container_of(listener, ev, on.transaction_op_destroy);
wl_list_remove(&ev->on.transaction_op_destroy.link);
free(ev->name);
free(ev);
}
static void
group_handle_create_workspace(struct wl_client *client,
struct wl_resource *resource, const char *name)
{
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
if (!addon) {
return;
}
struct lab_cosmic_workspace_group *group = addon->data;
struct ws_create_workspace_event *ev = znew(*ev);
ev->name = xstrdup(name);
struct lab_transaction_op *transaction_op = lab_transaction_op_add(
addon->ctx, CW_PENDING_WS_CREATE, group, ev);
ev->on.transaction_op_destroy.notify =
ws_create_workspace_handle_transaction_op_destroy;
wl_signal_add(&transaction_op->events.destroy, &ev->on.transaction_op_destroy);
}
static void
group_handle_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static const struct zcosmic_workspace_group_handle_v1_interface group_impl = {
.create_workspace = group_handle_create_workspace,
.destroy = group_handle_destroy,
};
static void
group_instance_resource_destroy(struct wl_resource *resource)
{
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
if (addon) {
lab_resource_addon_destroy(addon);
wl_resource_set_user_data(resource, NULL);
}
wl_list_remove(wl_resource_get_link(resource));
}
static struct wl_resource *
group_resource_create(struct lab_cosmic_workspace_group *group,
struct wl_resource *manager_resource, struct lab_transaction_session_context *ctx)
{
struct wl_client *client = wl_resource_get_client(manager_resource);
struct wl_resource *resource = wl_resource_create(client,
&zcosmic_workspace_group_handle_v1_interface,
wl_resource_get_version(manager_resource), 0);
if (!resource) {
wl_client_post_no_memory(client);
return NULL;
}
struct lab_wl_resource_addon *addon = lab_resource_addon_create(ctx);
addon->data = group;
wl_resource_set_implementation(resource, &group_impl, addon,
group_instance_resource_destroy);
wl_list_insert(&group->resources, wl_resource_get_link(resource));
return resource;
}
/* Group internal helpers */
static void
group_send_state(struct lab_cosmic_workspace_group *group, struct wl_resource *resource)
{
zcosmic_workspace_group_handle_v1_send_capabilities(
resource, &group->capabilities);
cosmic_group_output_send_initial_state(group, resource);
}
/* Manager itself */
static void
manager_handle_commit(struct wl_client *client, struct wl_resource *resource)
{
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
if (!addon) {
return;
}
struct lab_cosmic_workspace *workspace;
struct lab_cosmic_workspace_group *group;
struct lab_transaction_op *trans_op, *trans_op_tmp;
lab_transaction_for_each_safe(trans_op, trans_op_tmp, addon->ctx) {
switch (trans_op->change) {
case CW_PENDING_WS_CREATE: {
group = trans_op->src;
struct ws_create_workspace_event *ev = trans_op->data;
wl_signal_emit_mutable(&group->events.create_workspace, ev->name);
break;
}
case CW_PENDING_WS_ACTIVATE:
workspace = trans_op->src;
wl_signal_emit_mutable(&workspace->events.activate, NULL);
break;
case CW_PENDING_WS_DEACTIVATE:
workspace = trans_op->src;
wl_signal_emit_mutable(&workspace->events.deactivate, NULL);
break;
case CW_PENDING_WS_REMOVE:
workspace = trans_op->src;
wl_signal_emit_mutable(&workspace->events.remove, NULL);
break;
default:
wlr_log(WLR_ERROR, "Invalid transaction state: %u", trans_op->change);
}
lab_transaction_op_destroy(trans_op);
}
}
static void
manager_handle_stop(struct wl_client *client, struct wl_resource *resource)
{
zcosmic_workspace_manager_v1_send_finished(resource);
wl_resource_destroy(resource);
}
static const struct zcosmic_workspace_manager_v1_interface manager_impl = {
.commit = manager_handle_commit,
.stop = manager_handle_stop,
};
static void
manager_instance_resource_destroy(struct wl_resource *resource)
{
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
if (addon) {
lab_resource_addon_destroy(addon);
wl_resource_set_user_data(resource, NULL);
}
wl_list_remove(wl_resource_get_link(resource));
}
static void
manager_handle_bind(struct wl_client *client, void *data,
uint32_t version, uint32_t id)
{
struct lab_cosmic_workspace_manager *manager = data;
struct wl_resource *resource = wl_resource_create(client,
&zcosmic_workspace_manager_v1_interface,
version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
struct lab_wl_resource_addon *addon =
lab_resource_addon_create(/* session context*/ NULL);
addon->data = manager;
wl_resource_set_implementation(resource, &manager_impl,
addon, manager_instance_resource_destroy);
wl_list_insert(&manager->resources, wl_resource_get_link(resource));
struct lab_cosmic_workspace *workspace;
struct lab_cosmic_workspace_group *group;
wl_list_for_each(group, &manager->groups, link) {
/* Create group resource */
struct wl_resource *group_resource =
group_resource_create(group, resource, addon->ctx);
zcosmic_workspace_manager_v1_send_workspace_group(resource, group_resource);
group_send_state(group, group_resource);
/* Create workspace resource */
wl_list_for_each(workspace, &group->workspaces, link) {
struct wl_resource *workspace_resource =
workspace_resource_create(workspace, group_resource, addon->ctx);
zcosmic_workspace_group_handle_v1_send_workspace(
group_resource, workspace_resource);
workspace_send_initial_state(workspace, workspace_resource);
/* Send the current workspace state manually */
workspace_send_state(workspace, workspace_resource);
}
}
zcosmic_workspace_manager_v1_send_done(resource);
}
static void
manager_handle_display_destroy(struct wl_listener *listener, void *data)
{
struct lab_cosmic_workspace_manager *manager =
wl_container_of(listener, manager, on.display_destroy);
struct lab_cosmic_workspace_group *group, *tmp;
wl_list_for_each_safe(group, tmp, &manager->groups, link) {
lab_cosmic_workspace_group_destroy(group);
}
if (manager->idle_source) {
wl_event_source_remove(manager->idle_source);
}
wl_list_remove(&manager->on.display_destroy.link);
wl_global_destroy(manager->global);
free(manager);
}
/* Manager internal helpers */
static void
manager_idle_send_done(void *data)
{
struct lab_cosmic_workspace_manager *manager = data;
struct lab_cosmic_workspace *workspace;
struct lab_cosmic_workspace_group *group;
wl_list_for_each(group, &manager->groups, link) {
wl_list_for_each(workspace, &group->workspaces, link) {
if (workspace->state != workspace->state_pending) {
workspace->state = workspace->state_pending;
workspace_send_state(workspace, /*target*/ NULL);
}
}
}
struct wl_resource *resource;
wl_resource_for_each(resource, &manager->resources) {
zcosmic_workspace_manager_v1_send_done(resource);
}
manager->idle_source = NULL;
}
/* Internal API */
void
cosmic_manager_schedule_done_event(struct lab_cosmic_workspace_manager *manager)
{
if (manager->idle_source) {
return;
}
if (!manager->event_loop) {
return;
}
manager->idle_source = wl_event_loop_add_idle(
manager->event_loop, manager_idle_send_done, manager);
}
/* Public API */
struct lab_cosmic_workspace_manager *
lab_cosmic_workspace_manager_create(struct wl_display *display, uint32_t caps, uint32_t version)
{
assert(version <= COSMIC_WORKSPACE_V1_VERSION);
struct lab_cosmic_workspace_manager *manager = znew(*manager);
manager->global = wl_global_create(display,
&zcosmic_workspace_manager_v1_interface,
version, manager, manager_handle_bind);
if (!manager->global) {
free(manager);
return NULL;
}
manager->caps = caps;
manager->event_loop = wl_display_get_event_loop(display);
manager->on.display_destroy.notify = manager_handle_display_destroy;
wl_display_add_destroy_listener(display, &manager->on.display_destroy);
wl_list_init(&manager->groups);
wl_list_init(&manager->resources);
return manager;
}
struct lab_cosmic_workspace_group *
lab_cosmic_workspace_group_create(struct lab_cosmic_workspace_manager *manager)
{
assert(manager);
struct lab_cosmic_workspace_group *group = znew(*group);
group->manager = manager;
wl_array_init(&group->capabilities);
add_caps(&group->capabilities, manager->caps & CW_CAP_GRP_ALL);
wl_list_init(&group->outputs);
wl_list_init(&group->resources);
wl_list_init(&group->workspaces);
wl_signal_init(&group->events.create_workspace);
wl_signal_init(&group->events.destroy);
wl_list_append(&manager->groups, &group->link);
struct wl_resource *resource, *tmp;
wl_resource_for_each_safe(resource, tmp, &manager->resources) {
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
assert(addon && addon->ctx);
struct wl_resource *group_resource =
group_resource_create(group, resource, addon->ctx);
zcosmic_workspace_manager_v1_send_workspace_group(resource, group_resource);
group_send_state(group, group_resource);
}
cosmic_manager_schedule_done_event(manager);
return group;
}
void
lab_cosmic_workspace_group_destroy(struct lab_cosmic_workspace_group *group)
{
if (!group) {
return;
}
wl_signal_emit_mutable(&group->events.destroy, NULL);
struct lab_cosmic_workspace *ws, *ws_tmp;
wl_list_for_each_safe(ws, ws_tmp, &group->workspaces, link) {
lab_cosmic_workspace_destroy(ws);
}
struct wl_resource *resource, *res_tmp;
wl_resource_for_each_safe(resource, res_tmp, &group->resources) {
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
if (addon) {
lab_resource_addon_destroy(addon);
wl_resource_set_user_data(resource, NULL);
}
zcosmic_workspace_group_handle_v1_send_remove(resource);
wl_list_remove(wl_resource_get_link(resource));
wl_list_init(wl_resource_get_link(resource));
}
/* Cancel pending transaction operations involving this group */
struct lab_transaction_op *trans_op, *trans_op_tmp;
wl_resource_for_each(resource, &group->manager->resources) {
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
if (!addon) {
continue;
}
lab_transaction_for_each_safe(trans_op, trans_op_tmp, addon->ctx) {
if (trans_op->src == group) {
lab_transaction_op_destroy(trans_op);
}
}
}
wl_list_remove(&group->link);
wl_array_release(&group->capabilities);
free(group);
}
struct lab_cosmic_workspace *
lab_cosmic_workspace_create(struct lab_cosmic_workspace_group *group)
{
assert(group);
struct lab_cosmic_workspace *workspace = znew(*workspace);
workspace->group = group;
/*
* Ensures we are sending workspace->state_pending on the done event,
* regardless if the compositor has changed any state in between here
* and the scheduled done event or not.
*
* Without this we might have to send the state twice, first here and
* then again in the scheduled done event when there were any changes.
*/
workspace->state = CW_WS_STATE_INVALID;
wl_array_init(&workspace->capabilities);
add_caps(&workspace->capabilities, group->manager->caps & CW_CAP_WS_ALL);
wl_list_init(&workspace->resources);
wl_array_init(&workspace->coordinates);
wl_signal_init(&workspace->events.activate);
wl_signal_init(&workspace->events.deactivate);
wl_signal_init(&workspace->events.remove);
wl_signal_init(&workspace->events.destroy);
wl_list_append(&group->workspaces, &workspace->link);
/* Notify clients */
struct wl_resource *group_resource;
wl_resource_for_each(group_resource, &group->resources) {
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(group_resource);
assert(addon && addon->ctx);
struct wl_resource *workspace_resource =
workspace_resource_create(workspace, group_resource, addon->ctx);
zcosmic_workspace_group_handle_v1_send_workspace(
group_resource, workspace_resource);
workspace_send_initial_state(workspace, workspace_resource);
}
cosmic_manager_schedule_done_event(group->manager);
return workspace;
}
void
lab_cosmic_workspace_set_name(struct lab_cosmic_workspace *workspace, const char *name)
{
assert(workspace);
assert(name);
if (!workspace->name || strcmp(workspace->name, name)) {
free(workspace->name);
workspace->name = xstrdup(name);
struct wl_resource *resource;
wl_resource_for_each(resource, &workspace->resources) {
zcosmic_workspace_handle_v1_send_name(resource, workspace->name);
}
}
cosmic_manager_schedule_done_event(workspace->group->manager);
}
void
lab_cosmic_workspace_set_active(struct lab_cosmic_workspace *workspace, bool enabled)
{
workspace_set_state(workspace, CW_WS_STATE_ACTIVE, enabled);
}
void
lab_cosmic_workspace_set_urgent(struct lab_cosmic_workspace *workspace, bool enabled)
{
workspace_set_state(workspace, CW_WS_STATE_URGENT, enabled);
}
void
lab_cosmic_workspace_set_hidden(struct lab_cosmic_workspace *workspace, bool enabled)
{
workspace_set_state(workspace, CW_WS_STATE_HIDDEN, enabled);
}
void
lab_cosmic_workspace_set_coordinates(struct lab_cosmic_workspace *workspace,
struct wl_array *coordinates)
{
wl_array_release(&workspace->coordinates);
wl_array_init(&workspace->coordinates);
wl_array_copy(&workspace->coordinates, coordinates);
struct wl_resource *resource;
wl_resource_for_each(resource, &workspace->resources) {
zcosmic_workspace_handle_v1_send_coordinates(resource, &workspace->coordinates);
}
cosmic_manager_schedule_done_event(workspace->group->manager);
}
void
lab_cosmic_workspace_destroy(struct lab_cosmic_workspace *workspace)
{
if (!workspace) {
return;
}
wl_signal_emit_mutable(&workspace->events.destroy, NULL);
struct wl_resource *resource, *tmp;
wl_resource_for_each_safe(resource, tmp, &workspace->resources) {
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
if (addon) {
lab_resource_addon_destroy(addon);
wl_resource_set_user_data(resource, NULL);
}
zcosmic_workspace_handle_v1_send_remove(resource);
wl_list_remove(wl_resource_get_link(resource));
wl_list_init(wl_resource_get_link(resource));
}
cosmic_manager_schedule_done_event(workspace->group->manager);
/* Cancel pending transaction operations involving this workspace */
struct lab_transaction_op *trans_op, *trans_op_tmp;
wl_resource_for_each(resource, &workspace->group->manager->resources) {
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
if (!addon) {
continue;
}
lab_transaction_for_each_safe(trans_op, trans_op_tmp, addon->ctx) {
if (trans_op->src == workspace) {
lab_transaction_op_destroy(trans_op);
}
}
}
wl_list_remove(&workspace->link);
wl_array_release(&workspace->coordinates);
wl_array_release(&workspace->capabilities);
zfree(workspace->name);
free(workspace);
}

View file

@ -1,4 +0,0 @@
labwc_sources += files(
'cosmic-workspaces.c',
'output.c',
)

View file

@ -1,174 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <wayland-server-core.h>
#include <wlr/types/wlr_output.h>
#include <wlr/util/log.h>
#include "common/mem.h"
#include "cosmic-workspace-unstable-v1-protocol.h"
#include "protocols/cosmic-workspaces.h"
#include "protocols/cosmic-workspaces-internal.h"
struct group_output {
struct wlr_output *wlr_output;
struct lab_cosmic_workspace_group *group;
struct {
struct wl_listener group_destroy;
struct wl_listener output_bind;
struct wl_listener output_destroy;
} on;
struct wl_list link;
};
/* Internal helpers */
static void
group_output_send_event(struct wl_list *group_resources, struct wl_list *output_resources,
void (*notifier)(struct wl_resource *group, struct wl_resource *output))
{
struct wl_client *client;
struct wl_resource *group_resource, *output_resource;
wl_resource_for_each(group_resource, group_resources) {
client = wl_resource_get_client(group_resource);
wl_resource_for_each(output_resource, output_resources) {
if (wl_resource_get_client(output_resource) == client) {
notifier(group_resource, output_resource);
}
}
}
}
static void
group_output_destroy(struct group_output *group_output)
{
group_output_send_event(
&group_output->group->resources,
&group_output->wlr_output->resources,
zcosmic_workspace_group_handle_v1_send_output_leave);
cosmic_manager_schedule_done_event(group_output->group->manager);
wl_list_remove(&group_output->link);
wl_list_remove(&group_output->on.group_destroy.link);
wl_list_remove(&group_output->on.output_bind.link);
wl_list_remove(&group_output->on.output_destroy.link);
free(group_output);
}
/* Event handlers */
static void
handle_output_bind(struct wl_listener *listener, void *data)
{
struct group_output *group_output =
wl_container_of(listener, group_output, on.output_bind);
struct wlr_output_event_bind *event = data;
struct wl_client *client = wl_resource_get_client(event->resource);
bool sent = false;
struct wl_resource *group_resource;
wl_resource_for_each(group_resource, &group_output->group->resources) {
if (wl_resource_get_client(group_resource) == client) {
zcosmic_workspace_group_handle_v1_send_output_enter(
group_resource, event->resource);
sent = true;
}
}
if (!sent) {
return;
}
struct wl_resource *manager_resource;
struct wl_list *manager_resources = &group_output->group->manager->resources;
wl_resource_for_each(manager_resource, manager_resources) {
if (wl_resource_get_client(manager_resource) == client) {
zcosmic_workspace_manager_v1_send_done(manager_resource);
}
}
}
static void
handle_output_destroy(struct wl_listener *listener, void *data)
{
struct group_output *group_output =
wl_container_of(listener, group_output, on.output_destroy);
group_output_destroy(group_output);
}
static void
handle_group_destroy(struct wl_listener *listener, void *data)
{
struct group_output *group_output =
wl_container_of(listener, group_output, on.group_destroy);
group_output_destroy(group_output);
}
/* Internal API*/
void
cosmic_group_output_send_initial_state(struct lab_cosmic_workspace_group *group,
struct wl_resource *group_resource)
{
struct group_output *group_output;
struct wl_resource *output_resource;
struct wl_client *client = wl_resource_get_client(group_resource);
wl_list_for_each(group_output, &group->outputs, link) {
wl_resource_for_each(output_resource, &group_output->wlr_output->resources) {
if (wl_resource_get_client(output_resource) == client) {
zcosmic_workspace_group_handle_v1_send_output_enter(
group_resource, output_resource);
}
}
}
}
/* Public API */
void
lab_cosmic_workspace_group_output_enter(struct lab_cosmic_workspace_group *group,
struct wlr_output *wlr_output)
{
struct group_output *group_output;
wl_list_for_each(group_output, &group->outputs, link) {
if (group_output->wlr_output == wlr_output) {
return;
}
}
group_output = znew(*group_output);
group_output->wlr_output = wlr_output;
group_output->group = group;
group_output->on.group_destroy.notify = handle_group_destroy;
wl_signal_add(&group->events.destroy, &group_output->on.group_destroy);
group_output->on.output_bind.notify = handle_output_bind;
wl_signal_add(&wlr_output->events.bind, &group_output->on.output_bind);
group_output->on.output_destroy.notify = handle_output_destroy;
wl_signal_add(&wlr_output->events.destroy, &group_output->on.output_destroy);
wl_list_insert(&group->outputs, &group_output->link);
group_output_send_event(
&group_output->group->resources,
&group_output->wlr_output->resources,
zcosmic_workspace_group_handle_v1_send_output_enter);
cosmic_manager_schedule_done_event(group->manager);
}
void
lab_cosmic_workspace_group_output_leave(struct lab_cosmic_workspace_group *group,
struct wlr_output *wlr_output)
{
struct group_output *tmp;
struct group_output *group_output = NULL;
wl_list_for_each(tmp, &group->outputs, link) {
if (tmp->wlr_output == wlr_output) {
group_output = tmp;
break;
}
}
if (!group_output) {
wlr_log(WLR_ERROR, "output %s was never entered", wlr_output->name);
return;
}
group_output_destroy(group_output);
}

View file

@ -1,762 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <assert.h>
#include <wlr/util/log.h>
#include "common/mem.h"
#include "common/list.h"
#include "ext-workspace-v1-protocol.h"
#include "protocols/ext-workspace.h"
#include "protocols/ext-workspace-internal.h"
#include "protocols/transaction-addon.h"
/*
* .--------------------.
* | TODO |
* |--------------------|
* | - go through xml |
* | and verify impl |
* | - assert pub API |
* `--------------------´
*
*/
/* Only used within an assert() */
#ifndef NDEBUG
#define EXT_WORKSPACE_V1_VERSION 1
#endif
/*
* Set when creating a new workspace state so we
* don't end up having to send the state twice.
*/
#define WS_STATE_INVALID 0xffffffff
struct ws_create_workspace_event {
char *name;
struct {
struct wl_listener transaction_op_destroy;
} on;
};
/* Workspace */
static void
workspace_handle_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static void
workspace_handle_activate(struct wl_client *client, struct wl_resource *resource)
{
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
if (!addon) {
/* Workspace was destroyed from the compositor side */
return;
}
struct lab_ext_workspace *workspace = addon->data;
lab_transaction_op_add(addon->ctx, WS_PENDING_WS_ACTIVATE,
workspace, /*data*/ NULL);
}
static void
workspace_handle_deactivate(struct wl_client *client, struct wl_resource *resource)
{
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
if (!addon) {
/* Workspace was destroyed from the compositor side */
return;
}
struct lab_ext_workspace *workspace = addon->data;
lab_transaction_op_add(addon->ctx, WS_PENDING_WS_DEACTIVATE,
workspace, /*data*/ NULL);
}
static void
workspace_handle_assign(struct wl_client *client, struct wl_resource *resource,
struct wl_resource *new_group_resource)
{
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
if (!addon) {
/* Workspace was destroyed from the compositor side */
return;
}
struct lab_ext_workspace *workspace = addon->data;
struct lab_wl_resource_addon *grp_addon =
wl_resource_get_user_data(new_group_resource);
if (!grp_addon) {
/* Group was destroyed from the compositor side */
return;
}
struct lab_ext_workspace_group *new_grp = grp_addon->data;
lab_transaction_op_add(addon->ctx, WS_PENDING_WS_ASSIGN, workspace, new_grp);
}
static void
workspace_handle_remove(struct wl_client *client, struct wl_resource *resource)
{
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
if (!addon) {
/* Workspace was destroyed from the compositor side */
return;
}
struct lab_ext_workspace *workspace = addon->data;
lab_transaction_op_add(addon->ctx, WS_PENDING_WS_REMOVE,
workspace, /*data*/ NULL);
}
static const struct ext_workspace_handle_v1_interface workspace_impl = {
.destroy = workspace_handle_destroy,
.activate = workspace_handle_activate,
.deactivate = workspace_handle_deactivate,
.assign = workspace_handle_assign,
.remove = workspace_handle_remove,
};
static void
workspace_instance_resource_destroy(struct wl_resource *resource)
{
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
if (addon) {
lab_resource_addon_destroy(addon);
wl_resource_set_user_data(resource, NULL);
}
wl_list_remove(wl_resource_get_link(resource));
}
static struct wl_resource *
workspace_resource_create(struct lab_ext_workspace *workspace,
struct wl_resource *manager_resource,
struct lab_transaction_session_context *ctx)
{
struct wl_client *client = wl_resource_get_client(manager_resource);
struct wl_resource *resource = wl_resource_create(client,
&ext_workspace_handle_v1_interface,
wl_resource_get_version(manager_resource), 0);
if (!resource) {
wl_client_post_no_memory(client);
return NULL;
}
struct lab_wl_resource_addon *addon = lab_resource_addon_create(ctx);
addon->data = workspace;
wl_resource_set_implementation(resource, &workspace_impl, addon,
workspace_instance_resource_destroy);
wl_list_insert(&workspace->resources, wl_resource_get_link(resource));
return resource;
}
/* Workspace internal helpers */
static void
workspace_send_state(struct lab_ext_workspace *workspace, struct wl_resource *target)
{
if (target) {
ext_workspace_handle_v1_send_state(target, workspace->state);
} else {
struct wl_resource *resource;
wl_resource_for_each(resource, &workspace->resources) {
ext_workspace_handle_v1_send_state(resource, workspace->state);
}
}
}
static void
workspace_send_initial_state(struct lab_ext_workspace *workspace, struct wl_resource *resource)
{
ext_workspace_handle_v1_send_capabilities(resource, workspace->capabilities);
if (workspace->coordinates.size > 0) {
ext_workspace_handle_v1_send_coordinates(resource, &workspace->coordinates);
}
if (workspace->name) {
ext_workspace_handle_v1_send_name(resource, workspace->name);
}
if (workspace->id) {
ext_workspace_handle_v1_send_id(resource, workspace->id);
}
}
static void
workspace_set_state(struct lab_ext_workspace *workspace,
enum ext_workspace_handle_v1_state state, bool enabled)
{
if (!!(workspace->state_pending & state) == enabled) {
return;
}
if (enabled) {
workspace->state_pending |= state;
} else {
workspace->state_pending &= ~state;
}
ext_manager_schedule_done_event(workspace->manager);
}
/* Group */
static void
ws_create_workspace_handle_transaction_op_destroy(struct wl_listener *listener, void *data)
{
struct ws_create_workspace_event *ev =
wl_container_of(listener, ev, on.transaction_op_destroy);
wl_list_remove(&ev->on.transaction_op_destroy.link);
free(ev->name);
free(ev);
}
static void
group_handle_create_workspace(struct wl_client *client,
struct wl_resource *resource, const char *name)
{
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
if (!addon) {
return;
}
struct lab_ext_workspace_group *group = addon->data;
struct ws_create_workspace_event *ev = znew(*ev);
ev->name = xstrdup(name);
struct lab_transaction_op *transaction_op = lab_transaction_op_add(
addon->ctx, WS_PENDING_WS_CREATE, group, ev);
ev->on.transaction_op_destroy.notify =
ws_create_workspace_handle_transaction_op_destroy;
wl_signal_add(&transaction_op->events.destroy, &ev->on.transaction_op_destroy);
}
static void
group_handle_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static const struct ext_workspace_group_handle_v1_interface group_impl = {
.create_workspace = group_handle_create_workspace,
.destroy = group_handle_destroy,
};
static void
group_instance_resource_destroy(struct wl_resource *resource)
{
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
if (addon) {
lab_resource_addon_destroy(addon);
wl_resource_set_user_data(resource, NULL);
}
wl_list_remove(wl_resource_get_link(resource));
}
static struct wl_resource *
group_resource_create(struct lab_ext_workspace_group *group,
struct wl_resource *manager_resource, struct lab_transaction_session_context *ctx)
{
struct wl_client *client = wl_resource_get_client(manager_resource);
struct wl_resource *resource = wl_resource_create(client,
&ext_workspace_group_handle_v1_interface,
wl_resource_get_version(manager_resource), 0);
if (!resource) {
wl_client_post_no_memory(client);
return NULL;
}
struct lab_wl_resource_addon *addon = lab_resource_addon_create(ctx);
addon->data = group;
wl_resource_set_implementation(resource, &group_impl, addon,
group_instance_resource_destroy);
wl_list_insert(&group->resources, wl_resource_get_link(resource));
return resource;
}
/* Group internal helpers */
static void
group_send_state(struct lab_ext_workspace_group *group, struct wl_resource *resource)
{
ext_workspace_group_handle_v1_send_capabilities(
resource, group->capabilities);
ext_group_output_send_initial_state(group, resource);
}
/* Manager itself */
static void
manager_handle_commit(struct wl_client *client, struct wl_resource *resource)
{
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
if (!addon) {
return;
}
struct lab_ext_workspace *workspace;
struct lab_ext_workspace_group *group;
struct lab_transaction_op *trans_op, *trans_op_tmp;
lab_transaction_for_each_safe(trans_op, trans_op_tmp, addon->ctx) {
switch (trans_op->change) {
case WS_PENDING_WS_CREATE: {
group = trans_op->src;
struct ws_create_workspace_event *ev = trans_op->data;
wl_signal_emit_mutable(&group->events.create_workspace, ev->name);
break;
}
case WS_PENDING_WS_ACTIVATE:
workspace = trans_op->src;
wl_signal_emit_mutable(&workspace->events.activate, NULL);
break;
case WS_PENDING_WS_DEACTIVATE:
workspace = trans_op->src;
wl_signal_emit_mutable(&workspace->events.deactivate, NULL);
break;
case WS_PENDING_WS_REMOVE:
workspace = trans_op->src;
wl_signal_emit_mutable(&workspace->events.remove, NULL);
break;
case WS_PENDING_WS_ASSIGN:
workspace = trans_op->src;
wl_signal_emit_mutable(&workspace->events.assign, trans_op->data);
break;
default:
wlr_log(WLR_ERROR, "Invalid transaction state: %u", trans_op->change);
}
lab_transaction_op_destroy(trans_op);
}
}
static void
manager_handle_stop(struct wl_client *client, struct wl_resource *resource)
{
ext_workspace_manager_v1_send_finished(resource);
wl_resource_destroy(resource);
}
static const struct ext_workspace_manager_v1_interface manager_impl = {
.commit = manager_handle_commit,
.stop = manager_handle_stop,
};
static void
manager_instance_resource_destroy(struct wl_resource *resource)
{
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
if (addon) {
lab_resource_addon_destroy(addon);
wl_resource_set_user_data(resource, NULL);
}
wl_list_remove(wl_resource_get_link(resource));
}
static void
manager_handle_bind(struct wl_client *client, void *data,
uint32_t version, uint32_t id)
{
struct lab_ext_workspace_manager *manager = data;
struct wl_resource *manager_resource = wl_resource_create(client,
&ext_workspace_manager_v1_interface,
version, id);
if (!manager_resource) {
wl_client_post_no_memory(client);
return;
}
struct lab_wl_resource_addon *addon =
lab_resource_addon_create(/* session context*/ NULL);
addon->data = manager;
wl_resource_set_implementation(manager_resource, &manager_impl,
addon, manager_instance_resource_destroy);
wl_list_insert(&manager->resources, wl_resource_get_link(manager_resource));
struct lab_ext_workspace *workspace;
struct lab_ext_workspace_group *group;
wl_list_for_each(group, &manager->groups, link) {
/* Create group resource */
struct wl_resource *group_resource =
group_resource_create(group, manager_resource, addon->ctx);
ext_workspace_manager_v1_send_workspace_group(manager_resource, group_resource);
group_send_state(group, group_resource);
wl_list_for_each(workspace, &manager->workspaces, link) {
if (workspace->group != group) {
continue;
}
/* Create workspace resource for the current group */
struct wl_resource *workspace_resource = workspace_resource_create(
workspace, manager_resource, addon->ctx);
ext_workspace_manager_v1_send_workspace(
manager_resource, workspace_resource);
workspace_send_initial_state(workspace, workspace_resource);
workspace_send_state(workspace, workspace_resource);
ext_workspace_group_handle_v1_send_workspace_enter(
group_resource, workspace_resource);
}
}
/* Create workspace resource for workspaces not belonging to any group */
wl_list_for_each(workspace, &manager->workspaces, link) {
if (workspace->group) {
continue;
}
struct wl_resource *workspace_resource =
workspace_resource_create(workspace, manager_resource, addon->ctx);
ext_workspace_manager_v1_send_workspace(manager_resource, workspace_resource);
workspace_send_initial_state(workspace, workspace_resource);
workspace_send_state(workspace, workspace_resource);
}
ext_workspace_manager_v1_send_done(manager_resource);
}
static void
manager_handle_display_destroy(struct wl_listener *listener, void *data)
{
struct lab_ext_workspace_manager *manager =
wl_container_of(listener, manager, on.display_destroy);
struct lab_ext_workspace_group *group, *tmp;
wl_list_for_each_safe(group, tmp, &manager->groups, link) {
lab_ext_workspace_group_destroy(group);
}
struct lab_ext_workspace *ws, *ws_tmp;
wl_list_for_each_safe(ws, ws_tmp, &manager->workspaces, link) {
lab_ext_workspace_destroy(ws);
}
if (manager->idle_source) {
wl_event_source_remove(manager->idle_source);
}
wl_list_remove(&manager->on.display_destroy.link);
wl_global_destroy(manager->global);
free(manager);
}
/* Manager internal helpers */
static void
manager_idle_send_done(void *data)
{
struct lab_ext_workspace_manager *manager = data;
struct lab_ext_workspace *workspace;
wl_list_for_each(workspace, &manager->workspaces, link) {
if (workspace->state != workspace->state_pending) {
workspace->state = workspace->state_pending;
workspace_send_state(workspace, /*target*/ NULL);
}
}
struct wl_resource *resource;
wl_resource_for_each(resource, &manager->resources) {
ext_workspace_manager_v1_send_done(resource);
}
manager->idle_source = NULL;
}
/* Internal API */
void
ext_manager_schedule_done_event(struct lab_ext_workspace_manager *manager)
{
if (manager->idle_source) {
return;
}
if (!manager->event_loop) {
return;
}
manager->idle_source = wl_event_loop_add_idle(
manager->event_loop, manager_idle_send_done, manager);
}
static void
send_group_workspace_event(struct lab_ext_workspace_group *group,
struct lab_ext_workspace *workspace,
void (*fn)(struct wl_resource *grp_res, struct wl_resource *ws_res))
{
struct lab_wl_resource_addon *workspace_addon, *group_addon;
struct wl_resource *workspace_resource, *group_resource;
wl_resource_for_each(workspace_resource, &workspace->resources) {
workspace_addon = wl_resource_get_user_data(workspace_resource);
wl_resource_for_each(group_resource, &group->resources) {
group_addon = wl_resource_get_user_data(group_resource);
if (group_addon->ctx != workspace_addon->ctx) {
continue;
}
fn(group_resource, workspace_resource);
break;
}
}
}
/* Public API */
struct lab_ext_workspace_manager *
lab_ext_workspace_manager_create(struct wl_display *display, uint32_t caps, uint32_t version)
{
assert(display);
assert(version <= EXT_WORKSPACE_V1_VERSION);
struct lab_ext_workspace_manager *manager = znew(*manager);
manager->global = wl_global_create(display,
&ext_workspace_manager_v1_interface,
version, manager, manager_handle_bind);
if (!manager->global) {
free(manager);
return NULL;
}
manager->caps = caps;
manager->event_loop = wl_display_get_event_loop(display);
manager->on.display_destroy.notify = manager_handle_display_destroy;
wl_display_add_destroy_listener(display, &manager->on.display_destroy);
wl_list_init(&manager->groups);
wl_list_init(&manager->workspaces);
wl_list_init(&manager->resources);
return manager;
}
struct lab_ext_workspace_group *
lab_ext_workspace_group_create(struct lab_ext_workspace_manager *manager)
{
assert(manager);
struct lab_ext_workspace_group *group = znew(*group);
group->manager = manager;
group->capabilities = manager->caps & WS_CAP_GRP_ALL;
wl_list_init(&group->outputs);
wl_list_init(&group->resources);
wl_signal_init(&group->events.create_workspace);
wl_signal_init(&group->events.destroy);
wl_list_append(&manager->groups, &group->link);
struct wl_resource *resource, *tmp;
wl_resource_for_each_safe(resource, tmp, &manager->resources) {
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
assert(addon && addon->ctx);
struct wl_resource *group_resource =
group_resource_create(group, resource, addon->ctx);
ext_workspace_manager_v1_send_workspace_group(resource, group_resource);
group_send_state(group, group_resource);
}
ext_manager_schedule_done_event(manager);
return group;
}
void
lab_ext_workspace_group_destroy(struct lab_ext_workspace_group *group)
{
assert(group);
wl_signal_emit_mutable(&group->events.destroy, NULL);
struct lab_ext_workspace *workspace;
wl_list_for_each(workspace, &group->manager->workspaces, link) {
if (workspace->group == group) {
send_group_workspace_event(group, workspace,
ext_workspace_group_handle_v1_send_workspace_leave);
workspace->group = NULL;
}
}
struct wl_resource *resource, *res_tmp;
wl_resource_for_each_safe(resource, res_tmp, &group->resources) {
ext_workspace_group_handle_v1_send_removed(resource);
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
lab_resource_addon_destroy(addon);
wl_resource_set_user_data(resource, NULL);
wl_list_remove(wl_resource_get_link(resource));
wl_list_init(wl_resource_get_link(resource));
}
/* Cancel pending transaction ops involving this group */
struct lab_transaction_op *trans_op, *trans_op_tmp;
wl_resource_for_each(resource, &group->manager->resources) {
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
lab_transaction_for_each_safe(trans_op, trans_op_tmp, addon->ctx) {
if (trans_op->src == group || trans_op->data == group) {
lab_transaction_op_destroy(trans_op);
}
}
}
ext_manager_schedule_done_event(group->manager);
wl_list_remove(&group->link);
free(group);
}
struct lab_ext_workspace *
lab_ext_workspace_create(struct lab_ext_workspace_manager *manager, const char *id)
{
assert(manager);
struct lab_ext_workspace *workspace = znew(*workspace);
/*
* Ensures we are sending workspace->state_pending on the done event,
* regardless if the compositor has changed any state in between here
* and the scheduled done event or not.
*
* Without this we might have to send the state twice, first here and
* then again in the scheduled done event when there were any changes.
*/
workspace->state = WS_STATE_INVALID;
workspace->capabilities = (manager->caps & WS_CAP_WS_ALL) >> 16;
workspace->manager = manager;
if (id) {
workspace->id = xstrdup(id);
}
wl_list_init(&workspace->resources);
wl_array_init(&workspace->coordinates);
wl_signal_init(&workspace->events.activate);
wl_signal_init(&workspace->events.deactivate);
wl_signal_init(&workspace->events.remove);
wl_signal_init(&workspace->events.assign);
wl_signal_init(&workspace->events.destroy);
wl_list_append(&manager->workspaces, &workspace->link);
/* Notify clients */
struct lab_wl_resource_addon *manager_addon;
struct wl_resource *manager_resource, *workspace_resource;
wl_resource_for_each(manager_resource, &manager->resources) {
manager_addon = wl_resource_get_user_data(manager_resource);
workspace_resource = workspace_resource_create(
workspace, manager_resource, manager_addon->ctx);
ext_workspace_manager_v1_send_workspace(
manager_resource, workspace_resource);
workspace_send_initial_state(workspace, workspace_resource);
}
ext_manager_schedule_done_event(manager);
return workspace;
}
void
lab_ext_workspace_assign_to_group(struct lab_ext_workspace *workspace,
struct lab_ext_workspace_group *group)
{
assert(workspace);
if (workspace->group == group) {
return;
}
if (workspace->group) {
/* Send leave event for the old group */
send_group_workspace_event(workspace->group, workspace,
ext_workspace_group_handle_v1_send_workspace_leave);
ext_manager_schedule_done_event(workspace->manager);
}
workspace->group = group;
if (!group) {
return;
}
/* Send enter event for the new group */
send_group_workspace_event(group, workspace,
ext_workspace_group_handle_v1_send_workspace_enter);
ext_manager_schedule_done_event(workspace->manager);
}
void
lab_ext_workspace_set_name(struct lab_ext_workspace *workspace, const char *name)
{
assert(workspace);
assert(name);
if (!workspace->name || strcmp(workspace->name, name)) {
free(workspace->name);
workspace->name = xstrdup(name);
struct wl_resource *resource;
wl_resource_for_each(resource, &workspace->resources) {
ext_workspace_handle_v1_send_name(resource, workspace->name);
}
}
ext_manager_schedule_done_event(workspace->manager);
}
void
lab_ext_workspace_set_active(struct lab_ext_workspace *workspace, bool enabled)
{
assert(workspace);
workspace_set_state(workspace, EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE, enabled);
}
void
lab_ext_workspace_set_urgent(struct lab_ext_workspace *workspace, bool enabled)
{
assert(workspace);
workspace_set_state(workspace, EXT_WORKSPACE_HANDLE_V1_STATE_URGENT, enabled);
}
void
lab_ext_workspace_set_hidden(struct lab_ext_workspace *workspace, bool enabled)
{
assert(workspace);
workspace_set_state(workspace, EXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN, enabled);
}
void
lab_ext_workspace_set_coordinates(struct lab_ext_workspace *workspace,
struct wl_array *coordinates)
{
assert(workspace);
assert(coordinates);
wl_array_release(&workspace->coordinates);
wl_array_init(&workspace->coordinates);
wl_array_copy(&workspace->coordinates, coordinates);
struct wl_resource *resource;
wl_resource_for_each(resource, &workspace->resources) {
ext_workspace_handle_v1_send_coordinates(resource, &workspace->coordinates);
}
ext_manager_schedule_done_event(workspace->manager);
}
void
lab_ext_workspace_destroy(struct lab_ext_workspace *workspace)
{
assert(workspace);
wl_signal_emit_mutable(&workspace->events.destroy, NULL);
if (workspace->group) {
send_group_workspace_event(workspace->group, workspace,
ext_workspace_group_handle_v1_send_workspace_leave);
}
struct wl_resource *ws_res, *ws_tmp;
wl_resource_for_each_safe(ws_res, ws_tmp, &workspace->resources) {
ext_workspace_handle_v1_send_removed(ws_res);
struct lab_wl_resource_addon *ws_addon = wl_resource_get_user_data(ws_res);
lab_resource_addon_destroy(ws_addon);
wl_resource_set_user_data(ws_res, NULL);
wl_list_remove(wl_resource_get_link(ws_res));
wl_list_init(wl_resource_get_link(ws_res));
}
ext_manager_schedule_done_event(workspace->manager);
/* Cancel pending transaction ops involving this workspace */
struct wl_resource *resource;
struct lab_transaction_op *trans_op, *trans_op_tmp;
wl_resource_for_each(resource, &workspace->manager->resources) {
struct lab_wl_resource_addon *addon = wl_resource_get_user_data(resource);
lab_transaction_for_each_safe(trans_op, trans_op_tmp, addon->ctx) {
if (trans_op->src == workspace) {
lab_transaction_op_destroy(trans_op);
}
}
}
wl_list_remove(&workspace->link);
wl_array_release(&workspace->coordinates);
zfree(workspace->id);
zfree(workspace->name);
free(workspace);
}

View file

@ -1,4 +0,0 @@
labwc_sources += files(
'ext-workspace.c',
'output.c',
)

View file

@ -1,174 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <wayland-server-core.h>
#include <wlr/types/wlr_output.h>
#include <wlr/util/log.h>
#include "common/mem.h"
#include "ext-workspace-v1-protocol.h"
#include "protocols/ext-workspace.h"
#include "protocols/ext-workspace-internal.h"
struct group_output {
struct wlr_output *wlr_output;
struct lab_ext_workspace_group *group;
struct {
struct wl_listener group_destroy;
struct wl_listener output_bind;
struct wl_listener output_destroy;
} on;
struct wl_list link;
};
/* Internal helpers */
static void
group_output_send_event(struct wl_list *group_resources, struct wl_list *output_resources,
void (*notifier)(struct wl_resource *group, struct wl_resource *output))
{
struct wl_client *client;
struct wl_resource *group_resource, *output_resource;
wl_resource_for_each(group_resource, group_resources) {
client = wl_resource_get_client(group_resource);
wl_resource_for_each(output_resource, output_resources) {
if (wl_resource_get_client(output_resource) == client) {
notifier(group_resource, output_resource);
}
}
}
}
static void
group_output_destroy(struct group_output *group_output)
{
group_output_send_event(
&group_output->group->resources,
&group_output->wlr_output->resources,
ext_workspace_group_handle_v1_send_output_leave);
ext_manager_schedule_done_event(group_output->group->manager);
wl_list_remove(&group_output->link);
wl_list_remove(&group_output->on.group_destroy.link);
wl_list_remove(&group_output->on.output_bind.link);
wl_list_remove(&group_output->on.output_destroy.link);
free(group_output);
}
/* Event handlers */
static void
handle_output_bind(struct wl_listener *listener, void *data)
{
struct group_output *group_output =
wl_container_of(listener, group_output, on.output_bind);
struct wlr_output_event_bind *event = data;
struct wl_client *client = wl_resource_get_client(event->resource);
bool sent = false;
struct wl_resource *group_resource;
wl_resource_for_each(group_resource, &group_output->group->resources) {
if (wl_resource_get_client(group_resource) == client) {
ext_workspace_group_handle_v1_send_output_enter(
group_resource, event->resource);
sent = true;
}
}
if (!sent) {
return;
}
struct wl_resource *manager_resource;
struct wl_list *manager_resources = &group_output->group->manager->resources;
wl_resource_for_each(manager_resource, manager_resources) {
if (wl_resource_get_client(manager_resource) == client) {
ext_workspace_manager_v1_send_done(manager_resource);
}
}
}
static void
handle_output_destroy(struct wl_listener *listener, void *data)
{
struct group_output *group_output =
wl_container_of(listener, group_output, on.output_destroy);
group_output_destroy(group_output);
}
static void
handle_group_destroy(struct wl_listener *listener, void *data)
{
struct group_output *group_output =
wl_container_of(listener, group_output, on.group_destroy);
group_output_destroy(group_output);
}
/* Internal API*/
void
ext_group_output_send_initial_state(struct lab_ext_workspace_group *group,
struct wl_resource *group_resource)
{
struct group_output *group_output;
struct wl_resource *output_resource;
struct wl_client *client = wl_resource_get_client(group_resource);
wl_list_for_each(group_output, &group->outputs, link) {
wl_resource_for_each(output_resource, &group_output->wlr_output->resources) {
if (wl_resource_get_client(output_resource) == client) {
ext_workspace_group_handle_v1_send_output_enter(
group_resource, output_resource);
}
}
}
}
/* Public API */
void
lab_ext_workspace_group_output_enter(struct lab_ext_workspace_group *group,
struct wlr_output *wlr_output)
{
struct group_output *group_output;
wl_list_for_each(group_output, &group->outputs, link) {
if (group_output->wlr_output == wlr_output) {
return;
}
}
group_output = znew(*group_output);
group_output->wlr_output = wlr_output;
group_output->group = group;
group_output->on.group_destroy.notify = handle_group_destroy;
wl_signal_add(&group->events.destroy, &group_output->on.group_destroy);
group_output->on.output_bind.notify = handle_output_bind;
wl_signal_add(&wlr_output->events.bind, &group_output->on.output_bind);
group_output->on.output_destroy.notify = handle_output_destroy;
wl_signal_add(&wlr_output->events.destroy, &group_output->on.output_destroy);
wl_list_insert(&group->outputs, &group_output->link);
group_output_send_event(
&group_output->group->resources,
&group_output->wlr_output->resources,
ext_workspace_group_handle_v1_send_output_enter);
ext_manager_schedule_done_event(group->manager);
}
void
lab_ext_workspace_group_output_leave(struct lab_ext_workspace_group *group,
struct wlr_output *wlr_output)
{
struct group_output *tmp;
struct group_output *group_output = NULL;
wl_list_for_each(tmp, &group->outputs, link) {
if (tmp->wlr_output == wlr_output) {
group_output = tmp;
break;
}
}
if (!group_output) {
wlr_log(WLR_ERROR, "output %s was never entered", wlr_output->name);
return;
}
group_output_destroy(group_output);
}

View file

@ -1,6 +0,0 @@
labwc_sources += files(
'transaction-addon.c',
)
subdir('cosmic_workspaces')
subdir('ext-workspace')

View file

@ -1,70 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
#include "protocols/transaction-addon.h"
#include <assert.h>
#include <wayland-server-core.h>
#include "common/list.h"
#include "common/mem.h"
void
lab_transaction_op_destroy(struct lab_transaction_op *trans_op)
{
wl_signal_emit_mutable(&trans_op->events.destroy, trans_op);
wl_list_remove(&trans_op->link);
free(trans_op);
}
static void
transaction_destroy(struct wl_list *list)
{
struct lab_transaction_op *trans_op, *trans_op_tmp;
wl_list_for_each_safe(trans_op, trans_op_tmp, list, link) {
lab_transaction_op_destroy(trans_op);
}
}
void
lab_resource_addon_destroy(struct lab_wl_resource_addon *addon)
{
assert(addon);
assert(addon->ctx);
addon->ctx->ref_count--;
assert(addon->ctx->ref_count >= 0);
if (!addon->ctx->ref_count) {
transaction_destroy(&addon->ctx->transaction_ops);
free(addon->ctx);
}
free(addon);
}
struct lab_wl_resource_addon *
lab_resource_addon_create(struct lab_transaction_session_context *ctx)
{
struct lab_wl_resource_addon *addon = znew(*addon);
if (!ctx) {
ctx = znew(*ctx);
wl_list_init(&ctx->transaction_ops);
}
addon->ctx = ctx;
addon->ctx->ref_count++;
return addon;
}
struct lab_transaction_op *
lab_transaction_op_add(struct lab_transaction_session_context *ctx,
uint32_t pending_change, void *src, void *data)
{
assert(ctx);
struct lab_transaction_op *trans_op = znew(*trans_op);
trans_op->change = pending_change;
trans_op->src = src;
trans_op->data = data;
wl_signal_init(&trans_op->events.destroy);
wl_list_append(&ctx->transaction_ops, &trans_op->link);
return trans_op;
}

View file

@ -2,7 +2,7 @@
#include <assert.h> #include <assert.h>
#include <stdbool.h> #include <stdbool.h>
#include <strings.h> #include <strings.h>
#include <wlr/backend/libinput.h> #include <wlr/config.h>
#include <wlr/types/wlr_cursor.h> #include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_input_device.h> #include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_keyboard.h> #include <wlr/types/wlr_keyboard.h>
@ -31,6 +31,12 @@
#include "session-lock.h" #include "session-lock.h"
#include "view.h" #include "view.h"
#if WLR_HAS_LIBINPUT_BACKEND
#include <wlr/backend/libinput.h>
#else
#define wlr_input_device_is_libinput(device) (false)
#endif
static void static void
input_device_destroy(struct wl_listener *listener, void *data) input_device_destroy(struct wl_listener *listener, void *data)
{ {
@ -48,6 +54,7 @@ input_device_destroy(struct wl_listener *listener, void *data)
free(input); free(input);
} }
#if WLR_HAS_LIBINPUT_BACKEND
static enum lab_libinput_device_type static enum lab_libinput_device_type
device_type_from_wlr_device(struct wlr_input_device *wlr_input_device) device_type_from_wlr_device(struct wlr_input_device *wlr_input_device)
{ {
@ -101,6 +108,7 @@ get_category(struct wlr_input_device *device)
/* Use default profile as a fallback */ /* Use default profile as a fallback */
return libinput_category_get_default(); return libinput_category_get_default();
} }
#endif
static void static void
configure_libinput(struct wlr_input_device *wlr_input_device) configure_libinput(struct wlr_input_device *wlr_input_device)
@ -135,7 +143,7 @@ configure_libinput(struct wlr_input_device *wlr_input_device)
input->scroll_factor = 1.0; input->scroll_factor = 1.0;
return; return;
} }
#if WLR_HAS_LIBINPUT_BACKEND
struct libinput_device *libinput_dev = struct libinput_device *libinput_dev =
wlr_libinput_get_device_handle(wlr_input_device); wlr_libinput_get_device_handle(wlr_input_device);
if (!libinput_dev) { if (!libinput_dev) {
@ -347,6 +355,7 @@ configure_libinput(struct wlr_input_device *wlr_input_device)
wlr_log(WLR_INFO, "scroll factor configured (%g)", dc->scroll_factor); wlr_log(WLR_INFO, "scroll factor configured (%g)", dc->scroll_factor);
input->scroll_factor = dc->scroll_factor; input->scroll_factor = dc->scroll_factor;
#endif
} }
static struct wlr_output * static struct wlr_output *

View file

@ -6,17 +6,18 @@
#include <sys/wait.h> #include <sys/wait.h>
#include <wlr/backend/headless.h> #include <wlr/backend/headless.h>
#include <wlr/backend/multi.h> #include <wlr/backend/multi.h>
#include <wlr/config.h>
#include <wlr/render/allocator.h> #include <wlr/render/allocator.h>
#include <wlr/types/wlr_alpha_modifier_v1.h> #include <wlr/types/wlr_alpha_modifier_v1.h>
#include <wlr/types/wlr_data_control_v1.h> #include <wlr/types/wlr_data_control_v1.h>
#include <wlr/types/wlr_data_device.h> #include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_drm.h> #include <wlr/types/wlr_drm.h>
#include <wlr/types/wlr_drm_lease_v1.h>
#include <wlr/types/wlr_export_dmabuf_v1.h> #include <wlr/types/wlr_export_dmabuf_v1.h>
#include <wlr/types/wlr_ext_data_control_v1.h> #include <wlr/types/wlr_ext_data_control_v1.h>
#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h> #include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>
#include <wlr/types/wlr_ext_image_capture_source_v1.h> #include <wlr/types/wlr_ext_image_capture_source_v1.h>
#include <wlr/types/wlr_ext_image_copy_capture_v1.h> #include <wlr/types/wlr_ext_image_copy_capture_v1.h>
#include <wlr/types/wlr_fixes.h>
#include <wlr/types/wlr_foreign_toplevel_management_v1.h> #include <wlr/types/wlr_foreign_toplevel_management_v1.h>
#include <wlr/types/wlr_fractional_scale_v1.h> #include <wlr/types/wlr_fractional_scale_v1.h>
#include <wlr/types/wlr_input_method_v2.h> #include <wlr/types/wlr_input_method_v2.h>
@ -40,8 +41,11 @@
#include <wlr/types/wlr_xdg_foreign_v2.h> #include <wlr/types/wlr_xdg_foreign_v2.h>
#if HAVE_XWAYLAND #if HAVE_XWAYLAND
#include <wlr/xwayland.h> #include <wlr/xwayland.h>
#include "xwayland-shell-v1-protocol.h" #endif
#if WLR_HAS_DRM_BACKEND
#include <wlr/types/wlr_drm_lease_v1.h>
#endif #endif
#include "action.h" #include "action.h"
@ -197,6 +201,7 @@ handle_sigchld(int signal, void *data)
return 0; return 0;
} }
#if WLR_HAS_DRM_BACKEND
static void static void
handle_drm_lease_request(struct wl_listener *listener, void *data) handle_drm_lease_request(struct wl_listener *listener, void *data)
{ {
@ -208,6 +213,7 @@ handle_drm_lease_request(struct wl_listener *listener, void *data)
return; return;
} }
} }
#endif
static bool static bool
protocol_is_privileged(const struct wl_interface *iface) protocol_is_privileged(const struct wl_interface *iface)
@ -226,7 +232,6 @@ protocol_is_privileged(const struct wl_interface *iface)
"zwlr_data_control_manager_v1", "zwlr_data_control_manager_v1",
"wp_security_context_manager_v1", "wp_security_context_manager_v1",
"ext_idle_notifier_v1", "ext_idle_notifier_v1",
"zcosmic_workspace_manager_v1",
"zwlr_foreign_toplevel_manager_v1", "zwlr_foreign_toplevel_manager_v1",
"ext_foreign_toplevel_list_v1", "ext_foreign_toplevel_list_v1",
"ext_session_lock_manager_v1", "ext_session_lock_manager_v1",
@ -260,6 +265,7 @@ allow_for_sandbox(const struct wlr_security_context_v1_state *security_state,
"wl_data_device_manager", /* would be great if we could drop this one */ "wl_data_device_manager", /* would be great if we could drop this one */
"wl_seat", "wl_seat",
"xdg_wm_base", "xdg_wm_base",
"wl_fixes",
/* enhanced */ /* enhanced */
"wl_output", "wl_output",
"wl_drm", "wl_drm",
@ -312,7 +318,11 @@ server_global_filter(const struct wl_client *client, const struct wl_global *glo
? server.xwayland->server->client ? server.xwayland->server->client
: NULL; : NULL;
if (client != xwayland_client && !strcmp(iface->name, xwayland_shell_v1_interface.name)) { /*
* The interface name `xwayland_shell_v1_interface.name` is hard-coded
* here to avoid generating and including `xwayland-shell-v1-protocol.h`
*/
if (client != xwayland_client && !strcmp(iface->name, "xwayland_shell_v1")) {
/* Filter out the xwayland shell for usual clients */ /* Filter out the xwayland shell for usual clients */
return false; return false;
} }
@ -438,6 +448,8 @@ server_init(void)
wl_display_set_global_filter(server.wl_display, server_global_filter, NULL); wl_display_set_global_filter(server.wl_display, server_global_filter, NULL);
server.wl_event_loop = wl_display_get_event_loop(server.wl_display); server.wl_event_loop = wl_display_get_event_loop(server.wl_display);
wlr_fixes_create(server.wl_display, 1);
/* Catch signals */ /* Catch signals */
server.sighup_source = wl_event_loop_add_signal( server.sighup_source = wl_event_loop_add_signal(
server.wl_event_loop, SIGHUP, handle_sighup, NULL); server.wl_event_loop, SIGHUP, handle_sighup, NULL);
@ -588,6 +600,8 @@ server_init(void)
server.workspace_tree = lab_wlr_scene_tree_create(&server.scene->tree); server.workspace_tree = lab_wlr_scene_tree_create(&server.scene->tree);
server.xdg_popup_tree = lab_wlr_scene_tree_create(&server.scene->tree); server.xdg_popup_tree = lab_wlr_scene_tree_create(&server.scene->tree);
#if HAVE_XWAYLAND #if HAVE_XWAYLAND
// Creating/setting this is harmless when xwayland support is built-in
// but xwayland could not be successfully started.
server.unmanaged_tree = lab_wlr_scene_tree_create(&server.scene->tree); server.unmanaged_tree = lab_wlr_scene_tree_create(&server.scene->tree);
#endif #endif
server.menu_tree = lab_wlr_scene_tree_create(&server.scene->tree); server.menu_tree = lab_wlr_scene_tree_create(&server.scene->tree);
@ -689,6 +703,7 @@ server_init(void)
session_lock_init(); session_lock_init();
#if WLR_HAS_DRM_BACKEND
server.drm_lease_manager = wlr_drm_lease_v1_manager_create( server.drm_lease_manager = wlr_drm_lease_v1_manager_create(
server.wl_display, server.backend); server.wl_display, server.backend);
if (server.drm_lease_manager) { if (server.drm_lease_manager) {
@ -699,6 +714,7 @@ server_init(void)
wlr_log(WLR_DEBUG, "Failed to create wlr_drm_lease_device_v1"); wlr_log(WLR_DEBUG, "Failed to create wlr_drm_lease_device_v1");
wlr_log(WLR_INFO, "VR will not be available"); wlr_log(WLR_INFO, "VR will not be available");
} }
#endif
server.output_power_manager_v1 = server.output_power_manager_v1 =
wlr_output_power_manager_v1_create(server.wl_display); wlr_output_power_manager_v1_create(server.wl_display);

View file

@ -54,6 +54,8 @@ view_from_wlr_surface(struct wlr_surface *surface)
return xdg_surface->data; return xdg_surface->data;
} }
#if HAVE_XWAYLAND #if HAVE_XWAYLAND
// Doing this is harmless even in the case that xwayland could not be
// successfully started.
struct wlr_xwayland_surface *xsurface = struct wlr_xwayland_surface *xsurface =
wlr_xwayland_surface_try_from_wlr_surface(surface); wlr_xwayland_surface_try_from_wlr_surface(surface);
if (xsurface) { if (xsurface) {

View file

@ -8,6 +8,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <strings.h> #include <strings.h>
#include <wlr/types/wlr_ext_workspace_v1.h>
#include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_scene.h> #include <wlr/types/wlr_scene.h>
#include "buffer.h" #include "buffer.h"
@ -21,13 +22,14 @@
#include "common/borderset.h" #include "common/borderset.h"
#include "labwc.h" #include "labwc.h"
#include "output.h" #include "output.h"
#include "protocols/cosmic-workspaces.h"
#include "protocols/ext-workspace.h"
#include "theme.h" #include "theme.h"
#include "view.h" #include "view.h"
<<<<<<< master
#define COSMIC_WORKSPACES_VERSION 1 #define COSMIC_WORKSPACES_VERSION 1
=======
>>>>>>> master
#define EXT_WORKSPACES_VERSION 1 #define EXT_WORKSPACES_VERSION 1
/* Internal helpers */ /* Internal helpers */
@ -268,22 +270,19 @@ workspace_find_by_name(const char *name)
return NULL; return NULL;
} }
/* cosmic workspace handlers */
static void static void
handle_cosmic_workspace_activate(struct wl_listener *listener, void *data) handle_ext_workspace_commit(struct wl_listener *listener, void *data)
{ {
struct workspace *workspace = wl_container_of(listener, workspace, on_cosmic.activate); struct wlr_ext_workspace_v1_commit_event *event = data;
workspaces_switch_to(workspace, /* update_focus */ true);
wlr_log(WLR_INFO, "cosmic activating workspace %s", workspace->name);
}
/* ext workspace handlers */ struct wlr_ext_workspace_v1_request *req;
static void wl_list_for_each(req, event->requests, link) {
handle_ext_workspace_activate(struct wl_listener *listener, void *data) if (req->type == WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE) {
{ struct workspace *workspace = req->activate.workspace->data;
struct workspace *workspace = wl_container_of(listener, workspace, on_ext.activate);
workspaces_switch_to(workspace, /* update_focus */ true); workspaces_switch_to(workspace, /* update_focus */ true);
wlr_log(WLR_INFO, "ext activating workspace %s", workspace->name); wlr_log(WLR_INFO, "activating workspace %s", workspace->name);
}
}
} }
/* Internal API */ /* Internal API */
@ -302,23 +301,13 @@ add_workspace(const char *name)
wl_list_append(&server.workspaces.all, &workspace->link); wl_list_append(&server.workspaces.all, &workspace->link);
wlr_scene_node_set_enabled(&workspace->tree->node, false); wlr_scene_node_set_enabled(&workspace->tree->node, false);
/* cosmic */ workspace->ext_workspace = wlr_ext_workspace_handle_v1_create(
workspace->cosmic_workspace = lab_cosmic_workspace_create(server.workspaces.cosmic_group); server.workspaces.ext_manager, /*id*/ NULL,
lab_cosmic_workspace_set_name(workspace->cosmic_workspace, name); EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ACTIVATE);
workspace->ext_workspace->data = workspace;
workspace->on_cosmic.activate.notify = handle_cosmic_workspace_activate; wlr_ext_workspace_handle_v1_set_group(
wl_signal_add(&workspace->cosmic_workspace->events.activate, workspace->ext_workspace, server.workspaces.ext_group);
&workspace->on_cosmic.activate); wlr_ext_workspace_handle_v1_set_name(workspace->ext_workspace, name);
/* ext */
workspace->ext_workspace = lab_ext_workspace_create(
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);
workspace->on_ext.activate.notify = handle_ext_workspace_activate;
wl_signal_add(&workspace->ext_workspace->events.activate,
&workspace->on_ext.activate);
} }
static struct workspace * static struct workspace *
@ -460,19 +449,15 @@ _osd_show(void)
void void
workspaces_init(void) workspaces_init(void)
{ {
server.workspaces.cosmic_manager = lab_cosmic_workspace_manager_create( server.workspaces.ext_manager = wlr_ext_workspace_manager_v1_create(
server.wl_display, /* capabilities */ CW_CAP_WS_ACTIVATE, server.wl_display, EXT_WORKSPACES_VERSION);
COSMIC_WORKSPACES_VERSION);
server.workspaces.ext_manager = lab_ext_workspace_manager_create( server.workspaces.ext_group = wlr_ext_workspace_group_handle_v1_create(
server.wl_display, /* capabilities */ WS_CAP_WS_ACTIVATE, server.workspaces.ext_manager, /*caps*/ 0);
EXT_WORKSPACES_VERSION);
server.workspaces.cosmic_group = lab_cosmic_workspace_group_create( server.workspaces.on_ext_manager.commit.notify = handle_ext_workspace_commit;
server.workspaces.cosmic_manager); wl_signal_add(&server.workspaces.ext_manager->events.commit,
&server.workspaces.on_ext_manager.commit);
server.workspaces.ext_group = lab_ext_workspace_group_create(
server.workspaces.ext_manager);
wl_list_init(&server.workspaces.all); wl_list_init(&server.workspaces.all);
@ -499,8 +484,7 @@ workspaces_init(void)
server.workspaces.current = initial; server.workspaces.current = initial;
wlr_scene_node_set_enabled(&initial->tree->node, true); wlr_scene_node_set_enabled(&initial->tree->node, true);
lab_cosmic_workspace_set_active(initial->cosmic_workspace, true); wlr_ext_workspace_handle_v1_set_active(initial->ext_workspace, true);
lab_ext_workspace_set_active(initial->ext_workspace, true);
} }
/* /*
@ -520,9 +504,7 @@ workspaces_switch_to(struct workspace *target, bool update_focus)
wlr_scene_node_set_enabled( wlr_scene_node_set_enabled(
&server.workspaces.current->tree->node, false); &server.workspaces.current->tree->node, false);
lab_cosmic_workspace_set_active( wlr_ext_workspace_handle_v1_set_active(
server.workspaces.current->cosmic_workspace, false);
lab_ext_workspace_set_active(
server.workspaces.current->ext_workspace, false); server.workspaces.current->ext_workspace, false);
/* /*
@ -574,8 +556,7 @@ workspaces_switch_to(struct workspace *target, bool update_focus)
/* Ensure that only currently visible fullscreen windows hide the top layer */ /* Ensure that only currently visible fullscreen windows hide the top layer */
desktop_update_top_layer_visibility(); desktop_update_top_layer_visibility();
lab_cosmic_workspace_set_active(target->cosmic_workspace, true); wlr_ext_workspace_handle_v1_set_active(target->ext_workspace, true);
lab_ext_workspace_set_active(target->ext_workspace, true);
} }
void void
@ -627,11 +608,8 @@ destroy_workspace(struct workspace *workspace)
wlr_scene_node_destroy(&workspace->tree->node); wlr_scene_node_destroy(&workspace->tree->node);
zfree(workspace->name); zfree(workspace->name);
wl_list_remove(&workspace->link); wl_list_remove(&workspace->link);
wl_list_remove(&workspace->on_cosmic.activate.link);
wl_list_remove(&workspace->on_ext.activate.link);
lab_cosmic_workspace_destroy(workspace->cosmic_workspace); wlr_ext_workspace_handle_v1_destroy(workspace->ext_workspace);
lab_ext_workspace_destroy(workspace->ext_workspace);
free(workspace); free(workspace);
} }
@ -664,9 +642,7 @@ workspaces_reconfigure(void)
wlr_log(WLR_DEBUG, "Renaming workspace \"%s\" to \"%s\"", wlr_log(WLR_DEBUG, "Renaming workspace \"%s\" to \"%s\"",
workspace->name, conf->name); workspace->name, conf->name);
xstrdup_replace(workspace->name, conf->name); xstrdup_replace(workspace->name, conf->name);
lab_cosmic_workspace_set_name( wlr_ext_workspace_handle_v1_set_name(
workspace->cosmic_workspace, workspace->name);
lab_ext_workspace_set_name(
workspace->ext_workspace, workspace->name); workspace->ext_workspace, workspace->name);
} }
workspace_link = workspace_link->next; workspace_link = workspace_link->next;
@ -716,4 +692,5 @@ workspaces_destroy(void)
destroy_workspace(workspace); destroy_workspace(workspace);
} }
assert(wl_list_empty(&server.workspaces.all)); assert(wl_list_empty(&server.workspaces.all));
wl_list_remove(&server.workspaces.on_ext_manager.commit.link);
} }

View file

@ -25,20 +25,6 @@
#include "window-rules.h" #include "window-rules.h"
#include "workspaces.h" #include "workspaces.h"
enum atoms {
ATOM_NET_WM_ICON = 0,
ATOM_COUNT,
};
static const char * const atom_names[] = {
[ATOM_NET_WM_ICON] = "_NET_WM_ICON",
};
static_assert(ARRAY_SIZE(atom_names) == ATOM_COUNT, "atom names out of sync");
static xcb_atom_t atoms[ATOM_COUNT] = {0};
static void set_surface(struct view *view, struct wlr_surface *surface); static void set_surface(struct view *view, struct wlr_surface *surface);
static void handle_map(struct wl_listener *listener, void *data); static void handle_map(struct wl_listener *listener, void *data);
static void handle_unmap(struct wl_listener *listener, void *data); static void handle_unmap(struct wl_listener *listener, void *data);
@ -360,11 +346,53 @@ handle_associate(struct wl_listener *listener, void *data)
{ {
struct xwayland_view *xwayland_view = struct xwayland_view *xwayland_view =
wl_container_of(listener, xwayland_view, associate); wl_container_of(listener, xwayland_view, associate);
assert(xwayland_view->xwayland_surface && struct view *view = &xwayland_view->base;
xwayland_view->xwayland_surface->surface); struct wlr_xwayland_surface *xsurface = xwayland_view->xwayland_surface;
assert(xsurface && xsurface->surface);
set_surface(&xwayland_view->base, set_surface(view, xsurface->surface);
xwayland_view->xwayland_surface->surface);
/*
* Empirically, the associate event is always seen just after
* map_request but before surface map. Window properties are
* also read by wlroots just before emitting associate. So after
* some trial and error, this seems to be the best place to set
* initial view states and compute initial placement.
*/
ensure_initial_geometry_and_output(view);
/*
* Per the Extended Window Manager Hints (EWMH) spec: "The Window
* Manager SHOULD honor _NET_WM_STATE whenever a withdrawn window
* requests to be mapped."
*
* The following order of operations is intended to reduce the
* number of resize (Configure) events:
* 1. set fullscreen state
* 2. set decorations (depends on fullscreen state)
* 3. set maximized (geometry depends on decorations)
*/
view_set_fullscreen(view, xsurface->fullscreen);
if (!view->been_mapped) {
if (want_deco(xsurface)) {
view_set_ssd_mode(view, LAB_SSD_MODE_FULL);
} else {
view_set_ssd_mode(view, LAB_SSD_MODE_NONE);
}
}
enum view_axis axis = VIEW_AXIS_NONE;
if (xsurface->maximized_horz) {
axis |= VIEW_AXIS_HORIZONTAL;
}
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);
}
} }
static void static void
@ -406,8 +434,8 @@ handle_destroy(struct wl_listener *listener, void *data)
wl_list_remove(&xwayland_view->set_override_redirect.link); wl_list_remove(&xwayland_view->set_override_redirect.link);
wl_list_remove(&xwayland_view->set_strut_partial.link); wl_list_remove(&xwayland_view->set_strut_partial.link);
wl_list_remove(&xwayland_view->set_window_type.link); wl_list_remove(&xwayland_view->set_window_type.link);
wl_list_remove(&xwayland_view->set_icon.link);
wl_list_remove(&xwayland_view->focus_in.link); 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); wl_list_remove(&xwayland_view->on_view.always_on_top.link);
@ -629,29 +657,20 @@ handle_set_strut_partial(struct wl_listener *listener, void *data)
} }
static void static void
update_icon(struct xwayland_view *xwayland_view) handle_set_icon(struct wl_listener *listener, void *data)
{ {
if (!xwayland_view->xwayland_surface) { struct xwayland_view *xwayland_view =
return; wl_container_of(listener, xwayland_view, set_icon);
}
xcb_window_t window_id = xwayland_view->xwayland_surface->window_id; xcb_ewmh_get_wm_icon_reply_t icon_reply = {0};
if (!wlr_xwayland_surface_fetch_icon(xwayland_view->xwayland_surface,
xcb_connection_t *xcb_conn = wlr_xwayland_get_xwm_connection(server.xwayland); &icon_reply)) {
xcb_get_property_cookie_t cookie = xcb_get_property(xcb_conn, 0,
window_id, atoms[ATOM_NET_WM_ICON], XCB_ATOM_CARDINAL, 0, 0x10000);
xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_conn, cookie, NULL);
if (!reply) {
return;
}
xcb_ewmh_get_wm_icon_reply_t icon;
if (!xcb_ewmh_get_wm_icon_from_reply(&icon, reply)) {
wlr_log(WLR_INFO, "Invalid x11 icon"); wlr_log(WLR_INFO, "Invalid x11 icon");
view_set_icon(&xwayland_view->base, NULL, NULL); view_set_icon(&xwayland_view->base, NULL, NULL);
goto out; goto out;
} }
xcb_ewmh_wm_icon_iterator_t iter = xcb_ewmh_get_wm_icon_iterator(&icon); xcb_ewmh_wm_icon_iterator_t iter = xcb_ewmh_get_wm_icon_iterator(&icon_reply);
struct wl_array buffers; struct wl_array buffers;
wl_array_init(&buffers); wl_array_init(&buffers);
for (; iter.rem; xcb_ewmh_get_wm_icon_next(&iter)) { for (; iter.rem; xcb_ewmh_get_wm_icon_next(&iter)) {
@ -681,7 +700,7 @@ update_icon(struct xwayland_view *xwayland_view)
wl_array_release(&buffers); wl_array_release(&buffers);
out: out:
free(reply); xcb_ewmh_get_wm_icon_reply_wipe(&icon_reply);
} }
static void static void
@ -715,63 +734,6 @@ handle_focus_in(struct wl_listener *listener, void *data)
} }
} }
/*
* Sets the initial geometry of maximized/fullscreen views before
* actually mapping them, so that they can do their initial layout and
* drawing with the correct geometry. This avoids visual glitches and
* also avoids undesired layout changes with some apps (e.g. HomeBank).
*/
static void
handle_map_request(struct wl_listener *listener, void *data)
{
struct xwayland_view *xwayland_view =
wl_container_of(listener, xwayland_view, map_request);
struct view *view = &xwayland_view->base;
struct wlr_xwayland_surface *xsurface = xwayland_view->xwayland_surface;
if (view->mapped) {
/* Probably shouldn't happen, but be sure */
return;
}
/* Keep the view invisible until actually mapped */
wlr_scene_node_set_enabled(&view->scene_tree->node, false);
ensure_initial_geometry_and_output(view);
/*
* Per the Extended Window Manager Hints (EWMH) spec: "The Window
* Manager SHOULD honor _NET_WM_STATE whenever a withdrawn window
* requests to be mapped."
*
* The following order of operations is intended to reduce the
* number of resize (Configure) events:
* 1. set fullscreen state
* 2. set decorations (depends on fullscreen state)
* 3. set maximized (geometry depends on decorations)
*/
view_set_fullscreen(view, xsurface->fullscreen);
if (!view->been_mapped) {
if (want_deco(xsurface)) {
view_set_ssd_mode(view, LAB_SSD_MODE_FULL);
} else {
view_set_ssd_mode(view, LAB_SSD_MODE_NONE);
}
}
enum view_axis axis = VIEW_AXIS_NONE;
if (xsurface->maximized_horz) {
axis |= VIEW_AXIS_HORIZONTAL;
}
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);
}
}
static void static void
check_natural_geometry(struct view *view) check_natural_geometry(struct view *view)
{ {
@ -815,14 +777,6 @@ handle_map(struct wl_listener *listener, void *data)
return; return;
} }
/*
* The map_request event may not be received when an unmanaged
* (override-redirect) surface becomes managed. To make sure we
* have valid geometry in that case, call handle_map_request()
* explicitly (calling it twice is harmless).
*/
handle_map_request(&xwayland_view->map_request, NULL);
view->mapped = true; view->mapped = true;
if (!view->content_tree) { if (!view->content_tree) {
@ -1051,6 +1005,7 @@ xwayland_view_create(struct wlr_xwayland_surface *xsurface, bool mapped)
view->workspace = server.workspaces.current; view->workspace = server.workspaces.current;
view->scene_tree = lab_wlr_scene_tree_create( view->scene_tree = lab_wlr_scene_tree_create(
view->workspace->view_trees[VIEW_LAYER_NORMAL]); view->workspace->view_trees[VIEW_LAYER_NORMAL]);
wlr_scene_node_set_enabled(&view->scene_tree->node, false);
node_descriptor_create(&view->scene_tree->node, node_descriptor_create(&view->scene_tree->node,
LAB_NODE_VIEW, view, /*data*/ NULL); LAB_NODE_VIEW, view, /*data*/ NULL);
@ -1074,8 +1029,8 @@ xwayland_view_create(struct wlr_xwayland_surface *xsurface, bool mapped)
CONNECT_SIGNAL(xsurface, xwayland_view, set_override_redirect); CONNECT_SIGNAL(xsurface, xwayland_view, set_override_redirect);
CONNECT_SIGNAL(xsurface, xwayland_view, set_strut_partial); CONNECT_SIGNAL(xsurface, xwayland_view, set_strut_partial);
CONNECT_SIGNAL(xsurface, xwayland_view, set_window_type); CONNECT_SIGNAL(xsurface, xwayland_view, set_window_type);
CONNECT_SIGNAL(xsurface, xwayland_view, set_icon);
CONNECT_SIGNAL(xsurface, xwayland_view, focus_in); CONNECT_SIGNAL(xsurface, xwayland_view, focus_in);
CONNECT_SIGNAL(xsurface, xwayland_view, map_request);
/* Events from the view itself */ /* Events from the view itself */
CONNECT_SIGNAL(view, &xwayland_view->on_view, always_on_top); CONNECT_SIGNAL(view, &xwayland_view->on_view, always_on_top);
@ -1088,13 +1043,10 @@ xwayland_view_create(struct wlr_xwayland_surface *xsurface, bool mapped)
/* /*
* If a surface is already associated, then we've * If a surface is already associated, then we've
* missed the various initial set_* events as well. * 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_title(&view->set_title, NULL);
handle_set_class(&xwayland_view->set_class, NULL); handle_set_class(&xwayland_view->set_class, NULL);
update_icon(xwayland_view); handle_set_icon(&xwayland_view->set_icon, NULL);
} }
if (mapped) { if (mapped) {
handle_map(&xwayland_view->base.mappable.map, NULL); handle_map(&xwayland_view->base.mappable.map, NULL);
@ -1117,91 +1069,11 @@ handle_new_surface(struct wl_listener *listener, void *data)
} }
} }
static struct xwayland_view *
xwayland_view_from_window_id(xcb_window_t id)
{
struct view *view;
wl_list_for_each(view, &server.views, link) {
if (view->type != LAB_XWAYLAND_VIEW) {
continue;
}
struct xwayland_view *xwayland_view = xwayland_view_from_view(view);
if (xwayland_view->xwayland_surface
&& xwayland_view->xwayland_surface->window_id == id) {
return xwayland_view;
}
}
return NULL;
}
#define XCB_EVENT_RESPONSE_TYPE_MASK 0x7f
static bool
handle_x11_event(struct wlr_xwayland *wlr_xwayland, xcb_generic_event_t *event)
{
switch (event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) {
case XCB_PROPERTY_NOTIFY: {
xcb_property_notify_event_t *ev = (void *)event;
if (ev->atom == atoms[ATOM_NET_WM_ICON]) {
struct xwayland_view *xwayland_view =
xwayland_view_from_window_id(ev->window);
if (xwayland_view) {
update_icon(xwayland_view);
} else {
wlr_log(WLR_DEBUG, "icon property changed for unknown window");
}
return true;
}
break;
}
default:
break;
}
return false;
}
static void
sync_atoms(void)
{
xcb_connection_t *xcb_conn =
wlr_xwayland_get_xwm_connection(server.xwayland);
assert(xcb_conn);
wlr_log(WLR_DEBUG, "Syncing X11 atoms");
xcb_intern_atom_cookie_t cookies[ATOM_COUNT];
/* First request everything and then loop over the results to reduce latency */
for (size_t i = 0; i < ATOM_COUNT; i++) {
cookies[i] = xcb_intern_atom(xcb_conn, 0,
strlen(atom_names[i]), atom_names[i]);
}
for (size_t i = 0; i < ATOM_COUNT; i++) {
xcb_generic_error_t *err = NULL;
xcb_intern_atom_reply_t *reply =
xcb_intern_atom_reply(xcb_conn, cookies[i], &err);
if (reply) {
atoms[i] = reply->atom;
wlr_log(WLR_DEBUG, "Got X11 atom for %s: %u",
atom_names[i], reply->atom);
}
if (err) {
atoms[i] = XCB_ATOM_NONE;
wlr_log(WLR_INFO, "Failed to get X11 atom for %s",
atom_names[i]);
}
free(reply);
free(err);
}
}
static void static void
handle_server_ready(struct wl_listener *listener, void *data) handle_server_ready(struct wl_listener *listener, void *data)
{ {
/* Fire an Xwayland startup script if one (or many) can be found */ /* Fire an Xwayland startup script if one (or many) can be found */
session_run_script("xinitrc"); session_run_script("xinitrc");
sync_atoms();
} }
static void static void
@ -1218,8 +1090,9 @@ xwayland_server_init(struct wlr_compositor *compositor)
wlr_xwayland_create(server.wl_display, wlr_xwayland_create(server.wl_display,
compositor, /* lazy */ !rc.xwayland_persistence); compositor, /* lazy */ !rc.xwayland_persistence);
if (!server.xwayland) { if (!server.xwayland) {
wlr_log(WLR_ERROR, "cannot create xwayland server"); wlr_log(WLR_ERROR, "failed to create xwayland server, continuing without");
exit(EXIT_FAILURE); unsetenv("DISPLAY");
return;
} }
server.xwayland_new_surface.notify = handle_new_surface; server.xwayland_new_surface.notify = handle_new_surface;
wl_signal_add(&server.xwayland->events.new_surface, wl_signal_add(&server.xwayland->events.new_surface,
@ -1233,8 +1106,6 @@ xwayland_server_init(struct wlr_compositor *compositor)
wl_signal_add(&server.xwayland->events.ready, wl_signal_add(&server.xwayland->events.ready,
&server.xwayland_xwm_ready); &server.xwayland_xwm_ready);
server.xwayland->user_event_handler = handle_x11_event;
if (setenv("DISPLAY", server.xwayland->display_name, true) < 0) { if (setenv("DISPLAY", server.xwayland->display_name, true) < 0) {
wlr_log_errno(WLR_ERROR, "unable to set DISPLAY for xwayland"); wlr_log_errno(WLR_ERROR, "unable to set DISPLAY for xwayland");
} else { } else {
@ -1247,60 +1118,11 @@ xwayland_server_init(struct wlr_compositor *compositor)
server.seat.xcursor_manager, XCURSOR_DEFAULT, 1); server.seat.xcursor_manager, XCURSOR_DEFAULT, 1);
if (xcursor) { if (xcursor) {
struct wlr_xcursor_image *image = xcursor->images[0]; struct wlr_xcursor_image *image = xcursor->images[0];
wlr_xwayland_set_cursor(server.xwayland, image->buffer, struct wlr_buffer *cursor_buffer = wlr_xcursor_image_get_buffer(image);
image->width * 4, image->width, if (cursor_buffer) {
image->height, image->hotspot_x, wlr_xwayland_set_cursor(server.xwayland, cursor_buffer,
image->hotspot_y); image->hotspot_x, image->hotspot_y);
} }
}
void
xwayland_reset_cursor(void)
{
/*
* As xwayland caches the pixel data when not yet started up
* due to the delayed lazy startup approach, we do have to
* re-set the xwayland cursor image. Otherwise the first X11
* client connected will cause the xwayland server to use
* the cached (and potentially destroyed) pixel data.
*
* Calling this function after reloading the cursor theme
* ensures that the cached pixel data keeps being valid.
*
* To reproduce:
* - Compile with b_sanitize=address,undefined
* - Start labwc (nothing in autostart that could create
* a X11 connection, e.g. no GTK or X11 application)
* - Reconfigure
* - Start some X11 client
*/
if (!server.xwayland) {
return;
}
struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor(
server.seat.xcursor_manager, XCURSOR_DEFAULT, 1);
if (xcursor && !server.xwayland->xwm) {
/* Prevents setting the cursor on an active xwayland server */
struct wlr_xcursor_image *image = xcursor->images[0];
wlr_xwayland_set_cursor(server.xwayland, image->buffer,
image->width * 4, image->width,
image->height, image->hotspot_x,
image->hotspot_y);
return;
}
if (server.xwayland->cursor) {
/*
* The previous configured theme has set the
* default cursor or the xwayland server is
* currently running but still has a cached
* xcursor set that will be used on the next
* xwayland destroy -> lazy startup cycle.
*/
zfree(server.xwayland->cursor);
} }
} }
@ -1308,6 +1130,11 @@ void
xwayland_server_finish(void) xwayland_server_finish(void)
{ {
struct wlr_xwayland *xwayland = server.xwayland; struct wlr_xwayland *xwayland = server.xwayland;
if (!xwayland) {
return;
}
wl_list_remove(&server.xwayland_new_surface.link); wl_list_remove(&server.xwayland_new_surface.link);
wl_list_remove(&server.xwayland_server_ready.link); wl_list_remove(&server.xwayland_server_ready.link);
wl_list_remove(&server.xwayland_xwm_ready.link); wl_list_remove(&server.xwayland_xwm_ready.link);

View file

@ -1,7 +1,7 @@
[wrap-git] [wrap-git]
url = https://gitlab.freedesktop.org/wlroots/wlroots.git url = https://gitlab.freedesktop.org/wlroots/wlroots.git
revision = 0.19 revision = 0.20
[provide] [provide]
dependency_names = wlroots-0.19 dependency_names = wlroots-0.20
wlroots-0.19=wlroots wlroots-0.20=wlroots