]> git.mdlowis.com Git - proto/labwc.git/commitdiff
Convert SSD to scene nodes
authorConsolatis <35009135+Consolatis@users.noreply.github.com>
Mon, 21 Feb 2022 02:18:38 +0000 (03:18 +0100)
committerJohan Malm <jgm323@gmail.com>
Wed, 23 Feb 2022 21:47:01 +0000 (21:47 +0000)
23 files changed:
include/buffer.h
include/common/scene-helpers.h [new file with mode: 0644]
include/config/mousebind.h
include/labwc.h
include/ssd.h
src/buffer.c
src/common/meson.build
src/common/scene-helpers.c [new file with mode: 0644]
src/cursor.c
src/desktop.c
src/meson.build
src/server.c
src/ssd.c [deleted file]
src/ssd/meson.build [new file with mode: 0644]
src/ssd/ssd.c [new file with mode: 0644]
src/ssd/ssd_border.c [new file with mode: 0644]
src/ssd/ssd_extents.c [new file with mode: 0644]
src/ssd/ssd_part.c [new file with mode: 0644]
src/ssd/ssd_titlebar.c [new file with mode: 0644]
src/theme.c
src/view.c
src/xdg.c
src/xwayland.c

index 3ffec484aef7408e131e1014b3690e11f9845d37..af833f004a070d4da2f824e17c7651fdf77656db 100644 (file)
@@ -27,7 +27,7 @@
 #define __LABWC_BUFFER_H
 
 #include <cairo.h>
-#include "labwc.h"
+#include <wlr/types/wlr_buffer.h>
 
 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 (file)
index 0000000..a3aa032
--- /dev/null
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <wlr/types/wlr_scene.h>
+
+struct wlr_scene_rect *lab_wlr_scene_get_rect(struct wlr_scene_node *node);
index e2da203c2fab4c321890ac08e4e9a75fd22e6d6d..ae66532adbf159812553a26f7fea3f1ef72db91a 100644 (file)
@@ -2,8 +2,9 @@
 #ifndef __LABWC_MOUSEBIND_H
 #define __LABWC_MOUSEBIND_H
 
-#include "ssd.h"
 #include <wayland-util.h>
+#include "ssd.h"
+#include "config/keybind.h"
 
 enum mouse_event {
        MOUSE_ACTION_NONE = 0,
index 2d9cb239761aeea9469f77dbe20ff2151ba02f1e..c54b1f239e69ea529db7cfc70168365460b83cbb 100644 (file)
@@ -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;
index d711c060e74e785ed9e710fe37e5a648600fb3a8..0f4473eb6c892eea0c9b16003082e2a88fcec536 100644 (file)
@@ -2,6 +2,22 @@
 #ifndef __LABWC_SSD_H
 #define __LABWC_SSD_H
 
+#include "buffer.h"
+#include <wlr/util/box.h>
+
+#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_part {
-       struct wlr_box box;
-       enum ssd_part_type type;
+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;
 
        /*
-        * 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.
+        * Cache for current values.
+        * Used to detect actual changes so we
+        * don't update things we don't have to.
         */
        struct {
-               struct wlr_texture **active;
-               struct wlr_texture **inactive;
-       } texture;
+               int width;
+               int height;
+               struct ssd_state_title title;
+       } state;
 
-       /*
-        * If a part does not contain textures, it'll just be rendered as a
-        * rectangle with the following colors.
-        */
+       /* An invisble area around the view which allows resizing */
+       struct ssd_sub_tree extents;
+
+       /* The top of the view, containing buttons, title, .. */
        struct {
-               float *active;
-               float *inactive;
-       } color;
+               /* 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 {
+       enum ssd_part_type type;
+
+       /* Buffer pointer. May be NULL */
+       struct lab_data_buffer *buffer;
+
+       /* 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;
+};
 
-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);
+/* 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);
-void ssd_update_geometry(struct view *view, bool force);
-bool ssd_part_contains(enum ssd_part_type whole, enum ssd_part_type candidate);
+/* 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);
 
 #endif /* __LABWC_SSD_H */
index 3a70aa299a058833859ae9af72720cc9deb9d0df..2415d50951d1d65a58e9086ab0c91114079ad034 100644 (file)
@@ -26,6 +26,7 @@
 
 #include "config.h"
 #include <assert.h>
+#include <stdlib.h>
 #include <drm_fourcc.h>
 #include "buffer.h"
 
index 02c763aaf4e249072e6d60628ead876a8fb1dafc..fdfa72b28a62075de0fdb9ddc8431c46336b1583 100644 (file)
@@ -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 (file)
index 0000000..a973a69
--- /dev/null
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <assert.h>
+#include <wlr/types/wlr_scene.h>
+
+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;
+}
index adb68d3ff8b1f22b512d9336dc5ebec84abbf529..4751916e489726db0e150f21cf73502b646ead90 100644 (file)
@@ -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 &&
index d17544dd8e8e81a4fb8627dd3bad585ca171aaf3..51a3921032ed576e382338c86c04013abf5361ab 100644 (file)
@@ -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;
 }
 
index 1f9c51469ebee9937b79a967b7364f3358957ea4..b318de8fa531206f9372567f300faf8063eb1e61 100644 (file)
@@ -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')
index 60c6c4e8ab07416fffaf8278a99e0e0aa6f26ac9..9e279aa63ddd3d25c180ab4ffefafdf95f3847b9 100644 (file)
@@ -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 (file)
index 3edfd3b..0000000
--- 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 <assert.h>
-#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 (file)
index 0000000..9539541
--- /dev/null
@@ -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 (file)
index 0000000..14f0ee9
--- /dev/null
@@ -0,0 +1,300 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Helpers for view server side decorations
+ *
+ * Copyright (C) Johan Malm 2020-2021
+ */
+
+#include <assert.h>
+#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 (file)
index 0000000..399d2c8
--- /dev/null
@@ -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 (file)
index 0000000..0175d76
--- /dev/null
@@ -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 (file)
index 0000000..ab71863
--- /dev/null
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <assert.h>
+#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 (file)
index 0000000..16e1785
--- /dev/null
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define _POSIX_C_SOURCE 200809L
+#include <assert.h>
+#include <string.h>
+#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
index 948a6259ec703274eb8803d8c51050cc6d10bae9..5a4a6e5f514cb6f6f69448472f771ef9df8f3c06 100644 (file)
@@ -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 = {
index 34c04fca1bc4b681a5c98d2cdff599cde5c76256..64674482c08fd3dabcaff92b4c0ae301a0c6491d 100644 (file)
@@ -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);
                }
index fc78c6b533151d26ae21d8f68437ddcd1c376e89..ee06781c81989fb9e60eef0b918e4325491f0edf 100644 (file)
--- 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) {
index c72fe4718a3a9be2caa3905ea668fbe305606e62..5560ba7efc9e279724f347d27ca8f56e8954cda6 100644 (file)
@@ -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;