def ui_event(event, arg): if amethyst_running(): if event in ( "app_activate", "app_launch", "app_close", "win_open", "win_close", ): if event[:4] == "win_" and arg.app.name in ("Amethyst", "loginwindow"): return if event[:4] == "app_" and arg.name in ( "AddressBookSourceSync", "Google Software Update", "CoreServicesUIAgent", "AddressBookManager", "loginwindow", ): return try: print(event, arg) print(arg.app.name, arg.name) print(ui.active_window()) print(ui.active_window().hidden) print() except: pass press("alt-shift-z") cron.after("250ms", lambda: press("alt-shift-z")) cron.after("250ms", lambda: press("alt-shift-z"))
def set_voice_type(type, silent=None): global voice_type, last_voice_type if voice_type != VoiceType.SLEEPING: last_voice_type = voice_type voice_type = type talon_enabled = type == VoiceType.TALON dragon_enabled = type == VoiceType.DRAGON dictation_enabled = type == VoiceType.DICTATION global speech speech.set_enabled(type == VoiceType.TALON or type == VoiceType.DICTATION) global dictation_group if not dictation_enabled: dictation_group.disable() global engine if dragon_enabled: engine.mimic("wake up".split()) elif last_voice_type == VoiceType.DRAGON: # else: engine.mimic("go to sleep".split()) if dictation_enabled: # Without postponing this "go to sleep" will be printed cron.after("0s", lambda: dictation_group.enable()) if silent == None: show_mode_message()
def _maybe_record(): """In the right context, start recording on every mic, otherwise stop.""" global _last_transition if "user._noise_recorder_context" in scope.get("tag", []): # Assume it's a fullscreen video if the window is on the PRIMARY screen, # and matches the fullscreen dimensions. This may require the primary # screen to have a toolbar to work properly. app = ui.active_app() window = app.active_window should_record = 0 == window.rect.compare_to_rect(ui.main_screen().rect) else: should_record = False # The window dimensions can bounce around during the transitions to & from # fullscreen, so deadzones are used for debouncing. if should_record: if (not recording() and time.monotonic() > _last_transition + TRANSITION_DEADZONE): _last_transition = time.monotonic() noise, existing = noise_with_least_data() LOGGER.info(f'Recording noise with the least data: "{noise}", ' f"{existing / 60:0.1f} mins exist already.") record(noise) # TODO: Probably enable a tag here so people can hook behaviour elif recording( ) and time.monotonic() > _last_transition + TRANSITION_DEADZONE: _last_transition = time.monotonic() stop() # Lambda is used becayse Python thinks `print_total_noise_recorded` # isn't callable. cron.after("2s", actions.self.print_total_noise_recorded)
def update_screenshot(self): def finish_capture(): self.img = screen.capture_rect(self.rect) self.mcanvas.freeze() self.mcanvas.hide() cron.after("16ms", finish_capture)
def make_selection(index: int): global active_word_list cron.after("0s", close_homophones) if is_selection: clip.set(active_word_list[index - 1]) actions.insert(active_word_list[index - 1])
def reset_gui_timer(): global phrase_nonce if gui_enabled: phrase_nonce += 1 nonce = phrase_nonce gui.show() cron.after(f"{auto_hide_delay.get()}s", lambda: maybe_hide(nonce))
def _log(args): global text, can if "command" not in scope.get("mode"): return text = f"\"{' '.join(args['text'])}\"" can.show() can.freeze() actions.sleep(action_wait) cron.after(canvas_hide_wait, hide_canvas)
def do_capture(): print( "capturing area", self.offset_x, self.offset_y, self.width, self.height, ) self.img = screen.capture(self.offset_x, self.offset_y, self.width, self.height) cron.after("5ms", finish_capture)
def make_selection(m, choices, callback): cron.after("0s", close_choices) words = m._words d = None if len(words) == 1: d = int(parse_word(words[0])) else: d = int(parse_word(words[1])) choice = choices[d - 1] callback(choice)
def make_selection(m, is_selection, transform=lambda x: x): cron.after("0s", close_homophones) words = m._words d = None if len(words) == 1: d = int(parse_word(words[0])) else: d = int(parse_word(words[1])) w = active_word_list[d - 1] if len(words) > 1: w = transform(w) insert(w, is_selection)
def make_selection(m): cron.after("0s", close_directories) words = m._words d = None if len(words) == 1: d = int(parse_word(words[0])) else: d = int(parse_word(words[1])) w = active_word_list[d - 1] cd_statement = f"cd {dirs[w]} && ls" insert(cd_statement)
def make_selection(m): cron.after("0s", close_directories) words = m._words print("CLIPBOARD:", CLIPBOARD) d = None if len(words) == 1: d = int(parse_word(words[0])) else: d = int(parse_word(words[1])) w = CLIPBOARD[d - 1] Str(w)(None)
def _write_frames(self): """Write the frames so far to a file & clear them.""" sample_rate = 16000 # Ignore short recordings, these are probably accidental. if len(self._frames) >= sample_rate * MINIMUM_RECORDING_LENGTH: path = self._get_chunk_path() # TODO: Do this on a delay later # ^ What did I mean by this??? frames = self._frames self._frames = [] # TODO: Spawn thread for this? stopping the stream may also be slow. LOGGER.info(f"Writing noise file: {path}") flac.write_flac( str(path), frames, sample_rate=sample_rate, compression_level=1 ) # This will fire once per device, so deadzone it. duration = len(frames) / sample_rate # HACK: Sometimes Windows will suppress this notification so throw # it on a delay. (The delay also allows us the report of the total # to factor in all mics in this session.) # # TODO: Add up time from all split recordings # # TODO: Organisation of this notification scheme is gross. At some # point rewrite it. noise_name = self.noise_name def report_success(): nonlocal duration, noise_name total_hours = total_data() / 60 / 60 num_mics = len(amounts_recorded_by_device()) _notify_with_deadzone( "Noise Recorded", # TODO: Read this from disk to take into account multiple chunks f'Recorded {duration:0.0f} seconds of: "{noise_name}" ({duration/60:0.1f} mins). Say "delete last recording" to discard it. All noises: {total_hours:0.1f} hours across {num_mics} mics.', ), cron.after("500ms", report_success) else: LOGGER.info( f"Recording under {MINIMUM_RECORDING_LENGTH} seconds, file not written: {self}" ) # HACK: Sometimes Windows will suppress this notification so throw # it on a delay. cron.after( "500ms", # This will fire once per device, so deadzone it. lambda: _notify_with_deadzone( "Recording Discarded", f'Recording under {MINIMUM_RECORDING_LENGTH} seconds, discarding: "{self.noise_name}"', ), )
def terminal_hotkey(_, e): """Adds an alt-w to pick up zle selection as well.""" window = ui.active_window() bundle = window.app.bundle if bundle != "com.googlecode.iterm2": return if e == "cmd-c" and e.up: # print("intercept " + str(e)) Key("alt-w")(None) elif e == "enter" and e.up: # print("intercept " + str(e)) cron.after("500ms", lambda: update_ctx(None)) return True
def check_step(self, phrase): """Check if contents in the phrase match the voice commands available in the step""" if self.current_walkthrough is not None and self.is_in_right_context(): phrase_to_check = " ".join(phrase["phrase"]).lower() if self.current_stepnumber < len(self.current_walkthrough.steps): step = self.current_walkthrough.steps[self.current_stepnumber] current_length = len(self.current_words) for index, voice_command in enumerate(step.voice_commands): # Make sure the activations can only happen in-order if index >= current_length: if voice_command in phrase_to_check: self.current_words.append(voice_command) phrase_to_check = phrase_to_check.split(voice_command, 1)[1] else: break # Send an update about the voice commands said during the step if it has changed if current_length != len(self.current_words): step.said_walkthrough_commands = self.current_words[:] self.content.publish_event("walkthrough_step", "walkthrough_step", "replace", copy.copy(step), show=True, claim=True) # Skip to the next step if no voice commands are available voice_commands_remaining = copy.copy(step.voice_commands) all_commands_said = False for said_word in self.current_words: if said_word in voice_commands_remaining: voice_commands_remaining.remove(said_word) if len(step.voice_commands) > 0 and len(voice_commands_remaining) == 0 and not "skip step" in step.voice_commands and not "continue" in step.voice_commands: cron.cancel(self.next_step_job) self.next_step_job = cron.after("1500ms", self.next_step)
def on_finish(self): if self._gap_tolerance: with self._job_lock: self._end_job = cron.after(f"{self._gap_tolerance}ms", self._exit_safe) else: self._exit_safe()
def move_focus_indicator(self, window): cron.cancel(self.move_indicator_job) active_window = ui.active_window() if active_window.rect.x != self.previous_window_x and active_window.rect.y != self.previous_window_y: self.move_indicator_job = cron.after("30ms", self.update_focus_indicator)
def repeat(self): repeater = Rep(1) repeater.ctx = talon repeater(None) if self.job: self.job = cron.after(self.repeat_delay, self.repeat) print('REPEAT')
def make_selection(m, is_selection, transform=lambda x: x): cron.after("0s", close_homophones) words = m._words d = None if len(words) == 1: d = int(parse_word(words[0])) else: d = int(parse_word(words[1])) w = active_word_list[d - 1] if len(words) > 1: w = transform(w) if is_selection: clip.set(w) press("cmd-v", wait=0) else: Str(w)(None)
def hud_environment_change(self, hud_environment: str): if self.current_talon_hud_environment != hud_environment: self.set_current_flow("environment_changed") self.current_talon_hud_environment = hud_environment # Add a debouncer for the environment change to reduce flickering on transitioning cron.cancel(self.update_environment_debouncer) self.update_environment_debouncer = cron.after( "200ms", self.debounce_environment_change)
def on_noise(self, noise): if noise == 'hiss_start' and talon.enabled: if self.job is None: self.job = cron.after(self.initial_delay, self.repeat) print('HISS START') elif noise == 'hiss_end' and self.job: print('HISS STOP') cron.cancel(self.job) self.job = None
def on_draw(c): c.paint.typeface = "arial" # The min(width, height) is to not get gigantic size on portrait screens c.paint.textsize = round(min(c.width, c.height) / 2) text = f"{number}" rect = c.paint.measure_text(text)[1] x = c.x + c.width / 2 - rect.x - rect.width / 2 y = c.y + c.height / 2 + rect.height / 2 c.paint.style = c.paint.Style.FILL c.paint.color = "eeeeee" c.draw_text(text, x, y) c.paint.style = c.paint.Style.STROKE c.paint.color = "000000" c.draw_text(text, x, y) cron.after("3s", canvas.close)
def rephrase(phrase: Phrase, run_async: bool = False): """Re-evaluate and run phrase""" try: current_phrase = phrase_stack[-1] ts = current_phrase["_ts"] start = phrase.words[0].start - ts end = phrase.words[-1].end - ts samples = current_phrase["samples"] pstart = int(start * 16_000) pend = int(end * 16_000) samples = samples[pstart:pend] except KeyError: return if run_async: cron.after("0ms", lambda: speech_system._on_audio_frame(samples)) else: speech_system._on_audio_frame(samples)
def default_click(click_info: Click): modifiers = click_info.modifiers actions.self.queue_zoom_action(lambda: click_info.function(modifiers)) # If we're dragging, it means we intend to drop, so we can queue both # at once. # # HACK: Intercepting the function here is pretty hacky. # # TODO: Remove `str` cast once action path comparison works if str(click_info.function) == str(actions.user.drag): def queue_drop(): nonlocal modifiers actions.user.queue_zoom_action(lambda: actions.user.drop(modifiers)) # Add drop on a delay so the ding rings twice. cron.after("150ms", queue_drop)
def start(self): print(talon.last) if self.job is None: self.action, self.rule = talon.last_action[0] print('START') repeat_context.keymap({ 'stop': lambda m: self.stop(), }) repeat_context.load() self.job = cron.after(self.initial_delay, self.repeat)
def enable(self, persisted=False): if not self.enabled: self.enabled = True self.display_state.register("broadcast_update", self.broadcast_update) # Only reset the talon HUD environment after a user action # And only set the visible tag self.current_talon_hud_environment = settings.get( "user.talon_hud_environment", "") if persisted: self.set_current_flow("enabled") self.current_flow = "enable" ctx.tags = [ "user.talon_hud_available", "user.talon_hud_visible", "user.talon_hud_choices_visible" ] # Connect the events relating to non-content communication self.event_dispatch.register("persist_preferences", self.debounce_widget_preferences) self.event_dispatch.register("hide_context_menu", self.hide_context_menu) self.event_dispatch.register("deactivate_poller", self.deactivate_poller) self.event_dispatch.register("show_context_menu", self.move_context_menu) self.event_dispatch.register("synchronize_poller", self.synchronize_widget_poller) # Reload the preferences just in case a screen change happened in between the hidden state if persisted or self.current_flow in ["repair", "initialize"]: reload_theme = self.widget_manager.reload_preferences( True, self.current_talon_hud_environment) if reload_theme != self.theme.name: self.switch_theme(reload_theme, True) for widget in self.widget_manager.widgets: if widget.preferences.enabled and not widget.enabled: widget.enable() self.synchronize_pollers() ui.register("screen_change", self.reload_preferences) settings.register("user.talon_hud_environment", self.hud_environment_change) self.determine_active_setup_mouse() if persisted: self.preferences.persist_preferences({"enabled": True}) self.set_current_flow("manual") # Make sure context isn't updated in this thread because of automatic reloads cron.cancel(self.update_context_debouncer) self.update_context_debouncer = cron.after("50ms", self.update_context)
def load_state(self): if not os.path.exists(walkthrough_file_location): self.persist_walkthrough_steps(self.walkthrough_steps) fh = open(walkthrough_file_location, "r") lines = fh.readlines() fh.close() walkthrough_steps = {} for index,line in enumerate(lines): split_line = line.strip("\n").split(",") key = split_line[0] current_step = split_line[1] total_step = split_line[2] walkthrough_steps[key] = {"current": int(current_step), "total": int(total_step), "progress": int(current_step) / int(total_step) if int(total_step) > 0 else 0} self.walkthrough_steps = walkthrough_steps # For the initial loading, start the walkthrough if it hasn"t been completed fully yet if initial_walkthrough_title not in self.walkthrough_steps or \ self.walkthrough_steps[initial_walkthrough_title]["current"] < self.walkthrough_steps[initial_walkthrough_title]["total"]: cron.after("1s", self.start_up_hud)
def _on_hiss(start: bool): global _hiss_start_job if start: start_deadzone = settings["user.hiss_start_deadzone"] if start_deadzone: _hiss_start_job = cron.after(f"{start_deadzone}ms", _cued_start_hiss) else: actions.self.on_hiss(True) else: if _hiss_start_job: cron.cancel(_hiss_start_job) actions.self.on_hiss(False)
def on_noise(self, noise): pass now = time() # print(noise) if noise == 'pop': print('POP') if self.job: cron.cancel(self.job) self.job = None print('STOP') elif now - self.last_pop < 0.5 and self.job is not None: self.job = cron.after(self.initial_delay, self.repeat) print('START') self.last_pop = now
def insert(s): global last_insert, reenable_job last_insert = s if eye_zoom_mouse.zoom_mouse.enabled: eye_zoom_mouse.zoom_mouse.toggle() if eye_mouse.control_mouse.enabled: eye_mouse.control_mouse.toggle() ctrl.cursor_visible(True) if reenable_job is None: reenable_job = cron.after("3s", enable_tracking) if reenable_job is not None: debounce_enable_job() Str(s)(None)