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