xref: /plan9/sys/src/cmd/gs/src/gdevxini.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1989, 2000 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: gdevxini.c,v 1.14 2004/05/26 04:10:58 dan Exp $ */
18 /* X Windows driver initialization/finalization */
19 #include "memory_.h"
20 #include "x_.h"
21 #include "gx.h"
22 #include "gserrors.h"
23 #include "gxdevice.h"
24 #include "gxdevmem.h"
25 #include "gsparamx.h"
26 #include "gdevbbox.h"
27 #include "gdevx.h"
28 
29 extern char *getenv(const char *);
30 
31 extern const gx_device_bbox gs_bbox_device;
32 extern const gx_device_X gs_x11_device;
33 
34 extern const gx_device_bbox_procs_t gdev_x_box_procs;
35 
36 /* Define constants for orientation from ghostview */
37 /* Number represents clockwise rotation of the paper in degrees */
38 typedef enum {
39     Portrait = 0,		/* Normal portrait orientation */
40     Landscape = 90,		/* Normal landscape orientation */
41     Upsidedown = 180,		/* Don't think this will be used much */
42     Seascape = 270		/* Landscape rotated the wrong way */
43 } orientation;
44 
45 /* GC descriptors */
46 private_st_x11fontmap();
47 
48 /* ---------------- Opening/initialization ---------------- */
49 
50 /* Forward references */
51 private void gdev_x_setup_fontmap(gx_device_X *);
52 
53 /* Catch the alloc error when there is not enough resources for the
54  * backing pixmap.  Automatically shut off backing pixmap and let the
55  * user know when this happens.
56  *
57  * Because the X API was designed without adequate thought to reentrancy,
58  * these variables must be allocated statically.  We do not see how this
59  * code can work reliably in the presence of multi-threading.
60  */
61 private struct xv_ {
62     Boolean alloc_error;
63     XErrorHandler orighandler;
64     XErrorHandler oldhandler;
65 } x_error_handler;
66 
67 private int
x_catch_alloc(Display * dpy,XErrorEvent * err)68 x_catch_alloc(Display * dpy, XErrorEvent * err)
69 {
70     if (err->error_code == BadAlloc)
71 	x_error_handler.alloc_error = True;
72     if (x_error_handler.alloc_error)
73 	return 0;
74     return x_error_handler.oldhandler(dpy, err);
75 }
76 
77 int
x_catch_free_colors(Display * dpy,XErrorEvent * err)78 x_catch_free_colors(Display * dpy, XErrorEvent * err)
79 {
80     if (err->request_code == X_FreeColors)
81 	return 0;
82     return x_error_handler.orighandler(dpy, err);
83 }
84 
85 /* Open the X device */
86 int
gdev_x_open(gx_device_X * xdev)87 gdev_x_open(gx_device_X * xdev)
88 {
89     XSizeHints sizehints;
90     char *window_id;
91     XEvent event;
92     XVisualInfo xvinfo;
93     int nitems;
94     XtAppContext app_con;
95     Widget toplevel;
96     Display *dpy;
97     XColor xc;
98     int zero = 0;
99     int xid_height, xid_width;
100     int code;
101 
102 #ifdef DEBUG
103 # ifdef have_Xdebug
104     if (gs_debug['X']) {
105 	extern int _Xdebug;
106 
107 	_Xdebug = 1;
108     }
109 # endif
110 #endif
111     if (!(xdev->dpy = XOpenDisplay((char *)NULL))) {
112 	char *dispname = getenv("DISPLAY");
113 
114 	eprintf1("Cannot open X display `%s'.\n",
115 		 (dispname == NULL ? "(null)" : dispname));
116 	return_error(gs_error_ioerror);
117     }
118     xdev->dest = 0;
119     if ((window_id = getenv("GHOSTVIEW"))) {
120 	if (!(xdev->ghostview = sscanf(window_id, "%ld %ld",
121 				       &(xdev->win), &(xdev->dest)))) {
122 	    eprintf("Cannot get Window ID from ghostview.\n");
123 	    return_error(gs_error_ioerror);
124 	}
125     }
126     if (xdev->pwin != (Window) None) {	/* pick up the destination window parameters if specified */
127 	XWindowAttributes attrib;
128 
129 	xdev->win = xdev->pwin;
130 	if (XGetWindowAttributes(xdev->dpy, xdev->win, &attrib)) {
131 	    xdev->scr = attrib.screen;
132 	    xvinfo.visual = attrib.visual;
133 	    xdev->cmap = attrib.colormap;
134 	    xid_width = attrib.width;
135 	    xid_height = attrib.height;
136 	} else {
137 	    /* No idea why we can't get the attributes, but */
138 	    /* we shouldn't let it lead to a failure below. */
139 	    xid_width = xid_height = 0;
140 	}
141     } else if (xdev->ghostview) {
142 	XWindowAttributes attrib;
143 	Atom type;
144 	int format;
145 	unsigned long nitems, bytes_after;
146 	char *buf;
147 	Atom ghostview_atom = XInternAtom(xdev->dpy, "GHOSTVIEW", False);
148 
149 	if (XGetWindowAttributes(xdev->dpy, xdev->win, &attrib)) {
150 	    xdev->scr = attrib.screen;
151 	    xvinfo.visual = attrib.visual;
152 	    xdev->cmap = attrib.colormap;
153 	    xdev->width = attrib.width;
154 	    xdev->height = attrib.height;
155 	}
156 	/* Delete property if explicit dest is given */
157 	if (XGetWindowProperty(xdev->dpy, xdev->win, ghostview_atom, 0,
158 			       256, (xdev->dest != 0), XA_STRING,
159 			       &type, &format, &nitems, &bytes_after,
160 			       (unsigned char **)&buf) == 0 &&
161 	    type == XA_STRING) {
162 	    int llx, lly, urx, ury;
163 	    int left_margin = 0, bottom_margin = 0;
164 	    int right_margin = 0, top_margin = 0;
165 
166 	    /* We declare page_orientation as an int so that we can */
167 	    /* use an int * to reference it for sscanf; compilers */
168 	    /* might be tempted to use less space to hold it if */
169 	    /* it were declared as an orientation. */
170 	    int /*orientation */ page_orientation;
171 	    float xppp, yppp;	/* pixels per point */
172 
173 	    nitems = sscanf(buf,
174 			    "%ld %d %d %d %d %d %f %f %d %d %d %d",
175 			    &(xdev->bpixmap), &page_orientation,
176 			    &llx, &lly, &urx, &ury,
177 			    &(xdev->x_pixels_per_inch),
178 			    &(xdev->y_pixels_per_inch),
179 			    &left_margin, &bottom_margin,
180 			    &right_margin, &top_margin);
181 	    if (!(nitems == 8 || nitems == 12)) {
182 		eprintf("Cannot get ghostview property.\n");
183 		return_error(gs_error_ioerror);
184 	    }
185 	    if (xdev->dest && xdev->bpixmap) {
186 		eprintf("Both destination and backing pixmap specified.\n");
187 		return_error(gs_error_rangecheck);
188 	    }
189 	    if (xdev->dest) {
190 		Window root;
191 		int x, y;
192 		unsigned int width, height;
193 		unsigned int border_width, depth;
194 
195 		if (XGetGeometry(xdev->dpy, xdev->dest, &root, &x, &y,
196 				 &width, &height, &border_width, &depth)) {
197 		    xdev->width = width;
198 		    xdev->height = height;
199 		}
200 	    }
201 	    xppp = xdev->x_pixels_per_inch / 72.0;
202 	    yppp = xdev->y_pixels_per_inch / 72.0;
203 	    switch (page_orientation) {
204 		case Portrait:
205 		    xdev->initial_matrix.xx = xppp;
206 		    xdev->initial_matrix.xy = 0.0;
207 		    xdev->initial_matrix.yx = 0.0;
208 		    xdev->initial_matrix.yy = -yppp;
209 		    xdev->initial_matrix.tx = -llx * xppp;
210 		    xdev->initial_matrix.ty = ury * yppp;
211 		    break;
212 		case Landscape:
213 		    xdev->initial_matrix.xx = 0.0;
214 		    xdev->initial_matrix.xy = yppp;
215 		    xdev->initial_matrix.yx = xppp;
216 		    xdev->initial_matrix.yy = 0.0;
217 		    xdev->initial_matrix.tx = -lly * xppp;
218 		    xdev->initial_matrix.ty = -llx * yppp;
219 		    break;
220 		case Upsidedown:
221 		    xdev->initial_matrix.xx = -xppp;
222 		    xdev->initial_matrix.xy = 0.0;
223 		    xdev->initial_matrix.yx = 0.0;
224 		    xdev->initial_matrix.yy = yppp;
225 		    xdev->initial_matrix.tx = urx * xppp;
226 		    xdev->initial_matrix.ty = -lly * yppp;
227 		    break;
228 		case Seascape:
229 		    xdev->initial_matrix.xx = 0.0;
230 		    xdev->initial_matrix.xy = -yppp;
231 		    xdev->initial_matrix.yx = -xppp;
232 		    xdev->initial_matrix.yy = 0.0;
233 		    xdev->initial_matrix.tx = ury * xppp;
234 		    xdev->initial_matrix.ty = urx * yppp;
235 		    break;
236 	    }
237 
238 	    /* The following sets the imageable area according to the */
239 	    /* bounding box and margins sent by ghostview.            */
240 	    /* This code has been patched many times; its current state */
241 	    /* is per a recommendation by Tim Theisen on 4/28/95. */
242 
243 	    xdev->ImagingBBox[0] = llx - left_margin;
244 	    xdev->ImagingBBox[1] = lly - bottom_margin;
245 	    xdev->ImagingBBox[2] = urx + right_margin;
246 	    xdev->ImagingBBox[3] = ury + top_margin;
247 	    xdev->ImagingBBox_set = true;
248 
249 	} else if (xdev->pwin == (Window) None) {
250 	    eprintf("Cannot get ghostview property.\n");
251 	    return_error(gs_error_ioerror);
252 	}
253     } else {
254 	Screen *scr = DefaultScreenOfDisplay(xdev->dpy);
255 
256 	xdev->scr = scr;
257 	xvinfo.visual = DefaultVisualOfScreen(scr);
258 	xdev->cmap = DefaultColormapOfScreen(scr);
259 	if (xvinfo.visual->class != TrueColor) {
260 	    int scrno = DefaultScreen(xdev->dpy);
261 	    if ( XMatchVisualInfo(xdev->dpy, scrno, 24, TrueColor, &xvinfo) ||
262 		 XMatchVisualInfo(xdev->dpy, scrno, 32, TrueColor, &xvinfo) ||
263 		 XMatchVisualInfo(xdev->dpy, scrno, 16, TrueColor, &xvinfo) ||
264 		 XMatchVisualInfo(xdev->dpy, scrno, 15, TrueColor, &xvinfo)  ) {
265 		xdev->cmap = XCreateColormap (xdev->dpy,
266 					      DefaultRootWindow(xdev->dpy),
267 					      xvinfo.visual, AllocNone );
268 	    }
269 	}
270     }
271     xvinfo.visualid = XVisualIDFromVisual(xvinfo.visual);
272     xdev->vinfo = XGetVisualInfo(xdev->dpy, VisualIDMask, &xvinfo, &nitems);
273     if (xdev->vinfo == NULL) {
274 	eprintf("Cannot get XVisualInfo.\n");
275 	return_error(gs_error_ioerror);
276     }
277     /* Buggy X servers may cause a Bad Access on XFreeColors. */
278     x_error_handler.orighandler = XSetErrorHandler(x_catch_free_colors);
279 
280     /* Get X Resources.  Use the toolkit for this. */
281     XtToolkitInitialize();
282     app_con = XtCreateApplicationContext();
283     XtAppSetFallbackResources(app_con, gdev_x_fallback_resources);
284     dpy = XtOpenDisplay(app_con, NULL, "ghostscript", "Ghostscript",
285 			NULL, 0, &zero, NULL);
286     toplevel = XtAppCreateShell(NULL, "Ghostscript",
287 				applicationShellWidgetClass, dpy, NULL, 0);
288     XtGetApplicationResources(toplevel, (XtPointer) xdev,
289 			      gdev_x_resources, gdev_x_resource_count,
290 			      NULL, 0);
291 
292     /* Reserve foreground and background colors under the regular connection. */
293     xc.pixel = xdev->foreground;
294     XQueryColor(xdev->dpy, DefaultColormap(xdev->dpy,DefaultScreen(xdev->dpy)), &xc);
295     XAllocColor(xdev->dpy, xdev->cmap, &xc);
296     xdev->foreground = xc.pixel;
297     xc.pixel = xdev->background;
298     XQueryColor(xdev->dpy, DefaultColormap(xdev->dpy,DefaultScreen(xdev->dpy)), &xc);
299     XAllocColor(xdev->dpy, xdev->cmap, &xc);
300     xdev->background = xc.pixel;
301 
302     code = gdev_x_setup_colors(xdev);
303     if (code < 0) {
304 	XCloseDisplay(xdev->dpy);
305 	return code;
306     }
307     /* Now that the color map is setup check if the device is separable. */
308     check_device_separable((gx_device *)xdev);
309 
310     gdev_x_setup_fontmap(xdev);
311 
312     if (!xdev->ghostview) {
313 	XWMHints wm_hints;
314 	XSetWindowAttributes xswa;
315 	gx_device *dev = (gx_device *) xdev;
316 
317 	/* Take care of resolution and paper size. */
318 	if (xdev->x_pixels_per_inch == FAKE_RES ||
319 	    xdev->y_pixels_per_inch == FAKE_RES) {
320 	    float xsize = (float)xdev->width / xdev->x_pixels_per_inch;
321 	    float ysize = (float)xdev->height / xdev->y_pixels_per_inch;
322 
323 	    if (xdev->xResolution == 0.0 && xdev->yResolution == 0.0) {
324 		float dpi, xdpi, ydpi;
325 
326 		xdpi = 25.4 * WidthOfScreen(xdev->scr) /
327 		    WidthMMOfScreen(xdev->scr);
328 		ydpi = 25.4 * HeightOfScreen(xdev->scr) /
329 		    HeightMMOfScreen(xdev->scr);
330 		dpi = min(xdpi, ydpi);
331 		/*
332 		 * Some X servers provide very large "virtual screens", and
333 		 * return the virtual screen size for Width/HeightMM but the
334 		 * physical size for Width/Height.  Attempt to detect and
335 		 * correct for this now.  This is a KLUDGE required because
336 		 * the X server provides no way to read the screen
337 		 * resolution directly.
338 		 */
339 		if (dpi < 30)
340 		    dpi = 75;	/* arbitrary */
341 		else {
342 		    while (xsize * dpi > WidthOfScreen(xdev->scr) - 32 ||
343 			   ysize * dpi > HeightOfScreen(xdev->scr) - 32)
344 			dpi *= 0.95;
345 		}
346 		xdev->x_pixels_per_inch = dpi;
347 		xdev->y_pixels_per_inch = dpi;
348 	    } else {
349 		xdev->x_pixels_per_inch = xdev->xResolution;
350 		xdev->y_pixels_per_inch = xdev->yResolution;
351 	    }
352 	    if (xdev->width > WidthOfScreen(xdev->scr)) {
353 		xdev->width = xsize * xdev->x_pixels_per_inch;
354 	    }
355 	    if (xdev->height > HeightOfScreen(xdev->scr)) {
356 		xdev->height = ysize * xdev->y_pixels_per_inch;
357 	    }
358 	    xdev->MediaSize[0] =
359 		(float)xdev->width / xdev->x_pixels_per_inch * 72;
360 	    xdev->MediaSize[1] =
361 		(float)xdev->height / xdev->y_pixels_per_inch * 72;
362 	}
363 	sizehints.x = 0;
364 	sizehints.y = 0;
365 	sizehints.width = xdev->width;
366 	sizehints.height = xdev->height;
367 	sizehints.flags = 0;
368 
369 	if (xdev->geometry != NULL) {
370 	    /*
371 	     * Note that border_width must be set first.  We can't use
372 	     * scr, because that is a Screen*, and XWMGeometry wants
373 	     * the screen number.
374 	     */
375 	    char gstr[40];
376 	    int bitmask;
377 
378 	    sprintf(gstr, "%dx%d+%d+%d", sizehints.width,
379 		    sizehints.height, sizehints.x, sizehints.y);
380 	    bitmask = XWMGeometry(xdev->dpy, DefaultScreen(xdev->dpy),
381 				  xdev->geometry, gstr, xdev->borderWidth,
382 				  &sizehints,
383 				  &sizehints.x, &sizehints.y,
384 				  &sizehints.width, &sizehints.height,
385 				  &sizehints.win_gravity);
386 
387 	    if (bitmask & (XValue | YValue))
388 		sizehints.flags |= USPosition;
389 	}
390 	gx_default_get_initial_matrix(dev, &(xdev->initial_matrix));
391 
392 	if (xdev->pwin != (Window) None && xid_width != 0 && xid_height != 0) {
393 #if 0				/*************** */
394 
395 	    /*
396 	     * The user who originally implemented the WindowID feature
397 	     * provided the following code to scale the displayed output
398 	     * to fit in the window.  We think this is a bad idea,
399 	     * since it doesn't track window resizing and is generally
400 	     * completely at odds with the way Ghostscript treats
401 	     * window or paper size in all other contexts.  We are
402 	     * leaving the code here in case someone decides that
403 	     * this really is the behavior they want.
404 	     */
405 
406 	    /* Scale to fit in the window. */
407 	    xdev->initial_matrix.xx
408 		= xdev->initial_matrix.xx *
409 		(float)xid_width / (float)xdev->width;
410 	    xdev->initial_matrix.yy
411 		= xdev->initial_matrix.yy *
412 		(float)xid_height / (float)xdev->height;
413 
414 #endif /*************** */
415 	    xdev->width = xid_width;
416 	    xdev->height = xid_height;
417 	    xdev->initial_matrix.ty = xdev->height;
418 	} else {		/* !xdev->pwin */
419 	    xswa.event_mask = ExposureMask;
420 	    xswa.background_pixel = xdev->background;
421 	    xswa.border_pixel = xdev->borderColor;
422 	    xswa.colormap = xdev->cmap;
423 	    xdev->win = XCreateWindow(xdev->dpy, RootWindowOfScreen(xdev->scr),
424 				      sizehints.x, sizehints.y,		/* upper left */
425 				      xdev->width, xdev->height,
426 				      xdev->borderWidth,
427 				      xdev->vinfo->depth,
428 				      InputOutput,	/* class        */
429 				      xdev->vinfo->visual,	/* visual */
430 				      CWEventMask | CWBackPixel |
431 				      CWBorderPixel | CWColormap,
432 				      &xswa);
433 	    XStoreName(xdev->dpy, xdev->win, "ghostscript");
434 	    XSetWMNormalHints(xdev->dpy, xdev->win, &sizehints);
435 	    wm_hints.flags = InputHint;
436 	    wm_hints.input = False;
437 	    XSetWMHints(xdev->dpy, xdev->win, &wm_hints);	/* avoid input focus */
438 	}
439     }
440     /***
441      *** According to Ricard Torres (torres@upf.es), we have to wait until here
442      *** to close the toolkit connection, which we formerly did
443      *** just after the calls on XAllocColor above.  I suspect that
444      *** this will cause things to be left dangling if an error occurs
445      *** anywhere in the above code, but I'm willing to let users
446      *** fight over fixing it, since I have no idea what's right.
447      ***/
448 
449     /* And close the toolkit connection. */
450     XtDestroyWidget(toplevel);
451     XtCloseDisplay(dpy);
452     XtDestroyApplicationContext(app_con);
453 
454     xdev->ht.pixmap = (Pixmap) 0;
455     xdev->ht.id = gx_no_bitmap_id;;
456     xdev->fill_style = FillSolid;
457     xdev->function = GXcopy;
458     xdev->fid = (Font) 0;
459 
460     /* Set up a graphics context */
461     xdev->gc = XCreateGC(xdev->dpy, xdev->win, 0, (XGCValues *) NULL);
462     XSetFunction(xdev->dpy, xdev->gc, GXcopy);
463     XSetLineAttributes(xdev->dpy, xdev->gc, 0,
464 		       LineSolid, CapButt, JoinMiter);
465 
466     gdev_x_clear_window(xdev);
467 
468     if (!xdev->ghostview) {	/* Make the window appear. */
469 	XMapWindow(xdev->dpy, xdev->win);
470 
471 	/* Before anything else, do a flush and wait for */
472 	/* an exposure event. */
473 	XSync(xdev->dpy, False);
474 	if (xdev->pwin == (Window) None) {	/* there isn't a next event for existing windows */
475 	    XNextEvent(xdev->dpy, &event);
476 	}
477 	/* Now turn off graphics exposure events so they don't queue up */
478 	/* indefinitely.  Also, since we can't do anything about real */
479 	/* Expose events, mask them out. */
480 	XSetGraphicsExposures(xdev->dpy, xdev->gc, False);
481 	XSelectInput(xdev->dpy, xdev->win, NoEventMask);
482     } else {
483 	/* Create an unmapped window, that the window manager will ignore.
484 	 * This invisible window will be used to receive "next page"
485 	 * events from ghostview */
486 	XSetWindowAttributes attributes;
487 
488 	attributes.override_redirect = True;
489 	xdev->mwin = XCreateWindow(xdev->dpy, RootWindowOfScreen(xdev->scr),
490 				   0, 0, 1, 1, 0, CopyFromParent,
491 				   CopyFromParent, CopyFromParent,
492 				   CWOverrideRedirect, &attributes);
493 	xdev->NEXT = XInternAtom(xdev->dpy, "NEXT", False);
494 	xdev->PAGE = XInternAtom(xdev->dpy, "PAGE", False);
495 	xdev->DONE = XInternAtom(xdev->dpy, "DONE", False);
496     }
497 
498     xdev->ht.no_pixmap = XCreatePixmap(xdev->dpy, xdev->win, 1, 1,
499 				       xdev->vinfo->depth);
500 
501     return 0;
502 }
503 
504 /* Set up or take down buffering in a RAM image. */
505 private int
x_set_buffer(gx_device_X * xdev)506 x_set_buffer(gx_device_X * xdev)
507 {
508     /*
509      * We must use the stable memory here, since the existence of the
510      * buffer is independent of save/restore.
511      */
512     gs_memory_t *mem = gs_memory_stable(xdev->memory);
513     bool buffered = xdev->MaxBitmap > 0;
514     const gx_device_procs *procs;
515 
516  setup:
517     if (buffered) {
518 	/* We want to buffer. */
519 	/* Check that we can set up a memory device. */
520 	gx_device_memory *mdev = (gx_device_memory *)xdev->target;
521 
522 	if (mdev == 0 || mdev->color_info.depth != xdev->color_info.depth) {
523 	    const gx_device_memory *mdproto =
524 		gdev_mem_device_for_bits(xdev->color_info.depth);
525 
526 	    if (!mdproto) {
527 		buffered = false;
528 		goto setup;
529 	    }
530 	    if (mdev) {
531 		/* Update the pointer we're about to overwrite. */
532 		gx_device_set_target((gx_device_forward *)mdev, NULL);
533 	    } else {
534 		mdev = gs_alloc_struct(mem, gx_device_memory,
535 				       &st_device_memory, "memory device");
536 		if (mdev == 0) {
537 		    buffered = false;
538 		    goto setup;
539 		}
540 	    }
541 	    /*
542 	     * We want to forward image drawing to the memory device.
543 	     * That requires making the memory device forward its color
544 	     * mapping operations back to the X device, creating a circular
545 	     * pointer structure.  This is not a disaster, we just need to
546 	     * be aware that this is going on.
547 	     */
548 	    gs_make_mem_device(mdev, mdproto, mem, 0, (gx_device *)xdev);
549 	    gx_device_set_target((gx_device_forward *)xdev, (gx_device *)mdev);
550 	    xdev->is_buffered = true;
551 	}
552 	if (mdev->width != xdev->width || mdev->height != xdev->height) {
553 	    byte *buffer;
554 	    ulong space;
555 
556 	    space = gdev_mem_data_size(mdev, xdev->width, xdev->height);
557 	    if (space > xdev->MaxBitmap) {
558 		buffered = false;
559 		goto setup;
560 	    }
561 	    buffer =
562 		(xdev->buffer ?
563 		 (byte *)gs_resize_object(mem, xdev->buffer, space, "buffer") :
564 		 gs_alloc_bytes(mem, space, "buffer"));
565 	    if (!buffer) {
566 		buffered = false;
567 		goto setup;
568 	    }
569 	    xdev->buffer_size = space;
570 	    xdev->buffer = buffer;
571 	    mdev->width = xdev->width;
572 	    mdev->height = xdev->height;
573 	    mdev->color_info = xdev->color_info;
574 	    mdev->base = xdev->buffer;
575 	    gdev_mem_open_scan_lines(mdev, xdev->height);
576 	}
577 	xdev->white = gx_device_white((gx_device *)xdev);
578 	xdev->black = gx_device_black((gx_device *)xdev);
579 	procs = &gs_bbox_device.procs;
580     } else {
581 	/* Not buffering.  Release the buffer and memory device. */
582 	gs_free_object(mem, xdev->buffer, "buffer");
583 	xdev->buffer = 0;
584 	xdev->buffer_size = 0;
585 	if (!xdev->is_buffered)
586 	    return 0;
587 	gx_device_set_target((gx_device_forward *)xdev->target, NULL);
588 	gx_device_set_target((gx_device_forward *)xdev, NULL);
589 	xdev->is_buffered = false;
590 	procs = &gs_x11_device.procs;
591     }
592     if (dev_proc(xdev, fill_rectangle) != procs->fill_rectangle) {
593 #define COPY_PROC(p) set_dev_proc(xdev, p, procs->p)
594 	COPY_PROC(fill_rectangle);
595 	COPY_PROC(copy_mono);
596 	COPY_PROC(copy_color);
597 	COPY_PROC(copy_alpha);
598 	COPY_PROC(fill_path);
599 	COPY_PROC(stroke_path);
600 	COPY_PROC(fill_mask);
601 	COPY_PROC(fill_trapezoid);
602 	COPY_PROC(fill_parallelogram);
603 	COPY_PROC(fill_triangle);
604 	COPY_PROC(draw_thin_line);
605 	COPY_PROC(strip_tile_rectangle);
606 	COPY_PROC(strip_copy_rop);
607 	COPY_PROC(begin_typed_image);
608 	COPY_PROC(create_compositor);
609 	COPY_PROC(text_begin);
610 #undef COPY_PROC
611 	if (xdev->is_buffered) {
612     	    check_device_separable((gx_device *)xdev);
613 	    gx_device_forward_fill_in_procs((gx_device_forward *)xdev);
614 	    xdev->box_procs = gdev_x_box_procs;
615 	    xdev->box_proc_data = xdev;
616 	} else {
617     	    check_device_separable((gx_device *)xdev);
618 	    gx_device_fill_in_procs((gx_device *)xdev);
619 	}
620     }
621     return 0;
622 }
623 
624 /* Allocate the backing pixmap, if any, and clear the window. */
625 void
gdev_x_clear_window(gx_device_X * xdev)626 gdev_x_clear_window(gx_device_X * xdev)
627 {
628     if (!xdev->ghostview) {
629 	if (xdev->useBackingPixmap) {
630 	    if (xdev->bpixmap == 0) {
631 		x_error_handler.oldhandler = XSetErrorHandler(x_catch_alloc);
632 		x_error_handler.alloc_error = False;
633 		xdev->bpixmap =
634 		    XCreatePixmap(xdev->dpy, xdev->win,
635 				  xdev->width, xdev->height,
636 				  xdev->vinfo->depth);
637 		XSync(xdev->dpy, False);	/* Force the error */
638 		if (x_error_handler.alloc_error) {
639 		    xdev->useBackingPixmap = False;
640 #ifdef DEBUG
641 		    eprintf("Warning: Failed to allocated backing pixmap.\n");
642 #endif
643 		    if (xdev->bpixmap) {
644 			XFreePixmap(xdev->dpy, xdev->bpixmap);
645 			xdev->bpixmap = None;
646 			XSync(xdev->dpy, False);	/* Force the error */
647 		    }
648 		}
649 		x_error_handler.oldhandler =
650 		    XSetErrorHandler(x_error_handler.oldhandler);
651 	    }
652 	} else {
653 	    if (xdev->bpixmap != 0) {
654 		XFreePixmap(xdev->dpy, xdev->bpixmap);
655 		xdev->bpixmap = (Pixmap) 0;
656 	    }
657 	}
658     }
659     x_set_buffer(xdev);
660     /* Clear the destination pixmap to avoid initializing with garbage. */
661     if (xdev->dest == (Pixmap) 0) {
662 	xdev->dest = (xdev->bpixmap != (Pixmap) 0 ?
663 		  xdev->bpixmap : (Pixmap) xdev->win);
664     }
665     if (xdev->dest != (Pixmap) 0) {
666 	XSetForeground(xdev->dpy, xdev->gc, xdev->background);
667 	XFillRectangle(xdev->dpy, xdev->dest, xdev->gc,
668 		       0, 0, xdev->width, xdev->height);
669     }
670 
671     /* Clear the background pixmap to avoid initializing with garbage. */
672     if (xdev->bpixmap != (Pixmap) 0) {
673 	if (!xdev->ghostview)
674 	    XSetWindowBackgroundPixmap(xdev->dpy, xdev->win, xdev->bpixmap);
675 	XSetForeground(xdev->dpy, xdev->gc, xdev->background);
676 	XFillRectangle(xdev->dpy, xdev->bpixmap, xdev->gc,
677 		       0, 0, xdev->width, xdev->height);
678     }
679     /* Initialize foreground and background colors */
680     xdev->back_color = xdev->background;
681     XSetBackground(xdev->dpy, xdev->gc, xdev->background);
682     xdev->fore_color = xdev->background;
683     XSetForeground(xdev->dpy, xdev->gc, xdev->background);
684     xdev->colors_or = xdev->colors_and = xdev->background;
685 }
686 
687 /* ------ Initialize font mapping ------ */
688 
689 /* Extract the PostScript font name from the font map resource. */
690 private const char *
get_ps_name(const char ** cpp,int * len)691 get_ps_name(const char **cpp, int *len)
692 {
693     const char *ret;
694 
695     *len = 0;
696     /* skip over whitespace and newlines */
697     while (**cpp == ' ' || **cpp == '\t' || **cpp == '\n') {
698 	(*cpp)++;
699     }
700     /* return font name up to ":", whitespace, or end of string */
701     if (**cpp == ':' || **cpp == '\0') {
702 	return NULL;
703     }
704     ret = *cpp;
705     while (**cpp != ':' &&
706 	   **cpp != ' ' && **cpp != '\t' && **cpp != '\n' &&
707 	   **cpp != '\0') {
708 	(*cpp)++;
709 	(*len)++;
710     }
711     return ret;
712 }
713 
714 /* Extract the X11 font name from the font map resource. */
715 private const char *
get_x11_name(const char ** cpp,int * len)716 get_x11_name(const char **cpp, int *len)
717 {
718     const char *ret;
719     int dashes = 0;
720 
721     *len = 0;
722     /* skip over whitespace and the colon */
723     while (**cpp == ' ' || **cpp == '\t' ||
724 	   **cpp == ':') {
725 	(*cpp)++;
726     }
727     /* return font name up to end of line or string */
728     if (**cpp == '\0' || **cpp == '\n') {
729 	return NULL;
730     }
731     ret = *cpp;
732     while (dashes != 7 &&
733 	   **cpp != '\0' && **cpp != '\n') {
734 	if (**cpp == '-')
735 	    dashes++;
736 	(*cpp)++;
737 	(*len)++;
738     }
739     while (**cpp != '\0' && **cpp != '\n') {
740 	(*cpp)++;
741     }
742     if (dashes != 7)
743 	return NULL;
744     return ret;
745 }
746 
747 /* Scan one resource and build font map records. */
748 private void
scan_font_resource(const char * resource,x11fontmap ** pmaps,gs_memory_t * mem)749 scan_font_resource(const char *resource, x11fontmap **pmaps, gs_memory_t *mem)
750 {
751     const char *ps_name;
752     const char *x11_name;
753     int ps_name_len;
754     int x11_name_len;
755     x11fontmap *font;
756     const char *cp = resource;
757 
758     while ((ps_name = get_ps_name(&cp, &ps_name_len)) != 0) {
759 	x11_name = get_x11_name(&cp, &x11_name_len);
760 	if (x11_name) {
761 	    font = gs_alloc_struct(mem, x11fontmap, &st_x11fontmap,
762 				   "scan_font_resource(font)");
763 	    if (font == NULL)
764 		continue;
765 	    font->ps_name = (char *)
766 		gs_alloc_byte_array(mem, ps_name_len + 1, sizeof(char),
767 				    "scan_font_resource(ps_name)");
768 	    if (font->ps_name == NULL) {
769 		gs_free_object(mem, font, "scan_font_resource(font)");
770 		continue;
771 	    }
772 	    strncpy(font->ps_name, ps_name, ps_name_len);
773 	    font->ps_name[ps_name_len] = '\0';
774 	    font->x11_name = (char *)
775 		gs_alloc_byte_array(mem, x11_name_len, sizeof(char),
776 				    "scan_font_resource(x11_name)");
777 	    if (font->x11_name == NULL) {
778 		gs_free_object(mem, font->ps_name,
779 			       "scan_font_resource(ps_name)");
780 		gs_free_object(mem, font, "scan_font_resource(font)");
781 		continue;
782 	    }
783 	    strncpy(font->x11_name, x11_name, x11_name_len - 1);
784 	    font->x11_name[x11_name_len - 1] = '\0';
785 	    font->std.names = NULL;
786 	    font->std.count = -1;
787 	    font->iso.names = NULL;
788 	    font->iso.count = -1;
789 	    font->next = *pmaps;
790 	    *pmaps = font;
791 	}
792     }
793 }
794 
795 /* Scan all the font resources and set up the maps. */
796 private void
gdev_x_setup_fontmap(gx_device_X * xdev)797 gdev_x_setup_fontmap(gx_device_X * xdev)
798 {
799     if (!xdev->useXFonts)
800 	return;			/* If no external fonts, don't bother */
801 
802     scan_font_resource(xdev->regularFonts, &xdev->regular_fonts, xdev->memory);
803     scan_font_resource(xdev->symbolFonts, &xdev->symbol_fonts, xdev->memory);
804     scan_font_resource(xdev->dingbatFonts, &xdev->dingbat_fonts, xdev->memory);
805 }
806 
807 /* Clean up the instance after making a copy. */
808 int
gdev_x_finish_copydevice(gx_device * dev,const gx_device * from_dev)809 gdev_x_finish_copydevice(gx_device *dev, const gx_device *from_dev)
810 {
811     gx_device_X *xdev = (gx_device_X *) dev;
812 
813     /* Mark the new instance as closed. */
814     xdev->is_open = false;
815 
816     /* Prevent dangling references from the *_fonts lists. */
817     xdev->regular_fonts = 0;
818     xdev->symbol_fonts = 0;
819     xdev->dingbat_fonts = 0;
820 
821     /* Clear all other pointers. */
822     xdev->target = 0;
823     xdev->buffer = 0;
824     xdev->dpy = 0;
825     xdev->scr = 0;
826     xdev->vinfo = 0;
827 
828     /* Clear pointer-like parameters. */
829     xdev->win = (Window)None;
830     xdev->bpixmap = (Pixmap)0;
831     xdev->dest = (Pixmap)0;
832     xdev->cp.pixmap = (Pixmap)0;
833     xdev->ht.pixmap = (Pixmap)0;
834 
835     /* Reset pointer-related parameters. */
836     xdev->is_buffered = false;
837     /* See x_set_buffer for why we do this: */
838     set_dev_proc(xdev, fill_rectangle,
839 		 dev_proc(&gs_x11_device, fill_rectangle));
840 
841     return 0;
842 }
843 
844 /* ---------------- Get/put parameters ---------------- */
845 
846 /* Get the device parameters.  See below. */
847 int
gdev_x_get_params(gx_device * dev,gs_param_list * plist)848 gdev_x_get_params(gx_device * dev, gs_param_list * plist)
849 {
850     gx_device_X *xdev = (gx_device_X *) dev;
851     int code = gx_default_get_params(dev, plist);
852     long id = (long)xdev->pwin;
853 
854     if (code < 0 ||
855 	(code = param_write_long(plist, "WindowID", &id)) < 0 ||
856 	(code = param_write_bool(plist, ".IsPageDevice", &xdev->IsPageDevice)) < 0 ||
857 	(code = param_write_long(plist, "MaxBitmap", &xdev->MaxBitmap)) < 0 ||
858 	(code = param_write_int(plist, "MaxTempPixmap", &xdev->MaxTempPixmap)) < 0 ||
859 	(code = param_write_int(plist, "MaxTempImage", &xdev->MaxTempImage)) < 0 ||
860 	(code = param_write_int(plist, "MaxBufferedTotal", &xdev->MaxBufferedTotal)) < 0 ||
861 	(code = param_write_int(plist, "MaxBufferedArea", &xdev->MaxBufferedArea)) < 0 ||
862 	(code = param_write_int(plist, "MaxBufferedCount", &xdev->MaxBufferedCount)) < 0
863 	)
864 	DO_NOTHING;
865     return code;
866 }
867 
868 /* Set the device parameters.  We reimplement this so we can resize */
869 /* the window and avoid closing and reopening the device, and to add */
870 /* .IsPageDevice. */
871 int
gdev_x_put_params(gx_device * dev,gs_param_list * plist)872 gdev_x_put_params(gx_device * dev, gs_param_list * plist)
873 {
874     gx_device_X *xdev = (gx_device_X *) dev;
875     /*
876      * Provide copies of values of parameters being set:
877      * is_open, width, height, HWResolution, IsPageDevice, Max*.
878      */
879     gx_device_X values;
880 
881     long pwin = (long)xdev->pwin;
882     bool save_is_page = xdev->IsPageDevice;
883     int ecode = 0, code;
884     bool clear_window = false;
885 
886     values = *xdev;
887 
888     /* Handle extra parameters */
889 
890     ecode = param_put_long(plist, "WindowID", &pwin, ecode);
891     ecode = param_put_bool(plist, ".IsPageDevice", &values.IsPageDevice, ecode);
892     ecode = param_put_long(plist, "MaxBitmap", &values.MaxBitmap, ecode);
893     ecode = param_put_int(plist, "MaxTempPixmap", &values.MaxTempPixmap, ecode);
894     ecode = param_put_int(plist, "MaxTempImage", &values.MaxTempImage, ecode);
895     ecode = param_put_int(plist, "MaxBufferedTotal", &values.MaxBufferedTotal, ecode);
896     ecode = param_put_int(plist, "MaxBufferedArea", &values.MaxBufferedArea, ecode);
897     ecode = param_put_int(plist, "MaxBufferedCount", &values.MaxBufferedCount, ecode);
898 
899     if (ecode < 0)
900 	return ecode;
901 
902     /* Unless we specified a new window ID, */
903     /* prevent gx_default_put_params from closing the device. */
904     if (pwin == (long)xdev->pwin)
905 	dev->is_open = false;
906     xdev->IsPageDevice = values.IsPageDevice;
907     code = gx_default_put_params(dev, plist);
908     dev->is_open = values.is_open; /* saved value */
909     if (code < 0) {		/* Undo setting of .IsPageDevice */
910 	xdev->IsPageDevice = save_is_page;
911 	return code;
912     }
913     if (pwin != (long)xdev->pwin) {
914 	if (xdev->is_open)
915 	    gs_closedevice(dev);
916 	xdev->pwin = (Window) pwin;
917     }
918     /* If the device is open, resize the window. */
919     /* Don't do this if Ghostview is active. */
920     if (xdev->is_open && !xdev->ghostview &&
921 	(dev->width != values.width || dev->height != values.height ||
922 	 dev->HWResolution[0] != values.HWResolution[0] ||
923 	 dev->HWResolution[1] != values.HWResolution[1])
924 	) {
925 	int dw = dev->width - values.width;
926 	int dh = dev->height - values.height;
927 	double qx = dev->HWResolution[0] / values.HWResolution[0];
928 	double qy = dev->HWResolution[1] / values.HWResolution[1];
929 
930 	if (dw || dh) {
931 	    XResizeWindow(xdev->dpy, xdev->win,
932 			  dev->width, dev->height);
933 	    if (xdev->bpixmap != (Pixmap) 0) {
934 		XFreePixmap(xdev->dpy, xdev->bpixmap);
935 		xdev->bpixmap = (Pixmap) 0;
936 	    }
937 	    xdev->dest = 0;
938 	    clear_window = true;
939 	}
940 	/* Attempt to update the initial matrix in a sensible way. */
941 	/* The whole handling of the initial matrix is a hack! */
942 	if (xdev->initial_matrix.xy == 0) {
943 	    if (xdev->initial_matrix.xx < 0) {	/* 180 degree rotation */
944 		xdev->initial_matrix.tx += dw;
945 	    } else {		/* no rotation */
946 		xdev->initial_matrix.ty += dh;
947 	    }
948 	} else {
949 	    if (xdev->initial_matrix.xy < 0) {	/* 90 degree rotation */
950 		xdev->initial_matrix.tx += dh;
951 		xdev->initial_matrix.ty += dw;
952 	    } else {		/* 270 degree rotation */
953 	    }
954 	}
955 	xdev->initial_matrix.xx *= qx;
956 	xdev->initial_matrix.xy *= qx;
957 	xdev->initial_matrix.yx *= qy;
958 	xdev->initial_matrix.yy *= qy;
959     }
960     xdev->MaxTempPixmap = values.MaxTempPixmap;
961     xdev->MaxTempImage = values.MaxTempImage;
962     xdev->MaxBufferedTotal = values.MaxBufferedTotal;
963     xdev->MaxBufferedArea = values.MaxBufferedArea;
964     xdev->MaxBufferedCount = values.MaxBufferedCount;
965     if (clear_window || xdev->MaxBitmap != values.MaxBitmap) {
966 	/****** DO MORE FOR RESETTING MaxBitmap ******/
967 	xdev->MaxBitmap = values.MaxBitmap;
968 	if (xdev->is_open)
969 	    gdev_x_clear_window(xdev);
970     }
971     return 0;
972 }
973 
974 /* ---------------- Closing/finalization ---------------- */
975 
976 /* Free fonts when closing the device. */
977 private void
free_x_fontmaps(x11fontmap ** pmaps,gs_memory_t * mem)978 free_x_fontmaps(x11fontmap **pmaps, gs_memory_t *mem)
979 {
980     while (*pmaps) {
981 	x11fontmap *font = *pmaps;
982 
983 	*pmaps = font->next;
984 	if (font->std.names)
985 	    XFreeFontNames(font->std.names);
986 	if (font->iso.names)
987 	    XFreeFontNames(font->iso.names);
988 	gs_free_object(mem, font->x11_name, "free_x_fontmaps(x11_name)");
989 	gs_free_object(mem, font->ps_name, "free_x_fontmaps(ps_name)");
990 	gs_free_object(mem, font, "free_x_fontmaps(font)");
991     }
992 }
993 
994 /* Close the device. */
995 int
gdev_x_close(gx_device_X * xdev)996 gdev_x_close(gx_device_X *xdev)
997 {
998     if (xdev->ghostview)
999 	gdev_x_send_event(xdev, xdev->DONE);
1000     if (xdev->vinfo) {
1001 	XFree((char *)xdev->vinfo);
1002 	xdev->vinfo = NULL;
1003     }
1004     gdev_x_free_colors(xdev);
1005     free_x_fontmaps(&xdev->dingbat_fonts, xdev->memory);
1006     free_x_fontmaps(&xdev->symbol_fonts, xdev->memory);
1007     free_x_fontmaps(&xdev->regular_fonts, xdev->memory);
1008     if (xdev->cmap != DefaultColormapOfScreen(xdev->scr))
1009 	XFreeColormap(xdev->dpy, xdev->cmap);
1010     XCloseDisplay(xdev->dpy);
1011     return 0;
1012 }
1013