blob: c25c260781b1ae819a11e79a037064a782909933 [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>.
*/
/* BrailleLite/braille.c - Braille display library
* For Blazie Engineering's Braille Lite series
* Author: Nikhil Nair <nn201@cus.cam.ac.uk>
* Copyright (C) 1998 by Nikhil Nair.
* Some additions by: Nicolas Pitre <nico@fluxnic.net>
* Some modifications copyright 2001 by Stéphane Doyon <s.doyon@videotron.ca>.
* Some additions by: Dave Mielke <dave@mielke.cc>
*/
#include "prologue.h"
#include <stdio.h>
#include <string.h>
#include "log.h"
#include "parse.h"
#include "timing.h"
#include "async_wait.h"
#include "message.h"
typedef enum {
PARM_BAUDRATE,
PARM_KBEMU
} DriverParameter;
#define BRLPARMS "baudrate", "kbemu"
#include "brl_driver.h"
#include "braille.h"
#include "io_serial.h"
#define BL_NEED_ARRAYS
#include "bindings.h" /* for keybindings */
#define QSZ 256 /* size of internal input queue in bytes */
#define INT_CSR_SPEED 2 /* on/off time in cycles */
#define ACK_TIMEOUT 1000 /* timeout in ms for an ACK to come back */
SerialDevice *BL_serialDevice = NULL; /* file descriptor for Braille display */
static unsigned char *prevdata = NULL; /* previously received data */
static unsigned char *rawdata = NULL; /* writebrl() buffer for raw Braille data */
static int blitesz; /* set to 18 or 40 */
static int waiting_ack = 0; /* waiting acknowledgement flag */
static int reverse_kbd = 0; /* reverse keyboard flag */
static int intoverride = 0; /* internal override flag -
* highly dubious behaviour ...
*/
static int int_cursor = 0; /* position of internal cursor: 0 = none */
static unsigned int kbemu = 1; /* keyboard emulation (whether you can type) */
/* The input queue is only manipulated by the qput() and qget()
* functions.
*/
static unsigned char *qbase = NULL; /* start of queue in memory */
static int qoff = 0; /* offset of first byte */
static int qlen = 0; /* number of items in the queue */
/* Data type for a Braille Lite key, including translation into command
* codes:
*/
typedef struct
{
unsigned char raw; /* raw value, after any keyboard reversal */
int cmd; /* command code */
unsigned char asc; /* ASCII translation of Braille keys */
unsigned char spcbar; /* 1 = on, 0 = off */
unsigned char routing; /* routing key number */
}
blkey;
static int
qput (unsigned char c)
{
if (qlen == QSZ)
return EOF;
qbase[(qoff + qlen++) % QSZ] = c;
return 0;
}
static int
qget (blkey * kp)
{
unsigned char c;
int how;
static const unsigned char counts[] = {1, 3, 3};
unsigned char count;
if (qlen == 0)
return EOF;
c = qbase[qoff];
/* extended sequences start with a zero */
how = (c == 0)? 1:
(c == 0x80 && blitesz != 18)? 2:
0;
count = counts[how];
if (qlen < count)
return EOF;
memset (kp, 0, sizeof (*kp));
switch (how) {
case 0: /* non-extended sequences (BL18) */
/* We must deal with keyboard reversal here: */
if (reverse_kbd)
{
if (c >= 0x80) /* advance bar */
c ^= 0x03;
else
c = (c & 0x40) | ((c & 0x38) >> 3) | ((c & 0x07) << 3);
}
/* Now we fill in all the info about the keypress: */
if (c >= 0x80) /* advance bar */
{
kp->raw = c;
switch (c) {
case 0x83: /* left */
kp->cmd = BLT_BARLT;
break;
case 0x80: /* right */
kp->cmd = BLT_BARRT;
break;
default: /* unrecognised keypress */
kp->cmd = 0;
break;
}
}
else
{
kp->spcbar = ((c & 0x40)? 1: 0);
c &= 0x3f; /* leave only dot key info */
kp->raw = c;
kp->cmd = cmdtrans[c];
kp->asc = brltrans[c];
}
break;
case 1: { /* extended sequences (BL40) */
unsigned char c2 = qbase[((qoff + 1) % QSZ)];
unsigned char c3 = qbase[((qoff + 2) % QSZ)];
/* We must deal with keyboard reversal here: */
if (reverse_kbd)
{
if (c2 == 0)
{ /* advance bars or routing keys */
if (c3 & 0x80) /* advance bars */
c3 = ((c3 & 0xF0) |
((c3 & 0x1) << 3) | ((c3 & 0x2) << 1) |
((c3 & 0x4) >> 1) | ((c3 & 0x8) >> 3));
else if (c3 > 0 && c3 <= blitesz)
c3 = blitesz - c3 + 1;
}
else
{
c2 = (((c2 & 0x38) >> 3) | ((c2 & 0x07) << 3) |
((c2 & 0x40) << 1) | ((c2 & 0x80) >> 1));
c3 = ((c3 & 0x40) | ((c3 & 0x38) >> 3) | ((c3 & 0x07) << 3));
}
}
/* Now we fill in all the info about the keypress: */
if (c2 == 0) /* advance bars or routing keys */
{
kp->raw = c3;
if (c3 & 0x80)
kp->cmd = (*barcmds)[c3 & 0xF];
else if (c3 > 0 && c3 <= blitesz)
kp->routing = c3;
}
else
{
kp->spcbar = ((c3 & 0x40)? 1: 0);
c3 &= 0x3f; /* leave only dot key info */
kp->raw = (c2 & 0xC0) | c3; /* combine info for all 8 dots */
/* c2&0x3F and c3&0x3F are the same, i.e. dots 1-6. */
kp->cmd = cmdtrans[c3];
kp->asc = brltrans[c3];
}
break;
}
case 2: { /* extended sequences (millennium) */
unsigned char c3 = qbase[((qoff + 2) % QSZ)];
/* We must deal with keyboard reversal here: */
if (reverse_kbd)
c3 = ((c3 & 0x11) << 3) | ((c3 & 0x22) << 1) | ((c3 & 0x44) >> 1) | ((c3 & 0x88) >> 3);
kp->raw = c3;
if (c3 & 0x0f)
kp->cmd = (*barcmds)[((c3 & 0x1) << 3) | ((c3 & 0x2) << 1) | ((c3 & 0x4) >> 1) | ((c3 & 0x8) >> 3)];
else if (c3 & 0x30)
kp->cmd = rwwcmds[(c3 >> 4) & 0x3];
else if (c3 & 0xc0)
kp->cmd = lwwcmds[(c3 >> 6) & 0x3];
else
kp->cmd = 0;
break;
}
default:
kp->cmd = 0;
break;
}
/* adjust queue variables for next member */
qoff = (qoff + count) % QSZ;
qlen -= count;
return 0;
}
static void
qfill (void)
{
unsigned char c; /* character buffer */
while (serialReadData (BL_serialDevice, &c, 1, 0, 0) == 1)
{
if (waiting_ack && c == 5) /* ^e is the acknowledgement character ... */
waiting_ack = 0;
else
qput (c);
}
}
static void
qflush (void)
{
qfill();
qlen = 0;
}
static int
await_ack (void) {
TimePeriod period;
startTimePeriod(&period, ACK_TIMEOUT);
waiting_ack = 1;
do {
asyncWait(10); /* sleep for 10 ms */
qfill();
if (!waiting_ack) return 1;
} while (!afterTimePeriod(&period, NULL));
return 0;
}
static void
write_prebrl (void) {
static const unsigned char request[] = {0X05, 0X44}; /* code to send before Braille */
serialWriteData(BL_serialDevice, request, sizeof(request));
}
static int
brl_construct (BrailleDisplay *brl, char **parameters, const char *device)
{
static const unsigned int good_baudrates[] =
{300,600,1200,2400,4800,9600,19200,38400, 0};
unsigned int baudrate;
/* Init string for Model detection */
if (!*parameters[PARM_BAUDRATE] ||
!serialValidateBaud(&baudrate, "baud rate",
parameters[PARM_BAUDRATE], good_baudrates))
baudrate = BAUDRATE;
if (*parameters[PARM_KBEMU])
if (!validateYesNo(&kbemu, parameters[PARM_KBEMU]))
logMessage(LOG_WARNING, "%s: %s", "invalid keyboard emulation setting", parameters[PARM_KBEMU]);
kbemu = !!kbemu;
if (!isSerialDeviceIdentifier(&device)) {
unsupportedDeviceIdentifier(device);
return 0;
}
logMessage(LOG_DEBUG, "Opening serial port: %s", device);
if ((BL_serialDevice = serialOpenDevice(device))) {
if (serialRestartDevice(BL_serialDevice, baudrate)) {
if (serialSetFlowControl(BL_serialDevice, SERIAL_FLOW_HARDWARE)) {
if ((qbase = malloc(QSZ))) {
qflush();
write_prebrl();
if (await_ack()) {
logMessage(LOG_DEBUG, "Got response.");
/* Next, let's detect the BLT-Model (18, 40, M20, M40). */
barcmds = &bar2cmds;
{
unsigned char cells[18];
memset(cells, 0, sizeof(cells));
serialWriteData(BL_serialDevice, cells, sizeof(cells));
waiting_ack = 1;
asyncWait(400);
qfill();
if (waiting_ack) {
/* no response, so it must be BLT40 */
blitesz = 40;
brl->keyBindings = "40_m20_m40";
} else {
blitesz = sizeof(cells);
brl->keyBindings = "18";
}
}
{
static const unsigned char request[] = {0X05, 0X57}; /* code to send before Braille */
asyncWait(200);
qflush();
serialWriteData(BL_serialDevice, request, sizeof(request));
waiting_ack = 0;
asyncWait(200);
qfill();
if (qlen) {
char response[qlen + 1];
int length = 0;
do {
unsigned char byte = qbase[qoff % QSZ];
qoff = (qoff + 1) % QSZ, --qlen;
if (!byte) break;
response[length++] = byte;
} while (qlen);
response[length] = 0;
logMessage(LOG_INFO, "Braille Lite identity: %s", response);
if ((response[0] == 'X') &&
(response[1] == ' ') &&
(response[2] == 'B')) {
blitesz = atoi(&response[3]);
if (blitesz <= 20) barcmds = &bar1cmds;
}
}
}
logMessage(LOG_NOTICE, "Braille Lite %d detected.", blitesz);
brl->textColumns = blitesz; /* initialise size of display - */
brl->textRows = 1; /* Braille Lites are single line displays */
makeOutputTable(dotsTable_ISO11548_1);
makeInputTable();
/* Allocate space for buffers */
if ((prevdata = malloc(brl->textColumns))) {
memset(prevdata, 0, brl->textColumns);
if ((rawdata = malloc(brl->textColumns))) {
return 1;
//free(rawdata);
//rawdata = NULL;
} else {
logMallocError();
}
free(prevdata);
prevdata = NULL;
} else {
logMallocError();
}
} else {
logMessage(LOG_DEBUG, "BrailleLite not responding.");
}
free(qbase);
qbase = NULL;
} else {
logMallocError();
}
}
}
serialCloseDevice(BL_serialDevice);
BL_serialDevice = NULL;
}
return 0;
}
static void
brl_destruct (BrailleDisplay * brl)
{
if (rawdata) {
free(rawdata);
rawdata = NULL;
}
if (prevdata) {
free(prevdata);
prevdata = NULL;
}
if (qbase) {
free(qbase);
qbase = NULL;
}
if (BL_serialDevice) {
serialCloseDevice(BL_serialDevice);
BL_serialDevice = NULL;
}
}
static int
brl_writeWindow (BrailleDisplay * brl, const wchar_t *text)
{
short i; /* loop counter */
/* If the intoverride flag is set, then calls to writebrl() from the main
* module are ignored, because the display is in internal use.
* This is highly antisocial behaviour!
*/
if (intoverride)
return 1;
/* First, the internal cursor: */
if (int_cursor)
{
static int timer = 0; /* for internal cursor */
timer = (timer + 1) % (INT_CSR_SPEED * 2);
brl->buffer[int_cursor - 1] = (timer < INT_CSR_SPEED)?
(BRL_DOT1 | BRL_DOT2 | BRL_DOT3 | BRL_DOT7):
(BRL_DOT4 | BRL_DOT5 | BRL_DOT6 | BRL_DOT8);
}
/* Next we must handle display reversal: */
if (reverse_kbd) {
for (i=0; i<blitesz; i+=1) {
rawdata[i] = translateInputCell(brl->buffer[blitesz - 1 - i]);
}
} else {
memcpy(rawdata, brl->buffer, blitesz);
}
/* Only refresh display if the data has changed: */
if (cellsHaveChanged(prevdata, rawdata, blitesz, NULL, NULL, NULL))
{
/* Dot mapping from standard to BrailleLite: */
translateOutputCells(rawdata, rawdata, blitesz);
/* First we process any pending keystrokes, just in case any of them
* are ^e ...
*/
waiting_ack = 0; /* Not really necessary, but ... */
qfill ();
/* Next we send the ^eD sequence, and wait for an ACK */
waiting_ack = 1;
/* send the ^ED... */
write_prebrl();
if (!await_ack()) return 1;
/* OK, now we'll suppose we're all clear to send Braille data. */
serialWriteData(BL_serialDevice, rawdata, blitesz);
await_ack();
}
return 1;
}
static int
brl_readCommand (BrailleDisplay *brl, KeyTableCommandContext context)
{
static enum {
ST_NORMAL, /* transparent */
ST_CURSOR, /* position internal cursor */
ST_REPEAT, /* set repeat count */
ST_CONFIG /* preferences options */
} state = ST_NORMAL;
static int repeat = 0; /* repeat count for command */
static int repeatNext = 0; /* flag to indicate whether 0 we repeat the
same command or 1 we get the next command
and repeat that. */
static int hold, shift, shiftlck, ctrl, meta;
#ifdef USE_TEXTTRANS
static int dot8shift;
#endif /* USE_TEXTTRANS */
static blkey key;
static char outmsg[41];
int temp = BRL_CMD_NOOP;
again:
if(repeatNext || repeat == 0) {
/* Process any new keystrokes: */
qfill ();
if (qget (&key) == EOF) /* no keys to process */
return EOF;
repeatNext = 0;
}
if(repeat>0)
repeat--;
/* Our overall behaviour depends on the state variable (see above). */
switch (state)
{
case ST_NORMAL: /* transparent */
/* First we deal with external commands: */
do
{
/* if it's not an external command, go on */
if (!key.cmd)
break;
/* if advance bar, return with corresponding command */
if (key.asc == 0)
return key.cmd;
/* I thought I was smart when I suggested to remove CMD_CUT_END.
Well now there's this nasty exception: the command offset
depends on the display size! */
if(key.cmd == BRL_CMD_BLK(COPY_RECT) || key.cmd == BRL_CMD_BLK(COPY_LINE))
key.cmd += blitesz-1;
if(key.spcbar && (key.cmd &BRL_MSK_BLK) == BRL_CMD_BLK(PASSKEY)) {
/*
if(!kbemu)
return EOF;
*/
if (!shiftlck)
shift = 0;
ctrl = meta = 0;
#ifdef USE_TEXTTRANS
dot8shift = 0;
#endif /* USE_TEXTTRANS */
}
/* always OK if chorded */
if (key.spcbar)
return key.cmd;
/* kbemu could be on, then go on */
if (kbemu && context == KTB_CTX_DEFAULT)
break;
/* if it's a dangerous command it should have been chorded */
if (dangcmd[(key.raw & 0x38) >> 3] & (1 << (key.raw & 0x07)))
break;
/* finally we are OK */
return key.cmd;
}
while (0);
/* Next, internal commands: */
if (key.spcbar)
switch (key.asc)
{
case BLT_KBEMU: /* set keyboard emulation */
kbemu ^= 1;
shift = shiftlck = ctrl = meta = 0;
#ifdef USE_TEXTTRANS
dot8shift = 0;
#endif /* USE_TEXTTRANS */
if(kbemu)
message (NULL, gettext("keyboard emu on"), MSG_SILENT);
else message (NULL, gettext("keyboard emu off"), MSG_SILENT);
return BRL_CMD_NOOP;
case BLT_ROTATE: /* rotate Braille Lite by 180 degrees */
reverse_kbd ^= 1;
return BRL_CMD_NOOP;
case BLT_POSITN: /* position internal cursor */
int_cursor = blitesz / 2;
state = ST_CURSOR;
return BRL_CMD_NOOP;
case BLT_REPEAT: /* set repeat count */
hold = 0;
snprintf (outmsg, sizeof(outmsg), "%s:", gettext("repeat count"));
message (NULL, outmsg, MSG_SILENT | MSG_NODELAY);
intoverride = 1;
state = ST_REPEAT;
return BRL_CMD_NOOP;
case BLT_CONFIG: /* configuration menu */
snprintf (outmsg, sizeof(outmsg), "%s? [m/s/r/z]", gettext("config"));
message (NULL, outmsg, MSG_SILENT | MSG_NODELAY);
intoverride = 1;
state = ST_CONFIG;
return BRL_CMD_NOOP;
case ' ': /* practical exception for */
/* If keyboard mode off, space bar == BRL_CMD_HOME */
if (!kbemu || context != KTB_CTX_DEFAULT)
return BRL_CMD_HOME;
}
/* check for routing keys */
if (key.routing)
return (BRL_CMD_BLK(ROUTE) + key.routing - 1);
if (!kbemu)
return BRL_CMD_NOOP;
/* Now kbemu is definitely on. */
switch (key.raw & 0xC0)
{
case 0x40: /* dot 7 */
shift = 1;
break;
case 0xC0: /* dot 78 */
ctrl = 1;
break;
case 0x80: /* dot 8 */
#ifdef USE_TEXTTRANS
dot8shift = 1;
#else /* USE_TEXTTRANS */
meta = 1;
#endif /* USE_TEXTTRANS */
break;
}
if (key.spcbar && key.asc != ' ')
switch (key.asc)
{
case BLT_UPCASE: /* upper case next */
if (shift)
shiftlck = 1;
else
shift = 1;
return BRL_CMD_NOOP;
case BLT_UPCOFF: /* cancel upper case */
shift = shiftlck = 0;
return BRL_CMD_NOOP;
case BLT_CTRL: /* control next */
ctrl = 1;
return BRL_CMD_NOOP;
#ifdef USE_TEXTTRANS
case BLT_DOT8SHIFT: /* add dot 8 to next pattern */
dot8shift = 1;
return BRL_CMD_NOOP;
#endif /* USE_TEXTTRANS */
case BLT_META: /* meta next */
meta = 1;
return BRL_CMD_NOOP;
case BLT_ABORT: /* abort - quit keyboard emulation */
kbemu = 0;
message (NULL, gettext("keyboard emu off"), MSG_SILENT);
return BRL_CMD_NOOP;
default: /* unrecognised command */
shift = shiftlck = ctrl = meta = 0;
#ifdef USE_TEXTTRANS
dot8shift = 0;
#endif /* USE_TEXTTRANS */
return BRL_CMD_NOOP;
}
/* OK, it's an ordinary (non-chorded) keystroke, and kbemu is on. */
#ifndef USE_TEXTTRANS
if (ctrl && key.asc >= 96)
/* old code was (key.asc & 0x1f) */
temp = BRL_CMD_BLK(PASSCHAR) | key.asc | BRL_FLG_INPUT_CONTROL;
else if (meta && key.asc >= 96)
temp = BRL_CMD_BLK(PASSCHAR) | key.asc | BRL_FLG_INPUT_META;
else if (shift && (key.asc & 0x40))
/* old code was (key.asc & 0xdf) */
temp = BRL_CMD_BLK(PASSCHAR) | key.asc | BRL_FLG_INPUT_SHIFT;
else
temp = BRL_CMD_BLK(PASSCHAR) | key.asc;
#else /* USE_TEXTTRANS */
temp = BRL_CMD_BLK(PASSDOTS) |
(keys_to_dots[key.raw &0x3F]
| ((meta) ? BRL_FLG_INPUT_META : 0)
| ((ctrl) ? (BRL_DOT7 | BRL_DOT8) :
(shift) ? BRL_DOT7 :
(dot8shift) ? BRL_DOT8 : 0));
#endif /* USE_TEXTTRANS */
if (!shiftlck)
shift = 0;
ctrl = meta = 0;
#ifdef USE_TEXTTRANS
dot8shift = 0;
#endif /* USE_TEXTTRANS */
outmsg[0] = 0;
return temp;
case ST_CURSOR: /* position internal cursor */
switch (key.cmd)
{
case BRL_CMD_HOME: /* go to middle */
int_cursor = blitesz / 2;
break;
case BRL_CMD_LNBEG: /* beginning of display */
int_cursor = 1;
break;
case BRL_CMD_LNEND: /* end of display */
int_cursor = blitesz;
break;
case BRL_CMD_FWINLT: /* quarter left */
int_cursor = MAX (int_cursor - blitesz / 4, 1);
break;
case BRL_CMD_FWINRT: /* quarter right */
int_cursor = MIN (int_cursor + blitesz / 4, blitesz);
break;
case BRL_CMD_CHRLT: /* one character left */
if (int_cursor > 1)
int_cursor--;
break;
case BRL_CMD_CHRRT: /* one character right */
if (int_cursor < blitesz)
int_cursor++;
break;
case BRL_CMD_BLK(ROUTE): /* route cursor */
if (key.spcbar)
{
temp = BRL_CMD_BLK(ROUTE) + int_cursor - 1;
int_cursor = 0;
state = ST_NORMAL;
}
return temp;
case BRL_CMD_BLK(CLIP_NEW): /* begin copy */
case BRL_CMD_BLK(CLIP_ADD):
if (key.spcbar)
{
temp = key.cmd + int_cursor - 1;
int_cursor = 0;
state = ST_NORMAL;
}
return temp;
case BRL_CMD_BLK(COPY_RECT): /* end copy */
case BRL_CMD_BLK(COPY_LINE):
if (key.spcbar)
{
temp = key.cmd + int_cursor - 1;
int_cursor = 0;
state = ST_NORMAL;
}
return temp;
case BRL_CMD_DISPMD: /* attribute info */
temp = BRL_CMD_BLK(DESCCHAR) + int_cursor - 1;
int_cursor = 0;
state = ST_NORMAL;
return temp;
default:
if (key.asc == BLT_ABORT) {
/* cancel cursor positioning */
int_cursor = 0;
state = ST_NORMAL;
}
break;
}
if (key.routing)
int_cursor = key.routing;
return BRL_CMD_NOOP;
case ST_REPEAT: /* set repeat count */
if (key.asc >= '0' && key.asc <= '9')
{
hold = (hold * 10 + key.asc - '0') % 100;
if (hold) {
snprintf (outmsg, sizeof(outmsg), "%s: %d", gettext("repeat count"), hold);
} else {
snprintf (outmsg, sizeof(outmsg), "%s: ", gettext("repeat count"));
}
intoverride = 0;
message (NULL, outmsg, MSG_SILENT | MSG_NODELAY);
intoverride = 1;
}
else if (key.routing)
{
hold = key.routing +1;
snprintf (outmsg, sizeof(outmsg), "%s: %d", gettext("repeat count"), hold);
intoverride = 0;
message (NULL, outmsg, MSG_SILENT | MSG_NODELAY);
intoverride = 1;
}
else {
intoverride = 0;
outmsg[0] = 0;
state = ST_NORMAL;
if (hold > 0) {
if (key.asc == SWITCHVT_NEXT || key.asc == SWITCHVT_PREV)
/* That's chorded or not... */
return BRL_CMD_BLK(SWITCHVT) + (hold-1);
else if (key.asc == O_SETMARK)
return BRL_CMD_BLK(SETMARK) + (hold-1);
else if (key.asc == O_GOTOMARK)
return BRL_CMD_BLK(GOTOMARK) + (hold-1);
else if (key.spcbar) /* chorded */
switch (key.asc)
{
case BLT_ENDCMD: /* set repeat count */
if (hold > 1) {
/* repeat next command */
repeat = hold;
repeatNext = 1;
}
/* fall through */
case BLT_ABORT: /* abort or endcmd */
return BRL_CMD_NOOP;
}
/* if the key is any other, start repeating it. */
repeat = hold;
goto again;
}
}
return BRL_CMD_NOOP;
case ST_CONFIG: /* preferences options */
switch (key.asc)
{
case 'm': /* preferences menu */
intoverride = 0;
state = ST_NORMAL;
return BRL_CMD_PREFMENU;
case 's': /* save preferences */
intoverride = 0;
state = ST_NORMAL;
return BRL_CMD_PREFSAVE;
case 'r': /* restore saved preferences */
intoverride = 0;
state = ST_NORMAL;
return BRL_CMD_PREFLOAD;
case BLT_ABORT: /* abort */
intoverride = 0;
state = ST_NORMAL;
default: /* in any case */
return BRL_CMD_NOOP;
}
}
/* We should never reach this point ... */
return EOF;
}