def _find_resolution_offset(x, y, store): if store['CustomResolution'] is not None: try: resolution, (x_offset, y_offset) = monitor_offset((x, y), [store['CustomResolution'][0]]) except TypeError: return None return ((x - x_offset, y - y_offset), resolution) elif MULTI_MONITOR: try: resolution, (x_offset, y_offset) = monitor_offset((x, y), store['ResolutionTemp']) except TypeError: store['ResolutionTemp'] = monitor_info() try: resolution, (x_offset, y_offset) = monitor_offset((x, y), store['ResolutionTemp']) except TypeError: return None return ((x - x_offset, y - y_offset), resolution) else: resolution = store['Resolution'] return ((x, y), resolution)
def get_monitor_coordinate(x, y, store): """Find the resolution of the monitor and adjusted x, y coordinates.""" if store['ApplicationResolution'] is not None: try: resolution, (x_offset, y_offset) = monitor_offset( (x, y), [store['ApplicationResolution'][0]]) except TypeError: return None return ((x - x_offset, y - y_offset), resolution) elif MULTI_MONITOR: try: resolution, (x_offset, y_offset) = monitor_offset( (x, y), store['MonitorLimits']) except TypeError: store['MonitorLimits'] = monitor_info() try: resolution, (x_offset, y_offset) = monitor_offset( (x, y), store['MonitorLimits']) except TypeError: return None check_resolution(store['Data'], resolution) return ((x - x_offset, y - y_offset), resolution) else: resolution = store['Resolution'] return ((x, y), resolution)
def _start_tracking(): _background_process = None no_detection_wait = 2 try: q_feedback = Queue() #Setup message server if CONFIG['API']['RunServer']: q_msg = Queue() message = PrintFormat(MessageWithQueue(q_msg).send) server_port = get_free_port() local_message_server(port=server_port, q_main=q_msg, q_feedback=q_feedback) else: message = PrintFormat(MessageWithQueue().send) server_port = None #message(NOTIFY.get_output()) #Setup web server if CONFIG['API']['RunWeb']: app.config.update(create_pipe('REQUEST', duplex=False)) app.config.update(create_pipe('CONTROL', duplex=False)) app.config.update(create_pipe('STATUS', duplex=False)) app.config.update(create_pipe('PORT', duplex=False)) app.config.update(create_pipe('CONFIG', duplex=False)) app.config.update(create_pipe('CONFIG_UPDATE', duplex=False)) web_port = get_free_port() web_port = 65043 local_web_server(app=app, port=web_port, q_feedback=q_feedback) else: web_port = None message(NOTIFY.get_output()) #Start main script NOTIFY(MT_PATH) message(NOTIFY.get_output()) CONFIG.save() #Adjust timings to account for tick rate timer = { 'UpdateScreen': CONFIG['Advanced']['CheckResolution'], 'UpdatePrograms': CONFIG['Advanced']['CheckRunningApplications'], 'Save': CONFIG['Save']['Frequency'] * UPDATES_PER_SECOND, 'ReloadProgramList': CONFIG['Advanced']['ReloadApplicationList'], 'UpdateQueuedCommands': CONFIG['Advanced']['ShowQueuedCommands'], 'RefreshGamepads': CONFIG['Advanced']['RefreshGamepads'], 'HistoryCheck': CONFIG['Advanced']['HistoryCheck'] } store = { 'Resolution': { 'Current': monitor_info(), 'Previous': None, 'Boundaries': None }, 'Mouse': { 'Position': { 'Current': None, 'Previous': None }, 'NotMoved': 0, 'Inactive': False, 'Clicked': {}, 'LastClick': None, 'LastClickTime': 0, 'OffScreen': False, 'DoubleClickTime': get_double_click_time() / 1000 * UPDATES_PER_SECOND }, 'Keyboard': { 'KeysPressed': {k: False for k in KEYS.keys()}, 'KeysInvalid': set() }, 'LastActivity': 0, 'LastSent': 0, 'Save': { 'Finished': True, 'Next': timer['Save'] }, 'Gamepad': { 'ButtonsPressed': {} }, 'Flask': { 'App': app if web_port is not None else None, 'Port': { 'Web': web_port, 'Server': server_port } } } mouse_pos = store['Mouse']['Position'] #Start background processes q_bg_recv = Queue() q_bg_send = Queue() _background_process = Process(target=background_process, args=(q_bg_send, q_bg_recv)) _background_process.daemon = True _background_process.start() q_rp_recv = Queue() q_rp_send = Queue() _running_programs = Thread(target=running_processes, args=(q_rp_send, q_rp_recv, q_bg_send)) _running_programs.daemon = True _running_programs.start() ticks = 0 NOTIFY(START_MAIN) message(NOTIFY.get_output()) script_status = STATUS_RUNNING while script_status != STATUS_TERMINATED: with RefreshRateLimiter(UPDATES_PER_SECOND) as limiter: #Handle web server API requests if store['Flask']['App'] is not None: #Control state of script if store['Flask']['App'].config['PIPE_CONTROL_RECV'].poll( ): api_control = store['Flask']['App'].config[ 'PIPE_CONTROL_RECV'].recv() #Change if running or paused, or exit script if api_control in (STATUS_RUNNING, STATUS_PAUSED, STATUS_TERMINATED): script_status = api_control #Set config values if api_control == CONFIG_SET: config_header, config_var, config_val = store[ 'Flask']['App'].config[ 'PIPE_CONFIG_UPDATE_RECV'].recv() CONFIG[config_header][config_var] = config_val print('Set {}.{} to {}'.format( config_header, config_var, CONFIG[config_header][config_var])) #Requests that require response if store['Flask']['App'].config['PIPE_REQUEST_RECV'].poll( ): request_id = store['Flask']['App'].config[ 'PIPE_REQUEST_RECV'].recv() if request_id == FEEDBACK_STATUS: store['Flask']['App'].config[ 'PIPE_STATUS_SEND'].send(script_status) elif request_id == FEEDBACK_PORT: store['Flask']['App'].config[ 'PIPE_PORT_SEND'].send({ 'server': store['Flask']['Port']['Server'], 'web': store['Flask']['Port']['Web'] }) elif request_id == FEEDBACK_CONFIG: store['Flask']['App'].config[ 'PIPE_CONFIG_SEND'].send(CONFIG) if script_status != STATUS_RUNNING: continue #Send data to thread try: if frame_data or frame_data_rp: last_sent = ticks - store['LastSent'] if frame_data: if last_sent: frame_data['Ticks'] = last_sent q_bg_send.put(frame_data) if frame_data_rp: q_rp_send.put(frame_data_rp) store['LastSent'] = ticks except NameError: pass #Get messages from running program thread while not q_rp_recv.empty(): received = q_rp_recv.get() if isinstance(received, str): message(received, limiter.time) #Do not continue tracking held down keys #This behaviour is sometimes abused for games #but it will continue well after the game is quit elif isinstance(received, dict): if 'Program' in received: store['Keyboard']['KeysInvalid'] |= set([ k for k, v in get_items(store['Keyboard'] ['KeysPressed']) if v ]) #Print any messages from previous loop notify_extra = '' received_data = [] while not q_bg_recv.empty(): received_message = q_bg_recv.get() #Receive text messages try: if received_message.startswith( 'Traceback (most recent call last)'): q_bg_send.put({'Quit': True}) handle_error(received_message) except AttributeError: pass else: received_data.append(received_message) #Get notification when saving is finished try: received_message.pop('SaveFinished') except (KeyError, AttributeError): pass else: store['Save']['Finished'] = True store['Save']['Next'] = ticks + timer['Save'] output_list = [received_data] #Add on output from Notify class output_list.append(NOTIFY.get_output()) #Add output from server if CONFIG['API']['RunServer']: received_data = [] while not q_feedback.empty(): received_data.append(q_feedback.get()) output_list.append(received_data) #Join all valid outputs together output = u' | '.join(u' | '.join(msg_group) if isinstance( msg_group, (list, tuple)) else msg_group for msg_group in output_list if msg_group) if output: message(output, limiter.time) frame_data = {} frame_data_rp = {} #Mouse Movement mouse_pos['Current'] = get_cursor_pos() #Check if mouse is inactive (such as in a screensaver) if mouse_pos['Current'] is None: if not store['Mouse']['Inactive']: NOTIFY(MOUSE_UNDETECTED) store['Mouse']['Inactive'] = True time.sleep(no_detection_wait) continue #Check if mouse left the monitor elif (not MULTI_MONITOR and (not 0 <= mouse_pos['Current'][0] < store['Resolution']['Current'][0] or not 0 <= mouse_pos['Current'][1] < store['Resolution']['Current'][1])): if not store['Mouse']['OffScreen']: NOTIFY(MOUSE_OFFSCREEN) store['Mouse']['OffScreen'] = True elif store['Mouse']['OffScreen']: NOTIFY(MOUSE_ONSCREEN) store['Mouse']['OffScreen'] = False #Notify once if mouse is no longer inactive if store['Mouse']['Inactive']: store['Mouse']['Inactive'] = False NOTIFY(MOUSE_DETECTED) #Check if mouse is in a duplicate position if mouse_pos['Current'] is None or mouse_pos[ 'Current'] == mouse_pos['Previous']: store['Mouse']['NotMoved'] += 1 elif store['Mouse']['NotMoved']: store['Mouse']['NotMoved'] = 0 if not store['Mouse']['NotMoved']: if not store['Mouse']['OffScreen']: frame_data['MouseMove'] = (mouse_pos['Previous'], mouse_pos['Current']) NOTIFY(MOUSE_POSITION, mouse_pos['Current']) store['LastActivity'] = ticks #Mouse clicks click_repeat = CONFIG['Advanced']['RepeatClicks'] for mouse_button, clicked in enumerate(get_mouse_click()): mb_clicked = store['Mouse']['Clicked'].get( mouse_button, False) mb_data = (mouse_button, mouse_pos['Current']) if clicked: store['LastActivity'] = ticks #First click if not mb_clicked: #Double click double_click = False if (store['Mouse']['LastClickTime'] > ticks - store['Mouse']['DoubleClickTime'] and store['Mouse']['LastClick'] == mb_data): store['Mouse']['LastClickTime'] = 0 store['Mouse']['LastClick'] = None double_click = True try: frame_data['DoubleClick'].append(mb_data) except KeyError: frame_data['DoubleClick'] = [mb_data] else: store['Mouse']['LastClickTime'] = ticks #Single click store['Mouse']['Clicked'][mouse_button] = ticks if not store['Mouse']['OffScreen']: if double_click: NOTIFY(MOUSE_CLICKED_DOUBLE, mouse_button, mouse_pos['Current']) else: NOTIFY(MOUSE_CLICKED, mouse_button, mouse_pos['Current']) try: frame_data['MouseClick'].append(mb_data) except KeyError: frame_data['MouseClick'] = [mb_data] frame_data['MouseHeld'] = False else: if double_click: NOTIFY(MOUSE_CLICKED_DOUBLE, mouse_button) else: NOTIFY(MOUSE_CLICKED, mouse_button) #Held clicks elif click_repeat and mb_clicked < ticks - click_repeat: store['Mouse']['Clicked'][mouse_button] = ticks if not store['Mouse']['OffScreen']: NOTIFY(MOUSE_CLICKED_HELD, mouse_button, mouse_pos['Current']) try: frame_data['MouseClick'].append(mb_data) except KeyError: frame_data['MouseClick'] = [mb_data] frame_data['MouseHeld'] = True else: NOTIFY(MOUSE_CLICKED_HELD, mouse_button) store['Mouse']['LastClick'] = mb_data elif mb_clicked: NOTIFY(MOUSE_UNCLICKED) del store['Mouse']['Clicked'][mouse_button] store['LastActivity'] = ticks #Key presses keys_pressed = [] keys_held = [] key_status = store['Keyboard']['KeysPressed'] key_press_repeat = CONFIG['Advanced']['RepeatKeyPress'] key_invalid = store['Keyboard']['KeysInvalid'] _keys_held = [] _keys_pressed = [] _keys_released = [] for k in KEYS: if get_key_press(KEYS[k]): #Ignore if held down from last profile if k in key_invalid: continue keys_held.append(k) #If key is currently being held down if key_status[k]: if key_press_repeat and key_status[ k] < ticks - key_press_repeat: keys_pressed.append(k) _keys_held.append(k) key_status[k] = ticks #If key has been pressed else: keys_pressed.append(k) _keys_pressed.append(k) key_status[k] = ticks notify_key_press = list(keys_pressed) #If key has been released elif key_status[k]: key_status[k] = False _keys_released.append(k) #Mark key as valid again try: key_invalid.remove(k) except KeyError: pass if keys_pressed: frame_data['KeyPress'] = keys_pressed store['LastActivity'] = ticks if keys_held: frame_data['KeyHeld'] = keys_held store['LastActivity'] = ticks if _keys_pressed: NOTIFY(KEYBOARD_PRESSES, *_keys_pressed) if _keys_held: NOTIFY(KEYBOARD_PRESSES_HELD, *_keys_held) if _keys_released: NOTIFY(KEYBOARD_RELEASED, *_keys_released) if CONFIG['Main']['_TrackGamepads']: #Reload list of gamepads (in case one was plugged in) if timer['RefreshGamepads'] and not ticks % timer[ 'RefreshGamepads']: try: old_gamepads = set(gamepads) except UnboundLocalError: old_gamepads = set() gamepads = { gamepad.device_number: gamepad for gamepad in Gamepad.list_gamepads() } difference = set(gamepads) - old_gamepads for i, id in enumerate(difference): NOTIFY(GAMEPAD_FOUND, id) store['Gamepad']['ButtonsPressed'][id] = {} #Gamepad tracking (multiple controllers not tested yet) button_repeat = CONFIG['Advanced']['RepeatButtonPress'] invalid_ids = [] buttons_held = {} _buttons_pressed = {} _buttons_released = {} for id, gamepad in get_items(gamepads): #Repeat presses if button_repeat: for button_id, last_update in get_items( store['Gamepad']['ButtonsPressed'][id]): if last_update < ticks - button_repeat: try: buttons_held[id].append(button_id) except KeyError: buttons_held[id] = [button_id] store['Gamepad']['ButtonsPressed'][id][ button_id] = ticks with gamepad as gamepad_input: #Break the connection if controller can't be found if not gamepad.connected: NOTIFY(GAMEPAD_LOST, id) invalid_ids.append(id) continue #Axis events (thumbsticks, triggers, etc) #Send an update every tick, but only print the changes #The dead zone can be tracked now and ignored later printable = {} axis_updates = gamepad_input.get_axis( printable=printable) if axis_updates: store['LastActivity'] = ticks try: frame_data['GamepadAxis'].append( axis_updates) except KeyError: frame_data['GamepadAxis'] = [axis_updates] for axis, value in get_items(printable): NOTIFY(GAMEPAD_AXIS, id, axis, value) #Button events button_presses = gamepad_input.get_button() if button_presses: for button_id, state in get_items( button_presses): #Button pressed if state: try: frame_data[ 'GamepadButtonPress'].append( button_id) except KeyError: frame_data[ 'GamepadButtonPress'] = [ button_id ] store['Gamepad']['ButtonsPressed'][id][ button_id] = ticks try: _buttons_pressed[id].append( button_id) except KeyError: _buttons_pressed[id] = [button_id] #Button has been released elif button_id in store['Gamepad'][ 'ButtonsPressed'][id]: held_length = ticks - store['Gamepad'][ 'ButtonsPressed'][id][button_id] del store['Gamepad']['ButtonsPressed'][ id][button_id] try: _buttons_released[id].append( button_id) except KeyError: _buttons_released[id] = [button_id] #Send held buttons each frame for id, held_buttons in get_items( store['Gamepad']['ButtonsPressed']): if held_buttons: try: frame_data['GamepadButtonHeld'].add( held_buttons) except KeyError: frame_data['GamepadButtonHeld'] = set( held_buttons) #Cleanup disconnected controllers for id in invalid_ids: del gamepads[id] del store['Gamepad']['ButtonsPressed'][id] if buttons_held: try: frame_data['GamepadButtonPress'] += buttons_held except KeyError: frame_data['GamepadButtonPress'] = buttons_held store['LastActivity'] = ticks for id, buttons in get_items(buttons_held): NOTIFY(GAMEPAD_BUTTON_HELD, id, buttons) if _buttons_pressed: store['LastActivity'] = ticks for id, buttons in get_items(_buttons_pressed): NOTIFY(GAMEPAD_BUTTON_PRESS, id, buttons) if _buttons_released: store['LastActivity'] = ticks for id, buttons in get_items(_buttons_released): NOTIFY(GAMEPAD_BUTTON_RELEASED, id, buttons) #Resolution recalculate_mouse = False check_resolution = timer[ 'UpdateScreen'] and not ticks % timer['UpdateScreen'] #Check if resolution has changed if check_resolution: if MULTI_MONITOR: old_resolution = store['Resolution']['Boundaries'] store['Resolution']['Boundaries'] = monitor_info() if old_resolution != store['Resolution']['Boundaries']: frame_data['MonitorLimits'] = store['Resolution'][ 'Boundaries'] recalculate_mouse = True else: store['Resolution']['Current'] = monitor_info() if store['Resolution']['Previous'] != store[ 'Resolution']['Current']: if store['Resolution']['Previous'] is not None: NOTIFY(RESOLUTION_CHANGED, store['Resolution']['Previous'], store['Resolution']['Current']) frame_data['Resolution'] = store['Resolution'][ 'Current'] store['Resolution']['Previous'] = store[ 'Resolution']['Current'] #Display message that mouse has switched monitors if MULTI_MONITOR: try: current_mouse_pos = frame_data['MouseMove'][1] except KeyError: current_mouse_pos = mouse_pos['Current'] else: recalculate_mouse = True if recalculate_mouse: try: #Calculate which monitor the mouse is on try: current_screen_resolution = monitor_offset( current_mouse_pos, store['Resolution']['Boundaries'])[0] except TypeError: if check_resolution: raise TypeError #Send to background process if the monitor list changes old_resolution = store['Resolution'][ 'Boundaries'] store['Resolution'][ 'Boundaries'] = monitor_info() if old_resolution != store['Resolution'][ 'Boundaries']: frame_data['MonitorLimits'] = store[ 'Resolution']['Boundaries'] current_screen_resolution = monitor_offset( current_mouse_pos, store['Resolution']['Boundaries'])[0] except TypeError: pass else: if current_screen_resolution != store[ 'Resolution']['Previous']: if store['Resolution']['Previous'] is not None: NOTIFY(MONITOR_CHANGED, store['Resolution']['Previous'], current_screen_resolution) store['Resolution'][ 'Previous'] = current_screen_resolution #Send request to check history list if timer['HistoryCheck'] and not ticks % timer['HistoryCheck']: frame_data['HistoryCheck'] = True #Send request to update programs if timer['UpdatePrograms'] and not ticks % timer[ 'UpdatePrograms']: frame_data_rp['Update'] = True #Send request to reload program list if timer['ReloadProgramList'] and not ticks % timer[ 'ReloadProgramList']: frame_data_rp['Reload'] = True #Update user about the queue size if (timer['UpdateQueuedCommands'] and not ticks % timer['UpdateQueuedCommands'] and timer['Save'] and store['LastActivity'] > ticks - timer['Save']): try: NOTIFY(QUEUE_SIZE, q_bg_send.qsize()) except NotImplementedError: pass #Send save request if store['Save']['Finished'] and ticks and not ticks % store[ 'Save']['Next']: frame_data['Save'] = True store['Save']['Finished'] = False if store['Mouse']['OffScreen']: mouse_pos['Previous'] = None else: mouse_pos['Previous'] = mouse_pos['Current'] ticks += 1 except Exception as e: if _background_process is not None: try: q_bg_send.put({'Quit': True}) except IOError: pass handle_error(traceback.format_exc()) except KeyboardInterrupt: if _background_process is not None: try: q_bg_send.put({'Quit': True}) except IOError: pass NOTIFY(THREAD_EXIT) NOTIFY(PROCESS_EXIT) message(NOTIFY.get_output()) handle_error()
def start_tracking(): _background_process = None no_detection_wait = 2 try: NOTIFY(MT_PATH) _print(u'{} {}'.format(time_format(time.time()), NOTIFY.get_output())) CONFIG.save() timer = { 'UpdateScreen': CONFIG['Advanced']['CheckResolution'], 'UpdatePrograms': CONFIG['Advanced']['CheckRunningApplications'], 'Save': CONFIG['Save']['Frequency'] * UPDATES_PER_SECOND, 'ReloadProgramList': CONFIG['Advanced']['ReloadApplicationList'], 'UpdateQueuedCommands': CONFIG['Advanced']['ShowQueuedCommands'] } store = { 'Resolution': { 'Current': monitor_info(), 'Previous': None, 'Boundaries': None }, 'Mouse': { 'Position': { 'Current': None, 'Previous': None }, 'NotMoved': 0, 'Inactive': False, 'Clicked': {}, 'LastClick': None, 'LastClickTime': 0, 'OffScreen': False, 'DoubleClickTime': get_double_click_time() / 1000 }, 'Keyboard': { 'KeysPressed': {k: False for k in KEYS.keys()} }, 'LastActivity': 0, 'LastSent': 0, 'Save': { 'Finished': True, 'Next': timer['Save'] } } mouse_pos = store['Mouse']['Position'] #Start background processes q_bg_send = Queue() q_bg_recv = Queue() _background_process = Process(target=background_process, args=(q_bg_send, q_bg_recv)) _background_process.daemon = True _background_process.start() q_rp_send = Queue() q_rp_recv = Queue() _running_programs = ThreadHelper(running_processes, q_rp_send, q_rp_recv, q_bg_send) _running_programs.daemon = True _running_programs.start() i = 0 NOTIFY(START_MAIN) _print(u'{} {}'.format(time_format(time.time()), NOTIFY.get_output())) while True: with RefreshRateLimiter(UPDATES_PER_SECOND) as limiter: #Send data to thread try: if frame_data or frame_data_rp: last_sent = i - store['LastSent'] if frame_data: if last_sent: frame_data['Ticks'] = last_sent q_bg_send.put(frame_data) if frame_data_rp: q_rp_send.put(frame_data_rp) store['LastSent'] = i except NameError: pass while not q_rp_recv.empty(): _print(u'{} {}'.format(time_format(limiter.time), q_rp_recv.get())) #Print any messages from previous loop notify_extra = '' received_data = [] while not q_bg_recv.empty(): received_message = q_bg_recv.get() #Receive text messages try: if received_message.startswith( 'Traceback (most recent call last)'): q_bg_send.put({'Quit': True}) handle_error(received_message) except AttributeError: pass else: received_data.append(received_message) #Get notification when saving is finished try: received_message.pop('SaveFinished') except (KeyError, AttributeError): pass else: store['Save']['Finished'] = True store['Save']['Next'] = i + timer['Save'] if received_data: notify_extra = u' | '.join(received_data) notify_output = NOTIFY.get_output() if notify_extra: if notify_output: notify_output = notify_extra + ' | ' + notify_output else: notify_output = notify_extra if notify_output: _print(u'{} {}'.format(time_format(limiter.time), notify_output)) frame_data = {} frame_data_rp = {} mouse_pos['Current'] = get_cursor_pos() #Check if mouse is inactive (such as in a screensaver) if mouse_pos['Current'] is None: if not store['Mouse']['Inactive']: NOTIFY(MOUSE_UNDETECTED) store['Mouse']['Inactive'] = True time.sleep(no_detection_wait) continue #Check if mouse left the monitor elif (not MULTI_MONITOR and (not 0 <= mouse_pos['Current'][0] < store['Resolution']['Current'][0] or not 0 <= mouse_pos['Current'][1] < store['Resolution']['Current'][1])): if not store['Mouse']['OffScreen']: NOTIFY(MOUSE_OFFSCREEN) store['Mouse']['OffScreen'] = True elif store['Mouse']['OffScreen']: NOTIFY(MOUSE_ONSCREEN) store['Mouse']['OffScreen'] = False #Notify once if mouse is no longer inactive if store['Mouse']['Inactive']: store['Mouse']['Inactive'] = False NOTIFY(MOUSE_DETECTED) #Check if mouse is in a duplicate position if mouse_pos['Current'] is None or mouse_pos[ 'Current'] == mouse_pos['Previous']: store['Mouse']['NotMoved'] += 1 elif store['Mouse']['NotMoved']: store['Mouse']['NotMoved'] = 0 if not store['Mouse']['NotMoved']: if not store['Mouse']['OffScreen']: frame_data['MouseMove'] = (mouse_pos['Previous'], mouse_pos['Current']) NOTIFY(MOUSE_POSITION, mouse_pos['Current']) store['LastActivity'] = i #Mouse clicks click_repeat = CONFIG['Advanced']['RepeatClicks'] for mouse_button, clicked in enumerate(get_mouse_click()): mb_clicked = store['Mouse']['Clicked'].get( mouse_button, False) mb_data = (mouse_button, mouse_pos['Current']) if clicked: store['LastActivity'] = i #First click if not mb_clicked: #Double click double_click = False if (store['Mouse']['LastClickTime'] > limiter.time - store['Mouse']['DoubleClickTime'] and store['Mouse']['LastClick'] == mb_data): store['Mouse']['LastClickTime'] = 0 store['Mouse']['LastClick'] = None double_click = True try: frame_data['DoubleClick'].append(mb_data) except KeyError: frame_data['DoubleClick'] = [mb_data] else: store['Mouse']['LastClickTime'] = limiter.time #Single click store['Mouse']['Clicked'][ mouse_button] = limiter.time if not store['Mouse']['OffScreen']: if double_click: NOTIFY(MOUSE_CLICKED_DOUBLE, mouse_button, mouse_pos['Current']) else: NOTIFY(MOUSE_CLICKED, mouse_button, mouse_pos['Current']) try: frame_data['MouseClick'].append(mb_data) except KeyError: frame_data['MouseClick'] = [mb_data] frame_data['MouseHeld'] = False else: if double_click: NOTIFY(MOUSE_CLICKED_DOUBLE, mouse_button) else: NOTIFY(MOUSE_CLICKED, mouse_button) #Held clicks elif click_repeat and mb_clicked < limiter.time - click_repeat: store['Mouse']['Clicked'][ mouse_button] = limiter.time if not store['Mouse']['OffScreen']: NOTIFY(MOUSE_CLICKED_HELD, mouse_button, mouse_pos['Current']) try: frame_data['MouseClick'].append(mb_data) except KeyError: frame_data['MouseClick'] = [mb_data] frame_data['MouseHeld'] = True else: NOTIFY(MOUSE_CLICKED_HELD, mouse_button) store['Mouse']['LastClick'] = mb_data elif mb_clicked: NOTIFY(MOUSE_UNCLICKED) del store['Mouse']['Clicked'][mouse_button] store['LastActivity'] = i #Key presses keys_pressed = [] keys_held = [] key_status = store['Keyboard']['KeysPressed'] key_press_repeat = CONFIG['Advanced']['RepeatKeyPress'] _keys_held = [] _keys_pressed = [] for k in KEYS: if get_key_press(KEYS[k]): keys_held.append(k) #If key is currently being held down if key_status[k]: if key_press_repeat and key_status[ k] < limiter.time - key_press_repeat: keys_pressed.append(k) _keys_held.append(k) key_status[k] = limiter.time #If key has been pressed else: keys_pressed.append(k) _keys_pressed.append(k) key_status[k] = limiter.time notify_key_press = list(keys_pressed) #If key has been released elif key_status[k]: key_status[k] = False if keys_pressed: frame_data['KeyPress'] = keys_pressed if keys_held: frame_data['KeyHeld'] = keys_held store['LastActivity'] = i if _keys_held: NOTIFY(KEYBOARD_PRESSES_HELD, _keys_held) if _keys_pressed: NOTIFY(KEYBOARD_PRESSES, _keys_pressed) recalculate_mouse = False #Check if resolution has changed if timer['UpdateScreen'] and not i % timer['UpdateScreen']: if MULTI_MONITOR: try: old_resolution = list( store['Resolution']['Boundaries']) except TypeError: old_resolution = None store['Resolution']['Boundaries'] = monitor_info() frame_data['MonitorLimits'] = store['Resolution'][ 'Boundaries'] if old_resolution != store['Resolution']['Boundaries']: recalculate_mouse = True else: store['Resolution']['Current'] = monitor_info() if store['Resolution']['Previous'] != store[ 'Resolution']['Current']: if store['Resolution']['Previous'] is not None: NOTIFY(RESOLUTION_CHANGED, store['Resolution']['Previous'], store['Resolution']['Current']) frame_data['Resolution'] = ['Resolution' ]['Current'] store['Resolution']['Previous'] = store[ 'Resolution']['Current'] #Display message that mouse has switched monitors if MULTI_MONITOR: try: current_mouse_pos = frame_data['MouseMove'][1] except KeyError: current_mouse_pos = mouse_pos['Current'] else: recalculate_mouse = True if recalculate_mouse: try: try: res = monitor_offset( current_mouse_pos, store['Resolution']['Boundaries'])[0] except TypeError: frame_data['MonitorLimits'] = monitor_info() store['Resolution']['Boundaries'] = frame_data[ 'MonitorLimits'] res = monitor_offset( current_mouse_pos, store['Resolution']['Boundaries'])[0] except TypeError: pass else: store['Resolution']['Current'] = res if store['Resolution']['Previous'] is not None: if store['Resolution']['Current'] != store[ 'Resolution']['Previous']: NOTIFY(MONITOR_CHANGED, store['Resolution']['Previous'], store['Resolution']['Current']) store['Resolution']['Previous'] = store[ 'Resolution']['Current'] #Send request to update programs if timer['UpdatePrograms'] and not i % timer['UpdatePrograms']: frame_data_rp['Update'] = True #Send request to reload program list if timer['ReloadProgramList'] and not i % timer[ 'ReloadProgramList']: frame_data_rp['Reload'] = True #Update user about the queue size if (timer['UpdateQueuedCommands'] and not i % timer['UpdateQueuedCommands'] and timer['Save'] and store['LastActivity'] > i - timer['Save']): try: NOTIFY(QUEUE_SIZE, q_bg_send.qsize()) except NotImplementedError: pass #Send save request if store['Save'][ 'Finished'] and i and not i % store['Save']['Next']: frame_data['Save'] = True store['Save']['Finished'] = False if store['Mouse']['OffScreen']: mouse_pos['Previous'] = None else: mouse_pos['Previous'] = mouse_pos['Current'] i += 1 except Exception as e: if _background_process is not None: try: q_bg_send.put({'Quit': True}) except IOError: pass handle_error(traceback.format_exc()) except KeyboardInterrupt: if _background_process is not None: try: q_bg_send.put({'Quit': True}) except IOError: pass NOTIFY(THREAD_EXIT) NOTIFY(PROCESS_EXIT) _print(u'{} {}'.format(time_format(time.time()), NOTIFY.get_output())) handle_error()
def background_process(q_recv, q_send): """Function to handle all the data from the main thread.""" try: NOTIFY(START_THREAD) NOTIFY.send(q_send) store = {'Data': load_program(), 'LastProgram': None, 'Resolution': None, 'ResolutionTemp': None, 'ResolutionList': set(), 'Offset': (0, 0), 'LastResolution': None, 'ActivitySinceLastSave': False, 'SavesSkipped': 0, 'PingTimeout': CONFIG['Timer']['_Ping'] + 1} NOTIFY(DATA_LOADED) NOTIFY(QUEUE_SIZE, q_recv.qsize()) NOTIFY.send(q_send) while True: received_data = q_recv.get() ''' #Quit if data is not received by the ping interval #Seems buggy so disabling for now try: received_data = q_recv.get(timeout=store['PingTimeout']) except Empty: break ''' check_resolution = False if 'Save' in received_data: if store['ActivitySinceLastSave']: _save_wrapper(q_send, store['LastProgram'], store['Data'], False) NOTIFY(QUEUE_SIZE, q_recv.qsize()) store['ActivitySinceLastSave'] = False store['SavesSkipped'] = 0 else: store['SavesSkipped'] += 1 NOTIFY(SAVE_SKIP, CONFIG['Save']['Frequency'] * store['SavesSkipped'], q_recv.qsize()) q_send.put({'SaveFinished': None}) if 'Program' in received_data: current_program = received_data['Program'] if current_program != store['LastProgram']: if current_program is None: NOTIFY(APPLICATION_LOADING) else: NOTIFY(APPLICATION_LOADING, current_program) NOTIFY.send(q_send) #Save old profile _save_wrapper(q_send, store['LastProgram'], store['Data'], True) #Load new profile store['LastProgram'] = current_program store['Data'] = load_program(current_program) store['ActivitySinceLastSave'] = False #Check new resolution _check_resolution(store, store['Resolution']) store['ResolutionList'] = set() if store['Data']['Ticks']['Total']: NOTIFY(DATA_LOADED) else: NOTIFY(DATA_NOTFOUND) NOTIFY(QUEUE_SIZE, q_recv.qsize()) NOTIFY.send(q_send) if 'Resolution' in received_data: store['Resolution'] = received_data['Resolution'] _check_resolution(store, store['Resolution']) if 'MonitorLimits' in received_data: store['ResolutionTemp'] = received_data['MonitorLimits'] #Record key presses if 'KeyPress' in received_data: store['ActivitySinceLastSave'] = True for key in received_data['KeyPress']: try: store['Data']['Keys']['All']['Pressed'][key] += 1 except KeyError: store['Data']['Keys']['All']['Pressed'][key] = 1 try: store['Data']['Keys']['Session']['Pressed'][key] += 1 except KeyError: store['Data']['Keys']['Session']['Pressed'][key] = 1 #Record time keys are held down if 'KeyHeld' in received_data: store['ActivitySinceLastSave'] = True for key in received_data['KeyHeld']: try: store['Data']['Keys']['All']['Held'][key] += 1 except KeyError: store['Data']['Keys']['All']['Held'][key] = 1 try: store['Data']['Keys']['Session']['Held'][key] += 1 except KeyError: store['Data']['Keys']['Session']['Held'][key] = 1 #Calculate and track mouse movement if 'MouseMove' in received_data: store['ActivitySinceLastSave'] = True resolution = 0 _resolution = -1 start, end = received_data['MouseMove'] #distance = find_distance(end, start) #Calculate the pixels in the line if end is None: raise TypeError('debug - mouse moved without coordinates') if start is None: mouse_coordinates = [end] else: mouse_coordinates = [start, end] + calculate_line(start, end) #Don't bother calculating offset for each pixel #if both start and end are on the same monitor try: resolution, offset = monitor_offset(start, store['ResolutionTemp']) _resolution = monitor_offset(end, store['ResolutionTemp'])[0] except TypeError: pass #Write each pixel to the dictionary for pixel in mouse_coordinates: if MULTI_MONITOR: if resolution != _resolution: try: resolution, offset = monitor_offset(pixel, store['ResolutionTemp']) except TypeError: store['ResolutionTemp'] = monitor_info() try: resolution, offset = monitor_offset(pixel, store['ResolutionTemp']) except TypeError: continue pixel = (pixel[0] - offset[0], pixel[1] - offset[1]) if resolution not in store['ResolutionList']: _check_resolution(store, resolution) store['ResolutionList'].add(resolution) else: resolution = store['Resolution'] store['Data']['Maps']['Tracks'][resolution][pixel] = store['Data']['Ticks']['Current']['Tracks'] store['Data']['Ticks']['Current']['Tracks'] += 1 #Compress tracks if the count gets too high if store['Data']['Ticks']['Current']['Tracks'] > CONFIG['CompressMaps']['TrackMaximum']: compress_multplier = CONFIG['CompressMaps']['TrackReduction'] NOTIFY(TRACK_COMPRESS_START, 'track') NOTIFY.send(q_send) tracks = store['Data']['Maps']['Tracks'] for resolution in tracks.keys(): tracks[resolution] = {k: int(v // compress_multplier) for k, v in get_items(tracks[resolution])} tracks[resolution] = {k: v for k, v in get_items(tracks[resolution]) if v} if not tracks[resolution]: del tracks[resolution] NOTIFY(TRACK_COMPRESS_END, 'track') NOTIFY(QUEUE_SIZE, q_recv.qsize()) store['Data']['Ticks']['Current']['Tracks'] //= compress_multplier store['Data']['Ticks']['Session']['Current'] //= compress_multplier #Record mouse clicks if 'MouseClick' in received_data: store['ActivitySinceLastSave'] = True for mouse_button, pixel in received_data['MouseClick']: if MULTI_MONITOR: try: resolution, offset = monitor_offset(pixel, store['ResolutionTemp']) except TypeError: store['ResolutionTemp'] = monitor_info() try: resolution, offset = monitor_offset(pixel, store['ResolutionTemp']) except TypeError: continue pixel = (pixel[0] - offset[0], pixel[1] - offset[1]) else: resolution = store['Resolution'] try: store['Data']['Maps']['Clicks'][resolution][mouse_button][pixel] += 1 except KeyError: store['Data']['Maps']['Clicks'][resolution][mouse_button][pixel] = 1 #Increment the amount of time the script has been running for if 'Ticks' in received_data: store['Data']['Ticks']['Total'] += received_data['Ticks'] store['Data']['Ticks']['Recorded'] += 1 NOTIFY.send(q_send) #Exit process (this shouldn't happen for now) NOTIFY(THREAD_EXIT) NOTIFY.send(q_send) _save_wrapper(q_send, store['LastProgram'], store['Data'], False) except Exception as e: q_send.put(traceback.format_exc()) return
def start_tracking(): mouse_inactive_delay = 2 updates_per_second = CONFIG['Main']['UpdatesPerSecond'] timer = { 'UpdateScreen': CONFIG['Timer']['CheckResolution'], 'UpdatePrograms': CONFIG['Timer']['CheckPrograms'], 'Save': CONFIG['Save']['Frequency'], 'ReloadProgramList': CONFIG['Timer']['ReloadPrograms'], 'UpdateQueuedCommands': CONFIG['Timer']['_ShowQueuedCommands'] } timer = {k: v * updates_per_second for k, v in get_items(timer)} store = { 'Resolution': { 'Current': monitor_info(), 'Previous': None, 'Boundaries': None }, 'Mouse': { 'Position': { 'Current': None, 'Previous': None }, 'NotMoved': 0, 'Inactive': False, 'Clicked': {}, 'OffScreen': False }, 'Keyboard': { 'KeysPressed': {k: False for k in KEYS.keys()} }, 'LastActivity': 0, 'LastSent': 0, 'Save': { 'Finished': True, 'Next': timer['Save'] } } mouse_pos = store['Mouse']['Position'] #Start background process q_send = Queue() q_recv = Queue() p = Process(target=background_process, args=(q_send, q_recv)) #p.daemon = True p.start() q_send2 = Queue() q_recv2 = Queue() running_programs = ThreadHelper(running_processes, q_send2, q_recv2, q_send) running_programs.start() i = 0 NOTIFY(START_MAIN) while True: with RefreshRateLimiter(updates_per_second) as limiter: #Send data to thread try: if frame_data or frame_data_rp: last_sent = i - store['LastSent'] if frame_data: if last_sent: frame_data['Ticks'] = last_sent q_send.put(frame_data) if frame_data_rp: q_send2.put(frame_data_rp) store['LastSent'] = i except NameError: pass while not q_recv2.empty(): print_override('{} {}'.format(time_format(limiter.time), q_recv2.get())) #Print any messages from previous loop notify_extra = '' received_data = [] while not q_recv.empty(): received_message = q_recv.get() #Receive text messages try: if received_message.startswith( 'Traceback (most recent call last)'): return received_message except AttributeError: pass else: received_data.append(received_message) #Get notification when saving is finished try: received_message.pop('SaveFinished') except (KeyError, AttributeError): pass else: store['Save']['Finished'] = True store['Save']['Next'] = i + timer['Save'] if received_data: notify_extra = u' | '.join(received_data) notify_output = NOTIFY.get_output() if notify_extra: if notify_output: notify_output = notify_extra + ' | ' + notify_output else: notify_output = notify_extra if notify_output: print_override(u'{} {}'.format(time_format(limiter.time), notify_output)) frame_data = {} frame_data_rp = {} mouse_pos['Current'] = get_cursor_pos() #Check if mouse is inactive (such as in a screensaver) if mouse_pos['Current'] is None: if not store['Mouse']['Inactive']: NOTIFY(MOUSE_UNDETECTED) store['Mouse']['Inactive'] = True time.sleep(mouse_inactive_delay) continue #Check if mouse left the monitor elif ( not MULTI_MONITOR and (not 0 <= mouse_pos['Current'][0] < store['Resolution']['Current'][0] or not 0 <= mouse_pos['Current'][1] < store['Resolution']['Current'][1])): if not store['Mouse']['OffScreen']: NOTIFY(MOUSE_OFFSCREEN) store['Mouse']['OffScreen'] = True elif store['Mouse']['OffScreen']: NOTIFY(MOUSE_ONSCREEN) store['Mouse']['OffScreen'] = False #Notify once if mouse is no longer inactive if store['Mouse']['Inactive']: store['Mouse']['Inactive'] = False NOTIFY(MOUSE_DETECTED) #Check if mouse is in a duplicate position if mouse_pos['Current'] is None or mouse_pos[ 'Current'] == mouse_pos['Previous']: store['Mouse']['NotMoved'] += 1 elif store['Mouse']['NotMoved']: store['Mouse']['NotMoved'] = 0 if not store['Mouse']['NotMoved']: if not store['Mouse']['OffScreen']: frame_data['MouseMove'] = (mouse_pos['Previous'], mouse_pos['Current']) NOTIFY(MOUSE_POSITION, mouse_pos['Current']) store['LastActivity'] = i #Mouse clicks click_repeat = CONFIG['Main']['RepeatClicks'] for mouse_button, clicked in enumerate(get_mouse_click()): mb_clicked = store['Mouse']['Clicked'].get(mouse_button, False) mb_data = (mouse_button, mouse_pos['Current']) if clicked: store['LastActivity'] = i #First click if not mb_clicked: store['Mouse']['Clicked'][mouse_button] = limiter.time if not store['Mouse']['OffScreen']: NOTIFY(MOUSE_CLICKED, mouse_pos['Current'], mouse_button) try: frame_data['MouseClick'].append(mb_data) except KeyError: frame_data['MouseClick'] = [mb_data] else: NOTIFY(MOUSE_CLICKED_OFFSCREEN, mouse_button) #Held clicks elif click_repeat and mb_clicked < limiter.time - click_repeat: store['Mouse']['Clicked'][mouse_button] = limiter.time if not store['Mouse']['OffScreen']: NOTIFY(MOUSE_CLICKED_HELD, mouse_pos['Current'], mouse_button) try: frame_data['MouseClick'].append(mb_data) except KeyError: frame_data['MouseClick'] = [mb_data] elif mb_clicked: NOTIFY(MOUSE_UNCLICKED) del store['Mouse']['Clicked'][mouse_button] store['LastActivity'] = i #Key presses keys_pressed = [] keys_held = [] key_status = store['Keyboard']['KeysPressed'] key_press_repeat = CONFIG['Main']['RepeatKeyPress'] _keys_held = [] _keys_pressed = [] for k in KEYS: if get_key_press(KEYS[k]): keys_held.append(k) #If key is currently being held down if key_status[k]: if key_press_repeat and key_status[ k] < limiter.time - key_press_repeat: keys_pressed.append(k) _keys_held.append(k) key_status[k] = limiter.time #If key has been pressed else: keys_pressed.append(k) _keys_pressed.append(k) key_status[k] = limiter.time notify_key_press = list(keys_pressed) #If key has been released elif key_status[k]: key_status[k] = False if keys_pressed: frame_data['KeyPress'] = keys_pressed if keys_held: frame_data['KeyHeld'] = keys_held store['LastActivity'] = i if _keys_held: NOTIFY(KEYBOARD_PRESSES_HELD, _keys_held) if _keys_pressed: NOTIFY(KEYBOARD_PRESSES, _keys_pressed) #Check if resolution has changed if not i % timer['UpdateScreen']: if MULTI_MONITOR: frame_data['MonitorLimits'] = monitor_info() store['Resolution']['Boundaries'] = frame_data[ 'MonitorLimits'] else: store['Resolution']['Current'] = monitor_info() if store['Resolution']['Previous'] != store['Resolution'][ 'Current']: if store['Resolution']['Previous'] is not None: NOTIFY(RESOLUTION_CHANGED, store['Resolution']['Previous'], store['Resolution']['Current']) frame_data['Resolution'] = ['Resolution']['Current'] store['Resolution']['Previous'] = store['Resolution'][ 'Current'] #Display message that mouse has switched monitors if MULTI_MONITOR and 'MouseMove' in frame_data: try: try: res = monitor_offset( frame_data['MouseMove'][1], store['Resolution']['Boundaries'])[0] except TypeError: frame_data['MonitorLimits'] = monitor_info() store['Resolution']['Boundaries'] = frame_data[ 'MonitorLimits'] res = monitor_offset( frame_data['MouseMove'][1], store['Resolution']['Boundaries'])[0] except TypeError: pass else: store['Resolution']['Current'] = res if store['Resolution']['Previous'] is not None: if store['Resolution']['Current'] != store[ 'Resolution']['Previous']: NOTIFY(MONITOR_CHANGED, store['Resolution']['Previous'], store['Resolution']['Current']) store['Resolution']['Previous'] = store['Resolution'][ 'Current'] #Send request to update programs if not i % timer['UpdatePrograms']: frame_data_rp['Update'] = True #Send request to reload program list if not i % timer['ReloadProgramList']: frame_data_rp['Reload'] = True #Update user about the queue size if not i % timer['UpdateQueuedCommands'] and store[ 'LastActivity'] > i - timer['Save']: NOTIFY(QUEUE_SIZE, q_send.qsize()) #Send save request if store['Save'][ 'Finished'] and i and not i % store['Save']['Next']: frame_data['Save'] = True store['Save']['Finished'] = False if store['Mouse']['OffScreen']: mouse_pos['Previous'] = None else: mouse_pos['Previous'] = mouse_pos['Current'] i += 1