From babd7af8f83431f161096cf35df1b31ae305a433 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Wed, 13 Aug 2025 17:40:25 +0900 Subject: [PATCH] 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 | 43 ++++++++++++++++---------- src/xdg.c | 39 +++++------------------ src/xwayland.c | 43 ++++++-------------------- 13 files changed, 86 insertions(+), 154 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) +void +view_set_title(struct view *view, const char *title) { assert(view); - assert(prop); - if (view->impl->get_string_prop) { - const char *ret = view->impl->get_string_prop(view, prop); - return ret ? ret : ""; + if (!title) { + title = ""; } - return ""; -} -void -view_update_title(struct view *view) -{ - assert(view); + 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, -- 2.52.0