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