chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mupdisp / xterm.c
CommitLineData
fac14bbe
MW
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
21static 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
44extern unsigned char Waitmsg_bitmap[]; /* message to tell user to wait */
45extern int Waitmsg_width, Waitmsg_height;
46
47/* define X window screen and display things */
48static Display *Display_p;
49static int Xscreen;
50static XImage *Image_p;
51static XFontStruct *Font_info_p;
52static GC gc; /* X graphics context */
53static Window Win;
54static unsigned int Width, Height;
55static XSizeHints Size_hints; /* tell window manager what size we want */
56static unsigned long Foreground; /* color */
57static unsigned long Background; /* color */
58static char Mupdisp[] = "mupdisp";
59
60/* X resource manager things */
61static 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};
68static int Opt_table_size = sizeof(Option_table) / sizeof (XrmOptionDescRec);
69static 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";
75XrmDatabase Resource_db;
76
77
78/* local functions */
79static char * get_cmd_resource P((char *resource_name));
80static unsigned long get_color P((char *resource_name,
81 unsigned long default_value));
82static int color_ok P((char *resource_name, char *value, XColor *color_p));
83static void create_image P((int wid, int height));
84static void get_GC P((Window win));
85static void load_font P((void));
86static void TooSmall P((Window win));
87\f
88
89/* parse any X-specific aguments */
90
91void
92parse_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
106void
107xterm_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
231static void
232create_image(wid, height)
233
234int wid;
235int 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
269static unsigned long
270get_color(resource_name, default_value)
271
272char *resource_name;
273unsigned 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 */
298static char *
299get_cmd_resource(resource_name)
300
301char *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
329static int
330color_ok(resource_name, value, color_p)
331
332char *resource_name;
333char *value;
334XColor *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
359void
360xterm_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
478static void
479get_GC(win)
480
481Window 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
506static void
507load_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
519static void
520TooSmall(win)
521
522Window 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
536void
537xterm_cleanup(status)
538
539int 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
554void
555xterm_draw(line, small)
556
557int line; /* start drawing at this raster line */
558int 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
589void
590xterm_error(msg)
591
592char *msg;
593
594{
595 putc('\7', stderr);
596}
597\f
598
599/* draw a raster bitmap, centered on the window */
600
601void
602xterm_raster(bitmap, width, height)
603
604unsigned char *bitmap; /* what to display */
605int 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 */
641static short dummy;
642
643#endif
644