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