From 5862b65f898d6de22325552a947be9f444fc8e09 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Mon, 21 Feb 2022 03:18:38 +0100 Subject: [PATCH] Convert SSD to scene nodes --- include/buffer.h | 2 +- include/common/scene-helpers.h | 5 + include/config/mousebind.h | 3 +- include/labwc.h | 16 +- include/ssd.h | 172 +++++++++--- src/buffer.c | 1 + src/common/meson.build | 1 + src/common/scene-helpers.c | 11 + src/cursor.c | 24 +- src/desktop.c | 6 +- src/meson.build | 2 +- src/server.c | 2 +- src/ssd.c | 479 --------------------------------- src/ssd/meson.build | 7 + src/ssd/ssd.c | 300 +++++++++++++++++++++ src/ssd/ssd_border.c | 96 +++++++ src/ssd/ssd_extents.c | 119 ++++++++ src/ssd/ssd_part.c | 109 ++++++++ src/ssd/ssd_titlebar.c | 306 +++++++++++++++++++++ src/theme.c | 6 +- src/view.c | 7 +- src/xdg.c | 11 +- src/xwayland.c | 6 +- 23 files changed, 1134 insertions(+), 557 deletions(-) create mode 100644 include/common/scene-helpers.h create mode 100644 src/common/scene-helpers.c delete mode 100644 src/ssd.c create mode 100644 src/ssd/meson.build create mode 100644 src/ssd/ssd.c create mode 100644 src/ssd/ssd_border.c create mode 100644 src/ssd/ssd_extents.c create mode 100644 src/ssd/ssd_part.c create mode 100644 src/ssd/ssd_titlebar.c diff --git a/include/buffer.h b/include/buffer.h index 3ffec484..af833f00 100644 --- a/include/buffer.h +++ b/include/buffer.h @@ -27,7 +27,7 @@ #define __LABWC_BUFFER_H #include -#include "labwc.h" +#include struct lab_data_buffer { struct wlr_buffer base; diff --git a/include/common/scene-helpers.h b/include/common/scene-helpers.h new file mode 100644 index 00000000..a3aa032b --- /dev/null +++ b/include/common/scene-helpers.h @@ -0,0 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include + +struct wlr_scene_rect *lab_wlr_scene_get_rect(struct wlr_scene_node *node); diff --git a/include/config/mousebind.h b/include/config/mousebind.h index e2da203c..ae66532a 100644 --- a/include/config/mousebind.h +++ b/include/config/mousebind.h @@ -2,8 +2,9 @@ #ifndef __LABWC_MOUSEBIND_H #define __LABWC_MOUSEBIND_H -#include "ssd.h" #include +#include "ssd.h" +#include "config/keybind.h" enum mouse_event { MOUSE_ACTION_NONE = 0, diff --git a/include/labwc.h b/include/labwc.h index 2d9cb239..c54b1f23 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -153,6 +153,10 @@ struct server { struct wlr_box grab_box; uint32_t resize_edges; + /* SSD state */ + struct view *ssd_focused_view; + struct ssd_hover_state ssd_hover_state; + struct wlr_scene_tree *osd_tree; struct wlr_scene_tree *menu_tree; @@ -272,17 +276,7 @@ struct view { uint32_t configure_serial; } pending_move_resize; - struct { - bool enabled; - struct wl_list parts; - struct wlr_box box; /* remember geo so we know when to update */ - } ssd; - - /* The title is unique to each view, so we store these here */ - struct { - struct wlr_texture *active; - struct wlr_texture *inactive; - } title; + struct ssd ssd; struct wlr_foreign_toplevel_handle_v1 *toplevel_handle; struct wl_listener toplevel_handle_request_maximize; diff --git a/include/ssd.h b/include/ssd.h index d711c060..0f4473eb 100644 --- a/include/ssd.h +++ b/include/ssd.h @@ -2,6 +2,22 @@ #ifndef __LABWC_SSD_H #define __LABWC_SSD_H +#include "buffer.h" +#include + +#define SSD_HEIGHT 26 /* TODO: use theme->title_height */ +#define BUTTON_COUNT 4 +#define BUTTON_WIDTH 26 +#define EXTENDED_AREA 20 + +#define FOR_EACH(tmp, ...) \ +{ \ + __typeof__(tmp) _x[] = { __VA_ARGS__, NULL }; \ + size_t _i = 0; \ + for ((tmp) = _x[_i]; _i < sizeof(_x) / sizeof(_x[0]) - 1; (tmp) = _x[++_i]) + +#define FOR_EACH_END } + /* * Sequence these according to the order they should be processed for * press and hover events. Bear in mind that some of their respective @@ -32,51 +48,139 @@ enum ssd_part_type { LAB_SSD_END_MARKER }; -/* - * Defer including labwc.h because it is using enum ssd_part_type. - * This is an issue for headers like mousebind.h which only includes - * ssd.h but does not include labwc.h. - */ -#include "labwc.h" +/* Forward declare arguments */ +struct view; +struct wl_list; +struct wlr_box; +struct wlr_scene_tree; + +struct ssd_sub_tree { + struct wlr_scene_tree *tree; + struct wl_list parts; /* ssd_part::link */ +}; + +struct ssd_state_title_width { + int width; + bool truncated; +}; + +struct ssd_state_title { + char *text; + struct ssd_state_title_width active; + struct ssd_state_title_width inactive; +}; + +struct ssd { + bool enabled; + struct wlr_scene_tree *tree; + + /* + * Cache for current values. + * Used to detect actual changes so we + * don't update things we don't have to. + */ + struct { + int width; + int height; + struct ssd_state_title title; + } state; + + /* An invisble area around the view which allows resizing */ + struct ssd_sub_tree extents; + + /* The top of the view, containing buttons, title, .. */ + struct { + /* struct wlr_scene_tree *tree; unused for now */ + struct ssd_sub_tree active; + struct ssd_sub_tree inactive; + } titlebar; + + /* Borders allow resizing as well */ + struct { + /* struct wlr_scene_tree *tree; unused for now */ + struct ssd_sub_tree active; + struct ssd_sub_tree inactive; + } border; +}; struct ssd_part { - struct wlr_box box; enum ssd_part_type type; - /* - * The texture pointers are often held in other places such as the - * theme struct, so here we use ** in order to keep the code - * simple and avoid updating pointers as textures change. - */ - struct { - struct wlr_texture **active; - struct wlr_texture **inactive; - } texture; + /* Buffer pointer. May be NULL */ + struct lab_data_buffer *buffer; - /* - * If a part does not contain textures, it'll just be rendered as a - * rectangle with the following colors. - */ - struct { - float *active; - float *inactive; - } color; + /* This part represented in scene graph */ + struct wlr_scene_node *node; struct wl_list link; }; -struct view; +struct ssd_hover_state { + struct view *view; + enum ssd_part_type type; + struct wlr_scene_node *node; +}; +/* Public SSD API */ +void ssd_create(struct view *view); +void ssd_hide(struct view *view); +void ssd_set_active(struct view *view); +void ssd_update_title(struct view *view); +void ssd_update_geometry(struct view *view); +void ssd_reload(struct view *view); +void ssd_destroy(struct view *view); +/* Returns hover overlay node so it can be disabled later on */ +struct wlr_scene_node *ssd_button_hover_enable( + struct view *view, enum ssd_part_type type); + +/* Public SSD helpers */ +enum ssd_part_type ssd_at(struct view *view, double lx, double ly); +enum ssd_part_type ssd_get_part_type( + struct view *view, struct wlr_scene_node *node); +uint32_t ssd_resize_edges(enum ssd_part_type type); +bool ssd_is_button(enum ssd_part_type type); +bool ssd_part_contains(enum ssd_part_type whole, enum ssd_part_type candidate); + +/* SSD internal helpers to create various SSD elements */ +/* TODO: Replace some common args with a struct */ +struct ssd_part *add_scene_part( + struct wl_list *part_list, enum ssd_part_type type); +struct ssd_part *add_scene_rect( + struct wl_list *list, enum ssd_part_type type, + struct wlr_scene_node *parent, int width, int height, int x, int y, + float color[4]); +struct ssd_part *add_scene_buffer( + struct wl_list *list, enum ssd_part_type type, + struct wlr_scene_node *parent, struct wlr_buffer *buffer, int x, int y); +struct ssd_part *add_scene_button( + struct wl_list *part_list, enum ssd_part_type type, + struct wlr_scene_node *parent, float *bg_color, + struct wlr_buffer *icon_buffer, int x); +struct ssd_part *add_scene_button_corner( + struct wl_list *part_list, enum ssd_part_type type, + struct wlr_scene_node *parent, struct wlr_buffer *corner_buffer, + struct wlr_buffer *icon_buffer, int x); + +/* SSD internal helpers */ +struct ssd_part *ssd_get_part( + struct wl_list *part_list, enum ssd_part_type type); +void ssd_destroy_parts(struct wl_list *list); + +/* SSD internal */ +void ssd_titlebar_create(struct view *view); +void ssd_titlebar_update(struct view *view); +void ssd_titlebar_destroy(struct view *view); + +void ssd_border_create(struct view *view); +void ssd_border_update(struct view *view); +void ssd_border_destroy(struct view *view); + +void ssd_extents_create(struct view *view); +void ssd_extents_update(struct view *view); +void ssd_extents_destroy(struct view *view); + +/* TODO: clean up / update */ struct border ssd_thickness(struct view *view); struct wlr_box ssd_max_extents(struct view *view); -struct wlr_box ssd_visible_box(struct view *view, enum ssd_part_type type); -enum ssd_part_type ssd_at(struct view *view, double lx, double ly); -uint32_t ssd_resize_edges(enum ssd_part_type type); -void ssd_update_title(struct view *view); -void ssd_create(struct view *view); -void ssd_destroy(struct view *view); -void ssd_update_geometry(struct view *view, bool force); -bool ssd_part_contains(enum ssd_part_type whole, enum ssd_part_type candidate); -bool ssd_is_button(enum ssd_part_type type); #endif /* __LABWC_SSD_H */ diff --git a/src/buffer.c b/src/buffer.c index 3a70aa29..2415d509 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -26,6 +26,7 @@ #include "config.h" #include +#include #include #include "buffer.h" diff --git a/src/common/meson.build b/src/common/meson.build index 02c763aa..fdfa72b2 100644 --- a/src/common/meson.build +++ b/src/common/meson.build @@ -4,6 +4,7 @@ labwc_sources += files( 'font.c', 'grab-file.c', 'nodename.c', + 'scene-helpers.c', 'spawn.c', 'string-helpers.c', 'zfree.c', diff --git a/src/common/scene-helpers.c b/src/common/scene-helpers.c new file mode 100644 index 00000000..a973a691 --- /dev/null +++ b/src/common/scene-helpers.c @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include + +struct wlr_scene_rect * +lab_wlr_scene_get_rect(struct wlr_scene_node *node) +{ + assert(node->type == WLR_SCENE_NODE_RECT); + return (struct wlr_scene_rect *)node; +} diff --git a/src/cursor.c b/src/cursor.c index adb68d3f..4751916e 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -178,7 +178,6 @@ static void process_cursor_motion(struct server *server, uint32_t time) { static bool cursor_name_set_by_server; - static enum ssd_part_type last_button_hover = LAB_SSD_NONE; /* If the mode is non-passthrough, delegate to those functions. */ if (server->input_mode == LAB_INPUT_STATE_MOVE) { @@ -257,17 +256,24 @@ process_cursor_motion(struct server *server, uint32_t time) } } - /* Required for iconify/maximize/close button mouse-over deco */ + /* SSD button mouse-over */ + struct ssd_hover_state *hover = &server->ssd_hover_state; if (ssd_is_button(view_area)) { - if (last_button_hover != view_area) { - /* Cursor entered new button area */ - //damage_whole_current_output(server); - last_button_hover = view_area; + /* Cursor entered new button area */ + if (hover->view != view || hover->type != view_area) { + if (hover->node) { + wlr_scene_node_set_enabled(hover->node, false); + } + hover->view = view; + hover->type = view_area; + hover->node = ssd_button_hover_enable(view, view_area); } - } else if (last_button_hover != LAB_SSD_NONE) { + } else if (hover->node) { /* Cursor left button area */ - //damage_whole_current_output(server); - last_button_hover = LAB_SSD_NONE; + wlr_scene_node_set_enabled(hover->node, false); + hover->view = NULL; + hover->type = LAB_SSD_NONE; + hover->node = NULL; } if (surface && diff --git a/src/desktop.c b/src/desktop.c index d17544dd..51a39210 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -276,10 +276,6 @@ desktop_node_and_view_at(struct server *server, double lx, double ly, *view_area = LAB_SSD_LAYER_SURFACE; return NULL; } - *view_area = LAB_SSD_CLIENT; - } else { - /* TODO: remove */ - *view_area = LAB_SSD_NONE; } struct wlr_scene_node *osd = &server->osd_tree->node; struct wlr_scene_node *menu = &server->menu_tree->node; @@ -299,7 +295,7 @@ desktop_node_and_view_at(struct server *server, double lx, double ly, return NULL; } struct view *view = node->data; - /* TODO: *view_area = ssd_get_type(view, node) */ + *view_area = ssd_get_part_type(view, *scene_node); return view; } diff --git a/src/meson.build b/src/meson.build index 1f9c5146..b318de8f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -16,7 +16,6 @@ labwc_sources = files( 'resistance.c', 'seat.c', 'server.c', - 'ssd.c', 'theme.c', 'view.c', 'view-impl.c', @@ -37,3 +36,4 @@ subdir('common') subdir('config') subdir('xbm') subdir('menu') +subdir('ssd') diff --git a/src/server.c b/src/server.c index 60c6c4e8..9e279aa6 100644 --- a/src/server.c +++ b/src/server.c @@ -40,7 +40,7 @@ reload_config_and_theme(void) continue; } view->margin = ssd_thickness(view); - ssd_update_geometry(view, true); + ssd_reload(view); } menu_reconfigure(g_server); diff --git a/src/ssd.c b/src/ssd.c deleted file mode 100644 index 3edfd3b3..00000000 --- a/src/ssd.c +++ /dev/null @@ -1,479 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Helpers for view server side decorations - * - * Copyright (C) Johan Malm 2020-2021 - */ - -#include -#include "config/rcxml.h" -#include "common/font.h" -#include "labwc.h" -#include "theme.h" -#include "ssd.h" - -#define INVISIBLE_MARGIN (16) - -struct border -ssd_thickness(struct view *view) -{ - struct theme *theme = view->server->theme; - struct border border = { - .top = theme->title_height + theme->border_width, - .bottom = theme->border_width, - .left = theme->border_width, - .right = theme->border_width, - }; - return border; -} - -struct wlr_box -ssd_max_extents(struct view *view) -{ - struct border border = ssd_thickness(view); - struct wlr_box box = { - .x = view->x - border.left, - .y = view->y - border.top, - .width = view->w + border.left + border.right, - .height = view->h + border.top + border.bottom, - }; - return box; -} - -#define NR_BUTTONS (4) - -/** - * ssd_box - the 'full' decoration geometry which includes both visible - * and invisible parts. It typically includes an invisible margin outside - * the decoration. - * - * This function is used for determining decoration parts during user- - * interactive operations such as mouse hover or button press - */ -static struct wlr_box -ssd_box(struct view *view, enum ssd_part_type type) -{ - struct theme *theme = view->server->theme; - struct wlr_box box = { 0 }; - int16_t button_height = theme->title_height; - int16_t button_width = theme->title_height; - int16_t corner_square = theme->title_height + theme->border_width; - int16_t title_x_padding = (double)corner_square / 2; - - switch (type) { - case LAB_SSD_BUTTON_CLOSE: - box.x = view->x + view->w - button_width * 1; - box.y = view->y - button_height; - box.width = button_width; - box.height = button_height; - break; - case LAB_SSD_BUTTON_MAXIMIZE: - box.x = view->x + view->w - button_width * 2; - box.y = view->y - button_height; - box.width = button_width; - box.height = button_height; - break; - case LAB_SSD_BUTTON_ICONIFY: - box.x = view->x + view->w - button_width * 3; - box.y = view->y - button_height; - box.width = button_width; - box.height = button_height; - break; - case LAB_SSD_BUTTON_WINDOW_MENU: - box.x = view->x; - box.y = view->y - button_height; - box.width = button_width; - box.height = button_height; - break; - case LAB_SSD_PART_TITLEBAR: - box.x = view->x; - box.y = view->y - theme->title_height; - box.width = view->w; - box.height = theme->title_height; - break; - case LAB_SSD_PART_TITLE: - box.x = view->x + button_width + title_x_padding; - box.y = view->y - theme->title_height; - box.width = view->w - title_x_padding * 2 - NR_BUTTONS * button_width; - box.height = theme->title_height; - break; - case LAB_SSD_PART_CORNER_TOP_LEFT: - box.x = view->x - theme->border_width - INVISIBLE_MARGIN; - box.y = view->y - corner_square - INVISIBLE_MARGIN; - box.width = corner_square + INVISIBLE_MARGIN; - box.height = corner_square + INVISIBLE_MARGIN; - break; - case LAB_SSD_PART_CORNER_TOP_RIGHT: - box.x = view->x + view->w - theme->title_height; - box.y = view->y - corner_square - INVISIBLE_MARGIN; - box.width = corner_square + INVISIBLE_MARGIN; - box.height = corner_square + INVISIBLE_MARGIN; - break; - case LAB_SSD_PART_CORNER_BOTTOM_RIGHT: - box.x = view->x + view->w - corner_square; - box.y = view->y + view->h - corner_square; - box.width = corner_square + INVISIBLE_MARGIN; - box.height = corner_square + INVISIBLE_MARGIN; - break; - case LAB_SSD_PART_CORNER_BOTTOM_LEFT: - box.x = view->x - theme->border_width - INVISIBLE_MARGIN; - box.y = view->y + view->h - corner_square; - box.width = corner_square + INVISIBLE_MARGIN; - box.height = corner_square + INVISIBLE_MARGIN; - break; - case LAB_SSD_PART_TOP: - box.x = view->x + theme->title_height; - box.y = view->y - corner_square - INVISIBLE_MARGIN; - box.width = view->w - 2 * theme->title_height; - box.height = theme->border_width + INVISIBLE_MARGIN; - break; - case LAB_SSD_PART_RIGHT: - box.x = view->x + view->w; - box.y = view->y; - box.width = theme->border_width + INVISIBLE_MARGIN; - box.height = view->h; - break; - case LAB_SSD_PART_BOTTOM: - box.x = view->x - theme->border_width; - box.y = view->y + view->h; - box.width = view->w + 2 * theme->border_width; - box.height = theme->border_width + INVISIBLE_MARGIN; - break; - case LAB_SSD_PART_LEFT: - box.x = view->x - theme->border_width - INVISIBLE_MARGIN; - box.y = view->y; - box.width = theme->border_width + INVISIBLE_MARGIN; - box.height = view->h; - break; - case LAB_SSD_CLIENT: - box.x = view->x; - box.y = view->y; - box.width = view->w; - box.height = view->h; - break; - default: - break; - } - return box; -} - -static void -center_vertically(struct wlr_box *box, struct wlr_texture *texture) -{ - if (!texture) { - return; - } - box->y += (box->height - texture->height) / 2; -} - -static void -center_horizontally(struct view *view, struct wlr_box *box, - struct wlr_texture *texture) -{ - if (!texture) { - return; - } - box->x = view->x + (view->w - texture->width) / 2; -} - -static void -justify_right(struct view *view, struct wlr_box *box, - struct wlr_texture *texture) -{ - if (!texture) { - return; - } - box->x = view->x + (box->width - texture->width); -} - -bool -ssd_is_button(enum ssd_part_type type) -{ - return type == LAB_SSD_BUTTON_CLOSE || - type == LAB_SSD_BUTTON_MAXIMIZE || - type == LAB_SSD_BUTTON_ICONIFY || - type == LAB_SSD_BUTTON_WINDOW_MENU; -} - -struct wlr_box -ssd_visible_box(struct view *view, enum ssd_part_type type) -{ - struct theme *theme = view->server->theme; - struct wlr_box box = { 0 }; - switch (type) { - case LAB_SSD_BUTTON_CLOSE: - box = ssd_box(view, type); - break; - case LAB_SSD_BUTTON_MAXIMIZE: - box = ssd_box(view, type); - break; - case LAB_SSD_BUTTON_ICONIFY: - box = ssd_box(view, type); - break; - case LAB_SSD_BUTTON_WINDOW_MENU: - box = ssd_box(view, type); - break; - case LAB_SSD_PART_TITLEBAR: - box = ssd_box(view, type); - box.x += theme->title_height; - box.width -= 2 * theme->title_height; - break; - case LAB_SSD_PART_TITLE: - box = ssd_box(view, type); - center_vertically(&box, view->title.active); - if (theme->window_label_text_justify == LAB_JUSTIFY_CENTER) { - center_horizontally(view, &box, view->title.active); - } else if (theme->window_label_text_justify == LAB_JUSTIFY_RIGHT) { - justify_right(view, &box, view->title.active); - } - if (view->title.active) { - box.width = view->title.active->width; - box.height = view->title.active->height; - } - break; - case LAB_SSD_PART_CORNER_TOP_LEFT: - box = ssd_box(view, type); - box.x += INVISIBLE_MARGIN; - box.y += INVISIBLE_MARGIN; - box.width -= INVISIBLE_MARGIN; - box.height -= INVISIBLE_MARGIN; - break; - case LAB_SSD_PART_CORNER_TOP_RIGHT: - box = ssd_box(view, type); - box.y += INVISIBLE_MARGIN; - box.width -= INVISIBLE_MARGIN; - box.height -= INVISIBLE_MARGIN; - break; - case LAB_SSD_PART_TOP: - box = ssd_box(view, type); - box.y += INVISIBLE_MARGIN; - box.height -= INVISIBLE_MARGIN; - break; - case LAB_SSD_PART_RIGHT: - box = ssd_box(view, type); - box.width -= INVISIBLE_MARGIN; - break; - case LAB_SSD_PART_BOTTOM: - box = ssd_box(view, type); - box.height -= INVISIBLE_MARGIN; - break; - case LAB_SSD_PART_LEFT: - box = ssd_box(view, type); - box.x += INVISIBLE_MARGIN; - box.width -= INVISIBLE_MARGIN; - break; - case LAB_SSD_PART_CORNER_BOTTOM_RIGHT: - case LAB_SSD_PART_CORNER_BOTTOM_LEFT: - default: - break; - } - return box; -} - -enum ssd_part_type -ssd_at(struct view *view, double lx, double ly) -{ - enum ssd_part_type type; - for (type = 0; type < LAB_SSD_END_MARKER; ++type) { - struct wlr_box box = ssd_box(view, type); - if (wlr_box_contains_point(&box, lx, ly)) { - return type; - } - } - return LAB_SSD_NONE; -} - -uint32_t -ssd_resize_edges(enum ssd_part_type type) -{ - switch (type) { - case LAB_SSD_PART_TOP: - return WLR_EDGE_TOP; - case LAB_SSD_PART_RIGHT: - return WLR_EDGE_RIGHT; - case LAB_SSD_PART_BOTTOM: - return WLR_EDGE_BOTTOM; - case LAB_SSD_PART_LEFT: - return WLR_EDGE_LEFT; - case LAB_SSD_PART_CORNER_TOP_LEFT: - return WLR_EDGE_TOP | WLR_EDGE_LEFT; - case LAB_SSD_PART_CORNER_TOP_RIGHT: - return WLR_EDGE_RIGHT | WLR_EDGE_TOP; - case LAB_SSD_PART_CORNER_BOTTOM_RIGHT: - return WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT; - case LAB_SSD_PART_CORNER_BOTTOM_LEFT: - return WLR_EDGE_BOTTOM | WLR_EDGE_LEFT; - default: - break; - } - return 0; -} - - -static struct ssd_part * -add_part(struct view *view, enum ssd_part_type type) -{ - struct ssd_part *part = calloc(1, sizeof(struct ssd_part)); - part->type = type; - wl_list_insert(&view->ssd.parts, &part->link); - return part; -} - -void -ssd_update_title(struct view *view) -{ - -/* FIXME */ -#if 0 - struct theme *theme = view->server->theme; - - struct font font = { - .name = rc.font_name_activewindow, - .size = rc.font_size_activewindow, - }; -#endif - - struct ssd_part *part; - wl_list_for_each(part, &view->ssd.parts, link) { - if (part->type == LAB_SSD_PART_TITLE) { - part->box = ssd_box(view, part->type); - break; - } - } - if (part->type != LAB_SSD_PART_TITLE) { - return; - } - -/* FIXME */ -#if 0 - int max_width = part->box.width > 0 ? part->box.width : 1000; - - font_texture_create(view->server, &view->title.active, max_width, - view_get_string_prop(view, "title"), - &font, theme->window_active_label_text_color); - - font_texture_create(view->server, &view->title.inactive, max_width, - view_get_string_prop(view, "title"), - &font, theme->window_inactive_label_text_color); -#endif - - part->box = ssd_visible_box(view, part->type); -} - -void -ssd_create(struct view *view) -{ - struct theme *theme = view->server->theme; - struct ssd_part *part; - - view->ssd.box.x = view->x; - view->ssd.box.y = view->y; - view->ssd.box.width = view->w; - view->ssd.box.height = view->h; - - /* border */ - enum ssd_part_type border[4] = { - LAB_SSD_PART_TOP, - LAB_SSD_PART_RIGHT, - LAB_SSD_PART_BOTTOM, - LAB_SSD_PART_LEFT, - }; - for (int i = 0; i < 4; i++) { - part = add_part(view, border[i]); - part->box = ssd_visible_box(view, border[i]); - part->color.active = theme->window_active_border_color; - part->color.inactive = theme->window_inactive_border_color; - } - - /* titlebar */ - part = add_part(view, LAB_SSD_PART_TITLEBAR); - part->box = ssd_visible_box(view, LAB_SSD_PART_TITLEBAR); - part->color.active = theme->window_active_title_bg_color; - part->color.inactive = theme->window_inactive_title_bg_color; - - /* titlebar top-left corner */ - part = add_part(view, LAB_SSD_PART_CORNER_TOP_LEFT); - part->box = ssd_visible_box(view, part->type); -/* FIXME */ -#if 0 - part->texture.active = &theme->corner_top_left_active_normal; - part->texture.inactive = &theme->corner_top_left_inactive_normal; -#endif - - /* titlebar top-right corner */ - part = add_part(view, LAB_SSD_PART_CORNER_TOP_RIGHT); - part->box = ssd_visible_box(view, part->type); -/* FIXME */ -#if 0 - part->texture.active = &theme->corner_top_right_active_normal; - part->texture.inactive = &theme->corner_top_right_inactive_normal; -#endif - - /* title text */ - part = add_part(view, LAB_SSD_PART_TITLE); - ssd_update_title(view); - part->texture.active = &view->title.active; - part->texture.inactive = &view->title.inactive; -} - -void -ssd_destroy(struct view *view) -{ - struct ssd_part *part, *next; - wl_list_for_each_safe(part, next, &view->ssd.parts, link) { - wl_list_remove(&part->link); - free(part); - } -} - -static bool -geometry_changed(struct view *view) -{ - return view->x != view->ssd.box.x || view->y != view->ssd.box.y || - view->w != view->ssd.box.width || - view->h != view->ssd.box.height; -} - -void -ssd_update_geometry(struct view *view, bool force) -{ - if (!geometry_changed(view) && !force) { - return; - } - struct ssd_part *part; - wl_list_for_each(part, &view->ssd.parts, link) { - part->box = ssd_visible_box(view, part->type); - } - view->ssd.box.x = view->x; - view->ssd.box.y = view->y; - view->ssd.box.width = view->w; - view->ssd.box.height = view->h; - damage_all_outputs(view->server); -} - -bool -ssd_part_contains(enum ssd_part_type whole, enum ssd_part_type candidate) -{ - if (whole == candidate) { - return true; - } - if (whole == LAB_SSD_PART_TITLEBAR) { - return candidate >= LAB_SSD_BUTTON_CLOSE && candidate <= LAB_SSD_PART_TITLE; - } - if (whole == LAB_SSD_FRAME) { - return candidate >= LAB_SSD_BUTTON_CLOSE && candidate <= LAB_SSD_CLIENT; - } - if (whole == LAB_SSD_PART_TOP) { - return candidate == LAB_SSD_PART_CORNER_TOP_LEFT || candidate == LAB_SSD_PART_CORNER_BOTTOM_LEFT; - } - if (whole == LAB_SSD_PART_RIGHT) { - return candidate == LAB_SSD_PART_CORNER_TOP_RIGHT || candidate == LAB_SSD_PART_CORNER_BOTTOM_RIGHT; - } - if (whole == LAB_SSD_PART_BOTTOM) { - return candidate == LAB_SSD_PART_CORNER_BOTTOM_RIGHT || candidate == LAB_SSD_PART_CORNER_BOTTOM_LEFT; - } - if (whole == LAB_SSD_PART_LEFT) { - return candidate == LAB_SSD_PART_CORNER_TOP_LEFT || candidate == LAB_SSD_PART_CORNER_BOTTOM_LEFT; - } - return false; -} diff --git a/src/ssd/meson.build b/src/ssd/meson.build new file mode 100644 index 00000000..95395419 --- /dev/null +++ b/src/ssd/meson.build @@ -0,0 +1,7 @@ +labwc_sources += files( + 'ssd.c', + 'ssd_part.c', + 'ssd_titlebar.c', + 'ssd_border.c', + 'ssd_extents.c', +) diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c new file mode 100644 index 00000000..14f0ee95 --- /dev/null +++ b/src/ssd/ssd.c @@ -0,0 +1,300 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Helpers for view server side decorations + * + * Copyright (C) Johan Malm 2020-2021 + */ + +#include +#include "config/rcxml.h" +#include "common/font.h" +#include "labwc.h" +#include "theme.h" +#include "ssd.h" + +/* TODO: use theme->title_height instead of SSD_HEIGHT */ +struct border +ssd_thickness(struct view *view) +{ + struct theme *theme = view->server->theme; + struct border border = { + .top = SSD_HEIGHT, + .bottom = theme->border_width, + .left = theme->border_width, + .right = theme->border_width, + }; + return border; +} + +struct wlr_box +ssd_max_extents(struct view *view) +{ + struct border border = ssd_thickness(view); + struct wlr_box box = { + .x = view->x - border.left, + .y = view->y - border.top, + .width = view->w + border.left + border.right, + .height = view->h + border.top + border.bottom, + }; + return box; +} + +bool +ssd_is_button(enum ssd_part_type type) +{ + return type == LAB_SSD_BUTTON_CLOSE + || type == LAB_SSD_BUTTON_MAXIMIZE + || type == LAB_SSD_BUTTON_ICONIFY + || type == LAB_SSD_BUTTON_WINDOW_MENU; +} + +enum ssd_part_type +ssd_get_part_type(struct view *view, struct wlr_scene_node *node) +{ + if (!node) { + return LAB_SSD_NONE; + } else if (node->type == WLR_SCENE_NODE_SURFACE) { + return LAB_SSD_CLIENT; + } else if (!view->ssd.tree) { + return LAB_SSD_NONE; + } + + struct wl_list *part_list = NULL; + struct wlr_scene_node *grandparent = + node->parent ? node->parent->parent : NULL; + + /* active titlebar */ + if (node->parent == &view->ssd.titlebar.active.tree->node) { + part_list = &view->ssd.titlebar.active.parts; + } else if (grandparent == &view->ssd.titlebar.active.tree->node) { + part_list = &view->ssd.titlebar.active.parts; + + /* extents */ + } else if (node->parent == &view->ssd.extents.tree->node) { + part_list = &view->ssd.extents.parts; + + /* active border */ + } else if (node->parent == &view->ssd.border.active.tree->node) { + part_list = &view->ssd.border.active.parts; + + /* inactive titlebar */ + } else if (node->parent == &view->ssd.titlebar.inactive.tree->node) { + part_list = &view->ssd.titlebar.inactive.parts; + } else if (grandparent == &view->ssd.titlebar.inactive.tree->node) { + part_list = &view->ssd.titlebar.inactive.parts; + + /* inactive border */ + } else if (node->parent == &view->ssd.border.inactive.tree->node) { + part_list = &view->ssd.border.inactive.parts; + } + + if (part_list) { + struct ssd_part *part; + wl_list_for_each(part, part_list, link) { + if (node == part->node) { + return part->type; + } + } + } + return LAB_SSD_NONE; +} + +enum ssd_part_type +ssd_at(struct view *view, double lx, double ly) +{ + double sx, sy; + struct wlr_scene_node *node = wlr_scene_node_at( + &view->server->scene->node, lx, ly, &sx, &sy); + return ssd_get_part_type(view, node); +} + +uint32_t +ssd_resize_edges(enum ssd_part_type type) +{ + switch (type) { + case LAB_SSD_PART_TOP: + return WLR_EDGE_TOP; + case LAB_SSD_PART_RIGHT: + return WLR_EDGE_RIGHT; + case LAB_SSD_PART_BOTTOM: + return WLR_EDGE_BOTTOM; + case LAB_SSD_PART_LEFT: + return WLR_EDGE_LEFT; + case LAB_SSD_PART_CORNER_TOP_LEFT: + return WLR_EDGE_TOP | WLR_EDGE_LEFT; + case LAB_SSD_PART_CORNER_TOP_RIGHT: + return WLR_EDGE_RIGHT | WLR_EDGE_TOP; + case LAB_SSD_PART_CORNER_BOTTOM_RIGHT: + return WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT; + case LAB_SSD_PART_CORNER_BOTTOM_LEFT: + return WLR_EDGE_BOTTOM | WLR_EDGE_LEFT; + default: + break; + } + return 0; +} + +static void +_ssd_set_active(struct ssd *ssd, bool active) +{ + wlr_scene_node_set_enabled(&ssd->border.active.tree->node, active); + wlr_scene_node_set_enabled(&ssd->titlebar.active.tree->node, active); + wlr_scene_node_set_enabled(&ssd->border.inactive.tree->node, !active); + wlr_scene_node_set_enabled(&ssd->titlebar.inactive.tree->node, !active); +} + +void +ssd_create(struct view *view) +{ + if (view->ssd.tree) { + /* SSD was hidden. Just enable it */ + wlr_log(WLR_ERROR, "Unhiding SSD"); + wlr_scene_node_set_enabled(&view->ssd.tree->node, true); + return; + } + + wlr_log(WLR_ERROR, "Creating SSD"); + view->ssd.tree = wlr_scene_tree_create(&view->scene_tree->node); + wlr_scene_node_lower_to_bottom(&view->ssd.tree->node); + ssd_extents_create(view); + ssd_border_create(view); + ssd_titlebar_create(view); +} + +void +ssd_update_geometry(struct view *view) +{ + /* TODO: verify we are not called without reason. like in commit handlers */ + if (!view->ssd.tree || !view->surface) { + return; + } + + if (view->ssd.enabled && !view->ssd.tree->node.state.enabled) { + wlr_scene_node_set_enabled(&view->ssd.tree->node, true); + } + if (!view->ssd.enabled && view->ssd.tree->node.state.enabled) { + wlr_scene_node_set_enabled(&view->ssd.tree->node, false); + } + + int width = view->surface->current.width; + int height = view->surface->current.height; + if (width == view->ssd.state.width && height == view->ssd.state.height) { + return; + } + ssd_extents_update(view); + ssd_border_update(view); + ssd_titlebar_update(view); + + view->ssd.state.width = width; + view->ssd.state.height = height; +} + +void +ssd_hide(struct view *view) +{ + if (!view->ssd.tree) { + return; + } + + wlr_log(WLR_ERROR, "Hiding SSD"); + wlr_scene_node_set_enabled(&view->ssd.tree->node, false); +} + +void ssd_reload(struct view *view) +{ + if (!view->ssd.tree) { + return; + } + + bool view_was_active = view->server->ssd_focused_view == view; + ssd_destroy(view); + ssd_create(view); + if (view_was_active) { + view->server->ssd_focused_view = view; + } else { + _ssd_set_active(&view->ssd, false); + } +} + +void +ssd_destroy(struct view *view) +{ + if (!view->ssd.tree) { + return; + } + + wlr_log(WLR_ERROR, "Destroying SSD"); + + /* Maybe reset focused view */ + if (view->server->ssd_focused_view == view) { + view->server->ssd_focused_view = NULL; + } + + /* Maybe reset hover view */ + struct ssd_hover_state *hover_state; + hover_state = &view->server->ssd_hover_state; + if (hover_state->view == view) { + hover_state->view = NULL; + hover_state->type = LAB_SSD_NONE; + hover_state->node = NULL; + } + + /* Destroy subcomponents */ + ssd_titlebar_destroy(view); + ssd_border_destroy(view); + ssd_extents_destroy(view); + wlr_scene_node_destroy(&view->ssd.tree->node); + view->ssd.tree = NULL; +} + +bool +ssd_part_contains(enum ssd_part_type whole, enum ssd_part_type candidate) +{ + if (whole == candidate) { + return true; + } + if (whole == LAB_SSD_PART_TITLEBAR) { + return candidate >= LAB_SSD_BUTTON_CLOSE + && candidate <= LAB_SSD_PART_TITLE; + } + if (whole == LAB_SSD_FRAME) { + return candidate >= LAB_SSD_BUTTON_CLOSE + && candidate <= LAB_SSD_CLIENT; + } + if (whole == LAB_SSD_PART_TOP) { + return candidate == LAB_SSD_PART_CORNER_TOP_LEFT + || candidate == LAB_SSD_PART_CORNER_BOTTOM_LEFT; + } + if (whole == LAB_SSD_PART_RIGHT) { + return candidate == LAB_SSD_PART_CORNER_TOP_RIGHT + || candidate == LAB_SSD_PART_CORNER_BOTTOM_RIGHT; + } + if (whole == LAB_SSD_PART_BOTTOM) { + return candidate == LAB_SSD_PART_CORNER_BOTTOM_RIGHT + || candidate == LAB_SSD_PART_CORNER_BOTTOM_LEFT; + } + if (whole == LAB_SSD_PART_LEFT) { + return candidate == LAB_SSD_PART_CORNER_TOP_LEFT + || candidate == LAB_SSD_PART_CORNER_BOTTOM_LEFT; + } + return false; +} + +void +ssd_set_active(struct view *view) +{ + if (!view->ssd.tree) { + return; + } + + struct view *last = view->server->ssd_focused_view; + if (last == view) { + return; + } + if (last && last->ssd.tree) { + _ssd_set_active(&last->ssd, false); + } + _ssd_set_active(&view->ssd, true); + view->server->ssd_focused_view = view; +} diff --git a/src/ssd/ssd_border.c b/src/ssd/ssd_border.c new file mode 100644 index 00000000..399d2c8c --- /dev/null +++ b/src/ssd/ssd_border.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "labwc.h" +#include "ssd.h" +#include "theme.h" +#include "common/scene-helpers.h" + +#define FOR_EACH_STATE(view, tmp) FOR_EACH(tmp, \ + &(view)->ssd.border.active, \ + &(view)->ssd.border.inactive) + +void +ssd_border_create(struct view *view) +{ + struct theme *theme = view->server->theme; + int width = view->surface->current.width; + int height = view->surface->current.height; + int full_width = width + 2 * theme->border_width; + + float *color; + struct wlr_scene_node *parent; + struct ssd_sub_tree *subtree; + + FOR_EACH_STATE(view, subtree) { + subtree->tree = wlr_scene_tree_create(&view->ssd.tree->node); + parent = &subtree->tree->node; + wlr_scene_node_set_position(parent, -theme->border_width, 0); + if (subtree == &view->ssd.border.active) { + color = theme->window_active_border_color; + } else { + color = theme->window_inactive_border_color; + wlr_scene_node_set_enabled(parent, false); + } + wl_list_init(&subtree->parts); + add_scene_rect(&subtree->parts, LAB_SSD_PART_LEFT, parent, + theme->border_width, height, 0, 0, color); + add_scene_rect(&subtree->parts, LAB_SSD_PART_RIGHT, parent, + theme->border_width, height, + theme->border_width + width, 0, color); + add_scene_rect(&subtree->parts, LAB_SSD_PART_BOTTOM, parent, + full_width, theme->border_width, + 0, height, color); + } FOR_EACH_END +} + +void +ssd_border_update(struct view *view) +{ + struct theme *theme = view->server->theme; + + int width = view->surface->current.width; + int height = view->surface->current.height; + int full_width = width + 2 * theme->border_width; + + struct ssd_part *part; + struct wlr_scene_rect *rect; + struct ssd_sub_tree *subtree; + FOR_EACH_STATE(view, subtree) { + wl_list_for_each(part, &subtree->parts, link) { + rect = lab_wlr_scene_get_rect(part->node); + switch (part->type) { + case LAB_SSD_PART_LEFT: + wlr_scene_rect_set_size(rect, theme->border_width, height); + continue; + case LAB_SSD_PART_RIGHT: + wlr_scene_rect_set_size(rect, theme->border_width, height); + wlr_scene_node_set_position( + part->node, theme->border_width + width, 0); + continue; + case LAB_SSD_PART_BOTTOM: + wlr_scene_rect_set_size(rect, full_width, theme->border_width); + wlr_scene_node_set_position(part->node, 0, height); + continue; + default: + continue; + } + } + } FOR_EACH_END +} + +void +ssd_border_destroy(struct view *view) +{ + if (!view->ssd.border.active.tree) { + return; + } + + struct ssd_sub_tree *subtree; + FOR_EACH_STATE(view, subtree) { + ssd_destroy_parts(&subtree->parts); + wlr_scene_node_destroy(&subtree->tree->node); + subtree->tree = NULL; + } FOR_EACH_END +} + +#undef FOR_EACH_STATE diff --git a/src/ssd/ssd_extents.c b/src/ssd/ssd_extents.c new file mode 100644 index 00000000..0175d767 --- /dev/null +++ b/src/ssd/ssd_extents.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "labwc.h" +#include "ssd.h" +#include "theme.h" +#include "common/scene-helpers.h" + +void +ssd_extents_create(struct view *view) +{ + struct theme *theme = view->server->theme; + float invisible[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + struct wl_list *part_list = &view->ssd.extents.parts; + int width = view->surface->current.width; + int height = view->surface->current.height; + int full_height = height + theme->border_width + SSD_HEIGHT; + int full_width = width + 2 * theme->border_width; + int extended_area = EXTENDED_AREA; + + view->ssd.extents.tree = wlr_scene_tree_create( + &view->ssd.tree->node); + struct wlr_scene_node *parent = + &view->ssd.extents.tree->node; + wlr_scene_node_set_position(parent, + -(theme->border_width + extended_area), -(SSD_HEIGHT + extended_area)); + wl_list_init(&view->ssd.extents.parts); + + /* Top */ + add_scene_rect(part_list, LAB_SSD_PART_CORNER_TOP_LEFT, parent, + extended_area, extended_area, + 0, 0, invisible); + add_scene_rect(part_list, LAB_SSD_PART_TOP, parent, + full_width, extended_area, + extended_area, 0, invisible); + add_scene_rect(part_list, LAB_SSD_PART_CORNER_TOP_RIGHT, parent, + extended_area, extended_area, + extended_area + full_width, 0, invisible); + + /* Sides */ + add_scene_rect(part_list, LAB_SSD_PART_LEFT, parent, + extended_area, full_height, + 0, extended_area, invisible); + add_scene_rect(part_list, LAB_SSD_PART_RIGHT, parent, + extended_area, full_height, + extended_area + full_width, extended_area, invisible); + + /* Bottom */ + add_scene_rect(part_list, LAB_SSD_PART_CORNER_BOTTOM_LEFT, parent, + extended_area, extended_area, + 0, extended_area + full_height, invisible); + add_scene_rect(part_list, LAB_SSD_PART_BOTTOM, parent, + full_width, extended_area, + extended_area, extended_area + full_height, invisible); + add_scene_rect(part_list, LAB_SSD_PART_CORNER_BOTTOM_RIGHT, parent, + extended_area, extended_area, + extended_area + full_width, extended_area + full_height, invisible); +} + +void +ssd_extents_update(struct view *view) +{ + struct theme *theme = view->server->theme; + + int width = view->surface->current.width; + int height = view->surface->current.height; + int full_height = height + theme->border_width + SSD_HEIGHT; + int full_width = width + 2 * theme->border_width; + int extended_area = EXTENDED_AREA; + + struct ssd_part *part; + struct wlr_scene_rect *rect; + wl_list_for_each(part, &view->ssd.extents.parts, link) { + rect = lab_wlr_scene_get_rect(part->node); + switch (part->type) { + case LAB_SSD_PART_TOP: + wlr_scene_rect_set_size(rect, full_width, extended_area); + continue; + case LAB_SSD_PART_CORNER_TOP_RIGHT: + wlr_scene_node_set_position( + part->node, extended_area + full_width, 0); + continue; + case LAB_SSD_PART_LEFT: + wlr_scene_rect_set_size(rect, extended_area, full_height); + continue; + case LAB_SSD_PART_RIGHT: + wlr_scene_rect_set_size(rect, extended_area, full_height); + wlr_scene_node_set_position( + part->node, extended_area + full_width, extended_area); + continue; + case LAB_SSD_PART_CORNER_BOTTOM_LEFT: + wlr_scene_node_set_position( + part->node, 0, extended_area + full_height); + continue; + case LAB_SSD_PART_BOTTOM: + wlr_scene_rect_set_size(rect, full_width, extended_area); + wlr_scene_node_set_position( + part->node, extended_area, extended_area + full_height); + continue; + case LAB_SSD_PART_CORNER_BOTTOM_RIGHT: + wlr_scene_node_set_position(part->node, + extended_area + full_width, extended_area + full_height); + continue; + default: + continue; + } + } +} + +void +ssd_extents_destroy(struct view *view) +{ + if (!view->ssd.extents.tree) { + return; + } + + ssd_destroy_parts(&view->ssd.extents.parts); + wlr_scene_node_destroy(&view->ssd.extents.tree->node); + view->ssd.extents.tree = NULL; +} diff --git a/src/ssd/ssd_part.c b/src/ssd/ssd_part.c new file mode 100644 index 00000000..ab718637 --- /dev/null +++ b/src/ssd/ssd_part.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include "labwc.h" +#include "ssd.h" + +struct ssd_part * +add_scene_part(struct wl_list *part_list, enum ssd_part_type type) +{ + struct ssd_part *part = calloc(1, sizeof(struct ssd_part)); + part->type = type; + wl_list_insert(part_list->prev, &part->link); + return part; +} + +struct ssd_part * +add_scene_rect(struct wl_list *list, enum ssd_part_type type, + struct wlr_scene_node *parent, int width, int height, + int x, int y, float color[4]) +{ + struct ssd_part *part = add_scene_part(list, type); + part->node = &wlr_scene_rect_create( + parent, width, height, color)->node; + wlr_scene_node_set_position(part->node, x, y); + return part; +} + +struct ssd_part * +add_scene_buffer(struct wl_list *list, enum ssd_part_type type, + struct wlr_scene_node *parent, struct wlr_buffer *buffer, + int x, int y) +{ + struct ssd_part *part = add_scene_part(list, type); + part->node = &wlr_scene_buffer_create(parent, buffer)->node; + wlr_scene_node_set_position(part->node, x, y); + return part; +} + +static void +finish_scene_button(struct wl_list *part_list, enum ssd_part_type type, + struct wlr_scene_node *parent, struct wlr_buffer *icon_buffer) +{ + float hover_bg[4] = {0.15f, 0.15f, 0.15f, 0.3f}; + + /* Icon */ + add_scene_buffer(part_list, type, parent, icon_buffer, + (BUTTON_WIDTH - icon_buffer->width) / 2, + (SSD_HEIGHT - icon_buffer->height) / 2); + + /* Hover overlay */ + struct ssd_part *hover_part; + hover_part = add_scene_rect(part_list, type, parent, + BUTTON_WIDTH, SSD_HEIGHT, 0, 0, hover_bg); + wlr_scene_node_set_enabled(hover_part->node, false); +} + +struct ssd_part * +add_scene_button_corner(struct wl_list *part_list, enum ssd_part_type type, + struct wlr_scene_node *parent, struct wlr_buffer *corner_buffer, + struct wlr_buffer *icon_buffer, int x) +{ + struct ssd_part *part; + /* Background */ + part = add_scene_buffer(part_list, type, parent, corner_buffer, x, 0); + finish_scene_button(part_list, type, part->node, icon_buffer); + return part; +} + +struct ssd_part * +add_scene_button(struct wl_list *part_list, enum ssd_part_type type, + struct wlr_scene_node *parent, float *bg_color, + struct wlr_buffer *icon_buffer, int x) +{ + struct ssd_part *part; + /* Background */ + part = add_scene_rect(part_list, type, parent, + BUTTON_WIDTH, SSD_HEIGHT, x, 0, bg_color); + finish_scene_button(part_list, type, part->node, icon_buffer); + return part; +} + +struct ssd_part * +ssd_get_part(struct wl_list *part_list, enum ssd_part_type type) +{ + struct ssd_part *part; + wl_list_for_each(part, part_list, link) { + if (part->type == type) { + return part; + } + } + return NULL; +} + +void +ssd_destroy_parts(struct wl_list *list) +{ + struct ssd_part *part, *tmp; + wl_list_for_each_reverse_safe(part, tmp, list, link) { + if (part->node) { + wlr_scene_node_destroy(part->node); + } + if (part->buffer) { + wlr_buffer_drop(&part->buffer->base); + } + wl_list_remove(&part->link); + free(part); + } + assert(wl_list_empty(list)); +} diff --git a/src/ssd/ssd_titlebar.c b/src/ssd/ssd_titlebar.c new file mode 100644 index 00000000..16e1785b --- /dev/null +++ b/src/ssd/ssd_titlebar.c @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#define _POSIX_C_SOURCE 200809L +#include +#include +#include "labwc.h" +#include "ssd.h" +#include "theme.h" +#include "common/font.h" +#include "common/scene-helpers.h" + +#define FOR_EACH_STATE(view, tmp) FOR_EACH(tmp, \ + &(view)->ssd.titlebar.active, \ + &(view)->ssd.titlebar.inactive) + +void +ssd_titlebar_create(struct view *view) +{ + struct theme *theme = view->server->theme; + int width = view->surface->current.width; + int full_width = width + 2 * theme->border_width; + + float *color; + struct wlr_scene_node *parent; + struct wlr_buffer *corner_top_left; + struct wlr_buffer *corner_top_right; + + struct ssd_sub_tree *subtree; + FOR_EACH_STATE(view, subtree) { + subtree->tree = wlr_scene_tree_create(&view->ssd.tree->node); + parent = &subtree->tree->node; + wlr_scene_node_set_position(parent, -theme->border_width, -SSD_HEIGHT); + if (subtree == &view->ssd.titlebar.active) { + color = theme->window_active_title_bg_color; + corner_top_left = &theme->corner_top_left_active_normal->base; + corner_top_right = &theme->corner_top_right_active_normal->base; + } else { + color = theme->window_inactive_title_bg_color; + corner_top_left = &theme->corner_top_left_inactive_normal->base; + corner_top_right = &theme->corner_top_right_inactive_normal->base; + wlr_scene_node_set_enabled(parent, false); + } + wl_list_init(&subtree->parts); + + /* Title */ + add_scene_rect(&subtree->parts, LAB_SSD_PART_TITLEBAR, parent, + full_width - BUTTON_WIDTH * BUTTON_COUNT, SSD_HEIGHT, + BUTTON_WIDTH, 0, color); + /* Buttons */ + add_scene_button_corner(&subtree->parts, LAB_SSD_BUTTON_WINDOW_MENU, + parent, corner_top_left, + &theme->xbm_menu_active_unpressed->base, 0); + add_scene_button(&subtree->parts, LAB_SSD_BUTTON_ICONIFY, parent, + color, &theme->xbm_iconify_active_unpressed->base, + full_width - BUTTON_WIDTH * 3); + add_scene_button(&subtree->parts, LAB_SSD_BUTTON_MAXIMIZE, parent, + color, &theme->xbm_maximize_active_unpressed->base, + full_width - BUTTON_WIDTH * 2); + add_scene_button_corner(&subtree->parts, LAB_SSD_BUTTON_CLOSE, parent, + corner_top_right, &theme->xbm_close_active_unpressed->base, + full_width - BUTTON_WIDTH * 1); + } FOR_EACH_END + ssd_update_title(view); +} + +static bool +is_direct_child(struct wlr_scene_node *node, struct ssd_sub_tree *subtree) +{ + return node->parent == &subtree->tree->node; +} + +void +ssd_titlebar_update(struct view *view) +{ + int width = view->surface->current.width; + if (width == view->ssd.state.width) { + return; + } + int full_width = width + 2 * view->server->theme->border_width; + + struct ssd_part *part; + struct ssd_sub_tree *subtree; + FOR_EACH_STATE(view, subtree) { + wl_list_for_each(part, &subtree->parts, link) { + switch (part->type) { + case LAB_SSD_PART_TITLEBAR: + wlr_scene_rect_set_size(lab_wlr_scene_get_rect(part->node), + full_width - BUTTON_WIDTH * BUTTON_COUNT, SSD_HEIGHT); + continue; + case LAB_SSD_BUTTON_ICONIFY: + if (is_direct_child(part->node, subtree)) { + wlr_scene_node_set_position(part->node, + full_width - BUTTON_WIDTH * 3, 0); + } + continue; + case LAB_SSD_BUTTON_MAXIMIZE: + if (is_direct_child(part->node, subtree)) { + wlr_scene_node_set_position(part->node, + full_width - BUTTON_WIDTH * 2, 0); + } + continue; + case LAB_SSD_BUTTON_CLOSE: + if (is_direct_child(part->node, subtree)) { + wlr_scene_node_set_position(part->node, + full_width - BUTTON_WIDTH * 1, 0); + } + continue; + default: + continue; + } + } + } FOR_EACH_END + ssd_update_title(view); +} + +void +ssd_titlebar_destroy(struct view *view) +{ + if (!view->ssd.titlebar.active.tree) { + return; + } + + struct ssd_sub_tree *subtree; + FOR_EACH_STATE(view, subtree) { + ssd_destroy_parts(&subtree->parts); + wlr_scene_node_destroy(&subtree->tree->node); + subtree->tree = NULL; + } FOR_EACH_END + + if (view->ssd.state.title.text) { + free(view->ssd.state.title.text); + view->ssd.state.title.text = NULL; + } +} + +/* + * For ssd_update_title* we do not early out because + * .active and .inactive may result in different sizes + * of the title (font family/size) or background of + * the title (different button/border width). + */ + +static void +ssd_update_title_positions(struct view *view) +{ + struct theme *theme = view->server->theme; + int width = view->surface->current.width; + int full_width = width + 2 * view->server->theme->border_width; + + int x, y; + struct wlr_scene_rect *rect; + struct ssd_part *part; + struct ssd_sub_tree *subtree; + FOR_EACH_STATE(view, subtree) { + part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLE); + if (!part) { + wlr_log(WLR_ERROR, + "Failed to position SSD title: title node not found"); + continue; + } + + x = 0; + y = (SSD_HEIGHT - part->buffer->base.height) / 2; + rect = lab_wlr_scene_get_rect(part->node->parent); + if (rect->width <= 0) { + wlr_log(WLR_ERROR, + "Failed to position SSD title: not enough screen space"); + wlr_scene_node_set_position(part->node, x, y); + continue; + } + + if (theme->window_label_text_justify == LAB_JUSTIFY_CENTER) { + if (part->buffer->base.width + BUTTON_WIDTH * 2 <= rect->width) { + /* Center based on the full width */ + x = (full_width - part->buffer->base.width) / 2; + x -= BUTTON_WIDTH; + } else { + /* + * Center based on the width between the buttons. + * Title jumps around once this is hit but its still + * better than to hide behind the buttons on the right. + */ + x = (rect->width - part->buffer->base.width) / 2; + } + } else if (theme->window_label_text_justify == LAB_JUSTIFY_RIGHT) { + x = rect->width - part->buffer->base.width; + } else if (theme->window_label_text_justify == LAB_JUSTIFY_LEFT) { + /* TODO: maybe add some theme x padding here? */ + } + wlr_scene_node_set_position(part->node, x, y); + } FOR_EACH_END +} + +void +ssd_update_title(struct view *view) +{ + if (!view->ssd.tree) { + return; + } + + char *title = (char *)view_get_string_prop(view, "title"); + if (!title || !*title) { + return; + } + + struct theme *theme = view->server->theme; + struct ssd_state_title *state = &view->ssd.state.title; + bool title_unchanged = state->text && !strcmp(title, state->text); + + /* TODO: Do we only have active window fonts? */ + struct font font = { + .name = rc.font_name_activewindow, + .size = rc.font_size_activewindow, + }; + + float *text_color; + struct wlr_scene_rect *rect; + struct ssd_part *part; + struct ssd_part *parent_part; + struct ssd_sub_tree *subtree; + struct ssd_state_title_width *dstate; + FOR_EACH_STATE(view, subtree) { + parent_part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR); + if (!parent_part) { + wlr_log(WLR_ERROR, + "Failed to update SSD title: parent node not found"); + continue; + } + rect = lab_wlr_scene_get_rect(parent_part->node); + if (rect->width <= 0) { + wlr_log(WLR_ERROR, + "Failed to update SSD title: not enough screen space"); + continue; + } + if (subtree == &view->ssd.titlebar.active) { + dstate = &state->active; + text_color = theme->window_active_label_text_color; + } else { + dstate = &state->inactive; + text_color = theme->window_inactive_label_text_color; + } + if (title_unchanged + && !dstate->truncated && dstate->width < rect->width) { + /* title the same + we don't need to resize title */ + continue; + } + part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLE); + if (!part) { + part = add_scene_part(&subtree->parts, LAB_SSD_PART_TITLE); + } + if (part->node) { + wlr_scene_node_destroy(part->node); + } + font_buffer_update( + &part->buffer, rect->width, title, &font, text_color); + part->node = &wlr_scene_buffer_create( + parent_part->node, &part->buffer->base)->node; + dstate->width = part->buffer->base.width; + dstate->truncated = rect->width <= dstate->width; + } FOR_EACH_END + + if (state->text) { + free(state->text); + } + state->text = strdup(title); + ssd_update_title_positions(view); +} + +/* + * Returns the wlr_scene_node for hover effect. + * To disable the hover effect later on just call + * wlr_scene_node_set_enabled(node, false). + */ +struct wlr_scene_node * +ssd_button_hover_enable(struct view *view, enum ssd_part_type type) +{ + if (!view->ssd.tree) { + wlr_log(WLR_ERROR, "%s() for destroyed view", __func__); + return NULL; + } + + assert(ssd_is_button(type)); + struct ssd_part *part; + struct ssd_sub_tree *subtree; + FOR_EACH_STATE(view, subtree) { + if (subtree->tree->node.state.enabled) { + part = ssd_get_part(&subtree->parts, type); + if (!part) { + wlr_log(WLR_ERROR, "hover enable failed to find button"); + return NULL; + } + struct wlr_scene_node *child; + wl_list_for_each(child, &part->node->state.children, state.link) { + if (child->type == WLR_SCENE_NODE_RECT) { + wlr_scene_node_set_enabled(child, true); + return child; + } + } + } + } FOR_EACH_END + + wlr_log(WLR_ERROR, "hover enable failed to find button"); + return NULL; +} + +#undef FOR_EACH_STATE diff --git a/src/theme.c b/src/theme.c index 948a6259..5a4a6e5f 100644 --- a/src/theme.c +++ b/src/theme.c @@ -26,6 +26,7 @@ #include "theme.h" #include "xbm/xbm.h" #include "buffer.h" +#include "ssd.h" static int hex_to_dec(char c) @@ -417,12 +418,11 @@ rounded_rect(struct rounded_corner_ctx *ctx) static void create_corners(struct theme *theme) { - int corner_square = theme->title_height + theme->border_width; struct wlr_box box = { .x = 0, .y = 0, - .width = corner_square, - .height = corner_square, + .width = BUTTON_WIDTH, + .height = SSD_HEIGHT, }; struct rounded_corner_ctx ctx = { diff --git a/src/view.c b/src/view.c index 34c04fca..64674482 100644 --- a/src/view.c +++ b/src/view.c @@ -7,6 +7,9 @@ void view_set_activated(struct view *view, bool activated) { + if (view->ssd.enabled) { + ssd_set_active(view); + } if (view->impl->set_activated) { view->impl->set_activated(view, activated); } @@ -258,7 +261,7 @@ view_toggle_decorations(struct view *view) { if (!view->fullscreen) { view->ssd.enabled = !view->ssd.enabled; - ssd_update_geometry(view, true); + ssd_update_geometry(view); if (view->maximized) { view_apply_maximized_geometry(view); } @@ -270,7 +273,7 @@ view_set_decorations(struct view *view, bool decorations) { if (view->ssd.enabled != decorations && !view->fullscreen) { view->ssd.enabled = decorations; - ssd_update_geometry(view, true); + ssd_update_geometry(view); if (view->maximized) { view_apply_maximized_geometry(view); } diff --git a/src/xdg.c b/src/xdg.c index fc78c6b5..ee06781c 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -58,7 +58,7 @@ handle_commit(struct wl_listener *listener, void *data) view->pending_move_resize.configure_serial = 0; } } - ssd_update_geometry(view, false); + ssd_update_geometry(view); damage_view_part(view); } @@ -187,8 +187,8 @@ xdg_toplevel_view_configure(struct view *view, struct wlr_box geo) } else if (view->pending_move_resize.configure_serial == 0) { view->x = geo.x; view->y = geo.y; - ssd_update_geometry(view, false); - damage_all_outputs(view->server); + ssd_update_geometry(view); + //damage_all_outputs(view->server); } } #undef MAX @@ -198,8 +198,6 @@ xdg_toplevel_view_move(struct view *view, double x, double y) { view->x = x; view->y = y; - ssd_update_geometry(view, false); - damage_all_outputs(view->server); } static void @@ -344,6 +342,7 @@ xdg_toplevel_view_unmap(struct view *view) view->mapped = false; damage_all_outputs(view->server); wlr_scene_node_destroy(view->scene_node); + ssd_hide(view); wl_list_remove(&view->commit.link); desktop_focus_topmost_mapped_view(view->server); } @@ -390,9 +389,9 @@ xdg_surface_new(struct wl_listener *listener, void *data) view->type = LAB_XDG_SHELL_VIEW; view->impl = &xdg_toplevel_view_impl; view->xdg_surface = xdg_surface; - wl_list_init(&view->ssd.parts); view->scene_tree = wlr_scene_tree_create(&view->server->view_tree->node); + view->scene_node = wlr_scene_xdg_surface_create( &view->scene_tree->node, view->xdg_surface); if (!view->scene_node) { diff --git a/src/xwayland.c b/src/xwayland.c index c72fe471..5560ba7e 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -23,7 +23,7 @@ handle_commit(struct wl_listener *listener, void *data) view->pending_move_resize.height - view->h; view->pending_move_resize.update_y = false; } - ssd_update_geometry(view, false); + ssd_update_geometry(view); damage_view_whole(view); } @@ -188,8 +188,6 @@ move(struct view *view, double x, double y) struct wlr_xwayland_surface *s = view->xwayland_surface; wlr_xwayland_surface_configure(s, (int16_t)x, (int16_t)y, (uint16_t)s->width, (uint16_t)s->height); - ssd_update_geometry(view, false); - damage_all_outputs(view->server); } static void @@ -313,6 +311,7 @@ unmap(struct view *view) view->mapped = false; damage_all_outputs(view->server); wl_list_remove(&view->commit.link); + ssd_hide(view); desktop_focus_topmost_mapped_view(view->server); } } @@ -376,7 +375,6 @@ xwayland_surface_new(struct wl_listener *listener, void *data) view->type = LAB_XWAYLAND_VIEW; view->impl = &xwl_view_impl; view->xwayland_surface = xsurface; - wl_list_init(&view->ssd.parts); xsurface->data = view;