Holding a modifier key #1618
Closed
user202729
started this conversation in
Ideas
Replies: 1 comment
-
For now, I use a hack to get it to work (NOTE this overwrites --- a/plover/machine/keyboard.py
+++ b/plover/machine/keyboard.py
@@ -6,12 +6,18 @@
import enum
from collections import OrderedDict
+from pathlib import Path
+from threading import Thread, Lock
+from queue import Queue
+import json
+import asyncio
from plover import _
from plover.machine.base import StenotypeBase
from plover.misc import boolean
from plover.oslayer.keyboardcontrol import KeyboardCapture
-import json
+from plover.steno import Stroke
+from plover.oslayer.config import CONFIG_DIR
# i18n: Machine name.
@@ -113,9 +119,50 @@ class Keyboard(StenotypeBase):
super().set_keymap(keymap)
self._update_bindings()
+ async def _send_stroke(self, index: int, stroke_on_hold, stroke_on_release):
+ #print("hi from task")
+ await asyncio.sleep(0.2)
+ okay = False
+ with self._lock:
+ if self._current_state_index == index:
+ okay = True
+ self._stroke_on_release = stroke_on_release
+ #else:
+ #print("Not sending stroke", stroke_on_hold, "because state changed with index", self._current_state_index, "vs", index)
+ if okay:
+ print("Sending stroke", stroke_on_hold)
+ self._notify(stroke_on_hold.keys())
+
+ def _thread_fn(self):
+ #print("running the loop")
+ self._loop.run_forever()
+ #print("thread returned")
+
def start_capture(self):
"""Begin listening for output from the stenotype machine."""
self._initializing()
+ self._current_state_index = 0
+ self._current_state = None
+ self._current_task = None
+ self._loop = asyncio.new_event_loop()
+ self._stroke_on_release = None
+ self._events = Queue()
+ self._lock = Lock()
+ self._thread_object = Thread(target=self._thread_fn)
+ self._thread_object.start()
+
+ # idea: hold TPWHR for holding shift etc.
+ self._special_actions = {}
+ import itertools
+ for i in itertools.product(
+ (Stroke(0), Stroke("T")),
+ (Stroke(0), Stroke("K")),
+ (Stroke(0), Stroke("A")),
+ (Stroke(0), Stroke("O")),
+ ):
+ s=sum(i, Stroke("PWHR"))
+ self._special_actions[s] = (s|Stroke("-FPLT"), s|Stroke("-RBGS"))
+
try:
self._keyboard_capture = KeyboardCapture(self._ready, self._error)
self._keyboard_capture.key_down = self._key_down
@@ -131,6 +178,9 @@ class Keyboard(StenotypeBase):
def stop_capture(self):
"""Stop listening for output from the stenotype machine."""
+ self._unhold()
+ self._events.put(None)
+ self._thread_object.join(timeout=1)
if self._keyboard_capture is not None:
self._is_suppressed = False
self._update_suppression()
@@ -145,10 +195,37 @@ class Keyboard(StenotypeBase):
def suppress_last_stroke(self, send_backspaces):
pass
+ def _unhold(self):
+ if self._stroke_on_release is not None:
+ print("Release --- Sending stroke", self._stroke_on_release)
+ self._notify(self._stroke_on_release.keys())
+ self._stroke_on_release = None
+
+ def _keys_to_stroke(self, keys):
+ return Stroke({self._bindings.get(k) for k in keys} - {None})
+
def _key_down(self, key):
"""Called when a key is pressed."""
assert key is not None
- self._down_keys.add(key)
+
+ if key in self._down_keys:
+ return
+
+ self._unhold()
+ with self._lock:
+ self._current_state_index += 1
+ self._down_keys.add(key)
+
+ if self._keys_to_stroke(self._down_keys) in self._special_actions:
+ stroke_on_hold, stroke_on_release=self._special_actions[self._keys_to_stroke(self._down_keys)]
+ #print("notice state change", self._keys_to_stroke(self._down_keys))
+ if self._current_task is not None:
+ self._current_task.cancel()
+ self._current_task = asyncio.run_coroutine_threadsafe(
+ (self._send_stroke(self._current_state_index, stroke_on_hold, stroke_on_release)),
+ self._loop)
+ #print("task created", self._current_task)
+
if self._first_up_chord_send:
self._chord_already_sent = False
else:
@@ -157,6 +234,8 @@ class Keyboard(StenotypeBase):
def _key_up(self, key):
"""Called when a key is released."""
assert key is not None
+ self._unhold()
+ self._current_state_index += 1
self._down_keys.discard(key)
to be used together with #!/bin/python3
from plover.system import english_stenotype as e
from plover_python_dictionary_lib import get_context_from_system
context=get_context_from_system(e)
s=context.SingleDictionary
stroke=context.stroke
translation=context.translation
dictionary=s({})
for d, t in [
("down", "-FPLT"),
("up", "-RBGS"),
("", ""),
]:
dictionary |= (
s({"T": f"Shift_L:{d} ", "": ""}) *
s({"K": f"Control_L:{d} ", "": ""}) *
s({"A": f"Alt_L:{d} ", "": ""}) *
s({"O": f"Super_L:{d} ", "": ""}) *
stroke("PWHR") *
stroke(t)
).map(lambda x, d=d: "{#" + x + "}" if d else "{#}")
lookup = lambda strokes: dictionary.lookup_tuple(strokes)
LONGEST_KEY = dictionary.longest_key
if __name__=="__main__":
dictionary.print_items() |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
If you use a NKRO machine instead of a machine using a serial protocol, Plover can know which keys are currently being held in addition to which strokes are sent.
Given this information, it's theoretically possible to make Plover do things like "if I hold down STK, hold down the Ctrl modifier until I release the stroke"
While a simple hack is possible (hard code the strokes into the keyboard machine) I'm thinking how to best design this feature. I think one possible design is as follows.
+
and!
denotes "enter" the "holding the stroke" state and exiting that state.{"STK+": "{:delay_effect:0.1}{#+Alt}", "STK!": "{#-Alt}"}
.STK
is continuously held for 0.1s (the number can be specified in the dictionary entry. Or would it be better to make this a global setting?) then the stroke would be sent to the formatter.{#Alt}
is not a no operation in many programs, so the delay is needed in case the user actually want to write some stroke that contains STK instead and these keys happen to be sent first/released last.{#Alt(tab tab tab)}
(on Windows...). The user could possibly write a rule like{"STK-PB": "{##Tab}"}
, but some considerations are again needed -- the -P and -B keys will not be registered at the same time and another configuration option is needed to specify how long should another key being held take (without the entry being in the dictionary) before the Alt itself is released.Opinion?
Beta Was this translation helpful? Give feedback.
All reactions