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