Support Braille displays that conform to the HID Braille standard #12523
Conversation
…eeded to implement the Braille HID standard.
…erties to the Hid class to easily fetch caps and input and output value caps.
…HID standard to talk to braille displays that support it.
…SAGE_PAGE_BRAILLE then try using the Braille HID driver before anything else.
…g what devices a particular driver supports.
…t button data indices that are on.
….hid module. Bulk and Serial could be moved into their own modules at a later date.
This document includes many notes on how to develope with HID on Windows, and more specifically the HID braille specification.
…definitions section.
…ing removing the final statement as it is not really relevant.
…ls to the top of the definitions section.
… should continuously be called until it returns false, meaning there are no more devices left.
…alking about CreateFile.
… number of collections. usages and reports for braille displays at runtime.
…into the background section directly before it.
…nline code blocks.
|
This is an amazing new feature. |
LeonarddeR
left a comment
There was a problem hiding this comment.
I wonder whether HID should have precedence before all other drivers. Imagine if a manufacturer introduces a new firmware that enabled HID support and NVDA instantly uses the new HID driver instead of the manufacturer specific one. This would mean that key assignments instantly change.
|
It's a good point @LeonarddeR. I think for any such device we would want a way to set the driver automatically used. That said, I think I'd prefer to handle that problem at the time, if it ever came up. |
seanbudd
left a comment
There was a problem hiding this comment.
The documentation reads well to me, and the driver code is well documented and easy to follow as a result.
Convert older style percent strings to f-strings. Co-authored-by: Sean Budd <sean@nvaccess.org>
Unneeded unicode prefix. Co-authored-by: Sean Budd <sean@nvaccess.org>
Remove incorrect comment. Co-authored-by: Sean Budd <sean@nvaccess.org>
Remove unneeded space. Co-authored-by: Sean Budd <sean@nvaccess.org>
|
We are holding this from being merged to master until we have branched for 2021.2 beta1. We would like it on master for a significant amount of time before a beta. Plus I still have no real-world in-market device to test this with yet. |
See test results for failed build of commit 82a7be7782 |
Qchristensen
left a comment
There was a problem hiding this comment.
Just looking at the UserGuide changes, all fine, good work.
|
After the changelog was updated for this PR, only one blank line now appears between new features and changes, causing the "changes" section to not appear as a header. |
|
Thanks for catching. Fixed in commit 8cacd41
|
Fixes regression introduced by pr #12523.
|
Lovely! I haven't looked at the driver code yet, but I'm currently testing against a firmware draft that speaks Braille HID (at least as far as I understand it). Good news: The only issue I'm facing right now is that cursor routing keys are not recognized, and that joystick left and right are swapped, which may very well be my fault. For the curious, here is the report descriptor I am using. I'd appreciated it if someone could have a look at those cursor routing keys, and maybe suggest a correction. Descriptor codeIf anything specifically needs testing, I'd love to do so. |
|
I fixed my report descriptor by specifying the cursor routing keys as a bitmap rather than via an array index. The new descriptor is as follows: Descriptor CodeNow the keys are recognized, but in reverse order.
When I reverse the caps list before collecting, the order of for buttonCaps in reversed(self._dev.inputButtonCaps):
# ...Note that sorting by data index does not fix this. The indices are already in ascending order, and when processing the raw input report, the HID parser actually reverses the data so they once again match. I initially didn't believe it myself but that's what it's doing. |
|
@FelixGruetzmacher Thank you for this info. I had already seen that the
routing keys were reversed on two other displays and was very confused.
thanks for pointing me to the line in the Microsoft docs that clears
this up :)
I'll go ahead and make this change.
|
Hi @FelixGruetzmacher I have test the following report descriptor but the router key never worked: 0xa1, 0x02, // COLLECTION (Logical) 0x0a, 0x00, 0x01, // USAGE (Router Key) 0x15, 0x01, // LOGICAL_MINIMUM (1) 0x25, 0x10, // LOGICAL_MAXIMUM (16) 0x75, 0x08, // REPORT_SIZE (8) 0x95, 0x01, // REPORT_COUNT (1) 0x81, 0x00, // INPUT (Data,Ary,Abs) 0xc0, // END_COLLECTION |
|
Hi I just found this Request because I have the same problem. In the HUTRR78 the sample says: What obviously doesn't work. But I have to agree to @moyanming , a braille display with 40 cells are an extra 120 bytes. What I don't understand, regarding the HID Usage Tables Version 1.2 "Router Key" and the "Braille Keyboard Dot 1" are both of usage type "Sel". And the part for "Braille Keyboard Dot 1" to "Braille Keyboard Dot 8" works from the sample. Thx Stefan |
|
No solution of which I am aware. Every working implementation I know of
repeats the CR button usage n times for n cells.
While this means 3*n additional bytes, which is, admittedly, quite a lot,
keep in mind that this is a one-time transfer upon enumeration and will
therefore not impact performance. Do you think it is safe to accept this as
being just the way this standard has evolved? It's not beautiful but then
what is?
Am Do., 24. Okt. 2024 um 21:32 Uhr schrieb Stefan-Kliesch-FHP <
***@***.***>:
… Hi I just found this Request because I have the same problem.
Is there any solution yet?
In the HUTRR78 the sample says:
0x09, 0xFA, // USAGE (Router Set 1) 0xA1, 0x02, // Collection (Logical)
0x0A, 0x00, 0x01, // Usage (Router Key) 0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1) 0x75, 0x01, // Report Size (1) 0x95,
0x14, // Report Count (20) 0x81, 0x02, // Input (Data,Var,Abs,No
Wrap,Linear,Preferred
What obviously doesn't work.
But I have to agree to @moyanming <https://github.com/moyanming> , a
braille display with 40 cells are an extra 120 bytes.
A braille display with 80 cells and 2 rows of routing keys would be 480
bytes extra?!
It would be great if anybody can provide a solution.
Thx Stefan
—
Reply to this email directly, view it on GitHub
<#12523 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AEKTKKFHYHEZ2X6T6I5B2FDZ5FDN7AVCNFSM6AAAAABQRZITP6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDIMZWGE4DSNRZGI>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
|
Thank you @FelixGruetzmacher Best regards, |
Link to issue number:
None
Summary of the issue:
Braille Display devices allow being controlled by Screen Readers using a variety of connections such as Serial, USB and Bluetooth. The protocols used over these channels have traditionally been Braille Display manufacturer specific.
This has meant that in order for a Screen Reader to support a particular Braille display, it must have specific logic in the Screen Reader that knows how to speak the required device-specific protocol. Further to this, On Windows at least, an OS-level driver provided by the Braille Display manufacturer must also be installed by the user in order for the computer to detect and communicate with the device.
With the introduction of the HID (Human Interface Device) standard for USB (and later Bluetooth), it became possible to remove the need for the required OS-level device driver on Windows if the Braille Display manufacturer exposed the device as a custom HID device, however the Screen Reader still needed device-specific code, as being custom HID only simplified the low-level bytes communication, but did not standardize what those bytes actually meant.
In 2018, The HID specification was extended to define the concept of a Braille Display device, including setting of braille cells, and the standardizing of common buttons found on Braille displays such as routing keys, Braille dot input keys, braille space and panning keys.
NVDA should add specific support for a Braille HID device, so that Braille Displays that support this standard can automatically be detected and usable by NVDA.
Description of how this pull request fixes the issue:
hidpi.pywhich contains all necessary types and defines from Windows'hidpi.h(the HID parser header).HidPtypes that were previously added to hwIo.py have been moved tohidpi.py.hwIo.Hidclass has been extended to include some useful properties such as caps (HidP_GetCaps),inputButtonCaps(HidP_GetButtonCaps),inputValueCaps, andoutputValueCaps(HidP_GetValueCaps).brailleDisplayDriver(brailleDisplayDrivers/hid.py) has been added which supports Braille displays that use the Braille HID specification.hwIo.Hidobject.HIDP_CAPS.UsagePageof the HID device's top-level collection is set toHID_USAGE_PAGE_BRAILLE(0x41).HIDP_VALUE_CAPSstructure which represents the array of braille cells. I.e. theUsage IDis eitherEIGHT_DOT_BRAILLE_CELLorsix_dot_braille_cell. TheReportCountmember of this struct states the number of cells for the device. This structure is also saved off as it is later needed when writing braille to the display.HIDP_VALUE_CAPSstructures supported by the device, and stores them in a dictionary, keyed by theirDataIndexmember.HIDP_VALUE_CAPSstructure represents a range of data indices, then theHIDP_VALUE_CAPSstructure is added to the dictionary for each data index in that range. For example a device may expose the Braille dot input keys this way.HIDP_VALUE_CAPSstructure, and a calculated relativeDataIndexfrom the firstHIDP_VALUE_CAPSstructure with the sameLinkCollectionmember. Examples of collections areRouter_set_1orBraille_row. We certainly need this relative index for routing keys.BrailleDisplayDriver.displaymethod (for writing cells to the device) works as follows:ReportIDmember of the cellHIDP_VALUE_CAPSstructure found at construction.HidP_SetUsageValueArrayto place the data (braille cell dot patterns) into the report at the appropriate place, Using theUsage IDand collection number etc from the cellHIDP_VALUE_CAPSstructure.WriteFile. The number of bytes written will be the value ofHIDP_CAPS.OutputReportByteLength.BrailleDisplayDriverreceives input from the Braille display by providing a callback to thehwIo.Hidobject. TheHidobject uses overlapped IO andReadFileto continuously read blocks of data of the device's HID input report size (HIDP_CAPS.InputReportByteLength).BrailleDisplayDriver's_hidOnReceivecallback does the following:HidP_GetDatais used to extract allHIDP_DATAstructures from the report. these represent the state of all input buttons and other controls.DataIndexmember is recorded in a keys list (keys currently being pressed). As long as this list keeps growing, all these keys are treated as being a part of the upcoming gesture. If the list decreases, then these keys are used to create an NVDAInputGesturewhich is executed.BrailleDisplayDriver'sInputGestureclass processes the given data indices as follows:HIDP_VALUE_CAPSstructure and relative index in collection value for the data index, using the dictionary created in theBrailleDisplayDriver's construction.HIDP_VALUE_CAPSstructure represents a range of buttons, it calculates the correctUsage IDusing theDataIndexvalue and theDataIndexMinandUsageMinHIDP_VALUE_CAPSmembers.Usage IDis one of theBRAILLE_KEYBOARD_DOT_*values, it sets the appropriate bit in theInputGesture'sdotsmember.Usage IDis one of theBRAILLE_KEYBOARD_SPACE,BRAILLE_KEYBOARD_LEFT_SPACEorBRAILLE_KEYBOARD_RIGHT_SPACE, it sets theInputGesture'sspacemember to true.Usage IDisROUTER_KEY, then theInputGesture'sroutingIndexmember is set using the calculatedrelativeIndexInCollectionvalue for this data index.Usage ID'senumname, converted to camelCase, with prefixes such asBRAILLE_KEYBOARD_andBraille_removed. E.g.BRAILLE_PAN_LEFTbecomespanLeft. And for router collections, the router collection name is prefixed E.g.routerSet1_routerKey.gestureMaphas been provided for thisBrailleDisplayDriver, which is roughly based on the Brailliant B driver, for all gestures that are supported by Braille HID.hwPortUtils._getHidInfonow includes the device's top-level collection'sUsagePagevalue in the info it returns. Note that this does mean now that all HID devices are opened to get this, where as before we only opened Bluetooth devices.bdDetectlooks up an appropriateBrailleDisplayDriverfor a USB or Bluetooth device, or when it looks up compatible USB or Bluetooth devices for a givenBrailleDisplayDriver, if the USB or Bluetooth device is a HID device and its to-level collection'sUsagePagevalue isHID_USAGE_PAGE_BRAILLE(0x41), then the hidBrailleDisplayDriveris chosen before any other match. Thus if a HID device reports it is a HID braille display, the hidBrailleDisplayDriverwill automatically be used.Testing strategy:
Known issues with pull request:
None known
Change log entries:
New features
Changes
Bug fixes
For Developers
Code Review Checklist: