diff --git a/include/labwc.h b/include/labwc.h index d5ff2d95..2d9cb239 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -154,6 +154,7 @@ struct server { uint32_t resize_edges; struct wlr_scene_tree *osd_tree; + struct wlr_scene_tree *menu_tree; struct wl_list outputs; struct wl_listener new_output; @@ -175,8 +176,7 @@ struct server { struct theme *theme; - struct menu *rootmenu; - struct menu *windowmenu; + struct menu *menu_current; }; struct output { diff --git a/include/menu/menu.h b/include/menu/menu.h index 9cdc8c6f..a5ec7dd8 100644 --- a/include/menu/menu.h +++ b/include/menu/menu.h @@ -3,19 +3,29 @@ #define __LABWC_MENU_H #include -#include + +struct lab_data_buffer; +struct wlr_scene_node; + +enum menu_align { + LAB_MENU_OPEN_AUTO = 0, + LAB_MENU_OPEN_LEFT = 1 << 0, + LAB_MENU_OPEN_RIGHT = 1 << 1, + LAB_MENU_OPEN_TOP = 1 << 2, + LAB_MENU_OPEN_BOTTOM = 1 << 3, +}; + +struct menu_scene { + struct lab_data_buffer *buffer; + struct wlr_scene_node *text; + struct wlr_scene_node *background; +}; struct menuitem { struct wl_list actions; struct menu *submenu; - struct wlr_box box; - struct { - struct wlr_texture *active; - struct wlr_texture *inactive; - int offset_x; - int offset_y; - } texture; - bool selected; + struct menu_scene normal; + struct menu_scene selected; struct wl_list link; /* menu::menuitems */ }; @@ -23,27 +33,62 @@ struct menuitem { struct menu { char *id; char *label; - bool visible; + int item_height; struct menu *parent; - struct wlr_box box; + struct { + int width; + int height; + } size; struct wl_list menuitems; struct server *server; + struct { + struct menu *menu; + struct menuitem *item; + } selection; + struct wlr_scene_tree *scene_tree; }; void menu_init_rootmenu(struct server *server); void menu_init_windowmenu(struct server *server); void menu_finish(void); -/* menu_move - move to position (x, y) */ -void menu_move(struct menu *menu, int x, int y); +/** + * menu_get_by_id - get menu by id + * + * @id id string defined in menu.xml like "root-menu" + */ +struct menu *menu_get_by_id(const char *id); -/* menu_set_selected - select item at (x, y) */ -void menu_set_selected(struct menu *menu, int x, int y); +/** + * menu_open - open menu on position (x, y) + * + * This function will close server->menu_current, open the + * new menu and assign @menu to server->menu_current. + * + * Additionally, server->input_mode wil be set to LAB_INPUT_STATE_MENU. + */ +void menu_open(struct menu *menu, int x, int y); -/* menu_action_selected - select item at (x, y) */ -void menu_action_selected(struct server *server, struct menu *menu); +/** + * menu_process_cursor_motion + * + * - handles hover effects + * - may open/close submenus + */ +void menu_process_cursor_motion(struct menu *menu, struct wlr_scene_node *node); + +/** + * menu_call_actions - call actions associated with a menu entry + * + * If actions are found, server->menu_current will be closed and set to NULL + * Returns true if handled + */ +bool menu_call_actions(struct menu *menu, struct wlr_scene_node *node); + +/* menu_close - close menu */ +void menu_close(struct menu *menu); /* menu_reconfigure - reload theme and content */ -void menu_reconfigure(struct server *server, struct menu *menu); +void menu_reconfigure(struct server *server); #endif /* __LABWC_MENU_H */ diff --git a/src/action.c b/src/action.c index 23d3dcf3..65aee0dc 100644 --- a/src/action.c +++ b/src/action.c @@ -91,19 +91,15 @@ void action_list_free(struct wl_list *action_list) { static void show_menu(struct server *server, struct view *view, const char *menu_name) { - struct menu *menu = NULL; bool force_menu_top_left = false; - - if (!menu_name) { + struct menu *menu = menu_get_by_id(menu_name); + if (!menu) { return; } - - if (!strcasecmp(menu_name, "root-menu")) { - menu = server->rootmenu; - server->windowmenu->visible = false; - } else if (!strcasecmp(menu_name, "client-menu") && view) { - menu = server->windowmenu; - server->rootmenu->visible = false; + if (!strcasecmp(menu_name, "client-menu")) { + if (!view) { + return; + } enum ssd_part_type type = ssd_at(view, server->seat.cursor->x, server->seat.cursor->y); if (type == LAB_SSD_BUTTON_WINDOW_MENU) { @@ -113,13 +109,8 @@ show_menu(struct server *server, struct view *view, const char *menu_name) } else { force_menu_top_left = true; } - } else { - return; } - menu->visible = true; - server->input_mode = LAB_INPUT_STATE_MENU; - int x, y; if (force_menu_top_left) { x = view->x; @@ -128,8 +119,7 @@ show_menu(struct server *server, struct view *view, const char *menu_name) x = server->seat.cursor->x; y = server->seat.cursor->y; } - menu_move(menu, x, y); - damage_all_outputs(server); + menu_open(menu, x, y); } static struct view * diff --git a/src/cursor.c b/src/cursor.c index 633b0c0a..adb68d3f 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -41,7 +41,7 @@ request_cursor_notify(struct wl_listener *listener, void *data) { struct seat *seat = wl_container_of(listener, seat, request_cursor); /* - * This event is rasied by the seat when a client provides a cursor + * This event is raised by the seat when a client provides a cursor * image */ struct wlr_seat_pointer_request_set_cursor_event *event = data; @@ -187,19 +187,6 @@ process_cursor_motion(struct server *server, uint32_t time) } else if (server->input_mode == LAB_INPUT_STATE_RESIZE) { process_cursor_resize(server, time); return; - } else if (server->input_mode == LAB_INPUT_STATE_MENU) { - struct menu *menu = NULL; - if (server->rootmenu->visible) { - menu = server->rootmenu; - } else if (server->windowmenu->visible) { - menu = server->windowmenu; - } else { - return; - } - menu_set_selected(menu, - server->seat.cursor->x, server->seat.cursor->y); - damage_all_outputs(server); - return; } /* Otherwise, find view under the pointer and send the event along */ @@ -239,6 +226,10 @@ process_cursor_motion(struct server *server, uint32_t time) } } + if (view_area == LAB_SSD_MENU) { + menu_process_cursor_motion(server->menu_current, node); + return; + } if (view && rc.focus_follow_mouse) { desktop_focus_and_activate_view(&server->seat, view); @@ -648,11 +639,18 @@ cursor_button(struct wl_listener *listener, void *data) } if (server->input_mode == LAB_INPUT_STATE_MENU) { - if (server->rootmenu->visible) { - menu_action_selected(server, server->rootmenu); - } else if (server->windowmenu->visible) { - menu_action_selected(server, server->windowmenu); + if (!server->menu_current) { + wlr_log(WLR_ERROR, + "on mouse button input_mode STATE_MENU but no current menu"); + } else if (view_area != LAB_SSD_MENU) { + menu_close(server->menu_current); + server->menu_current = NULL; + } else if (!menu_call_actions(server->menu_current, node)) { + /* Action was not successfull, maybe this menu has a submenu */ + return; } + /* TODO: following causes stray release */ + /* Maybe add LAB_INPUT_STATE_IGNORE_MOUSE_RELEASE ? */ server->input_mode = LAB_INPUT_STATE_PASSTHROUGH; cursor_rebase(&server->seat, event->time_msec); return; diff --git a/src/debug.c b/src/debug.c index 46ff3723..8d36daf0 100644 --- a/src/debug.c +++ b/src/debug.c @@ -76,4 +76,12 @@ debug_dump_scene(struct server *server) dump_tree(node, 0, node->state.x, node->state.y); } } + + printf(":: osd_tree ::\n"); + node = &server->osd_tree->node; + dump_tree(node, 0, node->state.x, node->state.y); + + printf(":: menu_tree ::\n"); + node = &server->menu_tree->node; + dump_tree(node, 0, node->state.x, node->state.y); } diff --git a/src/desktop.c b/src/desktop.c index 7b6afa0a..d17544dd 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -282,12 +282,15 @@ desktop_node_and_view_at(struct server *server, double lx, double ly, *view_area = LAB_SSD_NONE; } struct wlr_scene_node *osd = &server->osd_tree->node; + struct wlr_scene_node *menu = &server->menu_tree->node; while (node && !node->data) { if (node == osd) { *view_area = LAB_SSD_OSD; return NULL; + } else if (node == menu) { + *view_area = LAB_SSD_MENU; + return NULL; } - /* TODO: node == &server->menu_tree->node */ node = node->parent; } if (!node) { diff --git a/src/menu/menu.c b/src/menu/menu.c index 5dc768c1..beb85c58 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -19,6 +19,7 @@ #include "menu/menu.h" #include "theme.h" #include "action.h" +#include "buffer.h" #define MENUWIDTH (110) #define MENU_ITEM_PADDING_Y (4) @@ -51,12 +52,19 @@ menu_create(struct server *server, const char *id, const char *label) menu->label = strdup(label); menu->parent = current_menu; menu->server = server; + menu->size.width = MENUWIDTH; + /* menu->size.height will be kept up to date by adding items */ + menu->scene_tree = wlr_scene_tree_create(&server->menu_tree->node); + wlr_scene_node_set_enabled(&menu->scene_tree->node, false); return menu; } -static struct menu * -get_menu_by_id(const char *id) +struct menu * +menu_get_by_id(const char *id) { + if (!id) { + return NULL; + } struct menu *menu; for (int i = 0; i < nr_menus; ++i) { menu = menus + i; @@ -74,32 +82,58 @@ item_create(struct menu *menu, const char *text) if (!menuitem) { return NULL; } -/* FIXME */ -#if 0 struct server *server = menu->server; struct theme *theme = server->theme; -#endif struct font font = { .name = rc.font_name_menuitem, .size = rc.font_size_menuitem, }; - menuitem->box.width = MENUWIDTH; - menuitem->box.height = font_height(&font) + 2 * MENU_ITEM_PADDING_Y; + if (!menu->item_height) { + menu->item_height = font_height(&font) + 2 * MENU_ITEM_PADDING_Y; + } -/* FIXME */ -#if 0 + int x, y; int item_max_width = MENUWIDTH - 2 * MENU_ITEM_PADDING_X; - font_texture_create(server, &menuitem->texture.active, item_max_width, - text, &font, theme->menu_items_active_text_color); - font_texture_create(server, &menuitem->texture.inactive, item_max_width, - text, &font, theme->menu_items_text_color); + struct wlr_scene_node *parent = &menu->scene_tree->node; - /* center align vertically */ - menuitem->texture.offset_y = - (menuitem->box.height - menuitem->texture.active->height) / 2; - menuitem->texture.offset_x = MENU_ITEM_PADDING_X; -#endif + /* Font buffer */ + font_buffer_create(&menuitem->normal.buffer, item_max_width, + text, &font, theme->menu_items_text_color); + font_buffer_create(&menuitem->selected.buffer, item_max_width, + text, &font, theme->menu_items_active_text_color); + + /* Item background nodes */ + menuitem->normal.background = &wlr_scene_rect_create(parent, + MENUWIDTH, menu->item_height, + theme->menu_items_bg_color)->node; + menuitem->selected.background = &wlr_scene_rect_create(parent, + MENUWIDTH, menu->item_height, + theme->menu_items_active_bg_color)->node; + + /* Font nodes */ + menuitem->normal.text = &wlr_scene_buffer_create( + menuitem->normal.background, &menuitem->normal.buffer->base)->node; + menuitem->selected.text = &wlr_scene_buffer_create( + menuitem->selected.background, &menuitem->selected.buffer->base)->node; + + /* Center font nodes */ + y = (menu->item_height - menuitem->normal.buffer->base.height) / 2; + x = MENU_ITEM_PADDING_X; + wlr_scene_node_set_position(menuitem->normal.text, x, y); + wlr_scene_node_set_position(menuitem->selected.text, x, y); + + /* Position the item in relation to its menu */ + int item_count = wl_list_length(&menu->menuitems); + y = item_count * menu->item_height; + wlr_scene_node_set_position(menuitem->normal.background, 0, y); + wlr_scene_node_set_position(menuitem->selected.background, 0, y); + + /* Hide selected state */ + wlr_scene_node_set_enabled(menuitem->selected.background, false); + + /* Update menu extends */ + menu->size.height = (item_count + 1) * menu->item_height; wl_list_insert(&menu->menuitems, &menuitem->link); wl_list_init(&menuitem->actions); @@ -210,7 +244,7 @@ handle_menu_element(xmlNode *n, struct server *server) current_menu = current_menu->parent; --menu_level; } else if (id) { - struct menu *menu = get_menu_by_id(id); + struct menu *menu = menu_get_by_id(id); if (menu) { current_item = item_create(current_menu, menu->label); current_item->submenu = menu; @@ -286,78 +320,127 @@ err: free(b.buf); } +static int +menu_get_full_width(struct menu *menu) +{ + int width = menu->size.width - menu->server->theme->menu_overlap_x; + int child_width; + int max_child_width = 0; + struct menuitem *item; + wl_list_for_each_reverse(item, &menu->menuitems, link) { + if (!item->submenu) { + continue; + } + child_width = menu_get_full_width(item->submenu); + if (child_width > max_child_width) { + max_child_width = child_width; + } + } + return width + max_child_width; +} + static void -menu_configure(struct menu *menu, int x, int y) +menu_configure(struct menu *menu, int lx, int ly, enum menu_align align) { struct theme *theme = menu->server->theme; - menu->box.x = x; - menu->box.y = y; + /* Get output local coordinates + output usable area */ + double ox = lx; + double oy = ly; + struct wlr_output *wlr_output = wlr_output_layout_output_at( + menu->server->output_layout, lx, ly); + wlr_output_layout_output_coords(menu->server->output_layout, + wlr_output, &ox, &oy); + struct wlr_box usable = output_usable_area_from_cursor_coords(menu->server); - int offset = 0; - struct menuitem *menuitem; - wl_list_for_each_reverse (menuitem, &menu->menuitems, link) { - menuitem->box.x = menu->box.x; - menuitem->box.y = menu->box.y + offset; - offset += menuitem->box.height; - if (menuitem->submenu) { - menu_configure(menuitem->submenu, menuitem->box.x - + MENUWIDTH - theme->menu_overlap_x, - menuitem->box.y + theme->menu_overlap_y); + if (align == LAB_MENU_OPEN_AUTO) { + int full_width = menu_get_full_width(menu); + if (ox + full_width > usable.width) { + align = LAB_MENU_OPEN_LEFT; + } else { + align = LAB_MENU_OPEN_RIGHT; } } - menu->box.width = MENUWIDTH; - menu->box.height = offset; + if (oy + menu->size.height > usable.height) { + align &= ~LAB_MENU_OPEN_BOTTOM; + align |= LAB_MENU_OPEN_TOP; + } else { + align &= ~LAB_MENU_OPEN_TOP; + align |= LAB_MENU_OPEN_BOTTOM; + } + + if (align & LAB_MENU_OPEN_LEFT) { + lx -= MENUWIDTH - theme->menu_overlap_x; + } + if (align & LAB_MENU_OPEN_TOP) { + ly -= menu->size.height; + if (menu->parent) { + /* For submenus adjust y to bottom left corner */ + ly += menu->item_height; + } + } + wlr_scene_node_set_position(&menu->scene_tree->node, lx, ly); + + int rel_y; + int new_lx, new_ly; + struct menuitem *item; + wl_list_for_each_reverse(item, &menu->menuitems, link) { + if (!item->submenu) { + continue; + } + if (align & LAB_MENU_OPEN_RIGHT) { + new_lx = lx + MENUWIDTH - theme->menu_overlap_x; + } else { + new_lx = lx; + } + rel_y = item->normal.background->state.y; + new_ly = ly + rel_y - theme->menu_overlap_y; + menu_configure(item->submenu, new_lx, new_ly, align); + } } void menu_init_rootmenu(struct server *server) { parse_xml("menu.xml", server); - server->rootmenu = get_menu_by_id("root-menu"); + struct menu *menu = menu_get_by_id("root-menu"); /* Default menu if no menu.xml found */ - if (!server->rootmenu) { + if (!menu) { current_menu = NULL; - server->rootmenu = menu_create(server, "root-menu", ""); + menu = menu_create(server, "root-menu", ""); } - if (wl_list_empty(&server->rootmenu->menuitems)) { - current_item = item_create(server->rootmenu, "Reconfigure"); + if (wl_list_empty(&menu->menuitems)) { + current_item = item_create(menu, "Reconfigure"); fill_item("name.action", "Reconfigure"); - current_item = item_create(server->rootmenu, "Exit"); + current_item = item_create(menu, "Exit"); fill_item("name.action", "Exit"); } - - server->rootmenu->visible = true; - menu_configure(server->rootmenu, 100, 100); } void menu_init_windowmenu(struct server *server) { - server->windowmenu = get_menu_by_id("client-menu"); + struct menu *menu = menu_get_by_id("client-menu"); /* Default menu if no menu.xml found */ - if (!server->windowmenu) { + if (!menu) { current_menu = NULL; - server->windowmenu = menu_create(server, "client-menu", ""); + menu = menu_create(server, "client-menu", ""); } - if (wl_list_empty(&server->windowmenu->menuitems)) { - current_item = item_create(server->windowmenu, "Minimize"); + if (wl_list_empty(&menu->menuitems)) { + current_item = item_create(menu, "Minimize"); fill_item("name.action", "Iconify"); - current_item = item_create(server->windowmenu, "Maximize"); + current_item = item_create(menu, "Maximize"); fill_item("name.action", "ToggleMaximize"); - current_item = item_create(server->windowmenu, "Fullscreen"); + current_item = item_create(menu, "Fullscreen"); fill_item("name.action", "ToggleFullscreen"); - current_item = item_create(server->windowmenu, "Decorations"); + current_item = item_create(menu, "Decorations"); fill_item("name.action", "ToggleDecorations"); - current_item = item_create(server->windowmenu, "Close"); + current_item = item_create(menu, "Close"); fill_item("name.action", "Close"); } - - server->windowmenu->visible = true; - menu_configure(server->windowmenu, 100, 100); } void @@ -370,104 +453,157 @@ menu_finish(void) wl_list_for_each_safe(item, next, &menu->menuitems, link) { wl_list_remove(&item->link); action_list_free(&item->actions); + wlr_scene_node_destroy(item->normal.text); + wlr_scene_node_destroy(item->selected.text); + wlr_scene_node_destroy(item->normal.background); + wlr_scene_node_destroy(item->selected.background); + wlr_buffer_drop(&item->normal.buffer->base); + wlr_buffer_drop(&item->selected.buffer->base); free(item); } + wlr_scene_node_destroy(&menu->scene_tree->node); } zfree(menus); alloc_menus = 0; nr_menus = 0; } +/* Sets selection (or clears selection if passing NULL) */ +static void +menu_set_selection(struct menu *menu, struct menuitem *item) +{ + /* Clear old selection */ + if (menu->selection.item) { + wlr_scene_node_set_enabled( + menu->selection.item->normal.background, true); + wlr_scene_node_set_enabled( + menu->selection.item->selected.background, false); + } + /* Set new selection */ + if (item) { + wlr_scene_node_set_enabled(item->normal.background, false); + wlr_scene_node_set_enabled(item->selected.background, true); + } + menu->selection.item = item; +} + static void close_all_submenus(struct menu *menu) { struct menuitem *item; wl_list_for_each (item, &menu->menuitems, link) { if (item->submenu) { - item->submenu->visible = false; + wlr_scene_node_set_enabled(&item->submenu->scene_tree->node, false); close_all_submenus(item->submenu); } } + menu->selection.menu = NULL; } void -menu_move(struct menu *menu, int x, int y) +menu_open(struct menu *menu, int x, int y) { assert(menu); + if (menu->server->menu_current) { + menu_close(menu->server->menu_current); + } close_all_submenus(menu); - menu_configure(menu, x, y); + menu_set_selection(menu, NULL); + menu_configure(menu, x, y, LAB_MENU_OPEN_AUTO); + wlr_scene_node_set_enabled(&menu->scene_tree->node, true); + menu->server->menu_current = menu; + menu->server->input_mode = LAB_INPUT_STATE_MENU; } -/* TODO: consider renaming function to menu_process_cursor_motion */ void -menu_set_selected(struct menu *menu, int x, int y) +menu_process_cursor_motion(struct menu *menu, struct wlr_scene_node *node) { - if (!menu->visible) { + if (!node) { + wlr_log(WLR_ERROR, "menu_process_cursor_motion() node == NULL"); return; } + assert(menu); + + /* TODO: this would be much easier if we could use node->data */ struct menuitem *item; wl_list_for_each (item, &menu->menuitems, link) { - item->selected = wlr_box_contains_point(&item->box, x, y); - - if (!item->selected) { - if (item->submenu && item->submenu->visible) { - /* - * Handle the case where a submenu is already - * open. - */ - item->selected = true; - menu_set_selected(item->submenu, x, y); - } - continue; + if (node == item->selected.background + || node->parent == item->selected.background) { + /* We are on an already selected item */ + return; } - - /* We're now on an item that has mouse-focus */ - if (item->submenu) { - if (item->submenu->visible) { - /* do nothing - submenu already open */ - } else { - /* open submenu */ - close_all_submenus(menu); - item->submenu->visible = true; - menu_set_selected(item->submenu, x, y); + if (node == item->normal.background + || node->parent == item->normal.background) { + /* We are on an item that has new mouse-focus */ + menu_set_selection(menu, item); + if (menu->selection.menu) { + /* Close old submenu tree */ + menu_close(menu->selection.menu); } - } else { - close_all_submenus(menu); + if (item->submenu) { + /* And open the new one */ + wlr_scene_node_set_enabled( + &item->submenu->scene_tree->node, true); + } + menu->selection.menu = item->submenu; + return; + } + if (item->submenu && item->submenu == menu->selection.menu) { + menu_process_cursor_motion(item->submenu, node); } } } -static void -menu_clear_selection(struct menu *menu) + +bool +menu_call_actions(struct menu *menu, struct wlr_scene_node *node) { - struct menuitem *item; - wl_list_for_each (item, &menu->menuitems, link) { - item->selected = false; - if (item->submenu) { - menu_clear_selection(item->submenu); + /* TODO: this would be much easier if we could use node->data */ + + if (!menu->selection.item) { + /* No item selected in current menu */ + wlr_log(WLR_ERROR, "No item on menu_action_selected"); + return false; + } + struct wlr_scene_node *menu_node = + menu->selection.item->selected.background; + if (node == menu_node || node->parent == menu_node) { + /* We found the correct menu item */ + if (menu->selection.item->submenu) { + /* ..but it just opens a submenu */ + return false; } + action(NULL, menu->server, &menu->selection.item->actions, 0); + menu_close(menu->server->menu_current); + menu->server->menu_current = NULL; + return true; + } + if (menu->selection.menu) { + return menu_call_actions(menu->selection.menu, node); + } + wlr_log(WLR_ERROR, "No match on menu_action_selected"); + return false; +} + +void +menu_close(struct menu *menu) +{ + if (!menu) { + wlr_log(WLR_ERROR, "Trying to close non exiting menu"); + return; + } + /* TODO: Maybe reset input state here instead of in cursor.c ? */ + wlr_scene_node_set_enabled(&menu->scene_tree->node, false); + menu_set_selection(menu, NULL); + if (menu->selection.menu) { + menu_close(menu->selection.menu); + menu->selection.menu = NULL; } } void -menu_action_selected(struct server *server, struct menu *menu) -{ - struct menuitem *menuitem; - wl_list_for_each (menuitem, &menu->menuitems, link) { - if (menuitem->selected && !menuitem->submenu) { - action(NULL, server, &menuitem->actions, 0); - break; - } - if (menuitem->submenu) { - menu_action_selected(server, menuitem->submenu); - } - } - menu_clear_selection(menu); -} - -void -menu_reconfigure(struct server *server, struct menu *menu) +menu_reconfigure(struct server *server) { menu_finish(); menu_init_rootmenu(server); diff --git a/src/server.c b/src/server.c index fbefa0e2..60c6c4e8 100644 --- a/src/server.c +++ b/src/server.c @@ -43,7 +43,7 @@ reload_config_and_theme(void) ssd_update_geometry(view, true); } - menu_reconfigure(g_server, g_server->rootmenu); + menu_reconfigure(g_server); seat_reconfigure(g_server); damage_all_outputs(g_server); } @@ -225,6 +225,7 @@ server_init(struct server *server) } server->view_tree = wlr_scene_tree_create(&server->scene->node); server->osd_tree = wlr_scene_tree_create(&server->scene->node); + server->menu_tree = wlr_scene_tree_create(&server->scene->node); wlr_scene_attach_output_layout(server->scene, server->output_layout); /*