From d2552232c754f4e53fdc1289956aad3d0b618176 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Fri, 11 Feb 2022 22:19:05 +0000 Subject: [PATCH 01/56] Add buffer.c to cover cairo-surface to wlr_buffer --- include/buffer.h | 60 +++++++++++++++++++++++++ src/buffer.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++ src/meson.build | 1 + 3 files changed, 174 insertions(+) create mode 100644 include/buffer.h create mode 100644 src/buffer.c diff --git a/include/buffer.h b/include/buffer.h new file mode 100644 index 00000000..8c141f8a --- /dev/null +++ b/include/buffer.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Based on wlroots/include/types/wlr_buffer.c + * + * Copyright (c) 2017, 2018 Drew DeVault + * Copyright (c) 2018-2021 Simon Ser, Simon Zeni + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __LABWC_BUFFER_H +#define __LABWC_BUFFER_H + +#include "labwc.h" + +/** + * A read-only buffer that holds a data pointer. + * + * This is suitable for passing raw pixel data to a function that accepts a + * wlr_buffer. + */ +struct lab_data_buffer { + struct wlr_buffer base; + + const void *data; + uint32_t format; + size_t stride; + + void *saved_data; +}; + +/** + * Wraps a read-only data pointer into a wlr_buffer. The data pointer may be + * accessed until readonly_data_buffer_drop() is called. + */ +struct lab_data_buffer *buffer_create(uint32_t format, size_t stride, + uint32_t width, uint32_t height, const void *data); + +/** + * Drops ownership of the buffer (see wlr_buffer_drop() for more details) and + * perform a copy of the data pointer if a consumer still has the buffer locked. + */ +bool buffer_drop(struct lab_data_buffer *buffer); + +#endif /* __LABWC_BUFFER_H */ diff --git a/src/buffer.c b/src/buffer.c new file mode 100644 index 00000000..0188ee01 --- /dev/null +++ b/src/buffer.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Based on wlroots/types/wlr_buffer.c + * + * Copyright (c) 2017, 2018 Drew DeVault + * Copyright (c) 2018-2021 Simon Ser, Simon Zeni + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" +#include +#include +#include "buffer.h" + +static const struct wlr_buffer_impl data_buffer_impl; + +static struct lab_data_buffer * +data_buffer_from_buffer(struct wlr_buffer *buffer) +{ + assert(buffer->impl == &data_buffer_impl); + return (struct lab_data_buffer *)buffer; +} + +static void +data_buffer_destroy(struct wlr_buffer *wlr_buffer) +{ + struct lab_data_buffer *buffer = data_buffer_from_buffer(wlr_buffer); + free(buffer->saved_data); + free(buffer); +} + +static bool +data_buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, uint32_t flags, + void **data, uint32_t *format, size_t *stride) +{ + struct lab_data_buffer *buffer = data_buffer_from_buffer(wlr_buffer); + if (buffer->data == NULL) { + return false; + } + if (flags & WLR_BUFFER_DATA_PTR_ACCESS_WRITE) { + return false; + } + *data = (void *)buffer->data; + *format = buffer->format; + *stride = buffer->stride; + return true; +} + +static void +data_buffer_end_data_ptr_access(struct wlr_buffer *wlr_buffer) +{ + /* noop */ +} + +static const struct wlr_buffer_impl data_buffer_impl = { + .destroy = data_buffer_destroy, + .begin_data_ptr_access = data_buffer_begin_data_ptr_access, + .end_data_ptr_access = data_buffer_end_data_ptr_access, +}; + +struct lab_data_buffer * +buffer_create(uint32_t format, size_t stride, uint32_t width, uint32_t height, + const void *data) +{ + struct lab_data_buffer *buffer = calloc(1, sizeof(*buffer)); + if (buffer == NULL) { + return NULL; + } + wlr_buffer_init(&buffer->base, &data_buffer_impl, width, height); + buffer->data = data; + buffer->format = format; + buffer->stride = stride; + return buffer; +} + + +bool +buffer_drop(struct lab_data_buffer *buffer) +{ + bool ok = true; + + if (buffer->base.n_locks > 0) { + size_t size = buffer->stride * buffer->base.height; + buffer->saved_data = malloc(size); + if (!buffer->saved_data) { + wlr_log_errno(WLR_ERROR, "allocation failed"); + ok = false; + buffer->data = NULL; + } else { + memcpy(buffer->saved_data, buffer->data, size); + buffer->data = buffer->saved_data; + } + } + wlr_buffer_drop(&buffer->base); + return ok; +} diff --git a/src/meson.build b/src/meson.build index cb761362..536b621a 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,5 +1,6 @@ labwc_sources = files( 'action.c', + 'buffer.c', 'cursor.c', 'damage.c', 'desktop.c', From 532656ad5b47d4326f53953698796275a3f733e1 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Fri, 11 Feb 2022 23:12:45 +0000 Subject: [PATCH 02/56] Use wlroots scene-graph API Move xdg-shell and xwayland-shell surfaces to new API Also render alt-tab on-screen-display by converting cairo-surface to wlr_buffer --- include/labwc.h | 60 +-- src/cursor.c | 91 ++-- src/damage.c | 16 - src/desktop.c | 172 +------ src/keyboard.c | 3 + src/meson.build | 2 - src/osd.c | 25 +- src/output.c | 956 +-------------------------------------- src/server.c | 12 +- src/subsurface.c | 36 -- src/view-child.c | 42 -- src/view.c | 42 +- src/xdg-popup.c | 74 +-- src/xdg.c | 81 ++-- src/xwayland-unmanaged.c | 22 + src/xwayland.c | 20 +- 16 files changed, 189 insertions(+), 1465 deletions(-) delete mode 100644 src/subsurface.c delete mode 100644 src/view-child.c diff --git a/include/labwc.h b/include/labwc.h index 2747aa9e..439ddff8 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -24,9 +25,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -111,6 +112,8 @@ struct seat { struct wl_listener idle_inhibitor_create; }; +struct lab_data_buffer; + struct server { struct wl_display *wl_display; struct wlr_renderer *renderer; @@ -137,6 +140,10 @@ struct server { struct wl_list unmanaged_surfaces; struct seat seat; + struct wlr_scene *scene; + + /* Tree for all non-layer xdg/xwayland-shell surfaces */ + struct wlr_scene_tree *view_tree; /* cursor interactive */ enum input_mode input_mode; @@ -145,6 +152,8 @@ struct server { struct wlr_box grab_box; uint32_t resize_edges; + struct wlr_scene_tree *osd_tree; + struct wl_list outputs; struct wl_listener new_output; struct wlr_output_layout *output_layout; @@ -172,14 +181,14 @@ struct output { struct wl_list link; /* server::outputs */ struct server *server; struct wlr_output *wlr_output; - struct wlr_output_damage *damage; + struct wlr_scene_output *scene_output; struct wl_list layers[4]; struct wlr_box usable_area; - struct wlr_texture *osd; + + struct lab_data_buffer *osd_buffer; struct wl_listener destroy; - struct wl_listener damage_frame; - struct wl_listener damage_destroy; + struct wl_listener frame; }; enum view_type { @@ -192,10 +201,6 @@ enum view_type { struct view_impl { void (*configure)(struct view *view, struct wlr_box geo); void (*close)(struct view *view); - void (*for_each_popup_surface)(struct view *view, - wlr_surface_iterator_func_t iterator, void *data); - void (*for_each_surface)(struct view *view, - wlr_surface_iterator_func_t iterator, void *data); const char *(*get_string_prop)(struct view *view, const char *prop); void (*map)(struct view *view); void (*move)(struct view *view, double x, double y); @@ -226,6 +231,7 @@ struct view { #endif }; struct wlr_surface *surface; + struct wlr_scene_node *scene_node; bool mapped; bool been_mapped; @@ -296,7 +302,6 @@ struct view { struct wl_listener set_app_id; /* class on xwayland */ struct wl_listener set_decorations; /* xwayland only */ struct wl_listener new_popup; /* xdg-shell only */ - struct wl_listener new_subsurface; /* xdg-shell only */ }; #if HAVE_XWAYLAND @@ -314,29 +319,6 @@ struct xwayland_unmanaged { }; #endif -struct view_child { - struct wlr_surface *surface; - struct view *parent; - struct wl_listener commit; - struct wl_listener new_subsurface; -}; - -struct view_subsurface { - struct view_child view_child; - struct wlr_subsurface *subsurface; - struct wl_listener destroy; -}; - -struct xdg_popup { - struct view_child view_child; - struct wlr_xdg_popup *wlr_popup; - - struct wl_listener destroy; - struct wl_listener map; - struct wl_listener unmap; - struct wl_listener new_popup; -}; - struct constraint { struct seat *seat; struct wlr_pointer_constraint_v1 *constraint; @@ -361,12 +343,6 @@ void xwayland_unmanaged_create(struct server *server, struct wlr_xwayland_surface *xsurface); #endif -void view_child_init(struct view_child *child, struct view *view, - struct wlr_surface *wlr_surface); -void view_child_finish(struct view_child *child); -void view_subsurface_create(struct view *view, - struct wlr_subsurface *wlr_subsurface); - void view_set_activated(struct view *view, bool activated); void view_close(struct view *view); struct border view_border(struct view *view); @@ -393,10 +369,6 @@ void view_toggle_decorations(struct view *view); void view_set_decorations(struct view *view, bool decorations); void view_toggle_fullscreen(struct view *view); void view_adjust_for_layout_change(struct view *view); -void view_for_each_surface(struct view *view, - wlr_surface_iterator_func_t iterator, void *user_data); -void view_for_each_popup_surface(struct view *view, - wlr_surface_iterator_func_t iterator, void *data); void view_discover_output(struct view *view); void view_move_to_edge(struct view *view, const char *direction); void view_snap_to_edge(struct view *view, const char *direction); @@ -503,7 +475,7 @@ void server_init(struct server *server); void server_start(struct server *server); void server_finish(struct server *server); -/* update onscreen display 'alt-tab' texture */ +/* update onscreen display 'alt-tab' buffer */ void osd_update(struct server *server); /* diff --git a/src/cursor.c b/src/cursor.c index 8e3227c7..37668dcf 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -12,25 +12,25 @@ #include "ssd.h" #include "config/mousebind.h" -void -cursor_rebase(struct seat *seat, uint32_t time_msec) -{ - double sx, sy; - struct wlr_surface *surface; - enum ssd_part_type view_area = LAB_SSD_NONE; - - desktop_surface_and_view_at(seat->server, seat->cursor->x, - seat->cursor->y, &surface, &sx, &sy, &view_area); - - if (surface) { - wlr_seat_pointer_notify_clear_focus(seat->seat); - wlr_seat_pointer_notify_enter(seat->seat, surface, sx, sy); - wlr_seat_pointer_notify_motion(seat->seat, time_msec, sx, sy); - } else { - cursor_set(seat, "left_ptr"); - wlr_seat_pointer_notify_clear_focus(seat->seat); - } -} +// void +// cursor_rebase(struct seat *seat, uint32_t time_msec) +// { +// double sx, sy; +// struct wlr_surface *surface; +// enum ssd_part_type view_area = LAB_SSD_NONE; +// +// desktop_surface_and_view_at(seat->server, seat->cursor->x, +// seat->cursor->y, &surface, &sx, &sy, &view_area); +// +// if (surface) { +// wlr_seat_pointer_notify_clear_focus(seat->seat); +// wlr_seat_pointer_notify_enter(seat->seat, surface, sx, sy); +// wlr_seat_pointer_notify_motion(seat->seat, time_msec, sx, sy); +// } else { +// cursor_set(seat, "left_ptr"); +// wlr_seat_pointer_notify_clear_focus(seat->seat); +// } +// } static void request_cursor_notify(struct wl_listener *listener, void *data) @@ -170,23 +170,6 @@ input_inhibit_blocks_surface(struct seat *seat, struct wl_resource *resource) inhibiting_client != wl_resource_get_client(resource); } -static struct output * -get_output(struct server *server, struct wlr_cursor *cursor) -{ - struct wlr_output *wlr_output = wlr_output_layout_output_at( - server->output_layout, cursor->x, cursor->y); - return output_from_wlr_output(server, wlr_output); -} - -static void -damage_whole_current_output(struct server *server) -{ - struct output *output = get_output(server, server->seat.cursor); - if (output) { - wlr_output_damage_add_whole(output->damage); - } -} - static void process_cursor_motion(struct server *server, uint32_t time) { @@ -278,12 +261,12 @@ process_cursor_motion(struct server *server, uint32_t time) if (ssd_is_button(view_area)) { if (last_button_hover != view_area) { /* Cursor entered new button area */ - damage_whole_current_output(server); + //damage_whole_current_output(server); last_button_hover = view_area; } } else if (last_button_hover != LAB_SSD_NONE) { /* Cursor left button area */ - damage_whole_current_output(server); + //damage_whole_current_output(server); last_button_hover = LAB_SSD_NONE; } @@ -639,7 +622,7 @@ cursor_button(struct wl_listener *listener, void *data) server->input_mode = LAB_INPUT_STATE_PASSTHROUGH; server->grabbed_view = NULL; } - cursor_rebase(&server->seat, event->time_msec); +// cursor_rebase(&server->seat, event->time_msec); } /* Handle _release_ on root window */ @@ -657,24 +640,24 @@ cursor_button(struct wl_listener *listener, void *data) menu_action_selected(server, server->windowmenu); } server->input_mode = LAB_INPUT_STATE_PASSTHROUGH; - cursor_rebase(&server->seat, event->time_msec); +// cursor_rebase(&server->seat, event->time_msec); return; } /* Handle _press_ on a layer surface */ - if (!view && surface) { - if (!wlr_surface_is_layer_surface(surface)) { - return; - } - struct wlr_layer_surface_v1 *layer = - wlr_layer_surface_v1_from_wlr_surface(surface); - if (layer->current.keyboard_interactive) { - seat_set_focus_layer(&server->seat, layer); - } - wlr_seat_pointer_notify_button(seat->seat, event->time_msec, - event->button, event->state); - return; - } +// if (!view && surface) { +// if (!wlr_surface_is_layer_surface(surface)) { +// return; +// } +// struct wlr_layer_surface_v1 *layer = +// wlr_layer_surface_v1_from_wlr_surface(surface); +// if (layer->current.keyboard_interactive) { +// seat_set_focus_layer(&server->seat, layer); +// } +// wlr_seat_pointer_notify_button(seat->seat, event->time_msec, +// event->button, event->state); +// return; +// } /* Handle _press_ on root window */ if (!view) { @@ -721,7 +704,7 @@ cursor_axis(struct wl_listener *listener, void *data) wlr_idle_notify_activity(seat->wlr_idle, seat->seat); /* Notify the client with pointer focus of the axis event. */ - cursor_rebase(seat, event->time_msec); +// cursor_rebase(seat, event->time_msec); wlr_seat_pointer_notify_axis(seat->seat, event->time_msec, event->orientation, event->delta, event->delta_discrete, event->source); diff --git a/src/damage.c b/src/damage.c index 3a1e52c6..983ff428 100644 --- a/src/damage.c +++ b/src/damage.c @@ -4,30 +4,14 @@ void damage_all_outputs(struct server *server) { - struct output *output; - wl_list_for_each(output, &server->outputs, link) { - if (output && output->wlr_output && output->damage) { - wlr_output_damage_add_whole(output->damage); - } - } } void damage_view_part(struct view *view) { - struct output *output; - wl_list_for_each (output, &view->server->outputs, link) { - output_damage_surface(output, view->surface, view->x, view->y, - false); - } } void damage_view_whole(struct view *view) { - struct output *output; - wl_list_for_each (output, &view->server->outputs, link) { - output_damage_surface(output, view->surface, view->x, view->y, - true); - } } diff --git a/src/desktop.c b/src/desktop.c index 9d80da0f..19881f18 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -255,179 +255,23 @@ desktop_focus_topmost_mapped_view(struct server *server) desktop_move_to_front(view); } -static bool -_view_at(struct view *view, double lx, double ly, struct wlr_surface **surface, - double *sx, double *sy) -{ - /* - * XDG toplevels may have nested surfaces, such as popup windows for - * context menus or tooltips. This function tests if any of those are - * underneath the coordinates lx and ly (in output Layout Coordinates). - * If so, it sets the surface pointer to that wlr_surface and the sx and - * sy coordinates to the coordinates relative to that surface's top-left - * corner. - */ - double view_sx = lx - view->x; - double view_sy = ly - view->y; - double _sx, _sy; - struct wlr_surface *_surface = NULL; - - switch (view->type) { - case LAB_XDG_SHELL_VIEW: - _surface = wlr_xdg_surface_surface_at( - view->xdg_surface, view_sx, view_sy, &_sx, &_sy); - break; -#if HAVE_XWAYLAND - case LAB_XWAYLAND_VIEW: - _surface = wlr_surface_surface_at(view->surface, view_sx, - view_sy, &_sx, &_sy); - break; -#endif - } - - if (_surface) { - *sx = _sx; - *sy = _sy; - *surface = _surface; - return true; - } - return false; -} - -static struct -wlr_surface *layer_surface_at(struct wl_list *layer, double ox, double oy, - double *sx, double *sy) -{ - struct lab_layer_surface *surface; - wl_list_for_each_reverse(surface, layer, link) { - double _sx = ox - surface->geo.x; - double _sy = oy - surface->geo.y; - struct wlr_surface *wlr_surface; - wlr_surface = wlr_layer_surface_v1_surface_at( - surface->layer_surface, _sx, _sy, sx, sy); - if (wlr_surface) { - return wlr_surface; - } - } - return NULL; -} - -static bool -surface_is_xdg_popup(struct wlr_surface *surface) -{ - if (wlr_surface_is_xdg_surface(surface)) { - struct wlr_xdg_surface *xdg_surface = - wlr_xdg_surface_from_wlr_surface(surface); - return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP; - } - return false; -} - -static struct wlr_surface * -layer_surface_popup_at(struct output *output, struct wl_list *layer, - double ox, double oy, double *sx, double *sy) -{ - struct lab_layer_surface *lab_layer; - wl_list_for_each_reverse(lab_layer, layer, link) { - double _sx = ox - lab_layer->geo.x; - double _sy = oy - lab_layer->geo.y; - struct wlr_surface *sub = wlr_layer_surface_v1_surface_at( - lab_layer->layer_surface, _sx, _sy, sx, sy); - if (sub && surface_is_xdg_popup(sub)) { - return sub; - } - } - return NULL; -} - struct view * desktop_surface_and_view_at(struct server *server, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy, enum ssd_part_type *view_area) { - struct wlr_output *wlr_output = wlr_output_layout_output_at( - server->output_layout, lx, ly); - struct output *output = output_from_wlr_output(server, wlr_output); + struct wlr_scene_node *node = + wlr_scene_node_at(&server->scene->node, lx, ly, sx, sy); - if (!output) { + if (!node || node->type != WLR_SCENE_NODE_SURFACE) { return NULL; } - - double ox = lx, oy = ly; - wlr_output_layout_output_coords(output->server->output_layout, - wlr_output, &ox, &oy); - - /* Overlay-layer */ - *surface = layer_surface_at( - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], - ox, oy, sx, sy); - if (*surface) { - return NULL; + *surface = wlr_scene_surface_from_node(node)->surface; + while (node && !node->data) { + node = node->parent; } - - /* Check all layer popups */ - *surface = layer_surface_popup_at(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], - ox, oy, sx, sy); - if (*surface) { - return NULL; - } - *surface = layer_surface_popup_at(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], - ox, oy, sx, sy); - if (*surface) { - return NULL; - } - *surface = layer_surface_popup_at(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - ox, oy, sx, sy); - if (*surface) { - return NULL; - } - - /* Top-layer */ - *surface = layer_surface_at( - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], - ox, oy, sx, sy); - if (*surface) { - return NULL; - } - - struct view *view; - wl_list_for_each (view, &server->views, link) { - if (!view->mapped) { - continue; - } - - /* This ignores server-side deco */ - if (_view_at(view, lx, ly, surface, sx, sy)) { - *view_area = LAB_SSD_CLIENT; - return view; - } - if (!view->ssd.enabled) { - continue; - } - - /* Now, let's deal with the server-side deco */ - *view_area = ssd_at(view, lx, ly); - if (*view_area != LAB_SSD_NONE) { - return view; - } - } - - *surface = layer_surface_at( - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], - ox, oy, sx, sy); - if (*surface) { - return NULL; - } - *surface = layer_surface_at( - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - ox, oy, sx, sy); - if (*surface) { - return NULL; - } - return NULL; + assert(node); + return node->data; } struct view * diff --git a/src/keyboard.c b/src/keyboard.c index 9b1bf846..00e0ce47 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -2,6 +2,7 @@ #include #include #include "action.h" +#include "buffer.h" #include "key-state.h" #include "labwc.h" @@ -49,6 +50,8 @@ keyboard_modifiers_notify(struct wl_listener *listener, void *data) server->cycle_view); desktop_move_to_front(server->cycle_view); server->cycle_view = NULL; + wlr_scene_node_set_enabled( + &server->osd_tree->node, false); } } diff --git a/src/meson.build b/src/meson.build index 536b621a..0bce7cd5 100644 --- a/src/meson.build +++ b/src/meson.build @@ -16,10 +16,8 @@ labwc_sources = files( 'seat.c', 'server.c', 'ssd.c', - 'subsurface.c', 'theme.c', 'view.c', - 'view-child.c', 'view-impl.c', 'xdg.c', 'xdg-deco.c', diff --git a/src/osd.c b/src/osd.c index f2a54967..f8b1ef0e 100644 --- a/src/osd.c +++ b/src/osd.c @@ -4,6 +4,7 @@ #include #include #include +#include "buffer.h" #include "common/buf.h" #include "common/font.h" #include "config/rcxml.h" @@ -76,7 +77,6 @@ get_osd_height(struct wl_list *views) void osd_update(struct server *server) { - struct wlr_renderer *renderer = server->renderer; struct theme *theme = server->theme; struct output *output; @@ -179,18 +179,21 @@ osd_update(struct server *server) g_object_unref(layout); - /* convert to wlr_texture */ cairo_surface_flush(surf); unsigned char *data = cairo_image_surface_get_data(surf); - struct wlr_texture *texture = wlr_texture_from_pixels(renderer, - DRM_FORMAT_ARGB8888, cairo_image_surface_get_stride(surf), - w, h, data); - - cairo_destroy(cairo); - cairo_surface_destroy(surf); - if (output->osd) { - wlr_texture_destroy(output->osd); + if (output->osd_buffer) { + buffer_drop(output->osd_buffer); } - output->osd = texture; + output->osd_buffer = buffer_create(DRM_FORMAT_ARGB8888, + cairo_image_surface_get_stride(surf), w, h, data); + + cairo_surface_destroy(surf); + + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_create( + &server->osd_tree->node, &output->osd_buffer->base); + + /* TODO: set position properly */ + wlr_scene_node_set_position(&scene_buffer->node, 10, 10); + wlr_scene_node_set_enabled(&server->osd_tree->node, true); } } diff --git a/src/output.c b/src/output.c index 9c40fcd5..fc69a294 100644 --- a/src/output.c +++ b/src/output.c @@ -9,949 +9,31 @@ #define _POSIX_C_SOURCE 200809L #include "config.h" #include +#include #include #include #include #include +#include "buffer.h" #include "labwc.h" #include "layers.h" #include "menu/menu.h" #include "ssd.h" #include "theme.h" -#define DEBUG (0) - -typedef void (*surface_iterator_func_t)(struct output *output, - struct wlr_surface *surface, struct wlr_box *box, - void *user_data); - -struct surface_iterator_data { - surface_iterator_func_t user_iterator; - void *user_data; - struct output *output; - struct view *view; - double ox, oy; - int width, height; -}; - -static bool -intersects_with_output(struct output *output, - struct wlr_output_layout *output_layout, - struct wlr_box *surface_box) -{ - /* The resolution can change if outputs are rotated */ - struct wlr_box output_box = {0}; - wlr_output_effective_resolution(output->wlr_output, &output_box.width, - &output_box.height); - struct wlr_box intersection; - return wlr_box_intersection(&intersection, &output_box, surface_box); -} - static void -output_for_each_surface_iterator(struct wlr_surface *surface, int sx, int sy, - void *user_data) -{ - struct surface_iterator_data *data = user_data; - struct output *output = data->output; - if (!wlr_surface_has_buffer(surface)) { - return; - } - struct wlr_box surface_box = { - .x = data->ox + sx + surface->sx, - .y = data->oy + sy + surface->sy, - .width = surface->current.width, - .height = surface->current.height, - }; - - if (!intersects_with_output(output, output->server->output_layout, - &surface_box)) { - return; - } - data->user_iterator(data->output, surface, &surface_box, - data->user_data); -} - -void -output_surface_for_each_surface(struct output *output, - struct wlr_surface *surface, double ox, double oy, - surface_iterator_func_t iterator, void *user_data) -{ - struct surface_iterator_data data = { - .user_iterator = iterator, - .user_data = user_data, - .output = output, - .ox = ox, - .oy = oy, - }; - assert(surface); - wlr_surface_for_each_surface(surface, - output_for_each_surface_iterator, &data); -} - -struct render_data { - pixman_region32_t *damage; -}; - -int -scale_length(int length, int offset, float scale) -{ - return round((offset + length) * scale) - round(offset * scale); -} - -void -scale_box(struct wlr_box *box, float scale) -{ - box->width = scale_length(box->width, box->x, scale); - box->height = scale_length(box->height, box->y, scale); - box->x = round(box->x * scale); - box->y = round(box->y * scale); -} - -static void -scissor_output(struct wlr_output *output, pixman_box32_t *rect) -{ - struct wlr_renderer *renderer = output->renderer; - - struct wlr_box box = { - .x = rect->x1, - .y = rect->y1, - .width = rect->x2 - rect->x1, - .height = rect->y2 - rect->y1, - }; - - int output_width, output_height; - wlr_output_transformed_resolution(output, &output_width, &output_height); - enum wl_output_transform transform = - wlr_output_transform_invert(output->transform); - wlr_box_transform(&box, &box, transform, output_width, output_height); - - wlr_renderer_scissor(renderer, &box); -} - -static void -render_texture(struct wlr_output *wlr_output, - pixman_region32_t *output_damage, struct wlr_texture *texture, - const struct wlr_fbox *src_box, const struct wlr_box *dst_box, - const float matrix[static 9]) -{ - struct wlr_renderer *renderer = wlr_output->renderer; - - pixman_region32_t damage; - pixman_region32_init(&damage); - pixman_region32_union_rect(&damage, &damage, dst_box->x, dst_box->y, - dst_box->width, dst_box->height); - pixman_region32_intersect(&damage, &damage, output_damage); - bool damaged = pixman_region32_not_empty(&damage); - if (!damaged) { - goto damage_finish; - } - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); - for (int i = 0; i < nrects; i++) { - scissor_output(wlr_output, &rects[i]); - const float alpha = 1.0f; - if (src_box != NULL) { - wlr_render_subtexture_with_matrix(renderer, texture, src_box, matrix, alpha); - } else { - wlr_render_texture_with_matrix(renderer, texture, matrix, alpha); - } - } - -damage_finish: - pixman_region32_fini(&damage); -} - -static void -render_surface_iterator(struct output *output, struct wlr_surface *surface, - struct wlr_box *_box, void *user_data) -{ - struct render_data *data = user_data; - struct wlr_output *wlr_output = output->wlr_output; - pixman_region32_t *output_damage = data->damage; - - struct wlr_texture *texture = wlr_surface_get_texture(surface); - if (!texture) { - wlr_log(WLR_DEBUG, "Cannot obtain surface texture"); - return; - } - - struct wlr_fbox src_box; - wlr_surface_get_buffer_source_box(surface, &src_box); - - struct wlr_box proj_box = *_box; - scale_box(&proj_box, wlr_output->scale); - - float matrix[9]; - enum wl_output_transform transform = - wlr_output_transform_invert(surface->current.transform); - wlr_matrix_project_box(matrix, &proj_box, transform, 0.0, - wlr_output->transform_matrix); - - struct wlr_box dst_box = *_box; - scale_box(&dst_box, wlr_output->scale); - - render_texture(wlr_output, output_damage, texture, &src_box, &dst_box, matrix); -} - -void -output_drag_icon_for_each_surface(struct output *output, struct seat *seat, - surface_iterator_func_t iterator, void *user_data) -{ - if (!seat->drag_icon || !seat->drag_icon->mapped) { - return; - } - double ox = seat->cursor->x, oy = seat->cursor->y; - wlr_output_layout_output_coords(output->server->output_layout, - output->wlr_output, &ox, &oy); - output_surface_for_each_surface(output, seat->drag_icon->surface, - ox, oy, iterator, user_data); -} - -static void -render_drag_icon(struct output *output, pixman_region32_t *damage) -{ - struct render_data data = { - .damage = damage, - }; - output_drag_icon_for_each_surface(output, &output->server->seat, render_surface_iterator, &data); -} - -#if HAVE_XWAYLAND -void -output_unmanaged_for_each_surface(struct output *output, - struct wl_list *unmanaged, surface_iterator_func_t iterator, - void *user_data) -{ - struct xwayland_unmanaged *unmanaged_surface; - wl_list_for_each(unmanaged_surface, unmanaged, link) { - struct wlr_xwayland_surface *xsurface = - unmanaged_surface->xwayland_surface; - double ox = unmanaged_surface->lx, oy = unmanaged_surface->ly; - wlr_output_layout_output_coords( - output->server->output_layout, output->wlr_output, &ox, &oy); - output_surface_for_each_surface(output, xsurface->surface, ox, oy, - iterator, user_data); - } -} - -static void -render_unmanaged(struct output *output, pixman_region32_t *damage, - struct wl_list *unmanaged) -{ - struct render_data data = { - .damage = damage, - }; - output_unmanaged_for_each_surface(output, unmanaged, - render_surface_iterator, &data); -} -#endif - -static void -output_view_for_each_surface(struct output *output, struct view *view, - surface_iterator_func_t iterator, void *user_data) -{ - struct surface_iterator_data data = { - .user_iterator = iterator, - .user_data = user_data, - .output = output, - .ox = view->x, - .oy = view->y, - }; - - wlr_output_layout_output_coords(output->server->output_layout, - output->wlr_output, &data.ox, &data.oy); - view_for_each_surface(view, output_for_each_surface_iterator, &data); -} - -void -output_view_for_each_popup_surface(struct output *output, struct view *view, - surface_iterator_func_t iterator, void *user_data) -{ - struct surface_iterator_data data = { - .user_iterator = iterator, - .user_data = user_data, - .output = output, - .ox = view->x, - .oy = view->y, - }; - - wlr_output_layout_output_coords(output->server->output_layout, - output->wlr_output, &data.ox, &data.oy); - view_for_each_popup_surface(view, output_for_each_surface_iterator, &data); -} - -/* for sending frame done */ -void -output_layer_for_each_surface(struct output *output, - struct wl_list *layer_surfaces, surface_iterator_func_t iterator, - void *user_data) -{ - struct lab_layer_surface *layer_surface; - wl_list_for_each(layer_surface, layer_surfaces, link) { - struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = - layer_surface->layer_surface; - struct wlr_surface *surface = wlr_layer_surface_v1->surface; - struct surface_iterator_data data = { - .user_iterator = iterator, - .user_data = user_data, - .output = output, - .view = NULL, - .ox = layer_surface->geo.x, - .oy = layer_surface->geo.y, - .width = surface->current.width, - .height = surface->current.height, - }; - wlr_layer_surface_v1_for_each_surface(wlr_layer_surface_v1, - output_for_each_surface_iterator, &data); - } -} - -static void -output_for_each_surface(struct output *output, surface_iterator_func_t iterator, - void *user_data) -{ - output_layer_for_each_surface(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - iterator, user_data); - output_layer_for_each_surface(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], - iterator, user_data); - - struct view *view; - wl_list_for_each_reverse(view, &output->server->views, link) { - if (!view->mapped) { - continue; - } - output_view_for_each_surface(output, view, iterator, user_data); - } - -#if HAVE_XWAYLAND - output_unmanaged_for_each_surface(output, - &output->server->unmanaged_surfaces, iterator, user_data); -#endif - - output_layer_for_each_surface(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], - iterator, user_data); - output_layer_for_each_surface(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], - iterator, user_data); - output_drag_icon_for_each_surface(output, &output->server->seat, iterator, user_data); -} - -struct send_frame_done_data { - struct timespec when; -}; - -static void -send_frame_done_iterator(struct output *output, struct wlr_surface *surface, - struct wlr_box *box, void *user_data) -{ - struct send_frame_done_data *data = user_data; - wlr_surface_send_frame_done(surface, &data->when); -} - -static void -send_frame_done(struct output *output, struct send_frame_done_data *data) -{ - output_for_each_surface(output, send_frame_done_iterator, data); -} - -void -render_rect(struct output *output, pixman_region32_t *output_damage, - const struct wlr_box *_box, float color[static 4]) -{ - struct wlr_output *wlr_output = output->wlr_output; - struct wlr_renderer *renderer = wlr_output->renderer; - - struct wlr_box box; - memcpy(&box, _box, sizeof(struct wlr_box)); - - double ox = 0, oy = 0; - wlr_output_layout_output_coords(output->server->output_layout, - output->wlr_output, &ox, &oy); - box.x += ox; - box.y += oy; - scale_box(&box, wlr_output->scale); - - pixman_region32_t damage; - pixman_region32_init(&damage); - pixman_region32_union_rect(&damage, &damage, box.x, box.y, - box.width, box.height); - pixman_region32_intersect(&damage, &damage, output_damage); - bool damaged = pixman_region32_not_empty(&damage); - if (!damaged) { - goto damage_finish; - } - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); - wlr_render_rect(renderer, &box, color, - wlr_output->transform_matrix); - } - -damage_finish: - pixman_region32_fini(&damage); -} - -void -render_rect_unfilled(struct output *output, pixman_region32_t *output_damage, - const struct wlr_box *_box, float color[static 4]) -{ - struct wlr_box box; - memcpy(&box, _box, sizeof(struct wlr_box)); - box.height = 1; - render_rect(output, output_damage, &box, color); - box.y += _box->height - 1; - render_rect(output, output_damage, &box, color); - memcpy(&box, _box, sizeof(struct wlr_box)); - box.width = 1; - render_rect(output, output_damage, &box, color); - box.x += _box->width - 1; - render_rect(output, output_damage, &box, color); -} - -static void -shrink(struct wlr_box *box, int size) -{ - box->x += size; - box->y += size; - box->width -= 2 * size; - box->height -= 2 * size; -} - -static void -render_cycle_box(struct output *output, pixman_region32_t *output_damage, - struct view *view) -{ - struct wlr_box box = { - .x = view->x, - .y = view->y, - .width = view->w, - .height = view->h, - }; - box.x -= view->margin.left; - box.y -= view->margin.top; - box.width += view->margin.left + view->margin.right; - box.height += view->margin.top + view->margin.bottom; - box.x += view->padding.left; - box.y += view->padding.top; - - float white[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; - float black[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; - render_rect_unfilled(output, output_damage, &box, white); - - for (int i = 0; i < 4; i++) { - shrink(&box, 1); - render_rect_unfilled(output, output_damage, &box, black); - } - shrink(&box, 1); - render_rect_unfilled(output, output_damage, &box, white); -} - -static void -render_icon(struct output *output, pixman_region32_t *output_damage, - struct wlr_box *box, struct wlr_texture *texture) -{ - /* centre-align icon if smaller than designated box */ - struct wlr_box button = { - .width = texture->width, - .height = texture->height, - }; - if (box->width > button.width) { - button.x = box->x + (box->width - button.width) / 2; - } else { - button.x = box->x; - button.width = box->width; - } - if (box->height > button.height) { - button.y = box->y + (box->height - button.height) / 2; - } else { - button.y = box->y; - button.height = box->height; - } - - double ox = 0, oy = 0; - wlr_output_layout_output_coords(output->server->output_layout, - output->wlr_output, &ox, &oy); - button.x += ox; - button.y += oy; - scale_box(&button, output->wlr_output->scale); - - float matrix[9]; - wlr_matrix_project_box(matrix, &button, WL_OUTPUT_TRANSFORM_NORMAL, 0, - output->wlr_output->transform_matrix); - render_texture(output->wlr_output, output_damage, texture, NULL, &button, - matrix); -} - -void -render_texture_helper(struct output *output, pixman_region32_t *output_damage, - struct wlr_box *_box, struct wlr_texture *texture) -{ - if (!texture) { - return; - } - struct wlr_box box; - memcpy(&box, _box, sizeof(struct wlr_box)); - - double ox = 0, oy = 0; - wlr_output_layout_output_coords(output->server->output_layout, - output->wlr_output, &ox, &oy); - box.x += ox; - box.y += oy; - scale_box(&box, output->wlr_output->scale); - - float matrix[9]; - wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, - output->wlr_output->transform_matrix); - render_texture(output->wlr_output, output_damage, texture, NULL, &box, - matrix); -} - -static void -render_osd(struct output *output, pixman_region32_t *damage, - struct server *server) -{ - /* show on screen display (osd) on all outputs */ - struct output *o; - struct wlr_output_layout *layout = server->output_layout; - struct wlr_output_layout_output *ol_output; - wl_list_for_each(o, &server->outputs, link) { - ol_output = wlr_output_layout_get(layout, o->wlr_output); - if (!ol_output) { - continue; - } - - if (!output->osd) { - continue; - } - struct wlr_box box = { - .x = ol_output->x + o->wlr_output->width - / 2, - .y = ol_output->y + o->wlr_output->height - / 2, - .width = output->osd->width, - .height = output->osd->height, - }; - box.x -= box.width / 2; - box.y -= box.height / 2; - - double ox = 0, oy = 0; - wlr_output_layout_output_coords(output->server->output_layout, - output->wlr_output, &ox, &oy); - box.x += ox; - box.y += oy; - float matrix[9]; - wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, - 0, output->wlr_output->transform_matrix); - render_texture(o->wlr_output, damage, output->osd, NULL, &box, - matrix); - } -} - -static void -render_deco(struct view *view, struct output *output, - pixman_region32_t *output_damage) -{ - if (!view->ssd.enabled || view->fullscreen) { - return; - } - - struct wlr_seat *seat = view->server->seat.seat; - bool focused = view->surface == seat->keyboard_state.focused_surface; - - /* render texture or rectangle */ - struct ssd_part *part; - wl_list_for_each_reverse(part, &view->ssd.parts, link) { - if (part->texture.active && *(part->texture.active)) { - struct wlr_texture *texture = focused ? - *(part->texture.active) : - *(part->texture.inactive); - render_texture_helper(output, output_damage, &part->box, - texture); - } else if (part->color.active && part->color.inactive) { - float *color = focused ? - part->color.active : - part->color.inactive; - render_rect(output, output_damage, &part->box, color); - } - } - - /* button background */ - struct wlr_cursor *cur = view->server->seat.cursor; - enum ssd_part_type type = ssd_at(view, cur->x, cur->y); - struct wlr_box box = ssd_visible_box(view, type); - if (ssd_is_button(type) && - wlr_box_contains_point(&box, cur->x, cur->y)) { - float *color = (float[4]) { 0.5, 0.5, 0.5, 0.5 }; - render_rect(output, output_damage, &box, color); - } - - /* buttons */ - struct theme *theme = view->server->theme; - if (view->surface == seat->keyboard_state.focused_surface) { - box = ssd_visible_box(view, LAB_SSD_BUTTON_CLOSE); - render_icon(output, output_damage, &box, - theme->xbm_close_active_unpressed); - box = ssd_visible_box(view, LAB_SSD_BUTTON_MAXIMIZE); - render_icon(output, output_damage, &box, - theme->xbm_maximize_active_unpressed); - box = ssd_visible_box(view, LAB_SSD_BUTTON_ICONIFY); - render_icon(output, output_damage, &box, - theme->xbm_iconify_active_unpressed); - box = ssd_visible_box(view, LAB_SSD_BUTTON_WINDOW_MENU); - render_icon(output, output_damage, &box, - theme->xbm_menu_active_unpressed); - } else { - box = ssd_visible_box(view, LAB_SSD_BUTTON_CLOSE); - render_icon(output, output_damage, &box, - theme->xbm_close_inactive_unpressed); - box = ssd_visible_box(view, LAB_SSD_BUTTON_MAXIMIZE); - render_icon(output, output_damage, &box, - theme->xbm_maximize_inactive_unpressed); - box = ssd_visible_box(view, LAB_SSD_BUTTON_ICONIFY); - render_icon(output, output_damage, &box, - theme->xbm_iconify_inactive_unpressed); - box = ssd_visible_box(view, LAB_SSD_BUTTON_WINDOW_MENU); - render_icon(output, output_damage, &box, - theme->xbm_menu_inactive_unpressed); - } -} - -static void -render_menu(struct output *output, pixman_region32_t *damage, struct menu *menu) -{ - if (!menu->visible) { - return; - } - struct server *server = output->server; - struct theme *theme = server->theme; - float matrix[9]; - - struct wlr_output_layout *output_layout = server->output_layout; - double ox = 0, oy = 0; - wlr_output_layout_output_coords(output_layout, output->wlr_output, - &ox, &oy); - - /* background */ - render_rect(output, damage, &menu->box, theme->menu_items_bg_color); - - /* items */ - struct menuitem *menuitem; - wl_list_for_each (menuitem, &menu->menuitems, link) { - struct wlr_box box = { - .x = menuitem->box.x + menuitem->texture.offset_x + ox, - .y = menuitem->box.y + menuitem->texture.offset_y + oy, - .width = menuitem->texture.active->width, - .height = menuitem->texture.active->height, - }; - scale_box(&box, output->wlr_output->scale); - wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, - 0, output->wlr_output->transform_matrix); - if (menuitem->selected) { - render_rect(output, damage, &menuitem->box, - theme->menu_items_active_bg_color); - render_texture(output->wlr_output, damage, - menuitem->texture.active, NULL, &box, matrix); - } else { - render_texture(output->wlr_output, damage, - menuitem->texture.inactive, NULL, &box, matrix); - } - /* render submenus */ - if (menuitem->submenu) { - render_menu(output, damage, menuitem->submenu); - } - } -} - -static void -output_layer_for_each_popup_surface(struct output *output, - struct wl_list *layer_surfaces, - surface_iterator_func_t iterator, - void *user_data) -{ - struct lab_layer_surface *layer_surface; - wl_list_for_each(layer_surface, layer_surfaces, link) { - struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = - layer_surface->layer_surface; - struct wlr_surface *surface = wlr_layer_surface_v1->surface; - struct surface_iterator_data data = { - .user_iterator = iterator, - .user_data = user_data, - .output = output, - .view = NULL, - .ox = layer_surface->geo.x, - .oy = layer_surface->geo.y, - .width = surface->current.width, - .height = surface->current.height, - }; - wlr_layer_surface_v1_for_each_popup_surface(wlr_layer_surface_v1, - output_for_each_surface_iterator, &data); - } -} - -static void -render_layer_popups(struct output *output, pixman_region32_t *damage, - struct wl_list *layer_surfaces) -{ - struct render_data data = { - .damage = damage, - }; - output_layer_for_each_popup_surface(output, layer_surfaces, - render_surface_iterator, &data); -} - -void -output_layer_for_each_surface_toplevel(struct output *output, - struct wl_list *layer_surfaces, surface_iterator_func_t iterator, - void *user_data) -{ - struct lab_layer_surface *layer_surface; - wl_list_for_each(layer_surface, layer_surfaces, link) { - struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = - layer_surface->layer_surface; - output_surface_for_each_surface(output, - wlr_layer_surface_v1->surface, layer_surface->geo.x, - layer_surface->geo.y, iterator, user_data); - } -} - -static void -render_layer_toplevel(struct output *output, pixman_region32_t *damage, - struct wl_list *layer_surfaces) -{ - struct render_data data = { - .damage = damage, - }; - output_layer_for_each_surface_toplevel(output, layer_surfaces, - render_surface_iterator, &data); -} - -static void -render_view_toplevels(struct view *view, struct output *output, - pixman_region32_t *damage) -{ - struct render_data data = { - .damage = damage, - }; - double ox = view->x; - double oy = view->y; - wlr_output_layout_output_coords(output->server->output_layout, - output->wlr_output, &ox, &oy); - output_surface_for_each_surface(output, view->surface, ox, oy, - render_surface_iterator, &data); -} - -static void -render_view_popups(struct view *view, struct output *output, - pixman_region32_t *damage) -{ - struct render_data data = { - .damage = damage, - }; - output_view_for_each_popup_surface(output, view, - render_surface_iterator, &data); -} - -void -output_render(struct output *output, pixman_region32_t *damage) -{ - struct server *server = output->server; - struct wlr_output *wlr_output = output->wlr_output; - - struct wlr_renderer *renderer = wlr_output->renderer; - if (!renderer) { - wlr_log(WLR_DEBUG, "no renderer"); - return; - } - - /* Calls glViewport and some other GL sanity checks */ - wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); - - if (!pixman_region32_not_empty(damage)) { - goto renderer_end; - } - -#if DEBUG - wlr_renderer_clear(renderer, (float[]){0.2f, 0.0f, 0.0f, 1.0f}); -#endif - - float color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); - for (int i = 0; i < nrects; i++) { - scissor_output(wlr_output, &rects[i]); - wlr_renderer_clear(renderer, color); - } - - render_layer_toplevel(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); - render_layer_toplevel(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); - - struct view *view; - wl_list_for_each_reverse (view, &server->views, link) { - if (!view->mapped) { - continue; - } - render_deco(view, output, damage); - render_view_toplevels(view, output, damage); - render_view_popups(view, output, damage); - } - -#if HAVE_XWAYLAND - render_unmanaged(output, damage, &output->server->unmanaged_surfaces); -#endif - - /* 'alt-tab' border */ - if (output->server->cycle_view) { - /* If the 'cycle_preview_contents' option is set in - * rc.xml, render the contents of the cycle_view over - * all other views (except for the OSD) - */ - if (rc.cycle_preview_contents) { - render_deco(output->server->cycle_view, output, damage); - render_view_toplevels(output->server->cycle_view, output, damage); - render_view_popups(output->server->cycle_view, output, damage); - } - - render_cycle_box(output, damage, output->server->cycle_view); - render_osd(output, damage, output->server); - } - - render_drag_icon(output, damage); - - render_layer_toplevel(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); - render_layer_popups(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); - render_layer_popups(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); - render_layer_popups(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); - - render_layer_toplevel(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); - render_layer_popups(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); - - if (output->server->input_mode == LAB_INPUT_STATE_MENU) { - render_menu(output, damage, server->rootmenu); - render_menu(output, damage, server->windowmenu); - } - -renderer_end: - /* Just in case hardware cursors not supported by GPU */ - wlr_output_render_software_cursors(wlr_output, damage); - wlr_renderer_scissor(renderer, NULL); - wlr_renderer_end(renderer); - - int output_width, output_height; - wlr_output_transformed_resolution(wlr_output, &output_width, - &output_height); - - pixman_region32_t frame_damage; - pixman_region32_init(&frame_damage); - - enum wl_output_transform transform = - wlr_output_transform_invert(wlr_output->transform); - wlr_region_transform(&frame_damage, &output->damage->current, - transform, output_width, output_height); - -#if DEBUG - pixman_region32_union_rect(&frame_damage, &frame_damage, 0, 0, - output_width, output_height); -#endif - - wlr_output_set_damage(wlr_output, &frame_damage); - pixman_region32_fini(&frame_damage); - - if (!wlr_output_commit(wlr_output)) { - wlr_log(WLR_ERROR, "could not commit output"); - } -} - -static void -damage_surface_iterator(struct output *output, struct wlr_surface *surface, - struct wlr_box *box, void *user_data) -{ - struct wlr_output *wlr_output = output->wlr_output; - bool whole = *(bool *) user_data; - - scale_box(box, output->wlr_output->scale); - - if (whole) { - wlr_output_damage_add_box(output->damage, box); - } else if (pixman_region32_not_empty(&surface->buffer_damage)) { - pixman_region32_t damage; - pixman_region32_init(&damage); - wlr_surface_get_effective_damage(surface, &damage); - - wlr_region_scale(&damage, &damage, wlr_output->scale); - if (ceil(wlr_output->scale) > surface->current.scale) { - wlr_region_expand(&damage, &damage, - ceil(wlr_output->scale) - surface->current.scale); - } - pixman_region32_translate(&damage, box->x, box->y); - wlr_output_damage_add(output->damage, &damage); - pixman_region32_fini(&damage); - } -} - -void -output_damage_surface(struct output *output, struct wlr_surface *surface, - double lx, double ly, bool whole) +output_frame_notify(struct wl_listener *listener, void *data) { + struct output *output = wl_container_of(listener, output, frame); if (!output->wlr_output->enabled) { return; } - double ox = lx, oy = ly; - wlr_output_layout_output_coords(output->server->output_layout, - output->wlr_output, &ox, &oy); - output_surface_for_each_surface(output, surface, ox, oy, - damage_surface_iterator, &whole); -} + wlr_scene_output_commit(output->scene_output); -static void -output_damage_frame_notify(struct wl_listener *listener, void *data) -{ - struct output *output = wl_container_of(listener, output, damage_frame); - - if (!output->wlr_output->enabled) { - return; - } - - bool needs_frame; - pixman_region32_t damage; - pixman_region32_init(&damage); - if (!wlr_output_damage_attach_render(output->damage, - &needs_frame, &damage)) { - return; - } - - if (needs_frame) { - output_render(output, &damage); - } else { - wlr_output_rollback(output->wlr_output); - } - pixman_region32_fini(&damage); - - struct send_frame_done_data frame_data = {0}; - clock_gettime(CLOCK_MONOTONIC, &frame_data.when); - send_frame_done(output, &frame_data); -} - -static void -output_damage_destroy_notify(struct wl_listener *listener, void *data) -{ - struct output *output = wl_container_of(listener, output, damage_destroy); - wl_list_remove(&output->damage_frame.link); - wl_list_remove(&output->damage_destroy.link); + struct timespec now = { 0 }; + clock_gettime(CLOCK_MONOTONIC, &now); + wlr_scene_output_send_frame_done(output->scene_output, &now); } static void @@ -959,6 +41,7 @@ output_destroy_notify(struct wl_listener *listener, void *data) { struct output *output = wl_container_of(listener, output, destroy); wl_list_remove(&output->link); + wl_list_remove(&output->frame.link); wl_list_remove(&output->destroy.link); } @@ -1018,18 +101,14 @@ new_output_notify(struct wl_listener *listener, void *data) output->wlr_output = wlr_output; wlr_output->data = output; output->server = server; - output->damage = wlr_output_damage_create(wlr_output); wlr_output_effective_resolution(wlr_output, &output->usable_area.width, &output->usable_area.height); wl_list_insert(&server->outputs, &output->link); output->destroy.notify = output_destroy_notify; wl_signal_add(&wlr_output->events.destroy, &output->destroy); - - output->damage_frame.notify = output_damage_frame_notify; - wl_signal_add(&output->damage->events.frame, &output->damage_frame); - output->damage_destroy.notify = output_damage_destroy_notify; - wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); + output->frame.notify = output_frame_notify; + wl_signal_add(&wlr_output->events.frame, &output->frame); wl_list_init(&output->layers[0]); wl_list_init(&output->layers[1]); @@ -1046,9 +125,17 @@ new_output_notify(struct wl_listener *listener, void *data) wlr_output_enable_adaptive_sync(wlr_output, true); } - output->osd = NULL; - wlr_output_layout_add_auto(server->output_layout, wlr_output); + + /* TODO: check this makes sense */ + struct wlr_scene_output *scene_output; + wl_list_for_each (scene_output, &output->server->scene->outputs, link) { + if (scene_output->output == wlr_output) { + output->scene_output = scene_output; + break; + } + } + assert(output->scene_output); } void @@ -1117,6 +204,7 @@ output_config_apply(struct server *server, bool need_to_remove = !head->state.enabled && o->enabled; if (need_to_remove) { + /* TODO: should we output->scene_output = NULL; ?? */ wlr_output_layout_remove(server->output_layout, o); } diff --git a/src/server.c b/src/server.c index 450b71e8..fd59599a 100644 --- a/src/server.c +++ b/src/server.c @@ -216,6 +216,17 @@ server_init(struct server *server) wl_list_init(&server->views); wl_list_init(&server->unmanaged_surfaces); + output_init(server); + + server->scene = wlr_scene_create(); + if (!server->scene) { + wlr_log(WLR_ERROR, "unable to create scene"); + exit(EXIT_FAILURE); + } + server->view_tree = wlr_scene_tree_create(&server->scene->node); + server->osd_tree = wlr_scene_tree_create(&server->scene->node); + wlr_scene_attach_output_layout(server->scene, server->output_layout); + /* * Create some hands-off wlroots interfaces. The compositor is * necessary for clients to allocate surfaces and the data device @@ -249,7 +260,6 @@ server_init(struct server *server) */ wlr_primary_selection_v1_device_manager_create(server->wl_display); - output_init(server); seat_init(server); /* Init xdg-shell */ diff --git a/src/subsurface.c b/src/subsurface.c deleted file mode 100644 index 79866355..00000000 --- a/src/subsurface.c +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2020 the sway authors - * This file is only needed in support of tracking damage - */ - -#include "labwc.h" - -static void -subsurface_handle_destroy(struct wl_listener *listener, void *data) -{ - struct view_subsurface *subsurface = wl_container_of(listener, - subsurface, destroy); - struct view_child *view_child = (struct view_child *)subsurface; - if (!view_child) { - return; - } - wl_list_remove(&subsurface->destroy.link); - view_child_finish(&subsurface->view_child); - free(subsurface); -} - -void -view_subsurface_create(struct view *view, struct wlr_subsurface *wlr_subsurface) -{ - struct view_subsurface *subsurface = - calloc(1, sizeof(struct view_subsurface)); - if (!subsurface) { - return; - } - view_child_init(&subsurface->view_child, view, wlr_subsurface->surface); - subsurface->subsurface = wlr_subsurface; - - subsurface->destroy.notify = subsurface_handle_destroy; - wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); -} diff --git a/src/view-child.c b/src/view-child.c deleted file mode 100644 index 462a53a4..00000000 --- a/src/view-child.c +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2020 the sway authors - * This file is only needed in support of tracking damage - */ - -#include "labwc.h" - -static void -view_child_handle_commit(struct wl_listener *listener, void *data) -{ - struct view_child *child = wl_container_of(listener, child, commit); - damage_all_outputs(child->parent->server); -} - -static void -view_child_handle_new_subsurface(struct wl_listener *listener, void *data) -{ - struct view_child *child; - child = wl_container_of(listener, child, new_subsurface); - struct wlr_subsurface *wlr_subsurface = data; - view_subsurface_create(child->parent, wlr_subsurface); -} - -void -view_child_finish(struct view_child *child) -{ - wl_list_remove(&child->commit.link); - wl_list_remove(&child->new_subsurface.link); -} - -void -view_child_init(struct view_child *child, struct view *view, - struct wlr_surface *wlr_surface) -{ - child->parent = view; - child->surface = wlr_surface; - child->commit.notify = view_child_handle_commit; - wl_signal_add(&wlr_surface->events.commit, &child->commit); - child->new_subsurface.notify = view_child_handle_new_subsurface; - wl_signal_add(&wlr_surface->events.new_subsurface, &child->new_subsurface); -} diff --git a/src/view.c b/src/view.c index 5587b1dc..9379cdf7 100644 --- a/src/view.c +++ b/src/view.c @@ -31,6 +31,7 @@ view_move(struct view *view, double x, double y) view->impl->move(view, x, y); } view_discover_output(view); + wlr_scene_node_set_position(view->scene_node, view->x, view->y); } void @@ -44,6 +45,7 @@ view_move_resize(struct view *view, struct wlr_box geo) } ssd_update_title(view); view_discover_output(view); + wlr_scene_node_set_position(view->scene_node, view->x, view->y); } #define MIN_VIEW_WIDTH (100) @@ -145,6 +147,7 @@ view_center(struct view *view) if (view_compute_centered_position(view, view->w, view->h, &x, &y)) { view_move(view, x, y); } + wlr_scene_node_set_position(view->scene_node, view->x, view->y); } static void @@ -220,6 +223,7 @@ view_maximize(struct view *view, bool maximize) if (view->fullscreen) { return; } + wlr_scene_node_set_position(view->scene_node, view->x, view->y); if (view->impl->maximize) { view->impl->maximize(view, maximize); } @@ -341,24 +345,6 @@ view_adjust_for_layout_change(struct view *view) } } -void -view_for_each_surface(struct view *view, wlr_surface_iterator_func_t iterator, - void *user_data) -{ - if (view->impl->for_each_surface) { - view->impl->for_each_surface(view, iterator, user_data); - } -} - -void -view_for_each_popup_surface(struct view *view, - wlr_surface_iterator_func_t iterator, void *data) -{ - if (view->impl->for_each_popup_surface) { - view->impl->for_each_popup_surface(view, iterator, data); - } -} - struct border view_border(struct view *view) { @@ -371,27 +357,9 @@ view_border(struct view *view) return border; } -static void -surface_enter_for_each_surface(struct wlr_surface *surface, int sx, int sy, - void *user_data) -{ - struct wlr_output *wlr_output = user_data; - wlr_surface_send_enter(surface, wlr_output); -} - -static void -surface_leave_for_each_surface(struct wlr_surface *surface, int sx, int sy, - void *user_data) -{ - struct wlr_output *wlr_output = user_data; - wlr_surface_send_leave(surface, wlr_output); -} - static void view_output_enter(struct view *view, struct wlr_output *wlr_output) { - view_for_each_surface(view, surface_enter_for_each_surface, - wlr_output); if (view->toplevel_handle) { wlr_foreign_toplevel_handle_v1_output_enter( view->toplevel_handle, wlr_output); @@ -401,8 +369,6 @@ view_output_enter(struct view *view, struct wlr_output *wlr_output) static void view_output_leave(struct view *view, struct wlr_output *wlr_output) { - view_for_each_surface(view, surface_leave_for_each_surface, - wlr_output); if (view->toplevel_handle) { wlr_foreign_toplevel_handle_v1_output_leave( view->toplevel_handle, wlr_output); diff --git a/src/xdg-popup.c b/src/xdg-popup.c index de5a925e..731967f2 100644 --- a/src/xdg-popup.c +++ b/src/xdg-popup.c @@ -3,63 +3,17 @@ * Copyright (C) 2020 the sway authors * * This file is only needed in support of - * - tracking damage * - unconstraining XDG popups */ #include "labwc.h" +/* TODO: surely this ought to just move to xdg.c now??? */ static void -xdg_popup_destroy(struct view_child *view_child) +popup_unconstrain(struct view *view, struct wlr_xdg_popup *popup) { - if (!view_child) { - return; - } - struct xdg_popup *popup = (struct xdg_popup *)view_child; - wl_list_remove(&popup->destroy.link); - wl_list_remove(&popup->map.link); - wl_list_remove(&popup->unmap.link); - wl_list_remove(&popup->new_popup.link); - view_child_finish(&popup->view_child); - free(popup); -} - -static void -handle_xdg_popup_map(struct wl_listener *listener, void *data) -{ - struct xdg_popup *popup = wl_container_of(listener, popup, map); - damage_all_outputs(popup->view_child.parent->server); -} - -static void -handle_xdg_popup_unmap(struct wl_listener *listener, void *data) -{ - struct xdg_popup *popup = wl_container_of(listener, popup, unmap); - damage_all_outputs(popup->view_child.parent->server); -} - -static void -handle_xdg_popup_destroy(struct wl_listener *listener, void *data) -{ - struct xdg_popup *popup = wl_container_of(listener, popup, destroy); - struct view_child *view_child = (struct view_child *)popup; - xdg_popup_destroy(view_child); -} - -static void -popup_handle_new_xdg_popup(struct wl_listener *listener, void *data) -{ - struct xdg_popup *popup = wl_container_of(listener, popup, new_popup); - struct wlr_xdg_popup *wlr_popup = data; - xdg_popup_create(popup->view_child.parent, wlr_popup); -} - -static void -popup_unconstrain(struct xdg_popup *popup) -{ - struct view *view = popup->view_child.parent; struct server *server = view->server; - struct wlr_box *popup_box = &popup->wlr_popup->geometry; + struct wlr_box *popup_box = &popup->geometry; struct wlr_output_layout *output_layout = server->output_layout; struct wlr_output *wlr_output = wlr_output_layout_output_at( output_layout, view->x + popup_box->x, view->y + popup_box->y); @@ -72,29 +26,11 @@ popup_unconstrain(struct xdg_popup *popup) .width = output_box->width, .height = output_box->height, }; - wlr_xdg_popup_unconstrain_from_box( - popup->wlr_popup, &output_toplevel_box); + wlr_xdg_popup_unconstrain_from_box(popup, &output_toplevel_box); } void xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup) { - struct xdg_popup *popup = calloc(1, sizeof(struct xdg_popup)); - if (!popup) { - return; - } - - popup->wlr_popup = wlr_popup; - view_child_init(&popup->view_child, view, wlr_popup->base->surface); - - popup->destroy.notify = handle_xdg_popup_destroy; - wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); - popup->map.notify = handle_xdg_popup_map; - wl_signal_add(&wlr_popup->base->events.map, &popup->map); - popup->unmap.notify = handle_xdg_popup_unmap; - wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap); - popup->new_popup.notify = popup_handle_new_xdg_popup; - wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); - - popup_unconstrain(popup); + popup_unconstrain(view, wlr_popup); } diff --git a/src/xdg.c b/src/xdg.c index 02b202e3..9920d3e5 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -3,10 +3,6 @@ #include "labwc.h" #include "ssd.h" -/* - * xdg_popup_create() and view_subsurface_create() are only called for the - * purposes of tracking damage. - */ static void handle_new_xdg_popup(struct wl_listener *listener, void *data) { @@ -15,14 +11,6 @@ handle_new_xdg_popup(struct wl_listener *listener, void *data) xdg_popup_create(view, wlr_popup); } -static void -new_subsurface_notify(struct wl_listener *listener, void *data) -{ - struct view *view = wl_container_of(listener, view, new_subsurface); - struct wlr_subsurface *wlr_subsurface = data; - view_subsurface_create(view, wlr_subsurface); -} - static bool has_ssd(struct view *view) { @@ -222,20 +210,6 @@ xdg_toplevel_view_close(struct view *view) wlr_xdg_toplevel_send_close(view->xdg_surface); } -static void -xdg_toplevel_view_for_each_popup_surface(struct view *view, - wlr_surface_iterator_func_t iterator, void *data) -{ - wlr_xdg_surface_for_each_popup_surface(view->xdg_surface, iterator, data); -} - -static void -xdg_toplevel_view_for_each_surface(struct view *view, - wlr_surface_iterator_func_t iterator, void *data) -{ - wlr_xdg_surface_for_each_surface(view->xdg_surface, iterator, data); -} - static void update_padding(struct view *view) { @@ -352,16 +326,6 @@ xdg_toplevel_view_map(struct view *view) if (!view->maximized && !view->fullscreen) { position_xdg_toplevel_view(view); } - - struct wlr_subsurface *subsurface; - wl_list_for_each(subsurface, &view->surface->current.subsurfaces_below, - current.link) { - view_subsurface_create(view, subsurface); - } - wl_list_for_each(subsurface, &view->surface->current.subsurfaces_above, - current.link) { - view_subsurface_create(view, subsurface); - } view_discover_output(view); view->been_mapped = true; @@ -370,9 +334,6 @@ xdg_toplevel_view_map(struct view *view) view->commit.notify = handle_commit; wl_signal_add(&view->xdg_surface->surface->events.commit, &view->commit); - view->new_subsurface.notify = new_subsurface_notify; - wl_signal_add(&view->surface->events.new_subsurface, - &view->new_subsurface); view_impl_map(view); } @@ -383,8 +344,8 @@ xdg_toplevel_view_unmap(struct view *view) if (view->mapped) { view->mapped = false; damage_all_outputs(view->server); + wlr_scene_node_destroy(view->scene_node); wl_list_remove(&view->commit.link); - wl_list_remove(&view->new_subsurface.link); desktop_focus_topmost_mapped_view(view->server); } } @@ -392,8 +353,6 @@ xdg_toplevel_view_unmap(struct view *view) static const struct view_impl xdg_toplevel_view_impl = { .configure = xdg_toplevel_view_configure, .close = xdg_toplevel_view_close, - .for_each_popup_surface = xdg_toplevel_view_for_each_popup_surface, - .for_each_surface = xdg_toplevel_view_for_each_surface, .get_string_prop = xdg_toplevel_view_get_string_prop, .map = xdg_toplevel_view_map, .move = xdg_toplevel_view_move, @@ -403,15 +362,39 @@ static const struct view_impl xdg_toplevel_view_impl = { .maximize = xdg_toplevel_view_maximize, }; +/* + * We use the following struct user_data pointers: + * - wlr_xdg_surface->data = view + * for the wlr_xdg_toplevel_decoration_v1 implementation + * - wlr_surface->data = scene_node + * to help the popups find their parent nodes + */ void xdg_surface_new(struct wl_listener *listener, void *data) { struct server *server = wl_container_of(listener, server, new_xdg_surface); struct wlr_xdg_surface *xdg_surface = data; - if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { + + /* + * We must add xdg popups to the scene graph so they get rendered. The + * wlroots scene graph provides a helper for this, but to use it we must + * provide the proper parent scene node of the xdg popup. To enable + * this, we always set the user data field of xdg_surfaces to the + * corresponding scene node. + */ + if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { + struct wlr_xdg_surface *parent = + wlr_xdg_surface_from_wlr_surface( + xdg_surface->popup->parent); + struct wlr_scene_node *parent_node = parent->surface->data; + xdg_surface->surface->data = + wlr_scene_xdg_surface_create(parent_node, xdg_surface); + /* TODO: unconstrain here rather than in xdg-popup.c? */ return; } + + /* WLR_XDG_SURFACE_ROLE_TOPLEVEL */ wlr_xdg_surface_ping(xdg_surface); struct view *view = calloc(1, sizeof(struct view)); @@ -421,8 +404,20 @@ xdg_surface_new(struct wl_listener *listener, void *data) view->xdg_surface = xdg_surface; wl_list_init(&view->ssd.parts); + view->scene_node = wlr_scene_xdg_surface_create( + &view->server->view_tree->node, view->xdg_surface); + if (!view->scene_node) { + wl_resource_post_no_memory(view->surface->resource); + return; + } + view->scene_node->data = view; + + /* In support of xdg_toplevel_decoration */ xdg_surface->data = view; + /* In support of xdg popups */ + xdg_surface->surface->data = view->scene_node; + view->map.notify = handle_map; wl_signal_add(&xdg_surface->events.map, &view->map); view->unmap.notify = handle_unmap; diff --git a/src/xwayland-unmanaged.c b/src/xwayland-unmanaged.c index b5c5922a..a80b9e6e 100644 --- a/src/xwayland-unmanaged.c +++ b/src/xwayland-unmanaged.c @@ -23,6 +23,22 @@ unmanaged_handle_commit(struct wl_listener *listener, void *data) damage_all_outputs(unmanaged->server); } +static struct view * +parent_view(struct server *server, struct wlr_xwayland_surface *surface) +{ + struct wlr_xwayland_surface *s = surface; + while (s->parent) { + s = s->parent; + } + struct view *view; + wl_list_for_each(view, &server->views, link) { + if (view->surface == s->surface) { + return view; + } + } + return NULL; +} + static void unmanaged_handle_map(struct wl_listener *listener, void *data) { @@ -42,6 +58,12 @@ unmanaged_handle_map(struct wl_listener *listener, void *data) if (wlr_xwayland_or_surface_wants_focus(xsurface)) { seat_focus_surface(&unmanaged->server->seat, xsurface->surface); } + + struct view *view = parent_view(unmanaged->server, xsurface); + struct wlr_scene_node *node = wlr_scene_subsurface_tree_create( + view->scene_node, xsurface->surface); + wlr_scene_node_set_position(node, unmanaged->lx - view->x, + unmanaged->ly - view->y); } static void diff --git a/src/xwayland.c b/src/xwayland.c index a8194562..a8d2f696 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -195,16 +195,6 @@ _close(struct view *view) damage_all_outputs(view->server); } -static void -for_each_surface(struct view *view, wlr_surface_iterator_func_t iterator, - void *data) -{ - if (!view->surface) { - return; - } - wlr_surface_for_each_surface(view->surface, iterator, data); -} - static const char * get_string_prop(struct view *view, const char *prop) { @@ -265,6 +255,15 @@ map(struct view *view) view->h = view->xwayland_surface->height; } view->surface = view->xwayland_surface->surface; + + view->scene_node = wlr_scene_surface_create( + &view->server->view_tree->node, view->surface); + if (!view->scene_node) { + wl_resource_post_no_memory(view->surface->resource); + return; + } + view->scene_node->data = view; + view->ssd.enabled = want_deco(view); if (view->ssd.enabled) { @@ -338,7 +337,6 @@ set_fullscreen(struct view *view, bool fullscreen) static const struct view_impl xwl_view_impl = { .configure = configure, .close = _close, - .for_each_surface = for_each_surface, .get_string_prop = get_string_prop, .map = map, .move = move, From 5934ed6682b4921bdb8cd7a0689ca3a59b74417f Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sat, 12 Feb 2022 19:43:32 +0000 Subject: [PATCH 03/56] Add debug_dump_scene() Dump view_tree and osd_tree on calling debug-action. Example bind: --- include/debug.h | 9 ++++++++ include/labwc.h | 2 ++ src/action.c | 3 ++- src/debug.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++ src/meson.build | 1 + 5 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 include/debug.h create mode 100644 src/debug.c diff --git a/include/debug.h b/include/debug.h new file mode 100644 index 00000000..5b4b4e0c --- /dev/null +++ b/include/debug.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __DEBUG_H +#define __DEBUG_H + +struct server; + +void debug_dump_scene(struct server *server); + +#endif /* __DEBUG_H */ diff --git a/include/labwc.h b/include/labwc.h index 439ddff8..c0b9d47b 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -173,6 +173,7 @@ struct server { struct view *cycle_view; struct theme *theme; + struct menu *rootmenu; struct menu *windowmenu; }; @@ -476,6 +477,7 @@ void server_start(struct server *server); void server_finish(struct server *server); /* update onscreen display 'alt-tab' buffer */ +void osd_finish(struct server *server); void osd_update(struct server *server); /* diff --git a/src/action.c b/src/action.c index dfb6ed59..23d3dcf3 100644 --- a/src/action.c +++ b/src/action.c @@ -3,6 +3,7 @@ #include #include "common/spawn.h" #include "common/zfree.h" +#include "debug.h" #include "labwc.h" #include "menu/menu.h" #include "ssd.h" @@ -161,7 +162,7 @@ action(struct view *activator, struct server *server, struct wl_list *actions, u } break; case ACTION_TYPE_DEBUG: - /* nothing */ + debug_dump_scene(server); break; case ACTION_TYPE_EXECUTE: { diff --git a/src/debug.c b/src/debug.c new file mode 100644 index 00000000..fda5d131 --- /dev/null +++ b/src/debug.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include "buffer.h" +#include "labwc.h" + +#define INDENT_SIZE (3) + +static char * +get_node_type(enum wlr_scene_node_type type) +{ + switch (type) { + case WLR_SCENE_NODE_ROOT: + return "root"; + case WLR_SCENE_NODE_TREE: + return "tree"; + case WLR_SCENE_NODE_SURFACE: + return "surface"; + case WLR_SCENE_NODE_RECT: + return "rect"; + case WLR_SCENE_NODE_BUFFER: + return "buffer"; + } + return "error"; +} + +static void +dump_tree(struct wlr_scene_node *node, int pos, int x, int y) +{ + char *type = get_node_type(node->type); + + if (pos) { + printf("%*c+-- ", pos, ' '); + } + printf("%s (%d,%d) [%p]\n", type, x, y, node); + + struct wlr_scene_node *child; + wl_list_for_each(child, &node->state.children, state.link) { + dump_tree(child, pos + INDENT_SIZE, x + child->state.x, + y + child->state.y); + } +} + +void +debug_dump_scene(struct server *server) +{ + struct wlr_scene_node *node; + + printf(":: view_tree ::\n"); + node = &server->view_tree->node; + dump_tree(node, 0, node->state.x, node->state.y); + + printf(":: osd_tree ::\n"); + node = &server->osd_tree->node; + dump_tree(node, 0, node->state.x, node->state.y); +} diff --git a/src/meson.build b/src/meson.build index 0bce7cd5..1f9c5146 100644 --- a/src/meson.build +++ b/src/meson.build @@ -3,6 +3,7 @@ labwc_sources = files( 'buffer.c', 'cursor.c', 'damage.c', + 'debug.c', 'desktop.c', 'foreign.c', 'interactive.c', From 45939097448b19d51c577630472fbef54a0f598a Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sat, 12 Feb 2022 22:04:27 +0000 Subject: [PATCH 04/56] osd: destroy old osd_tree children --- src/keyboard.c | 3 +-- src/osd.c | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/keyboard.c b/src/keyboard.c index 00e0ce47..1eef22a4 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -50,8 +50,7 @@ keyboard_modifiers_notify(struct wl_listener *listener, void *data) server->cycle_view); desktop_move_to_front(server->cycle_view); server->cycle_view = NULL; - wlr_scene_node_set_enabled( - &server->osd_tree->node, false); + osd_finish(server); } } diff --git a/src/osd.c b/src/osd.c index f8b1ef0e..f790cd16 100644 --- a/src/osd.c +++ b/src/osd.c @@ -74,11 +74,29 @@ get_osd_height(struct wl_list *views) return height; } +static void +destroy_osd_nodes(struct server *server) +{ + struct wlr_scene_node *child, *next; + struct wl_list *children = &server->osd_tree->node.state.children; + wl_list_for_each_safe(child, next, children, state.link) { + wlr_scene_node_destroy(child); + } +} + +void +osd_finish(struct server *server) +{ + destroy_osd_nodes(server); + wlr_scene_node_set_enabled(&server->osd_tree->node, false); +} + void osd_update(struct server *server) { struct theme *theme = server->theme; + destroy_osd_nodes(server); struct output *output; wl_list_for_each(output, &server->outputs, link) { float scale = output->wlr_output->scale; @@ -176,7 +194,6 @@ osd_update(struct server *server) pango_cairo_show_layout(cairo, layout); y += OSD_ITEM_HEIGHT; } - g_object_unref(layout); cairo_surface_flush(surf); From ebabc066ba9541e02bc47c4469f75a4ad65abc55 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sat, 12 Feb 2022 22:13:36 +0000 Subject: [PATCH 05/56] xwayland: in map() use wlr_scene_subsurface_tree_create() --- src/xwayland.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xwayland.c b/src/xwayland.c index a8d2f696..29475148 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -256,7 +256,7 @@ map(struct view *view) } view->surface = view->xwayland_surface->surface; - view->scene_node = wlr_scene_surface_create( + view->scene_node = wlr_scene_subsurface_tree_create( &view->server->view_tree->node, view->surface); if (!view->scene_node) { wl_resource_post_no_memory(view->surface->resource); From ba909e4fc7823ab3d131d574d22b30bc17c16cee Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sun, 13 Feb 2022 11:47:03 +0000 Subject: [PATCH 06/56] Refactor buffer implementation to take a cairo_t Remove buffer_drop() and just destroy it. --- include/buffer.h | 26 +++++------------------ src/buffer.c | 55 +++++++++++++++++++++--------------------------- src/osd.c | 10 ++------- 3 files changed, 31 insertions(+), 60 deletions(-) diff --git a/include/buffer.h b/include/buffer.h index 8c141f8a..dd841803 100644 --- a/include/buffer.h +++ b/include/buffer.h @@ -26,35 +26,19 @@ #ifndef __LABWC_BUFFER_H #define __LABWC_BUFFER_H +#include #include "labwc.h" -/** - * A read-only buffer that holds a data pointer. - * - * This is suitable for passing raw pixel data to a function that accepts a - * wlr_buffer. - */ struct lab_data_buffer { struct wlr_buffer base; - const void *data; + cairo_t *cairo; + void *data; uint32_t format; size_t stride; - - void *saved_data; }; -/** - * Wraps a read-only data pointer into a wlr_buffer. The data pointer may be - * accessed until readonly_data_buffer_drop() is called. - */ -struct lab_data_buffer *buffer_create(uint32_t format, size_t stride, - uint32_t width, uint32_t height, const void *data); - -/** - * Drops ownership of the buffer (see wlr_buffer_drop() for more details) and - * perform a copy of the data pointer if a consumer still has the buffer locked. - */ -bool buffer_drop(struct lab_data_buffer *buffer); +struct lab_data_buffer *buffer_create(cairo_t *cairo); +void buffer_destroy(struct lab_data_buffer *buffer); #endif /* __LABWC_BUFFER_H */ diff --git a/src/buffer.c b/src/buffer.c index 0188ee01..640a2189 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -42,7 +42,7 @@ static void data_buffer_destroy(struct wlr_buffer *wlr_buffer) { struct lab_data_buffer *buffer = data_buffer_from_buffer(wlr_buffer); - free(buffer->saved_data); + free(buffer->data); free(buffer); } @@ -50,13 +50,9 @@ static bool data_buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, uint32_t flags, void **data, uint32_t *format, size_t *stride) { - struct lab_data_buffer *buffer = data_buffer_from_buffer(wlr_buffer); - if (buffer->data == NULL) { - return false; - } - if (flags & WLR_BUFFER_DATA_PTR_ACCESS_WRITE) { - return false; - } + struct lab_data_buffer *buffer = + wl_container_of(wlr_buffer, buffer, base); + assert(buffer->data); *data = (void *)buffer->data; *format = buffer->format; *stride = buffer->stride; @@ -76,38 +72,35 @@ static const struct wlr_buffer_impl data_buffer_impl = { }; struct lab_data_buffer * -buffer_create(uint32_t format, size_t stride, uint32_t width, uint32_t height, - const void *data) +buffer_create(cairo_t *cairo) { struct lab_data_buffer *buffer = calloc(1, sizeof(*buffer)); - if (buffer == NULL) { + if (!buffer) { return NULL; } + cairo_surface_t *surf = cairo_get_target(cairo); + int width = cairo_image_surface_get_width(surf); + int height = cairo_image_surface_get_height(surf); wlr_buffer_init(&buffer->base, &data_buffer_impl, width, height); - buffer->data = data; - buffer->format = format; - buffer->stride = stride; + + buffer->cairo = cairo; + buffer->data = cairo_image_surface_get_data(surf); + buffer->format = DRM_FORMAT_ARGB8888; + buffer->stride = cairo_image_surface_get_stride(surf); + + if (!buffer->data) { + cairo_destroy(cairo); + free(buffer); + } return buffer; } - -bool -buffer_drop(struct lab_data_buffer *buffer) +void +buffer_destroy(struct lab_data_buffer *buffer) { - bool ok = true; - - if (buffer->base.n_locks > 0) { - size_t size = buffer->stride * buffer->base.height; - buffer->saved_data = malloc(size); - if (!buffer->saved_data) { - wlr_log_errno(WLR_ERROR, "allocation failed"); - ok = false; - buffer->data = NULL; - } else { - memcpy(buffer->saved_data, buffer->data, size); - buffer->data = buffer->saved_data; - } + if (!buffer) { + return; } + cairo_destroy(buffer->cairo); wlr_buffer_drop(&buffer->base); - return ok; } diff --git a/src/osd.c b/src/osd.c index f790cd16..9f7beea1 100644 --- a/src/osd.c +++ b/src/osd.c @@ -197,14 +197,8 @@ osd_update(struct server *server) g_object_unref(layout); cairo_surface_flush(surf); - unsigned char *data = cairo_image_surface_get_data(surf); - if (output->osd_buffer) { - buffer_drop(output->osd_buffer); - } - output->osd_buffer = buffer_create(DRM_FORMAT_ARGB8888, - cairo_image_surface_get_stride(surf), w, h, data); - - cairo_surface_destroy(surf); + buffer_destroy(output->osd_buffer); + output->osd_buffer = buffer_create(cairo); struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_create( &server->osd_tree->node, &output->osd_buffer->base); From f22bfc96b29295115675fed273fe3ffdb8af174d Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sun, 13 Feb 2022 13:00:26 +0000 Subject: [PATCH 07/56] buffer: create cairo_t inside buffer_create() --- include/buffer.h | 4 ++-- src/buffer.c | 30 +++++++++++++----------------- src/osd.c | 14 +++++++------- 3 files changed, 22 insertions(+), 26 deletions(-) diff --git a/include/buffer.h b/include/buffer.h index dd841803..63f92f87 100644 --- a/include/buffer.h +++ b/include/buffer.h @@ -38,7 +38,7 @@ struct lab_data_buffer { size_t stride; }; -struct lab_data_buffer *buffer_create(cairo_t *cairo); -void buffer_destroy(struct lab_data_buffer *buffer); +struct lab_data_buffer *buffer_create(uint32_t width, uint32_t height, + float scale); #endif /* __LABWC_BUFFER_H */ diff --git a/src/buffer.c b/src/buffer.c index 640a2189..69f55251 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -42,7 +42,12 @@ static void data_buffer_destroy(struct wlr_buffer *wlr_buffer) { struct lab_data_buffer *buffer = data_buffer_from_buffer(wlr_buffer); - free(buffer->data); + if (buffer->cairo) { + cairo_destroy(buffer->cairo); + } + if (buffer->data) { + free(buffer->data); + } free(buffer); } @@ -72,35 +77,26 @@ static const struct wlr_buffer_impl data_buffer_impl = { }; struct lab_data_buffer * -buffer_create(cairo_t *cairo) +buffer_create(uint32_t width, uint32_t height, float scale) { struct lab_data_buffer *buffer = calloc(1, sizeof(*buffer)); if (!buffer) { return NULL; } - cairo_surface_t *surf = cairo_get_target(cairo); - int width = cairo_image_surface_get_width(surf); - int height = cairo_image_surface_get_height(surf); wlr_buffer_init(&buffer->base, &data_buffer_impl, width, height); - buffer->cairo = cairo; + cairo_surface_t *surf = + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + cairo_surface_set_device_scale(surf, scale, scale); + + buffer->cairo = cairo_create(surf); buffer->data = cairo_image_surface_get_data(surf); buffer->format = DRM_FORMAT_ARGB8888; buffer->stride = cairo_image_surface_get_stride(surf); if (!buffer->data) { - cairo_destroy(cairo); + cairo_destroy(buffer->cairo); free(buffer); } return buffer; } - -void -buffer_destroy(struct lab_data_buffer *buffer) -{ - if (!buffer) { - return; - } - cairo_destroy(buffer->cairo); - wlr_buffer_drop(&buffer->base); -} diff --git a/src/osd.c b/src/osd.c index 9f7beea1..85b5e4fe 100644 --- a/src/osd.c +++ b/src/osd.c @@ -103,10 +103,13 @@ osd_update(struct server *server) int w = (OSD_ITEM_WIDTH + (2 * OSD_BORDER_WIDTH)) * scale; int h = get_osd_height(&server->views) * scale; - cairo_surface_t *surf = - cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); - cairo_surface_set_device_scale(surf, scale, scale); - cairo_t *cairo = cairo_create(surf); + if (output->osd_buffer) { + wlr_buffer_drop(&output->osd_buffer->base); + } + output->osd_buffer = buffer_create(w, h, scale); + + cairo_t *cairo = output->osd_buffer->cairo; + cairo_surface_t *surf = cairo_get_target(cairo); /* background */ set_source(cairo, theme->osd_bg_color); @@ -195,10 +198,7 @@ osd_update(struct server *server) y += OSD_ITEM_HEIGHT; } g_object_unref(layout); - cairo_surface_flush(surf); - buffer_destroy(output->osd_buffer); - output->osd_buffer = buffer_create(cairo); struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_create( &server->osd_tree->node, &output->osd_buffer->base); From 04ca9fbdf5c9ee85b888f4ae774c214d7a429595 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sun, 13 Feb 2022 21:59:14 +0000 Subject: [PATCH 08/56] buffer: use cairo_surface_destroy() ...rather than free(buffer->data) --- src/buffer.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index 69f55251..8ce275a5 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -43,10 +43,9 @@ data_buffer_destroy(struct wlr_buffer *wlr_buffer) { struct lab_data_buffer *buffer = data_buffer_from_buffer(wlr_buffer); if (buffer->cairo) { + cairo_surface_t *surf = cairo_get_target(buffer->cairo); cairo_destroy(buffer->cairo); - } - if (buffer->data) { - free(buffer->data); + cairo_surface_destroy(surf); } free(buffer); } @@ -96,7 +95,9 @@ buffer_create(uint32_t width, uint32_t height, float scale) if (!buffer->data) { cairo_destroy(buffer->cairo); + cairo_surface_destroy(surf); free(buffer); + buffer = NULL; } return buffer; } From b79744e7b0963a28ddfe0928ad8c089e836442a9 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Mon, 14 Feb 2022 20:20:16 +0000 Subject: [PATCH 09/56] Chase wlroots master We need it for wlr_scene_layer_surface_v1_create() --- include/labwc.h | 1 + include/layers.h | 1 - meson.build | 2 +- src/output.c | 12 ++++++------ src/server.c | 1 + src/xdg-popup.c | 13 +++++++------ src/xdg.c | 29 ++++++++++++----------------- subprojects/wlroots.wrap | 2 +- 8 files changed, 29 insertions(+), 32 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index c0b9d47b..6586fc55 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include diff --git a/include/layers.h b/include/layers.h index 35b41090..1613020b 100644 --- a/include/layers.h +++ b/include/layers.h @@ -2,7 +2,6 @@ #ifndef __LABWC_LAYERS_H #define __LABWC_LAYERS_H #include -#include #include struct server; diff --git a/meson.build b/meson.build index 09f6f0b2..e0da463e 100644 --- a/meson.build +++ b/meson.build @@ -37,7 +37,7 @@ if git.found() endif add_project_arguments('-DLABWC_VERSION=@0@'.format(version), language: 'c') -wlroots_version = ['>=0.15.0', '<0.16.0'] +wlroots_version = ['>=0.16.0', '<0.17.0'] wlroots_proj = subproject( 'wlroots', default_options: ['default_library=static', 'examples=false'], diff --git a/src/output.c b/src/output.c index fc69a294..297e4260 100644 --- a/src/output.c +++ b/src/output.c @@ -287,12 +287,12 @@ wlr_output_configuration_v1 *create_output_config(struct server *server) wlr_output_configuration_v1_destroy(config); return NULL; } - struct wlr_box *box = - wlr_output_layout_get_box(server->output_layout, - output->wlr_output); - if (box) { - head->state.x = box->x; - head->state.y = box->y; + struct wlr_box box; + wlr_output_layout_get_box(server->output_layout, + output->wlr_output, &box); + if (!wlr_box_empty(&box)) { + head->state.x = box.x; + head->state.y = box.y; } else { wlr_log(WLR_ERROR, "failed to get output layout box"); } diff --git a/src/server.c b/src/server.c index fd59599a..171ec6dd 100644 --- a/src/server.c +++ b/src/server.c @@ -240,6 +240,7 @@ server_init(struct server *server) wlr_log(WLR_ERROR, "unable to create the wlroots compositor"); exit(EXIT_FAILURE); } + wlr_subcompositor_create(server->wl_display); struct wlr_data_device_manager *device_manager = NULL; device_manager = wlr_data_device_manager_create(server->wl_display); diff --git a/src/xdg-popup.c b/src/xdg-popup.c index 731967f2..7a466536 100644 --- a/src/xdg-popup.c +++ b/src/xdg-popup.c @@ -17,14 +17,15 @@ popup_unconstrain(struct view *view, struct wlr_xdg_popup *popup) struct wlr_output_layout *output_layout = server->output_layout; struct wlr_output *wlr_output = wlr_output_layout_output_at( output_layout, view->x + popup_box->x, view->y + popup_box->y); - struct wlr_box *output_box = wlr_output_layout_get_box( - output_layout, wlr_output); + + struct wlr_box output_box; + wlr_output_layout_get_box(output_layout, wlr_output, &output_box); struct wlr_box output_toplevel_box = { - .x = output_box->x - view->x, - .y = output_box->y - view->y, - .width = output_box->width, - .height = output_box->height, + .x = output_box.x - view->x, + .y = output_box.y - view->y, + .width = output_box.width, + .height = output_box.height, }; wlr_xdg_popup_unconstrain_from_box(popup, &output_toplevel_box); } diff --git a/src/xdg.c b/src/xdg.c index 9920d3e5..a820eacf 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -127,29 +127,23 @@ static void handle_request_minimize(struct wl_listener *listener, void *data) { struct view *view = wl_container_of(listener, view, request_minimize); - struct wlr_xdg_surface *surface = data; - if (view) { - view_minimize(view, surface->toplevel->requested.minimized); - } + view_minimize(view, view->xdg_surface->toplevel->requested.minimized); } static void handle_request_maximize(struct wl_listener *listener, void *data) { struct view *view = wl_container_of(listener, view, request_maximize); - struct wlr_xdg_surface *surface = data; - if (view) { - view_maximize(view, surface->toplevel->requested.maximized); - } - + view_maximize(view, view->xdg_surface->toplevel->requested.maximized); } static void handle_request_fullscreen(struct wl_listener *listener, void *data) { struct view *view = wl_container_of(listener, view, request_fullscreen); - struct wlr_xdg_toplevel_set_fullscreen_event *e = data; - view_set_fullscreen(view, e->fullscreen, e->output); + view_set_fullscreen(view, + view->xdg_surface->toplevel->requested.fullscreen, + view->xdg_surface->toplevel->requested.fullscreen_output); } static void @@ -182,7 +176,7 @@ xdg_toplevel_view_configure(struct view *view, struct wlr_box geo) view->pending_move_resize.width = MAX(geo.width, min_width); view->pending_move_resize.height = MAX(geo.height, min_height); - uint32_t serial = wlr_xdg_toplevel_set_size(view->xdg_surface, + uint32_t serial = wlr_xdg_toplevel_set_size(view->xdg_surface->toplevel, (uint32_t)geo.width, (uint32_t)geo.height); if (serial > 0) { view->pending_move_resize.configure_serial = serial; @@ -207,7 +201,7 @@ xdg_toplevel_view_move(struct view *view, double x, double y) static void xdg_toplevel_view_close(struct view *view) { - wlr_xdg_toplevel_send_close(view->xdg_surface); + wlr_xdg_toplevel_send_close(view->xdg_surface->toplevel); } static void @@ -222,7 +216,7 @@ update_padding(struct view *view) static void xdg_toplevel_view_maximize(struct view *view, bool maximized) { - wlr_xdg_toplevel_set_maximized(view->xdg_surface, maximized); + wlr_xdg_toplevel_set_maximized(view->xdg_surface->toplevel, maximized); } static void @@ -230,14 +224,14 @@ xdg_toplevel_view_set_activated(struct view *view, bool activated) { struct wlr_xdg_surface *surface = view->xdg_surface; if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { - wlr_xdg_toplevel_set_activated(surface, activated); + wlr_xdg_toplevel_set_activated(surface->toplevel, activated); } } static void xdg_toplevel_view_set_fullscreen(struct view *view, bool fullscreen) { - wlr_xdg_toplevel_set_fullscreen(view->xdg_surface, fullscreen); + wlr_xdg_toplevel_set_fullscreen(view->xdg_surface->toplevel, fullscreen); } static bool @@ -251,7 +245,8 @@ parent_of(struct view *view) { struct view *p; wl_list_for_each (p, &view->server->views, link) { - if (p->xdg_surface == view->xdg_surface->toplevel->parent) { + if (p->xdg_surface->toplevel + == view->xdg_surface->toplevel->parent) { return p; } } diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap index 314d2c36..3c65aaac 100644 --- a/subprojects/wlroots.wrap +++ b/subprojects/wlroots.wrap @@ -1,3 +1,3 @@ [wrap-git] url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = 0.15.1 +revision = master From 177c1301839b3e329ff34778b13230f1c4b690dd Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Mon, 14 Feb 2022 20:22:23 +0000 Subject: [PATCH 10/56] build: set 'check: false' on run_command Future meson releases will change the default, so we explicitly set check: false to maintain behaviour --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index e0da463e..7aea92b2 100644 --- a/meson.build +++ b/meson.build @@ -30,7 +30,7 @@ add_project_arguments(cc.get_supported_arguments( version='"@0@"'.format(meson.project_version()) git = find_program('git', native: true, required: false) if git.found() - git_commit = run_command([git, 'describe', '--dirty']) + git_commit = run_command([git, 'describe', '--dirty'], check: false) if git_commit.returncode() == 0 version = '"@0@"'.format(git_commit.stdout().strip()) endif From 9fed8e0998a03cc3e08737ff2c9a2da65b93d55e Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sun, 20 Feb 2022 13:14:10 +0000 Subject: [PATCH 11/56] Handle popups under xdg-toplevels in xdg-popup.c ...to make it easier to deal them separetely from layer-shell popups --- src/xdg-popup.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++- src/xdg.c | 17 +++---------- 2 files changed, 65 insertions(+), 15 deletions(-) diff --git a/src/xdg-popup.c b/src/xdg-popup.c index 7a466536..df500a8a 100644 --- a/src/xdg-popup.c +++ b/src/xdg-popup.c @@ -4,11 +4,24 @@ * * This file is only needed in support of * - unconstraining XDG popups + * - keeping non-layer-shell xdg-popups outside the layers.c code */ #include "labwc.h" -/* TODO: surely this ought to just move to xdg.c now??? */ +struct view_child { + struct wlr_surface *surface; + struct view *parent; +}; + +struct xdg_popup { + struct view_child view_child; + struct wlr_xdg_popup *wlr_popup; + + struct wl_listener destroy; + struct wl_listener new_popup; +}; + static void popup_unconstrain(struct view *view, struct wlr_xdg_popup *popup) { @@ -30,8 +43,56 @@ popup_unconstrain(struct view *view, struct wlr_xdg_popup *popup) wlr_xdg_popup_unconstrain_from_box(popup, &output_toplevel_box); } +static void +handle_xdg_popup_destroy(struct wl_listener *listener, void *data) +{ + struct xdg_popup *popup = wl_container_of(listener, popup, destroy); + wl_list_remove(&popup->destroy.link); + wl_list_remove(&popup->new_popup.link); + free(popup); +} + +static void +popup_handle_new_xdg_popup(struct wl_listener *listener, void *data) +{ + struct xdg_popup *popup = wl_container_of(listener, popup, new_popup); + struct wlr_xdg_popup *wlr_popup = data; + xdg_popup_create(popup->view_child.parent, wlr_popup); +} + void xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup) { + struct xdg_popup *popup = calloc(1, sizeof(struct xdg_popup)); + if (!popup) { + return; + } + + popup->wlr_popup = wlr_popup; + popup->view_child.parent = view; + popup->view_child.surface = wlr_popup->base->surface; + + popup->destroy.notify = handle_xdg_popup_destroy; + wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); + popup->new_popup.notify = popup_handle_new_xdg_popup; + wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); + + /* + * We must add xdg popups to the scene graph so they get rendered. The + * wlroots scene graph provides a helper for this, but to use it we must + * provide the proper parent scene node of the xdg popup. To enable + * this, we always set the user data field of xdg_surfaces to the + * corresponding scene node. + */ + if (!wlr_surface_is_xdg_surface(wlr_popup->parent)) { + wlr_log(WLR_ERROR, "xdg_surface is not xdg"); + return; + } + struct wlr_xdg_surface *parent = + wlr_xdg_surface_from_wlr_surface(wlr_popup->parent); + struct wlr_scene_node *parent_node = parent->surface->data; + wlr_popup->base->surface->data = + wlr_scene_xdg_surface_create(parent_node, wlr_popup->base); + popup_unconstrain(view, wlr_popup); } diff --git a/src/xdg.c b/src/xdg.c index a820eacf..c3a8b0af 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -372,24 +372,13 @@ xdg_surface_new(struct wl_listener *listener, void *data) struct wlr_xdg_surface *xdg_surface = data; /* - * We must add xdg popups to the scene graph so they get rendered. The - * wlroots scene graph provides a helper for this, but to use it we must - * provide the proper parent scene node of the xdg popup. To enable - * this, we always set the user data field of xdg_surfaces to the - * corresponding scene node. + * We deal with popups in xdg-popup.c and layers.c as they have to be + * treated differently */ - if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { - struct wlr_xdg_surface *parent = - wlr_xdg_surface_from_wlr_surface( - xdg_surface->popup->parent); - struct wlr_scene_node *parent_node = parent->surface->data; - xdg_surface->surface->data = - wlr_scene_xdg_surface_create(parent_node, xdg_surface); - /* TODO: unconstrain here rather than in xdg-popup.c? */ + if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { return; } - /* WLR_XDG_SURFACE_ROLE_TOPLEVEL */ wlr_xdg_surface_ping(xdg_surface); struct view *view = calloc(1, sizeof(struct view)); From 688abf7db19516317c5b128e6046a8cdf79a850b Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sun, 20 Feb 2022 13:15:58 +0000 Subject: [PATCH 12/56] Handle layers under the scene-graph API This excludes popups. --- include/labwc.h | 1 + include/layers.h | 4 +- src/debug.c | 30 ++- src/layers.c | 621 +++++++++++++++-------------------------------- src/output.c | 31 ++- 5 files changed, 249 insertions(+), 438 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index 6586fc55..d0555290 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -185,6 +185,7 @@ struct output { struct wlr_output *wlr_output; struct wlr_scene_output *scene_output; struct wl_list layers[4]; + struct wlr_scene_tree *layer_tree[4]; struct wlr_box usable_area; struct lab_data_buffer *osd_buffer; diff --git a/include/layers.h b/include/layers.h index 1613020b..0eaccbb0 100644 --- a/include/layers.h +++ b/include/layers.h @@ -12,8 +12,8 @@ enum layer_parent { }; struct lab_layer_surface { - struct wlr_layer_surface_v1 *layer_surface; - struct wl_list link; + struct wl_list link; /* output::layers[] */ + struct wlr_scene_layer_surface_v1 *scene_layer_surface; struct wl_listener destroy; struct wl_listener map; diff --git a/src/debug.c b/src/debug.c index fda5d131..46ff3723 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +#include #include #include "buffer.h" #include "labwc.h" @@ -40,6 +41,23 @@ dump_tree(struct wlr_scene_node *node, int pos, int x, int y) } } +static char * +get_layer_name(uint32_t layer) +{ + switch (layer) { + case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND: + return "background"; + case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM: + return "bottom"; + case ZWLR_LAYER_SHELL_V1_LAYER_TOP: + return "top"; + case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY: + return "overlay"; + default: + abort(); + } +} + void debug_dump_scene(struct server *server) { @@ -49,7 +67,13 @@ debug_dump_scene(struct server *server) node = &server->view_tree->node; dump_tree(node, 0, node->state.x, node->state.y); - printf(":: osd_tree ::\n"); - node = &server->osd_tree->node; - dump_tree(node, 0, node->state.x, node->state.y); + printf(":: layer_tree ::\n"); + struct output *output; + wl_list_for_each(output, &server->outputs, link) { + for (int i = 0; i < 4; i++) { + node = &output->layer_tree[i]->node; + printf("layer-%s\n", get_layer_name(i)); + dump_tree(node, 0, node->state.x, node->state.y); + } + } } diff --git a/src/layers.c b/src/layers.c index 1200c95a..59e365b2 100644 --- a/src/layers.c +++ b/src/layers.c @@ -17,195 +17,38 @@ #include "layers.h" #include "labwc.h" -static void -apply_exclusive(struct wlr_box *usable_area, uint32_t anchor, int32_t exclusive, - int32_t margin_top, int32_t margin_right, int32_t margin_bottom, - int32_t margin_left) -{ - if (exclusive <= 0) { - return; - } - struct { - uint32_t anchors; - int *positive_axis; - int *negative_axis; - int margin; - } edges[] = { - { - .anchors = - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, - .positive_axis = &usable_area->y, - .negative_axis = &usable_area->height, - .margin = margin_top, - }, - { - .anchors = - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, - .positive_axis = NULL, - .negative_axis = &usable_area->height, - .margin = margin_bottom, - }, - { - .anchors = - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, - .positive_axis = &usable_area->x, - .negative_axis = &usable_area->width, - .margin = margin_left, - }, - { - .anchors = - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, - .positive_axis = NULL, - .negative_axis = &usable_area->width, - .margin = margin_right, - }, - }; - for (size_t i = 0; i < sizeof(edges) / sizeof(edges[0]); ++i) { - if ((anchor & edges[i].anchors) == edges[i].anchors) { - if (edges[i].positive_axis) { - *edges[i].positive_axis += - exclusive + edges[i].margin; - } - if (edges[i].negative_axis) { - *edges[i].negative_axis -= - exclusive + edges[i].margin; - } - } - } -} - -/** - * @list: struct lab_layer_surface - */ -static void -arrange_layer(struct wlr_output *output, struct wl_list *list, - struct wlr_box *usable_area, bool exclusive) -{ - struct lab_layer_surface *surface; - struct wlr_box full_area = { 0 }; - wlr_output_effective_resolution(output, &full_area.width, - &full_area.height); - wl_list_for_each_reverse(surface, list, link) { - struct wlr_layer_surface_v1 *layer = surface->layer_surface; - struct wlr_layer_surface_v1_state *state = &layer->current; - if (exclusive != (state->exclusive_zone > 0)) { - continue; - } - struct wlr_box bounds; - if (state->exclusive_zone == -1) { - bounds = full_area; - } else { - bounds = *usable_area; - } - struct wlr_box box = { - .width = state->desired_width, - .height = state->desired_height - }; - /* Horizontal axis */ - const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT - | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - if ((state->anchor & both_horiz) && box.width == 0) { - box.x = bounds.x; - box.width = bounds.width; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { - box.x = bounds.x; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { - box.x = bounds.x + (bounds.width - box.width); - } else { - box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); - } - /* Vertical axis */ - const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP - | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - if ((state->anchor & both_vert) && box.height == 0) { - box.y = bounds.y; - box.height = bounds.height; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { - box.y = bounds.y; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { - box.y = bounds.y + (bounds.height - box.height); - } else { - box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); - } - /* Margin */ - if ((state->anchor & both_horiz) == both_horiz) { - box.x += state->margin.left; - box.width -= state->margin.left + state->margin.right; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { - box.x += state->margin.left; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { - box.x -= state->margin.right; - } - if ((state->anchor & both_vert) == both_vert) { - box.y += state->margin.top; - box.height -= state->margin.top + state->margin.bottom; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { - box.y += state->margin.top; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { - box.y -= state->margin.bottom; - } - if (box.width < 0 || box.height < 0) { - wlr_log(WLR_ERROR, "surface has no positive size"); - continue; - } - - /* Apply */ - surface->geo = box; - apply_exclusive(usable_area, state->anchor, - state->exclusive_zone, state->margin.top, - state->margin.right, state->margin.bottom, - state->margin.left); - wlr_layer_surface_v1_configure(layer, box.width, box.height); - } -} - void arrange_layers(struct output *output) { - assert(output); + struct server *server = output->server; + struct wlr_scene_output *scene_output = + wlr_scene_get_scene_output(server->scene, output->wlr_output); - struct wlr_box usable_area = { 0 }; + struct wlr_box full_area = { 0 }; wlr_output_effective_resolution(output->wlr_output, - &usable_area.width, &usable_area.height); + &full_area.width, &full_area.height); + struct wlr_box usable_area = full_area; - /* Exclusive surfaces */ - arrange_layer(output->wlr_output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], - &usable_area, true); - arrange_layer(output->wlr_output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], - &usable_area, true); - arrange_layer(output->wlr_output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], - &usable_area, true); - arrange_layer(output->wlr_output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - &usable_area, true); - memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); + for (int i = 0; i < 4; i++) { + struct lab_layer_surface *lab_layer_surface; + wl_list_for_each(lab_layer_surface, &output->layers[i], link) { + struct wlr_scene_layer_surface_v1 *scene_layer_surface = + lab_layer_surface->scene_layer_surface; + wlr_scene_layer_surface_v1_configure( + scene_layer_surface, &full_area, &usable_area); + } + } - /* TODO: re-arrange all views taking into account updated usable_area */ + for (int i = 0; i < 4; i++) { + struct wlr_scene_node *node = &output->layer_tree[i]->node; + wlr_scene_node_set_position(node, + scene_output->x + usable_area.x, + scene_output->y + usable_area.y); + } - /* Non-exclusive surfaces */ - arrange_layer(output->wlr_output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], - &usable_area, false); - arrange_layer(output->wlr_output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], - &usable_area, false); - arrange_layer(output->wlr_output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], - &usable_area, false); - arrange_layer(output->wlr_output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - &usable_area, false); + + /* TODO: workout how we set this */ +// memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); /* Find topmost keyboard interactive layer, if such a layer exists */ uint32_t layers_above_shell[] = { @@ -217,7 +60,7 @@ arrange_layers(struct output *output) for (size_t i = 0; i < nlayers; ++i) { wl_list_for_each_reverse (layer, &output->layers[layers_above_shell[i]], link) { - if (layer->layer_surface->current.keyboard_interactive) { + if (layer->scene_layer_surface->layer_surface->current.keyboard_interactive) { topmost = layer; break; } @@ -228,7 +71,8 @@ arrange_layers(struct output *output) } struct seat *seat = &output->server->seat; if (topmost) { - seat_set_focus_layer(seat, topmost->layer_surface); + seat_set_focus_layer(seat, + topmost->scene_layer_surface->layer_surface); } else if (seat->focused_layer && !seat->focused_layer->current.keyboard_interactive) { seat_set_focus_layer(seat, NULL); @@ -240,9 +84,8 @@ output_destroy_notify(struct wl_listener *listener, void *data) { struct lab_layer_surface *layer = wl_container_of(listener, layer, output_destroy); - layer->layer_surface->output = NULL; wl_list_remove(&layer->output_destroy.link); - wlr_layer_surface_v1_destroy(layer->layer_surface); + wlr_layer_surface_v1_destroy(layer->scene_layer_surface->layer_surface); } static void @@ -250,8 +93,10 @@ surface_commit_notify(struct wl_listener *listener, void *data) { struct lab_layer_surface *layer = wl_container_of(listener, layer, surface_commit); - struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface; - struct wlr_output *wlr_output = layer->layer_surface->output; + struct wlr_layer_surface_v1 *layer_surface = + layer->scene_layer_surface->layer_surface; + struct wlr_output *wlr_output = + layer->scene_layer_surface->layer_surface->output; if (!wlr_output) { return; @@ -264,14 +109,13 @@ surface_commit_notify(struct wl_listener *listener, void *data) output_from_wlr_output(layer->server, wlr_output); arrange_layers(output); } - damage_all_outputs(layer->server); } static void unmap(struct lab_layer_surface *layer) { struct seat *seat = &layer->server->seat; - if (seat->focused_layer == layer->layer_surface) { + if (seat->focused_layer == layer->scene_layer_surface->layer_surface) { seat_set_focus_layer(seat, NULL); } damage_all_outputs(layer->server); @@ -282,17 +126,19 @@ destroy_notify(struct wl_listener *listener, void *data) { struct lab_layer_surface *layer = wl_container_of( listener, layer, destroy); - if (layer->layer_surface->mapped) { + if (layer->scene_layer_surface->layer_surface->mapped) { unmap(layer); } + + /* TODO: sort this out properly */ wl_list_remove(&layer->link); wl_list_remove(&layer->destroy.link); wl_list_remove(&layer->map.link); wl_list_remove(&layer->surface_commit.link); - if (layer->layer_surface->output) { + if (layer->scene_layer_surface->layer_surface->output) { wl_list_remove(&layer->output_destroy.link); struct output *output = output_from_wlr_output( - layer->server, layer->layer_surface->output); + layer->server, layer->scene_layer_surface->layer_surface->output); arrange_layers(output); } free(layer); @@ -312,224 +158,149 @@ map_notify(struct wl_listener *listener, void *data) wlr_surface_send_enter(l->surface, l->output); } -static void -subsurface_damage(struct lab_layer_subsurface *subsurface, bool whole) -{ - struct lab_layer_surface *layer = subsurface->layer_surface; - struct wlr_output *wlr_output = layer->layer_surface->output; - if (!wlr_output) { - return; - } -// struct output *output = wlr_output->data; -// int ox = subsurface->wlr_subsurface->current.x + layer->geo.x; -// int oy = subsurface->wlr_subsurface->current.y + layer->geo.y; - damage_all_outputs(layer->server); -} - -static void -subsurface_handle_unmap(struct wl_listener *listener, void *data) -{ - struct lab_layer_subsurface *subsurface = - wl_container_of(listener, subsurface, unmap); - subsurface_damage(subsurface, true); -} - -static void -subsurface_handle_map(struct wl_listener *listener, void *data) -{ - struct lab_layer_subsurface *subsurface = - wl_container_of(listener, subsurface, map); - subsurface_damage(subsurface, true); -} - -static void -subsurface_handle_commit(struct wl_listener *listener, void *data) -{ - struct lab_layer_subsurface *subsurface = - wl_container_of(listener, subsurface, commit); - subsurface_damage(subsurface, false); -} - -static void -subsurface_handle_destroy(struct wl_listener *listener, void *data) -{ - struct lab_layer_subsurface *subsurface = - wl_container_of(listener, subsurface, destroy); - - wl_list_remove(&subsurface->map.link); - wl_list_remove(&subsurface->unmap.link); - wl_list_remove(&subsurface->destroy.link); - wl_list_remove(&subsurface->commit.link); - free(subsurface); -} - -static struct -lab_layer_subsurface *create_subsurface(struct wlr_subsurface *wlr_subsurface, - struct lab_layer_surface *layer_surface) -{ - struct lab_layer_subsurface *subsurface = - calloc(1, sizeof(struct lab_layer_subsurface)); - if (!subsurface) { - return NULL; - } - - subsurface->wlr_subsurface = wlr_subsurface; - subsurface->layer_surface = layer_surface; - - subsurface->map.notify = subsurface_handle_map; - wl_signal_add(&wlr_subsurface->events.map, &subsurface->map); - subsurface->unmap.notify = subsurface_handle_unmap; - wl_signal_add(&wlr_subsurface->events.unmap, &subsurface->unmap); - subsurface->destroy.notify = subsurface_handle_destroy; - wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); - subsurface->commit.notify = subsurface_handle_commit; - wl_signal_add(&wlr_subsurface->surface->events.commit, - &subsurface->commit); - - return subsurface; -} - -static void -new_subsurface_notify(struct wl_listener *listener, void *data) -{ - struct lab_layer_surface *lab_layer_surface = - wl_container_of(listener, lab_layer_surface, new_subsurface); - struct wlr_subsurface *wlr_subsurface = data; - create_subsurface(wlr_subsurface, lab_layer_surface); -} - - -static struct -lab_layer_surface *popup_get_layer(struct lab_layer_popup *popup) -{ - while (popup->parent_type == LAYER_PARENT_POPUP) { - popup = popup->parent_popup; - } - return popup->parent_layer; -} - -static void -popup_damage(struct lab_layer_popup *layer_popup, bool whole) -{ - struct lab_layer_surface *layer; - while (true) { - if (layer_popup->parent_type == LAYER_PARENT_POPUP) { - layer_popup = layer_popup->parent_popup; - } else { - layer = layer_popup->parent_layer; - break; - } - } - damage_all_outputs(layer->server); -} - -static void -popup_handle_map(struct wl_listener *listener, void *data) -{ - struct lab_layer_popup *popup = wl_container_of(listener, popup, map); - struct lab_layer_surface *layer = popup_get_layer(popup); - struct wlr_output *wlr_output = layer->layer_surface->output; - wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output); - popup_damage(popup, true); -} - -static void -popup_handle_unmap(struct wl_listener *listener, void *data) -{ - struct lab_layer_popup *popup = wl_container_of(listener, popup, unmap); - popup_damage(popup, true); -} - -static void -popup_handle_commit(struct wl_listener *listener, void *data) -{ - struct lab_layer_popup *popup = wl_container_of(listener, popup, commit); - popup_damage(popup, false); -} - -static void -popup_handle_destroy(struct wl_listener *listener, void *data) -{ - struct lab_layer_popup *popup = - wl_container_of(listener, popup, destroy); - - wl_list_remove(&popup->map.link); - wl_list_remove(&popup->unmap.link); - wl_list_remove(&popup->destroy.link); - wl_list_remove(&popup->commit.link); - free(popup); -} - -static void -popup_unconstrain(struct lab_layer_popup *popup) -{ - struct lab_layer_surface *layer = popup_get_layer(popup); - struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; - struct output *output = layer->layer_surface->output->data; - - struct wlr_box output_box = { 0 }; - wlr_output_effective_resolution(output->wlr_output, &output_box.width, - &output_box.height); - - struct wlr_box output_toplevel_sx_box = { - .x = -layer->geo.x, - .y = -layer->geo.y, - .width = output_box.width, - .height = output_box.height, - }; - - wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); -} - -static void popup_handle_new_popup(struct wl_listener *listener, void *data); - -static struct lab_layer_popup * -create_popup(struct wlr_xdg_popup *wlr_popup, - enum layer_parent parent_type, void *parent) -{ - struct lab_layer_popup *popup = - calloc(1, sizeof(struct lab_layer_popup)); - if (!popup) { - return NULL; - } - - popup->wlr_popup = wlr_popup; - popup->parent_type = parent_type; - popup->parent_layer = parent; - - popup->map.notify = popup_handle_map; - wl_signal_add(&wlr_popup->base->events.map, &popup->map); - popup->unmap.notify = popup_handle_unmap; - wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap); - popup->destroy.notify = popup_handle_destroy; - wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); - popup->commit.notify = popup_handle_commit; - wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); - popup->new_popup.notify = popup_handle_new_popup; - wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); - - popup_unconstrain(popup); - - return popup; -} - -static void -popup_handle_new_popup(struct wl_listener *listener, void *data) -{ - struct lab_layer_popup *lab_layer_popup = - wl_container_of(listener, lab_layer_popup, new_popup); - struct wlr_xdg_popup *wlr_popup = data; - create_popup(wlr_popup, LAYER_PARENT_POPUP, lab_layer_popup); -} - -static void -new_popup_notify(struct wl_listener *listener, void *data) -{ - struct lab_layer_surface *lab_layer_surface = - wl_container_of(listener, lab_layer_surface, new_popup); - struct wlr_xdg_popup *wlr_popup = data; - create_popup(wlr_popup, LAYER_PARENT_LAYER, lab_layer_surface); -} +//static struct +//lab_layer_surface *popup_get_layer(struct lab_layer_popup *popup) +//{ +// while (popup->parent_type == LAYER_PARENT_POPUP) { +// popup = popup->parent_popup; +// } +// return popup->parent_layer; +//} +// +//static void +//popup_damage(struct lab_layer_popup *layer_popup, bool whole) +//{ +// struct lab_layer_surface *layer; +// while (true) { +// if (layer_popup->parent_type == LAYER_PARENT_POPUP) { +// layer_popup = layer_popup->parent_popup; +// } else { +// layer = layer_popup->parent_layer; +// break; +// } +// } +// damage_all_outputs(layer->server); +//} +// +//static void +//popup_handle_map(struct wl_listener *listener, void *data) +//{ +// struct lab_layer_popup *popup = wl_container_of(listener, popup, map); +// struct lab_layer_surface *layer = popup_get_layer(popup); +// struct wlr_output *wlr_output = layer->scene_layer_surface->layer_surface->output; +// wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output); +// popup_damage(popup, true); +//} +// +//static void +//popup_handle_unmap(struct wl_listener *listener, void *data) +//{ +// struct lab_layer_popup *popup = wl_container_of(listener, popup, unmap); +// popup_damage(popup, true); +//} +// +//static void +//popup_handle_commit(struct wl_listener *listener, void *data) +//{ +// struct lab_layer_popup *popup = wl_container_of(listener, popup, commit); +// popup_damage(popup, false); +//} +// +//static void +//popup_handle_destroy(struct wl_listener *listener, void *data) +//{ +// struct lab_layer_popup *popup = +// wl_container_of(listener, popup, destroy); +// wl_list_remove(&popup->destroy.link); +// free(popup); +//} +// +//static void +//popup_unconstrain(struct lab_layer_popup *popup) +//{ +// struct lab_layer_surface *layer = popup_get_layer(popup); +// struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; +// struct output *output = layer->scene_layer_surface->layer_surface->output->data; +// +// struct wlr_box output_box = { 0 }; +// wlr_output_effective_resolution(output->wlr_output, &output_box.width, +// &output_box.height); +// +// struct wlr_box output_toplevel_sx_box = { +// .x = -layer->geo.x, +// .y = -layer->geo.y, +// .width = output_box.width, +// .height = output_box.height, +// }; +// +// wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); +//} +// +//static void popup_handle_new_popup(struct wl_listener *listener, void *data); +// +//static struct lab_layer_popup * +//create_popup(struct wlr_xdg_popup *wlr_popup, +// enum layer_parent parent_type, void *parent) +//{ +// struct lab_layer_popup *popup = +// calloc(1, sizeof(struct lab_layer_popup)); +// if (!popup) { +// return NULL; +// } +// +// struct lab_layer_surface *layer = parent_type == LAYER_PARENT_LAYER +// ? (struct lab_layer_surface *)parent +// : (struct lab_layer_popup *)parent; +// struct server *server = layer->server; +// +// popup->wlr_popup = wlr_popup; +// popup->parent_type = parent_type; +// popup->parent_layer = parent; +// +// popup->destroy.notify = popup_handle_destroy; +// wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); +// popup->new_popup.notify = popup_handle_new_popup; +// wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); +// +// if (!wlr_surface_is_layer_surface(wlr_popup->base->surface)) { +// wlr_log(WLR_ERROR, "xdg_surface is not layer surface"); +// return; +// } +// +// struct wlr_output *wlr_output = +// layer->scene_layer_surface->layer_surface->data; +// struct output *output = output_from_wlr_output(server, wlr_output); +// +// struct wlr_scene_tree *selected_layer = +// output->layer_tree[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]; +// struct wlr_scene_node *node = +// wlr_scene_layer_surface_v1_create(&server->view_tree->node, +// wlr_popup->base->surface->data); +// wlr_popup->base->surface->data = +// wlr_scene_xdg_surface_create(&selected_layer->node, wlr_popup->base); +// +// popup_unconstrain(popup); +// +// return popup; +//} +// +//static void +//popup_handle_new_popup(struct wl_listener *listener, void *data) +//{ +// struct lab_layer_popup *lab_layer_popup = +// wl_container_of(listener, lab_layer_popup, new_popup); +// struct wlr_xdg_popup *wlr_popup = data; +// create_popup(wlr_popup, LAYER_PARENT_POPUP, lab_layer_popup); +//} +// +//static void +//new_popup_notify(struct wl_listener *listener, void *data) +//{ +// struct lab_layer_surface *lab_layer_surface = +// wl_container_of(listener, lab_layer_surface, new_popup); +// struct wlr_xdg_popup *wlr_popup = data; +// create_popup(wlr_popup, LAYER_PARENT_LAYER, lab_layer_surface); +//} static void new_layer_surface_notify(struct wl_listener *listener, void *data) @@ -564,18 +335,28 @@ new_layer_surface_notify(struct wl_listener *listener, void *data) surface->unmap.notify = unmap_notify; wl_signal_add(&layer_surface->events.unmap, &surface->unmap); - surface->new_popup.notify = new_popup_notify; - wl_signal_add(&layer_surface->events.new_popup, &surface->new_popup); - - surface->new_subsurface.notify = new_subsurface_notify; - wl_signal_add(&layer_surface->surface->events.new_subsurface, - &surface->new_subsurface); - - surface->layer_surface = layer_surface; - layer_surface->data = surface; - surface->server = server; + /* TODO: support popups */ +// surface->new_popup.notify = new_popup_notify; +// wl_signal_add(&layer_surface->events.new_popup, &surface->new_popup); +// +// surface->new_subsurface.notify = new_subsurface_notify; +// wl_signal_add(&layer_surface->surface->events.new_subsurface, +// &surface->new_subsurface); struct output *output = layer_surface->output->data; + + struct wlr_scene_tree *selected_layer = + output->layer_tree[layer_surface->current.layer]; + + surface->scene_layer_surface = wlr_scene_layer_surface_v1_create( + &selected_layer->node, layer_surface); + + surface->server = server; + surface->scene_layer_surface->layer_surface = layer_surface; + + /* wlr_surface->data needed to find parent in xdg_surface_new() */ + layer_surface->surface->data = surface->scene_layer_surface->node; + surface->output_destroy.notify = output_destroy_notify; wl_signal_add(&layer_surface->output->events.destroy, &surface->output_destroy); diff --git a/src/output.c b/src/output.c index 297e4260..1b29183d 100644 --- a/src/output.c +++ b/src/output.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include "buffer.h" @@ -110,14 +111,16 @@ new_output_notify(struct wl_listener *listener, void *data) output->frame.notify = output_frame_notify; wl_signal_add(&wlr_output->events.frame, &output->frame); - wl_list_init(&output->layers[0]); - wl_list_init(&output->layers[1]); - wl_list_init(&output->layers[2]); - wl_list_init(&output->layers[3]); - /* - * Arrange outputs from left-to-right in the order they appear. - * TODO: support configuration in run-time - */ + for (int i = 0; i < 4; i++) { + wl_list_init(&output->layers[i]); + output->layer_tree[i] = + wlr_scene_tree_create(&server->scene->node); + output->layer_tree[i]->node.data = output->wlr_output; + } + wlr_scene_node_lower_to_bottom(&output->layer_tree[1]->node); + wlr_scene_node_lower_to_bottom(&output->layer_tree[0]->node); + wlr_scene_node_raise_to_top(&output->layer_tree[2]->node); + wlr_scene_node_raise_to_top(&output->layer_tree[3]->node); if (rc.adaptive_sync) { wlr_log(WLR_INFO, "enable adaptive sync on %s", @@ -318,11 +321,13 @@ handle_output_layout_change(struct wl_listener *listener, void *data) "wlr_output_manager_v1_set_configuration()"); } struct output *output; - wl_list_for_each(output, &server->outputs, link) { - if (output) { - arrange_layers(output); - } - } + + /* FIXME: Sort this out */ +// wl_list_for_each(output, &server->outputs, link) { +// if (output) { +// arrange_layers(output); +// } +// } output_update_for_layout_change(server); } } From 7ea733761b5defc3927242eda75fbdfde6c9ee11 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Fri, 18 Feb 2022 00:07:37 +0100 Subject: [PATCH 13/56] Use view->scene_tree as toplevel for view + some legwork: desktop.c move_to_front() calls wlr_scene_node_raise_to_top desktop.c, ssd/include.h add LAB_SSD_{OSD, MENU, LAYER_SURFACE} enums desktop.c desktop_surface_and_view_at() -> desktop_node_and_view_at() cursor.c reenable cursor_rebase() cursor.c reenable button click on layer surface --- include/labwc.h | 11 +++--- include/ssd.h | 3 ++ src/cursor.c | 96 ++++++++++++++++++++++++++++--------------------- src/desktop.c | 44 ++++++++++++++++++----- src/view.c | 8 ++--- src/xdg.c | 14 ++++++-- src/xwayland.c | 14 ++++++-- 7 files changed, 126 insertions(+), 64 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index d0555290..d5ff2d95 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -234,6 +234,7 @@ struct view { #endif }; struct wlr_surface *surface; + struct wlr_scene_tree *scene_tree; struct wlr_scene_node *scene_node; bool mapped; @@ -419,11 +420,13 @@ void desktop_focus_topmost_mapped_view(struct server *server); bool isfocusable(struct view *view); /** - * desktop_surface_and_view_at - find view and surface at (lx, ly) - * Note: If surface points to layer-surface, view will be set to NULL + * desktop_node_and_view_at - find view and scene_node at (lx, ly) + * Note: If node points to layer-surface, view_area will be set + * to LAB_SSD_LAYER_SURFACE, if view points to another surface + * view_area will be LAB_SSD_CLIENT */ -struct view *desktop_surface_and_view_at(struct server *server, double lx, - double ly, struct wlr_surface **surface, double *sx, double *sy, +struct view *desktop_node_and_view_at(struct server *server, double lx, + double ly, struct wlr_scene_node **scene_node, double *sx, double *sy, enum ssd_part_type *view_area); struct view *desktop_view_at_cursor(struct server *server); diff --git a/include/ssd.h b/include/ssd.h index aeeedf0d..d711c060 100644 --- a/include/ssd.h +++ b/include/ssd.h @@ -26,6 +26,9 @@ enum ssd_part_type { LAB_SSD_CLIENT, LAB_SSD_FRAME, LAB_SSD_ROOT, + LAB_SSD_MENU, + LAB_SSD_OSD, + LAB_SSD_LAYER_SURFACE, LAB_SSD_END_MARKER }; diff --git a/src/cursor.c b/src/cursor.c index 37668dcf..633b0c0a 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -12,25 +12,29 @@ #include "ssd.h" #include "config/mousebind.h" -// void -// cursor_rebase(struct seat *seat, uint32_t time_msec) -// { -// double sx, sy; -// struct wlr_surface *surface; -// enum ssd_part_type view_area = LAB_SSD_NONE; -// -// desktop_surface_and_view_at(seat->server, seat->cursor->x, -// seat->cursor->y, &surface, &sx, &sy, &view_area); -// -// if (surface) { -// wlr_seat_pointer_notify_clear_focus(seat->seat); -// wlr_seat_pointer_notify_enter(seat->seat, surface, sx, sy); -// wlr_seat_pointer_notify_motion(seat->seat, time_msec, sx, sy); -// } else { -// cursor_set(seat, "left_ptr"); -// wlr_seat_pointer_notify_clear_focus(seat->seat); -// } -// } +void +cursor_rebase(struct seat *seat, uint32_t time_msec) +{ + double sx, sy; + struct wlr_scene_node *node; + enum ssd_part_type view_area = LAB_SSD_NONE; + struct wlr_surface *surface = NULL; + + desktop_node_and_view_at(seat->server, seat->cursor->x, + seat->cursor->y, &node, &sx, &sy, &view_area); + if (view_area == LAB_SSD_CLIENT || view_area == LAB_SSD_LAYER_SURFACE) { + surface = wlr_scene_surface_from_node(node)->surface; + } + + if (surface) { + wlr_seat_pointer_notify_clear_focus(seat->seat); + wlr_seat_pointer_notify_enter(seat->seat, surface, sx, sy); + wlr_seat_pointer_notify_motion(seat->seat, time_msec, sx, sy); + } else { + cursor_set(seat, "left_ptr"); + wlr_seat_pointer_notify_clear_focus(seat->seat); + } +} static void request_cursor_notify(struct wl_listener *listener, void *data) @@ -201,12 +205,17 @@ process_cursor_motion(struct server *server, uint32_t time) /* Otherwise, find view under the pointer and send the event along */ double sx, sy; struct wlr_seat *wlr_seat = server->seat.seat; - struct wlr_surface *surface = NULL; + struct wlr_scene_node *node; enum ssd_part_type view_area = LAB_SSD_NONE; - struct view *view = desktop_surface_and_view_at(server, - server->seat.cursor->x, server->seat.cursor->y, &surface, + struct view *view = desktop_node_and_view_at(server, + server->seat.cursor->x, server->seat.cursor->y, &node, &sx, &sy, &view_area); + struct wlr_surface *surface = NULL; + if (view_area == LAB_SSD_CLIENT || view_area == LAB_SSD_LAYER_SURFACE) { + surface = wlr_scene_surface_from_node(node)->surface; + } + /* resize handles */ uint32_t resize_edges = ssd_resize_edges(view_area); @@ -593,17 +602,22 @@ cursor_button(struct wl_listener *listener, void *data) wlr_idle_notify_activity(seat->wlr_idle, seat->seat); double sx, sy; - struct wlr_surface *surface; + struct wlr_scene_node *node; enum ssd_part_type view_area = LAB_SSD_NONE; uint32_t resize_edges; /* bindings to the Frame context swallow mouse events if activated */ bool triggered_frame_binding = false; - struct view *view = desktop_surface_and_view_at(server, - server->seat.cursor->x, server->seat.cursor->y, &surface, + struct view *view = desktop_node_and_view_at(server, + server->seat.cursor->x, server->seat.cursor->y, &node, &sx, &sy, &view_area); + struct wlr_surface *surface = NULL; + if (view_area == LAB_SSD_CLIENT || view_area == LAB_SSD_LAYER_SURFACE) { + surface = wlr_scene_surface_from_node(node)->surface; + } + /* get modifiers */ struct wlr_input_device *device = seat->keyboard_group->input_device; uint32_t modifiers = wlr_keyboard_get_modifiers(device->keyboard); @@ -622,7 +636,7 @@ cursor_button(struct wl_listener *listener, void *data) server->input_mode = LAB_INPUT_STATE_PASSTHROUGH; server->grabbed_view = NULL; } -// cursor_rebase(&server->seat, event->time_msec); + cursor_rebase(&server->seat, event->time_msec); } /* Handle _release_ on root window */ @@ -640,24 +654,24 @@ cursor_button(struct wl_listener *listener, void *data) menu_action_selected(server, server->windowmenu); } server->input_mode = LAB_INPUT_STATE_PASSTHROUGH; -// cursor_rebase(&server->seat, event->time_msec); + cursor_rebase(&server->seat, event->time_msec); return; } /* Handle _press_ on a layer surface */ -// if (!view && surface) { -// if (!wlr_surface_is_layer_surface(surface)) { -// return; -// } -// struct wlr_layer_surface_v1 *layer = -// wlr_layer_surface_v1_from_wlr_surface(surface); -// if (layer->current.keyboard_interactive) { -// seat_set_focus_layer(&server->seat, layer); -// } -// wlr_seat_pointer_notify_button(seat->seat, event->time_msec, -// event->button, event->state); -// return; -// } + if (view_area == LAB_SSD_LAYER_SURFACE && surface) { + if (!wlr_surface_is_layer_surface(surface)) { + return; + } + struct wlr_layer_surface_v1 *layer = + wlr_layer_surface_v1_from_wlr_surface(surface); + if (layer->current.keyboard_interactive) { + seat_set_focus_layer(&server->seat, layer); + } + wlr_seat_pointer_notify_button(seat->seat, event->time_msec, + event->button, event->state); + return; + } /* Handle _press_ on root window */ if (!view) { @@ -704,7 +718,7 @@ cursor_axis(struct wl_listener *listener, void *data) wlr_idle_notify_activity(seat->wlr_idle, seat->seat); /* Notify the client with pointer focus of the axis event. */ -// cursor_rebase(seat, event->time_msec); + cursor_rebase(seat, event->time_msec); wlr_seat_pointer_notify_axis(seat->seat, event->time_msec, event->orientation, event->delta, event->delta_discrete, event->source); diff --git a/src/desktop.c b/src/desktop.c index 19881f18..7b6afa0a 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -10,6 +10,7 @@ move_to_front(struct view *view) { wl_list_remove(&view->link); wl_list_insert(&view->server->views, &view->link); + wlr_scene_node_raise_to_top(&view->scene_tree->node); } #if HAVE_XWAYLAND @@ -256,32 +257,57 @@ desktop_focus_topmost_mapped_view(struct server *server) } struct view * -desktop_surface_and_view_at(struct server *server, double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy, +desktop_node_and_view_at(struct server *server, double lx, double ly, + struct wlr_scene_node **scene_node, double *sx, double *sy, enum ssd_part_type *view_area) { struct wlr_scene_node *node = wlr_scene_node_at(&server->scene->node, lx, ly, sx, sy); - if (!node || node->type != WLR_SCENE_NODE_SURFACE) { + *scene_node = node; + if (!node) { + *view_area = LAB_SSD_NONE; return NULL; } - *surface = wlr_scene_surface_from_node(node)->surface; + if (node->type == WLR_SCENE_NODE_SURFACE) { + struct wlr_surface *surface = + wlr_scene_surface_from_node(node)->surface; + if (wlr_surface_is_layer_surface(surface)) { + *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; while (node && !node->data) { + if (node == osd) { + *view_area = LAB_SSD_OSD; + return NULL; + } + /* TODO: node == &server->menu_tree->node */ node = node->parent; } - assert(node); - return node->data; + if (!node) { + wlr_log(WLR_ERROR, "Unknown node detected"); + *view_area = LAB_SSD_NONE; + return NULL; + } + struct view *view = node->data; + /* TODO: *view_area = ssd_get_type(view, node) */ + return view; } struct view * desktop_view_at_cursor(struct server *server) { double sx, sy; - struct wlr_surface *surface; + struct wlr_scene_node *node; enum ssd_part_type view_area = LAB_SSD_NONE; - return desktop_surface_and_view_at(server, + return desktop_node_and_view_at(server, server->seat.cursor->x, server->seat.cursor->y, - &surface, &sx, &sy, &view_area); + &node, &sx, &sy, &view_area); } diff --git a/src/view.c b/src/view.c index 9379cdf7..34c04fca 100644 --- a/src/view.c +++ b/src/view.c @@ -31,7 +31,7 @@ view_move(struct view *view, double x, double y) view->impl->move(view, x, y); } view_discover_output(view); - wlr_scene_node_set_position(view->scene_node, view->x, view->y); + wlr_scene_node_set_position(&view->scene_tree->node, view->x, view->y); } void @@ -45,7 +45,7 @@ view_move_resize(struct view *view, struct wlr_box geo) } ssd_update_title(view); view_discover_output(view); - wlr_scene_node_set_position(view->scene_node, view->x, view->y); + wlr_scene_node_set_position(&view->scene_tree->node, view->x, view->y); } #define MIN_VIEW_WIDTH (100) @@ -147,7 +147,7 @@ view_center(struct view *view) if (view_compute_centered_position(view, view->w, view->h, &x, &y)) { view_move(view, x, y); } - wlr_scene_node_set_position(view->scene_node, view->x, view->y); + wlr_scene_node_set_position(&view->scene_tree->node, view->x, view->y); } static void @@ -223,7 +223,7 @@ view_maximize(struct view *view, bool maximize) if (view->fullscreen) { return; } - wlr_scene_node_set_position(view->scene_node, view->x, view->y); + wlr_scene_node_set_position(&view->scene_tree->node, view->x, view->y); if (view->impl->maximize) { view->impl->maximize(view, maximize); } diff --git a/src/xdg.c b/src/xdg.c index c3a8b0af..fc78c6b5 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -86,7 +86,11 @@ handle_destroy(struct wl_listener *listener, void *data) interactive_end(view); wl_list_remove(&view->link); wl_list_remove(&view->destroy.link); - ssd_destroy(view); + if (view->scene_tree) { + ssd_destroy(view); + wlr_scene_node_destroy(&view->scene_tree->node); + view->scene_tree = NULL; + } free(view); } @@ -388,13 +392,17 @@ xdg_surface_new(struct wl_listener *listener, void *data) 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->server->view_tree->node, view->xdg_surface); + &view->scene_tree->node, view->xdg_surface); if (!view->scene_node) { + wlr_scene_node_destroy(&view->scene_tree->node); + view->scene_tree = NULL; wl_resource_post_no_memory(view->surface->resource); + /* TODO: should we free(view) here? */ return; } - view->scene_node->data = view; + view->scene_tree->node.data = view; /* In support of xdg_toplevel_decoration */ xdg_surface->data = view; diff --git a/src/xwayland.c b/src/xwayland.c index 29475148..c72fe471 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -90,7 +90,11 @@ handle_destroy(struct wl_listener *listener, void *data) wl_list_remove(&view->request_configure.link); wl_list_remove(&view->request_maximize.link); wl_list_remove(&view->request_fullscreen.link); - ssd_destroy(view); + if (view->scene_tree) { + ssd_destroy(view); + wlr_scene_node_destroy(&view->scene_tree->node); + view->scene_tree = NULL; + } free(view); } @@ -256,13 +260,17 @@ map(struct view *view) } view->surface = view->xwayland_surface->surface; + view->scene_tree = wlr_scene_tree_create(&view->server->view_tree->node); view->scene_node = wlr_scene_subsurface_tree_create( - &view->server->view_tree->node, view->surface); + &view->scene_tree->node, view->surface); if (!view->scene_node) { + wlr_scene_node_destroy(&view->scene_tree->node); + view->scene_tree = NULL; wl_resource_post_no_memory(view->surface->resource); + /* TODO: should we free(view) here? */ return; } - view->scene_node->data = view; + view->scene_tree->node.data = view; view->ssd.enabled = want_deco(view); From 044388a5cd1ccaef07b2b36e759325c3dc2aa099 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Thu, 17 Feb 2022 01:46:32 +0100 Subject: [PATCH 14/56] Prepare to move from wlr_texture to lab_data_buffer --- include/buffer.h | 10 ++++-- include/common/font.h | 21 ++++++----- include/theme.h | 28 +++++++-------- include/xbm/xbm.h | 2 +- src/buffer.c | 27 +++++++++++++- src/common/font.c | 34 +++++++++--------- src/main.c | 2 +- src/menu/menu.c | 6 ++++ src/osd.c | 2 +- src/server.c | 2 +- src/ssd.c | 13 +++++++ src/theme.c | 41 ++++++++++------------ src/xbm/xbm.c | 82 +++++++++++++++---------------------------- 13 files changed, 148 insertions(+), 122 deletions(-) diff --git a/include/buffer.h b/include/buffer.h index 63f92f87..3ffec484 100644 --- a/include/buffer.h +++ b/include/buffer.h @@ -36,9 +36,15 @@ struct lab_data_buffer { void *data; uint32_t format; size_t stride; + bool free_on_destroy; }; -struct lab_data_buffer *buffer_create(uint32_t width, uint32_t height, - float scale); +/* Create a buffer which creates a new cairo CAIRO_FORMAT_ARGB32 surface */ +struct lab_data_buffer *buffer_create_cairo(uint32_t width, uint32_t height, + float scale, bool free_on_destroy); + +/* Create a buffer which wraps a given DRM_FORMAT_ARGB8888 pointer */ +struct lab_data_buffer *buffer_create_wrap(void *pixel_data, uint32_t width, + uint32_t height, uint32_t stride, bool free_on_destroy); #endif /* __LABWC_BUFFER_H */ diff --git a/include/common/font.h b/include/common/font.h index c7af6848..bcf5acd2 100644 --- a/include/common/font.h +++ b/include/common/font.h @@ -2,9 +2,7 @@ #ifndef __LABWC_FONT_H #define __LABWC_FONT_H -struct server; -struct wlr_texture; -struct wlr_box; +struct lab_data_buffer; struct font { char *name; @@ -18,16 +16,23 @@ struct font { int font_height(struct font *font); /** - * texture_create - Create ARGB8888 texture using pango - * @server: context (for wlr_renderer) - * @texture: texture pointer; existing pointer will be freed + * font_buffer_create - Create ARGB8888 lab_data_buffer using pango + * @buffer: buffer pointer * @max_width: max allowable width; will be ellipsized if longer * @text: text to be generated as texture * @font: font description * @color: foreground color in rgba format */ -void font_texture_create(struct server *server, struct wlr_texture **texture, - int max_width, const char *text, struct font *font, float *color); +void font_buffer_create(struct lab_data_buffer **buffer, int max_width, + const char *text, struct font *font, float *color); + +/** + * font_buffer_update - Wrapper around font_buffer_create + * Only difference is that if given buffer pointer is != NULL + * wlr_buffer_drop() will be called on the buffer. + */ +void font_buffer_update(struct lab_data_buffer **buffer, int max_width, + const char *text, struct font *font, float *color); /** * font_finish - free some font related resources diff --git a/include/theme.h b/include/theme.h index 38aae4c0..a36b9dc1 100644 --- a/include/theme.h +++ b/include/theme.h @@ -54,20 +54,20 @@ struct theme { float osd_label_text_color[4]; /* textures */ - struct wlr_texture *xbm_close_active_unpressed; - struct wlr_texture *xbm_maximize_active_unpressed; - struct wlr_texture *xbm_iconify_active_unpressed; - struct wlr_texture *xbm_menu_active_unpressed; + struct lab_data_buffer *xbm_close_active_unpressed; + struct lab_data_buffer *xbm_maximize_active_unpressed; + struct lab_data_buffer *xbm_iconify_active_unpressed; + struct lab_data_buffer *xbm_menu_active_unpressed; - struct wlr_texture *xbm_close_inactive_unpressed; - struct wlr_texture *xbm_maximize_inactive_unpressed; - struct wlr_texture *xbm_iconify_inactive_unpressed; - struct wlr_texture *xbm_menu_inactive_unpressed; + struct lab_data_buffer *xbm_close_inactive_unpressed; + struct lab_data_buffer *xbm_maximize_inactive_unpressed; + struct lab_data_buffer *xbm_iconify_inactive_unpressed; + struct lab_data_buffer *xbm_menu_inactive_unpressed; - struct wlr_texture *corner_top_left_active_normal; - struct wlr_texture *corner_top_right_active_normal; - struct wlr_texture *corner_top_left_inactive_normal; - struct wlr_texture *corner_top_right_inactive_normal; + struct lab_data_buffer *corner_top_left_active_normal; + struct lab_data_buffer *corner_top_right_active_normal; + struct lab_data_buffer *corner_top_left_inactive_normal; + struct lab_data_buffer *corner_top_right_inactive_normal; /* not set in rc.xml/themerc, but derived from font & padding_height */ int title_height; @@ -76,12 +76,10 @@ struct theme { /** * theme_init - read openbox theme and generate button textures * @theme: theme data - * @renderer: wlr_renderer for creating button textures * @theme_name: theme-name in //openbox-3/themerc * Note is obtained in theme-dir.c */ -void theme_init(struct theme *theme, struct wlr_renderer *renderer, - const char *theme_name); +void theme_init(struct theme *theme, const char *theme_name); /** * theme_finish - free button textures diff --git a/include/xbm/xbm.h b/include/xbm/xbm.h index 739d9a2c..f0957d3d 100644 --- a/include/xbm/xbm.h +++ b/include/xbm/xbm.h @@ -9,6 +9,6 @@ /** * xbm_load - load theme xbm files into global theme struct */ -void xbm_load(struct theme *theme, struct wlr_renderer *renderer); +void xbm_load(struct theme *theme); #endif /* __LABWC_XBM_H */ diff --git a/src/buffer.c b/src/buffer.c index 8ce275a5..3a70aa29 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -42,10 +42,17 @@ static void data_buffer_destroy(struct wlr_buffer *wlr_buffer) { struct lab_data_buffer *buffer = data_buffer_from_buffer(wlr_buffer); + if (!buffer->free_on_destroy) { + free(buffer); + return; + } if (buffer->cairo) { cairo_surface_t *surf = cairo_get_target(buffer->cairo); cairo_destroy(buffer->cairo); cairo_surface_destroy(surf); + } else if (buffer->data) { + free(buffer->data); + buffer->data = NULL; } free(buffer); } @@ -76,7 +83,8 @@ static const struct wlr_buffer_impl data_buffer_impl = { }; struct lab_data_buffer * -buffer_create(uint32_t width, uint32_t height, float scale) +buffer_create_cairo(uint32_t width, uint32_t height, float scale, + bool free_on_destroy) { struct lab_data_buffer *buffer = calloc(1, sizeof(*buffer)); if (!buffer) { @@ -92,6 +100,7 @@ buffer_create(uint32_t width, uint32_t height, float scale) buffer->data = cairo_image_surface_get_data(surf); buffer->format = DRM_FORMAT_ARGB8888; buffer->stride = cairo_image_surface_get_stride(surf); + buffer->free_on_destroy = free_on_destroy; if (!buffer->data) { cairo_destroy(buffer->cairo); @@ -101,3 +110,19 @@ buffer_create(uint32_t width, uint32_t height, float scale) } return buffer; } + +struct lab_data_buffer * +buffer_create_wrap(void *pixel_data, uint32_t width, uint32_t height, + uint32_t stride, bool free_on_destroy) +{ + struct lab_data_buffer *buffer = calloc(1, sizeof(*buffer)); + if (!buffer) { + return NULL; + } + wlr_buffer_init(&buffer->base, &data_buffer_impl, width, height); + buffer->data = pixel_data; + buffer->format = DRM_FORMAT_ARGB8888; + buffer->stride = stride; + buffer->free_on_destroy = free_on_destroy; + return buffer; +} diff --git a/src/common/font.c b/src/common/font.c index e5222d00..6925a504 100644 --- a/src/common/font.c +++ b/src/common/font.c @@ -7,6 +7,7 @@ #include #include "common/font.h" #include "labwc.h" +#include "buffer.h" static PangoRectangle font_extents(struct font *font, const char *string) @@ -49,25 +50,33 @@ font_height(struct font *font) } void -font_texture_create(struct server *server, struct wlr_texture **texture, - int max_width, const char *text, struct font *font, float *color) +font_buffer_update(struct lab_data_buffer **buffer, int max_width, + const char *text, struct font *font, float *color) +{ + if (*buffer) { + wlr_buffer_drop(&(*buffer)->base); + *buffer = NULL; + } + font_buffer_create(buffer, max_width, text, font, color); +} + +void +font_buffer_create(struct lab_data_buffer **buffer, int max_width, + const char *text, struct font *font, float *color) { if (!text || !*text) { return; } - if (*texture) { - wlr_texture_destroy(*texture); - *texture = NULL; - } PangoRectangle rect = font_extents(font, text); if (max_width && rect.width > max_width) { rect.width = max_width; } + /* TODO: scale */ + *buffer = buffer_create_cairo(rect.width, rect.height, 1, true); - cairo_surface_t *surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, - rect.width, rect.height); - cairo_t *cairo = cairo_create(surf); + cairo_t *cairo = (*buffer)->cairo; + cairo_surface_t *surf = cairo_get_target(cairo); cairo_set_source_rgba(cairo, color[0], color[1], color[2], color[3]); cairo_move_to(cairo, 0, 0); @@ -88,13 +97,6 @@ font_texture_create(struct server *server, struct wlr_texture **texture, g_object_unref(layout); cairo_surface_flush(surf); - unsigned char *data = cairo_image_surface_get_data(surf); - *texture = wlr_texture_from_pixels(server->renderer, DRM_FORMAT_ARGB8888, - cairo_image_surface_get_stride(surf), rect.width, - rect.height, data); - - cairo_destroy(cairo); - cairo_surface_destroy(surf); } void diff --git a/src/main.c b/src/main.c index 4e419e5e..e3662f26 100644 --- a/src/main.c +++ b/src/main.c @@ -68,7 +68,7 @@ main(int argc, char *argv[]) server_start(&server); struct theme theme = { 0 }; - theme_init(&theme, server.renderer, rc.theme_name); + theme_init(&theme, rc.theme_name); server.theme = &theme; menu_init_rootmenu(&server); diff --git a/src/menu/menu.c b/src/menu/menu.c index 48b18336..5dc768c1 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -74,8 +74,11 @@ item_create(struct menu *menu, const char *text) if (!menuitem) { return NULL; } +/* FIXME */ +#if 0 struct server *server = menu->server; struct theme *theme = server->theme; +#endif struct font font = { .name = rc.font_name_menuitem, .size = rc.font_size_menuitem, @@ -84,6 +87,8 @@ item_create(struct menu *menu, const char *text) menuitem->box.width = MENUWIDTH; menuitem->box.height = font_height(&font) + 2 * MENU_ITEM_PADDING_Y; +/* FIXME */ +#if 0 int item_max_width = MENUWIDTH - 2 * MENU_ITEM_PADDING_X; font_texture_create(server, &menuitem->texture.active, item_max_width, text, &font, theme->menu_items_active_text_color); @@ -94,6 +99,7 @@ item_create(struct menu *menu, const char *text) menuitem->texture.offset_y = (menuitem->box.height - menuitem->texture.active->height) / 2; menuitem->texture.offset_x = MENU_ITEM_PADDING_X; +#endif wl_list_insert(&menu->menuitems, &menuitem->link); wl_list_init(&menuitem->actions); diff --git a/src/osd.c b/src/osd.c index 85b5e4fe..3c66d477 100644 --- a/src/osd.c +++ b/src/osd.c @@ -106,7 +106,7 @@ osd_update(struct server *server) if (output->osd_buffer) { wlr_buffer_drop(&output->osd_buffer->base); } - output->osd_buffer = buffer_create(w, h, scale); + output->osd_buffer = buffer_create_cairo(w, h, scale, true); cairo_t *cairo = output->osd_buffer->cairo; cairo_surface_t *surf = cairo_get_target(cairo); diff --git a/src/server.c b/src/server.c index 171ec6dd..fbefa0e2 100644 --- a/src/server.c +++ b/src/server.c @@ -32,7 +32,7 @@ reload_config_and_theme(void) rcxml_finish(); rcxml_read(NULL); theme_finish(g_server->theme); - theme_init(g_server->theme, g_server->renderer, rc.theme_name); + theme_init(g_server->theme, rc.theme_name); struct view *view; wl_list_for_each (view, &g_server->views, link) { diff --git a/src/ssd.c b/src/ssd.c index 0897fdaf..3edfd3b3 100644 --- a/src/ssd.c +++ b/src/ssd.c @@ -322,12 +322,16 @@ add_part(struct view *view, enum ssd_part_type type) 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) { @@ -340,6 +344,8 @@ ssd_update_title(struct view *view) 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, @@ -349,6 +355,7 @@ ssd_update_title(struct view *view) 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); } @@ -387,14 +394,20 @@ ssd_create(struct view *view) /* 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); diff --git a/src/theme.c b/src/theme.c index ec07f9b0..948a6259 100644 --- a/src/theme.c +++ b/src/theme.c @@ -25,6 +25,7 @@ #include "config/rcxml.h" #include "theme.h" #include "xbm/xbm.h" +#include "buffer.h" static int hex_to_dec(char c) @@ -336,8 +337,8 @@ set_source(cairo_t *cairo, float *c) cairo_set_source_rgba(cairo, c[0], c[1], c[2], c[3]); } -static struct wlr_texture * -rounded_rect(struct wlr_renderer *renderer, struct rounded_corner_ctx *ctx) +static struct lab_data_buffer * +rounded_rect(struct rounded_corner_ctx *ctx) { /* 1 degree in radians (=2Ï€/360) */ double deg = 0.017453292519943295; @@ -350,9 +351,12 @@ rounded_rect(struct wlr_renderer *renderer, struct rounded_corner_ctx *ctx) double h = ctx->box->height; double r = ctx->radius; - cairo_surface_t *surf = - cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); - cairo_t *cairo = cairo_create(surf); + struct lab_data_buffer *buffer; + /* TODO: scale */ + buffer = buffer_create_cairo(w, h, 1, true); + + cairo_t *cairo = buffer->cairo; + cairo_surface_t *surf = cairo_get_target(cairo); /* set transparent background */ cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); @@ -405,21 +409,13 @@ rounded_rect(struct wlr_renderer *renderer, struct rounded_corner_ctx *ctx) wlr_log(WLR_ERROR, "unknown corner type"); } cairo_stroke(cairo); - - /* convert to wlr_texture */ cairo_surface_flush(surf); - unsigned char *data = cairo_image_surface_get_data(surf); - struct wlr_texture *texture = wlr_texture_from_pixels(renderer, - DRM_FORMAT_ARGB8888, cairo_image_surface_get_stride(surf), - w, h, data); - cairo_destroy(cairo); - cairo_surface_destroy(surf); - return texture; + return buffer; } static void -create_corners(struct theme *theme, struct wlr_renderer *renderer) +create_corners(struct theme *theme) { int corner_square = theme->title_height + theme->border_width; struct wlr_box box = { @@ -437,20 +433,20 @@ create_corners(struct theme *theme, struct wlr_renderer *renderer) .border_color = theme->window_active_border_color, .corner = LAB_CORNER_TOP_LEFT, }; - theme->corner_top_left_active_normal = rounded_rect(renderer, &ctx); + theme->corner_top_left_active_normal = rounded_rect(&ctx); ctx.fill_color = theme->window_inactive_title_bg_color, ctx.border_color = theme->window_inactive_border_color, - theme->corner_top_left_inactive_normal = rounded_rect(renderer, &ctx); + theme->corner_top_left_inactive_normal = rounded_rect(&ctx); ctx.corner = LAB_CORNER_TOP_RIGHT; ctx.fill_color = theme->window_active_title_bg_color, ctx.border_color = theme->window_active_border_color, - theme->corner_top_right_active_normal = rounded_rect(renderer, &ctx); + theme->corner_top_right_active_normal = rounded_rect(&ctx); ctx.fill_color = theme->window_inactive_title_bg_color, ctx.border_color = theme->window_inactive_border_color, - theme->corner_top_right_inactive_normal = rounded_rect(renderer, &ctx); + theme->corner_top_right_inactive_normal = rounded_rect(&ctx); } static void @@ -480,8 +476,7 @@ post_processing(struct theme *theme) } void -theme_init(struct theme *theme, struct wlr_renderer *renderer, - const char *theme_name) +theme_init(struct theme *theme, const char *theme_name) { /* * Set some default values. This is particularly important on @@ -491,8 +486,8 @@ theme_init(struct theme *theme, struct wlr_renderer *renderer, theme_read(theme, theme_name); post_processing(theme); - create_corners(theme, renderer); - xbm_load(theme, renderer); + create_corners(theme); + xbm_load(theme); } void diff --git a/src/xbm/xbm.c b/src/xbm/xbm.c index 35a97cfa..a251ed44 100644 --- a/src/xbm/xbm.c +++ b/src/xbm/xbm.c @@ -15,6 +15,7 @@ #include "theme.h" #include "xbm/parse.h" #include "xbm/xbm.h" +#include "buffer.h" /* built-in 6x6 buttons */ char menu_button_normal[] = { 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00 }; @@ -23,28 +24,6 @@ char max_button_normal[] = { 0x3f, 0x3f, 0x21, 0x21, 0x21, 0x3f }; char max_button_toggled[] = { 0x3e, 0x22, 0x2f, 0x29, 0x39, 0x0f }; char close_button_normal[] = { 0x33, 0x3f, 0x1e, 0x1e, 0x3f, 0x33 }; -static struct wlr_texture * -texture_from_pixmap(struct wlr_renderer *renderer, struct pixmap *pixmap) -{ - if (!pixmap) { - return NULL; - } - return wlr_texture_from_pixels(renderer, DRM_FORMAT_ARGB8888, - pixmap->width * 4, pixmap->width, - pixmap->height, pixmap->data); -} - -static struct wlr_texture * -texture_from_builtin(struct wlr_renderer *renderer, const char *button) -{ - struct pixmap pixmap = parse_xbm_builtin(button, 6); - struct wlr_texture *texture = texture_from_pixmap(renderer, &pixmap); - if (pixmap.data) { - free(pixmap.data); - } - return texture; -} - static char * xbm_path(const char *button) { @@ -55,62 +34,59 @@ xbm_path(const char *button) } static void -load_button(struct wlr_renderer *renderer, const char *filename, - struct wlr_texture **texture, char *button) +load_button(const char *filename, struct lab_data_buffer **buffer, char *button) { - if (*texture) { - wlr_texture_destroy(*texture); - *texture = NULL; + struct pixmap pixmap = {0}; + if (*buffer) { + wlr_buffer_drop(&(*buffer)->base); + *buffer = NULL; } /* Read file into memory as it's easier to tokenzie that way */ - char *buffer = grab_file(xbm_path(filename)); - if (!buffer) { - goto out; + char *token_buffer = grab_file(xbm_path(filename)); + if (token_buffer) { + struct token *tokens = tokenize_xbm(token_buffer); + free(token_buffer); + pixmap = parse_xbm_tokens(tokens); + if (tokens) { + free(tokens); + } + } + if (!pixmap.data) { + pixmap = parse_xbm_builtin(button, 6); } - struct token *tokens = tokenize_xbm(buffer); - free(buffer); - struct pixmap pixmap = parse_xbm_tokens(tokens); - *texture = texture_from_pixmap(renderer, &pixmap); - if (tokens) { - free(tokens); - } - if (pixmap.data) { - free(pixmap.data); - } -out: - if (!(*texture)) { - *texture = texture_from_builtin(renderer, button); - } + /* Create buffer with free_on_destroy being true */ + *buffer = buffer_create_wrap(pixmap.data, pixmap.width, pixmap.height, + pixmap.width * 4, true); } void -xbm_load(struct theme *theme, struct wlr_renderer *r) +xbm_load(struct theme *theme) { parse_set_color(theme->window_active_button_menu_unpressed_image_color); - load_button(r, "menu.xbm", &theme->xbm_menu_active_unpressed, + load_button("menu.xbm", &theme->xbm_menu_active_unpressed, menu_button_normal); parse_set_color(theme->window_active_button_iconify_unpressed_image_color); - load_button(r, "iconify.xbm", &theme->xbm_iconify_active_unpressed, + load_button("iconify.xbm", &theme->xbm_iconify_active_unpressed, iconify_button_normal); parse_set_color(theme->window_active_button_max_unpressed_image_color); - load_button(r, "max.xbm", &theme->xbm_maximize_active_unpressed, + load_button("max.xbm", &theme->xbm_maximize_active_unpressed, max_button_normal); parse_set_color(theme->window_active_button_close_unpressed_image_color); - load_button(r, "close.xbm", &theme->xbm_close_active_unpressed, + load_button("close.xbm", &theme->xbm_close_active_unpressed, close_button_normal); parse_set_color(theme->window_inactive_button_menu_unpressed_image_color); - load_button(r, "menu.xbm", &theme->xbm_menu_inactive_unpressed, + load_button("menu.xbm", &theme->xbm_menu_inactive_unpressed, menu_button_normal); parse_set_color(theme->window_inactive_button_iconify_unpressed_image_color); - load_button(r, "iconify.xbm", &theme->xbm_iconify_inactive_unpressed, + load_button("iconify.xbm", &theme->xbm_iconify_inactive_unpressed, iconify_button_normal); parse_set_color(theme->window_inactive_button_max_unpressed_image_color); - load_button(r, "max.xbm", &theme->xbm_maximize_inactive_unpressed, + load_button("max.xbm", &theme->xbm_maximize_inactive_unpressed, max_button_normal); parse_set_color(theme->window_inactive_button_close_unpressed_image_color); - load_button(r, "close.xbm", &theme->xbm_close_inactive_unpressed, + load_button("close.xbm", &theme->xbm_close_inactive_unpressed, close_button_normal); } From 6488b933fabb71a900dec6d4e47005d54fd6c1f2 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sun, 20 Feb 2022 14:53:21 +0000 Subject: [PATCH 15/56] layers.c: set output->usable_area --- src/layers.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/layers.c b/src/layers.c index 59e365b2..94bf07ac 100644 --- a/src/layers.c +++ b/src/layers.c @@ -39,16 +39,7 @@ arrange_layers(struct output *output) } } - for (int i = 0; i < 4; i++) { - struct wlr_scene_node *node = &output->layer_tree[i]->node; - wlr_scene_node_set_position(node, - scene_output->x + usable_area.x, - scene_output->y + usable_area.y); - } - - - /* TODO: workout how we set this */ -// memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); + memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); /* Find topmost keyboard interactive layer, if such a layer exists */ uint32_t layers_above_shell[] = { @@ -77,6 +68,7 @@ arrange_layers(struct output *output) !seat->focused_layer->current.keyboard_interactive) { seat_set_focus_layer(seat, NULL); } + /* FIXME: should we call a desktop_arrange_all_views() here? */ } static void From f26b61137da9c5c9cea5008e8945891e591ca24f Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sun, 20 Feb 2022 15:00:20 +0000 Subject: [PATCH 16/56] layers.h: define LAB_NR_LAYERS (4) --- include/layers.h | 3 +++ src/layers.c | 2 +- src/output.c | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/include/layers.h b/include/layers.h index 0eaccbb0..da88aafe 100644 --- a/include/layers.h +++ b/include/layers.h @@ -6,6 +6,8 @@ struct server; +#define LAB_NR_LAYERS (4) + enum layer_parent { LAYER_PARENT_LAYER, LAYER_PARENT_POPUP, @@ -29,6 +31,7 @@ struct lab_layer_surface { struct server *server; }; +/* FIXME: do we still need lab_layer_popup and lab_layer_subsurface? */ struct lab_layer_popup { struct wlr_xdg_popup *wlr_popup; enum layer_parent parent_type; diff --git a/src/layers.c b/src/layers.c index 94bf07ac..901a72ec 100644 --- a/src/layers.c +++ b/src/layers.c @@ -29,7 +29,7 @@ arrange_layers(struct output *output) &full_area.width, &full_area.height); struct wlr_box usable_area = full_area; - for (int i = 0; i < 4; i++) { + for (int i = 0; i < LAB_NR_LAYERS; i++) { struct lab_layer_surface *lab_layer_surface; wl_list_for_each(lab_layer_surface, &output->layers[i], link) { struct wlr_scene_layer_surface_v1 *scene_layer_surface = diff --git a/src/output.c b/src/output.c index 1b29183d..07700c58 100644 --- a/src/output.c +++ b/src/output.c @@ -111,7 +111,7 @@ new_output_notify(struct wl_listener *listener, void *data) output->frame.notify = output_frame_notify; wl_signal_add(&wlr_output->events.frame, &output->frame); - for (int i = 0; i < 4; i++) { + for (int i = 0; i < LAB_NR_LAYERS; i++) { wl_list_init(&output->layers[i]); output->layer_tree[i] = wlr_scene_tree_create(&server->scene->node); From f2d40a8d69c776df280af289bf40549f0ff380e3 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Sat, 19 Feb 2022 02:05:38 +0100 Subject: [PATCH 17/56] Scene Menu --- include/labwc.h | 4 +- include/menu/menu.h | 81 +++++++--- src/action.c | 24 +-- src/cursor.c | 34 ++--- src/debug.c | 8 + src/desktop.c | 5 +- src/menu/menu.c | 354 ++++++++++++++++++++++++++++++-------------- src/server.c | 3 +- 8 files changed, 347 insertions(+), 166 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index d5ff2d95..2d9cb239 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -154,6 +154,7 @@ struct server { uint32_t resize_edges; struct wlr_scene_tree *osd_tree; + struct wlr_scene_tree *menu_tree; struct wl_list outputs; struct wl_listener new_output; @@ -175,8 +176,7 @@ struct server { struct theme *theme; - struct menu *rootmenu; - struct menu *windowmenu; + struct menu *menu_current; }; struct output { diff --git a/include/menu/menu.h b/include/menu/menu.h index 9cdc8c6f..a5ec7dd8 100644 --- a/include/menu/menu.h +++ b/include/menu/menu.h @@ -3,19 +3,29 @@ #define __LABWC_MENU_H #include -#include + +struct lab_data_buffer; +struct wlr_scene_node; + +enum menu_align { + LAB_MENU_OPEN_AUTO = 0, + LAB_MENU_OPEN_LEFT = 1 << 0, + LAB_MENU_OPEN_RIGHT = 1 << 1, + LAB_MENU_OPEN_TOP = 1 << 2, + LAB_MENU_OPEN_BOTTOM = 1 << 3, +}; + +struct menu_scene { + struct lab_data_buffer *buffer; + struct wlr_scene_node *text; + struct wlr_scene_node *background; +}; struct menuitem { struct wl_list actions; struct menu *submenu; - struct wlr_box box; - struct { - struct wlr_texture *active; - struct wlr_texture *inactive; - int offset_x; - int offset_y; - } texture; - bool selected; + struct menu_scene normal; + struct menu_scene selected; struct wl_list link; /* menu::menuitems */ }; @@ -23,27 +33,62 @@ struct menuitem { struct menu { char *id; char *label; - bool visible; + int item_height; struct menu *parent; - struct wlr_box box; + struct { + int width; + int height; + } size; struct wl_list menuitems; struct server *server; + struct { + struct menu *menu; + struct menuitem *item; + } selection; + struct wlr_scene_tree *scene_tree; }; void menu_init_rootmenu(struct server *server); void menu_init_windowmenu(struct server *server); void menu_finish(void); -/* menu_move - move to position (x, y) */ -void menu_move(struct menu *menu, int x, int y); +/** + * menu_get_by_id - get menu by id + * + * @id id string defined in menu.xml like "root-menu" + */ +struct menu *menu_get_by_id(const char *id); -/* menu_set_selected - select item at (x, y) */ -void menu_set_selected(struct menu *menu, int x, int y); +/** + * menu_open - open menu on position (x, y) + * + * This function will close server->menu_current, open the + * new menu and assign @menu to server->menu_current. + * + * Additionally, server->input_mode wil be set to LAB_INPUT_STATE_MENU. + */ +void menu_open(struct menu *menu, int x, int y); -/* menu_action_selected - select item at (x, y) */ -void menu_action_selected(struct server *server, struct menu *menu); +/** + * menu_process_cursor_motion + * + * - handles hover effects + * - may open/close submenus + */ +void menu_process_cursor_motion(struct menu *menu, struct wlr_scene_node *node); + +/** + * menu_call_actions - call actions associated with a menu entry + * + * If actions are found, server->menu_current will be closed and set to NULL + * Returns true if handled + */ +bool menu_call_actions(struct menu *menu, struct wlr_scene_node *node); + +/* menu_close - close menu */ +void menu_close(struct menu *menu); /* menu_reconfigure - reload theme and content */ -void menu_reconfigure(struct server *server, struct menu *menu); +void menu_reconfigure(struct server *server); #endif /* __LABWC_MENU_H */ diff --git a/src/action.c b/src/action.c index 23d3dcf3..65aee0dc 100644 --- a/src/action.c +++ b/src/action.c @@ -91,19 +91,15 @@ void action_list_free(struct wl_list *action_list) { static void show_menu(struct server *server, struct view *view, const char *menu_name) { - struct menu *menu = NULL; bool force_menu_top_left = false; - - if (!menu_name) { + struct menu *menu = menu_get_by_id(menu_name); + if (!menu) { return; } - - if (!strcasecmp(menu_name, "root-menu")) { - menu = server->rootmenu; - server->windowmenu->visible = false; - } else if (!strcasecmp(menu_name, "client-menu") && view) { - menu = server->windowmenu; - server->rootmenu->visible = false; + if (!strcasecmp(menu_name, "client-menu")) { + if (!view) { + return; + } enum ssd_part_type type = ssd_at(view, server->seat.cursor->x, server->seat.cursor->y); if (type == LAB_SSD_BUTTON_WINDOW_MENU) { @@ -113,13 +109,8 @@ show_menu(struct server *server, struct view *view, const char *menu_name) } else { force_menu_top_left = true; } - } else { - return; } - menu->visible = true; - server->input_mode = LAB_INPUT_STATE_MENU; - int x, y; if (force_menu_top_left) { x = view->x; @@ -128,8 +119,7 @@ show_menu(struct server *server, struct view *view, const char *menu_name) x = server->seat.cursor->x; y = server->seat.cursor->y; } - menu_move(menu, x, y); - damage_all_outputs(server); + menu_open(menu, x, y); } static struct view * diff --git a/src/cursor.c b/src/cursor.c index 633b0c0a..adb68d3f 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -41,7 +41,7 @@ request_cursor_notify(struct wl_listener *listener, void *data) { struct seat *seat = wl_container_of(listener, seat, request_cursor); /* - * This event is rasied by the seat when a client provides a cursor + * This event is raised by the seat when a client provides a cursor * image */ struct wlr_seat_pointer_request_set_cursor_event *event = data; @@ -187,19 +187,6 @@ process_cursor_motion(struct server *server, uint32_t time) } else if (server->input_mode == LAB_INPUT_STATE_RESIZE) { process_cursor_resize(server, time); return; - } else if (server->input_mode == LAB_INPUT_STATE_MENU) { - struct menu *menu = NULL; - if (server->rootmenu->visible) { - menu = server->rootmenu; - } else if (server->windowmenu->visible) { - menu = server->windowmenu; - } else { - return; - } - menu_set_selected(menu, - server->seat.cursor->x, server->seat.cursor->y); - damage_all_outputs(server); - return; } /* Otherwise, find view under the pointer and send the event along */ @@ -239,6 +226,10 @@ process_cursor_motion(struct server *server, uint32_t time) } } + if (view_area == LAB_SSD_MENU) { + menu_process_cursor_motion(server->menu_current, node); + return; + } if (view && rc.focus_follow_mouse) { desktop_focus_and_activate_view(&server->seat, view); @@ -648,11 +639,18 @@ cursor_button(struct wl_listener *listener, void *data) } if (server->input_mode == LAB_INPUT_STATE_MENU) { - if (server->rootmenu->visible) { - menu_action_selected(server, server->rootmenu); - } else if (server->windowmenu->visible) { - menu_action_selected(server, server->windowmenu); + if (!server->menu_current) { + wlr_log(WLR_ERROR, + "on mouse button input_mode STATE_MENU but no current menu"); + } else if (view_area != LAB_SSD_MENU) { + menu_close(server->menu_current); + server->menu_current = NULL; + } else if (!menu_call_actions(server->menu_current, node)) { + /* Action was not successfull, maybe this menu has a submenu */ + return; } + /* TODO: following causes stray release */ + /* Maybe add LAB_INPUT_STATE_IGNORE_MOUSE_RELEASE ? */ server->input_mode = LAB_INPUT_STATE_PASSTHROUGH; cursor_rebase(&server->seat, event->time_msec); return; diff --git a/src/debug.c b/src/debug.c index 46ff3723..8d36daf0 100644 --- a/src/debug.c +++ b/src/debug.c @@ -76,4 +76,12 @@ debug_dump_scene(struct server *server) dump_tree(node, 0, node->state.x, node->state.y); } } + + printf(":: osd_tree ::\n"); + node = &server->osd_tree->node; + dump_tree(node, 0, node->state.x, node->state.y); + + printf(":: menu_tree ::\n"); + node = &server->menu_tree->node; + dump_tree(node, 0, node->state.x, node->state.y); } diff --git a/src/desktop.c b/src/desktop.c index 7b6afa0a..d17544dd 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -282,12 +282,15 @@ desktop_node_and_view_at(struct server *server, double lx, double ly, *view_area = LAB_SSD_NONE; } struct wlr_scene_node *osd = &server->osd_tree->node; + struct wlr_scene_node *menu = &server->menu_tree->node; while (node && !node->data) { if (node == osd) { *view_area = LAB_SSD_OSD; return NULL; + } else if (node == menu) { + *view_area = LAB_SSD_MENU; + return NULL; } - /* TODO: node == &server->menu_tree->node */ node = node->parent; } if (!node) { diff --git a/src/menu/menu.c b/src/menu/menu.c index 5dc768c1..beb85c58 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -19,6 +19,7 @@ #include "menu/menu.h" #include "theme.h" #include "action.h" +#include "buffer.h" #define MENUWIDTH (110) #define MENU_ITEM_PADDING_Y (4) @@ -51,12 +52,19 @@ menu_create(struct server *server, const char *id, const char *label) menu->label = strdup(label); menu->parent = current_menu; menu->server = server; + menu->size.width = MENUWIDTH; + /* menu->size.height will be kept up to date by adding items */ + menu->scene_tree = wlr_scene_tree_create(&server->menu_tree->node); + wlr_scene_node_set_enabled(&menu->scene_tree->node, false); return menu; } -static struct menu * -get_menu_by_id(const char *id) +struct menu * +menu_get_by_id(const char *id) { + if (!id) { + return NULL; + } struct menu *menu; for (int i = 0; i < nr_menus; ++i) { menu = menus + i; @@ -74,32 +82,58 @@ item_create(struct menu *menu, const char *text) if (!menuitem) { return NULL; } -/* FIXME */ -#if 0 struct server *server = menu->server; struct theme *theme = server->theme; -#endif struct font font = { .name = rc.font_name_menuitem, .size = rc.font_size_menuitem, }; - menuitem->box.width = MENUWIDTH; - menuitem->box.height = font_height(&font) + 2 * MENU_ITEM_PADDING_Y; + if (!menu->item_height) { + menu->item_height = font_height(&font) + 2 * MENU_ITEM_PADDING_Y; + } -/* FIXME */ -#if 0 + int x, y; int item_max_width = MENUWIDTH - 2 * MENU_ITEM_PADDING_X; - font_texture_create(server, &menuitem->texture.active, item_max_width, - text, &font, theme->menu_items_active_text_color); - font_texture_create(server, &menuitem->texture.inactive, item_max_width, - text, &font, theme->menu_items_text_color); + struct wlr_scene_node *parent = &menu->scene_tree->node; - /* center align vertically */ - menuitem->texture.offset_y = - (menuitem->box.height - menuitem->texture.active->height) / 2; - menuitem->texture.offset_x = MENU_ITEM_PADDING_X; -#endif + /* Font buffer */ + font_buffer_create(&menuitem->normal.buffer, item_max_width, + text, &font, theme->menu_items_text_color); + font_buffer_create(&menuitem->selected.buffer, item_max_width, + text, &font, theme->menu_items_active_text_color); + + /* Item background nodes */ + menuitem->normal.background = &wlr_scene_rect_create(parent, + MENUWIDTH, menu->item_height, + theme->menu_items_bg_color)->node; + menuitem->selected.background = &wlr_scene_rect_create(parent, + MENUWIDTH, menu->item_height, + theme->menu_items_active_bg_color)->node; + + /* Font nodes */ + menuitem->normal.text = &wlr_scene_buffer_create( + menuitem->normal.background, &menuitem->normal.buffer->base)->node; + menuitem->selected.text = &wlr_scene_buffer_create( + menuitem->selected.background, &menuitem->selected.buffer->base)->node; + + /* Center font nodes */ + y = (menu->item_height - menuitem->normal.buffer->base.height) / 2; + x = MENU_ITEM_PADDING_X; + wlr_scene_node_set_position(menuitem->normal.text, x, y); + wlr_scene_node_set_position(menuitem->selected.text, x, y); + + /* Position the item in relation to its menu */ + int item_count = wl_list_length(&menu->menuitems); + y = item_count * menu->item_height; + wlr_scene_node_set_position(menuitem->normal.background, 0, y); + wlr_scene_node_set_position(menuitem->selected.background, 0, y); + + /* Hide selected state */ + wlr_scene_node_set_enabled(menuitem->selected.background, false); + + /* Update menu extends */ + menu->size.height = (item_count + 1) * menu->item_height; wl_list_insert(&menu->menuitems, &menuitem->link); wl_list_init(&menuitem->actions); @@ -210,7 +244,7 @@ handle_menu_element(xmlNode *n, struct server *server) current_menu = current_menu->parent; --menu_level; } else if (id) { - struct menu *menu = get_menu_by_id(id); + struct menu *menu = menu_get_by_id(id); if (menu) { current_item = item_create(current_menu, menu->label); current_item->submenu = menu; @@ -286,78 +320,127 @@ err: free(b.buf); } +static int +menu_get_full_width(struct menu *menu) +{ + int width = menu->size.width - menu->server->theme->menu_overlap_x; + int child_width; + int max_child_width = 0; + struct menuitem *item; + wl_list_for_each_reverse(item, &menu->menuitems, link) { + if (!item->submenu) { + continue; + } + child_width = menu_get_full_width(item->submenu); + if (child_width > max_child_width) { + max_child_width = child_width; + } + } + return width + max_child_width; +} + static void -menu_configure(struct menu *menu, int x, int y) +menu_configure(struct menu *menu, int lx, int ly, enum menu_align align) { struct theme *theme = menu->server->theme; - menu->box.x = x; - menu->box.y = y; + /* Get output local coordinates + output usable area */ + double ox = lx; + double oy = ly; + struct wlr_output *wlr_output = wlr_output_layout_output_at( + menu->server->output_layout, lx, ly); + wlr_output_layout_output_coords(menu->server->output_layout, + wlr_output, &ox, &oy); + struct wlr_box usable = output_usable_area_from_cursor_coords(menu->server); - int offset = 0; - struct menuitem *menuitem; - wl_list_for_each_reverse (menuitem, &menu->menuitems, link) { - menuitem->box.x = menu->box.x; - menuitem->box.y = menu->box.y + offset; - offset += menuitem->box.height; - if (menuitem->submenu) { - menu_configure(menuitem->submenu, menuitem->box.x - + MENUWIDTH - theme->menu_overlap_x, - menuitem->box.y + theme->menu_overlap_y); + if (align == LAB_MENU_OPEN_AUTO) { + int full_width = menu_get_full_width(menu); + if (ox + full_width > usable.width) { + align = LAB_MENU_OPEN_LEFT; + } else { + align = LAB_MENU_OPEN_RIGHT; } } - menu->box.width = MENUWIDTH; - menu->box.height = offset; + if (oy + menu->size.height > usable.height) { + align &= ~LAB_MENU_OPEN_BOTTOM; + align |= LAB_MENU_OPEN_TOP; + } else { + align &= ~LAB_MENU_OPEN_TOP; + align |= LAB_MENU_OPEN_BOTTOM; + } + + if (align & LAB_MENU_OPEN_LEFT) { + lx -= MENUWIDTH - theme->menu_overlap_x; + } + if (align & LAB_MENU_OPEN_TOP) { + ly -= menu->size.height; + if (menu->parent) { + /* For submenus adjust y to bottom left corner */ + ly += menu->item_height; + } + } + wlr_scene_node_set_position(&menu->scene_tree->node, lx, ly); + + int rel_y; + int new_lx, new_ly; + struct menuitem *item; + wl_list_for_each_reverse(item, &menu->menuitems, link) { + if (!item->submenu) { + continue; + } + if (align & LAB_MENU_OPEN_RIGHT) { + new_lx = lx + MENUWIDTH - theme->menu_overlap_x; + } else { + new_lx = lx; + } + rel_y = item->normal.background->state.y; + new_ly = ly + rel_y - theme->menu_overlap_y; + menu_configure(item->submenu, new_lx, new_ly, align); + } } void menu_init_rootmenu(struct server *server) { parse_xml("menu.xml", server); - server->rootmenu = get_menu_by_id("root-menu"); + struct menu *menu = menu_get_by_id("root-menu"); /* Default menu if no menu.xml found */ - if (!server->rootmenu) { + if (!menu) { current_menu = NULL; - server->rootmenu = menu_create(server, "root-menu", ""); + menu = menu_create(server, "root-menu", ""); } - if (wl_list_empty(&server->rootmenu->menuitems)) { - current_item = item_create(server->rootmenu, "Reconfigure"); + if (wl_list_empty(&menu->menuitems)) { + current_item = item_create(menu, "Reconfigure"); fill_item("name.action", "Reconfigure"); - current_item = item_create(server->rootmenu, "Exit"); + current_item = item_create(menu, "Exit"); fill_item("name.action", "Exit"); } - - server->rootmenu->visible = true; - menu_configure(server->rootmenu, 100, 100); } void menu_init_windowmenu(struct server *server) { - server->windowmenu = get_menu_by_id("client-menu"); + struct menu *menu = menu_get_by_id("client-menu"); /* Default menu if no menu.xml found */ - if (!server->windowmenu) { + if (!menu) { current_menu = NULL; - server->windowmenu = menu_create(server, "client-menu", ""); + menu = menu_create(server, "client-menu", ""); } - if (wl_list_empty(&server->windowmenu->menuitems)) { - current_item = item_create(server->windowmenu, "Minimize"); + if (wl_list_empty(&menu->menuitems)) { + current_item = item_create(menu, "Minimize"); fill_item("name.action", "Iconify"); - current_item = item_create(server->windowmenu, "Maximize"); + current_item = item_create(menu, "Maximize"); fill_item("name.action", "ToggleMaximize"); - current_item = item_create(server->windowmenu, "Fullscreen"); + current_item = item_create(menu, "Fullscreen"); fill_item("name.action", "ToggleFullscreen"); - current_item = item_create(server->windowmenu, "Decorations"); + current_item = item_create(menu, "Decorations"); fill_item("name.action", "ToggleDecorations"); - current_item = item_create(server->windowmenu, "Close"); + current_item = item_create(menu, "Close"); fill_item("name.action", "Close"); } - - server->windowmenu->visible = true; - menu_configure(server->windowmenu, 100, 100); } void @@ -370,104 +453,157 @@ menu_finish(void) wl_list_for_each_safe(item, next, &menu->menuitems, link) { wl_list_remove(&item->link); action_list_free(&item->actions); + wlr_scene_node_destroy(item->normal.text); + wlr_scene_node_destroy(item->selected.text); + wlr_scene_node_destroy(item->normal.background); + wlr_scene_node_destroy(item->selected.background); + wlr_buffer_drop(&item->normal.buffer->base); + wlr_buffer_drop(&item->selected.buffer->base); free(item); } + wlr_scene_node_destroy(&menu->scene_tree->node); } zfree(menus); alloc_menus = 0; nr_menus = 0; } +/* Sets selection (or clears selection if passing NULL) */ +static void +menu_set_selection(struct menu *menu, struct menuitem *item) +{ + /* Clear old selection */ + if (menu->selection.item) { + wlr_scene_node_set_enabled( + menu->selection.item->normal.background, true); + wlr_scene_node_set_enabled( + menu->selection.item->selected.background, false); + } + /* Set new selection */ + if (item) { + wlr_scene_node_set_enabled(item->normal.background, false); + wlr_scene_node_set_enabled(item->selected.background, true); + } + menu->selection.item = item; +} + static void close_all_submenus(struct menu *menu) { struct menuitem *item; wl_list_for_each (item, &menu->menuitems, link) { if (item->submenu) { - item->submenu->visible = false; + wlr_scene_node_set_enabled(&item->submenu->scene_tree->node, false); close_all_submenus(item->submenu); } } + menu->selection.menu = NULL; } void -menu_move(struct menu *menu, int x, int y) +menu_open(struct menu *menu, int x, int y) { assert(menu); + if (menu->server->menu_current) { + menu_close(menu->server->menu_current); + } close_all_submenus(menu); - menu_configure(menu, x, y); + menu_set_selection(menu, NULL); + menu_configure(menu, x, y, LAB_MENU_OPEN_AUTO); + wlr_scene_node_set_enabled(&menu->scene_tree->node, true); + menu->server->menu_current = menu; + menu->server->input_mode = LAB_INPUT_STATE_MENU; } -/* TODO: consider renaming function to menu_process_cursor_motion */ void -menu_set_selected(struct menu *menu, int x, int y) +menu_process_cursor_motion(struct menu *menu, struct wlr_scene_node *node) { - if (!menu->visible) { + if (!node) { + wlr_log(WLR_ERROR, "menu_process_cursor_motion() node == NULL"); return; } + assert(menu); + + /* TODO: this would be much easier if we could use node->data */ struct menuitem *item; wl_list_for_each (item, &menu->menuitems, link) { - item->selected = wlr_box_contains_point(&item->box, x, y); - - if (!item->selected) { - if (item->submenu && item->submenu->visible) { - /* - * Handle the case where a submenu is already - * open. - */ - item->selected = true; - menu_set_selected(item->submenu, x, y); - } - continue; + if (node == item->selected.background + || node->parent == item->selected.background) { + /* We are on an already selected item */ + return; } - - /* We're now on an item that has mouse-focus */ - if (item->submenu) { - if (item->submenu->visible) { - /* do nothing - submenu already open */ - } else { - /* open submenu */ - close_all_submenus(menu); - item->submenu->visible = true; - menu_set_selected(item->submenu, x, y); + if (node == item->normal.background + || node->parent == item->normal.background) { + /* We are on an item that has new mouse-focus */ + menu_set_selection(menu, item); + if (menu->selection.menu) { + /* Close old submenu tree */ + menu_close(menu->selection.menu); } - } else { - close_all_submenus(menu); + if (item->submenu) { + /* And open the new one */ + wlr_scene_node_set_enabled( + &item->submenu->scene_tree->node, true); + } + menu->selection.menu = item->submenu; + return; + } + if (item->submenu && item->submenu == menu->selection.menu) { + menu_process_cursor_motion(item->submenu, node); } } } -static void -menu_clear_selection(struct menu *menu) + +bool +menu_call_actions(struct menu *menu, struct wlr_scene_node *node) { - struct menuitem *item; - wl_list_for_each (item, &menu->menuitems, link) { - item->selected = false; - if (item->submenu) { - menu_clear_selection(item->submenu); + /* TODO: this would be much easier if we could use node->data */ + + if (!menu->selection.item) { + /* No item selected in current menu */ + wlr_log(WLR_ERROR, "No item on menu_action_selected"); + return false; + } + struct wlr_scene_node *menu_node = + menu->selection.item->selected.background; + if (node == menu_node || node->parent == menu_node) { + /* We found the correct menu item */ + if (menu->selection.item->submenu) { + /* ..but it just opens a submenu */ + return false; } + action(NULL, menu->server, &menu->selection.item->actions, 0); + menu_close(menu->server->menu_current); + menu->server->menu_current = NULL; + return true; + } + if (menu->selection.menu) { + return menu_call_actions(menu->selection.menu, node); + } + wlr_log(WLR_ERROR, "No match on menu_action_selected"); + return false; +} + +void +menu_close(struct menu *menu) +{ + if (!menu) { + wlr_log(WLR_ERROR, "Trying to close non exiting menu"); + return; + } + /* TODO: Maybe reset input state here instead of in cursor.c ? */ + wlr_scene_node_set_enabled(&menu->scene_tree->node, false); + menu_set_selection(menu, NULL); + if (menu->selection.menu) { + menu_close(menu->selection.menu); + menu->selection.menu = NULL; } } void -menu_action_selected(struct server *server, struct menu *menu) -{ - struct menuitem *menuitem; - wl_list_for_each (menuitem, &menu->menuitems, link) { - if (menuitem->selected && !menuitem->submenu) { - action(NULL, server, &menuitem->actions, 0); - break; - } - if (menuitem->submenu) { - menu_action_selected(server, menuitem->submenu); - } - } - menu_clear_selection(menu); -} - -void -menu_reconfigure(struct server *server, struct menu *menu) +menu_reconfigure(struct server *server) { menu_finish(); menu_init_rootmenu(server); diff --git a/src/server.c b/src/server.c index fbefa0e2..60c6c4e8 100644 --- a/src/server.c +++ b/src/server.c @@ -43,7 +43,7 @@ reload_config_and_theme(void) ssd_update_geometry(view, true); } - menu_reconfigure(g_server, g_server->rootmenu); + menu_reconfigure(g_server); seat_reconfigure(g_server); damage_all_outputs(g_server); } @@ -225,6 +225,7 @@ server_init(struct server *server) } server->view_tree = wlr_scene_tree_create(&server->scene->node); server->osd_tree = wlr_scene_tree_create(&server->scene->node); + server->menu_tree = wlr_scene_tree_create(&server->scene->node); wlr_scene_attach_output_layout(server->scene, server->output_layout); /* 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 18/56] 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; From a2523081e2886bbbb969ea4f5c1e8a8b8f39bfa9 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Tue, 22 Feb 2022 07:57:17 +0100 Subject: [PATCH 19/56] Handle 'lost' unmanaged xsurfaces + improve cursor handling --- include/labwc.h | 39 +++++++++-- include/ssd.h | 1 + src/cursor.c | 137 +++++++++++++++++++++------------------ src/desktop.c | 8 ++- src/server.c | 7 +- src/xwayland-unmanaged.c | 19 ++++-- 6 files changed, 135 insertions(+), 76 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index c54b1f23..bee3607f 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -143,9 +143,6 @@ struct server { struct seat seat; struct wlr_scene *scene; - /* Tree for all non-layer xdg/xwayland-shell surfaces */ - struct wlr_scene_tree *view_tree; - /* cursor interactive */ enum input_mode input_mode; struct view *grabbed_view; @@ -157,8 +154,16 @@ struct server { struct view *ssd_focused_view; struct ssd_hover_state ssd_hover_state; - struct wlr_scene_tree *osd_tree; + /* Tree for all non-layer xdg/xwayland-shell surfaces */ + struct wlr_scene_tree *view_tree; +#if HAVE_XWAYLAND + /* Tree for unmanaged xsurfaces without initialized view (usually popups) */ + struct wlr_scene_tree *unmanaged_tree; +#endif + /* Tree for built in menu */ struct wlr_scene_tree *menu_tree; + /* Tree for built in OSD / app switcher */ + struct wlr_scene_tree *osd_tree; struct wl_list outputs; struct wl_listener new_output; @@ -415,9 +420,29 @@ bool isfocusable(struct view *view); /** * desktop_node_and_view_at - find view and scene_node at (lx, ly) - * Note: If node points to layer-surface, view_area will be set - * to LAB_SSD_LAYER_SURFACE, if view points to another surface - * view_area will be LAB_SSD_CLIENT + * + * Behavior if node points to a surface: + * - If surface is a layer-surface, *view_area will be + * set to LAB_SSD_LAYER_SURFACE and view will be NULL. + * + * - If surface is a 'lost' unmanaged xsurface (one + * with a never-mapped parent view), *view_area will + * be set to LAB_SSD_UNMANAGED and view will be NULL. + * + * 'Lost' unmanaged xsurfaces are usually caused by + * X11 applications opening popups without setting + * the main window as parent. Example: VLC submenus. + * + * - Any other surface will cause *view_area be set to + * LAB_SSD_CLIENT and return the attached view. + * + * Behavior if node points to internal elements: + * - *view_area will be set to the appropiate enum value + * and view will be NULL if the node is not part of the SSD. + * + * If no node is found for the given layout coordinates, + * *view_area will be set to LAB_SSD_ROOT and view will be NULL. + * */ struct view *desktop_node_and_view_at(struct server *server, double lx, double ly, struct wlr_scene_node **scene_node, double *sx, double *sy, diff --git a/include/ssd.h b/include/ssd.h index 0f4473eb..c547b784 100644 --- a/include/ssd.h +++ b/include/ssd.h @@ -45,6 +45,7 @@ enum ssd_part_type { LAB_SSD_MENU, LAB_SSD_OSD, LAB_SSD_LAYER_SURFACE, + LAB_SSD_UNMANAGED, LAB_SSD_END_MARKER }; diff --git a/src/cursor.c b/src/cursor.c index 4751916e..e55dcf78 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -12,6 +12,17 @@ #include "ssd.h" #include "config/mousebind.h" +static bool +is_surface(enum ssd_part_type view_area) +{ + return view_area == LAB_SSD_CLIENT + || view_area == LAB_SSD_LAYER_SURFACE +#if HAVE_XWAYLAND + || view_area == LAB_SSD_UNMANAGED +#endif + ; +} + void cursor_rebase(struct seat *seat, uint32_t time_msec) { @@ -22,7 +33,7 @@ cursor_rebase(struct seat *seat, uint32_t time_msec) desktop_node_and_view_at(seat->server, seat->cursor->x, seat->cursor->y, &node, &sx, &sy, &view_area); - if (view_area == LAB_SSD_CLIENT || view_area == LAB_SSD_LAYER_SURFACE) { + if (is_surface(view_area)) { surface = wlr_scene_surface_from_node(node)->surface; } @@ -198,7 +209,7 @@ process_cursor_motion(struct server *server, uint32_t time) &sx, &sy, &view_area); struct wlr_surface *surface = NULL; - if (view_area == LAB_SSD_CLIENT || view_area == LAB_SSD_LAYER_SURFACE) { + if (is_surface(view_area)) { surface = wlr_scene_surface_from_node(node)->surface; } @@ -206,23 +217,21 @@ process_cursor_motion(struct server *server, uint32_t time) uint32_t resize_edges = ssd_resize_edges(view_area); /* Set cursor */ - if (!view) { - /* root, etc. */ + if (view_area == LAB_SSD_ROOT || view_area == LAB_SSD_MENU) { cursor_set(&server->seat, XCURSOR_DEFAULT); - } else { - if (resize_edges) { - cursor_name_set_by_server = true; - cursor_set(&server->seat, - wlr_xcursor_get_resize_name(resize_edges)); - } else if (ssd_part_contains(LAB_SSD_PART_TITLEBAR, view_area)) { - /* title and buttons */ - cursor_set(&server->seat, XCURSOR_DEFAULT); - cursor_name_set_by_server = true; - } else if (cursor_name_set_by_server) { - /* window content */ - cursor_set(&server->seat, XCURSOR_DEFAULT); - cursor_name_set_by_server = false; - } + } else if (resize_edges) { + cursor_name_set_by_server = true; + cursor_set(&server->seat, + wlr_xcursor_get_resize_name(resize_edges)); + } else if (ssd_part_contains(LAB_SSD_PART_TITLEBAR, view_area)) { + /* title and buttons */ + cursor_set(&server->seat, XCURSOR_DEFAULT); + cursor_name_set_by_server = true; + } else if (cursor_name_set_by_server) { + /* xdg/xwindow window content */ + /* layershell or unmanaged */ + cursor_set(&server->seat, XCURSOR_DEFAULT); + cursor_name_set_by_server = false; } if (view_area == LAB_SSD_MENU) { @@ -496,6 +505,13 @@ handle_release_mousebinding(struct view *view, struct server *server, break; } continue; + case MOUSE_ACTION_DRAG: + if (mousebind->pressed_in_context) { + /* Swallow the release event as well as the press one */ + activated_any = true; + activated_any_frame |= mousebind->context == LAB_SSD_FRAME; + } + continue; default: continue; } @@ -565,6 +581,9 @@ handle_press_mousebinding(struct view *view, struct server *server, * counted as a DOUBLECLICK. */ if (!double_click) { + /* Swallow the press event as well as the release one */ + activated_any = true; + activated_any_frame |= mousebind->context == LAB_SSD_FRAME; mousebind->pressed_in_context = true; } continue; @@ -601,7 +620,15 @@ cursor_button(struct wl_listener *listener, void *data) double sx, sy; struct wlr_scene_node *node; enum ssd_part_type view_area = LAB_SSD_NONE; - uint32_t resize_edges; + uint32_t resize_edges = 0; + + /** + * Used in WLR_BUTTON_RELEASED, set on WLR_BUTTON_PRESSED + * + * Automatically initialized with 0 / false and + * checkpatch.pl complains when done manually. + */ + static bool close_menu; /* bindings to the Frame context swallow mouse events if activated */ bool triggered_frame_binding = false; @@ -611,7 +638,7 @@ cursor_button(struct wl_listener *listener, void *data) &sx, &sy, &view_area); struct wlr_surface *surface = NULL; - if (view_area == LAB_SSD_CLIENT || view_area == LAB_SSD_LAYER_SURFACE) { + if (is_surface(view_area)) { surface = wlr_scene_surface_from_node(node)->surface; } @@ -622,9 +649,17 @@ cursor_button(struct wl_listener *listener, void *data) /* handle _release_ */ if (event->state == WLR_BUTTON_RELEASED) { if (server->input_mode == LAB_INPUT_STATE_MENU) { + if (close_menu) { + if (server->menu_current) { + menu_close(server->menu_current); + server->menu_current = NULL; + } + server->input_mode = LAB_INPUT_STATE_PASSTHROUGH; + cursor_rebase(&server->seat, event->time_msec); + close_menu = false; + } return; } - damage_all_outputs(server); if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) { /* Exit interactive move/resize/menu mode. */ if (server->grabbed_view == view) { @@ -634,63 +669,41 @@ cursor_button(struct wl_listener *listener, void *data) server->grabbed_view = NULL; } cursor_rebase(&server->seat, event->time_msec); - } - - /* Handle _release_ on root window */ - if (!view) { - handle_release_mousebinding(NULL, server, event->button, - modifiers, LAB_SSD_ROOT, 0); + return; } goto mousebindings; } + /* Handle _press */ if (server->input_mode == LAB_INPUT_STATE_MENU) { - if (!server->menu_current) { - wlr_log(WLR_ERROR, - "on mouse button input_mode STATE_MENU but no current menu"); - } else if (view_area != LAB_SSD_MENU) { - menu_close(server->menu_current); - server->menu_current = NULL; - } else if (!menu_call_actions(server->menu_current, node)) { - /* Action was not successfull, maybe this menu has a submenu */ - return; + if (view_area != LAB_SSD_MENU) { + /* We close the menu on release so we don't leak a stray release */ + close_menu = true; + } else if (menu_call_actions(server->menu_current, node)) { + /* Action was successfull, may fail if item contains a submenu */ + close_menu = true; } - /* TODO: following causes stray release */ - /* Maybe add LAB_INPUT_STATE_IGNORE_MOUSE_RELEASE ? */ - server->input_mode = LAB_INPUT_STATE_PASSTHROUGH; - cursor_rebase(&server->seat, event->time_msec); return; } /* Handle _press_ on a layer surface */ - if (view_area == LAB_SSD_LAYER_SURFACE && surface) { - if (!wlr_surface_is_layer_surface(surface)) { - return; - } + if (view_area == LAB_SSD_LAYER_SURFACE) { struct wlr_layer_surface_v1 *layer = wlr_layer_surface_v1_from_wlr_surface(surface); if (layer->current.keyboard_interactive) { seat_set_focus_layer(&server->seat, layer); } - wlr_seat_pointer_notify_button(seat->seat, event->time_msec, - event->button, event->state); - return; } - /* Handle _press_ on root window */ - if (!view) { - handle_press_mousebinding(NULL, server, - event->button, modifiers, LAB_SSD_ROOT, 0); - return; - } - - /* Determine closest resize edges in case action is Resize */ - resize_edges = ssd_resize_edges(view_area); - if (!resize_edges) { - resize_edges |= server->seat.cursor->x < view->x + view->w / 2 - ? WLR_EDGE_LEFT : WLR_EDGE_RIGHT; - resize_edges |= server->seat.cursor->y < view->y + view->h / 2 - ? WLR_EDGE_TOP : WLR_EDGE_BOTTOM; + if (view) { + /* Determine closest resize edges in case action is Resize */ + resize_edges = ssd_resize_edges(view_area); + if (!resize_edges) { + resize_edges |= server->seat.cursor->x < view->x + view->w / 2 + ? WLR_EDGE_LEFT : WLR_EDGE_RIGHT; + resize_edges |= server->seat.cursor->y < view->y + view->h / 2 + ? WLR_EDGE_TOP : WLR_EDGE_BOTTOM; + } } mousebindings: @@ -703,7 +716,7 @@ mousebindings: server, event->button, modifiers, view_area, resize_edges); } - if (!triggered_frame_binding) { + if (surface && !triggered_frame_binding) { /* Notify client with pointer focus of button press */ wlr_seat_pointer_notify_button(seat->seat, event->time_msec, event->button, event->state); diff --git a/src/desktop.c b/src/desktop.c index 51a39210..e829c701 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -266,7 +266,7 @@ desktop_node_and_view_at(struct server *server, double lx, double ly, *scene_node = node; if (!node) { - *view_area = LAB_SSD_NONE; + *view_area = LAB_SSD_ROOT; return NULL; } if (node->type == WLR_SCENE_NODE_SURFACE) { @@ -276,6 +276,12 @@ desktop_node_and_view_at(struct server *server, double lx, double ly, *view_area = LAB_SSD_LAYER_SURFACE; return NULL; } +#if HAVE_XWAYLAND + if (node->parent == &server->unmanaged_tree->node) { + *view_area = LAB_SSD_UNMANAGED; + return NULL; + } +#endif } struct wlr_scene_node *osd = &server->osd_tree->node; struct wlr_scene_node *menu = &server->menu_tree->node; diff --git a/src/server.c b/src/server.c index 9e279aa6..f29971d4 100644 --- a/src/server.c +++ b/src/server.c @@ -224,8 +224,11 @@ server_init(struct server *server) exit(EXIT_FAILURE); } server->view_tree = wlr_scene_tree_create(&server->scene->node); - server->osd_tree = wlr_scene_tree_create(&server->scene->node); +#if HAVE_XWAYLAND + server->unmanaged_tree = wlr_scene_tree_create(&server->scene->node); +#endif server->menu_tree = wlr_scene_tree_create(&server->scene->node); + server->osd_tree = wlr_scene_tree_create(&server->scene->node); wlr_scene_attach_output_layout(server->scene, server->output_layout); /* @@ -406,6 +409,8 @@ server_start(struct server *server) void server_finish(struct server *server) { + +/* TODO: clean up various scene_tree nodes */ #if HAVE_XWAYLAND wlr_xwayland_destroy(server->xwayland); #endif diff --git a/src/xwayland-unmanaged.c b/src/xwayland-unmanaged.c index a80b9e6e..03387320 100644 --- a/src/xwayland-unmanaged.c +++ b/src/xwayland-unmanaged.c @@ -32,7 +32,7 @@ parent_view(struct server *server, struct wlr_xwayland_surface *surface) } struct view *view; wl_list_for_each(view, &server->views, link) { - if (view->surface == s->surface) { + if (view->xwayland_surface == s) { return view; } } @@ -59,11 +59,20 @@ unmanaged_handle_map(struct wl_listener *listener, void *data) seat_focus_surface(&unmanaged->server->seat, xsurface->surface); } + int lx = unmanaged->lx; + int ly = unmanaged->ly; + struct wlr_scene_node *parent, *node; struct view *view = parent_view(unmanaged->server, xsurface); - struct wlr_scene_node *node = wlr_scene_subsurface_tree_create( - view->scene_node, xsurface->surface); - wlr_scene_node_set_position(node, unmanaged->lx - view->x, - unmanaged->ly - view->y); + if (!view || !view->scene_tree) { + parent = &view->server->unmanaged_tree->node; + } else { + lx -= view->x; + ly -= view->y; + parent = &view->scene_tree->node; + } + /* node will be destroyed automatically once surface is destroyed */ + node = &wlr_scene_surface_create(parent, xsurface->surface)->node; + wlr_scene_node_set_position(node, lx, ly); } static void From 17d83402eef8cbeb9f6793d66b7ec2804a5913e3 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Tue, 22 Feb 2022 10:01:49 +0100 Subject: [PATCH 20/56] xwayland.c: Fix xwayland views activating themself before mapping --- src/xwayland-unmanaged.c | 2 +- src/xwayland.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/xwayland-unmanaged.c b/src/xwayland-unmanaged.c index 03387320..78516b01 100644 --- a/src/xwayland-unmanaged.c +++ b/src/xwayland-unmanaged.c @@ -63,7 +63,7 @@ unmanaged_handle_map(struct wl_listener *listener, void *data) int ly = unmanaged->ly; struct wlr_scene_node *parent, *node; struct view *view = parent_view(unmanaged->server, xsurface); - if (!view || !view->scene_tree) { + if (!view || !view->scene_node) { parent = &view->server->unmanaged_tree->node; } else { lx -= view->x; diff --git a/src/xwayland.c b/src/xwayland.c index 5560ba7e..59878d12 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -258,7 +258,6 @@ map(struct view *view) } view->surface = view->xwayland_surface->surface; - view->scene_tree = wlr_scene_tree_create(&view->server->view_tree->node); view->scene_node = wlr_scene_subsurface_tree_create( &view->scene_tree->node, view->surface); if (!view->scene_node) { @@ -376,6 +375,7 @@ xwayland_surface_new(struct wl_listener *listener, void *data) view->impl = &xwl_view_impl; view->xwayland_surface = xsurface; + view->scene_tree = wlr_scene_tree_create(&view->server->view_tree->node); xsurface->data = view; view->map.notify = handle_map; From c2f7e0be3df2ef27a8e7bc87f0d81caca98fada3 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Tue, 22 Feb 2022 19:29:14 +0100 Subject: [PATCH 21/56] debug.c: try to id important elements --- src/debug.c | 187 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 140 insertions(+), 47 deletions(-) diff --git a/src/debug.c b/src/debug.c index 8d36daf0..7d95953e 100644 --- a/src/debug.c +++ b/src/debug.c @@ -4,9 +4,14 @@ #include "buffer.h" #include "labwc.h" -#define INDENT_SIZE (3) +#define HEADER_CHARS "------------------------------" -static char * +#define INDENT_SIZE 3 +#define IGNORE_SSD true +#define IGNORE_MENU true +#define LEFT_COL_SPACE 35 + +static const char * get_node_type(enum wlr_scene_node_type type) { switch (type) { @@ -24,64 +29,152 @@ get_node_type(enum wlr_scene_node_type type) return "error"; } -static void -dump_tree(struct wlr_scene_node *node, int pos, int x, int y) -{ - char *type = get_node_type(node->type); - - if (pos) { - printf("%*c+-- ", pos, ' '); - } - printf("%s (%d,%d) [%p]\n", type, x, y, node); - - struct wlr_scene_node *child; - wl_list_for_each(child, &node->state.children, state.link) { - dump_tree(child, pos + INDENT_SIZE, x + child->state.x, - y + child->state.y); - } -} - -static char * +static const char * get_layer_name(uint32_t layer) { switch (layer) { case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND: - return "background"; + return "layer-background"; case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM: - return "bottom"; + return "layer-bottom"; case ZWLR_LAYER_SHELL_V1_LAYER_TOP: - return "top"; + return "layer-top"; case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY: - return "overlay"; + return "layer-overlay"; default: abort(); } } +static const char * +get_view_part(struct view *view, struct wlr_scene_node *node) +{ + if (view && node == &view->scene_tree->node) { + return "view"; + } + if (view && node == view->scene_node) { + return "view->scene_node"; + } + if (!view || !view->ssd.tree) { + return NULL; + } + if (node == &view->ssd.tree->node) { + return "view->ssd"; + } + if (node == &view->ssd.titlebar.active.tree->node) { + return "titlebar.active"; + } + if (node == &view->ssd.titlebar.inactive.tree->node) { + return "titlebar.inactive"; + } + if (node == &view->ssd.border.active.tree->node) { + return "border.active"; + } + if (node == &view->ssd.border.inactive.tree->node) { + return "border.inactive"; + } + if (node == &view->ssd.extents.tree->node) { + return "extents"; + } + return NULL; +} + +static const char * +get_special(struct server *server, struct wlr_scene_node *node, + struct view **last_view) +{ + if (node == &server->scene->node) { + return "server->scene"; + } + if (node == &server->osd_tree->node) { + return "server->osd_tree"; + } + if (node == &server->menu_tree->node) { + return "server->menu_tree"; + } + if (node->parent == &server->scene->node) { + struct output *output; + wl_list_for_each(output, &server->outputs, link) { + for (int i = 0; i < 4; i++) { + if (node == &output->layer_tree[i]->node) { + return get_layer_name(i); + } + } + } + } +#if HAVE_XWAYLAND + if (node == &server->unmanaged_tree->node) { + return "server->unmanaged_tree"; + } +#endif + if (node == &server->view_tree->node) { + return "server->view_tree"; + } + if (node->parent == &server->view_tree->node) { + *last_view = node->data; + } + const char *view_part = get_view_part(*last_view, node); + if (view_part) { + return view_part; + } + return get_node_type(node->type); +} + +struct pad { + uint8_t left; + uint8_t right; +}; + +static struct pad +get_center_padding(const char *text, uint8_t max_width) +{ + struct pad pad; + size_t text_len = strlen(text); + pad.left = (double)(max_width - text_len) / 2 + 0.5f; + pad.right = max_width - pad.left - text_len; + return pad; +} + +static void +dump_tree(struct server *server, struct wlr_scene_node *node, + int pos, int x, int y) +{ + static struct view *view; + const char *type = get_special(server, node, &view); + + if (pos) { + printf("%*c+-- ", pos, ' '); + } else { + struct pad node_pad = get_center_padding("Node", 16); + printf(" %*c %4s %4s %*c%s\n", LEFT_COL_SPACE + 4, ' ', + "X", "Y", node_pad.left, ' ', "Node"); + printf(" %*c %.4s %.4s %.16s\n", LEFT_COL_SPACE + 4, ' ', + HEADER_CHARS, HEADER_CHARS, HEADER_CHARS); + printf(" "); + } + int padding = LEFT_COL_SPACE - pos - strlen(type); + if (!pos) { + padding += 3; + } + printf("%s %*c %4d %4d [%p]\n", type, padding, ' ', x, y, node); + + if ((IGNORE_MENU && node == &server->menu_tree->node) + || (IGNORE_SSD && view && view->ssd.tree + && node == &view->ssd.tree->node)) { + printf("%*c%s\n", pos + 4 + INDENT_SIZE, ' ', ""); + return; + } + struct wlr_scene_node *child; + wl_list_for_each(child, &node->state.children, state.link) { + dump_tree(server, child, pos + INDENT_SIZE, + x + child->state.x, y + child->state.y); + } +} + void debug_dump_scene(struct server *server) { - struct wlr_scene_node *node; - - printf(":: view_tree ::\n"); - node = &server->view_tree->node; - dump_tree(node, 0, node->state.x, node->state.y); - - printf(":: layer_tree ::\n"); - struct output *output; - wl_list_for_each(output, &server->outputs, link) { - for (int i = 0; i < 4; i++) { - node = &output->layer_tree[i]->node; - printf("layer-%s\n", get_layer_name(i)); - dump_tree(node, 0, node->state.x, node->state.y); - } - } - - printf(":: osd_tree ::\n"); - node = &server->osd_tree->node; - dump_tree(node, 0, node->state.x, node->state.y); - - printf(":: menu_tree ::\n"); - node = &server->menu_tree->node; - dump_tree(node, 0, node->state.x, node->state.y); + printf("\n"); + dump_tree(server, &server->scene->node, 0, 0, 0); + printf("\n"); } From 29021dad6a1d1a1ceb0d1fec4be114142e99d9e3 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Wed, 23 Feb 2022 01:32:07 +0100 Subject: [PATCH 22/56] xdg/xwayland: Do not destroy scene node on unmap This prevents segfaulting when unminimizing --- src/ssd/ssd.c | 4 ---- src/xdg.c | 13 +++++++------ src/xwayland.c | 27 +++++++++++++++------------ 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index 14f0ee95..677c2017 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -149,12 +149,10 @@ 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); @@ -224,8 +222,6 @@ ssd_destroy(struct view *view) return; } - wlr_log(WLR_ERROR, "Destroying SSD"); - /* Maybe reset focused view */ if (view->server->ssd_focused_view == view) { view->server->ssd_focused_view = NULL; diff --git a/src/xdg.c b/src/xdg.c index ee06781c..3b6178f3 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -299,8 +299,12 @@ xdg_toplevel_view_get_string_prop(struct view *view, const char *prop) static void xdg_toplevel_view_map(struct view *view) { + if (view->mapped) { + return; + } view->mapped = true; view->surface = view->xdg_surface->surface; + wlr_scene_node_set_enabled(&view->scene_tree->node, true); if (!view->been_mapped) { struct wlr_xdg_toplevel_requested *requested = &view->xdg_surface->toplevel->requested; @@ -340,9 +344,7 @@ xdg_toplevel_view_unmap(struct view *view) { if (view->mapped) { view->mapped = false; - damage_all_outputs(view->server); - wlr_scene_node_destroy(view->scene_node); - ssd_hide(view); + wlr_scene_node_set_enabled(&view->scene_tree->node, false); wl_list_remove(&view->commit.link); desktop_focus_topmost_mapped_view(view->server); } @@ -391,14 +393,13 @@ xdg_surface_new(struct wl_listener *listener, void *data) view->xdg_surface = xdg_surface; view->scene_tree = wlr_scene_tree_create(&view->server->view_tree->node); + wlr_scene_node_set_enabled(&view->scene_tree->node, false); view->scene_node = wlr_scene_xdg_surface_create( &view->scene_tree->node, view->xdg_surface); if (!view->scene_node) { - wlr_scene_node_destroy(&view->scene_tree->node); - view->scene_tree = NULL; + /* TODO: might need further clean up */ wl_resource_post_no_memory(view->surface->resource); - /* TODO: should we free(view) here? */ return; } view->scene_tree->node.data = view; diff --git a/src/xwayland.c b/src/xwayland.c index 59878d12..fa4be0fa 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -246,7 +246,11 @@ top_left_edge_boundary_check(struct view *view) static void map(struct view *view) { + if (view->mapped) { + return; + } view->mapped = true; + wlr_scene_node_set_enabled(&view->scene_tree->node, true); if (!view->fullscreen && view->xwayland_surface->fullscreen) { view_set_fullscreen(view, true, NULL); } @@ -256,18 +260,17 @@ map(struct view *view) view->w = view->xwayland_surface->width; view->h = view->xwayland_surface->height; } - view->surface = view->xwayland_surface->surface; - view->scene_node = wlr_scene_subsurface_tree_create( - &view->scene_tree->node, view->surface); - if (!view->scene_node) { - wlr_scene_node_destroy(&view->scene_tree->node); - view->scene_tree = NULL; - wl_resource_post_no_memory(view->surface->resource); - /* TODO: should we free(view) here? */ - return; + if (view->surface != view->xwayland_surface->surface) { + view->surface = view->xwayland_surface->surface; + view->scene_node = wlr_scene_subsurface_tree_create( + &view->scene_tree->node, view->surface); + if (!view->scene_node) { + /* TODO: might need further clean up */ + wl_resource_post_no_memory(view->surface->resource); + return; + } } - view->scene_tree->node.data = view; view->ssd.enabled = want_deco(view); @@ -308,9 +311,8 @@ unmap(struct view *view) { if (view->mapped) { view->mapped = false; - damage_all_outputs(view->server); + wlr_scene_node_set_enabled(&view->scene_tree->node, false); wl_list_remove(&view->commit.link); - ssd_hide(view); desktop_focus_topmost_mapped_view(view->server); } } @@ -376,6 +378,7 @@ xwayland_surface_new(struct wl_listener *listener, void *data) view->xwayland_surface = xsurface; view->scene_tree = wlr_scene_tree_create(&view->server->view_tree->node); + view->scene_tree->node.data = view; xsurface->data = view; view->map.notify = handle_map; From 581a2bb2f10c6f54f4762b4f4afb10a33a88f052 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Wed, 23 Feb 2022 22:19:16 +0000 Subject: [PATCH 23/56] cursor+keyboard: chase wlroots master Use wlr_input_device from input device base. Use wlr_keyboard where possible. --- src/cursor.c | 4 ++-- src/keyboard.c | 17 ++++++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/cursor.c b/src/cursor.c index e55dcf78..ef158cf4 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -643,8 +643,8 @@ cursor_button(struct wl_listener *listener, void *data) } /* get modifiers */ - struct wlr_input_device *device = seat->keyboard_group->input_device; - uint32_t modifiers = wlr_keyboard_get_modifiers(device->keyboard); + struct wlr_keyboard *keyboard = &seat->keyboard_group->keyboard; + uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard); /* handle _release_ */ if (event->state == WLR_BUTTON_RELEASED) { diff --git a/src/keyboard.c b/src/keyboard.c index 1eef22a4..88c356b5 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -41,10 +41,10 @@ keyboard_modifiers_notify(struct wl_listener *listener, void *data) if (server->cycle_view) { struct wlr_event_keyboard_key *event = data; - struct wlr_input_device *device = seat->keyboard_group->input_device; + struct wlr_keyboard *keyboard = &seat->keyboard_group->keyboard; damage_all_outputs(server); if ((event->state == WL_KEYBOARD_KEY_STATE_RELEASED) - && !any_modifiers_pressed(device->keyboard)) { + && !any_modifiers_pressed(keyboard)) { /* end cycle */ desktop_focus_and_activate_view(&server->seat, server->cycle_view); @@ -96,13 +96,13 @@ handle_compositor_keybindings(struct wl_listener *listener, { struct seat *seat = wl_container_of(listener, seat, keyboard_key); struct server *server = seat->server; - struct wlr_input_device *device = seat->keyboard_group->input_device; + struct wlr_keyboard *keyboard = &seat->keyboard_group->keyboard; /* Translate libinput keycode -> xkbcommon */ uint32_t keycode = event->keycode + 8; /* Get a list of keysyms based on the keymap for this keyboard */ const xkb_keysym_t *syms; - int nsyms = xkb_state_key_get_syms(device->keyboard->xkb_state, keycode, &syms); + int nsyms = xkb_state_key_get_syms(keyboard->xkb_state, keycode, &syms); bool handled = false; @@ -117,14 +117,13 @@ handle_compositor_keybindings(struct wl_listener *listener, && event->state == WL_KEYBOARD_KEY_STATE_RELEASED) { int nr_bound_keys = key_state_bound_key_remove(keycode); if (!nr_bound_keys) { - wlr_keyboard_set_repeat_info(device->keyboard, + wlr_keyboard_set_repeat_info(keyboard, rc.repeat_rate, rc.repeat_delay); } return true; } - uint32_t modifiers = - wlr_keyboard_get_modifiers(device->keyboard); + uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard); /* Catch C-A-F1 to C-A-F12 to change tty */ if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { @@ -190,7 +189,7 @@ keyboard_key_notify(struct wl_listener *listener, void *data) struct server *server = seat->server; struct wlr_event_keyboard_key *event = data; struct wlr_seat *wlr_seat = server->seat.seat; - struct wlr_input_device *device = seat->keyboard_group->input_device; + struct wlr_keyboard *keyboard = &seat->keyboard_group->keyboard; wlr_idle_notify_activity(seat->wlr_idle, seat->seat); bool handled = false; @@ -201,7 +200,7 @@ keyboard_key_notify(struct wl_listener *listener, void *data) } if (!handled) { - wlr_seat_set_keyboard(wlr_seat, device); + wlr_seat_set_keyboard(wlr_seat, &keyboard->base); wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, event->keycode, event->state); } From f4c9d1ba9fe0ad99c27fda3493a7f9c97b5163fe Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Wed, 23 Feb 2022 22:39:57 +0000 Subject: [PATCH 24/56] Set node position in xdg/xwayland commit handlers ...in order to sync resize/move events to avoid left/bottom edge wobbling when resizing with top/left resize edges --- src/view.c | 3 --- src/xdg.c | 2 ++ src/xwayland.c | 6 ++++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/view.c b/src/view.c index 64674482..740ed63f 100644 --- a/src/view.c +++ b/src/view.c @@ -48,7 +48,6 @@ view_move_resize(struct view *view, struct wlr_box geo) } ssd_update_title(view); view_discover_output(view); - wlr_scene_node_set_position(&view->scene_tree->node, view->x, view->y); } #define MIN_VIEW_WIDTH (100) @@ -150,7 +149,6 @@ view_center(struct view *view) if (view_compute_centered_position(view, view->w, view->h, &x, &y)) { view_move(view, x, y); } - wlr_scene_node_set_position(&view->scene_tree->node, view->x, view->y); } static void @@ -226,7 +224,6 @@ view_maximize(struct view *view, bool maximize) if (view->fullscreen) { return; } - wlr_scene_node_set_position(&view->scene_tree->node, view->x, view->y); if (view->impl->maximize) { view->impl->maximize(view, maximize); } diff --git a/src/xdg.c b/src/xdg.c index 3b6178f3..5ab801f3 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -57,6 +57,8 @@ handle_commit(struct wl_listener *listener, void *data) if (serial == view->xdg_surface->current.configure_serial) { view->pending_move_resize.configure_serial = 0; } + wlr_scene_node_set_position(&view->scene_tree->node, + view->x, view->y); } ssd_update_geometry(view); damage_view_part(view); diff --git a/src/xwayland.c b/src/xwayland.c index fa4be0fa..ddbff726 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -12,6 +12,8 @@ handle_commit(struct wl_listener *listener, void *data) /* Must receive commit signal before accessing surface->current* */ view->w = view->surface->current.width; view->h = view->surface->current.height; + bool move_pending = view->pending_move_resize.update_x + || view->pending_move_resize.update_y; if (view->pending_move_resize.update_x) { view->x = view->pending_move_resize.x + @@ -23,6 +25,10 @@ handle_commit(struct wl_listener *listener, void *data) view->pending_move_resize.height - view->h; view->pending_move_resize.update_y = false; } + if (move_pending) { + wlr_scene_node_set_position(&view->scene_tree->node, + view->x, view->y); + } ssd_update_geometry(view); damage_view_whole(view); } From c03366771645a38e8b13673afc586a051ffb5a3c Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Thu, 24 Feb 2022 01:27:29 +0100 Subject: [PATCH 25/56] src/ssd/: Don't access view->surface->current directly Instead use view->{w,h} which are set on client commit --- src/ssd/ssd.c | 6 +++--- src/ssd/ssd_border.c | 8 ++++---- src/ssd/ssd_extents.c | 8 ++++---- src/ssd/ssd_part.c | 9 +++++++++ src/ssd/ssd_titlebar.c | 32 ++++++++++++++------------------ 5 files changed, 34 insertions(+), 29 deletions(-) diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index 677c2017..72f56306 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -164,7 +164,7 @@ 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) { + if (!view->ssd.tree || !view->scene_node) { return; } @@ -175,8 +175,8 @@ ssd_update_geometry(struct view *view) wlr_scene_node_set_enabled(&view->ssd.tree->node, false); } - int width = view->surface->current.width; - int height = view->surface->current.height; + int width = view->w; + int height = view->h; if (width == view->ssd.state.width && height == view->ssd.state.height) { return; } diff --git a/src/ssd/ssd_border.c b/src/ssd/ssd_border.c index 399d2c8c..aa289e21 100644 --- a/src/ssd/ssd_border.c +++ b/src/ssd/ssd_border.c @@ -13,8 +13,8 @@ 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 width = view->w; + int height = view->h; int full_width = width + 2 * theme->border_width; float *color; @@ -48,8 +48,8 @@ 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 width = view->w; + int height = view->h; int full_width = width + 2 * theme->border_width; struct ssd_part *part; diff --git a/src/ssd/ssd_extents.c b/src/ssd/ssd_extents.c index 0175d767..d72b5df0 100644 --- a/src/ssd/ssd_extents.c +++ b/src/ssd/ssd_extents.c @@ -11,8 +11,8 @@ 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 width = view->w; + int height = view->h; int full_height = height + theme->border_width + SSD_HEIGHT; int full_width = width + 2 * theme->border_width; int extended_area = EXTENDED_AREA; @@ -61,8 +61,8 @@ 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 width = view->w; + int height = view->h; int full_height = height + theme->border_width + SSD_HEIGHT; int full_width = width + 2 * theme->border_width; int extended_area = EXTENDED_AREA; diff --git a/src/ssd/ssd_part.c b/src/ssd/ssd_part.c index ab718637..f6e55ba1 100644 --- a/src/ssd/ssd_part.c +++ b/src/ssd/ssd_part.c @@ -18,6 +18,15 @@ 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]) { + /* + * When initialized without surface being mapped, + * size may be negative. Just set to 0, next call + * to ssd_*_update() will update the rect to use + * its correct size. + */ + width = width >= 0 ? width : 0; + height = height >= 0 ? height : 0; + struct ssd_part *part = add_scene_part(list, type); part->node = &wlr_scene_rect_create( parent, width, height, color)->node; diff --git a/src/ssd/ssd_titlebar.c b/src/ssd/ssd_titlebar.c index 16e1785b..968bd144 100644 --- a/src/ssd/ssd_titlebar.c +++ b/src/ssd/ssd_titlebar.c @@ -17,7 +17,7 @@ void ssd_titlebar_create(struct view *view) { struct theme *theme = view->server->theme; - int width = view->surface->current.width; + int width = view->w; int full_width = width + 2 * theme->border_width; float *color; @@ -72,7 +72,7 @@ is_direct_child(struct wlr_scene_node *node, struct ssd_sub_tree *subtree) void ssd_titlebar_update(struct view *view) { - int width = view->surface->current.width; + int width = view->w; if (width == view->ssd.state.width) { return; } @@ -144,7 +144,7 @@ static void ssd_update_title_positions(struct view *view) { struct theme *theme = view->server->theme; - int width = view->surface->current.width; + int width = view->w; int full_width = width + 2 * view->server->theme->border_width; int x, y; @@ -154,8 +154,7 @@ ssd_update_title_positions(struct view *view) 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"); + /* view->surface never been mapped */ continue; } @@ -163,8 +162,6 @@ ssd_update_title_positions(struct view *view) 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; } @@ -221,17 +218,8 @@ ssd_update_title(struct view *view) 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; - } + assert(parent_part); + if (subtree == &view->ssd.titlebar.active) { dstate = &state->active; text_color = theme->window_active_label_text_color; @@ -239,11 +227,19 @@ ssd_update_title(struct view *view) dstate = &state->inactive; text_color = theme->window_inactive_label_text_color; } + + rect = lab_wlr_scene_get_rect(parent_part->node); + if (rect->width <= 0) { + dstate->truncated = true; + continue; + } + 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); From 7d98bc7aa22a70d82eeb87e5465e9159e59d4a15 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Thu, 24 Feb 2022 01:37:19 +0100 Subject: [PATCH 26/56] ssd/ssd_titlebar.c: Only store title state if title has been changed --- src/ssd/ssd_titlebar.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ssd/ssd_titlebar.c b/src/ssd/ssd_titlebar.c index 968bd144..1c7d9876 100644 --- a/src/ssd/ssd_titlebar.c +++ b/src/ssd/ssd_titlebar.c @@ -255,10 +255,12 @@ ssd_update_title(struct view *view) dstate->truncated = rect->width <= dstate->width; } FOR_EACH_END - if (state->text) { - free(state->text); + if (!title_unchanged) { + if (state->text) { + free(state->text); + } + state->text = strdup(title); } - state->text = strdup(title); ssd_update_title_positions(view); } From 953e8a4f9d8b4ff93e9de7e848c6ef9610a06674 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Thu, 24 Feb 2022 02:16:19 +0100 Subject: [PATCH 27/56] [style] rename action() to actions_run() --- include/action.h | 2 +- src/action.c | 3 ++- src/cursor.c | 6 +++--- src/keyboard.c | 2 +- src/menu/menu.c | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/include/action.h b/include/action.h index 44a16fd2..b23a0ec9 100644 --- a/include/action.h +++ b/include/action.h @@ -14,7 +14,7 @@ struct action { struct action *action_create(const char *action_name); void action_list_free(struct wl_list *action_list); -void action(struct view *activator, struct server *server, +void actions_run(struct view *activator, struct server *server, struct wl_list *actions, uint32_t resize_edges); #endif diff --git a/src/action.c b/src/action.c index 65aee0dc..a4376650 100644 --- a/src/action.c +++ b/src/action.c @@ -129,7 +129,8 @@ activator_or_focused_view(struct view *activator, struct server *server) } void -action(struct view *activator, struct server *server, struct wl_list *actions, uint32_t resize_edges) +actions_run(struct view *activator, struct server *server, + struct wl_list *actions, uint32_t resize_edges) { if (!actions) { wlr_log(WLR_ERROR, "empty actions"); diff --git a/src/cursor.c b/src/cursor.c index ef158cf4..5203a56c 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -261,7 +261,7 @@ process_cursor_motion(struct server *server, uint32_t time) } mousebind->pressed_in_context = false; - action(NULL, server, &mousebind->actions, resize_edges); + actions_run(NULL, server, &mousebind->actions, resize_edges); } } @@ -517,7 +517,7 @@ handle_release_mousebinding(struct view *view, struct server *server, } activated_any = true; activated_any_frame |= mousebind->context == LAB_SSD_FRAME; - action(view, server, &mousebind->actions, resize_edges); + actions_run(view, server, &mousebind->actions, resize_edges); } } /* @@ -599,7 +599,7 @@ handle_press_mousebinding(struct view *view, struct server *server, } activated_any = true; activated_any_frame |= mousebind->context == LAB_SSD_FRAME; - action(view, server, &mousebind->actions, resize_edges); + actions_run(view, server, &mousebind->actions, resize_edges); } } return activated_any && activated_any_frame; diff --git a/src/keyboard.c b/src/keyboard.c index 88c356b5..7776c3e7 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -70,7 +70,7 @@ handle_keybinding(struct server *server, uint32_t modifiers, xkb_keysym_t sym) for (size_t i = 0; i < keybind->keysyms_len; i++) { if (xkb_keysym_to_lower(sym) == keybind->keysyms[i]) { wlr_keyboard_set_repeat_info(kb, 0, 0); - action(NULL, server, &keybind->actions, 0); + actions_run(NULL, server, &keybind->actions, 0); return true; } } diff --git a/src/menu/menu.c b/src/menu/menu.c index beb85c58..1b7f9459 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -574,7 +574,7 @@ menu_call_actions(struct menu *menu, struct wlr_scene_node *node) /* ..but it just opens a submenu */ return false; } - action(NULL, menu->server, &menu->selection.item->actions, 0); + actions_run(NULL, menu->server, &menu->selection.item->actions, 0); menu_close(menu->server->menu_current); menu->server->menu_current = NULL; return true; From 9c4bacc9b7f781e23b56b05cde59bf26c91bd026 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Thu, 24 Feb 2022 02:33:17 +0100 Subject: [PATCH 28/56] [style] fix open brace after function definition --- src/action.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/action.c b/src/action.c index a4376650..e196a351 100644 --- a/src/action.c +++ b/src/action.c @@ -79,7 +79,8 @@ action_create(const char *action_name) return action; } -void action_list_free(struct wl_list *action_list) { +void action_list_free(struct wl_list *action_list) +{ struct action *action, *action_tmp; wl_list_for_each_safe(action, action_tmp, action_list, link) { wl_list_remove(&action->link); From 00ff00d9f93534efad63bccffda34f9ce14bf72f Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Thu, 24 Feb 2022 02:34:21 +0100 Subject: [PATCH 29/56] [style] use __func__ in log message --- src/menu/menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/menu/menu.c b/src/menu/menu.c index 1b7f9459..ff9a8b6a 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -519,7 +519,7 @@ void menu_process_cursor_motion(struct menu *menu, struct wlr_scene_node *node) { if (!node) { - wlr_log(WLR_ERROR, "menu_process_cursor_motion() node == NULL"); + wlr_log(WLR_ERROR, "%s() node == NULL", __func__); return; } assert(menu); From 5f62f2ba9993d46510e09ff088a9c8acdef26c52 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Fri, 25 Feb 2022 21:31:21 +0100 Subject: [PATCH 30/56] xwayland.c: Fix positioning with multiple queued configure events Prevents a single action like ToggleDecorations + ToggleMaximize to position the view somewhere with negative coordinates when unmaximizing. It may still position the view on negative coordinates but later commit events will fix the position. This issue only exists on xwayland because there are no configure serials which we could use to ignore all repositioning until we are at the latest desired state. --- include/labwc.h | 2 +- src/xwayland.c | 36 +++++++++++++++++++++++------------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index bee3607f..38bebc40 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -274,7 +274,7 @@ struct view { */ struct border padding; - struct { + struct view_pending_move_resize { bool update_x, update_y; double x, y; uint32_t width, height; diff --git a/src/xwayland.c b/src/xwayland.c index ddbff726..1dfb574a 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -10,27 +10,37 @@ handle_commit(struct wl_listener *listener, void *data) assert(view->surface); /* Must receive commit signal before accessing surface->current* */ - view->w = view->surface->current.width; - view->h = view->surface->current.height; - bool move_pending = view->pending_move_resize.update_x - || view->pending_move_resize.update_y; + struct wlr_surface_state *state = &view->surface->current; + struct view_pending_move_resize *pending = &view->pending_move_resize; - if (view->pending_move_resize.update_x) { - view->x = view->pending_move_resize.x + - view->pending_move_resize.width - view->w; - view->pending_move_resize.update_x = false; + bool move_pending = pending->update_x || pending->update_y; + bool size_changed = view->w != state->width || view->h != state->height; + + if (!move_pending && !size_changed) { + return; } - if (view->pending_move_resize.update_y) { - view->y = view->pending_move_resize.y + - view->pending_move_resize.height - view->h; - view->pending_move_resize.update_y = false; + + view->w = state->width; + view->h = state->height; + + if (pending->update_x) { + /* Adjust x for queued up configure events */ + view->x = pending->x + pending->width - view->w; + } + if (pending->update_y) { + /* Adjust y for queued up configure events */ + view->y = pending->y + pending->height - view->h; } if (move_pending) { wlr_scene_node_set_position(&view->scene_tree->node, view->x, view->y); } + if ((int)pending->width == view->w && (int)pending->height == view->h) { + /* We reached the end of all queued size changing configure events */ + pending->update_x = false; + pending->update_y = false; + } ssd_update_geometry(view); - damage_view_whole(view); } static void From 4c981b845fdf9331109108652e6b198fcdfbd251 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Fri, 25 Feb 2022 21:54:02 +0100 Subject: [PATCH 31/56] include/ssd.h remove unnecessary struct definition Instead, just tag the struct so we can reference it. --- include/ssd.h | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/include/ssd.h b/include/ssd.h index c547b784..fed6ba0c 100644 --- a/include/ssd.h +++ b/include/ssd.h @@ -65,12 +65,6 @@ struct ssd_state_title_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; @@ -83,7 +77,11 @@ struct ssd { struct { int width; int height; - struct ssd_state_title title; + struct ssd_state_title { + char *text; + struct ssd_state_title_width active; + struct ssd_state_title_width inactive; + } title; } state; /* An invisble area around the view which allows resizing */ From 2891ff245e163339002e77c8d7c8a78544707353 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Fri, 25 Feb 2022 22:31:24 +0000 Subject: [PATCH 32/56] Add node-descriptor for wlr_scene_nodes Support identification of wlr_scene_node role to enable simplification of codebase including the avoidance of iterating over lists of layer-surface, menuitems, and so on. Use node-descriptors for xdg toplevels and popups --- include/node-descriptor.h | 23 +++++++++++++++++++++++ src/desktop.c | 26 ++++++++++++++++++++++---- src/meson.build | 1 + src/node-descriptor.c | 37 +++++++++++++++++++++++++++++++++++++ src/xdg-popup.c | 3 +++ src/xdg.c | 4 +++- src/xwayland.c | 4 +++- 7 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 include/node-descriptor.h create mode 100644 src/node-descriptor.c diff --git a/include/node-descriptor.h b/include/node-descriptor.h new file mode 100644 index 00000000..d5225af9 --- /dev/null +++ b/include/node-descriptor.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __LABWC_NODE_DESCRIPTOR_H +#define __LABWC_NODE_DESCRIPTOR_H +#include + +enum node_descriptor_type { + LAB_NODE_DESC_NODE = 0, + LAB_NODE_DESC_VIEW, /* *data --> struct view */ + LAB_NODE_DESC_XDG_POPUP, /* *data --> struct view */ + LAB_NODE_DESC_LAYER_SURFACE, /* *data --> struct lab_layer_surface */ + LAB_NODE_DESC_LAYER_POPUP, /* *data --> struct lab_layer_popup */ +}; + +struct node_descriptor { + enum node_descriptor_type type; + void *data; + struct wl_listener destroy; +}; + +void node_descriptor_create(struct wlr_scene_node *node, + enum node_descriptor_type type, void *data); + +#endif /* __LABWC_NODE_DESCRIPTOR_H */ diff --git a/src/desktop.c b/src/desktop.c index e829c701..8e9a4bf0 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -3,6 +3,7 @@ #include #include "labwc.h" #include "layers.h" +#include "node-descriptor.h" #include "ssd.h" static void @@ -285,7 +286,21 @@ desktop_node_and_view_at(struct server *server, double lx, double ly, } struct wlr_scene_node *osd = &server->osd_tree->node; struct wlr_scene_node *menu = &server->menu_tree->node; - while (node && !node->data) { + while (node) { + struct node_descriptor *desc = node->data; + if (desc) { + if (desc->type == LAB_NODE_DESC_VIEW) { + goto has_view_data; + } + if (desc->type == LAB_NODE_DESC_XDG_POPUP) { + goto has_view_data; + } + if (desc->type == LAB_NODE_DESC_LAYER_POPUP) { + /* FIXME: we shouldn't have to set *view_area */ + *view_area = LAB_SSD_LAYER_SURFACE; + return NULL; + } + } if (node == osd) { *view_area = LAB_SSD_OSD; return NULL; @@ -297,10 +312,13 @@ desktop_node_and_view_at(struct server *server, double lx, double ly, } if (!node) { wlr_log(WLR_ERROR, "Unknown node detected"); - *view_area = LAB_SSD_NONE; - return NULL; } - struct view *view = node->data; + *view_area = LAB_SSD_NONE; + return NULL; + +has_view_data: + struct node_descriptor *desc = node->data; + struct view *view = desc->data; *view_area = ssd_get_part_type(view, *scene_node); return view; } diff --git a/src/meson.build b/src/meson.build index b318de8f..d690a63e 100644 --- a/src/meson.build +++ b/src/meson.build @@ -11,6 +11,7 @@ labwc_sources = files( 'key-state.c', 'layers.c', 'main.c', + 'node-descriptor.c', 'osd.c', 'output.c', 'resistance.c', diff --git a/src/node-descriptor.c b/src/node-descriptor.c new file mode 100644 index 00000000..efbcb048 --- /dev/null +++ b/src/node-descriptor.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include "node-descriptor.h" + +static void +descriptor_destroy(struct node_descriptor *node_descriptor) +{ + if (!node_descriptor) { + return; + } + wl_list_remove(&node_descriptor->destroy.link); + free(node_descriptor); +} + +static void +destroy_notify(struct wl_listener *listener, void *data) +{ + struct node_descriptor *node_descriptor = + wl_container_of(listener, node_descriptor, destroy); + descriptor_destroy(node_descriptor); +} + +void +node_descriptor_create(struct wlr_scene_node *node, + enum node_descriptor_type type, void *data) +{ + struct node_descriptor *node_descriptor = + calloc(1, sizeof(struct node_descriptor)); + if (!node_descriptor) { + return; + } + node_descriptor->type = type; + node_descriptor->data = data; + node_descriptor->destroy.notify = destroy_notify; + wl_signal_add(&node->events.destroy, &node_descriptor->destroy); + node->data = node_descriptor; +} diff --git a/src/xdg-popup.c b/src/xdg-popup.c index df500a8a..239a1332 100644 --- a/src/xdg-popup.c +++ b/src/xdg-popup.c @@ -8,6 +8,7 @@ */ #include "labwc.h" +#include "node-descriptor.h" struct view_child { struct wlr_surface *surface; @@ -93,6 +94,8 @@ xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup) struct wlr_scene_node *parent_node = parent->surface->data; wlr_popup->base->surface->data = wlr_scene_xdg_surface_create(parent_node, wlr_popup->base); + node_descriptor_create(wlr_popup->base->surface->data, + LAB_NODE_DESC_XDG_POPUP, view); popup_unconstrain(view, wlr_popup); } diff --git a/src/xdg.c b/src/xdg.c index 5ab801f3..7b433c4a 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #include #include "labwc.h" +#include "node-descriptor.h" #include "ssd.h" static void @@ -404,7 +405,8 @@ xdg_surface_new(struct wl_listener *listener, void *data) wl_resource_post_no_memory(view->surface->resource); return; } - view->scene_tree->node.data = view; + node_descriptor_create(&view->scene_tree->node, + LAB_NODE_DESC_VIEW, view); /* In support of xdg_toplevel_decoration */ xdg_surface->data = view; diff --git a/src/xwayland.c b/src/xwayland.c index 1dfb574a..f6756e4d 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #include #include "labwc.h" +#include "node-descriptor.h" #include "ssd.h" static void @@ -394,7 +395,8 @@ xwayland_surface_new(struct wl_listener *listener, void *data) view->xwayland_surface = xsurface; view->scene_tree = wlr_scene_tree_create(&view->server->view_tree->node); - view->scene_tree->node.data = view; + node_descriptor_create(&view->scene_tree->node, + LAB_NODE_DESC_VIEW, view); xsurface->data = view; view->map.notify = handle_map; From 49e8658a7b1dfcc9f1323076cdcaf348a08285bf Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sat, 26 Feb 2022 21:15:52 +0000 Subject: [PATCH 33/56] layer: support popups --- include/layers.h | 31 +---- src/layers.c | 293 ++++++++++++++++++++--------------------------- 2 files changed, 131 insertions(+), 193 deletions(-) diff --git a/include/layers.h b/include/layers.h index da88aafe..5db03c1f 100644 --- a/include/layers.h +++ b/include/layers.h @@ -8,13 +8,8 @@ struct server; #define LAB_NR_LAYERS (4) -enum layer_parent { - LAYER_PARENT_LAYER, - LAYER_PARENT_POPUP, -}; - struct lab_layer_surface { - struct wl_list link; /* output::layers[] */ + struct wl_list link; /* output::layers */ struct wlr_scene_layer_surface_v1 *scene_layer_surface; struct wl_listener destroy; @@ -23,39 +18,21 @@ struct lab_layer_surface { struct wl_listener surface_commit; struct wl_listener output_destroy; struct wl_listener new_popup; - struct wl_listener new_subsurface; struct wlr_box geo; bool mapped; - /* TODO: add extent and layer */ + /* TODO: add extent? */ struct server *server; }; -/* FIXME: do we still need lab_layer_popup and lab_layer_subsurface? */ struct lab_layer_popup { struct wlr_xdg_popup *wlr_popup; - enum layer_parent parent_type; - union { - struct lab_layer_surface *parent_layer; - struct lab_layer_popup *parent_popup; - }; - struct wl_listener map; - struct wl_listener unmap; + struct wlr_scene_node *scene_node; + struct wl_listener destroy; - struct wl_listener commit; struct wl_listener new_popup; }; -struct lab_layer_subsurface { - struct wlr_subsurface *wlr_subsurface; - struct lab_layer_surface *layer_surface; - - struct wl_listener map; - struct wl_listener unmap; - struct wl_listener destroy; - struct wl_listener commit; -}; - void layers_init(struct server *server); #endif /* __LABWC_LAYERS_H */ diff --git a/src/layers.c b/src/layers.c index 901a72ec..3207e4b3 100644 --- a/src/layers.c +++ b/src/layers.c @@ -2,7 +2,7 @@ /* * layers.c - layer-shell implementation * - * Based on: + * Based on * - https://git.sr.ht/~sircmpwm/wio * - https://github.com/swaywm/sway * Copyright (C) 2019 Drew DeVault and Sway developers @@ -16,17 +16,14 @@ #include #include "layers.h" #include "labwc.h" +#include "node-descriptor.h" void arrange_layers(struct output *output) { - struct server *server = output->server; - struct wlr_scene_output *scene_output = - wlr_scene_get_scene_output(server->scene, output->wlr_output); - struct wlr_box full_area = { 0 }; wlr_output_effective_resolution(output->wlr_output, - &full_area.width, &full_area.height); + &full_area.width, &full_area.height); struct wlr_box usable_area = full_area; for (int i = 0; i < LAB_NR_LAYERS; i++) { @@ -110,7 +107,6 @@ unmap(struct lab_layer_surface *layer) if (seat->focused_layer == layer->scene_layer_surface->layer_surface) { seat_set_focus_layer(seat, NULL); } - damage_all_outputs(layer->server); } static void @@ -122,15 +118,15 @@ destroy_notify(struct wl_listener *listener, void *data) unmap(layer); } - /* TODO: sort this out properly */ wl_list_remove(&layer->link); wl_list_remove(&layer->destroy.link); wl_list_remove(&layer->map.link); + wl_list_remove(&layer->unmap.link); wl_list_remove(&layer->surface_commit.link); if (layer->scene_layer_surface->layer_surface->output) { wl_list_remove(&layer->output_destroy.link); - struct output *output = output_from_wlr_output( - layer->server, layer->scene_layer_surface->layer_surface->output); + struct output *output = output_from_wlr_output(layer->server, + layer->scene_layer_surface->layer_surface->output); arrange_layers(output); } free(layer); @@ -139,160 +135,128 @@ destroy_notify(struct wl_listener *listener, void *data) static void unmap_notify(struct wl_listener *listener, void *data) { - struct lab_layer_surface *l = wl_container_of(listener, l, unmap); - unmap(l); + return; + struct lab_layer_surface *lab_layer_surface = + wl_container_of(listener, lab_layer_surface, unmap); + unmap(lab_layer_surface); } static void map_notify(struct wl_listener *listener, void *data) { - struct wlr_layer_surface_v1 *l = data; - wlr_surface_send_enter(l->surface, l->output); + return; + struct wlr_layer_surface_v1 *layer_surface = data; + wlr_surface_send_enter(layer_surface->surface, layer_surface->output); } -//static struct -//lab_layer_surface *popup_get_layer(struct lab_layer_popup *popup) -//{ -// while (popup->parent_type == LAYER_PARENT_POPUP) { -// popup = popup->parent_popup; -// } -// return popup->parent_layer; -//} -// -//static void -//popup_damage(struct lab_layer_popup *layer_popup, bool whole) -//{ -// struct lab_layer_surface *layer; -// while (true) { -// if (layer_popup->parent_type == LAYER_PARENT_POPUP) { -// layer_popup = layer_popup->parent_popup; -// } else { -// layer = layer_popup->parent_layer; -// break; -// } -// } -// damage_all_outputs(layer->server); -//} -// -//static void -//popup_handle_map(struct wl_listener *listener, void *data) -//{ -// struct lab_layer_popup *popup = wl_container_of(listener, popup, map); -// struct lab_layer_surface *layer = popup_get_layer(popup); -// struct wlr_output *wlr_output = layer->scene_layer_surface->layer_surface->output; -// wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output); -// popup_damage(popup, true); -//} -// -//static void -//popup_handle_unmap(struct wl_listener *listener, void *data) -//{ -// struct lab_layer_popup *popup = wl_container_of(listener, popup, unmap); -// popup_damage(popup, true); -//} -// -//static void -//popup_handle_commit(struct wl_listener *listener, void *data) -//{ -// struct lab_layer_popup *popup = wl_container_of(listener, popup, commit); -// popup_damage(popup, false); -//} -// -//static void -//popup_handle_destroy(struct wl_listener *listener, void *data) -//{ -// struct lab_layer_popup *popup = -// wl_container_of(listener, popup, destroy); -// wl_list_remove(&popup->destroy.link); -// free(popup); -//} -// -//static void -//popup_unconstrain(struct lab_layer_popup *popup) -//{ -// struct lab_layer_surface *layer = popup_get_layer(popup); -// struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; -// struct output *output = layer->scene_layer_surface->layer_surface->output->data; -// -// struct wlr_box output_box = { 0 }; -// wlr_output_effective_resolution(output->wlr_output, &output_box.width, -// &output_box.height); -// -// struct wlr_box output_toplevel_sx_box = { -// .x = -layer->geo.x, -// .y = -layer->geo.y, -// .width = output_box.width, -// .height = output_box.height, -// }; -// -// wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); -//} -// -//static void popup_handle_new_popup(struct wl_listener *listener, void *data); -// -//static struct lab_layer_popup * -//create_popup(struct wlr_xdg_popup *wlr_popup, -// enum layer_parent parent_type, void *parent) -//{ -// struct lab_layer_popup *popup = -// calloc(1, sizeof(struct lab_layer_popup)); -// if (!popup) { -// return NULL; -// } -// -// struct lab_layer_surface *layer = parent_type == LAYER_PARENT_LAYER -// ? (struct lab_layer_surface *)parent -// : (struct lab_layer_popup *)parent; -// struct server *server = layer->server; -// -// popup->wlr_popup = wlr_popup; -// popup->parent_type = parent_type; -// popup->parent_layer = parent; -// -// popup->destroy.notify = popup_handle_destroy; -// wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); -// popup->new_popup.notify = popup_handle_new_popup; -// wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); -// -// if (!wlr_surface_is_layer_surface(wlr_popup->base->surface)) { -// wlr_log(WLR_ERROR, "xdg_surface is not layer surface"); -// return; -// } -// -// struct wlr_output *wlr_output = -// layer->scene_layer_surface->layer_surface->data; -// struct output *output = output_from_wlr_output(server, wlr_output); -// -// struct wlr_scene_tree *selected_layer = -// output->layer_tree[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]; -// struct wlr_scene_node *node = -// wlr_scene_layer_surface_v1_create(&server->view_tree->node, -// wlr_popup->base->surface->data); -// wlr_popup->base->surface->data = -// wlr_scene_xdg_surface_create(&selected_layer->node, wlr_popup->base); -// -// popup_unconstrain(popup); -// -// return popup; -//} -// -//static void -//popup_handle_new_popup(struct wl_listener *listener, void *data) -//{ -// struct lab_layer_popup *lab_layer_popup = -// wl_container_of(listener, lab_layer_popup, new_popup); -// struct wlr_xdg_popup *wlr_popup = data; -// create_popup(wlr_popup, LAYER_PARENT_POPUP, lab_layer_popup); -//} -// -//static void -//new_popup_notify(struct wl_listener *listener, void *data) -//{ -// struct lab_layer_surface *lab_layer_surface = -// wl_container_of(listener, lab_layer_surface, new_popup); -// struct wlr_xdg_popup *wlr_popup = data; -// create_popup(wlr_popup, LAYER_PARENT_LAYER, lab_layer_surface); -//} +static struct +lab_layer_surface *popup_get_layer(struct lab_layer_popup *popup) +{ + struct wlr_scene_node *node = popup->scene_node; + while (node) { + if (node->data) { + struct node_descriptor *desc = node->data; + if (desc->type == LAB_NODE_DESC_LAYER_SURFACE) { + return desc->data; + } + } + node = node->parent; + } + return NULL; +} + +static void +popup_handle_destroy(struct wl_listener *listener, void *data) +{ + struct lab_layer_popup *popup = + wl_container_of(listener, popup, destroy); + wl_list_remove(&popup->destroy.link); + wl_list_remove(&popup->new_popup.link); + free(popup); +} + +static void +popup_unconstrain(struct lab_layer_popup *popup) +{ + struct lab_layer_surface *layer = popup_get_layer(popup); + if (!layer) { + return; + } + struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; + struct output *output = + layer->scene_layer_surface->layer_surface->output->data; + + struct wlr_box output_box = { 0 }; + wlr_output_effective_resolution(output->wlr_output, &output_box.width, + &output_box.height); + + /* + * Output geometry expressed in the coordinate system of the toplevel + * parent of popup + */ + struct wlr_box output_toplevel_sx_box = { + .x = -layer->geo.x, + .y = -layer->geo.y, + .width = output_box.width, + .height = output_box.height, + }; + + wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); +} + +static void popup_handle_new_popup(struct wl_listener *listener, void *data); + +static struct lab_layer_popup * +create_popup(struct wlr_xdg_popup *wlr_popup, struct wlr_scene_node *parent) +{ + struct lab_layer_popup *popup = + calloc(1, sizeof(struct lab_layer_popup)); + if (!popup) { + return NULL; + } + + popup->wlr_popup = wlr_popup; + popup->scene_node = + wlr_scene_xdg_surface_create(parent, wlr_popup->base); + if (!popup->scene_node) { + free(popup); + return NULL; + } + node_descriptor_create(popup->scene_node, + LAB_NODE_DESC_LAYER_POPUP, popup); + + popup->destroy.notify = popup_handle_destroy; + wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); + popup->new_popup.notify = popup_handle_new_popup; + wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); + + /* + * FIXME: should we put popups in ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY + * or a dedicated output->layer_popup_tree - so that for example + * a panel in the bottom layer displays any popup above views. + */ + + popup_unconstrain(popup); + return popup; +} + +static void +popup_handle_new_popup(struct wl_listener *listener, void *data) +{ + struct lab_layer_popup *lab_layer_popup = + wl_container_of(listener, lab_layer_popup, new_popup); + struct wlr_xdg_popup *wlr_popup = data; + create_popup(wlr_popup, lab_layer_popup->scene_node); +} + +static void +new_popup_notify(struct wl_listener *listener, void *data) +{ + struct lab_layer_surface *lab_layer_surface = + wl_container_of(listener, lab_layer_surface, new_popup); + struct wlr_xdg_popup *wlr_popup = data; + create_popup(wlr_popup, lab_layer_surface->scene_layer_surface->node); +} static void new_layer_surface_notify(struct wl_listener *listener, void *data) @@ -327,13 +291,8 @@ new_layer_surface_notify(struct wl_listener *listener, void *data) surface->unmap.notify = unmap_notify; wl_signal_add(&layer_surface->events.unmap, &surface->unmap); - /* TODO: support popups */ -// surface->new_popup.notify = new_popup_notify; -// wl_signal_add(&layer_surface->events.new_popup, &surface->new_popup); -// -// surface->new_subsurface.notify = new_subsurface_notify; -// wl_signal_add(&layer_surface->surface->events.new_subsurface, -// &surface->new_subsurface); + surface->new_popup.notify = new_popup_notify; + wl_signal_add(&layer_surface->events.new_popup, &surface->new_popup); struct output *output = layer_surface->output->data; @@ -342,12 +301,14 @@ new_layer_surface_notify(struct wl_listener *listener, void *data) surface->scene_layer_surface = wlr_scene_layer_surface_v1_create( &selected_layer->node, layer_surface); + node_descriptor_create(surface->scene_layer_surface->node, + LAB_NODE_DESC_LAYER_SURFACE, surface); surface->server = server; surface->scene_layer_surface->layer_surface = layer_surface; - /* wlr_surface->data needed to find parent in xdg_surface_new() */ - layer_surface->surface->data = surface->scene_layer_surface->node; +// /* wlr_surface->data needed to find parent in xdg_surface_new() */ +// layer_surface->surface->data = surface->scene_layer_surface->node; surface->output_destroy.notify = output_destroy_notify; wl_signal_add(&layer_surface->output->events.destroy, From cec0c5cee664bf6a6f3ed337e364790949d7f7ca Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sat, 26 Feb 2022 23:30:02 +0000 Subject: [PATCH 34/56] desktop.c: set *view_area right for layer-popup --- src/desktop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/desktop.c b/src/desktop.c index 8e9a4bf0..5f8a7609 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -297,7 +297,7 @@ desktop_node_and_view_at(struct server *server, double lx, double ly, } if (desc->type == LAB_NODE_DESC_LAYER_POPUP) { /* FIXME: we shouldn't have to set *view_area */ - *view_area = LAB_SSD_LAYER_SURFACE; + *view_area = LAB_SSD_CLIENT; return NULL; } } From c0f369aafb3f9d71d3913f7e08745508146cdef6 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Mon, 28 Feb 2022 22:30:36 +0000 Subject: [PATCH 35/56] Enable presentation time --- src/server.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/server.c b/src/server.c index f29971d4..808e3b35 100644 --- a/src/server.c +++ b/src/server.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -299,6 +300,14 @@ server_init(struct server *server) WLR_SERVER_DECORATION_MANAGER_MODE_SERVER : WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT); + struct wlr_presentation *presentation = + wlr_presentation_create(server->wl_display, server->backend); + if (!presentation) { + wlr_log(WLR_ERROR, "unable to create presentation interface"); + exit(EXIT_FAILURE); + } + wlr_scene_set_presentation(server->scene, presentation); + wlr_export_dmabuf_manager_v1_create(server->wl_display); wlr_screencopy_manager_v1_create(server->wl_display); wlr_data_control_manager_v1_create(server->wl_display); From e289100a5413b809c22a4bbf95334efa1ced4956 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Tue, 1 Mar 2022 20:45:31 +0000 Subject: [PATCH 36/56] xwayland-unmanaged: fix dereference NULL pointer bug Discovered when right-clicking names in hexchat --- src/xwayland-unmanaged.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xwayland-unmanaged.c b/src/xwayland-unmanaged.c index 78516b01..02c2fdd6 100644 --- a/src/xwayland-unmanaged.c +++ b/src/xwayland-unmanaged.c @@ -64,7 +64,7 @@ unmanaged_handle_map(struct wl_listener *listener, void *data) struct wlr_scene_node *parent, *node; struct view *view = parent_view(unmanaged->server, xsurface); if (!view || !view->scene_node) { - parent = &view->server->unmanaged_tree->node; + parent = &unmanaged->server->unmanaged_tree->node; } else { lx -= view->x; ly -= view->y; From bd9ac3478a3ff1625c639bd2d4cf211926901dad Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Tue, 1 Mar 2022 20:53:34 +0000 Subject: [PATCH 37/56] Remove src/damage.c --- include/labwc.h | 7 ------- src/action.c | 2 -- src/cursor.c | 2 -- src/damage.c | 17 ----------------- src/desktop.c | 1 - src/keyboard.c | 2 -- src/meson.build | 1 - src/output.c | 3 --- src/seat.c | 1 - src/server.c | 1 - src/view-impl.c | 2 -- src/view.c | 1 - src/xdg.c | 2 -- src/xwayland-unmanaged.c | 3 --- src/xwayland.c | 3 --- 15 files changed, 48 deletions(-) delete mode 100644 src/damage.c diff --git a/include/labwc.h b/include/labwc.h index 38bebc40..8eb48858 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -483,19 +483,12 @@ void interactive_begin(struct view *view, enum input_mode mode, void interactive_end(struct view *view); void output_init(struct server *server); -void output_damage_surface(struct output *output, struct wlr_surface *surface, - double lx, double ly, bool whole); -void scale_box(struct wlr_box *box, float scale); void output_manager_init(struct server *server); struct output *output_from_wlr_output(struct server *server, struct wlr_output *wlr_output); struct wlr_box output_usable_area_in_layout_coords(struct output *output); struct wlr_box output_usable_area_from_cursor_coords(struct server *server); -void damage_all_outputs(struct server *server); -void damage_view_whole(struct view *view); -void damage_view_part(struct view *view); - void server_init(struct server *server); void server_start(struct server *server); void server_finish(struct server *server); diff --git a/src/action.c b/src/action.c index e196a351..4a283cca 100644 --- a/src/action.c +++ b/src/action.c @@ -211,7 +211,6 @@ actions_run(struct view *activator, struct server *server, view = desktop_view_at_cursor(server); if (view) { desktop_focus_and_activate_view(&server->seat, view); - damage_all_outputs(server); } break; case ACTION_TYPE_ICONIFY: @@ -228,7 +227,6 @@ actions_run(struct view *activator, struct server *server, case ACTION_TYPE_RAISE: if (view) { desktop_move_to_front(view); - damage_all_outputs(server); } break; case ACTION_TYPE_RESIZE: diff --git a/src/cursor.c b/src/cursor.c index 5203a56c..f6716cb7 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -113,7 +113,6 @@ request_start_drag_notify(struct wl_listener *listener, void *data) static void process_cursor_move(struct server *server, uint32_t time) { - damage_all_outputs(server); double dx = server->seat.cursor->x - server->grab_x; double dy = server->seat.cursor->y - server->grab_y; struct view *view = server->grabbed_view; @@ -128,7 +127,6 @@ process_cursor_move(struct server *server, uint32_t time) static void process_cursor_resize(struct server *server, uint32_t time) { - damage_all_outputs(server); double dx = server->seat.cursor->x - server->grab_x; double dy = server->seat.cursor->y - server->grab_y; diff --git a/src/damage.c b/src/damage.c deleted file mode 100644 index 983ff428..00000000 --- a/src/damage.c +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include "labwc.h" - -void -damage_all_outputs(struct server *server) -{ -} - -void -damage_view_part(struct view *view) -{ -} - -void -damage_view_whole(struct view *view) -{ -} diff --git a/src/desktop.c b/src/desktop.c index 5f8a7609..c1745f81 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -199,7 +199,6 @@ desktop_cycle_view(struct server *server, struct view *current, view = wl_container_of(view->link.prev, view, link); } while (&view->link == &server->views || !isfocusable(view)); } - damage_all_outputs(server); return view; } diff --git a/src/keyboard.c b/src/keyboard.c index 7776c3e7..5f44ce21 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -42,7 +42,6 @@ keyboard_modifiers_notify(struct wl_listener *listener, void *data) if (server->cycle_view) { struct wlr_event_keyboard_key *event = data; struct wlr_keyboard *keyboard = &seat->keyboard_group->keyboard; - damage_all_outputs(server); if ((event->state == WL_KEYBOARD_KEY_STATE_RELEASED) && !any_modifiers_pressed(keyboard)) { /* end cycle */ @@ -138,7 +137,6 @@ handle_compositor_keybindings(struct wl_listener *listener, } if (server->cycle_view) { - damage_all_outputs(server); if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { for (int i = 0; i < nsyms; i++) { if (syms[i] == XKB_KEY_Escape) { diff --git a/src/meson.build b/src/meson.build index d690a63e..38d4803d 100644 --- a/src/meson.build +++ b/src/meson.build @@ -2,7 +2,6 @@ labwc_sources = files( 'action.c', 'buffer.c', 'cursor.c', - 'damage.c', 'debug.c', 'desktop.c', 'foreign.c', diff --git a/src/output.c b/src/output.c index 07700c58..61dcc286 100644 --- a/src/output.c +++ b/src/output.c @@ -186,9 +186,6 @@ output_update_for_layout_change(struct server *server) wlr_cursor_move(server->seat.cursor, NULL, 0, 0); wlr_xcursor_manager_set_cursor_image(server->seat.xcursor_manager, XCURSOR_DEFAULT, server->seat.cursor); - - /* Redraw everything */ - damage_all_outputs(server); } static void diff --git a/src/seat.c b/src/seat.c index 1f3d5d2a..45e40bf4 100644 --- a/src/seat.c +++ b/src/seat.c @@ -310,7 +310,6 @@ seat_set_focus_layer(struct seat *seat, struct wlr_layer_surface_v1 *layer) if (!layer) { seat->focused_layer = NULL; desktop_focus_topmost_mapped_view(seat->server); - damage_all_outputs(seat->server); return; } seat_focus_surface(seat, layer->surface); diff --git a/src/server.c b/src/server.c index 808e3b35..f23d9055 100644 --- a/src/server.c +++ b/src/server.c @@ -46,7 +46,6 @@ reload_config_and_theme(void) menu_reconfigure(g_server); seat_reconfigure(g_server); - damage_all_outputs(g_server); } static int diff --git a/src/view-impl.c b/src/view-impl.c index d10dd2a4..ff8f9e3f 100644 --- a/src/view-impl.c +++ b/src/view-impl.c @@ -12,6 +12,4 @@ view_impl_map(struct view *view) view_update_title(view); view_update_app_id(view); - - damage_all_outputs(view->server); } diff --git a/src/view.c b/src/view.c index 740ed63f..53065948 100644 --- a/src/view.c +++ b/src/view.c @@ -607,7 +607,6 @@ view_update_title(struct view *view) } ssd_update_title(view); wlr_foreign_toplevel_handle_v1_set_title(view->toplevel_handle, title); - damage_all_outputs(view->server); } void diff --git a/src/xdg.c b/src/xdg.c index 7b433c4a..ddf60bda 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -62,7 +62,6 @@ handle_commit(struct wl_listener *listener, void *data) view->x, view->y); } ssd_update_geometry(view); - damage_view_part(view); } static void @@ -191,7 +190,6 @@ xdg_toplevel_view_configure(struct view *view, struct wlr_box geo) view->x = geo.x; view->y = geo.y; ssd_update_geometry(view); - //damage_all_outputs(view->server); } } #undef MAX diff --git a/src/xwayland-unmanaged.c b/src/xwayland-unmanaged.c index 02c2fdd6..3c37dfd2 100644 --- a/src/xwayland-unmanaged.c +++ b/src/xwayland-unmanaged.c @@ -20,7 +20,6 @@ unmanaged_handle_commit(struct wl_listener *listener, void *data) struct wlr_xwayland_surface *xsurface = unmanaged->xwayland_surface; unmanaged->lx = xsurface->x; unmanaged->ly = xsurface->y; - damage_all_outputs(unmanaged->server); } static struct view * @@ -54,7 +53,6 @@ unmanaged_handle_map(struct wl_listener *listener, void *data) unmanaged->lx = xsurface->x; unmanaged->ly = xsurface->y; - damage_all_outputs(unmanaged->server); if (wlr_xwayland_or_surface_wants_focus(xsurface)) { seat_focus_surface(&unmanaged->server->seat, xsurface->surface); } @@ -81,7 +79,6 @@ unmanaged_handle_unmap(struct wl_listener *listener, void *data) struct xwayland_unmanaged *unmanaged = wl_container_of(listener, unmanaged, unmap); struct wlr_xwayland_surface *xsurface = unmanaged->xwayland_surface; - damage_all_outputs(unmanaged->server); wl_list_remove(&unmanaged->link); wl_list_remove(&unmanaged->commit.link); diff --git a/src/xwayland.c b/src/xwayland.c index f6756e4d..4777b7e6 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -128,7 +128,6 @@ handle_request_configure(struct wl_listener *listener, void *data) wlr_xwayland_surface_configure(view->xwayland_surface, event->x, event->y, MAX(event->width, min_width), MAX(event->height, min_height)); - damage_all_outputs(view->server); } #undef MAX @@ -194,7 +193,6 @@ configure(struct view *view, struct wlr_box geo) wlr_xwayland_surface_configure(view->xwayland_surface, (int16_t)geo.x, (int16_t)geo.y, (uint16_t)geo.width, (uint16_t)geo.height); - damage_all_outputs(view->server); } static void @@ -211,7 +209,6 @@ static void _close(struct view *view) { wlr_xwayland_surface_close(view->xwayland_surface); - damage_all_outputs(view->server); } static const char * From a1d93cc97cb1e204ee3e5854ce10364907151f43 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Wed, 2 Mar 2022 20:21:57 +0000 Subject: [PATCH 38/56] layers: position each layer_tree correctly relative to its output --- src/layers.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/layers.c b/src/layers.c index 3207e4b3..3b177b45 100644 --- a/src/layers.c +++ b/src/layers.c @@ -26,6 +26,14 @@ arrange_layers(struct output *output) &full_area.width, &full_area.height); struct wlr_box usable_area = full_area; + struct server *server = output->server; + struct wlr_scene_output *scene_output = + wlr_scene_get_scene_output(server->scene, output->wlr_output); + if (!scene_output) { + wlr_log(WLR_DEBUG, "no wlr_scene_output"); + return; + } + for (int i = 0; i < LAB_NR_LAYERS; i++) { struct lab_layer_surface *lab_layer_surface; wl_list_for_each(lab_layer_surface, &output->layers[i], link) { @@ -34,6 +42,9 @@ arrange_layers(struct output *output) wlr_scene_layer_surface_v1_configure( scene_layer_surface, &full_area, &usable_area); } + + wlr_scene_node_set_position(&output->layer_tree[i]->node, + scene_output->x, scene_output->y); } memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); From 99361d6ad80a8d1d11fbbdbfa60c03dbd44a4cdb Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Wed, 2 Mar 2022 20:23:20 +0000 Subject: [PATCH 39/56] layers.c: fix coding style --- src/layers.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/layers.c b/src/layers.c index 3b177b45..e17a006e 100644 --- a/src/layers.c +++ b/src/layers.c @@ -54,12 +54,15 @@ arrange_layers(struct output *output) ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, ZWLR_LAYER_SHELL_V1_LAYER_TOP, }; - size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]); + size_t nlayers = sizeof(layers_above_shell) + / sizeof(layers_above_shell[0]); struct lab_layer_surface *layer, *topmost = NULL; for (size_t i = 0; i < nlayers; ++i) { wl_list_for_each_reverse (layer, &output->layers[layers_above_shell[i]], link) { - if (layer->scene_layer_surface->layer_surface->current.keyboard_interactive) { + struct wlr_layer_surface_v1 *layer_surface = + layer->scene_layer_surface->layer_surface; + if (layer_surface->current.keyboard_interactive) { topmost = layer; break; } @@ -318,9 +321,6 @@ new_layer_surface_notify(struct wl_listener *listener, void *data) surface->server = server; surface->scene_layer_surface->layer_surface = layer_surface; -// /* wlr_surface->data needed to find parent in xdg_surface_new() */ -// layer_surface->surface->data = surface->scene_layer_surface->node; - surface->output_destroy.notify = output_destroy_notify; wl_signal_add(&layer_surface->output->events.destroy, &surface->output_destroy); From 04580fa825de7263f9869be4f26de7c236712706 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Wed, 2 Mar 2022 20:25:57 +0000 Subject: [PATCH 40/56] layers: arrange layers on output-layout change --- src/output.c | 12 ++++++------ src/server.c | 5 ++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/output.c b/src/output.c index 61dcc286..e7758d7f 100644 --- a/src/output.c +++ b/src/output.c @@ -156,6 +156,7 @@ output_init(struct server *server) wlr_log(WLR_ERROR, "unable to create output layout"); exit(EXIT_FAILURE); } + wlr_scene_attach_output_layout(server->scene, server->output_layout); /* Enable screen recording with wf-recorder */ wlr_xdg_output_manager_v1_create(server->wl_display, @@ -319,12 +320,11 @@ handle_output_layout_change(struct wl_listener *listener, void *data) } struct output *output; - /* FIXME: Sort this out */ -// wl_list_for_each(output, &server->outputs, link) { -// if (output) { -// arrange_layers(output); -// } -// } + wl_list_for_each(output, &server->outputs, link) { + if (output) { + arrange_layers(output); + } + } output_update_for_layout_change(server); } } diff --git a/src/server.c b/src/server.c index f23d9055..fccde143 100644 --- a/src/server.c +++ b/src/server.c @@ -216,8 +216,6 @@ server_init(struct server *server) wl_list_init(&server->views); wl_list_init(&server->unmanaged_surfaces); - output_init(server); - server->scene = wlr_scene_create(); if (!server->scene) { wlr_log(WLR_ERROR, "unable to create scene"); @@ -229,7 +227,8 @@ server_init(struct server *server) #endif server->menu_tree = wlr_scene_tree_create(&server->scene->node); server->osd_tree = wlr_scene_tree_create(&server->scene->node); - wlr_scene_attach_output_layout(server->scene, server->output_layout); + + output_init(server); /* * Create some hands-off wlroots interfaces. The compositor is From 98d5fd483d68c5ac943a751bd517d83cd0579e48 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Wed, 2 Mar 2022 20:29:29 +0000 Subject: [PATCH 41/56] layers: s/arrange_layers/layers_arrange/ Because we like to start public function names with the name of the file. Move prototype from labwc.h to layers.h --- include/labwc.h | 2 -- include/layers.h | 3 +++ src/layers.c | 8 ++++---- src/output.c | 2 +- src/server.c | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index 8eb48858..7691c9d0 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -470,8 +470,6 @@ void cursor_finish(struct seat *seat); void keyboard_init(struct seat *seat); void keyboard_finish(struct seat *seat); -void arrange_layers(struct output *output); - void seat_init(struct server *server); void seat_finish(struct server *server); void seat_reconfigure(struct server *server); diff --git a/include/layers.h b/include/layers.h index 5db03c1f..033b89fd 100644 --- a/include/layers.h +++ b/include/layers.h @@ -5,6 +5,7 @@ #include struct server; +struct output; #define LAB_NR_LAYERS (4) @@ -35,4 +36,6 @@ struct lab_layer_popup { void layers_init(struct server *server); +void layers_arrange(struct output *output); + #endif /* __LABWC_LAYERS_H */ diff --git a/src/layers.c b/src/layers.c index e17a006e..2cdba567 100644 --- a/src/layers.c +++ b/src/layers.c @@ -19,7 +19,7 @@ #include "node-descriptor.h" void -arrange_layers(struct output *output) +layers_arrange(struct output *output) { struct wlr_box full_area = { 0 }; wlr_output_effective_resolution(output->wlr_output, @@ -110,7 +110,7 @@ surface_commit_notify(struct wl_listener *listener, void *data) layer->mapped = layer_surface->mapped; struct output *output = output_from_wlr_output(layer->server, wlr_output); - arrange_layers(output); + layers_arrange(output); } } @@ -141,7 +141,7 @@ destroy_notify(struct wl_listener *listener, void *data) wl_list_remove(&layer->output_destroy.link); struct output *output = output_from_wlr_output(layer->server, layer->scene_layer_surface->layer_surface->output); - arrange_layers(output); + layers_arrange(output); } free(layer); } @@ -338,7 +338,7 @@ new_layer_surface_notify(struct wl_listener *listener, void *data) */ struct wlr_layer_surface_v1_state old_state = layer_surface->current; layer_surface->current = layer_surface->pending; - arrange_layers(output); + layers_arrange(output); layer_surface->current = old_state; } diff --git a/src/output.c b/src/output.c index e7758d7f..215a2611 100644 --- a/src/output.c +++ b/src/output.c @@ -322,7 +322,7 @@ handle_output_layout_change(struct wl_listener *listener, void *data) wl_list_for_each(output, &server->outputs, link) { if (output) { - arrange_layers(output); + layers_arrange(output); } } output_update_for_layout_change(server); diff --git a/src/server.c b/src/server.c index fccde143..a5fd3550 100644 --- a/src/server.c +++ b/src/server.c @@ -120,7 +120,7 @@ seat_disinhibit_input(struct seat *seat) */ struct output *output; wl_list_for_each(output, &seat->server->outputs, link) { - arrange_layers(output); + layers_arrange(output); } } From d3918eec0a27b934cb7998e0e410349531c1d2bd Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Wed, 2 Mar 2022 20:39:46 +0000 Subject: [PATCH 42/56] layers: only define LAB_NR_LAYERS locally where needed --- include/labwc.h | 6 ++++-- include/layers.h | 2 -- src/layers.c | 3 ++- src/output.c | 3 ++- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index 7691c9d0..8f5aa989 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -188,13 +188,14 @@ struct server { struct menu *menu_current; }; +#define LAB_NR_LAYERS (4) struct output { struct wl_list link; /* server::outputs */ struct server *server; struct wlr_output *wlr_output; struct wlr_scene_output *scene_output; - struct wl_list layers[4]; - struct wlr_scene_tree *layer_tree[4]; + struct wl_list layers[LAB_NR_LAYERS]; + struct wlr_scene_tree *layer_tree[LAB_NR_LAYERS]; struct wlr_box usable_area; struct lab_data_buffer *osd_buffer; @@ -202,6 +203,7 @@ struct output { struct wl_listener destroy; struct wl_listener frame; }; +#undef LAB_NR_LAYERS enum view_type { LAB_XDG_SHELL_VIEW, diff --git a/include/layers.h b/include/layers.h index 033b89fd..f7d3f2b2 100644 --- a/include/layers.h +++ b/include/layers.h @@ -7,8 +7,6 @@ struct server; struct output; -#define LAB_NR_LAYERS (4) - struct lab_layer_surface { struct wl_list link; /* output::layers */ struct wlr_scene_layer_surface_v1 *scene_layer_surface; diff --git a/src/layers.c b/src/layers.c index 2cdba567..1d6b2b6d 100644 --- a/src/layers.c +++ b/src/layers.c @@ -34,7 +34,8 @@ layers_arrange(struct output *output) return; } - for (int i = 0; i < LAB_NR_LAYERS; i++) { + int nr_layers = sizeof(output->layers) / sizeof(output->layers[0]); + for (int i = 0; i < nr_layers; i++) { struct lab_layer_surface *lab_layer_surface; wl_list_for_each(lab_layer_surface, &output->layers[i], link) { struct wlr_scene_layer_surface_v1 *scene_layer_surface = diff --git a/src/output.c b/src/output.c index 215a2611..c9981af3 100644 --- a/src/output.c +++ b/src/output.c @@ -111,7 +111,8 @@ new_output_notify(struct wl_listener *listener, void *data) output->frame.notify = output_frame_notify; wl_signal_add(&wlr_output->events.frame, &output->frame); - for (int i = 0; i < LAB_NR_LAYERS; i++) { + int nr_layers = sizeof(output->layers) / sizeof(output->layers[0]); + for (int i = 0; i < nr_layers; i++) { wl_list_init(&output->layers[i]); output->layer_tree[i] = wlr_scene_tree_create(&server->scene->node); From db9a808dca3a48a5eb9f12f728048bc6b7e67171 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Wed, 2 Mar 2022 21:26:33 +0100 Subject: [PATCH 43/56] debug.c: use node descriptors --- src/debug.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/debug.c b/src/debug.c index 7d95953e..c493cc79 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1,8 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-only +#include #include #include #include "buffer.h" #include "labwc.h" +#include "node-descriptor.h" #define HEADER_CHARS "------------------------------" @@ -111,7 +113,10 @@ get_special(struct server *server, struct wlr_scene_node *node, return "server->view_tree"; } if (node->parent == &server->view_tree->node) { - *last_view = node->data; + struct node_descriptor *desc = node->data; + assert(desc->type == LAB_NODE_DESC_VIEW + || desc->type == LAB_NODE_DESC_XDG_POPUP); + *last_view = desc->data; } const char *view_part = get_view_part(*last_view, node); if (view_part) { From 51e02e2afa27a0bc29cc856d263867801aa71567 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Wed, 2 Mar 2022 21:28:44 +0100 Subject: [PATCH 44/56] src/desktop.c: Fix label error Fixes "a label can only be part of a statement and a declaration is not a statement". --- src/desktop.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/desktop.c b/src/desktop.c index c1745f81..78c19a63 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -315,9 +315,11 @@ desktop_node_and_view_at(struct server *server, double lx, double ly, *view_area = LAB_SSD_NONE; return NULL; +struct view *view; +struct node_descriptor *desc; has_view_data: - struct node_descriptor *desc = node->data; - struct view *view = desc->data; + desc = node->data; + view = desc->data; *view_area = ssd_get_part_type(view, *scene_node); return view; } From 30298228e3a58d37dc9f9e2ca009d172d7ef9cea Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Wed, 2 Mar 2022 21:07:04 +0000 Subject: [PATCH 45/56] Rename node-descriptor.c to node.c ...in anticipation of adding helpers like node_view_from_node() --- include/{node-descriptor.h => node.h} | 0 src/debug.c | 2 +- src/desktop.c | 2 +- src/layers.c | 2 +- src/meson.build | 2 +- src/{node-descriptor.c => node.c} | 2 +- src/xdg-popup.c | 2 +- src/xdg.c | 2 +- src/xwayland.c | 2 +- 9 files changed, 8 insertions(+), 8 deletions(-) rename include/{node-descriptor.h => node.h} (100%) rename src/{node-descriptor.c => node.c} (96%) diff --git a/include/node-descriptor.h b/include/node.h similarity index 100% rename from include/node-descriptor.h rename to include/node.h diff --git a/src/debug.c b/src/debug.c index c493cc79..0d167fa6 100644 --- a/src/debug.c +++ b/src/debug.c @@ -4,7 +4,7 @@ #include #include "buffer.h" #include "labwc.h" -#include "node-descriptor.h" +#include "node.h" #define HEADER_CHARS "------------------------------" diff --git a/src/desktop.c b/src/desktop.c index 78c19a63..8c1c7401 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -3,7 +3,7 @@ #include #include "labwc.h" #include "layers.h" -#include "node-descriptor.h" +#include "node.h" #include "ssd.h" static void diff --git a/src/layers.c b/src/layers.c index 1d6b2b6d..ec7f6c6e 100644 --- a/src/layers.c +++ b/src/layers.c @@ -16,7 +16,7 @@ #include #include "layers.h" #include "labwc.h" -#include "node-descriptor.h" +#include "node.h" void layers_arrange(struct output *output) diff --git a/src/meson.build b/src/meson.build index 38d4803d..fd918939 100644 --- a/src/meson.build +++ b/src/meson.build @@ -10,7 +10,7 @@ labwc_sources = files( 'key-state.c', 'layers.c', 'main.c', - 'node-descriptor.c', + 'node.c', 'osd.c', 'output.c', 'resistance.c', diff --git a/src/node-descriptor.c b/src/node.c similarity index 96% rename from src/node-descriptor.c rename to src/node.c index efbcb048..8f7095c5 100644 --- a/src/node-descriptor.c +++ b/src/node.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #include -#include "node-descriptor.h" +#include "node.h" static void descriptor_destroy(struct node_descriptor *node_descriptor) diff --git a/src/xdg-popup.c b/src/xdg-popup.c index 239a1332..aea05f4d 100644 --- a/src/xdg-popup.c +++ b/src/xdg-popup.c @@ -8,7 +8,7 @@ */ #include "labwc.h" -#include "node-descriptor.h" +#include "node.h" struct view_child { struct wlr_surface *surface; diff --git a/src/xdg.c b/src/xdg.c index ddf60bda..6ff718fe 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #include #include "labwc.h" -#include "node-descriptor.h" +#include "node.h" #include "ssd.h" static void diff --git a/src/xwayland.c b/src/xwayland.c index 4777b7e6..7374a819 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #include #include "labwc.h" -#include "node-descriptor.h" +#include "node.h" #include "ssd.h" static void From b4cbc20e8c50c5f553272780cf5484c90c869fa0 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Wed, 2 Mar 2022 22:05:41 +0000 Subject: [PATCH 46/56] node.c: add helpers for returning structs from node data Support returning the following structs: - view from LAB_NODE_DESC_VIEW or LAB_NODE_DESC_XDG_POPUP - lab_layer_surface form LAB_NODE_DESC_LAYER_SURFACE - lab_layer_popup from LAB_NODE_DESC_LAYER_POPUP --- include/node.h | 44 +++++++++++++++++++++++++++++++++++++++----- src/debug.c | 6 +----- src/node.c | 29 ++++++++++++++++++++++++++--- 3 files changed, 66 insertions(+), 13 deletions(-) diff --git a/include/node.h b/include/node.h index d5225af9..ab222b8d 100644 --- a/include/node.h +++ b/include/node.h @@ -3,12 +3,16 @@ #define __LABWC_NODE_DESCRIPTOR_H #include +struct view; +struct lab_layer_surface; +struct lab_layer_popup; + enum node_descriptor_type { LAB_NODE_DESC_NODE = 0, - LAB_NODE_DESC_VIEW, /* *data --> struct view */ - LAB_NODE_DESC_XDG_POPUP, /* *data --> struct view */ - LAB_NODE_DESC_LAYER_SURFACE, /* *data --> struct lab_layer_surface */ - LAB_NODE_DESC_LAYER_POPUP, /* *data --> struct lab_layer_popup */ + LAB_NODE_DESC_VIEW, + LAB_NODE_DESC_XDG_POPUP, + LAB_NODE_DESC_LAYER_SURFACE, + LAB_NODE_DESC_LAYER_POPUP, }; struct node_descriptor { @@ -17,7 +21,37 @@ struct node_descriptor { struct wl_listener destroy; }; -void node_descriptor_create(struct wlr_scene_node *node, +/** + * node_descriptor_create - create node descriptor for wlr_scene_node user_data + * @scene_node: wlr_scene_node to attached node_descriptor to + * @type: node descriptor type + * @data: struct to point to as follows: + * - LAB_NODE_DESC_VIEW struct view + * - LAB_NODE_DESC_XDG_POPUP struct view + * - LAB_NODE_DESC_LAYER_SURFACE struct lab_layer_surface + * - LAB_NODE_DESC_LAYER_POPUP struct lab_layer_popup + */ +void node_descriptor_create(struct wlr_scene_node *scene_node, enum node_descriptor_type type, void *data); +/** + * node_view_from_node - return view struct from node + * @node_descriptor: node_descriptor from which to return data + */ +struct view *node_view_from_node(struct node_descriptor *node_descriptor); + +/** + * node_lab_surface_from_node - return lab_layer_surface struct from node + * @node_descriptor: node_descriptor from which to return data + */ +struct lab_layer_surface *node_layer_surface_from_node( + struct node_descriptor *node_descriptor); + +/** + * node_layer_popup_from_node - return lab_layer_popup struct from node + * @node_descriptor: node_descriptor from which to return data + */ +struct lab_layer_popup *node_layer_popup_from_node( + struct node_descriptor *node_descriptor); + #endif /* __LABWC_NODE_DESCRIPTOR_H */ diff --git a/src/debug.c b/src/debug.c index 0d167fa6..0e68adb0 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0-only -#include #include #include #include "buffer.h" @@ -113,10 +112,7 @@ get_special(struct server *server, struct wlr_scene_node *node, return "server->view_tree"; } if (node->parent == &server->view_tree->node) { - struct node_descriptor *desc = node->data; - assert(desc->type == LAB_NODE_DESC_VIEW - || desc->type == LAB_NODE_DESC_XDG_POPUP); - *last_view = desc->data; + *last_view = node_view_from_node(node->data); } const char *view_part = get_view_part(*last_view, node); if (view_part) { diff --git a/src/node.c b/src/node.c index 8f7095c5..bd656ea4 100644 --- a/src/node.c +++ b/src/node.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +#include #include #include "node.h" @@ -21,7 +22,7 @@ destroy_notify(struct wl_listener *listener, void *data) } void -node_descriptor_create(struct wlr_scene_node *node, +node_descriptor_create(struct wlr_scene_node *scene_node, enum node_descriptor_type type, void *data) { struct node_descriptor *node_descriptor = @@ -32,6 +33,28 @@ node_descriptor_create(struct wlr_scene_node *node, node_descriptor->type = type; node_descriptor->data = data; node_descriptor->destroy.notify = destroy_notify; - wl_signal_add(&node->events.destroy, &node_descriptor->destroy); - node->data = node_descriptor; + wl_signal_add(&scene_node->events.destroy, &node_descriptor->destroy); + scene_node->data = node_descriptor; +} + +struct view * +node_view_from_node(struct node_descriptor *node_descriptor) +{ + assert(node_descriptor->type == LAB_NODE_DESC_VIEW + || node_descriptor->type == LAB_NODE_DESC_XDG_POPUP); + return (struct view *)node_descriptor->data; +} + +struct lab_layer_surface * +node_layer_surface_from_node(struct node_descriptor *node_descriptor) +{ + assert(node_descriptor->type == LAB_NODE_DESC_LAYER_SURFACE); + return (struct lab_layer_surface *)node_descriptor->data; +} + +struct lab_layer_popup * +node_layer_popup_from_node(struct node_descriptor *node_descriptor) +{ + assert(node_descriptor->type == LAB_NODE_DESC_LAYER_POPUP); + return (struct lab_layer_popup *)node_descriptor->data; } From eb7645d0e0531a789290e7b0741330427afbd2ba Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Wed, 2 Mar 2022 22:24:30 +0000 Subject: [PATCH 47/56] node.c: use wlr_scene_node instead of node_descriptor --- include/node.h | 12 ++++++------ src/debug.c | 2 +- src/node.c | 12 +++++++++--- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/include/node.h b/include/node.h index ab222b8d..b1fffb9a 100644 --- a/include/node.h +++ b/include/node.h @@ -36,22 +36,22 @@ void node_descriptor_create(struct wlr_scene_node *scene_node, /** * node_view_from_node - return view struct from node - * @node_descriptor: node_descriptor from which to return data + * @wlr_scene_node: wlr_scene_node from which to return data */ -struct view *node_view_from_node(struct node_descriptor *node_descriptor); +struct view *node_view_from_node(struct wlr_scene_node *wlr_scene_node); /** * node_lab_surface_from_node - return lab_layer_surface struct from node - * @node_descriptor: node_descriptor from which to return data + * @wlr_scene_node: wlr_scene_node from which to return data */ struct lab_layer_surface *node_layer_surface_from_node( - struct node_descriptor *node_descriptor); + struct wlr_scene_node *wlr_scene_node); /** * node_layer_popup_from_node - return lab_layer_popup struct from node - * @node_descriptor: node_descriptor from which to return data + * @wlr_scene_node: wlr_scene_node from which to return data */ struct lab_layer_popup *node_layer_popup_from_node( - struct node_descriptor *node_descriptor); + struct wlr_scene_node *wlr_scene_node); #endif /* __LABWC_NODE_DESCRIPTOR_H */ diff --git a/src/debug.c b/src/debug.c index 0e68adb0..68dcbc2a 100644 --- a/src/debug.c +++ b/src/debug.c @@ -112,7 +112,7 @@ get_special(struct server *server, struct wlr_scene_node *node, return "server->view_tree"; } if (node->parent == &server->view_tree->node) { - *last_view = node_view_from_node(node->data); + *last_view = node_view_from_node(node); } const char *view_part = get_view_part(*last_view, node); if (view_part) { diff --git a/src/node.c b/src/node.c index bd656ea4..c189358a 100644 --- a/src/node.c +++ b/src/node.c @@ -38,23 +38,29 @@ node_descriptor_create(struct wlr_scene_node *scene_node, } struct view * -node_view_from_node(struct node_descriptor *node_descriptor) +node_view_from_node(struct wlr_scene_node *wlr_scene_node) { + assert(wlr_scene_node->data); + struct node_descriptor *node_descriptor = wlr_scene_node->data; assert(node_descriptor->type == LAB_NODE_DESC_VIEW || node_descriptor->type == LAB_NODE_DESC_XDG_POPUP); return (struct view *)node_descriptor->data; } struct lab_layer_surface * -node_layer_surface_from_node(struct node_descriptor *node_descriptor) +node_layer_surface_from_node(struct wlr_scene_node *wlr_scene_node) { + assert(wlr_scene_node->data); + struct node_descriptor *node_descriptor = wlr_scene_node->data; assert(node_descriptor->type == LAB_NODE_DESC_LAYER_SURFACE); return (struct lab_layer_surface *)node_descriptor->data; } struct lab_layer_popup * -node_layer_popup_from_node(struct node_descriptor *node_descriptor) +node_layer_popup_from_node(struct wlr_scene_node *wlr_scene_node) { + assert(wlr_scene_node->data); + struct node_descriptor *node_descriptor = wlr_scene_node->data; assert(node_descriptor->type == LAB_NODE_DESC_LAYER_POPUP); return (struct lab_layer_popup *)node_descriptor->data; } From 8444ba4075c0e80ec81aab5ad0af2ba38cff6bdb Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Thu, 3 Mar 2022 04:33:33 +0100 Subject: [PATCH 48/56] Convert menu to node_descriptors --- include/menu/menu.h | 15 +++++--- include/node.h | 9 +++++ src/cursor.c | 6 +-- src/desktop.c | 11 ++++-- src/menu/menu.c | 93 ++++++++++++++++++--------------------------- src/node.c | 9 +++++ 6 files changed, 75 insertions(+), 68 deletions(-) diff --git a/include/menu/menu.h b/include/menu/menu.h index a5ec7dd8..777bc6af 100644 --- a/include/menu/menu.h +++ b/include/menu/menu.h @@ -23,6 +23,7 @@ struct menu_scene { struct menuitem { struct wl_list actions; + struct menu *parent; struct menu *submenu; struct menu_scene normal; struct menu_scene selected; @@ -75,15 +76,19 @@ void menu_open(struct menu *menu, int x, int y); * - handles hover effects * - may open/close submenus */ -void menu_process_cursor_motion(struct menu *menu, struct wlr_scene_node *node); +void menu_process_cursor_motion(struct wlr_scene_node *node); /** - * menu_call_actions - call actions associated with a menu entry + * menu_call_actions - call actions associated with a menu node * - * If actions are found, server->menu_current will be closed and set to NULL - * Returns true if handled + * If menuitem connected to @node does not just open a submenu: + * - associated actions will be called + * - server->menu_current will be closed + * - server->menu_current will be set to NULL + * + * Returns true if actions have actually been executed */ -bool menu_call_actions(struct menu *menu, struct wlr_scene_node *node); +bool menu_call_actions(struct wlr_scene_node *node); /* menu_close - close menu */ void menu_close(struct menu *menu); diff --git a/include/node.h b/include/node.h index b1fffb9a..5cc7220e 100644 --- a/include/node.h +++ b/include/node.h @@ -6,6 +6,7 @@ struct view; struct lab_layer_surface; struct lab_layer_popup; +struct menuitem; enum node_descriptor_type { LAB_NODE_DESC_NODE = 0, @@ -13,6 +14,7 @@ enum node_descriptor_type { LAB_NODE_DESC_XDG_POPUP, LAB_NODE_DESC_LAYER_SURFACE, LAB_NODE_DESC_LAYER_POPUP, + LAB_NODE_DESC_MENUITEM, }; struct node_descriptor { @@ -54,4 +56,11 @@ struct lab_layer_surface *node_layer_surface_from_node( struct lab_layer_popup *node_layer_popup_from_node( struct wlr_scene_node *wlr_scene_node); +/** + * node_menuitem_from_node - return menuitem struct from node + * @wlr_scene_node: wlr_scene_node from which to return data + */ +struct menuitem *node_menuitem_from_node( + struct wlr_scene_node *wlr_scene_node); + #endif /* __LABWC_NODE_DESCRIPTOR_H */ diff --git a/src/cursor.c b/src/cursor.c index f6716cb7..7aa32d35 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -233,7 +233,7 @@ process_cursor_motion(struct server *server, uint32_t time) } if (view_area == LAB_SSD_MENU) { - menu_process_cursor_motion(server->menu_current, node); + menu_process_cursor_motion(node); return; } @@ -677,8 +677,8 @@ cursor_button(struct wl_listener *listener, void *data) if (view_area != LAB_SSD_MENU) { /* We close the menu on release so we don't leak a stray release */ close_menu = true; - } else if (menu_call_actions(server->menu_current, node)) { - /* Action was successfull, may fail if item contains a submenu */ + } else if (menu_call_actions(node)) { + /* Action was successfull, may fail if item just opens a submenu */ close_menu = true; } return; diff --git a/src/desktop.c b/src/desktop.c index 8c1c7401..d8768de5 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -284,9 +284,9 @@ desktop_node_and_view_at(struct server *server, double lx, double ly, #endif } struct wlr_scene_node *osd = &server->osd_tree->node; - struct wlr_scene_node *menu = &server->menu_tree->node; while (node) { struct node_descriptor *desc = node->data; + /* TODO: convert to switch() */ if (desc) { if (desc->type == LAB_NODE_DESC_VIEW) { goto has_view_data; @@ -299,13 +299,16 @@ desktop_node_and_view_at(struct server *server, double lx, double ly, *view_area = LAB_SSD_CLIENT; return NULL; } + if (desc->type == LAB_NODE_DESC_MENUITEM) { + /* Always return the top scene node for menu items */ + *scene_node = node; + *view_area = LAB_SSD_MENU; + return NULL; + } } if (node == osd) { *view_area = LAB_SSD_OSD; return NULL; - } else if (node == menu) { - *view_area = LAB_SSD_MENU; - return NULL; } node = node->parent; } diff --git a/src/menu/menu.c b/src/menu/menu.c index ff9a8b6a..2f34d4e3 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -20,6 +20,7 @@ #include "theme.h" #include "action.h" #include "buffer.h" +#include "node.h" #define MENUWIDTH (110) #define MENU_ITEM_PADDING_Y (4) @@ -82,6 +83,7 @@ item_create(struct menu *menu, const char *text) if (!menuitem) { return NULL; } + menuitem->parent = menu; struct server *server = menu->server; struct theme *theme = server->theme; struct font font = { @@ -117,6 +119,12 @@ item_create(struct menu *menu, const char *text) menuitem->selected.text = &wlr_scene_buffer_create( menuitem->selected.background, &menuitem->selected.buffer->base)->node; + /* Node descriptors for top scene nodes of menuitem */ + node_descriptor_create(menuitem->normal.background, + LAB_NODE_DESC_MENUITEM, menuitem); + node_descriptor_create(menuitem->selected.background, + LAB_NODE_DESC_MENUITEM, menuitem); + /* Center font nodes */ y = (menu->item_height - menuitem->normal.buffer->base.height) / 2; x = MENU_ITEM_PADDING_X; @@ -132,7 +140,7 @@ item_create(struct menu *menu, const char *text) /* Hide selected state */ wlr_scene_node_set_enabled(menuitem->selected.background, false); - /* Update menu extends */ + /* Update menu extents */ menu->size.height = (item_count + 1) * menu->item_height; wl_list_insert(&menu->menuitems, &menuitem->link); @@ -516,74 +524,47 @@ menu_open(struct menu *menu, int x, int y) } void -menu_process_cursor_motion(struct menu *menu, struct wlr_scene_node *node) +menu_process_cursor_motion(struct wlr_scene_node *node) { - if (!node) { - wlr_log(WLR_ERROR, "%s() node == NULL", __func__); + assert(node && node->data); + struct menuitem *item = node_menuitem_from_node(node); + + if (node == item->selected.background) { + /* We are on an already selected item */ return; } - assert(menu); - /* TODO: this would be much easier if we could use node->data */ - - struct menuitem *item; - wl_list_for_each (item, &menu->menuitems, link) { - if (node == item->selected.background - || node->parent == item->selected.background) { - /* We are on an already selected item */ - return; - } - if (node == item->normal.background - || node->parent == item->normal.background) { - /* We are on an item that has new mouse-focus */ - menu_set_selection(menu, item); - if (menu->selection.menu) { - /* Close old submenu tree */ - menu_close(menu->selection.menu); - } - if (item->submenu) { - /* And open the new one */ - wlr_scene_node_set_enabled( - &item->submenu->scene_tree->node, true); - } - menu->selection.menu = item->submenu; - return; - } - if (item->submenu && item->submenu == menu->selection.menu) { - menu_process_cursor_motion(item->submenu, node); - } + /* We are on an item that has new mouse-focus */ + menu_set_selection(item->parent, item); + if (item->parent->selection.menu) { + /* Close old submenu tree */ + menu_close(item->parent->selection.menu); } + + if (item->submenu) { + /* And open the new one */ + wlr_scene_node_set_enabled( + &item->submenu->scene_tree->node, true); + } + item->parent->selection.menu = item->submenu; } bool -menu_call_actions(struct menu *menu, struct wlr_scene_node *node) +menu_call_actions(struct wlr_scene_node *node) { - /* TODO: this would be much easier if we could use node->data */ + assert(node && node->data); + struct menuitem *item = node_menuitem_from_node(node); - if (!menu->selection.item) { - /* No item selected in current menu */ - wlr_log(WLR_ERROR, "No item on menu_action_selected"); + if (item->submenu) { + /* We received a click on an item that just opens a submenu */ return false; } - struct wlr_scene_node *menu_node = - menu->selection.item->selected.background; - if (node == menu_node || node->parent == menu_node) { - /* We found the correct menu item */ - if (menu->selection.item->submenu) { - /* ..but it just opens a submenu */ - return false; - } - actions_run(NULL, menu->server, &menu->selection.item->actions, 0); - menu_close(menu->server->menu_current); - menu->server->menu_current = NULL; - return true; - } - if (menu->selection.menu) { - return menu_call_actions(menu->selection.menu, node); - } - wlr_log(WLR_ERROR, "No match on menu_action_selected"); - return false; + + actions_run(NULL, item->parent->server, &item->actions, 0); + menu_close(item->parent->server->menu_current); + item->parent->server->menu_current = NULL; + return true; } void diff --git a/src/node.c b/src/node.c index c189358a..f667d82a 100644 --- a/src/node.c +++ b/src/node.c @@ -64,3 +64,12 @@ node_layer_popup_from_node(struct wlr_scene_node *wlr_scene_node) assert(node_descriptor->type == LAB_NODE_DESC_LAYER_POPUP); return (struct lab_layer_popup *)node_descriptor->data; } + +struct menuitem * +node_menuitem_from_node(struct wlr_scene_node *wlr_scene_node) +{ + assert(wlr_scene_node->data); + struct node_descriptor *node_descriptor = wlr_scene_node->data; + assert(node_descriptor->type == LAB_NODE_DESC_MENUITEM); + return (struct menuitem *)node_descriptor->data; +} From 34bd1fb6da7a35cfd96f39f087484c47802b33ff Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Thu, 3 Mar 2022 17:53:26 +0000 Subject: [PATCH 49/56] layers: unconstrain popups correctly ...to show popups when using @Consolatis's example panel https://github.com/Consolatis/wl_framework --- src/layers.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/layers.c b/src/layers.c index ec7f6c6e..6f1d5292 100644 --- a/src/layers.c +++ b/src/layers.c @@ -202,20 +202,22 @@ popup_unconstrain(struct lab_layer_popup *popup) layer->scene_layer_surface->layer_surface->output->data; struct wlr_box output_box = { 0 }; - wlr_output_effective_resolution(output->wlr_output, &output_box.width, - &output_box.height); + wlr_output_layout_get_box(output->server->output_layout, + output->wlr_output, &output_box); + + int lx, ly; + wlr_scene_node_coords(popup->scene_node, &lx, &ly); /* * Output geometry expressed in the coordinate system of the toplevel * parent of popup */ struct wlr_box output_toplevel_sx_box = { - .x = -layer->geo.x, - .y = -layer->geo.y, + .x = output_box.x - lx, + .y = output_box.y - ly, .width = output_box.width, .height = output_box.height, }; - wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); } From 6fd400b493ff7819761c782c589808340f8adc9d Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Thu, 3 Mar 2022 17:56:38 +0000 Subject: [PATCH 50/56] layers: put popups in separate node-tree This allows popups to be rendered above views and other layers. Without this, the popups of a layer-shell application in the bottom layer would render below views, which does not seem right. For example, consider the case of a panel with right-click popups. --- include/labwc.h | 1 + src/layers.c | 25 ++++++++++++++++++------- src/output.c | 17 +++++++++++++++++ 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index 8f5aa989..ee5d72d0 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -196,6 +196,7 @@ struct output { struct wlr_scene_output *scene_output; struct wl_list layers[LAB_NR_LAYERS]; struct wlr_scene_tree *layer_tree[LAB_NR_LAYERS]; + struct wlr_scene_tree *layer_popup_tree; struct wlr_box usable_area; struct lab_data_buffer *osd_buffer; diff --git a/src/layers.c b/src/layers.c index 6f1d5292..060d5cca 100644 --- a/src/layers.c +++ b/src/layers.c @@ -247,12 +247,6 @@ create_popup(struct wlr_xdg_popup *wlr_popup, struct wlr_scene_node *parent) popup->new_popup.notify = popup_handle_new_popup; wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); - /* - * FIXME: should we put popups in ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY - * or a dedicated output->layer_popup_tree - so that for example - * a panel in the bottom layer displays any popup above views. - */ - popup_unconstrain(popup); return popup; } @@ -272,7 +266,24 @@ new_popup_notify(struct wl_listener *listener, void *data) struct lab_layer_surface *lab_layer_surface = wl_container_of(listener, lab_layer_surface, new_popup); struct wlr_xdg_popup *wlr_popup = data; - create_popup(wlr_popup, lab_layer_surface->scene_layer_surface->node); + + /* + * We always put popups in a special popup_tree so that for example a + * panel in the bottom layer displays popups above views. + */ + struct server *server = lab_layer_surface->server; + struct wlr_output *wlr_output = + lab_layer_surface->scene_layer_surface->layer_surface->output; + struct output *output = output_from_wlr_output(server, wlr_output); + struct wlr_scene_node *node = &output->layer_popup_tree->node; + + create_popup(wlr_popup, node); + + struct wlr_box box = { 0 }; + wlr_output_layout_get_box(server->output_layout, wlr_output, &box); + box.x += lab_layer_surface->scene_layer_surface->node->state.x; + box.y += lab_layer_surface->scene_layer_surface->node->state.y; + wlr_scene_node_set_position(node, box.x, box.y); } static void diff --git a/src/output.c b/src/output.c index c9981af3..fbd629a1 100644 --- a/src/output.c +++ b/src/output.c @@ -111,6 +111,10 @@ new_output_notify(struct wl_listener *listener, void *data) output->frame.notify = output_frame_notify; wl_signal_add(&wlr_output->events.frame, &output->frame); + /* + * Create layer-trees (background, bottom, top and overlay) and + * a layer-popup-tree. + */ int nr_layers = sizeof(output->layers) / sizeof(output->layers[0]); for (int i = 0; i < nr_layers; i++) { wl_list_init(&output->layers[i]); @@ -118,10 +122,23 @@ new_output_notify(struct wl_listener *listener, void *data) wlr_scene_tree_create(&server->scene->node); output->layer_tree[i]->node.data = output->wlr_output; } + output->layer_popup_tree = wlr_scene_tree_create(&server->scene->node); + + /* + * Set the z-positions to achieve the following order (from top to + * bottom): + * - layer-shell popups + * - overlay layer + * - top layer + * - views + * - bottom layer + * - background layer + */ wlr_scene_node_lower_to_bottom(&output->layer_tree[1]->node); wlr_scene_node_lower_to_bottom(&output->layer_tree[0]->node); wlr_scene_node_raise_to_top(&output->layer_tree[2]->node); wlr_scene_node_raise_to_top(&output->layer_tree[3]->node); + wlr_scene_node_raise_to_top(&output->layer_popup_tree->node); if (rc.adaptive_sync) { wlr_log(WLR_INFO, "enable adaptive sync on %s", From 19f3a6ef83686dd573b47ed8d2428ddaff7db1fe Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Thu, 3 Mar 2022 18:20:16 +0000 Subject: [PATCH 51/56] output: attach node-descriptors to node-trees --- include/node.h | 1 + src/output.c | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/include/node.h b/include/node.h index 5cc7220e..5704761c 100644 --- a/include/node.h +++ b/include/node.h @@ -15,6 +15,7 @@ enum node_descriptor_type { LAB_NODE_DESC_LAYER_SURFACE, LAB_NODE_DESC_LAYER_POPUP, LAB_NODE_DESC_MENUITEM, + LAB_NODE_DESC_TREE, }; struct node_descriptor { diff --git a/src/output.c b/src/output.c index fbd629a1..d24f422b 100644 --- a/src/output.c +++ b/src/output.c @@ -19,6 +19,7 @@ #include "labwc.h" #include "layers.h" #include "menu/menu.h" +#include "node.h" #include "ssd.h" #include "theme.h" @@ -120,9 +121,12 @@ new_output_notify(struct wl_listener *listener, void *data) wl_list_init(&output->layers[i]); output->layer_tree[i] = wlr_scene_tree_create(&server->scene->node); - output->layer_tree[i]->node.data = output->wlr_output; + node_descriptor_create(&output->layer_tree[i]->node, + LAB_NODE_DESC_TREE, NULL); } output->layer_popup_tree = wlr_scene_tree_create(&server->scene->node); + node_descriptor_create(&output->layer_popup_tree->node, + LAB_NODE_DESC_TREE, NULL); /* * Set the z-positions to achieve the following order (from top to From 5deeca833fbdb6fb8e8bf1a7443c0c28861b838b Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Fri, 4 Mar 2022 04:38:06 +0100 Subject: [PATCH 52/56] ssd: Disable resize extents while in maximized or fullscreen state --- src/ssd/ssd_extents.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/ssd/ssd_extents.c b/src/ssd/ssd_extents.c index d72b5df0..b80753c4 100644 --- a/src/ssd/ssd_extents.c +++ b/src/ssd/ssd_extents.c @@ -17,13 +17,14 @@ ssd_extents_create(struct view *view) 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; + view->ssd.extents.tree = wlr_scene_tree_create(&view->ssd.tree->node); + struct wlr_scene_node *parent = &view->ssd.extents.tree->node; + if (view->maximized || view->fullscreen) { + wlr_scene_node_set_enabled(parent, false); + } + wl_list_init(&view->ssd.extents.parts); 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, @@ -59,6 +60,14 @@ ssd_extents_create(struct view *view) void ssd_extents_update(struct view *view) { + if (view->maximized || view->fullscreen) { + wlr_scene_node_set_enabled(&view->ssd.extents.tree->node, false); + return; + } + if (!view->ssd.extents.tree->node.state.enabled) { + wlr_scene_node_set_enabled(&view->ssd.extents.tree->node, true); + } + struct theme *theme = view->server->theme; int width = view->w; From 77ac3474b629766f2a2e259d4895a0733bc78729 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Fri, 4 Mar 2022 20:25:04 +0000 Subject: [PATCH 53/56] buffer.c: chase wlroots master Include https://gitlab.freedesktop.org/wlroots/wlroots/-/commit/39b68ea47a661b1f7562ce283652de08f222b2be --- src/buffer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/buffer.c b/src/buffer.c index 2415d509..44891005 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "buffer.h" static const struct wlr_buffer_impl data_buffer_impl; From 852a6f02facfaea0871ba375cb42e5c7bebca944 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sun, 6 Mar 2022 16:48:39 +0000 Subject: [PATCH 54/56] layers: fix layer_popup_tree implementation --- include/layers.h | 3 ++ src/layers.c | 127 +++++++++++++++++++++++------------------------ 2 files changed, 64 insertions(+), 66 deletions(-) diff --git a/include/layers.h b/include/layers.h index f7d3f2b2..b40c0cac 100644 --- a/include/layers.h +++ b/include/layers.h @@ -28,6 +28,9 @@ struct lab_layer_popup { struct wlr_xdg_popup *wlr_popup; struct wlr_scene_node *scene_node; + /* To simplify moving popup nodes from the bottom to the top layer */ + struct wlr_box output_toplevel_sx_box; + struct wl_listener destroy; struct wl_listener new_popup; }; diff --git a/src/layers.c b/src/layers.c index 060d5cca..2112b48b 100644 --- a/src/layers.c +++ b/src/layers.c @@ -164,22 +164,6 @@ map_notify(struct wl_listener *listener, void *data) wlr_surface_send_enter(layer_surface->surface, layer_surface->output); } -static struct -lab_layer_surface *popup_get_layer(struct lab_layer_popup *popup) -{ - struct wlr_scene_node *node = popup->scene_node; - while (node) { - if (node->data) { - struct node_descriptor *desc = node->data; - if (desc->type == LAB_NODE_DESC_LAYER_SURFACE) { - return desc->data; - } - } - node = node->parent; - } - return NULL; -} - static void popup_handle_destroy(struct wl_listener *listener, void *data) { @@ -190,41 +174,11 @@ popup_handle_destroy(struct wl_listener *listener, void *data) free(popup); } -static void -popup_unconstrain(struct lab_layer_popup *popup) -{ - struct lab_layer_surface *layer = popup_get_layer(popup); - if (!layer) { - return; - } - struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; - struct output *output = - layer->scene_layer_surface->layer_surface->output->data; - - struct wlr_box output_box = { 0 }; - wlr_output_layout_get_box(output->server->output_layout, - output->wlr_output, &output_box); - - int lx, ly; - wlr_scene_node_coords(popup->scene_node, &lx, &ly); - - /* - * Output geometry expressed in the coordinate system of the toplevel - * parent of popup - */ - struct wlr_box output_toplevel_sx_box = { - .x = output_box.x - lx, - .y = output_box.y - ly, - .width = output_box.width, - .height = output_box.height, - }; - wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); -} - static void popup_handle_new_popup(struct wl_listener *listener, void *data); static struct lab_layer_popup * -create_popup(struct wlr_xdg_popup *wlr_popup, struct wlr_scene_node *parent) +create_popup(struct wlr_xdg_popup *wlr_popup, struct wlr_scene_node *parent, + struct wlr_box *output_toplevel_sx_box) { struct lab_layer_popup *popup = calloc(1, sizeof(struct lab_layer_popup)); @@ -247,43 +201,84 @@ create_popup(struct wlr_xdg_popup *wlr_popup, struct wlr_scene_node *parent) popup->new_popup.notify = popup_handle_new_popup; wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); - popup_unconstrain(popup); + wlr_xdg_popup_unconstrain_from_box(wlr_popup, output_toplevel_sx_box); return popup; } +/* This popup's parent is a layer popup */ static void popup_handle_new_popup(struct wl_listener *listener, void *data) { struct lab_layer_popup *lab_layer_popup = wl_container_of(listener, lab_layer_popup, new_popup); struct wlr_xdg_popup *wlr_popup = data; - create_popup(wlr_popup, lab_layer_popup->scene_node); + struct lab_layer_popup *new_popup = create_popup(wlr_popup, + lab_layer_popup->scene_node, + &lab_layer_popup->output_toplevel_sx_box); + new_popup->output_toplevel_sx_box = + lab_layer_popup->output_toplevel_sx_box; } +/* + * We move popups from the bottom to the top layer so that they are + * rendered above views. + */ +static void +move_popup_to_top_layer(struct lab_layer_surface *toplevel, + struct lab_layer_popup *popup) +{ + struct server *server = toplevel->server; + struct wlr_output *wlr_output = + toplevel->scene_layer_surface->layer_surface->output; + struct output *output = output_from_wlr_output(server, wlr_output); + struct wlr_box box = { 0 }; + wlr_output_layout_get_box(server->output_layout, wlr_output, &box); + int lx = toplevel->scene_layer_surface->node->state.x + box.x; + int ly = toplevel->scene_layer_surface->node->state.y + box.y; + + struct wlr_scene_node *node = popup->scene_node; + wlr_scene_node_reparent(node, &output->layer_popup_tree->node); + wlr_scene_node_set_position(&output->layer_popup_tree->node, lx, ly); +} + +/* This popup's parent is a shell-layer surface */ static void new_popup_notify(struct wl_listener *listener, void *data) { - struct lab_layer_surface *lab_layer_surface = - wl_container_of(listener, lab_layer_surface, new_popup); + struct lab_layer_surface *toplevel = + wl_container_of(listener, toplevel, new_popup); struct wlr_xdg_popup *wlr_popup = data; + struct output *output = + toplevel->scene_layer_surface->layer_surface->output->data; + + struct wlr_box output_box = { 0 }; + wlr_output_layout_get_box(output->server->output_layout, + output->wlr_output, &output_box); + + int lx, ly; + wlr_scene_node_coords(toplevel->scene_layer_surface->node, &lx, &ly); /* - * We always put popups in a special popup_tree so that for example a - * panel in the bottom layer displays popups above views. + * Output geometry expressed in the coordinate system of the toplevel + * parent of popup. We store this struct the lab_layer_popup struct + * to make it easier to unconstrain children when we move popups from + * the bottom to the top layer. */ - struct server *server = lab_layer_surface->server; - struct wlr_output *wlr_output = - lab_layer_surface->scene_layer_surface->layer_surface->output; - struct output *output = output_from_wlr_output(server, wlr_output); - struct wlr_scene_node *node = &output->layer_popup_tree->node; + struct wlr_box output_toplevel_sx_box = { + .x = output_box.x - lx, + .y = output_box.y - ly, + .width = output_box.width, + .height = output_box.height, + }; + struct lab_layer_popup *popup = create_popup(wlr_popup, + toplevel->scene_layer_surface->node, + &output_toplevel_sx_box); + popup->output_toplevel_sx_box = output_toplevel_sx_box; - create_popup(wlr_popup, node); - - struct wlr_box box = { 0 }; - wlr_output_layout_get_box(server->output_layout, wlr_output, &box); - box.x += lab_layer_surface->scene_layer_surface->node->state.x; - box.y += lab_layer_surface->scene_layer_surface->node->state.y; - wlr_scene_node_set_position(node, box.x, box.y); + if (toplevel->scene_layer_surface->layer_surface->current.layer + == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) { + move_popup_to_top_layer(toplevel, popup); + } } static void From a60161586664f1288436ba7705bc0e1054262a32 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sun, 6 Mar 2022 17:06:14 +0000 Subject: [PATCH 55/56] ssd: add top border Raise border on top of (z-order) titlebar --- src/ssd/ssd.c | 2 +- src/ssd/ssd_border.c | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index 72f56306..bc86ca61 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -156,8 +156,8 @@ ssd_create(struct view *view) 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); + ssd_border_create(view); } void diff --git a/src/ssd/ssd_border.c b/src/ssd/ssd_border.c index aa289e21..104b5c5c 100644 --- a/src/ssd/ssd_border.c +++ b/src/ssd/ssd_border.c @@ -40,6 +40,9 @@ ssd_border_create(struct view *view) add_scene_rect(&subtree->parts, LAB_SSD_PART_BOTTOM, parent, full_width, theme->border_width, 0, height, color); + add_scene_rect(&subtree->parts, LAB_SSD_PART_TOP, parent, + full_width - 2 * BUTTON_WIDTH, theme->border_width, + BUTTON_WIDTH, -SSD_HEIGHT, color); } FOR_EACH_END } @@ -71,6 +74,13 @@ ssd_border_update(struct view *view) wlr_scene_rect_set_size(rect, full_width, theme->border_width); wlr_scene_node_set_position(part->node, 0, height); continue; + case LAB_SSD_PART_TOP: + wlr_scene_rect_set_size(rect, + full_width - 2 * BUTTON_WIDTH, + theme->border_width); + wlr_scene_node_set_position(part->node, + BUTTON_WIDTH, -SSD_HEIGHT); + continue; default: continue; } From 7a708a0dc95d45a06f660bf7de784074ee317dc5 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sun, 6 Mar 2022 17:12:00 +0000 Subject: [PATCH 56/56] xdg: set node position on commit and configure To avoid incorrect view position, for example on initial placement. --- src/xdg.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/xdg.c b/src/xdg.c index 6ff718fe..dc534499 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -58,9 +58,8 @@ handle_commit(struct wl_listener *listener, void *data) if (serial == view->xdg_surface->current.configure_serial) { view->pending_move_resize.configure_serial = 0; } - wlr_scene_node_set_position(&view->scene_tree->node, - view->x, view->y); } + wlr_scene_node_set_position(&view->scene_tree->node, view->x, view->y); ssd_update_geometry(view); } @@ -189,6 +188,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; + wlr_scene_node_set_position(&view->scene_tree->node, + view->x, view->y); ssd_update_geometry(view); } }