desktop: refresh workspace appearance for workspace changes

Fix workspace state updates so panels such as sfwbar can correctly
track workspace-filtered taskbars under labwc.

- remove restriction that skips workspace updates when the source is OSD
- trigger output_update_workspace_appearance() after workspace change
- return early when no view is present to avoid inconsistent updates

This resolves incorrect behavior where sfwbar (filter = workspace)
does not update properly under labwc, while it works correctly on sway.

Related: https://github.com/LBCrion/sfwbar/issues/412
This commit is contained in:
G7fya 2026-04-19 15:48:12 +00:00
parent 5f668a82ee
commit df1e8eb008
6 changed files with 97 additions and 4 deletions

View file

@ -8,6 +8,7 @@ struct foreign_toplevel;
struct foreign_toplevel *foreign_toplevel_create(struct view *view);
void foreign_toplevel_set_parent(struct foreign_toplevel *toplevel,
struct foreign_toplevel *parent);
void foreign_toplevel_refresh(struct foreign_toplevel *toplevel);
void foreign_toplevel_destroy(struct foreign_toplevel *toplevel);
#endif /* LABWC_FOREIGN_TOPLEVEL_H */

View file

@ -1,27 +1,75 @@
// SPDX-License-Identifier: GPL-2.0-only
#include "foreign-toplevel/foreign.h"
#include <assert.h>
#include <stdbool.h>
#include "common/mem.h"
#include "foreign-toplevel/ext-foreign.h"
#include "foreign-toplevel/wlr-foreign.h"
#include "labwc.h"
#include "view.h"
struct foreign_toplevel {
struct view *view;
/* *-toplevel implementations */
struct wlr_foreign_toplevel wlr_toplevel;
struct ext_foreign_toplevel ext_toplevel;
/* TODO: add struct xdg_x11_mapped_toplevel at some point */
};
static bool
should_export_to_panels(struct foreign_toplevel *toplevel)
{
struct view *view = toplevel->view;
if (!view || !view->mapped) {
return false;
}
if (view->visible_on_all_workspaces) {
return true;
}
return view->workspace == server.workspaces.current;
}
static bool
is_exported(struct foreign_toplevel *toplevel)
{
return !!toplevel->wlr_toplevel.handle || !!toplevel->ext_toplevel.handle;
}
void
foreign_toplevel_refresh(struct foreign_toplevel *toplevel)
{
bool want, have;
assert(toplevel);
want = should_export_to_panels(toplevel);
have = is_exported(toplevel);
if (want == have) {
return;
}
if (want) {
wlr_foreign_toplevel_init(&toplevel->wlr_toplevel, toplevel->view);
ext_foreign_toplevel_init(&toplevel->ext_toplevel, toplevel->view);
} else {
wlr_foreign_toplevel_finish(&toplevel->wlr_toplevel);
ext_foreign_toplevel_finish(&toplevel->ext_toplevel);
}
}
struct foreign_toplevel *
foreign_toplevel_create(struct view *view)
{
assert(view);
struct foreign_toplevel *toplevel = znew(*toplevel);
wlr_foreign_toplevel_init(&toplevel->wlr_toplevel, view);
ext_foreign_toplevel_init(&toplevel->ext_toplevel, view);
toplevel->view = view;
foreign_toplevel_refresh(toplevel);
return toplevel;
}
@ -30,6 +78,11 @@ void
foreign_toplevel_set_parent(struct foreign_toplevel *toplevel, struct foreign_toplevel *parent)
{
assert(toplevel);
if (!toplevel->wlr_toplevel.handle) {
return;
}
wlr_foreign_toplevel_set_parent(&toplevel->wlr_toplevel,
parent ? &parent->wlr_toplevel : NULL);
}

View file

@ -1581,6 +1581,10 @@ view_toggle_visible_on_all_workspaces(struct view *view)
assert(view);
view->visible_on_all_workspaces = !view->visible_on_all_workspaces;
ssd_update_geometry(view->ssd);
if (view->foreign_toplevel) {
foreign_toplevel_refresh(view->foreign_toplevel);
}
}
void
@ -1592,6 +1596,10 @@ view_move_to_workspace(struct view *view, struct workspace *workspace)
view->workspace = workspace;
wlr_scene_node_reparent(&view->scene_tree->node,
workspace->view_trees[view->layer]);
if (view->foreign_toplevel) {
foreign_toplevel_refresh(view->foreign_toplevel);
}
}
}

View file

@ -24,6 +24,12 @@
#include "show-desktop.h"
#include "theme.h"
#include "view.h"
#include "common/macros.h"
//#include "desktop.h"
#include "foreign-toplevel/foreign.h"
#include "input/keyboard.h"
#include "labwc.h"
#include "menu/menu.h"
#define EXT_WORKSPACES_VERSION 1
@ -464,9 +470,16 @@ workspaces_switch_to(struct workspace *target, bool update_focus)
/* Save the last visited workspace */
server.workspaces.last = server.workspaces.current;
/* Make sure new views will spawn on the new workspace */
/* Make sure new views will spawn on the new workspace */
server.workspaces.current = target;
struct view *it;
wl_list_for_each(it, &server.views, link) {
if (it->foreign_toplevel) {
foreign_toplevel_refresh(it->foreign_toplevel);
}
}
struct view *grabbed_view = server.grabbed_view;
if (grabbed_view) {
view_move_to_workspace(grabbed_view, target);

View file

@ -846,6 +846,11 @@ handle_map(struct wl_listener *listener, void *data)
}
view_impl_map(view);
if (view->foreign_toplevel) {
foreign_toplevel_refresh(view->foreign_toplevel);
}
view->been_mapped = true;
}
@ -856,6 +861,10 @@ handle_unmap(struct wl_listener *listener, void *data)
if (view->mapped) {
view->mapped = false;
view_impl_unmap(view);
if (view->foreign_toplevel) {
foreign_toplevel_refresh(view->foreign_toplevel);
}
}
}

View file

@ -823,6 +823,11 @@ handle_map(struct wl_listener *listener, void *data)
}
view_impl_map(view);
if (view->foreign_toplevel) {
foreign_toplevel_refresh(view->foreign_toplevel);
}
view->been_mapped = true;
}
@ -836,6 +841,10 @@ handle_unmap(struct wl_listener *listener, void *data)
view->mapped = false;
view_impl_unmap(view);
if (view->foreign_toplevel) {
foreign_toplevel_refresh(view->foreign_toplevel);
}
/*
* Destroy the content_tree at unmap. Alternatively, we could
* let wlr_scene manage its lifetime automatically, but this