class KeyboardHook(): """Keyboard Hook Class""" def __enter__(self): self.readHandle = GetStdHandle(STD_INPUT_HANDLE) self.readHandle.SetConsoleMode(ENABLE_PROCESSED_INPUT) self.input_lenth = len(self.readHandle.PeekConsoleInput(10000)) return self def __exit__(self, type, value, traceback): pass def reset(self): self.input_lenth = len(self.readHandle.PeekConsoleInput(10000)) return True def key_pressed(self): """poll method to check for keyboard input""" events_peek = self.readHandle.PeekConsoleInput(10000) #Events come in pairs of KEY_DOWN, KEY_UP so wait for at least 2 events if len(events_peek) >= (self.input_lenth + 2): self.input_lenth = len(events_peek) return True return False
class WindowsKeyPoller: def __init__(self): self.read_handle = GetStdHandle(STD_INPUT_HANDLE) self.read_handle.SetConsoleMode(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT) self.cur_event_length = 0 self.cur_keys_length = 0 self.captured_chars = [] def cleanup(self): pass def poll(self): if self.captured_chars: return self.captured_chars.pop(0) events_peek = self.read_handle.PeekConsoleInput(10000) if not events_peek: return None if not len(events_peek) == self.cur_event_length: for cur_event in events_peek[self.cur_event_length:]: if cur_event.EventType == KEY_EVENT: if ord(cur_event.Char) and cur_event.KeyDown: cur_char = str(cur_event.Char) self.captured_chars.append(cur_char) self.cur_event_length = len(events_peek) if self.captured_chars: return self.captured_chars.pop(0) else: return None
class KeyPoller(): def __init__(self): #https://stackoverflow.com/questions/13207678/whats-the-simplest-way-of-detecting-keyboard-input-in-python-from-the-terminal self.readHandle = GetStdHandle(STD_INPUT_HANDLE) self.readHandle.SetConsoleMode(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT) self.curEventLength = 0 self.curKeysLength = 0 self.capturedChars = [] def poll(self): if not len(self.capturedChars) == 0: return self.capturedChars.pop(0) eventsPeek = self.readHandle.PeekConsoleInput(10000) if len(eventsPeek) == 0: return None if not len(eventsPeek) == self.curEventLength: for curEvent in eventsPeek[self.curEventLength:]: if curEvent.EventType == KEY_EVENT: if ord(curEvent.Char) == 0 or not curEvent.KeyDown: pass else: curChar = str(curEvent.Char) self.capturedChars.append(curChar) self.curEventLength = len(eventsPeek) if not len(self.capturedChars) == 0: return self.capturedChars.pop(0) else: return None
class KeyPoller(): def __init__(self): self.readHandle = GetStdHandle(STD_INPUT_HANDLE) self.readHandle.SetConsoleMode(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT) self.curEventLength = 0 self.curKeysLength = 0 self.capturedChars = [] def poll(self): if not len(self.capturedChars) == 0: return self.capturedChars.pop(0) eventsPeek = self.readHandle.PeekConsoleInput(10000) if len(eventsPeek) == 0: return None if not len(eventsPeek) == self.curEventLength: for curEvent in eventsPeek[self.curEventLength:]: if curEvent.EventType == KEY_EVENT: if ord(curEvent.Char) == 0 or not curEvent.KeyDown: pass else: curChar = str(curEvent.Char) self.capturedChars.append(curChar) self.curEventLength = len(eventsPeek) if not len(self.capturedChars) == 0: return self.capturedChars.pop(0) else: return None
class KeyPoller(): def __enter__(self): global isWindows if isWindows: self.readHandle = GetStdHandle(STD_INPUT_HANDLE) self.readHandle.SetConsoleMode(ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT) self.curEventLength = 0 self.curKeysLength = 0 self.capturedChars = [] else: # Save the terminal settings self.fd = sys.stdin.fileno() self.new_term = termios.tcgetattr(self.fd) self.old_term = termios.tcgetattr(self.fd) # New terminal setting unbuffered self.new_term[3] = (self.new_term[3] & ~termios.ICANON & ~termios.ECHO) termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.new_term) return self def __exit__(self, type, value, traceback): if isWindows: pass else: termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old_term) def poll(self): if isWindows: if not len(self.capturedChars) == 0: return self.capturedChars.pop(0) eventsPeek = self.readHandle.PeekConsoleInput(10000) if len(eventsPeek) == 0: return None if not len(eventsPeek) == self.curEventLength: for curEvent in eventsPeek[self.curEventLength:]: if curEvent.EventType == KEY_EVENT: if ord(curEvent.Char) == 0 or not curEvent.KeyDown: pass else: curChar = str(curEvent.Char) self.capturedChars.append(curChar) self.curEventLength = len(eventsPeek) if not len(self.capturedChars) == 0: return self.capturedChars.pop(0) else: return None else: dr,dw,de = select.select([sys.stdin], [], [], 0) if not dr == []: return sys.stdin.read(1) return None
def prepare_virtual_console() -> None: """ On Windows, ensure that Console Virtual Terminal Sequences are enabled. """ if POSIX_MODE: return # https://docs.microsoft.com/en-us/windows/console/setconsolemode ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 # stdout/stderr will be the same console screen buffer, but this could return None or raise try: h = GetStdHandle(STD_OUTPUT_HANDLE) if h: h.SetConsoleMode(h.GetConsoleMode() | ENABLE_VIRTUAL_TERMINAL_PROCESSING) except pywinerror: logger.debug("Failed to set console mode", exc_info=True)
class KeyListener: def __enter__(self): self.readHandle = GetStdHandle(STD_INPUT_HANDLE) self.readHandle.SetConsoleMode(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT) self.capturedChars = [] return self def __exit__(self, type, value, traceback): pass def poll(self): if self.capturedChars: return self.capturedChars.pop(0) eventsPeek = self.readHandle.PeekConsoleInput(10000) if len(eventsPeek) == 0: return None eventsPeek = self.readHandle.ReadConsoleInput(10000) for curEvent in eventsPeek: if curEvent.EventType == KEY_EVENT: if not curEvent.KeyDown: pass else: char, control = None, None if curEvent.VirtualKeyCode in controlKeyValues: control = ControlKeys(curEvent.VirtualKeyCode) else: char = curEvent.Char self.capturedChars.append((char, control)) if self.capturedChars: return self.capturedChars.pop(0) else: return None
class KeyAsyncReader(): def __init__(self): self.stopLock = threading.Lock() self.stopped = True self.capturedChars = "" self.readHandle = GetStdHandle(STD_INPUT_HANDLE) self.readHandle.SetConsoleMode(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT) def startReading(self, readCallback): self.stopLock.acquire() try: if not self.stopped: raise Exception("Capture is already going") self.stopped = False self.readCallback = readCallback backgroundCaptureThread = threading.Thread( target=self.backgroundThreadReading) backgroundCaptureThread.daemon = True backgroundCaptureThread.start() except: self.stopLock.release() raise self.stopLock.release() def backgroundThreadReading(self): curEventLength = 0 curKeysLength = 0 while True: eventsPeek = self.readHandle.PeekConsoleInput(10000) self.stopLock.acquire() if self.stopped: self.stopLock.release() return self.stopLock.release() if len(eventsPeek) == 0: continue if not len(eventsPeek) == curEventLength: if self.getCharsFromEvents(eventsPeek[curEventLength:]): self.stopLock.acquire() self.stopped = True self.stopLock.release() break curEventLength = len(eventsPeek) time.sleep(1) def getCharsFromEvents(self, eventsPeek): callbackReturnedTrue = False for curEvent in eventsPeek: if curEvent.EventType == KEY_EVENT: if ord(curEvent.Char) == 0 or not curEvent.KeyDown: pass else: curChar = str(curEvent.Char) if self.readCallback(curChar) == True: callbackReturnedTrue = True return callbackReturnedTrue def stopReading(self): self.stopLock.acquire() self.stopped = True