Rev 4626 | Blame | Last modification | View Log | Download | RSS feed
/** R : A Computer Langage for Statistical Data Analysis* Copyright (C) 1995, 1996 Robert Gentleman and Ross Ihaka* Copyright (C) 1998, 1999 Guido Masarotto and Brian Ripley** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License as published by* the Free Software Foundation; either version 2 of the License, or* (at your option) any later version.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the* GNU General Public License for more details.** You should have received a copy of the GNU General Public License* along with this program; if not, write to the Free Software* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/#include "Defn.h"#include "Graphics.h"#include <stdio.h>#include "opt.h"#include "graphapp/ga.h"#include "graphapp/stdimg.h"#include "console.h"#include "rui.h"#include "devga.h" /* 'Public' routines from here */#include "winuser.h"extern console RConsole;extern int AllDevicesKilled;/********************************************************//* This device driver has been documented so that it be *//* used as a template for new drivers *//********************************************************/#define MM_PER_INCH 25.4 /* mm -> inch conversion */#define TRACEDEVGA(a)#define NOBM(a) if(!xd->kind){a;}#define CLIP if (xd->clip.width>0) gsetcliprect(_d,xd->clip)#define DRAW(a) {drawing _d=xd->gawin;CLIP;a;NOBM(_d=xd->bm;CLIP;a;)}#define SHOW gbitblt(xd->gawin,xd->bm,pt(0,0),getrect(xd->bm));/********************************************************//* Each driver can have its own device-specic graphical *//* parameters and resources. these should be wrapped *//* in a structure (like the x11Desc structure below) *//* and attached to the overall device description via *//* the dd->deviceSpecific pointer *//* NOTE that there are generic graphical parameters *//* which must be set by the device driver, but are *//* common to all device types (see Graphics.h) *//* so go in the GPar structure rather than this device- *//* specific structure *//********************************************************/typedef struct {/* R Graphics Parameters *//* local device copy so that we can detect *//* when parameter changes */int col; /* Color */int fg; /* Foreground */int bg; /* Background */int fontface; /* Typeface */int fontsize; /* Size in points */double fontangle;/* X11 Driver Specific *//* parameters with copy per x11 device */int kind;int windowWidth; /* Window width (pixels) */int windowHeight; /* Window height (pixels) */int resize; /* Window resized */window gawin; /* Graphics window */popup locpopup, grpopup;button stoploc;menubar mbar, mbarloc;menu msubsave;menuitem mgif, mps, mwm, mclpbm, mclpwm, mprint, mclose;menuitem mrec, madd, mreplace, mprev, mnext, mclear, msvar, mgvar;int recording, replaying, needsave;bitmap bm;rgb fgcolor; /* Foreground color */rgb bgcolor; /* Background color */rect clip; /* The clipping rectangle */int usefixed;font fixedfont;font font;int locator, clicked, px, py;int lty, lwd;} x11Desc;/********************************************************//* If there are resources that are shared by all devices*//* of this type, you may wish to make them globals *//* rather than including them in the device-specific *//* parameters structure (especially if they are large !)*//********************************************************//* X11 Driver Specific *//* parameters with only one copy for all x11 devices */static x11Desc *activedevice = NULL;static int numX11Devices = 0;/********************************************************//* There must be an entry point for the device driver *//* which will create device-specific resources, *//* initialise the device-specific parameters structure *//* and return whether the setup succeeded *//* This is called by the graphics engine when the user *//* creates a new device of this type *//********************************************************//* Device Driver Entry Point */int X11DeviceDriver(DevDesc *, char *, double, double, double);/********************************************************//* There are a number of actions that every device *//* driver is expected to perform (even if, in some *//* cases it does nothing - just so long as it doesn't *//* crash !). this is how the graphics engine interacts *//* with each device. ecah action will be documented *//* individually. *//* hooks for these actions must be set up when the *//* device is first created *//********************************************************//* Device Driver Actions */static void X11_Activate(DevDesc *);static void X11_Circle(double, double, int, double, int, int, DevDesc*);static void X11_Clip(double, double, double, double, DevDesc*);static void X11_Close(DevDesc*);static void X11_Deactivate(DevDesc *);static void X11_Hold(DevDesc*);static void X11_Line(double, double, double, double, int, DevDesc*);static int X11_Locator(double*, double*, DevDesc*);static void X11_Mode(int);static void X11_NewPage(DevDesc*);static int X11_Open(DevDesc*, x11Desc*, char*, double, double);static void X11_Polygon(int, double*, double*, int, int, int, DevDesc*);static void X11_Polyline(int, double*, double*, int, DevDesc*);static void X11_Rect(double, double, double, double, int, int, int, DevDesc*);static void X11_Resize(DevDesc*);static double X11_StrWidth(char*, DevDesc*);static void X11_Text(double, double, int, char*, double, double, double,DevDesc*);static void X11_MetricInfo(int, double*, double*, double*, DevDesc*);/********************************************************//* end of list of required device driver actions *//********************************************************//* Support Routines */static double pixelHeight(drawing d);static double pixelWidth(drawing d);static void SetColor(int, DevDesc *);static void SetFont(int, int, double, DevDesc *);static void SetLinetype(int, double, DevDesc *);void ProcessEvents();int isX11DeviceActive() {if (activedevice) return 1;else return 0;}void SaveX11DeviceAsGif(char *fn){image img;int l;if (!activedevice) {askok("No device active");return;}l = strlen(fn);if ((l < 5) || strcmpi(&fn[l - 4], ".gif")) {askok("File extension must be .gif");return;}img = bitmaptoimage(activedevice->bm);if (!img) {askok("Insufficient memory");return;}saveimage(img, fn);del(img);}/* Pixel Dimensions (Inches) */static double pixelWidth(drawing obj){return ((double) 1) / devicepixelsx(obj);}static double pixelHeight(drawing obj){return ((double) 1) / devicepixelsy(obj);}/* Font information array. *//* Point sizes: 6-24 *//* Faces: plain, bold, oblique, bold-oblique *//* Symbol may be added later */#define NFONT 19#define MAXFONT 32static int fontnum;static int fontinitdone = 0;static char *fontname[MAXFONT];static int fontstyle[MAXFONT];static void RStandardFonts(){int i;for (i = 0; i < 4; i++)fontname[i] = "Times New Roman";fontname[4] = "Symbol";fontstyle[0] = fontstyle[4] = Plain;fontstyle[1] = Bold;fontstyle[2] = Italic;fontstyle[3] = BoldItalic;fontnum = 5;fontinitdone = 2; /* =fontinit done & fonname must not befree-ed */}static void RFontInit(){int i, notdone;char *opt[2];char oops[256];sprintf(oops, "%s/Rdevga", getenv("R_USER"));notdone = 1;fontnum = 0;fontinitdone = 1;if (!optopenfile(oops)) {sprintf(oops, "%s/etc/Rdevga", getenv("R_HOME"));if (!optopenfile(oops)) {RStandardFonts();notdone = 0;}}while (notdone) {oops[0] = '\0';notdone = optread(opt, ':');if (notdone == 1)sprintf(oops, "[%s] Error at line %d.", optfile(), optline());else if (notdone == 2) {fontname[fontnum] = winstrdup(opt[0]);if (!fontname[fontnum])strcpy(oops, "Insufficient memory. ");else {if (!strcmpi(opt[1], "plain"))fontstyle[fontnum] = Plain;else if (!strcmpi(opt[1], "bold"))fontstyle[fontnum] = Bold;else if (!strcmpi(opt[1], "italic"))fontstyle[fontnum] = Italic;else if (!strcmpi(opt[1], "bold&italic"))fontstyle[fontnum] = BoldItalic;elsesprintf(oops, "Unknown style at line %d. ", optline());fontnum += 1;}}if (oops[0]) {optclosefile();strcat(oops, optfile());strcat(oops, " will be ignored.");askok(oops);for (i = 0; i < fontnum; i++) winfree(fontname);RStandardFonts();notdone = 0;}if (fontnum == MAXFONT) {optclosefile();notdone = 0;}}}static int SetBaseFont(x11Desc *xd){xd->fontface = 1;xd->fontsize = 12;xd->fontangle = 0.0;xd->usefixed = 0;xd->font = gnewfont(xd->gawin, fontname[0], fontstyle[0], 12, 0.0);if (!xd->font) {xd->usefixed = 1;xd->font = xd->fixedfont = FixedFont;if (!xd->fixedfont)return 0;}return 1;}/* Set the font size and face *//* If the font of this size and at that the specified *//* rotation is not present it is loaded. *//* 0 = plain text, 1 = bold *//* 2 = oblique, 3 = bold-oblique */#define SMALLEST 8#define LARGEST 64static void SetFont(int face, int size, double rot, DevDesc *dd){x11Desc *xd = (x11Desc *) dd->deviceSpecific;if (face < 1 || face > fontnum)face = 1;size = 2 * size / 2;if (size < SMALLEST)size = SMALLEST;if (size > LARGEST)size = LARGEST;if (!xd->usefixed &&(size != xd->fontsize || face != xd->fontface ||rot != xd->fontangle)) {del(xd->font); doevent();xd->font = gnewfont(xd->gawin,fontname[face - 1], fontstyle[face - 1],size, rot);if (xd->font) {xd->fontface = face;xd->fontsize = size;xd->fontangle = rot;} else {SetBaseFont(xd);}}}static void SetColor(int color, DevDesc *dd){int r, g, b;x11Desc *xd = (x11Desc *) dd->deviceSpecific;if (color != xd->col) {/* Gamma Correction *//* This is very experimental! */xd->col = color;if (dd->gp.gamma != 1) {r = (int) (255 * pow(R_RED(color) / 255.0, dd->gp.gamma));g = (int) (255 * pow(R_GREEN(color) / 255.0, dd->gp.gamma));b = (int) (255 * pow(R_BLUE(color) / 255.0, dd->gp.gamma));} else {r = R_RED(color);g = R_GREEN(color);b = R_BLUE(color);}xd->fgcolor = rgb(r, g, b);}}/** Some Notes on Line Textures** Line textures are stored as an array of 4-bit integers within* a single 32-bit word. These integers contain the lengths of* lines to be drawn with the pen alternately down and then up.* The device should try to arrange that these values are measured* in points if possible, although pixels is ok on most displays.** If newlty contains a line texture description it is decoded* as follows:** ndash = 0;* for(i=0 ; i<8 && newlty&15 ; i++) {* dashlist[ndash++] = newlty&15;* newlty = newlty>>4;* }* dashlist[0] = length of pen-down segment* dashlist[1] = length of pen-up segment* etc** An integer containing a zero terminates the pattern. Hence* ndash in this code fragment gives the length of the texture* description. If a description contains an odd number of* elements it is replicated to create a pattern with an* even number of elements. (If this is a pain, do something* different its not crucial).** 27/5/98 Paul - change to allow lty and lwd to interact:* the line texture is now scaled by the line width so that,* for example, a wide (lwd=2) dotted line (lty=2) has bigger* dots which are more widely spaced. Previously, such a line* would have "dots" which were wide, but not long, nor widely* spaced.*/static void SetLinetype(int newlty, double nlwd, DevDesc *dd){x11Desc *xd = (x11Desc *) dd->deviceSpecific;int newlwd;newlwd = nlwd;if (newlwd < 1)newlwd = 1;xd->lty = newlty;xd->lwd = newlwd;}/* Callback functions */static void HelpResize(window w,rect r){if (AllDevicesKilled) return;{DevDesc *dd = (DevDesc *) getdata(w);x11Desc *xd = (x11Desc *) dd->deviceSpecific;if (r.width) {if ((xd->windowWidth != r.width) ||((xd->windowHeight != r.height))) {xd->windowWidth = r.width;xd->windowHeight = r.height;xd->resize = 1;}}}}static void HelpClose(window w){if (AllDevicesKilled) return;{DevDesc *dd = (DevDesc *) getdata(w);KillDevice(dd);}}static void HelpExpose(window w,rect r){if (AllDevicesKilled) return;{DevDesc *dd = (DevDesc *) getdata(w);x11Desc *xd = (x11Desc *) dd->deviceSpecific;if (xd->resize) {dd->dp.resize(dd);xd->replaying = 1;playDisplayList(dd);xd->replaying = 0;ProcessEvents();} elseSHOW;}}static void HelpMouseClick(window w, int button, point pt){if (AllDevicesKilled) return;{DevDesc *dd = (DevDesc *) getdata(w);x11Desc *xd = (x11Desc *) dd->deviceSpecific;if (!xd->locator)return;if (button & LeftButton) {gabeep();xd->clicked = 1;xd->px = pt.x;xd->py = pt.y;} elsexd->clicked = 2;}}static void menustop(control m){DevDesc *dd = (DevDesc *) getdata(m);x11Desc *xd = (x11Desc *) dd->deviceSpecific;if (!xd->locator)return;xd->clicked = 2;}void fixslash(char *);static void menugif(control m){DevDesc *dd = (DevDesc *) getdata(m);x11Desc *xd = (x11Desc *) dd->deviceSpecific;char *fn;setuserfilter("Gif files (*.gif)\0*.gif\0\0");fn = askfilesave("Gif file", "");if (!fn) return;fixslash(fn);show(xd->gawin);SaveX11DeviceAsGif(fn);}SEXP R_ParseVector(SEXP,int,int *);static void private_devcopy(DevDesc *dd, char *cmd){x11Desc *xd = (x11Desc *) dd->deviceSpecific;SEXP in, pa;int status;int src = deviceNumber(dd);int old = curDevice();gsetcursor(xd->gawin, WatchCursor);show(xd->gawin);if (old != src)selectDevice(src);PROTECT(in = allocVector(STRSXP, 1));STRING(in)[0] = mkChar(cmd);PROTECT(pa = R_ParseVector(in, 1, &status));eval(pa, R_GlobalEnv);UNPROTECT(2);if (old != src)selectDevice(old);gsetcursor(xd->gawin, ArrowCursor);show(xd->gawin);}static void menups(control m){DevDesc *dd = (DevDesc *) getdata(m);char cmd[256], *fn;setuserfilter("Postscript files (*.ps)\0*.ps\0All files (*.*)\0*.*\0\0");fn = askfilesave("Postscript file", "");if (!fn) return;fixslash(fn);sprintf(cmd, "dev.print(device=postscript,file=\"%s\")", fn);private_devcopy(dd, cmd);}static void menuwm(control m){DevDesc *dd = (DevDesc *) getdata(m);char cmd[512], *fn;setuserfilter("Enhanced metafiles (*.emf)\0*.emf\0All files (*.*)\0*.*\0\0");fn = askfilesave("Enhanced metafiles", "");if (!fn) return;fixslash(fn);sprintf(cmd, "dev.print(device=x11,\"win.metafile:%s\",%f,%f,%d)",fn,(float) GConvertXUnits(1.0, NDC, INCHES, dd),(float) GConvertYUnits(1.0, NDC, INCHES, dd),dd->dp.ps);private_devcopy(dd, cmd);}static void menuclpwm(control m){char cmd[256];DevDesc *dd = (DevDesc *) getdata(m);sprintf(cmd, "dev.print(device=x11,\"win.metafile\",%f,%f,%d)",(float) GConvertXUnits(1.0, NDC, INCHES, dd),(float) GConvertYUnits(1.0, NDC, INCHES, dd),dd->dp.ps);private_devcopy(dd, cmd);}static void menuclpbm(control m){DevDesc *dd = (DevDesc *) getdata(m);x11Desc *xd = (x11Desc *) dd->deviceSpecific;show(xd->gawin);gsetcursor(xd->gawin, WatchCursor);copytoclipboard(xd->bm);gsetcursor(xd->gawin, ArrowCursor);}static void menuprint(control m){DevDesc *dd = (DevDesc *) getdata(m);x11Desc *xd = (x11Desc *) dd->deviceSpecific;DevDesc *ndd = (DevDesc *) malloc(sizeof(DevDesc));if (!ndd) {askok("No enough memory");return;}ndd->displayList = R_NilValue;GInit(&ndd->dp);if (!X11DeviceDriver(ndd, "win.print",GConvertXUnits(1.0, NDC, INCHES, dd),GConvertYUnits(1.0, NDC, INCHES, dd),dd->gp.ps)) {free(ndd);return;}gsetcursor(xd->gawin, WatchCursor);gsetVar(install(".Device"),mkString("win.print"), R_NilValue);addDevice(ndd);initDisplayList(ndd);ndd->displayList = dd->displayList;ndd->dpSaved = dd->dpSaved;playDisplayList(ndd);KillDevice(ndd);gsetcursor(xd->gawin, ArrowCursor);show(xd->gawin);}static void menuclose(control m){DevDesc *dd = (DevDesc *) getdata(m);x11Desc *xd = (x11Desc *) dd->deviceSpecific;HelpClose(xd->gawin);}/* plot history */void copyGPar(GPar *, GPar *);extern SEXP savedDisplayList;extern GPar savedGPar;#define GROWTH 4#define GETDL SEXP vDL=findVar(install(".SavedPlots"), R_NilValue)#define SETDL gsetVar(install(".SavedPlots"), vDL, R_NilValue)#define PLOTHISTORYMAGIC 31415#define pMAGIC (INTEGER(VECTOR(vDL)[0])[0])#define pNUMPLOTS (INTEGER(VECTOR(vDL)[1])[0])#define pMAXPLOTS (INTEGER(VECTOR(vDL)[2])[0])#define pCURRENTPOS (INTEGER(VECTOR(vDL)[3])[0])#define pHISTORY (VECTOR(vDL)[4])#define pCURRENT (VECTOR(pHISTORY)[pCURRENTPOS])#define pCURRENTdl (VECTOR(pCURRENT)[0])#define pCURRENTgp (INTEGER(VECTOR(pCURRENT)[1]))#define pCHECK if ((TYPEOF(vDL)!=VECSXP)||\(TYPEOF(VECTOR(vDL)[0])!=INTSXP) ||\(LENGTH(VECTOR(vDL)[0])!=1) ||\(pMAGIC != PLOTHISTORYMAGIC)) {\askok("History plot seems corrupted");\return;}#define pMOVE(a) {pCURRENTPOS+=a;\if(pCURRENTPOS<0) pCURRENTPOS=0;\if(pCURRENTPOS>pNUMPLOTS-1) pCURRENTPOS=pNUMPLOTS-1;\Replay(dd,vDL);SETDL;}#define pEXIST ((vDL!=R_UnboundValue) && (vDL!=R_NilValue))#define pMUSTEXIST if(!pEXIST){askok("No plot history!");return;}static SEXP NewPlotHistory(int n){SEXP vDL;int i;PROTECT(vDL = allocVector(VECSXP, 5));for (i = 0; i < 4; i++)PROTECT(VECTOR(vDL)[i] = allocVector(INTSXP, 1));PROTECT(pHISTORY = allocVector(VECSXP, n));pMAGIC = PLOTHISTORYMAGIC;pNUMPLOTS = 0;pMAXPLOTS = n;pCURRENTPOS = -1;for (i = 0; i < n; i++)VECTOR(pHISTORY)[i] = R_NilValue;SETDL;UNPROTECT(6);return vDL;}static SEXP GrowthPlotHistory(SEXP vDL){SEXP vOLD;int i, oldNPlots, oldCurrent;PROTECT(vOLD = pHISTORY);oldNPlots = pNUMPLOTS;oldCurrent = pCURRENTPOS;PROTECT(vDL = NewPlotHistory(pMAXPLOTS + GROWTH));for (i = 0; i < oldNPlots; i++)VECTOR(pHISTORY)[i] = VECTOR(vOLD)[i];pNUMPLOTS = oldNPlots;pCURRENTPOS = oldCurrent;SETDL;UNPROTECT(2);return vDL;}static void AddtoPlotHistory(SEXP dl,GPar *gp,int replace){SEXP nDL;int lGPar;int where;GETDL;if (dl == R_NilValue) {askok("Display list is void!!");return;}if (!pEXIST)vDL = NewPlotHistory(GROWTH);else if (!replace && (pNUMPLOTS == pMAXPLOTS))vDL = GrowthPlotHistory(vDL);PROTECT(vDL);pCHECK;if (replace)where = pCURRENTPOS;elsewhere = pNUMPLOTS;PROTECT(dl);PROTECT(nDL = allocVector(VECSXP, 2));VECTOR(nDL)[0] = dl;lGPar = 1 + sizeof(GPar) / sizeof(int);VECTOR(nDL)[1] = allocVector(INTSXP, lGPar);copyGPar(gp, (GPar *) INTEGER(VECTOR(nDL)[1]));VECTOR(pHISTORY)[where] = nDL;pCURRENTPOS = where;if (!replace) pNUMPLOTS += 1;SETDL;UNPROTECT(3);}static void Replay(DevDesc *dd,SEXP vDL){x11Desc *xd = (x11Desc *) dd->deviceSpecific;xd->replaying = 1;dd->displayList = pCURRENTdl;gsetcursor(xd->gawin, WatchCursor);copyGPar((GPar *) pCURRENTgp, &dd->dpSaved);playDisplayList(dd);xd->replaying = 0;if (!dd->displayListOn)initDisplayList(dd);gsetcursor(xd->gawin, ArrowCursor);}static void menurec(control m){DevDesc *dd = (DevDesc *) getdata(m);x11Desc *xd = (x11Desc *) dd->deviceSpecific;if (xd->recording) {xd->recording = 0;uncheck(m);} else {xd->recording = 1;check(m);}}static void menuadd(control m){DevDesc *dd = (DevDesc *) getdata(m);x11Desc *xd = (x11Desc *) dd->deviceSpecific;AddtoPlotHistory(dd->displayList, &dd->dpSaved, 0);xd->needsave = 0;}static void menureplace(control m){DevDesc *dd = (DevDesc *) getdata(m);GETDL;pMUSTEXIST;pCHECK;if (pCURRENTPOS < 0) {askok("No plot to replace!!");return;}AddtoPlotHistory(dd->displayList, &dd->dpSaved, 1);}static void menunext(control m){DevDesc *dd = (DevDesc *) getdata(m);x11Desc *xd = (x11Desc *) dd->deviceSpecific;GETDL;if (xd->needsave) return;pMUSTEXIST;pCHECK;if (pCURRENTPOS!=(pNUMPLOTS - 1)) pMOVE(1);}static void menuprev(control m){DevDesc *dd = (DevDesc*) getdata(m);x11Desc *xd = (x11Desc *) dd->deviceSpecific;GETDL;pMUSTEXIST;pCHECK;if (pNUMPLOTS) {if (xd->recording && xd->needsave && (dd->displayList != R_NilValue)) {AddtoPlotHistory(dd->displayList, &dd->dpSaved, 0);xd->needsave = 0;}pMOVE((xd->needsave) ? 0 : -1);}}static void menuclear(control m){gsetVar(install(".SavedPlots"), R_NilValue, R_NilValue);}static void menugvar(control m){SEXP vDL;char *v = askstring("Name", "");DevDesc *dd = (DevDesc *) getdata(m);if (!v)return;vDL = findVar(install(v), R_GlobalEnv);if (!pEXIST || !pNUMPLOTS) {askok("Variable doesn't exist or doesn't contain any plots!");return;}pCHECK;pCURRENTPOS = 0;Replay(dd, vDL);SETDL;}static void menusvar(control m){char *v;GETDL;pMUSTEXIST;pCHECK;v = askstring("Name", "");if (!v)return;setVar(install(v), vDL, R_GlobalEnv);}static void menuconsole(control m){show(RConsole);}static void CHelpKeyIn(control w,int key){DevDesc *dd = (DevDesc *) getdata(w);x11Desc *xd = (x11Desc *) dd->deviceSpecific;switch (key) {case INS:menuadd(xd->madd);break;case PGUP:menuprev(xd->mprev);break;case PGDN:menunext(xd->mnext);break;}}static void NHelpKeyIn(control w,int key){DevDesc *dd = (DevDesc *) getdata(w);x11Desc *xd = (x11Desc *) dd->deviceSpecific;if (getkeystate() != CtrlKey)return;key = 'A' + key - 1;if (key == 'C')menuclpbm(xd->mclpbm);if (dd->displayList == R_NilValue)return;if (key == 'W')menuclpwm(xd->mclpwm);if (key == 'P')menuprint(xd->mprint);}static void mbarf(control m){DevDesc *dd = (DevDesc *) getdata(m);x11Desc *xd = (x11Desc *) dd->deviceSpecific;GETDL;if (pEXIST) {enable(xd->mnext);enable(xd->mprev);if ((pCURRENTPOS >= 0) && (dd->displayList != R_NilValue))enable(xd->mreplace);elsedisable(xd->mreplace);enable(xd->msvar);enable(xd->mclear);} else {disable(xd->mnext);disable(xd->mprev);disable(xd->mreplace);disable(xd->msvar);disable(xd->mclear);}if (dd->displayList != R_NilValue) {enable(xd->madd);enable(xd->mprint);enable(xd->mgif);enable(xd->mwm);enable(xd->mps);enable(xd->mclpwm);} else {disable(xd->madd);disable(xd->mprint);disable(xd->msubsave);disable(xd->mgif);disable(xd->mwm);disable(xd->mps);disable(xd->mclpwm);}draw(xd->mbar);}/********************************************************//* device_Open is not usually called directly by the *//* graphics engine; it is usually only called from *//* the device-driver entry point. *//* this function should set up all of the device- *//* specific resources for a new device *//* this function is given a new structure for device- *//* specific information AND it must FREE the structure *//* if anything goes seriously wrong *//* NOTE that it is perfectly acceptable for this *//* function to set generic graphics parameters too *//* (i.e., override the generic parameter settings *//* which GInit sets up) all at the author's own risk *//* of course :) *//********************************************************/#define MCHECK(m) {if(!(m)) {del(xd->gawin); return 0;}}static int X11_Open(DevDesc *dd, x11Desc *xd, char *dsp, double w, double h){/* if have to bail out with "error" then must *//* free(dd) and free(xd) */int iw, ih;double dw, dh, d;rect rr;if (!fontinitdone)RFontInit();/* Foreground and Background Colors */xd->bg = dd->dp.bg = R_RGB(255, 255, 255);xd->fg = dd->dp.fg = R_RGB(0, 0, 0);xd->col = dd->dp.col = xd->fg;xd->fgcolor = Black;xd->bgcolor = White;/* Try to create a simple window *//* Want to know about exposures *//* and window-resizes and locations */if (!dsp[0]) {xd->kind = 0;dw = w / pixelWidth(NULL);dh = h / pixelHeight(NULL);if ((dw / devicewidth(NULL)) > 0.85) {d = dh / dw;dw = 0.85 * devicewidth(NULL);dh = d * dw;}if ((dh / deviceheight(NULL)) > 0.85) {d = dw / dh;dh = 0.85 * deviceheight(NULL);dw = d * dh;}iw = dw + 0.5;ih = dh + 0.5;if ((xd->gawin = newwindow("R Graphics",rect(devicewidth(NULL) - iw - 5, 0, iw, ih),Document | StandardWindow | Menubar))) {menu m;gsetcursor(xd->gawin, ArrowCursor);if (ismdi() && (RguiMDI & RW_TOOLBAR)) {int btsize = 24;rect r = rect(2, 2, btsize, btsize);control bt, tb;MCHECK(tb = newtoolbar(btsize + 4));gsetcursor(tb, ArrowCursor);addto(tb);MCHECK(bt = newtoolbutton(cam_image, r, menuclpwm));MCHECK(addtooltip(bt, "Copy to the clipboard as a metafile"));gsetcursor(bt, ArrowCursor);setdata(bt, (void *) dd);r.x += (btsize + 6);MCHECK(bt = newtoolbutton(print_image, r, menuprint));MCHECK(addtooltip(bt, "Print"));gsetcursor(bt, ArrowCursor);setdata(bt, (void *) dd);r.x += (btsize + 6);MCHECK(bt = newtoolbutton(console_image, r, menuconsole));MCHECK(addtooltip(bt, "Return focus to console"));gsetcursor(bt, ArrowCursor);setdata(bt, (void *) dd);r.x += (btsize + 6);MCHECK(xd->stoploc = newtoolbutton(stop_image, r, menustop));MCHECK(addtooltip(xd->stoploc, "Stop locator"));gsetcursor(bt, ArrowCursor);setdata(xd->stoploc,(void *) dd);hide(xd->stoploc);} elsexd->stoploc = NULL;/* First we prepare 'locator' menubar and popup */addto(xd->gawin);MCHECK(xd->mbarloc = newmenubar(NULL));MCHECK(newmenu("Stop"));MCHECK(m = newmenuitem("Stop locator", 0, menustop));setdata(m, (void *) dd);MCHECK(xd->locpopup = newpopup(NULL));MCHECK(m = newmenuitem("Stop", 0, menustop));setdata(m, (void *) dd);MCHECK(newmenuitem("Continue", 0, NULL));/* Normal menubar */MCHECK(xd->mbar = newmenubar(mbarf));MCHECK(m = newmenu("File"));MCHECK(xd->msubsave = newsubmenu(m, "Save as"));MCHECK(xd->mgif = newmenuitem("Gif", 0, menugif));MCHECK(xd->mwm = newmenuitem("Metafile", 0, menuwm));MCHECK(xd->mps = newmenuitem("Postscript", 0, menups));MCHECK(newsubmenu(m, "Copy to the clipboard"));MCHECK(xd->mclpbm = newmenuitem("as a Bitmap\tCTRL+C", 0,menuclpbm));MCHECK(xd->mclpwm = newmenuitem("as a Metafile\tCTRL+W", 0,menuclpwm));addto(m);MCHECK(newmenuitem("-", 0, NULL));MCHECK(xd->mprint = newmenuitem("Print\tCTRL+P", 0, menuprint));MCHECK(newmenuitem("-", 0, NULL));MCHECK(xd->mclose = newmenuitem("close Device", 0, menuclose));MCHECK(newmenu("History"));MCHECK(xd->mrec = newmenuitem("Recording", 0, menurec));MCHECK(newmenuitem("-", 0, NULL));MCHECK(xd->madd = newmenuitem("Add\tINS", 0, menuadd));MCHECK(xd->mreplace = newmenuitem("Replace", 0, menureplace));MCHECK(newmenuitem("-", 0, NULL));MCHECK(xd->mprev = newmenuitem("Previous\tPgUp", 0, menuprev));MCHECK(xd->mnext = newmenuitem("Next\tPgDown", 0, menunext));MCHECK(newmenuitem("-", 0, NULL));MCHECK(xd->msvar = newmenuitem("Save to variable", 0, menusvar));MCHECK(xd->mgvar = newmenuitem("Get from variable", 0, menugvar));MCHECK(newmenuitem("-", 0, NULL));MCHECK(xd->mclear = newmenuitem("Clear history", 0, menuclear));newmdimenu();/* Normal popup */MCHECK(xd->grpopup = newpopup(NULL));MCHECK(m = newmenuitem("Copy as metafile", 0, menuclpwm));setdata(m, (void *) dd);MCHECK(m = newmenuitem("Copy as bitmap", 0, menuclpbm));setdata(m, (void *) dd);MCHECK(newmenuitem("-", 0, NULL));MCHECK(m = newmenuitem("Save as metafile", 0, menuwm));setdata(m, (void *) dd);MCHECK(m = newmenuitem("Save as postscript", 0, menups));setdata(m, (void *) dd);MCHECK(newmenuitem("-", 0, NULL));MCHECK(m = newmenuitem("Print", 0, menuprint));setdata(m, (void *) dd);gchangepopup(xd->gawin, xd->grpopup);}} else if (!strcmp(dsp, "win.print")) {xd->kind = 1;xd->gawin = newprinter(MM_PER_INCH * w, MM_PER_INCH * h);if (!xd->gawin)return 0;} else {/** win.metafile[:] in memory (for the clipboard)* win.metafile:filename* anything else return 0*/char c;char *s = "win.metafile";int ls = strlen(s);int ld = strlen(dsp);int lmm = 100 * MM_PER_INCH;if (ls > ld)return 0;c = dsp[ls];dsp[ls] = '\0';if (strcmp(dsp, s) || (c && (c != ':')))return 0;dsp[ls] = c;xd->gawin = newmetafile((ld > ls) ? &dsp[ls + 1] : "",rect(0, 0, lmm * w, lmm * h));xd->kind = 2;if (!xd->gawin)return 0;}if (!SetBaseFont(xd)) {Rprintf("can't find any fonts\n");del(xd->gawin);return 0;}rr = getrect(xd->gawin);iw = xd->windowWidth = rr.width;ih = xd->windowHeight = rr.height;xd->clip = rr;setdata(xd->gawin, (void *) dd);if (!xd->kind) {xd->bm = newbitmap(iw, ih, getdepth(xd->gawin));if (!xd->bm) {del(xd->gawin);return 0;}addto(xd->gawin);setdata(xd->mbar, (void *) dd);setdata(xd->mgif, (void *) dd);setdata(xd->mps, (void *) dd);setdata(xd->mwm, (void *) dd);setdata(xd->mclpwm, (void *) dd);setdata(xd->mclpbm, (void *) dd);setdata(xd->mprint, (void *) dd);setdata(xd->mclose, (void *) dd);setdata(xd->mrec, (void *) dd);setdata(xd->mprev, (void *) dd);setdata(xd->mnext, (void *) dd);setdata(xd->mgvar, (void *) dd);setdata(xd->madd, (void *) dd);setdata(xd->mreplace, (void *) dd);show(xd->gawin);show(xd->gawin);setresize(xd->gawin, HelpResize);setredraw(xd->gawin, HelpExpose);setmousedown(xd->gawin, HelpMouseClick);setkeydown(xd->gawin, NHelpKeyIn);setkeyaction(xd->gawin, CHelpKeyIn);setclose(xd->gawin, HelpClose);xd->recording = 0;xd->replaying = 0;}numX11Devices++;xd->needsave = 0;return 1;}/********************************************************//* device_StrWidth should return the width of the given *//* string in DEVICE units (GStrWidth is responsible for *//* converting from DEVICE to whatever units the user *//* asked for *//********************************************************/static double X11_StrWidth(char *str, DevDesc *dd){x11Desc *xd = (x11Desc *) dd->deviceSpecific;double a;int size = dd->gp.cex * dd->gp.ps + 0.5;SetFont(dd->gp.font, size, 0.0, dd);a = (double) gstrwidth(xd->gawin, xd->font, str);return a;}/********************************************************//* device_MetricInfo should return height, depth, and *//* width information for the given character in DEVICE *//* units (GMetricInfo does the necessary conversions) *//* This is used for formatting mathematical expressions *//********************************************************//* Character Metric Information *//* Passing c == 0 gets font information */static void X11_MetricInfo(int c, double* ascent, double* descent,double* width, DevDesc *dd){int a, d, w;int size = dd->gp.cex * dd->gp.ps + 0.5;x11Desc *xd = (x11Desc *) dd->deviceSpecific;SetFont(dd->gp.font, size, 0.0, dd);gcharmetric(xd->gawin, xd->font, c, &a, &d, &w);*ascent = (double) a;*descent = (double) d;*width = (double) w;}/********************************************************//* device_Clip is given the left, right, bottom, and *//* top of a rectangle (in DEVICE coordinates). it *//* should have the side-effect that subsequent output *//* is clipped to the given rectangle *//********************************************************/static void X11_Clip(double x0, double x1, double y0, double y1, DevDesc *dd){x11Desc *xd = (x11Desc *) dd->deviceSpecific;xd->clip = rcanon(rpt(pt(x0, y0), pt(x1, y1)));}/********************************************************//* device_Resize is called whenever the device is *//* resized. the function must update the GPar *//* parameters (left, right, bottom, and top) for the *//* new device size *//* this is not usually called directly by the graphics *//* engine because the detection of device resizes *//* (e.g., a window resize) are usually detected by *//* device-specific code (see ProcessEvents in this file)*//********************************************************/static void X11_Resize(DevDesc *dd){x11Desc *xd = (x11Desc *) dd->deviceSpecific;if (xd->resize) {int iw, ih;dd->dp.left = dd->gp.left = 0.0;dd->dp.right = dd->gp.right = iw = xd->windowWidth;dd->dp.bottom = dd->gp.bottom = ih = xd->windowHeight;dd->dp.top = dd->gp.top = 0.0;xd->resize = 0;if (!xd->kind) {del(xd->bm);xd->bm = newbitmap(iw, ih, getdepth(xd->gawin));if (!xd->bm) {askok("Insufficient memory. Killing device");KillDevice(dd);}setbackground(xd->bm, xd->bgcolor);}}}/********************************************************//* device_NewPage is called whenever a new plot requires*//* a new page. a new page might mean just clearing the *//* device (as in this case) or moving to a new page *//* (e.g., postscript) *//********************************************************/static void X11_NewPage(DevDesc *dd){x11Desc *xd = (x11Desc *) dd->deviceSpecific;if ((xd->kind == 1) && xd->needsave)nextpage(xd->gawin);if ((xd->kind == 2) && xd->needsave)error("A metafile can store only one figure.\n");if (!xd->kind) {if (xd->recording && xd->needsave)AddtoPlotHistory(savedDisplayList, &savedGPar, 0);if (xd->replaying)xd->needsave = 0;elsexd->needsave = 1;}xd->bg = dd->dp.bg;xd->bgcolor = rgb(R_RED(xd->bg),R_GREEN(xd->bg),R_BLUE(xd->bg));if (xd->kind)xd->clip = getrect(xd->gawin);elsexd->clip = getrect(xd->bm);DRAW(gfillrect(_d, xd->bgcolor, getrect(_d)));if (xd->kind)xd->needsave = 1;}/********************************************************//* device_Close is called when the device is killed *//* this function is responsible for destroying any *//* device-specific resources that were created in *//* device_Open and for FREEing the device-specific *//* parameters structure *//********************************************************/static void X11_Close(DevDesc *dd){x11Desc *xd = (x11Desc *) dd->deviceSpecific;numX11Devices--;if (activedevice == xd)activedevice = NULL;if (!xd->kind) {hide(xd->gawin);del(xd->bm);}del(xd->font);del(xd->gawin);doevent(); /* this is needed since the GraphApp delayedclean-up *//* ,i.e, I want free all resources NOW */winfree(xd);}/********************************************************//* device_Activate is called when a device becomes the *//* active device. in this case it is used to change the*//* title of a window to indicate the active status of *//* the device to the user. not all device types will *//* do anything *//********************************************************/static unsigned char title[20] = "R Graphics";static void X11_Activate(DevDesc *dd){char t[50];char num[3];x11Desc *xd = (x11Desc *) dd->deviceSpecific;if (xd->replaying || xd->kind)return;strcpy(t, title);strcat(t, ": Device ");sprintf(num, "%i", deviceNumber(dd) + 1);strcat(t, num);strcat(t, " (ACTIVE)");settext(xd->gawin, t);activedevice = xd;}/********************************************************//* device_Deactivate is called when a device becomes *//* inactive. in this case it is used to change the *//* title of a window to indicate the inactive status of *//* the device to the user. not all device types will *//* do anything *//********************************************************/static void X11_Deactivate(DevDesc *dd){char t[50];char num[3];x11Desc *xd = (x11Desc *) dd->deviceSpecific;if (xd->replaying || xd->kind)return;strcpy(t, title);strcat(t, ": Device ");sprintf(num, "%i", deviceNumber(dd) + 1);strcat(t, num);strcat(t, " (inactive)");settext(xd->gawin, t);activedevice = NULL;}/********************************************************//* device_Rect should have the side-effect that a *//* rectangle is drawn with the given locations for its *//* opposite corners. the border of the rectangle *//* should be in the given "fg" colour and the rectangle *//* should be filled with the given "bg" colour *//* if "fg" is NA_INTEGER then no border should be drawn *//* if "bg" is NA_INTEGER then the rectangle should not *//* be filled *//* the locations are in an arbitrary coordinate system *//* and this function is responsible for converting the *//* locations to DEVICE coordinates using GConvert *//********************************************************/static void X11_Rect(double x0, double y0, double x1, double y1,int coords, int bg, int fg, DevDesc *dd){int tmp;x11Desc *xd = (x11Desc *) dd->deviceSpecific;rect r;/* These in-place conversions are ok */TRACEDEVGA("rect");GConvert(&x0, &y0, coords, DEVICE, dd);GConvert(&x1, &y1, coords, DEVICE, dd);if (x0 > x1) {tmp = x0;x0 = x1;x1 = tmp;}if (y0 > y1) {tmp = y0;y0 = y1;y1 = tmp;}r = rect((int) x0, (int) y0, (int) x1 - (int) x0, (int) y1 - (int) y0);if (bg != NA_INTEGER) {SetColor(bg, dd);DRAW(gfillrect(_d, xd->fgcolor, r));}if (fg != NA_INTEGER) {SetColor(fg, dd);SetLinetype(dd->gp.lty, dd->gp.lwd, dd);DRAW(gdrawrect(_d, xd->lwd, xd->lty, xd->fgcolor, r));}}/********************************************************//* device_Circle should have the side-effect that a *//* circle is drawn, centred at the given location, with *//* the given radius. the border of the circle should be*//* drawn in the given "col", and the circle should be *//* filled with the given "border" colour. *//* if "col" is NA_INTEGER then no border should be drawn*//* if "border" is NA_INTEGER then the circle should not *//* be filled *//* the location is in arbitrary coordinates and the *//* function is responsible for converting this to *//* DEVICE coordinates. the radius is given in DEVICE *//* coordinates *//********************************************************/static void X11_Circle(double x, double y, int coords,double r, int col, int border, DevDesc *dd){int ir, ix, iy;x11Desc *xd = (x11Desc *) dd->deviceSpecific;rect rr;TRACEDEVGA("circle");#ifdef OLDir = ceil(r);#elseir = floor(r + 0.5);#endif/* In-place conversion ok */GConvert(&x, &y, coords, DEVICE, dd);ix = (int) x;iy = (int) y;rr = rect(ix - ir, iy - ir, 2 * ir, 2 * ir);if (col != NA_INTEGER) {SetColor(col, dd);DRAW(gfillellipse(_d, xd->fgcolor, rr));}if (border != NA_INTEGER) {SetLinetype(dd->gp.lty, dd->gp.lwd, dd);SetColor(border, dd);DRAW(gdrawellipse(_d, xd->lwd, xd->fgcolor, rr));}}/********************************************************//* device_Line should have the side-effect that a single*//* line is drawn (from x1,y1 to x2,y2) *//* x1, y1, x2, and y2 are in arbitrary coordinates and *//* the function is responsible for converting them to *//* DEVICE coordinates using GConvert *//********************************************************/static void X11_Line(double x1, double y1, double x2, double y2,int coords, DevDesc *dd){int xx1, yy1, xx2, yy2;x11Desc *xd = (x11Desc *) dd->deviceSpecific;/* In-place conversion ok */TRACEDEVGA("line");GConvert(&x1, &y1, coords, DEVICE, dd);GConvert(&x2, &y2, coords, DEVICE, dd);xx1 = (int) x1;yy1 = (int) y1;xx2 = (int) x2;yy2 = (int) y2;SetColor(dd->gp.col, dd),SetLinetype(dd->gp.lty, dd->gp.lwd, dd);DRAW(gdrawline(_d, xd->lwd, xd->lty, xd->fgcolor,pt(xx1, yy1), pt(xx2, yy2)));}/********************************************************//* device_Polyline should have the side-effect that a *//* series of line segments are drawn using the given x *//* and y values *//* the x and y values are in arbitrary coordinates and *//* the function is responsible for converting them to *//* DEVICE coordinates using GConvert *//********************************************************/static void X11_Polyline(int n, double *x, double *y, int coords, DevDesc *dd){point *p = (point *) C_alloc(n, sizeof(point));double devx, devy;int i;x11Desc *xd = (x11Desc *) dd->deviceSpecific;TRACEDEVGA("pl");for ( i = 0; i < n; i++) {devx = x[i];devy = y[i];GConvert(&devx, &devy, coords, DEVICE, dd);p[i].x = (int) devx;p[i].y = (int) devy;}SetColor(dd->gp.col, dd),SetLinetype(dd->gp.lty, dd->gp.lwd, dd);DRAW(gdrawpolyline(_d, xd->lwd, xd->lty, xd->fgcolor, p, n, 0));C_free((char *) p);}/********************************************************//* device_Polygon should have the side-effect that a *//* polygon is drawn using the given x and y values *//* the polygon border should be drawn in the "fg" *//* colour and filled with the "bg" colour *//* if "fg" is NA_INTEGER don't draw the border *//* if "bg" is NA_INTEGER don't fill the polygon *//* the x and y values are in arbitrary coordinates and *//* the function is responsible for converting them to *//* DEVICE coordinates using GConvert *//********************************************************/static void X11_Polygon(int n, double *x, double *y, int coords,int bg, int fg, DevDesc *dd){point *points;double devx, devy;int i;x11Desc *xd = (x11Desc *) dd->deviceSpecific;TRACEDEVGA("plg");points = (point *) C_alloc(n , sizeof(point));if (!points)return;for (i = 0; i < n; i++) {devx = x[i];devy = y[i];GConvert(&devx, &devy, coords, DEVICE, dd);points[i].x = (int) (devx);points[i].y = (int) (devy);}if (bg != NA_INTEGER) {SetColor(bg, dd);DRAW(gfillpolygon(_d, xd->fgcolor, points, n));}if (fg != NA_INTEGER) {SetColor(fg, dd);SetLinetype(dd->gp.lty, dd->gp.lwd, dd);DRAW(gdrawpolygon(_d, xd->lwd, xd->lty, xd->fgcolor, points, n ));}C_free((char *) points);}/********************************************************//* device_Text should have the side-effect that the *//* given text is drawn at the given location *//* the text should be justified according to "xc" and *//* "yc" (0 = left, 0.5 = centre, 1 = right) *//* and rotated according to rot (degrees) *//* the location is in an arbitrary coordinate system *//* and this function is responsible for converting the *//* location to DEVICE coordinates using GConvert *//********************************************************/static double deg2rad = 0.01745329251994329576;static void X11_Text(double x, double y, int coords,char *str, double xc, double yc, double rot, DevDesc *dd){int size;double pixs, xl, yl;x11Desc *xd = (x11Desc *) dd->deviceSpecific;size = dd->gp.cex * dd->gp.ps + 0.5;GConvert(&x, &y, coords, DEVICE, dd);SetFont(dd->gp.font, size, 0.0, dd);if (xc != 0.0 || yc != 0) {xl = X11_StrWidth(str, dd);yl = GConvertYUnits(1, CHARS, DEVICE, dd);x += -xc * xl * cos(deg2rad * rot) +yc * yl * sin(deg2rad * rot);y -= -xc * xl * sin(deg2rad * rot) -yc * yl * cos(deg2rad * rot);}pixs = fontascent(xd->font) + fontdescent(xd->font) - 1;if ((rot <= 45) || ((rot > 135) && (rot <= 225)) || (rot > 315))y -= pixs;elsex -= pixs;SetFont(dd->gp.font, size, rot, dd);SetColor(dd->gp.col, dd);#ifdef NOCLIPTEXTgsetcliprect(xd->gawin, getrect(xd->gawin));gdrawstr(xd->gawin, xd->font, xd->fgcolor, pt(x, y), str);if (!xd->kind) {gsetcliprect(xd->bm, getrect(xd->bm));gdrawstr(xd->bm, xd->font, xd->fgcolor, pt(x, y), str);}#elseDRAW(gdrawstr(_d, xd->font, xd->fgcolor, pt(x,y), str));#endif}/********************************************************//* device_Locator should return the location of the next*//* mouse click (in DEVICE coordinates; GLocator is *//* responsible for any conversions) *//* not all devices will do anything (e.g., postscript) *//********************************************************/static int X11_Locator(double *x, double *y, DevDesc *dd){x11Desc *xd = (x11Desc *) dd->deviceSpecific;if (xd->kind)return 0;xd->locator = 1;xd->clicked = 0;show(xd->gawin);addto(xd->gawin);gchangemenubar(xd->mbarloc);if (xd->stoploc) {show(xd->stoploc);show(xd->gawin);}gchangepopup(xd->gawin, xd->locpopup);gsetcursor(xd->gawin, CrossCursor);setstatus("Locator is active");while (!xd->clicked) {SHOW;doevent();}addto(xd->gawin);gchangemenubar(xd->mbar);if (xd->stoploc) {hide(xd->stoploc);show(xd->gawin);}gsetcursor(xd->gawin, ArrowCursor);gchangepopup(xd->gawin, xd->grpopup);addto(xd->gawin);setstatus("R Graphics");xd->locator = 0;if (xd->clicked == 1) {*x = xd->px;*y = xd->py;return 1;} elsereturn 0;}/********************************************************//* device_Mode is called whenever the graphics engine *//* starts drawing (mode=1) or stops drawing (mode=1) *//* the device is not required to do anything *//********************************************************//* Set Graphics mode - not needed for X11 */static void X11_Mode(int mode){}/********************************************************//* i don't know what this is for and i can't find it *//* being used anywhere, but i'm loath to kill it in *//* case i'm missing something important *//********************************************************//* Hold the Picture Onscreen - not needed for X11 */static void X11_Hold(DevDesc *dd){}/********************************************************//* the device-driver entry point is given a device *//* description structure that it must set up. this *//* involves several important jobs ... *//* (1) it must ALLOCATE a new device-specific parameters*//* structure and FREE that structure if anything goes *//* wrong (i.e., it won't report a successful setup to *//* the graphics engine (the graphics engine is NOT *//* responsible for allocating or freeing device-specific*//* resources or parameters) *//* (2) it must initialise the device-specific resources *//* and parameters (mostly done by calling device_Open) *//* (3) it must initialise the generic graphical *//* parameters that are not initialised by GInit (because*//* only the device knows what values they should have) *//* see Graphics.h for the official list of these *//* (4) it may reset generic graphics parameters that *//* have already been initialised by GInit (although you *//* should know what you are doing if you do this) *//* (5) it must attach the device-specific parameters *//* structure to the device description structure *//* e.g., dd->deviceSpecfic = (void *) xd; *//* (6) it must FREE the overall device description if *//* it wants to bail out to the top-level *//* the graphics engine is responsible for allocating *//* the device description and freeing it in most cases *//* but if the device driver freaks out it needs to do *//* the clean-up itself *//********************************************************//* X11 Device Driver Arguments *//* cpars[0] = display name *//* cpars[1] = paper type (a4, letter, none) *//* npars[0] = width (inches) *//* npars[1] = height (inches) *//* npars[2] = base pointsize *//* npars[3] = paper orientation *//* 1 - portrait *//* 2 - landscape *//* 3 - flexible */intX11DeviceDriver(DevDesc *dd, char *display, double width, double height, double pointsize){/* if need to bail out with some sort of "error" then *//* must free(dd) */int ps;x11Desc *xd;rect rr;/* allocate new device description */if (!(xd = (x11Desc *) winmalloc(sizeof(x11Desc))))return 0;/* from here on, if need to bail out with "error", must also *//* free(xd) *//* Font will load at first use */ps = pointsize;if (ps < 6 || ps > 24)ps = 12;ps = 2 * (ps / 2);xd->fontface = -1;xd->fontsize = -1;dd->dp.font = 1;dd->dp.ps = ps;/* Start the Device Driver and Hardcopy. */if (!X11_Open(dd, xd, display, width, height)) {winfree(xd);return 0;}/* Set up Data Structures */dd->dp.open = X11_Open;dd->dp.close = X11_Close;dd->dp.activate = X11_Activate;dd->dp.deactivate = X11_Deactivate;dd->dp.resize = X11_Resize;dd->dp.newPage = X11_NewPage;dd->dp.clip = X11_Clip;dd->dp.strWidth = X11_StrWidth;dd->dp.text = X11_Text;dd->dp.rect = X11_Rect;dd->dp.circle = X11_Circle;dd->dp.line = X11_Line;dd->dp.polyline = X11_Polyline;dd->dp.polygon = X11_Polygon;dd->dp.locator = X11_Locator;dd->dp.mode = X11_Mode;dd->dp.hold = X11_Hold;dd->dp.metricInfo = X11_MetricInfo;/* set graphics parameters that must be set by device driver *//* Window Dimensions in Pixels */rr = getrect(xd->gawin);dd->dp.left = (xd->kind == 1) ? rr.x : 0; /* left */dd->dp.right = dd->dp.left + xd->windowWidth; /* right */dd->dp.top = (xd->kind == 1) ? rr.y : 0; /* top */dd->dp.bottom = dd->dp.top + xd->windowHeight; /* bottom *//* Nominal Character Sizes in Pixels */dd->dp.cra[0] = fontwidth(xd->font);dd->dp.cra[1] = fontascent(xd->font) + fontdescent(xd->font);/* Character Addressing Offsets *//* These are used to plot a single plotting character *//* so that it is exactly over the plotting point */dd->dp.xCharOffset = 0.50;dd->dp.yCharOffset = 0.40;dd->dp.yLineBias = 0.1;/* Inches per raster unit */dd->dp.ipr[0] = pixelWidth(xd->gawin);dd->dp.ipr[1] = pixelHeight(xd->gawin);/* Device capabilities *//* Clipping is problematic for X11 *//* Graphics is clipped, text is not */dd->dp.canResizePlot = 1;dd->dp.canChangeFont = 0;dd->dp.canRotateText = 1;dd->dp.canResizeText = 1;dd->dp.canClip = 1;/* initialise x11 device description (most of the work *//* has been done in X11_Open) */xd->resize = 0;xd->locator = 0;dd->deviceSpecific = (void *) xd;dd->displayListOn = 1;if (RConsole && xd->kind) show(RConsole);return 1;}