chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mupdisp / xterm.c
1
2 /* Copyright (c) 1995, 1996, 1998, 1999, 2000, 2001, 2004, 2005 by Arkkra Enterprises */
3 /* All rights reserved */
4
5 /* functions for displaying Mup/Ghostscript output under X windows. */
6
7 #include "mupdisp.h"
8
9 #ifdef XWINDOW
10
11 #include <X11/Xlib.h>
12 #include <X11/Xresource.h>
13
14 /* size for XLookupString buffer */
15 #define IBUFSIZ         8
16
17 /* X window icon to use when window is icon-ized.
18  * Shows musical notes. This was generated using the bitmap tool */
19 #define Disp_icon_width 32
20 #define Disp_icon_height 32
21 static unsigned char Disp_icon_bits[] = {
22    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00,
23    0x00, 0xc0, 0x0b, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xf8, 0x08, 0x00,
24    0x00, 0x1e, 0x08, 0x00, 0x00, 0x0e, 0x08, 0x00, 0x00, 0x02, 0x08, 0x00,
25    0xff, 0xff, 0xff, 0xff, 0x00, 0x02, 0x08, 0x14, 0x00, 0x02, 0x08, 0x22,
26    0x00, 0x02, 0x08, 0x23, 0x00, 0x02, 0x08, 0x15, 0xff, 0xff, 0xff, 0xff,
27    0x00, 0x02, 0x08, 0x01, 0x00, 0x02, 0x08, 0x01, 0x00, 0x82, 0x0f, 0x01,
28    0x00, 0xc2, 0x0f, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00, 0x82, 0x07, 0x01,
29    0x00, 0x02, 0x00, 0x01, 0xe0, 0x03, 0x02, 0x01, 0xf0, 0x03, 0x01, 0x01,
30    0xff, 0xff, 0xff, 0xff, 0xe0, 0x81, 0x00, 0x01, 0x00, 0x40, 0x00, 0x01,
31    0x80, 0x31, 0x00, 0x01, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
32    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
33
34 #define MINHEIGHT       (400)   /* allow this short a window in X window mode */
35 #define MAXHEIGHT       LINES_PER_PAGE  /* tallest window we allow */
36
37 #define SMALL 1
38 #define OK 0
39
40 /* bitmap of whether control key is pressed */
41 #define LEFTC   0x1
42 #define RIGHTC  0x2
43
44 extern unsigned char Waitmsg_bitmap[];  /* message to tell user to wait */
45 extern int Waitmsg_width, Waitmsg_height;
46
47 /* define X window screen and display things */
48 static Display *Display_p;
49 static int Xscreen;
50 static XImage *Image_p;
51 static XFontStruct *Font_info_p;
52 static GC gc;                   /* X graphics context */
53 static Window Win;
54 static unsigned int Width, Height;
55 static XSizeHints Size_hints;   /* tell window manager what size we want */
56 static unsigned long Foreground; /* color */
57 static unsigned long Background; /* color */
58 static char Mupdisp[] = "mupdisp";
59
60 /* X resource manager things */
61 static XrmOptionDescRec Option_table[] = {
62         { "-geometry",  ".geometry",    XrmoptionSepArg, (caddr_t) 0 },
63         { "-background","*background",  XrmoptionSepArg, (caddr_t) 0 },
64         { "-bg",        "*background",  XrmoptionSepArg, (caddr_t) 0 },
65         { "-foreground","*foreground",  XrmoptionSepArg, (caddr_t) 0 },
66         { "-fg",        "*foreground",  XrmoptionSepArg, (caddr_t) 0 },
67 };
68 static int Opt_table_size = sizeof(Option_table) / sizeof (XrmOptionDescRec);
69 static char Xoptions_usage[] =
70 "MUPADDOP=\
71  Also the following X options:\n\
72    -bg color            set background color\n\
73    -fg color            set foreground color\n\
74    -geometry XxY+N+M    set window size and location\n";
75 XrmDatabase Resource_db;
76
77
78 /* local functions */
79 static char * get_cmd_resource P((char *resource_name));
80 static unsigned long get_color P((char *resource_name,
81                 unsigned long default_value));
82 static int color_ok P((char *resource_name, char *value, XColor *color_p));
83 static void create_image P((int wid, int height));
84 static void get_GC P((Window win));
85 static void load_font P((void));
86 static void TooSmall P((Window win));
87 \f
88
89 /* parse any X-specific aguments */
90
91 void
92 parse_X_options()
93 {
94         XrmInitialize();
95         XrmParseCommand( &Resource_db, Option_table, Opt_table_size,
96                         Mupdisp, &Argc, Argv);
97         /* tell Mup about these additional options */
98         putenv(Xoptions_usage);
99 }
100 \f
101
102 /* setup for X-window operation. Basically copy things from the X manual,
103  * and customize as appropriate. */
104
105
106 void
107 xterm_setup()
108
109 {
110         int x = 0, y = 0;
111         unsigned int border_width = 4;
112         unsigned int display_width, display_height;
113         char *window_name = Mupdisp;
114         char *icon_name = Mupdisp;
115         Pixmap icon_pixmap;
116         char *display_name = NULL;
117         XEvent event;
118         char * value;
119         unsigned int dummy_width;       /* we don't allow Width to change */
120         XWindowAttributes attributes;
121         int got_geometry = 0;
122
123
124         /* If mupdisp is setuid to root so that libsvga can work on
125          * console devices, Ghostscript can fail under X.
126          * So relinquish our superuser-ism.
127          */
128         if (getuid() != geteuid()) {
129                 seteuid(getuid());
130         }
131
132         /* set up display */
133         if ( (Display_p = XOpenDisplay(display_name)) == NULL) {
134                 fprintf(stderr, "%s: can't connect to X server %s\n", Argv[0],
135                                 XDisplayName(display_name));
136                 generalcleanup(1);
137         }
138
139         Xscreen = DefaultScreen(Display_p);
140
141         display_width = DisplayWidth(Display_p, Xscreen);
142         display_height = DisplayHeight(Display_p, Xscreen);
143
144         /* this is our ideal size. Window manager may have other ideas */
145         Width = BITS_PER_LINE;
146         /* Use a height a little smaller than the screen height (to allow
147          * for window borders), or at a minimum, what's in Conf_info_p */
148         if (display_height - 50 > Conf_info_p->vlines) {
149                 Height = display_height - 50;
150         }
151         else {
152                 Height = Conf_info_p->vlines;
153         }
154
155         /* If user specified colors, get those, otherwise use black on white */
156         Background = get_color("background", WhitePixel(Display_p, Xscreen));
157         Foreground = get_color("foreground", BlackPixel(Display_p, Xscreen));
158         
159         /* Now see if user specified geometry, either from command line,
160          * or failing that, from default database. */
161         if ((value = get_cmd_resource("geometry")) != (char *) 0) {
162                 XParseGeometry(value, &x, &y, &dummy_width, &Height);
163                 got_geometry = 1;
164         }
165         else if ((value = XGetDefault(Display_p, Mupdisp, "geometry")) != 0) {
166                 XParseGeometry(value, &x, &y, &dummy_width, &Height);
167                 got_geometry = 1;
168         }
169         if (Height < MINHEIGHT) {
170                 Height = MINHEIGHT;
171         }
172         if (Height > MAXHEIGHT) {
173                 Height = MAXHEIGHT;
174         }
175
176
177         /* create window and icon */
178         Win = XCreateSimpleWindow(Display_p, RootWindow(Display_p, Xscreen),
179                         x, y, Width, Height, border_width,
180                         Foreground, Background);
181
182         icon_pixmap = XCreateBitmapFromData(Display_p, Win,
183                         (char *) Disp_icon_bits, Disp_icon_width,
184                         Disp_icon_height);
185
186         /* we want the width to be exactly the width of a page. The height
187          * can vary because we scroll in that direction */
188         if (got_geometry) {
189                 Size_hints.flags = PSize | PMinSize | PMaxSize | PPosition;
190                 Size_hints.x = x;
191                 Size_hints.y = y;
192         }
193         else {
194                 Size_hints.flags = PSize | PMinSize | PMaxSize;
195         }
196         Size_hints.width = Width;
197         Size_hints.height = Height;
198         Size_hints.min_width = Width;
199         Size_hints.max_width = Width;
200         Size_hints.min_height = MINHEIGHT;
201         Size_hints.max_height = (display_height < MAXHEIGHT
202                                         ? display_height : MAXHEIGHT);
203
204         XSetStandardProperties(Display_p, Win, window_name, icon_name,
205                         icon_pixmap, Argv, Argc, &Size_hints);
206
207         XSelectInput(Display_p, Win, ExposureMask | KeyPressMask |
208                         KeyReleaseMask | ButtonPressMask | StructureNotifyMask);
209
210         load_font();
211         get_GC(Win);
212         XMapWindow(Display_p, Win);
213
214         /* determine what height we actually got */
215         if (XGetWindowAttributes(Display_p, Win, &attributes) != 0) {
216                 Conf_info_p->vlines = Height = attributes.height;
217         }
218                 
219         create_image(Width, Height);
220
221         /* it seems we need to wait for an exposure event,(or maybe it's
222          * really MapNotify) before proceeding, or else the "wait" message
223          * doesn't get displayed, so wait for it */
224         XWindowEvent(Display_p, Win, ExposureMask, &event);
225 }
226 \f
227
228 /* create XImage for the display. If one already existed, free it first (which
229  * would happen if window was re-sized */
230
231 static void
232 create_image(wid, height)
233
234 int wid;
235 int height;
236
237 {
238         char *databuff;
239
240
241         /* if already have one, free that and redo if different size */
242         if (Image_p != (XImage *) 0) {
243                 if (Image_p->width == wid && Image_p->height == height) {
244                         /* already have one of correct size */
245                         return;
246                 }
247                 else {
248                         XDestroyImage(Image_p);
249                 }
250         }
251
252         /* create buffer for display image */
253         if ((databuff = (char *) malloc(BYTES_PER_LINE * height)) == (char *) 0) {
254                 Exit_errmsg = "Could not allocate memory\n";
255                 ( *(Conf_info_p->cleanup) )  (1);
256         }
257
258         Image_p = XCreateImage(Display_p, DefaultVisual(Display_p, Xscreen),
259                         1, XYBitmap, 0, databuff, wid, height,
260                         8, BYTES_PER_LINE);
261         Image_p->bitmap_unit = 8;
262         Image_p->bitmap_bit_order = MSBFirst;
263 }
264 \f
265
266 /* Look up the color resource named. If found, return its value,
267  * otherwise return the default value. */
268
269 static unsigned long
270 get_color(resource_name, default_value)
271
272 char *resource_name;
273 unsigned long default_value;
274
275 {
276         char *value;
277         XColor color;
278
279         /* First try looking up in command line resource database. */
280         if ((value = get_cmd_resource(resource_name)) != (char *) 0) {
281                 if (color_ok(resource_name, value, &color)) {
282                         return(color.pixel);
283                 }
284         }
285
286         /* failing that, try looking in the default database */
287         if ((value = XGetDefault(Display_p, Mupdisp, resource_name)) != 0) {
288                 if (color_ok(resource_name, value, &color)) {
289                         return(color.pixel);
290                 }
291         }
292
293         return(default_value);
294 }
295 \f
296
297 /* look up a value from the command line database. Return it if found, else 0 */
298 static char *
299 get_cmd_resource(resource_name)
300
301 char *resource_name;
302
303 {
304         XrmValue rm_value;
305         char *str_type[20];
306         char res_name[100];
307         char class_name[100];
308         int offset;
309
310
311         /* create the resource and class names. Class name has initial caps */
312         sprintf(res_name, "%s.%s", Mupdisp, resource_name);
313         sprintf(class_name, "%s.%s", Mupdisp, resource_name);
314         class_name[0] = toupper(class_name[0]);
315         offset = strlen(Mupdisp) + 1;
316         class_name[offset] = toupper(class_name[offset]);
317
318         /* look it up in command line resource database. */
319         if (XrmGetResource(Resource_db, res_name,
320                         class_name, str_type, &rm_value) == True) {
321                 return ((char *) rm_value.addr);
322         }
323         return((char *) 0);
324 }
325
326 /* Parse a color name and allocate it. If all goes well, fill in the
327  * XColor and return 1. If something goes wrong, return 0. */
328
329 static int
330 color_ok(resource_name, value, color_p)
331
332 char *resource_name;
333 char *value;
334 XColor *color_p;
335
336 {
337         if (XParseColor(Display_p,
338                         DefaultColormapOfScreen(
339                         DefaultScreenOfDisplay(Display_p)),
340                         value, color_p) != 0) {
341                 if (XAllocColor(Display_p,
342                                 DefaultColormapOfScreen(
343                                 DefaultScreenOfDisplay(Display_p)),
344                                 color_p) != 0) {
345                         return (1);
346                 }
347         }
348         else {
349                 fprintf(stderr, "invalid %s color: %s\n",
350                                 resource_name, value);
351         }
352         return(0);
353 }
354 \f
355
356 /* get input in X windows mode. Handle all the events, including mouse,
357  * resizing and exposure */
358  
359 void
360 xterm_user_interf()
361
362 {
363         XEvent report;          /* what X event happened */
364         char inpbuff[IBUFSIZ];
365         KeySym keysym;
366         XComposeStatus compose;
367         int window_size = OK;
368         static int control = 0; /* non-zero if control key is pressed */
369
370
371         while (1) {
372
373                 /* get an event and take appropriate action */
374                 XNextEvent(Display_p, &report);
375
376                 switch(report.type) {
377
378                 case Expose:
379                         /* repaint screen */
380                         while(XCheckTypedEvent(Display_p, Expose, &report))
381                                 ;
382                         if (window_size == SMALL) {
383                                 TooSmall(Win);
384                         }
385                         else {
386                                 do_cmd('r');
387                         }
388                         break;
389
390                 case ConfigureNotify:
391                         /* set up image of proper size */
392                         Width = report.xconfigure.width;
393                         Height = report.xconfigure.height;
394                         if ((Width < Size_hints.min_width) ||
395                                         (Height < Size_hints.min_height)) {
396                                 window_size = SMALL;
397                         }
398                         else {
399                                 window_size = OK;
400                         }
401                         create_image(Width, Height);
402                         Conf_info_p->vlines = Height;
403                         break;
404
405                 case ButtonPress:
406                         /* mouse. left is forward, right is backward scroll */
407                         if (report.xbutton.button == 1) {
408                                 do_cmd('f');
409                         }
410                         else if (report.xbutton.button == 3) {
411                                 do_cmd('b');
412                         }
413                         break;
414
415                 case KeyPress:
416                         /* keyboard input. Do appropriate command */
417                         XLookupString(&(report.xkey), inpbuff, IBUFSIZ, &keysym,
418                                                 &compose);
419                         /* the linux version of isascii claims
420                          * that isascii(0xff0d) is true! So I added the
421                          * extra check for < 256 (which isascii should already
422                          * be checking) */
423                         if (keysym < 256 && isascii(keysym)) {
424                                 if (control != 0) {
425                                         do_cmd(keysym & 0x1f);
426                                 }
427                                 else {
428                                         do_cmd(keysym);
429                                 }
430                         }
431                         else if (keysym == XK_Return) {
432                                 do_cmd('\n');
433                         }
434                         else if (keysym == XK_BackSpace) {
435                                 do_cmd('\b');
436                         }
437                         else if (keysym == XK_Up) {
438                                 /* use up key as synonym for scrolling back */
439                                 do_cmd('b');
440                         }
441                         else if (keysym == XK_Down) {
442                                 /* use down key as synonym for scrolling forward */
443                                 do_cmd('f');
444                         }
445                         else if (keysym == XK_Prior) {
446                                 /* use page up key as synonym for previous page */
447                                 do_cmd('p');
448                         }
449                         else if (keysym == XK_Next) {
450                                 /* use page down key as synonym for next page */
451                                 do_cmd('n');
452                         }
453                         else if (keysym == XK_Control_L || keysym == XK_Control_R) {
454                                 control++;
455                         }
456                         break;
457
458                 case KeyRelease:
459                         /* just check for control key release */
460                         XLookupString(&(report.xkey), inpbuff, IBUFSIZ, &keysym,
461                                                 &compose);
462                         if (keysym == XK_Control_L || keysym == XK_Control_R) {
463                                 if (control > 0) {
464                                         control--;
465                                 }
466                         }
467                         break;
468
469                 default:
470                         break;
471                 }
472         }
473 }
474 \f
475
476 /* create graphics context. Basically copy the example in the X book */
477
478 static void
479 get_GC(win)
480
481 Window win;
482
483 {
484         unsigned long valuemask = 0;
485         XGCValues values;
486         unsigned int line_width = 6;
487         int line_style = LineOnOffDash;
488         int cap_style = CapRound;
489         int join_style = JoinRound;
490         int dash_offset = 0;
491         static char dash_list[] = { 12, 24 };
492         int list_length = 2;
493
494         gc = XCreateGC(Display_p, Win, valuemask, &values);
495         XSetFont(Display_p, gc, Font_info_p->fid);
496         XSetForeground(Display_p, gc, Foreground);
497         XSetBackground(Display_p, gc, Background);
498         XSetLineAttributes(Display_p, gc, line_width, line_style, cap_style,
499                         join_style);
500         XSetDashes(Display_p, gc, dash_offset, dash_list, list_length);
501 }
502 \f
503
504 /* load a font. Copy example in X book */
505
506 static void
507 load_font()
508
509 {
510         char *fontname = "9x15";
511
512         if ((Font_info_p = XLoadQueryFont(Display_p, fontname)) == NULL) {
513                 fprintf(stderr, "can't open 9x15 font\n");
514                 generalcleanup(1);
515         }
516 }
517
518
519 static void
520 TooSmall(win)
521
522 Window win;
523
524 {
525         char *string1 = "Too small";
526
527         XDrawString(Display_p, win, gc, 2, Font_info_p->max_bounds.ascent + 2,
528                         string1, strlen(string1));
529         Exit_errmsg = "Window too small\n";
530         ( *(Conf_info_p->cleanup) )  (1);
531 }
532 \f
533
534 /* X cleanup function */
535
536 void
537 xterm_cleanup(status)
538
539 int status;
540
541 {
542         /* free all X resources */
543         XUnloadFont(Display_p, Font_info_p->fid);
544         XFreeGC(Display_p, gc);
545         XCloseDisplay(Display_p);
546
547         /* call non-terminal-type specific cleanup */
548         generalcleanup(status);
549 }
550 \f
551
552 /* draw screen in X mode */
553
554 void
555 xterm_draw(line, small)
556
557 int line;       /* start drawing at this raster line */
558 int small;      /* if YES, use small, full-page mode */
559
560 {
561         register int i;
562         long offset;                    /* offset into file where page begins */
563         int fd;                         /* file descriptor of bitmap file */
564
565
566         /* make sure we have a valid page */
567         if (Currpage_p == (struct Pginfo *) 0) {
568                 ( *(Conf_info_p->error) ) ("page # out of range");
569                 return;
570         }
571
572         /* find data in bitmap file */
573         offset = Currpage_p->seqnum * BYTES_PER_PAGE;
574         fd = gen1file(small);
575         lseek(fd, offset + line * BYTES_PER_LINE, SEEK_SET);
576
577         /* copy into memory and display it */
578         for (i = 0; i < Conf_info_p->vlines; i++) {
579                 read(fd, Image_p->data + i * BYTES_PER_LINE, BYTES_PER_LINE);
580         }
581         XPutImage(Display_p, Win, gc, Image_p, 0, 0, 0, 0, Width, Height);
582         XFlush(Display_p);
583 }
584 \f
585
586
587 /* Error handler. For now just beep. Maybe eventually pop up an error message */
588
589 void
590 xterm_error(msg)
591
592 char *msg;
593
594 {
595         putc('\7', stderr);
596 }
597 \f
598
599 /* draw a raster bitmap, centered on the window */
600
601 void
602 xterm_raster(bitmap, width, height)
603
604 unsigned char *bitmap;  /* what to display */
605 int width, height;      /* of bitmap, width is in bytes */
606
607 {
608         register int b;
609         int x, y;       /* upper left corner of where to put bitmap, x in bytes */
610         XImage *bm_image;
611         char *bmap;
612
613
614         /* figure out how to center on screen */
615         x = (BYTES_PER_LINE - width) / 2;
616         y = (Conf_info_p->vlines - height) / 2;
617
618         /* get space to image, copy, inverting to white on black. Display,
619          * then release the storage */
620         if ((bmap = (char *) malloc(width*height)) == (char *) 0) {
621                 Exit_errmsg = "Could not allocate memory\n";
622                 ( *(Conf_info_p->cleanup) )  (1);
623         }
624         for (b = width * height - 1; b >= 0; b--) {
625                 bmap[b] = bitmap[b] ^ 0xff;
626         }
627         bm_image = XCreateImage(Display_p, DefaultVisual(Display_p, Xscreen),
628                         1, XYBitmap, 0, bmap, width * 8, height,
629                         8, width);
630         bm_image->bitmap_unit = 8;
631         bm_image->bitmap_bit_order = MSBFirst;
632         XPutImage(Display_p, Win, gc, bm_image, 0, 0, x * 8, y, width * 8, height);
633         XDestroyImage(bm_image);
634         XFlush(Display_p);
635 }
636
637 #else
638
639 /* some compilers complain about files that are effectively empty,
640  * so put in something even when entire file is effectively ifdef-ed out */
641 static short dummy;
642
643 #endif
644