From 4e25347791e01572d5c6fb8ddfa17fea0c8a3921 Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Fri, 6 Mar 2026 20:18:40 +0200 Subject: [PATCH 01/28] use X macros to fill up enum action_type and const char *action_names[] To make it harder to have these 2 data structures go out of sync. No functional change. To understand X macros better, visit: - https://en.wikipedia.org/wiki/X_macro - https://philliptrudeau.com/blog/x-macro --- src/action.c | 229 +++++++++++++++++++++------------------------------ 1 file changed, 96 insertions(+), 133 deletions(-) diff --git a/src/action.c b/src/action.c index 2a0996e3..66fa9b20 100644 --- a/src/action.c +++ b/src/action.c @@ -68,147 +68,110 @@ struct action_arg_list { 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 { ACTION_TYPE_INVALID = 0, - ACTION_TYPE_NONE, - ACTION_TYPE_CLOSE, - ACTION_TYPE_KILL, - 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, +#define X(name, str) ACTION_TYPE_##name, + ACTION_TYPE_LIST(X) +#undef X }; -/* 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[] = { - "INVALID", - "None", - "Close", - "Kill", - "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", + [ACTION_TYPE_INVALID] = "INVALID", +#define X(name, str)[ACTION_TYPE_##name] = str, + ACTION_TYPE_LIST(X) +#undef X NULL }; +#undef ACTION_TYPE_LIST + void action_arg_add_str(struct action *action, const char *key, const char *value) { From fee38bceca0a16fb1685cfe83838d060582f3b6c Mon Sep 17 00:00:00 2001 From: Daniel Lublin Date: Wed, 25 Feb 2026 10:40:39 +0100 Subject: [PATCH 02/28] Handle xwayland not started (when HAVE_XWAYLAND, but lacking the binary) Closes https://github.com/labwc/labwc/issues/434 --- src/config/session.c | 35 ++++++++++++++++++++++++----------- src/input/cursor.c | 2 +- src/server.c | 2 ++ src/view.c | 2 ++ src/xwayland.c | 10 ++++++++-- 5 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/config/session.c b/src/config/session.c index 9a05d255..3ba7913b 100644 --- a/src/config/session.c +++ b/src/config/session.c @@ -21,7 +21,6 @@ #include "labwc.h" static const char *const env_vars[] = { - "DISPLAY", "WAYLAND_DISPLAY", "XDG_CURRENT_DESKTOP", "XCURSOR_SIZE", @@ -208,6 +207,21 @@ should_update_activation(void) 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 update_activation_env(bool initialize) { @@ -227,19 +241,18 @@ update_activation_env(bool initialize) char *env_keys = str_join(env_vars, "%s", " "); char *env_unset_keys = initialize ? NULL : str_join(env_vars, "%s=", " "); - 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); + execute_update(env_keys, env_unset_keys, initialize); free(env_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 diff --git a/src/input/cursor.c b/src/input/cursor.c index a7cba87d..315898b7 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -1166,7 +1166,7 @@ cursor_process_button_press(struct seat *seat, uint32_t button, uint32_t time_ms if (layer && layer->current.keyboard_interactive) { layer_try_set_focus(seat, layer); } -#ifdef HAVE_XWAYLAND +#if HAVE_XWAYLAND } else if (ctx.type == LAB_NODE_UNMANAGED) { desktop_focus_view_or_surface(seat, NULL, ctx.surface, /*raise*/ false); diff --git a/src/server.c b/src/server.c index c5ca9fff..2e3ece02 100644 --- a/src/server.c +++ b/src/server.c @@ -583,6 +583,8 @@ server_init(void) server.workspace_tree = lab_wlr_scene_tree_create(&server.scene->tree); server.xdg_popup_tree = lab_wlr_scene_tree_create(&server.scene->tree); #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); #endif server.menu_tree = lab_wlr_scene_tree_create(&server.scene->tree); diff --git a/src/view.c b/src/view.c index c44a2a6d..1d43fcfa 100644 --- a/src/view.c +++ b/src/view.c @@ -54,6 +54,8 @@ view_from_wlr_surface(struct wlr_surface *surface) return xdg_surface->data; } #if HAVE_XWAYLAND + // Doing this is harmless even in the case that xwayland could not be + // successfully started. struct wlr_xwayland_surface *xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface); if (xsurface) { diff --git a/src/xwayland.c b/src/xwayland.c index 7e033dfc..cfe7d691 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -1218,8 +1218,9 @@ xwayland_server_init(struct wlr_compositor *compositor) wlr_xwayland_create(server.wl_display, compositor, /* lazy */ !rc.xwayland_persistence); if (!server.xwayland) { - wlr_log(WLR_ERROR, "cannot create xwayland server"); - exit(EXIT_FAILURE); + wlr_log(WLR_ERROR, "failed to create xwayland server, continuing without"); + unsetenv("DISPLAY"); + return; } server.xwayland_new_surface.notify = handle_new_surface; wl_signal_add(&server.xwayland->events.new_surface, @@ -1308,6 +1309,11 @@ void xwayland_server_finish(void) { struct wlr_xwayland *xwayland = server.xwayland; + + if (!xwayland) { + return; + } + wl_list_remove(&server.xwayland_new_surface.link); wl_list_remove(&server.xwayland_server_ready.link); wl_list_remove(&server.xwayland_xwm_ready.link); From 3ce6328f6d18e5035f378b65bbf88217d9d3af26 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Fri, 27 Mar 2026 22:10:07 -0400 Subject: [PATCH 03/28] xwayland: set initial view state + geometry in handle_associate() 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. Fixes initial positioning of "urxvt -g -0-0". urxvt placement regressed in: 9903331995b4de7c99fb2c40c90ea9980ae70ad7 ("xwayland: rework setting initial geometry from surface") map_request handler was added ~2 years ago in: c9018da4c4d9539341e4592caa48e94a5e47c6ea ("xwayland: set initial geometry in map_request handler") I'm not sure if the map_request handler was incorrect from day one, or if something changed in wlroots and/or Xwayland since then. --- include/xwayland.h | 1 - src/xwayland.c | 118 ++++++++++++++++++--------------------------- 2 files changed, 47 insertions(+), 72 deletions(-) diff --git a/include/xwayland.h b/include/xwayland.h index 40fb17bc..69833d06 100644 --- a/include/xwayland.h +++ b/include/xwayland.h @@ -54,7 +54,6 @@ struct xwayland_view { struct wl_listener set_strut_partial; struct wl_listener set_window_type; struct wl_listener focus_in; - struct wl_listener map_request; /* Not (yet) implemented */ /* struct wl_listener set_role; */ diff --git a/src/xwayland.c b/src/xwayland.c index cfe7d691..ff31a7f1 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -360,11 +360,53 @@ handle_associate(struct wl_listener *listener, void *data) { struct xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, associate); - assert(xwayland_view->xwayland_surface && - xwayland_view->xwayland_surface->surface); + struct view *view = &xwayland_view->base; + struct wlr_xwayland_surface *xsurface = xwayland_view->xwayland_surface; + assert(xsurface && xsurface->surface); - set_surface(&xwayland_view->base, - xwayland_view->xwayland_surface->surface); + set_surface(view, xsurface->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 @@ -407,7 +449,6 @@ handle_destroy(struct wl_listener *listener, void *data) wl_list_remove(&xwayland_view->set_strut_partial.link); wl_list_remove(&xwayland_view->set_window_type.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); @@ -715,63 +756,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 check_natural_geometry(struct view *view) { @@ -815,14 +799,6 @@ handle_map(struct wl_listener *listener, void *data) 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; if (!view->content_tree) { @@ -1051,6 +1027,7 @@ xwayland_view_create(struct wlr_xwayland_surface *xsurface, bool mapped) view->workspace = server.workspaces.current; view->scene_tree = lab_wlr_scene_tree_create( view->workspace->view_trees[VIEW_LAYER_NORMAL]); + wlr_scene_node_set_enabled(&view->scene_tree->node, false); node_descriptor_create(&view->scene_tree->node, LAB_NODE_VIEW, view, /*data*/ NULL); @@ -1075,7 +1052,6 @@ xwayland_view_create(struct wlr_xwayland_surface *xsurface, bool mapped) CONNECT_SIGNAL(xsurface, xwayland_view, set_strut_partial); CONNECT_SIGNAL(xsurface, xwayland_view, set_window_type); CONNECT_SIGNAL(xsurface, xwayland_view, focus_in); - CONNECT_SIGNAL(xsurface, xwayland_view, map_request); /* Events from the view itself */ CONNECT_SIGNAL(view, &xwayland_view->on_view, always_on_top); From ea4e7e9a02e9e940d6ca12da7a04363a43d84f72 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Sat, 5 Jul 2025 18:10:07 +0200 Subject: [PATCH 04/28] CI: allow compiling wlroots as subproject --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ba7406cf..2308e80b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -95,6 +95,7 @@ jobs: apt-get install -y git gcc clang gdb xwayland apt-get build-dep -y labwc apt-get build-dep -y libwlroots-0.19-dev + apt-get build-dep -y libxkbcommon-dev - name: Install FreeBSD dependencies if: matrix.name == 'FreeBSD' @@ -121,7 +122,7 @@ jobs: xbps-install -y git meson gcc clang pkg-config scdoc \ cairo-devel glib-devel libpng-devel librsvg-devel libxml2-devel \ 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 - name: Build with gcc From 48bcccddc3a0f44fb514f32edc354313cda0cb7f Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Sat, 5 Jul 2025 16:59:41 +0200 Subject: [PATCH 05/28] chase wlroots: increase wlroots meson dep --- meson.build | 4 ++-- subprojects/wlroots.wrap | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/meson.build b/meson.build index ddb3d42b..4b622b5e 100644 --- a/meson.build +++ b/meson.build @@ -51,9 +51,9 @@ endif add_project_arguments('-DLABWC_VERSION=@0@'.format(version), language: 'c') wlroots = dependency( - 'wlroots-0.19', + 'wlroots-0.20', 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' diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap index 25a947ed..c1d52098 100644 --- a/subprojects/wlroots.wrap +++ b/subprojects/wlroots.wrap @@ -1,7 +1,7 @@ [wrap-git] url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = 0.19 +revision = f04ef79f619983bfb4a7c9908bdae62e0d0d5ba7 [provide] -dependency_names = wlroots-0.19 -wlroots-0.19=wlroots +dependency_names = wlroots-0.20 +wlroots-0.20=wlroots From 0c24252c85ed474cff77d508bc39a480b9993a5f Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Sat, 5 Jul 2025 17:03:38 +0200 Subject: [PATCH 06/28] chase wlroots: ime: rename to new_text_input (MR 5032) Ref: 536100488fc4c64528786801860f96cfa1a55765 (text-input-v3: Name new text input event correctly) --- src/input/ime.c | 2 +- subprojects/wlroots.wrap | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/input/ime.c b/src/input/ime.c index b55e816c..f622c639 100644 --- a/src/input/ime.c +++ b/src/input/ime.c @@ -585,7 +585,7 @@ input_method_relay_create(struct seat *seat) relay->popup_tree = lab_wlr_scene_tree_create(&server.scene->tree); 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_input_method.notify = handle_new_input_method; diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap index c1d52098..9199c211 100644 --- a/subprojects/wlroots.wrap +++ b/subprojects/wlroots.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = f04ef79f619983bfb4a7c9908bdae62e0d0d5ba7 +revision = 536100488fc4c64528786801860f96cfa1a55765 [provide] dependency_names = wlroots-0.20 From 511ed69c531bb5bddfe2a2e97586aab9f85ea300 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Wed, 13 Aug 2025 19:57:58 -0700 Subject: [PATCH 07/28] chase wlroots: add wl_fixes interface (MR 4685) Ref: 812675ba34ce612e9294e8a9814b1baf4b4775d4 (fixes: add implementation) --- src/server.c | 4 ++++ subprojects/wlroots.wrap | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/server.c b/src/server.c index 2e3ece02..727a8f08 100644 --- a/src/server.c +++ b/src/server.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -255,6 +256,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_seat", "xdg_wm_base", + "wl_fixes", /* enhanced */ "wl_output", "wl_drm", @@ -433,6 +435,8 @@ server_init(void) wl_display_set_global_filter(server.wl_display, server_global_filter, NULL); server.wl_event_loop = wl_display_get_event_loop(server.wl_display); + wlr_fixes_create(server.wl_display, 1); + /* Catch signals */ server.sighup_source = wl_event_loop_add_signal( server.wl_event_loop, SIGHUP, handle_sighup, NULL); diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap index 9199c211..d1791ee0 100644 --- a/subprojects/wlroots.wrap +++ b/subprojects/wlroots.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = 536100488fc4c64528786801860f96cfa1a55765 +revision = 812675ba34ce612e9294e8a9814b1baf4b4775d4 [provide] dependency_names = wlroots-0.20 From 5f8a6395a1b26b5d361092a165a98df595920d95 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Tue, 9 Sep 2025 14:58:03 +0200 Subject: [PATCH 08/28] chase wlroots: ime: rename to new_input_method (MR 5107) Ref: 06aacb2a6fd237a5e1062d611909432bbcf5b566 (input-method: rename input_method event to new_input_method) --- src/input/ime.c | 2 +- subprojects/wlroots.wrap | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/input/ime.c b/src/input/ime.c index f622c639..954e2ea0 100644 --- a/src/input/ime.c +++ b/src/input/ime.c @@ -589,7 +589,7 @@ input_method_relay_create(struct seat *seat) &relay->new_text_input); 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->focused_surface_destroy.notify = handle_focused_surface_destroy; diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap index d1791ee0..253b83d7 100644 --- a/subprojects/wlroots.wrap +++ b/subprojects/wlroots.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = 812675ba34ce612e9294e8a9814b1baf4b4775d4 +revision = 06aacb2a6fd237a5e1062d611909432bbcf5b566 [provide] dependency_names = wlroots-0.20 From 863863fc7cb43bab9fc528d4591e0b188934b765 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 18 Oct 2025 16:32:20 +0900 Subject: [PATCH 09/28] chase wlroots: ime: don't use `data` in kb grab destroy handler (MR 5170) Ref: 06275103f249cd2954630e59383342e102a6c1a3 (input-method-v2: Destroy keyboard grab before input method) Background: My MR in wlroots (!5107) stopped emitting `wlr_input_method_v2` on its `commit`/`destroy` events, but didn't stop emitting `wlr_input_method_keyboard_grab_v2` on its `destroy` event. That was because `handle_keyboard_grab_destroy()` was called *after* `handle_input_method_destroy()` for some reason, which caused segfault when dereferencing `relay->input_method.keyboard_grab` in `handle_keyboard_grab_destroy()`. MR 5170 reversed this weired order of destroy handler calls, and finally stopped emitting `wlr_input_method_keyboard_grab_v2` on its `destroy` event. --- src/input/ime.c | 3 ++- subprojects/wlroots.wrap | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/input/ime.c b/src/input/ime.c index 954e2ea0..1758d1c0 100644 --- a/src/input/ime.c +++ b/src/input/ime.c @@ -309,7 +309,8 @@ handle_keyboard_grab_destroy(struct wl_listener *listener, void *data) { struct input_method_relay *relay = 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); wl_list_remove(&relay->keyboard_grab_destroy.link); diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap index 253b83d7..18e5ef98 100644 --- a/subprojects/wlroots.wrap +++ b/subprojects/wlroots.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = 06aacb2a6fd237a5e1062d611909432bbcf5b566 +revision = 06275103f249cd2954630e59383342e102a6c1a3 [provide] dependency_names = wlroots-0.20 From 76f166ae42eeb875da0b8a00ec269c89eb05ec71 Mon Sep 17 00:00:00 2001 From: Manuel Barrio Linares Date: Thu, 8 Jan 2026 15:15:22 -0300 Subject: [PATCH 10/28] chase wlroots: wlr_xwayland_set_cursor now takes a wlr_buffer (MR 5230) Ref: 84d603acc06a45dd3c3a4b2cf1fd08b2933ca2b5 (xwayland: take wlr_buffer in wlr_xwayland_set_cursor()) Ref: 6ae54dca23064e897b393283887986e5719a747f (xwayland: lock new buffer instead of the old one) Co-Authored-By: Consolatis This wlroots change fixes a potential UAF which we dealt with in labwc. We can thus remove the workaround completely. --- include/xwayland.h | 2 -- src/input/cursor.c | 3 -- src/xwayland.c | 59 ++++------------------------------------ subprojects/wlroots.wrap | 2 +- 4 files changed, 6 insertions(+), 60 deletions(-) diff --git a/include/xwayland.h b/include/xwayland.h index 69833d06..2be7d934 100644 --- a/include/xwayland.h +++ b/include/xwayland.h @@ -79,8 +79,6 @@ void xwayland_adjust_usable_area(struct view *view, void xwayland_update_workarea(void); -void xwayland_reset_cursor(void); - void xwayland_flush(void); #endif /* HAVE_XWAYLAND */ diff --git a/src/input/cursor.c b/src/input/cursor.c index 315898b7..3985e9d6 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -1604,9 +1604,6 @@ void cursor_reload(struct seat *seat) { cursor_load(seat); -#if HAVE_XWAYLAND - xwayland_reset_cursor(); -#endif cursor_update_image(seat); } diff --git a/src/xwayland.c b/src/xwayland.c index ff31a7f1..263833ff 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -1224,60 +1224,11 @@ xwayland_server_init(struct wlr_compositor *compositor) server.seat.xcursor_manager, XCURSOR_DEFAULT, 1); if (xcursor) { 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); - } -} - -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); + struct wlr_buffer *cursor_buffer = wlr_xcursor_image_get_buffer(image); + if (cursor_buffer) { + wlr_xwayland_set_cursor(server.xwayland, cursor_buffer, + image->hotspot_x, image->hotspot_y); + } } } diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap index 18e5ef98..004e3a20 100644 --- a/subprojects/wlroots.wrap +++ b/subprojects/wlroots.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = 06275103f249cd2954630e59383342e102a6c1a3 +revision = 6ae54dca23064e897b393283887986e5719a747f [provide] dependency_names = wlroots-0.20 From c3fc78286a7e21c554aafe6d844c85f5035e333a Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Sat, 5 Jul 2025 17:37:22 +0200 Subject: [PATCH 11/28] chase wlroots: track 0.20 branch And move CI to use system wide wlroots deps where availble. --- .github/workflows/build.yml | 4 ++-- subprojects/wlroots.wrap | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2308e80b..b693d54d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -82,7 +82,7 @@ jobs: run: | pacman-key --init 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 \ libdisplay-info gdb ttf-dejavu foot libsfdo cmocka @@ -107,7 +107,7 @@ jobs: pkg set -yn pkg:mesa-dri # hack to skip llvm dependency pkg install -y git pcre2 meson gcc pkgconf cairo pango evdev-proto \ hwdata wayland-protocols libdisplay-info libepoll-shim \ - wlroots019 + wlroots020 run: echo "setup done" - name: Install Void Linux dependencies diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap index 004e3a20..eb797ad5 100644 --- a/subprojects/wlroots.wrap +++ b/subprojects/wlroots.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = 6ae54dca23064e897b393283887986e5719a747f +revision = 0.20 [provide] dependency_names = wlroots-0.20 From 647e9b7658c916d2c8c5a16ea60d608e9b071097 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Fri, 18 Jul 2025 18:47:39 +0900 Subject: [PATCH 12/28] Drop cosmic-workspace and use wlroots implementation of ext-workspace-v1 --- include/labwc.h | 9 +- .../protocols/cosmic-workspaces-internal.h | 24 - include/protocols/cosmic-workspaces.h | 94 --- include/protocols/ext-workspace-internal.h | 25 - include/protocols/ext-workspace.h | 109 --- include/protocols/transaction-addon.h | 84 -- include/workspaces.h | 15 +- protocols/cosmic-workspace-unstable-v1.xml | 364 --------- protocols/meson.build | 1 - src/meson.build | 1 - src/output.c | 11 +- .../cosmic_workspaces/cosmic-workspaces.c | 717 ---------------- src/protocols/cosmic_workspaces/meson.build | 4 - src/protocols/cosmic_workspaces/output.c | 174 ---- src/protocols/ext-workspace/ext-workspace.c | 762 ------------------ src/protocols/ext-workspace/meson.build | 4 - src/protocols/ext-workspace/output.c | 174 ---- src/protocols/meson.build | 6 - src/protocols/transaction-addon.c | 70 -- src/server.c | 1 - src/workspaces.c | 89 +- 21 files changed, 40 insertions(+), 2698 deletions(-) delete mode 100644 include/protocols/cosmic-workspaces-internal.h delete mode 100644 include/protocols/cosmic-workspaces.h delete mode 100644 include/protocols/ext-workspace-internal.h delete mode 100644 include/protocols/ext-workspace.h delete mode 100644 include/protocols/transaction-addon.h delete mode 100644 protocols/cosmic-workspace-unstable-v1.xml delete mode 100644 src/protocols/cosmic_workspaces/cosmic-workspaces.c delete mode 100644 src/protocols/cosmic_workspaces/meson.build delete mode 100644 src/protocols/cosmic_workspaces/output.c delete mode 100644 src/protocols/ext-workspace/ext-workspace.c delete mode 100644 src/protocols/ext-workspace/meson.build delete mode 100644 src/protocols/ext-workspace/output.c delete mode 100644 src/protocols/meson.build delete mode 100644 src/protocols/transaction-addon.c diff --git a/include/labwc.h b/include/labwc.h index 8b2cdf51..c579b06e 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -243,13 +243,14 @@ struct server { struct wl_list all; /* struct workspace.link */ struct workspace *current; struct workspace *last; - struct lab_cosmic_workspace_manager *cosmic_manager; - struct lab_cosmic_workspace_group *cosmic_group; - struct lab_ext_workspace_manager *ext_manager; - struct lab_ext_workspace_group *ext_group; + struct wlr_ext_workspace_manager_v1 *ext_manager; + struct wlr_ext_workspace_group_handle_v1 *ext_group; struct { struct wl_listener layout_output_added; } on; + struct { + struct wl_listener commit; + } on_ext_manager; } workspaces; struct wl_list outputs; diff --git a/include/protocols/cosmic-workspaces-internal.h b/include/protocols/cosmic-workspaces-internal.h deleted file mode 100644 index 50c7e75b..00000000 --- a/include/protocols/cosmic-workspaces-internal.h +++ /dev/null @@ -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 */ diff --git a/include/protocols/cosmic-workspaces.h b/include/protocols/cosmic-workspaces.h deleted file mode 100644 index 8776bfad..00000000 --- a/include/protocols/cosmic-workspaces.h +++ /dev/null @@ -1,94 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef LABWC_PROTOCOLS_COSMIC_WORKSPACES_H -#define LABWC_PROTOCOLS_COSMIC_WORKSPACES_H - -#include -#include - -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 */ diff --git a/include/protocols/ext-workspace-internal.h b/include/protocols/ext-workspace-internal.h deleted file mode 100644 index c5e7575f..00000000 --- a/include/protocols/ext-workspace-internal.h +++ /dev/null @@ -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 */ diff --git a/include/protocols/ext-workspace.h b/include/protocols/ext-workspace.h deleted file mode 100644 index cc422ac5..00000000 --- a/include/protocols/ext-workspace.h +++ /dev/null @@ -1,109 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef LABWC_PROTOCOLS_EXT_WORKSPACES_H -#define LABWC_PROTOCOLS_EXT_WORKSPACES_H - -#include -#include - -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 */ diff --git a/include/protocols/transaction-addon.h b/include/protocols/transaction-addon.h deleted file mode 100644 index 2c40b3dd..00000000 --- a/include/protocols/transaction-addon.h +++ /dev/null @@ -1,84 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef LABWC_PROTOCOLS_TRANSACTION_ADDON_H -#define LABWC_PROTOCOLS_TRANSACTION_ADDON_H - -#include - -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 */ diff --git a/include/workspaces.h b/include/workspaces.h index 5c0f4e7a..3a5c6d06 100644 --- a/include/workspaces.h +++ b/include/workspaces.h @@ -17,20 +17,7 @@ struct workspace { struct wlr_scene_tree *tree; struct wlr_scene_tree *view_trees[3]; - struct lab_cosmic_workspace *cosmic_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; + struct wlr_ext_workspace_handle_v1 *ext_workspace; }; void workspaces_init(void); diff --git a/protocols/cosmic-workspace-unstable-v1.xml b/protocols/cosmic-workspace-unstable-v1.xml deleted file mode 100644 index 76adedd9..00000000 --- a/protocols/cosmic-workspace-unstable-v1.xml +++ /dev/null @@ -1,364 +0,0 @@ - - - - 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. - - - - - 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. - - - - - 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. - - - - - - - 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. - - - - - - 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. - - - - - - 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. - - - - - - 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. - - - - - - - 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. - - - - - - - - - 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. - - - - - - - This event is emitted whenever an output is assigned to the workspace - group. - - - - - - - This event is emitted whenever an output is removed from the 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. - - - - - - - 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. - - - - - - 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. - - - - - - - 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. - - - - - - - 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). - - - - - This event is emitted immediately after the zcosmic_workspace_handle_v1 is - created and whenever the name of the workspace changes. - - - - - - - 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. - - - - - - - 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. - - - - - - - The different states that a workspace can have. - - - - - - - The workspace is not visible in its workspace group, and clients - attempting to visualize the compositor workspace state should not - display such workspaces. - - - - - - - - - - - - - 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. - - - - - - - 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. - - - - - - 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. - - - - - - 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. - - - - - - Request that this workspace be deactivated. - - There is no guarantee the workspace will be actually deactivated. - - - - - - Request that this workspace be removed. - - There is no guarantee the workspace will be actually removed. - - - - diff --git a/protocols/meson.build b/protocols/meson.build index 67c0d3d2..f360210e 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -25,7 +25,6 @@ server_protocols = [ 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-output-power-management-unstable-v1.xml', ] diff --git a/src/meson.build b/src/meson.build index a9afdc4f..40ec3170 100644 --- a/src/meson.build +++ b/src/meson.build @@ -55,6 +55,5 @@ subdir('foreign-toplevel') subdir('img') subdir('input') subdir('menu') -subdir('protocols') subdir('scaled-buffer') subdir('ssd') diff --git a/src/output.c b/src/output.c index 1fd0a03d..49544baa 100644 --- a/src/output.c +++ b/src/output.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -32,8 +33,6 @@ #include "node.h" #include "output-state.h" #include "output-virtual.h" -#include "protocols/cosmic-workspaces.h" -#include "protocols/ext-workspace.h" #include "regions.h" #include "session-lock.h" #include "view.h" @@ -285,9 +284,7 @@ add_output_to_layout(struct output *output) layout_output, output->scene_output); } - lab_cosmic_workspace_group_output_enter( - server.workspaces.cosmic_group, output->wlr_output); - lab_ext_workspace_group_output_enter( + wlr_ext_workspace_group_handle_v1_output_enter( server.workspaces.ext_group, output->wlr_output); /* (Re-)create regions from config */ @@ -729,9 +726,7 @@ output_config_apply(struct wlr_output_configuration_v1 *config) } else if (was_in_layout) { regions_evacuate_output(output); - lab_cosmic_workspace_group_output_leave( - server.workspaces.cosmic_group, output->wlr_output); - lab_ext_workspace_group_output_leave( + wlr_ext_workspace_group_handle_v1_output_leave( server.workspaces.ext_group, output->wlr_output); /* diff --git a/src/protocols/cosmic_workspaces/cosmic-workspaces.c b/src/protocols/cosmic_workspaces/cosmic-workspaces.c deleted file mode 100644 index d12611cb..00000000 --- a/src/protocols/cosmic_workspaces/cosmic-workspaces.c +++ /dev/null @@ -1,717 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include -#include -#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); -} diff --git a/src/protocols/cosmic_workspaces/meson.build b/src/protocols/cosmic_workspaces/meson.build deleted file mode 100644 index 31ce18b8..00000000 --- a/src/protocols/cosmic_workspaces/meson.build +++ /dev/null @@ -1,4 +0,0 @@ -labwc_sources += files( - 'cosmic-workspaces.c', - 'output.c', -) diff --git a/src/protocols/cosmic_workspaces/output.c b/src/protocols/cosmic_workspaces/output.c deleted file mode 100644 index 1e170e85..00000000 --- a/src/protocols/cosmic_workspaces/output.c +++ /dev/null @@ -1,174 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include -#include -#include -#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); -} diff --git a/src/protocols/ext-workspace/ext-workspace.c b/src/protocols/ext-workspace/ext-workspace.c deleted file mode 100644 index 30875811..00000000 --- a/src/protocols/ext-workspace/ext-workspace.c +++ /dev/null @@ -1,762 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include -#include -#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); -} diff --git a/src/protocols/ext-workspace/meson.build b/src/protocols/ext-workspace/meson.build deleted file mode 100644 index e781d13a..00000000 --- a/src/protocols/ext-workspace/meson.build +++ /dev/null @@ -1,4 +0,0 @@ -labwc_sources += files( - 'ext-workspace.c', - 'output.c', -) diff --git a/src/protocols/ext-workspace/output.c b/src/protocols/ext-workspace/output.c deleted file mode 100644 index 0d6a2a44..00000000 --- a/src/protocols/ext-workspace/output.c +++ /dev/null @@ -1,174 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include -#include -#include -#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); -} diff --git a/src/protocols/meson.build b/src/protocols/meson.build deleted file mode 100644 index d9d17bf5..00000000 --- a/src/protocols/meson.build +++ /dev/null @@ -1,6 +0,0 @@ -labwc_sources += files( - 'transaction-addon.c', -) - -subdir('cosmic_workspaces') -subdir('ext-workspace') diff --git a/src/protocols/transaction-addon.c b/src/protocols/transaction-addon.c deleted file mode 100644 index 808a2096..00000000 --- a/src/protocols/transaction-addon.c +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include "protocols/transaction-addon.h" -#include -#include -#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; -} diff --git a/src/server.c b/src/server.c index 727a8f08..653caf59 100644 --- a/src/server.c +++ b/src/server.c @@ -222,7 +222,6 @@ protocol_is_privileged(const struct wl_interface *iface) "zwlr_data_control_manager_v1", "wp_security_context_manager_v1", "ext_idle_notifier_v1", - "zcosmic_workspace_manager_v1", "zwlr_foreign_toplevel_manager_v1", "ext_foreign_toplevel_list_v1", "ext_session_lock_manager_v1", diff --git a/src/workspaces.c b/src/workspaces.c index 9b4ed664..7ff471a5 100644 --- a/src/workspaces.c +++ b/src/workspaces.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include "buffer.h" @@ -20,12 +21,9 @@ #include "input/keyboard.h" #include "labwc.h" #include "output.h" -#include "protocols/cosmic-workspaces.h" -#include "protocols/ext-workspace.h" #include "theme.h" #include "view.h" -#define COSMIC_WORKSPACES_VERSION 1 #define EXT_WORKSPACES_VERSION 1 /* Internal helpers */ @@ -210,22 +208,19 @@ workspace_find_by_name(const char *name) return NULL; } -/* cosmic workspace handlers */ 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); - workspaces_switch_to(workspace, /* update_focus */ true); - wlr_log(WLR_INFO, "cosmic activating workspace %s", workspace->name); -} + struct wlr_ext_workspace_v1_commit_event *event = data; -/* ext workspace handlers */ -static void -handle_ext_workspace_activate(struct wl_listener *listener, void *data) -{ - struct workspace *workspace = wl_container_of(listener, workspace, on_ext.activate); - workspaces_switch_to(workspace, /* update_focus */ true); - wlr_log(WLR_INFO, "ext activating workspace %s", workspace->name); + struct wlr_ext_workspace_v1_request *req; + wl_list_for_each(req, event->requests, link) { + if (req->type == WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE) { + struct workspace *workspace = req->activate.workspace->data; + workspaces_switch_to(workspace, /* update_focus */ true); + wlr_log(WLR_INFO, "activating workspace %s", workspace->name); + } + } } /* Internal API */ @@ -244,23 +239,13 @@ add_workspace(const char *name) wl_list_append(&server.workspaces.all, &workspace->link); wlr_scene_node_set_enabled(&workspace->tree->node, false); - /* cosmic */ - workspace->cosmic_workspace = lab_cosmic_workspace_create(server.workspaces.cosmic_group); - lab_cosmic_workspace_set_name(workspace->cosmic_workspace, name); - - workspace->on_cosmic.activate.notify = handle_cosmic_workspace_activate; - wl_signal_add(&workspace->cosmic_workspace->events.activate, - &workspace->on_cosmic.activate); - - /* 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); + workspace->ext_workspace = wlr_ext_workspace_handle_v1_create( + server.workspaces.ext_manager, /*id*/ NULL, + EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ACTIVATE); + workspace->ext_workspace->data = workspace; + wlr_ext_workspace_handle_v1_set_group( + workspace->ext_workspace, server.workspaces.ext_group); + wlr_ext_workspace_handle_v1_set_name(workspace->ext_workspace, name); } static struct workspace * @@ -402,19 +387,15 @@ _osd_show(void) void workspaces_init(void) { - server.workspaces.cosmic_manager = lab_cosmic_workspace_manager_create( - server.wl_display, /* capabilities */ CW_CAP_WS_ACTIVATE, - COSMIC_WORKSPACES_VERSION); + server.workspaces.ext_manager = wlr_ext_workspace_manager_v1_create( + server.wl_display, EXT_WORKSPACES_VERSION); - server.workspaces.ext_manager = lab_ext_workspace_manager_create( - server.wl_display, /* capabilities */ WS_CAP_WS_ACTIVATE, - EXT_WORKSPACES_VERSION); + server.workspaces.ext_group = wlr_ext_workspace_group_handle_v1_create( + server.workspaces.ext_manager, /*caps*/ 0); - server.workspaces.cosmic_group = lab_cosmic_workspace_group_create( - server.workspaces.cosmic_manager); - - server.workspaces.ext_group = lab_ext_workspace_group_create( - server.workspaces.ext_manager); + server.workspaces.on_ext_manager.commit.notify = handle_ext_workspace_commit; + wl_signal_add(&server.workspaces.ext_manager->events.commit, + &server.workspaces.on_ext_manager.commit); wl_list_init(&server.workspaces.all); @@ -441,8 +422,7 @@ workspaces_init(void) server.workspaces.current = initial; wlr_scene_node_set_enabled(&initial->tree->node, true); - lab_cosmic_workspace_set_active(initial->cosmic_workspace, true); - lab_ext_workspace_set_active(initial->ext_workspace, true); + wlr_ext_workspace_handle_v1_set_active(initial->ext_workspace, true); } /* @@ -462,9 +442,7 @@ workspaces_switch_to(struct workspace *target, bool update_focus) wlr_scene_node_set_enabled( &server.workspaces.current->tree->node, false); - lab_cosmic_workspace_set_active( - server.workspaces.current->cosmic_workspace, false); - lab_ext_workspace_set_active( + wlr_ext_workspace_handle_v1_set_active( server.workspaces.current->ext_workspace, false); /* @@ -516,8 +494,7 @@ workspaces_switch_to(struct workspace *target, bool update_focus) /* Ensure that only currently visible fullscreen windows hide the top layer */ desktop_update_top_layer_visibility(); - lab_cosmic_workspace_set_active(target->cosmic_workspace, true); - lab_ext_workspace_set_active(target->ext_workspace, true); + wlr_ext_workspace_handle_v1_set_active(target->ext_workspace, true); } void @@ -569,11 +546,8 @@ destroy_workspace(struct workspace *workspace) wlr_scene_node_destroy(&workspace->tree->node); zfree(workspace->name); 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); - lab_ext_workspace_destroy(workspace->ext_workspace); + wlr_ext_workspace_handle_v1_destroy(workspace->ext_workspace); free(workspace); } @@ -606,9 +580,7 @@ workspaces_reconfigure(void) wlr_log(WLR_DEBUG, "Renaming workspace \"%s\" to \"%s\"", workspace->name, conf->name); xstrdup_replace(workspace->name, conf->name); - lab_cosmic_workspace_set_name( - workspace->cosmic_workspace, workspace->name); - lab_ext_workspace_set_name( + wlr_ext_workspace_handle_v1_set_name( workspace->ext_workspace, workspace->name); } workspace_link = workspace_link->next; @@ -658,4 +630,5 @@ workspaces_destroy(void) destroy_workspace(workspace); } assert(wl_list_empty(&server.workspaces.all)); + wl_list_remove(&server.workspaces.on_ext_manager.commit.link); } From cee571287752cbc2e798194a0293fad683299ba2 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Mon, 30 Mar 2026 00:02:47 -0700 Subject: [PATCH 13/28] chase wlroots: wlr_scene_set_gamma_control_manager_v1 Ref: 515275ee7214bf91f8a758b660093eb4b932195a (wlr_scene: Introduce wlr_scene_set_gamma_control_manager_v1) This wlroots change eliminates the need for separate event tracking for gamma control application. v2: Fix code style v3: Rebase now that 0.20 is merged --- include/labwc.h | 1 - include/output.h | 2 -- src/output.c | 68 +++++------------------------------------------- 3 files changed, 6 insertions(+), 65 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index c579b06e..511fdd65 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -274,7 +274,6 @@ struct server { struct wl_listener renderer_lost; struct wlr_gamma_control_manager_v1 *gamma_control_manager_v1; - struct wl_listener gamma_control_set_gamma; struct session_lock_manager *session_lock_manager; diff --git a/include/output.h b/include/output.h index 4d5d63ae..89d8be04 100644 --- a/include/output.h +++ b/include/output.h @@ -38,8 +38,6 @@ struct output { * disconnected and connected again. */ uint64_t id_bit; - - bool gamma_lut_changed; }; #undef LAB_NR_LAYERS diff --git a/src/output.c b/src/output.c index 49544baa..f4f1da01 100644 --- a/src/output.c +++ b/src/output.c @@ -81,35 +81,6 @@ output_get_tearing_allowance(struct output *output) 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 handle_output_frame(struct wl_listener *listener, void *data) { @@ -129,23 +100,12 @@ handle_output_frame(struct wl_listener *listener, void *data) return; } - 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_output_state *pending = &output->pending; + struct wlr_scene_output *scene_output = output->scene_output; + 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 }; clock_gettime(CLOCK_MONOTONIC, &now); @@ -598,6 +558,8 @@ output_init(void) { server.gamma_control_manager_v1 = 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; wl_signal_add(&server.backend->events.new_output, &server.new_output); @@ -907,19 +869,6 @@ handle_output_layout_change(struct wl_listener *listener, void *data) 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 output_manager_init(void) { @@ -936,10 +885,6 @@ output_manager_init(void) server.output_manager_test.notify = handle_output_manager_test; wl_signal_add(&server.output_manager->events.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 @@ -948,7 +893,6 @@ output_manager_finish(void) wl_list_remove(&server.output_layout_change.link); wl_list_remove(&server.output_manager_apply.link); wl_list_remove(&server.output_manager_test.link); - wl_list_remove(&server.gamma_control_set_gamma.link); } struct output * From cdee4ba5f1bcaa7440f38832d417f7dc5e0e05ff Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 30 Mar 2026 18:32:30 +0900 Subject: [PATCH 14/28] xwayland: use wlr_xwayland_surface_fetch_icon() --- include/xwayland.h | 1 + src/xwayland.c | 44 +++++++++++--------------------------------- 2 files changed, 12 insertions(+), 33 deletions(-) diff --git a/include/xwayland.h b/include/xwayland.h index 2be7d934..379a2e8a 100644 --- a/include/xwayland.h +++ b/include/xwayland.h @@ -53,6 +53,7 @@ struct xwayland_view { struct wl_listener set_override_redirect; struct wl_listener set_strut_partial; struct wl_listener set_window_type; + struct wl_listener set_icon; struct wl_listener focus_in; /* Not (yet) implemented */ diff --git a/src/xwayland.c b/src/xwayland.c index 263833ff..90eec6b3 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -26,13 +26,11 @@ #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"); @@ -448,6 +446,7 @@ handle_destroy(struct wl_listener *listener, void *data) wl_list_remove(&xwayland_view->set_override_redirect.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_icon.link); wl_list_remove(&xwayland_view->focus_in.link); wl_list_remove(&xwayland_view->on_view.always_on_top.link); @@ -670,29 +669,20 @@ handle_set_strut_partial(struct wl_listener *listener, void *data) } static void -update_icon(struct xwayland_view *xwayland_view) +handle_set_icon(struct wl_listener *listener, void *data) { - if (!xwayland_view->xwayland_surface) { - return; - } + struct xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, set_icon); - xcb_window_t window_id = xwayland_view->xwayland_surface->window_id; - - xcb_connection_t *xcb_conn = wlr_xwayland_get_xwm_connection(server.xwayland); - 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)) { + xcb_ewmh_get_wm_icon_reply_t icon_reply = {0}; + if (!wlr_xwayland_surface_fetch_icon(xwayland_view->xwayland_surface, + &icon_reply)) { wlr_log(WLR_INFO, "Invalid x11 icon"); view_set_icon(&xwayland_view->base, NULL, NULL); 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; wl_array_init(&buffers); for (; iter.rem; xcb_ewmh_get_wm_icon_next(&iter)) { @@ -722,7 +712,7 @@ update_icon(struct xwayland_view *xwayland_view) wl_array_release(&buffers); out: - free(reply); + xcb_ewmh_get_wm_icon_reply_wipe(&icon_reply); } static void @@ -1051,6 +1041,7 @@ xwayland_view_create(struct wlr_xwayland_surface *xsurface, bool mapped) CONNECT_SIGNAL(xsurface, xwayland_view, set_override_redirect); CONNECT_SIGNAL(xsurface, xwayland_view, set_strut_partial); CONNECT_SIGNAL(xsurface, xwayland_view, set_window_type); + CONNECT_SIGNAL(xsurface, xwayland_view, set_icon); CONNECT_SIGNAL(xsurface, xwayland_view, focus_in); /* Events from the view itself */ @@ -1064,13 +1055,10 @@ xwayland_view_create(struct wlr_xwayland_surface *xsurface, bool mapped) /* * If a surface is already associated, then we've * missed the various initial set_* events as well. - * - * TODO: update_icon() -> handle_set_icon() after - * https://github.com/labwc/labwc/pull/2760 */ handle_set_title(&view->set_title, NULL); handle_set_class(&xwayland_view->set_class, NULL); - update_icon(xwayland_view); + handle_set_icon(&xwayland_view->set_icon, NULL); } if (mapped) { handle_map(&xwayland_view->base.mappable.map, NULL); @@ -1117,16 +1105,6 @@ 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: From 3128f50a250edd5cc6902173e8220479aec8a26e Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 30 Mar 2026 18:33:58 +0900 Subject: [PATCH 15/28] xwayland: remove atom synchronization It was used to get window icon via _NET_WM_ICON, which is now implemented by wlroots 0.20. Anyone who needs another atom can revert this commit and add atoms in the `atoms` array. --- src/xwayland.c | 84 -------------------------------------------------- 1 file changed, 84 deletions(-) diff --git a/src/xwayland.c b/src/xwayland.c index 90eec6b3..967364c5 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -25,18 +25,6 @@ #include "window-rules.h" #include "workspaces.h" -enum atoms { - - ATOM_COUNT, -}; - -static const char * const atom_names[] = { -}; - -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 handle_map(struct wl_listener *listener, void *data); static void handle_unmap(struct wl_listener *listener, void *data); @@ -1081,81 +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; - 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 handle_server_ready(struct wl_listener *listener, void *data) { /* Fire an Xwayland startup script if one (or many) can be found */ session_run_script("xinitrc"); - - sync_atoms(); } static void @@ -1188,8 +1106,6 @@ xwayland_server_init(struct wlr_compositor *compositor) wl_signal_add(&server.xwayland->events.ready, &server.xwayland_xwm_ready); - server.xwayland->user_event_handler = handle_x11_event; - if (setenv("DISPLAY", server.xwayland->display_name, true) < 0) { wlr_log_errno(WLR_ERROR, "unable to set DISPLAY for xwayland"); } else { From 862d230ff94137c0d0cbb4ddee8e61b6fbabe260 Mon Sep 17 00:00:00 2001 From: GlassOnTin Date: Mon, 30 Mar 2026 11:55:49 +0100 Subject: [PATCH 16/28] Make labnag a meson feature flag Add a 'labnag' meson option (default: auto) to allow disabling the labnag notification daemon at build time. This is useful for embedded/headless deployments (e.g. Android) where labnag is not needed, and avoids building its wayland-client dependencies. Disable with: meson setup build -Dlabnag=disabled --- clients/meson.build | 106 ++++++++++++++++++++++---------------------- meson_options.txt | 1 + 2 files changed, 55 insertions(+), 52 deletions(-) diff --git a/clients/meson.build b/clients/meson.build index 467bc035..55a4c0e5 100644 --- a/clients/meson.build +++ b/clients/meson.build @@ -1,59 +1,61 @@ -wayland_client = dependency('wayland-client') -wayland_cursor = dependency('wayland-cursor') +if get_option('labnag').allowed() + wayland_client = dependency('wayland-client') + wayland_cursor = dependency('wayland-cursor') -nag_sources = files( - 'labnag.c', - 'pool-buffer.c', -) - -wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') - -protocols = [ - wl_protocol_dir / 'stable/tablet/tablet-v2.xml', - wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml', - wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', - '../protocols/wlr-layer-shell-unstable-v1.xml', -] - -foreach xml : protocols - nag_sources += custom_target( - xml.underscorify() + '_c', - input: xml, - output: '@BASENAME@-protocol.c', - command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'], + nag_sources = files( + 'labnag.c', + 'pool-buffer.c', ) - nag_sources += custom_target( - xml.underscorify() + '_client_h', - input: xml, - output: '@BASENAME@-client-protocol.h', - command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'], - ) -endforeach -if host_machine.system() in ['freebsd', 'openbsd'] - # For signalfd() - epoll_dep = dependency('epoll-shim') -else - epoll_dep = [] + wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') + + protocols = [ + wl_protocol_dir / 'stable/tablet/tablet-v2.xml', + wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml', + wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', + '../protocols/wlr-layer-shell-unstable-v1.xml', + ] + + foreach xml : protocols + nag_sources += custom_target( + xml.underscorify() + '_c', + input: xml, + output: '@BASENAME@-protocol.c', + command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'], + ) + nag_sources += custom_target( + xml.underscorify() + '_client_h', + input: xml, + output: '@BASENAME@-client-protocol.h', + command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'], + ) + endforeach + + if host_machine.system() in ['freebsd', 'openbsd'] + # For signalfd() + epoll_dep = dependency('epoll-shim') + else + epoll_dep = [] + endif + + executable( + 'labnag', + nag_sources, + dependencies: [ + cairo, + pangocairo, + glib, + wayland_client, + wayland_cursor, + wlroots, + server_protos, + epoll_dep, + xkbcommon, + ], + include_directories: [labwc_inc], + install: true, + ) endif -executable( - 'labnag', - nag_sources, - dependencies: [ - cairo, - pangocairo, - glib, - wayland_client, - wayland_cursor, - wlroots, - server_protos, - epoll_dep, - xkbcommon, - ], - include_directories: [labwc_inc], - install: true, -) - clients = files('lab-sensible-terminal') install_data(clients, install_dir: get_option('bindir')) diff --git a/meson_options.txt b/meson_options.txt index a3da65a8..a47efa86 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -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('svg', type: 'feature', value: 'enabled', description: 'Enable svg window buttons') 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('static_analyzer', type: 'feature', value: 'disabled', description: 'Run gcc static analyzer') option('test', type: 'feature', value: 'disabled', description: 'Run tests') From 9a8154836c04765e209f77bd1e3810e2a4818324 Mon Sep 17 00:00:00 2001 From: GlassOnTin Date: Mon, 30 Mar 2026 11:56:31 +0100 Subject: [PATCH 17/28] keyboard: use XKB_CONTEXT_NO_SECURE_GETENV on Android Android's bionic libc implements secure_getenv() as a function that always returns NULL because app processes don't have AT_SECURE set. This prevents xkbcommon from reading XKB_DEFAULT_LAYOUT and other environment variables when resolving keyboard layouts. Use XKB_CONTEXT_NO_SECURE_GETENV on Android so xkbcommon falls back to regular getenv(), which works correctly in the Android app environment. --- src/input/keyboard.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/input/keyboard.c b/src/input/keyboard.c index 8e5c1f78..5a5c7595 100644 --- a/src/input/keyboard.c +++ b/src/input/keyboard.c @@ -745,7 +745,18 @@ set_layout(struct wlr_keyboard *kb) static bool fallback_mode; 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, XKB_KEYMAP_COMPILE_NO_FLAGS); From 46d687ab5414f2bd2d55db49be14601054d2fe75 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Mon, 30 Mar 2026 22:04:01 +0100 Subject: [PATCH 18/28] clients/labnag.c: return zero on get_text_size() error Ref: https://github.com/swaywm/sway/pull/9070 --- clients/labnag.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/clients/labnag.c b/clients/labnag.c index 237ee958..82b6e8cd 100644 --- a/clients/labnag.c +++ b/clients/labnag.c @@ -206,6 +206,16 @@ static void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height, 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_start(args, fmt); gchar *buf = g_strdup_vprintf(fmt, args); From 9209f611d552adbb36e59cef05aaca5d1e834efe Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Tue, 31 Mar 2026 19:17:33 +0200 Subject: [PATCH 19/28] build: require libinput 1.26 Bump the requirement for having support for tablet tool pressure range configuration. --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 4b622b5e..de73e8ea 100644 --- a/meson.build +++ b/meson.build @@ -71,7 +71,7 @@ xml2 = dependency('libxml-2.0') glib = dependency('glib-2.0') cairo = dependency('cairo') pangocairo = dependency('pangocairo') -input = dependency('libinput', version: '>=1.14') +input = dependency('libinput', version: '>=1.26') pixman = dependency('pixman-1') math = cc.find_library('m') png = dependency('libpng') From 0d049552bc98a59db7c0cc62f673a83c0de67b0c Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Sun, 13 Jul 2025 21:38:00 +0200 Subject: [PATCH 20/28] input: support tablet tool pressure range configuration Needs wlroots 0.20.0. --- include/config/rcxml.h | 2 ++ src/config/rcxml.c | 8 ++++++++ src/input/tablet.c | 23 +++++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/include/config/rcxml.h b/include/config/rcxml.h index aa950234..517cd907 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -144,6 +144,8 @@ struct rcxml { struct tablet_tool_config { enum lab_motion motion; double relative_motion_sensitivity; + double min_pressure; + double max_pressure; } tablet_tool; /* libinput */ diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 45093781..a3eeed63 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -1355,6 +1355,12 @@ entry(xmlNode *node, char *nodename, char *content) } else if (!strcasecmp(nodename, "relativeMotionSensitivity.tabletTool")) { rc.tablet_tool.relative_motion_sensitivity = 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")) { rc.menu_ignore_button_release_period = atoi(content); } else if (!strcasecmp(nodename, "showIcons.menu")) { @@ -1475,6 +1481,8 @@ rcxml_init(void) tablet_load_default_button_mappings(); rc.tablet_tool.motion = LAB_MOTION_ABSOLUTE; 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_delay = 600; diff --git a/src/input/tablet.c b/src/input/tablet.c index ed596e15..a4ca0fab 100644 --- a/src/input/tablet.c +++ b/src/input/tablet.c @@ -2,11 +2,13 @@ #include "input/tablet.h" #include #include +#include "wlr/backend/libinput.h" #include #include #include #include #include +#include #include "common/macros.h" #include "common/mem.h" #include "common/scene-helpers.h" @@ -336,6 +338,27 @@ handle_tablet_tool_proximity(struct wl_listener *listener, void *data) tool = tablet_tool_create(tablet->seat, ev->tool); } + 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); + } + } + /* * Enforce mouse emulation when the current tool is a tablet mouse. * Client support for tablet mouses in tablet mode is often incomplete From 674238caa9d56a623c3a0182a1aca75065f97cdd Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Mon, 14 Jul 2025 21:50:32 +0200 Subject: [PATCH 21/28] docs: document tablet tool pressure range --- docs/labwc-config.5.scd | 13 ++++++++++++- docs/rc.xml.all | 5 ++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index 03d5fc24..09f7b3eb 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -1090,7 +1090,8 @@ Note: To rotate touch events with output rotation, use the libinput ## TABLET TOOL ``` - + ``` ** [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 cursor. The default is "1.0". +** +** + 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 ``` diff --git a/docs/rc.xml.all b/docs/rc.xml.all index bc9566fe..bbec9d0b 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -573,8 +573,11 @@ *relativeMotionSensitivity* controls the speed of the cursor. Using a value lower than 1.0 decreases the speed, using a value greater than 1.0 increases the speed of the cursor. + The pressure range of a tablet tool can be controlled by adjusting + *minPressure* and *maxPressure*. --> - +