#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Fenrir TTY screen reader # By Chrys, Storm Dragon, and contributors. from fenrirscreenreader.core import debug import threading import time class ReadAllManager: """Manages continuous read-all functionality with speech completion callbacks.""" def __init__(self): self.active = False self.mode = None # 'line' or 'page' self.stop_requested = False self.env = None self.items_read = 0 self._lock = threading.Lock() def initialize(self, environment): self.env = environment def shutdown(self): self.stop_read_all() def start_read_all(self, mode): """Start continuous reading in line or page mode.""" with self._lock: if self.active: self.stop_read_all() self.active = True self.mode = mode self.stop_requested = False self.items_read = 0 self.env["runtime"]["DebugManager"].write_debug_out( f"ReadAllManager: Starting read-all mode '{mode}'", debug.DebugLevel.INFO ) # Send first navigation key self._send_navigation_key() def stop_read_all(self): """Stop continuous reading.""" with self._lock: if not self.active: return self.env["runtime"]["DebugManager"].write_debug_out( f"ReadAllManager: Stopping read-all after {self.items_read} {self.mode}s", debug.DebugLevel.INFO ) self.active = False self.stop_requested = True self.mode = None # Cancel any pending speech try: self.env['runtime']['SpeechDriver'].cancel() except Exception as e: self.env["runtime"]["DebugManager"].write_debug_out( f"ReadAllManager: Error canceling speech: {e}", debug.DebugLevel.ERROR ) def is_active(self): """Check if read-all is currently active.""" return self.active def get_mode(self): """Get current read-all mode ('line' or 'page').""" return self.mode def _send_navigation_key(self): """Send appropriate navigation key based on mode.""" if not self.active or self.stop_requested: return try: if self.mode == 'line': key_sequence = [[1, 'KEY_DOWN'], [0, 'KEY_DOWN']] key_name = 'KEY_DOWN' elif self.mode == 'page': key_sequence = [[1, 'KEY_PAGEDOWN'], [0, 'KEY_PAGEDOWN']] key_name = 'KEY_PAGEDOWN' else: return self.env["runtime"]["DebugManager"].write_debug_out( f"ReadAllManager: Sending {key_name} ({self.items_read + 1})", debug.DebugLevel.INFO ) self.env['runtime']['InputManager'].send_keys(key_sequence) self.items_read += 1 except Exception as e: self.env["runtime"]["DebugManager"].write_debug_out( f"ReadAllManager: Error sending key: {e}", debug.DebugLevel.ERROR ) self.stop_read_all() def speech_completed(self): """Called when speech completes - continue reading if active.""" if not self.active or self.stop_requested: return self.env["runtime"]["DebugManager"].write_debug_out( f"ReadAllManager: Speech completed, continuing read-all", debug.DebugLevel.INFO ) # Small delay to prevent overwhelming the system def continue_reading(): time.sleep(0.1) if self.active and not self.stop_requested: self._send_navigation_key() # Use daemon thread to avoid blocking thread = threading.Thread(target=continue_reading, daemon=True) thread.start()