| 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 |
}
|