Ejemplo n.º 1
0
    def speed(self, last_session=False, file_name=None):
        """Render speed track image."""

        track_data = self.data.get_speed()
        if track_data is None:
            Message('No tracking data found.')
            return None

        top_resolution, (min_value, max_value), tracks = track_data

        output_resolution, upscale_resolution = calculate_resolution(
            tracks.keys(), top_resolution)
        upscaled_arrays = upscale_arrays_to_resolution(tracks,
                                                       upscale_resolution)

        colour_range = self._get_colour_range(min_value, max_value,
                                              'GenerateSpeed')

        image_output = arrays_to_colour(colour_range, upscaled_arrays)
        image_output = image_output.resize(output_resolution, Image.ANTIALIAS)

        if file_name is None:
            file_name = self.name.generate('Speed', reload=True)

        if self.save:
            create_folder(file_name)
            Message('Saving image to "{}"...'.format(file_name))
            image_output.save(file_name)
            Message('Finished saving.')
Ejemplo n.º 2
0
def arrays_to_heatmap(numpy_arrays, gaussian_size, clip):
    """Convert list of arrays into a heatmap.
    The stages and values are chosen with trial and error, 
    so this function is still open to improvement.
    """

    #Add all arrays together
    Message('Merging arrays...')
    merged_arrays = numpy.merge(numpy_arrays, 'add', 'float64')

    #Set to constant values
    Message('Flattening values...')
    flattened = numpy.remap_to_range(merged_arrays)

    #Blur the array
    if gaussian_size:
        Message('Applying gaussian blur...')
        heatmap = blur(flattened, gaussian_size)
    else:
        heatmap = flattened

    Message('Finding range limits...')
    min_value = numpy.min(heatmap)

    #Lower the maximum value a little
    all_values = numpy.sort(heatmap.ravel(), unique=True)
    max_value = all_values[round_int(all_values.size * clip)]

    return ((min_value, max_value), heatmap)
Ejemplo n.º 3
0
    def keyboard(self, last_session=False, file_name=None):
        """Render keyboard image."""
        kb = DrawKeyboard(self.profile, self.data, last_session=last_session)

        image_output = kb.draw_image()

        if file_name is None:
            file_name = self.name.generate('Keyboard', reload=True)

        if self.save:
            create_folder(file_name)
            Message('Saving image to "{}"...'.format(file_name))
            image_output.save(file_name)
            Message('Finished saving.')
Ejemplo n.º 4
0
def get_resolution():
    """Get the resolution of the main monitor.
    Returns:
        (x, y) resolution as a tuple.
    """
    Message('Unable to read resolution, set to default of 1920x1080.')
    return (1920, 1080)
Ejemplo n.º 5
0
 def calculate(self):
     Message(self.string['coordinates'])
     (width,
      height), coordinate_dict = self.grid.generate_coordinates(self.keys)
     return {
         'Width': width,
         'Height': height,
         'Coordinates': coordinate_dict
     }
Ejemplo n.º 6
0
    def clicks(self, last_session=False, file_name=None, _double_click=False):
        """Render heatmap of clicks."""

        top_resolution, (min_value, max_value), clicks = self.data.get_clicks(
            session=last_session, double_click=_double_click)
        output_resolution, upscale_resolution = calculate_resolution(
            clicks.keys(), top_resolution)

        lmb = CONFIG['GenerateHeatmap']['_MouseButtonLeft']
        mmb = CONFIG['GenerateHeatmap']['_MouseButtonMiddle']
        rmb = CONFIG['GenerateHeatmap']['_MouseButtonRight']
        skip = []
        if lmb or mmb or rmb:
            if not lmb:
                skip.append(0)
            if not mmb:
                skip.append(1)
            if not rmb:
                skip.append(2)
        upscaled_arrays = upscale_arrays_to_resolution(clicks,
                                                       upscale_resolution,
                                                       skip=skip)

        (min_value, max_value), heatmap = arrays_to_heatmap(
            upscaled_arrays,
            gaussian_size=gaussian_size(upscale_resolution[0],
                                        upscale_resolution[1]),
            clip=1 - CONFIG['Advanced']['HeatmapRangeClipping'])

        colour_range = self._get_colour_range(min_value, max_value,
                                              'GenerateHeatmap')

        image_output = Image.fromarray(colour_range.convert_to_rgb(heatmap))
        image_output = image_output.resize(output_resolution, Image.ANTIALIAS)

        if file_name is None:
            file_name = self.name.generate('Clicks', reload=True)

        if self.save:
            create_folder(file_name)
            Message('Saving image to "{}"...'.format(file_name))
            image_output.save(file_name)
            Message('Finished saving.')
Ejemplo n.º 7
0
def upscale_arrays_to_resolution(arrays, target_resolution, skip=[]):
    """Upscale a dict of arrays to a certain resolution.
    The dictionary key must be a resolution,
    and the values can either be an array or list of arrays.
    
    Use skip to ignore array indexes in the list.
    """
    if isinstance(skip, int):
        skip = [skip]
    skip = set(skip)

    #Count number of arrays
    num_arrays = 0
    for resolution, array_list in get_items(arrays):
        if isinstance(array_list, (list, tuple)):
            array_len = len(array_list)
            num_arrays += array_len - len(
                [i for i in range(array_len) if i in skip])
        elif 0 not in skip:
            num_arrays += 1

    #Upscale each array
    Message('Upscaling arrays to {}x{}...'.format(target_resolution[0],
                                                  target_resolution[1]))
    processed = 0
    output = []
    for resolution, array_list in get_items(arrays):

        if not isinstance(array_list, (list, tuple)):
            array_list = [array_list]

        for i, array in enumerate(array_list):
            if i in skip:
                continue
            processed += 1
            Message('Processing array for {}x{} ({}/{})'.format(
                resolution[0], resolution[1], processed, num_arrays))
            zoom_factor = (target_resolution[1] / resolution[1],
                           target_resolution[0] / resolution[0])
            upscaled = upscale(array, zoom_factor)
            output.append(upscaled)
    return output
Ejemplo n.º 8
0
    def __init__(self, profile_name, data=None, last_session=False):

        all_strings = Language().get_strings()
        self._string = all_strings['string']
        self.string = all_strings['string']['keyboard']
        self.keys = all_strings['keyboard']['key']
        #self.word = all_strings['word']

        self.name = profile_name
        self.last_session = last_session
        Message(self._string['profile']['load'])
        self.reload(data)
Ejemplo n.º 9
0
    def get_output(self):
        allowed_levels = range(CONFIG['Advanced']['MessageLevel'], 3)
        output = [
            capitalize(u' | '.join(self.message_queue[i]))
            for i in allowed_levels
        ][::-1]
        message = u' | '.join(i for i in output if i)
        for msg in self._debug:
            Message(msg)

        self.reset()
        return message
Ejemplo n.º 10
0
    def convert_to_rgb(self, array):
        """Convert an array into an RGB numpy array."""

        message = 'Converting {} points to RGB values... (this may take a few seconds)'
        try:
            Message(message.format(array.size))
        except AttributeError:
            array = numpy.array(array)
            Message(message.format(array.size))

        new = numpy.round(numpy.divide(array - self.min, self._step_size), 0,
                          'int64')

        start_colour = self.cache[0]
        end_colour = self.cache[-1]
        colour_array = [[
            self.cache[item] if 0 <= item <= self.steps else
            start_colour if item < 0 else end_colour for item in sublst
        ] for sublst in new.tolist()]

        return numpy.array(colour_array, dtype='uint8')
Ejemplo n.º 11
0
    def keyboard(self, image_name):

        Message('Generating CSV from keyboard...')
        result = ['Key,Count,Time']
        for key in self.data['Keys']['All']['Pressed']:
            result.append('{},{},{}'.format(
                key, self.data['Keys']['All']['Pressed'][key],
                self.data['Keys']['All']['Held'][key]))

        file_name = image_name.generate('csv-keyboard', reload=True)
        create_folder(file_name)
        with open(file_name, 'w') as f:
            f.write('\n'.join(result))
Ejemplo n.º 12
0
def fix_poe_mine_build(profile_name, numpad_key):
    try:
        _num = 'NUM{}'.format(int(numpad_key))
    except ValueError:
        return False
    data = LoadData(profile_name, _reset_sessions=False)

    #Make sure record exists, quick delete if issue is obvious (0 press >0 held)
    try:
        if not data['Keys']['All']['Pressed'][_num]:
            raise KeyError

    except KeyError:
        try:
            del data['Keys']['All']['Held'][_num]
        except KeyError:
            pass

    else:
        #Count the other numpad items
        total = {'pressed': 0, 'held': 0, 'count': 0}
        for i in range(1, 10):
            if i == numpad_key:
                continue

            num = 'NUM{}'.format(i)
            try:
                total['pressed'] += data['Keys']['All']['Pressed'][num]
                total['held'] += data['Keys']['All']['Held'][num]
            except KeyError:
                pass
            else:
                total['count'] += 1

        #Get the average time the numpad is pressed for
        try:
            average_press_time = total['held'] / total['pressed']
        except ZeroDivisionError:
            average_press_time = 0
            Message('Unable to get an average as no other keypad data exists.')
            result = input(
                'Do you want to delete the numpad key instead (y/n)? ')
            if not is_yes(result):
                return False

        #Assign to numpad key
        new_held_time = round_int(data['Keys']['All']['Pressed'][_num] *
                                  average_press_time)
        data['Keys']['All']['Held'][_num] = new_held_time

    return save_data(profile_name, data)
Ejemplo n.º 13
0
    def tracks(self, image_name):

        Message('Generating CSV from tracks...')
        for resolution in self.data['Maps']['Tracks']:
            CONFIG['GenerateImages']['_TempResolutionX'], CONFIG[
                'GenerateImages']['_TempResolutionY'] = resolution

            result = self._generate(resolution,
                                    self.data['Maps']['Tracks'][resolution])
            if result is not None:
                file_name = image_name.generate('csv-tracks', reload=True)
                create_folder(file_name)
                with open(file_name, 'w') as f:
                    f.write(result)
Ejemplo n.º 14
0
 def _create_grid(self):
     Message(self.string['layout'])
     grid = KeyboardGrid(self.key_counts, _new_row=False)
     layout = Language().get_keyboard_layout()
     for row in layout:
         grid.new_row()
         for name, width, height in row:
             if name == '__STATS__':
                 hide_border = True
                 custom_colour = False
             else:
                 hide_border = False
                 custom_colour = None
             grid.add_key(name,
                          width,
                          height,
                          hide_border=hide_border,
                          custom_colour=custom_colour)
     return grid
Ejemplo n.º 15
0
def handle_error(trace=None, log=True):
    """Any errors are sent to here."""
    if trace is not None:

        output = [
            'Mouse Tracks {} ({}) | Python {} | {}'.format(
                VERSION, FILE_VERSION, PYTHON_VERSION, OPERATING_SYSTEM)
        ]
        output.append('')
        output.append(trace)
        output = '\n'.join(output)

        if log:
            file_name = format_file_path('{}\\error.txt'.format(DEFAULT_PATH))
            with open(file_name, 'w') as f:
                f.write(output)
        Message(trace)

        string = Language().get_strings()
        input(string['string']['exit'])

    sys.exit(0)
Ejemplo n.º 16
0
    def clicks(self, image_name):

        mouse_buttons = ['LMB', 'MMB', 'RMB']

        Message('Generating CSV from clicks...')

        for i, mouse_button in enumerate(self.data['Maps']['Click']['Single']):
            CONFIG['GenerateHeatmap']['_MouseButtonLeft'] = i == 0
            CONFIG['GenerateHeatmap']['_MouseButtonMiddle'] = i == 1
            CONFIG['GenerateHeatmap']['_MouseButtonRight'] = i == 2

            for resolution, array in get_items(
                    self.data['Maps']['Click']['Single'][mouse_button]):
                CONFIG['GenerateImages']['_TempResolutionX'], CONFIG[
                    'GenerateImages']['_TempResolutionY'] = resolution

                result = self._generate(resolution, array)
                if result is not None:
                    file_name = image_name.generate('csv-clicks', reload=True)
                    create_folder(file_name)
                    with open(file_name, 'w') as f:
                        f.write(result)
Ejemplo n.º 17
0
            average_press_time = 0
            Message('Unable to get an average as no other keypad data exists.')
            result = input(
                'Do you want to delete the numpad key instead (y/n)? ')
            if not is_yes(result):
                return False

        #Assign to numpad key
        new_held_time = round_int(data['Keys']['All']['Pressed'][_num] *
                                  average_press_time)
        data['Keys']['All']['Held'][_num] = new_held_time

    return save_data(profile_name, data)


Message('This is a fix for the numlock mine trick in Path of Exile.')
Message(
    'The trick fools the computer into thinking the key is held down, but it doesn\'t release when the game is quit.'
)
Message(
    'This fix replaces the \'held down\' time of the key with an average from the other numpad keys.'
)
Message(
    'The issue is now fixed for other profiles, as the held down keys will be ignored after a profile switch.'
)
profile_name = input('Type the name of the profile to fix: ')
numpad_key = input('Type which numpad key is being used for mines: ')
Message('Please wait while the fix is completed...')
if fix_poe_mine_build(profile_name, numpad_key):
    Message('Finished updating profile.')
else:
Ejemplo n.º 18
0
from __future__ import absolute_import

from core.compatibility import Message
from core.os import get_key_press
from core.track import RefreshRateLimiter

while True:
    with RefreshRateLimiter(10):
        keys = []
        for i in range(256):
            if get_key_press(i):
                keys.append(i)
        if keys:
            Message(keys)
Ejemplo n.º 19
0
    def draw_image(self, file_path=None, font='arial.ttf'):
        data = self.calculate()

        #Create image object
        image = Image.new('RGB', (data['Width'], data['Height']))
        background = tuple(list(data['Coordinates']['Background'][:3]) + [0])
        image.paste(data['Coordinates']['Background'],
                    (0, 0, data['Width'], data['Height']))
        pixels = image.load()

        #Add drop shadow
        shadow = (64, 64, 64)
        if (DROP_SHADOW_X or DROP_SHADOW_Y
            ) and data['Coordinates']['Background'][:3] == (255, 255, 255):
            Message(self.string['draw']['shadow'])
            shadow_colour = tuple(
                int(pow(i + 30, 0.9625))
                for i in data['Coordinates']['Shadow'])
            for colour in data['Coordinates']['Fill']:
                for x, y in data['Coordinates']['Fill'][colour]:
                    pixels[DROP_SHADOW_X + x, DROP_SHADOW_Y + y] = shadow

        #Fill colours
        Message(self.string['draw']['keys'])
        for colour in data['Coordinates']['Fill']:
            for x, y in data['Coordinates']['Fill'][colour]:
                pixels[x, y] = colour

        #Draw border
        Message(self.string['draw']['outline'])
        border = tuple(255 - i for i in data['Coordinates']['Background'])
        for x, y in data['Coordinates']['Outline']:
            pixels[x, y] = border

        #Draw text
        Message(self.string['draw']['text'])
        draw = ImageDraw.Draw(image)
        font_key = ImageFont.truetype(font, size=FONT_SIZE_MAIN)
        font_amount = ImageFont.truetype(font, size=FONT_SIZE_STATS)

        #Generate stats
        stats = [
            self.string['stats']['time'].format(
                T=ticks_to_seconds(self.ticks, 60))
        ]
        total_presses = format_amount(sum(self.key_counts['Pressed'].values()),
                                      'press',
                                      max_length=25,
                                      decimal_units=False)
        stats.append(self.string['stats']['count'].format(T=total_presses))
        if CONFIG['GenerateKeyboard']['DataSet'].lower() == 'time':
            stats.append(self.string['stats']['colour']['time'])
        elif CONFIG['GenerateKeyboard']['DataSet'].lower() == 'count':
            stats.append(self.string['stats']['colour']['count'])
        stats_text = ['{}:'.format(self.name), '\n'.join(stats)]

        #Write text to image
        for values in data['Coordinates']['Text']:
            x, y = values['Offset']
            text = values['KeyName']
            text_colour = values['Colour']

            #Override for stats text
            if text == '__STATS__':
                draw.text((x, y),
                          stats_text[0],
                          font=font_key,
                          fill=text_colour)
                y += (FONT_SIZE_MAIN + FONT_LINE_SPACING)
                draw.text((x, y),
                          stats_text[1],
                          font=font_amount,
                          fill=text_colour)
                continue

            height_multiplier = max(0, values['Dimensions'][1] - 1)
            x += FONT_OFFSET_X
            if not height_multiplier:
                y += FONT_OFFSET_Y
            y += (KEY_SIZE - FONT_SIZE_MAIN +
                  FONT_OFFSET_Y) * height_multiplier

            #Ensure each key is at least at a constant height
            if '\n' not in text:
                text += '\n'

            draw.text((x, y), text, font=font_key, fill=text_colour)

            #Correctly place count at bottom of key
            if height_multiplier:
                y = values['Offset'][1] + (
                    KEY_SIZE + KEY_PADDING) * height_multiplier + FONT_OFFSET_Y

            y += (FONT_SIZE_MAIN + FONT_LINE_SPACING) * (1 + text.count('\n'))

            #Here either do count or percent, but not both as it won't fit
            output_type = 'press'  #press or time
            max_width = int(10 * values['Dimensions'][0] - 3)

            text = format_amount(values['Counts'][output_type],
                                 output_type,
                                 max_length=max_width,
                                 min_length=max_width - 1,
                                 decimal_units=False)
            draw.text((x, y),
                      'x{}'.format(text),
                      font=font_amount,
                      fill=text_colour)

        if file_path:
            Message(self._string['image']['save']['start'])
            image.save(file_path, CONFIG['GenerateImages']['FileType'])
            Message(self._string['image']['save']['end'])
        return image
Ejemplo n.º 20
0
def user_generate():
    """Ask for options and generate an image.
    This seriously needs rewriting.
    
    Idea:
        List of profiles (choose page/type id/type name), shows the file size and last modified date of each profile.
        (Load profile)
        Say some stats about the profile
        Ask for mouse tracks, clicks and key presses
        For each of those, ask for colour profile and say the file location
        Ask to open folder (will require image path rewrite for a base path)
        Loop back to start if required
    """
    all_strings = Language().get_strings()
    _string = all_strings['string']
    string = all_strings['string']['image']
    word = all_strings['word']

    Message(string['profile']['list'].format(L=word['list']))
    profile = input()

    if profile.lower() == word['list'].lower():

        #Read the data folder and format names
        all_files = list_data_files()
        if not all_files:
            Message(string['profile']['empty'])
            Message(all_strings['exit'])
            input()
            sys.exit()
        programs = {format_name(DEFAULT_NAME): DEFAULT_NAME}

        app_list = AppList()
        for program_name in app_list.names:
            programs[format_name(program_name)] = program_name

        page = 1
        limit = 15
        maximum = len(all_files)
        total_pages = round_up(maximum / limit)

        sort_date = string['page']['sort']['date']
        sort_name = string['page']['sort']['name']
        change_sort = [sort_name, 2]

        #Ask for user input
        while True:
            offset = (page - 1) * limit

            results = all_files[offset:offset + limit]
            for i, r in enumerate(results):
                try:
                    program_name = programs[r]
                except KeyError:
                    program_name = r
                Message('{}: {}'.format(i + offset + 1, program_name))
            Message(string['page']['current'].format(C=page,
                                                     T=total_pages,
                                                     P=word['page']))

            Message(change_sort[0].format(
                S='{} {}'.format(word['sort'], change_sort[1])))
            Message(string['profile']['number']['input'])

            profile = input()
            last_page = page

            #Change page
            if profile.lower().startswith('{P} '.format(P=word['page'])):
                try:
                    page = int(profile.split()[1])
                    if not 0 < page <= total_pages:
                        raise ValueError
                except IndexError:
                    Message(string['page']['invalid'])
                except ValueError:
                    if page > total_pages:
                        page = total_pages
                    else:
                        page = 1

            #Shortcut to change page
            elif profile.endswith('>'):
                if page < total_pages:
                    page += 1
            elif profile.startswith('<'):
                if page > 1:
                    page -= 1

            #Change sorting of profile list
            elif (profile.lower().startswith('{} '.format(word['sort']))
                  or profile.lower() == word['sort']):
                try:
                    sort_level = int(profile.split()[1])
                except ValueError:
                    sort_level = 0
                except IndexError:
                    sort_level = 1
                try:
                    sort_reverse = int(profile.split()[2])
                except (ValueError, IndexError):
                    sort_reverse = 0
                if sort_level == 1:
                    all_files = list_data_files()
                    change_sort = [sort_name, 2]
                elif sort_level == 2:
                    all_files = sorted(list_data_files())
                    change_sort = [sort_date, 1]
                if sort_reverse:
                    all_files = all_files[::-1]

            #Select profile
            else:
                try:
                    num = int(profile) - 1
                    if not 0 <= num <= maximum:
                        raise IndexError
                    profile_name = all_files[num]
                    try:
                        profile = programs[all_files[num]]
                    except KeyError:
                        profile = all_files[num]
                    break
                except ValueError:
                    break
                except IndexError:
                    Message(string['profile']['number']['nomatch'])

        try:
            profile = programs[profile]
        except KeyError:
            pass

    #Load functions
    Message(_string['import'])
    from core.image import RenderImage

    Message(_string['profile']['load'].format(P=profile))
    try:
        r = RenderImage(profile)
    except ValueError:
        Message('Error: Selected profile is empty or doesn\'t exist.')
        return

    #Check if profile is running
    try:
        current_profile = format_name(RunningApplications().check()[0])
    except TypeError:
        pass
    else:
        selected_profile = format_name(profile)

        if current_profile == selected_profile:
            Message(string['profile']['running']['warning'])

            save_time = ticks_to_seconds(CONFIG['Save']['Frequency'], 1)
            metadata = load_data(profile, _metadata_only=True)
            if metadata['Modified'] is None:
                Message(string['save']['wait'])
                Message(string['save']['frequency'].format(T=save_time))
                Message(_string['exit'])
                input()
                sys.exit()
            else:
                last_save_time = time.time() - metadata['Modified']
                next_save_time = CONFIG['Save']['Frequency'] - last_save_time
                last_save = ticks_to_seconds(last_save_time,
                                             1,
                                             allow_decimals=False)
                next_save = ticks_to_seconds(next_save_time,
                                             1,
                                             allow_decimals=False)
                Message(string['save']['next'].format(T1=last_save,
                                                      T2=next_save))

    generate_tracks = False
    generate_speed = False
    generate_heatmap = False
    generate_keyboard = False
    generate_csv = False

    default_options = [True, False, True, True, False]

    kb_string = string['name']['keyboard']
    kph = r.keys_per_hour()
    if kph < 10:
        default_options[2] = False
        kb_string = '{} ({})'.format(
            kb_string,
            string['name']['low']['keyboard']).format(C=round(kph, 2))

    Message(string['option']['generate'])
    default_option_text = ' '.join(
        str(i + 1) for i, v in enumerate(default_options) if v)
    Message(string['option']['select'].format(V=default_option_text))
    Message('1: {}'.format(string['name']['track']))
    Message('2: {}'.format(string['name']['speed']))
    Message('3: {}'.format(string['name']['click']))
    Message('4: {}'.format(kb_string))
    Message('5: {}'.format(string['name']['csv']))

    selection = list(map(int, input().split()))
    result = value_select(selection, default_options, start=1)

    if result[0]:
        generate_tracks = True

    if result[1]:
        generate_speed = True

    if result[2]:

        generate_heatmap = True
        Message('Which mouse buttons should be included in the heatmap?.')

        default_options = [
            CONFIG['GenerateHeatmap']['_MouseButtonLeft'],
            CONFIG['GenerateHeatmap']['_MouseButtonMiddle'],
            CONFIG['GenerateHeatmap']['_MouseButtonRight']
        ]
        default_option_text = ' '.join(
            str(i + 1) for i, v in enumerate(default_options) if v)
        Message(string['option']['select'].format(V=default_option_text))

        Message('1: {}'.format(word['mousebutton']['left']))
        Message('2: {}'.format(word['mousebutton']['middle']))
        Message('3: {}'.format(word['mousebutton']['right']))
        selection = list(map(int, input().split()))
        heatmap_buttons = value_select(selection, default_options, start=1)
        CONFIG['GenerateHeatmap']['_MouseButtonLeft'] = heatmap_buttons[0]
        CONFIG['GenerateHeatmap']['_MouseButtonMiddle'] = heatmap_buttons[1]
        CONFIG['GenerateHeatmap']['_MouseButtonRight'] = heatmap_buttons[2]
        if not any(heatmap_buttons):
            generate_heatmap = False

    if result[3]:
        generate_keyboard = True

    if result[4]:
        generate_csv = True

    if any((generate_tracks, generate_speed, generate_heatmap,
            generate_keyboard, generate_csv)):

        last_session_start = r.data['Ticks']['Session']['Total']
        last_session_end = r.data['Ticks']['Total']
        all_time = ticks_to_seconds(last_session_end, UPDATES_PER_SECOND)
        last_session_time = ticks_to_seconds(
            last_session_end - last_session_start, UPDATES_PER_SECOND)

        csv_only = generate_csv and not any(
            (generate_tracks, generate_heatmap, generate_keyboard))
        if not last_session_time or last_session_time == all_time or csv_only:
            last_session = False

        else:
            while True:
                Message(string['option']['session']['select'])

                Message('1: {} [{}]'.format(
                    string['option']['session']['all'].format(T=all_time),
                    word['default']))
                Message('2: {}'.format(
                    string['option']['session']['last'].format(
                        T=last_session_time)))

                selection = list(map(int, input().split()))
                result = value_select(selection, [True, False], start=1)
                if result[0] and result[1]:
                    Message(string['option']['error']['single'])
                elif result[1]:
                    last_session = True
                    break
                else:
                    last_session = False
                    break

        #Generate
        if generate_tracks:
            r.tracks(last_session)
        if generate_speed:
            r.speed(last_session)
        if generate_heatmap:
            r.clicks(last_session)
        if generate_keyboard:
            r.keyboard(last_session)
        if generate_csv:
            r.csv()
        if CONFIG['GenerateImages']['OpenOnFinish']:
            Message(string['option']['open'])
            open_folder(r.name.generate())

    else:
        Message(string['option']['error']['nothing'])
from __future__ import absolute_import

from core.compatibility import Message
from core.os import WindowFocus
from core.track import RefreshRateLimiter

while True:
    with RefreshRateLimiter(10):
        Message(WindowFocus())