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