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);
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;
*/
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);
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) {
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);
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);
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;
}
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
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
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;
}
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;
}
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);
/* 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)) {
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 *
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 */
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
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
}
/* 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 */
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;
}
}
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);
}
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);
}
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);
}
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);
}
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
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;
}
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);
}
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
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;
&& 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 */
}
}
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;
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
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
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)
{
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,
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
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
* '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
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,