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