Skip to content

Commit 110a07d

Browse files
author
murrell
committed
add R_eval_with_gd() to "lock" a graphics device during eval() calls during device drawing. When graphics device is locked, any attempt to close/kill the device is ignored. This is to protect against the user closing the device via asyncronous event (e.g., mouse click) during device drawing (which could result in a crash as killing the device left dangling device pointers for any drawing code that was still to complete)
git-svn-id: https://svn.r-project.org/R/trunk@88805 00db46b3-68df-0310-9c12-caf00c1e9a41
1 parent 3582aff commit 110a07d

File tree

17 files changed

+186
-65
lines changed

17 files changed

+186
-65
lines changed

doc/NEWS.Rd

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,13 @@
153153
for allocating (and initialising) \code{DevDesc} structures.
154154
Can be used by external graphics devices.
155155
Satisfies \PR{18292}.
156+
157+
\item New function \code{R_eval_with_gd()} designed to \dQuote{lock} a
158+
graphics device during call to \code{eval()} within device drawing
159+
routines.
160+
External \emph{on-screen} graphics devices should consider using
161+
this function to avoid crashes from the device being closed by the
162+
user (e.g., a mouse click) during drawing.
156163
}
157164
}
158165

src/include/R_ext/GraphicsEngine.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,11 @@ struct _GEDevDesc {
295295
* so that nested calls are not
296296
* recorded on the display list
297297
*/
298+
Rboolean lock; /* The device is locked and unlocked
299+
* within R_eval_with_gd().
300+
* When the device is locked, attempts
301+
* to "kill" the device are ignored.
302+
*/
298303
/*
299304
* Stuff about the device that only graphics systems see.
300305
* The graphics engine has no idea what is in here.
@@ -564,6 +569,9 @@ SEXP CreateAtVector(double axp[], const double usr[], int nint, Rboolean logflag
564569
#define GAxisPars Rf_GAxisPars
565570
void GAxisPars(double *min, double *max, int *n, Rboolean log, int axis);
566571

572+
SEXP Rf_eval_with_gd(SEXP, SEXP, pGEDevDesc);
573+
#define eval_with_gd Rf_eval_with_gd
574+
567575
/* Patterns - from ../../main/patterns.c */
568576
Rboolean R_GE_isPattern(SEXP x);
569577
#define R_GE_linearGradientPattern 1

src/library/grDevices/src/cairo/cairoFns.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ static cairo_pattern_t *CairoTilingPattern(SEXP pattern, pX11Desc xd)
290290
cairo_set_matrix(cc, &tm);
291291
/* Play the pattern function to build the pattern */
292292
R_fcall = PROTECT(lang1(R_GE_tilingPatternFunction(pattern)));
293-
eval(R_fcall, R_GlobalEnv);
293+
Rf_eval_with_gd(R_fcall, R_GlobalEnv, GEcurrentDevice());
294294
UNPROTECT(1);
295295
/* Close group and return resulting pattern */
296296
cairo_tiling = cairo_pop_group(cc);
@@ -453,7 +453,7 @@ static cairo_path_t* CairoCreateClipPath(SEXP clipPath, int index, pX11Desc xd)
453453
cairo_new_path(cc);
454454
/* Play the clipPath function to build the clipping path */
455455
R_fcall = PROTECT(lang1(clipPath));
456-
eval(R_fcall, R_GlobalEnv);
456+
Rf_eval_with_gd(R_fcall, R_GlobalEnv, GEcurrentDevice());
457457
UNPROTECT(1);
458458
/* Apply path fill rule */
459459
switch (R_GE_clipPathFillRule(clipPath)) {
@@ -630,7 +630,7 @@ static cairo_pattern_t *CairoCreateMask(SEXP mask, pX11Desc xd)
630630
cairo_set_operator(cc, CAIRO_OPERATOR_OVER);
631631
/* Play the mask function to build the mask */
632632
R_fcall = PROTECT(lang1(mask));
633-
eval(R_fcall, R_GlobalEnv);
633+
Rf_eval_with_gd(R_fcall, R_GlobalEnv, GEcurrentDevice());
634634
UNPROTECT(1);
635635
/* Close group and return resulting mask */
636636
return cairo_pop_group(cc);
@@ -823,14 +823,14 @@ static cairo_pattern_t *CairoCreateGroup(SEXP src, int op, SEXP dst,
823823
if (dst != R_NilValue) {
824824
/* Play the destination function to draw the destination */
825825
R_fcall = PROTECT(lang1(dst));
826-
eval(R_fcall, R_GlobalEnv);
826+
Rf_eval_with_gd(R_fcall, R_GlobalEnv, GEcurrentDevice());
827827
UNPROTECT(1);
828828
}
829829
/* Set the group operator */
830830
cairo_set_operator(cc, CairoOperator(op));
831831
/* Play the source function to draw the source */
832832
R_fcall = PROTECT(lang1(src));
833-
eval(R_fcall, R_GlobalEnv);
833+
Rf_eval_with_gd(R_fcall, R_GlobalEnv, GEcurrentDevice());
834834
UNPROTECT(1);
835835

836836
/* Close group and return the resulting pattern */
@@ -2137,7 +2137,7 @@ static void CairoStrokePath(SEXP path,
21372137
cairo_new_path(cc);
21382138
/* Play the path function to build the path */
21392139
R_fcall = PROTECT(lang1(path));
2140-
eval(R_fcall, R_GlobalEnv);
2140+
Rf_eval_with_gd(R_fcall, R_GlobalEnv, GEcurrentDevice());
21412141
UNPROTECT(1);
21422142
/* Decrement the "appending" count */
21432143
xd->appending--;
@@ -2176,7 +2176,7 @@ static void CairoFillPath(SEXP path,
21762176
cairo_new_path(cc);
21772177
/* Play the path function to build the path */
21782178
R_fcall = PROTECT(lang1(path));
2179-
eval(R_fcall, R_GlobalEnv);
2179+
Rf_eval_with_gd(R_fcall, R_GlobalEnv, GEcurrentDevice());
21802180
UNPROTECT(1);
21812181
/* Decrement the "appending" count */
21822182
xd->appending--;
@@ -2217,7 +2217,7 @@ static void CairoFillStrokePath(SEXP path,
22172217
cairo_new_path(cc);
22182218
/* Play the path function to build the path */
22192219
R_fcall = PROTECT(lang1(path));
2220-
eval(R_fcall, R_GlobalEnv);
2220+
Rf_eval_with_gd(R_fcall, R_GlobalEnv, GEcurrentDevice());
22212221
UNPROTECT(1);
22222222
/* Decrement the "appending" count */
22232223
xd->appending--;

src/library/grDevices/src/devPS.c

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1687,7 +1687,11 @@ static SEXP getFontDB(const char *fontdbname) {
16871687
/* under lazy loading this will be a promise on first use */
16881688
if(TYPEOF(PSenv) == PROMSXP) {
16891689
PROTECT(PSenv);
1690-
PSenv = eval(PSenv, graphicsNS);
1690+
if (NoDevices()) {
1691+
PSenv = eval(PSenv, graphicsNS);
1692+
} else {
1693+
PSenv = Rf_eval_with_gd(PSenv, graphicsNS, NULL);
1694+
}
16911695
UNPROTECT(2);
16921696
PROTECT(PSenv);
16931697
}
@@ -2694,7 +2698,11 @@ static void PSFileHeader(FILE *fp,
26942698
/* under lazy loading this will be a promise on first use */
26952699
if(TYPEOF(prolog) == PROMSXP) {
26962700
PROTECT(prolog);
2697-
prolog = eval(prolog, graphicsNS);
2701+
if (NoDevices()) {
2702+
prolog = eval(prolog, graphicsNS);
2703+
} else {
2704+
prolog = Rf_eval_with_gd(prolog, graphicsNS, NULL);
2705+
}
26982706
UNPROTECT(1);
26992707
}
27002708
UNPROTECT(1);
@@ -2712,7 +2720,11 @@ static void PSFileHeader(FILE *fp,
27122720
/* under lazy loading this will be a promise on first use */
27132721
if(TYPEOF(prolog) == PROMSXP) {
27142722
PROTECT(prolog);
2715-
prolog = eval(prolog, graphicsNS);
2723+
if (NoDevices()) {
2724+
prolog = eval(prolog, graphicsNS);
2725+
} else {
2726+
prolog = Rf_eval_with_gd(prolog, graphicsNS, NULL);
2727+
}
27162728
UNPROTECT(1);
27172729
}
27182730
UNPROTECT(1);
@@ -5683,7 +5695,7 @@ static int newTiling(SEXP pattern, PDFDesc *pd)
56835695

56845696
/* Evaluate the pattern function to generate the pattern */
56855697
R_fcall = PROTECT(lang1(R_GE_tilingPatternFunction(pattern)));
5686-
eval(R_fcall, R_GlobalEnv);
5698+
Rf_eval_with_gd(R_fcall, R_GlobalEnv, NULL);
56875699
UNPROTECT(1);
56885700

56895701
/* Invalidate current settings so normal drawing enforces its settings */
@@ -5860,7 +5872,7 @@ static int newPath(SEXP path, int type, PDFDesc *pd)
58605872

58615873
/* Evaluate the path function to generate the clipping path */
58625874
R_fcall = PROTECT(lang1(path));
5863-
eval(R_fcall, R_GlobalEnv);
5875+
Rf_eval_with_gd(R_fcall, R_GlobalEnv, NULL);
58645876
UNPROTECT(1);
58655877

58665878
if (type == PDFclipPath) {
@@ -5918,7 +5930,7 @@ static int newMask(SEXP mask, PDFDesc *pd)
59185930

59195931
/* Evaluate the mask function to generate the mask */
59205932
R_fcall = PROTECT(lang1(mask));
5921-
eval(R_fcall, R_GlobalEnv);
5933+
Rf_eval_with_gd(R_fcall, R_GlobalEnv, NULL);
59225934
UNPROTECT(1);
59235935

59245936
/* Invalidate current settings so normal drawing enforces its settings */
@@ -6109,7 +6121,7 @@ static int newGroup(SEXP source, int op, SEXP destination, PDFDesc *pd)
61096121
if (destination != R_NilValue) {
61106122
/* Evaluate the destination function to generate the destination */
61116123
R_fcall = PROTECT(lang1(destination));
6112-
eval(R_fcall, R_GlobalEnv);
6124+
Rf_eval_with_gd(R_fcall, R_GlobalEnv, NULL);
61136125
UNPROTECT(1);
61146126
}
61156127

@@ -6119,7 +6131,7 @@ static int newGroup(SEXP source, int op, SEXP destination, PDFDesc *pd)
61196131

61206132
/* Evaluate the source function to generate the source */
61216133
R_fcall = PROTECT(lang1(source));
6122-
eval(R_fcall, R_GlobalEnv);
6134+
Rf_eval_with_gd(R_fcall, R_GlobalEnv, NULL);
61236135
UNPROTECT(1);
61246136

61256137
/* Some finalisation that endpage does

src/library/grDevices/src/devQuartz.c

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -886,7 +886,7 @@ static QPathRef QuartzCreateClipPath(SEXP clipPath, int index,
886886
CGContextBeginPath(ctx);
887887
/* Play the clipPath function to build the clipping path */
888888
R_fcall = PROTECT(lang1(clipPath));
889-
eval(R_fcall, R_GlobalEnv);
889+
Rf_eval_with_gd(R_fcall, R_GlobalEnv, NULL);
890890
UNPROTECT(1);
891891
/* Save the clipping path (for reuse) */
892892
quartz_clipPath->path = CGContextCopyPath(ctx);
@@ -1068,7 +1068,7 @@ static int QuartzCreateMask(SEXP mask,
10681068

10691069
/* Play the mask function to build the mask */
10701070
R_fcall = PROTECT(lang1(mask));
1071-
eval(R_fcall, R_GlobalEnv);
1071+
Rf_eval_with_gd(R_fcall, R_GlobalEnv, NULL);
10721072
UNPROTECT(1);
10731073

10741074
/* When working with an alpha mask, convert into a grayscale bitmap */
@@ -1263,7 +1263,7 @@ static SEXP QuartzCreateGroup(SEXP src, int op, SEXP dst,
12631263
if (dst != R_NilValue) {
12641264
/* Play the destination function to draw the destination */
12651265
R_fcall = PROTECT(lang1(dst));
1266-
eval(R_fcall, R_GlobalEnv);
1266+
Rf_eval_with_gd(R_fcall, R_GlobalEnv, NULL);
12671267
UNPROTECT(1);
12681268
}
12691269
/* Set the group operator */
@@ -1276,7 +1276,7 @@ static SEXP QuartzCreateGroup(SEXP src, int op, SEXP dst,
12761276
CGContextSetBlendMode(layerContext, QuartzOperator(op));
12771277
/* Play the source function to draw the source */
12781278
R_fcall = PROTECT(lang1(src));
1279-
eval(R_fcall, R_GlobalEnv);
1279+
Rf_eval_with_gd(R_fcall, R_GlobalEnv, NULL);
12801280
UNPROTECT(1);
12811281
}
12821282

@@ -1662,7 +1662,11 @@ const char *RQuartz_LookUpFontName(int fontface, const char *fontfamily)
16621662
PROTECT(ns = R_FindNamespace(ScalarString(mkChar("grDevices"))));
16631663
PROTECT_WITH_INDEX(env = findVar(install(".Quartzenv"), ns), &index);
16641664
if(TYPEOF(env) == PROMSXP)
1665-
REPROTECT(env = eval(env,ns) ,index);
1665+
if (NoDevices()) {
1666+
REPROTECT(env = eval(env, ns), index);
1667+
} else {
1668+
REPROTECT(env = Rf_eval_with_gd(env, ns, NULL), index);
1669+
}
16661670
PROTECT(db = findVar(install(".Quartz.Fonts"), env));
16671671
PROTECT(names = getAttrib(db, R_NamesSymbol));
16681672
if (*fontfamily) {
@@ -2685,7 +2689,7 @@ static SEXP RQuartz_setPattern(SEXP pattern, pDevDesc dd) {
26852689

26862690
/* Play the pattern function to draw the pattern on the pattern layer*/
26872691
SEXP R_fcall = PROTECT(lang1(R_GE_tilingPatternFunction(pattern)));
2688-
eval(R_fcall, R_GlobalEnv);
2692+
Rf_eval_with_gd(R_fcall, R_GlobalEnv, desc2GEdesc(dd));
26892693
UNPROTECT(1);
26902694

26912695
xd->appendingPattern = savedPattern;
@@ -2858,7 +2862,7 @@ static void RQuartz_stroke(SEXP path, const pGEcontext gc, pDevDesc dd)
28582862
CGContextBeginPath(ctx);
28592863
/* Play the path function to build the path */
28602864
R_fcall = PROTECT(lang1(path));
2861-
eval(R_fcall, R_GlobalEnv);
2865+
Rf_eval_with_gd(R_fcall, R_GlobalEnv, desc2GEDesc(dd));
28622866
UNPROTECT(1);
28632867
/* Decrement the "appending" count */
28642868
xd->appending--;
@@ -2894,7 +2898,7 @@ static void RQuartz_fill(SEXP path, int rule, const pGEcontext gc,
28942898
CGContextBeginPath(ctx);
28952899
/* Play the path function to build the path */
28962900
R_fcall = PROTECT(lang1(path));
2897-
eval(R_fcall, R_GlobalEnv);
2901+
Rf_eval_with_gd(R_fcall, R_GlobalEnv, desc2GEDesc(dd));
28982902
UNPROTECT(1);
28992903
/* Decrement the "appending" count */
29002904
xd->appending--;
@@ -2920,7 +2924,7 @@ static void QuartzFillStrokePath(SEXP path, CGContextRef ctx, QuartzDesc *xd)
29202924
CGContextBeginPath(ctx);
29212925
/* Play the path function to build the path */
29222926
R_fcall = PROTECT(lang1(path));
2923-
eval(R_fcall, R_GlobalEnv);
2927+
Rf_eval_with_gd(R_fcall, R_GlobalEnv, NULL);
29242928
UNPROTECT(1);
29252929
/* Decrement the "appending" count */
29262930
xd->appending--;

src/library/grDevices/src/devWindows.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,11 @@ static void init_PS_PDF(void)
380380
if(initS == R_UnboundValue)
381381
error("missing initPSandPDFfonts() in grDevices namespace: this should not happen");
382382
PROTECT(call = lang1(initS));
383-
eval(call, R_GlobalEnv);
383+
if (NoDevices()) {
384+
eval(call, R_GlobalEnv);
385+
} else {
386+
Rf_eval_with_gd(call, R_GlobalEnv, NULL);
387+
}
384388
UNPROTECT(1);
385389
}
386390

@@ -641,7 +645,12 @@ static char* translateFontFamily(const char* family) {
641645
PROTECT_WITH_INDEX(windowsenv = findVar(install(".WindowsEnv"),
642646
graphicsNS), &xpi);
643647
if(TYPEOF(windowsenv) == PROMSXP)
644-
REPROTECT(windowsenv = eval(windowsenv, graphicsNS), xpi);
648+
if (NoDevices()) {
649+
REPROTECT(windowsenv = eval(windowsenv, graphicsNS), xpi);
650+
} else {
651+
REPROTECT(windowsenv = Rf_eval_with_gd(windowsenv, graphicsNS,
652+
NULL), xpi);
653+
}
645654
PROTECT(fontdb = findVar(install(".Windows.Fonts"), windowsenv));
646655
PROTECT(fontnames = getAttrib(fontdb, R_NamesSymbol));
647656
nfonts = LENGTH(fontdb);

src/library/grDevices/src/devices.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,17 @@ SEXP devset(SEXP args)
109109
SEXP devoff(SEXP args)
110110
{
111111
checkArity_length;
112+
int devNum = INTEGER(CAR(args))[0];
113+
/* Check device number is valid (64 is max num devices) */
114+
if (devNum > 0 && devNum < 64) {
115+
pGEDevDesc gdd = GEgetDevice(devNum - 1);
116+
/* Check device is valid */
117+
if (gdd && gdd->lock) {
118+
/* Force unlock so that dev.off() always works */
119+
warning(_("Killing locked device"));
120+
gdd->lock = FALSE;
121+
}
122+
}
112123
killDevice(INTEGER(CAR(args))[0] - 1);
113124
return R_NilValue;
114125
}

src/library/grid/src/clippath.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ SEXP resolveClipPath(SEXP path, pGEDevDesc dd)
3030
setGridStateElement(dd, GSS_RESOLVINGPATH, ScalarLogical(TRUE));
3131
PROTECT(resolveFn = findFun(install("resolveClipPath"), R_gridEvalEnv));
3232
PROTECT(R_fcall = lang2(resolveFn, path));
33-
result = eval(R_fcall, R_gridEvalEnv);
33+
result = Rf_eval_with_gd(R_fcall, R_gridEvalEnv, dd);
3434
setGridStateElement(dd, GSS_RESOLVINGPATH, ScalarLogical(FALSE));
3535
UNPROTECT(2);
3636
return result;

src/library/grid/src/gpar.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ static SEXP resolveFill(SEXP pattern, int i)
285285
INTEGER(index)[0] = i + 1;
286286
PROTECT(resolveFn = findFun(install("resolveFill"), R_gridEvalEnv));
287287
PROTECT(R_fcall = lang3(resolveFn, pattern, index));
288-
result = eval(R_fcall, R_gridEvalEnv);
288+
result = Rf_eval_with_gd(R_fcall, R_gridEvalEnv, NULL);
289289
UNPROTECT(3);
290290
return result;
291291
}
@@ -295,7 +295,7 @@ static SEXP unresolveFill(SEXP pattern)
295295
SEXP unresolveFn, R_fcall, result;
296296
PROTECT(unresolveFn = findFun(install("unresolveFill"), R_gridEvalEnv));
297297
PROTECT(R_fcall = lang2(unresolveFn, pattern));
298-
result = eval(R_fcall, R_gridEvalEnv);
298+
result = Rf_eval_with_gd(R_fcall, R_gridEvalEnv, NULL);
299299
UNPROTECT(2);
300300
return result;
301301
}

0 commit comments

Comments
 (0)