Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New handy tech display models #9691

Merged
3 changes: 3 additions & 0 deletions source/bdDetect.py
Expand Up @@ -532,6 +532,8 @@ def driverSupportsAutoDetection(driver):
"VID_1FE4&PID_0087", # Basic Braille 80
"VID_1FE4&PID_008B", # Basic Braille 160
"VID_1FE4&PID_008C", # Basic Braille 84
"VID_1FE4&PID_0093", # Basic Braille Plus 32
"VID_1FE4&PID_0094", # Basic Braille Plus 40
"VID_1FE4&PID_0061", # Actilino
"VID_1FE4&PID_0064", # Active Star 40
})
Expand All @@ -548,6 +550,7 @@ def driverSupportsAutoDetection(driver):
"Active Braille AB",
"Active Star AS",
"Basic Braille BB",
"Basic Braille Plus BP",
"Braille Star 40 BS",
"Braillino BL",
"Braille Wave BW",
Expand Down
62 changes: 62 additions & 0 deletions source/brailleDisplayDrivers/handyTech.py
Expand Up @@ -24,7 +24,25 @@
import bdDetect
import time
import datetime
from ctypes import windll
LeonarddeR marked this conversation as resolved.
Show resolved Hide resolved
import windowUtils
from driverHandler import BooleanDriverSetting
import wx

class InvisibleDriverWindow(windowUtils.CustomWindow):
className = u"Handy_Tech_Server"
def __init__(self, driver):
super(InvisibleDriverWindow, self).__init__(u"Handy Tech Server")
self.driver = driver
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make this a weak reference. You can see how this works on the Model class.

There is also a possibility to provide a callback to the reference that is called when the reference dies, i.e. when the driver instance dies. We really need to make sure that the window is destroyed in that case. Could you also test this, e.g. by forcefully disconnecting a device and checking whether the window is destroyed correctly? I assume it will if terminate is called when the driver loses its connection.

def windowProc(self, hwnd, msg, wParam, lParam):
if msg == window_message:
if wParam == 100 and lParam == 1:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Were do 100 and 1 stand for? Could you make these constants? Note that constants are capitalised.

self.driver.go_to_sleep()
elif wParam == 100 and lParam == 0:
self.driver.wake_up()
return 0
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does 0 mean in this context?


window_message=windll.user32.RegisterWindowMessageW(u"Handy_Tech_Server")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now global to the driver module and therefore to NVDA as a whole. It would be nice if we could avoid this by registering the window message in create_message_window and unregister it in self.destroy_message_window


BAUD_RATE = 19200
PARITY = serial.PARITY_ODD
Expand Down Expand Up @@ -535,6 +553,7 @@ def getManualPorts(cls):

def __init__(self, port="auto"):
super(BrailleDisplayDriver, self).__init__()
wx.CallAfter(self.create_message_window)
LeonarddeR marked this conversation as resolved.
Show resolved Hide resolved
self.numCells = 0
self._model = None
self._ignoreKeyReleases = False
Expand All @@ -543,12 +562,14 @@ def __init__(self, port="auto"):
self._dotFirmness = 1
self._hidSerialBuffer = b""
self._atc = False
self._sleepcounter = 0

for portType, portId, port, portInfo in self._getTryPorts(port):
# At this point, a port bound to this display has been found.
# Try talking to the display.
self.isHid = portType == bdDetect.KEY_HID
self.isHidSerial = portId in USB_IDS_HID_CONVERTER
self.port = port
try:
if self.isHidSerial:
# This is either the standalone HID adapter cable for older displays,
Expand Down Expand Up @@ -581,10 +602,47 @@ def __init__(self, port="auto"):
self._dev.close()

else:
wx.CallAfter(self.destroy_message_window)
LeonarddeR marked this conversation as resolved.
Show resolved Hide resolved
raise RuntimeError("No Handy Tech display found")

def create_message_window(self):
try:
self._messageWindow = InvisibleDriverWindow(self)
except:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What exceptions do you expect here?

log.debugWarning("", exc_info=True)
def destroy_message_window(self):
LeonarddeR marked this conversation as resolved.
Show resolved Hide resolved
try:
LeonarddeR marked this conversation as resolved.
Show resolved Hide resolved
self._messageWindow.destroy()
except:
log.debugWarning("", exc_info=True)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure that if you move window message registering to the driver, that it is properly unregistered.


def go_to_sleep(self):
self._sleepcounter += 1
if self._dev is not None:
self._dev.close()
self._dev = None
time.sleep(self.timeout)
LeonarddeR marked this conversation as resolved.
Show resolved Hide resolved
def wake_up(self):
if self._sleepcounter > 0:
self._sleepcounter -= 1
if self._sleepcounter > 0: # Still not zero after decrementing
return
# Might throw if device no longer exists.
# We leave it to autodetection to grab it when it reappears.
if self.isHidSerial:
# This is either the standalone HID adapter cable for older displays,
# or an older display with a HID - serial adapter built in
self._dev = hwIo.Hid(self.port, onReceive=self._hidSerialOnReceive)
# Send a flush to open the serial channel
self._dev.write(HT_HID_RPT_InCommand + HT_HID_CMD_FlushBuffers)
elif self.isHid:
self._dev = hwIo.Hid(self.port, onReceive=self._hidOnReceive)
else:
self._dev = hwIo.Serial(self.port, baudrate=BAUD_RATE, parity=PARITY,
timeout=self.timeout, writeTimeout=self.timeout, onReceive=self._serialOnReceive)
LeonarddeR marked this conversation as resolved.
Show resolved Hide resolved
def terminate(self):
try:
wx.CallAfter(self.destroy_message_window)
super(BrailleDisplayDriver, self).terminate()
finally:
# We must sleep before closing the connection as not doing this can leave the display in a bad state where it can not be re-initialized.
Expand Down Expand Up @@ -634,6 +692,8 @@ def _set_dotFirmness(self, value):
self._dotFirmness = value

def sendPacket(self, packetType, data=""):
if self._dev is None:
return
LeonarddeR marked this conversation as resolved.
Show resolved Hide resolved
if type(data) == bool or type(data) == int:
data = chr(data)
if self.isHid:
Expand All @@ -642,6 +702,8 @@ def sendPacket(self, packetType, data=""):
self._dev.write(packetType + data)

def sendExtendedPacket(self, packetType, data=""):
if self._dev is None:
return
if type(data) == bool or type(data) == int:
data = chr(data)
packet = b"{length}{extType}{data}\x16".format(
Expand Down