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