Add IPC method to query keyboard layout
For example, swaymsg is currently the only tool that supports querying the keyboard layout. It likely uses IPC for this functionality. It would be a nice feature if Wayfire supported it in a similar way.
since we can get keyboard info using ipc get option value
❯❯❯ wfctl get keyboard
Layout: {'default': 'us', 'result': 'ok', 'value': 'brc'}, Variant: {'default': '', 'result': 'ok', 'value': 'intl'}
it's outside wayfire scope to get keyboard layout from the system besides internal conf, so I am closing this enhancement request.
I plan to add this anyway :) We want a way to get the current layout.
I really need to get the current layout to write a script to output the layout to the bar via the command-output widget. I found only one solution and it is not the best.
wayfire.ini
[autostart]
command_reset_layout_file = echo 0 > ~/.config/layout_indicator/current_layout_index
[command]
binding_change_layout_file =
wf-shell.ini [panel] command_output_2 = ~/.config/layout_indicator/show_layout command_output_period_2 = 1 command_output_icon_2 = keyboard
!/bin/bash ~/.config/layout_indicator/show-layout
INDEX=$(cat ~/.config/layout_indicator/current_layout_index 2>/dev/null || echo 0) LAYOUTS=$(crudini --get ~/.config/wayfire.ini input xkb_layout) IFS=',' read -ra LAYOUTS_ARRAY <<< "$LAYOUTS" echo "${LAYOUTS_ARRAY[$INDEX]}" | tr '[:lower:]' '[:upper:]'
~/.config/layout_indicator/change_layout_index
LAYOUTS=$(crudini --get ~/.config/wayfire.ini input xkb_layout 2>/dev/null) IFS=',' read -ra LAYOUTS_ARRAY <<< "$LAYOUTS" TOTAL_LAYOUTS=${#LAYOUTS_ARRAY[@]} # Общее количество раскладок CURRENT_INDEX=$(cat ~/.config/layout_indicator/current_layout_index 2>/dev/null || echo 0) NEW_INDEX=$(( (CURRENT_INDEX + 1) % TOTAL_LAYOUTS )) echo $NEW_INDEX > ~/.config/layout_indicator/current_layout_index setxkbmap ${LAYOUTS_ARRAY[$NEW_INDEX]}
I really need to get the current layout to write a script to output the layout to the bar via the command-output widget. I found only one solution and it is not the best.
wayfire.ini [autostart] command_reset_layout_file = echo 0 > ~/.config/layout_indicator/current_layout_index [command] binding_change_layout_file = KEY_SPACE command_change_layout_file = ~/.config/layout_indicator/change_layout_index
wf-shell.ini [panel] command_output_2 = ~/.config/layout_indicator/show_layout command_output_period_2 = 1 command_output_icon_2 = keyboard
!/bin/bash ~/.config/layout_indicator/show-layout
INDEX=$(cat ~/.config/layout_indicator/current_layout_index 2>/dev/null || echo 0) LAYOUTS=$(crudini --get ~/.config/wayfire.ini input xkb_layout) IFS=',' read -ra LAYOUTS_ARRAY <<< "$LAYOUTS" echo "${LAYOUTS_ARRAY[$INDEX]}" | tr '[:lower:]' '[:upper:]'
~/.config/layout_indicator/change_layout_index
LAYOUTS=$(crudini --get ~/.config/wayfire.ini input xkb_layout 2>/dev/null) IFS=',' read -ra LAYOUTS_ARRAY <<< "$LAYOUTS" TOTAL_LAYOUTS=${#LAYOUTS_ARRAY[@]} # Общее количество раскладок CURRENT_INDEX=$(cat ~/.config/layout_indicator/current_layout_index 2>/dev/null || echo 0) NEW_INDEX=$(( (CURRENT_INDEX + 1) % TOTAL_LAYOUTS )) echo $NEW_INDEX > ~/.config/layout_indicator/current_layout_index setxkbmap ${LAYOUTS_ARRAY[$NEW_INDEX]}
what we have for now is:
pip install wayfire
Then:
from wayfire import WayfireSocket
sock = WayfireSocket()
In [4]: sock.get_option_value("input/xkb_layout")
Out[4]: {'result': 'ok', 'value': 'br', 'default': 'us'}
In [6]: sock.get_option_value("input/xkb_rules")
Out[6]: {'result': 'ok', 'value': 'evdev', 'default': 'evdev'}
In [7]: sock.get_option_value("input/xkb_model")
Out[7]: {'result': 'ok', 'value': 'br', 'default': ''}
this is an interesting workaround for the time being, bind super+space for example as a normal binding which changes the keyboard layout globally .. not perfect but maybe it is enough for the moment (a proper ipc method will still be implemented for the next release ofc).
I’ll leave this example here for newcomers until we implement the proper method in the next release. This way, they can still bind the script to Super+Space.
from wayfire.ipc import WayfireSocket
def cycle_keyboard_layout(sock):
"""Cycles between 'br' and 'us' keyboard layouts"""
current = sock.get_option_value("input/xkb_layout")["value"]
new_layout = "us" if current == "br" else "br"
sock.set_option_values({"input/xkb_layout": new_layout})
print(f"Switched from {current} to {new_layout}")
return new_layout
# Usage:
sock = WayfireSocket()
print("Current layout:", sock.get_option_value("input/xkb_layout")["value"])
cycle_keyboard_layout(sock) # First switch
cycle_keyboard_layout(sock) # Second switch (returns to original)
@killown This script doesn't work. After I run it, I'm no longer able to switch the keyboard layout using the "Alt+Shift" shortcut as usual. I have to log out or reboot to restore normal behavior.
@killown This script doesn't work. After I run it, I'm no longer able to switch the keyboard layout using the "Alt+Shift" shortcut as usual. I have to log out or reboot to restore normal behavior.
This script is like changing wayfire.ini [input] xkb_layout = us, without the need to touch the config file
This work
https://github.com/user-attachments/assets/8d1c4b9c-5dc0-4ba4-8833-611671e8a34a
but there is a bug in wayfire or at least the lack of unset_option in ipc, if you set_option once and try to change in wayfire.ini, the config change won't take effect
@killown you can add a custom binding from the script, so that users don't have to do anything but start the script :) just an idea
@killown This script doesn't work. After I run it, I'm no longer able to switch the keyboard layout using the "Alt+Shift" shortcut as usual. I have to log out or reboot to restore normal behavior.
you can use this also, so you don't have to log out or reboot to restore normal behavior
import os
import mmap
def cycle_keyboard_layout():
config_path = os.path.expanduser("~/.config/wayfire.ini")
with open(config_path, 'r+') as f:
with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_WRITE) as mm:
match = mm.find(b'xkb_layout')
if match != -1:
line_start = mm.rfind(b'\n', 0, match) + 1
line_end = mm.find(b'\n', match)
current = mm[line_start:line_end].decode().split('=')[1].strip()
new_layout = "us" if current == "br" else "br"
mm[line_start:line_end] = f"xkb_layout = {new_layout}".ljust(line_end - line_start).encode()
else:
new_layout = "us"
mm.seek(0, 2)
if mm.size() > 0 and mm[-1:] != b'\n':
mm.write(b'\n')
mm.write(b'[input]\nxkb_layout = ' + new_layout.encode() + b'\n')
return new_layout
cycle_keyboard_layout()
Both script doesn't work for me.
The first one does the same thing, after running it, the function xkb_layout stop to work:
#!/usr/bin/python3
from wayfire.ipc import WayfireSocket
def cycle_keyboard_layout(sock):
"""Cycles between 'ca' and 'us' keyboard layouts"""
current = sock.get_option_value("input/xkb_layout")["value"]
new_layout = "us" if current == "ca" else "ca"
sock.set_option_values({"input/xkb_layout": new_layout})
print(f"Switched from {current} to {new_layout}")
return new_layout
sock = WayfireSocket()
print("Current layout:", sock.get_option_value("input/xkb_layout")["value"])
cycle_keyboard_layout(sock)
https://github.com/user-attachments/assets/bcc2f260-815a-45a4-b4ba-60967247426f
The second one change the file wayfire.ini To this "xkb_layout = us" or this "xkb_layout = ca", but in order to have my keyboard to work for "ca", I need this line "xkb_layout = us, ca"
#!/usr/bin/python3
import os
import mmap
def cycle_keyboard_layout():
config_path = os.path.expanduser("~/.config/wayfire.ini")
with open(config_path, 'r+') as f:
with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_WRITE) as mm:
match = mm.find(b'xkb_layout')
if match != -1:
line_start = mm.rfind(b'\n', 0, match) + 1
line_end = mm.find(b'\n', match)
current = mm[line_start:line_end].decode().split('=')[1].strip()
new_layout = "us" if current == "ca" else "ca"
mm[line_start:line_end] = f"xkb_layout = {new_layout}".ljust(line_end - line_start).encode()
else:
new_layout = "us"
mm.seek(0, 2)
if mm.size() > 0 and mm[-1:] != b'\n':
mm.write(b'\n')
mm.write(b'[input]\nxkb_layout = ' + new_layout.encode() + b'\n')
return new_layout
cycle_keyboard_layout()
@bluebyt @mrfoggg @killown check my latest wayfire and pywayfire PRs, you can get keyboard layout with the new methods. You can also watch for the keyboard-state-changed event, it will be emitted on every modifier however so it is a bit often. We don't emit lots of data so it should be fine however.
if you merge both prs, it will solve #2419
awesome ammen, thanks!
https://github.com/user-attachments/assets/8e5455e7-43a6-4ea2-b2bf-f27e22ba8c2d