blob: 6e0b8dd45c21b1961acfd4d0f66cab93808cd7a4 [file] [log] [blame]
/*
* 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>.
*/
/** EuroBraille/eu_esysiris.c
** Implements the ESYS and IRIS rev >=1.71 protocol
** Made by Yannick PLASSIARD <yan@mistigri.org>
*/
#include "prologue.h"
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "log.h"
#include "ascii.h"
#include "brldefs-eu.h"
#include "eu_protocol.h"
#include "eu_protocoldef.h"
#define MAXIMUM_DISPLAY_SIZE 80
#define COMMAND_KEY_ENTRY(k,n) KEY_ENTRY(CommandKeys, CMD, k, n)
#define BRAILLE_KEY_ENTRY(k,n) KEY_ENTRY(BrailleKeys, BRL, k, n)
BEGIN_KEY_NAME_TABLE(linear)
COMMAND_KEY_ENTRY(L1, "L1"),
COMMAND_KEY_ENTRY(L2, "L2"),
COMMAND_KEY_ENTRY(L3, "L3"),
COMMAND_KEY_ENTRY(L4, "L4"),
COMMAND_KEY_ENTRY(L5, "L5"),
COMMAND_KEY_ENTRY(L6, "L6"),
COMMAND_KEY_ENTRY(L7, "L7"),
COMMAND_KEY_ENTRY(L8, "L8"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLE(arrow)
COMMAND_KEY_ENTRY(Left, "Left"),
COMMAND_KEY_ENTRY(Right, "Right"),
COMMAND_KEY_ENTRY(Up, "Up"),
COMMAND_KEY_ENTRY(Down, "Down"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLE(switch1)
COMMAND_KEY_ENTRY(Switch1Left, "Switch1Left"),
COMMAND_KEY_ENTRY(Switch1Right, "Switch1Right"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLE(switch2)
COMMAND_KEY_ENTRY(Switch2Left, "Switch2Left"),
COMMAND_KEY_ENTRY(Switch2Right, "Switch2Right"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLE(switch3)
COMMAND_KEY_ENTRY(Switch3Left, "Switch3Left"),
COMMAND_KEY_ENTRY(Switch3Right, "Switch3Right"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLE(switch4)
COMMAND_KEY_ENTRY(Switch4Left, "Switch4Left"),
COMMAND_KEY_ENTRY(Switch4Right, "Switch4Right"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLE(switch5)
COMMAND_KEY_ENTRY(Switch5Left, "Switch5Left"),
COMMAND_KEY_ENTRY(Switch5Right, "Switch5Right"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLE(switch6)
COMMAND_KEY_ENTRY(Switch6Left, "Switch6Left"),
COMMAND_KEY_ENTRY(Switch6Right, "Switch6Right"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLE(joystick1)
COMMAND_KEY_ENTRY(LeftJoystickLeft, "LeftJoystickLeft"),
COMMAND_KEY_ENTRY(LeftJoystickRight, "LeftJoystickRight"),
COMMAND_KEY_ENTRY(LeftJoystickUp, "LeftJoystickUp"),
COMMAND_KEY_ENTRY(LeftJoystickDown, "LeftJoystickDown"),
COMMAND_KEY_ENTRY(LeftJoystickPress, "LeftJoystickPress"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLE(joystick2)
COMMAND_KEY_ENTRY(RightJoystickLeft, "RightJoystickLeft"),
COMMAND_KEY_ENTRY(RightJoystickRight, "RightJoystickRight"),
COMMAND_KEY_ENTRY(RightJoystickUp, "RightJoystickUp"),
COMMAND_KEY_ENTRY(RightJoystickDown, "RightJoystickDown"),
COMMAND_KEY_ENTRY(RightJoystickPress, "RightJoystickPress"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLE(keyboard)
BRAILLE_KEY_ENTRY(Dot1, "Dot1"),
BRAILLE_KEY_ENTRY(Dot2, "Dot2"),
BRAILLE_KEY_ENTRY(Dot3, "Dot3"),
BRAILLE_KEY_ENTRY(Dot4, "Dot4"),
BRAILLE_KEY_ENTRY(Dot5, "Dot5"),
BRAILLE_KEY_ENTRY(Dot6, "Dot6"),
BRAILLE_KEY_ENTRY(Dot7, "Dot7"),
BRAILLE_KEY_ENTRY(Dot8, "Dot8"),
BRAILLE_KEY_ENTRY(Backspace, "Backspace"),
BRAILLE_KEY_ENTRY(Space, "Space"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLE(routing)
KEY_GROUP_ENTRY(EU_GRP_RoutingKeys1, "RoutingKey1"),
KEY_GROUP_ENTRY(EU_GRP_RoutingKeys2, "RoutingKey2"),
END_KEY_NAME_TABLE
BEGIN_KEY_NAME_TABLES(iris)
KEY_NAME_TABLE(linear),
KEY_NAME_TABLE(arrow),
KEY_NAME_TABLE(keyboard),
KEY_NAME_TABLE(routing),
END_KEY_NAME_TABLES
BEGIN_KEY_NAME_TABLES(esys_small)
KEY_NAME_TABLE(switch1),
KEY_NAME_TABLE(switch2),
KEY_NAME_TABLE(joystick1),
KEY_NAME_TABLE(joystick2),
KEY_NAME_TABLE(keyboard),
KEY_NAME_TABLE(routing),
END_KEY_NAME_TABLES
BEGIN_KEY_NAME_TABLES(esys_medium)
KEY_NAME_TABLE(switch1),
KEY_NAME_TABLE(switch2),
KEY_NAME_TABLE(switch3),
KEY_NAME_TABLE(switch4),
KEY_NAME_TABLE(joystick1),
KEY_NAME_TABLE(joystick2),
KEY_NAME_TABLE(keyboard),
KEY_NAME_TABLE(routing),
END_KEY_NAME_TABLES
BEGIN_KEY_NAME_TABLES(esys_large)
KEY_NAME_TABLE(switch1),
KEY_NAME_TABLE(switch2),
KEY_NAME_TABLE(switch3),
KEY_NAME_TABLE(switch4),
KEY_NAME_TABLE(switch5),
KEY_NAME_TABLE(switch6),
KEY_NAME_TABLE(joystick1),
KEY_NAME_TABLE(joystick2),
KEY_NAME_TABLE(keyboard),
KEY_NAME_TABLE(routing),
END_KEY_NAME_TABLES
BEGIN_KEY_NAME_TABLES(esytime)
KEY_NAME_TABLE(joystick1),
KEY_NAME_TABLE(joystick2),
KEY_NAME_TABLE(linear),
KEY_NAME_TABLE(keyboard), // For braille keyboard when not in usb-hid mode.
KEY_NAME_TABLE(routing),
END_KEY_NAME_TABLES
PUBLIC_KEY_TABLE(iris)
PUBLIC_KEY_TABLE(esys_small)
PUBLIC_KEY_TABLE(esys_medium)
PUBLIC_KEY_TABLE(esys_large)
PUBLIC_KEY_TABLE(esytime)
typedef struct {
const char *modelName;
const KeyTableDefinition *keyTable;
unsigned char modelIdentifier;
unsigned char cellCount;
unsigned hasBrailleKeyboard:1;
unsigned hasAzertyKeyboard:1;
unsigned hasVisualDisplay:1;
unsigned hasOpticalBar:1;
unsigned isIris:1;
unsigned isEsys:1;
unsigned isEsytime:1;
} ModelEntry;
static const ModelEntry modelTable[] = {
{ .modelIdentifier = EU_IRIS_20,
.modelName = "Iris 20",
.cellCount = 20,
.hasBrailleKeyboard = 1,
.hasVisualDisplay = 1,
.isIris = 1,
.keyTable = &KEY_TABLE_DEFINITION(iris)
},
{ .modelIdentifier = EU_IRIS_40,
.modelName = "Iris 40",
.cellCount = 40,
.hasBrailleKeyboard = 1,
.hasVisualDisplay = 1,
.isIris = 1,
.keyTable = &KEY_TABLE_DEFINITION(iris)
},
{ .modelIdentifier = EU_IRIS_S20,
.modelName = "Iris S-20",
.cellCount = 20,
.hasBrailleKeyboard = 1,
.isIris = 1,
.keyTable = &KEY_TABLE_DEFINITION(iris)
},
{ .modelIdentifier = EU_IRIS_S32,
.modelName = "Iris S-32",
.cellCount = 32,
.hasBrailleKeyboard = 1,
.isIris = 1,
.keyTable = &KEY_TABLE_DEFINITION(iris)
},
{ .modelIdentifier = EU_IRIS_KB20,
.modelName = "Iris KB-20",
.cellCount = 20,
.hasAzertyKeyboard = 1,
.isIris = 1,
.keyTable = &KEY_TABLE_DEFINITION(iris)
},
{ .modelIdentifier = EU_IRIS_KB40,
.modelName = "Iris KB-40",
.cellCount = 40,
.hasAzertyKeyboard = 1,
.isIris = 1,
.keyTable = &KEY_TABLE_DEFINITION(iris)
},
{ .modelIdentifier = EU_ESYS_12,
.modelName = "Esys 12",
.cellCount = 12,
.hasBrailleKeyboard = 1,
.isEsys = 1,
.keyTable = &KEY_TABLE_DEFINITION(esys_small)
},
{ .modelIdentifier = EU_ESYS_40,
.modelName = "Esys 40",
.cellCount = 40,
.hasBrailleKeyboard = 1,
.isEsys = 1,
.keyTable = &KEY_TABLE_DEFINITION(esys_medium)
},
{ .modelIdentifier = EU_ESYS_LIGHT_40,
.modelName = "Esys Light 40",
.cellCount = 40,
.isEsys = 1,
.keyTable = &KEY_TABLE_DEFINITION(esys_medium)
},
{ .modelIdentifier = EU_ESYS_24,
.modelName = "Esys 24",
.cellCount = 24,
.hasBrailleKeyboard = 1,
.isEsys = 1,
.keyTable = &KEY_TABLE_DEFINITION(esys_small)
},
{ .modelIdentifier = EU_ESYS_64,
.modelName = "Esys 64",
.cellCount = 64,
.hasBrailleKeyboard = 1,
.isEsys = 1,
.keyTable = &KEY_TABLE_DEFINITION(esys_medium)
},
{ .modelIdentifier = EU_ESYS_80,
.modelName = "Esys 80",
.cellCount = 80,
.hasBrailleKeyboard = 1,
.isEsys = 1,
.keyTable = &KEY_TABLE_DEFINITION(esys_large)
},
{ .modelIdentifier = EU_ESYS_LIGHT_80,
.modelName = "Esys Light 80",
.cellCount = 80,
.isEsys = 1,
.keyTable = &KEY_TABLE_DEFINITION(esys_large)
},
{ .modelIdentifier = EU_ESYTIME_32,
.modelName = "Esytime 32",
.cellCount = 32,
.hasBrailleKeyboard = 1,
.hasOpticalBar = 1,
.isEsytime = 1,
.keyTable = &KEY_TABLE_DEFINITION(esytime)
},
{ .modelIdentifier = EU_ESYTIME_32_STANDARD,
.modelName = "Esytime 32 Standard",
.cellCount = 32,
.hasBrailleKeyboard = 1,
.isEsytime = 1,
.keyTable = &KEY_TABLE_DEFINITION(esytime)
},
{ .modelIdentifier = EU_ESYTIME_EVO,
.modelName = "Esytime Evolution",
.cellCount = 32,
.hasBrailleKeyboard = 1,
.hasOpticalBar = 1,
.isEsytime = 1,
.keyTable = &KEY_TABLE_DEFINITION(esytime)
},
{ .modelIdentifier = EU_ESYTIME_EVO_STANDARD,
.modelName = "Esytime Evolution Standard",
.cellCount = 32,
.hasBrailleKeyboard = 1,
.isEsytime = 1,
.keyTable = &KEY_TABLE_DEFINITION(esytime)
},
{ .modelName = NULL }
};
static int haveSystemInformation;
static const ModelEntry *model;
static uint32_t firmwareVersion;
static uint32_t protocolVersion;
static uint32_t deviceOptions;
static uint16_t maximumFrameLength;
static unsigned char forceWindowRewrite;
static unsigned char forceVisualRewrite;
static unsigned char forceCursorRewrite;
static unsigned char sequenceCheck;
static unsigned char sequenceKnown;
static unsigned char sequenceNumber;
static KeyNumberSet commandKeys;
static inline void
forceRewrite (void) {
forceWindowRewrite = 1;
forceVisualRewrite = 1;
forceCursorRewrite = 1;
}
static ssize_t
readPacket (BrailleDisplay *brl, void *packet, size_t size) {
unsigned char *buffer = packet;
const unsigned char pad = 0X55;
unsigned int offset = 0;
unsigned int length = 3;
while (1)
{
int started = offset > 0;
unsigned char byte;
if (!io->readByte(brl, &byte, started))
{
if (started) logPartialPacket(buffer, offset);
return (errno == EAGAIN)? 0: -1;
}
switch (offset)
{
case 0: {
unsigned char sequence = sequenceCheck;
sequenceCheck = 0;
if (sequence && sequenceKnown) {
if (byte == ++sequenceNumber) continue;
logInputProblem("Unexpected Sequence Number", &byte, 1);
sequenceKnown = 0;
}
if (byte == pad) continue;
if (byte == ASCII_STX) break;
if (sequence && !sequenceKnown) {
sequenceNumber = byte;
sequenceKnown = 1;
} else {
logIgnoredByte(byte);
}
continue;
}
case 1:
if ((byte == pad) && !sequenceKnown) {
sequenceNumber = buffer[0];
sequenceKnown = 1;
offset = 0;
continue;
}
break;
case 2:
length = ((buffer[1] << 8) | byte) + 2;
break;
default:
break;
}
if (offset < size)
{
buffer[offset] = byte;
}
else
{
if (offset == length) logTruncatedPacket(buffer, offset);
logDiscardedByte(byte);
}
if (++offset == length)
{
if (byte != ASCII_ETX)
{
logCorruptPacket(buffer, offset);
offset = 0;
length = 3;
continue;
}
sequenceCheck = 1;
logInputPacket(buffer, offset);
return offset;
}
}
}
static ssize_t
writePacket (BrailleDisplay *brl, const void *packet, size_t size) {
int packetSize = size + 2;
unsigned char buf[packetSize + 2];
if (!io || !packet || !size)
return (-1);
buf[0] = ASCII_STX;
buf[1] = (packetSize >> 8) & 0x00FF;
buf[2] = packetSize & 0x00FF;
memcpy(buf + 3, packet, size);
buf[sizeof(buf)-1] = ASCII_ETX;
logOutputPacket(buf, sizeof(buf));
return io->writeData(brl, buf, sizeof(buf));
}
static const ModelEntry *
getModelEntry (unsigned char identifier) {
const ModelEntry *mdl = modelTable;
while (mdl->modelName) {
if (mdl->modelIdentifier == identifier) return mdl;
mdl += 1;
}
return NULL;
}
static int
handleSystemInformation (BrailleDisplay *brl, unsigned char *packet) {
int logLevel = LOG_INFO;
const char *infoDescription;
enum {Unknown, End, String, Dec8, Dec16, Hex32} infoType;
switch(packet[0]) {
case LP_SYSTEM_SHORTNAME:
infoType = String;
infoDescription = "Short Name";
break;
case LP_SYSTEM_IDENTITY:
infoType = End;
break;
case LP_SYSTEM_DISPLAY_LENGTH:
if (haveSystemInformation) brl->resizeRequired = 1;
brl->textColumns = packet[1];
infoType = Dec8;
infoDescription = "Cell Count";
break;
case LP_SYSTEM_LANGUAGE:
infoType = String;
infoDescription = "Country Code";
break;
case LP_SYSTEM_FRAME_LENGTH:
maximumFrameLength = (packet[1] << 8)
| (packet[2] << 0)
;
infoType = Dec16;
infoDescription = "Maximum Frame Length";
break;
case LP_SYSTEM_NAME:
infoType = String;
infoDescription = "Long Name";
break;
case LP_SYSTEM_OPTION:
deviceOptions = (packet[1] << 24)
| (packet[2] << 16)
| (packet[3] << 8)
| (packet[4] << 0)
;
infoType = Hex32;
infoDescription = "Device Options";
break;
case LP_SYSTEM_PROTOCOL:
protocolVersion = ((packet[1] - '0') << 16)
| ((packet[3] - '0') << 8)
| ((packet[4] - '0') << 0)
;
infoType = String;
infoDescription = "Protocol Version";
break;
case LP_SYSTEM_SERIAL:
infoType = String;
infoDescription = "Serial Number";
break;
case LP_SYSTEM_TYPE:
{
unsigned char identifier = packet[1];
if (!(model = getModelEntry(identifier))) {
logMessage(LOG_WARNING, "unknown EuroBraille model: 0X%02X", identifier);
}
}
infoType = Dec8;
infoDescription = "Model Identifier";
break;
case LP_SYSTEM_SOFTWARE:
firmwareVersion = ((packet[1] - '0') << 16)
| ((packet[3] - '0') << 8)
| ((packet[4] - '0') << 0)
;
infoType = String;
infoDescription = "Firmware Version";
break;
default:
infoType = Unknown;
break;
}
switch (infoType) {
case Unknown:
logMessage(LOG_WARNING, "unknown Esysiris system information subcode: 0X%02X", packet[0]);
break;
case End:
logMessage(LOG_DEBUG, "end of Esysiris system information");
return 1;
case String:
logMessage(logLevel, "Esysiris %s: %s", infoDescription, &packet[1]);
break;
case Dec8:
logMessage(logLevel, "Esysiris %s: %u", infoDescription, packet[1]);
break;
case Dec16:
logMessage(logLevel, "Esysiris %s: %u", infoDescription, (packet[1] << 8) | packet[2]);
break;
case Hex32:
logMessage(logLevel, "Esysiris %s: 0X%02X%02X%02X%02X",
infoDescription, packet[1], packet[2], packet[3], packet[4]);
break;
default:
logMessage(LOG_WARNING, "unimplemented Esysiris system information subcode type: 0X%02X", infoType);
break;
}
return 0;
}
static int
makeKeyboardCommand (BrailleDisplay *brl, const unsigned char *packet) {
unsigned char a = packet[1];
unsigned char b = packet[2];
unsigned char c = packet[3];
unsigned char d = packet[4];
int command = 0;
switch (a) {
case 0:
switch (b) {
case 0:
command = BRL_CMD_BLK(PASSCHAR) | d;
break;
case ASCII_BS:
command = BRL_CMD_BLK(PASSKEY) | BRL_KEY_BACKSPACE;
break;
case ASCII_HT:
command = BRL_CMD_BLK(PASSKEY) | BRL_KEY_TAB;
break;
case ASCII_CR:
command = BRL_CMD_BLK(PASSKEY) | BRL_KEY_ENTER;
break;
case ASCII_ESC:
command = BRL_CMD_BLK(PASSKEY) | BRL_KEY_ESCAPE;
break;
case 0X20: // space
command = BRL_CMD_BLK(PASSCHAR) | b;
break;
default:
if ((b >= 0X70) && (b <= 0X7B)) {
command = BRL_CMD_BLK(PASSKEY) | (BRL_KEY_FUNCTION + (b - 0X70));
}
break;
}
break;
case 1:
switch (b) {
case 0X07:
command = BRL_CMD_BLK(PASSKEY) | BRL_KEY_HOME;
break;
case 0X08:
command = BRL_CMD_BLK(PASSKEY) | BRL_KEY_END;
break;
case 0X09:
command = BRL_CMD_BLK(PASSKEY) | BRL_KEY_PAGE_UP;
break;
case 0X0A:
command = BRL_CMD_BLK(PASSKEY) | BRL_KEY_PAGE_DOWN;
break;
case 0X0B:
command = BRL_CMD_BLK(PASSKEY) | BRL_KEY_CURSOR_LEFT;
break;
case 0X0C:
command = BRL_CMD_BLK(PASSKEY) | BRL_KEY_CURSOR_RIGHT;
break;
case 0X0D:
command = BRL_CMD_BLK(PASSKEY) | BRL_KEY_CURSOR_UP;
break;
case 0X0E:
command = BRL_CMD_BLK(PASSKEY) | BRL_KEY_CURSOR_DOWN;
break;
case 0X0F:
command = BRL_CMD_BLK(PASSKEY) | BRL_KEY_INSERT;
break;
case 0X10:
command = BRL_CMD_BLK(PASSKEY) | BRL_KEY_DELETE;
break;
default:
break;
}
break;
default:
break;
}
if (!command) return BRL_CMD_NOOP;
if (c & 0X02) command |= BRL_FLG_INPUT_CONTROL;
if (c & 0X04) command |= BRL_FLG_INPUT_META;
return command;
}
static int
handleKeyEvent (BrailleDisplay *brl, unsigned char *packet) {
switch (packet[0]) {
case LP_KEY_BRAILLE: {
KeyNumberSet keys = ((packet[1] << 8) | packet[2]) & 0X3Ff;
enqueueKeys(brl, keys, EU_GRP_BrailleKeys, 0);
return 1;
}
case LP_KEY_INTERACTIVE: {
unsigned char key = packet[2];
if ((key > 0) && (key <= brl->textColumns)) {
key -= 1;
switch (packet[1]) {
case INTERACTIVE_SINGLE_CLIC: // single click
enqueueKey(brl, EU_GRP_RoutingKeys1, key);
case INTERACTIVE_REPETITION: // repeat
return 1;
case INTERACTIVE_DOUBLE_CLIC: // double click
enqueueKey(brl, EU_GRP_RoutingKeys2, key);
return 1;
default:
break;
}
}
break;
}
case LP_KEY_COMMAND: {
KeyNumberSet keys;
if (model->isIris) {
keys = ((packet[1] << 8) | packet[2]) & 0XFFF;
} else {
keys = (packet[1] << 24) + (packet[2] << 16) + (packet[3] << 8) + packet[4];
}
if (model->isIris) {
enqueueKeys(brl, keys, EU_GRP_CommandKeys, 0);
} else {
enqueueUpdatedKeys(brl, keys, &commandKeys, EU_GRP_CommandKeys, 0);
}
return 1;
}
case LP_KEY_PC: {
int command = makeKeyboardCommand(brl, packet);
enqueueCommand(command);
if (command != BRL_CMD_NOOP) return 1;
break;
}
default:
break;
}
return 0;
}
static int
readCommand (BrailleDisplay *brl, KeyTableCommandContext ctx) {
unsigned char packet[2048];
ssize_t length;
while ((length = readPacket(brl, packet, sizeof(packet))) > 0) {
switch (packet[3]) {
case LP_SYSTEM:
if (handleSystemInformation(brl, packet+4)) haveSystemInformation = 1;
continue;
case LP_KEY:
if (handleKeyEvent(brl, packet+4)) continue;
break;
case LP_MODE:
if (packet[4] == LP_MODE_PILOT) {
/* return from internal menu */
forceRewrite();
}
continue;
case LP_VISU:
/* ignore visualization */
continue;
default:
break;
}
logUnexpectedPacket(packet, length);
}
return (length == -1)? BRL_CMD_RESTARTBRL: EOF;
}
static int
initializeDevice (BrailleDisplay *brl) {
int retriesLeft = 2;
haveSystemInformation = 0;
model = NULL;
firmwareVersion = 0;
protocolVersion = 0;
deviceOptions = 0;
maximumFrameLength = 0;
forceRewrite();
sequenceCheck = 0;
sequenceKnown = 0;
commandKeys = 0;
do {
{
static const unsigned char packet[] = {LP_SYSTEM, LP_SYSTEM_IDENTITY};
if (writePacket(brl, packet, sizeof(packet)) == -1) return 0;
}
while (io->awaitInput(brl, 500)) {
if (readCommand(brl, KTB_CTX_DEFAULT) == BRL_CMD_RESTARTBRL) return 0;
if (haveSystemInformation) {
if (!model) return 0;
setBrailleKeyTable(brl, model->keyTable);
if (!maximumFrameLength) {
if (model->isIris) maximumFrameLength = 2048;
if (model->isEsys) maximumFrameLength = 128;
if (model->isEsytime) maximumFrameLength = 512;
}
logMessage(LOG_INFO, "Model Detected: %s (%u cells)",
model->modelName, brl->textColumns);
return 1;
}
}
} while (retriesLeft-- && (errno == EAGAIN));
return 0;
}
static int
resetDevice (BrailleDisplay *brl) {
return 0;
}
static int
writeWindow (BrailleDisplay *brl) {
static unsigned char previousCells[MAXIMUM_DISPLAY_SIZE];
unsigned int size = brl->textColumns * brl->textRows;
if (cellsHaveChanged(previousCells, brl->buffer, size, NULL, NULL, &forceWindowRewrite)) {
unsigned char data[size + 2];
unsigned char *byte = data;
*byte++ = LP_BRAILLE_DISPLAY;
*byte++ = LP_BRAILLE_DISPLAY_STATIC;
byte = translateOutputCells(byte, brl->buffer, size);
if (writePacket(brl, data, byte-data) == -1) return 0;
}
return 1;
}
static int
hasVisualDisplay (BrailleDisplay *brl) {
return model->hasVisualDisplay;
}
static int
writeVisual (BrailleDisplay *brl, const wchar_t *text) {
if (model->hasVisualDisplay) {
{
static wchar_t previousText[MAXIMUM_DISPLAY_SIZE];
unsigned int size = brl->textColumns * brl->textRows;
if (textHasChanged(previousText, text, size, NULL, NULL, &forceVisualRewrite)) {
unsigned char data[size + 2];
unsigned char *byte = data;
*byte++ = LP_LCD_DISPLAY;
*byte++ = LP_LCD_DISPLAY_TEXT;
{
const wchar_t *character = text;
const wchar_t *end = character + size;
while (character < end) {
*byte++ = iswLatin1(*character)? *character: '?';
character += 1;
}
}
if (writePacket(brl, data, byte-data) == -1) return 0;
}
}
{
static int previousCursor;
if (cursorHasChanged(&previousCursor, brl->cursor, &forceCursorRewrite )) {
const unsigned char packet[] = {
LP_LCD_DISPLAY, LP_LCD_DISPLAY_CARET, ((brl->cursor != BRL_NO_CURSOR)? (brl->cursor + 1): 0)
};
if (writePacket(brl, packet, sizeof(packet)) == -1) return 0;
}
}
}
return 1;
}
const ProtocolOperations esysirisProtocolOperations = {
.protocolName = "esysiris",
.initializeDevice = initializeDevice,
.resetDevice = resetDevice,
.readPacket = readPacket,
.writePacket = writePacket,
.readCommand = readCommand,
.writeWindow = writeWindow,
.hasVisualDisplay = hasVisualDisplay,
.writeVisual = writeVisual
};