Updated Braille support now that I'm more familiar with how it should work.
This commit is contained in:
parent
84514edc96
commit
1696d62526
@ -2,65 +2,167 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Fenrir TTY screen reader
|
||||
# By Chrys, Storm Dragon, and contributers.
|
||||
# By Chrys, Storm Dragon, and contributors.
|
||||
|
||||
from fenrirscreenreader.core import debug
|
||||
from fenrirscreenreader.core.brailleDriver import brailleDriver
|
||||
|
||||
class driver(brailleDriver):
|
||||
"""BRLAPI implementation of the braille driver interface.
|
||||
Supports all braille displays compatible with BRLTTY."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the BRLAPI driver."""
|
||||
brailleDriver.__init__(self)
|
||||
self._brl = None
|
||||
self._last_written_text = ""
|
||||
|
||||
def initialize(self, environment):
|
||||
"""Initialize the braille driver with BRLAPI connection."""
|
||||
self.env = environment
|
||||
try:
|
||||
import brlapi
|
||||
self._brl = brlapi.Connection()
|
||||
self._deviceSize = self._brl.displaySize
|
||||
# Accept all key types from any braille display
|
||||
self._brl.acceptKeys(brlapi.rangeType_all, [0, brlapi.KEY_MAX])
|
||||
self._current_cursor_pos = 0
|
||||
except Exception as e:
|
||||
print(e)
|
||||
self.env['runtime']['debug'].writeDebugOut(str(e),debug.debugLevel.ERROR)
|
||||
self.env['runtime']['debug'].writeDebugOut(str(e), debug.debugLevel.ERROR)
|
||||
return
|
||||
self._isInitialized = True
|
||||
|
||||
def getDeviceSize(self):
|
||||
if not self._isInitialized:
|
||||
return (0,0)
|
||||
if not self._deviceSize:
|
||||
return (0,0)
|
||||
"""Get the size of the connected braille display."""
|
||||
if not self._isInitialized or not self._deviceSize:
|
||||
return (0, 0)
|
||||
return self._deviceSize
|
||||
|
||||
def flush(self):
|
||||
if not self._isInitialized:
|
||||
return
|
||||
try:
|
||||
self._brl.writeText('',0)
|
||||
except Exception as e:
|
||||
self.env['runtime']['debug'].writeDebugOut('BRAILLE.flush '+str(e),debug.debugLevel.ERROR)
|
||||
|
||||
def writeText(self,text):
|
||||
def writeText(self, text):
|
||||
"""Write text to the braille display."""
|
||||
if not self._isInitialized:
|
||||
return
|
||||
try:
|
||||
self._last_written_text = text
|
||||
# Handle unicode properly for international braille
|
||||
if isinstance(text, str):
|
||||
self._brl.writeText(text)
|
||||
else:
|
||||
self._brl.writeText(text.decode('utf-8', 'replace'))
|
||||
except Exception as e:
|
||||
self.env['runtime']['debug'].writeDebugOut('BRAILLE.writeText '+str(e),debug.debugLevel.ERROR)
|
||||
self.env['runtime']['debug'].writeDebugOut('BRAILLE.writeText ' + str(e), debug.debugLevel.ERROR)
|
||||
|
||||
def connectDevice(self):
|
||||
"""Establish connection with BRLAPI."""
|
||||
try:
|
||||
import brlapi
|
||||
self._brl = brlapi.Connection()
|
||||
# Accept all key types from the connected display
|
||||
self._brl.acceptKeys(brlapi.rangeType_all, [0, brlapi.KEY_MAX])
|
||||
return True
|
||||
except Exception as e:
|
||||
self.env['runtime']['debug'].writeDebugOut('BRAILLE.connectDevice ' + str(e), debug.debugLevel.ERROR)
|
||||
return False
|
||||
|
||||
def enterScreen(self, screen):
|
||||
"""Enter a specific TTY mode."""
|
||||
if not self._isInitialized:
|
||||
return
|
||||
try:
|
||||
self._brl.enterTtyMode(int(screen))
|
||||
except Exception as e:
|
||||
self.env['runtime']['debug'].writeDebugOut('BRAILLE.enterScreen ' + str(e), debug.debugLevel.ERROR)
|
||||
|
||||
def leveScreen(self):
|
||||
def leaveScreen(self):
|
||||
"""Leave the current TTY mode."""
|
||||
if not self._isInitialized:
|
||||
return
|
||||
try:
|
||||
self._brl.leaveTtyMode()
|
||||
except Exception as e:
|
||||
self.env['runtime']['debug'].writeDebugOut('BRAILLE.leaveScreen ' + str(e), debug.debugLevel.ERROR)
|
||||
|
||||
def setCursor(self, position):
|
||||
"""Set the cursor position on the braille display."""
|
||||
if not self._isInitialized:
|
||||
return
|
||||
try:
|
||||
max_pos = self.getDeviceSize()[0] - 1
|
||||
self._current_cursor_pos = max(0, min(position, max_pos))
|
||||
# Use BRLAPI's cursor command which works across different displays
|
||||
self._brl.writeDots(1 << self._current_cursor_pos)
|
||||
except Exception as e:
|
||||
self.env['runtime']['debug'].writeDebugOut('BRAILLE.setCursor ' + str(e), debug.debugLevel.ERROR)
|
||||
|
||||
def getCursorPosition(self):
|
||||
"""Get the current cursor position."""
|
||||
if not self._isInitialized:
|
||||
return 0
|
||||
return self._current_cursor_pos
|
||||
|
||||
def clear(self):
|
||||
"""Clear the braille display."""
|
||||
if not self._isInitialized:
|
||||
return
|
||||
try:
|
||||
self._brl.writeText('')
|
||||
self._last_written_text = ""
|
||||
self._current_cursor_pos = 0
|
||||
except Exception as e:
|
||||
self.env['runtime']['debug'].writeDebugOut('BRAILLE.clear ' + str(e), debug.debugLevel.ERROR)
|
||||
|
||||
def flush(self):
|
||||
"""Force an update of the physical display."""
|
||||
if not self._isInitialized:
|
||||
return
|
||||
try:
|
||||
if self._last_written_text:
|
||||
self._brl.writeText(self._last_written_text)
|
||||
except Exception as e:
|
||||
self.env['runtime']['debug'].writeDebugOut('BRAILLE.flush ' + str(e), debug.debugLevel.ERROR)
|
||||
|
||||
def handleKeyInput(self, key):
|
||||
"""Handle input from any BRLTTY-compatible braille display.
|
||||
Returns the key event for processing by Fenrir's input system."""
|
||||
if not self._isInitialized:
|
||||
return False
|
||||
try:
|
||||
key_code = self._brl.readKey(wait=0)
|
||||
if not key_code:
|
||||
return False
|
||||
|
||||
# Let Fenrir's input system handle the key
|
||||
# This allows proper handling regardless of display type
|
||||
if self.env['runtime']['debug'].debugLevel >= debug.debugLevel.INFO:
|
||||
self.env['runtime']['debug'].writeDebugOut(
|
||||
'BRAILLE.keyPressed: ' + str(key_code),
|
||||
debug.debugLevel.INFO
|
||||
)
|
||||
return True
|
||||
except brlapi.ConnectionError:
|
||||
self.env['runtime']['debug'].writeDebugOut(
|
||||
'BRAILLE: Connection lost. Attempting to reconnect...',
|
||||
debug.debugLevel.ERROR
|
||||
)
|
||||
self.connectDevice()
|
||||
return False
|
||||
except Exception as e:
|
||||
self.env['runtime']['debug'].writeDebugOut(
|
||||
'BRAILLE.handleKeyInput ' + str(e),
|
||||
debug.debugLevel.ERROR
|
||||
)
|
||||
return False
|
||||
|
||||
def shutdown(self):
|
||||
"""Shut down the BRLAPI driver."""
|
||||
if not self._isInitialized:
|
||||
return
|
||||
self.leveScreen()
|
||||
try:
|
||||
self.clear()
|
||||
self.leaveScreen()
|
||||
if self._brl:
|
||||
self._brl.closeConnection()
|
||||
self._brl = None
|
||||
self._isInitialized = False
|
||||
except Exception as e:
|
||||
self.env['runtime']['debug'].writeDebugOut('BRAILLE.shutdown ' + str(e), debug.debugLevel.ERROR)
|
||||
|
@ -1,82 +1,156 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Fenrir TTY screen reader
|
||||
# By Chrys, Storm Dragon, and contributors.
|
||||
|
||||
import brlapi
|
||||
from abc import ABC, abstractmethod
|
||||
from fenrirscreenreader.core import debug
|
||||
|
||||
class brailleDriver():
|
||||
class brailleDriver(ABC):
|
||||
"""Base class for Fenrir braille display drivers.
|
||||
|
||||
This abstract base class defines the interface that all braille drivers must implement.
|
||||
It provides basic initialization state tracking and defines the required methods
|
||||
for interacting with braille displays.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the driver with default state."""
|
||||
self._isInitialized = False
|
||||
self._brl = None
|
||||
self.deviceSize = None
|
||||
self.env = None
|
||||
self._current_cursor_pos = 0
|
||||
self._display_content = ""
|
||||
|
||||
@abstractmethod
|
||||
def initialize(self, environment):
|
||||
"""Initialize the BRLTTY connection."""
|
||||
self.env = environment
|
||||
try:
|
||||
self._brl = brlapi.Connection()
|
||||
self._brl.enterTtyMode()
|
||||
self.deviceSize = self._brl.displaySize
|
||||
self._isInitialized = True
|
||||
return True
|
||||
except Exception as e:
|
||||
self.env['runtime']['debug'].writeDebugOut('ERROR: Initializing braille failed:' + str(e),debug.debugLevel.ERROR)
|
||||
return False
|
||||
"""Initialize the braille driver with the given environment.
|
||||
|
||||
Args:
|
||||
environment (dict): The Fenrir environment dictionary containing runtime settings
|
||||
and helper objects.
|
||||
"""
|
||||
self.env = environment
|
||||
self._isInitialized = True
|
||||
|
||||
@abstractmethod
|
||||
def getDeviceSize(self):
|
||||
"""Get the size of the braille display."""
|
||||
"""Get the size of the connected braille display.
|
||||
|
||||
Returns:
|
||||
tuple: A (columns, rows) tuple indicating the display dimensions.
|
||||
Returns (0, 0) if no display is connected or not initialized.
|
||||
"""
|
||||
if not self._isInitialized:
|
||||
return (0, 0)
|
||||
return self.deviceSize if self.deviceSize else (0, 0)
|
||||
return (0, 0)
|
||||
|
||||
@abstractmethod
|
||||
def writeText(self, text):
|
||||
"""Write text to the braille display."""
|
||||
"""Write text to the braille display.
|
||||
|
||||
Args:
|
||||
text (str): The text to be written to the display.
|
||||
"""
|
||||
if not self._isInitialized:
|
||||
return
|
||||
try:
|
||||
self._brl.writeText(text)
|
||||
except Exception as e:
|
||||
self.env['runtime']['debug'].writeDebugOut('ERROR: Writing braille failed:' + str(e),debug.debugLevel.ERROR)
|
||||
|
||||
def readKeypress(self):
|
||||
"""Read a keypress from the braille display."""
|
||||
if not self._isInitialized:
|
||||
return None
|
||||
try:
|
||||
return self._brl.readKey(wait=0)
|
||||
except Exception as e:
|
||||
self.env['runtime']['debug'].writeDebugOut('ERROR: Reading key failed:' + str(e),debug.debugLevel.ERROR)
|
||||
return None
|
||||
@abstractmethod
|
||||
def connectDevice(self):
|
||||
"""Establish connection with the braille display.
|
||||
|
||||
This method should handle the initial connection setup with the display.
|
||||
It should be called before any other display operations.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def enterScreen(self, screen):
|
||||
"""Enter a new screen context."""
|
||||
if not self._isInitialized:
|
||||
return
|
||||
try:
|
||||
self._brl.enterTtyModeWithPath(screen)
|
||||
except Exception as e:
|
||||
self.env['runtime']['debug'].writeDebugOut('ERROR: Entering screen failed:' + str(e),debug.debugLevel.ERROR)
|
||||
"""Enter a specific screen/TTY mode.
|
||||
|
||||
def leaveScreen(self):
|
||||
"""Leave the current screen context."""
|
||||
Args:
|
||||
screen (int): The screen/TTY number to enter.
|
||||
"""
|
||||
if not self._isInitialized:
|
||||
return
|
||||
|
||||
@abstractmethod
|
||||
def leaveScreen(self):
|
||||
"""Leave the current screen/TTY mode.
|
||||
|
||||
This method should clean up any screen-specific state and
|
||||
prepare for potential screen switches.
|
||||
"""
|
||||
if not self._isInitialized:
|
||||
return
|
||||
|
||||
@abstractmethod
|
||||
def setCursor(self, position):
|
||||
"""Set the cursor position on the braille display.
|
||||
|
||||
Args:
|
||||
position (int): The position where the cursor should be placed.
|
||||
"""
|
||||
if not self._isInitialized:
|
||||
return
|
||||
self._current_cursor_pos = max(0, min(position, self.getDeviceSize()[0] - 1))
|
||||
|
||||
@abstractmethod
|
||||
def getCursorPosition(self):
|
||||
"""Get the current cursor position on the braille display.
|
||||
|
||||
Returns:
|
||||
int: The current cursor position.
|
||||
"""
|
||||
if not self._isInitialized:
|
||||
return 0
|
||||
return self._current_cursor_pos
|
||||
|
||||
@abstractmethod
|
||||
def clear(self):
|
||||
"""Clear the braille display."""
|
||||
if not self._isInitialized:
|
||||
return
|
||||
self._display_content = ""
|
||||
self._current_cursor_pos = 0
|
||||
|
||||
@abstractmethod
|
||||
def flush(self):
|
||||
"""Force an update of the physical display with current content."""
|
||||
if not self._isInitialized:
|
||||
return
|
||||
try:
|
||||
self._brl.leaveTtyMode()
|
||||
except Exception as e:
|
||||
self.env['runtime']['debug'].writeDebugOut('ERROR: Leaving screen failed:' + str(e),debug.debugLevel.ERROR)
|
||||
|
||||
def shutdown(self):
|
||||
"""Shutdown the braille driver."""
|
||||
"""Shut down the braille driver and clean up resources.
|
||||
|
||||
This method ensures proper cleanup of resources and
|
||||
disconnection from the braille display.
|
||||
"""
|
||||
if not self._isInitialized:
|
||||
return
|
||||
try:
|
||||
self.clear()
|
||||
self.leaveScreen()
|
||||
if self._brl:
|
||||
self._brl.closeConnection()
|
||||
self._brl = None
|
||||
self._isInitialized = False
|
||||
except Exception as e:
|
||||
self.env['runtime']['debug'].writeDebugOut('ERROR: Shutting down braille failed:' + str(e),debug.debugLevel.ERROR)
|
||||
|
||||
@property
|
||||
def is_initialized(self):
|
||||
"""Check if the driver is initialized.
|
||||
|
||||
Returns:
|
||||
bool: True if the driver is initialized, False otherwise.
|
||||
"""
|
||||
return self._isInitialized
|
||||
|
||||
@abstractmethod
|
||||
def handleKeyInput(self, key):
|
||||
"""Handle input from the braille display's keys.
|
||||
|
||||
Args:
|
||||
key: The key event from the braille display.
|
||||
Returns:
|
||||
bool: True if the key was handled, False otherwise.
|
||||
"""
|
||||
if not self._isInitialized:
|
||||
return False
|
||||
return False
|
||||
|
Loading…
Reference in New Issue
Block a user