1 /*
2  *  R : A Computer Language for Statistical Data Analysis
3  *  Copyright (C) 2004-2020   The R Core Team
4  *  Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
5  *  Copyright (C) 1998--2003  Guido Masarotto and Brian Ripley
6  *  Copyright (C) 2004        The R Foundation
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, a copy is available at
20  *  https://www.R-project.org/Licenses/
21  */
22 
23 /*--- Device Driver for Windows; this file started from
24  *  src/unix/X11/devX11.c
25  */
26 
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30 
31 #define R_USE_SIGNALS 1
32 #include <Defn.h>
33 #define R_USE_PROTOTYPES 1
34 #include <R_ext/GraphicsEngine.h>
35 
36 #include <Fileio.h>
37 #include <stdio.h>
38 #include "opt.h"
39 #include "graphapp/ga.h"
40 #include "graphapp/stdimg.h"
41 #include "console.h"
42 #include "rui.h"
43 #define WIN32_LEAN_AND_MEAN 1
44 /* Mingw-w64 defines this to be 0x0502 */
45 #ifndef _WIN32_WINNT
46 # define _WIN32_WINNT 0x0500
47 #endif
48 #include <windows.h>
49 #include "devWindows.h"
50 #define DEVWINDOWS 1
51 #include "grDevices.h"
52 
53 /* there are conflicts with Rmath.h */
54 #define imax2		Rf_imax2
55 #define imin2		Rf_imin2
56 int	imax2(int, int);
57 int	imin2(int, int);
58 
59 #ifdef ENABLE_NLS
60 #define G_(String) libintl_dgettext("RGui", String)
61 #define GN_(String) gettext_noop (String)
62 #else /* not NLS */
63 #define G_(String) (String)
64 #define GN_(String) String
65 #endif
66 
67 /* from extra.c */
68 extern size_t Rf_utf8towcs(wchar_t *wc, const char *s, size_t n);
69 
70 static
71 Rboolean GADeviceDriver(pDevDesc dd, const char *display, double width,
72 			double height, double pointsize,
73 			Rboolean recording, int resize, int bg, int canvas,
74 			double gamma, int xpos, int ypos, Rboolean buffered,
75 			SEXP psenv, Rboolean restoreConsole,
76 			const char *title, Rboolean clickToConfirm,
77 			Rboolean fillOddEven, const char *family, int quality);
78 
79 
80 /* a colour used to represent the background on png if transparent
81    NB: used as RGB and BGR
82 */
83 
84 #define PNG_TRANS 0xfdfefd
85 
86 /* these really are globals: per machine, not per window */
87 static double user_xpinch = 0.0, user_ypinch = 0.0;
88 
GAsetunits(double xpinch,double ypinch)89 static void GAsetunits(double xpinch, double ypinch)
90 {
91     user_xpinch = xpinch;
92     user_ypinch = ypinch;
93 }
94 
GArgb(int color,double gamma)95 static rgb GArgb(int color, double gamma)
96 {
97     int r, g, b;
98     if (gamma != 1) {
99 	r = (int) (255 * pow(R_RED(color) / 255.0, gamma));
100 	g = (int) (255 * pow(R_GREEN(color) / 255.0, gamma));
101 	b = (int) (255 * pow(R_BLUE(color) / 255.0, gamma));
102     } else {
103 	r = R_RED(color);
104 	g = R_GREEN(color);
105 	b = R_BLUE(color);
106     }
107     return rgb(r, g, b);
108 }
109 
110 
111 
112 	/********************************************************/
113 	/* This device driver has been documented so that it be	*/
114 	/* used as a template for new drivers			*/
115 	/********************************************************/
116 
117 #define MM_PER_INCH	25.4	/* mm -> inch conversion */
118 
119 #define TRACEDEVGA(a)
120 #define CLIP if (xd->clip.width>0) gsetcliprect(_d,xd->clip)
121 
122 static drawing _d;
123 
124 #define DRAW(a) {if(xd->kind != SCREEN) {_d=xd->gawin; CLIP; a;} else {_d=xd->bm; CLIP; a; if(!xd->buffered) {_d=xd->gawin; CLIP; a;} }}
125 
126 #define SHOW  if(xd->kind==SCREEN) {drawbits(xd); GALastUpdate = GetTickCount();}
127 #define SH if(xd->kind==SCREEN && xd->buffered && GA_xd) GA_Timer(xd)
128 
129 
130 #define SF 20  /* scrollbar resolution */
131 
132 	/********************************************************/
133 	/* Each driver can have its own device-specic graphical */
134 	/* parameters and resources.  these should be wrapped	*/
135 	/* in a structure (gadesc in devWindows.h)              */
136 	/* and attached to the overall device description via	*/
137 	/* the dd->deviceSpecific pointer			*/
138 	/* NOTE that there are generic graphical parameters	*/
139 	/* which must be set by the device driver, but are	*/
140 	/* common to all device types (see Graphics.h)		*/
141 	/* so go in the GPar structure rather than this device- */
142 	/* specific structure					*/
143 	/********************************************************/
144 
getregion(gadesc * xd)145 static rect getregion(gadesc *xd)
146 {
147     rect r = getrect(xd->bm);
148     r.x += max(0, xd->xshift);
149     r.y += max(0, xd->yshift);
150     r.width = min(r.width, xd->showWidth);
151     r.height = min(r.height, xd->showHeight);
152     return r;
153 }
154 
155 /* Update the screen 100ms after last plotting call or 500ms after
156    last update (by default).
157 
158    This runs on (asynchronous) timers for each device.
159    Macro SHOW does an immediate update, and records the update
160    in GALastUpdate.
161    SHOW is called for expose and mouse events, and newpage.
162 
163    Macro SH calls GA_Timer.  If it is more than 500ms since the last
164    update it does an update; otherwise it sets a timer running for
165    100ms. In either case cancels any existing timer.
166    SH is called for the graphics primitives.  (This could probably be
167    replace by calling from Mode(0)).
168 
169    There are two conditions:
170    (i) xd->buffered is true, which is a per-device condition.
171    (ii) GA_xd is non-null.  This is used to inhibit updates during shutdown
172    of the device, and also (post 2.14.0) when the device is held.
173 */
174 
175 static UINT_PTR TimerNo = 0;
176 static gadesc *GA_xd;
177 static DWORD GALastUpdate = 0;
178 
drawbits(gadesc * xd)179 static void drawbits(gadesc *xd)
180 {
181     if (xd)
182 	gbitblt(xd->gawin, xd->bm, pt(0,0), getrect(xd->bm));
183 }
184 
185 static VOID CALLBACK
GA_timer_proc(HWND hwnd,UINT message,UINT_PTR tid,DWORD time)186 GA_timer_proc(HWND hwnd, UINT message, UINT_PTR tid, DWORD time)
187 {
188     if ((message != WM_TIMER) || tid != TimerNo || !GA_xd) return;
189     drawbits(GA_xd);
190     GALastUpdate = time;
191 }
192 
GA_Timer(gadesc * xd)193 static void GA_Timer(gadesc *xd)
194 {
195     DWORD now = GetTickCount();
196     if(TimerNo != 0) KillTimer(0, TimerNo);
197     if(now > GALastUpdate + xd->timesince) {
198 	drawbits(xd);
199 	GALastUpdate = now;
200     } else {
201 	GA_xd = xd;
202 	TimerNo = SetTimer((HWND)0, (UINT_PTR)0, (UINT) xd->timeafter,
203 			   GA_timer_proc);
204     }
205 }
206 
GA_holdflush(pDevDesc dd,int level)207 static int GA_holdflush(pDevDesc dd, int level)
208 {
209     gadesc *xd = (gadesc *) dd->deviceSpecific;
210     if(!xd->buffered) return 0;
211     int old = xd->holdlevel;
212     xd->holdlevel += level;
213     if(xd->holdlevel <= 0) xd->holdlevel = 0;
214     if(xd->holdlevel == 0) {
215 	GA_xd = xd;
216 	gsetcursor(xd->gawin, ArrowCursor);
217 	drawbits(GA_xd);
218 	GALastUpdate = GetTickCount();
219     }
220     if (old == 0 && xd->holdlevel > 0) {
221 	if(TimerNo != 0) KillTimer(0, TimerNo);
222 	drawbits(xd);
223 	GA_xd = NULL;
224 	gsetcursor(xd->gawin, WatchCursor);
225     }
226     return xd->holdlevel;
227 }
228 
229 
230 	/********************************************************/
231 	/* There are a number of actions that every device	*/
232 	/* driver is expected to perform (even if, in some	*/
233 	/* cases it does nothing - just so long as it doesn't	*/
234 	/* crash !).  this is how the graphics engine interacts */
235 	/* with each device. Each action will be documented	*/
236 	/* individually.					*/
237 	/* hooks for these actions must be set up when the	*/
238 	/* device is first created				*/
239 	/********************************************************/
240 
241 	/* Device Driver Actions */
242 
243 static void GA_Activate(pDevDesc dd);
244 static void GA_Circle(double x, double y, double r,
245 		      const pGEcontext gc,
246 		      pDevDesc dd);
247 static void GA_Clip(double x0, double x1, double y0, double y1,
248 		     pDevDesc dd);
249 static void GA_Close(pDevDesc dd);
250 static void GA_Deactivate(pDevDesc dd);
251 static void GA_eventHelper(pDevDesc dd, int code);
252 static Rboolean GA_Locator(double *x, double *y, pDevDesc dd);
253 static void GA_Line(double x1, double y1, double x2, double y2,
254 		    const pGEcontext gc,
255 		    pDevDesc dd);
256 static void GA_MetricInfo(int c,
257 			  const pGEcontext gc,
258 			  double* ascent, double* descent,
259 			  double* width, pDevDesc dd);
260 static void GA_Mode(int mode, pDevDesc dd);
261 static void GA_NewPage(const pGEcontext gc,
262 		       pDevDesc dd);
263 static void GA_Path(double *x, double *y,
264                     int npoly, int *nper,
265                     Rboolean winding,
266                     const pGEcontext gc,
267                     pDevDesc dd);
268 static void GA_Polygon(int n, double *x, double *y,
269 		       const pGEcontext gc,
270 		       pDevDesc dd);
271 static void GA_Polyline(int n, double *x, double *y,
272 			const pGEcontext gc,
273 			pDevDesc dd);
274 static void GA_Rect(double x0, double y0, double x1, double y1,
275 		    const pGEcontext gc,
276 		    pDevDesc dd);
277 static void GA_Size(double *left, double *right,
278 		    double *bottom, double *top,
279 		    pDevDesc dd);
280 static void GA_Resize(pDevDesc dd);
281 static void GA_Raster(unsigned int *raster, int w, int h,
282                       double x, double y,
283                       double width, double height,
284                       double rot,
285                       Rboolean interpolate,
286                       const pGEcontext gc, pDevDesc dd);
287 static SEXP GA_Cap(pDevDesc dd);
288 static double GA_StrWidth(const char *str,
289 			  const pGEcontext gc,
290 			  pDevDesc dd);
291 static void GA_Text(double x, double y, const char *str,
292 		    double rot, double hadj,
293 		    const pGEcontext gc,
294 		    pDevDesc dd);
295 static Rboolean GA_Open(pDevDesc, gadesc*, const char*, double, double,
296 			Rboolean, int, int, double, int, int, int);
297 static Rboolean GA_NewFrameConfirm(pDevDesc);
298 static SEXP     GA_setPattern(SEXP pattern, pDevDesc dd);
299 static void     GA_releasePattern(SEXP ref, pDevDesc dd);
300 static SEXP     GA_setClipPath(SEXP path, SEXP ref, pDevDesc dd);
301 static void     GA_releaseClipPath(SEXP ref, pDevDesc dd);
302 static SEXP     GA_setMask(SEXP path, SEXP ref, pDevDesc dd);
303 static void     GA_releaseMask(SEXP ref, pDevDesc dd);
304 
305 
306 	/********************************************************/
307 	/* end of list of required device driver actions	*/
308 	/********************************************************/
309 
310 //#include "rbitmap.h"
311 extern int
312 R_SaveAsPng(void  *d, int width, int height,
313 	    unsigned int (*gp)(void *, int, int),
314 	    int bgr, FILE *fp, unsigned int transparent, int res);
315 extern int
316 R_SaveAsJpeg(void  *d, int width, int height,
317 	     unsigned int (*gp)(void *, int, int),
318 	     int bgr, int quality, FILE *outfile, int res);
319 extern int
320 R_SaveAsTIFF(void  *d, int width, int height,
321 	     unsigned int (*gp)(void *, int, int),
322 	     int bgr, const char *outfile, int res, int compression);
323 extern int
324 R_SaveAsBmp(void  *d, int width, int height,
325 	    unsigned int (*gp)(void *, int, int), int bgr, FILE *fp, int res);
326 const char * R_pngVersion(void);
327 const char * R_jpegVersion(void);
328 const char * R_tiffVersion(void);
329 
330 	/* Support Routines */
331 
332 static double pixelHeight(drawing  d);
333 static double pixelWidth(drawing d);
334 static void SetColor(int, double, gadesc*);
335 static void SetFont(pGEcontext, double, gadesc*);
336 static void SaveAsPng(pDevDesc dd, const char *fn);
337 static void SaveAsJpeg(pDevDesc dd, int quality, const char *fn);
338 static void SaveAsBmp(pDevDesc dd, const char *fn);
339 static void SaveAsTiff(pDevDesc dd, const char *fn);
340 static void SaveAsBitmap(pDevDesc dd, int res);
341 
PrivateCopyDevice(pDevDesc dd,pDevDesc ndd,const char * name)342 static void PrivateCopyDevice(pDevDesc dd, pDevDesc ndd, const char *name)
343 {
344     pGEDevDesc gdd;
345     int saveDev = curDevice();
346     gadesc *xd = (gadesc *) dd->deviceSpecific;
347     gsetcursor(xd->gawin, WatchCursor);
348     gsetVar(R_DeviceSymbol, mkString(name), R_BaseEnv);
349     ndd->displayListOn = FALSE;
350     gdd = GEcreateDevDesc(ndd);
351     GEaddDevice(gdd);
352     GEcopyDisplayList(ndevNumber(dd));
353     GEkillDevice(gdd);
354     selectDevice(saveDev);
355     gsetcursor(xd->gawin, ArrowCursor);
356     show(xd->gawin);
357 }
358 
SaveAsWin(pDevDesc dd,const char * display,Rboolean restoreConsole)359 static void SaveAsWin(pDevDesc dd, const char *display,
360 		      Rboolean restoreConsole)
361 {
362     pDevDesc ndd = (pDevDesc) calloc(1, sizeof(DevDesc));
363     if (!ndd) {
364 	R_ShowMessage(_("Not enough memory to copy graphics window"));
365 	return;
366     }
367     if(!R_CheckDeviceAvailableBool()) {
368 	free(ndd);
369 	R_ShowMessage(_("No device available to copy graphics window"));
370 	return;
371     }
372 
373     pGEDevDesc gdd = desc2GEDesc(dd);
374     if (GADeviceDriver(ndd, display,
375 		       fromDeviceWidth(toDeviceWidth(1.0, GE_NDC, gdd),
376 				       GE_INCHES, gdd),
377 		       fromDeviceHeight(toDeviceHeight(-1.0, GE_NDC, gdd),
378 					GE_INCHES, gdd),
379 		       ((gadesc*) dd->deviceSpecific)->basefontsize,
380 		       0, 1, White, White, 1, NA_INTEGER, NA_INTEGER, FALSE,
381 		       R_GlobalEnv, restoreConsole, "", FALSE,
382 		       ((gadesc*) dd->deviceSpecific)->fillOddEven, "",
383 		       DEFAULT_QUALITY))
384 	PrivateCopyDevice(dd, ndd, display);
385 }
386 
init_PS_PDF(void)387 static void init_PS_PDF(void)
388 {
389     SEXP call, initS, grNS=R_FindNamespace(mkString("grDevices"));
390 
391     initS = findVarInFrame3(grNS, install("initPSandPDFfonts"), TRUE);
392     if(initS == R_UnboundValue)
393 	error("missing initPSandPDFfonts() in grDevices namespace: this should not happen");
394     PROTECT(call = lang1(initS));
395     eval(call, R_GlobalEnv);
396     UNPROTECT(1);
397 }
398 
399 
SaveAsPostscript(pDevDesc dd,const char * fn)400 static void SaveAsPostscript(pDevDesc dd, const char *fn)
401 {
402     SEXP s;
403     pDevDesc ndd = (pDevDesc) calloc(1, sizeof(DevDesc));
404     pGEDevDesc gdd = desc2GEDesc(dd);
405     gadesc *xd = (gadesc *) dd->deviceSpecific;
406     char family[256], encoding[256], paper[256], bg[256], fg[256];
407     const char **afmpaths = NULL;
408 
409     if (!ndd) {
410 	R_ShowMessage(_("Not enough memory to copy graphics window"));
411 	return;
412     }
413     if(!R_CheckDeviceAvailableBool()) {
414 	free(ndd);
415 	R_ShowMessage(_("No device available to copy graphics window"));
416 	return;
417     }
418 
419     if(strchr(fn, '%')) error(_("'%%' is not allowed in file name"));
420 
421     /* need to initialize PS/PDF font database:
422        also sets .PostScript.Options */
423     init_PS_PDF();
424     /* Set default values and pad with zeroes ... */
425     strncpy(family, "Helvetica", 256);
426     strcpy(encoding, "ISOLatin1.enc");
427     strncpy(paper, "special", 256);
428     strncpy(bg, "transparent", 256);
429     strncpy(fg, "black", 256);
430     /* and then try to get it from .PostScript.Options */
431     s = findVar(install(".PostScript.Options"), xd->psenv);
432     if ((s != R_UnboundValue) && (s != R_NilValue)) {
433 	SEXP names = getAttrib(s, R_NamesSymbol);
434 	int i, done;
435 	for (i = 0, done = 0; (done<  4) && (i < length(s)) ; i++) {
436 	    if(!strcmp("family", CHAR(STRING_ELT(names, i)))) {
437 		strncpy(family, CHAR(STRING_ELT(VECTOR_ELT(s, i), 0)), 255);
438 		done++;
439 	    }
440 	    if(!strcmp("paper", CHAR(STRING_ELT(names, i)))) {
441 		strncpy(paper, CHAR(STRING_ELT(VECTOR_ELT(s, i), 0)), 255);
442 		done++;
443 		if(strcmp(paper, "default") == 0)
444 		    strncpy(paper, "special", 255);
445 	    }
446 	    if(!strcmp("bg", CHAR(STRING_ELT(names, i)))) {
447 		strncpy(bg, CHAR(STRING_ELT(VECTOR_ELT(s, i), 0)), 255);
448 		done++;
449 	    }
450 	    if(!strcmp("fg", CHAR(STRING_ELT(names, i)))) {
451 		strncpy(fg, CHAR(STRING_ELT(VECTOR_ELT(s, i), 0)), 255);
452 		done++;
453 	    }
454 	}
455     }
456     if (PSDeviceDriver(ndd, fn, paper, family, afmpaths, encoding,
457 		       bg, fg,
458 		       fromDeviceWidth(toDeviceWidth(1.0, GE_NDC, gdd),
459 				       GE_INCHES, gdd),
460 		       fromDeviceHeight(toDeviceHeight(-1.0, GE_NDC, gdd),
461 					GE_INCHES, gdd),
462 		       (double)0, ((gadesc*) dd->deviceSpecific)->basefontsize,
463 		       0, 1, 0, "", "R Graphics Output", R_NilValue, "rgb",
464 		       TRUE, xd->fillOddEven))
465 	/* horizontal=F, onefile=F, pagecentre=T, print.it=F */
466 	PrivateCopyDevice(dd, ndd, "postscript");
467 }
468 
469 
SaveAsPDF(pDevDesc dd,const char * fn)470 static void SaveAsPDF(pDevDesc dd, const char *fn)
471 {
472     SEXP s;
473     pDevDesc ndd = (pDevDesc) calloc(1, sizeof(DevDesc));
474     pGEDevDesc gdd = desc2GEDesc(dd);
475     gadesc *xd = (gadesc *) dd->deviceSpecific;
476     char family[256], encoding[256], bg[256], fg[256];
477     const char **afmpaths = NULL;
478     Rboolean useCompression = FALSE;
479 
480     if (!ndd) {
481 	R_ShowMessage(_("Not enough memory to copy graphics window"));
482 	return;
483     }
484     if(!R_CheckDeviceAvailableBool()) {
485 	free(ndd);
486 	R_ShowMessage(_("No device available to copy graphics window"));
487 	return;
488     }
489 
490     if(strchr(fn, '%')) error(_("'%%' is not allowed in file name"));
491 
492     /* Set default values... */
493     init_PS_PDF();
494     s = findVar(install(".PDF.Options"), xd->psenv);
495     strncpy(family, "Helvetica", 256);
496     strcpy(encoding, "ISOLatin1.enc");
497     strncpy(bg, "transparent", 256);
498     strncpy(fg, "black", 256);
499     /* and then try to get it from .PDF.Options */
500     if ((s != R_UnboundValue) && (s != R_NilValue)) {
501 	SEXP names = getAttrib(s, R_NamesSymbol);
502 	for (int i = 0; i < length(s) ; i++) {
503 	    if(!strcmp("family", CHAR(STRING_ELT(names, i))))
504 		strncpy(family, CHAR(STRING_ELT(VECTOR_ELT(s, i), 0)),255);
505 	    if(!strcmp("bg", CHAR(STRING_ELT(names, i))))
506 		strncpy(bg, CHAR(STRING_ELT(VECTOR_ELT(s, i), 0)), 255);
507 	    if(!strcmp("fg", CHAR(STRING_ELT(names, i))))
508 		strncpy(fg, CHAR(STRING_ELT(VECTOR_ELT(s, i), 0)), 255);
509 	    if(!strcmp("compress", CHAR(STRING_ELT(names, i))))
510 		useCompression = LOGICAL(VECTOR_ELT(s, i))[0] != 0;
511 	}
512     }
513     if (PDFDeviceDriver(ndd, fn, "special", family, afmpaths, encoding,
514 			bg, fg,
515 			fromDeviceWidth(toDeviceWidth(1.0, GE_NDC, gdd),
516 					GE_INCHES, gdd),
517 			fromDeviceHeight(toDeviceHeight(-1.0, GE_NDC, gdd),
518 					 GE_INCHES, gdd),
519 			((gadesc*) dd->deviceSpecific)->basefontsize,
520 			1, 0, "R Graphics Output", R_NilValue, 1, 4,
521 			"rgb", TRUE, TRUE, xd->fillOddEven, useCompression))
522 	PrivateCopyDevice(dd, ndd, "PDF");
523 }
524 
525 
526 			/* Pixel Dimensions (Inches) */
pixelWidth(drawing obj)527 static double pixelWidth(drawing obj)
528 {
529     return ((double) 1) / devicepixelsx(obj);
530 }
531 
pixelHeight(drawing obj)532 static double pixelHeight(drawing obj)
533 {
534     return ((double) 1) / devicepixelsy(obj);
535 }
536 
537 			/* Font information array. */
538 			/* Point sizes: 6-24 */
539 			/* Faces: plain, bold, oblique, bold-oblique */
540 			/* Symbol may be added later */
541 
542 #define NFONT 19
543 #define MAXFONT 32
544 static int fontnum;
545 static int fontinitdone = 0;/* in {0,1,2} */
546 static char *fontname[MAXFONT];
547 static int fontstyle[MAXFONT];
548 
RStandardFonts()549 static void RStandardFonts()
550 {
551     int   i;
552 
553     for (i = 0; i < 4; i++) fontname[i] = "Arial";
554     fontname[4] = "Symbol";
555     fontstyle[0] = fontstyle[4] = Plain;
556     fontstyle[1] = Bold;
557     fontstyle[2] = Italic;
558     fontstyle[3] = BoldItalic;
559     fontnum = 5;
560     fontinitdone = 2;		/* =fontinit done & fontname must not be
561 				   free-ed */
562 }
563 
564 
RFontInit()565 static void RFontInit()
566 {
567     int   i, notdone;
568     char *opt[2];
569     char  oops[256];
570 
571     snprintf(oops, 256, "%s/Rdevga", getenv("R_USER"));
572     notdone = 1;
573     fontnum = 0;
574     fontinitdone = 1;
575     if (!optopenfile(oops)) {
576 	snprintf(oops, 256, "%s/etc/Rdevga", getenv("R_HOME"));
577 	if (!optopenfile(oops)) {
578 	    RStandardFonts();
579 	    notdone = 0;
580 	}
581     }
582     while (notdone) {
583 	oops[0] = '\0';
584 	notdone = optread(opt, ':');
585 	if (notdone == 1)
586 	    snprintf(oops, 256, "[%s] Error at line %d.", optfile(), optline());
587 	else if (notdone == 2) {
588 	    fontname[fontnum] = strdup(opt[0]);
589 	    if (!fontname[fontnum])
590 		strcpy(oops, "Insufficient memory. ");
591 	    else {
592 		if (!strcmpi(opt[1], "plain"))
593 		    fontstyle[fontnum] = Plain;
594 		else if (!strcmpi(opt[1], "bold"))
595 		    fontstyle[fontnum] = Bold;
596 		else if (!strcmpi(opt[1], "italic"))
597 		    fontstyle[fontnum] = Italic;
598 		else if (!strcmpi(opt[1], "bold&italic"))
599 		    fontstyle[fontnum] = BoldItalic;
600 		else
601 		    snprintf(oops, 256, "Unknown style at line %d. ", optline());
602 		fontnum += 1;
603 	    }
604 	}
605 	if (oops[0]) {
606 	    optclosefile();
607 	    strcat(oops, optfile());
608 	    strcat(oops, " will be ignored.");
609 	    R_ShowMessage(oops);
610 	    for (i = 0; i < fontnum; i++) free(fontname[i]);
611 	    RStandardFonts();
612 	    notdone = 0;
613 	}
614 	if (fontnum == MAXFONT) {
615 	    optclosefile();
616 	    notdone = 0;
617 	}
618     }
619 }
620 
621 /* Return a non-relocatable copy of a string */
622 
SaveFontSpec(SEXP sxp,int offset)623 static char *SaveFontSpec(SEXP sxp, int offset)
624 {
625     char *s;
626     if(!isString(sxp) || length(sxp) <= offset)
627 	error(_("invalid font specification"));
628     s = R_alloc(strlen(CHAR(STRING_ELT(sxp, offset)))+1, sizeof(char));
629     strcpy(s, CHAR(STRING_ELT(sxp, offset)));
630     return s;
631 }
632 
633 /*
634  * Take the fontfamily from a gcontext (which is device-independent)
635  * and convert it into a Windows-specific font description using
636  * the Windows font database (see src/library/grDevices/R/windows/windows.R)
637  *
638  * IF gcontext fontfamily is empty ("")
639  * OR IF can't find gcontext fontfamily in font database
640  * THEN return NULL
641  */
translateFontFamily(const char * family)642 static char* translateFontFamily(const char* family) {
643     SEXP graphicsNS, windowsenv, fontdb, fontnames;
644     int i, nfonts;
645     char* result = NULL;
646     PROTECT_INDEX xpi;
647 
648     PROTECT(graphicsNS = R_FindNamespace(ScalarString(mkChar("grDevices"))));
649     PROTECT_WITH_INDEX(windowsenv = findVar(install(".WindowsEnv"),
650 					    graphicsNS), &xpi);
651     if(TYPEOF(windowsenv) == PROMSXP)
652 	REPROTECT(windowsenv = eval(windowsenv, graphicsNS), xpi);
653     PROTECT(fontdb = findVar(install(".Windows.Fonts"), windowsenv));
654     PROTECT(fontnames = getAttrib(fontdb, R_NamesSymbol));
655     nfonts = LENGTH(fontdb);
656     if (strlen(family) > 0) {
657 	int found = 0;
658 	for (i = 0; i < nfonts && !found; i++) {
659 	    const char* fontFamily = CHAR(STRING_ELT(fontnames, i));
660 	    if (strcmp(family, fontFamily) == 0) {
661 		found = 1;
662 		result = SaveFontSpec(VECTOR_ELT(fontdb, i), 0);
663 	    }
664 	}
665 	if (!found)
666 	    warning(_("font family not found in Windows font database"));
667     }
668     UNPROTECT(4);
669     return result;
670 }
671 
672 /* Set the font size and face */
673 /* If the font of this size and at that the specified */
674 /* rotation is not present it is loaded. */
675 /* 0 = plain text, 1 = bold */
676 /* 2 = oblique, 3 = bold-oblique */
677 
678 #define SMALLEST 1
679 
SetFont(pGEcontext gc,double rot,gadesc * xd)680 static void SetFont(pGEcontext gc, double rot, gadesc *xd)
681 {
682     int size, face = gc->fontface, usePoints;
683     char* fontfamily;
684     double fs = gc->cex * gc->ps;
685     int quality = xd->fontquality;
686 
687     usePoints = xd->kind <= METAFILE;
688     if (!usePoints && xd->res_dpi > 0) fs *= xd->res_dpi/72.0;
689     size = fs + 0.5;
690 
691     if (face < 1 || face > fontnum) face = 1;
692     if (size < SMALLEST) size = SMALLEST;
693     if (size != xd->fontsize || face != xd->fontface ||
694 	 rot != xd->fontangle || strcmp(gc->fontfamily, xd->fontfamily)) {
695 	if(xd->font) del(xd->font);
696 	doevent();
697 	/*
698 	 * If specify family = "", get family from face via Rdevga
699 	 *
700 	 * If specify a family and a face in 1 to 4 then get
701 	 * that family (mapped through WindowsFonts()) and face.
702 	 *
703 	 * If specify face > 4 then get font from face via Rdevga
704 	 * (whether specifed family or not).
705 	 */
706 	char * fm = gc->fontfamily;
707 	if (!fm[0]) fm = xd->basefontfamily;
708 	fontfamily = translateFontFamily(fm);
709 	if (fontfamily && face <= 4) {
710 	    xd->font = gnewfont2(xd->gawin,
711 				fontfamily, fontstyle[face - 1],
712 				size, rot, usePoints, quality);
713 	} else {
714 	    xd->font = gnewfont2(xd->gawin,
715 				fontname[face - 1], fontstyle[face - 1],
716 				size, rot, usePoints, quality);
717 	}
718 	if (xd->font) {
719 	    strcpy(xd->fontfamily, gc->fontfamily);
720 	    xd->fontface = face;
721 	    xd->fontsize = size;
722 	    xd->fontangle = rot;
723 	} else {
724 	    /* Fallback: set Arial */
725 	    if (face > 4) face = 1;
726 	    xd->font = gnewfont2(xd->gawin,
727 				"Arial", fontstyle[face - 1],
728 				size, rot, usePoints, quality);
729 	    if (!xd->font)
730 		error("unable to set or substitute a suitable font");
731 	    xd->fontface = face;
732 	    xd->fontsize = size;
733 	    xd->fontangle = rot;
734 	    strcpy(xd->fontfamily, "Arial");
735 	    warning("unable to set font: using Arial");
736 	}
737     }
738 }
739 
740 
SetColor(int color,double gamma,gadesc * xd)741 static void SetColor(int color, double gamma, gadesc *xd)
742 {
743     if (color != xd->col) {
744 	xd->col = color;
745 	xd->fgcolor = GArgb(color, gamma);
746     }
747 }
748 
749 
750 /*
751  *	Some Notes on Line Textures
752  *
753  *	Line textures are stored as an array of 4-bit integers within
754  *	a single 32-bit word.  These integers contain the lengths of
755  *	lines to be drawn with the pen alternately down and then up.
756  *	The device should try to arrange that these values are measured
757  *	in points if possible, although pixels is ok on most displays.
758  *
759  *	If newlty contains a line texture description it is decoded
760  *	as follows:
761  *
762  *		ndash = 0;
763  *		for(i=0 ; i<8 && newlty&15 ; i++) {
764  *			dashlist[ndash++] = newlty&15;
765  *			newlty = newlty>>4;
766  *		}
767  *		dashlist[0] = length of pen-down segment
768  *		dashlist[1] = length of pen-up segment
769  *		etc
770  *
771  *	An integer containing a zero terminates the pattern.  Hence
772  *	ndash in this code fragment gives the length of the texture
773  *	description.  If a description contains an odd number of
774  *	elements it is replicated to create a pattern with an
775  *	even number of elements.  (If this is a pain, do something
776  *	different its not crucial).
777  *
778  *	27/5/98 Paul - change to allow lty and lwd to interact:
779  *	the line texture is now scaled by the line width so that,
780  *	for example, a wide (lwd=2) dotted line (lty=2) has bigger
781  *	dots which are more widely spaced.  Previously, such a line
782  *	would have "dots" which were wide, but not long, nor widely
783  *	spaced.
784  *      In this driver, done in graphapp/gdraw.c
785  */
786 
SetLineStyle(const pGEcontext gc,pDevDesc dd)787 static void SetLineStyle(const pGEcontext gc, pDevDesc dd)
788 {
789     gadesc *xd = (gadesc *) dd->deviceSpecific;
790 
791     xd->lty = gc->lty;
792     if(xd->lwdscale != 1.0)
793 	xd->lwd = xd->lwdscale * gc->lwd;
794     else xd->lwd = gc->lwd;
795     if(xd->lwd < 1) xd->lwd = 1;
796     switch (gc->lend) {
797     case GE_ROUND_CAP:
798 	xd->lend = PS_ENDCAP_ROUND;
799 	break;
800     case GE_BUTT_CAP:
801 	xd->lend = PS_ENDCAP_FLAT;
802 	break;
803     case GE_SQUARE_CAP:
804 	xd->lend = PS_ENDCAP_SQUARE;
805 	break;
806     default:
807 	error(_("invalid line end"));
808     }
809     switch (gc->ljoin) {
810     case GE_ROUND_JOIN:
811 	xd->ljoin = PS_JOIN_ROUND;
812 	break;
813     case GE_MITRE_JOIN:
814 	xd->ljoin = PS_JOIN_MITER;
815 	break;
816     case GE_BEVEL_JOIN:
817 	xd->ljoin = PS_JOIN_BEVEL;
818 	break;
819     default:
820 	error(_("invalid line join"));
821     }
822 
823     xd->lmitre = gc->lmitre;
824 }
825 
826 /* Callback functions */
827 
828 
HelpResize(window w,rect r)829 static void HelpResize(window w, rect r)
830 {
831     if (AllDevicesKilled) return;
832     {
833 	pDevDesc dd = (pDevDesc) getdata(w);
834 	gadesc *xd = (gadesc *) dd->deviceSpecific;
835 
836 	if (r.width) {
837 	    if ((xd->windowWidth != r.width) ||
838 		((xd->windowHeight != r.height))) {
839 		xd->windowWidth = r.width;
840 		xd->windowHeight = r.height;
841 		xd->resize = TRUE;
842 	    }
843 	}
844     }
845 }
846 
HelpClose(window w)847 static void HelpClose(window w)
848 {
849     if (AllDevicesKilled) return;
850     {
851 	pDevDesc dd = (pDevDesc) getdata(w);
852 	killDevice(ndevNumber(dd));
853     }
854 }
855 
HelpExpose(window w,rect r)856 static void HelpExpose(window w, rect r)
857 {
858     if (AllDevicesKilled) return;
859     {
860 	pDevDesc dd = (pDevDesc) getdata(w);
861 	pGEDevDesc gdd = desc2GEDesc(dd);
862 	gadesc *xd = (gadesc *) dd->deviceSpecific;
863 
864 	if (xd->resize) {
865 	    GA_Resize(dd);
866 	    /* avoid trying to replay list if there has been no drawing */
867 	    if(gdd->dirty) {
868 		xd->replaying = TRUE;
869 		GEplayDisplayList(gdd);
870 		xd->replaying = FALSE;
871 	    }
872 	    R_ProcessEvents();
873 	} else
874 	    SHOW;
875     }
876 }
877 
HelpMouseClick(window w,int button,point pt)878 static void HelpMouseClick(window w, int button, point pt)
879 {
880     if (AllDevicesKilled) return;
881     {
882 	pDevDesc dd = (pDevDesc) getdata(w);
883 	gadesc *xd = (gadesc *) dd->deviceSpecific;
884 
885 	if (!xd->locator && !xd->confirmation && !dd->gettingEvent)
886 	    return;
887 	if (button & LeftButton) {
888 	    int useBeep = xd->locator &&
889 		asLogical(GetOption1(install("locatorBell")));
890 	    if(useBeep) gabeep();
891 	    xd->clicked = 1;
892 	    xd->px = pt.x;
893 	    xd->py = pt.y;
894 	} else
895 	    xd->clicked = 2;
896 	if (dd->gettingEvent) {
897 	    doMouseEvent(dd, meMouseDown, button, pt.x, pt.y);
898 	    if (xd->buffered) SHOW;
899 	}
900     }
901 }
902 
HelpMouseMove(window w,int button,point pt)903 static void HelpMouseMove(window w, int button, point pt)
904 {
905     if (AllDevicesKilled) return;
906     {
907 	pDevDesc dd = (pDevDesc) getdata(w);
908 	gadesc *xd = (gadesc *) dd->deviceSpecific;
909 
910 	if (dd->gettingEvent) {
911 	    doMouseEvent(dd, meMouseMove, button, pt.x, pt.y);
912 	    if (xd->buffered) SHOW;
913 	}
914     }
915 }
916 
HelpMouseUp(window w,int button,point pt)917 static void HelpMouseUp(window w, int button, point pt)
918 {
919     if (AllDevicesKilled) return;
920     {
921 	pDevDesc dd = (pDevDesc) getdata(w);
922 	gadesc *xd = (gadesc *) dd->deviceSpecific;
923 
924 	if (dd->gettingEvent) {
925 	    doMouseEvent(dd, meMouseUp,button, pt.x, pt.y);
926 	    if (xd->buffered) SHOW;
927 	}
928     }
929 }
930 
menustop(control m)931 static void menustop(control m)
932 {
933     pDevDesc dd = (pDevDesc) getdata(m);
934     gadesc *xd = (gadesc *) dd->deviceSpecific;
935     if (!xd->locator)
936 	return;
937     xd->clicked = 2;
938 }
939 
menunextplot(control m)940 static void menunextplot(control m)
941 {
942     pDevDesc dd = (pDevDesc) getdata(m);
943     gadesc *xd = (gadesc *) dd->deviceSpecific;
944     xd->clicked = 2;
945 }
946 
menufilebitmap(control m)947 static void menufilebitmap(control m)
948 {
949     pDevDesc dd = (pDevDesc) getdata(m);
950     gadesc *xd = (gadesc *) dd->deviceSpecific;
951     char *fn;
952     /* the following use a private hook to set the default extension */
953     if (m == xd->mpng) {
954 	setuserfilter("Png files (*.png)\0*.png\0\0");
955 	fn = askfilesave(G_("Portable network graphics file"), "|.png");
956     } else if (m == xd->mbmp) {
957 	setuserfilter("Windows bitmap files (*.bmp)\0*.bmp\0\0");
958 	fn = askfilesave(G_("Windows bitmap file"), "|.bmp");
959     } else if (m == xd->mtiff) {
960 	setuserfilter("TIFF files (*.tiff,*.tif)\0*.tiff;*.tif\0\0");
961 	fn = askfilesave(G_("TIFF file"), "|.tif");
962     } else {
963 	setuserfilter("Jpeg files (*.jpeg,*.jpg)\0*.jpeg;*.jpg\0\0");
964 	fn = askfilesave(G_("Jpeg file"), "|.jpg");
965     }
966     if (!fn) return;
967     gsetcursor(xd->gawin, WatchCursor);
968     show(xd->gawin);
969     if (m==xd->mpng) SaveAsPng(dd, fn);
970     else if (m==xd->mbmp) SaveAsBmp(dd, fn);
971     else if (m==xd->mtiff) SaveAsTiff(dd, fn);
972     else if (m==xd->mjpeg50) SaveAsJpeg(dd, 50, fn);
973     else if (m==xd->mjpeg75) SaveAsJpeg(dd, 75, fn);
974     else SaveAsJpeg(dd, 100, fn);
975     gsetcursor(xd->gawin, ArrowCursor);
976     show(xd->gawin);
977 }
978 
979 
menups(control m)980 static void menups(control m)
981 {
982     pDevDesc dd = (pDevDesc) getdata(m);
983     char  *fn;
984 
985     setuserfilter("Encapsulated postscript files (*.eps)\0*.eps\0Postscript files (*.ps)\0*.ps\0All files (*.*)\0*.*\0\0");
986     fn = askfilesave(G_("Postscript file"), "|.ps");
987     if (!fn) return;
988     SaveAsPostscript(dd, fn);
989 }
990 
991 
menupdf(control m)992 static void menupdf(control m)
993 {
994     pDevDesc dd = (pDevDesc) getdata(m);
995     char  *fn;
996 
997     setuserfilter("PDF files (*.pdf)\0*.pdf\0All files (*.*)\0*.*\0\0");
998     fn = askfilesave(G_("PDF file"), "|.pdf");
999     if (!fn) return;
1000     SaveAsPDF(dd, fn);
1001 }
1002 
1003 
menuwm(control m)1004 static void menuwm(control m)
1005 {
1006     pDevDesc dd = (pDevDesc) getdata(m);
1007     char  display[550], *fn;
1008 
1009     setuserfilter("Enhanced metafiles (*.emf)\0*.emf\0All files (*.*)\0*.*\0\0");
1010     fn = askfilesave(G_("Enhanced metafiles"), "|.emf");
1011     if (!fn) return;
1012     if(strlen(fn) > 512) {
1013 	askok(G_("file path selected is too long: only 512 bytes are allowed"));
1014 	return;
1015     }
1016     snprintf(display, 550, "win.metafile:%s", fn);
1017     SaveAsWin(dd, display, TRUE);
1018 }
1019 
1020 
menuclpwm(control m)1021 static void menuclpwm(control m)
1022 {
1023     pDevDesc dd = (pDevDesc) getdata(m);
1024     SaveAsWin(dd, "win.metafile", TRUE);
1025 }
1026 
menuclpbm(control m)1027 static void menuclpbm(control m)
1028 {
1029     pDevDesc dd = (pDevDesc) getdata(m);
1030     gadesc *xd = (gadesc *) dd->deviceSpecific;
1031 
1032     show(xd->gawin);
1033     gsetcursor(xd->gawin, WatchCursor);
1034     copytoclipboard(xd->bm);
1035     gsetcursor(xd->gawin, ArrowCursor);
1036 }
1037 
menustayontop(control m)1038 static void menustayontop(control m)
1039 {
1040     pDevDesc dd = (pDevDesc) getdata(m);
1041     gadesc *xd = (gadesc *) dd->deviceSpecific;
1042 
1043     BringToTop(xd->gawin, 2);
1044 }
1045 
menuprint(control m)1046 static void menuprint(control m)
1047 {
1048     pDevDesc dd = (pDevDesc) getdata(m);
1049     SaveAsWin(dd, "win.print:", TRUE);
1050 }
1051 
menuclose(control m)1052 static void menuclose(control m)
1053 {
1054     pDevDesc dd = (pDevDesc) getdata(m);
1055     gadesc *xd = (gadesc *) dd->deviceSpecific;
1056 
1057     HelpClose(xd->gawin);
1058 }
1059 
grpopupact(control m)1060 static void grpopupact(control m)
1061 {
1062     pDevDesc dd = (pDevDesc) getdata(m);
1063     gadesc *xd = (gadesc *) dd->deviceSpecific;
1064 
1065     if (ismdi())
1066 	disable(xd->grmenustayontop);
1067     else {
1068 	if (isTopmost(xd->gawin))
1069 	    check(xd->grmenustayontop);
1070 	else
1071 	    uncheck(xd->grmenustayontop);
1072     }
1073 }
1074 
1075 /* plot history */
1076 
1077 /* NB: this puts .SavedPlots in .GlobalEnv */
1078 #define GROWTH 4
1079 #define GETDL SEXP vDL=findVar(install(".SavedPlots"), R_GlobalEnv)
1080 #define SETDL defineVar(install(".SavedPlots"), vDL, R_GlobalEnv)
1081 /* altered in 1.4.0, as incompatible format */
1082 #define PLOTHISTORYMAGIC 31416
1083 #define pMAGIC      (INTEGER(VECTOR_ELT(vDL, 0))[0])
1084 #define pNUMPLOTS   (INTEGER(VECTOR_ELT(vDL, 1))[0])
1085 #define pMAXPLOTS   (INTEGER(VECTOR_ELT(vDL, 2))[0])
1086 #define pCURRENTPOS (INTEGER(VECTOR_ELT(vDL, 3))[0])
1087 #define pHISTORY    (VECTOR_ELT(vDL, 4))
1088 #define SET_pHISTORY(v)    (SET_VECTOR_ELT(vDL, 4, v))
1089 #define pCURRENT    (VECTOR_ELT(pHISTORY, pCURRENTPOS))
1090 #define pCURRENTdl  (VECTOR_ELT(pCURRENT, 0))
1091 #define pCURRENTgp  (INTEGER(VECTOR_ELT(pCURRENT, 1)))
1092 #define pCURRENTsnapshot (VECTOR_ELT(pCURRENT, 0))
1093 #define pCHECK      if ((TYPEOF(vDL)!=VECSXP)||\
1094 		       (TYPEOF(VECTOR_ELT(vDL, 0))!=INTSXP) ||\
1095 		       (LENGTH(VECTOR_ELT(vDL, 0))!=1) ||\
1096 		       (pMAGIC != PLOTHISTORYMAGIC)) {\
1097 		       R_ShowMessage(_("plot history seems corrupted"));\
1098 		       return;}
1099 #define pMOVE(a) {pCURRENTPOS+=a;\
1100 		  if(pCURRENTPOS<0) pCURRENTPOS=0;\
1101 		  if(pCURRENTPOS>pNUMPLOTS-1) pCURRENTPOS=pNUMPLOTS-1;\
1102 		  Replay(dd,vDL);SETDL;}
1103 #define pEXIST ((vDL!=R_UnboundValue) && (vDL!=R_NilValue))
1104 #define pMUSTEXIST if(!pEXIST){R_ShowMessage(_("no plot history!"));return;}
1105 
1106 
1107 
1108 
NewPlotHistory(int n)1109 static SEXP NewPlotHistory(int n)
1110 {
1111     SEXP  vDL, class;
1112     int   i;
1113 
1114     PROTECT(vDL = allocVector(VECSXP, 5));
1115     for (i = 0; i < 4; i++)
1116 	PROTECT(SET_VECTOR_ELT(vDL, i, allocVector(INTSXP, 1)));
1117     PROTECT(SET_pHISTORY (allocVector(VECSXP, n)));
1118     pMAGIC = PLOTHISTORYMAGIC;
1119     pNUMPLOTS = 0;
1120     pMAXPLOTS = n;
1121     pCURRENTPOS = -1;
1122     for (i = 0; i < n; i++)
1123 	SET_VECTOR_ELT(pHISTORY, i, R_NilValue);
1124     PROTECT(class = mkString("SavedPlots"));
1125     classgets(vDL, class);
1126     SETDL;
1127     UNPROTECT(7);
1128     return vDL;
1129 }
1130 
GrowthPlotHistory(SEXP vDL)1131 static SEXP GrowthPlotHistory(SEXP vDL)
1132 {
1133     SEXP  vOLD;
1134     int   i, oldNPlots, oldCurrent;
1135 
1136     PROTECT(vOLD = pHISTORY);
1137     oldNPlots = pNUMPLOTS;
1138     oldCurrent = pCURRENTPOS;
1139     PROTECT(vDL = NewPlotHistory(pMAXPLOTS + GROWTH));
1140     for (i = 0; i < oldNPlots; i++)
1141 	SET_VECTOR_ELT(pHISTORY, i, VECTOR_ELT(vOLD, i));
1142     pNUMPLOTS = oldNPlots;
1143     pCURRENTPOS = oldCurrent;
1144     SETDL;
1145     UNPROTECT(2);
1146     return vDL;
1147 }
1148 
AddtoPlotHistory(SEXP snapshot,int replace)1149 static void AddtoPlotHistory(SEXP snapshot, int replace)
1150 {
1151     int   where;
1152     SEXP  class;
1153 
1154     GETDL;
1155     PROTECT(snapshot);
1156 /*    if (dl == R_NilValue) {
1157 	R_ShowMessage("Display list is void!");
1158 	return;
1159 	} */
1160     if (!pEXIST)
1161 	vDL = NewPlotHistory(GROWTH);
1162     else if (!replace && (pNUMPLOTS == pMAXPLOTS))
1163 	vDL = GrowthPlotHistory(vDL);
1164     PROTECT(vDL);
1165     pCHECK;
1166     if (replace)
1167 	where = pCURRENTPOS;
1168     else
1169 	where = pNUMPLOTS;
1170 
1171     PROTECT(class = mkString("recordedplot"));
1172     classgets(snapshot, class);
1173     SET_VECTOR_ELT(pHISTORY, where, snapshot);
1174     pCURRENTPOS = where;
1175     if (!replace) pNUMPLOTS += 1;
1176     SETDL;
1177     UNPROTECT(3);
1178 }
1179 
1180 
Replay(pDevDesc dd,SEXP vDL)1181 static void Replay(pDevDesc dd, SEXP vDL)
1182 {
1183     gadesc *xd = (gadesc *) dd->deviceSpecific;
1184 
1185     xd->replaying = TRUE;
1186     gsetcursor(xd->gawin, WatchCursor);
1187     GEplaySnapshot(pCURRENT, desc2GEDesc(dd));
1188     xd->replaying = FALSE;
1189     gsetcursor(xd->gawin, ArrowCursor);
1190 }
1191 
menurec(control m)1192 static void menurec(control m)
1193 {
1194     pDevDesc dd = (pDevDesc) getdata(m);
1195     gadesc *xd = (gadesc *) dd->deviceSpecific;
1196 
1197     if (xd->recording) {
1198 	xd->recording = FALSE;
1199 	uncheck(m);
1200     } else {
1201 	xd->recording = TRUE;
1202 	check(m);
1203     }
1204 }
1205 
1206 
menuadd(control m)1207 static void menuadd(control m)
1208 {
1209     pDevDesc dd = (pDevDesc) getdata(m);
1210     gadesc *xd = (gadesc *) dd->deviceSpecific;
1211 
1212     AddtoPlotHistory(GEcreateSnapshot(desc2GEDesc(dd)), 0);
1213     xd->needsave = FALSE;
1214 }
1215 
menureplace(control m)1216 static void menureplace(control m)
1217 {
1218     pDevDesc dd = (pDevDesc) getdata(m);
1219 
1220     GETDL;
1221     pMUSTEXIST;
1222     pCHECK;
1223     if (pCURRENTPOS < 0) {
1224 	R_ShowMessage(G_("No plot to replace!"));
1225 	return;
1226     }
1227     AddtoPlotHistory(GEcreateSnapshot(desc2GEDesc(dd)), 1);
1228 }
1229 
menunext(control m)1230 static void menunext(control m)
1231 {
1232     pDevDesc dd = (pDevDesc) getdata(m);
1233     gadesc *xd = (gadesc *) dd->deviceSpecific;
1234 
1235     GETDL;
1236     if (xd->needsave) return;
1237     pMUSTEXIST;
1238     pCHECK;
1239     if (pCURRENTPOS != (pNUMPLOTS - 1)) pMOVE(1);
1240     PrintWarnings();
1241 }
1242 
menuprev(control m)1243 static void menuprev(control m)
1244 {
1245     pDevDesc dd = (pDevDesc) getdata(m);
1246     gadesc *xd = (gadesc *) dd->deviceSpecific;
1247 
1248     GETDL;
1249     pMUSTEXIST;
1250     pCHECK;
1251     if (pNUMPLOTS) {
1252 	if (xd->recording && xd->needsave) {
1253 	    pGEDevDesc gdd = desc2GEDesc(dd);
1254 	    if (gdd->displayList != R_NilValue) {
1255 		AddtoPlotHistory(GEcreateSnapshot(gdd), 0);
1256 		xd->needsave = FALSE;
1257 		vDL = findVar(install(".SavedPlots"), R_GlobalEnv);
1258 		/* may have changed vDL pointer */
1259 	    }
1260 	}
1261 	pMOVE((xd->needsave) ? 0 : -1);
1262     }
1263     PrintWarnings();
1264 }
1265 
menugrclear(control m)1266 static void menugrclear(control m)
1267 {
1268     defineVar(install(".SavedPlots"), R_NilValue, R_GlobalEnv);
1269 }
1270 
menugvar(control m)1271 static void menugvar(control m)
1272 {
1273     SEXP  vDL;
1274     char *v = askstring(G_("Variable name"), "");
1275     pDevDesc dd = (pDevDesc) getdata(m);
1276 
1277     if (!v)
1278 	return;
1279     vDL = findVar(install(v), R_GlobalEnv);
1280     pMUSTEXIST;
1281     pCHECK;
1282     if (!pNUMPLOTS) {
1283 	R_ShowMessage(G_("Variable doesn't contain any plots!"));
1284 	return;
1285     }
1286     pCURRENTPOS = 0;
1287     Replay(dd, vDL);
1288     SETDL;
1289 }
1290 
menusvar(control m)1291 static void menusvar(control m)
1292 {
1293     char *v;
1294 
1295     GETDL;
1296     pMUSTEXIST;
1297     pCHECK;
1298     v = askstring(G_("Name of variable to save to"), "");
1299     if (!v)
1300 	return;
1301     defineVar(install(v), vDL, R_GlobalEnv);
1302 }
1303 /* end of plot history */
1304 
menuconsole(control m)1305 static void menuconsole(control m)
1306 {
1307    show(RConsole);
1308 }
1309 
menuR(control m)1310 static void menuR(control m)
1311 {
1312     pDevDesc dd = (pDevDesc) getdata(m);
1313     gadesc *xd = (gadesc *) dd->deviceSpecific;
1314     check(xd->mR);
1315     uncheck(xd->mfix);
1316     uncheck(xd->mfit);
1317     xd->resizing = 1;
1318     xd->resize = TRUE;
1319     HelpExpose(m, getrect(xd->gawin));
1320 }
1321 
menufit(control m)1322 static void menufit(control m)
1323 {
1324     pDevDesc dd = (pDevDesc) getdata(m);
1325     gadesc *xd = (gadesc *) dd->deviceSpecific;
1326 
1327     uncheck(xd->mR);
1328     check(xd->mfit);
1329     uncheck(xd->mfix);
1330     xd->resizing = 2;
1331     xd->resize = TRUE;
1332     HelpExpose(m, getrect(xd->gawin));
1333 }
1334 
menufix(control m)1335 static void menufix(control m)
1336 {
1337     pDevDesc dd = (pDevDesc) getdata(m);
1338     gadesc *xd = (gadesc *) dd->deviceSpecific;
1339 
1340     uncheck(xd->mR);
1341     uncheck(xd->mfit);
1342     check(xd->mfix);
1343     xd->resizing = 3;
1344     xd->resize = TRUE;
1345     HelpExpose(m, getrect(xd->gawin));
1346 }
1347 
getKeyName(int key)1348 static R_KeyName getKeyName(int key)
1349 {
1350     if (F1 <= key && key <= F10) return knF1 + key - F1 ;
1351     else switch (key) {
1352 	case LEFT: return knLEFT;
1353 	case UP:   return knUP;
1354 	case RIGHT:return knRIGHT;
1355 	case DOWN: return knDOWN;
1356 	case PGUP: return knPGUP;
1357 	case PGDN: return knPGDN;
1358 	case END:  return knEND;
1359 	case HOME: return knHOME;
1360 	case INS:  return knINS;
1361 	case DEL:  return knDEL;
1362 	default:   return knUNKNOWN;
1363     }
1364 }
1365 
CHelpKeyIn(control w,int key)1366 static void CHelpKeyIn(control w, int key)
1367 {
1368     pDevDesc dd = (pDevDesc) getdata(w);
1369     gadesc *xd = (gadesc *) dd->deviceSpecific;
1370 
1371     R_KeyName keyname;
1372 
1373     if (dd->gettingEvent) {
1374 	keyname = getKeyName(key);
1375 	if (keyname > knUNKNOWN) {
1376 	    doKeybd(dd, keyname, NULL);
1377 	    if (xd->buffered) SHOW;
1378 	}
1379     } else {
1380 	if (xd->replaying) return;
1381 	switch (key) {
1382 	  case INS:
1383 	    menuadd(xd->madd);
1384 	    break;
1385 	  case PGUP:
1386 	    menuprev(xd->mprev);
1387 	    break;
1388 	  case PGDN:
1389 	    menunext(xd->mnext);
1390 	    break;
1391 	  case ENTER:
1392 	    xd->enterkey = TRUE;
1393 	    break;
1394 	}
1395     }
1396 }
1397 
1398 __declspec(dllimport) extern int UserBreak;
1399 
NHelpKeyIn(control w,int key)1400 static void NHelpKeyIn(control w, int key)
1401 {
1402     char keyname[7];
1403 
1404     pDevDesc dd = (pDevDesc) getdata(w);
1405     gadesc *xd = (gadesc *) dd->deviceSpecific;
1406 
1407     if (dd->gettingEvent) {
1408 	if (0 < key && key < 32) {
1409 	    strcpy(keyname, "ctrl- ");
1410 	    keyname[5] = (char) (key + 'A' - 1);
1411 	} else {
1412 	    keyname[0] = (char) key;
1413 	    keyname[1] = '\0';
1414 	}
1415 	doKeybd(dd, knUNKNOWN, keyname);
1416 	if (xd->buffered) SHOW;
1417     } else {
1418 	if (xd->replaying) return;
1419 	switch (key) {
1420 	  case '\n':  /* ENTER has been translated to newline */
1421 	    xd->enterkey = TRUE;
1422 	    return;
1423 	  case ESC:
1424 	    UserBreak = TRUE;
1425 	    return;
1426 	}
1427 	if (ggetkeystate() != CtrlKey) return;
1428 	key = 'A' + key - 1;
1429 	if (key == 'C') menuclpbm(xd->mclpbm);
1430 	if (desc2GEDesc(dd)->displayList == R_NilValue) return;
1431 	if (key == 'W') menuclpwm(xd->mclpwm);
1432 	else if (key == 'P') menuprint(xd->mprint);
1433     }
1434 }
1435 
mbarf(control m)1436 static void mbarf(control m)
1437 {
1438     pDevDesc dd = (pDevDesc) getdata(m);
1439     gadesc *xd = (gadesc *) dd->deviceSpecific;
1440 
1441     GETDL;
1442     if (pEXIST && !xd->replaying) {
1443 	enable(xd->mnext);
1444 	enable(xd->mprev);
1445 	if (pCURRENTPOS >= 0 && desc2GEDesc(dd)->displayList != R_NilValue)
1446 	    enable(xd->mreplace);
1447 	else
1448 	    disable(xd->mreplace);
1449 	enable(xd->msvar);
1450 	enable(xd->mclear);
1451     } else {
1452 	disable(xd->mnext);
1453 	disable(xd->mprev);
1454 	disable(xd->mreplace);
1455 	disable(xd->msvar);
1456 	disable(xd->mclear);
1457     }
1458     if (!xd->replaying)
1459 	enable(xd->mgvar);
1460     else
1461 	disable(xd->mgvar);
1462     if (!xd->replaying && desc2GEDesc(dd)->displayList != R_NilValue) {
1463 	enable(xd->madd);
1464 	enable(xd->mprint);
1465 	enable(xd->mpng);
1466 	enable(xd->mbmp);
1467 	enable(xd->mtiff);
1468 	enable(xd->mjpeg50);
1469 	enable(xd->mjpeg75);
1470 	enable(xd->mjpeg100);
1471 	enable(xd->mwm);
1472 	enable(xd->mps);
1473 	enable(xd->mpdf);
1474 	enable(xd->mclpwm);
1475 	enable(xd->mclpbm);
1476     } else {
1477 	disable(xd->madd);
1478 	disable(xd->mprint);
1479 	disable(xd->msubsave);
1480 	disable(xd->mpng);
1481 	disable(xd->mbmp);
1482 	disable(xd->mtiff);
1483 	disable(xd->mjpeg50);
1484 	disable(xd->mjpeg75);
1485 	disable(xd->mjpeg100);
1486 	disable(xd->mwm);
1487 	disable(xd->mps);
1488 	disable(xd->mpdf);
1489 	disable(xd->mclpwm);
1490 	disable(xd->mclpbm);
1491     }
1492     draw(xd->mbar);
1493 }
1494 
1495 
1496 	/********************************************************/
1497 	/* device_Open is not usually called directly by the	*/
1498 	/* graphics engine;  it is usually only called from	*/
1499 	/* the device-driver entry point.			*/
1500 	/* this function should set up all of the device-	*/
1501 	/* specific resources for a new device			*/
1502 	/* this function is given a new	structure for device-	*/
1503 	/* specific information AND it must FREE the structure	*/
1504 	/* if anything goes seriously wrong			*/
1505 	/* NOTE that it is perfectly acceptable for this	*/
1506 	/* function to set generic graphics parameters too	*/
1507 	/* (i.e., override the generic parameter settings	*/
1508 	/* which GInit sets up) all at the author's own risk	*/
1509 	/* of course :)						*/
1510 	/********************************************************/
1511 
1512 #define MCHECK(m) {if(!(m)) {del(xd->gawin); return 0;}}
1513 
devga_sbf(control c,int pos)1514 static void devga_sbf(control c, int pos)
1515 {
1516     pDevDesc dd = (pDevDesc) getdata(c);
1517     gadesc *xd = (gadesc *) dd->deviceSpecific;
1518     if (pos < 0) {
1519 	pos = -pos-1;
1520 	pos = min(pos*SF, (xd->origWidth - xd->windowWidth + SF-1));
1521 	xd->xshift = -pos;
1522     } else {
1523 	pos = min(pos*SF, (xd->origHeight - xd->windowHeight + SF-1));
1524 	xd->yshift = -pos;
1525     }
1526     xd->resize = 1;
1527     HelpExpose(c, getrect(xd->gawin));
1528 }
1529 
1530 
1531 static Rboolean
setupScreenDevice(pDevDesc dd,gadesc * xd,double w,double h,Rboolean recording,int resize,int xpos,int ypos)1532 setupScreenDevice(pDevDesc dd, gadesc *xd, double w, double h,
1533 		  Rboolean recording, int resize, int xpos, int ypos)
1534 {
1535     menu  m;
1536     int   iw, ih;
1537     int   cw, ch;
1538     double dw, dw0, dh, d;
1539     char buf[100];
1540 
1541     xd->kind = SCREEN;
1542     if (R_FINITE(user_xpinch) && user_xpinch > 0.0)
1543 	dw = dw0 = (int) (w * user_xpinch);
1544     else
1545 	dw = dw0 = (int) (w / pixelWidth(NULL));
1546     if (R_FINITE(user_ypinch) && user_ypinch > 0.0)
1547 	dh = (int) (h * user_ypinch);
1548     else
1549 	dh = (int) (h / pixelHeight(NULL));
1550 
1551     if (ismdi() && !isiconic(RFrame)) {
1552 	cw = RgetMDIwidth();
1553 	ch = RgetMDIheight();
1554     } else {
1555 	cw = devicewidth(NULL);
1556 	ch = deviceheight(NULL);
1557     }
1558 
1559     if (resize != 3) {
1560 	if ((dw / cw) > 0.85) {
1561 	    d = dh / dw;
1562 	    dw = 0.85 * cw;
1563 	    dh = d * dw;
1564 	}
1565 	if ((dh / ch) > 0.85) {
1566 	    d = dw / dh;
1567 	    dh = 0.85 * ch;
1568 	    dw = d * dh;
1569 	}
1570     } else {
1571 	dw = min(dw, 0.85*cw);
1572 	dh = min(dh, 0.85*ch);
1573     }
1574     iw = dw + 0.5;
1575     ih = dh + 0.5;
1576     if (resize == 2) xd->rescale_factor = dw/dw0;
1577     {
1578 	int grx, gry;
1579 	grx = (xpos == NA_INTEGER) ? Rwin_graphicsx : xpos;
1580 	gry = (ypos == NA_INTEGER) ? Rwin_graphicsy : ypos;
1581 	if (grx < 0) grx = cw - iw + grx;
1582 	if (gry < 0) gry = ch - ih + gry;
1583 	if (!(xd->gawin = newwindow("R Graphics",
1584 				    rect(grx, gry, iw, ih),
1585 				    Document | StandardWindow | Menubar |
1586 				    VScrollbar | HScrollbar | CanvasSize)
1587 		)) {
1588 	    warning("unable to open window");
1589 	    return FALSE;
1590 	}
1591     }
1592     gchangescrollbar(xd->gawin, VWINSB, 0, ih/SF-1, ih/SF, 0);
1593     gchangescrollbar(xd->gawin, HWINSB, 0, iw/SF-1, iw/SF, 0);
1594 
1595     addto(xd->gawin);
1596     gsetcursor(xd->gawin, ArrowCursor);
1597     if (ismdi()) {
1598 	int btsize = 24;
1599 	rect r = rect(2, 2, btsize, btsize);
1600 	control bt, tb;
1601 
1602 	MCHECK(tb = newtoolbar(btsize + 4));
1603 	gsetcursor(tb, ArrowCursor);
1604 	addto(tb);
1605 
1606 	MCHECK(bt = newtoolbutton(cam_image, r, menuclpwm));
1607 	MCHECK(addtooltip(bt, G_("Copy to the clipboard as a metafile")));
1608 	gsetcursor(bt, ArrowCursor);
1609 	setdata(bt, (void *) dd);
1610 	r.x += (btsize + 6);
1611 
1612 	MCHECK(bt = newtoolbutton(print_image, r, menuprint));
1613 	MCHECK(addtooltip(bt, G_("Print")));
1614 	gsetcursor(bt, ArrowCursor);
1615 	setdata(bt, (void *) dd);
1616 	r.x += (btsize + 6);
1617 
1618 	MCHECK(bt = newtoolbutton(console_image, r, menuconsole));
1619 	MCHECK(addtooltip(bt, G_("Return focus to Console")));
1620 	gsetcursor(bt, ArrowCursor);
1621 	setdata(bt, (void *) dd);
1622 	r.x += (btsize + 6);
1623 
1624 	MCHECK(xd->stoploc = newtoolbutton(stop_image, r, menustop));
1625 	MCHECK(addtooltip(xd->stoploc, G_("Stop locator")));
1626 	gsetcursor(bt, ArrowCursor);
1627 	setdata(xd->stoploc,(void *) dd);
1628 	hide(xd->stoploc);
1629     } else
1630 	xd->stoploc = NULL;
1631 
1632     /* First we prepare 'locator' menubar and popup */
1633     addto(xd->gawin);
1634     MCHECK(xd->mbarloc = newmenubar(NULL));
1635     MCHECK(newmenu(G_("Stop")));
1636     MCHECK(m = newmenuitem(G_("Stop locator"), 0, menustop));
1637     setdata(m, (void *) dd);
1638     MCHECK(xd->locpopup = newpopup(NULL));
1639     MCHECK(m = newmenuitem(G_("Stop"), 0, menustop));
1640     setdata(m, (void *) dd);
1641     MCHECK(newmenuitem(G_("Continue"), 0, NULL));
1642 
1643     /* Next the 'Click for next plot' menubar */
1644     MCHECK(xd->mbarconfirm = newmenubar(NULL));
1645     MCHECK(newmenu(G_("Next")));
1646     MCHECK(m = newmenuitem(G_("Next plot"), 0, menunextplot));
1647     setdata(m, (void *) dd);
1648 
1649     /* Normal menubar */
1650     MCHECK(xd->mbar = newmenubar(mbarf));
1651     MCHECK(m = newmenu(G_("File")));
1652     MCHECK(xd->msubsave = newsubmenu(m, G_("Save as")));
1653     MCHECK(xd->mwm = newmenuitem("Metafile...", 0, menuwm));
1654     MCHECK(xd->mps = newmenuitem("Postscript...", 0, menups));
1655     MCHECK(xd->mpdf = newmenuitem("PDF...", 0, menupdf));
1656     MCHECK(xd->mpng = newmenuitem("Png...", 0, menufilebitmap));
1657     MCHECK(xd->mbmp = newmenuitem("Bmp...", 0, menufilebitmap));
1658     MCHECK(xd->mtiff = newmenuitem("TIFF...", 0, menufilebitmap));
1659     MCHECK(newsubmenu(xd->msubsave, "Jpeg"));
1660     /* avoid gettext confusion with % */
1661     snprintf(buf, 100, G_("%s quality..."), "50%");
1662     MCHECK(xd->mjpeg50 = newmenuitem(buf, 0, menufilebitmap));
1663     snprintf(buf, 100, G_("%s quality..."), "75%");
1664     MCHECK(xd->mjpeg75 = newmenuitem(buf, 0, menufilebitmap));
1665     snprintf(buf, 100, G_("%s quality..."), "100%");
1666     MCHECK(xd->mjpeg100 = newmenuitem(buf, 0, menufilebitmap));
1667     MCHECK(newsubmenu(m, G_("Copy to the clipboard")));
1668     MCHECK(xd->mclpbm = newmenuitem(G_("as a Bitmap\tCTRL+C"), 0, menuclpbm));
1669     MCHECK(xd->mclpwm = newmenuitem(G_("as a Metafile\tCTRL+W"), 0, menuclpwm));
1670     addto(m);
1671     MCHECK(newmenuitem("-", 0, NULL));
1672     MCHECK(xd->mprint = newmenuitem(G_("Print..."), 'P', menuprint));
1673     MCHECK(newmenuitem("-", 0, NULL));
1674     MCHECK(xd->mclose = newmenuitem(G_("close Device"), 0, menuclose));
1675     MCHECK(newmenu(G_("History")));
1676     MCHECK(xd->mrec = newmenuitem(G_("Recording"), 0, menurec));
1677     if(recording) check(xd->mrec);
1678     MCHECK(newmenuitem("-", 0, NULL));
1679     MCHECK(xd->madd = newmenuitem(G_("Add\tINS"), 0, menuadd));
1680     MCHECK(xd->mreplace = newmenuitem(G_("Replace"), 0, menureplace));
1681     MCHECK(newmenuitem("-", 0, NULL));
1682     MCHECK(xd->mprev = newmenuitem(G_("Previous\tPgUp"), 0, menuprev));
1683     MCHECK(xd->mnext = newmenuitem(G_("Next\tPgDown"), 0, menunext));
1684     MCHECK(newmenuitem("-", 0, NULL));
1685     MCHECK(xd->msvar = newmenuitem(G_("Save to variable..."), 0, menusvar));
1686     MCHECK(xd->mgvar = newmenuitem(G_("Get from variable..."), 0, menugvar));
1687     MCHECK(newmenuitem("-", 0, NULL));
1688     MCHECK(xd->mclear = newmenuitem(G_("Clear history"), 0, menugrclear));
1689     MCHECK(newmenu(G_("Resize")));
1690     MCHECK(xd->mR = newmenuitem(G_("R mode"), 0, menuR));
1691     if(resize == 1) check(xd->mR);
1692     MCHECK(xd->mfit = newmenuitem(G_("Fit to window"), 0, menufit));
1693     if(resize == 2) check(xd->mfit);
1694     MCHECK(xd->mfix = newmenuitem(G_("Fixed size"), 0, menufix));
1695     if(resize == 3) check(xd->mfix);
1696     newmdimenu();
1697 
1698     /* Normal popup */
1699     MCHECK(xd->grpopup = newpopup(grpopupact));
1700     setdata(xd->grpopup, (void *) dd);
1701     MCHECK(m = newmenuitem(G_("Copy as metafile"), 0, menuclpwm));
1702     setdata(m, (void *) dd);
1703     MCHECK(m = newmenuitem(G_("Copy as bitmap"), 0, menuclpbm));
1704     setdata(m, (void *) dd);
1705     MCHECK(newmenuitem("-", 0, NULL));
1706     MCHECK(m = newmenuitem(G_("Save as metafile..."), 0, menuwm));
1707     setdata(m, (void *) dd);
1708     MCHECK(m = newmenuitem(G_("Save as postscript..."), 0, menups));
1709     setdata(m, (void *) dd);
1710     MCHECK(newmenuitem("-", 0, NULL));
1711     MCHECK(xd->grmenustayontop = newmenuitem(G_("Stay on top"), 0, menustayontop));
1712     setdata(xd->grmenustayontop, (void *) dd);
1713     MCHECK(newmenuitem("-", 0, NULL));
1714     MCHECK(m = newmenuitem(G_("Print..."), 'P', menuprint));
1715     setdata(m, (void *) dd);
1716     gchangepopup(xd->gawin, xd->grpopup);
1717 
1718     MCHECK(xd->bm = newbitmap(getwidth(xd->gawin), getheight(xd->gawin),
1719 			      getdepth(xd->gawin)));
1720     MCHECK(xd->bm2 = newbitmap(getwidth(xd->gawin), getheight(xd->gawin),
1721 			       getdepth(xd->gawin)));
1722     gfillrect(xd->gawin, xd->outcolor, getrect(xd->gawin));
1723     gfillrect(xd->bm, xd->outcolor, getrect(xd->bm));
1724     addto(xd->gawin);
1725     setdata(xd->mbar, (void *) dd);
1726     setdata(xd->mpng, (void *) dd);
1727     setdata(xd->mbmp, (void *) dd);
1728     setdata(xd->mtiff, (void *) dd);
1729     setdata(xd->mjpeg50, (void *) dd);
1730     setdata(xd->mjpeg75, (void *) dd);
1731     setdata(xd->mjpeg100, (void *) dd);
1732     setdata(xd->mps, (void *) dd);
1733     setdata(xd->mpdf, (void *) dd);
1734     setdata(xd->mwm, (void *) dd);
1735     setdata(xd->mclpwm, (void *) dd);
1736     setdata(xd->mclpbm, (void *) dd);
1737     setdata(xd->mprint, (void *) dd);
1738     setdata(xd->mclose, (void *) dd);
1739     setdata(xd->mrec, (void *) dd);
1740     setdata(xd->mprev, (void *) dd);
1741     setdata(xd->mnext, (void *) dd);
1742     setdata(xd->mgvar, (void *) dd);
1743     setdata(xd->madd, (void *) dd);
1744     setdata(xd->mreplace, (void *) dd);
1745     setdata(xd->mR, (void *) dd);
1746     setdata(xd->mfit, (void *) dd);
1747     setdata(xd->mfix, (void *) dd);
1748     if (ismdi() && !(RguiMDI & RW_TOOLBAR)) toolbar_hide();
1749     show(xd->gawin); /* twice, for a Windows bug */
1750     show(xd->gawin);
1751     BringToTop(xd->gawin, 0);
1752     sethit(xd->gawin, devga_sbf);
1753     setresize(xd->gawin, HelpResize);
1754     setredraw(xd->gawin, HelpExpose);
1755     setmousedown(xd->gawin, HelpMouseClick);
1756     setmousemove(xd->gawin, HelpMouseMove);
1757     setmousedrag(xd->gawin, HelpMouseMove);
1758     setmouseup(xd->gawin, HelpMouseUp);
1759     setkeydown(xd->gawin, NHelpKeyIn);
1760     setkeyaction(xd->gawin, CHelpKeyIn);
1761     setclose(xd->gawin, HelpClose);
1762     xd->recording = recording;
1763     xd->replaying = FALSE;
1764     xd->resizing = resize;
1765 
1766     dd->eventHelper = GA_eventHelper;
1767 
1768     dd->canGenMouseDown = TRUE;
1769     dd->canGenMouseMove = TRUE;
1770     dd->canGenMouseUp = TRUE;
1771     dd->canGenKeybd = TRUE;
1772     dd->canGenIdle = FALSE;
1773     dd->gettingEvent = FALSE;
1774 
1775     GA_xd = xd;
1776     return TRUE;
1777 }
1778 
GA_Open(pDevDesc dd,gadesc * xd,const char * dsp,double w,double h,Rboolean recording,int resize,int canvascolor,double gamma,int xpos,int ypos,int bg)1779 static Rboolean GA_Open(pDevDesc dd, gadesc *xd, const char *dsp,
1780 			double w, double h, Rboolean recording,
1781 			int resize, int canvascolor, double gamma,
1782 			int xpos, int ypos, int bg)
1783 {
1784     rect  rr;
1785     char buf[600]; /* allow for pageno formats */
1786 
1787     if (!fontinitdone)
1788 	RFontInit();
1789 
1790     /* Foreground and Background Colors */
1791     xd->bg = dd->startfill = bg;
1792     xd->col = dd->startcol = R_RGB(0, 0, 0);
1793 
1794     xd->fgcolor = Black;
1795     xd->bgcolor = xd->canvascolor = GArgb(canvascolor, gamma);
1796     xd->outcolor = myGetSysColor(COLOR_APPWORKSPACE);
1797     xd->rescale_factor = 1.0;
1798     xd->fast = 1;  /* Use 'cosmetic pens' if available.
1799 		      Overridden for printers and metafiles.
1800 		    */
1801     xd->xshift = xd->yshift = 0;
1802     xd->npage = 0;
1803     xd->fp = NULL; /* not all devices (e.g. TIFF) use the file pointer, but SaveAsBitmap
1804 		      looks at it */
1805 
1806     if (!dsp[0]) {
1807 	if (!setupScreenDevice(dd, xd, w, h, recording, resize, xpos, ypos))
1808 	    return FALSE;
1809 	xd->have_alpha = TRUE;
1810     } else if (!strncmp(dsp, "win.print:", 10)) {
1811 	xd->kind = PRINTER;
1812 	xd->fast = 0; /* use scalable line widths */
1813 	xd->gawin = newprinter(MM_PER_INCH * w, MM_PER_INCH * h, &dsp[10]);
1814 	if (!xd->gawin) {
1815 	    warning("unable to open printer");
1816 	    return FALSE;
1817 	}
1818     } else if (!strncmp(dsp, "png:", 4) || !strncmp(dsp,"bmp:", 4)) {
1819 	xd->res_dpi = (xpos == NA_INTEGER) ? 0 : xpos;
1820 	xd->bg = dd->startfill = canvascolor;
1821 	xd->kind = (dsp[0]=='p') ? PNG : BMP;
1822 	if(strlen(dsp+4) >= 512) error(_("filename too long in %s() call"),
1823 				       (dsp[0]=='p') ? "png" : "bmp");
1824 	strcpy(xd->filename, R_ExpandFileName(dsp+4));
1825 
1826 	if (w < 20 && h < 20)
1827 	    warning(_("'width=%d, height=%d' are unlikely values in pixels"),
1828 		    (int)w, (int) h);
1829 	/*
1830 	  Observe that given actual graphapp implementation 256 is
1831 	  irrelevant,i.e., depth of the bitmap is that of graphic card
1832 	  if required depth > 1
1833 	*/
1834 	if ((xd->gawin = newbitmap(w, h, 256)) == NULL) {
1835 	    warning(_("unable to allocate bitmap"));
1836 	    return FALSE;
1837 	}
1838 	xd->bm = xd->gawin;
1839 	if ((xd->bm2 = newbitmap(w, h, 256)) == NULL) {
1840 	    warning(_("unable to allocate bitmap"));
1841 	    return FALSE;
1842 	}
1843 	snprintf(buf, 600, xd->filename, 1);
1844 	if ((xd->fp = R_fopen(buf, "wb")) == NULL) {
1845 	    del(xd->gawin);
1846 	    warning(_("unable to open file '%s' for writing"), buf);
1847 	    return FALSE;
1848 	}
1849 	xd->have_alpha = TRUE;
1850     } else if (!strncmp(dsp, "jpeg:", 5)) {
1851 	char *p = strchr(&dsp[5], ':');
1852 	xd->res_dpi = (xpos == NA_INTEGER) ? 0 : xpos;
1853 	xd->bg = dd->startfill = canvascolor;
1854 	xd->kind = JPEG;
1855 	if (!p) return FALSE;
1856 	*p = '\0';
1857 	xd->quality = atoi(&dsp[5]);
1858 	*p = ':' ;
1859 	if(strlen(p+1) >= 512) error(_("filename too long in jpeg() call"));
1860 	strcpy(xd->filename, R_ExpandFileName(p+1));
1861 	if (w < 20 && h < 20)
1862 	    warning(_("'width=%d, height=%d' are unlikely values in pixels"),
1863 		    (int)w, (int) h);
1864 	if((xd->gawin = newbitmap(w, h, 256)) == NULL) {
1865 	    warning(_("unable to allocate bitmap"));
1866 	    return FALSE;
1867 	}
1868 	xd->bm = xd->gawin;
1869 	if ((xd->bm2 = newbitmap(w, h, 256)) == NULL) {
1870 	    warning(_("unable to allocate bitmap"));
1871 	    return FALSE;
1872 	}
1873 	snprintf(buf, 600, xd->filename, 1);
1874 	if ((xd->fp = R_fopen(buf, "wb")) == NULL) {
1875 	    del(xd->gawin);
1876 	    warning(_("unable to open file '%s' for writing"), buf);
1877 	    return FALSE;
1878 	}
1879 	xd->have_alpha = TRUE;
1880     } else if (!strncmp(dsp, "tiff:", 5)) {
1881 	char *p = strchr(&dsp[5], ':');
1882 	xd->res_dpi = (xpos == NA_INTEGER) ? 0 : xpos;
1883 	xd->bg = dd->startfill = canvascolor;
1884 	xd->kind = TIFF;
1885 	if (!p) return FALSE;
1886 	*p = '\0';
1887 	xd->quality = atoi(&dsp[5]);
1888 	*p = ':' ;
1889 	if(strlen(p+1) >= 512) error(_("filename too long in tiff() call"));
1890 	strcpy(xd->filename, R_ExpandFileName(p+1));
1891 	if (w < 20 && h < 20)
1892 	    warning(_("'width=%d, height=%d' are unlikely values in pixels"),
1893 		    (int) w, (int) h);
1894 	if((xd->gawin = newbitmap(w, h, 256)) == NULL) {
1895 	    warning(_("unable to allocate bitmap"));
1896 	    return FALSE;
1897 	}
1898 	xd->bm = xd->gawin;
1899 	if ((xd->bm2 = newbitmap(w, h, 256)) == NULL) {
1900 	    warning(_("unable to allocate bitmap"));
1901 	    return FALSE;
1902 	}
1903 	xd->have_alpha = TRUE;
1904     } else {
1905 	/*
1906 	 * win.metafile[:] in memory (for the clipboard)
1907 	 * win.metafile:filename
1908 	 * anything else return FALSE
1909 	 */
1910 	char  *s = "win.metafile";
1911 	int   ls = strlen(s);
1912 	int   ld = strlen(dsp);
1913 
1914 	if (ls > ld)
1915 	    return FALSE;
1916 	if (strncmp(dsp, s, ls) || (dsp[ls] && (dsp[ls] != ':'))) {
1917 	    warning("invalid specification for file name in win.metafile()");
1918 	    return FALSE;
1919 	}
1920 	if(ld > ls && strlen(&dsp[ls + 1]) >= 512)
1921 	    error(_("filename too long in win.metafile() call"));
1922 	strcpy(xd->filename, (ld > ls) ? &dsp[ls + 1] : "");
1923 	snprintf(buf, 600, xd->filename, 1);
1924 	xd->w = MM_PER_INCH * w;
1925 	xd->h =  MM_PER_INCH * h;
1926 	xd->gawin = newmetafile(buf, MM_PER_INCH * w, MM_PER_INCH * h);
1927 	xd->kind = METAFILE;
1928 	xd->fast = 0; /* use scalable line widths */
1929 	if (!xd->gawin) {
1930 	    if(ld > ls)
1931 		warning(_("unable to open metafile '%s' for writing"), buf);
1932 	    else
1933 		warning(_("unable to open clipboard to write metafile"));
1934 	    return FALSE;
1935 	}
1936     }
1937 
1938     if (xd->kind <= METAFILE)
1939 	xd->lwdscale = devicepixelsy(xd->gawin)/96.0; /* matches ps/pdf */
1940     else if (xd->res_dpi > 0)
1941 	xd->lwdscale = xd->res_dpi/96.0;
1942     else
1943 	xd->lwdscale = 72.0/96.0;
1944     if(xd->lwdscale < 1.0) xd->lwdscale = 1.0; /* at least one pixel */
1945     rr = getrect(xd->gawin);
1946     xd->origWidth = xd->showWidth = xd->windowWidth = rr.width;
1947     xd->origHeight = xd->showHeight = xd->windowHeight = rr.height;
1948     xd->clip = rr;
1949     setdata(xd->gawin, (void *) dd);
1950     xd->needsave = FALSE;
1951     return TRUE;
1952 }
1953 
1954 	/********************************************************/
1955 	/* device_StrWidth should return the width of the given */
1956 	/* string in DEVICE units (GStrWidth is responsible for */
1957 	/* converting from DEVICE to whatever units the user	*/
1958 	/* asked for						*/
1959 	/********************************************************/
1960 
GA_StrWidth(const char * str,const pGEcontext gc,pDevDesc dd)1961 static double GA_StrWidth(const char *str,
1962 			  const pGEcontext gc,
1963 			  pDevDesc dd)
1964 {
1965     gadesc *xd = (gadesc *) dd->deviceSpecific;
1966 
1967     SetFont(gc, 0.0, xd);
1968     return (double) gstrwidth1(xd->gawin, xd->font, str, CE_NATIVE);
1969 }
1970 
GA_StrWidth_UTF8(const char * str,const pGEcontext gc,pDevDesc dd)1971 static double GA_StrWidth_UTF8(const char *str,
1972 			      const pGEcontext gc,
1973 			      pDevDesc dd)
1974 {
1975     gadesc *xd = (gadesc *) dd->deviceSpecific;
1976     double a;
1977 
1978     /* This should never be called for symbol fonts */
1979     SetFont(gc, 0.0, xd);
1980     if(gc->fontface != 5)
1981 	a = (double) gstrwidth1(xd->gawin, xd->font, str, CE_UTF8);
1982     else
1983 	a = (double) gstrwidth1(xd->gawin, xd->font, str, CE_SYMBOL);
1984     return a;
1985 }
1986 
1987 	/********************************************************/
1988 	/* device_MetricInfo should return height, depth, and	*/
1989 	/* width information for the given character in DEVICE	*/
1990 	/* units (GMetricInfo does the necessary conversions)	*/
1991 	/* This is used for formatting mathematical expressions	*/
1992 	/********************************************************/
1993 
1994 	/* Character Metric Information */
1995 	/* Passing c == 0 gets font information.
1996 	   In a mbcslocale for a non-symbol font
1997 	   we pass a Unicode point, otherwise an 8-bit char, and
1998 	   we don't care which for a 7-bit char.
1999 	 */
2000 
GA_MetricInfo(int c,const pGEcontext gc,double * ascent,double * descent,double * width,pDevDesc dd)2001 static void GA_MetricInfo(int c,
2002 			  const pGEcontext gc,
2003 			  double* ascent, double* descent,
2004 			  double* width, pDevDesc dd)
2005 {
2006     int   a, d, w;
2007     gadesc *xd = (gadesc *) dd->deviceSpecific;
2008     Rboolean Unicode = mbcslocale;
2009 
2010     if (c < 0) { Unicode = TRUE; c = -c; }
2011     SetFont(gc, 0.0, xd);
2012     if(Unicode && gc->fontface != 5 && c > 127)
2013 	gwcharmetric(xd->gawin, xd->font, c, &a, &d, &w);
2014     else
2015 	gcharmetric(xd->gawin, xd->font, c, &a, &d, &w);
2016     /* Some Windows systems report that space has height and depth,
2017        so we have a kludge.  Note that 32 is space in symbol font too */
2018     if(c == 32) {
2019 	*ascent  = 0.0;
2020 	*descent = 0.0;
2021     } else {
2022 	*ascent  = (double) a;
2023 	*descent = (double) d;
2024     }
2025     *width   = (double) w;
2026 }
2027 
2028 	/********************************************************/
2029 	/* device_Clip is given the left, right, bottom, and	*/
2030 	/* top of a rectangle (in DEVICE coordinates).  it	*/
2031 	/* should have the side-effect that subsequent output	*/
2032 	/* is clipped to the given rectangle			*/
2033 	/********************************************************/
2034 
GA_Clip(double x0,double x1,double y0,double y1,pDevDesc dd)2035 static void GA_Clip(double x0, double x1, double y0, double y1, pDevDesc dd)
2036 {
2037     gadesc *xd = (gadesc *) dd->deviceSpecific;
2038     rect r;
2039 
2040     r = rcanon(rpt(pt(x0, y0), pt(x1, y1)));
2041     r.width  += 1;
2042     r.height += 1;
2043     xd->clip = r;
2044 }
2045 
2046 	/********************************************************/
2047 	/* device_Resize is called whenever the device is	*/
2048 	/* resized.  the function must update the GPar		*/
2049 	/* parameters (left, right, bottom, and top) for the	*/
2050 	/* new device size					*/
2051 	/* this is not usually called directly by the graphics	*/
2052 	/* engine because the detection of device resizes	*/
2053 	/* (e.g., a window resize) are usually detected by	*/
2054 	/* device-specific code	(see R_ProcessEvents)           */
2055 	/********************************************************/
2056 
GA_Size(double * left,double * right,double * bottom,double * top,pDevDesc dd)2057 static void GA_Size(double *left, double *right,
2058 		    double *bottom, double *top,
2059 		    pDevDesc dd)
2060 {
2061     *left = dd->left;
2062     *top = dd->top;
2063     /* There's a mysterious -0.0001 in the setting */
2064     *right = ceil(dd->right);
2065     *bottom = ceil(dd->bottom);
2066 }
2067 
GA_Resize(pDevDesc dd)2068 static void GA_Resize(pDevDesc dd)
2069 {
2070     gadesc *xd = (gadesc *) dd->deviceSpecific;
2071 
2072     if (xd->resize) {
2073 	int   iw, ih, iw0 = dd->right - dd->left,
2074 	    ih0 = dd->bottom - dd->top;
2075 	double fw, fh, rf, shift;
2076 
2077 	iw = xd->windowWidth;
2078 	ih = xd->windowHeight;
2079 	if(xd->resizing == 1) {
2080 	    /* last mode might have been 3, so remove scrollbars */
2081 	    gchangescrollbar(xd->gawin, VWINSB, 0, ih/SF-1, ih/SF, 0);
2082 	    gchangescrollbar(xd->gawin, HWINSB, 0, iw/SF-1, iw/SF, 0);
2083 	    dd->left = 0.0;
2084 	    dd->top = 0.0;
2085 	    dd->right = iw;
2086 	    dd->bottom = ih;
2087 	    xd->showWidth = iw;
2088 	    xd->showHeight =  ih;
2089 	} else if (xd->resizing == 2) {
2090 	    /* last mode might have been 3, so remove scrollbars */
2091 	    gchangescrollbar(xd->gawin, VWINSB, 0, ih/SF-1, ih/SF, 0);
2092 	    gchangescrollbar(xd->gawin, HWINSB, 0, iw/SF-1, iw/SF, 0);
2093 	    fw = (iw + 0.5)/(iw0 + 0.5);
2094 	    fh = (ih + 0.5)/(ih0 + 0.5);
2095 	    rf = min(fw, fh);
2096 	    xd->rescale_factor *= rf;
2097 	    {
2098 		SEXP scale;
2099 		PROTECT(scale = ScalarReal(rf));
2100 		GEhandleEvent(GE_ScalePS, dd, scale);
2101 		UNPROTECT(1);
2102 	    }
2103 	    if (fw < fh) {
2104 		dd->left = 0.0;
2105 		xd->showWidth = dd->right = iw;
2106 		xd->showHeight =  ih0*fw;
2107 		shift = (ih - xd->showHeight)/2.0;
2108 		dd->top = shift;
2109 		dd->bottom = ih0*fw + shift;
2110 		xd->xshift = 0; xd->yshift = shift;
2111 	    } else {
2112 		dd->top = 0.0;
2113 		xd->showHeight = dd->bottom = ih;
2114 		xd->showWidth = iw0*fh;
2115 		shift = (iw - xd->showWidth)/2.0;
2116 		dd->left = shift;
2117 		dd->right = iw0*fh + shift;
2118 		xd->xshift = shift; xd->yshift = 0;
2119 	    }
2120 	    xd->clip = getregion(xd);
2121 	} else if (xd->resizing == 3) {
2122 	    if(iw0 < iw) shift = (iw - iw0)/2.0;
2123 	    else shift = min(0, xd->xshift);
2124 	    dd->left = shift;
2125 	    dd->right = iw0 + shift;
2126 	    xd->xshift = shift;
2127 	    gchangescrollbar(xd->gawin, HWINSB, max(-shift,0)/SF,
2128 			     xd->origWidth/SF - 1, xd->windowWidth/SF, 0);
2129 	    if(ih0 < ih) shift = (ih - ih0)/2.0;
2130 	    else shift = min(0, xd->yshift);
2131 	    dd->top = shift;
2132 	    dd->bottom = ih0 + shift;
2133 	    xd->yshift = shift;
2134 	    gchangescrollbar(xd->gawin, VWINSB, max(-shift,0)/SF,
2135 			     xd->origHeight/SF - 1, xd->windowHeight/SF, 0);
2136 	    xd->showWidth = xd->origWidth + min(0, xd->xshift);
2137 	    xd->showHeight = xd->origHeight + min(0,  xd->yshift);
2138 	}
2139 	xd->resize = FALSE;
2140 	if (xd->kind == SCREEN) {
2141 	    del(xd->bm);
2142 	    xd->bm = newbitmap(iw, ih, getdepth(xd->gawin));
2143 	    if (!xd->bm) {
2144 		R_ShowMessage(_("Insufficient memory for resize. Killing device"));
2145 		killDevice(ndevNumber(dd));
2146 		return; /* since the device is killed */
2147 	    }
2148 	    if(xd->have_alpha) {
2149 		del(xd->bm2);
2150 		xd->bm2 = newbitmap(iw, ih, getdepth(xd->gawin));
2151 		if (!xd->bm2) {
2152 		    R_ShowMessage(_("Insufficient memory for resize. Disabling alpha blending"));
2153 		    xd->have_alpha = FALSE;
2154 		}
2155 	    }
2156 
2157 	    gfillrect(xd->gawin, xd->outcolor, getrect(xd->gawin));
2158 	    gfillrect(xd->bm, xd->outcolor, getrect(xd->bm));
2159 	}
2160     }
2161 }
2162 
2163 	/********************************************************/
2164 	/* device_NewPage is called whenever a new plot requires*/
2165 	/* a new page.  a new page might mean just clearing the	*/
2166 	/* device (as in this case) or moving to a new page	*/
2167 	/* (e.g., postscript)					*/
2168 	/********************************************************/
2169 
GA_NewPage(const pGEcontext gc,pDevDesc dd)2170 static void GA_NewPage(const pGEcontext gc,
2171 		       pDevDesc dd)
2172 {
2173     gadesc *xd = (gadesc *) dd->deviceSpecific;
2174 
2175     xd->npage++;
2176     if ((xd->kind == PRINTER) && xd->needsave)
2177 	nextpage(xd->gawin);
2178     if ((xd->kind == METAFILE) && xd->needsave) {
2179 	char buf[600];
2180 	if (strlen(xd->filename) == 0)
2181 	    error(_("a clipboard metafile can store only one figure."));
2182 	else {
2183 	    del(xd->gawin);
2184 	    snprintf(buf, 600, xd->filename, xd->npage);
2185 	    xd->gawin = newmetafile(buf, xd->w, xd->h);
2186 	    if(!xd->gawin)
2187 		error(_("metafile '%s' could not be created"), buf);
2188 	}
2189     }
2190     if ((xd->kind == PNG || xd->kind == JPEG || xd->kind == BMP)
2191 	&& xd->needsave) {
2192 	char buf[600];
2193 	SaveAsBitmap(dd, xd->res_dpi);
2194 	snprintf(buf, 600, xd->filename, xd->npage);
2195 	if ((xd->fp = R_fopen(buf, "wb")) == NULL)
2196 	    error(_("unable to open file '%s' for writing"), buf);
2197     }
2198     if (xd->kind == TIFF && xd->needsave) {
2199 	SaveAsBitmap(dd, xd->res_dpi);
2200     }
2201     if (xd->kind == SCREEN) {
2202 	if(xd->buffered && !xd->holdlevel) SHOW;
2203 	if (xd->recording && xd->needsave)
2204 	    AddtoPlotHistory(desc2GEDesc(dd)->savedSnapshot, 0);
2205 	if (xd->replaying)
2206 	    xd->needsave = FALSE;
2207 	else
2208 	    xd->needsave = TRUE;
2209     }
2210     xd->bg = gc->fill;
2211     xd->warn_trans = FALSE;
2212     {
2213 	unsigned int alpha = R_ALPHA(xd->bg);
2214 	if(alpha  == 0) xd->bgcolor = xd->canvascolor;
2215 	else {
2216 	    xd->bgcolor = GArgb(xd->bg, gc->gamma);
2217 	    if(alpha < 255)
2218 		xd->bgcolor = (alpha * xd->bgcolor +
2219 			       (255-alpha) * xd->canvascolor)/255;
2220 	}
2221     }
2222     if (xd->kind != SCREEN) {
2223 	xd->needsave = TRUE;
2224 	xd->clip = getrect(xd->gawin);
2225 	if(R_OPAQUE(xd->bg) || xd->kind == BMP || xd->kind == JPEG
2226 	   || xd->kind == TIFF) {
2227 	    DRAW(gfillrect(_d, xd->bgcolor, xd->clip));
2228 	} else if(xd->kind == PNG) {
2229 	    DRAW(gfillrect(_d, PNG_TRANS, xd->clip));
2230 	}
2231 	if(xd->kind == PNG)
2232 	    xd->pngtrans = ggetpixel(xd->gawin, pt(0,0)) | 0xff000000;
2233     } else {
2234 	xd->clip = getregion(xd);
2235 	DRAW(gfillrect(_d, xd->bgcolor, xd->clip));
2236     }
2237     SH;
2238 }
2239 
deleteGraphMenus(int devnum)2240 static void deleteGraphMenus(int devnum)
2241 {
2242     char prefix[15];
2243 
2244     snprintf(prefix, 15, "$Graph%i", devnum);
2245     windelmenus(prefix);
2246 }
2247 
2248 	/********************************************************/
2249 	/* device_Close is called when the device is killed	*/
2250 	/* this function is responsible for destroying any	*/
2251 	/* device-specific resources that were created in	*/
2252 	/* device_Open and for FREEing the device-specific	*/
2253 	/* parameters structure					*/
2254 	/********************************************************/
2255 
GA_Close(pDevDesc dd)2256 static void GA_Close(pDevDesc dd)
2257 {
2258     gadesc *xd = (gadesc *) dd->deviceSpecific;
2259     SEXP vDL;
2260 
2261     if (xd->cntxt)
2262     	((RCNTXT *)xd->cntxt)->cend = NULL;  /* Don't try to run cleanup; it will have already happened */
2263 
2264     if (dd->onExit) {
2265 	dd->onExit(dd);
2266     }
2267 
2268     if (xd->kind == SCREEN) {
2269 	if(xd->recording) {
2270 	    AddtoPlotHistory(GEcreateSnapshot(desc2GEDesc(dd)), 0);
2271 	    /* May have changed vDL, so can't use GETDL above */
2272 	    vDL = findVar(install(".SavedPlots"), R_GlobalEnv);
2273 	    pCURRENTPOS++; /* so PgUp goes to the last saved plot
2274 			      when a windows() device is opened */
2275 	}
2276 	hide(xd->gawin);
2277 
2278 	del(xd->bm);
2279 	/* If this is the active device and buffered, shut updates off */
2280 	if (xd == GA_xd) GA_xd = NULL;
2281 	deleteGraphMenus(ndevNumber(dd) + 1);
2282 
2283     } else if ((xd->kind == PNG) || (xd->kind == JPEG)
2284 	       || (xd->kind == BMP) || (xd->kind == TIFF)) {
2285 	if (xd->kind == TIFF) xd->npage++;
2286 	SaveAsBitmap(dd, xd->res_dpi);
2287     }
2288     del(xd->font);
2289     if(xd->bm2) del(xd->bm2);
2290     del(xd->gawin);
2291 /*
2292  * this is needed since the GraphApp delayed clean-up
2293  * ,i.e, I want free all resources NOW
2294  */
2295     /* I think the concern is rather to run all pending events on the
2296        device (but also on the console and others) */
2297     doevent();
2298     free(xd);
2299     dd->deviceSpecific = NULL;
2300 }
2301 
2302 	/********************************************************/
2303 	/* device_Activate is called when a device becomes the	*/
2304 	/* active device.  in this case it is used to change the*/
2305 	/* title of a window to indicate the active status of	*/
2306 	/* the device to the user.  not all device types will	*/
2307 	/* do anything						*/
2308 	/********************************************************/
2309 
GA_Activate(pDevDesc dd)2310 static void GA_Activate(pDevDesc dd)
2311 {
2312     char  t[150];
2313     gadesc *xd = (gadesc *) dd->deviceSpecific;
2314 
2315     if (xd->replaying || (xd->kind!=SCREEN))
2316 	return;
2317     if(strlen(xd->title)) {
2318 	snprintf(t, 140, xd->title, ndevNumber(dd) + 1);
2319 	t[139] = '\0';
2320     } else {
2321 	snprintf(t, 150, "R Graphics: Device %d", ndevNumber(dd) + 1);
2322     }
2323     strcat(t, " (ACTIVE)");
2324     settext(xd->gawin, t);
2325     if (xd != GA_xd)
2326     	drawbits(GA_xd);
2327     GA_xd = xd;
2328 }
2329 
2330 	/********************************************************/
2331 	/* device_Deactivate is called when a device becomes	*/
2332 	/* inactive.  in this case it is used to change the	*/
2333 	/* title of a window to indicate the inactive status of */
2334 	/* the device to the user.  not all device types will	*/
2335 	/* do anything						*/
2336 	/********************************************************/
2337 
GA_Deactivate(pDevDesc dd)2338 static void GA_Deactivate(pDevDesc dd)
2339 {
2340     char  t[150];
2341     gadesc *xd = (gadesc *) dd->deviceSpecific;
2342 
2343     if (xd->replaying || (xd->kind != SCREEN))
2344 	return;
2345     if(strlen(xd->title)) {
2346 	snprintf(t, 140, xd->title, ndevNumber(dd) + 1);
2347 	t[139] = '\0';
2348     } else {
2349 	snprintf(t, 150, "R Graphics: Device %d", ndevNumber(dd) + 1);
2350     }
2351     strcat(t, " (inactive)");
2352     settext(xd->gawin, t);
2353 }
2354 
2355 #define WARN_SEMI_TRANS { \
2356 	    if(!xd->warn_trans) warning(_("semi-transparency is not supported on this device: reported only once per page")); \
2357 	    xd->warn_trans = TRUE; \
2358 	}
2359 
2360 #define DRAW2(col) {if(xd->kind != SCREEN) gcopyalpha(xd->gawin,xd->bm2,r,R_ALPHA(col)); else {gcopyalpha(xd->bm,xd->bm2,r,R_ALPHA(col)); if(!xd->buffered) drawbits(xd);}}
2361 
2362 
2363 
2364 	/********************************************************/
2365 	/* device_Rect should have the side-effect that a	*/
2366 	/* rectangle is drawn with the given locations for its	*/
2367 	/* opposite corners.  the border of the rectangle	*/
2368 	/* should be in the given "fg" colour and the rectangle	*/
2369 	/* should be filled with the given "bg" colour		*/
2370 	/* if "fg" is NA_INTEGER then no border should be drawn */
2371 	/* if "bg" is NA_INTEGER then the rectangle should not	*/
2372 	/* be filled						*/
2373 	/* the locations are in an arbitrary coordinate system	*/
2374 	/* and this function is responsible for converting the	*/
2375 	/* locations to DEVICE coordinates using GConvert	*/
2376 	/********************************************************/
2377 
GA_Rect(double x0,double y0,double x1,double y1,const pGEcontext gc,pDevDesc dd)2378 static void GA_Rect(double x0, double y0, double x1, double y1,
2379 		    const pGEcontext gc,
2380 		    pDevDesc dd)
2381 {
2382     int   tmp;
2383     gadesc *xd = (gadesc *) dd->deviceSpecific;
2384     rect  r, rr;
2385 
2386     /* These in-place conversions are ok */
2387     TRACEDEVGA("rect");
2388 
2389     if (x0 > x1) {
2390 	tmp = x0;
2391 	x0 = x1;
2392 	x1 = tmp;
2393     }
2394     if (y0 > y1) {
2395 	tmp = y0;
2396 	y0 = y1;
2397 	y1 = tmp;
2398     }
2399     /* zero width or height disappears, so handle that case specially in case it's just rounding */
2400     if ((int)x0 == (int)x1 && x1-x0 >= 0.5) {
2401     	x1 = (int)x1;
2402     	x0 = x1 - 1.0;
2403     }
2404     if ((int)y0 == (int)y1 && y1-y0 >= 0.5) {
2405     	y1 = (int)y1;
2406     	y0 = y1 - 1.0;
2407     }
2408     r = rect((int) x0, (int) y0, (int)x1 - (int)x0, (int)y1 - (int)y0);
2409 
2410     SetColor(gc->fill, gc->gamma, xd);
2411     if (R_OPAQUE(gc->fill)) {
2412 	DRAW(gfillrect(_d, xd->fgcolor, r));
2413     } else if(R_ALPHA(gc->fill) > 0) {
2414 	if(xd->have_alpha) {
2415 	    rect cp = xd->clip;
2416 	    /* We are only working with the screen device here, so
2417 	       we can assume that x->bm is the current state.
2418 	       Copying from the screen window does not work. */
2419 	    /* Clip to the device region */
2420 	    rr = r;
2421 	    if (r.x < 0) {r.x = 0; r.width = r.width + rr.x;}
2422 	    if (r.y < 0) {r.y = 0; r.height = r.height + rr.y;}
2423 	    if (r.x + r.width > cp.x + cp.width)
2424 		r.width = cp.x + cp.width - r.x;
2425 	    if (r.y + r.height > cp.y + cp.height)
2426 		r.height = cp.y + cp.height - r.y;
2427 	    gsetcliprect(xd->bm, xd->clip);
2428 	    gcopy(xd->bm2, xd->bm, r);
2429 	    gfillrect(xd->bm2, xd->fgcolor, rr);
2430 	    DRAW2(gc->fill);
2431 	    r = rr;
2432 	} else WARN_SEMI_TRANS;
2433     }
2434 
2435     SetColor(gc->col, gc->gamma, xd);
2436     SetLineStyle(gc, dd);
2437     if (R_OPAQUE(gc->col)) {
2438 	DRAW(gdrawrect(_d, xd->lwd, xd->lty, xd->fgcolor, r, 0, xd->lend,
2439 		       xd->ljoin, xd->lmitre));
2440     } else if(R_ALPHA(gc->col) > 0) {
2441 	if(xd->have_alpha) {
2442 	    int adj, tol = xd->lwd; /* only half needed */
2443 	    rect cp = xd->clip;
2444 	    rr = r;
2445 	    r.x -= tol; r.y -= tol; r.width += 2*tol; r.height += 2*tol;
2446 	    if (r.x < 0) {adj = r.x; r.x = 0; r.width = r.width + adj;}
2447 	    if (r.y < 0) {adj = r.y; r.y = 0; r.height = r.height + adj;}
2448 	    if (r.x + r.width > cp.x + cp.width)
2449 		r.width = cp.x + cp.width - r.x;
2450 	    if (r.y + r.height > cp.y + cp.height)
2451 		r.height = cp.y + cp.height - r.y;
2452 	    gsetcliprect(xd->bm, xd->clip);
2453 	    gcopy(xd->bm2, xd->bm, r);
2454 	    gdrawrect(xd->bm2, xd->lwd, xd->lty, xd->fgcolor, rr, 0, xd->lend,
2455 		      xd->ljoin, xd->lmitre);
2456 	    DRAW2(gc->col);
2457 	} else WARN_SEMI_TRANS;
2458     }
2459     SH;
2460 }
2461 
2462 	/********************************************************/
2463 	/* device_Circle should have the side-effect that a	*/
2464 	/* circle is drawn, centred at the given location, with */
2465 	/* the given radius.  the border of the circle should be*/
2466 	/* drawn in the given "col", and the circle should be	*/
2467 	/* filled with the given "border" colour.		*/
2468 	/* if "col" is NA_INTEGER then no border should be drawn*/
2469 	/* if "border" is NA_INTEGER then the circle should not */
2470 	/* be filled						*/
2471 	/* the location is in arbitrary coordinates and the	*/
2472 	/* function is responsible for converting this to	*/
2473 	/* DEVICE coordinates.  the radius is given in DEVICE	*/
2474 	/* coordinates						*/
2475 	/********************************************************/
2476 
GA_Circle(double x,double y,double radius,const pGEcontext gc,pDevDesc dd)2477 static void GA_Circle(double x, double y, double radius,
2478 		      const pGEcontext gc,
2479 		      pDevDesc dd)
2480 {
2481     int   id, ix, iy;
2482     gadesc *xd = (gadesc *) dd->deviceSpecific;
2483     rect  r, rr;
2484 
2485     TRACEDEVGA("circle");
2486     id = 2*radius + 0.5;
2487     if (id < 2) id = 2; /* diameter 1 is near-invisible */
2488 
2489     ix = (int) x;
2490     iy = (int) y;
2491     r = rr = rect(ix - id/2, iy - id/2, id, id);
2492 
2493     SetColor(gc->fill, gc->gamma, xd);
2494     if (R_OPAQUE(gc->fill)) {
2495 	DRAW(gfillellipse(_d, xd->fgcolor, rr));
2496     } else if(R_ALPHA(gc->fill) > 0) {
2497 	if (xd->have_alpha) {
2498 	    rect cp = xd->clip;
2499 	    /* Clip to the device region */
2500 	    if (r.x < 0) {r.x = 0; r.width = r.width + rr.x;}
2501 	    if (r.y < 0) {r.y = 0; r.height = r.height + rr.y;}
2502 	    if (r.x + r.width > cp.x + cp.width)
2503 		r.width = cp.x + cp.width - r.x;
2504 	    if (r.y + r.height > cp.y + cp.height)
2505 		r.height = cp.y + cp.height - r.y;
2506 	    gsetcliprect(xd->bm, xd->clip);
2507 	    gcopy(xd->bm2, xd->bm, r);
2508 	    gfillellipse(xd->bm2, xd->fgcolor, rr);
2509 	    DRAW2(gc->fill);
2510 	    r = rr;
2511 	} else WARN_SEMI_TRANS;
2512     }
2513 
2514     SetColor(gc->col, gc->gamma, xd);
2515     SetLineStyle(gc, dd);
2516     if (R_OPAQUE(gc->col)) {
2517 	DRAW(gdrawellipse(_d, xd->lwd, xd->fgcolor, rr, 0, xd->lend,
2518 			  xd->ljoin, xd->lmitre));
2519     } else if(R_ALPHA(gc->col) > 0) {
2520 	if(xd->have_alpha) {
2521 	    int adj, tol = xd->lwd; /* only half needed */
2522 	    rect cp = xd->clip;
2523 	    r.x -= tol; r.y -= tol; r.width += 2*tol; r.height += 2*tol;
2524 	    if (r.x < 0) {adj = r.x; r.x = 0; r.width = r.width + adj;}
2525 	    if (r.y < 0) {adj = r.y; r.y = 0; r.height = r.height + adj;}
2526 	    if (r.x + r.width > cp.x + cp.width)
2527 		r.width = cp.x + cp.width - r.x;
2528 	    if (r.y + r.height > cp.y + cp.height)
2529 		r.height = cp.y + cp.height - r.y;
2530 	    gsetcliprect(xd->bm, xd->clip);
2531 	    gcopy(xd->bm2, xd->bm, r);
2532 	    gdrawellipse(xd->bm2, xd->lwd, xd->fgcolor, rr, 0, xd->lend,
2533 			 xd->ljoin, xd->lmitre);
2534 	    DRAW2(gc->col);
2535 	} else WARN_SEMI_TRANS;
2536     }
2537     SH;
2538 }
2539 
2540 	/********************************************************/
2541 	/* device_Line should have the side-effect that a single*/
2542 	/* line is drawn (from x1,y1 to x2,y2)			*/
2543 	/* x1, y1, x2, and y2 are in arbitrary coordinates and	*/
2544 	/* the function is responsible for converting them to	*/
2545 	/* DEVICE coordinates using GConvert			*/
2546 	/********************************************************/
2547 
GA_Line(double x1,double y1,double x2,double y2,const pGEcontext gc,pDevDesc dd)2548 static void GA_Line(double x1, double y1, double x2, double y2,
2549 		    const pGEcontext gc,
2550 		    pDevDesc dd)
2551 {
2552     int   xx1, yy1, xx2, yy2;
2553     gadesc *xd = (gadesc *) dd->deviceSpecific;
2554 
2555     /* In-place conversion ok */
2556     TRACEDEVGA("line");
2557     xx1 = (int) x1;
2558     yy1 = (int) y1;
2559     xx2 = (int) x2;
2560     yy2 = (int) y2;
2561 
2562     SetColor(gc->col, gc->gamma, xd);
2563     SetLineStyle(gc, dd);
2564     if (R_OPAQUE(gc->col)) {
2565 	DRAW(gdrawline(_d, xd->lwd, xd->lty, xd->fgcolor,
2566 		       pt(xx1, yy1), pt(xx2, yy2), 0, xd->lend,
2567 			  xd->ljoin, xd->lmitre));
2568 	SH;
2569     } else if(R_ALPHA(gc->col) > 0) {
2570 	if(xd->have_alpha) {
2571 	    rect r = xd->clip;
2572 	    gsetcliprect(xd->bm, xd->clip);
2573 	    gcopy(xd->bm2, xd->bm, r);
2574 	    gdrawline(xd->bm2, xd->lwd, xd->lty, xd->fgcolor,
2575 		      pt(xx1, yy1), pt(xx2, yy2), 0, xd->lend,
2576 		      xd->ljoin, xd->lmitre);
2577 	    DRAW2(gc->col);
2578 	    SH;
2579 	} else WARN_SEMI_TRANS;
2580     }
2581 }
2582 
2583 	/********************************************************/
2584 	/* device_Polyline should have the side-effect that a	*/
2585 	/* series of line segments are drawn using the given x	*/
2586 	/* and y values						*/
2587 	/* the x and y values are in arbitrary coordinates and	*/
2588 	/* the function is responsible for converting them to	*/
2589 	/* DEVICE coordinates using GConvert			*/
2590 	/********************************************************/
2591 
GA_Polyline(int n,double * x,double * y,const pGEcontext gc,pDevDesc dd)2592 static void GA_Polyline(int n, double *x, double *y,
2593 			const pGEcontext gc,
2594 			pDevDesc dd)
2595 {
2596     const void *vmax = vmaxget();
2597     point *p = (point *) R_alloc(n, sizeof(point));
2598     double devx, devy;
2599     int   i;
2600     gadesc *xd = (gadesc *) dd->deviceSpecific;
2601 
2602     TRACEDEVGA("pl");
2603     for (i = 0; i < n; i++) {
2604 	devx = x[i];
2605 	devy = y[i];
2606 	p[i].x = (int) devx;
2607 	p[i].y = (int) devy;
2608     }
2609 
2610     SetColor(gc->col, gc->gamma, xd);
2611     SetLineStyle(gc, dd);
2612     if (R_OPAQUE(gc->col)) {
2613 	DRAW(gdrawpolyline(_d, xd->lwd, xd->lty, xd->fgcolor, p, n, 0, 0,
2614 			   xd->lend, xd->ljoin, xd->lmitre));
2615     } else if(R_ALPHA(gc->col) > 0) {
2616 	if(xd->have_alpha) {
2617 	    rect r = xd->clip; /* lines can go well outside bbox of points */
2618 	    gsetcliprect(xd->bm, xd->clip);
2619 	    gcopy(xd->bm2, xd->bm, r);
2620 	    gdrawpolyline(xd->bm2, xd->lwd, xd->lty, xd->fgcolor, p, n, 0, 0,
2621 			  xd->lend, xd->ljoin, xd->lmitre);
2622 	    DRAW2(gc->col);
2623 	} else WARN_SEMI_TRANS;
2624     }
2625     vmaxset(vmax);
2626     SH;
2627 }
2628 
2629 	/********************************************************/
2630 	/* device_Polygon should have the side-effect that a	*/
2631 	/* polygon is drawn using the given x and y values	*/
2632 	/* the polygon border should be drawn in the "fg"	*/
2633 	/* colour and filled with the "bg" colour		*/
2634 	/* if "fg" is NA_INTEGER don't draw the border		*/
2635 	/* if "bg" is NA_INTEGER don't fill the polygon		*/
2636 	/* the x and y values are in arbitrary coordinates and	*/
2637 	/* the function is responsible for converting them to	*/
2638 	/* DEVICE coordinates using GConvert			*/
2639 	/********************************************************/
2640 
GA_Polygon(int n,double * x,double * y,const pGEcontext gc,pDevDesc dd)2641 static void GA_Polygon(int n, double *x, double *y,
2642 		       const pGEcontext gc,
2643 		       pDevDesc dd)
2644 {
2645     const void *vmax = vmaxget();
2646     point *points;
2647     rect r;
2648     double devx, devy;
2649     int   i, mx0 = 0, mx1 = 0, my0 = 0, my1 = 0;
2650     gadesc *xd = (gadesc *) dd->deviceSpecific;
2651 
2652     TRACEDEVGA("plg");
2653     points = (point *) R_alloc(n , sizeof(point));
2654     if (!points)
2655 	return;
2656     for (i = 0; i < n; i++) {
2657 	devx = x[i];
2658 	devy = y[i];
2659 	points[i].x = (int) (devx);
2660 	points[i].y = (int) (devy);
2661 	mx0 = imin2(mx0, points[i].x);
2662 	mx1 = imax2(mx1, points[i].x);
2663 	my0 = imin2(my0, points[i].y);
2664 	my1 = imax2(my1, points[i].y);
2665     }
2666     r.x = mx0; r.width = mx1 - mx0;
2667     r.y = my0; r.height = my1 - my0;
2668 
2669     if (xd->doSetPolyFill && xd->fillOddEven == FALSE) {
2670 	DRAW(gsetpolyfillmode(_d, 0));
2671 	xd->doSetPolyFill = FALSE;  /* Only set it once */
2672     }
2673 
2674     SetColor(gc->fill, gc->gamma, xd);
2675     if (R_OPAQUE(gc->fill)) {
2676 	DRAW(gfillpolygon(_d, xd->fgcolor, points, n));
2677     } else if(R_ALPHA(gc->fill) > 0) {
2678 	if(xd->have_alpha) {
2679 	    gsetcliprect(xd->bm, xd->clip);
2680 	    gcopy(xd->bm2, xd->bm, r);
2681 	    gfillpolygon(xd->bm2, xd->fgcolor, points, n);
2682 	    DRAW2(gc->fill);
2683 	} else WARN_SEMI_TRANS;
2684     }
2685 
2686     SetColor(gc->col, gc->gamma, xd);
2687     SetLineStyle(gc, dd);
2688     if (R_OPAQUE(gc->col)) {
2689 	DRAW(gdrawpolygon(_d, xd->lwd, xd->lty, xd->fgcolor, points, n, 0,
2690 			  xd->lend, xd->ljoin, xd->lmitre));
2691     } else if(R_ALPHA(gc->col) > 0) {
2692 	if(xd->have_alpha) {
2693 	    r = xd->clip;
2694 	    gsetcliprect(xd->bm, xd->clip);
2695 	    gcopy(xd->bm2, xd->bm, r);
2696 	    gdrawpolygon(xd->bm2, xd->lwd, xd->lty, xd->fgcolor, points, n, 0,
2697 			 xd->lend, xd->ljoin, xd->lmitre);
2698 	    DRAW2(gc->col);
2699 	} else WARN_SEMI_TRANS;
2700     }
2701     vmaxset(vmax);
2702     SH;
2703 }
2704 
GA_Path(double * x,double * y,int npoly,int * nper,Rboolean winding,const pGEcontext gc,pDevDesc dd)2705 static void GA_Path(double *x, double *y,
2706                     int npoly, int *nper,
2707                     Rboolean winding,
2708                     const pGEcontext gc,
2709                     pDevDesc dd)
2710 {
2711     const void *vmax = vmaxget();
2712     point *points;
2713     point *pointIndex;
2714     rect r;
2715     double devx, devy;
2716     int   i, mx0 = 0, mx1 = 0, my0 = 0, my1 = 0;
2717     gadesc *xd = (gadesc *) dd->deviceSpecific;
2718 
2719     int ntot = 0;
2720     for (i=0; i < npoly; i++) {
2721         ntot = ntot + nper[i];
2722     }
2723 
2724     TRACEDEVGA("path");
2725     points = (point *) R_alloc(ntot, sizeof(point));
2726     if (!points)
2727 	return;
2728     for (i = 0; i < ntot; i++) {
2729 	devx = x[i];
2730 	devy = y[i];
2731 	points[i].x = (int) (devx);
2732 	points[i].y = (int) (devy);
2733 	mx0 = imin2(mx0, points[i].x);
2734 	mx1 = imax2(mx1, points[i].x);
2735 	my0 = imin2(my0, points[i].y);
2736 	my1 = imax2(my1, points[i].y);
2737     }
2738     r.x = mx0; r.width = mx1 - mx0;
2739     r.y = my0; r.height = my1 - my0;
2740 
2741     if (winding) {
2742         DRAW(gsetpolyfillmode(_d, 0));
2743     } else {
2744         DRAW(gsetpolyfillmode(_d, 1));
2745     }
2746 
2747     SetColor(gc->fill, gc->gamma, xd);
2748     if (R_OPAQUE(gc->fill)) {
2749 	DRAW(gfillpolypolygon(_d, xd->fgcolor, points, npoly, nper));
2750     } else if(R_ALPHA(gc->fill) > 0) {
2751 	if(xd->have_alpha) {
2752 	    gsetcliprect(xd->bm, xd->clip);
2753 	    gcopy(xd->bm2, xd->bm, r);
2754 	    gfillpolypolygon(xd->bm2, xd->fgcolor, points, npoly, nper);
2755 	    DRAW2(gc->fill);
2756 	} else WARN_SEMI_TRANS;
2757     }
2758 
2759     SetColor(gc->col, gc->gamma, xd);
2760     SetLineStyle(gc, dd);
2761     if (R_OPAQUE(gc->col)) {
2762         pointIndex = points;
2763         for (i = 0; i < npoly; i++) {
2764             DRAW(gdrawpolygon(_d, xd->lwd, xd->lty, xd->fgcolor,
2765                               pointIndex, nper[i], 0,
2766                               xd->lend, xd->ljoin, xd->lmitre));
2767             pointIndex = pointIndex + nper[i];
2768         }
2769     } else if(R_ALPHA(gc->col) > 0) {
2770 	if(xd->have_alpha) {
2771 	    r = xd->clip;
2772 	    gsetcliprect(xd->bm, xd->clip);
2773 	    gcopy(xd->bm2, xd->bm, r);
2774             pointIndex = points;
2775             for (i = 0; i < npoly; i++) {
2776                 gdrawpolygon(xd->bm2, xd->lwd, xd->lty, xd->fgcolor,
2777                              pointIndex, nper[i], 0,
2778                              xd->lend, xd->ljoin, xd->lmitre);
2779                 pointIndex = pointIndex + nper[i];
2780             }
2781 	    DRAW2(gc->col);
2782 	} else WARN_SEMI_TRANS;
2783     }
2784     vmaxset(vmax);
2785     SH;
2786 }
2787 
doRaster(unsigned int * raster,int x,int y,int w,int h,double rot,pDevDesc dd)2788 static void doRaster(unsigned int *raster, int x, int y, int w, int h,
2789                      double rot, pDevDesc dd)
2790 {
2791     const void *vmax = vmaxget();
2792     gadesc *xd = (gadesc *) dd->deviceSpecific;
2793     rect  dr = rect(x, y, w, h);
2794     image img;
2795     GAbyte *imageData;
2796 
2797     TRACEDEVGA("raster");
2798 
2799     /* Create image object */
2800     img = newimage(w, h, 32);
2801 
2802     /* Set the image pixels from the raster.
2803        Windows uses 0xaarrggbb.
2804        AlphaBlend requires pre-multiplied alpha, that is it uses
2805        (src + (1-alpha)*dest) for each pixel colour.
2806        We could re-order the lines here (top to bottom) to avoid a copy
2807        in imagetobitmap.
2808      */
2809     imageData = (GAbyte *) R_alloc(4*w*h, sizeof(GAbyte));
2810     for (int i = 0; i < w*h; i++) {
2811         GAbyte alpha = R_ALPHA(raster[i]);
2812 	double fac = alpha/255.0;
2813 	imageData[i*4 + 3] = alpha;
2814 	imageData[i*4 + 2] = 0.49 + fac * R_RED(raster[i]);
2815 	imageData[i*4 + 1] = 0.49 + fac * R_GREEN(raster[i]);
2816 	imageData[i*4 + 0] = 0.49 + fac * R_BLUE(raster[i]);
2817     }
2818     setpixels(img, imageData);
2819     if(xd->kind != SCREEN) {
2820         gsetcliprect(xd->gawin, xd->clip);
2821 	gcopyalpha2(xd->gawin, img, dr);
2822     } else {
2823         gsetcliprect(xd->bm, xd->clip);
2824 	gcopyalpha2(xd->bm, img, dr);
2825         if(!xd->buffered)
2826 	    drawbits(xd);
2827     }
2828 
2829     /* Tidy up */
2830     delimage(img);
2831     SH;
2832     vmaxset(vmax);
2833 }
2834 
flipRaster(unsigned int * rasterImage,int imageWidth,int imageHeight,int invertX,int invertY,unsigned int * flippedRaster)2835 static void flipRaster(unsigned int *rasterImage,
2836                        int imageWidth, int imageHeight,
2837                        int invertX, int invertY,
2838                        unsigned int *flippedRaster) {
2839     int i, j;
2840     int rowInc, rowOff, colInc, colOff;
2841 
2842     if (invertX) {
2843         colInc = -1;
2844         colOff = imageWidth - 1;
2845     } else {
2846         colInc = 1;
2847         colOff = 0;
2848     }
2849     if (invertY) {
2850         rowInc = -1;
2851         rowOff = imageHeight - 1;
2852     } else {
2853         rowInc = 1;
2854         rowOff = 0;
2855     }
2856 
2857     for (i = 0; i < imageHeight ;i++) {
2858         for (j = 0; j < imageWidth; j++) {
2859             int row = (rowInc*i + rowOff);
2860             int col = (colInc*j + colOff);
2861             flippedRaster[i*imageWidth + j] =
2862                 rasterImage[row*imageWidth + col];
2863         }
2864     }
2865 }
2866 
GA_Raster(unsigned int * raster,int w,int h,double x,double y,double width,double height,double rot,Rboolean interpolate,const pGEcontext gc,pDevDesc dd)2867 static void GA_Raster(unsigned int *raster, int w, int h,
2868                       double x, double y,
2869                       double width, double height,
2870                       double rot,
2871                       Rboolean interpolate,
2872                       const pGEcontext gc, pDevDesc dd)
2873 {
2874     const void *vmax = vmaxget();
2875     double angle = rot*M_PI/180;
2876     unsigned int *image = raster;
2877     int imageWidth = w, imageHeight = h;
2878     Rboolean invertX = FALSE;
2879     Rboolean invertY = TRUE;
2880 
2881     /* The alphablend code cannot handle negative width or height */
2882     if (height < 0) {
2883         height = -height;
2884         invertY = FALSE;
2885     }
2886     if (width < 0) {
2887         width = -width;
2888         invertX = TRUE;
2889     }
2890 
2891     if (interpolate) {
2892         int newW = (int) (width + .5), newH = (int) (height + .5);
2893         unsigned int *newRaster;
2894 
2895         newRaster = (unsigned int *) R_alloc(newW * newH,
2896                                              sizeof(unsigned int));
2897         R_GE_rasterInterpolate(image, w, h, newRaster, newW, newH);
2898         image = newRaster;
2899         imageWidth = newW;
2900         imageHeight = newH;
2901 
2902     } else {
2903         /* Even if not interpolating, have to explicitly scale here
2904          * before doing rotation, so that image to rotate
2905          * is the right size AND so that can adjust (x, y)
2906          * correctly
2907          */
2908         int newW = (int) (width + .5), newH = (int) (height + .5);
2909         unsigned int *newRaster;
2910 
2911         newRaster = (unsigned int *) R_alloc(newW * newH,
2912                                              sizeof(unsigned int));
2913         R_GE_rasterScale(image, w, h, newRaster, newW, newH);
2914         image = newRaster;
2915         imageWidth = newW;
2916         imageHeight = newH;
2917     }
2918 
2919     if (invertX) {
2920         /* convert (x, y) from bottom-left to top-left */
2921         x -= imageWidth*cos(angle);
2922         if (angle != 0) y -= imageWidth*sin(angle);
2923     }
2924     if (!invertY) {
2925         /* convert (x, y) from bottom-left to top-left */
2926         y -= imageHeight*cos(angle);
2927         if (angle != 0) x -= imageHeight*sin(angle);
2928     }
2929 
2930     if (angle != 0) {
2931         int newW, newH;
2932         double xoff, yoff;
2933         unsigned int *resizedRaster, *rotatedRaster;
2934 
2935         R_GE_rasterRotatedSize(imageWidth, imageHeight, angle, &newW, &newH);
2936         R_GE_rasterRotatedOffset(imageWidth, imageHeight, angle, 0,
2937                                  &xoff, &yoff);
2938 
2939         resizedRaster = (unsigned int *) R_alloc(newW * newH,
2940                                              sizeof(unsigned int));
2941         R_GE_rasterResizeForRotation(image, imageWidth, imageHeight,
2942                                      resizedRaster, newW, newH, gc);
2943 
2944         rotatedRaster = (unsigned int *) R_alloc(newW * newH,
2945                                                  sizeof(unsigned int));
2946         R_GE_rasterRotate(resizedRaster, newW, newH, angle, rotatedRaster, gc,
2947                           /* Threshold alpha to
2948                            * transparent/opaque only
2949                            */
2950                           FALSE);
2951 
2952         /*
2953          * Adjust (x, y) for resized and rotated image
2954          */
2955         x -= (newW - imageWidth)/2 + xoff;
2956         y -= (newH - imageHeight)/2 - yoff;
2957 
2958         image = rotatedRaster;
2959         imageWidth = newW;
2960         imageHeight = newH;
2961     }
2962 
2963     if (invertX || invertY) {
2964         unsigned int *flippedRaster;
2965 
2966         flippedRaster = (unsigned int *) R_alloc(imageWidth * imageHeight,
2967                                                  sizeof(unsigned int));
2968         flipRaster(image, imageWidth, imageHeight,
2969                    invertX, invertY, flippedRaster);
2970         image = flippedRaster;
2971     }
2972 
2973     doRaster(image, (int) (x + .5), (int) (y + .5),
2974              imageWidth, imageHeight, rot, dd);
2975 
2976     vmaxset(vmax);
2977 }
2978 
GA_Cap(pDevDesc dd)2979 static SEXP GA_Cap(pDevDesc dd)
2980 {
2981     gadesc *xd = (gadesc *) dd->deviceSpecific;
2982     SEXP dim, raster = R_NilValue;
2983     image img = NULL;
2984     GAbyte *screenData;
2985 
2986     /* These in-place conversions are ok */
2987     TRACEDEVGA("cap");
2988 
2989     /* Only make sense for on-screen device */
2990     if(xd->kind == SCREEN) {
2991         img = bitmaptoimage(xd->gawin);
2992         if (imagedepth(img) == 8) img = convert8to32(img);
2993 
2994 	if (img) {
2995 	    int width = imagewidth(img), height = imageheight(img),
2996 		size = width*height;
2997 	    unsigned int *rint;
2998 
2999 	    screenData = getpixels(img);
3000 
3001 	    PROTECT(raster = allocVector(INTSXP, size));
3002 
3003 	    /* Copy each byte of screen to an R matrix.
3004 	     * The ARGB32 needs to be converted to R's ABGR32 */
3005 	    rint = (unsigned int *) INTEGER(raster);
3006 	    for (int i = 0; i < size; i++)
3007 		rint[i] = R_RGBA(screenData[i*4 + 2],
3008 				 screenData[i*4 + 1],
3009 				 screenData[i*4 + 0],
3010 				 255);
3011 	    PROTECT(dim = allocVector(INTSXP, 2));
3012 	    INTEGER(dim)[0] = height;
3013 	    INTEGER(dim)[1] = width;
3014 	    setAttrib(raster, R_DimSymbol, dim);
3015 
3016 	    UNPROTECT(2);
3017 	}
3018 
3019 	/* Tidy up */
3020 	delimage(img);
3021     }
3022 
3023 
3024     return raster;
3025 }
3026 
3027 	/********************************************************/
3028 	/* device_Text should have the side-effect that the	*/
3029 	/* given text is drawn at the given location		*/
3030 	/* the text should be rotated according to rot (degrees)*/
3031 	/* the location is in an arbitrary coordinate system	*/
3032 	/* and this function is responsible for converting the	*/
3033 	/* location to DEVICE coordinates using GConvert	*/
3034 	/********************************************************/
3035 
GA_Text0(double x,double y,const char * str,int enc,double rot,double hadj,const pGEcontext gc,pDevDesc dd)3036 static void GA_Text0(double x, double y, const char *str, int enc,
3037 		     double rot, double hadj,
3038 		     const pGEcontext gc,
3039 		     pDevDesc dd)
3040 {
3041     double pixs, xl, yl, rot1;
3042     gadesc *xd = (gadesc *) dd->deviceSpecific;
3043 
3044     pixs = - 1;
3045     xl = 0.0;
3046     yl = -pixs;
3047     rot1 = rot * DEG2RAD;
3048     x += -xl * cos(rot1) + yl * sin(rot1);
3049     y -= -xl * sin(rot1) - yl * cos(rot1);
3050 
3051     SetFont(gc, rot, xd);
3052     SetColor(gc->col, gc->gamma, xd);
3053     if (R_OPAQUE(gc->col)) {
3054 	if(gc->fontface != 5) {
3055 	    /* As from 2.7.0 can use Unicode always */
3056 	    int n = strlen(str), cnt;
3057 	    R_CheckStack2(sizeof(wchar_t)*(n+1));
3058 	    wchar_t wc[n+1];/* only need terminator to debug */
3059 	    cnt = (enc == CE_UTF8) ?
3060 		Rf_utf8towcs(wc, str, n+1): mbstowcs(wc, str, n);
3061 	    /* These macros need to be wrapped in braces */
3062 	    DRAW(gwdrawstr1(_d, xd->font, xd->fgcolor, pt(x, y),
3063 			    wc, cnt, hadj));
3064 	} else {
3065 	    DRAW(gdrawstr1(_d, xd->font, xd->fgcolor, pt(x, y), str, hadj));
3066 	}
3067     } else if(R_ALPHA(gc->col) > 0) {
3068 	/*  it is too hard to get a correct bounding box */
3069 	if(xd->have_alpha) {
3070 	    rect r = xd->clip;
3071 	    r = getregion(xd);
3072 	    gsetcliprect(xd->bm, xd->clip);
3073 	    gcopy(xd->bm2, xd->bm, r);
3074 	    if(gc->fontface != 5) {
3075 		int n = strlen(str), cnt;
3076 		R_CheckStack2(sizeof(wchar_t)*(n+1));
3077 		wchar_t wc[n+1];
3078 		cnt = (enc == CE_UTF8) ?
3079 		    Rf_utf8towcs(wc, str, n+1): mbstowcs(wc, str, n);
3080 		gwdrawstr1(xd->bm2, xd->font, xd->fgcolor, pt(x, y),
3081 			   wc, cnt, hadj);
3082 	    } else
3083 		gdrawstr1(xd->bm2, xd->font, xd->fgcolor, pt(x, y), str, hadj);
3084 	    DRAW2(gc->col);
3085 	} else WARN_SEMI_TRANS;
3086     }
3087     SH;
3088 }
3089 
GA_Text(double x,double y,const char * str,double rot,double hadj,const pGEcontext gc,pDevDesc dd)3090 static void GA_Text(double x, double y, const char *str,
3091 		    double rot, double hadj,
3092 		    const pGEcontext gc,
3093 		    pDevDesc dd)
3094 {
3095     GA_Text0(x, y, str, CE_NATIVE, rot, hadj, gc, dd);
3096 }
3097 
GA_Text_UTF8(double x,double y,const char * str,double rot,double hadj,const pGEcontext gc,pDevDesc dd)3098 static void GA_Text_UTF8(double x, double y, const char *str,
3099 			double rot, double hadj,
3100 			const pGEcontext gc,
3101 			pDevDesc dd)
3102 {
3103     GA_Text0(x, y, str, CE_UTF8, rot, hadj, gc, dd);
3104 }
3105 
3106 
3107 	/********************************************************/
3108 	/* device_Locator should return the location of the next*/
3109 	/* mouse click (in DEVICE coordinates;  GLocator is	*/
3110 	/* responsible for any conversions)			*/
3111 	/* not all devices will do anything (e.g., postscript)	*/
3112 	/********************************************************/
3113 
donelocator(void * data)3114 static void donelocator(void *data)
3115 {
3116     gadesc *xd;
3117     xd = (gadesc *)data;
3118     addto(xd->gawin);
3119     gchangemenubar(xd->mbar);
3120     if (xd->stoploc) {
3121       hide(xd->stoploc);
3122       show(xd->gawin);
3123     }
3124     gsetcursor(xd->gawin, ArrowCursor);
3125     gchangepopup(xd->gawin, xd->grpopup);
3126     addto(xd->gawin);
3127     setstatus(_("R Graphics"));
3128     xd->locator = FALSE;
3129 }
3130 
3131 static void GA_onExit(pDevDesc dd);
3132 
GA_Locator(double * x,double * y,pDevDesc dd)3133 static Rboolean GA_Locator(double *x, double *y, pDevDesc dd)
3134 {
3135     gadesc *xd = (gadesc *) dd->deviceSpecific;
3136     RCNTXT cntxt;
3137 
3138     if (xd->kind != SCREEN)
3139 	return FALSE;
3140     if (xd->holdlevel > 0)
3141 	error(_("attempt to use the locator after dev.hold()"));
3142     xd->locator = TRUE;
3143     xd->clicked = 0;
3144     show(xd->gawin);
3145     addto(xd->gawin);
3146     gchangemenubar(xd->mbarloc);
3147     if (xd->stoploc) {
3148       show(xd->stoploc);
3149       show(xd->gawin);
3150     }
3151     gchangepopup(xd->gawin, xd->locpopup);
3152     gsetcursor(xd->gawin, CrossCursor);
3153     setstatus(G_("Locator is active"));
3154 
3155     /* set up a context which will clean up if there's an error */
3156     begincontext(&cntxt, CTXT_CCODE, R_NilValue, R_NilValue, R_NilValue,
3157 		 R_NilValue, R_NilValue);
3158     cntxt.cend = &donelocator;
3159     cntxt.cenddata = xd;
3160     xd->cntxt = (void *) &cntxt;
3161 
3162     /* and an exit handler in case the window gets closed */
3163     dd->onExit = GA_onExit;
3164 
3165     while (!xd->clicked) {
3166 	SH;
3167 	R_WaitEvent();
3168 	R_ProcessEvents();
3169 	if (!dd->deviceSpecific) { /* closing the window on other systems calls error().
3170 	                             But that is not safe on Windows, so we NULL the device
3171 	                             specific field and call error() here instead. */
3172 	    error(_("graphics device closed during call to locator or identify"));
3173 	}
3174     }
3175 
3176     dd->onExit = NULL;
3177     xd->cntxt = NULL;
3178 
3179     endcontext(&cntxt);
3180     donelocator((void *)xd);
3181 
3182     if (xd->clicked == 1) {
3183 	*x = xd->px;
3184 	*y = xd->py;
3185 	return TRUE;
3186     } else
3187 	return FALSE;
3188 }
3189 
3190 	/********************************************************/
3191 	/* device_Mode is called whenever the graphics engine	*/
3192 	/* starts drawing (mode=1) or stops drawing (mode=0)	*/
3193 	/* the device is not required to do anything		*/
3194 	/********************************************************/
3195 
3196 /* Set Graphics mode - not needed for X11 */
GA_Mode(int mode,pDevDesc dd)3197 static void GA_Mode(int mode, pDevDesc dd)
3198 {
3199 }
3200 
GA_setPattern(SEXP pattern,pDevDesc dd)3201 static SEXP GA_setPattern(SEXP pattern, pDevDesc dd) {
3202     return R_NilValue;
3203 }
3204 
GA_releasePattern(SEXP ref,pDevDesc dd)3205 static void GA_releasePattern(SEXP ref, pDevDesc dd) {}
3206 
GA_setClipPath(SEXP path,SEXP ref,pDevDesc dd)3207 static SEXP GA_setClipPath(SEXP path, SEXP ref, pDevDesc dd) {
3208     return R_NilValue;
3209 }
3210 
GA_releaseClipPath(SEXP ref,pDevDesc dd)3211 static void GA_releaseClipPath(SEXP ref, pDevDesc dd) {}
3212 
GA_setMask(SEXP path,SEXP ref,pDevDesc dd)3213 static SEXP GA_setMask(SEXP path, SEXP ref, pDevDesc dd) {
3214     return R_NilValue;
3215 }
3216 
GA_releaseMask(SEXP ref,pDevDesc dd)3217 static void GA_releaseMask(SEXP ref, pDevDesc dd) {}
3218 
3219 
3220 	/********************************************************/
3221 	/* the device-driver entry point is given a device	*/
3222 	/* description structure that it must set up.  this	*/
3223 	/* involves several important jobs ...			*/
3224 	/* (1) it must ALLOCATE a new device-specific parameters*/
3225 	/* structure and FREE that structure if anything goes	*/
3226 	/* wrong (i.e., it won't report a successful setup to	*/
3227 	/* the graphics engine (the graphics engine is NOT	*/
3228 	/* responsible for allocating or freeing device-specific*/
3229 	/* resources or parameters)				*/
3230 	/* (2) it must initialise the device-specific resources */
3231 	/* and parameters (mostly done by calling device_Open)	*/
3232 	/* (3) it must initialise the generic graphical	*/
3233 	/* parameters that are not initialised by GInit (because*/
3234 	/* only the device knows what values they should have)	*/
3235 	/* see Graphics.h for the official list of these	*/
3236 	/* (4) it may reset generic graphics parameters that	*/
3237 	/* have already been initialised by GInit (although you	*/
3238 	/* should know what you are doing if you do this)	*/
3239 	/* (5) it must attach the device-specific parameters	*/
3240 	/* structure to the device description structure	*/
3241 	/* e.g., dd->deviceSpecific = (void *) xd;		*/
3242 	/* (6) it must FREE the overall device description if	*/
3243 	/* it wants to bail out to the top-level		*/
3244 	/* the graphics engine is responsible for allocating	*/
3245 	/* the device description and freeing it in most cases	*/
3246 	/* but if the device driver freaks out it needs to do	*/
3247 	/* the clean-up itself					*/
3248 	/********************************************************/
3249 
3250 
3251 static
GADeviceDriver(pDevDesc dd,const char * display,double width,double height,double pointsize,Rboolean recording,int resize,int bg,int canvas,double gamma,int xpos,int ypos,Rboolean buffered,SEXP psenv,Rboolean restoreConsole,const char * title,Rboolean clickToConfirm,Rboolean fillOddEven,const char * family,int quality)3252 Rboolean GADeviceDriver(pDevDesc dd, const char *display, double width,
3253 			double height, double pointsize,
3254 			Rboolean recording, int resize, int bg, int canvas,
3255 			double gamma, int xpos, int ypos, Rboolean buffered,
3256 			SEXP psenv, Rboolean restoreConsole,
3257 			const char *title, Rboolean clickToConfirm,
3258 			Rboolean fillOddEven, const char *family,
3259 			int quality)
3260 {
3261     /* if need to bail out with some sort of "error" then */
3262     /* must free(dd) */
3263 
3264     int   ps; /* This really is in (big) points */
3265     gadesc *xd;
3266     rect  rr;
3267 
3268     /* allocate new device description */
3269     if (!(xd = (gadesc *) malloc(sizeof(gadesc)))) {
3270 	warning("allocation failed in GADeviceDriver");
3271 	return FALSE;
3272     }
3273 
3274     /* from here on, if need to bail out with "error", must also */
3275     /* free(xd) */
3276 
3277     ps = pointsize;
3278     if (ps < 1) ps = 12;
3279     /* Ensures a font is selected at first use */
3280     xd->font = NULL;
3281     xd->fontface = -1;
3282     xd->fontsize = -1;
3283     xd->fontangle = 0.0;
3284     xd->fontfamily[0] = '\0';
3285     xd->basefontsize = ps ;
3286     dd->startfont = 1;
3287     dd->startps = ps;
3288     dd->startlty = LTY_SOLID;
3289     dd->startgamma = gamma;
3290     xd->bm = NULL;
3291     xd->bm2 = NULL;
3292     xd->have_alpha = FALSE; /* selectively overridden in GA_Open */
3293     xd->warn_trans = FALSE;
3294     strncpy(xd->title, title, 101);
3295     xd->title[100] = '\0';
3296     strncpy(xd->basefontfamily, family, 101);
3297     xd->basefontfamily[100] = '\0';
3298     xd->fontquality = quality;
3299     xd->doSetPolyFill = TRUE;     /* will only set it once */
3300     xd->fillOddEven = fillOddEven;
3301 
3302     /* Start the Device Driver and Hardcopy.  */
3303 
3304     if (!GA_Open(dd, xd, display, width, height, recording, resize, canvas,
3305 		 gamma, xpos, ypos, bg)) {
3306 	warning("opening device failed");
3307 	free(xd);
3308 	return FALSE;
3309     }
3310     dd->deviceSpecific = (void *) xd;
3311     /* Set up Data Structures  */
3312 
3313     dd->close = GA_Close;
3314     dd->activate = GA_Activate;
3315     dd->deactivate = GA_Deactivate;
3316     dd->size = GA_Size;
3317     dd->newPage = GA_NewPage;
3318     dd->clip = GA_Clip;
3319     dd->strWidth = GA_StrWidth;
3320     dd->text = GA_Text;
3321     dd->rect = GA_Rect;
3322     dd->circle = GA_Circle;
3323     dd->line = GA_Line;
3324     dd->polyline = GA_Polyline;
3325     dd->polygon = GA_Polygon;
3326     dd->path = GA_Path;
3327     dd->raster = GA_Raster;
3328     dd->cap = GA_Cap;
3329     dd->locator = GA_Locator;
3330     dd->mode = GA_Mode;
3331     dd->metricInfo = GA_MetricInfo;
3332     dd->newFrameConfirm = clickToConfirm ? GA_NewFrameConfirm : NULL;
3333     dd->hasTextUTF8 = TRUE;
3334     dd->strWidthUTF8 = GA_StrWidth_UTF8;
3335     dd->textUTF8 = GA_Text_UTF8;
3336     dd->useRotatedTextInContour = TRUE;
3337     xd->cntxt = NULL;
3338     dd->holdflush = GA_holdflush;
3339     xd->holdlevel = 0;
3340 
3341     dd->setPattern      = GA_setPattern;
3342     dd->releasePattern  = GA_releasePattern;
3343     dd->setClipPath     = GA_setClipPath;
3344     dd->releaseClipPath = GA_releaseClipPath;
3345     dd->setMask         = GA_setMask;
3346     dd->releaseMask     = GA_releaseMask;
3347 
3348     dd->haveRaster = 2;  /* full support */
3349     dd->haveCapture = dd->haveLocator = (xd->kind == SCREEN) ? 2 : 1;
3350     dd->haveTransparency = 2;
3351     switch(xd->kind) {
3352     case SCREEN:
3353 	dd->haveTransparentBg = 3;
3354 	break;
3355     case PRINTER:
3356     case METAFILE:
3357     case PNG:
3358 	dd->haveTransparentBg = 2;
3359 	break;
3360     default: /* JPEG, BMP, TIFF */
3361 	dd->haveTransparentBg = 1;
3362 	break;
3363     }
3364     /* set graphics parameters that must be set by device driver */
3365     /* Window Dimensions in Pixels */
3366     rr = getrect(xd->gawin);
3367     dd->left = (xd->kind == PRINTER) ? rr.x : 0;	/* left */
3368     dd->right = dd->left + rr.width - 0.0001;	/* right */
3369     dd->top = (xd->kind == PRINTER) ? rr.y : 0;	/* top */
3370     dd->bottom = dd->top + rr.height - 0.0001;	/* bottom */
3371     dd->clipLeft = dd->left; dd->clipRight = dd->right;
3372     dd->clipBottom = dd->bottom; dd->clipTop = dd->top;
3373 
3374     if (resize == 3) { /* might have got a shrunken window */
3375 	int iw = width/pixelWidth(NULL) + 0.5,
3376 	    ih = height/pixelHeight(NULL) + 0.5;
3377 	xd->origWidth = dd->right = iw;
3378 	xd->origHeight = dd->bottom = ih;
3379     }
3380 
3381     dd->startps = ps * xd->rescale_factor;
3382     if (xd->kind > METAFILE && xd->res_dpi > 0) ps *= xd->res_dpi/72.0;
3383 
3384     if (xd->kind <= METAFILE) {
3385 	/* it is 12 *point*, not 12 pixel */
3386 	double ps0 = ps * xd->rescale_factor;
3387 	dd->cra[0] = 0.9 * ps0 * devicepixelsx(xd->gawin) / 72.0;
3388 	dd->cra[1] = 1.2 * ps0 * devicepixelsy(xd->gawin) / 72.0;
3389     } else {
3390 	dd->cra[0] = 0.9 * ps;
3391 	dd->cra[1] = 1.2 * ps;
3392     }
3393 
3394     /* Character Addressing Offsets */
3395     /* These are used to plot a single plotting character */
3396     /* so that it is exactly over the plotting point */
3397 
3398     dd->xCharOffset = 0.50;
3399     dd->yCharOffset = 0.40;
3400     dd->yLineBias = 0.2;
3401 
3402     /* Inches per raster unit */
3403 
3404     if (xd->kind <= METAFILE) { /* non-screen devices set NA_real_ */
3405 	if (R_FINITE(user_xpinch) && user_xpinch > 0.0)
3406 	    dd->ipr[0] = 1.0/user_xpinch;
3407 	else
3408 	    dd->ipr[0] = pixelWidth(xd->gawin);
3409 	if (R_FINITE(user_ypinch) && user_ypinch > 0.0)
3410 	    dd->ipr[1] = 1.0/user_ypinch;
3411 	else
3412 	    dd->ipr[1] = pixelHeight(xd->gawin);
3413     } else if (xd->res_dpi > 0) {
3414 	dd->ipr[0] = dd->ipr[1] = 1.0/xd->res_dpi;
3415     } else {
3416 	dd->ipr[0] = dd->ipr[1] = 1.0/72.0;
3417     }
3418 
3419 
3420     /* Device capabilities */
3421     dd->canClip= TRUE;
3422     dd->canHAdj = 1; /* 0, 0.5, 1 */
3423     dd->canChangeGamma = FALSE;
3424 
3425     /* initialise device description (most of the work */
3426     /* has been done in GA_Open) */
3427 
3428     xd->resize = (resize == 3) || ismdi();   // MDI windows may be zoomed automatically
3429     xd->locator = FALSE;
3430     xd->buffered = buffered;
3431     xd->psenv = psenv;
3432     {
3433 	SEXP timeouts = GetOption1(install("windowsTimeouts"));
3434 	if(isInteger(timeouts)){
3435 	    xd->timeafter = INTEGER(timeouts)[0];
3436 	    xd->timesince = INTEGER(timeouts)[1];
3437 	} else {
3438 	    warning(_("option 'windowsTimeouts' should be integer"));
3439 	    xd->timeafter = 100;
3440 	    xd->timesince = 500;
3441 	}
3442     }
3443     dd->displayListOn = (xd->kind == SCREEN);
3444     dd->deviceVersion = R_GE_definitions;
3445     if (RConsole && restoreConsole) show(RConsole);
3446     return TRUE;
3447 }
3448 
savePlot(SEXP args)3449 SEXP savePlot(SEXP args)
3450 {
3451     SEXP filename, type;
3452     const char *fn, *tp; char display[550];
3453     int device;
3454     pDevDesc dd;
3455     Rboolean restoreConsole;
3456 
3457     args = CDR(args); /* skip entry point name */
3458     device = asInteger(CAR(args));
3459     if(device < 1 || device > NumDevices())
3460 	error(_("invalid device number in 'savePlot'"));
3461     dd = GEgetDevice(device - 1)->dev;
3462     if(!dd) error(_("invalid device in 'savePlot'"));
3463     filename = CADR(args);
3464     if (!isString(filename) || LENGTH(filename) != 1)
3465 	error(_("invalid filename argument in 'savePlot'"));
3466     /* in 2.8.0 this will always be passed as native, but be conserative */
3467     fn = translateCharFP(STRING_ELT(filename, 0));
3468     type = CADDR(args);
3469     if (!isString(type) || LENGTH(type) != 1)
3470 	error(_("invalid type argument in 'savePlot'"));
3471     tp = CHAR(STRING_ELT(type, 0));
3472     restoreConsole = asLogical(CADDDR(args));
3473 
3474     if(!strcmp(tp, "png")) {
3475 	SaveAsPng(dd, fn);
3476     } else if (!strcmp(tp,"bmp")) {
3477 	SaveAsBmp(dd,fn);
3478     } else if (!strcmp(tp,"tiff")) {
3479 	SaveAsTiff(dd,fn);
3480     } else if(!strcmp(tp, "jpeg") || !strcmp(tp,"jpg")) {
3481       /*Default quality suggested in libjpeg*/
3482 	SaveAsJpeg(dd, 75, fn);
3483     } else if(!strcmp(tp, "tiff") || !strcmp(tp,"tif")) {
3484 	SaveAsTiff(dd, fn);
3485     } else if (!strcmp(tp, "wmf") || !strcmp(tp, "emf")) {
3486 	if(strlen(fn) > 512) {
3487 	    askok(G_("file path selected is too long: only 512 bytes are allowed"));
3488 	    return R_NilValue;
3489 	}
3490 	snprintf(display, 550, "win.metafile:%s", fn);
3491 	SaveAsWin(dd, display, restoreConsole);
3492     } else if (!strcmp(tp, "ps") || !strcmp(tp, "eps")) {
3493 	SaveAsPostscript(dd, fn);
3494     } else if (!strcmp(tp, "pdf")) {
3495 	SaveAsPDF(dd, fn);
3496     } else
3497 	error(_("unknown type in savePlot"));
3498     return R_NilValue;
3499 }
3500 
3501 
3502 static int png_rows = 0;
3503 
privategetpixel2(void * d,int i,int j)3504 static unsigned int privategetpixel2(void *d,int i, int j)
3505 {
3506     rgb c;
3507     c = ((rgb *)d)[i*png_rows + j];
3508     return c | 0xff000000;
3509 }
3510 
3511 /* This is the device version */
3512 /* Values of res > 0 are used to set the resolution in the file */
SaveAsBitmap(pDevDesc dd,int res)3513 static void SaveAsBitmap(pDevDesc dd, int res)
3514 {
3515     rect r, r2;
3516     gadesc *xd = (gadesc *) dd->deviceSpecific;
3517     unsigned char *data;
3518 
3519     r = ggetcliprect(xd->gawin);
3520     gsetcliprect(xd->gawin, r2 = getrect(xd->gawin));
3521     if(xd->fp || xd->kind == TIFF) {
3522 	getbitmapdata2(xd->gawin, &data);
3523 	if(data) {
3524 	    png_rows = r2.width;
3525 	    if (xd->kind == PNG)
3526 		R_SaveAsPng(data, xd->windowWidth, xd->windowHeight,
3527 			    privategetpixel2, 0, xd->fp,
3528 			    R_OPAQUE(xd->bg) ? 0 : xd->pngtrans, res) ;
3529 	    else if (xd->kind == JPEG)
3530 		R_SaveAsJpeg(data, xd->windowWidth, xd->windowHeight,
3531 			     privategetpixel2, 0, xd->quality, xd->fp, res) ;
3532 	    else if (xd->kind == TIFF) {
3533 		char buf[600];
3534 		snprintf(buf, 600, xd->filename, xd->npage - 1);
3535 		R_SaveAsTIFF(data, xd->windowWidth, xd->windowHeight,
3536 			     privategetpixel2, 0, buf, res, xd->quality) ;
3537 	    } else
3538 		R_SaveAsBmp(data, xd->windowWidth, xd->windowHeight,
3539 			    privategetpixel2, 0, xd->fp, res);
3540 	    free(data);
3541 	} else
3542 	    warning(_("processing of the plot ran out of memory"));
3543 	if(xd->fp) fclose(xd->fp);
3544     }
3545     gsetcliprect(xd->gawin, r);
3546     xd->fp = NULL;
3547 }
3548 
3549 /* These are the menu item versions */
SaveAsPng(pDevDesc dd,const char * fn)3550 static void SaveAsPng(pDevDesc dd, const char *fn)
3551 {
3552     FILE *fp;
3553     rect r, r2;
3554     unsigned char *data;
3555     gadesc *xd = (gadesc *) dd->deviceSpecific;
3556 
3557     if ((fp = R_fopen(fn, "wb")) == NULL) {
3558 	char msg[MAX_PATH+32];
3559 
3560 	strcpy(msg, "Impossible to open ");
3561 	strncat(msg, fn, MAX_PATH);
3562 	R_ShowMessage(msg);
3563 	return;
3564     }
3565     r = ggetcliprect(xd->bm);
3566     gsetcliprect(xd->bm, r2 = getrect(xd->bm));
3567     getbitmapdata2(xd->bm, &data);
3568     if(data) {
3569 	png_rows = r2.width;
3570 	R_SaveAsPng(data, xd->windowWidth, xd->windowHeight,
3571 		    privategetpixel2, 0, fp, 0, 0) ;
3572 	free(data);
3573     } else
3574 	warning(_("processing of the plot ran out of memory"));
3575     gsetcliprect(xd->bm, r);
3576     fclose(fp);
3577 }
3578 
SaveAsJpeg(pDevDesc dd,int quality,const char * fn)3579 static void SaveAsJpeg(pDevDesc dd, int quality, const char *fn)
3580 {
3581     FILE *fp;
3582     rect r, r2;
3583     unsigned char *data;
3584     gadesc *xd = (gadesc *) dd->deviceSpecific;
3585 
3586     if ((fp = R_fopen(fn,"wb")) == NULL) {
3587 	char msg[MAX_PATH+32];
3588 	strcpy(msg, "Impossible to open ");
3589 	strncat(msg, fn, MAX_PATH);
3590 	R_ShowMessage(msg);
3591 	return;
3592     }
3593     r = ggetcliprect(xd->bm);
3594     gsetcliprect(xd->bm, r2 = getrect(xd->bm));
3595     getbitmapdata2(xd->bm, &data);
3596     if(data) {
3597 	png_rows = r2.width;
3598 	R_SaveAsJpeg(data,xd->windowWidth, xd->windowHeight,
3599 		     privategetpixel2, 0, quality, fp, 0) ;
3600 	free(data);
3601     } else
3602 	warning(_("processing of the plot ran out of memory"));
3603     gsetcliprect(xd->bm, r);
3604     fclose(fp);
3605 }
3606 
3607 
SaveAsBmp(pDevDesc dd,const char * fn)3608 static void SaveAsBmp(pDevDesc dd, const char *fn)
3609 {
3610     FILE *fp;
3611     rect r, r2;
3612     unsigned char *data;
3613     gadesc *xd = (gadesc *) dd->deviceSpecific;
3614 
3615     if ((fp = R_fopen(fn, "wb")) == NULL) {
3616 	char msg[MAX_PATH+32];
3617 
3618 	strcpy(msg, _("Impossible to open "));
3619 	strncat(msg, fn, MAX_PATH);
3620 	R_ShowMessage(msg);
3621 	return;
3622     }
3623     r = ggetcliprect(xd->bm);
3624     gsetcliprect(xd->bm, r2 = getrect(xd->bm));
3625 
3626     getbitmapdata2(xd->bm, &data);
3627     if(data) {
3628 	png_rows = r2.width;
3629 	R_SaveAsBmp(data, xd->windowWidth, xd->windowHeight,
3630 		    privategetpixel2, 0, fp, 0) ;
3631 	free(data);
3632     } else
3633 	warning(_("processing of the plot ran out of memory"));
3634     gsetcliprect(xd->bm, r);
3635     fclose(fp);
3636 }
3637 
SaveAsTiff(pDevDesc dd,const char * fn)3638 static void SaveAsTiff(pDevDesc dd, const char *fn)
3639 {
3640     rect r, r2;
3641     unsigned char *data;
3642     gadesc *xd = (gadesc *) dd->deviceSpecific;
3643 
3644     r = ggetcliprect(xd->bm);
3645     gsetcliprect(xd->bm, r2 = getrect(xd->bm));
3646 
3647     getbitmapdata2(xd->bm, &data);
3648     if(data) {
3649 	png_rows = r2.width;
3650 	R_SaveAsTIFF(data, xd->windowWidth, xd->windowHeight,
3651 		     privategetpixel2, 0, fn, 0, 1 /* no compression */) ;
3652 	free(data);
3653     } else
3654 	warning(_("processing of the plot ran out of memory"));
3655     gsetcliprect(xd->bm, r);
3656 }
3657 
3658 /* This is Guido's devga device, 'ga' for GraphApp. */
3659 
3660 #ifndef CLEARTYPE_QUALITY
3661 # define CLEARTYPE_QUALITY 5
3662 #endif
3663 
devga(SEXP args)3664 SEXP devga(SEXP args)
3665 {
3666     pGEDevDesc gdd;
3667     const char *display, *title, *family;
3668     const void *vmax;
3669     double height, width, ps, xpinch, ypinch, gamma;
3670     int recording = 0, resize = 1, bg, canvas, xpos, ypos, buffered, quality;
3671     Rboolean restoreConsole, clickToConfirm, fillOddEven;
3672     SEXP sc, psenv;
3673 
3674     vmax = vmaxget();
3675     args = CDR(args); /* skip entry point name */
3676     display = translateCharFP(STRING_ELT(CAR(args), 0));
3677     args = CDR(args);
3678     width = asReal(CAR(args));
3679     args = CDR(args);
3680     height = asReal(CAR(args));
3681     args = CDR(args);
3682     if (width <= 0 || height <= 0)
3683 	error(_("invalid 'width' or 'height'"));
3684     ps = asReal(CAR(args));
3685     args = CDR(args);
3686     recording = asLogical(CAR(args));
3687     if (recording == NA_LOGICAL)
3688 	error(_("invalid value of '%s'"), "record");
3689     args = CDR(args);
3690     resize = asInteger(CAR(args));
3691     if (resize == NA_INTEGER)
3692 	error(_("invalid value of '%s'"), "rescale");
3693     args = CDR(args);
3694     xpinch = asReal(CAR(args));
3695     args = CDR(args);
3696     ypinch = asReal(CAR(args));
3697     args = CDR(args);
3698     sc = CAR(args);
3699     if (!isString(sc) && !isInteger(sc) && !isLogical(sc) && !isReal(sc))
3700 	error(_("invalid value of '%s'"), "canvas");
3701     canvas = RGBpar(sc, 0);
3702     args = CDR(args);
3703     gamma = asReal(CAR(args));
3704     args = CDR(args);
3705     xpos = asInteger(CAR(args)); /* used for res in png/jpeg/bmp */
3706     args = CDR(args);
3707     ypos = asInteger(CAR(args));
3708     args = CDR(args);
3709     buffered = asLogical(CAR(args));
3710     if (buffered == NA_LOGICAL)
3711 	error(_("invalid value of '%s'"), "buffered");
3712     args = CDR(args);
3713     psenv = CAR(args);
3714     args = CDR(args);
3715     sc = CAR(args);
3716     if (!isString(sc) && !isInteger(sc) && !isLogical(sc) && !isReal(sc))
3717 	error(_("invalid value of '%s'"), "bg");
3718     bg = RGBpar(sc, 0);
3719     args = CDR(args);
3720     restoreConsole = asLogical(CAR(args));
3721     args = CDR(args);
3722     sc = CAR(args);
3723     if (!isString(sc) || LENGTH(sc) != 1)
3724 	error(_("invalid value of '%s'"), "title");
3725     title = CHAR(STRING_ELT(sc, 0));
3726     args = CDR(args);
3727     clickToConfirm = asLogical(CAR(args));
3728     args = CDR(args);
3729     fillOddEven = asLogical(CAR(args));
3730     if (fillOddEven == NA_LOGICAL)
3731 	error(_("invalid value of '%s'"), "fillOddEven");
3732     args = CDR(args);
3733     sc = CAR(args);
3734     if (!isString(sc) || LENGTH(sc) != 1)
3735 	error(_("invalid value of '%s'"), "family");
3736     family = CHAR(STRING_ELT(sc, 0));
3737     quality = DEFAULT_QUALITY;
3738     args = CDR(args);
3739     quality = asInteger(CAR(args));
3740 //    printf("fontquality=%d\n", quality);
3741     switch (quality) {
3742     case 1: quality = DEFAULT_QUALITY; break;
3743     case 2: quality = NONANTIALIASED_QUALITY; break;
3744     case 3: quality = CLEARTYPE_QUALITY; break;
3745     case 4: quality = ANTIALIASED_QUALITY; break;
3746     default: quality = DEFAULT_QUALITY;
3747     }
3748 
3749     R_GE_checkVersionOrDie(R_GE_version);
3750     R_CheckDeviceAvailable();
3751     BEGIN_SUSPEND_INTERRUPTS {
3752 	pDevDesc dev;
3753 	char type[100], *file = NULL, fn[MAX_PATH];
3754 	strcpy(type, "windows");
3755 	if (display[0]) {
3756 	    strncpy(type, display, 100 - 1);
3757 	    type[100 - 1] = '\0';
3758 	    char *p = strchr(display, ':');
3759 	    if (p) {
3760 		strncpy(fn, p+1, MAX_PATH - 1);
3761 		fn[MAX_PATH - 1] = '\0';
3762 		file = fn;
3763 	    }
3764 	    // Package tkrplot assumes the exact form here,
3765 	    // but remove suffix for all the others.
3766 	    p = strchr(type, ':');
3767 	    if(p && strncmp(display, "win.metafile", 12)) *p = '\0';
3768 	}
3769 	/* Allocate and initialize the device driver data */
3770 	if (!(dev = (pDevDesc) calloc(1, sizeof(DevDesc)))) return 0;
3771 	GAsetunits(xpinch, ypinch);
3772 	if (!GADeviceDriver(dev, display, width, height, ps,
3773 			    (Rboolean)recording, resize, bg, canvas, gamma,
3774 			    xpos, ypos, (Rboolean)buffered, psenv,
3775 			    restoreConsole, title, clickToConfirm,
3776 			    fillOddEven, family, quality)) {
3777 	    free(dev);
3778 	    error(_("unable to start %s() device"), type);
3779 	}
3780 	gdd = GEcreateDevDesc(dev);
3781 	GEaddDevice2f(gdd, type, file);
3782     } END_SUSPEND_INTERRUPTS;
3783     vmaxset(vmax);
3784     return R_NilValue;
3785 }
3786 
GA_onExit(pDevDesc dd)3787 static void GA_onExit(pDevDesc dd)
3788 {
3789     gadesc *xd = (gadesc *) dd->deviceSpecific;
3790 
3791     dd->onExit = NULL;
3792     xd->confirmation = FALSE;
3793     dd->gettingEvent = FALSE;
3794 
3795     if (xd->cntxt) endcontext((RCNTXT *)xd->cntxt);
3796     if (xd->locator) donelocator((void *)xd);
3797 
3798     addto(xd->gawin);
3799     gchangemenubar(xd->mbar);
3800     gchangepopup(xd->gawin, xd->grpopup);
3801     addto(xd->gawin);
3802     setstatus(_("R Graphics"));
3803     GA_Activate(dd);
3804 }
3805 
GA_NewFrameConfirm(pDevDesc dev)3806 static Rboolean GA_NewFrameConfirm(pDevDesc dev)
3807 {
3808     char *msg;
3809     gadesc *xd = dev->deviceSpecific;
3810 
3811     if (!xd || xd->kind != SCREEN) return FALSE;
3812 
3813     msg = G_("Waiting to confirm page change...");
3814     xd->confirmation = TRUE;
3815     xd->clicked = 0;
3816     xd->enterkey = 0;
3817     show(xd->gawin);
3818     addto(xd->gawin);
3819     gchangemenubar(xd->mbarconfirm);
3820     gchangepopup(xd->gawin, NULL);
3821     setstatus(msg);
3822     R_WriteConsole(msg, strlen(msg));
3823     R_WriteConsole("\n", 1);
3824     R_FlushConsole();
3825     settext(xd->gawin, G_("Click or hit ENTER for next page"));
3826     BringToTop(xd->gawin, 0);
3827     dev->onExit = GA_onExit;  /* install callback for cleanup */
3828     while (!xd->clicked && !xd->enterkey) {
3829 	SH;
3830 	R_WaitEvent();
3831 	R_ProcessEvents(); /* May not return if user interrupts */
3832     }
3833     dev->onExit(dev);
3834 
3835     return TRUE;
3836 }
3837 
GA_eventHelper(pDevDesc dd,int code)3838 static void GA_eventHelper(pDevDesc dd, int code)
3839 {
3840     gadesc *xd = dd->deviceSpecific;
3841 
3842     if (code == 1) {
3843     	show(xd->gawin);
3844     	addto(xd->gawin);
3845     	gchangemenubar(xd->mbar);
3846     	gchangepopup(xd->gawin, NULL);
3847     	if (isEnvironment(dd->eventEnv)) {
3848     	    SEXP prompt = findVar(install("prompt"), dd->eventEnv);
3849     	    if (isString(prompt) && length(prompt) == 1) {
3850     		setstatus(CHAR(asChar(prompt)));
3851     		settext(xd->gawin, CHAR(asChar(prompt)));
3852     	    } else {
3853     	    	setstatus("");
3854     	    	settext(xd->gawin, "");
3855     	    }
3856     	}
3857     	dd->onExit = GA_onExit;  /* install callback for cleanup */
3858     } else if (code == 0)
3859     	dd->onExit(dd);
3860 
3861     return;
3862 }
3863 
3864 
3865 #define WIN32_LEAN_AND_MEAN 1
3866 #include <windows.h>
3867 typedef int (*R_SaveAsBitmap)(/* variable set of args */);
3868 static R_SaveAsBitmap R_devCairo;
3869 static int RcairoAlreadyLoaded = 0;
3870 static HINSTANCE hRcairoDll;
3871 
3872 typedef SEXP (*R_cairoVersion_t)(void);
3873 static R_cairoVersion_t R_cairoVersion = NULL;
3874 typedef SEXP (*R_pangoVersion_t)(void);
3875 static R_pangoVersion_t R_pangoVersion = NULL;
3876 typedef SEXP (*R_cairoFT_t)(void);
3877 static R_cairoFT_t R_cairoFT = NULL;
3878 
Load_Rcairo_Dll()3879 static int Load_Rcairo_Dll()
3880 {
3881     if (!RcairoAlreadyLoaded) {
3882 	char szFullPath[PATH_MAX];
3883 	strcpy(szFullPath, R_HomeDir());
3884 	strcat(szFullPath, "/library/grDevices/libs/");
3885 	strcat(szFullPath, R_ARCH);
3886 	strcat(szFullPath, "/winCairo.dll");
3887 	if (((hRcairoDll = LoadLibrary(szFullPath)) != NULL) &&
3888 	    ((R_devCairo =
3889 	      (R_SaveAsBitmap)GetProcAddress(hRcairoDll, "in_Cairo"))
3890 	     != NULL)) {
3891 	    R_cairoVersion = (R_cairoVersion_t)
3892 		GetProcAddress(hRcairoDll, "in_CairoVersion");
3893 	    R_pangoVersion = (R_pangoVersion_t)
3894 		GetProcAddress(hRcairoDll, "in_PangoVersion");
3895 	    R_cairoFT = (R_cairoFT_t)
3896 		GetProcAddress(hRcairoDll, "in_CairoFT");
3897 	    RcairoAlreadyLoaded = 1;
3898 	} else {
3899 	    if (hRcairoDll != NULL) FreeLibrary(hRcairoDll);
3900 	    RcairoAlreadyLoaded = -1;
3901 	    char buf[1000];
3902 	    snprintf(buf, 1000, "Unable to load '%s'", szFullPath);
3903 	    R_ShowMessage(buf);
3904 	}
3905     }
3906     return (RcairoAlreadyLoaded > 0);
3907 }
3908 
3909 /*
3910    cairo(filename, type, width, height, pointsize, bg, res, antialias, quality)
3911 */
devCairo(SEXP args)3912 SEXP devCairo(SEXP args)
3913 {
3914     if (!Load_Rcairo_Dll())
3915 	error("unable to load winCairo.dll: was it built?");
3916     else (R_devCairo)(args);
3917     return R_NilValue;
3918 }
3919 
cairoVersion(void)3920 SEXP cairoVersion(void)
3921 {
3922     if (!Load_Rcairo_Dll() || R_cairoVersion == NULL) return mkString("");
3923     else return (R_cairoVersion)();
3924 }
3925 
pangoVersion(void)3926 SEXP pangoVersion(void)
3927 {
3928     if (!Load_Rcairo_Dll() || R_cairoVersion == NULL) return mkString("");
3929     else return (R_pangoVersion)();
3930 }
3931 
cairoFT(void)3932 SEXP cairoFT(void)
3933 {
3934     if (!Load_Rcairo_Dll() || R_cairoVersion == NULL) return mkString("");
3935     else return (R_cairoFT)();
3936 }
3937 
bmVersion(void)3938 SEXP bmVersion(void)
3939 {
3940     SEXP ans = PROTECT(allocVector(STRSXP, 3)),
3941 	nms = PROTECT(allocVector(STRSXP, 3));
3942     setAttrib(ans, R_NamesSymbol, nms);
3943     SET_STRING_ELT(nms, 0, mkChar("libpng"));
3944     SET_STRING_ELT(nms, 1, mkChar("jpeg"));
3945     SET_STRING_ELT(nms, 2, mkChar("libtiff"));
3946     SET_STRING_ELT(ans, 0, mkChar((R_pngVersion)()));
3947     SET_STRING_ELT(ans, 1, mkChar((R_jpegVersion)()));
3948     SET_STRING_ELT(ans, 2, mkChar((R_tiffVersion)()));
3949     UNPROTECT(2);
3950     return ans;
3951 }
3952