4

Is it possible to turn on particular keyboard layout with just one key like Control_L or Shift_L without losing their main function? I've tried to make custom keybinding for command, for example, setxkbmap -layout en and edit it via dconf-editor binding it to just Control_L. The layout switches on, but usual shortcuts with Control_L stop working.

4
  • So how would your system know if this time when pressing the left Ctrl you actually want to use it as the Ctrl key and that time you want it to change the layout?
    – terdon
    Mar 1 at 19:10
  • 1
    @terdon With great difficulty if it's on press. With great ease if it's on release
    – Daniel T
    Mar 1 at 19:35
  • Pro tip: use Caps for this. One key, and doesn't conflict with anything useful. Mar 2 at 3:48
  • @HolyBlackCat Caps is fine, but as other common shortcuts it switches layouts cyclically, so you must know what the current layout is to be sure what layout you are switching to. My goal is to switch on particular layout before typing without thinking about the current one.
    – tzdm
    Mar 2 at 7:01

1 Answer 1

4

Here is a Python script to do that. You need sudo apt install python3-xlib.

#!/usr/bin/env python3
# Based off https://stackoverflow.com/a/22368676/10477326
from Xlib.display import Display
from Xlib import X
from Xlib.ext import record
from Xlib.protocol import rq
import subprocess

TARGET_KEY = 37 # CTRL
target_pressed = False
disp = None

def handler(reply):
    global target_pressed
    data = reply.data
    # Listen for all keys
    while len(data):
        event, data = rq.EventField(None).parse_binary_value(data, disp.display, None, None)
        typ = event.type
        if typ not in (X.KeyPress, X.KeyRelease):
            # Interrupt on mouse Ctrl+Zoom or Ctrl+Click. Thank you @terdon
            target_pressed = False
            continue
        detail = event.detail
        # If there was another key in the middle,
        # stop because it's another key combination and not just
        # the one key that we desire.
        if detail != TARGET_KEY:
            target_pressed = False
            continue
        # On KeyPress, set a flag to wait for a KeyRelease to make sure
        # no other keys will be pressed.
        if typ == X.KeyPress:
            target_pressed = True
            continue
        # Previously another key in the middle was detected.
        if not target_pressed:
            continue
        # Now we know only the TARGET_KEY was pressed and released without any other keys.
        # Run your command
        subprocess.run(["setxkbmap", "-layout", "en"])
        target_pressed = False

disp = Display()
root = disp.screen().root
ctx = disp.record_create_context(
    0,
    [record.AllClients],
    [{
        'core_requests': (0, 0),
        'core_replies': (0, 0),
        'ext_requests': (0, 0, 0, 0),
        'ext_replies': (0, 0, 0, 0),
        'delivered_events': (0, 0),
        'device_events': (X.KeyReleaseMask, X.ButtonReleaseMask),
        'errors': (0, 0),
        'client_started': False,
        'client_died': False,
    }],
)
disp.record_enable_context(ctx, handler)
disp.record_free_context(ctx)

while True:
    event = root.display.next_event()

You can then mark it as executable and add a link to it in startup applications. The main idea is to listen for all keypresses globally on all windows. Then I listen for any other keypresses in between to know if you want to switch the layout or use another shortcut. The target key is still sent to the original window all the time.

4
  • 2
    Clever! But this still breaks normal ctrl key function, right? You can't hold down ctrl and scroll to change zoom level in your browser, for example. Or hard refresh with ctrl+click. Or anything else involving ctrl but not another keypress.
    – terdon
    Mar 1 at 21:25
  • 1
    @terdon Thank you. I changed mouse ButtonRelease and ButtonPress to cancel things instead of being ignored
    – Daniel T
    Mar 1 at 21:31
  • 2
    Nice! Have a upvote!
    – terdon
    Mar 1 at 21:39
  • It works perfectly for me, thank you so much!
    – tzdm
    Mar 2 at 8:45

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .