Skip to content

[feature] only show the current keyboard layout and follow the changes #820

@zaza42

Description

@zaza42

Long story long: The reason I still use xxkb in 2025 is 3 essential things:

  1. it shows the flag
  2. I can switch with alt-shift
  3. very low on resources

I just noticed that IceWM can also display flags instead of text (the manual wasn't clear that you have to copy a file called us.xpm to ~/.icewm/icons for this), so that part is solved.
There are all kinds of input remappers to bind alt-shift to e.g. alt+shift+c, but these consume more resources than the whole icewm. (and this is true for other keyboard indicators that aren't xxkb) And all keyboard switchers can track changes in the xorg keyboard layout, but IceWM can't. Even though the layout has changed, the flag remains the same.

So... what if IceWM just did this: draw the flag (or print the country code in its absence), and that's it.

I should add that I have very little skills with X11 and C, but with the help of an LLM I could create a patch that does just that. I've never made a pull request in my life, so I'll just copy it here first.
@gijsbers , take a look, what do you think?

diff -urNad icewm/src/default.h icewm-OnlyShowLayout/src/default.h
--- icewm/src/default.h	2025-11-25 03:17:11.339754991 +0100
+++ icewm-OnlyShowLayout/src/default.h	2025-11-25 03:18:19.352519366 +0100
@@ -4,6 +4,7 @@
 #include "yconfig.h"
 
 /************************************************************************************************************************************************************/
+XIV(bool, onlyShowLayout,                       false)
 XIV(bool, clickFocus,                           true)
 XIV(bool, focusOnAppRaise,                      false)
 XIV(bool, requestFocusOnAppRaise,               true)
@@ -244,6 +245,7 @@
 #ifdef CFGDEF
 
 cfoption icewm_preferences[] = {
+    OBV("OnlyShowLayout",                       &onlyShowLayout,                "Only show and follow the current keyboard layout"),
     OBV("ClickToFocus",                         &clickFocus,                    "Focus windows by clicking"),
     OBV("FocusOnAppRaise",                      &focusOnAppRaise,               "Focus windows when application requests to raise"),
     OBV("RequestFocusOnAppRaise",               &requestFocusOnAppRaise,        "Request focus (flashing in taskbar) when application requests raise"),
diff -urNad icewm/src/wmapp.cc icewm-OnlyShowLayout/src/wmapp.cc
--- icewm/src/wmapp.cc	2025-11-25 03:17:11.351755126 +0100
+++ icewm-OnlyShowLayout/src/wmapp.cc	2025-11-25 03:50:17.598133365 +0100
@@ -40,6 +40,11 @@
 #include <X11/Xproto.h>
 #include "ywordexp.h"
 #include "intl.h"
+#include <X11/XKBlib.h>
+#include <X11/extensions/XKBstr.h>
+
+static int xkbEventBase = 0;
+static int xkbErrorBase = 0;
 
 char const *ApplicationName("IceWM");
 RebootShutdown rebootOrShutdown = Logout;
@@ -1449,6 +1454,17 @@
 
     registerProtocols2(managerWindow);
 
+    // --- XKB extension enable for following keyboard layouts ---
+    if (onlyShowLayout) {
+       int major = XkbMajorVersion, minor = XkbMinorVersion;
+       if (XkbQueryExtension(xapp->display(), nullptr,
+                             &xkbEventBase, &xkbErrorBase,
+                             &major, &minor)) {
+           XkbSelectEvents(xapp->display(), XkbUseCoreKbd,
+                           XkbStateNotifyMask, XkbStateNotifyMask);
+       }
+    }
+
     initIcons();
     initIconSize();
     WPixRes::initPixmaps(this);
@@ -1663,6 +1679,16 @@
 }
 
 bool YWMApp::filterEvent(const XEvent &xev) {
+    if (onlyShowLayout && xev.type == xkbEventBase) {
+        const XkbEvent* xkbev = reinterpret_cast<const XkbEvent*>(&xev);
+        if (xkbev->any.xkb_type == XkbStateNotify) {
+            int group = xkbev->state.group;
+            if (manager && configKeyboards.nonempty()) {
+                manager->setKeyboard(configKeyboards[group % configKeyboards.getCount()]);
+            }
+            return true;
+        }
+    }
     if (xev.type == SelectionClear) {
         if (xev.xselectionclear.window == managerWindow) {
             manager->unmanageClients();
diff -urNad icewm/src/wmmgr.cc icewm-OnlyShowLayout/src/wmmgr.cc
--- icewm/src/wmmgr.cc	2025-11-25 03:17:11.351755126 +0100
+++ icewm-OnlyShowLayout/src/wmmgr.cc	2025-11-25 03:57:14.878839684 +0100
@@ -590,7 +590,7 @@
             taskBar->moveNext();
         return true;
     } else if (gKeySysKeyboardNext == key) {
-        if (configKeyboards.nonempty())
+        if (!onlyShowLayout && configKeyboards.nonempty())
             setKeyboard((fDefaultKeyboard + 1) % configKeyboards.getCount());
         return true;
     }
@@ -3676,26 +3676,32 @@
 void YWindowManager::setKeyboard(mstring keyboard) {
     if (keyboard != null && keyboard != fCurrentKeyboard) {
         fCurrentKeyboard = keyboard;
-        char program[] = "setxkbmap";
-        csmart path(path_lookup(program));
-        if (path) {
-            wordexp_t exp = {};
-            exp.we_offs = 1;
-            if (wordexp(keyboard, &exp, WRDE_NOCMD | WRDE_DOOFFS) == 0) {
-                exp.we_wordv[0] = program;
-                wmapp->runProgram(path, exp.we_wordv);
-                exp.we_wordv[0] = nullptr;
-                wordfree(&exp);
-            }
+        if (onlyShowLayout) {
             if (taskBar) {
                 taskBar->keyboardUpdate(keyboard);
             }
-        }
-        else if (ONCE) {
-            new YMsgBox(YMsgBox::mbOK,
-                        _("Missing program setxkbmap"),
-                        _("For keyboard switching, please install setxkbmap."),
-                        this, "key");
+        } else {
+            char program[] = "setxkbmap";
+            csmart path(path_lookup(program));
+            if (path) {
+                wordexp_t exp = {};
+                exp.we_offs = 1;
+                if (wordexp(keyboard, &exp, WRDE_NOCMD | WRDE_DOOFFS) == 0) {
+                    exp.we_wordv[0] = program;
+                    wmapp->runProgram(path, exp.we_wordv);
+                    exp.we_wordv[0] = nullptr;
+                    wordfree(&exp);
+                }
+                if (taskBar) {
+                    taskBar->keyboardUpdate(keyboard);
+                }
+            }
+            else if (ONCE) {
+                new YMsgBox(YMsgBox::mbOK,
+                            _("Missing program setxkbmap"),
+                            _("For keyboard switching, please install setxkbmap."),
+                            this, "key");
+            }
         }
     }
 }

Pro 1: I can use alt+shift (which fixes also #721, #632, #622)
Pro 2: no need to call an external binary for changing the layout --> more lightweight. (I'm changing my layout a LOT, even more times in one sentence)
Pro 3: I can rid of using xxkb forever. ;->

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions