1 /*
2  *  R : A Computer Language for Statistical Data Analysis
3  *  Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
4  *  Copyright (C) 1997--2015  The R Core Team
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, a copy is available at
18  *  https://www.R-project.org/Licenses/
19  */
20 
21 /* The version for R 2.1.0 is partly based on patches by
22    Ei-ji Nakama for use in Japanese.
23 
24    <MBCS> all the strings manipulated here like display and fonts specs
25    are probably ASCII, or at least start with ASCII in the part searched.
26 */
27 
28 #ifdef HAVE_CONFIG_H
29 # include <config.h>
30 #endif
31 
32 #include <Defn.h>
33 
34 /* rint is C99 */
35 #ifdef HAVE_RINT
36 #define R_rint(x) rint(x)
37 #else
38 #define R_rint(x) ((int) x + 0.5)
39 #endif
40 
41 /* needed on Solaris */
42 #define XK_MISCELLANY
43 #include <stdio.h>
44 #include <X11/X.h>
45 #include <X11/Xlib.h>
46 #include <X11/Xatom.h>
47 #include <X11/Xutil.h>
48 #include <X11/cursorfont.h>
49 #include <X11/Intrinsic.h>	/*->	Xlib.h	Xutil.h Xresource.h .. */
50 #ifdef HAVE_X11_Xmu
51 # include <X11/Xmu/Atoms.h>
52 #endif
53 #include <X11/keysymdef.h>
54 
55 
56 #define R_USE_PROTOTYPES 1
57 #include <R_ext/GraphicsEngine.h>
58 #include "Fileio.h"		/* R_fopen */
59 #include "rotated.h"		/* 'Public' routines from here */
60 /* For the input handlers of the event loop mechanism: */
61 #include <R_ext/eventloop.h>
62 #include <R_ext/Memory.h>	/* vmaxget */
63 
64 /* In theory we should do this, but it works less well
65 # ifdef X_HAVE_UTF8_STRING
66 #  define HAVE_XUTF8TEXTESCAPEMENT 1
67 #  define HAVE_XUTF8TEXTEXTENTS 1
68 # endif */
69 
70 typedef int (*X11IOhandler)(Display *);
71 
72 #include "devX11.h"
73 #include "rlogo_icon.h" /* hard-coded ARGB icon */
74 
75 #include <Rmodules/RX11.h>
76 
77 static Cursor watch_cursor = (Cursor) 0 ;
78 static Cursor arrow_cursor = (Cursor) 0 ;
79 static Cursor cross_cursor = (Cursor) 0 ;
80 
81 
82 #define MM_PER_INCH	25.4			/* mm -> inch conversion */
83 
84 #define X_BELL_VOLUME 0 /* integer between -100 and 100 for the volume
85 			    of the bell in locator. */
86 			/* Note: This is in relation to
87 			the general bell level. Was 50, but if > 0
88 			then "xset b off" will not disable the
89 			locator bell - pd 2002-3-11 */
90 /* a colour used to represent the background on png if transparent
91    NB: must be grey as used as RGB and BGR
92 */
93 #define PNG_TRANS 0xfefefe
94 
95 	/********************************************************/
96 	/* If there are resources that are shared by all devices*/
97 	/* of this type, you may wish to make them globals	*/
98 	/* rather than including them in the device-specific	*/
99 	/* parameters structure (especially if they are large !)*/
100 	/********************************************************/
101 
102 	/* X11 Driver Specific parameters
103 	 * with only one copy for all x11 devices */
104 
105 static Display *display;			/* Display */
106 static char dspname[101]="";
107 static int screen;				/* Screen */
108 static Window rootwin, group_leader;		/* Root Window, Group leader */
109 static Visual *visual;				/* Visual */
110 static int depth;				/* Pixmap depth */
111 static int Vclass;				/* Visual class */
112 static X_COLORTYPE model;			/* User color model */
113 static int maxcubesize;				/* Max colorcube size */
114 static XSetWindowAttributes attributes;		/* Window attributes */
115 static Colormap colormap;			/* Default color map */
116 static int whitepixel;				/* bg overlaying canvas */
117 static XContext devPtrContext;
118 static Atom _XA_WM_PROTOCOLS, protocol;
119 
120 static Rboolean displayOpen = FALSE;
121 static Rboolean inclose = FALSE;
122 static int numX11Devices = 0;
123 
124 	/********************************************************/
125 	/* There must be an entry point for the device driver	*/
126 	/* which will create device-specific resources,		*/
127 	/* initialise the device-specific parameters structure	*/
128 	/* and return whether the setup succeeded		*/
129 	/* This is called by the graphics engine when the user	*/
130 	/* creates a new device of this type			*/
131 	/********************************************************/
132 
133 
134 	/********************************************************/
135 	/* There are a number of actions that every device	*/
136 	/* driver is expected to perform (even if, in some	*/
137 	/* cases it does nothing - just so long as it doesn't	*/
138 	/* crash !).  this is how the graphics engine interacts */
139 	/* with each device. ecah action will be documented	*/
140 	/* individually.					*/
141 	/* hooks for these actions must be set up when the	*/
142 	/* device is first created				*/
143 	/********************************************************/
144 
145 	/* Device Driver Actions */
146 
147 static void X11_Activate(pDevDesc dd);
148 static void X11_Circle(double x, double y, double r,
149 		       const pGEcontext gc, pDevDesc dd);
150 static void X11_Clip(double x0, double x1, double y0, double y1,
151 		     pDevDesc dd);
152 static void X11_Close(pDevDesc dd);
153 static void X11_Deactivate(pDevDesc dd);
154 static Rboolean X11_Locator(double *x, double *y, pDevDesc dd);
155 static void X11_Line(double x1, double y1, double x2, double y2,
156 		     const pGEcontext gc, pDevDesc dd);
157 static void X11_MetricInfo(int c, const pGEcontext gc,
158 			   double* ascent, double* descent,
159 			   double* width, pDevDesc dd);
160 static void X11_Mode(int mode, pDevDesc dd);
161 static void X11_NewPage(const pGEcontext gc, pDevDesc dd);
162 static void X11_Polygon(int n, double *x, double *y,
163 			const pGEcontext gc, pDevDesc dd);
164 static void X11_Polyline(int n, double *x, double *y,
165 			 const pGEcontext gc, pDevDesc dd);
166 static void X11_Rect(double x0, double y0, double x1, double y1,
167 		     const pGEcontext gc, pDevDesc dd);
168 static void X11_Raster(unsigned int *raster, int w, int h,
169                        double x, double y, double width, double height,
170                        double rot, Rboolean interpolate,
171                        const pGEcontext gc, pDevDesc dd);
172 static SEXP X11_Cap(pDevDesc dd);
173 static void X11_Size(double *left, double *right,
174 		     double *bottom, double *top,
175 		     pDevDesc dd);
176 static double X11_StrWidth(const char *str, const pGEcontext gc, pDevDesc dd);
177 static void X11_Text(double x, double y, const char *str,
178 		     double rot, double hadj,
179 		     const pGEcontext gc, pDevDesc dd);
180 static void X11_eventHelper(pDevDesc dd, int code);
181 static SEXP     X11_setPattern(SEXP pattern, pDevDesc dd);
182 static void     X11_releasePattern(SEXP ref, pDevDesc dd);
183 static SEXP     X11_setClipPath(SEXP path, SEXP ref, pDevDesc dd);
184 static void     X11_releaseClipPath(SEXP ref, pDevDesc dd);
185 static SEXP     X11_setMask(SEXP path, SEXP ref, pDevDesc dd);
186 static void     X11_releaseMask(SEXP ref, pDevDesc dd);
187 
188 	/*************************************************/
189 	/* End of list of required device driver actions */
190 	/*************************************************/
191 
192 	/* Support Routines */
193 
194 static void *RLoadFont(pX11Desc, char*, int, int);
195 static double pixelHeight(void);
196 static double pixelWidth(void);
197 static void SetColor(unsigned int, pX11Desc);
198 static void SetFont(const pGEcontext, pX11Desc);
199 static void SetLinetype(const pGEcontext, pX11Desc);
200 static void X11_Close_bitmap(pX11Desc xd);
201 static char* translateFontFamily(char* family, pX11Desc xd);
202 
203 static double RedGamma	 = 1.0;
204 static double GreenGamma = 1.0;
205 static double BlueGamma	 = 1.0;
206 
207 #ifdef HAVE_WORKING_X11_CAIRO
208 # include "cairoFns.c"
209 
210 	/************************/
211 	/*        Buffering     */
212 	/************************/
213 
214 /*
215   Buffering is only implemented for the cairo-based devices.
216    The original (Feb 2008) version had two types:
217    - "nbcairo".  This wrote directly to a cairo_xlib_surface, xd->cs.
218    - "cairo".  This wrote to a cairo_image_surface xd->cs, and copied that to
219      the cairo_xlib_surface (xd->xcs) at mode(0) calls.
220 
221    Further types were introduced (experimentally) in May 2011.  We kept:
222    - "dbcairo".  Similar to cairo, but the copying is only done when needed
223       based on a timer.
224    Timing requires a medium-res timer. The current method is to update
225    ca 100ms after the last activity (using the event loop) or at a
226    mode(0) call if it is 500ms after the last update.
227  */
228 
229 #if (defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_REALTIME)) || defined(HAVE_GETTIMEOFDAY)
230 /* We need to avoid this in the rare case that it is only in seconds */
231 extern double currentTime(void); /* from datetime.c */
232 #else
233 /* Alternatively, use times() which R >= 2.14.0 requires.  This could
234   conceivably wrap around, but on the sort of system where this might
235   be used, clock_t is 32-bit (it is typically long or unsigned long)
236   and CLK_TCK is 60-100, so it happens after many months of uptime.
237 */
238 # include <sys/times.h>
239 # ifndef CLK_TCK
240 #   define CLK_TCK 60
241 # endif
currentTime(void)242 static double currentTime(void)
243 {
244     struct tms ti;
245     return ((double) times(&ti))/CLK_TCK;
246 }
247 #endif
248 
Cairo_update(pX11Desc xd)249 static void Cairo_update(pX11Desc xd)
250 {
251     if(inclose || !xd || !xd->buffered || xd->holdlevel > 0) return;
252     cairo_paint(xd->xcc);
253     /* workaround for bug in cairo 1.12.x (PR#15168) */
254     cairo_surface_flush(xd->xcs);
255     if (xd->type == WINDOW) XDefineCursor(display, xd->window, arrow_cursor);
256     XSync(display, 0);
257     xd->last = currentTime();
258 }
259 
260 
261 /*
262    We record a linked list of devices which are open and double-buffered.
263    The head of the list is a dummy entry to make removals the same for
264    any element.
265 */
266 struct xd_list {
267     pX11Desc this;
268    struct xd_list *next;
269 };
270 
271 typedef struct xd_list *Xdl;
272 static struct xd_list xdl0;
273 static Xdl xdl = &xdl0;
274 
CairoHandler(void)275 static void CairoHandler(void)
276 {
277     static int  buffer_lock = 0; /* reentrancy guard */
278     if (!buffer_lock && xdl->next) {
279 	double current = currentTime();
280 	buffer_lock = 1;
281 	for(Xdl z = xdl->next; z; z = z->next) {
282 	    pX11Desc xd = z->this;
283 	    if(xd->last > xd->last_activity) continue;
284 	    if((current - xd->last) < xd->update_interval) continue;
285 	    Cairo_update(xd);
286 	}
287 	buffer_lock = 0;
288     }
289 }
290 
291 /* private hooks in sys-std.c */
292 extern void (* Rg_PolledEvents)(void);
293 extern int Rg_wait_usec;
294 
295 /*
296   check for updates every 50ms:
297   by default the updater is only run >= 100ms after last update.
298 */
299 #define WAIT 50000
300 static int timingInstalled = 0;
addBuffering(pX11Desc xd)301 static void addBuffering(pX11Desc xd)
302 {
303     Xdl xdln = (Xdl) malloc(sizeof(struct xd_list));
304     if(!xdln) error("allocation failed in addBuffering");
305     xdln->this = xd;
306     xdln->next = xdl->next;
307     xdl->next = xdln;
308     if(timingInstalled) return;
309     timingInstalled = 1;
310     Rg_PolledEvents = CairoHandler;
311     Rg_wait_usec = WAIT;
312 }
313 
removeBuffering(pX11Desc xd)314 static void removeBuffering(pX11Desc xd)
315 {
316     for(Xdl z = xdl; z->next; z = z->next)
317 	if (z->next->this == xd) {
318 	    Xdl old = z->next;
319 	    z->next = z->next->next;
320 	    free(old);
321 	    break;
322 	}
323     if(xdl->next == NULL) {
324 	Rg_wait_usec = 0;
325 	timingInstalled = 0;
326     }
327 }
328 
Cairo_NewPage(const pGEcontext gc,pDevDesc dd)329 static void Cairo_NewPage(const pGEcontext gc, pDevDesc dd)
330 {
331     pX11Desc xd = (pX11Desc) dd->deviceSpecific;
332 
333     cairo_reset_clip(xd->cc);
334     xd->fill = R_OPAQUE(gc->fill) ? gc->fill: xd->canvas;
335     CairoColor(xd->fill, xd);
336     cairo_new_path(xd->cc);
337     cairo_paint(xd->cc);
338     if(xd->buffered) Cairo_update(xd);
339     else XSync(display, 0);
340 }
341 
Cairo_holdflush(pDevDesc dd,int level)342 static int Cairo_holdflush(pDevDesc dd, int level)
343 {
344     pX11Desc xd = (pX11Desc) dd->deviceSpecific;
345     int old = xd->holdlevel;
346 
347     if(!xd->buffered) return old;
348     xd->holdlevel += level;
349     if(xd->holdlevel <= 0) xd->holdlevel = 0;
350 //    printf("holdlevel = %d\n",  xd->holdlevel);
351     /* flush if at level zero - also changes cursor */
352     if(xd->holdlevel == 0) {
353 	if(xd->buffered) Cairo_update(xd);
354 	else {
355 	  if (xd->type == WINDOW) XDefineCursor(display, xd->window, arrow_cursor);
356 	    XSync(display, 0);
357 	}
358     } else if (old == 0) {
359 	/* May need to flush before holding */
360 	if(xd->buffered > 1 && xd->last_activity > xd->last) {
361 	    xd->holdlevel = old;
362 	    Cairo_update(xd);
363 	    xd->holdlevel = level;
364 	}
365 	if (xd->type == WINDOW) XDefineCursor(display, xd->window, watch_cursor);
366 	XSync(display, 0);
367     }
368     return xd->holdlevel;
369 }
370 #endif /* HAVE_WORKING_X11_CAIRO */
371 
372 
373 
374 	/************************/
375 	/* X11 Color Management */
376 	/************************/
377 
378 /* Variables Used To Store Colormap Information */
379 static struct { int red; int green; int blue; } RPalette[512];
380 static XColor XPalette[512];
381 static int PaletteSize;
382 
383 
384 /* Monochome Displays : Compute pixel values by converting */
385 /* RGB values to luminance and then thresholding. */
386 /* See: Foley & van Damm. */
387 
SetupMonochrome(void)388 static void SetupMonochrome(void)
389 {
390     depth = 1;
391 }
392 
GetMonochromePixel(int r,int g,int b)393 static unsigned GetMonochromePixel(int r, int g, int b)
394 {
395     if ((int)(0.299 * r + 0.587 * g + 0.114 * b) > 127)
396 	return (unsigned) WhitePixel(display, screen);
397     else
398 	return (unsigned) BlackPixel(display, screen);
399 }
400 
401 
402 /* Grayscale Displays : Compute pixel values by converting */
403 /* RGB values to luminance.  See: Foley & van Damm. */
404 
GetGrayScalePixel(int r,int g,int b)405 static unsigned GetGrayScalePixel(int r, int g, int b)
406 {
407     unsigned int d, dmin = 0xFFFFFFFF;
408     unsigned int dr;
409     int i;
410     unsigned int pixel = 0;  /* -Wall */
411     int gray = (int)((0.299 * r + 0.587 * g + 0.114 * b) + 0.0001);
412     for (i = 0; i < PaletteSize; i++) {
413 	dr = (RPalette[i].red - gray);
414 	d = dr * dr;
415 	if (d < dmin) {
416 	    pixel = (unsigned) XPalette[i].pixel;
417 	    dmin = d;
418 	}
419     }
420     return pixel;
421 }
422 
GetGrayPalette(Display * displ,Colormap cmap,int n)423 static Rboolean GetGrayPalette(Display *displ, Colormap cmap, int n)
424 {
425     int status, i, m;
426     m = 0;
427     i = 0;
428     for (i = 0; i < n; i++) {
429 	RPalette[i].red	  = (unsigned short) ((i * 0xff) / (n - 1));
430 	RPalette[i].green = RPalette[i].red;
431 	RPalette[i].blue  = RPalette[i].red;
432 	/* Gamma correct here */
433 	XPalette[i].red	  = (unsigned short)((i * 0xffff) / (n - 1));
434 	XPalette[i].green = XPalette[i].red;
435 	XPalette[i].blue  = XPalette[i].red;
436 	status = XAllocColor(displ, cmap, &XPalette[i]);
437 	if (status == 0) {
438 	    XPalette[i].flags = 0;
439 	    m++;
440 	}
441 	else
442 	    XPalette[i].flags = DoRed|DoGreen|DoBlue;
443     }
444     PaletteSize = n;
445     if (m > 0) {
446 	for (i = 0; i < PaletteSize; i++) {
447 	    if (XPalette[i].flags != 0)
448 		XFreeColors(displ, cmap, &(XPalette[i].pixel), 1, 0);
449 	}
450 	PaletteSize = 0;
451 	return FALSE;
452     }
453     else return TRUE;
454 }
455 
SetupGrayScale(void)456 static void SetupGrayScale(void)
457 {
458     int res = 0, d;
459     PaletteSize = 0;
460     /* try for 128 grays on an 8-bit display */
461     if (depth > 8) d = depth = 8; else d = depth - 1;
462     /* try (256), 128, 64, 32, 16 grays */
463     while (d >= 4 && !(res = GetGrayPalette(display, colormap, 1 << d)))
464 	d--;
465     if (!res) {
466 	/* Can't find a sensible grayscale, so revert to monochrome */
467 	warning(_("cannot set grayscale: reverting to monochrome"));
468 	model = MONOCHROME;
469 	SetupMonochrome();
470     }
471 }
472 
473 /* PseudoColor Displays : There are two strategies here. */
474 /* 1) allocate a standard color cube and match colors */
475 /* within that based on (weighted) distances in RGB space. */
476 /* 2) allocate colors exactly as they are requested until */
477 /* all color cells are used.  Fail with an error message */
478 /* when this happens. */
479 
480 static int RGBlevels[][3] = {  /* PseudoColor Palettes */
481     { 8, 8, 4 },
482     { 6, 7, 6 },
483     { 6, 6, 6 },
484     { 6, 6, 5 },
485     { 6, 6, 4 },
486     { 5, 5, 5 },
487     { 5, 5, 4 },
488     { 4, 4, 4 },
489     { 4, 4, 3 },
490     { 3, 3, 3 },
491     { 2, 2, 2 }
492 };
493 static int NRGBlevels = sizeof(RGBlevels) / (3 * sizeof(int));
494 
495 
GetColorPalette(Display * dpy,Colormap cmap,int nr,int ng,int nb)496 static int GetColorPalette(Display *dpy, Colormap cmap, int nr, int ng, int nb)
497 {
498     int status, i, m, r, g, b;
499     m = 0;
500     i = 0;
501     for (r = 0; r < nr; r++) {
502 	for (g = 0; g < ng; g++) {
503 	    for (b = 0; b < nb; b++) {
504 		RPalette[i].red	  = (r * 0xff) / (nr - 1);
505 		RPalette[i].green = (g * 0xff) / (ng - 1);
506 		RPalette[i].blue  = (b * 0xff) / (nb - 1);
507 		/* Perform Gamma Correction Here */
508 		XPalette[i].red	  =
509 		    (unsigned short)(pow(r / (nr - 1.0), RedGamma) * 0xffff);
510 		XPalette[i].green =
511 		    (unsigned short)(pow(g / (ng - 1.0), GreenGamma) * 0xffff);
512 		XPalette[i].blue  =
513 		    (unsigned short)(pow(b / (nb - 1.0), BlueGamma) * 0xffff);
514 		/* End Gamma Correction */
515 		status = XAllocColor(dpy, cmap, &XPalette[i]);
516 		if (status == 0) {
517 		    XPalette[i].flags = 0;
518 		    m++;
519 		}
520 		else
521 		    XPalette[i].flags = DoRed|DoGreen|DoBlue;
522 		i++;
523 	    }
524 	}
525     }
526     PaletteSize = nr * ng * nb;
527     if (m > 0) {
528 	for (i = 0; i < PaletteSize; i++) {
529 	    if (XPalette[i].flags != 0)
530 		XFreeColors(dpy, cmap, &(XPalette[i].pixel), 1, 0);
531 	}
532 	PaletteSize = 0;
533 	return 0;
534     }
535     else
536 	return 1;
537 }
538 
SetupPseudoColor(void)539 static void SetupPseudoColor(void)
540 {
541     int i, size;
542     PaletteSize = 0;
543     if (model == PSEUDOCOLOR1) {
544 	for (i = 0; i < NRGBlevels; i++) {
545 	    size = RGBlevels[i][0] * RGBlevels[i][1] * RGBlevels[i][2];
546 	    if (size < maxcubesize && GetColorPalette(display, colormap,
547 				RGBlevels[i][0],
548 				RGBlevels[i][1],
549 				RGBlevels[i][2]))
550 		break;
551 	}
552 	if (PaletteSize == 0) {
553 	    warning(_("X11 driver unable to obtain color cube\n  reverting to monochrome"));
554 	    model = MONOCHROME;
555 	    SetupMonochrome();
556 	}
557     }
558     else {
559 	PaletteSize = 0;
560     }
561 }
562 
GetPseudoColor1Pixel(int r,int g,int b)563 static unsigned int GetPseudoColor1Pixel(int r, int g, int b)
564 {
565     unsigned int d, dmin = 0xFFFFFFFF;
566     unsigned int dr, dg, db;
567     unsigned int pixel;
568     int i;
569     pixel = 0;			/* -Wall */
570     for (i = 0; i < PaletteSize; i++) {
571 	dr = (RPalette[i].red - r);
572 	dg = (RPalette[i].green - g);
573 	db = (RPalette[i].blue - b);
574 	d = dr * dr + dg * dg + db * db;
575 	if (d < dmin) {
576 	    pixel = (unsigned int) XPalette[i].pixel;
577 	    dmin = d;
578 	}
579     }
580     return pixel;
581 }
582 
GetPseudoColor2Pixel(int r,int g,int b)583 static unsigned int GetPseudoColor2Pixel(int r, int g, int b)
584 {
585     int i;
586     /* Search for previously allocated color */
587     for (i = 0; i < PaletteSize ; i++) {
588 	if (r == RPalette[i].red &&
589 	    g == RPalette[i].green &&
590 	    b == RPalette[i].blue) return (unsigned int) XPalette[i].pixel;
591     }
592     /* Attempt to allocate a new color */
593     XPalette[PaletteSize].red	=
594 	(unsigned short)(pow(r / 255.0, RedGamma) * 0xffff);
595     XPalette[PaletteSize].green =
596 	(unsigned short)(pow(g / 255.0, GreenGamma) * 0xffff);
597     XPalette[PaletteSize].blue	=
598 	(unsigned short)(pow(b / 255.0, BlueGamma) * 0xffff);
599     if (PaletteSize == 256 ||
600 	XAllocColor(display, colormap, &XPalette[PaletteSize]) == 0) {
601 	error(_("Error: X11 cannot allocate additional graphics colors.\n\
602 Consider using X11 with colortype=\"pseudo.cube\" or \"gray\"."));
603     }
604     RPalette[PaletteSize].red = r;
605     RPalette[PaletteSize].green = g;
606     RPalette[PaletteSize].blue = b;
607     PaletteSize++;
608     return (unsigned int)XPalette[PaletteSize - 1].pixel;
609 }
610 
GetPseudoColorPixel(int r,int g,int b)611 static unsigned int GetPseudoColorPixel(int r, int g, int b)
612 {
613     if (model == PSEUDOCOLOR1)
614 	return GetPseudoColor1Pixel(r, g, b);
615     else
616 	return GetPseudoColor2Pixel(r, g, b);
617 }
618 
619 /* Truecolor Displays : Allocate the colors as they are requested */
620 
621 static unsigned int RMask, RShift;
622 static unsigned int GMask, GShift;
623 static unsigned int BMask, BShift;
624 
SetupTrueColor(void)625 static void SetupTrueColor(void)
626 {
627     RMask = (unsigned int)visual->red_mask;
628     GMask = (unsigned int)visual->green_mask;
629     BMask = (unsigned int)visual->blue_mask;
630     RShift = 0; while ((RMask & 1) == 0) { RShift++; RMask >>= 1; }
631     GShift = 0; while ((GMask & 1) == 0) { GShift++; GMask >>= 1; }
632     BShift = 0; while ((BMask & 1) == 0) { BShift++; BMask >>= 1; }
633 }
634 
GetTrueColorPixel(int r,int g,int b)635 static unsigned GetTrueColorPixel(int r, int g, int b)
636 {
637     r = (int)(pow((r / 255.0), RedGamma) * 255);
638     g = (int)(pow((g / 255.0), GreenGamma) * 255);
639     b = (int)(pow((b / 255.0), BlueGamma) * 255);
640     return
641 	(((r * RMask) / 255) << RShift) |
642 	(((g * GMask) / 255) << GShift) |
643 	(((b * BMask) / 255) << BShift);
644 }
645 
646 /* Interface for General Visual */
647 
GetX11Pixel(int r,int g,int b)648 static unsigned int GetX11Pixel(int r, int g, int b)
649 {
650     switch(model) {
651     case MONOCHROME:
652 	return GetMonochromePixel(r, g, b);
653     case GRAYSCALE:
654 	return GetGrayScalePixel(r, g, b);
655     case PSEUDOCOLOR1:
656     case PSEUDOCOLOR2:
657 	return GetPseudoColorPixel(r, g, b);
658     case TRUECOLOR:
659 	return GetTrueColorPixel(r, g, b);
660     default:
661 	printf("Unknown Visual\n");
662     }
663     return 0;
664 }
665 
FreeX11Colors(void)666 static void FreeX11Colors(void)
667 {
668     int i;
669     if (model == PSEUDOCOLOR2) {
670 	for (i = 0; i < PaletteSize; i++)
671 	    XFreeColors(display, colormap, &(XPalette[i].pixel), 1, 0);
672 	PaletteSize = 0;
673     }
674 }
675 
SetupX11Color(void)676 static Rboolean SetupX11Color(void)
677 {
678     if (depth <= 1) {
679 	/* On monchome displays we must use black/white */
680 	model = MONOCHROME;
681 	SetupMonochrome();
682     }
683     else if (Vclass ==	StaticGray || Vclass == GrayScale) {
684 	if (model == MONOCHROME)
685 	    SetupMonochrome();
686 	else {
687 	    model = GRAYSCALE;
688 	    SetupGrayScale();
689 	}
690     }
691     else if (Vclass == StaticColor) {
692 	/* FIXME : Currently revert to mono. */
693 	/* Should do the real thing. */
694 	model = MONOCHROME;
695 	SetupMonochrome();
696     }
697     else if (Vclass ==	PseudoColor) {
698 	if (model == MONOCHROME)
699 	    SetupMonochrome();
700 	else if (model == GRAYSCALE)
701 	    SetupGrayScale();
702 	else {
703 	    if (model == TRUECOLOR)
704 		model = PSEUDOCOLOR2;
705 	    SetupPseudoColor();
706 	}
707     }
708     else if (Vclass == TrueColor) {
709 	if (model == MONOCHROME)
710 	    SetupMonochrome();
711 	else if (model == GRAYSCALE)
712 	    SetupGrayScale();
713 	else if (model == PSEUDOCOLOR1 || model == PSEUDOCOLOR2)
714 	    SetupPseudoColor();
715 	else
716 	    SetupTrueColor();
717     }
718     else if (Vclass == DirectColor) {
719 	/* FIXME : Currently revert to mono. */
720 	/* Should do the real thing. */
721 	model = MONOCHROME;
722 	SetupMonochrome();
723     }
724     else {
725 	printf("Unknown Visual\n");
726 	return FALSE;
727     }
728     return TRUE;
729 }
730 
731 	/* Pixel Dimensions (Inches) */
732 
733 
pixelWidth(void)734 static double pixelWidth(void)
735 {
736     double width, widthMM;
737     width = DisplayWidth(display, screen);
738     widthMM = DisplayWidthMM(display, screen);
739     return ((double)widthMM / (double)width) / MM_PER_INCH;
740 }
741 
pixelHeight(void)742 static double pixelHeight(void)
743 {
744     double height, heightMM;
745     height = DisplayHeight(display, screen);
746     heightMM = DisplayHeightMM(display, screen);
747     return ((double)heightMM / (double)height) / MM_PER_INCH;
748 }
749 
handleEvent(XEvent event)750 static void handleEvent(XEvent event)
751 {
752     if (event.xany.type == Expose) {
753 	/* ----- window repaint ------ */
754 	while (XCheckTypedWindowEvent(display, event.xexpose.window, Expose, &event));
755 	if (inclose) return;
756 	if (event.xexpose.count != 0) return;
757 	caddr_t temp;
758 	XFindContext(display, event.xexpose.window, devPtrContext, &temp);
759 	pDevDesc dd = (pDevDesc) temp;
760 	pGEDevDesc gdd = desc2GEDesc(dd);
761 	if(gdd->dirty) {
762 #ifdef HAVE_WORKING_X11_CAIRO
763 	    pX11Desc xd = (pX11Desc) dd->deviceSpecific;
764 	    /* We can use the buffered copy where we have it */
765 	    if(xd->buffered == 1) {
766 		cairo_paint(xd->xcc);
767 		/* workaround for bug in cairo 1.12.x (PR#15168) */
768 		cairo_surface_flush(xd->xcs);
769 	    } else if (xd->buffered > 1)
770 		/* rely on timer to repaint eventually */
771 		xd->last_activity = currentTime();
772 	    else
773 #endif
774 		GEplayDisplayList(gdd);
775 	    XSync(display, 0);
776 	}
777     } else if (event.type == ConfigureNotify) {
778 	while (XCheckTypedEvent(display, ConfigureNotify, &event)) ;
779 	if (inclose) return;
780 	caddr_t temp;
781 	XFindContext(display, event.xconfigure.window, devPtrContext, &temp);
782 	pDevDesc dd = (pDevDesc) temp;
783 	pX11Desc xd = (pX11Desc) dd->deviceSpecific;
784 	if (xd->windowWidth != event.xconfigure.width ||
785 	    xd->windowHeight != event.xconfigure.height) {
786 
787 	    /* ----- window resize ------ */
788 
789 	    xd->windowWidth = event.xconfigure.width;
790 	    xd->windowHeight = event.xconfigure.height;
791 #if defined HAVE_WORKING_X11_CAIRO
792 	    if(xd->useCairo) {
793 		if(xd->buffered) {
794 		    cairo_surface_destroy(xd->cs); xd->cs = NULL;
795 		    cairo_destroy(xd->cc); xd->cc = NULL;
796 		    cairo_xlib_surface_set_size(xd->xcs, xd->windowWidth,
797 						    xd->windowHeight);
798 		    xd->cs =
799 			cairo_image_surface_create(CAIRO_FORMAT_RGB24,
800 						   xd->windowWidth,
801 						   xd->windowHeight);
802 		    cairo_status_t res = cairo_surface_status(xd->cs);
803 		    if (res != CAIRO_STATUS_SUCCESS) {
804 			warning("cairo error '%s'",
805 				cairo_status_to_string(res));
806 			error("fatal error on resize: please shut down the device");
807 		    }
808 		    xd->cc = cairo_create(xd->cs);
809 		    cairo_set_antialias(xd->cc, xd->antialias);
810 		    cairo_set_source_surface (xd->xcc, xd->cs, 0, 0);
811 		} else { /* not buffered */
812 		    cairo_xlib_surface_set_size(xd->cs, xd->windowWidth,
813 						xd->windowHeight);
814 		    cairo_reset_clip(xd->cc);
815 		}
816 	    }
817 #endif
818 	    dd->size(&(dd->left), &(dd->right), &(dd->bottom), &(dd->top), dd);
819 	    /* gobble Expose events; we'll redraw anyway */
820 	    while (XCheckTypedWindowEvent(display, event.xexpose.window, Expose, &event));
821 	    pGEDevDesc gdd = desc2GEDesc(dd);
822 	    if(gdd->dirty) {
823 		GEplayDisplayList(gdd);
824 		XSync(display, 0);
825 	    }
826 	}
827     } else if ((event.type == ClientMessage) &&
828 	     (event.xclient.message_type == _XA_WM_PROTOCOLS)) {
829 	if (!inclose && event.xclient.data.l[0] == protocol) {
830 	    caddr_t temp;
831 	    XFindContext(display, event.xclient.window, devPtrContext, &temp);
832 	    killDevice(ndevNumber((pDevDesc) temp));
833 	}
834     }
835 }
836 
R_ProcessX11Events(void * data)837 static void R_ProcessX11Events(void *data)
838 {
839     XEvent event;
840 
841     while (!R_isForkedChild && displayOpen && XPending(display)) {
842 	XNextEvent(display, &event);
843 	/* printf("%i\n",event.type); */
844 	handleEvent(event);
845     }
846 }
847 
848 
849 	/************************/
850 	/* X11 Font Management  */
851 	/************************/
852 
853 static char *fontname = "-adobe-helvetica-%s-%s-*-*-%d-*-*-*-*-*-*-*";
854 static char *symbolname	 = "-adobe-symbol-medium-r-*-*-%d-*-*-*-*-*-*-*";
855 
856 static char *slant[]  = {"r", "o"};
857 static char *weight[] = {"medium", "bold"};
858 
859 /* Bitmap of the Adobe design sizes */
860 
861 static unsigned int adobe_sizes = 0x0403165D;
862 
863 #define MAXFONTS 64
864 #define CLRFONTS 16 /* Number to free when cache runs full */
865 
866 typedef struct {
867     char family[500];
868     int face, size;
869     R_XFont *font;
870 } cacheentry;
871 
872 static cacheentry fontcache[MAXFONTS];
873 static int nfonts = 0;
874 static int force_nonscalable = 0; /* for testing */
875 
876 #define ADOBE_SIZE(I) ((I) > 7 && (I) < 35 && (adobe_sizes & (1<<((I)-8))))
877 #define SMALLEST 2
878 
879 
R_XLoadQueryFont(Display * display,char * name)880 static R_XFont *R_XLoadQueryFont(Display *display, char *name)
881 {
882     R_XFont *tmp;
883     tmp = (R_XFont *) malloc(sizeof(R_XFont));
884     tmp->type = One_Font;
885     tmp->font = XLoadQueryFont(display, name);
886     if(tmp->font)
887 	return tmp;
888     else {
889 	free(tmp);
890 	return NULL;
891     }
892 }
893 
R_XFreeFont(Display * display,R_XFont * font)894 static void R_XFreeFont(Display *display, R_XFont *font)
895 {
896     if(font->type == Font_Set) XFreeFontSet(display, font->fontset);
897     else XFreeFont(display, font->font);
898     free(font);
899 }
900 
901 
902 /*
903  * Can't load Symbolfont to XFontSet!!
904  */
R_XLoadQueryFontSet(Display * display,const char * fontset_name)905 static R_XFont *R_XLoadQueryFontSet(Display *display,
906 				    const char *fontset_name)
907 {
908     R_XFont *tmp = (R_XFont *) malloc(sizeof(R_XFont));
909     XFontSet fontset;
910     int  /*i,*/ missing_charset_count;
911     char **missing_charset_list, *def_string;
912 
913 #ifdef DEBUG_X11
914     printf("loading fontset %s\n", fontset_name);
915 #endif
916     fontset = XCreateFontSet(display, fontset_name, &missing_charset_list,
917 			     &missing_charset_count, &def_string);
918     if(!fontset) {
919 	free(tmp);
920 	return NULL;
921     }
922     if (missing_charset_count) {
923 #ifdef DEBUG_X11
924 	int i;
925 	for(i = 0; i < missing_charset_count; i++)
926 	   warning("font for charset %s is lacking.", missing_charset_list[i]);
927 	XFreeStringList(missing_charset_list);
928 #endif
929     }
930     tmp->type = Font_Set;
931     tmp->fontset = fontset;
932     return tmp;
933 }
934 
935 
RLoadFont(pX11Desc xd,char * family,int face,int size)936 static void *RLoadFont(pX11Desc xd, char* family, int face, int size)
937 {
938     /* size is in points here */
939     int pixelsize, i, dpi;
940     cacheentry *f;
941     char buf[BUFSIZ];
942     char buf1[BUFSIZ];
943     R_XFont *tmp = NULL;
944 
945 #ifdef DEBUG_X11
946     printf("trying face %d size %d\n", face, size);
947 #endif
948 
949     if (size < SMALLEST) size = SMALLEST;
950     face--;
951 
952     if(xd->type == PNG || xd->type == JPEG ||
953        xd->type == TIFF || xd->type == BMP) {
954 	dpi = (xd->res_dpi > 0) ? (int)(xd->res_dpi + 0.5) : 72;
955     } else {
956 	dpi = (int)(1./pixelHeight() + 0.5);
957     }
958 
959     if(abs(dpi - 75) < 5) {
960 	/* use pointsize as pixel size */
961     } else if(abs(dpi - 100) < 5) {
962     /* Here's a 1st class fudge: make sure that the Adobe design sizes
963        8, 10, 11, 12, 14, 17, 18, 20, 24, 25, 34 can be obtained via
964        an integer "size" at 100 dpi, namely 6, 7, 8, 9, 10, 12, 13,
965        14, 17, 18, 24 points. It's almost y = x * 100/72, but not
966        quite. The constants were found using lm(). --pd */
967 	size = (int) R_rint(size * 1.43 - 0.4);
968     } else size = (int) R_rint(size * dpi/72);
969 
970     /* search fontcache */
971     for ( i = nfonts ; i-- ; ) {
972 	f = &fontcache[i];
973 	if ( strcmp(f->family, family) == 0 &&
974 	     f->face == face &&
975 	     f->size == size )
976 	    return f->font;
977     }
978 
979     /* 'size' is the requested size, 'pixelsize'  the size of the
980        actually allocated font*/
981     pixelsize = size;
982 
983     /*
984      * The symbol font face is a historical oddity
985      * Always use a standard font for font face 5
986      */
987     if (face == SYMBOL_FONTFACE - 1) /* NB: face-- above */
988 	sprintf(buf, xd->symbolfamily,  pixelsize);
989     else
990       if (mbcslocale && *slant[(face & 2) >> 1] == 'o') {
991 	sprintf(buf, family, weight[face & 1], slant[(face & 2) >> 1],
992 		pixelsize);
993 	sprintf(buf1, family, weight[face & 1], "i",  pixelsize);
994 	strcat(buf,",");
995 	strcat(buf,buf1);
996       } else
997 	  sprintf(buf, family, weight[face & 1], slant[(face & 2) >> 1],
998 		  pixelsize);
999 #ifdef DEBUG_X11
1000     Rprintf("loading:\n%s\n",buf);
1001 #endif
1002     if (!mbcslocale || face == SYMBOL_FONTFACE - 1)
1003       tmp = R_XLoadQueryFont(display, buf);
1004     else
1005       tmp = R_XLoadQueryFontSet(display, buf);
1006 
1007 #ifdef DEBUG_X11
1008     if (tmp) Rprintf("success\n"); else Rprintf("failure\n");
1009 #endif
1010     /*
1011      * IF can't find the font specified then
1012      * go to great lengths to find something else to use.
1013      */
1014     if (!tmp || (force_nonscalable && !ADOBE_SIZE(size)) ){
1015 	static int near[]=
1016 	  {14,14,14,17,17,18,20,20,20,20,24,24,24,25,25,25,25};
1017 	/* 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29  */
1018 
1019 	/* If ADOBE_SIZE(pixelsize) is true at this point then
1020 	   the user's system does not have the standard ADOBE font set
1021 	   so we just have to use a "fixed" font.
1022 	   If we can't find a "fixed" font then something is seriously
1023 	   wrong */
1024 	if ( ADOBE_SIZE(pixelsize) ) {
1025 	    if(tmp)
1026 		R_XFreeFont(display, tmp);
1027 	    if(mbcslocale)
1028 		tmp = (void*) R_XLoadQueryFontSet(display,
1029 		   "-*-fixed-medium-r-*--13-*-*-*-*-*-*-*");
1030 	    else
1031 		tmp = (void*) R_XLoadQueryFont(display, "fixed");
1032 
1033 	    if (tmp)
1034 		return tmp;
1035 	    else
1036 		error(_("could not find any X11 fonts\nCheck that the Font Path is correct."));
1037 	}
1038 
1039 	if ( pixelsize < 8 )
1040 	    pixelsize = 8;
1041 	else if (pixelsize == 9)
1042 	    pixelsize = 8;
1043 	else if (pixelsize < 30) /* must be at least 13 */
1044 	    pixelsize = near[size-13];
1045 	else
1046 	    pixelsize = 34;
1047 
1048 
1049 	if (face == SYMBOL_FONTFACE - 1)
1050 	    sprintf(buf, symbolname, pixelsize);
1051 	else
1052 	    sprintf(buf, fontname,
1053 		    weight[face & 1],
1054 		    slant[(face & 2) >> 1 ],  pixelsize);
1055 #ifdef DEBUG_X11
1056 	Rprintf("loading:\n%s\n",buf);
1057 #endif
1058 	if (!mbcslocale || face == SYMBOL_FONTFACE - 1)
1059 	    tmp = R_XLoadQueryFont(display, buf);
1060 	else
1061 	    tmp = R_XLoadQueryFontSet(display, buf);
1062 #ifdef DEBUG_X11
1063 	if (tmp) Rprintf("success\n"); else Rprintf("failure\n");
1064 #endif
1065     }
1066     if(!tmp && size > 24) {
1067 	/* final try, size 24 */
1068 	pixelsize = 24;
1069 	if (face == 4)
1070 	    sprintf(buf, symbolname, 24);
1071 	else
1072 	    sprintf(buf, fontname,
1073 		    weight[face & 1],
1074 		    slant[(face & 2) >> 1 ],  24);
1075 #ifdef DEBUG_X11
1076 	Rprintf("loading:\n%s\n",buf);
1077 #endif
1078 
1079 	if (!mbcslocale || face == SYMBOL_FONTFACE - 1)
1080 	    tmp = R_XLoadQueryFont(display, buf);
1081 	else
1082 	    tmp = R_XLoadQueryFontSet(display, buf);
1083 
1084 #ifdef DEBUG_X11
1085 	if (tmp) Rprintf("success\n"); else Rprintf("failure\n");
1086 #endif
1087     }
1088 
1089     if (tmp){
1090 	f = &fontcache[nfonts++];
1091 	strcpy(f->family, family);
1092 	f->face = face;
1093 	f->size = size;
1094 	f->font = tmp;
1095 	if (fabs( (pixelsize - size)/(double)size ) > 0.1)
1096 	    warning(_("X11 used font size %d when %d was requested"),
1097 		    pixelsize, size);
1098     }
1099     if (nfonts == MAXFONTS) /* make room in the font cache */
1100     {
1101 	for (i = 0 ; i < CLRFONTS ; i++)
1102 	      R_XFreeFont(display, fontcache[i].font);
1103 	for (i = CLRFONTS ; i < MAXFONTS ; i++)
1104 	    fontcache[i - CLRFONTS] = fontcache[i];
1105 	nfonts -= CLRFONTS;
1106     }
1107     return tmp;
1108 }
1109 
1110 
SetFont(const pGEcontext gc,pX11Desc xd)1111 static void SetFont(const pGEcontext gc, pX11Desc xd)
1112 {
1113     R_XFont *tmp;
1114     char *family = translateFontFamily(gc->fontfamily, xd);
1115     /* size is in points here */
1116     int size = (int)(gc->cex * gc->ps + 0.5), face = gc->fontface;
1117 
1118     if (face < 1 || face > 5) face = 1;
1119 
1120     if (size != xd->fontsize	|| face != xd->fontface ||
1121 	strcmp(family, xd->fontfamily) != 0) {
1122 
1123 	tmp = RLoadFont(xd, family, face, size);
1124 	if(tmp) {
1125 	    xd->font = tmp;
1126 	    strcpy(xd->fontfamily, family);
1127 	    xd->fontface = face;
1128 	    xd->fontsize = size;
1129 	} else
1130 	    error(_("X11 font %s, face %d at size %d could not be loaded"),
1131 		  family, face, size);
1132     }
1133 }
1134 
CheckAlpha(int color,pX11Desc xd)1135 static void CheckAlpha(int color, pX11Desc xd)
1136 {
1137     unsigned int alpha = R_ALPHA(color);
1138     if (alpha > 0 && alpha < 255 && !xd->warn_trans) {
1139 	warning(_("semi-transparency is not supported on this device: reported only once per page"));
1140 	xd->warn_trans = TRUE;
1141     }
1142 }
1143 
SetColor(unsigned int color,pX11Desc xd)1144 static void SetColor(unsigned int color, pX11Desc xd)
1145 {
1146     if (color != xd->col) {
1147 	int col = GetX11Pixel(R_RED(color), R_GREEN(color), R_BLUE(color));
1148 	xd->col = color;
1149 	XSetState(display, xd->wgc, col, whitepixel, GXcopy, AllPlanes);
1150     }
1151 }
1152 
gcToX11lend(R_GE_lineend lend)1153 static int gcToX11lend(R_GE_lineend lend) {
1154     int newend = CapRound; /* -Wall */
1155     switch (lend) {
1156     case GE_ROUND_CAP:
1157 	newend = CapRound;
1158 	break;
1159     case GE_BUTT_CAP:
1160 	newend = CapButt;
1161 	break;
1162     case GE_SQUARE_CAP:
1163 	newend = CapProjecting;
1164 	break;
1165     default:
1166 	error(_("invalid line end"));
1167     }
1168     return newend;
1169 }
1170 
gcToX11ljoin(R_GE_linejoin ljoin)1171 static int gcToX11ljoin(R_GE_linejoin ljoin) {
1172     int newjoin = JoinRound; /* -Wall */
1173     switch (ljoin) {
1174     case GE_ROUND_JOIN:
1175 	newjoin = JoinRound;
1176 	break;
1177     case GE_MITRE_JOIN:
1178 	newjoin = JoinMiter;
1179 	break;
1180     case GE_BEVEL_JOIN:
1181 	newjoin = JoinBevel;
1182 	break;
1183     default:
1184 	error(_("invalid line join"));
1185     }
1186     return newjoin;
1187 }
1188 
1189 /* --> See "Notes on Line Textures" in GraphicsEngine.h
1190  *
1191  *	27/5/98 Paul - change to allow lty and lwd to interact:
1192  *	the line texture is now scaled by the line width so that,
1193  *	for example, a wide (lwd=2) dotted line (lty=2) has bigger
1194  *	dots which are more widely spaced.  Previously, such a line
1195  *	would have "dots" which were wide, but not long, nor widely
1196  *	spaced.
1197  */
1198 
1199 /* Not at all clear the optimization here is worth it */
SetLinetype(const pGEcontext gc,pX11Desc xd)1200 static void SetLinetype(const pGEcontext gc, pX11Desc xd)
1201 {
1202     int i, newlty, newlend, newljoin;
1203     double newlwd;
1204 
1205     newlty = gc->lty;
1206     newlwd = gc->lwd;
1207     if (newlwd < 1)/* not less than 1 pixel */
1208 	newlwd = 1;
1209     if (newlty != xd->lty || newlwd != xd->lwd ||
1210 	gc->lend != xd->lend || gc->ljoin != xd->ljoin) {
1211 	xd->lty = newlty;
1212 	xd->lwd = newlwd;
1213 	xd->lend = gc->lend;
1214 	xd->ljoin = gc->ljoin;
1215 	newlend = gcToX11lend(gc->lend);
1216 	newljoin = gcToX11ljoin(gc->ljoin);
1217 	if (newlty == 0 || newlty == NA_INTEGER) {/* special hack for lty = 0 -- only for X11 */
1218 	    XSetLineAttributes(display, xd->wgc,
1219 			       (int)(newlwd*xd->lwdscale+0.5),
1220 			       LineSolid, newlend, newljoin);
1221 	} else {
1222 	    static char dashlist[8];
1223 	    for(i = 0 ; i < 8 && (newlty != 0); i++) {
1224 		int j = newlty & 15;
1225 		if (j == 0) j = 1; /* Or we die with an X Error */
1226 		/* scale line texture for line width */
1227 		j = (int)(j*newlwd*xd->lwdscale+0.5);
1228 		/* make sure that scaled line texture */
1229 		/* does not exceed X11 storage limits */
1230 		if (j > 255) j = 255;
1231 		dashlist[i] = (char) j;
1232 		newlty >>= 4;
1233 	    }
1234 	    /* NB if i is odd the pattern will be interpreted as
1235 	       the original pattern concatenated with itself */
1236 	    XSetDashes(display, xd->wgc, 0, dashlist, i);
1237 	    XSetLineAttributes(display, xd->wgc,
1238 			       (int)(newlwd*xd->lwdscale+0.5),
1239 			       LineOnOffDash, newlend, newljoin);
1240 	}
1241     }
1242 }
1243 
1244 /* Error handling. FIXME: This is rather sloppy; we ought to respect
1245    any 3rd party handlers by checking whether dsp is "our" display and
1246    calling the previous handler otherwise. */
1247 
R_X11Err(Display * dsp,XErrorEvent * event)1248 static int R_X11Err(Display *dsp, XErrorEvent *event)
1249 {
1250     char buff[1000];
1251     /* for tcl/tk */
1252     if (event->error_code == BadWindow) return 0;
1253 
1254     XGetErrorText(dsp, event->error_code, buff, 1000);
1255     warning(_("X11 protocol error: %s"), buff);
1256     return 0;
1257 }
1258 
R_X11IOErrSimple(Display * dsp)1259 static int NORET R_X11IOErrSimple(Display *dsp)
1260 {
1261     char *dn = XDisplayName(dspname);
1262     strcpy(dspname, "");
1263     error(_("X11 I/O error while opening X11 connection to '%s'"), dn);
1264 }
1265 
R_X11IOErr(Display * dsp)1266 static int NORET R_X11IOErr(Display *dsp)
1267 {
1268     int fd = ConnectionNumber(display);
1269     /*
1270     while (nfonts--)  R_XFreeFont(display, fontcache[nfonts].font);
1271     nfonts = 0;
1272     */
1273     removeInputHandler(&R_InputHandlers,
1274 		       getInputHandler(R_InputHandlers,fd));
1275     /*
1276     XCloseDisplay(display);
1277     displayOpen = FALSE;
1278     strcpy(dspname, "");
1279     */
1280     error(_("X11 fatal IO error: please save work and shut down R"));
1281 }
1282 
1283 #define USE_Xt 1
1284 
1285 #ifdef USE_Xt
1286 #include <X11/StringDefs.h>
1287 #include <X11/Shell.h>
1288 typedef struct gx_device_X_s {
1289     Pixel background, foreground, borderColor;
1290     Dimension borderWidth;
1291     String geometry;
1292 } gx_device_X;
1293 
1294 /* (String) casts are here to suppress warnings about discarding `const' */
1295 #define RINIT(a,b,t,s,o,it,n)\
1296   {(String)(a), (String)(b), (String)t, sizeof(s),\
1297    XtOffsetOf(gx_device_X, o), (String)it, (n)}
1298 #define rpix(a,b,o,n)\
1299   RINIT(a,b,XtRPixel,Pixel,o,XtRString,(XtPointer)(n))
1300 #define rdim(a,b,o,n)\
1301   RINIT(a,b,XtRDimension,Dimension,o,XtRImmediate,(XtPointer)(n))
1302 #define rstr(a,b,o,n)\
1303   RINIT(a,b,XtRString,String,o,XtRString,(char*)(n))
1304 
1305 static XtResource x_resources[] = {
1306     rpix(XtNbackground, XtCBackground, background, "XtDefaultBackground"),
1307     rstr(XtNgeometry, XtCGeometry, geometry, NULL),
1308 };
1309 
1310 static const int x_resource_count = XtNumber(x_resources);
1311 
1312 static String x_fallback_resources[] = {
1313     (String) "R_x11*Background: white",
1314     NULL
1315 };
1316 #endif
1317 
1318 Rboolean
X11_Open(pDevDesc dd,pX11Desc xd,const char * dsp,double w,double h,double gamma_fac,X_COLORTYPE colormodel,int maxcube,int bgcolor,int canvascolor,int res,int xpos,int ypos)1319 X11_Open(pDevDesc dd, pX11Desc xd, const char *dsp,
1320 	 double w, double h, double gamma_fac, X_COLORTYPE colormodel,
1321 	 int maxcube, int bgcolor, int canvascolor, int res,
1322 	 int xpos, int ypos)
1323 {
1324     /* if we have to bail out with "error", then must free(dd) and free(xd) */
1325     /* That means the *caller*: the X11DeviceDriver code frees xd, for example */
1326 
1327     XEvent event;
1328     int iw, ih, blackpixel;
1329     X_GTYPE type;
1330     const char *p = dsp;
1331     XGCValues gcv;
1332     XSizeHints *hint;
1333 
1334     if (!XSupportsLocale ())
1335 	warning(_("locale not supported by Xlib: some X ops will operate in C locale"));
1336     if (!XSetLocaleModifiers ("")) warning(_("X cannot set locale modifiers"));
1337 
1338     if (!strncmp(dsp, "png::", 5)) {
1339 #ifndef HAVE_PNG
1340 	warning(_("no png support in this version of R"));
1341 	return FALSE;
1342 #else
1343 	char buf[PATH_MAX]; /* allow for pageno formats */
1344 	FILE *fp;
1345 	if(strlen(dsp+5) >= PATH_MAX)
1346 	    error(_("filename too long in png() call"));
1347 	strcpy(xd->filename, dsp+5);
1348 	snprintf(buf, PATH_MAX, dsp+5, 1); /* page 1 to start */
1349 	if (!(fp = R_fopen(R_ExpandFileName(buf), "w"))) {
1350 	    warning(_("could not open PNG file '%s'"), buf);
1351 	    return FALSE;
1352 	}
1353 	xd->fp = fp;
1354 	type = PNG;
1355 	p = "";
1356 	xd->res_dpi = res; /* place holder */
1357 	dd->displayListOn = FALSE;
1358 #endif
1359     }
1360     else if (!strncmp(dsp, "jpeg::", 6)) {
1361 #ifndef HAVE_JPEG
1362 	warning(_("no jpeg support in this version of R"));
1363 	return FALSE;
1364 #else
1365 	char buf[PATH_MAX]; /* allow for pageno formats */
1366 	char tmp[PATH_MAX], *pp;
1367 	FILE *fp;
1368 	strcpy(tmp, dsp+6);
1369 	pp = strchr(tmp, ':'); *pp='\0';
1370 	xd->quality = atoi(dsp+6);
1371 	if(strlen(pp+1) >= PATH_MAX)
1372 	    error(_("filename too long in jpeg() call"));
1373 	strcpy(xd->filename, pp+1);
1374 	snprintf(buf, PATH_MAX, pp+1, 1); /* page 1 to start */
1375 	if (!(fp = R_fopen(R_ExpandFileName(buf), "w"))) {
1376 	    warning(_("could not open JPEG file '%s'"), buf);
1377 	    return FALSE;
1378 	}
1379 	xd->fp = fp;
1380 	type = JPEG;
1381 	p = "";
1382 	xd->res_dpi = res; /* place holder */
1383 	dd->displayListOn = FALSE;
1384 #endif
1385     }
1386     else if (!strncmp(dsp, "tiff::", 5)) {
1387 #ifndef HAVE_TIFF
1388 	warning(_("no tiff support in this version of R"));
1389 	return FALSE;
1390 #else
1391 	char tmp[PATH_MAX], *pp;
1392 	strcpy(tmp, dsp+6);
1393 	pp = strchr(tmp, ':'); *pp='\0';
1394 	xd->quality = atoi(dsp+6);
1395 	if(strlen(pp+1) >= PATH_MAX)
1396 	    error(_("filename too long in tiff() call"));
1397 	strcpy(xd->filename, pp+1);
1398 	xd->fp = NULL;
1399 	type = TIFF;
1400 	p = "";
1401 	xd->res_dpi = res; /* place holder */
1402 	dd->displayListOn = FALSE;
1403 #endif
1404     } else if (!strncmp(dsp, "bmp::", 5)) {
1405 	char buf[PATH_MAX]; /* allow for pageno formats */
1406 	FILE *fp;
1407 	if(strlen(dsp+5) >= PATH_MAX)
1408 	    error(_("filename too long in bmp() call"));
1409 	strcpy(xd->filename, dsp+5);
1410 	snprintf(buf, PATH_MAX, dsp+5, 1); /* page 1 to start */
1411 	if (!(fp = R_fopen(R_ExpandFileName(buf), "w"))) {
1412 	    warning(_("could not open BMP file '%s'"), buf);
1413 	    return FALSE;
1414 	}
1415 	xd->fp = fp;
1416 	type = BMP;
1417 	p = "";
1418 	xd->res_dpi = res; /* place holder */
1419 	dd->displayListOn = FALSE;
1420     } else if (!strcmp(dsp, "XImage")) {
1421 	type = XIMAGE;
1422 	xd->fp = NULL;
1423 	p = "";
1424     }
1425     else type = WINDOW;
1426     xd->type = type;
1427 
1428     /* If there is no server connection, establish one and */
1429     /* initialize the X11 device driver data structures. */
1430 
1431     if (!displayOpen) {
1432 	/* Bill Dunlap sees an error when tunneling to a non-existent
1433 	   X11 connection that BDR cannot reproduce.  We leave a handler set
1434 	   if we get an error, but that is rare.
1435 	*/
1436 	X11IOhandler old;
1437 	strncpy(dspname, p, 101);
1438 	dspname[100] = '\0';
1439 	old = XSetIOErrorHandler(R_X11IOErrSimple);
1440 	if ((display = XOpenDisplay(p)) == NULL) {
1441 	    XSetIOErrorHandler(old);
1442 	    warning(_("unable to open connection to X11 display '%s'"), p);
1443 	    return FALSE;
1444 	}
1445 	XSetIOErrorHandler(old);
1446 	Rf_setX11Display(display, gamma_fac, colormodel, maxcube, TRUE);
1447 	displayOpen = TRUE;
1448 	if(xd->handleOwnEvents == FALSE)
1449 	    addInputHandler(R_InputHandlers, ConnectionNumber(display),
1450 			    R_ProcessX11Events, XActivity);
1451     } else if(strcmp(p, dspname))
1452 	warning(_("ignoring 'display' argument as an X11 device is already open"));
1453     whitepixel = GetX11Pixel(R_RED(canvascolor), R_GREEN(canvascolor),
1454 			     R_BLUE(canvascolor));
1455     blackpixel = GetX11Pixel(0, 0, 0);
1456 #ifdef HAVE_WORKING_X11_CAIRO
1457     if(xd->useCairo && Vclass != TrueColor) {
1458 	warning(_("cairo-based types may only work correctly on TrueColor visuals"));
1459     }
1460 #endif
1461 
1462     /* Foreground and Background Colors */
1463 
1464     xd->fill = bgcolor; /* was transparent */
1465     xd->col = R_RGB(0, 0, 0);
1466     xd->canvas = canvascolor;
1467     if(type == JPEG && !R_OPAQUE(xd->canvas)) {
1468 	warning(_("jpeg() does not support transparency: using white bg"));
1469 	xd->canvas = 0xffffff;
1470     }
1471     if(type > WINDOW) xd->fill = xd->canvas;
1472 
1473 
1474     /* Try to create a simple window. */
1475     /* We want to know about exposures */
1476     /* and window-resizes and locations. */
1477 
1478     /*
1479      * <MBCS-FIXED>: R on gnome window manager task-bar button see?
1480      * I try it.
1481      * A button such as, maximization disappears
1482      * unless I give Hint for clear statement in
1483      * gnome window manager.
1484      */
1485 
1486     memset(&attributes, 0, sizeof(attributes));
1487     attributes.background_pixel = whitepixel;
1488     attributes.border_pixel = blackpixel;
1489     attributes.backing_store = NotUseful;
1490     attributes.event_mask = ButtonPressMask
1491       | PointerMotionMask
1492       | PointerMotionHintMask
1493       | ButtonReleaseMask
1494       | ExposureMask
1495       | StructureNotifyMask
1496       | KeyPressMask;
1497 
1498 
1499     if (type == WINDOW) {
1500 	int alreadyCreated = (xd->window != (Window)NULL);
1501 	if(alreadyCreated == 0) {
1502 	    xd->windowWidth = iw = (int)((ISNA(w)?7:w)/pixelWidth());
1503 	    xd->windowHeight = ih = (int)((ISNA(h)?7:h)/pixelHeight());
1504 
1505 	    hint = XAllocSizeHints();
1506 	    if(xpos == NA_INTEGER)
1507 		hint->x = numX11Devices*20 %
1508 		    ( DisplayWidth(display, screen) - iw - 10 );
1509 	    else hint->x = (xpos >= 0) ? xpos :
1510 		DisplayWidth(display, screen) - iw + xpos;
1511 
1512 	    if(ypos == NA_INTEGER)
1513 		hint->y = numX11Devices*20 %
1514 		    ( DisplayHeight(display, screen) + ih - 10 );
1515 	    else hint->y = (ypos >= 0)? ypos :
1516 		DisplayHeight(display, screen) - iw - ypos;
1517 	    hint->width  = iw;
1518 	    hint->height = ih;
1519 	    hint->flags  = PPosition | PSize;
1520 #ifdef USE_Xt
1521 	    {
1522 		XtAppContext app_con;
1523 		Widget toplevel;
1524 		Display *xtdpy;
1525 		int zero = 0;
1526 		gx_device_X xdev;
1527 
1528 		XtToolkitInitialize();
1529 
1530 		app_con = XtCreateApplicationContext();
1531 		XtAppSetFallbackResources(app_con, x_fallback_resources);
1532 		xtdpy = XtOpenDisplay(app_con, dspname, "r_x11", "R_x11",
1533 				      NULL, 0, &zero, NULL);
1534 		if(xtdpy) {
1535 		    toplevel = XtAppCreateShell(NULL, "R_x11",
1536 						applicationShellWidgetClass,
1537 						xtdpy, NULL, 0);
1538 		    XtGetApplicationResources(toplevel, (XtPointer) &xdev,
1539 					      x_resources,
1540 					      x_resource_count,
1541 					      NULL, 0);
1542 		    if (xdev.geometry != NULL) {
1543 			char gstr[40];
1544 			int bitmask;
1545 
1546 			sprintf(gstr, "%dx%d+%d+%d", hint->width,
1547 				hint->height, hint->x, hint->y);
1548 			bitmask = XWMGeometry(display, DefaultScreen(display),
1549 					      xdev.geometry, gstr,
1550 					      1,
1551 					      hint,
1552 					      &hint->x, &hint->y,
1553 					      &hint->width, &hint->height,
1554 					      &hint->win_gravity);
1555 
1556 			if (bitmask & (XValue | YValue))
1557 			    hint->flags |= USPosition;
1558 			if (bitmask & (WidthValue | HeightValue))
1559 			    hint->flags |= USSize;
1560 			/* Restore user-specified settings */
1561 			if(xpos != NA_INTEGER)
1562 			    hint->x = (xpos >= 0) ? xpos :
1563 				DisplayWidth(display, screen) - iw + xpos;
1564 			if(ypos != NA_INTEGER)
1565 			    hint->y = (ypos >= 0)? ypos :
1566 				DisplayHeight(display, screen) - iw - ypos;
1567 			if(!ISNA(w)) hint->width = iw;
1568 			if(!ISNA(h)) hint->height = ih;
1569 		    }
1570 		    XtDestroyWidget(toplevel);
1571 		    XtCloseDisplay(xtdpy);
1572 		} else {
1573 		    warning(_("unable to obtain information on display '%s'"),
1574 			    dsp);
1575 		}
1576 		XtDestroyApplicationContext(app_con);
1577 	    }
1578 #endif
1579 	    xd->windowWidth = hint->width;
1580 	    xd->windowHeight = hint->height;
1581 	    /*printf("x = %d, y = %d\n", hint->x, hint->y);*/
1582 	    xd->window = XCreateSimpleWindow(display,
1583 					     rootwin,
1584 					     hint->x,hint->y,
1585 					     hint->width, hint->height,
1586 					     1,
1587 					     blackpixel,
1588 					     whitepixel);
1589 	    if (xd->window == 0 ) {
1590 	      XFree(hint);
1591 	      warning(_("unable to create X11 window"));
1592 	      return FALSE;
1593 	    }
1594 	    XSetWMNormalHints(display, xd->window, hint);
1595 	    XFree(hint);
1596 	    XChangeWindowAttributes(display, xd->window,
1597 				    CWEventMask | CWBackPixel |
1598 				    CWBorderPixel | CWBackingStore,
1599 				    &attributes);
1600 
1601 	    XStoreName(display, xd->window, xd->title);
1602 
1603 	    /* See (PR#14588) */
1604 	    XClassHint *chint;
1605 	    chint = XAllocClassHint();
1606 	    if (chint) {
1607 		chint->res_name = "r_x11";
1608 		chint->res_class = "R_x11";
1609 		XSetClassHint(display, xd->window, chint);
1610 	    	XFree(chint);
1611 	    }
1612 
1613             /* set window icon */
1614             XChangeProperty(display, xd->window,
1615                             XInternAtom(display, "_NET_WM_ICON", False),
1616                             XInternAtom(display, "CARDINAL", False), 32,
1617                             PropModeReplace,
1618                             (const unsigned char*) rlogo_icon, 2 + 99*77);
1619 
1620 	    /* set the window group leader */
1621 	    XWMHints * hints;
1622 	    hints = XAllocWMHints();
1623 	    if (hints) {
1624 		hints->window_group = group_leader;
1625 		hints->flags |= WindowGroupHint;
1626 		XSetWMHints(display, xd->window, hints);
1627 		XFree(hints);
1628 	    }
1629 
1630 	    /* set up protocols so that window manager sends */
1631 	    /* me an event when user "destroys" window */
1632 	    _XA_WM_PROTOCOLS = XInternAtom(display, "WM_PROTOCOLS", 0);
1633 	    protocol = XInternAtom(display, "WM_DELETE_WINDOW", 0);
1634 	    XSetWMProtocols(display, xd->window, &protocol, 1);
1635 
1636 	    if(!arrow_cursor)
1637 		arrow_cursor = XCreateFontCursor(display, XC_left_ptr) ;
1638 	    if(!cross_cursor)
1639 		cross_cursor = XCreateFontCursor(display, XC_crosshair);
1640 	    if(!watch_cursor)
1641 		watch_cursor = XCreateFontCursor(display, XC_watch) ;
1642 	    if(xd->type==WINDOW) XDefineCursor(display, xd->window, arrow_cursor);
1643 
1644 #ifdef HAVE_WORKING_X11_CAIRO
1645 	    if(xd->useCairo) {
1646 		cairo_status_t res;
1647 		if(xd->buffered) {
1648 		    xd->xcs =
1649 			cairo_xlib_surface_create(display, xd->window,
1650 						  visual,
1651 						  xd->windowWidth,
1652 						  xd->windowHeight);
1653 		    res = cairo_surface_status(xd->xcs);
1654 		    if (res != CAIRO_STATUS_SUCCESS) {
1655 			warning("cairo error '%s'",
1656 				cairo_status_to_string(res));
1657 			/* bail out */
1658 			return FALSE;
1659 		    }
1660 		    xd->xcc = cairo_create(xd->xcs);
1661 		    res = cairo_status(xd->xcc);
1662 		    if (res != CAIRO_STATUS_SUCCESS) {
1663 			warning("cairo error '%s'",
1664 				cairo_status_to_string(res));
1665 			cairo_surface_destroy(xd->xcs);
1666 			/* bail out */
1667 			return FALSE;
1668 		    }
1669 		    xd->cs =
1670 			cairo_image_surface_create(CAIRO_FORMAT_RGB24,
1671 						   xd->windowWidth,
1672 						   xd->windowHeight);
1673 		    cairo_set_source_surface (xd->xcc, xd->cs, 0, 0);
1674 		    if(xd->buffered > 1) addBuffering(xd);
1675 		} else /* non-buffered */
1676 		    xd->cs =
1677 			cairo_xlib_surface_create(display, xd->window,
1678 						  visual,
1679 						  xd->windowWidth,
1680 						  xd->windowHeight);
1681 
1682 		res = cairo_surface_status(xd->cs);
1683 		if (res != CAIRO_STATUS_SUCCESS) {
1684 		    warning("cairo error '%s'", cairo_status_to_string(res));
1685 		    /* bail out */
1686 		    if(xd->xcs) cairo_surface_destroy(xd->xcs);
1687 		    if(xd->xcc) cairo_destroy(xd->xcc);
1688 		    return FALSE;
1689 		}
1690 		xd->cc = cairo_create(xd->cs);
1691 		res = cairo_status(xd->cc);
1692 		if (res != CAIRO_STATUS_SUCCESS) {
1693 		    warning("cairo error '%s'", cairo_status_to_string(res));
1694 		    cairo_surface_destroy(xd->cs);
1695 		    /* bail out */
1696 		    if(xd->xcs) cairo_surface_destroy(xd->xcs);
1697 		    if(xd->xcc) cairo_destroy(xd->xcc);
1698 		    return FALSE;
1699 		}
1700 		cairo_set_operator(xd->cc, CAIRO_OPERATOR_OVER);
1701 		cairo_set_antialias(xd->cc, xd->antialias);
1702 		CairoColor(xd->canvas, xd);
1703 		cairo_new_path(xd->cc);
1704 		cairo_paint(xd->cc);
1705 	    }
1706 
1707             CairoInitPatterns(xd);
1708             CairoInitClipPaths(xd);
1709             CairoInitMasks(xd);
1710             xd->appending = 0;
1711 #endif
1712 	}
1713 	/* Save the pDevDesc with the window for event dispatching */
1714 	XSaveContext(display, xd->window, devPtrContext, (caddr_t) dd);
1715 
1716 	/* Map the window */
1717 	if(alreadyCreated == 0) {
1718 	    XSelectInput(display, xd->window,
1719 			 ExposureMask | ButtonPressMask | StructureNotifyMask
1720 			 | ButtonReleaseMask | PointerMotionMask
1721                          | PointerMotionHintMask | KeyPressMask);
1722 	    XMapWindow(display, xd->window);
1723 	    XSync(display, 0);
1724 
1725 	    /* Gobble MapNotify events */
1726 
1727 	    while ( XPeekEvent(display, &event),
1728 		    !XCheckTypedEvent(display, MapNotify, &event))
1729 		;
1730 	    /* XNextEvent(display, &event);
1731 	       if (event.xany.type == Expose) {
1732 	       while (event.xexpose.count)
1733 	       XNextEvent(display, &event);
1734 	       }
1735 	    */
1736 	}
1737     } else { /* PIXMAP */
1738 	xd->windowWidth = iw = (int) w;
1739 	xd->windowHeight = ih = (int) h;
1740 	if (iw < 20 && ih < 20)
1741 	    warning(_("'width=%d, height=%d' are unlikely values in pixels"),
1742 		    iw, ih);
1743 	if ((xd->window = XCreatePixmap(
1744 	    display, rootwin,
1745 	    iw, ih, DefaultDepth(display, screen))) == 0) {
1746 	    warning(_("unable to create pixmap"));
1747 	    return FALSE;
1748 	}
1749 	/* Save the pDevDesc with the window for event dispatching */
1750 	/* Is this needed? */
1751 	XSaveContext(display, xd->window, devPtrContext, (caddr_t) dd);
1752 	xd->npages = 0;
1753     }
1754 
1755     /* Set the graphics context */
1756 
1757     gcv.arc_mode = ArcChord;
1758     xd->wgc = XCreateGC(display, xd->window, GCArcMode, &gcv);
1759     XSetState(display, xd->wgc, blackpixel, whitepixel, GXcopy, AllPlanes);
1760 
1761     /* ensure that line drawing is set up at the first graphics call */
1762     xd->lty = -1;
1763     xd->lwd = -1;
1764     xd->lend = 0;
1765     xd->ljoin = 0;
1766 
1767     numX11Devices++;
1768     return TRUE;
1769 }
1770 
1771 /* Return a non-relocatable copy of a string */
1772 
SaveFontSpec(SEXP sxp,int offset)1773 static char *SaveFontSpec(SEXP sxp, int offset)
1774 {
1775     char *s;
1776     if(!isString(sxp) || length(sxp) <= offset)
1777 	error(_("invalid font specification"));
1778     s = R_alloc(strlen(CHAR(STRING_ELT(sxp, offset)))+1, sizeof(char));
1779     strcpy(s, CHAR(STRING_ELT(sxp, offset)));
1780     return s;
1781 }
1782 
1783 /*
1784  * Take the fontfamily from a gcontext (which is device-independent)
1785  * and convert it into an X11-specific font description using
1786  * the X11 font database (see src/library/graphics/R/unix/x11.R)
1787  *
1788  * IF gcontext fontfamily is empty ("")
1789  * OR IF can't find gcontext fontfamily in font database
1790  * THEN return xd->basefontfamily (the family set up when the
1791  *   device was created)
1792  */
translateFontFamily(char * family,pX11Desc xd)1793 static char* translateFontFamily(char* family, pX11Desc xd)
1794 {
1795     SEXP graphicsNS, x11env, fontdb, fontnames;
1796     int i, nfonts;
1797     char* result = xd->basefontfamily;
1798     PROTECT_INDEX xpi;
1799 
1800     PROTECT(graphicsNS = R_FindNamespace(ScalarString(mkChar("grDevices"))));
1801     PROTECT_WITH_INDEX(x11env = findVar(install(".X11env"), graphicsNS), &xpi);
1802     if(TYPEOF(x11env) == PROMSXP)
1803 	REPROTECT(x11env = eval(x11env, graphicsNS), xpi);
1804     PROTECT(fontdb = findVar(install(".X11.Fonts"), x11env));
1805     PROTECT(fontnames = getAttrib(fontdb, R_NamesSymbol));
1806     nfonts = LENGTH(fontdb);
1807     if (family[0]) {
1808 	Rboolean found = FALSE;
1809 	for (i = 0; i < nfonts && !found; i++) {
1810 	    const char* fontFamily = CHAR(STRING_ELT(fontnames, i));
1811 	    if (strcmp(family, fontFamily) == 0) {
1812 		found = TRUE;
1813 		result = SaveFontSpec(VECTOR_ELT(fontdb, i), 0);
1814 	    }
1815 	}
1816 	if (!found)
1817 	    warning(_("font family not found in X11 font database"));
1818     }
1819     UNPROTECT(4);
1820     return result;
1821 }
1822 
X11_StrWidth(const char * str,const pGEcontext gc,pDevDesc dd)1823 static double X11_StrWidth(const char *str, const pGEcontext gc, pDevDesc dd)
1824 {
1825     pX11Desc xd = (pX11Desc) dd->deviceSpecific;
1826 
1827     SetFont(gc, xd);
1828 
1829     if (xd->font->type == One_Font)
1830 	return (double) XTextWidth(xd->font->font, str, (int)strlen(str));
1831     else  {
1832 #ifdef HAVE_XUTF8TEXTESCAPEMENT
1833 	if(utf8locale)
1834 	    return (double) Xutf8TextEscapement(xd->font->fontset,
1835 						str, (int)strlen(str));
1836 	else
1837 #endif
1838 	    return (double) XmbTextEscapement(xd->font->fontset,
1839 					      str, (int)strlen(str));
1840     }
1841 }
1842 
1843 
1844 	/* Character Metric Information */
1845 	/* Passing c == 0 gets font information */
1846 
X11_MetricInfo(int c,const pGEcontext gc,double * ascent,double * descent,double * width,pDevDesc dd)1847 static void X11_MetricInfo(int c, const pGEcontext gc,
1848 			   double* ascent, double* descent,
1849 			   double* width, pDevDesc dd)
1850 {
1851     pX11Desc xd = (pX11Desc) dd->deviceSpecific;
1852     int first = 0, last = 0;
1853     XFontStruct *f = NULL;
1854 
1855     if (c < 0)
1856 	error(_("invalid use of %d < 0 in '%s'"), c, "X11_MetricInfo");
1857 
1858     SetFont(gc, xd);
1859 
1860     *ascent = 0; *descent = 0; *width = 0; /* fallback position */
1861     if (xd->font) {
1862 	if (xd->font->type != One_Font) {
1863 	    char **ml; XFontStruct **fs_list;
1864 #ifdef DEBUG_X11
1865 	    int i, cnt = XFontsOfFontSet(xd->font->fontset, &fs_list, &ml);
1866 
1867 	    for (i = 0; i < cnt; i++) printf("%s\n", ml[i]);
1868 	    printf("--- end of fontlist ---\n\n");
1869 #else
1870 	    XFontsOfFontSet(xd->font->fontset, &fs_list, &ml);
1871 #endif
1872 
1873 	    f = fs_list[0];
1874 	} else f = xd->font->font;
1875 	first = f->min_char_or_byte2;
1876 	last = f->max_char_or_byte2;
1877     } else return;
1878 
1879     if (c == 0) {
1880 	*ascent = f->ascent;
1881 	*descent = f->descent;
1882 	*width = f->max_bounds.width;
1883 	return;
1884     }
1885 
1886     if (xd->font->type != One_Font) {  /* so an MBCS */
1887 	XRectangle ink, log;
1888 	char buf[16];
1889 
1890 	ucstomb(buf, (unsigned int) c);
1891 #ifdef HAVE_XUTF8TEXTEXTENTS
1892 	if(utf8locale)
1893 	    Xutf8TextExtents(xd->font->fontset, buf, (int)strlen(buf), &ink, &log);
1894 	else
1895 #endif
1896 	    XmbTextExtents(xd->font->fontset, buf, (int)strlen(buf), &ink, &log);
1897 	/* Rprintf("%d %d %d %d\n", ink.x, ink.y, ink.width, ink.height);
1898 	   Rprintf("%d %d %d %d\n", log.x, log.y, log.width, log.height); */
1899 	*ascent = -ink.y;
1900 	*descent = ink.y + ink.height;
1901 	/* <FIXME> why logical and not ink width? */
1902 	*width = log.width;
1903 	/* Rprintf("%d %lc w=%f a=%f d=%f\n", c, wc[0],
1904 		    *width, *ascent, *descent);*/
1905     } else { /* symbol font */
1906 	if(first <= c && c <= last) {
1907 	  /*
1908 	   * <MBCS-FIXED>: try demo(lm.glm,package="stats")
1909 	   * per_char is NULL case.
1910 	   */
1911 	  if(f->per_char) {
1912 	    *ascent = f->per_char[c-first].ascent;
1913 	    *descent = f->per_char[c-first].descent;
1914 	    *width = f->per_char[c-first].width;
1915 	  } else {
1916 	    *ascent = f->max_bounds.ascent;
1917 	    *descent = f->max_bounds.descent;
1918 	    *width = f->max_bounds.width;
1919 	  }
1920 	}
1921     }
1922 }
1923 
X11_Clip(double x0,double x1,double y0,double y1,pDevDesc dd)1924 static void X11_Clip(double x0, double x1, double y0, double y1,
1925 			pDevDesc dd)
1926 {
1927     pX11Desc xd = (pX11Desc) dd->deviceSpecific;
1928 
1929     if (x0 < x1) {
1930 	xd->clip.x = (unsigned short) x0 ;
1931 	xd->clip.width = (unsigned short) x1 - (unsigned short) x0 + 1;
1932     }
1933     else {
1934 	xd->clip.x = (unsigned short) x1;
1935 	xd->clip.width = (unsigned short) x0 - (unsigned short) x1 + 1;
1936     }
1937 
1938     if (y0 < y1) {
1939 	xd->clip.y = (unsigned short) y0;
1940 	xd->clip.height = (unsigned short) y1 -  (unsigned short) y0 + 1;
1941     }
1942     else {
1943 	xd->clip.y = (unsigned short) y1;
1944 	xd->clip.height = (unsigned short) y0 - (unsigned short) y1 + 1;
1945     }
1946 
1947     XSetClipRectangles(display, xd->wgc, 0, 0, &(xd->clip), 1, Unsorted);
1948 }
1949 
X11_Size(double * left,double * right,double * bottom,double * top,pDevDesc dd)1950 static void X11_Size(double *left, double *right,
1951 		     double *bottom, double *top,
1952 		     pDevDesc dd)
1953 {
1954     pX11Desc xd = (pX11Desc) dd->deviceSpecific;
1955 
1956     *left = 0.0;
1957     *right = xd->windowWidth;
1958     *bottom = xd->windowHeight;
1959     *top = 0.0;
1960 }
1961 
X11_NewPage(const pGEcontext gc,pDevDesc dd)1962 static void X11_NewPage(const pGEcontext gc, pDevDesc dd)
1963 {
1964     pX11Desc xd = (pX11Desc) dd->deviceSpecific;
1965 
1966     xd->warn_trans = FALSE;
1967     if (xd->type > WINDOW) {
1968 	if (xd->npages++) {
1969 	    /* try to preserve the page we do have */
1970 	    if (xd->type != XIMAGE) X11_Close_bitmap(xd);
1971 	    if (xd->type != XIMAGE && xd->fp != NULL) fclose(xd->fp);
1972 	    if (xd->type == PNG || xd->type == JPEG || xd->type == BMP) {
1973 		char buf[PATH_MAX];
1974 		snprintf(buf, PATH_MAX, xd->filename, xd->npages);
1975 		xd->fp = R_fopen(R_ExpandFileName(buf), "w");
1976 		if (!xd->fp)
1977 		    error(_("could not open file '%s'"), buf);
1978 	    }
1979 	}
1980 	CheckAlpha(gc->fill, xd);
1981 	xd->fill = R_OPAQUE(gc->fill) ? gc->fill: PNG_TRANS;
1982 	SetColor(xd->fill, xd);
1983 	xd->clip.x = 0; xd->clip.width = (unsigned short)xd->windowWidth;
1984 	xd->clip.y = 0; xd->clip.height = (unsigned short)xd->windowHeight;
1985 	XSetClipRectangles(display, xd->wgc, 0, 0, &(xd->clip), 1, Unsorted);
1986 	XFillRectangle(display, xd->window, xd->wgc, 0, 0,
1987 		       xd->windowWidth, xd->windowHeight);
1988 	return;
1989     }
1990 
1991     FreeX11Colors();
1992     if ( (model == PSEUDOCOLOR2) || (xd->fill != gc->fill)) {
1993 	xd->fill = R_OPAQUE(gc->fill) ? gc->fill : xd->canvas;
1994 	whitepixel = GetX11Pixel(R_RED(xd->fill),R_GREEN(xd->fill),R_BLUE(xd->fill));
1995 	XSetWindowBackground(display, xd->window, whitepixel);
1996     }
1997     XClearWindow(display, xd->window);
1998     XSync(display, 0);
1999 }
2000 
2001 #include "bitmap.h"
2002 
2003 static int knowncols[512];
2004 
bitgp(void * xi,int x,int y)2005 static unsigned int bitgp(void *xi, int x, int y)
2006 {
2007     int i, r, g, b;
2008     XColor xcol;
2009 
2010     /*	returns the colour of the (x,y) pixel stored as RGB */
2011     i = (int) XGetPixel((XImage *) xi, y, x);
2012     switch(model) {
2013     case MONOCHROME:
2014 	return i == 0 ? 0xFFFFFFFF : 0;
2015     case GRAYSCALE:
2016     case PSEUDOCOLOR1:
2017     case PSEUDOCOLOR2:
2018 	if (i < 512) {
2019 	    if (knowncols[i] < 0) {
2020 		xcol.pixel = i;
2021 		XQueryColor(display, colormap, &xcol);
2022 		knowncols[i] = ((xcol.red>>8)<<16) | ((xcol.green>>8)<<8)
2023 		    | (xcol.blue>>8);
2024 	    }
2025 	    return knowncols[i] | 0xFF000000;
2026 	} else {
2027 	    xcol.pixel = i;
2028 	    XQueryColor(display, colormap, &xcol);
2029 	    return ((xcol.red>>8)<<16) | ((xcol.green>>8)<<8) | (xcol.blue>>8);
2030 	}
2031     case TRUECOLOR:
2032 	r = ((i>>RShift)&RMask) * 255 /(RMask);
2033 	g = ((i>>GShift)&GMask) * 255 /(GMask);
2034 	b = ((i>>BShift)&BMask) * 255 /(BMask);
2035 	return (r<<16) | (g<<8) | b | 0xFF000000;
2036     default:
2037 	return 0;
2038     }
2039     /* return 0;  not reached, needed for some compilers */
2040 }
2041 
X11_Close_bitmap(pX11Desc xd)2042 static void X11_Close_bitmap(pX11Desc xd)
2043 {
2044     int i;
2045     XImage *xi;
2046     for (i = 0; i < 512; i++) knowncols[i] = -1;
2047     xi = XGetImage(display, xd->window, 0, 0,
2048 		   xd->windowWidth, xd->windowHeight,
2049 		   AllPlanes, ZPixmap);
2050     if (xd->type == PNG) {
2051 	unsigned int pngtrans = PNG_TRANS;
2052 	if(model == TRUECOLOR) {
2053 	    int i, r, g, b;
2054 	    /* some 'truecolor' displays distort colours */
2055 	    i = GetX11Pixel(R_RED(PNG_TRANS),
2056 			    R_GREEN(PNG_TRANS),
2057 			    R_BLUE(PNG_TRANS));
2058 	    r = ((i>>RShift)&RMask) * 255 /(RMask);
2059 	    g = ((i>>GShift)&GMask) * 255 /(GMask);
2060 	    b = ((i>>BShift)&BMask) * 255 /(BMask);
2061 	    pngtrans = (r<<16) | (g<<8) | b | 0xFF000000;
2062 	}
2063 	R_SaveAsPng(xi, xd->windowWidth, xd->windowHeight,
2064 		    bitgp, 0, xd->fp,
2065 		    (xd->fill != PNG_TRANS) ? 0 : pngtrans, xd->res_dpi);
2066     } else if (xd->type == JPEG)
2067 	R_SaveAsJpeg(xi, xd->windowWidth, xd->windowHeight,
2068 		     bitgp, 0, xd->quality, xd->fp, xd->res_dpi);
2069     else if (xd->type == BMP)
2070 	R_SaveAsBmp(xi, xd->windowWidth, xd->windowHeight,
2071 		    bitgp, 0, xd->fp, xd->res_dpi);
2072     else if (xd->type == TIFF) {
2073 	char buf[PATH_MAX];
2074 	snprintf(buf, PATH_MAX, xd->filename, xd->npages);
2075 	R_SaveAsTIFF(xi, xd->windowWidth, xd->windowHeight,
2076 		     bitgp, 0, R_ExpandFileName(buf), xd->res_dpi,
2077 		     xd->quality);
2078     }
2079 
2080     XDestroyImage(xi);
2081 }
2082 
X11_Close(pDevDesc dd)2083 static void X11_Close(pDevDesc dd)
2084 {
2085     pX11Desc xd = (pX11Desc) dd->deviceSpecific;
2086 
2087     if (xd->type == WINDOW) {
2088 #ifdef HAVE_WORKING_X11_CAIRO
2089 	if(xd->buffered > 1) removeBuffering(xd);
2090 #endif
2091 	/* process pending events */
2092 	/* set block on destroy events */
2093 	inclose = TRUE;
2094 	R_ProcessX11Events((void*) NULL);
2095 
2096 #ifdef HAVE_WORKING_X11_CAIRO
2097 	if(xd->useCairo) {
2098             CairoDestroyMasks(xd);
2099             CairoDestroyClipPaths(xd);
2100             CairoDestroyPatterns(xd);
2101 	    if(xd->cs) cairo_surface_destroy(xd->cs);
2102 	    if(xd->cc) cairo_destroy(xd->cc);
2103 	    if(xd->xcs) cairo_surface_destroy(xd->xcs);
2104 	    if(xd->xcc) cairo_destroy(xd->xcc);
2105 	}
2106 #endif
2107 
2108 	XFreeGC(display, xd->wgc);
2109 	XDestroyWindow(display, xd->window);
2110 	XSync(display, 0);
2111     } else {
2112 	if (xd->npages && xd->type != XIMAGE) X11_Close_bitmap(xd);
2113 	XFreeGC(display, xd->wgc);
2114 	XFreePixmap(display, xd->window);
2115 	if (xd->type != XIMAGE && xd->fp != NULL) fclose(xd->fp);
2116     }
2117 
2118     numX11Devices--;
2119     if (numX11Devices == 0)  {
2120 	int fd = ConnectionNumber(display);
2121 	/* Free Resources Here */
2122 	XDestroyWindow(display, group_leader);
2123 	while (nfonts--)
2124 	      R_XFreeFont(display, fontcache[nfonts].font);
2125 	nfonts = 0;
2126 	if(xd->handleOwnEvents == FALSE)
2127 	    removeInputHandler(&R_InputHandlers,
2128 			       getInputHandler(R_InputHandlers,fd));
2129 	if(arrow_cursor) XFreeCursor(display, arrow_cursor);
2130 	if(cross_cursor) XFreeCursor(display, cross_cursor);
2131 	if(watch_cursor) XFreeCursor(display, watch_cursor);
2132 	arrow_cursor = cross_cursor = watch_cursor = (Cursor) 0;
2133 	XCloseDisplay(display);
2134 	displayOpen = FALSE;
2135     }
2136 
2137     free(xd);
2138     inclose = FALSE;
2139 }
2140 
X11_Activate(pDevDesc dd)2141 static void X11_Activate(pDevDesc dd)
2142 {
2143     char t[150];
2144     pX11Desc xd = (pX11Desc) dd->deviceSpecific;
2145 
2146     if (xd->type > WINDOW) return;
2147     if(xd->title[0]) {
2148 	snprintf(t, 140, xd->title, ndevNumber(dd) + 1);
2149 	t[139] = '\0';
2150     } else {
2151 	sprintf(t, "R Graphics: Device %d", ndevNumber(dd) + 1);
2152     }
2153     strcat(t, " (ACTIVE)");
2154     XStoreName(display, xd->window, t);
2155     XSync(display, 0);
2156 }
2157 
X11_Deactivate(pDevDesc dd)2158 static void X11_Deactivate(pDevDesc dd)
2159 {
2160     char t[150];
2161     pX11Desc xd = (pX11Desc) dd->deviceSpecific;
2162 
2163     if (xd->type > WINDOW) return;
2164     if(xd->title[0]) {
2165 	snprintf(t, 140, xd->title, ndevNumber(dd) + 1);
2166 	t[139] = '\0';
2167     } else {
2168 	sprintf(t, "R Graphics: Device %d", ndevNumber(dd) + 1);
2169     }
2170     strcat(t, " (inactive)");
2171     XStoreName(display, xd->window, t);
2172     XSync(display, 0);
2173 }
2174 
X11_Rect(double x0,double y0,double x1,double y1,const pGEcontext gc,pDevDesc dd)2175 static void X11_Rect(double x0, double y0, double x1, double y1,
2176 		     const pGEcontext gc, pDevDesc dd)
2177 {
2178     double tmp;
2179     pX11Desc xd = (pX11Desc) dd->deviceSpecific;
2180 
2181     if (x0 > x1) {
2182 	tmp = x0;
2183 	x0 = x1;
2184 	x1 = tmp;
2185     }
2186     if (y0 > y1) {
2187 	tmp = y0;
2188 	y0 = y1;
2189 	y1 = tmp;
2190     }
2191     CheckAlpha(gc->fill, xd);
2192     if (R_OPAQUE(gc->fill)) {
2193 	SetColor(gc->fill, xd);
2194 	XFillRectangle(display, xd->window, xd->wgc, (int)x0, (int)y0,
2195 		       (int)x1 - (int)x0, (int)y1 - (int)y0);
2196     }
2197     CheckAlpha(gc->col, xd);
2198     if (R_OPAQUE(gc->col)) {
2199 	SetColor(gc->col, xd);
2200 	SetLinetype(gc, xd);
2201 	XDrawRectangle(display, xd->window, xd->wgc, (int)x0, (int)y0,
2202 		       (int)x1 - (int)x0, (int)y1 - (int)y0);
2203     }
2204 }
2205 
X11_Path(double * x,double * y,int npoly,int * nper,Rboolean winding,const pGEcontext gc,pDevDesc dd)2206 static void X11_Path(double *x, double *y,
2207                      int npoly, int *nper,
2208                      Rboolean winding,
2209                      const pGEcontext gc, pDevDesc dd)
2210 {
2211     warning(_("%s not available for this device"), "Path drawing");
2212 }
2213 
makeX11Pixel(unsigned int * rasterImage,int pixel)2214 static unsigned int makeX11Pixel(unsigned int * rasterImage, int pixel) {
2215     return GetX11Pixel(R_RED(rasterImage[pixel]),
2216                        R_GREEN(rasterImage[pixel]),
2217                        R_BLUE(rasterImage[pixel]));
2218 }
2219 
flipRaster(unsigned int * rasterImage,int imageWidth,int imageHeight,int invertX,int invertY,unsigned int * flippedRaster)2220 static void flipRaster(unsigned int *rasterImage,
2221                        int imageWidth, int imageHeight,
2222                        int invertX, int invertY,
2223                        unsigned int *flippedRaster) {
2224     int i, j;
2225     int rowInc, rowOff, colInc, colOff;
2226 
2227     if (invertX) {
2228         colInc = -1;
2229         colOff = imageWidth - 1;
2230     } else {
2231         colInc = 1;
2232         colOff = 0;
2233     }
2234     if (invertY) {
2235         rowInc = -1;
2236         rowOff = imageHeight - 1;
2237     } else {
2238         rowInc = 1;
2239         rowOff = 0;
2240     }
2241 
2242     for (i = 0; i < imageHeight ;i++) {
2243         for (j = 0; j < imageWidth; j++) {
2244             int row = (rowInc*i + rowOff);
2245             int col = (colInc*j + colOff);
2246             flippedRaster[i*imageWidth + j] =
2247                 rasterImage[row*imageWidth + col];
2248         }
2249     }
2250 }
2251 
X11_Raster(unsigned int * raster,int w,int h,double x,double y,double width,double height,double rot,Rboolean interpolate,const pGEcontext gc,pDevDesc dd)2252 static void X11_Raster(unsigned int *raster, int w, int h,
2253                       double x, double y,
2254                       double width, double height,
2255                       double rot,
2256                       Rboolean interpolate,
2257                       const pGEcontext gc, pDevDesc dd)
2258 {
2259     int i, j, pixel;
2260     int imageWidth;
2261     int imageHeight;
2262     int invertX = 0;
2263     int invertY = 0;
2264     double angle = rot*M_PI/180;
2265     pX11Desc xd = (pX11Desc) dd->deviceSpecific;
2266     XImage *image;
2267     unsigned int *rasterImage;
2268     const void *vmax = vmaxget();
2269 
2270     if (height < 0) {
2271         imageHeight = (int) -(height - .5);
2272         /* convert (x, y) from bottom-left to top-left */
2273         y = y - imageHeight*cos(angle);
2274         if (angle != 0) {
2275             x = x - imageHeight*sin(angle);
2276         }
2277     } else {
2278         imageHeight = (int) (height + .5);
2279         invertY = 1;
2280     }
2281 
2282     if (width < 0) {
2283         imageWidth = (int) -(width - .5);
2284         x = x - imageWidth*cos(angle);
2285         if (angle != 0)
2286             y = y + imageWidth*sin(angle);
2287         invertX = 1;
2288     } else {
2289         imageWidth = (int) (width + .5);
2290     }
2291 
2292     rasterImage = (unsigned int *) R_alloc(imageWidth * imageHeight,
2293                                            sizeof(unsigned int));
2294     if (interpolate) {
2295         R_GE_rasterInterpolate(raster, w, h,
2296                                rasterImage, imageWidth, imageHeight);
2297     } else {
2298         R_GE_rasterScale(raster, w, h,
2299                          rasterImage, imageWidth, imageHeight);
2300     }
2301 
2302     if (invertX || invertY) {
2303         unsigned int *flippedRaster;
2304 
2305         flippedRaster = (unsigned int *) R_alloc(imageWidth * imageHeight,
2306                                                  sizeof(unsigned int));
2307         flipRaster(rasterImage, imageWidth, imageHeight,
2308                    invertX, invertY, flippedRaster);
2309         rasterImage = flippedRaster;
2310     }
2311 
2312     if (rot != 0) {
2313 
2314         int newW, newH;
2315         double xoff, yoff;
2316         unsigned int *resizedRaster, *rotatedRaster;
2317 
2318         R_GE_rasterRotatedSize(imageWidth, imageHeight, angle, &newW, &newH);
2319         R_GE_rasterRotatedOffset(imageWidth, imageHeight, angle, 0,
2320                                  &xoff, &yoff);
2321 
2322         resizedRaster = (unsigned int *) R_alloc(newW * newH,
2323                                              sizeof(unsigned int));
2324         R_GE_rasterResizeForRotation(rasterImage, imageWidth, imageHeight,
2325                                      resizedRaster, newW, newH, gc);
2326 
2327         rotatedRaster = (unsigned int *) R_alloc(newW * newH,
2328                                                  sizeof(unsigned int));
2329         R_GE_rasterRotate(resizedRaster, newW, newH, angle, rotatedRaster, gc,
2330                           FALSE);
2331 
2332         /*
2333          * Adjust (x, y) for resized and rotated image
2334          */
2335         x = x - (newW - imageWidth)/2 - xoff;
2336         y = y - (newH - imageHeight)/2 + yoff;
2337 
2338         rasterImage = rotatedRaster;
2339         imageWidth = newW;
2340         imageHeight = newH;
2341     }
2342 
2343     image = XCreateImage(display, visual, depth,
2344                          ZPixmap,
2345                          0, /* offset */
2346                          /* This is just provides (at least enough)
2347                           * allocated memory for the image data;
2348                           * each pixel is set separately below
2349                           */
2350                          (char *) rasterImage,
2351                          imageWidth, imageHeight,
2352                          depth >= 24 ? 32 : 16, /* bitmap_pad */
2353                          0); /* bytes_per_line: 0 means auto-calculate*/
2354 
2355     if (image == NULL || XInitImage(image) == 0)
2356         error(_("Unable to create XImage"));
2357 
2358     for (i = 0; i < imageHeight ;i++) {
2359         for (j = 0; j < imageWidth; j++) {
2360             pixel = i*imageWidth + j;
2361             XPutPixel(image, j, i, makeX11Pixel(rasterImage, pixel));
2362         }
2363     }
2364 
2365     XPutImage(display, xd->window, xd->wgc,
2366               image, 0, 0,
2367               (int) x, (int) y, imageWidth, imageHeight);
2368 
2369     /* XFree() rather than XDestroyImage() because the latter
2370      * tries to free the image 'data' as well
2371      */
2372     XFree(image);
2373 
2374     vmaxset(vmax);
2375 }
2376 
X11_Cap(pDevDesc dd)2377 static SEXP X11_Cap(pDevDesc dd)
2378 {
2379     pX11Desc xd = (pX11Desc) dd->deviceSpecific;
2380     XImage *image = XGetImage(display, xd->window, 0, 0,
2381                               xd->windowWidth, xd->windowHeight,
2382                               AllPlanes, ZPixmap);
2383     SEXP raster = R_NilValue;
2384 
2385     if (image) {
2386         int i, j;
2387         SEXP dim;
2388         int size = xd->windowWidth * xd->windowHeight;
2389         const void *vmax = vmaxget();
2390         unsigned int *rint;
2391 
2392         PROTECT(raster = allocVector(INTSXP, size));
2393 
2394         /* Copy each byte of screen to an R matrix.
2395          * The ARGB32 needs to be converted to an R ABGR32 */
2396         rint = (unsigned int *) INTEGER(raster);
2397         for (i = 0; i < xd->windowHeight; i++) {
2398             for (j = 0; j < xd->windowWidth; j++) {
2399                 /*
2400                  * Convert each pixel in image to an R colour
2401                  */
2402                 rint[i*xd->windowWidth + j] = bitgp((void *) image, i, j);
2403             }
2404         }
2405         PROTECT(dim = allocVector(INTSXP, 2));
2406         INTEGER(dim)[0] = xd->windowHeight;
2407         INTEGER(dim)[1] = xd->windowWidth;
2408         setAttrib(raster, R_DimSymbol, dim);
2409 
2410         UNPROTECT(2);
2411 
2412         XDestroyImage(image);
2413         vmaxset(vmax);
2414     }
2415 
2416     return raster;
2417 }
2418 
X11_Circle(double x,double y,double r,const pGEcontext gc,pDevDesc dd)2419 static void X11_Circle(double x, double y, double r,
2420 		       const pGEcontext gc, pDevDesc dd)
2421 {
2422     int ir, ix, iy;
2423     pX11Desc xd = (pX11Desc) dd->deviceSpecific;
2424 
2425     ir = (int)floor(r + 0.5);
2426 
2427     ix = (int)x;
2428     iy = (int)y;
2429     CheckAlpha(gc->fill, xd);
2430     if (R_OPAQUE(gc->fill)) {
2431 	SetColor(gc->fill, xd);
2432 	XFillArc(display, xd->window, xd->wgc,
2433 		 ix-ir, iy-ir, 2*ir, 2*ir, 0, 23040);
2434     }
2435     CheckAlpha(gc->col, xd);
2436     if (R_OPAQUE(gc->col)) {
2437 	SetLinetype(gc, xd);
2438 	SetColor(gc->col, xd);
2439 	XDrawArc(display, xd->window, xd->wgc,
2440 		 ix-ir, iy-ir, 2*ir, 2*ir, 0, 23040);
2441     }
2442 }
2443 
X11_Line(double x1,double y1,double x2,double y2,const pGEcontext gc,pDevDesc dd)2444 static void X11_Line(double x1, double y1, double x2, double y2,
2445 		     const pGEcontext gc, pDevDesc dd)
2446 {
2447     int xx1, yy1, xx2, yy2;
2448     pX11Desc xd = (pX11Desc) dd->deviceSpecific;
2449 
2450     /* In-place conversion ok */
2451 
2452     xx1 = (int) x1;
2453     yy1 = (int) y1;
2454     xx2 = (int) x2;
2455     yy2 = (int) y2;
2456 
2457     CheckAlpha(gc->col, xd);
2458     if (R_OPAQUE(gc->col)) {
2459 	SetColor(gc->col, xd);
2460 	SetLinetype(gc, xd);
2461 	XDrawLine(display, xd->window, xd->wgc, xx1, yy1, xx2, yy2);
2462     }
2463 }
2464 
X11_Polyline(int n,double * x,double * y,const pGEcontext gc,pDevDesc dd)2465 static void X11_Polyline(int n, double *x, double *y,
2466 			 const pGEcontext gc, pDevDesc dd)
2467 {
2468     const void *vmax = vmaxget();
2469     XPoint *points;
2470     int i, j;
2471     pX11Desc xd = (pX11Desc) dd->deviceSpecific;
2472 
2473     points = (XPoint *) R_alloc(n, sizeof(XPoint));
2474 
2475     for(i = 0 ; i < n ; i++) {
2476 	points[i].x = (short)(x[i]);
2477 	points[i].y = (short)(y[i]);
2478     }
2479 
2480     CheckAlpha(gc->col, xd);
2481     if (R_OPAQUE(gc->col)) {
2482 	SetColor(gc->col, xd);
2483 	SetLinetype(gc, xd);
2484 /* Some X servers need npoints < 64K */
2485 	for(i = 0; i < n; i+= 10000-1) {
2486 	    j = n - i;
2487 	    j = (j <= 10000) ? j: 10000; /* allow for overlap */
2488 	    XDrawLines(display, xd->window, xd->wgc, points+i, j,
2489 		       CoordModeOrigin);
2490 	}
2491     }
2492 
2493     vmaxset(vmax);
2494 }
2495 
X11_Polygon(int n,double * x,double * y,const pGEcontext gc,pDevDesc dd)2496 static void X11_Polygon(int n, double *x, double *y,
2497 			const pGEcontext gc, pDevDesc dd)
2498 {
2499     const void *vmax = vmaxget();
2500     XPoint *points;
2501     int i;
2502     pX11Desc xd = (pX11Desc) dd->deviceSpecific;
2503 
2504     points = (XPoint *) R_alloc(n+1, sizeof(XPoint));
2505 
2506     for (i = 0 ; i < n ; i++) {
2507 	points[i].x = (short)(x[i]);
2508 	points[i].y = (short)(y[i]);
2509     }
2510     points[n].x = (short)(x[0]);
2511     points[n].y = (short)(y[0]);
2512     CheckAlpha(gc->fill, xd);
2513     if (R_OPAQUE(gc->fill)) {
2514 	SetColor(gc->fill, xd);
2515 	XFillPolygon(display, xd->window, xd->wgc, points, n,
2516 		     Complex, CoordModeOrigin);
2517     }
2518     CheckAlpha(gc->col, xd);
2519     if (R_OPAQUE(gc->col)) {
2520 	SetColor(gc->col, xd);
2521 	SetLinetype(gc, xd);
2522 	XDrawLines(display, xd->window, xd->wgc, points, n+1, CoordModeOrigin);
2523     }
2524 
2525     vmaxset(vmax);
2526 }
2527 
2528 
X11_Text(double x,double y,const char * str,double rot,double hadj,const pGEcontext gc,pDevDesc dd)2529 static void X11_Text(double x, double y,
2530 		     const char *str, double rot, double hadj,
2531 		     const pGEcontext gc, pDevDesc dd)
2532 {
2533     pX11Desc xd = (pX11Desc) dd->deviceSpecific;
2534 
2535     SetFont(gc, xd);
2536     CheckAlpha(gc->col, xd);
2537     if (R_OPAQUE(gc->col)) {
2538 	SetColor(gc->col, xd);
2539 	XRfRotDrawString(display, xd->font, rot, xd->window,
2540 			 xd->wgc, (int)x, (int)y, str);
2541     }
2542 }
2543 
X11_Locator(double * x,double * y,pDevDesc dd)2544 static Rboolean X11_Locator(double *x, double *y, pDevDesc dd)
2545 {
2546     XEvent event;
2547     pDevDesc ddEvent;
2548     pX11Desc xd = (pX11Desc) dd->deviceSpecific;
2549     caddr_t temp;
2550     int done = 0;
2551 
2552     if (xd->type > WINDOW) return 0;
2553 #ifdef HAVE_WORKING_X11_CAIRO
2554     if (xd->holdlevel > 0)
2555 	error(_("attempt to use the locator after dev.hold()"));
2556     if (xd->buffered) Cairo_update(xd);
2557 #endif
2558     R_ProcessX11Events((void*)NULL);	/* discard pending events */
2559     if(xd->type==WINDOW) XDefineCursor(display, xd->window, cross_cursor);
2560     XSync(display, 1);
2561     /* handle X events as normal until get a button */
2562     /* click in the desired device */
2563     while (!done && displayOpen) {
2564 	XNextEvent(display, &event);
2565 	/* possibly later R_CheckUserInterrupt(); */
2566 	if (event.type == ButtonPress) {
2567 	    XFindContext(display, event.xbutton.window,
2568 			 devPtrContext, &temp);
2569 	    ddEvent = (pDevDesc) temp;
2570 	    if (ddEvent == dd) {
2571 		if (event.xbutton.button == Button1) {
2572 		    int useBeep = asLogical(GetOption1(install("locatorBell")));
2573 		    *x = event.xbutton.x;
2574 		    *y = event.xbutton.y;
2575 		       /* Make a beep! Was print "\07", but that
2576 			  messes up some terminals. */
2577 		    if(useBeep) XBell(display, X_BELL_VOLUME);
2578 		    XSync(display, 0);
2579 		    done = 1;
2580 		}
2581 		else
2582 		    done = 2;
2583 	    }
2584 	}
2585 	else
2586 	    handleEvent(event);
2587     }
2588     /* In case it got closed asynchronously, PR#14872 */
2589     if (!displayOpen) return 0;
2590     /* if it was a Button1 succeed, otherwise fail */
2591     if(xd->type==WINDOW) XDefineCursor(display, xd->window, arrow_cursor);
2592     XSync(display, 0);
2593     return (done == 1);
2594 }
2595 
translate_key(KeySym keysym)2596 static int translate_key(KeySym keysym)
2597 {
2598     if ((keysym >= XK_F1) && (keysym <= XK_F12))
2599     	return knF1 + (int)keysym - XK_F1;
2600     else {
2601     	switch(keysym) {
2602 	case XK_Left: return knLEFT;
2603 	case XK_Up:   return knUP;
2604 	case XK_Right:return knRIGHT;
2605 	case XK_Down: return knDOWN;
2606 	case XK_Page_Up: 	return knPGUP;
2607 	case XK_Page_Down: 	return knPGDN;
2608 	case XK_End:  return knEND;
2609 	case XK_Begin:return knHOME;
2610 	case XK_Insert:  	return knINS;
2611 	}
2612     }
2613     return knUNKNOWN;
2614 }
2615 
X11_eventHelper(pDevDesc dd,int code)2616 static void X11_eventHelper(pDevDesc dd, int code)
2617 {
2618     XEvent event;
2619     pDevDesc ddEvent;
2620     pX11Desc xd = (pX11Desc) dd->deviceSpecific;
2621     caddr_t temp;
2622     int done = 0;
2623 
2624     if (xd->type > WINDOW) return;
2625     if (code == 1) {
2626     	R_ProcessX11Events((void*)NULL);	/* discard pending events */
2627     	if (isEnvironment(dd->eventEnv)) {
2628     	    SEXP prompt = findVar(install("prompt"), dd->eventEnv);
2629     	    if (isString(prompt) && length(prompt) == 1) {
2630     		 PROTECT(prompt);
2631     		 XStoreName(display, xd->window, CHAR(asChar(prompt)));
2632     		 UNPROTECT(1);
2633     	    } else
2634     	    	XStoreName(display, xd->window, "");
2635     	}
2636     	XSync(display, 1);
2637     } else if (code == 2) {
2638 	if (doesIdle(dd) && (XPending(display) == 0)) {
2639             // The device descriptor event environment has an idle
2640             // handler, and there are no pending events
2641             doIdle(dd);
2642             return;
2643         }
2644         XNextEvent(display, &event);
2645 	if (event.type == ButtonRelease || event.type == ButtonPress || event.type == MotionNotify) {
2646 	    int RButtons;
2647 	    XFindContext(display, event.xbutton.window,
2648 			 devPtrContext, &temp);
2649 	    ddEvent = (pDevDesc) temp;
2650 	    if (ddEvent == dd && dd->gettingEvent) {
2651 		if (event.type == MotionNotify) { /* Because of PointerMotionHintMask, need to update */
2652 		    Window root, child;
2653 		    int rootX, rootY, winX, winY;
2654 		    unsigned int mask;
2655 		    if (!XQueryPointer(display, event.xbutton.window,
2656                                       &root, &child, &rootX, &rootY,
2657 				      &winX, &winY, &mask)) {
2658 			done = 1;
2659 		    } else {
2660 			event.xbutton.x = winX;
2661 			event.xbutton.y = winY;
2662 		    }
2663 		    RButtons = mask >> 8;  /* See PR#16700 */
2664 		} else
2665 		    RButtons = 1 << (event.xbutton.button - 1);
2666 		if (!done) {
2667         	    doMouseEvent(dd, event.type == ButtonRelease ? meMouseUp :
2668         	                 event.type == ButtonPress ? meMouseDown : meMouseMove,
2669         	                 RButtons, event.xbutton.x, event.xbutton.y);
2670                     XSync(display, 0);
2671                     done = 1;
2672 		}
2673     	    }
2674 	} else if (event.type == KeyPress) {
2675 	    char keybuffer[13] = "";
2676 	    char *keystart=keybuffer;
2677 	    XComposeStatus compose;
2678   	    KeySym keysym;
2679 	    int keycode;
2680 	    if (event.xkey.state & ControlMask) {
2681 	    	keystart += 5;
2682 	    	sprintf(keybuffer, "ctrl-"); /* report control keys using labels like "ctrl-A" */
2683 	    	event.xkey.state &= ~ControlMask;
2684 	    	event.xkey.state |= ShiftMask;
2685 	    }
2686       	    XLookupString(&event.xkey, keystart,
2687 			  sizeof(keybuffer)-(int)(keystart-keybuffer),
2688 			  &keysym, &compose);
2689       	    /* Rprintf("keysym=%x\n", keysym); */
2690       	    if ((keycode = translate_key(keysym)) > knUNKNOWN)
2691       	    	doKeybd(dd, keycode, NULL);
2692       	    else if (*keystart)
2693 	    	doKeybd(dd, knUNKNOWN, keybuffer);
2694 	    done = 1;
2695 	}
2696 	if (!done)
2697 	    handleEvent(event);
2698     } else if (code == 0) {
2699 	/* Restore the default title */
2700 	if (ndevNumber(dd) == curDevice())
2701 	    X11_Activate(dd);
2702 	else
2703 	    X11_Deactivate(dd);
2704     }
2705 
2706     return;
2707 }
2708 
2709 	/********************************************************/
2710 	/* device_Mode is called whenever the graphics engine	*/
2711 	/* starts drawing (mode=1) or stops drawing (mode=0)	*/
2712 	/* the device is not required to do anything		*/
2713 	/********************************************************/
2714 
X11_Mode(int mode,pDevDesc dd)2715 static void X11_Mode(int mode, pDevDesc dd)
2716 {
2717     pX11Desc xd = (pX11Desc) dd->deviceSpecific;
2718     if(xd->holdlevel > 0) {
2719 #ifdef HAVE_WORKING_X11_CAIRO
2720 	if(mode == 0 && xd->buffered > 1)
2721 	    xd->last_activity = currentTime();
2722 #endif
2723 	return;
2724     }
2725     if(mode == 1) {
2726 	if(xd->type==WINDOW) XDefineCursor(display, xd->window, watch_cursor);
2727 	XSync(display, 0);
2728     }
2729     if(mode == 0) {
2730 #ifdef HAVE_WORKING_X11_CAIRO
2731 	if(xd->buffered > 1) {
2732 	    xd->last_activity = currentTime();
2733 	    if((currentTime() - xd->last) > 0.5 /* 5*xd->update_interval */)
2734 		Cairo_update(xd);
2735 	    return;
2736 	}
2737 	if(xd->buffered) {
2738 	    cairo_paint(xd->xcc);
2739 	    cairo_surface_flush(xd->xcs);
2740 	}
2741 
2742 #endif
2743 	if(xd->type==WINDOW) XDefineCursor(display, xd->window, arrow_cursor);
2744 	XSync(display, 0);
2745     }
2746 }
2747 
X11_setPattern(SEXP pattern,pDevDesc dd)2748 static SEXP X11_setPattern(SEXP pattern, pDevDesc dd) {
2749     return R_NilValue;
2750 }
2751 
X11_releasePattern(SEXP ref,pDevDesc dd)2752 static void X11_releasePattern(SEXP ref, pDevDesc dd) {}
2753 
X11_setClipPath(SEXP path,SEXP ref,pDevDesc dd)2754 static SEXP X11_setClipPath(SEXP path, SEXP ref, pDevDesc dd) {
2755     return R_NilValue;
2756 }
2757 
X11_releaseClipPath(SEXP ref,pDevDesc dd)2758 static void X11_releaseClipPath(SEXP ref, pDevDesc dd) {}
2759 
X11_setMask(SEXP path,SEXP ref,pDevDesc dd)2760 static SEXP X11_setMask(SEXP path, SEXP ref, pDevDesc dd) {
2761     return R_NilValue;
2762 }
2763 
X11_releaseMask(SEXP ref,pDevDesc dd)2764 static void X11_releaseMask(SEXP ref, pDevDesc dd) {}
2765 
2766 
2767 	/*  X11 Device Driver Arguments	:	*/
2768 	/*	1) display name			*/
2769 	/*	2) width (inches)		*/
2770 	/*	3) height (inches)		*/
2771 	/*	4) base pointsize		*/
2772 	/*	5) gamma correction factor	*/
2773 	/*	6) colormodel,			*/
2774 	/*	 see X_COLORTYPE at top of file */
2775 	/*	7) maxcube			*/
2776 
X11DeviceDriver(pDevDesc dd,const char * disp_name,double width,double height,double pointsize,double gamma_fac,X_COLORTYPE colormodel,int maxcube,int bgcolor,int canvascolor,SEXP sfonts,int res,int xpos,int ypos,const char * title,int useCairo,int antialias,const char * family,const char * symbolfamily,Rboolean usePUA)2777 Rboolean X11DeviceDriver(pDevDesc dd,
2778 			 const char *disp_name,
2779 			 double width,
2780 			 double height,
2781 			 double pointsize,
2782 			 double gamma_fac,
2783 			 X_COLORTYPE colormodel,
2784 			 int maxcube,
2785 			 int bgcolor,
2786 			 int canvascolor,
2787 			 SEXP sfonts,
2788 			 int res,
2789 			 int xpos, int ypos,
2790 			 const char *title,
2791 			 int useCairo,
2792 			 int antialias,
2793 			 const char *family,
2794 			 const char *symbolfamily,
2795                          Rboolean usePUA)
2796 {
2797     pX11Desc xd;
2798     const char *fn;
2799 
2800     xd = Rf_allocX11DeviceDesc(pointsize);
2801     if(!xd) return FALSE;
2802     xd->bg = bgcolor;
2803 #ifdef HAVE_WORKING_X11_CAIRO
2804     xd->useCairo = useCairo != 0;
2805     xd->buffered = 0;
2806     switch(useCairo) {
2807     case 0: break; /* Xlib */
2808     case 1: xd->buffered = 1; break; /* cairo */
2809     case 2: xd->buffered = 0; break; /* nbcairo */
2810     case 3: xd->buffered = 2; break; /* dbcairo */
2811     default:
2812 	warning("that type is not supported on this platform - using \"nbcairo\"");
2813 	xd->buffered = 0;
2814     }
2815     if(useCairo) {
2816 	switch(antialias){
2817 	case 1: xd->antialias = CAIRO_ANTIALIAS_DEFAULT; break;
2818 	case 2: xd->antialias = CAIRO_ANTIALIAS_NONE; break;
2819 	case 3: xd->antialias = CAIRO_ANTIALIAS_GRAY; break;
2820 	case 4: xd->antialias = CAIRO_ANTIALIAS_SUBPIXEL; break;
2821 	}
2822 
2823     }
2824 #else
2825     /* Currently this gets caught at R level */
2826     if(useCairo) {
2827 	warning("cairo-based types are not supported on this build - using \"Xlib\"");
2828 	useCairo = FALSE;
2829     }
2830 #endif
2831 
2832     if(!useCairo) {
2833 	if(strlen(fn = CHAR(STRING_ELT(sfonts, 0))) > 499) {
2834 	    strcpy(xd->basefontfamily, fontname);
2835 	    strcpy(xd->fontfamily, fontname);
2836 	} else {
2837 	    strcpy(xd->basefontfamily, fn);
2838 	    strcpy(xd->fontfamily, fn);
2839 	}
2840 	if(strlen(fn = CHAR(STRING_ELT(sfonts, 1))) > 499)
2841 	    strcpy(xd->symbolfamily, symbolname);
2842 	else strcpy(xd->symbolfamily, fn);
2843         xd->usePUA = TRUE;
2844     } else {
2845         strcpy(xd->basefontfamily, family);
2846         strcpy(xd->symbolfamily, symbolfamily);
2847         xd->usePUA = usePUA;
2848     }
2849 
2850     /*	Start the Device Driver and Hardcopy.  */
2851 
2852     strncpy(xd->title, title, 100);
2853     xd->title[100] = '\0';
2854 
2855 #ifdef HAVE_WORKING_X11_CAIRO
2856     {
2857 	SEXP timeouts = GetOption1(install("X11updates"));
2858 	double tm = asReal(timeouts);
2859 	xd->update_interval = (ISNAN(tm) || tm < 0) ? 0.10 : tm;
2860     }
2861 #endif
2862 
2863     if (!X11_Open(dd, xd, disp_name, width, height,
2864 		  gamma_fac, colormodel, maxcube, bgcolor,
2865 		  canvascolor, res, xpos, ypos)) {
2866 	free(xd);
2867 	return FALSE;
2868     }
2869 
2870     Rf_setX11DeviceData(dd, gamma_fac, xd);
2871     xd->fill = 0xffffffff; /* this is needed to ensure that the
2872 			      first newpage does set whitecolor
2873 			      if par("bg") is not transparent */
2874 
2875     return TRUE;
2876 }
2877 
2878 /**
2879   This fills the general device structure (dd) with the X-specific
2880   methods/functions. It also specifies the current values of the
2881   dimensions of the device, and establishes the fonts, line styles, etc.
2882  */
2883 int
Rf_setX11DeviceData(pDevDesc dd,double gamma_fac,pX11Desc xd)2884 Rf_setX11DeviceData(pDevDesc dd, double gamma_fac, pX11Desc xd)
2885 {
2886     double ps = xd->pointsize;
2887     int res0 = (xd->res_dpi > 0) ? xd->res_dpi : 72;
2888     /*	Set up Data Structures. */
2889 
2890 #ifdef HAVE_WORKING_X11_CAIRO
2891     if(xd->useCairo) {
2892 	dd->newPage = Cairo_NewPage;
2893 	dd->clip = Cairo_Clip;
2894 	dd->rect = Cairo_Rect;
2895 	dd->circle = Cairo_Circle;
2896 	dd->line = Cairo_Line;
2897 	dd->polyline = Cairo_Polyline;
2898 	dd->polygon = Cairo_Polygon;
2899         dd->path = Cairo_Path;
2900         dd->raster = Cairo_Raster;
2901         dd->cap = Cairo_Cap;
2902 	dd->hasTextUTF8 = TRUE;
2903 	dd->wantSymbolUTF8 = TRUE;
2904 #ifdef HAVE_PANGOCAIRO
2905 	dd->metricInfo = PangoCairo_MetricInfo;
2906 	dd->strWidth = dd->strWidthUTF8 = PangoCairo_StrWidth;
2907 	dd->text = dd->textUTF8 = PangoCairo_Text;
2908 #else
2909 	dd->metricInfo = Cairo_MetricInfo;
2910 	dd->strWidth = dd->strWidthUTF8 = Cairo_StrWidth;
2911 	dd->text = dd->textUTF8 = Cairo_Text;
2912 #endif
2913 	dd->holdflush = Cairo_holdflush;
2914 	dd->haveTransparency = 2;
2915 	dd->haveTransparentBg = 3;
2916 	dd->haveRaster = 2;
2917 	dd->haveCapture = (xd->type > WINDOW) ? 1 : 2;
2918 	dd->haveLocator = (xd->type > WINDOW) ? 1 : 2;
2919 
2920         dd->setPattern = Cairo_SetPattern;
2921         dd->releasePattern = Cairo_ReleasePattern;
2922         dd->setClipPath = Cairo_SetClipPath;
2923         dd->releaseClipPath = Cairo_ReleaseClipPath;
2924         dd->setMask = Cairo_SetMask;
2925         dd->releaseMask = Cairo_ReleaseMask;
2926 
2927     } else
2928 #endif
2929     {
2930 	dd->newPage = X11_NewPage;
2931 	dd->clip = X11_Clip;
2932 	dd->strWidth = X11_StrWidth;
2933 	dd->text = X11_Text;
2934 	dd->rect = X11_Rect;
2935         dd->path = X11_Path;
2936         dd->raster     = X11_Raster;
2937         dd->cap        = X11_Cap;
2938 	dd->circle = X11_Circle;
2939 	dd->line = X11_Line;
2940 	dd->polyline = X11_Polyline;
2941 	dd->polygon = X11_Polygon;
2942 	dd->metricInfo = X11_MetricInfo;
2943 	dd->hasTextUTF8 = FALSE;
2944 
2945 	dd->haveTransparency = 1;
2946 	dd->haveTransparentBg = 2;
2947 	dd->haveRaster = 3;
2948 	dd->haveCapture = (xd->type > WINDOW) ? 1 : 2;
2949 	dd->haveLocator = (xd->type > WINDOW) ? 1 : 2;
2950 
2951         dd->setPattern      = X11_setPattern;
2952         dd->releasePattern  = X11_releasePattern;
2953         dd->setClipPath     = X11_setClipPath;
2954         dd->releaseClipPath = X11_releaseClipPath;
2955         dd->setMask         = X11_setMask;
2956         dd->releaseMask     = X11_releaseMask;
2957     }
2958 
2959     dd->eventHelper = X11_eventHelper;
2960     dd->canGenMouseDown = TRUE;
2961     dd->canGenMouseUp = TRUE;
2962     dd->canGenMouseMove = TRUE;
2963     dd->canGenKeybd = TRUE;
2964     dd->canGenIdle = TRUE;
2965 
2966     dd->activate = X11_Activate;
2967     dd->close = X11_Close;
2968     dd->deactivate = X11_Deactivate;
2969     dd->size = X11_Size;
2970     dd->locator = X11_Locator;
2971     dd->mode = X11_Mode;
2972     dd->useRotatedTextInContour = FALSE;
2973 
2974     /* Set required graphics parameters. */
2975 
2976     /* Window Dimensions in Pixels */
2977     /* Initialise the clipping rect too */
2978 
2979     dd->left = dd->clipLeft = 0;			/* left */
2980     dd->right = dd->clipRight = xd->windowWidth;	/* right */
2981     dd->bottom = dd->clipBottom = xd->windowHeight;	/* bottom */
2982     dd->top = dd->clipTop = 0;			/* top */
2983 
2984     /* Nominal Character Sizes in Pixels */
2985     /* Recommendation from 'R internals': changed for 2.7.0 */
2986     /* Inches per raster unit */
2987 
2988     /* ps is in points, we want this in device units */
2989     if(xd->type == PNG || xd->type == JPEG ||
2990        xd->type == BMP || xd->type == TIFF) {
2991 	dd->cra[0] = 0.9*ps * res0/72.0;
2992 	dd->cra[1] = 1.2*ps * res0/72.0;
2993 	dd->ipr[0] =  dd->ipr[1] = 1.0/res0;
2994 	xd->lwdscale = res0/96.0;
2995     } else if(xd->type >= SVG) { /* SVG, PDF, PS -- unused */
2996 	/* Device units are bp */
2997 	dd->cra[0] = 0.9*ps;
2998 	dd->cra[1] = 1.2*ps;
2999 	dd->ipr[0] =  dd->ipr[1] = 1.0/72.0;
3000 	xd->lwdscale = 1.0/96.0;
3001     } else {
3002 	dd->cra[0] = 0.9*ps * 1.0/(72.0*pixelWidth());
3003 	dd->cra[1] = 1.2*ps * 1.0/(72.0*pixelHeight());
3004 	dd->ipr[0] = pixelWidth();
3005 	dd->ipr[1] = pixelHeight();
3006 	xd->lwdscale = 1.0/(96.0*pixelWidth());
3007 #ifdef HAVE_WORKING_X11_CAIRO
3008 	if(xd->useCairo) {
3009 # ifdef HAVE_PANGOCAIRO
3010 	    /* Pango's default resolution is 96 dpi */
3011 	    ps *= 1.0/(96.0*pixelWidth());
3012 # else
3013 	    /* Cairo's default resolution is 72 dpi */
3014 	    ps *= 1.0/(72.0*pixelWidth());
3015 # endif
3016 	}
3017 #endif
3018     }
3019 
3020     /* Character Addressing Offsets */
3021     /* These are used to plot a single plotting character */
3022     /* so that it is exactly over the plotting point */
3023 
3024     dd->xCharOffset = 0.4900;
3025     dd->yCharOffset = 0.3333;
3026     dd->yLineBias = 0.2;
3027 
3028 
3029     /* Device capabilities */
3030 
3031     dd->canClip = TRUE;
3032 #ifdef HAVE_WORKING_X11_CAIRO
3033     dd->canHAdj = xd->useCairo ? 2 : 0;
3034 #else
3035     dd->canHAdj = 0;
3036 #endif
3037     dd->canChangeGamma = FALSE;
3038 
3039     dd->startps = ps;
3040     xd->fontscale = 1.0;
3041     dd->startcol = xd->col;
3042     dd->startfill = xd->fill;
3043     dd->startlty = LTY_SOLID;
3044     dd->startfont = 1;
3045     dd->startgamma = gamma_fac;
3046 
3047     /* initialise x11 device description */
3048     /* (most of the work has been done in X11_Open) */
3049     xd->resize = 0;
3050 
3051     dd->deviceSpecific = (void *) xd;
3052 
3053     dd->displayListOn = TRUE;
3054     dd->deviceVersion = R_GE_definitions;
3055 
3056     return TRUE;
3057 }
3058 
3059 
3060 /**
3061  This allocates an X11Desc instance  and sets its default values.
3062  */
Rf_allocX11DeviceDesc(double ps)3063 pX11Desc Rf_allocX11DeviceDesc(double ps)
3064 {
3065     pX11Desc xd;
3066     /* allocate new device description */
3067     if (!(xd = (pX11Desc)calloc(1, sizeof(X11Desc))))
3068 	return NULL;
3069 
3070     /* From here on, if we need to bail out with "error", */
3071     /* then we must also free(xd). */
3072 
3073     /*	Font will load at first use.  */
3074 
3075     if (ps < 6 || ps > 24) ps = 12;
3076     xd->fontface = -1;
3077     xd->fontsize = -1;
3078     xd->pointsize = ps;
3079     xd->handleOwnEvents = FALSE;
3080     xd->window = (Window) NULL;
3081 
3082     return xd;
3083 }
3084 
3085 
3086 static
in_R_GetX11Image(int d,void * pximage,int * pwidth,int * pheight)3087 Rboolean in_R_GetX11Image(int d, void *pximage, int *pwidth, int *pheight)
3088 {
3089     SEXP dev = elt(findVar(install(".Devices"), R_BaseEnv), d);
3090 
3091     if (TYPEOF(dev) != STRSXP ||
3092 	!(strcmp(CHAR(STRING_ELT(dev, 0)), "XImage") == 0 ||
3093 	  strncmp(CHAR(STRING_ELT(dev, 0)), "PNG", 3) == 0 ||
3094 	  strncmp(CHAR(STRING_ELT(dev, 0)), "X11", 3) == 0))
3095 	return FALSE;
3096     else {
3097 	pX11Desc xd = GEgetDevice(d)->dev->deviceSpecific;
3098 
3099 	*((XImage**) pximage) =
3100 	    XGetImage(display, xd->window, 0, 0,
3101 		      xd->windowWidth, xd->windowHeight,
3102 		      AllPlanes, ZPixmap);
3103 	*pwidth = xd->windowWidth;
3104 	*pheight = xd->windowHeight;
3105 	return TRUE;
3106     }
3107 }
3108 
3109 /**
3110    Allows callers to retrieve the current Display setting for the process.
3111  */
3112 Display*
Rf_getX11Display(void)3113 Rf_getX11Display(void)
3114 {
3115     return(display);
3116 }
3117 
3118 
3119 /**
3120  Allows the caller to register the X11 Display object for the process.
3121  Typically this will be done when the first X device is created, but this allows
3122  other code to generate the Display object and then register it with the R graphics
3123  engine.
3124  In addition to providing the Display, the caller should also give the default value for the
3125  gamma factor and also the colormodel and color cube size. See the documentation for the x11()
3126  function.
3127  Finally, setHandlers controls whether the code establishes handlers for the X errors.
3128  */
3129 int
Rf_setX11Display(Display * dpy,double gamma_fac,X_COLORTYPE colormodel,int maxcube,Rboolean setHandlers)3130 Rf_setX11Display(Display *dpy, double gamma_fac, X_COLORTYPE colormodel,
3131 		 int maxcube, Rboolean setHandlers)
3132 {
3133 /*    static int alreadyDone = 0;
3134     if(alreadyDone) return(TRUE);
3135     alreadyDone = 1; */
3136     display = dpy;
3137 
3138 /* Note: this sets a global gamma, not just for the current device */
3139 #define SETGAMMA
3140 #ifdef SETGAMMA
3141     RedGamma   = gamma_fac;
3142     GreenGamma = gamma_fac;
3143     BlueGamma  = gamma_fac;
3144 #endif
3145     screen = DefaultScreen(display);
3146     rootwin = DefaultRootWindow(display);
3147     group_leader = XCreateSimpleWindow( /* never mapped or visible */
3148 	display, rootwin, 0, 0, 1, 1, 0, 0, 0
3149     );
3150     depth = DefaultDepth(display, screen);
3151     visual = DefaultVisual(display, screen);
3152     colormap = DefaultColormap(display, screen);
3153     Vclass = visual->class;
3154     model = colormodel;
3155     maxcubesize = maxcube;
3156     SetupX11Color();
3157     devPtrContext = XUniqueContext();
3158     displayOpen = TRUE;
3159     /* set error handlers */
3160     if(setHandlers == TRUE) {
3161 	XSetErrorHandler(R_X11Err);
3162 	XSetIOErrorHandler(R_X11IOErr);
3163     }
3164 
3165     return(TRUE);
3166 }
3167 
3168 typedef Rboolean (*X11DeviceDriverRoutine)(pDevDesc, char*,
3169 					   double, double, double, double,
3170 					   X_COLORTYPE, int, int);
3171 
3172 static void
Rf_addX11Device(const char * display,double width,double height,double ps,double gamma,int colormodel,int maxcubesize,int bgcolor,int canvascolor,const char * devname,SEXP sfonts,int res,int xpos,int ypos,const char * title,int useCairo,int antialias,const char * family,const char * symbolfamily,Rboolean usePUA,SEXP call)3173 Rf_addX11Device(const char *display, double width, double height, double ps,
3174 		double gamma, int colormodel, int maxcubesize,
3175 		int bgcolor, int canvascolor, const char *devname, SEXP sfonts,
3176 		int res, int xpos, int ypos, const char *title,
3177 		int useCairo, int antialias, const char * family,
3178                 const char * symbolfamily, Rboolean usePUA, SEXP call)
3179 {
3180     pDevDesc dev = NULL;
3181     pGEDevDesc dd;
3182 
3183     R_GE_checkVersionOrDie(R_GE_version);
3184     R_CheckDeviceAvailable();
3185     BEGIN_SUSPEND_INTERRUPTS {
3186 	/* Allocate and initialize the device driver data */
3187 	if (!(dev = (pDevDesc) calloc(1, sizeof(DevDesc)))) return;
3188 	if (!X11DeviceDriver(dev, display, width, height,
3189 			     ps, gamma, colormodel, maxcubesize,
3190 			     bgcolor, canvascolor, sfonts, res,
3191 			     xpos, ypos, title, useCairo, antialias, family,
3192                              symbolfamily, usePUA)) {
3193 	    free(dev);
3194 	    errorcall(call, _("unable to start device %s"), devname);
3195 	}
3196 	dd = GEcreateDevDesc(dev);
3197 	GEaddDevice2(dd, devname);
3198 
3199 	/* Requires dd to be set up first. */
3200 	R_ProcessX11Events((void*) NULL);
3201 
3202     } END_SUSPEND_INTERRUPTS;
3203 }
3204 
in_do_X11(SEXP call,SEXP op,SEXP args,SEXP env)3205 static SEXP in_do_X11(SEXP call, SEXP op, SEXP args, SEXP env)
3206 {
3207     const char *display, *cname, *devname, *title, *family, *symbolfamily;
3208     const void *vmax;
3209     double height, width, ps, gamma;
3210     int colormodel, maxcubesize, bgcolor, canvascolor, res, xpos, ypos,
3211 	useCairo, antialias;
3212     SEXP sc, sfonts, scsymbol, scusePUA;
3213     Rboolean usePUA;
3214 
3215     checkArity(op, args);
3216     vmax = vmaxget();
3217 
3218     if(R_isForkedChild)
3219 	error("a forked child should not open a graphics device");
3220 
3221     /* Decode the arguments */
3222     display = CHAR(STRING_ELT(CAR(args), 0)); args = CDR(args);
3223     width = asReal(CAR(args));	args = CDR(args);
3224     height = asReal(CAR(args)); args = CDR(args);
3225     if (width <= 0 || height <= 0)
3226 	errorcall(call, _("invalid 'width' or 'height'"));
3227     ps = asReal(CAR(args)); args = CDR(args);
3228     gamma = asReal(CAR(args)); args = CDR(args);
3229     if (gamma < 0 || gamma > 100)
3230 	errorcall(call, _("invalid '%s' value"), "gamma");
3231 
3232     if (!isValidString(CAR(args)))
3233 	error(_("invalid colortype passed to X11 driver"));
3234     cname = CHAR(STRING_ELT(CAR(args), 0));
3235     if (strcmp(cname, "mono") == 0)
3236 	colormodel = 0;
3237     else if (strcmp(cname, "gray") == 0 || strcmp(cname, "grey") == 0)
3238 	colormodel = 1;
3239     else if (strcmp(cname, "pseudo.cube") == 0)
3240 	colormodel = 2;
3241     else if (strcmp(cname, "pseudo") == 0)
3242 	colormodel = 3;
3243     else if (strcmp(cname, "true") == 0)
3244 	colormodel = 4;
3245     else {
3246 	warningcall(call,
3247 		    _("unknown X11 color/colour model -- using monochrome"));
3248 	colormodel = 0;
3249     }
3250     args = CDR(args);
3251     maxcubesize = asInteger(CAR(args));
3252     if (maxcubesize < 1 || maxcubesize > 256)
3253 	maxcubesize = 256;
3254     args = CDR(args);
3255     sc = CAR(args);
3256     if (!isString(sc) && !isInteger(sc) && !isLogical(sc) && !isReal(sc))
3257 	errorcall(call, _("invalid '%s' value"), "bg");
3258     bgcolor = RGBpar(sc, 0);
3259     args = CDR(args);
3260     sc = CAR(args);
3261     if (!isString(sc) && !isInteger(sc) && !isLogical(sc) && !isReal(sc))
3262 	errorcall(call, _("invalid '%s' value"), "canvas");
3263     canvascolor = RGBpar(sc, 0);
3264     args = CDR(args);
3265     sfonts = CAR(args);
3266     if (!isString(sfonts) || LENGTH(sfonts) != 2)
3267 	errorcall(call, _("invalid '%s' value"), "fonts");
3268     args = CDR(args);
3269     res = asInteger(CAR(args));
3270     args = CDR(args);
3271     xpos = asInteger(CAR(args));
3272     args = CDR(args);
3273     ypos = asInteger(CAR(args));
3274     args = CDR(args);
3275     sc = CAR(args);
3276     if (!isString(sc) || LENGTH(sc) != 1)
3277 	errorcall(call, _("invalid '%s' value"), "title");
3278     title = CHAR(STRING_ELT(sc, 0));
3279     args = CDR(args);
3280     useCairo = asInteger(CAR(args));
3281     if (useCairo == NA_INTEGER)
3282 	errorcall(call, _("invalid '%s' value"), "type");
3283     args = CDR(args);
3284     antialias = asInteger(CAR(args));
3285     if (antialias == NA_INTEGER)
3286 	errorcall(call, _("invalid '%s' value"), "antialias");
3287     args = CDR(args);
3288     sc = CAR(args);
3289     if (!isString(sc) || LENGTH(sc) != 1)
3290 	errorcall(call, _("invalid '%s' value"), "family");
3291     family = CHAR(STRING_ELT(sc, 0));
3292     args = CDR(args);
3293     scsymbol = CAR(args);
3294     if (!isString(scsymbol) || LENGTH(scsymbol) != 1)
3295 	errorcall(call, _("invalid '%s' value"), "symbolfamily");
3296     symbolfamily = CHAR(STRING_ELT(scsymbol, 0));
3297     /* scsymbol forced to have "usePUA" attribute in R code */
3298     scusePUA = getAttrib(scsymbol, install("usePUA"));
3299     usePUA = LOGICAL(scusePUA)[0];
3300 
3301     if (!strncmp(display, "png::", 5)) devname = "PNG";
3302     else if (!strncmp(display, "jpeg::", 6)) devname = "JPEG";
3303     else if (!strncmp(display, "tiff::", 6)) devname = "TIFF";
3304     else if (!strncmp(display, "bmp::", 5)) devname = "BMP";
3305     else if (!strcmp(display, "XImage")) devname = "XImage";
3306     else if (useCairo) devname = "X11cairo";
3307     else devname = "X11";
3308 
3309     Rf_addX11Device(display, width, height, ps, gamma, colormodel,
3310 		    maxcubesize, bgcolor, canvascolor, devname, sfonts,
3311 		    res, xpos, ypos, title, useCairo, antialias, family,
3312                     symbolfamily, usePUA, call);
3313     vmaxset(vmax);
3314     return R_NilValue;
3315 }
3316 
3317 
3318 #ifdef HAVE_WORKING_X11_CAIRO
3319 static int stride;
Sbitgp(void * xi,int x,int y)3320 static unsigned int Sbitgp(void *xi, int x, int y)
3321 {
3322     unsigned int *data = xi;
3323     return data[x*stride+y] | 0xFF000000; /* force opaque */
3324 }
3325 
3326 
3327 /* savePlot(filename, type, device) */
in_do_saveplot(SEXP call,SEXP op,SEXP args,SEXP env)3328 static SEXP in_do_saveplot(SEXP call, SEXP op, SEXP args, SEXP env)
3329 {
3330     int devNr;
3331     const char *fn, *type;
3332     pGEDevDesc gdd;
3333     pX11Desc xd;
3334 
3335     checkArity(op, args);
3336     if (!isString(CAR(args)) || LENGTH(CAR(args)) < 1)
3337 	error(_("invalid '%s' argument"), "filename");
3338     fn = R_ExpandFileName(translateChar(STRING_ELT(CAR(args), 0)));
3339     if (!isString(CADR(args)) || LENGTH(CADR(args)) < 1)
3340 	error(_("invalid '%s' argument"), "type");
3341     type = CHAR(STRING_ELT(CADR(args), 0));
3342     devNr = asInteger(CADDR(args));
3343     if (devNr == NA_INTEGER) error(_("invalid '%s' argument"), "device");
3344     gdd = GEgetDevice(devNr - 1); /* 0-based */
3345     if (!gdd->dirty) error(_("no plot on device to save"));
3346     xd = gdd->dev->deviceSpecific;
3347     if (!xd->cs || !xd->useCairo) error(_("not an open X11cairo device"));
3348     if (streql(type, "png")) {
3349 	cairo_status_t res = cairo_surface_write_to_png(xd->cs, fn);
3350 	if (res != CAIRO_STATUS_SUCCESS)
3351 	    error("cairo error '%s'", cairo_status_to_string(res));
3352     }
3353     else if (streql(type, "jpeg")) {
3354 	void *xi = cairo_image_surface_get_data(xd->cs);
3355 	FILE *fp = R_fopen(fn, "w");
3356 	if (!fp) error(_("cannot open file '%s'"), fn);
3357 	stride = xd->windowWidth;
3358 	R_SaveAsJpeg(xi, xd->windowWidth, xd->windowHeight,
3359 		     Sbitgp, 0, 75, fp, 0);
3360 	fclose(fp);
3361     } else if (streql(type, "tiff")) {
3362 	void *xi = cairo_image_surface_get_data(xd->cs);
3363 	stride = xd->windowWidth;
3364 	R_SaveAsTIFF(xi, xd->windowWidth, xd->windowHeight,
3365 		     Sbitgp, 0, fn, 0, 1L);
3366     } else
3367 	error(_("invalid '%s' argument"), "type");
3368     return R_NilValue;
3369 }
3370 #else
in_do_saveplot(SEXP call,SEXP op,SEXP args,SEXP env)3371 static SEXP in_do_saveplot(SEXP call, SEXP op, SEXP args, SEXP env)
3372 {
3373     error(_("savePlot() is not supported on this build"));
3374     return R_NilValue;
3375 }
3376 #endif
3377 
3378 
in_R_X11_access(void)3379 static int in_R_X11_access(void)
3380 {
3381     char *p;
3382     X11IOhandler old;
3383 
3384     if (displayOpen) return TRUE;
3385     if(!(p = getenv("DISPLAY"))) return FALSE;
3386     /* Bill Dunlap sees an error when tunneling to a non-existent
3387        X11 connection that BDR cannot reproduce.  We leave a handler set
3388        if we get an error, but that is rare.
3389     */
3390     old = XSetIOErrorHandler(R_X11IOErrSimple);
3391     if ((display = XOpenDisplay(NULL)) == NULL) {
3392 	XSetIOErrorHandler(old);
3393 	return FALSE;
3394     } else {
3395 	XCloseDisplay(display);
3396 	XSetIOErrorHandler(old);
3397 	return TRUE;
3398     }
3399 }
3400 
in_R_X11readclp(Rclpconn this,char * type)3401 static Rboolean in_R_X11readclp(Rclpconn this, char *type)
3402 {
3403     Window clpwin;
3404     Atom sel = XA_PRIMARY, pty, pty_type;
3405     XEvent evt;
3406     unsigned char *buffer;
3407     unsigned long pty_size, pty_items;
3408     int pty_format, ret;
3409     Rboolean res = TRUE;
3410 
3411     if (!displayOpen) {
3412 	if ((display = XOpenDisplay(NULL)) == NULL) {
3413 	    warning(_("unable to contact X11 display"));
3414 	    return FALSE;
3415 	}
3416     }
3417     if(strcmp(type, "X11_secondary") == 0) sel = XA_SECONDARY;
3418     if(strcmp(type, "X11_clipboard") == 0)
3419 #ifdef HAVE_X11_Xmu
3420       sel = XA_CLIPBOARD(display);
3421 #else
3422       error("X11 clipboard selection is not supported on this system");
3423 #endif
3424 
3425     pty = XInternAtom(display, "RCLIP_READ", False);
3426 
3427     clpwin = XCreateSimpleWindow(display, DefaultRootWindow(display),
3428 				 0, 0, 1, 1, 0, 0, 0);
3429     /* <FIXME> this is not optimal in a UTF-8 locale.
3430        What we should do is see if UTF-8 extensions are available
3431        (via X_HAVE_UTF8_STRING) then ask with target TARGETS and see if
3432        UTF8_STRING is available.  See
3433        http://www.pps.jussieu.fr/~jch/software/UTF8_STRING/UTF8_STRING.text
3434     */
3435 
3436     /* send a selection request */
3437     ret = XConvertSelection(display, sel, XA_STRING, pty, clpwin, CurrentTime);
3438 
3439     /* wait for the response */
3440     while(1) {
3441 	XNextEvent(display, &evt);
3442 	if (evt.type == SelectionNotify) break;
3443     }
3444 
3445     /* find the size and format of the data in the selection */
3446     ret = XGetWindowProperty(display, clpwin, pty, 0, 0, False, AnyPropertyType,
3447 			     &pty_type, &pty_format, &pty_items, &pty_size,
3448 			     &buffer);
3449     if (ret) {
3450 	warning(_("clipboard cannot be opened or contains no text"));
3451 	res = FALSE;
3452     } else {
3453 	XFree(buffer);
3454 	if (pty_format != 8) { /* bytes */
3455 	    warning(_("clipboard cannot be opened or contains no text"));
3456 	    res = FALSE;
3457 	} else { /* read the property */
3458 	    ret = XGetWindowProperty(display, clpwin, pty, 0, (long)pty_size, False,
3459 				     AnyPropertyType, &pty_type, &pty_format,
3460 				     &pty_items, &pty_size, &buffer);
3461 	    if (ret) {
3462 		warning(_("clipboard cannot be read (error code %d)"), ret);
3463 		res = FALSE;
3464 	    } else {
3465 		this->buff = (char *)malloc(pty_items + 1);
3466 		this->last = this->len = (int) pty_items;
3467 		if(this->buff) {
3468 		    /* property always ends in 'extra' zero byte */
3469 		    memcpy(this->buff, buffer, pty_items + 1);
3470 		} else {
3471 		    warning(_("memory allocation to copy clipboard failed"));
3472 		    res = FALSE;
3473 		}
3474 		XFree(buffer);
3475 	    }
3476 	}
3477     }
3478 
3479     XDeleteProperty(display, clpwin, pty);
3480     if (!displayOpen) {
3481 	XCloseDisplay(display);
3482 	strcpy(dspname, "");
3483     }
3484     return res;
3485 }
3486 
3487 #include <R_ext/Rdynload.h>
3488 
3489 extern const char * in_R_pngVersion(void);
3490 extern const char * in_R_jpegVersion(void);
3491 extern const char * in_R_tiffVersion(void);
3492 
R_init_R_X11(DllInfo * info)3493 void R_init_R_X11(DllInfo *info)
3494 {
3495     R_X11Routines *tmp;
3496     tmp = (R_X11Routines*) malloc(sizeof(R_X11Routines));
3497     if(!tmp) {
3498 	error(_("cannot allocate memory for X11Routines structure"));
3499 	return;
3500     }
3501     tmp->X11 = in_do_X11;
3502     tmp->saveplot = in_do_saveplot;
3503     tmp->image = in_R_GetX11Image;
3504     tmp->access = in_R_X11_access;
3505     tmp->readclp = in_R_X11readclp;
3506     tmp->R_pngVersion = in_R_pngVersion;
3507     tmp->R_jpegVersion = in_R_jpegVersion;
3508     tmp->R_tiffVersion = in_R_tiffVersion;
3509     R_setX11Routines(tmp);
3510 }
3511