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