@@ -102,6 +102,11 @@ def __init__(self, err: int | None, descr: str | None = None) -> None:
102102MOVE_DOWN = "\x1b [{}B"
103103CLEAR = "\x1b [H\x1b [J"
104104
105+ # State of control keys: https://learn.microsoft.com/en-us/windows/console/key-event-record-str
106+ ALT_ACTIVE = 0x01 | 0x02
107+ CTRL_ACTIVE = 0x04 | 0x08
108+ CTRL_OR_ALT_ACTIVE = ALT_ACTIVE | CTRL_ACTIVE
109+
105110
106111class _error (Exception ):
107112 pass
@@ -407,31 +412,33 @@ def get_event(self, block: bool = True) -> Event | None:
407412 continue
408413 return None
409414
410- key = rec .Event .KeyEvent .uChar .UnicodeChar
415+ key_event = rec .Event .KeyEvent
416+ raw_key = key = key_event .uChar .UnicodeChar
411417
412- if rec . Event . KeyEvent . uChar . UnicodeChar == "\r " :
413- # Make enter make unix-like
418+ if key == "\r " :
419+ # Make enter unix-like
414420 return Event (evt = "key" , data = "\n " , raw = b"\n " )
415- elif rec . Event . KeyEvent .wVirtualKeyCode == 8 :
421+ elif key_event .wVirtualKeyCode == 8 :
416422 # Turn backspace directly into the command
417- return Event (
418- evt = "key" ,
419- data = "backspace" ,
420- raw = rec .Event .KeyEvent .uChar .UnicodeChar ,
421- )
422- elif rec .Event .KeyEvent .uChar .UnicodeChar == "\x00 " :
423+ key = "backspace"
424+ elif key == "\x00 " :
423425 # Handle special keys like arrow keys and translate them into the appropriate command
424- code = VK_MAP .get (rec . Event . KeyEvent .wVirtualKeyCode )
426+ code = VK_MAP .get (key_event .wVirtualKeyCode )
425427 if code :
426- return Event (
427- evt = "key" , data = code , raw = rec . Event . KeyEvent . uChar . UnicodeChar
428- )
428+ if code in ( "left" , "right" ) and ( ctrlstate := key_event . dwControlKeyState ) and ctrlstate & CTRL_OR_ALT_ACTIVE :
429+ code = f"ctrl { code } "
430+ return Event ( evt = "key" , data = code , raw = key )
429431 if block :
430432 continue
431433
432434 return None
433435
434- return Event (evt = "key" , data = key , raw = rec .Event .KeyEvent .uChar .UnicodeChar )
436+ if (ctrlstate := key_event .dwControlKeyState ) and ctrlstate & ALT_ACTIVE :
437+ # first send meta, then send the key
438+ self .event_queue .insert (0 , Event (evt = "key" , data = key , raw = raw_key ))
439+ return Event (evt = "key" , data = "\033 " ) # keymap.py uses this for meta
440+
441+ return Event (evt = "key" , data = key , raw = raw_key )
435442
436443 def push_char (self , char : int | bytes ) -> None :
437444 """
0 commit comments