Skip to content

Commit 975bf7b

Browse files
committed
Repeat PyREPL key events on Windows when wRepeatCount > 1
Keys that are repeated into PyREPL were typed only once. This change makes those characters be typed the correct number of times.
1 parent 0ac40ac commit 975bf7b

File tree

1 file changed

+35
-22
lines changed

1 file changed

+35
-22
lines changed

Lib/_pyrepl/windows_console.py

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ def __init__(
128128
self.height = 25
129129
self.__offset = 0
130130
self.event_queue: deque[Event] = deque()
131+
self.key_repeat_queue: dequeue[Event] = deque()
131132
try:
132133
self.out = io._WindowsConsoleIO(self.output_fd, "w") # type: ignore[attr-defined]
133134
except ValueError:
@@ -390,6 +391,9 @@ def get_event(self, block: bool = True) -> Event | None:
390391
"""Return an Event instance. Returns None if |block| is false
391392
and there is no event pending, otherwise waits for the
392393
completion of an event."""
394+
if self.key_repeat_queue:
395+
return self.key_repeat_queue.pop()
396+
393397
if self.event_queue:
394398
return self.event_queue.pop()
395399

@@ -407,31 +411,40 @@ def get_event(self, block: bool = True) -> Event | None:
407411
continue
408412
return None
409413

410-
key = rec.Event.KeyEvent.uChar.UnicodeChar
411-
412-
if rec.Event.KeyEvent.uChar.UnicodeChar == "\r":
413-
# Make enter make unix-like
414-
return Event(evt="key", data="\n", raw=b"\n")
415-
elif rec.Event.KeyEvent.wVirtualKeyCode == 8:
416-
# Turn backspace directly into the command
414+
event = self._event_from_keyevent(rec.Event.KeyEvent)
415+
if event is None and block:
416+
continue
417+
418+
# Queue this key event to be repeated if wRepeatCount > 1, such as when a 'dead key' is pressed twice
419+
for _ in range(rec.Event.KeyEvent.wRepeatCount - 1):
420+
self.key_repeat_queue.appendleft(event)
421+
422+
return event
423+
424+
def _event_from_keyevent(self, keyevent: KeyEvent) -> Event | None:
425+
key = keyevent.uChar.UnicodeChar
426+
427+
if keyevent.uChar.UnicodeChar == "\r":
428+
# Make enter make unix-like
429+
return Event(evt="key", data="\n", raw=b"\n")
430+
elif keyevent.wVirtualKeyCode == 8:
431+
# Turn backspace directly into the command
432+
return Event(
433+
evt="key",
434+
data="backspace",
435+
raw=keyevent.uChar.UnicodeChar,
436+
)
437+
elif keyevent.uChar.UnicodeChar == "\x00":
438+
# Handle special keys like arrow keys and translate them into the appropriate command
439+
code = VK_MAP.get(keyevent.wVirtualKeyCode)
440+
if code:
417441
return Event(
418-
evt="key",
419-
data="backspace",
420-
raw=rec.Event.KeyEvent.uChar.UnicodeChar,
442+
evt="key", data=code, raw=keyevent.uChar.UnicodeChar
421443
)
422-
elif rec.Event.KeyEvent.uChar.UnicodeChar == "\x00":
423-
# Handle special keys like arrow keys and translate them into the appropriate command
424-
code = VK_MAP.get(rec.Event.KeyEvent.wVirtualKeyCode)
425-
if code:
426-
return Event(
427-
evt="key", data=code, raw=rec.Event.KeyEvent.uChar.UnicodeChar
428-
)
429-
if block:
430-
continue
431444

432-
return None
445+
return None
433446

434-
return Event(evt="key", data=key, raw=rec.Event.KeyEvent.uChar.UnicodeChar)
447+
return Event(evt="key", data=key, raw=keyevent.uChar.UnicodeChar)
435448

436449
def push_char(self, char: int | bytes) -> None:
437450
"""
@@ -479,7 +492,7 @@ def wait(self, timeout: float | None) -> bool:
479492
# Poor man's Windows select loop
480493
start_time = time.time()
481494
while True:
482-
if msvcrt.kbhit(): # type: ignore[attr-defined]
495+
if msvcrt.kbhit() or self.key_repeat_queue: # type: ignore[attr-defined]
483496
return True
484497
if timeout and time.time() - start_time > timeout / 1000:
485498
return False

0 commit comments

Comments
 (0)