1 /* Copyright (C) 1992, 1994, 1998 artofcode LLC.  All rights reserved.
2 
3   This program is free software; you can redistribute it and/or modify it
4   under the terms of the GNU General Public License as published by the
5   Free Software Foundation; either version 2 of the License, or (at your
6   option) any later version.
7 
8   This program is distributed in the hope that it will be useful, but
9   WITHOUT ANY WARRANTY; without even the implied warranty of
10   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   General Public License for more details.
12 
13   You should have received a copy of the GNU General Public License along
14   with this program; if not, write to the Free Software Foundation, Inc.,
15   59 Temple Place, Suite 330, Boston, MA, 02111-1307.
16 
17 */
18 
19 /*$Id: gdev3b1.c,v 1.2.6.1.2.1 2003/01/17 00:49:00 giles Exp $*/
20 /*
21  * This is a driver for the AT&T 3b1/7300/UnixPC console display.
22  *
23  * The image is built in a buffer the size of the page.  Once complete,
24  * a screen-sized subset is copied to the screen, and one can scroll
25  * through the entire image (move with "vi" or arrow keys).
26  *
27  * Written by Andy Fyfe, andy@cs.caltech.edu.
28  *
29  * There are a couple of undesirable "features" that I have found no
30  * way to work around.
31  *
32  * 1) Gs attempts to save the contents of the window before using it, and
33  *    then restores the contents afterward.  However, if the gs window is
34  *    not the current window, and there are small windows present, then
35  *    the saved image is incorrect, and thus the screen will not be correctly
36  *    restored.  This seems to be a bug in the 3b1 window driver.  Making
37  *    the gs window current before saving its contents is not an acceptable
38  *    solution.
39  *
40  * 2) Gs will enable the scrolling/help/cancel icons if the window has
41  *    a border.  Changing these border icons has the side effect of making
42  *    the gs window current.  This does circumvent the first problem though.
43  */
44 
45 /*
46  * About the ATT3B1_PERF flag (notes by Andy Fyfe):
47  *
48  * I am unable to profile gs on the 3b1, so I added ATT3B1_PERF as a
49  * quick way to find out how much time was spent in the 3b1 driver,
50  * through dynamically suppressing parts of the code at run time by
51  * setting environment variables.  I can then get the time spent in
52  * those parts by comparing the results of "time gs ....".
53  *
54  * At one point this was very useful, and led to a fairly substantial
55  * speedup of the fill and copy_mono routines.  It also showed that I
56  * wasn't going to get too much more, overall, by further attempts to
57  * optimize the 3b1 driver.  So those parts of the code controlled by
58  * ATT3B1_PERF have really now outlived their usefulness.
59  */
60 
61 #include "gx.h"
62 #include "gxdevice.h"
63 #include "gserrors.h"
64 
65 #include <errno.h>
66 #include <sys/window.h>
67 #include <sys/termio.h>
68 
69 typedef struct gx_device_att3b1_s {
70     gx_device_common;
71     int fd;				/* window file descriptor */
72     uchar *screen;			/* pointer to screen image */
73     ushort line_size;			/* size of screen line in bytes */
74     ulong screen_size;			/* size of screen image in bytes */
75     int page_num;				/* page number */
76 #ifdef ATT3B1_PERF
77     char *no_output, *no_fill, *no_copy;
78 #endif
79 } gx_device_att3b1;
80 #define att3b1dev ((gx_device_att3b1 *)dev)
81 
82 #define XDPI	100		/* to get a more-or-less square aspect ratio */
83 #define YDPI	72
84 #define XSIZE (8.5 * XDPI)	/* 8.5 x 11 inch page, by default */
85 #define YSIZE (11 * YDPI)
86 
87 static const ushort masks[] = { 0,
88     0x0001, 0x0003, 0x0007, 0x000f,
89     0x001f, 0x003f, 0x007f, 0x00ff,
90     0x01ff, 0x03ff, 0x07ff, 0x0fff,
91     0x1fff, 0x3fff, 0x7fff, 0xffff,
92 };
93 static uchar reverse_bits[256] = {
94   0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240,
95   8, 136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184, 120, 248,
96   4, 132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52, 180, 116, 244,
97   12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252,
98   2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242,
99   10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250,
100   6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246,
101   14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254,
102   1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241,
103   9, 137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249,
104   5, 133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245,
105   13, 141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253,
106   3, 131, 67, 195, 35, 163, 99, 227, 19, 147, 83, 211, 51, 179, 115, 243,
107   11, 139, 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59, 187, 123, 251,
108   7, 135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247,
109   15, 143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127, 255
110 };
111 
112 dev_proc_open_device(att3b1_open);
113 dev_proc_close_device(att3b1_close);
114 dev_proc_fill_rectangle(att3b1_fill_rectangle);
115 dev_proc_copy_mono(att3b1_copy_mono);
116 dev_proc_output_page(att3b1_output_page);
117 
118 private gx_device_procs att3b1_procs = {
119     att3b1_open,
120     gx_default_get_initial_matrix,
121     gx_default_sync_output,
122     att3b1_output_page,
123     att3b1_close,
124     gx_default_map_rgb_color,
125     gx_default_map_color_rgb,
126     att3b1_fill_rectangle,
127     gx_default_tile_rectangle,
128     att3b1_copy_mono,
129     gx_default_copy_color,
130     gx_default_draw_line,
131     gx_default_get_bits
132 };
133 
134 gx_device_att3b1 gs_att3b1_device = {
135     std_device_std_body(gx_device_att3b1, &att3b1_procs, "att3b1",
136       XSIZE, YSIZE, XDPI, YDPI),
137      { 0 },			/* std_procs */
138     -1, 0, 0,			/* fd, screen, line_size, */
139     0, 0,			/* screen size, page */
140 #ifdef ATT3B1_PERF
141     0, 0, 0,			/* no_output, no_fill, no_copy */
142 #endif
143 };
144 
145 int
att3b1_open(gx_device * dev)146 att3b1_open(gx_device *dev)
147 {
148     struct uwdata uw;
149 
150 #ifdef ATT3B1_PERF
151     char *getenv(const char *);
152 #endif
153 
154     if (att3b1dev->fd >= 0) {
155 	close(att3b1dev->fd);
156 	att3b1dev->fd = -1;
157     }
158 
159     if (att3b1dev->screen != NULL) {
160 	gs_free((char *)att3b1dev->screen,
161 		att3b1dev->screen_size, 1, "att3b1_open");
162 	att3b1dev->screen = 0;
163 	att3b1dev->screen_size = 0;
164     }
165 
166     att3b1dev->fd = open("/dev/tty", 2);
167     if (att3b1dev->fd < 0) {
168 	lprintf1("att3b1_open: open /dev/tty failed [%d]\n", errno);
169 	return_error(gs_error_ioerror);
170     }
171 
172     /* Verify that /dev/tty is associated with a console window. */
173     if (ioctl(att3b1dev->fd, WIOCGETD, &uw) < 0) {
174 	lprintf1("att3b1_open: can not obtain window data [%d]\n", errno);
175 	lprintf("att3b1_open: the att3b1 device requires a console window\n");
176 	att3b1_close(dev);
177 	return_error(gs_error_ioerror);
178     }
179 
180     /* we need an even number of bytes per line */
181     att3b1dev->line_size = ((att3b1dev->width + 15) / 16) * 2;
182     att3b1dev->screen_size = att3b1dev->line_size * att3b1dev->height;
183 
184     att3b1dev->screen =
185 	(uchar *)gs_malloc(att3b1dev->screen_size, 1, "att3b1_open");
186     if (att3b1dev->screen == NULL) {
187 	att3b1_close(dev);
188 	return_error(gs_error_VMerror);
189     }
190 
191     att3b1dev->page_num = 1;
192 
193 #ifdef ATT3B1_PERF
194     att3b1dev->no_output = getenv("GS_NOOUTPUT");
195     att3b1dev->no_fill = getenv("GS_NOFILL");
196     att3b1dev->no_copy = getenv("GS_NOCOPY");
197 #endif
198 
199     return 0;
200 }
201 
202 int
att3b1_close(gx_device * dev)203 att3b1_close(gx_device *dev)
204 {
205     if (att3b1dev->fd >= 0) {
206 	close(att3b1dev->fd);
207 	att3b1dev->fd = -1;
208     }
209 
210     if (att3b1dev->screen != NULL) {
211 	gs_free((char *)att3b1dev->screen,
212 		att3b1dev->screen_size, 1, "att3b1_close");
213 	att3b1dev->screen = 0;
214 	att3b1dev->screen_size = 0;
215     }
216 
217     return 0;
218 }
219 
220 int
att3b1_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index colour)221 att3b1_fill_rectangle(gx_device *dev, int x, int y, int w, int h,
222                       gx_color_index colour)
223 {
224     uint o, b, wl, wr, w2;
225     ushort *p, *q, maskl, maskr;
226 
227 #ifdef ATT3B1_PERF
228     if (att3b1dev->no_fill) return 0;
229 #endif
230 
231     fit_fill(dev, x, y, w, h);
232 
233     /* following fit_fill, we can assume x, y, w, h are unsigned. */
234 
235     p = (ushort *)&att3b1dev->screen[(ushort)y*att3b1dev->line_size] +
236 	(uint)x/16;
237     o = (uint)x % 16;
238     b = 16 - o;
239     wl = ((uint)w < b) ? (uint)w : b;
240     maskl = masks[wl] << o;
241     w -= wl;
242     wr = (uint)w % 16;
243     maskr = masks[wr];
244 
245     if (colour == 0) {
246 	maskl = ~maskl;
247 	maskr = ~maskr;
248 	while (h-- > 0) {
249 	    q = p;
250 	    w2 = w;
251 	    *q++ &= maskl;
252 	    while (w2 >= 16) {
253 		*q++ = 0;
254 		w2 -= 16;
255 	    }
256 	    *q &= maskr;
257 	    p += (att3b1dev->line_size / 2);
258 	}
259     }
260     else {
261 	while (h-- > 0) {
262 	    q = p;
263 	    w2 = w;
264 	    *q++ |= maskl;
265 	    while (w2 >= 16) {
266 		*q++ = 0xffff;
267 		w2 -= 16;
268 	    }
269 	    *q |= maskr;
270 	    p += (att3b1dev->line_size / 2);
271 	}
272     }
273 
274     return 0;
275 }
276 
277 #ifdef __GNUC__
278 #define rotate(value, count) \
279     asm("ror%.l	%2,%0" : "=d" (value) : "0" (value), "d" (count))
280 #else
281 #define rotate(value, count) \
282     value = (value >> count) | (value << (32-count))
283 #endif
284 
285 int
att3b1_copy_mono(gx_device * dev,const uchar * data,int data_x,int raster,gx_bitmap_id id,int x,int y,int width,int height,gx_color_index colour0,gx_color_index colour1)286 att3b1_copy_mono(gx_device *dev, const uchar *data,
287 		 int data_x, int raster, gx_bitmap_id id,
288 		 int x, int y, int width, int height,
289 		 gx_color_index colour0, gx_color_index colour1)
290 {
291     const ushort *src_p, *src_q;
292     ushort *dst_p, *dst_q;
293     ulong bits, mask, *p;
294     uint src_o, src_b, dst_o, dst_b, op;
295     uint w1, w2;
296 
297 #ifdef ATT3B1_PERF
298     if (att3b1dev->no_copy) return 0;
299 #endif
300 
301     if (colour1 == colour0)		/* vacuous case */
302 	return att3b1_fill_rectangle(dev, x, y, width, height, colour0);
303 
304     fit_copy(dev, data, data_x, raster, id, x, y, width, height);
305 
306     /* following fit_copy, we can assume x, y, width, height are unsigned. */
307 
308     /*
309      * In what follows, we're assuming that each row of the input bitmap
310      * is short-aligned, that is, that both "data" and "raster" are even.
311      */
312     src_p = ((const ushort *)data) + (uint)data_x/16;
313     src_o = (uint)data_x % 16;
314     src_b = 16 - src_o;
315 
316     dst_p = (ushort *)&att3b1dev->screen[(ushort)y*att3b1dev->line_size] +
317 	    (uint)x/16;
318     dst_o = (uint)x % 16;
319     dst_b = 16 - dst_o;
320 
321     op = (int)colour0 * 3 + (int)colour1 + 4;
322 
323     while (height-- > 0) {
324 	w2 = width;
325 	src_q = src_p;
326 	dst_q = dst_p;
327 
328 	while (w2 > 0) {
329 	    w1 = (w2 < 16) ? w2 : 16;
330 	    mask = masks[w1];
331 	    /*
332 	     * We are assuming that the bitmap "data" is typically aligned.
333 	     * Thus the test for this special case is typically a win over
334 	     * a 16-bit shift.
335 	     */
336 	    if (src_o == 0)
337 		bits = *src_q++;
338 	    else {
339 		bits = *((ulong *)src_q) >> src_b;
340 		bits &= 0xffff;
341 		src_q++;
342 	    }
343 	    if (w1 <= 8)
344 		bits = reverse_bits[bits>>8];
345 	    else
346 		bits = (reverse_bits[bits&0xff] << 8) | reverse_bits[bits>>8];
347 	    /*
348 	     * While the input bit map is assumed to be typically aligned, we
349 	     * assume that the place in the image is not.  Thus we don't
350 	     * separate out the aligned case.  Doing so would cost a test,
351 	     * and only reduce the average shift by about 1.
352 	     */
353 	    p = (ulong *)dst_q;
354 	    switch(op) {
355 	    case 1:	/* not src and dst */
356 		bits = ~(bits & mask);
357 		rotate(bits,dst_b);
358 		*p &= bits;
359 		break;
360 	    case 2:	/* src or dst */
361 		bits = bits & mask;
362 		rotate(bits,dst_b);
363 		*p |= bits;
364 		break;
365 	    case 3:	/* src and dst */
366 		bits = bits | ~mask;
367 		rotate(bits,dst_b);
368 		*p &= bits;
369 		break;
370 	    case 5:	/* src */
371 		rotate(bits,dst_b);
372 		rotate(mask,dst_b);
373 		*p = (*p & ~mask) | (bits & mask);
374 		break;
375 	    case 6:	/* not src or dst */
376 		bits = ~bits & mask;
377 		rotate(bits,dst_b);
378 		*p |= bits;
379 		break;
380 	    case 7:	/* not src */
381 		rotate(bits,dst_b);
382 		rotate(mask,dst_b);
383 		*p = (*p & ~mask) | (~bits & mask);
384 		break;
385 	    }
386 	    dst_q++;
387 	    w2 -= w1;
388 	}
389 
390 	src_p += (raster / 2);
391 	dst_p += (att3b1dev->line_size / 2);
392     }
393 
394     return 0;
395 }
396 
397 static int getKeyboard(gx_device *);
398 
399 const char *help_msg[] = {
400     "h, j, k, l, UP, DOWN, LEFT, RIGHT  move the page (0.25\" h, 0.5\" v)",
401     "H, J, K, L, BEG, END               move to far edge of the page",
402     "^U, ^D, ROLL UP, ROLL DOWN	        scroll up or down (1/2 screen height)",
403     "^F, ^B, PAGE UP, PAGE DOWN	        scroll up or down (full screen height)",
404     "c, C                               centre page horizontally, vertically",
405     "<, >, ^, _                         fine movements (single pixel)",
406     "^L, ^R, r, HOME                    move to default position",
407     "=, MARK                            make current position the default",
408     "I                                  invert the image (black <-> white)",
409     "q, x, ^C, EXIT, CANCL, n, f, NEXT,",
410     "    SPACE, RETURN, ENTER           end the page",
411     "?, HELP                            help screen",
412 };
413 
414 static void
do_help(gx_device * dev)415 do_help(gx_device *dev)
416 {
417     int i;
418     struct utdata ut;
419 
420     /* we would like to save the cursor position, but we can't */
421     write(att3b1dev->fd, "\033[2J\033[H", 7);
422 
423     /* write help screen */
424     for (i=0; i < sizeof(help_msg)/sizeof(help_msg[0]); ++i) {
425 	write(att3b1dev->fd, help_msg[i], strlen(help_msg[i]));
426 	write(att3b1dev->fd, "\n", 1);
427     }
428     ut.ut_num = WTXTSLK1;
429     strcpy(ut.ut_text, "Press any key to continue");
430     ioctl(att3b1dev->fd, WIOCSETTEXT, &ut);
431 
432     /* wait for keyboard input */
433     i = getKeyboard(dev);
434 
435     /* clear screen and put cursor at the bottom of the screen */
436     write(att3b1dev->fd, "\033[2J\033[99;1H", 11);
437 }
438 
439 private int
att3b1_do_output_page(gx_device * dev,int num_copies,int flush)440 att3b1_do_output_page(gx_device *dev, int num_copies, int flush)
441 {
442     struct urdata ur;
443     struct utdata ut, ut_orig;
444     struct uwdata uw;
445     int uflags;
446     struct termio old, new;
447     int xorigin, yorigin;
448     static int def_xorigin = 0, def_yorigin = 0;
449     int screen_width, screen_height;
450     int inverted = 0;
451     int error = 0;
452     int ch;
453     ushort *p;
454     ushort save_image[WINWIDTH * WINHEIGHT / 16];
455 
456 #ifdef ATT3B1_PERF
457     if (att3b1dev->no_output) return 0;
458 #endif
459 
460     /*
461      * initialize, and save screen state
462      */
463 
464     if (ioctl(att3b1dev->fd, WIOCGETD, &uw) < 0) {
465 	lprintf1("att3b1_output_page: window WIOCGETD ioctl failed [%d]\n",
466 	    errno);
467 	att3b1_close(dev);
468 	return_error(gs_error_ioerror);
469     }
470 
471     /*
472      * we assume, henceforth, that screen ioctl calls will succeed
473      */
474 
475     write(att3b1dev->fd, "\a\033[=1C", 6);
476 
477     uflags = uw.uw_uflags;
478     if (!(uflags & NBORDER)) {
479 	uw.uw_uflags = BORDHSCROLL | BORDVSCROLL | BORDHELP | BORDCANCEL;
480 	ioctl(att3b1dev->fd, WIOCSETD, &uw);
481     }
482 
483     ut_orig.ut_num = WTXTSLK1;
484     ioctl(att3b1dev->fd, WIOCGETTEXT, &ut_orig);
485 
486     /* This isn't necessary, but helps a bit when the following attempt
487        to get the current screen image fails (without any indication). */
488     memset(save_image, '\0', sizeof(save_image));
489 
490     ur.ur_srcbase = 0;
491     ur.ur_srcwidth = 0;
492     ur.ur_srcx = 0;
493     ur.ur_srcy = 0;
494     ur.ur_dstbase = save_image;
495     ur.ur_dstwidth = WINWIDTH / 8;
496     ur.ur_dstx = 0;
497     ur.ur_dsty = 0;
498     ur.ur_width = uw.uw_width;
499     ur.ur_height = uw.uw_height;
500     ur.ur_srcop = SRCSRC;
501     ur.ur_dstop = DSTSRC;
502     ur.ur_pattern = 0;
503     ioctl(att3b1dev->fd, WIOCRASTOP, &ur);
504 
505     ioctl(att3b1dev->fd, TCGETA, &old);
506     new = old;
507     new.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
508     new.c_cc[VMIN] = 1;
509     ioctl(att3b1dev->fd, TCSETAF, &new);
510 
511     screen_width = (uw.uw_width < att3b1dev->width) ? uw.uw_width
512 				: att3b1dev->width;
513     screen_height = (uw.uw_height < att3b1dev->height) ? uw.uw_height
514 				: att3b1dev->height;
515 
516     write(att3b1dev->fd, "\033[2J", 4);
517 
518     ur.ur_srcwidth = att3b1dev->line_size;
519     ur.ur_width = screen_width;
520     ur.ur_height = screen_height;
521     ur.ur_dstbase = 0;
522     ur.ur_dstwidth = 0;
523 
524     /*
525      * allow one to move the screen window through the entire image
526      */
527 
528     xorigin = def_xorigin;
529     yorigin = def_yorigin;
530 
531     while (1) {
532 	/* Things go bad if ur_srcx >= 2048 */
533 	ur.ur_srcbase = (ushort *)att3b1dev->screen + (xorigin >> 4);
534 	ur.ur_srcx = xorigin & 15;
535 	ur.ur_srcy = yorigin;
536 
537 	if (ioctl(att3b1dev->fd, WIOCRASTOP, &ur) < 0) {
538 	    lprintf1(
539 		"att3b1_output_page: window WIOCRASTOP ioctl failed [%d]\n",
540 		errno);
541 	    error = gs_error_ioerror;
542 	}
543 
544 	ut.ut_num = WTXTSLK1;
545 	sprintf(ut.ut_text,
546 	    "%s %d, top right (%d,%d), size (%d,%d), press '?' for help.",
547 	    flush ? "Showpage" : "Copypage", att3b1dev->page_num, xorigin, yorigin,
548 	    att3b1dev->width, att3b1dev->height);
549 	ioctl(att3b1dev->fd, WIOCSETTEXT, &ut);
550 
551 	ch = error ? 'q' : getKeyboard(dev);
552 
553 	switch(ch) {
554 	case 'h':
555 	    xorigin -= ((uint)(int)att3b1dev->x_pixels_per_inch+3)/4;
556 	    break;
557 
558 	case 'k':
559 	    yorigin -= ((uint)(int)att3b1dev->y_pixels_per_inch+1)/2;
560 	    break;
561 
562 	case 'l':
563 	    xorigin += ((uint)(int)att3b1dev->x_pixels_per_inch+3)/4;
564 	    break;
565 
566 	case 'j':
567 	    yorigin += ((uint)(int)att3b1dev->y_pixels_per_inch+1)/2;
568 	    break;
569 
570 	case 'H':
571 	    xorigin = 0;
572 	    break;
573 
574 	case 'K':
575 	    yorigin = 0;
576 	    break;
577 
578 	case 'L':
579 	    xorigin = att3b1dev->width - screen_width;
580 	    break;
581 
582 	case 'J':
583 	    yorigin = att3b1dev->height - screen_height;
584 	    break;
585 
586 	case '<':
587 	    xorigin -= 1;
588 	    break;
589 
590 	case '>':
591 	    xorigin += 1;
592 	    break;
593 
594 	case '^':
595 	    yorigin -= 1;
596 	    break;
597 
598 	case '_':
599 	    yorigin += 1;
600 	    break;
601 
602 
603 	case '\025':	/* control-U */
604 	    yorigin -= screen_height/2;
605 	    break;
606 
607 	case '\004':	/* control-D */
608 	    yorigin += screen_height/2;
609 	    break;
610 
611 	case '\002':	/* control-B */
612 	    yorigin -= screen_height;
613 	    break;
614 
615 	case '\006':	/* control-F */
616 	    yorigin += screen_height;
617 	    break;
618 
619 	case '\f':
620 	case 'r' :
621 	case '\022':	/* control-R */
622 	    xorigin = def_xorigin;
623 	    yorigin = def_yorigin;
624 	    break;
625 
626 	case 'c':	/* centre horizontally */
627 	    xorigin = (att3b1dev->width - screen_width) / 2;
628 	    break;
629 
630 	case 'C':	/* centre vertically */
631 	    yorigin = (att3b1dev->height - screen_height) / 2;
632 	    break;
633 
634 	case '=':
635 	    def_xorigin = xorigin;
636 	    def_yorigin = yorigin;
637 	    break;
638 
639 	case 'I':
640 	    for (p = (ushort *)att3b1dev->screen;
641 	      p < (ushort *)&att3b1dev->screen[att3b1dev->screen_size]; ++p)
642 		*p = ~ *p;
643 	    inverted = !inverted;
644 	    break;
645 
646 	case '?':
647 	    do_help(dev);
648 	    break;
649 
650 	case -1:
651 	    error = gs_error_ioerror;
652 	    /* fall through, for cleanup */
653 
654 	case 'q':
655 	case 'x':
656 	case '\003':	/* control-C */
657 	case 'n':
658 	case 'f':
659 	case ' ':
660 	case '\n':
661 	case '\r':
662 	    if (flush)
663 		att3b1dev->page_num++;
664 	    else if (inverted)	/* restore inverted image for copypage */
665 		for (p = (ushort *)att3b1dev->screen;
666 		  p < (ushort *)&att3b1dev->screen[att3b1dev->screen_size]; ++p)
667 		    *p = ~ *p;
668 	    if (!(uflags & NBORDER)) {
669 		ioctl(att3b1dev->fd, WIOCGETD, &uw); /*window may have moved*/
670 		uw.uw_uflags = uflags;
671 		ioctl(att3b1dev->fd, WIOCSETD, &uw);
672 	    }
673 	    ur.ur_srcbase = save_image;
674 	    ur.ur_srcwidth = WINWIDTH / 8;
675 	    ur.ur_width = uw.uw_width;
676 	    ur.ur_height = uw.uw_height;
677 	    ur.ur_srcx = 0;
678 	    ur.ur_srcy = 0;
679 	    ioctl(att3b1dev->fd, WIOCRASTOP, &ur);
680 	    ioctl(att3b1dev->fd, WIOCSETTEXT, &ut_orig);
681 	    ioctl(att3b1dev->fd, TCSETAF, &old);
682 	    write(att3b1dev->fd, "\033[=0C", 5);
683 
684 	    if (error) {
685 		att3b1_close(dev);
686 		return_error(error);
687 	    }
688 	    else
689 		return 0;
690 	}
691 
692 	if (xorigin >= att3b1dev->width - screen_width)
693 	    xorigin = att3b1dev->width - screen_width;
694 	if (xorigin < 0)
695 	    xorigin = 0;
696 	if (yorigin >= att3b1dev->height - screen_height)
697 	    yorigin = att3b1dev->height - screen_height;
698 	if (yorigin < 0)
699 	    yorigin = 0;
700     }
701 }
702 int
att3b1_output_page(gx_device * dev,int num_copies,int flush)703 att3b1_output_page(gx_device *dev, int num_copies, int flush)
704 {
705     int code = att3b1_do_output_page(dev, num_copies, flush);
706 
707     if (code >= 0)
708 	code = gx_finish_output_page(dev, num_copies, flush);
709     return code;
710 }
711 
712 static int
get_char(gx_device * dev)713 get_char(gx_device *dev)
714 {
715     char ch;
716     int count;
717 
718     count = read(att3b1dev->fd, &ch, 1);
719     if (count == 0)
720 	return 'q';
721     else if (count < 0)
722 	return -1;
723     else
724 	return ch;
725 }
726 
727 static int
getKeyboard(gx_device * dev)728 getKeyboard(gx_device *dev)
729 {
730     char ch;
731 
732     ch = get_char(dev);
733 
734     if (ch != '\033')
735 	return ch;
736 
737     /*
738      * If the char is escape, interpret the escape sequence and return
739      * an equivalent single character.
740      *
741      * Note that a mouse click on a window border icon is translated
742      * to the corresponding key, for example, the "up" icon generates
743      * roll-up/page-up/beg for the left/middle/right mouse button.
744      */
745 
746     switch (get_char(dev)) {
747     case '[':
748 	switch(get_char(dev)) {
749 	case 'A':	/* up arrow */
750 	    return 'k';
751 	case 'T':	/* shift up arrow (roll up) */
752 	    return '\025';
753 	case 'B':	/* down arrow */
754 	    return 'j';
755 	case 'S':	/* shift down arrow (roll down) */
756 	    return '\004';
757 	case 'C':	/* right arrow */
758 	    return 'l';
759 	case 'D':	/* left arrow */
760 	    return 'h';
761 	case 'H':	/* home */
762 	    return 'r';
763 	case 'U':	/* page down */
764 	    return '\006';
765 	case 'V':	/* page up */
766 	    return '\002';
767 	}
768 	break;
769     case 'O':
770 	switch(get_char(dev)) {
771 	case 'm':	/* help */
772 	case 'M':	/* shift help */
773 	    return '?';
774 	case 'k':	/* exit */
775 	case 'K':	/* shift exit */
776 	case 'w':	/* cancl */
777 	case 'W':	/* shift cancl */
778 	    return 'q';
779 	}
780 	break;
781     case 'N':
782 	switch(get_char(dev)) {
783 	case 'h':	/* next */
784 	    return 'f';
785 	case 'i':	/* mark */
786 	    return '=';
787 	case 'L':	/* shift right arrow */
788 	    return 'l';
789 	case 'K':	/* shift left arrow */
790 	    return 'h';
791 	}
792 	break;
793     case '9':	/* Beg */
794 	return 'K';
795     case '0':	/* End */
796 	return 'J';
797     }
798     return '\0';
799 }
800