The R Project SVN R

Rev

Rev 27675 | Rev 29948 | 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
22913 ripley 4
 *  Copyright (C) 1997--2003  Robert Gentleman, Ross Ihaka and the
13974 hornik 5
 *			      R Development Core Team
6
 *
7
 *  This program is free software; you can redistribute it and/or modify
8
 *  it under the terms of the GNU General Public License as published by
9
 *  the Free Software Foundation; either version 2 of the License, or
10
 *  (at your option) any later version.
11
 *
12
 *  This program is distributed in the hope that it will be useful,
13
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 *  GNU General Public License for more details.
16
 *
17
 *  You should have received a copy of the GNU General Public License
18
 *  along with this program; if not, write to the Free Software
19
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
 */
21
 
22
#ifdef HAVE_CONFIG_H
19500 hornik 23
# include <config.h>
13974 hornik 24
#endif
25
 
26
#ifdef HAVE_RINT
27
#define R_rint(x) rint(x)
28
#else
29
#define R_rint(x) ((int) x + 0.5)
30
#endif
31
 
32
#include <stdio.h>
33
#include <X11/X.h>
34
#include <X11/Xlib.h>
35
#include <X11/Xatom.h>
36
#include <X11/Xutil.h>
37
#include <X11/cursorfont.h>
19500 hornik 38
#include <X11/Intrinsic.h>	/*->	Xlib.h	Xutil.h Xresource.h .. */
13974 hornik 39
 
40
 
41
#include "Defn.h"
42
#include "Graphics.h"
19500 hornik 43
#include "Fileio.h"		/* R_fopen */
44
#include "rotated.h"		/* 'Public' routines from here */
13974 hornik 45
/* For the input handlers of the event loop mechanism: */
20430 maechler 46
#include <R_ext/eventloop.h>
19500 hornik 47
#include <R_ext/Memory.h>	/* vmaxget */
13974 hornik 48
#include "Rdevices.h"
49
 
50
#define R_X11_DEVICE 1
51
#include "devX11.h"
52
 
19500 hornik 53
#include <R_ext/RX11.h>
14945 duncan 54
 
13974 hornik 55
#define CURSOR		XC_crosshair		/* Default cursor */
56
#define MM_PER_INCH	25.4			/* mm -> inch conversion */
57
 
58
#define IS_100DPI ((int) (1./pixelHeight() + 0.5) == 100)
59
 
60
 
20430 maechler 61
#define X_BELL_VOLUME 0 /* integer between -100 and 100 for the volume
13974 hornik 62
                            of the bell in locator. */
20430 maechler 63
			/* Note: This is in relation to
18640 pd 64
			the general bell level. Was 50, but if > 0
65
			then "xset b off" will not disable the
66
			locator bell - pd 2002-3-11 */
16124 ripley 67
/* a colour used to represent the background on png if transparent
68
   NB: must be grey as used as RGB and BGR
69
*/
70
#define PNG_TRANS 0xfefefe
71
 
13974 hornik 72
	/********************************************************/
73
	/* If there are resources that are shared by all devices*/
74
	/* of this type, you may wish to make them globals	*/
75
	/* rather than including them in the device-specific	*/
76
	/* parameters structure (especially if they are large !)*/
77
	/********************************************************/
78
 
79
	/* X11 Driver Specific parameters
80
	 * with only one copy for all x11 devices */
81
 
82
static Display *display;			/* Display */
83
static int screen;				/* Screen */
84
static Window rootwin;				/* Root Window */
85
static Visual *visual;				/* Visual */
86
static int depth;				/* Pixmap depth */
87
static int Vclass;				/* Visual class */
88
static X_COLORTYPE model;			/* User color model */
89
static int maxcubesize;				/* Max colorcube size */
90
static XSetWindowAttributes attributes;		/* Window attributes */
91
static Colormap colormap;			/* Default color map */
92
static int blackpixel;				/* Black */
93
static int whitepixel;				/* White */
94
static XContext devPtrContext;
95
static Atom _XA_WM_PROTOCOLS, protocol;
96
 
97
static Rboolean displayOpen = FALSE;
98
static Rboolean inclose = FALSE;
99
static int numX11Devices = 0;
100
 
101
	/********************************************************/
102
	/* There must be an entry point for the device driver	*/
103
	/* which will create device-specific resources,		*/
104
	/* initialise the device-specific parameters structure	*/
105
	/* and return whether the setup succeeded		*/
106
	/* This is called by the graphics engine when the user	*/
107
	/* creates a new device of this type			*/
108
	/********************************************************/
109
 
110
 
111
	/********************************************************/
112
	/* There are a number of actions that every device	*/
113
	/* driver is expected to perform (even if, in some	*/
114
	/* cases it does nothing - just so long as it doesn't	*/
115
	/* crash !).  this is how the graphics engine interacts */
116
	/* with each device. ecah action will be documented	*/
117
	/* individually.					*/
118
	/* hooks for these actions must be set up when the	*/
119
	/* device is first created				*/
120
	/********************************************************/
121
 
122
	/* Device Driver Actions */
123
 
16935 hornik 124
static void newX11_Activate(NewDevDesc *dd);
125
static void newX11_Circle(double x, double y, double r,
27236 murrell 126
			  R_GE_gcontext *gc,
17165 murrell 127
			  NewDevDesc *dd);
20430 maechler 128
static void newX11_Clip(double x0, double x1, double y0, double y1,
27236 murrell 129
			NewDevDesc *dd);
16935 hornik 130
static void newX11_Close(NewDevDesc *dd);
131
static void newX11_Deactivate(NewDevDesc *dd);
132
static void newX11_Hold(NewDevDesc *dd);
133
static Rboolean newX11_Locator(double *x, double *y, NewDevDesc *dd);
134
static void newX11_Line(double x1, double y1, double x2, double y2,
27236 murrell 135
			R_GE_gcontext *gc,
17165 murrell 136
			NewDevDesc *dd);
27236 murrell 137
static void newX11_MetricInfo(int c, 
138
			      R_GE_gcontext *gc,
16935 hornik 139
			      double* ascent, double* descent,
140
			      double* width, NewDevDesc *dd);
141
static void newX11_Mode(int mode, NewDevDesc *dd);
27236 murrell 142
static void newX11_NewPage(R_GE_gcontext *gc, NewDevDesc *dd);
20430 maechler 143
Rboolean newX11_Open(NewDevDesc *dd, newX11Desc *xd,
144
		     char *dsp, double w, double h,
145
		     double gamma_fac, X_COLORTYPE colormodel,
16935 hornik 146
		     int maxcube, int canvascolor);
20430 maechler 147
static void newX11_Polygon(int n, double *x, double *y,
27236 murrell 148
			   R_GE_gcontext *gc,
17165 murrell 149
			   NewDevDesc *dd);
20430 maechler 150
static void newX11_Polyline(int n, double *x, double *y,
27236 murrell 151
			    R_GE_gcontext *gc,
16935 hornik 152
			    NewDevDesc *dd);
153
static void newX11_Rect(double x0, double y0, double x1, double y1,
27236 murrell 154
			R_GE_gcontext *gc,
17165 murrell 155
			NewDevDesc *dd);
16935 hornik 156
static void newX11_Size(double *left, double *right,
157
		     double *bottom, double *top,
158
		     NewDevDesc *dd);
27236 murrell 159
static double newX11_StrWidth(char *str, 
160
			      R_GE_gcontext *gc,
161
			      NewDevDesc *dd);
20430 maechler 162
static void newX11_Text(double x, double y, char *str,
163
			double rot, double hadj,
27236 murrell 164
			R_GE_gcontext *gc,
17165 murrell 165
			NewDevDesc *dd);
13974 hornik 166
 
167
	/*************************************************/
168
	/* End of list of required device driver actions */
169
	/*************************************************/
170
 
171
	/* Support Routines */
172
 
27069 ripley 173
static XFontStruct *RLoadFont(newX11Desc*, int, int);
13974 hornik 174
static double pixelHeight(void);
175
static double pixelWidth(void);
16935 hornik 176
static int SetBaseFont(newX11Desc*);
177
static void SetColor(int, NewDevDesc*);
178
static void SetFont(int, int, NewDevDesc*);
179
static void SetLinetype(int, double, NewDevDesc*);
21022 ripley 180
static void X11_Close_bitmap(newX11Desc *xd);
13974 hornik 181
 
182
 
21022 ripley 183
 
13974 hornik 184
	/************************/
185
	/* X11 Color Management */
186
	/************************/
187
 
188
static double RedGamma	 = 0.6;
189
static double GreenGamma = 0.6;
190
static double BlueGamma	 = 0.6;
191
 
192
 
193
/* Variables Used To Store Colormap Information */
194
static struct { int red; int green; int blue; } RPalette[512];
195
static XColor XPalette[512];
196
static int PaletteSize;
197
 
198
 
199
/* Monochome Displays : Compute pixel values by converting */
200
/* RGB values to luminance and then thresholding. */
201
/* See: Foley & van Damm. */
202
 
203
static void SetupMonochrome()
204
{
205
    depth = 1;
206
}
207
 
208
static unsigned GetMonochromePixel(int r, int g, int b)
209
{
210
    if ((int)(0.299 * r + 0.587 * g + 0.114 * b) > 127)
211
	return WhitePixel(display, screen);
212
    else
213
	return BlackPixel(display, screen);
214
}
215
 
216
 
217
/* Grayscale Displays : Compute pixel values by converting */
218
/* RGB values to luminance.  See: Foley & van Damm. */
219
 
220
static unsigned GetGrayScalePixel(int r, int g, int b)
221
{
222
    unsigned int d, dmin = 0xFFFFFFFF;
223
    unsigned int dr;
224
    int i;
225
    unsigned int pixel = 0;  /* -Wall */
226
    int gray = (0.299 * r + 0.587 * g + 0.114 * b) + 0.0001;
227
    for (i = 0; i < PaletteSize; i++) {
228
	dr = (RPalette[i].red - gray);
229
	d = dr * dr;
230
	if (d < dmin) {
231
	    pixel = XPalette[i].pixel;
232
	    dmin = d;
233
	}
234
    }
235
    return pixel;
236
}
237
 
238
static Rboolean GetGrayPalette(Display *displ, Colormap cmap, int n)
239
{
240
    int status, i, m;
241
    m = 0;
242
    i = 0;
243
    for (i = 0; i < n; i++) {
244
	RPalette[i].red	  = (i * 0xff) / (n - 1);
245
	RPalette[i].green = RPalette[i].red;
246
	RPalette[i].blue  = RPalette[i].red;
247
	/* Gamma correct here */
248
	XPalette[i].red	  = (i * 0xffff) / (n - 1);
249
	XPalette[i].green = XPalette[i].red;
250
	XPalette[i].blue  = XPalette[i].red;
251
	status = XAllocColor(displ, cmap, &XPalette[i]);
252
	if (status == 0) {
253
	    XPalette[i].flags = 0;
254
	    m++;
255
	}
256
	else
257
	    XPalette[i].flags = DoRed|DoGreen|DoBlue;
258
    }
259
    PaletteSize = n;
260
    if (m > 0) {
261
	for (i = 0; i < PaletteSize; i++) {
262
	    if (XPalette[i].flags != 0)
263
		XFreeColors(displ, cmap, &(XPalette[i].pixel), 1, 0);
264
	}
265
	PaletteSize = 0;
266
	return FALSE;
267
    }
268
    else return TRUE;
269
}
270
 
271
static void SetupGrayScale()
272
{
273
    int res = 0, d;
274
    PaletteSize = 0;
275
    /* try for 128 grays on an 8-bit display */
276
    if (depth > 8) d = depth = 8; else d = depth - 1;
277
    /* try (256), 128, 64, 32, 16 grays */
278
    while (d >= 4 && !(res = GetGrayPalette(display, colormap, 1 << d)))
279
	d--;
280
    if (!res) {
281
	/* Can't find a sensible grayscale, so revert to monochrome */
282
	warning("can't set grayscale: reverting to monochrome");
283
	model = MONOCHROME;
284
	SetupMonochrome();
285
    }
286
}
287
 
288
/* PseudoColor Displays : There are two strategies here. */
289
/* 1) allocate a standard color cube and match colors */
290
/* within that based on (weighted) distances in RGB space. */
291
/* 2) allocate colors exactly as they are requested until */
292
/* all color cells are used.  Fail with an error message */
293
/* when this happens. */
294
 
295
static int RGBlevels[][3] = {  /* PseudoColor Palettes */
296
    { 8, 8, 4 },
297
    { 6, 7, 6 },
298
    { 6, 6, 6 },
299
    { 6, 6, 5 },
300
    { 6, 6, 4 },
301
    { 5, 5, 5 },
302
    { 5, 5, 4 },
303
    { 4, 4, 4 },
304
    { 4, 4, 3 },
305
    { 3, 3, 3 },
306
    { 2, 2, 2 }
307
};
308
static int NRGBlevels = sizeof(RGBlevels) / (3 * sizeof(int));
309
 
310
 
311
static int GetColorPalette(Display *dpy, Colormap cmap, int nr, int ng, int nb)
312
{
313
    int status, i, m, r, g, b;
314
    m = 0;
315
    i = 0;
316
    for (r = 0; r < nr; r++) {
317
	for (g = 0; g < ng; g++) {
318
	    for (b = 0; b < nb; b++) {
319
		RPalette[i].red	  = (r * 0xff) / (nr - 1);
320
		RPalette[i].green = (g * 0xff) / (ng - 1);
321
		RPalette[i].blue  = (b * 0xff) / (nb - 1);
322
		/* Perform Gamma Correction Here */
323
		XPalette[i].red	  = pow(r / (nr - 1.0), RedGamma) * 0xffff;
324
		XPalette[i].green = pow(g / (ng - 1.0), GreenGamma) * 0xffff;
325
		XPalette[i].blue  = pow(b / (nb - 1.0), BlueGamma) * 0xffff;
326
		/* End Gamma Correction */
327
		status = XAllocColor(dpy, cmap, &XPalette[i]);
328
		if (status == 0) {
329
		    XPalette[i].flags = 0;
330
		    m++;
331
		}
332
		else
333
		    XPalette[i].flags = DoRed|DoGreen|DoBlue;
334
		i++;
335
	    }
336
	}
337
    }
338
    PaletteSize = nr * ng * nb;
339
    if (m > 0) {
340
	for (i = 0; i < PaletteSize; i++) {
341
	    if (XPalette[i].flags != 0)
342
		XFreeColors(dpy, cmap, &(XPalette[i].pixel), 1, 0);
343
	}
344
	PaletteSize = 0;
345
	return 0;
346
    }
347
    else
348
	return 1;
349
}
350
 
351
static void SetupPseudoColor()
352
{
353
    int i, size;
354
    PaletteSize = 0;
355
    if (model == PSEUDOCOLOR1) {
356
	for (i = 0; i < NRGBlevels; i++) {
357
	    size = RGBlevels[i][0] * RGBlevels[i][1] * RGBlevels[i][2];
358
	    if (size < maxcubesize && GetColorPalette(display, colormap,
359
				RGBlevels[i][0],
360
				RGBlevels[i][1],
361
				RGBlevels[i][2]))
362
		break;
363
	}
364
	if (PaletteSize == 0) {
365
	    warning("X11 driver unable to obtain color cube\n  reverting to monochrome");
366
	    model = MONOCHROME;
367
	    SetupMonochrome();
368
	}
369
    }
370
    else {
371
	PaletteSize = 0;
372
    }
373
}
374
 
375
static unsigned int GetPseudoColor1Pixel(int r, int g, int b)
376
{
377
    unsigned int d, dmin = 0xFFFFFFFF;
378
    unsigned int dr, dg, db;
379
    unsigned int pixel;
380
    int i;
381
    pixel = 0;			/* -Wall */
382
    for (i = 0; i < PaletteSize; i++) {
383
	dr = (RPalette[i].red - r);
384
	dg = (RPalette[i].green - g);
385
	db = (RPalette[i].blue - b);
386
	d = dr * dr + dg * dg + db * db;
387
	if (d < dmin) {
388
	    pixel = XPalette[i].pixel;
389
	    dmin = d;
390
	}
391
    }
392
    return pixel;
393
}
394
 
395
static unsigned int GetPseudoColor2Pixel(int r, int g, int b)
396
{
397
    int i;
398
    /* Search for previously allocated color */
399
    for (i = 0; i < PaletteSize ; i++) {
400
	if (r == RPalette[i].red &&
401
	    g == RPalette[i].green &&
402
	    b == RPalette[i].blue) return XPalette[i].pixel;
403
    }
404
    /* Attempt to allocate a new color */
405
    XPalette[PaletteSize].red	= pow(r / 255.0, RedGamma) * 0xffff;
406
    XPalette[PaletteSize].green = pow(g / 255.0, GreenGamma) * 0xffff;
407
    XPalette[PaletteSize].blue	= pow(b / 255.0, BlueGamma) * 0xffff;
408
    if (PaletteSize == 256 ||
409
	XAllocColor(display, colormap, &XPalette[PaletteSize]) == 0) {
410
	error("Error: X11 cannot allocate additional graphics colors.\n"
411
	      "Consider using X11 with colortype=\"pseudo.cube\" or \"gray\".");
412
    }
413
    RPalette[PaletteSize].red = r;
414
    RPalette[PaletteSize].green = g;
415
    RPalette[PaletteSize].blue = b;
416
    PaletteSize++;
417
    return XPalette[PaletteSize - 1].pixel;
418
}
419
 
420
static unsigned int GetPseudoColorPixel(int r, int g, int b)
421
{
422
    if (model == PSEUDOCOLOR1)
423
	return GetPseudoColor1Pixel(r, g, b);
424
    else
425
	return GetPseudoColor2Pixel(r, g, b);
426
}
427
 
428
/* Truecolor Displays : Allocate the colors as they are requested */
429
 
430
static unsigned int RMask, RShift;
431
static unsigned int GMask, GShift;
432
static unsigned int BMask, BShift;
433
 
434
static void SetupTrueColor()
435
{
436
    RMask = visual->red_mask;
437
    GMask = visual->green_mask;
438
    BMask = visual->blue_mask;
439
    RShift = 0; while ((RMask & 1) == 0) { RShift++; RMask >>= 1; }
440
    GShift = 0; while ((GMask & 1) == 0) { GShift++; GMask >>= 1; }
441
    BShift = 0; while ((BMask & 1) == 0) { BShift++; BMask >>= 1; }
442
}
443
 
444
static unsigned GetTrueColorPixel(int r, int g, int b)
445
{
446
    r = pow((r / 255.0), RedGamma) * 255;
447
    g = pow((g / 255.0), GreenGamma) * 255;
448
    b = pow((b / 255.0), BlueGamma) * 255;
449
    return
450
	(((r * RMask) / 255) << RShift) |
451
	(((g * GMask) / 255) << GShift) |
452
	(((b * BMask) / 255) << BShift);
453
}
454
 
455
/* Interface for General Visual */
456
 
457
static unsigned int GetX11Pixel(int r, int g, int b)
458
{
459
    switch(model) {
460
    case MONOCHROME:
461
	return GetMonochromePixel(r, g, b);
462
    case GRAYSCALE:
463
	return GetGrayScalePixel(r, g, b);
464
    case PSEUDOCOLOR1:
465
    case PSEUDOCOLOR2:
466
	return GetPseudoColorPixel(r, g, b);
467
    case TRUECOLOR:
468
	return GetTrueColorPixel(r, g, b);
469
    default:
470
	printf("Unknown Visual\n");
471
    }
472
    return 0;
473
}
474
 
475
static void FreeX11Colors()
476
{
477
    int i;
478
    if (model == PSEUDOCOLOR2) {
479
	for (i = 0; i < PaletteSize; i++)
480
	    XFreeColors(display, colormap, &(XPalette[i].pixel), 1, 0);
481
	PaletteSize = 0;
482
    }
483
}
484
 
485
static Rboolean SetupX11Color()
486
{
487
    if (depth <= 1) {
488
	/* On monchome displays we must use black/white */
489
	model = MONOCHROME;
490
	SetupMonochrome();
491
    }
492
    else if (Vclass ==	StaticGray || Vclass == GrayScale) {
493
	if (model == MONOCHROME)
494
	    SetupMonochrome();
495
	else {
496
	    model = GRAYSCALE;
497
	    SetupGrayScale();
498
	}
499
    }
500
    else if (Vclass == StaticColor) {
501
	/* FIXME : Currently revert to mono. */
502
	/* Should do the real thing. */
503
	model = MONOCHROME;
504
	SetupMonochrome();
505
    }
506
    else if (Vclass ==	PseudoColor) {
507
	if (model == MONOCHROME)
508
	    SetupMonochrome();
509
	else if (model == GRAYSCALE)
510
	    SetupGrayScale();
511
	else {
512
	    if (model == TRUECOLOR)
513
		model = PSEUDOCOLOR2;
514
	    SetupPseudoColor();
515
	}
516
    }
517
    else if (Vclass == TrueColor) {
518
	if (model == MONOCHROME)
519
	    SetupMonochrome();
520
	else if (model == GRAYSCALE)
521
	    SetupGrayScale();
522
	else if (model == PSEUDOCOLOR1 || model == PSEUDOCOLOR2)
523
	    SetupPseudoColor();
524
	else
525
	    SetupTrueColor();
526
    }
527
    else if (Vclass == DirectColor) {
528
	/* FIXME : Currently revert to mono. */
529
	/* Should do the real thing. */
530
	model = MONOCHROME;
531
	SetupMonochrome();
532
    }
533
    else {
534
	printf("Unknown Visual\n");
535
	return FALSE;
536
    }
537
    return TRUE;
538
}
539
 
540
	/* Pixel Dimensions (Inches) */
541
 
542
 
543
static double pixelWidth(void)
544
{
545
    double width, widthMM;
546
    width = DisplayWidth(display, screen);
547
    widthMM = DisplayWidthMM(display, screen);
548
    return ((double)widthMM / (double)width) / MM_PER_INCH;
549
}
550
 
551
static double pixelHeight(void)
552
{
553
    double height, heightMM;
554
    height = DisplayHeight(display, screen);
555
    heightMM = DisplayHeightMM(display, screen);
556
    return ((double)heightMM / (double)height) / MM_PER_INCH;
557
}
558
 
559
static void handleEvent(XEvent event)
560
{
561
    caddr_t temp;
23953 hornik 562
    NewDevDesc *dd = NULL;	/* -Wall */
16935 hornik 563
    newX11Desc *xd;
17385 murrell 564
    int devNum = 0;
23801 pd 565
    int do_update = 0;
13974 hornik 566
 
567
    if (event.xany.type == Expose) {
568
	while(XCheckTypedEvent(display, Expose, &event))
569
	    ;
570
	XFindContext(display, event.xexpose.window,
571
		     devPtrContext, &temp);
16935 hornik 572
	dd = (NewDevDesc *) temp;
23801 pd 573
	if (event.xexpose.count == 0)
574
	    do_update = 1;
13974 hornik 575
    }
576
    else if (event.type == ConfigureNotify) {
23801 pd 577
	while(XCheckTypedEvent(display, ConfigureNotify, &event))
578
	    ;
13974 hornik 579
	XFindContext(display, event.xconfigure.window,
580
		     devPtrContext, &temp);
16935 hornik 581
	dd = (NewDevDesc *) temp;
582
	xd = (newX11Desc *) dd->deviceSpecific;
23801 pd 583
	if (xd->windowWidth != event.xconfigure.width ||
584
	    xd->windowHeight != event.xconfigure.height)
585
	    do_update = 1;
13974 hornik 586
	xd->windowWidth = event.xconfigure.width;
587
	xd->windowHeight = event.xconfigure.height;
23801 pd 588
        dd->size(&(dd->left), &(dd->right), &(dd->bottom), &(dd->top),
589
		     dd);
590
 
591
	if (do_update) /* Gobble Expose events; we'll redraw anyway */
592
	    while(XCheckTypedEvent(display, Expose, &event))
593
		;
13974 hornik 594
    }
595
    else if ((event.type == ClientMessage) &&
596
	     (event.xclient.message_type == _XA_WM_PROTOCOLS))
597
	if (!inclose && event.xclient.data.l[0] == protocol) {
598
	    XFindContext(display, event.xclient.window,
599
			 devPtrContext, &temp);
16935 hornik 600
	    dd = (NewDevDesc *) temp;
16966 murrell 601
	    KillDevice((DevDesc*) GetDevice(devNumber((DevDesc*) dd)));
13974 hornik 602
	}
23801 pd 603
 
604
    if (do_update) {
605
	/* It appears possible that a device may receive an expose
606
	 * event in the middle of the device being "kill"ed by R
607
	 * This means that R knows nothing about the device
608
	 * so devNumber becomes 0 (the null device) and it is not
609
	 * a good idea to pass the null device to GEplayDisplayList
610
	 */
611
	devNum = devNumber((DevDesc*) dd);
612
	if (devNum > 0)
613
	    GEplayDisplayList((GEDevDesc*) GetDevice(devNum));
614
    }
13974 hornik 615
}
616
 
617
static void R_ProcessEvents(void *data)
618
{
619
    XEvent event;
620
 
621
    while (displayOpen && XPending(display)) {
622
	XNextEvent(display, &event);
623
	/* printf("%i\n",event.type); */
624
	handleEvent(event);
625
    }
626
}
627
 
27069 ripley 628
static char *fontname = "-adobe-helvetica-%s-%s-*-*-%d-*-*-*-*-*-*-*";
629
static char *symbolname	 = "-adobe-symbol-*-*-*-*-%d-*-*-*-*-*-*-*";
13974 hornik 630
 
631
static char *slant[]  = {"r", "o"};
632
static char *weight[] = {"medium", "bold"};
633
 
634
/* Bitmap of the Adobe design sizes */
635
 
636
static unsigned int adobe_sizes = 0x0403165D;
637
 
638
#define MAXFONTS 64
639
#define CLRFONTS 16 /* Number to free when cache runs full */
640
 
641
typedef struct {int face, size;	 XFontStruct *font;} cacheentry;
642
 
643
static cacheentry fontcache[MAXFONTS];
644
static int nfonts = 0;
645
static int force_nonscalable = 0; /* for testing */
646
 
647
#define ADOBE_SIZE(I) ((I) > 7 && (I) < 35 && (adobe_sizes & (1<<((I)-8))))
648
#define SMALLEST 2
649
 
27069 ripley 650
static XFontStruct *RLoadFont(newX11Desc *xd, int face, int size)
13974 hornik 651
{
652
    int pixelsize, i;
653
    cacheentry *f;
654
    char buf[128];
655
    XFontStruct *tmp;
656
 
657
    if (size < SMALLEST) size = SMALLEST;
658
    face--;
659
 
660
    /* Here's a 1st class fudge: make sure that the Adobe design sizes
661
       8, 10, 11, 12, 14, 17, 18, 20, 24, 25, 34 can be obtained via
662
       an integer "size" at 100 dpi, namely 6, 7, 8, 9, 10, 12, 13,
663
       14, 17, 18, 24 points. It's almost y = x * 100/72, but not
664
       quite. The constants were found using lm(). --pd */
665
    if (IS_100DPI) size = R_rint(size * 1.43 - 0.4);
666
 
667
    /* search fontcache */
668
    for ( i = nfonts ; i-- ; ) {
669
	f = &fontcache[i];
670
	if ( f->face == face && f->size == size ) return f->font;
671
    }
672
 
673
    /* 'size' is the requested size, 'pixelsize'  the size of the
674
       actually allocated font*/
675
    pixelsize = size;
676
 
677
    if (face == 4)
27069 ripley 678
	sprintf(buf, xd->symbolfamily,  pixelsize);
13974 hornik 679
    else
27069 ripley 680
	sprintf(buf, xd->fontfamily,
13974 hornik 681
		weight[face & 1],
682
		slant[(face & 2)>>1 ],	pixelsize);
683
#ifdef DEBUG_X11
684
    Rprintf("loading:\n%s\n",buf);
685
#endif
686
    tmp = XLoadQueryFont(display, buf);
687
#ifdef DEBUG_X11
688
    if (tmp) Rprintf("success\n"); else Rprintf("failure\n");
689
#endif
690
    if (!tmp || (force_nonscalable && !ADOBE_SIZE(size)) ){
691
	static int near[]=
692
	  {14,14,14,17,17,18,20,20,20,20,24,24,24,25,25,25,25};
693
	/* 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29  */
694
 
695
	/* If ADOBE_SIZE(pixelsize) is true at this point then
696
	   the user's system does not have the standard ADOBE font set
697
	   so we just have to use a "fixed" font.
698
	   If we can't find a "fixed" font then something is seriously
699
	   wrong */
700
	if ( ADOBE_SIZE(pixelsize) ) {
701
	    tmp = XLoadQueryFont(display, "fixed");
702
	    if (tmp)
703
		return tmp;
704
	    else
705
		error("Could not find any X11 fonts\nCheck that the Font Path is correct.");
706
	}
707
 
708
	if ( pixelsize < 8 )
709
	    pixelsize = 8;
710
	else if (pixelsize == 9)
711
	    pixelsize = 8;
712
	else if (pixelsize < 30) /* must be at least 13 */
713
	    pixelsize = near[size-13];
714
	else
715
	    pixelsize = 34;
716
 
717
 
718
	if (face == 4)
719
	    sprintf(buf, symbolname, pixelsize);
720
	else
721
	    sprintf(buf, fontname,
722
		    weight[face & 1],
723
		    slant[(face & 2)>>1 ],  pixelsize);
724
#ifdef DEBUG_X11
725
	Rprintf("loading:\n%s\n",buf);
726
#endif
727
	tmp = XLoadQueryFont(display, buf);
728
#ifdef DEBUG_X11
729
	if (tmp) Rprintf("success\n"); else Rprintf("failure\n");
730
#endif
731
    }
732
    if(!tmp && size > 24) {
733
	/* final try, size 24 */
734
	pixelsize = 24;
735
	if (face == 4)
736
	    sprintf(buf, symbolname, 24);
737
	else
738
	    sprintf(buf, fontname,
739
		    weight[face & 1],
740
		    slant[(face & 2)>>1 ],  24);
741
#ifdef DEBUG_X11
742
	Rprintf("loading:\n%s\n",buf);
743
#endif
744
	tmp = XLoadQueryFont(display, buf);
745
#ifdef DEBUG_X11
746
	if (tmp) Rprintf("success\n"); else Rprintf("failure\n");
747
#endif
748
    }
749
 
20430 maechler 750
 
13974 hornik 751
    if (tmp){
752
	f = &fontcache[nfonts++];
753
	f->face = face;
754
	f->size = size;
755
	f->font = tmp;
756
	if (fabs( (pixelsize - size)/(double)size ) > 0.1)
757
	    warning("X11 used font size %d when %d was requested",
758
		    pixelsize, size);
759
    }
760
    if (nfonts == MAXFONTS) /* make room in the font cache */
761
    {
762
	for (i = 0 ; i < CLRFONTS ; i++)
763
	    XFreeFont(display, fontcache[i].font);
764
	for (i = CLRFONTS ; i < MAXFONTS ; i++)
765
	    fontcache[i - CLRFONTS] = fontcache[i];
766
	nfonts -= CLRFONTS;
767
    }
768
    return tmp;
769
}
770
 
16935 hornik 771
static int SetBaseFont(newX11Desc *xd)
13974 hornik 772
{
773
    xd->fontface = xd->basefontface;
774
    xd->fontsize = xd->basefontsize;
775
    xd->usefixed = 0;
27069 ripley 776
    xd->font = RLoadFont(xd, xd->fontface, xd->fontsize);
13974 hornik 777
    if (!xd->font) {
778
	xd->usefixed = 1;
779
	xd->font = xd->fixedfont = XLoadQueryFont(display, "fixed");
780
	if (!xd->fixedfont)
781
	    return 0;
782
    }
783
    return 1;
784
}
785
 
16935 hornik 786
static void SetFont(int face, int size, NewDevDesc *dd)
13974 hornik 787
{
16935 hornik 788
    newX11Desc *xd = (newX11Desc *) dd->deviceSpecific;
13974 hornik 789
    XFontStruct *tmp;
790
 
791
    if (face < 1 || face > 5) face = 1;
792
 
793
    if (!xd->usefixed && (size != xd->fontsize	|| face != xd->fontface)) {
27069 ripley 794
	tmp = RLoadFont(xd, face, size);
13974 hornik 795
	if(tmp) {
796
	    xd->font = tmp;
797
	    xd->fontface = face;
798
	    xd->fontsize = size;
799
	    XSetFont(display, xd->wgc, xd->font->fid);
20430 maechler 800
	} else
13974 hornik 801
	    error("X11 font at size %d could not be loaded", size);
802
    }
803
}
804
 
805
 
16935 hornik 806
static void SetColor(int color, NewDevDesc *dd)
13974 hornik 807
{
16935 hornik 808
    newX11Desc *xd = (newX11Desc *) dd->deviceSpecific;
13974 hornik 809
    if (color != xd->col) {
810
	blackpixel = GetX11Pixel(R_RED(color), R_GREEN(color), R_BLUE(color));
811
	xd->col = color;
812
	XSetState(display, xd->wgc, blackpixel, whitepixel, GXcopy, AllPlanes);
813
    }
814
}
815
 
20430 maechler 816
/* --> See "Notes on Line Textures" in ../../include/Rgraphics.h
13974 hornik 817
 *
818
 *	27/5/98 Paul - change to allow lty and lwd to interact:
819
 *	the line texture is now scaled by the line width so that,
820
 *	for example, a wide (lwd=2) dotted line (lty=2) has bigger
821
 *	dots which are more widely spaced.  Previously, such a line
822
 *	would have "dots" which were wide, but not long, nor widely
823
 *	spaced.
824
 */
16935 hornik 825
static void SetLinetype(int newlty, double nlwd, NewDevDesc *dd)
13974 hornik 826
{
827
    static char dashlist[8];
20430 maechler 828
    int i, newlwd;
16935 hornik 829
    newX11Desc *xd = (newX11Desc *) dd->deviceSpecific;
13974 hornik 830
 
20430 maechler 831
    newlwd = nlwd;/*cast*/
13974 hornik 832
    if (newlwd < 1)/* not less than 1 pixel */
833
	newlwd = 1;
834
    if (newlty != xd->lty || newlwd != xd->lwd) {
835
	xd->lty = newlty;
836
	xd->lwd = newlwd;
20430 maechler 837
	if (newlty == 0) {/* special hack for lty = 0 -- only for X11 */
13974 hornik 838
	    XSetLineAttributes(display,
839
			       xd->wgc,
840
			       newlwd,
841
			       LineSolid,
842
			       CapRound,
843
			       JoinRound);
844
	}
845
	else {
846
	    for(i = 0 ; i < 8 && (newlty != 0); i++) {
847
		int j = newlty & 15;
848
		if (j == 0) j = 1; /* Or we die with an X Error */
849
		/* scale line texture for line width */
850
		j = j*newlwd;
851
		/* make sure that scaled line texture */
852
		/* does not exceed X11 storage limits */
853
		if (j > 255) j=255;
20430 maechler 854
		dashlist[i] = j;
13974 hornik 855
		newlty = newlty >> 4;
856
	    }
25387 ripley 857
	    /* NB if i is odd the pattern will be interpreted as
858
	       the original pattern concatenated with itself */
20430 maechler 859
	    XSetDashes(display, xd->wgc, 0, dashlist, i);
13974 hornik 860
	    XSetLineAttributes(display,
861
			       xd->wgc,
862
			       newlwd,
863
			       LineOnOffDash,
864
			       CapButt,
865
			       JoinRound);
866
	}
867
    }
868
}
869
 
870
static int R_X11Err(Display *dsp, XErrorEvent *event)
871
{
872
    char buff[1000];
873
    XGetErrorText(dsp, event->error_code, buff, 1000);
874
    warning("X11 protocol error: %s", buff);
875
    return 0;
876
}
877
 
878
static int R_X11IOErr(Display *dsp)
879
{
880
    int fd = ConnectionNumber(display);
881
    /*
882
    while (nfonts--)  XFreeFont(display, fontcache[nfonts].font);
883
    nfonts = 0;
884
    */
885
    removeInputHandler(&R_InputHandlers,
886
		       getInputHandler(R_InputHandlers,fd));
887
    /*
888
    XCloseDisplay(display);
889
    displayOpen = FALSE;
890
    */
891
    error("X11 fatal IO error: please save work and shut down R");
892
    return 0; /* but should never get here */
893
}
894
 
895
 
20430 maechler 896
Rboolean
897
newX11_Open(NewDevDesc *dd, newX11Desc *xd, char *dsp, double w, double h,
898
	 double gamma_fac, X_COLORTYPE colormodel, int maxcube,
15655 ripley 899
	 int canvascolor)
13974 hornik 900
{
901
    /* if we have to bail out with "error", then must free(dd) and free(xd) */
16135 ripley 902
    /* That means the *caller*: the X11DeviceDriver code frees xd, for example */
13974 hornik 903
 
904
    XEvent event;
905
    int iw, ih;
906
    X_GTYPE type;
907
    char *p = dsp;
908
    XGCValues gcv;
909
    /* Indicates whether the display is created within this particular call: */
910
    Rboolean DisplayOpened = FALSE;
911
 
912
    if (!strncmp(dsp, "png::", 5)) {
25331 ripley 913
	char buf[PATH_MAX]; /* allow for pageno formats */
13974 hornik 914
	FILE *fp;
915
#ifndef HAVE_PNG
916
	warning("No png support in this version of R");
917
	return FALSE;
25272 ripley 918
#else
25331 ripley 919
	if(strlen(dsp+5) >= PATH_MAX) error("filename too long in png() call");
21022 ripley 920
	strcpy(xd->filename, dsp+5);
25331 ripley 921
	snprintf(buf, PATH_MAX, dsp+5, 1); /* page 1 to start */
21022 ripley 922
	if (!(fp = R_fopen(R_ExpandFileName(buf), "w"))) {
923
	    warning("could not open PNG file `%s'", buf);
13974 hornik 924
	    return FALSE;
925
	}
926
	xd->fp = fp;
927
	type = PNG;
928
	p = "";
25272 ripley 929
#endif
20430 maechler 930
    }
13974 hornik 931
    else if (!strncmp(dsp, "jpeg::", 6)) {
25331 ripley 932
	char buf[PATH_MAX]; /* allow for pageno formats */
13974 hornik 933
	FILE *fp;
934
#ifndef HAVE_JPEG
935
	warning("No jpeg support in this version of R");
936
	return FALSE;
25272 ripley 937
#else
13974 hornik 938
	p = strchr(dsp+6, ':'); *p='\0';
939
	xd->quality = atoi(dsp+6);
25331 ripley 940
	if(strlen(p+1) >= PATH_MAX) error("filename too long in jpeg() call");
21022 ripley 941
	strcpy(xd->filename, p+1);
25331 ripley 942
	snprintf(buf, PATH_MAX, p+1, 1); /* page 1 to start */
21022 ripley 943
	if (!(fp = R_fopen(R_ExpandFileName(buf), "w"))) {
944
	    warning("could not open JPEG file `%s'", buf);
13974 hornik 945
	    return FALSE;
946
	}
947
	xd->fp = fp;
948
	type = JPEG;
949
	p = "";
25272 ripley 950
#endif
13974 hornik 951
    } else if (!strcmp(dsp, "XImage")) {
952
	type = XIMAGE;
953
	xd->fp = NULL;
954
	p = "";
955
    }
956
    else type = WINDOW;
957
    xd->type = type;
958
 
959
    /* If there is no server connection, establish one and */
960
    /* initialize the X11 device driver data structures. */
961
 
962
    if (!displayOpen) {
16135 ripley 963
	if ((display = XOpenDisplay(p)) == NULL) {
964
	    warning("unable to open connection to X11 display`%s'", p);
15659 ripley 965
	    return FALSE;
16135 ripley 966
	}
15659 ripley 967
	DisplayOpened = TRUE;
968
	Rf_setX11Display(display, gamma_fac, colormodel, maxcube, TRUE);
969
	displayOpen = TRUE;
970
	if(xd->handleOwnEvents == FALSE)
971
	    addInputHandler(R_InputHandlers, ConnectionNumber(display),
972
			    R_ProcessEvents, XActivity);
13974 hornik 973
    }
15655 ripley 974
    /* whitepixel = GetX11Pixel(255, 255, 255); */
20430 maechler 975
    whitepixel = GetX11Pixel(R_RED(canvascolor), R_GREEN(canvascolor),
15655 ripley 976
			     R_BLUE(canvascolor));
13974 hornik 977
    blackpixel = GetX11Pixel(0, 0, 0);
978
 
979
    if (!SetBaseFont(xd)) {
980
	Rprintf("can't find X11 font\n");
981
	return FALSE;
982
    }
983
 
984
    /* Foreground and Background Colors */
985
 
16935 hornik 986
    xd->fill = 0xffffffff; /* transparent, was R_RGB(255, 255, 255); */
987
    xd->col = R_RGB(0, 0, 0);
15655 ripley 988
    xd->canvas = canvascolor;
16135 ripley 989
    if(type == JPEG && !R_OPAQUE(xd->canvas)) {
16124 ripley 990
	warning("jpeg() does not support transparency: using white bg");
991
	xd->canvas = 0xffffff;
992
    }
16935 hornik 993
    if(type > WINDOW) xd->fill = xd->canvas;
13974 hornik 994
 
20430 maechler 995
 
13974 hornik 996
    /* Try to create a simple window. */
997
    /* We want to know about exposures */
998
    /* and window-resizes and locations. */
999
 
1000
    attributes.background_pixel = whitepixel;
1001
    attributes.border_pixel = blackpixel;
1002
    attributes.backing_store = Always;
1003
    attributes.event_mask = ButtonPressMask
1004
	| ExposureMask
1005
	| StructureNotifyMask;
1006
 
1007
 
1008
    if (type == WINDOW) {
15655 ripley 1009
	int alreadyCreated = (xd->window != (Window)NULL);
1010
	if(alreadyCreated == 0) {
1011
	    xd->windowWidth = iw = w/pixelWidth();
1012
	    xd->windowHeight = ih = h/pixelHeight();
1013
	    if ((xd->window = XCreateWindow(
1014
		display, rootwin,
1015
		DisplayWidth(display, screen) - iw - 10, 10, iw, ih, 1,
1016
		DefaultDepth(display, screen),
1017
		InputOutput,
1018
		DefaultVisual(display, screen),
1019
		CWEventMask | CWBackPixel | CWBorderPixel | CWBackingStore,
16135 ripley 1020
		&attributes)) == 0) {
1021
		warning("unable to create X11 window");
15655 ripley 1022
		return FALSE;
16135 ripley 1023
	    }
20430 maechler 1024
 
15655 ripley 1025
	    XChangeProperty(display, xd->window, XA_WM_NAME, XA_STRING,
20430 maechler 1026
			    8, PropModeReplace,
15655 ripley 1027
			    (unsigned char*)"R Graphics", 13);
13974 hornik 1028
 
15655 ripley 1029
	    xd->gcursor = XCreateFontCursor(display, CURSOR);
1030
	    XDefineCursor(display, xd->window, xd->gcursor);
13974 hornik 1031
 
15655 ripley 1032
	    /* set up protocols so that window manager sends */
1033
	    /* me an event when user "destroys" window */
1034
	    _XA_WM_PROTOCOLS = XInternAtom(display, "WM_PROTOCOLS", 0);
1035
	    protocol = XInternAtom(display, "WM_DELETE_WINDOW", 0);
15659 ripley 1036
	    XSetWMProtocols(display, xd->window, &protocol, 1);
20430 maechler 1037
 
15655 ripley 1038
	}
16935 hornik 1039
	/* Save the NewDevDesc* with the window for event dispatching */
13974 hornik 1040
	XSaveContext(display, xd->window, devPtrContext, (caddr_t) dd);
1041
 
1042
	/* Map the window */
1043
	if(alreadyCreated == 0) {
15659 ripley 1044
	    XSelectInput(display, xd->window,
1045
			 ExposureMask | ButtonPressMask | StructureNotifyMask);
1046
	    XMapWindow(display, xd->window);
1047
	    XSync(display, 0);
20430 maechler 1048
 
15659 ripley 1049
	    /* Gobble expose events */
20430 maechler 1050
 
1051
	    while ( XPeekEvent(display, &event),
17010 murrell 1052
		    !XCheckTypedEvent(display, Expose, &event))
1053
                ;
1054
	    /* XNextEvent(display, &event);
1055
	       if (event.xany.type == Expose) {
1056
	       while (event.xexpose.count)
1057
	       XNextEvent(display, &event);
1058
	       }
1059
	    */
13974 hornik 1060
	}
1061
    } else { /* PIXMAP */
1062
	xd->windowWidth = iw = w;
1063
	xd->windowHeight = ih = h;
1064
	if ((xd->window = XCreatePixmap(
1065
	    display, rootwin,
16135 ripley 1066
	    iw, ih, DefaultDepth(display, screen))) == 0) {
1067
	    warning("unable to create pixmap");
13974 hornik 1068
	    return FALSE;
16135 ripley 1069
	}
16935 hornik 1070
	/* Save the NewDevDesc* with the window for event dispatching */
13974 hornik 1071
	/* Is this needed? */
1072
	XSaveContext(display, xd->window, devPtrContext, (caddr_t) dd);
1073
	xd->npages = 0;
1074
    }
1075
 
1076
    /* Set the graphics context */
1077
 
1078
    gcv.arc_mode = ArcChord;
1079
    xd->wgc = XCreateGC(display, xd->window, GCArcMode, &gcv);
1080
    XSetState(display, xd->wgc, blackpixel, whitepixel, GXcopy, AllPlanes);
1081
    XSetFont(display, xd->wgc, xd->font->fid);
1082
 
1083
    /* ensure that line drawing is set up at the first */
1084
    /* graphics call */
1085
    xd->lty = -1;
1086
    xd->lwd = -1;
1087
 
1088
 
1089
    numX11Devices++;
1090
    return TRUE;
1091
}
1092
 
27236 murrell 1093
static double newX11_StrWidth(char *str,
1094
			      R_GE_gcontext *gc,
1095
			      NewDevDesc *dd)
13974 hornik 1096
{
16935 hornik 1097
    newX11Desc *xd = (newX11Desc *) dd->deviceSpecific;
13974 hornik 1098
 
27236 murrell 1099
    int size = gc->cex * gc->ps + 0.5;
1100
    SetFont(gc->fontface, size, dd);
13974 hornik 1101
    return (double)XTextWidth(xd->font, str, strlen(str));
1102
}
1103
 
1104
 
1105
	/* Character Metric Information */
1106
	/* Passing c == 0 gets font information */
1107
 
27236 murrell 1108
static void newX11_MetricInfo(int c,
1109
			      R_GE_gcontext *gc,
16935 hornik 1110
			      double* ascent, double* descent,
1111
			      double* width, NewDevDesc *dd)
13974 hornik 1112
{
1113
    int first, last;
27236 murrell 1114
    int size = gc->cex * gc->ps + 0.5;
16935 hornik 1115
    newX11Desc *xd = (newX11Desc *) dd->deviceSpecific;
13974 hornik 1116
 
27236 murrell 1117
    SetFont(gc->fontface, size, dd);
13974 hornik 1118
    first = xd->font->min_char_or_byte2;
1119
    last = xd->font->max_char_or_byte2;
1120
 
1121
    if (c == 0) {
1122
	*ascent = xd->font->ascent;
1123
	*descent = xd->font->descent;
1124
	*width = xd->font->max_bounds.width;
1125
    }
1126
    else if (first <= c && c <= last) {
1127
	*ascent = xd->font->per_char[c-first].ascent;
1128
	*descent = xd->font->per_char[c-first].descent;
1129
	*width = xd->font->per_char[c-first].width;
1130
    }
1131
    else {
1132
	*ascent = 0;
1133
	*descent = 0;
1134
	*width = 0;
1135
    }
1136
}
1137
 
20430 maechler 1138
static void newX11_Clip(double x0, double x1, double y0, double y1,
16935 hornik 1139
			NewDevDesc *dd)
13974 hornik 1140
{
16935 hornik 1141
    newX11Desc *xd = (newX11Desc *) dd->deviceSpecific;
13974 hornik 1142
 
1143
    if (x0 < x1) {
20424 murrell 1144
	xd->clip.x = (int) x0 ;
13974 hornik 1145
	xd->clip.width = (int) x1 - (int) x0 + 1;
1146
    }
1147
    else {
20424 murrell 1148
	xd->clip.x = (int) x1;
13974 hornik 1149
	xd->clip.width = (int) x0 - (int) x1 + 1;
1150
    }
1151
 
1152
    if (y0 < y1) {
20424 murrell 1153
	xd->clip.y = (int) y0;
13974 hornik 1154
	xd->clip.height = (int) y1 -  (int) y0 + 1;
1155
    }
1156
    else {
20424 murrell 1157
	xd->clip.y = (int) y1;
13974 hornik 1158
	xd->clip.height = (int) y0 - (int) y1 + 1;
1159
    }
1160
 
1161
    XSetClipRectangles(display, xd->wgc, 0, 0, &(xd->clip), 1, Unsorted);
1162
#ifdef XSYNC
1163
    if (xd->type == WINDOW) XSync(display, 0);
1164
#endif
1165
}
1166
 
16935 hornik 1167
static void newX11_Size(double *left, double *right,
1168
		     double *bottom, double *top,
1169
		     NewDevDesc *dd)
13974 hornik 1170
{
16935 hornik 1171
    newX11Desc *xd = (newX11Desc *) dd->deviceSpecific;
20430 maechler 1172
 
16935 hornik 1173
    *left = 0.0;
1174
    *right = xd->windowWidth;
1175
    *bottom = xd->windowHeight;
1176
    *top = 0.0;
13974 hornik 1177
}
1178
 
27236 murrell 1179
static void newX11_NewPage(R_GE_gcontext *gc,
1180
			   NewDevDesc *dd)
13974 hornik 1181
{
16935 hornik 1182
    newX11Desc *xd = (newX11Desc *) dd->deviceSpecific;
13974 hornik 1183
 
1184
    if (xd->type > WINDOW) {
21022 ripley 1185
	if (xd->npages++) {
1186
	    /* try to preserve the page we do have */
1187
	    if (xd->type != XIMAGE) X11_Close_bitmap(xd);
1188
	    if (xd->type != XIMAGE && xd->fp != NULL) fclose(xd->fp);
1189
	    if (xd->type == PNG) {
25331 ripley 1190
		char buf[PATH_MAX];
1191
		snprintf(buf, PATH_MAX, xd->filename, xd->npages);
21022 ripley 1192
		xd->fp = R_fopen(R_ExpandFileName(buf), "w");
1193
		if (!xd->fp)
1194
		    error("could not open PNG file `%s'", buf);
1195
	    }
1196
	    if (xd->type == JPEG) {
25331 ripley 1197
		char buf[PATH_MAX];
1198
		snprintf(buf, PATH_MAX, xd->filename, xd->npages);
21022 ripley 1199
		xd->fp = R_fopen(R_ExpandFileName(buf), "w");
1200
		if (!xd->fp)
1201
		    error("could not open JPEG file `%s'", buf);
1202
	    }
1203
	    /* error("attempt to draw second page on pixmap device");*/
1204
	}
16124 ripley 1205
/* we want to override the default bg="transparent" */
16935 hornik 1206
/*	xd->fill = R_OPAQUE(dd->bg) ? dd->bg : xd->canvas; */
27236 murrell 1207
	xd->fill = R_OPAQUE(gc->fill) ? gc->fill: PNG_TRANS;
16935 hornik 1208
	SetColor(xd->fill, dd);
13974 hornik 1209
	XFillRectangle(display, xd->window, xd->wgc, 0, 0,
1210
		       xd->windowWidth, xd->windowHeight);
1211
	return;
1212
    }
1213
 
1214
    FreeX11Colors();
27236 murrell 1215
    if ( (model == PSEUDOCOLOR2) || (xd->fill != gc->fill)) {
1216
	xd->fill = R_OPAQUE(gc->fill) ? gc->fill : xd->canvas;
16935 hornik 1217
	whitepixel = GetX11Pixel(R_RED(xd->fill),R_GREEN(xd->fill),R_BLUE(xd->fill));
13974 hornik 1218
	XSetWindowBackground(display, xd->window, whitepixel);
1219
    }
1220
    XClearWindow(display, xd->window);
1221
#ifdef XSYNC
1222
    XSync(display, 0);
1223
#endif
1224
}
1225
 
20430 maechler 1226
extern int R_SaveAsPng(void  *d, int width, int height,
13974 hornik 1227
		       unsigned long (*gp)(XImage *, int, int),
15641 ripley 1228
		       int bgr, FILE *fp, unsigned int transparent);
13974 hornik 1229
 
20430 maechler 1230
extern int R_SaveAsJpeg(void  *d, int width, int height,
13974 hornik 1231
			unsigned long (*gp)(XImage *, int, int),
1232
			int bgr, int quality, FILE *outfile);
1233
 
21022 ripley 1234
 
13974 hornik 1235
static long knowncols[512];
1236
 
1237
 
1238
static unsigned long bitgp(XImage *xi, int x, int y)
1239
{
1240
    int i, r, g, b;
1241
    XColor xcol;
1242
    /*	returns the colour of the (x,y) pixel stored as RGB */
1243
    i = XGetPixel(xi, y, x);
1244
    switch(model) {
1245
    case MONOCHROME:
1246
	return (i==0)?0xFFFFFF:0;
1247
    case GRAYSCALE:
1248
    case PSEUDOCOLOR1:
1249
    case PSEUDOCOLOR2:
1250
	if (i < 512) {
1251
	    if (knowncols[i] < 0) {
1252
		xcol.pixel = i;
1253
		XQueryColor(display, colormap, &xcol);
1254
		knowncols[i] = ((xcol.red>>8)<<16) | ((xcol.green>>8)<<8)
1255
		    | (xcol.blue>>8);
1256
	    }
1257
	    return knowncols[i];
20430 maechler 1258
	}
13974 hornik 1259
	else {
1260
	    xcol.pixel = i;
1261
	    XQueryColor(display, colormap, &xcol);
1262
	    return ((xcol.red>>8)<<16) | ((xcol.green>>8)<<8) | (xcol.blue>>8);
1263
	}
1264
    case TRUECOLOR:
1265
	r = ((i>>RShift)&RMask) * 255 /(RMask);
1266
	g = ((i>>GShift)&GMask) * 255 /(GMask);
1267
	b = ((i>>BShift)&BMask) * 255 /(BMask);
1268
	return (r<<16) | (g<<8) | b;
1269
    default:
1270
	return 0;
1271
    }
1272
    return 0; /* not reached, needed for some compilers */
1273
}
1274
 
21022 ripley 1275
static void X11_Close_bitmap(newX11Desc *xd)
1276
{
1277
    int i;
1278
    XImage *xi;
1279
    for (i = 0; i < 512; i++) knowncols[i] = -1;
1280
    xi = XGetImage(display, xd->window, 0, 0,
1281
		   xd->windowWidth, xd->windowHeight,
1282
		   AllPlanes, ZPixmap);
1283
    if (xd->type == PNG) {
1284
	unsigned int pngtrans = PNG_TRANS;
1285
	if(model == TRUECOLOR) {
1286
	    int i, r, g, b;
1287
	    /* some `truecolor' displays distort colours */
1288
	    i = GetX11Pixel(R_RED(PNG_TRANS),
1289
			    R_GREEN(PNG_TRANS),
1290
			    R_BLUE(PNG_TRANS));
1291
	    r = ((i>>RShift)&RMask) * 255 /(RMask);
1292
	    g = ((i>>GShift)&GMask) * 255 /(GMask);
1293
	    b = ((i>>BShift)&BMask) * 255 /(BMask);
1294
	    pngtrans = (r<<16) | (g<<8) | b;
1295
	}
1296
	R_SaveAsPng(xi, xd->windowWidth, xd->windowHeight,
1297
		    bitgp, 0, xd->fp,
1298
		    (xd->fill != PNG_TRANS) ? 0 : pngtrans);
1299
    } else if (xd->type == JPEG)
1300
	R_SaveAsJpeg(xi, xd->windowWidth, xd->windowHeight,
1301
		     bitgp, 0, xd->quality, xd->fp);
1302
    XDestroyImage(xi);
1303
}
1304
 
16935 hornik 1305
static void newX11_Close(NewDevDesc *dd)
13974 hornik 1306
{
16935 hornik 1307
    newX11Desc *xd = (newX11Desc *) dd->deviceSpecific;
13974 hornik 1308
 
1309
    if (xd->type == WINDOW) {
1310
	/* process pending events */
1311
	/* set block on destroy events */
1312
	inclose = TRUE;
1313
	R_ProcessEvents((void*) NULL);
1314
 
1315
	XFreeCursor(display, xd->gcursor);
1316
	XDestroyWindow(display, xd->window);
1317
	XSync(display, 0);
1318
    } else {
21022 ripley 1319
	if (xd->npages && xd->type != XIMAGE) X11_Close_bitmap(xd);
13974 hornik 1320
	XFreeGC(display, xd->wgc);
1321
	XFreePixmap(display, xd->window);
1322
	if (xd->type != XIMAGE && xd->fp != NULL) fclose(xd->fp);
1323
    }
1324
 
1325
    numX11Devices--;
1326
    if (numX11Devices == 0)  {
1327
      int fd = ConnectionNumber(display);
1328
	/* Free Resources Here */
1329
	while (nfonts--)  XFreeFont(display, fontcache[nfonts].font);
1330
	nfonts = 0;
1331
        if(xd->handleOwnEvents == FALSE)
14545 ripley 1332
	    removeInputHandler(&R_InputHandlers,
1333
			       getInputHandler(R_InputHandlers,fd));
13974 hornik 1334
	XCloseDisplay(display);
1335
	displayOpen = FALSE;
1336
    }
1337
 
1338
    free(xd);
1339
    inclose = FALSE;
1340
}
1341
 
1342
static char title[11] = "R Graphics";
1343
 
16935 hornik 1344
static void newX11_Activate(NewDevDesc *dd)
13974 hornik 1345
{
1346
    char t[50];
1347
    char num[3];
16935 hornik 1348
    newX11Desc *xd = (newX11Desc *) dd->deviceSpecific;
13974 hornik 1349
 
1350
    if (xd->type > WINDOW) return;
1351
    strcpy(t, title);
1352
    strcat(t, ": Device ");
16935 hornik 1353
    sprintf(num, "%i", devNumber((DevDesc*)(dd))+1);
13974 hornik 1354
    strcat(t, num);
1355
    strcat(t, " (ACTIVE)");
1356
    XChangeProperty(display, xd->window, XA_WM_NAME, XA_STRING,
19269 pd 1357
		    8, PropModeReplace, (unsigned char*)t, strlen(t));
13974 hornik 1358
    XSync(display, 0);
1359
}
1360
 
16935 hornik 1361
static void newX11_Deactivate(NewDevDesc *dd)
13974 hornik 1362
{
1363
    char t[50];
1364
    char num[3];
16935 hornik 1365
    newX11Desc *xd = (newX11Desc *) dd->deviceSpecific;
13974 hornik 1366
 
1367
    if (xd->type > WINDOW) return;
1368
    strcpy(t, title);
1369
    strcat(t, ": Device ");
16935 hornik 1370
    sprintf(num, "%i", devNumber((DevDesc*)(dd))+1);
13974 hornik 1371
    strcat(t, num);
1372
    strcat(t, " (inactive)");
1373
    XChangeProperty(display, xd->window, XA_WM_NAME, XA_STRING,
19269 pd 1374
		    8, PropModeReplace, (unsigned char*)t, strlen(t));
13974 hornik 1375
    XSync(display, 0);
1376
}
1377
 
16935 hornik 1378
static void newX11_Rect(double x0, double y0, double x1, double y1,
27236 murrell 1379
			R_GE_gcontext *gc,
17165 murrell 1380
			NewDevDesc *dd)
13974 hornik 1381
{
1382
    int tmp;
16935 hornik 1383
    newX11Desc *xd = (newX11Desc *) dd->deviceSpecific;
13974 hornik 1384
 
1385
    if (x0 > x1) {
1386
	tmp = x0;
1387
	x0 = x1;
1388
	x1 = tmp;
1389
    }
1390
    if (y0 > y1) {
1391
	tmp = y0;
1392
	y0 = y1;
1393
	y1 = tmp;
1394
    }
27236 murrell 1395
    if (R_OPAQUE(gc->fill)) {
1396
	SetColor(gc->fill, dd);
13974 hornik 1397
	XFillRectangle(display, xd->window, xd->wgc, (int)x0, (int)y0,
1398
		       (int)x1 - (int)x0, (int)y1 - (int)y0);
1399
    }
27236 murrell 1400
    if (R_OPAQUE(gc->col)) {
1401
	SetColor(gc->col, dd);
1402
	SetLinetype(gc->lty, gc->lwd, dd);
13974 hornik 1403
	XDrawRectangle(display, xd->window, xd->wgc, (int)x0, (int)y0,
1404
		       (int)x1 - (int)x0, (int)y1 - (int)y0);
1405
    }
1406
#ifdef XSYNC
1407
    if (xd->type == WINDOW) XSync(display, 0);
1408
#endif
1409
}
1410
 
16935 hornik 1411
static void newX11_Circle(double x, double y, double r,
27236 murrell 1412
			  R_GE_gcontext *gc,
17165 murrell 1413
			  NewDevDesc *dd)
13974 hornik 1414
{
1415
    int ir, ix, iy;
16935 hornik 1416
    newX11Desc *xd = (newX11Desc *) dd->deviceSpecific;
13974 hornik 1417
 
1418
    ir = floor(r + 0.5);
1419
 
1420
    ix = (int)x;
1421
    iy = (int)y;
27236 murrell 1422
    if (R_OPAQUE(gc->fill)) {
1423
	SetColor(gc->fill, dd);
13974 hornik 1424
	XFillArc(display, xd->window, xd->wgc,
1425
		 ix-ir, iy-ir, 2*ir, 2*ir, 0, 23040);
1426
    }
27236 murrell 1427
    if (R_OPAQUE(gc->col)) {
1428
	SetLinetype(gc->lty, gc->lwd, dd);
1429
	SetColor(gc->col, dd);
13974 hornik 1430
	XDrawArc(display, xd->window, xd->wgc,
1431
		 ix-ir, iy-ir, 2*ir, 2*ir, 0, 23040);
1432
    }
1433
}
1434
 
16935 hornik 1435
static void newX11_Line(double x1, double y1, double x2, double y2,
27236 murrell 1436
			R_GE_gcontext *gc,
16935 hornik 1437
			NewDevDesc *dd)
13974 hornik 1438
{
1439
    int xx1, yy1, xx2, yy2;
16935 hornik 1440
    newX11Desc *xd = (newX11Desc *) dd->deviceSpecific;
13974 hornik 1441
 
1442
    /* In-place conversion ok */
1443
 
1444
    xx1 = (int) x1;
1445
    yy1 = (int) y1;
1446
    xx2 = (int) x2;
1447
    yy2 = (int) y2;
1448
 
27236 murrell 1449
    if (R_OPAQUE(gc->col)) {
1450
	SetColor(gc->col, dd);
1451
	SetLinetype(gc->lty, gc->lwd, dd);
15648 ripley 1452
	XDrawLine(display, xd->window, xd->wgc, xx1, yy1, xx2, yy2);
13974 hornik 1453
#ifdef XSYNC
15648 ripley 1454
	if (xd->type == WINDOW) XSync(display, 0);
13974 hornik 1455
#endif
15648 ripley 1456
    }
13974 hornik 1457
}
1458
 
20430 maechler 1459
static void newX11_Polyline(int n, double *x, double *y,
27236 murrell 1460
			    R_GE_gcontext *gc,
16935 hornik 1461
			    NewDevDesc *dd)
13974 hornik 1462
{
15321 luke 1463
    char *vmax = vmaxget();
13974 hornik 1464
    XPoint *points;
1465
    int i, j;
16935 hornik 1466
    newX11Desc *xd = (newX11Desc *) dd->deviceSpecific;
13974 hornik 1467
 
15321 luke 1468
    points = (XPoint *) R_alloc(n, sizeof(XPoint));
1469
 
13974 hornik 1470
    for(i=0 ; i<n ; i++) {
16935 hornik 1471
	points[i].x = (int)(x[i]);
1472
	points[i].y = (int)(y[i]);
13974 hornik 1473
    }
1474
 
27236 murrell 1475
    if (R_OPAQUE(gc->col)) {
1476
	SetColor(gc->col, dd);
1477
	SetLinetype(gc->lty, gc->lwd, dd);
13974 hornik 1478
/* Some X servers need npoints < 64K */
15648 ripley 1479
	for(i = 0; i < n; i+= 10000-1) {
1480
	    j = n - i;
1481
	    j = (j <= 10000) ? j: 10000; /* allow for overlap */
20430 maechler 1482
	    XDrawLines(display, xd->window, xd->wgc, points+i, j,
15648 ripley 1483
		       CoordModeOrigin);
1484
	}
13974 hornik 1485
#ifdef XSYNC
15648 ripley 1486
	if (xd->type == WINDOW) XSync(display, 0);
13974 hornik 1487
#endif
15648 ripley 1488
    }
13974 hornik 1489
 
15321 luke 1490
    vmaxset(vmax);
13974 hornik 1491
}
1492
 
20430 maechler 1493
static void newX11_Polygon(int n, double *x, double *y,
27236 murrell 1494
			   R_GE_gcontext *gc,
17165 murrell 1495
			   NewDevDesc *dd)
13974 hornik 1496
{
15321 luke 1497
    char *vmax = vmaxget();
13974 hornik 1498
    XPoint *points;
1499
    int i;
16935 hornik 1500
    newX11Desc *xd = (newX11Desc *) dd->deviceSpecific;
13974 hornik 1501
 
15321 luke 1502
    points = (XPoint *) R_alloc(n+1, sizeof(XPoint));
13974 hornik 1503
 
1504
    for (i=0 ; i<n ; i++) {
16935 hornik 1505
	points[i].x = (int)(x[i]);
1506
	points[i].y = (int)(y[i]);
13974 hornik 1507
    }
16935 hornik 1508
    points[n].x = (int)(x[0]);
1509
    points[n].y = (int)(y[0]);
27236 murrell 1510
    if (R_OPAQUE(gc->fill)) {
1511
	SetColor(gc->fill, dd);
13974 hornik 1512
	XFillPolygon(display, xd->window, xd->wgc, points, n, Complex, CoordModeOrigin);
1513
#ifdef XSYNC
1514
	if (xd->type == WINDOW) XSync(display, 0);
1515
#endif
1516
    }
27236 murrell 1517
    if (R_OPAQUE(gc->col)) {
1518
	SetColor(gc->col, dd);
1519
	SetLinetype(gc->lty, gc->lwd, dd);
13974 hornik 1520
	XDrawLines(display, xd->window, xd->wgc, points, n+1, CoordModeOrigin);
1521
#ifdef XSYNC
1522
	if (xd->type == WINDOW) XSync(display, 0);
1523
#endif
1524
    }
1525
 
15321 luke 1526
    vmaxset(vmax);
13974 hornik 1527
}
1528
 
1529
 
20430 maechler 1530
static void newX11_Text(double x, double y,
1531
			char *str, double rot, double hadj,
27236 murrell 1532
			R_GE_gcontext *gc,
17165 murrell 1533
			NewDevDesc *dd)
13974 hornik 1534
{
1535
    int len, size;
1536
/*    double xl, yl, rot1;*/
16935 hornik 1537
    newX11Desc *xd = (newX11Desc *) dd->deviceSpecific;
13974 hornik 1538
 
27236 murrell 1539
    size = gc->cex * gc->ps + 0.5;
1540
    SetFont(gc->fontface, size, dd);
1541
    if (R_OPAQUE(gc->col)) {
1542
	SetColor(gc->col, dd);
15648 ripley 1543
	len = strlen(str);
1544
	XRotDrawString(display, xd->font, rot, xd->window, xd->wgc,
1545
		       (int)x, (int)y, str);
13974 hornik 1546
#ifdef XSYNC
15648 ripley 1547
	if (xd->type == WINDOW) XSync(display, 0);
13974 hornik 1548
#endif
15648 ripley 1549
    }
13974 hornik 1550
}
1551
 
16935 hornik 1552
static Rboolean newX11_Locator(double *x, double *y, NewDevDesc *dd)
13974 hornik 1553
{
1554
    XEvent event;
16935 hornik 1555
    NewDevDesc *ddEvent;
1556
    newX11Desc *xd = (newX11Desc *) dd->deviceSpecific;
13974 hornik 1557
    caddr_t temp;
1558
    int done = 0;
1559
 
1560
    if (xd->type > WINDOW) return 0;
1561
    R_ProcessEvents((void*)NULL);	/* discard pending events */
1562
    XSync(display, 1);
1563
    /* handle X events as normal until get a button */
1564
    /* click in the desired device */
1565
    while (!done && displayOpen) {
1566
	XNextEvent(display, &event);
27677 ripley 1567
	/* possibly later R_CheckUserInterrupt(); */
13974 hornik 1568
	if (event.type == ButtonPress) {
1569
	    XFindContext(display, event.xbutton.window,
1570
			 devPtrContext, &temp);
16935 hornik 1571
	    ddEvent = (NewDevDesc *) temp;
13974 hornik 1572
	    if (ddEvent == dd) {
1573
		if (event.xbutton.button == Button1) {
24345 ripley 1574
		    int useBeep = asLogical(GetOption(install("locatorBell"), 
1575
						      R_NilValue));
13974 hornik 1576
		    *x = event.xbutton.x;
1577
		    *y = event.xbutton.y;
20430 maechler 1578
  		       /* Make a beep! Was print "\07", but that
13974 hornik 1579
                          messes up some terminals. */
24345 ripley 1580
                    if(useBeep) XBell(display, X_BELL_VOLUME);
13974 hornik 1581
		    XSync(display, 0);
1582
		    done = 1;
1583
		}
1584
		else
1585
		    done = 2;
1586
	    }
1587
	}
1588
	else
1589
	    handleEvent(event);
1590
    }
1591
    /* if it was a Button1 succeed, otherwise fail */
1592
    return (done == 1);
1593
}
1594
 
1595
/* Set Graphics mode - not needed for X11 */
16935 hornik 1596
static void newX11_Mode(int mode, NewDevDesc *dd)
13974 hornik 1597
{
1598
 
1599
#ifdef XSYNC
1600
    if (mode == 0) XSync(display, 0);
1601
#else
1602
    XSync(display, 0);
1603
#endif
1604
}
1605
 
1606
/* Hold the Picture Onscreen - not needed for X11 */
16935 hornik 1607
static void newX11_Hold(NewDevDesc *dd)
13974 hornik 1608
{
1609
}
1610
 
1611
 
1612
	/*  X11 Device Driver Arguments	:	*/
1613
	/*	1) display name			*/
1614
	/*	2) width (inches)		*/
1615
	/*	3) height (inches)		*/
1616
	/*	4) base pointsize		*/
1617
	/*	5) gamma correction factor	*/
1618
	/*	6) colormodel,			*/
1619
	/*	 see X_COLORTYPE at top of file */
1620
	/*	7) maxcube			*/
1621
 
16935 hornik 1622
Rboolean newX11DeviceDriver(DevDesc *dd,
1623
			    char *disp_name,
1624
			    double width,
1625
			    double height,
1626
			    double pointsize,
1627
			    double gamma_fac,
1628
			    X_COLORTYPE colormodel,
27069 ripley 1629
			    int maxcube, 
1630
			    int canvascolor, 
1631
			    SEXP sfonts)
13974 hornik 1632
{
16935 hornik 1633
    newX11Desc *xd;
27069 ripley 1634
    char *fn;
13974 hornik 1635
 
16935 hornik 1636
    xd = Rf_allocNewX11DeviceDesc(pointsize);
20430 maechler 1637
    /* Used to set dd->dp.font=1 and dd->dp.ps=pointsize,
16935 hornik 1638
     * but Paul removed that.
1639
     * This sort of initialisation occurs in R base graphics now.
1640
     */
13974 hornik 1641
 
27069 ripley 1642
    if(strlen(fn = CHAR(STRING_ELT(sfonts, 0))) > 499)
1643
	strcpy(xd->fontfamily, fontname);
1644
    else strcpy(xd->fontfamily,fn);
1645
    if(strlen(fn = CHAR(STRING_ELT(sfonts, 1))) > 499)
1646
	strcpy(xd->symbolfamily, symbolname);
1647
    else strcpy(xd->symbolfamily,fn);
1648
 
13974 hornik 1649
    /*	Start the Device Driver and Hardcopy.  */
1650
 
20430 maechler 1651
    if (!newX11_Open((NewDevDesc*)(dd), xd, disp_name, width, height,
16935 hornik 1652
		     gamma_fac, colormodel, maxcube, canvascolor)) {
13974 hornik 1653
	free(xd);
1654
	return FALSE;
1655
    }
1656
 
17165 murrell 1657
    Rf_setNewX11DeviceData((NewDevDesc*)(dd), gamma_fac, xd);
16974 pd 1658
 
1659
#if BUG
13974 hornik 1660
    R_ProcessEvents((void*) NULL);
16974 pd 1661
#endif
13974 hornik 1662
 
1663
    return TRUE;
1664
}
1665
 
16935 hornik 1666
/**
1667
  This fills the general device structure (dd) with the X-specific
20430 maechler 1668
  methods/functions. It also specifies the current values of the
16935 hornik 1669
  dimensions of the device, and establishes the fonts, line styles, etc.
1670
 */
1671
int
17165 murrell 1672
Rf_setNewX11DeviceData(NewDevDesc *dd, double gamma_fac, newX11Desc *xd)
16935 hornik 1673
{
1674
    dd->newDevStruct = 1;
1675
 
1676
    /*	Set up Data Structures. */
1677
 
1678
    dd->open = newX11_Open;
1679
    dd->close = newX11_Close;
1680
    dd->activate = newX11_Activate;
1681
    dd->deactivate = newX11_Deactivate;
1682
    dd->size = newX11_Size;
1683
    dd->newPage = newX11_NewPage;
1684
    dd->clip = newX11_Clip;
1685
    dd->strWidth = newX11_StrWidth;
1686
    dd->text = newX11_Text;
1687
    dd->rect = newX11_Rect;
1688
    dd->circle = newX11_Circle;
1689
    dd->line = newX11_Line;
1690
    dd->polyline = newX11_Polyline;
1691
    dd->polygon = newX11_Polygon;
1692
    dd->locator = newX11_Locator;
1693
    dd->mode = newX11_Mode;
1694
    dd->hold = newX11_Hold;
16952 murrell 1695
    dd->metricInfo = newX11_MetricInfo;
16935 hornik 1696
 
1697
    /* Set required graphics parameters. */
1698
 
1699
    /* Window Dimensions in Pixels */
1700
    /* Initialise the clipping rect too */
1701
 
1702
    dd->left = dd->clipLeft = 0;			/* left */
1703
    dd->right = dd->clipRight = xd->windowWidth;	/* right */
1704
    dd->bottom = dd->clipBottom = xd->windowHeight;	/* bottom */
1705
    dd->top = dd->clipTop = 0;			/* top */
1706
 
1707
    /* Nominal Character Sizes in Pixels */
1708
 
1709
    SetBaseFont(xd);
1710
    dd->cra[0] = xd->font->max_bounds.rbearing -
1711
	xd->font->min_bounds.lbearing;
1712
    dd->cra[1] = xd->font->max_bounds.ascent +
1713
	xd->font->max_bounds.descent;
1714
 
1715
    /* Character Addressing Offsets */
1716
    /* These are used to plot a single plotting character */
1717
    /* so that it is exactly over the plotting point */
1718
 
1719
    dd->xCharOffset = 0.4900;
1720
    dd->yCharOffset = 0.3333;
1721
    dd->yLineBias = 0.1;
1722
 
1723
    /* Inches per raster unit */
1724
 
1725
    dd->ipr[0] = pixelWidth();
1726
    dd->ipr[1] = pixelHeight();
1727
 
1728
    /* Device capabilities */
1729
 
1730
    dd->canResizePlot = TRUE;
1731
    dd->canChangeFont = FALSE;
1732
    dd->canRotateText = TRUE;
1733
    dd->canResizeText = TRUE;
1734
    dd->canClip = TRUE;
1735
    dd->canHAdj = 0;
17165 murrell 1736
    dd->canChangeGamma = FALSE;
16935 hornik 1737
 
1738
    dd->startps = xd->basefontsize;
1739
    dd->startcol = xd->col;
1740
    dd->startfill = xd->fill;
17165 murrell 1741
    dd->startlty = LTY_SOLID;
1742
    dd->startfont = 1;
1743
    dd->startgamma = gamma_fac;
16935 hornik 1744
 
1745
    /* initialise x11 device description */
1746
    /* (most of the work has been done in X11_Open) */
1747
 
1748
    xd->cex = 1.0;
1749
    xd->lty = 0;
1750
    xd->resize = 0;
1751
 
1752
    dd->deviceSpecific = (void *) xd;
1753
 
1754
    dd->displayListOn = TRUE;
1755
 
1756
  return(TRUE);
1757
}
1758
 
1759
 
1760
/**
1761
 This allocates an newX11Desc instance  and sets its default values.
1762
 */
1763
newX11Desc * Rf_allocNewX11DeviceDesc(double ps)
1764
{
1765
    newX11Desc *xd;
1766
    /* allocate new device description */
1767
    if (!(xd = (newX11Desc*)calloc(1, sizeof(newX11Desc))))
1768
	return FALSE;
1769
 
1770
    /* From here on, if we need to bail out with "error", */
1771
    /* then we must also free(xd). */
1772
 
1773
    /*	Font will load at first use.  */
1774
 
1775
    if (ps < 6 || ps > 24) ps = 12;
1776
    xd->fontface = -1;
1777
    xd->fontsize = -1;
1778
    xd->basefontface = 1;
1779
    xd->basefontsize = ps;
1780
    xd->handleOwnEvents = FALSE;
1781
    xd->window = (Window) NULL;
1782
 
1783
    return(xd);
1784
}
1785
 
1786
 
22915 ripley 1787
static
1788
Rboolean in_R_GetX11Image(int d, void *pximage, int *pwidth, int *pheight)
13974 hornik 1789
{
1790
    SEXP dev = elt(findVar(install(".Devices"), R_NilValue), d);
1791
 
1792
    if (TYPEOF(dev) != STRSXP ||
1793
	!(strcmp(CHAR(STRING_ELT(dev, 0)), "XImage") == 0 ||
1794
	  strncmp(CHAR(STRING_ELT(dev, 0)), "PNG", 3) == 0 ||
1795
	  strncmp(CHAR(STRING_ELT(dev, 0)), "X11", 3) == 0))
1796
	return FALSE;
1797
    else {
18660 luke 1798
	NewDevDesc *dd = ((GEDevDesc *)GetDevice(d))->dev;
1799
	newX11Desc *xd = dd->deviceSpecific;
13974 hornik 1800
 
22915 ripley 1801
	*((XImage**) pximage) =
1802
            XGetImage(display, xd->window, 0, 0,
22913 ripley 1803
				     xd->windowWidth, xd->windowHeight,
1804
				     AllPlanes, ZPixmap);
13974 hornik 1805
	*pwidth = xd->windowWidth;
1806
	*pheight = xd->windowHeight;
1807
	return TRUE;
1808
    }
1809
}
1810
 
1811
/**
1812
   Allows callers to retrieve the current Display setting for the process.
1813
 */
1814
Display*
1815
Rf_getX11Display()
1816
{
1817
  return(display);
1818
}
1819
 
1820
 
1821
/**
20430 maechler 1822
 Allows the caller to register the X11 Display object for the process.
13974 hornik 1823
 Typically this will be done when the first X device is created, but this allows
20430 maechler 1824
 other code to generate the Display object and then register it with the R graphics
1825
 engine.
13974 hornik 1826
 In addition to providing the Display, the caller should also give the default value for the
1827
 gamma factor and also the colormodel and color cube size. See the documentation for the x11()
20430 maechler 1828
 function.
13974 hornik 1829
 Finally, setHandlers controls whether the code establishes handlers for the X errors.
1830
 */
1831
int
20430 maechler 1832
Rf_setX11Display(Display *dpy, double gamma_fac, X_COLORTYPE colormodel,
14662 ripley 1833
		 int maxcube, Rboolean setHandlers)
13974 hornik 1834
{
15680 ripley 1835
/*    static int alreadyDone = 0;
14662 ripley 1836
    if(alreadyDone) return(TRUE);
15680 ripley 1837
    alreadyDone = 1; */
14662 ripley 1838
    display = dpy;
13974 hornik 1839
 
1840
#define SETGAMMA
1841
#ifdef SETGAMMA
14662 ripley 1842
    RedGamma   = gamma_fac;
1843
    GreenGamma = gamma_fac;
1844
    BlueGamma  = gamma_fac;
13974 hornik 1845
#endif
14662 ripley 1846
    screen = DefaultScreen(display);
1847
    rootwin = DefaultRootWindow(display);
1848
    depth = DefaultDepth(display, screen);
1849
    visual = DefaultVisual(display, screen);
1850
    colormap = DefaultColormap(display, screen);
1851
    Vclass = visual->class;
1852
    model = colormodel;
1853
    maxcubesize = maxcube;
1854
    SetupX11Color();
1855
    devPtrContext = XUniqueContext();
1856
    displayOpen = TRUE;
1857
    /* set error handlers */
1858
    if(setHandlers == TRUE) {
1859
	XSetErrorHandler(R_X11Err);
1860
	XSetIOErrorHandler(R_X11IOErr);
1861
    }
13974 hornik 1862
 
14662 ripley 1863
    return(TRUE);
13974 hornik 1864
}
1865
 
22913 ripley 1866
typedef Rboolean (*X11DeviceDriverRoutine)(DevDesc*, char*, 
1867
					   double, double, double, double,
1868
					   X_COLORTYPE, int, int);
1869
 
1870
/* Return a non-relocatable copy of a string */
1871
 
1872
static SEXP gcall;
1873
 
1874
static char *SaveString(SEXP sxp, int offset)
1875
{
1876
    char *s;
1877
    if(!isString(sxp) || length(sxp) <= offset)
1878
	errorcall(gcall, "invalid string argument");
1879
    s = R_alloc(strlen(CHAR(STRING_ELT(sxp, offset)))+1, sizeof(char));
1880
    strcpy(s, CHAR(STRING_ELT(sxp, offset)));
1881
    return s;
1882
}
1883
 
1884
static DevDesc* 
1885
Rf_addX11Device(char *display, double width, double height, double ps, 
1886
		double gamma, int colormodel, int maxcubesize,
27069 ripley 1887
		int canvascolor, char *devname, SEXP sfonts)
22913 ripley 1888
{
1889
    NewDevDesc *dev = NULL;
1890
    GEDevDesc *dd;
1891
    R_CheckDeviceAvailable();
1892
    BEGIN_SUSPEND_INTERRUPTS {
1893
	/* Allocate and initialize the device driver data */
1894
	if (!(dev = (NewDevDesc*)calloc(1, sizeof(NewDevDesc))))
1895
	    return 0;
1896
	/* Do this for early redraw attempts */
1897
	dev->newDevStruct = 1;
1898
	dev->displayList = R_NilValue;
1899
	/* Make sure that this is initialised before a GC can occur.
1900
	 * This (and displayList) get protected during GC
1901
	 */
1902
	dev->savedSnapshot = R_NilValue;
1903
	/* Took out the GInit because MOST of it is setting up
1904
	 * R base graphics parameters.  
1905
	 * This is supposed to happen via addDevice now.
1906
	 */
1907
	if (!newX11DeviceDriver((DevDesc*)(dev), display, width, height, 
27069 ripley 1908
				ps, gamma, colormodel, maxcubesize, 
1909
				canvascolor, sfonts)) {
22913 ripley 1910
	    free(dev);
1911
	    errorcall(gcall, "unable to start device %s", devname);
1912
       	}
1913
	gsetVar(install(".Device"), mkString(devname), R_NilValue);
1914
	dd = GEcreateDevDesc(dev);
1915
	addDevice((DevDesc*) dd);
1916
	GEinitDisplayList(dd);
1917
    } END_SUSPEND_INTERRUPTS;
1918
 
1919
    return((DevDesc*) dd);
1920
}
1921
 
1922
SEXP in_do_X11(SEXP call, SEXP op, SEXP args, SEXP env)
1923
{
1924
    char *display, *vmax, *cname, *devname;
1925
    double height, width, ps, gamma;
1926
    int colormodel, maxcubesize, canvascolor;
27069 ripley 1927
    SEXP sc, sfonts;
22913 ripley 1928
 
1929
    gcall = call;
1930
    vmax = vmaxget();
1931
 
1932
    /* Decode the arguments */
1933
    display = SaveString(CAR(args), 0); args = CDR(args);
1934
    width = asReal(CAR(args));	args = CDR(args);
1935
    height = asReal(CAR(args)); args = CDR(args);
1936
    if (width <= 0 || height <= 0)
1937
	errorcall(call, "invalid width or height");
1938
    ps = asReal(CAR(args)); args = CDR(args);
1939
    gamma = asReal(CAR(args)); args = CDR(args);
1940
    if (gamma < 0 || gamma > 100)
1941
	errorcall(call, "invalid gamma value");
1942
 
1943
    if (!isValidString(CAR(args)))
1944
	error("invalid colortype passed to X11 driver");
1945
    cname = CHAR(STRING_ELT(CAR(args), 0));
1946
    if (strcmp(cname, "mono") == 0)
1947
	colormodel = 0;
1948
    else if (strcmp(cname, "gray") == 0 || strcmp(cname, "grey") == 0)
1949
	colormodel = 1;
1950
    else if (strcmp(cname, "pseudo.cube") == 0)
1951
	colormodel = 2;
1952
    else if (strcmp(cname, "pseudo") == 0)
1953
	colormodel = 3;
1954
    else if (strcmp(cname, "true") == 0)
1955
	colormodel = 4;
1956
    else {
1957
	warningcall(call, 
1958
		    "unknown X11 color/colour model -- using monochrome");
1959
	colormodel = 0;
1960
    }
1961
    args = CDR(args);
1962
    maxcubesize = asInteger(CAR(args));
1963
    if (maxcubesize < 1 || maxcubesize > 256)
1964
        maxcubesize = 256;
1965
    args = CDR(args);
1966
    sc = CAR(args);
1967
    if (!isString(sc) && !isInteger(sc) && !isLogical(sc) && !isReal(sc))
1968
	errorcall(call, "invalid value of `canvas'");
1969
    canvascolor = RGBpar(sc, 0);
27069 ripley 1970
    args = CDR(args);
1971
    sfonts = CAR(args);
1972
    if (!isString(sfonts) || LENGTH(sfonts) != 2)
1973
	errorcall(call, "invalid value of `fonts'");
22913 ripley 1974
 
1975
    devname = "X11";
1976
    if (!strncmp(display, "png::", 5)) devname = "PNG";
1977
    else if (!strncmp(display, "jpeg::", 6)) devname = "JPEG";
1978
    else if (!strcmp(display, "XImage")) devname = "XImage";
1979
 
1980
    Rf_addX11Device(display, width, height, ps, gamma, colormodel, 
27069 ripley 1981
		    maxcubesize, canvascolor, devname, sfonts);
22913 ripley 1982
    vmaxset(vmax);
1983
    return R_NilValue;
1984
}
1985
 
14945 duncan 1986
extern SEXP RX11_dataentry(SEXP call, SEXP op, SEXP args, SEXP rho);
1987
 
22913 ripley 1988
void R_init_R_X11(DllInfo *info)
14945 duncan 1989
{
22913 ripley 1990
    R_X11Routines *tmp;
1991
    tmp = (R_X11Routines*) malloc(sizeof(R_X11Routines));
1992
    if(!tmp) {
1993
	error("Cannot allocate memory for X11Routines structure");
1994
	return;
1995
    }
1996
    tmp->X11 = in_do_X11;
1997
    tmp->de = RX11_dataentry;
22915 ripley 1998
    tmp->image = in_R_GetX11Image;
22913 ripley 1999
    R_setX11Routines(tmp);
14945 duncan 2000
}