From 3e74fdd55eae45f25a85b2edf8bbaeee34f48e06 Mon Sep 17 00:00:00 2001 From: Philipp Kaeser Date: Thu, 2 Jan 2025 20:49:14 +0100 Subject: [PATCH 1/2] Moves the toplevel's menu specification out of xdg_toplevel.c --- src/CMakeLists.txt | 2 + src/tl_menu.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++ src/tl_menu.h | 58 +++++++++++++++++++++++++++ src/xdg_toplevel.c | 54 +++++++------------------ 4 files changed, 173 insertions(+), 39 deletions(-) create mode 100644 src/tl_menu.c create mode 100644 src/tl_menu.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c09b3768..597de9de 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -35,6 +35,7 @@ SET(PUBLIC_HEADER_FILES server.h subprocess_monitor.h task_list.h + tl_menu.h xdg_decoration.h xdg_popup.h xdg_shell.h @@ -66,6 +67,7 @@ TARGET_SOURCES(wlmaker_lib PRIVATE server.c subprocess_monitor.c task_list.c + tl_menu.c xdg_decoration.c xdg_popup.c xdg_shell.c diff --git a/src/tl_menu.c b/src/tl_menu.c new file mode 100644 index 00000000..65026306 --- /dev/null +++ b/src/tl_menu.c @@ -0,0 +1,98 @@ +/* ========================================================================= */ +/** + * @file tl_menu.c + * + * @copyright + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "tl_menu.h" + +#include "action_item.h" + +/* == Declarations ========================================================= */ + +/** State of a toplevel's window menu. */ +struct _wlmaker_tl_menu_t { + /** Pointer to the window's @ref wlmtk_menu_t. */ + wlmtk_menu_t *menu_ptr; +}; + +/** Temporary: Struct for defining an item for the window menu. */ +typedef struct { + /** Text to use for the menu item. */ + const char *text_ptr; + /** Action to be executed for that menu item. */ + wlmaker_action_t action; +} wlmaker_window_menu_item_t; + +/* == Data ================================================================= */ +/** Menu items for the XDG toplevel's window menu. */ +static const wlmaker_window_menu_item_t _xdg_toplevel_menu_items[] = { + { "Maximize", WLMAKER_ACTION_WINDOW_MAXIMIZE }, + { "Unmaximize", WLMAKER_ACTION_WINDOW_UNMAXIMIZE }, + { "Fullscreen", WLMAKER_ACTION_WINDOW_TOGGLE_FULLSCREEN }, + { "Shade", WLMAKER_ACTION_WINDOW_SHADE }, + { "Unshade", WLMAKER_ACTION_WINDOW_UNSHADE }, + { "To prev. workspace", WLMAKER_ACTION_WINDOW_TO_PREVIOUS_WORKSPACE }, + { "To next workspace", WLMAKER_ACTION_WINDOW_TO_NEXT_WORKSPACE }, + { "Close", WLMAKER_ACTION_WINDOW_CLOSE }, + { NULL, 0 } // Sentinel. +}; + +/* == Exported methods ===================================================== */ + +/* ------------------------------------------------------------------------- */ +wlmaker_tl_menu_t *wlmaker_tl_menu_create( + wlmtk_window_t *window_ptr, + wlmaker_server_t *server_ptr) +{ + wlmaker_tl_menu_t *tl_menu_ptr = logged_calloc( + 1, sizeof(wlmaker_tl_menu_t)); + if (NULL == tl_menu_ptr) return NULL; + tl_menu_ptr->menu_ptr = wlmtk_window_menu(window_ptr); + + for (const wlmaker_window_menu_item_t *i_ptr = &_xdg_toplevel_menu_items[0]; + i_ptr->text_ptr != NULL; + ++i_ptr) { + + wlmaker_action_item_t *action_item_ptr = wlmaker_action_item_create( + i_ptr->text_ptr, + &server_ptr->style.menu.item, + i_ptr->action, + server_ptr, + server_ptr->env_ptr); + if (NULL == action_item_ptr) { + wlmaker_tl_menu_destroy(tl_menu_ptr); + return NULL; + } + wlmtk_menu_add_item( + tl_menu_ptr->menu_ptr, + wlmaker_action_item_menu_item(action_item_ptr)); + } + + + return tl_menu_ptr; +} + +/* ------------------------------------------------------------------------- */ +void wlmaker_tl_menu_destroy(wlmaker_tl_menu_t *tl_menu_ptr) +{ + free(tl_menu_ptr); +} + +/* == Local (static) methods =============================================== */ + +/* == End of tl_menu.c ===================================================== */ diff --git a/src/tl_menu.h b/src/tl_menu.h new file mode 100644 index 00000000..893a8094 --- /dev/null +++ b/src/tl_menu.h @@ -0,0 +1,58 @@ +/* ========================================================================= */ +/** + * @file tl_menu.h + * + * @copyright + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef __WLMAKER_TL_MENU_H__ +#define __WLMAKER_TL_MENU_H__ + +#include "toolkit/toolkit.h" + +#include "server.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/** Forward declaration: State of a toplevel's menu. */ +typedef struct _wlmaker_tl_menu_t wlmaker_tl_menu_t; + +/** + * Creates a (window) menu for a toplevel (window). + * + * @param window_ptr + * @param server_ptr + * + * @return pointer to the toplevel's menu state or NULL on error. + */ +wlmaker_tl_menu_t *wlmaker_tl_menu_create( + wlmtk_window_t *window_ptr, + wlmaker_server_t *server_ptr); + +/** + * Destroys the toplevel's menu. + * + * @param tl_menu_ptr + */ +void wlmaker_tl_menu_destroy(wlmaker_tl_menu_t *tl_menu_ptr); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif /* __TL_MENU_H__ */ +/* == End of tl_menu.h ===================================================== */ diff --git a/src/xdg_toplevel.c b/src/xdg_toplevel.c index de294588..b9ce5ca8 100644 --- a/src/xdg_toplevel.c +++ b/src/xdg_toplevel.c @@ -20,7 +20,7 @@ #include "xdg_shell.h" -#include "action_item.h" +#include "tl_menu.h" #include "xdg_popup.h" #include @@ -41,6 +41,9 @@ typedef struct { /** The corresponding wlroots XDG toplevel. */ struct wlr_xdg_toplevel *wlr_xdg_toplevel_ptr; + /** The toplevel's window menu. */ + wlmaker_tl_menu_t *tl_menu_ptr; + /** Listener for the `destroy` signal of the `wlr_xdg_toplevel::events`. */ struct wl_listener destroy_listener; /** Listener for the `new_popup` signal of the `wlr_xdg_surface`. */ @@ -72,14 +75,6 @@ typedef struct { struct wl_listener toplevel_set_app_id_listener; } xdg_toplevel_surface_t; -/** Temporary: Struct for defining an item for the window menu. */ -typedef struct { - /** Text to use for the menu item. */ - const char *text_ptr; - /** Action to be executed for that menu item. */ - wlmaker_action_t action; -} wlmaker_window_menu_item_t; - static xdg_toplevel_surface_t *xdg_toplevel_surface_create( struct wlr_xdg_toplevel *wlr_xdg_toplevel_ptr, wlmaker_server_t *server_ptr); @@ -156,19 +151,6 @@ const wlmtk_content_vmt_t _xdg_toplevel_content_vmt = { .set_activated = content_set_activated, }; -/** Menu items for the XDG toplevel's window menu. */ -static const wlmaker_window_menu_item_t _xdg_toplevel_menu_items[] = { - { "Maximize", WLMAKER_ACTION_WINDOW_MAXIMIZE }, - { "Unmaximize", WLMAKER_ACTION_WINDOW_UNMAXIMIZE }, - { "Fullscreen", WLMAKER_ACTION_WINDOW_TOGGLE_FULLSCREEN }, - { "Shade", WLMAKER_ACTION_WINDOW_SHADE }, - { "Unshade", WLMAKER_ACTION_WINDOW_UNSHADE }, - { "To prev. workspace", WLMAKER_ACTION_WINDOW_TO_PREVIOUS_WORKSPACE }, - { "To next workspace", WLMAKER_ACTION_WINDOW_TO_NEXT_WORKSPACE }, - { "Close", WLMAKER_ACTION_WINDOW_CLOSE }, - { NULL, 0 } // Sentinel. -}; - /* == Exported methods ===================================================== */ /* ------------------------------------------------------------------------- */ @@ -194,23 +176,12 @@ wlmtk_window_t *wlmtk_window_create_from_xdg_toplevel( bs_log(BS_INFO, "Created window %p for wlmtk XDG toplevel surface %p", wlmtk_window_ptr, surface_ptr); - for (const wlmaker_window_menu_item_t *i_ptr = &_xdg_toplevel_menu_items[0]; - i_ptr->text_ptr != NULL; - ++i_ptr) { - - wlmaker_action_item_t *action_item_ptr = wlmaker_action_item_create( - i_ptr->text_ptr, - &server_ptr->style.menu.item, - i_ptr->action, - server_ptr, - server_ptr->env_ptr); - if (NULL == action_item_ptr) { - wlmtk_window_destroy(wlmtk_window_ptr); - return NULL; - } - wlmtk_menu_add_item( - wlmtk_window_menu(wlmtk_window_ptr), - wlmaker_action_item_menu_item(action_item_ptr)); + // FIXME: + surface_ptr->tl_menu_ptr = wlmaker_tl_menu_create( + wlmtk_window_ptr, server_ptr); + if (NULL == surface_ptr->tl_menu_ptr) { + xdg_toplevel_surface_destroy(surface_ptr); + return NULL; } return wlmtk_window_ptr; @@ -351,6 +322,11 @@ void xdg_toplevel_surface_destroy( wl_list_remove(&xts_ptr->new_popup_listener.link); wl_list_remove(&xts_ptr->destroy_listener.link); + if (NULL != xdg_tl_surface_ptr->tl_menu_ptr) { + wlmaker_tl_menu_destroy(xdg_tl_surface_ptr->tl_menu_ptr); + xdg_tl_surface_ptr->tl_menu_ptr = NULL; + } + wlmtk_content_fini(&xts_ptr->super_content); if (NULL != xdg_tl_surface_ptr->surface_ptr) { From 9ab0d2fb904c6db47f16822fdc0ab94c3b20215e Mon Sep 17 00:00:00 2001 From: Philipp Kaeser Date: Tue, 7 Jan 2025 21:45:04 +0100 Subject: [PATCH 2/2] Add window menu also to X11 windows. --- doc/ROADMAP.md | 2 +- src/tl_menu.c | 1 - src/toolkit/content.c | 8 ++++++-- src/xdg_toplevel.c | 8 +++----- src/xwl_toplevel.c | 19 +++++++++++++++++++ 5 files changed, 29 insertions(+), 9 deletions(-) diff --git a/doc/ROADMAP.md b/doc/ROADMAP.md index ef23bd95..31c9722f 100644 --- a/doc/ROADMAP.md +++ b/doc/ROADMAP.md @@ -22,7 +22,7 @@ Support for visual effects to improve usability, but not for pure show. * [done] Menu shown on right-button-down, items trigger on right-button-up. * [done] When invoked on unclaimed button, exits menu on button release. * [done] Available as window menu in windows. - * Available also for X11 windows. + * [done] Available also for X11 windows. * Available as (hardcoded) application menu. * Menu with submenus. * Window menu adapting to window state. diff --git a/src/tl_menu.c b/src/tl_menu.c index 65026306..8ce8f6b1 100644 --- a/src/tl_menu.c +++ b/src/tl_menu.c @@ -83,7 +83,6 @@ wlmaker_tl_menu_t *wlmaker_tl_menu_create( wlmaker_action_item_menu_item(action_item_ptr)); } - return tl_menu_ptr; } diff --git a/src/toolkit/content.c b/src/toolkit/content.c index 91076231..bede5df2 100644 --- a/src/toolkit/content.c +++ b/src/toolkit/content.c @@ -60,8 +60,6 @@ bool wlmtk_content_init( &content_ptr->super_container.super_element, &_wlmtk_content_element_vmt); - wlmtk_content_set_element(content_ptr, element_ptr); - if (!wlmtk_container_init(&content_ptr->popup_container, env_ptr)) { wlmtk_content_fini(content_ptr); return false; @@ -73,6 +71,8 @@ bool wlmtk_content_init( &content_ptr->popup_container.super_element, true); + wlmtk_content_set_element(content_ptr, element_ptr); + return true; } @@ -123,6 +123,10 @@ void wlmtk_content_set_element( content_ptr->element_ptr = element_ptr; wlmtk_element_set_visible(element_ptr, true); + // FIXME + wlmtk_container_raise_element_to_top( + &content_ptr->super_container, + &content_ptr->popup_container.super_element); } } diff --git a/src/xdg_toplevel.c b/src/xdg_toplevel.c index b9ce5ca8..27103f0d 100644 --- a/src/xdg_toplevel.c +++ b/src/xdg_toplevel.c @@ -171,12 +171,7 @@ wlmtk_window_t *wlmtk_window_create_from_xdg_toplevel( xdg_toplevel_surface_destroy(surface_ptr); return NULL; } - wl_signal_emit(&server_ptr->window_created_event, wlmtk_window_ptr); - - bs_log(BS_INFO, "Created window %p for wlmtk XDG toplevel surface %p", - wlmtk_window_ptr, surface_ptr); - // FIXME: surface_ptr->tl_menu_ptr = wlmaker_tl_menu_create( wlmtk_window_ptr, server_ptr); if (NULL == surface_ptr->tl_menu_ptr) { @@ -184,6 +179,9 @@ wlmtk_window_t *wlmtk_window_create_from_xdg_toplevel( return NULL; } + wl_signal_emit(&server_ptr->window_created_event, wlmtk_window_ptr); + bs_log(BS_INFO, "Created window %p for wlmtk XDG toplevel surface %p", + wlmtk_window_ptr, surface_ptr); return wlmtk_window_ptr; } diff --git a/src/xwl_toplevel.c b/src/xwl_toplevel.c index 2c3d0bd9..dc5c98b4 100644 --- a/src/xwl_toplevel.c +++ b/src/xwl_toplevel.c @@ -21,6 +21,8 @@ #include "xwl_toplevel.h" +#include "tl_menu.h" + /* == Declarations ========================================================= */ /** State of a XWayland toplevel window. */ @@ -31,6 +33,9 @@ struct _wlmaker_xwl_toplevel_t { /** Back-link to server. */ wlmaker_server_t *server_ptr; + /** The toplevel's window menu. */ + wlmaker_tl_menu_t *tl_menu_ptr; + /** Listener for `map` event of the surface. */ struct wl_listener surface_map_listener; /** Listener for `unmap` event of the surface. */ @@ -66,6 +71,15 @@ wlmaker_xwl_toplevel_t *wlmaker_xwl_toplevel_create( wlmaker_xwl_toplevel_destroy(xwl_toplevel_ptr); return NULL; } + + xwl_toplevel_ptr->tl_menu_ptr = wlmaker_tl_menu_create( + xwl_toplevel_ptr->window_ptr, + server_ptr); + if (NULL == xwl_toplevel_ptr->tl_menu_ptr) { + wlmaker_xwl_toplevel_destroy(xwl_toplevel_ptr); + return NULL; + } + wl_signal_emit(&server_ptr->window_created_event, xwl_toplevel_ptr->window_ptr); @@ -98,6 +112,11 @@ void wlmaker_xwl_toplevel_destroy( wl_list_remove(&xwl_toplevel_ptr->surface_unmap_listener.link); wl_list_remove(&xwl_toplevel_ptr->surface_map_listener.link); + if (NULL != xwl_toplevel_ptr->tl_menu_ptr) { + wlmaker_tl_menu_destroy(xwl_toplevel_ptr->tl_menu_ptr); + xwl_toplevel_ptr->tl_menu_ptr = NULL; + } + free(xwl_toplevel_ptr); }