From 8724b26123f97bcbc986ab1058c0e075798475a5 Mon Sep 17 00:00:00 2001 From: Philipp Kaeser <130065133+phkaeser@users.noreply.github.com> Date: Thu, 13 Feb 2025 22:00:12 +0100 Subject: [PATCH] Adds support for 'exclusive' keyboard interactivity on top and bottom layers. (#170) --- src/layer_panel.c | 51 +++++++++++++++++++++++++++++++++++------ src/toolkit/content.c | 22 ++++++++++++++++++ src/toolkit/content.h | 4 ++++ src/toolkit/window.c | 7 ++++++ src/toolkit/workspace.c | 5 ++-- 5 files changed, 80 insertions(+), 9 deletions(-) diff --git a/src/layer_panel.c b/src/layer_panel.c index 890d3f0a..8cda037c 100644 --- a/src/layer_panel.c +++ b/src/layer_panel.c @@ -65,7 +65,8 @@ static void _wlmaker_layer_panel_destroy( static bool _wlmaker_layer_panel_apply_keyboard( wlmaker_layer_panel_t *layer_panel_ptr, - enum zwlr_layer_surface_v1_keyboard_interactivity interactivity); + enum zwlr_layer_surface_v1_keyboard_interactivity interactivity, + enum zwlr_layer_shell_v1_layer zwlr_layer); static bool _wlmaker_layer_panel_apply_layer( wlmaker_layer_panel_t *layer_panel_ptr, enum zwlr_layer_shell_v1_layer zwlr_layer); @@ -251,12 +252,46 @@ wlmtk_workspace_layer_t _wlmaker_layer_from_zwlr_layer( } /* ------------------------------------------------------------------------- */ -/** Applies the requested keyboard setting. Currently warns on non-zero. */ +/** + * Applies the requested keyboard setting. + * + * Supports 'NONE' and 'EXCLUSIVE' interactivity, but the latter only on + * top and overlay layers. + * + * TODO(kaeser@gubbe.ch): Implement full support, once layer elements have a + * means to organically obtain and release keyboard focus (eg. through pointer + * button clicks). + * + * @param layer_panel_ptr + * @param interactivity + * @param zwlr_layer + * + * @return true on success. + */ bool _wlmaker_layer_panel_apply_keyboard( wlmaker_layer_panel_t *layer_panel_ptr, - enum zwlr_layer_surface_v1_keyboard_interactivity interactivity) + enum zwlr_layer_surface_v1_keyboard_interactivity interactivity, + enum zwlr_layer_shell_v1_layer zwlr_layer) { - if (ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE != interactivity) { + switch (interactivity) { + case ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE: + break; + + case ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE: + if (ZWLR_LAYER_SHELL_V1_LAYER_TOP != zwlr_layer && + ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY != zwlr_layer) { + wl_resource_post_error( + layer_panel_ptr->wlr_layer_surface_v1_ptr->resource, + WL_DISPLAY_ERROR_IMPLEMENTATION, + "Exclusive interactivity unsupported on layer %d", zwlr_layer); + return false; + } + + wlmtk_surface_set_activated(layer_panel_ptr->wlmtk_surface_ptr, true); + break; + + case ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND: + default: wl_resource_post_error( layer_panel_ptr->wlr_layer_surface_v1_ptr->resource, WL_DISPLAY_ERROR_IMPLEMENTATION, @@ -264,6 +299,7 @@ bool _wlmaker_layer_panel_apply_keyboard( interactivity); return false; } + return true; } @@ -380,12 +416,13 @@ void _wlmaker_layer_panel_handle_surface_commit( &pos); // Updates keyboard and layer values. Ignore failures here. - _wlmaker_layer_panel_apply_keyboard( - layer_panel_ptr, - state_ptr->keyboard_interactive); _wlmaker_layer_panel_apply_layer( layer_panel_ptr, state_ptr->layer); + _wlmaker_layer_panel_apply_keyboard( + layer_panel_ptr, + state_ptr->keyboard_interactive, + state_ptr->layer); } /* ------------------------------------------------------------------------- */ diff --git a/src/toolkit/content.c b/src/toolkit/content.c index bede5df2..49651f20 100644 --- a/src/toolkit/content.c +++ b/src/toolkit/content.c @@ -35,11 +35,15 @@ static void _wlmtk_content_element_get_dimensions( int *right_ptr, int *bottom_ptr); +static void _wlmtk_content_element_keyboard_blur( + wlmtk_element_t *element_ptr); + /* == Data ================================================================= */ /** Virtual method table for the content's superclass @ref wlmtk_element_t. */ static const wlmtk_element_vmt_t _wlmtk_content_element_vmt = { .get_dimensions = _wlmtk_content_element_get_dimensions, + .keyboard_blur = _wlmtk_content_element_keyboard_blur, }; /* == Exported methods ===================================================== */ @@ -290,6 +294,24 @@ void _wlmtk_content_element_get_dimensions( left_ptr, top_ptr, right_ptr, bottom_ptr); } +/* ------------------------------------------------------------------------- */ +/** + * De-activates keyboard focus for the content: Propagates the blur to all + * children, and then de-activates the content's window. + * + * @param element_ptr + */ +void _wlmtk_content_element_keyboard_blur(wlmtk_element_t *element_ptr) +{ + wlmtk_content_t *content_ptr = BS_CONTAINER_OF( + element_ptr, wlmtk_content_t, super_container.super_element); + + content_ptr->orig_super_element_vmt.keyboard_blur(element_ptr); + if (NULL != content_ptr->window_ptr) { + wlmtk_window_set_activated(content_ptr->window_ptr, false); + } +} + /* == Fake content, for tests ============================================== */ static void _wlmtk_fake_content_request_close(wlmtk_content_t *content_ptr); diff --git a/src/toolkit/content.h b/src/toolkit/content.h index ae16ae9e..d30c9427 100644 --- a/src/toolkit/content.h +++ b/src/toolkit/content.h @@ -102,6 +102,10 @@ struct _wlmtk_content_vmt_t { /** * Sets whether this content as activated (keyboard focus). * + * The implementation must (for the effective contained element) issue a + * call to @ref wlmtk_container_set_keyboard_focus_element to claim or + * release keyboard focus. + * * @param content_ptr * @param activated */ diff --git a/src/toolkit/window.c b/src/toolkit/window.c index a832f01b..b85dbfd3 100644 --- a/src/toolkit/window.c +++ b/src/toolkit/window.c @@ -316,6 +316,8 @@ void wlmtk_window_set_activated( wlmtk_window_t *window_ptr, bool activated) { + if (window_ptr->activated == activated) return; + window_ptr->activated = activated; wlmtk_content_set_activated(window_ptr->content_ptr, activated); if (NULL != window_ptr->titlebar_ptr) { @@ -324,6 +326,11 @@ void wlmtk_window_set_activated( if (!activated) { wlmtk_window_menu_set_enabled(window_ptr, false); + + // TODO(kaeser@gubbe.ch): Should test this behaviour. + if (NULL != window_ptr->workspace_ptr) { + wlmtk_workspace_activate_window(window_ptr->workspace_ptr, NULL); + } } } diff --git a/src/toolkit/workspace.c b/src/toolkit/workspace.c index c3d587ca..d9cb80e5 100644 --- a/src/toolkit/workspace.c +++ b/src/toolkit/workspace.c @@ -615,16 +615,17 @@ void wlmtk_workspace_activate_window( if (workspace_ptr->activated_window_ptr == window_ptr) return; if (NULL != workspace_ptr->activated_window_ptr) { - wlmtk_window_set_activated(workspace_ptr->activated_window_ptr, false); + wlmtk_window_t *w_ptr = workspace_ptr->activated_window_ptr; workspace_ptr->formerly_activated_window_ptr = workspace_ptr->activated_window_ptr; workspace_ptr->activated_window_ptr = NULL; + wlmtk_window_set_activated(w_ptr, false); } if (NULL != window_ptr) { if (workspace_ptr->enabled) { - wlmtk_window_set_activated(window_ptr, true); workspace_ptr->activated_window_ptr = window_ptr; + wlmtk_window_set_activated(window_ptr, true); } workspace_ptr->formerly_activated_window_ptr = window_ptr; }