This is useful when using remote-desktop applications (e.g. VNC/RDP client). By following this guide, you'll be able to use the keyboard shortcut Left Ctrl + Right Ctrl to capture/release the dom0 keyboard by the qube to which the currently focused window belongs to.
Install qubes-input-proxy-receiver package in the template of a qube that you want to be able to capture the dom0 keyboard.
In dom0 install python3-evdev package:
sudo qubes-dom0-update python3-evdev
Create a python program in dom0 that will capture and release the keyboard:
mkdir -p /home/user/bin
cat << 'EOF' | tee /home/user/bin/kbcapture.py > /dev/null
#!/usr/bin/python3
# First argument is a path to a keyboard input device e.g. /dev/input/eventX
import evdev
import subprocess
import re
import sys
def notify_send(summary, body):
display = ':'+subprocess.check_output(['ls', '/tmp/.X11-unix/']).decode('utf-8').replace('X', '')[0]
who = subprocess.check_output(['who']).decode('utf-8')
user = [line for line in who.split('\n') if f'({display})' in line][0].split(None, 1)[0]
uid = subprocess.check_output(['id', '-u', user]).decode('utf-8').rstrip()
subprocess.Popen(['sudo', '-u', user, f'DISPLAY={display}', f'DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/{uid}/bus', 'notify-send', summary, body])
if len(sys.argv) == 1:
notify_send('Keyboard capture ERROR', f"Specify the path to a keyboard input device as an argument.")
sys.exit(1)
window = subprocess.check_output(['xdotool', 'getwindowfocus'])
try:
qube_name = re.findall('"([^"]*)"', subprocess.check_output(['xprop', '_QUBES_VMNAME', '-id', window]).decode('utf-8'))[0]
except:
notify_send('Keyboard capture ERROR', f"Couldn't get the qube's name from the focused window.")
sys.exit(1)
try:
kbd = evdev.InputDevice(sys.argv[1])
except:
notify_send('Keyboard capture ERROR', f"Couldn't open the keyboard input device. Make sure that you passed the correct path as an argument.")
sys.exit(1)
kbd.grab()
RCtrl = False
LCtrl = False
with evdev.UInput.from_device(kbd, name='kbdcapture') as ui:
p=subprocess.Popen(['qvm-run', '-u', 'root', '--pass-io', f'--localcmd=input-proxy-sender {ui.device.path}', qube_name, 'input-proxy-receiver --keyboard'], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
notify_send('Keyboard capture', f'dom0 keyboard is captured by the qube {qube_name}')
for ev in kbd.read_loop():
if ev.type == evdev.ecodes.EV_KEY:
if (ev.code == evdev.ecodes.KEY_LEFTCTRL and ev.value == 1 and RCtrl or
ev.code == evdev.ecodes.KEY_RIGHTCTRL and ev.value == 1 and LCtrl):
p.terminate()
notify_send('Keyboard capture', f'dom0 keyboard is released from the qube {qube_name}')
kbd.ungrab()
break
else:
ui.write(evdev.ecodes.EV_KEY, ev.code, ev.value)
LCtrl = (ev.code == evdev.ecodes.KEY_LEFTCTRL and ev.value)
RCtrl = (ev.code == evdev.ecodes.KEY_RIGHTCTRL and ev.value)
else:
ui.write(ev.type, ev.code, ev.value)
EOF
chmod +x /home/user/bin/kbcapture.py
Determine the path to the keyboard input device in dom0 (/dev/input/eventX). Run this command in dom0 to list all available input devices:
sudo libinput list-devices
Capabilities: keyboard corresponding to your keyboard, for example:
...
Device: AT Translated Set 2 keyboard
Kernel: /dev/input/event2
Group: 3
Seat: seat0, default
Capabilities: keyboard
Tap-to-click: n/a
Tap-and-drag: n/a
Tap drag lock: n/a
Left-handed: n/a
Nat.scrolling: n/a
Middle emulation: n/a
Calibration: n/a
Scroll methods: none
Click methods: none
Disable-w-typing: n/a
Disable-w-trackpointing: n/a
Accel profiles: n/a
Rotation: n/a
...
dev/input/event2 here is the path that you'll need.
Configure keyboard shortcut to run this program:
Open Qubes App Menu Q -> Gear icon -> System Settings -> Keyboard -> Application Shortcuts -> Add button
Set command to this one, but change /dev/input/eventX to the correct path of your keyboard input device:
sudo /home/user/bin/kbcapture.py /dev/input/eventX
Related github feature request: https://github.com/QubesOS/qubes-issues/issues/9785
If you need to allow the temporary VM keyboard capture for HVM Windows qubes or qubes without qubes tools installed, you can use this solution instead: https://forum.qubes-os.org/t/mouse-and-keyboard-passthrough-to-windows-hvm/16905