diff --git a/.classpath b/.classpath index 61c34d64..846c5e76 100644 --- a/.classpath +++ b/.classpath @@ -5,8 +5,12 @@ - - + + + + + + diff --git a/.gitignore b/.gitignore index 6e756686..53e0869c 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,6 @@ misc/windowsexecutablestub/Release *.sdf *.suo linux/ + +local.properties + diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..26d33521 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/ant.xml b/.idea/ant.xml new file mode 100644 index 00000000..a2a47698 --- /dev/null +++ b/.idea/ant.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..ebc8004b --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..35eb1ddf --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.project b/.project index 2ff7c3d9..79e62f8b 100644 --- a/.project +++ b/.project @@ -20,4 +20,15 @@ org.eclipse.jdt.core.javanature org.python.pydev.pythonNature + + + 1642294714551 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + diff --git a/Base - Launch Mode in Eclipse.launch b/Base - Launch Mode in Eclipse.launch index e561a978..fead4b7f 100644 --- a/Base - Launch Mode in Eclipse.launch +++ b/Base - Launch Mode in Eclipse.launch @@ -1,7 +1,7 @@ - + @@ -12,37 +12,35 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - - - - - + + + + + + + - + diff --git a/Base - Sketch Runner First.launch b/Base - Sketch Runner First.launch index dc466d79..2d663d80 100644 --- a/Base - Sketch Runner First.launch +++ b/Base - Sketch Runner First.launch @@ -1,29 +1,33 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HOWTO-HACK.txt b/HOWTO-HACK.txt index 54e9902c..5fb20355 100644 --- a/HOWTO-HACK.txt +++ b/HOWTO-HACK.txt @@ -44,8 +44,8 @@ the original repo: You'll need source for processing and processing-video. - $ git clone --depth=1 https://github.com/processing/processing.git - $ git clone --depth=1 https://github.com/processing/processing-video.git + $ git clone https://github.com/processing/processing4.git + $ git clone https://github.com/processing/processing-video.git If you are a Linux user, you'll also have to create a directory named `linux` inside your `processing.py` directory. @@ -67,7 +67,7 @@ Configuring Eclipse ------------------- If you want to modify the code, and you're an Eclipse user, you can "Import -existing project into workspace" a few of times, once for processing.py +existing project into workspace" a few times, once for processing.py itself (which lives in the processing.py top level directory), and once each for processing.core, processing-java, processing-app, and processing-video, which you'll find in the directories where you've diff --git a/README.markdown b/README.markdown index 568ec63e..2c3e5717 100644 --- a/README.markdown +++ b/README.markdown @@ -2,10 +2,10 @@ Write real [Processing](http://processing.org/) sketches in Python. -* Based on [Processing 3.0](http://processing.org/), and compatible with most [3rd party libraries](http://www.processing.org/reference/libraries/). +* Based on [Processing 4](http://processing.org/), and compatible with most [3rd party libraries](http://www.processing.org/reference/libraries/). * Source compatible with [Python 2.7.3](http://python.org). -Tested on Mac OS 10.10 and Ubuntu 14. +Tested on Mac OS 11.6.2 and Ubuntu 14. [![Build Status](https://travis-ci.org/jdf/processing.py.svg?branch=master)](https://travis-ci.org/jdf/processing.py) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fjdf%2Fprocessing.py.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fjdf%2Fprocessing.py?ref=badge_shield) @@ -14,13 +14,11 @@ Tested on Mac OS 10.10 and Ubuntu 14. ### Processing Development Environment ### -If you're looking to write Processing sketches in Python, your best bet is to use -Python Mode. The project is still in its early days, and documentation is lacking, -but there are many example sketches to get you started. In general, the Processing -reference works just fine for Python mode. +If you're looking to write Processing 4 sketches in Python, your best bet is to use +Python Mode. First, [download Processing](http://processing.org/download). Then, install -Python Mode: +Python Mode 4: @@ -82,9 +80,9 @@ In case you need other primitive values, please [let us know](http://github.com/ ### Why was this project created? ### -I ([Jonathan](http://MrFeinberg.com/)) recently gave a talk about Processing to a group of rather bright 8th-graders, +I ([Jonathan](http://MrFeinberg.com/)) once gave a talk about Processing to a group of rather bright 8th-graders, as part of a computer-programming summer camp they were attending at my office. -Their curriculum up to that point had been in Python, which is an eminently +Their curriculum up to that point had been in Python, which is a sensible choice, given the [pedagogical roots](http://en.wikipedia.org/wiki/ABC_%28programming_language%29) of the language. diff --git a/SketchRunner.launch b/SketchRunner.launch index e0c7b6f2..0d4711ff 100644 --- a/SketchRunner.launch +++ b/SketchRunner.launch @@ -1,19 +1,26 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build.xml b/build.xml index e162ee55..ce03e859 100644 --- a/build.xml +++ b/build.xml @@ -7,7 +7,7 @@ Processing Development Environment and for the standalone processing.py distribution. - This has been tested on Mac OSX and linux. It is not expected to work at + This has been tested on macOS and Linux. It is not expected to work at all on Windows. -jdf @@ -16,28 +16,32 @@ ant test - to run unit tests. ant mode.zip - to create a Python Mode zip file. ant make-all-distributions - to create a processing.py release. - ant -Dplatform=macosx make-distribution - to create the OSX processing.py release. + ant -Dplatform=macos make-distribution - to create the macOS processing.py release. ====================================================================== --> Write Processing sketches in Python - - - + + + + + + + - - + + @@ -62,7 +66,7 @@ - + @@ -72,10 +76,10 @@ file="runtime/src/jycessing/build.properties" comment="Python mode build number"> - + - - + + @@ -90,7 +94,7 @@ - + @@ -104,7 +108,7 @@ - + - - + + - + - + @@ -149,14 +153,14 @@ - - + + @@ -164,9 +168,9 @@ + file="${processing.dir}/core/library/gluegen-rt-natives-${natives.platform}.jar"/> + file="${processing.dir}/core/library/jogl-all-natives-${natives.platform}.jar"/> @@ -177,7 +181,7 @@ - + @@ -193,40 +197,40 @@ - + - + - + - + - + targetfile="${processing-video.dir}/library/video.jar"> + + depends="check-video-needs-build" + unless="video-is-uptodate"> - - + + @@ -234,7 +238,7 @@ - + @@ -244,7 +248,7 @@ - + - @@ -420,9 +417,9 @@ - - @@ -435,10 +432,10 @@ - + - @@ -486,7 +483,7 @@ @@ -502,14 +499,14 @@ @@ -534,21 +531,21 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/mode/mode.properties b/mode/mode.properties index 87e5ee02..9274ee27 100644 --- a/mode/mode.properties +++ b/mode/mode.properties @@ -1,11 +1,11 @@ -name = Python Mode for Processing 3 +name = Python Mode for Processing 4 authorList = [Jonathan Feinberg](http://MrFeinberg.com/) url = https://github.com/jdf/processing.py -sentence = Write Processing sketches in Python. +sentence = Write Processing 4 sketches in Python. paragraph = version = @@version@@ prettyVersion = @@pretty-version@@ -# 3.0 -minRevision = 246 -# <3.2 +# 4.1.4 or 4.2 (additional changes made to support Python Mode) +minRevision = 1292 +# works with any later release maxRevision = 0 diff --git a/processing-py-test-suite.launch b/processing-py-test-suite.launch index 7d5ac6b7..b5c2dad2 100644 --- a/processing-py-test-suite.launch +++ b/processing-py-test-suite.launch @@ -1,44 +1,38 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/runtime/src/jycessing/PAppletJythonDriver.java b/runtime/src/jycessing/PAppletJythonDriver.java index b310a352..e5a4cbf7 100755 --- a/runtime/src/jycessing/PAppletJythonDriver.java +++ b/runtime/src/jycessing/PAppletJythonDriver.java @@ -15,21 +15,17 @@ import java.awt.Component; import java.awt.Frame; +import java.awt.GraphicsDevice; import java.awt.Point; -import java.awt.Window; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.io.File; -import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; -import java.lang.Thread.UncaughtExceptionHandler; import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -51,7 +47,6 @@ import org.python.core.PyFunction; import org.python.core.PyIndentationError; import org.python.core.PyInteger; -import org.python.core.PyJavaType; import org.python.core.PyObject; import org.python.core.PyObjectDerived; import org.python.core.PySet; @@ -63,27 +58,24 @@ import org.python.core.PyUnicode; import org.python.util.InteractiveConsole; -import com.google.common.base.Charsets; -import com.google.common.io.Files; import com.jogamp.newt.opengl.GLWindow; import jycessing.IOUtil.ResourceReader; -import jycessing.jni.OSX; import jycessing.mode.run.WrappedPrintStream; import jycessing.mode.run.WrappedPrintStream.PushedOut; + import processing.awt.PSurfaceAWT; import processing.core.PApplet; import processing.core.PConstants; import processing.core.PImage; import processing.core.PSurface; +import processing.core.ThinkDifferent; import processing.event.KeyEvent; import processing.event.MouseEvent; -import processing.javafx.PSurfaceFX; import processing.opengl.PGraphicsOpenGL; import processing.opengl.PShader; import processing.opengl.PSurfaceJOGL; -@SuppressWarnings("serial") public class PAppletJythonDriver extends PApplet { public static final String C_LIKE_LOGICAL_AND_ERROR_MESSAGE = @@ -106,6 +98,14 @@ public class PAppletJythonDriver extends PApplet { private Field frameField; + static private Class fxSurfaceClass; + static { + try { + // Get class object for use in dynamic instanceof calls for PSurfaceFX + fxSurfaceClass = Class.forName("processing.javafx.PSurfaceFX"); + } catch (ClassNotFoundException ignored) { } + } + private PythonSketchError terminalException = null; protected final PyStringMap builtins; @@ -258,16 +258,13 @@ private static PythonSketchError toSketchException(Throwable t) { if (t instanceof PythonSketchError) { return (PythonSketchError) t; } - if (t instanceof PySyntaxError) { - final PySyntaxError e = (PySyntaxError) t; + if (t instanceof final PySyntaxError e) { return extractSketchErrorFromPyExceptionValue((PyTuple) e.value); } - if (t instanceof PyIndentationError) { - final PyIndentationError e = (PyIndentationError) t; + if (t instanceof final PyIndentationError e) { return extractSketchErrorFromPyExceptionValue((PyTuple) e.value); } - if (t instanceof PyException) { - final PyException e = (PyException) t; + if (t instanceof final PyException e) { final Pattern tbParse = Pattern.compile("^\\s*File \"([^\"]+)\", line (\\d+)", Pattern.MULTILINE); final Matcher m = tbParse.matcher(e.toString()); @@ -305,8 +302,8 @@ private static PythonSketchError extractSketchErrorFromPyExceptionValue(final Py final PyTuple context = (PyTuple) tup.get(1); final File file = new File((String) context.get(0)); final String fileName = file.getName(); - final int lineNumber = ((Integer) context.get(1)).intValue() - 1; - final int column = ((Integer) context.get(2)).intValue(); + final int lineNumber = (Integer) context.get(1) - 1; + final int column = (Integer) context.get(2); if (pyMessage.startsWith("no viable alternative")) { return noViableAlternative(file, lineNumber, column, pyMessage); } @@ -324,11 +321,6 @@ private static PythonSketchError extractSketchErrorFromPyExceptionValue(final Py * *

This function takes a stab at finding such a thing, and reporting it. Otherwise, it throws a * slightly less cryptic error message. - * - * @param file - * @param line - * @param column - * @return */ private static PythonSketchError noViableAlternative( final File file, final int lineNo, final int column, final String message) { @@ -345,29 +337,29 @@ private static PythonSketchError noViableAlternative( file.getName(), lineNo, column); - try { - int lineIndex = 0; - for (final String line : Files.readLines(file, Charsets.UTF_8)) { + int lineIndex = 0; + String[] lines = PApplet.loadStrings(file); + if (lines != null) { + for (final String line : lines) { final Matcher m = NAKED_COLOR.matcher(line); if (m.find()) { final String color = m.group(1); return new PythonSketchError( - "Did you try to name a color here? " - + "Colors in Python mode are either strings, like '#" - + color - + "', or " - + "large hex integers, like 0xFF" - + color.toUpperCase() - + ".", - file.getName(), - lineIndex, - m.start(1)); + "Did you try to name a color here? " + + "Colors in Python mode are either strings, like '#" + + color + + "', or " + + "large hex integers, like 0xFF" + + color.toUpperCase() + + ".", + file.getName(), + lineIndex, + m.start(1)); } lineIndex++; } - } catch (final IOException e) { - System.err.println("While trying to read " + file + ": " + e.getMessage()); - return defaultException; + } else { + System.err.println("Could not read " + file); } return defaultException; } @@ -422,25 +414,17 @@ public void doPrint(final String s) { detectedPixelDensity = interp.get("__pixelDensity__").asInt(); detectedSmooth = interp.get("__smooth__").asInt() != 0; detectedNoSmooth = interp.get("__noSmooth__").asInt() != 0; + detectedFullScreen = interp.get("__fullScreen__").asInt() != 0; // TODO does not work [fry 230221] final String r = interp.get("__renderer__").asString(); - if (r.equals("JAVA2D")) { - detectedRenderer = JAVA2D; - } else if (r.equals("P2D")) { - detectedRenderer = P2D; - } else if (r.equals("P3D")) { - detectedRenderer = P3D; - } else if (r.equals("OPENGL")) { - detectedRenderer = P3D; - } else if (r.equals("FX2D")) { - detectedRenderer = FX2D; - } else if (r.equals("PDF")) { - detectedRenderer = PDF; - } else if (r.equals("SVG")) { - detectedRenderer = SVG; - } else if (r.equals("DXF")) { - detectedRenderer = DXF; - } else { - detectedRenderer = r; + switch (r) { + case "JAVA2D" -> detectedRenderer = JAVA2D; + case "P2D" -> detectedRenderer = P2D; + case "P3D", "OPENGL" -> detectedRenderer = P3D; + case "FX2D" -> detectedRenderer = FX2D; + case "PDF" -> detectedRenderer = PDF; + case "SVG" -> detectedRenderer = SVG; + case "DXF" -> detectedRenderer = DXF; + default -> detectedRenderer = r; } processedStaticSketch = interp.get("__cleaned_sketch__"); } @@ -482,8 +466,7 @@ protected PSurface initSurface() { } s.setTitle(pySketchPath.getFileName().toString().replaceAll("\\..*$", "")); - if (s instanceof PSurfaceAWT) { - final PSurfaceAWT surf = (PSurfaceAWT) s; + if (s instanceof final PSurfaceAWT surf) { final Component c = (Component) surf.getNative(); c.addComponentListener( new ComponentAdapter() { @@ -492,8 +475,7 @@ public void componentHidden(final ComponentEvent e) { finishedLatch.countDown(); } }); - } else if (s instanceof PSurfaceJOGL) { - final PSurfaceJOGL surf = (PSurfaceJOGL) s; + } else if (s instanceof final PSurfaceJOGL surf) { final GLWindow win = (GLWindow) surf.getNative(); win.addWindowListener( new com.jogamp.newt.event.WindowAdapter() { @@ -502,12 +484,12 @@ public void windowDestroyed(final com.jogamp.newt.event.WindowEvent arg0) { finishedLatch.countDown(); } }); - } else if (s instanceof PSurfaceFX) { + } else if (fxSurfaceClass != null && fxSurfaceClass.isInstance(s)) { System.err.println("I don't know how to watch FX2D windows for close."); } final PyObject pyG; - if (g instanceof PGraphicsOpenGL) { + if (g instanceof final PGraphicsOpenGL glGraphics) { /* * The name "camera" in PGraphicsOpenGL can refer to either a PMatrix3D field * or a couple of functions of that name. Unfortunately, Python only has one namespace, @@ -528,7 +510,6 @@ public void windowDestroyed(final com.jogamp.newt.event.WindowEvent arg0) { * # read back what we did * print g.cameraMatrix.m02 */ - final PGraphicsOpenGL glGraphics = (PGraphicsOpenGL) g; final PyObject cameraMatrix = Py.java2py(glGraphics.camera); final PyObject cameraInvMatrix = Py.java2py(glGraphics.cameraInv); final PyObject modelviewMatrix = Py.java2py(glGraphics.modelview); @@ -592,16 +573,12 @@ public void method(final String name) { * * @param obj either a {@link PyString} or a {@link PyFunction} to run in a thread. */ + @SuppressWarnings("unused") public void thread(final Object obj) { if (obj instanceof String) { super.thread((String) obj); } else if (obj instanceof PyFunction) { - new Thread() { - @Override - public void run() { - ((PyFunction) obj).__call__(); - } - }.start(); + new Thread(() -> ((PyFunction) obj).__call__()).start(); } } @@ -627,63 +604,63 @@ public void findSketchMethods() throws PythonSketchError { setupMeth = interp.get("setup"); mousePressedFunc = - new EventFunction("mousePressed") { - @Override - protected void callSuper(final MouseEvent event) { - PAppletJythonDriver.super.mousePressed(event); - } - }; + new EventFunction<>("mousePressed") { + @Override + protected void callSuper(final MouseEvent event) { + PAppletJythonDriver.super.mousePressed(event); + } + }; mouseClickedFunc = - new EventFunction("mouseClicked") { - @Override - protected void callSuper(final MouseEvent event) { - PAppletJythonDriver.super.mouseClicked(event); - } - }; + new EventFunction<>("mouseClicked") { + @Override + protected void callSuper(final MouseEvent event) { + PAppletJythonDriver.super.mouseClicked(event); + } + }; mouseMovedFunc = - new EventFunction("mouseMoved") { - @Override - protected void callSuper(final MouseEvent event) { - PAppletJythonDriver.super.mouseMoved(event); - } - }; + new EventFunction<>("mouseMoved") { + @Override + protected void callSuper(final MouseEvent event) { + PAppletJythonDriver.super.mouseMoved(event); + } + }; mouseReleasedFunc = - new EventFunction("mouseReleased") { - @Override - protected void callSuper(final MouseEvent event) { - PAppletJythonDriver.super.mouseReleased(event); - } - }; + new EventFunction<>("mouseReleased") { + @Override + protected void callSuper(final MouseEvent event) { + PAppletJythonDriver.super.mouseReleased(event); + } + }; mouseDraggedFunc = - new EventFunction("mouseDragged") { - @Override - protected void callSuper(final MouseEvent event) { - PAppletJythonDriver.super.mouseDragged(event); - } - }; + new EventFunction<>("mouseDragged") { + @Override + protected void callSuper(final MouseEvent event) { + PAppletJythonDriver.super.mouseDragged(event); + } + }; // keyPressed is renamed to __keyPressed__ by the preprocessor. keyPressedFunc = - new EventFunction("__keyPressed__") { - @Override - protected void callSuper(final KeyEvent event) { - PAppletJythonDriver.super.keyPressed(event); - } - }; + new EventFunction<>("__keyPressed__") { + @Override + protected void callSuper(final KeyEvent event) { + PAppletJythonDriver.super.keyPressed(event); + } + }; keyReleasedFunc = - new EventFunction("keyReleased") { - @Override - protected void callSuper(final KeyEvent event) { - PAppletJythonDriver.super.keyReleased(event); - } - }; + new EventFunction<>("keyReleased") { + @Override + protected void callSuper(final KeyEvent event) { + PAppletJythonDriver.super.keyReleased(event); + } + }; keyTypedFunc = - new EventFunction("keyTyped") { - @Override - protected void callSuper(final KeyEvent event) { - PAppletJythonDriver.super.keyTyped(event); - } - }; + new EventFunction<>("keyTyped") { + @Override + protected void callSuper(final KeyEvent event) { + PAppletJythonDriver.super.keyTyped(event); + } + }; settingsMeth = interp.get("settings"); stopMeth = interp.get("stop"); @@ -730,62 +707,44 @@ public PyObject __call__(final PyObject[] args, final String[] kws) { PyObject meth; if ((meth = interp.get("noteOn")) != null) { switch (argCount(meth)) { - default: - throw new RuntimeException( - "only noteOn(channel, pitch, velocity) or " - + "noteOn(channel, pitch, velocity, timestamp, bus_name) " - + "are supported by Python Mode"); - case 3: - noteOn3Meth = meth; - break; - case 5: - noteOn5Meth = meth; + default -> throw new RuntimeException( + "only noteOn(channel, pitch, velocity) or " + + "noteOn(channel, pitch, velocity, timestamp, bus_name) " + + "are supported by Python Mode"); + case 3 -> noteOn3Meth = meth; + case 5 -> noteOn5Meth = meth; } } if ((meth = interp.get("noteOff")) != null) { switch (argCount(meth)) { - case 1: - throw new RuntimeException( - "only noteOff(channel, pitch, velocity) or " - + "noteOff(channel, pitch, velocity, timestamp, bus_name) " - + "are supported by Python Mode"); - case 3: - noteOff3Meth = meth; - break; - case 5: - noteOff5Meth = meth; + case 1 -> throw new RuntimeException( + "only noteOff(channel, pitch, velocity) or " + + "noteOff(channel, pitch, velocity, timestamp, bus_name) " + + "are supported by Python Mode"); + case 3 -> noteOff3Meth = meth; + case 5 -> noteOff5Meth = meth; } } if ((meth = interp.get("controllerChange")) != null) { switch (argCount(meth)) { - case 1: - throw new RuntimeException( - "only controllerChange(channel, pitch, velocity) or " - + "controllerChange(channel, pitch, velocity, timestamp, bus_name) " - + "are supported by Python Mode"); - case 3: - controllerChange3Meth = meth; - break; - case 5: - controllerChange5Meth = meth; + case 1 -> throw new RuntimeException( + "only controllerChange(channel, pitch, velocity) or " + + "controllerChange(channel, pitch, velocity, timestamp, bus_name) " + + "are supported by Python Mode"); + case 3 -> controllerChange3Meth = meth; + case 5 -> controllerChange5Meth = meth; } } if ((meth = interp.get("rawMidi")) != null) { switch (argCount(meth)) { - case 1: - rawMidi1Meth = meth; - break; - case 3: - rawMidi3Meth = meth; + case 1 -> rawMidi1Meth = meth; + case 3 -> rawMidi3Meth = meth; } } if ((meth = interp.get("midiMessage")) != null) { switch (argCount(meth)) { - case 1: - midiMessage1Meth = meth; - break; - case 3: - midiMessage3Meth = meth; + case 1 -> midiMessage1Meth = meth; + case 3 -> midiMessage3Meth = meth; } } @@ -883,31 +842,34 @@ private void wrapMouseVariables() { public void start() { // I want to quit on runtime exceptions. // Processing just sits there by default. - Thread.setDefaultUncaughtExceptionHandler( - new UncaughtExceptionHandler() { - @Override - public void uncaughtException(final Thread t, final Throwable e) { - terminalException = toSketchException(e); - try { - handleMethods("dispose"); - } catch (final Exception noop) { - // give up - } - finishedLatch.countDown(); - } - }); + Thread.setDefaultUncaughtExceptionHandler((t, e) -> { + terminalException = toSketchException(e); + try { + handleMethods("dispose"); + } catch (final Exception noop) { + // give up + } + finishedLatch.countDown(); + }); super.start(); } - private void bringToFront() { - if (PApplet.platform == PConstants.MACOSX) { - OSX.bringToFront(); - } - } public void runAndBlock(final String[] args) throws PythonSketchError { PApplet.runSketch(args, this); - bringToFront(); + + // bring the sketch to the front + if (PApplet.platform == PConstants.MACOS) { + ThinkDifferent.activateIgnoringOtherApps(); + } + + if (detectedFullScreen) { + if (surface instanceof PSurfaceAWT) { + Frame frame = ((PSurfaceAWT.SmoothCanvas) surface.getNative()).getFrame(); + GraphicsDevice displayDevice = frame.getGraphicsConfiguration().getDevice(); + displayDevice.setFullScreenWindow(frame); + } + } try { finishedLatch.await(); @@ -934,7 +896,9 @@ public void runAndBlock(final String[] args) throws PythonSketchError { maybeShutdownSoundEngine(); Thread.setDefaultUncaughtExceptionHandler(null); - if (PApplet.platform == PConstants.MACOSX && Arrays.asList(args).contains("fullScreen")) { + + /* + if (PApplet.platform == PConstants.MACOS && Arrays.asList(args).contains("fullScreen")) { // Frame should be OS-X fullscreen, and it won't stop being that unless the jvm // exits or we explicitly tell it to minimize. // (If it's disposed, it'll leave a gray blank window behind it.) @@ -949,7 +913,8 @@ public void runAndBlock(final String[] args) throws PythonSketchError { } } } - if (surface instanceof PSurfaceFX) { + */ + if (fxSurfaceClass != null && fxSurfaceClass.isInstance(surface)) { // Sadly, JavaFX is an abomination, and there's no way to run an FX sketch more than once, // so we must actually exit. Runner.log("JavaFX requires SketchRunner to terminate. Farewell!"); @@ -987,24 +952,6 @@ private void maybeShutdownSoundEngine() { } } - /** - * Use reflection to call - * com.apple.eawt.Application.getApplication().requestToggleFullScreen(window); - */ - private static void macosxFullScreenToggle(final Window window) { - try { - final Class appClass = Class.forName("com.apple.eawt.Application"); - final Method getAppMethod = appClass.getMethod("getApplication"); - final Object app = getAppMethod.invoke(null); - final Method requestMethod = appClass.getMethod("requestToggleFullScreen", Window.class); - requestMethod.invoke(app, window); - } catch (final ClassNotFoundException cnfe) { - // ignored - } catch (final Exception e) { - e.printStackTrace(); - } - } - /** * Permit the punning use of set() by mucking with the builtin "set" Type. If you call it with 3 * arguments, it acts like the Processing set(x, y, whatever) method. If you call it with 0 or 1 @@ -1017,7 +964,7 @@ private void setSet() { new PyType(PyType.TYPE) { { builtin = true; - init(PySet.class, new HashSet()); + init(PySet.class, new HashSet<>()); invalidateMethodCache(); } @@ -1135,14 +1082,17 @@ public int lerpColor(final int c1, final int c2, final float amt) { * invoke the fill(float) method, unless we provide a long int * version to catch it. */ + @SuppressWarnings("unused") public void fill(final long argb) { fill((int) (argb & 0xFFFFFFFF)); } + @SuppressWarnings("unused") public void stroke(final long argb) { stroke((int) (argb & 0xFFFFFFFF)); } + @SuppressWarnings("unused") public void background(final long argb) { background((int) (argb & 0xFFFFFFFF)); } @@ -1151,14 +1101,17 @@ public void background(final long argb) { * Python can't parse web colors, so we let the user do '#RRGGBB' * as a string. */ + @SuppressWarnings("unused") public void fill(final String argbSpec) { fill(parseColorSpec(argbSpec)); } + @SuppressWarnings("unused") public void stroke(final String argbSpec) { stroke(parseColorSpec(argbSpec)); } + @SuppressWarnings("unused") public void background(final String argbSpec) { background(parseColorSpec(argbSpec)); } diff --git a/runtime/src/jycessing/Runner.java b/runtime/src/jycessing/Runner.java index 29d3b712..8fac8e0d 100755 --- a/runtime/src/jycessing/Runner.java +++ b/runtime/src/jycessing/Runner.java @@ -53,7 +53,7 @@ public class Runner { static { final int archBits = Integer.parseInt(System.getProperty("sun.arch.data.model")); - if (PApplet.platform == PConstants.MACOSX) { + if (PApplet.platform == PConstants.MACOS) { ARCH = "macosx" + archBits; } else if (PApplet.platform == PConstants.WINDOWS) { ARCH = "macosx" + archBits; @@ -89,7 +89,7 @@ static void log(final Object... objs) { return; } for (final Object o : objs) { - System.err.print(String.valueOf(o)); + System.err.print(o); } System.err.println(); } @@ -337,7 +337,7 @@ public static synchronized void runSketchBlocking( interp.exec("import sys\n"); - // Add all of the sketch's requested sys.path entries, and add all jar + // Add all the sketch's requested sys.path entries, and add all jar // files found there, recursively. final Set userLibs = new TreeSet<>(); for (final File entry : sketch.getPathEntries()) { @@ -351,7 +351,7 @@ public static synchronized void runSketchBlocking( new LibraryImporter(libDirs, interp); if (sketch.getLibraryPolicy() == LibraryPolicy.PROMISCUOUS) { - log("Promiscusouly adding all libraries in " + libDirs); + log("Promiscuously adding all libraries in " + libDirs); // Recursively search the "libraries" directory for jar files and // directories containing dynamic libraries. final Set libs = new HashSet<>(); diff --git a/runtime/src/jycessing/build.properties b/runtime/src/jycessing/build.properties index 1a22f6ed..acd55828 100644 --- a/runtime/src/jycessing/build.properties +++ b/runtime/src/jycessing/build.properties @@ -1,5 +1,5 @@ #Python mode build number -#Tue, 19 May 2020 13:23:09 -0400 +#Sat, 22 Jan 2022 11:36:58 -0500 -build.date=2020/05/19 13\:23 -build.number=3063 +build.date=2022/01/22 11\:36 +build.number=4001 diff --git a/runtime/src/jycessing/jni/OSX.java b/runtime/src/jycessing/jni/OSX.java deleted file mode 100644 index aeb2ebfb..00000000 --- a/runtime/src/jycessing/jni/OSX.java +++ /dev/null @@ -1,39 +0,0 @@ -package jycessing.jni; - -import jycessing.mode.PythonMode; -import processing.core.PApplet; -import processing.core.PConstants; - -public class OSX { - private static volatile boolean didLoad = false; - - public static void bringToFront() { - if (!didLoad && (PApplet.platform == PConstants.MACOSX)) { - try { - System.loadLibrary("jniosx"); - didLoad = true; - } catch (final UnsatisfiedLinkError err) { - System.err.println("Hmm. Can't load native code to bring window to front."); - } - } - if (didLoad) { - activateIgnoringOtherApps(); - } - } - - public static void bringToFront(PythonMode mode) { - if (!didLoad && (PApplet.platform == PConstants.MACOSX)) { - String path = null; - try { - path = mode.getContentFile("mode").getAbsolutePath() + "/libjniosx.dylib"; - System.load(path); - didLoad = true; - } catch (final UnsatisfiedLinkError err) { - System.err.println("Hmm. Can't load native code to bring window to front using the absolute path: " + path + "."); - } - } - bringToFront(); - } - - private static native void activateIgnoringOtherApps(); -} diff --git a/runtime/src/jycessing/jni/jniosx/jniosx/jniosx.m b/runtime/src/jycessing/jni/jniosx/jniosx/jniosx.m deleted file mode 100644 index dd58aae4..00000000 --- a/runtime/src/jycessing/jni/jniosx/jniosx/jniosx.m +++ /dev/null @@ -1,8 +0,0 @@ -#include "jycessing_jni_OSX.h" - -#import "AppKit/AppKit.h" - -JNIEXPORT void JNICALL Java_jycessing_jni_OSX_activateIgnoringOtherApps -(JNIEnv *env, jclass klass) { - [NSApp activateIgnoringOtherApps:true]; -} diff --git a/runtime/src/jycessing/jni/jycessing_jni_OSX.h b/runtime/src/jycessing/jni/jycessing_jni_OSX.h deleted file mode 100644 index 79fa36f5..00000000 --- a/runtime/src/jycessing/jni/jycessing_jni_OSX.h +++ /dev/null @@ -1,21 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* Header for class jycessing_jni_OSX */ - -#ifndef _Included_jycessing_jni_OSX -#define _Included_jycessing_jni_OSX -#ifdef __cplusplus -extern "C" { -#endif -/* - * Class: jycessing_jni_OSX - * Method: activateIgnoringOtherApps - * Signature: ()V - */ -JNIEXPORT void JNICALL Java_jycessing_jni_OSX_activateIgnoringOtherApps - (JNIEnv *, jclass); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/runtime/src/jycessing/mode/PyEditor.java b/runtime/src/jycessing/mode/PyEditor.java index 7bd824b8..cfdfb99a 100644 --- a/runtime/src/jycessing/mode/PyEditor.java +++ b/runtime/src/jycessing/mode/PyEditor.java @@ -2,13 +2,13 @@ import java.awt.Point; import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.UUID; @@ -21,8 +21,7 @@ import jycessing.DisplayType; import jycessing.IOUtil; -import jycessing.jni.OSX; -import jycessing.mode.export.ExportDialog; +import jycessing.mode.export.Exporter; import jycessing.mode.run.PdeSketch; import jycessing.mode.run.PdeSketch.LocationType; import jycessing.mode.run.SketchService; @@ -45,11 +44,12 @@ import processing.app.ui.EditorException; import processing.app.ui.EditorState; import processing.app.ui.EditorToolbar; +import processing.app.ui.ExportPrompt; import processing.app.ui.Toolkit; import processing.core.PApplet; import processing.core.PConstants; +import processing.core.ThinkDifferent; -@SuppressWarnings("serial") public class PyEditor extends Editor { @SuppressWarnings("unused") @@ -108,7 +108,7 @@ public void windowClosing(final WindowEvent e) { @Override protected JEditTextArea createTextArea() { - return new PdeTextArea(new PdeTextAreaDefaults(mode), new PyInputHandler(this), this); + return new PdeTextArea(new PdeTextAreaDefaults(), new PyInputHandler(this), this); } public String getId() { @@ -116,14 +116,11 @@ public String getId() { } private MouseWheelListener createHorizontalScrollListener() { - return new MouseWheelListener() { - @Override - public void mouseWheelMoved(final MouseWheelEvent e) { - if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL && e.isShiftDown()) { - final int current = textarea.getHorizontalScrollPosition(); - final int delta = e.getUnitsToScroll() * 6; - textarea.setHorizontalScrollPosition(current + delta); - } + return e -> { + if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL && e.isShiftDown()) { + final int current = textarea.getHorizontalScrollPosition(); + final int delta = e.getUnitsToScroll() * 6; + textarea.setHorizontalScrollPosition(current + delta); } }; } @@ -153,7 +150,7 @@ private void cleanupTempSketch() { log("Deleted " + tempSketch); assert (!tempSketch.toFile().exists()); } catch (final IOException e) { - System.err.println(e); + e.printStackTrace(); } } tempSketch = null; @@ -166,12 +163,7 @@ public JMenu buildFileMenu() { final String appTitle = Language.text("Export Application"); final JMenuItem exportApplication = Toolkit.newJMenuItem(appTitle, 'E'); exportApplication.addActionListener( - new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - handleExportApplication(); - } - }); + e -> handleExportApplication()); return buildFileMenu(new JMenuItem[] {exportApplication}); } @@ -227,31 +219,13 @@ public void actionPerformed(final ActionEvent e) { @Override public JMenu buildSketchMenu() { final JMenuItem runItem = Toolkit.newJMenuItem(Language.text("toolbar.run"), 'R'); - runItem.addActionListener( - new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - handleRun(); - } - }); + runItem.addActionListener(e -> handleRun()); final JMenuItem presentItem = Toolkit.newJMenuItemShift(Language.text("toolbar.present"), 'R'); - presentItem.addActionListener( - new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - handlePresent(); - } - }); + presentItem.addActionListener(e -> handlePresent()); final JMenuItem stopItem = new JMenuItem(Language.text("toolbar.stop")); - stopItem.addActionListener( - new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - handleStop(); - } - }); + stopItem.addActionListener(e -> handleStop()); return buildSketchMenu(new JMenuItem[] {runItem, presentItem, stopItem}); } @@ -288,7 +262,8 @@ public void handleExportApplication() { } } - new ExportDialog(this, sketch).go(); + //new ExportDialog(this, sketch).go(); + new ExportPrompt(this, () -> new Exporter(PyEditor.this).export()); } public File getModeContentFile(final String filename) { @@ -304,12 +279,11 @@ public File getSplashFile() { * * @return a new directory containing a saved version of the current (presumably modified) sketch * code. - * @throws IOException */ private Path createTempSketch() throws IOException { final Path tmp = Files.createTempDirectory(sketch.getName()); for (final SketchCode code : sketch.getCode()) { - Files.write(tmp.resolve(code.getFileName()), code.getProgram().getBytes("utf-8")); + Files.write(tmp.resolve(code.getFileName()), code.getProgram().getBytes(StandardCharsets.UTF_8)); } return tmp; } @@ -353,8 +327,8 @@ private void runSketch(final DisplayType displayType) { } private void bringToFront() { - if (PApplet.platform == PConstants.MACOSX) { - OSX.bringToFront(pyMode); + if (PApplet.platform == PConstants.MACOS) { + ThinkDifferent.activateIgnoringOtherApps(); } } diff --git a/runtime/src/jycessing/mode/export/Arch.java b/runtime/src/jycessing/mode/export/Arch.java deleted file mode 100644 index 122cf781..00000000 --- a/runtime/src/jycessing/mode/export/Arch.java +++ /dev/null @@ -1,16 +0,0 @@ -package jycessing.mode.export; - -/** - * The architecture of the platform we're exporting to. (The "bits" field is necessary because the - * processing Library API uses magic integers for architecture.) - */ -public enum Arch { - X86(32), - AMD64(64); - - public final int bits; - - Arch(final int bits) { - this.bits = bits; - } -} diff --git a/runtime/src/jycessing/mode/export/ExportDialog.java b/runtime/src/jycessing/mode/export/ExportDialog.java deleted file mode 100644 index c92ec1aa..00000000 --- a/runtime/src/jycessing/mode/export/ExportDialog.java +++ /dev/null @@ -1,389 +0,0 @@ -package jycessing.mode.export; - -import java.awt.Color; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.Rectangle; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.io.File; - -import javax.swing.BorderFactory; -import javax.swing.Box; -import javax.swing.BoxLayout; -import javax.swing.JCheckBox; -import javax.swing.JDialog; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.SwingConstants; -import javax.swing.border.BevelBorder; -import javax.swing.border.EmptyBorder; -import javax.swing.border.TitledBorder; - -import jycessing.mode.PyEditor; -import jycessing.mode.PythonMode; -import processing.app.Platform; -import processing.app.Preferences; -import processing.app.Sketch; -import processing.app.ui.ColorChooser; - -/** - * This is the export window that pops up when the user clicks [=>]. It tries to look as much as - * possible like java mode's exporter. Rather than passing the options they select directly to - * Exporter, it sets them in their user preferences, which Exporter and the various Export classes - * reference later. - */ -@SuppressWarnings("serial") -public class ExportDialog extends JDialog { - - @SuppressWarnings("unused") - private static void log(final String msg) { - if (PythonMode.VERBOSE) { - System.err.println(ExportDialog.class.getSimpleName() + ": " + msg); - } - } - - private final Sketch sketch; - private final PyEditor editor; - private final JOptionPane optionPane; - - public ExportDialog(final PyEditor editor, final Sketch sketch) { - super(editor, "Export Application", true); - - this.editor = editor; - this.sketch = sketch; - - log("Setting up export dialog"); - - final JPanel center = createCenterPanel(); - - final JPanel platforms = createPlatformsPanel(); - center.add(platforms); - - final JPanel present = createPresentPanel(); - center.add(present); - - final JPanel embed = createEmbedJavaPanel(platforms.getPreferredSize().width); - center.add(embed); - - // I haven't tested this on a Mac yet - if (Platform.isMacOS()) { - final JPanel signingProblems = createMacSigningWarning(platforms.getPreferredSize().width); - center.add(signingProblems); - } - - final String[] options = {"Export", "Cancel"}; - optionPane = - new JOptionPane( - center, - JOptionPane.PLAIN_MESSAGE, - JOptionPane.YES_NO_OPTION, - null, - options, - options[0]); - - this.setContentPane(optionPane); - optionPane.addPropertyChangeListener( - new PropertyChangeListener() { - @Override - public void propertyChange(final PropertyChangeEvent e) { - final String prop = e.getPropertyName(); - - if (isVisible() - && (e.getSource() == optionPane) - && (prop.equals(JOptionPane.VALUE_PROPERTY))) { - setVisible(false); - } - } - }); - this.pack(); - this.setResizable(false); - - final Rectangle bounds = editor.getBounds(); - final Dimension size = this.getSize(); - this.setLocation( - bounds.x + (bounds.width - size.width) / 2, bounds.y + (bounds.height - size.height) / 2); - } - - public void go() { - log("Launching export dialog"); - - this.setVisible(true); - - // Wait until they click "Export" or "Cancel" - final Object value = optionPane.getValue(); - - if (value.equals("Export")) { - new Exporter(editor, sketch).export(); - } else { - editor.statusNotice("Export to Application Cancelled"); - } - } - - private JPanel createCenterPanel() { - final JPanel panel = new JPanel(); - panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); - panel.add(Box.createVerticalStrut(6)); - final String line1 = "Export to Application creates double-clickable,"; - final String line2 = "standalone applications for the selected plaforms."; - final JLabel label1 = new JLabel(line1, SwingConstants.CENTER); - final JLabel label2 = new JLabel(line2, SwingConstants.CENTER); - label1.setAlignmentX(Component.LEFT_ALIGNMENT); - label2.setAlignmentX(Component.LEFT_ALIGNMENT); - panel.add(label1); - panel.add(label2); - panel.add(Box.createVerticalStrut(12)); - return panel; - } - - private JPanel createPlatformsPanel() { - final JCheckBox windowsButton = new JCheckBox("Windows"); - windowsButton.setSelected(Preferences.getBoolean("export.application.platform.windows")); - windowsButton.addItemListener( - new ItemListener() { - @Override - public void itemStateChanged(final ItemEvent e) { - Preferences.setBoolean( - "export.application.platform.windows", windowsButton.isSelected()); - } - }); - - final JCheckBox macosxButton = new JCheckBox("Mac OS X"); - macosxButton.setSelected(Preferences.getBoolean("export.application.platform.macosx")); - macosxButton.addItemListener( - new ItemListener() { - @Override - public void itemStateChanged(final ItemEvent e) { - Preferences.setBoolean("export.application.platform.macosx", macosxButton.isSelected()); - } - }); - - final JCheckBox linuxButton = new JCheckBox("Linux"); - linuxButton.setSelected(Preferences.getBoolean("export.application.platform.linux")); - linuxButton.addItemListener( - new ItemListener() { - @Override - public void itemStateChanged(final ItemEvent e) { - Preferences.setBoolean("export.application.platform.linux", linuxButton.isSelected()); - } - }); - - final JPanel platformPanel = new JPanel(); - platformPanel.add(windowsButton); - platformPanel.add(Box.createHorizontalStrut(6)); - platformPanel.add(macosxButton); - platformPanel.add(Box.createHorizontalStrut(6)); - platformPanel.add(linuxButton); - platformPanel.setBorder(new TitledBorder("Platforms")); - platformPanel.setAlignmentX(Component.LEFT_ALIGNMENT); - return platformPanel; - } - - private JPanel createPresentPanel() { - final JPanel presentPanel = new JPanel(); - presentPanel.setLayout(new BoxLayout(presentPanel, BoxLayout.Y_AXIS)); - - final JCheckBox showStopButton = new JCheckBox("Show a Stop button"); - showStopButton.setSelected(Preferences.getBoolean("export.application.stop")); - showStopButton.addItemListener( - new ItemListener() { - @Override - public void itemStateChanged(final ItemEvent e) { - Preferences.setBoolean("export.application.stop", showStopButton.isSelected()); - } - }); - showStopButton.setEnabled(Preferences.getBoolean("export.application.fullscreen")); - showStopButton.setBorder(new EmptyBorder(3, 13, 6, 13)); - - final JCheckBox fullScreenButton = new JCheckBox("Full Screen (Present mode)"); - fullScreenButton.setSelected(Preferences.getBoolean("export.application.fullscreen")); - fullScreenButton.addItemListener( - new ItemListener() { - @Override - public void itemStateChanged(final ItemEvent e) { - final boolean selected = fullScreenButton.isSelected(); - Preferences.setBoolean("export.application.fullscreen", selected); - showStopButton.setEnabled(selected); - } - }); - fullScreenButton.setBorder(new EmptyBorder(3, 13, 3, 13)); - - final Box fullScreenBox = Box.createHorizontalBox(); - fullScreenBox.add(fullScreenButton); - fullScreenBox.add(new ColorPreference("run.present.bgcolor", editor)); - fullScreenBox.add(Box.createHorizontalStrut(10)); - fullScreenBox.add(Box.createHorizontalGlue()); - presentPanel.add(fullScreenBox); - - final Box showStopBox = Box.createHorizontalBox(); - showStopBox.add(showStopButton); - showStopBox.add(new ColorPreference("run.present.stop.color", editor)); - showStopBox.add(Box.createHorizontalStrut(10)); - showStopBox.add(Box.createHorizontalGlue()); - presentPanel.add(showStopBox); - - presentPanel.setBorder(new TitledBorder("Full Screen")); - presentPanel.setAlignmentX(Component.LEFT_ALIGNMENT); - - return presentPanel; - } - - private JPanel createEmbedJavaPanel(final int divWidth) { - final JPanel embedPanel = new JPanel(); - embedPanel.setLayout(new BoxLayout(embedPanel, BoxLayout.Y_AXIS)); - - String platformName = null; - if (Platform.isMacOS()) { - platformName = "Mac OS X"; - } else if (Platform.isWindows()) { - platformName = "Windows (" + Platform.getNativeBits() + "-bit)"; - } else if (Platform.isLinux()) { - platformName = "Linux (" + Platform.getNativeBits() + "-bit)"; - } - - final boolean embed = Preferences.getBoolean("export.application.embed_java"); - final String embedWarning = - "

" - + "Embedding Java will make the " - + platformName - + " application " - + "larger, but it will be far more likely to work. " - + "Users on other platforms will need to install Java 7."; - final String nopeWarning = - "
" - + "Users on all platforms will have to install the latest " - + "version of Java 7 from http://java.com/download. " - + "
 "; - // "from java.com/download."; - final JLabel warningLabel = new JLabel(embed ? embedWarning : nopeWarning); - warningLabel.addMouseListener( - new MouseAdapter() { - @Override - public void mousePressed(final MouseEvent event) { - Platform.openURL("http://java.com/download"); - } - }); - warningLabel.setBorder(new EmptyBorder(3, 13, 3, 13)); - - final JCheckBox embedJavaButton = new JCheckBox("Embed Java for " + platformName); - embedJavaButton.setSelected(embed); - embedJavaButton.addItemListener( - new ItemListener() { - @Override - public void itemStateChanged(final ItemEvent e) { - final boolean selected = embedJavaButton.isSelected(); - Preferences.setBoolean("export.application.embed_java", selected); - if (selected) { - warningLabel.setText(embedWarning); - } else { - warningLabel.setText(nopeWarning); - } - } - }); - embedJavaButton.setBorder(new EmptyBorder(3, 13, 3, 13)); - - embedPanel.add(embedJavaButton); - embedPanel.add(warningLabel); - embedPanel.setBorder(new TitledBorder("Embed Java")); - - return embedPanel; - } - - private JPanel createMacSigningWarning(final int divWidth) { - final JPanel signPanel = new JPanel(); - signPanel.setLayout(new BoxLayout(signPanel, BoxLayout.Y_AXIS)); - signPanel.setBorder(new TitledBorder("Code Signing")); - - final StringBuilder thePain = - new StringBuilder( - "In recent versions of OS X, Apple has introduced the \u201CGatekeeper\u201D system, " - + "which makes it more difficult to run applications like those exported from Processing. "); - - if (new File("/usr/bin/codesign_allocate").exists()) { - thePain.append( - "This application will be \u201Cself-signed\u201D which means that Finder may report that the " - + "application is from an \u201Cunidentified developer\u201D. If the application will not " - + "run, try right-clicking the app and selecting Open from the pop-up menu. Or you can visit " - + "System Preferences \u2192 Security & Privacy and select Allow apps downloaded from: anywhere. "); - } else { - thePain.append( - "Gatekeeper requires applications to be \u201Csigned\u201D, or they will be reported as damaged. " - + "To prevent this message, install Xcode (and the Command Line Tools) from the App Store, or visit " - + "System Preferences \u2192 Security & Privacy and select Allow apps downloaded from: anywhere. "); - } - thePain.append( - "To avoid the messages entirely, manually code sign your app. " - + "For more information: https://developer.apple.com/developer-id/"); - - final JLabel area = - new JLabel( - "
" - + thePain.toString() - + "
"); - - area.setBorder(new EmptyBorder(3, 13, 3, 13)); - signPanel.add(area); - signPanel.setAlignmentX(Component.LEFT_ALIGNMENT); - - area.addMouseListener( - new MouseAdapter() { - @Override - public void mousePressed(final MouseEvent event) { - Platform.openURL("https://developer.apple.com/developer-id/"); - } - }); - return signPanel; - } - - static class ColorPreference extends JPanel implements ActionListener { - ColorChooser chooser; - String prefName; - - public ColorPreference(final String pref, final PyEditor editor) { - prefName = pref; - - setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED)); - setPreferredSize(new Dimension(30, 20)); - setMaximumSize(new Dimension(30, 20)); - - addMouseListener( - new MouseAdapter() { - @Override - public void mouseReleased(final MouseEvent e) { - final Color color = Preferences.getColor(prefName); - chooser = new ColorChooser(editor, true, color, "Select", ColorPreference.this); - chooser.show(); - } - }); - } - - @Override - public void paintComponent(final Graphics g) { - g.setColor(Preferences.getColor(prefName)); - final Dimension size = getSize(); - g.fillRect(0, 0, size.width, size.height); - } - - @Override - public void actionPerformed(final ActionEvent e) { - final Color color = chooser.getColor(); - Preferences.setColor(prefName, color); - repaint(); - chooser.hide(); - } - } -} diff --git a/runtime/src/jycessing/mode/export/ExportedSketch.java b/runtime/src/jycessing/mode/export/ExportedSketch.java index 5b3fc216..9b69387a 100644 --- a/runtime/src/jycessing/mode/export/ExportedSketch.java +++ b/runtime/src/jycessing/mode/export/ExportedSketch.java @@ -2,7 +2,7 @@ import java.io.File; import java.io.FileNotFoundException; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; @@ -46,7 +46,7 @@ public ExportedSketch(final String[] args) throws Exception { } final List codeLines = - Files.readAllLines(sketchPath.toPath(), Charset.forName("UTF-8")); + Files.readAllLines(sketchPath.toPath(), StandardCharsets.UTF_8); final StringBuilder code = new StringBuilder(); for (final String line : codeLines) { diff --git a/runtime/src/jycessing/mode/export/Exporter.java b/runtime/src/jycessing/mode/export/Exporter.java index b4386b36..fd86f41d 100644 --- a/runtime/src/jycessing/mode/export/Exporter.java +++ b/runtime/src/jycessing/mode/export/Exporter.java @@ -9,6 +9,7 @@ import processing.app.Platform; import processing.app.Preferences; import processing.app.Sketch; +import processing.app.ui.ExportPrompt; /** * Class that handles doing the actual exporting. All this currently does is figure out libraries @@ -23,70 +24,38 @@ private static void log(final String msg) { } } - // Architecture of the currently running Processing JRE. - // Used to determine what platform we can embed java in. - public static final Arch processingArch; - - static { - if (Platform.getNativeBits() == Arch.X86.bits) { - processingArch = Arch.X86; - } else { - processingArch = Arch.AMD64; - } - } - private final Sketch sketch; - private final PyEditor editor; // I don't really want to pass this around but there's some - // functionality + private final PyEditor editor; - // I need - - public Exporter(final PyEditor editor, final Sketch sketch) { - this.sketch = sketch; + public Exporter(final PyEditor editor) { this.editor = editor; + this.sketch = editor.getSketch(); } public void export() { // Work out the libraries the sketch exports - we only need to do this once. final Set libraries = new ImportExtractor(sketch).getLibraries(); - // Now, do this for each platform: - if (Preferences.getBoolean("export.application.platform.linux")) { - try { - new LinuxExport(Arch.X86, sketch, editor, libraries).export(); - } catch (final IOException e) { - e.printStackTrace(); - editor.statusError("Export to linux32 failed!"); - } - try { - new LinuxExport(Arch.AMD64, sketch, editor, libraries).export(); - } catch (final IOException e) { - e.printStackTrace(); - editor.statusError("Export to linux64 failed!"); - } - } - if (Preferences.getBoolean("export.application.platform.windows")) { - try { - new WindowsExport(Arch.X86, sketch, editor, libraries).export(); - } catch (final IOException e) { - e.printStackTrace(); - editor.statusError("Export to windows32 failed!"); - } - try { - new WindowsExport(Arch.AMD64, sketch, editor, libraries).export(); - } catch (final IOException e) { - e.printStackTrace(); - editor.statusError("Export to windows64 failed!"); - } - } - if (Preferences.getBoolean("export.application.platform.macosx")) { + final String hostVariant = Platform.getVariant(); + for (String variant : Preferences.get(ExportPrompt.EXPORT_VARIANTS).split(",")) { + // Can only embed Java on the native platform + boolean embed = variant.equals(hostVariant) && + Preferences.getBoolean("export.application.embed_java"); + try { - new MacExport(sketch, editor, libraries).export(); + if (variant.startsWith("windows-")) { + new WindowsExport(variant, sketch, editor, libraries, embed).export(); + } else if (variant.startsWith("macos-")) { + new MacExport(variant, sketch, editor, libraries, embed).export(); + } else if (variant.startsWith("linux-")) { + new LinuxExport(variant, sketch, editor, libraries, embed).export(); + } } catch (final IOException e) { e.printStackTrace(); - editor.statusError("Export to macosx failed!"); + editor.statusError("Export to " + variant + " failed"); } } + log("Opening result folder."); Platform.openFolder(sketch.getFolder()); } diff --git a/runtime/src/jycessing/mode/export/LinuxExport.java b/runtime/src/jycessing/mode/export/LinuxExport.java index 0450f273..fb568540 100644 --- a/runtime/src/jycessing/mode/export/LinuxExport.java +++ b/runtime/src/jycessing/mode/export/LinuxExport.java @@ -18,7 +18,6 @@ import processing.app.Sketch; import processing.app.Util; import processing.core.PApplet; -import processing.core.PConstants; /** * Performs an export to Linux (32/64). The linux export folder layout is as follows: @@ -41,26 +40,18 @@ protected void log(final String msg) { } } - public LinuxExport( - final Arch arch, final Sketch sketch, final PyEditor editor, final Set libraries) { - this.id = PConstants.LINUX; - this.arch = arch; - this.name = PConstants.platformNames[id] + arch.bits; + public LinuxExport(final String variant, final Sketch sketch, final PyEditor editor, final Set libraries, boolean embedJava) { + this.variant = variant; this.sketch = sketch; this.editor = editor; this.libraries = libraries; + this.embedJava = embedJava; } @Override public void export() throws IOException { - // Work out user preferences and other possibilities we care about - final boolean embedJava = - (id == PApplet.platform) - && Preferences.getBoolean("export.application.embed_java") - && arch == Exporter.processingArch; - // Work out the folders we'll be (maybe) using - final File destFolder = new File(sketch.getFolder(), "application." + name); + final File destFolder = new File(sketch.getFolder(), variant); final File javaFolder = new File(destFolder, "java"); copyBasicStructure(destFolder); diff --git a/runtime/src/jycessing/mode/export/MacExport.java b/runtime/src/jycessing/mode/export/MacExport.java index 948370e0..0a7cf18a 100644 --- a/runtime/src/jycessing/mode/export/MacExport.java +++ b/runtime/src/jycessing/mode/export/MacExport.java @@ -23,7 +23,6 @@ import processing.app.Sketch; import processing.app.Util; import processing.core.PApplet; -import processing.core.PConstants; /** * A Mac export. @@ -50,23 +49,18 @@ protected void log(final String msg) { } } - public MacExport(final Sketch sketch, final PyEditor editor, final Set libraries) { - this.id = PConstants.MACOSX; - this.name = PConstants.platformNames[id]; + public MacExport(final String variant, final Sketch sketch, final PyEditor editor, final Set libraries, final boolean embedJava) { + this.variant = variant; this.sketch = sketch; this.editor = editor; this.libraries = libraries; - this.arch = Arch.AMD64; + this.embedJava = embedJava; } @Override public void export() throws IOException { - // Work out user preferences and other possibilities we care about - final boolean embedJava = - (id == PApplet.platform) && Preferences.getBoolean("export.application.embed_java"); - // Work out the folders we'll be (maybe) using - final File destFolder = new File(sketch.getFolder(), "application." + name); + final File destFolder = new File(sketch.getFolder(), variant); final File appRootFolder = new File(destFolder, sketch.getName() + ".app"); final File contentsFolder = new File(appRootFolder, "Contents"); final File binFolder = new File(contentsFolder, "MacOS"); diff --git a/runtime/src/jycessing/mode/export/PlatformExport.java b/runtime/src/jycessing/mode/export/PlatformExport.java index 036dd17b..c72f6317 100644 --- a/runtime/src/jycessing/mode/export/PlatformExport.java +++ b/runtime/src/jycessing/mode/export/PlatformExport.java @@ -14,12 +14,14 @@ /** Subclass this to add more platforms, if we ever want to add freebsd or haiku or something */ public abstract class PlatformExport { - protected int id; - protected String name; + //protected int id; + //protected Arch arch; + protected String variant; // replaces id and arch in Processing 4 +// protected String name; protected PyEditor editor; protected Sketch sketch; protected Set libraries; - protected Arch arch; + protected boolean embedJava; /** Instance so that subclasses can override it. */ protected abstract void log(final String msg); @@ -76,8 +78,7 @@ public void copyBasicStructure(final File destFolder) throws IOException { final File libraryExportFolder = new File(libFolder, library.getFolder().getName() + "/library/"); libraryExportFolder.mkdirs(); - for (final File exportFile : - library.getApplicationExports(id, Integer.toString(arch.bits))) { + for (final File exportFile : library.getApplicationExports(variant)) { log("Exporting: " + exportFile); final String exportName = exportFile.getName(); if (!exportFile.exists()) { @@ -104,7 +105,7 @@ public void copyBasicStructure(final File destFolder) throws IOException { log("Copying core processing stuff to export"); for (final File exportFile : new Library(Platform.getContentFile("core")) - .getApplicationExports(id, Integer.toString(arch.bits))) { + .getApplicationExports(variant)) { if (exportFile.isDirectory()) { Util.copyDir(exportFile, new File(jycessingFolder, exportFile.getName())); } else { diff --git a/runtime/src/jycessing/mode/export/WindowsExport.java b/runtime/src/jycessing/mode/export/WindowsExport.java index 5aa2fd96..a89f336d 100644 --- a/runtime/src/jycessing/mode/export/WindowsExport.java +++ b/runtime/src/jycessing/mode/export/WindowsExport.java @@ -47,24 +47,17 @@ protected void log(final String msg) { } } - public WindowsExport( - final Arch arch, final Sketch sketch, final PyEditor editor, final Set libraries) { - this.id = PConstants.WINDOWS; - this.arch = arch; - this.name = PConstants.platformNames[id] + arch.bits; + public WindowsExport(final String variant, final Sketch sketch, final PyEditor editor, final Set libraries, boolean embedJava) { + this.variant = variant; this.sketch = sketch; this.editor = editor; this.libraries = libraries; + this.embedJava = embedJava; } @Override public void export() throws IOException { - final boolean embedJava = - (id == PApplet.platform) - && Preferences.getBoolean("export.application.embed_java") - && arch == Exporter.processingArch; - - final File destFolder = new File(sketch.getFolder(), "application." + name); + final File destFolder = new File(sketch.getFolder(), variant); final File javaFolder = new File(destFolder, "java"); copyBasicStructure(destFolder); @@ -109,7 +102,7 @@ private XML buildLaunch4jConfig(final File destFolder, final boolean embedJava) config.addChild("errTitle").setContent("Sketchy Behavior"); config.addChild("icon").setContent(iconFile.getAbsolutePath()); config.addChild("chdir").setContent("."); - config.addChild(buildJREOptions(embedJava, setMemory, arch)); + config.addChild(buildJREOptions(embedJava, setMemory)); config.addChild(buildRunnerOptions(presentMode, stopButton)); config.addChild(buildClassPathOptions(jycessingFolder)); log("Configuration done: " + config.format(0)); @@ -188,7 +181,7 @@ private void runLaunch4j(final File configFile) throws IOException { } } - private XML buildJREOptions(final boolean embedJava, final boolean setMemory, final Arch arch) { + private XML buildJREOptions(final boolean embedJava, final boolean setMemory) { log("Building JRE options."); final XML jre = new XML("jre"); if (embedJava) { @@ -200,14 +193,7 @@ private XML buildJREOptions(final boolean embedJava, final boolean setMemory, fi // We always add the minVersion tag, which means that the sketch will always try to look for // Java on the system - by default when java isn't embedded, as a fallback when it is jre.addChild("minVersion").setContent("1.7.0_40"); - - switch (arch) { - case AMD64: - jre.addChild("runtimeBits").setContent("64"); - break; - case X86: - jre.addChild("runtimeBits").setContent("32"); - } + jre.addChild("runtimeBits").setContent("64"); if (setMemory) { jre.addChild("initialHeapSize").setContent(Preferences.get("run.options.memory.initial")); diff --git a/runtime/src/jycessing/mode/run/OSXAdapter.java b/runtime/src/jycessing/mode/run/OSXAdapter.java deleted file mode 100644 index fd84d349..00000000 --- a/runtime/src/jycessing/mode/run/OSXAdapter.java +++ /dev/null @@ -1,233 +0,0 @@ -package jycessing.mode.run; - -/* - -File: OSXAdapter.java - -Abstract: Hooks existing preferences/about/quit functionality from an - existing Java app into handlers for the Mac OS X application menu. - Uses a Proxy object to dynamically implement the - com.apple.eawt.ApplicationListener interface and register it with the - com.apple.eawt.Application object. This allows the complete project - to be both built and run on any platform without any stubs or - placeholders. Useful for developers looking to implement Mac OS X - features while supporting multiple platforms with minimal impact. - -Version: 2.0 - -Disclaimer: IMPORTANT: This Apple software is supplied to you by -Apple Inc. ("Apple") in consideration of your agreement to the -following terms, and your use, installation, modification or -redistribution of this Apple software constitutes acceptance of these -terms. If you do not agree with these terms, please do not use, -install, modify or redistribute this Apple software. - -In consideration of your agreement to abide by the following terms, and -subject to these terms, Apple grants you a personal, non-exclusive -license, under Apple's copyrights in this original Apple software (the -"Apple Software"), to use, reproduce, modify and redistribute the Apple -Software, with or without modifications, in source and/or binary forms; -provided that if you redistribute the Apple Software in its entirety and -without modifications, you must retain this notice and the following -text and disclaimers in all such redistributions of the Apple Software. -Neither the name, trademarks, service marks or logos of Apple Inc. -may be used to endorse or promote products derived from the Apple -Software without specific prior written permission from Apple. Except -as expressly stated in this notice, no other rights or licenses, express -or implied, are granted by Apple herein, including but not limited to -any patent rights that may be infringed by your derivative works or by -other works in which the Apple Software may be incorporated. - -The Apple Software is provided by Apple on an "AS IS" basis. APPLE -MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION -THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND -OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. - -IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL -OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, -MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED -AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), -STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -Copyright (C) 2003-2007 Apple, Inc., All Rights Reserved - -*/ - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; - -public class OSXAdapter implements InvocationHandler { - - protected Object targetObject; - protected Method targetMethod; - protected String proxySignature; - - static Object macOSXApplication; - - // Pass this method an Object and Method equipped to perform application shutdown logic - // The method passed should return a boolean stating whether or not the quit should occur - public static void setQuitHandler(final Object target, final Method quitHandler) { - setHandler(new OSXAdapter("handleQuit", target, quitHandler)); - } - - // Pass this method an Object and Method equipped to display application info - // They will be called when the About menu item is selected from the application menu - public static void setAboutHandler(final Object target, final Method aboutHandler) { - final boolean enableAboutMenu = (target != null && aboutHandler != null); - if (enableAboutMenu) { - setHandler(new OSXAdapter("handleAbout", target, aboutHandler)); - } - // If we're setting a handler, enable the About menu item by calling - // com.apple.eawt.Application reflectively - try { - final Method enableAboutMethod = - macOSXApplication - .getClass() - .getDeclaredMethod("setEnabledAboutMenu", new Class[] {boolean.class}); - enableAboutMethod.invoke(macOSXApplication, new Object[] {Boolean.valueOf(enableAboutMenu)}); - } catch (final Exception ex) { - System.err.println("OSXAdapter could not access the About Menu"); - ex.printStackTrace(); - } - } - - // Pass this method an Object and a Method equipped to display application options - // They will be called when the Preferences menu item is selected from the application menu - public static void setPreferencesHandler(final Object target, final Method prefsHandler) { - final boolean enablePrefsMenu = (target != null && prefsHandler != null); - if (enablePrefsMenu) { - setHandler(new OSXAdapter("handlePreferences", target, prefsHandler)); - } - // If we're setting a handler, enable the Preferences menu item by calling - // com.apple.eawt.Application reflectively - try { - final Method enablePrefsMethod = - macOSXApplication - .getClass() - .getDeclaredMethod("setEnabledPreferencesMenu", new Class[] {boolean.class}); - enablePrefsMethod.invoke(macOSXApplication, new Object[] {Boolean.valueOf(enablePrefsMenu)}); - } catch (final Exception ex) { - System.err.println("OSXAdapter could not access the About Menu: " + ex); - } - } - - // Pass this method an Object and a Method equipped to handle document events from the Finder - // Documents are registered with the Finder via the CFBundleDocumentTypes dictionary in the - // application bundle's Info.plist - public static void setFileHandler(final Object target, final Method fileHandler) { - setHandler( - new OSXAdapter("handleOpenFile", target, fileHandler) { - // Override OSXAdapter.callTarget to send information on the - // file to be opened - @Override - public boolean callTarget(final Object appleEvent) { - if (appleEvent != null) { - try { - final Method getFilenameMethod = - appleEvent.getClass().getDeclaredMethod("getFilename", (Class[]) null); - final String filename = - (String) getFilenameMethod.invoke(appleEvent, (Object[]) null); - this.targetMethod.invoke(this.targetObject, new Object[] {filename}); - } catch (final Exception ex) { - - } - } - return true; - } - }); - } - - // setHandler creates a Proxy object from the passed OSXAdapter and adds it as an - // ApplicationListener - public static void setHandler(final OSXAdapter adapter) { - try { - final Class applicationClass = Class.forName("com.apple.eawt.Application"); - if (macOSXApplication == null) { - macOSXApplication = - applicationClass.getConstructor((Class[]) null).newInstance((Object[]) null); - } - final Class applicationListenerClass = Class.forName("com.apple.eawt.ApplicationListener"); - final Method addListenerMethod = - applicationClass.getDeclaredMethod( - "addApplicationListener", new Class[] {applicationListenerClass}); - // Create a proxy object around this handler that can be reflectively added as an Apple - // ApplicationListener - final Object osxAdapterProxy = - Proxy.newProxyInstance( - OSXAdapter.class.getClassLoader(), new Class[] {applicationListenerClass}, adapter); - addListenerMethod.invoke(macOSXApplication, new Object[] {osxAdapterProxy}); - } catch (final ClassNotFoundException cnfe) { - System.err.println( - "This version of Mac OS X does not support the Apple EAWT. ApplicationEvent handling has been disabled (" - + cnfe - + ")"); - } catch (final Exception ex) { // Likely a NoSuchMethodException or an IllegalAccessException - // loading/invoking eawt.Application methods - System.err.println("Mac OS X Adapter could not talk to EAWT:" + ex); - } - } - - // Each OSXAdapter has the name of the EAWT method it intends to listen for (handleAbout, for - // example), - // the Object that will ultimately perform the task, and the Method to be called on that Object - protected OSXAdapter(final String proxySignature, final Object target, final Method handler) { - this.proxySignature = proxySignature; - this.targetObject = target; - this.targetMethod = handler; - } - - // Override this method to perform any operations on the event - // that comes with the various callbacks - // See setFileHandler above for an example - public boolean callTarget(final Object appleEvent) - throws InvocationTargetException, IllegalAccessException { - final Object result = targetMethod.invoke(targetObject, (Object[]) null); - if (result == null) { - return true; - } - return Boolean.valueOf(result.toString()).booleanValue(); - } - - // InvocationHandler implementation - // This is the entry point for our proxy object; it is called every time an ApplicationListener - // method is invoked - @Override - public Object invoke(final Object proxy, final Method method, final Object[] args) - throws Throwable { - if (isCorrectMethod(method, args)) { - final boolean handled = callTarget(args[0]); - setApplicationEventHandled(args[0], handled); - } - // All of the ApplicationListener methods are void; return null regardless of what happens - return null; - } - - // Compare the method that was called to the intended method when the OSXAdapter instance was - // created - // (e.g. handleAbout, handleQuit, handleOpenFile, etc.) - protected boolean isCorrectMethod(final Method method, final Object[] args) { - return (targetMethod != null && proxySignature.equals(method.getName()) && args.length == 1); - } - - // It is important to mark the ApplicationEvent as handled and cancel the default behavior - // This method checks for a boolean result from the proxy method and sets the event accordingly - protected void setApplicationEventHandled(final Object event, final boolean handled) { - if (event != null) { - try { - final Method setHandledMethod = - event.getClass().getDeclaredMethod("setHandled", new Class[] {boolean.class}); - // If the target method returns a boolean, use that as a hint - setHandledMethod.invoke(event, new Object[] {Boolean.valueOf(handled)}); - } catch (final Exception ex) { - System.err.println("OSXAdapter was unable to handle an ApplicationEvent: " + event); - ex.printStackTrace(); - } - } - } -} diff --git a/runtime/src/jycessing/mode/run/RMIUtils.java b/runtime/src/jycessing/mode/run/RMIUtils.java index a096b6bf..5cfdf987 100644 --- a/runtime/src/jycessing/mode/run/RMIUtils.java +++ b/runtime/src/jycessing/mode/run/RMIUtils.java @@ -1,6 +1,5 @@ package jycessing.mode.run; -import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; @@ -19,19 +18,10 @@ public class RMIUtils { static final int RMI_PORT = 8220; - private static final RMIClientSocketFactory clientFactory = new RMIClientSocketFactory() { - @Override - public Socket createSocket(final String host, final int port) throws IOException { - return new Socket(host, port); - } - }; + private static final RMIClientSocketFactory clientFactory = Socket::new; - private static final RMIServerSocketFactory serverFactory = new RMIServerSocketFactory() { - @Override - public ServerSocket createServerSocket(final int port) throws IOException { - return new ServerSocket(port, 50, InetAddress.getLoopbackAddress()); - } - }; + private static final RMIServerSocketFactory serverFactory = + port -> new ServerSocket(port, 50, InetAddress.getLoopbackAddress()); static { System.setProperty("sun.rmi.transport.tcp.localHostNameTimeOut", "1000"); @@ -90,7 +80,7 @@ public static void bind(final Remote remote, final Class remot try { log("Unbinding " + registryKey + " from registry."); registry().unbind(registryKey); - } catch (final Exception e) { + } catch (final Exception ignored) { } })); } catch (final Exception e) { diff --git a/runtime/src/jycessing/mode/run/SketchRunner.java b/runtime/src/jycessing/mode/run/SketchRunner.java index cb79df20..0a274d5f 100644 --- a/runtime/src/jycessing/mode/run/SketchRunner.java +++ b/runtime/src/jycessing/mode/run/SketchRunner.java @@ -1,16 +1,17 @@ package jycessing.mode.run; +import java.awt.Desktop; import java.rmi.RemoteException; +import processing.app.Platform; +import processing.app.SketchException; + import jycessing.Printer; import jycessing.PythonSketchError; import jycessing.Runner; import jycessing.SketchPositionListener; import jycessing.mode.PythonMode; import jycessing.mode.run.RMIUtils.RMIProblem; -import processing.app.SketchException; -import processing.core.PApplet; -import processing.core.PConstants; public class SketchRunner implements SketchService { @@ -28,14 +29,21 @@ private static void log(final String msg) { public SketchRunner(final String id, final ModeService modeService) { this.id = id; this.modeService = modeService; - if (PApplet.platform == PConstants.MACOSX) { + if (Platform.isMacOS()) { + //OSXAdapter.setQuitHandler(this, this.getClass().getMethod("preventUserQuit")); try { - OSXAdapter.setQuitHandler(this, this.getClass().getMethod("preventUserQuit")); + Desktop.getDesktop().setQuitHandler((event, quitResponse) -> { + if (preventUserQuit()) { + quitResponse.performQuit(); + } else { + quitResponse.cancelQuit(); + } + }); } catch (final Throwable e) { System.err.println(e.getMessage()); } } - new Thread(() -> Runner.warmup(), "SketchRunner Warmup Thread").start(); + new Thread(Runner::warmup, "SketchRunner Warmup Thread").start(); } /** @@ -57,7 +65,7 @@ public boolean preventUserQuit() { return true; } log("Cancelling quit, but stopping sketch."); - new Thread(() -> stopSketch()).start(); + new Thread(this::stopSketch).start(); return false; } @@ -68,7 +76,7 @@ public void shutdown() { System.exit(0); } - private abstract class RemotePrinter implements Printer { + private abstract static class RemotePrinter implements Printer { abstract protected void doPrint(String s) throws RemoteException; public void print(final Object o) { diff --git a/runtime/src/jycessing/mode/run/SketchServiceProcess.java b/runtime/src/jycessing/mode/run/SketchServiceProcess.java index b2e018fa..352aec62 100644 --- a/runtime/src/jycessing/mode/run/SketchServiceProcess.java +++ b/runtime/src/jycessing/mode/run/SketchServiceProcess.java @@ -155,6 +155,39 @@ private ProcessBuilder createServerCommand() { // enable assertions command.add("-ea"); + // Give introspection permissions. + final String[] exports = {"java.base/sun.nio.ch", // + "java.base/java.nio", // + "java.base/java.lang", // + "java.base/java.lang.constant", // + "java.base/java.lang.reflect", // + "java.base/java.io", // + "jdk.unsupported/sun.misc", // + "java.base/java.nio.charset", // + "java.base/java.util", // + "java.base/java.net", // + "java.base/java.text", // + "java.base/java.util.concurrent", // + "java.sql/java.sql", // + "java.base/java.util.concurrent.locks", // + "java.base/sun.util.calendar", // + "java.base/java.nio.channels", // + "java.base/java.security", // + "java.base/java.util.concurrent.atomic", // + "java.base/java.security.cert", // + "java.base/javax.net.ssl", // + "java.naming/javax.naming.ldap", // + "java.base/java.security.spec", // + "java.base/javax.net.ssl", // + "java.base/javax.security.auth.x500", // + "java.security.jgss/org.ietf.jgss", // + "java.desktop/com.apple.eawt", // + }; + for (final String s : exports) { + command.add(String.format("--add-exports=%s=ALL-UNNAMED", s)); + command.add(String.format("--add-opens=%s=ALL-UNNAMED", s)); + } + // Run the SketchRunner main. command.add(SketchRunner.class.getName()); diff --git a/testmode.sh b/testmode.sh index f7356c29..d318e0d1 100755 --- a/testmode.sh +++ b/testmode.sh @@ -2,12 +2,12 @@ export VERBOSE_PYTHON_MODE=true PROCESSINGPY=$(pwd) -PROCESSING=../processing +PROCESSING=../processing4 MODES=~/Documents/Processing/modes; if [[ $(uname) == 'Darwin' ]]; then - RUNPROCESSING=$PROCESSING/build/macosx/work/Processing.app/Contents/MacOS/Processing -else + RUNPROCESSING=$PROCESSING/build/macos/work/Processing.app/Contents/MacOS/Processing +else RUNPROCESSING="$PROCESSING/build/linux/work/processing" MODES=~/sketchbook/modes; fi