Skip to content

Commit 8a2962e

Browse files
steve-sansalond
authored andcommitted
AWT Graphics: close the device when window is closed
(cherry picked from commit 52a899f)
1 parent 4fc0d04 commit 8a2962e

File tree

3 files changed

+75
-17
lines changed

3 files changed

+75
-17
lines changed

com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/WindowDevice.java

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,14 @@
2424

2525
import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
2626
import com.oracle.truffle.r.library.fastrGrid.device.awt.JFrameDevice;
27+
import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
28+
import com.oracle.truffle.r.runtime.RCaller;
2729
import com.oracle.truffle.r.runtime.RError;
2830
import com.oracle.truffle.r.runtime.RError.Message;
2931
import com.oracle.truffle.r.runtime.context.RContext;
32+
import com.oracle.truffle.r.runtime.data.RFunction;
33+
import com.oracle.truffle.r.runtime.data.RPromise;
34+
import com.oracle.truffle.r.runtime.env.REnvironment;
3035

3136
/**
3237
* Contains code specific to FastR device that shows the graphical output interactively in a window.
@@ -45,6 +50,7 @@ public static GridDevice createWindowDevice(int width, int height) {
4550
RContext ctx = RContext.getInstance();
4651
if (ctx.hasExecutor()) {
4752
frameDevice.setResizeListener(() -> redrawAll(ctx));
53+
frameDevice.setCloseListener(() -> devOff(ctx));
4854
} else {
4955
noSchedulingSupportWarning();
5056
}
@@ -56,14 +62,44 @@ public static RError awtNotSupported() {
5662
}
5763

5864
private static void redrawAll(RContext ctx) {
59-
if (ctx.hasExecutor()) {
65+
if (!ctx.hasExecutor()) {
66+
// to be robust we re-check the executor availability
67+
return;
68+
}
69+
ctx.schedule(() -> {
70+
Object prev = ctx.getEnv().getContext().enter();
71+
GridContext.getContext(ctx).evalInternalRFunction("redrawAll");
72+
ctx.getEnv().getContext().leave(prev);
73+
});
74+
}
75+
76+
private static void devOff(RContext ctx) {
77+
if (!ctx.hasExecutor()) {
6078
// to be robust we re-check the executor availability
61-
ctx.schedule(() -> {
62-
Object prev = ctx.getEnv().getContext().enter();
63-
GridContext.getContext(ctx).evalInternalRFunction("redrawAll");
64-
ctx.getEnv().getContext().leave(prev);
65-
});
79+
return;
80+
}
81+
ctx.schedule(() -> {
82+
Object prev = ctx.getEnv().getContext().enter();
83+
RFunction devOffFun = getDevOffFunction(ctx);
84+
if (devOffFun != null) {
85+
RContext.getEngine().evalFunction(devOffFun, REnvironment.baseEnv(ctx).getFrame(), RCaller.topLevel, true, null);
86+
} else {
87+
RError.warning(RError.NO_CALLER, Message.GENERIC, "Could not locate grDevices::dev.off to close the window device.");
88+
}
89+
ctx.getEnv().getContext().leave(prev);
90+
});
91+
}
92+
93+
private static RFunction getDevOffFunction(RContext ctx) {
94+
Object grDevices = ctx.stateREnvironment.getNamespaceRegistry().get("grDevices");
95+
if (!(grDevices instanceof REnvironment)) {
96+
return null;
97+
}
98+
Object devOff = ((REnvironment) grDevices).get("dev.off");
99+
if (devOff instanceof RPromise) {
100+
devOff = PromiseHelperNode.evaluateSlowPath((RPromise) devOff);
66101
}
102+
return devOff instanceof RFunction ? (RFunction) devOff : null;
67103
}
68104

69105
private static void noSchedulingSupportWarning() {

com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/awt/JFrameDevice.java

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,15 @@
4242
import javax.imageio.ImageIO;
4343
import javax.swing.JFrame;
4444
import javax.swing.JPanel;
45+
import javax.swing.SwingUtilities;
4546

4647
import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext;
4748
import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
4849
import com.oracle.truffle.r.library.fastrGrid.device.ImageSaver;
4950

5051
/**
5152
* This device paints everything into an image, which is painted into a AWT component in its
52-
* {@code paint} method.
53+
* {@code paint} method. Note that this class is not thread safe.
5354
*/
5455
public final class JFrameDevice implements GridDevice, ImageSaver {
5556

@@ -64,14 +65,26 @@ public final class JFrameDevice implements GridDevice, ImageSaver {
6465
private Graphics2DDevice inner;
6566
private boolean isOnHold = false;
6667

67-
private FastRFrame currentFrame;
68+
/**
69+
* Value of this field is set from the AWT thread, any code using it in the main thread should
70+
* first check if it is not {@code null}.
71+
*/
72+
private volatile FastRFrame currentFrame;
73+
/**
74+
* The closing operation invokes {@code dev.off()} to close the device on the R side (remove it
75+
* from {@code .Devices} etc.), but that eventually calls into {@link #close()} where we need to
76+
* know that the AWT window is being closed by the user and so we do not need to close it.
77+
*/
78+
private volatile boolean isClosing = false;
6879
private Runnable onResize;
6980
private Runnable onClose;
7081

7182
public JFrameDevice(int width, int height) {
72-
currentFrame = new FastRFrame(new FastRPanel(width, height));
73-
openGraphics2DDevice(currentFrame.fastRComponent.getWidth(), currentFrame.fastRComponent.getHeight());
83+
openGraphics2DDevice(width, height);
7484
componentImage = image;
85+
SwingUtilities.invokeLater(() -> {
86+
currentFrame = new FastRFrame(new FastRPanel(width, height));
87+
});
7588
}
7689

7790
@Override
@@ -110,7 +123,9 @@ public void flush() {
110123
@Override
111124
public void close() throws DeviceCloseException {
112125
disposeGraphics2DDevice();
113-
currentFrame.dispose();
126+
if (!isClosing && currentFrame != null) {
127+
currentFrame.dispose();
128+
}
114129
componentImage = null;
115130
}
116131

@@ -231,17 +246,21 @@ private void resize(int newWidth, int newHeight) {
231246
}
232247

233248
private void ensureOpen() {
234-
if (!currentFrame.isVisible()) {
249+
if (currentFrame != null && !currentFrame.isVisible()) {
235250
int width = inner.getWidthAwt();
236251
int height = inner.getHeightAwt();
237-
currentFrame = new FastRFrame(new FastRPanel(width, height));
252+
// Note: the assumption is that this class is single threaded
238253
disposeGraphics2DDevice();
239254
openGraphics2DDevice(width, height);
255+
currentFrame = null;
256+
SwingUtilities.invokeLater(() -> {
257+
currentFrame = new FastRFrame(new FastRPanel(width, height));
258+
});
240259
}
241260
}
242261

243262
private void repaint() {
244-
if (!isOnHold) {
263+
if (!isOnHold && currentFrame != null) {
245264
currentFrame.repaint();
246265
}
247266
}
@@ -312,6 +331,7 @@ class FastRFrame extends JFrame {
312331
FastRFrame(FastRPanel fastRComponent) throws HeadlessException {
313332
super("FastR");
314333
this.fastRComponent = fastRComponent;
334+
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
315335
addListeners();
316336
getContentPane().add(fastRComponent);
317337
pack();
@@ -320,10 +340,12 @@ class FastRFrame extends JFrame {
320340
}
321341

322342
private void addListeners() {
323-
addWindowFocusListener(new WindowAdapter() {
343+
addWindowListener(new WindowAdapter() {
324344
@Override
325345
public void windowClosing(WindowEvent e) {
326-
if (onClose != null) {
346+
super.windowClosing(e);
347+
if (!isClosing && onClose != null) {
348+
isClosing = true;
327349
onClose.run();
328350
}
329351
}

com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1062,7 +1062,7 @@ protected String getSearchName() {
10621062
/**
10631063
* When a function is invoked a {@link Function} environment may be created in response to the R
10641064
* {@code environment()} base package function, and it will have an associated frame. We hide
1065-
* the creation of {@link Function} environments to ensure the <i>at most one>/i> invariant and
1065+
* the creation of {@link Function} environments to ensure the <i>at most one</i> invariant and
10661066
* store the value in the frame immediately.
10671067
*/
10681068
public static final class Function extends REnvironment {

0 commit comments

Comments
 (0)