Skip to content

Commit

Permalink
Adds a toplevel window menu, for both XDG and X11 windows. (#155)
Browse files Browse the repository at this point in the history
* Moves the toplevel's menu specification out of xdg_toplevel.c

* Add window menu also to X11 windows.
  • Loading branch information
phkaeser authored Jan 7, 2025
1 parent a47e0f7 commit 856ba4a
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 46 deletions.
2 changes: 1 addition & 1 deletion doc/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
97 changes: 97 additions & 0 deletions src/tl_menu.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/* ========================================================================= */
/**
* @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 ===================================================== */
58 changes: 58 additions & 0 deletions src/tl_menu.h
Original file line number Diff line number Diff line change
@@ -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 ===================================================== */
8 changes: 6 additions & 2 deletions src/toolkit/content.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}

Expand Down Expand Up @@ -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);
}
}

Expand Down
60 changes: 17 additions & 43 deletions src/xdg_toplevel.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

#include "xdg_shell.h"

#include "action_item.h"
#include "tl_menu.h"
#include "xdg_popup.h"

#include <wlr/version.h>
Expand All @@ -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`. */
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 ===================================================== */

/* ------------------------------------------------------------------------- */
Expand All @@ -189,30 +171,17 @@ 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);

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));
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;
}

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;
}

Expand Down Expand Up @@ -351,6 +320,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) {
Expand Down
19 changes: 19 additions & 0 deletions src/xwl_toplevel.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

#include "xwl_toplevel.h"

#include "tl_menu.h"

/* == Declarations ========================================================= */

/** State of a XWayland toplevel window. */
Expand All @@ -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. */
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
}

Expand Down

0 comments on commit 856ba4a

Please sign in to comment.