Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

modules: Keyboard Layout #85

Closed
wants to merge 3 commits into from
Closed

Conversation

harishkrupo
Copy link
Contributor

This patch adds a new module to show the current keyboard layout.

Signed-off-by: Harish Krupo [email protected]

@harishkrupo
Copy link
Contributor Author

@Alexays please review

@harishkrupo
Copy link
Contributor Author

harishkrupo commented Nov 4, 2018

I tested it by running:
swaymsg "input <keyboard> xkb_layout de"
The label changes to german from English.

@eternal-sorrow
Copy link

Doesn't work for me. When I hit keyboard layout switch shortcut, label of module does not change (input layout changes though).

Also, would be nice to set it to output only short language code as 'en' or 'de' instead of full layout name.

@harishkrupo
Copy link
Contributor Author

harishkrupo commented Nov 5, 2018

@eternal-sorrow Thanks for trying!

Doesn't work for me. When I hit keyboard layout switch shortcut, label of module does not change (input layout changes though).

Okay, I am a bit confused here. I don't change keyboard layouts, so I am not very familiar with the topic. On the issues page, I was referred to https://github.com/swaywm/sway/wiki#input-configuration which contains documentation of how to change the input configuration.
Could you give me more information? Which DE/WM do you use and how do you change the layout? ( we need to figure out which command runs when you hit the layout switch key).

Also, would be nice to set it to output only short language code as 'en' or 'de' instead of full layout name.

Okay, will check if the library provides short names.

@eternal-sorrow
Copy link

I use sway 1.0 beta1. To switch keyboard layout, i've set up input section in my sway config like this:

input "my keyboard name" {
    xkb_layout us,ru
    xkb_options grp:win_space_toggle
}

And so, I switch layout with Super+Space shortcut.

@harishkrupo
Copy link
Contributor Author

@eternal-sorrow Could you also paste the bindsym Super+Space ... line?

@harishkrupo
Copy link
Contributor Author

also, by layout are you also referring to the qwerty/dvorak schemes?

@eternal-sorrow
Copy link

@eternal-sorrow Could you also paste the bindsym Super+Space ... line?

No such line in my config. xkb_options grp:win_space_toggle does that.

also, by layout are you also referring to the qwerty/dvorak schemes?

no, just Russian/English layout

@harishkrupo
Copy link
Contributor Author

Thanks, got it. let me check.

@@ -9,6 +10,7 @@ class IModule {
virtual ~IModule() = default;
virtual auto update() -> void = 0;
virtual operator Gtk::Widget &() = 0;
virtual void handleSeat(struct wl_seat*, uint32_t) {};
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like this way of doing it, why can't we use the existing dp.emit when we get the seat?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, agreed. This is a RFC patch. I was just trying out to see if we can get the keyboard layout.
Could you please explain how we can use the dp.emit() call to send the seat details?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can store the seat in the bar and pass bar as parameters to the module like sway workspaces and so when emit is called in the module, you just need to access the variable

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay, got it.
I am actually currently discussing with the folks on the #sway IRC, on how to receive the layout change event when the xkb_options key combination is used. I am not sure on how to receive the event without the client actually focused. Do you have any idea?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll look into it!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I searched for a solution. looks like this can be achieved only with a new protocol (or maybe there is an existing one?).
The compositor currently sends modifier events only to the client which has the focus. The keyboard layout group is sent as part of the modifier event. We would need a new protocol to register for such modifier events so that we can change the layout based on the received event.

@Alexays Were you able to find something?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the way to go for now is polling then? Kind of sucks to have a delay.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

poll what? unless we receive the modifiers from the compositor we will never know of the layout change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is one request in layer shell interface to set keyboard interactivity. Unfortunately, on setting it, all the keyboard events are sent only to the bar as it is the topmost :(

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

poll what? unless we receive the modifiers from the compositor we will never know of the layout change.

Ah, okay, my assumption was that the information about the current layout could be queried somewhere, but upon further reading it appears that it's managed by Sway and, right now, isn't exposed at all. Bummer.

@harishkrupo
Copy link
Contributor Author

I have added a new request to wlroots's layer shell v1 interface (patch available here.) This request informs the compositor of our intent to receive keyboard modifiers. When we receive the modifiers, we update our xkb state (specifically the group state) and then call emit. When we query for the layout in the update, it will show the new layout.
The wlroot's patch is a POC. I will clean up the patches and send it to the sway community for review. Hopefully, they will suggest how to properly implement it or provide an alternate solution to receive the keyboard modifiers.
@Alexays In the mean time, please feel free to review it. :)

@harishkrupo
Copy link
Contributor Author

Also, I am yet to incorporate your reviews in the patch. Will do that once I have a proper plan on the solution (either via the protocol or something different)

@Alexays
Copy link
Owner

Alexays commented Nov 7, 2018

Wow such a contribution thank you <3

This patch adds a new module to show the current keyboard layout.

Signed-off-by: Harish Krupo <[email protected]>
@harishkrupo
Copy link
Contributor Author

harishkrupo commented Nov 13, 2018

Update: Discussions about the new protocol are still ongoing.
In the mean time, I got a patch merged into sway which sends the keyboard layout via the IPC_GET_INPUTS. See: swaywm/sway@d8ad429
If we were to use this IPC call, then we will have to poll for the information, which I don't like. I strongly prefer using the protocol to get this information, but if that doesn't go according to plan then we will fall back to this.

@harishkrupo
Copy link
Contributor Author

harishkrupo commented Nov 14, 2018

@eternal-sorrow If you need it now, then update to the latest sway and add this script to a custom module:

swaymsg -t get_inputs | jq '.[]  | select(.identifier == "1241:5890:USB_Keyboard") | .xkb_active_layout_name'

Replace "1241:5890:USB_Keyboard" with your keyboard identifier and install jq.

@eternal-sorrow
Copy link

eternal-sorrow commented Nov 14, 2018

update to the latest sway

I'll wait till beta.2 release.

@alecmev
Copy link
Contributor

alecmev commented Nov 20, 2018

Adjusted the script slightly:

#!/usr/bin/env bash

while true; do
  x="`swaymsg -t get_inputs | jq --raw-output '[.[] | select(.type == "keyboard") | .xkb_active_layout_name | select(contains("English (US)") | not)] | first'`"
  if [[ "${x}" == "null" ]]; then
    echo English
  else
    echo "${x}"
  fi

  sleep 1
done

Drops the need to hardcode your keyboard ID, and also a bit more efficient than having Waybar launch a shell every N seconds. Change "English (US)" to your default. Used like so:

"custom/layout": {
  "exec": "path/to/script/above"
}

No interval. Sharing this for copy-pasting.

Edit: Looked into registering on-click/on-scroll layout switching, but Sway's current state of the art is this:

swaymsg -t 'command' 'input <device_id> xkb_layout <layout_name>'

Can't even set it back to a comma-separated list. So you must maintain your own keyboard-layout-managing script, which takes care of finding out what is the currently active keyboard, what layouts are needed, and what layout to switch to. Basically what XKB does, way too much ad hoc code.

@alecmev
Copy link
Contributor

alecmev commented Aug 27, 2019

Sway 1.2 (released 5 hours ago) brings this goodie (thanks, @RedSoxFan!), allowing us to finally get rid of polling (props to @asahaf for spotting this here). It makes the script slightly more complicated:

#!/usr/bin/env bash

swaymsg \
  --type get_inputs | \
  jq \
    --raw-output \ '
      [
        .[] |
          select(.type == "keyboard") |
          .xkb_active_layout_name |
          select(contains("English (US)") | not)
      ] |
        first // "English"
    '

swaymsg \
  --type subscribe \
  --monitor \
  --raw \
  '["input"]' | \
  jq \
    --raw-output \
    --unbuffered \ '
      select(.change == "xkb_layout") |
        .input.xkb_active_layout_name |
        sub(" \\(US\\)"; "")
    '

... but is so worth it! No more electronic ink feel.

As before, adjust accordingly if English (US) is not your default.

@eternal-sorrow
Copy link

eternal-sorrow commented Aug 29, 2019

here's my python variant (you'll need i3ipc-python >=2.2.1)

#!/usr/bin/env -S python3 -u
# vi:syntax=python

import i3ipc

sway = i3ipc.Connection()

# this script works with only one keyboard. we find it's ID.
# it should have more than one layout
for input_dev in sway.get_inputs():
	if input_dev.type == 'keyboard' and len(input_dev.xkb_layout_names) > 1:
		keyboard = input_dev.identifier
		print(input_dev.xkb_active_layout_name[:2].upper())
		break

def on_input(sway, event):
	if event.input.identifier == keyboard:
		print(event.input.xkb_active_layout_name[:2].upper())

sway.on('input::xkb_layout', on_input)
sway.main()

@ghost
Copy link

ghost commented Oct 15, 2019

Thanks @jeremejevs. Here is my variant:

  • supports multiple keyboards (via identifier)
  • independ of layout name
  • output is 2 first letters of layout name (ex EN)

Module (Note: you can replace ascii_upcase with ascii_downcase to get output in LC):

#!/bin/bash

swaymsg -t get_inputs | jq -r \
    "first(.[]|select(.identifier == \"$1\" and .type == \"keyboard\")) \
    | .xkb_active_layout_name \
    | .[0:2] \
    | ascii_upcase"

swaymsg -mrt subscribe '["input"]' | jq -r --unbuffered \
    "select(.change == \"xkb_layout\")
    | .input
    | select(.identifier == \"$1\" and .type == \"keyboard\") \
    | .xkb_active_layout_name \
    | .[0:2] \
    | ascii_upcase"

Usage (pass identifier as argument):

    "custom/layout": {
        "format": " {}",
        "exec": "~/.config/waybar/modules/kblayout '1:1:AT_Translated_Set_2_keyboard'",
        "tooltip": false
    },
    "custom/layout_ext": {
        "format": " {}",
        "exec": "~/.config/waybar/modules/kblayout '3141:30219:SONiX_USB_Keyboard'",
        "exec-if": "swaymsg -t get_inputs | grep '3141:30219:SONiX_USB_Keyboard'",
        "tooltip": false
    }

@crabvk
Copy link

crabvk commented Dec 1, 2019

Thanks @jeremejevs and @M1s4k1. Made a little change to show emoji flags

#!/usr/bin/env fish

set icons '["🇺🇸", "🇷🇺"]'

swaymsg -t get_inputs | jq -rcM --argjson icons $icons \
    "first(.[] | select(.identifier == \"$argv[1]\" and .type == \"keyboard\")) \
    | \$icons[.xkb_active_layout_index]"

swaymsg -mrt subscribe '["input"]' | jq -rcM --unbuffered --argjson icons $icons \
    "select(.change == \"xkb_layout\")
    | .input
    | select(.identifier == \"$argv[1]\" and .type == \"keyboard\") \
    | \$icons[.xkb_active_layout_index]"

I also tried to return '{"percentage": 0}' (and 1) from jq with config:

"custom/kblayout": {
    "format": "{icon}",
    "exec": "kblayout.fish '1:1:AT_Translated_Set_2_keyboard'",
    "return-type": "json",
    "format-icons": ["🇺🇸", "🇷🇺"],
    "tooltip": false
}

but it doesn't get updates from swaymsg -mrt subscribe ..., maybe a 🐞 in waybar.

PS, don't forget to add pkill -9 -f kblayout.fish to your ~/.config/waybar/waybar.sh

@Alexays
Copy link
Owner

Alexays commented Apr 11, 2020

Superseded by #659

@Alexays Alexays closed this Apr 11, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants