blob: 04553b7910a89c42d187cb1112c050804744bcf5 [file] [log] [blame] [edit]
/*
* BRLTTY - A background process providing access to the console screen (when in
* text mode) for a blind person using a refreshable braille display.
*
* Copyright (C) 1995-2023 by The BRLTTY Developers.
*
* BRLTTY comes with ABSOLUTELY NO WARRANTY.
*
* This is free software, placed under the terms of the
* GNU Lesser General Public License, as published by the Free Software
* Foundation; either version 2.1 of the License, or (at your option) any
* later version. Please see the file LICENSE-LGPL for details.
*
* Web Page: http://brltty.app/
*
* This software is maintained by Dave Mielke <dave@mielke.cc>.
*/
#include "prologue.h"
#include <string.h>
#include <errno.h>
#include "log.h"
#include "brl_driver.h"
#include "brldefs-np.h"
#define PROBE_RETRY_LIMIT 3
#define PROBE_INPUT_TIMEOUT 1000
#define MAXIMUM_RESPONSE_SIZE 3
#define MAXIMUM_CELL_COUNT (NP_KEY_ROUTING_MAX - NP_KEY_ROUTING_MIN + 1)
BEGIN_KEY_NAME_TABLE(navigation)
KEY_NAME_ENTRY(NP_KEY_Brl1, "Brl1"),
KEY_NAME_ENTRY(NP_KEY_Brl2, "Brl2"),
KEY_NAME_ENTRY(NP_KEY_Brl3, "Brl3"),
KEY_NAME_ENTRY(NP_KEY_Brl4, "Brl4"),
KEY_NAME_ENTRY(NP_KEY_Brl5, "Brl5"),
KEY_NAME_ENTRY(NP_KEY_Brl6, "Brl6"),
KEY_NAME_ENTRY(NP_KEY_Brl7, "Brl7"),
KEY_NAME_ENTRY(NP_KEY_Brl8, "Brl8"),
KEY_NAME_ENTRY(NP_KEY_Enter, "Enter"),
KEY_NAME_ENTRY(NP_KEY_Space, "Space"),
KEY_NAME_ENTRY(NP_KEY_PadCenter, "PadCenter"),
KEY_NAME_ENTRY(NP_KEY_PadLeft, "PadLeft"),
KEY_NAME_ENTRY(NP_KEY_PadRight, "PadRight"),
KEY_NAME_ENTRY(NP_KEY_PadUp, "PadUp"),
KEY_NAME_ENTRY(NP_KEY_PadDown, "PadDown"),
KEY_NAME_ENTRY(NP_KEY_NavLeft, "NavLeft"),
KEY_NAME_ENTRY(NP_KEY_NavRight, "NavRight"),
KEY_GROUP_ENTRY(NP_GRP_RoutingKeys, "RoutingKey"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLES(all)
KEY_NAME_TABLE(navigation),
END_KEY_NAME_TABLES
DEFINE_KEY_TABLE(all)
BEGIN_KEY_TABLE_LIST
&KEY_TABLE_DEFINITION(all),
END_KEY_TABLE_LIST
struct BrailleDataStruct {
unsigned char forceRewrite;
unsigned char textCells[MAXIMUM_CELL_COUNT];
};
static int
writeBytes (BrailleDisplay *brl, const unsigned char *bytes, size_t count) {
return writeBraillePacket(brl, NULL, bytes, count);
}
static size_t
readPacket (BrailleDisplay *brl, void *packet, size_t size) {
unsigned char *bytes = packet;
size_t offset = 0;
size_t length = 0;
while (1) {
unsigned char byte;
{
int started = offset > 0;
if (!gioReadByte(brl->gioEndpoint, &byte, started)) {
if (started) logPartialPacket(bytes, offset);
return 0;
}
}
gotByte:
if (offset == 0) {
switch (byte) {
case 0XFC:
length = 2;
break;
case 0XFD:
length = 2;
break;
default:
logIgnoredByte(byte);
continue;
}
} else {
int unexpected = 0;
if (offset == 1) {
if (bytes[0] == 0XFD) {
switch (byte) {
case 0X2F:
length = 3;
break;
default:
unexpected = 1;
break;
}
}
}
if (unexpected) {
logShortPacket(bytes, offset);
offset = 0;
length = 0;
goto gotByte;
}
}
if (offset < size) {
bytes[offset] = byte;
if (offset == (length - 1)) {
logInputPacket(bytes, length);
return length;
}
} else {
if (offset == size) logTruncatedPacket(bytes, offset);
logDiscardedByte(byte);
}
offset += 1;
}
}
static int
connectResource (BrailleDisplay *brl, const char *identifier) {
GioDescriptor descriptor;
gioInitializeDescriptor(&descriptor);
descriptor.bluetooth.channelNumber = 1;
if (connectBrailleResource(brl, identifier, &descriptor, NULL)) {
return 1;
}
return 0;
}
static int
writeIdentifyRequest (BrailleDisplay *brl) {
return 1;
}
static BrailleResponseResult
isIdentityResponse (BrailleDisplay *brl, const void *packet, size_t size) {
const unsigned char *bytes = packet;
return ((size == 3) && (bytes[0] == 0XFD) && (bytes[1] == 0X2F))?
BRL_RSP_DONE:
BRL_RSP_UNEXPECTED;
}
static int
brl_construct (BrailleDisplay *brl, char **parameters, const char *device) {
if ((brl->data = malloc(sizeof(*brl->data)))) {
memset(brl->data, 0, sizeof(*brl->data));
if (connectResource(brl, device)) {
unsigned char response[MAXIMUM_RESPONSE_SIZE];
if (probeBrailleDisplay(brl, PROBE_RETRY_LIMIT, NULL, PROBE_INPUT_TIMEOUT,
writeIdentifyRequest,
readPacket, &response, sizeof(response),
isIdentityResponse)) {
setBrailleKeyTable(brl, &KEY_TABLE_DEFINITION(all));
MAKE_OUTPUT_TABLE(0X01, 0X04, 0X10, 0X02, 0X08, 0X20, 0X40, 0X80);
brl->textColumns = MAXIMUM_CELL_COUNT;
brl->data->forceRewrite = 1;
return 1;
}
disconnectBrailleResource(brl, NULL);
}
free(brl->data);
} else {
logMallocError();
}
return 0;
}
static void
brl_destruct (BrailleDisplay *brl) {
disconnectBrailleResource(brl, NULL);
if (brl->data) {
free(brl->data);
brl->data = NULL;
}
}
static int
brl_writeWindow (BrailleDisplay *brl, const wchar_t *text) {
if (cellsHaveChanged(brl->data->textCells, brl->buffer, brl->textColumns, NULL, NULL, &brl->data->forceRewrite)) {
unsigned char bytes[(brl->textColumns * 2) + 2];
unsigned char *byte = bytes;
{
int i;
for (i=brl->textColumns-1; i>=0; i-=1) {
*byte++ = 0XFC;
*byte++ = translateOutputCell(brl->data->textCells[i]);
}
}
*byte++ = 0XFD;
*byte++ = 0X10;
if (!writeBytes(brl, bytes, byte-bytes)) return 0;
}
return 1;
}
static int
brl_readCommand (BrailleDisplay *brl, KeyTableCommandContext context) {
unsigned char packet[MAXIMUM_RESPONSE_SIZE];
size_t size;
while ((size = readPacket(brl, packet, sizeof(packet)))) {
switch (packet[0]) {
case 0XFD:
switch (packet[1]) {
case 0X2F:
continue;
default:
break;
}
break;
case 0XFC: {
unsigned int key = packet[1];
if ((key >= NP_KEY_ROUTING_MIN) && (key <= NP_KEY_ROUTING_MAX)) {
enqueueKey(brl, NP_GRP_RoutingKeys, (key - NP_KEY_ROUTING_MIN));
continue;
} else {
int press = !!(key & NP_KEY_NAVIGATION_PRESS);
if (press) key &= ~NP_KEY_NAVIGATION_PRESS;
enqueueKeyEvent(brl, NP_GRP_NavigationKeys, key, press);
continue;
}
break;
}
}
logUnexpectedPacket(packet, size);
}
return (errno == EAGAIN)? EOF: BRL_CMD_RESTARTBRL;
}