def start(self) -> mp.Process: """ Starts the async window-focus watcher and the ml/eyetracker module. """ # If async watcher already running if self._async_proc_win is not None and self._async_proc_win.is_alive(): warn('HUD State Manager already running.') # Else, not running -- start it else: # Start the state watcher ctx = mp.get_context('fork') self._async_signal_q_win = ctx.Queue(maxsize=1) self._async_output_q_win = ctx.Queue(maxsize=1) self._async_proc_win = ctx.Process( target=self._async_winstate_watcher, args=(self._async_signal_q_win, self._async_output_q_win)) self._async_proc_win.start() # Start the eyetracker self._gazetracker.open() self._gazetracker.start() # Give time to spin up sleep(1) # Start the user pos guide updater self._async_proc_pos = Thread( target=self._async_userpos_watcher, args=(self._gazetracker, self.hud.status_panel)) self._async_proc_pos.start() return self._async_proc_win
def prev_active_window(self): """ Returns an Xlib.Window obj for the previously active window. """ # Request prev active window ID from async queue try: self._async_signal_q_win.put_nowait(SIGNAL_REQUEST_PREV_ACTIVE_WINDOW) except AttributeError: warn('Requested win state but Win State Watcher not yet started.') # Receive ID from asyc queue and return its derived window obj window_id = self._async_output_q_win.get() return self._disp.create_resource_object('window', window_id)
def stop(self) -> mp.Process: """ Stops the async focus watcher and eyetracker, and performs cleanup. """ # Unset any keybd modifier toggles the user may have set self._reset_keyb_modifers(toggle_btnviz=False) # Send kill signal to the asynch procs try: self._async_signal_q_win.put_nowait(SIGNAL_STOP) except AttributeError: warn('Received STOP but HUD State Manager not yet started.') except mp.queues.Full: pass else: self._gazetracker.stop() self._gazetracker.close() return self._async_proc_win
def payload_run_external(self, **kwargs): """ Runs the external cmd given by the payload. :param kwargs: Arg 'payload' is expected. """ self._focus_prev_active_win() # Extract kwarg payload = kwargs['payload'] # (str) Well-formatted python list # Ensure cmd given as a list of the cmd and its args cmd = eval(payload) if not cmd or not isinstance(cmd, list): raise ValueError(f'Invalid cmd format: {payload}') # Run the cmd proc = Popen(cmd, stderr=PIPE) stderr = proc.communicate()[1] proc.wait() # If there were build errors, quit if stderr and not stderr.decode().startswith('Created symlink'): warn(f'Cmd "{cmd}" stderr output: \n{stderr}')
HUDButton(widget=None, text=txt, alt_text=None, payload=key_id, payload_type='keystroke')) print(f'Button read: {key} <{key_id}>') # Map buttons from keystrokes until ESC pressed with keyboard.Listener(on_press=on_press) as listener: listener.join() # Stop listening/mapping btns # Write resulting layout to json, prompting to overwrite iff exists outfile = Path(OUTPUT_DIR, f'{args.panel_name}.{OUTPUT_EXT}') if outfile.exists(): warn(f'File already exists: {outfile}') if input('Overwrite [y|N]? ') != 'y': print('Done. Results not written.') exit() with open(outfile, 'w') as f: json.dump(panel_btn_map, f, indent=4, default=json_helper, separators=(',', ': ')) print(f'Done. Wrote results to {outfile}.')
def payload_keyboard_toggle_modifer(self, **kwargs): """ Updates the keyboard controller to reflect the given toggle key press. E.g. To toggle shift key on/off. In the process, focus is returned to the previously focused window. :param kwargs: Args 'btn' and 'payload' are expected. """ self._focus_prev_active_win() # Extract kwargs payload = kwargs['payload'] # (int) Key vk code sender = kwargs['btn'] # (HUDPanel.HUDButton) Payload sender # Ensure modifier is supported if payload == VK_NUMLOCK: raise NotImplementedError('NumLock') elif payload == VK_SCROLLLOCK: raise NotImplementedError('ScrollLock') # Convert payload to KeyCode and denote capslock status keycode = self._keyboard._KeyCode.from_vk(payload) # If payload is for capslock, handle as a single-click modifier if payload == VK_CAPSLOCK: self._keyboard.press(keycode) self._keyboard.release(keycode) self.hud.set_btn_viz_toggle( sender, toggle_on=self._keyboard._caps_lock) # Else, if payload is the hold-modifer btn elif payload == VK_MODLOCK: toggle_down = not self._keyboard_hold_modifiers self._keyboard_hold_modifiers = toggle_down self.hud.set_btn_viz_toggle(sender, toggle_on=toggle_down) self._reset_keyb_modifers() if not toggle_down else None # Else, handle press/releases modifier (ex: alt, shift, etc.) else: with self._keyboard.modifiers as modifiers: modifier = self._keyboard._as_modifier(keycode) if not modifier: raise ValueError(f'Unsupported modifier: {keycode}') # If btn not previously in the down state, send keypress if modifier not in [m for m in modifiers]: toggle_down = True self._keyboard.press(keycode) self._keyboard_active_modifier_btns.append(sender) # else, send key release else: toggle_down = False try: self._keyboard_active_modifier_btns.remove(sender) except ValueError: # Will occur if, say, l_shift set but r_shift clicked warn('Attempted to unset a modifier that was not set.') return else: self._keyboard.release(keycode) # Update btn state according to new toggle state self.hud.set_btn_viz_toggle(sender, toggle_on=toggle_down) if modifier == self._keyboard._Key.shift: self.hud.keyb_panel.set_btn_text(use_alt_text=toggle_down)