You are not logged in.

#1 2022-07-10 06:53:37

daalexdugin
Member
Registered: 2022-07-10
Posts: 11

[SOLVED] - currently selected layout (get the info for the statusbar)

If I use just one keyboard layout:
$ setxkbmap us

I can get the current layout this way:
$ setxkbmap -query | awk '/layout/{print $2}'

But if I set setxkbmap to toggle between 2 layouts:
$ setxkbmap -layout us,es -option grp:alt_shift_toggle

there's a problem:
$ setxkbmap -query | awk '/layout/{print $2}'
returns "us,es"

How can I get the currently selected keyboard layout?
Either "us" or "es", and not "us,es".

I need this info for my statusbar.

Last edited by daalexdugin (2022-07-15 04:49:23)

Offline

#2 2022-07-10 07:18:10

jasonwryan
Anarchist
From: .nz
Registered: 2009-05-09
Posts: 30,424
Website

Re: [SOLVED] - currently selected layout (get the info for the statusbar)


Arch + dwm   •   Mercurial repos  •   Surfraw

Registered Linux User #482438

Offline

#3 2022-07-10 07:58:09

daalexdugin
Member
Registered: 2022-07-10
Posts: 11

Re: [SOLVED] - currently selected layout (get the info for the statusbar)

Thank you for the answer.
If I were to use any additional software, I'd look into official repos first.
For example, there are keyboard daemons in the Community repo: sxhkd and xbindkeys.

I could run a simple shell script every time a certain key combo's pressed:
#!/bin/sh
(setxkbmap -query | grep -q "layout:\s\+us") && setxkbmap es || setxkbmap us

But I hoped it was possible to get the current layout info without installing any additional software.
I mean the functionality is already there. Setxkbmap can toggle layouts.
Which means it knows the current layout at any given moment.

Offline

#4 2022-07-10 08:42:14

seth
Member
Registered: 2012-09-03
Posts: 51,648

Re: [SOLVED] - currently selected layout (get the info for the statusbar)

setxkbmap es || setxkbmap us

You don't want to do that at all, but use one of

grep -iE xk_iso.\*_group /usr/include/X11/keysymdef.h

w/ eg. xdotool.
"setxkbmap us" is not the same as merely changing the group.

xkblayout-state might be overkill, see eg. https://unix.stackexchange.com/a/422493
Querying the LED state (other suggestion in that thread) will only work if you use caps lock as indicator for the current layout.

Offline

#5 2022-07-10 15:55:59

daalexdugin
Member
Registered: 2022-07-10
Posts: 11

Re: [SOLVED] - currently selected layout (get the info for the statusbar)

Thank you, the GetXkbLayout utility from Stackexchange works.
When I run it, it shows the current layout.
And when I press ALT+SHIFT, setxkbmap toggles layouts.

But how to make my status bar update itself every time I press the key combo?
Can setxkbmap signal changes somehow?
Or installing a keyboard daemon is the only possible solution?

My status bar is a simple shell script. By default it updates itself every minute:

while true; do
   xsetroot -name "$(volume) $(keyboard) $(time)"
   sleep 1m
done

But I could use another shell script that kills the 1 minute timer
and therefore forces the status bar to update itself:

#!/bin/sh
sv_target_pid=$(ps -afx | grep "sleep" | grep "1m" | awk '{print $1}')
([ -n "${sv_target_pid}" ]) && (kill ${sv_target_pid})

The script should be triggered every time I press the ALT+SHIFT combo.

Offline

#6 2022-07-10 16:55:47

seth
Member
Registered: 2012-09-03
Posts: 51,648

Re: [SOLVED] - currently selected layout (get the info for the statusbar)

And when I press ALT+SHIFT, setxkbmap toggles layouts.

I hope that means you're using "grp:alt_shift_toggle"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <X11/XKBlib.h>
#include <X11/extensions/XKBrules.h>

Atom wm_name;
Atom string;

void printGroup(Display *dpy, int grp, Bool useLabel) {

    char *group;
    if (useLabel) {
        group = XGetAtomName(dpy, XkbGetKeyboard(dpy, XkbAllComponentsMask, XkbUseCoreKbd)->names->groups[grp]);
    } else {
        XkbRF_VarDefsRec vd;
        XkbRF_GetNamesProp(dpy, NULL, &vd);

        group = strtok(vd.layout, ",");
        for (int i = 0; i < grp; i++) {
            group = strtok(NULL, ",");
            if (group == NULL) {
                fprintf(stderr, "Group out of bounds: %s\n", grp);
                return;
            }
        }
    }

    printf("%s\n", group);
    XChangeProperty(dpy, RootWindow(dpy, DefaultScreen(dpy)), wm_name, string, 8, PropModeReplace, (unsigned char*)group, strlen(group));
}

int main(int argc, char **argv) {
    Display *dpy = XOpenDisplay(NULL);

    if (dpy == NULL) {
        fprintf(stderr, "Cannot open display\n");
        exit(1);
    }
    
    Bool useLabel = (argc < 2 || strcmp(argv[1], "2lc"));
    
    wm_name = XInternAtom(dpy, "WM_NAME", 0);
    string = XInternAtom(dpy, "STRING", 0);

    XkbStateRec state;
    XkbGetState(dpy, XkbUseCoreKbd, &state);
    printGroup(dpy, state.group, useLabel);
    
    int opcode, xkbEventType, error, major, minor;
    XkbQueryExtension(dpy, &opcode, &xkbEventType, &error, &major, &minor);
    XkbSelectEventDetails(dpy, XkbUseCoreKbd, XkbStateNotify, XkbAllStateComponentsMask, XkbAllStateComponentsMask);

    XEvent event;
    int lastlang = -1;
    while (1) {
        XNextEvent(dpy, &event);
        if (event.type == xkbEventType) {
            XkbEvent* xkbe = (XkbEvent*)&event;
            if (xkbe->any.xkb_type == XkbStateNotify) {
                if (xkbe->state.group != lastlang) {
                    lastlang = xkbe->state.group;
                    printGroup(dpy, lastlang, useLabel);
                }
            }
        }
    }
}

Last edited by seth (2022-07-11 16:19:37)

Offline

#7 2022-07-11 15:54:51

daalexdugin
Member
Registered: 2022-07-10
Posts: 11

Re: [SOLVED] - currently selected layout (get the info for the statusbar)

The while loop, it's supposed to update the name of the root window in X every time I press ALT+SHIFT, right?
Well, when I run this utility, it changes the name of the root window just once.
After that no matter how many times I toggle layouts
the changes are not reflected in the root window.
https://youtu.be/Y2IK29dkpA4

seth wrote:

I hope that means you're using "grp:alt_shift_toggle"

Yes, I am. And my configuration is persistent across reboots.
https://wiki.archlinux.org/title/Xorg/K … tion_files

Offline

#8 2022-07-11 16:20:01

seth
Member
Registered: 2012-09-03
Posts: 51,648

Re: [SOLVED] - currently selected layout (get the info for the statusbar)

Forgot to select the events and also some braces…

Edit: see the edited post.

Last edited by seth (2022-07-11 16:20:24)

Offline

#9 2022-07-11 19:56:59

daalexdugin
Member
Registered: 2022-07-10
Posts: 11

Re: [SOLVED] - currently selected layout (get the info for the statusbar)

It sets the name of the root window now.
As a result it also overwrites my status bar entirely.
https://youtu.be/ajsc7HqKosQ

status bar (launched from the .xinitrc file):

#!/bin/sh
volume () {
    printf "%s" "$(pamixer --get-volume-human)"
}
keyboard () {
    printf "%s" "$(setxkbmap -query | awk '/layout/{print $2}' | tr '[:lower:]' '[:upper:]')"

    # The utility from Stackexchange can be used instead
    # Just comment out this line:
    # printf("Full name: %s\n", group);

    # And replace this one:
    # printf("Layout name: %s\n", tok);
    # with these:
    # for (size_t i = 0; i < strlen(tok); ++i)
    # {
    #     printf("%c", to upper((unsigned char) tok[i]));
    # }
}
time () {
    printf "%s" "$(date +%H:%M)"
}
while true; do
    xsetroot -name "$(volume) $(keyboard) $(time)"
    sleep 1m
done

volume (bound to key combos):

#!/bin/sh
case "$1" in
    "+") pamixer --increase $2 ;;
    "-") pamixer --decrease $2 ;;
    "=") pamixer --set-volume $2 ;;
esac

/home/user/scripts/bar-refresh

keyboard (bound to a key combo):

#!/bin/sh
(setxkkbmap -query | grep -q "layout:\s\+us") && setxkbmap es || setxkbmap us

# But this approach doesn't allow us to do this:
# setxkbmap -layout us,es -option grp:alt_shift_toggle
# because the status bar will be displaying "us,es" and not just one of them

/home/user/scripts/bar-refresh

bar-refresh (called from scripts):

#!/bin/sh
sv_target_pid=$(ps -afx | grep "sleep" | grep "1m" | awk '{print $1}')
([ -n "${sv_target_pid}" ]) && (kill $ {sv_target_pid})

Offline

#10 2022-07-11 20:01:02

seth
Member
Registered: 2012-09-03
Posts: 51,648

Re: [SOLVED] - currently selected layout (get the info for the statusbar)

https://github.com/luebking/nodesk/blob … ndicator.c
I'll add a parameter to suppress that and you'll have to parse the output in your script.

Offline

#11 2022-07-12 10:18:12

daalexdugin
Member
Registered: 2022-07-10
Posts: 11

Re: [SOLVED] - currently selected layout (get the info for the statusbar)

seth wrote:

I'll add a parameter to suppress that

With “noprop” passed to xkb_indicator utility,
how to force my status bar to refresh itself when I toggle layouts?

Do I still need to use a keyboard daemon
that will run the bar-refresh script every time I press ALT+SHIFT?
Just like I do with the Stackexchange version of the utility?

Offline

#12 2022-07-12 17:32:11

seth
Member
Registered: 2012-09-03
Posts: 51,648

Re: [SOLVED] - currently selected layout (get the info for the statusbar)

No, you read the output and when something shows up, you execute whatever you want to execute.
I added parameters to only print the current layout and wait for the next event only.

You'll figure the rest on your own - we're miles away from

But I hoped it was possible to get the current layout info without installing any additional software.

and rather at "I want somebody to maintain my system for me"…

Offline

#13 2022-07-13 04:43:45

daalexdugin
Member
Registered: 2022-07-10
Posts: 11

Re: [SOLVED] - currently selected layout (get the info for the statusbar)

You helped me when you provided the link to the Stackexchange forum.
We could close the topic right then because my initial question was answered.

From that point on, we started to talk about updating the status bar:

daalexdugin wrote:

Thank you, the GetXkbLayout utility from Stackexchange works.
When I run it, it shows the current layout.
And when I press ALT+SHIFT, setxkbmap toggles layouts.

But how to make my status bar update itself every time I press the key combo?

You suggested the solution that overwrites my status bar entirely
which wasn’t an acceptable solution for obvious reasons.

And here we are. Now you think I overstepped your boundaries for some reason.

seth wrote:

No, you read the output and when something shows up, you execute whatever you want to execute.

Sounds like a keyboard daemon to me. I don’t know what else to google.
My status bar script updates the name of the root window once a minute and that’s it.

Anyway, thank you for your time. I do appreciate your help. Let’s close the topic.

Offline

#14 2022-07-13 06:50:57

seth
Member
Registered: 2012-09-03
Posts: 51,648

Re: [SOLVED] - currently selected layout (get the info for the statusbar)

You suggested the solution that overwrites my status bar entirely which wasn’t an acceptable solution for obvious reasons.

Which was at #9-#11 when you went on to ask me to write your statusbar script for you.

You're aware that bash has this concept, it's called a pipe, that allows you to read ouput from something™ into the input of something™ - including a script?
So you can just wait for something™ to print something™ and then act on that output…
You actually have all the tools at hand to do what you want.

Let’s close the topic.

Mark resolved threads by editing your initial posts subject - so others will know that there's no task left, but maybe a solution to find.
Thanks.

Offline

#15 2022-07-13 20:14:04

daalexdugin
Member
Registered: 2022-07-10
Posts: 11

Re: [SOLVED] - currently selected layout (get the info for the statusbar)

seth wrote:

you went on to ask me to write your statusbar script for you.

No, I didn't.

And I'm not aware about the waiting-for-something-to-print-something part either.
The real-time-monitoring or whatever this thing is.

I’ve seen examples of how to deal with programs that print output once and then stop.
We can re-run such programs however often we need.

The utility from Stackexchange is like that. It returns the current layout and stops.
So we could re-run it like 2 times a second but update the status bar only when layouts were toggled.

getxkblayout-loop.sh:

#!/bin/sh

var1=”oldvalue”
while true; do
	var2=$($HOME/bin/getxkblaout)
	if [ $var1 != $var2 ]; then
		$(/$HOME/scripts/update-status.sh $var2)
		var1=$var2
		#echo “var2 is $var2”
	#else
		#echo “var1 = var2, don’t update var1”
	fi
	sleep 0.5s
done

update-status.sh:

#!/bin/sh

volume () {
	printf “%s” “$(pamixer --get-volume-human)”
}
time () {
	printf “%s” “$(date +%H:%M)”
}
xsetroot -name “$(volume) $1 $(time)”

Dealing with the program that is constantly running is another story.

Offline

#16 2022-07-13 20:22:26

seth
Member
Registered: 2012-09-03
Posts: 51,648

Re: [SOLVED] - currently selected layout (get the info for the statusbar)

No, I didn't.

Do I still need to use a keyboard daemon that will run the bar-refresh script every time I press ALT+SHIFT?
Just like I do with the Stackexchange version of the utility?

aka "how can I use this"

And I'm not aware about the waiting-for-something-to-print-something part either.

ping google.com | while read line; do echo "Pong: $line"; done

You're welcome.

Offline

#17 2022-07-15 04:22:15

daalexdugin
Member
Registered: 2022-07-10
Posts: 11

Re: [SOLVED] - currently selected layout (get the info for the statusbar)

seth wrote:
ping google.com | while read line; do echo "Pong: $line"; done

We can pipe an infinite output from a program to a while loop
and react to it line by line.
And the while loop doesn’t even need a sleep timer!
It reacts to each line immediately!
That’s so cool! Thank you!

#!/bin/sh

xkb () {
	$HOME/bin/xkb_indicator noprop 2lc
}

volume () {
	printf "%s" "$(pamixer --get-volume-human)"
}

time () {
	printf "%s" "$(date +%H:%M)"
}

xkb | while read xkbline; do
	xsetroot -name "$(volume) $xkbline $(time)"
}

Offline

#18 2022-07-15 06:08:27

seth
Member
Registered: 2012-09-03
Posts: 51,648

Re: [SOLVED] - currently selected layout (get the info for the statusbar)

You'll either need a second loop (hint you can fork functions) or time-limit the read and restart that loop frequently to get the clock updated.
Not sure whether pactl/pamixer has some sort of tracking/follow mode - I stay away from PA as far as I can.

Offline

#19 2022-07-16 05:45:17

daalexdugin
Member
Registered: 2022-07-10
Posts: 11

Re: [SOLVED] - currently selected layout (get the info for the statusbar)

seth wrote:

You'll either need a second loop (hint you can fork functions) or time-limit the read and restart that loop frequently to get the clock updated.

Good call.
Since timeout doesn’t seem to work with internal functions,
we can move our internal function to a separate script and now it can be treated as a command.

Our main loop. It updates the clock every minute:

#!/bin/sh
# bar-timeout.sh

while true; do
	timeout 1m $HOME/scripts/bar-xkbline.sh
done

This script listens to the xkb_indicator utility and draws the status bar:

#!/bin/sh
# bar-xkbline.sh

xkb () {
	$HOME/bin/xkb_indicator noprop 2lc
}
volume () {
	printf “%s” “$(pamixer --pamixer --get-volume-human)”
}
time () {
	printf “%s” “$(date +%H:%M)”
}

ping archlinux.org | while read xkbline; do
#xkb | while read xkbline; do
	xsetroot -name “$(volume) $xkbline $(time)”
done

It works for ping but not for xkb_indicator.

Offline

#20 2022-07-16 07:52:24

seth
Member
Registered: 2012-09-03
Posts: 51,648

Re: [SOLVED] - currently selected layout (get the info for the statusbar)

read --help
#!/bin/bash
foo() {
while true; do
    echo foo;
    sleep 1
done
}

bar() {
while true; do
    sleep 0.5
    echo bar;
    sleep 0.5
done
}

foo & bar

Offline

#21 2022-07-19 09:43:37

daalexdugin
Member
Registered: 2022-07-10
Posts: 11

Re: [SOLVED] - currently selected layout (get the info for the statusbar)

Thanks a lot for your help!

Offline

Board footer

Powered by FluxBB