]> git.mdlowis.com Git - proto/labwc.git/commitdiff
osd: add multi-monitor support
authorSamet Aylak <sametaylak29@gmail.com>
Mon, 10 Nov 2025 04:16:26 +0000 (23:16 -0500)
committerGitHub <noreply@github.com>
Mon, 10 Nov 2025 04:16:26 +0000 (05:16 +0100)
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.

docs/labwc-config.5.scd
docs/rc.xml.all
include/config/rcxml.h
include/config/types.h
src/config/rcxml.c
src/osd/osd.c

index 6be5f5f6b10f8a12b636278b7817328230d1a2ec..9b0f8291dde4ec647cf757083037d92bfe0b87e9 100644 (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:
 
index c581ff51ec8bef03d4c54759877d0e1c60c7e1ae..97698aac23a5eb5f8f56fb58d7a91929ba247a9d 100644 (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%" />
     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>
index c20cd8003c528b60576781b0cf6ec14dfa756a0b..91a73d58dd578602d238f462c9495555e69ad725 100644 (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;
 
index e832a65896a4c9a7cd373d4f2f95ec3aea27631b..76e699a4ab6252e36c0962a881d67d6b06d52811 100644 (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 */
index 1acdd825f770708be5d0c008373918ad4bfcfad1..264c65a83c9e32397688f14a0e2eadf971da076e 100644 (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;
index ca64920c0f196e9687ad577c5b9383fb2cb30300..3f2d807464a5a5bc9f8d59baf3a91f7e37cbba8e 100644 (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);
                }
        }