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