The R Project SVN R

Rev

Rev 75867 | Rev 78446 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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