Comment verrouiller le clavier pour éviter que d’autres pressions de touches soient envoyées sur X11 / Linux / Gnome?

J’écris un programme anti-RSI / pause de frappe pour Ubuntu Linux en python. Je voudrais pouvoir “verrouiller le clavier” pour que toutes les pressions soient ignorées jusqu’à ce que je le “déverrouille”. Je veux être capable de forcer l’utilisateur à faire une pause de frappe.

Je voudrais un moyen programmatique pour “éteindre” le clavier (presque instantanément) jusqu’à ce que mon programme le libère plus tard (qui pourrait être 0.1 sec → 10 secondes plus tard). Alors que j’ai “éteint le clavier”, aucune pression sur une touche ne doit être envoyée à une fenêtre, à un gestionnaire de fenêtre, etc. De préférence, l’écran doit toujours afficher le même contenu. Le clavier doit être verrouillé même si ce programme n’est pas à l’avant-garde et n’a pas le focus.

Certains programmes peuvent déjà le faire (par exemple, Work Rave)

Comment est-ce que je fais ceci sous Linux / X11? (Préférable en Python)

Cela peut être fait facilement avec un script shell utilisant xinput:

#!/bin/sh do_it() { # need error checking there. We should also ressortingct which device gets # deactivated, by checking other properties. keyboard_ids="$(xinput list | sed -rn 's/.*id=([0-9]+).*slave\s+keyboard.*/\1/p')" for keyboard_id in $keyboard_ids; do # 121 is "Device Active". # use xinput watch-props $device_id to see some properties. xinput set-int-prop $keyboard_id 121 8 $1; done; } # you maybe don't want to exit in case of failure there. do_it 0 ; sleep 5; do_it 1 

Cette logique est facilement réinscriptible en Python. Si l’installation de xinput est problématique, il peut être judicieux de récupérer la source de xinput et d’essayer de la réimplémenter dans Python en utilisant une bibliothèque comme python-xlib.

Sur la base de cela , voici un code que j’ai proposé:

 class KeyboardLocker: def __init__(self, serio=0): self._on = False self.serio = serio def on(self): return self._on def write_value(self,path, value): with open(path, "a") as f: f.write(value) def toggle(self): if self.on(): self.turn_off() else: self.turn_on() def description(self): path = '/sys/devices/platform/i8042/serio%d/description' % (self.serio,) with open(path, "r") as f: description = f.read() return description def turn_on(self): try: self.write_value('/sys/devices/platform/i8042/serio%d/bind_mode' % (self.serio,), 'auto') except IOError, e: self._on = False raise else: self._on = True return self.on() def turn_off(self): try: self.write_value('/sys/devices/platform/i8042/serio%d/bind_mode' % (self.serio,), 'manual') self.write_value('/sys/devices/platform/i8042/serio%d/drvctl' % (self.serio,), 'psmouse') except IOError, e: self._on = True raise else: self._on = False return self.on() if __name__ == "__main__": kl = KeyboardLocker(serio=0) device = kl.description() print "We got a lock on", device proceed = raw_input("Do you want to proceed? (y/n)").lower().startswith("y") import sys if not proceed: sys.exit(1) kl.turn_off() import time wait = 5 print "Sleeping few seconds...", wait time.sleep(wait) print "Voila!" kl.turn_on() raw_input("Does it work now?") 

Testé sur Linux Mint 12, X11, portable HP, Gnome. Je ne sais pas si cela vous importe 🙂

UPDATE Ajout d’une option pour modifier le chemin, par exemple “serio0” ou “serio1”. Et imprime la description, pour moi serio0 m’a donné: i8042 KBD port , probablement si vous avez “KBD” dedans, c’est bon, continuez, sinon je ne vous donne aucune garantie 🙂

La manière canonique de le faire est de saisir l’entrée. Pour cela aucune fenêtre ne doit être réellement visible. Une fenêtre de saisie seulement fait généralement l’affaire. Cependant, vous devriez donner à l’utilisateur une sorte de retour, pourquoi sa consortingbution ne fonctionne plus. Faire cela en tant que capture de focus a l’avantage qu’une panne du programme ne rendra pas le système insensible.

BTW: Je pense que l’interruption forcée de l’utilisateur, peut-être au milieu d’une opération critique est un énorme non-Go! Je n’ai jamais compris le but de ces programmes. L’utilisateur sera assis devant l’écran au ralenti, perdant peut-être ses pensées. Juste mes 2 centimes.

Ce n’est pas mon code en passant

Cela empêche l’utilisateur d’appuyer sur la touche Win, la touche Maj ou la touche Alt, mais je suis sûr que vous pouvez également appliquer d’autres touches.

 Public Class KeyboardJammer Private Delegate Function HookCallback(ByVal nCode As Integer, ByVal wParam As Integer, ByVal lParam As IntPtr) As Integer Private Shared HookDelegate As HookCallback Private Shared HookId As Integer Private Const Wh_Keyboard_LL As Integer = 13 Private Const Vk_Tab As Integer = 9 Private Const Vk_Escape As Integer = 27 Private Const Vk_LWinKey As Integer = 91 Private Const Vk_RWinkKey As Integer = 92 Private Shared Function KeyBoardHookProc(ByVal nCode As Integer, ByVal wParam As Integer, ByVal lParam As IntPtr) As Integer 'All keyboard events will be sent here.' 'Dont process just pass along.' If nCode < 0 Then Return CallNextHookEx(HookId, nCode, wParam, lParam) End If 'Extract the keyboard structure from the lparam' 'This will contain the virtual key and any flags.' 'This is using the my.computer.keyboard to get the' 'flags instead' Dim KeyboardSruct As KBDLLHOOKSTRUCT = Marshal.PtrToStructure(lParam, GetType(KBDLLHOOKSTRUCT)) If KeyboardSruct.vkCode = Vk_Tab And My.Computer.Keyboard.AltKeyDown Then 'Alt Tab' Return 1 ElseIf KeyboardSruct.vkCode = Vk_Escape And My.Computer.Keyboard.CtrlKeyDown Then 'Control Escape' Return 1 ElseIf KeyboardSruct.vkCode = Vk_LWinKey Or KeyboardSruct.vkCode = Vk_RWinkKey Then If KeyboardSruct.vkCode = Vk_Tab Then 'Winkey Tab' Return 1 Else 'Winkey' Return 1 End If ElseIf KeyboardSruct.vkCode = Vk_Escape And My.Computer.Keyboard.AltKeyDown Then 'Alt Escape' Return 1 End If 'Send the message along' Return CallNextHookEx(HookId, nCode, wParam, lParam) End Function Public Shared Sub Jam() 'Add the low level keyboard hook' If HookId = 0 Then HookDelegate = AddressOf KeyBoardHookProc HookId = SetWindowsHookEx(Wh_Keyboard_LL, HookDelegate, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly.GetModules()(0)), 0) If HookId = 0 Then 'error' End If End If End Sub Public Shared Sub UnJam() 'Remove the hook' UnhookWindowsHookEx(HookId) End Sub  _ Private Shared Function CallNextHookEx( _ ByVal idHook As Integer, _ ByVal nCode As Integer, _ ByVal wParam As IntPtr, _ ByVal lParam As IntPtr) As Integer End Function  _ Private Shared Function SetWindowsHookEx( _ ByVal idHook As Integer, _ ByVal HookProc As HookCallback, _ ByVal hInstance As IntPtr, _ ByVal wParam As Integer) As Integer End Function  _ Private Shared Function UnhookWindowsHookEx(ByVal idHook As Integer) As Integer End Function Private Structure KBDLLHOOKSTRUCT Public vkCode As Integer Public scanCode As Integer Public flags As Integer Public time As Integer Public dwExtraInfo As IntPtr End Structure End Class 

Usage:

 KeyboardJammer.Jam()