Rev 15321 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/** R : A Computer Language for Statistical Data Analysis* Copyright (C) 1995, 1996 Robert Gentleman and Ross Ihaka* Copyright (C) 1997--2000 Robert Gentleman, Ross Ihaka and the* R Development Core Team** 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA*/#ifdef HAVE_CONFIG_H#include <config.h>#endif/*#define OLD*/#ifdef HAVE_RINT#define R_rint(x) rint(x)#else#define R_rint(x) ((int) x + 0.5)#endif#include <stdio.h>#include <X11/X.h>#include <X11/Xlib.h>#include <X11/Xatom.h>#include <X11/Xutil.h>#include <X11/cursorfont.h>#include <X11/Intrinsic.h>/*-> Xlib.h Xutil.h Xresource.h .. */#include "Defn.h"#include "Graphics.h"#include "Fileio.h" /* R_fopen */#include "rotated.h"/* 'Public' routines from here *//* For the input handlers of the event loop mechanism: */#include "R_ext/eventloop.h"#include "R_ext/Memory.h" /* vmaxget */#include "Rdevices.h"#define R_X11_DEVICE 1#include "devX11.h"#include "R_ext/RX11.h"/********************************************************//* This device driver has been documented so that it be *//* used as a template for new drivers *//********************************************************/#define CURSOR XC_crosshair /* Default cursor */#define MM_PER_INCH 25.4 /* mm -> inch conversion */#define IS_100DPI ((int) (1./pixelHeight() + 0.5) == 100)#define X_BELL_VOLUME 50 /* integer between -100 and 100 for the volumeof the bell in locator. *//********************************************************//* 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 Display *display; /* Display */static int screen; /* Screen */static Window rootwin; /* Root Window */static Visual *visual; /* Visual */static int depth; /* Pixmap depth */static int Vclass; /* Visual class */static X_COLORTYPE model; /* User color model */static int maxcubesize; /* Max colorcube size */static XSetWindowAttributes attributes; /* Window attributes */static Colormap colormap; /* Default color map */static int blackpixel; /* Black */static int whitepixel; /* White */static XContext devPtrContext;static Atom _XA_WM_PROTOCOLS, protocol;static Rboolean displayOpen = FALSE;static Rboolean inclose = FALSE;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 *//********************************************************//********************************************************//* 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 Rboolean X11_Locator(double*, double*, DevDesc*);static void X11_Mode(int, DevDesc*);static void X11_NewPage(DevDesc*);Rboolean X11_Open(DevDesc*, x11Desc*, char*, double, double, double, X_COLORTYPE, int);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, DevDesc*);static void X11_MetricInfo(int, double*, double*, double*, DevDesc*);/*************************************************//* End of list of required device driver actions *//*************************************************//* Support Routines */static XFontStruct *RLoadFont(int, int);static double pixelHeight(void);static double pixelWidth(void);static int SetBaseFont(x11Desc*);static void SetColor(int, DevDesc*);static void SetFont(int, int, DevDesc*);static void SetLinetype(int, double, DevDesc*);/************************//* X11 Color Management *//************************/static double RedGamma = 0.6;static double GreenGamma = 0.6;static double BlueGamma = 0.6;/* Variables Used To Store Colormap Information */static struct { int red; int green; int blue; } RPalette[512];static XColor XPalette[512];static int PaletteSize;/* Monochome Displays : Compute pixel values by converting *//* RGB values to luminance and then thresholding. *//* See: Foley & van Damm. */static void SetupMonochrome(){depth = 1;}static unsigned GetMonochromePixel(int r, int g, int b){if ((int)(0.299 * r + 0.587 * g + 0.114 * b) > 127)return WhitePixel(display, screen);elsereturn BlackPixel(display, screen);}/* Grayscale Displays : Compute pixel values by converting *//* RGB values to luminance. See: Foley & van Damm. */static unsigned GetGrayScalePixel(int r, int g, int b){unsigned int d, dmin = 0xFFFFFFFF;unsigned int dr;int i;unsigned int pixel = 0; /* -Wall */int gray = (0.299 * r + 0.587 * g + 0.114 * b) + 0.0001;for (i = 0; i < PaletteSize; i++) {dr = (RPalette[i].red - gray);d = dr * dr;if (d < dmin) {pixel = XPalette[i].pixel;dmin = d;}}return pixel;}static Rboolean GetGrayPalette(Display *displ, Colormap cmap, int n){int status, i, m;m = 0;i = 0;for (i = 0; i < n; i++) {RPalette[i].red = (i * 0xff) / (n - 1);RPalette[i].green = RPalette[i].red;RPalette[i].blue = RPalette[i].red;/* Gamma correct here */XPalette[i].red = (i * 0xffff) / (n - 1);XPalette[i].green = XPalette[i].red;XPalette[i].blue = XPalette[i].red;status = XAllocColor(displ, cmap, &XPalette[i]);if (status == 0) {XPalette[i].flags = 0;m++;}elseXPalette[i].flags = DoRed|DoGreen|DoBlue;}PaletteSize = n;if (m > 0) {for (i = 0; i < PaletteSize; i++) {if (XPalette[i].flags != 0)XFreeColors(displ, cmap, &(XPalette[i].pixel), 1, 0);}PaletteSize = 0;return FALSE;}else return TRUE;}static void SetupGrayScale(){int res = 0, d;PaletteSize = 0;/* try for 128 grays on an 8-bit display */if (depth > 8) d = depth = 8; else d = depth - 1;/* try (256), 128, 64, 32, 16 grays */while (d >= 4 && !(res = GetGrayPalette(display, colormap, 1 << d)))d--;if (!res) {/* Can't find a sensible grayscale, so revert to monochrome */warning("can't set grayscale: reverting to monochrome");model = MONOCHROME;SetupMonochrome();}}/* PseudoColor Displays : There are two strategies here. *//* 1) allocate a standard color cube and match colors *//* within that based on (weighted) distances in RGB space. *//* 2) allocate colors exactly as they are requested until *//* all color cells are used. Fail with an error message *//* when this happens. */static int RGBlevels[][3] = { /* PseudoColor Palettes */{ 8, 8, 4 },{ 6, 7, 6 },{ 6, 6, 6 },{ 6, 6, 5 },{ 6, 6, 4 },{ 5, 5, 5 },{ 5, 5, 4 },{ 4, 4, 4 },{ 4, 4, 3 },{ 3, 3, 3 },{ 2, 2, 2 }};static int NRGBlevels = sizeof(RGBlevels) / (3 * sizeof(int));static int GetColorPalette(Display *dpy, Colormap cmap, int nr, int ng, int nb){int status, i, m, r, g, b;m = 0;i = 0;for (r = 0; r < nr; r++) {for (g = 0; g < ng; g++) {for (b = 0; b < nb; b++) {RPalette[i].red = (r * 0xff) / (nr - 1);RPalette[i].green = (g * 0xff) / (ng - 1);RPalette[i].blue = (b * 0xff) / (nb - 1);/* Perform Gamma Correction Here */#ifdef OLDXPalette[i].red = (r * 0xffff) / (nr - 1);XPalette[i].green = (g * 0xffff) / (ng - 1);XPalette[i].blue = (b * 0xffff) / (nb - 1);#elseXPalette[i].red = pow(r / (nr - 1.0), RedGamma) * 0xffff;XPalette[i].green = pow(g / (ng - 1.0), GreenGamma) * 0xffff;XPalette[i].blue = pow(b / (nb - 1.0), BlueGamma) * 0xffff;#endif/* End Gamma Correction */status = XAllocColor(dpy, cmap, &XPalette[i]);if (status == 0) {XPalette[i].flags = 0;m++;}elseXPalette[i].flags = DoRed|DoGreen|DoBlue;i++;}}}PaletteSize = nr * ng * nb;if (m > 0) {for (i = 0; i < PaletteSize; i++) {if (XPalette[i].flags != 0)XFreeColors(dpy, cmap, &(XPalette[i].pixel), 1, 0);}PaletteSize = 0;return 0;}elsereturn 1;}static void SetupPseudoColor(){int i, size;PaletteSize = 0;if (model == PSEUDOCOLOR1) {for (i = 0; i < NRGBlevels; i++) {size = RGBlevels[i][0] * RGBlevels[i][1] * RGBlevels[i][2];if (size < maxcubesize && GetColorPalette(display, colormap,RGBlevels[i][0],RGBlevels[i][1],RGBlevels[i][2]))break;}if (PaletteSize == 0) {warning("X11 driver unable to obtain color cube\n reverting to monochrome");model = MONOCHROME;SetupMonochrome();}}else {PaletteSize = 0;}}static unsigned int GetPseudoColor1Pixel(int r, int g, int b){unsigned int d, dmin = 0xFFFFFFFF;unsigned int dr, dg, db;unsigned int pixel;int i;pixel = 0; /* -Wall */for (i = 0; i < PaletteSize; i++) {dr = (RPalette[i].red - r);dg = (RPalette[i].green - g);db = (RPalette[i].blue - b);d = dr * dr + dg * dg + db * db;if (d < dmin) {pixel = XPalette[i].pixel;dmin = d;}}return pixel;}static unsigned int GetPseudoColor2Pixel(int r, int g, int b){int i;/* Search for previously allocated color */for (i = 0; i < PaletteSize ; i++) {if (r == RPalette[i].red &&g == RPalette[i].green &&b == RPalette[i].blue) return XPalette[i].pixel;}/* Attempt to allocate a new color */XPalette[PaletteSize].red = pow(r / 255.0, RedGamma) * 0xffff;XPalette[PaletteSize].green = pow(g / 255.0, GreenGamma) * 0xffff;XPalette[PaletteSize].blue = pow(b / 255.0, BlueGamma) * 0xffff;if (PaletteSize == 256 ||XAllocColor(display, colormap, &XPalette[PaletteSize]) == 0) {error("Error: X11 cannot allocate additional graphics colors.\n""Consider using X11 with colortype=\"pseudo.cube\" or \"gray\".");}RPalette[PaletteSize].red = r;RPalette[PaletteSize].green = g;RPalette[PaletteSize].blue = b;PaletteSize++;return XPalette[PaletteSize - 1].pixel;}static unsigned int GetPseudoColorPixel(int r, int g, int b){if (model == PSEUDOCOLOR1)return GetPseudoColor1Pixel(r, g, b);elsereturn GetPseudoColor2Pixel(r, g, b);}/* Truecolor Displays : Allocate the colors as they are requested */static unsigned int RMask, RShift;static unsigned int GMask, GShift;static unsigned int BMask, BShift;static void SetupTrueColor(){RMask = visual->red_mask;GMask = visual->green_mask;BMask = visual->blue_mask;RShift = 0; while ((RMask & 1) == 0) { RShift++; RMask >>= 1; }GShift = 0; while ((GMask & 1) == 0) { GShift++; GMask >>= 1; }BShift = 0; while ((BMask & 1) == 0) { BShift++; BMask >>= 1; }}static unsigned GetTrueColorPixel(int r, int g, int b){r = pow((r / 255.0), RedGamma) * 255;g = pow((g / 255.0), GreenGamma) * 255;b = pow((b / 255.0), BlueGamma) * 255;return(((r * RMask) / 255) << RShift) |(((g * GMask) / 255) << GShift) |(((b * BMask) / 255) << BShift);}/* Interface for General Visual */static unsigned int GetX11Pixel(int r, int g, int b){switch(model) {case MONOCHROME:return GetMonochromePixel(r, g, b);case GRAYSCALE:return GetGrayScalePixel(r, g, b);case PSEUDOCOLOR1:case PSEUDOCOLOR2:return GetPseudoColorPixel(r, g, b);case TRUECOLOR:return GetTrueColorPixel(r, g, b);default:printf("Unknown Visual\n");}return 0;}static void FreeX11Colors(){int i;if (model == PSEUDOCOLOR2) {for (i = 0; i < PaletteSize; i++)XFreeColors(display, colormap, &(XPalette[i].pixel), 1, 0);PaletteSize = 0;}}static Rboolean SetupX11Color(){if (depth <= 1) {/* On monchome displays we must use black/white */model = MONOCHROME;SetupMonochrome();}else if (Vclass == StaticGray || Vclass == GrayScale) {if (model == MONOCHROME)SetupMonochrome();else {model = GRAYSCALE;SetupGrayScale();}}else if (Vclass == StaticColor) {/* FIXME : Currently revert to mono. *//* Should do the real thing. */model = MONOCHROME;SetupMonochrome();}else if (Vclass == PseudoColor) {if (model == MONOCHROME)SetupMonochrome();else if (model == GRAYSCALE)SetupGrayScale();else {if (model == TRUECOLOR)model = PSEUDOCOLOR2;SetupPseudoColor();}}else if (Vclass == TrueColor) {if (model == MONOCHROME)SetupMonochrome();else if (model == GRAYSCALE)SetupGrayScale();else if (model == PSEUDOCOLOR1 || model == PSEUDOCOLOR2)SetupPseudoColor();elseSetupTrueColor();}else if (Vclass == DirectColor) {/* FIXME : Currently revert to mono. *//* Should do the real thing. */model = MONOCHROME;SetupMonochrome();}else {printf("Unknown Visual\n");return FALSE;}return TRUE;}/* Pixel Dimensions (Inches) */static double pixelWidth(void){double width, widthMM;width = DisplayWidth(display, screen);widthMM = DisplayWidthMM(display, screen);return ((double)widthMM / (double)width) / MM_PER_INCH;}static double pixelHeight(void){double height, heightMM;height = DisplayHeight(display, screen);heightMM = DisplayHeightMM(display, screen);return ((double)heightMM / (double)height) / MM_PER_INCH;}static void handleEvent(XEvent event){caddr_t temp;DevDesc *dd;x11Desc *xd;if (event.xany.type == Expose) {while(XCheckTypedEvent(display, Expose, &event));XFindContext(display, event.xexpose.window,devPtrContext, &temp);dd = (DevDesc *) temp;xd = (x11Desc *) dd->deviceSpecific;if (xd->resize)dd->dp.resize(dd);playDisplayList(dd);}else if (event.type == ConfigureNotify) {XFindContext(display, event.xconfigure.window,devPtrContext, &temp);dd = (DevDesc *) temp;xd = (x11Desc *) dd->deviceSpecific;xd->windowWidth = event.xconfigure.width;xd->windowHeight = event.xconfigure.height;xd->resize = 1;}else if ((event.type == ClientMessage) &&(event.xclient.message_type == _XA_WM_PROTOCOLS))if (!inclose && event.xclient.data.l[0] == protocol) {XFindContext(display, event.xclient.window,devPtrContext, &temp);dd = (DevDesc *) temp;KillDevice(dd);}}static void R_ProcessEvents(void *data){XEvent event;while (displayOpen && XPending(display)) {XNextEvent(display, &event);/* printf("%i\n",event.type); */handleEvent(event);}}#ifdef OLD/* Font information array. *//* Point sizes: 6-24 *//* Faces: plain, bold, oblique, bold-oblique *//* Symbol may be added later */#define NFONT 19static XFontStruct *fontarray[NFONT][5] = {{NULL, NULL, NULL, NULL, NULL}, /* 6 */{NULL, NULL, NULL, NULL, NULL}, /* 7 */{NULL, NULL, NULL, NULL, NULL}, /* 8 */{NULL, NULL, NULL, NULL, NULL}, /* 9 */{NULL, NULL, NULL, NULL, NULL}, /* 10 */{NULL, NULL, NULL, NULL, NULL}, /* 11 */{NULL, NULL, NULL, NULL, NULL}, /* 12 */{NULL, NULL, NULL, NULL, NULL}, /* 13 */{NULL, NULL, NULL, NULL, NULL}, /* 14 */{NULL, NULL, NULL, NULL, NULL}, /* 15 */{NULL, NULL, NULL, NULL, NULL}, /* 16 */{NULL, NULL, NULL, NULL, NULL}, /* 17 */{NULL, NULL, NULL, NULL, NULL}, /* 18 */{NULL, NULL, NULL, NULL, NULL}, /* 19 */{NULL, NULL, NULL, NULL, NULL}, /* 20 */{NULL, NULL, NULL, NULL, NULL}, /* 21 */{NULL, NULL, NULL, NULL, NULL}, /* 22 */{NULL, NULL, NULL, NULL, NULL}, /* 23 */{NULL, NULL, NULL, NULL, NULL}, /* 24 */};static int missingfont[NFONT][5] = {{0, 0, 0, 0, 0}, /* 6 */{0, 0, 0, 0, 0}, /* 7 */{0, 0, 0, 0, 0}, /* 8 */{0, 0, 0, 0, 0}, /* 9 */{0, 0, 0, 0, 0}, /* 10 */{0, 0, 0, 0, 0}, /* 11 */{0, 0, 0, 0, 0}, /* 12 */{0, 0, 0, 0, 0}, /* 13 */{0, 0, 0, 0, 0}, /* 14 */{0, 0, 0, 0, 0}, /* 15 */{0, 0, 0, 0, 0}, /* 16 */{0, 0, 0, 0, 0}, /* 17 */{0, 0, 0, 0, 0}, /* 18 */{0, 0, 0, 0, 0}, /* 19 */{0, 0, 0, 0, 0}, /* 20 */{0, 0, 0, 0, 0}, /* 21 */{0, 0, 0, 0, 0}, /* 22 */{0, 0, 0, 0, 0}, /* 23 */{0, 0, 0, 0, 0}, /* 24 */};static char *fontname_R6 = "-adobe-helvetica-%s-%s-*-*-*-%d-*-*-*-*-*-*";static char *fontname_R5 = "-adobe-helvetica-%s-%s-*-*-*-%d-*-*-*-*-*-*";static char *symbolname = "-adobe-symbol-*-*-*-*-*-%d-*-*-*-*-*-*";static char *fontname;static char *slant[] = {"r", "o"};static char *weight[] = {"medium", "bold"};/* attempt to load a font into the fontarray and return the font *//* if the font is already there don't load it again *//* if can't load the font, return NULL */static XFontStruct *RLoadFont(int face, int size){if (fontarray[size-6][face-1])return fontarray[size-6][face-1];else {char buf[128];XFontStruct *tmp;if (face == 5)sprintf(buf, symbolname, 10 * size);elsesprintf(buf, fontname,weight[(face-1)%2],slant[((face-1)/2)%2], 10 * size);#ifdef DEBUG_X11Rprintf("loading:\n%s\n",buf);#endiftmp = XLoadQueryFont(display, buf);#ifdef DEBUG_X11if (tmp) Rprintf("success\n"); else Rprintf("failure\n");#endifif (tmp)fontarray[size-6][face-1] = tmp;return tmp;}}/* Quiz the server about fonts. *//* 1) Try for 100dpi (X11R6) font *//* 2) Try for *dpi (X11R6) font *//* 3) Try "fixed" and if that fails, bail out */static int SetBaseFont(x11Desc *xd){xd->fontface = 1;xd->fontsize = 12;xd->usefixed = 0;fontname = fontname_R6;xd->font = RLoadFont(xd->fontface, xd->fontsize);if (!xd->font) {fontname = fontname_R5;xd->font = RLoadFont(xd->fontface, xd->fontsize);}if (!xd->font) {xd->usefixed = 1;xd->font = xd->fixedfont = XLoadQueryFont(display, "fixed");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 24static void SetFont(int face, int size, DevDesc *dd){x11Desc *xd = (x11Desc *) dd->deviceSpecific;if (face < 1 || face > 5) 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)) {while(size < dd->gp.ps) {if (fontarray[size-6][face-1]) goto found;if (!missingfont[size-6][face-1]) {fontarray[size-6][face-1] = RLoadFont(face, size);if (fontarray[size-6][face-1]) goto found;missingfont[size-6][face-1] = 1;}size += 2;}while(size >= dd->gp.ps) {if (fontarray[size-6][face-1]) goto found;if (!missingfont[size-6][face-1]) {fontarray[size-6][face-1] = RLoadFont(face, size);if (fontarray[size-6][face-1]) goto found;missingfont[size-6][face-1] = 1;}size -= 2;}size = dd->gp.ps;face = 1;found:xd->font = fontarray[size-6][face-1];xd->fontface = face;xd->fontsize = size;XSetFont(display, xd->wgc, xd->font->fid);}}#else /* not OLD */static char *fontname = "-*-helvetica-%s-%s-*-*-%d-*-*-*-*-*-*-*";static char *symbolname = "-*-symbol-*-*-*-*-%d-*-*-*-*-*-*-*";static char *slant[] = {"r", "o"};static char *weight[] = {"medium", "bold"};/* Bitmap of the Adobe design sizes */static unsigned int adobe_sizes = 0x0403165D;#define MAXFONTS 64#define CLRFONTS 16 /* Number to free when cache runs full */typedef struct {int face, size; XFontStruct *font;} cacheentry;static cacheentry fontcache[MAXFONTS];static int nfonts = 0;static int force_nonscalable = 0; /* for testing */#define ADOBE_SIZE(I) ((I) > 7 && (I) < 35 && (adobe_sizes & (1<<((I)-8))))#define SMALLEST 2static XFontStruct *RLoadFont(int face, int size){int pixelsize, i;cacheentry *f;char buf[128];XFontStruct *tmp;if (size < SMALLEST) size = SMALLEST;face--;/* Here's a 1st class fudge: make sure that the Adobe design sizes8, 10, 11, 12, 14, 17, 18, 20, 24, 25, 34 can be obtained viaan integer "size" at 100 dpi, namely 6, 7, 8, 9, 10, 12, 13,14, 17, 18, 24 points. It's almost y = x * 100/72, but notquite. The constants were found using lm(). --pd */if (IS_100DPI) size = R_rint(size * 1.43 - 0.4);/* search fontcache */for ( i = nfonts ; i-- ; ) {f = &fontcache[i];if ( f->face == face && f->size == size ) return f->font;}/* 'size' is the requested size, 'pixelsize' the size of theactually allocated font*/pixelsize = size;if (face == 4)sprintf(buf, symbolname, pixelsize);elsesprintf(buf, fontname,weight[face & 1],slant[(face & 2)>>1 ], pixelsize);#ifdef DEBUG_X11Rprintf("loading:\n%s\n",buf);#endiftmp = XLoadQueryFont(display, buf);#ifdef DEBUG_X11if (tmp) Rprintf("success\n"); else Rprintf("failure\n");#endifif (!tmp || (force_nonscalable && !ADOBE_SIZE(size)) ){static int near[]={14,14,14,17,17,18,20,20,20,20,24,24,24,25,25,25,25};/* 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 *//* If ADOBE_SIZE(pixelsize) is true at this point thenthe user's system does not have the standard ADOBE font setso we just have to use a "fixed" font.If we can't find a "fixed" font then something is seriouslywrong */if ( ADOBE_SIZE(pixelsize) ) {tmp = XLoadQueryFont(display, "fixed");if (tmp)return tmp;elseerror("Could not find any X11 fonts\nCheck that the Font Path is correct.");}if ( pixelsize < 8 )pixelsize = 8;else if (pixelsize == 9)pixelsize = 8;else if (pixelsize < 30) /* must be at least 13 */pixelsize = near[size-13];elsepixelsize = 34;if (face == 4)sprintf(buf, symbolname, pixelsize);elsesprintf(buf, fontname,weight[face & 1],slant[(face & 2)>>1 ], pixelsize);#ifdef DEBUG_X11Rprintf("loading:\n%s\n",buf);#endiftmp = XLoadQueryFont(display, buf);#ifdef DEBUG_X11if (tmp) Rprintf("success\n"); else Rprintf("failure\n");#endif}if(!tmp && size > 24) {/* final try, size 24 */pixelsize = 24;if (face == 4)sprintf(buf, symbolname, 24);elsesprintf(buf, fontname,weight[face & 1],slant[(face & 2)>>1 ], 24);#ifdef DEBUG_X11Rprintf("loading:\n%s\n",buf);#endiftmp = XLoadQueryFont(display, buf);#ifdef DEBUG_X11if (tmp) Rprintf("success\n"); else Rprintf("failure\n");#endif}if (tmp){f = &fontcache[nfonts++];f->face = face;f->size = size;f->font = tmp;if (fabs( (pixelsize - size)/(double)size ) > 0.1)warning("X11 used font size %d when %d was requested",pixelsize, size);}if (nfonts == MAXFONTS) /* make room in the font cache */{for (i = 0 ; i < CLRFONTS ; i++)XFreeFont(display, fontcache[i].font);for (i = CLRFONTS ; i < MAXFONTS ; i++)fontcache[i - CLRFONTS] = fontcache[i];nfonts -= CLRFONTS;}return tmp;}static int SetBaseFont(x11Desc *xd){xd->fontface = xd->basefontface;xd->fontsize = xd->basefontsize;xd->usefixed = 0;xd->font = RLoadFont(xd->fontface, xd->fontsize);if (!xd->font) {xd->usefixed = 1;xd->font = xd->fixedfont = XLoadQueryFont(display, "fixed");if (!xd->fixedfont)return 0;}return 1;}static void SetFont(int face, int size, DevDesc *dd){x11Desc *xd = (x11Desc *) dd->deviceSpecific;XFontStruct *tmp;if (face < 1 || face > 5) face = 1;if (!xd->usefixed && (size != xd->fontsize || face != xd->fontface)) {tmp = RLoadFont(face, size);if(tmp) {xd->font = tmp;xd->fontface = face;xd->fontsize = size;XSetFont(display, xd->wgc, xd->font->fid);} elseerror("X11 font at size %d could not be loaded", size);}}#endifstatic void SetColor(int color, DevDesc *dd){x11Desc *xd = (x11Desc *) dd->deviceSpecific;if (color != xd->col) {blackpixel = GetX11Pixel(R_RED(color), R_GREEN(color), R_BLUE(color));xd->col = color;XSetState(display, xd->wgc, blackpixel, whitepixel, GXcopy, AllPlanes);}}/* --> See "Notes on Line Textures" in ../include/Graphics.h** 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){static char dashlist[8];int i, ndash, newlwd;x11Desc *xd = (x11Desc *) dd->deviceSpecific;newlwd = nlwd;if (newlwd < 1)/* not less than 1 pixel */newlwd = 1;if (newlty != xd->lty || newlwd != xd->lwd) {xd->lty = newlty;xd->lwd = newlwd;if (newlty == 0) {/* special hack for lty = 0 -- only for X11 */XSetLineAttributes(display,xd->wgc,newlwd,LineSolid,CapRound,JoinRound);}else {ndash = 0;for(i = 0 ; i < 8 && (newlty != 0); i++) {int j = newlty & 15;if (j == 0) j = 1; /* Or we die with an X Error *//* scale line texture for line width */j = j*newlwd;/* make sure that scaled line texture *//* does not exceed X11 storage limits */if (j > 255) j=255;dashlist[ndash++] = j;newlty = newlty >> 4;}XSetDashes(display, xd->wgc, 0, dashlist, ndash);XSetLineAttributes(display,xd->wgc,newlwd,LineOnOffDash,CapButt,JoinRound);}}}/********************************************************//* 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 :) *//********************************************************/static int R_X11Err(Display *dsp, XErrorEvent *event){char buff[1000];XGetErrorText(dsp, event->error_code, buff, 1000);warning("X11 protocol error: %s", buff);return 0;}static int R_X11IOErr(Display *dsp){int fd = ConnectionNumber(display);/*while (nfonts--) XFreeFont(display, fontcache[nfonts].font);nfonts = 0;*/removeInputHandler(&R_InputHandlers,getInputHandler(R_InputHandlers,fd));/*XCloseDisplay(display);displayOpen = FALSE;*/error("X11 fatal IO error: please save work and shut down R");return 0; /* but should never get here */}RbooleanX11_Open(DevDesc *dd, x11Desc *xd, char *dsp, double w, double h,double gamma_fac, X_COLORTYPE colormodel, int maxcube){/* if we have to bail out with "error", then must free(dd) and free(xd) */XEvent event;int iw, ih;X_GTYPE type;char *p = dsp;XGCValues gcv;/* Indicates whether the display is created within this particular call: */Rboolean DisplayOpened = FALSE;if (!strncmp(dsp, "png::", 5)) {FILE *fp;#ifndef HAVE_PNGwarning("No png support in this version of R");return FALSE;#endifif (!(fp = R_fopen(R_ExpandFileName(dsp+5), "w"))) {warning("could not open PNG file `%s'", dsp+6);return FALSE;}xd->fp = fp;type = PNG;p = "";}else if (!strncmp(dsp, "jpeg::", 6)) {FILE *fp;#ifndef HAVE_JPEGwarning("No jpeg support in this version of R");return FALSE;#endifp = strchr(dsp+6, ':'); *p='\0';xd->quality = atoi(dsp+6);if (!(fp = R_fopen(R_ExpandFileName(p+1), "w"))) {warning("could not open JPEG file `%s'", p+1);return FALSE;}xd->fp = fp;type = JPEG;p = "";} else if (!strcmp(dsp, "XImage")) {type = XIMAGE;xd->fp = NULL;p = "";}else type = WINDOW;xd->type = type;/* If there is no server connection, establish one and *//* initialize the X11 device driver data structures. */if (!displayOpen) {if ((display = XOpenDisplay(p)) == NULL)return FALSE;DisplayOpened = TRUE;Rf_setX11Display(display, gamma_fac, colormodel, maxcube, TRUE);displayOpen = TRUE;if(xd->handleOwnEvents == FALSE)addInputHandler(R_InputHandlers, ConnectionNumber(display),R_ProcessEvents, XActivity);}whitepixel = GetX11Pixel(255, 255, 255);blackpixel = GetX11Pixel(0, 0, 0);if (!SetBaseFont(xd)) {Rprintf("can't find X11 font\n");return FALSE;}/* 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;/* Try to create a simple window. *//* We want to know about exposures *//* and window-resizes and locations. */attributes.background_pixel = whitepixel;attributes.border_pixel = blackpixel;attributes.backing_store = Always;attributes.event_mask = ButtonPressMask| ExposureMask| StructureNotifyMask;if (type == WINDOW) {int alreadyCreated = (xd->window != (Window)NULL);if(alreadyCreated == 0) {xd->windowWidth = iw = w/pixelWidth();xd->windowHeight = ih = h/pixelHeight();if ((xd->window = XCreateWindow(display, rootwin,DisplayWidth(display, screen) - iw - 10, 10, iw, ih, 1,DefaultDepth(display, screen),InputOutput,DefaultVisual(display, screen),CWEventMask | CWBackPixel | CWBorderPixel | CWBackingStore,&attributes)) == 0)return FALSE;XChangeProperty( display, xd->window, XA_WM_NAME, XA_STRING,8, PropModeReplace, (unsigned char*)"R Graphics", 13);xd->gcursor = XCreateFontCursor(display, CURSOR);XDefineCursor(display, xd->window, xd->gcursor);/* set up protocols so that window manager sends *//* me an event when user "destroys" window */_XA_WM_PROTOCOLS = XInternAtom(display, "WM_PROTOCOLS", 0);protocol = XInternAtom(display, "WM_DELETE_WINDOW", 0);XSetWMProtocols(display, xd->window, &protocol, 1);}/* Save the devDesc* with the window for event dispatching */XSaveContext(display, xd->window, devPtrContext, (caddr_t) dd);/* Map the window */if(alreadyCreated == 0) {XSelectInput(display, xd->window,ExposureMask | ButtonPressMask | StructureNotifyMask);XMapWindow(display, xd->window);XSync(display, 0);/* Gobble expose events */XNextEvent(display, &event);if (event.xany.type == Expose) {while (event.xexpose.count)XNextEvent(display, &event);}}} else { /* PIXMAP */xd->windowWidth = iw = w;xd->windowHeight = ih = h;if ((xd->window = XCreatePixmap(display, rootwin,iw, ih, DefaultDepth(display, screen))) == 0)return FALSE;/* Save the devDesc* with the window for event dispatching *//* Is this needed? */XSaveContext(display, xd->window, devPtrContext, (caddr_t) dd);xd->npages = 0;}/* Set the graphics context */gcv.arc_mode = ArcChord;xd->wgc = XCreateGC(display, xd->window, GCArcMode, &gcv);XSetState(display, xd->wgc, blackpixel, whitepixel, GXcopy, AllPlanes);XSetFont(display, xd->wgc, xd->font->fid);/* ensure that line drawing is set up at the first *//* graphics call */xd->lty = -1;xd->lwd = -1;numX11Devices++;return TRUE;}/********************************************************//* 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;int size = dd->gp.cex * dd->gp.ps + 0.5;SetFont(dd->gp.font, size, dd);return (double)XTextWidth(xd->font, str, strlen(str));}/********************************************************//* 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 *//* and for exact centering of text (see GText) *//* If the device cannot provide metric information then *//* it MUST return 0.0 for ascent, descent, and width *//********************************************************//* Character Metric Information *//* Passing c == 0 gets font information */static void X11_MetricInfo(int c, double* ascent, double* descent,double* width, DevDesc *dd){int first, last;int size = dd->gp.cex * dd->gp.ps + 0.5;x11Desc *xd = (x11Desc *) dd->deviceSpecific;SetFont(dd->gp.font, size, dd);first = xd->font->min_char_or_byte2;last = xd->font->max_char_or_byte2;if (c == 0) {*ascent = xd->font->ascent;*descent = xd->font->descent;*width = xd->font->max_bounds.width;}else if (first <= c && c <= last) {*ascent = xd->font->per_char[c-first].ascent;*descent = xd->font->per_char[c-first].descent;*width = xd->font->per_char[c-first].width;}else {*ascent = 0;*descent = 0;*width = 0;}}/********************************************************//* 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;if (x0 < x1) {xd->clip.x = (int) x0 ;xd->clip.width = (int) x1 - (int) x0 + 1;}else {xd->clip.x = (int) x1;xd->clip.width = (int) x0 - (int) x1 + 1;}if (y0 < y1) {xd->clip.y = (int) y0;xd->clip.height = (int) y1 - (int) y0 + 1;}else {xd->clip.y = (int) y1;xd->clip.height = (int) y0 - (int) y1 + 1;}XSetClipRectangles(display, xd->wgc, 0, 0, &(xd->clip), 1, Unsorted);#ifdef XSYNCif (xd->type == WINDOW) XSync(display, 0);#endif}/********************************************************//* 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 R_ProcessEvents in this file)*//********************************************************/static void X11_Resize(DevDesc *dd){x11Desc *xd = (x11Desc *) dd->deviceSpecific;if (xd->resize) {dd->dp.left = dd->gp.left = 0.0;dd->dp.right = dd->gp.right = xd->windowWidth;dd->dp.bottom = dd->gp.bottom = xd->windowHeight;dd->dp.top = dd->gp.top = 0.0;xd->resize = 0;}}/********************************************************//* 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->type > WINDOW) {if (xd->npages++)error("attempt to draw second page on pixmap device");xd->bg = dd->dp.bg;SetColor(xd->bg, dd);XFillRectangle(display, xd->window, xd->wgc, 0, 0,xd->windowWidth, xd->windowHeight);return;}FreeX11Colors();if ( (model == PSEUDOCOLOR2) || (xd->bg != dd->dp.bg)) {xd->bg = dd->dp.bg;whitepixel = GetX11Pixel(R_RED(xd->bg),R_GREEN(xd->bg),R_BLUE(xd->bg));XSetWindowBackground(display, xd->window, whitepixel);}XClearWindow(display, xd->window);#ifdef XSYNCXSync(display, 0);#endif}extern int R_SaveAsPng(void *d, int width, int height,unsigned long (*gp)(XImage *, int, int),int bgr, FILE *fp);extern int R_SaveAsJpeg(void *d, int width, int height,unsigned long (*gp)(XImage *, int, int),int bgr, int quality, FILE *outfile);static long knowncols[512];static unsigned long bitgp(XImage *xi, int x, int y){int i, r, g, b;XColor xcol;/* returns the colour of the (x,y) pixel stored as RGB */i = XGetPixel(xi, y, x);switch(model) {case MONOCHROME:return (i==0)?0xFFFFFF:0;case GRAYSCALE:case PSEUDOCOLOR1:case PSEUDOCOLOR2:if (i < 512) {if (knowncols[i] < 0) {xcol.pixel = i;XQueryColor(display, colormap, &xcol);knowncols[i] = ((xcol.red>>8)<<16) | ((xcol.green>>8)<<8)| (xcol.blue>>8);}return knowncols[i];}else {xcol.pixel = i;XQueryColor(display, colormap, &xcol);return ((xcol.red>>8)<<16) | ((xcol.green>>8)<<8) | (xcol.blue>>8);}case TRUECOLOR:r = ((i>>RShift)&RMask) * 255 /(RMask);g = ((i>>GShift)&GMask) * 255 /(GMask);b = ((i>>BShift)&BMask) * 255 /(BMask);return (r<<16) | (g<<8) | b;default:return 0;}return 0; /* not reached, needed for some compilers */}/********************************************************//* 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){#ifdef OLDint i, j;#endifx11Desc *xd = (x11Desc *) dd->deviceSpecific;if (xd->type == WINDOW) {/* process pending events *//* set block on destroy events */inclose = TRUE;R_ProcessEvents((void*) NULL);XFreeCursor(display, xd->gcursor);XDestroyWindow(display, xd->window);XSync(display, 0);} else {if (xd->npages && xd->type != XIMAGE) {int i;XImage *xi;for (i = 0; i < 512; i++) knowncols[i] = -1;xi = XGetImage(display, xd->window, 0, 0,xd->windowWidth, xd->windowHeight,AllPlanes, ZPixmap);if (xd->type == PNG)R_SaveAsPng(xi, xd->windowWidth, xd->windowHeight,bitgp, 0, xd->fp);else if (xd->type == JPEG)R_SaveAsJpeg(xi, xd->windowWidth, xd->windowHeight,bitgp, 0, xd->quality, xd->fp);XDestroyImage(xi);}XFreeGC(display, xd->wgc);XFreePixmap(display, xd->window);if (xd->type != XIMAGE && xd->fp != NULL) fclose(xd->fp);}numX11Devices--;if (numX11Devices == 0) {int fd = ConnectionNumber(display);/* Free Resources Here */#ifdef OLDfor(i=0 ; i<NFONT ; i++)for(j=0 ; j<5 ; j++) {if (fontarray[i][j] != NULL) {XUnloadFont(display, fontarray[i][j]->fid);fontarray[i][j] = NULL;}missingfont[i][j] = 0;}#elsewhile (nfonts--) XFreeFont(display, fontcache[nfonts].font);nfonts = 0;#endifif(xd->handleOwnEvents == FALSE)removeInputHandler(&R_InputHandlers,getInputHandler(R_InputHandlers,fd));XCloseDisplay(display);displayOpen = FALSE;}free(xd);inclose = FALSE;}/********************************************************//* 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 char title[11] = "R Graphics";static void X11_Activate(DevDesc *dd){char t[50];char num[3];x11Desc *xd = (x11Desc *) dd->deviceSpecific;if (xd->type > WINDOW) return;strcpy(t, title);strcat(t, ": Device ");sprintf(num, "%i", deviceNumber(dd)+1);strcat(t, num);strcat(t, " (ACTIVE)");XChangeProperty(display, xd->window, XA_WM_NAME, XA_STRING,8, PropModeReplace, (unsigned char*)t, 50);XSync(display, 0);}/********************************************************//* 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->type > WINDOW) return;strcpy(t, title);strcat(t, ": Device ");sprintf(num, "%i", deviceNumber(dd)+1);strcat(t, num);strcat(t, " (inactive)");XChangeProperty(display, xd->window, XA_WM_NAME, XA_STRING,8, PropModeReplace, (unsigned char*)t, 50);XSync(display, 0);}/********************************************************//* 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;/* These in-place conversions are ok */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;}if (bg != NA_INTEGER) {SetColor(bg, dd);XFillRectangle(display, xd->window, xd->wgc, (int)x0, (int)y0,(int)x1 - (int)x0, (int)y1 - (int)y0);SetColor(bg, dd);XFillRectangle(display, xd->window, xd->wgc, (int)x0, (int)y0,(int)x1 - (int)x0, (int)y1 - (int)y0);}if (fg != NA_INTEGER) {SetColor(fg, dd);SetLinetype(dd->gp.lty, dd->gp.lwd, dd);XDrawRectangle(display, xd->window, xd->wgc, (int)x0, (int)y0,(int)x1 - (int)x0, (int)y1 - (int)y0);}#ifdef XSYNCif (xd->type == WINDOW) XSync(display, 0);#endif}/********************************************************//* 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;#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;if (col != NA_INTEGER) {SetColor(col, dd);XFillArc(display, xd->window, xd->wgc,ix-ir, iy-ir, 2*ir, 2*ir, 0, 23040);}if (border != NA_INTEGER) {SetLinetype(dd->gp.lty, dd->gp.lwd, dd);SetColor(border, dd);XDrawArc(display, xd->window, xd->wgc,ix-ir, iy-ir, 2*ir, 2*ir, 0, 23040);}}/********************************************************//* 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 */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);XDrawLine(display, xd->window, xd->wgc, xx1, yy1, xx2, yy2);#ifdef XSYNCif (xd->type == WINDOW) XSync(display, 0);#endif}/********************************************************//* 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){char *vmax = vmaxget();XPoint *points;double devx, devy;int i, j;x11Desc *xd = (x11Desc *) dd->deviceSpecific;points = (XPoint *) R_alloc(n, sizeof(XPoint));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);}SetColor(dd->gp.col, dd);SetLinetype(dd->gp.lty, dd->gp.lwd, dd);/* Some X servers need npoints < 64K */for(i = 0; i < n; i+= 10000-1) {j = n - i;j = (j <= 10000) ? j: 10000; /* allow for overlap */XDrawLines(display, xd->window, xd->wgc, points+i, j, CoordModeOrigin);}#ifdef XSYNCif (xd->type == WINDOW) XSync(display, 0);#endifvmaxset(vmax);}/********************************************************//* 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){char *vmax = vmaxget();XPoint *points;double devx, devy;int i;x11Desc *xd = (x11Desc *) dd->deviceSpecific;points = (XPoint *) R_alloc(n+1, sizeof(XPoint));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);}devx = x[0]; devy = y[0];GConvert(&devx, &devy, coords, DEVICE, dd);points[n].x = (int)(devx);points[n].y = (int)(devy);if (bg != NA_INTEGER) {SetColor(bg, dd);XFillPolygon(display, xd->window, xd->wgc, points, n, Complex, CoordModeOrigin);#ifdef XSYNCif (xd->type == WINDOW) XSync(display, 0);#endif}if (fg != NA_INTEGER) {SetColor(fg, dd);SetLinetype(dd->gp.lty, dd->gp.lwd, dd);XDrawLines(display, xd->window, xd->wgc, points, n+1, CoordModeOrigin);#ifdef XSYNCif (xd->type == WINDOW) XSync(display, 0);#endif}vmaxset(vmax);}/********************************************************//* device_Text should have the side-effect that the *//* given text is drawn at the given location *//* the text should be 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 void X11_Text(double x, double y, int coords,char *str, double rot, double hadj, DevDesc *dd){int len, size;/* double xl, yl, rot1;*/x11Desc *xd = (x11Desc *) dd->deviceSpecific;size = dd->gp.cex * dd->gp.ps + 0.5;SetFont(dd->gp.font, size, dd);SetColor(dd->gp.col, dd);len = strlen(str);GConvert(&x, &y, coords, DEVICE, dd);XRotDrawString(display, xd->font, rot, xd->window, xd->wgc,(int)x, (int)y, str);#ifdef XSYNCif (xd->type == WINDOW) XSync(display, 0);#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 anythin (e.g., postscript) *//********************************************************/static Rboolean X11_Locator(double *x, double *y, DevDesc *dd){XEvent event;DevDesc *ddEvent;x11Desc *xd = (x11Desc *) dd->deviceSpecific;caddr_t temp;int done = 0;if (xd->type > WINDOW) return 0;R_ProcessEvents((void*)NULL); /* discard pending events */XSync(display, 1);/* handle X events as normal until get a button *//* click in the desired device */while (!done && displayOpen) {XNextEvent(display, &event);if (event.type == ButtonPress) {XFindContext(display, event.xbutton.window,devPtrContext, &temp);ddEvent = (DevDesc *) temp;if (ddEvent == dd) {if (event.xbutton.button == Button1) {*x = event.xbutton.x;*y = event.xbutton.y;/* Make a beep! Was print "\07", but thatmesses up some terminals. */XBell(display, X_BELL_VOLUME);XSync(display, 0);done = 1;}elsedone = 2;}}elsehandleEvent(event);}/* if it was a Button1 succeed, otherwise fail */return (done == 1);}/********************************************************//* 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, DevDesc *dd){#ifdef XSYNCif (mode == 0) XSync(display, 0);#elseXSync(display, 0);#endif}/********************************************************//* 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 : *//* 1) display name *//* 2) width (inches) *//* 3) height (inches) *//* 4) base pointsize *//* 5) gamma correction factor *//* 6) colormodel, *//* see X_COLORTYPE at top of file *//* 7) maxcube */RbooleanX11DeviceDriver(DevDesc *dd,char *disp_name,double width,double height,double pointsize,double gamma_fac,X_COLORTYPE colormodel,int maxcube){x11Desc *xd;xd = Rf_allocX11DeviceDesc(pointsize);dd->dp.font = 1;dd->dp.ps = pointsize;/* Start the Device Driver and Hardcopy. */if (!X11_Open(dd, xd, disp_name, width, height,gamma_fac, colormodel, maxcube)) {free(xd);return FALSE;}Rf_setX11DeviceData(dd, xd);R_ProcessEvents((void*) NULL);return TRUE;}Rboolean R_GetX11Image(int d, XImage **pximage, int *pwidth, int *pheight){SEXP dev = elt(findVar(install(".Devices"), R_NilValue), d);if (TYPEOF(dev) != STRSXP ||!(strcmp(CHAR(STRING_ELT(dev, 0)), "XImage") == 0 ||strncmp(CHAR(STRING_ELT(dev, 0)), "PNG", 3) == 0 ||strncmp(CHAR(STRING_ELT(dev, 0)), "X11", 3) == 0))return FALSE;else {DevDesc *dd = GetDevice(d);x11Desc *xd = (x11Desc *) dd->deviceSpecific;*pximage = XGetImage(display, xd->window, 0, 0,xd->windowWidth, xd->windowHeight,AllPlanes, ZPixmap);*pwidth = xd->windowWidth;*pheight = xd->windowHeight;return TRUE;}}/**Allows callers to retrieve the current Display setting for the process.*/Display*Rf_getX11Display(){return(display);}/**Allows the caller to register the X11 Display object for the process.Typically this will be done when the first X device is created, but this allowsother code to generate the Display object and then register it with the R graphicsengine.In addition to providing the Display, the caller should also give the default value for thegamma factor and also the colormodel and color cube size. See the documentation for the x11()function.Finally, setHandlers controls whether the code establishes handlers for the X errors.*/intRf_setX11Display(Display *dpy, double gamma_fac, X_COLORTYPE colormodel,int maxcube, Rboolean setHandlers){static int alreadyDone = 0;if(alreadyDone) return(TRUE);alreadyDone = 1;display = dpy;#define SETGAMMA#ifdef SETGAMMARedGamma = gamma_fac;GreenGamma = gamma_fac;BlueGamma = gamma_fac;#endifscreen = DefaultScreen(display);rootwin = DefaultRootWindow(display);depth = DefaultDepth(display, screen);visual = DefaultVisual(display, screen);colormap = DefaultColormap(display, screen);Vclass = visual->class;model = colormodel;maxcubesize = maxcube;SetupX11Color();devPtrContext = XUniqueContext();displayOpen = TRUE;/* set error handlers */if(setHandlers == TRUE) {XSetErrorHandler(R_X11Err);XSetIOErrorHandler(R_X11IOErr);}return(TRUE);}/**This fills the general device structure (dd) with the X-specificmethods/functions. It also specifies the current values of thedimensions of the device, and establishes the fonts, line styles, etc.*/intRf_setX11DeviceData(DevDesc *dd, x11Desc *xd){/* 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 required graphics parameters. *//* Window Dimensions in Pixels */dd->dp.left = 0; /* left */dd->dp.right = xd->windowWidth; /* right */dd->dp.bottom = xd->windowHeight; /* bottom */dd->dp.top = 0; /* top *//* Nominal Character Sizes in Pixels */SetBaseFont(xd);dd->dp.cra[0] = xd->font->max_bounds.rbearing -xd->font->min_bounds.lbearing;dd->dp.cra[1] = xd->font->max_bounds.ascent +xd->font->max_bounds.descent;/* 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.4900;dd->dp.yCharOffset = 0.3333;dd->dp.yLineBias = 0.1;/* Inches per raster unit */dd->dp.ipr[0] = pixelWidth();dd->dp.ipr[1] = pixelHeight();/* Device capabilities */dd->dp.canResizePlot = TRUE;dd->dp.canChangeFont = FALSE;dd->dp.canRotateText = TRUE;dd->dp.canResizeText = TRUE;dd->dp.canClip = TRUE;dd->dp.canHAdj = FALSE;dd->dp.ask = FALSE;/* initialise x11 device description *//* (most of the work has been done in X11_Open) */xd->cex = 1.0;xd->srt = 0.0;xd->lty = 0;xd->resize = 0;dd->deviceSpecific = (void *) xd;dd->displayListOn = TRUE;return(TRUE);}/**This allocates an x11Desc instance and sets its default values.*/x11Desc *Rf_allocX11DeviceDesc(double ps){x11Desc *xd;/* allocate new device description */if (!(xd = (x11Desc*)malloc(sizeof(x11Desc))))return FALSE;/* From here on, if we need to bail out with "error", *//* then we must also free(xd). *//* Font will load at first use. */if (ps < 6 || ps > 24) ps = 12;xd->fontface = -1;xd->fontsize = -1;xd->basefontface = 1;xd->basefontsize = ps;xd->handleOwnEvents = FALSE;xd->window = (Window) NULL;return(xd);}extern SEXP RX11_dataentry(SEXP call, SEXP op, SEXP args, SEXP rho);voidR_init_X11(DllInfo *info){/* Ideally, we would not cast X11DeviceDriver here.However, the declaration in R_ext/RX11.h doesn't have accessto the definition of X_COLORTYPE, at present. Thus we needto explicitly cast to avoid compiler warnings.*/R_setX11Routines((R_X11DeviceDriverRoutine) X11DeviceDriver,RX11_dataentry,R_GetX11Image);}