osd: add multi-monitor support
Some checks failed
labwc.github.io / notify (push) Has been cancelled

Adds `output` attribute to control which monitor(s) display the window
switcher OSD. Supports three modes:
- "all": display on all monitors (default)
- "pointer": display on monitor with mouse cursor
- "keyboard": display on monitor with keyboard focus

The configuration structure is also refactored to nest OSD-specific
settings (show, style, output, thumbnailLabelFormat) under an <osd>
element within <windowSwitcher>, improving logical organization.
This commit is contained in:
Samet Aylak 2025-11-09 23:16:26 -05:00 committed by GitHub
parent 79fbb611e0
commit d65caf8bfd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 105 additions and 25 deletions

View file

@ -339,7 +339,8 @@ this is for compatibility with Openbox.
## WINDOW SWITCHER
```
<windowSwitcher show="yes" style="classic" preview="yes" outlines="yes" allWorkspaces="no" thumbnailLabelFormat="%T">
<windowSwitcher preview="yes" outlines="yes" allWorkspaces="no">
<osd show="yes" style="classic" output="all" thumbnailLabelFormat="%T" />
<fields>
<field content="icon" width="5%" />
<field content="desktop_entry_name" width="30%" />
@ -348,14 +349,7 @@ this is for compatibility with Openbox.
</windowSwitcher>
```
*<windowSwitcher show="" style="" preview="" outlines="" allWorkspaces="" unshade="" thumbnailLabelFormat="">*
*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.
*<windowSwitcher preview="" outlines="" allWorkspaces="" unshade="">*
*preview* [yes|no] Preview the contents of the selected window when
switching between windows. Default is yes.
@ -369,12 +363,26 @@ this is for compatibility with Openbox.
*unshade* [yes|no] Temporarily unshade windows when switching between
them and permanently unshade on the final selection. Default is yes.
*<osd show="" style="" output="" thumbnailLabelFormat="" />*
*show* [yes|no] Draw the OnScreenDisplay when switching between
windows. Default is yes.
*style* [classic|thumbnail] Configures the style of the OSD.
"classic" displays window information like icons and titles in a vertical list.
"thumbnail" shows window thumbnail, icon and title in grids.
*output* [all|pointer|keyboard] Configures which monitor(s) show the OSD.
"all" displays the OSD on all monitors.
"pointer" displays the OSD on the monitor containing the mouse pointer.
"keyboard" displays the OSD on the monitor with keyboard focus.
Default is "all".
*thumbnailLabelFormat* Format to be used for the thumbnail label according to *custom*
field below, only applied when using *<windowSwitcher style="thumbnail" />*.
field below, only applied when using *<osd style="thumbnail" />*.
Default is "%T".
*<windowSwitcher><fields><field content="" width="%">*
Define window switcher fields when using *<windowSwitcher style="classic" />*.
Define window switcher fields when using *<osd style="classic" />*.
*content* defines what the field shows and can be any of:

View file

@ -77,8 +77,8 @@
</font>
</theme>
<windowSwitcher show="yes" style="classic" preview="yes"
outlines="yes" allWorkspaces="no" unshade="yes">
<windowSwitcher preview="yes" outlines="yes" allWorkspaces="no" unshade="yes">
<osd show="yes" style="classic" output="all" thumbnailLabelFormat="%T" />
<fields>
<field content="icon" width="5%" />
<field content="desktop_entry_name" width="30%" />
@ -98,7 +98,8 @@
Some contents are fixed-length and others are variable-length.
See "man 5 labwc-config" for details.
<windowSwitcher show="yes" preview="no" outlines="no" allWorkspaces="yes">
<windowSwitcher preview="no" outlines="no" allWorkspaces="yes">
<osd show="yes" />
<fields>
<field content="workspace" width="5%" />
<field content="state" width="3%" />
@ -118,7 +119,8 @@
then workspace name, then identifier/app-id, then the window title.
It uses 100% of OSD window width.
<windowSwitcher show="yes" preview="no" outlines="no" allWorkspaces="yes">
<windowSwitcher preview="no" outlines="no" allWorkspaces="yes">
<osd show="yes" />
<fields>
<field content="custom" format="foobar %b %3s %-10o %-20W %-10i %t" width="100%" />
</fields>

View file

@ -183,6 +183,7 @@ struct rcxml {
enum lab_view_criteria criteria;
struct wl_list fields; /* struct window_switcher_field.link */
enum window_switcher_style style;
enum osd_output_criteria output_criteria;
char *thumbnail_label_format;
} window_switcher;

View file

@ -112,4 +112,10 @@ enum window_switcher_style {
WINDOW_SWITCHER_THUMBNAIL,
};
enum osd_output_criteria {
OSD_OUTPUT_ALL,
OSD_OUTPUT_POINTER,
OSD_OUTPUT_KEYBOARD,
};
#endif /* LABWC_CONFIG_TYPES_H */

View file

@ -1068,7 +1068,7 @@ entry(xmlNode *node, char *nodename, char *content)
load_default_mouse_bindings();
} else if (!strcasecmp(nodename, "prefix.desktops")) {
xstrdup_replace(rc.workspace_config.prefix, content);
} else if (!strcasecmp(nodename, "thumbnailLabelFormat.windowSwitcher")) {
} else if (!strcasecmp(nodename, "thumbnailLabelFormat.osd.windowSwitcher")) {
xstrdup_replace(rc.window_switcher.thumbnail_label_format, content);
} else if (!lab_xml_node_is_leaf(node)) {
@ -1201,15 +1201,50 @@ entry(xmlNode *node, char *nodename, char *content)
wlr_log(WLR_ERROR, "ignoring invalid value for notifyClient");
}
/* <windowSwitcher show="" preview="" outlines="" /> */
/*
* <windowSwitcher preview="" outlines="">
* <osd show="" style="" output="" thumbnailLabelFormat="" />
* </windowSwitcher>
*
* thumnailLabelFormat is handled above to allow for an empty value
*/
} else if (!strcasecmp(nodename, "show.osd.windowSwitcher")) {
set_bool(content, &rc.window_switcher.show);
} else if (!strcasecmp(nodename, "style.osd.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 {
wlr_log(WLR_ERROR, "Invalid windowSwitcher style %s: "
"should be one of classic|thumbnail", content);
}
} else if (!strcasecmp(nodename, "output.osd.windowSwitcher")) {
if (!strcasecmp(content, "all")) {
rc.window_switcher.output_criteria = OSD_OUTPUT_ALL;
} else if (!strcasecmp(content, "pointer")) {
rc.window_switcher.output_criteria = OSD_OUTPUT_POINTER;
} else if (!strcasecmp(content, "keyboard")) {
rc.window_switcher.output_criteria = OSD_OUTPUT_KEYBOARD;
} else {
wlr_log(WLR_ERROR, "Invalid windowSwitcher output %s: "
"should be one of all|pointer|keyboard", content);
}
/* The following two are for backward compatibility only. */
} else if (!strcasecmp(nodename, "show.windowSwitcher")) {
set_bool(content, &rc.window_switcher.show);
wlr_log(WLR_ERROR, "<windowSwitcher show=\"\" /> is deprecated."
" Use <osd 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;
}
wlr_log(WLR_ERROR, "<windowSwitcher style=\"\" /> is deprecated."
" Use <osd style=\"\" />");
} else if (!strcasecmp(nodename, "preview.windowSwitcher")) {
set_bool(content, &rc.window_switcher.preview);
} else if (!strcasecmp(nodename, "outlines.windowSwitcher")) {
@ -1431,6 +1466,7 @@ rcxml_init(void)
rc.window_switcher.show = true;
rc.window_switcher.style = WINDOW_SWITCHER_CLASSIC;
rc.window_switcher.output_criteria = OSD_OUTPUT_ALL;
rc.window_switcher.thumbnail_label_format = xstrdup("%T");
rc.window_switcher.preview = true;
rc.window_switcher.outlines = true;

View file

@ -294,6 +294,20 @@ preview_cycled_view(struct view *view)
wlr_scene_node_raise_to_top(osd_state->preview_node);
}
static void
update_osd_on_output(struct server *server, struct output *output,
struct osd_impl *osd_impl, struct wl_array *views)
{
if (!output_is_usable(output)) {
return;
}
if (!output->osd_scene.tree) {
osd_impl->create(output, views);
assert(output->osd_scene.tree);
}
osd_impl->update(output);
}
static void
update_osd(struct server *server)
{
@ -318,16 +332,29 @@ update_osd(struct server *server)
if (rc.window_switcher.show) {
/* Display the actual OSD */
struct output *output;
wl_list_for_each(output, &server->outputs, link) {
if (!output_is_usable(output)) {
continue;
switch (rc.window_switcher.output_criteria) {
case OSD_OUTPUT_ALL: {
struct output *output;
wl_list_for_each(output, &server->outputs, link) {
update_osd_on_output(server, output, osd_impl, &views);
}
break;
}
if (!output->osd_scene.tree) {
osd_impl->create(output, &views);
assert(output->osd_scene.tree);
case OSD_OUTPUT_POINTER:
update_osd_on_output(server,
output_nearest_to_cursor(server), osd_impl, &views);
break;
case OSD_OUTPUT_KEYBOARD: {
struct output *output;
if (server->active_view) {
output = server->active_view->output;
} else {
/* Fallback to pointer, if there is no active_view */
output = output_nearest_to_cursor(server);
}
update_osd_on_output(server, output, osd_impl, &views);
break;
}
osd_impl->update(output);
}
}