Skip to content

Commit

Permalink
Support multi-line braille displays in HidBrailleDriver (#17001)
Browse files Browse the repository at this point in the history
Closes #16993

Summary of the issue:
Multi-line braille displays using the HID standard driver only support the first line of cells.

Description of user facing changes
Users with multi-line braille displays that connect using the HidBrailleDriver will now be able to use all cells of their display.

Description of development approach
HidBrailleDriver._cellValueCaps is now a list of all braille cell controls in the device. numRows is set to the length of that list and numCols is set to the ReportCount of each element of the list. In display, the cells are split across the elements of _cellValueCaps.
  • Loading branch information
alexmoon authored Aug 15, 2024
1 parent fa442e5 commit 83b2e0a
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 20 deletions.
62 changes: 42 additions & 20 deletions source/brailleDisplayDrivers/hidBrailleStandard.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
# Copyright (C) 2021 NV Access Limited

from dataclasses import dataclass
from typing import List, Optional
from typing import List
import enum
import itertools
import braille
import inputCore
from logHandler import log
Expand Down Expand Up @@ -90,7 +91,8 @@ def registerAutomaticDetection(cls, driverRegistrar: DriverRegistrar):

def __init__(self, port="auto"):
super().__init__()
self.numCells = 0
self.numRows = 1
self.numCols = 0

for portType, portId, port, portInfo in self._getTryPorts(port):
if portType != bdDetect.DeviceType.HID:
Expand All @@ -103,16 +105,27 @@ def __init__(self, port="auto"):
continue # Couldn't connect.
if self._dev.usagePage != HID_USAGE_PAGE_BRAILLE:
log.debug("Not braille")
self._dev.close()
continue
cellValueCaps = self._findCellValueCaps()
if cellValueCaps:
if len(cellValueCaps) > 0:
if any(x.ReportCount != cellValueCaps[0].ReportCount for x in cellValueCaps):
log.warning("Found multi-line display with an irregular shape, ignoring.")
self._dev.close()
continue
self.numRows = len(cellValueCaps)
self.numCols = cellValueCaps[0].ReportCount
self._maxNumberOfCells = self.numCells
self._cellValueCaps = cellValueCaps
self._numberOfCellsValueCaps = self._findNumberOfCellsValueCaps()
self.numCells = self._maxNumberOfCells = cellValueCaps.ReportCount
if self.numRows == 1:
self._numberOfCellsValueCaps = self._findNumberOfCellsValueCaps()
elif self._findNumberOfCellsValueCaps():
log.warning("Reserved braille cells are not supported on multi-line displays")
# A display responded.
log.info(
"Found display with {cells} cells connected via {type} ({port})".format(
cells=self.numCells,
"Found display with {rows} rows, {cols} cols connected via {type} ({port})".format(
rows=self.numRows,
cols=self.numCols,
type=portType,
port=port,
),
Expand All @@ -126,8 +139,10 @@ def __init__(self, port="auto"):
self._keysDown = set()
self._ignoreKeyReleases = False

def _findCellValueCaps(self) -> Optional[hidpi.HIDP_VALUE_CAPS]:
for valueCaps in self._dev.outputValueCaps:
def _findCellValueCaps(self) -> list[hidpi.HIDP_VALUE_CAPS]:
return [
valueCaps
for valueCaps in self._dev.outputValueCaps
if (
valueCaps.LinkUsagePage == HID_USAGE_PAGE_BRAILLE
and valueCaps.LinkUsage == BraillePageUsageID.BRAILLE_ROW
Expand All @@ -137,9 +152,8 @@ def _findCellValueCaps(self) -> Optional[hidpi.HIDP_VALUE_CAPS]:
BraillePageUsageID.SIX_DOT_BRAILLE_CELL,
)
and valueCaps.ReportCount > 0
):
return valueCaps
return None
)
]

def _findNumberOfCellsValueCaps(self) -> hidpi.HIDP_VALUE_CAPS | None:
for valueCaps in self._dev.inputValueCaps:
Expand Down Expand Up @@ -226,14 +240,22 @@ def display(self, cells: List[int]):
# cells will already be padded up to numCells.
padded_cells = cells + [0] * (self._maxNumberOfCells - len(cells))
cellBytes = b"".join(intToByte(cell) for cell in padded_cells)
report = hwIo.hid.HidOutputReport(self._dev, reportID=self._cellValueCaps.ReportID)
report.setUsageValueArray(
HID_USAGE_PAGE_BRAILLE,
self._cellValueCaps.LinkCollection,
self._cellValueCaps.u1.NotRange.Usage,
cellBytes,
)
self._dev.write(report.data)
# Iterate through the output reports
for reportID, valueCaps in itertools.groupby(self._cellValueCaps, lambda x: x.ReportID):
report = hwIo.hid.HidOutputReport(self._dev, reportID=reportID)
# Iterate through each row in this report
for valueCap in valueCaps:
# Take the cells for this row from the front of the cellBytes list
rowCellBytes = cellBytes[: valueCap.ReportCount]
cellBytes = cellBytes[valueCap.ReportCount :]

report.setUsageValueArray(
HID_USAGE_PAGE_BRAILLE,
valueCap.LinkCollection,
valueCap.u1.NotRange.Usage,
rowCellBytes,
)
self._dev.write(report.data)

gestureMap = inputCore.GlobalGestureMap(
{
Expand Down
1 change: 1 addition & 0 deletions user_docs/en/changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ The available options are:
* The `-c`/`--config-path` and `--disable-addons` command line options are now respected when launching an update from within NVDA. (#16937)
* eSpeak NG has been updated to 1.52-dev commit `961454ff`. (#16775)
* Added new languages Faroese and Xextan.
* When using a multi-line braille display via the standard HID braille driver, all lines of cells will be used. (#16993, @alexmoon)

### Bug Fixes

Expand Down

0 comments on commit 83b2e0a

Please sign in to comment.