From bb2d34a5c12b2d4d6f30d0e9f4dd3cb8f110bfb6 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Sat, 12 Jul 2025 21:10:59 -0400 Subject: [PATCH 001/248] xwayland: fix swapped width/height in _NET_WM_ICON stride calculation Probably slipped through since most window icons are square anyway. --- src/xwayland.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/xwayland.c b/src/xwayland.c index a648877e..f156e0a3 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -624,8 +624,8 @@ update_icon(struct xwayland_view *xwayland_view) struct wl_array buffers; wl_array_init(&buffers); for (; iter.rem; xcb_ewmh_get_wm_icon_next(&iter)) { - size_t stride = iter.height * 4; - uint32_t *buf = xzalloc(iter.width * stride); + size_t stride = iter.width * 4; + uint32_t *buf = xzalloc(iter.height * stride); /* Pre-multiply alpha */ for (uint32_t y = 0; y < iter.height; y++) { From 359dd8f3c7a400a89613d013e5b9ba5647cdd476 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Sat, 12 Jul 2025 21:30:26 -0400 Subject: [PATCH 002/248] img: fix apparent double-free in img_svg_render() failure path img_svg_render() calls g_object_unref() on the RsvgHandle in its error path, but the handle is owned by the shared lab_img_data struct and will be double-freed later by lab_img_destroy(). The double-free was introduced when img_svg_load() was split from img_svg_render(). The g_object_unref() should have been removed from img_svg_render() but was missed. Fixes: 16dbdc64e58d66011bbf319b92de844dab0ca8d9 ("ssd: rework titlebar button rendering") --- src/img/img-svg.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/img/img-svg.c b/src/img/img-svg.c index b5bf8670..bf671dc4 100644 --- a/src/img/img-svg.c +++ b/src/img/img-svg.c @@ -65,6 +65,5 @@ img_svg_render(RsvgHandle *svg, int w, int h, double scale) error: wlr_buffer_drop(&buffer->base); cairo_destroy(cr); - g_object_unref(svg); return NULL; } From 13ff64f6e4e90916579521eb227ea72ab7a99353 Mon Sep 17 00:00:00 2001 From: lynxy Date: Thu, 3 Jul 2025 00:04:27 +0200 Subject: [PATCH 003/248] actions: added query tiled=any comparison for rc.xml simplification --- docs/labwc-actions.5.scd | 2 +- include/view.h | 13 ++++++++----- src/view.c | 4 +++- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/docs/labwc-actions.5.scd b/docs/labwc-actions.5.scd index bf760fdb..52a3d2fb 100644 --- a/docs/labwc-actions.5.scd +++ b/docs/labwc-actions.5.scd @@ -474,7 +474,7 @@ Actions that execute other actions. Used in keyboard/mouse bindings. The "left" , "right", "left-occupied" and "right-occupied" directions will not wrap. - *tiled* [up|right|down|left|center] + *tiled* [up|right|down|left|center|any] Whether the client is tiled (snapped) along the the indicated screen edge. diff --git a/include/view.h b/include/view.h index 2ea83cd7..c4824803 100644 --- a/include/view.h +++ b/include/view.h @@ -62,11 +62,14 @@ enum view_axis { enum view_edge { VIEW_EDGE_INVALID = 0, - VIEW_EDGE_LEFT, - VIEW_EDGE_RIGHT, - VIEW_EDGE_UP, - VIEW_EDGE_DOWN, - VIEW_EDGE_CENTER, + VIEW_EDGE_LEFT = (1 << 0), + VIEW_EDGE_RIGHT = (1 << 1), + VIEW_EDGE_UP = (1 << 2), + VIEW_EDGE_DOWN = (1 << 3), + VIEW_EDGE_CENTER = (1 << 4), + + VIEW_EDGE_ALL = (VIEW_EDGE_LEFT | VIEW_EDGE_RIGHT | + VIEW_EDGE_UP | VIEW_EDGE_DOWN | VIEW_EDGE_CENTER), }; enum view_wants_focus { diff --git a/src/view.c b/src/view.c index 59c32e27..1a9d92cc 100644 --- a/src/view.c +++ b/src/view.c @@ -166,7 +166,7 @@ view_matches_query(struct view *view, struct view_query *query) return false; } - if (query->tiled != VIEW_EDGE_INVALID && query->tiled != view->tiled) { + if (query->tiled != VIEW_EDGE_INVALID && !(query->tiled & view->tiled)) { return false; } @@ -2116,6 +2116,8 @@ view_edge_parse(const char *direction) return VIEW_EDGE_DOWN; } else if (!strcasecmp(direction, "center")) { return VIEW_EDGE_CENTER; + } else if (!strcasecmp(direction, "any")) { + return VIEW_EDGE_ALL; } else { return VIEW_EDGE_INVALID; } From 254f526f71311bfe52d547f5430dda8606f31cda Mon Sep 17 00:00:00 2001 From: lynxy Date: Thu, 3 Jul 2025 00:39:32 +0200 Subject: [PATCH 004/248] actions: prevent users entering invalid direction=any value --- src/action.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/action.c b/src/action.c index a88ce309..6df68979 100644 --- a/src/action.c +++ b/src/action.c @@ -344,7 +344,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char bool allow_center = action->type == ACTION_TYPE_TOGGLE_SNAP_TO_EDGE || action->type == ACTION_TYPE_SNAP_TO_EDGE; if ((edge == VIEW_EDGE_CENTER && !allow_center) - || edge == VIEW_EDGE_INVALID) { + || edge == VIEW_EDGE_INVALID || edge == VIEW_EDGE_ALL) { wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", action_names[action->type], argument, content); } else { @@ -452,7 +452,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char } if (!strcmp(argument, "direction")) { enum view_edge edge = view_edge_parse(content); - if (edge == VIEW_EDGE_CENTER) { + if (edge == VIEW_EDGE_CENTER || edge == VIEW_EDGE_ALL) { wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", action_names[action->type], argument, content); } else { From 2bcd8277c4af0758fae0df971467231d279bf00d Mon Sep 17 00:00:00 2001 From: lynxy Date: Mon, 14 Jul 2025 23:47:42 +0200 Subject: [PATCH 005/248] comments: added short description to view_edge enum --- include/view.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/view.h b/include/view.h index c4824803..1e54f898 100644 --- a/include/view.h +++ b/include/view.h @@ -28,7 +28,6 @@ * In labwc, a view is a container for surfaces which can be moved around by * the user. In practice this means XDG toplevel and XWayland windows. */ - enum view_type { LAB_XDG_SHELL_VIEW, #if HAVE_XWAYLAND @@ -59,6 +58,12 @@ enum view_axis { VIEW_AXIS_INVALID = (1 << 2), }; +/** + * Edges to which a view can be snapped to. "All" is used as + * a catchall for every valid edge in order to simplify certain + * types of conditionals, but it is only valid for a selection + * of options in rc.xml. + */ enum view_edge { VIEW_EDGE_INVALID = 0, From ac47be3019e9dc6ebc14d90ada1db9be359bc54c Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Thu, 3 Jul 2025 23:35:37 -0400 Subject: [PATCH 006/248] menu: do not modify literal string constants Mutable string literals are a "legacy" C feature best avoided. v2: move string_truncate_at_pattern() outside fill_item() --- src/menu/menu.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/menu/menu.c b/src/menu/menu.c index c89c2b88..461c3153 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -467,16 +467,8 @@ menu_create_scene(struct menu *menu) * */ static void -fill_item(char *nodename, char *content) +fill_item(const char *nodename, const char *content) { - /* - * Nodenames for most menu-items end with '.item.menu' but top-level - * pipemenu items do not have the associated element so merely - * end with a '.item' - */ - string_truncate_at_pattern(nodename, ".item.menu"); - string_truncate_at_pattern(nodename, ".item"); - /* defines the start of a new item */ if (!strcmp(nodename, "label")) { current_item = item_create(current_menu, content, false); @@ -572,6 +564,13 @@ entry(xmlNode *node, char *nodename, char *content) printf("%s: %s\n", nodename, content ? content : (char *)cdata); } if (in_item) { + /* + * Nodenames for most menu-items end with '.item.menu' + * but top-level pipemenu items do not have the associated + * element so merely end with '.item' + */ + string_truncate_at_pattern(nodename, ".item.menu"); + string_truncate_at_pattern(nodename, ".item"); fill_item(nodename, content ? content : (char *)cdata); } xmlFree(cdata); From cd8a8c2bf698cce2f1a12d9d11188b1d6ff3f26c Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Fri, 4 Jul 2025 00:18:53 -0400 Subject: [PATCH 007/248] keybind: pointer to string literal should be const Assigning to (char*) creates a mutable string literal, which was likely not intended here and wasn't necessary. --- src/config/keybind.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/keybind.c b/src/config/keybind.c index 5e5002f7..d59b64eb 100644 --- a/src/config/keybind.c +++ b/src/config/keybind.c @@ -123,7 +123,7 @@ keybind_create(const char *keybind) xkb_keysym_t keysyms[MAX_KEYSYMS]; gchar **symnames = g_strsplit(keybind, "-", -1); for (size_t i = 0; symnames[i]; i++) { - char *symname = symnames[i]; + const char *symname = symnames[i]; /* * Since "-" is used as a separator, a keybind string like "W--" * becomes "W", "", "". This means that it is impossible to bind From 8b7ae52a912a4afadf7382108838f8a4a15e6350 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Fri, 4 Jul 2025 00:37:39 -0400 Subject: [PATCH 008/248] src: avoid implicit int/bool -> enum conversions Use the defined enum constants instead. --- src/config/rcxml.c | 2 +- src/input/keyboard.c | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/config/rcxml.c b/src/config/rcxml.c index cc207603..5dea021a 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -1556,7 +1556,7 @@ rcxml_init(void) rc.gap = 0; rc.adaptive_sync = LAB_ADAPTIVE_SYNC_DISABLED; - rc.allow_tearing = false; + rc.allow_tearing = LAB_TEARING_DISABLED; rc.auto_enable_outputs = true; rc.reuse_output_mode = false; rc.xwayland_persistence = false; diff --git a/src/input/keyboard.c b/src/input/keyboard.c index 4c53b580..15b549f1 100644 --- a/src/input/keyboard.c +++ b/src/input/keyboard.c @@ -387,7 +387,7 @@ get_keyinfo(struct wlr_keyboard *wlr_keyboard, uint32_t evdev_keycode) return keyinfo; } -static bool +static enum lab_key_handled handle_key_release(struct server *server, uint32_t evdev_keycode) { /* @@ -395,7 +395,7 @@ handle_key_release(struct server *server, uint32_t evdev_keycode) * forwarded to clients to avoid stuck keys. */ if (!key_state_corresponding_press_event_was_bound(evdev_keycode)) { - return false; + return LAB_KEY_HANDLED_FALSE; } /* @@ -416,7 +416,7 @@ handle_key_release(struct server *server, uint32_t evdev_keycode) * not forward the corresponding release event to clients. */ key_state_bound_key_remove(evdev_keycode); - return true; + return LAB_KEY_HANDLED_TRUE; } static bool @@ -518,10 +518,10 @@ handle_compositor_keybindings(struct keyboard *keyboard, key_state_bound_key_remove(event->keycode); if (locked && !cur_keybind->allow_when_locked) { cur_keybind = NULL; - return true; + return LAB_KEY_HANDLED_TRUE; } actions_run(NULL, server, &cur_keybind->actions, NULL); - return true; + return LAB_KEY_HANDLED_TRUE; } else { return handle_key_release(server, event->keycode); } @@ -542,11 +542,11 @@ handle_compositor_keybindings(struct keyboard *keyboard, if (server->input_mode == LAB_INPUT_STATE_MENU) { key_state_store_pressed_key_as_bound(event->keycode); handle_menu_keys(server, &keyinfo.translated); - return true; + return LAB_KEY_HANDLED_TRUE; } else if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) { if (handle_cycle_view_key(server, &keyinfo)) { key_state_store_pressed_key_as_bound(event->keycode); - return true; + return LAB_KEY_HANDLED_TRUE; } } } @@ -565,10 +565,10 @@ handle_compositor_keybindings(struct keyboard *keyboard, if (!cur_keybind->on_release) { actions_run(NULL, server, &cur_keybind->actions, NULL); } - return true; + return LAB_KEY_HANDLED_TRUE; } - return false; + return LAB_KEY_HANDLED_FALSE; } static int From b48c250177bb698e9deb595d438d0a0144994434 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Fri, 4 Jul 2025 00:44:22 -0400 Subject: [PATCH 009/248] src: prefer 'if' over 'goto' where convenient 'goto' should not be used for normal control flow. v2 add comment in lieu of goto label --- src/input/keyboard.c | 29 ++++++++++++++--------------- src/ssd/ssd-titlebar.c | 19 +++++++++---------- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/src/input/keyboard.c b/src/input/keyboard.c index 15b549f1..17a21395 100644 --- a/src/input/keyboard.c +++ b/src/input/keyboard.c @@ -265,23 +265,21 @@ static struct keybind * match_keybinding(struct server *server, struct keyinfo *keyinfo, bool is_virtual) { - if (is_virtual) { - goto process_syms; + if (!is_virtual) { + /* First try keycodes */ + struct keybind *keybind = match_keybinding_for_sym(server, + keyinfo->modifiers, XKB_KEY_NoSymbol, keyinfo->xkb_keycode); + if (keybind) { + wlr_log(WLR_DEBUG, "keycode matched"); + return keybind; + } } - /* First try keycodes */ - struct keybind *keybind = match_keybinding_for_sym(server, - keyinfo->modifiers, XKB_KEY_NoSymbol, keyinfo->xkb_keycode); - if (keybind) { - wlr_log(WLR_DEBUG, "keycode matched"); - return keybind; - } - -process_syms: /* Then fall back to keysyms */ for (int i = 0; i < keyinfo->translated.nr_syms; i++) { - keybind = match_keybinding_for_sym(server, keyinfo->modifiers, - keyinfo->translated.syms[i], keyinfo->xkb_keycode); + struct keybind *keybind = + match_keybinding_for_sym(server, keyinfo->modifiers, + keyinfo->translated.syms[i], keyinfo->xkb_keycode); if (keybind) { wlr_log(WLR_DEBUG, "translated keysym matched"); return keybind; @@ -290,8 +288,9 @@ process_syms: /* And finally test for keysyms without modifier */ for (int i = 0; i < keyinfo->raw.nr_syms; i++) { - keybind = match_keybinding_for_sym(server, keyinfo->modifiers, - keyinfo->raw.syms[i], keyinfo->xkb_keycode); + struct keybind *keybind = + match_keybinding_for_sym(server, keyinfo->modifiers, + keyinfo->raw.syms[i], keyinfo->xkb_keycode); if (keybind) { wlr_log(WLR_DEBUG, "raw keysym matched"); return keybind; diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index d44adf41..57d49600 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -543,20 +543,19 @@ ssd_update_button_hover(struct wlr_scene_node *node, struct ssd_hover_state *hover_state) { struct ssd_button *button = NULL; - if (!node || !node->data) { - goto disable_old_hover; - } - struct node_descriptor *desc = node->data; - if (desc->type == LAB_NODE_DESC_SSD_BUTTON) { - button = node_ssd_button_from_node(node); - if (button == hover_state->button) { - /* Cursor is still on the same button */ - return; + if (node && node->data) { + struct node_descriptor *desc = node->data; + if (desc->type == LAB_NODE_DESC_SSD_BUTTON) { + button = node_ssd_button_from_node(node); + if (button == hover_state->button) { + /* Cursor is still on the same button */ + return; + } } } -disable_old_hover: + /* Disable old hover */ if (hover_state->button) { update_button_state(hover_state->button, LAB_BS_HOVERD, false); hover_state->view = NULL; From 681f9e9a7bf6e33845967abebb32eb93aa9b5716 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Fri, 4 Jul 2025 00:42:25 -0400 Subject: [PATCH 010/248] src: add braces to switch cases containing declarations This limits the scope of the declarations to avoid accidents. --- src/img/img-xbm.c | 3 ++- src/protocols/cosmic_workspaces/cosmic-workspaces.c | 3 ++- src/protocols/ext-workspace/ext-workspace.c | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/img/img-xbm.c b/src/img/img-xbm.c index f01b689b..7f1571e4 100644 --- a/src/img/img-xbm.c +++ b/src/img/img-xbm.c @@ -141,12 +141,13 @@ tokenize_xbm(char *buffer) add_token(&ctx, TOKEN_IDENT); get_identifier_token(&ctx); continue; - case '0' ... '9': + case '0' ... '9': { add_token(&ctx, TOKEN_INT); get_number_token(&ctx); struct token *token = ctx.tokens + ctx.nr_tokens - 1; token->value = (int)strtol(token->name, NULL, 0); continue; + } case '{': add_token(&ctx, TOKEN_SPECIAL); get_special_char_token(&ctx); diff --git a/src/protocols/cosmic_workspaces/cosmic-workspaces.c b/src/protocols/cosmic_workspaces/cosmic-workspaces.c index b39e2d17..20d8b822 100644 --- a/src/protocols/cosmic_workspaces/cosmic-workspaces.c +++ b/src/protocols/cosmic_workspaces/cosmic-workspaces.c @@ -323,11 +323,12 @@ manager_handle_commit(struct wl_client *client, struct wl_resource *resource) struct lab_transaction_op *trans_op, *trans_op_tmp; lab_transaction_for_each_safe(trans_op, trans_op_tmp, addon->ctx) { switch (trans_op->change) { - case CW_PENDING_WS_CREATE: + case CW_PENDING_WS_CREATE: { group = trans_op->src; struct ws_create_workspace_event *ev = trans_op->data; wl_signal_emit_mutable(&group->events.create_workspace, ev->name); break; + } case CW_PENDING_WS_ACTIVATE: workspace = trans_op->src; wl_signal_emit_mutable(&workspace->events.activate, NULL); diff --git a/src/protocols/ext-workspace/ext-workspace.c b/src/protocols/ext-workspace/ext-workspace.c index 92032375..eb02e3dc 100644 --- a/src/protocols/ext-workspace/ext-workspace.c +++ b/src/protocols/ext-workspace/ext-workspace.c @@ -295,11 +295,12 @@ manager_handle_commit(struct wl_client *client, struct wl_resource *resource) struct lab_transaction_op *trans_op, *trans_op_tmp; lab_transaction_for_each_safe(trans_op, trans_op_tmp, addon->ctx) { switch (trans_op->change) { - case WS_PENDING_WS_CREATE: + case WS_PENDING_WS_CREATE: { group = trans_op->src; struct ws_create_workspace_event *ev = trans_op->data; wl_signal_emit_mutable(&group->events.create_workspace, ev->name); break; + } case WS_PENDING_WS_ACTIVATE: workspace = trans_op->src; wl_signal_emit_mutable(&workspace->events.activate, NULL); From 407a29aa239dd7c4a0ee90ccd08529229457d8ce Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Fri, 4 Jul 2025 00:15:56 -0400 Subject: [PATCH 011/248] string-helpers: remove restrict qualifier 'restrict' is harmful as it encourages the compiler to make dangerous assumptions while increasing cognitive load on the human programmer. The extra 1% (or whatever) of performance here is not worth the cost. --- include/common/string-helpers.h | 3 +-- src/common/string-helpers.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/include/common/string-helpers.h b/include/common/string-helpers.h index efa5bfd2..e49fa110 100644 --- a/include/common/string-helpers.h +++ b/include/common/string-helpers.h @@ -59,8 +59,7 @@ char *strdup_printf(const char *fmt, ...); * The separator is arbitrary. When the separator is NULL, a single space will * be used. */ -char *str_join(const char *const parts[], - const char *restrict fmt, const char *restrict sep); +char *str_join(const char *const parts[], const char *fmt, const char *sep); /** * str_endswith - indicate whether a string ends with a given suffix diff --git a/src/common/string-helpers.c b/src/common/string-helpers.c index f753d1a1..f94e1dd9 100644 --- a/src/common/string-helpers.c +++ b/src/common/string-helpers.c @@ -93,8 +93,7 @@ strdup_printf(const char *fmt, ...) } char * -str_join(const char *const parts[], - const char *restrict fmt, const char *restrict sep) +str_join(const char *const parts[], const char *fmt, const char *sep) { assert(parts); From a802d6b20ab0d0f33806e1e33118a711ba1277a6 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Fri, 4 Jul 2025 00:12:21 -0400 Subject: [PATCH 012/248] src: avoid tentative definitions of static data Having multiple declarations of the same static data (where one is considered "tentative") is kind of an obscure C feature -- I didn't even know the name of it until today. It's also forbidden in C++. In the case of circular dependencies between static data <-> function, the more typical pattern is to forward-declare the function, then the data, then provide the function definition. Let's follow that pattern. --- src/buffer.c | 17 +++++++++-------- src/osd-field.c | 41 +++++++++++++++++++++-------------------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index 1faa4620..8a2aa202 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -33,14 +33,8 @@ #include "common/box.h" #include "common/mem.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 struct lab_data_buffer *data_buffer_from_buffer( + struct wlr_buffer *buffer); static void data_buffer_destroy(struct wlr_buffer *wlr_buffer) @@ -80,6 +74,13 @@ static const struct wlr_buffer_impl data_buffer_impl = { .end_data_ptr_access = data_buffer_end_data_ptr_access, }; +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; +} + struct lab_data_buffer * buffer_adopt_cairo_surface(cairo_surface_t *surface) { diff --git a/src/osd-field.c b/src/osd-field.c index 9eec0837..2885febe 100644 --- a/src/osd-field.c +++ b/src/osd-field.c @@ -21,8 +21,6 @@ struct field_converter { field_conversion_type *fn; }; -static const struct field_converter field_converter[]; - /* Internal helpers */ static const char * @@ -200,6 +198,27 @@ field_set_title_short(struct buf *buf, struct view *view, const char *format) buf_add(buf, get_title_if_different(view)); } +static void field_set_custom(struct buf *buf, struct view *view, + const char *format); + +static const struct field_converter field_converter[LAB_FIELD_COUNT] = { + [LAB_FIELD_TYPE] = { 'B', field_set_type }, + [LAB_FIELD_TYPE_SHORT] = { 'b', field_set_type_short }, + [LAB_FIELD_WIN_STATE_ALL] = { 'S', field_set_win_state_all }, + [LAB_FIELD_WIN_STATE] = { 's', field_set_win_state }, + [LAB_FIELD_IDENTIFIER] = { 'I', field_set_identifier }, + [LAB_FIELD_TRIMMED_IDENTIFIER] = { 'i', field_set_identifier_trimmed }, + [LAB_FIELD_DESKTOP_ENTRY_NAME] = { 'n', field_set_desktop_entry_name}, + [LAB_FIELD_WORKSPACE] = { 'W', field_set_workspace }, + [LAB_FIELD_WORKSPACE_SHORT] = { 'w', field_set_workspace_short }, + [LAB_FIELD_OUTPUT] = { 'O', field_set_output }, + [LAB_FIELD_OUTPUT_SHORT] = { 'o', field_set_output_short }, + [LAB_FIELD_TITLE] = { 'T', field_set_title }, + [LAB_FIELD_TITLE_SHORT] = { 't', field_set_title_short }, + /* fmt_char can never be matched so prevents LAB_FIELD_CUSTOM recursion */ + [LAB_FIELD_CUSTOM] = { '\0', field_set_custom }, +}; + static void field_set_custom(struct buf *buf, struct view *view, const char *format) { @@ -278,24 +297,6 @@ reset_format: buf_reset(&field_result); } -static const struct field_converter field_converter[LAB_FIELD_COUNT] = { - [LAB_FIELD_TYPE] = { 'B', field_set_type }, - [LAB_FIELD_TYPE_SHORT] = { 'b', field_set_type_short }, - [LAB_FIELD_WIN_STATE_ALL] = { 'S', field_set_win_state_all }, - [LAB_FIELD_WIN_STATE] = { 's', field_set_win_state }, - [LAB_FIELD_IDENTIFIER] = { 'I', field_set_identifier }, - [LAB_FIELD_TRIMMED_IDENTIFIER] = { 'i', field_set_identifier_trimmed }, - [LAB_FIELD_DESKTOP_ENTRY_NAME] = { 'n', field_set_desktop_entry_name}, - [LAB_FIELD_WORKSPACE] = { 'W', field_set_workspace }, - [LAB_FIELD_WORKSPACE_SHORT] = { 'w', field_set_workspace_short }, - [LAB_FIELD_OUTPUT] = { 'O', field_set_output }, - [LAB_FIELD_OUTPUT_SHORT] = { 'o', field_set_output_short }, - [LAB_FIELD_TITLE] = { 'T', field_set_title }, - [LAB_FIELD_TITLE_SHORT] = { 't', field_set_title_short }, - /* fmt_char can never be matched so prevents LAB_FIELD_CUSTOM recursion */ - [LAB_FIELD_CUSTOM] = { '\0', field_set_custom }, -}; - struct window_switcher_field * osd_field_create(void) { From 4afbfac5285d964ba0d44e457609383288e4dc5a Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Fri, 4 Jul 2025 00:40:15 -0400 Subject: [PATCH 013/248] src: put designated initializers in member order Out-of-order named initializers are allowed in C (unlike in C++) but are still surprising - I don't see a reason not to put them in order. --- src/edges.c | 6 +++--- src/input/ime.c | 6 +++--- src/ssd/ssd.c | 2 +- src/theme.c | 18 +++++++++--------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/edges.c b/src/edges.c index 6d64c511..50b7e615 100644 --- a/src/edges.c +++ b/src/edges.c @@ -283,8 +283,8 @@ subtract_view_from_space(struct view *view, pixman_region32_t *available) struct wlr_box view_size = ssd_max_extents(view); pixman_box32_t view_rect = { .x1 = view_size.x, - .x2 = view_size.x + view_size.width, .y1 = view_size.y, + .x2 = view_size.x + view_size.width, .y2 = view_size.y + view_size.height }; @@ -431,10 +431,10 @@ edges_find_neighbors(struct border *nearest_edges, struct view *view, struct border win_edges = { .top = v->current.y - border.top, - .left = v->current.x - border.left, + .right = v->current.x + v->current.width + border.right, .bottom = v->current.y + border.bottom + view_effective_height(v, /* use_pending */ false), - .right = v->current.x + v->current.width + border.right, + .left = v->current.x - border.left, }; validate_edges(nearest_edges, view_edges, diff --git a/src/input/ime.c b/src/input/ime.c index ded28f5c..e76b2f2c 100644 --- a/src/input/ime.c +++ b/src/input/ime.c @@ -224,13 +224,13 @@ update_popup_position(struct input_method_popup *popup) .anchor_rect = cursor_rect, .anchor = XDG_POSITIONER_ANCHOR_BOTTOM_LEFT, .gravity = XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT, + .constraint_adjustment = + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y + | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X, .size = { .width = popup->popup_surface->surface->current.width, .height = popup->popup_surface->surface->current.height, }, - .constraint_adjustment = - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y - | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X, }; struct wlr_box popup_box; diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index 65416ca3..48d416b9 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -46,9 +46,9 @@ ssd_thickness(struct view *view) struct border thickness = { .top = theme->titlebar_height + theme->border_width, + .right = theme->border_width, .bottom = theme->border_width, .left = theme->border_width, - .right = theme->border_width, }; if (view->ssd_titlebar_hidden) { diff --git a/src/theme.c b/src/theme.c index 5671c195..5fe9c1a2 100644 --- a/src/theme.c +++ b/src/theme.c @@ -289,49 +289,49 @@ load_buttons(struct theme *theme) { struct button buttons[] = { { .name = "menu", + .fallback_button = (const char[]){ 0x00, 0x21, 0x33, 0x1E, 0x0C, 0x00 }, .type = LAB_SSD_BUTTON_WINDOW_MENU, .state_set = 0, - .fallback_button = (const char[]){ 0x00, 0x21, 0x33, 0x1E, 0x0C, 0x00 }, }, { .name = "iconify", + .fallback_button = (const char[]){ 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f }, .type = LAB_SSD_BUTTON_ICONIFY, .state_set = 0, - .fallback_button = (const char[]){ 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f }, }, { .name = "max", + .fallback_button = (const char[]){ 0x3f, 0x3f, 0x21, 0x21, 0x21, 0x3f }, .type = LAB_SSD_BUTTON_MAXIMIZE, .state_set = 0, - .fallback_button = (const char[]){ 0x3f, 0x3f, 0x21, 0x21, 0x21, 0x3f }, }, { .name = "max_toggled", + .fallback_button = (const char[]){ 0x3e, 0x22, 0x2f, 0x29, 0x39, 0x0f }, .type = LAB_SSD_BUTTON_MAXIMIZE, .state_set = LAB_BS_TOGGLED, - .fallback_button = (const char[]){ 0x3e, 0x22, 0x2f, 0x29, 0x39, 0x0f }, }, { .name = "shade", + .fallback_button = (const char[]){ 0x3f, 0x3f, 0x00, 0x0c, 0x1e, 0x3f }, .type = LAB_SSD_BUTTON_SHADE, .state_set = 0, - .fallback_button = (const char[]){ 0x3f, 0x3f, 0x00, 0x0c, 0x1e, 0x3f }, }, { .name = "shade_toggled", + .fallback_button = (const char[]){ 0x3f, 0x3f, 0x00, 0x3f, 0x1e, 0x0c }, .type = LAB_SSD_BUTTON_SHADE, .state_set = LAB_BS_TOGGLED, - .fallback_button = (const char[]){ 0x3f, 0x3f, 0x00, 0x3f, 0x1e, 0x0c }, }, { .name = "desk", + .fallback_button = (const char[]){ 0x33, 0x33, 0x00, 0x00, 0x33, 0x33 }, .type = LAB_SSD_BUTTON_OMNIPRESENT, .state_set = 0, - .fallback_button = (const char[]){ 0x33, 0x33, 0x00, 0x00, 0x33, 0x33 }, }, { .name = "desk_toggled", + .fallback_button = (const char[]){ 0x00, 0x1e, 0x1a, 0x16, 0x1e, 0x00 }, .type = LAB_SSD_BUTTON_OMNIPRESENT, .state_set = LAB_BS_TOGGLED, - .fallback_button = (const char[]){ 0x00, 0x1e, 0x1a, 0x16, 0x1e, 0x00 }, }, { .name = "close", + .fallback_button = (const char[]){ 0x33, 0x3f, 0x1e, 0x1e, 0x3f, 0x33 }, .type = LAB_SSD_BUTTON_CLOSE, .state_set = 0, - .fallback_button = (const char[]){ 0x33, 0x3f, 0x1e, 0x1e, 0x3f, 0x33 }, }, { .name = "menu_hover", .type = LAB_SSD_BUTTON_WINDOW_MENU, From 7d2b5150e81a2b85a949942d1f49eee356533ac5 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Fri, 4 Jul 2025 00:45:59 -0400 Subject: [PATCH 014/248] session-lock: eliminate pointless compound literal In C++, this would have caused a use-after-free. In C, the unnamed array remains in scope longer, so it's okay, but still pointless. --- src/session-lock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/session-lock.c b/src/session-lock.c index edad8f64..0a140ca6 100644 --- a/src/session-lock.c +++ b/src/session-lock.c @@ -216,7 +216,7 @@ session_lock_output_create(struct session_lock_manager *manager, struct output * * all outputs with an opaque color such that their normal content is * fully hidden */ - float *black = (float[4]) { 0.f, 0.f, 0.f, 1.f }; + float black[4] = { 0.f, 0.f, 0.f, 1.f }; struct wlr_scene_rect *background = wlr_scene_rect_create(tree, 0, 0, black); if (!background) { wlr_log(WLR_ERROR, "session-lock: wlr_scene_rect_create()"); From 1747d9e96147693d2709cdf0e2e62797752943f3 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Fri, 4 Jul 2025 00:17:18 -0400 Subject: [PATCH 015/248] buf: avoid 'new' as variable name It's just good practice to avoid C++ keywords, in case someone someday wants to compile this code as C++. --- src/common/buf.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/common/buf.c b/src/common/buf.c index 1e103fae..0fe810fc 100644 --- a/src/common/buf.c +++ b/src/common/buf.c @@ -13,15 +13,15 @@ void buf_expand_tilde(struct buf *s) { - struct buf new = BUF_INIT; + struct buf tmp = BUF_INIT; for (int i = 0 ; i < s->len ; i++) { if (s->data[i] == '~') { - buf_add(&new, getenv("HOME")); + buf_add(&tmp, getenv("HOME")); } else { - buf_add_char(&new, s->data[i]); + buf_add_char(&tmp, s->data[i]); } } - buf_move(s, &new); + buf_move(s, &tmp); } static void @@ -45,7 +45,7 @@ isvalid(char p) void buf_expand_shell_variables(struct buf *s) { - struct buf new = BUF_INIT; + struct buf tmp = BUF_INIT; struct buf environment_variable = BUF_INIT; for (int i = 0 ; i < s->len ; i++) { @@ -62,14 +62,14 @@ buf_expand_shell_variables(struct buf *s) strip_curly_braces(environment_variable.data); p = getenv(environment_variable.data); if (p) { - buf_add(&new, p); + buf_add(&tmp, p); } } else { - buf_add_char(&new, s->data[i]); + buf_add_char(&tmp, s->data[i]); } } buf_reset(&environment_variable); - buf_move(s, &new); + buf_move(s, &tmp); } static void From cb79eccea1e259b1979ec4767922256defcda8f3 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Tue, 22 Jul 2025 00:19:22 +0900 Subject: [PATCH 016/248] cursor: prevent hi-res mice triggering scroll actions too often Hi-res mice produces mulitple scroll events with `delta_discrete` != 0 during a single "click". This patch makes them trigger `Scroll` actions only when the accumulated `delta_discrete` exceeds 120 (= 1 click). See https://lists.freedesktop.org/archives/wayland-devel/2019-April/040377.html for how hi-res scroll events are reported. --- include/labwc.h | 7 +++-- src/input/cursor.c | 75 +++++++++++++++++++++++----------------------- 2 files changed, 42 insertions(+), 40 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index cff78588..5f4ae133 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -111,9 +111,10 @@ struct seat { bool cursor_visible; struct wlr_cursor *cursor; struct wlr_xcursor_manager *xcursor_manager; - struct { - double x, y; - } smooth_scroll_offset; + struct accumulated_scroll { + double delta; + double delta_discrete; + } accumulated_scrolls[2]; /* indexed by wl_pointer_axis */ bool cursor_scroll_wheel_emulation; /* diff --git a/src/input/cursor.c b/src/input/cursor.c index c5f9e4ae..d55e6bc0 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -1245,38 +1245,44 @@ struct scroll_info { }; static struct scroll_info -compare_delta(double delta, double delta_discrete, double *accum) +compare_delta(double delta, double delta_discrete, struct accumulated_scroll *accum) { - /* - * Smooth scroll deltas are in surface space, so treating each unit as a - * scroll event would result in too-fast scrolling. - * - * This fudge factor (inherited from various historic projects, incl. Weston) - * produces events at a more reasonable rate. - * - * For historic context, see: - * https://lists.freedesktop.org/archives/wayland-devel/2019-April/040377.html - */ - const double SCROLL_THRESHOLD = 10.0; struct scroll_info info = {0}; - if (delta_discrete < 0 || delta < 0) { - info.direction = -1; - } else if (delta_discrete > 0 || delta > 0) { - info.direction = 1; - } - - if (delta == 0.0) { - /* Delta 0 marks the end of a scroll */ - *accum = 0.0; + if (delta_discrete) { + /* mice */ + info.direction = delta_discrete > 0 ? 1 : -1; + accum->delta_discrete += delta_discrete; + /* + * Non-hi-res mice produce delta_discrete of ±120 for every + * "click", so it always triggers actions. But for hi-res mice + * that produce smaller delta_discrete, we accumulate it and + * run actions after it exceeds 120(= 1 click). + */ + if (fabs(accum->delta_discrete) >= 120.0) { + accum->delta_discrete = fmod(accum->delta_discrete, 120.0); + info.run_action = true; + } } else { - /* Accumulate smooth scrolling until we hit threshold */ - *accum += delta; - } - - if (delta_discrete != 0 || fabs(*accum) > SCROLL_THRESHOLD) { - *accum = fmod(*accum, SCROLL_THRESHOLD); - info.run_action = true; + /* 2-finger scrolling on touchpads */ + if (delta == 0) { + /* delta=0 marks the end of a scroll */ + accum->delta = 0; + return info; + } + info.direction = delta > 0 ? 1 : -1; + accum->delta += delta; + /* + * The threshold of 10 is inherited from various historic + * projects including weston. + * + * For historic context, see: + * https://lists.freedesktop.org/archives/wayland-devel/2019-April/040377.html + */ + if (fabs(accum->delta) >= 10.0) { + accum->delta = fmod(accum->delta, 10.0); + info.run_action = true; + } } return info; @@ -1290,21 +1296,16 @@ process_cursor_axis(struct server *server, enum wl_pointer_axis orientation, uint32_t modifiers = keyboard_get_all_modifiers(&server->seat); enum direction direction = LAB_DIRECTION_INVALID; - struct scroll_info info = {0}; + struct scroll_info info = compare_delta(delta, delta_discrete, + &server->seat.accumulated_scrolls[orientation]); if (orientation == WL_POINTER_AXIS_HORIZONTAL_SCROLL) { - info = compare_delta(delta, delta_discrete, - &server->seat.smooth_scroll_offset.x); - if (info.direction < 0) { direction = LAB_DIRECTION_LEFT; } else if (info.direction > 0) { direction = LAB_DIRECTION_RIGHT; } } else if (orientation == WL_POINTER_AXIS_VERTICAL_SCROLL) { - info = compare_delta(delta, delta_discrete, - &server->seat.smooth_scroll_offset.y); - if (info.direction < 0) { direction = LAB_DIRECTION_UP; } else if (info.direction > 0) { @@ -1324,8 +1325,8 @@ process_cursor_axis(struct server *server, enum wl_pointer_axis orientation, && mousebind->mouse_event == MOUSE_ACTION_SCROLL) { handled = true; /* - * Action may not be executed if the accumulated scroll - * delta on touchpads doesn't exceed the threshold + * Action may not be executed if the accumulated scroll delta + * on touchpads or hi-res mice doesn't exceed the threshold */ if (info.run_action) { actions_run(ctx.view, server, &mousebind->actions, &ctx); From d96656ccdcef7e3dd1fca5cde0608d4220b61585 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Fri, 18 Jul 2025 23:06:20 -0400 Subject: [PATCH 017/248] menu: add struct menu_parse_context to reduce static vars The lifetime of the "current_" variables (current_menu, current_item, current_item_action) is very difficult to understand from reading the code. It appears that e.g. current_menu could still point to a previous menu when starting to parse a new one, with unpredictable results. Let's use a context struct when parsing, and consistently initialize it when beginning to build a new menu. Lightly tested with: - default menus (no menu.xml) - example static menu from labwc.github.io/getting-started.html - an added "client-list-combined-menu" sub-menu - pipe menu generated by `labwc-menu-generator -p` v2: style fix --- src/menu/menu.c | 267 ++++++++++++++++++++++++------------------------ 1 file changed, 136 insertions(+), 131 deletions(-) diff --git a/src/menu/menu.c b/src/menu/menu.c index 461c3153..dd9f7f59 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -17,7 +17,6 @@ #include "common/font.h" #include "common/lab-scene-rect.h" #include "common/list.h" -#include "common/macros.h" #include "common/mem.h" #include "common/nodename.h" #include "common/scaled-font-buffer.h" @@ -38,11 +37,13 @@ #define ICON_SIZE (rc.theme->menu_item_height - 2 * rc.theme->menu_items_padding_y) /* state-machine variables for processing */ -static bool in_item; -static struct menuitem *current_item; -static struct action *current_item_action; - -static struct menu *current_menu; +struct menu_parse_context { + struct server *server; + struct menu *menu; + struct menuitem *item; + struct action *action; + bool in_item; +}; static bool waiting_for_pipe_menu; static struct menuitem *selected_item; @@ -72,7 +73,8 @@ is_unique_id(struct server *server, const char *id) } static struct menu * -menu_create(struct server *server, const char *id, const char *label) +menu_create(struct server *server, struct menu *parent, const char *id, + const char *label) { if (!is_unique_id(server, id)) { wlr_log(WLR_ERROR, "menu id %s already exists", id); @@ -84,7 +86,7 @@ menu_create(struct server *server, const char *id, const char *label) wl_list_init(&menu->menuitems); menu->id = xstrdup(id); menu->label = xstrdup(label ? label : id); - menu->parent = current_menu; + menu->parent = parent; menu->server = server; menu->is_pipemenu_child = waiting_for_pipe_menu; return menu; @@ -467,33 +469,33 @@ menu_create_scene(struct menu *menu) * */ static void -fill_item(const char *nodename, const char *content) +fill_item(struct menu_parse_context *ctx, const char *nodename, + const char *content) { /* defines the start of a new item */ if (!strcmp(nodename, "label")) { - current_item = item_create(current_menu, content, false); - current_item_action = NULL; - } else if (!current_item) { + ctx->item = item_create(ctx->menu, content, false); + ctx->action = NULL; + } else if (!ctx->item) { wlr_log(WLR_ERROR, "expect element first. " "nodename: '%s' content: '%s'", nodename, content); } else if (!strcmp(nodename, "icon")) { #if HAVE_LIBSFDO if (rc.menu_show_icons && !string_null_or_empty(content)) { - xstrdup_replace(current_item->icon_name, content); - current_menu->has_icons = true; + xstrdup_replace(ctx->item->icon_name, content); + ctx->menu->has_icons = true; } #endif } else if (!strcmp(nodename, "name.action")) { - current_item_action = action_create(content); - if (current_item_action) { - wl_list_append(¤t_item->actions, - ¤t_item_action->link); + ctx->action = action_create(content); + if (ctx->action) { + wl_list_append(&ctx->item->actions, &ctx->action->link); } - } else if (!current_item_action) { + } else if (!ctx->action) { wlr_log(WLR_ERROR, "expect element first. " "nodename: '%s' content: '%s'", nodename, content); } else { - action_arg_from_xml_node(current_item_action, nodename, content); + action_arg_from_xml_node(ctx->action, nodename, content); } } @@ -546,7 +548,8 @@ nodename_supports_cdata(char *nodename) } static void -entry(xmlNode *node, char *nodename, char *content) +entry(struct menu_parse_context *ctx, xmlNode *node, char *nodename, + char *content) { if (!nodename) { return; @@ -563,7 +566,7 @@ entry(xmlNode *node, char *nodename, char *content) if (getenv("LABWC_DEBUG_MENU_NODENAMES")) { printf("%s: %s\n", nodename, content ? content : (char *)cdata); } - if (in_item) { + if (ctx->in_item) { /* * Nodenames for most menu-items end with '.item.menu' * but top-level pipemenu items do not have the associated @@ -571,13 +574,13 @@ entry(xmlNode *node, char *nodename, char *content) */ string_truncate_at_pattern(nodename, ".item.menu"); string_truncate_at_pattern(nodename, ".item"); - fill_item(nodename, content ? content : (char *)cdata); + fill_item(ctx, nodename, content ? content : (char *)cdata); } xmlFree(cdata); } static void -process_node(xmlNode *node) +process_node(struct menu_parse_context *ctx, xmlNode *node) { static char buffer[256]; @@ -586,24 +589,24 @@ process_node(xmlNode *node) return; } char *name = nodename(node, buffer, sizeof(buffer)); - entry(node, name, content); + entry(ctx, node, name, content); } -static void xml_tree_walk(xmlNode *node, struct server *server); +static void xml_tree_walk(struct menu_parse_context *ctx, xmlNode *node); static void -traverse(xmlNode *n, struct server *server) +traverse(struct menu_parse_context *ctx, xmlNode *n) { xmlAttr *attr; - process_node(n); + process_node(ctx, n); for (attr = n->properties; attr; attr = attr->next) { - xml_tree_walk(attr->children, server); + xml_tree_walk(ctx, attr->children); } - xml_tree_walk(n->children, server); + xml_tree_walk(ctx, n->children); } -static bool parse_buf(struct server *server, struct buf *buf); +static bool parse_buf(struct menu_parse_context *ctx, struct buf *buf); static int handle_pipemenu_readable(int fd, uint32_t mask, void *_ctx); static int handle_pipemenu_timeout(void *_ctx); @@ -614,7 +617,7 @@ static int handle_pipemenu_timeout(void *_ctx); * * Menuitem of submenu type - has ID only */ static void -handle_menu_element(xmlNode *n, struct server *server) +handle_menu_element(struct menu_parse_context *ctx, xmlNode *n) { char *label = (char *)xmlGetProp(n, (const xmlChar *)"label"); char *icon_name = (char *)xmlGetProp(n, (const xmlChar *)"icon"); @@ -629,9 +632,10 @@ handle_menu_element(xmlNode *n, struct server *server) if (execute && label) { wlr_log(WLR_DEBUG, "pipemenu '%s:%s:%s'", id, label, execute); - struct menu *pipemenu = menu_create(server, id, label); + struct menu *pipemenu = + menu_create(ctx->server, ctx->menu, id, label); pipemenu->execute = xstrdup(execute); - if (!current_menu) { + if (!ctx->menu) { /* * A pipemenu may not have its parent like: * @@ -641,18 +645,18 @@ handle_menu_element(xmlNode *n, struct server *server) * */ } else { - current_item = item_create(current_menu, label, + ctx->item = item_create(ctx->menu, label, /* arrow */ true); - fill_item("icon", icon_name); - current_item_action = NULL; - current_item->submenu = pipemenu; + fill_item(ctx, "icon", icon_name); + ctx->action = NULL; + ctx->item->submenu = pipemenu; } - } else if ((label && current_menu) || !current_menu) { + } else if ((label && ctx->menu) || !ctx->menu) { /* - * (label && current_menu) refers to + * (label && ctx->menu) refers to * which is an nested (inline) menu definition. * - * (!current_menu) catches: + * (!ctx->menu) catches: * * * @@ -668,22 +672,22 @@ handle_menu_element(xmlNode *n, struct server *server) * attribute to make it easier for users to define "root-menu" * and "client-menu". */ - struct menu *parent_menu = current_menu; - current_menu = menu_create(server, id, label); + struct menu *parent_menu = ctx->menu; + ctx->menu = menu_create(ctx->server, parent_menu, id, label); if (icon_name) { - current_menu->icon_name = xstrdup(icon_name); + ctx->menu->icon_name = xstrdup(icon_name); } if (label && parent_menu) { /* * In a nested (inline) menu definition we need to * create an item pointing to the new submenu */ - current_item = item_create(parent_menu, label, true); - fill_item("icon", icon_name); - current_item->submenu = current_menu; + ctx->item = item_create(parent_menu, label, true); + fill_item(ctx, "icon", icon_name); + ctx->item->submenu = ctx->menu; } - traverse(n, server); - current_menu = parent_menu; + traverse(ctx, n); + ctx->menu = parent_menu; } else { /* * (when inside another element) creates an @@ -700,13 +704,13 @@ handle_menu_element(xmlNode *n, struct server *server) goto error; } - struct menu *menu = menu_get_by_id(server, id); + struct menu *menu = menu_get_by_id(ctx->server, id); if (!menu) { wlr_log(WLR_ERROR, "no menu with id '%s'", id); goto error; } - struct menu *iter = current_menu; + struct menu *iter = ctx->menu; while (iter) { if (iter == menu) { wlr_log(WLR_ERROR, "menus with the same id '%s' " @@ -716,9 +720,9 @@ handle_menu_element(xmlNode *n, struct server *server) iter = iter->parent; } - current_item = item_create(current_menu, menu->label, true); - fill_item("icon", menu->icon_name); - current_item->submenu = menu; + ctx->item = item_create(ctx->menu, menu->label, true); + fill_item(ctx, "icon", menu->icon_name); + ctx->item->submenu = menu; } error: free(label); @@ -729,45 +733,45 @@ error: /* This can be one of and */ static void -handle_separator_element(xmlNode *n) +handle_separator_element(struct menu_parse_context *ctx, xmlNode *n) { char *label = (char *)xmlGetProp(n, (const xmlChar *)"label"); - current_item = separator_create(current_menu, label); + ctx->item = separator_create(ctx->menu, label); free(label); } static void -xml_tree_walk(xmlNode *node, struct server *server) +xml_tree_walk(struct menu_parse_context *ctx, xmlNode *node) { for (xmlNode *n = node; n && n->name; n = n->next) { if (!strcasecmp((char *)n->name, "comment")) { continue; } if (!strcasecmp((char *)n->name, "menu")) { - handle_menu_element(n, server); + handle_menu_element(ctx, n); continue; } if (!strcasecmp((char *)n->name, "separator")) { - handle_separator_element(n); + handle_separator_element(ctx, n); continue; } if (!strcasecmp((char *)n->name, "item")) { - if (!current_menu) { + if (!ctx->menu) { wlr_log(WLR_ERROR, "ignoring without parent "); continue; } - in_item = true; - traverse(n, server); - in_item = false; + ctx->in_item = true; + traverse(ctx, n); + ctx->in_item = false; continue; } - traverse(n, server); + traverse(ctx, n); } } static bool -parse_buf(struct server *server, struct buf *buf) +parse_buf(struct menu_parse_context *ctx, struct buf *buf) { int options = 0; xmlDoc *d = xmlReadMemory(buf->data, buf->len, NULL, NULL, options); @@ -775,7 +779,7 @@ parse_buf(struct server *server, struct buf *buf) wlr_log(WLR_ERROR, "xmlParseMemory()"); return false; } - xml_tree_walk(xmlDocGetRootElement(d), server); + xml_tree_walk(ctx, xmlDocGetRootElement(d)); xmlFreeDoc(d); xmlCleanupParser(); return true; @@ -801,7 +805,8 @@ parse_stream(struct server *server, FILE *stream) buf_add(&b, line); } free(line); - parse_buf(server, &b); + struct menu_parse_context ctx = {.server = server}; + parse_buf(&ctx, &b); buf_reset(&b); } @@ -922,7 +927,7 @@ static void init_client_send_to_menu(struct server *server) { /* Just create placeholder. Contents will be created when launched */ - menu_create(server, "client-send-to-menu", ""); + menu_create(server, NULL, "client-send-to-menu", ""); } /* @@ -941,19 +946,21 @@ update_client_send_to_menu(struct server *server) reset_menu(menu); + struct menu_parse_context ctx = {.server = server}; struct workspace *workspace; wl_list_for_each(workspace, &server->workspaces.all, link) { if (workspace == server->workspaces.current) { char *label = strdup_printf(">%s<", workspace->name); - current_item = item_create(menu, label, + ctx.item = item_create(menu, label, /*show arrow*/ false); free(label); } else { - current_item = item_create(menu, workspace->name, /*show arrow*/ false); + ctx.item = item_create(menu, workspace->name, + /*show arrow*/ false); } - fill_item("name.action", "SendToDesktop"); - fill_item("to.action", workspace->name); + fill_item(&ctx, "name.action", "SendToDesktop"); + fill_item(&ctx, "to.action", workspace->name); } menu_create_scene(menu); @@ -963,7 +970,7 @@ static void init_client_list_combined_menu(struct server *server) { /* Just create placeholder. Contents will be created when launched */ - menu_create(server, "client-list-combined-menu", ""); + menu_create(server, NULL, "client-list-combined-menu", ""); } /* @@ -982,6 +989,7 @@ update_client_list_combined_menu(struct server *server) reset_menu(menu); + struct menu_parse_context ctx = {.server = server}; struct workspace *workspace; struct view *view; struct buf buffer = BUF_INIT; @@ -989,7 +997,7 @@ update_client_list_combined_menu(struct server *server) wl_list_for_each(workspace, &server->workspaces.all, link) { buf_add_fmt(&buffer, workspace == server->workspaces.current ? ">%s<" : "%s", workspace->name); - current_item = separator_create(menu, buffer.data); + ctx.item = separator_create(menu, buffer.data); buf_clear(&buffer); wl_list_for_each(view, &server->views, link) { @@ -1004,17 +1012,19 @@ update_client_list_combined_menu(struct server *server) } buf_add(&buffer, title); - current_item = item_create(menu, buffer.data, /*show arrow*/ false); - current_item->client_list_view = view; - fill_item("name.action", "Focus"); - fill_item("name.action", "Raise"); + ctx.item = item_create(menu, buffer.data, + /*show arrow*/ false); + ctx.item->client_list_view = view; + fill_item(&ctx, "name.action", "Focus"); + fill_item(&ctx, "name.action", "Raise"); buf_clear(&buffer); menu->has_icons = true; } } - current_item = item_create(menu, _("Go there..."), /*show arrow*/ false); - fill_item("name.action", "GoToDesktop"); - fill_item("to.action", workspace->name); + ctx.item = item_create(menu, _("Go there..."), + /*show arrow*/ false); + fill_item(&ctx, "name.action", "GoToDesktop"); + fill_item(&ctx, "to.action", workspace->name); } buf_reset(&buffer); menu_create_scene(menu); @@ -1027,19 +1037,19 @@ init_rootmenu(struct server *server) /* Default menu if no menu.xml found */ if (!menu) { - current_menu = NULL; - menu = menu_create(server, "root-menu", ""); + struct menu_parse_context ctx = {.server = server}; + menu = menu_create(server, NULL, "root-menu", ""); - current_item = item_create(menu, _("Terminal"), false); - fill_item("name.action", "Execute"); - fill_item("command.action", "lab-sensible-terminal"); + ctx.item = item_create(menu, _("Terminal"), false); + fill_item(&ctx, "name.action", "Execute"); + fill_item(&ctx, "command.action", "lab-sensible-terminal"); - current_item = separator_create(menu, NULL); + ctx.item = separator_create(menu, NULL); - current_item = item_create(menu, _("Reconfigure"), false); - fill_item("name.action", "Reconfigure"); - current_item = item_create(menu, _("Exit"), false); - fill_item("name.action", "Exit"); + ctx.item = item_create(menu, _("Reconfigure"), false); + fill_item(&ctx, "name.action", "Reconfigure"); + ctx.item = item_create(menu, _("Exit"), false); + fill_item(&ctx, "name.action", "Exit"); } } @@ -1050,43 +1060,44 @@ init_windowmenu(struct server *server) /* Default menu if no menu.xml found */ if (!menu) { - current_menu = NULL; - menu = menu_create(server, "client-menu", ""); - current_item = item_create(menu, _("Minimize"), false); - fill_item("name.action", "Iconify"); - current_item = item_create(menu, _("Maximize"), false); - fill_item("name.action", "ToggleMaximize"); - current_item = item_create(menu, _("Fullscreen"), false); - fill_item("name.action", "ToggleFullscreen"); - current_item = item_create(menu, _("Roll Up/Down"), false); - fill_item("name.action", "ToggleShade"); - current_item = item_create(menu, _("Decorations"), false); - fill_item("name.action", "ToggleDecorations"); - current_item = item_create(menu, _("Always on Top"), false); - fill_item("name.action", "ToggleAlwaysOnTop"); + struct menu_parse_context ctx = {.server = server}; + menu = menu_create(server, NULL, "client-menu", ""); + ctx.item = item_create(menu, _("Minimize"), false); + fill_item(&ctx, "name.action", "Iconify"); + ctx.item = item_create(menu, _("Maximize"), false); + fill_item(&ctx, "name.action", "ToggleMaximize"); + ctx.item = item_create(menu, _("Fullscreen"), false); + fill_item(&ctx, "name.action", "ToggleFullscreen"); + ctx.item = item_create(menu, _("Roll Up/Down"), false); + fill_item(&ctx, "name.action", "ToggleShade"); + ctx.item = item_create(menu, _("Decorations"), false); + fill_item(&ctx, "name.action", "ToggleDecorations"); + ctx.item = item_create(menu, _("Always on Top"), false); + fill_item(&ctx, "name.action", "ToggleAlwaysOnTop"); /* Workspace sub-menu */ - struct menu *workspace_menu = menu_create(server, "workspaces", ""); - current_item = item_create(workspace_menu, _("Move Left"), false); + struct menu *workspace_menu = + menu_create(server, NULL, "workspaces", ""); + ctx.item = item_create(workspace_menu, _("Move Left"), false); /* * is true by default so * GoToDesktop will be called as part of the action. */ - fill_item("name.action", "SendToDesktop"); - fill_item("to.action", "left"); - current_item = item_create(workspace_menu, _("Move Right"), false); - fill_item("name.action", "SendToDesktop"); - fill_item("to.action", "right"); - current_item = separator_create(workspace_menu, ""); - current_item = item_create(workspace_menu, + fill_item(&ctx, "name.action", "SendToDesktop"); + fill_item(&ctx, "to.action", "left"); + ctx.item = item_create(workspace_menu, _("Move Right"), false); + fill_item(&ctx, "name.action", "SendToDesktop"); + fill_item(&ctx, "to.action", "right"); + ctx.item = separator_create(workspace_menu, ""); + ctx.item = item_create(workspace_menu, _("Always on Visible Workspace"), false); - fill_item("name.action", "ToggleOmnipresent"); + fill_item(&ctx, "name.action", "ToggleOmnipresent"); - current_item = item_create(menu, _("Workspace"), true); - current_item->submenu = workspace_menu; + ctx.item = item_create(menu, _("Workspace"), true); + ctx.item->submenu = workspace_menu; - current_item = item_create(menu, _("Close"), false); - fill_item("name.action", "Close"); + ctx.item = item_create(menu, _("Close"), false); + fill_item(&ctx, "name.action", "Close"); } if (wl_list_length(&rc.workspace_config.workspaces) == 1) { @@ -1173,11 +1184,6 @@ menu_finish(struct server *server) wl_list_for_each_safe(menu, tmp_menu, &server->menus, link) { menu_free(menu); } - - /* Reset state vars for starting fresh when Reload is triggered */ - current_item = NULL; - current_item_action = NULL; - current_menu = NULL; } void @@ -1333,19 +1339,18 @@ static void create_pipe_menu(struct menu_pipe_context *ctx) { struct server *server = ctx->pipemenu->server; - struct menu *old_current_menu = current_menu; - current_menu = ctx->pipemenu; - if (!parse_buf(server, &ctx->buf)) { - goto restore_menus; + struct menu_parse_context parse_ctx = { + .server = server, + .menu = ctx->pipemenu, + }; + if (!parse_buf(&parse_ctx, &ctx->buf)) { + return; } /* TODO: apply validate() only for generated pipemenus */ validate(server); /* Finally open the new submenu tree */ open_menu(ctx->pipemenu, ctx->anchor_rect); - -restore_menus: - current_menu = old_current_menu; } static void From 0d9384de743857992a09a103b3c45f9f8f54ece7 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sun, 27 Jul 2025 08:00:10 +0900 Subject: [PATCH 018/248] style: remove newlines after wl_list_for_each_reverse() --- src/xdg.c | 3 +-- src/xwayland.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/xdg.c b/src/xdg.c index a2fbf1b8..8b041361 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -557,8 +557,7 @@ xdg_toplevel_view_append_children(struct view *self, struct wl_array *children) struct wlr_xdg_toplevel *toplevel = xdg_toplevel_from_view(self); struct view *view; - wl_list_for_each_reverse(view, &self->server->views, link) - { + wl_list_for_each_reverse(view, &self->server->views, link) { if (view == self) { continue; } diff --git a/src/xwayland.c b/src/xwayland.c index f156e0a3..a3b85dc7 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -980,8 +980,7 @@ xwayland_view_append_children(struct view *self, struct wl_array *children) struct wlr_xwayland_surface *surface = xwayland_surface_from_view(self); struct view *view; - wl_list_for_each_reverse(view, &self->server->views, link) - { + wl_list_for_each_reverse(view, &self->server->views, link) { if (view == self) { continue; } From f7702af9e4d3e665796d48ba55b07d8a1c4547b0 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Sun, 27 Jul 2025 15:55:43 +0200 Subject: [PATCH 019/248] src/xdg.c: prevent interacting with un-initialized xdg toplevels after unmap Fixes: #2937 Fixes: #2944 Originally-Reported-By: tranzystorekk via IRC --- src/xdg.c | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/xdg.c b/src/xdg.c index 8b041361..5621e07e 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -488,6 +488,8 @@ xdg_toplevel_view_configure(struct view *view, struct wlr_box geo) { uint32_t serial = 0; + struct wlr_xdg_toplevel *toplevel = xdg_toplevel_from_view(view); + /* * We do not need to send a configure request unless the size * changed (wayland has no notion of a global position). If the @@ -496,8 +498,20 @@ xdg_toplevel_view_configure(struct view *view, struct wlr_box geo) */ if (geo.width != view->pending.width || geo.height != view->pending.height) { - serial = wlr_xdg_toplevel_set_size(xdg_toplevel_from_view(view), - geo.width, geo.height); + if (toplevel->base->initialized) { + serial = wlr_xdg_toplevel_set_size(toplevel, geo.width, geo.height); + } else { + /* + * This may happen, for example, when a panel resizes because a + * foreign-toplevel has been destroyed. This would then trigger + * a call to desktop_arrange_all_views() which in turn explicitly + * also tries to configure unmapped surfaces. This is fine when + * trying to resize surfaces before they are mapped but it will + * also try to resize surfaces which have been unmapped but their + * associated struct view has not been destroyed yet. + */ + wlr_log(WLR_DEBUG, "Preventing configure of uninitialized surface"); + } } view->pending = geo; @@ -519,6 +533,10 @@ xdg_toplevel_view_close(struct view *view) static void xdg_toplevel_view_maximize(struct view *view, enum view_axis maximized) { + if (!xdg_toplevel_from_view(view)->base->initialized) { + wlr_log(WLR_DEBUG, "Prevented maximize notification for a non-intialized view"); + return; + } uint32_t serial = wlr_xdg_toplevel_set_maximized( xdg_toplevel_from_view(view), maximized == VIEW_AXIS_BOTH); if (serial > 0) { @@ -578,6 +596,10 @@ xdg_toplevel_view_append_children(struct view *self, struct wl_array *children) static void xdg_toplevel_view_set_activated(struct view *view, bool activated) { + if (!xdg_toplevel_from_view(view)->base->initialized) { + wlr_log(WLR_DEBUG, "Prevented activating a non-intialized view"); + return; + } uint32_t serial = wlr_xdg_toplevel_set_activated( xdg_toplevel_from_view(view), activated); if (serial > 0) { @@ -588,6 +610,10 @@ xdg_toplevel_view_set_activated(struct view *view, bool activated) static void xdg_toplevel_view_set_fullscreen(struct view *view, bool fullscreen) { + if (!xdg_toplevel_from_view(view)->base->initialized) { + wlr_log(WLR_DEBUG, "Prevented fullscreening a non-intialized view"); + return; + } uint32_t serial = wlr_xdg_toplevel_set_fullscreen( xdg_toplevel_from_view(view), fullscreen); if (serial > 0) { @@ -603,6 +629,11 @@ xdg_toplevel_view_notify_tiled(struct view *view) return; } + if (!xdg_toplevel_from_view(view)->base->initialized) { + wlr_log(WLR_DEBUG, "Prevented tiling notification for a non-intialized view"); + return; + } + enum wlr_edges edge = WLR_EDGE_NONE; bool want_edge = rc.snap_tiling_events_mode & LAB_TILING_EVENTS_EDGE; From 73095f75cbec1eee6dc1b8db76b4b8b0d99c954b Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Thu, 24 Jul 2025 01:18:36 +0900 Subject: [PATCH 020/248] Remove seat->nr_inhibited_keybind_views --- include/labwc.h | 3 --- src/input/keyboard.c | 3 +-- src/view.c | 6 ------ 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index 5f4ae133..fbeb992b 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -129,9 +129,6 @@ struct seat { struct wlr_pointer_constraint_v1 *current_constraint; - /* In support for ToggleKeybinds */ - uint32_t nr_inhibited_keybind_views; - /* Used to hide the workspace OSD after switching workspaces */ struct wl_event_source *workspace_osd_timer; bool workspace_osd_shown_by_modifier; diff --git a/src/input/keyboard.c b/src/input/keyboard.c index 17a21395..676366d9 100644 --- a/src/input/keyboard.c +++ b/src/input/keyboard.c @@ -211,8 +211,7 @@ match_keybinding_for_sym(struct server *server, uint32_t modifiers, if (modifiers ^ keybind->modifiers) { continue; } - if (server->seat.nr_inhibited_keybind_views - && server->active_view + if (server->active_view && server->active_view->inhibits_keybinds && !actions_contain_toggle_keybinds(&keybind->actions)) { continue; diff --git a/src/view.c b/src/view.c index 1a9d92cc..bc3cf35c 100644 --- a/src/view.c +++ b/src/view.c @@ -2437,11 +2437,6 @@ view_toggle_keybinds(struct view *view) { assert(view); view->inhibits_keybinds = !view->inhibits_keybinds; - if (view->inhibits_keybinds) { - view->server->seat.nr_inhibited_keybind_views++; - } else { - view->server->seat.nr_inhibited_keybind_views--; - } if (view->ssd_enabled) { ssd_enable_keybind_inhibit_indicator(view->ssd, @@ -2621,7 +2616,6 @@ view_destroy(struct view *view) if (view->inhibits_keybinds) { view->inhibits_keybinds = false; - server->seat.nr_inhibited_keybind_views--; } osd_on_view_destroy(view); From 024ab280a055ffc3666649a7be03fe804155de37 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Thu, 24 Jul 2025 11:08:03 +0900 Subject: [PATCH 021/248] cursor: also toggle mousebinds with ToggleKeybinds Mousebinds can still be applied when the cursor is over their decoration --- docs/labwc-actions.5.scd | 8 ++++---- include/view.h | 3 ++- src/input/cursor.c | 12 ++++++++++++ src/input/keyboard.c | 4 +--- src/view.c | 11 +++++++---- 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/docs/labwc-actions.5.scd b/docs/labwc-actions.5.scd index 52a3d2fb..604a8468 100644 --- a/docs/labwc-actions.5.scd +++ b/docs/labwc-actions.5.scd @@ -227,10 +227,10 @@ Actions are used in menus and keyboard/mouse bindings. window. ** - Stop handling keybinds other than ToggleKeybinds itself. - This can be used to allow A-Tab and similar keybinds to be delivered - to Virtual Machines, VNC clients or nested compositors. - A second call will restore all original keybinds. + Stop handling keybinds/mousebinds other than ToggleKeybinds itself. + This can be used to allow A-Tab and similar keybinds/mousebinds to be + delivered to Virtual Machines, VNC clients or nested compositors. + A second call will restore all original keybinds/mousebinds. This action will only affect the window that had keyboard focus when the binding was executed. Thus when switching to another window, all diff --git a/include/view.h b/include/view.h index 1e54f898..5e1c5baf 100644 --- a/include/view.h +++ b/include/view.h @@ -229,7 +229,7 @@ struct view { bool visible_on_all_workspaces; enum view_edge tiled; uint32_t edges_visible; /* enum wlr_edges bitset */ - bool inhibits_keybinds; + bool inhibits_keybinds; /* also inhibits mousebinds */ xkb_layout_index_t keyboard_layout; /* Pointer to an output owned struct region, may be NULL */ @@ -526,6 +526,7 @@ void mappable_connect(struct mappable *mappable, struct wlr_surface *surface, void mappable_disconnect(struct mappable *mappable); void view_toggle_keybinds(struct view *view); +bool view_inhibits_actions(struct view *view, struct wl_list *actions); void view_set_activated(struct view *view, bool activated); void view_set_output(struct view *view, struct output *output); diff --git a/src/input/cursor.c b/src/input/cursor.c index d55e6bc0..6ce81dab 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -610,6 +610,10 @@ cursor_process_motion(struct server *server, uint32_t time, double *sx, double * struct mousebind *mousebind; wl_list_for_each(mousebind, &rc.mousebinds, link) { + if (ctx.type == LAB_SSD_CLIENT + && view_inhibits_actions(ctx.view, &mousebind->actions)) { + continue; + } if (mousebind->mouse_event == MOUSE_ACTION_DRAG && mousebind->pressed_in_context) { /* @@ -949,6 +953,10 @@ process_release_mousebinding(struct server *server, uint32_t modifiers = keyboard_get_all_modifiers(&server->seat); wl_list_for_each(mousebind, &rc.mousebinds, link) { + if (ctx->type == LAB_SSD_CLIENT + && view_inhibits_actions(ctx->view, &mousebind->actions)) { + continue; + } if (ssd_part_contains(mousebind->context, ctx->type) && mousebind->button == button && modifiers == mousebind->modifiers) { @@ -1016,6 +1024,10 @@ process_press_mousebinding(struct server *server, struct cursor_context *ctx, uint32_t modifiers = keyboard_get_all_modifiers(&server->seat); wl_list_for_each(mousebind, &rc.mousebinds, link) { + if (ctx->type == LAB_SSD_CLIENT + && view_inhibits_actions(ctx->view, &mousebind->actions)) { + continue; + } if (ssd_part_contains(mousebind->context, ctx->type) && mousebind->button == button && modifiers == mousebind->modifiers) { diff --git a/src/input/keyboard.c b/src/input/keyboard.c index 676366d9..0739529d 100644 --- a/src/input/keyboard.c +++ b/src/input/keyboard.c @@ -211,9 +211,7 @@ match_keybinding_for_sym(struct server *server, uint32_t modifiers, if (modifiers ^ keybind->modifiers) { continue; } - if (server->active_view - && server->active_view->inhibits_keybinds - && !actions_contain_toggle_keybinds(&keybind->actions)) { + if (view_inhibits_actions(server->active_view, &keybind->actions)) { continue; } if (sym == XKB_KEY_NoSymbol) { diff --git a/src/view.c b/src/view.c index bc3cf35c..3a6fa0bd 100644 --- a/src/view.c +++ b/src/view.c @@ -4,6 +4,7 @@ #include #include #include +#include "action.h" #include "buffer.h" #include "common/box.h" #include "common/list.h" @@ -2444,6 +2445,12 @@ view_toggle_keybinds(struct view *view) } } +bool +view_inhibits_actions(struct view *view, struct wl_list *actions) +{ + return view && view->inhibits_keybinds && !actions_contain_toggle_keybinds(actions); +} + void mappable_connect(struct mappable *mappable, struct wlr_surface *surface, wl_notify_func_t notify_map, wl_notify_func_t notify_unmap) @@ -2614,10 +2621,6 @@ view_destroy(struct view *view) zfree(view->tiled_region_evacuate); } - if (view->inhibits_keybinds) { - view->inhibits_keybinds = false; - } - osd_on_view_destroy(view); undecorate(view); From b3b6715cdf678549116edae25827dcb42bac06fe Mon Sep 17 00:00:00 2001 From: Weblate Date: Sat, 19 Jul 2025 20:47:42 +0200 Subject: [PATCH 022/248] Translation updates from weblate Co-authored-by: Abdullah Albaroty Co-authored-by: Hugo Carvalho Co-authored-by: Moo Co-authored-by: Weblate Co-authored-by: kmephistoh Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/ar/ Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/lt/ Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/pt/ Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/zh_CN/ Translation: Labwc/labwc --- po/ar.po | 10 ++++++---- po/lt.po | 11 +++++++---- po/pt.po | 7 ++++--- po/zh_CN.po | 9 +++++---- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/po/ar.po b/po/ar.po index 94e860f8..46ec2870 100644 --- a/po/ar.po +++ b/po/ar.po @@ -8,14 +8,16 @@ msgstr "" "Project-Id-Version: labwc\n" "Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" "POT-Creation-Date: 2024-09-19 21:09+1000\n" -"PO-Revision-Date: 2024-09-23 20:01+0000\n" +"PO-Revision-Date: 2025-07-17 07:22+0000\n" "Last-Translator: Abdullah Albaroty \n" -"Language-Team: Arabic \n" +"Language-Team: Arabic \n" "Language: ar\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n" +"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " +"&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n" "X-Generator: Weblate 4.2.1\n" #: src/menu/menu.c:1016 @@ -24,7 +26,7 @@ msgstr "اذهب هناك..." #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "" +msgstr "الطرفية" #: src/menu/menu.c:1040 msgid "Reconfigure" diff --git a/po/lt.po b/po/lt.po index 9c734b53..3f9d6113 100644 --- a/po/lt.po +++ b/po/lt.po @@ -8,14 +8,17 @@ msgstr "" "Project-Id-Version: labwc\n" "Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" "POT-Creation-Date: 2024-09-19 21:09+1000\n" -"PO-Revision-Date: 2024-11-13 21:01+0000\n" +"PO-Revision-Date: 2025-07-19 18:47+0000\n" "Last-Translator: Moo \n" -"Language-Team: Lithuanian \n" +"Language-Team: Lithuanian \n" "Language: lt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n % 10 == 1 && (n % 100 < 11 || n % 100 > 19)) ? 0 : ((n % 10 >= 2 && n % 10 <= 9 && (n % 100 < 11 || n % 100 > 19)) ? 1 : 2);\n" +"Plural-Forms: nplurals=3; plural=(n % 10 == 1 && (n % 100 < 11 || n % 100 > " +"19)) ? 0 : ((n % 10 >= 2 && n % 10 <= 9 && (n % 100 < 11 || n % 100 > 19)) ? " +"1 : 2);\n" "X-Generator: Weblate 4.2.1\n" #: src/menu/menu.c:1016 @@ -24,7 +27,7 @@ msgstr "Eiti ten..." #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "" +msgstr "Terminalas" #: src/menu/menu.c:1040 msgid "Reconfigure" diff --git a/po/pt.po b/po/pt.po index 0bb6ff3f..6d70076d 100644 --- a/po/pt.po +++ b/po/pt.po @@ -8,9 +8,10 @@ msgstr "" "Project-Id-Version: labwc\n" "Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" "POT-Creation-Date: 2024-09-19 21:09+1000\n" -"PO-Revision-Date: 2024-09-21 20:01+0000\n" +"PO-Revision-Date: 2025-07-13 08:01+0000\n" "Last-Translator: Hugo Carvalho \n" -"Language-Team: Portuguese \n" +"Language-Team: Portuguese \n" "Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -24,7 +25,7 @@ msgstr "Ir lá..." #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "" +msgstr "Terminal" #: src/menu/menu.c:1040 msgid "Reconfigure" diff --git a/po/zh_CN.po b/po/zh_CN.po index c872c2d6..7662cecb 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -8,9 +8,10 @@ msgstr "" "Project-Id-Version: labwc\n" "Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" "POT-Creation-Date: 2024-09-19 21:09+1000\n" -"PO-Revision-Date: 2025-04-30 08:01+0000\n" -"Last-Translator: knm100 \n" -"Language-Team: Chinese (Simplified) \n" +"PO-Revision-Date: 2025-07-15 05:27+0000\n" +"Last-Translator: kmephistoh \n" +"Language-Team: Chinese (Simplified) \n" "Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -24,7 +25,7 @@ msgstr "前往..." #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "" +msgstr "终端" #: src/menu/menu.c:1040 msgid "Reconfigure" From bdaf85eda11befa5882f6dcd17786b393022ef76 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 28 Jul 2025 13:54:10 +0900 Subject: [PATCH 023/248] Fix some warnings from clang-tidy Notably this fixes a possible null pointer dereference in warp_cursor() when output_nearest_to_cursor() returns null. --- src/action.c | 11 +++++------ src/input/cursor.c | 1 + src/xdg.c | 3 --- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/action.c b/src/action.c index 6df68979..8bde5098 100644 --- a/src/action.c +++ b/src/action.c @@ -823,8 +823,9 @@ get_target_output(struct output *output, struct server *server, } static void -warp_cursor(struct view *view, struct output *output, const char *to, const char *x, const char *y) +warp_cursor(struct server *server, struct view *view, const char *to, const char *x, const char *y) { + struct output *output = output_nearest_to_cursor(server); struct wlr_box target_area = {0}; int goto_x; int goto_y; @@ -855,8 +856,8 @@ warp_cursor(struct view *view, struct output *output, const char *to, const char target_area.y + target_area.height + offset_y; } - wlr_cursor_warp(output->server->seat.cursor, NULL, goto_x, goto_y); - cursor_update_focus(output->server); + wlr_cursor_warp(server->seat.cursor, NULL, goto_x, goto_y); + cursor_update_focus(server); } void @@ -1352,9 +1353,7 @@ actions_run(struct view *activator, struct server *server, const char *to = action_get_str(action, "to", "output"); const char *x = action_get_str(action, "x", "center"); const char *y = action_get_str(action, "y", "center"); - struct output *output = output_nearest_to_cursor(server); - - warp_cursor(view, output, to, x, y); + warp_cursor(server, view, to, x, y); break; } case ACTION_TYPE_HIDE_CURSOR: diff --git a/src/input/cursor.c b/src/input/cursor.c index 6ce81dab..a0d9b3fd 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -302,6 +302,7 @@ process_cursor_resize(struct server *server, uint32_t time) static uint32_t last_resize_time = 0; static struct view *last_resize_view = NULL; + assert(server->grabbed_view); if (server->grabbed_view == last_resize_view) { int32_t refresh = 0; if (output_is_usable(last_resize_view->output)) { diff --git a/src/xdg.c b/src/xdg.c index 5621e07e..be8cfc20 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -1054,9 +1054,6 @@ static void handle_xdg_toplevel_icon_set_icon(struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel_icon_manager_v1_set_icon_event *event = data; - - struct server *server = - wl_container_of(listener, server, xdg_toplevel_icon_set_icon); struct wlr_xdg_surface *xdg_surface = event->toplevel->base; struct view *view = xdg_surface->data; assert(view); From 69b912219475653be0d190ae9509240c6fb69a69 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Mon, 28 Jul 2025 20:48:56 +0100 Subject: [PATCH 024/248] NEWS.md: update with notes on wlroots changes --- NEWS.md | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/NEWS.md b/NEWS.md index 46b27395..e2adec14 100644 --- a/NEWS.md +++ b/NEWS.md @@ -36,13 +36,28 @@ The format is based on [Keep a Changelog] | 2021-04-15 | [0.2.0] | 0.13.0 | 5011 | | 2021-03-05 | [0.1.0] | 0.12.0 | 4627 | -## [0.9.0] +## Notes on wlroots-0.19 -The main focus has been to port labwc to wlroots 0.19 [#2388] and fix associated -issues. Special thanks to @Consolatis @jlindgren90 for this. - -There is a regression warning worth noting for the switch to wlroots 0.19: +There are some regression warnings worth noting for the switch to wlroots 0.19: +- The DRM backend now destroys/recreates outputs on VT switch and in some cases + on suspend/resume too. The reason for this change was that (i) the KMS state + is undefined when a VT is switched away; and (ii) the previous outputs had + issues with restoration, particularly when the output configuration had + changed whilst switched away. This change causes two issues for users: + - Some layer-shell clients do not re-appear on output re-connection, or may + appear on a different output. Whilst this has always been the case, it will + now also happen in said situations. We recommend layer-shell clients to + handle the new-output and surface-destroy signals to achieve desired + behaviours. + - Some Gtk clients issue critical warnings as they assume that at least one + output is always available. This will be fixed in `Gtk-3.24.50`. It is + believed to be a harmless warning, but it can be avoided by running labwc + with the environment variable `LABWC_FALLBACK_OUTPUT=NOOP-fallback` to + temporarily create a fallback-output when the last physical display + disconnects. [#2914] [#2939] [wlroots-4878] [gtk-8792] +- Due to a single-pixel protocol issue, `waylock` and `chayang` do not work. + This will be fixed in `wlroots-0.19.1`. [#2943] [wlroots-5098] - Menu item can no longer be activated in any Gtk applications with a single press-drag-release mouse action. For context: This is due to ambiguity in the specifications and contrary implementations. For example, Gtk applications are @@ -54,6 +69,17 @@ There is a regression warning worth noting for the switch to wlroots 0.19: around a bug on the wlroots side which is expected to be fixed in wlroots `0.19.1` [#2887] +[wlroots-4878]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4878 +[wlroots-5098]:https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5098 +[gtk-8792]: https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/8792 + +## [unreleased] + +## [0.9.0] + +The main focus has been to port labwc to wlroots 0.19 [#2388] and fix associated +issues. Special thanks to @Consolatis @jlindgren90 for this. + ### Added - Add client `lab-sensible-terminal` and add a `Terminal` entry to the default @@ -2548,3 +2574,6 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16 [#2886]: https://github.com/labwc/labwc/pull/2886 [#2887]: https://github.com/labwc/labwc/pull/2887 [#2891]: https://github.com/labwc/labwc/pull/2891 +[#2914]: https://github.com/labwc/labwc/pull/2914 +[#2939]: https://github.com/labwc/labwc/pull/2939 +[#2943]: https://github.com/labwc/labwc/pull/2943 From c9b576982d929f029fb1867240e63876f1a78b77 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Mon, 28 Jul 2025 01:03:22 -0400 Subject: [PATCH 025/248] include: add missing header dependencies Ensure that headers compile correctly regardless of include order. --- include/common/graphic-helpers.h | 2 ++ include/config/touch.h | 2 +- include/input/tablet.h | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/include/common/graphic-helpers.h b/include/common/graphic-helpers.h index 3fdde6ad..886a4c92 100644 --- a/include/common/graphic-helpers.h +++ b/include/common/graphic-helpers.h @@ -2,6 +2,8 @@ #ifndef LABWC_GRAPHIC_HELPERS_H #define LABWC_GRAPHIC_HELPERS_H +#include +#include #include struct wlr_fbox; diff --git a/include/config/touch.h b/include/config/touch.h index 12f7b032..1a05670e 100644 --- a/include/config/touch.h +++ b/include/config/touch.h @@ -2,7 +2,7 @@ #ifndef LABWC_TOUCH_CONFIG_H #define LABWC_TOUCH_CONFIG_H -#include +#include #include struct touch_config_entry { diff --git a/include/input/tablet.h b/include/input/tablet.h index 29aeb72b..d90fbdb4 100644 --- a/include/input/tablet.h +++ b/include/input/tablet.h @@ -4,6 +4,7 @@ #include #include +#include "config/tablet-tool.h" struct seat; struct wlr_device; From 31d42b50e2eb319c332fb196e05b34aaf7d465cd Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Mon, 28 Jul 2025 01:02:01 -0400 Subject: [PATCH 026/248] src: include primary header first This is a common practice in C projects, which simply enforces that each header must compile cleanly without implicit dependencies on other headers (see also the previous commit). --- src/action.c | 2 +- src/buffer.c | 2 +- src/common/box.c | 2 +- src/common/buf.c | 2 +- src/common/dir.c | 2 +- src/common/direction.c | 2 +- src/common/fd-util.c | 3 +-- src/common/file-helpers.c | 2 +- src/common/font.c | 2 +- src/common/graphic-helpers.c | 2 +- src/common/lab-scene-rect.c | 2 +- src/common/match.c | 2 +- src/common/mem.c | 2 +- src/common/nodename.c | 2 +- src/common/parse-bool.c | 2 +- src/common/parse-double.c | 2 +- src/common/scaled-font-buffer.c | 2 +- src/common/scaled-icon-buffer.c | 2 +- src/common/scaled-img-buffer.c | 2 +- src/common/scaled-scene-buffer.c | 2 +- src/common/scene-helpers.c | 2 +- src/common/set.c | 2 +- src/common/spawn.c | 2 +- src/common/string-helpers.c | 2 +- src/common/surface-helpers.c | 2 +- src/config/keybind.c | 2 +- src/config/libinput.c | 2 +- src/config/mousebind.c | 2 +- src/config/rcxml.c | 2 +- src/config/session.c | 2 +- src/config/tablet-tool.c | 2 +- src/config/tablet.c | 2 +- src/config/touch.c | 1 + src/debug.c | 2 +- src/desktop-entry.c | 2 +- src/dnd.c | 2 +- src/edges.c | 2 +- src/idle.c | 2 +- src/img/img-png.c | 2 +- src/img/img-svg.c | 2 +- src/img/img-xbm.c | 2 +- src/img/img-xpm.c | 2 +- src/img/img.c | 2 +- src/input/cursor.c | 1 + src/input/gestures.c | 2 +- src/input/ime.c | 2 +- src/input/input.c | 2 +- src/input/key-state.c | 2 +- src/input/keyboard.c | 2 +- src/input/tablet-pad.c | 2 +- src/input/tablet.c | 2 +- src/input/touch.c | 2 +- src/layers.c | 2 +- src/magnifier.c | 2 +- src/menu/menu.c | 2 +- src/node.c | 2 +- src/osd.c | 2 +- src/output-state.c | 2 +- src/output-virtual.c | 2 +- src/overlay.c | 2 +- src/placement.c | 2 +- src/protocols/transaction-addon.c | 2 +- src/regions.c | 2 +- src/resistance.c | 2 +- src/resize-outlines.c | 2 +- src/session-lock.c | 1 + src/snap-constraints.c | 2 +- src/snap.c | 2 +- src/theme.c | 2 +- src/view-impl-common.c | 2 +- src/view.c | 2 +- src/window-rules.c | 2 +- src/workspaces.c | 2 +- src/xwayland.c | 2 +- 74 files changed, 74 insertions(+), 72 deletions(-) diff --git a/src/action.c b/src/action.c index 8bde5098..adcae7cf 100644 --- a/src/action.c +++ b/src/action.c @@ -1,12 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L +#include "action.h" #include #include #include #include #include #include -#include "action.h" #include "common/macros.h" #include "common/list.h" #include "common/mem.h" diff --git a/src/buffer.c b/src/buffer.c index 8a2aa202..04ca4328 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -24,12 +24,12 @@ * SOFTWARE. */ +#include "buffer.h" #include #include #include #include #include -#include "buffer.h" #include "common/box.h" #include "common/mem.h" diff --git a/src/common/box.c b/src/common/box.c index 0b1c0b44..84db600d 100644 --- a/src/common/box.c +++ b/src/common/box.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only -#include #include "common/box.h" +#include #include "common/macros.h" bool diff --git a/src/common/buf.c b/src/common/buf.c index 0fe810fc..c98fc97f 100644 --- a/src/common/buf.c +++ b/src/common/buf.c @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "common/buf.h" #include #include #include #include #include #include -#include "common/buf.h" #include "common/macros.h" #include "common/mem.h" #include "common/string-helpers.h" diff --git a/src/common/dir.c b/src/common/dir.c index 2b0016d7..7b55a023 100644 --- a/src/common/dir.c +++ b/src/common/dir.c @@ -4,13 +4,13 @@ * * Copyright Johan Malm 2020 */ +#include "common/dir.h" #include #include #include #include #include #include -#include "common/dir.h" #include "common/buf.h" #include "common/list.h" #include "common/mem.h" diff --git a/src/common/direction.c b/src/common/direction.c index fa88eee4..ba23d189 100644 --- a/src/common/direction.c +++ b/src/common/direction.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "common/direction.h" #include #include -#include "common/direction.h" #include "view.h" enum wlr_direction diff --git a/src/common/fd-util.c b/src/common/fd-util.c index 672f467e..883e2c84 100644 --- a/src/common/fd-util.c +++ b/src/common/fd-util.c @@ -1,11 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L +#include "common/fd-util.h" #include #include -#include "common/fd-util.h" - static struct rlimit original_nofile_rlimit = {0}; void diff --git a/src/common/file-helpers.c b/src/common/file-helpers.c index 2361b546..14dc6e6f 100644 --- a/src/common/file-helpers.c +++ b/src/common/file-helpers.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only -#include #include "common/file-helpers.h" +#include bool file_exists(const char *filename) diff --git a/src/common/font.c b/src/common/font.c index 9599c846..8823f445 100644 --- a/src/common/font.c +++ b/src/common/font.c @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "common/font.h" #include #include #include #include #include #include -#include "common/font.h" #include "common/graphic-helpers.h" #include "common/string-helpers.h" #include "labwc.h" diff --git a/src/common/graphic-helpers.c b/src/common/graphic-helpers.c index 7a8af89a..602507a9 100644 --- a/src/common/graphic-helpers.c +++ b/src/common/graphic-helpers.c @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "common/graphic-helpers.h" #include #include /* g_ascii_strcasecmp */ #include -#include "common/graphic-helpers.h" #include "common/macros.h" #include "xcolor-table.h" diff --git a/src/common/lab-scene-rect.c b/src/common/lab-scene-rect.c index bc23422d..2d0c7408 100644 --- a/src/common/lab-scene-rect.c +++ b/src/common/lab-scene-rect.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "common/lab-scene-rect.h" #include #include -#include "common/lab-scene-rect.h" #include "common/mem.h" struct border_scene { diff --git a/src/common/match.c b/src/common/match.c index 9bc3cf8b..2293a71c 100644 --- a/src/common/match.c +++ b/src/common/match.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only -#include #include "common/match.h" +#include bool match_glob(const char *pattern, const char *string) diff --git a/src/common/mem.c b/src/common/mem.c index e63987b5..a7289ea0 100644 --- a/src/common/mem.c +++ b/src/common/mem.c @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L +#include "common/mem.h" #include #include #include -#include "common/mem.h" static void die_if_null(void *ptr) diff --git a/src/common/nodename.c b/src/common/nodename.c index 86dfd620..269e671c 100644 --- a/src/common/nodename.c +++ b/src/common/nodename.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "common/nodename.h" #include #include -#include "common/nodename.h" char * nodename(xmlNode *node, char *buf, int len) diff --git a/src/common/parse-bool.c b/src/common/parse-bool.c index dc6780f5..561da763 100644 --- a/src/common/parse-bool.c +++ b/src/common/parse-bool.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "common/parse-bool.h" #include #include -#include "common/parse-bool.h" enum three_state parse_three_state(const char *str) diff --git a/src/common/parse-double.c b/src/common/parse-double.c index db440d0e..0e32ef3e 100644 --- a/src/common/parse-double.c +++ b/src/common/parse-double.c @@ -1,10 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "common/parse-double.h" #include #include #include #include #include "common/mem.h" -#include "common/parse-double.h" struct dec_separator { int index; diff --git a/src/common/scaled-font-buffer.c b/src/common/scaled-font-buffer.c index 64584b03..8faa9495 100644 --- a/src/common/scaled-font-buffer.c +++ b/src/common/scaled-font-buffer.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L +#include "common/scaled-font-buffer.h" #include #include #include @@ -10,7 +11,6 @@ #include "common/graphic-helpers.h" #include "common/mem.h" #include "common/scaled-scene-buffer.h" -#include "common/scaled-font-buffer.h" #include "common/string-helpers.h" static struct lab_data_buffer * diff --git a/src/common/scaled-icon-buffer.c b/src/common/scaled-icon-buffer.c index bb9ca9eb..62cb301e 100644 --- a/src/common/scaled-icon-buffer.c +++ b/src/common/scaled-icon-buffer.c @@ -1,12 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L +#include "common/scaled-icon-buffer.h" #include #include #include #include "buffer.h" #include "common/macros.h" #include "common/mem.h" -#include "common/scaled-icon-buffer.h" #include "common/scaled-scene-buffer.h" #include "common/string-helpers.h" #include "config.h" diff --git a/src/common/scaled-img-buffer.c b/src/common/scaled-img-buffer.c index 817e222c..c66ada7c 100644 --- a/src/common/scaled-img-buffer.c +++ b/src/common/scaled-img-buffer.c @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L +#include "common/scaled-img-buffer.h" #include #include #include #include "buffer.h" #include "common/mem.h" -#include "common/scaled-img-buffer.h" #include "common/scaled-scene-buffer.h" #include "img/img.h" #include "node.h" diff --git a/src/common/scaled-scene-buffer.c b/src/common/scaled-scene-buffer.c index 39ac44dc..a86077f5 100644 --- a/src/common/scaled-scene-buffer.c +++ b/src/common/scaled-scene-buffer.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L +#include "common/scaled-scene-buffer.h" #include #include #include @@ -11,7 +12,6 @@ #include "common/list.h" #include "common/macros.h" #include "common/mem.h" -#include "common/scaled-scene-buffer.h" #include "node.h" /* diff --git a/src/common/scene-helpers.c b/src/common/scene-helpers.c index e40e41cc..9b787b26 100644 --- a/src/common/scene-helpers.c +++ b/src/common/scene-helpers.c @@ -1,12 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "common/scene-helpers.h" #include #include #include #include #include #include -#include "common/scene-helpers.h" #include "labwc.h" #include "magnifier.h" #include "output-state.h" diff --git a/src/common/set.c b/src/common/set.c index 64df2717..dffc9a86 100644 --- a/src/common/set.c +++ b/src/common/set.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only -#include #include "common/set.h" +#include bool lab_set_contains(struct lab_set *set, uint32_t value) diff --git a/src/common/spawn.c b/src/common/spawn.c index 6e8e7c51..51de6b4a 100644 --- a/src/common/spawn.c +++ b/src/common/spawn.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L +#include "common/spawn.h" #include #include #include @@ -9,7 +10,6 @@ #include #include #include -#include "common/spawn.h" #include "common/fd-util.h" static void diff --git a/src/common/string-helpers.c b/src/common/string-helpers.c index f94e1dd9..2d8f8de3 100644 --- a/src/common/string-helpers.c +++ b/src/common/string-helpers.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "common/string-helpers.h" #include #include #include @@ -7,7 +8,6 @@ #include #include #include "common/mem.h" -#include "common/string-helpers.h" enum str_flags { STR_FLAG_NONE = 0, diff --git a/src/common/surface-helpers.c b/src/common/surface-helpers.c index e173aa10..f13a4d6e 100644 --- a/src/common/surface-helpers.c +++ b/src/common/surface-helpers.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "common/surface-helpers.h" #include #include #include -#include "common/surface-helpers.h" struct wlr_layer_surface_v1 * subsurface_parent_layer(struct wlr_surface *wlr_surface) diff --git a/src/config/keybind.c b/src/config/keybind.c index d59b64eb..7560cb16 100644 --- a/src/config/keybind.c +++ b/src/config/keybind.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L +#include "config/keybind.h" #include #include #include @@ -8,7 +9,6 @@ #include #include "common/list.h" #include "common/mem.h" -#include "config/keybind.h" #include "config/rcxml.h" #include "labwc.h" diff --git a/src/config/libinput.c b/src/config/libinput.c index c76a9ff9..f8f48f18 100644 --- a/src/config/libinput.c +++ b/src/config/libinput.c @@ -1,10 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "config/libinput.h" #include #include #include "common/mem.h" #include "common/list.h" #include "common/string-helpers.h" -#include "config/libinput.h" #include "config/rcxml.h" static void diff --git a/src/config/mousebind.c b/src/config/mousebind.c index 19169d55..0f49b6a5 100644 --- a/src/config/mousebind.c +++ b/src/config/mousebind.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L +#include "config/mousebind.h" #include #include #include @@ -7,7 +8,6 @@ #include #include "common/list.h" #include "common/mem.h" -#include "config/mousebind.h" #include "config/rcxml.h" uint32_t diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 5dea021a..e8c4185e 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L +#include "config/rcxml.h" #include #include #include @@ -29,7 +30,6 @@ #include "config/libinput.h" #include "config/mousebind.h" #include "config/tablet.h" -#include "config/rcxml.h" #include "labwc.h" #include "osd.h" #include "regions.h" diff --git a/src/config/session.c b/src/config/session.c index 5408bbcb..4ce24c57 100644 --- a/src/config/session.c +++ b/src/config/session.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L +#include "config/session.h" #include #include #include @@ -18,7 +19,6 @@ #include "common/parse-bool.h" #include "common/spawn.h" #include "common/string-helpers.h" -#include "config/session.h" #include "labwc.h" static const char *const env_vars[] = { diff --git a/src/config/tablet-tool.c b/src/config/tablet-tool.c index 7e171410..61e23ba5 100644 --- a/src/config/tablet-tool.c +++ b/src/config/tablet-tool.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L +#include "config/tablet-tool.h" #include #include -#include "config/tablet-tool.h" enum motion tablet_parse_motion(const char *name) diff --git a/src/config/tablet.c b/src/config/tablet.c index b7d16c24..4ac52178 100644 --- a/src/config/tablet.c +++ b/src/config/tablet.c @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L +#include "config/tablet.h" #include #include #include #include #include "common/parse-double.h" -#include "config/tablet.h" #include "config/rcxml.h" #include "input/tablet-pad.h" diff --git a/src/config/touch.c b/src/config/touch.c index 0ff530e8..fde785e1 100644 --- a/src/config/touch.c +++ b/src/config/touch.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L +#include "config/touch.h" #include #include #include "common/list.h" diff --git a/src/debug.c b/src/debug.c index 4123187b..e482a357 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1,10 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "debug.h" #include #include #include "common/lab-scene-rect.h" #include "common/scene-helpers.h" #include "common/string-helpers.h" -#include "debug.h" #include "input/ime.h" #include "labwc.h" #include "node.h" diff --git a/src/desktop-entry.c b/src/desktop-entry.c index 05e0168a..6fc842a8 100644 --- a/src/desktop-entry.c +++ b/src/desktop-entry.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "desktop-entry.h" #include #include #include @@ -9,7 +10,6 @@ #include "common/macros.h" #include "common/mem.h" #include "common/string-helpers.h" -#include "desktop-entry.h" #include "img/img.h" #include "labwc.h" diff --git a/src/dnd.c b/src/dnd.c index eeca114a..b09ca261 100644 --- a/src/dnd.c +++ b/src/dnd.c @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "dnd.h" #include #include #include #include -#include "dnd.h" #include "input/cursor.h" #include "labwc.h" /* for struct seat */ #include "view.h" diff --git a/src/edges.c b/src/edges.c index 50b7e615..237991a2 100644 --- a/src/edges.c +++ b/src/edges.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "edges.h" #include #include #include @@ -8,7 +9,6 @@ #include "common/box.h" #include "common/macros.h" #include "config/rcxml.h" -#include "edges.h" #include "labwc.h" #include "view.h" #include "node.h" diff --git a/src/idle.c b/src/idle.c index 568b680f..611fe938 100644 --- a/src/idle.c +++ b/src/idle.c @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "idle.h" #include #include #include #include #include "common/mem.h" -#include "idle.h" struct lab_idle_inhibitor { struct wlr_idle_inhibitor_v1 *wlr_inhibitor; diff --git a/src/img/img-png.c b/src/img/img-png.c index 79aeafff..5e151f21 100644 --- a/src/img/img-png.c +++ b/src/img/img-png.c @@ -3,6 +3,7 @@ * Copyright (C) Johan Malm 2023 */ #define _POSIX_C_SOURCE 200809L +#include "img/img-png.h" #include #include #include @@ -12,7 +13,6 @@ #include #include "buffer.h" #include "common/string-helpers.h" -#include "img/img-png.h" /* * cairo_image_surface_create_from_png() does not gracefully handle non-png diff --git a/src/img/img-svg.c b/src/img/img-svg.c index bf671dc4..ad2d2916 100644 --- a/src/img/img-svg.c +++ b/src/img/img-svg.c @@ -3,6 +3,7 @@ * Copyright (C) Johan Malm 2023 */ #define _POSIX_C_SOURCE 200809L +#include "img/img-svg.h" #include #include #include @@ -11,7 +12,6 @@ #include #include "buffer.h" #include "common/string-helpers.h" -#include "img/img-svg.h" #include "labwc.h" RsvgHandle * diff --git a/src/img/img-xbm.c b/src/img/img-xbm.c index 7f1571e4..0a402d39 100644 --- a/src/img/img-xbm.c +++ b/src/img/img-xbm.c @@ -6,6 +6,7 @@ */ #define _POSIX_C_SOURCE 200809L +#include "img/img-xbm.h" #include #include #include @@ -14,7 +15,6 @@ #include #include #include "img/img.h" -#include "img/img-xbm.h" #include "common/grab-file.h" #include "common/mem.h" #include "common/string-helpers.h" diff --git a/src/img/img-xpm.c b/src/img/img-xpm.c index ad50bc5e..0d251b49 100644 --- a/src/img/img-xpm.c +++ b/src/img/img-xpm.c @@ -11,6 +11,7 @@ * Adapted for labwc by John Lindgren, 2024 */ +#include "img/img-xpm.h" #include #include #include @@ -21,7 +22,6 @@ #include "common/buf.h" #include "common/graphic-helpers.h" #include "common/mem.h" -#include "img/img-xpm.h" enum buf_op { op_header, op_cmap, op_body }; diff --git a/src/img/img.c b/src/img/img.c index 03b9130a..d062022e 100644 --- a/src/img/img.c +++ b/src/img/img.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "img/img.h" #include #include #include "buffer.h" @@ -9,7 +10,6 @@ #include "common/macros.h" #include "common/mem.h" #include "common/string-helpers.h" -#include "img/img.h" #include "img/img-png.h" #if HAVE_RSVG #include "img/img-svg.h" diff --git a/src/input/cursor.c b/src/input/cursor.c index a0d9b3fd..be292822 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L +#include "input/cursor.h" #include #include #include diff --git a/src/input/gestures.c b/src/input/gestures.c index 4cb94c6f..5a0365fe 100644 --- a/src/input/gestures.c +++ b/src/input/gestures.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "input/gestures.h" #include #include "common/macros.h" -#include "input/gestures.h" #include "labwc.h" #include "idle.h" diff --git a/src/input/ime.c b/src/input/ime.c index e76b2f2c..0cb656de 100644 --- a/src/input/ime.c +++ b/src/input/ime.c @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only /* Based on Sway (https://github.com/swaywm/sway) */ +#include "input/ime.h" #include #include "common/mem.h" -#include "input/ime.h" #include "node.h" #include "view.h" diff --git a/src/input/input.c b/src/input/input.c index d78dca1a..61afdc6d 100644 --- a/src/input/input.c +++ b/src/input/input.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "input/cursor.h" #include "input/input.h" +#include "input/cursor.h" #include "input/keyboard.h" void diff --git a/src/input/key-state.c b/src/input/key-state.c index 24a3f606..12992615 100644 --- a/src/input/key-state.c +++ b/src/input/key-state.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "input/key-state.h" #include #include #include @@ -6,7 +7,6 @@ #include #include #include "common/set.h" -#include "input/key-state.h" static struct lab_set pressed, bound, pressed_sent; diff --git a/src/input/keyboard.c b/src/input/keyboard.c index 0739529d..4c2a0704 100644 --- a/src/input/keyboard.c +++ b/src/input/keyboard.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L +#include "input/keyboard.h" #include #include #include @@ -10,7 +11,6 @@ #include "common/three-state.h" #include "idle.h" #include "input/ime.h" -#include "input/keyboard.h" #include "input/key-state.h" #include "labwc.h" #include "menu/menu.h" diff --git a/src/input/tablet-pad.c b/src/input/tablet-pad.c index 2453be96..19127058 100644 --- a/src/input/tablet-pad.c +++ b/src/input/tablet-pad.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "input/tablet-pad.h" #include #include #include @@ -9,7 +10,6 @@ #include "common/mem.h" #include "config/rcxml.h" #include "input/cursor.h" -#include "input/tablet-pad.h" #include "input/tablet.h" #include "labwc.h" diff --git a/src/input/tablet.c b/src/input/tablet.c index a4ec328a..c53c7d70 100644 --- a/src/input/tablet.c +++ b/src/input/tablet.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "input/tablet.h" #include #include #include @@ -12,7 +13,6 @@ #include "config/rcxml.h" #include "config/mousebind.h" #include "input/cursor.h" -#include "input/tablet.h" #include "input/tablet-pad.h" #include "labwc.h" #include "idle.h" diff --git a/src/input/touch.c b/src/input/touch.c index ec6a2bf7..2cc79784 100644 --- a/src/input/touch.c +++ b/src/input/touch.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "input/touch.h" #include #include #include @@ -6,7 +7,6 @@ #include "common/mem.h" #include "common/scene-helpers.h" #include "idle.h" -#include "input/touch.h" #include "labwc.h" #include "config/mousebind.h" #include "action.h" diff --git a/src/layers.c b/src/layers.c index 2ae560df..44a2e131 100644 --- a/src/layers.c +++ b/src/layers.c @@ -6,6 +6,7 @@ * Copyright (C) 2019 Drew DeVault and Sway developers */ +#include "layers.h" #include #include #include @@ -18,7 +19,6 @@ #include "common/macros.h" #include "common/mem.h" #include "config/rcxml.h" -#include "layers.h" #include "labwc.h" #include "node.h" diff --git a/src/magnifier.c b/src/magnifier.c index 0846e02a..9fb5b264 100644 --- a/src/magnifier.c +++ b/src/magnifier.c @@ -1,12 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "magnifier.h" #include #include #include #include #include "common/box.h" #include "labwc.h" -#include "magnifier.h" #include "theme.h" static bool magnify_on; diff --git a/src/menu/menu.c b/src/menu/menu.c index dd9f7f59..a44790eb 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L +#include "menu/menu.h" #include #include #include @@ -25,7 +26,6 @@ #include "common/spawn.h" #include "common/string-helpers.h" #include "labwc.h" -#include "menu/menu.h" #include "workspaces.h" #include "view.h" #include "node.h" diff --git a/src/node.c b/src/node.c index 10d79a23..ae47044c 100644 --- a/src/node.c +++ b/src/node.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "node.h" #include #include #include "common/mem.h" -#include "node.h" static void descriptor_destroy(struct node_descriptor *node_descriptor) diff --git a/src/osd.c b/src/osd.c index 9213a1b3..89ba415d 100644 --- a/src/osd.c +++ b/src/osd.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "osd.h" #include #include #include @@ -15,7 +16,6 @@ #include "config/rcxml.h" #include "labwc.h" #include "node.h" -#include "osd.h" #include "theme.h" #include "view.h" #include "window-rules.h" diff --git a/src/output-state.c b/src/output-state.c index 62e2c4d1..7ebcc19e 100644 --- a/src/output-state.c +++ b/src/output-state.c @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "output-state.h" #include #include #include "labwc.h" -#include "output-state.h" void output_state_init(struct output *output) diff --git a/src/output-virtual.c b/src/output-virtual.c index 3853c9ef..888f832b 100644 --- a/src/output-virtual.c +++ b/src/output-virtual.c @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "output-virtual.h" #include #include #include #include "common/string-helpers.h" #include "labwc.h" -#include "output-virtual.h" static struct wlr_output *fallback_output = NULL; diff --git a/src/overlay.c b/src/overlay.c index 354f5ad9..78a9c172 100644 --- a/src/overlay.c +++ b/src/overlay.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "overlay.h" #include #include "common/lab-scene-rect.h" #include "labwc.h" -#include "overlay.h" #include "view.h" #include "theme.h" diff --git a/src/placement.c b/src/placement.c index 1e4fd937..c0377038 100644 --- a/src/placement.c +++ b/src/placement.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "placement.h" #include #include #include @@ -6,7 +7,6 @@ #include "common/macros.h" #include "common/mem.h" #include "labwc.h" -#include "placement.h" #include "ssd.h" #include "view.h" diff --git a/src/protocols/transaction-addon.c b/src/protocols/transaction-addon.c index c4678497..808a2096 100644 --- a/src/protocols/transaction-addon.c +++ b/src/protocols/transaction-addon.c @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "protocols/transaction-addon.h" #include #include #include "common/list.h" #include "common/mem.h" -#include "protocols/transaction-addon.h" void lab_transaction_op_destroy(struct lab_transaction_op *trans_op) diff --git a/src/regions.c b/src/regions.c index 112cc6e3..3463c97f 100644 --- a/src/regions.c +++ b/src/regions.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L +#include "regions.h" #include #include #include @@ -12,7 +13,6 @@ #include "common/mem.h" #include "input/keyboard.h" #include "labwc.h" -#include "regions.h" #include "view.h" bool diff --git a/src/resistance.c b/src/resistance.c index bc1bf8ad..2f09c0e9 100644 --- a/src/resistance.c +++ b/src/resistance.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "resistance.h" #include #include #include "common/border.h" @@ -6,7 +7,6 @@ #include "config/rcxml.h" #include "edges.h" #include "labwc.h" -#include "resistance.h" #include "snap-constraints.h" #include "view.h" diff --git a/src/resize-outlines.c b/src/resize-outlines.c index 9186bc6f..fd522419 100644 --- a/src/resize-outlines.c +++ b/src/resize-outlines.c @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "resize-outlines.h" #include #include "common/lab-scene-rect.h" #include "ssd.h" -#include "resize-outlines.h" #include "labwc.h" bool diff --git a/src/session-lock.c b/src/session-lock.c index 0a140ca6..fe118566 100644 --- a/src/session-lock.c +++ b/src/session-lock.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L +#include "session-lock.h" #include #include "common/mem.h" #include "labwc.h" diff --git a/src/snap-constraints.c b/src/snap-constraints.c index bc1ffd14..04eed5f7 100644 --- a/src/snap-constraints.c +++ b/src/snap-constraints.c @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "snap-constraints.h" #include #include #include "common/macros.h" #include "labwc.h" -#include "snap-constraints.h" #include "view.h" /* diff --git a/src/snap.c b/src/snap.c index 228572fa..4e8e1d44 100644 --- a/src/snap.c +++ b/src/snap.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "snap.h" #include #include #include @@ -7,7 +8,6 @@ #include "edges.h" #include "labwc.h" #include "snap-constraints.h" -#include "snap.h" #include "view.h" static void diff --git a/src/theme.c b/src/theme.c index 5fe9c1a2..961c640b 100644 --- a/src/theme.c +++ b/src/theme.c @@ -6,6 +6,7 @@ */ #define _POSIX_C_SOURCE 200809L +#include "theme.h" #include "config.h" #include #include @@ -30,7 +31,6 @@ #include "config/rcxml.h" #include "img/img.h" #include "labwc.h" -#include "theme.h" #include "buffer.h" #include "ssd.h" diff --git a/src/view-impl-common.c b/src/view-impl-common.c index 3357367b..9e2c1243 100644 --- a/src/view-impl-common.c +++ b/src/view-impl-common.c @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-only /* view-impl-common.c: common code for shell view->impl functions */ +#include "view-impl-common.h" #include #include #include "foreign-toplevel.h" #include "labwc.h" #include "view.h" -#include "view-impl-common.h" #include "window-rules.h" void diff --git a/src/view.c b/src/view.c index 3a6fa0bd..98c61d92 100644 --- a/src/view.c +++ b/src/view.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "view.h" #include #include #include @@ -25,7 +26,6 @@ #include "snap-constraints.h" #include "snap.h" #include "ssd.h" -#include "view.h" #include "window-rules.h" #include "wlr/util/log.h" #include "workspaces.h" diff --git a/src/window-rules.c b/src/window-rules.c index fa21f749..ed980efc 100644 --- a/src/window-rules.c +++ b/src/window-rules.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L +#include "window-rules.h" #include #include #include @@ -11,7 +12,6 @@ #include "config/rcxml.h" #include "labwc.h" #include "view.h" -#include "window-rules.h" static bool other_instances_exist(struct view *self, struct view_query *query) diff --git a/src/workspaces.c b/src/workspaces.c index 45b2f918..fc1cd524 100644 --- a/src/workspaces.c +++ b/src/workspaces.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L +#include "workspaces.h" #include #include #include @@ -18,7 +19,6 @@ #include "protocols/cosmic-workspaces.h" #include "protocols/ext-workspace.h" #include "view.h" -#include "workspaces.h" #include "xwayland.h" #define COSMIC_WORKSPACES_VERSION 1 diff --git a/src/xwayland.c b/src/xwayland.c index a3b85dc7..fae5b659 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L +#include "xwayland.h" #include #include #include @@ -17,7 +18,6 @@ #include "view-impl-common.h" #include "window-rules.h" #include "workspaces.h" -#include "xwayland.h" enum atoms { ATOM_NET_WM_ICON = 0, From 0d396f84a7834605fa7f17d759282ed3cb10a67e Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Tue, 29 Jul 2025 01:17:42 -0400 Subject: [PATCH 027/248] CONTRIBUTING.md: minor updates - style "GLib" with two capitals as in its own upstream docs - use title-case consistently for headings - add missing heading to table of contents - update dead GLib documentation URL --- CONTRIBUTING.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 37cfa4e4..6a67c9e8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,9 +10,10 @@ - [4.2 Devault Deviations](#devault-deviations) - [4.3 Labwc Specifics](#labwc-specifics) - [4.3.1 API](#api) - - [4.3.2 The Use of glib](#the-use-of-glib) - - [4.3.3 The use of GNU extensions](#the-use-of-gnu-extensions) + - [4.3.2 The Use of GLib](#the-use-of-glib) + - [4.3.3 The Use of GNU Extensions](#the-use-of-gnu-extensions) - [4.3.4 Naming Conventions](#naming-conventions) + - [4.3.5 Switch Statements with Variable Declarations](#switch-statements-with-variable-declarations) - [5. Commit Messages](#commit-messages) - [6. Unit Tests](#unit-tests) - [7. Submitting Patches](#submitting-patches) @@ -242,14 +243,14 @@ We have a very small, modest API and encourage you to use it. [common/array.h]: https://github.com/labwc/labwc/blob/master/include/common/array.h [common/macros.h]: https://github.com/labwc/labwc/blob/master/include/common/macros.h -### The Use of glib +### The Use of GLib -We try to keep the use of glib pretty minimal for the following reasons: +We try to keep the use of GLib pretty minimal for the following reasons: -- The use of glib has been known to make AddressSanitiser diagnose false +- The use of GLib has been known to make AddressSanitiser diagnose false positives and negatives. -- Log messages coming from glib functions look inconsistent. -- The use of glib functions, naming-conventions and iterators in a code base +- Log messages coming from GLib functions look inconsistent. +- The use of GLib functions, naming-conventions and iterators in a code base that is predominantly ANSI C creates a clash which makes readability and maintainability harder. - Mixing gmalloc()/malloc() and respective free()s can create problems with @@ -272,7 +273,7 @@ devs: - `g_pattern_match_simple()` When using these types of functions it is often desirable to support with some -glib code, which is okay provided it is kept local and self-contained. See +GLib code, which is okay provided it is kept local and self-contained. See example from `src/theme.c`: ``` @@ -287,7 +288,7 @@ match(const gchar *pattern, const gchar *string) } ``` -### The use of GNU extensions +### The Use of GNU Extensions We avoid [GNU C extensions] because we want to fit into the eco-system (wayland and wlroots) we live in. @@ -323,7 +324,7 @@ We use the prefix `handle_` for signal-handler-functions in order to be consistent with sway and rootston. For example `view->request_resize.notify = handle_request_resize` -### Switch statements with variable declarations +### Switch Statements with Variable Declarations Unlike many modern languages, C doesn't create a new scope after `case FOO:`. Therefore, we wrap codes following `case FOO:` that include variable @@ -481,11 +482,11 @@ follow the steps to be taken: [GNU C extensions]: https://gcc.gnu.org/onlinedocs/gcc/C-Extensions.html [`wl_container_of()`]: https://github.com/wayland-project/wayland/blob/985ab55d59db45ea62795c76dff5949343e86b2f/src/wayland-util.h#L409 -[^1]: The reference documentation for glib notes that: +[^1]: The reference documentation for GLib notes that: "It's important to match g_malloc() with g_free(), plain malloc() with free(), and (if you're using C++) new with delete and new[] with delete[]. Otherwise bad things can happen, since these allocators may use different memory pools (and new/delete call constructors and destructors)." - See: https://developer.gimp.org/api/2.0/glib/glib-Memory-Allocation.html + See: https://docs.gtk.org/glib/memory.html From 330c55e1b248519472f33dbe26ad0c2a32781850 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Tue, 29 Jul 2025 01:59:40 -0400 Subject: [PATCH 028/248] CONTRIBUTING.md: add section on include order --- CONTRIBUTING.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6a67c9e8..c1025234 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,6 +14,7 @@ - [4.3.3 The Use of GNU Extensions](#the-use-of-gnu-extensions) - [4.3.4 Naming Conventions](#naming-conventions) - [4.3.5 Switch Statements with Variable Declarations](#switch-statements-with-variable-declarations) + - [4.3.6 Order of #includes](#order-of-includes) - [5. Commit Messages](#commit-messages) - [6. Unit Tests](#unit-tests) - [7. Submitting Patches](#submitting-patches) @@ -350,6 +351,26 @@ case BAZ: But please also consider refactoring the code into a separate function if it becomes lengthy. +### Order of #includes + +In new files, please order `#include` lines as follows: + +- In each `.c` file, first include the matching `.h` file, if there is + one. For example, `#include "common/font.h"` should come first in + `src/common/font.c`. This practice helps to ensure that each header + compiles cleanly on its own, without implicit dependencies on other + headers being included first. + +- Then list any "system" headers (those not part of labwc) in alphebetical + order, using angle brackets. This includes 3rd-party library headers + such as ``, as well as wlroots headers. + +- Then list any other labwc headers in alphetical order, using quotation + marks and relative to the `include/` folder. Subfolders below `include/`, + such as `common/`, should be specified even when including one header + from another in the same folder (for example, `#include "common/buf.h"` + from `include/common/grab-file.h`). + # Commit Messages The log messages that explain changes are just as important as the changes From 503af1050517683a7116070bb148c19a7035813d Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Thu, 10 Apr 2025 00:43:21 +0900 Subject: [PATCH 029/248] rcxml: convert dotted properties into nested nodes before processing For example, the following node: is converted to: ShowMenu root-menu 1 2 ...before processing the entire xml tree. This is a preparation to prevent breaking changes when we refactor rcxml.c to use recursion instead of encoding nodes into dotted strings. --- include/common/xml.h | 29 +++++++++ src/common/meson.build | 1 + src/common/xml.c | 131 +++++++++++++++++++++++++++++++++++++++++ src/config/rcxml.c | 5 +- t/meson.build | 11 +++- t/xml.c | 128 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 302 insertions(+), 3 deletions(-) create mode 100644 include/common/xml.h create mode 100644 src/common/xml.c create mode 100644 t/xml.c diff --git a/include/common/xml.h b/include/common/xml.h new file mode 100644 index 00000000..7bc8eb26 --- /dev/null +++ b/include/common/xml.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_XML_H +#define LABWC_XML_H + +#include + +/* + * Converts dotted attributes into nested nodes. + * For example, the following node: + * + * + * + * is converted to: + * + * + * + * ShowMenu + * root-menu + * + * 1 + * 2 + * + * + * + */ +void lab_xml_expand_dotted_attributes(xmlNode *root); + +#endif /* LABWC_XML_H */ diff --git a/src/common/meson.build b/src/common/meson.build index 1e226297..aa0fc413 100644 --- a/src/common/meson.build +++ b/src/common/meson.build @@ -23,4 +23,5 @@ labwc_sources += files( 'surface-helpers.c', 'spawn.c', 'string-helpers.c', + 'xml.c', ) diff --git a/src/common/xml.c b/src/common/xml.c new file mode 100644 index 00000000..08ec18b6 --- /dev/null +++ b/src/common/xml.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include "common/xml.h" + +/* + * Converts an attribute A.B.C="X" into X + */ +static xmlNode * +create_attribute_tree(const xmlAttr *attr) +{ + gchar **parts = g_strsplit((char *)attr->name, ".", -1); + int length = g_strv_length(parts); + xmlNode *root_node = NULL; + xmlNode *parent_node = NULL; + xmlNode *current_node = NULL; + + for (int i = length - 1; i >= 0; i--) { + gchar *part = parts[i]; + if (!*part) { + /* Ignore empty string */ + continue; + } + current_node = xmlNewNode(NULL, (xmlChar *)part); + if (parent_node) { + xmlAddChild(parent_node, current_node); + } else { + root_node = current_node; + } + parent_node = current_node; + } + + /* + * Note: empty attributes or attributes with only dots are forbidden + * and root_node becomes never NULL here. + */ + assert(root_node); + + xmlChar *content = xmlNodeGetContent(attr->children); + xmlNodeSetContent(current_node, content); + xmlFree(content); + + g_strfreev(parts); + return root_node; +} + +/* + * Consider . + * These three attributes are represented by following trees. + * action(dst)---name + * action(src)---position---x + * action--------position---y + * When we call merge_two_trees(dst, src) for the first 2 trees above, we walk + * over the trees from their roots towards leaves, and merge the identical + * node 'action' like: + * action(dst)---name + * \--------position---x + * action(src)---position---y + * And when we call merge_two_trees(dst, src) again, we walk over the dst tree + * like 'action'->'position'->'x' and the src tree like 'action'->'position'->'y'. + * First, we merge the identical node 'action' again like: + * action---name + * \---position(dst)---x + * \--position(src)---y + * Next, we merge the identical node 'position' like: + * action---name + * \---position---x + * \----y + */ +static bool +merge_two_trees(xmlNode *dst, xmlNode *src) +{ + bool merged = false; + + while (dst && src && src->children + && !strcasecmp((char *)dst->name, (char *)src->name)) { + xmlNode *next_dst = dst->last; + xmlNode *next_src = src->children; + xmlAddChild(dst, src->children); + xmlUnlinkNode(src); + xmlFreeNode(src); + src = next_src; + dst = next_dst; + merged = true; + } + + return merged; +} + +void +lab_xml_expand_dotted_attributes(xmlNode *parent) +{ + xmlNode *old_first_child = parent->children; + xmlNode *prev_tree = NULL; + + if (parent->type != XML_ELEMENT_NODE) { + return; + } + + for (xmlAttr *attr = parent->properties; attr;) { + /* Convert the attribute with dots into an xml tree */ + xmlNode *tree = create_attribute_tree(attr); + if (!tree) { + /* The attribute doesn't contain dots */ + prev_tree = NULL; + attr = attr->next; + continue; + } + + /* Try to merge the tree with the previous one */ + if (!merge_two_trees(prev_tree, tree)) { + /* If not merged, add the tree as a new child */ + if (old_first_child) { + xmlAddPrevSibling(old_first_child, tree); + } else { + xmlAddChild(parent, tree); + } + prev_tree = tree; + } + + xmlAttr *next_attr = attr->next; + xmlRemoveProp(attr); + attr = next_attr; + } + + for (xmlNode *node = parent->children; node; node = node->next) { + lab_xml_expand_dotted_attributes(node); + } +} diff --git a/src/config/rcxml.c b/src/config/rcxml.c index e8c4185e..c3abbf93 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -25,6 +25,7 @@ #include "common/parse-double.h" #include "common/string-helpers.h" #include "common/three-state.h" +#include "common/xml.h" #include "config/default-bindings.h" #include "config/keybind.h" #include "config/libinput.h" @@ -1509,7 +1510,9 @@ rcxml_parse_xml(struct buf *b) return; } struct parser_state init_state = {0}; - xml_tree_walk(xmlDocGetRootElement(d), &init_state); + xmlNode *root = xmlDocGetRootElement(d); + lab_xml_expand_dotted_attributes(root); + xml_tree_walk(root, &init_state); xmlFreeDoc(d); xmlCleanupParser(); } diff --git a/t/meson.build b/t/meson.build index eb997fe2..66534a2c 100644 --- a/t/meson.build +++ b/t/meson.build @@ -3,15 +3,21 @@ test_lib = static_library( sources: files( '../src/common/buf.c', '../src/common/mem.c', - '../src/common/string-helpers.c' + '../src/common/string-helpers.c', + '../src/common/xml.c', ), include_directories: [labwc_inc], - dependencies: [dep_cmocka], + dependencies: [ + dep_cmocka, + glib, + xml2, + ], ) tests = [ 'buf-simple', 'str', + 'xml', ] foreach t : tests @@ -22,6 +28,7 @@ foreach t : tests sources: '@0@.c'.format(t), include_directories: [labwc_inc], link_with: [test_lib], + dependencies: [xml2], ), is_parallel: false, ) diff --git a/t/xml.c b/t/xml.c new file mode 100644 index 00000000..003632a6 --- /dev/null +++ b/t/xml.c @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: GPL-2.0-only +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include "common/macros.h" +#include "common/xml.h" + +struct test_case { + const char *before, *after; +} test_cases[] = {{ + "", + + "" + "" + "ShowMenu" + "root-menu" + "" + "1" + "2" + "" + "" + "" +}, { + "", + + "" + "111" + "222" + "" +}, { + "", + + "" + "111" + "222" + "333" + "" +}, { + "", + + "" + "111" + "222" + "333" + "" +}, { + "", + + "" + "111" + "222" + "" +}, { + "", + + "" + "111" + "222" + "333" + "", +}, { + "" + "" + "" + "", + + "" + "111" + "111" + "", +}, { + "" + "222" + "", + + "" + "111" + "222" + "", +}, { + "" + "111" + "111" + "", + + "" + "111" + "111" + "", +}, { + "", + + "111" +}}; + +static void +test_lab_xml_expand_dotted_attributes(void **state) +{ + (void)state; + + for (size_t i = 0; i < ARRAY_SIZE(test_cases); i++) { + xmlDoc *doc = xmlReadDoc((xmlChar *)test_cases[i].before, + NULL, NULL, 0); + xmlNode *root = xmlDocGetRootElement(doc); + + lab_xml_expand_dotted_attributes(root); + + xmlBuffer *buf = xmlBufferCreate(); + xmlNodeDump(buf, root->doc, root, 0, 0); + assert_string_equal(test_cases[i].after, (char *)buf->content); + xmlBufferFree(buf); + + xmlFreeDoc(doc); + } +} + +int main(int argc, char **argv) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_lab_xml_expand_dotted_attributes), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} From 888184109879174768df6788eff61f5433eaf1e2 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Fri, 11 Apr 2025 20:25:12 +0900 Subject: [PATCH 030/248] common/xml: add helpers to parse rc.xml --- include/common/xml.h | 39 +++++++++++++++++++++++++ scripts/checkpatch.pl | 1 + src/common/xml.c | 67 +++++++++++++++++++++++++++++++++++++++++++ t/meson.build | 2 ++ 4 files changed, 109 insertions(+) diff --git a/include/common/xml.h b/include/common/xml.h index 7bc8eb26..3cfa5539 100644 --- a/include/common/xml.h +++ b/include/common/xml.h @@ -3,6 +3,7 @@ #define LABWC_XML_H #include +#include /* * Converts dotted attributes into nested nodes. @@ -26,4 +27,42 @@ */ void lab_xml_expand_dotted_attributes(xmlNode *root); +/* Returns true if the node only contains a string or is empty */ +bool lab_xml_node_is_leaf(xmlNode *node); + +bool lab_xml_get_node(xmlNode *node, const char *key, xmlNode **dst_node); +bool lab_xml_get_string(xmlNode *node, const char *key, char *s, size_t len); +bool lab_xml_get_int(xmlNode *node, const char *key, int *i); +bool lab_xml_get_bool(xmlNode *node, const char *key, bool *b); + +static inline xmlNode * +lab_xml_get_next_child(xmlNode *child) +{ + if (!child) { + return NULL; + } + do { + child = child->next; + } while (child && child->type != XML_ELEMENT_NODE); + + return child; +} + +static inline void +lab_xml_get_key_and_content(xmlNode *node, char **name, char **content) +{ + if (node) { + *name = (char *)node->name; + *content = (char *)xmlNodeGetContent(node); + } +} + +#define LAB_XML_FOR_EACH(parent, child, key, content) \ + for ((child) = (parent)->children, \ + lab_xml_get_key_and_content((child), &(key), &(content)); \ + (child); \ + xmlFree((xmlChar *)(content)), \ + (child) = lab_xml_get_next_child(child), \ + lab_xml_get_key_and_content((child), &(key), &(content))) + #endif /* LABWC_XML_H */ diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 6f835f22..f9e9d1d9 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -5532,6 +5532,7 @@ sub process { if ($starts_with_if_while_etc && !length($s) && $filename ne "include/view.h" && $filename ne "include/common/array.h" + && $filename ne "include/common/xml.h" && $filename ne "include/ssd-internal.h") { CHK("BRACES", "[labwc-custom] open brace { expected after if/while/for/switch - even with single statement blocks"); } diff --git a/src/common/xml.c b/src/common/xml.c index 08ec18b6..df8c26e4 100644 --- a/src/common/xml.c +++ b/src/common/xml.c @@ -4,6 +4,7 @@ #include #include #include "common/xml.h" +#include "common/parse-bool.h" /* * Converts an attribute A.B.C="X" into X @@ -129,3 +130,69 @@ lab_xml_expand_dotted_attributes(xmlNode *parent) lab_xml_expand_dotted_attributes(node); } } + +bool +lab_xml_node_is_leaf(xmlNode *node) +{ + if (node->type != XML_ELEMENT_NODE) { + return false; + } + for (xmlNode *child = node->children; child; child = child->next) { + if (child->type != XML_TEXT_NODE) { + return false; + } + } + return true; +} + +static bool +get_node(xmlNode *node, const char *key, xmlNode **dst_node, bool leaf_only) +{ + for (xmlNode *child = node->last; child; child = child->prev) { + if (child->type != XML_ELEMENT_NODE) { + continue; + } + if (leaf_only && !lab_xml_node_is_leaf(child)) { + continue; + } + if (!strcasecmp((char *)child->name, key)) { + *dst_node = child; + return true; + } + } + return false; +} + +bool +lab_xml_get_node(xmlNode *node, const char *key, xmlNode **dst_node) +{ + return get_node(node, key, dst_node, /* leaf_only */ false); +} + +bool +lab_xml_get_string(xmlNode *node, const char *key, char *s, size_t len) +{ + xmlNode *child; + if (get_node(node, key, &child, /* leaf_only */ true)) { + xmlChar *content = xmlNodeGetContent(child); + g_strlcpy(s, (char *)content, len); + xmlFree(content); + return true; + } + return false; +} + +bool +lab_xml_get_bool(xmlNode *node, const char *key, bool *b) +{ + xmlNode *child; + if (get_node(node, key, &child, /* leaf_only */ true)) { + char *s = (char *)xmlNodeGetContent(child); + int ret = parse_bool(s, -1); + if (ret >= 0) { + *b = ret; + return true; + } + } + return false; +} diff --git a/t/meson.build b/t/meson.build index 66534a2c..6aafc1e9 100644 --- a/t/meson.build +++ b/t/meson.build @@ -5,12 +5,14 @@ test_lib = static_library( '../src/common/mem.c', '../src/common/string-helpers.c', '../src/common/xml.c', + '../src/common/parse-bool.c', ), include_directories: [labwc_inc], dependencies: [ dep_cmocka, glib, xml2, + wlroots, ], ) From 9462457cc2359f945ba5fcab4f8e2aad343b4a18 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 12 Apr 2025 00:57:03 +0900 Subject: [PATCH 031/248] rcxml: rewrite action parser This commit rewrites the nested action parser into append_actions() which is used by following commits. At this point, it's not used yet and parsing "If" action is temporarily disabled. --- src/config/rcxml.c | 255 +++++++++++++++++++++++---------------------- 1 file changed, 128 insertions(+), 127 deletions(-) diff --git a/src/config/rcxml.c b/src/config/rcxml.c index c3abbf93..bf108cae 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -432,112 +432,141 @@ fill_region(char *nodename, char *content, struct parser_state *state) } static void -fill_action_query(char *nodename, char *content, struct action *action, struct parser_state *state) +fill_action_query(struct action *action, xmlNode *node, struct view_query *query) { - if (!action) { - wlr_log(WLR_ERROR, "No parent action for query: %s=%s", nodename, content); - return; - } - - string_truncate_at_pattern(nodename, ".keybind.keyboard"); - string_truncate_at_pattern(nodename, ".mousebind.context.mouse"); - - if (!strcasecmp(nodename, "query.action")) { - state->current_view_query = NULL; - } - - string_truncate_at_pattern(nodename, ".query.action"); - - if (!content) { - return; - } - - if (!state->current_view_query) { - struct wl_list *queries = action_get_querylist(action, "query"); - if (!queries) { - action_arg_add_querylist(action, "query"); - queries = action_get_querylist(action, "query"); + xmlNode *child; + char *key, *content; + LAB_XML_FOR_EACH(node, child, key, content) { + if (!strcasecmp(key, "identifier")) { + xstrdup_replace(query->identifier, content); + } else if (!strcasecmp(key, "title")) { + xstrdup_replace(query->title, content); + } else if (!strcmp(key, "type")) { + query->window_type = parse_window_type(content); + } else if (!strcasecmp(key, "sandboxEngine")) { + xstrdup_replace(query->sandbox_engine, content); + } else if (!strcasecmp(key, "sandboxAppId")) { + xstrdup_replace(query->sandbox_app_id, content); + } else if (!strcasecmp(key, "shaded")) { + query->shaded = parse_three_state(content); + } else if (!strcasecmp(key, "maximized")) { + query->maximized = view_axis_parse(content); + } else if (!strcasecmp(key, "iconified")) { + query->iconified = parse_three_state(content); + } else if (!strcasecmp(key, "focused")) { + query->focused = parse_three_state(content); + } else if (!strcasecmp(key, "omnipresent")) { + query->omnipresent = parse_three_state(content); + } else if (!strcasecmp(key, "tiled")) { + query->tiled = view_edge_parse(content); + } else if (!strcasecmp(key, "tiled_region")) { + xstrdup_replace(query->tiled_region, content); + } else if (!strcasecmp(key, "desktop")) { + xstrdup_replace(query->desktop, content); + } else if (!strcasecmp(key, "decoration")) { + query->decoration = ssd_mode_parse(content); + } else if (!strcasecmp(key, "monitor")) { + xstrdup_replace(query->monitor, content); } - state->current_view_query = view_query_create(); - wl_list_append(queries, &state->current_view_query->link); - } - - if (!strcasecmp(nodename, "identifier")) { - xstrdup_replace(state->current_view_query->identifier, content); - } else if (!strcasecmp(nodename, "title")) { - xstrdup_replace(state->current_view_query->title, content); - } else if (!strcmp(nodename, "type")) { - state->current_view_query->window_type = parse_window_type(content); - } else if (!strcasecmp(nodename, "sandboxEngine")) { - xstrdup_replace(state->current_view_query->sandbox_engine, content); - } else if (!strcasecmp(nodename, "sandboxAppId")) { - xstrdup_replace(state->current_view_query->sandbox_app_id, content); - } else if (!strcasecmp(nodename, "shaded")) { - state->current_view_query->shaded = parse_three_state(content); - } else if (!strcasecmp(nodename, "maximized")) { - state->current_view_query->maximized = view_axis_parse(content); - } else if (!strcasecmp(nodename, "iconified")) { - state->current_view_query->iconified = parse_three_state(content); - } else if (!strcasecmp(nodename, "focused")) { - state->current_view_query->focused = parse_three_state(content); - } else if (!strcasecmp(nodename, "omnipresent")) { - state->current_view_query->omnipresent = parse_three_state(content); - } else if (!strcasecmp(nodename, "tiled")) { - state->current_view_query->tiled = view_edge_parse(content); - } else if (!strcasecmp(nodename, "tiled_region")) { - xstrdup_replace(state->current_view_query->tiled_region, content); - } else if (!strcasecmp(nodename, "desktop")) { - xstrdup_replace(state->current_view_query->desktop, content); - } else if (!strcasecmp(nodename, "decoration")) { - state->current_view_query->decoration = ssd_mode_parse(content); - } else if (!strcasecmp(nodename, "monitor")) { - xstrdup_replace(state->current_view_query->monitor, content); } } +static void append_actions(xmlNode *node, struct wl_list *list); + static void -fill_child_action(char *nodename, char *content, struct action *parent, - const char *branch_name, struct parser_state *state) +parse_action_args(xmlNode *node, struct action *action) { - if (!parent) { - wlr_log(WLR_ERROR, "No parent action for branch: %s=%s", nodename, content); - return; - } - - string_truncate_at_pattern(nodename, ".keybind.keyboard"); - string_truncate_at_pattern(nodename, ".mousebind.context.mouse"); - string_truncate_at_pattern(nodename, ".then.action"); - string_truncate_at_pattern(nodename, ".else.action"); - string_truncate_at_pattern(nodename, ".none.action"); - - if (!strcasecmp(nodename, "action")) { - state->current_child_action = NULL; - } - - if (!content) { - return; - } - - struct wl_list *siblings = action_get_actionlist(parent, branch_name); - if (!siblings) { - action_arg_add_actionlist(parent, branch_name); - siblings = action_get_actionlist(parent, branch_name); - } - - if (!strcasecmp(nodename, "name.action")) { - if (!strcasecmp(content, "If") || !strcasecmp(content, "ForEach")) { - wlr_log(WLR_ERROR, "action '%s' cannot be a child action", content); - return; + xmlNode *child; + char *key, *content; + LAB_XML_FOR_EACH(node, child, key, content) { + if (!strcasecmp(key, "query")) { + struct wl_list *querylist = + action_get_querylist(action, "query"); + if (!querylist) { + action_arg_add_querylist(action, "query"); + querylist = action_get_querylist(action, "query"); + } + struct view_query *query = view_query_create(); + fill_action_query(action, child, query); + wl_list_append(querylist, &query->link); + } else if (!strcasecmp(key, "then")) { + struct wl_list *actions = + action_get_actionlist(action, "then"); + if (!actions) { + action_arg_add_actionlist(action, "then"); + actions = action_get_actionlist(action, "then"); + } + append_actions(child, actions); + } else if (!strcasecmp(key, "else")) { + struct wl_list *actions = + action_get_actionlist(action, "else"); + if (!actions) { + action_arg_add_actionlist(action, "else"); + actions = action_get_actionlist(action, "else"); + } + append_actions(child, actions); + } else if (!strcasecmp(key, "none")) { + struct wl_list *actions = + action_get_actionlist(action, "none"); + if (!actions) { + action_arg_add_actionlist(action, "none"); + actions = action_get_actionlist(action, "none"); + } + append_actions(child, actions); + } else if (!strcasecmp(key, "name")) { + /* Ignore */ + } else if (lab_xml_node_is_leaf(child)) { + /* Handle normal action args */ + char buffer[256]; + char *node_name = nodename(child, buffer, sizeof(buffer)); + action_arg_from_xml_node(action, node_name, content); + } else { + /* Handle nested args like in ShowMenu */ + parse_action_args(child, action); } - state->current_child_action = action_create(content); - if (state->current_child_action) { - wl_list_append(siblings, &state->current_child_action->link); + } +} + +static struct action * +parse_action(xmlNode *node) +{ + char name[256]; + struct action *action = NULL; + + if (lab_xml_get_string(node, "name", name, sizeof(name))) { + action = action_create(name); + } + if (!action) { + return NULL; + } + + parse_action_args(node, action); + return action; +} + +static void +append_actions(xmlNode *node, struct wl_list *list) +{ + xmlNode *child; + char *key, *content; + LAB_XML_FOR_EACH(node, child, key, content) { + if (strcasecmp(key, "action")) { + continue; + } + if (lab_xml_node_is_leaf(child)) { + /* + * A mousebind contains two types of "action" nodes: + * + * + * + * The first node (action="Click") is skipped. + */ + continue; + } + struct action *action = parse_action(child); + if (action) { + wl_list_append(list, &action->link); } - } else if (!state->current_child_action) { - wlr_log(WLR_ERROR, "expect element first. " - "nodename: '%s' content: '%s'", nodename, content); - } else { - action_arg_from_xml_node(state->current_child_action, nodename, content); } } @@ -1055,38 +1084,10 @@ entry(xmlNode *node, char *nodename, char *content, struct parser_state *state) fill_usable_area_override(nodename, content, state); } if (state->in_keybind) { - if (state->in_action_query) { - fill_action_query(nodename, content, - state->current_keybind_action, state); - } else if (state->in_action_then_branch) { - fill_child_action(nodename, content, - state->current_keybind_action, "then", state); - } else if (state->in_action_else_branch) { - fill_child_action(nodename, content, - state->current_keybind_action, "else", state); - } else if (state->in_action_none_branch) { - fill_child_action(nodename, content, - state->current_keybind_action, "none", state); - } else { - fill_keybind(nodename, content, state); - } + fill_keybind(nodename, content, state); } if (state->in_mousebind) { - if (state->in_action_query) { - fill_action_query(nodename, content, - state->current_mousebind_action, state); - } else if (state->in_action_then_branch) { - fill_child_action(nodename, content, - state->current_mousebind_action, "then", state); - } else if (state->in_action_else_branch) { - fill_child_action(nodename, content, - state->current_mousebind_action, "else", state); - } else if (state->in_action_none_branch) { - fill_child_action(nodename, content, - state->current_mousebind_action, "none", state); - } else { - fill_mousebind(nodename, content, state); - } + fill_mousebind(nodename, content, state); } if (state->in_touch) { fill_touch(nodename, content, state); From 433168457f71ecacd60a8b10671d38bc245029c4 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 12 Apr 2025 03:32:37 +0900 Subject: [PATCH 032/248] rcxml: rewrite parser --- src/config/rcxml.c | 62 +++++++++++++++------------------------------- 1 file changed, 20 insertions(+), 42 deletions(-) diff --git a/src/config/rcxml.c b/src/config/rcxml.c index bf108cae..ade6201a 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -571,49 +571,26 @@ append_actions(xmlNode *node, struct wl_list *list) } static void -fill_keybind(char *nodename, char *content, struct parser_state *state) +fill_keybind(xmlNode *node) { - if (!content) { + struct keybind *keybind = NULL; + char keyname[256]; + + if (lab_xml_get_string(node, "key", keyname, sizeof(keyname))) { + keybind = keybind_create(keyname); + if (!keybind) { + wlr_log(WLR_ERROR, "Invalid keybind: %s", keyname); + } + } + if (!keybind) { return; } - string_truncate_at_pattern(nodename, ".keybind.keyboard"); - if (!strcmp(nodename, "key")) { - state->current_keybind = keybind_create(content); - state->current_keybind_action = NULL; - /* - * If an invalid keybind has been provided, - * keybind_create() complains. - */ - if (!state->current_keybind) { - wlr_log(WLR_ERROR, "Invalid keybind: %s", content); - return; - } - } else if (!state->current_keybind) { - wlr_log(WLR_ERROR, "expect element first. " - "nodename: '%s' content: '%s'", nodename, content); - } else if (!strcasecmp(nodename, "onRelease")) { - set_bool(content, &state->current_keybind->on_release); - } else if (!strcasecmp(nodename, "layoutDependent")) { - set_bool(content, &state->current_keybind->use_syms_only); - } else if (!strcasecmp(nodename, "allowWhenLocked")) { - set_bool(content, &state->current_keybind->allow_when_locked); - } else if (!strcmp(nodename, "name.action")) { - state->current_keybind_action = action_create(content); - if (state->current_keybind_action) { - wl_list_append(&state->current_keybind->actions, - &state->current_keybind_action->link); - } - } else if (!state->current_keybind_action) { - wlr_log(WLR_ERROR, "expect element first. " - "nodename: '%s' content: '%s'", nodename, content); - } else { - /* - * Here we deal with action sub-elements such as , , - * , and so on. This is common to key- and - * mousebinds. - */ - action_arg_from_xml_node(state->current_keybind_action, nodename, content); - } + + lab_xml_get_bool(node, "onRelease", &keybind->on_release); + lab_xml_get_bool(node, "layoutDependent", &keybind->use_syms_only); + lab_xml_get_bool(node, "allowWhenLocked", &keybind->allow_when_locked); + + append_actions(node, &keybind->actions); } static void @@ -1083,8 +1060,9 @@ entry(xmlNode *node, char *nodename, char *content, struct parser_state *state) if (state->in_usable_area_override) { fill_usable_area_override(nodename, content, state); } - if (state->in_keybind) { - fill_keybind(nodename, content, state); + if (!strcasecmp(nodename, "keybind.keyboard")) { + fill_keybind(node); + return; } if (state->in_mousebind) { fill_mousebind(nodename, content, state); From be31df097cfe5a0549fe7df11492eeeab5856b46 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 12 Apr 2025 03:41:27 +0900 Subject: [PATCH 033/248] rcxml: rewrite parser --- src/config/rcxml.c | 76 +++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 42 deletions(-) diff --git a/src/config/rcxml.c b/src/config/rcxml.c index ade6201a..cba0f887 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -594,7 +594,7 @@ fill_keybind(xmlNode *node) } static void -fill_mousebind(char *nodename, char *content, struct parser_state *state) +fill_mousebind(xmlNode *node, const char *context) { /* * Example of what we are parsing: @@ -605,46 +605,40 @@ fill_mousebind(char *nodename, char *content, struct parser_state *state) * */ - if (!state->current_mouse_context) { - wlr_log(WLR_ERROR, "expect element first. " - "nodename: '%s' content: '%s'", nodename, content); - return; - } else if (!strcmp(nodename, "mousebind.context.mouse")) { - wlr_log(WLR_INFO, "create mousebind for %s", - state->current_mouse_context); - state->current_mousebind = mousebind_create(state->current_mouse_context); - state->current_mousebind_action = NULL; - return; - } else if (!content) { + wlr_log(WLR_INFO, "create mousebind for %s", context); + struct mousebind *mousebind = mousebind_create(context); + + char buf[256]; + if (lab_xml_get_string(node, "button", buf, sizeof(buf))) { + mousebind->button = mousebind_button_from_str( + buf, &mousebind->modifiers); + } + if (lab_xml_get_string(node, "direction", buf, sizeof(buf))) { + mousebind->direction = mousebind_direction_from_str( + buf, &mousebind->modifiers); + } + if (lab_xml_get_string(node, "action", buf, sizeof(buf))) { + /* */ + mousebind->mouse_event = mousebind_event_from_str(buf); + } + + append_actions(node, &mousebind->actions); +} + +static void +fill_mouse_context(xmlNode *node) +{ + char context_name[256]; + if (!lab_xml_get_string(node, "name", context_name, sizeof(context_name))) { return; } - string_truncate_at_pattern(nodename, ".mousebind.context.mouse"); - if (!state->current_mousebind) { - wlr_log(WLR_ERROR, - "expect element first. " - "nodename: '%s' content: '%s'", nodename, content); - } else if (!strcmp(nodename, "button")) { - state->current_mousebind->button = mousebind_button_from_str(content, - &state->current_mousebind->modifiers); - } else if (!strcmp(nodename, "direction")) { - state->current_mousebind->direction = mousebind_direction_from_str(content, - &state->current_mousebind->modifiers); - } else if (!strcmp(nodename, "action")) { - /* */ - state->current_mousebind->mouse_event = - mousebind_event_from_str(content); - } else if (!strcmp(nodename, "name.action")) { - state->current_mousebind_action = action_create(content); - if (state->current_mousebind_action) { - wl_list_append(&state->current_mousebind->actions, - &state->current_mousebind_action->link); + xmlNode *child; + char *key, *content; + LAB_XML_FOR_EACH(node, child, key, content) { + if (!strcasecmp(key, "mousebind")) { + fill_mousebind(child, context_name); } - } else if (!state->current_mousebind_action) { - wlr_log(WLR_ERROR, "expect element first. " - "nodename: '%s' content: '%s'", nodename, content); - } else { - action_arg_from_xml_node(state->current_mousebind_action, nodename, content); } } @@ -1064,8 +1058,9 @@ entry(xmlNode *node, char *nodename, char *content, struct parser_state *state) fill_keybind(node); return; } - if (state->in_mousebind) { - fill_mousebind(nodename, content, state); + if (!strcasecmp(nodename, "context.mouse")) { + fill_mouse_context(node); + return; } if (state->in_touch) { fill_touch(nodename, content, state); @@ -1203,9 +1198,6 @@ entry(xmlNode *node, char *nodename, char *content, struct parser_state *state) } else if (!strcasecmp(nodename, "scrollFactor.mouse")) { /* This is deprecated. Show an error message in post_processing() */ set_double(content, &mouse_scroll_factor); - } else if (!strcasecmp(nodename, "name.context.mouse")) { - state->current_mouse_context = content; - state->current_mousebind = NULL; } else if (!strcasecmp(nodename, "repeatRate.keyboard")) { rc.repeat_rate = atoi(content); From 8ae2d72cef4f93530c3eb071bf32b0793405aa34 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 12 Apr 2025 01:27:49 +0900 Subject: [PATCH 034/248] rcxml: rewrite parser --- src/config/rcxml.c | 52 ++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/src/config/rcxml.c b/src/config/rcxml.c index cba0f887..fd56e249 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -241,32 +241,29 @@ err: } static void -fill_usable_area_override(char *nodename, char *content, struct parser_state *state) +fill_usable_area_override(xmlNode *node) { - if (!strcasecmp(nodename, "margin")) { - state->current_usable_area_override = znew(*state->current_usable_area_override); - wl_list_append(&rc.usable_area_overrides, - &state->current_usable_area_override->link); - return; - } - string_truncate_at_pattern(nodename, ".margin"); - if (!content) { - /* nop */ - } else if (!state->current_usable_area_override) { - wlr_log(WLR_ERROR, "no usable-area-override object"); - } else if (!strcmp(nodename, "output")) { - xstrdup_replace(state->current_usable_area_override->output, content); - } else if (!strcmp(nodename, "left")) { - state->current_usable_area_override->margin.left = atoi(content); - } else if (!strcmp(nodename, "right")) { - state->current_usable_area_override->margin.right = atoi(content); - } else if (!strcmp(nodename, "top")) { - state->current_usable_area_override->margin.top = atoi(content); - } else if (!strcmp(nodename, "bottom")) { - state->current_usable_area_override->margin.bottom = atoi(content); - } else { - wlr_log(WLR_ERROR, "Unexpected data usable-area-override parser: %s=\"%s\"", - nodename, content); + struct usable_area_override *usable_area_override = + znew(*usable_area_override); + wl_list_append(&rc.usable_area_overrides, &usable_area_override->link); + + xmlNode *child; + char *key, *content; + LAB_XML_FOR_EACH(node, child, key, content) { + if (!strcmp(key, "output")) { + xstrdup_replace(usable_area_override->output, content); + } else if (!strcmp(key, "left")) { + usable_area_override->margin.left = atoi(content); + } else if (!strcmp(key, "right")) { + usable_area_override->margin.right = atoi(content); + } else if (!strcmp(key, "top")) { + usable_area_override->margin.top = atoi(content); + } else if (!strcmp(key, "bottom")) { + usable_area_override->margin.bottom = atoi(content); + } else { + wlr_log(WLR_ERROR, "Unexpected data usable-area-override " + "parser: %s=\"%s\"", key, content); + } } } @@ -1051,8 +1048,9 @@ entry(xmlNode *node, char *nodename, char *content, struct parser_state *state) printf("%s: %s\n", nodename, content); } - if (state->in_usable_area_override) { - fill_usable_area_override(nodename, content, state); + if (!strcasecmp(nodename, "margin")) { + fill_usable_area_override(node); + return; } if (!strcasecmp(nodename, "keybind.keyboard")) { fill_keybind(node); From 7adbfe88aa6c5b382f752313d7fbfa44c083a0c6 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 12 Apr 2025 01:33:28 +0900 Subject: [PATCH 035/248] rcxml: rewrite parser --- src/config/rcxml.c | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/src/config/rcxml.c b/src/config/rcxml.c index fd56e249..eef58f0b 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -640,27 +640,24 @@ fill_mouse_context(xmlNode *node) } static void -fill_touch(char *nodename, char *content, struct parser_state *state) +fill_touch(xmlNode *node) { - if (!strcasecmp(nodename, "touch")) { - state->current_touch = znew(*state->current_touch); - wl_list_append(&rc.touch_configs, &state->current_touch->link); - return; - } + struct touch_config_entry *touch_config = znew(*touch_config); + wl_list_append(&rc.touch_configs, &touch_config->link); - if (!content) { - return; - } - - if (!strcasecmp(nodename, "deviceName.touch")) { - xstrdup_replace(state->current_touch->device_name, content); - } else if (!strcasecmp(nodename, "mapToOutput.touch")) { - xstrdup_replace(state->current_touch->output_name, content); - } else if (!strcasecmp(nodename, "mouseEmulation.touch")) { - set_bool(content, &state->current_touch->force_mouse_emulation); - } else { - wlr_log(WLR_ERROR, "Unexpected data in touch parser: %s=\"%s\"", - nodename, content); + xmlNode *child; + char *key, *content; + LAB_XML_FOR_EACH(node, child, key, content) { + if (!strcasecmp(key, "deviceName")) { + xstrdup_replace(touch_config->device_name, content); + } else if (!strcasecmp(key, "mapToOutput")) { + xstrdup_replace(touch_config->output_name, content); + } else if (!strcasecmp(key, "mouseEmulation")) { + set_bool(content, &touch_config->force_mouse_emulation); + } else { + wlr_log(WLR_ERROR, "Unexpected data in touch parser: %s=\"%s\"", + key, content); + } } } @@ -1060,8 +1057,8 @@ entry(xmlNode *node, char *nodename, char *content, struct parser_state *state) fill_mouse_context(node); return; } - if (state->in_touch) { - fill_touch(nodename, content, state); + if (!strcasecmp(nodename, "touch")) { + fill_touch(node); return; } if (state->in_libinput_category) { From a0d2e6a64bdeb83c177a3a4780184c8a94c9131d Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 12 Apr 2025 01:40:39 +0900 Subject: [PATCH 036/248] rcxml: rewrite parser --- src/config/rcxml.c | 314 ++++++++++++++++++++++----------------------- 1 file changed, 153 insertions(+), 161 deletions(-) diff --git a/src/config/rcxml.c b/src/config/rcxml.c index eef58f0b..4d69545a 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -700,112 +700,102 @@ err: } static void -fill_libinput_category(char *nodename, char *content, struct parser_state *state) +fill_libinput_category(xmlNode *node) { /* * Create a new profile (libinput-category) on `` * so that the 'default' profile can be created without even providing a * category="" attribute (same as ...) */ - if (!strcmp(nodename, "device.libinput")) { - state->current_libinput_category = libinput_category_create(); - } + struct libinput_category *category = libinput_category_create(); - if (!content) { - return; - } + xmlNode *child; + char *key, *content; + LAB_XML_FOR_EACH(node, child, key, content) { + if (!strcmp(key, "category")) { + /* + * First we try to get a type based on a number of + * pre-defined terms, for example: 'default', 'touch', + * 'touchpad' and 'non-touch' + */ + category->type = get_device_type(content); - if (!state->current_libinput_category) { - return; - } - - string_truncate_at_pattern(nodename, ".device.libinput"); - - if (!strcmp(nodename, "category")) { - /* - * First we try to get a type based on a number of pre-defined - * terms, for example: 'default', 'touch', 'touchpad' and - * 'non-touch' - */ - state->current_libinput_category->type = get_device_type(content); - - /* - * If we couldn't match against any of those terms, we use the - * provided value to define the device name that the settings - * should be applicable to. - */ - if (state->current_libinput_category->type == LAB_LIBINPUT_DEVICE_NONE) { - xstrdup_replace(state->current_libinput_category->name, content); - } - } else if (!strcasecmp(nodename, "naturalScroll")) { - set_bool_as_int(content, &state->current_libinput_category->natural_scroll); - } else if (!strcasecmp(nodename, "leftHanded")) { - set_bool_as_int(content, &state->current_libinput_category->left_handed); - } else if (!strcasecmp(nodename, "pointerSpeed")) { - set_float(content, &state->current_libinput_category->pointer_speed); - if (state->current_libinput_category->pointer_speed < -1) { - state->current_libinput_category->pointer_speed = -1; - } else if (state->current_libinput_category->pointer_speed > 1) { - state->current_libinput_category->pointer_speed = 1; - } - } else if (!strcasecmp(nodename, "tap")) { - int ret = parse_bool(content, -1); - if (ret < 0) { - return; - } - state->current_libinput_category->tap = ret - ? LIBINPUT_CONFIG_TAP_ENABLED - : LIBINPUT_CONFIG_TAP_DISABLED; - } else if (!strcasecmp(nodename, "tapButtonMap")) { - if (!strcmp(content, "lrm")) { - state->current_libinput_category->tap_button_map = - LIBINPUT_CONFIG_TAP_MAP_LRM; - } else if (!strcmp(content, "lmr")) { - state->current_libinput_category->tap_button_map = - LIBINPUT_CONFIG_TAP_MAP_LMR; - } else { - wlr_log(WLR_ERROR, "invalid tapButtonMap"); - } - } else if (!strcasecmp(nodename, "tapAndDrag")) { - int ret = parse_bool(content, -1); - if (ret < 0) { - return; - } - state->current_libinput_category->tap_and_drag = ret - ? LIBINPUT_CONFIG_DRAG_ENABLED - : LIBINPUT_CONFIG_DRAG_DISABLED; - } else if (!strcasecmp(nodename, "dragLock")) { - if (!strcasecmp(content, "timeout")) { - /* "timeout" enables drag-lock with timeout */ - state->current_libinput_category->drag_lock = - LIBINPUT_CONFIG_DRAG_LOCK_ENABLED; - return; - } - int ret = parse_bool(content, -1); - if (ret < 0) { - return; - } - /* "yes" enables drag-lock, without timeout if libinput >= 1.27 */ - int enabled = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED; + /* + * If we couldn't match against any of those terms, we + * use the provided value to define the device name + * that the settings should be applicable to. + */ + if (category->type == LAB_LIBINPUT_DEVICE_NONE) { + xstrdup_replace(category->name, content); + } + } else if (!strcasecmp(key, "naturalScroll")) { + set_bool_as_int(content, &category->natural_scroll); + } else if (!strcasecmp(key, "leftHanded")) { + set_bool_as_int(content, &category->left_handed); + } else if (!strcasecmp(key, "pointerSpeed")) { + set_float(content, &category->pointer_speed); + if (category->pointer_speed < -1) { + category->pointer_speed = -1; + } else if (category->pointer_speed > 1) { + category->pointer_speed = 1; + } + } else if (!strcasecmp(key, "tap")) { + int ret = parse_bool(content, -1); + if (ret < 0) { + continue; + } + category->tap = ret + ? LIBINPUT_CONFIG_TAP_ENABLED + : LIBINPUT_CONFIG_TAP_DISABLED; + } else if (!strcasecmp(key, "tapButtonMap")) { + if (!strcmp(content, "lrm")) { + category->tap_button_map = + LIBINPUT_CONFIG_TAP_MAP_LRM; + } else if (!strcmp(content, "lmr")) { + category->tap_button_map = + LIBINPUT_CONFIG_TAP_MAP_LMR; + } else { + wlr_log(WLR_ERROR, "invalid tapButtonMap"); + } + } else if (!strcasecmp(key, "tapAndDrag")) { + int ret = parse_bool(content, -1); + if (ret < 0) { + continue; + } + category->tap_and_drag = ret + ? LIBINPUT_CONFIG_DRAG_ENABLED + : LIBINPUT_CONFIG_DRAG_DISABLED; + } else if (!strcasecmp(key, "dragLock")) { + if (!strcasecmp(content, "timeout")) { + /* "timeout" enables drag-lock with timeout */ + category->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED; + continue; + } + int ret = parse_bool(content, -1); + if (ret < 0) { + continue; + } + /* "yes" enables drag-lock, without timeout if libinput >= 1.27 */ + int enabled = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED; #if HAVE_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY - enabled = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY; + enabled = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY; #endif - state->current_libinput_category->drag_lock = ret ? - enabled : LIBINPUT_CONFIG_DRAG_LOCK_DISABLED; - } else if (!strcasecmp(nodename, "threeFingerDrag")) { + category->drag_lock = ret ? + enabled : LIBINPUT_CONFIG_DRAG_LOCK_DISABLED; + } else if (!strcasecmp(key, "threeFingerDrag")) { #if HAVE_LIBINPUT_CONFIG_3FG_DRAG_ENABLED_3FG if (!strcmp(content, "3")) { - state->current_libinput_category->three_finger_drag = + category->three_finger_drag = LIBINPUT_CONFIG_3FG_DRAG_ENABLED_3FG; } else if (!strcmp(content, "4")) { - state->current_libinput_category->three_finger_drag = + category->three_finger_drag = LIBINPUT_CONFIG_3FG_DRAG_ENABLED_4FG; } else { int ret = parse_bool(content, -1); if (ret < 0) { - return; + continue; } - state->current_libinput_category->three_finger_drag = ret + category->three_finger_drag = ret ? LIBINPUT_CONFIG_3FG_DRAG_ENABLED_3FG : LIBINPUT_CONFIG_3FG_DRAG_DISABLED; } @@ -813,80 +803,82 @@ fill_libinput_category(char *nodename, char *content, struct parser_state *state wlr_log(WLR_ERROR, " is only" " supported in libinput >= 1.28"); #endif - } else if (!strcasecmp(nodename, "accelProfile")) { - state->current_libinput_category->accel_profile = - get_accel_profile(content); - } else if (!strcasecmp(nodename, "middleEmulation")) { - int ret = parse_bool(content, -1); - if (ret < 0) { - return; - } - state->current_libinput_category->middle_emu = ret - ? LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED - : LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; - } else if (!strcasecmp(nodename, "disableWhileTyping")) { - int ret = parse_bool(content, -1); - if (ret < 0) { - return; - } - state->current_libinput_category->dwt = ret - ? LIBINPUT_CONFIG_DWT_ENABLED - : LIBINPUT_CONFIG_DWT_DISABLED; - } else if (!strcasecmp(nodename, "clickMethod")) { - if (!strcasecmp(content, "none")) { - state->current_libinput_category->click_method = - LIBINPUT_CONFIG_CLICK_METHOD_NONE; - } else if (!strcasecmp(content, "clickfinger")) { - state->current_libinput_category->click_method = - LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; - } else if (!strcasecmp(content, "buttonAreas")) { - state->current_libinput_category->click_method = - LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; - } else { - wlr_log(WLR_ERROR, "invalid clickMethod"); - } - } else if (!strcasecmp(nodename, "scrollMethod")) { - if (!strcasecmp(content, "none")) { - state->current_libinput_category->scroll_method = - LIBINPUT_CONFIG_SCROLL_NO_SCROLL; - } else if (!strcasecmp(content, "edge")) { - state->current_libinput_category->scroll_method = - LIBINPUT_CONFIG_SCROLL_EDGE; - } else if (!strcasecmp(content, "twofinger")) { - state->current_libinput_category->scroll_method = - LIBINPUT_CONFIG_SCROLL_2FG; - } else { - wlr_log(WLR_ERROR, "invalid scrollMethod"); - } - } else if (!strcasecmp(nodename, "sendEventsMode")) { - state->current_libinput_category->send_events_mode = - get_send_events_mode(content); - } else if (!strcasecmp(nodename, "calibrationMatrix")) { - errno = 0; - state->current_libinput_category->have_calibration_matrix = true; - float *mat = state->current_libinput_category->calibration_matrix; - gchar **elements = g_strsplit(content, " ", -1); - guint i = 0; - for (; elements[i]; ++i) { - char *end_str = NULL; - mat[i] = strtof(elements[i], &end_str); - if (errno == ERANGE || *end_str != '\0' || i == 6 || *elements[i] == '\0') { - wlr_log(WLR_ERROR, "invalid calibration matrix element" - " %s (index %d), expect six floats", - elements[i], i); - state->current_libinput_category->have_calibration_matrix = false; - errno = 0; - break; + } else if (!strcasecmp(key, "accelProfile")) { + category->accel_profile = + get_accel_profile(content); + } else if (!strcasecmp(key, "middleEmulation")) { + int ret = parse_bool(content, -1); + if (ret < 0) { + continue; } + category->middle_emu = ret + ? LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED + : LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; + } else if (!strcasecmp(key, "disableWhileTyping")) { + int ret = parse_bool(content, -1); + if (ret < 0) { + continue; + } + category->dwt = ret + ? LIBINPUT_CONFIG_DWT_ENABLED + : LIBINPUT_CONFIG_DWT_DISABLED; + } else if (!strcasecmp(key, "clickMethod")) { + if (!strcasecmp(content, "none")) { + category->click_method = + LIBINPUT_CONFIG_CLICK_METHOD_NONE; + } else if (!strcasecmp(content, "clickfinger")) { + category->click_method = + LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; + } else if (!strcasecmp(content, "buttonAreas")) { + category->click_method = + LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; + } else { + wlr_log(WLR_ERROR, "invalid clickMethod"); + } + } else if (!strcasecmp(key, "scrollMethod")) { + if (!strcasecmp(content, "none")) { + category->scroll_method = + LIBINPUT_CONFIG_SCROLL_NO_SCROLL; + } else if (!strcasecmp(content, "edge")) { + category->scroll_method = + LIBINPUT_CONFIG_SCROLL_EDGE; + } else if (!strcasecmp(content, "twofinger")) { + category->scroll_method = + LIBINPUT_CONFIG_SCROLL_2FG; + } else { + wlr_log(WLR_ERROR, "invalid scrollMethod"); + } + } else if (!strcasecmp(key, "sendEventsMode")) { + category->send_events_mode = + get_send_events_mode(content); + } else if (!strcasecmp(key, "calibrationMatrix")) { + errno = 0; + category->have_calibration_matrix = true; + float *mat = category->calibration_matrix; + gchar **elements = g_strsplit(content, " ", -1); + guint i = 0; + for (; elements[i]; ++i) { + char *end_str = NULL; + mat[i] = strtof(elements[i], &end_str); + if (errno == ERANGE || *end_str != '\0' || i == 6 + || *elements[i] == '\0') { + wlr_log(WLR_ERROR, "invalid calibration " + "matrix element %s (index %d), " + "expect six floats", elements[i], i); + category->have_calibration_matrix = false; + errno = 0; + break; + } + } + if (i != 6 && category->have_calibration_matrix) { + wlr_log(WLR_ERROR, "wrong number of calibration " + "matrix elements, expected 6, got %d", i); + category->have_calibration_matrix = false; + } + g_strfreev(elements); + } else if (!strcasecmp(key, "scrollFactor")) { + set_double(content, &category->scroll_factor); } - if (i != 6 && state->current_libinput_category->have_calibration_matrix) { - wlr_log(WLR_ERROR, "wrong number of calibration matrix elements," - " expected 6, got %d", i); - state->current_libinput_category->have_calibration_matrix = false; - } - g_strfreev(elements); - } else if (!strcasecmp(nodename, "scrollFactor")) { - set_double(content, &state->current_libinput_category->scroll_factor); } } @@ -1061,8 +1053,8 @@ entry(xmlNode *node, char *nodename, char *content, struct parser_state *state) fill_touch(node); return; } - if (state->in_libinput_category) { - fill_libinput_category(nodename, content, state); + if (!strcasecmp(nodename, "device.libinput")) { + fill_libinput_category(node); return; } if (state->in_regions) { From 73dd3b8de1f9b971b6e9e79647bb946740b2ab80 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 12 Apr 2025 01:48:11 +0900 Subject: [PATCH 037/248] rcxml: rewrite parser --- src/config/rcxml.c | 77 ++++++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 33 deletions(-) diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 4d69545a..cddf8951 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -391,40 +391,51 @@ fill_window_switcher_field(char *nodename, char *content, struct parser_state *s } static void -fill_region(char *nodename, char *content, struct parser_state *state) +fill_region(xmlNode *node) { - string_truncate_at_pattern(nodename, ".region.regions"); + struct region *region = znew(*region); + wl_list_append(&rc.regions, ®ion->link); - if (!strcasecmp(nodename, "region.regions")) { - state->current_region = znew(*state->current_region); - wl_list_append(&rc.regions, &state->current_region->link); - } else if (!content) { - /* intentionally left empty */ - } else if (!state->current_region) { - wlr_log(WLR_ERROR, "Expecting current_region->name) { - state->current_region->name = xstrdup(content); + xmlNode *child; + char *key, *content; + LAB_XML_FOR_EACH(node, child, key, content) { + if (!strcasecmp(key, "name")) { + xstrdup_replace(region->name, content); + } else if (strstr("xywidtheight", key) + && !strchr(content, '%')) { + wlr_log(WLR_ERROR, "Removing invalid region " + "'%s': %s='%s' misses a trailing %%", + region->name, key, content); + wl_list_remove(®ion->link); + zfree(region->name); + zfree(region); + return; + } else if (!strcmp(key, "x")) { + region->percentage.x = atoi(content); + } else if (!strcmp(key, "y")) { + region->percentage.y = atoi(content); + } else if (!strcmp(key, "width")) { + region->percentage.width = atoi(content); + } else if (!strcmp(key, "height")) { + region->percentage.height = atoi(content); + } else { + wlr_log(WLR_ERROR, "Unexpected data in region " + "parser: %s=\"%s\"", key, content); + } + } +} + +static void +fill_regions(xmlNode *node) +{ + /* TODO: make sure is empty here */ + + xmlNode *child; + char *key, *content; + LAB_XML_FOR_EACH(node, child, key, content) { + if (!strcasecmp(key, "region")) { + fill_region(child); } - } else if (strstr("xywidtheight", nodename) && !strchr(content, '%')) { - wlr_log(WLR_ERROR, "Removing invalid region '%s': %s='%s' misses" - " a trailing %%", state->current_region->name, nodename, content); - wl_list_remove(&state->current_region->link); - zfree(state->current_region->name); - zfree(state->current_region); - } else if (!strcmp(nodename, "x")) { - state->current_region->percentage.x = atoi(content); - } else if (!strcmp(nodename, "y")) { - state->current_region->percentage.y = atoi(content); - } else if (!strcmp(nodename, "width")) { - state->current_region->percentage.width = atoi(content); - } else if (!strcmp(nodename, "height")) { - state->current_region->percentage.height = atoi(content); - } else { - wlr_log(WLR_ERROR, "Unexpected data in region parser: %s=\"%s\"", - nodename, content); } } @@ -1057,8 +1068,8 @@ entry(xmlNode *node, char *nodename, char *content, struct parser_state *state) fill_libinput_category(node); return; } - if (state->in_regions) { - fill_region(nodename, content, state); + if (!strcasecmp(nodename, "regions")) { + fill_regions(node); return; } if (state->in_window_switcher_field) { From 936c5f6df1a1b8be00c7bac20b6bdf8396613eda Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 12 Apr 2025 02:08:45 +0900 Subject: [PATCH 038/248] rcxml: rewrite parser --- include/osd.h | 1 - src/config/rcxml.c | 39 +++++++++++++++++++++++---------------- src/osd-field.c | 7 ------- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/include/osd.h b/include/osd.h index 806b88d4..971389d2 100644 --- a/include/osd.h +++ b/include/osd.h @@ -56,7 +56,6 @@ void osd_field_get_content(struct window_switcher_field *field, struct buf *buf, struct view *view); /* Used by rcxml.c when parsing the config */ -struct window_switcher_field *osd_field_create(void); void osd_field_arg_from_xml_node(struct window_switcher_field *field, const char *nodename, const char *content); bool osd_field_is_valid(struct window_switcher_field *field); diff --git a/src/config/rcxml.c b/src/config/rcxml.c index cddf8951..92683cbe 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -372,21 +372,29 @@ clear_window_switcher_fields(void) } static void -fill_window_switcher_field(char *nodename, char *content, struct parser_state *state) +fill_window_switcher_field(xmlNode *node) { - if (!strcasecmp(nodename, "field.fields.windowswitcher")) { - state->current_field = osd_field_create(); - wl_list_append(&rc.window_switcher.fields, &state->current_field->link); - return; - } + struct window_switcher_field *field = znew(*field); + wl_list_append(&rc.window_switcher.fields, &field->link); - string_truncate_at_pattern(nodename, ".field.fields.windowswitcher"); - if (!content) { - /* intentionally left empty */ - } else if (!state->current_field) { - wlr_log(WLR_ERROR, "no "); - } else { - osd_field_arg_from_xml_node(state->current_field, nodename, content); + xmlNode *child; + char *key, *content; + LAB_XML_FOR_EACH(node, child, key, content) { + osd_field_arg_from_xml_node(field, key, content); + } +} + +static void +fill_window_switcher_fields(xmlNode *node) +{ + clear_window_switcher_fields(); + + xmlNode *child; + char *key, *content; + LAB_XML_FOR_EACH(node, child, key, content) { + if (!strcasecmp(key, "field")) { + fill_window_switcher_field(child); + } } } @@ -1072,8 +1080,8 @@ entry(xmlNode *node, char *nodename, char *content, struct parser_state *state) fill_regions(node); return; } - if (state->in_window_switcher_field) { - fill_window_switcher_field(nodename, content, state); + if (!strcasecmp(nodename, "fields.windowSwitcher")) { + fill_window_switcher_fields(node); return; } if (state->in_window_rules) { @@ -1428,7 +1436,6 @@ xml_tree_walk(xmlNode *node, struct parser_state *state) continue; } if (!strcasecmp((char *)n->name, "fields")) { - clear_window_switcher_fields(); state->in_window_switcher_field = true; traverse(n, state); state->in_window_switcher_field = false; diff --git a/src/osd-field.c b/src/osd-field.c index 2885febe..0819e1e6 100644 --- a/src/osd-field.c +++ b/src/osd-field.c @@ -297,13 +297,6 @@ reset_format: buf_reset(&field_result); } -struct window_switcher_field * -osd_field_create(void) -{ - struct window_switcher_field *field = znew(*field); - return field; -} - void osd_field_arg_from_xml_node(struct window_switcher_field *field, const char *nodename, const char *content) From ba00f57dad2d8b1161b38d5e060ad84891347b87 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 12 Apr 2025 02:40:31 +0900 Subject: [PATCH 039/248] rcxml: rewrite parser --- src/config/rcxml.c | 144 ++++++++++++++++++++++----------------------- 1 file changed, 71 insertions(+), 73 deletions(-) diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 92683cbe..0147c072 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -282,82 +282,82 @@ set_property(const char *str, enum property *variable) *variable = ret ? LAB_PROP_TRUE : LAB_PROP_FALSE; } +static void append_actions(xmlNode *node, struct wl_list *list); + static void -fill_window_rule(char *nodename, char *content, struct parser_state *state) +fill_window_rule(xmlNode *node) { - if (!strcasecmp(nodename, "windowRule.windowRules")) { - state->current_window_rule = znew(*state->current_window_rule); - state->current_window_rule->window_type = -1; // Window types are >= 0 - wl_list_append(&rc.window_rules, &state->current_window_rule->link); - wl_list_init(&state->current_window_rule->actions); - return; + struct window_rule *window_rule = znew(*window_rule); + window_rule->window_type = -1; // Window types are >= 0 + wl_list_append(&rc.window_rules, &window_rule->link); + wl_list_init(&window_rule->actions); + + xmlNode *child; + char *key, *content; + LAB_XML_FOR_EACH(node, child, key, content) { + /* Criteria */ + if (!strcmp(key, "identifier")) { + xstrdup_replace(window_rule->identifier, content); + } else if (!strcmp(key, "title")) { + xstrdup_replace(window_rule->title, content); + } else if (!strcmp(key, "type")) { + window_rule->window_type = parse_window_type(content); + } else if (!strcasecmp(key, "matchOnce")) { + set_bool(content, &window_rule->match_once); + } else if (!strcasecmp(key, "sandboxEngine")) { + xstrdup_replace(window_rule->sandbox_engine, content); + } else if (!strcasecmp(key, "sandboxAppId")) { + xstrdup_replace(window_rule->sandbox_app_id, content); + + /* Event */ + } else if (!strcmp(key, "event")) { + /* + * This is just in readiness for adding any other types of + * events in the future. We default to onFirstMap anyway. + */ + if (!strcasecmp(content, "onFirstMap")) { + window_rule->event = LAB_WINDOW_RULE_EVENT_ON_FIRST_MAP; + } + + /* Properties */ + } else if (!strcasecmp(key, "serverDecoration")) { + set_property(content, &window_rule->server_decoration); + } else if (!strcasecmp(key, "iconPriority")) { + if (!strcasecmp(content, "client")) { + window_rule->icon_prefer_client = LAB_PROP_TRUE; + } else if (!strcasecmp(content, "server")) { + window_rule->icon_prefer_client = LAB_PROP_FALSE; + } else { + wlr_log(WLR_ERROR, + "Invalid value for window rule property 'iconPriority'"); + } + } else if (!strcasecmp(key, "skipTaskbar")) { + set_property(content, &window_rule->skip_taskbar); + } else if (!strcasecmp(key, "skipWindowSwitcher")) { + set_property(content, &window_rule->skip_window_switcher); + } else if (!strcasecmp(key, "ignoreFocusRequest")) { + set_property(content, &window_rule->ignore_focus_request); + } else if (!strcasecmp(key, "ignoreConfigureRequest")) { + set_property(content, &window_rule->ignore_configure_request); + } else if (!strcasecmp(key, "fixedPosition")) { + set_property(content, &window_rule->fixed_position); + } } - string_truncate_at_pattern(nodename, ".windowrule.windowrules"); - if (!content) { - /* nop */ - } else if (!state->current_window_rule) { - wlr_log(WLR_ERROR, "no window-rule"); + append_actions(node, &window_rule->actions); +} - /* Criteria */ - } else if (!strcmp(nodename, "identifier")) { - xstrdup_replace(state->current_window_rule->identifier, content); - } else if (!strcmp(nodename, "title")) { - xstrdup_replace(state->current_window_rule->title, content); - } else if (!strcmp(nodename, "type")) { - state->current_window_rule->window_type = parse_window_type(content); - } else if (!strcasecmp(nodename, "matchOnce")) { - set_bool(content, &state->current_window_rule->match_once); - } else if (!strcasecmp(nodename, "sandboxEngine")) { - xstrdup_replace(state->current_window_rule->sandbox_engine, content); - } else if (!strcasecmp(nodename, "sandboxAppId")) { - xstrdup_replace(state->current_window_rule->sandbox_app_id, content); +static void +fill_window_rules(xmlNode *node) +{ + /* TODO: make sure is empty here */ - /* Event */ - } else if (!strcmp(nodename, "event")) { - /* - * This is just in readiness for adding any other types of - * events in the future. We default to onFirstMap anyway. - */ - if (!strcasecmp(content, "onFirstMap")) { - state->current_window_rule->event = LAB_WINDOW_RULE_EVENT_ON_FIRST_MAP; + xmlNode *child; + char *key, *content; + LAB_XML_FOR_EACH(node, child, key, content) { + if (!strcasecmp(key, "windowRule")) { + fill_window_rule(child); } - - /* Properties */ - } else if (!strcasecmp(nodename, "serverDecoration")) { - set_property(content, &state->current_window_rule->server_decoration); - } else if (!strcasecmp(nodename, "iconPriority")) { - if (!strcasecmp(content, "client")) { - state->current_window_rule->icon_prefer_client = LAB_PROP_TRUE; - } else if (!strcasecmp(content, "server")) { - state->current_window_rule->icon_prefer_client = LAB_PROP_FALSE; - } else { - wlr_log(WLR_ERROR, - "Invalid value for window rule property 'iconPriority'"); - } - } else if (!strcasecmp(nodename, "skipTaskbar")) { - set_property(content, &state->current_window_rule->skip_taskbar); - } else if (!strcasecmp(nodename, "skipWindowSwitcher")) { - set_property(content, &state->current_window_rule->skip_window_switcher); - } else if (!strcasecmp(nodename, "ignoreFocusRequest")) { - set_property(content, &state->current_window_rule->ignore_focus_request); - } else if (!strcasecmp(nodename, "ignoreConfigureRequest")) { - set_property(content, &state->current_window_rule->ignore_configure_request); - } else if (!strcasecmp(nodename, "fixedPosition")) { - set_property(content, &state->current_window_rule->fixed_position); - - /* Actions */ - } else if (!strcmp(nodename, "name.action")) { - state->current_window_rule_action = action_create(content); - if (state->current_window_rule_action) { - wl_list_append(&state->current_window_rule->actions, - &state->current_window_rule_action->link); - } - } else if (!state->current_window_rule_action) { - wlr_log(WLR_ERROR, "expect element first. " - "nodename: '%s' content: '%s'", nodename, content); - } else { - action_arg_from_xml_node(state->current_window_rule_action, nodename, content); } } @@ -487,8 +487,6 @@ fill_action_query(struct action *action, xmlNode *node, struct view_query *query } } -static void append_actions(xmlNode *node, struct wl_list *list); - static void parse_action_args(xmlNode *node, struct action *action) { @@ -1084,8 +1082,8 @@ entry(xmlNode *node, char *nodename, char *content, struct parser_state *state) fill_window_switcher_fields(node); return; } - if (state->in_window_rules) { - fill_window_rule(nodename, content, state); + if (!strcasecmp(nodename, "windowRules")) { + fill_window_rules(node); return; } From a865cc077766e3daabb0f7d762d9b77d547013c8 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 12 Apr 2025 02:50:05 +0900 Subject: [PATCH 040/248] rcxml: reorder functions --- src/config/rcxml.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 0147c072..b7251c94 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -943,6 +943,27 @@ set_font_attr(struct font *font, const char *nodename, const char *content) } } +static enum font_place +enum_font_place(const char *place) +{ + if (!place || place[0] == '\0') { + return FONT_PLACE_NONE; + } + if (!strcasecmp(place, "ActiveWindow")) { + return FONT_PLACE_ACTIVEWINDOW; + } else if (!strcasecmp(place, "InactiveWindow")) { + return FONT_PLACE_INACTIVEWINDOW; + } else if (!strcasecmp(place, "MenuHeader")) { + return FONT_PLACE_MENUHEADER; + } else if (!strcasecmp(place, "MenuItem")) { + return FONT_PLACE_MENUITEM; + } else if (!strcasecmp(place, "OnScreenDisplay") + || !strcasecmp(place, "OSD")) { + return FONT_PLACE_OSD; + } + return FONT_PLACE_UNKNOWN; +} + static void fill_font(char *nodename, char *content, enum font_place place) { @@ -986,27 +1007,6 @@ fill_font(char *nodename, char *content, enum font_place place) } } -static enum font_place -enum_font_place(const char *place) -{ - if (!place || place[0] == '\0') { - return FONT_PLACE_NONE; - } - if (!strcasecmp(place, "ActiveWindow")) { - return FONT_PLACE_ACTIVEWINDOW; - } else if (!strcasecmp(place, "InactiveWindow")) { - return FONT_PLACE_INACTIVEWINDOW; - } else if (!strcasecmp(place, "MenuHeader")) { - return FONT_PLACE_MENUHEADER; - } else if (!strcasecmp(place, "MenuItem")) { - return FONT_PLACE_MENUITEM; - } else if (!strcasecmp(place, "OnScreenDisplay") - || !strcasecmp(place, "OSD")) { - return FONT_PLACE_OSD; - } - return FONT_PLACE_UNKNOWN; -} - static void set_adaptive_sync_mode(const char *str, enum adaptive_sync_mode *variable) { From 619cae67fa0cee719776647ec6bb8e7a518c94c9 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 12 Apr 2025 03:09:14 +0900 Subject: [PATCH 041/248] rcxml: rewrite parser --- src/config/rcxml.c | 94 +++++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 51 deletions(-) diff --git a/src/config/rcxml.c b/src/config/rcxml.c index b7251c94..bfd9155d 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -965,45 +965,50 @@ enum_font_place(const char *place) } static void -fill_font(char *nodename, char *content, enum font_place place) +fill_font(xmlNode *node) { - if (!content) { - return; + enum font_place font_place = FONT_PLACE_NONE; + char buf[256]; + if (lab_xml_get_string(node, "place", buf, sizeof(buf))) { + font_place = enum_font_place(buf); } - string_truncate_at_pattern(nodename, ".font.theme"); - switch (place) { - case FONT_PLACE_NONE: - /* - * If is used without a place="" - * attribute, we set all font variables - */ - set_font_attr(&rc.font_activewindow, nodename, content); - set_font_attr(&rc.font_inactivewindow, nodename, content); - set_font_attr(&rc.font_menuheader, nodename, content); - set_font_attr(&rc.font_menuitem, nodename, content); - set_font_attr(&rc.font_osd, nodename, content); - break; - case FONT_PLACE_ACTIVEWINDOW: - set_font_attr(&rc.font_activewindow, nodename, content); - break; - case FONT_PLACE_INACTIVEWINDOW: - set_font_attr(&rc.font_inactivewindow, nodename, content); - break; - case FONT_PLACE_MENUHEADER: - set_font_attr(&rc.font_menuheader, nodename, content); - break; - case FONT_PLACE_MENUITEM: - set_font_attr(&rc.font_menuitem, nodename, content); - break; - case FONT_PLACE_OSD: - set_font_attr(&rc.font_osd, nodename, content); - break; + xmlNode *child; + char *key, *content; + LAB_XML_FOR_EACH(node, child, key, content) { + switch (font_place) { + case FONT_PLACE_NONE: + /* + * If is used without a + * place="" attribute, we set all font variables + */ + set_font_attr(&rc.font_activewindow, key, content); + set_font_attr(&rc.font_inactivewindow, key, content); + set_font_attr(&rc.font_menuheader, key, content); + set_font_attr(&rc.font_menuitem, key, content); + set_font_attr(&rc.font_osd, key, content); + break; + case FONT_PLACE_ACTIVEWINDOW: + set_font_attr(&rc.font_activewindow, key, content); + break; + case FONT_PLACE_INACTIVEWINDOW: + set_font_attr(&rc.font_inactivewindow, key, content); + break; + case FONT_PLACE_MENUHEADER: + set_font_attr(&rc.font_menuheader, key, content); + break; + case FONT_PLACE_MENUITEM: + set_font_attr(&rc.font_menuitem, key, content); + break; + case FONT_PLACE_OSD: + set_font_attr(&rc.font_osd, key, content); + break; - /* TODO: implement for all font places */ + /* TODO: implement for all font places */ - default: - break; + default: + break; + } } } @@ -1039,9 +1044,6 @@ set_tearing_mode(const char *str, enum tearing_mode *variable) static void entry(xmlNode *node, char *nodename, char *content, struct parser_state *state) { - /* current */ - static enum font_place font_place = FONT_PLACE_NONE; - static uint32_t button_map_from; if (!nodename) { @@ -1086,6 +1088,10 @@ entry(xmlNode *node, char *nodename, char *content, struct parser_state *state) fill_window_rules(node); return; } + if (!strcasecmp(nodename, "font.theme")) { + fill_font(node); + return; + } /* handle nodes without content, e.g. */ if (!strcmp(nodename, "default.keyboard")) { @@ -1124,12 +1130,6 @@ entry(xmlNode *node, char *nodename, char *content, struct parser_state *state) if (!content) { return; } - if (!strcmp(nodename, "place.font.theme")) { - font_place = enum_font_place(content); - if (font_place == FONT_PLACE_UNKNOWN) { - wlr_log(WLR_ERROR, "invalid font place %s", content); - } - } if (!strcmp(nodename, "decoration.core")) { if (!strcmp(content, "client")) { @@ -1178,14 +1178,6 @@ entry(xmlNode *node, char *nodename, char *content, struct parser_state *state) set_bool(content, &rc.shadows_enabled); } else if (!strcasecmp(nodename, "dropShadowsOnTiled.theme")) { set_bool(content, &rc.shadows_on_tiled); - } else if (!strcmp(nodename, "name.font.theme")) { - fill_font(nodename, content, font_place); - } else if (!strcmp(nodename, "size.font.theme")) { - fill_font(nodename, content, font_place); - } else if (!strcmp(nodename, "slant.font.theme")) { - fill_font(nodename, content, font_place); - } else if (!strcmp(nodename, "weight.font.theme")) { - fill_font(nodename, content, font_place); } else if (!strcasecmp(nodename, "followMouse.focus")) { set_bool(content, &rc.focus_follow_mouse); } else if (!strcasecmp(nodename, "followMouseRequiresMovement.focus")) { From 67f36d9e1309ab61fc8d98f031a6078f8382934a Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 12 Apr 2025 03:21:13 +0900 Subject: [PATCH 042/248] rcxml: rewrite parser --- src/config/rcxml.c | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/config/rcxml.c b/src/config/rcxml.c index bfd9155d..7881e762 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -678,6 +678,30 @@ fill_touch(xmlNode *node) } } +static void +fill_tablet_button_map(xmlNode *node) +{ + uint32_t map_from; + uint32_t map_to; + char buf[256]; + + if (lab_xml_get_string(node, "button", buf, sizeof(buf))) { + map_from = tablet_button_from_str(buf); + } else { + wlr_log(WLR_ERROR, "Invalid 'button' argument for tablet button mapping"); + return; + } + + if (lab_xml_get_string(node, "to", buf, sizeof(buf))) { + map_to = mousebind_button_from_str(buf, NULL); + } else { + wlr_log(WLR_ERROR, "Invalid 'to' argument for tablet button mapping"); + return; + } + + tablet_button_mapping_add(map_from, map_to); +} + static int get_accel_profile(const char *s) { @@ -1044,8 +1068,6 @@ set_tearing_mode(const char *str, enum tearing_mode *variable) static void entry(xmlNode *node, char *nodename, char *content, struct parser_state *state) { - static uint32_t button_map_from; - if (!nodename) { return; } @@ -1092,6 +1114,10 @@ entry(xmlNode *node, char *nodename, char *content, struct parser_state *state) fill_font(node); return; } + if (!strcasecmp(nodename, "map.tablet")) { + fill_tablet_button_map(node); + return; + } /* handle nodes without content, e.g. */ if (!strcmp(nodename, "default.keyboard")) { @@ -1104,11 +1130,6 @@ entry(xmlNode *node, char *nodename, char *content, struct parser_state *state) return; } - if (!strcasecmp(nodename, "map.tablet")) { - button_map_from = UINT32_MAX; - return; - } - if (!strcasecmp(nodename, "prefix.desktops")) { xstrdup_replace(rc.workspace_config.prefix, content ? content : ""); return; @@ -1318,17 +1339,6 @@ entry(xmlNode *node, char *nodename, char *content, struct parser_state *state) rc.tablet.box.width = tablet_get_dbl_if_positive(content, "width"); } else if (!strcasecmp(nodename, "height.area.tablet")) { rc.tablet.box.height = tablet_get_dbl_if_positive(content, "height"); - } else if (!strcasecmp(nodename, "button.map.tablet")) { - button_map_from = tablet_button_from_str(content); - } else if (!strcasecmp(nodename, "to.map.tablet")) { - if (button_map_from != UINT32_MAX) { - uint32_t button_map_to = mousebind_button_from_str(content, NULL); - if (button_map_to != UINT32_MAX) { - tablet_button_mapping_add(button_map_from, button_map_to); - } - } else { - wlr_log(WLR_ERROR, "Missing 'button' argument for tablet button mapping"); - } } else if (!strcasecmp(nodename, "motion.tabletTool")) { rc.tablet_tool.motion = tablet_parse_motion(content); } else if (!strcasecmp(nodename, "relativeMotionSensitivity.tabletTool")) { From ad970544e18076d24fb201d6a27e8b23e70da782 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 12 Apr 2025 05:06:08 +0900 Subject: [PATCH 043/248] string-helper: add str_space_only() --- include/common/string-helpers.h | 6 ++++++ src/common/string-helpers.c | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/include/common/string-helpers.h b/include/common/string-helpers.h index e49fa110..0509d33c 100644 --- a/include/common/string-helpers.h +++ b/include/common/string-helpers.h @@ -9,6 +9,12 @@ */ bool string_null_or_empty(const char *s); +/** + * str_space_only - Check if the string only contains white-space characters + * @s: string to check + */ +bool str_space_only(const char *s); + /** * trim_last_field() - Trim last field of string splitting on provided delim * @buf: string to trim diff --git a/src/common/string-helpers.c b/src/common/string-helpers.c index 2d8f8de3..a0d73034 100644 --- a/src/common/string-helpers.c +++ b/src/common/string-helpers.c @@ -204,3 +204,14 @@ str_equal(const char *a, const char *b) { return a == b || (a && b && !strcmp(a, b)); } + +bool +str_space_only(const char *s) +{ + for (; *s; s++) { + if (!isspace(*s)) { + return false; + } + } + return true; +} From 2f414a438b7b8759548a7c87b34a24c348231499 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 12 Apr 2025 04:20:02 +0900 Subject: [PATCH 044/248] rcxml: simplify the logic to traverse xml tree --- src/config/rcxml.c | 237 ++++++++------------------------------------- 1 file changed, 38 insertions(+), 199 deletions(-) diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 7881e762..f545a9ef 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -38,35 +38,6 @@ #include "window-rules.h" #include "workspaces.h" -struct parser_state { - bool in_regions; - bool in_usable_area_override; - bool in_keybind; - bool in_mousebind; - bool in_touch; - bool in_libinput_category; - bool in_window_switcher_field; - bool in_window_rules; - bool in_action_query; - bool in_action_then_branch; - bool in_action_else_branch; - bool in_action_none_branch; - struct usable_area_override *current_usable_area_override; - struct keybind *current_keybind; - struct mousebind *current_mousebind; - struct touch_config_entry *current_touch; - struct libinput_category *current_libinput_category; - const char *current_mouse_context; - struct action *current_keybind_action; - struct action *current_mousebind_action; - struct region *current_region; - struct window_switcher_field *current_field; - struct window_rule *current_window_rule; - struct action *current_window_rule_action; - struct view_query *current_view_query; - struct action *current_child_action; -}; - /* for backward compatibility of */ static double mouse_scroll_factor = -1; @@ -1065,12 +1036,10 @@ set_tearing_mode(const char *str, enum tearing_mode *variable) } } -static void -entry(xmlNode *node, char *nodename, char *content, struct parser_state *state) +/* Returns true if the node's children should also be traversed */ +static bool +entry(xmlNode *node, char *nodename, char *content) { - if (!nodename) { - return; - } string_truncate_at_pattern(nodename, ".openbox_config"); string_truncate_at_pattern(nodename, ".labwc_config"); @@ -1078,81 +1047,45 @@ entry(xmlNode *node, char *nodename, char *content, struct parser_state *state) printf("%s: %s\n", nodename, content); } + /* handle nested nodes */ if (!strcasecmp(nodename, "margin")) { fill_usable_area_override(node); - return; - } - if (!strcasecmp(nodename, "keybind.keyboard")) { + } else if (!strcasecmp(nodename, "keybind.keyboard")) { fill_keybind(node); - return; - } - if (!strcasecmp(nodename, "context.mouse")) { + } else if (!strcasecmp(nodename, "context.mouse")) { fill_mouse_context(node); - return; - } - if (!strcasecmp(nodename, "touch")) { + } else if (!strcasecmp(nodename, "touch")) { fill_touch(node); - return; - } - if (!strcasecmp(nodename, "device.libinput")) { + } else if (!strcasecmp(nodename, "device.libinput")) { fill_libinput_category(node); - return; - } - if (!strcasecmp(nodename, "regions")) { + } else if (!strcasecmp(nodename, "regions")) { fill_regions(node); - return; - } - if (!strcasecmp(nodename, "fields.windowSwitcher")) { + } else if (!strcasecmp(nodename, "fields.windowSwitcher")) { fill_window_switcher_fields(node); - return; - } - if (!strcasecmp(nodename, "windowRules")) { + } else if (!strcasecmp(nodename, "windowRules")) { fill_window_rules(node); - return; - } - if (!strcasecmp(nodename, "font.theme")) { + } else if (!strcasecmp(nodename, "font.theme")) { fill_font(node); - return; - } - if (!strcasecmp(nodename, "map.tablet")) { + } else if (!strcasecmp(nodename, "map.tablet")) { fill_tablet_button_map(node); - return; - } /* handle nodes without content, e.g. */ - if (!strcmp(nodename, "default.keyboard")) { + } else if (!strcmp(nodename, "default.keyboard")) { load_default_key_bindings(); - return; - } - if (!strcmp(nodename, "devault.mouse") - || !strcmp(nodename, "default.mouse")) { + } else if (!strcmp(nodename, "default.mouse")) { load_default_mouse_bindings(); - return; - } + } else if (!strcasecmp(nodename, "prefix.desktops")) { + xstrdup_replace(rc.workspace_config.prefix, content); - if (!strcasecmp(nodename, "prefix.desktops")) { - xstrdup_replace(rc.workspace_config.prefix, content ? content : ""); - return; - } + } else if (!lab_xml_node_is_leaf(node)) { + /* parse children of nested nodes other than above */ + return true; - /* - * Nodenames where we want to honour !content have to be parsed above - * this point. An example of this is: - * - * - * - * - * - * In the case of the element having content, the node will be - * processed twice; first for the element itself (with no content) and - * then the content itself. In this situation xstrdup_replace() is - * called twice, but the end result is the right one. - */ - if (!content) { - return; - } + } else if (str_space_only(content)) { + /* ignore empty leaf nodes other than above */ - if (!strcmp(nodename, "decoration.core")) { + /* handle non-empty leaf nodes */ + } else if (!strcmp(nodename, "decoration.core")) { if (!strcmp(content, "client")) { rc.xdg_shell_server_side_deco = false; } else { @@ -1361,117 +1294,22 @@ entry(xmlNode *node, char *nodename, char *content, struct parser_state *state) } else if (!strcasecmp(nodename, "useFilter.magnifier")) { set_bool(content, &rc.mag_filter); } + + return false; } static void -process_node(xmlNode *node, struct parser_state *state) +traverse(xmlNode *node) { - char *content; - static char buffer[256]; - char *name; - - content = (char *)node->content; - if (xmlIsBlankNode(node)) { - return; - } - name = nodename(node, buffer, sizeof(buffer)); - entry(node, name, content, state); -} - -static void xml_tree_walk(xmlNode *node, struct parser_state *state); - -static void -traverse(xmlNode *n, struct parser_state *state) -{ - xmlAttr *attr; - - process_node(n, state); - for (attr = n->properties; attr; attr = attr->next) { - xml_tree_walk(attr->children, state); - } - xml_tree_walk(n->children, state); -} - -static void -xml_tree_walk(xmlNode *node, struct parser_state *state) -{ - for (xmlNode *n = node; n && n->name; n = n->next) { - if (!strcasecmp((char *)n->name, "comment")) { - continue; + xmlNode *child; + char *key, *content; + LAB_XML_FOR_EACH(node, child, key, content) { + (void)key; + char buffer[256]; + char *name = nodename(child, buffer, sizeof(buffer)); + if (entry(child, name, content)) { + traverse(child); } - if (!strcasecmp((char *)n->name, "margin")) { - state->in_usable_area_override = true; - traverse(n, state); - state->in_usable_area_override = false; - continue; - } - if (!strcasecmp((char *)n->name, "keybind")) { - state->in_keybind = true; - traverse(n, state); - state->in_keybind = false; - continue; - } - if (!strcasecmp((char *)n->name, "mousebind")) { - state->in_mousebind = true; - traverse(n, state); - state->in_mousebind = false; - continue; - } - if (!strcasecmp((char *)n->name, "touch")) { - state->in_touch = true; - traverse(n, state); - state->in_touch = false; - continue; - } - if (!strcasecmp((char *)n->name, "device")) { - state->in_libinput_category = true; - traverse(n, state); - state->in_libinput_category = false; - continue; - } - if (!strcasecmp((char *)n->name, "regions")) { - state->in_regions = true; - traverse(n, state); - state->in_regions = false; - continue; - } - if (!strcasecmp((char *)n->name, "fields")) { - state->in_window_switcher_field = true; - traverse(n, state); - state->in_window_switcher_field = false; - continue; - } - if (!strcasecmp((char *)n->name, "windowRules")) { - state->in_window_rules = true; - traverse(n, state); - state->in_window_rules = false; - continue; - } - if (!strcasecmp((char *)n->name, "query")) { - state->in_action_query = true; - traverse(n, state); - state->in_action_query = false; - continue; - } - if (!strcasecmp((char *)n->name, "then")) { - state->in_action_then_branch = true; - traverse(n, state); - state->in_action_then_branch = false; - continue; - } - if (!strcasecmp((char *)n->name, "else")) { - state->in_action_else_branch = true; - traverse(n, state); - state->in_action_else_branch = false; - continue; - } - if (!strcasecmp((char *)n->name, "none")) { - state->in_action_none_branch = true; - traverse(n, state); - state->in_action_none_branch = false; - continue; - } - traverse(n, state); } } @@ -1485,10 +1323,11 @@ rcxml_parse_xml(struct buf *b) wlr_log(WLR_ERROR, "error parsing config file"); return; } - struct parser_state init_state = {0}; xmlNode *root = xmlDocGetRootElement(d); + lab_xml_expand_dotted_attributes(root); - xml_tree_walk(root, &init_state); + traverse(root); + xmlFreeDoc(d); xmlCleanupParser(); } From e21fc065c412977c8cdc1aa09a18ab5b227bf651 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Sat, 26 Jul 2025 15:34:45 -0400 Subject: [PATCH 045/248] include: split output.h from labwc.h --- include/labwc.h | 72 -------------------------- include/output.h | 81 ++++++++++++++++++++++++++++++ src/action.c | 1 + src/common/scene-helpers.c | 1 + src/debug.c | 1 + src/desktop.c | 1 + src/edges.c | 1 + src/foreign-toplevel/wlr-foreign.c | 1 + src/input/cursor.c | 1 + src/input/ime.c | 1 + src/interactive.c | 1 + src/layers.c | 1 + src/magnifier.c | 1 + src/menu/menu.c | 1 + src/osd-field.c | 1 + src/osd.c | 2 +- src/output-state.c | 3 +- src/output-virtual.c | 1 + src/output.c | 1 + src/overlay.c | 1 + src/placement.c | 3 +- src/regions.c | 1 + src/seat.c | 1 + src/server.c | 1 + src/session-lock.c | 1 + src/snap.c | 3 +- src/ssd/ssd-extents.c | 2 +- src/view.c | 4 +- src/workspaces.c | 3 +- src/xdg-popup.c | 1 + src/xdg.c | 1 + src/xwayland.c | 1 + 32 files changed, 112 insertions(+), 84 deletions(-) create mode 100644 include/output.h diff --git a/include/labwc.h b/include/labwc.h index fbeb992b..642cf4cb 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -403,39 +403,6 @@ struct server { pid_t primary_client_pid; }; -#define LAB_NR_LAYERS (4) - -struct output { - struct wl_list link; /* server.outputs */ - struct server *server; - struct wlr_output *wlr_output; - struct wlr_output_state pending; - struct wlr_scene_output *scene_output; - struct wlr_scene_tree *layer_tree[LAB_NR_LAYERS]; - struct wlr_scene_tree *layer_popup_tree; - struct wlr_scene_tree *osd_tree; - struct wlr_scene_tree *session_lock_tree; - struct wlr_scene_buffer *workspace_osd; - - struct osd_scene { - struct wl_array items; /* struct osd_scene_item */ - struct wlr_scene_tree *tree; - } osd_scene; - - /* In output-relative scene coordinates */ - struct wlr_box usable_area; - - struct wl_list regions; /* struct region.link */ - - struct wl_listener destroy; - struct wl_listener frame; - struct wl_listener request_state; - - bool gamma_lut_changed; -}; - -#undef LAB_NR_LAYERS - struct constraint { struct seat *seat; struct wlr_pointer_constraint_v1 *constraint; @@ -548,45 +515,6 @@ void interactive_cancel(struct view *view); /* Possibly returns VIEW_EDGE_CENTER if is yes */ enum view_edge edge_from_cursor(struct seat *seat, struct output **dest_output); -void output_init(struct server *server); -void output_finish(struct server *server); -void output_manager_init(struct server *server); -struct output *output_from_wlr_output(struct server *server, - struct wlr_output *wlr_output); -struct output *output_from_name(struct server *server, const char *name); -struct output *output_nearest_to(struct server *server, int lx, int ly); -struct output *output_nearest_to_cursor(struct server *server); - -/** - * output_get_adjacent() - get next output, in a given direction, - * from a given output - * - * @output: reference output - * @edge: direction in which to look for the nearest output - * @wrap: if true, wrap around at layout edge - * - * Note: if output is NULL, the output nearest the cursor will be used as the - * reference instead. - */ -struct output *output_get_adjacent(struct output *output, - enum view_edge edge, bool wrap); - -bool output_is_usable(struct output *output); -void output_update_usable_area(struct output *output); -void output_update_all_usable_areas(struct server *server, bool layout_changed); -bool output_get_tearing_allowance(struct output *output); -struct wlr_box output_usable_area_in_layout_coords(struct output *output); -void handle_output_power_manager_set_mode(struct wl_listener *listener, - void *data); -void output_enable_adaptive_sync(struct output *output, bool enabled); - -/** - * output_max_scale() - get maximum scale factor of all usable outputs. - * Used when loading/rendering resources (e.g. icons) that may be - * displayed on any output. - */ -float output_max_scale(struct server *server); - void handle_tearing_new_object(struct wl_listener *listener, void *data); void server_init(struct server *server); diff --git a/include/output.h b/include/output.h new file mode 100644 index 00000000..d96a8c21 --- /dev/null +++ b/include/output.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_OUTPUT_H +#define LABWC_OUTPUT_H + +#include + +enum view_edge; + +#define LAB_NR_LAYERS (4) + +struct output { + struct wl_list link; /* server.outputs */ + struct server *server; + struct wlr_output *wlr_output; + struct wlr_output_state pending; + struct wlr_scene_output *scene_output; + struct wlr_scene_tree *layer_tree[LAB_NR_LAYERS]; + struct wlr_scene_tree *layer_popup_tree; + struct wlr_scene_tree *osd_tree; + struct wlr_scene_tree *session_lock_tree; + struct wlr_scene_buffer *workspace_osd; + + struct osd_scene { + struct wl_array items; /* struct osd_scene_item */ + struct wlr_scene_tree *tree; + } osd_scene; + + /* In output-relative scene coordinates */ + struct wlr_box usable_area; + + struct wl_list regions; /* struct region.link */ + + struct wl_listener destroy; + struct wl_listener frame; + struct wl_listener request_state; + + bool gamma_lut_changed; +}; + +#undef LAB_NR_LAYERS + +void output_init(struct server *server); +void output_finish(struct server *server); +void output_manager_init(struct server *server); +struct output *output_from_wlr_output(struct server *server, + struct wlr_output *wlr_output); +struct output *output_from_name(struct server *server, const char *name); +struct output *output_nearest_to(struct server *server, int lx, int ly); +struct output *output_nearest_to_cursor(struct server *server); + +/** + * output_get_adjacent() - get next output, in a given direction, + * from a given output + * + * @output: reference output + * @edge: direction in which to look for the nearest output + * @wrap: if true, wrap around at layout edge + * + * Note: if output is NULL, the output nearest the cursor will be used as the + * reference instead. + */ +struct output *output_get_adjacent(struct output *output, + enum view_edge edge, bool wrap); + +bool output_is_usable(struct output *output); +void output_update_usable_area(struct output *output); +void output_update_all_usable_areas(struct server *server, bool layout_changed); +bool output_get_tearing_allowance(struct output *output); +struct wlr_box output_usable_area_in_layout_coords(struct output *output); +void handle_output_power_manager_set_mode(struct wl_listener *listener, + void *data); +void output_enable_adaptive_sync(struct output *output, bool enabled); + +/** + * output_max_scale() - get maximum scale factor of all usable outputs. + * Used when loading/rendering resources (e.g. icons) that may be + * displayed on any output. + */ +float output_max_scale(struct server *server); + +#endif // LABWC_OUTPUT_H diff --git a/src/action.c b/src/action.c index adcae7cf..ec1914c4 100644 --- a/src/action.c +++ b/src/action.c @@ -18,6 +18,7 @@ #include "magnifier.h" #include "menu/menu.h" #include "osd.h" +#include "output.h" #include "output-virtual.h" #include "regions.h" #include "ssd.h" diff --git a/src/common/scene-helpers.c b/src/common/scene-helpers.c index 9b787b26..5f8a8139 100644 --- a/src/common/scene-helpers.c +++ b/src/common/scene-helpers.c @@ -9,6 +9,7 @@ #include #include "labwc.h" #include "magnifier.h" +#include "output.h" #include "output-state.h" struct wlr_surface * diff --git a/src/debug.c b/src/debug.c index e482a357..60b4a82c 100644 --- a/src/debug.c +++ b/src/debug.c @@ -8,6 +8,7 @@ #include "input/ime.h" #include "labwc.h" #include "node.h" +#include "output.h" #include "ssd.h" #include "view.h" #include "workspaces.h" diff --git a/src/desktop.c b/src/desktop.c index 28af6cc6..739cc3ef 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -8,6 +8,7 @@ #include "layers.h" #include "node.h" #include "osd.h" +#include "output.h" #include "ssd.h" #include "view.h" #include "window-rules.h" diff --git a/src/edges.c b/src/edges.c index 237991a2..94c541b5 100644 --- a/src/edges.c +++ b/src/edges.c @@ -10,6 +10,7 @@ #include "common/macros.h" #include "config/rcxml.h" #include "labwc.h" +#include "output.h" #include "view.h" #include "node.h" diff --git a/src/foreign-toplevel/wlr-foreign.c b/src/foreign-toplevel/wlr-foreign.c index 76eb01e5..7c915ac4 100644 --- a/src/foreign-toplevel/wlr-foreign.c +++ b/src/foreign-toplevel/wlr-foreign.c @@ -3,6 +3,7 @@ #include #include "common/macros.h" #include "labwc.h" +#include "output.h" #include "view.h" #include "foreign-toplevel-internal.h" diff --git a/src/input/cursor.c b/src/input/cursor.c index be292822..d7eabb75 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -25,6 +25,7 @@ #include "labwc.h" #include "layers.h" #include "menu/menu.h" +#include "output.h" #include "regions.h" #include "resistance.h" #include "resize-outlines.h" diff --git a/src/input/ime.c b/src/input/ime.c index 0cb656de..1e4cddc4 100644 --- a/src/input/ime.c +++ b/src/input/ime.c @@ -5,6 +5,7 @@ #include #include "common/mem.h" #include "node.h" +#include "output.h" #include "view.h" #define SAME_CLIENT(wlr_obj1, wlr_obj2) \ diff --git a/src/interactive.c b/src/interactive.c index bd620edb..f6fd5450 100644 --- a/src/interactive.c +++ b/src/interactive.c @@ -3,6 +3,7 @@ #include "edges.h" #include "input/keyboard.h" #include "labwc.h" +#include "output.h" #include "regions.h" #include "resize-indicator.h" #include "snap.h" diff --git a/src/layers.c b/src/layers.c index 44a2e131..7ed3f84d 100644 --- a/src/layers.c +++ b/src/layers.c @@ -21,6 +21,7 @@ #include "config/rcxml.h" #include "labwc.h" #include "node.h" +#include "output.h" #define LAB_LAYERSHELL_VERSION 4 diff --git a/src/magnifier.c b/src/magnifier.c index 9fb5b264..49499211 100644 --- a/src/magnifier.c +++ b/src/magnifier.c @@ -7,6 +7,7 @@ #include #include "common/box.h" #include "labwc.h" +#include "output.h" #include "theme.h" static bool magnify_on; diff --git a/src/menu/menu.c b/src/menu/menu.c index a44790eb..f8695a2a 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -26,6 +26,7 @@ #include "common/spawn.h" #include "common/string-helpers.h" #include "labwc.h" +#include "output.h" #include "workspaces.h" #include "view.h" #include "node.h" diff --git a/src/osd-field.c b/src/osd-field.c index 0819e1e6..c27b9379 100644 --- a/src/osd-field.c +++ b/src/osd-field.c @@ -9,6 +9,7 @@ #include "labwc.h" #include "desktop-entry.h" #include "osd.h" +#include "output.h" /* includes '%', terminating 's' and NULL byte, 8 is enough for %-9999s */ #define LAB_FIELD_SINGLE_FMT_MAX_LEN 8 diff --git a/src/osd.c b/src/osd.c index 89ba415d..6ae3381d 100644 --- a/src/osd.c +++ b/src/osd.c @@ -8,7 +8,6 @@ #include "common/buf.h" #include "common/font.h" #include "common/lab-scene-rect.h" -#include "common/macros.h" #include "common/scaled-font-buffer.h" #include "common/scaled-icon-buffer.h" #include "common/scene-helpers.h" @@ -16,6 +15,7 @@ #include "config/rcxml.h" #include "labwc.h" #include "node.h" +#include "output.h" #include "theme.h" #include "view.h" #include "window-rules.h" diff --git a/src/output-state.c b/src/output-state.c index 7ebcc19e..7da1108b 100644 --- a/src/output-state.c +++ b/src/output-state.c @@ -3,7 +3,8 @@ #include "output-state.h" #include #include -#include "labwc.h" +#include +#include "output.h" void output_state_init(struct output *output) diff --git a/src/output-virtual.c b/src/output-virtual.c index 888f832b..164be7b6 100644 --- a/src/output-virtual.c +++ b/src/output-virtual.c @@ -6,6 +6,7 @@ #include #include "common/string-helpers.h" #include "labwc.h" +#include "output.h" static struct wlr_output *fallback_output = NULL; diff --git a/src/output.c b/src/output.c index 3b4a26ca..99c1b847 100644 --- a/src/output.c +++ b/src/output.c @@ -7,6 +7,7 @@ */ #define _POSIX_C_SOURCE 200809L +#include "output.h" #include #include #include diff --git a/src/overlay.c b/src/overlay.c index 78a9c172..cf7b96b1 100644 --- a/src/overlay.c +++ b/src/overlay.c @@ -3,6 +3,7 @@ #include #include "common/lab-scene-rect.h" #include "labwc.h" +#include "output.h" #include "view.h" #include "theme.h" diff --git a/src/placement.c b/src/placement.c index c0377038..869bede3 100644 --- a/src/placement.c +++ b/src/placement.c @@ -3,10 +3,9 @@ #include #include #include -#include -#include "common/macros.h" #include "common/mem.h" #include "labwc.h" +#include "output.h" #include "ssd.h" #include "view.h" diff --git a/src/regions.c b/src/regions.c index 3463c97f..26c1e6d7 100644 --- a/src/regions.c +++ b/src/regions.c @@ -13,6 +13,7 @@ #include "common/mem.h" #include "input/keyboard.h" #include "labwc.h" +#include "output.h" #include "view.h" bool diff --git a/src/seat.c b/src/seat.c index 0beaf487..1b3b7a3f 100644 --- a/src/seat.c +++ b/src/seat.c @@ -17,6 +17,7 @@ #include "input/keyboard.h" #include "input/key-state.h" #include "labwc.h" +#include "output.h" #include "view.h" static void diff --git a/src/server.c b/src/server.c index 214885b1..ca24392a 100644 --- a/src/server.c +++ b/src/server.c @@ -47,6 +47,7 @@ #include "layers.h" #include "magnifier.h" #include "menu/menu.h" +#include "output.h" #include "output-state.h" #include "output-virtual.h" #include "regions.h" diff --git a/src/session-lock.c b/src/session-lock.c index fe118566..1dce3b6a 100644 --- a/src/session-lock.c +++ b/src/session-lock.c @@ -5,6 +5,7 @@ #include "common/mem.h" #include "labwc.h" #include "node.h" +#include "output.h" struct session_lock_output { struct wlr_scene_tree *tree; diff --git a/src/snap.c b/src/snap.c index 4e8e1d44..7991d48c 100644 --- a/src/snap.c +++ b/src/snap.c @@ -1,12 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-only #include "snap.h" #include -#include #include #include "common/border.h" -#include "common/macros.h" #include "edges.h" #include "labwc.h" +#include "output.h" #include "snap-constraints.h" #include "view.h" diff --git a/src/ssd/ssd-extents.c b/src/ssd/ssd-extents.c index c2ff6d0c..90f12ce6 100644 --- a/src/ssd/ssd-extents.c +++ b/src/ssd/ssd-extents.c @@ -2,10 +2,10 @@ #include #include -#include "common/mem.h" #include "common/scene-helpers.h" #include "config/rcxml.h" #include "labwc.h" +#include "output.h" #include "ssd-internal.h" #include "theme.h" #include "view.h" diff --git a/src/view.c b/src/view.c index 98c61d92..45b87458 100644 --- a/src/view.c +++ b/src/view.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #include "view.h" #include -#include #include #include #include @@ -9,16 +8,15 @@ #include "buffer.h" #include "common/box.h" #include "common/list.h" -#include "common/macros.h" #include "common/match.h" #include "common/mem.h" -#include "common/parse-bool.h" #include "common/scene-helpers.h" #include "foreign-toplevel.h" #include "input/keyboard.h" #include "labwc.h" #include "menu/menu.h" #include "osd.h" +#include "output.h" #include "output-state.h" #include "placement.h" #include "regions.h" diff --git a/src/workspaces.c b/src/workspaces.c index fc1cd524..2057adf0 100644 --- a/src/workspaces.c +++ b/src/workspaces.c @@ -8,7 +8,6 @@ #include #include #include -#include "config.h" #include "buffer.h" #include "common/font.h" #include "common/graphic-helpers.h" @@ -16,10 +15,10 @@ #include "common/mem.h" #include "input/keyboard.h" #include "labwc.h" +#include "output.h" #include "protocols/cosmic-workspaces.h" #include "protocols/ext-workspace.h" #include "view.h" -#include "xwayland.h" #define COSMIC_WORKSPACES_VERSION 1 #define EXT_WORKSPACES_VERSION 1 diff --git a/src/xdg-popup.c b/src/xdg-popup.c index d4daadd6..23942b69 100644 --- a/src/xdg-popup.c +++ b/src/xdg-popup.c @@ -11,6 +11,7 @@ #include "common/mem.h" #include "labwc.h" #include "node.h" +#include "output.h" #include "view.h" struct xdg_popup { diff --git a/src/xdg.c b/src/xdg.c index be8cfc20..f551c204 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -12,6 +12,7 @@ #include "labwc.h" #include "menu/menu.h" #include "node.h" +#include "output.h" #include "snap-constraints.h" #include "view.h" #include "view-impl-common.h" diff --git a/src/xwayland.c b/src/xwayland.c index fae5b659..097fc731 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -13,6 +13,7 @@ #include "foreign-toplevel.h" #include "labwc.h" #include "node.h" +#include "output.h" #include "ssd.h" #include "view.h" #include "view-impl-common.h" From e1475a1e47b5cde159c82f081c70b36876bc0d98 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Mon, 28 Jul 2025 01:22:10 -0400 Subject: [PATCH 046/248] include: reduce global includes in labwc.h --- include/labwc.h | 45 ++------------------------------------ src/action.c | 1 + src/config/keybind.c | 1 + src/decorations/kde-deco.c | 1 + src/desktop.c | 3 +++ src/input/cursor.c | 8 ++++++- src/input/ime.c | 3 +++ src/input/keyboard.c | 3 +++ src/input/tablet-pad.c | 1 + src/input/touch.c | 2 ++ src/layers.c | 1 + src/magnifier.c | 2 ++ src/main.c | 4 +--- src/menu/menu.c | 1 + src/output.c | 5 +++++ src/overlay.c | 1 + src/seat.c | 7 ++++++ src/server.c | 20 +++++++++++++---- src/ssd/ssd-border.c | 1 + src/ssd/ssd-extents.c | 1 + src/ssd/ssd-shadow.c | 2 +- src/ssd/ssd.c | 1 + src/tearing.c | 1 + src/theme.c | 1 + src/view.c | 4 ++++ src/workspaces.c | 1 + src/xdg-popup.c | 1 + src/xdg.c | 2 ++ src/xwayland-unmanaged.c | 3 +++ src/xwayland.c | 3 +++ 30 files changed, 78 insertions(+), 52 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index 642cf4cb..b05d519d 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -2,53 +2,10 @@ #ifndef LABWC_H #define LABWC_H #include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include "common/set.h" -#include "config/keybind.h" -#include "config/rcxml.h" #include "input/cursor.h" #include "overlay.h" -#include "regions.h" -#include "session-lock.h" #if HAVE_NLS #include #include @@ -60,6 +17,8 @@ #define XCURSOR_DEFAULT "left_ptr" #define XCURSOR_SIZE 24 +struct wlr_xdg_popup; + enum input_mode { LAB_INPUT_STATE_PASSTHROUGH = 0, LAB_INPUT_STATE_MOVE, diff --git a/src/action.c b/src/action.c index ec1914c4..c4fd131a 100644 --- a/src/action.c +++ b/src/action.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "common/macros.h" #include "common/list.h" diff --git a/src/config/keybind.c b/src/config/keybind.c index 7560cb16..9003e5ee 100644 --- a/src/config/keybind.c +++ b/src/config/keybind.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "common/list.h" #include "common/mem.h" diff --git a/src/decorations/kde-deco.c b/src/decorations/kde-deco.c index a615811b..123173df 100644 --- a/src/decorations/kde-deco.c +++ b/src/decorations/kde-deco.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #include #include +#include #include "common/list.h" #include "common/mem.h" #include "decorations.h" diff --git a/src/desktop.c b/src/desktop.c index 739cc3ef..a7cd86a2 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -1,6 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only #include "config.h" #include +#include +#include +#include #include "common/scene-helpers.h" #include "common/surface-helpers.h" #include "dnd.h" diff --git a/src/input/cursor.c b/src/input/cursor.c index d7eabb75..852eca3f 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -7,7 +7,14 @@ #include #include #include +#include +#include #include +#include +#include +#include +#include +#include #include #include "action.h" #include "common/macros.h" @@ -15,7 +22,6 @@ #include "common/scene-helpers.h" #include "common/surface-helpers.h" #include "config/mousebind.h" -#include "config/tablet-tool.h" #include "dnd.h" #include "idle.h" #include "input/gestures.h" diff --git a/src/input/ime.c b/src/input/ime.c index 1e4cddc4..bbb8723a 100644 --- a/src/input/ime.c +++ b/src/input/ime.c @@ -3,6 +3,9 @@ #include "input/ime.h" #include +#include +#include +#include #include "common/mem.h" #include "node.h" #include "output.h" diff --git a/src/input/keyboard.c b/src/input/keyboard.c index 4c2a0704..8d1b9ae3 100644 --- a/src/input/keyboard.c +++ b/src/input/keyboard.c @@ -6,9 +6,11 @@ #include #include #include +#include #include "action.h" #include "common/macros.h" #include "common/three-state.h" +#include "config/keybind.h" #include "idle.h" #include "input/ime.h" #include "input/key-state.h" @@ -16,6 +18,7 @@ #include "menu/menu.h" #include "osd.h" #include "regions.h" +#include "session-lock.h" #include "view.h" #include "workspaces.h" diff --git a/src/input/tablet-pad.c b/src/input/tablet-pad.c index 19127058..c2992ef9 100644 --- a/src/input/tablet-pad.c +++ b/src/input/tablet-pad.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include diff --git a/src/input/touch.c b/src/input/touch.c index 2cc79784..c0745f37 100644 --- a/src/input/touch.c +++ b/src/input/touch.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only #include "input/touch.h" #include +#include +#include #include #include #include "common/macros.h" diff --git a/src/layers.c b/src/layers.c index 7ed3f84d..d22c16ad 100644 --- a/src/layers.c +++ b/src/layers.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "common/macros.h" #include "common/mem.h" diff --git a/src/magnifier.c b/src/magnifier.c index 49499211..d9704c80 100644 --- a/src/magnifier.c +++ b/src/magnifier.c @@ -2,8 +2,10 @@ #include "magnifier.h" #include +#include #include #include +#include #include #include "common/box.h" #include "labwc.h" diff --git a/src/main.c b/src/main.c index ef093112..c69090bc 100644 --- a/src/main.c +++ b/src/main.c @@ -1,13 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L +#include #include #include -#include #include -#include "common/dir.h" #include "common/fd-util.h" #include "common/font.h" -#include "common/mem.h" #include "common/spawn.h" #include "config/session.h" #include "labwc.h" diff --git a/src/menu/menu.c b/src/menu/menu.c index f8695a2a..dd902bc2 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/src/output.c b/src/output.c index 99c1b847..5288b6e9 100644 --- a/src/output.c +++ b/src/output.c @@ -14,7 +14,11 @@ #include #include #include +#include #include +#include +#include +#include #include #include #include @@ -31,6 +35,7 @@ #include "protocols/cosmic-workspaces.h" #include "protocols/ext-workspace.h" #include "regions.h" +#include "session-lock.h" #include "view.h" #include "xwayland.h" diff --git a/src/overlay.c b/src/overlay.c index cf7b96b1..4b1c33cd 100644 --- a/src/overlay.c +++ b/src/overlay.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #include "overlay.h" #include +#include #include "common/lab-scene-rect.h" #include "labwc.h" #include "output.h" diff --git a/src/seat.c b/src/seat.c index 1b3b7a3f..a932b903 100644 --- a/src/seat.c +++ b/src/seat.c @@ -5,8 +5,14 @@ #include #include #include +#include +#include #include +#include #include +#include +#include +#include #include #include "common/macros.h" #include "common/mem.h" @@ -18,6 +24,7 @@ #include "input/key-state.h" #include "labwc.h" #include "output.h" +#include "session-lock.h" #include "view.h" static void diff --git a/src/server.c b/src/server.c index ca24392a..a9e021dc 100644 --- a/src/server.c +++ b/src/server.c @@ -6,25 +6,36 @@ #include #include #include +#include #include #include -#include +#include #include +#include #include +#include #include +#include +#include #include #include #include +#include #include +#include +#include #include #include +#include +#include #include -#include -#include #include #include -#include +#include #include +#include +#include +#include #include #include #include @@ -52,6 +63,7 @@ #include "output-virtual.h" #include "regions.h" #include "resize-indicator.h" +#include "session-lock.h" #include "theme.h" #include "view.h" #include "workspaces.h" diff --git a/src/ssd/ssd-border.c b/src/ssd/ssd-border.c index e96c65c6..26216ed6 100644 --- a/src/ssd/ssd-border.c +++ b/src/ssd/ssd-border.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #include +#include #include "common/scene-helpers.h" #include "labwc.h" #include "ssd-internal.h" diff --git a/src/ssd/ssd-extents.c b/src/ssd/ssd-extents.c index 90f12ce6..f8fdd9f0 100644 --- a/src/ssd/ssd-extents.c +++ b/src/ssd/ssd-extents.c @@ -2,6 +2,7 @@ #include #include +#include #include "common/scene-helpers.h" #include "config/rcxml.h" #include "labwc.h" diff --git a/src/ssd/ssd-shadow.c b/src/ssd/ssd-shadow.c index 29ee1ca2..8c1f8839 100644 --- a/src/ssd/ssd-shadow.c +++ b/src/ssd/ssd-shadow.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #include -#include "common/scene-helpers.h" +#include #include "labwc.h" #include "buffer.h" #include "ssd-internal.h" diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index 48d416b9..ac37f15b 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -8,6 +8,7 @@ #include #include +#include #include "common/mem.h" #include "common/scene-helpers.h" #include "labwc.h" diff --git a/src/tearing.c b/src/tearing.c index 50b0611c..eaba51a0 100644 --- a/src/tearing.c +++ b/src/tearing.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only +#include #include "common/mem.h" #include "labwc.h" #include "view.h" diff --git a/src/theme.c b/src/theme.c index 961c640b..2d5ac12b 100644 --- a/src/theme.c +++ b/src/theme.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/src/view.c b/src/view.c index 45b87458..d6ac89b9 100644 --- a/src/view.c +++ b/src/view.c @@ -2,8 +2,11 @@ #include "view.h" #include #include +#include #include +#include #include +#include #include "action.h" #include "buffer.h" #include "common/box.h" @@ -21,6 +24,7 @@ #include "placement.h" #include "regions.h" #include "resize-indicator.h" +#include "session-lock.h" #include "snap-constraints.h" #include "snap.h" #include "ssd.h" diff --git a/src/workspaces.c b/src/workspaces.c index 2057adf0..70215b80 100644 --- a/src/workspaces.c +++ b/src/workspaces.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "buffer.h" #include "common/font.h" #include "common/graphic-helpers.h" diff --git a/src/xdg-popup.c b/src/xdg-popup.c index 23942b69..cc14d920 100644 --- a/src/xdg-popup.c +++ b/src/xdg-popup.c @@ -7,6 +7,7 @@ * - keeping non-layer-shell xdg-popups outside the layers.c code */ +#include #include "common/macros.h" #include "common/mem.h" #include "labwc.h" diff --git a/src/xdg.c b/src/xdg.c index f551c204..5a7a6176 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -2,6 +2,8 @@ #include #include +#include +#include #include #include "buffer.h" #include "common/array.h" diff --git a/src/xwayland-unmanaged.c b/src/xwayland-unmanaged.c index 2fc15cfe..b003e4ec 100644 --- a/src/xwayland-unmanaged.c +++ b/src/xwayland-unmanaged.c @@ -1,5 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only #include +#include +#include +#include #include #include "common/list.h" #include "common/macros.h" diff --git a/src/xwayland.c b/src/xwayland.c index 097fc731..e793916f 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -3,6 +3,9 @@ #include "xwayland.h" #include #include +#include +#include +#include #include #include "buffer.h" #include "common/array.h" From 2e6b30eb50a03303d977345e568fa7a557f570ab Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Sat, 26 Jul 2025 16:23:02 -0400 Subject: [PATCH 047/248] include: move a few types from labwc.h to better locations --- include/input/input.h | 11 ++++++++++- include/input/keyboard.h | 21 +++++++++++++++++--- include/labwc.h | 42 ---------------------------------------- include/osd.h | 6 ++++++ src/input/cursor.c | 6 ++++++ src/input/ime.c | 1 + 6 files changed, 41 insertions(+), 46 deletions(-) diff --git a/include/input/input.h b/include/input/input.h index 37758e0e..520216b6 100644 --- a/include/input/input.h +++ b/include/input/input.h @@ -2,7 +2,16 @@ #ifndef LABWC_INPUT_H #define LABWC_INPUT_H -struct seat; +#include + +struct input { + struct wlr_input_device *wlr_input_device; + struct seat *seat; + /* Set for pointer/touch devices */ + double scroll_factor; + struct wl_listener destroy; + struct wl_list link; /* seat.inputs */ +}; void input_handlers_init(struct seat *seat); void input_handlers_finish(struct seat *seat); diff --git a/include/input/keyboard.h b/include/input/keyboard.h index d080d30a..6836d9bc 100644 --- a/include/input/keyboard.h +++ b/include/input/keyboard.h @@ -4,10 +4,25 @@ #include #include +#include "input/input.h" -struct seat; -struct keyboard; -struct wlr_keyboard; +/* + * Virtual keyboards should not belong to seat->keyboard_group. As a result we + * need to be able to ascertain which wlr_keyboard key/modifier events come from + * and we achieve that by using `struct keyboard` which inherits `struct input` + * and adds keyboard specific listeners and a wlr_keyboard pointer. + */ +struct keyboard { + struct input base; + struct wlr_keyboard *wlr_keyboard; + bool is_virtual; + struct wl_listener modifiers; + struct wl_listener key; + /* key repeat for compositor keybinds */ + uint32_t keybind_repeat_keycode; + int32_t keybind_repeat_rate; + struct wl_event_source *keybind_repeat; +}; void keyboard_reset_current_keybind(void); void keyboard_configure(struct seat *seat, struct wlr_keyboard *kb, diff --git a/include/labwc.h b/include/labwc.h index b05d519d..feae372e 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -27,33 +27,6 @@ enum input_mode { LAB_INPUT_STATE_WINDOW_SWITCHER, }; -struct input { - struct wlr_input_device *wlr_input_device; - struct seat *seat; - /* Set for pointer/touch devices */ - double scroll_factor; - struct wl_listener destroy; - struct wl_list link; /* seat.inputs */ -}; - -/* - * Virtual keyboards should not belong to seat->keyboard_group. As a result we - * need to be able to ascertain which wlr_keyboard key/modifier events come from - * and we achieve that by using `struct keyboard` which inherits `struct input` - * and adds keyboard specific listeners and a wlr_keyboard pointer. - */ -struct keyboard { - struct input base; - struct wlr_keyboard *wlr_keyboard; - bool is_virtual; - struct wl_listener modifiers; - struct wl_listener key; - /* key repeat for compositor keybinds */ - uint32_t keybind_repeat_keycode; - int32_t keybind_repeat_rate; - struct wl_event_source *keybind_repeat; -}; - struct seat { struct wlr_seat *seat; struct server *server; @@ -181,15 +154,6 @@ struct seat { struct wl_listener new_virtual_keyboard; }; -struct lab_data_buffer; -struct workspace; - -enum lab_cycle_dir { - LAB_CYCLE_DIR_NONE, - LAB_CYCLE_DIR_FORWARD, - LAB_CYCLE_DIR_BACKWARD, -}; - struct server { struct wl_display *wl_display; struct wl_event_loop *wl_event_loop; /* Can be used for timer events */ @@ -362,12 +326,6 @@ struct server { pid_t primary_client_pid; }; -struct constraint { - struct seat *seat; - struct wlr_pointer_constraint_v1 *constraint; - struct wl_listener destroy; -}; - void xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup); void xdg_shell_init(struct server *server); void xdg_shell_finish(struct server *server); diff --git a/include/osd.h b/include/osd.h index 971389d2..39a73b8b 100644 --- a/include/osd.h +++ b/include/osd.h @@ -5,6 +5,12 @@ #include #include +enum lab_cycle_dir { + LAB_CYCLE_DIR_NONE, + LAB_CYCLE_DIR_FORWARD, + LAB_CYCLE_DIR_BACKWARD, +}; + /* TODO: add field with keyboard layout? */ enum window_switcher_field_content { LAB_FIELD_NONE = 0, diff --git a/src/input/cursor.c b/src/input/cursor.c index 852eca3f..0fc2bbd0 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -41,6 +41,12 @@ #define LAB_CURSOR_SHAPE_V1_VERSION 1 +struct constraint { + struct seat *seat; + struct wlr_pointer_constraint_v1 *constraint; + struct wl_listener destroy; +}; + static const char * const *cursor_names = NULL; /* Usual cursor names */ diff --git a/src/input/ime.c b/src/input/ime.c index bbb8723a..350374df 100644 --- a/src/input/ime.c +++ b/src/input/ime.c @@ -7,6 +7,7 @@ #include #include #include "common/mem.h" +#include "input/keyboard.h" #include "node.h" #include "output.h" #include "view.h" From b966a1ed73a80eeee136d1bcbed3a43e1cc27ccc Mon Sep 17 00:00:00 2001 From: Weblate Date: Wed, 30 Jul 2025 10:01:19 +0200 Subject: [PATCH 048/248] Translation updates from weblate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: BigELK176 ≡ Co-authored-by: Valera Co-authored-by: Weblate Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/ru/ Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/zh_TW/ Translation: Labwc/labwc --- po/LINGUAS | 2 +- po/ru.po | 12 ++++---- po/zh_TW.po | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 po/zh_TW.po diff --git a/po/LINGUAS b/po/LINGUAS index 2878ab89..92310a79 100644 --- a/po/LINGUAS +++ b/po/LINGUAS @@ -1 +1 @@ -ar ca cs da de el es et eu fa fi fr gl hu id it ja ka ko lt ms nl pa pl pt pt_BR ru sk sv tr uk zh_CN +ar ca cs da de el es et eu fa fi fr gl hu id it ja ka ko lt ms nl pa pl pt pt_BR ru sk sv tr uk zh_CN zh_TW diff --git a/po/ru.po b/po/ru.po index 88c97b55..fc6f4745 100644 --- a/po/ru.po +++ b/po/ru.po @@ -8,14 +8,16 @@ msgstr "" "Project-Id-Version: labwc\n" "Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" "POT-Creation-Date: 2024-09-19 21:09+1000\n" -"PO-Revision-Date: 2024-12-08 21:01+0000\n" -"Last-Translator: unabomberlive <7alinchik@mail.ru>\n" -"Language-Team: Russian \n" +"PO-Revision-Date: 2025-07-30 08:01+0000\n" +"Last-Translator: Valera \n" +"Language-Team: Russian \n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Generator: Weblate 4.2.1\n" #: src/menu/menu.c:1016 @@ -24,7 +26,7 @@ msgstr "Перейти..." #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "" +msgstr "Терминал" #: src/menu/menu.c:1040 msgid "Reconfigure" diff --git a/po/zh_TW.po b/po/zh_TW.po new file mode 100644 index 00000000..e11c45ab --- /dev/null +++ b/po/zh_TW.po @@ -0,0 +1,80 @@ +# Labwc pot file +# Copyright (C) 2024 +# This file is distributed under the same license as the labwc package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: labwc\n" +"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" +"POT-Creation-Date: 2024-09-19 21:09+1000\n" +"PO-Revision-Date: 2025-07-30 08:01+0000\n" +"Last-Translator: BigELK176 ≡ \n" +"Language-Team: Chinese (Traditional) \n" +"Language: zh_TW\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 4.2.1\n" + +#: src/menu/menu.c:1016 +msgid "Go there..." +msgstr "前往…" + +#: src/menu/menu.c:1034 +msgid "Terminal" +msgstr "終端" + +#: src/menu/menu.c:1040 +msgid "Reconfigure" +msgstr "重新設定" + +#: src/menu/menu.c:1042 +msgid "Exit" +msgstr "退出" + +#: src/menu/menu.c:1056 +msgid "Minimize" +msgstr "最小化" + +#: src/menu/menu.c:1058 +msgid "Maximize" +msgstr "最大化" + +#: src/menu/menu.c:1060 +msgid "Fullscreen" +msgstr "全螢幕" + +#: src/menu/menu.c:1062 +msgid "Roll Up/Down" +msgstr "捲上/捲下" + +#: src/menu/menu.c:1064 +msgid "Decorations" +msgstr "裝飾" + +#: src/menu/menu.c:1066 +msgid "Always on Top" +msgstr "總是在最上層" + +#: src/menu/menu.c:1071 +msgid "Move Left" +msgstr "移左" + +#: src/menu/menu.c:1078 +msgid "Move Right" +msgstr "移右" + +#: src/menu/menu.c:1083 +msgid "Always on Visible Workspace" +msgstr "總是在可見工作區" + +#: src/menu/menu.c:1086 +msgid "Workspace" +msgstr "工作區" + +#: src/menu/menu.c:1089 +msgid "Close" +msgstr "關閉" From d5c03ab7fb5e6ba53ebb156facb899a7b91dd454 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 2 Aug 2025 16:25:36 +0900 Subject: [PATCH 049/248] include/edges.h: remove unimplemented function --- include/edges.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/edges.h b/include/edges.h index 47dd6f1b..38aeb897 100644 --- a/include/edges.h +++ b/include/edges.h @@ -101,9 +101,6 @@ typedef void (*edge_validator_t)(int *best, struct edge current, void edges_initialize(struct border *edges); -void edges_adjust_geom(struct view *view, struct border edges, - uint32_t resize_edges, struct wlr_box *geom); - void edges_find_neighbors(struct border *nearest_edges, struct view *view, struct wlr_box origin, struct wlr_box target, struct output *output, edge_validator_t validator, bool ignore_hidden); From ca8d98e80f25ba8d1fd9576f4774aa0ab67fb469 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 2 Aug 2025 20:10:09 +0900 Subject: [PATCH 050/248] cursor: fix Scroll mousebinds not inhibited with ToggleKeybinds fixup for 024ab280 --- src/input/cursor.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/input/cursor.c b/src/input/cursor.c index 0fc2bbd0..062c3465 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -1346,6 +1346,10 @@ process_cursor_axis(struct server *server, enum wl_pointer_axis orientation, if (direction != LAB_DIRECTION_INVALID) { struct mousebind *mousebind; wl_list_for_each(mousebind, &rc.mousebinds, link) { + if (ctx.type == LAB_SSD_CLIENT + && view_inhibits_actions(ctx.view, &mousebind->actions)) { + continue; + } if (ssd_part_contains(mousebind->context, ctx.type) && mousebind->direction == direction && modifiers == mousebind->modifiers From 38e57891b5df3e7d9293baf9e73e3f505e4f5467 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 5 Jul 2025 16:06:38 +0900 Subject: [PATCH 051/248] overlay: take into account for edge overlay This also deduplicates get_edge_snap_box() in interactive.c and view_get_edge_snap_box() in view.c. --- include/labwc.h | 1 - include/view.h | 4 ++++ src/interactive.c | 8 ++------ src/overlay.c | 26 ++++---------------------- src/view.c | 19 +++++++++++++------ 5 files changed, 23 insertions(+), 35 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index feae372e..0cfdce19 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -429,7 +429,6 @@ void interactive_anchor_to_cursor(struct server *server, struct wlr_box *geo); void interactive_begin(struct view *view, enum input_mode mode, uint32_t edges); void interactive_finish(struct view *view); void interactive_cancel(struct view *view); -/* Possibly returns VIEW_EDGE_CENTER if is yes */ enum view_edge edge_from_cursor(struct seat *seat, struct output **dest_output); void handle_tearing_new_object(struct wl_listener *listener, void *data); diff --git a/include/view.h b/include/view.h index 5e1c5baf..04ed7d64 100644 --- a/include/view.h +++ b/include/view.h @@ -501,6 +501,10 @@ bool view_contains_window_type(struct view *view, enum window_type window_type); */ enum view_edge view_edge_invert(enum view_edge edge); +/* If view is NULL, the size of SSD is not considered */ +struct wlr_box view_get_edge_snap_box(struct view *view, struct output *output, + enum view_edge edge); + /** * view_is_focusable() - Check whether or not a view can be focused * @view: view to be checked diff --git a/src/interactive.c b/src/interactive.c index f6fd5450..15b4323d 100644 --- a/src/interactive.c +++ b/src/interactive.c @@ -195,11 +195,7 @@ edge_from_cursor(struct seat *seat, struct output **dest_output) } else if (cursor_x >= area->x + area->width - snap_range) { return VIEW_EDGE_RIGHT; } else if (cursor_y <= area->y + snap_range) { - if (rc.snap_top_maximize) { - return VIEW_EDGE_CENTER; - } else { - return VIEW_EDGE_UP; - } + return VIEW_EDGE_UP; } else if (cursor_y >= area->y + area->height - snap_range) { return VIEW_EDGE_DOWN; } else { @@ -223,7 +219,7 @@ snap_to_edge(struct view *view) * Don't store natural geometry here (it was * stored already in interactive_begin()) */ - if (edge == VIEW_EDGE_CENTER) { + if (edge == VIEW_EDGE_UP && rc.snap_top_maximize) { /* */ view_maximize(view, VIEW_AXIS_BOTH, /*store_natural_geometry*/ false); diff --git a/src/overlay.c b/src/overlay.c index 4b1c33cd..3424f160 100644 --- a/src/overlay.c +++ b/src/overlay.c @@ -114,31 +114,13 @@ show_region_overlay(struct seat *seat, struct region *region) show_overlay(seat, &seat->overlay.region_rect, ®ion->geo); } -/* TODO: share logic with view_get_edge_snap_box() */ static struct wlr_box get_edge_snap_box(enum view_edge edge, struct output *output) { - struct wlr_box box = output_usable_area_in_layout_coords(output); - switch (edge) { - case VIEW_EDGE_RIGHT: - box.x += box.width / 2; - /* fallthrough */ - case VIEW_EDGE_LEFT: - box.width /= 2; - break; - case VIEW_EDGE_DOWN: - box.y += box.height / 2; - /* fallthrough */ - case VIEW_EDGE_UP: - box.height /= 2; - break; - case VIEW_EDGE_CENTER: - /* */ - break; - default: - /* not reached */ - assert(false); + if (edge == VIEW_EDGE_UP && rc.snap_top_maximize) { + return output_usable_area_in_layout_coords(output); + } else { + return view_get_edge_snap_box(NULL, output, edge); } - return box; } static int diff --git a/src/view.c b/src/view.c index d6ac89b9..2f6e6277 100644 --- a/src/view.c +++ b/src/view.c @@ -440,7 +440,7 @@ view_edge_invert(enum view_edge edge) } } -static struct wlr_box +struct wlr_box view_get_edge_snap_box(struct view *view, struct output *output, enum view_edge edge) { @@ -469,14 +469,21 @@ view_get_edge_snap_box(struct view *view, struct output *output, break; } - struct border margin = ssd_get_margin(view->ssd); struct wlr_box dst = { - .x = x_offset + usable.x + margin.left, - .y = y_offset + usable.y + margin.top, - .width = base_width - margin.left - margin.right, - .height = base_height - margin.top - margin.bottom, + .x = x_offset + usable.x, + .y = y_offset + usable.y, + .width = base_width, + .height = base_height, }; + if (view) { + struct border margin = ssd_get_margin(view->ssd); + dst.x += margin.left; + dst.y += margin.top; + dst.width -= margin.left + margin.right; + dst.height -= margin.top + margin.bottom; + } + return dst; } From 6e7c4a181e094575fbe81ea3b544182345647346 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 5 Jul 2025 16:07:04 +0900 Subject: [PATCH 052/248] overlay: take into account for region overlay --- include/view.h | 1 + src/overlay.c | 3 +- src/view.c | 90 +++++++++++++++++++++++++++----------------------- 3 files changed, 52 insertions(+), 42 deletions(-) diff --git a/include/view.h b/include/view.h index 04ed7d64..a7aa1af0 100644 --- a/include/view.h +++ b/include/view.h @@ -504,6 +504,7 @@ enum view_edge view_edge_invert(enum view_edge edge); /* If view is NULL, the size of SSD is not considered */ struct wlr_box view_get_edge_snap_box(struct view *view, struct output *output, enum view_edge edge); +struct wlr_box view_get_region_snap_box(struct view *view, struct region *region); /** * view_is_focusable() - Check whether or not a view can be focused diff --git a/src/overlay.c b/src/overlay.c index 3424f160..2aecf007 100644 --- a/src/overlay.c +++ b/src/overlay.c @@ -111,7 +111,8 @@ show_region_overlay(struct seat *seat, struct region *region) inactivate_overlay(&seat->overlay); seat->overlay.active.region = region; - show_overlay(seat, &seat->overlay.region_rect, ®ion->geo); + struct wlr_box geo = view_get_region_snap_box(NULL, region); + show_overlay(seat, &seat->overlay.region_rect, &geo); } static struct wlr_box get_edge_snap_box(enum view_edge edge, struct output *output) diff --git a/src/view.c b/src/view.c index 2f6e6277..99a597cc 100644 --- a/src/view.c +++ b/src/view.c @@ -1195,6 +1195,54 @@ view_apply_natural_geometry(struct view *view) view_move_resize(view, geometry); } +struct wlr_box +view_get_region_snap_box(struct view *view, struct region *region) +{ + struct wlr_box geo = region->geo; + + /* Adjust for rc.gap */ + if (rc.gap) { + double half_gap = rc.gap / 2.0; + struct wlr_fbox offset = { + .x = half_gap, + .y = half_gap, + .width = -rc.gap, + .height = -rc.gap + }; + struct wlr_box usable = + output_usable_area_in_layout_coords(region->output); + if (geo.x == usable.x) { + offset.x += half_gap; + offset.width -= half_gap; + } + if (geo.y == usable.y) { + offset.y += half_gap; + offset.height -= half_gap; + } + if (geo.x + geo.width == usable.x + usable.width) { + offset.width -= half_gap; + } + if (geo.y + geo.height == usable.y + usable.height) { + offset.height -= half_gap; + } + geo.x += offset.x; + geo.y += offset.y; + geo.width += offset.width; + geo.height += offset.height; + } + + /* And adjust for current view */ + if (view) { + struct border margin = ssd_get_margin(view->ssd); + geo.x += margin.left; + geo.y += margin.top; + geo.width -= margin.left + margin.right; + geo.height -= margin.top + margin.bottom; + } + + return geo; +} + static void view_apply_region_geometry(struct view *view) { @@ -1220,47 +1268,7 @@ view_apply_region_geometry(struct view *view) } } - /* Create a copy of the original region geometry */ - struct wlr_box geo = view->tiled_region->geo; - - /* Adjust for rc.gap */ - if (rc.gap) { - double half_gap = rc.gap / 2.0; - struct wlr_fbox offset = { - .x = half_gap, - .y = half_gap, - .width = -rc.gap, - .height = -rc.gap - }; - struct wlr_box usable = - output_usable_area_in_layout_coords(output); - if (geo.x == usable.x) { - offset.x += half_gap; - offset.width -= half_gap; - } - if (geo.y == usable.y) { - offset.y += half_gap; - offset.height -= half_gap; - } - if (geo.x + geo.width == usable.x + usable.width) { - offset.width -= half_gap; - } - if (geo.y + geo.height == usable.y + usable.height) { - offset.height -= half_gap; - } - geo.x += offset.x; - geo.y += offset.y; - geo.width += offset.width; - geo.height += offset.height; - } - - /* And adjust for current view */ - struct border margin = ssd_get_margin(view->ssd); - geo.x += margin.left; - geo.y += margin.top; - geo.width -= margin.left + margin.right; - geo.height -= margin.top + margin.bottom; - + struct wlr_box geo = view_get_region_snap_box(view, view->tiled_region); view_move_resize(view, geo); } From 9fe1509e333aef017c8287558dbda6f0a6b21e0f Mon Sep 17 00:00:00 2001 From: Weblate Date: Sat, 2 Aug 2025 10:01:19 +0200 Subject: [PATCH 053/248] Translation updates from weblate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Dimitrios Glentadakis Co-authored-by: Weblate Co-authored-by: 이정희 Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/el/ Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/ko/ Translation: Labwc/labwc --- po/el.po | 9 +++++---- po/ko.po | 7 ++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/po/el.po b/po/el.po index 29e1d28f..8dcaab37 100644 --- a/po/el.po +++ b/po/el.po @@ -8,9 +8,10 @@ msgstr "" "Project-Id-Version: labwc\n" "Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" "POT-Creation-Date: 2024-09-19 21:09+1000\n" -"PO-Revision-Date: 2024-09-25 20:01+0000\n" -"Last-Translator: Giannis Antypas \n" -"Language-Team: Greek \n" +"PO-Revision-Date: 2025-08-02 08:01+0000\n" +"Last-Translator: Dimitrios Glentadakis \n" +"Language-Team: Greek \n" "Language: el\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -24,7 +25,7 @@ msgstr "Πήγαινε εκεί..." #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "" +msgstr "Τερματικό" #: src/menu/menu.c:1040 msgid "Reconfigure" diff --git a/po/ko.po b/po/ko.po index bacefdb9..8af99f83 100644 --- a/po/ko.po +++ b/po/ko.po @@ -8,9 +8,10 @@ msgstr "" "Project-Id-Version: labwc\n" "Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" "POT-Creation-Date: 2024-09-19 21:09+1000\n" -"PO-Revision-Date: 2024-12-10 02:04+0000\n" +"PO-Revision-Date: 2025-08-02 03:27+0000\n" "Last-Translator: 이정희 \n" -"Language-Team: Korean \n" +"Language-Team: Korean \n" "Language: ko\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -24,7 +25,7 @@ msgstr "빨리 가기..." #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "" +msgstr "터미널" #: src/menu/menu.c:1040 msgid "Reconfigure" From 6c50a6281700bc7bf7dcf25ce53c64e2d72ebe03 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Thu, 31 Jul 2025 21:37:03 +0100 Subject: [PATCH 054/248] NEWS.md: update notes for 0.9.1 --- NEWS.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/NEWS.md b/NEWS.md index e2adec14..f8a744d5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog] | Date | All Changes | wlroots version | lines-of-code | |------------|---------------|-----------------|---------------| +| 2025-08-02 | [0.9.1] | 0.19.0 | 28605 | | 2025-07-11 | [0.9.0] | 0.19.0 | 28586 | | 2025-05-02 | [0.8.4] | 0.18.2 | 27679 | | 2025-02-21 | [0.8.3] | 0.18.2 | 27671 | @@ -75,6 +76,28 @@ There are some regression warnings worth noting for the switch to wlroots 0.19: ## [unreleased] +## [0.9.1] + +This is an earlier-than-usual release containinig bug fixes only. It has been +done on a separate branch to avoid the inclusion of refactoring and new +features. + +``` + 0.9.1 <--- bug-fixes only + / + / +0.8.4--------0.9.0-------- <-- master +``` + +### Fixed + +- Prevent interaction with un-initialized xdg-shell windows after unmap to fix a + bug exposed by `wlroots-0.19.0` resulting in a compositor crash in certain + (unusual) circumstances [#2948] [#2937] [#2944] @Consolatis +- Fix double-free in `img_svg_render()` failure path [#2910] @jlindgren90 +- Fix swapped width/height in XWayland client `_NET_WM_ICON` stride calculation + [#2909] @jlindgren90 + ## [0.9.0] The main focus has been to port labwc to wlroots 0.19 [#2388] and fix associated @@ -2150,6 +2173,7 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16 [Keep a Changelog]: https://keepachangelog.com/en/1.0.0/ [unreleased]: https://github.com/labwc/labwc/compare/0.9.0...HEAD +[0.9.1]: https://github.com/labwc/labwc/compare/0.9.0...0.9.1 [0.9.0]: https://github.com/labwc/labwc/compare/0.8.4...0.9.0 [0.8.4]: https://github.com/labwc/labwc/compare/0.8.3...0.8.4 [0.8.3]: https://github.com/labwc/labwc/compare/0.8.2...0.8.3 @@ -2574,6 +2598,11 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16 [#2886]: https://github.com/labwc/labwc/pull/2886 [#2887]: https://github.com/labwc/labwc/pull/2887 [#2891]: https://github.com/labwc/labwc/pull/2891 +[#2909]: https://github.com/labwc/labwc/pull/2909 +[#2910]: https://github.com/labwc/labwc/pull/2910 [#2914]: https://github.com/labwc/labwc/pull/2914 +[#2937]: https://github.com/labwc/labwc/pull/2937 [#2939]: https://github.com/labwc/labwc/pull/2939 [#2943]: https://github.com/labwc/labwc/pull/2943 +[#2944]: https://github.com/labwc/labwc/pull/2944 +[#2948]: https://github.com/labwc/labwc/pull/2948 From 15e3c32b5be009fce003789e2d67e2f8087c7462 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Fri, 1 Aug 2025 16:06:58 +0900 Subject: [PATCH 055/248] action.c: split actions_run() --- src/action.c | 959 ++++++++++++++++++++++++++------------------------- 1 file changed, 483 insertions(+), 476 deletions(-) diff --git a/src/action.c b/src/action.c index c4fd131a..0d6f9e32 100644 --- a/src/action.c +++ b/src/action.c @@ -862,6 +862,488 @@ warp_cursor(struct server *server, struct view *view, const char *to, const char cursor_update_focus(server); } +static void +run_action(struct view *view, struct server *server, struct action *action, + struct cursor_context *ctx) +{ + switch (action->type) { + case ACTION_TYPE_CLOSE: + if (view) { + view_close(view); + } + break; + case ACTION_TYPE_KILL: + if (view) { + /* Send SIGTERM to the process associated with the surface */ + assert(view->impl->get_pid); + pid_t pid = view->impl->get_pid(view); + if (pid == getpid()) { + wlr_log(WLR_ERROR, "Preventing sending SIGTERM to labwc"); + } else if (pid > 0) { + kill(pid, SIGTERM); + } + } + break; + case ACTION_TYPE_DEBUG: + debug_dump_scene(server); + break; + case ACTION_TYPE_EXECUTE: { + struct buf cmd = BUF_INIT; + buf_add(&cmd, action_get_str(action, "command", NULL)); + buf_expand_tilde(&cmd); + spawn_async_no_shell(cmd.data); + buf_reset(&cmd); + break; + } + case ACTION_TYPE_EXIT: + wl_display_terminate(server->wl_display); + break; + case ACTION_TYPE_MOVE_TO_EDGE: + if (view) { + /* Config parsing makes sure that direction is a valid direction */ + enum view_edge edge = action_get_int(action, "direction", 0); + bool snap_to_windows = action_get_bool(action, "snapWindows", true); + view_move_to_edge(view, edge, snap_to_windows); + } + break; + case ACTION_TYPE_TOGGLE_SNAP_TO_EDGE: + case ACTION_TYPE_SNAP_TO_EDGE: + if (view) { + /* Config parsing makes sure that direction is a valid direction */ + enum view_edge edge = action_get_int(action, "direction", 0); + if (action->type == ACTION_TYPE_TOGGLE_SNAP_TO_EDGE + && view->maximized == VIEW_AXIS_NONE + && !view->fullscreen + && view_is_tiled(view) + && view->tiled == edge) { + view_set_untiled(view); + view_apply_natural_geometry(view); + break; + } + view_snap_to_edge(view, edge, + /*across_outputs*/ true, + /*store_natural_geometry*/ true); + } + break; + case ACTION_TYPE_GROW_TO_EDGE: + if (view) { + /* Config parsing makes sure that direction is a valid direction */ + enum view_edge edge = action_get_int(action, "direction", 0); + view_grow_to_edge(view, edge); + } + break; + case ACTION_TYPE_SHRINK_TO_EDGE: + if (view) { + /* Config parsing makes sure that direction is a valid direction */ + enum view_edge edge = action_get_int(action, "direction", 0); + view_shrink_to_edge(view, edge); + } + break; + case ACTION_TYPE_NEXT_WINDOW: + if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) { + osd_cycle(server, LAB_CYCLE_DIR_FORWARD); + } else { + osd_begin(server, LAB_CYCLE_DIR_FORWARD); + } + break; + case ACTION_TYPE_PREVIOUS_WINDOW: + if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) { + osd_cycle(server, LAB_CYCLE_DIR_BACKWARD); + } else { + osd_begin(server, LAB_CYCLE_DIR_BACKWARD); + } + break; + case ACTION_TYPE_RECONFIGURE: + kill(getpid(), SIGHUP); + break; + case ACTION_TYPE_SHOW_MENU: + show_menu(server, view, ctx, + action_get_str(action, "menu", NULL), + action_get_bool(action, "atCursor", true), + action_get_str(action, "x.position", NULL), + action_get_str(action, "y.position", NULL)); + break; + case ACTION_TYPE_TOGGLE_MAXIMIZE: + if (view) { + enum view_axis axis = action_get_int(action, + "direction", VIEW_AXIS_BOTH); + view_toggle_maximize(view, axis); + } + break; + case ACTION_TYPE_MAXIMIZE: + if (view) { + enum view_axis axis = action_get_int(action, + "direction", VIEW_AXIS_BOTH); + view_maximize(view, axis, + /*store_natural_geometry*/ true); + } + break; + case ACTION_TYPE_UNMAXIMIZE: + if (view) { + enum view_axis axis = action_get_int(action, + "direction", VIEW_AXIS_BOTH); + view_maximize(view, view->maximized & ~axis, + /*store_natural_geometry*/ true); + } + break; + case ACTION_TYPE_TOGGLE_FULLSCREEN: + if (view) { + view_toggle_fullscreen(view); + } + break; + case ACTION_TYPE_SET_DECORATIONS: + if (view) { + enum ssd_mode mode = action_get_int(action, + "decorations", LAB_SSD_MODE_FULL); + bool force_ssd = action_get_bool(action, + "forceSSD", false); + view_set_decorations(view, mode, force_ssd); + } + break; + case ACTION_TYPE_TOGGLE_DECORATIONS: + if (view) { + view_toggle_decorations(view); + } + break; + case ACTION_TYPE_TOGGLE_ALWAYS_ON_TOP: + if (view) { + view_toggle_always_on_top(view); + } + break; + case ACTION_TYPE_TOGGLE_ALWAYS_ON_BOTTOM: + if (view) { + view_toggle_always_on_bottom(view); + } + break; + case ACTION_TYPE_TOGGLE_OMNIPRESENT: + if (view) { + view_toggle_visible_on_all_workspaces(view); + } + break; + case ACTION_TYPE_FOCUS: + if (view) { + desktop_focus_view(view, /*raise*/ false); + } + break; + case ACTION_TYPE_UNFOCUS: + seat_focus_surface(&server->seat, NULL); + break; + case ACTION_TYPE_ICONIFY: + if (view) { + view_minimize(view, true); + } + break; + case ACTION_TYPE_MOVE: + if (view) { + interactive_begin(view, LAB_INPUT_STATE_MOVE, 0); + } + break; + case ACTION_TYPE_RAISE: + if (view) { + view_move_to_front(view); + } + break; + case ACTION_TYPE_LOWER: + if (view) { + view_move_to_back(view); + } + break; + case ACTION_TYPE_RESIZE: + if (view) { + uint32_t resize_edges = cursor_get_resize_edges( + server->seat.cursor, ctx); + interactive_begin(view, LAB_INPUT_STATE_RESIZE, + resize_edges); + } + break; + case ACTION_TYPE_RESIZE_RELATIVE: + if (view) { + int left = action_get_int(action, "left", 0); + int right = action_get_int(action, "right", 0); + int top = action_get_int(action, "top", 0); + int bottom = action_get_int(action, "bottom", 0); + view_resize_relative(view, left, right, top, bottom); + } + break; + case ACTION_TYPE_MOVETO: + if (view) { + int x = action_get_int(action, "x", 0); + int y = action_get_int(action, "y", 0); + struct border margin = ssd_thickness(view); + view_move(view, x + margin.left, y + margin.top); + } + break; + case ACTION_TYPE_RESIZETO: + if (view) { + int width = action_get_int(action, "width", 0); + int height = action_get_int(action, "height", 0); + + /* + * To support only setting one of width/height + * in + * we fall back to current dimension when unset. + */ + struct wlr_box box = { + .x = view->pending.x, + .y = view->pending.y, + .width = width ? : view->pending.width, + .height = height ? : view->pending.height, + }; + view_set_shade(view, false); + view_move_resize(view, box); + } + break; + case ACTION_TYPE_MOVE_RELATIVE: + if (view) { + int x = action_get_int(action, "x", 0); + int y = action_get_int(action, "y", 0); + view_move_relative(view, x, y); + } + break; + case ACTION_TYPE_MOVETO_CURSOR: + wlr_log(WLR_ERROR, + "Action MoveToCursor is deprecated. To ensure your config works in future labwc " + "releases, please use "); + if (view) { + view_move_to_cursor(view); + } + break; + case ACTION_TYPE_SEND_TO_DESKTOP: + if (!view) { + break; + } + /* Falls through to GoToDesktop */ + case ACTION_TYPE_GO_TO_DESKTOP: { + bool follow = true; + bool wrap = action_get_bool(action, "wrap", true); + const char *to = action_get_str(action, "to", NULL); + /* + * `to` is always != NULL here because otherwise we would have + * removed the action during the initial parsing step as it is + * a required argument for both SendToDesktop and GoToDesktop. + */ + struct workspace *target_workspace = workspaces_find( + server->workspaces.current, to, wrap); + if (!target_workspace) { + break; + } + if (action->type == ACTION_TYPE_SEND_TO_DESKTOP) { + view_move_to_workspace(view, target_workspace); + follow = action_get_bool(action, "follow", true); + + /* Ensure that the focus is not on another desktop */ + if (!follow && server->active_view == view) { + desktop_focus_topmost_view(server); + } + } + if (follow) { + workspaces_switch_to(target_workspace, + /*update_focus*/ true); + } + break; + } + case ACTION_TYPE_MOVE_TO_OUTPUT: { + if (!view) { + break; + } + struct output *target_output = + get_target_output(view->output, server, action); + if (target_output) { + view_move_to_output(view, target_output); + } + break; + } + case ACTION_TYPE_FIT_TO_OUTPUT: + if (!view) { + break; + } + view_constrain_size_to_that_of_usable_area(view); + break; + case ACTION_TYPE_TOGGLE_SNAP_TO_REGION: + case ACTION_TYPE_SNAP_TO_REGION: { + if (!view) { + break; + } + struct output *output = view->output; + if (!output) { + break; + } + const char *region_name = action_get_str(action, "region", NULL); + struct region *region = regions_from_name(region_name, output); + if (region) { + if (action->type == ACTION_TYPE_TOGGLE_SNAP_TO_REGION + && view->maximized == VIEW_AXIS_NONE + && !view->fullscreen + && view_is_tiled(view) + && view->tiled_region == region) { + view_set_untiled(view); + view_apply_natural_geometry(view); + break; + } + view_snap_to_region(view, region, + /*store_natural_geometry*/ true); + } else { + wlr_log(WLR_ERROR, "Invalid SnapToRegion id: '%s'", region_name); + } + break; + } + case ACTION_TYPE_UNSNAP: + if (view && !view->fullscreen && !view_is_floating(view)) { + view_maximize(view, VIEW_AXIS_NONE, + /* store_natural_geometry */ false); + view_set_untiled(view); + view_apply_natural_geometry(view); + } + break; + case ACTION_TYPE_TOGGLE_KEYBINDS: + if (view) { + view_toggle_keybinds(view); + } + break; + case ACTION_TYPE_FOCUS_OUTPUT: { + struct output *output = output_nearest_to_cursor(server); + struct output *target_output = + get_target_output(output, server, action); + if (target_output) { + desktop_focus_output(target_output); + } + break; + } + case ACTION_TYPE_IF: + if (view) { + run_if_action(view, server, action); + } + break; + case ACTION_TYPE_FOR_EACH: { + struct wl_array views; + struct view **item; + bool matches = false; + wl_array_init(&views); + view_array_append(server, &views, LAB_VIEW_CRITERIA_NONE); + wl_array_for_each(item, &views) { + matches |= run_if_action(*item, server, action); + } + wl_array_release(&views); + if (!matches) { + struct wl_list *child_actions; + child_actions = action_get_actionlist(action, "none"); + if (child_actions) { + actions_run(view, server, child_actions, NULL); + } + } + break; + } + case ACTION_TYPE_VIRTUAL_OUTPUT_ADD: { + /* TODO: rename this argument to "outputName" */ + const char *output_name = + action_get_str(action, "output_name", NULL); + output_virtual_add(server, output_name, + /*store_wlr_output*/ NULL); + break; + } + case ACTION_TYPE_VIRTUAL_OUTPUT_REMOVE: { + /* TODO: rename this argument to "outputName" */ + const char *output_name = + action_get_str(action, "output_name", NULL); + output_virtual_remove(server, output_name); + break; + } + case ACTION_TYPE_AUTO_PLACE: + if (view) { + enum view_placement_policy policy = + action_get_int(action, "policy", LAB_PLACE_AUTOMATIC); + view_place_by_policy(view, + /* allow_cursor */ true, policy); + } + break; + case ACTION_TYPE_TOGGLE_TEARING: + if (view) { + switch (view->force_tearing) { + case LAB_STATE_UNSPECIFIED: + view->force_tearing = + output_get_tearing_allowance(view->output) + ? LAB_STATE_DISABLED : LAB_STATE_ENABLED; + break; + case LAB_STATE_DISABLED: + view->force_tearing = LAB_STATE_ENABLED; + break; + case LAB_STATE_ENABLED: + view->force_tearing = LAB_STATE_DISABLED; + break; + } + wlr_log(WLR_ERROR, "force tearing %sabled", + view->force_tearing == LAB_STATE_ENABLED + ? "en" : "dis"); + } + break; + case ACTION_TYPE_TOGGLE_SHADE: + if (view) { + view_set_shade(view, !view->shaded); + } + break; + case ACTION_TYPE_SHADE: + if (view) { + view_set_shade(view, true); + } + break; + case ACTION_TYPE_UNSHADE: + if (view) { + view_set_shade(view, false); + } + break; + case ACTION_TYPE_ENABLE_SCROLL_WHEEL_EMULATION: + server->seat.cursor_scroll_wheel_emulation = true; + break; + case ACTION_TYPE_DISABLE_SCROLL_WHEEL_EMULATION: + server->seat.cursor_scroll_wheel_emulation = false; + break; + case ACTION_TYPE_TOGGLE_SCROLL_WHEEL_EMULATION: + server->seat.cursor_scroll_wheel_emulation = + !server->seat.cursor_scroll_wheel_emulation; + break; + case ACTION_TYPE_ENABLE_TABLET_MOUSE_EMULATION: + rc.tablet.force_mouse_emulation = true; + break; + case ACTION_TYPE_DISABLE_TABLET_MOUSE_EMULATION: + rc.tablet.force_mouse_emulation = false; + break; + case ACTION_TYPE_TOGGLE_TABLET_MOUSE_EMULATION: + rc.tablet.force_mouse_emulation = !rc.tablet.force_mouse_emulation; + break; + case ACTION_TYPE_TOGGLE_MAGNIFY: + magnifier_toggle(server); + break; + case ACTION_TYPE_ZOOM_IN: + magnifier_set_scale(server, MAGNIFY_INCREASE); + break; + case ACTION_TYPE_ZOOM_OUT: + magnifier_set_scale(server, MAGNIFY_DECREASE); + break; + case ACTION_TYPE_WARP_CURSOR: { + const char *to = action_get_str(action, "to", "output"); + const char *x = action_get_str(action, "x", "center"); + const char *y = action_get_str(action, "y", "center"); + warp_cursor(server, view, to, x, y); + break; + } + case ACTION_TYPE_HIDE_CURSOR: + cursor_set_visible(&server->seat, false); + break; + case ACTION_TYPE_INVALID: + wlr_log(WLR_ERROR, "Not executing unknown action"); + break; + default: + /* + * If we get here it must be a BUG caused most likely by + * action_names and action_type being out of sync or by + * adding a new action without installing a handler here. + */ + wlr_log(WLR_ERROR, + "Not executing invalid action (%u)" + " This is a BUG. Please report.", action->type); + } +} + void actions_run(struct view *activator, struct server *server, struct wl_list *actions, struct cursor_context *cursor_ctx) @@ -898,481 +1380,6 @@ actions_run(struct view *activator, struct server *server, */ struct view *view = view_for_action(activator, server, action, &ctx); - switch (action->type) { - case ACTION_TYPE_CLOSE: - if (view) { - view_close(view); - } - break; - case ACTION_TYPE_KILL: - if (view) { - /* Send SIGTERM to the process associated with the surface */ - assert(view->impl->get_pid); - pid_t pid = view->impl->get_pid(view); - if (pid == getpid()) { - wlr_log(WLR_ERROR, "Preventing sending SIGTERM to labwc"); - } else if (pid > 0) { - kill(pid, SIGTERM); - } - } - break; - case ACTION_TYPE_DEBUG: - debug_dump_scene(server); - break; - case ACTION_TYPE_EXECUTE: { - struct buf cmd = BUF_INIT; - buf_add(&cmd, action_get_str(action, "command", NULL)); - buf_expand_tilde(&cmd); - spawn_async_no_shell(cmd.data); - buf_reset(&cmd); - break; - } - case ACTION_TYPE_EXIT: - wl_display_terminate(server->wl_display); - break; - case ACTION_TYPE_MOVE_TO_EDGE: - if (view) { - /* Config parsing makes sure that direction is a valid direction */ - enum view_edge edge = action_get_int(action, "direction", 0); - bool snap_to_windows = action_get_bool(action, "snapWindows", true); - view_move_to_edge(view, edge, snap_to_windows); - } - break; - case ACTION_TYPE_TOGGLE_SNAP_TO_EDGE: - case ACTION_TYPE_SNAP_TO_EDGE: - if (view) { - /* Config parsing makes sure that direction is a valid direction */ - enum view_edge edge = action_get_int(action, "direction", 0); - if (action->type == ACTION_TYPE_TOGGLE_SNAP_TO_EDGE - && view->maximized == VIEW_AXIS_NONE - && !view->fullscreen - && view_is_tiled(view) - && view->tiled == edge) { - view_set_untiled(view); - view_apply_natural_geometry(view); - break; - } - view_snap_to_edge(view, edge, - /*across_outputs*/ true, - /*store_natural_geometry*/ true); - } - break; - case ACTION_TYPE_GROW_TO_EDGE: - if (view) { - /* Config parsing makes sure that direction is a valid direction */ - enum view_edge edge = action_get_int(action, "direction", 0); - view_grow_to_edge(view, edge); - } - break; - case ACTION_TYPE_SHRINK_TO_EDGE: - if (view) { - /* Config parsing makes sure that direction is a valid direction */ - enum view_edge edge = action_get_int(action, "direction", 0); - view_shrink_to_edge(view, edge); - } - break; - case ACTION_TYPE_NEXT_WINDOW: - if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) { - osd_cycle(server, LAB_CYCLE_DIR_FORWARD); - } else { - osd_begin(server, LAB_CYCLE_DIR_FORWARD); - } - break; - case ACTION_TYPE_PREVIOUS_WINDOW: - if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) { - osd_cycle(server, LAB_CYCLE_DIR_BACKWARD); - } else { - osd_begin(server, LAB_CYCLE_DIR_BACKWARD); - } - break; - case ACTION_TYPE_RECONFIGURE: - kill(getpid(), SIGHUP); - break; - case ACTION_TYPE_SHOW_MENU: - show_menu(server, view, &ctx, - action_get_str(action, "menu", NULL), - action_get_bool(action, "atCursor", true), - action_get_str(action, "x.position", NULL), - action_get_str(action, "y.position", NULL)); - break; - case ACTION_TYPE_TOGGLE_MAXIMIZE: - if (view) { - enum view_axis axis = action_get_int(action, - "direction", VIEW_AXIS_BOTH); - view_toggle_maximize(view, axis); - } - break; - case ACTION_TYPE_MAXIMIZE: - if (view) { - enum view_axis axis = action_get_int(action, - "direction", VIEW_AXIS_BOTH); - view_maximize(view, axis, - /*store_natural_geometry*/ true); - } - break; - case ACTION_TYPE_UNMAXIMIZE: - if (view) { - enum view_axis axis = action_get_int(action, - "direction", VIEW_AXIS_BOTH); - view_maximize(view, view->maximized & ~axis, - /*store_natural_geometry*/ true); - } - break; - case ACTION_TYPE_TOGGLE_FULLSCREEN: - if (view) { - view_toggle_fullscreen(view); - } - break; - case ACTION_TYPE_SET_DECORATIONS: - if (view) { - enum ssd_mode mode = action_get_int(action, - "decorations", LAB_SSD_MODE_FULL); - bool force_ssd = action_get_bool(action, - "forceSSD", false); - view_set_decorations(view, mode, force_ssd); - } - break; - case ACTION_TYPE_TOGGLE_DECORATIONS: - if (view) { - view_toggle_decorations(view); - } - break; - case ACTION_TYPE_TOGGLE_ALWAYS_ON_TOP: - if (view) { - view_toggle_always_on_top(view); - } - break; - case ACTION_TYPE_TOGGLE_ALWAYS_ON_BOTTOM: - if (view) { - view_toggle_always_on_bottom(view); - } - break; - case ACTION_TYPE_TOGGLE_OMNIPRESENT: - if (view) { - view_toggle_visible_on_all_workspaces(view); - } - break; - case ACTION_TYPE_FOCUS: - if (view) { - desktop_focus_view(view, /*raise*/ false); - } - break; - case ACTION_TYPE_UNFOCUS: - seat_focus_surface(&server->seat, NULL); - break; - case ACTION_TYPE_ICONIFY: - if (view) { - view_minimize(view, true); - } - break; - case ACTION_TYPE_MOVE: - if (view) { - interactive_begin(view, LAB_INPUT_STATE_MOVE, 0); - } - break; - case ACTION_TYPE_RAISE: - if (view) { - view_move_to_front(view); - } - break; - case ACTION_TYPE_LOWER: - if (view) { - view_move_to_back(view); - } - break; - case ACTION_TYPE_RESIZE: - if (view) { - uint32_t resize_edges = cursor_get_resize_edges( - server->seat.cursor, &ctx); - interactive_begin(view, LAB_INPUT_STATE_RESIZE, - resize_edges); - } - break; - case ACTION_TYPE_RESIZE_RELATIVE: - if (view) { - int left = action_get_int(action, "left", 0); - int right = action_get_int(action, "right", 0); - int top = action_get_int(action, "top", 0); - int bottom = action_get_int(action, "bottom", 0); - view_resize_relative(view, left, right, top, bottom); - } - break; - case ACTION_TYPE_MOVETO: - if (view) { - int x = action_get_int(action, "x", 0); - int y = action_get_int(action, "y", 0); - struct border margin = ssd_thickness(view); - view_move(view, x + margin.left, y + margin.top); - } - break; - case ACTION_TYPE_RESIZETO: - if (view) { - int width = action_get_int(action, "width", 0); - int height = action_get_int(action, "height", 0); - - /* - * To support only setting one of width/height - * in - * we fall back to current dimension when unset. - */ - struct wlr_box box = { - .x = view->pending.x, - .y = view->pending.y, - .width = width ? : view->pending.width, - .height = height ? : view->pending.height, - }; - view_set_shade(view, false); - view_move_resize(view, box); - } - break; - case ACTION_TYPE_MOVE_RELATIVE: - if (view) { - int x = action_get_int(action, "x", 0); - int y = action_get_int(action, "y", 0); - view_move_relative(view, x, y); - } - break; - case ACTION_TYPE_MOVETO_CURSOR: - wlr_log(WLR_ERROR, - "Action MoveToCursor is deprecated. To ensure your config works in future labwc " - "releases, please use "); - if (view) { - view_move_to_cursor(view); - } - break; - case ACTION_TYPE_SEND_TO_DESKTOP: - if (!view) { - break; - } - /* Falls through to GoToDesktop */ - case ACTION_TYPE_GO_TO_DESKTOP: { - bool follow = true; - bool wrap = action_get_bool(action, "wrap", true); - const char *to = action_get_str(action, "to", NULL); - /* - * `to` is always != NULL here because otherwise we would have - * removed the action during the initial parsing step as it is - * a required argument for both SendToDesktop and GoToDesktop. - */ - struct workspace *target_workspace = workspaces_find( - server->workspaces.current, to, wrap); - if (!target_workspace) { - break; - } - if (action->type == ACTION_TYPE_SEND_TO_DESKTOP) { - view_move_to_workspace(view, target_workspace); - follow = action_get_bool(action, "follow", true); - - /* Ensure that the focus is not on another desktop */ - if (!follow && server->active_view == view) { - desktop_focus_topmost_view(server); - } - } - if (follow) { - workspaces_switch_to(target_workspace, - /*update_focus*/ true); - } - break; - } - case ACTION_TYPE_MOVE_TO_OUTPUT: { - if (!view) { - break; - } - struct output *target_output = - get_target_output(view->output, server, action); - if (target_output) { - view_move_to_output(view, target_output); - } - break; - } - case ACTION_TYPE_FIT_TO_OUTPUT: - if (!view) { - break; - } - view_constrain_size_to_that_of_usable_area(view); - break; - case ACTION_TYPE_TOGGLE_SNAP_TO_REGION: - case ACTION_TYPE_SNAP_TO_REGION: { - if (!view) { - break; - } - struct output *output = view->output; - if (!output) { - break; - } - const char *region_name = action_get_str(action, "region", NULL); - struct region *region = regions_from_name(region_name, output); - if (region) { - if (action->type == ACTION_TYPE_TOGGLE_SNAP_TO_REGION - && view->maximized == VIEW_AXIS_NONE - && !view->fullscreen - && view_is_tiled(view) - && view->tiled_region == region) { - view_set_untiled(view); - view_apply_natural_geometry(view); - break; - } - view_snap_to_region(view, region, - /*store_natural_geometry*/ true); - } else { - wlr_log(WLR_ERROR, "Invalid SnapToRegion id: '%s'", region_name); - } - break; - } - case ACTION_TYPE_UNSNAP: - if (view && !view->fullscreen && !view_is_floating(view)) { - view_maximize(view, VIEW_AXIS_NONE, - /* store_natural_geometry */ false); - view_set_untiled(view); - view_apply_natural_geometry(view); - } - break; - case ACTION_TYPE_TOGGLE_KEYBINDS: - if (view) { - view_toggle_keybinds(view); - } - break; - case ACTION_TYPE_FOCUS_OUTPUT: { - struct output *output = output_nearest_to_cursor(server); - struct output *target_output = - get_target_output(output, server, action); - if (target_output) { - desktop_focus_output(target_output); - } - break; - } - case ACTION_TYPE_IF: - if (view) { - run_if_action(view, server, action); - } - break; - case ACTION_TYPE_FOR_EACH: { - struct wl_array views; - struct view **item; - bool matches = false; - wl_array_init(&views); - view_array_append(server, &views, LAB_VIEW_CRITERIA_NONE); - wl_array_for_each(item, &views) { - matches |= run_if_action(*item, server, action); - } - wl_array_release(&views); - if (!matches) { - struct wl_list *child_actions; - child_actions = action_get_actionlist(action, "none"); - if (child_actions) { - actions_run(view, server, child_actions, NULL); - } - } - break; - } - case ACTION_TYPE_VIRTUAL_OUTPUT_ADD: { - /* TODO: rename this argument to "outputName" */ - const char *output_name = - action_get_str(action, "output_name", NULL); - output_virtual_add(server, output_name, - /*store_wlr_output*/ NULL); - break; - } - case ACTION_TYPE_VIRTUAL_OUTPUT_REMOVE: { - /* TODO: rename this argument to "outputName" */ - const char *output_name = - action_get_str(action, "output_name", NULL); - output_virtual_remove(server, output_name); - break; - } - case ACTION_TYPE_AUTO_PLACE: - if (view) { - enum view_placement_policy policy = - action_get_int(action, "policy", LAB_PLACE_AUTOMATIC); - view_place_by_policy(view, - /* allow_cursor */ true, policy); - } - break; - case ACTION_TYPE_TOGGLE_TEARING: - if (view) { - switch (view->force_tearing) { - case LAB_STATE_UNSPECIFIED: - view->force_tearing = - output_get_tearing_allowance(view->output) - ? LAB_STATE_DISABLED : LAB_STATE_ENABLED; - break; - case LAB_STATE_DISABLED: - view->force_tearing = LAB_STATE_ENABLED; - break; - case LAB_STATE_ENABLED: - view->force_tearing = LAB_STATE_DISABLED; - break; - } - wlr_log(WLR_ERROR, "force tearing %sabled", - view->force_tearing == LAB_STATE_ENABLED - ? "en" : "dis"); - } - break; - case ACTION_TYPE_TOGGLE_SHADE: - if (view) { - view_set_shade(view, !view->shaded); - } - break; - case ACTION_TYPE_SHADE: - if (view) { - view_set_shade(view, true); - } - break; - case ACTION_TYPE_UNSHADE: - if (view) { - view_set_shade(view, false); - } - break; - case ACTION_TYPE_ENABLE_SCROLL_WHEEL_EMULATION: - server->seat.cursor_scroll_wheel_emulation = true; - break; - case ACTION_TYPE_DISABLE_SCROLL_WHEEL_EMULATION: - server->seat.cursor_scroll_wheel_emulation = false; - break; - case ACTION_TYPE_TOGGLE_SCROLL_WHEEL_EMULATION: - server->seat.cursor_scroll_wheel_emulation = - !server->seat.cursor_scroll_wheel_emulation; - break; - case ACTION_TYPE_ENABLE_TABLET_MOUSE_EMULATION: - rc.tablet.force_mouse_emulation = true; - break; - case ACTION_TYPE_DISABLE_TABLET_MOUSE_EMULATION: - rc.tablet.force_mouse_emulation = false; - break; - case ACTION_TYPE_TOGGLE_TABLET_MOUSE_EMULATION: - rc.tablet.force_mouse_emulation = !rc.tablet.force_mouse_emulation; - break; - case ACTION_TYPE_TOGGLE_MAGNIFY: - magnifier_toggle(server); - break; - case ACTION_TYPE_ZOOM_IN: - magnifier_set_scale(server, MAGNIFY_INCREASE); - break; - case ACTION_TYPE_ZOOM_OUT: - magnifier_set_scale(server, MAGNIFY_DECREASE); - break; - case ACTION_TYPE_WARP_CURSOR: { - const char *to = action_get_str(action, "to", "output"); - const char *x = action_get_str(action, "x", "center"); - const char *y = action_get_str(action, "y", "center"); - warp_cursor(server, view, to, x, y); - break; - } - case ACTION_TYPE_HIDE_CURSOR: - cursor_set_visible(&server->seat, false); - break; - case ACTION_TYPE_INVALID: - wlr_log(WLR_ERROR, "Not executing unknown action"); - break; - default: - /* - * If we get here it must be a BUG caused most likely by - * action_names and action_type being out of sync or by - * adding a new action without installing a handler here. - */ - wlr_log(WLR_ERROR, - "Not executing invalid action (%u)" - " This is a BUG. Please report.", action->type); - } + run_action(view, server, action, &ctx); } } From b9c84f9c3848eabf55e0acfb670da85075ec2c62 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Fri, 1 Aug 2025 16:15:00 +0900 Subject: [PATCH 056/248] action: allow if-action without activator view ..in preparation for If/ForEach action with , which should be executed whether or not any window is focused. This patch makes actions execute branch if no window is focused or hovered. --- src/action.c | 70 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/src/action.c b/src/action.c index 0d6f9e32..7441c81b 100644 --- a/src/action.c +++ b/src/action.c @@ -776,29 +776,24 @@ view_for_action(struct view *activator, struct server *server, } static bool -run_if_action(struct view *view, struct server *server, struct action *action) +match_queries(struct view *view, struct action *action) { - struct view_query *query; - struct wl_list *queries, *actions; - const char *branch = "then"; + struct wl_list *queries = action_get_querylist(action, "query"); + if (!queries) { + return true; + } + if (!view) { + return false; + } - queries = action_get_querylist(action, "query"); - if (queries) { - branch = "else"; - /* All queries are OR'ed */ - wl_list_for_each(query, queries, link) { - if (view_matches_query(view, query)) { - branch = "then"; - break; - } + /* All queries are OR'ed */ + struct view_query *query; + wl_list_for_each(query, queries, link) { + if (view_matches_query(view, query)) { + return true; } } - - actions = action_get_actionlist(action, branch); - if (actions) { - actions_run(view, server, actions, NULL); - } - return !strcmp(branch, "then"); + return false; } static struct output * @@ -1209,26 +1204,43 @@ run_action(struct view *view, struct server *server, struct action *action, } break; } - case ACTION_TYPE_IF: - if (view) { - run_if_action(view, server, action); + case ACTION_TYPE_IF: { + struct wl_list *actions; + if (match_queries(view, action)) { + actions = action_get_actionlist(action, "then"); + } else { + actions = action_get_actionlist(action, "else"); + } + if (actions) { + actions_run(view, server, actions, ctx); } break; + } case ACTION_TYPE_FOR_EACH: { - struct wl_array views; - struct view **item; + struct wl_list *actions = NULL; bool matches = false; + + struct wl_array views; wl_array_init(&views); view_array_append(server, &views, LAB_VIEW_CRITERIA_NONE); + + struct view **item; wl_array_for_each(item, &views) { - matches |= run_if_action(*item, server, action); + if (match_queries(*item, action)) { + matches = true; + actions = action_get_actionlist(action, "then"); + } else { + actions = action_get_actionlist(action, "else"); + } + if (actions) { + actions_run(*item, server, actions, ctx); + } } wl_array_release(&views); if (!matches) { - struct wl_list *child_actions; - child_actions = action_get_actionlist(action, "none"); - if (child_actions) { - actions_run(view, server, child_actions, NULL); + actions = action_get_actionlist(action, "none"); + if (actions) { + actions_run(view, server, actions, NULL); } } break; From 5a50d87ee2b2bd009e557ee21a61cee2af4593d5 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sun, 3 Aug 2025 00:02:02 +0900 Subject: [PATCH 057/248] common/xml: let LAB_XML_FOR_EACH() skip first child text nodes Before this patch, first text nodes like the spaces between and below were also travered by LAB_XML_FOR_EACH(): foo --- include/common/xml.h | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/include/common/xml.h b/include/common/xml.h index 3cfa5539..8b49d0e4 100644 --- a/include/common/xml.h +++ b/include/common/xml.h @@ -35,16 +35,13 @@ bool lab_xml_get_string(xmlNode *node, const char *key, char *s, size_t len); bool lab_xml_get_int(xmlNode *node, const char *key, int *i); bool lab_xml_get_bool(xmlNode *node, const char *key, bool *b); +/* also skips other unusual nodes like comments */ static inline xmlNode * -lab_xml_get_next_child(xmlNode *child) +lab_xml_skip_text(xmlNode *child) { - if (!child) { - return NULL; - } - do { + while (child && child->type != XML_ELEMENT_NODE) { child = child->next; - } while (child && child->type != XML_ELEMENT_NODE); - + } return child; } @@ -58,11 +55,13 @@ lab_xml_get_key_and_content(xmlNode *node, char **name, char **content) } #define LAB_XML_FOR_EACH(parent, child, key, content) \ - for ((child) = (parent)->children, \ + for ((child) = lab_xml_skip_text((parent)->children), \ lab_xml_get_key_and_content((child), &(key), &(content)); \ + \ (child); \ + \ xmlFree((xmlChar *)(content)), \ - (child) = lab_xml_get_next_child(child), \ + (child) = lab_xml_skip_text((child)->next), \ lab_xml_get_key_and_content((child), &(key), &(content))) #endif /* LABWC_XML_H */ From 00ed40454dc52392bade3dac69a27af30c13f00a Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sun, 3 Aug 2025 16:09:18 +0900 Subject: [PATCH 058/248] common/xml: parse CDATA as text Before this patch, was ignored in many cases. For example, this didn't work: --- src/common/xml.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/xml.c b/src/common/xml.c index df8c26e4..797af9c0 100644 --- a/src/common/xml.c +++ b/src/common/xml.c @@ -138,7 +138,8 @@ lab_xml_node_is_leaf(xmlNode *node) return false; } for (xmlNode *child = node->children; child; child = child->next) { - if (child->type != XML_TEXT_NODE) { + if (child->type != XML_TEXT_NODE + && child->type != XML_CDATA_SECTION_NODE) { return false; } } From 03004cf44b2c2f1f4f42a8263dc214a60774d546 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sun, 3 Aug 2025 16:55:59 +0900 Subject: [PATCH 059/248] menu: fix segfault with toplevel Before this patch, labwc crashed menu.xml like this: --- src/menu/menu.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/menu/menu.c b/src/menu/menu.c index dd902bc2..8d80231f 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -269,6 +269,8 @@ item_create_scene(struct menuitem *menuitem, int *item_y) static struct menuitem * separator_create(struct menu *menu, const char *label) { + assert(menu); + struct menuitem *menuitem = znew(*menuitem); menuitem->parent = menu; menuitem->selectable = false; @@ -754,6 +756,11 @@ xml_tree_walk(struct menu_parse_context *ctx, xmlNode *node) continue; } if (!strcasecmp((char *)n->name, "separator")) { + if (!ctx->menu) { + wlr_log(WLR_ERROR, + "ignoring without parent "); + continue; + } handle_separator_element(ctx, n); continue; } From 4b0ac0234ce93d797bbea575774f2f0686c1b7d5 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 2 Aug 2025 21:16:40 +0900 Subject: [PATCH 060/248] view: refactor view_edge_parse() --- include/view.h | 2 +- src/action.c | 14 +++++++------- src/config/rcxml.c | 3 ++- src/view.c | 22 +++++++++++++++------- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/include/view.h b/include/view.h index a7aa1af0..6b2ccdb6 100644 --- a/include/view.h +++ b/include/view.h @@ -668,7 +668,7 @@ void view_init(struct view *view); void view_destroy(struct view *view); enum view_axis view_axis_parse(const char *direction); -enum view_edge view_edge_parse(const char *direction); +enum view_edge view_edge_parse(const char *direction, bool tiled, bool any); enum view_placement_policy view_placement_parse(const char *policy); /* xdg.c */ diff --git a/src/action.c b/src/action.c index 7441c81b..8d5eb5ac 100644 --- a/src/action.c +++ b/src/action.c @@ -342,11 +342,10 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char case ACTION_TYPE_GROW_TO_EDGE: case ACTION_TYPE_SHRINK_TO_EDGE: if (!strcmp(argument, "direction")) { - enum view_edge edge = view_edge_parse(content); - bool allow_center = action->type == ACTION_TYPE_TOGGLE_SNAP_TO_EDGE - || action->type == ACTION_TYPE_SNAP_TO_EDGE; - if ((edge == VIEW_EDGE_CENTER && !allow_center) - || edge == VIEW_EDGE_INVALID || edge == VIEW_EDGE_ALL) { + bool tiled = (action->type == ACTION_TYPE_TOGGLE_SNAP_TO_EDGE + || action->type == ACTION_TYPE_SNAP_TO_EDGE); + enum view_edge edge = view_edge_parse(content, tiled, /*any*/ false); + if (edge == VIEW_EDGE_INVALID) { wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", action_names[action->type], argument, content); } else { @@ -453,8 +452,9 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char goto cleanup; } if (!strcmp(argument, "direction")) { - enum view_edge edge = view_edge_parse(content); - if (edge == VIEW_EDGE_CENTER || edge == VIEW_EDGE_ALL) { + enum view_edge edge = view_edge_parse(content, + /*tiled*/ false, /*any*/ false); + if (edge == VIEW_EDGE_INVALID) { wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", action_names[action->type], argument, content); } else { diff --git a/src/config/rcxml.c b/src/config/rcxml.c index f545a9ef..9c8e3e67 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -445,7 +445,8 @@ fill_action_query(struct action *action, xmlNode *node, struct view_query *query } else if (!strcasecmp(key, "omnipresent")) { query->omnipresent = parse_three_state(content); } else if (!strcasecmp(key, "tiled")) { - query->tiled = view_edge_parse(content); + query->tiled = view_edge_parse(content, + /*tiled*/ true, /*any*/ true); } else if (!strcasecmp(key, "tiled_region")) { xstrdup_replace(query->tiled_region, content); } else if (!strcasecmp(key, "desktop")) { diff --git a/src/view.c b/src/view.c index 99a597cc..f1ffa85c 100644 --- a/src/view.c +++ b/src/view.c @@ -2119,7 +2119,7 @@ view_axis_parse(const char *direction) } enum view_edge -view_edge_parse(const char *direction) +view_edge_parse(const char *direction, bool tiled, bool any) { if (!direction) { return VIEW_EDGE_INVALID; @@ -2132,13 +2132,21 @@ view_edge_parse(const char *direction) return VIEW_EDGE_RIGHT; } else if (!strcasecmp(direction, "down")) { return VIEW_EDGE_DOWN; - } else if (!strcasecmp(direction, "center")) { - return VIEW_EDGE_CENTER; - } else if (!strcasecmp(direction, "any")) { - return VIEW_EDGE_ALL; - } else { - return VIEW_EDGE_INVALID; } + + if (any) { + if (!strcasecmp(direction, "any")) { + return VIEW_EDGE_ALL; + } + } + + if (tiled) { + if (!strcasecmp(direction, "center")) { + return VIEW_EDGE_CENTER; + } + } + + return VIEW_EDGE_INVALID; } enum view_placement_policy From 6441bd58f32c57afb48d70b6e9c35c6431042380 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 2 Aug 2025 21:11:12 +0900 Subject: [PATCH 061/248] view: don't use bitset for VIEW_EDGE_ALL We will use bitset for views snapped to corner (e.g. top-left = TOP|LEFT) --- include/view.h | 4 +--- src/view.c | 12 +++++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/include/view.h b/include/view.h index 6b2ccdb6..240d1f7d 100644 --- a/include/view.h +++ b/include/view.h @@ -72,9 +72,7 @@ enum view_edge { VIEW_EDGE_UP = (1 << 2), VIEW_EDGE_DOWN = (1 << 3), VIEW_EDGE_CENTER = (1 << 4), - - VIEW_EDGE_ALL = (VIEW_EDGE_LEFT | VIEW_EDGE_RIGHT | - VIEW_EDGE_UP | VIEW_EDGE_DOWN | VIEW_EDGE_CENTER), + VIEW_EDGE_ANY = (1 << 5), }; enum view_wants_focus { diff --git a/src/view.c b/src/view.c index f1ffa85c..4f9345d5 100644 --- a/src/view.c +++ b/src/view.c @@ -169,8 +169,14 @@ view_matches_query(struct view *view, struct view_query *query) return false; } - if (query->tiled != VIEW_EDGE_INVALID && !(query->tiled & view->tiled)) { - return false; + if (query->tiled == VIEW_EDGE_ANY) { + if (!view->tiled) { + return false; + } + } else if (query->tiled != VIEW_EDGE_INVALID) { + if (query->tiled != view->tiled) { + return false; + } } const char *tiled_region = @@ -2136,7 +2142,7 @@ view_edge_parse(const char *direction, bool tiled, bool any) if (any) { if (!strcasecmp(direction, "any")) { - return VIEW_EDGE_ALL; + return VIEW_EDGE_ANY; } } From b0ff2911b6282cf965f4a74ed867a4d5f9f18bbd Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 2 Aug 2025 21:35:34 +0900 Subject: [PATCH 062/248] src/overlay.c: minor refactor --- src/overlay.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/overlay.c b/src/overlay.c index 2aecf007..b7557b23 100644 --- a/src/overlay.c +++ b/src/overlay.c @@ -115,7 +115,8 @@ show_region_overlay(struct seat *seat, struct region *region) show_overlay(seat, &seat->overlay.region_rect, &geo); } -static struct wlr_box get_edge_snap_box(enum view_edge edge, struct output *output) +static struct wlr_box +get_edge_snap_box(enum view_edge edge, struct output *output) { if (edge == VIEW_EDGE_UP && rc.snap_top_maximize) { return output_usable_area_in_layout_coords(output); @@ -197,8 +198,7 @@ show_edge_overlay(struct seat *seat, enum view_edge edge, wl_event_source_timer_update(seat->overlay.timer, delay); } else { /* Show overlay now */ - struct wlr_box box = get_edge_snap_box(seat->overlay.active.edge, - seat->overlay.active.output); + struct wlr_box box = get_edge_snap_box(edge, output); show_overlay(seat, &seat->overlay.edge_rect, &box); } } From 2f183cdcb6be9e2a8926ccaa276ad6345444fe98 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 2 Aug 2025 21:35:51 +0900 Subject: [PATCH 063/248] interactive: allow snapping to corner edges In addition to , configures the distance from the screen corner to trigger quater window snapping. Also, new values "up-left", "up-right", "down-left" and "down-right" are allowed for and . --- docs/labwc-actions.5.scd | 5 +-- docs/labwc-config.5.scd | 12 ++++--- docs/rc.xml.all | 1 + include/common/direction.h | 3 +- include/config/rcxml.h | 1 + include/labwc.h | 11 ++++++- include/view.h | 7 +++++ src/common/direction.c | 20 ++++++------ src/config/rcxml.c | 3 ++ src/interactive.c | 64 +++++++++++++++++++++++++++----------- src/output.c | 6 +++- src/overlay.c | 38 +++++++--------------- src/view.c | 52 ++++++++++++++++--------------- src/xdg.c | 13 ++++++++ 14 files changed, 147 insertions(+), 89 deletions(-) diff --git a/docs/labwc-actions.5.scd b/docs/labwc-actions.5.scd index 604a8468..fa38698a 100644 --- a/docs/labwc-actions.5.scd +++ b/docs/labwc-actions.5.scd @@ -95,7 +95,8 @@ Actions are used in menus and keyboard/mouse bindings. **++ ** Resize window to fill half the output in the given direction. Supports - directions "left", "up", "right", "down" and "center". + directions "left", "up", "right", "down", "up-left", "up-right", "down-left", + "down-right" and "center". ToggleSnapToEdge additionally toggles the active window between tiled to the given direction and its untiled position. @@ -474,7 +475,7 @@ Actions that execute other actions. Used in keyboard/mouse bindings. The "left" , "right", "left-occupied" and "right-occupied" directions will not wrap. - *tiled* [up|right|down|left|center|any] + *tiled* [up|right|down|left|top-left|top-right|down-left|down-right|center|any] Whether the client is tiled (snapped) along the the indicated screen edge. diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index 5b303138..7def5b92 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -412,11 +412,13 @@ activated with SnapToEdge actions or, optionally, by dragging windows to the edges of an output. Edge snapping causes a window to occupy half of its output, extending outward from the snapped edge. -** - If an interactive move ends with the cursor a maximum distance *range*, - (in pixels) from the edge of an output, the move will trigger a - SnapToEdge action for that edge. A *range* of 0 disables snapping via - interactive moves. Default is 10. +**++ +** + If an interactive move ends with the cursor within ** pixels of an + output edge, the window is snapped to the edge. If it's also within + ** pixels of an output corner, the window is snapped to the + corner instead. A ** of 0 disables snapping. + Default is 10 for ** and 50 for **. ** [yes|no] Show an overlay when snapping to a window to an edge. Default is yes. diff --git a/docs/rc.xml.all b/docs/rc.xml.all index 9342ee15..bc0af78a 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -155,6 +155,7 @@ 10 + 50 diff --git a/include/common/direction.h b/include/common/direction.h index bc8cbd0c..1faa62bb 100644 --- a/include/common/direction.h +++ b/include/common/direction.h @@ -2,9 +2,10 @@ #ifndef LABWC_DIRECTION_H #define LABWC_DIRECTION_H +#include #include "view.h" -enum wlr_direction direction_from_view_edge(enum view_edge edge); +bool direction_from_view_edge(enum view_edge edge, enum wlr_direction *direction); enum wlr_direction direction_get_opposite(enum wlr_direction direction); #endif /* LABWC_DIRECTION_H */ diff --git a/include/config/rcxml.h b/include/config/rcxml.h index 05f75098..a96b6620 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -145,6 +145,7 @@ struct rcxml { /* window snapping */ int snap_edge_range; + int snap_edge_corner_range; bool snap_overlay_enabled; int snap_overlay_delay_inner; int snap_overlay_delay_outer; diff --git a/include/labwc.h b/include/labwc.h index 0cfdce19..6b90bfd6 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -429,7 +429,16 @@ void interactive_anchor_to_cursor(struct server *server, struct wlr_box *geo); void interactive_begin(struct view *view, enum input_mode mode, uint32_t edges); void interactive_finish(struct view *view); void interactive_cancel(struct view *view); -enum view_edge edge_from_cursor(struct seat *seat, struct output **dest_output); + +/** + * Returns the edge to snap a window to. + * For example, if the output-relative cursor position (x,y) fulfills + * x <= () and y <= (), + * then edge1=VIEW_EDGE_UP and edge2=VIEW_EDGE_LEFT. + * The value of (edge1|edge2) can be passed to view_snap_to_edge(). + */ +bool edge_from_cursor(struct seat *seat, struct output **dest_output, + enum view_edge *edge1, enum view_edge *edge2); void handle_tearing_new_object(struct wl_listener *listener, void *data); diff --git a/include/view.h b/include/view.h index 240d1f7d..c7f5908c 100644 --- a/include/view.h +++ b/include/view.h @@ -73,6 +73,10 @@ enum view_edge { VIEW_EDGE_DOWN = (1 << 3), VIEW_EDGE_CENTER = (1 << 4), VIEW_EDGE_ANY = (1 << 5), + VIEW_EDGE_UPLEFT = (VIEW_EDGE_UP | VIEW_EDGE_LEFT), + VIEW_EDGE_UPRIGHT = (VIEW_EDGE_UP | VIEW_EDGE_RIGHT), + VIEW_EDGE_DOWNLEFT = (VIEW_EDGE_DOWN | VIEW_EDGE_LEFT), + VIEW_EDGE_DOWNRIGHT = (VIEW_EDGE_DOWN | VIEW_EDGE_RIGHT), }; enum view_wants_focus { @@ -524,6 +528,9 @@ bool view_is_focusable(struct view *view); */ void view_offer_focus(struct view *view); +struct wlr_box view_get_edge_snap_box(struct view *view, struct output *output, + enum view_edge edge); + void mappable_connect(struct mappable *mappable, struct wlr_surface *surface, wl_notify_func_t notify_map, wl_notify_func_t notify_unmap); void mappable_disconnect(struct mappable *mappable); diff --git a/src/common/direction.c b/src/common/direction.c index ba23d189..3729f3d7 100644 --- a/src/common/direction.c +++ b/src/common/direction.c @@ -4,22 +4,24 @@ #include #include "view.h" -enum wlr_direction -direction_from_view_edge(enum view_edge edge) +bool +direction_from_view_edge(enum view_edge edge, enum wlr_direction *direction) { switch (edge) { case VIEW_EDGE_LEFT: - return WLR_DIRECTION_LEFT; + *direction = WLR_DIRECTION_LEFT; + return true; case VIEW_EDGE_RIGHT: - return WLR_DIRECTION_RIGHT; + *direction = WLR_DIRECTION_RIGHT; + return true; case VIEW_EDGE_UP: - return WLR_DIRECTION_UP; + *direction = WLR_DIRECTION_UP; + return true; case VIEW_EDGE_DOWN: - return WLR_DIRECTION_DOWN; - case VIEW_EDGE_CENTER: - case VIEW_EDGE_INVALID: + *direction = WLR_DIRECTION_DOWN; + return true; default: - return WLR_DIRECTION_UP; + return false; } } diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 9c8e3e67..92878491 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -1175,6 +1175,8 @@ entry(xmlNode *node, char *nodename, char *content) rc.unmaximize_threshold = atoi(content); } else if (!strcasecmp(nodename, "range.snapping")) { rc.snap_edge_range = atoi(content); + } else if (!strcasecmp(nodename, "cornerRange.snapping")) { + rc.snap_edge_corner_range = atoi(content); } else if (!strcasecmp(nodename, "enabled.overlay.snapping")) { set_bool(content, &rc.snap_overlay_enabled); } else if (!strcasecmp(nodename, "inner.delay.overlay.snapping")) { @@ -1411,6 +1413,7 @@ rcxml_init(void) rc.unmaximize_threshold = 150; rc.snap_edge_range = 10; + rc.snap_edge_corner_range = 50; rc.snap_overlay_enabled = true; rc.snap_overlay_delay_inner = 500; rc.snap_overlay_delay_outer = 500; diff --git a/src/interactive.c b/src/interactive.c index 15b4323d..057426fa 100644 --- a/src/interactive.c +++ b/src/interactive.c @@ -164,22 +164,26 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges) } } -enum view_edge -edge_from_cursor(struct seat *seat, struct output **dest_output) +bool +edge_from_cursor(struct seat *seat, struct output **dest_output, + enum view_edge *edge1, enum view_edge *edge2) { + *dest_output = NULL; + *edge1 = VIEW_EDGE_INVALID; + *edge2 = VIEW_EDGE_INVALID; + if (!view_is_floating(seat->server->grabbed_view)) { - return VIEW_EDGE_INVALID; + return false; } - int snap_range = rc.snap_edge_range; - if (!snap_range) { - return VIEW_EDGE_INVALID; + if (rc.snap_edge_range == 0) { + return false; } struct output *output = output_nearest_to_cursor(seat->server); if (!output_is_usable(output)) { wlr_log(WLR_ERROR, "output at cursor is unusable"); - return VIEW_EDGE_INVALID; + return false; } *dest_output = output; @@ -190,18 +194,39 @@ edge_from_cursor(struct seat *seat, struct output **dest_output) output->wlr_output, &cursor_x, &cursor_y); struct wlr_box *area = &output->usable_area; - if (cursor_x <= area->x + snap_range) { - return VIEW_EDGE_LEFT; - } else if (cursor_x >= area->x + area->width - snap_range) { - return VIEW_EDGE_RIGHT; - } else if (cursor_y <= area->y + snap_range) { - return VIEW_EDGE_UP; - } else if (cursor_y >= area->y + area->height - snap_range) { - return VIEW_EDGE_DOWN; + + int top = cursor_y - area->y; + int bottom = area->y + area->height - cursor_y; + int left = cursor_x - area->x; + int right = area->x + area->width - cursor_x; + + if (top < rc.snap_edge_range) { + *edge1 = VIEW_EDGE_UP; + } else if (bottom < rc.snap_edge_range) { + *edge1 = VIEW_EDGE_DOWN; + } else if (left < rc.snap_edge_range) { + *edge1 = VIEW_EDGE_LEFT; + } else if (right < rc.snap_edge_range) { + *edge1 = VIEW_EDGE_RIGHT; } else { - /* Not close to any edge */ - return VIEW_EDGE_INVALID; + return false; } + + if (*edge1 == VIEW_EDGE_UP || *edge1 == VIEW_EDGE_DOWN) { + if (left < rc.snap_edge_corner_range) { + *edge2 = VIEW_EDGE_LEFT; + } else if (right < rc.snap_edge_corner_range) { + *edge2 = VIEW_EDGE_RIGHT; + } + } else if (*edge1 == VIEW_EDGE_LEFT || *edge1 == VIEW_EDGE_RIGHT) { + if (top < rc.snap_edge_corner_range) { + *edge2 = VIEW_EDGE_UP; + } else if (bottom < rc.snap_edge_corner_range) { + *edge2 = VIEW_EDGE_DOWN; + } + } + + return true; } /* Returns true if view was snapped to any edge */ @@ -209,10 +234,11 @@ static bool snap_to_edge(struct view *view) { struct output *output; - enum view_edge edge = edge_from_cursor(&view->server->seat, &output); - if (edge == VIEW_EDGE_INVALID) { + enum view_edge edge1, edge2; + if (!edge_from_cursor(&view->server->seat, &output, &edge1, &edge2)) { return false; } + enum view_edge edge = edge1 | edge2; view_set_output(view, output); /* diff --git a/src/output.c b/src/output.c index 5288b6e9..42d8205d 100644 --- a/src/output.c +++ b/src/output.c @@ -979,6 +979,11 @@ output_get_adjacent(struct output *output, enum view_edge edge, bool wrap) return NULL; } + enum wlr_direction direction; + if (!direction_from_view_edge(edge, &direction)) { + return NULL; + } + struct wlr_box box = output_usable_area_in_layout_coords(output); int lx = box.x + box.width / 2; int ly = box.y + box.height / 2; @@ -987,7 +992,6 @@ output_get_adjacent(struct output *output, enum view_edge edge, bool wrap) struct wlr_output *new_output = NULL; struct wlr_output *current_output = output->wlr_output; struct wlr_output_layout *layout = output->server->output_layout; - enum wlr_direction direction = direction_from_view_edge(edge); new_output = wlr_output_layout_adjacent_output(layout, direction, current_output, lx, ly); diff --git a/src/overlay.c b/src/overlay.c index b7557b23..c6c51067 100644 --- a/src/overlay.c +++ b/src/overlay.c @@ -2,6 +2,7 @@ #include "overlay.h" #include #include +#include "common/direction.h" #include "common/lab-scene-rect.h" #include "labwc.h" #include "output.h" @@ -137,42 +138,27 @@ handle_edge_overlay_timeout(void *data) return 0; } -static enum wlr_direction -get_wlr_direction(enum view_edge edge) -{ - switch (edge) { - case VIEW_EDGE_LEFT: - return WLR_DIRECTION_LEFT; - case VIEW_EDGE_RIGHT: - return WLR_DIRECTION_RIGHT; - case VIEW_EDGE_UP: - case VIEW_EDGE_CENTER: - return WLR_DIRECTION_UP; - case VIEW_EDGE_DOWN: - return WLR_DIRECTION_DOWN; - default: - /* not reached */ - assert(false); - return 0; - } -} - static bool edge_has_adjacent_output_from_cursor(struct seat *seat, struct output *output, enum view_edge edge) { + enum wlr_direction dir; + if (!direction_from_view_edge(edge, &dir)) { + return false; + } return wlr_output_layout_adjacent_output( - seat->server->output_layout, get_wlr_direction(edge), + seat->server->output_layout, dir, output->wlr_output, seat->cursor->x, seat->cursor->y); } static void -show_edge_overlay(struct seat *seat, enum view_edge edge, +show_edge_overlay(struct seat *seat, enum view_edge edge1, enum view_edge edge2, struct output *output) { if (!rc.snap_overlay_enabled) { return; } + uint32_t edge = edge1 | edge2; if (seat->overlay.active.edge == edge && seat->overlay.active.output == output) { return; @@ -182,7 +168,7 @@ show_edge_overlay(struct seat *seat, enum view_edge edge, seat->overlay.active.output = output; int delay; - if (edge_has_adjacent_output_from_cursor(seat, output, edge)) { + if (edge_has_adjacent_output_from_cursor(seat, output, edge1)) { delay = rc.snap_overlay_delay_inner; } else { delay = rc.snap_overlay_delay_outer; @@ -219,9 +205,9 @@ overlay_update(struct seat *seat) /* Edge-snapping overlay */ struct output *output; - enum view_edge edge = edge_from_cursor(seat, &output); - if (edge != VIEW_EDGE_INVALID) { - show_edge_overlay(seat, edge, output); + enum view_edge edge1, edge2; + if (edge_from_cursor(seat, &output, &edge1, &edge2)) { + show_edge_overlay(seat, edge1, edge2, output); return; } diff --git a/src/view.c b/src/view.c index 4f9345d5..95cafc80 100644 --- a/src/view.c +++ b/src/view.c @@ -451,35 +451,29 @@ view_get_edge_snap_box(struct view *view, struct output *output, enum view_edge edge) { struct wlr_box usable = output_usable_area_in_layout_coords(output); - int x_offset = edge == VIEW_EDGE_RIGHT - ? (usable.width + rc.gap) / 2 : rc.gap; - int y_offset = edge == VIEW_EDGE_DOWN - ? (usable.height + rc.gap) / 2 : rc.gap; + int x1 = rc.gap; + int y1 = rc.gap; + int x2 = usable.width - rc.gap; + int y2 = usable.height - rc.gap; - int base_width, base_height; - switch (edge) { - case VIEW_EDGE_LEFT: - case VIEW_EDGE_RIGHT: - base_width = (usable.width - 3 * rc.gap) / 2; - base_height = usable.height - 2 * rc.gap; - break; - case VIEW_EDGE_UP: - case VIEW_EDGE_DOWN: - base_width = usable.width - 2 * rc.gap; - base_height = (usable.height - 3 * rc.gap) / 2; - break; - default: - case VIEW_EDGE_CENTER: - base_width = usable.width - 2 * rc.gap; - base_height = usable.height - 2 * rc.gap; - break; + if (edge & VIEW_EDGE_RIGHT) { + x1 = (usable.width + rc.gap) / 2; + } + if (edge & VIEW_EDGE_LEFT) { + x2 = (usable.width - rc.gap) / 2; + } + if (edge & VIEW_EDGE_DOWN) { + y1 = (usable.height + rc.gap) / 2; + } + if (edge & VIEW_EDGE_UP) { + y2 = (usable.height - rc.gap) / 2; } struct wlr_box dst = { - .x = x_offset + usable.x, - .y = y_offset + usable.y, - .width = base_width, - .height = base_height, + .x = x1 + usable.x, + .y = y1 + usable.y, + .width = x2 - x1, + .height = y2 - y1, }; if (view) { @@ -2149,6 +2143,14 @@ view_edge_parse(const char *direction, bool tiled, bool any) if (tiled) { if (!strcasecmp(direction, "center")) { return VIEW_EDGE_CENTER; + } else if (!strcasecmp(direction, "up-left")) { + return VIEW_EDGE_UPLEFT; + } else if (!strcasecmp(direction, "up-right")) { + return VIEW_EDGE_UPRIGHT; + } else if (!strcasecmp(direction, "down-left")) { + return VIEW_EDGE_DOWNLEFT; + } else if (!strcasecmp(direction, "down-right")) { + return VIEW_EDGE_DOWNRIGHT; } } diff --git a/src/xdg.c b/src/xdg.c index 5a7a6176..363bd478 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -660,6 +660,19 @@ xdg_toplevel_view_notify_tiled(struct view *view) case VIEW_EDGE_DOWN: edge = WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT; break; + case VIEW_EDGE_UPLEFT: + edge = WLR_EDGE_TOP | WLR_EDGE_LEFT; + break; + case VIEW_EDGE_UPRIGHT: + edge = WLR_EDGE_TOP | WLR_EDGE_RIGHT; + break; + case VIEW_EDGE_DOWNLEFT: + edge = WLR_EDGE_BOTTOM | WLR_EDGE_LEFT; + break; + case VIEW_EDGE_DOWNRIGHT: + edge = WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT; + break; + /* TODO: VIEW_EDGE_CENTER? */ default: edge = WLR_EDGE_NONE; } From bfaab101af6d26fb9737ca7b574ccde3c6abaddb Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 4 Aug 2025 12:54:29 +0900 Subject: [PATCH 064/248] Make append_actions() public Also rename it to append_parsed_actions() --- include/config/rcxml.h | 7 +++++++ src/config/rcxml.c | 18 ++++++++---------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/include/config/rcxml.h b/include/config/rcxml.h index a96b6620..65107440 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "common/border.h" #include "common/buf.h" @@ -196,4 +197,10 @@ void rcxml_parse_xml(struct buf *b); void rcxml_read(const char *filename); void rcxml_finish(void); +/* + * Parse the child nodes and append them to the list. + * FIXME: move this function to somewhere else. + */ +void append_parsed_actions(xmlNode *node, struct wl_list *list); + #endif /* LABWC_RCXML_H */ diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 92878491..b913f0c4 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -253,8 +253,6 @@ set_property(const char *str, enum property *variable) *variable = ret ? LAB_PROP_TRUE : LAB_PROP_FALSE; } -static void append_actions(xmlNode *node, struct wl_list *list); - static void fill_window_rule(xmlNode *node) { @@ -315,7 +313,7 @@ fill_window_rule(xmlNode *node) } } - append_actions(node, &window_rule->actions); + append_parsed_actions(node, &window_rule->actions); } static void @@ -482,7 +480,7 @@ parse_action_args(xmlNode *node, struct action *action) action_arg_add_actionlist(action, "then"); actions = action_get_actionlist(action, "then"); } - append_actions(child, actions); + append_parsed_actions(child, actions); } else if (!strcasecmp(key, "else")) { struct wl_list *actions = action_get_actionlist(action, "else"); @@ -490,7 +488,7 @@ parse_action_args(xmlNode *node, struct action *action) action_arg_add_actionlist(action, "else"); actions = action_get_actionlist(action, "else"); } - append_actions(child, actions); + append_parsed_actions(child, actions); } else if (!strcasecmp(key, "none")) { struct wl_list *actions = action_get_actionlist(action, "none"); @@ -498,7 +496,7 @@ parse_action_args(xmlNode *node, struct action *action) action_arg_add_actionlist(action, "none"); actions = action_get_actionlist(action, "none"); } - append_actions(child, actions); + append_parsed_actions(child, actions); } else if (!strcasecmp(key, "name")) { /* Ignore */ } else if (lab_xml_node_is_leaf(child)) { @@ -530,8 +528,8 @@ parse_action(xmlNode *node) return action; } -static void -append_actions(xmlNode *node, struct wl_list *list) +void +append_parsed_actions(xmlNode *node, struct wl_list *list) { xmlNode *child; char *key, *content; @@ -576,7 +574,7 @@ fill_keybind(xmlNode *node) lab_xml_get_bool(node, "layoutDependent", &keybind->use_syms_only); lab_xml_get_bool(node, "allowWhenLocked", &keybind->allow_when_locked); - append_actions(node, &keybind->actions); + append_parsed_actions(node, &keybind->actions); } static void @@ -608,7 +606,7 @@ fill_mousebind(xmlNode *node, const char *context) mousebind->mouse_event = mousebind_event_from_str(buf); } - append_actions(node, &mousebind->actions); + append_parsed_actions(node, &mousebind->actions); } static void From 17d66e560321ee2d76b89db23131cc40d11022a2 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 4 Aug 2025 12:55:13 +0900 Subject: [PATCH 065/248] menu: refactor parser ...with the same approach as rcxml.c - `If` actions now works for menus - `name` argument no longer have to be the first argument of - `label` argument no longer have to be the first argument of --- docs/labwc.1.scd | 3 +- src/menu/menu.c | 376 +++++++++++++++++------------------------------ 2 files changed, 133 insertions(+), 246 deletions(-) diff --git a/docs/labwc.1.scd b/docs/labwc.1.scd index 4e643918..4840c0fa 100644 --- a/docs/labwc.1.scd +++ b/docs/labwc.1.scd @@ -134,8 +134,7 @@ example: *LABWC_DEBUG_FOO=1 labwc*. Increase logging of paths for config files (for example rc.xml, autostart, environment and menu.xml) as well as titlebar buttons. -*LABWC_DEBUG_CONFIG_NODENAMES*++ -*LABWC_DEBUG_MENU_NODENAMES* +*LABWC_DEBUG_CONFIG_NODENAMES* Enable logging of all nodenames (for example *policy.placement: Cascade* for *Cascade*) for config and menu files respectively. diff --git a/src/menu/menu.c b/src/menu/menu.c index 8d80231f..0232e2d5 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -20,12 +20,12 @@ #include "common/lab-scene-rect.h" #include "common/list.h" #include "common/mem.h" -#include "common/nodename.h" #include "common/scaled-font-buffer.h" #include "common/scaled-icon-buffer.h" #include "common/scene-helpers.h" #include "common/spawn.h" #include "common/string-helpers.h" +#include "common/xml.h" #include "labwc.h" #include "output.h" #include "workspaces.h" @@ -38,15 +38,6 @@ #define ICON_SIZE (rc.theme->menu_item_height - 2 * rc.theme->menu_items_padding_y) -/* state-machine variables for processing */ -struct menu_parse_context { - struct server *server; - struct menu *menu; - struct menuitem *item; - struct action *action; - bool in_item; -}; - static bool waiting_for_pipe_menu; static struct menuitem *selected_item; @@ -140,7 +131,7 @@ validate(struct server *server) } static struct menuitem * -item_create(struct menu *menu, const char *text, bool show_arrow) +item_create(struct menu *menu, const char *text, const char *icon_name, bool show_arrow) { assert(menu); assert(text); @@ -152,6 +143,13 @@ item_create(struct menu *menu, const char *text, bool show_arrow) menuitem->text = xstrdup(text); menuitem->arrow = show_arrow ? "›" : NULL; +#if HAVE_LIBSFDO + if (rc.menu_show_icons && !string_null_or_empty(icon_name)) { + menuitem->icon_name = xstrdup(icon_name); + menu->has_icons = true; + } +#endif + menuitem->native_width = font_width(&rc.font_menuitem, text); if (menuitem->arrow) { menuitem->native_width += font_width(&rc.font_menuitem, menuitem->arrow); @@ -473,34 +471,22 @@ menu_create_scene(struct menu *menu) * */ static void -fill_item(struct menu_parse_context *ctx, const char *nodename, - const char *content) +fill_item(struct menu *menu, xmlNode *node) { - /* defines the start of a new item */ - if (!strcmp(nodename, "label")) { - ctx->item = item_create(ctx->menu, content, false); - ctx->action = NULL; - } else if (!ctx->item) { - wlr_log(WLR_ERROR, "expect element first. " - "nodename: '%s' content: '%s'", nodename, content); - } else if (!strcmp(nodename, "icon")) { -#if HAVE_LIBSFDO - if (rc.menu_show_icons && !string_null_or_empty(content)) { - xstrdup_replace(ctx->item->icon_name, content); - ctx->menu->has_icons = true; - } -#endif - } else if (!strcmp(nodename, "name.action")) { - ctx->action = action_create(content); - if (ctx->action) { - wl_list_append(&ctx->item->actions, &ctx->action->link); - } - } else if (!ctx->action) { - wlr_log(WLR_ERROR, "expect element first. " - "nodename: '%s' content: '%s'", nodename, content); - } else { - action_arg_from_xml_node(ctx->action, nodename, content); + char *label = (char *)xmlGetProp(node, (xmlChar *)"label"); + char *icon_name = (char *)xmlGetProp(node, (xmlChar *)"icon"); + if (!label) { + wlr_log(WLR_ERROR, "missing label in "); + goto out; } + + struct menuitem *item = item_create(menu, (char *)label, icon_name, false); + lab_xml_expand_dotted_attributes(node); + append_parsed_actions(node, &item->actions); + +out: + free(label); + free(icon_name); } static void @@ -516,103 +502,10 @@ item_destroy(struct menuitem *item) free(item); } -/* - * We support XML CDATA for in menu.xml in order to provide backward - * compatibility with obmenu-generator. For example: - * - * - * - * - * - * - * - * - * - * is an old, deprecated openbox variety of . We support it - * for backward compatibility with old openbox-menu generators. It has the same - * function and - * - * The following nodenames support CDATA. - * - command.action.item.*menu.openbox_menu - * - execute.action.item.*menu.openbox_menu - * - command.action.item.openbox_pipe_menu - * - execute.action.item.openbox_pipe_menu - * - command.action.item.*menu.openbox_pipe_menu - * - execute.action.item.*menu.openbox_pipe_menu - * - * The *menu allows nested menus with nodenames such as ...menu.menu... or - * ...menu.menu.menu... and so on. We could use match_glob() for all of the - * above but it seems simpler to just check the first three fields. - */ -static bool -nodename_supports_cdata(char *nodename) -{ - return !strncmp("command.action.", nodename, 15) - || !strncmp("execute.action.", nodename, 15); -} - -static void -entry(struct menu_parse_context *ctx, xmlNode *node, char *nodename, - char *content) -{ - if (!nodename) { - return; - } - xmlChar *cdata = NULL; - if (!content && nodename_supports_cdata(nodename)) { - cdata = xmlNodeGetContent(node); - } - if (!content && !cdata) { - return; - } - string_truncate_at_pattern(nodename, ".openbox_menu"); - string_truncate_at_pattern(nodename, ".openbox_pipe_menu"); - if (getenv("LABWC_DEBUG_MENU_NODENAMES")) { - printf("%s: %s\n", nodename, content ? content : (char *)cdata); - } - if (ctx->in_item) { - /* - * Nodenames for most menu-items end with '.item.menu' - * but top-level pipemenu items do not have the associated - * element so merely end with '.item' - */ - string_truncate_at_pattern(nodename, ".item.menu"); - string_truncate_at_pattern(nodename, ".item"); - fill_item(ctx, nodename, content ? content : (char *)cdata); - } - xmlFree(cdata); -} - -static void -process_node(struct menu_parse_context *ctx, xmlNode *node) -{ - static char buffer[256]; - - char *content = (char *)node->content; - if (xmlIsBlankNode(node)) { - return; - } - char *name = nodename(node, buffer, sizeof(buffer)); - entry(ctx, node, name, content); -} - -static void xml_tree_walk(struct menu_parse_context *ctx, xmlNode *node); - -static void -traverse(struct menu_parse_context *ctx, xmlNode *n) -{ - xmlAttr *attr; - - process_node(ctx, n); - for (attr = n->properties; attr; attr = attr->next) { - xml_tree_walk(ctx, attr->children); - } - xml_tree_walk(ctx, n->children); -} - -static bool parse_buf(struct menu_parse_context *ctx, struct buf *buf); +static bool parse_buf(struct server *server, struct menu *menu, struct buf *buf); static int handle_pipemenu_readable(int fd, uint32_t mask, void *_ctx); static int handle_pipemenu_timeout(void *_ctx); +static void fill_menu_children(struct server *server, struct menu *parent, xmlNode *n); /* * elements have three different roles: @@ -621,7 +514,7 @@ static int handle_pipemenu_timeout(void *_ctx); * * Menuitem of submenu type - has ID only */ static void -handle_menu_element(struct menu_parse_context *ctx, xmlNode *n) +fill_menu(struct server *server, struct menu *parent, xmlNode *n) { char *label = (char *)xmlGetProp(n, (const xmlChar *)"label"); char *icon_name = (char *)xmlGetProp(n, (const xmlChar *)"icon"); @@ -636,10 +529,9 @@ handle_menu_element(struct menu_parse_context *ctx, xmlNode *n) if (execute && label) { wlr_log(WLR_DEBUG, "pipemenu '%s:%s:%s'", id, label, execute); - struct menu *pipemenu = - menu_create(ctx->server, ctx->menu, id, label); + struct menu *pipemenu = menu_create(server, parent, id, label); pipemenu->execute = xstrdup(execute); - if (!ctx->menu) { + if (!parent) { /* * A pipemenu may not have its parent like: * @@ -649,18 +541,16 @@ handle_menu_element(struct menu_parse_context *ctx, xmlNode *n) * */ } else { - ctx->item = item_create(ctx->menu, label, - /* arrow */ true); - fill_item(ctx, "icon", icon_name); - ctx->action = NULL; - ctx->item->submenu = pipemenu; + struct menuitem *item = item_create(parent, label, + icon_name, /* arrow */ true); + item->submenu = pipemenu; } - } else if ((label && ctx->menu) || !ctx->menu) { + } else if ((label && parent) || !parent) { /* - * (label && ctx->menu) refers to + * (label && parent) refers to * which is an nested (inline) menu definition. * - * (!ctx->menu) catches: + * (!parent) catches: * * * @@ -676,22 +566,20 @@ handle_menu_element(struct menu_parse_context *ctx, xmlNode *n) * attribute to make it easier for users to define "root-menu" * and "client-menu". */ - struct menu *parent_menu = ctx->menu; - ctx->menu = menu_create(ctx->server, parent_menu, id, label); + struct menu *menu = menu_create(server, parent, id, label); if (icon_name) { - ctx->menu->icon_name = xstrdup(icon_name); + menu->icon_name = xstrdup(icon_name); } - if (label && parent_menu) { + if (label && parent) { /* * In a nested (inline) menu definition we need to * create an item pointing to the new submenu */ - ctx->item = item_create(parent_menu, label, true); - fill_item(ctx, "icon", icon_name); - ctx->item->submenu = ctx->menu; + struct menuitem *item = item_create(parent, label, + icon_name, true); + item->submenu = menu; } - traverse(ctx, n); - ctx->menu = parent_menu; + fill_menu_children(server, menu, n); } else { /* * (when inside another element) creates an @@ -708,13 +596,13 @@ handle_menu_element(struct menu_parse_context *ctx, xmlNode *n) goto error; } - struct menu *menu = menu_get_by_id(ctx->server, id); + struct menu *menu = menu_get_by_id(server, id); if (!menu) { wlr_log(WLR_ERROR, "no menu with id '%s'", id); goto error; } - struct menu *iter = ctx->menu; + struct menu *iter = parent; while (iter) { if (iter == menu) { wlr_log(WLR_ERROR, "menus with the same id '%s' " @@ -724,9 +612,9 @@ handle_menu_element(struct menu_parse_context *ctx, xmlNode *n) iter = iter->parent; } - ctx->item = item_create(ctx->menu, menu->label, true); - fill_item(ctx, "icon", menu->icon_name); - ctx->item->submenu = menu; + struct menuitem *item = item_create(parent, menu->label, + parent->icon_name, true); + item->submenu = menu; } error: free(label); @@ -737,50 +625,42 @@ error: /* This can be one of and */ static void -handle_separator_element(struct menu_parse_context *ctx, xmlNode *n) +fill_separator(struct menu *menu, xmlNode *n) { char *label = (char *)xmlGetProp(n, (const xmlChar *)"label"); - ctx->item = separator_create(ctx->menu, label); + separator_create(menu, label); free(label); } +/* parent==NULL when processing toplevel menus in menu.xml */ static void -xml_tree_walk(struct menu_parse_context *ctx, xmlNode *node) +fill_menu_children(struct server *server, struct menu *parent, xmlNode *n) { - for (xmlNode *n = node; n && n->name; n = n->next) { - if (!strcasecmp((char *)n->name, "comment")) { - continue; - } - if (!strcasecmp((char *)n->name, "menu")) { - handle_menu_element(ctx, n); - continue; - } - if (!strcasecmp((char *)n->name, "separator")) { - if (!ctx->menu) { + xmlNode *child; + char *key, *content; + LAB_XML_FOR_EACH(n, child, key, content) { + if (!strcasecmp(key, "menu")) { + fill_menu(server, parent, child); + } else if (!strcasecmp(key, "separator")) { + if (!parent) { wlr_log(WLR_ERROR, "ignoring without parent "); continue; } - handle_separator_element(ctx, n); - continue; - } - if (!strcasecmp((char *)n->name, "item")) { - if (!ctx->menu) { + fill_separator(parent, child); + } else if (!strcasecmp(key, "item")) { + if (!parent) { wlr_log(WLR_ERROR, "ignoring without parent "); continue; } - ctx->in_item = true; - traverse(ctx, n); - ctx->in_item = false; - continue; + fill_item(parent, child); } - traverse(ctx, n); } } static bool -parse_buf(struct menu_parse_context *ctx, struct buf *buf) +parse_buf(struct server *server, struct menu *parent, struct buf *buf) { int options = 0; xmlDoc *d = xmlReadMemory(buf->data, buf->len, NULL, NULL, options); @@ -788,7 +668,10 @@ parse_buf(struct menu_parse_context *ctx, struct buf *buf) wlr_log(WLR_ERROR, "xmlParseMemory()"); return false; } - xml_tree_walk(ctx, xmlDocGetRootElement(d)); + + xmlNode *root = xmlDocGetRootElement(d); + fill_menu_children(server, parent, root); + xmlFreeDoc(d); xmlCleanupParser(); return true; @@ -814,8 +697,7 @@ parse_stream(struct server *server, FILE *stream) buf_add(&b, line); } free(line); - struct menu_parse_context ctx = {.server = server}; - parse_buf(&ctx, &b); + parse_buf(server, NULL, &b); buf_reset(&b); } @@ -939,6 +821,14 @@ init_client_send_to_menu(struct server *server) menu_create(server, NULL, "client-send-to-menu", ""); } +static struct action * +item_add_action(struct menuitem *item, const char *action_name) +{ + struct action *action = action_create(action_name); + wl_list_append(&item->actions, &action->link); + return action; +} + /* * This is client-send-to-menu * an internal menu similar to root-menu and client-menu @@ -955,21 +845,22 @@ update_client_send_to_menu(struct server *server) reset_menu(menu); - struct menu_parse_context ctx = {.server = server}; struct workspace *workspace; wl_list_for_each(workspace, &server->workspaces.all, link) { + struct buf buf = BUF_INIT; if (workspace == server->workspaces.current) { - char *label = strdup_printf(">%s<", workspace->name); - ctx.item = item_create(menu, label, - /*show arrow*/ false); - free(label); + buf_add_fmt(&buf, ">%s<", workspace->name); } else { - ctx.item = item_create(menu, workspace->name, - /*show arrow*/ false); + buf_add(&buf, workspace->name); } - fill_item(&ctx, "name.action", "SendToDesktop"); - fill_item(&ctx, "to.action", workspace->name); + struct menuitem *item = item_create(menu, buf.data, + NULL, /*show arrow*/ false); + + struct action *action = item_add_action(item, "SendToDesktop"); + action_arg_add_str(action, "to", "name"); + + buf_clear(&buf); } menu_create_scene(menu); @@ -998,7 +889,7 @@ update_client_list_combined_menu(struct server *server) reset_menu(menu); - struct menu_parse_context ctx = {.server = server}; + struct menuitem *item; struct workspace *workspace; struct view *view; struct buf buffer = BUF_INIT; @@ -1006,7 +897,7 @@ update_client_list_combined_menu(struct server *server) wl_list_for_each(workspace, &server->workspaces.all, link) { buf_add_fmt(&buffer, workspace == server->workspaces.current ? ">%s<" : "%s", workspace->name); - ctx.item = separator_create(menu, buffer.data); + separator_create(menu, buffer.data); buf_clear(&buffer); wl_list_for_each(view, &server->views, link) { @@ -1021,19 +912,19 @@ update_client_list_combined_menu(struct server *server) } buf_add(&buffer, title); - ctx.item = item_create(menu, buffer.data, + item = item_create(menu, buffer.data, NULL, /*show arrow*/ false); - ctx.item->client_list_view = view; - fill_item(&ctx, "name.action", "Focus"); - fill_item(&ctx, "name.action", "Raise"); + item->client_list_view = view; + item_add_action(item, "Focus"); + item_add_action(item, "Raise"); buf_clear(&buffer); menu->has_icons = true; } } - ctx.item = item_create(menu, _("Go there..."), + item = item_create(menu, _("Go there..."), NULL, /*show arrow*/ false); - fill_item(&ctx, "name.action", "GoToDesktop"); - fill_item(&ctx, "to.action", workspace->name); + struct action *action = item_add_action(item, "GoToDesktop"); + action_arg_add_str(action, "to", workspace->name); } buf_reset(&buffer); menu_create_scene(menu); @@ -1043,22 +934,22 @@ static void init_rootmenu(struct server *server) { struct menu *menu = menu_get_by_id(server, "root-menu"); + struct menuitem *item; /* Default menu if no menu.xml found */ if (!menu) { - struct menu_parse_context ctx = {.server = server}; menu = menu_create(server, NULL, "root-menu", ""); - ctx.item = item_create(menu, _("Terminal"), false); - fill_item(&ctx, "name.action", "Execute"); - fill_item(&ctx, "command.action", "lab-sensible-terminal"); + item = item_create(menu, _("Terminal"), NULL, false); + struct action *action = item_add_action(item, "Execute"); + action_arg_add_str(action, "command", "lab-sensible-terminal"); - ctx.item = separator_create(menu, NULL); + separator_create(menu, NULL); - ctx.item = item_create(menu, _("Reconfigure"), false); - fill_item(&ctx, "name.action", "Reconfigure"); - ctx.item = item_create(menu, _("Exit"), false); - fill_item(&ctx, "name.action", "Exit"); + item = item_create(menu, _("Reconfigure"), NULL, false); + item_add_action(item, "Reconfigure"); + item = item_create(menu, _("Exit"), NULL, false); + item_add_action(item, "Exit"); } } @@ -1066,47 +957,48 @@ static void init_windowmenu(struct server *server) { struct menu *menu = menu_get_by_id(server, "client-menu"); + struct menuitem *item; + struct action *action; /* Default menu if no menu.xml found */ if (!menu) { - struct menu_parse_context ctx = {.server = server}; menu = menu_create(server, NULL, "client-menu", ""); - ctx.item = item_create(menu, _("Minimize"), false); - fill_item(&ctx, "name.action", "Iconify"); - ctx.item = item_create(menu, _("Maximize"), false); - fill_item(&ctx, "name.action", "ToggleMaximize"); - ctx.item = item_create(menu, _("Fullscreen"), false); - fill_item(&ctx, "name.action", "ToggleFullscreen"); - ctx.item = item_create(menu, _("Roll Up/Down"), false); - fill_item(&ctx, "name.action", "ToggleShade"); - ctx.item = item_create(menu, _("Decorations"), false); - fill_item(&ctx, "name.action", "ToggleDecorations"); - ctx.item = item_create(menu, _("Always on Top"), false); - fill_item(&ctx, "name.action", "ToggleAlwaysOnTop"); + item = item_create(menu, _("Minimize"), NULL, false); + item_add_action(item, "Iconify"); + item = item_create(menu, _("Maximize"), NULL, false); + item_add_action(item, "ToggleMaximize"); + item = item_create(menu, _("Fullscreen"), NULL, false); + item_add_action(item, "ToggleFullscreen"); + item = item_create(menu, _("Roll Up/Down"), NULL, false); + item_add_action(item, "ToggleShade"); + item = item_create(menu, _("Decorations"), NULL, false); + item_add_action(item, "ToggleDecorations"); + item = item_create(menu, _("Always on Top"), NULL, false); + item_add_action(item, "ToggleAlwaysOnTop"); /* Workspace sub-menu */ struct menu *workspace_menu = menu_create(server, NULL, "workspaces", ""); - ctx.item = item_create(workspace_menu, _("Move Left"), false); + item = item_create(workspace_menu, _("Move Left"), NULL, false); /* * is true by default so * GoToDesktop will be called as part of the action. */ - fill_item(&ctx, "name.action", "SendToDesktop"); - fill_item(&ctx, "to.action", "left"); - ctx.item = item_create(workspace_menu, _("Move Right"), false); - fill_item(&ctx, "name.action", "SendToDesktop"); - fill_item(&ctx, "to.action", "right"); - ctx.item = separator_create(workspace_menu, ""); - ctx.item = item_create(workspace_menu, - _("Always on Visible Workspace"), false); - fill_item(&ctx, "name.action", "ToggleOmnipresent"); + action = item_add_action(item, "SendToDesktop"); + action_arg_add_str(action, "to", "left"); + item = item_create(workspace_menu, _("Move Right"), NULL, false); + action = item_add_action(item, "SendToDesktop"); + action_arg_add_str(action, "to", "right"); + separator_create(workspace_menu, ""); + item = item_create(workspace_menu, + _("Always on Visible Workspace"), NULL, false); + item_add_action(item, "ToggleOmnipresent"); - ctx.item = item_create(menu, _("Workspace"), true); - ctx.item->submenu = workspace_menu; + item = item_create(menu, _("Workspace"), NULL, true); + item->submenu = workspace_menu; - ctx.item = item_create(menu, _("Close"), false); - fill_item(&ctx, "name.action", "Close"); + item = item_create(menu, _("Close"), NULL, false); + item_add_action(item, "Close"); } if (wl_list_length(&rc.workspace_config.workspaces) == 1) { @@ -1348,11 +1240,7 @@ static void create_pipe_menu(struct menu_pipe_context *ctx) { struct server *server = ctx->pipemenu->server; - struct menu_parse_context parse_ctx = { - .server = server, - .menu = ctx->pipemenu, - }; - if (!parse_buf(&parse_ctx, &ctx->buf)) { + if (!parse_buf(server, ctx->pipemenu, &ctx->buf)) { return; } /* TODO: apply validate() only for generated pipemenus */ From bc344619776f4ad34dc12994653e2aa3abad110c Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Mon, 4 Aug 2025 20:28:25 -0400 Subject: [PATCH 066/248] output: make autoEnableOutputs=no apply only to drm outputs It is not really useful for other output backends and just results in no outputs being enabled at all. (This is mainly an annoyance for developers normally running with drm but occasionally nested.) --- docs/labwc-config.5.scd | 2 +- src/output.c | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index 7def5b92..860391c9 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -216,7 +216,7 @@ this is for compatibility with Openbox. ** [yes|no] Automatically enable outputs at startup and when new outputs are - connected. Default is yes. + connected. This option applies only to drm outputs. Default is yes. Caution: Disabling this option will make the labwc session unusable unless an external tool such as `wlr-randr` or `kanshi` is used to diff --git a/src/output.c b/src/output.c index 42d8205d..0002865c 100644 --- a/src/output.c +++ b/src/output.c @@ -547,7 +547,13 @@ handle_new_output(struct wl_listener *listener, void *data) wlr_scene_node_raise_to_top(&output->osd_tree->node); wlr_scene_node_raise_to_top(&output->session_lock_tree->node); - if (rc.auto_enable_outputs) { + /* + * autoEnableOutputs=no only makes sense for outputs that can be + * hotplugged - currently only drm outputs. With wl/x11/headless + * it would result in no outputs being enabled at all. This check + * might need tweaking if wlroots adds other output backends. + */ + if (rc.auto_enable_outputs || !wlr_output_is_drm(wlr_output)) { configure_new_output(server, output); } From 2547f96984b79bbb1c732a0afa4d0d62be7c18f0 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Mon, 4 Aug 2025 20:55:16 -0400 Subject: [PATCH 067/248] view: assert internal signals are disconnected before destroy If they are not empty, then we are headed for use-after-free shortly. An assert() failure is easier to debug than UAF, so let's fail early. Inspired by: https://gitlab.freedesktop.org/wlroots/wlroots/-/commit/8f56f7ca43257cc05c7c4eb57a0f541e05cf9a79 --- src/view.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/view.c b/src/view.c index 95cafc80..33b92444 100644 --- a/src/view.c +++ b/src/view.c @@ -2684,6 +2684,16 @@ view_destroy(struct view *view) view->scene_tree = NULL; } + assert(wl_list_empty(&view->events.new_app_id.listener_list)); + assert(wl_list_empty(&view->events.new_title.listener_list)); + assert(wl_list_empty(&view->events.new_outputs.listener_list)); + assert(wl_list_empty(&view->events.maximized.listener_list)); + assert(wl_list_empty(&view->events.minimized.listener_list)); + assert(wl_list_empty(&view->events.fullscreened.listener_list)); + assert(wl_list_empty(&view->events.activated.listener_list)); + assert(wl_list_empty(&view->events.set_icon.listener_list)); + assert(wl_list_empty(&view->events.destroy.listener_list)); + /* Remove view from server->views */ wl_list_remove(&view->link); free(view); From e530f437088f6b02cb2e57e40575fe83497c93ab Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 7 Jul 2025 05:15:46 +0900 Subject: [PATCH 068/248] ime: don't use data argument in some signal handlers In wlroots 0.20, the those data argument will be just NULL. --- src/input/ime.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/input/ime.c b/src/input/ime.c index 350374df..cf8cd2d9 100644 --- a/src/input/ime.c +++ b/src/input/ime.c @@ -270,8 +270,7 @@ handle_input_method_commit(struct wl_listener *listener, void *data) { struct input_method_relay *relay = wl_container_of(listener, relay, input_method_commit); - struct wlr_input_method_v2 *input_method = data; - assert(relay->input_method == input_method); + struct wlr_input_method_v2 *input_method = relay->input_method; struct text_input *text_input = relay->active_text_input; if (!text_input) { @@ -305,7 +304,9 @@ handle_keyboard_grab_destroy(struct wl_listener *listener, void *data) { struct input_method_relay *relay = wl_container_of(listener, relay, keyboard_grab_destroy); - struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data; + struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = + relay->input_method->keyboard_grab; + wl_list_remove(&relay->keyboard_grab_destroy.link); if (keyboard_grab->keyboard) { @@ -343,7 +344,6 @@ handle_input_method_destroy(struct wl_listener *listener, void *data) { struct input_method_relay *relay = wl_container_of(listener, relay, input_method_destroy); - assert(relay->input_method == data); wl_list_remove(&relay->input_method_commit.link); wl_list_remove(&relay->input_method_grab_keyboard.link); wl_list_remove(&relay->input_method_new_popup_surface.link); From 9557091a1a100cb9b2646b5de61f9dbe796053e8 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Wed, 6 Aug 2025 00:23:40 -0400 Subject: [PATCH 069/248] xdg: don't try to reposition unmapped view in timeout handler --- src/xdg.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/xdg.c b/src/xdg.c index 363bd478..d71a7d93 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -288,6 +288,14 @@ handle_configure_timeout(void *data) view->pending_configure_serial = 0; view->pending_configure_timeout = NULL; + /* + * No need to do anything else if the view is just being slow to + * map - the map handler will take care of the positioning. + */ + if (!view->mapped) { + return 0; /* ignored per wl_event_loop docs */ + } + bool empty_pending = wlr_box_empty(&view->pending); if (empty_pending || view->pending.x != view->current.x || view->pending.y != view->current.y) { From 508df093c4c499f3159da04e88228b15a3c7ae49 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Wed, 6 Aug 2025 00:30:54 -0400 Subject: [PATCH 070/248] xdg: try to keep view on the same output in timeout case --- src/xdg.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/xdg.c b/src/xdg.c index d71a7d93..e54fdea0 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -322,6 +322,13 @@ handle_configure_timeout(void *data) wlr_log(WLR_INFO, "using fallback position"); view->pending.x = VIEW_FALLBACK_X; view->pending.y = VIEW_FALLBACK_Y; + /* At least try to keep it on the same output */ + if (output_is_usable(view->output)) { + struct wlr_box box = + output_usable_area_in_layout_coords(view->output); + view->pending.x += box.x; + view->pending.y += box.y; + } } view->current.x = view->pending.x; view->current.y = view->pending.y; From d87ef7568a97f03a84061feb94c7ce1dd37c11da Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Wed, 6 Aug 2025 21:05:28 +0100 Subject: [PATCH 071/248] common/xml.c: fix memory leak in lab_xml_get_bool() --- src/common/xml.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/xml.c b/src/common/xml.c index 797af9c0..4868d7ed 100644 --- a/src/common/xml.c +++ b/src/common/xml.c @@ -190,6 +190,7 @@ lab_xml_get_bool(xmlNode *node, const char *key, bool *b) if (get_node(node, key, &child, /* leaf_only */ true)) { char *s = (char *)xmlNodeGetContent(child); int ret = parse_bool(s, -1); + xmlFree(s); if (ret >= 0) { *b = ret; return true; From 55b495f3981aab0b0af825cca594324a2f24fbb2 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Thu, 7 Aug 2025 15:27:12 -0400 Subject: [PATCH 072/248] foreign-toplevel: disconnect internal signals from handle_handle_destroy() If the handle gets destroyed from the wlroots side before the view is destroyed, the internal signals (emitted from the view) are not disconnected and will assert() if invoked. --- src/foreign-toplevel/ext-foreign.c | 16 +++++++++------- src/foreign-toplevel/wlr-foreign.c | 28 +++++++++++++++------------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/foreign-toplevel/ext-foreign.c b/src/foreign-toplevel/ext-foreign.c index e959b626..e6c27fef 100644 --- a/src/foreign-toplevel/ext-foreign.c +++ b/src/foreign-toplevel/ext-foreign.c @@ -15,6 +15,14 @@ handle_handle_destroy(struct wl_listener *listener, void *data) /* Client side requests */ wl_list_remove(&ext_toplevel->on.handle_destroy.link); + + /* Compositor side state changes */ + wl_list_remove(&ext_toplevel->on_view.new_app_id.link); + wl_list_remove(&ext_toplevel->on_view.new_title.link); + + /* Internal signals */ + wl_list_remove(&ext_toplevel->on_foreign_toplevel.toplevel_destroy.link); + ext_toplevel->handle = NULL; } @@ -60,14 +68,8 @@ handle_toplevel_destroy(struct wl_listener *listener, void *data) return; } + /* invokes handle_handle_destroy() which does more cleanup */ wlr_ext_foreign_toplevel_handle_v1_destroy(ext_toplevel->handle); - - /* Compositor side state changes */ - wl_list_remove(&ext_toplevel->on_view.new_app_id.link); - wl_list_remove(&ext_toplevel->on_view.new_title.link); - - /* Internal signals */ - wl_list_remove(&ext_toplevel->on_foreign_toplevel.toplevel_destroy.link); } /* Internal API */ diff --git a/src/foreign-toplevel/wlr-foreign.c b/src/foreign-toplevel/wlr-foreign.c index 7c915ac4..8252abf3 100644 --- a/src/foreign-toplevel/wlr-foreign.c +++ b/src/foreign-toplevel/wlr-foreign.c @@ -72,6 +72,20 @@ handle_handle_destroy(struct wl_listener *listener, void *data) wl_list_remove(&wlr_toplevel->on.request_activate.link); wl_list_remove(&wlr_toplevel->on.request_close.link); wl_list_remove(&wlr_toplevel->on.handle_destroy.link); + + /* Compositor side state changes */ + wl_list_remove(&wlr_toplevel->on_view.new_app_id.link); + wl_list_remove(&wlr_toplevel->on_view.new_title.link); + wl_list_remove(&wlr_toplevel->on_view.new_outputs.link); + wl_list_remove(&wlr_toplevel->on_view.maximized.link); + wl_list_remove(&wlr_toplevel->on_view.minimized.link); + wl_list_remove(&wlr_toplevel->on_view.fullscreened.link); + wl_list_remove(&wlr_toplevel->on_view.activated.link); + + /* Internal signals */ + wl_list_remove(&wlr_toplevel->on_foreign_toplevel.toplevel_parent.link); + wl_list_remove(&wlr_toplevel->on_foreign_toplevel.toplevel_destroy.link); + wlr_toplevel->handle = NULL; } @@ -203,20 +217,8 @@ handle_toplevel_destroy(struct wl_listener *listener, void *data) listener, wlr_toplevel, on_foreign_toplevel.toplevel_destroy); assert(wlr_toplevel->handle); + /* invokes handle_handle_destroy() which does more cleanup */ wlr_foreign_toplevel_handle_v1_destroy(wlr_toplevel->handle); - - /* Compositor side state changes */ - wl_list_remove(&wlr_toplevel->on_view.new_app_id.link); - wl_list_remove(&wlr_toplevel->on_view.new_title.link); - wl_list_remove(&wlr_toplevel->on_view.new_outputs.link); - wl_list_remove(&wlr_toplevel->on_view.maximized.link); - wl_list_remove(&wlr_toplevel->on_view.minimized.link); - wl_list_remove(&wlr_toplevel->on_view.fullscreened.link); - wl_list_remove(&wlr_toplevel->on_view.activated.link); - - /* Internal signals */ - wl_list_remove(&wlr_toplevel->on_foreign_toplevel.toplevel_parent.link); - wl_list_remove(&wlr_toplevel->on_foreign_toplevel.toplevel_destroy.link); } /* Internal API */ From 02df0a15d714dc47934a0c6ba4ba57e3f841daa5 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Thu, 7 Aug 2025 20:30:59 -0400 Subject: [PATCH 073/248] foreign-toplevel: simplify and fully separate ext-foreign/wlr-foreign Currently, the dependencies between foreign-toplevel[-internal], ext-foreign, and wlr-foreign are cyclical and a bit complex. I suggest we reorganize it into a simpler hierarchy: foreign-toplevel/ -> foreign.c/h -> (depends on) ext-foreign.c/h -> (depends on) wlr-foreign.c/h The refactored code is smaller and (IMO) easier to follow. In detail: - Add include/foreign-toplevel folder mirroring src/foreign-toplevel - Split foreign-toplevel-internal.h to ext-foreign.h and wlr-foreign.h - Eliminate ext-/wlr-foreign.c -> foreign.c reverse dependencies (including internal signals and foreign_request* functions) - Make struct foreign_toplevel private to foreign.c Lightly tested with qmpanel (which uses wlr-foreign-toplevel). v2: reorder foreign-toplevel internal API funcs --- include/foreign-toplevel-internal.h | 83 -------- include/foreign-toplevel/ext-foreign.h | 27 +++ .../foreign.h} | 0 include/foreign-toplevel/wlr-foreign.h | 39 ++++ src/foreign-toplevel/ext-foreign.c | 74 ++++--- src/foreign-toplevel/foreign.c | 60 ++---- src/foreign-toplevel/wlr-foreign.c | 181 ++++++++---------- src/view-impl-common.c | 3 +- src/view.c | 2 +- src/xdg.c | 2 +- src/xwayland.c | 2 +- 11 files changed, 202 insertions(+), 271 deletions(-) delete mode 100644 include/foreign-toplevel-internal.h create mode 100644 include/foreign-toplevel/ext-foreign.h rename include/{foreign-toplevel.h => foreign-toplevel/foreign.h} (100%) create mode 100644 include/foreign-toplevel/wlr-foreign.h diff --git a/include/foreign-toplevel-internal.h b/include/foreign-toplevel-internal.h deleted file mode 100644 index 7be87b3d..00000000 --- a/include/foreign-toplevel-internal.h +++ /dev/null @@ -1,83 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef LABWC_FOREIGN_TOPLEVEL_INTERNAL_H -#define LABWC_FOREIGN_TOPLEVEL_INTERNAL_H - -#include -#include -#include "foreign-toplevel.h" - -struct foreign_toplevel { - struct view *view; - - /* *-toplevel implementations */ - struct wlr_foreign_toplevel { - struct wlr_foreign_toplevel_handle_v1 *handle; - - /* Client side events */ - struct { - struct wl_listener request_maximize; - struct wl_listener request_minimize; - struct wl_listener request_fullscreen; - struct wl_listener request_activate; - struct wl_listener request_close; - struct wl_listener handle_destroy; - } on; - - /* Compositor side state updates */ - struct { - struct wl_listener new_app_id; - struct wl_listener new_title; - struct wl_listener new_outputs; - struct wl_listener maximized; - struct wl_listener minimized; - struct wl_listener fullscreened; - struct wl_listener activated; - } on_view; - - /* Internal signals */ - struct { - struct wl_listener toplevel_parent; - struct wl_listener toplevel_destroy; - } on_foreign_toplevel; - - } wlr_toplevel; - - struct ext_foreign_toplevel { - struct wlr_ext_foreign_toplevel_handle_v1 *handle; - - /* Client side events */ - struct { - struct wl_listener handle_destroy; - } on; - - /* Compositor side state updates */ - struct { - struct wl_listener new_app_id; - struct wl_listener new_title; - } on_view; - - /* Internal signals */ - struct { - struct wl_listener toplevel_destroy; - } on_foreign_toplevel; - - } ext_toplevel; - - /* TODO: add struct xdg_x11_mapped_toplevel at some point */ - - struct { - struct wl_signal toplevel_parent; /* struct view *parent */ - struct wl_signal toplevel_destroy; - } events; -}; - -void ext_foreign_toplevel_init(struct foreign_toplevel *toplevel); -void wlr_foreign_toplevel_init(struct foreign_toplevel *toplevel); - -void foreign_request_minimize(struct foreign_toplevel *toplevel, bool minimized); -void foreign_request_maximize(struct foreign_toplevel *toplevel, enum view_axis axis); -void foreign_request_fullscreen(struct foreign_toplevel *toplevel, bool fullscreen); -void foreign_request_activate(struct foreign_toplevel *toplevel); -void foreign_request_close(struct foreign_toplevel *toplevel); - -#endif /* LABWC_FOREIGN_TOPLEVEL_INTERNAL_H */ diff --git a/include/foreign-toplevel/ext-foreign.h b/include/foreign-toplevel/ext-foreign.h new file mode 100644 index 00000000..d9442c02 --- /dev/null +++ b/include/foreign-toplevel/ext-foreign.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_EXT_FOREIGN_TOPLEVEL_H +#define LABWC_EXT_FOREIGN_TOPLEVEL_H + +#include + +struct ext_foreign_toplevel { + struct view *view; + struct wlr_ext_foreign_toplevel_handle_v1 *handle; + + /* Client side events */ + struct { + struct wl_listener handle_destroy; + } on; + + /* Compositor side state updates */ + struct { + struct wl_listener new_app_id; + struct wl_listener new_title; + } on_view; +}; + +void ext_foreign_toplevel_init(struct ext_foreign_toplevel *ext_toplevel, + struct view *view); +void ext_foreign_toplevel_finish(struct ext_foreign_toplevel *ext_toplevel); + +#endif /* LABWC_EXT_FOREIGN_TOPLEVEL_H */ diff --git a/include/foreign-toplevel.h b/include/foreign-toplevel/foreign.h similarity index 100% rename from include/foreign-toplevel.h rename to include/foreign-toplevel/foreign.h diff --git a/include/foreign-toplevel/wlr-foreign.h b/include/foreign-toplevel/wlr-foreign.h new file mode 100644 index 00000000..2da44752 --- /dev/null +++ b/include/foreign-toplevel/wlr-foreign.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_WLR_FOREIGN_TOPLEVEL_H +#define LABWC_WLR_FOREIGN_TOPLEVEL_H + +#include + +struct wlr_foreign_toplevel { + struct view *view; + struct wlr_foreign_toplevel_handle_v1 *handle; + + /* Client side events */ + struct { + struct wl_listener request_maximize; + struct wl_listener request_minimize; + struct wl_listener request_fullscreen; + struct wl_listener request_activate; + struct wl_listener request_close; + struct wl_listener handle_destroy; + } on; + + /* Compositor side state updates */ + struct { + struct wl_listener new_app_id; + struct wl_listener new_title; + struct wl_listener new_outputs; + struct wl_listener maximized; + struct wl_listener minimized; + struct wl_listener fullscreened; + struct wl_listener activated; + } on_view; +}; + +void wlr_foreign_toplevel_init(struct wlr_foreign_toplevel *wlr_toplevel, + struct view *view); +void wlr_foreign_toplevel_set_parent(struct wlr_foreign_toplevel *wlr_toplevel, + struct wlr_foreign_toplevel *parent); +void wlr_foreign_toplevel_finish(struct wlr_foreign_toplevel *wlr_toplevel); + +#endif /* LABWC_WLR_FOREIGN_TOPLEVEL_H */ diff --git a/src/foreign-toplevel/ext-foreign.c b/src/foreign-toplevel/ext-foreign.c index e6c27fef..587193a6 100644 --- a/src/foreign-toplevel/ext-foreign.c +++ b/src/foreign-toplevel/ext-foreign.c @@ -1,10 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "foreign-toplevel/ext-foreign.h" #include #include #include "common/macros.h" #include "labwc.h" #include "view.h" -#include "foreign-toplevel-internal.h" /* ext signals */ static void @@ -20,9 +20,6 @@ handle_handle_destroy(struct wl_listener *listener, void *data) wl_list_remove(&ext_toplevel->on_view.new_app_id.link); wl_list_remove(&ext_toplevel->on_view.new_title.link); - /* Internal signals */ - wl_list_remove(&ext_toplevel->on_foreign_toplevel.toplevel_destroy.link); - ext_toplevel->handle = NULL; } @@ -30,60 +27,44 @@ handle_handle_destroy(struct wl_listener *listener, void *data) static void handle_new_app_id(struct wl_listener *listener, void *data) { - struct foreign_toplevel *toplevel = - wl_container_of(listener, toplevel, ext_toplevel.on_view.new_app_id); - assert(toplevel->ext_toplevel.handle); + struct ext_foreign_toplevel *ext_toplevel = + wl_container_of(listener, ext_toplevel, on_view.new_app_id); + assert(ext_toplevel->handle); struct wlr_ext_foreign_toplevel_handle_v1_state state = { - .title = view_get_string_prop(toplevel->view, "title"), - .app_id = view_get_string_prop(toplevel->view, "app_id") + .title = view_get_string_prop(ext_toplevel->view, "title"), + .app_id = view_get_string_prop(ext_toplevel->view, "app_id") }; - wlr_ext_foreign_toplevel_handle_v1_update_state( - toplevel->ext_toplevel.handle, &state); + wlr_ext_foreign_toplevel_handle_v1_update_state(ext_toplevel->handle, + &state); } static void handle_new_title(struct wl_listener *listener, void *data) { - struct foreign_toplevel *toplevel = - wl_container_of(listener, toplevel, ext_toplevel.on_view.new_title); - assert(toplevel->ext_toplevel.handle); + struct ext_foreign_toplevel *ext_toplevel = + wl_container_of(listener, ext_toplevel, on_view.new_title); + assert(ext_toplevel->handle); struct wlr_ext_foreign_toplevel_handle_v1_state state = { - .title = view_get_string_prop(toplevel->view, "title"), - .app_id = view_get_string_prop(toplevel->view, "app_id") + .title = view_get_string_prop(ext_toplevel->view, "title"), + .app_id = view_get_string_prop(ext_toplevel->view, "app_id") }; - wlr_ext_foreign_toplevel_handle_v1_update_state( - toplevel->ext_toplevel.handle, &state); -} - -/* Internal signals */ -static void -handle_toplevel_destroy(struct wl_listener *listener, void *data) -{ - struct ext_foreign_toplevel *ext_toplevel = - wl_container_of(listener, ext_toplevel, on_foreign_toplevel.toplevel_destroy); - - if (!ext_toplevel->handle) { - return; - } - - /* invokes handle_handle_destroy() which does more cleanup */ - wlr_ext_foreign_toplevel_handle_v1_destroy(ext_toplevel->handle); + wlr_ext_foreign_toplevel_handle_v1_update_state(ext_toplevel->handle, + &state); } /* Internal API */ void -ext_foreign_toplevel_init(struct foreign_toplevel *toplevel) +ext_foreign_toplevel_init(struct ext_foreign_toplevel *ext_toplevel, + struct view *view) { - struct ext_foreign_toplevel *ext_toplevel = &toplevel->ext_toplevel; - struct view *view = toplevel->view; - assert(view->server->foreign_toplevel_list); + ext_toplevel->view = view; struct wlr_ext_foreign_toplevel_handle_v1_state state = { - .title = view_get_string_prop(toplevel->view, "title"), - .app_id = view_get_string_prop(toplevel->view, "app_id") + .title = view_get_string_prop(view, "title"), + .app_id = view_get_string_prop(view, "app_id") }; ext_toplevel->handle = wlr_ext_foreign_toplevel_handle_v1_create( view->server->foreign_toplevel_list, &state); @@ -101,7 +82,16 @@ ext_foreign_toplevel_init(struct foreign_toplevel *toplevel) /* Compositor side state changes */ CONNECT_SIGNAL(view, &ext_toplevel->on_view, new_app_id); CONNECT_SIGNAL(view, &ext_toplevel->on_view, new_title); - - /* Internal signals */ - CONNECT_SIGNAL(toplevel, &ext_toplevel->on_foreign_toplevel, toplevel_destroy); +} + +void +ext_foreign_toplevel_finish(struct ext_foreign_toplevel *ext_toplevel) +{ + if (!ext_toplevel->handle) { + return; + } + + /* invokes handle_handle_destroy() which does more cleanup */ + wlr_ext_foreign_toplevel_handle_v1_destroy(ext_toplevel->handle); + assert(!ext_toplevel->handle); } diff --git a/src/foreign-toplevel/foreign.c b/src/foreign-toplevel/foreign.c index a469305c..14563431 100644 --- a/src/foreign-toplevel/foreign.c +++ b/src/foreign-toplevel/foreign.c @@ -1,44 +1,19 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "foreign-toplevel/foreign.h" #include -#include -#include "common/macros.h" #include "common/mem.h" -#include "labwc.h" +#include "foreign-toplevel/ext-foreign.h" +#include "foreign-toplevel/wlr-foreign.h" #include "view.h" -#include "foreign-toplevel-internal.h" -/* Internal API */ -void -foreign_request_minimize(struct foreign_toplevel *toplevel, bool minimized) -{ - view_minimize(toplevel->view, minimized); -} +struct foreign_toplevel { + /* *-toplevel implementations */ + struct wlr_foreign_toplevel wlr_toplevel; + struct ext_foreign_toplevel ext_toplevel; -void -foreign_request_maximize(struct foreign_toplevel *toplevel, enum view_axis axis) -{ - view_maximize(toplevel->view, axis, /*store_natural_geometry*/ true); -} + /* TODO: add struct xdg_x11_mapped_toplevel at some point */ +}; -void -foreign_request_fullscreen(struct foreign_toplevel *toplevel, bool fullscreen) -{ - view_set_fullscreen(toplevel->view, fullscreen); -} - -void -foreign_request_activate(struct foreign_toplevel *toplevel) -{ - desktop_focus_view(toplevel->view, /*raise*/ true); -} - -void -foreign_request_close(struct foreign_toplevel *toplevel) -{ - view_close(toplevel->view); -} - -/* Public API */ struct foreign_toplevel * foreign_toplevel_create(struct view *view) { @@ -46,13 +21,8 @@ foreign_toplevel_create(struct view *view) assert(view->mapped); struct foreign_toplevel *toplevel = znew(*toplevel); - toplevel->view = view; - - wl_signal_init(&toplevel->events.toplevel_parent); - wl_signal_init(&toplevel->events.toplevel_destroy); - - wlr_foreign_toplevel_init(toplevel); - ext_foreign_toplevel_init(toplevel); + wlr_foreign_toplevel_init(&toplevel->wlr_toplevel, view); + ext_foreign_toplevel_init(&toplevel->ext_toplevel, view); return toplevel; } @@ -61,15 +31,15 @@ void foreign_toplevel_set_parent(struct foreign_toplevel *toplevel, struct foreign_toplevel *parent) { assert(toplevel); - wl_signal_emit_mutable(&toplevel->events.toplevel_parent, parent); + wlr_foreign_toplevel_set_parent(&toplevel->wlr_toplevel, + parent ? &parent->wlr_toplevel : NULL); } void foreign_toplevel_destroy(struct foreign_toplevel *toplevel) { assert(toplevel); - wl_signal_emit_mutable(&toplevel->events.toplevel_destroy, NULL); - assert(!toplevel->wlr_toplevel.handle); - assert(!toplevel->ext_toplevel.handle); + wlr_foreign_toplevel_finish(&toplevel->wlr_toplevel); + ext_foreign_toplevel_finish(&toplevel->ext_toplevel); free(toplevel); } diff --git a/src/foreign-toplevel/wlr-foreign.c b/src/foreign-toplevel/wlr-foreign.c index 8252abf3..9f0ed8e6 100644 --- a/src/foreign-toplevel/wlr-foreign.c +++ b/src/foreign-toplevel/wlr-foreign.c @@ -1,62 +1,63 @@ // SPDX-License-Identifier: GPL-2.0-only +#include "foreign-toplevel/wlr-foreign.h" #include #include #include "common/macros.h" #include "labwc.h" #include "output.h" #include "view.h" -#include "foreign-toplevel-internal.h" /* wlr signals */ static void handle_request_minimize(struct wl_listener *listener, void *data) { - struct foreign_toplevel *toplevel = wl_container_of( - listener, toplevel, wlr_toplevel.on.request_minimize); + struct wlr_foreign_toplevel *wlr_toplevel = + wl_container_of(listener, wlr_toplevel, on.request_minimize); struct wlr_foreign_toplevel_handle_v1_minimized_event *event = data; - foreign_request_minimize(toplevel, event->minimized); + view_minimize(wlr_toplevel->view, event->minimized); } static void handle_request_maximize(struct wl_listener *listener, void *data) { - struct foreign_toplevel *toplevel = wl_container_of( - listener, toplevel, wlr_toplevel.on.request_maximize); + struct wlr_foreign_toplevel *wlr_toplevel = + wl_container_of(listener, wlr_toplevel, on.request_maximize); struct wlr_foreign_toplevel_handle_v1_maximized_event *event = data; - foreign_request_maximize(toplevel, - event->maximized ? VIEW_AXIS_BOTH : VIEW_AXIS_NONE); + view_maximize(wlr_toplevel->view, + event->maximized ? VIEW_AXIS_BOTH : VIEW_AXIS_NONE, + /*store_natural_geometry*/ true); } static void handle_request_fullscreen(struct wl_listener *listener, void *data) { - struct foreign_toplevel *toplevel = wl_container_of( - listener, toplevel, wlr_toplevel.on.request_fullscreen); + struct wlr_foreign_toplevel *wlr_toplevel = + wl_container_of(listener, wlr_toplevel, on.request_fullscreen); struct wlr_foreign_toplevel_handle_v1_fullscreen_event *event = data; /* TODO: This ignores event->output */ - foreign_request_fullscreen(toplevel, event->fullscreen); + view_set_fullscreen(wlr_toplevel->view, event->fullscreen); } static void handle_request_activate(struct wl_listener *listener, void *data) { - struct foreign_toplevel *toplevel = wl_container_of( - listener, toplevel, wlr_toplevel.on.request_activate); + struct wlr_foreign_toplevel *wlr_toplevel = + wl_container_of(listener, wlr_toplevel, on.request_activate); /* In a multi-seat world we would select seat based on event->seat here. */ - foreign_request_activate(toplevel); + desktop_focus_view(wlr_toplevel->view, /*raise*/ true); } static void handle_request_close(struct wl_listener *listener, void *data) { - struct foreign_toplevel *toplevel = wl_container_of( - listener, toplevel, wlr_toplevel.on.request_close); + struct wlr_foreign_toplevel *wlr_toplevel = + wl_container_of(listener, wlr_toplevel, on.request_close); - foreign_request_close(toplevel); + view_close(wlr_toplevel->view); } static void @@ -82,10 +83,6 @@ handle_handle_destroy(struct wl_listener *listener, void *data) wl_list_remove(&wlr_toplevel->on_view.fullscreened.link); wl_list_remove(&wlr_toplevel->on_view.activated.link); - /* Internal signals */ - wl_list_remove(&wlr_toplevel->on_foreign_toplevel.toplevel_parent.link); - wl_list_remove(&wlr_toplevel->on_foreign_toplevel.toplevel_destroy.link); - wlr_toplevel->handle = NULL; } @@ -93,41 +90,41 @@ handle_handle_destroy(struct wl_listener *listener, void *data) static void handle_new_app_id(struct wl_listener *listener, void *data) { - struct foreign_toplevel *toplevel = - wl_container_of(listener, toplevel, wlr_toplevel.on_view.new_app_id); - assert(toplevel->wlr_toplevel.handle); + struct wlr_foreign_toplevel *wlr_toplevel = + wl_container_of(listener, wlr_toplevel, on_view.new_app_id); + assert(wlr_toplevel->handle); - const char *app_id = view_get_string_prop(toplevel->view, "app_id"); - const char *wlr_app_id = toplevel->wlr_toplevel.handle->app_id; + const char *app_id = view_get_string_prop(wlr_toplevel->view, "app_id"); + const char *wlr_app_id = wlr_toplevel->handle->app_id; if (app_id && wlr_app_id && !strcmp(app_id, wlr_app_id)) { /* Don't send app_id if they are the same */ return; } - wlr_foreign_toplevel_handle_v1_set_app_id(toplevel->wlr_toplevel.handle, app_id); + wlr_foreign_toplevel_handle_v1_set_app_id(wlr_toplevel->handle, app_id); } static void handle_new_title(struct wl_listener *listener, void *data) { - struct foreign_toplevel *toplevel = - wl_container_of(listener, toplevel, wlr_toplevel.on_view.new_title); - assert(toplevel->wlr_toplevel.handle); + struct wlr_foreign_toplevel *wlr_toplevel = + wl_container_of(listener, wlr_toplevel, on_view.new_title); + assert(wlr_toplevel->handle); - const char *title = view_get_string_prop(toplevel->view, "title"); - const char *wlr_title = toplevel->wlr_toplevel.handle->title; + const char *title = view_get_string_prop(wlr_toplevel->view, "title"); + const char *wlr_title = wlr_toplevel->handle->title; if (title && wlr_title && !strcmp(title, wlr_title)) { /* Don't send title if they are the same */ return; } - wlr_foreign_toplevel_handle_v1_set_title(toplevel->wlr_toplevel.handle, title); + wlr_foreign_toplevel_handle_v1_set_title(wlr_toplevel->handle, title); } static void handle_new_outputs(struct wl_listener *listener, void *data) { - struct foreign_toplevel *toplevel = - wl_container_of(listener, toplevel, wlr_toplevel.on_view.new_outputs); - assert(toplevel->wlr_toplevel.handle); + struct wlr_foreign_toplevel *wlr_toplevel = + wl_container_of(listener, wlr_toplevel, on_view.new_outputs); + assert(wlr_toplevel->handle); /* * Loop over all outputs and notify foreign_toplevel clients about changes. @@ -137,13 +134,13 @@ handle_new_outputs(struct wl_listener *listener, void *data) * wlr_foreign_toplevel handle the rest. */ struct output *output; - wl_list_for_each(output, &toplevel->view->server->outputs, link) { - if (view_on_output(toplevel->view, output)) { + wl_list_for_each(output, &wlr_toplevel->view->server->outputs, link) { + if (view_on_output(wlr_toplevel->view, output)) { wlr_foreign_toplevel_handle_v1_output_enter( - toplevel->wlr_toplevel.handle, output->wlr_output); + wlr_toplevel->handle, output->wlr_output); } else { wlr_foreign_toplevel_handle_v1_output_leave( - toplevel->wlr_toplevel.handle, output->wlr_output); + wlr_toplevel->handle, output->wlr_output); } } } @@ -151,84 +148,55 @@ handle_new_outputs(struct wl_listener *listener, void *data) static void handle_maximized(struct wl_listener *listener, void *data) { - struct foreign_toplevel *toplevel = - wl_container_of(listener, toplevel, wlr_toplevel.on_view.maximized); - assert(toplevel->wlr_toplevel.handle); + struct wlr_foreign_toplevel *wlr_toplevel = + wl_container_of(listener, wlr_toplevel, on_view.maximized); + assert(wlr_toplevel->handle); - wlr_foreign_toplevel_handle_v1_set_maximized( - toplevel->wlr_toplevel.handle, - toplevel->view->maximized == VIEW_AXIS_BOTH); + wlr_foreign_toplevel_handle_v1_set_maximized(wlr_toplevel->handle, + wlr_toplevel->view->maximized == VIEW_AXIS_BOTH); } static void handle_minimized(struct wl_listener *listener, void *data) { - struct foreign_toplevel *toplevel = - wl_container_of(listener, toplevel, wlr_toplevel.on_view.minimized); - assert(toplevel->wlr_toplevel.handle); + struct wlr_foreign_toplevel *wlr_toplevel = + wl_container_of(listener, wlr_toplevel, on_view.minimized); + assert(wlr_toplevel->handle); - wlr_foreign_toplevel_handle_v1_set_minimized( - toplevel->wlr_toplevel.handle, toplevel->view->minimized); + wlr_foreign_toplevel_handle_v1_set_minimized(wlr_toplevel->handle, + wlr_toplevel->view->minimized); } static void handle_fullscreened(struct wl_listener *listener, void *data) { - struct foreign_toplevel *toplevel = - wl_container_of(listener, toplevel, wlr_toplevel.on_view.fullscreened); - assert(toplevel->wlr_toplevel.handle); + struct wlr_foreign_toplevel *wlr_toplevel = + wl_container_of(listener, wlr_toplevel, on_view.fullscreened); + assert(wlr_toplevel->handle); - wlr_foreign_toplevel_handle_v1_set_fullscreen( - toplevel->wlr_toplevel.handle, toplevel->view->fullscreen); + wlr_foreign_toplevel_handle_v1_set_fullscreen(wlr_toplevel->handle, + wlr_toplevel->view->fullscreen); } static void handle_activated(struct wl_listener *listener, void *data) { - struct foreign_toplevel *toplevel = - wl_container_of(listener, toplevel, wlr_toplevel.on_view.activated); - assert(toplevel->wlr_toplevel.handle); + struct wlr_foreign_toplevel *wlr_toplevel = + wl_container_of(listener, wlr_toplevel, on_view.activated); + assert(wlr_toplevel->handle); bool *activated = data; - wlr_foreign_toplevel_handle_v1_set_activated( - toplevel->wlr_toplevel.handle, *activated); -} - -/* Internal signals */ -static void -handle_toplevel_parent(struct wl_listener *listener, void *data) -{ - struct wlr_foreign_toplevel *wlr_toplevel = wl_container_of( - listener, wlr_toplevel, on_foreign_toplevel.toplevel_parent); - struct foreign_toplevel *parent = data; - - assert(wlr_toplevel->handle); - - /* The wlroots wlr-foreign-toplevel impl ensures parent is reset to NULL on destroy */ - wlr_foreign_toplevel_handle_v1_set_parent(wlr_toplevel->handle, parent - ? parent->wlr_toplevel.handle - : NULL); -} - -static void -handle_toplevel_destroy(struct wl_listener *listener, void *data) -{ - struct wlr_foreign_toplevel *wlr_toplevel = wl_container_of( - listener, wlr_toplevel, on_foreign_toplevel.toplevel_destroy); - - assert(wlr_toplevel->handle); - /* invokes handle_handle_destroy() which does more cleanup */ - wlr_foreign_toplevel_handle_v1_destroy(wlr_toplevel->handle); + wlr_foreign_toplevel_handle_v1_set_activated(wlr_toplevel->handle, + *activated); } /* Internal API */ void -wlr_foreign_toplevel_init(struct foreign_toplevel *toplevel) +wlr_foreign_toplevel_init(struct wlr_foreign_toplevel *wlr_toplevel, + struct view *view) { - struct wlr_foreign_toplevel *wlr_toplevel = &toplevel->wlr_toplevel; - struct view *view = toplevel->view; - assert(view->server->foreign_toplevel_manager); + wlr_toplevel->view = view; wlr_toplevel->handle = wlr_foreign_toplevel_handle_v1_create( view->server->foreign_toplevel_manager); @@ -262,8 +230,29 @@ wlr_foreign_toplevel_init(struct foreign_toplevel *toplevel) CONNECT_SIGNAL(view, &wlr_toplevel->on_view, minimized); CONNECT_SIGNAL(view, &wlr_toplevel->on_view, fullscreened); CONNECT_SIGNAL(view, &wlr_toplevel->on_view, activated); - - /* Internal signals */ - CONNECT_SIGNAL(toplevel, &wlr_toplevel->on_foreign_toplevel, toplevel_parent); - CONNECT_SIGNAL(toplevel, &wlr_toplevel->on_foreign_toplevel, toplevel_destroy); +} + +void +wlr_foreign_toplevel_set_parent(struct wlr_foreign_toplevel *wlr_toplevel, + struct wlr_foreign_toplevel *parent) +{ + if (!wlr_toplevel->handle) { + return; + } + + /* The wlroots wlr-foreign-toplevel impl ensures parent is reset to NULL on destroy */ + wlr_foreign_toplevel_handle_v1_set_parent(wlr_toplevel->handle, + parent ? parent->handle : NULL); +} + +void +wlr_foreign_toplevel_finish(struct wlr_foreign_toplevel *wlr_toplevel) +{ + if (!wlr_toplevel->handle) { + return; + } + + /* invokes handle_handle_destroy() which does more cleanup */ + wlr_foreign_toplevel_handle_v1_destroy(wlr_toplevel->handle); + assert(!wlr_toplevel->handle); } diff --git a/src/view-impl-common.c b/src/view-impl-common.c index 9e2c1243..8e67d0c5 100644 --- a/src/view-impl-common.c +++ b/src/view-impl-common.c @@ -1,9 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only /* view-impl-common.c: common code for shell view->impl functions */ #include "view-impl-common.h" -#include #include -#include "foreign-toplevel.h" +#include "foreign-toplevel/foreign.h" #include "labwc.h" #include "view.h" #include "window-rules.h" diff --git a/src/view.c b/src/view.c index 33b92444..d86b06c5 100644 --- a/src/view.c +++ b/src/view.c @@ -14,7 +14,7 @@ #include "common/match.h" #include "common/mem.h" #include "common/scene-helpers.h" -#include "foreign-toplevel.h" +#include "foreign-toplevel/foreign.h" #include "input/keyboard.h" #include "labwc.h" #include "menu/menu.h" diff --git a/src/xdg.c b/src/xdg.c index e54fdea0..a67a6aea 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -10,7 +10,7 @@ #include "common/macros.h" #include "common/mem.h" #include "decorations.h" -#include "foreign-toplevel.h" +#include "foreign-toplevel/foreign.h" #include "labwc.h" #include "menu/menu.h" #include "node.h" diff --git a/src/xwayland.c b/src/xwayland.c index e793916f..c7954517 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -13,7 +13,7 @@ #include "common/mem.h" #include "config/rcxml.h" #include "config/session.h" -#include "foreign-toplevel.h" +#include "foreign-toplevel/foreign.h" #include "labwc.h" #include "node.h" #include "output.h" From 418f9cb05907be6209a36ed74d5749ce853b2e35 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Thu, 7 Aug 2025 17:45:30 +0900 Subject: [PATCH 074/248] ime: fix segfault when IME is killed Fixes up e530f43. When IME (e.g. fcitx5) is killed, relay->input_method is destroyed and then relay->input_method->keyboard_grab is destroyed, which causes null pointer dereference and crashes labwc. Possible solutions are: - Let wlroots keep emitting keyboard grab as `data` from keyboard grab's destroy handler just like before - Let wlroots destroy keyboard grab before input method - Let compositor store keyboard grab as relay->keyboard_grab But let's just revert the change in e530f43 for now. --- src/input/ime.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/input/ime.c b/src/input/ime.c index cf8cd2d9..f6e0ac99 100644 --- a/src/input/ime.c +++ b/src/input/ime.c @@ -304,8 +304,8 @@ handle_keyboard_grab_destroy(struct wl_listener *listener, void *data) { struct input_method_relay *relay = wl_container_of(listener, relay, keyboard_grab_destroy); - struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = - relay->input_method->keyboard_grab; + struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data; + assert(keyboard_grab); wl_list_remove(&relay->keyboard_grab_destroy.link); From 32e308b5d512e501097d7ba5871345f0f7710917 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Sat, 9 Aug 2025 04:48:39 +0200 Subject: [PATCH 075/248] scaled-icon-buffer: prevent accidental downcasting of scale This causes blurry icons on non-integer scales and triggers an assert within cairo when using a output scale < 1. Fixes: #2983 --- src/common/scaled-icon-buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/scaled-icon-buffer.c b/src/common/scaled-icon-buffer.c index 62cb301e..49387bac 100644 --- a/src/common/scaled-icon-buffer.c +++ b/src/common/scaled-icon-buffer.c @@ -45,7 +45,7 @@ choose_best_icon_buffer(struct scaled_icon_buffer *self, int icon_size, double s } static struct lab_data_buffer * -img_to_buffer(struct lab_img *img, int width, int height, int scale) +img_to_buffer(struct lab_img *img, int width, int height, double scale) { struct lab_data_buffer *buffer = lab_img_render(img, width, height, scale); lab_img_destroy(img); From fba73a003652917799dd3d26165a52ea686b7f93 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Fri, 20 Dec 2024 10:14:32 +0100 Subject: [PATCH 076/248] action: add support for in 'If' actions ...and allow If Action without activator view. For example: Also revert the change in b9c84f9 that branch is always taken when no window is focused. Co-Authored-by: johanmalm Co-Authored-by: tokyo4j --- include/action.h | 3 ++ src/action.c | 135 +++++++++++++++++++++++++++++++++++++++++++---- src/server.c | 11 ++-- 3 files changed, 135 insertions(+), 14 deletions(-) diff --git a/include/action.h b/include/action.h index b8a271e9..cb88d0d5 100644 --- a/include/action.h +++ b/include/action.h @@ -3,6 +3,7 @@ #define LABWC_ACTION_H #include +#include #include struct view; @@ -47,6 +48,8 @@ bool actions_contain_toggle_keybinds(struct wl_list *action_list); void actions_run(struct view *activator, struct server *server, struct wl_list *actions, struct cursor_context *ctx); +bool action_check_prompt_result(pid_t pid, int exit_code); + void action_free(struct action *action); void action_list_free(struct wl_list *action_list); diff --git a/src/action.c b/src/action.c index 8d5eb5ac..657810bf 100644 --- a/src/action.c +++ b/src/action.c @@ -493,6 +493,11 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char goto cleanup; } break; + case ACTION_TYPE_IF: + if (!strcmp(argument, "message.prompt")) { + action_arg_add_str(action, "message.prompt", content); + } + goto cleanup; } wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s'", @@ -775,16 +780,115 @@ view_for_action(struct view *activator, struct server *server, } } +struct action_prompt { + /* Set when created */ + struct server *server; + struct action *action; + struct view *view; + + /* Set when executed */ + pid_t pid; + + struct { + struct wl_listener destroy; + } on_view; + struct wl_list link; +}; + +static struct wl_list prompts = WL_LIST_INIT(&prompts); + +static void +action_prompt_destroy(struct action_prompt *prompt) +{ + wl_list_remove(&prompt->on_view.destroy.link); + wl_list_remove(&prompt->link); + free(prompt); +} + +static void +handle_view_destroy(struct wl_listener *listener, void *data) +{ + struct action_prompt *prompt = wl_container_of(listener, prompt, on_view.destroy); + wl_list_remove(&prompt->on_view.destroy.link); + wl_list_init(&prompt->on_view.destroy.link); + prompt->view = NULL; +} + +static void +action_prompt_create(struct view *view, struct server *server, struct action *action) +{ + char *command = strdup_printf("labnag -m \"%s\" -Z \"%s\" : -Z \"%s\" :", + action_get_str(action, "message.prompt", "Choose wisely"), + _("Yes"), _("No")); + + int pipe_fd; + pid_t prompt_pid = spawn_piped(command, &pipe_fd); + if (prompt_pid < 0) { + wlr_log(WLR_ERROR, "Failed to create action prompt"); + goto cleanup; + } + /* FIXME: closing stdout might confuse clients */ + close(pipe_fd); + + struct action_prompt *prompt = znew(*prompt); + prompt->server = server; + prompt->action = action; + prompt->view = view; + prompt->pid = prompt_pid; + if (view) { + prompt->on_view.destroy.notify = handle_view_destroy; + wl_signal_add(&view->events.destroy, &prompt->on_view.destroy); + } else { + /* Allows removing during destroy */ + wl_list_init(&prompt->on_view.destroy.link); + } + + wl_list_insert(&prompts, &prompt->link); + +cleanup: + free(command); +} + +bool +action_check_prompt_result(pid_t pid, int exit_code) +{ + struct action_prompt *prompt, *tmp; + wl_list_for_each_safe(prompt, tmp, &prompts, link) { + if (prompt->pid != pid) { + continue; + } + + wlr_log(WLR_INFO, "Found pending prompt for exit code %d", exit_code); + struct wl_list *actions = NULL; + if (exit_code == 0) { + wlr_log(WLR_INFO, "Selected the 'then' branch"); + actions = action_get_actionlist(prompt->action, "then"); + } else { + wlr_log(WLR_INFO, "Selected the 'else' branch"); + actions = action_get_actionlist(prompt->action, "else"); + } + if (actions) { + wlr_log(WLR_INFO, "Running actions"); + actions_run(prompt->view, prompt->server, + actions, /*cursor_ctx*/ NULL); + } else { + wlr_log(WLR_INFO, "No actions for selected branch"); + } + action_prompt_destroy(prompt); + return true; + } + return false; +} + static bool match_queries(struct view *view, struct action *action) { + assert(view); + struct wl_list *queries = action_get_querylist(action, "query"); if (!queries) { return true; } - if (!view) { - return false; - } /* All queries are OR'ed */ struct view_query *query; @@ -1205,14 +1309,23 @@ run_action(struct view *view, struct server *server, struct action *action, break; } case ACTION_TYPE_IF: { - struct wl_list *actions; - if (match_queries(view, action)) { - actions = action_get_actionlist(action, "then"); - } else { - actions = action_get_actionlist(action, "else"); - } - if (actions) { - actions_run(view, server, actions, ctx); + /* At least one of the queries was matched or there was no query */ + if (action_get_str(action, "message.prompt", NULL)) { + /* + * We delay the selection and execution of the + * branch until we get a response from the user. + */ + action_prompt_create(view, server, action); + } else if (view) { + struct wl_list *actions; + if (match_queries(view, action)) { + actions = action_get_actionlist(action, "then"); + } else { + actions = action_get_actionlist(action, "else"); + } + if (actions) { + actions_run(view, server, actions, ctx); + } } break; } diff --git a/src/server.c b/src/server.c index a9e021dc..e8befd55 100644 --- a/src/server.c +++ b/src/server.c @@ -46,6 +46,7 @@ #endif #include "drm-lease-v1-protocol.h" +#include "action.h" #include "common/macros.h" #include "common/scaled-scene-buffer.h" #include "config/rcxml.h" @@ -160,9 +161,11 @@ handle_sigchld(int signal, void *data) const char *signame; switch (info.si_code) { case CLD_EXITED: - wlr_log(info.si_status == 0 ? WLR_DEBUG : WLR_ERROR, - "spawned child %ld exited with %d", - (long)info.si_pid, info.si_status); + if (!action_check_prompt_result(info.si_pid, info.si_status)) { + wlr_log(info.si_status == 0 ? WLR_DEBUG : WLR_ERROR, + "spawned child %ld exited with %d", + (long)info.si_pid, info.si_status); + } break; case CLD_KILLED: case CLD_DUMPED: @@ -171,6 +174,8 @@ handle_sigchld(int signal, void *data) "spawned child %ld terminated with signal %d (%s)", (long)info.si_pid, info.si_status, signame ? signame : "unknown"); + /* Allow cleanup of killed prompt */ + action_check_prompt_result(info.si_pid, -info.si_status); break; default: wlr_log(WLR_ERROR, From 6fd14987dd56cdcbb0e12b05a1273f315f3bd51e Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Mon, 28 Apr 2025 20:33:50 +0100 Subject: [PATCH 077/248] build: refactor in preparation for man page not beginning with 'labwc-' No functional change intended. --- docs/meson.build | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/docs/meson.build b/docs/meson.build index a6676784..87acdab1 100644 --- a/docs/meson.build +++ b/docs/meson.build @@ -1,16 +1,15 @@ scdoc = find_program('scdoc', required: get_option('man-pages')) if scdoc.found() - sections = [ - '.1', - '-actions.5', - '-config.5', - '-menu.5', - '-theme.5', + manpages = [ + 'labwc.1', + 'labwc-actions.5', + 'labwc-config.5', + 'labwc-menu.5', + 'labwc-theme.5', ] - foreach s : sections - markdown = 'labwc' + s + '.scd' - manpage = 'labwc' + s + foreach manpage : manpages + markdown = manpage + '.scd' custom_target( manpage, input: markdown, @@ -19,7 +18,7 @@ if scdoc.found() feed: true, capture: true, install: true, - install_dir: join_paths(get_option('mandir'), 'man' + s.split('.')[-1]) + install_dir: join_paths(get_option('mandir'), 'man' + manpage.split('.')[-1]) ) endforeach endif From c63d35c9420bf0bdb7849dc00a2d679e9ac57524 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Mon, 28 Apr 2025 20:38:19 +0100 Subject: [PATCH 078/248] Add labnag Based on swaynag (https://github.com/swaywm/sway/tree/master/swaynag) Copied at commit: https://github.com/swaywm/sway/commit/03483ff3707a358d935e451d39748e58c205ce8a Contains the following modifiations: - Some functional changes including: - Disable exclusive-zone by default (Written-by: @Consolatis) and add command line option -x|--exclusive-zone - Add close timeout (Written-by: @Consolatis) and -t|--timeout option - Use index of button (from right-to-left) for exit code - Disable reading from config file and remove associated --type option - Refactoring including: - Use wlr_log() instead of the log.{c,h} functions - Use wl_list instead of sway's list.c implementation - In the pango wrapper functions, use glib's g_strdup_vprintf() rather than the original stringop.c functions - Align with labwc coding style to pass checkpatch.pl - Re-licenced from MIT to GPL-2.0, and add Copyright notices for original authors v2 - Remove option -s|--dismiss-button and the default "X" button. To get such a button, "-Z X :" - Remove options -b and -z because there is no requirement to run in a terminal. - Remove *-no-terminal from options --button and --button-dismiss because commands are now always run directly without a terminal. v3 - Allow -B/-Z options without action-argument - Invert button order of -B/-Z so that `labnag -m foo -Z x -Z y -Z z` results in three buttons with "x" furthest to the left, and "z" on the right (rather than the other way around). - Use signalfd() to prevent race conditions on SIGTERM v4 - Limit number of stdin lines to 200 to avoid hogging CPU Co-Authored-by: tokyo4j --- clients/labnag.c | 1652 +++++++++++++++++++++++++++++++++++++++++ clients/meson.build | 47 ++ clients/pool-buffer.c | 145 ++++ clients/pool-buffer.h | 30 + docs/labnag.1.scd | 109 +++ docs/meson.build | 1 + meson.build | 1 + scripts/check | 2 +- 8 files changed, 1986 insertions(+), 1 deletion(-) create mode 100644 clients/labnag.c create mode 100644 clients/meson.build create mode 100644 clients/pool-buffer.c create mode 100644 clients/pool-buffer.h create mode 100644 docs/labnag.1.scd diff --git a/clients/labnag.c b/clients/labnag.c new file mode 100644 index 00000000..514ac864 --- /dev/null +++ b/clients/labnag.c @@ -0,0 +1,1652 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Based on https://github.com/swaywm/sway/tree/master/swaynag + * + * Copyright (C) 2016-2017 Drew DeVault + * Copyright (C) 2025 Johan Malm + */ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pool-buffer.h" +#include "cursor-shape-v1-client-protocol.h" +#include "wlr-layer-shell-unstable-v1-client-protocol.h" + +#define LABNAG_MAX_HEIGHT 500 +#define LAB_EXIT_FAILURE 255 +#define LAB_EXIT_SUCCESS 0 + +struct conf { + PangoFontDescription *font_description; + char *output; + uint32_t anchors; + int32_t layer; /* enum zwlr_layer_shell_v1_layer or -1 if unset */ + + /* Colors */ + uint32_t button_text; + uint32_t button_background; + uint32_t details_background; + uint32_t background; + uint32_t text; + uint32_t border; + uint32_t border_bottom; + + /* Sizing */ + ssize_t bar_border_thickness; + ssize_t message_padding; + ssize_t details_border_thickness; + ssize_t button_border_thickness; + ssize_t button_gap; + ssize_t button_gap_close; + ssize_t button_margin_right; + ssize_t button_padding; +}; + +struct nag; + +struct pointer { + struct wl_pointer *pointer; + uint32_t serial; + struct wl_cursor_theme *cursor_theme; + struct wl_cursor_image *cursor_image; + struct wl_surface *cursor_surface; + int x; + int y; +}; + +struct seat { + struct wl_seat *wl_seat; + uint32_t wl_name; + struct nag *nag; + struct pointer pointer; + struct wl_list link; /* nag.seats */ +}; + +struct output { + char *name; + struct wl_output *wl_output; + uint32_t wl_name; + uint32_t scale; + struct nag *nag; + struct wl_list link; /* nag.outputs */ +}; + +struct button { + char *text; + char *action; + int x; + int y; + int width; + int height; + bool expand; + bool dismiss; + struct wl_list link; +}; + +enum { + FD_WAYLAND, + FD_TIMER, + FD_SIGNAL, + + NR_FDS, +}; + +struct nag { + bool run_display; + + struct wl_display *display; + struct wl_compositor *compositor; + struct wl_seat *seat; + struct wl_shm *shm; + struct wl_list outputs; + struct wl_list seats; + struct output *output; + struct zwlr_layer_shell_v1 *layer_shell; + struct zwlr_layer_surface_v1 *layer_surface; + struct wp_cursor_shape_manager_v1 *cursor_shape_manager; + struct wl_surface *surface; + + uint32_t width; + uint32_t height; + int32_t scale; + struct pool_buffer buffers[2]; + struct pool_buffer *current_buffer; + + struct conf *conf; + char *message; + struct wl_list buttons; + struct pollfd pollfds[NR_FDS]; + + struct { + bool visible; + char *message; + char *details_text; + int close_timeout; + bool use_exclusive_zone; + + int x; + int y; + int width; + int height; + + int offset; + int visible_lines; + int total_lines; + struct button *button_details; + struct button button_up; + struct button button_down; + } details; +}; + +static int exit_status = LAB_EXIT_FAILURE; + +static void close_pollfd(struct pollfd *pollfd); + +static PangoLayout * +get_pango_layout(cairo_t *cairo, const PangoFontDescription *desc, + const char *text, double scale, bool markup) +{ + PangoLayout *layout = pango_cairo_create_layout(cairo); + pango_context_set_round_glyph_positions(pango_layout_get_context(layout), false); + + PangoAttrList *attrs; + if (markup) { + char *buf; + GError *error = NULL; + if (pango_parse_markup(text, -1, 0, &attrs, &buf, NULL, &error)) { + pango_layout_set_text(layout, buf, -1); + free(buf); + } else { + wlr_log(WLR_ERROR, "pango_parse_markup '%s' -> error %s", + text, error->message); + g_error_free(error); + markup = false; /* fallback to plain text */ + } + } + if (!markup) { + attrs = pango_attr_list_new(); + pango_layout_set_text(layout, text, -1); + } + + pango_attr_list_insert(attrs, pango_attr_scale_new(scale)); + pango_layout_set_font_description(layout, desc); + pango_layout_set_single_paragraph_mode(layout, 1); + pango_layout_set_attributes(layout, attrs); + pango_attr_list_unref(attrs); + return layout; +} + +static void +get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height, + int *baseline, double scale, bool markup, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + gchar *buf = g_strdup_vprintf(fmt, args); + va_end(args); + if (!buf) { + return; + } + + PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup); + pango_cairo_update_layout(cairo, layout); + pango_layout_get_pixel_size(layout, width, height); + if (baseline) { + *baseline = pango_layout_get_baseline(layout) / PANGO_SCALE; + } + g_object_unref(layout); + + g_free(buf); +} + +static void +render_text(cairo_t *cairo, const PangoFontDescription *desc, double scale, + bool markup, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + gchar *buf = g_strdup_vprintf(fmt, args); + va_end(args); + if (!buf) { + return; + } + + PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup); + cairo_font_options_t *fo = cairo_font_options_create(); + cairo_get_font_options(cairo, fo); + pango_cairo_context_set_font_options(pango_layout_get_context(layout), fo); + cairo_font_options_destroy(fo); + pango_cairo_update_layout(cairo, layout); + pango_cairo_show_layout(cairo, layout); + g_object_unref(layout); + + g_free(buf); +} + +static void +cairo_set_source_u32(cairo_t *cairo, uint32_t color) +{ + cairo_set_source_rgba(cairo, + (color >> (3*8) & 0xFF) / 255.0, + (color >> (2*8) & 0xFF) / 255.0, + (color >> (1*8) & 0xFF) / 255.0, + (color >> (0*8) & 0xFF) / 255.0); +} + +static uint32_t +render_message(cairo_t *cairo, struct nag *nag) +{ + int text_width, text_height; + get_text_size(cairo, nag->conf->font_description, &text_width, + &text_height, NULL, 1, true, "%s", nag->message); + + int padding = nag->conf->message_padding; + + uint32_t ideal_height = text_height + padding * 2; + uint32_t ideal_surface_height = ideal_height; + if (nag->height < ideal_surface_height) { + return ideal_surface_height; + } + + cairo_set_source_u32(cairo, nag->conf->text); + cairo_move_to(cairo, padding, (int)(ideal_height - text_height) / 2); + render_text(cairo, nag->conf->font_description, 1, false, + "%s", nag->message); + + return ideal_surface_height; +} + +static void +render_details_scroll_button(cairo_t *cairo, struct nag *nag, + struct button *button) +{ + int text_width, text_height; + get_text_size(cairo, nag->conf->font_description, &text_width, + &text_height, NULL, 1, true, "%s", button->text); + + int border = nag->conf->button_border_thickness; + int padding = nag->conf->button_padding; + + cairo_set_source_u32(cairo, nag->conf->details_background); + cairo_rectangle(cairo, button->x, button->y, + button->width, button->height); + cairo_fill(cairo); + + cairo_set_source_u32(cairo, nag->conf->button_background); + cairo_rectangle(cairo, button->x + border, button->y + border, + button->width - (border * 2), + button->height - (border * 2)); + cairo_fill(cairo); + + cairo_set_source_u32(cairo, nag->conf->button_text); + cairo_move_to(cairo, button->x + border + padding, + button->y + border + (button->height - text_height) / 2); + render_text(cairo, nag->conf->font_description, 1, true, + "%s", button->text); +} + +static int +get_detailed_scroll_button_width(cairo_t *cairo, struct nag *nag) +{ + int up_width, down_width, temp_height; + get_text_size(cairo, nag->conf->font_description, &up_width, &temp_height, + NULL, 1, true, "%s", nag->details.button_up.text); + get_text_size(cairo, nag->conf->font_description, &down_width, &temp_height, + NULL, 1, true, "%s", nag->details.button_down.text); + + int text_width = up_width > down_width ? up_width : down_width; + int border = nag->conf->button_border_thickness; + int padding = nag->conf->button_padding; + + return text_width + border * 2 + padding * 2; +} + +static uint32_t +render_detailed(cairo_t *cairo, struct nag *nag, uint32_t y) +{ + uint32_t width = nag->width; + + int border = nag->conf->details_border_thickness; + int padding = nag->conf->message_padding; + int decor = padding + border; + + nag->details.x = decor; + nag->details.y = y + decor; + nag->details.width = width - decor * 2; + + PangoLayout *layout = get_pango_layout(cairo, nag->conf->font_description, + nag->details.message, 1, false); + pango_layout_set_width(layout, + (nag->details.width - padding * 2) * PANGO_SCALE); + pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); + pango_layout_set_single_paragraph_mode(layout, false); + pango_cairo_update_layout(cairo, layout); + nag->details.total_lines = pango_layout_get_line_count(layout); + + PangoLayoutLine *line; + line = pango_layout_get_line_readonly(layout, nag->details.offset); + gint offset = line->start_index; + const char *text = pango_layout_get_text(layout); + pango_layout_set_text(layout, text + offset, strlen(text) - offset); + + int text_width, text_height; + pango_cairo_update_layout(cairo, layout); + pango_layout_get_pixel_size(layout, &text_width, &text_height); + + bool show_buttons = nag->details.offset > 0; + int button_width = get_detailed_scroll_button_width(cairo, nag); + if (show_buttons) { + nag->details.width -= button_width; + pango_layout_set_width(layout, + (nag->details.width - padding * 2) * PANGO_SCALE); + } + + uint32_t ideal_height; + do { + ideal_height = nag->details.y + text_height + decor + padding * 2; + if (ideal_height > LABNAG_MAX_HEIGHT) { + ideal_height = LABNAG_MAX_HEIGHT; + + if (!show_buttons) { + show_buttons = true; + nag->details.width -= button_width; + pango_layout_set_width(layout, + (nag->details.width - padding * 2) * PANGO_SCALE); + } + } + + nag->details.height = ideal_height - nag->details.y - decor; + pango_layout_set_height(layout, + (nag->details.height - padding * 2) * PANGO_SCALE); + pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); + pango_cairo_update_layout(cairo, layout); + pango_layout_get_pixel_size(layout, &text_width, &text_height); + } while (text_height != (nag->details.height - padding * 2)); + + nag->details.visible_lines = pango_layout_get_line_count(layout); + + if (show_buttons) { + nag->details.button_up.x = nag->details.x + nag->details.width; + nag->details.button_up.y = nag->details.y; + nag->details.button_up.width = button_width; + nag->details.button_up.height = nag->details.height / 2; + render_details_scroll_button(cairo, nag, &nag->details.button_up); + + nag->details.button_down.x = nag->details.x + nag->details.width; + nag->details.button_down.y = + nag->details.button_up.y + nag->details.button_up.height; + nag->details.button_down.width = button_width; + nag->details.button_down.height = nag->details.height / 2; + render_details_scroll_button(cairo, nag, &nag->details.button_down); + } + + cairo_set_source_u32(cairo, nag->conf->details_background); + cairo_rectangle(cairo, nag->details.x, nag->details.y, + nag->details.width, nag->details.height); + cairo_fill(cairo); + + cairo_move_to(cairo, nag->details.x + padding, nag->details.y + padding); + cairo_set_source_u32(cairo, nag->conf->text); + pango_cairo_show_layout(cairo, layout); + g_object_unref(layout); + + return ideal_height; +} + +static uint32_t +render_button(cairo_t *cairo, struct nag *nag, struct button *button, int *x) +{ + int text_width, text_height; + get_text_size(cairo, nag->conf->font_description, &text_width, + &text_height, NULL, 1, true, "%s", button->text); + + int border = nag->conf->button_border_thickness; + int padding = nag->conf->button_padding; + + uint32_t ideal_height = text_height + padding * 2 + border * 2; + uint32_t ideal_surface_height = ideal_height; + if (nag->height < ideal_surface_height) { + return ideal_surface_height; + } + + button->x = *x - border - text_width - padding * 2 + 1; + button->y = (int)(ideal_height - text_height) / 2 - padding + 1; + button->width = text_width + padding * 2; + button->height = text_height + padding * 2; + + cairo_set_source_u32(cairo, nag->conf->border); + cairo_rectangle(cairo, button->x - border, button->y - border, + button->width + border * 2, button->height + border * 2); + cairo_fill(cairo); + + cairo_set_source_u32(cairo, nag->conf->button_background); + cairo_rectangle(cairo, button->x, button->y, + button->width, button->height); + cairo_fill(cairo); + + cairo_set_source_u32(cairo, nag->conf->button_text); + cairo_move_to(cairo, button->x + padding, button->y + padding); + render_text(cairo, nag->conf->font_description, 1, true, + "%s", button->text); + + *x = button->x - border; + + return ideal_surface_height; +} + +static uint32_t +render_to_cairo(cairo_t *cairo, struct nag *nag) +{ + uint32_t max_height = 0; + + cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); + cairo_set_source_u32(cairo, nag->conf->background); + cairo_paint(cairo); + + uint32_t h = render_message(cairo, nag); + max_height = h > max_height ? h : max_height; + + int x = nag->width - nag->conf->button_margin_right; + x -= nag->conf->button_gap_close; + + struct button *button; + wl_list_for_each(button, &nag->buttons, link) { + h = render_button(cairo, nag, button, &x); + max_height = h > max_height ? h : max_height; + x -= nag->conf->button_gap; + } + + if (nag->details.visible) { + h = render_detailed(cairo, nag, max_height); + max_height = h > max_height ? h : max_height; + } + + int border = nag->conf->bar_border_thickness; + if (max_height > nag->height) { + max_height += border; + } + cairo_set_source_u32(cairo, nag->conf->border_bottom); + cairo_rectangle(cairo, 0, + nag->height - border, + nag->width, + border); + cairo_fill(cairo); + + return max_height; +} + +static void +render_frame(struct nag *nag) +{ + if (!nag->run_display) { + return; + } + + cairo_surface_t *recorder = cairo_recording_surface_create( + CAIRO_CONTENT_COLOR_ALPHA, NULL); + cairo_t *cairo = cairo_create(recorder); + cairo_scale(cairo, nag->scale, nag->scale); + cairo_save(cairo); + cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); + cairo_paint(cairo); + cairo_restore(cairo); + uint32_t height = render_to_cairo(cairo, nag); + if (height != nag->height) { + zwlr_layer_surface_v1_set_size(nag->layer_surface, 0, height); + if (nag->details.use_exclusive_zone) { + zwlr_layer_surface_v1_set_exclusive_zone( + nag->layer_surface, height); + } + wl_surface_commit(nag->surface); + wl_display_roundtrip(nag->display); + } else { + nag->current_buffer = get_next_buffer(nag->shm, + nag->buffers, + nag->width * nag->scale, + nag->height * nag->scale); + if (!nag->current_buffer) { + wlr_log(WLR_DEBUG, "Failed to get buffer. Skipping frame."); + goto cleanup; + } + + cairo_t *shm = nag->current_buffer->cairo; + cairo_save(shm); + cairo_set_operator(shm, CAIRO_OPERATOR_CLEAR); + cairo_paint(shm); + cairo_restore(shm); + cairo_set_source_surface(shm, recorder, 0.0, 0.0); + cairo_paint(shm); + + wl_surface_set_buffer_scale(nag->surface, nag->scale); + wl_surface_attach(nag->surface, + nag->current_buffer->buffer, 0, 0); + wl_surface_damage(nag->surface, 0, 0, + nag->width, nag->height); + wl_surface_commit(nag->surface); + wl_display_roundtrip(nag->display); + } + +cleanup: + cairo_surface_destroy(recorder); + cairo_destroy(cairo); +} + +static void +seat_destroy(struct seat *seat) +{ + if (seat->pointer.cursor_theme) { + wl_cursor_theme_destroy(seat->pointer.cursor_theme); + } + if (seat->pointer.pointer) { + wl_pointer_destroy(seat->pointer.pointer); + } + wl_seat_destroy(seat->wl_seat); + wl_list_remove(&seat->link); + free(seat); +} + +static void +nag_destroy(struct nag *nag) +{ + nag->run_display = false; + + struct button *button, *next; + wl_list_for_each_safe(button, next, &nag->buttons, link) { + wl_list_remove(&button->link); + free(button); + } + free(nag->details.message); + + pango_font_description_free(nag->conf->font_description); + + if (nag->layer_surface) { + zwlr_layer_surface_v1_destroy(nag->layer_surface); + } + + if (nag->surface) { + wl_surface_destroy(nag->surface); + } + + if (nag->layer_shell) { + zwlr_layer_shell_v1_destroy(nag->layer_shell); + } + + if (nag->cursor_shape_manager) { + wp_cursor_shape_manager_v1_destroy(nag->cursor_shape_manager); + } + + struct seat *seat, *tmpseat; + wl_list_for_each_safe(seat, tmpseat, &nag->seats, link) { + seat_destroy(seat); + } + + destroy_buffer(&nag->buffers[0]); + destroy_buffer(&nag->buffers[1]); + + if (nag->outputs.prev || nag->outputs.next) { + struct output *output, *temp; + wl_list_for_each_safe(output, temp, &nag->outputs, link) { + wl_output_destroy(output->wl_output); + free(output->name); + wl_list_remove(&output->link); + free(output); + }; + } + + if (nag->compositor) { + wl_compositor_destroy(nag->compositor); + } + + if (nag->shm) { + wl_shm_destroy(nag->shm); + } + + if (nag->display) { + wl_display_disconnect(nag->display); + } + pango_cairo_font_map_set_default(NULL); + + close_pollfd(&nag->pollfds[FD_TIMER]); + close_pollfd(&nag->pollfds[FD_SIGNAL]); +} + +static void +button_execute(struct nag *nag, struct button *button) +{ + wlr_log(WLR_DEBUG, "Executing [%s]: %s", button->text, button->action); + if (button->expand) { + nag->details.visible = !nag->details.visible; + render_frame(nag); + return; + } + if (button->dismiss) { + nag->run_display = false; + } + if (button->action) { + pid_t pid = fork(); + if (pid < 0) { + wlr_log_errno(WLR_DEBUG, "Failed to fork"); + return; + } else if (pid == 0) { + /* + * Child process. Will be used to prevent zombie + * processes + */ + pid = fork(); + if (pid < 0) { + wlr_log_errno(WLR_DEBUG, "Failed to fork"); + return; + } else if (pid == 0) { + /* + * Child of the child. Will be reparented to the + * init process + */ + execlp("sh", "sh", "-c", button->action, NULL); + wlr_log_errno(WLR_DEBUG, "execlp failed"); + _exit(LAB_EXIT_FAILURE); + } + _exit(EXIT_SUCCESS); + } + + if (waitpid(pid, NULL, 0) < 0) { + wlr_log_errno(WLR_DEBUG, "waitpid failed"); + } + } +} + +static void +layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, + uint32_t serial, uint32_t width, uint32_t height) +{ + struct nag *nag = data; + nag->width = width; + nag->height = height; + zwlr_layer_surface_v1_ack_configure(surface, serial); + render_frame(nag); +} + +static void +layer_surface_closed(void *data, struct zwlr_layer_surface_v1 *surface) +{ + struct nag *nag = data; + nag_destroy(nag); +} + +static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { + .configure = layer_surface_configure, + .closed = layer_surface_closed, +}; + +static void +surface_enter(void *data, struct wl_surface *surface, struct wl_output *output) +{ + struct nag *nag = data; + struct output *nag_output; + wl_list_for_each(nag_output, &nag->outputs, link) { + if (nag_output->wl_output == output) { + wlr_log(WLR_DEBUG, "Surface enter on output %s", + nag_output->name); + nag->output = nag_output; + nag->scale = nag->output->scale; + render_frame(nag); + break; + } + } +} + +static void +surface_leave(void *data, struct wl_surface *wl_surface, struct wl_output *output) +{ + /* nop */ +} + +static const struct wl_surface_listener surface_listener = { + .enter = surface_enter, + .leave = surface_leave, +}; + +static void +update_cursor(struct seat *seat) +{ + struct pointer *pointer = &seat->pointer; + struct nag *nag = seat->nag; + if (pointer->cursor_theme) { + wl_cursor_theme_destroy(pointer->cursor_theme); + } + const char *cursor_theme = getenv("XCURSOR_THEME"); + unsigned int cursor_size = 24; + const char *env_cursor_size = getenv("XCURSOR_SIZE"); + if (env_cursor_size && *env_cursor_size) { + errno = 0; + char *end; + unsigned int size = strtoul(env_cursor_size, &end, 10); + if (!*end && errno == 0) { + cursor_size = size; + } + } + pointer->cursor_theme = wl_cursor_theme_load( + cursor_theme, cursor_size * nag->scale, nag->shm); + if (!pointer->cursor_theme) { + wlr_log(WLR_ERROR, "Failed to load cursor theme"); + return; + } + struct wl_cursor *cursor = + wl_cursor_theme_get_cursor(pointer->cursor_theme, "default"); + if (!cursor) { + wlr_log(WLR_ERROR, "Failed to get default cursor from theme"); + return; + } + pointer->cursor_image = cursor->images[0]; + wl_surface_set_buffer_scale(pointer->cursor_surface, + nag->scale); + wl_surface_attach(pointer->cursor_surface, + wl_cursor_image_get_buffer(pointer->cursor_image), 0, 0); + wl_pointer_set_cursor(pointer->pointer, pointer->serial, + pointer->cursor_surface, + pointer->cursor_image->hotspot_x / nag->scale, + pointer->cursor_image->hotspot_y / nag->scale); + wl_surface_damage_buffer(pointer->cursor_surface, 0, 0, + INT32_MAX, INT32_MAX); + wl_surface_commit(pointer->cursor_surface); +} + +static void +update_all_cursors(struct nag *nag) +{ + struct seat *seat; + wl_list_for_each(seat, &nag->seats, link) { + if (seat->pointer.pointer) { + update_cursor(seat); + } + } +} + +static void +wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, + struct wl_surface *surface, wl_fixed_t surface_x, + wl_fixed_t surface_y) +{ + struct seat *seat = data; + + struct pointer *pointer = &seat->pointer; + pointer->x = wl_fixed_to_int(surface_x); + pointer->y = wl_fixed_to_int(surface_y); + + if (seat->nag->cursor_shape_manager) { + struct wp_cursor_shape_device_v1 *device = + wp_cursor_shape_manager_v1_get_pointer( + seat->nag->cursor_shape_manager, wl_pointer); + wp_cursor_shape_device_v1_set_shape(device, serial, + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT); + wp_cursor_shape_device_v1_destroy(device); + } else { + pointer->serial = serial; + update_cursor(seat); + } +} + +static void +wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *surface) +{ + /* nop */ +} + +static void +wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, + wl_fixed_t surface_x, wl_fixed_t surface_y) +{ + struct seat *seat = data; + seat->pointer.x = wl_fixed_to_int(surface_x); + seat->pointer.y = wl_fixed_to_int(surface_y); +} + +static void +wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, + uint32_t time, uint32_t button, uint32_t state) +{ + struct seat *seat = data; + struct nag *nag = seat->nag; + + if (state != WL_POINTER_BUTTON_STATE_PRESSED) { + return; + } + + double x = seat->pointer.x; + double y = seat->pointer.y; + + int index = 0; + struct button *nagbutton; + wl_list_for_each(nagbutton, &nag->buttons, link) { + if (x >= nagbutton->x + && y >= nagbutton->y + && x < nagbutton->x + nagbutton->width + && y < nagbutton->y + nagbutton->height) { + button_execute(nag, nagbutton); + exit_status = index; + return; + } + ++index; + } + + if (nag->details.visible && + nag->details.total_lines != nag->details.visible_lines) { + struct button button_up = nag->details.button_up; + if (x >= button_up.x + && y >= button_up.y + && x < button_up.x + button_up.width + && y < button_up.y + button_up.height + && nag->details.offset > 0) { + nag->details.offset--; + render_frame(nag); + return; + } + + struct button button_down = nag->details.button_down; + int bot = nag->details.total_lines; + bot -= nag->details.visible_lines; + if (x >= button_down.x + && y >= button_down.y + && x < button_down.x + button_down.width + && y < button_down.y + button_down.height + && nag->details.offset < bot) { + nag->details.offset++; + render_frame(nag); + return; + } + } +} + +static void +wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, + uint32_t axis, wl_fixed_t value) +{ + struct seat *seat = data; + struct nag *nag = seat->nag; + if (!nag->details.visible + || seat->pointer.x < nag->details.x + || seat->pointer.y < nag->details.y + || seat->pointer.x >= nag->details.x + nag->details.width + || seat->pointer.y >= nag->details.y + nag->details.height + || nag->details.total_lines == nag->details.visible_lines) { + return; + } + + int direction = wl_fixed_to_int(value); + int bot = nag->details.total_lines - nag->details.visible_lines; + if (direction < 0 && nag->details.offset > 0) { + nag->details.offset--; + } else if (direction > 0 && nag->details.offset < bot) { + nag->details.offset++; + } + + render_frame(nag); +} + +static void +wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) +{ + struct seat *seat = data; + /* pointer inputs clears timer for auto-closing */ + close_pollfd(&seat->nag->pollfds[FD_TIMER]); +} + +static void +wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer, + uint32_t axis_source) +{ + /* nop */ +} + +static void +wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis) +{ + /* nop */ +} + +static void +wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, + uint32_t axis, int32_t discrete) +{ + /* nop */ +} + +static const struct wl_pointer_listener pointer_listener = { + .enter = wl_pointer_enter, + .leave = wl_pointer_leave, + .motion = wl_pointer_motion, + .button = wl_pointer_button, + .axis = wl_pointer_axis, + .frame = wl_pointer_frame, + .axis_source = wl_pointer_axis_source, + .axis_stop = wl_pointer_axis_stop, + .axis_discrete = wl_pointer_axis_discrete, +}; + +static void +seat_handle_capabilities(void *data, struct wl_seat *wl_seat, + enum wl_seat_capability caps) +{ + struct seat *seat = data; + bool cap_pointer = caps & WL_SEAT_CAPABILITY_POINTER; + if (cap_pointer && !seat->pointer.pointer) { + seat->pointer.pointer = wl_seat_get_pointer(wl_seat); + wl_pointer_add_listener(seat->pointer.pointer, + &pointer_listener, seat); + } else if (!cap_pointer && seat->pointer.pointer) { + wl_pointer_destroy(seat->pointer.pointer); + seat->pointer.pointer = NULL; + } +} + +static void +seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name) +{ + /* nop */ +} + +static const struct wl_seat_listener seat_listener = { + .capabilities = seat_handle_capabilities, + .name = seat_handle_name, +}; + +static void +output_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, + int32_t physical_width, int32_t physical_height, + int32_t subpixel, const char *make, const char *model, + int32_t transform) +{ + /* nop */ +} + +static void +output_mode(void *data, struct wl_output *wl_output, uint32_t flags, + int32_t width, int32_t height, int32_t refresh) +{ + /* nop */ +} + +static void +output_done(void *data, struct wl_output *output) +{ + /* nop */ +} + +static void +output_scale(void *data, struct wl_output *output, int32_t factor) +{ + struct output *nag_output = data; + nag_output->scale = factor; + if (nag_output->nag->output == nag_output) { + nag_output->nag->scale = nag_output->scale; + if (!nag_output->nag->cursor_shape_manager) { + update_all_cursors(nag_output->nag); + } + render_frame(nag_output->nag); + } +} + +static void +output_name(void *data, struct wl_output *output, const char *name) +{ + struct output *nag_output = data; + nag_output->name = strdup(name); + + const char *outname = nag_output->nag->conf->output; + if (!nag_output->nag->output && outname && + strcmp(outname, name) == 0) { + wlr_log(WLR_DEBUG, "Using output %s", name); + nag_output->nag->output = nag_output; + } +} + +static void +output_description(void *data, struct wl_output *wl_output, + const char *description) +{ + /* nop */ +} + +static const struct wl_output_listener output_listener = { + .geometry = output_geometry, + .mode = output_mode, + .done = output_done, + .scale = output_scale, + .name = output_name, + .description = output_description, +}; + +static void +handle_global(void *data, struct wl_registry *registry, uint32_t name, + const char *interface, uint32_t version) +{ + struct nag *nag = data; + if (strcmp(interface, wl_compositor_interface.name) == 0) { + nag->compositor = wl_registry_bind(registry, name, + &wl_compositor_interface, 4); + } else if (strcmp(interface, wl_seat_interface.name) == 0) { + struct seat *seat = calloc(1, sizeof(*seat)); + if (!seat) { + perror("calloc"); + return; + } + + seat->nag = nag; + seat->wl_name = name; + seat->wl_seat = + wl_registry_bind(registry, name, &wl_seat_interface, 5); + + wl_seat_add_listener(seat->wl_seat, &seat_listener, seat); + + wl_list_insert(&nag->seats, &seat->link); + } else if (strcmp(interface, wl_shm_interface.name) == 0) { + nag->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); + } else if (strcmp(interface, wl_output_interface.name) == 0) { + if (!nag->output) { + struct output *output = calloc(1, sizeof(*output)); + if (!output) { + perror("calloc"); + return; + } + output->wl_output = wl_registry_bind(registry, name, + &wl_output_interface, 4); + output->wl_name = name; + output->scale = 1; + output->nag = nag; + wl_list_insert(&nag->outputs, &output->link); + wl_output_add_listener(output->wl_output, + &output_listener, output); + } + } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { + nag->layer_shell = wl_registry_bind( + registry, name, &zwlr_layer_shell_v1_interface, 1); + } else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) { + nag->cursor_shape_manager = wl_registry_bind( + registry, name, &wp_cursor_shape_manager_v1_interface, 1); + } +} + +static void +handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) +{ + struct nag *nag = data; + if (nag->output->wl_name == name) { + nag->run_display = false; + } + + struct seat *seat, *tmpseat; + wl_list_for_each_safe(seat, tmpseat, &nag->seats, link) { + if (seat->wl_name == name) { + seat_destroy(seat); + } + } +} + +static const struct wl_registry_listener registry_listener = { + .global = handle_global, + .global_remove = handle_global_remove, +}; + +static void +nag_setup_cursors(struct nag *nag) +{ + struct seat *seat; + + wl_list_for_each(seat, &nag->seats, link) { + struct pointer *p = &seat->pointer; + + p->cursor_surface = + wl_compositor_create_surface(nag->compositor); + assert(p->cursor_surface); + } +} + +static void +nag_setup(struct nag *nag) +{ + nag->display = wl_display_connect(NULL); + if (!nag->display) { + wlr_log(WLR_ERROR, "Unable to connect to the compositor. " + "If your compositor is running, check or set the " + "WAYLAND_DISPLAY environment variable."); + exit(LAB_EXIT_FAILURE); + } + + nag->scale = 1; + + struct wl_registry *registry = wl_display_get_registry(nag->display); + wl_registry_add_listener(registry, ®istry_listener, nag); + if (wl_display_roundtrip(nag->display) < 0) { + wlr_log(WLR_ERROR, "failed to register with the wayland display"); + exit(LAB_EXIT_FAILURE); + } + + assert(nag->compositor && nag->layer_shell && nag->shm); + + /* Second roundtrip to get wl_output properties */ + if (wl_display_roundtrip(nag->display) < 0) { + wlr_log(WLR_ERROR, "Error during outputs init."); + nag_destroy(nag); + exit(LAB_EXIT_FAILURE); + } + + if (!nag->output && nag->conf->output) { + wlr_log(WLR_ERROR, "Output '%s' not found", nag->conf->output); + nag_destroy(nag); + exit(LAB_EXIT_FAILURE); + } + + if (!nag->cursor_shape_manager) { + nag_setup_cursors(nag); + } + + nag->surface = wl_compositor_create_surface(nag->compositor); + assert(nag->surface); + wl_surface_add_listener(nag->surface, &surface_listener, nag); + + nag->layer_surface = zwlr_layer_shell_v1_get_layer_surface( + nag->layer_shell, nag->surface, + nag->output ? nag->output->wl_output : NULL, + nag->conf->layer, + "nag"); + assert(nag->layer_surface); + zwlr_layer_surface_v1_add_listener(nag->layer_surface, + &layer_surface_listener, nag); + zwlr_layer_surface_v1_set_anchor(nag->layer_surface, + nag->conf->anchors); + + wl_registry_destroy(registry); + + nag->pollfds[FD_WAYLAND].fd = wl_display_get_fd(nag->display); + nag->pollfds[FD_WAYLAND].events = POLLIN; + + if (nag->details.close_timeout != 0) { + nag->pollfds[FD_TIMER].fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); + nag->pollfds[FD_TIMER].events = POLLIN; + struct itimerspec timeout = { + .it_value.tv_sec = nag->details.close_timeout, + }; + timerfd_settime(nag->pollfds[FD_TIMER].fd, 0, &timeout, NULL); + } else { + nag->pollfds[FD_TIMER].fd = -1; + } + + sigset_t mask; + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + sigprocmask(SIG_BLOCK, &mask, NULL); + nag->pollfds[FD_SIGNAL].fd = signalfd(-1, &mask, SFD_CLOEXEC | SFD_NONBLOCK); + nag->pollfds[FD_SIGNAL].events = POLLIN; +} + +static void +close_pollfd(struct pollfd *pollfd) +{ + if (pollfd->fd == -1) { + return; + } + close(pollfd->fd); + pollfd->fd = -1; + pollfd->events = 0; + pollfd->revents = 0; +} + +static void +nag_run(struct nag *nag) +{ + nag->run_display = true; + render_frame(nag); + while (nag->run_display) { + while (wl_display_prepare_read(nag->display) != 0) { + wl_display_dispatch_pending(nag->display); + } + + errno = 0; + if (wl_display_flush(nag->display) == -1 && errno != EAGAIN) { + break; + } + + if (!nag->run_display) { + break; + } + + poll(nag->pollfds, NR_FDS, -1); + if (nag->pollfds[FD_WAYLAND].revents & POLLIN) { + wl_display_read_events(nag->display); + } else { + wl_display_cancel_read(nag->display); + } + if (nag->pollfds[FD_TIMER].revents & POLLIN) { + break; + } + if (nag->pollfds[FD_SIGNAL].revents & POLLIN) { + break; + } + } +} + +static void +conf_init(struct conf *conf) +{ + conf->font_description = pango_font_description_from_string("pango:Sans 10"); + conf->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP + | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT + | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + conf->layer = ZWLR_LAYER_SHELL_V1_LAYER_TOP; + conf->button_background = 0x333333FF; + conf->details_background = 0x333333FF; + conf->background = 0x323232FF; + conf->text = 0xFFFFFFFF; + conf->button_text = 0xFFFFFFFF; + conf->border = 0x222222FF; + conf->border_bottom = 0x444444FF; + conf->bar_border_thickness = 2; + conf->message_padding = 8; + conf->details_border_thickness = 3; + conf->button_border_thickness = 3; + conf->button_gap = 20; + conf->button_gap_close = 15; + conf->button_margin_right = 2; + conf->button_padding = 3; + conf->button_background = 0x680A0AFF; + conf->details_background = 0x680A0AFF; + conf->background = 0x900000FF; + conf->text = 0xFFFFFFFF; + conf->button_text = 0xFFFFFFFF; + conf->border = 0xD92424FF; + conf->border_bottom = 0x470909FF; +} + +static bool +parse_color(const char *color, uint32_t *result) +{ + if (color[0] == '#') { + ++color; + } + int len = strlen(color); + if ((len != 6 && len != 8) || !isxdigit(color[0]) || !isxdigit(color[1])) { + return false; + } + char *ptr; + uint32_t parsed = strtoul(color, &ptr, 16); + if (*ptr != '\0') { + return false; + } + *result = len == 6 ? ((parsed << 8) | 0xFF) : parsed; + return true; +} + +/* + * As labnag is slow for large "detailed messages" we curtail stdin at an + * arbitrary size to avoid hogging the CPU. + */ +#define MAX_STDIN_LINES 200 +static char * +read_and_trim_stdin(void) +{ + char *buffer = NULL, *line = NULL; + size_t buffer_len = 0, line_size = 0; + int line_count = 0; + while (line_count < MAX_STDIN_LINES) { + ssize_t nread = getline(&line, &line_size, stdin); + if (nread == -1) { + if (feof(stdin)) { + break; + } else { + perror("getline"); + goto freeline; + } + } + buffer = realloc(buffer, buffer_len + nread + 1); + if (!buffer) { + perror("realloc"); + return NULL; + } + memcpy(&buffer[buffer_len], line, nread + 1); + buffer_len += nread; + ++line_count; + } + free(line); + + while (buffer_len && buffer[buffer_len - 1] == '\n') { + buffer[--buffer_len] = '\0'; + } + + return buffer; + +freeline: + free(line); + return NULL; +} + +static int +nag_parse_options(int argc, char **argv, struct nag *nag, + struct conf *conf, bool *debug) +{ + enum type_options { + TO_COLOR_BACKGROUND = 256, + TO_COLOR_BORDER, + TO_COLOR_BORDER_BOTTOM, + TO_COLOR_BUTTON, + TO_COLOR_DETAILS, + TO_COLOR_TEXT, + TO_COLOR_BUTTON_TEXT, + TO_THICK_BAR_BORDER, + TO_PADDING_MESSAGE, + TO_THICK_DET_BORDER, + TO_THICK_BTN_BORDER, + TO_GAP_BTN, + TO_GAP_BTN_DISMISS, + TO_MARGIN_BTN_RIGHT, + TO_PADDING_BTN, + }; + + static const struct option opts[] = { + {"button", required_argument, NULL, 'B'}, + {"button-dismiss", required_argument, NULL, 'Z'}, + {"debug", no_argument, NULL, 'd'}, + {"edge", required_argument, NULL, 'e'}, + {"layer", required_argument, NULL, 'y'}, + {"font", required_argument, NULL, 'f'}, + {"help", no_argument, NULL, 'h'}, + {"detailed-message", no_argument, NULL, 'l'}, + {"detailed-button", required_argument, NULL, 'L'}, + {"message", required_argument, NULL, 'm'}, + {"output", required_argument, NULL, 'o'}, + {"timeout", no_argument, NULL, 't'}, + {"version", no_argument, NULL, 'v'}, + + {"background", required_argument, NULL, TO_COLOR_BACKGROUND}, + {"border", required_argument, NULL, TO_COLOR_BORDER}, + {"border-bottom", required_argument, NULL, TO_COLOR_BORDER_BOTTOM}, + {"button-background", required_argument, NULL, TO_COLOR_BUTTON}, + {"text", required_argument, NULL, TO_COLOR_TEXT}, + {"button-text", required_argument, NULL, TO_COLOR_BUTTON_TEXT}, + {"border-bottom-size", required_argument, NULL, TO_THICK_BAR_BORDER}, + {"message-padding", required_argument, NULL, TO_PADDING_MESSAGE}, + {"details-border-size", required_argument, NULL, TO_THICK_DET_BORDER}, + {"details-background", required_argument, NULL, TO_COLOR_DETAILS}, + {"button-border-size", required_argument, NULL, TO_THICK_BTN_BORDER}, + {"button-gap", required_argument, NULL, TO_GAP_BTN}, + {"button-dismiss-gap", required_argument, NULL, TO_GAP_BTN_DISMISS}, + {"button-margin-right", required_argument, NULL, TO_MARGIN_BTN_RIGHT}, + {"button-padding", required_argument, NULL, TO_PADDING_BTN}, + + {0, 0, 0, 0} + }; + + const char *usage = + "Usage: nag [options...]\n" + "\n" + " -B, --button [] Create a button with text\n" + " -Z, --button-dismiss []\n" + " Like -B but dismiss nag when pressed\n" + " -d, --debug Enable debugging.\n" + " -e, --edge top|bottom Set the edge to use.\n" + " -y, --layer overlay|top|bottom|background\n" + " Set the layer to use.\n" + " -f, --font Set the font to use.\n" + " -h, --help Show help message and quit.\n" + " -l, --detailed-message Read a detailed message from stdin.\n" + " -L, --detailed-button Set the text of the detail button.\n" + " -m, --message Set the message text.\n" + " -o, --output Set the output to use.\n" + " -t, --timeout Set duration to close dialog.\n" + " -x, --exclusive-zone Use exclusive zone.\n" + " -v, --version Show the version number and quit.\n" + "\n" + "The following appearance options can also be given:\n" + " --background RRGGBB[AA] Background color.\n" + " --border RRGGBB[AA] Border color.\n" + " --border-bottom RRGGBB[AA] Bottom border color.\n" + " --button-background RRGGBB[AA] Button background color.\n" + " --text RRGGBB[AA] Text color.\n" + " --button-text RRGGBB[AA] Button text color.\n" + " --border-bottom-size size Thickness of the bar border.\n" + " --message-padding padding Padding for the message.\n" + " --details-border-size size Thickness for the details border.\n" + " --details-background RRGGBB[AA] Details background color.\n" + " --button-border-size size Thickness for the button border.\n" + " --button-gap gap Size of the gap between buttons\n" + " --button-dismiss-gap gap Size of the gap for dismiss button.\n" + " --button-margin-right margin Margin from dismiss button to edge.\n" + " --button-padding padding Padding for the button text.\n"; + + optind = 1; + while (1) { + int c = getopt_long(argc, argv, "B:Z:c:de:y:f:hlL:m:o:s:t:vx", opts, NULL); + if (c == -1) { + break; + } + switch (c) { + case 'B': /* Button */ + case 'Z': /* Button (Dismiss) */ { + struct button *button = calloc(1, sizeof(*button)); + if (!button) { + perror("calloc"); + return LAB_EXIT_FAILURE; + } + if (argv[optind] && argv[optind][0] != '-') { + button->action = argv[optind]; + optind++; + } + button->text = optarg; + button->dismiss = c == 'Z'; + wl_list_insert(&nag->buttons, &button->link); + break; + } + case 'd': /* Debug */ + *debug = true; + break; + case 'e': /* Edge */ + if (strcmp(optarg, "top") == 0) { + conf->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP + | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT + | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + } else if (strcmp(optarg, "bottom") == 0) { + conf->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM + | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT + | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + } else { + fprintf(stderr, "Invalid edge: %s\n", optarg); + return LAB_EXIT_FAILURE; + } + break; + case 'y': /* Layer */ + if (strcmp(optarg, "background") == 0) { + conf->layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND; + } else if (strcmp(optarg, "bottom") == 0) { + conf->layer = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; + } else if (strcmp(optarg, "top") == 0) { + conf->layer = ZWLR_LAYER_SHELL_V1_LAYER_TOP; + } else if (strcmp(optarg, "overlay") == 0) { + conf->layer = ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY; + } else { + fprintf(stderr, "Invalid layer: %s\n" + "Usage: --layer overlay|top|bottom|background\n", + optarg); + return LAB_EXIT_FAILURE; + } + break; + case 'f': /* Font */ + pango_font_description_free(conf->font_description); + conf->font_description = pango_font_description_from_string(optarg); + break; + case 'l': /* Detailed Message */ + free(nag->details.message); + nag->details.message = read_and_trim_stdin(); + if (!nag->details.message) { + return LAB_EXIT_FAILURE; + } + nag->details.button_up.text = "▲"; + nag->details.button_down.text = "▼"; + break; + case 'L': /* Detailed Button Text */ + nag->details.details_text = optarg; + break; + case 'm': /* Message */ + nag->message = optarg; + break; + case 'o': /* Output */ + free(conf->output); + conf->output = optarg; + break; + case 't': + nag->details.close_timeout = atoi(optarg); + break; + case 'x': + nag->details.use_exclusive_zone = true; + break; + case 'v': /* Version */ + printf("labnag " LABWC_VERSION "\n"); + return LAB_EXIT_FAILURE; + case TO_COLOR_BACKGROUND: /* Background color */ + if (!parse_color(optarg, &conf->background)) { + fprintf(stderr, "Invalid background color: %s", optarg); + } + break; + case TO_COLOR_BORDER: /* Border color */ + if (!parse_color(optarg, &conf->border)) { + fprintf(stderr, "Invalid border color: %s", optarg); + } + break; + case TO_COLOR_BORDER_BOTTOM: /* Bottom border color */ + if (!parse_color(optarg, &conf->border_bottom)) { + fprintf(stderr, "Invalid border bottom color: %s", optarg); + } + break; + case TO_COLOR_BUTTON: /* Button background color */ + if (!parse_color(optarg, &conf->button_background)) { + fprintf(stderr, "Invalid button background color: %s", optarg); + } + break; + case TO_COLOR_DETAILS: /* Details background color */ + if (!parse_color(optarg, &conf->details_background)) { + fprintf(stderr, "Invalid details background color: %s", optarg); + } + break; + case TO_COLOR_TEXT: /* Text color */ + if (!parse_color(optarg, &conf->text)) { + fprintf(stderr, "Invalid text color: %s", optarg); + } + break; + case TO_COLOR_BUTTON_TEXT: /* Button text color */ + if (!parse_color(optarg, &conf->button_text)) { + fprintf(stderr, "Invalid button text color: %s", optarg); + } + break; + case TO_THICK_BAR_BORDER: /* Bottom border thickness */ + conf->bar_border_thickness = strtol(optarg, NULL, 0); + break; + case TO_PADDING_MESSAGE: /* Message padding */ + conf->message_padding = strtol(optarg, NULL, 0); + break; + case TO_THICK_DET_BORDER: /* Details border thickness */ + conf->details_border_thickness = strtol(optarg, NULL, 0); + break; + case TO_THICK_BTN_BORDER: /* Button border thickness */ + conf->button_border_thickness = strtol(optarg, NULL, 0); + break; + case TO_GAP_BTN: /* Gap between buttons */ + conf->button_gap = strtol(optarg, NULL, 0); + break; + case TO_GAP_BTN_DISMISS: /* Gap between dismiss button */ + conf->button_gap_close = strtol(optarg, NULL, 0); + break; + case TO_MARGIN_BTN_RIGHT: /* Margin on the right side of button area */ + conf->button_margin_right = strtol(optarg, NULL, 0); + break; + case TO_PADDING_BTN: /* Padding for the button text */ + conf->button_padding = strtol(optarg, NULL, 0); + break; + default: /* Help or unknown flag */ + fprintf(c == 'h' ? stdout : stderr, "%s", usage); + return LAB_EXIT_FAILURE; + } + } + + return LAB_EXIT_SUCCESS; +} + +int +main(int argc, char **argv) +{ + struct conf conf = { 0 }; + conf_init(&conf); + + struct nag nag = { + .conf = &conf, + }; + + wl_list_init(&nag.buttons); + wl_list_init(&nag.outputs); + wl_list_init(&nag.seats); + + nag.details.details_text = "Toggle details"; + nag.details.close_timeout = 5; + nag.details.use_exclusive_zone = false; + + bool debug = false; + if (argc > 1) { + exit_status = nag_parse_options(argc, argv, &nag, &conf, &debug); + if (exit_status == LAB_EXIT_FAILURE) { + goto cleanup; + } + } + wlr_log_init(debug ? WLR_DEBUG : WLR_ERROR, NULL); + + if (!nag.message) { + wlr_log(WLR_ERROR, "No message passed. Please provide --message/-m"); + exit_status = LAB_EXIT_FAILURE; + goto cleanup; + } + + if (nag.details.message) { + nag.details.button_details = calloc(1, sizeof(struct button)); + assert(nag.details.button_details); + nag.details.button_details->text = nag.details.details_text; + assert(nag.details.button_details->text); + nag.details.button_details->expand = true; + wl_list_insert(nag.buttons.prev, &nag.details.button_details->link); + } + + wlr_log(WLR_DEBUG, "Output: %s", nag.conf->output); + wlr_log(WLR_DEBUG, "Anchors: %lu", (unsigned long)nag.conf->anchors); + wlr_log(WLR_DEBUG, "Message: %s", nag.message); + char *font = pango_font_description_to_string(nag.conf->font_description); + wlr_log(WLR_DEBUG, "Font: %s", font); + free(font); + wlr_log(WLR_DEBUG, "Buttons"); + + struct button *button; + wl_list_for_each(button, &nag.buttons, link) { + wlr_log(WLR_DEBUG, "\t[%s] `%s`", button->text, button->action); + } + + nag_setup(&nag); + + nag_run(&nag); + +cleanup: + nag_destroy(&nag); + return exit_status; +} diff --git a/clients/meson.build b/clients/meson.build new file mode 100644 index 00000000..7c6fa5a5 --- /dev/null +++ b/clients/meson.build @@ -0,0 +1,47 @@ +wayland_client = dependency('wayland-client') +wayland_cursor = dependency('wayland-cursor') + +nag_sources = files( + 'labnag.c', + 'pool-buffer.c', +) + +wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') + +protocols = [ + wl_protocol_dir / 'stable/tablet/tablet-v2.xml', + wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml', + wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', + '../protocols/wlr-layer-shell-unstable-v1.xml', +] + +foreach xml : protocols + nag_sources += custom_target( + xml.underscorify() + '_c', + input: xml, + output: '@BASENAME@-protocol.c', + command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'], + ) + nag_sources += custom_target( + xml.underscorify() + '_client_h', + input: xml, + output: '@BASENAME@-client-protocol.h', + command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'], + ) +endforeach + +executable( + 'labnag', + nag_sources, + dependencies: [ + cairo, + pangocairo, + glib, + wayland_client, + wayland_cursor, + wlroots, + server_protos, + ], + install: true +) + diff --git a/clients/pool-buffer.c b/clients/pool-buffer.c new file mode 100644 index 00000000..5dabc8aa --- /dev/null +++ b/clients/pool-buffer.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copied from https://github.com/swaywm/sway + * + * Copyright (C) 2016-2017 Drew DeVault + */ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pool-buffer.h" + +static int anonymous_shm_open(void) +{ + int retries = 100; + + do { + // try a probably-unique name + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + pid_t pid = getpid(); + char name[50]; + snprintf(name, sizeof(name), "/labnag-%x-%x", + (unsigned int)pid, (unsigned int)ts.tv_nsec); + + // shm_open guarantees that O_CLOEXEC is set + int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd >= 0) { + shm_unlink(name); + return fd; + } + + --retries; + } while (retries > 0 && errno == EEXIST); + + return -1; +} + +static void buffer_release(void *data, struct wl_buffer *wl_buffer) +{ + struct pool_buffer *buffer = data; + buffer->busy = false; +} + +static const struct wl_buffer_listener buffer_listener = { + .release = buffer_release +}; + +static struct pool_buffer *create_buffer(struct wl_shm *shm, + struct pool_buffer *buf, int32_t width, int32_t height, + uint32_t format) +{ + uint32_t stride = width * 4; + size_t size = stride * height; + + int fd = anonymous_shm_open(); + if (fd == -1) { + return NULL; + } + if (ftruncate(fd, size) < 0) { + close(fd); + return NULL; + } + void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size); + buf->buffer = wl_shm_pool_create_buffer(pool, 0, + width, height, stride, format); + wl_shm_pool_destroy(pool); + close(fd); + + buf->size = size; + buf->width = width; + buf->height = height; + buf->data = data; + buf->surface = cairo_image_surface_create_for_data(data, + CAIRO_FORMAT_ARGB32, width, height, stride); + buf->cairo = cairo_create(buf->surface); + buf->pango = pango_cairo_create_context(buf->cairo); + + wl_buffer_add_listener(buf->buffer, &buffer_listener, buf); + return buf; +} + +void destroy_buffer(struct pool_buffer *buffer) +{ + if (buffer->buffer) { + wl_buffer_destroy(buffer->buffer); + buffer->buffer = NULL; + } + if (buffer->cairo) { + cairo_destroy(buffer->cairo); + buffer->cairo = NULL; + } + if (buffer->surface) { + cairo_surface_destroy(buffer->surface); + buffer->surface = NULL; + } + if (buffer->pango) { + g_object_unref(buffer->pango); + buffer->pango = NULL; + } + if (buffer->data) { + munmap(buffer->data, buffer->size); + buffer->data = NULL; + } +} + +struct pool_buffer *get_next_buffer(struct wl_shm *shm, + struct pool_buffer pool[static 2], uint32_t width, uint32_t height) +{ + struct pool_buffer *buffer = NULL; + + for (size_t i = 0; i < 2; ++i) { + if (pool[i].busy) { + continue; + } + buffer = &pool[i]; + } + + if (!buffer) { + return NULL; + } + + if (buffer->width != width || buffer->height != height) { + destroy_buffer(buffer); + } + + if (!buffer->buffer) { + if (!create_buffer(shm, buffer, width, height, + WL_SHM_FORMAT_ARGB8888)) { + return NULL; + } + } + buffer->busy = true; + return buffer; +} diff --git a/clients/pool-buffer.h b/clients/pool-buffer.h new file mode 100644 index 00000000..28282ca0 --- /dev/null +++ b/clients/pool-buffer.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copied from https://github.com/swaywm/sway + * + * Copyright (C) 2016-2017 Drew DeVault + */ +#ifndef LAB_POOL_BUFFER_H +#define LAB_POOL_BUFFER_H +#include +#include +#include +#include +#include + +struct pool_buffer { + struct wl_buffer *buffer; + cairo_surface_t *surface; + cairo_t *cairo; + PangoContext *pango; + uint32_t width, height; + void *data; + size_t size; + bool busy; +}; + +struct pool_buffer *get_next_buffer(struct wl_shm *shm, + struct pool_buffer pool[static 2], uint32_t width, uint32_t height); +void destroy_buffer(struct pool_buffer *buffer); + +#endif /* LAB_POOL_BUFFER_H */ diff --git a/docs/labnag.1.scd b/docs/labnag.1.scd new file mode 100644 index 00000000..60970141 --- /dev/null +++ b/docs/labnag.1.scd @@ -0,0 +1,109 @@ +labnag(1) + +# NAME + +labnag - Show dialog with message and buttons + +# SYNOPSIS + +_labnag_ [options...] + +# OPTIONS + +*-B, --button* [] + Create a button with the text _text_ that optionally executes _action_ + when pressed. Multiple buttons can be defined by providing the flag + multiple times. Buttons will appear in the order they are provided from + lef to right. + +*-Z, --button-dismiss* [] + Create a button with the text _text_ that optionally executes _action_ + when pressed, and dismisses labnag. Multiple buttons can be defined by + providing the flag multiple times. Buttons will appear in the order + they are provided from lef to right. + +*-d, --debug* + Enable debugging. + +*-e, --edge* top|bottom + Set the edge to use. + +*-y, --layer* overlay|top|bottom|background + Set the layer to use. + +*-f, --font* + Set the font to use. + +*-h, --help* + Show help message and quit. + +*-l, --detailed-message* + Read a detailed message from stdin. A button to toggle details will be + added. Details are shown in a scrollable multi-line text area. + +*-L, --detailed-button* + Set the text for the button that toggles details. This has no effect if + there is not a detailed message. The default is _Toggle details_. + +*-m, --message* + Set the message text. + +*-o, --output* + Set the output to use. This should be the name of a _xdg\_output_. + +*-t, --timeout* + Set duration to close dialog. Default is 5 seconds. + +*-x, --exclusive-zone* + Use exclusive zone. Default is false. + +*-v, --version* + Show the version number and quit. + +# APPEARANCE OPTIONS + +*--background* + Set the color of the background. + +*--border* + Set the color of the border. + +*--border-bottom* + Set the color of the bottom border. + +*--button-background* + Set the color for the background for buttons. + +*--text* + Set the text color. + +*--button-text* + Set the button text color. + +*--border-bottom-size* + Set the thickness of the bottom border. + +*--message-padding* + Set the padding for the message. + +*--details-background* + Set the color for the background for details. + +*--details-border-size* + Set the thickness for the details border. + +*--button-border-size* + Set the thickness for the button border. + +*--button-gap* + Set the size of the gap between buttons. + +*--button-dismiss-gap* + Set the size of the gap between the dismiss button and another button. + +*--button-margin-right* + Set the margin from the right of the dismiss button to edge. + +*--button-padding* + Set the padding for the button text. + diff --git a/docs/meson.build b/docs/meson.build index 87acdab1..2f89f2e7 100644 --- a/docs/meson.build +++ b/docs/meson.build @@ -7,6 +7,7 @@ if scdoc.found() 'labwc-config.5', 'labwc-menu.5', 'labwc-theme.5', + 'labnag.1', ] foreach manpage : manpages markdown = manpage + '.scd' diff --git a/meson.build b/meson.build index 3172c7e5..dec66dd3 100644 --- a/meson.build +++ b/meson.build @@ -180,6 +180,7 @@ endif subdir('include') subdir('src') subdir('docs') +subdir('clients') dep_cmocka = dependency('cmocka', required: get_option('test')) if dep_cmocka.found() diff --git a/scripts/check b/scripts/check index 9e108666..40d48b1e 100755 --- a/scripts/check +++ b/scripts/check @@ -19,7 +19,7 @@ run_checks () { return $? fi - find src/ include/ \( -name "*.c" -o -name "*.h" \) -type f -print0 | + find src/ include/ clients/ \( -name "*.c" -o -name "*.h" \) -type f -print0 | nice xargs -0 --max-args 1 --max-procs $(nproc) \ scripts/checkpatch.pl --terse --no-tree --strict --file return $? From 8a5a04cc1a49f270b14400e1274cdebbfe050aee Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Tue, 24 Jun 2025 20:44:32 +0100 Subject: [PATCH 079/248] labwc-action(5): describe If action --- docs/labwc-actions.5.scd | 21 +++++++++++++++++++++ docs/menu.xml | 10 ++++++++++ 2 files changed, 31 insertions(+) diff --git a/docs/labwc-actions.5.scd b/docs/labwc-actions.5.scd index fa38698a..7aa78507 100644 --- a/docs/labwc-actions.5.scd +++ b/docs/labwc-actions.5.scd @@ -423,6 +423,7 @@ Actions that execute other actions. Used in keyboard/mouse bindings. ``` + @@ -494,6 +495,26 @@ Actions that execute other actions. Used in keyboard/mouse bindings. This argument is optional. + *prompt* + Display a yes/no prompt dialog (labnag by default). If 'yes' is + selected, the *then* branch will be taken; and similarly with + 'no' and *else*. This argument is optional. Note that the syntax + is different to that of Openbox where a prompt element is not + tied to If-actions but would just be a child of the downstream + action. The reason for this difference is increased flexibility + and functionality gained by optionally using an *else* branch. + + ``` + + + + + + + + + ``` + *then* A list of actions to be executed if the window matches any query. This argument is optional. diff --git a/docs/menu.xml b/docs/menu.xml index d1ea8607..536d63a4 100644 --- a/docs/menu.xml +++ b/docs/menu.xml @@ -65,6 +65,16 @@ + From 6c3cf84b07077ece99a956cf0a9983233054fe48 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Fri, 8 Aug 2025 19:17:22 +0100 Subject: [PATCH 080/248] labnag: fix FreeBSD signalfd() build error --- .github/workflows/build.yml | 3 ++- clients/labnag.c | 3 +++ clients/meson.build | 8 ++++++++ scripts/checkpatch.pl | 1 + 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 892d8867..b0ff3053 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -103,7 +103,8 @@ jobs: sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf pkg set -yn pkg:mesa-dri # hack to skip llvm dependency pkg install -y git meson gcc pkgconf cairo pango evdev-proto \ - hwdata wayland-protocols wlroots019 libdisplay-info + hwdata wayland-protocols libdisplay-info libepoll-shim \ + wlroots019 run: echo "setup done" - name: Install Void Linux dependencies diff --git a/clients/labnag.c b/clients/labnag.c index 514ac864..fa3c3aea 100644 --- a/clients/labnag.c +++ b/clients/labnag.c @@ -17,6 +17,9 @@ #include #include #include +#ifdef __FreeBSD__ +#include /* For signalfd() */ +#endif #include #include #include diff --git a/clients/meson.build b/clients/meson.build index 7c6fa5a5..3a80a4f0 100644 --- a/clients/meson.build +++ b/clients/meson.build @@ -30,6 +30,13 @@ foreach xml : protocols ) endforeach +if host_machine.system() in ['freebsd', 'openbsd'] + # For signalfd() + epoll_dep = dependency('epoll-shim') +else + epoll_dep = [] +endif + executable( 'labnag', nag_sources, @@ -41,6 +48,7 @@ executable( wayland_cursor, wlroots, server_protos, + epoll_dep, ], install: true ) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index f9e9d1d9..933dbc59 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -5659,6 +5659,7 @@ sub process { $var !~ /^(?:_?Pango\w+)/ && $var !~ /^(?:xml\w+)/ && $var !~ /^(?:GString|GError|GHashTable)/ && + $var !~ /^(?:__FreeBSD__)/ && $var !~ /^(?:RsvgRectangle|RsvgHandle)/ && $var !~ /^(?:XKB_KEY_XF86Switch_VT_1)/ && From 7c3a587015c98deb5ec1d750b4467997d68d5d27 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sat, 9 Aug 2025 10:54:06 +0100 Subject: [PATCH 081/248] action: fix labnag button order --- src/action.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/action.c b/src/action.c index 657810bf..eb027087 100644 --- a/src/action.c +++ b/src/action.c @@ -817,9 +817,9 @@ handle_view_destroy(struct wl_listener *listener, void *data) static void action_prompt_create(struct view *view, struct server *server, struct action *action) { - char *command = strdup_printf("labnag -m \"%s\" -Z \"%s\" : -Z \"%s\" :", + char *command = strdup_printf("labnag -m \"%s\" -Z \"%s\" -Z \"%s\"", action_get_str(action, "message.prompt", "Choose wisely"), - _("Yes"), _("No")); + _("No"), _("Yes")); int pipe_fd; pid_t prompt_pid = spawn_piped(command, &pipe_fd); From 073fd02300195b889c1d7b0513cedc5f5dc779a0 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 9 Aug 2025 19:53:10 +0900 Subject: [PATCH 082/248] osd: add null-check for output->osd_scene.tree `wlr_scene_node_destroy(&output->osd_scene.tree->node);` does nothing for null pointer, but ASAN is unhappy about accessing (NULL)->node. --- src/osd.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/osd.c b/src/osd.c index 6ae3381d..39846795 100644 --- a/src/osd.c +++ b/src/osd.c @@ -33,9 +33,10 @@ destroy_osd_scenes(struct server *server) { struct output *output; wl_list_for_each(output, &server->outputs, link) { - wlr_scene_node_destroy(&output->osd_scene.tree->node); - output->osd_scene.tree = NULL; - + if (output->osd_scene.tree) { + wlr_scene_node_destroy(&output->osd_scene.tree->node); + output->osd_scene.tree = NULL; + } wl_array_release(&output->osd_scene.items); wl_array_init(&output->osd_scene.items); } From 09b4e9605eede34bf087b189d2297107878335a4 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sat, 9 Aug 2025 15:15:35 +0100 Subject: [PATCH 083/248] labnag: return special value on timeout ...to avoid taking either of the or branches on timeout. --- clients/labnag.c | 4 ++-- clients/meson.build | 1 + include/action-prompt-codes.h | 9 +++++++++ src/action.c | 5 ++++- 4 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 include/action-prompt-codes.h diff --git a/clients/labnag.c b/clients/labnag.c index fa3c3aea..7509d065 100644 --- a/clients/labnag.c +++ b/clients/labnag.c @@ -29,13 +29,12 @@ #include #include #include +#include "action-prompt-codes.h" #include "pool-buffer.h" #include "cursor-shape-v1-client-protocol.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" #define LABNAG_MAX_HEIGHT 500 -#define LAB_EXIT_FAILURE 255 -#define LAB_EXIT_SUCCESS 0 struct conf { PangoFontDescription *font_description; @@ -1239,6 +1238,7 @@ nag_run(struct nag *nag) wl_display_cancel_read(nag->display); } if (nag->pollfds[FD_TIMER].revents & POLLIN) { + exit_status = LAB_EXIT_TIMEOUT; break; } if (nag->pollfds[FD_SIGNAL].revents & POLLIN) { diff --git a/clients/meson.build b/clients/meson.build index 3a80a4f0..fefd5bd4 100644 --- a/clients/meson.build +++ b/clients/meson.build @@ -50,6 +50,7 @@ executable( server_protos, epoll_dep, ], + include_directories: [labwc_inc], install: true ) diff --git a/include/action-prompt-codes.h b/include/action-prompt-codes.h new file mode 100644 index 00000000..d1ca0f05 --- /dev/null +++ b/include/action-prompt-codes.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_ACTION_PROMPT_CODES_H +#define LABWC_ACTION_PROMPT_CODES_H + +#define LAB_EXIT_FAILURE 255 +#define LAB_EXIT_TIMEOUT 254 +#define LAB_EXIT_SUCCESS 0 + +#endif /* LABWC_ACTION_PROMPT_CODES_H */ diff --git a/src/action.c b/src/action.c index eb027087..f377e3bd 100644 --- a/src/action.c +++ b/src/action.c @@ -8,6 +8,7 @@ #include #include #include +#include "action-prompt-codes.h" #include "common/macros.h" #include "common/list.h" #include "common/mem.h" @@ -860,9 +861,11 @@ action_check_prompt_result(pid_t pid, int exit_code) wlr_log(WLR_INFO, "Found pending prompt for exit code %d", exit_code); struct wl_list *actions = NULL; - if (exit_code == 0) { + if (exit_code == LAB_EXIT_SUCCESS) { wlr_log(WLR_INFO, "Selected the 'then' branch"); actions = action_get_actionlist(prompt->action, "then"); + } else if (exit_code == LAB_EXIT_TIMEOUT) { + /* no-op */ } else { wlr_log(WLR_INFO, "Selected the 'else' branch"); actions = action_get_actionlist(prompt->action, "else"); From e05b924e4df51d242582a2e8ca941edf46352a6d Mon Sep 17 00:00:00 2001 From: Narrat Date: Tue, 12 Aug 2025 23:25:45 +0200 Subject: [PATCH 084/248] docs/menu.xml: adjust labnag example It was missing the respective closing tag --- docs/menu.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/menu.xml b/docs/menu.xml index 536d63a4..e5575618 100644 --- a/docs/menu.xml +++ b/docs/menu.xml @@ -68,11 +68,12 @@ From 01a7ac79951cc98dae26781fcd93e4b8dc059598 Mon Sep 17 00:00:00 2001 From: Weblate Date: Mon, 11 Aug 2025 02:27:15 +0200 Subject: [PATCH 085/248] Translation updates from weblate Co-authored-by: Oliver Chiasson Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/fr/ Translation: Labwc/labwc --- po/fr.po | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/po/fr.po b/po/fr.po index 78b8bfca..1b9c8879 100644 --- a/po/fr.po +++ b/po/fr.po @@ -8,9 +8,10 @@ msgstr "" "Project-Id-Version: labwc\n" "Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" "POT-Creation-Date: 2024-09-19 21:09+1000\n" -"PO-Revision-Date: 2024-12-15 01:13+0000\n" -"Last-Translator: vTT \n" -"Language-Team: French \n" +"PO-Revision-Date: 2025-08-11 00:27+0000\n" +"Last-Translator: Oliver Chiasson \n" +"Language-Team: French \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -24,7 +25,7 @@ msgstr "Y aller…" #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "" +msgstr "Terminal" #: src/menu/menu.c:1040 msgid "Reconfigure" From 2ce3f39c1ff0eb652da59658b4d582fea1583acc Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Wed, 13 Aug 2025 23:52:24 +0900 Subject: [PATCH 086/248] menu: allow client-{list-combined,send-to}-menu as submenu of static menu Also, their labels are changed to "Windows" and "Send to desktop" which are the same as Openbox. --- src/menu/menu.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/src/menu/menu.c b/src/menu/menu.c index 0232e2d5..8ceb92b6 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -814,13 +814,6 @@ menu_hide_submenu(struct server *server, const char *id) } } -static void -init_client_send_to_menu(struct server *server) -{ - /* Just create placeholder. Contents will be created when launched */ - menu_create(server, NULL, "client-send-to-menu", ""); -} - static struct action * item_add_action(struct menuitem *item, const char *action_name) { @@ -866,13 +859,6 @@ update_client_send_to_menu(struct server *server) menu_create_scene(menu); } -static void -init_client_list_combined_menu(struct server *server) -{ - /* Just create placeholder. Contents will be created when launched */ - menu_create(server, NULL, "client-list-combined-menu", ""); -} - /* * This is client-list-combined-menu an internal menu similar to root-menu and * client-menu. @@ -1010,11 +996,14 @@ void menu_init(struct server *server) { wl_list_init(&server->menus); + + /* Just create placeholder. Contents will be created when launched */ + menu_create(server, NULL, "client-list-combined-menu", _("Windows")); + menu_create(server, NULL, "client-send-to-menu", _("Send to desktop")); + parse_xml("menu.xml", server); init_rootmenu(server); init_windowmenu(server); - init_client_list_combined_menu(server); - init_client_send_to_menu(server); validate(server); } From 2a039d4f241a81f4b7de6a7f4a00cde7c1ca0752 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Thu, 14 Aug 2025 00:08:06 +0900 Subject: [PATCH 087/248] menu: fix client-send-to-menu Fixes 17d66e5 --- 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 8ceb92b6..0009861d 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -851,7 +851,7 @@ update_client_send_to_menu(struct server *server) NULL, /*show arrow*/ false); struct action *action = item_add_action(item, "SendToDesktop"); - action_arg_add_str(action, "to", "name"); + action_arg_add_str(action, "to", workspace->name); buf_clear(&buf); } From 72e1945a4cc64080f1a544d81f84c51d5f4f6101 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Fri, 15 Aug 2025 02:38:02 +0900 Subject: [PATCH 088/248] menu: allow overwriting submenu icon Allow overwriting the icon of item linking to another menu like below (the icon for "krita" should be shown): This commit also fixes my mistake in 17d66e5 (s/parent->icon/menu->icon/) that showed incorrect icon in an item linking to another menu. --- 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 0009861d..69527ce0 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -613,7 +613,7 @@ fill_menu(struct server *server, struct menu *parent, xmlNode *n) } struct menuitem *item = item_create(parent, menu->label, - parent->icon_name, true); + icon_name ? icon_name : menu->icon_name, true); item->submenu = menu; } error: From 6e949e623a14349f7a61f44e0872a14b0a8b71da Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Thu, 14 Aug 2025 14:35:44 -0400 Subject: [PATCH 089/248] input/cursor: fix assignment/equality mix-up --- src/input/cursor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input/cursor.c b/src/input/cursor.c index 062c3465..1ad7f127 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -725,7 +725,7 @@ handle_constraint_commit(struct wl_listener *listener, void *data) struct wlr_pointer_constraint_v1 *constraint = seat->current_constraint; /* Prevents unused variable warning when compiled without asserts */ (void)constraint; - assert(constraint->surface = data); + assert(constraint->surface == data); } static void From d9f7ccf3aa256a133fa4f2b62d19a473435ae621 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Fri, 15 Aug 2025 01:29:15 -0400 Subject: [PATCH 090/248] menu: fix use-after-free at exit with sub-menu selected Sequence of events: - menu_finish() frees the sub-menu first - the selection.menu of the parent menu is now dangling - menu_finish() frees the parent menu - menu_free() calls menu_close_root() on the parent menu - menu_close_root() tries to close the (freed) sub-menu - boom Extending nullify_item_pointing_to_this_menu() avoids the crash. --- src/menu/menu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/menu/menu.c b/src/menu/menu.c index 69527ce0..136d2b44 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -1027,6 +1027,10 @@ nullify_item_pointing_to_this_menu(struct menu *menu) if (iter->parent == menu) { iter->parent = NULL; } + + if (iter->selection.menu == menu) { + iter->selection.menu = NULL; + } } } From 6fe61f884627746cc57664d77fc5e8c3b2e6ef34 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Wed, 13 Aug 2025 20:34:07 +0100 Subject: [PATCH 091/248] menu: use client-send-to-menu as 'Workspace' submenu ...because that is more flexible and how it is in openbox. I have had in mind that we should do this since the original implementation, and #2994 just jogged my memory. --- src/menu/menu.c | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/src/menu/menu.c b/src/menu/menu.c index 136d2b44..f610c1c3 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -840,6 +840,10 @@ update_client_send_to_menu(struct server *server) struct workspace *workspace; + /* + * is true by default so + * GoToDesktop will be called as part of the action. + */ wl_list_for_each(workspace, &server->workspaces.all, link) { struct buf buf = BUF_INIT; if (workspace == server->workspaces.current) { @@ -856,6 +860,11 @@ update_client_send_to_menu(struct server *server) buf_clear(&buf); } + separator_create(menu, ""); + struct menuitem *item = item_create(menu, + _("Always on Visible Workspace"), NULL, false); + item_add_action(item, "ToggleOmnipresent"); + menu_create_scene(menu); } @@ -944,7 +953,6 @@ init_windowmenu(struct server *server) { struct menu *menu = menu_get_by_id(server, "client-menu"); struct menuitem *item; - struct action *action; /* Default menu if no menu.xml found */ if (!menu) { @@ -963,25 +971,8 @@ init_windowmenu(struct server *server) item_add_action(item, "ToggleAlwaysOnTop"); /* Workspace sub-menu */ - struct menu *workspace_menu = - menu_create(server, NULL, "workspaces", ""); - item = item_create(workspace_menu, _("Move Left"), NULL, false); - /* - * is true by default so - * GoToDesktop will be called as part of the action. - */ - action = item_add_action(item, "SendToDesktop"); - action_arg_add_str(action, "to", "left"); - item = item_create(workspace_menu, _("Move Right"), NULL, false); - action = item_add_action(item, "SendToDesktop"); - action_arg_add_str(action, "to", "right"); - separator_create(workspace_menu, ""); - item = item_create(workspace_menu, - _("Always on Visible Workspace"), NULL, false); - item_add_action(item, "ToggleOmnipresent"); - item = item_create(menu, _("Workspace"), NULL, true); - item->submenu = workspace_menu; + item->submenu = menu_get_by_id(server, "client-send-to-menu"); item = item_create(menu, _("Close"), NULL, false); item_add_action(item, "Close"); @@ -999,7 +990,7 @@ menu_init(struct server *server) /* Just create placeholder. Contents will be created when launched */ menu_create(server, NULL, "client-list-combined-menu", _("Windows")); - menu_create(server, NULL, "client-send-to-menu", _("Send to desktop")); + menu_create(server, NULL, "client-send-to-menu", _("Workspace")); parse_xml("menu.xml", server); init_rootmenu(server); From 8bcea29a1c26dff05aeaaae833a37d880e161434 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Wed, 13 Aug 2025 21:37:51 +0100 Subject: [PATCH 092/248] menu: fix leak in update_client_send_to_menu() --- src/menu/menu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/menu/menu.c b/src/menu/menu.c index f610c1c3..8e3c004a 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -844,8 +844,8 @@ update_client_send_to_menu(struct server *server) * is true by default so * GoToDesktop will be called as part of the action. */ + struct buf buf = BUF_INIT; wl_list_for_each(workspace, &server->workspaces.all, link) { - struct buf buf = BUF_INIT; if (workspace == server->workspaces.current) { buf_add_fmt(&buf, ">%s<", workspace->name); } else { @@ -859,6 +859,7 @@ update_client_send_to_menu(struct server *server) buf_clear(&buf); } + buf_reset(&buf); separator_create(menu, ""); struct menuitem *item = item_create(menu, From 8d60fb27cd43612796ebb56ff5ff880877eca04b Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Wed, 13 Aug 2025 21:38:40 +0100 Subject: [PATCH 093/248] labwc-menu(5): reflect use of client-send-to-menu in client menu --- docs/menu.xml | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/docs/menu.xml b/docs/menu.xml index e5575618..b9dda361 100644 --- a/docs/menu.xml +++ b/docs/menu.xml @@ -25,18 +25,7 @@ Any menu with the id "workspaces" will be hidden if there is only a single workspace available. --> - - - - - - - - - - - - + @@ -609,7 +611,13 @@ - sendEventsMode [yes|no|disabledOnExternalMouse] - calibrationMatrix [six float values split by space] - scrollFactor [float] + + The following ... block may not be complete for + your requirements. Default values are device specific. Only set an option + if you require to override the default. Valid values must be inserted. + --> + From 3ee12b1e951680bd3ac494db72efd8bb879ccd1e Mon Sep 17 00:00:00 2001 From: 01micko <01micko@gmx.com> Date: Wed, 20 Aug 2025 22:26:22 +1000 Subject: [PATCH 100/248] clients/labnag.c: add newline to color errors. --- clients/labnag.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/clients/labnag.c b/clients/labnag.c index 7509d065..e145b556 100644 --- a/clients/labnag.c +++ b/clients/labnag.c @@ -1524,37 +1524,37 @@ nag_parse_options(int argc, char **argv, struct nag *nag, return LAB_EXIT_FAILURE; case TO_COLOR_BACKGROUND: /* Background color */ if (!parse_color(optarg, &conf->background)) { - fprintf(stderr, "Invalid background color: %s", optarg); + fprintf(stderr, "Invalid background color: %s\n", optarg); } break; case TO_COLOR_BORDER: /* Border color */ if (!parse_color(optarg, &conf->border)) { - fprintf(stderr, "Invalid border color: %s", optarg); + fprintf(stderr, "Invalid border color: %s\n", optarg); } break; case TO_COLOR_BORDER_BOTTOM: /* Bottom border color */ if (!parse_color(optarg, &conf->border_bottom)) { - fprintf(stderr, "Invalid border bottom color: %s", optarg); + fprintf(stderr, "Invalid border bottom color: %s\n", optarg); } break; case TO_COLOR_BUTTON: /* Button background color */ if (!parse_color(optarg, &conf->button_background)) { - fprintf(stderr, "Invalid button background color: %s", optarg); + fprintf(stderr, "Invalid button background color: %s\n", optarg); } break; case TO_COLOR_DETAILS: /* Details background color */ if (!parse_color(optarg, &conf->details_background)) { - fprintf(stderr, "Invalid details background color: %s", optarg); + fprintf(stderr, "Invalid details background color: %s\n", optarg); } break; case TO_COLOR_TEXT: /* Text color */ if (!parse_color(optarg, &conf->text)) { - fprintf(stderr, "Invalid text color: %s", optarg); + fprintf(stderr, "Invalid text color: %s\n", optarg); } break; case TO_COLOR_BUTTON_TEXT: /* Button text color */ if (!parse_color(optarg, &conf->button_text)) { - fprintf(stderr, "Invalid button text color: %s", optarg); + fprintf(stderr, "Invalid button text color: %s\n", optarg); } break; case TO_THICK_BAR_BORDER: /* Bottom border thickness */ From 6dea8c0dcc8c7d23f8455813e081686fe4342464 Mon Sep 17 00:00:00 2001 From: Marvin Dostal Date: Sat, 16 Aug 2025 12:31:45 +0200 Subject: [PATCH 101/248] desktop-entry: fix partial string matching Use the base instead of the full string for the comparison. --- src/desktop-entry.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/desktop-entry.c b/src/desktop-entry.c index 6fc842a8..b21df3bc 100644 --- a/src/desktop-entry.c +++ b/src/desktop-entry.c @@ -268,7 +268,7 @@ get_db_entry_by_id_fuzzy(struct sfdo_desktop_db *db, const char *app_id) int alen = strlen(app_id); int dlen = strlen(desktop_id_base); - if (!strncasecmp(app_id, desktop_id, alen > dlen ? dlen : alen)) { + if (!strncasecmp(app_id, desktop_id_base, alen > dlen ? dlen : alen)) { return entry; } } From 9d49d19cd28c63a82b1ceafec7887c6c8f6ce6bc Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Sun, 17 Aug 2025 16:01:50 -0400 Subject: [PATCH 102/248] include: add config/types.h --- include/common/direction.h | 4 +- include/common/parse-bool.h | 7 +- include/common/three-state.h | 11 ---- include/config/rcxml.h | 37 +++++------ include/config/tablet-tool.h | 9 +-- include/config/tablet.h | 16 +---- include/config/types.h | 118 +++++++++++++++++++++++++++++++++ include/input/tablet.h | 2 +- include/labwc.h | 4 +- include/output.h | 4 +- include/overlay.h | 2 +- include/resize-indicator.h | 6 -- include/snap.h | 6 +- include/ssd-internal.h | 1 + include/ssd.h | 9 +-- include/view.h | 124 ++++++++--------------------------- src/action.c | 32 ++++----- src/common/dir.c | 1 + src/common/direction.c | 10 +-- src/common/parse-bool.c | 6 +- src/config/rcxml.c | 43 ++++++------ src/config/session.c | 1 + src/config/tablet-tool.c | 8 +-- src/config/tablet.c | 2 +- src/debug.c | 1 + src/decorations/kde-deco.c | 1 + src/decorations/xdg-deco.c | 1 + src/desktop-entry.c | 1 + src/dnd.c | 1 + src/input/cursor.c | 1 + src/input/keyboard.c | 2 +- src/input/tablet-pad.c | 1 + src/input/tablet.c | 23 +++---- src/input/touch.c | 7 +- src/interactive.c | 33 +++++----- src/magnifier.c | 1 + src/main.c | 1 + src/menu/menu.c | 1 + src/output.c | 5 +- src/overlay.c | 17 ++--- src/placement.c | 1 + src/regions.c | 1 + src/resize-outlines.c | 4 +- src/seat.c | 3 + src/snap.c | 31 ++++----- src/ssd/resize-indicator.c | 2 + src/ssd/ssd-part.c | 1 + src/ssd/ssd-shadow.c | 5 +- src/ssd/ssd-titlebar.c | 2 +- src/ssd/ssd.c | 3 +- src/view.c | 108 +++++++++++++++--------------- src/workspaces.c | 2 + src/xdg.c | 25 +++---- src/xwayland.c | 34 +++++----- 54 files changed, 414 insertions(+), 368 deletions(-) delete mode 100644 include/common/three-state.h create mode 100644 include/config/types.h diff --git a/include/common/direction.h b/include/common/direction.h index 1faa62bb..be05d29c 100644 --- a/include/common/direction.h +++ b/include/common/direction.h @@ -3,9 +3,9 @@ #define LABWC_DIRECTION_H #include -#include "view.h" +#include "config/types.h" -bool direction_from_view_edge(enum view_edge edge, enum wlr_direction *direction); +bool direction_from_edge(enum lab_edge edge, enum wlr_direction *direction); enum wlr_direction direction_get_opposite(enum wlr_direction direction); #endif /* LABWC_DIRECTION_H */ diff --git a/include/common/parse-bool.h b/include/common/parse-bool.h index 97afd586..5f306d10 100644 --- a/include/common/parse-bool.h +++ b/include/common/parse-bool.h @@ -1,17 +1,18 @@ /* SPDX-License-Identifier: GPL-2.0-only */ #ifndef LABWC_PARSE_BOOL_H #define LABWC_PARSE_BOOL_H + #include -#include "common/three-state.h" +#include "config/types.h" /** - * parse_three_state() - Parse boolean value of string as a three-state enum. + * parse_tristate() - Parse boolean value of string as a three-state enum. * @string: String to interpret. This check is case-insensitive. * * Return: LAB_STATE_DISABLED for false; LAB_STATE_ENABLED for true; * LAB_STATE_UNSPECIFIED for non-boolean */ -enum three_state parse_three_state(const char *str); +enum lab_tristate parse_tristate(const char *str); /** * parse_bool() - Parse boolean value of string. diff --git a/include/common/three-state.h b/include/common/three-state.h deleted file mode 100644 index 2e3d69d2..00000000 --- a/include/common/three-state.h +++ /dev/null @@ -1,11 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef LABWC_THREE_STATE_H -#define LABWC_THREE_STATE_H - -enum three_state { - LAB_STATE_UNSPECIFIED = 0, - LAB_STATE_ENABLED, - LAB_STATE_DISABLED -}; - -#endif /* LABWC_THREE_STATE_H */ diff --git a/include/config/rcxml.h b/include/config/rcxml.h index 65107440..a7eb22bd 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -3,29 +3,17 @@ #define LABWC_RCXML_H #include -#include #include +#include #include #include "common/border.h" #include "common/buf.h" #include "common/font.h" -#include "common/three-state.h" -#include "config/touch.h" -#include "config/tablet.h" -#include "config/tablet-tool.h" -#include "config/libinput.h" -#include "resize-indicator.h" +#include "config/types.h" #include "ssd.h" -#include "theme.h" -enum view_placement_policy { - LAB_PLACE_INVALID = 0, - LAB_PLACE_CENTER, - LAB_PLACE_CURSOR, - LAB_PLACE_AUTOMATIC, - LAB_PLACE_CASCADE, -}; +#define BUTTON_MAP_MAX 16 enum adaptive_sync_mode { LAB_ADAPTIVE_SYNC_DISABLED, @@ -33,6 +21,12 @@ enum adaptive_sync_mode { LAB_ADAPTIVE_SYNC_FULLSCREEN, }; +enum resize_indicator_mode { + LAB_RESIZE_INDICATOR_NEVER = 0, + LAB_RESIZE_INDICATOR_ALWAYS, + LAB_RESIZE_INDICATOR_NON_PIXEL +}; + enum tearing_mode { LAB_TEARING_DISABLED = 0, LAB_TEARING_ENABLED, @@ -48,6 +42,11 @@ enum tiling_events_mode { (LAB_TILING_EVENTS_REGION | LAB_TILING_EVENTS_EDGE), }; +struct button_map_entry { + uint32_t from; + uint32_t to; +}; + struct title_button { enum ssd_part_type type; struct wl_list link; @@ -72,7 +71,7 @@ struct rcxml { enum tearing_mode allow_tearing; bool auto_enable_outputs; bool reuse_output_mode; - enum view_placement_policy placement_policy; + enum lab_placement_policy placement_policy; bool xwayland_persistence; bool primary_selection; int placement_cascade_offset_x; @@ -110,7 +109,7 @@ struct rcxml { /* keyboard */ int repeat_rate; int repeat_delay; - enum three_state kb_numlock_enable; + enum lab_tristate kb_numlock_enable; bool kb_layout_per_window; struct wl_list keybinds; /* struct keybind.link */ @@ -126,12 +125,12 @@ struct rcxml { bool force_mouse_emulation; char *output_name; struct wlr_fbox box; - enum rotation rotation; + enum lab_rotation rotation; uint16_t button_map_count; struct button_map_entry button_map[BUTTON_MAP_MAX]; } tablet; struct tablet_tool_config { - enum motion motion; + enum lab_motion motion; double relative_motion_sensitivity; } tablet_tool; diff --git a/include/config/tablet-tool.h b/include/config/tablet-tool.h index bd1943af..62aec1c1 100644 --- a/include/config/tablet-tool.h +++ b/include/config/tablet-tool.h @@ -2,13 +2,8 @@ #ifndef LABWC_TABLET_TOOL_CONFIG_H #define LABWC_TABLET_TOOL_CONFIG_H -#include +#include "config/types.h" -enum motion { - LAB_TABLET_MOTION_ABSOLUTE = 0, - LAB_TABLET_MOTION_RELATIVE, -}; - -enum motion tablet_parse_motion(const char *name); +enum lab_motion tablet_parse_motion(const char *name); #endif /* LABWC_TABLET_TOOL_CONFIG_H */ diff --git a/include/config/tablet.h b/include/config/tablet.h index 8e0c7c67..ae3772b5 100644 --- a/include/config/tablet.h +++ b/include/config/tablet.h @@ -3,22 +3,10 @@ #define LABWC_TABLET_CONFIG_H #include - -enum rotation { - LAB_ROTATE_NONE = 0, - LAB_ROTATE_90, - LAB_ROTATE_180, - LAB_ROTATE_270, -}; - -#define BUTTON_MAP_MAX 16 -struct button_map_entry { - uint32_t from; - uint32_t to; -}; +#include "config/types.h" double tablet_get_dbl_if_positive(const char *content, const char *name); -enum rotation tablet_parse_rotation(int value); +enum lab_rotation tablet_parse_rotation(int value); uint32_t tablet_button_from_str(const char *button); void tablet_button_mapping_add(uint32_t from, uint32_t to); void tablet_load_default_button_mappings(void); diff --git a/include/config/types.h b/include/config/types.h new file mode 100644 index 00000000..e8277463 --- /dev/null +++ b/include/config/types.h @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_CONFIG_TYPES_H +#define LABWC_CONFIG_TYPES_H + +/* + * Shared (basic) types related to user configuration. + * + * Please try to keep dependencies on other headers minimal, + * since config/types.h gets included in many source files. + * + * For the full config struct, see config/rcxml.h. + */ + +/** + * Edges to which a view can be snapped. "Any" is used as + * a catch-all for every valid edge in order to simplify certain + * types of conditionals, but it is only valid for a selection + * of options in rc.xml. + */ +enum lab_edge { + LAB_EDGE_INVALID = 0, + + LAB_EDGE_LEFT = (1 << 0), + LAB_EDGE_RIGHT = (1 << 1), + LAB_EDGE_UP = (1 << 2), + LAB_EDGE_DOWN = (1 << 3), + LAB_EDGE_CENTER = (1 << 4), + LAB_EDGE_ANY = (1 << 5), + + LAB_EDGE_UPLEFT = (LAB_EDGE_UP | LAB_EDGE_LEFT), + LAB_EDGE_UPRIGHT = (LAB_EDGE_UP | LAB_EDGE_RIGHT), + LAB_EDGE_DOWNLEFT = (LAB_EDGE_DOWN | LAB_EDGE_LEFT), + LAB_EDGE_DOWNRIGHT = (LAB_EDGE_DOWN | LAB_EDGE_RIGHT), +}; + +enum lab_motion { + LAB_MOTION_ABSOLUTE = 0, + LAB_MOTION_RELATIVE, +}; + +enum lab_placement_policy { + LAB_PLACE_INVALID = 0, + LAB_PLACE_CENTER, + LAB_PLACE_CURSOR, + LAB_PLACE_AUTOMATIC, + LAB_PLACE_CASCADE, +}; + +enum lab_rotation { + LAB_ROTATE_NONE = 0, + LAB_ROTATE_90, + LAB_ROTATE_180, + LAB_ROTATE_270, +}; + +enum lab_ssd_mode { + LAB_SSD_MODE_INVALID, + LAB_SSD_MODE_NONE, + LAB_SSD_MODE_BORDER, + LAB_SSD_MODE_FULL, +}; + +enum lab_tristate { + LAB_STATE_UNSPECIFIED = 0, + LAB_STATE_ENABLED, + LAB_STATE_DISABLED +}; + +/* All criteria is applied in AND logic */ +enum lab_view_criteria { + /* No filter -> all focusable views */ + LAB_VIEW_CRITERIA_NONE = 0, + + /* + * Includes always-on-top views, e.g. + * what is visible on the current workspace + */ + LAB_VIEW_CRITERIA_CURRENT_WORKSPACE = 1 << 0, + + /* Positive criteria */ + LAB_VIEW_CRITERIA_FULLSCREEN = 1 << 1, + LAB_VIEW_CRITERIA_ALWAYS_ON_TOP = 1 << 2, + LAB_VIEW_CRITERIA_ROOT_TOPLEVEL = 1 << 3, + + /* Negative criteria */ + LAB_VIEW_CRITERIA_NO_ALWAYS_ON_TOP = 1 << 6, + LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER = 1 << 7, + LAB_VIEW_CRITERIA_NO_OMNIPRESENT = 1 << 8, +}; + +/* + * Window types are based on the NET_WM constants from X11. See: + * https://specifications.freedesktop.org/wm-spec/1.4/ar01s05.html#id-1.6.7 + * + * The enum constants are intended to match wlr_xwayland_net_wm_window_type. + * Redefining the same constants here may seem redundant, but is necessary + * to make them available even in builds with xwayland support disabled. + */ +enum lab_window_type { + LAB_WINDOW_TYPE_DESKTOP = 0, + LAB_WINDOW_TYPE_DOCK, + LAB_WINDOW_TYPE_TOOLBAR, + LAB_WINDOW_TYPE_MENU, + LAB_WINDOW_TYPE_UTILITY, + LAB_WINDOW_TYPE_SPLASH, + LAB_WINDOW_TYPE_DIALOG, + LAB_WINDOW_TYPE_DROPDOWN_MENU, + LAB_WINDOW_TYPE_POPUP_MENU, + LAB_WINDOW_TYPE_TOOLTIP, + LAB_WINDOW_TYPE_NOTIFICATION, + LAB_WINDOW_TYPE_COMBO, + LAB_WINDOW_TYPE_DND, + LAB_WINDOW_TYPE_NORMAL, + + LAB_WINDOW_TYPE_LEN +}; + +#endif /* LABWC_CONFIG_TYPES_H */ diff --git a/include/input/tablet.h b/include/input/tablet.h index d90fbdb4..e6c1373e 100644 --- a/include/input/tablet.h +++ b/include/input/tablet.h @@ -21,7 +21,7 @@ struct drawing_tablet_tool { */ bool force_mouse_emulation; - enum motion motion_mode; + enum lab_motion motion_mode; double x, y, dx, dy; double distance; double pressure; diff --git a/include/labwc.h b/include/labwc.h index 6b90bfd6..a4971e45 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -434,11 +434,11 @@ void interactive_cancel(struct view *view); * Returns the edge to snap a window to. * For example, if the output-relative cursor position (x,y) fulfills * x <= () and y <= (), - * then edge1=VIEW_EDGE_UP and edge2=VIEW_EDGE_LEFT. + * then edge1=LAB_EDGE_UP and edge2=LAB_EDGE_LEFT. * The value of (edge1|edge2) can be passed to view_snap_to_edge(). */ bool edge_from_cursor(struct seat *seat, struct output **dest_output, - enum view_edge *edge1, enum view_edge *edge2); + enum lab_edge *edge1, enum lab_edge *edge2); void handle_tearing_new_object(struct wl_listener *listener, void *data); diff --git a/include/output.h b/include/output.h index d96a8c21..87da60d9 100644 --- a/include/output.h +++ b/include/output.h @@ -4,7 +4,7 @@ #include -enum view_edge; +enum lab_edge; #define LAB_NR_LAYERS (4) @@ -60,7 +60,7 @@ struct output *output_nearest_to_cursor(struct server *server); * reference instead. */ struct output *output_get_adjacent(struct output *output, - enum view_edge edge, bool wrap); + enum lab_edge edge, bool wrap); bool output_is_usable(struct output *output); void output_update_usable_area(struct output *output); diff --git a/include/overlay.h b/include/overlay.h index 6a0ee020..96897cad 100644 --- a/include/overlay.h +++ b/include/overlay.h @@ -27,7 +27,7 @@ struct overlay { struct region *region; /* Snap-to-edge overlay */ - enum view_edge edge; + enum lab_edge edge; struct output *output; } active; diff --git a/include/resize-indicator.h b/include/resize-indicator.h index 36e7b0d1..e8fe8480 100644 --- a/include/resize-indicator.h +++ b/include/resize-indicator.h @@ -5,12 +5,6 @@ struct server; struct view; -enum resize_indicator_mode { - LAB_RESIZE_INDICATOR_NEVER = 0, - LAB_RESIZE_INDICATOR_ALWAYS, - LAB_RESIZE_INDICATOR_NON_PIXEL -}; - void resize_indicator_reconfigure(struct server *server); void resize_indicator_show(struct view *view); void resize_indicator_update(struct view *view); diff --git a/include/snap.h b/include/snap.h index 4ac8025d..f5c2e229 100644 --- a/include/snap.h +++ b/include/snap.h @@ -8,13 +8,13 @@ struct wlr_box; void snap_move_to_edge(struct view *view, - enum view_edge direction, bool snap_to_windows, int *dx, int *dy); + enum lab_edge direction, bool snap_to_windows, int *dx, int *dy); void snap_grow_to_next_edge(struct view *view, - enum view_edge direction, struct wlr_box *geo); + enum lab_edge direction, struct wlr_box *geo); void snap_shrink_to_next_edge(struct view *view, - enum view_edge direction, struct wlr_box *geo); + enum lab_edge direction, struct wlr_box *geo); void snap_invalidate_edge_cache(struct view *view); void snap_update_cache_geometry(struct view *view); diff --git a/include/ssd-internal.h b/include/ssd-internal.h index ec64311e..4721de75 100644 --- a/include/ssd-internal.h +++ b/include/ssd-internal.h @@ -5,6 +5,7 @@ #include #include "common/macros.h" #include "ssd.h" +#include "theme.h" #include "view.h" #define FOR_EACH(tmp, ...) \ diff --git a/include/ssd.h b/include/ssd.h index eda0370e..5b7de1d5 100644 --- a/include/ssd.h +++ b/include/ssd.h @@ -61,13 +61,6 @@ enum ssd_part_type { LAB_SSD_END_MARKER }; -enum ssd_mode { - LAB_SSD_MODE_INVALID, - LAB_SSD_MODE_NONE, - LAB_SSD_MODE_BORDER, - LAB_SSD_MODE_FULL, -}; - /* Forward declare arguments */ struct ssd; struct ssd_button; @@ -110,7 +103,7 @@ enum ssd_part_type ssd_get_part_type(const struct ssd *ssd, struct wlr_scene_node *node, struct wlr_cursor *cursor); uint32_t ssd_resize_edges(enum ssd_part_type type); bool ssd_part_contains(enum ssd_part_type whole, enum ssd_part_type candidate); -enum ssd_mode ssd_mode_parse(const char *mode); +enum lab_ssd_mode ssd_mode_parse(const char *mode); /* TODO: clean up / update */ struct border ssd_thickness(struct view *view); diff --git a/include/view.h b/include/view.h index c7f5908c..2aa7df67 100644 --- a/include/view.h +++ b/include/view.h @@ -2,15 +2,13 @@ #ifndef LABWC_VIEW_H #define LABWC_VIEW_H -#include "config/rcxml.h" -#include "config.h" -#include "ssd.h" #include #include #include #include #include -#include "common/three-state.h" +#include "config.h" +#include "config/types.h" #define LAB_MIN_VIEW_HEIGHT 60 @@ -58,27 +56,6 @@ enum view_axis { VIEW_AXIS_INVALID = (1 << 2), }; -/** - * Edges to which a view can be snapped to. "All" is used as - * a catchall for every valid edge in order to simplify certain - * types of conditionals, but it is only valid for a selection - * of options in rc.xml. - */ -enum view_edge { - VIEW_EDGE_INVALID = 0, - - VIEW_EDGE_LEFT = (1 << 0), - VIEW_EDGE_RIGHT = (1 << 1), - VIEW_EDGE_UP = (1 << 2), - VIEW_EDGE_DOWN = (1 << 3), - VIEW_EDGE_CENTER = (1 << 4), - VIEW_EDGE_ANY = (1 << 5), - VIEW_EDGE_UPLEFT = (VIEW_EDGE_UP | VIEW_EDGE_LEFT), - VIEW_EDGE_UPRIGHT = (VIEW_EDGE_UP | VIEW_EDGE_RIGHT), - VIEW_EDGE_DOWNLEFT = (VIEW_EDGE_DOWN | VIEW_EDGE_LEFT), - VIEW_EDGE_DOWNRIGHT = (VIEW_EDGE_DOWN | VIEW_EDGE_RIGHT), -}; - enum view_wants_focus { /* View does not want focus */ VIEW_WANTS_FOCUS_NEVER = 0, @@ -99,33 +76,6 @@ enum view_wants_focus { VIEW_WANTS_FOCUS_UNLIKELY, }; -/* - * Window types are based on the NET_WM constants from X11. See: - * https://specifications.freedesktop.org/wm-spec/1.4/ar01s05.html#id-1.6.7 - * - * The enum constants are intended to match wlr_xwayland_net_wm_window_type. - * Redefining the same constants here may seem redundant, but is necessary - * to make them available even in builds with xwayland support disabled. - */ -enum window_type { - NET_WM_WINDOW_TYPE_DESKTOP = 0, - NET_WM_WINDOW_TYPE_DOCK, - NET_WM_WINDOW_TYPE_TOOLBAR, - NET_WM_WINDOW_TYPE_MENU, - NET_WM_WINDOW_TYPE_UTILITY, - NET_WM_WINDOW_TYPE_SPLASH, - NET_WM_WINDOW_TYPE_DIALOG, - NET_WM_WINDOW_TYPE_DROPDOWN_MENU, - NET_WM_WINDOW_TYPE_POPUP_MENU, - NET_WM_WINDOW_TYPE_TOOLTIP, - NET_WM_WINDOW_TYPE_NOTIFICATION, - NET_WM_WINDOW_TYPE_COMBO, - NET_WM_WINDOW_TYPE_DND, - NET_WM_WINDOW_TYPE_NORMAL, - - WINDOW_TYPE_LEN -}; - struct view; struct wlr_surface; struct foreign_toplevel; @@ -175,7 +125,7 @@ struct view_impl { bool (*has_strut_partial)(struct view *self); /* returns true if view declared itself a window type */ bool (*contains_window_type)(struct view *view, - enum window_type window_type); + enum lab_window_type window_type); /* returns the client pid that this view belongs to */ pid_t (*get_pid)(struct view *view); }; @@ -227,9 +177,9 @@ struct view { enum view_axis maximized; bool fullscreen; bool tearing_hint; - enum three_state force_tearing; + enum lab_tristate force_tearing; bool visible_on_all_workspaces; - enum view_edge tiled; + enum lab_edge tiled; uint32_t edges_visible; /* enum wlr_edges bitset */ bool inhibits_keybinds; /* also inhibits mousebinds */ xkb_layout_index_t keyboard_layout; @@ -327,15 +277,15 @@ struct view_query { int window_type; char *sandbox_engine; char *sandbox_app_id; - enum three_state shaded; + enum lab_tristate shaded; enum view_axis maximized; - enum three_state iconified; - enum three_state focused; - enum three_state omnipresent; - enum view_edge tiled; + enum lab_tristate iconified; + enum lab_tristate focused; + enum lab_tristate omnipresent; + enum lab_edge tiled; char *tiled_region; char *desktop; - enum ssd_mode decoration; + enum lab_ssd_mode decoration; char *monitor; }; @@ -349,28 +299,6 @@ struct xdg_toplevel_view { struct wl_listener new_popup; }; -/* All criteria is applied in AND logic */ -enum lab_view_criteria { - /* No filter -> all focusable views */ - LAB_VIEW_CRITERIA_NONE = 0, - - /* - * Includes always-on-top views, e.g. - * what is visible on the current workspace - */ - LAB_VIEW_CRITERIA_CURRENT_WORKSPACE = 1 << 0, - - /* Positive criteria */ - LAB_VIEW_CRITERIA_FULLSCREEN = 1 << 1, - LAB_VIEW_CRITERIA_ALWAYS_ON_TOP = 1 << 2, - LAB_VIEW_CRITERIA_ROOT_TOPLEVEL = 1 << 3, - - /* Negative criteria */ - LAB_VIEW_CRITERIA_NO_ALWAYS_ON_TOP = 1 << 6, - LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER = 1 << 7, - LAB_VIEW_CRITERIA_NO_OMNIPRESENT = 1 << 8, -}; - /** * view_from_wlr_surface() - returns the view associated with a * wlr_surface, or NULL if the surface has no associated view. @@ -492,20 +420,20 @@ void view_array_append(struct server *server, struct wl_array *views, enum lab_view_criteria criteria); enum view_wants_focus view_wants_focus(struct view *view); -bool view_contains_window_type(struct view *view, enum window_type window_type); +bool view_contains_window_type(struct view *view, enum lab_window_type window_type); /** * view_edge_invert() - select the opposite of a provided edge * - * VIEW_EDGE_CENTER and VIEW_EDGE_INVALID both map to VIEW_EDGE_INVALID. + * LAB_EDGE_CENTER and LAB_EDGE_INVALID both map to LAB_EDGE_INVALID. * * @edge: edge to be inverted */ -enum view_edge view_edge_invert(enum view_edge edge); +enum lab_edge view_edge_invert(enum lab_edge edge); /* If view is NULL, the size of SSD is not considered */ struct wlr_box view_get_edge_snap_box(struct view *view, struct output *output, - enum view_edge edge); + enum lab_edge edge); struct wlr_box view_get_region_snap_box(struct view *view, struct region *region); /** @@ -529,7 +457,7 @@ bool view_is_focusable(struct view *view); void view_offer_focus(struct view *view); struct wlr_box view_get_edge_snap_box(struct view *view, struct output *output, - enum view_edge edge); + enum lab_edge edge); void mappable_connect(struct mappable *mappable, struct wlr_surface *surface, wl_notify_func_t notify_map, wl_notify_func_t notify_unmap); @@ -591,7 +519,7 @@ void view_center(struct view *view, const struct wlr_box *ref); * @policy: placement policy to apply */ void view_place_by_policy(struct view *view, bool allow_cursor, - enum view_placement_policy policy); + enum lab_placement_policy policy); void view_constrain_size_to_that_of_usable_area(struct view *view); void view_restore_to(struct view *view, struct wlr_box geometry); @@ -614,16 +542,16 @@ bool view_is_tiled(struct view *view); bool view_is_tiled_and_notify_tiled(struct view *view); bool view_is_floating(struct view *view); void view_move_to_workspace(struct view *view, struct workspace *workspace); -enum ssd_mode view_get_ssd_mode(struct view *view); -void view_set_ssd_mode(struct view *view, enum ssd_mode mode); -void view_set_decorations(struct view *view, enum ssd_mode mode, bool force_ssd); +enum lab_ssd_mode view_get_ssd_mode(struct view *view); +void view_set_ssd_mode(struct view *view, enum lab_ssd_mode mode); +void view_set_decorations(struct view *view, enum lab_ssd_mode mode, bool force_ssd); void view_toggle_fullscreen(struct view *view); void view_invalidate_last_layout_geometry(struct view *view); void view_adjust_for_layout_change(struct view *view); -void view_move_to_edge(struct view *view, enum view_edge direction, bool snap_to_windows); -void view_grow_to_edge(struct view *view, enum view_edge direction); -void view_shrink_to_edge(struct view *view, enum view_edge direction); -void view_snap_to_edge(struct view *view, enum view_edge direction, +void view_move_to_edge(struct view *view, enum lab_edge direction, bool snap_to_windows); +void view_grow_to_edge(struct view *view, enum lab_edge direction); +void view_shrink_to_edge(struct view *view, enum lab_edge direction); +void view_snap_to_edge(struct view *view, enum lab_edge direction, bool across_outputs, bool store_natural_geometry); void view_snap_to_region(struct view *view, struct region *region, bool store_natural_geometry); void view_move_to_output(struct view *view, struct output *output); @@ -673,8 +601,8 @@ void view_init(struct view *view); void view_destroy(struct view *view); enum view_axis view_axis_parse(const char *direction); -enum view_edge view_edge_parse(const char *direction, bool tiled, bool any); -enum view_placement_policy view_placement_parse(const char *policy); +enum lab_edge view_edge_parse(const char *direction, bool tiled, bool any); +enum lab_placement_policy view_placement_parse(const char *policy); /* xdg.c */ struct wlr_xdg_surface *xdg_surface_from_view(struct view *view); diff --git a/src/action.c b/src/action.c index f377e3bd..2722998f 100644 --- a/src/action.c +++ b/src/action.c @@ -15,7 +15,9 @@ #include "common/parse-bool.h" #include "common/spawn.h" #include "common/string-helpers.h" +#include "config/rcxml.h" #include "debug.h" +#include "input/keyboard.h" #include "labwc.h" #include "magnifier.h" #include "menu/menu.h" @@ -24,9 +26,9 @@ #include "output-virtual.h" #include "regions.h" #include "ssd.h" +#include "theme.h" #include "view.h" #include "workspaces.h" -#include "input/keyboard.h" enum action_arg_type { LAB_ACTION_ARG_STR = 0, @@ -345,8 +347,8 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char if (!strcmp(argument, "direction")) { bool tiled = (action->type == ACTION_TYPE_TOGGLE_SNAP_TO_EDGE || action->type == ACTION_TYPE_SNAP_TO_EDGE); - enum view_edge edge = view_edge_parse(content, tiled, /*any*/ false); - if (edge == VIEW_EDGE_INVALID) { + enum lab_edge edge = view_edge_parse(content, tiled, /*any*/ false); + if (edge == LAB_EDGE_INVALID) { wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", action_names[action->type], argument, content); } else { @@ -389,7 +391,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char break; case ACTION_TYPE_SET_DECORATIONS: if (!strcmp(argument, "decorations")) { - enum ssd_mode mode = ssd_mode_parse(content); + enum lab_ssd_mode mode = ssd_mode_parse(content); if (mode != LAB_SSD_MODE_INVALID) { action_arg_add_int(action, argument, mode); } else { @@ -453,9 +455,9 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char goto cleanup; } if (!strcmp(argument, "direction")) { - enum view_edge edge = view_edge_parse(content, + enum lab_edge edge = view_edge_parse(content, /*tiled*/ false, /*any*/ false); - if (edge == VIEW_EDGE_INVALID) { + if (edge == LAB_EDGE_INVALID) { wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", action_names[action->type], argument, content); } else { @@ -477,7 +479,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char break; case ACTION_TYPE_AUTO_PLACE: if (!strcmp(argument, "policy")) { - enum view_placement_policy policy = + enum lab_placement_policy policy = view_placement_parse(content); if (policy == LAB_PLACE_INVALID) { wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", @@ -913,8 +915,8 @@ get_target_output(struct output *output, struct server *server, if (output_name) { target = output_from_name(server, output_name); } else { - enum view_edge edge = - action_get_int(action, "direction", VIEW_EDGE_INVALID); + enum lab_edge edge = + action_get_int(action, "direction", LAB_EDGE_INVALID); bool wrap = action_get_bool(action, "wrap", false); target = output_get_adjacent(output, edge, wrap); } @@ -1003,7 +1005,7 @@ run_action(struct view *view, struct server *server, struct action *action, case ACTION_TYPE_MOVE_TO_EDGE: if (view) { /* Config parsing makes sure that direction is a valid direction */ - enum view_edge edge = action_get_int(action, "direction", 0); + enum lab_edge edge = action_get_int(action, "direction", 0); bool snap_to_windows = action_get_bool(action, "snapWindows", true); view_move_to_edge(view, edge, snap_to_windows); } @@ -1012,7 +1014,7 @@ run_action(struct view *view, struct server *server, struct action *action, case ACTION_TYPE_SNAP_TO_EDGE: if (view) { /* Config parsing makes sure that direction is a valid direction */ - enum view_edge edge = action_get_int(action, "direction", 0); + enum lab_edge edge = action_get_int(action, "direction", 0); if (action->type == ACTION_TYPE_TOGGLE_SNAP_TO_EDGE && view->maximized == VIEW_AXIS_NONE && !view->fullscreen @@ -1030,14 +1032,14 @@ run_action(struct view *view, struct server *server, struct action *action, case ACTION_TYPE_GROW_TO_EDGE: if (view) { /* Config parsing makes sure that direction is a valid direction */ - enum view_edge edge = action_get_int(action, "direction", 0); + enum lab_edge edge = action_get_int(action, "direction", 0); view_grow_to_edge(view, edge); } break; case ACTION_TYPE_SHRINK_TO_EDGE: if (view) { /* Config parsing makes sure that direction is a valid direction */ - enum view_edge edge = action_get_int(action, "direction", 0); + enum lab_edge edge = action_get_int(action, "direction", 0); view_shrink_to_edge(view, edge); } break; @@ -1095,7 +1097,7 @@ run_action(struct view *view, struct server *server, struct action *action, break; case ACTION_TYPE_SET_DECORATIONS: if (view) { - enum ssd_mode mode = action_get_int(action, + enum lab_ssd_mode mode = action_get_int(action, "decorations", LAB_SSD_MODE_FULL); bool force_ssd = action_get_bool(action, "forceSSD", false); @@ -1378,7 +1380,7 @@ run_action(struct view *view, struct server *server, struct action *action, } case ACTION_TYPE_AUTO_PLACE: if (view) { - enum view_placement_policy policy = + enum lab_placement_policy policy = action_get_int(action, "policy", LAB_PLACE_AUTOMATIC); view_place_by_policy(view, /* allow_cursor */ true, policy); diff --git a/src/common/dir.c b/src/common/dir.c index 7b55a023..a76d5c74 100644 --- a/src/common/dir.c +++ b/src/common/dir.c @@ -15,6 +15,7 @@ #include "common/list.h" #include "common/mem.h" #include "common/string-helpers.h" +#include "config/rcxml.h" #include "labwc.h" struct dir { diff --git a/src/common/direction.c b/src/common/direction.c index 3729f3d7..df1c9299 100644 --- a/src/common/direction.c +++ b/src/common/direction.c @@ -5,19 +5,19 @@ #include "view.h" bool -direction_from_view_edge(enum view_edge edge, enum wlr_direction *direction) +direction_from_edge(enum lab_edge edge, enum wlr_direction *direction) { switch (edge) { - case VIEW_EDGE_LEFT: + case LAB_EDGE_LEFT: *direction = WLR_DIRECTION_LEFT; return true; - case VIEW_EDGE_RIGHT: + case LAB_EDGE_RIGHT: *direction = WLR_DIRECTION_RIGHT; return true; - case VIEW_EDGE_UP: + case LAB_EDGE_UP: *direction = WLR_DIRECTION_UP; return true; - case VIEW_EDGE_DOWN: + case LAB_EDGE_DOWN: *direction = WLR_DIRECTION_DOWN; return true; default: diff --git a/src/common/parse-bool.c b/src/common/parse-bool.c index 561da763..30890315 100644 --- a/src/common/parse-bool.c +++ b/src/common/parse-bool.c @@ -3,8 +3,8 @@ #include #include -enum three_state -parse_three_state(const char *str) +enum lab_tristate +parse_tristate(const char *str) { if (!str) { goto error_not_a_boolean; @@ -33,7 +33,7 @@ error_not_a_boolean: int parse_bool(const char *str, int default_value) { - enum three_state val = parse_three_state(str); + enum lab_tristate val = parse_tristate(str); if (val == LAB_STATE_UNSPECIFIED) { return default_value; } diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 48d21816..17703e68 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -24,13 +24,14 @@ #include "common/parse-bool.h" #include "common/parse-double.h" #include "common/string-helpers.h" -#include "common/three-state.h" #include "common/xml.h" #include "config/default-bindings.h" #include "config/keybind.h" #include "config/libinput.h" #include "config/mousebind.h" #include "config/tablet.h" +#include "config/tablet-tool.h" +#include "config/touch.h" #include "labwc.h" #include "osd.h" #include "regions.h" @@ -62,33 +63,33 @@ parse_window_type(const char *type) return -1; } if (!strcasecmp(type, "desktop")) { - return NET_WM_WINDOW_TYPE_DESKTOP; + return LAB_WINDOW_TYPE_DESKTOP; } else if (!strcasecmp(type, "dock")) { - return NET_WM_WINDOW_TYPE_DOCK; + return LAB_WINDOW_TYPE_DOCK; } else if (!strcasecmp(type, "toolbar")) { - return NET_WM_WINDOW_TYPE_TOOLBAR; + return LAB_WINDOW_TYPE_TOOLBAR; } else if (!strcasecmp(type, "menu")) { - return NET_WM_WINDOW_TYPE_MENU; + return LAB_WINDOW_TYPE_MENU; } else if (!strcasecmp(type, "utility")) { - return NET_WM_WINDOW_TYPE_UTILITY; + return LAB_WINDOW_TYPE_UTILITY; } else if (!strcasecmp(type, "splash")) { - return NET_WM_WINDOW_TYPE_SPLASH; + return LAB_WINDOW_TYPE_SPLASH; } else if (!strcasecmp(type, "dialog")) { - return NET_WM_WINDOW_TYPE_DIALOG; + return LAB_WINDOW_TYPE_DIALOG; } else if (!strcasecmp(type, "dropdown_menu")) { - return NET_WM_WINDOW_TYPE_DROPDOWN_MENU; + return LAB_WINDOW_TYPE_DROPDOWN_MENU; } else if (!strcasecmp(type, "popup_menu")) { - return NET_WM_WINDOW_TYPE_POPUP_MENU; + return LAB_WINDOW_TYPE_POPUP_MENU; } else if (!strcasecmp(type, "tooltip")) { - return NET_WM_WINDOW_TYPE_TOOLTIP; + return LAB_WINDOW_TYPE_TOOLTIP; } else if (!strcasecmp(type, "notification")) { - return NET_WM_WINDOW_TYPE_NOTIFICATION; + return LAB_WINDOW_TYPE_NOTIFICATION; } else if (!strcasecmp(type, "combo")) { - return NET_WM_WINDOW_TYPE_COMBO; + return LAB_WINDOW_TYPE_COMBO; } else if (!strcasecmp(type, "dnd")) { - return NET_WM_WINDOW_TYPE_DND; + return LAB_WINDOW_TYPE_DND; } else if (!strcasecmp(type, "normal")) { - return NET_WM_WINDOW_TYPE_NORMAL; + return LAB_WINDOW_TYPE_NORMAL; } else { return -1; } @@ -433,15 +434,15 @@ fill_action_query(struct action *action, xmlNode *node, struct view_query *query } else if (!strcasecmp(key, "sandboxAppId")) { xstrdup_replace(query->sandbox_app_id, content); } else if (!strcasecmp(key, "shaded")) { - query->shaded = parse_three_state(content); + query->shaded = parse_tristate(content); } else if (!strcasecmp(key, "maximized")) { query->maximized = view_axis_parse(content); } else if (!strcasecmp(key, "iconified")) { - query->iconified = parse_three_state(content); + query->iconified = parse_tristate(content); } else if (!strcasecmp(key, "focused")) { - query->focused = parse_three_state(content); + query->focused = parse_tristate(content); } else if (!strcasecmp(key, "omnipresent")) { - query->omnipresent = parse_three_state(content); + query->omnipresent = parse_tristate(content); } else if (!strcasecmp(key, "tiled")) { query->tiled = view_edge_parse(content, /*tiled*/ true, /*any*/ true); @@ -1108,7 +1109,7 @@ entry(xmlNode *node, char *nodename, char *content) } else if (!strcasecmp(nodename, "primarySelection.core")) { set_bool(content, &rc.primary_selection); } else if (!strcmp(nodename, "policy.placement")) { - enum view_placement_policy policy = view_placement_parse(content); + enum lab_placement_policy policy = view_placement_parse(content); if (policy != LAB_PLACE_INVALID) { rc.placement_policy = policy; } @@ -1401,7 +1402,7 @@ rcxml_init(void) rc.tablet.rotation = 0; rc.tablet.box = (struct wlr_fbox){0}; tablet_load_default_button_mappings(); - rc.tablet_tool.motion = LAB_TABLET_MOTION_ABSOLUTE; + rc.tablet_tool.motion = LAB_MOTION_ABSOLUTE; rc.tablet_tool.relative_motion_sensitivity = 1.0; rc.repeat_rate = 25; diff --git a/src/config/session.c b/src/config/session.c index 4ce24c57..c90c655d 100644 --- a/src/config/session.c +++ b/src/config/session.c @@ -19,6 +19,7 @@ #include "common/parse-bool.h" #include "common/spawn.h" #include "common/string-helpers.h" +#include "config/rcxml.h" #include "labwc.h" static const char *const env_vars[] = { diff --git a/src/config/tablet-tool.c b/src/config/tablet-tool.c index 61e23ba5..228e4b53 100644 --- a/src/config/tablet-tool.c +++ b/src/config/tablet-tool.c @@ -4,14 +4,14 @@ #include #include -enum motion +enum lab_motion tablet_parse_motion(const char *name) { if (!strcasecmp(name, "Absolute")) { - return LAB_TABLET_MOTION_ABSOLUTE; + return LAB_MOTION_ABSOLUTE; } else if (!strcasecmp(name, "Relative")) { - return LAB_TABLET_MOTION_RELATIVE; + return LAB_MOTION_RELATIVE; } wlr_log(WLR_ERROR, "Invalid value for tablet motion: %s", name); - return LAB_TABLET_MOTION_ABSOLUTE; + return LAB_MOTION_ABSOLUTE; } diff --git a/src/config/tablet.c b/src/config/tablet.c index 4ac52178..17435a9f 100644 --- a/src/config/tablet.c +++ b/src/config/tablet.c @@ -21,7 +21,7 @@ tablet_get_dbl_if_positive(const char *content, const char *name) return value; } -enum rotation +enum lab_rotation tablet_parse_rotation(int value) { switch (value) { diff --git a/src/debug.c b/src/debug.c index 60b4a82c..41537103 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #include "debug.h" +#include #include #include #include "common/lab-scene-rect.h" diff --git a/src/decorations/kde-deco.c b/src/decorations/kde-deco.c index 123173df..bf746163 100644 --- a/src/decorations/kde-deco.c +++ b/src/decorations/kde-deco.c @@ -4,6 +4,7 @@ #include #include "common/list.h" #include "common/mem.h" +#include "config/rcxml.h" #include "decorations.h" #include "labwc.h" #include "view.h" diff --git a/src/decorations/xdg-deco.c b/src/decorations/xdg-deco.c index d69808e6..d9eb3918 100644 --- a/src/decorations/xdg-deco.c +++ b/src/decorations/xdg-deco.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #include #include "common/mem.h" +#include "config/rcxml.h" #include "decorations.h" #include "labwc.h" #include "view.h" diff --git a/src/desktop-entry.c b/src/desktop-entry.c index b21df3bc..67b85e1d 100644 --- a/src/desktop-entry.c +++ b/src/desktop-entry.c @@ -10,6 +10,7 @@ #include "common/macros.h" #include "common/mem.h" #include "common/string-helpers.h" +#include "config/rcxml.h" #include "img/img.h" #include "labwc.h" diff --git a/src/dnd.c b/src/dnd.c index b09ca261..8ed7c311 100644 --- a/src/dnd.c +++ b/src/dnd.c @@ -4,6 +4,7 @@ #include #include #include +#include "config/rcxml.h" #include "input/cursor.h" #include "labwc.h" /* for struct seat */ #include "view.h" diff --git a/src/input/cursor.c b/src/input/cursor.c index 1ad7f127..22d0e384 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -22,6 +22,7 @@ #include "common/scene-helpers.h" #include "common/surface-helpers.h" #include "config/mousebind.h" +#include "config/rcxml.h" #include "dnd.h" #include "idle.h" #include "input/gestures.h" diff --git a/src/input/keyboard.c b/src/input/keyboard.c index 28ae0271..0050b860 100644 --- a/src/input/keyboard.c +++ b/src/input/keyboard.c @@ -9,8 +9,8 @@ #include #include "action.h" #include "common/macros.h" -#include "common/three-state.h" #include "config/keybind.h" +#include "config/rcxml.h" #include "idle.h" #include "input/ime.h" #include "input/key-state.h" diff --git a/src/input/tablet-pad.c b/src/input/tablet-pad.c index c2992ef9..06e00f4e 100644 --- a/src/input/tablet-pad.c +++ b/src/input/tablet-pad.c @@ -10,6 +10,7 @@ #include "common/macros.h" #include "common/mem.h" #include "config/rcxml.h" +#include "config/tablet.h" #include "input/cursor.h" #include "input/tablet.h" #include "labwc.h" diff --git a/src/input/tablet.c b/src/input/tablet.c index c53c7d70..ab96fb84 100644 --- a/src/input/tablet.c +++ b/src/input/tablet.c @@ -12,6 +12,7 @@ #include "common/scene-helpers.h" #include "config/rcxml.h" #include "config/mousebind.h" +#include "config/tablet.h" #include "input/cursor.h" #include "input/tablet-pad.h" #include "labwc.h" @@ -92,8 +93,8 @@ tablet_tool_create(struct seat *seat, return tool; } -static enum motion -tool_motion_mode(enum motion motion, struct wlr_tablet_tool *tool) +static enum lab_motion +tool_motion_mode(enum lab_motion motion, struct wlr_tablet_tool *tool) { /* * Absolute positioning doesn't make sense @@ -102,7 +103,7 @@ tool_motion_mode(enum motion motion, struct wlr_tablet_tool *tool) switch (tool->type) { case WLR_TABLET_TOOL_TYPE_MOUSE: case WLR_TABLET_TOOL_TYPE_LENS: - return LAB_TABLET_MOTION_RELATIVE; + return LAB_MOTION_RELATIVE; default: return motion; } @@ -137,7 +138,7 @@ adjust_for_tablet_area(double tablet_width, double tablet_height, } static void -adjust_for_rotation(enum rotation rotation, double *x, double *y) +adjust_for_rotation(enum lab_rotation rotation, double *x, double *y) { double tmp; switch (rotation) { @@ -161,7 +162,7 @@ adjust_for_rotation(enum rotation rotation, double *x, double *y) } static void -adjust_for_rotation_relative(enum rotation rotation, double *dx, double *dy) +adjust_for_rotation_relative(enum lab_rotation rotation, double *dx, double *dy) { double tmp; switch (rotation) { @@ -220,11 +221,11 @@ tablet_get_coords(struct drawing_tablet *tablet, struct drawing_tablet_tool *too /* initialize here to avoid a maybe-uninitialized compiler warning */ double lx = -1, ly = -1; switch (tool->motion_mode) { - case LAB_TABLET_MOTION_ABSOLUTE: + case LAB_MOTION_ABSOLUTE: wlr_cursor_absolute_to_layout_coords(tablet->seat->cursor, tablet->wlr_input_device, *x, *y, &lx, &ly); break; - case LAB_TABLET_MOTION_RELATIVE: + case LAB_MOTION_RELATIVE: /* * Deltas dx,dy will be directly passed into wlr_cursor_move, * so we can add those directly here to determine our future @@ -262,11 +263,11 @@ notify_motion(struct drawing_tablet *tablet, struct drawing_tablet_tool *tool, } switch (tool->motion_mode) { - case LAB_TABLET_MOTION_ABSOLUTE: + case LAB_MOTION_ABSOLUTE: wlr_cursor_warp_absolute(tablet->seat->cursor, tablet->wlr_input_device, x, y); break; - case LAB_TABLET_MOTION_RELATIVE: + case LAB_MOTION_RELATIVE: wlr_cursor_move(tablet->seat->cursor, tablet->wlr_input_device, dx, dy); break; @@ -500,12 +501,12 @@ handle_tablet_tool_axis(struct wl_listener *listener, void *data) } switch (tool->motion_mode) { - case LAB_TABLET_MOTION_ABSOLUTE: + case LAB_MOTION_ABSOLUTE: cursor_emulate_move_absolute(tablet->seat, &ev->tablet->base, x, y, ev->time_msec); break; - case LAB_TABLET_MOTION_RELATIVE: + case LAB_MOTION_RELATIVE: cursor_emulate_move(tablet->seat, &ev->tablet->base, dx, dy, ev->time_msec); diff --git a/src/input/touch.c b/src/input/touch.c index c0745f37..82b9042b 100644 --- a/src/input/touch.c +++ b/src/input/touch.c @@ -5,13 +5,16 @@ #include #include #include +#include "action.h" #include "common/macros.h" #include "common/mem.h" #include "common/scene-helpers.h" +#include "config/mousebind.h" +#include "config/rcxml.h" +#include "config/touch.h" #include "idle.h" #include "labwc.h" -#include "config/mousebind.h" -#include "action.h" +#include "ssd.h" /* Holds layout -> surface offsets to report motion events in relative coords */ struct touch_point { diff --git a/src/interactive.c b/src/interactive.c index 057426fa..b50d27a4 100644 --- a/src/interactive.c +++ b/src/interactive.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #include +#include "config/rcxml.h" #include "edges.h" #include "input/keyboard.h" #include "labwc.h" @@ -166,11 +167,11 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges) bool edge_from_cursor(struct seat *seat, struct output **dest_output, - enum view_edge *edge1, enum view_edge *edge2) + enum lab_edge *edge1, enum lab_edge *edge2) { *dest_output = NULL; - *edge1 = VIEW_EDGE_INVALID; - *edge2 = VIEW_EDGE_INVALID; + *edge1 = LAB_EDGE_INVALID; + *edge2 = LAB_EDGE_INVALID; if (!view_is_floating(seat->server->grabbed_view)) { return false; @@ -201,28 +202,28 @@ edge_from_cursor(struct seat *seat, struct output **dest_output, int right = area->x + area->width - cursor_x; if (top < rc.snap_edge_range) { - *edge1 = VIEW_EDGE_UP; + *edge1 = LAB_EDGE_UP; } else if (bottom < rc.snap_edge_range) { - *edge1 = VIEW_EDGE_DOWN; + *edge1 = LAB_EDGE_DOWN; } else if (left < rc.snap_edge_range) { - *edge1 = VIEW_EDGE_LEFT; + *edge1 = LAB_EDGE_LEFT; } else if (right < rc.snap_edge_range) { - *edge1 = VIEW_EDGE_RIGHT; + *edge1 = LAB_EDGE_RIGHT; } else { return false; } - if (*edge1 == VIEW_EDGE_UP || *edge1 == VIEW_EDGE_DOWN) { + if (*edge1 == LAB_EDGE_UP || *edge1 == LAB_EDGE_DOWN) { if (left < rc.snap_edge_corner_range) { - *edge2 = VIEW_EDGE_LEFT; + *edge2 = LAB_EDGE_LEFT; } else if (right < rc.snap_edge_corner_range) { - *edge2 = VIEW_EDGE_RIGHT; + *edge2 = LAB_EDGE_RIGHT; } - } else if (*edge1 == VIEW_EDGE_LEFT || *edge1 == VIEW_EDGE_RIGHT) { + } else if (*edge1 == LAB_EDGE_LEFT || *edge1 == LAB_EDGE_RIGHT) { if (top < rc.snap_edge_corner_range) { - *edge2 = VIEW_EDGE_UP; + *edge2 = LAB_EDGE_UP; } else if (bottom < rc.snap_edge_corner_range) { - *edge2 = VIEW_EDGE_DOWN; + *edge2 = LAB_EDGE_DOWN; } } @@ -234,18 +235,18 @@ static bool snap_to_edge(struct view *view) { struct output *output; - enum view_edge edge1, edge2; + enum lab_edge edge1, edge2; if (!edge_from_cursor(&view->server->seat, &output, &edge1, &edge2)) { return false; } - enum view_edge edge = edge1 | edge2; + enum lab_edge edge = edge1 | edge2; view_set_output(view, output); /* * Don't store natural geometry here (it was * stored already in interactive_begin()) */ - if (edge == VIEW_EDGE_UP && rc.snap_top_maximize) { + if (edge == LAB_EDGE_UP && rc.snap_top_maximize) { /* */ view_maximize(view, VIEW_AXIS_BOTH, /*store_natural_geometry*/ false); diff --git a/src/magnifier.c b/src/magnifier.c index d9704c80..3764ad16 100644 --- a/src/magnifier.c +++ b/src/magnifier.c @@ -8,6 +8,7 @@ #include #include #include "common/box.h" +#include "config/rcxml.h" #include "labwc.h" #include "output.h" #include "theme.h" diff --git a/src/main.c b/src/main.c index c69090bc..068829dc 100644 --- a/src/main.c +++ b/src/main.c @@ -7,6 +7,7 @@ #include "common/fd-util.h" #include "common/font.h" #include "common/spawn.h" +#include "config/rcxml.h" #include "config/session.h" #include "labwc.h" #include "theme.h" diff --git a/src/menu/menu.c b/src/menu/menu.c index 8e3c004a..ddd31f8b 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -26,6 +26,7 @@ #include "common/spawn.h" #include "common/string-helpers.h" #include "common/xml.h" +#include "config/rcxml.h" #include "labwc.h" #include "output.h" #include "workspaces.h" diff --git a/src/output.c b/src/output.c index 0002865c..6633df2d 100644 --- a/src/output.c +++ b/src/output.c @@ -27,6 +27,7 @@ #include "common/macros.h" #include "common/mem.h" #include "common/scene-helpers.h" +#include "config/rcxml.h" #include "labwc.h" #include "layers.h" #include "node.h" @@ -977,7 +978,7 @@ output_nearest_to_cursor(struct server *server) } struct output * -output_get_adjacent(struct output *output, enum view_edge edge, bool wrap) +output_get_adjacent(struct output *output, enum lab_edge edge, bool wrap) { if (!output_is_usable(output)) { wlr_log(WLR_ERROR, @@ -986,7 +987,7 @@ output_get_adjacent(struct output *output, enum view_edge edge, bool wrap) } enum wlr_direction direction; - if (!direction_from_view_edge(edge, &direction)) { + if (!direction_from_edge(edge, &direction)) { return NULL; } diff --git a/src/overlay.c b/src/overlay.c index c6c51067..c0c47d0e 100644 --- a/src/overlay.c +++ b/src/overlay.c @@ -4,6 +4,7 @@ #include #include "common/direction.h" #include "common/lab-scene-rect.h" +#include "config/rcxml.h" #include "labwc.h" #include "output.h" #include "view.h" @@ -96,7 +97,7 @@ inactivate_overlay(struct overlay *overlay) &overlay->edge_rect.tree->node, false); } overlay->active.region = NULL; - overlay->active.edge = VIEW_EDGE_INVALID; + overlay->active.edge = LAB_EDGE_INVALID; overlay->active.output = NULL; if (overlay->timer) { wl_event_source_timer_update(overlay->timer, 0); @@ -117,9 +118,9 @@ show_region_overlay(struct seat *seat, struct region *region) } static struct wlr_box -get_edge_snap_box(enum view_edge edge, struct output *output) +get_edge_snap_box(enum lab_edge edge, struct output *output) { - if (edge == VIEW_EDGE_UP && rc.snap_top_maximize) { + if (edge == LAB_EDGE_UP && rc.snap_top_maximize) { return output_usable_area_in_layout_coords(output); } else { return view_get_edge_snap_box(NULL, output, edge); @@ -130,7 +131,7 @@ static int handle_edge_overlay_timeout(void *data) { struct seat *seat = data; - assert(seat->overlay.active.edge != VIEW_EDGE_INVALID + assert(seat->overlay.active.edge != LAB_EDGE_INVALID && seat->overlay.active.output); struct wlr_box box = get_edge_snap_box(seat->overlay.active.edge, seat->overlay.active.output); @@ -140,10 +141,10 @@ handle_edge_overlay_timeout(void *data) static bool edge_has_adjacent_output_from_cursor(struct seat *seat, struct output *output, - enum view_edge edge) + enum lab_edge edge) { enum wlr_direction dir; - if (!direction_from_view_edge(edge, &dir)) { + if (!direction_from_edge(edge, &dir)) { return false; } return wlr_output_layout_adjacent_output( @@ -152,7 +153,7 @@ edge_has_adjacent_output_from_cursor(struct seat *seat, struct output *output, } static void -show_edge_overlay(struct seat *seat, enum view_edge edge1, enum view_edge edge2, +show_edge_overlay(struct seat *seat, enum lab_edge edge1, enum lab_edge edge2, struct output *output) { if (!rc.snap_overlay_enabled) { @@ -205,7 +206,7 @@ overlay_update(struct seat *seat) /* Edge-snapping overlay */ struct output *output; - enum view_edge edge1, edge2; + enum lab_edge edge1, edge2; if (edge_from_cursor(seat, &output, &edge1, &edge2)) { show_edge_overlay(seat, edge1, edge2, output); return; diff --git a/src/placement.c b/src/placement.c index 869bede3..2b5cdf61 100644 --- a/src/placement.c +++ b/src/placement.c @@ -4,6 +4,7 @@ #include #include #include "common/mem.h" +#include "config/rcxml.h" #include "labwc.h" #include "output.h" #include "ssd.h" diff --git a/src/regions.c b/src/regions.c index 26c1e6d7..5243b95a 100644 --- a/src/regions.c +++ b/src/regions.c @@ -11,6 +11,7 @@ #include #include "common/list.h" #include "common/mem.h" +#include "config/rcxml.h" #include "input/keyboard.h" #include "labwc.h" #include "output.h" diff --git a/src/resize-outlines.c b/src/resize-outlines.c index fd522419..abb201a1 100644 --- a/src/resize-outlines.c +++ b/src/resize-outlines.c @@ -3,8 +3,10 @@ #include "resize-outlines.h" #include #include "common/lab-scene-rect.h" -#include "ssd.h" #include "labwc.h" +#include "resize-indicator.h" +#include "ssd.h" +#include "theme.h" bool resize_outlines_enabled(struct view *view) diff --git a/src/seat.c b/src/seat.c index 5685d0c5..97f99703 100644 --- a/src/seat.c +++ b/src/seat.c @@ -16,6 +16,9 @@ #include #include "common/macros.h" #include "common/mem.h" +#include "config/libinput.h" +#include "config/rcxml.h" +#include "config/touch.h" #include "input/ime.h" #include "input/tablet.h" #include "input/tablet-pad.h" diff --git a/src/snap.c b/src/snap.c index 7991d48c..ea9d5d46 100644 --- a/src/snap.c +++ b/src/snap.c @@ -3,6 +3,7 @@ #include #include #include "common/border.h" +#include "config/rcxml.h" #include "edges.h" #include "labwc.h" #include "output.h" @@ -50,7 +51,7 @@ check_edge(int *next, struct edge current, struct edge target, } void -snap_move_to_edge(struct view *view, enum view_edge direction, +snap_move_to_edge(struct view *view, enum lab_edge direction, bool snap_to_windows, int *dx, int *dy) { assert(view); @@ -79,26 +80,26 @@ snap_move_to_edge(struct view *view, enum view_edge direction, * needs no further consideration. */ switch (direction) { - case VIEW_EDGE_LEFT: + case LAB_EDGE_LEFT: target.x = usable.x + ssd.left + rc.gap; if (target.x >= view->pending.x) { return; } break; - case VIEW_EDGE_RIGHT: + case LAB_EDGE_RIGHT: target.x = usable.x + usable.width - rc.gap - target.width - ssd.right; if (target.x <= view->pending.x) { return; } break; - case VIEW_EDGE_UP: + case LAB_EDGE_UP: target.y = usable.y + ssd.top + rc.gap; if (target.y >= view->pending.y) { return; } break; - case VIEW_EDGE_DOWN: + case LAB_EDGE_DOWN: target.y = usable.y + usable.height - rc.gap - ssd.bottom - view_effective_height(view, /* use_pending */ true); if (target.y <= view->pending.y) { @@ -133,7 +134,7 @@ snap_move_to_edge(struct view *view, enum view_edge direction, void snap_grow_to_next_edge(struct view *view, - enum view_edge direction, struct wlr_box *geo) + enum lab_edge direction, struct wlr_box *geo) { assert(view); assert(!view->shaded); @@ -152,22 +153,22 @@ snap_grow_to_next_edge(struct view *view, /* First try to grow the view to the relevant edge of its output. */ switch (direction) { - case VIEW_EDGE_LEFT: + case LAB_EDGE_LEFT: geo->x = usable.x + ssd.left + rc.gap; geo->width = view->pending.x + view->pending.width - geo->x; resize_edges = WLR_EDGE_LEFT; break; - case VIEW_EDGE_RIGHT: + case LAB_EDGE_RIGHT: geo->width = usable.x + usable.width - rc.gap - ssd.right - view->pending.x; resize_edges = WLR_EDGE_RIGHT; break; - case VIEW_EDGE_UP: + case LAB_EDGE_UP: geo->y = usable.y + ssd.top + rc.gap; geo->height = view->pending.y + view->pending.height - geo->y; resize_edges = WLR_EDGE_TOP; break; - case VIEW_EDGE_DOWN: + case LAB_EDGE_DOWN: geo->height = usable.y + usable.height - rc.gap - ssd.bottom - view->pending.y; resize_edges = WLR_EDGE_BOTTOM; @@ -213,7 +214,7 @@ snap_grow_to_next_edge(struct view *view, void snap_shrink_to_next_edge(struct view *view, - enum view_edge direction, struct wlr_box *geo) + enum lab_edge direction, struct wlr_box *geo) { assert(view); assert(!view->shaded); @@ -228,21 +229,21 @@ snap_shrink_to_next_edge(struct view *view, * minimum size requirements. */ switch (direction) { - case VIEW_EDGE_RIGHT: + case LAB_EDGE_RIGHT: geo->width = MAX(geo->width / 2, min_width); geo->x = view->pending.x + view->pending.width - geo->width; resize_edges = WLR_EDGE_LEFT; break; - case VIEW_EDGE_LEFT: + case LAB_EDGE_LEFT: geo->width = MAX(geo->width / 2, min_width); resize_edges = WLR_EDGE_RIGHT; break; - case VIEW_EDGE_DOWN: + case LAB_EDGE_DOWN: geo->height = MAX(geo->height / 2, LAB_MIN_VIEW_HEIGHT); geo->y = view->pending.y + view->pending.height - geo->height; resize_edges = WLR_EDGE_TOP; break; - case VIEW_EDGE_UP: + case LAB_EDGE_UP: geo->height = MAX(geo->height / 2, LAB_MIN_VIEW_HEIGHT); resize_edges = WLR_EDGE_BOTTOM; break; diff --git a/src/ssd/resize-indicator.c b/src/ssd/resize-indicator.c index c30900cf..10fe3690 100644 --- a/src/ssd/resize-indicator.c +++ b/src/ssd/resize-indicator.c @@ -5,9 +5,11 @@ #include #include "common/macros.h" #include "common/scaled-font-buffer.h" +#include "config/rcxml.h" #include "labwc.h" #include "resize-indicator.h" #include "resize-outlines.h" +#include "theme.h" #include "view.h" static void diff --git a/src/ssd/ssd-part.c b/src/ssd/ssd-part.c index 1efc7cad..317d076c 100644 --- a/src/ssd/ssd-part.c +++ b/src/ssd/ssd-part.c @@ -7,6 +7,7 @@ #include "common/mem.h" #include "common/scaled-icon-buffer.h" #include "common/scaled-img-buffer.h" +#include "config/rcxml.h" #include "labwc.h" #include "node.h" #include "ssd-internal.h" diff --git a/src/ssd/ssd-shadow.c b/src/ssd/ssd-shadow.c index 8c1f8839..c1743802 100644 --- a/src/ssd/ssd-shadow.c +++ b/src/ssd/ssd-shadow.c @@ -1,13 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-only #include +#include #include -#include "labwc.h" #include "buffer.h" +#include "config/rcxml.h" +#include "labwc.h" #include "ssd-internal.h" #include "theme.h" #include "view.h" -#include #define FOR_EACH_STATE(ssd, tmp) FOR_EACH(tmp, \ &(ssd)->shadow.active, \ diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 57d49600..9ac0d238 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -5,13 +5,13 @@ #include #include #include "buffer.h" -#include "config.h" #include "common/mem.h" #include "common/scaled-font-buffer.h" #include "common/scaled-icon-buffer.h" #include "common/scaled-img-buffer.h" #include "common/scene-helpers.h" #include "common/string-helpers.h" +#include "config/rcxml.h" #include "desktop-entry.h" #include "img/img.h" #include "labwc.h" diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index ac37f15b..97338c26 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -11,6 +11,7 @@ #include #include "common/mem.h" #include "common/scene-helpers.h" +#include "config/rcxml.h" #include "labwc.h" #include "ssd-internal.h" #include "theme.h" @@ -408,7 +409,7 @@ ssd_part_contains(enum ssd_part_type whole, enum ssd_part_type candidate) return false; } -enum ssd_mode +enum lab_ssd_mode ssd_mode_parse(const char *mode) { if (!mode) { diff --git a/src/view.c b/src/view.c index d86b06c5..4088512a 100644 --- a/src/view.c +++ b/src/view.c @@ -14,6 +14,7 @@ #include "common/match.h" #include "common/mem.h" #include "common/scene-helpers.h" +#include "config/rcxml.h" #include "foreign-toplevel/foreign.h" #include "input/keyboard.h" #include "labwc.h" @@ -28,6 +29,7 @@ #include "snap-constraints.h" #include "snap.h" #include "ssd.h" +#include "theme.h" #include "window-rules.h" #include "wlr/util/log.h" #include "workspaces.h" @@ -96,7 +98,7 @@ view_query_free(struct view_query *query) } static bool -query_tristate_match(enum three_state desired, bool actual) +query_tristate_match(enum lab_tristate desired, bool actual) { switch (desired) { case LAB_STATE_ENABLED: @@ -169,11 +171,11 @@ view_matches_query(struct view *view, struct view_query *query) return false; } - if (query->tiled == VIEW_EDGE_ANY) { + if (query->tiled == LAB_EDGE_ANY) { if (!view->tiled) { return false; } - } else if (query->tiled != VIEW_EDGE_INVALID) { + } else if (query->tiled != LAB_EDGE_INVALID) { if (query->tiled != view->tiled) { return false; } @@ -204,7 +206,7 @@ view_matches_query(struct view *view, struct view_query *query) } } - enum ssd_mode decor = view_get_ssd_mode(view); + enum lab_ssd_mode decor = view_get_ssd_mode(view); if (query->decoration != LAB_SSD_MODE_INVALID && query->decoration != decor) { return false; } @@ -216,11 +218,11 @@ view_matches_query(struct view *view, struct view_query *query) return false; } if (!strcasecmp(query->monitor, "left") && - output_get_adjacent(current, VIEW_EDGE_LEFT, false) != view->output) { + output_get_adjacent(current, LAB_EDGE_LEFT, false) != view->output) { return false; } if (!strcasecmp(query->monitor, "right") && - output_get_adjacent(current, VIEW_EDGE_RIGHT, false) != view->output) { + output_get_adjacent(current, LAB_EDGE_RIGHT, false) != view->output) { return false; } if (output_from_name(view->server, query->monitor) != view->output) { @@ -385,7 +387,7 @@ view_wants_focus(struct view *view) } bool -view_contains_window_type(struct view *view, enum window_type window_type) +view_contains_window_type(struct view *view, enum lab_window_type window_type) { assert(view); if (view->impl->contains_window_type) { @@ -427,28 +429,28 @@ view_offer_focus(struct view *view) * They may be called repeatably during output layout changes. */ -enum view_edge -view_edge_invert(enum view_edge edge) +enum lab_edge +view_edge_invert(enum lab_edge edge) { switch (edge) { - case VIEW_EDGE_LEFT: - return VIEW_EDGE_RIGHT; - case VIEW_EDGE_RIGHT: - return VIEW_EDGE_LEFT; - case VIEW_EDGE_UP: - return VIEW_EDGE_DOWN; - case VIEW_EDGE_DOWN: - return VIEW_EDGE_UP; - case VIEW_EDGE_CENTER: - case VIEW_EDGE_INVALID: + case LAB_EDGE_LEFT: + return LAB_EDGE_RIGHT; + case LAB_EDGE_RIGHT: + return LAB_EDGE_LEFT; + case LAB_EDGE_UP: + return LAB_EDGE_DOWN; + case LAB_EDGE_DOWN: + return LAB_EDGE_UP; + case LAB_EDGE_CENTER: + case LAB_EDGE_INVALID: default: - return VIEW_EDGE_INVALID; + return LAB_EDGE_INVALID; } } struct wlr_box view_get_edge_snap_box(struct view *view, struct output *output, - enum view_edge edge) + enum lab_edge edge) { struct wlr_box usable = output_usable_area_in_layout_coords(output); int x1 = rc.gap; @@ -456,16 +458,16 @@ view_get_edge_snap_box(struct view *view, struct output *output, int x2 = usable.width - rc.gap; int y2 = usable.height - rc.gap; - if (edge & VIEW_EDGE_RIGHT) { + if (edge & LAB_EDGE_RIGHT) { x1 = (usable.width + rc.gap) / 2; } - if (edge & VIEW_EDGE_LEFT) { + if (edge & LAB_EDGE_LEFT) { x2 = (usable.width - rc.gap) / 2; } - if (edge & VIEW_EDGE_DOWN) { + if (edge & LAB_EDGE_DOWN) { y1 = (usable.height + rc.gap) / 2; } - if (edge & VIEW_EDGE_UP) { + if (edge & LAB_EDGE_UP) { y2 = (usable.height - rc.gap) / 2; } @@ -1115,7 +1117,7 @@ view_cascade(struct view *view) void view_place_by_policy(struct view *view, bool allow_cursor, - enum view_placement_policy policy) + enum lab_placement_policy policy) { if (allow_cursor && policy == LAB_PLACE_CURSOR) { view_move_to_cursor(view); @@ -1458,7 +1460,7 @@ void view_set_untiled(struct view *view) { assert(view); - view->tiled = VIEW_EDGE_INVALID; + view->tiled = LAB_EDGE_INVALID; view->tiled_region = NULL; zfree(view->tiled_region_evacuate); view_notify_tiled(view); @@ -1569,7 +1571,7 @@ view_wants_decorations(struct view *view) } void -view_set_decorations(struct view *view, enum ssd_mode mode, bool force_ssd) +view_set_decorations(struct view *view, enum lab_ssd_mode mode, bool force_ssd) { assert(view); @@ -1584,7 +1586,7 @@ view_toggle_decorations(struct view *view) { assert(view); - enum ssd_mode mode = view_get_ssd_mode(view); + enum lab_ssd_mode mode = view_get_ssd_mode(view); if (rc.ssd_keep_border && mode == LAB_SSD_MODE_FULL) { view_set_ssd_mode(view, LAB_SSD_MODE_BORDER); } else if (mode != LAB_SSD_MODE_NONE) { @@ -1674,7 +1676,7 @@ undecorate(struct view *view) view->ssd = NULL; } -enum ssd_mode +enum lab_ssd_mode view_get_ssd_mode(struct view *view) { assert(view); @@ -1689,7 +1691,7 @@ view_get_ssd_mode(struct view *view) } void -view_set_ssd_mode(struct view *view, enum ssd_mode mode) +view_set_ssd_mode(struct view *view, enum lab_ssd_mode mode) { assert(view); @@ -1974,7 +1976,7 @@ shift_view_to_usable_1d(int size, } void -view_move_to_edge(struct view *view, enum view_edge direction, bool snap_to_windows) +view_move_to_edge(struct view *view, enum lab_edge direction, bool snap_to_windows) { assert(view); if (!output_is_usable(view->output)) { @@ -2020,16 +2022,16 @@ view_move_to_edge(struct view *view, enum view_edge direction, bool snap_to_wind /* Compute the new position in the direction of motion */ direction = view_edge_invert(direction); switch (direction) { - case VIEW_EDGE_LEFT: + case LAB_EDGE_LEFT: destination_x = left; break; - case VIEW_EDGE_RIGHT: + case LAB_EDGE_RIGHT: destination_x = right - view->pending.width; break; - case VIEW_EDGE_UP: + case LAB_EDGE_UP: destination_y = top; break; - case VIEW_EDGE_DOWN: + case LAB_EDGE_DOWN: destination_y = bottom - view_effective_height(view, /* use_pending */ true); break; @@ -2057,7 +2059,7 @@ view_move_to_edge(struct view *view, enum view_edge direction, bool snap_to_wind } void -view_grow_to_edge(struct view *view, enum view_edge direction) +view_grow_to_edge(struct view *view, enum lab_edge direction) { assert(view); /* TODO: allow grow to edge if maximized along the other axis */ @@ -2078,7 +2080,7 @@ view_grow_to_edge(struct view *view, enum view_edge direction) } void -view_shrink_to_edge(struct view *view, enum view_edge direction) +view_shrink_to_edge(struct view *view, enum lab_edge direction) { assert(view); @@ -2118,46 +2120,46 @@ view_axis_parse(const char *direction) } } -enum view_edge +enum lab_edge view_edge_parse(const char *direction, bool tiled, bool any) { if (!direction) { - return VIEW_EDGE_INVALID; + return LAB_EDGE_INVALID; } if (!strcasecmp(direction, "left")) { - return VIEW_EDGE_LEFT; + return LAB_EDGE_LEFT; } else if (!strcasecmp(direction, "up")) { - return VIEW_EDGE_UP; + return LAB_EDGE_UP; } else if (!strcasecmp(direction, "right")) { - return VIEW_EDGE_RIGHT; + return LAB_EDGE_RIGHT; } else if (!strcasecmp(direction, "down")) { - return VIEW_EDGE_DOWN; + return LAB_EDGE_DOWN; } if (any) { if (!strcasecmp(direction, "any")) { - return VIEW_EDGE_ANY; + return LAB_EDGE_ANY; } } if (tiled) { if (!strcasecmp(direction, "center")) { - return VIEW_EDGE_CENTER; + return LAB_EDGE_CENTER; } else if (!strcasecmp(direction, "up-left")) { - return VIEW_EDGE_UPLEFT; + return LAB_EDGE_UPLEFT; } else if (!strcasecmp(direction, "up-right")) { - return VIEW_EDGE_UPRIGHT; + return LAB_EDGE_UPRIGHT; } else if (!strcasecmp(direction, "down-left")) { - return VIEW_EDGE_DOWNLEFT; + return LAB_EDGE_DOWNLEFT; } else if (!strcasecmp(direction, "down-right")) { - return VIEW_EDGE_DOWNRIGHT; + return LAB_EDGE_DOWNRIGHT; } } - return VIEW_EDGE_INVALID; + return LAB_EDGE_INVALID; } -enum view_placement_policy +enum lab_placement_policy view_placement_parse(const char *policy) { if (!policy) { @@ -2178,7 +2180,7 @@ view_placement_parse(const char *policy) } void -view_snap_to_edge(struct view *view, enum view_edge edge, +view_snap_to_edge(struct view *view, enum lab_edge edge, bool across_outputs, bool store_natural_geometry) { assert(view); diff --git a/src/workspaces.c b/src/workspaces.c index 70215b80..04856a47 100644 --- a/src/workspaces.c +++ b/src/workspaces.c @@ -14,11 +14,13 @@ #include "common/graphic-helpers.h" #include "common/list.h" #include "common/mem.h" +#include "config/rcxml.h" #include "input/keyboard.h" #include "labwc.h" #include "output.h" #include "protocols/cosmic-workspaces.h" #include "protocols/ext-workspace.h" +#include "theme.h" #include "view.h" #define COSMIC_WORKSPACES_VERSION 1 diff --git a/src/xdg.c b/src/xdg.c index a67a6aea..e38038e5 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -9,6 +9,7 @@ #include "common/array.h" #include "common/macros.h" #include "common/mem.h" +#include "config/rcxml.h" #include "decorations.h" #include "foreign-toplevel/foreign.h" #include "labwc.h" @@ -66,7 +67,7 @@ xdg_toplevel_view_get_size_hints(struct view *view) static bool xdg_toplevel_view_contains_window_type(struct view *view, - enum window_type window_type) + enum lab_window_type window_type) { assert(view); @@ -78,9 +79,9 @@ xdg_toplevel_view_contains_window_type(struct view *view, || toplevel->parent; switch (window_type) { - case NET_WM_WINDOW_TYPE_NORMAL: + case LAB_WINDOW_TYPE_NORMAL: return !is_dialog; - case NET_WM_WINDOW_TYPE_DIALOG: + case LAB_WINDOW_TYPE_DIALOG: return is_dialog; default: return false; @@ -663,31 +664,31 @@ xdg_toplevel_view_notify_tiled(struct view *view) */ if (want_edge) { switch (view->tiled) { - case VIEW_EDGE_LEFT: + case LAB_EDGE_LEFT: edge = WLR_EDGE_LEFT | WLR_EDGE_TOP | WLR_EDGE_BOTTOM; break; - case VIEW_EDGE_RIGHT: + case LAB_EDGE_RIGHT: edge = WLR_EDGE_RIGHT | WLR_EDGE_TOP | WLR_EDGE_BOTTOM; break; - case VIEW_EDGE_UP: + case LAB_EDGE_UP: edge = WLR_EDGE_TOP | WLR_EDGE_LEFT | WLR_EDGE_RIGHT; break; - case VIEW_EDGE_DOWN: + case LAB_EDGE_DOWN: edge = WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT; break; - case VIEW_EDGE_UPLEFT: + case LAB_EDGE_UPLEFT: edge = WLR_EDGE_TOP | WLR_EDGE_LEFT; break; - case VIEW_EDGE_UPRIGHT: + case LAB_EDGE_UPRIGHT: edge = WLR_EDGE_TOP | WLR_EDGE_RIGHT; break; - case VIEW_EDGE_DOWNLEFT: + case LAB_EDGE_DOWNLEFT: edge = WLR_EDGE_BOTTOM | WLR_EDGE_LEFT; break; - case VIEW_EDGE_DOWNRIGHT: + case LAB_EDGE_DOWNRIGHT: edge = WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT; break; - /* TODO: VIEW_EDGE_CENTER? */ + /* TODO: LAB_EDGE_CENTER? */ default: edge = WLR_EDGE_NONE; } diff --git a/src/xwayland.c b/src/xwayland.c index c7954517..557305dc 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -41,40 +41,40 @@ static void xwayland_view_unmap(struct view *view, bool client_request); static bool xwayland_view_contains_window_type(struct view *view, - enum window_type window_type) + enum lab_window_type window_type) { /* Compile-time check that the enum types match */ - static_assert(NET_WM_WINDOW_TYPE_DESKTOP == + static_assert(LAB_WINDOW_TYPE_DESKTOP == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DESKTOP - && NET_WM_WINDOW_TYPE_DOCK == + && LAB_WINDOW_TYPE_DOCK == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DOCK - && NET_WM_WINDOW_TYPE_TOOLBAR == + && LAB_WINDOW_TYPE_TOOLBAR == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_TOOLBAR - && NET_WM_WINDOW_TYPE_MENU == + && LAB_WINDOW_TYPE_MENU == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_MENU - && NET_WM_WINDOW_TYPE_UTILITY == + && LAB_WINDOW_TYPE_UTILITY == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_UTILITY - && NET_WM_WINDOW_TYPE_SPLASH == + && LAB_WINDOW_TYPE_SPLASH == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_SPLASH - && NET_WM_WINDOW_TYPE_DIALOG == + && LAB_WINDOW_TYPE_DIALOG == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DIALOG - && NET_WM_WINDOW_TYPE_DROPDOWN_MENU == + && LAB_WINDOW_TYPE_DROPDOWN_MENU == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DROPDOWN_MENU - && NET_WM_WINDOW_TYPE_POPUP_MENU == + && LAB_WINDOW_TYPE_POPUP_MENU == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_POPUP_MENU - && NET_WM_WINDOW_TYPE_TOOLTIP == + && LAB_WINDOW_TYPE_TOOLTIP == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_TOOLTIP - && NET_WM_WINDOW_TYPE_NOTIFICATION == + && LAB_WINDOW_TYPE_NOTIFICATION == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_NOTIFICATION - && NET_WM_WINDOW_TYPE_COMBO == + && LAB_WINDOW_TYPE_COMBO == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_COMBO - && NET_WM_WINDOW_TYPE_DND == + && LAB_WINDOW_TYPE_DND == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DND - && NET_WM_WINDOW_TYPE_NORMAL == + && LAB_WINDOW_TYPE_NORMAL == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_NORMAL - && WINDOW_TYPE_LEN == + && LAB_WINDOW_TYPE_LEN == (int)WLR_XWAYLAND_NET_WM_WINDOW_TYPE_NORMAL + 1, - "enum window_type does not match wlr_xwayland_net_wm_window_type"); + "lab_window_type does not match wlr_xwayland_net_wm_window_type"); assert(view); struct wlr_xwayland_surface *surface = xwayland_surface_from_view(view); From beb3c0fa09206a6c0c59e2a51095f90a126d62db Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Thu, 21 Aug 2025 00:23:07 -0400 Subject: [PATCH 103/248] config: add comment to enum lab_motion --- include/config/types.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/config/types.h b/include/config/types.h index e8277463..246c7527 100644 --- a/include/config/types.h +++ b/include/config/types.h @@ -33,6 +33,10 @@ enum lab_edge { LAB_EDGE_DOWNRIGHT = (LAB_EDGE_DOWN | LAB_EDGE_RIGHT), }; +/** + * Indicates whether tablet tool motion events should be reported using + * absolute or relative coordinates + */ enum lab_motion { LAB_MOTION_ABSOLUTE = 0, LAB_MOTION_RELATIVE, From 1b575ce81619fd1e6c4b9e103c7dd725714403a8 Mon Sep 17 00:00:00 2001 From: 01micko <01micko@gmx.com> Date: Thu, 21 Aug 2025 20:03:33 +1000 Subject: [PATCH 104/248] docs/labnag.1.scd: fix typo --- docs/labnag.1.scd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/labnag.1.scd b/docs/labnag.1.scd index 60970141..5dc91ed8 100644 --- a/docs/labnag.1.scd +++ b/docs/labnag.1.scd @@ -14,13 +14,13 @@ _labnag_ [options...] Create a button with the text _text_ that optionally executes _action_ when pressed. Multiple buttons can be defined by providing the flag multiple times. Buttons will appear in the order they are provided from - lef to right. + left to right. *-Z, --button-dismiss* [] Create a button with the text _text_ that optionally executes _action_ when pressed, and dismisses labnag. Multiple buttons can be defined by providing the flag multiple times. Buttons will appear in the order - they are provided from lef to right. + they are provided from left to right. *-d, --debug* Enable debugging. From 48ba23fc8d0193a73c0e82a2426c8bff730940d1 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Thu, 21 Aug 2025 17:57:29 +0900 Subject: [PATCH 105/248] Add common/edge.c --- include/common/direction.h | 2 +- include/common/edge.h | 38 +++++++++++++++++++++++ include/config/types.h | 22 -------------- include/view.h | 11 +------ src/action.c | 4 +-- src/common/edge.c | 59 ++++++++++++++++++++++++++++++++++++ src/common/meson.build | 1 + src/config/rcxml.c | 2 +- src/view.c | 62 ++------------------------------------ 9 files changed, 105 insertions(+), 96 deletions(-) create mode 100644 include/common/edge.h create mode 100644 src/common/edge.c diff --git a/include/common/direction.h b/include/common/direction.h index be05d29c..a1e6530e 100644 --- a/include/common/direction.h +++ b/include/common/direction.h @@ -3,7 +3,7 @@ #define LABWC_DIRECTION_H #include -#include "config/types.h" +#include "common/edge.h" bool direction_from_edge(enum lab_edge edge, enum wlr_direction *direction); enum wlr_direction direction_get_opposite(enum wlr_direction direction); diff --git a/include/common/edge.h b/include/common/edge.h new file mode 100644 index 00000000..23b5fdde --- /dev/null +++ b/include/common/edge.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_EDGE_H +#define LABWC_EDGE_H + +#include + +/** + * Represents an edge or direction (e.g. window tiling, window motion) + */ +enum lab_edge { + LAB_EDGE_INVALID = 0, + + LAB_EDGE_LEFT = (1 << 0), + LAB_EDGE_RIGHT = (1 << 1), + LAB_EDGE_UP = (1 << 2), + LAB_EDGE_DOWN = (1 << 3), + LAB_EDGE_CENTER = (1 << 4), /* for window tiling */ + LAB_EDGE_ANY = (1 << 5), /* for window rules */ + + /* for window tiling */ + LAB_EDGE_UPLEFT = (LAB_EDGE_UP | LAB_EDGE_LEFT), + LAB_EDGE_UPRIGHT = (LAB_EDGE_UP | LAB_EDGE_RIGHT), + LAB_EDGE_DOWNLEFT = (LAB_EDGE_DOWN | LAB_EDGE_LEFT), + LAB_EDGE_DOWNRIGHT = (LAB_EDGE_DOWN | LAB_EDGE_RIGHT), +}; + +enum lab_edge lab_edge_parse(const char *direction, bool tiled, bool any); + +/** + * lab_edge_invert() - select the opposite of a provided edge + * + * Returns LAB_EDGE_INVALID for edges other than UP/DOWN/LEFT/RIGHT. + * + * @edge: edge to be inverted + */ +enum lab_edge lab_edge_invert(enum lab_edge edge); + +#endif /* LABWC_EDGE_H */ diff --git a/include/config/types.h b/include/config/types.h index 246c7527..18d08700 100644 --- a/include/config/types.h +++ b/include/config/types.h @@ -11,28 +11,6 @@ * For the full config struct, see config/rcxml.h. */ -/** - * Edges to which a view can be snapped. "Any" is used as - * a catch-all for every valid edge in order to simplify certain - * types of conditionals, but it is only valid for a selection - * of options in rc.xml. - */ -enum lab_edge { - LAB_EDGE_INVALID = 0, - - LAB_EDGE_LEFT = (1 << 0), - LAB_EDGE_RIGHT = (1 << 1), - LAB_EDGE_UP = (1 << 2), - LAB_EDGE_DOWN = (1 << 3), - LAB_EDGE_CENTER = (1 << 4), - LAB_EDGE_ANY = (1 << 5), - - LAB_EDGE_UPLEFT = (LAB_EDGE_UP | LAB_EDGE_LEFT), - LAB_EDGE_UPRIGHT = (LAB_EDGE_UP | LAB_EDGE_RIGHT), - LAB_EDGE_DOWNLEFT = (LAB_EDGE_DOWN | LAB_EDGE_LEFT), - LAB_EDGE_DOWNRIGHT = (LAB_EDGE_DOWN | LAB_EDGE_RIGHT), -}; - /** * Indicates whether tablet tool motion events should be reported using * absolute or relative coordinates diff --git a/include/view.h b/include/view.h index 2aa7df67..4efb2283 100644 --- a/include/view.h +++ b/include/view.h @@ -7,6 +7,7 @@ #include #include #include +#include "common/edge.h" #include "config.h" #include "config/types.h" @@ -422,15 +423,6 @@ void view_array_append(struct server *server, struct wl_array *views, enum view_wants_focus view_wants_focus(struct view *view); bool view_contains_window_type(struct view *view, enum lab_window_type window_type); -/** - * view_edge_invert() - select the opposite of a provided edge - * - * LAB_EDGE_CENTER and LAB_EDGE_INVALID both map to LAB_EDGE_INVALID. - * - * @edge: edge to be inverted - */ -enum lab_edge view_edge_invert(enum lab_edge edge); - /* If view is NULL, the size of SSD is not considered */ struct wlr_box view_get_edge_snap_box(struct view *view, struct output *output, enum lab_edge edge); @@ -601,7 +593,6 @@ void view_init(struct view *view); void view_destroy(struct view *view); enum view_axis view_axis_parse(const char *direction); -enum lab_edge view_edge_parse(const char *direction, bool tiled, bool any); enum lab_placement_policy view_placement_parse(const char *policy); /* xdg.c */ diff --git a/src/action.c b/src/action.c index 2722998f..a65d7371 100644 --- a/src/action.c +++ b/src/action.c @@ -347,7 +347,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char if (!strcmp(argument, "direction")) { bool tiled = (action->type == ACTION_TYPE_TOGGLE_SNAP_TO_EDGE || action->type == ACTION_TYPE_SNAP_TO_EDGE); - enum lab_edge edge = view_edge_parse(content, tiled, /*any*/ false); + enum lab_edge edge = lab_edge_parse(content, tiled, /*any*/ false); if (edge == LAB_EDGE_INVALID) { wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", action_names[action->type], argument, content); @@ -455,7 +455,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char goto cleanup; } if (!strcmp(argument, "direction")) { - enum lab_edge edge = view_edge_parse(content, + enum lab_edge edge = lab_edge_parse(content, /*tiled*/ false, /*any*/ false); if (edge == LAB_EDGE_INVALID) { wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", diff --git a/src/common/edge.c b/src/common/edge.c new file mode 100644 index 00000000..20b8eb9c --- /dev/null +++ b/src/common/edge.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include "common/edge.h" +#include + +enum lab_edge +lab_edge_parse(const char *direction, bool tiled, bool any) +{ + if (!direction) { + return LAB_EDGE_INVALID; + } + if (!strcasecmp(direction, "left")) { + return LAB_EDGE_LEFT; + } else if (!strcasecmp(direction, "up")) { + return LAB_EDGE_UP; + } else if (!strcasecmp(direction, "right")) { + return LAB_EDGE_RIGHT; + } else if (!strcasecmp(direction, "down")) { + return LAB_EDGE_DOWN; + } + + if (any) { + if (!strcasecmp(direction, "any")) { + return LAB_EDGE_ANY; + } + } + + if (tiled) { + if (!strcasecmp(direction, "center")) { + return LAB_EDGE_CENTER; + } else if (!strcasecmp(direction, "up-left")) { + return LAB_EDGE_UPLEFT; + } else if (!strcasecmp(direction, "up-right")) { + return LAB_EDGE_UPRIGHT; + } else if (!strcasecmp(direction, "down-left")) { + return LAB_EDGE_DOWNLEFT; + } else if (!strcasecmp(direction, "down-right")) { + return LAB_EDGE_DOWNRIGHT; + } + } + + return LAB_EDGE_INVALID; +} + +enum lab_edge +lab_edge_invert(enum lab_edge edge) +{ + switch (edge) { + case LAB_EDGE_LEFT: + return LAB_EDGE_RIGHT; + case LAB_EDGE_RIGHT: + return LAB_EDGE_LEFT; + case LAB_EDGE_UP: + return LAB_EDGE_DOWN; + case LAB_EDGE_DOWN: + return LAB_EDGE_UP; + default: + return LAB_EDGE_INVALID; + } +} diff --git a/src/common/meson.build b/src/common/meson.build index aa0fc413..e0b90336 100644 --- a/src/common/meson.build +++ b/src/common/meson.build @@ -3,6 +3,7 @@ labwc_sources += files( 'box.c', 'buf.c', 'dir.c', + 'edge.c', 'fd-util.c', 'file-helpers.c', 'font.c', diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 17703e68..8746836b 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -444,7 +444,7 @@ fill_action_query(struct action *action, xmlNode *node, struct view_query *query } else if (!strcasecmp(key, "omnipresent")) { query->omnipresent = parse_tristate(content); } else if (!strcasecmp(key, "tiled")) { - query->tiled = view_edge_parse(content, + query->tiled = lab_edge_parse(content, /*tiled*/ true, /*any*/ true); } else if (!strcasecmp(key, "tiled_region")) { xstrdup_replace(query->tiled_region, content); diff --git a/src/view.c b/src/view.c index 4088512a..ff863d32 100644 --- a/src/view.c +++ b/src/view.c @@ -429,25 +429,6 @@ view_offer_focus(struct view *view) * They may be called repeatably during output layout changes. */ -enum lab_edge -view_edge_invert(enum lab_edge edge) -{ - switch (edge) { - case LAB_EDGE_LEFT: - return LAB_EDGE_RIGHT; - case LAB_EDGE_RIGHT: - return LAB_EDGE_LEFT; - case LAB_EDGE_UP: - return LAB_EDGE_DOWN; - case LAB_EDGE_DOWN: - return LAB_EDGE_UP; - case LAB_EDGE_CENTER: - case LAB_EDGE_INVALID: - default: - return LAB_EDGE_INVALID; - } -} - struct wlr_box view_get_edge_snap_box(struct view *view, struct output *output, enum lab_edge edge) @@ -2020,7 +2001,7 @@ view_move_to_edge(struct view *view, enum lab_edge direction, bool snap_to_windo int destination_y = view->pending.y; /* Compute the new position in the direction of motion */ - direction = view_edge_invert(direction); + direction = lab_edge_invert(direction); switch (direction) { case LAB_EDGE_LEFT: destination_x = left; @@ -2120,45 +2101,6 @@ view_axis_parse(const char *direction) } } -enum lab_edge -view_edge_parse(const char *direction, bool tiled, bool any) -{ - if (!direction) { - return LAB_EDGE_INVALID; - } - if (!strcasecmp(direction, "left")) { - return LAB_EDGE_LEFT; - } else if (!strcasecmp(direction, "up")) { - return LAB_EDGE_UP; - } else if (!strcasecmp(direction, "right")) { - return LAB_EDGE_RIGHT; - } else if (!strcasecmp(direction, "down")) { - return LAB_EDGE_DOWN; - } - - if (any) { - if (!strcasecmp(direction, "any")) { - return LAB_EDGE_ANY; - } - } - - if (tiled) { - if (!strcasecmp(direction, "center")) { - return LAB_EDGE_CENTER; - } else if (!strcasecmp(direction, "up-left")) { - return LAB_EDGE_UPLEFT; - } else if (!strcasecmp(direction, "up-right")) { - return LAB_EDGE_UPRIGHT; - } else if (!strcasecmp(direction, "down-left")) { - return LAB_EDGE_DOWNLEFT; - } else if (!strcasecmp(direction, "down-right")) { - return LAB_EDGE_DOWNRIGHT; - } - } - - return LAB_EDGE_INVALID; -} - enum lab_placement_policy view_placement_parse(const char *policy) { @@ -2215,7 +2157,7 @@ view_snap_to_edge(struct view *view, enum lab_edge edge, } /* When switching outputs, jump to the opposite edge */ - edge = view_edge_invert(edge); + edge = lab_edge_invert(edge); } if (view->maximized != VIEW_AXIS_NONE) { From 943f5751ee008faf1ee5e745f10086c95ea5183d Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Thu, 21 Aug 2025 13:45:58 +0900 Subject: [PATCH 106/248] view: unify ssd_enabled and ssd_titlebar_hidden to ssd_mode --- include/config/types.h | 4 ++-- include/view.h | 5 ++--- src/ssd/ssd.c | 12 +++++------ src/view.c | 47 +++++++++++++++++------------------------- 4 files changed, 29 insertions(+), 39 deletions(-) diff --git a/include/config/types.h b/include/config/types.h index 18d08700..7f90b151 100644 --- a/include/config/types.h +++ b/include/config/types.h @@ -36,10 +36,10 @@ enum lab_rotation { }; enum lab_ssd_mode { - LAB_SSD_MODE_INVALID, - LAB_SSD_MODE_NONE, + LAB_SSD_MODE_NONE = 0, LAB_SSD_MODE_BORDER, LAB_SSD_MODE_FULL, + LAB_SSD_MODE_INVALID, }; enum lab_tristate { diff --git a/include/view.h b/include/view.h index 4efb2283..e7dce886 100644 --- a/include/view.h +++ b/include/view.h @@ -170,8 +170,7 @@ struct view { bool mapped; bool been_mapped; - bool ssd_enabled; - bool ssd_titlebar_hidden; + enum lab_ssd_mode ssd_mode; enum ssd_preference ssd_preference; bool shaded; bool minimized; @@ -534,7 +533,7 @@ bool view_is_tiled(struct view *view); bool view_is_tiled_and_notify_tiled(struct view *view); bool view_is_floating(struct view *view); void view_move_to_workspace(struct view *view, struct workspace *workspace); -enum lab_ssd_mode view_get_ssd_mode(struct view *view); +bool view_titlebar_visible(struct view *view); void view_set_ssd_mode(struct view *view, enum lab_ssd_mode mode); void view_set_decorations(struct view *view, enum lab_ssd_mode mode, bool force_ssd); void view_toggle_fullscreen(struct view *view); diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index 97338c26..db706085 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -32,7 +32,7 @@ ssd_thickness(struct view *view) * in border-only deco mode as view->ssd would only be set * after ssd_create() returns. */ - if (!view->ssd_enabled || view->fullscreen) { + if (!view->ssd_mode || view->fullscreen) { return (struct border){ 0 }; } @@ -40,7 +40,7 @@ ssd_thickness(struct view *view) if (view->maximized == VIEW_AXIS_BOTH) { struct border thickness = { 0 }; - if (!view->ssd_titlebar_hidden) { + if (view_titlebar_visible(view)) { thickness.top += theme->titlebar_height; } return thickness; @@ -53,7 +53,7 @@ ssd_thickness(struct view *view) .left = theme->border_width, }; - if (view->ssd_titlebar_hidden) { + if (!view_titlebar_visible(view)) { thickness.top -= theme->titlebar_height; } return thickness; @@ -89,14 +89,14 @@ static enum ssd_part_type get_resizing_type(const struct ssd *ssd, struct wlr_cursor *cursor) { struct view *view = ssd ? ssd->view : NULL; - if (!view || !cursor || !view->ssd_enabled || view->fullscreen) { + if (!view || !cursor || !view->ssd_mode || view->fullscreen) { return LAB_SSD_NONE; } struct wlr_box view_box = view->current; view_box.height = view_effective_height(view, /* use_pending */ false); - if (!view->ssd_titlebar_hidden) { + if (view_titlebar_visible(view)) { /* If the titlebar is visible, consider it part of the view */ int titlebar_height = view->server->theme->titlebar_height; view_box.y -= titlebar_height; @@ -250,7 +250,7 @@ ssd_create(struct view *view, bool active) */ ssd_titlebar_create(ssd); ssd_border_create(ssd); - if (view->ssd_titlebar_hidden) { + if (!view_titlebar_visible(view)) { /* Ensure we keep the old state on Reconfigure or when exiting fullscreen */ ssd_set_titlebar(ssd, false); } diff --git a/src/view.c b/src/view.c index ff863d32..75f8bad1 100644 --- a/src/view.c +++ b/src/view.c @@ -80,6 +80,7 @@ view_query_create(void) struct view_query *query = znew(*query); query->window_type = -1; query->maximized = VIEW_AXIS_INVALID; + query->decoration = LAB_SSD_MODE_INVALID; return query; } @@ -206,8 +207,8 @@ view_matches_query(struct view *view, struct view_query *query) } } - enum lab_ssd_mode decor = view_get_ssd_mode(view); - if (query->decoration != LAB_SSD_MODE_INVALID && query->decoration != decor) { + if (query->decoration != LAB_SSD_MODE_INVALID + && query->decoration != view->ssd_mode) { return false; } @@ -1316,7 +1317,7 @@ view_apply_maximized_geometry(struct view *view) &natural.x, &natural.y); } - if (view->ssd_enabled) { + if (view->ssd_mode) { struct border border = ssd_thickness(view); box.x += border.left; box.y += border.top; @@ -1557,7 +1558,7 @@ view_set_decorations(struct view *view, enum lab_ssd_mode mode, bool force_ssd) assert(view); if (force_ssd || view_wants_decorations(view) - || mode < view_get_ssd_mode(view)) { + || mode < view->ssd_mode) { view_set_ssd_mode(view, mode); } } @@ -1567,10 +1568,9 @@ view_toggle_decorations(struct view *view) { assert(view); - enum lab_ssd_mode mode = view_get_ssd_mode(view); - if (rc.ssd_keep_border && mode == LAB_SSD_MODE_FULL) { + if (rc.ssd_keep_border && view->ssd_mode == LAB_SSD_MODE_FULL) { view_set_ssd_mode(view, LAB_SSD_MODE_BORDER); - } else if (mode != LAB_SSD_MODE_NONE) { + } else if (view->ssd_mode != LAB_SSD_MODE_NONE) { view_set_ssd_mode(view, LAB_SSD_MODE_NONE); } else { view_set_ssd_mode(view, LAB_SSD_MODE_FULL); @@ -1657,18 +1657,10 @@ undecorate(struct view *view) view->ssd = NULL; } -enum lab_ssd_mode -view_get_ssd_mode(struct view *view) +bool +view_titlebar_visible(struct view *view) { - assert(view); - - if (!view->ssd_enabled) { - return LAB_SSD_MODE_NONE; - } else if (view->ssd_titlebar_hidden) { - return LAB_SSD_MODE_BORDER; - } else { - return LAB_SSD_MODE_FULL; - } + return view->ssd_mode == LAB_SSD_MODE_FULL; } void @@ -1677,7 +1669,7 @@ view_set_ssd_mode(struct view *view, enum lab_ssd_mode mode) assert(view); if (view->shaded || view->fullscreen - || mode == view_get_ssd_mode(view)) { + || mode == view->ssd_mode) { return; } @@ -1685,12 +1677,11 @@ view_set_ssd_mode(struct view *view, enum lab_ssd_mode mode) * Set these first since they are referenced * within the call tree of ssd_create() and ssd_thickness() */ - view->ssd_enabled = mode != LAB_SSD_MODE_NONE; - view->ssd_titlebar_hidden = mode != LAB_SSD_MODE_FULL; + view->ssd_mode = mode; - if (view->ssd_enabled) { + if (mode) { decorate(view); - ssd_set_titlebar(view->ssd, !view->ssd_titlebar_hidden); + ssd_set_titlebar(view->ssd, view_titlebar_visible(view)); } else { undecorate(view); } @@ -1718,7 +1709,7 @@ set_fullscreen(struct view *view, bool fullscreen) } /* Hide decorations when going fullscreen */ - if (fullscreen && view->ssd_enabled) { + if (fullscreen && view->ssd_mode) { undecorate(view); } @@ -1730,7 +1721,7 @@ set_fullscreen(struct view *view, bool fullscreen) wl_signal_emit_mutable(&view->events.fullscreened, NULL); /* Re-show decorations when no longer fullscreen */ - if (!fullscreen && view->ssd_enabled) { + if (!fullscreen && view->ssd_mode) { decorate(view); } @@ -2393,7 +2384,7 @@ void view_reload_ssd(struct view *view) { assert(view); - if (view->ssd_enabled && !view->fullscreen) { + if (view->ssd_mode && !view->fullscreen) { undecorate(view); decorate(view); } @@ -2416,7 +2407,7 @@ view_toggle_keybinds(struct view *view) assert(view); view->inhibits_keybinds = !view->inhibits_keybinds; - if (view->ssd_enabled) { + if (view->ssd_mode) { ssd_enable_keybind_inhibit_indicator(view->ssd, view->inhibits_keybinds); } @@ -2498,7 +2489,7 @@ view_set_shade(struct view *view, bool shaded) } /* Views without a title-bar or SSD cannot be shaded */ - if (shaded && (!view->ssd || view->ssd_titlebar_hidden)) { + if (shaded && (!view->ssd || !view_titlebar_visible(view))) { return; } From 888dbedeed416ee18e73129fc5bb0a23eebc31d8 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Thu, 21 Aug 2025 13:56:37 +0900 Subject: [PATCH 107/248] ssd: allow hiding titlebar on maximization hides the titlebar when a window is maximized. Co-authored-by: @CosmicFusion --- docs/labwc-config.5.scd | 6 ++++++ docs/rc.xml.all | 1 + include/config/rcxml.h | 1 + src/config/rcxml.c | 7 +++++++ src/ssd/ssd.c | 6 ++++++ src/view.c | 4 ++++ 6 files changed, 25 insertions(+) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index 860391c9..e86fb75f 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -171,6 +171,7 @@ this is for compatibility with Openbox. ``` server + titlebar 0 no no @@ -186,6 +187,11 @@ this is for compatibility with Openbox. that it is not always possible to turn off client side decorations. Default is server. +** [titlebar|none] + Specify how server side decorations are shown for maximized windows. + *titlebar* shows titlebar above a maximized window. *none* shows no server + side decorations around a maximized window. Default is titlebar. + ** The distance in pixels between windows and output edges when using movement actions, for example MoveToEdge. Default is 0. diff --git a/docs/rc.xml.all b/docs/rc.xml.all index 3084aee5..91dc7791 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -11,6 +11,7 @@ server + titlebar 0 no no diff --git a/include/config/rcxml.h b/include/config/rcxml.h index a7eb22bd..cb3dd1b9 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -66,6 +66,7 @@ struct rcxml { /* core */ bool xdg_shell_server_side_deco; + bool hide_maximized_window_titlebar; int gap; enum adaptive_sync_mode adaptive_sync; enum tearing_mode allow_tearing; diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 8746836b..52e02c4b 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -1094,6 +1094,12 @@ entry(xmlNode *node, char *nodename, char *content) } else { rc.xdg_shell_server_side_deco = true; } + } else if (!strcasecmp(nodename, "maximizedDecoration.core")) { + if (!strcasecmp(content, "titlebar")) { + rc.hide_maximized_window_titlebar = false; + } else if (!strcasecmp(content, "none")) { + rc.hide_maximized_window_titlebar = true; + } } else if (!strcmp(nodename, "gap.core")) { rc.gap = atoi(content); } else if (!strcasecmp(nodename, "adaptiveSync.core")) { @@ -1370,6 +1376,7 @@ rcxml_init(void) rc.placement_cascade_offset_y = 0; rc.xdg_shell_server_side_deco = true; + rc.hide_maximized_window_titlebar = false; rc.show_title = true; rc.title_layout_loaded = false; rc.ssd_keep_border = true; diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index db706085..3d0dd7a0 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -312,6 +312,12 @@ ssd_update_geometry(struct ssd *ssd) || ssd->state.was_squared != squared || ssd->state.was_omnipresent != view->visible_on_all_workspaces; + /* + * (Un)maximization updates titlebar visibility with + * maximizedDecoration=none + */ + ssd_set_titlebar(ssd, view_titlebar_visible(view)); + if (update_extents) { ssd_extents_update(ssd); } diff --git a/src/view.c b/src/view.c index 75f8bad1..a81b03ad 100644 --- a/src/view.c +++ b/src/view.c @@ -1660,6 +1660,10 @@ undecorate(struct view *view) bool view_titlebar_visible(struct view *view) { + if (view->maximized == VIEW_AXIS_BOTH + && rc.hide_maximized_window_titlebar) { + return false; + } return view->ssd_mode == LAB_SSD_MODE_FULL; } From ebd39dfe0d1ded5af933075f5400909c9d2a2c99 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Sat, 23 Aug 2025 10:44:26 -0400 Subject: [PATCH 108/248] view: respect client-initiated resize of non-maximized axis When implementing single-axis maximize some time ago, I made the simplifying assumption that a view couldn't be resized while maximized (even in only one axis). And indeed for compositor-initiated resize, we always unmaximize the view first. However, I didn't account for the client resizing the non-maximized axis, which we can't (and shouldn't) prevent. When this happens, we should also update the natural geometry of that single axis so that we don't undo the resize when un-maximizing. P.S. xdg-shell clients resizing the *maximized* axis is still an unsolved problem, exacerbated by the fact that xdg-shell protocol doesn't allow clients to even know about single-axis maximize. P.P.S. the view_invalidate_last_layout_geometry() logic may need similar updates, I'm not sure. --- src/interactive.c | 4 ++-- src/view.c | 27 +++++++++++++++++++++++---- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/interactive.c b/src/interactive.c index b50d27a4..8a8903da 100644 --- a/src/interactive.c +++ b/src/interactive.c @@ -90,9 +90,9 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges) return; } + /* Store natural geometry at start of move */ + view_store_natural_geometry(view); if (view_is_floating(view)) { - /* Store natural geometry at start of move */ - view_store_natural_geometry(view); view_invalidate_last_layout_geometry(view); } diff --git a/src/view.c b/src/view.c index a81b03ad..8ddef7d4 100644 --- a/src/view.c +++ b/src/view.c @@ -971,8 +971,11 @@ void view_store_natural_geometry(struct view *view) { assert(view); - if (!view_is_floating(view)) { - /* Do not overwrite the stored geometry with special cases */ + /* + * Do not overwrite the stored geometry if fullscreen or tiled. + * Maximized views are handled on a per-axis basis (see below). + */ + if (view->fullscreen || view_is_tiled(view)) { return; } @@ -983,7 +986,14 @@ view_store_natural_geometry(struct view *view) * xdg-toplevel configure event, which means the application should * choose its own size. */ - view->natural_geometry = view->pending; + if (!(view->maximized & VIEW_AXIS_HORIZONTAL)) { + view->natural_geometry.x = view->pending.x; + view->natural_geometry.width = view->pending.width; + } + if (!(view->maximized & VIEW_AXIS_VERTICAL)) { + view->natural_geometry.y = view->pending.y; + view->natural_geometry.height = view->pending.height; + } } int @@ -1472,11 +1482,20 @@ view_maximize(struct view *view, enum view_axis axis, */ interactive_cancel(view); if (store_natural_geometry && view_is_floating(view)) { - view_store_natural_geometry(view); view_invalidate_last_layout_geometry(view); } } + /* + * Update natural geometry for any axis that wasn't already + * maximized. This is needed even when unmaximizing, because in + * single-axis cases the client may have resized the other axis + * while one axis was maximized. + */ + if (store_natural_geometry) { + view_store_natural_geometry(view); + } + /* * When natural geometry is unknown (0x0) for an xdg-shell view, * we normally send a configure event of 0x0 to get the client's From 55ee96761a047de6592f18ecdc9c3e4bff06ea37 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 25 Aug 2025 18:54:22 +0900 Subject: [PATCH 109/248] window-rules: fix window rules not being applied In 943f5751, I initialized heap-allocated `view_query` used for `If` actions with `decoration=LAB_SSD_MODE_INVALID`, but I forgot to do that for stack-allocated `view_query` used for window rules. --- src/view.c | 1 + src/window-rules.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/view.c b/src/view.c index 8ddef7d4..8c697daf 100644 --- a/src/view.c +++ b/src/view.c @@ -78,6 +78,7 @@ struct view_query * view_query_create(void) { struct view_query *query = znew(*query); + /* Must be synced with view_matches_criteria() in window-rules.c */ query->window_type = -1; query->maximized = VIEW_AXIS_INVALID; query->decoration = LAB_SSD_MODE_INVALID; diff --git a/src/window-rules.c b/src/window-rules.c index ed980efc..8283e801 100644 --- a/src/window-rules.c +++ b/src/window-rules.c @@ -36,7 +36,9 @@ view_matches_criteria(struct window_rule *rule, struct view *view) .window_type = rule->window_type, .sandbox_engine = rule->sandbox_engine, .sandbox_app_id = rule->sandbox_app_id, + /* Must be synced with view_query_create() */ .maximized = VIEW_AXIS_INVALID, + .decoration = LAB_SSD_MODE_INVALID, }; if (rule->match_once && other_instances_exist(view, &query)) { From 657c08aaa1c139ae13561e0fbd939c0355fa2c15 Mon Sep 17 00:00:00 2001 From: Rainer Kuemmerle Date: Sat, 8 Mar 2025 19:28:57 +0100 Subject: [PATCH 110/248] action: add toggle for GoToDesktop Adds an option "toogle" to GoToDesktop. In case the target is already where we are, we go back to the last desktop instead. Example of rc.xml 1 yes --- docs/labwc-actions.5.scd | 5 ++++- src/action.c | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/docs/labwc-actions.5.scd b/docs/labwc-actions.5.scd index 7aa78507..aa273111 100644 --- a/docs/labwc-actions.5.scd +++ b/docs/labwc-actions.5.scd @@ -278,7 +278,7 @@ Actions are used in menus and keyboard/mouse bindings. Resizes active window size to width and height of the output when the window size exceeds the output size. -** +** Switch to workspace. *to* The workspace to switch to. Supported values are "current", "last", @@ -288,6 +288,9 @@ Actions are used in menus and keyboard/mouse bindings. *wrap* [yes|no] Wrap around from last desktop to first, and vice versa. Default yes. + *toggle* [yes|no] Toggle to “last” if already on the workspace that + would be the actual destination. Default no. + ** Send active window to workspace. diff --git a/src/action.c b/src/action.c index a65d7371..6f75537e 100644 --- a/src/action.c +++ b/src/action.c @@ -440,6 +440,11 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char action_arg_add_bool(action, argument, parse_bool(content, true)); goto cleanup; } + if (!strcmp(argument, "toggle")) { + action_arg_add_bool( + action, argument, parse_bool(content, false)); + goto cleanup; + } break; case ACTION_TYPE_TOGGLE_SNAP_TO_REGION: case ACTION_TYPE_SNAP_TO_REGION: @@ -1228,6 +1233,13 @@ run_action(struct view *view, struct server *server, struct action *action, */ struct workspace *target_workspace = workspaces_find( server->workspaces.current, to, wrap); + if (action->type == ACTION_TYPE_GO_TO_DESKTOP) { + bool toggle = action_get_bool(action, "toggle", false); + if (target_workspace == server->workspaces.current + && toggle) { + target_workspace = server->workspaces.last; + } + } if (!target_workspace) { break; } From 4d1be7eada7a72f67051b4b6c4339dd8664bd7fa Mon Sep 17 00:00:00 2001 From: David Barr Date: Tue, 26 Aug 2025 21:08:31 +0100 Subject: [PATCH 111/248] menu: add brackets around minimised window titles in client-list menu (#3002) --- src/menu/menu.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/menu/menu.c b/src/menu/menu.c index ddd31f8b..4e483989 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -907,8 +907,11 @@ update_client_list_combined_menu(struct server *server) if (view == server->active_view) { buf_add(&buffer, "*"); } - buf_add(&buffer, title); - + if (view->minimized) { + buf_add_fmt(&buffer, "(%s)", title); + } else { + buf_add(&buffer, title); + } item = item_create(menu, buffer.data, NULL, /*show arrow*/ false); item->client_list_view = view; From ef766d16f03132777b730732ab9a78fe8be9d827 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Tue, 26 Aug 2025 20:27:34 -0400 Subject: [PATCH 112/248] common: flesh out enum lab_edge and prefer over wlr_edges/wlr_direction I like the new common/edge.h. I don't like how inconsistently we use it. Current situation: - enum wlr_edges and wlr_direction are designed to be used as bitset, and are defined compatibly - enum lab_edge is *also* designed to be used as bitset, but incompatible with the others (LEFT/RIGHT come before UP/DOWN) - we use an inconsistent mix of all three *AND* uint32_t (usually with the WLR_EDGE constants rather than the LAB_EDGE constants), and convert between them on an ad-hoc basis, sometimes implicitly Let's clean this up: - reorder enum lab_edge to be compatible with the two wlr enums (check this by static_assert) - use TOP/BOTTOM naming rather than UP/DOWN (matches wlr_edges) - add constants for the remaining possible combinations of the 4 edges - use lab_edge for all internal edge/direction fields, consistently - add lab_edge_is_cardinal() as a sanity check before casting to enum wlr_direction, and then eliminate all of direction.c/h Instead of "enum wlr_edges direction", we now have "enum lab_edge direction" which is not that much better. At least we are now clear that we're overloading one enum with two meanings. --- include/common/direction.h | 11 ----- include/common/edge.h | 69 +++++++++++++++++++++----- include/edges.h | 5 +- include/input/cursor.h | 14 +++--- include/labwc.h | 7 +-- include/snap-constraints.h | 12 ++--- include/ssd.h | 5 +- include/view.h | 2 +- src/action.c | 11 +++-- src/common/direction.c | 44 ----------------- src/common/edge.c | 55 ++++++++++++++++----- src/common/meson.build | 1 - src/edges.c | 99 +++++++++++++++----------------------- src/input/cursor.c | 48 ++++++++---------- src/interactive.c | 18 +++---- src/output.c | 13 ++--- src/overlay.c | 16 +++--- src/resistance.c | 2 +- src/snap-constraints.c | 38 +++++++-------- src/snap.c | 32 ++++++------ src/ssd/ssd.c | 20 ++++---- src/theme.c | 35 +++++--------- src/view-impl-common.c | 6 +-- src/view.c | 12 ++--- src/xdg.c | 37 ++++++-------- src/xwayland.c | 2 +- 26 files changed, 294 insertions(+), 320 deletions(-) delete mode 100644 include/common/direction.h delete mode 100644 src/common/direction.c diff --git a/include/common/direction.h b/include/common/direction.h deleted file mode 100644 index a1e6530e..00000000 --- a/include/common/direction.h +++ /dev/null @@ -1,11 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef LABWC_DIRECTION_H -#define LABWC_DIRECTION_H - -#include -#include "common/edge.h" - -bool direction_from_edge(enum lab_edge edge, enum wlr_direction *direction); -enum wlr_direction direction_get_opposite(enum wlr_direction direction); - -#endif /* LABWC_DIRECTION_H */ diff --git a/include/common/edge.h b/include/common/edge.h index 23b5fdde..07d517f5 100644 --- a/include/common/edge.h +++ b/include/common/edge.h @@ -2,34 +2,77 @@ #ifndef LABWC_EDGE_H #define LABWC_EDGE_H -#include +#include /** - * Represents an edge or direction (e.g. window tiling, window motion) + * Unified/overloaded enum representing edges, corners, and directions. + * Used in many different contexts (moving, resizing, tiling) and with + * somewhat different semantics depending on context. + * + * Examples: + * - LAB_EDGE_TOP can also mean "up" or "north". + * - LAB_EDGES_TOP_LEFT can mean "top left corner" or "northwest". + * + * The enum is designed to be used as a bitset, and combinations of + * edges typically mean what you'd expect from the context. For example, + * LAB_EDGES_TOP_LEFT is used when resizing a view from its top-left + * corner, or when tiling a view in the top-left corner of an output. + * + * All 16 possible combinations of TOP/BOTTOM/LEFT/RIGHT are listed for + * completeness. Not all combinations make sense in all contexts. + * + * LAB_EDGE_NONE is sometimes used to mean "invalid". + * + * LAB_EDGE_ANY means "any edge or combination of edges (except NONE)" + * and is distinct from LAB_EDGE_ALL (which means all 4 edges). + * + * LAB_EDGE_TOP/BOTTOM/LEFT/RIGHT match the corresponding values of + * enum wlr_edges and enum wlr_direction, so that conversion between + * enums can be done with a simple cast. */ enum lab_edge { - LAB_EDGE_INVALID = 0, + LAB_EDGE_NONE = 0, - LAB_EDGE_LEFT = (1 << 0), - LAB_EDGE_RIGHT = (1 << 1), - LAB_EDGE_UP = (1 << 2), - LAB_EDGE_DOWN = (1 << 3), + LAB_EDGE_TOP = (1 << 0), /* or UP */ + LAB_EDGE_BOTTOM = (1 << 1), /* or DOWN */ + LAB_EDGE_LEFT = (1 << 2), + LAB_EDGE_RIGHT = (1 << 3), LAB_EDGE_CENTER = (1 << 4), /* for window tiling */ LAB_EDGE_ANY = (1 << 5), /* for window rules */ - /* for window tiling */ - LAB_EDGE_UPLEFT = (LAB_EDGE_UP | LAB_EDGE_LEFT), - LAB_EDGE_UPRIGHT = (LAB_EDGE_UP | LAB_EDGE_RIGHT), - LAB_EDGE_DOWNLEFT = (LAB_EDGE_DOWN | LAB_EDGE_LEFT), - LAB_EDGE_DOWNRIGHT = (LAB_EDGE_DOWN | LAB_EDGE_RIGHT), + /* corners or ordinal directions (NW/NE/SW/SE) */ + LAB_EDGES_TOP_LEFT = (LAB_EDGE_TOP | LAB_EDGE_LEFT), + LAB_EDGES_TOP_RIGHT = (LAB_EDGE_TOP | LAB_EDGE_RIGHT), + LAB_EDGES_BOTTOM_LEFT = (LAB_EDGE_BOTTOM | LAB_EDGE_LEFT), + LAB_EDGES_BOTTOM_RIGHT = (LAB_EDGE_BOTTOM | LAB_EDGE_RIGHT), + + /* opposite edges */ + LAB_EDGES_TOP_BOTTOM = (LAB_EDGE_TOP | LAB_EDGE_BOTTOM), + LAB_EDGES_LEFT_RIGHT = (LAB_EDGE_LEFT | LAB_EDGE_RIGHT), + + /* all 4 edges */ + LAB_EDGES_ALL = (LAB_EDGE_TOP | LAB_EDGE_BOTTOM | + LAB_EDGE_LEFT | LAB_EDGE_RIGHT), + + /* 3-edge combinations (for completeness) */ + LAB_EDGES_EXCEPT_TOP = (LAB_EDGES_ALL ^ LAB_EDGE_TOP), + LAB_EDGES_EXCEPT_BOTTOM = (LAB_EDGES_ALL ^ LAB_EDGE_BOTTOM), + LAB_EDGES_EXCEPT_LEFT = (LAB_EDGES_ALL ^ LAB_EDGE_LEFT), + LAB_EDGES_EXCEPT_RIGHT = (LAB_EDGES_ALL ^ LAB_EDGE_RIGHT), }; enum lab_edge lab_edge_parse(const char *direction, bool tiled, bool any); +/** + * Returns true if edge is TOP, BOTTOM, LEFT, or RIGHT + * (i.e. one of the four cardinal directions N/S/W/E) + */ +bool lab_edge_is_cardinal(enum lab_edge edge); + /** * lab_edge_invert() - select the opposite of a provided edge * - * Returns LAB_EDGE_INVALID for edges other than UP/DOWN/LEFT/RIGHT. + * Returns LAB_EDGE_NONE for edges other than TOP/BOTTOM/LEFT/RIGHT. * * @edge: edge to be inverted */ diff --git a/include/edges.h b/include/edges.h index 38aeb897..439b5a34 100644 --- a/include/edges.h +++ b/include/edges.h @@ -4,7 +4,7 @@ #include #include -#include +#include "common/edge.h" #include "common/macros.h" struct border; @@ -113,9 +113,10 @@ void edges_adjust_move_coords(struct view *view, struct border edges, int *x, int *y, bool use_pending); void edges_adjust_resize_geom(struct view *view, struct border edges, - uint32_t resize_edges, struct wlr_box *geom, bool use_pending); + enum lab_edge resize_edges, struct wlr_box *geom, bool use_pending); bool edges_traverse_edge(struct edge current, struct edge target, struct edge edge); void edges_calculate_visibility(struct server *server, struct view *ignored_view); + #endif /* LABWC_EDGES_H */ diff --git a/include/input/cursor.h b/include/input/cursor.h index c49606e2..094bc04f 100644 --- a/include/input/cursor.h +++ b/include/input/cursor.h @@ -3,7 +3,7 @@ #define LABWC_CURSOR_H #include -#include +#include "common/edge.h" #include "ssd.h" struct view; @@ -11,7 +11,6 @@ struct seat; struct server; struct wlr_surface; struct wlr_scene_node; -enum wl_pointer_button_state; /* Cursors used internally by labwc */ enum lab_cursors { @@ -85,20 +84,19 @@ void cursor_set_visible(struct seat *seat, bool visible); * This is mostly important when either resizing a window using a * keyboard modifier or when using the Resize action from a keybind. */ -uint32_t cursor_get_resize_edges(struct wlr_cursor *cursor, +enum lab_edge cursor_get_resize_edges(struct wlr_cursor *cursor, struct cursor_context *ctx); /** - * cursor_get_from_edge - translate wlroots edge enum to lab_cursor enum - * @resize_edges - WLR_EDGE_ combination like WLR_EDGE_TOP | WLR_EDGE_RIGHT + * cursor_get_from_edge - translate lab_edge enum to lab_cursor enum + * @resize_edges - edge(s) being resized * - * Returns LAB_CURSOR_DEFAULT on WLR_EDGE_NONE * Returns the appropriate lab_cursors enum if @resize_edges * is one of the 4 corners or one of the 4 edges. * - * Asserts on invalid edge combinations like WLR_EDGE_LEFT | WLR_EDGE_RIGHT + * Returns LAB_CURSOR_DEFAULT on any other value. */ -enum lab_cursors cursor_get_from_edge(uint32_t resize_edges); +enum lab_cursors cursor_get_from_edge(enum lab_edge resize_edges); /** * cursor_update_focus - update cursor focus, may update the cursor icon diff --git a/include/labwc.h b/include/labwc.h index a4971e45..0629a8b8 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -209,7 +209,7 @@ struct server { double grab_x, grab_y; /* View geometry when interactive move/resize is requested */ struct wlr_box grab_box; - uint32_t resize_edges; + enum lab_edge resize_edges; /* * 'active_view' is generally the view with keyboard-focus, updated with @@ -426,7 +426,8 @@ void seat_focus_override_end(struct seat *seat); */ void interactive_anchor_to_cursor(struct server *server, struct wlr_box *geo); -void interactive_begin(struct view *view, enum input_mode mode, uint32_t edges); +void interactive_begin(struct view *view, enum input_mode mode, + enum lab_edge edges); void interactive_finish(struct view *view); void interactive_cancel(struct view *view); @@ -434,7 +435,7 @@ void interactive_cancel(struct view *view); * Returns the edge to snap a window to. * For example, if the output-relative cursor position (x,y) fulfills * x <= () and y <= (), - * then edge1=LAB_EDGE_UP and edge2=LAB_EDGE_LEFT. + * then edge1=LAB_EDGE_TOP and edge2=LAB_EDGE_LEFT. * The value of (edge1|edge2) can be passed to view_snap_to_edge(). */ bool edge_from_cursor(struct seat *seat, struct output **dest_output, diff --git a/include/snap-constraints.h b/include/snap-constraints.h index 8ad32a1b..c7ed7f8a 100644 --- a/include/snap-constraints.h +++ b/include/snap-constraints.h @@ -2,21 +2,19 @@ #ifndef LABWC_SNAP_CONSTRAINTS_H #define LABWC_SNAP_CONSTRAINTS_H -#include - -#include "common/border.h" -#include "view.h" +#include "common/edge.h" +struct view; struct wlr_box; -void snap_constraints_set(struct view *view, - enum wlr_edges direction, struct wlr_box geom); +void snap_constraints_set(struct view *view, enum lab_edge direction, + struct wlr_box geom); void snap_constraints_invalidate(struct view *view); void snap_constraints_update(struct view *view); struct wlr_box snap_constraints_effective(struct view *view, - enum wlr_edges direction, bool use_pending); + enum lab_edge direction, bool use_pending); #endif /* LABWC_SNAP_CONSTRAINTS_H */ diff --git a/include/ssd.h b/include/ssd.h index 5b7de1d5..cf0d3e24 100644 --- a/include/ssd.h +++ b/include/ssd.h @@ -2,8 +2,9 @@ #ifndef LABWC_SSD_H #define LABWC_SSD_H -#include #include "common/border.h" +#include "common/edge.h" +#include "config/types.h" struct wlr_cursor; @@ -101,7 +102,7 @@ struct view *ssd_button_get_view(const struct ssd_button *button); /* Public SSD helpers */ enum ssd_part_type ssd_get_part_type(const struct ssd *ssd, struct wlr_scene_node *node, struct wlr_cursor *cursor); -uint32_t ssd_resize_edges(enum ssd_part_type type); +enum lab_edge ssd_resize_edges(enum ssd_part_type type); bool ssd_part_contains(enum ssd_part_type whole, enum ssd_part_type candidate); enum lab_ssd_mode ssd_mode_parse(const char *mode); diff --git a/include/view.h b/include/view.h index e7dce886..2767f980 100644 --- a/include/view.h +++ b/include/view.h @@ -180,7 +180,7 @@ struct view { enum lab_tristate force_tearing; bool visible_on_all_workspaces; enum lab_edge tiled; - uint32_t edges_visible; /* enum wlr_edges bitset */ + enum lab_edge edges_visible; bool inhibits_keybinds; /* also inhibits mousebinds */ xkb_layout_index_t keyboard_layout; diff --git a/src/action.c b/src/action.c index 6f75537e..eda9138c 100644 --- a/src/action.c +++ b/src/action.c @@ -348,7 +348,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char bool tiled = (action->type == ACTION_TYPE_TOGGLE_SNAP_TO_EDGE || action->type == ACTION_TYPE_SNAP_TO_EDGE); enum lab_edge edge = lab_edge_parse(content, tiled, /*any*/ false); - if (edge == LAB_EDGE_INVALID) { + if (edge == LAB_EDGE_NONE) { wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", action_names[action->type], argument, content); } else { @@ -462,7 +462,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char if (!strcmp(argument, "direction")) { enum lab_edge edge = lab_edge_parse(content, /*tiled*/ false, /*any*/ false); - if (edge == LAB_EDGE_INVALID) { + if (edge == LAB_EDGE_NONE) { wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", action_names[action->type], argument, content); } else { @@ -921,7 +921,7 @@ get_target_output(struct output *output, struct server *server, target = output_from_name(server, output_name); } else { enum lab_edge edge = - action_get_int(action, "direction", LAB_EDGE_INVALID); + action_get_int(action, "direction", LAB_EDGE_NONE); bool wrap = action_get_bool(action, "wrap", false); target = output_get_adjacent(output, edge, wrap); } @@ -1144,7 +1144,8 @@ run_action(struct view *view, struct server *server, struct action *action, break; case ACTION_TYPE_MOVE: if (view) { - interactive_begin(view, LAB_INPUT_STATE_MOVE, 0); + interactive_begin(view, LAB_INPUT_STATE_MOVE, + LAB_EDGE_NONE); } break; case ACTION_TYPE_RAISE: @@ -1159,7 +1160,7 @@ run_action(struct view *view, struct server *server, struct action *action, break; case ACTION_TYPE_RESIZE: if (view) { - uint32_t resize_edges = cursor_get_resize_edges( + enum lab_edge resize_edges = cursor_get_resize_edges( server->seat.cursor, ctx); interactive_begin(view, LAB_INPUT_STATE_RESIZE, resize_edges); diff --git a/src/common/direction.c b/src/common/direction.c deleted file mode 100644 index df1c9299..00000000 --- a/src/common/direction.c +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include "common/direction.h" -#include -#include -#include "view.h" - -bool -direction_from_edge(enum lab_edge edge, enum wlr_direction *direction) -{ - switch (edge) { - case LAB_EDGE_LEFT: - *direction = WLR_DIRECTION_LEFT; - return true; - case LAB_EDGE_RIGHT: - *direction = WLR_DIRECTION_RIGHT; - return true; - case LAB_EDGE_UP: - *direction = WLR_DIRECTION_UP; - return true; - case LAB_EDGE_DOWN: - *direction = WLR_DIRECTION_DOWN; - return true; - default: - return false; - } -} - -enum wlr_direction -direction_get_opposite(enum wlr_direction direction) -{ - switch (direction) { - case WLR_DIRECTION_RIGHT: - return WLR_DIRECTION_LEFT; - case WLR_DIRECTION_LEFT: - return WLR_DIRECTION_RIGHT; - case WLR_DIRECTION_DOWN: - return WLR_DIRECTION_UP; - case WLR_DIRECTION_UP: - return WLR_DIRECTION_DOWN; - default: - assert(0); /* Unreachable */ - return WLR_DIRECTION_UP; - } -} diff --git a/src/common/edge.c b/src/common/edge.c index 20b8eb9c..c241396b 100644 --- a/src/common/edge.c +++ b/src/common/edge.c @@ -1,21 +1,36 @@ // SPDX-License-Identifier: GPL-2.0-only #include "common/edge.h" +#include #include +#include +#include + +static_assert((int)LAB_EDGE_TOP == (int)WLR_EDGE_TOP + && (int)LAB_EDGE_BOTTOM == (int)WLR_EDGE_BOTTOM + && (int)LAB_EDGE_LEFT == (int)WLR_EDGE_LEFT + && (int)LAB_EDGE_RIGHT == (int)WLR_EDGE_RIGHT, + "enum lab_edge does not match enum wlr_edges"); + +static_assert((int)LAB_EDGE_TOP == (int)WLR_DIRECTION_UP + && (int)LAB_EDGE_BOTTOM == (int)WLR_DIRECTION_DOWN + && (int)LAB_EDGE_LEFT == (int)WLR_DIRECTION_LEFT + && (int)LAB_EDGE_RIGHT == (int)WLR_DIRECTION_RIGHT, + "enum lab_edge does not match enum wlr_direction"); enum lab_edge lab_edge_parse(const char *direction, bool tiled, bool any) { if (!direction) { - return LAB_EDGE_INVALID; + return LAB_EDGE_NONE; } if (!strcasecmp(direction, "left")) { return LAB_EDGE_LEFT; } else if (!strcasecmp(direction, "up")) { - return LAB_EDGE_UP; + return LAB_EDGE_TOP; } else if (!strcasecmp(direction, "right")) { return LAB_EDGE_RIGHT; } else if (!strcasecmp(direction, "down")) { - return LAB_EDGE_DOWN; + return LAB_EDGE_BOTTOM; } if (any) { @@ -28,17 +43,31 @@ lab_edge_parse(const char *direction, bool tiled, bool any) if (!strcasecmp(direction, "center")) { return LAB_EDGE_CENTER; } else if (!strcasecmp(direction, "up-left")) { - return LAB_EDGE_UPLEFT; + return LAB_EDGES_TOP_LEFT; } else if (!strcasecmp(direction, "up-right")) { - return LAB_EDGE_UPRIGHT; + return LAB_EDGES_TOP_RIGHT; } else if (!strcasecmp(direction, "down-left")) { - return LAB_EDGE_DOWNLEFT; + return LAB_EDGES_BOTTOM_LEFT; } else if (!strcasecmp(direction, "down-right")) { - return LAB_EDGE_DOWNRIGHT; + return LAB_EDGES_BOTTOM_RIGHT; } } - return LAB_EDGE_INVALID; + return LAB_EDGE_NONE; +} + +bool +lab_edge_is_cardinal(enum lab_edge edge) +{ + switch (edge) { + case LAB_EDGE_TOP: + case LAB_EDGE_BOTTOM: + case LAB_EDGE_LEFT: + case LAB_EDGE_RIGHT: + return true; + default: + return false; + } } enum lab_edge @@ -49,11 +78,11 @@ lab_edge_invert(enum lab_edge edge) return LAB_EDGE_RIGHT; case LAB_EDGE_RIGHT: return LAB_EDGE_LEFT; - case LAB_EDGE_UP: - return LAB_EDGE_DOWN; - case LAB_EDGE_DOWN: - return LAB_EDGE_UP; + case LAB_EDGE_TOP: + return LAB_EDGE_BOTTOM; + case LAB_EDGE_BOTTOM: + return LAB_EDGE_TOP; default: - return LAB_EDGE_INVALID; + return LAB_EDGE_NONE; } } diff --git a/src/common/meson.build b/src/common/meson.build index e0b90336..306a2ba7 100644 --- a/src/common/meson.build +++ b/src/common/meson.build @@ -1,5 +1,4 @@ labwc_sources += files( - 'direction.c', 'box.c', 'buf.c', 'dir.c', diff --git a/src/edges.c b/src/edges.c index 94c541b5..2e322433 100644 --- a/src/edges.c +++ b/src/edges.c @@ -40,32 +40,32 @@ edges_initialize(struct border *edges) } static inline struct edge -build_edge(struct border region, enum wlr_edges direction, int pad) +build_edge(struct border region, enum lab_edge direction, int pad) { struct edge edge = { 0 }; switch (direction) { - case WLR_EDGE_LEFT: + case LAB_EDGE_LEFT: edge.offset = clipped_sub(region.left, pad); edge.min = region.top; edge.max = region.bottom; break; - case WLR_EDGE_RIGHT: + case LAB_EDGE_RIGHT: edge.offset = clipped_add(region.right, pad); edge.min = region.top; edge.max = region.bottom; break; - case WLR_EDGE_TOP: + case LAB_EDGE_TOP: edge.offset = clipped_sub(region.top, pad); edge.min = region.left; edge.max = region.right; break; - case WLR_EDGE_BOTTOM: + case LAB_EDGE_BOTTOM: edge.offset = clipped_add(region.bottom, pad); edge.min = region.left; edge.max = region.right; break; - case WLR_EDGE_NONE: + default: /* Should never be reached */ wlr_log(WLR_ERROR, "invalid direction"); abort(); @@ -75,14 +75,14 @@ build_edge(struct border region, enum wlr_edges direction, int pad) } static inline bool -is_lesser(enum wlr_edges direction) +is_lesser(enum lab_edge direction) { - return direction == WLR_EDGE_LEFT || direction == WLR_EDGE_TOP; + return direction == LAB_EDGE_LEFT || direction == LAB_EDGE_TOP; } static inline struct edge -build_visible_edge(struct border region, enum wlr_edges direction, - int pad, uint32_t edges_visible) +build_visible_edge(struct border region, enum lab_edge direction, + int pad, enum lab_edge edges_visible) { struct edge edge = build_edge(region, direction, pad); @@ -97,7 +97,7 @@ static void validate_single_region_edge(int *valid_edge, struct border view, struct border target, struct border region, edge_validator_t validator, - enum wlr_edges direction, uint32_t edges_visible) + enum lab_edge direction, enum lab_edge edges_visible) { /* * When a view snaps to another while moving to its target, it can do @@ -115,26 +115,8 @@ validate_single_region_edge(int *valid_edge, * the region borders for aligned edges only. */ - enum wlr_edges opposing = WLR_EDGE_NONE; - - switch (direction) { - case WLR_EDGE_TOP: - opposing = WLR_EDGE_BOTTOM; - break; - case WLR_EDGE_BOTTOM: - opposing = WLR_EDGE_TOP; - break; - case WLR_EDGE_LEFT: - opposing = WLR_EDGE_RIGHT; - break; - case WLR_EDGE_RIGHT: - opposing = WLR_EDGE_LEFT; - break; - case WLR_EDGE_NONE: - /* Should never be reached */ - assert(false); - return; - } + enum lab_edge opposing = lab_edge_invert(direction); + assert(opposing != LAB_EDGE_NONE); validator(valid_edge, build_edge(view, direction, 0), @@ -147,31 +129,31 @@ validate_single_region_edge(int *valid_edge, static void validate_edges(struct border *valid_edges, struct border view, struct border target, - struct border region, uint32_t edges_visible, + struct border region, enum lab_edge edges_visible, edge_validator_t validator) { /* Check for edges encountered during movement of left edge */ validate_single_region_edge(&valid_edges->left, - view, target, region, validator, WLR_EDGE_LEFT, edges_visible); + view, target, region, validator, LAB_EDGE_LEFT, edges_visible); /* Check for edges encountered during movement of right edge */ validate_single_region_edge(&valid_edges->right, - view, target, region, validator, WLR_EDGE_RIGHT, edges_visible); + view, target, region, validator, LAB_EDGE_RIGHT, edges_visible); /* Check for edges encountered during movement of top edge */ validate_single_region_edge(&valid_edges->top, - view, target, region, validator, WLR_EDGE_TOP, edges_visible); + view, target, region, validator, LAB_EDGE_TOP, edges_visible); /* Check for edges encountered during movement of bottom edge */ validate_single_region_edge(&valid_edges->bottom, - view, target, region, validator, WLR_EDGE_BOTTOM, edges_visible); + view, target, region, validator, LAB_EDGE_BOTTOM, edges_visible); } static void validate_single_output_edge(int *valid_edge, struct border view, struct border target, struct border region, edge_validator_t validator, - enum wlr_edges direction) + enum lab_edge direction) { static struct border unbounded = { .top = INT_MIN, @@ -226,24 +208,24 @@ validate_output_edges(struct border *valid_edges, /* Left edge encounters a half-infinite region to the left of the output */ validate_single_output_edge(&valid_edges->left, - view, target, output, validator, WLR_EDGE_LEFT); + view, target, output, validator, LAB_EDGE_LEFT); /* Right edge encounters a half-infinite region to the right of the output */ validate_single_output_edge(&valid_edges->right, - view, target, output, validator, WLR_EDGE_RIGHT); + view, target, output, validator, LAB_EDGE_RIGHT); /* Top edge encounters a half-infinite region above the output */ validate_single_output_edge(&valid_edges->top, - view, target, output, validator, WLR_EDGE_TOP); + view, target, output, validator, LAB_EDGE_TOP); /* Bottom edge encounters a half-infinite region below the output */ validate_single_output_edge(&valid_edges->bottom, - view, target, output, validator, WLR_EDGE_BOTTOM); + view, target, output, validator, LAB_EDGE_BOTTOM); } -static uint32_t +static enum lab_edge compute_edges_visible(const struct wlr_box *view_size, const pixman_box32_t *view_rect, const pixman_region32_t *available) @@ -258,19 +240,19 @@ compute_edges_visible(const struct wlr_box *view_size, const pixman_box32_t *rects = pixman_region32_rectangles(&intersection, &nrects); - uint32_t edges_visible = 0; + enum lab_edge edges_visible = LAB_EDGE_NONE; for (int i = 0; i < nrects; i++) { if (rects[i].x1 == view_rect->x1) { - edges_visible |= WLR_EDGE_LEFT; + edges_visible |= LAB_EDGE_LEFT; } if (rects[i].y1 == view_rect->y1) { - edges_visible |= WLR_EDGE_TOP; + edges_visible |= LAB_EDGE_TOP; } if (rects[i].x2 == view_rect->x2) { - edges_visible |= WLR_EDGE_RIGHT; + edges_visible |= LAB_EDGE_RIGHT; } if (rects[i].y2 == view_rect->y2) { - edges_visible |= WLR_EDGE_BOTTOM; + edges_visible |= LAB_EDGE_BOTTOM; } } pixman_region32_fini(&intersection); @@ -294,11 +276,10 @@ subtract_view_from_space(struct view *view, pixman_region32_t *available) switch (overlap) { case PIXMAN_REGION_IN: - view->edges_visible = WLR_EDGE_TOP | WLR_EDGE_RIGHT - | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT; + view->edges_visible = LAB_EDGES_ALL; break; case PIXMAN_REGION_OUT: - view->edges_visible = 0; + view->edges_visible = LAB_EDGE_NONE; return; case PIXMAN_REGION_PART: view->edges_visible = compute_edges_visible( @@ -411,11 +392,10 @@ edges_find_neighbors(struct border *nearest_edges, struct view *view, continue; } - uint32_t edges_visible = ignore_hidden ? v->edges_visible : - WLR_EDGE_TOP | WLR_EDGE_LEFT - | WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT; + enum lab_edge edges_visible = + ignore_hidden ? v->edges_visible : LAB_EDGES_ALL; - if (edges_visible == 0) { + if (edges_visible == LAB_EDGE_NONE) { continue; } @@ -543,7 +523,8 @@ edges_adjust_move_coords(struct view *view, struct border edges, void edges_adjust_resize_geom(struct view *view, struct border edges, - uint32_t resize_edges, struct wlr_box *geom, bool use_pending) + enum lab_edge resize_edges, struct wlr_box *geom, + bool use_pending) { assert(view); @@ -556,24 +537,24 @@ edges_adjust_resize_geom(struct view *view, struct border edges, * any valid nearest edge in the corresponding direction. */ - if (resize_edges & WLR_EDGE_LEFT) { + if (resize_edges & LAB_EDGE_LEFT) { if (BOUNDED_INT(edges.left)) { geom->x = edges.left + border.left + rc.gap; geom->width = view_geom->width + view_geom->x - geom->x; } - } else if (resize_edges & WLR_EDGE_RIGHT) { + } else if (resize_edges & LAB_EDGE_RIGHT) { if (BOUNDED_INT(edges.right)) { geom->width = edges.right - view_geom->x - border.right - rc.gap; } } - if (resize_edges & WLR_EDGE_TOP) { + if (resize_edges & LAB_EDGE_TOP) { if (BOUNDED_INT(edges.top)) { geom->y = edges.top + border.top + rc.gap; geom->height = view_geom->height + view_geom->y - geom->y; } - } else if (resize_edges & WLR_EDGE_BOTTOM) { + } else if (resize_edges & LAB_EDGE_BOTTOM) { if (BOUNDED_INT(edges.bottom)) { geom->height = edges.bottom - view_geom->y - border.bottom - rc.gap; diff --git a/src/input/cursor.c b/src/input/cursor.c index 22d0e384..cd7b5f34 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -88,40 +88,34 @@ static_assert( "X11 cursor names are out of sync"); enum lab_cursors -cursor_get_from_edge(uint32_t resize_edges) +cursor_get_from_edge(enum lab_edge resize_edges) { switch (resize_edges) { - case WLR_EDGE_NONE: - return LAB_CURSOR_DEFAULT; - case WLR_EDGE_TOP | WLR_EDGE_LEFT: + case LAB_EDGES_TOP_LEFT: return LAB_CURSOR_RESIZE_NW; - case WLR_EDGE_TOP: + case LAB_EDGE_TOP: return LAB_CURSOR_RESIZE_N; - case WLR_EDGE_TOP | WLR_EDGE_RIGHT: + case LAB_EDGES_TOP_RIGHT: return LAB_CURSOR_RESIZE_NE; - case WLR_EDGE_RIGHT: + case LAB_EDGE_RIGHT: return LAB_CURSOR_RESIZE_E; - case WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT: + case LAB_EDGES_BOTTOM_RIGHT: return LAB_CURSOR_RESIZE_SE; - case WLR_EDGE_BOTTOM: + case LAB_EDGE_BOTTOM: return LAB_CURSOR_RESIZE_S; - case WLR_EDGE_BOTTOM | WLR_EDGE_LEFT: + case LAB_EDGES_BOTTOM_LEFT: return LAB_CURSOR_RESIZE_SW; - case WLR_EDGE_LEFT: + case LAB_EDGE_LEFT: return LAB_CURSOR_RESIZE_W; default: - wlr_log(WLR_ERROR, - "Failed to resolve wlroots edge %u to cursor name", resize_edges); - assert(false); + return LAB_CURSOR_DEFAULT; } - - return LAB_CURSOR_DEFAULT; } static enum lab_cursors cursor_get_from_ssd(enum ssd_part_type view_area) { - uint32_t resize_edges = ssd_resize_edges(view_area); + enum lab_edge resize_edges = ssd_resize_edges(view_area); return cursor_get_from_edge(resize_edges); } @@ -342,32 +336,32 @@ process_cursor_resize(struct server *server, uint32_t time) struct view *view = server->grabbed_view; struct wlr_box new_view_geo = view->current; - if (server->resize_edges & WLR_EDGE_TOP) { + if (server->resize_edges & LAB_EDGE_TOP) { /* Shift y to anchor bottom edge when resizing top */ new_view_geo.y = server->grab_box.y + dy; new_view_geo.height = server->grab_box.height - dy; - } else if (server->resize_edges & WLR_EDGE_BOTTOM) { + } else if (server->resize_edges & LAB_EDGE_BOTTOM) { new_view_geo.height = server->grab_box.height + dy; } - if (server->resize_edges & WLR_EDGE_LEFT) { + if (server->resize_edges & LAB_EDGE_LEFT) { /* Shift x to anchor right edge when resizing left */ new_view_geo.x = server->grab_box.x + dx; new_view_geo.width = server->grab_box.width - dx; - } else if (server->resize_edges & WLR_EDGE_RIGHT) { + } else if (server->resize_edges & LAB_EDGE_RIGHT) { new_view_geo.width = server->grab_box.width + dx; } resistance_resize_apply(view, &new_view_geo); view_adjust_size(view, &new_view_geo.width, &new_view_geo.height); - if (server->resize_edges & WLR_EDGE_TOP) { + if (server->resize_edges & LAB_EDGE_TOP) { /* After size adjustments, make sure to anchor bottom edge */ new_view_geo.y = server->grab_box.y + server->grab_box.height - new_view_geo.height; } - if (server->resize_edges & WLR_EDGE_LEFT) { + if (server->resize_edges & LAB_EDGE_LEFT) { /* After size adjustments, make sure to anchor bottom right */ new_view_geo.x = server->grab_box.x + server->grab_box.width - new_view_geo.width; @@ -582,18 +576,18 @@ cursor_update_common(struct server *server, struct cursor_context *ctx, return false; } -uint32_t +enum lab_edge cursor_get_resize_edges(struct wlr_cursor *cursor, struct cursor_context *ctx) { - uint32_t resize_edges = ssd_resize_edges(ctx->type); + enum lab_edge resize_edges = ssd_resize_edges(ctx->type); if (ctx->view && !resize_edges) { struct wlr_box box = ctx->view->current; resize_edges |= (int)cursor->x < box.x + box.width / 2 ? - WLR_EDGE_LEFT : WLR_EDGE_RIGHT; + LAB_EDGE_LEFT : LAB_EDGE_RIGHT; resize_edges |= (int)cursor->y < box.y + box.height / 2 ? - WLR_EDGE_TOP : WLR_EDGE_BOTTOM; + LAB_EDGE_TOP : LAB_EDGE_BOTTOM; } return resize_edges; } diff --git a/src/interactive.c b/src/interactive.c index 8a8903da..517b898e 100644 --- a/src/interactive.c +++ b/src/interactive.c @@ -55,7 +55,7 @@ interactive_anchor_to_cursor(struct server *server, struct wlr_box *geo) } void -interactive_begin(struct view *view, enum input_mode mode, uint32_t edges) +interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges) { /* * This function sets up an interactive move or resize operation, where @@ -170,8 +170,8 @@ edge_from_cursor(struct seat *seat, struct output **dest_output, enum lab_edge *edge1, enum lab_edge *edge2) { *dest_output = NULL; - *edge1 = LAB_EDGE_INVALID; - *edge2 = LAB_EDGE_INVALID; + *edge1 = LAB_EDGE_NONE; + *edge2 = LAB_EDGE_NONE; if (!view_is_floating(seat->server->grabbed_view)) { return false; @@ -202,9 +202,9 @@ edge_from_cursor(struct seat *seat, struct output **dest_output, int right = area->x + area->width - cursor_x; if (top < rc.snap_edge_range) { - *edge1 = LAB_EDGE_UP; + *edge1 = LAB_EDGE_TOP; } else if (bottom < rc.snap_edge_range) { - *edge1 = LAB_EDGE_DOWN; + *edge1 = LAB_EDGE_BOTTOM; } else if (left < rc.snap_edge_range) { *edge1 = LAB_EDGE_LEFT; } else if (right < rc.snap_edge_range) { @@ -213,7 +213,7 @@ edge_from_cursor(struct seat *seat, struct output **dest_output, return false; } - if (*edge1 == LAB_EDGE_UP || *edge1 == LAB_EDGE_DOWN) { + if (*edge1 == LAB_EDGE_TOP || *edge1 == LAB_EDGE_BOTTOM) { if (left < rc.snap_edge_corner_range) { *edge2 = LAB_EDGE_LEFT; } else if (right < rc.snap_edge_corner_range) { @@ -221,9 +221,9 @@ edge_from_cursor(struct seat *seat, struct output **dest_output, } } else if (*edge1 == LAB_EDGE_LEFT || *edge1 == LAB_EDGE_RIGHT) { if (top < rc.snap_edge_corner_range) { - *edge2 = LAB_EDGE_UP; + *edge2 = LAB_EDGE_TOP; } else if (bottom < rc.snap_edge_corner_range) { - *edge2 = LAB_EDGE_DOWN; + *edge2 = LAB_EDGE_BOTTOM; } } @@ -246,7 +246,7 @@ snap_to_edge(struct view *view) * Don't store natural geometry here (it was * stored already in interactive_begin()) */ - if (edge == LAB_EDGE_UP && rc.snap_top_maximize) { + if (edge == LAB_EDGE_TOP && rc.snap_top_maximize) { /* */ view_maximize(view, VIEW_AXIS_BOTH, /*store_natural_geometry*/ false); diff --git a/src/output.c b/src/output.c index 6633df2d..f683c2de 100644 --- a/src/output.c +++ b/src/output.c @@ -23,7 +23,6 @@ #include #include #include -#include "common/direction.h" #include "common/macros.h" #include "common/mem.h" #include "common/scene-helpers.h" @@ -986,8 +985,8 @@ output_get_adjacent(struct output *output, enum lab_edge edge, bool wrap) return NULL; } - enum wlr_direction direction; - if (!direction_from_edge(edge, &direction)) { + /* Allow only up/down/left/right */ + if (!lab_edge_is_cardinal(edge)) { return NULL; } @@ -999,16 +998,18 @@ output_get_adjacent(struct output *output, enum lab_edge edge, bool wrap) struct wlr_output *new_output = NULL; struct wlr_output *current_output = output->wlr_output; struct wlr_output_layout *layout = output->server->output_layout; - new_output = wlr_output_layout_adjacent_output(layout, direction, - current_output, lx, ly); + /* Cast from enum lab_edge to enum wlr_direction is safe */ + new_output = wlr_output_layout_adjacent_output(layout, + (enum wlr_direction)edge, current_output, lx, ly); /* * Optionally wrap around from top-to-bottom or left-to-right, and vice * versa. */ if (wrap && !new_output) { + enum lab_edge opposite = lab_edge_invert(edge); new_output = wlr_output_layout_farthest_output(layout, - direction_get_opposite(direction), current_output, lx, ly); + (enum wlr_direction)opposite, current_output, lx, ly); } /* diff --git a/src/overlay.c b/src/overlay.c index c0c47d0e..0c2235d3 100644 --- a/src/overlay.c +++ b/src/overlay.c @@ -2,7 +2,6 @@ #include "overlay.h" #include #include -#include "common/direction.h" #include "common/lab-scene-rect.h" #include "config/rcxml.h" #include "labwc.h" @@ -97,7 +96,7 @@ inactivate_overlay(struct overlay *overlay) &overlay->edge_rect.tree->node, false); } overlay->active.region = NULL; - overlay->active.edge = LAB_EDGE_INVALID; + overlay->active.edge = LAB_EDGE_NONE; overlay->active.output = NULL; if (overlay->timer) { wl_event_source_timer_update(overlay->timer, 0); @@ -120,7 +119,7 @@ show_region_overlay(struct seat *seat, struct region *region) static struct wlr_box get_edge_snap_box(enum lab_edge edge, struct output *output) { - if (edge == LAB_EDGE_UP && rc.snap_top_maximize) { + if (edge == LAB_EDGE_TOP && rc.snap_top_maximize) { return output_usable_area_in_layout_coords(output); } else { return view_get_edge_snap_box(NULL, output, edge); @@ -131,7 +130,7 @@ static int handle_edge_overlay_timeout(void *data) { struct seat *seat = data; - assert(seat->overlay.active.edge != LAB_EDGE_INVALID + assert(seat->overlay.active.edge != LAB_EDGE_NONE && seat->overlay.active.output); struct wlr_box box = get_edge_snap_box(seat->overlay.active.edge, seat->overlay.active.output); @@ -143,12 +142,13 @@ static bool edge_has_adjacent_output_from_cursor(struct seat *seat, struct output *output, enum lab_edge edge) { - enum wlr_direction dir; - if (!direction_from_edge(edge, &dir)) { + /* Allow only up/down/left/right */ + if (!lab_edge_is_cardinal(edge)) { return false; } + /* Cast from enum lab_edge to enum wlr_direction is safe */ return wlr_output_layout_adjacent_output( - seat->server->output_layout, dir, + seat->server->output_layout, (enum wlr_direction)edge, output->wlr_output, seat->cursor->x, seat->cursor->y); } @@ -159,7 +159,7 @@ show_edge_overlay(struct seat *seat, enum lab_edge edge1, enum lab_edge edge2, if (!rc.snap_overlay_enabled) { return; } - uint32_t edge = edge1 | edge2; + enum lab_edge edge = edge1 | edge2; if (seat->overlay.active.edge == edge && seat->overlay.active.output == output) { return; diff --git a/src/resistance.c b/src/resistance.c index 2f09c0e9..4566c5d0 100644 --- a/src/resistance.c +++ b/src/resistance.c @@ -167,7 +167,7 @@ resistance_resize_apply(struct view *view, struct wlr_box *new_geom) edges_initialize(&next_edges); /* Use a constrained, effective geometry for snapping if appropriate */ - enum wlr_edges resize_edges = view->server->resize_edges; + enum lab_edge resize_edges = view->server->resize_edges; struct wlr_box origin = snap_constraints_effective(view, resize_edges, /* use_pending */ false); diff --git a/src/snap-constraints.c b/src/snap-constraints.c index 04eed5f7..d579a387 100644 --- a/src/snap-constraints.c +++ b/src/snap-constraints.c @@ -39,7 +39,7 @@ static struct { bool pending; int vertical_offset; int horizontal_offset; - enum wlr_edges resize_edges; + enum lab_edge resize_edges; struct wlr_box geom; } last_snap_hit; @@ -50,12 +50,12 @@ snap_constraints_reset(void) last_snap_hit.pending = false; last_snap_hit.horizontal_offset = INT_MIN; last_snap_hit.vertical_offset = INT_MIN; - last_snap_hit.resize_edges = WLR_EDGE_NONE; + last_snap_hit.resize_edges = LAB_EDGE_NONE; memset(&last_snap_hit.geom, 0, sizeof(last_snap_hit.geom)); } static bool -snap_constraints_are_valid(struct view *view, enum wlr_edges resize_edges) +snap_constraints_are_valid(struct view *view, enum lab_edge resize_edges) { assert(view); @@ -70,20 +70,20 @@ snap_constraints_are_valid(struct view *view, enum wlr_edges resize_edges) } /* Cache is not valid if edge offsets are invalid */ - if (resize_edges & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)) { + if (resize_edges & LAB_EDGES_LEFT_RIGHT) { if (!BOUNDED_INT(last_snap_hit.horizontal_offset)) { return false; } - if ((resize_edges & WLR_EDGE_LEFT) && (resize_edges & WLR_EDGE_RIGHT)) { + if ((resize_edges & LAB_EDGE_LEFT) && (resize_edges & LAB_EDGE_RIGHT)) { return false; } - } else if (resize_edges & (WLR_EDGE_TOP | WLR_EDGE_BOTTOM)) { + } else if (resize_edges & LAB_EDGES_TOP_BOTTOM) { if (!BOUNDED_INT(last_snap_hit.vertical_offset)) { return false; } - if ((resize_edges & WLR_EDGE_TOP) && (resize_edges & WLR_EDGE_BOTTOM)) { + if ((resize_edges & LAB_EDGE_TOP) && (resize_edges & LAB_EDGE_BOTTOM)) { return false; } } else { @@ -95,24 +95,24 @@ snap_constraints_are_valid(struct view *view, enum wlr_edges resize_edges) } void -snap_constraints_set(struct view *view, - enum wlr_edges resize_edges, struct wlr_box geom) +snap_constraints_set(struct view *view, enum lab_edge resize_edges, + struct wlr_box geom) { assert(view); /* Set horizontal offset when resizing horizontal edges */ last_snap_hit.horizontal_offset = INT_MIN; - if (resize_edges & WLR_EDGE_LEFT) { + if (resize_edges & LAB_EDGE_LEFT) { last_snap_hit.horizontal_offset = geom.x; - } else if (resize_edges & WLR_EDGE_RIGHT) { + } else if (resize_edges & LAB_EDGE_RIGHT) { last_snap_hit.horizontal_offset = geom.x + geom.width; } /* Set vertical offset when resizing vertical edges */ last_snap_hit.vertical_offset = INT_MIN; - if (resize_edges & WLR_EDGE_TOP) { + if (resize_edges & LAB_EDGE_TOP) { last_snap_hit.vertical_offset = geom.y; - } else if (resize_edges & WLR_EDGE_BOTTOM) { + } else if (resize_edges & LAB_EDGE_BOTTOM) { last_snap_hit.vertical_offset = geom.y + geom.height; } @@ -166,8 +166,8 @@ snap_constraints_update(struct view *view) } struct wlr_box -snap_constraints_effective(struct view *view, - enum wlr_edges resize_edges, bool use_pending) +snap_constraints_effective(struct view *view, enum lab_edge resize_edges, + bool use_pending) { assert(view); @@ -181,15 +181,15 @@ snap_constraints_effective(struct view *view, /* Override changing edge with constrained value */ struct wlr_box geom = real_geom; - if (resize_edges & WLR_EDGE_LEFT) { + if (resize_edges & LAB_EDGE_LEFT) { geom.x = last_snap_hit.horizontal_offset; - } else if (resize_edges & WLR_EDGE_RIGHT) { + } else if (resize_edges & LAB_EDGE_RIGHT) { geom.width = last_snap_hit.horizontal_offset - geom.x; } - if (resize_edges & WLR_EDGE_TOP) { + if (resize_edges & LAB_EDGE_TOP) { geom.y = last_snap_hit.vertical_offset; - } else if (resize_edges & WLR_EDGE_BOTTOM) { + } else if (resize_edges & LAB_EDGE_BOTTOM) { geom.height = last_snap_hit.vertical_offset - geom.y; } diff --git a/src/snap.c b/src/snap.c index ea9d5d46..fcb8f335 100644 --- a/src/snap.c +++ b/src/snap.c @@ -93,13 +93,13 @@ snap_move_to_edge(struct view *view, enum lab_edge direction, return; } break; - case LAB_EDGE_UP: + case LAB_EDGE_TOP: target.y = usable.y + ssd.top + rc.gap; if (target.y >= view->pending.y) { return; } break; - case LAB_EDGE_DOWN: + case LAB_EDGE_BOTTOM: target.y = usable.y + usable.height - rc.gap - ssd.bottom - view_effective_height(view, /* use_pending */ true); if (target.y <= view->pending.y) { @@ -149,29 +149,29 @@ snap_grow_to_next_edge(struct view *view, struct border ssd = ssd_thickness(view); struct wlr_box usable = output_usable_area_in_layout_coords(output); - uint32_t resize_edges; + enum lab_edge resize_edges; /* First try to grow the view to the relevant edge of its output. */ switch (direction) { case LAB_EDGE_LEFT: geo->x = usable.x + ssd.left + rc.gap; geo->width = view->pending.x + view->pending.width - geo->x; - resize_edges = WLR_EDGE_LEFT; + resize_edges = LAB_EDGE_LEFT; break; case LAB_EDGE_RIGHT: geo->width = usable.x + usable.width - rc.gap - ssd.right - view->pending.x; - resize_edges = WLR_EDGE_RIGHT; + resize_edges = LAB_EDGE_RIGHT; break; - case LAB_EDGE_UP: + case LAB_EDGE_TOP: geo->y = usable.y + ssd.top + rc.gap; geo->height = view->pending.y + view->pending.height - geo->y; - resize_edges = WLR_EDGE_TOP; + resize_edges = LAB_EDGE_TOP; break; - case LAB_EDGE_DOWN: + case LAB_EDGE_BOTTOM: geo->height = usable.y + usable.height - rc.gap - ssd.bottom - view->pending.y; - resize_edges = WLR_EDGE_BOTTOM; + resize_edges = LAB_EDGE_BOTTOM; break; default: return; @@ -220,7 +220,7 @@ snap_shrink_to_next_edge(struct view *view, assert(!view->shaded); *geo = view->pending; - uint32_t resize_edges; + enum lab_edge resize_edges; int min_width = view_get_min_width(); /* @@ -232,20 +232,20 @@ snap_shrink_to_next_edge(struct view *view, case LAB_EDGE_RIGHT: geo->width = MAX(geo->width / 2, min_width); geo->x = view->pending.x + view->pending.width - geo->width; - resize_edges = WLR_EDGE_LEFT; + resize_edges = LAB_EDGE_LEFT; break; case LAB_EDGE_LEFT: geo->width = MAX(geo->width / 2, min_width); - resize_edges = WLR_EDGE_RIGHT; + resize_edges = LAB_EDGE_RIGHT; break; - case LAB_EDGE_DOWN: + case LAB_EDGE_BOTTOM: geo->height = MAX(geo->height / 2, LAB_MIN_VIEW_HEIGHT); geo->y = view->pending.y + view->pending.height - geo->height; - resize_edges = WLR_EDGE_TOP; + resize_edges = LAB_EDGE_TOP; break; - case LAB_EDGE_UP: + case LAB_EDGE_TOP: geo->height = MAX(geo->height / 2, LAB_MIN_VIEW_HEIGHT); - resize_edges = WLR_EDGE_BOTTOM; + resize_edges = LAB_EDGE_BOTTOM; break; default: return; diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index 3d0dd7a0..7a45f37e 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -205,28 +205,28 @@ ssd_get_part_type(const struct ssd *ssd, struct wlr_scene_node *node, return resizing_type != LAB_SSD_NONE ? resizing_type : part_type; } -uint32_t +enum lab_edge ssd_resize_edges(enum ssd_part_type type) { switch (type) { case LAB_SSD_PART_TOP: - return WLR_EDGE_TOP; + return LAB_EDGE_TOP; case LAB_SSD_PART_RIGHT: - return WLR_EDGE_RIGHT; + return LAB_EDGE_RIGHT; case LAB_SSD_PART_BOTTOM: - return WLR_EDGE_BOTTOM; + return LAB_EDGE_BOTTOM; case LAB_SSD_PART_LEFT: - return WLR_EDGE_LEFT; + return LAB_EDGE_LEFT; case LAB_SSD_PART_CORNER_TOP_LEFT: - return WLR_EDGE_TOP | WLR_EDGE_LEFT; + return LAB_EDGES_TOP_LEFT; case LAB_SSD_PART_CORNER_TOP_RIGHT: - return WLR_EDGE_RIGHT | WLR_EDGE_TOP; + return LAB_EDGES_TOP_RIGHT; case LAB_SSD_PART_CORNER_BOTTOM_RIGHT: - return WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT; + return LAB_EDGES_BOTTOM_RIGHT; case LAB_SSD_PART_CORNER_BOTTOM_LEFT: - return WLR_EDGE_BOTTOM | WLR_EDGE_LEFT; + return LAB_EDGES_BOTTOM_LEFT; default: - return WLR_EDGE_NONE; + return LAB_EDGE_NONE; } } diff --git a/src/theme.c b/src/theme.c index 2d5ac12b..e16325e8 100644 --- a/src/theme.c +++ b/src/theme.c @@ -43,10 +43,9 @@ struct button { uint8_t state_set; }; -enum corner { - LAB_CORNER_UNKNOWN = 0, - LAB_CORNER_TOP_LEFT, - LAB_CORNER_TOP_RIGHT, +enum rounded_corner { + ROUNDED_CORNER_TOP_LEFT, + ROUNDED_CORNER_TOP_RIGHT }; struct rounded_corner_ctx { @@ -55,7 +54,7 @@ struct rounded_corner_ctx { double line_width; cairo_pattern_t *fill_pattern; float *border_color; - enum corner corner; + enum rounded_corner corner; }; #define zero_array(arr) memset(arr, 0, sizeof(arr)) @@ -1110,10 +1109,6 @@ theme_read(struct theme *theme, struct wl_list *paths) static struct lab_data_buffer * rounded_rect(struct rounded_corner_ctx *ctx) { - if (ctx->corner == LAB_CORNER_UNKNOWN) { - return NULL; - } - double w = ctx->box->width; double h = ctx->box->height; double r = ctx->radius; @@ -1147,20 +1142,18 @@ rounded_rect(struct rounded_corner_ctx *ctx) cairo_set_line_width(cairo, 0.0); cairo_new_sub_path(cairo); switch (ctx->corner) { - case LAB_CORNER_TOP_LEFT: + case ROUNDED_CORNER_TOP_LEFT: cairo_arc(cairo, r, r, r, 180 * deg, 270 * deg); cairo_line_to(cairo, w, 0); cairo_line_to(cairo, w, h); cairo_line_to(cairo, 0, h); break; - case LAB_CORNER_TOP_RIGHT: + case ROUNDED_CORNER_TOP_RIGHT: cairo_arc(cairo, w - r, r, r, -90 * deg, 0 * deg); cairo_line_to(cairo, w, h); cairo_line_to(cairo, 0, h); cairo_line_to(cairo, 0, 0); break; - default: - wlr_log(WLR_ERROR, "unknown corner type"); } cairo_close_path(cairo); cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); @@ -1203,20 +1196,18 @@ rounded_rect(struct rounded_corner_ctx *ctx) cairo_set_line_width(cairo, ctx->line_width); double half_line_width = ctx->line_width / 2.0; switch (ctx->corner) { - case LAB_CORNER_TOP_LEFT: + case ROUNDED_CORNER_TOP_LEFT: cairo_move_to(cairo, half_line_width, h); cairo_line_to(cairo, half_line_width, r); cairo_move_to(cairo, r, half_line_width); cairo_line_to(cairo, w, half_line_width); break; - case LAB_CORNER_TOP_RIGHT: + case ROUNDED_CORNER_TOP_RIGHT: cairo_move_to(cairo, 0, half_line_width); cairo_line_to(cairo, w - r, half_line_width); cairo_move_to(cairo, w - half_line_width, r); cairo_line_to(cairo, w - half_line_width, h); break; - default: - wlr_log(WLR_ERROR, "unknown corner type"); } cairo_stroke(cairo); @@ -1264,16 +1255,14 @@ rounded_rect(struct rounded_corner_ctx *ctx) cairo_set_line_width(cairo, line_width); half_line_width = line_width / 2.0; switch (ctx->corner) { - case LAB_CORNER_TOP_LEFT: + case ROUNDED_CORNER_TOP_LEFT: cairo_move_to(cairo, half_line_width, r); cairo_arc(cairo, r, r, r - half_line_width, 180 * deg, 270 * deg); break; - case LAB_CORNER_TOP_RIGHT: + case ROUNDED_CORNER_TOP_RIGHT: cairo_move_to(cairo, w - r, half_line_width); cairo_arc(cairo, w - r, r, r - half_line_width, -90 * deg, 0 * deg); break; - default: - break; } cairo_stroke(cairo); @@ -1374,10 +1363,10 @@ create_corners(struct theme *theme) .line_width = theme->border_width, .fill_pattern = theme->window[active].titlebar_pattern, .border_color = theme->window[active].border_color, - .corner = LAB_CORNER_TOP_LEFT, + .corner = ROUNDED_CORNER_TOP_LEFT, }; theme->window[active].corner_top_left_normal = rounded_rect(&ctx); - ctx.corner = LAB_CORNER_TOP_RIGHT; + ctx.corner = ROUNDED_CORNER_TOP_RIGHT; theme->window[active].corner_top_right_normal = rounded_rect(&ctx); } } diff --git a/src/view-impl-common.c b/src/view-impl-common.c index 8e67d0c5..377d8c76 100644 --- a/src/view-impl-common.c +++ b/src/view-impl-common.c @@ -59,7 +59,7 @@ view_impl_unmap(struct view *view) } static bool -resizing_edge(struct view *view, uint32_t edge) +resizing_edge(struct view *view, enum lab_edge edge) { struct server *server = view->server; return server->input_mode == LAB_INPUT_STATE_RESIZE @@ -86,7 +86,7 @@ view_impl_apply_geometry(struct view *view, int w, int h) * reliable on its own. The combination of the two methods * should catch 99% of resize cases that we care about. */ - bool resizing_left_edge = resizing_edge(view, WLR_EDGE_LEFT); + bool resizing_left_edge = resizing_edge(view, LAB_EDGE_LEFT); if (resizing_left_edge || (current->x != pending->x && current->x + current->width == pending->x + pending->width)) { @@ -96,7 +96,7 @@ view_impl_apply_geometry(struct view *view, int w, int h) } /* Anchor bottom edge if resizing via top edge */ - bool resizing_top_edge = resizing_edge(view, WLR_EDGE_TOP); + bool resizing_top_edge = resizing_edge(view, LAB_EDGE_TOP); if (resizing_top_edge || (current->y != pending->y && current->y + current->height == pending->y + pending->height)) { diff --git a/src/view.c b/src/view.c index 8c697daf..67457d56 100644 --- a/src/view.c +++ b/src/view.c @@ -177,7 +177,7 @@ view_matches_query(struct view *view, struct view_query *query) if (!view->tiled) { return false; } - } else if (query->tiled != LAB_EDGE_INVALID) { + } else if (query->tiled != LAB_EDGE_NONE) { if (query->tiled != view->tiled) { return false; } @@ -447,10 +447,10 @@ view_get_edge_snap_box(struct view *view, struct output *output, if (edge & LAB_EDGE_LEFT) { x2 = (usable.width - rc.gap) / 2; } - if (edge & LAB_EDGE_DOWN) { + if (edge & LAB_EDGE_BOTTOM) { y1 = (usable.height + rc.gap) / 2; } - if (edge & LAB_EDGE_UP) { + if (edge & LAB_EDGE_TOP) { y2 = (usable.height - rc.gap) / 2; } @@ -1453,7 +1453,7 @@ void view_set_untiled(struct view *view) { assert(view); - view->tiled = LAB_EDGE_INVALID; + view->tiled = LAB_EDGE_NONE; view->tiled_region = NULL; zfree(view->tiled_region_evacuate); view_notify_tiled(view); @@ -2024,10 +2024,10 @@ view_move_to_edge(struct view *view, enum lab_edge direction, bool snap_to_windo case LAB_EDGE_RIGHT: destination_x = right - view->pending.width; break; - case LAB_EDGE_UP: + case LAB_EDGE_TOP: destination_y = top; break; - case LAB_EDGE_DOWN: + case LAB_EDGE_BOTTOM: destination_y = bottom - view_effective_height(view, /* use_pending */ true); break; diff --git a/src/xdg.c b/src/xdg.c index e38038e5..00c4e226 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -399,7 +399,7 @@ handle_request_move(struct wl_listener *listener, void *data) */ struct view *view = wl_container_of(listener, view, request_move); if (view == view->server->seat.pressed.view) { - interactive_begin(view, LAB_INPUT_STATE_MOVE, 0); + interactive_begin(view, LAB_INPUT_STATE_MOVE, LAB_EDGE_NONE); } } @@ -653,7 +653,7 @@ xdg_toplevel_view_notify_tiled(struct view *view) return; } - enum wlr_edges edge = WLR_EDGE_NONE; + enum lab_edge edge = LAB_EDGE_NONE; bool want_edge = rc.snap_tiling_events_mode & LAB_TILING_EVENTS_EDGE; bool want_region = rc.snap_tiling_events_mode & LAB_TILING_EVENTS_REGION; @@ -665,39 +665,32 @@ xdg_toplevel_view_notify_tiled(struct view *view) if (want_edge) { switch (view->tiled) { case LAB_EDGE_LEFT: - edge = WLR_EDGE_LEFT | WLR_EDGE_TOP | WLR_EDGE_BOTTOM; + edge = LAB_EDGES_EXCEPT_RIGHT; break; case LAB_EDGE_RIGHT: - edge = WLR_EDGE_RIGHT | WLR_EDGE_TOP | WLR_EDGE_BOTTOM; + edge = LAB_EDGES_EXCEPT_LEFT; break; - case LAB_EDGE_UP: - edge = WLR_EDGE_TOP | WLR_EDGE_LEFT | WLR_EDGE_RIGHT; + case LAB_EDGE_TOP: + edge = LAB_EDGES_EXCEPT_BOTTOM; break; - case LAB_EDGE_DOWN: - edge = WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT; + case LAB_EDGE_BOTTOM: + edge = LAB_EDGES_EXCEPT_TOP; break; - case LAB_EDGE_UPLEFT: - edge = WLR_EDGE_TOP | WLR_EDGE_LEFT; - break; - case LAB_EDGE_UPRIGHT: - edge = WLR_EDGE_TOP | WLR_EDGE_RIGHT; - break; - case LAB_EDGE_DOWNLEFT: - edge = WLR_EDGE_BOTTOM | WLR_EDGE_LEFT; - break; - case LAB_EDGE_DOWNRIGHT: - edge = WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT; + case LAB_EDGES_TOP_LEFT: + case LAB_EDGES_TOP_RIGHT: + case LAB_EDGES_BOTTOM_LEFT: + case LAB_EDGES_BOTTOM_RIGHT: + edge = view->tiled; break; /* TODO: LAB_EDGE_CENTER? */ default: - edge = WLR_EDGE_NONE; + edge = LAB_EDGE_NONE; } } if (want_region && view->tiled_region) { /* Region-snapped views are considered tiled on all edges */ - edge = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | - WLR_EDGE_TOP | WLR_EDGE_BOTTOM; + edge = LAB_EDGES_ALL; } uint32_t serial = diff --git a/src/xwayland.c b/src/xwayland.c index 557305dc..9f311e86 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -280,7 +280,7 @@ handle_request_move(struct wl_listener *listener, void *data) */ struct view *view = wl_container_of(listener, view, request_move); if (view == view->server->seat.pressed.view) { - interactive_begin(view, LAB_INPUT_STATE_MOVE, 0); + interactive_begin(view, LAB_INPUT_STATE_MOVE, LAB_EDGE_NONE); } } From 80b28f16c7cf4507e020ed86a28cddda00e5393b Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Tue, 26 Aug 2025 23:48:05 -0400 Subject: [PATCH 113/248] tree-wide: use enum types/constants where appropriate - add LAB_WINDOW_TYPE_INVALID in place of literal -1 - document more clearly that enum lab_view_criteria is a bitset - other one-off replacements of integer values/types for consistency Note: variables of type enum lab_view_criteria are already used extensively throughout the code to contain combinations of the declared enum values. I am not introducing any new usage here, just changing the single uint32_t to be consistent with all the other usages. --- include/config/rcxml.h | 2 +- include/config/types.h | 12 +++++++++++- include/osd.h | 1 - include/output.h | 3 +-- include/view.h | 2 +- include/window-rules.h | 3 ++- src/config/rcxml.c | 10 +++++----- src/img/img-xbm.c | 2 +- src/input/cursor.c | 4 ++-- src/view.c | 5 +++-- 10 files changed, 27 insertions(+), 17 deletions(-) diff --git a/include/config/rcxml.h b/include/config/rcxml.h index cb3dd1b9..86daf4e4 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -173,7 +173,7 @@ struct rcxml { bool show; bool preview; bool outlines; - uint32_t criteria; + enum lab_view_criteria criteria; struct wl_list fields; /* struct window_switcher_field.link */ } window_switcher; diff --git a/include/config/types.h b/include/config/types.h index 7f90b151..75f207ac 100644 --- a/include/config/types.h +++ b/include/config/types.h @@ -48,7 +48,16 @@ enum lab_tristate { LAB_STATE_DISABLED }; -/* All criteria is applied in AND logic */ +/* + * This enum type is a set of bit flags where each set bit makes the + * criteria more restrictive. For example: + * + * (LAB_VIEW_CRITERIA_FULLSCREEN | LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) + * matches only fullscreen views on the current workspace, while + * + * (LAB_VIEW_CRITERIA_ALWAYS_ON_TOP | LAB_VIEW_CRITERIA_NO_ALWAYS_ON_TOP) + * would be contradictory and match nothing at all. + */ enum lab_view_criteria { /* No filter -> all focusable views */ LAB_VIEW_CRITERIA_NONE = 0, @@ -79,6 +88,7 @@ enum lab_view_criteria { * to make them available even in builds with xwayland support disabled. */ enum lab_window_type { + LAB_WINDOW_TYPE_INVALID = -1, LAB_WINDOW_TYPE_DESKTOP = 0, LAB_WINDOW_TYPE_DOCK, LAB_WINDOW_TYPE_TOOLBAR, diff --git a/include/osd.h b/include/osd.h index 39a73b8b..1457aeb2 100644 --- a/include/osd.h +++ b/include/osd.h @@ -43,7 +43,6 @@ struct window_switcher_field { struct buf; struct view; struct server; -enum lab_cycle_dir; /* Begin window switcher */ void osd_begin(struct server *server, enum lab_cycle_dir direction); diff --git a/include/output.h b/include/output.h index 87da60d9..413b43c5 100644 --- a/include/output.h +++ b/include/output.h @@ -3,8 +3,7 @@ #define LABWC_OUTPUT_H #include - -enum lab_edge; +#include "common/edge.h" #define LAB_NR_LAYERS (4) diff --git a/include/view.h b/include/view.h index 2767f980..f2dcb5c9 100644 --- a/include/view.h +++ b/include/view.h @@ -274,7 +274,7 @@ struct view_query { struct wl_list link; char *identifier; char *title; - int window_type; + enum lab_window_type window_type; char *sandbox_engine; char *sandbox_app_id; enum lab_tristate shaded; diff --git a/include/window-rules.h b/include/window-rules.h index 4b7f6673..1bee4c09 100644 --- a/include/window-rules.h +++ b/include/window-rules.h @@ -4,6 +4,7 @@ #include #include +#include "config/types.h" enum window_rule_event { LAB_WINDOW_RULE_EVENT_ON_FIRST_MAP = 0, @@ -24,7 +25,7 @@ enum property { struct window_rule { char *identifier; char *title; - int window_type; + enum lab_window_type window_type; char *sandbox_engine; char *sandbox_app_id; bool match_once; diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 52e02c4b..cbe0e81d 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -56,11 +56,11 @@ enum font_place { static void load_default_key_bindings(void); static void load_default_mouse_bindings(void); -static int +static enum lab_window_type parse_window_type(const char *type) { if (!type) { - return -1; + return LAB_WINDOW_TYPE_INVALID; } if (!strcasecmp(type, "desktop")) { return LAB_WINDOW_TYPE_DESKTOP; @@ -91,7 +91,7 @@ parse_window_type(const char *type) } else if (!strcasecmp(type, "normal")) { return LAB_WINDOW_TYPE_NORMAL; } else { - return -1; + return LAB_WINDOW_TYPE_INVALID; } } @@ -258,7 +258,7 @@ static void fill_window_rule(xmlNode *node) { struct window_rule *window_rule = znew(*window_rule); - window_rule->window_type = -1; // Window types are >= 0 + window_rule->window_type = LAB_WINDOW_TYPE_INVALID; wl_list_append(&rc.window_rules, &window_rule->link); wl_list_init(&window_rule->actions); @@ -1406,7 +1406,7 @@ rcxml_init(void) rc.tablet.force_mouse_emulation = false; rc.tablet.output_name = NULL; - rc.tablet.rotation = 0; + rc.tablet.rotation = LAB_ROTATE_NONE; rc.tablet.box = (struct wlr_fbox){0}; tablet_load_default_button_mappings(); rc.tablet_tool.motion = LAB_MOTION_ABSOLUTE; diff --git a/src/img/img-xbm.c b/src/img/img-xbm.c index 0a402d39..ca433cad 100644 --- a/src/img/img-xbm.c +++ b/src/img/img-xbm.c @@ -251,7 +251,7 @@ parse_xbm_builtin(const char *button, int size, uint32_t color) t[i].value = button[i]; t[i].type = TOKEN_INT; } - t[size].type = 0; + t[size].type = TOKEN_NONE; process_bytes(&pixmap, t, color); return pixmap; } diff --git a/src/input/cursor.c b/src/input/cursor.c index cd7b5f34..4c6b353e 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -882,7 +882,7 @@ handle_motion(struct wl_listener *listener, void *data) cursor_set_visible(seat, /* visible */ true); if (seat->cursor_scroll_wheel_emulation) { - uint32_t orientation; + enum wl_pointer_axis orientation; double delta; if (fabs(event->delta_x) > fabs(event->delta_y)) { orientation = WL_POINTER_AXIS_HORIZONTAL_SCROLL; @@ -1014,7 +1014,7 @@ is_double_click(long double_click_speed, uint32_t button, */ last_button = 0; last_view = NULL; - last_type = 0; + last_type = LAB_SSD_NONE; return true; } return false; diff --git a/src/view.c b/src/view.c index 67457d56..521ffe58 100644 --- a/src/view.c +++ b/src/view.c @@ -79,7 +79,7 @@ view_query_create(void) { struct view_query *query = znew(*query); /* Must be synced with view_matches_criteria() in window-rules.c */ - query->window_type = -1; + query->window_type = LAB_WINDOW_TYPE_INVALID; query->maximized = VIEW_AXIS_INVALID; query->decoration = LAB_SSD_MODE_INVALID; return query; @@ -132,7 +132,8 @@ view_matches_query(struct view *view, struct view_query *query) return false; } - if (query->window_type >= 0 && !view_contains_window_type(view, query->window_type)) { + if (query->window_type != LAB_WINDOW_TYPE_INVALID + && !view_contains_window_type(view, query->window_type)) { return false; } From c8943ca242d44f3118bee70b38114c0b50561708 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Wed, 27 Aug 2025 13:04:52 -0400 Subject: [PATCH 114/248] seat: improve debug logging when configuring input devices I needed to debug an input configuration issue and found the debug output not-super-helpful, so I made some improvements: - Print the name and "sysname" (e.g. event11) of the device being configured. Note that the name alone isn't enough since there can be multiple identically-named devices. - Print the config category matched for each device. - Print the config values (if any) being applied. For enums, only the numeric value is printed since I'm lazy. - Don't print "pointer acceleration configured" if neither a pointer speed nor acceleration profile is configured (it's confusing). --- include/config/libinput.h | 1 + src/config/libinput.c | 19 ++++++++++++++ src/seat.c | 52 +++++++++++++++++++++++++++++---------- 3 files changed, 59 insertions(+), 13 deletions(-) diff --git a/include/config/libinput.h b/include/config/libinput.h index 9de8f252..80b3fc10 100644 --- a/include/config/libinput.h +++ b/include/config/libinput.h @@ -38,6 +38,7 @@ struct libinput_category { }; enum lab_libinput_device_type get_device_type(const char *s); +const char *libinput_device_type_name(enum lab_libinput_device_type type); struct libinput_category *libinput_category_create(void); struct libinput_category *libinput_category_get_default(void); diff --git a/src/config/libinput.c b/src/config/libinput.c index f8f48f18..1209d267 100644 --- a/src/config/libinput.c +++ b/src/config/libinput.c @@ -51,6 +51,25 @@ get_device_type(const char *s) return LAB_LIBINPUT_DEVICE_NONE; } +const char * +libinput_device_type_name(enum lab_libinput_device_type type) +{ + switch (type) { + case LAB_LIBINPUT_DEVICE_NONE: + break; + case LAB_LIBINPUT_DEVICE_DEFAULT: + return "default"; + case LAB_LIBINPUT_DEVICE_TOUCH: + return "touch"; + case LAB_LIBINPUT_DEVICE_TOUCHPAD: + return "touchpad"; + case LAB_LIBINPUT_DEVICE_NON_TOUCH: + return "non-touch"; + } + /* none/invalid */ + return "(none)"; +} + struct libinput_category * libinput_category_create(void) { diff --git a/src/seat.c b/src/seat.c index 97f99703..9817e78b 100644 --- a/src/seat.c +++ b/src/seat.c @@ -149,6 +149,13 @@ configure_libinput(struct wlr_input_device *wlr_input_device) */ assert(dc); + wlr_log(WLR_INFO, "configuring input device %s (%s)", + libinput_device_get_name(libinput_dev), + libinput_device_get_sysname(libinput_dev)); + + wlr_log(WLR_INFO, "matched category: %s", + dc->name ? dc->name : libinput_device_type_name(dc->type)); + libinput_device_config_tap_set_enabled(libinput_dev, libinput_device_config_tap_get_default_enabled(libinput_dev)); libinput_device_config_tap_set_button_map(libinput_dev, @@ -156,7 +163,8 @@ configure_libinput(struct wlr_input_device *wlr_input_device) if (libinput_device_config_tap_get_finger_count(libinput_dev) <= 0) { wlr_log(WLR_INFO, "tap unavailable"); } else { - wlr_log(WLR_INFO, "tap configured"); + wlr_log(WLR_INFO, "tap configured (tap=%d, button_map=%d)", + dc->tap, dc->tap_button_map); libinput_device_config_tap_set_enabled(libinput_dev, dc->tap); libinput_device_config_tap_set_button_map(libinput_dev, dc->tap_button_map); @@ -168,7 +176,8 @@ configure_libinput(struct wlr_input_device *wlr_input_device) || dc->tap_and_drag < 0) { wlr_log(WLR_INFO, "tap-and-drag not configured"); } else { - wlr_log(WLR_INFO, "tap-and-drag configured"); + wlr_log(WLR_INFO, "tap-and-drag configured (%d)", + dc->tap_and_drag); libinput_device_config_tap_set_drag_enabled( libinput_dev, dc->tap_and_drag); } @@ -179,7 +188,7 @@ configure_libinput(struct wlr_input_device *wlr_input_device) || dc->drag_lock < 0) { wlr_log(WLR_INFO, "drag lock not configured"); } else { - wlr_log(WLR_INFO, "drag lock configured"); + wlr_log(WLR_INFO, "drag lock configured (%d)", dc->drag_lock); libinput_device_config_tap_set_drag_lock_enabled( libinput_dev, dc->drag_lock); } @@ -191,7 +200,8 @@ configure_libinput(struct wlr_input_device *wlr_input_device) || dc->three_finger_drag < 0) { wlr_log(WLR_INFO, "three-finger drag not configured"); } else { - wlr_log(WLR_INFO, "three-finger drag configured"); + wlr_log(WLR_INFO, "three-finger drag configured (%d)", + dc->three_finger_drag); libinput_device_config_3fg_drag_set_enabled( libinput_dev, dc->three_finger_drag); } @@ -203,7 +213,8 @@ configure_libinput(struct wlr_input_device *wlr_input_device) || dc->natural_scroll < 0) { wlr_log(WLR_INFO, "natural scroll not configured"); } else { - wlr_log(WLR_INFO, "natural scroll configured"); + wlr_log(WLR_INFO, "natural scroll configured (%d)", + dc->natural_scroll); libinput_device_config_scroll_set_natural_scroll_enabled( libinput_dev, dc->natural_scroll); } @@ -214,7 +225,8 @@ configure_libinput(struct wlr_input_device *wlr_input_device) || dc->left_handed < 0) { wlr_log(WLR_INFO, "left-handed mode not configured"); } else { - wlr_log(WLR_INFO, "left-handed mode configured"); + wlr_log(WLR_INFO, "left-handed mode configured (%d)", + dc->left_handed); libinput_device_config_left_handed_set(libinput_dev, dc->left_handed); } @@ -226,14 +238,24 @@ configure_libinput(struct wlr_input_device *wlr_input_device) if (libinput_device_config_accel_is_available(libinput_dev) == 0) { wlr_log(WLR_INFO, "pointer acceleration unavailable"); } else { - wlr_log(WLR_INFO, "pointer acceleration configured"); if (dc->pointer_speed >= -1) { + wlr_log(WLR_INFO, "pointer speed configured (%g)", + dc->pointer_speed); libinput_device_config_accel_set_speed(libinput_dev, dc->pointer_speed); + } else { + wlr_log(WLR_INFO, "pointer speed not configured"); } + if (dc->accel_profile > 0) { + wlr_log(WLR_INFO, + "pointer accel profile configured (%d)", + dc->accel_profile); libinput_device_config_accel_set_profile(libinput_dev, dc->accel_profile); + } else { + wlr_log(WLR_INFO, + "pointer accel profile not configured"); } } @@ -243,7 +265,8 @@ configure_libinput(struct wlr_input_device *wlr_input_device) == 0 || dc->middle_emu < 0) { wlr_log(WLR_INFO, "middle emulation not configured"); } else { - wlr_log(WLR_INFO, "middle emulation configured"); + wlr_log(WLR_INFO, "middle emulation configured (%d)", + dc->middle_emu); libinput_device_config_middle_emulation_set_enabled( libinput_dev, dc->middle_emu); } @@ -254,7 +277,7 @@ configure_libinput(struct wlr_input_device *wlr_input_device) || dc->dwt < 0) { wlr_log(WLR_INFO, "dwt not configured"); } else { - wlr_log(WLR_INFO, "dwt configured"); + wlr_log(WLR_INFO, "dwt configured (%d)", dc->dwt); libinput_device_config_dwt_set_enabled(libinput_dev, dc->dwt); } @@ -266,7 +289,8 @@ configure_libinput(struct wlr_input_device *wlr_input_device) || dc->click_method < 0) { wlr_log(WLR_INFO, "click method not configured"); } else { - wlr_log(WLR_INFO, "click method configured"); + wlr_log(WLR_INFO, "click method configured (%d)", + dc->click_method); /* * Note, the documentation claims that: @@ -289,7 +313,8 @@ configure_libinput(struct wlr_input_device *wlr_input_device) & dc->scroll_method) == 0) { wlr_log(WLR_INFO, "scroll method not supported"); } else { - wlr_log(WLR_INFO, "scroll method configured"); + wlr_log(WLR_INFO, "scroll method configured (%d)", + dc->scroll_method); libinput_device_config_scroll_set_method(libinput_dev, dc->scroll_method); } @@ -301,7 +326,8 @@ configure_libinput(struct wlr_input_device *wlr_input_device) || dc->send_events_mode < 0) { wlr_log(WLR_INFO, "send events mode not configured"); } else { - wlr_log(WLR_INFO, "send events mode configured"); + wlr_log(WLR_INFO, "send events mode configured (%d)", + dc->send_events_mode); libinput_device_config_send_events_set_mode(libinput_dev, dc->send_events_mode); } @@ -317,7 +343,7 @@ configure_libinput(struct wlr_input_device *wlr_input_device) libinput_device_config_calibration_set_matrix(libinput_dev, dc->calibration_matrix); } - wlr_log(WLR_INFO, "scroll factor configured"); + wlr_log(WLR_INFO, "scroll factor configured (%g)", dc->scroll_factor); input->scroll_factor = dc->scroll_factor; } From da90e62346cca42ab989c798d0307fd7a9983f39 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Mon, 25 Aug 2025 16:11:50 +0100 Subject: [PATCH 115/248] NEWS.md: interim update --- NEWS.md | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/NEWS.md b/NEWS.md index f8a744d5..21b6fe7c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -76,6 +76,65 @@ There are some regression warnings worth noting for the switch to wlroots 0.19: ## [unreleased] +### Added + +- Add `` to allow hiding titlebar + when window is maximized. @CosmicFusion @tokyo4j [#3015] +- Use client-send-to-menu as 'Workspace' submenu in built-in client-menu + @johanmalm [#2995] +- Allow overwriting submenu icon to increase flexibility and enhance Openbox + compatibility. @tokyo4j [#2998] +- Allow client-{list-combined,send-to}-menu as submenu of static menu @tokyo4j + [#2994] +- Add `labnag` (a dialog client with message and buttons) and associated + `` option in 'If' actions. @johanmalm @Consolatis @tokyo4j [#2699] +- Allow snapping to corner edges during interactive move with associated config + options ``. @tokyo4j [#2885] +- Support new values "up-left", "up-right", "down-left" and "down-right" with + `` and + ``. @tokyo4j [#2885] +- XML parsing improvements as listed below. @tokyo4j [#2667] [#2967] [#2971] + - Support nested `If` and `ForEach` actions + - Parse CDATA as text all nodes + - Remove ordering constraint of attributes in ``, `` and + `` + - `If` actions now works for menus + - For menus, the `name` argument no longer has to be the first argument of + ``; and the `label` argument no longer has to be the first argument + of `` +- Toggle mousebinds with the `ToggleKeybinds` action @tokyo4j [#2942] +- Add support for direction value 'any' with tiled queries. This allows users + to query for any snap directions without using multiple query statements + @lynxy [#2883] + +### Fixed + +- Fix false positives when matching desktop entries @datMaffin [#3004] +- Prevent accidental downcasting of scale in scaled-icon-buffer to avoid blurry + icons on non-integer scales and a cairo assert when using a output scale < 1. + @Consolatis #2984 +- Fix xdg-shell windows moving between outputs due to configure timeout + @jlindgren90 [#2976] +- Fix segfault with toplevel `` in `menu.xml` @tokyo4j [#2970] +- Prevent hi-res mice triggering scroll actions too often @tokyo4j [#2933] + +### Changed + +- Respect client-initiated window resize of non-maximized axis, for example + remember the width of vertically-maximized window resizing itself + horizontally. @jlindgren90 [#3020] +- Remember position of window along non-maximized axis during interactive move. + @jlindgren90 [#3020] +- Restore default libinput device values on reconfigure with empty value, rather + than leaving the old configuration. This makes rc.xml more declarative. + @tokyo4j [#3011] +- Change `If` action when used without a focused window to execute the `` + branch (previously it was just ignored). The reason for this is to make things + more consistent with ``. It is not anticipated that this will affect + anyone's workflow but is mentioned here for completeness. +- Make `autoEnableOutputs=no` apply only to drm outputs @jlindgren90 [#2972] +- Take into account `` for edge and region overlays @tokyo4j [#2965] + ## [0.9.1] This is an earlier-than-usual release containinig bug fixes only. It has been From a59f98525efccd47098575c4de114083e81cb61f Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Wed, 13 Aug 2025 20:17:43 +0900 Subject: [PATCH 116/248] ssd-titlebar: create title buffer in ssd_titlebar_create() --- src/ssd/ssd-titlebar.c | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 9ac0d238..2cd963ff 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -81,6 +81,15 @@ ssd_titlebar_create(struct ssd *ssd) corner_top_right, width - corner_width, -rc.theme->border_width); + /* Title */ + struct ssd_part *title_part = + add_scene_part(&subtree->parts, LAB_SSD_PART_TITLE); + title_part->buffer = scaled_font_buffer_create_for_titlebar( + subtree->tree, theme->titlebar_height, + theme->window[active].titlebar_pattern); + assert(title_part->buffer); + title_part->node = &title_part->buffer->scene_buffer->node; + /* Buttons */ struct title_button *b; int x = theme->window_titlebar_padding_width; @@ -389,11 +398,6 @@ ssd_update_title_positions(struct ssd *ssd, int offset_left, int offset_right) struct ssd_sub_tree *subtree; FOR_EACH_STATE(ssd, subtree) { part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLE); - if (!part || !part->node) { - /* view->surface never been mapped */ - /* Or we somehow failed to allocate a scaled titlebar buffer */ - continue; - } buffer_width = part->buffer ? part->buffer->width : 0; buffer_height = part->buffer ? part->buffer->height : 0; @@ -504,24 +508,8 @@ ssd_update_title(struct ssd *ssd) } part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLE); - if (!part) { - /* Initialize part and wlr_scene_buffer without attaching a buffer */ - part = add_scene_part(&subtree->parts, LAB_SSD_PART_TITLE); - part->buffer = scaled_font_buffer_create_for_titlebar( - subtree->tree, theme->titlebar_height, - theme->window[active].titlebar_pattern); - if (part->buffer) { - part->node = &part->buffer->scene_buffer->node; - } else { - wlr_log(WLR_ERROR, "Failed to create title node"); - } - } - - if (part->buffer) { - scaled_font_buffer_update(part->buffer, title, - title_bg_width, font, - text_color, bg_color); - } + scaled_font_buffer_update(part->buffer, title, title_bg_width, + font, text_color, bg_color); /* And finally update the cache */ dstate->width = part->buffer ? part->buffer->width : 0; From aa3dbb4f859f6493f8aff3710ce941bbeac9a2d7 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Wed, 13 Aug 2025 20:22:48 +0900 Subject: [PATCH 117/248] ssd-extents: factor out resize_extent_within_usable() --- src/ssd/ssd-extents.c | 116 +++++++++++++++++++----------------------- 1 file changed, 52 insertions(+), 64 deletions(-) diff --git a/src/ssd/ssd-extents.c b/src/ssd/ssd-extents.c index f8fdd9f0..bb3f661e 100644 --- a/src/ssd/ssd-extents.c +++ b/src/ssd/ssd-extents.c @@ -48,6 +48,54 @@ ssd_extents_create(struct ssd *ssd) ssd_extents_update(ssd); } +static void +resize_extent_within_usable(struct wlr_scene_rect *rect, + pixman_region32_t *usable, int x, int y, int w, int h) +{ + pixman_region32_t intersection; + pixman_region32_init(&intersection); + /* Constrain part to output->usable_area */ + pixman_region32_intersect_rect(&intersection, usable, x, y, w, h); + int nrects; + const pixman_box32_t *inter_rects = + pixman_region32_rectangles(&intersection, &nrects); + + if (nrects == 0) { + /* Not visible */ + wlr_scene_node_set_enabled(&rect->node, false); + goto out; + } + + /* + * For each edge, the invisible grab area is resized + * to not cover layer-shell clients such as panels. + * However, only one resize operation is used per edge, + * so if a window is in the unlikely position that it + * is near a panel but also overspills onto another screen, + * the invisible grab-area on the other screen would be + * smaller than would normally be the case. + * + * Thus only use the first intersecting rect, this is + * a compromise as it doesn't require us to create + * multiple scene rects for a given extent edge + * and still works in 95% of the cases. + */ + struct wlr_box result_box = (struct wlr_box) { + .x = inter_rects[0].x1, + .y = inter_rects[0].y1, + .width = inter_rects[0].x2 - inter_rects[0].x1, + .height = inter_rects[0].y2 - inter_rects[0].y1 + }; + + wlr_scene_node_set_enabled(&rect->node, true); + + wlr_scene_node_set_position(&rect->node, result_box.x, result_box.y); + wlr_scene_rect_set_size(rect, result_box.width, result_box.height); + +out: + pixman_region32_fini(&intersection); +} + void ssd_extents_update(struct ssd *ssd) { @@ -73,8 +121,6 @@ ssd_extents_update(struct ssd *ssd) int border_width = MAX(rc.resize_minimum_area, theme->border_width); int extended_area = MAX(0, rc.resize_minimum_area - theme->border_width); - struct wlr_box part_box; - struct wlr_box result_box; struct ssd_part *part; struct wlr_scene_rect *rect; struct wlr_box target; @@ -87,11 +133,8 @@ ssd_extents_update(struct ssd *ssd) * Convert all output usable areas that the * view is currently on into a pixman region */ - int nrects; pixman_region32_t usable; - pixman_region32_t intersection; pixman_region32_init(&usable); - pixman_region32_init(&intersection); struct output *output; wl_list_for_each(output, &view->server->outputs, link) { if (!view_on_output(view, output)) { @@ -103,9 +146,10 @@ ssd_extents_update(struct ssd *ssd) usable_area.y, usable_area.width, usable_area.height); } - /* Remember base layout coordinates */ + /* Move usable area to the coordinate system of extents */ int base_x, base_y; wlr_scene_node_coords(&ssd->extents.tree->node, &base_x, &base_y); + pixman_region32_translate(&usable, -base_x, -base_y); wl_list_for_each(part, &ssd->extents.parts, link) { rect = wlr_scene_rect_from_node(part->node); @@ -141,65 +185,9 @@ ssd_extents_update(struct ssd *ssd) target = (struct wlr_box){0}; } - /* Get layout geometry of what the part *should* be */ - part_box.x = base_x + target.x; - part_box.y = base_y + target.y; - part_box.width = target.width; - part_box.height = target.height; - - /* Constrain part to output->usable_area */ - pixman_region32_clear(&intersection); - pixman_region32_intersect_rect(&intersection, &usable, - part_box.x, part_box.y, part_box.width, part_box.height); - const pixman_box32_t *inter_rects = - pixman_region32_rectangles(&intersection, &nrects); - - if (nrects == 0) { - /* Not visible */ - wlr_scene_node_set_enabled(part->node, false); - continue; - } - - /* - * For each edge, the invisible grab area is resized - * to not cover layer-shell clients such as panels. - * However, only one resize operation is used per edge, - * so if a window is in the unlikely position that it - * is near a panel but also overspills onto another screen, - * the invisible grab-area on the other screen would be - * smaller than would normally be the case. - * - * Thus only use the first intersecting rect, this is - * a compromise as it doesn't require us to create - * multiple scene rects for a given extent edge - * and still works in 95% of the cases. - */ - result_box = (struct wlr_box) { - .x = inter_rects[0].x1, - .y = inter_rects[0].y1, - .width = inter_rects[0].x2 - inter_rects[0].x1, - .height = inter_rects[0].y2 - inter_rects[0].y1 - }; - - if (!part->node->enabled) { - wlr_scene_node_set_enabled(part->node, true); - } - - if (part_box.width != result_box.width - || part_box.height != result_box.height) { - /* Partly visible */ - wlr_scene_rect_set_size(rect, result_box.width, - result_box.height); - wlr_scene_node_set_position(part->node, - target.x + (result_box.x - part_box.x), - target.y + (result_box.y - part_box.y)); - } else { - /* Fully visible */ - wlr_scene_node_set_position(part->node, target.x, target.y); - wlr_scene_rect_set_size(rect, target.width, target.height); - } + resize_extent_within_usable(rect, &usable, + target.x, target.y, target.width, target.height); } - pixman_region32_fini(&intersection); pixman_region32_fini(&usable); } From f347a818e3a7bc21230afe589601b3b9ef7c7f6d Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Wed, 13 Aug 2025 21:00:11 +0900 Subject: [PATCH 118/248] ssd: clean up scene management Our codebase for ssd scenes has grown with a lot of technical debts: - We needed to call `ssd_get_part()` everywhere to get the scene node of a ssd part. We then needed to cast it to `wlr_scene_rect` and `wlr_scene_buffer`. This bloated our codebase and even blocked duplicated button types in ``. - `ssd_get_part_type()` was a dirty hack. It compared parent, grandparent and grandgrandparent of a node with each subtree in the ssd to get the part type of the node. To resolve this issues, this commit changes how ssd scenes are managed: - Access scene rects and scene buffers just as a member of `struct ssd`. - `ssd_part` is now a attachment to a scene node that can be accessed via node_descriptor->data, with a new node-descriptor type `LAB_NODE_DESC_SSD_PART`. `LAB_NODE_DESC_SSD_BUTTON` is unified into it. Now the scene graph under ssd->tree looks like below. The parentheses indicate the type of ssd_part attached to the node: ssd->tree (LAB_SSD_NONE) +--titlebar (LAB_SSD_PART_TITLEBAR) | +--inactive | | +--background bar | | +--left corner | | +--right corner | | +--title (LAB_SSD_PART_TITLE) | | +--iconify button (LAB_SSD_BUTTON_ICONIFY) | | | +--normal close icon image | | | +--hovered close icon image | | | +--... | | +--window icon (LAB_SSD_BUTTON_WINDOW_ICON) | | | +--window icon image | | +--... | +--active | +--... +--border | +--inactive | | +--top | | +--... | +--active | +--top | +--... +--shadow | +--inactive | | +--top | | +--... | +--active | +--top | +--... +--extents +--top +--... When hovering on SSD, `get_cursor_context()` traverses this scene node from the leaf. If it finds a `ssd_part` attached to the node, it returns `ssd_part_type` that represents the resizing direction, button types or `Title`/`Titlebar`. --- include/node.h | 10 +- include/ssd-internal.h | 176 ++++++++++++++----------- include/ssd.h | 25 ++-- include/theme.h | 1 + src/config/rcxml.c | 1 + src/desktop.c | 32 +++-- src/node.c | 8 +- src/ssd/ssd-border.c | 130 +++++++------------ src/ssd/ssd-extents.c | 76 +++-------- src/ssd/ssd-part.c | 147 ++++++++------------- src/ssd/ssd-shadow.c | 234 ++++++++++++++------------------- src/ssd/ssd-titlebar.c | 287 ++++++++++++++++++----------------------- src/ssd/ssd.c | 115 ++++------------- 13 files changed, 516 insertions(+), 726 deletions(-) diff --git a/include/node.h b/include/node.h index 80d03e9f..1c012392 100644 --- a/include/node.h +++ b/include/node.h @@ -7,7 +7,7 @@ struct view; struct lab_layer_surface; struct lab_layer_popup; struct menuitem; -struct ssd_button; +struct ssd_part; struct scaled_scene_buffer; enum node_descriptor_type { @@ -21,7 +21,7 @@ enum node_descriptor_type { LAB_NODE_DESC_MENUITEM, LAB_NODE_DESC_TREE, LAB_NODE_DESC_SCALED_SCENE_BUFFER, - LAB_NODE_DESC_SSD_BUTTON, + LAB_NODE_DESC_SSD_PART, }; struct node_descriptor { @@ -44,7 +44,7 @@ struct node_descriptor { * - LAB_NODE_DESC_LAYER_SURFACE struct lab_layer_surface * - LAB_NODE_DESC_LAYER_POPUP struct lab_layer_popup * - LAB_NODE_DESC_MENUITEM struct menuitem - * - LAB_NODE_DESC_SSD_BUTTON struct ssd_button + * - LAB_NODE_DESC_SSD_PART struct ssd_part */ void node_descriptor_create(struct wlr_scene_node *scene_node, enum node_descriptor_type type, void *data); @@ -77,10 +77,10 @@ struct menuitem *node_menuitem_from_node( struct wlr_scene_node *wlr_scene_node); /** - * node_ssd_button_from_node - return ssd_button struct from node + * node_ssd_part_from_node - return ssd_part struct from node * @wlr_scene_node: wlr_scene_node from which to return data */ -struct ssd_button *node_ssd_button_from_node( +struct ssd_part *node_ssd_part_from_node( struct wlr_scene_node *wlr_scene_node); /** diff --git a/include/ssd-internal.h b/include/ssd-internal.h index 4721de75..aa20dee3 100644 --- a/include/ssd-internal.h +++ b/include/ssd-internal.h @@ -3,52 +3,53 @@ #define LABWC_SSD_INTERNAL_H #include -#include "common/macros.h" #include "ssd.h" #include "theme.h" #include "view.h" -#define FOR_EACH(tmp, ...) \ -{ \ - __typeof__(tmp) _x[] = { __VA_ARGS__, NULL }; \ - size_t _i = 0; \ - for ((tmp) = _x[_i]; _i < ARRAY_SIZE(_x) - 1; (tmp) = _x[++_i]) - -#define FOR_EACH_END } - -struct ssd_button { - struct view *view; - enum ssd_part_type type; - /* - * Bitmap of lab_button_state that represents a combination of - * hover/toggled/rounded states. - */ - uint8_t state_set; - /* - * Image buffers for each combination of hover/toggled/rounded states. - * img_buffers[state_set] is displayed. Some of these can be NULL - * (e.g. img_buffers[LAB_BS_ROUNDED] is set only for corner buttons). - * - * When "type" is LAB_SSD_BUTTON_WINDOW_ICON, these are all NULL and - * window_icon is used instead. - */ - struct scaled_img_buffer *img_buffers[LAB_BS_ALL + 1]; - - struct scaled_icon_buffer *window_icon; - - struct wl_listener destroy; -}; - -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; }; +/* + * The scene-graph of SSD looks like below. The parentheses indicate the type of + * ssd_part attached to the node. + * + * ssd->tree (LAB_SSD_NONE) + * +--titlebar (LAB_SSD_PART_TITLEBAR) + * | +--inactive + * | | +--background bar + * | | +--left corner + * | | +--right corner + * | | +--title (LAB_SSD_PART_TITLE) + * | | +--iconify button (LAB_SSD_BUTTON_ICONIFY) + * | | | +--normal close icon image + * | | | +--hovered close icon image + * | | | +--... + * | | +--window icon (LAB_SSD_BUTTON_WINDOW_ICON) + * | | | +--window icon image + * | | +--... + * | +--active + * | +--... + * +--border + * | +--inactive + * | | +--top + * | | +--... + * | +--active + * | +--top + * | +--... + * +--shadow + * | +--inactive + * | | +--top + * | | +--... + * | +--active + * | +--top + * | +--... + * +--extents + * +--top + * +--... + */ struct ssd { struct view *view; struct wlr_scene_tree *tree; @@ -81,33 +82,49 @@ struct ssd { struct wlr_box geometry; struct ssd_state_title { char *text; - struct ssd_state_title_width active; - struct ssd_state_title_width inactive; + /* indexed by enum ssd_active_state */ + struct ssd_state_title_width dstates[2]; } title; } state; /* An invisible area around the view which allows resizing */ - struct ssd_sub_tree extents; + struct ssd_extents_scene { + struct wlr_scene_tree *tree; + struct wlr_scene_rect *top, *bottom, *left, *right; + } extents; /* The top of the view, containing buttons, title, .. */ - struct { + struct ssd_titlebar_scene { int height; struct wlr_scene_tree *tree; - struct ssd_sub_tree active; - struct ssd_sub_tree inactive; + struct ssd_titlebar_subtree { + struct wlr_scene_tree *tree; + struct wlr_scene_buffer *corner_left; + struct wlr_scene_buffer *corner_right; + struct wlr_scene_buffer *bar; + struct scaled_font_buffer *title; + /* List of ssd_parts that represent buttons */ + struct wl_list buttons_left; + struct wl_list buttons_right; + } subtrees[2]; /* indexed by enum ssd_active_state */ } titlebar; /* Borders allow resizing as well */ - struct { + struct ssd_border_scene { struct wlr_scene_tree *tree; - struct ssd_sub_tree active; - struct ssd_sub_tree inactive; + struct ssd_border_subtree { + struct wlr_scene_tree *tree; + struct wlr_scene_rect *top, *bottom, *left, *right; + } subtrees[2]; /* indexed by enum ssd_active_state */ } border; - struct { + struct ssd_shadow_scene { struct wlr_scene_tree *tree; - struct ssd_sub_tree active; - struct ssd_sub_tree inactive; + struct ssd_shadow_subtree { + struct wlr_scene_tree *tree; + struct wlr_scene_buffer *top, *bottom, *left, *right, + *top_left, *top_right, *bottom_left, *bottom_right; + } subtrees[2]; /* indexed by enum ssd_active_state */ } shadow; /* @@ -118,46 +135,61 @@ struct ssd { struct border margin; }; +/* + * ssd_part wraps a scene-node with ssd-specific information and can be + * accessed with node_ssd_part_from_node(wlr_scene_node *). + * This allows get_cursor_context() in desktop.c to see which SSD part is under + * the cursor. + */ struct ssd_part { enum ssd_part_type type; - - /* Buffer pointer. May be NULL */ - struct scaled_font_buffer *buffer; + struct view *view; /* This part represented in scene graph */ struct wlr_scene_node *node; - - struct wl_list link; + struct wl_listener node_destroy; }; +struct ssd_part_button { + struct ssd_part base; + /* + * Bitmap of lab_button_state that represents a combination of + * hover/toggled/rounded states. + */ + uint8_t state_set; + /* + * Image buffers for each combination of hover/toggled/rounded states. + * img_buffers[state_set] is displayed. Some of these can be NULL + * (e.g. img_buffers[LAB_BS_ROUNDED] is set only for corner buttons). + * + * When the type of ssd_part pointing to ssd_button is + * LAB_SSD_BUTTON_WINDOW_ICON, these are all NULL and window_icon is + * used instead. + */ + struct scaled_img_buffer *img_buffers[LAB_BS_ALL + 1]; + + struct scaled_icon_buffer *window_icon; + + struct wl_list link; /* ssd_titlebar_subtree.buttons_{left,right} */ +}; + +/* FIXME: This structure is redundant as ssd_part contains view */ struct ssd_hover_state { struct view *view; - struct ssd_button *button; + struct ssd_part_button *button; }; struct wlr_buffer; struct wlr_scene_tree; /* 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_tree *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_tree *parent, struct wlr_buffer *buffer, int x, int y); -struct ssd_part *add_scene_button(struct wl_list *part_list, +struct ssd_part *attach_ssd_part(enum ssd_part_type type, struct view *view, + struct wlr_scene_node *node); +struct ssd_part_button *attach_ssd_part_button(struct wl_list *button_parts, enum ssd_part_type type, struct wlr_scene_tree *parent, - struct lab_img *buffers[LAB_BS_ALL + 1], int x, int y, + struct lab_img *imgs[LAB_BS_ALL + 1], int x, int y, struct view *view); - -/* 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); +struct ssd_part_button *button_try_from_ssd_part(struct ssd_part *part); /* SSD internal */ void ssd_titlebar_create(struct ssd *ssd); diff --git a/include/ssd.h b/include/ssd.h index cf0d3e24..f3597f31 100644 --- a/include/ssd.h +++ b/include/ssd.h @@ -6,6 +6,13 @@ #include "common/edge.h" #include "config/types.h" +enum ssd_active_state { + SSD_INACTIVE = 0, + SSD_ACTIVE = 1, +}; + +#define FOR_EACH_ACTIVE_STATE(active) for (active = SSD_INACTIVE; active <= SSD_ACTIVE; active++) + struct wlr_cursor; /* @@ -36,11 +43,8 @@ enum ssd_part_type { LAB_SSD_BUTTON, LAB_SSD_PART_TITLEBAR, - LAB_SSD_PART_TITLEBAR_CORNER_RIGHT, - LAB_SSD_PART_TITLEBAR_CORNER_LEFT, LAB_SSD_PART_TITLE, - /* shared by shadows, borders and extents */ LAB_SSD_PART_CORNER_TOP_LEFT, LAB_SSD_PART_CORNER_TOP_RIGHT, LAB_SSD_PART_CORNER_BOTTOM_RIGHT, @@ -64,8 +68,8 @@ enum ssd_part_type { /* Forward declare arguments */ struct ssd; -struct ssd_button; struct ssd_hover_state; +struct ssd_part; struct view; struct wlr_scene; struct wlr_scene_node; @@ -96,12 +100,17 @@ struct ssd_hover_state *ssd_hover_state_new(void); void ssd_update_button_hover(struct wlr_scene_node *node, struct ssd_hover_state *hover_state); -enum ssd_part_type ssd_button_get_type(const struct ssd_button *button); -struct view *ssd_button_get_view(const struct ssd_button *button); +enum ssd_part_type ssd_part_get_type(const struct ssd_part *part); +struct view *ssd_part_get_view(const struct ssd_part *part); /* Public SSD helpers */ -enum ssd_part_type ssd_get_part_type(const struct ssd *ssd, - struct wlr_scene_node *node, struct wlr_cursor *cursor); + +/* + * Returns a part type that represents a mouse context like "Top", "Left" and + * "TRCorner" when the cursor is on the window border or resizing handle. + */ +enum ssd_part_type ssd_get_resizing_type(const struct ssd *ssd, + struct wlr_cursor *cursor); enum lab_edge ssd_resize_edges(enum ssd_part_type type); bool ssd_part_contains(enum ssd_part_type whole, enum ssd_part_type candidate); enum lab_ssd_mode ssd_mode_parse(const char *mode); diff --git a/include/theme.h b/include/theme.h index 92d4a99e..68a43f2f 100644 --- a/include/theme.h +++ b/include/theme.h @@ -192,6 +192,7 @@ struct theme { int mag_border_width; }; +/* TODO: replace with enum ssd_active_state */ #define THEME_INACTIVE 0 #define THEME_ACTIVE 1 diff --git a/src/config/rcxml.c b/src/config/rcxml.c index cbe0e81d..280c0256 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -155,6 +155,7 @@ fill_section(const char *content, struct wl_list *list, uint32_t *found_buttons) assert(type != LAB_SSD_NONE); + /* We no longer need this check, but let's keep it just in case */ if (*found_buttons & (1 << type)) { wlr_log(WLR_ERROR, "ignoring duplicated button type '%s'", identifier); diff --git a/src/desktop.c b/src/desktop.c index a7cd86a2..9eabab93 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -302,22 +302,30 @@ get_cursor_context(struct server *server) case LAB_NODE_DESC_VIEW: case LAB_NODE_DESC_XDG_POPUP: ret.view = desc->data; - ret.type = ssd_get_part_type( - ret.view->ssd, ret.node, cursor); - if (ret.type == LAB_SSD_CLIENT) { + if (ret.node->type == WLR_SCENE_NODE_BUFFER + && lab_wlr_surface_from_node(ret.node)) { + ret.type = LAB_SSD_CLIENT; ret.surface = lab_wlr_surface_from_node(ret.node); + } else { + /* should never be reached */ + wlr_log(WLR_ERROR, "cursor not on client or ssd"); } return ret; - case LAB_NODE_DESC_SSD_BUTTON: { - /* - * Always return the top scene node for SSD - * buttons - */ - struct ssd_button *button = - node_ssd_button_from_node(node); + case LAB_NODE_DESC_SSD_PART: { + struct ssd_part *part = node_ssd_part_from_node(node); ret.node = node; - ret.type = ssd_button_get_type(button); - ret.view = ssd_button_get_view(button); + ret.view = ssd_part_get_view(part); + + /* Detect mouse contexts like Top, Left and TRCorner */ + ret.type = ssd_get_resizing_type(ret.view->ssd, cursor); + if (ret.type == LAB_SSD_NONE) { + /* + * Otherwise, detect mouse contexts like + * Title, Titlebar and Iconify + */ + ret.type = ssd_part_get_type(part); + } + return ret; } case LAB_NODE_DESC_LAYER_SURFACE: diff --git a/src/node.c b/src/node.c index ae47044c..37c63a7f 100644 --- a/src/node.c +++ b/src/node.c @@ -71,13 +71,13 @@ node_menuitem_from_node(struct wlr_scene_node *wlr_scene_node) return (struct menuitem *)node_descriptor->data; } -struct ssd_button * -node_ssd_button_from_node(struct wlr_scene_node *wlr_scene_node) +struct ssd_part * +node_ssd_part_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_SSD_BUTTON); - return (struct ssd_button *)node_descriptor->data; + assert(node_descriptor->type == LAB_NODE_DESC_SSD_PART); + return (struct ssd_part *)node_descriptor->data; } struct scaled_scene_buffer * diff --git a/src/ssd/ssd-border.c b/src/ssd/ssd-border.c index 26216ed6..4fff0ad4 100644 --- a/src/ssd/ssd-border.c +++ b/src/ssd/ssd-border.c @@ -2,16 +2,13 @@ #include #include +#include "common/macros.h" #include "common/scene-helpers.h" #include "labwc.h" #include "ssd-internal.h" #include "theme.h" #include "view.h" -#define FOR_EACH_STATE(ssd, tmp) FOR_EACH(tmp, \ - &(ssd)->border.active, \ - &(ssd)->border.inactive) - void ssd_border_create(struct ssd *ssd) { @@ -25,35 +22,37 @@ ssd_border_create(struct ssd *ssd) int full_width = width + 2 * theme->border_width; int corner_width = ssd_get_corner_width(); - float *color; - struct wlr_scene_tree *parent; - struct ssd_sub_tree *subtree; - int active; - ssd->border.tree = wlr_scene_tree_create(ssd->tree); wlr_scene_node_set_position(&ssd->border.tree->node, -theme->border_width, 0); - FOR_EACH_STATE(ssd, subtree) { + enum ssd_active_state active; + FOR_EACH_ACTIVE_STATE(active) { + struct ssd_border_subtree *subtree = &ssd->border.subtrees[active]; subtree->tree = wlr_scene_tree_create(ssd->border.tree); - parent = subtree->tree; - active = (subtree == &ssd->border.active) ? - THEME_ACTIVE : THEME_INACTIVE; + struct wlr_scene_tree *parent = subtree->tree; wlr_scene_node_set_enabled(&parent->node, active); - color = theme->window[active].border_color; + float *color = theme->window[active].border_color; - 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); - add_scene_rect(&subtree->parts, LAB_SSD_PART_TOP, parent, - MAX(width - 2 * corner_width, 0), theme->border_width, + subtree->left = wlr_scene_rect_create(parent, + theme->border_width, height, color); + wlr_scene_node_set_position(&subtree->left->node, 0, 0); + + subtree->right = wlr_scene_rect_create(parent, + theme->border_width, height, color); + wlr_scene_node_set_position(&subtree->right->node, + theme->border_width + width, 0); + + subtree->bottom = wlr_scene_rect_create(parent, + full_width, theme->border_width, color); + wlr_scene_node_set_position(&subtree->bottom->node, + 0, height); + + subtree->top = wlr_scene_rect_create(parent, + MAX(width - 2 * corner_width, 0), theme->border_width, color); + wlr_scene_node_set_position(&subtree->top->node, theme->border_width + corner_width, - -(ssd->titlebar.height + theme->border_width), color); - } FOR_EACH_END + -(ssd->titlebar.height + theme->border_width)); + } if (view->maximized == VIEW_AXIS_BOTH) { wlr_scene_node_set_enabled(&ssd->border.tree->node, false); @@ -129,50 +128,30 @@ ssd_border_update(struct ssd *ssd) ? 0 : theme->border_width + corner_width; - struct ssd_part *part; - struct wlr_scene_rect *rect; - struct ssd_sub_tree *subtree; - FOR_EACH_STATE(ssd, subtree) { - wl_list_for_each(part, &subtree->parts, link) { - rect = wlr_scene_rect_from_node(part->node); - switch (part->type) { - case LAB_SSD_PART_LEFT: - wlr_scene_rect_set_size(rect, - theme->border_width, - side_height); - wlr_scene_node_set_position(part->node, - 0, - side_y); - continue; - case LAB_SSD_PART_RIGHT: - wlr_scene_rect_set_size(rect, - theme->border_width, - side_height); - wlr_scene_node_set_position(part->node, - theme->border_width + width, - side_y); - 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; - case LAB_SSD_PART_TOP: - wlr_scene_rect_set_size(rect, - top_width, - theme->border_width); - wlr_scene_node_set_position(part->node, - top_x, - -(ssd->titlebar.height + theme->border_width)); - continue; - default: - continue; - } - } - } FOR_EACH_END + enum ssd_active_state active; + FOR_EACH_ACTIVE_STATE(active) { + struct ssd_border_subtree *subtree = &ssd->border.subtrees[active]; + + wlr_scene_rect_set_size(subtree->left, + theme->border_width, side_height); + wlr_scene_node_set_position(&subtree->left->node, + 0, side_y); + + wlr_scene_rect_set_size(subtree->right, + theme->border_width, side_height); + wlr_scene_node_set_position(&subtree->right->node, + theme->border_width + width, side_y); + + wlr_scene_rect_set_size(subtree->bottom, + full_width, theme->border_width); + wlr_scene_node_set_position(&subtree->bottom->node, + 0, height); + + wlr_scene_rect_set_size(subtree->top, + top_width, theme->border_width); + wlr_scene_node_set_position(&subtree->top->node, + top_x, -(ssd->titlebar.height + theme->border_width)); + } } void @@ -181,15 +160,6 @@ ssd_border_destroy(struct ssd *ssd) assert(ssd); assert(ssd->border.tree); - struct ssd_sub_tree *subtree; - FOR_EACH_STATE(ssd, subtree) { - ssd_destroy_parts(&subtree->parts); - wlr_scene_node_destroy(&subtree->tree->node); - subtree->tree = NULL; - } FOR_EACH_END - wlr_scene_node_destroy(&ssd->border.tree->node); - ssd->border.tree = NULL; + ssd->border = (struct ssd_border_scene){0}; } - -#undef FOR_EACH_STATE diff --git a/src/ssd/ssd-extents.c b/src/ssd/ssd-extents.c index bb3f661e..8a38c39e 100644 --- a/src/ssd/ssd-extents.c +++ b/src/ssd/ssd-extents.c @@ -11,22 +11,11 @@ #include "theme.h" #include "view.h" -static struct ssd_part * -add_extent(struct wl_list *part_list, enum ssd_part_type type, - struct wlr_scene_tree *parent) -{ - float invisible[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; - struct ssd_part *part = add_scene_part(part_list, type); - part->node = &wlr_scene_rect_create(parent, 0, 0, invisible)->node; - return part; -} - void ssd_extents_create(struct ssd *ssd) { struct view *view = ssd->view; struct theme *theme = view->server->theme; - struct wl_list *part_list = &ssd->extents.parts; int border_width = MAX(0, MAX(rc.resize_minimum_area, theme->border_width)); @@ -35,14 +24,14 @@ ssd_extents_create(struct ssd *ssd) if (view->fullscreen || view->maximized == VIEW_AXIS_BOTH) { wlr_scene_node_set_enabled(&parent->node, false); } - wl_list_init(&ssd->extents.parts); wlr_scene_node_set_position(&parent->node, -border_width, -(ssd->titlebar.height + border_width)); - add_extent(part_list, LAB_SSD_PART_TOP, parent); - add_extent(part_list, LAB_SSD_PART_LEFT, parent); - add_extent(part_list, LAB_SSD_PART_RIGHT, parent); - add_extent(part_list, LAB_SSD_PART_BOTTOM, parent); + float invisible[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + ssd->extents.top = wlr_scene_rect_create(parent, 0, 0, invisible); + ssd->extents.left = wlr_scene_rect_create(parent, 0, 0, invisible); + ssd->extents.right = wlr_scene_rect_create(parent, 0, 0, invisible); + ssd->extents.bottom = wlr_scene_rect_create(parent, 0, 0, invisible); /* Initial manual update to keep X11 applications happy */ ssd_extents_update(ssd); @@ -121,10 +110,6 @@ ssd_extents_update(struct ssd *ssd) int border_width = MAX(rc.resize_minimum_area, theme->border_width); int extended_area = MAX(0, rc.resize_minimum_area - theme->border_width); - struct ssd_part *part; - struct wlr_scene_rect *rect; - struct wlr_box target; - /* Make sure we update the y offset based on titlebar shown / hidden */ wlr_scene_node_set_position(&ssd->extents.tree->node, -border_width, -(ssd->titlebar.height + border_width)); @@ -151,43 +136,19 @@ ssd_extents_update(struct ssd *ssd) wlr_scene_node_coords(&ssd->extents.tree->node, &base_x, &base_y); pixman_region32_translate(&usable, -base_x, -base_y); - wl_list_for_each(part, &ssd->extents.parts, link) { - rect = wlr_scene_rect_from_node(part->node); - switch (part->type) { - case LAB_SSD_PART_TOP: - target.x = 0; - target.y = 0; - target.width = full_width + extended_area * 2; - target.height = extended_area; - break; - case LAB_SSD_PART_LEFT: - target.x = 0; - target.y = extended_area; - target.width = extended_area; - target.height = full_height; - break; - case LAB_SSD_PART_RIGHT: - target.x = extended_area + full_width; - target.y = extended_area; - target.width = extended_area; - target.height = full_height; - break; - case LAB_SSD_PART_BOTTOM: - target.x = 0; - target.y = extended_area + full_height; - target.width = full_width + extended_area * 2; - target.height = extended_area; - break; - default: - /* not reached */ - assert(false); - /* suppress warnings with NDEBUG */ - target = (struct wlr_box){0}; - } + resize_extent_within_usable(ssd->extents.top, &usable, + 0, 0, + full_width + extended_area * 2, extended_area); + resize_extent_within_usable(ssd->extents.left, &usable, + 0, extended_area, + extended_area, full_height); + resize_extent_within_usable(ssd->extents.right, &usable, + extended_area + full_width, extended_area, + extended_area, full_height); + resize_extent_within_usable(ssd->extents.bottom, &usable, + 0, extended_area + full_height, + full_width + extended_area * 2, extended_area); - resize_extent_within_usable(rect, &usable, - target.x, target.y, target.width, target.height); - } pixman_region32_fini(&usable); } @@ -198,7 +159,6 @@ ssd_extents_destroy(struct ssd *ssd) return; } - ssd_destroy_parts(&ssd->extents.parts); wlr_scene_node_destroy(&ssd->extents.tree->node); - ssd->extents.tree = NULL; + ssd->extents = (struct ssd_extents_scene){0}; } diff --git a/src/ssd/ssd-part.c b/src/ssd/ssd-part.c index 317d076c..09dbf6a1 100644 --- a/src/ssd/ssd-part.c +++ b/src/ssd/ssd-part.c @@ -1,100 +1,77 @@ // SPDX-License-Identifier: GPL-2.0-only #include -#include "buffer.h" -#include "common/box.h" +#include "config/rcxml.h" #include "common/list.h" #include "common/mem.h" #include "common/scaled-icon-buffer.h" #include "common/scaled-img-buffer.h" -#include "config/rcxml.h" -#include "labwc.h" #include "node.h" #include "ssd-internal.h" /* Internal helpers */ static void -handle_button_node_destroy(struct wl_listener *listener, void *data) +handle_node_destroy(struct wl_listener *listener, void *data) { - struct ssd_button *button = wl_container_of(listener, button, destroy); - wl_list_remove(&button->destroy.link); - free(button); -} + struct ssd_part *part = wl_container_of(listener, part, node_destroy); + wl_list_remove(&part->node_destroy.link); -/* - * Create a new node_descriptor containing a link to a new ssd_button struct. - * Both will be destroyed automatically once the scene_node they are attached - * to is destroyed. - */ -static struct ssd_button * -ssd_button_descriptor_create(struct wlr_scene_node *node) -{ - /* Create new ssd_button */ - struct ssd_button *button = znew(*button); + struct ssd_part_button *button = button_try_from_ssd_part(part); + if (button) { + wl_list_remove(&button->link); + } - /* Let it destroy automatically when the scene node destroys */ - button->destroy.notify = handle_button_node_destroy; - wl_signal_add(&node->events.destroy, &button->destroy); - - /* And finally attach the ssd_button to a node descriptor */ - node_descriptor_create(node, LAB_NODE_DESC_SSD_BUTTON, button); - return button; + free(part); } /* Internal API */ -struct ssd_part * -add_scene_part(struct wl_list *part_list, enum ssd_part_type type) + +/* + * Create a new node_descriptor containing a link to a new ssd_part struct. + * Both will be destroyed automatically once the scene_node they are attached + * to is destroyed. + */ +static void +init_ssd_part(struct ssd_part *part, enum ssd_part_type type, + struct view *view, struct wlr_scene_node *node) { - struct ssd_part *part = znew(*part); part->type = type; - wl_list_append(part_list, &part->link); - return part; + part->node = node; + part->view = view; + + node_descriptor_create(node, LAB_NODE_DESC_SSD_PART, part); + part->node_destroy.notify = handle_node_destroy; + wl_signal_add(&node->events.destroy, &part->node_destroy); } struct ssd_part * -add_scene_rect(struct wl_list *list, enum ssd_part_type type, - struct wlr_scene_tree *parent, int width, int height, - int x, int y, float color[4]) +attach_ssd_part(enum ssd_part_type type, struct view *view, + struct wlr_scene_node *node) { - assert(width >= 0 && height >= 0); - 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); + assert(!ssd_part_contains(LAB_SSD_BUTTON, type)); + struct ssd_part *part = znew(*part); + init_ssd_part(part, type, view, node); return part; } -struct ssd_part * -add_scene_buffer(struct wl_list *list, enum ssd_part_type type, - struct wlr_scene_tree *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; -} - -struct ssd_part * -add_scene_button(struct wl_list *part_list, enum ssd_part_type type, +struct ssd_part_button * +attach_ssd_part_button(struct wl_list *button_parts, enum ssd_part_type type, struct wlr_scene_tree *parent, struct lab_img *imgs[LAB_BS_ALL + 1], int x, int y, struct view *view) { - struct ssd_part *button_root = add_scene_part(part_list, type); - parent = wlr_scene_tree_create(parent); - button_root->node = &parent->node; - wlr_scene_node_set_position(button_root->node, x, y); + struct wlr_scene_tree *root = wlr_scene_tree_create(parent); + wlr_scene_node_set_position(&root->node, x, y); - struct ssd_button *button = ssd_button_descriptor_create(button_root->node); - button->type = type; - button->view = view; + assert(ssd_part_contains(LAB_SSD_BUTTON, type)); + struct ssd_part_button *button = znew(*button); + init_ssd_part(&button->base, type, view, &root->node); + wl_list_append(button_parts, &button->link); /* Hitbox */ float invisible[4] = { 0, 0, 0, 0 }; - add_scene_rect(part_list, type, parent, - rc.theme->window_button_width, rc.theme->window_button_height, 0, 0, - invisible); + wlr_scene_rect_create(root, rc.theme->window_button_width, + rc.theme->window_button_height, invisible); /* Icons */ int button_width = rc.theme->window_button_width; @@ -110,14 +87,13 @@ add_scene_button(struct wl_list *part_list, enum ssd_part_type type, int icon_padding = button_width / 10; if (type == LAB_SSD_BUTTON_WINDOW_ICON) { - struct ssd_part *icon_part = add_scene_part(part_list, type); struct scaled_icon_buffer *icon_buffer = - scaled_icon_buffer_create(parent, view->server, + scaled_icon_buffer_create(root, view->server, button_width - 2 * icon_padding, button_height); - scaled_icon_buffer_set_view(icon_buffer, view); assert(icon_buffer); - icon_part->node = &icon_buffer->scene_buffer->node; - wlr_scene_node_set_position(icon_part->node, icon_padding, 0); + struct wlr_scene_node *icon_node = &icon_buffer->scene_buffer->node; + scaled_icon_buffer_set_view(icon_buffer, view); + wlr_scene_node_set_position(icon_node, icon_padding, 0); button->window_icon = icon_buffer; } else { for (uint8_t state_set = LAB_BS_DEFAULT; @@ -125,13 +101,12 @@ add_scene_button(struct wl_list *part_list, enum ssd_part_type type, if (!imgs[state_set]) { continue; } - struct ssd_part *icon_part = add_scene_part(part_list, type); struct scaled_img_buffer *img_buffer = scaled_img_buffer_create( - parent, imgs[state_set], rc.theme->window_button_width, + root, imgs[state_set], rc.theme->window_button_width, rc.theme->window_button_height); assert(img_buffer); - icon_part->node = &img_buffer->scene_buffer->node; - wlr_scene_node_set_enabled(icon_part->node, false); + struct wlr_scene_node *icon_node = &img_buffer->scene_buffer->node; + wlr_scene_node_set_enabled(icon_node, false); button->img_buffers[state_set] = img_buffer; } /* Initially show non-hover, non-toggled, unrounded variant */ @@ -139,34 +114,14 @@ add_scene_button(struct wl_list *part_list, enum ssd_part_type type, &button->img_buffers[LAB_BS_DEFAULT]->scene_buffer->node, true); } - return button_root; + return button; } -struct ssd_part * -ssd_get_part(struct wl_list *part_list, enum ssd_part_type type) +struct ssd_part_button * +button_try_from_ssd_part(struct ssd_part *part) { - struct ssd_part *part; - wl_list_for_each(part, part_list, link) { - if (part->type == type) { - return part; - } + if (ssd_part_contains(LAB_SSD_BUTTON, part->type)) { + return (struct ssd_part_button *)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); - part->node = NULL; - } - /* part->buffer will free itself along the scene_buffer node */ - part->buffer = NULL; - wl_list_remove(&part->link); - free(part); - } - assert(wl_list_empty(list)); -} diff --git a/src/ssd/ssd-shadow.c b/src/ssd/ssd-shadow.c index c1743802..2e86c864 100644 --- a/src/ssd/ssd-shadow.c +++ b/src/ssd/ssd-shadow.c @@ -10,10 +10,6 @@ #include "theme.h" #include "view.h" -#define FOR_EACH_STATE(ssd, tmp) FOR_EACH(tmp, \ - &(ssd)->shadow.active, \ - &(ssd)->shadow.inactive) - /* * Implements point_accepts_input for a buffer which never accepts input * because drop-shadows should never catch clicks! @@ -50,12 +46,10 @@ corner_scale_crop(struct wlr_scene_buffer *buffer, int horizontal_overlap, * drop-shadow. */ static void -set_shadow_part_geometry(struct ssd_part *part, int width, int height, - int titlebar_height, int corner_size, int inset, - int visible_shadow_width) +set_shadow_parts_geometry(struct ssd_shadow_subtree *subtree, + int width, int height, int titlebar_height, int corner_size, + int inset, int visible_shadow_width) { - struct wlr_scene_buffer *scene_buf = - wlr_scene_buffer_from_node(part->node); /* * If the shadow inset is greater than half the overall window height * or width (eg. because the window is shaded or because we have a @@ -81,83 +75,69 @@ set_shadow_part_geometry(struct ssd_part *part, int width, int height, * the top-left and bottom-right corners one pixel wider (if the width * is odd) or taller (if the height is odd). */ - if (part->type == LAB_SSD_PART_CORNER_TOP_LEFT - || part->type == LAB_SSD_PART_CORNER_BOTTOM_RIGHT) { - if (horizontal_overlap > 0) { - horizontal_overlap -= width % 2; - } - if (vertical_overlap > 0) { - vertical_overlap -= height % 2; - } + int horizontal_overlap_downsized = horizontal_overlap; + if (horizontal_overlap > 0) { + horizontal_overlap_downsized -= width % 2; + } + int vertical_overlap_downsized = vertical_overlap; + if (vertical_overlap > 0) { + vertical_overlap_downsized -= height % 2; } int x; int y; - switch (part->type) { - case LAB_SSD_PART_CORNER_BOTTOM_RIGHT: - x = width - inset + horizontal_overlap; - y = -titlebar_height + height - inset + vertical_overlap; - wlr_scene_node_set_position(part->node, x, y); - corner_scale_crop(scene_buf, horizontal_overlap, - vertical_overlap, corner_size); - break; - case LAB_SSD_PART_CORNER_BOTTOM_LEFT: - x = -visible_shadow_width; - y = -titlebar_height + height - inset + vertical_overlap; - wlr_scene_node_set_position(part->node, x, y); - corner_scale_crop(scene_buf, horizontal_overlap, - vertical_overlap, corner_size); - break; - case LAB_SSD_PART_CORNER_TOP_LEFT: - x = -visible_shadow_width; - y = -titlebar_height - visible_shadow_width; - wlr_scene_node_set_position(part->node, x, y); - corner_scale_crop(scene_buf, horizontal_overlap, - vertical_overlap, corner_size); - break; - case LAB_SSD_PART_CORNER_TOP_RIGHT: - x = width - inset + horizontal_overlap; - y = -titlebar_height - visible_shadow_width; - wlr_scene_node_set_position(part->node, x, y); - corner_scale_crop(scene_buf, horizontal_overlap, - vertical_overlap, corner_size); - break; - case LAB_SSD_PART_RIGHT: - x = width; - y = -titlebar_height + inset; - wlr_scene_node_set_position(part->node, x, y); - wlr_scene_buffer_set_dest_size( - scene_buf, visible_shadow_width, MAX(height - 2 * inset, 0)); - wlr_scene_node_set_enabled(part->node, show_sides); - break; - case LAB_SSD_PART_BOTTOM: - x = inset; - y = -titlebar_height + height; - wlr_scene_node_set_position(part->node, x, y); - wlr_scene_buffer_set_dest_size( - scene_buf, MAX(width - 2 * inset, 0), visible_shadow_width); - wlr_scene_node_set_enabled(part->node, show_topbottom); - break; - case LAB_SSD_PART_LEFT: - x = -visible_shadow_width; - y = -titlebar_height + inset; - wlr_scene_node_set_position(part->node, x, y); - wlr_scene_buffer_set_dest_size( - scene_buf, visible_shadow_width, MAX(height - 2 * inset, 0)); - wlr_scene_node_set_enabled(part->node, show_sides); - break; - case LAB_SSD_PART_TOP: - x = inset; - y = -titlebar_height - visible_shadow_width; - wlr_scene_node_set_position(part->node, x, y); - wlr_scene_buffer_set_dest_size( - scene_buf, MAX(width - 2 * inset, 0), visible_shadow_width); - wlr_scene_node_set_enabled(part->node, show_topbottom); - break; - default: - break; - } + x = width - inset + horizontal_overlap_downsized; + y = -titlebar_height + height - inset + vertical_overlap_downsized; + wlr_scene_node_set_position(&subtree->bottom_right->node, x, y); + corner_scale_crop(subtree->bottom_right, horizontal_overlap_downsized, + vertical_overlap_downsized, corner_size); + + x = -visible_shadow_width; + y = -titlebar_height + height - inset + vertical_overlap; + wlr_scene_node_set_position(&subtree->bottom_left->node, x, y); + corner_scale_crop(subtree->bottom_left, horizontal_overlap, + vertical_overlap, corner_size); + + x = -visible_shadow_width; + y = -titlebar_height - visible_shadow_width; + wlr_scene_node_set_position(&subtree->top_left->node, x, y); + corner_scale_crop(subtree->top_left, horizontal_overlap_downsized, + vertical_overlap_downsized, corner_size); + + x = width - inset + horizontal_overlap; + y = -titlebar_height - visible_shadow_width; + wlr_scene_node_set_position(&subtree->top_right->node, x, y); + corner_scale_crop(subtree->top_right, horizontal_overlap, + vertical_overlap, corner_size); + + x = width; + y = -titlebar_height + inset; + wlr_scene_node_set_position(&subtree->right->node, x, y); + wlr_scene_buffer_set_dest_size(subtree->right, + visible_shadow_width, MAX(height - 2 * inset, 0)); + wlr_scene_node_set_enabled(&subtree->right->node, show_sides); + + x = inset; + y = -titlebar_height + height; + wlr_scene_node_set_position(&subtree->bottom->node, x, y); + wlr_scene_buffer_set_dest_size(subtree->bottom, + MAX(width - 2 * inset, 0), visible_shadow_width); + wlr_scene_node_set_enabled(&subtree->bottom->node, show_topbottom); + + x = -visible_shadow_width; + y = -titlebar_height + inset; + wlr_scene_node_set_position(&subtree->left->node, x, y); + wlr_scene_buffer_set_dest_size(subtree->left, + visible_shadow_width, MAX(height - 2 * inset, 0)); + wlr_scene_node_set_enabled(&subtree->left->node, show_sides); + + x = inset; + y = -titlebar_height - visible_shadow_width; + wlr_scene_node_set_position(&subtree->top->node, x, y); + wlr_scene_buffer_set_dest_size(subtree->top, + MAX(width - 2 * inset, 0), visible_shadow_width); + wlr_scene_node_set_enabled(&subtree->top->node, show_topbottom); } static void @@ -169,17 +149,14 @@ set_shadow_geometry(struct ssd *ssd) int width = view->current.width; int height = view_effective_height(view, false) + titlebar_height; - struct ssd_part *part; - struct ssd_sub_tree *subtree; - - FOR_EACH_STATE(ssd, subtree) { + enum ssd_active_state active; + FOR_EACH_ACTIVE_STATE(active) { + struct ssd_shadow_subtree *subtree = &ssd->shadow.subtrees[active]; if (!subtree->tree) { /* Looks like this type of shadow is disabled */ continue; } - int active = (subtree == &ssd->shadow.active) ? - THEME_ACTIVE : THEME_INACTIVE; int visible_shadow_width = theme->window[active].shadow_size; /* inset as a proportion of shadow width */ double inset_proportion = SSD_SHADOW_INSET; @@ -194,23 +171,19 @@ set_shadow_geometry(struct ssd *ssd) int corner_size = theme->window[active].shadow_corner_top->logical_height; - wl_list_for_each(part, &subtree->parts, link) { - set_shadow_part_geometry(part, width, height, - titlebar_height, corner_size, inset, - visible_shadow_width); - } - } FOR_EACH_END + set_shadow_parts_geometry(subtree, width, height, + titlebar_height, corner_size, inset, + visible_shadow_width); + } } -static void -make_shadow(struct wl_list *parts, enum ssd_part_type type, +static struct wlr_scene_buffer * +make_shadow(struct view *view, struct wlr_scene_tree *parent, struct wlr_buffer *buf, enum wl_output_transform tx) { - struct ssd_part *part = add_scene_buffer( - parts, type, parent, buf, 0, 0); struct wlr_scene_buffer *scene_buf = - wlr_scene_buffer_from_node(part->node); + wlr_scene_buffer_create(parent, buf); wlr_scene_buffer_set_transform(scene_buf, tx); scene_buf->point_accepts_input = never_accepts_input; /* @@ -218,6 +191,7 @@ make_shadow(struct wl_list *parts, enum ssd_part_type type, * pixel wide/tall. Use nearest-neighbour scaling to workaround. */ scene_buf->filter_mode = WLR_SCALE_FILTER_NEAREST; + return scene_buf; } void @@ -229,51 +203,47 @@ ssd_shadow_create(struct ssd *ssd) ssd->shadow.tree = wlr_scene_tree_create(ssd->tree); struct theme *theme = ssd->view->server->theme; - struct wlr_buffer *corner_top_buffer; - struct wlr_buffer *corner_bottom_buffer; - struct wlr_buffer *edge_buffer; - struct ssd_sub_tree *subtree; - struct wlr_scene_tree *parent; - int active; + struct view *view = ssd->view; - FOR_EACH_STATE(ssd, subtree) { - wl_list_init(&subtree->parts); + enum ssd_active_state active; + FOR_EACH_ACTIVE_STATE(active) { + struct ssd_shadow_subtree *subtree = &ssd->shadow.subtrees[active]; if (!rc.shadows_enabled) { /* Shadows are globally disabled */ continue; } - active = (subtree == &ssd->shadow.active) ? - THEME_ACTIVE : THEME_INACTIVE; if (theme->window[active].shadow_size == 0) { /* Window shadows are disabled */ continue; } subtree->tree = wlr_scene_tree_create(ssd->shadow.tree); - parent = subtree->tree; - corner_top_buffer = &theme->window[active].shadow_corner_top->base; - corner_bottom_buffer = &theme->window[active].shadow_corner_bottom->base; - edge_buffer = &theme->window[active].shadow_edge->base; + struct wlr_scene_tree *parent = subtree->tree; + struct wlr_buffer *corner_top_buffer = + &theme->window[active].shadow_corner_top->base; + struct wlr_buffer *corner_bottom_buffer = + &theme->window[active].shadow_corner_bottom->base; + struct wlr_buffer *edge_buffer = + &theme->window[active].shadow_edge->base; - make_shadow(&subtree->parts, LAB_SSD_PART_CORNER_BOTTOM_RIGHT, - parent, corner_bottom_buffer, WL_OUTPUT_TRANSFORM_NORMAL); - make_shadow(&subtree->parts, LAB_SSD_PART_CORNER_BOTTOM_LEFT, - parent, corner_bottom_buffer, WL_OUTPUT_TRANSFORM_FLIPPED); - make_shadow(&subtree->parts, LAB_SSD_PART_CORNER_TOP_LEFT, - parent, corner_top_buffer, WL_OUTPUT_TRANSFORM_180); - make_shadow(&subtree->parts, LAB_SSD_PART_CORNER_TOP_RIGHT, - parent, corner_top_buffer, WL_OUTPUT_TRANSFORM_FLIPPED_180); - make_shadow(&subtree->parts, LAB_SSD_PART_RIGHT, parent, + subtree->bottom_right = make_shadow(view, parent, + corner_bottom_buffer, WL_OUTPUT_TRANSFORM_NORMAL); + subtree->bottom_left = make_shadow(view, parent, + corner_bottom_buffer, WL_OUTPUT_TRANSFORM_FLIPPED); + subtree->top_left = make_shadow(view, parent, + corner_top_buffer, WL_OUTPUT_TRANSFORM_180); + subtree->top_right = make_shadow(view, parent, + corner_top_buffer, WL_OUTPUT_TRANSFORM_FLIPPED_180); + subtree->right = make_shadow(view, parent, edge_buffer, WL_OUTPUT_TRANSFORM_NORMAL); - make_shadow(&subtree->parts, LAB_SSD_PART_BOTTOM, parent, + subtree->bottom = make_shadow(view, parent, edge_buffer, WL_OUTPUT_TRANSFORM_90); - make_shadow(&subtree->parts, LAB_SSD_PART_LEFT, parent, + subtree->left = make_shadow(view, parent, edge_buffer, WL_OUTPUT_TRANSFORM_180); - make_shadow(&subtree->parts, LAB_SSD_PART_TOP, parent, + subtree->top = make_shadow(view, parent, edge_buffer, WL_OUTPUT_TRANSFORM_270); - - } FOR_EACH_END + } ssd_shadow_update(ssd); } @@ -310,16 +280,6 @@ ssd_shadow_destroy(struct ssd *ssd) assert(ssd); assert(ssd->shadow.tree); - struct ssd_sub_tree *subtree; - FOR_EACH_STATE(ssd, subtree) { - ssd_destroy_parts(&subtree->parts); - /* - * subtree->tree will be destroyed when its - * parent (ssd->shadow.tree) is destroyed. - */ - subtree->tree = NULL; - } FOR_EACH_END - wlr_scene_node_destroy(&ssd->shadow.tree->node); - ssd->shadow.tree = NULL; + ssd->shadow = (struct ssd_shadow_scene){0}; } diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 2cd963ff..fabcaf7a 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -20,10 +20,6 @@ #include "theme.h" #include "view.h" -#define FOR_EACH_STATE(ssd, tmp) FOR_EACH(tmp, \ - &(ssd)->titlebar.active, \ - &(ssd)->titlebar.inactive) - static void set_squared_corners(struct ssd *ssd, bool enable); static void set_alt_button_icon(struct ssd *ssd, enum ssd_part_type type, bool enable); static void update_visible_buttons(struct ssd *ssd); @@ -36,30 +32,26 @@ ssd_titlebar_create(struct ssd *ssd) int width = view->current.width; int corner_width = ssd_get_corner_width(); - struct wlr_scene_tree *parent; - struct wlr_buffer *titlebar_fill; - struct wlr_buffer *corner_top_left; - struct wlr_buffer *corner_top_right; - int active; - ssd->titlebar.tree = wlr_scene_tree_create(ssd->tree); + attach_ssd_part(LAB_SSD_PART_TITLEBAR, view, &ssd->tree->node); - struct ssd_sub_tree *subtree; - FOR_EACH_STATE(ssd, subtree) { + enum ssd_active_state active; + FOR_EACH_ACTIVE_STATE(active) { + struct ssd_titlebar_subtree *subtree = &ssd->titlebar.subtrees[active]; subtree->tree = wlr_scene_tree_create(ssd->titlebar.tree); - parent = subtree->tree; - active = (subtree == &ssd->titlebar.active) ? - THEME_ACTIVE : THEME_INACTIVE; - titlebar_fill = &theme->window[active].titlebar_fill->base; - corner_top_left = &theme->window[active].corner_top_left_normal->base; - corner_top_right = &theme->window[active].corner_top_right_normal->base; + struct wlr_scene_tree *parent = subtree->tree; wlr_scene_node_set_enabled(&parent->node, active); wlr_scene_node_set_position(&parent->node, 0, -theme->titlebar_height); - wl_list_init(&subtree->parts); + + struct wlr_buffer *titlebar_fill = + &theme->window[active].titlebar_fill->base; + struct wlr_buffer *corner_top_left = + &theme->window[active].corner_top_left_normal->base; + struct wlr_buffer *corner_top_right = + &theme->window[active].corner_top_right_normal->base; /* Background */ - struct wlr_scene_buffer *bg_scene_buffer = - wlr_scene_buffer_create(parent, titlebar_fill); + subtree->bar = wlr_scene_buffer_create(parent, titlebar_fill); /* * Work around the wlroots/pixman bug that widened 1px buffer * becomes translucent when bilinear filtering is used. @@ -68,27 +60,25 @@ ssd_titlebar_create(struct ssd *ssd) */ if (wlr_renderer_is_pixman(view->server->renderer)) { wlr_scene_buffer_set_filter_mode( - bg_scene_buffer, WLR_SCALE_FILTER_NEAREST); + subtree->bar, WLR_SCALE_FILTER_NEAREST); } - struct ssd_part *bg_part = - add_scene_part(&subtree->parts, LAB_SSD_PART_TITLEBAR); - bg_part->node = &bg_scene_buffer->node; - wlr_scene_node_set_position(bg_part->node, corner_width, 0); + wlr_scene_node_set_position(&subtree->bar->node, corner_width, 0); - add_scene_buffer(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_LEFT, parent, - corner_top_left, -rc.theme->border_width, -rc.theme->border_width); - add_scene_buffer(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_RIGHT, parent, - corner_top_right, width - corner_width, - -rc.theme->border_width); + subtree->corner_left = wlr_scene_buffer_create(parent, corner_top_left); + wlr_scene_node_set_position(&subtree->corner_left->node, + -rc.theme->border_width, -rc.theme->border_width); + + subtree->corner_right = wlr_scene_buffer_create(parent, corner_top_right); + wlr_scene_node_set_position(&subtree->corner_right->node, + width - corner_width, -rc.theme->border_width); /* Title */ - struct ssd_part *title_part = - add_scene_part(&subtree->parts, LAB_SSD_PART_TITLE); - title_part->buffer = scaled_font_buffer_create_for_titlebar( + subtree->title = scaled_font_buffer_create_for_titlebar( subtree->tree, theme->titlebar_height, theme->window[active].titlebar_pattern); - assert(title_part->buffer); - title_part->node = &title_part->buffer->scene_buffer->node; + assert(subtree->title); + attach_ssd_part(LAB_SSD_PART_TITLE, + view, &subtree->title->scene_buffer->node); /* Buttons */ struct title_button *b; @@ -97,10 +87,13 @@ ssd_titlebar_create(struct ssd *ssd) /* Center vertically within titlebar */ int y = (theme->titlebar_height - theme->window_button_height) / 2; + wl_list_init(&subtree->buttons_left); + wl_list_init(&subtree->buttons_right); + wl_list_for_each(b, &rc.title_buttons_left, link) { struct lab_img **imgs = theme->window[active].button_imgs[b->type]; - add_scene_button(&subtree->parts, b->type, parent, + attach_ssd_part_button(&subtree->buttons_left, b->type, parent, imgs, x, y, view); x += theme->window_button_width + theme->window_button_spacing; } @@ -110,10 +103,10 @@ ssd_titlebar_create(struct ssd *ssd) x -= theme->window_button_width + theme->window_button_spacing; struct lab_img **imgs = theme->window[active].button_imgs[b->type]; - add_scene_button(&subtree->parts, b->type, parent, + attach_ssd_part_button(&subtree->buttons_right, b->type, parent, imgs, x, y, view); } - } FOR_EACH_END + } update_visible_buttons(ssd); @@ -140,7 +133,7 @@ ssd_titlebar_create(struct ssd *ssd) } static void -update_button_state(struct ssd_button *button, enum lab_button_state state, +update_button_state(struct ssd_part_button *button, enum lab_button_state state, bool enable) { if (enable) { @@ -168,56 +161,54 @@ set_squared_corners(struct ssd *ssd, bool enable) int corner_width = ssd_get_corner_width(); struct theme *theme = view->server->theme; - struct ssd_part *part; - struct ssd_sub_tree *subtree; int x = enable ? 0 : corner_width; - FOR_EACH_STATE(ssd, subtree) { - part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR); - wlr_scene_node_set_position(part->node, x, 0); - wlr_scene_buffer_set_dest_size( - wlr_scene_buffer_from_node(part->node), + enum ssd_active_state active; + FOR_EACH_ACTIVE_STATE(active) { + struct ssd_titlebar_subtree *subtree = &ssd->titlebar.subtrees[active]; + + wlr_scene_node_set_position(&subtree->bar->node, x, 0); + wlr_scene_buffer_set_dest_size(subtree->bar, MAX(width - 2 * x, 0), theme->titlebar_height); - part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_LEFT); - wlr_scene_node_set_enabled(part->node, !enable); + wlr_scene_node_set_enabled(&subtree->corner_left->node, !enable); - part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_RIGHT); - wlr_scene_node_set_enabled(part->node, !enable); + wlr_scene_node_set_enabled(&subtree->corner_right->node, !enable); /* (Un)round the corner buttons */ - struct title_button *title_button; - wl_list_for_each(title_button, &rc.title_buttons_left, link) { - part = ssd_get_part(&subtree->parts, title_button->type); - struct ssd_button *button = node_ssd_button_from_node(part->node); + struct ssd_part_button *button; + wl_list_for_each(button, &subtree->buttons_left, link) { update_button_state(button, LAB_BS_ROUNDED, !enable); break; } - wl_list_for_each_reverse(title_button, &rc.title_buttons_right, link) { - part = ssd_get_part(&subtree->parts, title_button->type); - struct ssd_button *button = node_ssd_button_from_node(part->node); + wl_list_for_each(button, &subtree->buttons_right, link) { update_button_state(button, LAB_BS_ROUNDED, !enable); break; } - } FOR_EACH_END + } } static void set_alt_button_icon(struct ssd *ssd, enum ssd_part_type type, bool enable) { - struct ssd_part *part; - struct ssd_button *button; - struct ssd_sub_tree *subtree; + enum ssd_active_state active; + FOR_EACH_ACTIVE_STATE(active) { + struct ssd_titlebar_subtree *subtree = &ssd->titlebar.subtrees[active]; - FOR_EACH_STATE(ssd, subtree) { - part = ssd_get_part(&subtree->parts, type); - if (!part) { - return; + struct ssd_part_button *button; + wl_list_for_each(button, &subtree->buttons_left, link) { + if (button->base.type == type) { + update_button_state(button, + LAB_BS_TOGGLED, enable); + } } - - button = node_ssd_button_from_node(part->node); - update_button_state(button, LAB_BS_TOGGLED, enable); - } FOR_EACH_END + wl_list_for_each(button, &subtree->buttons_right, link) { + if (button->base.type == type) { + update_button_state(button, + LAB_BS_TOGGLED, enable); + } + } + } } /* @@ -254,27 +245,25 @@ update_visible_buttons(struct ssd *ssd) } } - int button_count; - struct ssd_part *part; - struct ssd_sub_tree *subtree; - struct title_button *b; - FOR_EACH_STATE(ssd, subtree) { - button_count = 0; - wl_list_for_each(b, &rc.title_buttons_left, link) { - part = ssd_get_part(&subtree->parts, b->type); - wlr_scene_node_set_enabled(part->node, + enum ssd_active_state active; + FOR_EACH_ACTIVE_STATE(active) { + struct ssd_titlebar_subtree *subtree = &ssd->titlebar.subtrees[active]; + int button_count = 0; + + struct ssd_part_button *button; + wl_list_for_each(button, &subtree->buttons_left, link) { + wlr_scene_node_set_enabled(button->base.node, button_count < button_count_left); button_count++; } button_count = 0; - wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { - part = ssd_get_part(&subtree->parts, b->type); - wlr_scene_node_set_enabled(part->node, + wl_list_for_each(button, &subtree->buttons_right, link) { + wlr_scene_node_set_enabled(button->base.node, button_count < button_count_right); button_count++; } - } FOR_EACH_END + } } void @@ -318,34 +307,31 @@ ssd_titlebar_update(struct ssd *ssd) /* Center buttons vertically within titlebar */ int y = (theme->titlebar_height - theme->window_button_height) / 2; int x; - struct ssd_part *part; - struct ssd_sub_tree *subtree; - struct title_button *b; int bg_offset = maximized || squared ? 0 : corner_width; - FOR_EACH_STATE(ssd, subtree) { - part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR); - wlr_scene_buffer_set_dest_size( - wlr_scene_buffer_from_node(part->node), + + enum ssd_active_state active; + FOR_EACH_ACTIVE_STATE(active) { + struct ssd_titlebar_subtree *subtree = &ssd->titlebar.subtrees[active]; + wlr_scene_buffer_set_dest_size(subtree->bar, MAX(width - bg_offset * 2, 0), theme->titlebar_height); x = theme->window_titlebar_padding_width; - wl_list_for_each(b, &rc.title_buttons_left, link) { - part = ssd_get_part(&subtree->parts, b->type); - wlr_scene_node_set_position(part->node, x, y); + struct ssd_part_button *button; + wl_list_for_each(button, &subtree->buttons_left, link) { + wlr_scene_node_set_position(button->base.node, x, y); x += theme->window_button_width + theme->window_button_spacing; } x = width - corner_width; - part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_RIGHT); - wlr_scene_node_set_position(part->node, x, -rc.theme->border_width); + wlr_scene_node_set_position(&subtree->corner_right->node, + x, -rc.theme->border_width); x = width - theme->window_titlebar_padding_width + theme->window_button_spacing; - wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { - part = ssd_get_part(&subtree->parts, b->type); + wl_list_for_each(button, &subtree->buttons_right, link) { x -= theme->window_button_width + theme->window_button_spacing; - wlr_scene_node_set_position(part->node, x, y); + wlr_scene_node_set_position(button->base.node, x, y); } - } FOR_EACH_END + } ssd_update_title(ssd); } @@ -357,19 +343,9 @@ ssd_titlebar_destroy(struct ssd *ssd) return; } - struct ssd_sub_tree *subtree; - FOR_EACH_STATE(ssd, subtree) { - ssd_destroy_parts(&subtree->parts); - wlr_scene_node_destroy(&subtree->tree->node); - subtree->tree = NULL; - } FOR_EACH_END - - if (ssd->state.title.text) { - zfree(ssd->state.title.text); - } - + zfree(ssd->state.title.text); wlr_scene_node_destroy(&ssd->titlebar.tree->node); - ssd->titlebar.tree = NULL; + ssd->titlebar = (struct ssd_titlebar_scene){0}; } /* @@ -392,43 +368,40 @@ ssd_update_title_positions(struct ssd *ssd, int offset_left, int offset_right) int width = view->current.width; int title_bg_width = width - offset_left - offset_right; - int x, y; - int buffer_height, buffer_width; - struct ssd_part *part; - struct ssd_sub_tree *subtree; - FOR_EACH_STATE(ssd, subtree) { - part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLE); + enum ssd_active_state active; + FOR_EACH_ACTIVE_STATE(active) { + struct ssd_titlebar_subtree *subtree = &ssd->titlebar.subtrees[active]; + struct scaled_font_buffer *title = subtree->title; + int x, y; - buffer_width = part->buffer ? part->buffer->width : 0; - buffer_height = part->buffer ? part->buffer->height : 0; x = offset_left; - y = (theme->titlebar_height - buffer_height) / 2; + y = (theme->titlebar_height - title->height) / 2; if (title_bg_width <= 0) { - wlr_scene_node_set_enabled(part->node, false); + wlr_scene_node_set_enabled(&title->scene_buffer->node, false); continue; } - wlr_scene_node_set_enabled(part->node, true); + wlr_scene_node_set_enabled(&title->scene_buffer->node, true); if (theme->window_label_text_justify == LAB_JUSTIFY_CENTER) { - if (buffer_width + MAX(offset_left, offset_right) * 2 <= width) { + if (title->width + MAX(offset_left, offset_right) * 2 <= width) { /* Center based on the full width */ - x = (width - buffer_width) / 2; + x = (width - title->width) / 2; } 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 += (title_bg_width - buffer_width) / 2; + x += (title_bg_width - title->width) / 2; } } else if (theme->window_label_text_justify == LAB_JUSTIFY_RIGHT) { - x += title_bg_width - buffer_width; + x += title_bg_width - title->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 + wlr_scene_node_set_position(&title->scene_buffer->node, x, y); + } } /* @@ -438,23 +411,21 @@ ssd_update_title_positions(struct ssd *ssd, int offset_left, int offset_right) static void get_title_offsets(struct ssd *ssd, int *offset_left, int *offset_right) { - struct ssd_sub_tree *subtree = &ssd->titlebar.active; + struct ssd_titlebar_subtree *subtree = &ssd->titlebar.subtrees[SSD_ACTIVE]; int button_width = ssd->view->server->theme->window_button_width; int button_spacing = ssd->view->server->theme->window_button_spacing; int padding_width = ssd->view->server->theme->window_titlebar_padding_width; *offset_left = padding_width; *offset_right = padding_width; - struct title_button *b; - wl_list_for_each(b, &rc.title_buttons_left, link) { - struct ssd_part *part = ssd_get_part(&subtree->parts, b->type); - if (part->node->enabled) { + struct ssd_part_button *button; + wl_list_for_each(button, &subtree->buttons_left, link) { + if (button->base.node->enabled) { *offset_left += button_width + button_spacing; } } - wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { - struct ssd_part *part = ssd_get_part(&subtree->parts, b->type); - if (part->node->enabled) { + wl_list_for_each(button, &subtree->buttons_right, link) { + if (button->base.node->enabled) { *offset_right += button_width + button_spacing; } } @@ -477,24 +448,17 @@ ssd_update_title(struct ssd *ssd) struct ssd_state_title *state = &ssd->state.title; bool title_unchanged = state->text && !strcmp(title, state->text); - const float *text_color; - const float bg_color[4] = {0, 0, 0, 0}; /* ignored */ - struct font *font = NULL; - struct ssd_part *part; - struct ssd_sub_tree *subtree; - struct ssd_state_title_width *dstate; - int active; - int offset_left, offset_right; get_title_offsets(ssd, &offset_left, &offset_right); int title_bg_width = view->current.width - offset_left - offset_right; - FOR_EACH_STATE(ssd, subtree) { - active = (subtree == &ssd->titlebar.active) ? - THEME_ACTIVE : THEME_INACTIVE; - dstate = active ? &state->active : &state->inactive; - text_color = theme->window[active].label_text_color; - font = active ? &rc.font_activewindow : &rc.font_inactivewindow; + enum ssd_active_state active; + FOR_EACH_ACTIVE_STATE(active) { + struct ssd_titlebar_subtree *subtree = &ssd->titlebar.subtrees[active]; + struct ssd_state_title_width *dstate = &state->dstates[active]; + const float *text_color = theme->window[active].label_text_color; + struct font *font = active ? + &rc.font_activewindow : &rc.font_inactivewindow; if (title_bg_width <= 0) { dstate->truncated = true; @@ -507,15 +471,15 @@ ssd_update_title(struct ssd *ssd) continue; } - part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLE); - scaled_font_buffer_update(part->buffer, title, title_bg_width, - font, text_color, bg_color); + const float bg_color[4] = {0, 0, 0, 0}; /* ignored */ + scaled_font_buffer_update(subtree->title, title, + title_bg_width, font, + text_color, bg_color); /* And finally update the cache */ - dstate->width = part->buffer ? part->buffer->width : 0; + dstate->width = subtree->title->width; dstate->truncated = title_bg_width <= dstate->width; - - } FOR_EACH_END + } if (!title_unchanged) { if (state->text) { @@ -530,12 +494,13 @@ void ssd_update_button_hover(struct wlr_scene_node *node, struct ssd_hover_state *hover_state) { - struct ssd_button *button = NULL; + struct ssd_part_button *button = NULL; if (node && node->data) { struct node_descriptor *desc = node->data; - if (desc->type == LAB_NODE_DESC_SSD_BUTTON) { - button = node_ssd_button_from_node(node); + if (desc->type == LAB_NODE_DESC_SSD_PART) { + button = button_try_from_ssd_part( + node_ssd_part_from_node(node)); if (button == hover_state->button) { /* Cursor is still on the same button */ return; @@ -551,7 +516,7 @@ ssd_update_button_hover(struct wlr_scene_node *node, } if (button) { update_button_state(button, LAB_BS_HOVERD, true); - hover_state->view = button->view; + hover_state->view = button->base.view; hover_state->button = button; } } @@ -566,5 +531,3 @@ ssd_should_be_squared(struct ssd *ssd) || view->current.width < corner_width * 2) && view->maximized != VIEW_AXIS_BOTH; } - -#undef FOR_EACH_STATE diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index 7a45f37e..adf7c2cb 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -85,8 +85,8 @@ ssd_max_extents(struct view *view) * (generally rc.resize_corner_range, but clipped to view size) of the view * bounds, so check the cursor against the view here. */ -static enum ssd_part_type -get_resizing_type(const struct ssd *ssd, struct wlr_cursor *cursor) +enum ssd_part_type +ssd_get_resizing_type(const struct ssd *ssd, struct wlr_cursor *cursor) { struct view *view = ssd ? ssd->view : NULL; if (!view || !cursor || !view->ssd_mode || view->fullscreen) { @@ -136,75 +136,6 @@ get_resizing_type(const struct ssd *ssd, struct wlr_cursor *cursor) return LAB_SSD_NONE; } -enum ssd_part_type -ssd_get_part_type(const struct ssd *ssd, struct wlr_scene_node *node, - struct wlr_cursor *cursor) -{ - if (!node) { - return LAB_SSD_NONE; - } else if (node->type == WLR_SCENE_NODE_BUFFER - && lab_wlr_surface_from_node(node)) { - return LAB_SSD_CLIENT; - } else if (!ssd) { - return LAB_SSD_NONE; - } - - const struct wl_list *part_list = NULL; - struct wlr_scene_tree *grandparent = - node->parent ? node->parent->node.parent : NULL; - struct wlr_scene_tree *greatgrandparent = - grandparent ? grandparent->node.parent : NULL; - - /* active titlebar */ - if (node->parent == ssd->titlebar.active.tree) { - part_list = &ssd->titlebar.active.parts; - } else if (grandparent == ssd->titlebar.active.tree) { - part_list = &ssd->titlebar.active.parts; - } else if (greatgrandparent == ssd->titlebar.active.tree) { - part_list = &ssd->titlebar.active.parts; - - /* extents */ - } else if (node->parent == ssd->extents.tree) { - part_list = &ssd->extents.parts; - - /* active border */ - } else if (node->parent == ssd->border.active.tree) { - part_list = &ssd->border.active.parts; - - /* inactive titlebar */ - } else if (node->parent == ssd->titlebar.inactive.tree) { - part_list = &ssd->titlebar.inactive.parts; - } else if (grandparent == ssd->titlebar.inactive.tree) { - part_list = &ssd->titlebar.inactive.parts; - } else if (greatgrandparent == ssd->titlebar.inactive.tree) { - part_list = &ssd->titlebar.inactive.parts; - - /* inactive border */ - } else if (node->parent == ssd->border.inactive.tree) { - part_list = &ssd->border.inactive.parts; - } - - enum ssd_part_type part_type = LAB_SSD_NONE; - - if (part_list) { - struct ssd_part *part; - wl_list_for_each(part, part_list, link) { - if (node == part->node) { - part_type = part->type; - break; - } - } - } - - if (part_type == LAB_SSD_NONE) { - return part_type; - } - - /* Perform cursor-based context checks */ - enum ssd_part_type resizing_type = get_resizing_type(ssd, cursor); - return resizing_type != LAB_SSD_NONE ? resizing_type : part_type; -} - enum lab_edge ssd_resize_edges(enum ssd_part_type type) { @@ -238,6 +169,7 @@ ssd_create(struct view *view, bool active) ssd->view = view; ssd->tree = wlr_scene_tree_create(view->scene_tree); + attach_ssd_part(LAB_SSD_NONE, view, &ssd->tree->node); wlr_scene_node_lower_to_bottom(&ssd->tree->node); ssd->titlebar.height = view->server->theme->titlebar_height; ssd_shadow_create(ssd); @@ -438,17 +370,19 @@ ssd_set_active(struct ssd *ssd, bool active) if (!ssd) { return; } - wlr_scene_node_set_enabled(&ssd->border.active.tree->node, active); - wlr_scene_node_set_enabled(&ssd->titlebar.active.tree->node, active); - if (ssd->shadow.active.tree) { + enum ssd_active_state active_state; + FOR_EACH_ACTIVE_STATE(active_state) { wlr_scene_node_set_enabled( - &ssd->shadow.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); - if (ssd->shadow.inactive.tree) { + &ssd->border.subtrees[active_state].tree->node, + active == active_state); wlr_scene_node_set_enabled( - &ssd->shadow.inactive.tree->node, !active); + &ssd->titlebar.subtrees[active_state].tree->node, + active == active_state); + if (ssd->shadow.subtrees[active_state].tree) { + wlr_scene_node_set_enabled( + &ssd->shadow.subtrees[active_state].tree->node, + active == active_state); + } } } @@ -474,10 +408,7 @@ ssd_enable_keybind_inhibit_indicator(struct ssd *ssd, bool enable) float *color = enable ? rc.theme->window_toggled_keybinds_color : rc.theme->window[THEME_ACTIVE].border_color; - - struct ssd_part *part = ssd_get_part(&ssd->border.active.parts, LAB_SSD_PART_TOP); - struct wlr_scene_rect *rect = wlr_scene_rect_from_node(part->node); - wlr_scene_rect_set_color(rect, color); + wlr_scene_rect_set_color(ssd->border.subtrees[SSD_ACTIVE].top, color); } struct ssd_hover_state * @@ -487,15 +418,15 @@ ssd_hover_state_new(void) } enum ssd_part_type -ssd_button_get_type(const struct ssd_button *button) +ssd_part_get_type(const struct ssd_part *part) { - return button ? button->type : LAB_SSD_NONE; + return part ? part->type : LAB_SSD_NONE; } struct view * -ssd_button_get_view(const struct ssd_button *button) +ssd_part_get_view(const struct ssd_part *part) { - return button ? button->view : NULL; + return part ? part->view : NULL; } bool @@ -516,16 +447,16 @@ ssd_debug_get_node_name(const struct ssd *ssd, struct wlr_scene_node *node) if (node == &ssd->tree->node) { return "view->ssd"; } - if (node == &ssd->titlebar.active.tree->node) { + if (node == &ssd->titlebar.subtrees[SSD_ACTIVE].tree->node) { return "titlebar.active"; } - if (node == &ssd->titlebar.inactive.tree->node) { + if (node == &ssd->titlebar.subtrees[SSD_INACTIVE].tree->node) { return "titlebar.inactive"; } - if (node == &ssd->border.active.tree->node) { + if (node == &ssd->border.subtrees[SSD_ACTIVE].tree->node) { return "border.active"; } - if (node == &ssd->border.inactive.tree->node) { + if (node == &ssd->border.subtrees[SSD_INACTIVE].tree->node) { return "border.inactive"; } if (node == &ssd->extents.tree->node) { From 57a1ea6cb522b4dccc469880efc145e6db7e629f Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Fri, 6 Dec 2024 19:08:27 +0900 Subject: [PATCH 119/248] Move osd.c and osd-field.c into src/osd --- src/meson.build | 7 +++---- src/osd/meson.build | 4 ++++ src/{ => osd}/osd-field.c | 0 src/{ => osd}/osd.c | 0 4 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 src/osd/meson.build rename src/{ => osd}/osd-field.c (100%) rename src/{ => osd}/osd.c (100%) diff --git a/src/meson.build b/src/meson.build index 40cf0f69..24338994 100644 --- a/src/meson.build +++ b/src/meson.build @@ -11,8 +11,6 @@ labwc_sources = files( 'magnifier.c', 'main.c', 'node.c', - 'osd.c', - 'osd-field.c', 'output.c', 'output-state.c', 'output-virtual.c', @@ -49,12 +47,13 @@ if have_libsfdo ) endif -subdir('img') subdir('common') subdir('config') subdir('decorations') subdir('foreign-toplevel') +subdir('img') subdir('input') subdir('menu') -subdir('ssd') +subdir('osd') subdir('protocols') +subdir('ssd') diff --git a/src/osd/meson.build b/src/osd/meson.build new file mode 100644 index 00000000..0ea1abd3 --- /dev/null +++ b/src/osd/meson.build @@ -0,0 +1,4 @@ +labwc_sources += files( + 'osd.c', + 'osd-field.c', +) diff --git a/src/osd-field.c b/src/osd/osd-field.c similarity index 100% rename from src/osd-field.c rename to src/osd/osd-field.c diff --git a/src/osd.c b/src/osd/osd.c similarity index 100% rename from src/osd.c rename to src/osd/osd.c From 92ee5083f035a807cf7185eb2ee303988d0482d4 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Fri, 8 Aug 2025 12:29:08 +0900 Subject: [PATCH 120/248] osd: split to osd.c and osd-classic.c --- include/osd.h | 18 ++++ src/osd/meson.build | 1 + src/osd/osd-classic.c | 218 ++++++++++++++++++++++++++++++++++++++++++ src/osd/osd.c | 197 +------------------------------------- 4 files changed, 240 insertions(+), 194 deletions(-) create mode 100644 src/osd/osd-classic.c diff --git a/include/osd.h b/include/osd.h index 1457aeb2..ad0b3bce 100644 --- a/include/osd.h +++ b/include/osd.h @@ -5,6 +5,8 @@ #include #include +struct output; + enum lab_cycle_dir { LAB_CYCLE_DIR_NONE, LAB_CYCLE_DIR_FORWARD, @@ -66,4 +68,20 @@ void osd_field_arg_from_xml_node(struct window_switcher_field *field, bool osd_field_is_valid(struct window_switcher_field *field); void osd_field_free(struct window_switcher_field *field); +/* Internal API */ +struct osd_impl { + /* + * Create a scene-tree of OSD for an output. + * This sets output->osd_scene.{items,tree}. + */ + void (*create)(struct output *output, struct wl_array *views); + /* + * Update output->osd_scene.tree to highlight + * server->osd_state.cycle_view. + */ + void (*update)(struct output *output); +}; + +extern struct osd_impl osd_classic_impl; + #endif // LABWC_OSD_H diff --git a/src/osd/meson.build b/src/osd/meson.build index 0ea1abd3..4f4da2cb 100644 --- a/src/osd/meson.build +++ b/src/osd/meson.build @@ -1,4 +1,5 @@ labwc_sources += files( 'osd.c', + 'osd-classic.c', 'osd-field.c', ) diff --git a/src/osd/osd-classic.c b/src/osd/osd-classic.c new file mode 100644 index 00000000..24679378 --- /dev/null +++ b/src/osd/osd-classic.c @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include "common/array.h" +#include "common/buf.h" +#include "common/font.h" +#include "common/lab-scene-rect.h" +#include "common/scaled-font-buffer.h" +#include "common/scaled-icon-buffer.h" +#include "common/scene-helpers.h" +#include "common/string-helpers.h" +#include "config/rcxml.h" +#include "labwc.h" +#include "node.h" +#include "osd.h" +#include "output.h" +#include "theme.h" +#include "view.h" +#include "window-rules.h" +#include "workspaces.h" + +struct osd_classic_scene_item { + struct view *view; + struct wlr_scene_node *highlight_outline; +}; + +static void +osd_classic_create(struct output *output, struct wl_array *views) +{ + assert(!output->osd_scene.tree); + + struct server *server = output->server; + struct theme *theme = server->theme; + bool show_workspace = wl_list_length(&rc.workspace_config.workspaces) > 1; + const char *workspace_name = server->workspaces.current->name; + + int w = theme->osd_window_switcher_width; + if (theme->osd_window_switcher_width_is_percent) { + w = output->wlr_output->width + * theme->osd_window_switcher_width / 100; + } + int h = wl_array_len(views) * rc.theme->osd_window_switcher_item_height + + 2 * rc.theme->osd_border_width + + 2 * rc.theme->osd_window_switcher_padding; + if (show_workspace) { + /* workspace indicator */ + h += theme->osd_window_switcher_item_height; + } + + output->osd_scene.tree = wlr_scene_tree_create(output->osd_tree); + + float *text_color = theme->osd_label_text_color; + float *bg_color = theme->osd_bg_color; + + /* Draw background */ + struct lab_scene_rect_options bg_opts = { + .border_colors = (float *[1]) {theme->osd_border_color}, + .nr_borders = 1, + .border_width = theme->osd_border_width, + .bg_color = bg_color, + .width = w, + .height = h, + }; + lab_scene_rect_create(output->osd_scene.tree, &bg_opts); + + int y = theme->osd_border_width + theme->osd_window_switcher_padding; + + /* Draw workspace indicator */ + if (show_workspace) { + struct font font = rc.font_osd; + font.weight = PANGO_WEIGHT_BOLD; + + /* Center workspace indicator on the x axis */ + int x = (w - font_width(&font, workspace_name)) / 2; + if (x < 0) { + wlr_log(WLR_ERROR, + "not enough space for workspace name in osd"); + goto error; + } + + struct scaled_font_buffer *font_buffer = + scaled_font_buffer_create(output->osd_scene.tree); + wlr_scene_node_set_position(&font_buffer->scene_buffer->node, + x, y + (theme->osd_window_switcher_item_height + - font_height(&font)) / 2); + scaled_font_buffer_update(font_buffer, workspace_name, 0, + &font, text_color, bg_color); + y += theme->osd_window_switcher_item_height; + } + + struct buf buf = BUF_INIT; + int nr_fields = wl_list_length(&rc.window_switcher.fields); + + /* This is the width of the area available for text fields */ + int field_widths_sum = w - 2 * theme->osd_border_width + - 2 * theme->osd_window_switcher_padding + - 2 * theme->osd_window_switcher_item_active_border_width + - (nr_fields + 1) * theme->osd_window_switcher_item_padding_x; + if (field_widths_sum <= 0) { + wlr_log(WLR_ERROR, "Not enough spaces for osd contents"); + goto error; + } + + /* Draw text for each node */ + struct view **view; + wl_array_for_each(view, views) { + struct osd_classic_scene_item *item = + wl_array_add(&output->osd_scene.items, sizeof(*item)); + item->view = *view; + /* + * OSD border + * +---------------------------------+ + * | | + * | item border | + * |+-------------------------------+| + * || || + * ||padding between each field || + * ||| field-1 | field-2 | field-n ||| + * || || + * || || + * |+-------------------------------+| + * | | + * | | + * +---------------------------------+ + */ + int x = theme->osd_border_width + + theme->osd_window_switcher_padding + + theme->osd_window_switcher_item_active_border_width + + theme->osd_window_switcher_item_padding_x; + struct wlr_scene_tree *item_root = + wlr_scene_tree_create(output->osd_scene.tree); + + struct window_switcher_field *field; + wl_list_for_each(field, &rc.window_switcher.fields, link) { + int field_width = field_widths_sum * field->width / 100.0; + struct wlr_scene_node *node = NULL; + int height = -1; + + if (field->content == LAB_FIELD_ICON) { + int icon_size = MIN(field_width, + theme->osd_window_switcher_item_icon_size); + struct scaled_icon_buffer *icon_buffer = + scaled_icon_buffer_create(item_root, + server, icon_size, icon_size); + scaled_icon_buffer_set_view(icon_buffer, *view); + node = &icon_buffer->scene_buffer->node; + height = icon_size; + } else { + buf_clear(&buf); + osd_field_get_content(field, &buf, *view); + + if (!string_null_or_empty(buf.data)) { + struct scaled_font_buffer *font_buffer = + scaled_font_buffer_create(item_root); + scaled_font_buffer_update(font_buffer, + buf.data, field_width, + &rc.font_osd, text_color, bg_color); + node = &font_buffer->scene_buffer->node; + height = font_height(&rc.font_osd); + } + } + + if (node) { + int item_height = + theme->osd_window_switcher_item_height; + wlr_scene_node_set_position(node, + x, y + (item_height - height) / 2); + } + x += field_width + theme->osd_window_switcher_item_padding_x; + } + + /* Highlight around selected window's item */ + int highlight_x = theme->osd_border_width + + theme->osd_window_switcher_padding; + struct lab_scene_rect_options highlight_opts = { + .border_colors = (float *[1]) {text_color}, + .nr_borders = 1, + .border_width = + theme->osd_window_switcher_item_active_border_width, + .width = w - 2 * theme->osd_border_width + - 2 * theme->osd_window_switcher_padding, + .height = theme->osd_window_switcher_item_height, + }; + + struct lab_scene_rect *highlight_rect = lab_scene_rect_create( + output->osd_scene.tree, &highlight_opts); + item->highlight_outline = &highlight_rect->tree->node; + wlr_scene_node_set_position(item->highlight_outline, highlight_x, y); + wlr_scene_node_set_enabled(item->highlight_outline, false); + + y += theme->osd_window_switcher_item_height; + } + buf_reset(&buf); + +error:; + /* Center OSD */ + struct wlr_box usable = output_usable_area_in_layout_coords(output); + wlr_scene_node_set_position(&output->osd_scene.tree->node, + usable.x + usable.width / 2 - w / 2, + usable.y + usable.height / 2 - h / 2); +} + +static void +osd_classic_update(struct output *output) +{ + struct osd_classic_scene_item *item; + wl_array_for_each(item, &output->osd_scene.items) { + wlr_scene_node_set_enabled(item->highlight_outline, + item->view == output->server->osd_state.cycle_view); + } +} + +struct osd_impl osd_classic_impl = { + .create = osd_classic_create, + .update = osd_classic_update, +}; diff --git a/src/osd/osd.c b/src/osd/osd.c index 39846795..51a0721c 100644 --- a/src/osd/osd.c +++ b/src/osd/osd.c @@ -5,13 +5,10 @@ #include #include #include "common/array.h" -#include "common/buf.h" -#include "common/font.h" #include "common/lab-scene-rect.h" #include "common/scaled-font-buffer.h" #include "common/scaled-icon-buffer.h" #include "common/scene-helpers.h" -#include "common/string-helpers.h" #include "config/rcxml.h" #include "labwc.h" #include "node.h" @@ -21,11 +18,6 @@ #include "window-rules.h" #include "workspaces.h" -struct osd_scene_item { - struct view *view; - struct wlr_scene_node *highlight_outline; -}; - static void update_osd(struct server *server); static void @@ -267,196 +259,13 @@ preview_cycled_view(struct view *view) wlr_scene_node_raise_to_top(osd_state->preview_node); } -static void -create_osd_scene(struct output *output, struct wl_array *views) -{ - struct server *server = output->server; - struct theme *theme = server->theme; - bool show_workspace = wl_list_length(&rc.workspace_config.workspaces) > 1; - const char *workspace_name = server->workspaces.current->name; - - int w = theme->osd_window_switcher_width; - if (theme->osd_window_switcher_width_is_percent) { - w = output->wlr_output->width - * theme->osd_window_switcher_width / 100; - } - int h = wl_array_len(views) * rc.theme->osd_window_switcher_item_height - + 2 * rc.theme->osd_border_width - + 2 * rc.theme->osd_window_switcher_padding; - if (show_workspace) { - /* workspace indicator */ - h += theme->osd_window_switcher_item_height; - } - - output->osd_scene.tree = wlr_scene_tree_create(output->osd_tree); - - float *text_color = theme->osd_label_text_color; - float *bg_color = theme->osd_bg_color; - - /* Draw background */ - struct lab_scene_rect_options bg_opts = { - .border_colors = (float *[1]) {theme->osd_border_color}, - .nr_borders = 1, - .border_width = theme->osd_border_width, - .bg_color = bg_color, - .width = w, - .height = h, - }; - lab_scene_rect_create(output->osd_scene.tree, &bg_opts); - - int y = theme->osd_border_width + theme->osd_window_switcher_padding; - - /* Draw workspace indicator */ - if (show_workspace) { - struct font font = rc.font_osd; - font.weight = PANGO_WEIGHT_BOLD; - - /* Center workspace indicator on the x axis */ - int x = (w - font_width(&font, workspace_name)) / 2; - if (x < 0) { - wlr_log(WLR_ERROR, - "not enough space for workspace name in osd"); - goto error; - } - - struct scaled_font_buffer *font_buffer = - scaled_font_buffer_create(output->osd_scene.tree); - wlr_scene_node_set_position(&font_buffer->scene_buffer->node, - x, y + (theme->osd_window_switcher_item_height - - font_height(&font)) / 2); - scaled_font_buffer_update(font_buffer, workspace_name, 0, - &font, text_color, bg_color); - y += theme->osd_window_switcher_item_height; - } - - struct buf buf = BUF_INIT; - int nr_fields = wl_list_length(&rc.window_switcher.fields); - - /* This is the width of the area available for text fields */ - int field_widths_sum = w - 2 * theme->osd_border_width - - 2 * theme->osd_window_switcher_padding - - 2 * theme->osd_window_switcher_item_active_border_width - - (nr_fields + 1) * theme->osd_window_switcher_item_padding_x; - if (field_widths_sum <= 0) { - wlr_log(WLR_ERROR, "Not enough spaces for osd contents"); - goto error; - } - - /* Draw text for each node */ - struct view **view; - wl_array_for_each(view, views) { - struct osd_scene_item *item = - wl_array_add(&output->osd_scene.items, sizeof(*item)); - item->view = *view; - /* - * OSD border - * +---------------------------------+ - * | | - * | item border | - * |+-------------------------------+| - * || || - * ||padding between each field || - * ||| field-1 | field-2 | field-n ||| - * || || - * || || - * |+-------------------------------+| - * | | - * | | - * +---------------------------------+ - */ - int x = theme->osd_border_width - + theme->osd_window_switcher_padding - + theme->osd_window_switcher_item_active_border_width - + theme->osd_window_switcher_item_padding_x; - struct wlr_scene_tree *item_root = - wlr_scene_tree_create(output->osd_scene.tree); - - struct window_switcher_field *field; - wl_list_for_each(field, &rc.window_switcher.fields, link) { - int field_width = field_widths_sum * field->width / 100.0; - struct wlr_scene_node *node = NULL; - int height = -1; - - if (field->content == LAB_FIELD_ICON) { - int icon_size = MIN(field_width, - theme->osd_window_switcher_item_icon_size); - struct scaled_icon_buffer *icon_buffer = - scaled_icon_buffer_create(item_root, - server, icon_size, icon_size); - scaled_icon_buffer_set_view(icon_buffer, *view); - node = &icon_buffer->scene_buffer->node; - height = icon_size; - } else { - buf_clear(&buf); - osd_field_get_content(field, &buf, *view); - - if (!string_null_or_empty(buf.data)) { - struct scaled_font_buffer *font_buffer = - scaled_font_buffer_create(item_root); - scaled_font_buffer_update(font_buffer, - buf.data, field_width, - &rc.font_osd, text_color, bg_color); - node = &font_buffer->scene_buffer->node; - height = font_height(&rc.font_osd); - } - } - - if (node) { - int item_height = - theme->osd_window_switcher_item_height; - wlr_scene_node_set_position(node, - x, y + (item_height - height) / 2); - } - x += field_width + theme->osd_window_switcher_item_padding_x; - } - - /* Highlight around selected window's item */ - int highlight_x = theme->osd_border_width - + theme->osd_window_switcher_padding; - struct lab_scene_rect_options highlight_opts = { - .border_colors = (float *[1]) {text_color}, - .nr_borders = 1, - .border_width = - theme->osd_window_switcher_item_active_border_width, - .width = w - 2 * theme->osd_border_width - - 2 * theme->osd_window_switcher_padding, - .height = theme->osd_window_switcher_item_height, - }; - - struct lab_scene_rect *highlight_rect = lab_scene_rect_create( - output->osd_scene.tree, &highlight_opts); - item->highlight_outline = &highlight_rect->tree->node; - wlr_scene_node_set_position(item->highlight_outline, highlight_x, y); - wlr_scene_node_set_enabled(item->highlight_outline, false); - - y += theme->osd_window_switcher_item_height; - } - buf_reset(&buf); - -error:; - /* Center OSD */ - struct wlr_box usable = output_usable_area_in_layout_coords(output); - wlr_scene_node_set_position(&output->osd_scene.tree->node, - usable.x + usable.width / 2 - w / 2, - usable.y + usable.height / 2 - h / 2); -} - -static void -update_item_highlight(struct output *output) -{ - struct osd_scene_item *item; - wl_array_for_each(item, &output->osd_scene.items) { - wlr_scene_node_set_enabled(item->highlight_outline, - item->view == output->server->osd_state.cycle_view); - } -} - static void update_osd(struct server *server) { struct wl_array views; wl_array_init(&views); view_array_append(server, &views, rc.window_switcher.criteria); + struct osd_impl *osd_impl = &osd_classic_impl; if (!wl_array_len(&views) || !server->osd_state.cycle_view) { osd_finish(server); @@ -471,10 +280,10 @@ update_osd(struct server *server) continue; } if (!output->osd_scene.tree) { - create_osd_scene(output, &views); + osd_impl->create(output, &views); assert(output->osd_scene.tree); } - update_item_highlight(output); + osd_impl->update(output); } } From 6e2805f6921cf8435f3f1e67b76ececb8318f494 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Fri, 8 Aug 2025 12:42:41 +0900 Subject: [PATCH 121/248] theme: move osd.window-switcher.* to osd.window-switcher.style-classic.* Backward compatibility is preserved. --- docs/labwc-theme.5.scd | 17 ++++--- docs/themerc | 12 ++--- include/theme.h | 29 ++++++------ src/osd/osd-classic.c | 53 ++++++++++------------ src/osd/osd.c | 2 +- src/ssd/resize-indicator.c | 10 ++-- src/theme.c | 93 ++++++++++++++++++++++---------------- 7 files changed, 115 insertions(+), 101 deletions(-) diff --git a/docs/labwc-theme.5.scd b/docs/labwc-theme.5.scd index fd21edd8..dc249611 100644 --- a/docs/labwc-theme.5.scd +++ b/docs/labwc-theme.5.scd @@ -303,28 +303,27 @@ all are supported. Text color of on-screen-display. Inherits *window.active.label.text.color* if not set. -*osd.window-switcher.width* - Width of window switcher in pixels. Default is 600. - Width can also be percent of the width of the monitor. - % is mandatory as last character in this case, max 100% +*osd.window-switcher.style-classic.width* + Width of window switcher in pixels. Width can also be a percentage of the + monitor width by adding '%' as suffix (e.g. 70%). Default is 600. -*osd.window-switcher.padding* +*osd.window-switcher.style-classic.padding* Padding of window switcher in pixels. This is the space between the window-switcher border and its items. Default is 4. -*osd.window-switcher.item.padding.x* +*osd.window-switcher.style-classic.item.padding.x* Horizontal padding of window switcher entries in pixels. Default is 10. -*osd.window-switcher.item.padding.y* +*osd.window-switcher.style-classic.item.padding.y* Vertical padding of window switcher entries in pixels. Default is 1. -*osd.window-switcher.item.active.border.width* +*osd.window-switcher.style-classic.item.active.border.width* Border width of the selection box in the window switcher in pixels. Default is 2. -*osd.window-switcher.item.icon.size* +*osd.window-switcher.style-classic.item.icon.size* Size of the icon in window switcher, in pixels. If not set, the font size derived from is used. diff --git a/docs/themerc b/docs/themerc index 045c1021..a4a70799 100644 --- a/docs/themerc +++ b/docs/themerc @@ -91,14 +91,14 @@ osd.label.text.color: #000000 # width can be set as percent (of screen width) # example 50% or 75% instead of 600, max 100% -osd.window-switcher.width: 600 +osd.window-switcher.style-classic.width: 600 -osd.window-switcher.padding: 4 -osd.window-switcher.item.padding.x: 10 -osd.window-switcher.item.padding.y: 1 -osd.window-switcher.item.active.border.width: 2 +osd.window-switcher.style-classic.padding: 4 +osd.window-switcher.style-classic.item.padding.x: 10 +osd.window-switcher.style-classic.item.padding.y: 1 +osd.window-switcher.style-classic.item.active.border.width: 2 # The icon size the same as the font size by default -# osd.window-switcher.item.icon.size: 50 +# osd.window-switcher.style-classic.item.icon.size: 50 osd.window-switcher.preview.border.width: 1 osd.window-switcher.preview.border.color: #dddda6,#000000,#dddda6 diff --git a/include/theme.h b/include/theme.h index 68a43f2f..6f4a81c6 100644 --- a/include/theme.h +++ b/include/theme.h @@ -164,13 +164,22 @@ struct theme { float osd_border_color[4]; float osd_label_text_color[4]; - int osd_window_switcher_width; - int osd_window_switcher_padding; - int osd_window_switcher_item_padding_x; - int osd_window_switcher_item_padding_y; - int osd_window_switcher_item_active_border_width; - int osd_window_switcher_item_icon_size; - bool osd_window_switcher_width_is_percent; + struct window_switcher_classic_theme { + int width; + int padding; + int item_padding_x; + int item_padding_y; + int item_active_border_width; + int item_icon_size; + bool width_is_percent; + + /* + * Not set in rc.xml/themerc, but derived from the tallest + * titlebar object plus 2 * window_titlebar_padding_height + */ + int item_height; + } osd_window_switcher_classic; + int osd_window_switcher_preview_border_width; float osd_window_switcher_preview_border_color[3][4]; @@ -181,12 +190,6 @@ struct theme { struct theme_snapping_overlay snapping_overlay_region, snapping_overlay_edge; - /* - * Not set in rc.xml/themerc, but derived from the tallest titlebar - * object plus 2 * window_titlebar_padding_height - */ - int osd_window_switcher_item_height; - /* magnifier */ float mag_border_color[4]; int mag_border_width; diff --git a/src/osd/osd-classic.c b/src/osd/osd-classic.c index 24679378..6ec165c5 100644 --- a/src/osd/osd-classic.c +++ b/src/osd/osd-classic.c @@ -33,20 +33,20 @@ osd_classic_create(struct output *output, struct wl_array *views) struct server *server = output->server; struct theme *theme = server->theme; + struct window_switcher_classic_theme *switcher_theme = + &theme->osd_window_switcher_classic; bool show_workspace = wl_list_length(&rc.workspace_config.workspaces) > 1; const char *workspace_name = server->workspaces.current->name; - int w = theme->osd_window_switcher_width; - if (theme->osd_window_switcher_width_is_percent) { - w = output->wlr_output->width - * theme->osd_window_switcher_width / 100; + int w = switcher_theme->width; + if (switcher_theme->width_is_percent) { + w = output->wlr_output->width * switcher_theme->width / 100; } - int h = wl_array_len(views) * rc.theme->osd_window_switcher_item_height - + 2 * rc.theme->osd_border_width - + 2 * rc.theme->osd_window_switcher_padding; + int h = wl_array_len(views) * switcher_theme->item_height + + 2 * rc.theme->osd_border_width + 2 * switcher_theme->padding; if (show_workspace) { /* workspace indicator */ - h += theme->osd_window_switcher_item_height; + h += switcher_theme->item_height; } output->osd_scene.tree = wlr_scene_tree_create(output->osd_tree); @@ -65,7 +65,7 @@ osd_classic_create(struct output *output, struct wl_array *views) }; lab_scene_rect_create(output->osd_scene.tree, &bg_opts); - int y = theme->osd_border_width + theme->osd_window_switcher_padding; + int y = theme->osd_border_width + switcher_theme->padding; /* Draw workspace indicator */ if (show_workspace) { @@ -83,11 +83,10 @@ osd_classic_create(struct output *output, struct wl_array *views) struct scaled_font_buffer *font_buffer = scaled_font_buffer_create(output->osd_scene.tree); wlr_scene_node_set_position(&font_buffer->scene_buffer->node, - x, y + (theme->osd_window_switcher_item_height - - font_height(&font)) / 2); + x, y + (switcher_theme->item_height - font_height(&font)) / 2); scaled_font_buffer_update(font_buffer, workspace_name, 0, &font, text_color, bg_color); - y += theme->osd_window_switcher_item_height; + y += switcher_theme->item_height; } struct buf buf = BUF_INIT; @@ -95,9 +94,9 @@ osd_classic_create(struct output *output, struct wl_array *views) /* This is the width of the area available for text fields */ int field_widths_sum = w - 2 * theme->osd_border_width - - 2 * theme->osd_window_switcher_padding - - 2 * theme->osd_window_switcher_item_active_border_width - - (nr_fields + 1) * theme->osd_window_switcher_item_padding_x; + - 2 * switcher_theme->padding + - 2 * switcher_theme->item_active_border_width + - (nr_fields + 1) * switcher_theme->item_padding_x; if (field_widths_sum <= 0) { wlr_log(WLR_ERROR, "Not enough spaces for osd contents"); goto error; @@ -126,9 +125,9 @@ osd_classic_create(struct output *output, struct wl_array *views) * +---------------------------------+ */ int x = theme->osd_border_width - + theme->osd_window_switcher_padding - + theme->osd_window_switcher_item_active_border_width - + theme->osd_window_switcher_item_padding_x; + + switcher_theme->padding + + switcher_theme->item_active_border_width + + switcher_theme->item_padding_x; struct wlr_scene_tree *item_root = wlr_scene_tree_create(output->osd_scene.tree); @@ -140,7 +139,7 @@ osd_classic_create(struct output *output, struct wl_array *views) if (field->content == LAB_FIELD_ICON) { int icon_size = MIN(field_width, - theme->osd_window_switcher_item_icon_size); + switcher_theme->item_icon_size); struct scaled_icon_buffer *icon_buffer = scaled_icon_buffer_create(item_root, server, icon_size, icon_size); @@ -163,25 +162,23 @@ osd_classic_create(struct output *output, struct wl_array *views) } if (node) { - int item_height = - theme->osd_window_switcher_item_height; + int item_height = switcher_theme->item_height; wlr_scene_node_set_position(node, x, y + (item_height - height) / 2); } - x += field_width + theme->osd_window_switcher_item_padding_x; + x += field_width + switcher_theme->item_padding_x; } /* Highlight around selected window's item */ int highlight_x = theme->osd_border_width - + theme->osd_window_switcher_padding; + + switcher_theme->padding; struct lab_scene_rect_options highlight_opts = { .border_colors = (float *[1]) {text_color}, .nr_borders = 1, - .border_width = - theme->osd_window_switcher_item_active_border_width, + .border_width = switcher_theme->item_active_border_width, .width = w - 2 * theme->osd_border_width - - 2 * theme->osd_window_switcher_padding, - .height = theme->osd_window_switcher_item_height, + - 2 * switcher_theme->padding, + .height = switcher_theme->item_height, }; struct lab_scene_rect *highlight_rect = lab_scene_rect_create( @@ -190,7 +187,7 @@ osd_classic_create(struct output *output, struct wl_array *views) wlr_scene_node_set_position(item->highlight_outline, highlight_x, y); wlr_scene_node_set_enabled(item->highlight_outline, false); - y += theme->osd_window_switcher_item_height; + y += switcher_theme->item_height; } buf_reset(&buf); diff --git a/src/osd/osd.c b/src/osd/osd.c index 51a0721c..9bef5ce3 100644 --- a/src/osd/osd.c +++ b/src/osd/osd.c @@ -272,7 +272,7 @@ update_osd(struct server *server) goto out; } - if (rc.window_switcher.show && rc.theme->osd_window_switcher_width > 0) { + if (rc.window_switcher.show) { /* Display the actual OSD */ struct output *output; wl_list_for_each(output, &server->outputs, link) { diff --git a/src/ssd/resize-indicator.c b/src/ssd/resize-indicator.c index 10fe3690..cc5731ae 100644 --- a/src/ssd/resize-indicator.c +++ b/src/ssd/resize-indicator.c @@ -12,6 +12,8 @@ #include "theme.h" #include "view.h" +#define PADDING rc.theme->osd_window_switcher_classic.padding + static void resize_indicator_reconfigure_view(struct resize_indicator *indicator) { @@ -19,7 +21,7 @@ resize_indicator_reconfigure_view(struct resize_indicator *indicator) struct theme *theme = rc.theme; indicator->height = font_height(&rc.font_osd) - + 2 * theme->osd_window_switcher_padding + + 2 * PADDING + 2 * theme->osd_border_width; /* Static positions */ @@ -27,8 +29,8 @@ resize_indicator_reconfigure_view(struct resize_indicator *indicator) theme->osd_border_width, theme->osd_border_width); wlr_scene_node_set_position(&indicator->text->scene_buffer->node, - theme->osd_border_width + theme->osd_window_switcher_padding, - theme->osd_border_width + theme->osd_window_switcher_padding); + theme->osd_border_width + PADDING, + theme->osd_border_width + PADDING); /* Colors */ wlr_scene_rect_set_color(indicator->border, theme->osd_border_color); @@ -107,7 +109,7 @@ resize_indicator_set_size(struct resize_indicator *indicator, int width) /* We are not using a width-cache-early-out here to allow for theme changes */ indicator->width = width - + 2 * rc.theme->osd_window_switcher_padding + + 2 * PADDING + 2 * rc.theme->osd_border_width; wlr_scene_rect_set_size(indicator->border, indicator->width, indicator->height); diff --git a/src/theme.c b/src/theme.c index e16325e8..f874e585 100644 --- a/src/theme.c +++ b/src/theme.c @@ -608,13 +608,13 @@ theme_builtin(struct theme *theme, struct server *server) theme->menu_title_text_justify = parse_justification("Center"); parse_hexstr("#ffffff", theme->menu_title_text_color); - theme->osd_window_switcher_width = 600; - theme->osd_window_switcher_width_is_percent = false; - theme->osd_window_switcher_padding = 4; - theme->osd_window_switcher_item_padding_x = 10; - theme->osd_window_switcher_item_padding_y = 1; - theme->osd_window_switcher_item_active_border_width = 2; - theme->osd_window_switcher_item_icon_size = -1; + theme->osd_window_switcher_classic.width = 600; + theme->osd_window_switcher_classic.width_is_percent = false; + theme->osd_window_switcher_classic.padding = 4; + theme->osd_window_switcher_classic.item_padding_x = 10; + theme->osd_window_switcher_classic.item_padding_y = 1; + theme->osd_window_switcher_classic.item_active_border_width = 2; + theme->osd_window_switcher_classic.item_icon_size = -1; /* inherit settings in post_processing() if not set elsewhere */ theme->osd_window_switcher_preview_border_width = INT_MIN; @@ -679,6 +679,9 @@ entry(struct theme *theme, const char *key, const char *value) return; } + struct window_switcher_classic_theme *switcher_classic_theme = + &theme->osd_window_switcher_classic; + /* * Note that in order for the pattern match to apply to more than just * the first instance, "else if" cannot be used throughout this function @@ -949,38 +952,45 @@ entry(struct theme *theme, const char *key, const char *value) if (match_glob(key, "osd.border.color")) { parse_color(value, theme->osd_border_color); } - if (match_glob(key, "osd.window-switcher.width")) { + /* classic window switcher */ + if (match_glob(key, "osd.window-switcher.style-classic.width") + || match_glob(key, "osd.window-switcher.width")) { if (strrchr(value, '%')) { - theme->osd_window_switcher_width_is_percent = true; + switcher_classic_theme->width_is_percent = true; } else { - theme->osd_window_switcher_width_is_percent = false; + switcher_classic_theme->width_is_percent = false; } - theme->osd_window_switcher_width = get_int_if_positive( - value, "osd.window-switcher.width"); + switcher_classic_theme->width = get_int_if_positive(value, + "osd.window-switcher.style-classic.width"); } - if (match_glob(key, "osd.window-switcher.padding")) { - theme->osd_window_switcher_padding = get_int_if_positive( - value, "osd.window-switcher.padding"); + if (match_glob(key, "osd.window-switcher.style-classic.padding") + || match_glob(key, "osd.window-switcher.padding")) { + switcher_classic_theme->padding = get_int_if_positive(value, + "osd.window-switcher.style-classic.padding"); } - if (match_glob(key, "osd.window-switcher.item.padding.x")) { - theme->osd_window_switcher_item_padding_x = - get_int_if_positive( - value, "osd.window-switcher.item.padding.x"); + if (match_glob(key, "osd.window-switcher.style-classic.item.padding.x") + || match_glob(key, "osd.window-switcher.item.padding.x")) { + switcher_classic_theme->item_padding_x = + get_int_if_positive(value, + "osd.window-switcher.style-classic.item.padding.x"); } - if (match_glob(key, "osd.window-switcher.item.padding.y")) { - theme->osd_window_switcher_item_padding_y = - get_int_if_positive( - value, "osd.window-switcher.item.padding.y"); + if (match_glob(key, "osd.window-switcher.style-classic.item.padding.y") + || match_glob(key, "osd.window-switcher.item.padding.y")) { + switcher_classic_theme->item_padding_y = + get_int_if_positive(value, + "osd.window-switcher.style-classic.item.padding.y"); } - if (match_glob(key, "osd.window-switcher.item.active.border.width")) { - theme->osd_window_switcher_item_active_border_width = - get_int_if_positive( - value, "osd.window-switcher.item.active.border.width"); + if (match_glob(key, "osd.window-switcher.style-classic.item.active.border.width") + || match_glob(key, "osd.window-switcher.item.active.border.width")) { + switcher_classic_theme->item_active_border_width = + get_int_if_positive(value, + "osd.window-switcher.style-classic.item.active.border.width"); } - if (match_glob(key, "osd.window-switcher.item.icon.size")) { - theme->osd_window_switcher_item_icon_size = - get_int_if_positive( - value, "osd.window-switcher.item.icon.size"); + if (match_glob(key, "osd.window-switcher.style-classic.item.icon.size") + || match_glob(key, "osd.window-switcher.item.icon.size")) { + switcher_classic_theme->item_icon_size = + get_int_if_positive(value, + "osd.window-switcher.style-classic.item.icon.size"); } if (match_glob(key, "osd.window-switcher.preview.border.width")) { theme->osd_window_switcher_preview_border_width = @@ -1582,6 +1592,9 @@ get_titlebar_height(struct theme *theme) static void post_processing(struct theme *theme) { + struct window_switcher_classic_theme *switcher_classic_theme = + &theme->osd_window_switcher_classic; + theme->titlebar_height = get_titlebar_height(theme); fill_background_colors(&theme->window[THEME_INACTIVE].title_bg); @@ -1594,14 +1607,14 @@ post_processing(struct theme *theme) + 2 * theme->menu_items_padding_y; int osd_font_height = font_height(&rc.font_osd); - if (theme->osd_window_switcher_item_icon_size <= 0) { - theme->osd_window_switcher_item_icon_size = osd_font_height; + if (switcher_classic_theme->item_icon_size <= 0) { + switcher_classic_theme->item_icon_size = osd_font_height; } int osd_field_height = - MAX(osd_font_height, theme->osd_window_switcher_item_icon_size); - theme->osd_window_switcher_item_height = osd_field_height - + 2 * theme->osd_window_switcher_item_padding_y - + 2 * theme->osd_window_switcher_item_active_border_width; + MAX(osd_font_height, switcher_classic_theme->item_icon_size); + switcher_classic_theme->item_height = osd_field_height + + 2 * switcher_classic_theme->item_padding_y + + 2 * switcher_classic_theme->item_active_border_width; if (rc.corner_radius >= theme->titlebar_height) { rc.corner_radius = theme->titlebar_height - 1; @@ -1667,9 +1680,9 @@ post_processing(struct theme *theme) if (theme->osd_workspace_switcher_boxes_height == 0) { theme->osd_workspace_switcher_boxes_width = 0; } - if (theme->osd_window_switcher_width_is_percent) { - theme->osd_window_switcher_width = - MIN(theme->osd_window_switcher_width, 100); + if (switcher_classic_theme->width_is_percent) { + switcher_classic_theme->width = + MIN(switcher_classic_theme->width, 100); } if (theme->osd_window_switcher_preview_border_width == INT_MIN) { theme->osd_window_switcher_preview_border_width = From 2e9292b7a3f55463030bc2a9640498525ffa3cd6 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Fri, 8 Aug 2025 14:53:34 +0900 Subject: [PATCH 122/248] osd: support window switcher with thumbnails The new-style window switcher can be enabled with . New theme entries: osd.window-switcher.style-thumbnail.width.max: 80% osd.window-switcher.style-thumbnail.padding: 4 osd.window-switcher.style-thumbnail.item.width: 300 osd.window-switcher.style-thumbnail.item.height: 250 osd.window-switcher.style-thumbnail.item.padding: 10 osd.window-switcher.style-thumbnail.item.active.border.width: 2 osd.window-switcher.style-thumbnail.item.active.border.color: #589bda osd.window-switcher.style-thumbnail.item.active.bg.color: #c7e2fc osd.window-switcher.style-thumbnail.item.icon.size: 60 --- docs/labwc-config.5.scd | 10 +- docs/labwc-theme.5.scd | 38 ++++++ docs/rc.xml.all | 2 +- docs/themerc | 11 ++ include/config/rcxml.h | 1 + include/config/types.h | 5 + include/osd.h | 1 + include/theme.h | 15 +++ src/config/rcxml.c | 7 + src/osd/meson.build | 1 + src/osd/osd-thumbnail.c | 280 ++++++++++++++++++++++++++++++++++++++++ src/osd/osd.c | 11 +- src/theme.c | 57 ++++++++ 13 files changed, 434 insertions(+), 5 deletions(-) create mode 100644 src/osd/osd-thumbnail.c diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index e86fb75f..1dc59a8c 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -278,7 +278,7 @@ this is for compatibility with Openbox. ## WINDOW SWITCHER ``` - + @@ -287,10 +287,14 @@ this is for compatibility with Openbox. ``` -** +** *show* [yes|no] Draw the OnScreenDisplay when switching between windows. Default is yes. + *style* [classic|thumbnail] Configures the style of the OnScreenDisplay. + "classic" displays window information like icons and titles in a vertical list. + "thumbnail" shows window thumbnail, icon and title in grids. + *preview* [yes|no] Preview the contents of the selected window when switching between windows. Default is yes. @@ -302,7 +306,7 @@ this is for compatibility with Openbox. are shown). ** - Define window switcher fields. + Define window switcher fields when using **. *content* defines what the field shows and can be any of: diff --git a/docs/labwc-theme.5.scd b/docs/labwc-theme.5.scd index dc249611..efc3a1a5 100644 --- a/docs/labwc-theme.5.scd +++ b/docs/labwc-theme.5.scd @@ -303,6 +303,10 @@ all are supported. Text color of on-screen-display. Inherits *window.active.label.text.color* if not set. +*osd.window-switcher.style-classic* + Theme for window switcher when using . + See below for details. + *osd.window-switcher.style-classic.width* Width of window switcher in pixels. Width can also be a percentage of the monitor width by adding '%' as suffix (e.g. 70%). Default is 600. @@ -328,6 +332,40 @@ all are supported. If not set, the font size derived from is used. +*osd.window-switcher.style-thumbnail* + Theme for window switcher when using . + See below for details. + +*osd.window-switcher.style-thumbnail.width.max* + Maximum width of window switcher in pixels. Width can also be a percentage of + the monitor width by adding '%' as suffix (e.g. 70%). Default is 80%. + +*osd.window-switcher.style-thumbnail.padding* + Padding of window switcher in pixels. This is the space between the + window-switcher border and its items. Default is 4. + +*osd.window-switcher.style-thumbnail.item.width* + Width of window switcher items in pixels. Default is 300. + +*osd.window-switcher.style-thumbnail.item.height* + Height of window switcher items in pixels. Default is 250. + +*osd.window-switcher.style-thumbnail.item.padding* + Padding of window switcher items in pixels. This is the space between the + border around selected items and window thumbnail. Default is 2. + +*osd.window-switcher.style-thumbnail.item.active.border.width* + Border width of selected window switcher items in pixels. Default is 2. + +*osd.window-switcher.style-thumbnail.item.active.border.color* + Color of border around selected window switcher items. Default is #589bda. + +*osd.window-switcher.style-thumbnail.item.active.bg.color* + Color of selected window switcher items. Default is #c7e2fc. + +*osd.window-switcher.style-thumbnail.item.icon.size* + Size of window icons in window switcher items in pixels. Default is 60. + *osd.window-switcher.preview.border.width* Border width of the outlines shown as the preview of the window selected by window switcher. Inherits *osd.border.width* if not set. diff --git a/docs/rc.xml.all b/docs/rc.xml.all index 91dc7791..940b2f96 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -75,7 +75,7 @@ - + diff --git a/docs/themerc b/docs/themerc index a4a70799..7b251a3c 100644 --- a/docs/themerc +++ b/docs/themerc @@ -99,6 +99,17 @@ osd.window-switcher.style-classic.item.padding.y: 1 osd.window-switcher.style-classic.item.active.border.width: 2 # The icon size the same as the font size by default # osd.window-switcher.style-classic.item.icon.size: 50 + +osd.window-switcher.style-thumbnail.width.max: 80% +osd.window-switcher.style-thumbnail.padding: 4 +osd.window-switcher.style-thumbnail.item.width: 300 +osd.window-switcher.style-thumbnail.item.height: 250 +osd.window-switcher.style-thumbnail.item.padding: 10 +osd.window-switcher.style-thumbnail.item.active.border.width: 2 +osd.window-switcher.style-thumbnail.item.active.border.color: #589bda +osd.window-switcher.style-thumbnail.item.active.bg.color: #c7e2fc +osd.window-switcher.style-thumbnail.item.icon.size: 60 + osd.window-switcher.preview.border.width: 1 osd.window-switcher.preview.border.color: #dddda6,#000000,#dddda6 diff --git a/include/config/rcxml.h b/include/config/rcxml.h index 86daf4e4..0da38114 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -175,6 +175,7 @@ struct rcxml { bool outlines; enum lab_view_criteria criteria; struct wl_list fields; /* struct window_switcher_field.link */ + enum window_switcher_style style; } window_switcher; struct wl_list window_rules; /* struct window_rule.link */ diff --git a/include/config/types.h b/include/config/types.h index 75f207ac..e832a658 100644 --- a/include/config/types.h +++ b/include/config/types.h @@ -107,4 +107,9 @@ enum lab_window_type { LAB_WINDOW_TYPE_LEN }; +enum window_switcher_style { + WINDOW_SWITCHER_CLASSIC, + WINDOW_SWITCHER_THUMBNAIL, +}; + #endif /* LABWC_CONFIG_TYPES_H */ diff --git a/include/osd.h b/include/osd.h index ad0b3bce..a5c2a4cc 100644 --- a/include/osd.h +++ b/include/osd.h @@ -83,5 +83,6 @@ struct osd_impl { }; extern struct osd_impl osd_classic_impl; +extern struct osd_impl osd_thumbnail_impl; #endif // LABWC_OSD_H diff --git a/include/theme.h b/include/theme.h index 6f4a81c6..75f3c8c0 100644 --- a/include/theme.h +++ b/include/theme.h @@ -180,6 +180,21 @@ struct theme { int item_height; } osd_window_switcher_classic; + struct window_switcher_thumbnail_theme { + int max_width; + int padding; + int item_width; + int item_height; + int item_padding; + int item_active_border_width; + float item_active_border_color[4]; + float item_active_bg_color[4]; + int item_icon_size; + bool max_width_is_percent; + + int title_height; + } osd_window_switcher_thumbnail; + int osd_window_switcher_preview_border_width; float osd_window_switcher_preview_border_color[3][4]; diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 280c0256..26b56ec6 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -1210,6 +1210,12 @@ entry(xmlNode *node, char *nodename, char *content) /* */ } else if (!strcasecmp(nodename, "show.windowSwitcher")) { set_bool(content, &rc.window_switcher.show); + } else if (!strcasecmp(nodename, "style.windowSwitcher")) { + if (!strcasecmp(content, "classic")) { + rc.window_switcher.style = WINDOW_SWITCHER_CLASSIC; + } else if (!strcasecmp(content, "thumbnail")) { + rc.window_switcher.style = WINDOW_SWITCHER_THUMBNAIL; + } } else if (!strcasecmp(nodename, "preview.windowSwitcher")) { set_bool(content, &rc.window_switcher.preview); } else if (!strcasecmp(nodename, "outlines.windowSwitcher")) { @@ -1431,6 +1437,7 @@ rcxml_init(void) rc.snap_tiling_events_mode = LAB_TILING_EVENTS_ALWAYS; rc.window_switcher.show = true; + rc.window_switcher.style = WINDOW_SWITCHER_CLASSIC; rc.window_switcher.preview = true; rc.window_switcher.outlines = true; rc.window_switcher.criteria = LAB_VIEW_CRITERIA_CURRENT_WORKSPACE diff --git a/src/osd/meson.build b/src/osd/meson.build index 4f4da2cb..17b4bf51 100644 --- a/src/osd/meson.build +++ b/src/osd/meson.build @@ -2,4 +2,5 @@ labwc_sources += files( 'osd.c', 'osd-classic.c', 'osd-field.c', + 'osd-thumbnail.c', ) diff --git a/src/osd/osd-thumbnail.c b/src/osd/osd-thumbnail.c new file mode 100644 index 00000000..2f37d50d --- /dev/null +++ b/src/osd/osd-thumbnail.c @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include "config/rcxml.h" +#include "config/types.h" +#include "common/array.h" +#include "common/box.h" +#include "common/lab-scene-rect.h" +#include "common/scaled-font-buffer.h" +#include "common/scaled-icon-buffer.h" +#include "labwc.h" +#include "osd.h" +#include "output.h" +#include "theme.h" + +struct osd_thumbnail_scene_item { + struct view *view; + struct wlr_scene_tree *tree; + struct scaled_font_buffer *normal_title; + struct scaled_font_buffer *active_title; + struct lab_scene_rect *active_bg; +}; + +static void +render_node(struct server *server, struct wlr_render_pass *pass, + struct wlr_scene_node *node, int x, int y) +{ + switch (node->type) { + case WLR_SCENE_NODE_TREE: { + struct wlr_scene_tree *tree = wlr_scene_tree_from_node(node); + struct wlr_scene_node *child; + wl_list_for_each(child, &tree->children, link) { + render_node(server, pass, child, x + node->x, y + node->y); + } + break; + } + case WLR_SCENE_NODE_BUFFER: { + struct wlr_scene_buffer *scene_buffer = + wlr_scene_buffer_from_node(node); + if (!scene_buffer->buffer) { + break; + } + struct wlr_texture *texture = wlr_texture_from_buffer( + server->renderer, scene_buffer->buffer); + if (!texture) { + break; + } + wlr_render_pass_add_texture(pass, &(struct wlr_render_texture_options){ + .texture = texture, + .src_box = scene_buffer->src_box, + .dst_box = { + .x = x, + .y = y, + .width = scene_buffer->dst_width, + .height = scene_buffer->dst_height, + }, + .transform = scene_buffer->transform, + }); + wlr_texture_destroy(texture); + break; + } + case WLR_SCENE_NODE_RECT: + /* should be unreached */ + wlr_log(WLR_ERROR, "ignoring rect"); + break; + } +} + +static struct wlr_buffer * +render_thumb(struct output *output, struct view *view) +{ + struct server *server = output->server; + struct wlr_buffer *buffer = wlr_allocator_create_buffer(server->allocator, + view->current.width, view->current.height, + &output->wlr_output->swapchain->format); + struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass( + server->renderer, buffer, NULL); + render_node(server, pass, &view->content_tree->node, 0, 0); + if (!wlr_render_pass_submit(pass)) { + wlr_log(WLR_ERROR, "failed to submit render pass"); + wlr_buffer_drop(buffer); + return NULL; + } + return buffer; +} + +static struct scaled_font_buffer * +create_title(struct wlr_scene_tree *parent, + struct window_switcher_thumbnail_theme *switcher_theme, + const char *title, const float *title_color, + const float *bg_color, int y) +{ + struct scaled_font_buffer *buffer = + scaled_font_buffer_create(parent); + scaled_font_buffer_update(buffer, title, + switcher_theme->item_width - 2 * switcher_theme->item_padding, + &rc.font_osd, title_color, bg_color); + wlr_scene_node_set_position(&buffer->scene_buffer->node, + (switcher_theme->item_width - buffer->width) / 2, y); + return buffer; +} + +static struct osd_thumbnail_scene_item * +create_item_scene(struct wlr_scene_tree *parent, struct view *view, + struct output *output) +{ + struct server *server = output->server; + struct theme *theme = server->theme; + struct window_switcher_thumbnail_theme *switcher_theme = + &theme->osd_window_switcher_thumbnail; + int padding = theme->border_width + switcher_theme->item_padding; + int title_y = switcher_theme->item_height - padding - switcher_theme->title_height; + struct wlr_box thumb_bounds = { + .x = padding, + .y = padding, + .width = switcher_theme->item_width - 2 * padding, + .height = title_y - 2 * padding, + }; + if (thumb_bounds.width <= 0 || thumb_bounds.height <= 0) { + wlr_log(WLR_ERROR, "too small thumbnail area"); + return NULL; + } + + struct osd_thumbnail_scene_item *item = + wl_array_add(&output->osd_scene.items, sizeof(*item)); + item->tree = wlr_scene_tree_create(parent); + item->view = view; + + /* background for selected item */ + struct lab_scene_rect_options opts = { + .width = switcher_theme->item_width, + .height = switcher_theme->item_height, + .bg_color = switcher_theme->item_active_bg_color, + .nr_borders = 1, + .border_colors = (float *[1]) { switcher_theme->item_active_border_color }, + .border_width = switcher_theme->item_active_border_width, + }; + item->active_bg = lab_scene_rect_create(item->tree, &opts); + + /* thumbnail */ + struct wlr_buffer *thumb_buffer = render_thumb(output, view); + if (thumb_buffer) { + struct wlr_scene_buffer *thumb_scene_buffer = + wlr_scene_buffer_create(item->tree, thumb_buffer); + wlr_buffer_drop(thumb_buffer); + struct wlr_box thumb_box = box_fit_within( + thumb_buffer->width, thumb_buffer->height, + &thumb_bounds); + wlr_scene_buffer_set_dest_size(thumb_scene_buffer, + thumb_box.width, thumb_box.height); + wlr_scene_node_set_position(&thumb_scene_buffer->node, + thumb_box.x, thumb_box.y); + } + + /* title */ + const char *title = view_get_string_prop(view, "title"); + if (title) { + item->normal_title = create_title(item->tree, switcher_theme, + title, theme->osd_label_text_color, + theme->osd_bg_color, title_y); + item->active_title = create_title(item->tree, switcher_theme, + title, theme->osd_label_text_color, + switcher_theme->item_active_bg_color, title_y); + } + + /* icon */ + int icon_size = switcher_theme->item_icon_size; + struct scaled_icon_buffer *icon_buffer = scaled_icon_buffer_create( + item->tree, server, icon_size, icon_size); + scaled_icon_buffer_set_view(icon_buffer, view); + int x = (switcher_theme->item_width - icon_size) / 2; + int y = title_y - padding - icon_size + 10; /* slide by 10px */ + wlr_scene_node_set_position(&icon_buffer->scene_buffer->node, x, y); + + return item; +} + +static void +get_items_geometry(struct output *output, struct theme *theme, + int nr_thumbs, int *nr_rows, int *nr_cols) +{ + struct window_switcher_thumbnail_theme *thumb_theme = + &theme->osd_window_switcher_thumbnail; + int output_width = output->wlr_output->width / output->wlr_output->scale; + + int max_bg_width = thumb_theme->max_width; + if (thumb_theme->max_width_is_percent) { + max_bg_width = output_width * thumb_theme->max_width / 100; + } + + *nr_rows = 1; + *nr_cols = nr_thumbs; + while (1) { + assert(*nr_rows <= nr_thumbs); + int bg_width = *nr_cols * thumb_theme->item_width + + theme->osd_border_width + thumb_theme->padding; + if (bg_width < max_bg_width) { + break; + } + if (*nr_rows >= nr_thumbs) { + break; + } + (*nr_rows)++; + *nr_cols = ceilf((float)nr_thumbs / *nr_rows); + } +} + +static void +osd_thumbnail_create(struct output *output, struct wl_array *views) +{ + assert(!output->osd_scene.tree); + + struct theme *theme = output->server->theme; + struct window_switcher_thumbnail_theme *switcher_theme = + &theme->osd_window_switcher_thumbnail; + int padding = theme->osd_border_width + switcher_theme->padding; + + output->osd_scene.tree = wlr_scene_tree_create(output->osd_tree); + + int nr_views = wl_array_len(views); + assert(nr_views > 0); + int nr_rows, nr_cols; + get_items_geometry(output, theme, nr_views, &nr_rows, &nr_cols); + + /* items */ + struct view **view; + int index = 0; + wl_array_for_each(view, views) { + struct osd_thumbnail_scene_item *item = create_item_scene( + output->osd_scene.tree, *view, output); + if (!item) { + break; + } + int x = (index % nr_cols) * switcher_theme->item_width + padding; + int y = (index / nr_cols) * switcher_theme->item_height + padding; + wlr_scene_node_set_position(&item->tree->node, x, y); + index++; + } + + /* background */ + struct lab_scene_rect_options bg_opts = { + .width = nr_cols * switcher_theme->item_width + 2 * padding, + .height = nr_rows * switcher_theme->item_height + 2 * padding, + .bg_color = theme->osd_bg_color, + .nr_borders = 1, + .border_width = theme->osd_border_width, + .border_colors = (float *[1]) { theme->osd_border_color }, + }; + struct lab_scene_rect *bg = + lab_scene_rect_create(output->osd_scene.tree, &bg_opts); + wlr_scene_node_lower_to_bottom(&bg->tree->node); + + /* center */ + struct wlr_box usable = output_usable_area_in_layout_coords(output); + int lx = usable.x + (usable.width - bg_opts.width) / 2; + int ly = usable.y + (usable.height - bg_opts.height) / 2; + wlr_scene_node_set_position(&output->osd_scene.tree->node, lx, ly); +} + +static void +osd_thumbnail_update(struct output *output) +{ + struct osd_thumbnail_scene_item *item; + wl_array_for_each(item, &output->osd_scene.items) { + bool active = (item->view == output->server->osd_state.cycle_view); + wlr_scene_node_set_enabled(&item->active_bg->tree->node, active); + wlr_scene_node_set_enabled( + &item->active_title->scene_buffer->node, active); + wlr_scene_node_set_enabled( + &item->normal_title->scene_buffer->node, !active); + } +} + +struct osd_impl osd_thumbnail_impl = { + .create = osd_thumbnail_create, + .update = osd_thumbnail_update, +}; diff --git a/src/osd/osd.c b/src/osd/osd.c index 9bef5ce3..28714d72 100644 --- a/src/osd/osd.c +++ b/src/osd/osd.c @@ -265,7 +265,16 @@ update_osd(struct server *server) struct wl_array views; wl_array_init(&views); view_array_append(server, &views, rc.window_switcher.criteria); - struct osd_impl *osd_impl = &osd_classic_impl; + + struct osd_impl *osd_impl = NULL; + switch (rc.window_switcher.style) { + case WINDOW_SWITCHER_CLASSIC: + osd_impl = &osd_classic_impl; + break; + case WINDOW_SWITCHER_THUMBNAIL: + osd_impl = &osd_thumbnail_impl; + break; + } if (!wl_array_len(&views) || !server->osd_state.cycle_view) { osd_finish(server); diff --git a/src/theme.c b/src/theme.c index f874e585..45e42c15 100644 --- a/src/theme.c +++ b/src/theme.c @@ -616,6 +616,17 @@ theme_builtin(struct theme *theme, struct server *server) theme->osd_window_switcher_classic.item_active_border_width = 2; theme->osd_window_switcher_classic.item_icon_size = -1; + theme->osd_window_switcher_thumbnail.max_width = 80; + theme->osd_window_switcher_thumbnail.max_width_is_percent = true; + theme->osd_window_switcher_thumbnail.padding = 4; + theme->osd_window_switcher_thumbnail.item_width = 300; + theme->osd_window_switcher_thumbnail.item_height = 250; + theme->osd_window_switcher_thumbnail.item_padding = 10; + theme->osd_window_switcher_thumbnail.item_active_border_width = 2; + parse_color("#589bda", theme->osd_window_switcher_thumbnail.item_active_border_color); + parse_color("#c7e2fc", theme->osd_window_switcher_thumbnail.item_active_bg_color); + theme->osd_window_switcher_thumbnail.item_icon_size = 60; + /* inherit settings in post_processing() if not set elsewhere */ theme->osd_window_switcher_preview_border_width = INT_MIN; zero_array(theme->osd_window_switcher_preview_border_color); @@ -681,6 +692,8 @@ entry(struct theme *theme, const char *key, const char *value) struct window_switcher_classic_theme *switcher_classic_theme = &theme->osd_window_switcher_classic; + struct window_switcher_thumbnail_theme *switcher_thumb_theme = + &theme->osd_window_switcher_thumbnail; /* * Note that in order for the pattern match to apply to more than just @@ -992,6 +1005,47 @@ entry(struct theme *theme, const char *key, const char *value) get_int_if_positive(value, "osd.window-switcher.style-classic.item.icon.size"); } + /* thumbnail window switcher */ + if (match_glob(key, "osd.window-switcher.style-thumbnail.width.max")) { + if (strrchr(value, '%')) { + switcher_thumb_theme->max_width_is_percent = true; + } else { + switcher_thumb_theme->max_width_is_percent = false; + } + switcher_thumb_theme->max_width = get_int_if_positive( + value, "osd.window-switcher.style-thumbnail.width.max"); + } + if (match_glob(key, "osd.window-switcher.style-thumbnail.padding")) { + switcher_thumb_theme->padding = get_int_if_positive( + value, "osd.window-switcher.style-thumbnail.padding"); + } + if (match_glob(key, "osd.window-switcher.style-thumbnail.item.width")) { + switcher_thumb_theme->item_width = get_int_if_positive( + value, "osd.window-switcher.style-thumbnail.item.width"); + } + if (match_glob(key, "osd.window-switcher.style-thumbnail.item.height")) { + switcher_thumb_theme->item_height = get_int_if_positive( + value, "osd.window-switcher.style-thumbnail.item.height"); + } + if (match_glob(key, "osd.window-switcher.style-thumbnail.item.padding")) { + switcher_thumb_theme->item_padding = get_int_if_positive( + value, "osd.window-switcher.style-thumbnail.item.padding"); + } + if (match_glob(key, "osd.window-switcher.style-thumbnail.item.active.border.width")) { + switcher_thumb_theme->item_active_border_width = get_int_if_positive( + value, "osd.window-switcher.style-thumbnail.item.active.border.width"); + } + if (match_glob(key, "osd.window-switcher.style-thumbnail.item.active.border.color")) { + parse_color(value, switcher_thumb_theme->item_active_border_color); + } + if (match_glob(key, "osd.window-switcher.style-thumbnail.item.active.bg.color")) { + parse_color(value, switcher_thumb_theme->item_active_bg_color); + } + if (match_glob(key, "osd.window-switcher.style-thumbnail.item.icon.size")) { + switcher_thumb_theme->item_icon_size = get_int_if_positive( + value, "osd.window-switcher.style-thumbnail.item.icon.size"); + } + if (match_glob(key, "osd.window-switcher.preview.border.width")) { theme->osd_window_switcher_preview_border_width = get_int_if_positive( @@ -1594,6 +1648,8 @@ post_processing(struct theme *theme) { struct window_switcher_classic_theme *switcher_classic_theme = &theme->osd_window_switcher_classic; + struct window_switcher_thumbnail_theme *switcher_thumb_theme = + &theme->osd_window_switcher_thumbnail; theme->titlebar_height = get_titlebar_height(theme); @@ -1607,6 +1663,7 @@ post_processing(struct theme *theme) + 2 * theme->menu_items_padding_y; int osd_font_height = font_height(&rc.font_osd); + switcher_thumb_theme->title_height = osd_font_height; if (switcher_classic_theme->item_icon_size <= 0) { switcher_classic_theme->item_icon_size = osd_font_height; } From a382389e5ee0823531a7fd34534b6f364555cab8 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 30 Aug 2025 14:13:59 +0900 Subject: [PATCH 123/248] scaled-icon-buffer: fix large client-side icon not being loaded This commit fixes that client-side icons were not loaded when the rendered icon size is larger than icon sizes from the client. This bug has become more likely to happen due to the new thumnail-style window switcher. The cause was `abs(INT_MIN)` becomes `INT_MIN` due to integer overflow. --- src/common/scaled-icon-buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/scaled-icon-buffer.c b/src/common/scaled-icon-buffer.c index 49387bac..5dd619f1 100644 --- a/src/common/scaled-icon-buffer.c +++ b/src/common/scaled-icon-buffer.c @@ -22,7 +22,7 @@ static struct lab_data_buffer * choose_best_icon_buffer(struct scaled_icon_buffer *self, int icon_size, double scale) { - int best_dist = INT_MIN; + int best_dist = -INT_MAX; struct lab_data_buffer *best_buffer = NULL; struct lab_data_buffer **buffer; From 3499f40a6ba40ecebe5549d82acad9a0c105d379 Mon Sep 17 00:00:00 2001 From: 01micko <01micko@gmx.com> Date: Sat, 30 Aug 2025 20:09:31 +1000 Subject: [PATCH 124/248] meson.build: move lab-sensible-terminal install routine. Moved to clients/meson.build as per the comment in meson.build. --- clients/meson.build | 2 ++ meson.build | 4 ---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/clients/meson.build b/clients/meson.build index fefd5bd4..c131915d 100644 --- a/clients/meson.build +++ b/clients/meson.build @@ -54,3 +54,5 @@ executable( install: true ) +clients = files('lab-sensible-terminal') +install_data(clients, install_dir: get_option('bindir')) diff --git a/meson.build b/meson.build index dec66dd3..5f4cfe9b 100644 --- a/meson.build +++ b/meson.build @@ -199,10 +199,6 @@ install_data('data/labwc.desktop', install_dir: get_option('datadir') / 'wayland install_data('data/labwc-portals.conf', install_dir: get_option('datadir') / 'xdg-desktop-portal') -# TODO: move this to clients/meson.build after the labnag PR -clients = files('clients/lab-sensible-terminal') -install_data(clients, install_dir: get_option('bindir')) - icons = ['labwc-symbolic.svg', 'labwc.svg'] foreach icon : icons icon_path = join_paths('data', icon) From f0ccbfed182bb33f2e5c0ca389887de3eb2c34af Mon Sep 17 00:00:00 2001 From: 01micko <01micko@gmx.com> Date: Sat, 30 Aug 2025 20:17:43 +1000 Subject: [PATCH 125/248] docs/labnag.1.scd: add a labnag example --- docs/labnag.1.scd | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/labnag.1.scd b/docs/labnag.1.scd index 5dc91ed8..68ed3b68 100644 --- a/docs/labnag.1.scd +++ b/docs/labnag.1.scd @@ -107,3 +107,34 @@ _labnag_ [options...] *--button-padding* Set the padding for the button text. +# EXAMPLE + +This is a simple example of a _labnag_ logout GUI. + +``` +#!/bin/sh + +# logout with labnag + +labnag \\ + -f "Hack Regular 10"\\ + -m "Choose your logout option"\\ + -Z " Lock " "gtklock -d"\ + -Z " Logout " "labwc -e"\\ + -Z "Shutdown " "systemctl poweroff"\\ + -Z " Reboot " "systemctl reboot"\\ + -Z "Hibernate" "systemctl hibernate"\\ + -Z " Suspend " "systemctl suspend"\\ + -Z " Cancel "\\ + --background 00ffff\\ + --button-background 00ffff\\ + --border 00ccccaa\\ + --text 000000\\ + --button-text 000000\\ + --button-gap 8\\ + --button-margin-right 0\\ + --button-padding 5\\ + --button-border-size 2\\ + -t 60 +``` + From d1a9529239bd7e46e8bf5a08fc845abbcd71f60c Mon Sep 17 00:00:00 2001 From: 01micko <01micko@gmx.com> Date: Sat, 30 Aug 2025 21:51:14 +1000 Subject: [PATCH 126/248] docs/labnag.1.scd: fix missed escape --- docs/labnag.1.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/labnag.1.scd b/docs/labnag.1.scd index 68ed3b68..8ff5fe18 100644 --- a/docs/labnag.1.scd +++ b/docs/labnag.1.scd @@ -119,7 +119,7 @@ This is a simple example of a _labnag_ logout GUI. labnag \\ -f "Hack Regular 10"\\ -m "Choose your logout option"\\ - -Z " Lock " "gtklock -d"\ + -Z " Lock " "gtklock -d"\\ -Z " Logout " "labwc -e"\\ -Z "Shutdown " "systemctl poweroff"\\ -Z " Reboot " "systemctl reboot"\\ From 78d47c5c377f656d7d2ed9112627a58af1897d72 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sun, 31 Aug 2025 04:38:36 +0900 Subject: [PATCH 127/248] overlay.h: reduce included headers --- include/overlay.h | 7 +++---- src/input/tablet.c | 1 + src/input/touch.c | 1 + src/osd/osd-thumbnail.c | 2 +- src/overlay.c | 3 ++- src/resize-outlines.c | 1 + 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/include/overlay.h b/include/overlay.h index 96897cad..111532a1 100644 --- a/include/overlay.h +++ b/include/overlay.h @@ -2,10 +2,9 @@ #ifndef LABWC_OVERLAY_H #define LABWC_OVERLAY_H -#include -#include "common/graphic-helpers.h" -#include "regions.h" -#include "view.h" +#include "common/edge.h" + +struct seat; /* TODO: replace this with single lab_scene_rect */ struct overlay_rect { diff --git a/src/input/tablet.c b/src/input/tablet.c index ab96fb84..9d9b9815 100644 --- a/src/input/tablet.c +++ b/src/input/tablet.c @@ -18,6 +18,7 @@ #include "labwc.h" #include "idle.h" #include "action.h" +#include "view.h" bool tablet_tool_has_focused_surface(struct seat *seat) diff --git a/src/input/touch.c b/src/input/touch.c index 82b9042b..0486535e 100644 --- a/src/input/touch.c +++ b/src/input/touch.c @@ -15,6 +15,7 @@ #include "idle.h" #include "labwc.h" #include "ssd.h" +#include "view.h" /* Holds layout -> surface offsets to report motion events in relative coords */ struct touch_point { diff --git a/src/osd/osd-thumbnail.c b/src/osd/osd-thumbnail.c index 2f37d50d..3ede04d6 100644 --- a/src/osd/osd-thumbnail.c +++ b/src/osd/osd-thumbnail.c @@ -5,7 +5,6 @@ #include #include #include "config/rcxml.h" -#include "config/types.h" #include "common/array.h" #include "common/box.h" #include "common/lab-scene-rect.h" @@ -15,6 +14,7 @@ #include "osd.h" #include "output.h" #include "theme.h" +#include "view.h" struct osd_thumbnail_scene_item { struct view *view; diff --git a/src/overlay.c b/src/overlay.c index 0c2235d3..2a5cc845 100644 --- a/src/overlay.c +++ b/src/overlay.c @@ -6,8 +6,9 @@ #include "config/rcxml.h" #include "labwc.h" #include "output.h" -#include "view.h" +#include "regions.h" #include "theme.h" +#include "view.h" static void create_overlay_rect(struct seat *seat, struct overlay_rect *rect, diff --git a/src/resize-outlines.c b/src/resize-outlines.c index abb201a1..a32351ac 100644 --- a/src/resize-outlines.c +++ b/src/resize-outlines.c @@ -7,6 +7,7 @@ #include "resize-indicator.h" #include "ssd.h" #include "theme.h" +#include "view.h" bool resize_outlines_enabled(struct view *view) From 66e26cd28a44b3830cc2b03bde3a25cda8df2e34 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sun, 31 Aug 2025 04:41:08 +0900 Subject: [PATCH 128/248] overlay: refactor - Use a single `lab_scene_rect` for both overlay background and outlines, like I described in the TODO comment in ffd4005. - Simplify the resource management by destroying the overlay tree when it's hidden. I think its overhead is pretty minimal. - Share a single `lab_scene_rect` for both region/edge overlays. --- include/overlay.h | 25 ++------ src/debug.c | 25 ++------ src/interactive.c | 2 +- src/output.c | 2 +- src/overlay.c | 145 +++++++++++----------------------------------- src/regions.c | 2 +- src/seat.c | 2 +- src/workspaces.c | 2 +- 8 files changed, 51 insertions(+), 154 deletions(-) diff --git a/include/overlay.h b/include/overlay.h index 111532a1..a7911f3a 100644 --- a/include/overlay.h +++ b/include/overlay.h @@ -6,19 +6,8 @@ struct seat; -/* TODO: replace this with single lab_scene_rect */ -struct overlay_rect { - struct wlr_scene_tree *tree; - - bool bg_enabled; - struct wlr_scene_rect *bg_rect; - - bool border_enabled; - struct lab_scene_rect *border_rect; -}; - struct overlay { - struct overlay_rect region_rect, edge_rect; + struct lab_scene_rect *rect; /* Represents currently shown or delayed overlay */ struct { @@ -34,15 +23,13 @@ struct overlay { struct wl_event_source *timer; }; -void overlay_reconfigure(struct seat *seat); - -/* Calls overlay_hide() internally if there's no overlay to show */ +/* + * Shows or updates an overlay when the grabbed window can be snapped to + * a region or an output edge. Calls overlay_finish() otherwise. + */ void overlay_update(struct seat *seat); -/* This function must be called when server->grabbed_view is destroyed */ -void overlay_hide(struct seat *seat); - -/* This function is called to clean up the timer on exit */ +/* Destroys the overlay if it exists */ void overlay_finish(struct seat *seat); #endif diff --git a/src/debug.c b/src/debug.c index 41537103..97c011f2 100644 --- a/src/debug.c +++ b/src/debug.c @@ -22,7 +22,7 @@ #define IGNORE_SSD true #define IGNORE_MENU true #define IGNORE_OSD_PREVIEW_OUTLINE true -#define IGNORE_SNAPPING_PREVIEW_OUTLINE true +#define IGNORE_SNAPPING_OVERLAY true static struct view *last_view; @@ -141,15 +141,10 @@ get_special(struct server *server, struct wlr_scene_node *node) if (node == &server->seat.drag.icons->node) { return "seat->drag.icons"; } - if (server->seat.overlay.region_rect.tree - && node == &server->seat.overlay.region_rect.tree->node) { + if (server->seat.overlay.rect + && node == &server->seat.overlay.rect->tree->node) { /* Created on-demand */ - return "seat->overlay.region_rect"; - } - if (server->seat.overlay.edge_rect.tree - && node == &server->seat.overlay.edge_rect.tree->node) { - /* Created on-demand */ - return "seat->overlay.edge_rect"; + return "seat->overlay.rect"; } if (server->seat.input_method_relay->popup_tree && node == &server->seat.input_method_relay->popup_tree->node) { @@ -224,21 +219,13 @@ dump_tree(struct server *server, struct wlr_scene_node *node, struct lab_scene_rect *osd_preview_outline = server->osd_state.preview_outline; - struct lab_scene_rect *region_snapping_overlay_outline = - server->seat.overlay.region_rect.border_rect; - struct lab_scene_rect *edge_snapping_overlay_outline = - server->seat.overlay.edge_rect.border_rect; if ((IGNORE_MENU && node == &server->menu_tree->node) || (IGNORE_SSD && last_view && ssd_debug_is_root_node(last_view->ssd, node)) || (IGNORE_OSD_PREVIEW_OUTLINE && osd_preview_outline && node == &osd_preview_outline->tree->node) - || (IGNORE_SNAPPING_PREVIEW_OUTLINE - && region_snapping_overlay_outline - && node == ®ion_snapping_overlay_outline->tree->node) - || (IGNORE_SNAPPING_PREVIEW_OUTLINE - && edge_snapping_overlay_outline - && node == &edge_snapping_overlay_outline->tree->node)) { + || (IGNORE_SNAPPING_OVERLAY && server->seat.overlay.rect + && node == &server->seat.overlay.rect->tree->node)) { printf("%*c%s\n", pos + 4 + INDENT_SIZE, ' ', ""); return; } diff --git a/src/interactive.c b/src/interactive.c index 517b898e..f0c30e75 100644 --- a/src/interactive.c +++ b/src/interactive.c @@ -303,7 +303,7 @@ interactive_cancel(struct view *view) return; } - overlay_hide(&view->server->seat); + overlay_finish(&view->server->seat); resize_indicator_hide(view); diff --git a/src/output.c b/src/output.c index f683c2de..7715b899 100644 --- a/src/output.c +++ b/src/output.c @@ -171,7 +171,7 @@ handle_output_destroy(struct wl_listener *listener, void *data) regions_evacuate_output(output); regions_destroy(seat, &output->regions); if (seat->overlay.active.output == output) { - overlay_hide(seat); + overlay_finish(seat); } wl_list_remove(&output->link); wl_list_remove(&output->frame.link); diff --git a/src/overlay.c b/src/overlay.c index 2a5cc845..4cf6c4fd 100644 --- a/src/overlay.c +++ b/src/overlay.c @@ -11,97 +11,40 @@ #include "view.h" static void -create_overlay_rect(struct seat *seat, struct overlay_rect *rect, - struct theme_snapping_overlay *theme) -{ - struct server *server = seat->server; - - rect->bg_enabled = theme->bg_enabled; - rect->border_enabled = theme->border_enabled; - rect->tree = wlr_scene_tree_create(&server->scene->tree); - - if (rect->bg_enabled) { - /* Create a filled rectangle */ - rect->bg_rect = wlr_scene_rect_create( - rect->tree, 0, 0, theme->bg_color); - } - - if (rect->border_enabled) { - /* Create outlines */ - struct lab_scene_rect_options opts = { - .border_colors = (float *[3]) { - theme->border_color[0], - theme->border_color[1], - theme->border_color[2], - }, - .nr_borders = 3, - .border_width = theme->border_width, - }; - rect->border_rect = lab_scene_rect_create(rect->tree, &opts); - } - - wlr_scene_node_set_enabled(&rect->tree->node, false); -} - -void overlay_reconfigure(struct seat *seat) -{ - if (seat->overlay.region_rect.tree) { - wlr_scene_node_destroy(&seat->overlay.region_rect.tree->node); - } - if (seat->overlay.edge_rect.tree) { - wlr_scene_node_destroy(&seat->overlay.edge_rect.tree->node); - } - - struct theme *theme = seat->server->theme; - create_overlay_rect(seat, &seat->overlay.region_rect, - &theme->snapping_overlay_region); - create_overlay_rect(seat, &seat->overlay.edge_rect, - &theme->snapping_overlay_edge); -} - -static void -show_overlay(struct seat *seat, struct overlay_rect *rect, struct wlr_box *box) +show_overlay(struct seat *seat, struct theme_snapping_overlay *overlay_theme, + struct wlr_box *box) { struct server *server = seat->server; struct view *view = server->grabbed_view; assert(view); + assert(!seat->overlay.rect); - if (!rect->tree) { - overlay_reconfigure(seat); - assert(rect->tree); + struct lab_scene_rect_options opts = { + .width = box->width, + .height = box->height, + }; + if (overlay_theme->bg_enabled) { + /* Create a filled rectangle */ + opts.bg_color = overlay_theme->bg_color; + } + float *border_colors[3] = { + overlay_theme->border_color[0], + overlay_theme->border_color[1], + overlay_theme->border_color[2], + }; + if (overlay_theme->border_enabled) { + /* Create outlines */ + opts.border_colors = border_colors; + opts.nr_borders = 3; + opts.border_width = overlay_theme->border_width; } - if (rect->bg_enabled) { - wlr_scene_rect_set_size(rect->bg_rect, box->width, box->height); - } - if (rect->border_enabled) { - lab_scene_rect_set_size(rect->border_rect, box->width, box->height); - } + seat->overlay.rect = + lab_scene_rect_create(view->scene_tree->node.parent, &opts); - struct wlr_scene_node *node = &rect->tree->node; - wlr_scene_node_reparent(node, view->scene_tree->node.parent); + struct wlr_scene_node *node = &seat->overlay.rect->tree->node; wlr_scene_node_place_below(node, &view->scene_tree->node); wlr_scene_node_set_position(node, box->x, box->y); - wlr_scene_node_set_enabled(node, true); -} - -static void -inactivate_overlay(struct overlay *overlay) -{ - if (overlay->region_rect.tree) { - wlr_scene_node_set_enabled( - &overlay->region_rect.tree->node, false); - } - if (overlay->edge_rect.tree) { - wlr_scene_node_set_enabled( - &overlay->edge_rect.tree->node, false); - } - overlay->active.region = NULL; - overlay->active.edge = LAB_EDGE_NONE; - overlay->active.output = NULL; - if (overlay->timer) { - wl_event_source_timer_update(overlay->timer, 0); - } } static void @@ -110,11 +53,11 @@ show_region_overlay(struct seat *seat, struct region *region) if (region == seat->overlay.active.region) { return; } - inactivate_overlay(&seat->overlay); + overlay_finish(seat); seat->overlay.active.region = region; struct wlr_box geo = view_get_region_snap_box(NULL, region); - show_overlay(seat, &seat->overlay.region_rect, &geo); + show_overlay(seat, &rc.theme->snapping_overlay_region, &geo); } static struct wlr_box @@ -135,7 +78,7 @@ handle_edge_overlay_timeout(void *data) && seat->overlay.active.output); struct wlr_box box = get_edge_snap_box(seat->overlay.active.edge, seat->overlay.active.output); - show_overlay(seat, &seat->overlay.edge_rect, &box); + show_overlay(seat, &rc.theme->snapping_overlay_edge, &box); return 0; } @@ -165,7 +108,7 @@ show_edge_overlay(struct seat *seat, enum lab_edge edge1, enum lab_edge edge2, && seat->overlay.active.output == output) { return; } - inactivate_overlay(&seat->overlay); + overlay_finish(seat); seat->overlay.active.edge = edge; seat->overlay.active.output = output; @@ -186,8 +129,7 @@ show_edge_overlay(struct seat *seat, enum lab_edge edge1, enum lab_edge edge2, wl_event_source_timer_update(seat->overlay.timer, delay); } else { /* Show overlay now */ - struct wlr_box box = get_edge_snap_box(edge, output); - show_overlay(seat, &seat->overlay.edge_rect, &box); + handle_edge_overlay_timeout(seat); } } @@ -213,36 +155,17 @@ overlay_update(struct seat *seat) return; } - overlay_hide(seat); -} - -void -overlay_hide(struct seat *seat) -{ - struct overlay *overlay = &seat->overlay; - struct server *server = seat->server; - - inactivate_overlay(overlay); - - /* - * Reparent the rectangle nodes to server's scene-tree so they don't - * get destroyed on view destruction - */ - if (overlay->region_rect.tree) { - wlr_scene_node_reparent(&overlay->region_rect.tree->node, - &server->scene->tree); - } - if (overlay->edge_rect.tree) { - wlr_scene_node_reparent(&overlay->edge_rect.tree->node, - &server->scene->tree); - } + overlay_finish(seat); } void overlay_finish(struct seat *seat) { + if (seat->overlay.rect) { + wlr_scene_node_destroy(&seat->overlay.rect->tree->node); + } if (seat->overlay.timer) { wl_event_source_remove(seat->overlay.timer); - seat->overlay.timer = NULL; } + seat->overlay = (struct overlay){0}; } diff --git a/src/regions.c b/src/regions.c index 5243b95a..c06698b9 100644 --- a/src/regions.c +++ b/src/regions.c @@ -166,7 +166,7 @@ regions_destroy(struct seat *seat, struct wl_list *regions) wl_list_for_each_safe(region, region_tmp, regions, link) { wl_list_remove(®ion->link); if (seat && seat->overlay.active.region == region) { - overlay_hide(seat); + overlay_finish(seat); } zfree(region->name); zfree(region); diff --git a/src/seat.c b/src/seat.c index 9817e78b..25d8fc57 100644 --- a/src/seat.c +++ b/src/seat.c @@ -731,7 +731,7 @@ seat_reconfigure(struct server *server) struct seat *seat = &server->seat; struct input *input; cursor_reload(seat); - overlay_reconfigure(seat); + overlay_finish(seat); keyboard_reset_current_keybind(); wl_list_for_each(input, &seat->inputs, link) { switch (input->wlr_input_device->type) { diff --git a/src/workspaces.c b/src/workspaces.c index 04856a47..5345f69d 100644 --- a/src/workspaces.c +++ b/src/workspaces.c @@ -602,7 +602,7 @@ workspaces_reconfigure(struct server *server) } /* # of configured workspaces decreased */ - overlay_hide(&server->seat); + overlay_finish(&server->seat); struct workspace *first_workspace = wl_container_of(server->workspaces.all.next, first_workspace, link); From 6ed26173947392d275cafc9f5f02b65b03ee4b03 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 1 Sep 2025 17:16:53 +0900 Subject: [PATCH 129/248] view.c: remove outdated comment Since a5d89a2, xdg-shell views can request very small window geometry. --- src/view.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/view.c b/src/view.c index 521ffe58..293d466c 100644 --- a/src/view.c +++ b/src/view.c @@ -762,10 +762,7 @@ view_adjust_size(struct view *view, int *w, int *h) *w = round_to_increment(*w, hints.base_width, hints.width_inc); *h = round_to_increment(*h, hints.base_height, hints.height_inc); - /* - * If a minimum width/height was not set, then use default. - * This is currently always the case for xdg-shell views. - */ + /* If a minimum width/height was not set, then use default */ if (hints.min_width < 1) { hints.min_width = min_width; } From 164b17c279874679374786bae1dc517b3962c8d7 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 1 Sep 2025 17:29:24 +0900 Subject: [PATCH 130/248] view: use fixed default window width Now it's not very reasonable to determine the default window width based on the titlebar geometry, as the titlebar can be shrunk to 1px. Let's use the fixed value of 100px for simplification. --- include/view.h | 6 +++++- src/snap.c | 5 ++--- src/view.c | 14 +------------- src/xwayland.c | 4 +--- 4 files changed, 9 insertions(+), 20 deletions(-) diff --git a/include/view.h b/include/view.h index f2dcb5c9..94181015 100644 --- a/include/view.h +++ b/include/view.h @@ -11,6 +11,11 @@ #include "config.h" #include "config/types.h" +/* + * Default minimal window size. Clients can explicitly set smaller values via + * e.g. xdg_toplevel::set_min_size. + */ +#define LAB_MIN_VIEW_WIDTH 100 #define LAB_MIN_VIEW_HEIGHT 60 /* @@ -573,7 +578,6 @@ const char *view_get_string_prop(struct view *view, const char *prop); void view_update_title(struct view *view); void view_update_app_id(struct view *view); void view_reload_ssd(struct view *view); -int view_get_min_width(void); void view_set_shade(struct view *view, bool shaded); diff --git a/src/snap.c b/src/snap.c index fcb8f335..83087e26 100644 --- a/src/snap.c +++ b/src/snap.c @@ -221,7 +221,6 @@ snap_shrink_to_next_edge(struct view *view, *geo = view->pending; enum lab_edge resize_edges; - int min_width = view_get_min_width(); /* * First shrink the view along the relevant edge. The maximum shrink @@ -230,12 +229,12 @@ snap_shrink_to_next_edge(struct view *view, */ switch (direction) { case LAB_EDGE_RIGHT: - geo->width = MAX(geo->width / 2, min_width); + geo->width = MAX(geo->width / 2, LAB_MIN_VIEW_WIDTH); geo->x = view->pending.x + view->pending.width - geo->width; resize_edges = LAB_EDGE_LEFT; break; case LAB_EDGE_LEFT: - geo->width = MAX(geo->width / 2, min_width); + geo->width = MAX(geo->width / 2, LAB_MIN_VIEW_WIDTH); resize_edges = LAB_EDGE_RIGHT; break; case LAB_EDGE_BOTTOM: diff --git a/src/view.c b/src/view.c index 293d466c..4173f643 100644 --- a/src/view.c +++ b/src/view.c @@ -744,7 +744,6 @@ view_adjust_size(struct view *view, int *w, int *h) { assert(view); struct view_size_hints hints = view_get_size_hints(view); - int min_width = view_get_min_width(); /* * "If a base size is not provided, the minimum size is to be @@ -764,7 +763,7 @@ view_adjust_size(struct view *view, int *w, int *h) /* If a minimum width/height was not set, then use default */ if (hints.min_width < 1) { - hints.min_width = min_width; + hints.min_width = LAB_MIN_VIEW_WIDTH; } if (hints.min_height < 1) { hints.min_height = LAB_MIN_VIEW_HEIGHT; @@ -2412,17 +2411,6 @@ view_reload_ssd(struct view *view) } } -int -view_get_min_width(void) -{ - int button_count_left = wl_list_length(&rc.title_buttons_left); - int button_count_right = wl_list_length(&rc.title_buttons_right); - return (rc.theme->window_button_width * (button_count_left + button_count_right)) + - (rc.theme->window_button_spacing * MAX((button_count_right - 1), 0)) + - (rc.theme->window_button_spacing * MAX((button_count_left - 1), 0)) + - (2 * rc.theme->window_titlebar_padding_width); -} - void view_toggle_keybinds(struct view *view) { diff --git a/src/xwayland.c b/src/xwayland.c index 9f311e86..62c96831 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -748,15 +748,13 @@ handle_map_request(struct wl_listener *listener, void *data) static void check_natural_geometry(struct view *view) { - int min_width = view_get_min_width(); - /* * Some applications (example: Thonny) don't set a reasonable * un-maximized size when started maximized. Try to detect this * and set a fallback size. */ if (!view_is_floating(view) - && (view->natural_geometry.width < min_width + && (view->natural_geometry.width < LAB_MIN_VIEW_WIDTH || view->natural_geometry.height < LAB_MIN_VIEW_HEIGHT)) { view_set_fallback_natural_geometry(view); } From e6b5d91b638de9447a5e36fd11ee0d93abf5cda2 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 1 Sep 2025 17:04:41 +0900 Subject: [PATCH 131/248] view: let `view_set_fallback_natural_geometry()` return wlr_box --- include/view.h | 2 +- src/view.c | 18 +++++++++--------- src/xwayland.c | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/view.h b/include/view.h index 94181015..97fb2343 100644 --- a/include/view.h +++ b/include/view.h @@ -484,7 +484,7 @@ void view_moved(struct view *view); void view_minimize(struct view *view, bool minimized); bool view_compute_centered_position(struct view *view, const struct wlr_box *ref, int w, int h, int *x, int *y); -void view_set_fallback_natural_geometry(struct view *view); +struct wlr_box view_get_fallback_natural_geometry(struct view *view); void view_store_natural_geometry(struct view *view); /** diff --git a/src/view.c b/src/view.c index 4173f643..f48169fa 100644 --- a/src/view.c +++ b/src/view.c @@ -953,16 +953,16 @@ adjust_floating_geometry(struct view *view, struct wlr_box *geometry, &geometry->x, &geometry->y); } -void -view_set_fallback_natural_geometry(struct view *view) +struct wlr_box +view_get_fallback_natural_geometry(struct view *view) { - view->natural_geometry.width = VIEW_FALLBACK_WIDTH; - view->natural_geometry.height = VIEW_FALLBACK_HEIGHT; + struct wlr_box box = { + .width = VIEW_FALLBACK_WIDTH, + .height = VIEW_FALLBACK_HEIGHT, + }; view_compute_centered_position(view, NULL, - view->natural_geometry.width, - view->natural_geometry.height, - &view->natural_geometry.x, - &view->natural_geometry.y); + box.width, box.height, &box.x, &box.y); + return box; } void @@ -1502,7 +1502,7 @@ view_maximize(struct view *view, enum view_axis axis, */ if ((axis == VIEW_AXIS_HORIZONTAL || axis == VIEW_AXIS_VERTICAL) && wlr_box_empty(&view->natural_geometry)) { - view_set_fallback_natural_geometry(view); + view->natural_geometry = view_get_fallback_natural_geometry(view); } set_maximized(view, axis); diff --git a/src/xwayland.c b/src/xwayland.c index 62c96831..0a39ffdc 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -756,7 +756,7 @@ check_natural_geometry(struct view *view) if (!view_is_floating(view) && (view->natural_geometry.width < LAB_MIN_VIEW_WIDTH || view->natural_geometry.height < LAB_MIN_VIEW_HEIGHT)) { - view_set_fallback_natural_geometry(view); + view->natural_geometry = view_get_fallback_natural_geometry(view); } } From 72a5df16ea6ca713133d465c559b8e1ec7a1779c Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 1 Sep 2025 17:52:06 +0900 Subject: [PATCH 132/248] view: restore initially-maximized window position after unplug/plug `update_last_layout_geometry()` stores `view->natural_geometry` in `view->last_layout_geometry`, but it's empty for initially-maximized windows, so their positions were not restored after outputs are unplugged and plugged (also when VT switching in wlroots 0.19.0). This commit sets the fallback natural geometry (at the center of the output) so that initially-maximized windows reappears in the same output. --- src/view.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/view.c b/src/view.c index f48169fa..54eb6039 100644 --- a/src/view.c +++ b/src/view.c @@ -1806,8 +1806,12 @@ update_last_layout_geometry(struct view *view) if (view_is_floating(view)) { view->last_layout_geometry = view->pending; - } else { + } else if (!wlr_box_empty(&view->natural_geometry)) { view->last_layout_geometry = view->natural_geometry; + } else { + /* e.g. initially-maximized window */ + view->last_layout_geometry = + view_get_fallback_natural_geometry(view); } } From 6d2140c4b737f643b22164b4fff21af499a1aded Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Mon, 1 Sep 2025 11:49:24 -0400 Subject: [PATCH 133/248] view: expose view_set_maximized() instead of view_restore_to() view_restore_to() (which is just set_maximized() + view_move_resize()) hasn't aged well and doesn't line up with typical usage anymore: - it's missing view_set_untiled(), which has to be called separately - it always forces view_move_resize() even when that's not needed - it doesn't allow un-maximizing only one axis (see next commit) - the fullscreen check is unnecessary (already checked in callers) Eliminate it and just expose view_set_maximized() instead. No functional change intended in this commit. --- include/view.h | 2 +- src/input/cursor.c | 3 ++- src/interactive.c | 5 +++-- src/view.c | 37 ++++++++++++++----------------------- 4 files changed, 20 insertions(+), 27 deletions(-) diff --git a/include/view.h b/include/view.h index 97fb2343..5ff70984 100644 --- a/include/view.h +++ b/include/view.h @@ -518,7 +518,7 @@ void view_place_by_policy(struct view *view, bool allow_cursor, enum lab_placement_policy policy); void view_constrain_size_to_that_of_usable_area(struct view *view); -void view_restore_to(struct view *view, struct wlr_box geometry); +void view_set_maximized(struct view *view, enum view_axis maximized); void view_set_untiled(struct view *view); void view_maximize(struct view *view, enum view_axis axis, bool store_natural_geometry); diff --git a/src/input/cursor.c b/src/input/cursor.c index 4c6b353e..a051092f 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -291,8 +291,9 @@ process_cursor_move(struct server *server, uint32_t time) interactive_anchor_to_cursor(server, &new_geo); /* Shaded clients will not process resize events until unshaded */ view_set_shade(view, false); + view_set_maximized(view, VIEW_AXIS_NONE); view_set_untiled(view); - view_restore_to(view, new_geo); + view_move_resize(view, new_geo); x = new_geo.x; y = new_geo.y; } diff --git a/src/interactive.c b/src/interactive.c index f0c30e75..adc06b8c 100644 --- a/src/interactive.c +++ b/src/interactive.c @@ -122,8 +122,8 @@ interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges) * maximized/tiled state but keep the same geometry as * the starting point for the resize. */ + view_set_maximized(view, VIEW_AXIS_NONE); view_set_untiled(view); - view_restore_to(view, view->pending); cursor_shape = cursor_get_from_edge(edges); break; default: @@ -153,8 +153,9 @@ interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges) interactive_anchor_to_cursor(server, &natural_geo); /* Shaded clients will not process resize events until unshaded */ view_set_shade(view, false); + view_set_maximized(view, VIEW_AXIS_NONE); view_set_untiled(view); - view_restore_to(view, natural_geo); + view_move_resize(view, natural_geo); } if (rc.resize_indicator) { diff --git a/src/view.c b/src/view.c index 54eb6039..1bc205e0 100644 --- a/src/view.c +++ b/src/view.c @@ -660,7 +660,7 @@ view_move_relative(struct view *view, int x, int y) view_maximize(view, VIEW_AXIS_NONE, /*store_natural_geometry*/ false); if (view_is_tiled(view)) { view_set_untiled(view); - view_restore_to(view, view->natural_geometry); + view_move_resize(view, view->natural_geometry); } view_move(view, view->pending.x + x, view->pending.y + y); } @@ -678,7 +678,7 @@ view_move_to_cursor(struct view *view) view_maximize(view, VIEW_AXIS_NONE, /*store_natural_geometry*/ false); if (view_is_tiled(view)) { view_set_untiled(view); - view_restore_to(view, view->natural_geometry); + view_move_resize(view, view->natural_geometry); } struct border margin = ssd_thickness(view); @@ -1367,10 +1367,18 @@ view_apply_special_geometry(struct view *view) } } -/* For internal use only. Does not update geometry. */ -static void -set_maximized(struct view *view, enum view_axis maximized) +/* + * Sets maximized state without updating geometry. Used in interactive + * move/resize. In most other cases, use view_maximize() instead. + */ +void +view_set_maximized(struct view *view, enum view_axis maximized) { + assert(view); + if (view->maximized == maximized) { + return; + } + if (view->impl->maximize) { view->impl->maximize(view, maximized); } @@ -1386,23 +1394,6 @@ set_maximized(struct view *view, enum view_axis maximized) ssd_update_margin(view->ssd); } -/* - * Un-maximize view and move it to specific geometry. Does not reset - * tiled state (use view_set_untiled() if you want that). - */ -void -view_restore_to(struct view *view, struct wlr_box geometry) -{ - assert(view); - if (view->fullscreen) { - return; - } - if (view->maximized != VIEW_AXIS_NONE) { - set_maximized(view, VIEW_AXIS_NONE); - } - view_move_resize(view, geometry); -} - bool view_is_tiled(struct view *view) { @@ -1505,7 +1496,7 @@ view_maximize(struct view *view, enum view_axis axis, view->natural_geometry = view_get_fallback_natural_geometry(view); } - set_maximized(view, axis); + view_set_maximized(view, axis); if (view_is_floating(view)) { view_apply_natural_geometry(view); } else { From f9f4219ed495885ce1d679dcb2395b3b386f3765 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Mon, 1 Sep 2025 12:07:52 -0400 Subject: [PATCH 134/248] interactive: un-maximize only axes that are being resized When resizing in only one axis (horizontal/vertical), there's no reason to un-maximize the other axis. Supporting logic was landed in the previous commit and in ebd39dfe0d1d (which fixed the client-initiated resize case), so all that remains is to make a small change in interactive_begin(). --- src/interactive.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/interactive.c b/src/interactive.c index adc06b8c..061fe07a 100644 --- a/src/interactive.c +++ b/src/interactive.c @@ -119,10 +119,17 @@ interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges) /* * If tiled or maximized in only one direction, reset - * maximized/tiled state but keep the same geometry as - * the starting point for the resize. + * tiled state and un-maximize the relevant axes, but + * keep the same geometry as the starting point. */ - view_set_maximized(view, VIEW_AXIS_NONE); + enum view_axis maximized = view->maximized; + if (edges & LAB_EDGES_LEFT_RIGHT) { + maximized &= ~VIEW_AXIS_HORIZONTAL; + } + if (edges & LAB_EDGES_TOP_BOTTOM) { + maximized &= ~VIEW_AXIS_VERTICAL; + } + view_set_maximized(view, maximized); view_set_untiled(view); cursor_shape = cursor_get_from_edge(edges); break; From b5b257092d6bf4e2e120639768a8da4e0d567ce2 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Mon, 1 Sep 2025 20:47:05 +0100 Subject: [PATCH 135/248] NEWS.md: link from TOC to each section --- NEWS.md | 217 +++++++++++++++++++++++++++++++++------------ scripts/.gitignore | 1 + scripts/rip.c | 2 +- 3 files changed, 163 insertions(+), 57 deletions(-) diff --git a/NEWS.md b/NEWS.md index 21b6fe7c..8cfac48f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog] | Date | All Changes | wlroots version | lines-of-code | |------------|---------------|-----------------|---------------| +| 2025-08-25 | [unreleased] | 0.19.0 | | | 2025-08-02 | [0.9.1] | 0.19.0 | 28605 | | 2025-07-11 | [0.9.0] | 0.19.0 | 28586 | | 2025-05-02 | [0.8.4] | 0.18.2 | 27679 | @@ -37,6 +38,35 @@ The format is based on [Keep a Changelog] | 2021-04-15 | [0.2.0] | 0.13.0 | 5011 | | 2021-03-05 | [0.1.0] | 0.12.0 | 4627 | +[unreleased]: NEWS.md#unreleased +[0.9.1]: NEWS.md#091---2025-08-02 +[0.9.0]: NEWS.md#090---2025-07-11 +[0.8.4]: NEWS.md#084---2025-05-02 +[0.8.3]: NEWS.md#083---2025-02-21 +[0.8.2]: NEWS.md#082---2024-12-13 +[0.8.1]: NEWS.md#081---2024-10-25 +[0.8.0]: NEWS.md#080---2024-08-16 +[0.7.4]: NEWS.md#074---2024-06-19 +[0.7.3]: NEWS.md#073---2024-06-12 +[0.7.2]: NEWS.md#072---2024-05-10 +[0.7.1]: NEWS.md#071---2024-03-01 +[0.7.0]: NEWS.md#070---2023-12-22 +[0.6.6]: NEWS.md#065---2023-11-25 +[0.6.5]: NEWS.md#064---2023-09-23 +[0.6.4]: NEWS.md#063---2023-07-14 +[0.6.3]: NEWS.md#062---2023-05-08 +[0.6.2]: NEWS.md#061---2023-03-20 +[0.6.1]: NEWS.md#060---2023-01-29 +[0.6.0]: NEWS.md#060---2022-11-17 +[0.5.3]: NEWS.md#053---2022-07-15 +[0.5.2]: NEWS.md#052---2022-05-17 +[0.5.1]: NEWS.md#051---2022-04-08 +[0.5.0]: NEWS.md#050---2022-02-18 +[0.4.0]: NEWS.md#040---2021-12-31 +[0.3.0]: NEWS.md#030---2021-06-28 +[0.2.0]: NEWS.md#020---2021-04-15 +[0.1.0]: NEWS.md#010---2021-03-05 + ## Notes on wlroots-0.19 There are some regression warnings worth noting for the switch to wlroots 0.19: @@ -74,7 +104,9 @@ There are some regression warnings worth noting for the switch to wlroots 0.19: [wlroots-5098]:https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5098 [gtk-8792]: https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/8792 -## [unreleased] +## unreleased + +[unreleased-commits] ### Added @@ -135,7 +167,9 @@ There are some regression warnings worth noting for the switch to wlroots 0.19: - Make `autoEnableOutputs=no` apply only to drm outputs @jlindgren90 [#2972] - Take into account `` for edge and region overlays @tokyo4j [#2965] -## [0.9.1] +## 0.9.1 - 2025-08-02 + +[0.9.1-commits] This is an earlier-than-usual release containinig bug fixes only. It has been done on a separate branch to avoid the inclusion of refactoring and new @@ -157,7 +191,9 @@ features. - Fix swapped width/height in XWayland client `_NET_WM_ICON` stride calculation [#2909] @jlindgren90 -## [0.9.0] +## 0.9.0 - 2025-07-11 + +[0.9.0-commits] The main focus has been to port labwc to wlroots 0.19 [#2388] and fix associated issues. Special thanks to @Consolatis @jlindgren90 for this. @@ -278,7 +314,9 @@ window.*.title.bg.colorTo.splitTo: ``` -## [0.8.4] +## 0.8.4 - 2025-05-02 + +[0.8.4-commits] This release predominantly consists of bug-fixes, code simplification and usability improvements. Amongst the new features the most noteworthy is the @@ -342,7 +380,9 @@ release. - Increase default `` to 10 to make it easier to snap windows on the edge between two monitors. @johanmalm [#2602] [#2608] -## [0.8.3] +## 0.8.3 - 2025-02-21 + +[0.8.3-commits] The eye-catching new features of this release are undoubtedly: 1. Support for the `ext-workspace` protocol with big thanks to @Consolatis @@ -460,7 +500,9 @@ Notes to package maintainers: and followMouseRequiresMovement="no" in rc.xml, keyboard-focus semantics have subtly changed when using the window-switcher. [#2455] -## [0.8.2] +## 0.8.2 - 2024-12-13 + +[0.8.2-commits] This is a shorter release cycle compared with the usual 10-week one because it contains a significant number of stability and cleanliness fixes which warrant @@ -599,7 +641,9 @@ menu.border.color: #aaaaaa Openbox's behavior and (ii) behave as already described in our own documentation. [#2380] -## [0.8.1] +## 0.8.1 - 2024-10-25 + +[0.8.1-commits] The most noteworthy additions in this release are: @@ -747,7 +791,9 @@ window.inactive.button.shade.unpressed.image.color per-device configuration of scroll factor (e.g. setting different scroll factors for mice and touchpads). [#2057] -## [0.8.0] +## 0.8.0 - 2024-08-16 + +[0.8.0-commits] The main focus in this release has been to port labwc to wlroots 0.18 and to grind out associated regressions. Nonetheless, it contains a few non-related @@ -828,7 +874,9 @@ have been attributed with a 'Written-by' against each relevant log entry. - Make windows stay fullscreen when associated output is disconnected. [#2040] -## [0.7.4] +## 0.7.4 - 2024-06-19 + +[0.7.4-commits] ### Fixed @@ -836,7 +884,9 @@ have been attributed with a 'Written-by' against each relevant log entry. - Fix magnifier by disabling direct scanout when active. [#1989] - Fix crash triggered by pipemenu without parent `` element. [#1988] -## [0.7.3] +## 0.7.3 - 2024-06-12 + +[0.7.3-commits] Following a couple of big releases, this one feels like more steady with lots of focus on bug fixes and stability. In terms of new features the most noteworthy @@ -925,7 +975,9 @@ joint effort by @spl237 and @Consolatis. - Action `MoveToCursor` is deprecated in favour of: ``. -## [0.7.2] +## 0.7.2 - 2024-05-10 + +[0.7.2-commits] This release shaped up to be the second in a row that is larger than usual in terms of both fixes and new features. Significant additions include @@ -1193,7 +1245,9 @@ osd.window-switcher.preview.border.color: #ffffff,#00a2ff,#ffffff the DRM backend or by explicit request using environment variable `LABWC_UPDATE_ACTIVATION_ENV`. -## [0.7.1] +## 0.7.1 - 2024-03-01 + +[0.7.1-commits] ### Added @@ -1364,7 +1418,9 @@ osd.window-switcher.preview.border.color: #ffffff,#00a2ff,#ffffff full `app_id`. Consult the labwc-config(5) manual page for more details. [#1309] -## [0.7.0] - 2023-12-22 +## 0.7.0 - 2023-12-22 + +[0.7.0-commits] The main effort in this release has gone into porting labwc to wlroots 0.17 and tidying up regressions. Nonetheless, it contains a significant number of @@ -1424,7 +1480,9 @@ Should bug fixes be required against `0.6.6` (built with wlroots `0.16`), a - Use the GTK3 notebook header color as the default active title color (small change from `#dddad6` to `#e1dedb`). Written-by: @dimkr -## [0.6.6] - 2023-11-25 +## 0.6.6 - 2023-11-25 + +[0.6.6-commits] We do not normally call out contributions by core devs in the changelog, but a special thanks goes to @jlindgren90 in this release for lots of work @@ -1538,7 +1596,9 @@ relating to surface focus and keyboard issues, amongst others. will not appear anymore. - Always switch to the workspace containing the view being focused. -## [0.6.5] - 2023-09-23 +## 0.6.5 - 2023-09-23 + +[0.6.5-commits] ### Added @@ -1614,7 +1674,9 @@ relating to surface focus and keyboard issues, amongst others. - Do not expand environment variables in `Exec` action `` argument (but still resolve tilde). -## [0.6.4] - 2023-07-14 +## 0.6.4 - 2023-07-14 + +[0.6.4-commits] ### Added @@ -1655,7 +1717,9 @@ relating to surface focus and keyboard issues, amongst others. - Make `ToggleKeybinds` applicable only to the window that has keyboard focus when the action is executed. -## [0.6.3] - 2023-05-08 +## 0.6.3 - 2023-05-08 + +[0.6.3-commits] ### Added @@ -1727,7 +1791,9 @@ relating to surface focus and keyboard issues, amongst others. - Default to follow="true" for SendToDesktop action as per Openbox 3.6 specification. -## [0.6.2] - 2023-03-20 +## 0.6.2 - 2023-03-20 + +[0.6.2-commits] This release contains refactoring and simplification relating to view-output association and xdg/xwayland configure/map events. @@ -1789,7 +1855,9 @@ Unless otherwise stated all contributions are by the core-devs ``` -## [0.6.1] - 2023-01-29 +## 0.6.1 - 2023-01-29 + +[0.6.1-commits] As usual, this release contains lots of refactoring and bug fixes with particular thanks going to @Consolatis, @jlindgren90, @bi4k8, @Flrian and @@ -1852,7 +1920,9 @@ particular thanks going to @Consolatis, @jlindgren90, @bi4k8, @Flrian and VS Code and Discord lagging over time. [#553] Written-by: @Joshua-Ashton - Do not switch output on SnapToEdge if view is maximized. Written-by: @Flrian -## [0.6.0] - 2022-11-17 +## 0.6.0 - 2022-11-17 + +[0.6.0-commits] This release contains significant refactoring to use the wlroots scene-graph API. This touches many areas of the code, particularly @@ -2019,7 +2089,9 @@ reported, tested and fixed issues. Particular mentions go to @bi4k8, exited the compositor by mistake trying to get out of alt-tab cycling or similar. -## [0.5.3] - 2022-07-15 +## 0.5.3 - 2022-07-15 + +[0.5.3-commits] ### Added @@ -2041,7 +2113,9 @@ reported, tested and fixed issues. Particular mentions go to @bi4k8, - Do not segfault on missing drag icon. Written-by: @Consolatis - Fix windows erratically sticking to edges during move/resize [#331] [#309] -## [0.5.2] - 2022-05-17 +## 0.5.2 - 2022-05-17 + +[0.5.2-commits] This is a minor bugfix release mostly to ease packaging. @@ -2049,7 +2123,9 @@ This is a minor bugfix release mostly to ease packaging. - Properly use system provided wlroots. Written-by: @eli-schwartz -## [0.5.1] - 2022-04-08 +## 0.5.1 - 2022-04-08 + +[0.5.1-commits] ### Added @@ -2076,7 +2152,9 @@ This is a minor bugfix release mostly to ease packaging. - Fix qt application crash on touchpad scroll [#225] Written-by: @Consolatis -## [0.5.0] - 2022-02-18 +## 0.5.0 - 2022-02-18 + +[0.5.0-commits] As usual, this release contains a bunch of fixes and improvements, of which the most notable feature-type changes are listed below. A big @@ -2113,7 +2191,9 @@ This release contains the following two breaking changes: updating any local `rc.xml` settings in accordance with `docs/rc.xml.all` -## [0.4.0] - 2021-12-31 +## 0.4.0 - 2021-12-31 + +[0.4.0-commits] Compile with wlroots 0.15.0 @@ -2188,7 +2268,9 @@ feature-type changes are listed below. - The config option `` has changed to `` (breaking change) -## [0.3.0] - 2021-06-28 +## 0.3.0 - 2021-06-28 + +[0.3.0-commits] Compile with wlroots 0.14.0 @@ -2199,7 +2281,9 @@ Compile with wlroots 0.14.0 - Do not use Clearlooks-3.4 theme by default, just use built-in theme - Fix bug which triggered Qt application segfault -## [0.2.0] - 2021-04-15 +## 0.2.0 - 2021-04-15 + +[0.2.0-commits] Compile with wlroots 0.13.0 @@ -2212,7 +2296,9 @@ Compile with wlroots 0.13.0 - Add labwc-environment(5) - Call `wlr_output_enable_adaptive_sync()` if `LABWC_ADAPTIVE_SYNC` set -## [0.1.0] - 2021-03-05 +## 0.1.0 - 2021-03-05 + +[0.1.0-commits] Compile with wlroots 0.12.0 and wayland-server >=1.16 @@ -2231,34 +2317,34 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16 ShowMenu [Keep a Changelog]: https://keepachangelog.com/en/1.0.0/ -[unreleased]: https://github.com/labwc/labwc/compare/0.9.0...HEAD -[0.9.1]: https://github.com/labwc/labwc/compare/0.9.0...0.9.1 -[0.9.0]: https://github.com/labwc/labwc/compare/0.8.4...0.9.0 -[0.8.4]: https://github.com/labwc/labwc/compare/0.8.3...0.8.4 -[0.8.3]: https://github.com/labwc/labwc/compare/0.8.2...0.8.3 -[0.8.2]: https://github.com/labwc/labwc/compare/0.8.1...0.8.2 -[0.8.1]: https://github.com/labwc/labwc/compare/0.8.0...0.8.1 -[0.8.0]: https://github.com/labwc/labwc/compare/0.7.3...0.8.0 -[0.7.4]: https://github.com/labwc/labwc/compare/0.7.3...0.7.4 -[0.7.3]: https://github.com/labwc/labwc/compare/0.7.2...0.7.3 -[0.7.2]: https://github.com/labwc/labwc/compare/0.7.1...0.7.2 -[0.7.1]: https://github.com/labwc/labwc/compare/0.7.0...0.7.1 -[0.7.0]: https://github.com/labwc/labwc/compare/0.6.6...0.7.0 -[0.6.6]: https://github.com/labwc/labwc/compare/0.6.5...0.6.6 -[0.6.5]: https://github.com/labwc/labwc/compare/0.6.4...0.6.5 -[0.6.4]: https://github.com/labwc/labwc/compare/0.6.3...0.6.4 -[0.6.3]: https://github.com/labwc/labwc/compare/0.6.2...0.6.3 -[0.6.2]: https://github.com/labwc/labwc/compare/0.6.1...0.6.2 -[0.6.1]: https://github.com/labwc/labwc/compare/0.6.0...0.6.1 -[0.6.0]: https://github.com/labwc/labwc/compare/0.5.0...0.6.0 -[0.5.3]: https://github.com/labwc/labwc/compare/0.5.2...0.5.3 -[0.5.2]: https://github.com/labwc/labwc/compare/0.5.1...0.5.2 -[0.5.1]: https://github.com/labwc/labwc/compare/0.5.0...0.5.1 -[0.5.0]: https://github.com/labwc/labwc/compare/0.4.0...0.5.0 -[0.4.0]: https://github.com/labwc/labwc/compare/0.3.0...0.4.0 -[0.3.0]: https://github.com/labwc/labwc/compare/0.2.0...0.3.0 -[0.2.0]: https://github.com/labwc/labwc/compare/0.1.0...0.2.0 -[0.1.0]: https://github.com/labwc/labwc/compare/081339e...0.1.0 +[unreleased-commits]: https://github.com/labwc/labwc/compare/0.9.0...HEAD +[0.9.1-commits]: https://github.com/labwc/labwc/compare/0.9.0...0.9.1 +[0.9.0-commits]: https://github.com/labwc/labwc/compare/0.8.4...0.9.0 +[0.8.4-commits]: https://github.com/labwc/labwc/compare/0.8.3...0.8.4 +[0.8.3-commits]: https://github.com/labwc/labwc/compare/0.8.2...0.8.3 +[0.8.2-commits]: https://github.com/labwc/labwc/compare/0.8.1...0.8.2 +[0.8.1-commits]: https://github.com/labwc/labwc/compare/0.8.0...0.8.1 +[0.8.0-commits]: https://github.com/labwc/labwc/compare/0.7.3...0.8.0 +[0.7.4-commits]: https://github.com/labwc/labwc/compare/0.7.3...0.7.4 +[0.7.3-commits]: https://github.com/labwc/labwc/compare/0.7.2...0.7.3 +[0.7.2-commits]: https://github.com/labwc/labwc/compare/0.7.1...0.7.2 +[0.7.1-commits]: https://github.com/labwc/labwc/compare/0.7.0...0.7.1 +[0.7.0-commits]: https://github.com/labwc/labwc/compare/0.6.6...0.7.0 +[0.6.6-commits]: https://github.com/labwc/labwc/compare/0.6.5...0.6.6 +[0.6.5-commits]: https://github.com/labwc/labwc/compare/0.6.4...0.6.5 +[0.6.4-commits]: https://github.com/labwc/labwc/compare/0.6.3...0.6.4 +[0.6.3-commits]: https://github.com/labwc/labwc/compare/0.6.2...0.6.3 +[0.6.2-commits]: https://github.com/labwc/labwc/compare/0.6.1...0.6.2 +[0.6.1-commits]: https://github.com/labwc/labwc/compare/0.6.0...0.6.1 +[0.6.0-commits]: https://github.com/labwc/labwc/compare/0.5.0...0.6.0 +[0.5.3-commits]: https://github.com/labwc/labwc/compare/0.5.2...0.5.3 +[0.5.2-commits]: https://github.com/labwc/labwc/compare/0.5.1...0.5.2 +[0.5.1-commits]: https://github.com/labwc/labwc/compare/0.5.0...0.5.1 +[0.5.0-commits]: https://github.com/labwc/labwc/compare/0.4.0...0.5.0 +[0.4.0-commits]: https://github.com/labwc/labwc/compare/0.3.0...0.4.0 +[0.3.0-commits]: https://github.com/labwc/labwc/compare/0.2.0...0.3.0 +[0.2.0-commits]: https://github.com/labwc/labwc/compare/0.1.0...0.2.0 +[0.1.0-commits]: https://github.com/labwc/labwc/compare/081339e...0.1.0 [#162]: https://github.com/labwc/labwc/pull/162 [#163]: https://github.com/labwc/labwc/pull/163 @@ -2605,12 +2691,14 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16 [#2652]: https://github.com/labwc/labwc/pull/2652 [#2653]: https://github.com/labwc/labwc/pull/2653 [#2657]: https://github.com/labwc/labwc/pull/2657 +[#2667]: https://github.com/labwc/labwc/pull/2667 [#2669]: https://github.com/labwc/labwc/pull/2669 [#2678]: https://github.com/labwc/labwc/pull/2678 [#2686]: https://github.com/labwc/labwc/pull/2686 [#2688]: https://github.com/labwc/labwc/pull/2688 [#2692]: https://github.com/labwc/labwc/pull/2692 [#2693]: https://github.com/labwc/labwc/pull/2693 +[#2699]: https://github.com/labwc/labwc/pull/2699 [#2700]: https://github.com/labwc/labwc/pull/2700 [#2710]: https://github.com/labwc/labwc/pull/2710 [#2713]: https://github.com/labwc/labwc/pull/2713 @@ -2654,14 +2742,31 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16 [#2873]: https://github.com/labwc/labwc/pull/2873 [#2874]: https://github.com/labwc/labwc/pull/2874 [#2877]: https://github.com/labwc/labwc/pull/2877 +[#2883]: https://github.com/labwc/labwc/pull/2883 +[#2885]: https://github.com/labwc/labwc/pull/2885 [#2886]: https://github.com/labwc/labwc/pull/2886 [#2887]: https://github.com/labwc/labwc/pull/2887 [#2891]: https://github.com/labwc/labwc/pull/2891 [#2909]: https://github.com/labwc/labwc/pull/2909 [#2910]: https://github.com/labwc/labwc/pull/2910 [#2914]: https://github.com/labwc/labwc/pull/2914 +[#2933]: https://github.com/labwc/labwc/pull/2933 [#2937]: https://github.com/labwc/labwc/pull/2937 [#2939]: https://github.com/labwc/labwc/pull/2939 +[#2942]: https://github.com/labwc/labwc/pull/2942 [#2943]: https://github.com/labwc/labwc/pull/2943 [#2944]: https://github.com/labwc/labwc/pull/2944 [#2948]: https://github.com/labwc/labwc/pull/2948 +[#2965]: https://github.com/labwc/labwc/pull/2965 +[#2967]: https://github.com/labwc/labwc/pull/2967 +[#2970]: https://github.com/labwc/labwc/pull/2970 +[#2971]: https://github.com/labwc/labwc/pull/2971 +[#2972]: https://github.com/labwc/labwc/pull/2972 +[#2976]: https://github.com/labwc/labwc/pull/2976 +[#2994]: https://github.com/labwc/labwc/pull/2994 +[#2995]: https://github.com/labwc/labwc/pull/2995 +[#2998]: https://github.com/labwc/labwc/pull/2998 +[#3004]: https://github.com/labwc/labwc/pull/3004 +[#3011]: https://github.com/labwc/labwc/pull/3011 +[#3015]: https://github.com/labwc/labwc/pull/3015 +[#3020]: https://github.com/labwc/labwc/pull/3020 diff --git a/scripts/.gitignore b/scripts/.gitignore index ed76aa70..dcd0305c 100644 --- a/scripts/.gitignore +++ b/scripts/.gitignore @@ -1,2 +1,3 @@ *.o find-banned +rip diff --git a/scripts/rip.c b/scripts/rip.c index d7600b95..832b87a2 100644 --- a/scripts/rip.c +++ b/scripts/rip.c @@ -65,7 +65,7 @@ int main(int argc, char **argv) *p = '\0'; } /* Do not process the references at the bottom of NEWS.md */ - if (!strncmp(line, "[0.1.0]", 7)) { + if (!strncmp(line, "[0.1.0-commits]", 15)) { break; } process_line(line); From 02be24bf59188b2b1cb4fa4a1890952f15619e8e Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Tue, 2 Sep 2025 08:32:23 +0900 Subject: [PATCH 136/248] mousebind: support `Border` context This new context includes `Top`...`BRCorner` and makes it easier to modify the mousebinds bound to them. --- docs/labwc-config.5.scd | 1 + docs/rc.xml.all | 37 +------------------------------ include/config/default-bindings.h | 37 +------------------------------ include/ssd.h | 1 + src/config/mousebind.c | 2 ++ src/ssd/ssd.c | 4 ++++ 6 files changed, 10 insertions(+), 72 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index 1dc59a8c..95600d3b 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -747,6 +747,7 @@ extending outward from the snapped edge. - AllDesktops: A button that, by default, toggles omnipresence of a window. - Close: A button that, by default, closses a window. + - Border: The window's border including Top...BRCorner below. - Top: The top edge of the window's border. - Bottom: The bottom edge of the window's border. - Left: The left edge of the window's border. diff --git a/docs/rc.xml.all b/docs/rc.xml.all index 940b2f96..f91985c3 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -375,42 +375,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/include/config/default-bindings.h b/include/config/default-bindings.h index 98c0b206..12ec5c4c 100644 --- a/include/config/default-bindings.h +++ b/include/config/default-bindings.h @@ -143,42 +143,7 @@ static struct mouse_combos { const char *name, *value; } attributes[2]; } mouse_combos[] = { { - .context = "Left", - .button = "Left", - .event = "Drag", - .action = "Resize", - }, { - .context = "Top", - .button = "Left", - .event = "Drag", - .action = "Resize", - }, { - .context = "Bottom", - .button = "Left", - .event = "Drag", - .action = "Resize", - }, { - .context = "Right", - .button = "Left", - .event = "Drag", - .action = "Resize", - }, { - .context = "TLCorner", - .button = "Left", - .event = "Drag", - .action = "Resize", - }, { - .context = "TRCorner", - .button = "Left", - .event = "Drag", - .action = "Resize", - }, { - .context = "BRCorner", - .button = "Left", - .event = "Drag", - .action = "Resize", - }, { - .context = "BLCorner", + .context = "Border", .button = "Left", .event = "Drag", .action = "Resize", diff --git a/include/ssd.h b/include/ssd.h index f3597f31..f1e03843 100644 --- a/include/ssd.h +++ b/include/ssd.h @@ -53,6 +53,7 @@ enum ssd_part_type { LAB_SSD_PART_RIGHT, LAB_SSD_PART_BOTTOM, LAB_SSD_PART_LEFT, + LAB_SSD_PART_BORDER, LAB_SSD_CLIENT, LAB_SSD_FRAME, diff --git a/src/config/mousebind.c b/src/config/mousebind.c index 0f49b6a5..ad7f6712 100644 --- a/src/config/mousebind.c +++ b/src/config/mousebind.c @@ -132,6 +132,8 @@ context_from_str(const char *str) return LAB_SSD_PART_CORNER_BOTTOM_RIGHT; } else if (!strcasecmp(str, "BLCorner")) { return LAB_SSD_PART_CORNER_BOTTOM_LEFT; + } else if (!strcasecmp(str, "Border")) { + return LAB_SSD_PART_BORDER; } else if (!strcasecmp(str, "Top")) { return LAB_SSD_PART_TOP; } else if (!strcasecmp(str, "Right")) { diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index adf7c2cb..d0603d5e 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -328,6 +328,10 @@ ssd_part_contains(enum ssd_part_type whole, enum ssd_part_type candidate) return candidate >= LAB_SSD_BUTTON_CLOSE && candidate <= LAB_SSD_CLIENT; } + if (whole == LAB_SSD_PART_BORDER) { + return candidate >= LAB_SSD_PART_CORNER_TOP_LEFT + && candidate <= LAB_SSD_PART_LEFT; + } if (whole == LAB_SSD_PART_TOP) { return candidate == LAB_SSD_PART_CORNER_TOP_LEFT || candidate == LAB_SSD_PART_CORNER_TOP_RIGHT; From 074b27fd47dd814d869995fee6f83a51a6562a98 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Tue, 2 Sep 2025 17:47:01 +0900 Subject: [PATCH 137/248] scaled-scene-buffer: restructure source files - Rename `scaled_scene_buffer` to `scaled_buffer`. This makes it clear that `scaled_{font,img,icon}_buffers` are implementations of it. - Move the files from `src/common` to `src/scaled-buffer` as `scaled_icon_buffer` heavily depends on `server` and `view` etc. --- include/node.h | 8 +-- .../scaled-buffer.h} | 58 +++++++++---------- .../scaled-font-buffer.h | 6 +- .../scaled-icon-buffer.h | 4 +- .../scaled-img-buffer.h | 4 +- src/common/meson.build | 4 -- src/desktop.c | 2 +- src/menu/menu.c | 10 ++-- src/meson.build | 1 + src/node.c | 8 +-- src/osd/osd-classic.c | 4 +- src/osd/osd-thumbnail.c | 4 +- src/osd/osd.c | 4 +- src/scaled-buffer/meson.build | 6 ++ .../scaled-buffer.c} | 44 +++++++------- .../scaled-font-buffer.c | 20 +++---- .../scaled-icon-buffer.c | 28 ++++----- .../scaled-img-buffer.c | 22 +++---- src/server.c | 4 +- src/ssd/resize-indicator.c | 2 +- src/ssd/ssd-part.c | 4 +- src/ssd/ssd-titlebar.c | 6 +- 22 files changed, 128 insertions(+), 125 deletions(-) rename include/{common/scaled-scene-buffer.h => scaled-buffer/scaled-buffer.h} (73%) rename include/{common => scaled-buffer}/scaled-font-buffer.h (96%) rename include/{common => scaled-buffer}/scaled-icon-buffer.h (96%) rename include/{common => scaled-buffer}/scaled-img-buffer.h (98%) create mode 100644 src/scaled-buffer/meson.build rename src/{common/scaled-scene-buffer.c => scaled-buffer/scaled-buffer.c} (83%) rename src/{common => scaled-buffer}/scaled-font-buffer.c (87%) rename src/{common => scaled-buffer}/scaled-icon-buffer.c (92%) rename src/{common => scaled-buffer}/scaled-img-buffer.c (72%) diff --git a/include/node.h b/include/node.h index 1c012392..d28a8517 100644 --- a/include/node.h +++ b/include/node.h @@ -8,7 +8,7 @@ struct lab_layer_surface; struct lab_layer_popup; struct menuitem; struct ssd_part; -struct scaled_scene_buffer; +struct scaled_buffer; enum node_descriptor_type { LAB_NODE_DESC_NODE = 0, @@ -20,7 +20,7 @@ enum node_descriptor_type { LAB_NODE_DESC_IME_POPUP, LAB_NODE_DESC_MENUITEM, LAB_NODE_DESC_TREE, - LAB_NODE_DESC_SCALED_SCENE_BUFFER, + LAB_NODE_DESC_SCALED_BUFFER, LAB_NODE_DESC_SSD_PART, }; @@ -84,10 +84,10 @@ struct ssd_part *node_ssd_part_from_node( struct wlr_scene_node *wlr_scene_node); /** - * node_scaled_scene_buffer_from_node - return scaled_scene_buffer from node + * node_scaled_buffer_from_node - return scaled_buffer from node * @wlr_scene_node: wlr_scene_node from which to return data */ -struct scaled_scene_buffer *node_scaled_scene_buffer_from_node( +struct scaled_buffer *node_scaled_buffer_from_node( struct wlr_scene_node *wlr_scene_node); #endif /* LABWC_NODE_DESCRIPTOR_H */ diff --git a/include/common/scaled-scene-buffer.h b/include/scaled-buffer/scaled-buffer.h similarity index 73% rename from include/common/scaled-scene-buffer.h rename to include/scaled-buffer/scaled-buffer.h index 657b9b57..c2af6054 100644 --- a/include/common/scaled-scene-buffer.h +++ b/include/scaled-buffer/scaled-buffer.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef LABWC_SCALED_SCENE_BUFFER_H -#define LABWC_SCALED_SCENE_BUFFER_H +#ifndef LABWC_SCALED_BUFFER_H +#define LABWC_SCALED_BUFFER_H #include @@ -9,20 +9,20 @@ struct wlr_buffer; struct wlr_scene_tree; struct lab_data_buffer; -struct scaled_scene_buffer; +struct scaled_buffer; -struct scaled_scene_buffer_impl { +struct scaled_buffer_impl { /* Return a new buffer optimized for the new scale */ struct lab_data_buffer *(*create_buffer) - (struct scaled_scene_buffer *scaled_buffer, double scale); + (struct scaled_buffer *scaled_buffer, double scale); /* Might be NULL or used for cleaning up */ - void (*destroy)(struct scaled_scene_buffer *scaled_buffer); + void (*destroy)(struct scaled_buffer *scaled_buffer); /* Returns true if the two buffers are visually the same */ - bool (*equal)(struct scaled_scene_buffer *scaled_buffer_a, - struct scaled_scene_buffer *scaled_buffer_b); + bool (*equal)(struct scaled_buffer *scaled_buffer_a, + struct scaled_buffer *scaled_buffer_b); }; -struct scaled_scene_buffer { +struct scaled_buffer { struct wlr_scene_buffer *scene_buffer; int width; /* unscaled, read only */ int height; /* unscaled, read only */ @@ -35,7 +35,7 @@ struct scaled_scene_buffer { struct wl_list cache; /* struct scaled_buffer_cache_entry.link */ struct wl_listener destroy; struct wl_listener outputs_update; - const struct scaled_scene_buffer_impl *impl; + const struct scaled_buffer_impl *impl; struct wl_list link; /* all_scaled_buffers */ }; @@ -78,22 +78,22 @@ struct scaled_scene_buffer { * to handle the majority of use cases where a view is moved between no more * than two different scales. * - * scaled_scene_buffer will clean up automatically once the internal + * scaled_buffer will clean up automatically once the internal * wlr_scene_buffer is being destroyed. If implementation->destroy is set * it will also get called so a consumer of this API may clean up its own * allocations. * - * Besides caching buffers for each scale per scaled_scene_buffer, we also - * store all the scaled_scene_buffers from all the implementers in a list + * Besides caching buffers for each scale per scaled_buffer, we also + * store all the scaled_buffers from all the implementers in a list * in order to reuse backing buffers for visually duplicated - * scaled_scene_buffers found via impl->equal(). + * scaled_buffers found via impl->equal(). * * All requested lab_data_buffers via impl->create_buffer() will be locked * during the lifetime of the buffer in the internal cache and unlocked * when being evacuated from the cache (due to LAB_SCALED_BUFFER_MAX_CACHE * or the internal wlr_scene_buffer being destroyed). * - * If drop_buffer was set during creation of the scaled_scene_buffer, the + * If drop_buffer was set during creation of the scaled_buffer, the * backing wlr_buffer behind a lab_data_buffer will also get dropped * (via wlr_buffer_drop). If there are no more locks (consumers) of the * respective buffer this will then cause the lab_data_buffer to be free'd. @@ -103,21 +103,21 @@ struct scaled_scene_buffer { * destroyed until the buffer is evacuated from the internal cache and thus * unlocked. * - * This allows using scaled_scene_buffer for an autoscaling font_buffer + * This allows using scaled_buffer for an autoscaling font_buffer * (which gets free'd automatically) and also for theme components like * rounded corner images or button icons whose buffers only exist once but - * are references by multiple windows with their own scaled_scene_buffers. + * are references by multiple windows with their own scaled_buffers. * * The rough idea is: use drop_buffer = true for one-shot buffers and false - * for buffers that should outlive the scaled_scene_buffer instance itself. + * for buffers that should outlive the scaled_buffer instance itself. */ -struct scaled_scene_buffer *scaled_scene_buffer_create( +struct scaled_buffer *scaled_buffer_create( struct wlr_scene_tree *parent, - const struct scaled_scene_buffer_impl *implementation, + const struct scaled_buffer_impl *implementation, bool drop_buffer); /** - * scaled_scene_buffer_request_update - mark the buffer that needs to be + * scaled_buffer_request_update - mark the buffer that needs to be * updated * @width: the width of the buffer to be rendered, in scene coordinates * @height: the height of the buffer to be rendered, in scene coordinates @@ -125,22 +125,22 @@ struct scaled_scene_buffer *scaled_scene_buffer_create( * This function should be called when the states bound to the buffer are * updated and ready for rendering. */ -void scaled_scene_buffer_request_update(struct scaled_scene_buffer *self, +void scaled_buffer_request_update(struct scaled_buffer *self, int width, int height); /** - * scaled_scene_buffer_invalidate_sharing - clear the list of entire cached - * scaled_scene_buffers used to share visually dupliated buffers. This should + * scaled_buffer_invalidate_sharing - clear the list of entire cached + * scaled_buffers used to share visually dupliated buffers. This should * be called on Reconfigure to force updates of newly created - * scaled_scene_buffers rather than reusing ones created before Reconfigure. + * scaled_buffers rather than reusing ones created before Reconfigure. */ -void scaled_scene_buffer_invalidate_sharing(void); +void scaled_buffer_invalidate_sharing(void); /* Private */ -struct scaled_scene_buffer_cache_entry { - struct wl_list link; /* struct scaled_scene_buffer.cache */ +struct scaled_buffer_cache_entry { + struct wl_list link; /* struct scaled_buffer.cache */ struct wlr_buffer *buffer; double scale; }; -#endif /* LABWC_SCALED_SCENE_BUFFER_H */ +#endif /* LABWC_SCALED_BUFFER_H */ diff --git a/include/common/scaled-font-buffer.h b/include/scaled-buffer/scaled-font-buffer.h similarity index 96% rename from include/common/scaled-font-buffer.h rename to include/scaled-buffer/scaled-font-buffer.h index ddccbc82..6953f9da 100644 --- a/include/common/scaled-font-buffer.h +++ b/include/scaled-buffer/scaled-font-buffer.h @@ -6,7 +6,7 @@ struct wlr_scene_tree; struct wlr_scene_buffer; -struct scaled_scene_buffer; +struct scaled_buffer; struct scaled_font_buffer { struct wlr_scene_buffer *scene_buffer; @@ -19,7 +19,7 @@ struct scaled_font_buffer { float color[4]; float bg_color[4]; struct font font; - struct scaled_scene_buffer *scaled_buffer; + struct scaled_buffer *scaled_buffer; /* * The following fields are used only for the titlebar, where @@ -34,7 +34,7 @@ struct scaled_font_buffer { /** * Create an auto scaling font buffer, providing a wlr_scene_buffer node for - * display. It gets destroyed automatically when the backing scaled_scene_buffer + * display. It gets destroyed automatically when the backing scaled_buffer * is being destroyed which in turn happens automatically when the backing * wlr_scene_buffer (or one of its parents) is being destroyed. * diff --git a/include/common/scaled-icon-buffer.h b/include/scaled-buffer/scaled-icon-buffer.h similarity index 96% rename from include/common/scaled-icon-buffer.h rename to include/scaled-buffer/scaled-icon-buffer.h index 46e71ebf..b6830f3e 100644 --- a/include/common/scaled-icon-buffer.h +++ b/include/scaled-buffer/scaled-icon-buffer.h @@ -10,7 +10,7 @@ struct wlr_scene_node; struct wlr_scene_buffer; struct scaled_icon_buffer { - struct scaled_scene_buffer *scaled_buffer; + struct scaled_buffer *scaled_buffer; struct wlr_scene_buffer *scene_buffer; struct server *server; /* for window icon */ @@ -34,7 +34,7 @@ struct scaled_icon_buffer { /* * Create an auto scaling icon buffer, providing a wlr_scene_buffer node for - * display. It gets destroyed automatically when the backing scaled_scene_buffer + * display. It gets destroyed automatically when the backing scaled_buffer * is being destroyed which in turn happens automatically when the backing * wlr_scene_buffer (or one of its parents) is being destroyed. */ diff --git a/include/common/scaled-img-buffer.h b/include/scaled-buffer/scaled-img-buffer.h similarity index 98% rename from include/common/scaled-img-buffer.h rename to include/scaled-buffer/scaled-img-buffer.h index 938baeb2..48cbd9c4 100644 --- a/include/common/scaled-img-buffer.h +++ b/include/scaled-buffer/scaled-img-buffer.h @@ -10,7 +10,7 @@ struct wlr_scene_buffer; struct lab_img; struct scaled_img_buffer { - struct scaled_scene_buffer *scaled_buffer; + struct scaled_buffer *scaled_buffer; struct wlr_scene_buffer *scene_buffer; struct lab_img *img; int width; @@ -56,7 +56,7 @@ struct scaled_img_buffer { /* * Create an auto scaling image buffer, providing a wlr_scene_buffer node for - * display. It gets destroyed automatically when the backing scaled_scene_buffer + * display. It gets destroyed automatically when the backing scaled_buffer * is being destroyed which in turn happens automatically when the backing * wlr_scene_buffer (or one of its parents) is being destroyed. * diff --git a/src/common/meson.build b/src/common/meson.build index 306a2ba7..eddc6710 100644 --- a/src/common/meson.build +++ b/src/common/meson.build @@ -14,10 +14,6 @@ labwc_sources += files( 'nodename.c', 'parse-bool.c', 'parse-double.c', - 'scaled-font-buffer.c', - 'scaled-icon-buffer.c', - 'scaled-img-buffer.c', - 'scaled-scene-buffer.c', 'scene-helpers.c', 'set.c', 'surface-helpers.c', diff --git a/src/desktop.c b/src/desktop.c index 9eabab93..42b6f436 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -350,7 +350,7 @@ get_cursor_context(struct server *server) return ret; case LAB_NODE_DESC_NODE: case LAB_NODE_DESC_TREE: - case LAB_NODE_DESC_SCALED_SCENE_BUFFER: + case LAB_NODE_DESC_SCALED_BUFFER: break; } } diff --git a/src/menu/menu.c b/src/menu/menu.c index 4e483989..84db869c 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -20,19 +20,19 @@ #include "common/lab-scene-rect.h" #include "common/list.h" #include "common/mem.h" -#include "common/scaled-font-buffer.h" -#include "common/scaled-icon-buffer.h" #include "common/scene-helpers.h" #include "common/spawn.h" #include "common/string-helpers.h" #include "common/xml.h" #include "config/rcxml.h" #include "labwc.h" -#include "output.h" -#include "workspaces.h" -#include "view.h" #include "node.h" +#include "output.h" +#include "scaled-buffer/scaled-font-buffer.h" +#include "scaled-buffer/scaled-icon-buffer.h" #include "theme.h" +#include "view.h" +#include "workspaces.h" #define PIPEMENU_MAX_BUF_SIZE 1048576 /* 1 MiB */ #define PIPEMENU_TIMEOUT_IN_MS 4000 /* 4 seconds */ diff --git a/src/meson.build b/src/meson.build index 24338994..330b5daf 100644 --- a/src/meson.build +++ b/src/meson.build @@ -56,4 +56,5 @@ subdir('input') subdir('menu') subdir('osd') subdir('protocols') +subdir('scaled-buffer') subdir('ssd') diff --git a/src/node.c b/src/node.c index 37c63a7f..7a17c1ed 100644 --- a/src/node.c +++ b/src/node.c @@ -80,11 +80,11 @@ node_ssd_part_from_node(struct wlr_scene_node *wlr_scene_node) return (struct ssd_part *)node_descriptor->data; } -struct scaled_scene_buffer * -node_scaled_scene_buffer_from_node(struct wlr_scene_node *wlr_scene_node) +struct scaled_buffer * +node_scaled_buffer_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_SCALED_SCENE_BUFFER); - return (struct scaled_scene_buffer *)node_descriptor->data; + assert(node_descriptor->type == LAB_NODE_DESC_SCALED_BUFFER); + return (struct scaled_buffer *)node_descriptor->data; } diff --git a/src/osd/osd-classic.c b/src/osd/osd-classic.c index 6ec165c5..501a86a2 100644 --- a/src/osd/osd-classic.c +++ b/src/osd/osd-classic.c @@ -7,8 +7,6 @@ #include "common/buf.h" #include "common/font.h" #include "common/lab-scene-rect.h" -#include "common/scaled-font-buffer.h" -#include "common/scaled-icon-buffer.h" #include "common/scene-helpers.h" #include "common/string-helpers.h" #include "config/rcxml.h" @@ -16,6 +14,8 @@ #include "node.h" #include "osd.h" #include "output.h" +#include "scaled-buffer/scaled-font-buffer.h" +#include "scaled-buffer/scaled-icon-buffer.h" #include "theme.h" #include "view.h" #include "window-rules.h" diff --git a/src/osd/osd-thumbnail.c b/src/osd/osd-thumbnail.c index 3ede04d6..9804d1ec 100644 --- a/src/osd/osd-thumbnail.c +++ b/src/osd/osd-thumbnail.c @@ -8,11 +8,11 @@ #include "common/array.h" #include "common/box.h" #include "common/lab-scene-rect.h" -#include "common/scaled-font-buffer.h" -#include "common/scaled-icon-buffer.h" #include "labwc.h" #include "osd.h" #include "output.h" +#include "scaled-buffer/scaled-font-buffer.h" +#include "scaled-buffer/scaled-icon-buffer.h" #include "theme.h" #include "view.h" diff --git a/src/osd/osd.c b/src/osd/osd.c index 28714d72..4103444e 100644 --- a/src/osd/osd.c +++ b/src/osd/osd.c @@ -6,13 +6,13 @@ #include #include "common/array.h" #include "common/lab-scene-rect.h" -#include "common/scaled-font-buffer.h" -#include "common/scaled-icon-buffer.h" #include "common/scene-helpers.h" #include "config/rcxml.h" #include "labwc.h" #include "node.h" #include "output.h" +#include "scaled-buffer/scaled-font-buffer.h" +#include "scaled-buffer/scaled-icon-buffer.h" #include "theme.h" #include "view.h" #include "window-rules.h" diff --git a/src/scaled-buffer/meson.build b/src/scaled-buffer/meson.build new file mode 100644 index 00000000..7bd8fa8b --- /dev/null +++ b/src/scaled-buffer/meson.build @@ -0,0 +1,6 @@ +labwc_sources += files( + 'scaled-font-buffer.c', + 'scaled-icon-buffer.c', + 'scaled-img-buffer.c', + 'scaled-buffer.c', +) diff --git a/src/common/scaled-scene-buffer.c b/src/scaled-buffer/scaled-buffer.c similarity index 83% rename from src/common/scaled-scene-buffer.c rename to src/scaled-buffer/scaled-buffer.c index a86077f5..c478ed00 100644 --- a/src/common/scaled-scene-buffer.c +++ b/src/scaled-buffer/scaled-buffer.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "common/scaled-scene-buffer.h" +#include "scaled-buffer/scaled-buffer.h" #include #include #include @@ -15,14 +15,14 @@ #include "node.h" /* - * This holds all the scaled_scene_buffers from all the implementers. + * This holds all the scaled_buffers from all the implementers. * This is used to share visually duplicated buffers found via impl->equal(). */ static struct wl_list all_scaled_buffers = WL_LIST_INIT(&all_scaled_buffers); /* Internal API */ static void -_cache_entry_destroy(struct scaled_scene_buffer_cache_entry *cache_entry, bool drop_buffer) +_cache_entry_destroy(struct scaled_buffer_cache_entry *cache_entry, bool drop_buffer) { wl_list_remove(&cache_entry->link); if (cache_entry->buffer) { @@ -35,10 +35,10 @@ _cache_entry_destroy(struct scaled_scene_buffer_cache_entry *cache_entry, bool d free(cache_entry); } -static struct scaled_scene_buffer_cache_entry * -find_cache_for_scale(struct scaled_scene_buffer *scene_buffer, double scale) +static struct scaled_buffer_cache_entry * +find_cache_for_scale(struct scaled_buffer *scene_buffer, double scale) { - struct scaled_scene_buffer_cache_entry *cache_entry; + struct scaled_buffer_cache_entry *cache_entry; wl_list_for_each(cache_entry, &scene_buffer->cache, link) { if (cache_entry->scale == scale) { return cache_entry; @@ -48,12 +48,12 @@ find_cache_for_scale(struct scaled_scene_buffer *scene_buffer, double scale) } static void -_update_buffer(struct scaled_scene_buffer *self, double scale) +_update_buffer(struct scaled_buffer *self, double scale) { self->active_scale = scale; /* Search for cached buffer of specified scale */ - struct scaled_scene_buffer_cache_entry *cache_entry = + struct scaled_buffer_cache_entry *cache_entry = find_cache_for_scale(self, scale); if (cache_entry) { /* LRU cache, recently used in front */ @@ -71,8 +71,8 @@ _update_buffer(struct scaled_scene_buffer *self, double scale) struct wlr_buffer *wlr_buffer = NULL; if (self->impl->equal) { - /* Search from other cached scaled-scene-buffers */ - struct scaled_scene_buffer *scene_buffer; + /* Search from other cached scaled-buffers */ + struct scaled_buffer *scene_buffer; wl_list_for_each(scene_buffer, &all_scaled_buffers, link) { if (scene_buffer == self) { continue; @@ -146,8 +146,8 @@ _update_buffer(struct scaled_scene_buffer *self, double scale) static void _handle_node_destroy(struct wl_listener *listener, void *data) { - struct scaled_scene_buffer_cache_entry *cache_entry, *cache_entry_tmp; - struct scaled_scene_buffer *self = wl_container_of(listener, self, destroy); + struct scaled_buffer_cache_entry *cache_entry, *cache_entry_tmp; + struct scaled_buffer *self = wl_container_of(listener, self, destroy); wl_list_remove(&self->destroy.link); wl_list_remove(&self->outputs_update.link); @@ -167,7 +167,7 @@ _handle_node_destroy(struct wl_listener *listener, void *data) static void _handle_outputs_update(struct wl_listener *listener, void *data) { - struct scaled_scene_buffer *self = + struct scaled_buffer *self = wl_container_of(listener, self, outputs_update); double max_scale = 0; @@ -181,16 +181,16 @@ _handle_outputs_update(struct wl_listener *listener, void *data) } /* Public API */ -struct scaled_scene_buffer * -scaled_scene_buffer_create(struct wlr_scene_tree *parent, - const struct scaled_scene_buffer_impl *impl, +struct scaled_buffer * +scaled_buffer_create(struct wlr_scene_tree *parent, + const struct scaled_buffer_impl *impl, bool drop_buffer) { assert(parent); assert(impl); assert(impl->create_buffer); - struct scaled_scene_buffer *self = znew(*self); + struct scaled_buffer *self = znew(*self); self->scene_buffer = wlr_scene_buffer_create(parent, NULL); if (!self->scene_buffer) { wlr_log(WLR_ERROR, "Failed to create scene buffer"); @@ -198,7 +198,7 @@ scaled_scene_buffer_create(struct wlr_scene_tree *parent, return NULL; } node_descriptor_create(&self->scene_buffer->node, - LAB_NODE_DESC_SCALED_SCENE_BUFFER, self); + LAB_NODE_DESC_SCALED_BUFFER, self); self->impl = impl; /* @@ -223,14 +223,14 @@ scaled_scene_buffer_create(struct wlr_scene_tree *parent, } void -scaled_scene_buffer_request_update(struct scaled_scene_buffer *self, +scaled_buffer_request_update(struct scaled_buffer *self, int width, int height) { assert(self); assert(width >= 0); assert(height >= 0); - struct scaled_scene_buffer_cache_entry *cache_entry, *cache_entry_tmp; + struct scaled_buffer_cache_entry *cache_entry, *cache_entry_tmp; wl_list_for_each_safe(cache_entry, cache_entry_tmp, &self->cache, link) { _cache_entry_destroy(cache_entry, self->drop_buffer); } @@ -256,9 +256,9 @@ scaled_scene_buffer_request_update(struct scaled_scene_buffer *self, } void -scaled_scene_buffer_invalidate_sharing(void) +scaled_buffer_invalidate_sharing(void) { - struct scaled_scene_buffer *scene_buffer, *tmp; + struct scaled_buffer *scene_buffer, *tmp; wl_list_for_each_safe(scene_buffer, tmp, &all_scaled_buffers, link) { wl_list_remove(&scene_buffer->link); wl_list_init(&scene_buffer->link); diff --git a/src/common/scaled-font-buffer.c b/src/scaled-buffer/scaled-font-buffer.c similarity index 87% rename from src/common/scaled-font-buffer.c rename to src/scaled-buffer/scaled-font-buffer.c index 8faa9495..7b252481 100644 --- a/src/common/scaled-font-buffer.c +++ b/src/scaled-buffer/scaled-font-buffer.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "common/scaled-font-buffer.h" +#include "scaled-buffer/scaled-font-buffer.h" #include #include #include @@ -10,11 +10,11 @@ #include "common/font.h" #include "common/graphic-helpers.h" #include "common/mem.h" -#include "common/scaled-scene-buffer.h" #include "common/string-helpers.h" +#include "scaled-buffer/scaled-buffer.h" static struct lab_data_buffer * -_create_buffer(struct scaled_scene_buffer *scaled_buffer, double scale) +_create_buffer(struct scaled_buffer *scaled_buffer, double scale) { struct lab_data_buffer *buffer = NULL; struct scaled_font_buffer *self = scaled_buffer->data; @@ -39,7 +39,7 @@ _create_buffer(struct scaled_scene_buffer *scaled_buffer, double scale) } static void -_destroy(struct scaled_scene_buffer *scaled_buffer) +_destroy(struct scaled_buffer *scaled_buffer) { struct scaled_font_buffer *self = scaled_buffer->data; scaled_buffer->data = NULL; @@ -51,8 +51,8 @@ _destroy(struct scaled_scene_buffer *scaled_buffer) } static bool -_equal(struct scaled_scene_buffer *scaled_buffer_a, - struct scaled_scene_buffer *scaled_buffer_b) +_equal(struct scaled_buffer *scaled_buffer_a, + struct scaled_buffer *scaled_buffer_b) { struct scaled_font_buffer *a = scaled_buffer_a->data; struct scaled_font_buffer *b = scaled_buffer_b->data; @@ -69,7 +69,7 @@ _equal(struct scaled_scene_buffer *scaled_buffer_a, && a->bg_pattern == b->bg_pattern; } -static const struct scaled_scene_buffer_impl impl = { +static const struct scaled_buffer_impl impl = { .create_buffer = _create_buffer, .destroy = _destroy, .equal = _equal, @@ -81,7 +81,7 @@ scaled_font_buffer_create(struct wlr_scene_tree *parent) { assert(parent); struct scaled_font_buffer *self = znew(*self); - struct scaled_scene_buffer *scaled_buffer = scaled_scene_buffer_create( + struct scaled_buffer *scaled_buffer = scaled_buffer_create( parent, &impl, /* drop_buffer */ true); if (!scaled_buffer) { free(self); @@ -138,7 +138,7 @@ scaled_font_buffer_update(struct scaled_font_buffer *self, const char *text, &self->width, &computed_height); self->height = (self->fixed_height > 0) ? self->fixed_height : computed_height; - scaled_scene_buffer_request_update(self->scaled_buffer, + scaled_buffer_request_update(self->scaled_buffer, self->width, self->height); } @@ -152,6 +152,6 @@ scaled_font_buffer_set_max_width(struct scaled_font_buffer *self, int max_width) &self->width, &computed_height); self->height = (self->fixed_height > 0) ? self->fixed_height : computed_height; - scaled_scene_buffer_request_update(self->scaled_buffer, + scaled_buffer_request_update(self->scaled_buffer, self->width, self->height); } diff --git a/src/common/scaled-icon-buffer.c b/src/scaled-buffer/scaled-icon-buffer.c similarity index 92% rename from src/common/scaled-icon-buffer.c rename to src/scaled-buffer/scaled-icon-buffer.c index 5dd619f1..b8f08efc 100644 --- a/src/common/scaled-icon-buffer.c +++ b/src/scaled-buffer/scaled-icon-buffer.c @@ -1,19 +1,19 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "common/scaled-icon-buffer.h" +#include "scaled-buffer/scaled-icon-buffer.h" #include #include #include #include "buffer.h" #include "common/macros.h" #include "common/mem.h" -#include "common/scaled-scene-buffer.h" #include "common/string-helpers.h" #include "config.h" #include "config/rcxml.h" #include "desktop-entry.h" #include "img/img.h" #include "node.h" +#include "scaled-buffer/scaled-buffer.h" #include "view.h" #include "window-rules.h" @@ -97,7 +97,7 @@ load_server_icon(struct scaled_icon_buffer *self, int icon_size, double scale) #endif /* HAVE_LIBSFDO */ static struct lab_data_buffer * -_create_buffer(struct scaled_scene_buffer *scaled_buffer, double scale) +_create_buffer(struct scaled_buffer *scaled_buffer, double scale) { #if HAVE_LIBSFDO struct scaled_icon_buffer *self = scaled_buffer->data; @@ -168,7 +168,7 @@ set_icon_buffers(struct scaled_icon_buffer *self, struct wl_array *buffers) } static void -_destroy(struct scaled_scene_buffer *scaled_buffer) +_destroy(struct scaled_buffer *scaled_buffer) { struct scaled_icon_buffer *self = scaled_buffer->data; if (self->view) { @@ -194,8 +194,8 @@ icon_buffers_equal(struct wl_array *a, struct wl_array *b) } static bool -_equal(struct scaled_scene_buffer *scaled_buffer_a, - struct scaled_scene_buffer *scaled_buffer_b) +_equal(struct scaled_buffer *scaled_buffer_a, + struct scaled_buffer *scaled_buffer_b) { struct scaled_icon_buffer *a = scaled_buffer_a->data; struct scaled_icon_buffer *b = scaled_buffer_b->data; @@ -209,7 +209,7 @@ _equal(struct scaled_scene_buffer *scaled_buffer_a, && a->height == b->height; } -static struct scaled_scene_buffer_impl impl = { +static struct scaled_buffer_impl impl = { .create_buffer = _create_buffer, .destroy = _destroy, .equal = _equal, @@ -222,7 +222,7 @@ scaled_icon_buffer_create(struct wlr_scene_tree *parent, struct server *server, assert(parent); assert(width >= 0 && height >= 0); - struct scaled_scene_buffer *scaled_buffer = scaled_scene_buffer_create( + struct scaled_buffer *scaled_buffer = scaled_buffer_create( parent, &impl, /* drop_buffer */ true); struct scaled_icon_buffer *self = znew(*self); self->scaled_buffer = scaled_buffer; @@ -256,7 +256,7 @@ handle_view_set_icon(struct wl_listener *listener, void *data) } set_icon_buffers(self, &self->view->icon.buffers); - scaled_scene_buffer_request_update(self->scaled_buffer, + scaled_buffer_request_update(self->scaled_buffer, self->width, self->height); } @@ -272,7 +272,7 @@ handle_view_new_title(struct wl_listener *listener, void *data) return; } self->view_icon_prefer_client = prefer_client; - scaled_scene_buffer_request_update(self->scaled_buffer, + scaled_buffer_request_update(self->scaled_buffer, self->width, self->height); } @@ -290,7 +290,7 @@ handle_view_new_app_id(struct wl_listener *listener, void *data) xstrdup_replace(self->view_app_id, app_id); self->view_icon_prefer_client = window_rules_get_property( self->view, "iconPreferClient") == LAB_PROP_TRUE; - scaled_scene_buffer_request_update(self->scaled_buffer, + scaled_buffer_request_update(self->scaled_buffer, self->width, self->height); } @@ -348,14 +348,14 @@ scaled_icon_buffer_set_icon_name(struct scaled_icon_buffer *self, return; } xstrdup_replace(self->icon_name, icon_name); - scaled_scene_buffer_request_update(self->scaled_buffer, self->width, self->height); + scaled_buffer_request_update(self->scaled_buffer, self->width, self->height); } struct scaled_icon_buffer * scaled_icon_buffer_from_node(struct wlr_scene_node *node) { - struct scaled_scene_buffer *scaled_buffer = - node_scaled_scene_buffer_from_node(node); + struct scaled_buffer *scaled_buffer = + node_scaled_buffer_from_node(node); assert(scaled_buffer->impl == &impl); return scaled_buffer->data; } diff --git a/src/common/scaled-img-buffer.c b/src/scaled-buffer/scaled-img-buffer.c similarity index 72% rename from src/common/scaled-img-buffer.c rename to src/scaled-buffer/scaled-img-buffer.c index c66ada7c..488c861c 100644 --- a/src/common/scaled-img-buffer.c +++ b/src/scaled-buffer/scaled-img-buffer.c @@ -1,17 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L -#include "common/scaled-img-buffer.h" +#include "scaled-buffer/scaled-img-buffer.h" #include #include #include #include "buffer.h" #include "common/mem.h" -#include "common/scaled-scene-buffer.h" #include "img/img.h" #include "node.h" +#include "scaled-buffer/scaled-buffer.h" static struct lab_data_buffer * -_create_buffer(struct scaled_scene_buffer *scaled_buffer, double scale) +_create_buffer(struct scaled_buffer *scaled_buffer, double scale) { struct scaled_img_buffer *self = scaled_buffer->data; struct lab_data_buffer *buffer = lab_img_render(self->img, @@ -20,7 +20,7 @@ _create_buffer(struct scaled_scene_buffer *scaled_buffer, double scale) } static void -_destroy(struct scaled_scene_buffer *scaled_buffer) +_destroy(struct scaled_buffer *scaled_buffer) { struct scaled_img_buffer *self = scaled_buffer->data; lab_img_destroy(self->img); @@ -28,8 +28,8 @@ _destroy(struct scaled_scene_buffer *scaled_buffer) } static bool -_equal(struct scaled_scene_buffer *scaled_buffer_a, - struct scaled_scene_buffer *scaled_buffer_b) +_equal(struct scaled_buffer *scaled_buffer_a, + struct scaled_buffer *scaled_buffer_b) { struct scaled_img_buffer *a = scaled_buffer_a->data; struct scaled_img_buffer *b = scaled_buffer_b->data; @@ -39,7 +39,7 @@ _equal(struct scaled_scene_buffer *scaled_buffer_a, && a->height == b->height; } -static struct scaled_scene_buffer_impl impl = { +static struct scaled_buffer_impl impl = { .create_buffer = _create_buffer, .destroy = _destroy, .equal = _equal, @@ -53,7 +53,7 @@ scaled_img_buffer_create(struct wlr_scene_tree *parent, struct lab_img *img, assert(img); assert(width >= 0 && height >= 0); - struct scaled_scene_buffer *scaled_buffer = scaled_scene_buffer_create( + struct scaled_buffer *scaled_buffer = scaled_buffer_create( parent, &impl, /* drop_buffer */ true); struct scaled_img_buffer *self = znew(*self); self->scaled_buffer = scaled_buffer; @@ -64,7 +64,7 @@ scaled_img_buffer_create(struct wlr_scene_tree *parent, struct lab_img *img, scaled_buffer->data = self; - scaled_scene_buffer_request_update(scaled_buffer, width, height); + scaled_buffer_request_update(scaled_buffer, width, height); return self; } @@ -72,8 +72,8 @@ scaled_img_buffer_create(struct wlr_scene_tree *parent, struct lab_img *img, struct scaled_img_buffer * scaled_img_buffer_from_node(struct wlr_scene_node *node) { - struct scaled_scene_buffer *scaled_buffer = - node_scaled_scene_buffer_from_node(node); + struct scaled_buffer *scaled_buffer = + node_scaled_buffer_from_node(node); assert(scaled_buffer->impl == &impl); return scaled_buffer->data; } diff --git a/src/server.c b/src/server.c index e8befd55..2b48b5ae 100644 --- a/src/server.c +++ b/src/server.c @@ -48,7 +48,6 @@ #include "drm-lease-v1-protocol.h" #include "action.h" #include "common/macros.h" -#include "common/scaled-scene-buffer.h" #include "config/rcxml.h" #include "config/session.h" #include "decorations.h" @@ -64,6 +63,7 @@ #include "output-virtual.h" #include "regions.h" #include "resize-indicator.h" +#include "scaled-buffer/scaled-buffer.h" #include "session-lock.h" #include "theme.h" #include "view.h" @@ -80,7 +80,7 @@ static void reload_config_and_theme(struct server *server) { - scaled_scene_buffer_invalidate_sharing(); + scaled_buffer_invalidate_sharing(); rcxml_finish(); rcxml_read(rc.config_file); theme_finish(server->theme); diff --git a/src/ssd/resize-indicator.c b/src/ssd/resize-indicator.c index cc5731ae..59a66022 100644 --- a/src/ssd/resize-indicator.c +++ b/src/ssd/resize-indicator.c @@ -4,11 +4,11 @@ #include #include #include "common/macros.h" -#include "common/scaled-font-buffer.h" #include "config/rcxml.h" #include "labwc.h" #include "resize-indicator.h" #include "resize-outlines.h" +#include "scaled-buffer/scaled-font-buffer.h" #include "theme.h" #include "view.h" diff --git a/src/ssd/ssd-part.c b/src/ssd/ssd-part.c index 09dbf6a1..d5430660 100644 --- a/src/ssd/ssd-part.c +++ b/src/ssd/ssd-part.c @@ -4,9 +4,9 @@ #include "config/rcxml.h" #include "common/list.h" #include "common/mem.h" -#include "common/scaled-icon-buffer.h" -#include "common/scaled-img-buffer.h" #include "node.h" +#include "scaled-buffer/scaled-icon-buffer.h" +#include "scaled-buffer/scaled-img-buffer.h" #include "ssd-internal.h" /* Internal helpers */ diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index fabcaf7a..13e3ebf6 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -6,9 +6,6 @@ #include #include "buffer.h" #include "common/mem.h" -#include "common/scaled-font-buffer.h" -#include "common/scaled-icon-buffer.h" -#include "common/scaled-img-buffer.h" #include "common/scene-helpers.h" #include "common/string-helpers.h" #include "config/rcxml.h" @@ -16,6 +13,9 @@ #include "img/img.h" #include "labwc.h" #include "node.h" +#include "scaled-buffer/scaled-font-buffer.h" +#include "scaled-buffer/scaled-icon-buffer.h" +#include "scaled-buffer/scaled-img-buffer.h" #include "ssd-internal.h" #include "theme.h" #include "view.h" From f1f8782bb038e9d02a0308a634438bf886e24c10 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Wed, 3 Sep 2025 20:30:22 +0100 Subject: [PATCH 138/248] labnag: s/nag/labnag/ in usage string Fixes #3051 --- clients/labnag.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clients/labnag.c b/clients/labnag.c index e145b556..92eda9cb 100644 --- a/clients/labnag.c +++ b/clients/labnag.c @@ -1398,7 +1398,7 @@ nag_parse_options(int argc, char **argv, struct nag *nag, }; const char *usage = - "Usage: nag [options...]\n" + "Usage: labnag [options...]\n" "\n" " -B, --button [] Create a button with text\n" " -Z, --button-dismiss []\n" From a9f927047a4043bc5177e5762a6fb9e4346f5fea Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Mon, 1 Sep 2025 20:56:51 +0100 Subject: [PATCH 139/248] default-bindings.h: focus & raise on border press ...because it is probably what most people expect and it makes the behavior consistent with that of Openbox. Fixes: #3039 --- docs/rc.xml.all | 4 ++++ include/config/default-bindings.h | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/docs/rc.xml.all b/docs/rc.xml.all index f91985c3..a96993ab 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -376,6 +376,10 @@ + + + + diff --git a/include/config/default-bindings.h b/include/config/default-bindings.h index 12ec5c4c..08caeade 100644 --- a/include/config/default-bindings.h +++ b/include/config/default-bindings.h @@ -143,6 +143,16 @@ static struct mouse_combos { const char *name, *value; } attributes[2]; } mouse_combos[] = { { + .context = "Border", + .button = "Left", + .event = "Press", + .action = "Focus", + }, { + .context = "Border", + .button = "Left", + .event = "Press", + .action = "Raise", + }, { .context = "Border", .button = "Left", .event = "Drag", From 130bbc9e6fef5a8d54e52901150eace5e10c00a5 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Thu, 4 Sep 2025 18:44:38 +0900 Subject: [PATCH 140/248] view.c: remove cruft in view_apply_maximized_geometry() --- src/view.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/view.c b/src/view.c index 1bc205e0..92e5db92 100644 --- a/src/view.c +++ b/src/view.c @@ -1302,14 +1302,6 @@ view_apply_maximized_geometry(struct view *view) assert(output_is_usable(output)); struct wlr_box box = output_usable_area_in_layout_coords(output); - if (box.height == output->wlr_output->height - && output->wlr_output->scale != 1) { - box.height /= output->wlr_output->scale; - } - if (box.width == output->wlr_output->width - && output->wlr_output->scale != 1) { - box.width /= output->wlr_output->scale; - } /* * If one axis (horizontal or vertical) is unmaximized, it From 98c77cd6a7457829889e433d0cbe47fc3c340b60 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sun, 7 Sep 2025 03:42:50 +0900 Subject: [PATCH 141/248] ssd-titlebar: don't bind ssd_part to ssd->tree Fixes up f347a81. This mistake didn't cause misbehaviors because the ssd_part bound to ssd->tree is just a placeholder to let get_cursor_context() call ssd_get_resizing_type() when the cursor is on border/extents. --- src/ssd/ssd-titlebar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 13e3ebf6..e2cb2e06 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -33,7 +33,7 @@ ssd_titlebar_create(struct ssd *ssd) int corner_width = ssd_get_corner_width(); ssd->titlebar.tree = wlr_scene_tree_create(ssd->tree); - attach_ssd_part(LAB_SSD_PART_TITLEBAR, view, &ssd->tree->node); + attach_ssd_part(LAB_SSD_PART_TITLEBAR, view, &ssd->titlebar.tree->node); enum ssd_active_state active; FOR_EACH_ACTIVE_STATE(active) { From 502473f343d1f635a23966f1ae5081c3dc89c633 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Sat, 6 Sep 2025 11:43:48 -0400 Subject: [PATCH 142/248] theme: fix spelling of LAB_BS_HOVERED --- include/theme.h | 6 +++--- src/ssd/ssd-titlebar.c | 4 ++-- src/theme.c | 22 +++++++++++----------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/include/theme.h b/include/theme.h index 75f3c8c0..7b0451b4 100644 --- a/include/theme.h +++ b/include/theme.h @@ -41,11 +41,11 @@ struct theme_snapping_overlay { enum lab_button_state { LAB_BS_DEFAULT = 0, - LAB_BS_HOVERD = 1 << 0, + LAB_BS_HOVERED = 1 << 0, LAB_BS_TOGGLED = 1 << 1, LAB_BS_ROUNDED = 1 << 2, - LAB_BS_ALL = LAB_BS_HOVERD | LAB_BS_TOGGLED | LAB_BS_ROUNDED, + LAB_BS_ALL = LAB_BS_HOVERED | LAB_BS_TOGGLED | LAB_BS_ROUNDED, }; struct theme_background { @@ -104,7 +104,7 @@ struct theme { * The texture of a window buttons for each hover/toggled/rounded * state. This can be accessed like: * - * buttons[LAB_SSD_BUTTON_ICONIFY][LAB_BS_HOVERD | LAB_BS_TOGGLED] + * buttons[LAB_SSD_BUTTON_ICONIFY][LAB_BS_HOVERED | LAB_BS_TOGGLED] * * Elements in buttons[0] are all NULL since LAB_SSD_BUTTON_FIRST is 1. */ diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index e2cb2e06..8076d02f 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -510,12 +510,12 @@ ssd_update_button_hover(struct wlr_scene_node *node, /* Disable old hover */ if (hover_state->button) { - update_button_state(hover_state->button, LAB_BS_HOVERD, false); + update_button_state(hover_state->button, LAB_BS_HOVERED, false); hover_state->view = NULL; hover_state->button = NULL; } if (button) { - update_button_state(button, LAB_BS_HOVERD, true); + update_button_state(button, LAB_BS_HOVERED, true); hover_state->view = button->base.view; hover_state->button = button; } diff --git a/src/theme.c b/src/theme.c index 45e42c15..5c323cf6 100644 --- a/src/theme.c +++ b/src/theme.c @@ -217,9 +217,9 @@ load_button(struct theme *theme, struct button *b, int active) * If hover-icons do not exist, add fallbacks by copying the non-hover * variant and then adding an overlay. */ - if (!*img && (b->state_set & LAB_BS_HOVERD)) { + if (!*img && (b->state_set & LAB_BS_HOVERED)) { struct lab_img *non_hover_img = - button_imgs[b->type][b->state_set & ~LAB_BS_HOVERD]; + button_imgs[b->type][b->state_set & ~LAB_BS_HOVERED]; *img = lab_img_copy(non_hover_img); lab_img_add_modifier(*img, draw_hover_overlay_on_button); @@ -335,50 +335,50 @@ load_buttons(struct theme *theme) }, { .name = "menu_hover", .type = LAB_SSD_BUTTON_WINDOW_MENU, - .state_set = LAB_BS_HOVERD, + .state_set = LAB_BS_HOVERED, /* no fallback (non-hover variant is used instead) */ }, { .name = "iconify_hover", .type = LAB_SSD_BUTTON_ICONIFY, - .state_set = LAB_BS_HOVERD, + .state_set = LAB_BS_HOVERED, /* no fallback (non-hover variant is used instead) */ }, { .name = "max_hover", .type = LAB_SSD_BUTTON_MAXIMIZE, - .state_set = LAB_BS_HOVERD, + .state_set = LAB_BS_HOVERED, /* no fallback (non-hover variant is used instead) */ }, { .name = "max_toggled_hover", .alt_name = "max_hover_toggled", .type = LAB_SSD_BUTTON_MAXIMIZE, - .state_set = LAB_BS_TOGGLED | LAB_BS_HOVERD, + .state_set = LAB_BS_TOGGLED | LAB_BS_HOVERED, /* no fallback (non-hover variant is used instead) */ }, { .name = "shade_hover", .type = LAB_SSD_BUTTON_SHADE, - .state_set = LAB_BS_HOVERD, + .state_set = LAB_BS_HOVERED, /* no fallback (non-hover variant is used instead) */ }, { .name = "shade_toggled_hover", .alt_name = "shade_hover_toggled", .type = LAB_SSD_BUTTON_SHADE, - .state_set = LAB_BS_TOGGLED | LAB_BS_HOVERD, + .state_set = LAB_BS_TOGGLED | LAB_BS_HOVERED, /* no fallback (non-hover variant is used instead) */ }, { .name = "desk_hover", /* no fallback (non-hover variant is used instead) */ .type = LAB_SSD_BUTTON_OMNIPRESENT, - .state_set = LAB_BS_HOVERD, + .state_set = LAB_BS_HOVERED, }, { .name = "desk_toggled_hover", .alt_name = "desk_hover_toggled", .type = LAB_SSD_BUTTON_OMNIPRESENT, - .state_set = LAB_BS_TOGGLED | LAB_BS_HOVERD, + .state_set = LAB_BS_TOGGLED | LAB_BS_HOVERED, /* no fallback (non-hover variant is used instead) */ }, { .name = "close_hover", .type = LAB_SSD_BUTTON_CLOSE, - .state_set = LAB_BS_HOVERD, + .state_set = LAB_BS_HOVERED, /* no fallback (non-hover variant is used instead) */ }, }; From 11885348761347408ba20885a36cfed52e143a5d Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Wed, 3 Sep 2025 05:08:52 -0400 Subject: [PATCH 143/248] common: add node-type.c/h (renaming ssd_part_type to lab_node_type) ssd_part_type contains several node types that are not actually part of server-side decorations (ROOT, MENU, OSD, etc.) Rename it accordingly and move it to a common location, along with some related conversion/comparison functions. --- include/common/node-type.h | 63 ++++++++++++++++++ include/config/mousebind.h | 6 +- include/config/rcxml.h | 4 +- include/input/cursor.h | 4 +- include/ssd-internal.h | 8 +-- include/ssd.h | 53 +-------------- include/theme.h | 6 +- src/action.c | 4 +- src/common/meson.build | 1 + src/common/node-type.c | 129 +++++++++++++++++++++++++++++++++++++ src/config/mousebind.c | 59 +---------------- src/config/rcxml.c | 21 +++--- src/desktop.c | 20 +++--- src/edges.c | 5 +- src/input/cursor.c | 44 ++++++------- src/input/tablet.c | 2 +- src/input/touch.c | 2 +- src/osd/osd.c | 1 + src/server.c | 1 + src/snap.c | 3 +- src/ssd/resize-indicator.c | 2 +- src/ssd/ssd-border.c | 1 + src/ssd/ssd-part.c | 14 ++-- src/ssd/ssd-shadow.c | 1 + src/ssd/ssd-titlebar.c | 21 +++--- src/ssd/ssd.c | 102 +++++------------------------ src/theme.c | 82 +++++++++++------------ src/xwayland.c | 1 - 28 files changed, 345 insertions(+), 315 deletions(-) create mode 100644 include/common/node-type.h create mode 100644 src/common/node-type.c diff --git a/include/common/node-type.h b/include/common/node-type.h new file mode 100644 index 00000000..92e7f4b9 --- /dev/null +++ b/include/common/node-type.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_NODE_TYPE_H +#define LABWC_NODE_TYPE_H + +#include "common/edge.h" + +/* + * In labwc, "node type" indicates the role of a wlr_scene_node in the + * overall desktop. It also maps more-or-less to the openbox concept of + * "context" (as used when defining mouse bindings). + * + * Some types (BUTTON, BORDER, etc.) refer to groups or categories of + * other node types rather than individual nodes. These categories are + * defined as ranges (e.g. BUTTON means anything from BUTTON_FIRST to + * BUTTON_LAST), therefore order is significant. + * + * Be sure to keep node_type_contains() in sync with any changes! + */ +enum lab_node_type { + LAB_NODE_NONE = 0, + + LAB_NODE_BUTTON_CLOSE = 1, + LAB_NODE_BUTTON_MAXIMIZE, + LAB_NODE_BUTTON_ICONIFY, + LAB_NODE_BUTTON_WINDOW_ICON, + LAB_NODE_BUTTON_WINDOW_MENU, + LAB_NODE_BUTTON_SHADE, + LAB_NODE_BUTTON_OMNIPRESENT, + LAB_NODE_BUTTON_FIRST = LAB_NODE_BUTTON_CLOSE, + LAB_NODE_BUTTON_LAST = LAB_NODE_BUTTON_OMNIPRESENT, + LAB_NODE_BUTTON, + + LAB_NODE_TITLEBAR, + LAB_NODE_TITLE, + + LAB_NODE_CORNER_TOP_LEFT, + LAB_NODE_CORNER_TOP_RIGHT, + LAB_NODE_CORNER_BOTTOM_RIGHT, + LAB_NODE_CORNER_BOTTOM_LEFT, + LAB_NODE_BORDER_TOP, + LAB_NODE_BORDER_RIGHT, + LAB_NODE_BORDER_BOTTOM, + LAB_NODE_BORDER_LEFT, + LAB_NODE_BORDER, + + LAB_NODE_CLIENT, + LAB_NODE_FRAME, + LAB_NODE_ROOT, + LAB_NODE_MENU, + LAB_NODE_OSD, + LAB_NODE_LAYER_SURFACE, + LAB_NODE_LAYER_SUBSURFACE, + LAB_NODE_UNMANAGED, + LAB_NODE_ALL, +}; + +enum lab_node_type node_type_parse(const char *context); + +bool node_type_contains(enum lab_node_type whole, enum lab_node_type part); + +enum lab_edge node_type_to_edges(enum lab_node_type type); + +#endif /* LABWC_NODE_TYPE_H */ diff --git a/include/config/mousebind.h b/include/config/mousebind.h index 0d78973b..b86ed4e7 100644 --- a/include/config/mousebind.h +++ b/include/config/mousebind.h @@ -2,9 +2,9 @@ #ifndef LABWC_MOUSEBIND_H #define LABWC_MOUSEBIND_H +#include #include -#include "ssd.h" -#include "config/keybind.h" +#include "common/node-type.h" enum mouse_event { MOUSE_ACTION_NONE = 0, @@ -25,7 +25,7 @@ enum direction { }; struct mousebind { - enum ssd_part_type context; + enum lab_node_type context; /* ex: BTN_LEFT, BTN_RIGHT from linux/input_event_codes.h */ uint32_t button; diff --git a/include/config/rcxml.h b/include/config/rcxml.h index 0da38114..42b60f10 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -10,8 +10,8 @@ #include "common/border.h" #include "common/buf.h" #include "common/font.h" +#include "common/node-type.h" #include "config/types.h" -#include "ssd.h" #define BUTTON_MAP_MAX 16 @@ -48,7 +48,7 @@ struct button_map_entry { }; struct title_button { - enum ssd_part_type type; + enum lab_node_type type; struct wl_list link; }; diff --git a/include/input/cursor.h b/include/input/cursor.h index 094bc04f..8dd8a274 100644 --- a/include/input/cursor.h +++ b/include/input/cursor.h @@ -4,7 +4,7 @@ #include #include "common/edge.h" -#include "ssd.h" +#include "common/node-type.h" struct view; struct seat; @@ -32,7 +32,7 @@ struct cursor_context { struct view *view; struct wlr_scene_node *node; struct wlr_surface *surface; - enum ssd_part_type type; + enum lab_node_type type; double sx, sy; }; diff --git a/include/ssd-internal.h b/include/ssd-internal.h index aa20dee3..13e5e422 100644 --- a/include/ssd-internal.h +++ b/include/ssd-internal.h @@ -3,7 +3,7 @@ #define LABWC_SSD_INTERNAL_H #include -#include "ssd.h" +#include "common/border.h" #include "theme.h" #include "view.h" @@ -142,7 +142,7 @@ struct ssd { * the cursor. */ struct ssd_part { - enum ssd_part_type type; + enum lab_node_type type; struct view *view; /* This part represented in scene graph */ @@ -183,10 +183,10 @@ struct wlr_buffer; struct wlr_scene_tree; /* SSD internal helpers to create various SSD elements */ -struct ssd_part *attach_ssd_part(enum ssd_part_type type, struct view *view, +struct ssd_part *attach_ssd_part(enum lab_node_type type, struct view *view, struct wlr_scene_node *node); struct ssd_part_button *attach_ssd_part_button(struct wl_list *button_parts, - enum ssd_part_type type, struct wlr_scene_tree *parent, + enum lab_node_type type, struct wlr_scene_tree *parent, struct lab_img *imgs[LAB_BS_ALL + 1], int x, int y, struct view *view); struct ssd_part_button *button_try_from_ssd_part(struct ssd_part *part); diff --git a/include/ssd.h b/include/ssd.h index f1e03843..4ab404e2 100644 --- a/include/ssd.h +++ b/include/ssd.h @@ -3,7 +3,7 @@ #define LABWC_SSD_H #include "common/border.h" -#include "common/edge.h" +#include "common/node-type.h" #include "config/types.h" enum ssd_active_state { @@ -22,51 +22,6 @@ struct wlr_cursor; */ #define SSD_SHADOW_INSET 0.3 -/* - * Sequence these according to the order they should be processed for - * press and hover events. Bear in mind that some of their respective - * interactive areas overlap, so for example buttons need to come before title. - */ -enum ssd_part_type { - LAB_SSD_NONE = 0, - - LAB_SSD_BUTTON_CLOSE = 1, - LAB_SSD_BUTTON_MAXIMIZE, - LAB_SSD_BUTTON_ICONIFY, - LAB_SSD_BUTTON_WINDOW_ICON, - LAB_SSD_BUTTON_WINDOW_MENU, - LAB_SSD_BUTTON_SHADE, - LAB_SSD_BUTTON_OMNIPRESENT, - /* only for internal use */ - LAB_SSD_BUTTON_FIRST = LAB_SSD_BUTTON_CLOSE, - LAB_SSD_BUTTON_LAST = LAB_SSD_BUTTON_OMNIPRESENT, - LAB_SSD_BUTTON, - - LAB_SSD_PART_TITLEBAR, - LAB_SSD_PART_TITLE, - - LAB_SSD_PART_CORNER_TOP_LEFT, - LAB_SSD_PART_CORNER_TOP_RIGHT, - LAB_SSD_PART_CORNER_BOTTOM_RIGHT, - LAB_SSD_PART_CORNER_BOTTOM_LEFT, - LAB_SSD_PART_TOP, - LAB_SSD_PART_RIGHT, - LAB_SSD_PART_BOTTOM, - LAB_SSD_PART_LEFT, - LAB_SSD_PART_BORDER, - - LAB_SSD_CLIENT, - LAB_SSD_FRAME, - LAB_SSD_ROOT, - LAB_SSD_MENU, - LAB_SSD_OSD, - LAB_SSD_LAYER_SURFACE, - LAB_SSD_LAYER_SUBSURFACE, - LAB_SSD_UNMANAGED, - LAB_SSD_ALL, - LAB_SSD_END_MARKER -}; - /* Forward declare arguments */ struct ssd; struct ssd_hover_state; @@ -101,7 +56,7 @@ struct ssd_hover_state *ssd_hover_state_new(void); void ssd_update_button_hover(struct wlr_scene_node *node, struct ssd_hover_state *hover_state); -enum ssd_part_type ssd_part_get_type(const struct ssd_part *part); +enum lab_node_type ssd_part_get_type(const struct ssd_part *part); struct view *ssd_part_get_view(const struct ssd_part *part); /* Public SSD helpers */ @@ -110,10 +65,8 @@ struct view *ssd_part_get_view(const struct ssd_part *part); * Returns a part type that represents a mouse context like "Top", "Left" and * "TRCorner" when the cursor is on the window border or resizing handle. */ -enum ssd_part_type ssd_get_resizing_type(const struct ssd *ssd, +enum lab_node_type ssd_get_resizing_type(const struct ssd *ssd, struct wlr_cursor *cursor); -enum lab_edge ssd_resize_edges(enum ssd_part_type type); -bool ssd_part_contains(enum ssd_part_type whole, enum ssd_part_type candidate); enum lab_ssd_mode ssd_mode_parse(const char *mode); /* TODO: clean up / update */ diff --git a/include/theme.h b/include/theme.h index 7b0451b4..4e9f4705 100644 --- a/include/theme.h +++ b/include/theme.h @@ -10,7 +10,7 @@ #include #include -#include "ssd.h" +#include "common/node-type.h" struct lab_img; @@ -90,7 +90,7 @@ struct theme { struct theme_background title_bg; /* TODO: add toggled/hover/pressed/disabled colors for buttons */ - float button_colors[LAB_SSD_BUTTON_LAST + 1][4]; + float button_colors[LAB_NODE_BUTTON_LAST + 1][4]; float border_color[4]; float toggled_keybinds_color[4]; @@ -109,7 +109,7 @@ struct theme { * Elements in buttons[0] are all NULL since LAB_SSD_BUTTON_FIRST is 1. */ struct lab_img *button_imgs - [LAB_SSD_BUTTON_LAST + 1][LAB_BS_ALL + 1]; + [LAB_NODE_BUTTON_LAST + 1][LAB_BS_ALL + 1]; /* * The titlebar background is specified as a cairo_pattern diff --git a/src/action.c b/src/action.c index eda9138c..69e4e66b 100644 --- a/src/action.c +++ b/src/action.c @@ -707,8 +707,8 @@ show_menu(struct server *server, struct view *view, struct cursor_context *ctx, x = extent.x; y = view->current.y; /* Push the client menu underneath the button */ - if (is_client_menu && ssd_part_contains( - LAB_SSD_BUTTON, ctx->type)) { + if (is_client_menu && node_type_contains( + LAB_NODE_BUTTON, ctx->type)) { assert(ctx->node); int lx, ly; wlr_scene_node_coords(ctx->node, &lx, &ly); diff --git a/src/common/meson.build b/src/common/meson.build index eddc6710..b1087629 100644 --- a/src/common/meson.build +++ b/src/common/meson.build @@ -12,6 +12,7 @@ labwc_sources += files( 'match.c', 'mem.c', 'nodename.c', + 'node-type.c', 'parse-bool.c', 'parse-double.c', 'scene-helpers.c', diff --git a/src/common/node-type.c b/src/common/node-type.c new file mode 100644 index 00000000..67958dd4 --- /dev/null +++ b/src/common/node-type.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include "common/node-type.h" +#include +#include + +enum lab_node_type +node_type_parse(const char *context) +{ + if (!strcasecmp(context, "Close")) { + return LAB_NODE_BUTTON_CLOSE; + } else if (!strcasecmp(context, "Maximize")) { + return LAB_NODE_BUTTON_MAXIMIZE; + } else if (!strcasecmp(context, "Iconify")) { + return LAB_NODE_BUTTON_ICONIFY; + } else if (!strcasecmp(context, "WindowMenu")) { + return LAB_NODE_BUTTON_WINDOW_MENU; + } else if (!strcasecmp(context, "Icon")) { + return LAB_NODE_BUTTON_WINDOW_ICON; + } else if (!strcasecmp(context, "Shade")) { + return LAB_NODE_BUTTON_SHADE; + } else if (!strcasecmp(context, "AllDesktops")) { + return LAB_NODE_BUTTON_OMNIPRESENT; + } else if (!strcasecmp(context, "Titlebar")) { + return LAB_NODE_TITLEBAR; + } else if (!strcasecmp(context, "Title")) { + return LAB_NODE_TITLE; + } else if (!strcasecmp(context, "TLCorner")) { + return LAB_NODE_CORNER_TOP_LEFT; + } else if (!strcasecmp(context, "TRCorner")) { + return LAB_NODE_CORNER_TOP_RIGHT; + } else if (!strcasecmp(context, "BRCorner")) { + return LAB_NODE_CORNER_BOTTOM_RIGHT; + } else if (!strcasecmp(context, "BLCorner")) { + return LAB_NODE_CORNER_BOTTOM_LEFT; + } else if (!strcasecmp(context, "Border")) { + return LAB_NODE_BORDER; + } else if (!strcasecmp(context, "Top")) { + return LAB_NODE_BORDER_TOP; + } else if (!strcasecmp(context, "Right")) { + return LAB_NODE_BORDER_RIGHT; + } else if (!strcasecmp(context, "Bottom")) { + return LAB_NODE_BORDER_BOTTOM; + } else if (!strcasecmp(context, "Left")) { + return LAB_NODE_BORDER_LEFT; + } else if (!strcasecmp(context, "Frame")) { + return LAB_NODE_FRAME; + } else if (!strcasecmp(context, "Client")) { + return LAB_NODE_CLIENT; + } else if (!strcasecmp(context, "Desktop")) { + return LAB_NODE_ROOT; + } else if (!strcasecmp(context, "Root")) { + return LAB_NODE_ROOT; + } else if (!strcasecmp(context, "All")) { + return LAB_NODE_ALL; + } + wlr_log(WLR_ERROR, "unknown mouse context (%s)", context); + return LAB_NODE_NONE; +} + +bool +node_type_contains(enum lab_node_type whole, enum lab_node_type part) +{ + if (whole == part || whole == LAB_NODE_ALL) { + return true; + } + if (whole == LAB_NODE_BUTTON) { + return part >= LAB_NODE_BUTTON_FIRST + && part <= LAB_NODE_BUTTON_LAST; + } + if (whole == LAB_NODE_TITLEBAR) { + return part >= LAB_NODE_BUTTON_FIRST + && part <= LAB_NODE_TITLE; + } + if (whole == LAB_NODE_TITLE) { + /* "Title" includes blank areas of "Titlebar" as well */ + return part >= LAB_NODE_TITLEBAR + && part <= LAB_NODE_TITLE; + } + if (whole == LAB_NODE_FRAME) { + return part >= LAB_NODE_BUTTON_FIRST + && part <= LAB_NODE_CLIENT; + } + if (whole == LAB_NODE_BORDER) { + return part >= LAB_NODE_CORNER_TOP_LEFT + && part <= LAB_NODE_BORDER_LEFT; + } + if (whole == LAB_NODE_BORDER_TOP) { + return part == LAB_NODE_CORNER_TOP_LEFT + || part == LAB_NODE_CORNER_TOP_RIGHT; + } + if (whole == LAB_NODE_BORDER_RIGHT) { + return part == LAB_NODE_CORNER_TOP_RIGHT + || part == LAB_NODE_CORNER_BOTTOM_RIGHT; + } + if (whole == LAB_NODE_BORDER_BOTTOM) { + return part == LAB_NODE_CORNER_BOTTOM_RIGHT + || part == LAB_NODE_CORNER_BOTTOM_LEFT; + } + if (whole == LAB_NODE_BORDER_LEFT) { + return part == LAB_NODE_CORNER_TOP_LEFT + || part == LAB_NODE_CORNER_BOTTOM_LEFT; + } + return false; +} + +enum lab_edge +node_type_to_edges(enum lab_node_type type) +{ + switch (type) { + case LAB_NODE_BORDER_TOP: + return LAB_EDGE_TOP; + case LAB_NODE_BORDER_RIGHT: + return LAB_EDGE_RIGHT; + case LAB_NODE_BORDER_BOTTOM: + return LAB_EDGE_BOTTOM; + case LAB_NODE_BORDER_LEFT: + return LAB_EDGE_LEFT; + case LAB_NODE_CORNER_TOP_LEFT: + return LAB_EDGES_TOP_LEFT; + case LAB_NODE_CORNER_TOP_RIGHT: + return LAB_EDGES_TOP_RIGHT; + case LAB_NODE_CORNER_BOTTOM_RIGHT: + return LAB_EDGES_BOTTOM_RIGHT; + case LAB_NODE_CORNER_BOTTOM_LEFT: + return LAB_EDGES_BOTTOM_LEFT; + default: + return LAB_EDGE_NONE; + } +} diff --git a/src/config/mousebind.c b/src/config/mousebind.c index ad7f6712..7460dfad 100644 --- a/src/config/mousebind.c +++ b/src/config/mousebind.c @@ -8,6 +8,7 @@ #include #include "common/list.h" #include "common/mem.h" +#include "config/keybind.h" #include "config/rcxml.h" uint32_t @@ -103,60 +104,6 @@ mousebind_event_from_str(const char *str) return MOUSE_ACTION_NONE; } -static enum ssd_part_type -context_from_str(const char *str) -{ - if (!strcasecmp(str, "Close")) { - return LAB_SSD_BUTTON_CLOSE; - } else if (!strcasecmp(str, "Maximize")) { - return LAB_SSD_BUTTON_MAXIMIZE; - } else if (!strcasecmp(str, "Iconify")) { - return LAB_SSD_BUTTON_ICONIFY; - } else if (!strcasecmp(str, "WindowMenu")) { - return LAB_SSD_BUTTON_WINDOW_MENU; - } else if (!strcasecmp(str, "Icon")) { - return LAB_SSD_BUTTON_WINDOW_ICON; - } else if (!strcasecmp(str, "Shade")) { - return LAB_SSD_BUTTON_SHADE; - } else if (!strcasecmp(str, "AllDesktops")) { - return LAB_SSD_BUTTON_OMNIPRESENT; - } else if (!strcasecmp(str, "Titlebar")) { - return LAB_SSD_PART_TITLEBAR; - } else if (!strcasecmp(str, "Title")) { - return LAB_SSD_PART_TITLE; - } else if (!strcasecmp(str, "TLCorner")) { - return LAB_SSD_PART_CORNER_TOP_LEFT; - } else if (!strcasecmp(str, "TRCorner")) { - return LAB_SSD_PART_CORNER_TOP_RIGHT; - } else if (!strcasecmp(str, "BRCorner")) { - return LAB_SSD_PART_CORNER_BOTTOM_RIGHT; - } else if (!strcasecmp(str, "BLCorner")) { - return LAB_SSD_PART_CORNER_BOTTOM_LEFT; - } else if (!strcasecmp(str, "Border")) { - return LAB_SSD_PART_BORDER; - } else if (!strcasecmp(str, "Top")) { - return LAB_SSD_PART_TOP; - } else if (!strcasecmp(str, "Right")) { - return LAB_SSD_PART_RIGHT; - } else if (!strcasecmp(str, "Bottom")) { - return LAB_SSD_PART_BOTTOM; - } else if (!strcasecmp(str, "Left")) { - return LAB_SSD_PART_LEFT; - } else if (!strcasecmp(str, "Frame")) { - return LAB_SSD_FRAME; - } else if (!strcasecmp(str, "Client")) { - return LAB_SSD_CLIENT; - } else if (!strcasecmp(str, "Desktop")) { - return LAB_SSD_ROOT; - } else if (!strcasecmp(str, "Root")) { - return LAB_SSD_ROOT; - } else if (!strcasecmp(str, "All")) { - return LAB_SSD_ALL; - } - wlr_log(WLR_ERROR, "unknown mouse context (%s)", str); - return LAB_SSD_NONE; -} - bool mousebind_the_same(struct mousebind *a, struct mousebind *b) { @@ -176,8 +123,8 @@ mousebind_create(const char *context) return NULL; } struct mousebind *m = znew(*m); - m->context = context_from_str(context); - if (m->context != LAB_SSD_NONE) { + m->context = node_type_parse(context); + if (m->context != LAB_NODE_NONE) { wl_list_append(&rc.mousebinds, &m->link); } wl_list_init(&m->actions); diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 26b56ec6..a2b31922 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -35,6 +35,7 @@ #include "labwc.h" #include "osd.h" #include "regions.h" +#include "ssd.h" #include "view.h" #include "window-rules.h" #include "workspaces.h" @@ -126,34 +127,34 @@ fill_section(const char *content, struct wl_list *list, uint32_t *found_buttons) if (string_null_or_empty(identifier)) { continue; } - enum ssd_part_type type = LAB_SSD_NONE; + enum lab_node_type type = LAB_NODE_NONE; if (!strcmp(identifier, "icon")) { #if HAVE_LIBSFDO - type = LAB_SSD_BUTTON_WINDOW_ICON; + type = LAB_NODE_BUTTON_WINDOW_ICON; #else wlr_log(WLR_ERROR, "libsfdo is not linked. " "Replacing 'icon' in titlebar layout with 'menu'."); - type = LAB_SSD_BUTTON_WINDOW_MENU; + type = LAB_NODE_BUTTON_WINDOW_MENU; #endif } else if (!strcmp(identifier, "menu")) { - type = LAB_SSD_BUTTON_WINDOW_MENU; + type = LAB_NODE_BUTTON_WINDOW_MENU; } else if (!strcmp(identifier, "iconify")) { - type = LAB_SSD_BUTTON_ICONIFY; + type = LAB_NODE_BUTTON_ICONIFY; } else if (!strcmp(identifier, "max")) { - type = LAB_SSD_BUTTON_MAXIMIZE; + type = LAB_NODE_BUTTON_MAXIMIZE; } else if (!strcmp(identifier, "close")) { - type = LAB_SSD_BUTTON_CLOSE; + type = LAB_NODE_BUTTON_CLOSE; } else if (!strcmp(identifier, "shade")) { - type = LAB_SSD_BUTTON_SHADE; + type = LAB_NODE_BUTTON_SHADE; } else if (!strcmp(identifier, "desk")) { - type = LAB_SSD_BUTTON_OMNIPRESENT; + type = LAB_NODE_BUTTON_OMNIPRESENT; } else { wlr_log(WLR_ERROR, "invalid titleLayout identifier '%s'", identifier); continue; } - assert(type != LAB_SSD_NONE); + assert(type != LAB_NODE_NONE); /* We no longer need this check, but let's keep it just in case */ if (*found_buttons & (1 << type)) { diff --git a/src/desktop.c b/src/desktop.c index 42b6f436..f312c765 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -263,7 +263,7 @@ get_surface_from_layer_node(struct wlr_scene_node *node) struct cursor_context get_cursor_context(struct server *server) { - struct cursor_context ret = {.type = LAB_SSD_NONE}; + struct cursor_context ret = {.type = LAB_NODE_NONE}; struct wlr_cursor *cursor = server->seat.cursor; /* Prevent drag icons to be on top of the hitbox detection */ @@ -281,7 +281,7 @@ get_cursor_context(struct server *server) ret.node = node; if (!node) { - ret.type = LAB_SSD_ROOT; + ret.type = LAB_NODE_ROOT; return ret; } @@ -289,7 +289,7 @@ get_cursor_context(struct server *server) if (node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_surface *surface = lab_wlr_surface_from_node(node); if (node->parent == server->unmanaged_tree) { - ret.type = LAB_SSD_UNMANAGED; + ret.type = LAB_NODE_UNMANAGED; ret.surface = surface; return ret; } @@ -304,7 +304,7 @@ get_cursor_context(struct server *server) ret.view = desc->data; if (ret.node->type == WLR_SCENE_NODE_BUFFER && lab_wlr_surface_from_node(ret.node)) { - ret.type = LAB_SSD_CLIENT; + ret.type = LAB_NODE_CLIENT; ret.surface = lab_wlr_surface_from_node(ret.node); } else { /* should never be reached */ @@ -318,7 +318,7 @@ get_cursor_context(struct server *server) /* Detect mouse contexts like Top, Left and TRCorner */ ret.type = ssd_get_resizing_type(ret.view->ssd, cursor); - if (ret.type == LAB_SSD_NONE) { + if (ret.type == LAB_NODE_NONE) { /* * Otherwise, detect mouse contexts like * Title, Titlebar and Iconify @@ -330,23 +330,23 @@ get_cursor_context(struct server *server) } case LAB_NODE_DESC_LAYER_SURFACE: ret.node = node; - ret.type = LAB_SSD_LAYER_SURFACE; + ret.type = LAB_NODE_LAYER_SURFACE; ret.surface = get_surface_from_layer_node(node); return ret; case LAB_NODE_DESC_LAYER_POPUP: ret.node = node; - ret.type = LAB_SSD_CLIENT; + ret.type = LAB_NODE_CLIENT; ret.surface = get_surface_from_layer_node(node); return ret; case LAB_NODE_DESC_SESSION_LOCK_SURFACE: /* fallthrough */ case LAB_NODE_DESC_IME_POPUP: - ret.type = LAB_SSD_CLIENT; + ret.type = LAB_NODE_CLIENT; ret.surface = lab_wlr_surface_from_node(ret.node); return ret; case LAB_NODE_DESC_MENUITEM: /* Always return the top scene node for menu items */ ret.node = node; - ret.type = LAB_SSD_MENU; + ret.type = LAB_NODE_MENU; return ret; case LAB_NODE_DESC_NODE: case LAB_NODE_DESC_TREE: @@ -373,7 +373,7 @@ get_cursor_context(struct server *server) if (surface && wlr_subsurface_try_from_wlr_surface(surface) && subsurface_parent_layer(surface)) { ret.surface = surface; - ret.type = LAB_SSD_LAYER_SUBSURFACE; + ret.type = LAB_NODE_LAYER_SUBSURFACE; return ret; } } diff --git a/src/edges.c b/src/edges.c index 2e322433..f59107cf 100644 --- a/src/edges.c +++ b/src/edges.c @@ -10,9 +10,10 @@ #include "common/macros.h" #include "config/rcxml.h" #include "labwc.h" -#include "output.h" -#include "view.h" #include "node.h" +#include "output.h" +#include "ssd.h" +#include "view.h" static void edges_for_target_geometry(struct border *edges, struct view *view, diff --git a/src/input/cursor.c b/src/input/cursor.c index a051092f..afbdd5e8 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -113,9 +113,9 @@ cursor_get_from_edge(enum lab_edge resize_edges) } static enum lab_cursors -cursor_get_from_ssd(enum ssd_part_type view_area) +cursor_get_from_ssd(enum lab_node_type view_area) { - enum lab_edge resize_edges = ssd_resize_edges(view_area); + enum lab_edge resize_edges = node_type_to_edges(view_area); return cursor_get_from_edge(resize_edges); } @@ -580,7 +580,7 @@ cursor_update_common(struct server *server, struct cursor_context *ctx, enum lab_edge cursor_get_resize_edges(struct wlr_cursor *cursor, struct cursor_context *ctx) { - enum lab_edge resize_edges = ssd_resize_edges(ctx->type); + enum lab_edge resize_edges = node_type_to_edges(ctx->type); if (ctx->view && !resize_edges) { struct wlr_box box = ctx->view->current; resize_edges |= @@ -609,7 +609,7 @@ cursor_process_motion(struct server *server, uint32_t time, double *sx, double * struct cursor_context ctx = get_cursor_context(server); struct seat *seat = &server->seat; - if (ctx.type == LAB_SSD_MENU) { + if (ctx.type == LAB_NODE_MENU) { menu_process_cursor_motion(ctx.node); cursor_set(&server->seat, LAB_CURSOR_DEFAULT); return false; @@ -621,7 +621,7 @@ cursor_process_motion(struct server *server, uint32_t time, double *sx, double * struct mousebind *mousebind; wl_list_for_each(mousebind, &rc.mousebinds, link) { - if (ctx.type == LAB_SSD_CLIENT + if (ctx.type == LAB_NODE_CLIENT && view_inhibits_actions(ctx.view, &mousebind->actions)) { continue; } @@ -964,11 +964,11 @@ process_release_mousebinding(struct server *server, uint32_t modifiers = keyboard_get_all_modifiers(&server->seat); wl_list_for_each(mousebind, &rc.mousebinds, link) { - if (ctx->type == LAB_SSD_CLIENT + if (ctx->type == LAB_NODE_CLIENT && view_inhibits_actions(ctx->view, &mousebind->actions)) { continue; } - if (ssd_part_contains(mousebind->context, ctx->type) + if (node_type_contains(mousebind->context, ctx->type) && mousebind->button == button && modifiers == mousebind->modifiers) { switch (mousebind->mouse_event) { @@ -991,7 +991,7 @@ static bool is_double_click(long double_click_speed, uint32_t button, struct cursor_context *ctx) { - static enum ssd_part_type last_type; + static enum lab_node_type last_type; static uint32_t last_button; static struct view *last_view; static struct timespec last_click; @@ -1015,7 +1015,7 @@ is_double_click(long double_click_speed, uint32_t button, */ last_button = 0; last_view = NULL; - last_type = LAB_SSD_NONE; + last_type = LAB_NODE_NONE; return true; } return false; @@ -1035,11 +1035,11 @@ process_press_mousebinding(struct server *server, struct cursor_context *ctx, uint32_t modifiers = keyboard_get_all_modifiers(&server->seat); wl_list_for_each(mousebind, &rc.mousebinds, link) { - if (ctx->type == LAB_SSD_CLIENT + if (ctx->type == LAB_NODE_CLIENT && view_inhibits_actions(ctx->view, &mousebind->actions)) { continue; } - if (ssd_part_contains(mousebind->context, ctx->type) + if (node_type_contains(mousebind->context, ctx->type) && mousebind->button == button && modifiers == mousebind->modifiers) { switch (mousebind->mouse_event) { @@ -1053,9 +1053,9 @@ process_press_mousebinding(struct server *server, struct cursor_context *ctx, if (!double_click) { /* Swallow the press event */ consumed_by_frame_context |= - mousebind->context == LAB_SSD_FRAME; + mousebind->context == LAB_NODE_FRAME; consumed_by_frame_context |= - mousebind->context == LAB_SSD_ALL; + mousebind->context == LAB_NODE_ALL; mousebind->pressed_in_context = true; } continue; @@ -1069,8 +1069,8 @@ process_press_mousebinding(struct server *server, struct cursor_context *ctx, default: continue; } - consumed_by_frame_context |= mousebind->context == LAB_SSD_FRAME; - consumed_by_frame_context |= mousebind->context == LAB_SSD_ALL; + consumed_by_frame_context |= mousebind->context == LAB_NODE_FRAME; + consumed_by_frame_context |= mousebind->context == LAB_NODE_ALL; actions_run(ctx->view, server, &mousebind->actions, ctx); } } @@ -1108,14 +1108,14 @@ cursor_process_button_press(struct seat *seat, uint32_t button, uint32_t time_ms * Action processing does not run for these surfaces and thus * the Focus action (used for normal views) does not work. */ - if (ctx.type == LAB_SSD_LAYER_SURFACE) { + if (ctx.type == LAB_NODE_LAYER_SURFACE) { wlr_log(WLR_DEBUG, "press on layer-surface"); struct wlr_layer_surface_v1 *layer = wlr_layer_surface_v1_try_from_wlr_surface(ctx.surface); if (layer && layer->current.keyboard_interactive) { layer_try_set_focus(seat, layer); } - } else if (ctx.type == LAB_SSD_LAYER_SUBSURFACE) { + } else if (ctx.type == LAB_NODE_LAYER_SUBSURFACE) { wlr_log(WLR_DEBUG, "press on layer-subsurface"); struct wlr_layer_surface_v1 *layer = subsurface_parent_layer(ctx.surface); @@ -1123,13 +1123,13 @@ cursor_process_button_press(struct seat *seat, uint32_t button, uint32_t time_ms layer_try_set_focus(seat, layer); } #ifdef HAVE_XWAYLAND - } else if (ctx.type == LAB_SSD_UNMANAGED) { + } else if (ctx.type == LAB_NODE_UNMANAGED) { desktop_focus_view_or_surface(seat, NULL, ctx.surface, /*raise*/ false); #endif } - if (ctx.type != LAB_SSD_CLIENT && ctx.type != LAB_SSD_LAYER_SUBSURFACE + if (ctx.type != LAB_NODE_CLIENT && ctx.type != LAB_NODE_LAYER_SUBSURFACE && wlr_seat_pointer_has_grab(seat->seat)) { /* * If we have an active popup grab (an open popup) we want to @@ -1173,7 +1173,7 @@ cursor_process_button_release(struct seat *seat, uint32_t button, if (server->input_mode == LAB_INPUT_STATE_MENU) { /* TODO: take into account overflow of time_msec */ if (time_msec - press_msec > rc.menu_ignore_button_release_period) { - if (ctx.type == LAB_SSD_MENU) { + if (ctx.type == LAB_NODE_MENU) { menu_call_selected_actions(server); } else { menu_close_root(server); @@ -1342,11 +1342,11 @@ process_cursor_axis(struct server *server, enum wl_pointer_axis orientation, if (direction != LAB_DIRECTION_INVALID) { struct mousebind *mousebind; wl_list_for_each(mousebind, &rc.mousebinds, link) { - if (ctx.type == LAB_SSD_CLIENT + if (ctx.type == LAB_NODE_CLIENT && view_inhibits_actions(ctx.view, &mousebind->actions)) { continue; } - if (ssd_part_contains(mousebind->context, ctx.type) + if (node_type_contains(mousebind->context, ctx.type) && mousebind->direction == direction && modifiers == mousebind->modifiers && mousebind->mouse_event == MOUSE_ACTION_SCROLL) { diff --git a/src/input/tablet.c b/src/input/tablet.c index 9d9b9815..11a5a7d5 100644 --- a/src/input/tablet.c +++ b/src/input/tablet.c @@ -666,7 +666,7 @@ handle_tablet_tool_button(struct wl_listener *listener, void *data) wl_list_for_each(mousebind, &rc.mousebinds, link) { if (mousebind->mouse_event == MOUSE_ACTION_PRESS && mousebind->button == button - && mousebind->context == LAB_SSD_CLIENT) { + && mousebind->context == LAB_NODE_CLIENT) { actions_run(view, tool->seat->server, &mousebind->actions, NULL); } diff --git a/src/input/touch.c b/src/input/touch.c index 0486535e..9abd9991 100644 --- a/src/input/touch.c +++ b/src/input/touch.c @@ -167,7 +167,7 @@ handle_touch_down(struct wl_listener *listener, void *data) wl_list_for_each(mousebind, &rc.mousebinds, link) { if (mousebind->mouse_event == MOUSE_ACTION_PRESS && mousebind->button == BTN_LEFT - && mousebind->context == LAB_SSD_CLIENT) { + && mousebind->context == LAB_NODE_CLIENT) { actions_run(view, seat->server, &mousebind->actions, NULL); } } diff --git a/src/osd/osd.c b/src/osd/osd.c index 4103444e..e8d32348 100644 --- a/src/osd/osd.c +++ b/src/osd/osd.c @@ -13,6 +13,7 @@ #include "output.h" #include "scaled-buffer/scaled-font-buffer.h" #include "scaled-buffer/scaled-icon-buffer.h" +#include "ssd.h" #include "theme.h" #include "view.h" #include "window-rules.h" diff --git a/src/server.c b/src/server.c index 2b48b5ae..256d1bf5 100644 --- a/src/server.c +++ b/src/server.c @@ -65,6 +65,7 @@ #include "resize-indicator.h" #include "scaled-buffer/scaled-buffer.h" #include "session-lock.h" +#include "ssd.h" #include "theme.h" #include "view.h" #include "workspaces.h" diff --git a/src/snap.c b/src/snap.c index 83087e26..3da5f701 100644 --- a/src/snap.c +++ b/src/snap.c @@ -2,12 +2,13 @@ #include "snap.h" #include #include +#include #include "common/border.h" #include "config/rcxml.h" #include "edges.h" -#include "labwc.h" #include "output.h" #include "snap-constraints.h" +#include "ssd.h" #include "view.h" static void diff --git a/src/ssd/resize-indicator.c b/src/ssd/resize-indicator.c index 59a66022..6c22e656 100644 --- a/src/ssd/resize-indicator.c +++ b/src/ssd/resize-indicator.c @@ -3,12 +3,12 @@ #include #include #include -#include "common/macros.h" #include "config/rcxml.h" #include "labwc.h" #include "resize-indicator.h" #include "resize-outlines.h" #include "scaled-buffer/scaled-font-buffer.h" +#include "ssd.h" #include "theme.h" #include "view.h" diff --git a/src/ssd/ssd-border.c b/src/ssd/ssd-border.c index 4fff0ad4..54ef73e9 100644 --- a/src/ssd/ssd-border.c +++ b/src/ssd/ssd-border.c @@ -5,6 +5,7 @@ #include "common/macros.h" #include "common/scene-helpers.h" #include "labwc.h" +#include "ssd.h" #include "ssd-internal.h" #include "theme.h" #include "view.h" diff --git a/src/ssd/ssd-part.c b/src/ssd/ssd-part.c index d5430660..00cfe7af 100644 --- a/src/ssd/ssd-part.c +++ b/src/ssd/ssd-part.c @@ -32,7 +32,7 @@ handle_node_destroy(struct wl_listener *listener, void *data) * to is destroyed. */ static void -init_ssd_part(struct ssd_part *part, enum ssd_part_type type, +init_ssd_part(struct ssd_part *part, enum lab_node_type type, struct view *view, struct wlr_scene_node *node) { part->type = type; @@ -45,17 +45,17 @@ init_ssd_part(struct ssd_part *part, enum ssd_part_type type, } struct ssd_part * -attach_ssd_part(enum ssd_part_type type, struct view *view, +attach_ssd_part(enum lab_node_type type, struct view *view, struct wlr_scene_node *node) { - assert(!ssd_part_contains(LAB_SSD_BUTTON, type)); + assert(!node_type_contains(LAB_NODE_BUTTON, type)); struct ssd_part *part = znew(*part); init_ssd_part(part, type, view, node); return part; } struct ssd_part_button * -attach_ssd_part_button(struct wl_list *button_parts, enum ssd_part_type type, +attach_ssd_part_button(struct wl_list *button_parts, enum lab_node_type type, struct wlr_scene_tree *parent, struct lab_img *imgs[LAB_BS_ALL + 1], int x, int y, struct view *view) @@ -63,7 +63,7 @@ attach_ssd_part_button(struct wl_list *button_parts, enum ssd_part_type type, struct wlr_scene_tree *root = wlr_scene_tree_create(parent); wlr_scene_node_set_position(&root->node, x, y); - assert(ssd_part_contains(LAB_SSD_BUTTON, type)); + assert(node_type_contains(LAB_NODE_BUTTON, type)); struct ssd_part_button *button = znew(*button); init_ssd_part(&button->base, type, view, &root->node); wl_list_append(button_parts, &button->link); @@ -86,7 +86,7 @@ attach_ssd_part_button(struct wl_list *button_parts, enum ssd_part_type type, */ int icon_padding = button_width / 10; - if (type == LAB_SSD_BUTTON_WINDOW_ICON) { + if (type == LAB_NODE_BUTTON_WINDOW_ICON) { struct scaled_icon_buffer *icon_buffer = scaled_icon_buffer_create(root, view->server, button_width - 2 * icon_padding, button_height); @@ -120,7 +120,7 @@ attach_ssd_part_button(struct wl_list *button_parts, enum ssd_part_type type, struct ssd_part_button * button_try_from_ssd_part(struct ssd_part *part) { - if (ssd_part_contains(LAB_SSD_BUTTON, part->type)) { + if (node_type_contains(LAB_NODE_BUTTON, part->type)) { return (struct ssd_part_button *)part; } return NULL; diff --git a/src/ssd/ssd-shadow.c b/src/ssd/ssd-shadow.c index 2e86c864..a535112e 100644 --- a/src/ssd/ssd-shadow.c +++ b/src/ssd/ssd-shadow.c @@ -6,6 +6,7 @@ #include "buffer.h" #include "config/rcxml.h" #include "labwc.h" +#include "ssd.h" #include "ssd-internal.h" #include "theme.h" #include "view.h" diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 8076d02f..5bd1bda9 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -16,12 +16,13 @@ #include "scaled-buffer/scaled-font-buffer.h" #include "scaled-buffer/scaled-icon-buffer.h" #include "scaled-buffer/scaled-img-buffer.h" +#include "ssd.h" #include "ssd-internal.h" #include "theme.h" #include "view.h" static void set_squared_corners(struct ssd *ssd, bool enable); -static void set_alt_button_icon(struct ssd *ssd, enum ssd_part_type type, bool enable); +static void set_alt_button_icon(struct ssd *ssd, enum lab_node_type type, bool enable); static void update_visible_buttons(struct ssd *ssd); void @@ -33,7 +34,7 @@ ssd_titlebar_create(struct ssd *ssd) int corner_width = ssd_get_corner_width(); ssd->titlebar.tree = wlr_scene_tree_create(ssd->tree); - attach_ssd_part(LAB_SSD_PART_TITLEBAR, view, &ssd->titlebar.tree->node); + attach_ssd_part(LAB_NODE_TITLEBAR, view, &ssd->titlebar.tree->node); enum ssd_active_state active; FOR_EACH_ACTIVE_STATE(active) { @@ -77,7 +78,7 @@ ssd_titlebar_create(struct ssd *ssd) subtree->tree, theme->titlebar_height, theme->window[active].titlebar_pattern); assert(subtree->title); - attach_ssd_part(LAB_SSD_PART_TITLE, + attach_ssd_part(LAB_NODE_TITLE, view, &subtree->title->scene_buffer->node); /* Buttons */ @@ -115,7 +116,7 @@ ssd_titlebar_create(struct ssd *ssd) bool maximized = view->maximized == VIEW_AXIS_BOTH; bool squared = ssd_should_be_squared(ssd); if (maximized) { - set_alt_button_icon(ssd, LAB_SSD_BUTTON_MAXIMIZE, true); + set_alt_button_icon(ssd, LAB_NODE_BUTTON_MAXIMIZE, true); ssd->state.was_maximized = true; } if (squared) { @@ -124,11 +125,11 @@ ssd_titlebar_create(struct ssd *ssd) set_squared_corners(ssd, maximized || squared); if (view->shaded) { - set_alt_button_icon(ssd, LAB_SSD_BUTTON_SHADE, true); + set_alt_button_icon(ssd, LAB_NODE_BUTTON_SHADE, true); } if (view->visible_on_all_workspaces) { - set_alt_button_icon(ssd, LAB_SSD_BUTTON_OMNIPRESENT, true); + set_alt_button_icon(ssd, LAB_NODE_BUTTON_OMNIPRESENT, true); } } @@ -189,7 +190,7 @@ set_squared_corners(struct ssd *ssd, bool enable) } static void -set_alt_button_icon(struct ssd *ssd, enum ssd_part_type type, bool enable) +set_alt_button_icon(struct ssd *ssd, enum lab_node_type type, bool enable) { enum ssd_active_state active; FOR_EACH_ACTIVE_STATE(active) { @@ -281,19 +282,19 @@ ssd_titlebar_update(struct ssd *ssd) || ssd->state.was_squared != squared) { set_squared_corners(ssd, maximized || squared); if (ssd->state.was_maximized != maximized) { - set_alt_button_icon(ssd, LAB_SSD_BUTTON_MAXIMIZE, maximized); + set_alt_button_icon(ssd, LAB_NODE_BUTTON_MAXIMIZE, maximized); } ssd->state.was_maximized = maximized; ssd->state.was_squared = squared; } if (ssd->state.was_shaded != view->shaded) { - set_alt_button_icon(ssd, LAB_SSD_BUTTON_SHADE, view->shaded); + set_alt_button_icon(ssd, LAB_NODE_BUTTON_SHADE, view->shaded); ssd->state.was_shaded = view->shaded; } if (ssd->state.was_omnipresent != view->visible_on_all_workspaces) { - set_alt_button_icon(ssd, LAB_SSD_BUTTON_OMNIPRESENT, + set_alt_button_icon(ssd, LAB_NODE_BUTTON_OMNIPRESENT, view->visible_on_all_workspaces); ssd->state.was_omnipresent = view->visible_on_all_workspaces; } diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index d0603d5e..cd6a9d01 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -6,6 +6,7 @@ * Copyright (C) Johan Malm 2020-2021 */ +#include "ssd.h" #include #include #include @@ -85,12 +86,12 @@ ssd_max_extents(struct view *view) * (generally rc.resize_corner_range, but clipped to view size) of the view * bounds, so check the cursor against the view here. */ -enum ssd_part_type +enum lab_node_type ssd_get_resizing_type(const struct ssd *ssd, struct wlr_cursor *cursor) { struct view *view = ssd ? ssd->view : NULL; if (!view || !cursor || !view->ssd_mode || view->fullscreen) { - return LAB_SSD_NONE; + return LAB_NODE_NONE; } struct wlr_box view_box = view->current; @@ -105,7 +106,7 @@ ssd_get_resizing_type(const struct ssd *ssd, struct wlr_cursor *cursor) if (wlr_box_contains_point(&view_box, cursor->x, cursor->y)) { /* A cursor in bounds of the view is never in an SSD context */ - return LAB_SSD_NONE; + return LAB_NODE_NONE; } int corner_height = MAX(0, MIN(rc.resize_corner_range, view_box.height / 2)); @@ -116,49 +117,24 @@ ssd_get_resizing_type(const struct ssd *ssd, struct wlr_cursor *cursor) bool bottom = cursor->y > view_box.y + view_box.height - corner_height; if (top && left) { - return LAB_SSD_PART_CORNER_TOP_LEFT; + return LAB_NODE_CORNER_TOP_LEFT; } else if (top && right) { - return LAB_SSD_PART_CORNER_TOP_RIGHT; + return LAB_NODE_CORNER_TOP_RIGHT; } else if (bottom && left) { - return LAB_SSD_PART_CORNER_BOTTOM_LEFT; + return LAB_NODE_CORNER_BOTTOM_LEFT; } else if (bottom && right) { - return LAB_SSD_PART_CORNER_BOTTOM_RIGHT; + return LAB_NODE_CORNER_BOTTOM_RIGHT; } else if (top) { - return LAB_SSD_PART_TOP; + return LAB_NODE_BORDER_TOP; } else if (bottom) { - return LAB_SSD_PART_BOTTOM; + return LAB_NODE_BORDER_BOTTOM; } else if (left) { - return LAB_SSD_PART_LEFT; + return LAB_NODE_BORDER_LEFT; } else if (right) { - return LAB_SSD_PART_RIGHT; + return LAB_NODE_BORDER_RIGHT; } - return LAB_SSD_NONE; -} - -enum lab_edge -ssd_resize_edges(enum ssd_part_type type) -{ - switch (type) { - case LAB_SSD_PART_TOP: - return LAB_EDGE_TOP; - case LAB_SSD_PART_RIGHT: - return LAB_EDGE_RIGHT; - case LAB_SSD_PART_BOTTOM: - return LAB_EDGE_BOTTOM; - case LAB_SSD_PART_LEFT: - return LAB_EDGE_LEFT; - case LAB_SSD_PART_CORNER_TOP_LEFT: - return LAB_EDGES_TOP_LEFT; - case LAB_SSD_PART_CORNER_TOP_RIGHT: - return LAB_EDGES_TOP_RIGHT; - case LAB_SSD_PART_CORNER_BOTTOM_RIGHT: - return LAB_EDGES_BOTTOM_RIGHT; - case LAB_SSD_PART_CORNER_BOTTOM_LEFT: - return LAB_EDGES_BOTTOM_LEFT; - default: - return LAB_EDGE_NONE; - } + return LAB_NODE_NONE; } struct ssd * @@ -169,7 +145,7 @@ ssd_create(struct view *view, bool active) ssd->view = view; ssd->tree = wlr_scene_tree_create(view->scene_tree); - attach_ssd_part(LAB_SSD_NONE, view, &ssd->tree->node); + attach_ssd_part(LAB_NODE_NONE, view, &ssd->tree->node); wlr_scene_node_lower_to_bottom(&ssd->tree->node); ssd->titlebar.height = view->server->theme->titlebar_height; ssd_shadow_create(ssd); @@ -305,52 +281,6 @@ ssd_destroy(struct ssd *ssd) free(ssd); } -bool -ssd_part_contains(enum ssd_part_type whole, enum ssd_part_type candidate) -{ - if (whole == candidate || whole == LAB_SSD_ALL) { - return true; - } - if (whole == LAB_SSD_BUTTON) { - return candidate >= LAB_SSD_BUTTON_CLOSE - && candidate <= LAB_SSD_BUTTON_OMNIPRESENT; - } - if (whole == LAB_SSD_PART_TITLEBAR) { - return candidate >= LAB_SSD_BUTTON_CLOSE - && candidate <= LAB_SSD_PART_TITLE; - } - if (whole == LAB_SSD_PART_TITLE) { - /* "Title" includes blank areas of "Titlebar" as well */ - return candidate >= LAB_SSD_PART_TITLEBAR - && 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_BORDER) { - return candidate >= LAB_SSD_PART_CORNER_TOP_LEFT - && candidate <= LAB_SSD_PART_LEFT; - } - if (whole == LAB_SSD_PART_TOP) { - return candidate == LAB_SSD_PART_CORNER_TOP_LEFT - || candidate == LAB_SSD_PART_CORNER_TOP_RIGHT; - } - 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; -} - enum lab_ssd_mode ssd_mode_parse(const char *mode) { @@ -421,10 +351,10 @@ ssd_hover_state_new(void) return znew(struct ssd_hover_state); } -enum ssd_part_type +enum lab_node_type ssd_part_get_type(const struct ssd_part *part) { - return part ? part->type : LAB_SSD_NONE; + return part ? part->type : LAB_NODE_NONE; } struct view * diff --git a/src/theme.c b/src/theme.c index 5c323cf6..a8a3227d 100644 --- a/src/theme.c +++ b/src/theme.c @@ -39,7 +39,7 @@ struct button { const char *name; const char *alt_name; const char *fallback_button; /* built-in 6x6 button */ - enum ssd_part_type type; + enum lab_node_type type; uint8_t state_set; }; @@ -290,94 +290,94 @@ load_buttons(struct theme *theme) struct button buttons[] = { { .name = "menu", .fallback_button = (const char[]){ 0x00, 0x21, 0x33, 0x1E, 0x0C, 0x00 }, - .type = LAB_SSD_BUTTON_WINDOW_MENU, + .type = LAB_NODE_BUTTON_WINDOW_MENU, .state_set = 0, }, { .name = "iconify", .fallback_button = (const char[]){ 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f }, - .type = LAB_SSD_BUTTON_ICONIFY, + .type = LAB_NODE_BUTTON_ICONIFY, .state_set = 0, }, { .name = "max", .fallback_button = (const char[]){ 0x3f, 0x3f, 0x21, 0x21, 0x21, 0x3f }, - .type = LAB_SSD_BUTTON_MAXIMIZE, + .type = LAB_NODE_BUTTON_MAXIMIZE, .state_set = 0, }, { .name = "max_toggled", .fallback_button = (const char[]){ 0x3e, 0x22, 0x2f, 0x29, 0x39, 0x0f }, - .type = LAB_SSD_BUTTON_MAXIMIZE, + .type = LAB_NODE_BUTTON_MAXIMIZE, .state_set = LAB_BS_TOGGLED, }, { .name = "shade", .fallback_button = (const char[]){ 0x3f, 0x3f, 0x00, 0x0c, 0x1e, 0x3f }, - .type = LAB_SSD_BUTTON_SHADE, + .type = LAB_NODE_BUTTON_SHADE, .state_set = 0, }, { .name = "shade_toggled", .fallback_button = (const char[]){ 0x3f, 0x3f, 0x00, 0x3f, 0x1e, 0x0c }, - .type = LAB_SSD_BUTTON_SHADE, + .type = LAB_NODE_BUTTON_SHADE, .state_set = LAB_BS_TOGGLED, }, { .name = "desk", .fallback_button = (const char[]){ 0x33, 0x33, 0x00, 0x00, 0x33, 0x33 }, - .type = LAB_SSD_BUTTON_OMNIPRESENT, + .type = LAB_NODE_BUTTON_OMNIPRESENT, .state_set = 0, }, { .name = "desk_toggled", .fallback_button = (const char[]){ 0x00, 0x1e, 0x1a, 0x16, 0x1e, 0x00 }, - .type = LAB_SSD_BUTTON_OMNIPRESENT, + .type = LAB_NODE_BUTTON_OMNIPRESENT, .state_set = LAB_BS_TOGGLED, }, { .name = "close", .fallback_button = (const char[]){ 0x33, 0x3f, 0x1e, 0x1e, 0x3f, 0x33 }, - .type = LAB_SSD_BUTTON_CLOSE, + .type = LAB_NODE_BUTTON_CLOSE, .state_set = 0, }, { .name = "menu_hover", - .type = LAB_SSD_BUTTON_WINDOW_MENU, + .type = LAB_NODE_BUTTON_WINDOW_MENU, .state_set = LAB_BS_HOVERED, /* no fallback (non-hover variant is used instead) */ }, { .name = "iconify_hover", - .type = LAB_SSD_BUTTON_ICONIFY, + .type = LAB_NODE_BUTTON_ICONIFY, .state_set = LAB_BS_HOVERED, /* no fallback (non-hover variant is used instead) */ }, { .name = "max_hover", - .type = LAB_SSD_BUTTON_MAXIMIZE, + .type = LAB_NODE_BUTTON_MAXIMIZE, .state_set = LAB_BS_HOVERED, /* no fallback (non-hover variant is used instead) */ }, { .name = "max_toggled_hover", .alt_name = "max_hover_toggled", - .type = LAB_SSD_BUTTON_MAXIMIZE, + .type = LAB_NODE_BUTTON_MAXIMIZE, .state_set = LAB_BS_TOGGLED | LAB_BS_HOVERED, /* no fallback (non-hover variant is used instead) */ }, { .name = "shade_hover", - .type = LAB_SSD_BUTTON_SHADE, + .type = LAB_NODE_BUTTON_SHADE, .state_set = LAB_BS_HOVERED, /* no fallback (non-hover variant is used instead) */ }, { .name = "shade_toggled_hover", .alt_name = "shade_hover_toggled", - .type = LAB_SSD_BUTTON_SHADE, + .type = LAB_NODE_BUTTON_SHADE, .state_set = LAB_BS_TOGGLED | LAB_BS_HOVERED, /* no fallback (non-hover variant is used instead) */ }, { .name = "desk_hover", /* no fallback (non-hover variant is used instead) */ - .type = LAB_SSD_BUTTON_OMNIPRESENT, + .type = LAB_NODE_BUTTON_OMNIPRESENT, .state_set = LAB_BS_HOVERED, }, { .name = "desk_toggled_hover", .alt_name = "desk_hover_toggled", - .type = LAB_SSD_BUTTON_OMNIPRESENT, + .type = LAB_NODE_BUTTON_OMNIPRESENT, .state_set = LAB_BS_TOGGLED | LAB_BS_HOVERED, /* no fallback (non-hover variant is used instead) */ }, { .name = "close_hover", - .type = LAB_SSD_BUTTON_CLOSE, + .type = LAB_NODE_BUTTON_CLOSE, .state_set = LAB_BS_HOVERED, /* no fallback (non-hover variant is used instead) */ }, }; @@ -572,8 +572,8 @@ theme_builtin(struct theme *theme, struct server *server) theme->window_button_spacing = 0; theme->window_button_hover_bg_corner_radius = 0; - for (enum ssd_part_type type = LAB_SSD_BUTTON_FIRST; - type <= LAB_SSD_BUTTON_LAST; type++) { + for (enum lab_node_type type = LAB_NODE_BUTTON_FIRST; + type <= LAB_NODE_BUTTON_LAST; type++) { parse_hexstr("#000000", theme->window[THEME_INACTIVE].button_colors[type]); parse_hexstr("#000000", @@ -802,15 +802,15 @@ entry(struct theme *theme, const char *key, const char *value) /* universal button */ if (match_glob(key, "window.active.button.unpressed.image.color")) { - for (enum ssd_part_type type = LAB_SSD_BUTTON_FIRST; - type <= LAB_SSD_BUTTON_LAST; type++) { + for (enum lab_node_type type = LAB_NODE_BUTTON_FIRST; + type <= LAB_NODE_BUTTON_LAST; type++) { parse_color(value, theme->window[THEME_ACTIVE].button_colors[type]); } } if (match_glob(key, "window.inactive.button.unpressed.image.color")) { - for (enum ssd_part_type type = LAB_SSD_BUTTON_FIRST; - type <= LAB_SSD_BUTTON_LAST; type++) { + for (enum lab_node_type type = LAB_NODE_BUTTON_FIRST; + type <= LAB_NODE_BUTTON_LAST; type++) { parse_color(value, theme->window[THEME_INACTIVE].button_colors[type]); } @@ -819,55 +819,55 @@ entry(struct theme *theme, const char *key, const char *value) /* individual buttons */ if (match_glob(key, "window.active.button.menu.unpressed.image.color")) { parse_color(value, theme->window[THEME_ACTIVE] - .button_colors[LAB_SSD_BUTTON_WINDOW_MENU]); + .button_colors[LAB_NODE_BUTTON_WINDOW_MENU]); parse_color(value, theme->window[THEME_ACTIVE] - .button_colors[LAB_SSD_BUTTON_WINDOW_ICON]); + .button_colors[LAB_NODE_BUTTON_WINDOW_ICON]); } if (match_glob(key, "window.active.button.iconify.unpressed.image.color")) { parse_color(value, theme->window[THEME_ACTIVE] - .button_colors[LAB_SSD_BUTTON_ICONIFY]); + .button_colors[LAB_NODE_BUTTON_ICONIFY]); } if (match_glob(key, "window.active.button.max.unpressed.image.color")) { parse_color(value, theme->window[THEME_ACTIVE] - .button_colors[LAB_SSD_BUTTON_MAXIMIZE]); + .button_colors[LAB_NODE_BUTTON_MAXIMIZE]); } if (match_glob(key, "window.active.button.shade.unpressed.image.color")) { parse_color(value, theme->window[THEME_ACTIVE] - .button_colors[LAB_SSD_BUTTON_SHADE]); + .button_colors[LAB_NODE_BUTTON_SHADE]); } if (match_glob(key, "window.active.button.desk.unpressed.image.color")) { parse_color(value, theme->window[THEME_ACTIVE] - .button_colors[LAB_SSD_BUTTON_OMNIPRESENT]); + .button_colors[LAB_NODE_BUTTON_OMNIPRESENT]); } if (match_glob(key, "window.active.button.close.unpressed.image.color")) { parse_color(value, theme->window[THEME_ACTIVE] - .button_colors[LAB_SSD_BUTTON_CLOSE]); + .button_colors[LAB_NODE_BUTTON_CLOSE]); } if (match_glob(key, "window.inactive.button.menu.unpressed.image.color")) { parse_color(value, theme->window[THEME_INACTIVE] - .button_colors[LAB_SSD_BUTTON_WINDOW_MENU]); + .button_colors[LAB_NODE_BUTTON_WINDOW_MENU]); parse_color(value, theme->window[THEME_INACTIVE] - .button_colors[LAB_SSD_BUTTON_WINDOW_ICON]); + .button_colors[LAB_NODE_BUTTON_WINDOW_ICON]); } if (match_glob(key, "window.inactive.button.iconify.unpressed.image.color")) { parse_color(value, theme->window[THEME_INACTIVE] - .button_colors[LAB_SSD_BUTTON_ICONIFY]); + .button_colors[LAB_NODE_BUTTON_ICONIFY]); } if (match_glob(key, "window.inactive.button.max.unpressed.image.color")) { parse_color(value, theme->window[THEME_INACTIVE] - .button_colors[LAB_SSD_BUTTON_MAXIMIZE]); + .button_colors[LAB_NODE_BUTTON_MAXIMIZE]); } if (match_glob(key, "window.inactive.button.shade.unpressed.image.color")) { parse_color(value, theme->window[THEME_INACTIVE] - .button_colors[LAB_SSD_BUTTON_SHADE]); + .button_colors[LAB_NODE_BUTTON_SHADE]); } if (match_glob(key, "window.inactive.button.desk.unpressed.image.color")) { parse_color(value, theme->window[THEME_INACTIVE] - .button_colors[LAB_SSD_BUTTON_OMNIPRESENT]); + .button_colors[LAB_NODE_BUTTON_OMNIPRESENT]); } if (match_glob(key, "window.inactive.button.close.unpressed.image.color")) { parse_color(value, theme->window[THEME_INACTIVE] - .button_colors[LAB_SSD_BUTTON_CLOSE]); + .button_colors[LAB_NODE_BUTTON_CLOSE]); } /* window drop-shadows */ @@ -1811,8 +1811,8 @@ static void destroy_img(struct lab_img **img) void theme_finish(struct theme *theme) { - for (enum ssd_part_type type = LAB_SSD_BUTTON_FIRST; - type <= LAB_SSD_BUTTON_LAST; type++) { + for (enum lab_node_type type = LAB_NODE_BUTTON_FIRST; + type <= LAB_NODE_BUTTON_LAST; type++) { for (uint8_t state_set = LAB_BS_DEFAULT; state_set <= LAB_BS_ALL; state_set++) { destroy_img(&theme->window[THEME_INACTIVE] diff --git a/src/xwayland.c b/src/xwayland.c index 0a39ffdc..9776dd1d 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -17,7 +17,6 @@ #include "labwc.h" #include "node.h" #include "output.h" -#include "ssd.h" #include "view.h" #include "view-impl-common.h" #include "window-rules.h" From ba426e2271771dd95528aa93511143a3aabfa31d Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Wed, 3 Sep 2025 01:20:53 -0400 Subject: [PATCH 144/248] ssd: eliminate ssd_hover_state (addressing a FIXME) --- include/labwc.h | 2 +- include/ssd-internal.h | 6 ------ include/ssd.h | 7 +++---- src/input/cursor.c | 2 +- src/input/touch.c | 2 +- src/server.c | 3 --- src/ssd/ssd-titlebar.c | 14 +++++--------- src/ssd/ssd.c | 14 +++----------- 8 files changed, 14 insertions(+), 36 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index 0629a8b8..98e33575 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -225,7 +225,7 @@ struct server { */ struct view *active_view; - struct ssd_hover_state *ssd_hover_state; + struct ssd_part_button *hovered_button; /* Tree for all non-layer xdg/xwayland-shell surfaces */ struct wlr_scene_tree *view_tree; diff --git a/include/ssd-internal.h b/include/ssd-internal.h index 13e5e422..d1af095e 100644 --- a/include/ssd-internal.h +++ b/include/ssd-internal.h @@ -173,12 +173,6 @@ struct ssd_part_button { struct wl_list link; /* ssd_titlebar_subtree.buttons_{left,right} */ }; -/* FIXME: This structure is redundant as ssd_part contains view */ -struct ssd_hover_state { - struct view *view; - struct ssd_part_button *button; -}; - struct wlr_buffer; struct wlr_scene_tree; diff --git a/include/ssd.h b/include/ssd.h index 4ab404e2..11fae7ab 100644 --- a/include/ssd.h +++ b/include/ssd.h @@ -23,8 +23,8 @@ struct wlr_cursor; #define SSD_SHADOW_INSET 0.3 /* Forward declare arguments */ +struct server; struct ssd; -struct ssd_hover_state; struct ssd_part; struct view; struct wlr_scene; @@ -52,9 +52,8 @@ void ssd_set_titlebar(struct ssd *ssd, bool enabled); void ssd_enable_keybind_inhibit_indicator(struct ssd *ssd, bool enable); void ssd_enable_shade(struct ssd *ssd, bool enable); -struct ssd_hover_state *ssd_hover_state_new(void); -void ssd_update_button_hover(struct wlr_scene_node *node, - struct ssd_hover_state *hover_state); +void ssd_update_hovered_button(struct server *server, + struct wlr_scene_node *node); enum lab_node_type ssd_part_get_type(const struct ssd_part *part); struct view *ssd_part_get_view(const struct ssd_part *part); diff --git a/src/input/cursor.c b/src/input/cursor.c index afbdd5e8..f25bce2d 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -515,7 +515,7 @@ cursor_update_common(struct server *server, struct cursor_context *ctx, struct seat *seat = &server->seat; struct wlr_seat *wlr_seat = seat->seat; - ssd_update_button_hover(ctx->node, server->ssd_hover_state); + ssd_update_hovered_button(server, ctx->node); if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) { /* diff --git a/src/input/touch.c b/src/input/touch.c index 9abd9991..9f4f1eca 100644 --- a/src/input/touch.c +++ b/src/input/touch.c @@ -206,7 +206,7 @@ handle_touch_up(struct wl_listener *listener, void *data) } else { cursor_emulate_button(seat, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED, event->time_msec); - ssd_update_button_hover(NULL, seat->server->ssd_hover_state); + ssd_update_hovered_button(seat->server, NULL); } wl_list_remove(&touch_point->link); zfree(touch_point); diff --git a/src/server.c b/src/server.c index 256d1bf5..f7273c12 100644 --- a/src/server.c +++ b/src/server.c @@ -549,8 +549,6 @@ server_init(struct server *server) wl_list_init(&server->views); wl_list_init(&server->unmanaged_surfaces); - server->ssd_hover_state = ssd_hover_state_new(); - server->scene = wlr_scene_create(); if (!server->scene) { wlr_log(WLR_ERROR, "unable to create scene"); @@ -795,5 +793,4 @@ server_finish(struct server *server) wlr_scene_node_destroy(&server->scene->tree.node); wl_display_destroy(server->wl_display); - free(server->ssd_hover_state); } diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 5bd1bda9..05cbdb9a 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -492,8 +492,7 @@ ssd_update_title(struct ssd *ssd) } void -ssd_update_button_hover(struct wlr_scene_node *node, - struct ssd_hover_state *hover_state) +ssd_update_hovered_button(struct server *server, struct wlr_scene_node *node) { struct ssd_part_button *button = NULL; @@ -502,7 +501,7 @@ ssd_update_button_hover(struct wlr_scene_node *node, if (desc->type == LAB_NODE_DESC_SSD_PART) { button = button_try_from_ssd_part( node_ssd_part_from_node(node)); - if (button == hover_state->button) { + if (button == server->hovered_button) { /* Cursor is still on the same button */ return; } @@ -510,15 +509,12 @@ ssd_update_button_hover(struct wlr_scene_node *node, } /* Disable old hover */ - if (hover_state->button) { - update_button_state(hover_state->button, LAB_BS_HOVERED, false); - hover_state->view = NULL; - hover_state->button = NULL; + if (server->hovered_button) { + update_button_state(server->hovered_button, LAB_BS_HOVERED, false); } + server->hovered_button = button; if (button) { update_button_state(button, LAB_BS_HOVERED, true); - hover_state->view = button->base.view; - hover_state->button = button; } } diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index cd6a9d01..67d5966b 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -264,11 +264,9 @@ ssd_destroy(struct ssd *ssd) /* Maybe reset hover view */ struct view *view = ssd->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->button = NULL; + struct server *server = view->server; + if (server->hovered_button && server->hovered_button->base.view == view) { + server->hovered_button = NULL; } /* Destroy subcomponents */ @@ -345,12 +343,6 @@ ssd_enable_keybind_inhibit_indicator(struct ssd *ssd, bool enable) wlr_scene_rect_set_color(ssd->border.subtrees[SSD_ACTIVE].top, color); } -struct ssd_hover_state * -ssd_hover_state_new(void) -{ - return znew(struct ssd_hover_state); -} - enum lab_node_type ssd_part_get_type(const struct ssd_part *part) { From f12957177923153c2f611814abb5a8bd32270eaa Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Wed, 3 Sep 2025 05:32:44 -0400 Subject: [PATCH 145/248] ssd: unify struct ssd_part with struct node_descriptor struct ssd_part and struct node_descriptor seem to have essentially the same purpose: tag a wlr_scene_node with some extra data indicating what we're using it for. Also, as with enum ssd_part_type (now lab_node_type), ssd_part is used for several types of nodes that are not part of SSD. So instead of the current chaining (node_descriptor -> ssd_part), let's flatten/unify the two structs. In detail: - First, merge node_descriptor_type into lab_node_type. - Add a separate view pointer in node_descriptor, since in the case of SSD buttons we need separate view and button data pointers. - Rename ssd_part_button to simply ssd_button. It no longer contains an ssd_part as base. - Add node_try_ssd_button_from_node() which replaces node_ssd_part_from_node() + button_try_from_ssd_part(). - Factor out ssd_button_free() to be called in node descriptor destroy. - Finally, get_cursor_context() needs a little reorganization to handle the unified structs. Overall, this simplifies the code a bit, and in my opinion makes it easier to understand. No functional change intended. --- include/common/node-type.h | 13 ++++- include/labwc.h | 2 +- include/node.h | 52 ++++++------------- include/ssd-internal.h | 22 ++------ include/ssd.h | 5 +- src/desktop.c | 76 +++++++++++++++------------- src/edges.c | 2 +- src/input/cursor.c | 4 +- src/input/ime.c | 3 +- src/layers.c | 4 +- src/menu/menu.c | 12 ++--- src/node.c | 56 ++++++++++---------- src/output.c | 8 +-- src/scaled-buffer/scaled-buffer.c | 2 +- src/session-lock.c | 2 +- src/ssd/meson.build | 2 +- src/ssd/{ssd-part.c => ssd-button.c} | 64 ++++------------------- src/ssd/ssd-titlebar.c | 53 +++++++++---------- src/ssd/ssd.c | 19 ++----- src/xdg-popup.c | 2 +- src/xdg.c | 2 +- src/xwayland.c | 3 +- 22 files changed, 169 insertions(+), 239 deletions(-) rename src/ssd/{ssd-part.c => ssd-button.c} (61%) diff --git a/include/common/node-type.h b/include/common/node-type.h index 92e7f4b9..0afa5f58 100644 --- a/include/common/node-type.h +++ b/include/common/node-type.h @@ -46,12 +46,23 @@ enum lab_node_type { LAB_NODE_CLIENT, LAB_NODE_FRAME, LAB_NODE_ROOT, - LAB_NODE_MENU, + LAB_NODE_MENUITEM, LAB_NODE_OSD, LAB_NODE_LAYER_SURFACE, LAB_NODE_LAYER_SUBSURFACE, LAB_NODE_UNMANAGED, LAB_NODE_ALL, + + /* translated to LAB_NODE_CLIENT by get_cursor_context() */ + LAB_NODE_VIEW, + LAB_NODE_XDG_POPUP, + LAB_NODE_LAYER_POPUP, + LAB_NODE_SESSION_LOCK_SURFACE, + LAB_NODE_IME_POPUP, + + /* never returned by get_cursor_context() */ + LAB_NODE_TREE, + LAB_NODE_SCALED_BUFFER, }; enum lab_node_type node_type_parse(const char *context); diff --git a/include/labwc.h b/include/labwc.h index 98e33575..2509292e 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -225,7 +225,7 @@ struct server { */ struct view *active_view; - struct ssd_part_button *hovered_button; + struct ssd_button *hovered_button; /* Tree for all non-layer xdg/xwayland-shell surfaces */ struct wlr_scene_tree *view_tree; diff --git a/include/node.h b/include/node.h index d28a8517..7e8b6b50 100644 --- a/include/node.h +++ b/include/node.h @@ -2,30 +2,11 @@ #ifndef LABWC_NODE_DESCRIPTOR_H #define LABWC_NODE_DESCRIPTOR_H #include - -struct view; -struct lab_layer_surface; -struct lab_layer_popup; -struct menuitem; -struct ssd_part; -struct scaled_buffer; - -enum node_descriptor_type { - LAB_NODE_DESC_NODE = 0, - LAB_NODE_DESC_VIEW, - LAB_NODE_DESC_XDG_POPUP, - LAB_NODE_DESC_LAYER_SURFACE, - LAB_NODE_DESC_LAYER_POPUP, - LAB_NODE_DESC_SESSION_LOCK_SURFACE, - LAB_NODE_DESC_IME_POPUP, - LAB_NODE_DESC_MENUITEM, - LAB_NODE_DESC_TREE, - LAB_NODE_DESC_SCALED_BUFFER, - LAB_NODE_DESC_SSD_PART, -}; +#include "common/node-type.h" struct node_descriptor { - enum node_descriptor_type type; + enum lab_node_type type; + struct view *view; void *data; struct wl_listener destroy; }; @@ -38,16 +19,15 @@ struct node_descriptor { * * @scene_node: wlr_scene_node to attached node_descriptor to * @type: node descriptor type + * @view: associated view * @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 - * - LAB_NODE_DESC_MENUITEM struct menuitem - * - LAB_NODE_DESC_SSD_PART struct ssd_part + * - LAB_NODE_LAYER_SURFACE struct lab_layer_surface + * - LAB_NODE_LAYER_POPUP struct lab_layer_popup + * - LAB_NODE_MENUITEM struct menuitem + * - LAB_NODE_BUTTON_* struct ssd_button */ void node_descriptor_create(struct wlr_scene_node *scene_node, - enum node_descriptor_type type, void *data); + enum lab_node_type type, struct view *view, void *data); /** * node_view_from_node - return view struct from node @@ -76,13 +56,6 @@ struct lab_layer_popup *node_layer_popup_from_node( struct menuitem *node_menuitem_from_node( struct wlr_scene_node *wlr_scene_node); -/** - * node_ssd_part_from_node - return ssd_part struct from node - * @wlr_scene_node: wlr_scene_node from which to return data - */ -struct ssd_part *node_ssd_part_from_node( - struct wlr_scene_node *wlr_scene_node); - /** * node_scaled_buffer_from_node - return scaled_buffer from node * @wlr_scene_node: wlr_scene_node from which to return data @@ -90,4 +63,11 @@ struct ssd_part *node_ssd_part_from_node( struct scaled_buffer *node_scaled_buffer_from_node( struct wlr_scene_node *wlr_scene_node); +/** + * node_try_ssd_button_from_node - return ssd_button or NULL from node + * @wlr_scene_node: wlr_scene_node from which to return data + */ +struct ssd_button *node_try_ssd_button_from_node( + struct wlr_scene_node *wlr_scene_node); + #endif /* LABWC_NODE_DESCRIPTOR_H */ diff --git a/include/ssd-internal.h b/include/ssd-internal.h index d1af095e..22f8ae45 100644 --- a/include/ssd-internal.h +++ b/include/ssd-internal.h @@ -135,23 +135,10 @@ struct ssd { struct border margin; }; -/* - * ssd_part wraps a scene-node with ssd-specific information and can be - * accessed with node_ssd_part_from_node(wlr_scene_node *). - * This allows get_cursor_context() in desktop.c to see which SSD part is under - * the cursor. - */ -struct ssd_part { - enum lab_node_type type; - struct view *view; - - /* This part represented in scene graph */ +struct ssd_button { struct wlr_scene_node *node; - struct wl_listener node_destroy; -}; + enum lab_node_type type; -struct ssd_part_button { - struct ssd_part base; /* * Bitmap of lab_button_state that represents a combination of * hover/toggled/rounded states. @@ -177,13 +164,10 @@ struct wlr_buffer; struct wlr_scene_tree; /* SSD internal helpers to create various SSD elements */ -struct ssd_part *attach_ssd_part(enum lab_node_type type, struct view *view, - struct wlr_scene_node *node); -struct ssd_part_button *attach_ssd_part_button(struct wl_list *button_parts, +struct ssd_button *attach_ssd_button(struct wl_list *button_parts, enum lab_node_type type, struct wlr_scene_tree *parent, struct lab_img *imgs[LAB_BS_ALL + 1], int x, int y, struct view *view); -struct ssd_part_button *button_try_from_ssd_part(struct ssd_part *part); /* SSD internal */ void ssd_titlebar_create(struct ssd *ssd); diff --git a/include/ssd.h b/include/ssd.h index 11fae7ab..7ae9ff1a 100644 --- a/include/ssd.h +++ b/include/ssd.h @@ -25,7 +25,7 @@ struct wlr_cursor; /* Forward declare arguments */ struct server; struct ssd; -struct ssd_part; +struct ssd_button; struct view; struct wlr_scene; struct wlr_scene_node; @@ -55,8 +55,7 @@ void ssd_enable_shade(struct ssd *ssd, bool enable); void ssd_update_hovered_button(struct server *server, struct wlr_scene_node *node); -enum lab_node_type ssd_part_get_type(const struct ssd_part *part); -struct view *ssd_part_get_view(const struct ssd_part *part); +void ssd_button_free(struct ssd_button *button); /* Public SSD helpers */ diff --git a/src/desktop.c b/src/desktop.c index f312c765..bb9b5dfd 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -247,11 +247,11 @@ get_surface_from_layer_node(struct wlr_scene_node *node) { assert(node->data); struct node_descriptor *desc = (struct node_descriptor *)node->data; - if (desc->type == LAB_NODE_DESC_LAYER_SURFACE) { + if (desc->type == LAB_NODE_LAYER_SURFACE) { struct lab_layer_surface *surface; surface = node_layer_surface_from_node(node); return surface->scene_layer_surface->layer_surface->surface; - } else if (desc->type == LAB_NODE_DESC_LAYER_POPUP) { + } else if (desc->type == LAB_NODE_LAYER_POPUP) { struct lab_layer_popup *popup; popup = node_layer_popup_from_node(node); return popup->wlr_popup->base->surface; @@ -299,9 +299,9 @@ get_cursor_context(struct server *server) struct node_descriptor *desc = node->data; if (desc) { switch (desc->type) { - case LAB_NODE_DESC_VIEW: - case LAB_NODE_DESC_XDG_POPUP: - ret.view = desc->data; + case LAB_NODE_VIEW: + case LAB_NODE_XDG_POPUP: + ret.view = desc->view; if (ret.node->type == WLR_SCENE_NODE_BUFFER && lab_wlr_surface_from_node(ret.node)) { ret.type = LAB_NODE_CLIENT; @@ -311,10 +311,43 @@ get_cursor_context(struct server *server) wlr_log(WLR_ERROR, "cursor not on client or ssd"); } return ret; - case LAB_NODE_DESC_SSD_PART: { - struct ssd_part *part = node_ssd_part_from_node(node); + case LAB_NODE_LAYER_SURFACE: ret.node = node; - ret.view = ssd_part_get_view(part); + ret.type = LAB_NODE_LAYER_SURFACE; + ret.surface = get_surface_from_layer_node(node); + return ret; + case LAB_NODE_LAYER_POPUP: + ret.node = node; + ret.type = LAB_NODE_CLIENT; + ret.surface = get_surface_from_layer_node(node); + return ret; + case LAB_NODE_SESSION_LOCK_SURFACE: /* fallthrough */ + case LAB_NODE_IME_POPUP: + ret.type = LAB_NODE_CLIENT; + ret.surface = lab_wlr_surface_from_node(ret.node); + return ret; + case LAB_NODE_MENUITEM: + /* Always return the top scene node for menu items */ + ret.node = node; + ret.type = LAB_NODE_MENUITEM; + return ret; + case LAB_NODE_TREE: + case LAB_NODE_SCALED_BUFFER: + /* Continue to parent node */ + break; + default: + /* + * All other node descriptors (buttons, title, + * etc.) should have an associated view. + */ + if (!desc->view) { + wlr_log(WLR_ERROR, "cursor not on any view " + "(node type %d)", desc->type); + return ret; + } + + ret.node = node; + ret.view = desc->view; /* Detect mouse contexts like Top, Left and TRCorner */ ret.type = ssd_get_resizing_type(ret.view->ssd, cursor); @@ -323,36 +356,11 @@ get_cursor_context(struct server *server) * Otherwise, detect mouse contexts like * Title, Titlebar and Iconify */ - ret.type = ssd_part_get_type(part); + ret.type = desc->type; } return ret; } - case LAB_NODE_DESC_LAYER_SURFACE: - ret.node = node; - ret.type = LAB_NODE_LAYER_SURFACE; - ret.surface = get_surface_from_layer_node(node); - return ret; - case LAB_NODE_DESC_LAYER_POPUP: - ret.node = node; - ret.type = LAB_NODE_CLIENT; - ret.surface = get_surface_from_layer_node(node); - return ret; - case LAB_NODE_DESC_SESSION_LOCK_SURFACE: /* fallthrough */ - case LAB_NODE_DESC_IME_POPUP: - ret.type = LAB_NODE_CLIENT; - ret.surface = lab_wlr_surface_from_node(ret.node); - return ret; - case LAB_NODE_DESC_MENUITEM: - /* Always return the top scene node for menu items */ - ret.node = node; - ret.type = LAB_NODE_MENU; - return ret; - case LAB_NODE_DESC_NODE: - case LAB_NODE_DESC_TREE: - case LAB_NODE_DESC_SCALED_BUFFER: - break; - } } /* Edge-case nodes without node-descriptors */ diff --git a/src/edges.c b/src/edges.c index f59107cf..79532226 100644 --- a/src/edges.c +++ b/src/edges.c @@ -314,7 +314,7 @@ subtract_node_tree(struct wlr_scene_tree *tree, pixman_region32_t *available, } node_desc = node->data; - if (node_desc && node_desc->type == LAB_NODE_DESC_VIEW) { + if (node_desc && node_desc->type == LAB_NODE_VIEW) { view = node_view_from_node(node); if (view != ignored_view) { subtract_view_from_space(view, available); diff --git a/src/input/cursor.c b/src/input/cursor.c index f25bce2d..42757128 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -609,7 +609,7 @@ cursor_process_motion(struct server *server, uint32_t time, double *sx, double * struct cursor_context ctx = get_cursor_context(server); struct seat *seat = &server->seat; - if (ctx.type == LAB_NODE_MENU) { + if (ctx.type == LAB_NODE_MENUITEM) { menu_process_cursor_motion(ctx.node); cursor_set(&server->seat, LAB_CURSOR_DEFAULT); return false; @@ -1173,7 +1173,7 @@ cursor_process_button_release(struct seat *seat, uint32_t button, if (server->input_mode == LAB_INPUT_STATE_MENU) { /* TODO: take into account overflow of time_msec */ if (time_msec - press_msec > rc.menu_ignore_button_release_period) { - if (ctx.type == LAB_NODE_MENU) { + if (ctx.type == LAB_NODE_MENUITEM) { menu_call_selected_actions(server); } else { menu_close_root(server); diff --git a/src/input/ime.c b/src/input/ime.c index f6e0ac99..fb88ee15 100644 --- a/src/input/ime.c +++ b/src/input/ime.c @@ -393,7 +393,8 @@ handle_input_method_new_popup_surface(struct wl_listener *listener, void *data) popup->tree = wlr_scene_subsurface_tree_create( relay->popup_tree, popup->popup_surface->surface); - node_descriptor_create(&popup->tree->node, LAB_NODE_DESC_IME_POPUP, NULL); + node_descriptor_create(&popup->tree->node, LAB_NODE_IME_POPUP, + /*view*/ NULL, /*data*/ NULL); wl_list_insert(&relay->popups, &popup->link); diff --git a/src/layers.c b/src/layers.c index d22c16ad..685f8973 100644 --- a/src/layers.c +++ b/src/layers.c @@ -473,7 +473,7 @@ create_popup(struct server *server, struct wlr_xdg_popup *wlr_popup, wlr_popup->base->surface->data = popup->scene_tree; node_descriptor_create(&popup->scene_tree->node, - LAB_NODE_DESC_LAYER_POPUP, popup); + LAB_NODE_LAYER_POPUP, /*view*/ NULL, popup); popup->destroy.notify = handle_popup_destroy; wl_signal_add(&wlr_popup->events.destroy, &popup->destroy); @@ -624,7 +624,7 @@ handle_new_layer_surface(struct wl_listener *listener, void *data) layer_surface->surface->data = surface->scene_layer_surface->tree; node_descriptor_create(&surface->scene_layer_surface->tree->node, - LAB_NODE_DESC_LAYER_SURFACE, surface); + LAB_NODE_LAYER_SURFACE, /*view*/ NULL, surface); surface->server = server; surface->scene_layer_surface->layer_surface = layer_surface; diff --git a/src/menu/menu.c b/src/menu/menu.c index 84db869c..e55f7626 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -246,8 +246,8 @@ item_create_scene(struct menuitem *menuitem, int *item_y) /* Menu item root node */ menuitem->tree = wlr_scene_tree_create(menu->scene_tree); - node_descriptor_create(&menuitem->tree->node, - LAB_NODE_DESC_MENUITEM, menuitem); + node_descriptor_create(&menuitem->tree->node, LAB_NODE_MENUITEM, + /*view*/ NULL, menuitem); /* Create scenes for unselected/selected states */ menuitem->normal_tree = item_create_scene_for_state(menuitem, @@ -295,8 +295,8 @@ separator_create_scene(struct menuitem *menuitem, int *item_y) /* Menu item root node */ menuitem->tree = wlr_scene_tree_create(menu->scene_tree); - node_descriptor_create(&menuitem->tree->node, - LAB_NODE_DESC_MENUITEM, menuitem); + node_descriptor_create(&menuitem->tree->node, LAB_NODE_MENUITEM, + /*view*/ NULL, menuitem); /* Tree to hold background and line buffer */ menuitem->normal_tree = wlr_scene_tree_create(menuitem->tree); @@ -343,8 +343,8 @@ title_create_scene(struct menuitem *menuitem, int *item_y) /* Menu item root node */ menuitem->tree = wlr_scene_tree_create(menu->scene_tree); - node_descriptor_create(&menuitem->tree->node, - LAB_NODE_DESC_MENUITEM, menuitem); + node_descriptor_create(&menuitem->tree->node, LAB_NODE_MENUITEM, + /*view*/ NULL, menuitem); /* Tree to hold background and text buffer */ menuitem->normal_tree = wlr_scene_tree_create(menuitem->tree); diff --git a/src/node.c b/src/node.c index 7a17c1ed..e5132e7c 100644 --- a/src/node.c +++ b/src/node.c @@ -3,31 +3,29 @@ #include #include #include "common/mem.h" - -static void -descriptor_destroy(struct node_descriptor *node_descriptor) -{ - if (!node_descriptor) { - return; - } - wl_list_remove(&node_descriptor->destroy.link); - free(node_descriptor); -} +#include "ssd.h" static void handle_node_destroy(struct wl_listener *listener, void *data) { struct node_descriptor *node_descriptor = wl_container_of(listener, node_descriptor, destroy); - descriptor_destroy(node_descriptor); + + if (node_type_contains(LAB_NODE_BUTTON, node_descriptor->type)) { + ssd_button_free(node_descriptor->data); + } + + wl_list_remove(&node_descriptor->destroy.link); + free(node_descriptor); } void node_descriptor_create(struct wlr_scene_node *scene_node, - enum node_descriptor_type type, void *data) + enum lab_node_type type, struct view *view, void *data) { struct node_descriptor *node_descriptor = znew(*node_descriptor); node_descriptor->type = type; + node_descriptor->view = view; node_descriptor->data = data; node_descriptor->destroy.notify = handle_node_destroy; wl_signal_add(&scene_node->events.destroy, &node_descriptor->destroy); @@ -39,9 +37,7 @@ 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; + return node_descriptor->view; } struct lab_layer_surface * @@ -49,7 +45,7 @@ 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); + assert(node_descriptor->type == LAB_NODE_LAYER_SURFACE); return (struct lab_layer_surface *)node_descriptor->data; } @@ -58,7 +54,7 @@ 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); + assert(node_descriptor->type == LAB_NODE_LAYER_POPUP); return (struct lab_layer_popup *)node_descriptor->data; } @@ -67,24 +63,28 @@ 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); + assert(node_descriptor->type == LAB_NODE_MENUITEM); return (struct menuitem *)node_descriptor->data; } -struct ssd_part * -node_ssd_part_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_SSD_PART); - return (struct ssd_part *)node_descriptor->data; -} - struct scaled_buffer * node_scaled_buffer_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_SCALED_BUFFER); + assert(node_descriptor->type == LAB_NODE_SCALED_BUFFER); return (struct scaled_buffer *)node_descriptor->data; } + +struct ssd_button * +node_try_ssd_button_from_node(struct wlr_scene_node *wlr_scene_node) +{ + assert(wlr_scene_node->data); + struct node_descriptor *node_descriptor = wlr_scene_node->data; + + if (node_type_contains(LAB_NODE_BUTTON, node_descriptor->type)) { + return (struct ssd_button *)node_descriptor->data; + } + + return NULL; +} diff --git a/src/output.c b/src/output.c index 7715b899..c5c88dfc 100644 --- a/src/output.c +++ b/src/output.c @@ -511,17 +511,17 @@ handle_new_output(struct wl_listener *listener, void *data) output->layer_tree[i] = wlr_scene_tree_create(&server->scene->tree); node_descriptor_create(&output->layer_tree[i]->node, - LAB_NODE_DESC_TREE, NULL); + LAB_NODE_TREE, /*view*/ NULL, /*data*/ NULL); } output->layer_popup_tree = wlr_scene_tree_create(&server->scene->tree); node_descriptor_create(&output->layer_popup_tree->node, - LAB_NODE_DESC_TREE, NULL); + LAB_NODE_TREE, /*view*/ NULL, /*data*/ NULL); output->osd_tree = wlr_scene_tree_create(&server->scene->tree); node_descriptor_create(&output->osd_tree->node, - LAB_NODE_DESC_TREE, NULL); + LAB_NODE_TREE, /*view*/ NULL, /*data*/ NULL); output->session_lock_tree = wlr_scene_tree_create(&server->scene->tree); node_descriptor_create(&output->session_lock_tree->node, - LAB_NODE_DESC_TREE, NULL); + LAB_NODE_TREE, /*view*/ NULL, /*data*/ NULL); /* * Set the z-positions to achieve the following order (from top to diff --git a/src/scaled-buffer/scaled-buffer.c b/src/scaled-buffer/scaled-buffer.c index c478ed00..c4a3219c 100644 --- a/src/scaled-buffer/scaled-buffer.c +++ b/src/scaled-buffer/scaled-buffer.c @@ -198,7 +198,7 @@ scaled_buffer_create(struct wlr_scene_tree *parent, return NULL; } node_descriptor_create(&self->scene_buffer->node, - LAB_NODE_DESC_SCALED_BUFFER, self); + LAB_NODE_SCALED_BUFFER, /*view*/ NULL, self); self->impl = impl; /* diff --git a/src/session-lock.c b/src/session-lock.c index 1dce3b6a..277233cc 100644 --- a/src/session-lock.c +++ b/src/session-lock.c @@ -135,7 +135,7 @@ handle_new_surface(struct wl_listener *listener, void *data) struct wlr_scene_tree *surface_tree = wlr_scene_subsurface_tree_create(lock_output->tree, lock_surface->surface); node_descriptor_create(&surface_tree->node, - LAB_NODE_DESC_SESSION_LOCK_SURFACE, NULL); + LAB_NODE_SESSION_LOCK_SURFACE, /*view*/ NULL, /*data*/ NULL); lock_output->surface_destroy.notify = handle_surface_destroy; wl_signal_add(&lock_surface->events.destroy, &lock_output->surface_destroy); diff --git a/src/ssd/meson.build b/src/ssd/meson.build index c37eef45..a316409b 100644 --- a/src/ssd/meson.build +++ b/src/ssd/meson.build @@ -1,7 +1,7 @@ labwc_sources += files( 'resize-indicator.c', 'ssd.c', - 'ssd-part.c', + 'ssd-button.c', 'ssd-titlebar.c', 'ssd-border.c', 'ssd-extents.c', diff --git a/src/ssd/ssd-part.c b/src/ssd/ssd-button.c similarity index 61% rename from src/ssd/ssd-part.c rename to src/ssd/ssd-button.c index 00cfe7af..af209f89 100644 --- a/src/ssd/ssd-part.c +++ b/src/ssd/ssd-button.c @@ -7,55 +7,13 @@ #include "node.h" #include "scaled-buffer/scaled-icon-buffer.h" #include "scaled-buffer/scaled-img-buffer.h" +#include "ssd.h" #include "ssd-internal.h" -/* Internal helpers */ -static void -handle_node_destroy(struct wl_listener *listener, void *data) -{ - struct ssd_part *part = wl_container_of(listener, part, node_destroy); - wl_list_remove(&part->node_destroy.link); - - struct ssd_part_button *button = button_try_from_ssd_part(part); - if (button) { - wl_list_remove(&button->link); - } - - free(part); -} - /* Internal API */ -/* - * Create a new node_descriptor containing a link to a new ssd_part struct. - * Both will be destroyed automatically once the scene_node they are attached - * to is destroyed. - */ -static void -init_ssd_part(struct ssd_part *part, enum lab_node_type type, - struct view *view, struct wlr_scene_node *node) -{ - part->type = type; - part->node = node; - part->view = view; - - node_descriptor_create(node, LAB_NODE_DESC_SSD_PART, part); - part->node_destroy.notify = handle_node_destroy; - wl_signal_add(&node->events.destroy, &part->node_destroy); -} - -struct ssd_part * -attach_ssd_part(enum lab_node_type type, struct view *view, - struct wlr_scene_node *node) -{ - assert(!node_type_contains(LAB_NODE_BUTTON, type)); - struct ssd_part *part = znew(*part); - init_ssd_part(part, type, view, node); - return part; -} - -struct ssd_part_button * -attach_ssd_part_button(struct wl_list *button_parts, enum lab_node_type type, +struct ssd_button * +attach_ssd_button(struct wl_list *button_parts, enum lab_node_type type, struct wlr_scene_tree *parent, struct lab_img *imgs[LAB_BS_ALL + 1], int x, int y, struct view *view) @@ -64,8 +22,10 @@ attach_ssd_part_button(struct wl_list *button_parts, enum lab_node_type type, wlr_scene_node_set_position(&root->node, x, y); assert(node_type_contains(LAB_NODE_BUTTON, type)); - struct ssd_part_button *button = znew(*button); - init_ssd_part(&button->base, type, view, &root->node); + struct ssd_button *button = znew(*button); + button->node = &root->node; + button->type = type; + node_descriptor_create(&root->node, type, view, button); wl_list_append(button_parts, &button->link); /* Hitbox */ @@ -117,11 +77,9 @@ attach_ssd_part_button(struct wl_list *button_parts, enum lab_node_type type, return button; } -struct ssd_part_button * -button_try_from_ssd_part(struct ssd_part *part) +/* called from node descriptor destroy */ +void ssd_button_free(struct ssd_button *button) { - if (node_type_contains(LAB_NODE_BUTTON, part->type)) { - return (struct ssd_part_button *)part; - } - return NULL; + wl_list_remove(&button->link); + free(button); } diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 05cbdb9a..84dd527a 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -34,7 +34,8 @@ ssd_titlebar_create(struct ssd *ssd) int corner_width = ssd_get_corner_width(); ssd->titlebar.tree = wlr_scene_tree_create(ssd->tree); - attach_ssd_part(LAB_NODE_TITLEBAR, view, &ssd->titlebar.tree->node); + node_descriptor_create(&ssd->titlebar.tree->node, + LAB_NODE_TITLEBAR, view, /*data*/ NULL); enum ssd_active_state active; FOR_EACH_ACTIVE_STATE(active) { @@ -78,8 +79,8 @@ ssd_titlebar_create(struct ssd *ssd) subtree->tree, theme->titlebar_height, theme->window[active].titlebar_pattern); assert(subtree->title); - attach_ssd_part(LAB_NODE_TITLE, - view, &subtree->title->scene_buffer->node); + node_descriptor_create(&subtree->title->scene_buffer->node, + LAB_NODE_TITLE, view, /*data*/ NULL); /* Buttons */ struct title_button *b; @@ -94,7 +95,7 @@ ssd_titlebar_create(struct ssd *ssd) wl_list_for_each(b, &rc.title_buttons_left, link) { struct lab_img **imgs = theme->window[active].button_imgs[b->type]; - attach_ssd_part_button(&subtree->buttons_left, b->type, parent, + attach_ssd_button(&subtree->buttons_left, b->type, parent, imgs, x, y, view); x += theme->window_button_width + theme->window_button_spacing; } @@ -104,7 +105,7 @@ ssd_titlebar_create(struct ssd *ssd) x -= theme->window_button_width + theme->window_button_spacing; struct lab_img **imgs = theme->window[active].button_imgs[b->type]; - attach_ssd_part_button(&subtree->buttons_right, b->type, parent, + attach_ssd_button(&subtree->buttons_right, b->type, parent, imgs, x, y, view); } } @@ -134,7 +135,7 @@ ssd_titlebar_create(struct ssd *ssd) } static void -update_button_state(struct ssd_part_button *button, enum lab_button_state state, +update_button_state(struct ssd_button *button, enum lab_button_state state, bool enable) { if (enable) { @@ -177,7 +178,7 @@ set_squared_corners(struct ssd *ssd, bool enable) wlr_scene_node_set_enabled(&subtree->corner_right->node, !enable); /* (Un)round the corner buttons */ - struct ssd_part_button *button; + struct ssd_button *button; wl_list_for_each(button, &subtree->buttons_left, link) { update_button_state(button, LAB_BS_ROUNDED, !enable); break; @@ -196,15 +197,15 @@ set_alt_button_icon(struct ssd *ssd, enum lab_node_type type, bool enable) FOR_EACH_ACTIVE_STATE(active) { struct ssd_titlebar_subtree *subtree = &ssd->titlebar.subtrees[active]; - struct ssd_part_button *button; + struct ssd_button *button; wl_list_for_each(button, &subtree->buttons_left, link) { - if (button->base.type == type) { + if (button->type == type) { update_button_state(button, LAB_BS_TOGGLED, enable); } } wl_list_for_each(button, &subtree->buttons_right, link) { - if (button->base.type == type) { + if (button->type == type) { update_button_state(button, LAB_BS_TOGGLED, enable); } @@ -251,16 +252,16 @@ update_visible_buttons(struct ssd *ssd) struct ssd_titlebar_subtree *subtree = &ssd->titlebar.subtrees[active]; int button_count = 0; - struct ssd_part_button *button; + struct ssd_button *button; wl_list_for_each(button, &subtree->buttons_left, link) { - wlr_scene_node_set_enabled(button->base.node, + wlr_scene_node_set_enabled(button->node, button_count < button_count_left); button_count++; } button_count = 0; wl_list_for_each(button, &subtree->buttons_right, link) { - wlr_scene_node_set_enabled(button->base.node, + wlr_scene_node_set_enabled(button->node, button_count < button_count_right); button_count++; } @@ -317,9 +318,9 @@ ssd_titlebar_update(struct ssd *ssd) MAX(width - bg_offset * 2, 0), theme->titlebar_height); x = theme->window_titlebar_padding_width; - struct ssd_part_button *button; + struct ssd_button *button; wl_list_for_each(button, &subtree->buttons_left, link) { - wlr_scene_node_set_position(button->base.node, x, y); + wlr_scene_node_set_position(button->node, x, y); x += theme->window_button_width + theme->window_button_spacing; } @@ -330,7 +331,7 @@ ssd_titlebar_update(struct ssd *ssd) x = width - theme->window_titlebar_padding_width + theme->window_button_spacing; wl_list_for_each(button, &subtree->buttons_right, link) { x -= theme->window_button_width + theme->window_button_spacing; - wlr_scene_node_set_position(button->base.node, x, y); + wlr_scene_node_set_position(button->node, x, y); } } @@ -419,14 +420,14 @@ get_title_offsets(struct ssd *ssd, int *offset_left, int *offset_right) *offset_left = padding_width; *offset_right = padding_width; - struct ssd_part_button *button; + struct ssd_button *button; wl_list_for_each(button, &subtree->buttons_left, link) { - if (button->base.node->enabled) { + if (button->node->enabled) { *offset_left += button_width + button_spacing; } } wl_list_for_each(button, &subtree->buttons_right, link) { - if (button->base.node->enabled) { + if (button->node->enabled) { *offset_right += button_width + button_spacing; } } @@ -494,17 +495,13 @@ ssd_update_title(struct ssd *ssd) void ssd_update_hovered_button(struct server *server, struct wlr_scene_node *node) { - struct ssd_part_button *button = NULL; + struct ssd_button *button = NULL; if (node && node->data) { - struct node_descriptor *desc = node->data; - if (desc->type == LAB_NODE_DESC_SSD_PART) { - button = button_try_from_ssd_part( - node_ssd_part_from_node(node)); - if (button == server->hovered_button) { - /* Cursor is still on the same button */ - return; - } + button = node_try_ssd_button_from_node(node); + if (button == server->hovered_button) { + /* Cursor is still on the same button */ + return; } } diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index 67d5966b..afdbcdf9 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -14,6 +14,7 @@ #include "common/scene-helpers.h" #include "config/rcxml.h" #include "labwc.h" +#include "node.h" #include "ssd-internal.h" #include "theme.h" #include "view.h" @@ -145,7 +146,8 @@ ssd_create(struct view *view, bool active) ssd->view = view; ssd->tree = wlr_scene_tree_create(view->scene_tree); - attach_ssd_part(LAB_NODE_NONE, view, &ssd->tree->node); + node_descriptor_create(&ssd->tree->node, + LAB_NODE_NONE, view, /*data*/ NULL); wlr_scene_node_lower_to_bottom(&ssd->tree->node); ssd->titlebar.height = view->server->theme->titlebar_height; ssd_shadow_create(ssd); @@ -265,7 +267,8 @@ ssd_destroy(struct ssd *ssd) /* Maybe reset hover view */ struct view *view = ssd->view; struct server *server = view->server; - if (server->hovered_button && server->hovered_button->base.view == view) { + if (server->hovered_button && node_view_from_node( + server->hovered_button->node) == view) { server->hovered_button = NULL; } @@ -343,18 +346,6 @@ ssd_enable_keybind_inhibit_indicator(struct ssd *ssd, bool enable) wlr_scene_rect_set_color(ssd->border.subtrees[SSD_ACTIVE].top, color); } -enum lab_node_type -ssd_part_get_type(const struct ssd_part *part) -{ - return part ? part->type : LAB_NODE_NONE; -} - -struct view * -ssd_part_get_view(const struct ssd_part *part) -{ - return part ? part->view : NULL; -} - bool ssd_debug_is_root_node(const struct ssd *ssd, struct wlr_scene_node *node) { diff --git a/src/xdg-popup.c b/src/xdg-popup.c index cc14d920..722a32ff 100644 --- a/src/xdg-popup.c +++ b/src/xdg-popup.c @@ -164,5 +164,5 @@ xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup) wlr_popup->base->surface->data = wlr_scene_xdg_surface_create(parent_tree, wlr_popup->base); node_descriptor_create(wlr_popup->base->surface->data, - LAB_NODE_DESC_XDG_POPUP, view); + LAB_NODE_XDG_POPUP, view, /*data*/ NULL); } diff --git a/src/xdg.c b/src/xdg.c index 00c4e226..f25a0e84 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -1023,7 +1023,7 @@ handle_new_xdg_toplevel(struct wl_listener *listener, void *data) } view->content_tree = tree; node_descriptor_create(&view->scene_tree->node, - LAB_NODE_DESC_VIEW, view); + LAB_NODE_VIEW, view, /*data*/ NULL); /* * The xdg_toplevel_decoration and kde_server_decoration protocols diff --git a/src/xwayland.c b/src/xwayland.c index 9776dd1d..2127bd52 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -1090,7 +1090,8 @@ xwayland_view_create(struct server *server, view->workspace = server->workspaces.current; view->scene_tree = wlr_scene_tree_create(view->workspace->tree); - node_descriptor_create(&view->scene_tree->node, LAB_NODE_DESC_VIEW, view); + node_descriptor_create(&view->scene_tree->node, + LAB_NODE_VIEW, view, /*data*/ NULL); CONNECT_SIGNAL(xsurface, view, destroy); CONNECT_SIGNAL(xsurface, view, request_minimize); From d14f5ca2280f83ff3ca910d9d726cce0911927db Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Sat, 6 Sep 2025 14:04:21 -0400 Subject: [PATCH 146/248] ssd: update comments referencing ssd_part/ssd_part_type --- include/input/cursor.h | 8 ++++---- include/ssd-internal.h | 25 ++++++++++++------------- include/theme.h | 4 ++-- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/include/input/cursor.h b/include/input/cursor.h index 8dd8a274..e6cfb448 100644 --- a/include/input/cursor.h +++ b/include/input/cursor.h @@ -41,25 +41,25 @@ struct cursor_context { * * Behavior if node points to a surface: * - If surface is a layer-surface, type will be - * set to LAB_SSD_LAYER_SURFACE and view will be NULL. + * set to LAB_NODE_LAYER_SURFACE and view will be NULL. * * - If surface is a 'lost' unmanaged xsurface (one * with a never-mapped parent view), type will - * be set to LAB_SSD_UNMANAGED and view will be NULL. + * be set to LAB_NODE_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 type to be set to - * LAB_SSD_CLIENT and return the attached view. + * LAB_NODE_CLIENT and return the attached view. * * Behavior if node points to internal elements: * - type will be set to the appropriate 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, - * type will be set to LAB_SSD_ROOT and view will be NULL. + * type will be set to LAB_NODE_ROOT and view will be NULL. * */ struct cursor_context get_cursor_context(struct server *server); diff --git a/include/ssd-internal.h b/include/ssd-internal.h index 22f8ae45..db8d02a6 100644 --- a/include/ssd-internal.h +++ b/include/ssd-internal.h @@ -13,21 +13,22 @@ struct ssd_state_title_width { }; /* - * The scene-graph of SSD looks like below. The parentheses indicate the type of - * ssd_part attached to the node. + * The scene-graph of SSD looks like below. The parentheses indicate the + * type of each node (enum lab_node_type, stored in the node_descriptor + * attached to the wlr_scene_node). * - * ssd->tree (LAB_SSD_NONE) - * +--titlebar (LAB_SSD_PART_TITLEBAR) + * ssd->tree (LAB_NODE_NONE) + * +--titlebar (LAB_NODE_TITLEBAR) * | +--inactive * | | +--background bar * | | +--left corner * | | +--right corner - * | | +--title (LAB_SSD_PART_TITLE) - * | | +--iconify button (LAB_SSD_BUTTON_ICONIFY) + * | | +--title (LAB_NODE_TITLE) + * | | +--iconify button (LAB_NODE_BUTTON_ICONIFY) * | | | +--normal close icon image * | | | +--hovered close icon image * | | | +--... - * | | +--window icon (LAB_SSD_BUTTON_WINDOW_ICON) + * | | +--window icon (LAB_NODE_BUTTON_WINDOW_ICON) * | | | +--window icon image * | | +--... * | +--active @@ -103,9 +104,8 @@ struct ssd { struct wlr_scene_buffer *corner_right; struct wlr_scene_buffer *bar; struct scaled_font_buffer *title; - /* List of ssd_parts that represent buttons */ - struct wl_list buttons_left; - struct wl_list buttons_right; + struct wl_list buttons_left; /* ssd_button.link */ + struct wl_list buttons_right; /* ssd_button.link */ } subtrees[2]; /* indexed by enum ssd_active_state */ } titlebar; @@ -149,9 +149,8 @@ struct ssd_button { * img_buffers[state_set] is displayed. Some of these can be NULL * (e.g. img_buffers[LAB_BS_ROUNDED] is set only for corner buttons). * - * When the type of ssd_part pointing to ssd_button is - * LAB_SSD_BUTTON_WINDOW_ICON, these are all NULL and window_icon is - * used instead. + * When the button type is LAB_NODE_BUTTON_WINDOW_ICON, + * these are all NULL and window_icon is used instead. */ struct scaled_img_buffer *img_buffers[LAB_BS_ALL + 1]; diff --git a/include/theme.h b/include/theme.h index 4e9f4705..0df661e5 100644 --- a/include/theme.h +++ b/include/theme.h @@ -104,9 +104,9 @@ struct theme { * The texture of a window buttons for each hover/toggled/rounded * state. This can be accessed like: * - * buttons[LAB_SSD_BUTTON_ICONIFY][LAB_BS_HOVERED | LAB_BS_TOGGLED] + * buttons[LAB_NODE_BUTTON_ICONIFY][LAB_BS_HOVERED | LAB_BS_TOGGLED] * - * Elements in buttons[0] are all NULL since LAB_SSD_BUTTON_FIRST is 1. + * Elements in buttons[0] are all NULL since LAB_NODE_BUTTON_FIRST is 1. */ struct lab_img *button_imgs [LAB_NODE_BUTTON_LAST + 1][LAB_BS_ALL + 1]; From 2b26a2c73f6a65ff4ced4d2929d91792090910aa Mon Sep 17 00:00:00 2001 From: Weblate Date: Sat, 6 Sep 2025 06:27:30 +0200 Subject: [PATCH 147/248] Translation updates from weblate Co-authored-by: virtual-hand Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/pt_BR/ Translation: Labwc/labwc --- po/pt_BR.po | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/po/pt_BR.po b/po/pt_BR.po index 678915b6..89a23170 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -8,9 +8,10 @@ msgstr "" "Project-Id-Version: labwc\n" "Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" "POT-Creation-Date: 2024-09-19 21:09+1000\n" -"PO-Revision-Date: 2025-02-04 21:01+0000\n" -"Last-Translator: EggSupernova \n" -"Language-Team: Portuguese (Brazil) \n" +"PO-Revision-Date: 2025-09-06 04:27+0000\n" +"Last-Translator: virtual-hand \n" +"Language-Team: Portuguese (Brazil) \n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -24,7 +25,7 @@ msgstr "Vá para..." #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "" +msgstr "Terminal" #: src/menu/menu.c:1040 msgid "Reconfigure" From a29bf5366fbbe0c42c1cb8ff445ecb162c7198cd Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Thu, 21 Aug 2025 00:26:27 -0400 Subject: [PATCH 148/248] idle_manager: remove unused parameter and field wlr_seat isn't used anywhere. --- include/idle.h | 2 +- src/idle.c | 4 +--- src/server.c | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/include/idle.h b/include/idle.h index 50a660c2..845fdf41 100644 --- a/include/idle.h +++ b/include/idle.h @@ -5,7 +5,7 @@ struct wl_display; struct wlr_seat; -void idle_manager_create(struct wl_display *display, struct wlr_seat *wlr_seat); +void idle_manager_create(struct wl_display *display); void idle_manager_notify_activity(struct wlr_seat *seat); #endif /* LABWC_IDLE_H */ diff --git a/src/idle.c b/src/idle.c index 611fe938..b8e96c2d 100644 --- a/src/idle.c +++ b/src/idle.c @@ -19,7 +19,6 @@ struct lab_idle_manager { struct wl_listener on_new_inhibitor; struct wl_listener on_destroy; } inhibitor; - struct wlr_seat *wlr_seat; }; static struct lab_idle_manager *manager; @@ -67,11 +66,10 @@ handle_inhibitor_manager_destroy(struct wl_listener *listener, void *data) } void -idle_manager_create(struct wl_display *display, struct wlr_seat *wlr_seat) +idle_manager_create(struct wl_display *display) { assert(!manager); manager = znew(*manager); - manager->wlr_seat = wlr_seat; manager->ext = wlr_idle_notifier_v1_create(display); diff --git a/src/server.c b/src/server.c index f7273c12..7560986d 100644 --- a/src/server.c +++ b/src/server.c @@ -661,7 +661,7 @@ server_init(struct server *server) wlr_fractional_scale_manager_v1_create(server->wl_display, LAB_WLR_FRACTIONAL_SCALE_V1_VERSION); - idle_manager_create(server->wl_display, server->seat.seat); + idle_manager_create(server->wl_display); server->relative_pointer_manager = wlr_relative_pointer_manager_v1_create( server->wl_display); From e9f17dc4a20d8e9533849812e55c4551c21b7968 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Wed, 3 Sep 2025 00:00:01 -0400 Subject: [PATCH 149/248] array: use die_if_null() from common/mem.c --- include/common/array.h | 9 +++------ include/common/mem.h | 6 ++++++ src/common/mem.c | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/include/common/array.h b/include/common/array.h index a1ea7499..b60bafd6 100644 --- a/include/common/array.h +++ b/include/common/array.h @@ -1,9 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0-only */ #ifndef LABWC_ARRAY_H #define LABWC_ARRAY_H -#include -#include + #include +#include "common/mem.h" /* * Wayland's wl_array API is a bit sparse consisting only of @@ -66,10 +66,7 @@ wl_array_len(struct wl_array *array) #define array_add(_arr, _val) do { \ __typeof__(_val) *_entry = wl_array_add( \ (_arr), sizeof(__typeof__(_val))); \ - if (!_entry) { \ - perror("Failed to allocate memory"); \ - exit(EXIT_FAILURE); \ - } \ + die_if_null(_entry); \ *_entry = (_val); \ } while (0) diff --git a/include/common/mem.h b/include/common/mem.h index d0d5fdc5..ba19c1d6 100644 --- a/include/common/mem.h +++ b/include/common/mem.h @@ -4,6 +4,12 @@ #include +/* + * If ptr is NULL, prints an error (based on errno) and exits. + * Suitable for checking the return value of malloc() etc. + */ +void die_if_null(void *ptr); + /* * As defined in busybox, weston, etc. * Allocates zero-filled memory; calls exit() on error. diff --git a/src/common/mem.c b/src/common/mem.c index a7289ea0..72ba3324 100644 --- a/src/common/mem.c +++ b/src/common/mem.c @@ -5,7 +5,7 @@ #include #include -static void +void die_if_null(void *ptr) { if (!ptr) { From 7c185c1a8520684369bd73d1aacd1465e1cc9c44 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Tue, 2 Sep 2025 23:02:27 -0400 Subject: [PATCH 150/248] include: reduce unnecessary #includes --- include/common/nodename.h | 1 - include/config/rcxml.h | 3 ++- include/img/img.h | 1 - include/input/ime.h | 1 - include/input/tablet.h | 2 +- include/placement.h | 3 ++- include/resistance.h | 6 +++++- include/scaled-buffer/scaled-img-buffer.h | 2 -- include/snap.h | 4 ++-- include/ssd.h | 1 - src/action.c | 1 + src/config/rcxml.c | 1 + src/input/ime.c | 1 + src/osd/osd-field.c | 1 + src/resize-outlines.c | 1 + 15 files changed, 17 insertions(+), 12 deletions(-) diff --git a/include/common/nodename.h b/include/common/nodename.h index f05c1283..89a4fb1a 100644 --- a/include/common/nodename.h +++ b/include/common/nodename.h @@ -4,7 +4,6 @@ #include #include -#include /** * nodename - give xml node an ascii name diff --git a/include/config/rcxml.h b/include/config/rcxml.h index 42b60f10..33c49376 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -8,7 +8,6 @@ #include #include "common/border.h" -#include "common/buf.h" #include "common/font.h" #include "common/node-type.h" #include "config/types.h" @@ -42,6 +41,8 @@ enum tiling_events_mode { (LAB_TILING_EVENTS_REGION | LAB_TILING_EVENTS_EDGE), }; +struct buf; + struct button_map_entry { uint32_t from; uint32_t to; diff --git a/include/img/img.h b/include/img/img.h index f299335e..82c695b6 100644 --- a/include/img/img.h +++ b/include/img/img.h @@ -4,7 +4,6 @@ #include #include -#include #include enum lab_img_type { diff --git a/include/input/ime.h b/include/input/ime.h index fcd292ce..ebf30c36 100644 --- a/include/input/ime.h +++ b/include/input/ime.h @@ -5,7 +5,6 @@ #include #include -#include "labwc.h" struct keyboard; diff --git a/include/input/tablet.h b/include/input/tablet.h index e6c1373e..094dad9a 100644 --- a/include/input/tablet.h +++ b/include/input/tablet.h @@ -4,7 +4,7 @@ #include #include -#include "config/tablet-tool.h" +#include "config/types.h" struct seat; struct wlr_device; diff --git a/include/placement.h b/include/placement.h index 99f1ca28..5210117e 100644 --- a/include/placement.h +++ b/include/placement.h @@ -4,7 +4,8 @@ #include #include -#include "view.h" + +struct view; bool placement_find_best(struct view *view, struct wlr_box *geometry); diff --git a/include/resistance.h b/include/resistance.h index 6a155ceb..0a6c2783 100644 --- a/include/resistance.h +++ b/include/resistance.h @@ -1,7 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-only */ #ifndef LABWC_RESISTANCE_H #define LABWC_RESISTANCE_H -#include "labwc.h" + +#include +#include + +struct view; /** * resistance_unsnap_apply() - Apply resistance when dragging a diff --git a/include/scaled-buffer/scaled-img-buffer.h b/include/scaled-buffer/scaled-img-buffer.h index 48cbd9c4..23fb9e17 100644 --- a/include/scaled-buffer/scaled-img-buffer.h +++ b/include/scaled-buffer/scaled-img-buffer.h @@ -2,8 +2,6 @@ #ifndef LABWC_SCALED_IMG_BUFFER_H #define LABWC_SCALED_IMG_BUFFER_H -#include - struct wlr_scene_tree; struct wlr_scene_node; struct wlr_scene_buffer; diff --git a/include/snap.h b/include/snap.h index f5c2e229..09bf6baf 100644 --- a/include/snap.h +++ b/include/snap.h @@ -2,9 +2,9 @@ #ifndef LABWC_SNAP_H #define LABWC_SNAP_H -#include "common/border.h" -#include "view.h" +#include "common/edge.h" +struct view; struct wlr_box; void snap_move_to_edge(struct view *view, diff --git a/include/ssd.h b/include/ssd.h index 7ae9ff1a..1340c8dc 100644 --- a/include/ssd.h +++ b/include/ssd.h @@ -2,7 +2,6 @@ #ifndef LABWC_SSD_H #define LABWC_SSD_H -#include "common/border.h" #include "common/node-type.h" #include "config/types.h" diff --git a/src/action.c b/src/action.c index 69e4e66b..d02bf541 100644 --- a/src/action.c +++ b/src/action.c @@ -9,6 +9,7 @@ #include #include #include "action-prompt-codes.h" +#include "common/buf.h" #include "common/macros.h" #include "common/list.h" #include "common/mem.h" diff --git a/src/config/rcxml.c b/src/config/rcxml.c index a2b31922..08a70edf 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -16,6 +16,7 @@ #include #include #include "action.h" +#include "common/buf.h" #include "common/dir.h" #include "common/list.h" #include "common/macros.h" diff --git a/src/input/ime.c b/src/input/ime.c index fb88ee15..36bc8252 100644 --- a/src/input/ime.c +++ b/src/input/ime.c @@ -8,6 +8,7 @@ #include #include "common/mem.h" #include "input/keyboard.h" +#include "labwc.h" #include "node.h" #include "output.h" #include "view.h" diff --git a/src/osd/osd-field.c b/src/osd/osd-field.c index c27b9379..c8f796d1 100644 --- a/src/osd/osd-field.c +++ b/src/osd/osd-field.c @@ -2,6 +2,7 @@ #include #include #include +#include "common/buf.h" #include "common/mem.h" #include "config/rcxml.h" #include "view.h" diff --git a/src/resize-outlines.c b/src/resize-outlines.c index a32351ac..fdb8baa1 100644 --- a/src/resize-outlines.c +++ b/src/resize-outlines.c @@ -2,6 +2,7 @@ #include "resize-outlines.h" #include +#include "common/border.h" #include "common/lab-scene-rect.h" #include "labwc.h" #include "resize-indicator.h" From b00873a988d7a94a94a62351735117b53761f194 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Tue, 2 Sep 2025 23:19:38 -0400 Subject: [PATCH 151/248] src: remove unused #includes (via include-what-you-use) --- clients/labnag.c | 5 ----- clients/pool-buffer.c | 4 ---- src/common/box.c | 1 - src/common/dir.c | 3 --- src/common/font.c | 4 ---- src/common/graphic-helpers.c | 2 +- src/common/parse-double.c | 1 - src/common/scene-helpers.c | 4 ---- src/common/spawn.c | 1 - src/config/keybind.c | 1 - src/config/mousebind.c | 1 - src/config/rcxml.c | 4 ---- src/config/session.c | 2 -- src/config/touch.c | 1 - src/desktop-entry.c | 2 -- src/desktop.c | 3 --- src/edges.c | 1 - src/img/img-png.c | 2 -- src/img/img-svg.c | 3 --- src/img/img-xbm.c | 3 --- src/img/img.c | 1 - src/input/cursor.c | 4 ---- src/input/ime.c | 1 - src/input/key-state.c | 2 -- src/input/keyboard.c | 2 -- src/input/tablet-pad.c | 2 -- src/input/tablet.c | 2 -- src/interactive.c | 1 - src/layers.c | 2 -- src/menu/menu.c | 2 -- src/osd/osd-classic.c | 8 ++------ src/osd/osd-thumbnail.c | 1 - src/osd/osd.c | 6 +----- src/output.c | 3 --- src/protocols/cosmic_workspaces/cosmic-workspaces.c | 1 - src/protocols/ext-workspace/ext-workspace.c | 2 -- src/regions.c | 2 -- src/resistance.c | 2 -- src/scaled-buffer/scaled-font-buffer.c | 2 -- src/scaled-buffer/scaled-icon-buffer.c | 1 - src/scaled-buffer/scaled-img-buffer.c | 3 --- src/server.c | 3 --- src/snap-constraints.c | 2 +- src/ssd/ssd-border.c | 1 - src/ssd/ssd-extents.c | 2 -- src/ssd/ssd-shadow.c | 1 - src/ssd/ssd-titlebar.c | 3 --- src/ssd/ssd.c | 1 - src/theme.c | 1 - src/view-impl-common.c | 1 - src/view.c | 2 -- src/window-rules.c | 4 ---- 52 files changed, 5 insertions(+), 114 deletions(-) diff --git a/clients/labnag.c b/clients/labnag.c index 92eda9cb..20fb1dbe 100644 --- a/clients/labnag.c +++ b/clients/labnag.c @@ -16,17 +16,14 @@ #include #include #include -#include #ifdef __FreeBSD__ #include /* For signalfd() */ #endif #include -#include #include #include #include #include -#include #include #include #include "action-prompt-codes.h" @@ -62,8 +59,6 @@ struct conf { ssize_t button_padding; }; -struct nag; - struct pointer { struct wl_pointer *pointer; uint32_t serial; diff --git a/clients/pool-buffer.c b/clients/pool-buffer.c index 5dabc8aa..35e86033 100644 --- a/clients/pool-buffer.c +++ b/clients/pool-buffer.c @@ -5,18 +5,14 @@ * Copyright (C) 2016-2017 Drew DeVault */ #define _POSIX_C_SOURCE 200809L -#include #include #include #include #include #include -#include -#include #include #include #include -#include #include "pool-buffer.h" static int anonymous_shm_open(void) diff --git a/src/common/box.c b/src/common/box.c index 84db600d..2520f733 100644 --- a/src/common/box.c +++ b/src/common/box.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only #include "common/box.h" -#include #include "common/macros.h" bool diff --git a/src/common/dir.c b/src/common/dir.c index a76d5c74..76b4e65e 100644 --- a/src/common/dir.c +++ b/src/common/dir.c @@ -7,16 +7,13 @@ #include "common/dir.h" #include #include -#include #include #include -#include #include "common/buf.h" #include "common/list.h" #include "common/mem.h" #include "common/string-helpers.h" #include "config/rcxml.h" -#include "labwc.h" struct dir { const char *prefix; diff --git a/src/common/font.c b/src/common/font.c index 8823f445..81784ea8 100644 --- a/src/common/font.c +++ b/src/common/font.c @@ -1,14 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-only #include "common/font.h" #include -#include #include -#include -#include #include #include "common/graphic-helpers.h" #include "common/string-helpers.h" -#include "labwc.h" #include "buffer.h" PangoFontDescription * diff --git a/src/common/graphic-helpers.c b/src/common/graphic-helpers.c index 602507a9..bc0adbc4 100644 --- a/src/common/graphic-helpers.c +++ b/src/common/graphic-helpers.c @@ -3,7 +3,7 @@ #include "common/graphic-helpers.h" #include #include /* g_ascii_strcasecmp */ -#include +#include #include "common/macros.h" #include "xcolor-table.h" diff --git a/src/common/parse-double.c b/src/common/parse-double.c index 0e32ef3e..a41e57f2 100644 --- a/src/common/parse-double.c +++ b/src/common/parse-double.c @@ -2,7 +2,6 @@ #include "common/parse-double.h" #include #include -#include #include #include "common/mem.h" diff --git a/src/common/scene-helpers.c b/src/common/scene-helpers.c index 5f8a8139..517789c0 100644 --- a/src/common/scene-helpers.c +++ b/src/common/scene-helpers.c @@ -5,12 +5,8 @@ #include #include #include -#include -#include -#include "labwc.h" #include "magnifier.h" #include "output.h" -#include "output-state.h" struct wlr_surface * lab_wlr_surface_from_node(struct wlr_scene_node *node) diff --git a/src/common/spawn.c b/src/common/spawn.c index 51de6b4a..898c4800 100644 --- a/src/common/spawn.c +++ b/src/common/spawn.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include diff --git a/src/config/keybind.c b/src/config/keybind.c index 2635b7bc..1705983a 100644 --- a/src/config/keybind.c +++ b/src/config/keybind.c @@ -3,7 +3,6 @@ #include "config/keybind.h" #include #include -#include #include #include #include diff --git a/src/config/mousebind.c b/src/config/mousebind.c index 7460dfad..6b5254f3 100644 --- a/src/config/mousebind.c +++ b/src/config/mousebind.c @@ -4,7 +4,6 @@ #include #include #include -#include #include #include "common/list.h" #include "common/mem.h" diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 08a70edf..35195345 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -2,17 +2,13 @@ #define _POSIX_C_SOURCE 200809L #include "config/rcxml.h" #include -#include #include #include -#include #include #include #include #include #include -#include -#include #include #include #include "action.h" diff --git a/src/config/session.c b/src/config/session.c index c90c655d..515b6803 100644 --- a/src/config/session.c +++ b/src/config/session.c @@ -8,14 +8,12 @@ #include #include #include -#include #include #include #include #include "common/buf.h" #include "common/dir.h" #include "common/file-helpers.h" -#include "common/mem.h" #include "common/parse-bool.h" #include "common/spawn.h" #include "common/string-helpers.h" diff --git a/src/config/touch.c b/src/config/touch.c index fde785e1..05592a46 100644 --- a/src/config/touch.c +++ b/src/config/touch.c @@ -3,7 +3,6 @@ #include "config/touch.h" #include #include -#include "common/list.h" #include "config/rcxml.h" static struct touch_config_entry * diff --git a/src/desktop-entry.c b/src/desktop-entry.c index 67b85e1d..60cff28e 100644 --- a/src/desktop-entry.c +++ b/src/desktop-entry.c @@ -7,12 +7,10 @@ #include #include #include -#include "common/macros.h" #include "common/mem.h" #include "common/string-helpers.h" #include "config/rcxml.h" #include "img/img.h" - #include "labwc.h" static const char *debug_libsfdo; diff --git a/src/desktop.c b/src/desktop.c index bb9b5dfd..7bfb2cbb 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -10,13 +10,10 @@ #include "labwc.h" #include "layers.h" #include "node.h" -#include "osd.h" #include "output.h" #include "ssd.h" #include "view.h" -#include "window-rules.h" #include "workspaces.h" -#include "xwayland.h" #if HAVE_XWAYLAND #include diff --git a/src/edges.c b/src/edges.c index 79532226..bdd1ef45 100644 --- a/src/edges.c +++ b/src/edges.c @@ -3,7 +3,6 @@ #include #include #include -#include #include #include "common/border.h" #include "common/box.h" diff --git a/src/img/img-png.c b/src/img/img-png.c index 5e151f21..29eba44d 100644 --- a/src/img/img-png.c +++ b/src/img/img-png.c @@ -4,12 +4,10 @@ */ #define _POSIX_C_SOURCE 200809L #include "img/img-png.h" -#include #include #include #include #include -#include #include #include "buffer.h" #include "common/string-helpers.h" diff --git a/src/img/img-svg.c b/src/img/img-svg.c index ad2d2916..1c12bde3 100644 --- a/src/img/img-svg.c +++ b/src/img/img-svg.c @@ -6,13 +6,10 @@ #include "img/img-svg.h" #include #include -#include #include -#include #include #include "buffer.h" #include "common/string-helpers.h" -#include "labwc.h" RsvgHandle * img_svg_load(const char *filename) diff --git a/src/img/img-xbm.c b/src/img/img-xbm.c index ca433cad..6ecbbc9a 100644 --- a/src/img/img-xbm.c +++ b/src/img/img-xbm.c @@ -8,13 +8,10 @@ #define _POSIX_C_SOURCE 200809L #include "img/img-xbm.h" #include -#include #include #include #include #include -#include -#include "img/img.h" #include "common/grab-file.h" #include "common/mem.h" #include "common/string-helpers.h" diff --git a/src/img/img.c b/src/img/img.c index d062022e..41bda9e4 100644 --- a/src/img/img.c +++ b/src/img/img.c @@ -2,7 +2,6 @@ #include "img/img.h" #include -#include #include "buffer.h" #include "config.h" #include "common/box.h" diff --git a/src/input/cursor.c b/src/input/cursor.c index 42757128..a24235cc 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -2,8 +2,6 @@ #define _POSIX_C_SOURCE 200809L #include "input/cursor.h" #include -#include -#include #include #include #include @@ -19,7 +17,6 @@ #include "action.h" #include "common/macros.h" #include "common/mem.h" -#include "common/scene-helpers.h" #include "common/surface-helpers.h" #include "config/mousebind.h" #include "config/rcxml.h" @@ -33,7 +30,6 @@ #include "layers.h" #include "menu/menu.h" #include "output.h" -#include "regions.h" #include "resistance.h" #include "resize-outlines.h" #include "ssd.h" diff --git a/src/input/ime.c b/src/input/ime.c index 36bc8252..18fb252f 100644 --- a/src/input/ime.c +++ b/src/input/ime.c @@ -11,7 +11,6 @@ #include "labwc.h" #include "node.h" #include "output.h" -#include "view.h" #define SAME_CLIENT(wlr_obj1, wlr_obj2) \ (wl_resource_get_client((wlr_obj1)->resource) \ diff --git a/src/input/key-state.c b/src/input/key-state.c index 12992615..def6b72c 100644 --- a/src/input/key-state.c +++ b/src/input/key-state.c @@ -4,8 +4,6 @@ #include #include #include -#include -#include #include "common/set.h" static struct lab_set pressed, bound, pressed_sent; diff --git a/src/input/keyboard.c b/src/input/keyboard.c index 0050b860..3ca616e1 100644 --- a/src/input/keyboard.c +++ b/src/input/keyboard.c @@ -3,7 +3,6 @@ #include "input/keyboard.h" #include #include -#include #include #include #include @@ -17,7 +16,6 @@ #include "labwc.h" #include "menu/menu.h" #include "osd.h" -#include "regions.h" #include "session-lock.h" #include "view.h" #include "workspaces.h" diff --git a/src/input/tablet-pad.c b/src/input/tablet-pad.c index 06e00f4e..5d9e6dd9 100644 --- a/src/input/tablet-pad.c +++ b/src/input/tablet-pad.c @@ -1,11 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only #include "input/tablet-pad.h" -#include #include #include #include #include -#include #include #include "common/macros.h" #include "common/mem.h" diff --git a/src/input/tablet.c b/src/input/tablet.c index 11a5a7d5..4b12579a 100644 --- a/src/input/tablet.c +++ b/src/input/tablet.c @@ -1,9 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #include "input/tablet.h" -#include #include #include -#include #include #include #include diff --git a/src/interactive.c b/src/interactive.c index 061fe07a..2dc4faa1 100644 --- a/src/interactive.c +++ b/src/interactive.c @@ -7,7 +7,6 @@ #include "output.h" #include "regions.h" #include "resize-indicator.h" -#include "snap.h" #include "view.h" #include "window-rules.h" diff --git a/src/layers.c b/src/layers.c index 685f8973..c8f8e953 100644 --- a/src/layers.c +++ b/src/layers.c @@ -10,9 +10,7 @@ #include #include #include -#include #include -#include #include #include #include diff --git a/src/menu/menu.c b/src/menu/menu.c index e55f7626..5cbd5804 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -3,7 +3,6 @@ #include "menu/menu.h" #include #include -#include #include #include #include @@ -20,7 +19,6 @@ #include "common/lab-scene-rect.h" #include "common/list.h" #include "common/mem.h" -#include "common/scene-helpers.h" #include "common/spawn.h" #include "common/string-helpers.h" #include "common/xml.h" diff --git a/src/osd/osd-classic.c b/src/osd/osd-classic.c index 501a86a2..be740f2f 100644 --- a/src/osd/osd-classic.c +++ b/src/osd/osd-classic.c @@ -1,24 +1,20 @@ // SPDX-License-Identifier: GPL-2.0-only #include -#include -#include +#include #include +#include #include "common/array.h" #include "common/buf.h" #include "common/font.h" #include "common/lab-scene-rect.h" -#include "common/scene-helpers.h" #include "common/string-helpers.h" #include "config/rcxml.h" #include "labwc.h" -#include "node.h" #include "osd.h" #include "output.h" #include "scaled-buffer/scaled-font-buffer.h" #include "scaled-buffer/scaled-icon-buffer.h" #include "theme.h" -#include "view.h" -#include "window-rules.h" #include "workspaces.h" struct osd_classic_scene_item { diff --git a/src/osd/osd-thumbnail.c b/src/osd/osd-thumbnail.c index 9804d1ec..0345582a 100644 --- a/src/osd/osd-thumbnail.c +++ b/src/osd/osd-thumbnail.c @@ -2,7 +2,6 @@ #include #include #include -#include #include #include "config/rcxml.h" #include "common/array.h" diff --git a/src/osd/osd.c b/src/osd/osd.c index e8d32348..8500eaf1 100644 --- a/src/osd/osd.c +++ b/src/osd/osd.c @@ -1,23 +1,19 @@ // SPDX-License-Identifier: GPL-2.0-only #include "osd.h" #include -#include -#include +#include #include #include "common/array.h" #include "common/lab-scene-rect.h" #include "common/scene-helpers.h" #include "config/rcxml.h" #include "labwc.h" -#include "node.h" #include "output.h" #include "scaled-buffer/scaled-font-buffer.h" #include "scaled-buffer/scaled-icon-buffer.h" #include "ssd.h" #include "theme.h" #include "view.h" -#include "window-rules.h" -#include "workspaces.h" static void update_osd(struct server *server); diff --git a/src/output.c b/src/output.c index c5c88dfc..c561f0f7 100644 --- a/src/output.c +++ b/src/output.c @@ -12,8 +12,6 @@ #include #include #include -#include -#include #include #include #include @@ -21,7 +19,6 @@ #include #include #include -#include #include #include "common/macros.h" #include "common/mem.h" diff --git a/src/protocols/cosmic_workspaces/cosmic-workspaces.c b/src/protocols/cosmic_workspaces/cosmic-workspaces.c index 20d8b822..d12611cb 100644 --- a/src/protocols/cosmic_workspaces/cosmic-workspaces.c +++ b/src/protocols/cosmic_workspaces/cosmic-workspaces.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only #include -#include #include #include "common/array.h" #include "common/mem.h" diff --git a/src/protocols/ext-workspace/ext-workspace.c b/src/protocols/ext-workspace/ext-workspace.c index eb02e3dc..30875811 100644 --- a/src/protocols/ext-workspace/ext-workspace.c +++ b/src/protocols/ext-workspace/ext-workspace.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #include -#include #include -#include "common/array.h" #include "common/mem.h" #include "common/list.h" #include "ext-workspace-v1-protocol.h" diff --git a/src/regions.c b/src/regions.c index c06698b9..bb0ecb23 100644 --- a/src/regions.c +++ b/src/regions.c @@ -6,9 +6,7 @@ #include #include #include -#include #include -#include #include "common/list.h" #include "common/mem.h" #include "config/rcxml.h" diff --git a/src/resistance.c b/src/resistance.c index 4566c5d0..12cbec90 100644 --- a/src/resistance.c +++ b/src/resistance.c @@ -1,9 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #include "resistance.h" #include -#include #include "common/border.h" -#include "common/macros.h" #include "config/rcxml.h" #include "edges.h" #include "labwc.h" diff --git a/src/scaled-buffer/scaled-font-buffer.c b/src/scaled-buffer/scaled-font-buffer.c index 7b252481..61fd5db3 100644 --- a/src/scaled-buffer/scaled-font-buffer.c +++ b/src/scaled-buffer/scaled-font-buffer.c @@ -4,9 +4,7 @@ #include #include #include -#include #include -#include "buffer.h" #include "common/font.h" #include "common/graphic-helpers.h" #include "common/mem.h" diff --git a/src/scaled-buffer/scaled-icon-buffer.c b/src/scaled-buffer/scaled-icon-buffer.c index b8f08efc..b7124336 100644 --- a/src/scaled-buffer/scaled-icon-buffer.c +++ b/src/scaled-buffer/scaled-icon-buffer.c @@ -5,7 +5,6 @@ #include #include #include "buffer.h" -#include "common/macros.h" #include "common/mem.h" #include "common/string-helpers.h" #include "config.h" diff --git a/src/scaled-buffer/scaled-img-buffer.c b/src/scaled-buffer/scaled-img-buffer.c index 488c861c..4ce9618a 100644 --- a/src/scaled-buffer/scaled-img-buffer.c +++ b/src/scaled-buffer/scaled-img-buffer.c @@ -2,9 +2,6 @@ #define _POSIX_C_SOURCE 200809L #include "scaled-buffer/scaled-img-buffer.h" #include -#include -#include -#include "buffer.h" #include "common/mem.h" #include "img/img.h" #include "node.h" diff --git a/src/server.c b/src/server.c index 7560986d..56f091b9 100644 --- a/src/server.c +++ b/src/server.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -45,7 +44,6 @@ #include "xwayland-shell-v1-protocol.h" #endif -#include "drm-lease-v1-protocol.h" #include "action.h" #include "common/macros.h" #include "config/rcxml.h" @@ -59,7 +57,6 @@ #include "magnifier.h" #include "menu/menu.h" #include "output.h" -#include "output-state.h" #include "output-virtual.h" #include "regions.h" #include "resize-indicator.h" diff --git a/src/snap-constraints.c b/src/snap-constraints.c index d579a387..fd1f09cd 100644 --- a/src/snap-constraints.c +++ b/src/snap-constraints.c @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only #include "snap-constraints.h" #include +#include #include #include "common/macros.h" -#include "labwc.h" #include "view.h" /* diff --git a/src/ssd/ssd-border.c b/src/ssd/ssd-border.c index 54ef73e9..f0db4fff 100644 --- a/src/ssd/ssd-border.c +++ b/src/ssd/ssd-border.c @@ -3,7 +3,6 @@ #include #include #include "common/macros.h" -#include "common/scene-helpers.h" #include "labwc.h" #include "ssd.h" #include "ssd-internal.h" diff --git a/src/ssd/ssd-extents.c b/src/ssd/ssd-extents.c index 8a38c39e..bc1ed9f7 100644 --- a/src/ssd/ssd-extents.c +++ b/src/ssd/ssd-extents.c @@ -1,9 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only -#include #include #include -#include "common/scene-helpers.h" #include "config/rcxml.h" #include "labwc.h" #include "output.h" diff --git a/src/ssd/ssd-shadow.c b/src/ssd/ssd-shadow.c index a535112e..53c6631d 100644 --- a/src/ssd/ssd-shadow.c +++ b/src/ssd/ssd-shadow.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #include -#include #include #include "buffer.h" #include "config/rcxml.h" diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 84dd527a..b9f95202 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -6,11 +6,8 @@ #include #include "buffer.h" #include "common/mem.h" -#include "common/scene-helpers.h" #include "common/string-helpers.h" #include "config/rcxml.h" -#include "desktop-entry.h" -#include "img/img.h" #include "labwc.h" #include "node.h" #include "scaled-buffer/scaled-font-buffer.h" diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index afdbcdf9..ca3f10a9 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -11,7 +11,6 @@ #include #include #include "common/mem.h" -#include "common/scene-helpers.h" #include "config/rcxml.h" #include "labwc.h" #include "node.h" diff --git a/src/theme.c b/src/theme.c index a8a3227d..f0b48158 100644 --- a/src/theme.c +++ b/src/theme.c @@ -7,7 +7,6 @@ #define _POSIX_C_SOURCE 200809L #include "theme.h" -#include "config.h" #include #include #include diff --git a/src/view-impl-common.c b/src/view-impl-common.c index 377d8c76..d529941a 100644 --- a/src/view-impl-common.c +++ b/src/view-impl-common.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* view-impl-common.c: common code for shell view->impl functions */ #include "view-impl-common.h" -#include #include "foreign-toplevel/foreign.h" #include "labwc.h" #include "view.h" diff --git a/src/view.c b/src/view.c index 92e5db92..1f93d140 100644 --- a/src/view.c +++ b/src/view.c @@ -13,7 +13,6 @@ #include "common/list.h" #include "common/match.h" #include "common/mem.h" -#include "common/scene-helpers.h" #include "config/rcxml.h" #include "foreign-toplevel/foreign.h" #include "input/keyboard.h" @@ -33,7 +32,6 @@ #include "window-rules.h" #include "wlr/util/log.h" #include "workspaces.h" -#include "xwayland.h" #if HAVE_XWAYLAND #include diff --git a/src/window-rules.c b/src/window-rules.c index 8283e801..0b8f1101 100644 --- a/src/window-rules.c +++ b/src/window-rules.c @@ -3,12 +3,8 @@ #include "window-rules.h" #include #include -#include -#include #include -#include #include "action.h" -#include "common/match.h" #include "config/rcxml.h" #include "labwc.h" #include "view.h" From d2ce31fcc96d28a0200aa5875a68686638bcdbe9 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Sat, 6 Sep 2025 20:06:32 -0400 Subject: [PATCH 152/248] tree-wide: use forward declarations for wlr types --- include/config/keybind.h | 3 ++- include/input/cursor.h | 4 +++- include/input/ime.h | 4 ++-- include/input/tablet-pad.h | 1 - include/input/tablet.h | 1 - include/labwc.h | 1 + include/layers.h | 5 +++-- include/node.h | 5 ++++- include/session-lock.h | 2 +- include/theme.h | 2 +- src/action.c | 1 + src/desktop.c | 4 ++++ src/edges.c | 2 ++ src/input/cursor.c | 2 ++ src/input/gestures.c | 1 + src/input/ime.c | 5 +++++ src/input/keyboard.c | 1 + src/input/tablet-pad.c | 1 + src/input/tablet.c | 2 ++ src/input/touch.c | 1 + src/interactive.c | 1 + src/layers.c | 2 ++ src/magnifier.c | 1 + src/menu/menu.c | 2 +- src/node.c | 1 + src/osd/osd.c | 1 + src/output-virtual.c | 1 + src/output.c | 1 + src/overlay.c | 2 ++ src/regions.c | 1 + src/seat.c | 1 + src/session-lock.c | 3 +++ src/ssd/ssd-button.c | 1 + src/ssd/ssd-titlebar.c | 1 + src/ssd/ssd.c | 1 + src/view.c | 1 + src/workspaces.c | 1 + src/xdg-popup.c | 1 + src/xdg.c | 2 ++ src/xwayland.c | 2 ++ 40 files changed, 63 insertions(+), 12 deletions(-) diff --git a/include/config/keybind.h b/include/config/keybind.h index 3d923530..bef3e189 100644 --- a/include/config/keybind.h +++ b/include/config/keybind.h @@ -2,7 +2,8 @@ #ifndef LABWC_KEYBIND_H #define LABWC_KEYBIND_H -#include +#include +#include #include #define MAX_KEYSYMS 32 diff --git a/include/input/cursor.h b/include/input/cursor.h index e6cfb448..5afa831b 100644 --- a/include/input/cursor.h +++ b/include/input/cursor.h @@ -2,13 +2,15 @@ #ifndef LABWC_CURSOR_H #define LABWC_CURSOR_H -#include +#include #include "common/edge.h" #include "common/node-type.h" struct view; struct seat; struct server; +struct wlr_input_device; +struct wlr_cursor; struct wlr_surface; struct wlr_scene_node; diff --git a/include/input/ime.h b/include/input/ime.h index ebf30c36..046e0926 100644 --- a/include/input/ime.h +++ b/include/input/ime.h @@ -3,10 +3,10 @@ #ifndef LABWC_IME_H #define LABWC_IME_H -#include -#include +#include struct keyboard; +struct wlr_keyboard_key_event; /* * The relay structure manages the relationship between text-inputs and diff --git a/include/input/tablet-pad.h b/include/input/tablet-pad.h index 3ada7634..e8be74f5 100644 --- a/include/input/tablet-pad.h +++ b/include/input/tablet-pad.h @@ -3,7 +3,6 @@ #define LABWC_TABLET_PAD_H #include -#include struct seat; struct wlr_device; diff --git a/include/input/tablet.h b/include/input/tablet.h index 094dad9a..eb3234f4 100644 --- a/include/input/tablet.h +++ b/include/input/tablet.h @@ -3,7 +3,6 @@ #define LABWC_TABLET_H #include -#include #include "config/types.h" struct seat; diff --git a/include/labwc.h b/include/labwc.h index 2509292e..72dbe687 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -2,6 +2,7 @@ #ifndef LABWC_H #define LABWC_H #include "config.h" +#include #include #include "common/set.h" #include "input/cursor.h" diff --git a/include/layers.h b/include/layers.h index b995fc8b..da1bdb86 100644 --- a/include/layers.h +++ b/include/layers.h @@ -1,8 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0-only */ #ifndef LABWC_LAYERS_H #define LABWC_LAYERS_H -#include -#include + +#include +#include struct server; struct output; diff --git a/include/node.h b/include/node.h index 7e8b6b50..52ebed96 100644 --- a/include/node.h +++ b/include/node.h @@ -1,9 +1,12 @@ /* SPDX-License-Identifier: GPL-2.0-only */ #ifndef LABWC_NODE_DESCRIPTOR_H #define LABWC_NODE_DESCRIPTOR_H -#include + +#include #include "common/node-type.h" +struct wlr_scene_node; + struct node_descriptor { enum lab_node_type type; struct view *view; diff --git a/include/session-lock.h b/include/session-lock.h index 715c859f..207f0393 100644 --- a/include/session-lock.h +++ b/include/session-lock.h @@ -2,7 +2,7 @@ #ifndef LABWC_SESSION_LOCK_H #define LABWC_SESSION_LOCK_H -#include +#include struct output; struct server; diff --git a/include/theme.h b/include/theme.h index 0df661e5..797b5072 100644 --- a/include/theme.h +++ b/include/theme.h @@ -9,7 +9,7 @@ #define LABWC_THEME_H #include -#include +#include #include "common/node-type.h" struct lab_img; diff --git a/src/action.c b/src/action.c index d02bf541..60c57b33 100644 --- a/src/action.c +++ b/src/action.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include "action-prompt-codes.h" diff --git a/src/desktop.c b/src/desktop.c index 7bfb2cbb..099c493b 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -1,6 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-only #include "config.h" #include +#include +#include +#include +#include #include #include #include diff --git a/src/edges.c b/src/edges.c index bdd1ef45..56fb8bfa 100644 --- a/src/edges.c +++ b/src/edges.c @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include "common/border.h" #include "common/box.h" diff --git a/src/input/cursor.c b/src/input/cursor.c index a24235cc..36a9046b 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -4,8 +4,10 @@ #include #include #include +#include #include #include +#include #include #include #include diff --git a/src/input/gestures.c b/src/input/gestures.c index 5a0365fe..b40da82c 100644 --- a/src/input/gestures.c +++ b/src/input/gestures.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #include "input/gestures.h" +#include #include #include "common/macros.h" #include "labwc.h" diff --git a/src/input/ime.c b/src/input/ime.c index 18fb252f..92d88ffe 100644 --- a/src/input/ime.c +++ b/src/input/ime.c @@ -3,7 +3,12 @@ #include "input/ime.h" #include +#include #include +#include +#include +#include +#include #include #include #include "common/mem.h" diff --git a/src/input/keyboard.c b/src/input/keyboard.c index 3ca616e1..ae92e73f 100644 --- a/src/input/keyboard.c +++ b/src/input/keyboard.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "action.h" #include "common/macros.h" #include "config/keybind.h" diff --git a/src/input/tablet-pad.c b/src/input/tablet-pad.c index 5d9e6dd9..230b6e6b 100644 --- a/src/input/tablet-pad.c +++ b/src/input/tablet-pad.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "common/macros.h" #include "common/mem.h" diff --git a/src/input/tablet.c b/src/input/tablet.c index 4b12579a..319c06b3 100644 --- a/src/input/tablet.c +++ b/src/input/tablet.c @@ -2,7 +2,9 @@ #include "input/tablet.h" #include #include +#include #include +#include #include #include #include "common/macros.h" diff --git a/src/input/touch.c b/src/input/touch.c index 9f4f1eca..d6557014 100644 --- a/src/input/touch.c +++ b/src/input/touch.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #include "input/touch.h" #include +#include #include #include #include diff --git a/src/interactive.c b/src/interactive.c index 2dc4faa1..f66a66b3 100644 --- a/src/interactive.c +++ b/src/interactive.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #include +#include #include "config/rcxml.h" #include "edges.h" #include "input/keyboard.h" diff --git a/src/layers.c b/src/layers.c index c8f8e953..f26208ef 100644 --- a/src/layers.c +++ b/src/layers.c @@ -11,8 +11,10 @@ #include #include #include +#include #include #include +#include #include #include #include "common/macros.h" diff --git a/src/magnifier.c b/src/magnifier.c index 3764ad16..424865c9 100644 --- a/src/magnifier.c +++ b/src/magnifier.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include diff --git a/src/menu/menu.c b/src/menu/menu.c index 5cbd5804..e7a7b90c 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include "action.h" diff --git a/src/node.c b/src/node.c index e5132e7c..046fa5e2 100644 --- a/src/node.c +++ b/src/node.c @@ -2,6 +2,7 @@ #include "node.h" #include #include +#include #include "common/mem.h" #include "ssd.h" diff --git a/src/osd/osd.c b/src/osd/osd.c index 8500eaf1..df1b7ad9 100644 --- a/src/osd/osd.c +++ b/src/osd/osd.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "common/array.h" #include "common/lab-scene-rect.h" #include "common/scene-helpers.h" diff --git a/src/output-virtual.c b/src/output-virtual.c index 164be7b6..f3a40e10 100644 --- a/src/output-virtual.c +++ b/src/output-virtual.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "common/string-helpers.h" #include "labwc.h" #include "output.h" diff --git a/src/output.c b/src/output.c index c561f0f7..98da25ad 100644 --- a/src/output.c +++ b/src/output.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/src/overlay.c b/src/overlay.c index 4cf6c4fd..508a531a 100644 --- a/src/overlay.c +++ b/src/overlay.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only #include "overlay.h" #include +#include +#include #include #include "common/lab-scene-rect.h" #include "config/rcxml.h" diff --git a/src/regions.c b/src/regions.c index bb0ecb23..a5170d4f 100644 --- a/src/regions.c +++ b/src/regions.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "common/list.h" #include "common/mem.h" diff --git a/src/seat.c b/src/seat.c index 25d8fc57..05f5a006 100644 --- a/src/seat.c +++ b/src/seat.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include diff --git a/src/session-lock.c b/src/session-lock.c index 277233cc..a370241e 100644 --- a/src/session-lock.c +++ b/src/session-lock.c @@ -2,6 +2,9 @@ #define _POSIX_C_SOURCE 200809L #include "session-lock.h" #include +#include +#include +#include #include "common/mem.h" #include "labwc.h" #include "node.h" diff --git a/src/ssd/ssd-button.c b/src/ssd/ssd-button.c index af209f89..50131a12 100644 --- a/src/ssd/ssd-button.c +++ b/src/ssd/ssd-button.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #include +#include #include "config/rcxml.h" #include "common/list.h" #include "common/mem.h" diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index b9f95202..595a203d 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "buffer.h" #include "common/mem.h" #include "common/string-helpers.h" diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index ca3f10a9..0e900a65 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -9,6 +9,7 @@ #include "ssd.h" #include #include +#include #include #include "common/mem.h" #include "config/rcxml.h" diff --git a/src/view.c b/src/view.c index 1f93d140..ef871313 100644 --- a/src/view.c +++ b/src/view.c @@ -2,6 +2,7 @@ #include "view.h" #include #include +#include #include #include #include diff --git a/src/workspaces.c b/src/workspaces.c index 5345f69d..43f19b18 100644 --- a/src/workspaces.c +++ b/src/workspaces.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "buffer.h" #include "common/font.h" diff --git a/src/xdg-popup.c b/src/xdg-popup.c index 722a32ff..aded80b5 100644 --- a/src/xdg-popup.c +++ b/src/xdg-popup.c @@ -7,6 +7,7 @@ * - keeping non-layer-shell xdg-popups outside the layers.c code */ +#include #include #include "common/macros.h" #include "common/mem.h" diff --git a/src/xdg.c b/src/xdg.c index f25a0e84..1e09dd99 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -1,7 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only #include +#include #include +#include #include #include #include diff --git a/src/xwayland.c b/src/xwayland.c index 2127bd52..0604dcaf 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include #include From b02d7d0e18659b992759e8658b6d4d4393f5da6c Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Sun, 7 Sep 2025 16:47:58 +0200 Subject: [PATCH 153/248] src/output.c: restore drm lease include Introduced in #3062. Its usage is guarded by a wlroots version check which prevented the CI to detect the issue. When compiled with a wlroots version > 0.19.0 (like the chase 0.20 PR) the error shows up. --- src/output.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/output.c b/src/output.c index 98da25ad..e47da9ca 100644 --- a/src/output.c +++ b/src/output.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include From 3923b27384046167d02c96ee96d24f3330058acf Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sun, 7 Sep 2025 22:10:27 +0900 Subject: [PATCH 154/248] desktop.c: don't print errors when cursor is on resize-indicator In f347a81, I thought there are only window contents and SSD under `view->scene_tree` and forgot about the resize-indicator. I also refactored the logic around it: - Remove `ret.node->type == WLR_SCENE_NODE_BUFFER` check since it's already done by `lab_wlr_surface_from_node()` - Eliminate duplicated call to `lab_wlr_surface_from_node()` --- src/desktop.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/desktop.c b/src/desktop.c index 099c493b..efd46dc5 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -303,13 +303,12 @@ get_cursor_context(struct server *server) case LAB_NODE_VIEW: case LAB_NODE_XDG_POPUP: ret.view = desc->view; - if (ret.node->type == WLR_SCENE_NODE_BUFFER - && lab_wlr_surface_from_node(ret.node)) { + ret.surface = lab_wlr_surface_from_node(ret.node); + if (ret.surface) { ret.type = LAB_NODE_CLIENT; - ret.surface = lab_wlr_surface_from_node(ret.node); } else { - /* should never be reached */ - wlr_log(WLR_ERROR, "cursor not on client or ssd"); + /* e.g. when cursor is on resize-indicator */ + ret.type = LAB_NODE_NONE; } return ret; case LAB_NODE_LAYER_SURFACE: From fb3dcb7959470fb9179b3782640fea9c287577b8 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sun, 7 Sep 2025 21:45:29 +0900 Subject: [PATCH 155/248] scaled-buffer: remove *_from_node() functions --- include/common/node-type.h | 1 - include/node.h | 7 ------- include/scaled-buffer/scaled-icon-buffer.h | 3 --- include/scaled-buffer/scaled-img-buffer.h | 3 --- src/desktop.c | 1 - src/node.c | 9 --------- src/scaled-buffer/scaled-buffer.c | 2 -- src/scaled-buffer/scaled-icon-buffer.c | 9 --------- src/scaled-buffer/scaled-img-buffer.c | 9 --------- 9 files changed, 44 deletions(-) diff --git a/include/common/node-type.h b/include/common/node-type.h index 0afa5f58..0ae1c4a9 100644 --- a/include/common/node-type.h +++ b/include/common/node-type.h @@ -62,7 +62,6 @@ enum lab_node_type { /* never returned by get_cursor_context() */ LAB_NODE_TREE, - LAB_NODE_SCALED_BUFFER, }; enum lab_node_type node_type_parse(const char *context); diff --git a/include/node.h b/include/node.h index 52ebed96..d6d177f0 100644 --- a/include/node.h +++ b/include/node.h @@ -59,13 +59,6 @@ struct lab_layer_popup *node_layer_popup_from_node( struct menuitem *node_menuitem_from_node( struct wlr_scene_node *wlr_scene_node); -/** - * node_scaled_buffer_from_node - return scaled_buffer from node - * @wlr_scene_node: wlr_scene_node from which to return data - */ -struct scaled_buffer *node_scaled_buffer_from_node( - struct wlr_scene_node *wlr_scene_node); - /** * node_try_ssd_button_from_node - return ssd_button or NULL from node * @wlr_scene_node: wlr_scene_node from which to return data diff --git a/include/scaled-buffer/scaled-icon-buffer.h b/include/scaled-buffer/scaled-icon-buffer.h index b6830f3e..45492ec2 100644 --- a/include/scaled-buffer/scaled-icon-buffer.h +++ b/include/scaled-buffer/scaled-icon-buffer.h @@ -48,7 +48,4 @@ void scaled_icon_buffer_set_view(struct scaled_icon_buffer *self, void scaled_icon_buffer_set_icon_name(struct scaled_icon_buffer *self, const char *icon_name); -/* Obtain scaled_icon_buffer from wlr_scene_node */ -struct scaled_icon_buffer *scaled_icon_buffer_from_node(struct wlr_scene_node *node); - #endif /* LABWC_SCALED_ICON_BUFFER_H */ diff --git a/include/scaled-buffer/scaled-img-buffer.h b/include/scaled-buffer/scaled-img-buffer.h index 23fb9e17..e8784eb0 100644 --- a/include/scaled-buffer/scaled-img-buffer.h +++ b/include/scaled-buffer/scaled-img-buffer.h @@ -64,7 +64,4 @@ struct scaled_img_buffer { struct scaled_img_buffer *scaled_img_buffer_create(struct wlr_scene_tree *parent, struct lab_img *img, int width, int height); -/* Obtain scaled_img_buffer from wlr_scene_node */ -struct scaled_img_buffer *scaled_img_buffer_from_node(struct wlr_scene_node *node); - #endif /* LABWC_SCALED_IMG_BUFFER_H */ diff --git a/src/desktop.c b/src/desktop.c index efd46dc5..2327c193 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -332,7 +332,6 @@ get_cursor_context(struct server *server) ret.type = LAB_NODE_MENUITEM; return ret; case LAB_NODE_TREE: - case LAB_NODE_SCALED_BUFFER: /* Continue to parent node */ break; default: diff --git a/src/node.c b/src/node.c index 046fa5e2..025789e8 100644 --- a/src/node.c +++ b/src/node.c @@ -68,15 +68,6 @@ node_menuitem_from_node(struct wlr_scene_node *wlr_scene_node) return (struct menuitem *)node_descriptor->data; } -struct scaled_buffer * -node_scaled_buffer_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_SCALED_BUFFER); - return (struct scaled_buffer *)node_descriptor->data; -} - struct ssd_button * node_try_ssd_button_from_node(struct wlr_scene_node *wlr_scene_node) { diff --git a/src/scaled-buffer/scaled-buffer.c b/src/scaled-buffer/scaled-buffer.c index c4a3219c..8bef0a75 100644 --- a/src/scaled-buffer/scaled-buffer.c +++ b/src/scaled-buffer/scaled-buffer.c @@ -197,8 +197,6 @@ scaled_buffer_create(struct wlr_scene_tree *parent, free(self); return NULL; } - node_descriptor_create(&self->scene_buffer->node, - LAB_NODE_SCALED_BUFFER, /*view*/ NULL, self); self->impl = impl; /* diff --git a/src/scaled-buffer/scaled-icon-buffer.c b/src/scaled-buffer/scaled-icon-buffer.c index b7124336..0cd09b22 100644 --- a/src/scaled-buffer/scaled-icon-buffer.c +++ b/src/scaled-buffer/scaled-icon-buffer.c @@ -349,12 +349,3 @@ scaled_icon_buffer_set_icon_name(struct scaled_icon_buffer *self, xstrdup_replace(self->icon_name, icon_name); scaled_buffer_request_update(self->scaled_buffer, self->width, self->height); } - -struct scaled_icon_buffer * -scaled_icon_buffer_from_node(struct wlr_scene_node *node) -{ - struct scaled_buffer *scaled_buffer = - node_scaled_buffer_from_node(node); - assert(scaled_buffer->impl == &impl); - return scaled_buffer->data; -} diff --git a/src/scaled-buffer/scaled-img-buffer.c b/src/scaled-buffer/scaled-img-buffer.c index 4ce9618a..50baa961 100644 --- a/src/scaled-buffer/scaled-img-buffer.c +++ b/src/scaled-buffer/scaled-img-buffer.c @@ -65,12 +65,3 @@ scaled_img_buffer_create(struct wlr_scene_tree *parent, struct lab_img *img, return self; } - -struct scaled_img_buffer * -scaled_img_buffer_from_node(struct wlr_scene_node *node) -{ - struct scaled_buffer *scaled_buffer = - node_scaled_buffer_from_node(node); - assert(scaled_buffer->impl == &impl); - return scaled_buffer->data; -} From b9b23f59319ee060170ad5f8fe58117937412b19 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sun, 7 Sep 2025 21:47:08 +0900 Subject: [PATCH 156/248] output: remove node descriptors --- include/common/node-type.h | 3 --- src/desktop.c | 3 --- src/output.c | 8 -------- 3 files changed, 14 deletions(-) diff --git a/include/common/node-type.h b/include/common/node-type.h index 0ae1c4a9..41f4d381 100644 --- a/include/common/node-type.h +++ b/include/common/node-type.h @@ -59,9 +59,6 @@ enum lab_node_type { LAB_NODE_LAYER_POPUP, LAB_NODE_SESSION_LOCK_SURFACE, LAB_NODE_IME_POPUP, - - /* never returned by get_cursor_context() */ - LAB_NODE_TREE, }; enum lab_node_type node_type_parse(const char *context); diff --git a/src/desktop.c b/src/desktop.c index 2327c193..75c2648e 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -331,9 +331,6 @@ get_cursor_context(struct server *server) ret.node = node; ret.type = LAB_NODE_MENUITEM; return ret; - case LAB_NODE_TREE: - /* Continue to parent node */ - break; default: /* * All other node descriptors (buttons, title, diff --git a/src/output.c b/src/output.c index e47da9ca..22e9fc4e 100644 --- a/src/output.c +++ b/src/output.c @@ -509,18 +509,10 @@ handle_new_output(struct wl_listener *listener, void *data) for (size_t i = 0; i < ARRAY_SIZE(output->layer_tree); i++) { output->layer_tree[i] = wlr_scene_tree_create(&server->scene->tree); - node_descriptor_create(&output->layer_tree[i]->node, - LAB_NODE_TREE, /*view*/ NULL, /*data*/ NULL); } output->layer_popup_tree = wlr_scene_tree_create(&server->scene->tree); - node_descriptor_create(&output->layer_popup_tree->node, - LAB_NODE_TREE, /*view*/ NULL, /*data*/ NULL); output->osd_tree = wlr_scene_tree_create(&server->scene->tree); - node_descriptor_create(&output->osd_tree->node, - LAB_NODE_TREE, /*view*/ NULL, /*data*/ NULL); output->session_lock_tree = wlr_scene_tree_create(&server->scene->tree); - node_descriptor_create(&output->session_lock_tree->node, - LAB_NODE_TREE, /*view*/ NULL, /*data*/ NULL); /* * Set the z-positions to achieve the following order (from top to From 574b20fbff89a56189462e2cf3599f94a28846bb Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sun, 7 Sep 2025 21:48:51 +0900 Subject: [PATCH 157/248] ssd: attach LAB_NODE_SSD_ROOT to ssd->tree This doesn't change any behaviors. Attaching LAB_NODE_NONE node-descriptor to ssd->tree looks strange, this patch uses new LAB_NODE_SSD_ROOT instead. The node-descriptor attached to ssd->tree is needed for get_cursor_context() to detect cursor hovering on borders/extents. I also updated get_cursor_context() to make my intent clearer. --- include/common/node-type.h | 6 ++++++ include/ssd-internal.h | 2 +- src/desktop.c | 43 +++++++++++++++++++++++++------------- src/ssd/ssd.c | 8 ++++++- 4 files changed, 43 insertions(+), 16 deletions(-) diff --git a/include/common/node-type.h b/include/common/node-type.h index 41f4d381..280a6c59 100644 --- a/include/common/node-type.h +++ b/include/common/node-type.h @@ -59,6 +59,12 @@ enum lab_node_type { LAB_NODE_LAYER_POPUP, LAB_NODE_SESSION_LOCK_SURFACE, LAB_NODE_IME_POPUP, + + /* + * translated to LAB_CORNER_* or LAB_BORDER* by + * ssd_get_resizing_type() + */ + LAB_NODE_SSD_ROOT, }; enum lab_node_type node_type_parse(const char *context); diff --git a/include/ssd-internal.h b/include/ssd-internal.h index db8d02a6..600b9076 100644 --- a/include/ssd-internal.h +++ b/include/ssd-internal.h @@ -17,7 +17,7 @@ struct ssd_state_title_width { * type of each node (enum lab_node_type, stored in the node_descriptor * attached to the wlr_scene_node). * - * ssd->tree (LAB_NODE_NONE) + * ssd->tree (LAB_NODE_SSD_ROOT) * +--titlebar (LAB_NODE_TITLEBAR) * | +--inactive * | | +--background bar diff --git a/src/desktop.c b/src/desktop.c index 75c2648e..766a3e3a 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -331,31 +331,46 @@ get_cursor_context(struct server *server) ret.node = node; ret.type = LAB_NODE_MENUITEM; return ret; - default: - /* - * All other node descriptors (buttons, title, - * etc.) should have an associated view. - */ - if (!desc->view) { - wlr_log(WLR_ERROR, "cursor not on any view " - "(node type %d)", desc->type); - return ret; - } - + case LAB_NODE_BUTTON_FIRST...LAB_NODE_BUTTON_LAST: + case LAB_NODE_SSD_ROOT: + case LAB_NODE_TITLE: + case LAB_NODE_TITLEBAR: ret.node = node; ret.view = desc->view; + /* + * A node_descriptor attached to a ssd part + * must have an associated view. + */ + assert(ret.view); - /* Detect mouse contexts like Top, Left and TRCorner */ + /* + * When cursor is on the ssd border or extents, + * desc->type is usually LAB_NODE_SSD_ROOT. + * But desc->type can also be LAB_NODE_TITLEBAR + * when cursor is on the curved border at the + * titlebar. + * + * ssd_get_resizing_type() overwrites both of + * them with LAB_NODE_{BORDER,CORNER}_* node + * types, which are mapped to mouse contexts + * like Left and TLCorner. + */ ret.type = ssd_get_resizing_type(ret.view->ssd, cursor); if (ret.type == LAB_NODE_NONE) { /* - * Otherwise, detect mouse contexts like - * Title, Titlebar and Iconify + * If cursor is not on border/extents, + * just use desc->type which should be + * mapped to mouse contexts like Title, + * Titlebar and Iconify. */ ret.type = desc->type; } return ret; + default: + /* Other node types are not attached a scene node */ + wlr_log(WLR_ERROR, "unexpected node type: %d", desc->type); + break; } } diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index 0e900a65..9ec7fa3b 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -146,8 +146,14 @@ ssd_create(struct view *view, bool active) ssd->view = view; ssd->tree = wlr_scene_tree_create(view->scene_tree); + + /* + * Attach node_descriptor to the root node so that get_cursor_context() + * detect cursor hovering on borders and extents. + */ node_descriptor_create(&ssd->tree->node, - LAB_NODE_NONE, view, /*data*/ NULL); + LAB_NODE_SSD_ROOT, view, /*data*/ NULL); + wlr_scene_node_lower_to_bottom(&ssd->tree->node); ssd->titlebar.height = view->server->theme->titlebar_height; ssd_shadow_create(ssd); From 3d670b772dddd928ee9d8e5889c53b62c0210327 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Tue, 9 Sep 2025 02:51:33 +0900 Subject: [PATCH 158/248] desktop: don't use LAB_NODE_LAYER_SUBSURFACE node type Instead, set ctx.type = LAB_NODE_LAYER_SURFACE for both layer-surfaces and layer-subsurfaces. This patch preserves the existing behaviors: - Pressing a subsurface of an on-demand layer-surface gives pointer focus to the subsurface, but gives keyboard focus to the parent layer-surface (related: a5fcbfaf). - Pressing a subsurface of a layer-surface doesn't close a popup (related: a89bcc3c). --- include/common/node-type.h | 1 - include/common/surface-helpers.h | 16 --------------- src/common/meson.build | 1 - src/common/surface-helpers.c | 27 ------------------------- src/desktop.c | 27 +------------------------ src/input/cursor.c | 34 +++++++++++++++++++++----------- 6 files changed, 23 insertions(+), 83 deletions(-) delete mode 100644 include/common/surface-helpers.h delete mode 100644 src/common/surface-helpers.c diff --git a/include/common/node-type.h b/include/common/node-type.h index 280a6c59..ac83b2b5 100644 --- a/include/common/node-type.h +++ b/include/common/node-type.h @@ -49,7 +49,6 @@ enum lab_node_type { LAB_NODE_MENUITEM, LAB_NODE_OSD, LAB_NODE_LAYER_SURFACE, - LAB_NODE_LAYER_SUBSURFACE, LAB_NODE_UNMANAGED, LAB_NODE_ALL, diff --git a/include/common/surface-helpers.h b/include/common/surface-helpers.h deleted file mode 100644 index 1dd7fd74..00000000 --- a/include/common/surface-helpers.h +++ /dev/null @@ -1,16 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef LABWC_SURFACE_HELPERS_H -#define LABWC_SURFACE_HELPERS_H - -struct wlr_surface; -struct wlr_layer_surface_v1; - -/** - * subsurface_parent_layer() - Get wlr_layer_surface from layer-subsurface - * @wlr_surface: The wlr_surface of the wlr_subsurface for which to get the - * layer-surface. - */ -struct wlr_layer_surface_v1 *subsurface_parent_layer( - struct wlr_surface *wlr_surface); - -#endif /* LABWC_SURFACE_HELPERS_H */ diff --git a/src/common/meson.build b/src/common/meson.build index b1087629..39b4d4b4 100644 --- a/src/common/meson.build +++ b/src/common/meson.build @@ -17,7 +17,6 @@ labwc_sources += files( 'parse-double.c', 'scene-helpers.c', 'set.c', - 'surface-helpers.c', 'spawn.c', 'string-helpers.c', 'xml.c', diff --git a/src/common/surface-helpers.c b/src/common/surface-helpers.c deleted file mode 100644 index f13a4d6e..00000000 --- a/src/common/surface-helpers.c +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include "common/surface-helpers.h" -#include -#include -#include - -struct wlr_layer_surface_v1 * -subsurface_parent_layer(struct wlr_surface *wlr_surface) -{ - struct wlr_subsurface *subsurface = - wlr_subsurface_try_from_wlr_surface(wlr_surface); - if (!subsurface) { - return NULL; - } - struct wlr_surface *parent = subsurface->parent; - if (!parent) { - wlr_log(WLR_DEBUG, "subsurface %p has no parent", subsurface); - return NULL; - } - struct wlr_layer_surface_v1 *wlr_layer_surface = - wlr_layer_surface_v1_try_from_wlr_surface(parent); - if (wlr_layer_surface) { - return wlr_layer_surface; - } - /* Recurse in case there are nested sub-surfaces */ - return subsurface_parent_layer(parent); -} diff --git a/src/desktop.c b/src/desktop.c index 766a3e3a..eb712562 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -9,7 +9,6 @@ #include #include #include "common/scene-helpers.h" -#include "common/surface-helpers.h" #include "dnd.h" #include "labwc.h" #include "layers.h" @@ -312,9 +311,8 @@ get_cursor_context(struct server *server) } return ret; case LAB_NODE_LAYER_SURFACE: - ret.node = node; ret.type = LAB_NODE_LAYER_SURFACE; - ret.surface = get_surface_from_layer_node(node); + ret.surface = lab_wlr_surface_from_node(ret.node); return ret; case LAB_NODE_LAYER_POPUP: ret.node = node; @@ -374,29 +372,6 @@ get_cursor_context(struct server *server) } } - /* Edge-case nodes without node-descriptors */ - if (node->type == WLR_SCENE_NODE_BUFFER) { - struct wlr_surface *surface = lab_wlr_surface_from_node(node); - - /* - * Handle layer-shell subsurfaces - * - * These don't have node-descriptors, but need to be - * able to receive pointer actions so we have to process - * them here. - * - * Test by running `gtk-layer-demo -k exclusive`, then - * open the 'set margin' dialog and try setting the - * margin with the pointer. - */ - if (surface && wlr_subsurface_try_from_wlr_surface(surface) - && subsurface_parent_layer(surface)) { - ret.surface = surface; - ret.type = LAB_NODE_LAYER_SUBSURFACE; - return ret; - } - } - /* node->parent is always a *wlr_scene_tree */ node = node->parent ? &node->parent->node : NULL; } diff --git a/src/input/cursor.c b/src/input/cursor.c index 36a9046b..f3ae9f31 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -19,7 +19,6 @@ #include "action.h" #include "common/macros.h" #include "common/mem.h" -#include "common/surface-helpers.h" #include "config/mousebind.h" #include "config/rcxml.h" #include "dnd.h" @@ -1075,6 +1074,25 @@ process_press_mousebinding(struct server *server, struct cursor_context *ctx, return consumed_by_frame_context; } +static struct wlr_layer_surface_v1 * +get_root_layer(struct wlr_surface *wlr_surface) +{ + assert(wlr_surface); + struct wlr_subsurface *subsurface = + wlr_subsurface_try_from_wlr_surface(wlr_surface); + if (subsurface) { + if (subsurface->parent) { + return get_root_layer(subsurface->parent); + } else { + /* never reached? */ + wlr_log(WLR_ERROR, "subsurface without parent"); + return NULL; + } + } else { + return wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface); + } +} + static uint32_t press_msec; bool @@ -1107,16 +1125,8 @@ cursor_process_button_press(struct seat *seat, uint32_t button, uint32_t time_ms * the Focus action (used for normal views) does not work. */ if (ctx.type == LAB_NODE_LAYER_SURFACE) { - wlr_log(WLR_DEBUG, "press on layer-surface"); - struct wlr_layer_surface_v1 *layer = - wlr_layer_surface_v1_try_from_wlr_surface(ctx.surface); - if (layer && layer->current.keyboard_interactive) { - layer_try_set_focus(seat, layer); - } - } else if (ctx.type == LAB_NODE_LAYER_SUBSURFACE) { - wlr_log(WLR_DEBUG, "press on layer-subsurface"); - struct wlr_layer_surface_v1 *layer = - subsurface_parent_layer(ctx.surface); + wlr_log(WLR_DEBUG, "press on layer-(sub)surface"); + struct wlr_layer_surface_v1 *layer = get_root_layer(ctx.surface); if (layer && layer->current.keyboard_interactive) { layer_try_set_focus(seat, layer); } @@ -1127,7 +1137,7 @@ cursor_process_button_press(struct seat *seat, uint32_t button, uint32_t time_ms #endif } - if (ctx.type != LAB_NODE_CLIENT && ctx.type != LAB_NODE_LAYER_SUBSURFACE + if (ctx.type != LAB_NODE_CLIENT && ctx.type != LAB_NODE_LAYER_SURFACE && wlr_seat_pointer_has_grab(seat->seat)) { /* * If we have an active popup grab (an open popup) we want to From 3a3b873d0eca507b6fd6c89bf789c737107dd6c4 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Tue, 9 Sep 2025 02:53:31 +0900 Subject: [PATCH 159/248] desktop: let get_cursor_context() return layer-popup subsurface With this change, when a layer-popup has a subsurface and cursor is on it, wl_pointer.enter/motion events are sent to the subsurface rather than its parent layer-popup surface. I think this follows wayland spec more closely. Tested with my demo: https://github.com/tokyo4j/wayland-demo/tree/layer-popup --- src/desktop.c | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/src/desktop.c b/src/desktop.c index eb712562..3ed19082 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -242,23 +242,6 @@ desktop_update_top_layer_visibility(struct server *server) } } -static struct wlr_surface * -get_surface_from_layer_node(struct wlr_scene_node *node) -{ - assert(node->data); - struct node_descriptor *desc = (struct node_descriptor *)node->data; - if (desc->type == LAB_NODE_LAYER_SURFACE) { - struct lab_layer_surface *surface; - surface = node_layer_surface_from_node(node); - return surface->scene_layer_surface->layer_surface->surface; - } else if (desc->type == LAB_NODE_LAYER_POPUP) { - struct lab_layer_popup *popup; - popup = node_layer_popup_from_node(node); - return popup->wlr_popup->base->surface; - } - return NULL; -} - /* TODO: make this less big and scary */ struct cursor_context get_cursor_context(struct server *server) @@ -315,11 +298,7 @@ get_cursor_context(struct server *server) ret.surface = lab_wlr_surface_from_node(ret.node); return ret; case LAB_NODE_LAYER_POPUP: - ret.node = node; - ret.type = LAB_NODE_CLIENT; - ret.surface = get_surface_from_layer_node(node); - return ret; - case LAB_NODE_SESSION_LOCK_SURFACE: /* fallthrough */ + case LAB_NODE_SESSION_LOCK_SURFACE: case LAB_NODE_IME_POPUP: ret.type = LAB_NODE_CLIENT; ret.surface = lab_wlr_surface_from_node(ret.node); From 9f7e532852ba199b845f6ac9c98804010fbe378e Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Tue, 9 Sep 2025 02:56:37 +0900 Subject: [PATCH 160/248] desktop: refactor get_cursor_context() This patch should not change any behaviors. This clarifies the semantics of cursor_context returned by get_cursor_context() as I described in cursor.h; when cursor is on a subsurface (e.g. xdg/x11/layer/session-lock), the returned ctx.surface and ctx.node points to the subsurface rather than its parent. --- include/input/cursor.h | 32 +++++++++++--------------------- src/desktop.c | 10 ++++------ 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/include/input/cursor.h b/include/input/cursor.h index 5afa831b..13ab72d1 100644 --- a/include/input/cursor.h +++ b/include/input/cursor.h @@ -39,30 +39,20 @@ struct cursor_context { }; /** - * get_cursor_context - find view and scene_node at cursor + * get_cursor_context - find view, surface and scene_node at cursor * - * Behavior if node points to a surface: - * - If surface is a layer-surface, type will be - * set to LAB_NODE_LAYER_SURFACE and view will be NULL. + * If the cursor is on a client-drawn surface: + * - ctx.{surface,node} points to the surface, which may be a subsurface. + * - ctx.view is set if the node is associated to a xdg/x11 window. + * - ctx.type is LAYER_SURFACE or UNMANAGED if the node is a layer-shell + * surface or an X11 unmanaged surface. Otherwise, CLIENT is set. * - * - If surface is a 'lost' unmanaged xsurface (one - * with a never-mapped parent view), type will - * be set to LAB_NODE_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 type to be set to - * LAB_NODE_CLIENT and return the attached view. - * - * Behavior if node points to internal elements: - * - type will be set to the appropriate 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, - * type will be set to LAB_NODE_ROOT and view will be NULL. + * If the cursor is on a server-side component (SSD part and menu item): + * - ctx.node points to the root node of that component + * - ctx.view is set if the component is a SSD part + * - ctx.type specifies the component (e.g. MENU_ITEM, BORDER_TOP, BUTTON_ICONIFY) * + * If no node is found at cursor, ctx.type is set to ROOT. */ struct cursor_context get_cursor_context(struct server *server); diff --git a/src/desktop.c b/src/desktop.c index 3ed19082..dc2aa1a2 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -262,18 +262,18 @@ get_cursor_context(struct server *server) dnd_icons_show(&server->seat, true); } - ret.node = node; if (!node) { ret.type = LAB_NODE_ROOT; return ret; } + ret.node = node; + ret.surface = lab_wlr_surface_from_node(node); #if HAVE_XWAYLAND + /* TODO: attach LAB_NODE_UNMANAGED node-descriptor to unmanaged surfaces */ if (node->type == WLR_SCENE_NODE_BUFFER) { - struct wlr_surface *surface = lab_wlr_surface_from_node(node); if (node->parent == server->unmanaged_tree) { ret.type = LAB_NODE_UNMANAGED; - ret.surface = surface; return ret; } } @@ -285,7 +285,6 @@ get_cursor_context(struct server *server) case LAB_NODE_VIEW: case LAB_NODE_XDG_POPUP: ret.view = desc->view; - ret.surface = lab_wlr_surface_from_node(ret.node); if (ret.surface) { ret.type = LAB_NODE_CLIENT; } else { @@ -295,13 +294,11 @@ get_cursor_context(struct server *server) return ret; case LAB_NODE_LAYER_SURFACE: ret.type = LAB_NODE_LAYER_SURFACE; - ret.surface = lab_wlr_surface_from_node(ret.node); return ret; case LAB_NODE_LAYER_POPUP: case LAB_NODE_SESSION_LOCK_SURFACE: case LAB_NODE_IME_POPUP: ret.type = LAB_NODE_CLIENT; - ret.surface = lab_wlr_surface_from_node(ret.node); return ret; case LAB_NODE_MENUITEM: /* Always return the top scene node for menu items */ @@ -312,6 +309,7 @@ get_cursor_context(struct server *server) case LAB_NODE_SSD_ROOT: case LAB_NODE_TITLE: case LAB_NODE_TITLEBAR: + /* Always return the top scene node for ssd parts */ ret.node = node; ret.view = desc->view; /* From f2639226c7431bc11472ca19a4994e722d33ad22 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Tue, 9 Sep 2025 21:21:04 +0100 Subject: [PATCH 161/248] labwc-config(5): add example for autoEnableOutputs ...with inspiration from example in #3059 by @jlindgren90 --- docs/labwc-config.5.scd | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index 95600d3b..a854d886 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -228,6 +228,23 @@ this is for compatibility with Openbox. unless an external tool such as `wlr-randr` or `kanshi` is used to manage outputs. + The reason for the existance of this option is that after losing signal + from the PC (e.g. by `wlopm -off`), some monitors do an input detection + that makes it appear (from the PC side) to disconnect and reconnect a + few seconds later, causing the monitor to turn back on again (as labwc + auto-enables newly connected outputs by default). + + An example usage pattern to avoid the above behavior looks as follows: + - Set ** to *no* + - Run kanshi (e.g. from autostart) and rely on it to enable new outputs + - Have swayidle kill and restart kanshi when entering powersave as + follows: + + ``` + swayidle -w timeout 600 \\ + 'pkill kanshi ; wlopm --off \*' resume 'kanshi & wlopm --on \*' + ``` + ** [yes|no] Try to re-use the existing output mode (resolution / refresh rate). This may prevent unnecessary screenblank delays when starting labwc From e17ec0203c3ea837829e8a50b62714430823c789 Mon Sep 17 00:00:00 2001 From: "thatonecoder (formerly Coccocoa's Helper)" <157546848+Coccocoahelper@users.noreply.github.com> Date: Thu, 11 Sep 2025 08:47:31 +0100 Subject: [PATCH 162/248] CONTRIBUTING.md: fix some typos, lots of Oxford commas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The primary change is “Github”/“github” ⇾ “GitHub”, but there are plenty of others. --- CONTRIBUTING.md | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c1025234..1a2251ad 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,14 +23,14 @@ # How to Contribute -1. Report bugs as github issues. We use a template prompting you to provide +1. Report bugs as GitHub issues. We use a template prompting you to provide some sensible information such as what happened, what you expected to happen - and steps to reproduce. If applicable try with default configuration. If - you are able to, try to do some debugging (guidelines below). + and steps to reproduce. If applicable try with default configuration. If + you are able to, try debugging (guidelines below). -2. Submit patches as github pull-requests. If you wish to introduces significant +2. Submit patches as GitHub pull requests. If you wish to introduce significant changes or new features, consult the [scope document], discuss on IRC or via - a github issue first. + a GitHub issue first. # Debugging @@ -251,14 +251,14 @@ We try to keep the use of GLib pretty minimal for the following reasons: - The use of GLib has been known to make AddressSanitiser diagnose false positives and negatives. - Log messages coming from GLib functions look inconsistent. -- The use of GLib functions, naming-conventions and iterators in a code base +- The use of GLib functions, naming-conventions and iterators in a codebase that is predominantly ANSI C creates a clash which makes readability and maintainability harder. - Mixing gmalloc()/malloc() and respective free()s can create problems with memory pools [^1] -Having said that, with our use of cairo and pango we depend on glib-2.0 anyway -so linking with it and making use of some of its helper functions comes for free +Having said that, with our use of cairo and pango we depend on glib-2.0 anyway, +so linking with it and making use of some of its helper functions comes for free, and can keep the code simpler. For example, if we were going to carry out extensive string manipulation, @@ -291,7 +291,7 @@ match(const gchar *pattern, const gchar *string) ### The Use of GNU Extensions -We avoid [GNU C extensions] because we want to fit into the eco-system +We avoid [GNU C extensions] because we want to fit into the ecosystem (wayland and wlroots) we live in. We do use `__typeof__` which strictly speaking is a GNU C extension (`typeof`) @@ -300,18 +300,18 @@ but through the use of `__` is supported by gcc and clang without defining in the [`wl_container_of()`] macro which is needed in `wl_list*` and it does provide pretty big benefits in terms of type safety. -We compile with `-std=c11` because that's what 'wlroots' uses and we do not +We compile with `-std=c11` because that's what 'wlroots' uses, and we do not want to increase the entry-level for OSs without good reason (and currently we can't think of one). ### Naming Conventions -There are three types of coordinate systems: surface, output and layout - for +There are three types of coordinate systems: surface, output, and layout — for which the variables (sx, sy), (ox, oy) and (lx, ly) are used respectively in line with wlroots. With the introduction of the scene-graph API, some wlroots functions also use -node coordinates (nx, ny) but we prefer (sx, sy) where possible. +node coordinates (nx, ny), but we prefer (sx, sy) where possible. We do not worry about namespace issues too much and we try to not make the code a pain to use just to uniquify names. If we were writing a library we would @@ -361,11 +361,11 @@ In new files, please order `#include` lines as follows: compiles cleanly on its own, without implicit dependencies on other headers being included first. -- Then list any "system" headers (those not part of labwc) in alphebetical +- Then list any "system" headers (those not part of labwc) in alphabetical order, using angle brackets. This includes 3rd-party library headers such as ``, as well as wlroots headers. -- Then list any other labwc headers in alphetical order, using quotation +- Then list any other labwc headers in alphabetical order, using quotation marks and relative to the `include/` folder. Subfolders below `include/`, such as `common/`, should be specified even when including one header from another in the same folder (for example, `#include "common/buf.h"` @@ -388,11 +388,11 @@ This first line should: - In most cases be prefixed with "area: " where area refers to a filename or identifier for the general area of the code being modified. - Not capitalize the first word following the "area: " prefix, unless - it's a name, acronym or similar. + it's a name, acronym, or similar. - Skip the full stop And please wrap the commit message at max 74 characters, otherwise `git log` -and similar look so weird. URLs and other references are exempt. +and similar look very weird. URLs and other references are exempt. # Unit Tests @@ -404,9 +404,9 @@ In the bigger scheme of validating that the compositor meets users' needs, unit tests do not contribute a great deal. However, they have a role to play in providing some verification that stand-alone functions behave as expected. -On this project, writing unit-tests is not compulsory nor do we measure +On this project, writing unit-tests is not compulsory, nor do we measure coverage. The inclusion of the t/ directory does not signify a move towards -test-driven development. We intend to use unit tests sparingly and only when +test-driven development. We intend to use unit tests sparingly, and only when devs find them useful. ## Usage @@ -432,17 +432,17 @@ and use the web interface. Adding new languages should work, otherwise the administrators can be contacted. Suggestions for improving existing translations can be added without account. -### Github Pull Request +### GitHub Pull Request Translators can add their `MY_LOCALE.po` files to the `po` directory -based on `po/labwc.pot` and issue a pull request. To do this they can +based on `po/labwc.pot`, and issue a pull request. To do this they can generate their `MY_LOCALE.po` file in a few steps: 1. Edit the `po/LINGUAS` file to add their locale code in English alphabetical order to the field of locale codes. 2. Copy the `po/labwc.pot` to `po/MY_LOCALE.po` 3. Edit the newly generated `MY_LOCALE.po` file with some of their -contact and locale details in the header of the file then add the +contact and locale details in the header of the file. Then, add the translation strings under each English string. [See this tutorial for further guidance](https://www.labri.fr/perso/fleury/posts/programming/a-quick-gettext-tutorial.html) @@ -483,13 +483,13 @@ follow the steps to be taken: 2. Update `NEWS.md` with the release details and run `git commit -m 'NEWS.md: update notes for X.Y.Z'` Note: If new dependencies are needed, make this clear. -3. In `meson.build` update the version and (if required) the wlroots +3. In `meson.build`, update the version, and (if required) the wlroots dependency version. Then run `git commit -m 'build: bump version to X.Y.Z'` 4. Run `git tag -a X.Y.Z`. The first line of the commit message should be "labwc X.Y.Z" and the body should be the `NEWS.md` additions removing hash characters (#) from the headings as these will otherwise be ignored by git. -5. On github, create a 'Release' as some distros use this as a trigger. Set it +5. On GitHub, create a 'Release' as some distros use this as a trigger. Set it as 'latest release'. [scope document]: https://github.com/labwc/labwc-scope#readme @@ -510,4 +510,3 @@ follow the steps to be taken: different memory pools (and new/delete call constructors and destructors)." See: https://docs.gtk.org/glib/memory.html - From bca0ec07ac162ba7bed3d7aa66ed005bbd380063 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Mon, 8 Sep 2025 22:35:23 -0400 Subject: [PATCH 163/248] rcxml: use fixed arrays for rc.title_buttons_* These are just lists of enum lab_node_type, with a bounded size and no middle-insertions/removals, so linked lists are overkill. Also, the use of wl_list_for_each[_reverse] just to access the first or last entry in the list (corner button) was weird. --- include/config/rcxml.h | 16 +++++++++------- src/config/rcxml.c | 33 ++++++++++----------------------- src/ssd/ssd-titlebar.c | 19 ++++++++++--------- src/theme.c | 27 +++++++++------------------ 4 files changed, 38 insertions(+), 57 deletions(-) diff --git a/include/config/rcxml.h b/include/config/rcxml.h index 33c49376..89e215c0 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -14,6 +14,9 @@ #define BUTTON_MAP_MAX 16 +/* max of one button of each type (no repeats) */ +#define TITLE_BUTTONS_MAX ((LAB_NODE_BUTTON_LAST + 1) - LAB_NODE_BUTTON_FIRST) + enum adaptive_sync_mode { LAB_ADAPTIVE_SYNC_DISABLED, LAB_ADAPTIVE_SYNC_ENABLED, @@ -48,11 +51,6 @@ struct button_map_entry { uint32_t to; }; -struct title_button { - enum lab_node_type type; - struct wl_list link; -}; - struct usable_area_override { struct border margin; char *output; @@ -88,8 +86,12 @@ struct rcxml { char *theme_name; char *icon_theme_name; char *fallback_app_icon_name; - struct wl_list title_buttons_left; - struct wl_list title_buttons_right; + + enum lab_node_type title_buttons_left[TITLE_BUTTONS_MAX]; + int nr_title_buttons_left; + enum lab_node_type title_buttons_right[TITLE_BUTTONS_MAX]; + int nr_title_buttons_right; + int corner_radius; bool show_title; bool title_layout_loaded; diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 35195345..74733441 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -116,7 +116,8 @@ parse_window_type(const char *type) * desk D All-desktops toggle (aka omnipresent) */ static void -fill_section(const char *content, struct wl_list *list, uint32_t *found_buttons) +fill_section(const char *content, enum lab_node_type *buttons, int *count, + uint32_t *found_buttons /* bitmask */) { gchar **identifiers = g_strsplit(content, ",", -1); for (size_t i = 0; identifiers[i]; ++i) { @@ -162,9 +163,8 @@ fill_section(const char *content, struct wl_list *list, uint32_t *found_buttons) *found_buttons |= (1 << type); - struct title_button *item = znew(*item); - item->type = type; - wl_list_append(list, &item->link); + assert(*count < TITLE_BUTTONS_MAX); + buttons[(*count)++] = type; } g_strfreev(identifiers); } @@ -172,15 +172,8 @@ fill_section(const char *content, struct wl_list *list, uint32_t *found_buttons) static void clear_title_layout(void) { - struct title_button *button, *button_tmp; - wl_list_for_each_safe(button, button_tmp, &rc.title_buttons_left, link) { - wl_list_remove(&button->link); - zfree(button); - } - wl_list_for_each_safe(button, button_tmp, &rc.title_buttons_right, link) { - wl_list_remove(&button->link); - zfree(button); - } + rc.nr_title_buttons_left = 0; + rc.nr_title_buttons_right = 0; rc.title_layout_loaded = false; } @@ -189,11 +182,6 @@ fill_title_layout(char *content) { clear_title_layout(); - struct wl_list *sections[] = { - &rc.title_buttons_left, - &rc.title_buttons_right, - }; - gchar **parts = g_strsplit(content, ":", -1); if (g_strv_length(parts) != 2) { @@ -202,9 +190,10 @@ fill_title_layout(char *content) } uint32_t found_buttons = 0; - for (size_t i = 0; parts[i]; ++i) { - fill_section(parts[i], sections[i], &found_buttons); - } + fill_section(parts[0], rc.title_buttons_left, + &rc.nr_title_buttons_left, &found_buttons); + fill_section(parts[1], rc.title_buttons_right, + &rc.nr_title_buttons_right, &found_buttons); rc.title_layout_loaded = true; err: @@ -1362,8 +1351,6 @@ rcxml_init(void) static bool has_run; if (!has_run) { - wl_list_init(&rc.title_buttons_left); - wl_list_init(&rc.title_buttons_right); wl_list_init(&rc.usable_area_overrides); wl_list_init(&rc.keybinds); wl_list_init(&rc.mousebinds); diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 595a203d..13f5e7df 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -81,7 +81,6 @@ ssd_titlebar_create(struct ssd *ssd) LAB_NODE_TITLE, view, /*data*/ NULL); /* Buttons */ - struct title_button *b; int x = theme->window_titlebar_padding_width; /* Center vertically within titlebar */ @@ -90,20 +89,22 @@ ssd_titlebar_create(struct ssd *ssd) wl_list_init(&subtree->buttons_left); wl_list_init(&subtree->buttons_right); - wl_list_for_each(b, &rc.title_buttons_left, link) { + for (int b = 0; b < rc.nr_title_buttons_left; b++) { + enum lab_node_type type = rc.title_buttons_left[b]; struct lab_img **imgs = - theme->window[active].button_imgs[b->type]; - attach_ssd_button(&subtree->buttons_left, b->type, parent, + theme->window[active].button_imgs[type]; + attach_ssd_button(&subtree->buttons_left, type, parent, imgs, x, y, view); x += theme->window_button_width + theme->window_button_spacing; } x = width - theme->window_titlebar_padding_width + theme->window_button_spacing; - wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { + for (int b = rc.nr_title_buttons_right - 1; b >= 0; b--) { x -= theme->window_button_width + theme->window_button_spacing; + enum lab_node_type type = rc.title_buttons_right[b]; struct lab_img **imgs = - theme->window[active].button_imgs[b->type]; - attach_ssd_button(&subtree->buttons_right, b->type, parent, + theme->window[active].button_imgs[type]; + attach_ssd_button(&subtree->buttons_right, type, parent, imgs, x, y, view); } } @@ -223,8 +224,8 @@ update_visible_buttons(struct ssd *ssd) int width = MAX(view->current.width - 2 * theme->window_titlebar_padding_width, 0); int button_width = theme->window_button_width; int button_spacing = theme->window_button_spacing; - int button_count_left = wl_list_length(&rc.title_buttons_left); - int button_count_right = wl_list_length(&rc.title_buttons_right); + int button_count_left = rc.nr_title_buttons_left; + int button_count_right = rc.nr_title_buttons_right; /* Make sure infinite loop never occurs */ assert(button_width > 0); diff --git a/src/theme.c b/src/theme.c index f0b48158..d5279367 100644 --- a/src/theme.c +++ b/src/theme.c @@ -231,25 +231,16 @@ load_button(struct theme *theme, struct button *b, int active) struct lab_img **rounded_img = &button_imgs[b->type][b->state_set | LAB_BS_ROUNDED]; - struct title_button *leftmost_button; - wl_list_for_each(leftmost_button, - &rc.title_buttons_left, link) { - if (leftmost_button->type == b->type) { - *rounded_img = lab_img_copy(*img); - lab_img_add_modifier(*rounded_img, - round_left_corner_button); - } - break; + if (rc.nr_title_buttons_left > 0 + && b->type == rc.title_buttons_left[0]) { + *rounded_img = lab_img_copy(*img); + lab_img_add_modifier(*rounded_img, round_left_corner_button); } - struct title_button *rightmost_button; - wl_list_for_each_reverse(rightmost_button, - &rc.title_buttons_right, link) { - if (rightmost_button->type == b->type) { - *rounded_img = lab_img_copy(*img); - lab_img_add_modifier(*rounded_img, - round_right_corner_button); - } - break; + if (rc.nr_title_buttons_right > 0 + && b->type == rc.title_buttons_right + [rc.nr_title_buttons_right - 1]) { + *rounded_img = lab_img_copy(*img); + lab_img_add_modifier(*rounded_img, round_right_corner_button); } } From 0ce10f6afa5093dd92e0b0e317f136ca4b177fff Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Sat, 6 Sep 2025 23:31:00 -0400 Subject: [PATCH 164/248] interactive: add braces around case containing declaration --- src/interactive.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/interactive.c b/src/interactive.c index f66a66b3..f9c7f771 100644 --- a/src/interactive.c +++ b/src/interactive.c @@ -101,7 +101,7 @@ interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges) cursor_shape = LAB_CURSOR_GRAB; break; - case LAB_INPUT_STATE_RESIZE: + case LAB_INPUT_STATE_RESIZE: { if (view->shaded || view->fullscreen || view->maximized == VIEW_AXIS_BOTH) { /* @@ -133,6 +133,7 @@ interactive_begin(struct view *view, enum input_mode mode, enum lab_edge edges) view_set_untiled(view); cursor_shape = cursor_get_from_edge(edges); break; + } default: /* Should not be reached */ return; From 072d45d4b2e1ee3773084320e674a460c9356fb9 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Sat, 6 Sep 2025 23:31:38 -0400 Subject: [PATCH 165/248] osd-thumbnail: put designated initializers in order --- src/osd/osd-thumbnail.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/osd/osd-thumbnail.c b/src/osd/osd-thumbnail.c index 0345582a..24505a31 100644 --- a/src/osd/osd-thumbnail.c +++ b/src/osd/osd-thumbnail.c @@ -130,12 +130,12 @@ create_item_scene(struct wlr_scene_tree *parent, struct view *view, /* background for selected item */ struct lab_scene_rect_options opts = { + .border_colors = (float *[1]) { switcher_theme->item_active_border_color }, + .nr_borders = 1, + .border_width = switcher_theme->item_active_border_width, + .bg_color = switcher_theme->item_active_bg_color, .width = switcher_theme->item_width, .height = switcher_theme->item_height, - .bg_color = switcher_theme->item_active_bg_color, - .nr_borders = 1, - .border_colors = (float *[1]) { switcher_theme->item_active_border_color }, - .border_width = switcher_theme->item_active_border_width, }; item->active_bg = lab_scene_rect_create(item->tree, &opts); @@ -241,12 +241,12 @@ osd_thumbnail_create(struct output *output, struct wl_array *views) /* background */ struct lab_scene_rect_options bg_opts = { - .width = nr_cols * switcher_theme->item_width + 2 * padding, - .height = nr_rows * switcher_theme->item_height + 2 * padding, - .bg_color = theme->osd_bg_color, + .border_colors = (float *[1]) { theme->osd_border_color }, .nr_borders = 1, .border_width = theme->osd_border_width, - .border_colors = (float *[1]) { theme->osd_border_color }, + .bg_color = theme->osd_bg_color, + .width = nr_cols * switcher_theme->item_width + 2 * padding, + .height = nr_rows * switcher_theme->item_height + 2 * padding, }; struct lab_scene_rect *bg = lab_scene_rect_create(output->osd_scene.tree, &bg_opts); From 5ce20b2b958df6638752f0579eba06b5a95449fd Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Mon, 8 Sep 2025 20:10:42 -0400 Subject: [PATCH 166/248] rcxml: use const char* for string literals --- src/config/rcxml.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 74733441..aeeecc20 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -178,7 +178,7 @@ clear_title_layout(void) } static void -fill_title_layout(char *content) +fill_title_layout(const char *content) { clear_title_layout(); From d54051d9c12eeb713251781e9f5273b39565879d Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Mon, 8 Sep 2025 20:34:27 -0400 Subject: [PATCH 167/248] clang-format: tweak to match existing code a little better "clang-format -i src/view.c" before: 1 file changed, 204 insertions(+), 169 deletions(-) "clang-format -i src/view.c" after: 1 file changed, 181 insertions(+), 146 deletions(-) --- .clang-format | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.clang-format b/.clang-format index a8e09433..b3b6af52 100644 --- a/.clang-format +++ b/.clang-format @@ -12,14 +12,21 @@ UseTab: Always IndentWidth: 8 ContinuationIndentWidth: 8 AlignAfterOpenBracket: DontAlign +AlignOperands: false AlwaysBreakAfterDefinitionReturnType: true BreakBeforeBinaryOperators: NonAssignment BreakBeforeBraces: Linux IndentCaseLabels: false +PenaltyBreakOpenParenthesis: 100 +PenaltyReturnTypeOnItsOwnLine: 500 SpaceBeforeParens: ControlStatementsExceptControlMacros ForEachMacros: ['for_each_view', + 'for_each_view_reverse', 'wl_array_for_each', 'wl_list_for_each', 'wl_list_for_each_reverse', 'wl_list_for_each_reverse_safe', 'wl_list_for_each_safe'] +IncludeCategories: + - Regex: '<.*>' + - Regex: '.*' From f09ace51bfb85b82cba8e434c69c6466aabd3f90 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 15 Sep 2025 03:31:56 +0900 Subject: [PATCH 168/248] view: fix Before this commit, branch was always executed with monitor="current", monitor="left" or monitor="right" queries. For example: --- src/view.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/view.c b/src/view.c index ef871313..97616cbc 100644 --- a/src/view.c +++ b/src/view.c @@ -215,20 +215,25 @@ view_matches_query(struct view *view, struct view_query *query) if (query->monitor) { struct output *current = output_nearest_to_cursor(view->server); - - if (!strcasecmp(query->monitor, "current") && current != view->output) { - return false; - } - if (!strcasecmp(query->monitor, "left") && - output_get_adjacent(current, LAB_EDGE_LEFT, false) != view->output) { - return false; - } - if (!strcasecmp(query->monitor, "right") && - output_get_adjacent(current, LAB_EDGE_RIGHT, false) != view->output) { - return false; - } - if (output_from_name(view->server, query->monitor) != view->output) { - return false; + if (!strcasecmp(query->monitor, "current")) { + if (current != view->output) { + return false; + } + } else if (!strcasecmp(query->monitor, "left")) { + if (output_get_adjacent(current, LAB_EDGE_LEFT, false) + != view->output) { + return false; + } + } else if (!strcasecmp(query->monitor, "right")) { + if (output_get_adjacent(current, LAB_EDGE_RIGHT, false) + != view->output) { + return false; + } + } else { + if (output_from_name(view->server, query->monitor) + != view->output) { + return false; + } } } From 268ef857db51699d284422c9fac46711c70999a8 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Mon, 15 Sep 2025 19:31:05 +0100 Subject: [PATCH 169/248] NEWS.md: interim update --- NEWS.md | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 8cfac48f..4216dad6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,7 +9,7 @@ The format is based on [Keep a Changelog] | Date | All Changes | wlroots version | lines-of-code | |------------|---------------|-----------------|---------------| -| 2025-08-25 | [unreleased] | 0.19.0 | | +| 2025-09-15 | [unreleased] | 0.19.0 | 28686 | | 2025-08-02 | [0.9.1] | 0.19.0 | 28605 | | 2025-07-11 | [0.9.0] | 0.19.0 | 28586 | | 2025-05-02 | [0.8.4] | 0.18.2 | 27679 | @@ -110,6 +110,12 @@ There are some regression warnings worth noting for the switch to wlroots 0.19: ### Added +- Support `Border` context for mousebinds as an alias for `Top`...`BRCorner` to + make configuration easier. @tokyo4j [#3047] +- Add window-switcher mode with thumbnails. This can be enabled with: + ``. @tokyo4j [#2981] +- Add `toggle` option to `GoToDesktop` action. This has the effect of going back + to the last desktop if already on the target. @RainerKuemmerle [#3024] - Add `` to allow hiding titlebar when window is maximized. @CosmicFusion @tokyo4j [#3015] - Use client-send-to-menu as 'Workspace' submenu in built-in client-menu @@ -141,6 +147,10 @@ There are some regression warnings worth noting for the switch to wlroots 0.19: ### Fixed +- Restore initially-maximized window position after unplug/plug @tokyo4j [#3042] +- Fix large client-side icon not being loaded when the rendered icon size is + larger than icon sizes from the client. @tokyo4j [#3033] +- Improve debug logging for configuring input devices @jlindgren90 [#3028] - Fix false positives when matching desktop entries @datMaffin [#3004] - Prevent accidental downcasting of scale in scaled-icon-buffer to avoid blurry icons on non-integer scales and a cairo assert when using a output scale < 1. @@ -152,6 +162,16 @@ There are some regression warnings worth noting for the switch to wlroots 0.19: ### Changed +- `Focus` and `Raise` on window border press because it is probably what most + people expect and it makes the behavior consistent with that of Openbox. + @johanmalm [#3039] [#3049] +- On interactive resize, only un-maximize the axis/axes that are being resized. + @jlindgren90 [#3043] +- Change theme setting `osd.window-switcher.*` to + `osd.window-switcher.style-classic.*`. Backward compatibility is preserved. + @tokyo4j [#2981] +- In client-list menu, add brackets around the titles of any minimised windows + @davidphilipbarr [#3002] - Respect client-initiated window resize of non-maximized axis, for example remember the width of vertically-maximized window resizing itself horizontally. @jlindgren90 [#3020] @@ -2763,10 +2783,20 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16 [#2971]: https://github.com/labwc/labwc/pull/2971 [#2972]: https://github.com/labwc/labwc/pull/2972 [#2976]: https://github.com/labwc/labwc/pull/2976 +[#2981]: https://github.com/labwc/labwc/pull/2981 [#2994]: https://github.com/labwc/labwc/pull/2994 [#2995]: https://github.com/labwc/labwc/pull/2995 [#2998]: https://github.com/labwc/labwc/pull/2998 +[#3002]: https://github.com/labwc/labwc/pull/3002 [#3004]: https://github.com/labwc/labwc/pull/3004 [#3011]: https://github.com/labwc/labwc/pull/3011 [#3015]: https://github.com/labwc/labwc/pull/3015 [#3020]: https://github.com/labwc/labwc/pull/3020 +[#3024]: https://github.com/labwc/labwc/pull/3024 +[#3028]: https://github.com/labwc/labwc/pull/3028 +[#3033]: https://github.com/labwc/labwc/pull/3033 +[#3039]: https://github.com/labwc/labwc/pull/3039 +[#3042]: https://github.com/labwc/labwc/pull/3042 +[#3043]: https://github.com/labwc/labwc/pull/3043 +[#3047]: https://github.com/labwc/labwc/pull/3047 +[#3049]: https://github.com/labwc/labwc/pull/3049 From c6503e299feaf267b800c96251f3c1895a51d292 Mon Sep 17 00:00:00 2001 From: cunlem <87859114+cunlem@users.noreply.github.com> Date: Tue, 16 Sep 2025 18:48:20 +0000 Subject: [PATCH 170/248] Update labwc-actions.5.scd --- docs/labwc-actions.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/labwc-actions.5.scd b/docs/labwc-actions.5.scd index aa273111..4879873d 100644 --- a/docs/labwc-actions.5.scd +++ b/docs/labwc-actions.5.scd @@ -479,7 +479,7 @@ Actions that execute other actions. Used in keyboard/mouse bindings. The "left" , "right", "left-occupied" and "right-occupied" directions will not wrap. - *tiled* [up|right|down|left|top-left|top-right|down-left|down-right|center|any] + *tiled* [up|right|down|left|up-left|up-right|down-left|down-right|center|any] Whether the client is tiled (snapped) along the the indicated screen edge. From 387e62d87b51a387d1e07ab6d1118fd586cfb9c5 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Wed, 17 Sep 2025 21:12:08 +0100 Subject: [PATCH 171/248] README.md: remove yambar reference as discontinued --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 26ce81ca..7f21b2b5 100644 --- a/README.md +++ b/README.md @@ -249,7 +249,7 @@ Suggested apps to use with Labwc: - Screen shooter: [grim] - Screen recorder: [wf-recorder] - Background image: [swaybg] -- Panel: [waybar], [yambar], [lavalauncher], [sfwbar], [xfce4-panel] +- Panel: [waybar], [lavalauncher], [sfwbar], [xfce4-panel] - Launchers: [bemenu], [fuzzel], [wofi] - Output managers: [wlopm], [kanshi], [wlr-randr] - Screen locker: [swaylock] @@ -292,7 +292,6 @@ The default window bar menu can be translated on the [weblate platform](https:// [wf-recorder]: https://github.com/ammen99/wf-recorder [swaybg]: https://github.com/swaywm/swaybg [waybar]: https://github.com/Alexays/Waybar -[yambar]: https://codeberg.org/dnkl/yambar [lavalauncher]: https://sr.ht/~leon_plickat/LavaLauncher [sfwbar]: https://github.com/LBCrion/sfwbar [xfce4-panel]: https://gitlab.xfce.org/xfce/xfce4-panel From 0db3b9309bce5eca478781c3b5af3324e5db990b Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Thu, 18 Sep 2025 21:39:30 +0100 Subject: [PATCH 172/248] libsfdo.wrap: update revision to v0.1.4 --- subprojects/libsfdo.wrap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subprojects/libsfdo.wrap b/subprojects/libsfdo.wrap index 22df1d6a..d05264db 100644 --- a/subprojects/libsfdo.wrap +++ b/subprojects/libsfdo.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://gitlab.freedesktop.org/vyivel/libsfdo.git -revision = v0.1.3 +revision = v0.1.4 [provide] dependency_names = libsfdo-basedir, libsfdo-desktop, libsfdo-icon From af6a0df2311fa8b38c532285b3868a649f7998f5 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 15 Sep 2025 03:54:40 +0900 Subject: [PATCH 173/248] view: remove an obsolete code in view_snap_to_edge() We no longer need to call view_apply_tiled_geometry() there, since we now clear view->tiled when dragging a tiled window since 9f51384. --- src/view.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/view.c b/src/view.c index 97616cbc..13b36a54 100644 --- a/src/view.c +++ b/src/view.c @@ -2145,17 +2145,7 @@ view_snap_to_edge(struct view *view, enum lab_edge edge, if (across_outputs && view->tiled == edge && view->maximized == VIEW_AXIS_NONE) { /* We are already tiled for this edge; try to switch outputs */ output = output_get_adjacent(view->output, edge, /* wrap */ false); - if (!output) { - /* - * No more output to move to - * - * We re-apply the tiled geometry without changing any - * state because the window might have been moved away - * (and thus got untiled) and then snapped back to the - * original edge. - */ - view_apply_tiled_geometry(view); return; } From 2ac48116e14491abec2326e70190a32192df6764 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Fri, 5 Sep 2025 12:14:52 +0900 Subject: [PATCH 174/248] action: allow SnapToEdge to combine two cardinal directions This patch adds `combine` argument to (Toggle)SnapToEdge actions. This allows to snap a window to e.g. up-left by running two actions: - `` - `` Then running `` snaps it to left again. This behavior is almost the same as KWin, except that snapping a up-right-tiled window to right doesn't move it to the right-adjacent output, but makes it right-tiled first. --- docs/labwc-actions.5.scd | 17 +++++++++----- include/view.h | 2 +- src/action.c | 22 +++++++++++------- src/interactive.c | 5 ++--- src/view.c | 48 ++++++++++++++++++++++++++++++++-------- 5 files changed, 68 insertions(+), 26 deletions(-) diff --git a/docs/labwc-actions.5.scd b/docs/labwc-actions.5.scd index 4879873d..4a76a374 100644 --- a/docs/labwc-actions.5.scd +++ b/docs/labwc-actions.5.scd @@ -92,11 +92,18 @@ Actions are used in menus and keyboard/mouse bindings. Move window relative to its current position. Positive value of x moves it right, negative left. Positive value of y moves it down, negative up. -**++ -** - Resize window to fill half the output in the given direction. Supports - directions "left", "up", "right", "down", "up-left", "up-right", "down-left", - "down-right" and "center". +**++ +** + Resize window to fill half or quarter the output in the given direction. + + *direction* [up|down|left|right|up-left|up-right|down-left|down-right|center] + Direction in which to snap the window. + + *combine* [yes|no] + Allows to snap a window to an output corner by combining two + directions. For example, snapping a window to *right* and then + to *up* places it in the *up-right* quarter of the output. + Default is no. ToggleSnapToEdge additionally toggles the active window between tiled to the given direction and its untiled position. diff --git a/include/view.h b/include/view.h index 5ff70984..efdcf3fd 100644 --- a/include/view.h +++ b/include/view.h @@ -548,7 +548,7 @@ void view_move_to_edge(struct view *view, enum lab_edge direction, bool snap_to_ void view_grow_to_edge(struct view *view, enum lab_edge direction); void view_shrink_to_edge(struct view *view, enum lab_edge direction); void view_snap_to_edge(struct view *view, enum lab_edge direction, - bool across_outputs, bool store_natural_geometry); + bool across_outputs, bool combine, bool store_natural_geometry); void view_snap_to_region(struct view *view, struct region *region, bool store_natural_geometry); void view_move_to_output(struct view *view, struct output *output); diff --git a/src/action.c b/src/action.c index 60c57b33..ab9d3e18 100644 --- a/src/action.c +++ b/src/action.c @@ -337,11 +337,6 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char } break; case ACTION_TYPE_MOVE_TO_EDGE: - if (!strcasecmp(argument, "snapWindows")) { - action_arg_add_bool(action, argument, parse_bool(content, true)); - goto cleanup; - } - /* Falls through */ case ACTION_TYPE_TOGGLE_SNAP_TO_EDGE: case ACTION_TYPE_SNAP_TO_EDGE: case ACTION_TYPE_GROW_TO_EDGE: @@ -358,6 +353,17 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char } goto cleanup; } + if (action->type == ACTION_TYPE_MOVE_TO_EDGE + && !strcasecmp(argument, "snapWindows")) { + action_arg_add_bool(action, argument, parse_bool(content, true)); + goto cleanup; + } + if ((action->type == ACTION_TYPE_SNAP_TO_EDGE + || action->type == ACTION_TYPE_TOGGLE_SNAP_TO_EDGE) + && !strcasecmp(argument, "combine")) { + action_arg_add_bool(action, argument, parse_bool(content, false)); + goto cleanup; + } break; case ACTION_TYPE_SHOW_MENU: if (!strcmp(argument, "menu")) { @@ -1031,9 +1037,9 @@ run_action(struct view *view, struct server *server, struct action *action, view_apply_natural_geometry(view); break; } - view_snap_to_edge(view, edge, - /*across_outputs*/ true, - /*store_natural_geometry*/ true); + bool combine = action_get_bool(action, "combine", false); + view_snap_to_edge(view, edge, /*across_outputs*/ true, + combine, /*store_natural_geometry*/ true); } break; case ACTION_TYPE_GROW_TO_EDGE: diff --git a/src/interactive.c b/src/interactive.c index f9c7f771..8ca258c1 100644 --- a/src/interactive.c +++ b/src/interactive.c @@ -260,9 +260,8 @@ snap_to_edge(struct view *view) view_maximize(view, VIEW_AXIS_BOTH, /*store_natural_geometry*/ false); } else { - view_snap_to_edge(view, edge, - /*across_outputs*/ false, - /*store_natural_geometry*/ false); + view_snap_to_edge(view, edge, /*across_outputs*/ false, + /*combine*/ false, /*store_natural_geometry*/ false); } return true; diff --git a/src/view.c b/src/view.c index 13b36a54..e402b626 100644 --- a/src/view.c +++ b/src/view.c @@ -2126,7 +2126,7 @@ view_placement_parse(const char *policy) void view_snap_to_edge(struct view *view, enum lab_edge edge, - bool across_outputs, bool store_natural_geometry) + bool across_outputs, bool combine, bool store_natural_geometry) { assert(view); @@ -2142,15 +2142,45 @@ view_snap_to_edge(struct view *view, enum lab_edge edge, view_set_shade(view, false); - if (across_outputs && view->tiled == edge && view->maximized == VIEW_AXIS_NONE) { - /* We are already tiled for this edge; try to switch outputs */ - output = output_get_adjacent(view->output, edge, /* wrap */ false); - if (!output) { - return; - } + if (lab_edge_is_cardinal(edge) && view->maximized == VIEW_AXIS_NONE) { + enum lab_edge invert_edge = lab_edge_invert(edge); + /* Represents axis of snapping direction */ + enum lab_edge parallel_mask = edge | invert_edge; + /* + * The vector view->tiled is split to components + * parallel/orthogonal to snapping direction. For example, + * view->tiled=TOP_LEFT is split to parallel_tiled=TOP and + * orthogonal_tiled=LEFT when edge=TOP or edge=BOTTOM. + */ + enum lab_edge parallel_tiled = view->tiled & parallel_mask; + enum lab_edge orthogonal_tiled = view->tiled & ~parallel_mask; - /* When switching outputs, jump to the opposite edge */ - edge = lab_edge_invert(edge); + if (across_outputs && view->tiled == edge) { + /* + * E.g. when window is tiled to up and being snapped + * to up again, move it to the output above and tile + * it to down. + */ + output = output_get_adjacent(view->output, edge, + /* wrap */ false); + if (!output) { + return; + } + edge = invert_edge; + } else if (combine && parallel_tiled == invert_edge + && orthogonal_tiled != LAB_EDGE_NONE) { + /* + * E.g. when window is tiled to downleft/downright and + * being snapped to up, tile it to left/right. + */ + edge = view->tiled & ~parallel_mask; + } else if (combine && parallel_tiled == LAB_EDGE_NONE) { + /* + * E.g. when window is tiled to left/right and being + * snapped to up, tile it to upleft/upright. + */ + edge = view->tiled | edge; + } } if (view->maximized != VIEW_AXIS_NONE) { From 24f39e3a41b50cca3f0c26bada9295a3b39ded9e Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Wed, 17 Sep 2025 16:25:32 +0900 Subject: [PATCH 175/248] default-bindings.h: set combine="yes" for SnapToEdge keybinds --- docs/labwc-config.5.scd | 2 +- docs/rc.xml.all | 8 ++++---- include/config/default-bindings.h | 16 ++++++++++++++++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index a854d886..7ddf2d5b 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -727,7 +727,7 @@ extending outward from the snapped edge. W-Return - lab-sensible-terminal A-F4 - close window W-a - toggle maximize - W- - resize window to fill half the output + W- - resize window to fill half or quarter of the output A-Space - show window menu ``` diff --git a/docs/rc.xml.all b/docs/rc.xml.all index a96993ab..12d0c761 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -271,16 +271,16 @@ - + - + - + - + diff --git a/include/config/default-bindings.h b/include/config/default-bindings.h index 08caeade..9d42d237 100644 --- a/include/config/default-bindings.h +++ b/include/config/default-bindings.h @@ -35,6 +35,10 @@ static struct key_combos { .name = "direction", .value = "left", }, + .attributes[1] = { + .name = "combine", + .value = "yes", + }, }, { .binding = "W-Right", .action = "SnapToEdge", @@ -42,6 +46,10 @@ static struct key_combos { .name = "direction", .value = "right", }, + .attributes[1] = { + .name = "combine", + .value = "yes", + }, }, { .binding = "W-Up", .action = "SnapToEdge", @@ -49,6 +57,10 @@ static struct key_combos { .name = "direction", .value = "up", }, + .attributes[1] = { + .name = "combine", + .value = "yes", + }, }, { .binding = "W-Down", .action = "SnapToEdge", @@ -56,6 +68,10 @@ static struct key_combos { .name = "direction", .value = "down", }, + .attributes[1] = { + .name = "combine", + .value = "yes", + }, }, { .binding = "A-Space", .action = "ShowMenu", From 141f932efa5b4b8c6068ca1e72512a9b542edcbb Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Fri, 19 Sep 2025 20:50:03 +0100 Subject: [PATCH 176/248] README.md: remove high-level scope summary ...as it is very old and not relevant anymore. --- README.md | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/README.md b/README.md index 7f21b2b5..1e8d3759 100644 --- a/README.md +++ b/README.md @@ -101,21 +101,8 @@ spend our effort. A lot of emphasis is put on code simplicity when considering features. -The main development effort is focused on producing a solid foundation for a -stacking compositor rather than adding configuration and theming options. - See [scope] for full details on implemented features. -High-level summary of items that Labwc supports: - -- [x] Config files (rc.xml, autostart, shutdown, environment, menu.xml) -- [x] Theme files and xbm/png/svg icons -- [x] Basic desktop and client menus -- [x] HiDPI -- [x] wlroots protocols such as `output-management`, `layer-shell` and - `foreign-toplevel` -- [x] Optionally xwayland - ### 1.5 Videos | video link | date | duration From ebce406b11c94f5eb3c79f54f0128bfd17901a18 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sun, 21 Sep 2025 20:40:49 +0900 Subject: [PATCH 177/248] font: remove 4px padding on the right Added `menu.items.padding.x` padding between item text and arrow instead. Replaced `if (!string)` with `if (string_null_or_empty(string))` in `font_extents()` just as a minor optimization. --- src/common/font.c | 6 +----- src/menu/menu.c | 8 +++++--- src/ssd/resize-indicator.c | 3 --- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/common/font.c b/src/common/font.c index 81784ea8..b307729c 100644 --- a/src/common/font.c +++ b/src/common/font.c @@ -22,7 +22,7 @@ static PangoRectangle font_extents(struct font *font, const char *string) { PangoRectangle rect = { 0 }; - if (!string) { + if (string_null_or_empty(string)) { return rect; } cairo_surface_t *surface; @@ -43,10 +43,6 @@ font_extents(struct font *font, const char *string) pango_layout_get_extents(layout, NULL, &rect); pango_extents_to_pixels(&rect, NULL); - /* we put a 2 px edge on each side - because Openbox does it :) */ - /* TODO: remove the 4 pixel addition and always do the padding by the caller */ - rect.width += 4; - cairo_destroy(c); cairo_surface_destroy(surface); pango_font_description_free(desc); diff --git a/src/menu/menu.c b/src/menu/menu.c index e7a7b90c..455035ec 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -135,6 +135,7 @@ item_create(struct menu *menu, const char *text, const char *icon_name, bool sho assert(menu); assert(text); + struct theme *theme = menu->server->theme; struct menuitem *menuitem = znew(*menuitem); menuitem->parent = menu; menuitem->selectable = true; @@ -151,7 +152,8 @@ item_create(struct menu *menu, const char *text, const char *icon_name, bool sho menuitem->native_width = font_width(&rc.font_menuitem, text); if (menuitem->arrow) { - menuitem->native_width += font_width(&rc.font_menuitem, menuitem->arrow); + menuitem->native_width += font_width(&rc.font_menuitem, menuitem->arrow) + + theme->menu_items_padding_x; } wl_list_append(&menu->menuitems, &menuitem->link); @@ -177,7 +179,7 @@ item_create_scene_for_state(struct menuitem *item, float *text_color, int bg_width = menu->size.width - 2 * theme->menu_border_width; int arrow_width = item->arrow ? - font_width(&rc.font_menuitem, item->arrow) : 0; + font_width(&rc.font_menuitem, item->arrow) + theme->menu_items_padding_x : 0; int label_max_width = bg_width - 2 * theme->menu_items_padding_x - arrow_width - icon_width; @@ -227,7 +229,7 @@ item_create_scene_for_state(struct menuitem *item, float *text_color, scaled_font_buffer_update(arrow_buffer, item->arrow, -1, &rc.font_menuitem, text_color, bg_color); /* Vertically center and right-align arrow */ - x += label_max_width; + x += label_max_width + theme->menu_items_padding_x; y = (theme->menu_item_height - label_buffer->height) / 2; wlr_scene_node_set_position(&arrow_buffer->scene_buffer->node, x, y); diff --git a/src/ssd/resize-indicator.c b/src/ssd/resize-indicator.c index 6c22e656..3a635edc 100644 --- a/src/ssd/resize-indicator.c +++ b/src/ssd/resize-indicator.c @@ -193,9 +193,6 @@ resize_indicator_update(struct view *view) /* Let the indicator change width as required by the content */ int width = font_width(&rc.font_osd, text); - /* font_extents() adds 4 pixels to the calculated width */ - width -= 4; - resize_indicator_set_size(indicator, width); /* Center the indicator in the window */ From 7028e65154058afac01f77226e351332b1b669ec Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Tue, 23 Sep 2025 19:27:45 +0100 Subject: [PATCH 178/248] labnag: fix segfault caused by providing --timeout as long option --- clients/labnag.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clients/labnag.c b/clients/labnag.c index 20fb1dbe..bea25dd1 100644 --- a/clients/labnag.c +++ b/clients/labnag.c @@ -1370,7 +1370,7 @@ nag_parse_options(int argc, char **argv, struct nag *nag, {"detailed-button", required_argument, NULL, 'L'}, {"message", required_argument, NULL, 'm'}, {"output", required_argument, NULL, 'o'}, - {"timeout", no_argument, NULL, 't'}, + {"timeout", required_argument, NULL, 't'}, {"version", no_argument, NULL, 'v'}, {"background", required_argument, NULL, TO_COLOR_BACKGROUND}, From 57655866365c4422c8f3352960c0ccc088d27bce Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Mon, 22 Sep 2025 18:32:42 +0100 Subject: [PATCH 179/248] config: add `` ...to enable configuration of the action prompt command. Also set some better defaults for labnag. The new default command is: labnag \ --message '%m' \ --button-dismiss '%n' \ --button-dismiss '%y' \ --background '%b' \ --text '%t' \ --border '%t' \ --border-bottom '%t' \ --button-background '%b' \ --button-text '%t' \ --border-bottom-size 1 \ --button-border-size 3 \ --timeout 0 ...where the conversion specifiers are defined as follows: %m: the `` message option %n: _("No") %y: _("Yes") %b: osd.bg.color %t: osd.label.text.color This config options also enables the use of a different dialog client, for example like this: zenity --question --text="%m" --- docs/rc.xml.all | 4 ++ include/action-prompt-command.h | 12 ++++ include/action.h | 2 + include/common/buf.h | 11 ++++ include/config/rcxml.h | 2 + src/action-prompt-command.c | 108 ++++++++++++++++++++++++++++++++ src/action.c | 14 +++-- src/common/buf.c | 24 +++++++ src/config/rcxml.c | 21 +++++++ src/meson.build | 1 + 10 files changed, 193 insertions(+), 6 deletions(-) create mode 100644 include/action-prompt-command.h create mode 100644 src/action-prompt-command.c diff --git a/docs/rc.xml.all b/docs/rc.xml.all index 12d0c761..ed80fdf0 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -19,6 +19,10 @@ no no yes + diff --git a/include/action-prompt-command.h b/include/action-prompt-command.h new file mode 100644 index 00000000..219896b9 --- /dev/null +++ b/include/action-prompt-command.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_ACTION_PROMPT_COMMAND_H +#define LABWC_ACTION_PROMPT_COMMAND_H + +struct buf; +struct action; +struct theme; + +void action_prompt_command(struct buf *buf, const char *format, + struct action *action, struct theme *theme); + +#endif /* LABWC_ACTION_PROMPT_COMMAND_H */ diff --git a/include/action.h b/include/action.h index cb88d0d5..31ecfb6e 100644 --- a/include/action.h +++ b/include/action.h @@ -23,6 +23,8 @@ struct action { struct action *action_create(const char *action_name); +const char *action_get_str(struct action *action, const char *key, + const char *default_value); bool action_is_valid(struct action *action); bool action_is_show_menu(struct action *action); diff --git a/include/common/buf.h b/include/common/buf.h index a75c1144..857b6c48 100644 --- a/include/common/buf.h +++ b/include/common/buf.h @@ -50,6 +50,17 @@ void buf_expand_shell_variables(struct buf *s); */ void buf_add_fmt(struct buf *s, const char *fmt, ...); +/** + * buf_add_hex_color - add rgb color as hex string to C string buffer + * @s: buffer + * @color: rgb color to be added + * + * For example: + * - With the input 'red' (defined as red[4] = { 1.0f, 0.0f, 0.0f, 1.0f}) the + * string "#ff0000ff" will be written to the buffer. + */ +void buf_add_hex_color(struct buf *s, float color[4]); + /** * buf_add - add data to C string buffer * @s: buffer diff --git a/include/config/rcxml.h b/include/config/rcxml.h index 89e215c0..e3b7bf06 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -74,6 +74,8 @@ struct rcxml { enum lab_placement_policy placement_policy; bool xwayland_persistence; bool primary_selection; + char *prompt_command; + int placement_cascade_offset_x; int placement_cascade_offset_y; diff --git a/src/action-prompt-command.c b/src/action-prompt-command.c new file mode 100644 index 00000000..65bd0b49 --- /dev/null +++ b/src/action-prompt-command.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0-only +#define _POSIX_C_SOURCE 200809L +#include "action-prompt-command.h" +#include +#include +#include "action.h" +#include "common/buf.h" +#include "labwc.h" /* for gettext */ +#include "theme.h" + +enum { + LAB_PROMPT_NONE = 0, + LAB_PROMPT_MESSAGE, + LAB_PROMPT_NO, + LAB_PROMPT_YES, + LAB_PROMPT_BG_COL, + LAB_PROMPT_TEXT_COL, + + LAB_PROMPT_COUNT +}; + +typedef void field_conversion_type(struct buf *buf, struct action *action, + struct theme *theme); + +struct field_converter { + const char fmt_char; + field_conversion_type *fn; +}; + +/* %m */ +static void +set_message(struct buf *buf, struct action *action, struct theme *theme) +{ + buf_add(buf, action_get_str(action, "message.prompt", "Choose wisely")); +} + +/* %n */ +static void +set_no(struct buf *buf, struct action *action, struct theme *theme) +{ + buf_add(buf, _("No")); +} + +/* %y */ +static void +set_yes(struct buf *buf, struct action *action, struct theme *theme) +{ + buf_add(buf, _("Yes")); +} + +/* %b */ +static void +set_bg_col(struct buf *buf, struct action *action, struct theme *theme) +{ + buf_add_hex_color(buf, theme->osd_bg_color); +} + +/* %t */ +static void +set_text_col(struct buf *buf, struct action *action, struct theme *theme) +{ + buf_add_hex_color(buf, theme->osd_label_text_color); +} + +static const struct field_converter field_converter[LAB_PROMPT_COUNT] = { + [LAB_PROMPT_MESSAGE] = { 'm', set_message }, + [LAB_PROMPT_NO] = { 'n', set_no }, + [LAB_PROMPT_YES] = { 'y', set_yes }, + [LAB_PROMPT_BG_COL] = { 'b', set_bg_col }, + [LAB_PROMPT_TEXT_COL] = { 't', set_text_col }, +}; + +void +action_prompt_command(struct buf *buf, const char *format, + struct action *action, struct theme *theme) +{ + if (!format) { + wlr_log(WLR_ERROR, "missing format"); + return; + } + + for (const char *p = format; *p; p++) { + /* + * If we're not on a conversion specifier (like %m) then just + * keep adding it to the buffer + */ + if (*p != '%') { + buf_add_char(buf, *p); + continue; + } + + /* Process the %* conversion specifier */ + ++p; + + bool found = false; + for (unsigned char i = 0; i < LAB_PROMPT_COUNT; i++) { + if (*p == field_converter[i].fmt_char) { + field_converter[i].fn(buf, action, theme); + found = true; + break; + } + } + if (!found) { + wlr_log(WLR_ERROR, + "invalid prompt command conversion specifier '%c'", *p); + } + } +} diff --git a/src/action.c b/src/action.c index ab9d3e18..62ab2b2d 100644 --- a/src/action.c +++ b/src/action.c @@ -10,6 +10,7 @@ #include #include #include "action-prompt-codes.h" +#include "action-prompt-command.h" #include "common/buf.h" #include "common/macros.h" #include "common/list.h" @@ -281,7 +282,7 @@ action_get_arg(struct action *action, const char *key, enum action_arg_type type return NULL; } -static const char * +const char * action_get_str(struct action *action, const char *key, const char *default_value) { struct action_arg_str *arg = action_get_arg(action, key, LAB_ACTION_ARG_STR); @@ -833,12 +834,13 @@ handle_view_destroy(struct wl_listener *listener, void *data) static void action_prompt_create(struct view *view, struct server *server, struct action *action) { - char *command = strdup_printf("labnag -m \"%s\" -Z \"%s\" -Z \"%s\"", - action_get_str(action, "message.prompt", "Choose wisely"), - _("No"), _("Yes")); + struct buf command = BUF_INIT; + action_prompt_command(&command, rc.prompt_command, action, rc.theme); + + wlr_log(WLR_INFO, "prompt command: '%s'", command.data); int pipe_fd; - pid_t prompt_pid = spawn_piped(command, &pipe_fd); + pid_t prompt_pid = spawn_piped(command.data, &pipe_fd); if (prompt_pid < 0) { wlr_log(WLR_ERROR, "Failed to create action prompt"); goto cleanup; @@ -862,7 +864,7 @@ action_prompt_create(struct view *view, struct server *server, struct action *ac wl_list_insert(&prompts, &prompt->link); cleanup: - free(command); + buf_reset(&command); } bool diff --git a/src/common/buf.c b/src/common/buf.c index c98fc97f..bd8e82d0 100644 --- a/src/common/buf.c +++ b/src/common/buf.c @@ -128,6 +128,30 @@ buf_add_fmt(struct buf *s, const char *fmt, ...) s->data[s->len] = 0; } +void +buf_add_hex_color(struct buf *s, float color[4]) +{ + /* + * In theme.c parse_hexstr() colors are pre-multiplied (by alpha) as + * expected by wlr_scene(). We therefore need to reverse that here. + * + * For details, see https://github.com/labwc/labwc/pull/1685 + */ + float alpha = color[3]; + + /* Avoid division by zero */ + if (alpha == 0.0f) { + buf_add(s, "#00000000"); + return; + } + + buf_add_fmt(s, "#%02x%02x%02x%02x", + (int)(color[0] / alpha * 255), + (int)(color[1] / alpha * 255), + (int)(color[2] / alpha * 255), + (int)(alpha * 255)); +} + void buf_add(struct buf *s, const char *data) { diff --git a/src/config/rcxml.c b/src/config/rcxml.c index aeeecc20..162f3f1d 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -1102,6 +1102,10 @@ entry(xmlNode *node, char *nodename, char *content) set_bool(content, &rc.xwayland_persistence); } else if (!strcasecmp(nodename, "primarySelection.core")) { set_bool(content, &rc.primary_selection); + + } else if (!strcasecmp(nodename, "promptCommand.core")) { + xstrdup_replace(rc.prompt_command, content); + } else if (!strcmp(nodename, "policy.placement")) { enum lab_placement_policy policy = view_placement_parse(content); if (policy != LAB_PLACE_INVALID) { @@ -1624,6 +1628,22 @@ post_processing(void) load_default_mouse_bindings(); } + if (!rc.prompt_command) { + rc.prompt_command = + xstrdup("labnag " + "--message '%m' " + "--button-dismiss '%n' " + "--button-dismiss '%y' " + "--background '%b' " + "--text '%t' " + "--border '%t' " + "--border-bottom '%t' " + "--button-background '%b' " + "--button-text '%t' " + "--border-bottom-size 1 " + "--button-border-size 3 " + "--timeout 0"); + } if (!rc.fallback_app_icon_name) { rc.fallback_app_icon_name = xstrdup("labwc"); } @@ -1886,6 +1906,7 @@ rcxml_finish(void) zfree(rc.font_menuheader.name); zfree(rc.font_menuitem.name); zfree(rc.font_osd.name); + zfree(rc.prompt_command); zfree(rc.theme_name); zfree(rc.icon_theme_name); zfree(rc.fallback_app_icon_name); diff --git a/src/meson.build b/src/meson.build index 330b5daf..dc760f0c 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,5 +1,6 @@ labwc_sources = files( 'action.c', + 'action-prompt-command.c', 'buffer.c', 'debug.c', 'desktop.c', From 5fdebedcd99d03a38c50f650a3d016c0850811d7 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Tue, 23 Sep 2025 19:26:52 +0100 Subject: [PATCH 180/248] labwc-config(5): document --- docs/labwc-config.5.scd | 48 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index 7ddf2d5b..52cd9638 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -179,6 +179,7 @@ this is for compatibility with Openbox. no no yes + [see details below] ``` @@ -265,6 +266,53 @@ this is for compatibility with Openbox. up/down) in Chromium and electron based clients without inadvertantly pasting the primary clipboard. Default is yes. +** + Set command to be invoked for an action prompt (**) + + The following conversion specifiers are supported: + - *%m*: the ** message option + - *%n*: "No" (in local language if translation is available) + - *%y*: "Yes" (in local language if translation is available) + - *%b*: osd.bg.color + - *%t*: osd.label.text.color + + The default prompt command is: + + ``` + labnag \\ + --message '%m' \\ + --button-dismiss '%n' \\ + --button-dismiss '%y' \\ + --background '%b' \\ + --text '%t' \\ + --border '%t' \\ + --border-bottom '%t' \\ + --button-background '%b' \\ + --button-text '%t' \\ + --border-bottom-size 1 \\ + --button-border-size 3 \\ + --timeout 0 + ``` + + Example 1: The prompt can be configured to use a different dialog client + + ``` + + zenity --question --text="%m" + + ``` + + Example 2: A more complex zenity command could be used: + + ``` + zenity \\ + --question \\ + --title="" \\ + --text="%m" \\ + --ok-label="%y" \\ + --cancel-label="%n" + ``` + ## PLACEMENT ``` From 7a5b7aa37881690e26a01aada6ffcf668d0df52f Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Wed, 24 Sep 2025 20:16:37 +0100 Subject: [PATCH 181/248] rcxml.h: minor tweaks to order of variables --- include/config/rcxml.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/config/rcxml.h b/include/config/rcxml.h index e3b7bf06..b1c28d12 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -71,11 +71,12 @@ struct rcxml { enum tearing_mode allow_tearing; bool auto_enable_outputs; bool reuse_output_mode; - enum lab_placement_policy placement_policy; bool xwayland_persistence; bool primary_selection; char *prompt_command; + /* placement */ + enum lab_placement_policy placement_policy; int placement_cascade_offset_x; int placement_cascade_offset_y; From 6bbdc3c6dc3ba5bfd3702454325723b975845d2b Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Thu, 25 Sep 2025 19:24:52 +0100 Subject: [PATCH 182/248] Remove unused function menu_call_actions() --- include/menu/menu.h | 12 ------------ src/menu/menu.c | 9 --------- 2 files changed, 21 deletions(-) diff --git a/include/menu/menu.h b/include/menu/menu.h index a4d236d6..f65ff9a3 100644 --- a/include/menu/menu.h +++ b/include/menu/menu.h @@ -100,18 +100,6 @@ void menu_open_root(struct menu *menu, int x, int y); */ void menu_process_cursor_motion(struct wlr_scene_node *node); -/** - * menu_call_actions - call actions associated with a menu node - * - * 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 wlr_scene_node *node); - /** * menu_close_root- close root menu * diff --git a/src/menu/menu.c b/src/menu/menu.c index 455035ec..fca755b2 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -1544,15 +1544,6 @@ menu_process_cursor_motion(struct wlr_scene_node *node) menu_process_item_selection(item); } -bool -menu_call_actions(struct wlr_scene_node *node) -{ - assert(node && node->data); - struct menuitem *item = node_menuitem_from_node(node); - - return menu_execute_item(item); -} - void menu_close_root(struct server *server) { From 0bf2678f9dc2b3ca55881c1dcf3a60b36182988d Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Thu, 25 Sep 2025 19:25:51 +0100 Subject: [PATCH 183/248] Remove unused function scaled_font_buffer_set_max_width() --- include/scaled-buffer/scaled-font-buffer.h | 8 -------- src/scaled-buffer/scaled-font-buffer.c | 14 -------------- 2 files changed, 22 deletions(-) diff --git a/include/scaled-buffer/scaled-font-buffer.h b/include/scaled-buffer/scaled-font-buffer.h index 6953f9da..a5e95087 100644 --- a/include/scaled-buffer/scaled-font-buffer.h +++ b/include/scaled-buffer/scaled-font-buffer.h @@ -73,12 +73,4 @@ void scaled_font_buffer_update(struct scaled_font_buffer *self, const char *text int max_width, struct font *font, const float *color, const float *bg_color); -/** - * Update the max width of an existing auto scaling font buffer - * and force a new render. - * - * No steps are taken to detect if its actually required to render a new buffer. - */ -void scaled_font_buffer_set_max_width(struct scaled_font_buffer *self, int max_width); - #endif /* LABWC_SCALED_FONT_BUFFER_H */ diff --git a/src/scaled-buffer/scaled-font-buffer.c b/src/scaled-buffer/scaled-font-buffer.c index 61fd5db3..bb93fc67 100644 --- a/src/scaled-buffer/scaled-font-buffer.c +++ b/src/scaled-buffer/scaled-font-buffer.c @@ -139,17 +139,3 @@ scaled_font_buffer_update(struct scaled_font_buffer *self, const char *text, scaled_buffer_request_update(self->scaled_buffer, self->width, self->height); } - -void -scaled_font_buffer_set_max_width(struct scaled_font_buffer *self, int max_width) -{ - self->max_width = max_width; - - int computed_height; - font_get_buffer_size(self->max_width, self->text, &self->font, - &self->width, &computed_height); - self->height = (self->fixed_height > 0) ? - self->fixed_height : computed_height; - scaled_buffer_request_update(self->scaled_buffer, - self->width, self->height); -} From 139a5f0383b9e10d93d6d25c85c5abe7b4670877 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Thu, 25 Sep 2025 19:27:01 +0100 Subject: [PATCH 184/248] Remove unused function output_max_scale() --- include/output.h | 7 ------- src/output.c | 14 -------------- 2 files changed, 21 deletions(-) diff --git a/include/output.h b/include/output.h index 413b43c5..ceccaf9f 100644 --- a/include/output.h +++ b/include/output.h @@ -70,11 +70,4 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener, void *data); void output_enable_adaptive_sync(struct output *output, bool enabled); -/** - * output_max_scale() - get maximum scale factor of all usable outputs. - * Used when loading/rendering resources (e.g. icons) that may be - * displayed on any output. - */ -float output_max_scale(struct server *server); - #endif // LABWC_OUTPUT_H diff --git a/src/output.c b/src/output.c index 22e9fc4e..8345c653 100644 --- a/src/output.c +++ b/src/output.c @@ -1142,17 +1142,3 @@ output_enable_adaptive_sync(struct output *output, bool enabled) enabled ? "en" : "dis", output->wlr_output->name); } } - -float -output_max_scale(struct server *server) -{ - /* Never return less than 1, in case outputs are disabled */ - float scale = 1; - struct output *output; - wl_list_for_each(output, &server->outputs, link) { - if (output_is_usable(output)) { - scale = MAX(scale, output->wlr_output->scale); - } - } - return scale; -} From 34e52a40c740de0fb150e31871ce80e464e319ef Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Thu, 25 Sep 2025 19:33:55 +0100 Subject: [PATCH 185/248] Remove unused function node_layer_popup_from_node() --- include/node.h | 7 ------- src/node.c | 9 --------- 2 files changed, 16 deletions(-) diff --git a/include/node.h b/include/node.h index d6d177f0..bfad1373 100644 --- a/include/node.h +++ b/include/node.h @@ -45,13 +45,6 @@ struct view *node_view_from_node(struct wlr_scene_node *wlr_scene_node); struct lab_layer_surface *node_layer_surface_from_node( struct wlr_scene_node *wlr_scene_node); -/** - * node_layer_popup_from_node - return lab_layer_popup struct from node - * @wlr_scene_node: wlr_scene_node from which to return data - */ -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 diff --git a/src/node.c b/src/node.c index 025789e8..ce4fb040 100644 --- a/src/node.c +++ b/src/node.c @@ -50,15 +50,6 @@ node_layer_surface_from_node(struct wlr_scene_node *wlr_scene_node) return (struct lab_layer_surface *)node_descriptor->data; } -struct lab_layer_popup * -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_LAYER_POPUP); - return (struct lab_layer_popup *)node_descriptor->data; -} - struct menuitem * node_menuitem_from_node(struct wlr_scene_node *wlr_scene_node) { From ee87b4fc30914eca576e70c3fd6afa0a0ac77972 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Thu, 25 Sep 2025 19:35:22 +0100 Subject: [PATCH 186/248] Remove unused function trim_last_field() --- include/common/string-helpers.h | 9 --------- src/common/string-helpers.c | 9 --------- 2 files changed, 18 deletions(-) diff --git a/include/common/string-helpers.h b/include/common/string-helpers.h index 0509d33c..35c994b2 100644 --- a/include/common/string-helpers.h +++ b/include/common/string-helpers.h @@ -15,15 +15,6 @@ bool string_null_or_empty(const char *s); */ bool str_space_only(const char *s); -/** - * trim_last_field() - Trim last field of string splitting on provided delim - * @buf: string to trim - * @delim: delimitator - * - * Example: With delim='_' and buf="foo_bar_baz" the return value is "foo_bar" - */ -void trim_last_field(char *buf, char delim); - /** * string_strip - strip white space left and right * Note: this function does a left skip, so the returning pointer cannot be diff --git a/src/common/string-helpers.c b/src/common/string-helpers.c index a0d73034..13fe6f68 100644 --- a/src/common/string-helpers.c +++ b/src/common/string-helpers.c @@ -20,15 +20,6 @@ string_null_or_empty(const char *s) return !s || !*s; } -void -trim_last_field(char *buf, char delim) -{ - char *p = strrchr(buf, delim); - if (p) { - *p = '\0'; - } -} - static void rtrim(char *s) { From bdc8e1c546f76be969b6da9ee9f5054c1f2566eb Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Thu, 25 Sep 2025 19:36:37 +0100 Subject: [PATCH 187/248] Remove unused function lab_xml_get_node() --- include/common/xml.h | 1 - src/common/xml.c | 6 ------ 2 files changed, 7 deletions(-) diff --git a/include/common/xml.h b/include/common/xml.h index 8b49d0e4..16c319ec 100644 --- a/include/common/xml.h +++ b/include/common/xml.h @@ -30,7 +30,6 @@ void lab_xml_expand_dotted_attributes(xmlNode *root); /* Returns true if the node only contains a string or is empty */ bool lab_xml_node_is_leaf(xmlNode *node); -bool lab_xml_get_node(xmlNode *node, const char *key, xmlNode **dst_node); bool lab_xml_get_string(xmlNode *node, const char *key, char *s, size_t len); bool lab_xml_get_int(xmlNode *node, const char *key, int *i); bool lab_xml_get_bool(xmlNode *node, const char *key, bool *b); diff --git a/src/common/xml.c b/src/common/xml.c index 4868d7ed..7e089267 100644 --- a/src/common/xml.c +++ b/src/common/xml.c @@ -164,12 +164,6 @@ get_node(xmlNode *node, const char *key, xmlNode **dst_node, bool leaf_only) return false; } -bool -lab_xml_get_node(xmlNode *node, const char *key, xmlNode **dst_node) -{ - return get_node(node, key, dst_node, /* leaf_only */ false); -} - bool lab_xml_get_string(xmlNode *node, const char *key, char *s, size_t len) { From 1692c47fa08038640ef0a6442182320e68540149 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Thu, 25 Sep 2025 19:38:13 +0100 Subject: [PATCH 188/248] Remove unused function key_state_nr_pressed_keys() --- include/input/key-state.h | 1 - src/input/key-state.c | 6 ------ 2 files changed, 7 deletions(-) diff --git a/include/input/key-state.h b/include/input/key-state.h index c53a4b10..d9971b74 100644 --- a/include/input/key-state.h +++ b/include/input/key-state.h @@ -24,6 +24,5 @@ void key_state_store_pressed_key_as_bound(uint32_t keycode); bool key_state_corresponding_press_event_was_bound(uint32_t keycode); void key_state_bound_key_remove(uint32_t keycode); int key_state_nr_bound_keys(void); -int key_state_nr_pressed_keys(void); #endif /* LABWC_KEY_STATE_H */ diff --git a/src/input/key-state.c b/src/input/key-state.c index def6b72c..492f7b0f 100644 --- a/src/input/key-state.c +++ b/src/input/key-state.c @@ -84,9 +84,3 @@ key_state_nr_bound_keys(void) { return bound.size; } - -int -key_state_nr_pressed_keys(void) -{ - return pressed.size; -} From 26bd02d45730ccfa99bccb786a9a07e0aa32fd04 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Wed, 24 Sep 2025 20:25:27 +0100 Subject: [PATCH 189/248] Add translate.h for HAVE_NLS includes/defines ...to shrink labwc.h footprint --- include/labwc.h | 7 ------- include/translate.h | 14 ++++++++++++++ src/action-prompt-command.c | 2 +- src/config/rcxml.c | 1 + src/desktop-entry.c | 1 + src/main.c | 1 + src/menu/menu.c | 1 + 7 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 include/translate.h diff --git a/include/labwc.h b/include/labwc.h index 72dbe687..c75b1e39 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -7,13 +7,6 @@ #include "common/set.h" #include "input/cursor.h" #include "overlay.h" -#if HAVE_NLS -#include -#include -#define _ gettext -#else -#define _(s) (s) -#endif #define XCURSOR_DEFAULT "left_ptr" #define XCURSOR_SIZE 24 diff --git a/include/translate.h b/include/translate.h new file mode 100644 index 00000000..f7fc51e2 --- /dev/null +++ b/include/translate.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_TRANSLATE_H +#define LABWC_TRANSLATE_H +#include "config.h" + +#if HAVE_NLS +#include +#include +#define _ gettext +#else +#define _(s) (s) +#endif + +#endif /* LABWC_TRANSLATE_H */ diff --git a/src/action-prompt-command.c b/src/action-prompt-command.c index 65bd0b49..faad41c9 100644 --- a/src/action-prompt-command.c +++ b/src/action-prompt-command.c @@ -5,8 +5,8 @@ #include #include "action.h" #include "common/buf.h" -#include "labwc.h" /* for gettext */ #include "theme.h" +#include "translate.h" enum { LAB_PROMPT_NONE = 0, diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 162f3f1d..89293c43 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -33,6 +33,7 @@ #include "osd.h" #include "regions.h" #include "ssd.h" +#include "translate.h" #include "view.h" #include "window-rules.h" #include "workspaces.h" diff --git a/src/desktop-entry.c b/src/desktop-entry.c index 60cff28e..7aefa77e 100644 --- a/src/desktop-entry.c +++ b/src/desktop-entry.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #include "desktop-entry.h" +#include #include #include #include diff --git a/src/main.c b/src/main.c index 068829dc..755a0a0c 100644 --- a/src/main.c +++ b/src/main.c @@ -11,6 +11,7 @@ #include "config/session.h" #include "labwc.h" #include "theme.h" +#include "translate.h" #include "menu/menu.h" struct rcxml rc = { 0 }; diff --git a/src/menu/menu.c b/src/menu/menu.c index fca755b2..0e23e850 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -29,6 +29,7 @@ #include "scaled-buffer/scaled-font-buffer.h" #include "scaled-buffer/scaled-icon-buffer.h" #include "theme.h" +#include "translate.h" #include "view.h" #include "workspaces.h" From c9030dcc5b321abb2afb3a38d00b795081c8fe4d Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Fri, 26 Sep 2025 17:52:13 +0100 Subject: [PATCH 190/248] CI: fix broken FreeBSD CI by setting -Dlibsfdo:b_ndebug=false MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ...because with with meson setup build -Dbuildtype=release -Db_ndebug=true \ --werror --force-fallback-for=libsfdo we get the following warning: In file included from ../subprojects/libsfdo/common/dirs.c:5: ../subprojects/libsfdo/include/common/membuild.h: In function ‘sfdo_membuild_validate’: ../subprojects/libsfdo/include/common/membuild.h:29:65: error: unused parameter ‘membuild’ [-Werror=unused-parameter] 29 | static inline void sfdo_membuild_validate(struct sfdo_membuild *membuild) { ...because `sfdo_membuild_validate()` contains nothing but an `assert()` and that therefore results in an `unused-parameter` warning with `NDEBUG`. https://gitlab.freedesktop.org/vyivel/libsfdo/-/blob/main/include/common/membuild.h?ref_type=heads#L30 --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b0ff3053..2b76b4d2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -168,6 +168,7 @@ jobs: meson setup build-gcc-release -Dxwayland=enabled \ -Dbuildtype=release -Db_ndebug=true --werror meson configure build-gcc-release -Dwlroots:b_ndebug=false || true + meson configure build-gcc-release -Dlibsfdo:b_ndebug=false || true meson compile -C build-gcc-release ' | $TARGET @@ -190,6 +191,7 @@ jobs: meson setup build-clang-release -Dxwayland=enabled \ -Dbuildtype=release -Db_ndebug=true --werror meson configure build-clang-release -Dwlroots:b_ndebug=false || true + meson configure build-clang-release -Dlibsfdo:b_ndebug=false || true meson compile -C build-clang-release ' | $TARGET From 94c980c6be2195281e7bde30f58e0be40cfd2fe5 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Fri, 26 Sep 2025 20:42:31 +0100 Subject: [PATCH 191/248] action: fix UAF when using prompt during reconfigure Reported-by: @jlindgren90 Fixes #3106 --- include/action.h | 1 + src/action.c | 9 +++++++++ src/server.c | 3 +++ 3 files changed, 13 insertions(+) diff --git a/include/action.h b/include/action.h index 31ecfb6e..7692e383 100644 --- a/include/action.h +++ b/include/action.h @@ -50,6 +50,7 @@ bool actions_contain_toggle_keybinds(struct wl_list *action_list); void actions_run(struct view *activator, struct server *server, struct wl_list *actions, struct cursor_context *ctx); +void action_prompts_destroy(void); bool action_check_prompt_result(pid_t pid, int exit_code); void action_free(struct action *action); diff --git a/src/action.c b/src/action.c index 62ab2b2d..9a5623ff 100644 --- a/src/action.c +++ b/src/action.c @@ -867,6 +867,15 @@ cleanup: buf_reset(&command); } +void +action_prompts_destroy(void) +{ + struct action_prompt *prompt, *tmp; + wl_list_for_each_safe(prompt, tmp, &prompts, link) { + action_prompt_destroy(prompt); + } +} + bool action_check_prompt_result(pid_t pid, int exit_code) { diff --git a/src/server.c b/src/server.c index 56f091b9..c39649b8 100644 --- a/src/server.c +++ b/src/server.c @@ -78,6 +78,9 @@ static void reload_config_and_theme(struct server *server) { + /* Avoid UAF when dialog client is used during reconfigure */ + action_prompts_destroy(); + scaled_buffer_invalidate_sharing(); rcxml_finish(); rcxml_read(rc.config_file); From 1043a9becc9858c229cce6130bee29942d8202ac Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 29 Sep 2025 16:03:36 +0900 Subject: [PATCH 192/248] test: fix build error in t/xml.c 38a1a9b broke `t/xml.c` due to `macros.h` requiring `wlr/version.h`. This commit fixes it by adding `wlroots` as a direct dependency of the test executables. --- t/meson.build | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/t/meson.build b/t/meson.build index 6aafc1e9..07d55a1f 100644 --- a/t/meson.build +++ b/t/meson.build @@ -1,3 +1,10 @@ +test_deps = [ + dep_cmocka, + glib, + xml2, + wlroots, +] + test_lib = static_library( 'test_lib', sources: files( @@ -8,12 +15,7 @@ test_lib = static_library( '../src/common/parse-bool.c', ), include_directories: [labwc_inc], - dependencies: [ - dep_cmocka, - glib, - xml2, - wlroots, - ], + dependencies: test_deps, ) tests = [ @@ -30,7 +32,7 @@ foreach t : tests sources: '@0@.c'.format(t), include_directories: [labwc_inc], link_with: [test_lib], - dependencies: [xml2], + dependencies: test_deps, ), is_parallel: false, ) From 7e27f78662045c7b1fdb3b357b1e5d5c78a3a486 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 29 Sep 2025 16:04:37 +0900 Subject: [PATCH 193/248] test/xml: use xmlBufferContent() xmlBuffer->content has been deprecated. --- t/xml.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/xml.c b/t/xml.c index 003632a6..a7be6b87 100644 --- a/t/xml.c +++ b/t/xml.c @@ -111,7 +111,7 @@ test_lab_xml_expand_dotted_attributes(void **state) xmlBuffer *buf = xmlBufferCreate(); xmlNodeDump(buf, root->doc, root, 0, 0); - assert_string_equal(test_cases[i].after, (char *)buf->content); + assert_string_equal(test_cases[i].after, (char *)xmlBufferContent(buf)); xmlBufferFree(buf); xmlFreeDoc(doc); From 46bd1fef95eccf372ad5ea26105f8b1dfba1f1d9 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 29 Sep 2025 16:13:37 +0900 Subject: [PATCH 194/248] CI: add unit tests --- .github/workflows/build.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2b76b4d2..0cf69f85 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -82,7 +82,7 @@ jobs: pacman -Syu --noconfirm pacman -S --noconfirm git meson clang wlroots0.19 libdrm libinput \ wayland-protocols cairo pango libxml2 xorg-xwayland librsvg \ - libdisplay-info gdb ttf-dejavu foot libsfdo + libdisplay-info gdb ttf-dejavu foot libsfdo cmocka - name: Install Debian Testing dependencies if: matrix.name == 'Debian' @@ -207,6 +207,18 @@ jobs: meson compile -C build-gcc-no-feature ' | $TARGET + # Unit tests, run on Arch only + - name: Build with gcc - unit test + if: matrix.name == 'Arch' + run: | + echo ' + cd "$GITHUB_WORKSPACE" + export CC=gcc + meson setup build-gcc-unit-test -Dtest=enabled --werror + meson compile -C build-gcc-unit-test + meson test -C build-gcc-unit-test --print-errorlogs + ' | $TARGET + # Runtime tests, these run on Arch and Void only (the later due to libmusl being used) - name: Build with gcc - runtime test if: matrix.name == 'Arch' From c5cd1f691d3390aa2b6091f882651b8f1aff47dd Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 29 Sep 2025 17:07:43 +0900 Subject: [PATCH 195/248] CI: use libwlroots-0.19-dev build-dep for Debian --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0cf69f85..e2876ac9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -92,7 +92,7 @@ jobs: apt-get upgrade -y apt-get install -y git gcc clang gdb xwayland apt-get build-dep -y labwc - apt-get build-dep -y libwlroots-0.18-dev + apt-get build-dep -y libwlroots-0.19-dev - name: Install FreeBSD dependencies if: matrix.name == 'FreeBSD' From 27f3097f8f882abc710e94bcb9a177319140d0b9 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Fri, 26 Sep 2025 17:47:47 +0100 Subject: [PATCH 196/248] build: add 'sections' option to show unused functions --- meson.build | 10 ++++++++++ meson_options.txt | 1 + 2 files changed, 11 insertions(+) diff --git a/meson.build b/meson.build index 5f4cfe9b..0ac5cea1 100644 --- a/meson.build +++ b/meson.build @@ -134,6 +134,15 @@ if get_option('static_analyzer').enabled() add_project_arguments(['-fanalyzer'], language: 'c') endif +link_args = [] +if get_option('sections').enabled() + add_project_arguments(['-ffunction-sections'], language: 'c') + link_args += [ + '-Wl,--gc-sections', + '-Wl,--print-gc-sections', + ] +endif + msgfmt = find_program('msgfmt', required: get_option('nls')) if msgfmt.found() source_root = meson.current_source_dir() @@ -193,6 +202,7 @@ executable( include_directories: [labwc_inc], dependencies: labwc_deps, install: true, + link_args: link_args, ) install_data('data/labwc.desktop', install_dir: get_option('datadir') / 'wayland-sessions') diff --git a/meson_options.txt b/meson_options.txt index ec3fe85d..a3da65a8 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -5,3 +5,4 @@ option('icon', type: 'feature', value: 'enabled', description: 'Enable window ic option('nls', type: 'feature', value: 'auto', description: 'Enable native language support') option('static_analyzer', type: 'feature', value: 'disabled', description: 'Run gcc static analyzer') option('test', type: 'feature', value: 'disabled', description: 'Run tests') +option('sections', type: 'feature', value: 'disabled', description: 'Show unused functions') From 040e25f38e1865feb400cea6576b28d274e09ba8 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Fri, 26 Sep 2025 17:46:20 +0100 Subject: [PATCH 197/248] Privatize private view_get_root() --- include/view.h | 1 - src/view.c | 20 ++++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/include/view.h b/include/view.h index efdcf3fd..45bf42d8 100644 --- a/include/view.h +++ b/include/view.h @@ -554,7 +554,6 @@ void view_move_to_output(struct view *view, struct output *output); void view_move_to_front(struct view *view); void view_move_to_back(struct view *view); -struct view *view_get_root(struct view *view); void view_append_children(struct view *view, struct wl_array *children); /** diff --git a/src/view.c b/src/view.c index e402b626..aa15ef92 100644 --- a/src/view.c +++ b/src/view.c @@ -240,6 +240,16 @@ view_matches_query(struct view *view, struct view_query *query) return true; } +static struct view * +view_get_root(struct view *view) +{ + assert(view); + if (view->impl->get_root) { + return view->impl->get_root(view); + } + return view; +} + static bool matches_criteria(struct view *view, enum lab_view_criteria criteria) { @@ -2327,16 +2337,6 @@ view_move_to_back(struct view *view) desktop_update_top_layer_visibility(view->server); } -struct view * -view_get_root(struct view *view) -{ - assert(view); - if (view->impl->get_root) { - return view->impl->get_root(view); - } - return view; -} - void view_append_children(struct view *view, struct wl_array *children) { From 286005e121c5409f72ea9c60ceb3f1aa12d61625 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Fri, 26 Sep 2025 19:14:30 +0100 Subject: [PATCH 198/248] Privatize rcxml_parse_xml() --- include/config/rcxml.h | 1 - src/config/rcxml.c | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/include/config/rcxml.h b/include/config/rcxml.h index b1c28d12..e4fd184b 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -200,7 +200,6 @@ struct rcxml { extern struct rcxml rc; -void rcxml_parse_xml(struct buf *b); void rcxml_read(const char *filename); void rcxml_finish(void); diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 89293c43..9bf38941 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -1323,8 +1323,7 @@ traverse(xmlNode *node) } } -/* Exposed in header file to allow unit tests to parse buffers */ -void +static void rcxml_parse_xml(struct buf *b) { int options = 0; From 950c634cea8251b4f4d6649281ee0481a3eece48 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Fri, 26 Sep 2025 19:15:29 +0100 Subject: [PATCH 199/248] Privatize xwayland_surface_from_view() --- include/xwayland.h | 2 -- src/xwayland.c | 30 +++++++++++++++--------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/include/xwayland.h b/include/xwayland.h index b5bd2623..5fa20e11 100644 --- a/include/xwayland.h +++ b/include/xwayland.h @@ -65,8 +65,6 @@ void xwayland_unmanaged_create(struct server *server, void xwayland_view_create(struct server *server, struct wlr_xwayland_surface *xsurface, bool mapped); -struct wlr_xwayland_surface *xwayland_surface_from_view(struct view *view); - void xwayland_server_init(struct server *server, struct wlr_compositor *compositor); void xwayland_server_finish(struct server *server); diff --git a/src/xwayland.c b/src/xwayland.c index 0604dcaf..0ab92f0d 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -40,6 +40,21 @@ static xcb_atom_t atoms[ATOM_COUNT] = {0}; static void xwayland_view_unmap(struct view *view, bool client_request); +static struct xwayland_view * +xwayland_view_from_view(struct view *view) +{ + assert(view->type == LAB_XWAYLAND_VIEW); + return (struct xwayland_view *)view; +} + +static struct wlr_xwayland_surface * +xwayland_surface_from_view(struct view *view) +{ + struct xwayland_view *xwayland_view = xwayland_view_from_view(view); + assert(xwayland_view->xwayland_surface); + return xwayland_view->xwayland_surface; +} + static bool xwayland_view_contains_window_type(struct view *view, enum lab_window_type window_type) @@ -186,21 +201,6 @@ top_parent_of(struct view *view) return s; } -static struct xwayland_view * -xwayland_view_from_view(struct view *view) -{ - assert(view->type == LAB_XWAYLAND_VIEW); - return (struct xwayland_view *)view; -} - -struct wlr_xwayland_surface * -xwayland_surface_from_view(struct view *view) -{ - struct xwayland_view *xwayland_view = xwayland_view_from_view(view); - assert(xwayland_view->xwayland_surface); - return xwayland_view->xwayland_surface; -} - static void ensure_initial_geometry_and_output(struct view *view) { From 2ea0f6fff4d02891c0081d95f8d9ce28afcc775b Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Fri, 26 Sep 2025 19:16:01 +0100 Subject: [PATCH 200/248] Privatize output_manager_init() --- include/output.h | 1 - src/output.c | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/output.h b/include/output.h index ceccaf9f..888f62c7 100644 --- a/include/output.h +++ b/include/output.h @@ -40,7 +40,6 @@ struct output { void output_init(struct server *server); void output_finish(struct server *server); -void output_manager_init(struct server *server); struct output *output_from_wlr_output(struct server *server, struct wlr_output *wlr_output); struct output *output_from_name(struct server *server, const char *name); diff --git a/src/output.c b/src/output.c index 8345c653..adee3f9d 100644 --- a/src/output.c +++ b/src/output.c @@ -551,6 +551,8 @@ handle_new_output(struct wl_listener *listener, void *data) do_output_layout_change(server); } +static void output_manager_init(struct server *server); + void output_init(struct server *server) { @@ -890,7 +892,7 @@ handle_gamma_control_set_gamma(struct wl_listener *listener, void *data) wlr_output_schedule_frame(output->wlr_output); } -void +static void output_manager_init(struct server *server) { server->output_manager = wlr_output_manager_v1_create(server->wl_display); From eb41c6a3b02f064a8bee539de5fbf4a717200180 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Fri, 26 Sep 2025 19:17:27 +0100 Subject: [PATCH 201/248] Privatize view_contains_window_type() --- include/view.h | 1 - src/view.c | 20 ++++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/include/view.h b/include/view.h index 45bf42d8..0970bb02 100644 --- a/include/view.h +++ b/include/view.h @@ -425,7 +425,6 @@ void view_array_append(struct server *server, struct wl_array *views, enum lab_view_criteria criteria); enum view_wants_focus view_wants_focus(struct view *view); -bool view_contains_window_type(struct view *view, enum lab_window_type window_type); /* If view is NULL, the size of SSD is not considered */ struct wlr_box view_get_edge_snap_box(struct view *view, struct output *output, diff --git a/src/view.c b/src/view.c index aa15ef92..29736b80 100644 --- a/src/view.c +++ b/src/view.c @@ -120,6 +120,16 @@ query_str_match(const char *condition, const char *value) return value && match_glob(condition, value); } +static bool +view_contains_window_type(struct view *view, enum lab_window_type window_type) +{ + assert(view); + if (view->impl->contains_window_type) { + return view->impl->contains_window_type(view, window_type); + } + return false; +} + bool view_matches_query(struct view *view, struct view_query *query) { @@ -403,16 +413,6 @@ view_wants_focus(struct view *view) return VIEW_WANTS_FOCUS_ALWAYS; } -bool -view_contains_window_type(struct view *view, enum lab_window_type window_type) -{ - assert(view); - if (view->impl->contains_window_type) { - return view->impl->contains_window_type(view, window_type); - } - return false; -} - bool view_is_focusable(struct view *view) { From 9ec49144ac65db8a56e678670e12e31b308df753 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Fri, 26 Sep 2025 19:17:56 +0100 Subject: [PATCH 202/248] Privatize desktop_topmost_focusable_view() --- include/labwc.h | 1 - src/desktop.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index c75b1e39..5a3bbefa 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -359,7 +359,6 @@ void desktop_focus_view_or_surface(struct seat *seat, struct view *view, void desktop_arrange_all_views(struct server *server); void desktop_focus_output(struct output *output); -struct view *desktop_topmost_focusable_view(struct server *server); /** * Toggles the (output local) visibility of the layershell top layer diff --git a/src/desktop.c b/src/desktop.c index dc2aa1a2..bac6c065 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -132,7 +132,7 @@ desktop_focus_view_or_surface(struct seat *seat, struct view *view, } } -struct view * +static struct view * desktop_topmost_focusable_view(struct server *server) { struct view *view; From 60d536304bd72d818b40ac9ecb0a056c25fabaa8 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Fri, 26 Sep 2025 19:21:34 +0100 Subject: [PATCH 203/248] Privatize view_append_children() --- include/view.h | 1 - src/view.c | 18 +++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/include/view.h b/include/view.h index 0970bb02..ba662550 100644 --- a/include/view.h +++ b/include/view.h @@ -553,7 +553,6 @@ void view_move_to_output(struct view *view, struct output *output); void view_move_to_front(struct view *view); void view_move_to_back(struct view *view); -void view_append_children(struct view *view, struct wl_array *children); /** * view_get_modal_dialog() - returns any modal dialog found among this diff --git a/src/view.c b/src/view.c index 29736b80..792401cf 100644 --- a/src/view.c +++ b/src/view.c @@ -808,6 +808,15 @@ _minimize(struct view *view, bool minimized) } } +static void +view_append_children(struct view *view, struct wl_array *children) +{ + assert(view); + if (view->impl->append_children) { + view->impl->append_children(view, children); + } +} + static void minimize_sub_views(struct view *view, bool minimized) { @@ -2337,15 +2346,6 @@ view_move_to_back(struct view *view) desktop_update_top_layer_visibility(view->server); } -void -view_append_children(struct view *view, struct wl_array *children) -{ - assert(view); - if (view->impl->append_children) { - view->impl->append_children(view, children); - } -} - struct view * view_get_modal_dialog(struct view *view) { From c8581b3fed4d3685fe673a891af5a721bcedd1bf Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Mon, 29 Sep 2025 21:30:04 +0100 Subject: [PATCH 204/248] include/common/xml.h: fix declaration/definition arg name difference --- include/common/xml.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/common/xml.h b/include/common/xml.h index 16c319ec..e218226f 100644 --- a/include/common/xml.h +++ b/include/common/xml.h @@ -25,7 +25,7 @@ * * */ -void lab_xml_expand_dotted_attributes(xmlNode *root); +void lab_xml_expand_dotted_attributes(xmlNode *parent); /* Returns true if the node only contains a string or is empty */ bool lab_xml_node_is_leaf(xmlNode *node); From e558d0d619ef4e867475cf1ae86b73fb067b2ac6 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Wed, 1 Oct 2025 15:22:54 +0900 Subject: [PATCH 205/248] labnag: rename options for color --- clients/labnag.c | 64 +++++++++++++++++++++-------------------- docs/labnag.1.scd | 26 ++++++++--------- docs/labwc-config.5.scd | 12 ++++---- src/config/rcxml.c | 12 ++++---- 4 files changed, 58 insertions(+), 56 deletions(-) diff --git a/clients/labnag.c b/clients/labnag.c index bea25dd1..23e0be1f 100644 --- a/clients/labnag.c +++ b/clients/labnag.c @@ -45,7 +45,7 @@ struct conf { uint32_t details_background; uint32_t background; uint32_t text; - uint32_t border; + uint32_t button_border; uint32_t border_bottom; /* Sizing */ @@ -429,7 +429,7 @@ render_button(cairo_t *cairo, struct nag *nag, struct button *button, int *x) button->width = text_width + padding * 2; button->height = text_height + padding * 2; - cairo_set_source_u32(cairo, nag->conf->border); + cairo_set_source_u32(cairo, nag->conf->button_border); cairo_rectangle(cairo, button->x - border, button->y - border, button->width + border * 2, button->height + border * 2); cairo_fill(cairo); @@ -1255,7 +1255,7 @@ conf_init(struct conf *conf) conf->background = 0x323232FF; conf->text = 0xFFFFFFFF; conf->button_text = 0xFFFFFFFF; - conf->border = 0x222222FF; + conf->button_border = 0x222222FF; conf->border_bottom = 0x444444FF; conf->bar_border_thickness = 2; conf->message_padding = 8; @@ -1270,7 +1270,7 @@ conf_init(struct conf *conf) conf->background = 0x900000FF; conf->text = 0xFFFFFFFF; conf->button_text = 0xFFFFFFFF; - conf->border = 0xD92424FF; + conf->button_border = 0xD92424FF; conf->border_bottom = 0x470909FF; } @@ -1342,9 +1342,9 @@ nag_parse_options(int argc, char **argv, struct nag *nag, { enum type_options { TO_COLOR_BACKGROUND = 256, - TO_COLOR_BORDER, + TO_COLOR_BUTTON_BORDER, TO_COLOR_BORDER_BOTTOM, - TO_COLOR_BUTTON, + TO_COLOR_BUTTON_BG, TO_COLOR_DETAILS, TO_COLOR_TEXT, TO_COLOR_BUTTON_TEXT, @@ -1373,16 +1373,16 @@ nag_parse_options(int argc, char **argv, struct nag *nag, {"timeout", required_argument, NULL, 't'}, {"version", no_argument, NULL, 'v'}, - {"background", required_argument, NULL, TO_COLOR_BACKGROUND}, - {"border", required_argument, NULL, TO_COLOR_BORDER}, - {"border-bottom", required_argument, NULL, TO_COLOR_BORDER_BOTTOM}, - {"button-background", required_argument, NULL, TO_COLOR_BUTTON}, - {"text", required_argument, NULL, TO_COLOR_TEXT}, - {"button-text", required_argument, NULL, TO_COLOR_BUTTON_TEXT}, + {"background-color", required_argument, NULL, TO_COLOR_BACKGROUND}, + {"button-border-color", required_argument, NULL, TO_COLOR_BUTTON_BORDER}, + {"border-bottom-color", required_argument, NULL, TO_COLOR_BORDER_BOTTOM}, + {"button-background-color", required_argument, NULL, TO_COLOR_BUTTON_BG}, + {"text-color", required_argument, NULL, TO_COLOR_TEXT}, + {"button-text-color", required_argument, NULL, TO_COLOR_BUTTON_TEXT}, {"border-bottom-size", required_argument, NULL, TO_THICK_BAR_BORDER}, {"message-padding", required_argument, NULL, TO_PADDING_MESSAGE}, {"details-border-size", required_argument, NULL, TO_THICK_DET_BORDER}, - {"details-background", required_argument, NULL, TO_COLOR_DETAILS}, + {"details-background-color", required_argument, NULL, TO_COLOR_DETAILS}, {"button-border-size", required_argument, NULL, TO_THICK_BTN_BORDER}, {"button-gap", required_argument, NULL, TO_GAP_BTN}, {"button-dismiss-gap", required_argument, NULL, TO_GAP_BTN_DISMISS}, @@ -1413,21 +1413,23 @@ nag_parse_options(int argc, char **argv, struct nag *nag, " -v, --version Show the version number and quit.\n" "\n" "The following appearance options can also be given:\n" - " --background RRGGBB[AA] Background color.\n" - " --border RRGGBB[AA] Border color.\n" - " --border-bottom RRGGBB[AA] Bottom border color.\n" - " --button-background RRGGBB[AA] Button background color.\n" - " --text RRGGBB[AA] Text color.\n" - " --button-text RRGGBB[AA] Button text color.\n" - " --border-bottom-size size Thickness of the bar border.\n" - " --message-padding padding Padding for the message.\n" - " --details-border-size size Thickness for the details border.\n" - " --details-background RRGGBB[AA] Details background color.\n" - " --button-border-size size Thickness for the button border.\n" - " --button-gap gap Size of the gap between buttons\n" - " --button-dismiss-gap gap Size of the gap for dismiss button.\n" - " --button-margin-right margin Margin from dismiss button to edge.\n" - " --button-padding padding Padding for the button text.\n"; + " --background-color RRGGBB[AA] Background color.\n" + " --button-border-color RRGGBB[AA] Button border color.\n" + " --border-bottom-color RRGGBB[AA] Bottom border color.\n" + " --button-background-color RRGGBB[AA]\n" + " Button background color.\n" + " --text-color RRGGBB[AA] Text color.\n" + " --button-text-color RRGGBB[AA] Button text color.\n" + " --border-bottom-size size Thickness of the bar border.\n" + " --message-padding padding Padding for the message.\n" + " --details-border-size size Thickness for the details border.\n" + " --details-background-color RRGGBB[AA]\n" + " Details background color.\n" + " --button-border-size size Thickness for the button border.\n" + " --button-gap gap Size of the gap between buttons\n" + " --button-dismiss-gap gap Size of the gap for dismiss button.\n" + " --button-margin-right margin Margin from dismiss button to edge.\n" + " --button-padding padding Padding for the button text.\n"; optind = 1; while (1) { @@ -1522,8 +1524,8 @@ nag_parse_options(int argc, char **argv, struct nag *nag, fprintf(stderr, "Invalid background color: %s\n", optarg); } break; - case TO_COLOR_BORDER: /* Border color */ - if (!parse_color(optarg, &conf->border)) { + case TO_COLOR_BUTTON_BORDER: /* Border color */ + if (!parse_color(optarg, &conf->button_border)) { fprintf(stderr, "Invalid border color: %s\n", optarg); } break; @@ -1532,7 +1534,7 @@ nag_parse_options(int argc, char **argv, struct nag *nag, fprintf(stderr, "Invalid border bottom color: %s\n", optarg); } break; - case TO_COLOR_BUTTON: /* Button background color */ + case TO_COLOR_BUTTON_BG: /* Button background color */ if (!parse_color(optarg, &conf->button_background)) { fprintf(stderr, "Invalid button background color: %s\n", optarg); } diff --git a/docs/labnag.1.scd b/docs/labnag.1.scd index 8ff5fe18..c8aa4d09 100644 --- a/docs/labnag.1.scd +++ b/docs/labnag.1.scd @@ -62,22 +62,22 @@ _labnag_ [options...] # APPEARANCE OPTIONS -*--background* +*--background-color* Set the color of the background. -*--border* - Set the color of the border. +*--button-border-color* + Set the color of the button border. -*--border-bottom* +*--border-bottom-color* Set the color of the bottom border. -*--button-background* +*--button-background-color* Set the color for the background for buttons. -*--text* +*--text-color* Set the text color. -*--button-text* +*--button-text-color* Set the button text color. *--border-bottom-size* @@ -86,7 +86,7 @@ _labnag_ [options...] *--message-padding* Set the padding for the message. -*--details-background* +*--details-background-color* Set the color for the background for details. *--details-border-size* @@ -126,11 +126,11 @@ labnag \\ -Z "Hibernate" "systemctl hibernate"\\ -Z " Suspend " "systemctl suspend"\\ -Z " Cancel "\\ - --background 00ffff\\ - --button-background 00ffff\\ - --border 00ccccaa\\ - --text 000000\\ - --button-text 000000\\ + --background-color 00ffff\\ + --button-background-color 00ffff\\ + --button-border-color 00ccccaa\\ + --text-color 000000\\ + --button-text-color 000000\\ --button-gap 8\\ --button-margin-right 0\\ --button-padding 5\\ diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index 52cd9638..d389c4cf 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -283,12 +283,12 @@ this is for compatibility with Openbox. --message '%m' \\ --button-dismiss '%n' \\ --button-dismiss '%y' \\ - --background '%b' \\ - --text '%t' \\ - --border '%t' \\ - --border-bottom '%t' \\ - --button-background '%b' \\ - --button-text '%t' \\ + --background-color '%b' \\ + --text-color '%t' \\ + --button-border-color '%t' \\ + --border-bottom-color '%t' \\ + --button-background-color '%b' \\ + --button-text-color '%t' \\ --border-bottom-size 1 \\ --button-border-size 3 \\ --timeout 0 diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 9bf38941..1353b3c2 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -1634,12 +1634,12 @@ post_processing(void) "--message '%m' " "--button-dismiss '%n' " "--button-dismiss '%y' " - "--background '%b' " - "--text '%t' " - "--border '%t' " - "--border-bottom '%t' " - "--button-background '%b' " - "--button-text '%t' " + "--background-color '%b' " + "--text-color '%t' " + "--button-border-color '%t' " + "--border-bottom-color '%t' " + "--button-background-color '%b' " + "--button-text-color '%t' " "--border-bottom-size 1 " "--button-border-size 3 " "--timeout 0"); From 389cef9c3b8a97e099c09ad62bea06299fb3020a Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Wed, 1 Oct 2025 20:47:33 +0100 Subject: [PATCH 206/248] include/common/box.h: fix declaration/definition arg name difference --- include/common/box.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/common/box.h b/include/common/box.h index 3d900a0e..45c0fc4d 100644 --- a/include/common/box.h +++ b/include/common/box.h @@ -18,7 +18,7 @@ void box_union(struct wlr_box *box_dest, struct wlr_box *box_a, * The returned x & y coordinates are the centered content position * relative to the top-left corner of the bounding box. */ -struct wlr_box box_fit_within(int width, int height, struct wlr_box *bounding_box); +struct wlr_box box_fit_within(int width, int height, struct wlr_box *bound); struct wlr_fbox box_to_fbox(struct wlr_box *box); From 4c1e66f6c8d9fd19684e0d5d899526f8c4c7525f Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Wed, 1 Oct 2025 20:48:15 +0100 Subject: [PATCH 207/248] include/common/buf.h: fix declaration/definition arg name difference --- include/common/buf.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/common/buf.h b/include/common/buf.h index 857b6c48..24158630 100644 --- a/include/common/buf.h +++ b/include/common/buf.h @@ -71,9 +71,9 @@ void buf_add(struct buf *s, const char *data); /** * buf_add_char - add single char to C string buffer * @s: buffer - * @data: char to be added + * @ch: char to be added */ -void buf_add_char(struct buf *s, char data); +void buf_add_char(struct buf *s, char ch); /** * buf_clear - clear the buffer, internal allocations are preserved From 0d0d1075ccb6c714a0c829354558bad839510e6a Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Wed, 1 Oct 2025 20:48:45 +0100 Subject: [PATCH 208/248] include/common/scene-helpers: fix declaration/definition arg name difference --- include/common/scene-helpers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/common/scene-helpers.h b/include/common/scene-helpers.h index 021d3b16..453052fb 100644 --- a/include/common/scene-helpers.h +++ b/include/common/scene-helpers.h @@ -20,6 +20,6 @@ struct wlr_scene_node *lab_wlr_scene_get_prev_node(struct wlr_scene_node *node); /* A variant of wlr_scene_output_commit() that respects wlr_output->pending */ bool lab_wlr_scene_output_commit(struct wlr_scene_output *scene_output, - struct wlr_output_state *output_state); + struct wlr_output_state *state); #endif /* LABWC_SCENE_HELPERS_H */ From bed0be8a88aa5c8a05618c97ab8385aa260ad591 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Wed, 1 Oct 2025 20:50:31 +0100 Subject: [PATCH 209/248] src/common/graphic-helpers.c: fix declaration/definition arg name difference --- src/common/graphic-helpers.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/common/graphic-helpers.c b/src/common/graphic-helpers.c index bc0adbc4..4371c9e5 100644 --- a/src/common/graphic-helpers.c +++ b/src/common/graphic-helpers.c @@ -27,34 +27,34 @@ draw_cairo_border(cairo_t *cairo, struct wlr_fbox fbox, double line_width) /* Sets the cairo color. Splits the single color channels */ void -set_cairo_color(cairo_t *cairo, const float *c) +set_cairo_color(cairo_t *cairo, const float *color) { /* * We are dealing with pre-multiplied colors * but cairo expects unmultiplied colors here */ - float alpha = c[3]; + float alpha = color[3]; if (alpha == 0.0f) { cairo_set_source_rgba(cairo, 0, 0, 0, 0); return; } - cairo_set_source_rgba(cairo, c[0] / alpha, c[1] / alpha, - c[2] / alpha, alpha); + cairo_set_source_rgba(cairo, color[0] / alpha, color[1] / alpha, + color[2] / alpha, alpha); } cairo_pattern_t * -color_to_pattern(const float *c) +color_to_pattern(const float *color) { - float alpha = c[3]; + float alpha = color[3]; if (alpha == 0.0f) { return cairo_pattern_create_rgba(0, 0, 0, 0); } - return cairo_pattern_create_rgba( - c[0] / alpha, c[1] / alpha, c[2] / alpha, alpha); + return cairo_pattern_create_rgba(color[0] / alpha, color[1] / alpha, + color[2] / alpha, alpha); } /* From 998ff9e7b5f0433d140220aee64e9629b1a88d76 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Thu, 2 Oct 2025 04:06:48 +0900 Subject: [PATCH 210/248] osd-thumbnail: update default colors of selected window item Previously, the default values of `osd.window-switcher.style-thumbnail.item.active.{bg,border}.color` were blue. But they caused the selected window title in the window switcher to be unreadable due to duplicated colors of the text and background with Openbox themes like Numix. Instead, this commit updates them to follow other themes configurations. The default border color of the selected window item is now `osd.label.text.color` with 50% opacity and the background is `osd.label.text.color` with 15% opacity. For subpixel antialiasing to work, the background color is calculated by manually blending `osd.label.text.color` and `osd.bg.color`, rather than just updating the alpha with 50% or 15%. --- docs/labwc-theme.5.scd | 6 ++++-- docs/themerc | 4 ++-- src/theme.c | 37 +++++++++++++++++++++++++++++++++++-- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/docs/labwc-theme.5.scd b/docs/labwc-theme.5.scd index efc3a1a5..fad8a77e 100644 --- a/docs/labwc-theme.5.scd +++ b/docs/labwc-theme.5.scd @@ -358,10 +358,12 @@ all are supported. Border width of selected window switcher items in pixels. Default is 2. *osd.window-switcher.style-thumbnail.item.active.border.color* - Color of border around selected window switcher items. Default is #589bda. + Color of border around selected window switcher items. + Default is *osd.label.text.color* with 50% opacity. *osd.window-switcher.style-thumbnail.item.active.bg.color* - Color of selected window switcher items. Default is #c7e2fc. + Color of selected window switcher items. + Default is *osd.label.text.color* with 15% opacity. *osd.window-switcher.style-thumbnail.item.icon.size* Size of window icons in window switcher items in pixels. Default is 60. diff --git a/docs/themerc b/docs/themerc index 7b251a3c..a52dfacd 100644 --- a/docs/themerc +++ b/docs/themerc @@ -106,8 +106,8 @@ osd.window-switcher.style-thumbnail.item.width: 300 osd.window-switcher.style-thumbnail.item.height: 250 osd.window-switcher.style-thumbnail.item.padding: 10 osd.window-switcher.style-thumbnail.item.active.border.width: 2 -osd.window-switcher.style-thumbnail.item.active.border.color: #589bda -osd.window-switcher.style-thumbnail.item.active.bg.color: #c7e2fc +osd.window-switcher.style-thumbnail.item.active.border.color: #706f6d +osd.window-switcher.style-thumbnail.item.active.bg.color: #bfbcba osd.window-switcher.style-thumbnail.item.icon.size: 60 osd.window-switcher.preview.border.width: 1 diff --git a/src/theme.c b/src/theme.c index d5279367..a3aec34a 100644 --- a/src/theme.c +++ b/src/theme.c @@ -613,8 +613,8 @@ theme_builtin(struct theme *theme, struct server *server) theme->osd_window_switcher_thumbnail.item_height = 250; theme->osd_window_switcher_thumbnail.item_padding = 10; theme->osd_window_switcher_thumbnail.item_active_border_width = 2; - parse_color("#589bda", theme->osd_window_switcher_thumbnail.item_active_border_color); - parse_color("#c7e2fc", theme->osd_window_switcher_thumbnail.item_active_bg_color); + theme->osd_window_switcher_thumbnail.item_active_border_color[0] = FLT_MIN; + theme->osd_window_switcher_thumbnail.item_active_bg_color[0] = FLT_MIN; theme->osd_window_switcher_thumbnail.item_icon_size = 60; /* inherit settings in post_processing() if not set elsewhere */ @@ -1633,6 +1633,31 @@ get_titlebar_height(struct theme *theme) return h; } +/* Blend foreground color (with new alpha) with background color */ +static void +blend_color_with_bg(float *dst, float *fg, float fg_a, float *bg) +{ + /* Guard against zero division */ + if (fg[3] <= 0.0f) { + memset(dst, 0, sizeof(float) * 4); + return; + } + + /* Redo premultiplication to update foreground alpha */ + float new_fg[4] = { + fg[0] / fg[3] * fg_a, + fg[1] / fg[3] * fg_a, + fg[2] / fg[3] * fg_a, + fg_a, + }; + + /* Blend colors */ + dst[0] = new_fg[0] + bg[0] * (1.0f - new_fg[3]); + dst[1] = new_fg[1] + bg[1] * (1.0f - new_fg[3]); + dst[2] = new_fg[2] + bg[2] * (1.0f - new_fg[3]); + dst[3] = new_fg[3] + bg[3] * (1.0f - new_fg[3]); +} + static void post_processing(struct theme *theme) { @@ -1721,6 +1746,14 @@ post_processing(struct theme *theme) memcpy(theme->osd_border_color, theme->osd_label_text_color, sizeof(theme->osd_border_color)); } + if (switcher_thumb_theme->item_active_border_color[0] == FLT_MIN) { + blend_color_with_bg(switcher_thumb_theme->item_active_border_color, + theme->osd_label_text_color, 0.50, theme->osd_bg_color); + } + if (switcher_thumb_theme->item_active_bg_color[0] == FLT_MIN) { + blend_color_with_bg(switcher_thumb_theme->item_active_bg_color, + theme->osd_label_text_color, 0.15, theme->osd_bg_color); + } if (theme->osd_workspace_switcher_boxes_width == 0) { theme->osd_workspace_switcher_boxes_height = 0; } From cb0a4b875efe49bea8613f22601bb056e3a025e2 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Wed, 8 Oct 2025 18:50:18 +0200 Subject: [PATCH 211/248] desktop-entry.c: on detecting a broken icon theme, fall back to hicolor Fixes: #3126 Reported-By: Kreevoz --- src/desktop-entry.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/desktop-entry.c b/src/desktop-entry.c index 7aefa77e..4d61a9dc 100644 --- a/src/desktop-entry.c +++ b/src/desktop-entry.c @@ -108,6 +108,18 @@ desktop_entry_init(struct server *server) sfdo->icon_theme = sfdo_icon_theme_load( sfdo->icon_ctx, rc.icon_theme_name, load_options); + if (!sfdo->icon_theme) { + /* + * sfdo_icon_theme_load() falls back to hicolor theme with + * _ALLOW_MISSING flag when the theme is missing, but just + * fails when the theme is invalid. + * So manually call sfdo_icon_theme_load() again here. + */ + wlr_log(WLR_ERROR, "Failed to load icon theme %s, falling back to 'hicolor'", + rc.icon_theme_name); + sfdo->icon_theme = sfdo_icon_theme_load( + sfdo->icon_ctx, "hicolor", load_options); + } if (!sfdo->icon_theme) { goto err_icon_theme; } From 814af0ae4db881bba934e0076144570057242770 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Wed, 8 Oct 2025 19:47:57 +0200 Subject: [PATCH 212/248] desktop-entry.c: don't demote error messages with LABWC_DEBUG_LIBSFDO Also add additional logging to tell users how to get more information about failures to load the icon theme. --- src/desktop-entry.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/desktop-entry.c b/src/desktop-entry.c index 4d61a9dc..79b015f2 100644 --- a/src/desktop-entry.c +++ b/src/desktop-entry.c @@ -37,9 +37,10 @@ log_handler(enum sfdo_log_level level, const char *fmt, va_list args, void *tag) /* * To avoid logging issues with .desktop files as errors, all libsfdo - * error-logging is demoted to info level. + * error-logging is demoted to info level unless running with + * LABWC_DEBUG_LIBSFDO. */ - if (level == SFDO_LOG_LEVEL_ERROR) { + if (!debug_libsfdo && level == SFDO_LOG_LEVEL_ERROR) { level = SFDO_LOG_LEVEL_INFO; } @@ -117,6 +118,12 @@ desktop_entry_init(struct server *server) */ wlr_log(WLR_ERROR, "Failed to load icon theme %s, falling back to 'hicolor'", rc.icon_theme_name); + + if (!debug_libsfdo) { + wlr_log(WLR_ERROR, "Further information is available by setting " + "the LABWC_DEBUG_LIBSFDO=1 env var before starting labwc"); + } + sfdo->icon_theme = sfdo_icon_theme_load( sfdo->icon_ctx, "hicolor", load_options); } @@ -141,6 +148,10 @@ err_desktop_ctx: err_basedir_ctx: free(sfdo); wlr_log(WLR_ERROR, "Failed to initialize icon loader"); + if (!debug_libsfdo) { + wlr_log(WLR_ERROR, "Further information is available by setting " + "the LABWC_DEBUG_LIBSFDO=1 env var before starting labwc"); + } } void From 017152da52d500cec0f60f51870c29422844e8ca Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Wed, 8 Oct 2025 20:57:45 +0100 Subject: [PATCH 213/248] build: use spaces instead of tab --- clients/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clients/meson.build b/clients/meson.build index c131915d..54b92db8 100644 --- a/clients/meson.build +++ b/clients/meson.build @@ -51,7 +51,7 @@ executable( epoll_dep, ], include_directories: [labwc_inc], - install: true + install: true, ) clients = files('lab-sensible-terminal') From 7166efe7bf3fe239b73b394759f59835653d6d32 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Wed, 8 Oct 2025 22:10:24 +0200 Subject: [PATCH 214/248] CI: also run on clients/ changes --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e2876ac9..a9ccbb06 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,6 +17,7 @@ on: - 'src/**' - 'include/**' - 'protocols/**' + - 'clients/**' - 'scripts/**' - '.github/workflows/**' From c27d4955a42184c2bd593cd4c2e1725a2d7f3cf6 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Wed, 8 Oct 2025 18:32:30 +0900 Subject: [PATCH 215/248] desktop-entry: fix wrong description of sfdo-icon flags --- src/desktop-entry.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/desktop-entry.c b/src/desktop-entry.c index 79b015f2..7617fa26 100644 --- a/src/desktop-entry.c +++ b/src/desktop-entry.c @@ -96,15 +96,15 @@ desktop_entry_init(struct server *server) * We set some relaxed load options to accommodate delinquent themes in * the wild, namely: * - * - SFDO_ICON_THEME_LOAD_OPTION_ALLOW_MISSING to "impose less - * restrictions on the format of icon theme files" + * - SFDO_ICON_THEME_LOAD_OPTION_RELAXED to "impose less restrictions + * on the format of icon theme files" * - * - SFDO_ICON_THEME_LOAD_OPTION_RELAXED to "continue loading even if it - * fails to find a theme or one of its dependencies." + * - SFDO_ICON_THEME_LOAD_OPTION_ALLOW_MISSING to "continue loading + * even if it fails to find a theme or one of its dependencies." */ int load_options = SFDO_ICON_THEME_LOAD_OPTIONS_DEFAULT - | SFDO_ICON_THEME_LOAD_OPTION_ALLOW_MISSING - | SFDO_ICON_THEME_LOAD_OPTION_RELAXED; + | SFDO_ICON_THEME_LOAD_OPTION_RELAXED + | SFDO_ICON_THEME_LOAD_OPTION_ALLOW_MISSING; sfdo->icon_theme = sfdo_icon_theme_load( sfdo->icon_ctx, From 474c513ed6565df300b91e44d3ec77f9da5105c5 Mon Sep 17 00:00:00 2001 From: Tobias Bengfort Date: Thu, 9 Oct 2025 09:39:59 +0200 Subject: [PATCH 216/248] fix double free for libxml2 < 2.13 xmlAddChild() only unlinks the second argument since libxml2 2.13. regression from 503af105 --- src/common/xml.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/xml.c b/src/common/xml.c index 7e089267..57c415ec 100644 --- a/src/common/xml.c +++ b/src/common/xml.c @@ -79,7 +79,8 @@ merge_two_trees(xmlNode *dst, xmlNode *src) && !strcasecmp((char *)dst->name, (char *)src->name)) { xmlNode *next_dst = dst->last; xmlNode *next_src = src->children; - xmlAddChild(dst, src->children); + xmlUnlinkNode(next_src); + xmlAddChild(dst, next_src); xmlUnlinkNode(src); xmlFreeNode(src); src = next_src; From e1820adcd3367cc5e5459c6f3fcecb8c62f4f747 Mon Sep 17 00:00:00 2001 From: Tobias Bengfort Date: Thu, 9 Oct 2025 09:45:36 +0200 Subject: [PATCH 217/248] fix typo in comment --- src/common/xml.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/xml.c b/src/common/xml.c index 57c415ec..0185d9a3 100644 --- a/src/common/xml.c +++ b/src/common/xml.c @@ -48,7 +48,7 @@ create_attribute_tree(const xmlAttr *attr) } /* - * Consider . + * Consider . * These three attributes are represented by following trees. * action(dst)---name * action(src)---position---x From 6cdfe32af0a2e0fac2b2dcc46498d780f143c80f Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Thu, 9 Oct 2025 21:24:44 +0900 Subject: [PATCH 218/248] rcxml: move from to --- docs/labwc-config.5.scd | 11 +++++------ docs/rc.xml.all | 2 +- src/config/rcxml.c | 12 ++++++------ 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index d389c4cf..21ac2629 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -171,7 +171,6 @@ this is for compatibility with Openbox. ``` server - titlebar 0 no no @@ -188,11 +187,6 @@ this is for compatibility with Openbox. that it is not always possible to turn off client side decorations. Default is server. -** [titlebar|none] - Specify how server side decorations are shown for maximized windows. - *titlebar* shows titlebar above a maximized window. *none* shows no server - side decorations around a maximized window. Default is titlebar. - ** The distance in pixels between windows and output edges when using movement actions, for example MoveToEdge. Default is 0. @@ -603,6 +597,11 @@ extending outward from the snapped edge. Even when disabling server side decorations via ToggleDecorations, keep a small border (and resize area) around the window. Default is yes. +** [titlebar|none] + Specify how server side decorations are shown for maximized windows. + *titlebar* shows titlebar above a maximized window. *none* shows no server + side decorations around a maximized window. Default is titlebar. + ** [yes|no] Should drop-shadows be rendered behind windows. Default is no. diff --git a/docs/rc.xml.all b/docs/rc.xml.all index ed80fdf0..44ed984a 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -11,7 +11,6 @@ server - titlebar 0 no no @@ -45,6 +44,7 @@ 8 yes + titlebar no no diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 1353b3c2..4ed6bd6d 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -1083,12 +1083,6 @@ entry(xmlNode *node, char *nodename, char *content) } else { rc.xdg_shell_server_side_deco = true; } - } else if (!strcasecmp(nodename, "maximizedDecoration.core")) { - if (!strcasecmp(content, "titlebar")) { - rc.hide_maximized_window_titlebar = false; - } else if (!strcasecmp(content, "none")) { - rc.hide_maximized_window_titlebar = true; - } } else if (!strcmp(nodename, "gap.core")) { rc.gap = atoi(content); } else if (!strcasecmp(nodename, "adaptiveSync.core")) { @@ -1130,6 +1124,12 @@ entry(xmlNode *node, char *nodename, char *content) rc.corner_radius = atoi(content); } else if (!strcasecmp(nodename, "keepBorder.theme")) { set_bool(content, &rc.ssd_keep_border); + } else if (!strcasecmp(nodename, "maximizedDecoration.theme")) { + if (!strcasecmp(content, "titlebar")) { + rc.hide_maximized_window_titlebar = false; + } else if (!strcasecmp(content, "none")) { + rc.hide_maximized_window_titlebar = true; + } } else if (!strcasecmp(nodename, "dropShadows.theme")) { set_bool(content, &rc.shadows_enabled); } else if (!strcasecmp(nodename, "dropShadowsOnTiled.theme")) { From e44a48953067fdf1cc95eb79750f0f4353ea203f Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Fri, 19 Sep 2025 20:47:56 +0100 Subject: [PATCH 219/248] NEWS.md: update notes for 0.9.2 --- NEWS.md | 47 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/NEWS.md b/NEWS.md index 4216dad6..1ae97c51 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,7 +9,7 @@ The format is based on [Keep a Changelog] | Date | All Changes | wlroots version | lines-of-code | |------------|---------------|-----------------|---------------| -| 2025-09-15 | [unreleased] | 0.19.0 | 28686 | +| 2025-10-10 | [0.9.2] | 0.19.1 | 28818 | | 2025-08-02 | [0.9.1] | 0.19.0 | 28605 | | 2025-07-11 | [0.9.0] | 0.19.0 | 28586 | | 2025-05-02 | [0.8.4] | 0.18.2 | 27679 | @@ -39,6 +39,7 @@ The format is based on [Keep a Changelog] | 2021-03-05 | [0.1.0] | 0.12.0 | 4627 | [unreleased]: NEWS.md#unreleased +[0.9.2]: NEWS.md#092---2025-10-10 [0.9.1]: NEWS.md#091---2025-08-02 [0.9.0]: NEWS.md#090---2025-07-11 [0.8.4]: NEWS.md#084---2025-05-02 @@ -100,23 +101,35 @@ There are some regression warnings worth noting for the switch to wlroots 0.19: around a bug on the wlroots side which is expected to be fixed in wlroots `0.19.1` [#2887] +With wlroots compiled with libwayland (>= 1.24.0), there is an invisible margin +preventing pointer focus on some layer-shell surfaces including those created by +Gtk. In simple words, this is because libwayland now rounds floats a bit +differently [#3099]. There is a pending fix [wlroots-5159]. + [wlroots-4878]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4878 [wlroots-5098]:https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5098 +[wlroots-5159]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5159 [gtk-8792]: https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/8792 ## unreleased [unreleased-commits] +## 0.9.2 - 2025-10-10 + +[0.9.2-commits] + ### Added +- Allow `SnapToEdge` and `ToggleSnapToEdge` to combine two cardinal directions + with the config option `combine="yes|no"`. [#3081] @tokyo4j - Support `Border` context for mousebinds as an alias for `Top`...`BRCorner` to make configuration easier. @tokyo4j [#3047] - Add window-switcher mode with thumbnails. This can be enabled with: ``. @tokyo4j [#2981] - Add `toggle` option to `GoToDesktop` action. This has the effect of going back to the last desktop if already on the target. @RainerKuemmerle [#3024] -- Add `` to allow hiding titlebar +- Add `` to allow hiding titlebar when window is maximized. @CosmicFusion @tokyo4j [#3015] - Use client-send-to-menu as 'Workspace' submenu in built-in client-menu @johanmalm [#2995] @@ -126,6 +139,7 @@ There are some regression warnings worth noting for the switch to wlroots 0.19: [#2994] - Add `labnag` (a dialog client with message and buttons) and associated `` option in 'If' actions. @johanmalm @Consolatis @tokyo4j [#2699] +- Support config option `` @johanmalm [#3097] - Allow snapping to corner edges during interactive move with associated config options ``. @tokyo4j [#2885] - Support new values "up-left", "up-right", "down-left" and "down-right" with @@ -147,6 +161,7 @@ There are some regression warnings worth noting for the switch to wlroots 0.19: ### Fixed +- On detecting broken icon theme, fall back on 'hicolor' @Consolatis [#3126] - Restore initially-maximized window position after unplug/plug @tokyo4j [#3042] - Fix large client-side icon not being loaded when the rendered icon size is larger than icon sizes from the client. @tokyo4j [#3033] @@ -162,6 +177,27 @@ There are some regression warnings worth noting for the switch to wlroots 0.19: ### Changed +- Change default keybind `W-` to combine cardinal directions to support + resizing of windows to fill a quarter of an output. This only affects users + who do not use an `rc.xml` (thereby using default keybinds) or use the + `` option. Previous behavior can be restored by setting + `combine="no"` as shown below. [#3081] @tokyo4j + +``` + + + + + + + + + + + + +``` + - `Focus` and `Raise` on window border press because it is probably what most people expect and it makes the behavior consistent with that of Openbox. @johanmalm [#3039] [#3049] @@ -2337,7 +2373,8 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16 ShowMenu [Keep a Changelog]: https://keepachangelog.com/en/1.0.0/ -[unreleased-commits]: https://github.com/labwc/labwc/compare/0.9.0...HEAD +[unreleased-commits]: https://github.com/labwc/labwc/compare/0.9.2...HEAD +[0.9.2-commits]: https://github.com/labwc/labwc/compare/0.9.1...0.9.2 [0.9.1-commits]: https://github.com/labwc/labwc/compare/0.9.0...0.9.1 [0.9.0-commits]: https://github.com/labwc/labwc/compare/0.8.4...0.9.0 [0.8.4-commits]: https://github.com/labwc/labwc/compare/0.8.3...0.8.4 @@ -2800,3 +2837,7 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16 [#3043]: https://github.com/labwc/labwc/pull/3043 [#3047]: https://github.com/labwc/labwc/pull/3047 [#3049]: https://github.com/labwc/labwc/pull/3049 +[#3081]: https://github.com/labwc/labwc/pull/3081 +[#3097]: https://github.com/labwc/labwc/pull/3097 +[#3099]: https://github.com/labwc/labwc/pull/3099 +[#3126]: https://github.com/labwc/labwc/pull/3126 From d94e5da815564516d40c1f40ad651553547f071e Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Fri, 10 Oct 2025 17:30:55 +0900 Subject: [PATCH 220/248] view: fix unexpected view->tiled with SnapToEdge against centered view In 2ac4811, I was missing that windows can be tiled to "center". As a result, after executing `` against a center-tiled window, `view->tiled` is set to `CENTER|LEFT`. --- src/view.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/view.c b/src/view.c index 792401cf..ddfe6b1e 100644 --- a/src/view.c +++ b/src/view.c @@ -2161,7 +2161,8 @@ view_snap_to_edge(struct view *view, enum lab_edge edge, view_set_shade(view, false); - if (lab_edge_is_cardinal(edge) && view->maximized == VIEW_AXIS_NONE) { + if (lab_edge_is_cardinal(edge) && view->maximized == VIEW_AXIS_NONE + && view->tiled != LAB_EDGE_CENTER) { enum lab_edge invert_edge = lab_edge_invert(edge); /* Represents axis of snapping direction */ enum lab_edge parallel_mask = edge | invert_edge; From 70e5beb5ec4de9d4f01b1528c0016ccc9355730c Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Fri, 10 Oct 2025 19:50:59 +0100 Subject: [PATCH 221/248] build: bump version to 0.9.2 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 0ac5cea1..d3ba14da 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'labwc', 'c', - version: '0.9.0', + version: '0.9.2', license: 'GPL-2.0-only', meson_version: '>=0.59.0', default_options: [ From 2f96664670c7196f4526006102a1cb80b95e441e Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Wed, 1 Oct 2025 21:09:17 +0900 Subject: [PATCH 222/248] build: fix build with libinput as a subproject Unfortunately, has_header_symbol() doesn't work with internal dependencies. Ref: https://github.com/mesonbuild/meson/issues/13553 --- meson.build | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index d3ba14da..335bf629 100644 --- a/meson.build +++ b/meson.build @@ -127,7 +127,9 @@ conf_data.set10('HAVE_RSVG', have_rsvg) conf_data.set10('HAVE_LIBSFDO', have_libsfdo) foreach sym : ['LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY', 'LIBINPUT_CONFIG_3FG_DRAG_ENABLED_3FG'] - conf_data.set10('HAVE_' + sym, cc.has_header_symbol('libinput.h', sym, dependencies: input)) + has_sym = input.type_name() != 'internal' \ + and cc.has_header_symbol('libinput.h', sym, dependencies: input) + conf_data.set10('HAVE_' + sym, has_sym) endforeach if get_option('static_analyzer').enabled() From 5f981226c27d85a408341fb4edca6b5b9790ecf4 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Wed, 1 Oct 2025 11:54:26 +0900 Subject: [PATCH 223/248] action: simplify action_prompt_command() --- include/action-prompt-command.h | 12 ---- src/action-prompt-command.c | 108 -------------------------------- src/action.c | 48 +++++++++++++- src/meson.build | 1 - 4 files changed, 46 insertions(+), 123 deletions(-) delete mode 100644 include/action-prompt-command.h delete mode 100644 src/action-prompt-command.c diff --git a/include/action-prompt-command.h b/include/action-prompt-command.h deleted file mode 100644 index 219896b9..00000000 --- a/include/action-prompt-command.h +++ /dev/null @@ -1,12 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef LABWC_ACTION_PROMPT_COMMAND_H -#define LABWC_ACTION_PROMPT_COMMAND_H - -struct buf; -struct action; -struct theme; - -void action_prompt_command(struct buf *buf, const char *format, - struct action *action, struct theme *theme); - -#endif /* LABWC_ACTION_PROMPT_COMMAND_H */ diff --git a/src/action-prompt-command.c b/src/action-prompt-command.c deleted file mode 100644 index faad41c9..00000000 --- a/src/action-prompt-command.c +++ /dev/null @@ -1,108 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#define _POSIX_C_SOURCE 200809L -#include "action-prompt-command.h" -#include -#include -#include "action.h" -#include "common/buf.h" -#include "theme.h" -#include "translate.h" - -enum { - LAB_PROMPT_NONE = 0, - LAB_PROMPT_MESSAGE, - LAB_PROMPT_NO, - LAB_PROMPT_YES, - LAB_PROMPT_BG_COL, - LAB_PROMPT_TEXT_COL, - - LAB_PROMPT_COUNT -}; - -typedef void field_conversion_type(struct buf *buf, struct action *action, - struct theme *theme); - -struct field_converter { - const char fmt_char; - field_conversion_type *fn; -}; - -/* %m */ -static void -set_message(struct buf *buf, struct action *action, struct theme *theme) -{ - buf_add(buf, action_get_str(action, "message.prompt", "Choose wisely")); -} - -/* %n */ -static void -set_no(struct buf *buf, struct action *action, struct theme *theme) -{ - buf_add(buf, _("No")); -} - -/* %y */ -static void -set_yes(struct buf *buf, struct action *action, struct theme *theme) -{ - buf_add(buf, _("Yes")); -} - -/* %b */ -static void -set_bg_col(struct buf *buf, struct action *action, struct theme *theme) -{ - buf_add_hex_color(buf, theme->osd_bg_color); -} - -/* %t */ -static void -set_text_col(struct buf *buf, struct action *action, struct theme *theme) -{ - buf_add_hex_color(buf, theme->osd_label_text_color); -} - -static const struct field_converter field_converter[LAB_PROMPT_COUNT] = { - [LAB_PROMPT_MESSAGE] = { 'm', set_message }, - [LAB_PROMPT_NO] = { 'n', set_no }, - [LAB_PROMPT_YES] = { 'y', set_yes }, - [LAB_PROMPT_BG_COL] = { 'b', set_bg_col }, - [LAB_PROMPT_TEXT_COL] = { 't', set_text_col }, -}; - -void -action_prompt_command(struct buf *buf, const char *format, - struct action *action, struct theme *theme) -{ - if (!format) { - wlr_log(WLR_ERROR, "missing format"); - return; - } - - for (const char *p = format; *p; p++) { - /* - * If we're not on a conversion specifier (like %m) then just - * keep adding it to the buffer - */ - if (*p != '%') { - buf_add_char(buf, *p); - continue; - } - - /* Process the %* conversion specifier */ - ++p; - - bool found = false; - for (unsigned char i = 0; i < LAB_PROMPT_COUNT; i++) { - if (*p == field_converter[i].fmt_char) { - field_converter[i].fn(buf, action, theme); - found = true; - break; - } - } - if (!found) { - wlr_log(WLR_ERROR, - "invalid prompt command conversion specifier '%c'", *p); - } - } -} diff --git a/src/action.c b/src/action.c index 9a5623ff..a78b01e8 100644 --- a/src/action.c +++ b/src/action.c @@ -10,7 +10,6 @@ #include #include #include "action-prompt-codes.h" -#include "action-prompt-command.h" #include "common/buf.h" #include "common/macros.h" #include "common/list.h" @@ -30,6 +29,7 @@ #include "regions.h" #include "ssd.h" #include "theme.h" +#include "translate.h" #include "view.h" #include "workspaces.h" @@ -831,11 +831,55 @@ handle_view_destroy(struct wl_listener *listener, void *data) prompt->view = NULL; } +static void +print_prompt_command(struct buf *buf, const char *format, + struct action *action, struct theme *theme) +{ + assert(format); + + for (const char *p = format; *p; p++) { + /* + * If we're not on a conversion specifier (like %m) then just + * keep adding it to the buffer + */ + if (*p != '%') { + buf_add_char(buf, *p); + continue; + } + + /* Process the %* conversion specifier */ + ++p; + + switch (*p) { + case 'm': + buf_add(buf, action_get_str(action, + "message.prompt", "Choose wisely")); + break; + case 'n': + buf_add(buf, _("No")); + break; + case 'y': + buf_add(buf, _("Yes")); + break; + case 'b': + buf_add_hex_color(buf, theme->osd_bg_color); + break; + case 't': + buf_add_hex_color(buf, theme->osd_label_text_color); + break; + default: + wlr_log(WLR_ERROR, + "invalid prompt command conversion specifier '%c'", *p); + break; + } + } +} + static void action_prompt_create(struct view *view, struct server *server, struct action *action) { struct buf command = BUF_INIT; - action_prompt_command(&command, rc.prompt_command, action, rc.theme); + print_prompt_command(&command, rc.prompt_command, action, rc.theme); wlr_log(WLR_INFO, "prompt command: '%s'", command.data); diff --git a/src/meson.build b/src/meson.build index dc760f0c..330b5daf 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,6 +1,5 @@ labwc_sources = files( 'action.c', - 'action-prompt-command.c', 'buffer.c', 'debug.c', 'desktop.c', From 5e8df27f84f59b775fbf981f734427061e194e98 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Wed, 1 Oct 2025 14:25:43 +0900 Subject: [PATCH 224/248] osd-classic: add theme options for selected window item This commit adds new theme options: - osd.window-switcher.style-classic.item.active.border.color - osd.window-switcher.style-classic.item.active.bg.color These theme options configures the border/background of selected window item in the `classic` style window switcher. Their default values are identical to `thumbnail` style window switcher, which means the default border color is now `osd.label.text.color` with 50% opacity and the default background color is now `osd.label.text.color` with 15% opacity. --- docs/labwc-theme.5.scd | 8 +++ docs/themerc | 2 + include/theme.h | 2 + src/osd/osd-classic.c | 114 ++++++++++++++++++++++++----------------- src/theme.c | 16 ++++++ 5 files changed, 96 insertions(+), 46 deletions(-) diff --git a/docs/labwc-theme.5.scd b/docs/labwc-theme.5.scd index fad8a77e..8097615b 100644 --- a/docs/labwc-theme.5.scd +++ b/docs/labwc-theme.5.scd @@ -327,6 +327,14 @@ all are supported. Border width of the selection box in the window switcher in pixels. Default is 2. +*osd.window-switcher.style-classic.item.active.border.color* + Border color around the selected window switcher item. + Default is *osd.label.text.color* with 50% opacity. + +*osd.window-switcher.style-classic.item.active.bg.color* + Background color of the selected window switcher item. + Default is *osd.label.text.color* with 15% opacity. + *osd.window-switcher.style-classic.item.icon.size* Size of the icon in window switcher, in pixels. If not set, the font size derived from diff --git a/docs/themerc b/docs/themerc index a52dfacd..9139170c 100644 --- a/docs/themerc +++ b/docs/themerc @@ -97,6 +97,8 @@ osd.window-switcher.style-classic.padding: 4 osd.window-switcher.style-classic.item.padding.x: 10 osd.window-switcher.style-classic.item.padding.y: 1 osd.window-switcher.style-classic.item.active.border.width: 2 +osd.window-switcher.style-classic.item.active.border.color: #706f6d +osd.window-switcher.style-classic.item.active.bg.color: #bfbcba # The icon size the same as the font size by default # osd.window-switcher.style-classic.item.icon.size: 50 diff --git a/include/theme.h b/include/theme.h index 797b5072..e7e13d66 100644 --- a/include/theme.h +++ b/include/theme.h @@ -170,6 +170,8 @@ struct theme { int item_padding_x; int item_padding_y; int item_active_border_width; + float item_active_border_color[4]; + float item_active_bg_color[4]; int item_icon_size; bool width_is_percent; diff --git a/src/osd/osd-classic.c b/src/osd/osd-classic.c index be740f2f..4cb92867 100644 --- a/src/osd/osd-classic.c +++ b/src/osd/osd-classic.c @@ -19,9 +19,59 @@ struct osd_classic_scene_item { struct view *view; - struct wlr_scene_node *highlight_outline; + struct wlr_scene_tree *normal_tree, *active_tree; }; +static void +create_fields_scene(struct server *server, struct view *view, + struct wlr_scene_tree *parent, const float *text_color, + const float *bg_color, int field_widths_sum, int x, int y) +{ + struct theme *theme = server->theme; + struct window_switcher_classic_theme *switcher_theme = + &theme->osd_window_switcher_classic; + + struct window_switcher_field *field; + wl_list_for_each(field, &rc.window_switcher.fields, link) { + int field_width = field_widths_sum * field->width / 100.0; + struct wlr_scene_node *node = NULL; + int height = -1; + + if (field->content == LAB_FIELD_ICON) { + int icon_size = MIN(field_width, + switcher_theme->item_icon_size); + struct scaled_icon_buffer *icon_buffer = + scaled_icon_buffer_create(parent, + server, icon_size, icon_size); + scaled_icon_buffer_set_view(icon_buffer, view); + node = &icon_buffer->scene_buffer->node; + height = icon_size; + } else { + struct buf buf = BUF_INIT; + osd_field_get_content(field, &buf, view); + + if (!string_null_or_empty(buf.data)) { + struct scaled_font_buffer *font_buffer = + scaled_font_buffer_create(parent); + scaled_font_buffer_update(font_buffer, + buf.data, field_width, + &rc.font_osd, text_color, bg_color); + node = &font_buffer->scene_buffer->node; + height = font_height(&rc.font_osd); + } + + buf_reset(&buf); + } + + if (node) { + int item_height = switcher_theme->item_height; + wlr_scene_node_set_position(node, + x, y + (item_height - height) / 2); + } + x += field_width + switcher_theme->item_padding_x; + } +} + static void osd_classic_create(struct output *output, struct wl_array *views) { @@ -126,62 +176,33 @@ osd_classic_create(struct output *output, struct wl_array *views) + switcher_theme->item_padding_x; struct wlr_scene_tree *item_root = wlr_scene_tree_create(output->osd_scene.tree); + item->normal_tree = wlr_scene_tree_create(item_root); + item->active_tree = wlr_scene_tree_create(item_root); + wlr_scene_node_set_enabled(&item->active_tree->node, false); - struct window_switcher_field *field; - wl_list_for_each(field, &rc.window_switcher.fields, link) { - int field_width = field_widths_sum * field->width / 100.0; - struct wlr_scene_node *node = NULL; - int height = -1; - - if (field->content == LAB_FIELD_ICON) { - int icon_size = MIN(field_width, - switcher_theme->item_icon_size); - struct scaled_icon_buffer *icon_buffer = - scaled_icon_buffer_create(item_root, - server, icon_size, icon_size); - scaled_icon_buffer_set_view(icon_buffer, *view); - node = &icon_buffer->scene_buffer->node; - height = icon_size; - } else { - buf_clear(&buf); - osd_field_get_content(field, &buf, *view); - - if (!string_null_or_empty(buf.data)) { - struct scaled_font_buffer *font_buffer = - scaled_font_buffer_create(item_root); - scaled_font_buffer_update(font_buffer, - buf.data, field_width, - &rc.font_osd, text_color, bg_color); - node = &font_buffer->scene_buffer->node; - height = font_height(&rc.font_osd); - } - } - - if (node) { - int item_height = switcher_theme->item_height; - wlr_scene_node_set_position(node, - x, y + (item_height - height) / 2); - } - x += field_width + switcher_theme->item_padding_x; - } + float *active_bg_color = switcher_theme->item_active_bg_color; + float *active_border_color = switcher_theme->item_active_border_color; /* Highlight around selected window's item */ int highlight_x = theme->osd_border_width + switcher_theme->padding; struct lab_scene_rect_options highlight_opts = { - .border_colors = (float *[1]) {text_color}, + .border_colors = (float *[1]) {active_border_color}, + .bg_color = active_bg_color, .nr_borders = 1, .border_width = switcher_theme->item_active_border_width, .width = w - 2 * theme->osd_border_width - 2 * switcher_theme->padding, .height = switcher_theme->item_height, }; - struct lab_scene_rect *highlight_rect = lab_scene_rect_create( - output->osd_scene.tree, &highlight_opts); - item->highlight_outline = &highlight_rect->tree->node; - wlr_scene_node_set_position(item->highlight_outline, highlight_x, y); - wlr_scene_node_set_enabled(item->highlight_outline, false); + item->active_tree, &highlight_opts); + wlr_scene_node_set_position(&highlight_rect->tree->node, highlight_x, y); + + create_fields_scene(server, *view, item->normal_tree, + text_color, bg_color, field_widths_sum, x, y); + create_fields_scene(server, *view, item->active_tree, + text_color, active_bg_color, field_widths_sum, x, y); y += switcher_theme->item_height; } @@ -200,8 +221,9 @@ osd_classic_update(struct output *output) { struct osd_classic_scene_item *item; wl_array_for_each(item, &output->osd_scene.items) { - wlr_scene_node_set_enabled(item->highlight_outline, - item->view == output->server->osd_state.cycle_view); + bool active = item->view == output->server->osd_state.cycle_view; + wlr_scene_node_set_enabled(&item->normal_tree->node, !active); + wlr_scene_node_set_enabled(&item->active_tree->node, active); } } diff --git a/src/theme.c b/src/theme.c index a3aec34a..acdbc16b 100644 --- a/src/theme.c +++ b/src/theme.c @@ -604,6 +604,8 @@ theme_builtin(struct theme *theme, struct server *server) theme->osd_window_switcher_classic.item_padding_x = 10; theme->osd_window_switcher_classic.item_padding_y = 1; theme->osd_window_switcher_classic.item_active_border_width = 2; + theme->osd_window_switcher_classic.item_active_border_color[0] = FLT_MIN; + theme->osd_window_switcher_classic.item_active_bg_color[0] = FLT_MIN; theme->osd_window_switcher_classic.item_icon_size = -1; theme->osd_window_switcher_thumbnail.max_width = 80; @@ -989,6 +991,12 @@ entry(struct theme *theme, const char *key, const char *value) get_int_if_positive(value, "osd.window-switcher.style-classic.item.active.border.width"); } + if (match_glob(key, "osd.window-switcher.style-classic.item.active.border.color")) { + parse_color(value, switcher_classic_theme->item_active_border_color); + } + if (match_glob(key, "osd.window-switcher.style-classic.item.active.bg.color")) { + parse_color(value, switcher_classic_theme->item_active_bg_color); + } if (match_glob(key, "osd.window-switcher.style-classic.item.icon.size") || match_glob(key, "osd.window-switcher.item.icon.size")) { switcher_classic_theme->item_icon_size = @@ -1746,6 +1754,14 @@ post_processing(struct theme *theme) memcpy(theme->osd_border_color, theme->osd_label_text_color, sizeof(theme->osd_border_color)); } + if (switcher_classic_theme->item_active_border_color[0] == FLT_MIN) { + blend_color_with_bg(switcher_classic_theme->item_active_border_color, + theme->osd_label_text_color, 0.50, theme->osd_bg_color); + } + if (switcher_classic_theme->item_active_bg_color[0] == FLT_MIN) { + blend_color_with_bg(switcher_classic_theme->item_active_bg_color, + theme->osd_label_text_color, 0.15, theme->osd_bg_color); + } if (switcher_thumb_theme->item_active_border_color[0] == FLT_MIN) { blend_color_with_bg(switcher_thumb_theme->item_active_border_color, theme->osd_label_text_color, 0.50, theme->osd_bg_color); From e6f54a0fc823107d171a26216f43ec7bea0bf953 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Mon, 29 Sep 2025 14:51:17 -0400 Subject: [PATCH 225/248] menu: use xmlFree() for return value of xmlGetProp() --- src/menu/menu.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/menu/menu.c b/src/menu/menu.c index 0e23e850..faf0c8da 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -487,8 +487,8 @@ fill_item(struct menu *menu, xmlNode *node) append_parsed_actions(node, &item->actions); out: - free(label); - free(icon_name); + xmlFree(label); + xmlFree(icon_name); } static void @@ -619,10 +619,10 @@ fill_menu(struct server *server, struct menu *parent, xmlNode *n) item->submenu = menu; } error: - free(label); - free(icon_name); - free(execute); - free(id); + xmlFree(label); + xmlFree(icon_name); + xmlFree(execute); + xmlFree(id); } /* This can be one of and */ @@ -631,7 +631,7 @@ fill_separator(struct menu *menu, xmlNode *n) { char *label = (char *)xmlGetProp(n, (const xmlChar *)"label"); separator_create(menu, label); - free(label); + xmlFree(label); } /* parent==NULL when processing toplevel menus in menu.xml */ From 40eed3915a2c50d80578a72e92883c6b4726d2c7 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Wed, 1 Oct 2025 12:41:00 -0400 Subject: [PATCH 226/248] osd,ssd: don't cast away const --- src/osd/osd-field.c | 2 +- src/ssd/ssd-titlebar.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/osd/osd-field.c b/src/osd/osd-field.c index c8f796d1..5b275bbb 100644 --- a/src/osd/osd-field.c +++ b/src/osd/osd-field.c @@ -36,7 +36,7 @@ get_app_id_or_class(struct view *view, bool trim) /* remove the first two nodes of 'org.' strings */ if (trim && !strncmp(identifier, "org.", 4)) { - char *p = (char *)identifier + 4; + const char *p = identifier + 4; p = strchr(p, '.'); if (p) { return ++p; diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 13f5e7df..152b9b61 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -440,7 +440,7 @@ ssd_update_title(struct ssd *ssd) } struct view *view = ssd->view; - char *title = (char *)view_get_string_prop(view, "title"); + const char *title = view_get_string_prop(view, "title"); if (string_null_or_empty(title)) { return; } From da96513e70d6f3b9377cf78be5a5dab2cf82fcd1 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Sat, 11 Oct 2025 21:47:40 -0400 Subject: [PATCH 227/248] menu: remove redundant cast --- 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 faf0c8da..89f061c1 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -482,7 +482,7 @@ fill_item(struct menu *menu, xmlNode *node) goto out; } - struct menuitem *item = item_create(menu, (char *)label, icon_name, false); + struct menuitem *item = item_create(menu, label, icon_name, false); lab_xml_expand_dotted_attributes(node); append_parsed_actions(node, &item->actions); From 27cc7389856d34c3d289b01f5b4c677690cd7aeb Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sun, 12 Oct 2025 23:59:04 +0900 Subject: [PATCH 228/248] osd-thumbnail: make sure item->{normal,active}_title are non-null The if-statement doesn't make sense, because `view_get_string_prop()` never returns NULL. And if it did, it would cause segfault in `osd_thumbnail_update()`. --- src/osd/osd-thumbnail.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/osd/osd-thumbnail.c b/src/osd/osd-thumbnail.c index 24505a31..d17f6fc3 100644 --- a/src/osd/osd-thumbnail.c +++ b/src/osd/osd-thumbnail.c @@ -156,14 +156,12 @@ create_item_scene(struct wlr_scene_tree *parent, struct view *view, /* title */ const char *title = view_get_string_prop(view, "title"); - if (title) { - item->normal_title = create_title(item->tree, switcher_theme, - title, theme->osd_label_text_color, - theme->osd_bg_color, title_y); - item->active_title = create_title(item->tree, switcher_theme, - title, theme->osd_label_text_color, - switcher_theme->item_active_bg_color, title_y); - } + item->normal_title = create_title(item->tree, switcher_theme, + title, theme->osd_label_text_color, + theme->osd_bg_color, title_y); + item->active_title = create_title(item->tree, switcher_theme, + title, theme->osd_label_text_color, + switcher_theme->item_active_bg_color, title_y); /* icon */ int icon_size = switcher_theme->item_icon_size; From babd7af8f83431f161096cf35df1b31ae305a433 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Wed, 13 Aug 2025 17:40:25 +0900 Subject: [PATCH 229/248] view: store title/app_id in view This simplifies our codes and eliminates duplicated `view.events.new_{title,app_id}` events. This should not change any behaviors. --- include/view.h | 10 +++--- src/debug.c | 5 ++- src/foreign-toplevel/ext-foreign.c | 14 ++++---- src/foreign-toplevel/wlr-foreign.c | 20 +++--------- src/menu/menu.c | 8 ++--- src/osd/osd-field.c | 34 ++++++------------- src/osd/osd-thumbnail.c | 5 ++- src/scaled-buffer/scaled-icon-buffer.c | 2 +- src/ssd/ssd-titlebar.c | 12 +++---- src/view-impl-common.c | 5 +-- src/view.c | 45 ++++++++++++++++---------- src/xdg.c | 39 +++++----------------- src/xwayland.c | 43 ++++++------------------ 13 files changed, 87 insertions(+), 155 deletions(-) diff --git a/include/view.h b/include/view.h index ba662550..a8456de2 100644 --- a/include/view.h +++ b/include/view.h @@ -106,7 +106,6 @@ struct view_size_hints { struct view_impl { void (*configure)(struct view *view, struct wlr_box geo); void (*close)(struct view *view); - const char *(*get_string_prop)(struct view *view, const char *prop); void (*map)(struct view *view); void (*set_activated)(struct view *view, bool activated); void (*set_fullscreen)(struct view *view, bool fullscreen); @@ -173,6 +172,10 @@ struct view { struct wlr_scene_tree *scene_tree; struct wlr_scene_tree *content_tree; + /* These are never NULL and an empty string is set instead. */ + char *title; + char *app_id; /* WM_CLASS for xwayland windows */ + bool mapped; bool been_mapped; enum lab_ssd_mode ssd_mode; @@ -571,9 +574,8 @@ bool view_on_output(struct view *view, struct output *output); */ bool view_has_strut_partial(struct view *view); -const char *view_get_string_prop(struct view *view, const char *prop); -void view_update_title(struct view *view); -void view_update_app_id(struct view *view); +void view_set_title(struct view *view, const char *title); +void view_set_app_id(struct view *view, const char *app_id); void view_reload_ssd(struct view *view); void view_set_shade(struct view *view, bool shaded); diff --git a/src/debug.c b/src/debug.c index 97c011f2..84b844d8 100644 --- a/src/debug.c +++ b/src/debug.c @@ -71,11 +71,10 @@ get_view_part(struct view *view, struct wlr_scene_node *node) return NULL; } if (node == &view->scene_tree->node) { - const char *app_id = view_get_string_prop(view, "app_id"); - if (string_null_or_empty(app_id)) { + if (string_null_or_empty(view->app_id)) { return "view"; } - snprintf(view_name, sizeof(view_name), "view (%s)", app_id); + snprintf(view_name, sizeof(view_name), "view (%s)", view->app_id); return view_name; } if (node == &view->content_tree->node) { diff --git a/src/foreign-toplevel/ext-foreign.c b/src/foreign-toplevel/ext-foreign.c index 587193a6..748050fa 100644 --- a/src/foreign-toplevel/ext-foreign.c +++ b/src/foreign-toplevel/ext-foreign.c @@ -32,8 +32,8 @@ handle_new_app_id(struct wl_listener *listener, void *data) assert(ext_toplevel->handle); struct wlr_ext_foreign_toplevel_handle_v1_state state = { - .title = view_get_string_prop(ext_toplevel->view, "title"), - .app_id = view_get_string_prop(ext_toplevel->view, "app_id") + .title = ext_toplevel->view->title, + .app_id = ext_toplevel->view->app_id, }; wlr_ext_foreign_toplevel_handle_v1_update_state(ext_toplevel->handle, &state); @@ -47,8 +47,8 @@ handle_new_title(struct wl_listener *listener, void *data) assert(ext_toplevel->handle); struct wlr_ext_foreign_toplevel_handle_v1_state state = { - .title = view_get_string_prop(ext_toplevel->view, "title"), - .app_id = view_get_string_prop(ext_toplevel->view, "app_id") + .title = ext_toplevel->view->title, + .app_id = ext_toplevel->view->app_id, }; wlr_ext_foreign_toplevel_handle_v1_update_state(ext_toplevel->handle, &state); @@ -63,15 +63,15 @@ ext_foreign_toplevel_init(struct ext_foreign_toplevel *ext_toplevel, ext_toplevel->view = view; struct wlr_ext_foreign_toplevel_handle_v1_state state = { - .title = view_get_string_prop(view, "title"), - .app_id = view_get_string_prop(view, "app_id") + .title = view->title, + .app_id = view->app_id, }; ext_toplevel->handle = wlr_ext_foreign_toplevel_handle_v1_create( view->server->foreign_toplevel_list, &state); if (!ext_toplevel->handle) { wlr_log(WLR_ERROR, "cannot create ext toplevel handle for (%s)", - view_get_string_prop(view, "title")); + view->title); return; } diff --git a/src/foreign-toplevel/wlr-foreign.c b/src/foreign-toplevel/wlr-foreign.c index 9f0ed8e6..a84775f5 100644 --- a/src/foreign-toplevel/wlr-foreign.c +++ b/src/foreign-toplevel/wlr-foreign.c @@ -94,13 +94,8 @@ handle_new_app_id(struct wl_listener *listener, void *data) wl_container_of(listener, wlr_toplevel, on_view.new_app_id); assert(wlr_toplevel->handle); - const char *app_id = view_get_string_prop(wlr_toplevel->view, "app_id"); - const char *wlr_app_id = wlr_toplevel->handle->app_id; - if (app_id && wlr_app_id && !strcmp(app_id, wlr_app_id)) { - /* Don't send app_id if they are the same */ - return; - } - wlr_foreign_toplevel_handle_v1_set_app_id(wlr_toplevel->handle, app_id); + wlr_foreign_toplevel_handle_v1_set_app_id(wlr_toplevel->handle, + wlr_toplevel->view->app_id); } static void @@ -110,13 +105,8 @@ handle_new_title(struct wl_listener *listener, void *data) wl_container_of(listener, wlr_toplevel, on_view.new_title); assert(wlr_toplevel->handle); - const char *title = view_get_string_prop(wlr_toplevel->view, "title"); - const char *wlr_title = wlr_toplevel->handle->title; - if (title && wlr_title && !strcmp(title, wlr_title)) { - /* Don't send title if they are the same */ - return; - } - wlr_foreign_toplevel_handle_v1_set_title(wlr_toplevel->handle, title); + wlr_foreign_toplevel_handle_v1_set_title(wlr_toplevel->handle, + wlr_toplevel->view->title); } static void @@ -202,7 +192,7 @@ wlr_foreign_toplevel_init(struct wlr_foreign_toplevel *wlr_toplevel, view->server->foreign_toplevel_manager); if (!wlr_toplevel->handle) { wlr_log(WLR_ERROR, "cannot create wlr foreign toplevel handle for (%s)", - view_get_string_prop(view, "title")); + view->title); return; } diff --git a/src/menu/menu.c b/src/menu/menu.c index 89f061c1..3770e7f6 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -900,8 +900,8 @@ update_client_list_combined_menu(struct server *server) wl_list_for_each(view, &server->views, link) { if (view->workspace == workspace) { - const char *title = view_get_string_prop(view, "title"); - if (!view->foreign_toplevel || string_null_or_empty(title)) { + if (!view->foreign_toplevel + || string_null_or_empty(view->title)) { continue; } @@ -909,9 +909,9 @@ update_client_list_combined_menu(struct server *server) buf_add(&buffer, "*"); } if (view->minimized) { - buf_add_fmt(&buffer, "(%s)", title); + buf_add_fmt(&buffer, "(%s)", view->title); } else { - buf_add(&buffer, title); + buf_add(&buffer, view->title); } item = item_create(menu, buffer.data, NULL, /*show arrow*/ false); diff --git a/src/osd/osd-field.c b/src/osd/osd-field.c index 5b275bbb..9852b11f 100644 --- a/src/osd/osd-field.c +++ b/src/osd/osd-field.c @@ -26,13 +26,9 @@ struct field_converter { /* Internal helpers */ static const char * -get_app_id_or_class(struct view *view, bool trim) +get_identifier(struct view *view, bool trim) { - /* - * XWayland clients return WM_CLASS for 'app_id' so we don't need a - * special case for that here. - */ - const char *identifier = view_get_string_prop(view, "app_id"); + const char *identifier = view->app_id; /* remove the first two nodes of 'org.' strings */ if (trim && !strncmp(identifier, "org.", 4)) { @@ -49,14 +45,13 @@ static const char * get_desktop_name(struct view *view) { #if HAVE_LIBSFDO - const char *app_id = view_get_string_prop(view, "app_id"); - const char *name = desktop_entry_name_lookup(view->server, app_id); + const char *name = desktop_entry_name_lookup(view->server, view->app_id); if (name) { return name; } #endif - return get_app_id_or_class(view, /* trim */ true); + return get_identifier(view, /* trim */ true); } static const char * @@ -73,21 +68,12 @@ get_type(struct view *view, bool short_form) return "???"; } -static const char * -get_title(struct view *view) -{ - return view_get_string_prop(view, "title"); -} - static const char * get_title_if_different(struct view *view) { - const char *identifier = get_app_id_or_class(view, /*trim*/ false); - const char *title = get_title(view); - if (!identifier) { - return title; - } - return (!title || !strcmp(identifier, title)) ? NULL : title; + const char *identifier = get_identifier(view, /*trim*/ false); + const char *title = view->title; + return !strcmp(identifier, title) ? NULL : title; } /* Field handlers */ @@ -169,14 +155,14 @@ static void field_set_identifier(struct buf *buf, struct view *view, const char *format) { /* custom type conversion-specifier: I */ - buf_add(buf, get_app_id_or_class(view, /*trim*/ false)); + buf_add(buf, get_identifier(view, /*trim*/ false)); } static void field_set_identifier_trimmed(struct buf *buf, struct view *view, const char *format) { /* custom type conversion-specifier: i */ - buf_add(buf, get_app_id_or_class(view, /*trim*/ true)); + buf_add(buf, get_identifier(view, /*trim*/ true)); } static void @@ -190,7 +176,7 @@ static void field_set_title(struct buf *buf, struct view *view, const char *format) { /* custom type conversion-specifier: T */ - buf_add(buf, get_title(view)); + buf_add(buf, view->title); } static void diff --git a/src/osd/osd-thumbnail.c b/src/osd/osd-thumbnail.c index d17f6fc3..c9e6b283 100644 --- a/src/osd/osd-thumbnail.c +++ b/src/osd/osd-thumbnail.c @@ -155,12 +155,11 @@ create_item_scene(struct wlr_scene_tree *parent, struct view *view, } /* title */ - const char *title = view_get_string_prop(view, "title"); item->normal_title = create_title(item->tree, switcher_theme, - title, theme->osd_label_text_color, + view->title, theme->osd_label_text_color, theme->osd_bg_color, title_y); item->active_title = create_title(item->tree, switcher_theme, - title, theme->osd_label_text_color, + view->title, theme->osd_label_text_color, switcher_theme->item_active_bg_color, title_y); /* icon */ diff --git a/src/scaled-buffer/scaled-icon-buffer.c b/src/scaled-buffer/scaled-icon-buffer.c index 0cd09b22..ceab0509 100644 --- a/src/scaled-buffer/scaled-icon-buffer.c +++ b/src/scaled-buffer/scaled-icon-buffer.c @@ -281,7 +281,7 @@ handle_view_new_app_id(struct wl_listener *listener, void *data) struct scaled_icon_buffer *self = wl_container_of(listener, self, on_view.new_app_id); - const char *app_id = view_get_string_prop(self->view, "app_id"); + const char *app_id = self->view->app_id; if (str_equal(app_id, self->view_app_id)) { return; } diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 152b9b61..09f5362a 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -440,14 +440,13 @@ ssd_update_title(struct ssd *ssd) } struct view *view = ssd->view; - const char *title = view_get_string_prop(view, "title"); - if (string_null_or_empty(title)) { + if (string_null_or_empty(view->title)) { return; } struct theme *theme = view->server->theme; struct ssd_state_title *state = &ssd->state.title; - bool title_unchanged = state->text && !strcmp(title, state->text); + bool title_unchanged = state->text && !strcmp(view->title, state->text); int offset_left, offset_right; get_title_offsets(ssd, &offset_left, &offset_right); @@ -473,7 +472,7 @@ ssd_update_title(struct ssd *ssd) } const float bg_color[4] = {0, 0, 0, 0}; /* ignored */ - scaled_font_buffer_update(subtree->title, title, + scaled_font_buffer_update(subtree->title, view->title, title_bg_width, font, text_color, bg_color); @@ -483,10 +482,7 @@ ssd_update_title(struct ssd *ssd) } if (!title_unchanged) { - if (state->text) { - free(state->text); - } - state->text = xstrdup(title); + xstrdup_replace(state->text, view->title); } ssd_update_title_positions(ssd, offset_left, offset_right); } diff --git a/src/view-impl-common.c b/src/view-impl-common.c index d529941a..df72fe62 100644 --- a/src/view-impl-common.c +++ b/src/view-impl-common.c @@ -10,8 +10,6 @@ void view_impl_map(struct view *view) { desktop_focus_view(view, /*raise*/ true); - view_update_title(view); - view_update_app_id(view); if (!view->been_mapped) { window_rules_apply(view, LAB_WINDOW_RULE_EVENT_ON_FIRST_MAP); } @@ -36,8 +34,7 @@ view_impl_map(struct view *view) desktop_update_top_layer_visibility(view->server); wlr_log(WLR_DEBUG, "[map] identifier=%s, title=%s", - view_get_string_prop(view, "app_id"), - view_get_string_prop(view, "title")); + view->app_id, view->title); } void diff --git a/src/view.c b/src/view.c index ddfe6b1e..87ef8566 100644 --- a/src/view.c +++ b/src/view.c @@ -133,11 +133,11 @@ view_contains_window_type(struct view *view, enum lab_window_type window_type) bool view_matches_query(struct view *view, struct view_query *query) { - if (!query_str_match(query->identifier, view_get_string_prop(view, "app_id"))) { + if (!query_str_match(query->identifier, view->app_id)) { return false; } - if (!query_str_match(query->title, view_get_string_prop(view, "title"))) { + if (!query_str_match(query->title, view->title)) { return false; } @@ -2385,31 +2385,36 @@ view_has_strut_partial(struct view *view) view->impl->has_strut_partial(view); } -/* Note: It is safe to assume that this function never returns NULL */ -const char * -view_get_string_prop(struct view *view, const char *prop) -{ - assert(view); - assert(prop); - if (view->impl->get_string_prop) { - const char *ret = view->impl->get_string_prop(view, prop); - return ret ? ret : ""; - } - return ""; -} - void -view_update_title(struct view *view) +view_set_title(struct view *view, const char *title) { assert(view); + if (!title) { + title = ""; + } + + if (!strcmp(view->title, title)) { + return; + } + xstrdup_replace(view->title, title); + ssd_update_title(view->ssd); wl_signal_emit_mutable(&view->events.new_title, NULL); } void -view_update_app_id(struct view *view) +view_set_app_id(struct view *view, const char *app_id) { assert(view); + if (!app_id) { + app_id = ""; + } + + if (!strcmp(view->app_id, app_id)) { + return; + } + xstrdup_replace(view->app_id, app_id); + wl_signal_emit_mutable(&view->events.new_app_id, NULL); } @@ -2562,6 +2567,9 @@ view_init(struct view *view) wl_signal_init(&view->events.activated); wl_signal_init(&view->events.set_icon); wl_signal_init(&view->events.destroy); + + view->title = xstrdup(""); + view->app_id = xstrdup(""); } void @@ -2585,6 +2593,9 @@ view_destroy(struct view *view) wl_list_remove(&view->set_title.link); wl_list_remove(&view->destroy.link); + zfree(view->title); + zfree(view->app_id); + if (view->foreign_toplevel) { foreign_toplevel_destroy(view->foreign_toplevel); view->foreign_toplevel = NULL; diff --git a/src/xdg.c b/src/xdg.c index 1e09dd99..f877e7ba 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -216,7 +216,7 @@ handle_commit(struct wl_listener *listener, void *data) && extent.height == view->pending.height) { wlr_log(WLR_DEBUG, "window geometry for client (%s) " "appears to be incorrect - ignoring", - view_get_string_prop(view, "app_id")); + view->app_id); size = extent; /* Use surface extent instead */ } } @@ -283,9 +283,8 @@ handle_configure_timeout(void *data) assert(view->pending_configure_serial > 0); assert(view->pending_configure_timeout); - const char *app_id = view_get_string_prop(view, "app_id"); wlr_log(WLR_INFO, "client (%s) did not respond to configure request " - "in %d ms", app_id, CONFIGURE_TIMEOUT_MS); + "in %d ms", view->app_id, CONFIGURE_TIMEOUT_MS); wl_event_source_remove(view->pending_configure_timeout); view->pending_configure_serial = 0; @@ -492,7 +491,9 @@ static void handle_set_title(struct wl_listener *listener, void *data) { struct view *view = wl_container_of(listener, view, set_title); - view_update_title(view); + struct wlr_xdg_toplevel *toplevel = xdg_toplevel_from_view(view); + + view_set_title(view, toplevel->title); } static void @@ -501,7 +502,9 @@ handle_set_app_id(struct wl_listener *listener, void *data) struct xdg_toplevel_view *xdg_toplevel_view = wl_container_of(listener, xdg_toplevel_view, set_app_id); struct view *view = &xdg_toplevel_view->base; - view_update_app_id(view); + struct wlr_xdg_toplevel *toplevel = xdg_toplevel_from_view(view); + + view_set_app_id(view, toplevel->app_id); } static void @@ -740,31 +743,6 @@ set_initial_position(struct view *view) view_place_by_policy(view, /* allow_cursor */ true, rc.placement_policy); } -static const char * -xdg_toplevel_view_get_string_prop(struct view *view, const char *prop) -{ - struct xdg_toplevel_view *xdg_view = xdg_toplevel_view_from_view(view); - struct wlr_xdg_toplevel *xdg_toplevel = xdg_view->xdg_surface - ? xdg_view->xdg_surface->toplevel - : NULL; - if (!xdg_toplevel) { - /* - * This may happen due to a matchOnce rule when - * a view is destroyed while A-Tab is open. See - * https://github.com/labwc/labwc/issues/1082#issuecomment-1716137180 - */ - return ""; - } - - if (!strcmp(prop, "title")) { - return xdg_toplevel->title ? xdg_toplevel->title : ""; - } - if (!strcmp(prop, "app_id")) { - return xdg_toplevel->app_id ? xdg_toplevel->app_id : ""; - } - return ""; -} - static void init_foreign_toplevel(struct view *view) { @@ -884,7 +862,6 @@ xdg_view_get_pid(struct view *view) static const struct view_impl xdg_toplevel_view_impl = { .configure = xdg_toplevel_view_configure, .close = xdg_toplevel_view_close, - .get_string_prop = xdg_toplevel_view_get_string_prop, .map = xdg_toplevel_view_map, .set_activated = xdg_toplevel_view_set_activated, .set_fullscreen = xdg_toplevel_view_set_fullscreen, diff --git a/src/xwayland.c b/src/xwayland.c index 0ab92f0d..6b408a78 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -498,7 +498,8 @@ static void handle_set_title(struct wl_listener *listener, void *data) { struct view *view = wl_container_of(listener, view, set_title); - view_update_title(view); + struct xwayland_view *xwayland_view = xwayland_view_from_view(view); + view_set_title(view, xwayland_view->xwayland_surface->title); } static void @@ -507,35 +508,7 @@ handle_set_class(struct wl_listener *listener, void *data) struct xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, set_class); struct view *view = &xwayland_view->base; - view_update_app_id(view); -} -static void -xwayland_view_close(struct view *view) -{ - wlr_xwayland_surface_close(xwayland_surface_from_view(view)); -} - -static const char * -xwayland_view_get_string_prop(struct view *view, const char *prop) -{ - struct xwayland_view *xwayland_view = xwayland_view_from_view(view); - struct wlr_xwayland_surface *xwayland_surface = xwayland_view->xwayland_surface; - if (!xwayland_surface) { - /* - * This may happen due to a matchOnce rule when - * a view is destroyed while A-Tab is open. See - * https://github.com/labwc/labwc/issues/1082#issuecomment-1716137180 - */ - return ""; - } - - if (!strcmp(prop, "title")) { - return xwayland_surface->title ? xwayland_surface->title : ""; - } - if (!strcmp(prop, "class")) { - return xwayland_surface->class ? xwayland_surface->class : ""; - } /* * Use the WM_CLASS 'instance' (1st string) for the app_id. Per * ICCCM, this is usually "the trailing part of the name used to @@ -545,10 +518,13 @@ xwayland_view_get_string_prop(struct view *view, const char *prop) * 'instance' except for being capitalized. We want lowercase * here since we use the app_id for icon lookups. */ - if (!strcmp(prop, "app_id")) { - return xwayland_surface->instance ? xwayland_surface->instance : ""; - } - return ""; + view_set_app_id(view, xwayland_view->xwayland_surface->instance); +} + +static void +xwayland_view_close(struct view *view) +{ + wlr_xwayland_surface_close(xwayland_surface_from_view(view)); } static void @@ -1050,7 +1026,6 @@ xwayland_view_get_pid(struct view *view) static const struct view_impl xwayland_view_impl = { .configure = xwayland_view_configure, .close = xwayland_view_close, - .get_string_prop = xwayland_view_get_string_prop, .map = xwayland_view_map, .set_activated = xwayland_view_set_activated, .set_fullscreen = xwayland_view_set_fullscreen, From 364a1d520705ced7e6056cdda0fcfa3dd4643400 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Fri, 3 Oct 2025 02:10:00 +0200 Subject: [PATCH 230/248] osd: allow window switcher to temporary unshade windows This can be configured with a new unshade="yes|no" argument for windowSwitcher in rc.xml Fixes: #3111 --- docs/labwc-config.5.scd | 5 ++++- docs/rc.xml.all | 3 ++- include/config/rcxml.h | 1 + include/labwc.h | 1 + src/config/rcxml.c | 3 +++ src/input/keyboard.c | 3 +++ src/osd/osd.c | 18 +++++++++++++++--- 7 files changed, 29 insertions(+), 5 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index 21ac2629..20149bee 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -346,7 +346,7 @@ this is for compatibility with Openbox. ``` -** +** *show* [yes|no] Draw the OnScreenDisplay when switching between windows. Default is yes. @@ -364,6 +364,9 @@ this is for compatibility with Openbox. they are on. Default no (that is only windows on the current workspace are shown). + *unshade* [yes|no] Temporarily unshade windows when switching between + them and permanently unshade on the final selection. Default is yes. + ** Define window switcher fields when using **. diff --git a/docs/rc.xml.all b/docs/rc.xml.all index 44ed984a..1dd08c4d 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -79,7 +79,8 @@ - + diff --git a/include/config/rcxml.h b/include/config/rcxml.h index e4fd184b..50874571 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -179,6 +179,7 @@ struct rcxml { bool show; bool preview; bool outlines; + bool unshade; enum lab_view_criteria criteria; struct wl_list fields; /* struct window_switcher_field.link */ enum window_switcher_style style; diff --git a/include/labwc.h b/include/labwc.h index 5a3bbefa..735f57b8 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -303,6 +303,7 @@ struct server { /* Set when in cycle (alt-tab) mode */ struct osd_state { struct view *cycle_view; + bool preview_was_shaded; bool preview_was_enabled; struct wlr_scene_node *preview_node; struct wlr_scene_tree *preview_parent; diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 4ed6bd6d..be49eaeb 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -1217,6 +1217,8 @@ entry(xmlNode *node, char *nodename, char *content) rc.window_switcher.criteria &= ~LAB_VIEW_CRITERIA_CURRENT_WORKSPACE; } + } else if (!strcasecmp(nodename, "unshade.windowSwitcher")) { + set_bool(content, &rc.window_switcher.unshade); /* Remove this long term - just a friendly warning for now */ } else if (strstr(nodename, "windowswitcher.core")) { @@ -1429,6 +1431,7 @@ rcxml_init(void) rc.window_switcher.style = WINDOW_SWITCHER_CLASSIC; rc.window_switcher.preview = true; rc.window_switcher.outlines = true; + rc.window_switcher.unshade = true; rc.window_switcher.criteria = LAB_VIEW_CRITERIA_CURRENT_WORKSPACE | LAB_VIEW_CRITERIA_ROOT_TOPLEVEL | LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER; diff --git a/src/input/keyboard.c b/src/input/keyboard.c index ae92e73f..8f7a3e81 100644 --- a/src/input/keyboard.c +++ b/src/input/keyboard.c @@ -96,6 +96,9 @@ end_cycling(struct server *server) /* FIXME: osd_finish() transiently sets focus to the old surface */ osd_finish(server); /* Note that server->osd_state.cycle_view is cleared at this point */ + if (rc.window_switcher.unshade) { + view_set_shade(cycle_view, false); + } desktop_focus_view(cycle_view, /*raise*/ true); } diff --git a/src/osd/osd.c b/src/osd/osd.c index df1b7ad9..e436c19e 100644 --- a/src/osd/osd.c +++ b/src/osd/osd.c @@ -9,6 +9,7 @@ #include "common/scene-helpers.h" #include "config/rcxml.h" #include "labwc.h" +#include "node.h" #include "output.h" #include "scaled-buffer/scaled-font-buffer.h" #include "scaled-buffer/scaled-icon-buffer.h" @@ -160,9 +161,14 @@ restore_preview_node(struct server *server) if (!osd_state->preview_was_enabled) { wlr_scene_node_set_enabled(osd_state->preview_node, false); } + if (osd_state->preview_was_shaded) { + struct view *view = node_view_from_node(osd_state->preview_node); + view_set_shade(view, true); + } osd_state->preview_node = NULL; osd_state->preview_parent = NULL; osd_state->preview_anchor = NULL; + osd_state->preview_was_shaded = false; } } @@ -203,6 +209,7 @@ osd_finish(struct server *server) server->osd_state.preview_node = NULL; server->osd_state.preview_anchor = NULL; server->osd_state.cycle_view = NULL; + server->osd_state.preview_was_shaded = false; destroy_osd_scenes(server); @@ -244,6 +251,10 @@ preview_cycled_view(struct view *view) if (!osd_state->preview_was_enabled) { wlr_scene_node_set_enabled(osd_state->preview_node, true); } + if (rc.window_switcher.unshade && view->shaded) { + view_set_shade(view, false); + osd_state->preview_was_shaded = true; + } /* * FIXME: This abuses an implementation detail of the always-on-top tree. @@ -294,6 +305,10 @@ update_osd(struct server *server) } } + if (rc.window_switcher.preview) { + preview_cycled_view(server->osd_state.cycle_view); + } + /* Outline current window */ if (rc.window_switcher.outlines) { if (view_is_focusable(server->osd_state.cycle_view)) { @@ -301,9 +316,6 @@ update_osd(struct server *server) } } - if (rc.window_switcher.preview) { - preview_cycled_view(server->osd_state.cycle_view); - } out: wl_array_release(&views); } From 2b3aadb6afb2b7cd1031ce0559746cda4074a89a Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Thu, 2 Oct 2025 14:43:25 +0900 Subject: [PATCH 231/248] labnag: s/LAB_EXIT_TIMEOUT/LAB_EXIT_CANCELLED/ --- clients/labnag.c | 2 +- include/action-prompt-codes.h | 2 +- src/action.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clients/labnag.c b/clients/labnag.c index 23e0be1f..c9566328 100644 --- a/clients/labnag.c +++ b/clients/labnag.c @@ -1233,7 +1233,7 @@ nag_run(struct nag *nag) wl_display_cancel_read(nag->display); } if (nag->pollfds[FD_TIMER].revents & POLLIN) { - exit_status = LAB_EXIT_TIMEOUT; + exit_status = LAB_EXIT_CANCELLED; break; } if (nag->pollfds[FD_SIGNAL].revents & POLLIN) { diff --git a/include/action-prompt-codes.h b/include/action-prompt-codes.h index d1ca0f05..68d1b066 100644 --- a/include/action-prompt-codes.h +++ b/include/action-prompt-codes.h @@ -3,7 +3,7 @@ #define LABWC_ACTION_PROMPT_CODES_H #define LAB_EXIT_FAILURE 255 -#define LAB_EXIT_TIMEOUT 254 +#define LAB_EXIT_CANCELLED 254 #define LAB_EXIT_SUCCESS 0 #endif /* LABWC_ACTION_PROMPT_CODES_H */ diff --git a/src/action.c b/src/action.c index a78b01e8..a351160d 100644 --- a/src/action.c +++ b/src/action.c @@ -934,7 +934,7 @@ action_check_prompt_result(pid_t pid, int exit_code) if (exit_code == LAB_EXIT_SUCCESS) { wlr_log(WLR_INFO, "Selected the 'then' branch"); actions = action_get_actionlist(prompt->action, "then"); - } else if (exit_code == LAB_EXIT_TIMEOUT) { + } else if (exit_code == LAB_EXIT_CANCELLED) { /* no-op */ } else { wlr_log(WLR_INFO, "Selected the 'else' branch"); From 03c70e8a5e3e685a6d94fbca5d9eda1e56ef49e5 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Fri, 3 Oct 2025 01:13:39 +0900 Subject: [PATCH 232/248] labnag: remove redundant lines in conf_init() --- clients/labnag.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/clients/labnag.c b/clients/labnag.c index c9566328..0c279b2a 100644 --- a/clients/labnag.c +++ b/clients/labnag.c @@ -1250,13 +1250,6 @@ conf_init(struct conf *conf) | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; conf->layer = ZWLR_LAYER_SHELL_V1_LAYER_TOP; - conf->button_background = 0x333333FF; - conf->details_background = 0x333333FF; - conf->background = 0x323232FF; - conf->text = 0xFFFFFFFF; - conf->button_text = 0xFFFFFFFF; - conf->button_border = 0x222222FF; - conf->border_bottom = 0x444444FF; conf->bar_border_thickness = 2; conf->message_padding = 8; conf->details_border_thickness = 3; From ef73431367c30f9ab2241f577e4816f23e7794b5 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Wed, 1 Oct 2025 14:49:23 +0900 Subject: [PATCH 233/248] labnag: add --keyboard-focus option The new `--keyboard-focus [none|on-demand|exclusive]` option (default: `none`) allows to some keyboard controls in labnag: Right-arrow or Tab: move the button selection to the right Left-arrow or Shift-Tab: move the button selection to the left Enter: press the selected button Escape: close labnag The selected button is highlighted with the inner 1px border. Maybe we can instead use different colors for the selected button, but I prefer the inner border for now because it doesn't require us to add new color options or make them inherit labwc's theme. --- clients/labnag.c | 237 +++++++++++++++++++++++++++++++++++++++++++- clients/meson.build | 1 + docs/labnag.1.scd | 3 + 3 files changed, 237 insertions(+), 4 deletions(-) diff --git a/clients/labnag.c b/clients/labnag.c index 0c279b2a..31ac4e9d 100644 --- a/clients/labnag.c +++ b/clients/labnag.c @@ -19,6 +19,7 @@ #ifdef __FreeBSD__ #include /* For signalfd() */ #endif +#include #include #include #include @@ -26,6 +27,7 @@ #include #include #include +#include #include "action-prompt-codes.h" #include "pool-buffer.h" #include "cursor-shape-v1-client-protocol.h" @@ -38,6 +40,7 @@ struct conf { char *output; uint32_t anchors; int32_t layer; /* enum zwlr_layer_shell_v1_layer or -1 if unset */ + enum zwlr_layer_surface_v1_keyboard_interactivity keyboard_focus; /* Colors */ uint32_t button_text; @@ -69,11 +72,18 @@ struct pointer { int y; }; +struct keyboard { + struct wl_keyboard *keyboard; + struct xkb_keymap *keymap; + struct xkb_state *state; +}; + struct seat { struct wl_seat *wl_seat; uint32_t wl_name; struct nag *nag; struct pointer pointer; + struct keyboard keyboard; struct wl_list link; /* nag.seats */ }; @@ -130,6 +140,7 @@ struct nag { struct conf *conf; char *message; struct wl_list buttons; + int selected_button; struct pollfd pollfds[NR_FDS]; struct { @@ -409,7 +420,8 @@ render_detailed(cairo_t *cairo, struct nag *nag, uint32_t y) } static uint32_t -render_button(cairo_t *cairo, struct nag *nag, struct button *button, int *x) +render_button(cairo_t *cairo, struct nag *nag, struct button *button, + bool selected, int *x) { int text_width, text_height; get_text_size(cairo, nag->conf->font_description, &text_width, @@ -439,6 +451,14 @@ render_button(cairo_t *cairo, struct nag *nag, struct button *button, int *x) button->width, button->height); cairo_fill(cairo); + if (selected) { + cairo_set_source_u32(cairo, nag->conf->button_border); + cairo_set_line_width(cairo, 1); + cairo_rectangle(cairo, button->x + 1.5, button->y + 1.5, + button->width - 3, button->height - 3); + cairo_stroke(cairo); + } + cairo_set_source_u32(cairo, nag->conf->button_text); cairo_move_to(cairo, button->x + padding, button->y + padding); render_text(cairo, nag->conf->font_description, 1, true, @@ -464,11 +484,13 @@ render_to_cairo(cairo_t *cairo, struct nag *nag) int x = nag->width - nag->conf->button_margin_right; x -= nag->conf->button_gap_close; + int idx = 0; struct button *button; wl_list_for_each(button, &nag->buttons, link) { - h = render_button(cairo, nag, button, &x); + h = render_button(cairo, nag, button, idx == nag->selected_button, &x); max_height = h > max_height ? h : max_height; x -= nag->conf->button_gap; + idx++; } if (nag->details.visible) { @@ -555,6 +577,15 @@ seat_destroy(struct seat *seat) if (seat->pointer.pointer) { wl_pointer_destroy(seat->pointer.pointer); } + if (seat->keyboard.keyboard) { + wl_keyboard_destroy(seat->keyboard.keyboard); + } + if (seat->keyboard.keymap) { + xkb_keymap_unref(seat->keyboard.keymap); + } + if (seat->keyboard.state) { + xkb_state_unref(seat->keyboard.state); + } wl_seat_destroy(seat->wl_seat); wl_list_remove(&seat->link); free(seat); @@ -939,12 +970,170 @@ static const struct wl_pointer_listener pointer_listener = { .axis_discrete = wl_pointer_axis_discrete, }; +static void +wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, + uint32_t format, int32_t fd, uint32_t size) +{ + struct seat *seat = data; + + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { + wlr_log(WLR_ERROR, "unreconizable keymap format: %d", format); + close(fd); + return; + } + + char *map_shm = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + if (map_shm == MAP_FAILED) { + wlr_log_errno(WLR_ERROR, "mmap()"); + close(fd); + return; + } + + if (seat->keyboard.keymap) { + xkb_keymap_unref(seat->keyboard.keymap); + seat->keyboard.keymap = NULL; + } + if (seat->keyboard.state) { + xkb_state_unref(seat->keyboard.state); + seat->keyboard.state = NULL; + } + struct xkb_context *xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + seat->keyboard.keymap = xkb_keymap_new_from_string(xkb, map_shm, + XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); + if (seat->keyboard.keymap) { + seat->keyboard.state = xkb_state_new(seat->keyboard.keymap); + } else { + wlr_log(WLR_ERROR, "failed to compile keymap"); + } + xkb_context_unref(xkb); + + munmap(map_shm, size); + close(fd); +} + +static void +wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, + struct wl_surface *surface, struct wl_array *keys) +{ +} + +static void +wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, + struct wl_surface *surface) +{ +} + +static void +wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, + uint32_t time, uint32_t key, uint32_t state) +{ + struct seat *seat = data; + struct nag *nag = seat->nag; + + if (!seat->keyboard.keymap || !seat->keyboard.state) { + wlr_log(WLR_ERROR, "keymap/state unavailable"); + return; + } + + if (state != WL_KEYBOARD_KEY_STATE_PRESSED) { + return; + } + + key += 8; + const xkb_keysym_t *syms; + if (!xkb_keymap_key_get_syms_by_level(seat->keyboard.keymap, + key, 0, 0, &syms)) { + wlr_log(WLR_ERROR, "failed to translate key: %d", key); + return; + } + xkb_mod_mask_t mods = xkb_state_serialize_mods(seat->keyboard.state, + XKB_STATE_MODS_EFFECTIVE); + xkb_mod_index_t shift_idx = xkb_keymap_mod_get_index( + seat->keyboard.keymap, XKB_MOD_NAME_SHIFT); + bool shift = shift_idx != XKB_MOD_INVALID && (mods & (1 << shift_idx)); + + int nr_buttons = wl_list_length(&nag->buttons); + + switch (syms[0]) { + case XKB_KEY_Left: + case XKB_KEY_Right: + case XKB_KEY_Tab: { + if (nr_buttons <= 0) { + break; + } + int direction; + if (syms[0] == XKB_KEY_Left || (syms[0] == XKB_KEY_Tab && shift)) { + direction = 1; + } else { + direction = -1; + } + nag->selected_button += nr_buttons + direction; + nag->selected_button %= nr_buttons; + render_frame(nag); + close_pollfd(&nag->pollfds[FD_TIMER]); + break; + } + case XKB_KEY_Escape: + exit_status = LAB_EXIT_CANCELLED; + nag->run_display = false; + break; + case XKB_KEY_Return: + case XKB_KEY_KP_Enter: { + int idx = 0; + struct button *button; + wl_list_for_each(button, &nag->buttons, link) { + if (idx == nag->selected_button) { + button_execute(nag, button); + close_pollfd(&nag->pollfds[FD_TIMER]); + exit_status = idx; + break; + } + idx++; + } + break; + } + } +} + +static void +wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group) +{ + struct seat *seat = data; + + if (!seat->keyboard.state) { + wlr_log(WLR_ERROR, "xkb state unavailable"); + return; + } + + xkb_state_update_mask(seat->keyboard.state, mods_depressed, + mods_latched, mods_locked, 0, 0, group); +} + +static void +wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, + int32_t rate, int32_t delay) +{ +} + +static const struct wl_keyboard_listener keyboard_listener = { + .keymap = wl_keyboard_keymap, + .enter = wl_keyboard_enter, + .leave = wl_keyboard_leave, + .key = wl_keyboard_key, + .modifiers = wl_keyboard_modifiers, + .repeat_info = wl_keyboard_repeat_info, +}; + static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability caps) { struct seat *seat = data; bool cap_pointer = caps & WL_SEAT_CAPABILITY_POINTER; + bool cap_keyboard = caps & WL_SEAT_CAPABILITY_KEYBOARD; + if (cap_pointer && !seat->pointer.pointer) { seat->pointer.pointer = wl_seat_get_pointer(wl_seat); wl_pointer_add_listener(seat->pointer.pointer, @@ -953,6 +1142,15 @@ seat_handle_capabilities(void *data, struct wl_seat *wl_seat, wl_pointer_destroy(seat->pointer.pointer); seat->pointer.pointer = NULL; } + + if (cap_keyboard && !seat->keyboard.keyboard) { + seat->keyboard.keyboard = wl_seat_get_keyboard(wl_seat); + wl_keyboard_add_listener(seat->keyboard.keyboard, + &keyboard_listener, seat); + } else if (!cap_keyboard && seat->keyboard.keyboard) { + wl_keyboard_destroy(seat->keyboard.keyboard); + seat->keyboard.keyboard = NULL; + } } static void @@ -1075,7 +1273,7 @@ handle_global(void *data, struct wl_registry *registry, uint32_t name, } } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { nag->layer_shell = wl_registry_bind( - registry, name, &zwlr_layer_shell_v1_interface, 1); + registry, name, &zwlr_layer_shell_v1_interface, 4); } else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) { nag->cursor_shape_manager = wl_registry_bind( registry, name, &wp_cursor_shape_manager_v1_interface, 1); @@ -1170,6 +1368,8 @@ nag_setup(struct nag *nag) &layer_surface_listener, nag); zwlr_layer_surface_v1_set_anchor(nag->layer_surface, nag->conf->anchors); + zwlr_layer_surface_v1_set_keyboard_interactivity(nag->layer_surface, + nag->conf->keyboard_focus); wl_registry_destroy(registry); @@ -1250,6 +1450,7 @@ conf_init(struct conf *conf) | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; conf->layer = ZWLR_LAYER_SHELL_V1_LAYER_TOP; + conf->keyboard_focus = ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE; conf->bar_border_thickness = 2; conf->message_padding = 8; conf->details_border_thickness = 3; @@ -1357,6 +1558,7 @@ nag_parse_options(int argc, char **argv, struct nag *nag, {"debug", no_argument, NULL, 'd'}, {"edge", required_argument, NULL, 'e'}, {"layer", required_argument, NULL, 'y'}, + {"keyboard-focus", required_argument, NULL, 'k'}, {"font", required_argument, NULL, 'f'}, {"help", no_argument, NULL, 'h'}, {"detailed-message", no_argument, NULL, 'l'}, @@ -1395,6 +1597,8 @@ nag_parse_options(int argc, char **argv, struct nag *nag, " -e, --edge top|bottom Set the edge to use.\n" " -y, --layer overlay|top|bottom|background\n" " Set the layer to use.\n" + " -k, --keyboard-focus none|exclusive|on-demand|\n" + " Set the policy for keyboard focus.\n" " -f, --font Set the font to use.\n" " -h, --help Show help message and quit.\n" " -l, --detailed-message Read a detailed message from stdin.\n" @@ -1426,7 +1630,7 @@ nag_parse_options(int argc, char **argv, struct nag *nag, optind = 1; while (1) { - int c = getopt_long(argc, argv, "B:Z:c:de:y:f:hlL:m:o:s:t:vx", opts, NULL); + int c = getopt_long(argc, argv, "B:Z:c:de:y:k:f:hlL:m:o:s:t:vx", opts, NULL); if (c == -1) { break; } @@ -1480,6 +1684,23 @@ nag_parse_options(int argc, char **argv, struct nag *nag, return LAB_EXIT_FAILURE; } break; + case 'k': + if (strcmp(optarg, "none") == 0) { + conf->keyboard_focus = + ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE; + } else if (strcmp(optarg, "exclusive") == 0) { + conf->keyboard_focus = + ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE; + } else if (strcmp(optarg, "on-demand") == 0) { + conf->keyboard_focus = + ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND; + } else { + fprintf(stderr, "Invalid keyboard focus: %s\n" + "Usage: --keyboard-focus none|exclusive|on-demand\n", + optarg); + return LAB_EXIT_FAILURE; + } + break; case 'f': /* Font */ pango_font_description_free(conf->font_description); conf->font_description = pango_font_description_from_string(optarg); @@ -1622,6 +1843,14 @@ main(int argc, char **argv) wl_list_insert(nag.buttons.prev, &nag.details.button_details->link); } + int nr_buttons = wl_list_length(&nag.buttons); + if (conf.keyboard_focus && nr_buttons > 0) { + /* select the leftmost button */ + nag.selected_button = nr_buttons - 1; + } else { + nag.selected_button = -1; + } + wlr_log(WLR_DEBUG, "Output: %s", nag.conf->output); wlr_log(WLR_DEBUG, "Anchors: %lu", (unsigned long)nag.conf->anchors); wlr_log(WLR_DEBUG, "Message: %s", nag.message); diff --git a/clients/meson.build b/clients/meson.build index 54b92db8..467bc035 100644 --- a/clients/meson.build +++ b/clients/meson.build @@ -49,6 +49,7 @@ executable( wlroots, server_protos, epoll_dep, + xkbcommon, ], include_directories: [labwc_inc], install: true, diff --git a/docs/labnag.1.scd b/docs/labnag.1.scd index c8aa4d09..a2a36c10 100644 --- a/docs/labnag.1.scd +++ b/docs/labnag.1.scd @@ -31,6 +31,9 @@ _labnag_ [options...] *-y, --layer* overlay|top|bottom|background Set the layer to use. +*-k, --keyboard-focus none|exclusive|on-demand* + Set the policy for keyboard focus. + *-f, --font* Set the font to use. From 953249249cfb20952af48df2b19aba3cde159a5c Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 13 Oct 2025 18:58:23 +0900 Subject: [PATCH 234/248] rcxml: call labnag with --keyboard-focus on-demand by default --- docs/labwc-config.5.scd | 1 + src/config/rcxml.c | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index 20149bee..995be07c 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -285,6 +285,7 @@ this is for compatibility with Openbox. --button-text-color '%t' \\ --border-bottom-size 1 \\ --button-border-size 3 \\ + --keyboard-focus on-demand \\ --timeout 0 ``` diff --git a/src/config/rcxml.c b/src/config/rcxml.c index be49eaeb..6d0caf80 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -1645,6 +1645,7 @@ post_processing(void) "--button-text-color '%t' " "--border-bottom-size 1 " "--button-border-size 3 " + "--keyboard-focus on-demand " "--timeout 0"); } if (!rc.fallback_app_icon_name) { From 77a11568a7b9c8d42e29102416c040d4dfc83c32 Mon Sep 17 00:00:00 2001 From: Tobias Bengfort Date: Thu, 9 Oct 2025 08:59:40 +0200 Subject: [PATCH 235/248] implement is_modal_dialog() for xdg shell --- src/server.c | 1 + src/xdg.c | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/server.c b/src/server.c index c39649b8..abaaaf0b 100644 --- a/src/server.c +++ b/src/server.c @@ -276,6 +276,7 @@ allow_for_sandbox(const struct wlr_security_context_v1_state *security_state, "zxdg_importer_v1", "zxdg_importer_v2", "xdg_toplevel_icon_manager_v1", + "xdg_dialog_v1", /* plus */ "wp_alpha_modifier_v1", "wp_linux_drm_syncobj_manager_v1", diff --git a/src/xdg.c b/src/xdg.c index f877e7ba..83ab9bb9 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include "buffer.h" @@ -617,6 +618,18 @@ xdg_toplevel_view_append_children(struct view *self, struct wl_array *children) } } +static bool +xdg_toplevel_view_is_modal_dialog(struct view *view) +{ + struct wlr_xdg_toplevel *toplevel = xdg_toplevel_from_view(view); + struct wlr_xdg_dialog_v1 *dialog = + wlr_xdg_dialog_v1_try_from_wlr_xdg_toplevel(toplevel); + if (!dialog) { + return false; + } + return dialog->modal; +} + static void xdg_toplevel_view_set_activated(struct view *view, bool activated) { @@ -871,6 +884,7 @@ static const struct view_impl xdg_toplevel_view_impl = { .minimize = xdg_toplevel_view_minimize, .get_root = xdg_toplevel_view_get_root, .append_children = xdg_toplevel_view_append_children, + .is_modal_dialog = xdg_toplevel_view_is_modal_dialog, .get_size_hints = xdg_toplevel_view_get_size_hints, .contains_window_type = xdg_toplevel_view_contains_window_type, .get_pid = xdg_view_get_pid, @@ -1116,6 +1130,8 @@ xdg_shell_init(struct server *server) server->xdg_toplevel_icon_set_icon.notify = handle_xdg_toplevel_icon_set_icon; wl_signal_add(&server->xdg_toplevel_icon_manager->events.set_icon, &server->xdg_toplevel_icon_set_icon); + + wlr_xdg_wm_dialog_v1_create(server->wl_display, 1); } void From c78a0fe1b4e34ed1f0e32de0461330e64d7988d4 Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Mon, 7 Jul 2025 00:53:36 +0300 Subject: [PATCH 236/248] windowswitcher: show 's' as "state" for shaded views While at it sorted the code to show 'm' before 's' and 's' before 'M' - from the least visible to the most visible state. --- docs/labwc-config.5.scd | 6 +++--- src/osd/osd-field.c | 9 ++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index 995be07c..ca167b62 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -401,9 +401,9 @@ this is for compatibility with Openbox. fields are: - 'B' - shell type, values [xwayland|xdg-shell] - 'b' - shell type (short form), values [X|W] - - 'S' - state of window, values [M|m|F] (3 spaces allocated) - (maximized, minimized, fullscreen) - - 's' - state of window (short form), values [M|m|F] (1 space) + - 'S' - state of window, values [m|s|M|F] (4 spaces allocated) + (minimized, shaded, maximized, fullscreen) + - 's' - state of window (short form), values [m|s|M|F] (1 space) - 'I' - wm-class/app-id - 'i' - wm-class/app-id trimmed, remove "org." if available - 'n' - desktop entry/file application name, falls back to diff --git a/src/osd/osd-field.c b/src/osd/osd-field.c index 9852b11f..01c4e48b 100644 --- a/src/osd/osd-field.c +++ b/src/osd/osd-field.c @@ -111,10 +111,12 @@ static void field_set_win_state(struct buf *buf, struct view *view, const char *format) { /* custom type conversion-specifier: s */ - if (view->maximized) { - buf_add(buf, "M"); - } else if (view->minimized) { + if (view->minimized) { buf_add(buf, "m"); + } else if (view->shaded) { + buf_add(buf, "s"); + } else if (view->maximized) { + buf_add(buf, "M"); } else if (view->fullscreen) { buf_add(buf, "F"); } else { @@ -127,6 +129,7 @@ field_set_win_state_all(struct buf *buf, struct view *view, const char *format) { /* custom type conversion-specifier: S */ buf_add(buf, view->minimized ? "m" : " "); + buf_add(buf, view->shaded ? "s" : " "); buf_add(buf, view->maximized ? "M" : " "); buf_add(buf, view->fullscreen ? "F" : " "); /* TODO: add always-on-top and omnipresent ? */ From 89fab2d4499d7e69fcfac554f98c30b31e1e4466 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Tue, 14 Oct 2025 13:44:44 +0900 Subject: [PATCH 237/248] theme: replace THEME_(IN)ACTIVE with SSD_(IN)ACTIVE --- include/theme.h | 6 +- src/ssd/ssd-shadow.c | 4 +- src/ssd/ssd.c | 2 +- src/theme.c | 147 ++++++++++++++++++++++--------------------- 4 files changed, 79 insertions(+), 80 deletions(-) diff --git a/include/theme.h b/include/theme.h index e7e13d66..58ea3e13 100644 --- a/include/theme.h +++ b/include/theme.h @@ -83,7 +83,7 @@ struct theme { /* * Themes/textures for each active/inactive window. Indexed by - * THEME_INACTIVE and THEME_ACTIVE. + * ssd_active_state. */ struct { /* title background pattern (solid or gradient) */ @@ -212,10 +212,6 @@ struct theme { int mag_border_width; }; -/* TODO: replace with enum ssd_active_state */ -#define THEME_INACTIVE 0 -#define THEME_ACTIVE 1 - struct server; /** diff --git a/src/ssd/ssd-shadow.c b/src/ssd/ssd-shadow.c index 53c6631d..2ead17ce 100644 --- a/src/ssd/ssd-shadow.c +++ b/src/ssd/ssd-shadow.c @@ -259,8 +259,8 @@ ssd_shadow_update(struct ssd *ssd) bool maximized = view->maximized == VIEW_AXIS_BOTH; bool tiled_shadows = false; if (rc.shadows_on_tiled) { - if (rc.gap >= theme->window[THEME_ACTIVE].shadow_size - && rc.gap >= theme->window[THEME_INACTIVE].shadow_size) { + if (rc.gap >= theme->window[SSD_ACTIVE].shadow_size + && rc.gap >= theme->window[SSD_INACTIVE].shadow_size) { tiled_shadows = true; } else { wlr_log(WLR_INFO, "gap size < shadow_size, ignore rc.shadows_ontiled"); diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index 9ec7fa3b..d1381c1a 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -348,7 +348,7 @@ ssd_enable_keybind_inhibit_indicator(struct ssd *ssd, bool enable) float *color = enable ? rc.theme->window_toggled_keybinds_color - : rc.theme->window[THEME_ACTIVE].border_color; + : rc.theme->window[SSD_ACTIVE].border_color; wlr_scene_rect_set_color(ssd->border.subtrees[SSD_ACTIVE].top, color); } diff --git a/src/theme.c b/src/theme.c index acdbc16b..f6684ee9 100644 --- a/src/theme.c +++ b/src/theme.c @@ -162,7 +162,7 @@ get_button_filename(char *buf, size_t len, const char *name, const char *postfix } static void -load_button(struct theme *theme, struct button *b, int active) +load_button(struct theme *theme, struct button *b, enum ssd_active_state active) { struct lab_img *(*button_imgs)[LAB_BS_ALL + 1] = theme->window[active].button_imgs; @@ -374,8 +374,8 @@ load_buttons(struct theme *theme) for (size_t i = 0; i < ARRAY_SIZE(buttons); ++i) { struct button *b = &buttons[i]; - load_button(theme, b, THEME_INACTIVE); - load_button(theme, b, THEME_ACTIVE); + load_button(theme, b, SSD_INACTIVE); + load_button(theme, b, SSD_ACTIVE); } } @@ -537,24 +537,24 @@ theme_builtin(struct theme *theme, struct server *server) theme->window_titlebar_padding_height = 0; theme->window_titlebar_padding_width = 0; - parse_hexstr("#aaaaaa", theme->window[THEME_ACTIVE].border_color); - parse_hexstr("#aaaaaa", theme->window[THEME_INACTIVE].border_color); + parse_hexstr("#aaaaaa", theme->window[SSD_ACTIVE].border_color); + parse_hexstr("#aaaaaa", theme->window[SSD_INACTIVE].border_color); parse_hexstr("#ff0000", theme->window_toggled_keybinds_color); - theme->window[THEME_ACTIVE].title_bg.gradient = LAB_GRADIENT_NONE; - theme->window[THEME_INACTIVE].title_bg.gradient = LAB_GRADIENT_NONE; - parse_hexstr("#e1dedb", theme->window[THEME_ACTIVE].title_bg.color); - parse_hexstr("#f6f5f4", theme->window[THEME_INACTIVE].title_bg.color); - theme->window[THEME_ACTIVE].title_bg.color_split_to[0] = FLT_MIN; - theme->window[THEME_INACTIVE].title_bg.color_split_to[0] = FLT_MIN; - theme->window[THEME_ACTIVE].title_bg.color_to[0] = FLT_MIN; - theme->window[THEME_INACTIVE].title_bg.color_to[0] = FLT_MIN; - theme->window[THEME_ACTIVE].title_bg.color_to_split_to[0] = FLT_MIN; - theme->window[THEME_INACTIVE].title_bg.color_to_split_to[0] = FLT_MIN; + theme->window[SSD_ACTIVE].title_bg.gradient = LAB_GRADIENT_NONE; + theme->window[SSD_INACTIVE].title_bg.gradient = LAB_GRADIENT_NONE; + parse_hexstr("#e1dedb", theme->window[SSD_ACTIVE].title_bg.color); + parse_hexstr("#f6f5f4", theme->window[SSD_INACTIVE].title_bg.color); + theme->window[SSD_ACTIVE].title_bg.color_split_to[0] = FLT_MIN; + theme->window[SSD_INACTIVE].title_bg.color_split_to[0] = FLT_MIN; + theme->window[SSD_ACTIVE].title_bg.color_to[0] = FLT_MIN; + theme->window[SSD_INACTIVE].title_bg.color_to[0] = FLT_MIN; + theme->window[SSD_ACTIVE].title_bg.color_to_split_to[0] = FLT_MIN; + theme->window[SSD_INACTIVE].title_bg.color_to_split_to[0] = FLT_MIN; - parse_hexstr("#000000", theme->window[THEME_ACTIVE].label_text_color); - parse_hexstr("#000000", theme->window[THEME_INACTIVE].label_text_color); + parse_hexstr("#000000", theme->window[SSD_ACTIVE].label_text_color); + parse_hexstr("#000000", theme->window[SSD_INACTIVE].label_text_color); theme->window_label_text_justify = parse_justification("Center"); theme->window_button_width = 26; @@ -565,15 +565,15 @@ theme_builtin(struct theme *theme, struct server *server) for (enum lab_node_type type = LAB_NODE_BUTTON_FIRST; type <= LAB_NODE_BUTTON_LAST; type++) { parse_hexstr("#000000", - theme->window[THEME_INACTIVE].button_colors[type]); + theme->window[SSD_INACTIVE].button_colors[type]); parse_hexstr("#000000", - theme->window[THEME_ACTIVE].button_colors[type]); + theme->window[SSD_ACTIVE].button_colors[type]); } - theme->window[THEME_ACTIVE].shadow_size = 60; - theme->window[THEME_INACTIVE].shadow_size = 40; - parse_hexstr("#00000060", theme->window[THEME_ACTIVE].shadow_color); - parse_hexstr("#00000040", theme->window[THEME_INACTIVE].shadow_color); + theme->window[SSD_ACTIVE].shadow_size = 60; + theme->window[SSD_INACTIVE].shadow_size = 40; + parse_hexstr("#00000060", theme->window[SSD_ACTIVE].shadow_color); + parse_hexstr("#00000040", theme->window[SSD_INACTIVE].shadow_color); theme->menu_overlap_x = 0; theme->menu_overlap_y = 0; @@ -711,15 +711,15 @@ entry(struct theme *theme, const char *key, const char *value) } if (match_glob(key, "window.active.border.color")) { - parse_color(value, theme->window[THEME_ACTIVE].border_color); + parse_color(value, theme->window[SSD_ACTIVE].border_color); } if (match_glob(key, "window.inactive.border.color")) { - parse_color(value, theme->window[THEME_INACTIVE].border_color); + parse_color(value, theme->window[SSD_INACTIVE].border_color); } /* border.color is obsolete, but handled for backward compatibility */ if (match_glob(key, "border.color")) { - parse_color(value, theme->window[THEME_ACTIVE].border_color); - parse_color(value, theme->window[THEME_INACTIVE].border_color); + parse_color(value, theme->window[SSD_ACTIVE].border_color); + parse_color(value, theme->window[SSD_INACTIVE].border_color); } if (match_glob(key, "window.active.indicator.toggled-keybind.color")) { @@ -727,41 +727,41 @@ entry(struct theme *theme, const char *key, const char *value) } if (match_glob(key, "window.active.title.bg")) { - theme->window[THEME_ACTIVE].title_bg.gradient = parse_gradient(value); + theme->window[SSD_ACTIVE].title_bg.gradient = parse_gradient(value); } if (match_glob(key, "window.inactive.title.bg")) { - theme->window[THEME_INACTIVE].title_bg.gradient = parse_gradient(value); + theme->window[SSD_INACTIVE].title_bg.gradient = parse_gradient(value); } if (match_glob(key, "window.active.title.bg.color")) { - parse_color(value, theme->window[THEME_ACTIVE].title_bg.color); + parse_color(value, theme->window[SSD_ACTIVE].title_bg.color); } if (match_glob(key, "window.inactive.title.bg.color")) { - parse_color(value, theme->window[THEME_INACTIVE].title_bg.color); + parse_color(value, theme->window[SSD_INACTIVE].title_bg.color); } if (match_glob(key, "window.active.title.bg.color.splitTo")) { - parse_color(value, theme->window[THEME_ACTIVE].title_bg.color_split_to); + parse_color(value, theme->window[SSD_ACTIVE].title_bg.color_split_to); } if (match_glob(key, "window.inactive.title.bg.color.splitTo")) { - parse_color(value, theme->window[THEME_INACTIVE].title_bg.color_split_to); + parse_color(value, theme->window[SSD_INACTIVE].title_bg.color_split_to); } if (match_glob(key, "window.active.title.bg.colorTo")) { - parse_color(value, theme->window[THEME_ACTIVE].title_bg.color_to); + parse_color(value, theme->window[SSD_ACTIVE].title_bg.color_to); } if (match_glob(key, "window.inactive.title.bg.colorTo")) { - parse_color(value, theme->window[THEME_INACTIVE].title_bg.color_to); + parse_color(value, theme->window[SSD_INACTIVE].title_bg.color_to); } if (match_glob(key, "window.active.title.bg.colorTo.splitTo")) { - parse_color(value, theme->window[THEME_ACTIVE].title_bg.color_to_split_to); + parse_color(value, theme->window[SSD_ACTIVE].title_bg.color_to_split_to); } if (match_glob(key, "window.inactive.title.bg.colorTo.splitTo")) { - parse_color(value, theme->window[THEME_INACTIVE].title_bg.color_to_split_to); + parse_color(value, theme->window[SSD_INACTIVE].title_bg.color_to_split_to); } if (match_glob(key, "window.active.label.text.color")) { - parse_color(value, theme->window[THEME_ACTIVE].label_text_color); + parse_color(value, theme->window[SSD_ACTIVE].label_text_color); } if (match_glob(key, "window.inactive.label.text.color")) { - parse_color(value, theme->window[THEME_INACTIVE].label_text_color); + parse_color(value, theme->window[SSD_INACTIVE].label_text_color); } if (match_glob(key, "window.label.text.justify")) { theme->window_label_text_justify = parse_justification(value); @@ -797,85 +797,85 @@ entry(struct theme *theme, const char *key, const char *value) for (enum lab_node_type type = LAB_NODE_BUTTON_FIRST; type <= LAB_NODE_BUTTON_LAST; type++) { parse_color(value, - theme->window[THEME_ACTIVE].button_colors[type]); + theme->window[SSD_ACTIVE].button_colors[type]); } } if (match_glob(key, "window.inactive.button.unpressed.image.color")) { for (enum lab_node_type type = LAB_NODE_BUTTON_FIRST; type <= LAB_NODE_BUTTON_LAST; type++) { parse_color(value, - theme->window[THEME_INACTIVE].button_colors[type]); + theme->window[SSD_INACTIVE].button_colors[type]); } } /* individual buttons */ if (match_glob(key, "window.active.button.menu.unpressed.image.color")) { - parse_color(value, theme->window[THEME_ACTIVE] + parse_color(value, theme->window[SSD_ACTIVE] .button_colors[LAB_NODE_BUTTON_WINDOW_MENU]); - parse_color(value, theme->window[THEME_ACTIVE] + parse_color(value, theme->window[SSD_ACTIVE] .button_colors[LAB_NODE_BUTTON_WINDOW_ICON]); } if (match_glob(key, "window.active.button.iconify.unpressed.image.color")) { - parse_color(value, theme->window[THEME_ACTIVE] + parse_color(value, theme->window[SSD_ACTIVE] .button_colors[LAB_NODE_BUTTON_ICONIFY]); } if (match_glob(key, "window.active.button.max.unpressed.image.color")) { - parse_color(value, theme->window[THEME_ACTIVE] + parse_color(value, theme->window[SSD_ACTIVE] .button_colors[LAB_NODE_BUTTON_MAXIMIZE]); } if (match_glob(key, "window.active.button.shade.unpressed.image.color")) { - parse_color(value, theme->window[THEME_ACTIVE] + parse_color(value, theme->window[SSD_ACTIVE] .button_colors[LAB_NODE_BUTTON_SHADE]); } if (match_glob(key, "window.active.button.desk.unpressed.image.color")) { - parse_color(value, theme->window[THEME_ACTIVE] + parse_color(value, theme->window[SSD_ACTIVE] .button_colors[LAB_NODE_BUTTON_OMNIPRESENT]); } if (match_glob(key, "window.active.button.close.unpressed.image.color")) { - parse_color(value, theme->window[THEME_ACTIVE] + parse_color(value, theme->window[SSD_ACTIVE] .button_colors[LAB_NODE_BUTTON_CLOSE]); } if (match_glob(key, "window.inactive.button.menu.unpressed.image.color")) { - parse_color(value, theme->window[THEME_INACTIVE] + parse_color(value, theme->window[SSD_INACTIVE] .button_colors[LAB_NODE_BUTTON_WINDOW_MENU]); - parse_color(value, theme->window[THEME_INACTIVE] + parse_color(value, theme->window[SSD_INACTIVE] .button_colors[LAB_NODE_BUTTON_WINDOW_ICON]); } if (match_glob(key, "window.inactive.button.iconify.unpressed.image.color")) { - parse_color(value, theme->window[THEME_INACTIVE] + parse_color(value, theme->window[SSD_INACTIVE] .button_colors[LAB_NODE_BUTTON_ICONIFY]); } if (match_glob(key, "window.inactive.button.max.unpressed.image.color")) { - parse_color(value, theme->window[THEME_INACTIVE] + parse_color(value, theme->window[SSD_INACTIVE] .button_colors[LAB_NODE_BUTTON_MAXIMIZE]); } if (match_glob(key, "window.inactive.button.shade.unpressed.image.color")) { - parse_color(value, theme->window[THEME_INACTIVE] + parse_color(value, theme->window[SSD_INACTIVE] .button_colors[LAB_NODE_BUTTON_SHADE]); } if (match_glob(key, "window.inactive.button.desk.unpressed.image.color")) { - parse_color(value, theme->window[THEME_INACTIVE] + parse_color(value, theme->window[SSD_INACTIVE] .button_colors[LAB_NODE_BUTTON_OMNIPRESENT]); } if (match_glob(key, "window.inactive.button.close.unpressed.image.color")) { - parse_color(value, theme->window[THEME_INACTIVE] + parse_color(value, theme->window[SSD_INACTIVE] .button_colors[LAB_NODE_BUTTON_CLOSE]); } /* window drop-shadows */ if (match_glob(key, "window.active.shadow.size")) { - theme->window[THEME_ACTIVE].shadow_size = get_int_if_positive( + theme->window[SSD_ACTIVE].shadow_size = get_int_if_positive( value, "window.active.shadow.size"); } if (match_glob(key, "window.inactive.shadow.size")) { - theme->window[THEME_INACTIVE].shadow_size = get_int_if_positive( + theme->window[SSD_INACTIVE].shadow_size = get_int_if_positive( value, "window.inactive.shadow.size"); } if (match_glob(key, "window.active.shadow.color")) { - parse_color(value, theme->window[THEME_ACTIVE].shadow_color); + parse_color(value, theme->window[SSD_ACTIVE].shadow_color); } if (match_glob(key, "window.inactive.shadow.color")) { - parse_color(value, theme->window[THEME_INACTIVE].shadow_color); + parse_color(value, theme->window[SSD_INACTIVE].shadow_color); } if (match_glob(key, "menu.overlap.x")) { @@ -1396,7 +1396,8 @@ create_titlebar_fill(cairo_pattern_t *pattern, int height) static void create_backgrounds(struct theme *theme) { - for (int active = THEME_INACTIVE; active <= THEME_ACTIVE; active++) { + enum ssd_active_state active; + FOR_EACH_ACTIVE_STATE(active) { theme->window[active].titlebar_pattern = create_titlebar_pattern( &theme->window[active].title_bg, theme->titlebar_height); @@ -1418,7 +1419,8 @@ create_corners(struct theme *theme) .height = theme->titlebar_height + theme->border_width, }; - for (int active = THEME_INACTIVE; active <= THEME_ACTIVE; active++) { + enum ssd_active_state active; + FOR_EACH_ACTIVE_STATE(active) { struct rounded_corner_ctx ctx = { .box = &box, .radius = rc.corner_radius, @@ -1548,7 +1550,7 @@ shadow_corner_gradient(struct lab_data_buffer *buffer, int visible_size, } static void -create_shadow(struct theme *theme, int active) +create_shadow(struct theme *theme, enum ssd_active_state active) { /* Size of shadow visible extending beyond the window */ int visible_size = theme->window[active].shadow_size; @@ -1590,8 +1592,8 @@ create_shadow(struct theme *theme, int active) static void create_shadows(struct theme *theme) { - create_shadow(theme, THEME_INACTIVE); - create_shadow(theme, THEME_ACTIVE); + create_shadow(theme, SSD_INACTIVE); + create_shadow(theme, SSD_ACTIVE); } static void @@ -1676,8 +1678,8 @@ post_processing(struct theme *theme) theme->titlebar_height = get_titlebar_height(theme); - fill_background_colors(&theme->window[THEME_INACTIVE].title_bg); - fill_background_colors(&theme->window[THEME_ACTIVE].title_bg); + fill_background_colors(&theme->window[SSD_INACTIVE].title_bg); + fill_background_colors(&theme->window[SSD_ACTIVE].title_bg); theme->menu_item_height = font_height(&rc.font_menuitem) + 2 * theme->menu_items_padding_y; @@ -1722,14 +1724,14 @@ post_processing(struct theme *theme) } if (theme->menu_border_color[0] == FLT_MIN) { memcpy(theme->menu_border_color, - theme->window[THEME_ACTIVE].border_color, + theme->window[SSD_ACTIVE].border_color, sizeof(theme->menu_border_color)); } /* Inherit OSD settings if not set */ if (theme->osd_bg_color[0] == FLT_MIN) { memcpy(theme->osd_bg_color, - theme->window[THEME_ACTIVE].title_bg.color, + theme->window[SSD_ACTIVE].title_bg.color, sizeof(theme->osd_bg_color)); } if (theme->osd_border_width == INT_MIN) { @@ -1737,7 +1739,7 @@ post_processing(struct theme *theme) } if (theme->osd_label_text_color[0] == FLT_MIN) { memcpy(theme->osd_label_text_color, - theme->window[THEME_ACTIVE].label_text_color, + theme->window[SSD_ACTIVE].label_text_color, sizeof(theme->osd_label_text_color)); } if (theme->osd_border_color[0] == FLT_MIN) { @@ -1854,14 +1856,15 @@ theme_finish(struct theme *theme) type <= LAB_NODE_BUTTON_LAST; type++) { for (uint8_t state_set = LAB_BS_DEFAULT; state_set <= LAB_BS_ALL; state_set++) { - destroy_img(&theme->window[THEME_INACTIVE] + destroy_img(&theme->window[SSD_INACTIVE] .button_imgs[type][state_set]); - destroy_img(&theme->window[THEME_ACTIVE] + destroy_img(&theme->window[SSD_ACTIVE] .button_imgs[type][state_set]); } } - for (int active = THEME_INACTIVE; active <= THEME_ACTIVE; active++) { + enum ssd_active_state active; + FOR_EACH_ACTIVE_STATE(active) { zfree_pattern(theme->window[active].titlebar_pattern); zdrop(&theme->window[active].titlebar_fill); zdrop(&theme->window[active].corner_top_left_normal); From eebf5b3e4edfb66e4d1e55e0f4c217887d36b233 Mon Sep 17 00:00:00 2001 From: Weblate Date: Sat, 11 Oct 2025 22:01:24 +0200 Subject: [PATCH 238/248] Translation updates from weblate Co-authored-by: alvaroelpob Co-authored-by: p-bo Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/ca/ Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/cs/ Translation: Labwc/labwc --- po/ca.po | 9 +++++---- po/cs.po | 23 +++++++++++++---------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/po/ca.po b/po/ca.po index f9c6975e..7bc442bc 100644 --- a/po/ca.po +++ b/po/ca.po @@ -8,9 +8,10 @@ msgstr "" "Project-Id-Version: labwc\n" "Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" "POT-Creation-Date: 2024-09-19 21:09+1000\n" -"PO-Revision-Date: 2025-03-29 11:25+0000\n" -"Last-Translator: Davidmp \n" -"Language-Team: Catalan \n" +"PO-Revision-Date: 2025-10-11 20:01+0000\n" +"Last-Translator: alvaroelpob \n" +"Language-Team: Catalan \n" "Language: ca\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -24,7 +25,7 @@ msgstr "Ves-hi..." #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "" +msgstr "Terminal" #: src/menu/menu.c:1040 msgid "Reconfigure" diff --git a/po/cs.po b/po/cs.po index 27fe6a68..11ff25ce 100644 --- a/po/cs.po +++ b/po/cs.po @@ -8,29 +8,32 @@ msgstr "" "Project-Id-Version: labwc\n" "Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" "POT-Creation-Date: 2024-09-19 21:09+1000\n" -"PO-Revision-Date: 2024-03-02 02:00+0100\n" -"Last-Translator: zenobit \n" -"Language-Team: Czech \n" +"PO-Revision-Date: 2025-10-11 20:01+0000\n" +"Last-Translator: p-bo \n" +"Language-Team: Czech \n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" +"X-Generator: Weblate 4.2.1\n" #: src/menu/menu.c:1016 msgid "Go there..." -msgstr "" +msgstr "Přejít tam..." #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "" +msgstr "Terminál" #: src/menu/menu.c:1040 msgid "Reconfigure" -msgstr "Překonfigurovat" +msgstr "Přenastavit" #: src/menu/menu.c:1042 msgid "Exit" -msgstr "Odejít" +msgstr "Ukončit" #: src/menu/menu.c:1056 msgid "Minimize" @@ -46,7 +49,7 @@ msgstr "Na celou obrazovku" #: src/menu/menu.c:1062 msgid "Roll Up/Down" -msgstr "Rolovat nahoru/dolů" +msgstr "Posouvat nahoru/dolů" #: src/menu/menu.c:1064 msgid "Decorations" @@ -66,11 +69,11 @@ msgstr "Posunout doprava" #: src/menu/menu.c:1083 msgid "Always on Visible Workspace" -msgstr "Vždy na viditelné Pracovní Ploše" +msgstr "Vždy na viditelné Pracovní ploše" #: src/menu/menu.c:1086 msgid "Workspace" -msgstr "Pracovní Plocha" +msgstr "Pracovní plocha" #: src/menu/menu.c:1089 msgid "Close" From 7f67b9c8664b57eefb70644b2c17ceb378acd49e Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Wed, 15 Oct 2025 16:36:01 +0900 Subject: [PATCH 239/248] Don't remove newlines when parsing config, menu and XBM Removing newlines in rc.xml and menu.xml caused parser error with following content: ...though it is a valid XML. Let's not do that. I moved `grab_file()` to `buf.c` and renamed it to `buf_from_file()`, because it now directly touches `struct buf` and I don't like having a source file only for one function. --- include/common/buf.h | 7 +++++++ include/common/grab-file.h | 20 -------------------- src/common/buf.c | 35 +++++++++++++++++++++++++++++++++++ src/common/grab-file.c | 36 ------------------------------------ src/common/meson.build | 1 - src/config/rcxml.c | 16 ++-------------- src/img/img-xbm.c | 4 ++-- src/menu/menu.c | 32 ++++---------------------------- 8 files changed, 50 insertions(+), 101 deletions(-) delete mode 100644 include/common/grab-file.h delete mode 100644 src/common/grab-file.c diff --git a/include/common/buf.h b/include/common/buf.h index 24158630..1298cac2 100644 --- a/include/common/buf.h +++ b/include/common/buf.h @@ -109,4 +109,11 @@ void buf_reset(struct buf *s); */ void buf_move(struct buf *dst, struct buf *src); +/** + * buf_from_file - read file into memory buffer + * @filename: file to read + * Free returned buffer with buf_reset(). + */ +struct buf buf_from_file(const char *filename); + #endif /* LABWC_BUF_H */ diff --git a/include/common/grab-file.h b/include/common/grab-file.h deleted file mode 100644 index 6a6de56d..00000000 --- a/include/common/grab-file.h +++ /dev/null @@ -1,20 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Read file into memory - * - * Copyright Johan Malm 2020 - */ - -#ifndef LABWC_GRAB_FILE_H -#define LABWC_GRAB_FILE_H - -#include "common/buf.h" - -/** - * grab_file - read file into memory buffer - * @filename: file to read - * Free returned buffer with buf_reset(). - */ -struct buf grab_file(const char *filename); - -#endif /* LABWC_GRAB_FILE_H */ diff --git a/src/common/buf.c b/src/common/buf.c index bd8e82d0..c141b62e 100644 --- a/src/common/buf.c +++ b/src/common/buf.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "common/macros.h" #include "common/mem.h" #include "common/string-helpers.h" @@ -203,3 +204,37 @@ buf_move(struct buf *dst, struct buf *src) *dst = *src; *src = BUF_INIT; } + +struct buf +buf_from_file(const char *filename) +{ + struct buf buf = BUF_INIT; + FILE *stream = fopen(filename, "r"); + if (!stream) { + return buf; + } + + if (fseek(stream, 0, SEEK_END) == -1) { + wlr_log_errno(WLR_ERROR, "fseek(%s)", filename); + fclose(stream); + return buf; + } + long size = ftell(stream); + if (size == -1) { + wlr_log_errno(WLR_ERROR, "ftell(%s)", filename); + fclose(stream); + return buf; + } + rewind(stream); + + buf_expand(&buf, size + 1); + if (fread(buf.data, 1, size, stream) == (size_t)size) { + buf.len = size; + buf.data[size] = '\0'; + } else { + wlr_log_errno(WLR_ERROR, "fread(%s)", filename); + buf_reset(&buf); + } + fclose(stream); + return buf; +} diff --git a/src/common/grab-file.c b/src/common/grab-file.c deleted file mode 100644 index 9c90045c..00000000 --- a/src/common/grab-file.c +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Read file into memory - * - * Copyright Johan Malm 2020 - */ - -#define _POSIX_C_SOURCE 200809L -#include "common/grab-file.h" -#include "common/buf.h" - -#include -#include -#include - -struct buf -grab_file(const char *filename) -{ - char *line = NULL; - size_t len = 0; - FILE *stream = fopen(filename, "r"); - if (!stream) { - return BUF_INIT; - } - struct buf buffer = BUF_INIT; - while ((getline(&line, &len, stream) != -1)) { - char *p = strrchr(line, '\n'); - if (p) { - *p = '\0'; - } - buf_add(&buffer, line); - } - free(line); - fclose(stream); - return buffer; -} diff --git a/src/common/meson.build b/src/common/meson.build index 39b4d4b4..4cf52023 100644 --- a/src/common/meson.build +++ b/src/common/meson.build @@ -6,7 +6,6 @@ labwc_sources += files( 'fd-util.c', 'file-helpers.c', 'font.c', - 'grab-file.c', 'graphic-helpers.c', 'lab-scene-rect.c', 'match.c', diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 6d0caf80..a966577c 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -1872,25 +1872,13 @@ rcxml_read(const char *filename) */ for (struct wl_list *elm = iter(&paths); elm != &paths; elm = iter(elm)) { struct path *path = wl_container_of(elm, path, link); - FILE *stream = fopen(path->string, "r"); - if (!stream) { + struct buf b = buf_from_file(path->string); + if (!b.len) { continue; } wlr_log(WLR_INFO, "read config file %s", path->string); - struct buf b = BUF_INIT; - char *line = NULL; - size_t len = 0; - while (getline(&line, &len, stream) != -1) { - char *p = strrchr(line, '\n'); - if (p) { - *p = '\0'; - } - buf_add(&b, line); - } - zfree(line); - fclose(stream); rcxml_parse_xml(&b); buf_reset(&b); if (!should_merge_config) { diff --git a/src/img/img-xbm.c b/src/img/img-xbm.c index 6ecbbc9a..91269f64 100644 --- a/src/img/img-xbm.c +++ b/src/img/img-xbm.c @@ -12,7 +12,7 @@ #include #include #include -#include "common/grab-file.h" +#include "common/buf.h" #include "common/mem.h" #include "common/string-helpers.h" #include "buffer.h" @@ -273,7 +273,7 @@ img_xbm_load(const char *filename, float *rgba) uint32_t color = argb32(rgba); /* Read file into memory as it's easier to tokenize that way */ - struct buf token_buf = grab_file(filename); + struct buf token_buf = buf_from_file(filename); if (token_buf.len) { struct token *tokens = tokenize_xbm(token_buf.data); pixmap = parse_xbm_tokens(tokens, color); diff --git a/src/menu/menu.c b/src/menu/menu.c index 3770e7f6..bc0fdcad 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -679,30 +679,6 @@ parse_buf(struct server *server, struct menu *parent, struct buf *buf) return true; } -/* - * @stream can come from either of the following: - * - fopen() in the case of reading a file such as menu.xml - * - popen() when processing pipemenus - */ -static void -parse_stream(struct server *server, FILE *stream) -{ - char *line = NULL; - size_t len = 0; - struct buf b = BUF_INIT; - - while (getline(&line, &len, stream) != -1) { - char *p = strrchr(line, '\n'); - if (p) { - *p = '\0'; - } - buf_add(&b, line); - } - free(line); - parse_buf(server, NULL, &b); - buf_reset(&b); -} - static void parse_xml(const char *filename, struct server *server) { @@ -715,13 +691,13 @@ parse_xml(const char *filename, struct server *server) for (struct wl_list *elm = iter(&paths); elm != &paths; elm = iter(elm)) { struct path *path = wl_container_of(elm, path, link); - FILE *stream = fopen(path->string, "r"); - if (!stream) { + struct buf buf = buf_from_file(path->string); + if (!buf.len) { continue; } wlr_log(WLR_INFO, "read menu file %s", path->string); - parse_stream(server, stream); - fclose(stream); + parse_buf(server, /*parent*/ NULL, &buf); + buf_reset(&buf); if (!should_merge_config) { break; } From be44991def676110bb52dce036f0721e22ea9fef Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Thu, 16 Oct 2025 06:27:20 +0900 Subject: [PATCH 240/248] tests: remove placeholders for arguments --- t/buf-simple.c | 6 ------ t/str.c | 2 -- t/xml.c | 2 -- 3 files changed, 10 deletions(-) diff --git a/t/buf-simple.c b/t/buf-simple.c index ff22aa10..72d9fd40 100644 --- a/t/buf-simple.c +++ b/t/buf-simple.c @@ -12,8 +12,6 @@ static void test_expand_title(void **state) { - (void)state; - struct buf s = BUF_INIT; char TEMPLATE[] = "foo ~/bar"; @@ -58,8 +56,6 @@ test_expand_title(void **state) static void test_buf_add_fmt(void **state) { - (void)state; - struct buf s = BUF_INIT; buf_add(&s, "foo"); @@ -72,8 +68,6 @@ test_buf_add_fmt(void **state) static void test_buf_add_char(void **state) { - (void)state; - const char long_string[] = "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; size_t len = strlen(long_string); diff --git a/t/str.c b/t/str.c index 79debb1c..159f95d3 100644 --- a/t/str.c +++ b/t/str.c @@ -11,8 +11,6 @@ static void test_str_starts_with(void **state) { - (void)state; - assert_true(str_starts_with(" foo", 'f', " \t\r\n")); assert_true(str_starts_with("f", 'f', " \t\r\n")); assert_false(str_starts_with(" foo", '<', " ")); diff --git a/t/xml.c b/t/xml.c index a7be6b87..f4c70744 100644 --- a/t/xml.c +++ b/t/xml.c @@ -100,8 +100,6 @@ struct test_case { static void test_lab_xml_expand_dotted_attributes(void **state) { - (void)state; - for (size_t i = 0; i < ARRAY_SIZE(test_cases); i++) { xmlDoc *doc = xmlReadDoc((xmlChar *)test_cases[i].before, NULL, NULL, 0); From d8260feef20705e0aa84f30c1f898d8061f850db Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Thu, 16 Oct 2025 16:43:59 +0900 Subject: [PATCH 241/248] CI: also run on t/ changes --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a9ccbb06..b7329c7b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,6 +18,7 @@ on: - 'include/**' - 'protocols/**' - 'clients/**' + - 't/**' - 'scripts/**' - '.github/workflows/**' From 261ec3c5093e89a27916210dbddc8e9e31c1d8d7 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 20 Oct 2025 04:02:34 +0900 Subject: [PATCH 242/248] scripts/check: run style check for test files Co-authored-by: @johanmalm --- scripts/check | 2 +- scripts/checkpatch.pl | 1 + t/buf-simple.c | 7 ++++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/scripts/check b/scripts/check index 40d48b1e..473fcfdc 100755 --- a/scripts/check +++ b/scripts/check @@ -19,7 +19,7 @@ run_checks () { return $? fi - find src/ include/ clients/ \( -name "*.c" -o -name "*.h" \) -type f -print0 | + find src/ include/ clients/ t/ \( -name "*.c" -o -name "*.h" \) -type f -print0 | nice xargs -0 --max-args 1 --max-procs $(nproc) \ scripts/checkpatch.pl --terse --no-tree --strict --file return $? diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 933dbc59..217536a6 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -5661,6 +5661,7 @@ sub process { $var !~ /^(?:GString|GError|GHashTable)/ && $var !~ /^(?:__FreeBSD__)/ && $var !~ /^(?:RsvgRectangle|RsvgHandle)/ && + $var !~ /^(?:CMUnitTest)/ && $var !~ /^(?:XKB_KEY_XF86Switch_VT_1)/ && #Ignore SI style variants like nS, mV and dB diff --git a/t/buf-simple.c b/t/buf-simple.c index 72d9fd40..d3ebaeb9 100644 --- a/t/buf-simple.c +++ b/t/buf-simple.c @@ -68,7 +68,12 @@ test_buf_add_fmt(void **state) static void test_buf_add_char(void **state) { - const char long_string[] = "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; + static const char long_string[] = + "123456789012345678901234567890123456789012345678901234567890 " + "123456789012345678901234567890123456789012345678901234567890 " + "123456789012345678901234567890123456789012345678901234567890 " + "123456789012345678901234567890123456789012345678901234567890 " + "123456789012345678901234567890123456789012345678901234567890 "; size_t len = strlen(long_string); /* From df3ed58d5b649d6d91b4b405154bfaf430001c82 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sun, 19 Oct 2025 18:48:38 +0100 Subject: [PATCH 243/248] NEWS.md: interim update --- NEWS.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/NEWS.md b/NEWS.md index 1ae97c51..e7d8b383 100644 --- a/NEWS.md +++ b/NEWS.md @@ -115,6 +115,51 @@ differently [#3099]. There is a pending fix [wlroots-5159]. [unreleased-commits] +### Added + +- With the window-switcher custom field state specifiers 's' and 'S', show 's' + for shaded window @domo141 [#2895] +- Support `xdg-dialog` protocol to enable better handling of modal dialogs @xi + [#3134] +- labnag: add --keyboard-focus option @tokyo4j [#3120] +- Allow window switcher to temporarily unshade windows using config option + `` @Amodio @Consolatis [#3124] +- For the 'classic' style window-switcher, add the following theme options: + - `osd.window-switcher.style-classic.item.active.border.color` + - `osd.window-switcher.style-classic.item.active.bg.color` + @tokyo4j [#3118] + +### Fixed + +- Don't remove newlines when parsing config, menu and XBM because doing so can + cause parser error in some unusual situations like the one shown below. + @tokyo4j [#3148] + +``` + +``` + +### Changed + +- If XML documents (like rc.xml and menu.xml) have an XML declaration (typically + ``), this XML declaration must be the first thing in the + document. In previous versions, line breaks (`\n`) were allowed before due to + the way the files were parsed, but this is approach caused other issues like + [#3145] and is contrary to XML syntax. [#3148] [#3153] +- With the window-switcher custom field state specifiers 's' and 'S', change the + display order from M|m|F to m|s|M|F; and increase the size from three + characters wide to four. @domo141 [#2895] +- Call labnag with on-demand keyboard interactivity by default @tokyo4j [#3120] +- Temporarily unshade windows when switching windows. Restore old behaviour with + `` @Amodio @Consolatis [#3124] +- In the classic style window-switcher, the default color of the selected window + item has been changed to inherit the border color but with 15% opacity + @tokyo4j [#3118] + ## 0.9.2 - 2025-10-10 [0.9.2-commits] @@ -2804,6 +2849,7 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16 [#2886]: https://github.com/labwc/labwc/pull/2886 [#2887]: https://github.com/labwc/labwc/pull/2887 [#2891]: https://github.com/labwc/labwc/pull/2891 +[#2895]: https://github.com/labwc/labwc/pull/2895 [#2909]: https://github.com/labwc/labwc/pull/2909 [#2910]: https://github.com/labwc/labwc/pull/2910 [#2914]: https://github.com/labwc/labwc/pull/2914 @@ -2840,4 +2886,11 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16 [#3081]: https://github.com/labwc/labwc/pull/3081 [#3097]: https://github.com/labwc/labwc/pull/3097 [#3099]: https://github.com/labwc/labwc/pull/3099 +[#3118]: https://github.com/labwc/labwc/pull/3118 +[#3120]: https://github.com/labwc/labwc/pull/3120 +[#3124]: https://github.com/labwc/labwc/pull/3124 [#3126]: https://github.com/labwc/labwc/pull/3126 +[#3134]: https://github.com/labwc/labwc/pull/3134 +[#3145]: https://github.com/labwc/labwc/pull/3145 +[#3148]: https://github.com/labwc/labwc/pull/3148 +[#3153]: https://github.com/labwc/labwc/pull/3153 From 1e406dd6495bf7e55dccc2e75578efee79a0df18 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sun, 19 Oct 2025 19:41:24 +0100 Subject: [PATCH 244/248] labnag: use overlay layer by default ...so that the dialog is still visible when some client is using fullscreen mode. --- docs/labwc-config.5.scd | 1 + src/config/rcxml.c | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index ca167b62..00817898 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -286,6 +286,7 @@ this is for compatibility with Openbox. --border-bottom-size 1 \\ --button-border-size 3 \\ --keyboard-focus on-demand \\ + --layer overlay \\ --timeout 0 ``` diff --git a/src/config/rcxml.c b/src/config/rcxml.c index a966577c..6ed1522b 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -1646,6 +1646,7 @@ post_processing(void) "--border-bottom-size 1 " "--button-border-size 3 " "--keyboard-focus on-demand " + "--layer overlay " "--timeout 0"); } if (!rc.fallback_app_icon_name) { From 28cd50406730d9f50761c0898d3fcb90c6c11761 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Sun, 19 Oct 2025 22:15:37 -0400 Subject: [PATCH 245/248] desktop: work around client-side rounding issues at right/bottom pixel This also avoids a similar server-side rounding issue with some combinations of wlroots and libwayland versions. See: - https://gitlab.freedesktop.org/wayland/wayland/-/issues/555 - https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5159 Tested with qmpanel (similar to lxqt-panel) at 1x and 2x output scale. Does not help if scaling is done client-side, e.g. QT_SCALE_FACTOR=2. Fixes: #2379 Fixes: #3099 --- src/desktop.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/desktop.c b/src/desktop.c index bac6c065..2d883445 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -242,6 +242,39 @@ desktop_update_top_layer_visibility(struct server *server) } } +/* + * Work around rounding issues in some clients (notably Qt apps) where + * cursor coordinates in the rightmost or bottom pixel are incorrectly + * rounded up, putting them outside the surface bounds. The effect is + * especially noticeable in right/bottom desktop panels, since driving + * the cursor to the edge of the screen no longer works. + * + * Under X11, such rounding issues went unnoticed since cursor positions + * were always integers (i.e. whole pixel boundaries) anyway. Until more + * clients/toolkits are fractional-pixel clean, limit surface cursor + * coordinates to (w - 1, h - 1) as a workaround. + */ +static void +avoid_edge_rounding_issues(struct cursor_context *ctx) +{ + if (!ctx->surface) { + return; + } + + int w = ctx->surface->current.width; + int h = ctx->surface->current.height; + /* + * The cursor isn't expected to be outside the surface bounds + * here, but check (sx < w, sy < h) just in case. + */ + if (ctx->sx > w - 1 && ctx->sx < w) { + ctx->sx = w - 1; + } + if (ctx->sy > h - 1 && ctx->sy < h) { + ctx->sy = h - 1; + } +} + /* TODO: make this less big and scary */ struct cursor_context get_cursor_context(struct server *server) @@ -269,6 +302,8 @@ get_cursor_context(struct server *server) ret.node = node; ret.surface = lab_wlr_surface_from_node(node); + avoid_edge_rounding_issues(&ret); + #if HAVE_XWAYLAND /* TODO: attach LAB_NODE_UNMANAGED node-descriptor to unmanaged surfaces */ if (node->type == WLR_SCENE_NODE_BUFFER) { From df8e3d819cf32ea46a6ff14d8e61135b6900b5e7 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sun, 19 Oct 2025 19:30:38 +0100 Subject: [PATCH 246/248] docs/: remove XML declarations in .xml files ...because we don't like them anymore and libxml2 does not consider them mandatory. --- docs/menu.xml | 2 -- docs/rc.xml | 2 -- docs/rc.xml.all | 2 -- 3 files changed, 6 deletions(-) diff --git a/docs/menu.xml b/docs/menu.xml index b9dda361..4b4d5dda 100644 --- a/docs/menu.xml +++ b/docs/menu.xml @@ -1,5 +1,3 @@ - - diff --git a/docs/rc.xml b/docs/rc.xml index edcca868..ec9a2f6f 100644 --- a/docs/rc.xml +++ b/docs/rc.xml @@ -1,5 +1,3 @@ - -