Esempio n. 1
0
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)
Esempio n. 2
0
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)
Esempio n. 3
0
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()
Esempio n. 4
0
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()
Esempio n. 5
0
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
Esempio n. 6
0
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