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