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