macro_recorder: basic linux macro recording

main
Ilya Zhuravlev 2020-12-24 09:13:20 -05:00
parent cb9e0c90cd
commit abf62439e4
3 changed files with 185 additions and 5 deletions

View File

@ -1,15 +1,20 @@
# SPDX-License-Identifier: GPL-2.0-or-later
import sys import sys
import keyboard import keyboard
def key_cb(key): def key_cb(key):
print(key) names = keyboard._nixkeyboard.to_name[(key.scan_code, ())] or ["unknown"]
name = names[0]
name = keyboard.normalize_name(name)
sys.stdout.write("{}:{}\n".format(key.event_type, name))
sys.stdout.flush()
def linux_keystroke_recorder(): def linux_keystroke_recorder():
keyboard.hook(key_cb) keyboard.hook(key_cb)
print("Recording")
while True: while True:
ch = sys.stdin.read(1) ch = sys.stdin.read(1)
if ch == "q": if ch == "q":

View File

@ -0,0 +1,38 @@
# SPDX-License-Identifier: GPL-2.0-or-later
from keycodes import keycode_label
class KeyDown:
def __init__(self, keycode):
self.keycode = keycode
def __repr__(self):
return "Down({})".format(keycode_label(self.keycode.code))
class KeyUp:
def __init__(self, keycode):
self.keycode = keycode
def __repr__(self):
return "Up({})".format(keycode_label(self.keycode.code))
class KeyTap:
def __init__(self, keycode):
self.keycode = keycode
def __repr__(self):
return "Tap({})".format(keycode_label(self.keycode.code))
class KeyString:
def __init__(self, string):
self.string = string
def __repr__(self):
return "SendString({})".format(self.string)

View File

@ -1,21 +1,116 @@
# SPDX-License-Identifier: GPL-2.0-or-later
import sys import sys
from PyQt5 import QtCore from PyQt5 import QtCore
from PyQt5.QtCore import QProcess from PyQt5.QtCore import QProcess, pyqtSignal
from PyQt5.QtWidgets import QPushButton, QWidget, QApplication, QVBoxLayout from PyQt5.QtWidgets import QPushButton, QWidget, QApplication, QVBoxLayout
from fbs_runtime.application_context import is_frozen from fbs_runtime.application_context import is_frozen
from basic_editor import BasicEditor from basic_editor import BasicEditor
from keycodes import KEYCODES_BASIC
from macro_key import KeyDown, KeyUp
from util import tr from util import tr
from vial_device import VialKeyboard from vial_device import VialKeyboard
class LinuxRecorder(QWidget): class LinuxRecorder(QWidget):
keystroke = pyqtSignal(object)
stopped = pyqtSignal()
mapping_qmk_id = {
"esc": "KC_ESCAPE",
"1": "KC_1",
"2": "KC_2",
"3": "KC_3",
"4": "KC_4",
"5": "KC_5",
"6": "KC_6",
"7": "KC_7",
"8": "KC_8",
"9": "KC_9",
"0": "KC_0",
"-": "KC_MINUS",
"=": "KC_EQUAL",
"backspace": "KC_BSPACE",
"tab": "KC_TAB",
"q": "KC_Q",
"w": "KC_W",
"e": "KC_E",
"r": "KC_R",
"t": "KC_T",
"y": "KC_Y",
"u": "KC_U",
"i": "KC_I",
"o": "KC_O",
"p": "KC_P",
"[": "KC_LBRACKET",
"]": "KC_RBRACKET",
"enter": "KC_ENTER",
"ctrl": "KC_LCTRL",
"a": "KC_A",
"s": "KC_S",
"d": "KC_D",
"f": "KC_F",
"g": "KC_G",
"h": "KC_H",
"j": "KC_J",
"k": "KC_K",
"l": "KC_L",
";": "KC_SCOLON",
"'": "KC_QUOTE",
"`": "KC_GRAVE",
"shift": "KC_LSHIFT",
"\\": "KC_BSLASH",
"z": "KC_Z",
"x": "KC_X",
"c": "KC_C",
"v": "KC_V",
"b": "KC_B",
"n": "KC_N",
"m": "KC_M",
",": "KC_COMMA",
".": "KC_DOT",
"/": "KC_SLASH",
"alt": "KC_LALT",
"space": "KC_SPACE",
"caps lock": "KC_CAPSLOCK",
"f1": "KC_F1",
"f2": "KC_F2",
"f3": "KC_F3",
"f4": "KC_F4",
"f5": "KC_F5",
"f6": "KC_F6",
"f7": "KC_F7",
"f8": "KC_F8",
"f9": "KC_F9",
"f10": "KC_F10",
"f11": "KC_F11",
"f12": "KC_F12",
"num lock": "KC_NUMLOCK",
"scroll lock": "KC_SCROLLLOCK",
"break": "KC_PAUSE",
"home": "KC_HOME",
"up": "KC_UP",
"page up": "KC_PGUP",
"left": "KC_LEFT",
"right": "KC_RIGHT",
"end": "KC_END",
"down": "KC_DOWN",
"page down": "KC_PGDOWN",
"insert": "KC_INSERT",
"delete": "KC_DELETE",
"pause": "KC_PAUSE",
"windows": "KC_LGUI",
"menu": "KC_APPLICATION",
}
mapping = dict()
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.process = None self.process = QProcess()
self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.X11BypassWindowManagerHint) self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.X11BypassWindowManagerHint)
@ -32,7 +127,6 @@ class LinuxRecorder(QWidget):
center = QApplication.desktop().availableGeometry(self).center() center = QApplication.desktop().availableGeometry(self).center()
self.move(center.x() - self.width() * 0.5, 0) self.move(center.x() - self.width() * 0.5, 0)
self.process = QProcess()
args = [sys.executable] args = [sys.executable]
if is_frozen(): if is_frozen():
args += sys.argv[1:] args += sys.argv[1:]
@ -40,11 +134,43 @@ class LinuxRecorder(QWidget):
args += sys.argv args += sys.argv
args += ["--linux-recorder"] args += ["--linux-recorder"]
self.process.readyReadStandardOutput.connect(self.on_output)
self.process.start("pkexec", args, QProcess.Unbuffered | QProcess.ReadWrite) self.process.start("pkexec", args, QProcess.Unbuffered | QProcess.ReadWrite)
def on_stop(self): def on_stop(self):
self.process.write(b"q") self.process.write(b"q")
self.process.waitForFinished()
self.process.close()
self.hide() self.hide()
self.stopped.emit()
def action2cls(self, action):
if action == "up":
return KeyUp
elif action == "down":
return KeyDown
else:
raise RuntimeError("unexpected action={}".format(action))
def key2code(self, key):
return self.mapping.get(key, None)
def on_output(self):
if self.process.canReadLine():
line = bytes(self.process.readLine()).decode("utf-8")
action, key = line.strip().split(":")
code = self.key2code(key)
if code is not None:
self.keystroke.emit(self.action2cls(action)(code))
for linux, qmk in LinuxRecorder.mapping_qmk_id.items():
for k in KEYCODES_BASIC:
if k.qmk_id == qmk:
LinuxRecorder.mapping[linux] = k
break
if linux not in LinuxRecorder.mapping:
raise RuntimeError("Misconfigured - cannot determine QMK keycode value for {}:{} pair".format(linux, qmk))
class MacroRecorder(BasicEditor): class MacroRecorder(BasicEditor):
@ -52,7 +178,11 @@ class MacroRecorder(BasicEditor):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.keystrokes = []
self.recorder = LinuxRecorder() self.recorder = LinuxRecorder()
self.recorder.keystroke.connect(self.on_keystroke)
self.recorder.stopped.connect(self.on_stop)
self.recording = False self.recording = False
btn = QPushButton("Record") btn = QPushButton("Record")
@ -68,4 +198,11 @@ class MacroRecorder(BasicEditor):
return return
def on_record_clicked(self): def on_record_clicked(self):
self.keystrokes = []
self.recorder.start() self.recorder.start()
def on_stop(self):
print(self.keystrokes)
def on_keystroke(self, keystroke):
self.keystrokes.append(keystroke)