Rev 26266 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/** R : A Computer Language for Statistical Data Analysis* file devQuartz.c* Copyright (C) 2002-2003 Stefano M. Iacus and the R 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., 675 Mass Ave, Cambridge, MA 02139, USA.*/#ifndef __QUARTZ_DEVICE__#define __QUARTZ_DEVICE__#ifdef HAVE_CONFIG_H#include <config.h>#endif#include <Defn.h>#include <Graphics.h>#include <Rdevices.h>#if defined(__APPLE_CC__) && defined(HAVE_AQUA)#define __DEBUGGING__unsigned char Lat2Mac[] = {32, 32, 32, 32, 32, 32, 32, 32, 32, 32,32, 32, 32, 32, 32, 32, 245, 96, 171, 246,247, 248, 249, 250, 172, 32, 251, 252, 32, 253,254, 255, 32, 193, 162, 163, 32, 180, 32, 164,172, 169, 187, 199, 194, 45, 168, 248, 161, 177,32, 32, 171, 181, 166, 225, 252, 32, 188, 200,32, 32, 32, 192, 203, 231, 229, 204, 128, 129,174, 130, 233, 131, 230, 232, 237, 234, 235, 236,32, 132, 241, 238, 239, 205, 133, 32, 175, 244,242, 243, 134, 32, 32, 167, 136, 135, 137, 139,138, 140, 190, 141, 143, 142, 144, 145, 147, 146,148, 149, 32, 150, 152, 151, 153, 155, 154, 214,191, 157, 156, 158, 159, 32, 32, 216};unsigned char Mac2Lat[] = {196, 197, 199, 201, 209, 214, 220, 225, 224, 226,228, 227, 229, 231, 233, 232, 234, 235, 237, 236,238, 239, 241, 243, 242, 244, 246, 245, 250, 249,251, 252, 32, 176, 162, 163, 167, 32, 182, 223,174, 169, 32, 146, 152, 32, 198, 216, 32, 177,32, 32, 165, 181, 32, 32, 32, 32, 32, 170,186, 32, 230, 248, 191, 161, 172, 32, 32, 32,32, 171, 187, 32, 32, 192, 195, 213, 32, 32,32, 32, 32, 32, 96, 39, 247, 32, 255, 32,32, 32, 32, 32, 32, 32, 32, 183, 32, 32,32, 194, 202, 193, 203, 200, 205, 206, 207, 204,211, 212, 32, 210, 218, 219, 217, 144, 147, 148,149, 150, 151, 154, 155, 157, 158, 159};#include <Carbon/Carbon.h>#include <CoreFoundation/CoreFoundation.h>#include <ApplicationServices/ApplicationServices.h>#define R_RED(col) (((col) )&255)#define R_GREEN(col) (((col)>> 8)&255)#define R_BLUE(col) (((col)>>16)&255)#define kRAppSignature '0FFF'extern DL_FUNC ptr_GetQuartzParameters;void GetQuartzParameters(double *width, double *height, double *ps, char *family, Rboolean *antialias, Rboolean *autorefresh) {ptr_GetQuartzParameters(width, height, ps, family, antialias, autorefresh);}#define kOnScreen 0#define kOnFilePDF 1#define kOnFilePICT 2#define kOnClipboard 3#define kOnPrinter 4/***************************************************************************//* 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 {int cex;int windowWidth;int windowHeight;Boolean resize;int Text_Font; /* 0 is system font and 4 is monaco */int fontface; /* Typeface */int fontsize; /* Size in points */int usefixed;int color; /* color */int fill; /* fill color */WindowPtr window;int lineType;int lineWidth;Boolean Antialias; /* Use Antialiasing */Boolean Autorefresh;char *family;CGContextRef context; /* This is the context used by Quartz for OnScreen drawings */CGContextRef auxcontext; /* Additional context used for: cliboard, printer, file */double xscale;double yscale;int where;}QuartzDesc;OSStatus QuartzEventHandler(EventHandlerCallRef inCallRef, EventRef inEvent, void* inUserData);static const EventTypeSpec QuartzEvents[] ={{ kEventClassWindow, kEventWindowClose },{ kEventClassWindow, kEventWindowBoundsChanged }};Rboolean innerQuartzDeviceDriver(NewDevDesc *dd, char *display,double width, double height, double pointsize,char *family, Rboolean antialias, Rboolean autorefresh);Rboolean QuartzDeviceDriver(DevDesc *dd, char *display,double width, double height, double pointsize,char *family, Rboolean antialias, Rboolean autorefresh);OSStatus SetCGContext(QuartzDesc *xd);/* Device primitives */static Rboolean Quartz_Open(NewDevDesc *, QuartzDesc *, char *,double, double);static void Quartz_Close(NewDevDesc *dd);static void Quartz_Activate(NewDevDesc *dd);static void Quartz_Deactivate(NewDevDesc *dd);static void Quartz_Size(double *left, double *right,double *bottom, double *top, NewDevDesc *dd);static void Quartz_NewPage(int fill, double gamma, NewDevDesc *dd);static void Quartz_Clip(double x0, double x1, double y0, double y1,NewDevDesc *dd);static double Quartz_StrWidth(char *str, int font,double cex, double ps, NewDevDesc *dd);static void Quartz_Text(double x, double y, char *str,double rot, double hadj, int col, double gamma, int font,double cex, double ps, NewDevDesc *dd);static void Quartz_Rect(double x0, double y0, double x1, double y1,int col, int fill, double gamma, int lty, double lwd,NewDevDesc *dd);static void Quartz_Circle(double x, double y, double r, int col,int fill, double gamma, int lty, double lwd, NewDevDesc *dd);static void Quartz_Line(double x1, double y1, double x2, double y2,int col, double gamma, int lty, double lwd, NewDevDesc *dd);static void Quartz_Polyline(int n, double *x, double *y, int col,double gamma, int lty, double lwd, NewDevDesc *dd);static void Quartz_Polygon(int n, double *x, double *y, int col, int fill,double gamma, int lty, double lwd, NewDevDesc *dd);static Rboolean Quartz_Locator(double *x, double *y, NewDevDesc *dd);static void Quartz_Mode(int mode, NewDevDesc *dd);static void Quartz_Hold(NewDevDesc *dd);static void Quartz_MetricInfo(int c, int font, double cex, double ps,double* ascent, double* descent, double* width,NewDevDesc *dd);static void Quartz_SetFill(int fill, double gamma, NewDevDesc *dd);static void Quartz_SetStroke(int color, double gamma, NewDevDesc *dd);static void Quartz_SetLineDash(int lty, double lwd, NewDevDesc *dd);static void Quartz_SetLineWidth(double lwd, NewDevDesc *dd);static void Quartz_SetFont(int font, double cex, double ps, NewDevDesc *dd);static CGContextRef GetContext(QuartzDesc *xd);static SEXP gcall;static char *SaveString(SEXP sxp, int offset){char *s;if(!isString(sxp) || length(sxp) <= offset)errorcall(gcall, "invalid string argument");s = R_alloc(strlen(CHAR(STRING_ELT(sxp, offset)))+1, sizeof(char));strcpy(s, CHAR(STRING_ELT(sxp, offset)));return s;}/* Quartz Device Driver Parameters:* ----------------- cf with ../unix/X11/devX11.c* display = display* width = width in inches* height = height in inches* ps = pointsize* family = Postscript fon family name* Antialias = wheter to make antialiasing*/SEXP do_Quartz(SEXP call, SEXP op, SEXP args, SEXP env){NewDevDesc *dev = NULL;GEDevDesc *dd;char *display, *vmax, *family=NULL;char fontfamily[255];double height, width, ps;Rboolean antialias, autorefresh;gcall = call;vmax = vmaxget();display = SaveString(CAR(args), 0);args = CDR(args);width = asReal(CAR(args)); args = CDR(args);height = asReal(CAR(args)); args = CDR(args);if (width <= 0 || height <= 0)errorcall(call, "invalid width or height");ps = asReal(CAR(args)); args = CDR(args);family = SaveString(CAR(args), 0); args = CDR(args);antialias = asLogical(CAR(args)); args = CDR(args);autorefresh = asLogical(CAR(args));R_CheckDeviceAvailable();/* Allocate and initialize the device driver data */BEGIN_SUSPEND_INTERRUPTS {if (!(dev = (NewDevDesc *) calloc(1, sizeof(NewDevDesc))))return 0;/* Do this for early redraw attempts */dev->displayList = R_NilValue;/* Make sure that this is initialised before a GC can occur.* This (and displayList) get protected during GC*/dev->savedSnapshot = R_NilValue;strcpy(fontfamily, family);GetQuartzParameters(&width, &height, &ps, fontfamily, &antialias, &autorefresh);if (!QuartzDeviceDriver((DevDesc *)dev, display, width, height, ps,fontfamily, antialias, autorefresh)) {free(dev);errorcall(call, "unable to start device Quartz\n");}gsetVar(install(".Device"), mkString("quartz"), R_NilValue);dd = GEcreateDevDesc(dev);addDevice((DevDesc*)dd);GEinitDisplayList(dd);} END_SUSPEND_INTERRUPTS;vmaxset(vmax);return R_NilValue;}Rboolean QuartzDeviceDriver(DevDesc *dd, char *display,double width, double height, double pointsize,char *family, Rboolean antialias, Rboolean autorefresh){return innerQuartzDeviceDriver((NewDevDesc *)dd, display,width, height, pointsize, family, antialias, autorefresh);}Rboolean innerQuartzDeviceDriver(NewDevDesc *dd, char *display,double width, double height, double pointsize,char *family, Rboolean antialias, Rboolean autorefresh){QuartzDesc *xd;int ps;Rect rect;OSStatus err;if (!(xd = (QuartzDesc *)malloc(sizeof(QuartzDesc))))return 0;if(!Quartz_Open(dd, xd, display, width, height))return(FALSE);ps = pointsize;if (ps < 6 || ps > 24) ps = 10;ps = 2 * (ps / 2);dd->startps = ps;dd->startfont = 1;dd->startlty = LTY_SOLID;dd->startgamma = 1;dd->newDevStruct = 1;dd->open = Quartz_Open;dd->close = Quartz_Close;dd->activate = Quartz_Activate;dd->deactivate = Quartz_Deactivate;dd->size = Quartz_Size;dd->newPage = Quartz_NewPage;dd->clip = Quartz_Clip;dd->strWidth = Quartz_StrWidth;dd->text = Quartz_Text;dd->rect = Quartz_Rect;dd->circle = Quartz_Circle;dd->line = Quartz_Line;dd->polyline = Quartz_Polyline;dd->polygon = Quartz_Polygon;dd->locator = Quartz_Locator;dd->mode = Quartz_Mode;dd->hold = Quartz_Hold;dd->metricInfo = Quartz_MetricInfo;dd->left = 0;dd->right = xd->windowWidth;dd->bottom = xd->windowHeight;dd->top = 0;dd->xCharOffset = 0.4900;dd->yCharOffset = 0.3333;dd->yLineBias = 0.1;dd->cra[0] = ps / 2;dd->cra[1] = ps;dd->ipr[0] = 1.0 / 72;dd->ipr[1] = 1.0 / 72;dd->canResizePlot = TRUE;dd->canChangeFont = TRUE;dd->canRotateText = TRUE;dd->canResizeText = TRUE;dd->canClip = FALSE;dd->canHAdj = 0;dd->canChangeGamma = FALSE;/* It is used to set the font that you will be used on the postscript anddrawing.*//* There is the place for you to set the default value of the MAC Devices */xd->cex = 1.0;xd->resize = 0;xd->Text_Font = 4; /* initial is monaco */xd->fontface = 0; /* initial is plain text */xd->fontsize = 12; /* initial is 12 size */xd->Antialias = antialias; /* by default Antialias if on */xd->Autorefresh = autorefresh; /* by default it is on */if(family){xd->family = malloc(sizeof(family)+1);strcpy(xd->family,family);}elsexd->family = NULL;xd->where = kOnScreen;err = SetCGContext(xd);/* This scale factor is needed in MetricInfo */xd->xscale = width/72.0;xd->yscale = height/72.0;dd->deviceSpecific = (void *) xd;dd->displayListOn = TRUE;return 1;}OSStatus SetCGContext(QuartzDesc *xd){Rect rect;OSStatus err = noErr;CGRect cgRect;if(xd->context)CGContextRelease(xd->context);if(xd->auxcontext)CGContextRelease(xd->auxcontext);err = CreateCGContextForPort(GetWindowPort(xd->window), &xd->context);/* Translate to QuickDraw coordinate system */GetPortBounds(GetWindowPort(xd->window), &rect);CGContextTranslateCTM(xd->context,0, (float)(rect.bottom - rect.top));/* Be aware that by performing a negative scale in the following line ofcode, your text will also be flipped*/CGContextScaleCTM(xd->context, 1, -1);/* We apply here Antialiasing if necessary */CGContextSetShouldAntialias(xd->context, xd->Antialias);return err;}static Rboolean Quartz_Open(NewDevDesc *dd, QuartzDesc *xd, char *dsp,double wid, double hgt){OSStatus err;WindowRef devWindow = NULL;Rect devBounds;Str255 Title;char buffer[250];int devnum = devNumber((DevDesc *)dd);xd->windowWidth = wid*72;xd->windowHeight = hgt*72;xd->window = NULL;xd->context = NULL;xd->auxcontext = NULL;dd->startfill = R_RGB(255, 255, 255);dd->startcol = R_RGB(0, 0, 0);/* Create a new window with the specified size */SetRect(&devBounds, 400, 400, 400 + xd->windowWidth, 400 + xd->windowHeight ) ;err = CreateNewWindow( kDocumentWindowClass, kWindowStandardHandlerAttribute|kWindowVerticalZoomAttribute | kWindowCollapseBoxAttribute|kWindowResizableAttribute | kWindowCloseBoxAttribute ,& devBounds, & devWindow);sprintf(buffer,"Quartz (%d) - Active",devnum+1);CopyCStringToPascal(buffer,Title);SetWTitle(devWindow, Title);ShowWindow(devWindow);err = InstallWindowEventHandler( devWindow, NewEventHandlerUPP(QuartzEventHandler),GetEventTypeCount(QuartzEvents),QuartzEvents, (void *)devWindow, NULL);if(err != noErr)return(0);xd->window = devWindow;xd->color = xd->fill = NA_INTEGER;xd->resize = false;xd->lineType = 0;xd->lineWidth = 1;return TRUE;}static void Quartz_Close(NewDevDesc *dd){QuartzDesc *xd = (QuartzDesc *) dd->deviceSpecific;if(xd->window)DisposeWindow(xd->window);if(xd->family)free(xd->family);if(xd->context)CGContextRelease(xd->context);if(xd->auxcontext)CGContextRelease(xd->auxcontext);free(xd);}static void Quartz_Activate(NewDevDesc *dd){Str255 Title;char buffer[250];QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;int devnum = devNumber((DevDesc *)dd);OSStatus err;sprintf(buffer,"Quartz (%d) - Active",devnum+1);CopyCStringToPascal(buffer,Title);SetWTitle(xd->window,Title);/*We add a property to the Window each time we activate it.We should only make this the first time we open the device.*/err = SetWindowProperty(xd->window,kRAppSignature,'QRTZ',sizeof(int),&devnum);ShowWindow(xd->window);}static void Quartz_Deactivate(NewDevDesc *dd){Str255 Title;char buffer[250];QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;int devnum = devNumber((DevDesc *)dd);sprintf(buffer,"Quartz (%d) - Not Active",devnum+1);CopyCStringToPascal(buffer,Title);SetWTitle(xd->window,Title);ShowWindow(xd->window);}static void Quartz_Size(double *left, double *right,double *bottom, double *top, NewDevDesc *dd){QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;Rect portRect;GetWindowPortBounds ( xd->window, & portRect ) ;*left = 0.0;*right = portRect.right;*bottom = portRect.bottom;*top = 0.0;xd->windowWidth = *right - *left;xd->windowHeight = *bottom - *top;SetCGContext(xd);return;}void Quartz_ReSizeWin(NewDevDesc *dd){}static CGContextRef GetContext(QuartzDesc *xd){switch(xd->where){case kOnScreen:return(xd->context);break;case kOnFilePDF:return(xd->auxcontext);break;default:return(NULL);break;}}static void Quartz_NewPage(int fill, double gamma, NewDevDesc *dd){QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;CGPoint origin = {0.0, 0.0};CGSize size;CGRect area;size.width = xd->windowWidth;size.height = xd->windowHeight;area.origin = origin;area.size = size;if(fill == NA_INTEGER)fill = R_RGB(255, 255, 255);Quartz_SetFill(fill, gamma, dd);CGContextFillRect( GetContext(xd), area);CGContextFlush( GetContext(xd) ); /* we need to flash it just now */}static void Quartz_Clip(double x0, double x1, double y0, double y1,NewDevDesc *dd){return;}static double Quartz_StrWidth(char *str, int font,double cex, double ps, NewDevDesc *dd){QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;CGPoint position;CGContextSaveGState( GetContext(xd) );CGContextTranslateCTM( GetContext(xd), 0, 0 );CGContextScaleCTM( GetContext(xd), -1, 1);CGContextRotateCTM( GetContext(xd), -1.0 * 3.1416);CGContextSetTextDrawingMode( GetContext(xd), kCGTextInvisible );Quartz_SetFont(font, cex, ps, dd);CGContextShowTextAtPoint( GetContext(xd), 0, 0, str, strlen(str) );position = CGContextGetTextPosition( GetContext(xd) );CGContextRestoreGState( GetContext(xd) );return(position.x);}static void Quartz_SetFont(int font, double cex, double ps, NewDevDesc *dd){QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;int size = cex * ps + 0.5;FMFontFamily CurrFontId;GrafPtr savePort;Str255 CurrFontName;char CurrFont[256];GetPort(&savePort);SetPortWindowPort(xd->window);switch(font){case 5:strcpy(CurrFont,"Symbol");break;default:if(xd->family)strcpy(CurrFont,xd->family);elsestrcpy(CurrFont,"Helvetica");break;}CGContextSelectFont( GetContext(xd), CurrFont, size, kCGEncodingMacRoman);CopyCStringToPascal(CurrFont,CurrFontName);GetFNum(CurrFontName, &CurrFontId);TextSize(size);TextFont(CurrFontId);SetPort(savePort);}static void Quartz_Text(double x, double y, char *str,double rot, double hadj, int col, double gamma, int font,double cex, double ps, NewDevDesc *dd){int len,i;char *buf=NULL;unsigned char tmp;QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;CGContextSaveGState( GetContext(xd) );CGContextTranslateCTM( GetContext(xd), x, y );CGContextScaleCTM( GetContext(xd) , -1, 1);CGContextRotateCTM( GetContext(xd) , (-1.0 + 2*rot/360) * 3.1416);Quartz_SetStroke( col, gamma, dd);CGContextSetTextDrawingMode( GetContext(xd), kCGTextFill );Quartz_SetFill(col, gamma, dd);Quartz_SetFont(font, cex, ps, dd);len = strlen(str);if(font == 5)CGContextShowTextAtPoint( GetContext(xd), 0, 0, str, strlen(str) );else {if( (buf = malloc(len)) != NULL){for(i=0;i <len;i++){tmp = (unsigned char)str[i];if(tmp>127)buf[i] = (char)Lat2Mac[tmp-127-1];elsebuf[i] = str[i];}CGContextShowTextAtPoint( GetContext(xd), 0, 0, buf, len );free(buf);}}CGContextRestoreGState( GetContext(xd) );}static void Quartz_Rect(double x0, double y0, double x1, double y1,int col, int fill, double gamma, int lty, double lwd,NewDevDesc *dd){QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;CGRect rect;CGPoint origin;CGSize size;origin.x = x0;origin.y = y0;size.width = x1-x0;size.height = y1-y0;rect.size = size;rect.origin = origin;CGContextSaveGState( GetContext(xd) );Quartz_SetLineWidth(lwd, dd);Quartz_SetLineDash(lty, lwd, dd);Quartz_SetFill( fill, gamma, dd);CGContextFillRect( GetContext(xd), rect);Quartz_SetStroke( col, gamma, dd);CGContextStrokeRect( GetContext(xd), rect);CGContextRestoreGState( GetContext(xd) );}static void Quartz_Circle(double x, double y, double r, int col,int fill, double gamma, int lty, double lwd, NewDevDesc *dd){QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;CGContextSaveGState( GetContext(xd) );CGContextBeginPath( GetContext(xd) );Quartz_SetLineWidth(lwd, dd);Quartz_SetLineDash(lty, lwd, dd);CGContextAddArc( GetContext(xd), (float)x , (float)y, (float)r, 3.141592654 * 2.0, 0.0, 0);Quartz_SetFill( fill, gamma, dd);CGContextFillPath( GetContext(xd) );Quartz_SetStroke( col, gamma, dd);CGContextAddArc( GetContext(xd), (float)x , (float)y, (float)r, 3.141592654 * 2.0, 0.0, 0);CGContextStrokePath( GetContext(xd) );CGContextRestoreGState( GetContext(xd) );}static void Quartz_Line(double x1, double y1, double x2, double y2,int col, double gamma, int lty, double lwd, NewDevDesc *dd){QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;CGPoint lines[ 2 ];Rect rect;CGContextSaveGState( GetContext(xd) );CGContextBeginPath( GetContext(xd) );lines[0].x = (float)x1;lines[0].y = (float)y1;lines[1].x = (float)x2;lines[1].y = (float)y2;Quartz_SetLineWidth(lwd, dd);Quartz_SetLineDash(lty, lwd, dd);CGContextAddLines( GetContext(xd), &lines[0], 2 );Quartz_SetStroke( col, gamma, dd);CGContextStrokePath( GetContext(xd) );CGContextRestoreGState( GetContext(xd) );}static void Quartz_Polyline(int n, double *x, double *y, int col,double gamma, int lty, double lwd, NewDevDesc *dd){CGPoint *lines;int i;CGrafPtr savedPort, port;QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;lines = (CGPoint *)malloc(sizeof(CGPoint)*n);if(lines == NULL)return;for (i = 0; i < n; i++) {lines[i].x = (float)x[i];lines[i].y = (float)y[i];}CGContextSaveGState( GetContext(xd) );Quartz_SetLineWidth(lwd, dd);Quartz_SetLineDash(lty, lwd, dd);CGContextBeginPath( GetContext(xd) );CGContextAddLines( GetContext(xd), &lines[0], n );Quartz_SetStroke( col, gamma, dd);CGContextStrokePath( GetContext(xd) );CGContextRestoreGState( GetContext(xd) );}static void Quartz_SetLineDash(int newlty, double lwd, NewDevDesc *dd){QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;float dashlist[8];int i, ndash = 0;lwd *= 0.75; /* kludge from postscript/pdf */for(i = 0; i < 8 && newlty & 15 ; i++) {dashlist[ndash++] = (lwd >= 1 ? lwd: 1) * (newlty & 15);newlty = newlty >> 4;}CGContextSetLineDash( GetContext(xd), 0, dashlist, ndash);xd->lineType = newlty;}static void Quartz_SetLineWidth(double lwd, NewDevDesc *dd){QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;if(lwd < 1)lwd=1;xd->lineWidth = lwd;CGContextSetLineWidth( GetContext(xd), lwd );}static void Quartz_SetStroke(int color, double gamma, NewDevDesc *dd){QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;float alpha = 1.0;xd->color = color;if(color == NA_INTEGER)alpha = 0.0;CGContextSetRGBStrokeColor( GetContext(xd), (float)R_RED(color)/255.0, (float)R_GREEN(color)/255.0, (float)R_BLUE(color)/255.0, alpha);}static void Quartz_SetFill(int fill, double gamma, NewDevDesc *dd){QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;float alpha = 1.0;xd->fill = fill;if(fill == NA_INTEGER)alpha = 0.0;CGContextSetRGBFillColor( GetContext(xd), (float)R_RED(fill)/255.0, (float)R_GREEN(fill)/255.0, (float)R_BLUE(fill)/255.0, alpha);}static void Quartz_Polygon(int n, double *x, double *y, int col, int fill,double gamma, int lty, double lwd, NewDevDesc *dd){int i;QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;CGPoint *lines;CGContextSaveGState( GetContext(xd) );CGContextBeginPath( GetContext(xd) );Quartz_SetLineWidth(lwd, dd);Quartz_SetLineDash(lty, lwd, dd);lines = (CGPoint *)malloc(sizeof(CGPoint)*(n+1));if(lines == NULL)return;for (i = 0; i < n; i++) {lines[i].x = (float)x[i];lines[i].y = (float)y[i];}lines[n].x = (float)x[0];lines[n].y = (float)y[0];CGContextAddLines( GetContext(xd), &lines[0], n+1 );Quartz_SetFill( fill, gamma, dd);CGContextFillPath( GetContext(xd) );CGContextAddLines( GetContext(xd), &lines[0], n+1 );Quartz_SetStroke( col, gamma, dd);CGContextStrokePath( GetContext(xd) );CGContextRestoreGState( GetContext(xd) );}static Rboolean Quartz_Locator(double *x, double *y, NewDevDesc *dd){EventRecord event;SInt16 key;Boolean gotEvent;Boolean mouseClick = false;Point myPoint;WindowPtr window;SInt16 partCode;GrafPtr savePort;Cursor arrow ;QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;GetPort(&savePort);SetPortWindowPort(xd->window);SetCursor( GetQDGlobalsArrow ( & arrow ) ) ;while(!mouseClick) {gotEvent = WaitNextEvent( everyEvent, &event, 0, nil);if (event.what == mouseDown) {partCode = FindWindow(event.where, &window);if ((window == (xd->window)) && (partCode == inContent)) {myPoint = event.where;GlobalToLocal(&myPoint);*x = (double)(myPoint.h);*y = (double)(myPoint.v);mouseClick = true;}}if (event.what == keyDown) {key = (event.message & charCodeMask);if (key == 0x0D){SetPort(savePort);return FALSE;}}}SetPort(savePort);return TRUE;}static void Quartz_Mode(int mode, NewDevDesc *dd){QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;if(mode == 0)CGContextFlush( GetContext(xd) );}static void Quartz_Hold(NewDevDesc *dd){return;}#define FixedToFloat(a) ((float)(a) / fixed1)#define FloatToFixed(a) ((Fixed)((float) (a) * fixed1))static void Quartz_MetricInfo(int c, int font, double cex, double ps,double* ascent, double* descent, double* width,NewDevDesc *dd){FMetricRec myFMetric;QuartzDesc *xd = (QuartzDesc *) dd-> deviceSpecific;char testo[2];CGrafPtr savedPort;Rect bounds;CGPoint position;testo[0] = c;testo[1] = '\0';GetPort(&savedPort);SetPort(GetWindowPort(xd->window));Quartz_SetFont(font, cex, ps, dd);if(c==0){FontMetrics(&myFMetric);*ascent = xd->yscale *floor(cex * ps + 0.5) * FixedToFloat(myFMetric.ascent);*descent = xd->yscale*floor(cex * ps + 0.5) * FixedToFloat(myFMetric.descent);} else {CGContextSaveGState( GetContext(xd) );CGContextTranslateCTM( GetContext(xd), 0, 0 );CGContextScaleCTM( GetContext(xd), -1, 1);CGContextRotateCTM( GetContext(xd), -1.0 * 3.1416);CGContextSetTextDrawingMode( GetContext(xd), kCGTextInvisible );Quartz_SetFont(font, cex, ps, dd);CGContextShowTextAtPoint( GetContext(xd), 0, 0, testo, 1 );position = CGContextGetTextPosition( GetContext(xd) );CGContextRestoreGState( GetContext(xd) );QDTextBounds(1,testo,&bounds);*ascent = -bounds.top;*descent = bounds.bottom;*width = bounds.right - bounds.left;*width = position.x;}SetPort(savedPort);return;}OSStatus QuartzEventHandler( EventHandlerCallRef inCallRef, EventRef inEvent, void* inUserData ){OSStatus err = eventNotHandledErr;UInt32 eventKind = GetEventKind( inEvent ), RWinCode, devsize;int devnum;WindowRef EventWindow;EventRef REvent;NewDevDesc *dd;if( GetEventClass(inEvent) != kEventClassWindow)return(err);GetEventParameter(inEvent, kEventParamDirectObject, typeWindowRef, NULL, sizeof(EventWindow),NULL, &EventWindow);if(GetWindowProperty(EventWindow, kRAppSignature, 'QRTZ', sizeof(int), NULL, &devnum) != noErr)return eventNotHandledErr;switch(eventKind){case kEventWindowClose:{KillDevice(GetDevice(devnum));err= noErr;}break;case kEventWindowBoundsChanged:if( (dd = ((GEDevDesc*) GetDevice(devnum))->dev) ){QuartzDesc *xd = (QuartzDesc *) dd-> deviceSpecific;Rect portRect;GetWindowPortBounds ( xd->window, & portRect ) ;if( (xd->windowWidth != portRect.right) || (xd->windowHeight != portRect.bottom) ){dd->size(&(dd->left), &(dd->right), &(dd->bottom), &(dd->top), dd);GEplayDisplayList((GEDevDesc*) GetDevice(devnum));}err = noErr;}break;default:break;}return err;}#elseSEXP do_Quartz(SEXP call, SEXP op, SEXP args, SEXP env){warning("Quartz device not available on this platform\n");return R_NilValue;}#endif /* __APPLE_CC__ && HAVE_AQUA*/#endif /* __QUARTZ_DEVICE__ */