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