]> git.mdlowis.com Git - proto/labwc.git/commitdiff
view: store title/app_id in view
authortokyo4j <hrak1529@gmail.com>
Wed, 13 Aug 2025 08:40:25 +0000 (17:40 +0900)
committerHiroaki Yamamoto <hrak1529@gmail.com>
Mon, 13 Oct 2025 17:27:13 +0000 (02:27 +0900)
This simplifies our codes and eliminates duplicated
`view.events.new_{title,app_id}` events. This should not change any
behaviors.

13 files changed:
include/view.h
src/debug.c
src/foreign-toplevel/ext-foreign.c
src/foreign-toplevel/wlr-foreign.c
src/menu/menu.c
src/osd/osd-field.c
src/osd/osd-thumbnail.c
src/scaled-buffer/scaled-icon-buffer.c
src/ssd/ssd-titlebar.c
src/view-impl-common.c
src/view.c
src/xdg.c
src/xwayland.c

index ba6625505c44b662ee4d1b6ca83efa7212d1a920..a8456de258d2e9cb6b3a9fce3e08f41bec850baf 100644 (file)
@@ -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);
index 97c011f2ecc79ffa438f9a8dbe0f60cb8f0d7996..84b844d8a472cf9381a8ce419d69e779e9acb544 100644 (file)
@@ -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) {
index 587193a69d27b393176ddb686924011c34d2523b..748050faae37a5e1540ea606fe64847cb5d5b640 100644 (file)
@@ -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;
        }
 
index 9f0ed8e632a0b4fcdcd62b2ce98e6ef22a44e3e3..a84775f53d46468893f1ff471dc461d3febe8d1a 100644 (file)
@@ -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;
        }
 
index 89f061c14c3ee06f30857f954df13341042056a8..3770e7f6f603289ac595306a6319d7b2545eb633 100644 (file)
@@ -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);
index 5b275bbbb5da15e4f9251b4c01e6bd05fbaf4b45..9852b11fc65f7c26f9252c1adad78cc1e4c262c1 100644 (file)
@@ -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
index d17f6fc3b91ce05af99f3fbe72fe78c2fce73080..c9e6b283e8a5622385e942534d05446b800819c1 100644 (file)
@@ -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 */
index 0cd09b22bcae955dbeb40b3bab869101e771722b..ceab050955c09c6e115e550d5f16602c5630041c 100644 (file)
@@ -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;
        }
index 152b9b614f0653bb884e48c2df8ce767d2fe53ff..09f5362a21fcb66ee05e0dae078de315b3d720aa 100644 (file)
@@ -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);
 }
index d529941a0eb7e02213c304388c14d6b078353a85..df72fe62757b5aeed6cc129d6b5bf093731bf7db 100644 (file)
@@ -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
index ddfe6b1e1ab84b144067fb685e56d4a6855574a2..87ef8566c44fa33edfd6a96566e23c8eff37b7c6 100644 (file)
@@ -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;
index 1e09dd99c8731a7f10e53cbf1e64a21ed9514e03..f877e7ba0ece158c91fb8214c22f694a6f78c2a0 100644 (file)
--- 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,
index 0ab92f0da506792c9b013ebc5076aebc26f6b708..6b408a78ae0ac433e7fe8d70784e92554c7f43e1 100644 (file)
@@ -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,