#!/usr/bin/env python3
"""keyboard-lock: grab all keyboard devices for N seconds to prevent accidental input.

Usage: keyboard-lock <15|30|60>
  Ctrl+U to cancel early.

Requires read/write access to /dev/input/* — either be in the 'input' group
or run with sudo.
"""

import sys
import time
import select
import evdev
from evdev import InputDevice, ecodes


def find_keyboards():
    keyboards = []
    for path in evdev.list_devices():
        try:
            dev = InputDevice(path)
            caps = dev.capabilities()
            keys = caps.get(ecodes.EV_KEY, [])
            # Must have alphabetic keys and space — filters out mice, joysticks, etc.
            if ecodes.KEY_A in keys and ecodes.KEY_SPACE in keys:
                keyboards.append(dev)
        except Exception:
            pass
    return keyboards


def lock_keyboard(duration: int):
    keyboards = find_keyboards()
    if not keyboards:
        sys.exit(
            "Error: no keyboard devices found.\n"
            "Ensure you are in the 'input' group or run with sudo."
        )

    grabbed = []
    for kb in keyboards:
        try:
            kb.grab()
            grabbed.append(kb)
        except PermissionError:
            for g in grabbed:
                try:
                    g.ungrab()
                except Exception:
                    pass
            sys.exit(
                f"Permission denied for {kb.path}.\n"
                f"Run:  sudo usermod -aG input $USER  then log out and back in,\n"
                f"or prefix the command with sudo."
            )

    def release_all():
        for kb in grabbed:
            try:
                kb.ungrab()
            except Exception:
                pass

    names = ", ".join(kb.name for kb in grabbed)
    print(f"Grabbed: {names}")
    print(f"[LOCKED] {duration}s  |  Ctrl+U to unlock early\n")

    ctrl_keys_held = set()
    deadline = time.monotonic() + duration
    bar_width = 30

    try:
        while True:
            remaining = deadline - time.monotonic()
            if remaining <= 0:
                break

            filled = int(bar_width * (1 - remaining / duration))
            bar = "\u2588" * filled + "\u2591" * (bar_width - filled)
            print(f"\r  [{bar}]  {remaining:5.1f}s  ", end="", flush=True)

            readable, _, _ = select.select(grabbed, [], [], 0.1)
            for dev in readable:
                try:
                    for event in dev.read():
                        if event.type != ecodes.EV_KEY:
                            continue
                        is_ctrl = event.code in (
                            ecodes.KEY_LEFTCTRL,
                            ecodes.KEY_RIGHTCTRL,
                        )
                        if is_ctrl:
                            if event.value != 0:   # pressed or repeat
                                ctrl_keys_held.add(event.code)
                            else:                  # released
                                ctrl_keys_held.discard(event.code)
                        elif (
                            event.code == ecodes.KEY_U
                            and event.value == 1   # key-down only
                            and ctrl_keys_held
                        ):
                            release_all()
                            print("\n\n[UNLOCKED] Cancelled early.")
                            return
                except Exception:
                    pass

    except KeyboardInterrupt:
        pass  # Ctrl+C from the terminal won't reach here while grabbed, but just in case

    release_all()
    print("\n\n[UNLOCKED] Done — keyboard restored.")


def main():
    valid = {"15", "30", "60"}
    if len(sys.argv) != 2 or sys.argv[1] not in valid:
        print(f"Usage: keyboard-lock <15|30|60>")
        print(f"  Locks the keyboard for the given number of seconds.")
        print(f"  Press Ctrl+U to cancel early.")
        sys.exit(1)

    lock_keyboard(int(sys.argv[1]))


if __name__ == "__main__":
    main()

Usage

  1. Save to .local/bin/keyboard-lock
  2. Make executable
chmod +x /home/<user>/.local/bin/keyboard-lock
  1. Join the input group:
sudo usermod -aG input $USER
  1. Log out and in, or activate the group in current shell with newgrp input
  2. Finally use with desired timer:
keyboard-lock 15      # 15 seconds
keyboard-lock 30      # 30 seconds
keyboard-lock 60      # 60 seconds
# Can be cancelled early with Ctrl+U