brltty.py 3.11 KB
#brailleDisplayDrivers/brltty.py
#A part of NonVisual Desktop Access (NVDA)
#This file is covered by the GNU General Public License.
#See the file COPYING for more details.
#Copyright (C) 2008-2010 James Teh <jamie@jantrid.net>

import time
import wx
import braille
from logHandler import log
import inputCore
try:
	import brlapi
	BRLAPI_CMD_KEYS = dict((code, name[8:].lower())
		for name, code in brlapi.__dict__.iteritems() if name.startswith("KEY_CMD_"))
except ImportError:
	pass

KEY_CHECK_INTERVAL = 50

class BrailleDisplayDriver(braille.BrailleDisplayDriver):
	"""brltty braille display driver.
	"""
	name = "brltty"
	description = "brltty"

	@classmethod
	def check(cls):
		try:
			brlapi
			return True
		except NameError:
			pass
		return False

	def __init__(self):
		super(BrailleDisplayDriver, self).__init__()
		self._con = brlapi.Connection()
		self._con.enterTtyModeWithPath()
		self._keyCheckTimer = wx.PyTimer(self._handleKeyPresses)
		self._keyCheckTimer.Start(KEY_CHECK_INTERVAL)
		# BRLTTY simulates key presses for braille typing keys, so let BRLTTY handle them.
		# NVDA may eventually implement this itself, but there's no reason to deny BRLTTY users this functionality in the meantime.
		self._con.ignoreKeys(brlapi.rangeType_type, (long(brlapi.KEY_TYPE_SYM),))

	def terminate(self):
		super(BrailleDisplayDriver, self).terminate()
		# Exceptions might be raised if initialisation failed. Just ignore them.
		try:
			self._keyCheckTimer.Stop()
			self._keyCheckTimer = None
		except:
			pass
		try:
			# Give BRLTTY a chance to write the last piece of data to the display.
			time.sleep(0.05)
			self._con.leaveTtyMode()
		except:
			pass

	def _get_numCells(self):
		return self._con.displaySize[0]

	def display(self, cells):
		cells = "".join(chr(cell) for cell in cells)
		# HACK: Temporarily work around a bug which causes brltty to freeze if data is written while there are key presses waiting.
		# Simply consume and act upon any waiting key presses.
		self._handleKeyPresses()
		self._con.writeDots(cells)

	def _handleKeyPresses(self):
		while True:
			try:
				key = self._con.readKey(False)
			except:
				log.error("Error reading key press from brlapi", exc_info=True)
				return
			if not key:
				break
			key = self._con.expandKeyCode(key)
			self._onKeyPress(key)

	def _onKeyPress(self, key):
		keyType = key["type"]
		command = key["command"]
		argument = key["argument"]
		if keyType == brlapi.KEY_TYPE_CMD:
			try:
				inputCore.manager.executeGesture(InputGesture(command, argument))
			except inputCore.NoInputGestureAction:
				pass

	gestureMap = inputCore.GlobalGestureMap({
		"globalCommands.GlobalCommands": {
			"braille_scrollBack": ("br(brltty):fwinlt",),
			"braille_scrollForward": ("br(brltty):fwinrt",),
			"braille_previousLine": ("br(brltty):lnup",),
			"braille_nextLine": ("br(brltty):lndn",),
			"braille_routeTo": ("br(brltty):route",),
		}
	})

class InputGesture(braille.BrailleDisplayGesture):

	source = BrailleDisplayDriver.name

	def __init__(self, command, argument):
		super(InputGesture, self).__init__()
		self.id = BRLAPI_CMD_KEYS[command]
		if command == brlapi.KEY_CMD_ROUTE:
			self.routingIndex = argument