#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Fenrir TTY screen reader # By Chrys, Storm Dragon, and contributors. import time from fenrirscreenreader.core.i18n import _ class command: def __init__(self): pass def initialize(self, environment): self.env = environment def shutdown(self): pass def get_description(self): return _("Announces incoming text changes") def _was_handled_by_tab_completion(self, delta_text): """Check if this delta was already handled by tab completion to avoid duplicates""" if "tabCompletion" not in self.env["commandBuffer"]: return False tab_state = self.env["commandBuffer"]["tabCompletion"] # Check if this exact delta was processed recently by tab completion if (tab_state.get("lastProcessedDelta") == delta_text and tab_state.get("lastProcessedTime")): # Only suppress if processed within the last 50ms to avoid stale suppression # Reduced from 100ms to minimize false positives with rapid multi-line updates time_since_processed = time.time() - tab_state["lastProcessedTime"] if time_since_processed <= 0.05: return True return False def run(self): if not self.env["runtime"]["SettingsManager"].get_setting_as_bool( "speech", "auto_read_incoming" ): return # is there something to read? if not self.env["runtime"]["ScreenManager"].is_delta(ignoreSpace=True): return delta_text = self.env["screen"]["new_delta"] # Skip if tab completion already handled this delta if self._was_handled_by_tab_completion(delta_text): return # Flood protection: skip if too many lines to prevent system slowdown # Get threshold from settings, default to 500 lines flood_threshold = self.env["runtime"]["SettingsManager"].get_setting_as_int( "speech", "flood_protection_lines" ) if flood_threshold <= 0: flood_threshold = 500 # Fallback default line_count = delta_text.count('\n') + 1 if line_count > flood_threshold: # Too much text - skip to prevent system slowdown return # this must be a keyecho or something # if len(self.env['screen']['new_delta'].strip(' \n\t')) <= 1: x_move = abs( self.env["screen"]["new_cursor"]["x"] - self.env["screen"]["old_cursor"]["x"] ) y_move = abs( self.env["screen"]["new_cursor"]["y"] - self.env["screen"]["old_cursor"]["y"] ) if (x_move >= 1) and x_move == len(delta_text): # if len(self.env['screen']['new_delta'].strip(' \n\t0123456789')) # <= 2: if "\n" not in delta_text: return # print(x_move, y_move, len(self.env['screen']['new_delta']), len(self.env['screen']['newNegativeDelta'])) self.env["runtime"]["OutputManager"].present_text( delta_text, interrupt=False, flush=False ) def set_callback(self, callback): pass