1/*
2 * A braille interface to unix tty terminals
3 *
4 * Authors:  Hadi Bargi Rangin  bargi@dots.physics.orst.edu
5 *           Bill Barry         barryb@dots.physics.orst.edu
6 *
7 * Copyright (c) 1995 by Science Access Project, Oregon State University.
8 *
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2, or (at your option)
13 * any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program (see the file COPYING); if not, write to the
22 * Free Software Foundation, Inc.,
23 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
24 *
25 ****************************************************************
26 */
27
28#include <stdio.h>
29#include <fcntl.h>
30#include <sys/stat.h>
31
32#include "config.h"
33#include "screen.h"
34#include "extern.h"
35#include "braille.h"
36
37#ifdef HAVE_BRAILLE
38
39
40extern int bd_init_powerbraille_40 __P((void));
41extern int bd_init_powerbraille_80 __P((void));
42extern int bd_init_navigator_40 __P((void));
43
44extern struct layer *flayer;
45extern struct display *displays, *display;
46extern char *rc_name;
47
48
49
50
51/* global variables */
52
53struct braille_display bd;
54
55struct bd_type {
56  char *name;
57  int (*init) __P((void));
58};
59
60static struct bd_type bd_typelist[] =
61{
62  {"powerbraille_40", bd_init_powerbraille_40},
63  {"powerbraille_80", bd_init_powerbraille_80},
64  {"navigator_40"   , bd_init_navigator_40}
65};
66
67static void position_braille_cursor __P((void));
68static int  initialize_braille_display_type __P((char *));
69static int  open_braille_device __P(());
70static int  load_braille_table __P((char *));
71static void bd_signal __P((void));
72static void bd_bc_left __P((void));
73static void bd_bc_right __P((void));
74static void bd_bc_up __P((void));
75static void bd_bc_down __P((void));
76static void bd_upper_left __P((void));
77static void bd_upper_right __P((void));
78static void bd_lower_left __P((void));
79static void bd_lower_right __P((void));
80static int  bd_do_search __P((int, int, int));
81static void bd_normalize __P((int, int));
82static void bd_readev_fn __P((struct event *, char *));
83static void bd_writeev_fn __P((struct event *, char *));
84static void bd_selectev_fn __P((struct event *, char *));
85
86static unsigned char btable_local [] =
87{
88  0xC8,0xC1,0xC3,0xC9,0xD9,0xD1,0xCB,0xDB,
89  0xD3,0xCA,0xDA,0xC5,0xC7,0xCD,0xDD,0xD5,
90  0xCF,0xDF,0xD7,0xCE,0xDE,0xE5,0xE7,0xFA,
91  0xED,0xFD,0xF5,0xEA,0xF3,0xFB,0xD8,0xF8,
92  0x00,0x2E,0x10,0x3C,0x2B,0x29,0x2F,0x04,
93  0x37,0x3E,0x21,0x2C,0x20,0x24,0x28,0x0C,
94  0x34,0x02,0x06,0x12,0x32,0x22,0x16,0x36,
95  0x26,0x14,0x31,0x30,0x23,0x3F,0x1C,0x39,
96  0x48,0x41,0x43,0x49,0x59,0x51,0x4B,0x5B,
97  0x53,0x4A,0x5A,0x45,0x47,0x4D,0x5D,0x55,
98  0x4F,0x5F,0x57,0x4E,0x5E,0x65,0x67,0x7A,
99  0x6D,0x7D,0x75,0x6A,0x73,0x7B,0x58,0x38,
100  0x08,0x01,0x03,0x09,0x19,0x11,0x0B,0x1B,
101  0x13,0x0A,0x1A,0x05,0x07,0x0D,0x1D,0x15,
102  0x0F,0x1F,0x17,0x0E,0x1E,0x25,0x27,0x3A,
103  0x2D,0x3D,0x35,0x2A,0x33,0x3B,0x18,0x78,
104  0x88,0x81,0x83,0x89,0x99,0x91,0x8B,0x9B,
105  0x93,0x8A,0x9A,0x85,0x87,0x8D,0x9D,0x95,
106  0x8F,0x9F,0x97,0x8E,0x9E,0xA5,0xA7,0xBA,
107  0xAD,0xBD,0xB5,0xAA,0xB3,0xBB,0x98,0xB8,
108  0x40,0x6E,0x50,0x7C,0x6B,0x69,0x6F,0x44,
109  0x77,0x7E,0x61,0x6C,0x60,0x64,0x68,0x4C,
110  0x74,0x42,0x46,0x52,0x72,0x62,0x56,0x76,
111  0x66,0x54,0x71,0x70,0x63,0x7F,0x5C,0x79,
112  0xC0,0xEE,0xD0,0xFC,0xEB,0xE9,0xEF,0xC4,
113  0xF7,0xFE,0xE1,0xEC,0xE0,0xE4,0xE8,0xCC,
114  0xF4,0xC2,0xC6,0xD2,0xF2,0xE2,0xD6,0xF6,
115  0xE6,0xD4,0xF1,0xF0,0xE3,0xFF,0xDC,0xF9,
116  0x80,0xAE,0x90,0xBC,0xAB,0xA9,0xAF,0x84,
117  0xB7,0xBE,0xA1,0xAC,0xA0,0xA4,0xA8,0x8C,
118  0xB4,0x82,0x86,0x92,0xB2,0xA2,0x96,0xB6,
119  0xA6,0x94,0xB1,0xB0,0xA3,0xBF,0x9C,0xB9
120};
121
122void
123InitBraille()
124{
125  bd.bd_start_braille=0;
126  bd.bd_port = 0;
127  bd.bd_braille_table = SaveStr("internal us-braille.tbl");
128  bd.bd_type = 0;
129  bd.bd_baud = 9600;
130  bd.bd_bell = 1;
131  bd.bd_eightdot = 1;
132  bd.bd_info = 0;
133  bd.bd_link = 1;
134  bd.bd_ncells = 0;
135  bd.bd_width = 0;
136  bd.bd_ncrc = 1;
137  bd.bd_scroll = 1;
138  bd.bd_skip = 0;
139  bd.bd_using_braille = 0;
140  bd.bd_obuflen = 0;
141  bd.bd_fd = -1;
142  bcopy((char *)btable_local, bd.bd_btable, 256);
143}
144
145static int
146initialize_braille_display_type(s)
147char *s;
148{
149  int i;
150
151  for (i = 0; i < sizeof(bd_typelist)/sizeof(*bd_typelist); i++)
152    if (!strcmp(s, bd_typelist[i].name))
153      break;
154  if (i == sizeof(bd_typelist)/sizeof(*bd_typelist))
155    {
156      Msg(0, "No entry for bd_type: %s ", s);
157      return -1;
158    }
159  bd.bd_type = bd_typelist[i].name;
160  if ((*bd_typelist[i].init)())
161    return -1;
162
163  if (!bd.bd_width)
164    bd.bd_width = bd.bd_ncells;
165
166  return 0;
167}
168
169void
170StartBraille()
171{
172  bd.bd_dpy = displays;
173
174  debug("StartBraille called\n");
175  evdeq(&bd.bd_readev);
176  evdeq(&bd.bd_writeev);
177  evdeq(&bd.bd_selectev);
178  bd.bd_using_braille = 0;
179
180  if (!bd.bd_start_braille)
181    return;
182
183  if (bd.bd_type == 0 || bd.bd_port == 0)
184    return;
185
186  if (bd.bd_fd < 0 && open_braille_device())
187    {
188      Msg(0, "bd_port turned off");
189      free(bd.bd_port);
190      bd.bd_port = 0;
191      return;
192    }
193
194  /* check if braille display is connected and turned on */
195  if (bd.bd_response_test())
196    {
197      Msg(0, "Make sure that braille display is connected and turned on. ");
198      Msg(0, "start_braille turned off");
199      bd.bd_start_braille = 0;
200    }
201  else
202    {
203      bd.bd_using_braille = 1;
204      bd.bd_readev.fd = bd.bd_writeev.fd = bd.bd_fd;
205      bd.bd_readev.type  = EV_READ;
206      bd.bd_writeev.type = EV_WRITE;
207      bd.bd_selectev.type = EV_ALWAYS;
208      bd.bd_readev.data = bd.bd_writeev.data = bd.bd_selectev.data = (char *)&bd;
209      bd.bd_readev.handler  = bd_readev_fn;
210      bd.bd_writeev.handler = bd_writeev_fn;
211      bd.bd_selectev.handler = bd_selectev_fn;
212      evenq(&bd.bd_readev);
213      bd.bd_writeev.condpos = &bd.bd_obuflen;
214      bd.bd_writeev.condneg = 0;
215      evenq(&bd.bd_writeev);
216      bd.bd_selectev.pri = -20;
217      evenq(&bd.bd_selectev);
218    }
219}
220
221
222static int
223load_braille_table(tablename)
224char *tablename;
225{
226  int i, j, c, p;
227  FILE *fp;
228  char buffer[80], a[10];
229
230  if ((fp = secfopen(tablename, "r")) == 0)
231    {
232      Msg(errno, "Braille table not found: %s ", tablename);
233      return -1;
234    }
235  bzero(bd.bd_btable, 256);
236  /* format:
237   * Dec  Hex    Braille      Description
238   *  7   07    (12-45--8)    BEL
239   */
240  while (fgets(buffer, sizeof(buffer), fp))
241    {
242      if (buffer[0] == '#')
243	continue;
244      sscanf(buffer,"%d %x %8s", &i, &j, a);
245      if (i < 0 || i > 255)
246	continue;
247      for (j=1, p=1, c=0; j<9; j++, p*=2)
248	if (a[j] == '0' + j)
249	  c += p;
250      bd.bd_btable[i] = c;
251    }
252  fclose(fp);
253  return 0;
254}
255
256
257static int
258open_braille_device(s)
259char *s;
260{
261  char str[256];
262
263  sprintf(str, "%d cs8 -istrip ixon ixoff", bd.bd_baud);
264  bd.bd_fd = OpenTTY(bd.bd_port, str);
265  if (bd.bd_fd == -1)
266    {
267      Msg(errno, "open comm port failed: %s ", bd.bd_port);
268      return -1;
269    }
270  fcntl(bd.bd_fd, F_SETFL, FNBLOCK);
271  return 0;
272}
273
274
275static void
276position_braille_cursor()
277{
278  int  sx = bd.bd_sx;
279  int  bx = BD_FORE->w_bd_x;
280  int  eol = BD_FORE->w_width;
281  int  w = bd.bd_width;
282
283  if (bd.bd_scroll)
284    bx = sx - w + bd.bd_ncrc;	/* keep rc centered in window */
285  else
286    bx = w * (int)(sx / w);	/* increase bc in integral steps */
287
288  if (bx > eol - w)
289    bx = eol - w;
290  if (bx < 0)
291    bx = 0;
292  BD_FORE->w_bd_x = bx;
293  BD_FORE->w_bd_y = bd.bd_sy;
294}
295
296
297void
298RefreshBraille()
299{
300  int i, y, xs, xe;
301  int cursor_pos;
302
303  if (!bd.bd_using_braille)
304    return;
305  if (!BD_FORE)
306    return;
307  bcopy(bd.bd_line, bd.bd_oline, bd.bd_ncells);
308  bd.bd_refreshing = 1;
309  flayer = bd.bd_dpy->d_forecv->c_layer;
310  bd.bd_sx = flayer->l_x;
311  bd.bd_sy = flayer->l_y;
312  display = bd.bd_dpy;
313  if ((D_obufp != D_obuf) && bd.bd_link)
314    {
315      /* jump to real cursor */
316      debug("calling position_braille_cursor\n");
317      position_braille_cursor();
318    }
319  bclear(bd.bd_line, bd.bd_ncells);
320
321  y = BD_FORE->w_bd_y;
322  xs = BD_FORE->w_bd_x;
323
324  if (bd.bd_info & 1)
325    {
326      sprintf(bd.bd_line, "%02d%02d", (BD_FORE->w_bd_x + 1) % 100, (BD_FORE->w_bd_y + 1) % 100);
327      bd.bd_line[4] = ' ';
328    }
329  if (bd.bd_info & 2)
330    {
331      sprintf(bd.bd_line + bd.bd_ncells - 4, "%02d%02d",(bd.bd_sx +1) % 100, (bd.bd_sy +1) % 100);
332    }
333
334  xe = xs + bd.bd_width - 1;
335
336  if (xs > flayer->l_width - 1)
337    xs = flayer->l_width - 1;
338  if (xe > flayer->l_width - 1)
339    xe = flayer->l_width - 1;
340
341  if (D_status)
342    {
343      sprintf(bd.bd_line, "**%-*.*s", bd.bd_ncells - 2, bd.bd_ncells - 2, D_status_lastmsg ? D_status_lastmsg : "unknown msg");
344      xs = xe = -1;
345    }
346  else if (xs <= xe)
347    {
348      LayRedisplayLine(-1, xs, xe, 1);
349      LayRedisplayLine(y, xs, xe, 1);
350    }
351
352  debug1("Braille: got >%s<\n", bd.bd_line);
353
354  bd.bd_refreshing = 0;
355
356  if (y == bd.bd_sy && xs <= bd.bd_sx && bd.bd_sx <= xe)
357    cursor_pos = bd.bd_sx - xs + (bd.bd_info & 1 ? 4 : 0);
358  else
359    cursor_pos = bd.bd_ncells;
360  for (i = 0; i < bd.bd_ncells; i++)
361    if (bd.bd_line[i] != bd.bd_oline[i])
362      break;
363  if (bd.bd_cursorpos != cursor_pos || i < bd.bd_ncells)
364    bd.write_line_braille(bd.bd_line, bd.bd_ncells, cursor_pos);
365  bd.bd_cursorpos = cursor_pos;
366}
367
368
369/**********************************************************************
370 *
371 */
372
373/*
374 * So, why is there a Flush() down below? The reason is simple: the
375 * cursor warp (if bd_link is on) checks the obuf to see if something
376 * happened. If there would be no Flush, screen would warp the
377 * bd cursor if a bd movement command tries to ring the bell.
378 * (In other words: this is a gross hack!)
379 */
380static void
381bd_signal()
382{
383  if (!bd.bd_bell)
384    return;
385  display = bd.bd_dpy;
386  if (D_obufp != D_obuf)
387    AddCStr(D_BL);
388  else
389    {
390      AddCStr(D_BL);
391      Flush();
392    }
393}
394
395static int
396bd_do_search(y, xs, xe)
397int y, xs, xe;
398{
399  int oy = BD_FORE->w_bd_y;
400
401  if (!bd.bd_skip)	/* no skip mode, found it */
402    {
403      if (xs > xe)
404	return 0;
405      bd.bd_searchmin = xs;
406      bd.bd_searchmax = xe;
407      return 1;
408    }
409  flayer = bd.bd_dpy->d_forecv->c_layer;
410  bd.bd_searchmax = -1;
411  bd.bd_searchmin = flayer->l_width;
412  if (xs <= xe)
413    {
414      BD_FORE->w_bd_y = y;	/* stupid hack */
415      bd.bd_refreshing = bd.bd_searching = 1;
416      bd.bd_searchstart = xs;
417      bd.bd_searchend   = xe;
418      LayRedisplayLine(-1, xs, xe, 1);
419      LayRedisplayLine(y, xs, xe, 1);
420      bd.bd_refreshing = bd.bd_searching = 0;
421      BD_FORE->w_bd_y = oy;
422    }
423  return bd.bd_searchmax >= 0;
424}
425
426static void
427bd_normalize(x, y)
428int x, y;
429{
430  if (x > BD_FORE->w_width - bd.bd_width)
431    x = BD_FORE->w_width - bd.bd_width;
432  if (x < 0)
433    x = 0;
434  if (y < 0)
435    {
436      bd_signal();
437      y = 0;
438    }
439  if (y >= BD_FORE->w_height)
440    {
441      bd_signal();
442      y = BD_FORE->w_height - 1;
443    }
444  if (x != BD_FORE->w_bd_x || y != BD_FORE->w_bd_y)
445    bd.bd_moved = 1;
446  BD_FORE->w_bd_x = x;
447  BD_FORE->w_bd_y = y;
448}
449
450static void
451bd_bc_left()
452{
453  int bx = BD_FORE->w_bd_x, by = BD_FORE->w_bd_y;
454  int ex;
455
456  ex = bx - 1;
457  bx = 0;
458  for (; by >= 0; by--)
459    {
460      if (bd_do_search(by, 0, ex))
461	{
462	  if (!bd.bd_skip && by != BD_FORE->w_bd_y)
463	    bd_signal();
464	  bx = bd.bd_searchmax + 1 - bd.bd_width;
465	  break;
466	}
467      ex = BD_FORE->w_width - 1;
468    }
469  bd_normalize(bx, by);
470}
471
472static void
473bd_bc_right()
474{
475  int bx = BD_FORE->w_bd_x, by = BD_FORE->w_bd_y;
476  int sx;
477
478  sx = bx + bd.bd_width;
479  bx = BD_FORE->w_width - bd.bd_width;
480  for (; by < BD_FORE->w_height; by++)
481    {
482      if (bd_do_search(by, sx, BD_FORE->w_width - 1))
483	{
484	  if (!bd.bd_skip && by != BD_FORE->w_bd_y)
485	    bd_signal();
486	  bx = bd.bd_searchmin;
487	  break;
488	}
489      sx = 0;
490    }
491  bd_normalize(bx, by);
492}
493
494static void
495bd_bc_up()
496{
497  int bx = BD_FORE->w_bd_x, by = BD_FORE->w_bd_y;
498
499  for (by--; by >= 0; by--)
500    if (bd_do_search(by, bx, bx + bd.bd_width - 1))
501      break;
502  bd_normalize(bx, by);
503}
504
505static void
506bd_bc_down()
507{
508  int bx = BD_FORE->w_bd_x, by = BD_FORE->w_bd_y;
509
510  for (by++; by < BD_FORE->w_height; by++)
511    if (bd_do_search(by, bx, bx + bd.bd_width - 1))
512      break;
513  bd_normalize(bx, by);
514}
515
516
517static void
518bd_upper_left()
519{
520  bd_normalize(0, 0);
521}
522
523
524static void
525bd_upper_right()
526{
527  bd_normalize(BD_FORE->w_width - bd.bd_width, 0);
528}
529
530
531static void
532bd_lower_left()
533{
534  bd_normalize(0, BD_FORE->w_height - 1);
535}
536
537
538static void
539bd_lower_right()
540{
541  bd_normalize(BD_FORE->w_width - bd.bd_width, BD_FORE->w_height -1);
542}
543
544/**********************************************************************
545 *
546 */
547
548
549static void
550bd_check(x, c)
551int x, c;
552{
553  if (c == ' ')
554    return;
555  if (x < bd.bd_searchstart || x > bd.bd_searchend)
556    return;
557  if (x > bd.bd_searchmax)
558    bd.bd_searchmax = x;
559  if (x < bd.bd_searchmin)
560    bd.bd_searchmin = x;
561}
562
563
564
565/*ARGSUSED*/
566void
567BGotoPos(la, x, y)
568struct layer *la;
569int x, y;
570{
571}
572
573/*ARGSUSED*/
574void
575BCDisplayLine(la, ml, y, xs, xe, isblank)
576struct layer *la;
577struct mline *ml;
578int y, xs, xe;
579int isblank;
580{
581  int x;
582  int sx, ex;
583  char *l;
584
585  if (y != BD_FORE->w_bd_y)
586    return;
587  if (bd.bd_searching)
588    {
589      for (x = xs; x <= xe; x++)
590	bd_check(x, ml->image[x]);
591      return;
592    }
593  l = bd.bd_line;
594  sx = BD_FORE->w_bd_x;
595  ex = sx + bd.bd_width - 1;
596  if (bd.bd_info & 1)
597    l += 4;
598  for (x = xs; x <= xe; x++)
599    if (x >= sx && x <= ex)
600      l[x - sx] = ml->image[x];
601}
602
603/*ARGSUSED*/
604void
605BPutChar(la, c, x, y)
606struct layer *la;
607struct mchar *c;
608int x, y;
609{
610  int sx, ex;
611  char *l;
612
613  if (y != BD_FORE->w_bd_y)
614    return;
615  if (bd.bd_searching)
616    {
617      bd_check(x, c->image);
618      return;
619    }
620  l = bd.bd_line;
621  sx = BD_FORE->w_bd_x;
622  ex = sx + bd.bd_width - 1;
623  if (bd.bd_info & 1)
624    l += 4;
625  if (x >= sx && x <= ex)
626    l[x - sx] = c->image;
627}
628
629/*ARGSUSED*/
630void
631BPutStr(la, s, n, r, x, y)
632struct layer *la;
633char *s;
634int n;
635struct mchar *r;
636int x, y;
637{
638  int sx, ex;
639  char *l;
640
641  if (y != BD_FORE->w_bd_y)
642    return;
643  if (bd.bd_searching)
644    {
645      for (; n > 0; n--, s++, x++)
646	bd_check(x, *s);
647      return;
648    }
649  l = bd.bd_line;
650  sx = BD_FORE->w_bd_x;
651  ex = sx + bd.bd_width - 1;
652  if (bd.bd_info & 1)
653    l += 4;
654  for (; n > 0; n--, s++, x++)
655    if (x >= sx && x <= ex)
656      l[x - sx] = *s;
657}
658
659
660
661/**********************************************************************
662 *
663 */
664
665static char *infonames[] = {"none", "bc", "sc", "bc+sc"};
666
667void
668DoBrailleAction(act, msgok)
669struct action *act;
670int msgok;
671{
672  int nr, dosig;
673  int n, l, o;
674  char *s, **args;
675  struct stat st;
676
677  nr = act->nr;
678  args = act->args;
679  dosig = display && !*rc_name;
680
681  switch(nr)
682    {
683    case RC_BD_BELL:
684      if (ParseSwitch(act, &bd.bd_bell) || !msgok)
685	{
686	  bd_signal();
687	  break;
688	}
689      Msg(0, bd.bd_bell ? "bd_bell is on." : "bd_bell is off.");
690      break;
691
692    case RC_BD_EIGHTDOT:
693      if (ParseSwitch(act, &bd.bd_eightdot) || !msgok)
694	break;
695      Msg(0, "switched to %d-dots system.", bd.bd_eightdot ? 8 : 6);
696      break;
697
698    case RC_BD_INFO:
699      n = bd.bd_info;
700      if (*args)
701	{
702	  if (strlen(*args) == 4)
703	    n = args[0][n] - '0';
704	  else if (ParseNum(act, &n))
705	    break;
706	}
707      if (n < 0 && n > 3)
708	{
709          Msg(0, "Out of range; 0 <= bd_info >= 3 ");
710	  break;
711	}
712      /* bd_width at the beginning is unknown */
713      if (bd.bd_width == 0)
714        break;
715
716      o = (bd.bd_info * 2 + 2) & 12;
717      l = (n * 2 + 2) & 12;
718      if (l >= bd.bd_ncells)
719	{
720	  Msg(0, "bd_info is too large for braille display.");
721	  break;
722	}
723      if (l >= bd.bd_width + o)
724	{
725	  Msg(0, "bd_info is too large for bd_width.");
726	  break;
727	}
728      bd.bd_width += o - l;
729      bd.bd_info = n;
730
731      if (msgok)
732	Msg(0, "bd_info is %s.", infonames[n]);
733      position_braille_cursor();
734      break;
735
736    case RC_BD_LINK:
737      if (*args == 0 && bd.bd_moved)
738	bd.bd_link = 0;
739      if (ParseSwitch(act, &bd.bd_link))
740	break;
741      if (bd.bd_link)
742	{
743	  bd.bd_moved = 0;
744	  if (dosig)
745	    bd_signal();
746	  position_braille_cursor();
747	}
748      if (msgok)
749        Msg(0, bd.bd_link ? "bd_link is on." : "bd_link is off.");
750      break;
751
752    case RC_BD_SKIP:
753      if (ParseSwitch(act, &bd.bd_skip))
754	break;
755      if (bd.bd_skip && dosig)
756	bd_signal();
757      if (msgok)
758        Msg(0, bd.bd_skip ? "bd_skip is on." : "bd_skip is off.");
759      break;
760
761    case RC_BD_SCROLL:
762      if (ParseSwitch(act, &bd.bd_scroll) || !msgok)
763	{
764	  position_braille_cursor();
765          break;
766	}
767      Msg(0, bd.bd_scroll ? "bd_scroll is on." : "bd_scroll is off.");
768      break;
769
770    case RC_BD_NCRC:
771      n = bd.bd_ncrc;
772      if (*args)
773	{
774	  if (args[0][0] == '+')
775	    n = (n + atoi(*args + 1)) % bd.bd_width + 1;
776	  else if (args[0][0] == '-')
777	    n = (n - atoi(*args + 1)) % bd.bd_width + 1;
778	  else if (ParseNum(act, &n))
779	    break;
780	}
781      if (n < 1 ||  n > bd.bd_width)
782	{
783	  Msg(0, "Out of range; 1 <= bd_ncrc >= %d", bd.bd_width);
784	  break;
785	}
786      bd.bd_ncrc = n;
787      if (msgok)
788	Msg(0, "bd_ncrc status is: %d ", bd.bd_ncrc);
789      position_braille_cursor();
790      break;
791
792    case RC_BD_BRAILLE_TABLE:
793      s = 0;
794      if (*args)
795	{
796	  if (ParseSaveStr(act, &s))
797	    break;
798	  if (load_braille_table(s))
799	    {
800	      free(s);
801	      break;
802	    }
803	  if (bd.bd_braille_table)
804	    free(bd.bd_braille_table);
805	  bd.bd_braille_table = s;
806	}
807      if (msgok)
808	Msg(0, "bd_braille_table is: %s ", bd.bd_braille_table);
809      break;
810
811    case RC_BD_PORT:
812      s = 0;
813      if (*args)
814	{
815          if (ParseSaveStr(act, &s))
816	    break;
817
818	  if (stat(s, &st) || !S_ISCHR(st.st_mode) || access(s, R_OK|W_OK))
819	    {
820	      Msg(0, "Cannot access braille device port %s", s);
821	      free(s);
822	      break;
823	    }
824	  if (bd.bd_fd >= 0)
825	    close(bd.bd_fd);
826	  bd.bd_fd = -1;
827	  if (bd.bd_port)
828	    free(bd.bd_port);
829	  bd.bd_port = s;
830	}
831      if (msgok)
832	Msg(0, "bd_port is: %s ", bd.bd_port ? bd.bd_port : "not set");
833      StartBraille();
834      break;
835
836    case RC_BD_TYPE:
837      s = 0;
838      if (*args)
839        if (ParseSaveStr(act, &s) || initialize_braille_display_type(s))
840	  break;
841      if (msgok)
842	Msg(0, "bd_type is: %s ", bd.bd_type ? bd.bd_type : "not set");
843      StartBraille();
844      break;
845
846    case RC_BD_START_BRAILLE:
847      if (ParseSwitch(act, &bd.bd_start_braille))
848	break;
849      if (msgok)
850        Msg(0, bd.bd_start_braille ? "bd_start_braille is on." : "bd_start_braille is off.");
851      StartBraille();
852      break;
853
854    case RC_BD_WIDTH:
855      n = bd.bd_width;
856      if (*args)
857	{
858	  if (ParseNum(act, &n))
859	    break;
860	}
861      if (n <= 0)
862	{
863	  Msg(0, "Invalid value for bd_width: %d ", n);
864	  break;
865	}
866      l = (bd.bd_info * 2 + 2) & 12;
867      if (n > bd.bd_ncells - l || n < l)
868	{
869	  Msg(0, "bd_info is too large for bd_width.");
870	  break;
871	}
872      bd.bd_width = n;
873      if (msgok)
874	Msg(0, "bd_width is: %d ", bd.bd_width);
875      break;
876
877    case RC_BD_BC_LEFT:
878      bd_bc_left();
879      break;
880
881    case RC_BD_BC_RIGHT:
882      bd_bc_right();
883      break;
884
885    case RC_BD_BC_UP:
886      bd_bc_up();
887      break;
888
889    case RC_BD_BC_DOWN:
890      bd_bc_down();
891      break;
892
893    case RC_BD_UPPER_LEFT:
894      bd_upper_left();
895      break;
896
897    case RC_BD_UPPER_RIGHT:
898      bd_upper_right();
899      break;
900
901    case RC_BD_LOWER_LEFT:
902      bd_lower_left();
903      break;
904
905    case RC_BD_LOWER_RIGHT:
906      bd_lower_right();
907      break;
908
909    default:
910      break;
911    }
912}
913
914static void
915bd_readev_fn(ev, data)
916struct event *ev;
917char *data;
918{
919  bd.buttonpress();
920}
921
922static void
923bd_writeev_fn(ev, data)
924struct event *ev;
925char *data;
926{
927  int len;
928
929  if (bd.bd_obuflen == 0)
930    return;
931  if ((len = write(bd.bd_fd, bd.bd_obuf, bd.bd_obuflen)) < 0)
932    len = bd.bd_obuflen;	/* dead braille display */
933  if ((bd.bd_obuflen -= len))
934    bcopy(bd.bd_obuf + len, bd.bd_obuf, bd.bd_obuflen);
935}
936
937static void
938bd_selectev_fn(ev, data)
939struct event *ev;
940char *data;
941{
942  RefreshBraille();
943}
944
945#endif /* HAVE_BRAILLE */
946