() {
+ @Override
+ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs)
+ throws IOException {
+ Files.delete(file);
+ return CONTINUE;
+ }
- @Override
- public FileVisitResult postVisitDirectory(final Path dir, final IOException exc)
- throws IOException {
- if (exc != null) {
- throw exc;
- }
- Files.delete(dir);
- return CONTINUE;
- }
- });
+ @Override
+ public FileVisitResult postVisitDirectory(final Path dir, final IOException exc)
+ throws IOException {
+ if (exc != null) {
+ throw exc;
+ }
+ Files.delete(dir);
+ return CONTINUE;
+ }
+ });
}
public static void copy(final Path src, final Path target) throws IOException {
diff --git a/runtime/src/jycessing/LibraryImporter.java b/runtime/src/jycessing/LibraryImporter.java
index 9c4e91e0..50f432e6 100644
--- a/runtime/src/jycessing/LibraryImporter.java
+++ b/runtime/src/jycessing/LibraryImporter.java
@@ -1,7 +1,6 @@
package jycessing;
import java.io.File;
-import java.io.FileFilter;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Field;
@@ -15,6 +14,7 @@
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
@@ -34,16 +34,16 @@
/**
* {@link LibraryImporter} contributes the add_library function to processing.py.
- *
- * add_library causes the given Processing library (all of its jar files and
- * library subdirectories potentially containing native code) to be added to the
- * system {@link ClassLoader}, the native code search path, and the Jython sys.path.
- * It then generates import statements bringing all top-level classes available
- * in the main library jar file into the sketch's namespace.
- *
- *
Note: Once jar files and directories have been added in this fashion to the
- * classloader and native lib search path, they're good and stuck there. In practice,
- * this hasn't presented any difficulty. But it's good to keep in mind.
+ *
+ *
add_library causes the given Processing library (all of its jar files and library
+ * subdirectories potentially containing native code) to be added to the system {@link ClassLoader},
+ * the native code search path, and the Jython sys.path. It then generates import statements
+ * bringing all top-level classes available in the main library jar file into the sketch's
+ * namespace.
+ *
+ *
Note: Once jar files and directories have been added in this fashion to the classloader and
+ * native lib search path, they're good and stuck there. In practice, this hasn't presented any
+ * difficulty. But it's good to keep in mind.
*/
class LibraryImporter {
private static void log(final String msg) {
@@ -87,14 +87,13 @@ public PyObject __call__(final PyObject[] args, final String[] kws) {
}
/**
- * Locate the library in the library folder "libName", find what
- * it exports for the current platform, and add exports to the
- * system classpath, the system native library path, and jython's
+ * Locate the library in the library folder "libName", find what it exports for the current
+ * platform, and add exports to the system classpath, the system native library path, and jython's
* sys.path.
- *
- * Then, go through the main jar file of the library and import
- * all of its publicly exposed classes.
- *
+ *
+ *
Then, go through the main jar file of the library and import all of its publicly exposed
+ * classes.
+ *
* @param libName The name of the library to import
*/
protected void addLibrary(final String libName) {
@@ -130,12 +129,33 @@ protected void addLibrary(final String libName) {
if (libDir == null) {
interp.exec("raise Exception('This sketch requires the \"" + libName + "\" library.')");
}
+
final File contentsDir = new File(libDir, "library");
if (!contentsDir.exists()) {
interp.exec("raise Exception('The library " + libName + " is malformed and won't import.')");
}
final File mainJar = new File(contentsDir, libName + ".jar");
+ log("mainJar: " + mainJar);
+ log("Adding dir: " + contentsDir);
+ recursivelyAddJarsToClasspath(contentsDir);
+ log("Platform: " + PLATFORM + " Bits: " + BITS);
+ if (PLATFORM.indexOf("windows") != -1) {
+ File nativeDir;
+ nativeDir = new File(libDir, "library/windows" + BITS);
+ if (!nativeDir.isDirectory()) {
+ nativeDir = contentsDir;
+ }
+ recursivelyLoadNativeLibraries(nativeDir);
+ }
+
+ if (mainJar.exists()) {
+ log("adding mainJar");
+ importPublicClassesFromJar(mainJar);
+ }
+ }
+
+ private void recursivelyAddJarsToClasspath(final File contentsDir) {
final List resources = findResources(contentsDir);
final PySystemState sys = Py.getSystemState();
for (final File resource : resources) {
@@ -145,26 +165,74 @@ protected void addLibrary(final String libName) {
addJarToClassLoader(resource.getAbsoluteFile());
log("Appending " + resource.getAbsolutePath() + " to sys.path.");
- sys.path.append(Py.newString(resource.getAbsolutePath()));
+ sys.path.append(Py.newStringUTF8(resource.getAbsolutePath()));
+ } else if (resource.isDirectory()) {
+ recursivelyAddToClasspath(resource);
+ }
+ }
+ }
- // Are we missing any extensions?
- } else if (name.matches("^.*\\.(so|dll|dylib|jnilib)$")) {
+ private void recursivelyLoadNativeLibraries(final File contentsDir) {
+ final List resources = findResources(contentsDir);
+ final PySystemState sys = Py.getSystemState();
+ LinkedList stack = new LinkedList(resources);
+ log("Dependency stack length: " + stack.size());
+ // Terrible, horrible, no good, very bad, hack to load dependencies
+ // There are n! permutations here, which is unrealistic.
+ // 4096 seems like a realistic number to give up on
+ int timeToDie = 4096;
+ while (!stack.isEmpty() && timeToDie > 0) {
+ File resource = stack.poll();
+ final String name = resource.getName();
+ log("Resource name: " + name);
+ if (name.matches("^.*\\.(so|dll|dylib|jnilib)$")) {
// Add *containing directory* to native search path
+ log("addDirectoryToNativeSearchPath: " + resource.getAbsoluteFile().getParentFile().getAbsolutePath());
addDirectoryToNativeSearchPath(resource.getAbsoluteFile().getParentFile());
+ log("Loading library: " + resource.getAbsoluteFile().getName());
+ try {
+ System.load(resource.getAbsolutePath());
+ } catch (UnsatisfiedLinkError e) {
+ stack.add(resource);
+ }
+ } else if (resource.isDirectory()) {
+ recursivelyAddToClasspath(resource);
+ }
+ timeToDie--;
+ if (timeToDie == 0) {
+ throw new UnsatisfiedLinkError("Unable to satisfy dependencies for " + name + " after 4096 tries.");
}
}
+ }
- importPublicClassesFromJar(mainJar);
+ private void recursivelyAddToClasspath(final File contentsDir) {
+ final List resources = findResources(contentsDir);
+ final PySystemState sys = Py.getSystemState();
+ for (final File resource : resources) {
+ final String name = resource.getName();
+ if (name.endsWith(".jar") || name.endsWith(".zip")) {
+ // Contains stuff we want
+ addJarToClassLoader(resource.getAbsoluteFile());
+
+ log("Appending " + resource.getAbsolutePath() + " to sys.path.");
+ sys.path.append(Py.newStringUTF8(resource.getAbsolutePath()));
+ } else if (name.matches("^.*\\.(so|dll|dylib|jnilib)$")) {
+ // Add *containing directory* to native search path
+ log("addDirectoryToNativeSearchPath: " + resource.getAbsoluteFile().getParentFile().getName());
+ addDirectoryToNativeSearchPath(resource.getAbsoluteFile().getParentFile());
+ } else if (resource.isDirectory()) {
+ recursivelyAddToClasspath(resource);
+ }
+ }
}
/**
- * Find all of the resources a library requires on this platform.
- * See https://github.com/processing/processing/wiki/Library-Basics.
- *
- * First, finds the library.
- * Second, tries to parse export.txt, and follow its instructions.
+ * Find all of the resources a library requires on this platform. See
+ * https://github.com/processing/processing/wiki/Library-Basics.
+ *
+ * First, finds the library. Second, tries to parse export.txt, and follow its instructions.
* Third, tries to understand folder structure, and export according to that.
- *
+ *
* @param libName The name of the library to add.
* @return The list of files we need to import.
*/
@@ -174,7 +242,9 @@ protected List findResources(final File contentsDir) {
resources = findResourcesFromExportTxt(contentsDir);
if (resources == null) {
log("Falling back to directory structure.");
- resources = findResourcesFromDirectoryStructure(contentsDir);
+ resources = Arrays.asList(contentsDir.listFiles((final File dir, final String s) -> {
+ return !s.startsWith(".");
+ }));
}
return resources;
}
@@ -211,7 +281,7 @@ private List findResourcesFromExportTxt(final File contentsDir) {
}
final List resources = new ArrayList<>();
for (final String resourceName : resourceNames) {
- final File resource = new File(contentsDir, resourceName);
+ final File resource = new File(contentsDir, resourceName).getAbsoluteFile();
if (resource.exists()) {
resources.add(resource);
} else {
@@ -223,49 +293,11 @@ private List findResourcesFromExportTxt(final File contentsDir) {
return resources;
}
- private List findResourcesFromDirectoryStructure(final File contentsDir) {
- final List childNames = Arrays.asList(contentsDir.list());
- final List resources = new ArrayList();
-
- // Find platform-specific stuff
- File platformDir = null;
- if (childNames.contains(PLATFORM + BITS)) {
- final File potentialPlatformDir = new File(contentsDir, PLATFORM + BITS);
- if (potentialPlatformDir.isDirectory()) {
- platformDir = potentialPlatformDir;
- }
- }
- if (platformDir == null && childNames.contains(PLATFORM)) {
- final File potentialPlatformDir = new File(contentsDir, PLATFORM);
- if (potentialPlatformDir.isDirectory()) {
- platformDir = potentialPlatformDir;
- }
- }
- if (platformDir != null) {
- log("Found platform-specific directory " + platformDir.getAbsolutePath());
- for (final File resource : platformDir.listFiles()) {
- resources.add(resource);
- }
- }
-
- // Find multi-platform stuff; always do this
- final File[] commonResources = contentsDir.listFiles(new FileFilter() {
- @Override
- public boolean accept(final File file) {
- return !file.isDirectory();
- }
- });
- for (final File resource : commonResources) {
- resources.add(resource);
- }
- return resources;
- }
-
/**
- * Parse an export.txt file to figure out what we need to load for this platform.
- * This is all duplicated from processing.app.Library / processing.app.Base,
- * but we don't have the PDE around at runtime so we can't use them.
- *
+ * Parse an export.txt file to figure out what we need to load for this platform. This is all
+ * duplicated from processing.app.Library / processing.app.Base, but we don't have the PDE around
+ * at runtime so we can't use them.
+ *
* @param exportTxt The export.txt file; must exist.
*/
private Map parseExportTxt(final File exportTxt) throws Exception {
@@ -290,8 +322,8 @@ private Map parseExportTxt(final File exportTxt) throws Except
}
/**
- * Use a brittle and egregious hack to forcibly add the given jar file to the
- * system classloader.
+ * Use a brittle and egregious hack to forcibly add the given jar file to the system classloader.
+ *
* @param jar The jar to add to the system classloader.
*/
private void addJarToClassLoader(final File jar) {
@@ -315,14 +347,11 @@ private void addJarToClassLoader(final File jar) {
}
/**
- * Add the given path to the list of paths searched for DLLs (as in those
- * loaded by loadLibrary). A hack, which depends on the presence of a
- * particular field in ClassLoader. Known to work on all recent Sun JVMs and
- * OS X.
+ * Add the given path to the list of paths searched for DLLs (as in those loaded by loadLibrary).
+ * A hack, which depends on the presence of a particular field in ClassLoader. Known to work on
+ * all recent Sun JVMs and OS X.
*
- *
- * See this
- * thread.
+ *
See this thread.
*/
private void addDirectoryToNativeSearchPath(final File dllDir) {
final String newPath = dllDir.getAbsolutePath();
@@ -340,13 +369,12 @@ private void addDirectoryToNativeSearchPath(final File dllDir) {
field.set(null, tmp);
log("Added " + newPath + " to java.library.path.");
} catch (final Exception e) {
- System.err.println("While attempting to add " + newPath
- + " to the processing.py library search path: " + e.getClass().getSimpleName() + "--"
- + e.getMessage());
+ System.err.println(
+ "While attempting to add " + newPath + " to the processing.py library search path: "
+ + e.getClass().getSimpleName() + "--" + e.getMessage());
}
}
-
private static final Pattern validPythonIdentifier = Pattern.compile("[a-zA-Z_][a-zA-Z0-9_]*");
/*
@@ -357,9 +385,9 @@ private void addDirectoryToNativeSearchPath(final File dllDir) {
com.foo.Banana$1.class
com.foo.Banana$2.class
com.bar.Kiwi.class
-
+
then we'll generate these import statements:
-
+
from com.foo import Banana
from com.bar import Kiwi
*/
@@ -396,6 +424,18 @@ private void importPublicClassesFromJar(final File jarPath) {
continue;
}
+ /**
+ * A class in a library jar may refer to other classes not present in this runtime.
+ * Proactively draw out such an error now, so we can catch it (rather than way inside the
+ * Jython interpreter, where it unceremoniously throws and terminates). The particular
+ * example that led to this hack is proscene's reference to Android classes.
+ */
+ try {
+ Class.forName(String.format("%s.%s", packageName, className)).getMethods();
+ } catch (final ClassNotFoundException | NoClassDefFoundError e) {
+ log("Rejecting " + name);
+ continue;
+ }
final String importStatement = String.format("from %s import %s", packageName, className);
log(importStatement);
interp.exec(importStatement);
diff --git a/runtime/src/jycessing/MixedModeError.java b/runtime/src/jycessing/MixedModeError.java
index 1bb84329..e9676b06 100644
--- a/runtime/src/jycessing/MixedModeError.java
+++ b/runtime/src/jycessing/MixedModeError.java
@@ -8,5 +8,4 @@ public MixedModeError() {
public MixedModeError(final String message, final String fileName, final int line) {
super(message, fileName, line);
}
-
}
diff --git a/runtime/src/jycessing/MixedSmoothError.java b/runtime/src/jycessing/MixedSmoothError.java
new file mode 100644
index 00000000..32940ca8
--- /dev/null
+++ b/runtime/src/jycessing/MixedSmoothError.java
@@ -0,0 +1,11 @@
+package jycessing;
+
+public class MixedSmoothError extends PythonSketchError {
+ public MixedSmoothError() {
+ super("smooth() and noSmooth() cannot be used in the same sketch.");
+ }
+
+ public MixedSmoothError(final String message, final String fileName, final int line) {
+ super(message, fileName, line);
+ }
+}
diff --git a/runtime/src/jycessing/PAppletJythonDriver.java b/runtime/src/jycessing/PAppletJythonDriver.java
old mode 100644
new mode 100755
index 0238e01d..b310a352
--- a/runtime/src/jycessing/PAppletJythonDriver.java
+++ b/runtime/src/jycessing/PAppletJythonDriver.java
@@ -1,21 +1,20 @@
/*
* Copyright 2010 Jonathan Feinberg
*
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package jycessing;
import java.awt.Component;
+import java.awt.Frame;
import java.awt.Point;
import java.awt.Window;
import java.awt.event.ComponentAdapter;
@@ -31,17 +30,18 @@
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;
import java.util.concurrent.CountDownLatch;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import jycessing.IOUtil.ResourceReader;
-import jycessing.mode.run.WrappedPrintStream;
-import jycessing.mode.run.WrappedPrintStream.PushedOut;
+import javax.sound.midi.MidiMessage;
import org.python.core.CompileMode;
import org.python.core.CompilerFlags;
+import org.python.core.JyAttribute;
import org.python.core.Py;
import org.python.core.PyBaseCode;
import org.python.core.PyBoolean;
@@ -53,6 +53,7 @@
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;
import org.python.core.PyString;
import org.python.core.PyStringMap;
@@ -62,6 +63,14 @@
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;
@@ -69,18 +78,11 @@
import processing.core.PSurface;
import processing.event.KeyEvent;
import processing.event.MouseEvent;
+import processing.javafx.PSurfaceFX;
+import processing.opengl.PGraphicsOpenGL;
import processing.opengl.PShader;
import processing.opengl.PSurfaceJOGL;
-import com.google.common.base.Charsets;
-import com.google.common.io.Files;
-import com.jogamp.newt.opengl.GLWindow;
-
-/**
- *
- * @author Jonathan Feinberg <jdf@pobox.com>
- *
- */
@SuppressWarnings("serial")
public class PAppletJythonDriver extends PApplet {
@@ -92,8 +94,8 @@ public class PAppletJythonDriver extends PApplet {
private static final ResourceReader resourceReader =
new ResourceReader(PAppletJythonDriver.class);
+ private static final String GET_SETTINGS_SCRIPT = resourceReader.readText("get_settings.py");
private static final String DETECT_MODE_SCRIPT = resourceReader.readText("detect_sketch_mode.py");
-
private static final String PREPROCESS_SCRIPT = resourceReader.readText("pyde_preprocessor.py");
static {
@@ -102,6 +104,8 @@ public class PAppletJythonDriver extends PApplet {
useNativeSelect = false;
}
+ private Field frameField;
+
private PythonSketchError terminalException = null;
protected final PyStringMap builtins;
@@ -113,7 +117,9 @@ public class PAppletJythonDriver extends PApplet {
private final CountDownLatch finishedLatch = new CountDownLatch(1);
private enum Mode {
- STATIC, ACTIVE, MIXED
+ STATIC,
+ ACTIVE,
+ MIXED
}
// A static-mode sketch must be interpreted from within the setup() method.
@@ -121,12 +127,24 @@ private enum Mode {
// definitions, which we then invoke during the run loop.
private final Mode mode;
+ private int detectedWidth, detectedHeight, detectedPixelDensity;
+ private String detectedRenderer;
+ private boolean detectedSmooth, detectedNoSmooth, detectedFullScreen;
+ private PyObject processedStaticSketch;
+
+ private static int argCount(final PyObject func) {
+ if (!(func instanceof PyFunction)) {
+ return -1;
+ }
+ return ((PyBaseCode) ((PyFunction) func).__code__).co_argcount;
+ }
+
/**
- * The Processing event handling functions can take 0 or 1 argument.
- * This class represents such a function.
- *
If the user did not implement the variant that takes an event,
- * then we have to pass through to the super implementation, or else
- * the zero-arg version won't get called.
+ * The Processing event handling functions can take 0 or 1 argument. This class represents such a
+ * function.
+ *
+ *
If the user did not implement the variant that takes an event, then we have to pass through
+ * to the super implementation, or else the zero-arg version won't get called.
*/
private abstract class EventFunction {
private final PyFunction func;
@@ -135,8 +153,8 @@ private abstract class EventFunction {
protected abstract void callSuper(T event);
public EventFunction(final String funcName) {
- func = (PyFunction)interp.get(funcName);
- argCount = func == null ? -1 : ((PyBaseCode)(func).__code__).co_argcount;
+ func = (PyFunction) interp.get(funcName);
+ argCount = argCount(func);
}
public void invoke() {
@@ -154,27 +172,66 @@ public void invoke(final T event) {
}
}
+ /**
+ * We maintain a map from function name to function in order to implement {@link
+ * PApplet#method(String)}.
+ */
+ private final Map allFunctionsByName = new HashMap<>();
+
// These are all of the methods that PApplet might call in your sketch. If
// you have implemented a method, we save it and call it.
- private PyObject setupMeth, settingsMeth, drawMeth, initMeth, pauseMeth, resumeMeth, stopMeth,
- sketchFullScreenMeth, sketchWidthMeth, sketchHeightMeth, sketchRendererMeth;
+ private PyObject setupMeth, settingsMeth, drawMeth, pauseMeth, resumeMeth;
+
+ // For compatibility, we look for definitions of both stop() and dispose()
+ // in the user's sketch, and call whatever's defined of those two when the sketch
+ // is dispose()d. If you define both, they'll both be called.
+ private PyObject stopMeth, disposeMeth;
private EventFunction keyPressedFunc, keyReleasedFunc, keyTypedFunc;
- private EventFunction mousePressedFunc, mouseClickedFunc, mouseMovedFunc,
- mouseReleasedFunc, mouseDraggedFunc;
+ private EventFunction mousePressedFunc,
+ mouseClickedFunc,
+ mouseMovedFunc,
+ mouseReleasedFunc,
+ mouseDraggedFunc;
private PyObject mouseWheelMeth; // Can only be called with a MouseEvent; no need for shenanigans
- // Implement the Video library's callback.
+ // Implement the Video library's callbacks.
private PyObject captureEventMeth, movieEventMeth;
+ // Implement the Serial library's callback.
+ private PyObject serialEventMeth;
+
+ // Implement the net library's callbacks.
+ private PyObject clientEventMeth, disconnectEventMeth, serverEventMeth;
+
+ // Implement the oscP5 library's callback.
+ private PyObject oscEventMeth;
+
+ // Implement themidibus callbacks.
+ private PyObject noteOn3Meth,
+ noteOff3Meth,
+ controllerChange3Meth,
+ rawMidi1Meth,
+ midiMessage1Meth,
+ noteOn5Meth,
+ noteOff5Meth,
+ controllerChange5Meth,
+ rawMidi3Meth,
+ midiMessage3Meth;
+
+ // Implement processing_websockets callbacks.
+ private PyObject webSocketEventMeth, webSocketServerEventMeth;
+
private SketchPositionListener sketchPositionListener;
private void processSketch(final String scriptSource) throws PythonSketchError {
try {
interp.set("__processing_source__", programText);
final PyCode code =
- Py.compile_flags(scriptSource, pySketchPath.toString(), CompileMode.exec,
- new CompilerFlags());
- interp.exec(code);
+ Py.compile_flags(
+ scriptSource, pySketchPath.toString(), CompileMode.exec, new CompilerFlags());
+ try (PushedOut out = wrappedStdout.pushStdout()) {
+ interp.exec(code);
+ }
Py.flushLine();
} catch (Throwable t) {
while (t.getCause() != null) {
@@ -186,6 +243,7 @@ private void processSketch(final String scriptSource) throws PythonSketchError {
/**
* Handy method for raising a Python exception in the current interpreter frame.
+ *
* @param msg TypeError message.
*/
private PyObject raiseTypeError(final String msg) {
@@ -198,18 +256,18 @@ private static PythonSketchError toSketchException(Throwable t) {
t = t.getCause();
}
if (t instanceof PythonSketchError) {
- return (PythonSketchError)t;
+ return (PythonSketchError) t;
}
if (t instanceof PySyntaxError) {
- final PySyntaxError e = (PySyntaxError)t;
- return extractSketchErrorFromPyExceptionValue((PyTuple)e.value);
+ final PySyntaxError e = (PySyntaxError) t;
+ return extractSketchErrorFromPyExceptionValue((PyTuple) e.value);
}
if (t instanceof PyIndentationError) {
- final PyIndentationError e = (PyIndentationError)t;
- return extractSketchErrorFromPyExceptionValue((PyTuple)e.value);
+ final PyIndentationError e = (PyIndentationError) t;
+ return extractSketchErrorFromPyExceptionValue((PyTuple) e.value);
}
if (t instanceof PyException) {
- final PyException e = (PyException)t;
+ final PyException e = (PyException) t;
final Pattern tbParse =
Pattern.compile("^\\s*File \"([^\"]+)\", line (\\d+)", Pattern.MULTILINE);
final Matcher m = tbParse.matcher(e.toString());
@@ -228,7 +286,7 @@ private static PythonSketchError toSketchException(Throwable t) {
}
line = Integer.parseInt(m.group(2)) - 1;
}
- if (((PyType)e.type).getName().equals("ImportError")) {
+ if (((PyType) e.type).getName().equals("ImportError")) {
final Pattern importStar = Pattern.compile("import\\s+\\*");
if (importStar.matcher(e.toString()).find()) {
return new PythonSketchError("import * does not work in this environment.", file, line);
@@ -242,13 +300,13 @@ private static PythonSketchError toSketchException(Throwable t) {
}
private static PythonSketchError extractSketchErrorFromPyExceptionValue(final PyTuple tup) {
- final String pyMessage = (String)tup.get(0);
+ final String pyMessage = (String) tup.get(0);
final String message = maybeMakeFriendlyMessage(pyMessage);
- final PyTuple context = (PyTuple)tup.get(1);
- final File file = new File((String)context.get(0));
+ 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)).intValue() - 1;
+ final int column = ((Integer) context.get(2)).intValue();
if (pyMessage.startsWith("no viable alternative")) {
return noViableAlternative(file, lineNumber, column, pyMessage);
}
@@ -259,21 +317,24 @@ private static PythonSketchError extractSketchErrorFromPyExceptionValue(final Py
private static final Pattern NAKED_COLOR = Pattern.compile("[(,]\\s*#([0-9a-fA-F]{6})\\b");
/**
- * The message "no vialble alternative" is a strong indication that there's an unclosed
- * paren somewhere before the triggering line. Maybe the user tried to specify a color
- * as in Java Processing, like fill(#FFAA55), which Python sees as an open
- * paren followed by a comment.
- * This function takes a stab at finding such a thing, and reporting it. Otherwise,
- * it throws a slightly less cryptic error message.
+ * The message "no viable alternative" is a strong indication that there's an unclosed paren
+ * somewhere before the triggering line. Maybe the user tried to specify a color as in Java
+ * Processing, like fill(#FFAA55), which Python sees as an open paren followed by a
+ * comment.
+ *
+ *
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) {
+ private static PythonSketchError noViableAlternative(
+ final File file, final int lineNo, final int column, final String message) {
if (message.equals("no viable alternative at input '&'")) {
- return new PythonSketchError(C_LIKE_LOGICAL_AND_ERROR_MESSAGE, file.getName(), lineNo, column);
+ return new PythonSketchError(
+ C_LIKE_LOGICAL_AND_ERROR_MESSAGE, file.getName(), lineNo, column);
}
if (message.equals("no viable alternative at input '|'")) {
return new PythonSketchError(C_LIKE_LOGICAL_OR_ERROR_MESSAGE, file.getName(), lineNo, column);
@@ -281,17 +342,26 @@ private static PythonSketchError noViableAlternative(final File file, final int
final PythonSketchError defaultException =
new PythonSketchError(
"Maybe there's an unclosed paren or quote mark somewhere before this line?",
- file.getName(), lineNo, column);
+ file.getName(),
+ lineNo,
+ column);
try {
int lineIndex = 0;
for (final String line : Files.readLines(file, Charsets.UTF_8)) {
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));
+ 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));
}
lineIndex++;
}
@@ -320,26 +390,60 @@ public void frameMoved(final int x, final int y) {
}
}
- public PAppletJythonDriver(final InteractiveConsole interp, final String pySketchPath,
- final String programText, final Printer stdout) throws PythonSketchError {
- this.wrappedStdout = new WrappedPrintStream(System.out) {
- @Override
- public void doPrint(final String s) {
- stdout.print(s);
- }
- };
+ public PAppletJythonDriver(
+ final InteractiveConsole interp,
+ final String pySketchPath,
+ final String programText,
+ final Printer stdout)
+ throws PythonSketchError {
+ this.wrappedStdout =
+ new WrappedPrintStream(System.out) {
+ @Override
+ public void doPrint(final String s) {
+ stdout.print(s);
+ }
+ };
this.programText = programText;
this.pySketchPath = Paths.get(pySketchPath);
this.interp = interp;
- this.builtins = (PyStringMap)interp.getSystemState().getBuiltins();
+ this.builtins = (PyStringMap) interp.getSystemState().getBuiltins();
- interp.set("__file__", new File(pySketchPath).getName());
+ interp.set("__file__", Py.newStringUTF8(new File(pySketchPath).getName()));
processSketch(DETECT_MODE_SCRIPT);
this.mode = Mode.valueOf(interp.get("__mode__").asString());
Runner.log("Mode: ", mode.name());
if (mode == Mode.MIXED) {
throw interp.get("__error__", MixedModeError.class);
}
+ if (mode == Mode.STATIC) {
+ processSketch(GET_SETTINGS_SCRIPT);
+ detectedWidth = interp.get("__width__").asInt();
+ detectedHeight = interp.get("__height__").asInt();
+ detectedPixelDensity = interp.get("__pixelDensity__").asInt();
+ detectedSmooth = interp.get("__smooth__").asInt() != 0;
+ detectedNoSmooth = interp.get("__noSmooth__").asInt() != 0;
+ 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;
+ }
+ processedStaticSketch = interp.get("__cleaned_sketch__");
+ }
initializeStatics(builtins);
setFilter();
@@ -347,35 +451,122 @@ public void doPrint(final String s) {
setSet();
setColorMethods();
setText();
- builtins.__setitem__("g", Py.java2py(g));
// Make sure key and keyCode are defined.
- builtins.__setitem__("key", Py.newUnicode((char)0));
+ builtins.__setitem__("key", Py.newUnicode((char) 0));
builtins.__setitem__("keyCode", pyint(0));
}
+ // method to find the frame field, rather than relying on an Exception
+ private Field getFrameField() {
+ for (Field field : getClass().getFields()) {
+ if (field.getName().equals("frame")) {
+ return field;
+ }
+ }
+ return null;
+ }
+
@Override
protected PSurface initSurface() {
final PSurface s = super.initSurface();
+
+ frameField = getFrameField();
+ if (frameField != null) {
+ try {
+ // eliminate a memory leak from 2.x compat hack
+ frameField.set(this, null);
+ } catch (Exception e) {
+ // safe enough to ignore; this was a workaround
+ }
+ }
+
+ s.setTitle(pySketchPath.getFileName().toString().replaceAll("\\..*$", ""));
if (s instanceof PSurfaceAWT) {
- final PSurfaceAWT surf = (PSurfaceAWT)s;
- final Component c = (Component)surf.getNative();
- c.addComponentListener(new ComponentAdapter() {
- @Override
- public void componentHidden(final ComponentEvent e) {
- finishedLatch.countDown();
- }
- });
+ final PSurfaceAWT surf = (PSurfaceAWT) s;
+ final Component c = (Component) surf.getNative();
+ c.addComponentListener(
+ new ComponentAdapter() {
+ @Override
+ public void componentHidden(final ComponentEvent e) {
+ finishedLatch.countDown();
+ }
+ });
} else if (s instanceof PSurfaceJOGL) {
- final PSurfaceJOGL surf = (PSurfaceJOGL)s;
- final GLWindow win = (GLWindow)surf.getNative();
- win.addWindowListener(new com.jogamp.newt.event.WindowAdapter() {
- @Override
- public void windowDestroyed(final com.jogamp.newt.event.WindowEvent arg0) {
- finishedLatch.countDown();
- }
- });
+ final PSurfaceJOGL surf = (PSurfaceJOGL) s;
+ final GLWindow win = (GLWindow) surf.getNative();
+ win.addWindowListener(
+ new com.jogamp.newt.event.WindowAdapter() {
+ @Override
+ public void windowDestroyed(final com.jogamp.newt.event.WindowEvent arg0) {
+ finishedLatch.countDown();
+ }
+ });
+ } else if (s instanceof PSurfaceFX) {
+ System.err.println("I don't know how to watch FX2D windows for close.");
}
+
+ final PyObject pyG;
+ if (g instanceof PGraphicsOpenGL) {
+ /*
+ * 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,
+ * not separate namespaces for functions and fields. So here we create new attributes
+ * on the "g" object that are aliases to the matrix fields. It's only necessary to
+ * do this for "camera" itself, but, for the sake of consistency, we do it for
+ * all of them.
+ *
+ * So, in Python Mode:
+ *
+ * def setup():
+ * size(300, 300, P3D)
+ *
+ * def mousePressed():
+ * # do something silly to the PGraphics camera
+ * g.camera(1, 2, 3, 4, 5, 6, 7, 8, 9)
+ *
+ * # 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);
+ final PyObject modelviewInvMatrix = Py.java2py(glGraphics.modelviewInv);
+ final PyObject projmodelviewMatrix = Py.java2py(glGraphics.projmodelview);
+ pyG =
+ new PyObjectDerived(PyType.fromClass(g.getClass(), false)) {
+ @Override
+ public PyObject __findattr_ex__(final String name) {
+ if (name == "cameraMatrix") { // name is interned
+ return cameraMatrix;
+ }
+ if (name == "cameraInvMatrix") {
+ return cameraInvMatrix;
+ }
+ if (name == "modelviewMatrix") {
+ return modelviewMatrix;
+ }
+ if (name == "modelviewInvMatrix") {
+ return modelviewInvMatrix;
+ }
+ if (name == "projmodelviewMatrix") {
+ return projmodelviewMatrix;
+ }
+ return super.__findattr_ex__(name);
+ }
+ };
+ JyAttribute.setAttr(pyG, JyAttribute.JAVA_PROXY_ATTR, g);
+ } else {
+ pyG = Py.java2py(g);
+ }
+ // We want the builtin "g" object to behave just like PGraphics instances
+ // created with createGraphics(), so that its beginPGL can be used in a with
+ // statement, etc.
+ interp.set("__original_g__", pyG);
+ final PyObject wrappedG = interp.eval("PGraphicsPythonModeWrapper(__original_g__)");
+ builtins.__setitem__("g", wrappedG);
+
return s;
}
@@ -384,6 +575,36 @@ public void exitActual() {
finishedLatch.countDown();
}
+ /** Call the user-defined Python function with the given name. */
+ @Override
+ public void method(final String name) {
+ final PyFunction f = allFunctionsByName.get(name);
+ if (f != null) {
+ f.__call__();
+ } else {
+ super.method(name);
+ }
+ }
+
+ /**
+ * Python Mode's {@link PApplet#thread(String)} can take a {@link String} (like Java mode) or a
+ * function object.
+ *
+ * @param obj either a {@link PyString} or a {@link PyFunction} to run in a thread.
+ */
+ 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();
+ }
+ }
+
public void findSketchMethods() throws PythonSketchError {
if (mode == Mode.ACTIVE) {
// Executing the sketch will bind method names ("draw") to PyCode
@@ -392,89 +613,185 @@ public void findSketchMethods() throws PythonSketchError {
processSketch(PREPROCESS_SCRIPT);
}
+ for (final Object local : ((PyStringMap) interp.getLocals()).items()) {
+ final PyTuple t = (PyTuple) local;
+ final String k = t.get(0).toString();
+ final Object v = t.get(1);
+ if (v instanceof PyFunction) {
+ allFunctionsByName.put(k, (PyFunction) v);
+ }
+ }
+
// Find and cache any PApplet callbacks defined in the Python sketch
drawMeth = interp.get("draw");
setupMeth = interp.get("setup");
- mousePressedFunc = 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);
- }
- };
- mouseMovedFunc = 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);
- }
- };
- mouseDraggedFunc = new EventFunction("mouseDragged") {
- @Override
- protected void callSuper(final MouseEvent event) {
- PAppletJythonDriver.super.mouseDragged(event);
- }
- };
-
- keyPressedFunc = 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);
- }
- };
- keyTypedFunc = new EventFunction("keyTyped") {
- @Override
- protected void callSuper(final KeyEvent event) {
- PAppletJythonDriver.super.keyTyped(event);
- }
- };
+ mousePressedFunc =
+ 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);
+ }
+ };
+ mouseMovedFunc =
+ 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);
+ }
+ };
+ mouseDraggedFunc =
+ 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);
+ }
+ };
+ keyReleasedFunc =
+ 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);
+ }
+ };
- sketchFullScreenMeth = interp.get("sketchFullScreen");
- sketchWidthMeth = interp.get("sketchWidth");
- sketchHeightMeth = interp.get("sketchHeight");
- sketchRendererMeth = interp.get("sketchRenderer");
settingsMeth = interp.get("settings");
- initMeth = interp.get("init");
stopMeth = interp.get("stop");
+ disposeMeth = interp.get("dispose");
pauseMeth = interp.get("pause");
resumeMeth = interp.get("resume");
mouseWheelMeth = interp.get("mouseWheel");
if (mousePressedFunc.func != null) {
// The user defined a mousePressed() method, which will hide the magical
// Processing variable boolean mousePressed. We have to do some magic.
- interp.getLocals().__setitem__("mousePressed", new PyBoolean(false) {
- @Override
- public boolean getBooleanValue() {
- return mousePressed;
- }
-
- @Override
- public PyObject __call__(final PyObject[] args, final String[] kws) {
- return mousePressedFunc.func.__call__(args, kws);
- }
- });
+ interp
+ .getLocals()
+ .__setitem__(
+ "mousePressed",
+ new PyBoolean(false) {
+ @Override
+ public boolean getBooleanValue() {
+ return mousePressed;
+ }
+
+ @Override
+ public PyObject __call__(final PyObject[] args, final String[] kws) {
+ return mousePressedFunc.func.__call__(args, kws);
+ }
+ });
}
// Video library callbacks.
captureEventMeth = interp.get("captureEvent");
movieEventMeth = interp.get("movieEvent");
+
+ // Serial library callback.
+ serialEventMeth = interp.get("serialEvent");
+
+ // Net library callbacks.
+ clientEventMeth = interp.get("clientEvent");
+ disconnectEventMeth = interp.get("disconnectEvent");
+ serverEventMeth = interp.get("serverEvent");
+
+ // oscP5 library callback.
+ oscEventMeth = interp.get("oscEvent");
+
+ // themidibus callbacks
+ 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;
+ }
+ }
+ 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;
+ }
+ }
+ 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;
+ }
+ }
+ if ((meth = interp.get("rawMidi")) != null) {
+ switch (argCount(meth)) {
+ case 1:
+ rawMidi1Meth = meth;
+ break;
+ case 3:
+ rawMidi3Meth = meth;
+ }
+ }
+ if ((meth = interp.get("midiMessage")) != null) {
+ switch (argCount(meth)) {
+ case 1:
+ midiMessage1Meth = meth;
+ break;
+ case 3:
+ midiMessage3Meth = meth;
+ }
+ }
+
+ // processing_websockets callbacks.
+ webSocketEventMeth = interp.get("webSocketEvent");
+ webSocketServerEventMeth = interp.get("webSocketServerEvent");
}
/*
@@ -483,6 +800,7 @@ public PyObject __call__(final PyObject[] args, final String[] kws) {
* them to avoid garbage collection.
*/
private static final PyInteger[] CHEAP_INTS = new PyInteger[20000];
+
static {
for (int i = 0; i < CHEAP_INTS.length; i++) {
CHEAP_INTS[i] = Py.newInteger(i);
@@ -515,18 +833,21 @@ protected void wrapProcessingVariables() {
builtins.__setitem__("focused", Py.newBoolean(focused));
builtins.__setitem__("keyPressed", Py.newBoolean(keyPressed));
builtins.__setitem__("frameCount", pyint(frameCount));
- builtins.__setitem__("frameRate", new PyFloat(frameRate) {
- @Override
- public PyObject __call__(final PyObject[] args, final String[] kws) {
- switch (args.length) {
- default:
- return raiseTypeError("Can't call \"frameRate\" with " + args.length + " parameters.");
- case 1:
- frameRate((float)args[0].asDouble());
- return Py.None;
- }
- }
- });
+ builtins.__setitem__(
+ "frameRate",
+ new PyFloat(frameRate) {
+ @Override
+ public PyObject __call__(final PyObject[] args, final String[] kws) {
+ switch (args.length) {
+ default:
+ return raiseTypeError(
+ "Can't call \"frameRate\" with " + args.length + " parameters.");
+ case 1:
+ frameRate((float) args[0].asDouble());
+ return Py.None;
+ }
+ }
+ });
}
// We only change the "key" variable as necessary to avoid generating
@@ -562,23 +883,32 @@ 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(
+ 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();
+ }
+ });
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();
+
try {
finishedLatch.await();
} catch (final InterruptedException interrupted) {
@@ -590,14 +920,47 @@ public void runAndBlock(final String[] args) throws PythonSketchError {
// fallthrough
}
} finally {
+ try {
+ if (stopMeth != null) {
+ stopMeth.__call__();
+ }
+ if (disposeMeth != null) {
+ disposeMeth.__call__();
+ }
+ } catch (Throwable t) {
+ System.err.println("while disposing: " + t);
+ }
+
+ maybeShutdownSoundEngine();
+
+ Thread.setDefaultUncaughtExceptionHandler(null);
if (PApplet.platform == PConstants.MACOSX && 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.)
Runner.log("Disabling fullscreen.");
- macosxFullScreenToggle(frame);
+
+ if (frameField != null) {
+ try {
+ Frame frameObj = (Frame) frameField.get(this);
+ macosxFullScreenToggle(frameObj);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ if (surface instanceof PSurfaceFX) {
+ // 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!");
+ System.exit(0);
+ }
+ final Object nativeWindow = surface.getNative();
+ if (nativeWindow instanceof com.jogamp.newt.Window) {
+ ((com.jogamp.newt.Window) nativeWindow).destroy();
+ } else {
+ surface.setVisible(false);
}
- surface.setVisible(false);
}
if (terminalException != null) {
throw terminalException;
@@ -605,10 +968,30 @@ public void runAndBlock(final String[] args) throws PythonSketchError {
}
/**
- * Use reflection to call
- * com.apple.eawt.Application.getApplication().requestToggleFullScreen(window);
+ * The sound library is designed to depend on exiting the VM when the sketch
+ * exits, or rather, it's not designed to work if startup up with one
+ * PApplet, but then attempted to be used with another. This hack nukes
+ * the Engine singleton so that it has to be re-instantiated on the next
+ * run.
+ */
+ private void maybeShutdownSoundEngine() {
+ try {
+ final Class> appClass = Class.forName("processing.sound.Engine");
+ final Field singleton = appClass.getDeclaredField("singleton");
+ singleton.setAccessible(true);
+ singleton.set(null, null);
+ } catch (final ClassNotFoundException cnfe) {
+ // ignored
+ } catch (final Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Use reflection to call
+ * com.apple.eawt.Application.getApplication().requestToggleFullScreen(window);
*/
- static private void macosxFullScreenToggle(final Window window) {
+ private static void macosxFullScreenToggle(final Window window) {
try {
final Class> appClass = Class.forName("com.apple.eawt.Application");
final Method getAppMethod = appClass.getMethod("getApplication");
@@ -623,104 +1006,117 @@ static private void macosxFullScreenToggle(final Window window) {
}
/**
- * 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 args, it constructs a Python
- * set.
+ * 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
+ * args, it constructs a Python set.
*/
private void setSet() {
- final PyType originalSet = (PyType)builtins.__getitem__("set");
- builtins.__setitem__("set", new PyType(PyType.TYPE) {
- {
- builtin = true;
- init(PySet.class, new HashSet());
- invalidateMethodCache();
- }
+ final PyType originalSet = (PyType) builtins.__getitem__("set");
+ builtins.__setitem__(
+ "set",
+ new PyType(PyType.TYPE) {
+ {
+ builtin = true;
+ init(PySet.class, new HashSet());
+ invalidateMethodCache();
+ }
- @Override
- public PyObject __call__(final PyObject[] args, final String[] kws) {
- switch (args.length) {
- default:
- return originalSet.__call__(args, kws);
- case 3:
- final int x = args[0].asInt();
- final int y = args[1].asInt();
- final PyObject c = args[2];
- final PyType tc = c.getType();
- if (tc.getProxyType() != null && PImage.class.isAssignableFrom(tc.getProxyType())) {
- set(x, y, (processing.core.PImage)c.__tojava__(processing.core.PImage.class));
- return Py.None;
- } else {
- set(x, y, interpretColorArg(c));
- return Py.None;
+ @Override
+ public PyObject __call__(final PyObject[] args, final String[] kws) {
+ switch (args.length) {
+ default:
+ return originalSet.__call__(args, kws);
+ case 3:
+ final int x = args[0].asInt();
+ final int y = args[1].asInt();
+ final PyObject c = args[2];
+ final PyType tc = c.getType();
+ if (tc.getProxyType() != null && PImage.class.isAssignableFrom(tc.getProxyType())) {
+ set(x, y, (processing.core.PImage) c.__tojava__(processing.core.PImage.class));
+ return Py.None;
+ } else {
+ set(x, y, interpretColorArg(c));
+ return Py.None;
+ }
}
- }
- }
- });
+ }
+ });
}
/**
- * Permit both the Processing map() (which is a linear interpolation function) and
- * the Python map() (which is a list transformation).
+ * Permit both the Processing map() (which is a linear interpolation function) and the Python
+ * map() (which is a list transformation).
*/
private void setMap() {
final PyObject builtinMap = builtins.__getitem__("map");
- builtins.__setitem__("map", new PyObject() {
-
- @Override
- public PyObject __call__(final PyObject[] args, final String[] kws) {
- switch (args.length) {
- default:
- return builtinMap.__call__(args, kws);
- case 5:
- final PyObject value = args[0];
- final PyObject start1 = args[1];
- final PyObject stop1 = args[2];
- final PyObject start2 = args[3];
- final PyObject stop2 = args[4];
- if (value.isNumberType() && start1.isNumberType() && stop1.isNumberType()
- && start2.isNumberType() && stop2.isNumberType()) {
- return Py.newFloat(map((float)value.asDouble(), (float)start1.asDouble(),
- (float)stop1.asDouble(), (float)start2.asDouble(), (float)stop2.asDouble()));
- } else {
- return builtinMap.__call__(args, kws);
+ builtins.__setitem__(
+ "map",
+ new PyObject() {
+
+ @Override
+ public PyObject __call__(final PyObject[] args, final String[] kws) {
+ switch (args.length) {
+ default:
+ return builtinMap.__call__(args, kws);
+ case 5:
+ final PyObject value = args[0];
+ final PyObject start1 = args[1];
+ final PyObject stop1 = args[2];
+ final PyObject start2 = args[3];
+ final PyObject stop2 = args[4];
+ if (value.isNumberType()
+ && start1.isNumberType()
+ && stop1.isNumberType()
+ && start2.isNumberType()
+ && stop2.isNumberType()) {
+ return Py.newFloat(
+ map(
+ (float) value.asDouble(),
+ (float) start1.asDouble(),
+ (float) stop1.asDouble(),
+ (float) start2.asDouble(),
+ (float) stop2.asDouble()));
+ } else {
+ return builtinMap.__call__(args, kws);
+ }
}
- }
- }
- });
+ }
+ });
}
/**
- * Permit both the Processing filter() (which does image processing) and the
- * Python filter() (which does list comprehensions).
+ * Permit both the Processing filter() (which does image processing) and the Python filter()
+ * (which does list comprehensions).
*/
private void setFilter() {
final PyObject builtinFilter = builtins.__getitem__("filter");
- builtins.__setitem__("filter", new PyObject() {
- @Override
- public PyObject __call__(final PyObject[] args, final String[] kws) {
- switch (args.length) {
- case 1:
- final PyObject value = args[0];
- if (value.isNumberType()) {
- filter(value.asInt());
- } else {
- filter(Py.tojava(value, PShader.class));
+ builtins.__setitem__(
+ "filter",
+ new PyObject() {
+ @Override
+ public PyObject __call__(final PyObject[] args, final String[] kws) {
+ switch (args.length) {
+ case 1:
+ final PyObject value = args[0];
+ if (value.isNumberType()) {
+ filter(value.asInt());
+ } else {
+ filter(Py.tojava(value, PShader.class));
+ }
+ return Py.None;
+ case 2:
+ final PyObject a = args[0];
+ final PyObject b = args[1];
+ if (a.isNumberType()) {
+ filter(a.asInt(), (float) b.asDouble());
+ return Py.None;
+ }
+ //$FALL-THROUGH$
+ default:
+ return builtinFilter.__call__(args, kws);
}
- return Py.None;
- case 2:
- final PyObject a = args[0];
- final PyObject b = args[1];
- if (a.isNumberType()) {
- filter(a.asInt(), (float)b.asDouble());
- return Py.None;
- }
- //$FALL-THROUGH$
- default:
- return builtinFilter.__call__(args, kws);
- }
- }
- });
+ }
+ });
}
// If you call lerpColor in an active-mode sketch before setup() has run,
@@ -740,15 +1136,15 @@ public int lerpColor(final int c1, final int c2, final float amt) {
* version to catch it.
*/
public void fill(final long argb) {
- fill((int)(argb & 0xFFFFFFFF));
+ fill((int) (argb & 0xFFFFFFFF));
}
public void stroke(final long argb) {
- stroke((int)(argb & 0xFFFFFFFF));
+ stroke((int) (argb & 0xFFFFFFFF));
}
public void background(final long argb) {
- background((int)(argb & 0xFFFFFFFF));
+ background((int) (argb & 0xFFFFFFFF));
}
/*
@@ -780,8 +1176,8 @@ private boolean isString(final PyObject o) {
}
/**
- * The positional arguments to lerpColor may be long integers or CSS-style
- * string specs.
+ * The positional arguments to lerpColor may be long integers or CSS-style string specs.
+ *
* @param arg A color argument.
* @return the integer correspnding to the intended color.
*/
@@ -790,115 +1186,130 @@ private int interpretColorArg(final PyObject arg) {
}
/**
- * Permit both the instance method lerpColor and the static method lerpColor.
- * Also permit 0xAARRGGBB, '#RRGGBB', and 0-255.
+ * Permit both the instance method lerpColor and the static method lerpColor. Also permit
+ * 0xAARRGGBB, '#RRGGBB', and 0-255.
*/
private void setColorMethods() {
- builtins.__setitem__("lerpColor", new PyObject() {
- @Override
- public PyObject __call__(final PyObject[] args, final String[] kws) {
- final int c1 = interpretColorArg(args[0]);
- final int c2 = interpretColorArg(args[1]);
- final float amt = (float)args[2].asDouble();
- switch (args.length) {
- case 3:
- return pyint(lerpColor(c1, c2, amt));
- case 4:
- final int colorMode = (int)(args[3].asLong() & 0xFFFFFFFF);
- return pyint(lerpColor(c1, c2, amt, colorMode));
- //$FALL-THROUGH$
- default:
- return raiseTypeError("lerpColor takes either 3 or 4 arguments, but I got "
- + args.length + ".");
- }
- }
- });
- builtins.__setitem__("alpha", new PyObject() {
- @Override
- public PyObject __call__(final PyObject[] args, final String[] kws) {
- return Py.newFloat(alpha(interpretColorArg(args[0])));
- }
- });
- builtins.__setitem__("red", new PyObject() {
- @Override
- public PyObject __call__(final PyObject[] args, final String[] kws) {
- return Py.newFloat(red(interpretColorArg(args[0])));
- }
- });
- builtins.__setitem__("green", new PyObject() {
- @Override
- public PyObject __call__(final PyObject[] args, final String[] kws) {
- return Py.newFloat(green(interpretColorArg(args[0])));
- }
- });
- builtins.__setitem__("blue", new PyObject() {
- @Override
- public PyObject __call__(final PyObject[] args, final String[] kws) {
- return Py.newFloat(blue(interpretColorArg(args[0])));
- }
- });
- builtins.__setitem__("hue", new PyObject() {
- @Override
- public PyObject __call__(final PyObject[] args, final String[] kws) {
- return Py.newFloat(hue(interpretColorArg(args[0])));
- }
- });
- builtins.__setitem__("saturation", new PyObject() {
- @Override
- public PyObject __call__(final PyObject[] args, final String[] kws) {
- return Py.newFloat(saturation(interpretColorArg(args[0])));
- }
- });
- builtins.__setitem__("brightness", new PyObject() {
- @Override
- public PyObject __call__(final PyObject[] args, final String[] kws) {
- return Py.newFloat(brightness(interpretColorArg(args[0])));
- }
- });
+ builtins.__setitem__(
+ "lerpColor",
+ new PyObject() {
+ @Override
+ public PyObject __call__(final PyObject[] args, final String[] kws) {
+ final int c1 = interpretColorArg(args[0]);
+ final int c2 = interpretColorArg(args[1]);
+ final float amt = (float) args[2].asDouble();
+ switch (args.length) {
+ case 3:
+ return pyint(lerpColor(c1, c2, amt));
+ case 4:
+ final int colorMode = (int) (args[3].asLong() & 0xFFFFFFFF);
+ return pyint(lerpColor(c1, c2, amt, colorMode));
+ default:
+ return raiseTypeError(
+ "lerpColor takes either 3 or 4 arguments, but I got " + args.length + ".");
+ }
+ }
+ });
+ builtins.__setitem__(
+ "alpha",
+ new PyObject() {
+ @Override
+ public PyObject __call__(final PyObject[] args, final String[] kws) {
+ return Py.newFloat(alpha(interpretColorArg(args[0])));
+ }
+ });
+ builtins.__setitem__(
+ "red",
+ new PyObject() {
+ @Override
+ public PyObject __call__(final PyObject[] args, final String[] kws) {
+ return Py.newFloat(red(interpretColorArg(args[0])));
+ }
+ });
+ builtins.__setitem__(
+ "green",
+ new PyObject() {
+ @Override
+ public PyObject __call__(final PyObject[] args, final String[] kws) {
+ return Py.newFloat(green(interpretColorArg(args[0])));
+ }
+ });
+ builtins.__setitem__(
+ "blue",
+ new PyObject() {
+ @Override
+ public PyObject __call__(final PyObject[] args, final String[] kws) {
+ return Py.newFloat(blue(interpretColorArg(args[0])));
+ }
+ });
+ builtins.__setitem__(
+ "hue",
+ new PyObject() {
+ @Override
+ public PyObject __call__(final PyObject[] args, final String[] kws) {
+ return Py.newFloat(hue(interpretColorArg(args[0])));
+ }
+ });
+ builtins.__setitem__(
+ "saturation",
+ new PyObject() {
+ @Override
+ public PyObject __call__(final PyObject[] args, final String[] kws) {
+ return Py.newFloat(saturation(interpretColorArg(args[0])));
+ }
+ });
+ builtins.__setitem__(
+ "brightness",
+ new PyObject() {
+ @Override
+ public PyObject __call__(final PyObject[] args, final String[] kws) {
+ return Py.newFloat(brightness(interpretColorArg(args[0])));
+ }
+ });
}
/**
- * Hide the variants of text() that take char/char[] and array indices, since that's
- * not necessary in Python, and since they mask the text()s that take take strings.
+ * Hide the variants of text() that take char/char[] and array indices, since that's not necessary
+ * in Python, and since they mask the text()s that take take strings.
*/
private void setText() {
- builtins.__setitem__("text", new PyObject() {
- @Override
- public PyObject __call__(final PyObject[] args, final String[] kws) {
- if (args.length < 3 || args.length > 5) {
- raiseTypeError("text() takes 3-5 arguments, but I got " + args.length + ".");
- }
- final PyObject a = args[0];
- final float x1 = (float)args[1].asDouble();
- final float y1 = (float)args[2].asDouble();
- if (args.length == 3) {
- if (isString(a)) {
- text(a.asString(), x1, y1);
- } else if (a.getType() == PyInteger.TYPE) {
- text(a.asInt(), x1, y1);
- } else {
- text((float)a.asDouble(), x1, y1);
- }
- } else if (args.length == 4) {
- final float z1 = (float)args[3].asDouble();
- if (isString(a)) {
- text(a.asString(), x1, y1, z1);
- } else if (a.getType() == PyInteger.TYPE) {
- text(a.asInt(), x1, y1, z1);
- } else {
- text((float)a.asDouble(), x1, y1, z1);
+ builtins.__setitem__(
+ "text",
+ new PyObject() {
+ @Override
+ public PyObject __call__(final PyObject[] args, final String[] kws) {
+ if (args.length < 3 || args.length > 5) {
+ raiseTypeError("text() takes 3-5 arguments, but I got " + args.length + ".");
+ }
+ final PyObject a = args[0];
+ final float x1 = (float) args[1].asDouble();
+ final float y1 = (float) args[2].asDouble();
+ if (args.length == 3) {
+ if (isString(a)) {
+ text(a.asString(), x1, y1);
+ } else if (a.getType() == PyInteger.TYPE) {
+ text(a.asInt(), x1, y1);
+ } else {
+ text((float) a.asDouble(), x1, y1);
+ }
+ } else if (args.length == 4) {
+ final float z1 = (float) args[3].asDouble();
+ if (isString(a)) {
+ text(a.asString(), x1, y1, z1);
+ } else if (a.getType() == PyInteger.TYPE) {
+ text(a.asInt(), x1, y1, z1);
+ } else {
+ text((float) a.asDouble(), x1, y1, z1);
+ }
+ } else /* 5 */ {
+ text(a.asString(), x1, y1, (float) args[3].asDouble(), (float) args[4].asDouble());
+ }
+ return Py.None;
}
- } else /* 5 */{
- text(a.asString(), x1, y1, (float)args[3].asDouble(), (float)args[4].asDouble());
- }
- return Py.None;
- }
- });
+ });
}
- /**
- * Populate the Python builtins namespace with PConstants.
- */
+ /** Populate the Python builtins namespace with PConstants. */
public static void initializeStatics(final PyStringMap builtins) {
for (final Field f : PConstants.class.getDeclaredFields()) {
final int mods = f.getModifiers();
@@ -913,15 +1324,23 @@ public static void initializeStatics(final PyStringMap builtins) {
}
/**
- * We have to override PApplet's size method in order to reset the Python
- * context's knowledge of the magic variables that reflect the state of the
- * sketch's world, particularly width and height.
+ * We have to override PApplet's size method in order to reset the Python context's knowledge of
+ * the magic variables that reflect the state of the sketch's world, particularly width and
+ * height.
*/
@Override
- public void size(final int iwidth, final int iheight, final String irenderer, final String ipath) {
+ public void size(
+ final int iwidth, final int iheight, final String irenderer, final String ipath) {
super.size(iwidth, iheight, irenderer, ipath);
- builtins.__setitem__("g", Py.java2py(g));
- builtins.__setitem__("frame", Py.java2py(frame));
+ if (frameField != null) {
+ // should surface be added instead? [fry 210616]
+ try {
+ Frame frameObj = (Frame) frameField.get(this);
+ builtins.__setitem__("frame", Py.java2py(frameObj));
+ } catch (IllegalAccessException ignored) {
+ // log this?
+ }
+ }
builtins.__setitem__("width", pyint(width));
builtins.__setitem__("height", pyint(height));
}
@@ -931,33 +1350,60 @@ public void settings() {
try {
if (settingsMeth != null) {
settingsMeth.__call__();
- } else {
- super.settings();
+ } else if (mode == Mode.STATIC) {
+ if (detectedFullScreen) {
+ fullScreen();
+ } else {
+ if (detectedRenderer != null) {
+ size(detectedWidth, detectedHeight, detectedRenderer);
+ } else {
+ size(detectedWidth, detectedHeight);
+ }
+ }
+ pixelDensity(detectedPixelDensity);
+ if (detectedSmooth && detectedNoSmooth) {
+ throw new MixedSmoothError();
+ }
+ if (detectedSmooth) {
+ smooth();
+ } else if (detectedNoSmooth) {
+ noSmooth();
+ }
}
} catch (final Exception e) {
terminalException = toSketchException(e);
- exit();
+ exitActual();
}
}
@Override
public void setup() {
- builtins.__setitem__("frame", Py.java2py(frame));
+ if (frameField != null) {
+ // should surface be added instead? [fry 210616]
+ try {
+ Frame frameObj = (Frame) frameField.get(this);
+ builtins.__setitem__("frame", Py.java2py(frameObj));
+ } catch (IllegalAccessException ignored) {
+ // log this?
+ }
+ }
wrapProcessingVariables();
- try {
- if (mode == Mode.STATIC) {
- // A static sketch gets called once, from this spot.
- Runner.log("Interpreting static-mode sketch.");
- processSketch(PREPROCESS_SCRIPT);
- } else if (setupMeth != null) {
- // Call the Python sketch's setup()
+ if (mode == Mode.STATIC) {
+ // A static sketch gets called once, from this spot.
+ Runner.log("Interpreting static-mode sketch.");
+ try {
+ try (PushedOut out = wrappedStdout.pushStdout()) {
+ interp.exec(processedStaticSketch);
+ }
+ } catch (final Exception e) {
+ terminalException = toSketchException(e);
+ exitActual();
+ }
+ } else if (setupMeth != null) {
+ // Call the Python sketch's setup()
+ try (PushedOut out = wrappedStdout.pushStdout()) {
setupMeth.__call__();
}
- } catch (final Exception e) {
- // No longer necessary in 3.0a7
- // checkForRendererChangeException(e);
- terminalException = toSketchException(e);
- exit();
}
}
@@ -967,7 +1413,12 @@ public void draw() {
if (drawMeth == null) {
super.draw();
} else if (!finished) {
- drawMeth.__call__();
+ try (PushedOut out = wrappedStdout.pushStdout()) {
+ drawMeth.__call__();
+ }
+ }
+ if (exitCalled()) {
+ exitActual();
}
}
@@ -1025,7 +1476,6 @@ public void mouseReleased(final MouseEvent e) {
mouseReleasedFunc.invoke(e);
}
-
@Override
public void mouseDragged() {
wrapMouseVariables();
@@ -1070,7 +1520,6 @@ public void keyReleased(final KeyEvent e) {
keyReleasedFunc.invoke(e);
}
-
@Override
public void keyTyped() {
wrapKeyVariables();
@@ -1083,17 +1532,6 @@ public void keyTyped(final KeyEvent e) {
keyTypedFunc.invoke(e);
}
- @Override
- public void stop() {
- try {
- if (stopMeth != null) {
- stopMeth.__call__();
- }
- } finally {
- super.stop();
- }
- }
-
@Override
public void pause() {
try {
@@ -1117,8 +1555,8 @@ public void resume() {
}
/**
- * Processing uses reflection to call file selection callbacks by name.
- * We fake out that stuff with one of these babies.
+ * Processing uses reflection to call file selection callbacks by name. We fake out that stuff
+ * with one of these babies.
*/
public class FileSelectCallbackProxy {
private final PyObject callback;
@@ -1190,6 +1628,133 @@ public void movieEvent(final Object movie) {
}
}
+ // Serial library callback.
+ public void serialEvent(final Object whichPort) {
+ if (serialEventMeth != null) {
+ serialEventMeth.__call__(Py.java2py(whichPort));
+ }
+ }
+
+ // Net library callbacks.
+ public void clientEvent(final Object someClient) {
+ if (clientEventMeth != null) {
+ clientEventMeth.__call__(Py.java2py(someClient));
+ }
+ }
+
+ public void disconnectEvent(final Object someClient) {
+ if (disconnectEventMeth != null) {
+ disconnectEventMeth.__call__(Py.java2py(someClient));
+ }
+ }
+
+ public void serverEvent(final Object someServer, final Object someClient) {
+ if (serverEventMeth != null) {
+ serverEventMeth.__call__(Py.java2py(someServer), Py.java2py(someClient));
+ }
+ }
+
+ // oscP5 callback.
+ public void oscEvent(final Object oscMessage) {
+ if (oscEventMeth != null) {
+ oscEventMeth.__call__(Py.java2py(oscMessage));
+ }
+ }
+
+ // themidibus callbacks
+ public void noteOn(final int channel, final int pitch, final int velocity) {
+ if (noteOn3Meth != null) {
+ noteOn3Meth.__call__(pyint(channel), pyint(pitch), pyint(velocity));
+ }
+ }
+
+ public void noteOn(
+ final int channel,
+ final int pitch,
+ final int velocity,
+ final long time,
+ final String busName) {
+ if (noteOn5Meth != null) {
+ noteOn5Meth.__call__(
+ new PyObject[] {
+ pyint(channel), pyint(pitch), pyint(velocity), Py.newLong(time), Py.newString(busName)
+ });
+ }
+ }
+
+ public void noteOff(final int channel, final int pitch, final int velocity) {
+ if (noteOff3Meth != null) {
+ noteOff3Meth.__call__(pyint(channel), pyint(pitch), pyint(velocity));
+ }
+ }
+
+ public void noteOff(
+ final int channel,
+ final int pitch,
+ final int velocity,
+ final long time,
+ final String busName) {
+ if (noteOff5Meth != null) {
+ noteOff5Meth.__call__(
+ new PyObject[] {
+ pyint(channel), pyint(pitch), pyint(velocity), Py.newLong(time), Py.newString(busName)
+ });
+ }
+ }
+
+ public void controllerChange(final int channel, final int number, final int value) {
+ if (controllerChange3Meth != null) {
+ controllerChange3Meth.__call__(pyint(channel), pyint(number), pyint(value));
+ }
+ }
+
+ public void controllerChange(
+ final int channel, final int number, final int value, final long time, final String busName) {
+ if (controllerChange5Meth != null) {
+ controllerChange5Meth.__call__(
+ new PyObject[] {
+ pyint(channel), pyint(number), pyint(value), Py.newLong(time), Py.newString(busName)
+ });
+ }
+ }
+
+ public void rawMidi(final byte[] data) {
+ if (rawMidi1Meth != null) {
+ rawMidi1Meth.__call__(Py.java2py(data));
+ }
+ }
+
+ public void rawMidi(final byte[] data, final long time, final String busName) {
+ if (rawMidi3Meth != null) {
+ rawMidi3Meth.__call__(Py.java2py(data), Py.newLong(time), Py.newString(busName));
+ }
+ }
+
+ public void midiMessage(final MidiMessage msg) {
+ if (midiMessage1Meth != null) {
+ midiMessage1Meth.__call__(Py.java2py(msg));
+ }
+ }
+
+ public void midiMessage(final MidiMessage msg, final long time, final String busName) {
+ if (midiMessage3Meth != null) {
+ midiMessage3Meth.__call__(Py.java2py(msg), Py.newLong(time), Py.newString(busName));
+ }
+ }
+
+ // processing_websockets callbacks.
+ public void webSocketEvent(final String msg) {
+ if (webSocketEventMeth != null) {
+ webSocketEventMeth.__call__(Py.java2py(msg));
+ }
+ }
+
+ public void webSocketServerEvent(final String msg) {
+ if (webSocketServerEventMeth != null) {
+ webSocketServerEventMeth.__call__(Py.java2py(msg));
+ }
+ }
+
public void setSketchPositionListener(final SketchPositionListener sketchPositionListener) {
this.sketchPositionListener = sketchPositionListener;
}
diff --git a/runtime/src/jycessing/Printer.java b/runtime/src/jycessing/Printer.java
index c2b517b1..2001557e 100644
--- a/runtime/src/jycessing/Printer.java
+++ b/runtime/src/jycessing/Printer.java
@@ -2,4 +2,6 @@
public interface Printer {
void print(Object o);
+
+ void flush();
}
diff --git a/runtime/src/jycessing/PythonSketchError.java b/runtime/src/jycessing/PythonSketchError.java
index cf23ad92..cb62de3a 100644
--- a/runtime/src/jycessing/PythonSketchError.java
+++ b/runtime/src/jycessing/PythonSketchError.java
@@ -17,8 +17,8 @@ public PythonSketchError(final String message, final String fileName, final int
this(message, fileName, line, 0);
}
- public PythonSketchError(final String message, final String fileName, final int line,
- final int column) {
+ public PythonSketchError(
+ final String message, final String fileName, final int line, final int column) {
super(message);
this.fileName = fileName;
diff --git a/runtime/src/jycessing/ReflectionUtil.java b/runtime/src/jycessing/ReflectionUtil.java
index 466ae3a9..513b4668 100644
--- a/runtime/src/jycessing/ReflectionUtil.java
+++ b/runtime/src/jycessing/ReflectionUtil.java
@@ -13,8 +13,8 @@ public static Field accessibleField(final Class> c, final String fieldName) {
}
}
- public static void setObjectStatic(final Class klass, final String fieldName,
- final Object value) {
+ public static void setObjectStatic(
+ final Class klass, final String fieldName, final Object value) {
try {
accessibleField(klass, fieldName).set(null, value);
} catch (final Exception e) {
@@ -22,8 +22,8 @@ public static void setObjectStatic(final Class klass, final String fieldN
}
}
- public static void setBooleanStatic(final Class klass, final String fieldName,
- final boolean value) {
+ public static void setBooleanStatic(
+ final Class klass, final String fieldName, final boolean value) {
try {
accessibleField(klass, fieldName).set(null, value);
} catch (final Exception e) {
diff --git a/runtime/src/jycessing/RunnableSketch.java b/runtime/src/jycessing/RunnableSketch.java
index 4e2bbd13..28a0a3b2 100644
--- a/runtime/src/jycessing/RunnableSketch.java
+++ b/runtime/src/jycessing/RunnableSketch.java
@@ -5,49 +5,32 @@
import jycessing.Runner.LibraryPolicy;
-/**
- *
- * Everything Runner.runSketchBlocking needs to know to be able to run a sketch.
- *
- */
+/** Everything Runner.runSketchBlocking needs to know to be able to run a sketch. */
public interface RunnableSketch {
- /**
- * @return The main file of the sketch
- */
+ /** @return The main file of the sketch */
public abstract File getMainFile();
- /**
- * @return The primary code to run
- */
+ /** @return The primary code to run */
public abstract String getMainCode();
- /**
- * @return The main directory of the sketch
- */
+ /** @return The main directory of the sketch */
public abstract File getHomeDirectory();
- /**
- * @return Files to append to sys.path
- */
+ /** @return Files to append to sys.path */
public abstract List getPathEntries();
- /**
- * @return Arguments to pass to PApplet
- */
+ /** @return Arguments to pass to PApplet */
public abstract String[] getPAppletArguments();
- /**
- * @return Directories to search for Processing libraries
- */
+ /** @return Directories to search for Processing libraries */
public abstract List getLibraryDirectories();
- /**
- * @return How eagerly we should look for libraries
- */
+ /** @return How eagerly we should look for libraries */
public abstract LibraryPolicy getLibraryPolicy();
/**
* Should probably be true, unless you're a warmup sketch
+ *
* @return Whether the sketch should run or not
*/
public abstract boolean shouldRun();
diff --git a/runtime/src/jycessing/Runner.java b/runtime/src/jycessing/Runner.java
old mode 100644
new mode 100755
index d4a5ec1d..29d3b712
--- a/runtime/src/jycessing/Runner.java
+++ b/runtime/src/jycessing/Runner.java
@@ -29,10 +29,7 @@
import java.util.List;
import java.util.Properties;
import java.util.Set;
-
-import jycessing.launcher.LaunchHelper;
-import jycessing.launcher.StandaloneSketch;
-import jycessing.mode.export.ExportedSketch;
+import java.util.TreeSet;
import org.python.core.Py;
import org.python.core.PyList;
@@ -42,6 +39,9 @@
import org.python.util.InteractiveConsole;
import org.python.util.PythonInterpreter;
+import jycessing.launcher.LaunchHelper;
+import jycessing.launcher.StandaloneSketch;
+import jycessing.mode.export.ExportedSketch;
import processing.core.PApplet;
import processing.core.PConstants;
@@ -50,6 +50,7 @@ public class Runner {
private static final String BUILD_PROPERTIES = "build.properties";
private static final String ARCH;
+
static {
final int archBits = Integer.parseInt(System.getProperty("sun.arch.data.model"));
if (PApplet.platform == PConstants.MACOSX) {
@@ -76,12 +77,12 @@ private static String loadBuildNumber() {
}
}
- private static final String LAUNCHER_TEXT = IOUtil.readResourceAsText(LaunchHelper.class,
- "launcher.py");
+ private static final String LAUNCHER_TEXT =
+ IOUtil.readResourceAsText(LaunchHelper.class, "launcher.py");
private static final String CORE_TEXT = IOUtil.readResourceAsText(Runner.class, "core.py");
// -Dverbose=true for some logging
- static public boolean VERBOSE = Boolean.getBoolean("verbose");
+ public static boolean VERBOSE = Boolean.getBoolean("verbose");
static void log(final Object... objs) {
if (!VERBOSE) {
@@ -94,9 +95,8 @@ static void log(final Object... objs) {
}
/**
- * Recursively search the given directory for jar files and directories
- * containing dynamic libraries, adding them to the classpath and the
- * library path respectively.
+ * Recursively search the given directory for jar files and directories containing dynamic
+ * libraries, adding them to the classpath and the library path respectively.
*/
private static void searchForExtraStuff(final File dir, final Set entries) {
if (dir == null) {
@@ -111,24 +111,28 @@ private static void searchForExtraStuff(final File dir, final Set entrie
log("Searching: ", dir);
- final File[] dlls = dir.listFiles(new FilenameFilter() {
- @Override
- public boolean accept(final File dir, final String name) {
- return name.matches("^.+\\.(so|dll|jnilib|dylib)$");
- }
- });
+ final File[] dlls =
+ dir.listFiles(
+ new FilenameFilter() {
+ @Override
+ public boolean accept(final File dir, final String name) {
+ return name.matches("^.+\\.(so|dll|jnilib|dylib)$");
+ }
+ });
if (dlls != null && dlls.length > 0) {
entries.add(dir.getAbsolutePath());
} else {
log("No DLLs in ", dir);
}
- final File[] jars = dir.listFiles(new FilenameFilter() {
- @Override
- public boolean accept(final File dir, final String name) {
- return name.matches("^.+\\.jar$");
- }
- });
+ final File[] jars =
+ dir.listFiles(
+ new FilenameFilter() {
+ @Override
+ public boolean accept(final File dir, final String name) {
+ return name.matches("^.+\\.jar$");
+ }
+ });
if (!(jars == null || jars.length == 0)) {
for (final File jar : jars) {
entries.add(jar.getAbsolutePath());
@@ -137,12 +141,14 @@ public boolean accept(final File dir, final String name) {
log("No JARs in ", dir);
}
- final File[] dirs = dir.listFiles(new FileFilter() {
- @Override
- public boolean accept(final File f) {
- return f.isDirectory() && f.getName().charAt(0) != '.';
- }
- });
+ final File[] dirs =
+ dir.listFiles(
+ new FileFilter() {
+ @Override
+ public boolean accept(final File f) {
+ return f.isDirectory() && f.getName().charAt(0) != '.';
+ }
+ });
if (!(dirs == null || dirs.length == 0)) {
for (final File d : dirs) {
searchForExtraStuff(d, entries);
@@ -155,11 +161,8 @@ public boolean accept(final File f) {
public static RunnableSketch sketch;
/**
- *
- * Entrypoint for non-PDE sketches. If we find ARGS_EXPORTED in the argument list,
- * Launch as an exported sketch.
- * Otherwise, launch as a standalone processing.py sketch.
- *
+ * Entrypoint for non-PDE sketches. If we find ARGS_EXPORTED in the argument list, Launch as an
+ * exported sketch. Otherwise, launch as a standalone processing.py sketch.
*/
public static void main(final String[] args) throws Exception {
if (args.length < 1) {
@@ -187,18 +190,12 @@ public static void main(final String[] args) throws Exception {
System.exit(0);
}
- /**
- * Specifies how to deal with the libraries directory.
- */
+ /** Specifies how to deal with the libraries directory. */
public enum LibraryPolicy {
- /**
- * Preemptively put every jar file and directory under the library path on sys.path.
- */
+ /** Preemptively put every jar file and directory under the library path on sys.path. */
PROMISCUOUS,
- /**
- * Only put jar files and directories onto sys.path as called for by add_library.
- */
+ /** Only put jar files and directories onto sys.path as called for by add_library. */
SELECTIVE
}
@@ -266,8 +263,8 @@ public List getPathEntries() {
}
/**
- * warmup() front-loads a huge amount of slow IO so that when the user gets around
- * to running a sketch, most of the slow work is already done.
+ * warmup() front-loads a huge amount of slow IO so that when the user gets around to running a
+ * sketch, most of the slow work is already done.
*/
public static void warmup() {
try (final WarmupSketch warmup = new WarmupSketch()) {
@@ -277,40 +274,60 @@ public static void warmup() {
}
}
- public synchronized static void runSketchBlocking(final RunnableSketch sketch,
- final Printer stdout, final Printer stderr) throws PythonSketchError {
+ public static synchronized void runSketchBlocking(
+ final RunnableSketch sketch, final Printer stdout, final Printer stderr)
+ throws PythonSketchError {
runSketchBlocking(sketch, stdout, stderr, null);
}
- public synchronized static void runSketchBlocking(final RunnableSketch sketch,
- final Printer stdout, final Printer stderr,
- final SketchPositionListener sketchPositionListener) throws PythonSketchError {
+ public static synchronized void runSketchBlocking(
+ final RunnableSketch sketch,
+ final Printer stdout,
+ final Printer stderr,
+ final SketchPositionListener sketchPositionListener)
+ throws PythonSketchError {
final Properties props = new Properties();
// Suppress sys-package-manager output.
props.setProperty("python.verbose", "error");
+ // Attempt to prevent "Failed to install '': java.nio.charset.UnsupportedCharsetException: cp0."
+ props.put("python.console.encoding", "utf-8");
+ props.put("python.io.encoding", "utf-8");
+ props.put("python.io.errors", "utf-8");
+
+ props.put("python.import.site", "false");
+
+ props.put("python.cachedir.skip", "false");
+ props.put("python.cachedir",
+ new File(System.getProperty("java.io.tmpdir"), "PythonModeCache").getAbsolutePath());
+
+ // Permit python subclasses to access protect fields, as in Beads.
+ props.put("python.security.respectJavaAccessibility", "false");
+
// Can be handy for class loading issues and the like.
// props.setProperty("python.verbose", "debug");
final StringBuilder pythonPath = new StringBuilder();
- final List libDirs = sketch.getLibraryDirectories();
+ // The code may be located in a temp dir, if the sketch is unsaved.
+ final String actualCodeLocation = sketch.getMainFile().getParentFile().getAbsolutePath();
+ pythonPath.append(File.pathSeparator).append(actualCodeLocation);
final String sketchDirPath = sketch.getHomeDirectory().getAbsolutePath();
pythonPath.append(File.pathSeparator).append(sketchDirPath);
-
props.setProperty("python.path", pythonPath.toString());
props.setProperty("python.main", sketch.getMainFile().getAbsolutePath());
props.setProperty("python.main.root", sketchDirPath);
final String[] args = sketch.getPAppletArguments();
+ log("PythonInterpreter.initialize args: " + String.join(" ", args));
PythonInterpreter.initialize(null, props, args);
final PySystemState sys = Py.getSystemState();
- final PyStringMap originalModules = ((PyStringMap)sys.modules).copy();
- final PyList originalPath = new PyList((PyObject)sys.path);
- final PyStringMap builtins = (PyStringMap)sys.getBuiltins();
+ final PyStringMap originalModules = ((PyStringMap) sys.modules).copy();
+ final PyList originalPath = new PyList((PyObject) sys.path);
+ final PyStringMap builtins = (PyStringMap) sys.getBuiltins();
final PyStringMap originalBuiltins = builtins.copy();
try {
final InteractiveConsole interp = new InteractiveConsole();
@@ -322,15 +339,16 @@ public synchronized static void runSketchBlocking(final RunnableSketch sketch,
// Add all of the sketch's requested sys.path entries, and add all jar
// files found there, recursively.
- final Set userLibs = new HashSet<>();
+ final Set userLibs = new TreeSet<>();
for (final File entry : sketch.getPathEntries()) {
- sys.path.insert(0, Py.newString(entry.getAbsolutePath()));
+ userLibs.add(entry.getAbsolutePath());
searchForExtraStuff(entry, userLibs);
}
// Add the add_library function to the sketch namespace.
+ final List libDirs = sketch.getLibraryDirectories();
if (libDirs != null) {
- new LibraryImporter(sketch.getLibraryDirectories(), interp);
+ new LibraryImporter(libDirs, interp);
if (sketch.getLibraryPolicy() == LibraryPolicy.PROMISCUOUS) {
log("Promiscusouly adding all libraries in " + libDirs);
@@ -341,16 +359,15 @@ public synchronized static void runSketchBlocking(final RunnableSketch sketch,
searchForExtraStuff(dir, libs);
}
for (final String lib : libs) {
- sys.path.insert(0, Py.newString(lib));
+ userLibs.add(lib);
}
}
}
for (final String lib : userLibs) {
- sys.path.insert(0, Py.newString(lib));
+ sys.path.insert(0, Py.newStringUTF8(lib));
}
-
// Make fake "launcher" module available to sketches - will only work with standalone sketches
interp.exec(LAUNCHER_TEXT);
@@ -362,13 +379,13 @@ public synchronized static void runSketchBlocking(final RunnableSketch sketch,
* bound methods (such as loadImage(), noSmooth(), noise(), etc.) in the builtins
* namespace.
*/
- interp.set("__cwd__", sketch.getHomeDirectory().getAbsolutePath());
+ interp.set("__cwd__", Py.newStringUTF8(sketch.getHomeDirectory().getAbsolutePath()));
interp.set("__python_mode_build__", BUILD_NUMBER);
interp.set("__stdout__", stdout);
interp.set("__stderr__", stderr);
final PAppletJythonDriver applet =
- new PAppletJythonDriver(interp, sketch.getMainFile().toString(), sketch.getMainCode(),
- stdout);
+ new PAppletJythonDriver(
+ interp, sketch.getMainFile().toString(), sketch.getMainCode(), stdout);
interp.set("__papplet__", applet);
interp.exec(CORE_TEXT);
@@ -407,9 +424,8 @@ public synchronized static void runSketchBlocking(final RunnableSketch sketch,
}
/**
- * The urllib module unit tests exposed a bug in how the codecs module
- * is mangled when the world is reset around it. This hack forces it to
- * reinitialize.
+ * The urllib module unit tests exposed a bug in how the codecs module is mangled when the world
+ * is reset around it. This hack forces it to reinitialize.
*/
private static void resetCodecsModule() {
ReflectionUtil.setObject(Py.getSystemState(), "codecState", null);
diff --git a/runtime/src/jycessing/StreamPrinter.java b/runtime/src/jycessing/StreamPrinter.java
index 0bed005b..63c8d351 100644
--- a/runtime/src/jycessing/StreamPrinter.java
+++ b/runtime/src/jycessing/StreamPrinter.java
@@ -15,4 +15,9 @@ public void print(final Object o) {
stream.print(String.valueOf(o));
stream.flush();
}
+
+ @Override
+ public void flush() {
+ stream.flush();
+ }
}
diff --git a/runtime/src/jycessing/TreeCopier.java b/runtime/src/jycessing/TreeCopier.java
index 95025896..ac6eb9ee 100644
--- a/runtime/src/jycessing/TreeCopier.java
+++ b/runtime/src/jycessing/TreeCopier.java
@@ -48,9 +48,7 @@
class TreeCopier implements FileVisitor {
- /**
- * Copy source file to target location.
- */
+ /** Copy source file to target location. */
static void copyFile(final Path source, final Path target) {
try {
Files.copy(source, target, COPY_ATTRIBUTES, REPLACE_EXISTING);
diff --git a/runtime/src/jycessing/annotations/PythonUsage.java b/runtime/src/jycessing/annotations/PythonUsage.java
index 329dc0d3..4f642ebb 100644
--- a/runtime/src/jycessing/annotations/PythonUsage.java
+++ b/runtime/src/jycessing/annotations/PythonUsage.java
@@ -1,6 +1,4 @@
-/**
- *
- */
+/** */
package jycessing.annotations;
import java.lang.annotation.ElementType;
@@ -9,17 +7,17 @@
import java.lang.annotation.Target;
/**
- * Reminds you that this method is used from Python. Unless you know what
- * you're doing, do not change its name.
- *
+ * Reminds you that this method is used from Python. Unless you know what you're doing, do not
+ * change its name.
+ *
* @author Ralf Biedert
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
public @interface PythonUsage {
/**
- * The original method name. Must match with the actual method name.
- *
+ * The original method name. Must match with the actual method name.
+ *
* @return The original method name.
*/
public String methodName();
diff --git a/runtime/src/jycessing/build.properties b/runtime/src/jycessing/build.properties
index ddc46b28..1a22f6ed 100644
--- a/runtime/src/jycessing/build.properties
+++ b/runtime/src/jycessing/build.properties
@@ -1,5 +1,5 @@
#Python mode build number
-#Wed, 08 Jul 2015 14:03:43 -0400
+#Tue, 19 May 2020 13:23:09 -0400
-build.date=2015/07/08 14\:03
-build.number=0412
+build.date=2020/05/19 13\:23
+build.number=3063
diff --git a/runtime/src/jycessing/core.py b/runtime/src/jycessing/core.py
index ca449fcf..30d068d4 100644
--- a/runtime/src/jycessing/core.py
+++ b/runtime/src/jycessing/core.py
@@ -5,45 +5,21 @@
from numbers import Number
-# Bring all of the core Processing classes by name into the builtin namespace.
-from processing.core import PApplet
-__builtin__.PApplet = PApplet
-
-from processing.core import PConstants
-__builtin__.PConstants = PConstants
-
-from processing.core import PFont
-__builtin__.PFont = PFont
-
-from processing.core import PGraphics
-__builtin__.PGraphics = PGraphics
-
-from processing.core import PImage
-__builtin__.PImage = PImage
-
-from processing.core import PMatrix
-__builtin__.PMatrix = PMatrix
-from processing.core import PMatrix2D
-__builtin__.PMatrix2D = PMatrix2D
-
-from processing.core import PMatrix3D
-__builtin__.PMatrix3D = PMatrix3D
-
-from processing.core import PShape
-__builtin__.PShape = PShape
-
-from processing.core import PShapeOBJ
-__builtin__.PShapeOBJ = PShapeOBJ
-
-from processing.core import PShapeSVG
-__builtin__.PShapeSVG = PShapeSVG
+# Bring all of the core Processing classes by name into the builtin namespace.
+import importlib
+for klass in """
+ PApplet PConstants PFont PGraphics PImage PMatrix PMatrix2D PMatrix3D
+ PShape PShapeOBJ PShapeSVG PStyle PSurface
+""".split():
+ module = importlib.import_module("processing.core.%s" % klass)
+ assert module
+ setattr(__builtin__, klass, module)
-from processing.core import PStyle
-__builtin__.PStyle = PStyle
-from processing.core import PSurface
-__builtin__.PSurface = PSurface
+module = importlib.import_module("processing.opengl.PGL")
+assert module
+setattr(__builtin__, "PGL", module)
# PVector requires special handling, because it exposes the same method names
# as static methods and instance methods.
@@ -52,30 +28,28 @@ class PVector(__pvector__):
def __instance_add__(self, *args):
if len(args) == 1:
v = args[0]
- self.x += v.x
- self.y += v.y
- self.z += v.z
else:
- self.x += args[0]
- self.y += args[1]
- self.z += args[2]
+ v = args
+ self.x += v[0]
+ self.y += v[1]
+ self.z += v[2]
+ return self
def __instance_sub__(self, *args):
if len(args) == 1:
v = args[0]
- self.x -= v.x
- self.y -= v.y
- self.z -= v.z
else:
- self.x -= args[0]
- self.y -= args[1]
- self.z -= args[2]
+ v = args
+ self.x -= v[0]
+ self.y -= v[1]
+ self.z -= v[2]
+ return self
def __instance_mult__(self, o):
- PVector.mult(self, o, self)
+ return PVector.mult(self, o, self)
def __instance_div__(self, f):
- PVector.div(self, f, self)
+ return PVector.div(self, f, self)
def __instance_cross__(self, o):
return PVector.cross(self, o, self)
@@ -86,8 +60,9 @@ def __instance_dist__(self, o):
def __instance_dot__(self, *args):
if len(args) == 1:
v = args[0]
- return self.x * v.x + self.y * v.y + self.z * v.z
- return self.x * args[0] + self.y * args[1] + self.z * args[2]
+ else:
+ v = args
+ return self.x * v[0] + self.y * v[1] + self.z * v[2]
def __instance_lerp__(self, *args):
if len(args) == 4:
@@ -97,13 +72,14 @@ def __instance_lerp__(self, *args):
t = args[3]
elif len(args) == 2:
v = args[0]
- x = v.x
- y = v.y
- z = v.z
+ x = v[0]
+ y = v[1]
+ z = v[2]
t = args[1]
else:
raise Exception('lerp takes either (x, y, z, t) or (v, t)')
__pvector__.lerp(self, x, y, z, t)
+ return self
def __init__(self, x=0, y=0, z=0):
__pvector__.__init__(self, x, y, z)
@@ -122,6 +98,12 @@ def get(self):
def copy(self):
return PVector(self.x, self.y, self.z)
+ def __getitem__(self, k):
+ return getattr(self, ('x','y','z')[k])
+
+ def __setitem__(self, k, v):
+ setattr(self, ('x','y','z')[k], v)
+
def __copy__(self):
return PVector(self.x, self.y, self.z)
@@ -131,15 +113,15 @@ def __deepcopy__(self, memo):
@classmethod
def add(cls, a, b, dest=None):
if dest is None:
- return PVector(a.x + b.x, a.y + b.y, a.z + b.z)
- dest.set(a.x + b.x, a.y + b.y, a.z + b.z)
+ return PVector(a.x + b[0], a.y + b[1], a.z + b[2])
+ dest.set(a.x + b[0], a.y + b[1], a.z + b[2])
return dest
@classmethod
def sub(cls, a, b, dest=None):
if dest is None:
- return PVector(a.x - b.x, a.y - b.y, a.z - b.z)
- dest.set(a.x - b.x, a.y - b.y, a.z - b.z)
+ return PVector(a.x - b[0], a.y - b[1], a.z - b[2])
+ dest.set(a.x - b[0], a.y - b[1], a.z - b[2])
return dest
@classmethod
@@ -172,14 +154,29 @@ def lerp(cls, a, b, f):
@classmethod
def cross(cls, a, b, dest=None):
- x = a.y * b.z - b.y * a.z
- y = a.z * b.x - b.z * a.x
- z = a.x * b.y - b.x * a.y
+ x = a.y * b[2] - b[1] * a.z
+ y = a.z * b[0] - b[2] * a.x
+ z = a.x * b[1] - b[0] * a.y
if dest is None:
return PVector(x, y, z)
dest.set(x, y, z)
return dest
+ @classmethod
+ def fromAngle(cls, *args):
+ jpv = __pvector__.fromAngle(*args)
+ return PVector(jpv.x, jpv.y, jpv.z)
+
+ @classmethod
+ def random2D(cls, *args):
+ jpv = __pvector__.random2D(*args)
+ return PVector(jpv.x, jpv.y, jpv.z)
+
+ @classmethod
+ def random3D(cls, *args):
+ jpv = __pvector__.random3D(*args)
+ return PVector(jpv.x, jpv.y, jpv.z)
+
def __add__(a, b):
return PVector.add(a, b, None)
@@ -222,7 +219,7 @@ def __idiv__(a, b):
return a
def __eq__(a, b):
- return a.x == b.x and a.y == b.y and a.z == b.z
+ return a.x == b[0] and a.y == b[1] and a.z == b[2]
def __lt__(a, b):
return a.magSq() < b.magSq()
@@ -239,8 +236,8 @@ def __ge__(a, b):
# Now expose the funky PVector class as a builtin.
__builtin__.PVector = PVector
-# Make it available to sketches by the name "this", to better match existing
-# Java-based documentation for third-party libraries, and such.
+# Make the PApplet available to sketches by the name "this", to better match
+# existing Java-based documentation for third-party libraries, and such.
__builtin__.this = __papplet__
@@ -248,184 +245,45 @@ def __ge__(a, b):
# https://github.com/kazimuth/python-mode-processing for the
# technique of exploiting Jython's bound methods, which is tidy
# and simple.
-__builtin__.ambient = __papplet__.ambient
-__builtin__.ambientLight = __papplet__.ambientLight
-__builtin__.applyMatrix = __papplet__.applyMatrix
-__builtin__.arc = __papplet__.arc
-__builtin__.background = __papplet__.background
-__builtin__.beginCamera = __papplet__.beginCamera
-__builtin__.beginContour = __papplet__.beginContour
-__builtin__.beginPGL = __papplet__.beginPGL
-__builtin__.beginRaw = __papplet__.beginRaw
-__builtin__.beginRecord = __papplet__.beginRecord
-__builtin__.beginShape = __papplet__.beginShape
-__builtin__.bezier = __papplet__.bezier
-__builtin__.bezierDetail = __papplet__.bezierDetail
-__builtin__.bezierPoint = __papplet__.bezierPoint
-__builtin__.bezierTangent = __papplet__.bezierTangent
-__builtin__.bezierVertex = __papplet__.bezierVertex
-__builtin__.blend = __papplet__.blend
-__builtin__.blendMode = __papplet__.blendMode
-__builtin__.box = __papplet__.box
-__builtin__.camera = __papplet__.camera
-__builtin__.clear = __papplet__.clear
-__builtin__.clip = __papplet__.clip
-__builtin__.color = __papplet__.color
-__builtin__.colorMode = __papplet__.colorMode
-__builtin__.copy = __papplet__.copy
-__builtin__.createFont = __papplet__.createFont
-__builtin__.createGraphics = __papplet__.createGraphics
-__builtin__.createImage = __papplet__.createImage
-__builtin__.createInput = __papplet__.createInput
-__builtin__.createOutput = __papplet__.createOutput
-__builtin__.createReader = __papplet__.createReader
-__builtin__.createShape = __papplet__.createShape
-__builtin__.cursor = __papplet__.cursor
-__builtin__.curve = __papplet__.curve
-__builtin__.curveDetail = __papplet__.curveDetail
-__builtin__.curvePoint = __papplet__.curvePoint
-__builtin__.curveTangent = __papplet__.curveTangent
-__builtin__.curveTightness = __papplet__.curveTightness
-__builtin__.curveVertex = __papplet__.curveVertex
-__builtin__.delay = __papplet__.delay
-__builtin__.directionalLight = __papplet__.directionalLight
-__builtin__.ellipse = __papplet__.ellipse
-__builtin__.ellipseMode = __papplet__.ellipseMode
-__builtin__.emissive = __papplet__.emissive
-__builtin__.endCamera = __papplet__.endCamera
-__builtin__.endContour = __papplet__.endContour
-__builtin__.endPGL = __papplet__.endPGL
-__builtin__.endRaw = __papplet__.endRaw
-__builtin__.endRecord = __papplet__.endRecord
-__builtin__.endShape = __papplet__.endShape
-__builtin__.exit = __papplet__.exit
-__builtin__.fill = __papplet__.fill
# We handle filter() by hand to permit both P5's filter() and Python's filter().
# __builtin__.filter = __papplet__.filter
-__builtin__.frameRate = __papplet__.frameRate
-__builtin__.frustum = __papplet__.frustum
-
-__builtin__.hint = __papplet__.hint
-__builtin__.image = __papplet__.image
-__builtin__.imageMode = __papplet__.imageMode
-
-__builtin__.lightFalloff = __papplet__.lightFalloff
-__builtin__.lightSpecular = __papplet__.lightSpecular
-__builtin__.lights = __papplet__.lights
-__builtin__.line = __papplet__.line
-__builtin__.link = __papplet__.link
-__builtin__.loadBytes = __papplet__.loadBytes
-__builtin__.loadFont = __papplet__.loadFont
-__builtin__.loadImage = __papplet__.loadImage
-__builtin__.loadJSONArray = __papplet__.loadJSONArray
-__builtin__.loadJSONObject = __papplet__.loadJSONObject
-__builtin__.loadPixels = __papplet__.loadPixels
-__builtin__.loadShader = __papplet__.loadShader
-__builtin__.loadShape = __papplet__.loadShape
-__builtin__.loadTable = __papplet__.loadTable
-__builtin__.loadXML = __papplet__.loadXML
-__builtin__.loop = __papplet__.loop
-__builtin__.millis = __papplet__.millis
-__builtin__.modelX = __papplet__.modelX
-__builtin__.modelY = __papplet__.modelY
-__builtin__.modelZ = __papplet__.modelZ
-__builtin__.noClip = __papplet__.noClip
-__builtin__.noCursor = __papplet__.noCursor
-__builtin__.noFill = __papplet__.noFill
-__builtin__.noLights = __papplet__.noLights
-__builtin__.noLoop = __papplet__.noLoop
-__builtin__.noSmooth = __papplet__.noSmooth
-__builtin__.noStroke = __papplet__.noStroke
-__builtin__.noTint = __papplet__.noTint
-__builtin__.noise = __papplet__.noise
-__builtin__.noiseDetail = __papplet__.noiseDetail
-__builtin__.noiseSeed = __papplet__.noiseSeed
-__builtin__.normal = __papplet__.normal
-__builtin__.ortho = __papplet__.ortho
-__builtin__.parseXML = __papplet__.parseXML
-__builtin__.perspective = __papplet__.perspective
-__builtin__.point = __papplet__.point
-__builtin__.pointLight = __papplet__.pointLight
-__builtin__.popMatrix = __papplet__.popMatrix
-__builtin__.popStyle = __papplet__.popStyle
-__builtin__.printArray = __papplet__.printArray
-__builtin__.printCamera = __papplet__.printCamera
-__builtin__.printMatrix = __papplet__.printMatrix
-__builtin__.printProjection = __papplet__.printProjection
-__builtin__.quad = __papplet__.quad
-__builtin__.quadraticVertex = __papplet__.quadraticVertex
-
# Processing's "random" function works as documented, but is blown
# away if the user does a python 'import random'. This seems
# reasonable to me.
__builtin__.random = __papplet__.random
-__builtin__.randomGaussian = __papplet__.randomGaussian
-__builtin__.randomSeed = __papplet__.randomSeed
-__builtin__.rect = __papplet__.rect
-__builtin__.rectMode = __papplet__.rectMode
-__builtin__.redraw = __papplet__.redraw
-__builtin__.requestImage = __papplet__.requestImage
-__builtin__.resetMatrix = __papplet__.resetMatrix
-__builtin__.resetShader = __papplet__.resetShader
-__builtin__.rotate = __papplet__.rotate
-__builtin__.rotateX = __papplet__.rotateX
-__builtin__.rotateY = __papplet__.rotateY
-__builtin__.rotateZ = __papplet__.rotateZ
-__builtin__.save = __papplet__.save
-__builtin__.saveBytes = __papplet__.saveBytes
-__builtin__.saveFrame = __papplet__.saveFrame
-__builtin__.saveJSONArray = __papplet__.saveJSONArray
-__builtin__.saveJSONObject = __papplet__.saveJSONObject
-__builtin__.saveStream = __papplet__.saveStream
-__builtin__.saveStrings = __papplet__.saveStrings
-__builtin__.saveTable = __papplet__.saveTable
-__builtin__.saveXML = __papplet__.saveXML
-__builtin__.scale = __papplet__.scale
-__builtin__.screenX = __papplet__.screenX
-__builtin__.screenY = __papplet__.screenY
-__builtin__.screenZ = __papplet__.screenZ
-__builtin__.selectFolder = __papplet__.selectFolder
-__builtin__.selectInput = __papplet__.selectInput
-__builtin__.selectOutput = __papplet__.selectOutput
-__builtin__.shader = __papplet__.shader
-__builtin__.shape = __papplet__.shape
-__builtin__.shapeMode = __papplet__.shapeMode
-__builtin__.shearX = __papplet__.shearX
-__builtin__.shearY = __papplet__.shearY
-__builtin__.shininess = __papplet__.shininess
-__builtin__.size = __papplet__.size
-__builtin__.smooth = __papplet__.smooth
-__builtin__.specular = __papplet__.specular
-__builtin__.sphere = __papplet__.sphere
-__builtin__.sphereDetail = __papplet__.sphereDetail
-__builtin__.spotLight = __papplet__.spotLight
-__builtin__.stroke = __papplet__.stroke
-__builtin__.strokeCap = __papplet__.strokeCap
-__builtin__.strokeJoin = __papplet__.strokeJoin
-__builtin__.strokeWeight = __papplet__.strokeWeight
-
# Because of two 5-arg text() methods, we have to do this in Java.
# __builtin__.text = __papplet__.text
-__builtin__.textAlign = __papplet__.textAlign
-__builtin__.textAscent = __papplet__.textAscent
-__builtin__.textDescent = __papplet__.textDescent
-__builtin__.textFont = __papplet__.textFont
-__builtin__.textLeading = __papplet__.textLeading
-__builtin__.textMode = __papplet__.textMode
-__builtin__.textSize = __papplet__.textSize
-__builtin__.textWidth = __papplet__.textWidth
-__builtin__.texture = __papplet__.texture
-__builtin__.textureMode = __papplet__.textureMode
-__builtin__.textureWrap = __papplet__.textureWrap
-__builtin__.tint = __papplet__.tint
-__builtin__.translate = __papplet__.translate
-__builtin__.triangle = __papplet__.triangle
-__builtin__.updatePixels = __papplet__.updatePixels
-__builtin__.vertex = __papplet__.vertex
+for f in """
+ ambient ambientLight applyMatrix arc beginCamera beginContour beginPGL
+ beginRaw beginRecord beginShape bezier bezierDetail bezierPoint bezierTangent
+ bezierVertex blendMode box camera clear clip color colorMode createFont
+ createImage createInput createOutput createReader createShape curve curveDetail
+ curvePoint curveTangent curveTightness curveVertex delay directionalLight
+ displayDensity ellipse ellipseMode emissive endCamera endContour endPGL endRaw
+ endRecord endShape exit fill frameRate frustum fullScreen hint imageMode
+ launch lightFalloff lightSpecular lights line link loadBytes loadFont loadImage
+ loadJSONArray loadJSONObject loadPixels loadShader loadShape loadTable loadXML
+ loop millis modelX modelY modelZ noClip noCursor noFill noLights noLoop
+ noSmooth noStroke noTint noise noiseDetail noiseSeed normal ortho parseXML
+ perspective pixelDensity point pointLight popMatrix popStyle printArray
+ printCamera printMatrix printProjection quad quadraticVertex randomGaussian
+ randomSeed rect rectMode redraw requestImage resetMatrix resetShader rotate
+ rotateX rotateY rotateZ save saveBytes saveFrame saveJSONArray saveJSONObject
+ saveStream saveTable saveXML scale screenX screenY screenZ selectFolder
+ selectInput selectOutput shader shape shapeMode shearX shearY shininess
+ size sketchPath smooth specular sphere sphereDetail spotLight stroke strokeCap
+ strokeJoin strokeWeight textAlign textAscent textDescent textFont textLeading
+ textMode textSize textWidth textureMode textureWrap thread tint translate
+ triangle updatePixels vertex
+ image background mask blend copy cursor texture
+ circle square push pop
+""".split():
+ assert getattr(__papplet__, f)
+ setattr(__builtin__, f, getattr(__papplet__, f))
'''
In order to get colors to behave reasonably, they have to be cast to positive
@@ -453,78 +311,44 @@ def __ge__(a, b):
# And these are PApplet static methods. Some are commented out to indicate
# that we prefer or require Jython's implementation.
#__builtin__.abs = PApplet.abs
-__builtin__.acos = PApplet.acos
-__builtin__.append = PApplet.append
-__builtin__.arrayCopy = PApplet.arrayCopy
-__builtin__.asin = PApplet.asin
-__builtin__.atan = PApplet.atan
-__builtin__.atan2 = PApplet.atan2
-__builtin__.binary = PApplet.binary
-__builtin__.blendColor = PApplet.blendColor
-__builtin__.ceil = PApplet.ceil
-__builtin__.concat = PApplet.concat
-__builtin__.constrain = lambda x, a, b: max(min(x, b), a)
-__builtin__.cos = PApplet.cos
-__builtin__.createInput = PApplet.createInput
-__builtin__.createOutput = PApplet.createOutput
-__builtin__.createReader = PApplet.createReader
-__builtin__.day = PApplet.day
-__builtin__.debug = PApplet.debug
-__builtin__.degrees = PApplet.degrees
-__builtin__.dist = PApplet.dist
# __builtin__.exec = PApplet.exec
-__builtin__.exp = PApplet.exp
-__builtin__.expand = PApplet.expand
-__builtin__.floor = PApplet.floor
-__builtin__.hex = PApplet.hex
-__builtin__.hour = PApplet.hour
-__builtin__.join = PApplet.join
-__builtin__.lerp = PApplet.lerp
-__builtin__.loadBytes = PApplet.loadBytes
-__builtin__.log = PApplet.log
-__builtin__.mag = PApplet.mag
# We permit both Python and P5's map()s.
# __builtin__.map = PApplet.map
-__builtin__.match = PApplet.match
-__builtin__.matchAll = PApplet.matchAll
# __builtin__.max = PApplet.max
# __builtin__.min = PApplet.min
-__builtin__.minute = PApplet.minute
-__builtin__.month = PApplet.month
-__builtin__.nf = PApplet.nf
-__builtin__.nfc = PApplet.nfc
-__builtin__.nfp = PApplet.nfp
-__builtin__.nfs = PApplet.nfs
-__builtin__.norm = PApplet.norm
-__builtin__.pow = PApplet.pow
# __builtin__.print = PApplet.print
-#__builtin__.println = PApplet.println
-__builtin__.radians = PApplet.radians
-__builtin__.reverse = PApplet.reverse
+# __builtin__.println = PApplet.println
# __builtin__.round = PApplet.round
-__builtin__.saveBytes = PApplet.saveBytes
-__builtin__.saveStream = PApplet.saveStream
-__builtin__.saveStrings = PApplet.saveStrings
-__builtin__.second = PApplet.second
-__builtin__.shorten = PApplet.shorten
-__builtin__.sin = PApplet.sin
-__builtin__.sort = PApplet.sort
-__builtin__.splice = PApplet.splice
-__builtin__.split = PApplet.split
-__builtin__.splitTokens = PApplet.splitTokens
-__builtin__.sq = PApplet.sq
-__builtin__.sqrt = PApplet.sqrt
-__builtin__.subset = PApplet.subset
-__builtin__.tan = PApplet.tan
-__builtin__.trim = PApplet.trim
-__builtin__.unbinary = PApplet.unbinary
-__builtin__.unhex = PApplet.unhex
-__builtin__.year = PApplet.year
+
+__builtin__.constrain = lambda x, a, b: max(min(x, b), a)
+
+for f in """
+acos append arrayCopy asin atan atan2 binary blendColor ceil
+concat cos createInput createOutput createReader day debug degrees dist
+exp expand floor hex hour join lerp loadBytes log mag match matchAll
+minute month nf nfc nfp nfs norm pow radians reverse saveBytes saveStream
+saveStrings second shorten sin sort splice split splitTokens sq sqrt
+subset tan trim unbinary unhex year
+""".split():
+ assert getattr(PApplet, f)
+ setattr(__builtin__, f, getattr(PApplet, f))
# Here are some names that resolve to static *and* instance methods.
# Dispatch them to the appropriate methods based on the type of their
# arguments.
+def __createInput__(o):
+ if isinstance(o, basestring):
+ return __papplet__.createInput(o)
+ return PApplet.createInput(o)
+__builtin__.createInput = __createInput__
+
+def __createOutput__(o):
+ if isinstance(o, basestring):
+ return __papplet__.createOutput(o)
+ return PApplet.createOutput(o)
+__builtin__.createOutput = __createOutput__
+
def __createReader__(o):
if isinstance(o, basestring):
return __papplet__.createReader(o)
@@ -601,11 +425,15 @@ def __open__(filename, *args, **kwargs):
class FakeStdOut():
def write(self, s):
__stdout__.print(s)
+ def flush(self):
+ __stdout__.flush()
sys.stdout = FakeStdOut()
class FakeStdErr():
def write(self, s):
__stderr__.print(s)
+ def flush(self):
+ __stderr__.flush()
sys.stderr = FakeStdErr()
del FakeStdOut, FakeStdErr
@@ -615,6 +443,74 @@ def __println__(o):
__builtin__.println = __println__
+from org.python.core import Py
+class PGraphicsPythonModeWrapper(object):
+ class popper(object):
+ def __init__(self, enter_func, exit_func, *args):
+ self.exit_func = exit_func
+ self.as_result = enter_func(*args)
+ def __enter__(self):
+ return self.as_result
+ def __exit__(self, *args):
+ self.exit_func()
+
+ def __init__(self, g):
+ self.__dict__['g'] = g
+
+ def _get_wrapped_image_(self):
+ return self.__dict__['g']
+
+ # Coerce this object into the wrapped PGraphics.
+ def __tojava__(self, klass):
+ if isinstance(self.g, klass):
+ return self.g
+ return Py.NoConversion
+
+ def beginDraw(self):
+ return PGraphicsPythonModeWrapper.popper(
+ self.g.beginDraw, self.g.endDraw)
+
+ def pushMatrix(self):
+ return PGraphicsPythonModeWrapper.popper(
+ self.g.pushMatrix, self.g.popMatrix)
+
+ def beginShape(self, *args):
+ return PGraphicsPythonModeWrapper.popper(
+ self.g.beginShape, self.g.endShape, *args)
+
+ def beginRaw(self, *args):
+ return PGraphicsPythonModeWrapper.popper(
+ self.g.beginRaw, self.g.endRaw, *args)
+
+ def beginPGL(self):
+ return PGraphicsPythonModeWrapper.popper(
+ self.g.beginPGL, self.g.endPGL)
+
+ # The PGraphicsJava2D shadows its ellipse, rect, arc, and line functions
+ # with field declarations, so we must bypass them here.
+ def ellipse(self, *args):
+ PGraphics.ellipse(self, *args)
+
+ def rect(self, *args):
+ PGraphics.rect(self, *args)
+
+ def line(self, *args):
+ PGraphics.line(self, *args)
+
+ def arc(self, *args):
+ PGraphics.arc(self, *args)
+
+ def __getattr__(self, attr):
+ return getattr(self.g, attr)
+
+ def __setattr__(self, attr, value):
+ return setattr(self.g, attr, value)
+
+def FakeCreateGraphics(*args):
+ return PGraphicsPythonModeWrapper(__papplet__.createGraphics(*args))
+__builtin__.createGraphics = FakeCreateGraphics
+del FakeCreateGraphics
+
# Implement
#
# with pushFoo():
@@ -666,6 +562,7 @@ def shim(*args):
makePopper('beginShape', 'endShape',
close_args=[CLOSE], exposed_name='beginClosedShape')
makePopper('beginCamera', 'endCamera')
+makePopper('push', 'pop')
import os
os.chdir(__cwd__)
diff --git a/runtime/src/jycessing/detect_sketch_mode.py b/runtime/src/jycessing/detect_sketch_mode.py
index b19910b5..bc01c764 100644
--- a/runtime/src/jycessing/detect_sketch_mode.py
+++ b/runtime/src/jycessing/detect_sketch_mode.py
@@ -16,6 +16,8 @@
^(
draw
|
+ settings
+ |
setup
|
key(Pressed|Released|Typed)
@@ -58,6 +60,8 @@
(load|update)Pixels
|
background|clear|(no)?(Fill|Stroke)
+ |
+ createFont
)$
""", re.X)
@@ -78,7 +82,7 @@ def detect_mode(code, filename):
if not isinstance(e, ast.Call):
continue
f = e.func
- if illegalActiveModeCall.match(f.id):
+ if hasattr(f, 'id') and illegalActiveModeCall.match(f.id):
return 'MIXED', MixedModeError(
"You can't call %s() outside a function in \"active mode\"." % f.id,
__file__, node.lineno - 1)
diff --git a/runtime/src/jycessing/get_settings.py b/runtime/src/jycessing/get_settings.py
new file mode 100644
index 00000000..b099bdda
--- /dev/null
+++ b/runtime/src/jycessing/get_settings.py
@@ -0,0 +1,49 @@
+import ast
+
+__width__ = 100
+__height__ = 100
+__renderer__ = "JAVA2D"
+__fullScreen__ = False
+__smooth__ = False
+__noSmooth__ = False
+__pixelDensity__ = 1
+
+def extract_settings(module):
+ global __width__, __height__, __renderer__, __fullScreen__, __smooth__, __noSmooth__
+ global __pixelDensity__
+ toremove = []
+ for node in module.body:
+ if isinstance(node, ast.Expr) and isinstance(node.value, ast.Call):
+ func = node.value.func
+ if not hasattr(func, 'id'): continue
+ if func.id in ('size', 'smooth', 'noSmooth', 'fullScreen', 'pixelDensity'):
+ toremove.append(node)
+ if func.id == 'size':
+ args = node.value.args
+ if len(args) > 0 and isinstance(args[0], ast.Num):
+ __width__ = args[0].n
+ if len(args) > 1 and isinstance(args[1], ast.Num):
+ __height__ = args[1].n
+ if len(args) > 2:
+ if isinstance(args[2], ast.Str):
+ __renderer__ = args[2].s
+ elif isinstance(args[2], ast.Name):
+ __renderer__ = args[2].id
+ elif func.id == 'fullScreen':
+ __fullScreen__ = True
+ elif func.id == 'smooth':
+ __smooth__ = True
+ elif func.id == 'noSmooth':
+ __noSmooth__ = True
+ elif func.id == 'pixelDensity':
+ args = node.value.args
+ if len(args) > 0 and isinstance(args[0], ast.Num):
+ __pixelDensity__ = args[0].n
+
+ for node in toremove:
+ module.body.remove(node)
+
+module = ast.parse(__processing_source__ + "\n\n", filename=__file__)
+extract_settings(module)
+
+__cleaned_sketch__ = compile(module, __file__, mode='exec')
diff --git a/runtime/src/jycessing/jni/OSX.java b/runtime/src/jycessing/jni/OSX.java
new file mode 100644
index 00000000..aeb2ebfb
--- /dev/null
+++ b/runtime/src/jycessing/jni/OSX.java
@@ -0,0 +1,39 @@
+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
new file mode 100644
index 00000000..dd58aae4
--- /dev/null
+++ b/runtime/src/jycessing/jni/jniosx/jniosx/jniosx.m
@@ -0,0 +1,8 @@
+#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
new file mode 100644
index 00000000..79fa36f5
--- /dev/null
+++ b/runtime/src/jycessing/jni/jycessing_jni_OSX.h
@@ -0,0 +1,21 @@
+/* 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/launcher/LaunchHelper.java b/runtime/src/jycessing/launcher/LaunchHelper.java
index ffdca55e..d3ca4a3c 100644
--- a/runtime/src/jycessing/launcher/LaunchHelper.java
+++ b/runtime/src/jycessing/launcher/LaunchHelper.java
@@ -9,14 +9,14 @@
/**
* Launch and deployment service for various platforms.
- *
+ *
* @author Ralf Biedert
*/
public class LaunchHelper {
/**
* Copies one of our resources to a given file.
- *
+ *
* @param name
* @param path
* @throws IOException
diff --git a/runtime/src/jycessing/launcher/StandaloneSketch.java b/runtime/src/jycessing/launcher/StandaloneSketch.java
index f2790c03..38d9a4f8 100644
--- a/runtime/src/jycessing/launcher/StandaloneSketch.java
+++ b/runtime/src/jycessing/launcher/StandaloneSketch.java
@@ -19,11 +19,7 @@
import jycessing.annotations.PythonUsage;
import processing.core.PApplet;
-/**
- *
- * An old-school processing.py script.
- *
- */
+/** An old-school processing.py script. */
public class StandaloneSketch implements RunnableSketch {
static void log(final Object... objs) {
@@ -42,9 +38,9 @@ static void log(final Object... objs) {
/**
* Returns the path of the main processing-py.jar file.
- *
- * Used from launcher.py
- *
+ *
+ * Used from launcher.py
+ *
* @return the path of processing-py.jar.
*/
@PythonUsage(methodName = "getMainJarFile")
@@ -59,9 +55,8 @@ public File getMainJarFile() {
}
/**
- * Returns the 'root' folder of this instance. Used when running with a
- * wrapper.
- *
+ * Returns the 'root' folder of this instance. Used when running with a wrapper.
+ *
* @return the distribution root directory.
*/
public File getRuntimeRoot() {
@@ -76,8 +71,6 @@ public File getRuntimeRoot() {
return jar.getParentFile();
}
-
-
public StandaloneSketch(final String[] args) throws Exception {
final List argsList = Arrays.asList(args);
@@ -116,8 +109,8 @@ public StandaloneSketch(final String[] args) throws Exception {
}
final Pattern JAR_RESOURCE =
- Pattern.compile("jar:file:(.+?)/processing-py\\.jar!/jycessing/"
- + Pattern.quote(BUILD_PROPERTIES));
+ Pattern.compile(
+ "jar:file:(.+?)/processing-py\\.jar!/jycessing/" + Pattern.quote(BUILD_PROPERTIES));
final Pattern FILE_RESOURCE =
Pattern.compile("file:(.+?)/bin/jycessing/" + Pattern.quote(BUILD_PROPERTIES));
@@ -149,8 +142,8 @@ public File getHomeDirectory() {
@Override
public String[] getPAppletArguments() {
return new String[] {
- PApplet.ARGS_SKETCH_FOLDER + "=" + sketchPath.getParentFile().getAbsolutePath(),
- sketchPath.getName() // must be last argument
+ PApplet.ARGS_SKETCH_FOLDER + "=" + sketchPath.getParentFile().getAbsolutePath(),
+ sketchPath.getName() // must be last argument
};
}
@@ -188,5 +181,4 @@ public List getPathEntries() {
return entries;
}
-
}
diff --git a/runtime/src/jycessing/mode/FormatServer.java b/runtime/src/jycessing/mode/FormatServer.java
index 8401cfbb..e508de57 100644
--- a/runtime/src/jycessing/mode/FormatServer.java
+++ b/runtime/src/jycessing/mode/FormatServer.java
@@ -11,9 +11,9 @@
import processing.app.exec.StreamPump;
/**
- * This class manages running and communicating with a server that knows how to pretty-print
- * Python source code. The server is written in python. If a CPython interpreter is available,
- * we'll use it; otherwise we start up a Java processes using Jython to run the server.
+ * This class manages running and communicating with a server that knows how to pretty-print Python
+ * source code. The server is written in python. If a CPython interpreter is available, we'll use
+ * it; otherwise we start up a Java processes using Jython to run the server.
*/
public class FormatServer implements Formatter {
@@ -41,8 +41,9 @@ private static boolean nativePythonAvailable() {
/**
* If a python exectuable is available on this machine, use it to run the formatting server.
- * Otherwise, use the same Java that ran the PDE to interpret the formatting server with
- * Jython, which takes a very long time to start up.
+ * Otherwise, use the same Java that ran the PDE to interpret the formatting server with Jython,
+ * which takes a very long time to start up.
+ *
* @param formatServerPath Path to the format server script source.
* @return a ProcessBuilder that, when started, will run the formatting server.
*/
@@ -56,43 +57,40 @@ private ProcessBuilder getPythonProcess(final String formatServerPath) {
return new ProcessBuilder(Platform.getJavaPath(), "-jar", jython, formatServerPath);
}
- /**
- * Starts the formatting server.
- */
+ /** Starts the formatting server. */
public void start() {
started = true;
final String serverpy = new File(modeHome, "formatter/format_server.py").getAbsolutePath();
final ProcessBuilder pb = getPythonProcess(serverpy);
pb.redirectErrorStream(true);
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- log("Starting up the format server.");
- server = pb.start();
- Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
- @Override
- public void run() {
- started = false;
- shutdown();
- }
- }));
- new StreamPump(server.getInputStream(), "Format Server Output").addTarget(System.err)
- .start();
- } catch (final Exception e) {
- throw new RuntimeException(pb.toString(), e);
- }
- }
- }, "FormatServerStarter").start();
+ new Thread(
+ () -> {
+ try {
+ log("Starting up the format server.");
+ server = pb.start();
+ Runtime.getRuntime()
+ .addShutdownHook(
+ new Thread(
+ () -> {
+ started = false;
+ shutdown();
+ }));
+ new StreamPump(server.getInputStream(), "Format Server Output")
+ .addTarget(System.err)
+ .start();
+ } catch (final Exception e) {
+ throw new RuntimeException(pb.toString(), e);
+ }
+ },
+ "FormatServerStarter")
+ .start();
}
public boolean isStarted() {
return started;
}
- /**
- * Called as a ShutdownHook. Stops the formatting server.
- */
+ /** Called as a ShutdownHook. Stops the formatting server. */
public void shutdown() {
sendShutdown();
server.destroy();
@@ -104,7 +102,7 @@ public String format(final String text) {
// Connect to format server.
try (final Socket sock = new Socket("localhost", 10011);
final DataOutputStream out = new DataOutputStream(sock.getOutputStream());
- final DataInputStream in = new DataInputStream(sock.getInputStream());) {
+ final DataInputStream in = new DataInputStream(sock.getInputStream()); ) {
final byte[] encoded = text.getBytes("utf-8");
// Send big-endian encoded integer representing length of utf-8 encoded source code.
out.writeInt(encoded.length);
@@ -124,13 +122,11 @@ public String format(final String text) {
}
}
- /**
- * Tells the formatting server to shut down gracefully.
- */
+ /** Tells the formatting server to shut down gracefully. */
private void sendShutdown() {
log("Sending shutdown message to format server.");
try (final Socket sock = new Socket("localhost", 10011);
- final DataOutputStream out = new DataOutputStream(sock.getOutputStream());) {
+ final DataOutputStream out = new DataOutputStream(sock.getOutputStream()); ) {
// -1 is a sentinel value meaning "die".
out.writeInt(-1);
out.flush();
diff --git a/runtime/src/jycessing/mode/PyEditor.java b/runtime/src/jycessing/mode/PyEditor.java
index 18d466ae..7bd824b8 100644
--- a/runtime/src/jycessing/mode/PyEditor.java
+++ b/runtime/src/jycessing/mode/PyEditor.java
@@ -21,6 +21,7 @@
import jycessing.DisplayType;
import jycessing.IOUtil;
+import jycessing.jni.OSX;
import jycessing.mode.export.ExportDialog;
import jycessing.mode.run.PdeSketch;
import jycessing.mode.run.PdeSketch.LocationType;
@@ -30,16 +31,23 @@
import processing.app.Base;
import processing.app.Formatter;
import processing.app.Language;
+import processing.app.Library;
import processing.app.Messages;
import processing.app.Mode;
import processing.app.Platform;
+import processing.app.Preferences;
import processing.app.SketchCode;
import processing.app.SketchException;
+import processing.app.syntax.JEditTextArea;
+import processing.app.syntax.PdeTextArea;
+import processing.app.syntax.PdeTextAreaDefaults;
import processing.app.ui.Editor;
import processing.app.ui.EditorException;
import processing.app.ui.EditorState;
import processing.app.ui.EditorToolbar;
import processing.app.ui.Toolkit;
+import processing.core.PApplet;
+import processing.core.PConstants;
@SuppressWarnings("serial")
public class PyEditor extends Editor {
@@ -52,8 +60,8 @@ private static void log(final String msg) {
}
/**
- * Every PyEditor has a UUID that the {@link SketchServiceManager} uses to
- * route events from the {@link SketchService} to its owning editor.
+ * Every PyEditor has a UUID that the {@link SketchServiceManager} uses to route events from the
+ * {@link SketchService} to its owning editor.
*/
private final String id;
@@ -62,20 +70,19 @@ private static void log(final String msg) {
private final SketchServiceProcess sketchService;
/**
- * If the user runs a dirty sketch, we create a temp dir containing the
- * modified state of the sketch and run it from there. We keep track
- * of it in this variable in order to delete it when done running.
+ * If the user runs a dirty sketch, we create a temp dir containing the modified state of the
+ * sketch and run it from there. We keep track of it in this variable in order to delete it when
+ * done running.
*/
private Path tempSketch;
-
protected PyEditor(final Base base, final String path, final EditorState state, final Mode mode)
throws EditorException {
super(base, path, state, mode);
id = UUID.randomUUID().toString();
inputHandler = new PyInputHandler(this);
- pyMode = (PythonMode)mode;
+ pyMode = (PythonMode) mode;
// Provide horizontal scrolling.
textarea.addMouseWheelListener(createHorizontalScrollListener());
@@ -86,20 +93,22 @@ protected PyEditor(final Base base, final String path, final EditorState state,
// Ensure that the sketch service gets properly destroyed when either the
// JVM terminates or this editor closes, whichever comes first.
- final Thread cleanup = new Thread(new Runnable() {
- @Override
- public void run() {
- sketchServiceManager.destroySketchService(PyEditor.this);
- }
- });
+ final Thread cleanup =
+ new Thread(() -> sketchServiceManager.destroySketchService(PyEditor.this));
Runtime.getRuntime().addShutdownHook(cleanup);
- addWindowListener(new WindowAdapter() {
- @Override
- public void windowClosing(final WindowEvent e) {
- cleanup.run();
- Runtime.getRuntime().removeShutdownHook(cleanup);
- }
- });
+ addWindowListener(
+ new WindowAdapter() {
+ @Override
+ public void windowClosing(final WindowEvent e) {
+ cleanup.run();
+ Runtime.getRuntime().removeShutdownHook(cleanup);
+ }
+ });
+ }
+
+ @Override
+ protected JEditTextArea createTextArea() {
+ return new PdeTextArea(new PdeTextAreaDefaults(mode), new PyInputHandler(this), this);
}
public String getId() {
@@ -151,65 +160,98 @@ private void cleanupTempSketch() {
}
}
- /**
- * Build menus.
- */
+ /** Build menus. */
@Override
public JMenu buildFileMenu() {
- final String appTitle = Language.text("toolbar.export_application");
+ 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();
- }
- });
+ exportApplication.addActionListener(
+ new ActionListener() {
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ handleExportApplication();
+ }
+ });
return buildFileMenu(new JMenuItem[] {exportApplication});
}
@Override
public JMenu buildHelpMenu() {
- final JMenu menu = new JMenu("Help");
- menu.add(new JMenuItem(new AbstractAction("Report a bug in Python Mode") {
- @Override
- public void actionPerformed(final ActionEvent e) {
- Platform.openURL("http://github.com/jdf/processing.py-bugs/issues");
- }
- }));
- menu.add(new JMenuItem(new AbstractAction("Contribute to Python Mode") {
- @Override
- public void actionPerformed(final ActionEvent e) {
- Platform.openURL("http://github.com/jdf/processing.py");
- }
- }));
+ // I add a zero-width space to the end of the word "Help" in order to
+ // prevent OS X from auto-disabling everything in the menu when running
+ // a sketch.
+ final JMenu menu = new JMenu("Help\u200B");
+ menu.add(
+ new JMenuItem(
+ new AbstractAction("References") {
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ Platform.openURL("http://py.processing.org/reference/");
+ }
+ }));
+ menu.add(
+ new JMenuItem(
+ new AbstractAction("Tutorials") {
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ Platform.openURL("http://py.processing.org/tutorials/");
+ }
+ }));
+ menu.add(
+ new JMenuItem(
+ new AbstractAction("Examples") {
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ Platform.openURL("http://py.processing.org/examples/");
+ }
+ }));
+ menu.add(
+ new JMenuItem(
+ new AbstractAction("Report a bug in Python Mode") {
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ Platform.openURL("http://github.com/jdf/processing.py-bugs/issues");
+ }
+ }));
+ menu.add(
+ new JMenuItem(
+ new AbstractAction("Contribute to Python Mode") {
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ Platform.openURL("http://github.com/jdf/processing.py");
+ }
+ }));
return menu;
}
@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(
+ new ActionListener() {
+ @Override
+ public void actionPerformed(final ActionEvent 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(
+ new ActionListener() {
+ @Override
+ public void actionPerformed(final ActionEvent 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(
+ new ActionListener() {
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ handleStop();
+ }
+ });
return buildSketchMenu(new JMenuItem[] {runItem, presentItem, stopItem});
}
@@ -224,19 +266,21 @@ public EditorToolbar createToolbar() {
return new PyToolbar(this);
}
- /**
- * TODO(James Gilles): Create this!
- * Create export GUI and hand off results to performExport()
- */
+ /** TODO(James Gilles): Create this! Create export GUI and hand off results to performExport() */
public void handleExportApplication() {
// Leaving this here because it seems like it's more the editor's responsibility
if (sketch.isModified()) {
final Object[] options = {"OK", "Cancel"};
final int result =
- JOptionPane
- .showOptionDialog(this, "Save changes before export?", "Save",
- JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, options,
- options[0]);
+ JOptionPane.showOptionDialog(
+ this,
+ "Save changes before export?",
+ "Save",
+ JOptionPane.OK_CANCEL_OPTION,
+ JOptionPane.QUESTION_MESSAGE,
+ null,
+ options,
+ options[0]);
if (result == JOptionPane.OK_OPTION) {
handleSave(true);
} else {
@@ -256,10 +300,10 @@ public File getSplashFile() {
}
/**
- * Save the current state of the sketch code into a temp dir, and return
- * the created directory.
- * @return a new directory containing a saved version of the current
- * (presumably modified) sketch code.
+ * Save the current state of the sketch code into a temp dir, and return the created directory.
+ *
+ * @return a new directory containing a saved version of the current (presumably modified) sketch
+ * code.
* @throws IOException
*/
private Path createTempSketch() throws IOException {
@@ -281,8 +325,8 @@ private void runSketch(final DisplayType displayType) {
tempSketch = createTempSketch();
sketchPath = tempSketch.resolve(sketchMainFileName).toFile();
} catch (final IOException e) {
- Messages.showError("Sketchy Behavior", "I can't copy your unsaved work\n"
- + "to a temp directory.", e);
+ Messages.showError(
+ "Sketchy Behavior", "I can't copy your unsaved work\n" + "to a temp directory.", e);
return;
}
} else {
@@ -300,17 +344,25 @@ private void runSketch(final DisplayType displayType) {
}
try {
- sketchService
- .runSketch(new PdeSketch(sketch, sketchPath, displayType, location, locationType));
+ sketchService.runSketch(
+ new PdeSketch(sketch, sketchPath, displayType, location, locationType,
+ Preferences.get("run.present.bgcolor"), Preferences.get("run.present.stop.color")));
} catch (final SketchException e) {
statusError(e);
}
}
+ private void bringToFront() {
+ if (PApplet.platform == PConstants.MACOSX) {
+ OSX.bringToFront(pyMode);
+ }
+ }
+
@Override
public void deactivateRun() {
restoreToolbar();
cleanupTempSketch();
+ bringToFront();
}
public void handleRun() {
@@ -329,25 +381,15 @@ public void handleStop() {
}
private void restoreToolbar() {
- // toolbar.deactivate(PyToolbar.SAVE);
toolbar.deactivateStop();
toolbar.deactivateRun();
toFront();
}
- public void handleSave() {
- // toolbar.activate(PyToolbar.SAVE);
- super.handleSave(true);
- restoreToolbar();
- recolor();
- }
-
@Override
- public boolean handleSaveAs() {
- // toolbar.activate(PyToolbar.SAVE);
- final boolean result = super.handleSaveAs();
- restoreToolbar();
- return result;
+ public void handleSaveImpl() {
+ super.handleSaveImpl();
+ recolor();
}
@Override
@@ -357,11 +399,19 @@ public void statusError(final String what) { // sketch died for some reason
}
@Override
- public void handleImportLibrary(final String jarPath) {
+ public void handleImportLibrary(final String libraryName) {
sketch.ensureExistence();
- final String name = new File(jarPath).getParentFile().getParentFile().getName();
+
+ final Library lib = mode.findLibraryByName(libraryName);
+ if (lib == null) {
+ statusError("Unable to locate library: " + libraryName);
+ return;
+ }
+
+ final String name = new File(lib.getJarPath()).getParentFile().getParentFile().getName();
if (Pattern.compile("^add_library\\(\\s*'" + name + "'\\s*\\)\\s*$", Pattern.MULTILINE)
- .matcher(getText()).find()) {
+ .matcher(getText())
+ .find()) {
return;
}
setSelection(0, 0); // scroll to start
@@ -410,4 +460,10 @@ public void printOut(final String msg) {
public void printErr(final String msg) {
console.message(msg, true);
}
+
+ @Override
+ public void showReference(final String filename) {
+ Platform.openURL(
+ String.format("http://py.processing.org/reference/%s", filename.replace("_.", ".")));
+ }
}
diff --git a/runtime/src/jycessing/mode/PyInputHandler.java b/runtime/src/jycessing/mode/PyInputHandler.java
index 948b73ff..da5c6f9f 100644
--- a/runtime/src/jycessing/mode/PyInputHandler.java
+++ b/runtime/src/jycessing/mode/PyInputHandler.java
@@ -2,7 +2,6 @@
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
-import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Stack;
import java.util.regex.Matcher;
@@ -13,44 +12,36 @@
import processing.app.syntax.PdeInputHandler;
import processing.app.ui.Editor;
-/**
- * This class provides Pythonic handling of TAB, BACKSPACE, and ENTER keys.
- */
+/** This class provides Pythonic handling of keystrokes. */
public class PyInputHandler extends PdeInputHandler {
final PyEditor pyEditor;
- JEditTextArea textArea; // assigned on first key press
-
// ctrl-alt on windows & linux, cmd-alt on os x
- private static int CTRL_ALT = ActionEvent.ALT_MASK
- | Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
+ private static int CTRL_ALT =
+ ActionEvent.ALT_MASK | Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
// 4 spaces per pep8
private static final String TAB = " ";
private static final int TAB_SIZE = TAB.length();
public PyInputHandler(final Editor editor) {
+ super(editor);
pyEditor = (PyEditor)editor;
- textArea = pyEditor.getTextArea();
-
- // This is a somewhat gross "shim" keyListener.
- // The HandlePressed() Method was not properly overriding the
- // handlePressed() within pdeInputHandler.java so I circumvent the need for
- // the pde call by adding an additional keyListener here. It's in no way ideal,
- // but is a quick fix to the "No Returns or Tabs" bug in the new
- // python mode PDEX. You can find the pdeInputHandler.java at the url below.
- // https://github.com/processing/processing/blob/master/
- // app/src/processing/app/syntax/PdeInputHandler.java#L243
-
- textArea.addKeyListener(new KeyAdapter() {
- @Override
- public void keyPressed(final KeyEvent e) {
- if (handlePressed(e)) {
- e.consume();
- }
- }
- });
+ }
+
+ private static boolean isPrintableChar(final char c) {
+ if (c >= 32 && c <= 127) {
+ return true;
+ }
+ if (c == KeyEvent.CHAR_UNDEFINED || Character.isISOControl(c)) {
+ return false;
+ }
+ final Character.UnicodeBlock block = Character.UnicodeBlock.of(c);
+ return block != null && block != Character.UnicodeBlock.SPECIALS;
+ }
+ JEditTextArea getTextArea() {
+ return pyEditor.getTextArea();
}
@Override
@@ -59,14 +50,12 @@ public boolean handlePressed(final KeyEvent event) {
final int code = event.getKeyCode();
final int mods = event.getModifiers();
+ final JEditTextArea textArea = getTextArea();
final Sketch sketch = pyEditor.getSketch();
- if (textArea == null) {
- textArea = pyEditor.getTextArea();
- }
// things that change the content of the text area
- if ((code == KeyEvent.VK_BACK_SPACE) || (code == KeyEvent.VK_TAB)
- || (code == KeyEvent.VK_ENTER) || (mods == 0 && c >= 32 && c < 128)) {
+ if (!event.isMetaDown() && (code == KeyEvent.VK_BACK_SPACE || code == KeyEvent.VK_TAB
+ || code == KeyEvent.VK_ENTER || isPrintableChar(c))) {
sketch.setModified(true);
}
@@ -104,7 +93,8 @@ public boolean handlePressed(final KeyEvent event) {
}
final LineInfo currentLine = new LineInfo(thisLine);
if (currentLine.caretInText) {
- // The caret is in the text; let the text editor handle this backspace.
+ // The caret is in the text; let the text editor handle this
+ // backspace.
break;
}
// The caret is not in the text; treat it as a request to unindent.
@@ -129,14 +119,10 @@ public boolean handlePressed(final KeyEvent event) {
return false;
}
- /**
- * A line is some whitespace followed by a bunch of whatever.
- */
+ /** A line is some whitespace followed by a bunch of whatever. */
private static final Pattern LINE = Pattern.compile("^(\\s*)(.*)$");
- /**
- * Everything we need to know about a line in the text editor.
- */
+ /** Everything we need to know about a line in the text editor. */
private class LineInfo {
public final int lineNumber;
@@ -146,12 +132,14 @@ private class LineInfo {
// The text content after whatever indent.
public final String text;
- // Whether or not the caret happens to be positioned in the text portion of the line.
+ // Whether or not the caret happens to be positioned in the text portion
+ // of the line.
public final boolean caretInText;
LineInfo(final int lineNumber) {
this.lineNumber = lineNumber;
+ final JEditTextArea textArea = getTextArea();
final Matcher m = LINE.matcher(textArea.getLineText(lineNumber));
if (!m.matches()) {
throw new AssertionError("How can a line have less than nothing in it?");
@@ -161,12 +149,14 @@ private class LineInfo {
final int caretLinePos =
textArea.getCaretPosition() - textArea.getLineStartOffset(lineNumber);
caretInText = caretLinePos > space.length();
- // Calculate the current indent measured in tab stops of TAB_SIZE spaces.
+ // Calculate the current indent measured in tab stops of TAB_SIZE
+ // spaces.
int currentIndent = 0;
int spaceCounter = 0;
for (int i = 0; i < space.length(); i++) {
spaceCounter++;
- // A literal tab character advances to the next tab stop, as does the TAB_SIZEth space
+ // A literal tab character advances to the next tab stop, as
+ // does the TAB_SIZEth space
// character in a row.
if (spaceCounter % TAB_SIZE == 0 || space.charAt(i) == '\t') {
currentIndent++;
@@ -183,15 +173,24 @@ public String toString() {
}
/**
- * Maybe change the indent of the current selection. If sign is positive, then increase the
- * indent; otherwise, decrease it.
- * If the last non-comment, non-blank line ends with ":", then the maximum indent for the
- * current line is one greater than the indent of that ":"-bearing line. Otherwise, the maximum
- * indent is equal to the indent of the last non-comment line.
- *
The minimum indent is 0.
- * @param sign The direction in which to modify the indent of the current line.
+ * Maybe change the indent of the current selection. If sign is positive,
+ * then increase the indent; otherwise, decrease it.
+ *
+ *
+ * If the last non-comment, non-blank line ends with ":", then the maximum
+ * indent for the current line is one greater than the indent of that
+ * ":"-bearing line. Otherwise, the maximum indent is equal to the indent of
+ * the last non-comment line.
+ *
+ *
+ * The minimum indent is 0.
+ *
+ * @param sign The direction in which to modify the indent of the current
+ * line.
*/
public void indent(final int sign) {
+ final JEditTextArea textArea = getTextArea();
+
final int startLine = textArea.getSelectionStartLine();
final int stopLine = textArea.getSelectionStopLine();
final int selectionStart = textArea.getSelectionStart();
@@ -231,19 +230,23 @@ public void indent(final int sign) {
for (int i = startLine; i <= stopLine; i++) {
indentLineBy(i, deltaIndent);
}
- textArea.setSelectionStart(getAbsoluteCaretPositionRelativeToLineEnd(startLine,
- startLineEndRelativePos));
- textArea.setSelectionEnd(getAbsoluteCaretPositionRelativeToLineEnd(stopLine,
- stopLineEndRelativePos));
+ textArea.setSelectionStart(
+ getAbsoluteCaretPositionRelativeToLineEnd(startLine, startLineEndRelativePos));
+ textArea.setSelectionEnd(
+ getAbsoluteCaretPositionRelativeToLineEnd(stopLine, stopLineEndRelativePos));
}
private int getAbsoluteCaretPositionRelativeToLineEnd(final int line,
final int lineEndRelativePosition) {
+ final JEditTextArea textArea = getTextArea();
+
return Math.max(textArea.getLineStopOffset(line) - lineEndRelativePosition,
textArea.getLineStartOffset(line));
}
private void indentLineBy(final int line, final int deltaIndent) {
+ final JEditTextArea textArea = getTextArea();
+
final LineInfo currentLine = new LineInfo(line);
final int newIndent = Math.max(0, currentLine.indent + deltaIndent);
@@ -253,24 +256,14 @@ private void indentLineBy(final int line, final int deltaIndent) {
}
sb.append(currentLine.text);
- // Adjust for off by one error when de indenting allowing easier traversal through text area.
- if (deltaIndent < 0) {
- textArea.select(textArea.getLineStartOffset(line) + 1, textArea.getLineStopOffset(line) - 1);
- } else {
- textArea.select(textArea.getLineStartOffset(line), textArea.getLineStopOffset(line) - 1);
- }
-
-
- final String newLine = sb.toString();
- textArea.setSelectedText(newLine);
-
+ textArea.select(textArea.getLineStartOffset(line), textArea.getLineStopOffset(line) - 1);
+ textArea.setSelectedText(sb.toString());
textArea.selectNone();
}
private static final Pattern INITIAL_WHITESPACE = Pattern.compile("^(\\s*)");
/*
- * This can be fooled by a line like
- * print "He said: #LOLHASHTAG!"
+ * This can be fooled by a line like print "He said: #LOLHASHTAG!"
*/
private static final Pattern TERMINAL_COLON = Pattern.compile(":\\s*(#.*)?$");
private static final Pattern POP_CONTEXT = Pattern.compile("^\\s*(return|break|continue)\\b");
@@ -284,15 +277,22 @@ private boolean isClose(final char c) {
}
/**
- * Search for an unterminated paren or bracket. If found, return
- * its index in the given text. Otherwise return -1.
- *
Ignores syntax errors, treating (foo] as a valid construct.
- *
Assumes that the text contains no surrogate characters.
+ * Search for an unterminated paren or bracket. If found, return its index
+ * in the given text. Otherwise return -1.
+ *
+ *
+ * Ignores syntax errors, treating (foo] as a valid construct.
+ *
+ *
+ * Assumes that the text contains no surrogate characters.
+ *
* @param cursor The current cursor position in the given text.
* @param text The text to search for an unterminated paren or bracket.
* @return The index of the unterminated paren, or -1.
*/
private int indexOfUnclosedParen() {
+ final JEditTextArea textArea = getTextArea();
+
final int cursor = textArea.getCaretPosition();
final String text = textArea.getText();
final Stack stack = new Stack();
@@ -327,6 +327,7 @@ private String indentOf(final String line) {
}
private String getInitialWhitespace() {
+ final JEditTextArea textArea = getTextArea();
final String text = textArea.getText();
final int cursor = textArea.getCaretPosition();
final int lineNumber = textArea.getLineOfOffset(cursor);
@@ -372,6 +373,8 @@ private String getInitialWhitespace() {
}
private String newline() {
+ final JEditTextArea textArea = getTextArea();
+
final int cursor = textArea.getCaretPosition();
if (cursor <= 1) {
return "\n";
@@ -390,7 +393,8 @@ private String newline() {
if (TERMINAL_COLON.matcher(line).find()) {
return "\n" + initialWhitespace + TAB;
}
- // TODO: popping context on return should return to the indent of the last def.
+ // TODO: popping context on return should return to the indent of the
+ // last def.
if (POP_CONTEXT.matcher(line).find()) {
final int currentIndentLength = initialWhitespace.length();
final int spaceCount = Math.max(0, currentIndentLength - 4);
diff --git a/runtime/src/jycessing/mode/PyKeywordMap.java b/runtime/src/jycessing/mode/PyKeywordMap.java
index 685d2ea2..4875520e 100644
--- a/runtime/src/jycessing/mode/PyKeywordMap.java
+++ b/runtime/src/jycessing/mode/PyKeywordMap.java
@@ -7,17 +7,16 @@
public class PyKeywordMap {
private final Keyword[] map;
- /**
- * Creates a new KeywordMap.
- */
+ /** Creates a new KeywordMap. */
public PyKeywordMap() {
this(52);
}
/**
* Creates a new KeywordMap.
- * @param mapLength The number of `buckets' to create.
- * A value of 52 will give good performance for most maps.
+ *
+ * @param mapLength The number of `buckets' to create. A value of 52 will give good performance
+ * for most maps.
*/
public PyKeywordMap(final int mapLength) {
this.mapLength = mapLength;
@@ -26,6 +25,7 @@ public PyKeywordMap(final int mapLength) {
/**
* Looks up a key.
+ *
* @param text The text segment
* @param offset The offset of the substring within the text segment
* @param length The length of the substring
@@ -50,8 +50,8 @@ public byte lookup(final Segment text, final int offset, final int length) {
/**
* Adds a key-value mapping.
- * @param keyword The key
- * @Param id The value
+ *
+ * @param keyword The key @Param id The value
*/
public void add(final String keyword, final byte id) {
final int key = getStringMapKey(keyword);
@@ -83,5 +83,4 @@ public Keyword(final char[] keyword, final byte id, final Keyword next) {
public byte id;
public Keyword next;
}
-
}
diff --git a/runtime/src/jycessing/mode/PyToolbar.java b/runtime/src/jycessing/mode/PyToolbar.java
index d4d53f3c..dac5eeba 100644
--- a/runtime/src/jycessing/mode/PyToolbar.java
+++ b/runtime/src/jycessing/mode/PyToolbar.java
@@ -14,7 +14,7 @@ public PyToolbar(final Editor editor) {
@Override
public void handleRun(final int modifiers) {
- final PyEditor peditor = (PyEditor)editor;
+ final PyEditor peditor = (PyEditor) editor;
final boolean shift = (modifiers & InputEvent.SHIFT_MASK) != 0;
if (shift) {
peditor.handlePresent();
@@ -25,7 +25,7 @@ public void handleRun(final int modifiers) {
@Override
public void handleStop() {
- final PyEditor peditor = (PyEditor)editor;
+ final PyEditor peditor = (PyEditor) editor;
peditor.handleStop();
}
}
diff --git a/runtime/src/jycessing/mode/PythonMode.java b/runtime/src/jycessing/mode/PythonMode.java
index 4715a795..c994983a 100644
--- a/runtime/src/jycessing/mode/PythonMode.java
+++ b/runtime/src/jycessing/mode/PythonMode.java
@@ -19,29 +19,31 @@
public class PythonMode extends Mode {
/**
- * If the environment variable VERBOSE_PYTHON_MODE is equal to the string "true", then
- * many Python Mode operations will be logged to standard error.
+ * If the environment variable VERBOSE_PYTHON_MODE is equal to the string "true", then many Python
+ * Mode operations will be logged to standard error.
*/
public static final boolean VERBOSE = Boolean.parseBoolean(System.getenv("VERBOSE_PYTHON_MODE"));
/**
- * If the environment variable SKETCH_RUNNER_FIRST is equal to the string "true", then
- * {@link PythonMode} expects that the {@link SketchRunner} is already running and waiting
- * to be communicated with (as when you're debugging it in Eclipse, for example).
+ * If the environment variable SKETCH_RUNNER_FIRST is equal to the string "true", then {@link
+ * PythonMode} expects that the {@link SketchRunner} is already running and waiting to be
+ * communicated with (as when you're debugging it in Eclipse, for example).
*/
- public static final boolean SKETCH_RUNNER_FIRST = Boolean.parseBoolean(System
- .getenv("SKETCH_RUNNER_FIRST"));
+ public static final boolean SKETCH_RUNNER_FIRST =
+ Boolean.parseBoolean(System.getenv("SKETCH_RUNNER_FIRST"));
+
+ public static final String SITE_PACKAGES = "site-packages";
/**
- * Python auto-formatting is handled by a server. {@link FormatServer} handles
- * the lifecycle of, and communication with, that server.
+ * Python auto-formatting is handled by a server. {@link FormatServer} handles the lifecycle of,
+ * and communication with, that server.
*/
private final FormatServer formatServer;
/**
- * Sketches are run in external JVM processes. The {@link SketchServiceManager} handles
- * the creation and destruction of those processes, and routes communication between
- * {@link PyEditor}s and their affiliated {@link SketchService}s.
+ * Sketches are run in external JVM processes. The {@link SketchServiceManager} handles the
+ * creation and destruction of those processes, and routes communication between {@link PyEditor}s
+ * and their affiliated {@link SketchService}s.
*/
private final SketchServiceManager sketchServiceManager;
@@ -56,6 +58,20 @@ public PythonMode(final Base base, final File folder) {
* and not on any API. May break in the future.
*/
librariesFolder = Platform.getContentFile("modes/java/libraries");
+ final File pythonLibs = getSitePackages();
+ if (pythonLibs.exists()) {
+ if (!pythonLibs.isDirectory()) {
+ System.err.println(pythonLibs + " exists but is not directory");
+ }
+ } else {
+ if (!pythonLibs.mkdirs()) {
+ System.err.println("cannot create " + pythonLibs);
+ }
+ }
+ }
+
+ public static File getSitePackages() {
+ return new File(Base.getSketchbookLibrariesFolder(), SITE_PACKAGES);
}
@Override
@@ -74,10 +90,10 @@ public Editor createEditor(final Base base, final String path, final EditorState
sketchServiceManager.start();
}
- try {
+ try {
return new PyEditor(base, path, state, this);
- } catch(EditorException e) {
- Messages.showError("Editor Exception", "Issue Creating Editor", e);
+ } catch (final EditorException e) {
+ Messages.showError("Editor Exception", "Issue Creating Editor", e);
return null;
}
}
@@ -119,5 +135,4 @@ public TokenMarker createTokenMarker() {
public SketchServiceManager getSketchServiceManager() {
return sketchServiceManager;
}
-
}
diff --git a/runtime/src/jycessing/mode/PythonTokenMarker.java b/runtime/src/jycessing/mode/PythonTokenMarker.java
index 91eae7af..39c3fa94 100644
--- a/runtime/src/jycessing/mode/PythonTokenMarker.java
+++ b/runtime/src/jycessing/mode/PythonTokenMarker.java
@@ -37,7 +37,7 @@ public void addColoring(final String keyword, final String coloring) {
paren = true;
break;
}
- keywords.add(keyword, (byte)id);
+ keywords.add(keyword, (byte) id);
}
@Override
@@ -49,7 +49,8 @@ public byte markTokensImpl(byte token, final Segment line, final int lineIndex)
final int length = line.count + offset;
boolean backslash = false;
- loop: for (int i = offset; i < length; i++) {
+ loop:
+ for (int i = offset; i < length; i++) {
final int i1 = (i + 1);
final char c = array[i];
@@ -165,43 +166,73 @@ public byte markTokensImpl(byte token, final Segment line, final int lineIndex)
return token;
}
+ /** Constants (QUARTER_PI, CORNERS, etc.) */
+ private static final byte CONSTANT = Token.LITERAL2;
+
+ /** Keywords (void, int, boolean, etc.) */
+ private static final byte KEYWORD = Token.KEYWORD1;
+
+ /** Fields [variables within a class] */
+ private static final byte FIELD = Token.KEYWORD2;
+
+ /** Loop/function-like blocks (for, while, etc.) */
+ private static final byte LOOP_KEYWORD = Token.KEYWORD3;
+
+ /** Functions */
+ private static final byte FUNCTION = Token.FUNCTION1;
+
+ /** Methods (functions inside a class) */
+ private static final byte METHOD = Token.FUNCTION2;
+
+ /** Built-in Processing functions (setup, draw, mouseDragged). */
+ private static final byte BUILTIN_FUNCTION = Token.FUNCTION4;
+
+ /** Built-in Processing functions (setup, draw, mouseDragged). */
+ private static final byte OPERATOR = Token.OPERATOR;
+
public static PyKeywordMap getKeywords() {
if (pyKeywords == null) {
pyKeywords = new PyKeywordMap();
- pyKeywords.add("__init__", Token.FUNCTION2);
- pyKeywords.add("and", Token.KEYWORD3);
- pyKeywords.add("as", Token.KEYWORD3);
- pyKeywords.add("assert", Token.KEYWORD1);
- pyKeywords.add("break", Token.KEYWORD1);
- pyKeywords.add("chr", Token.FUNCTION1);
- pyKeywords.add("class", Token.KEYWORD2);
- pyKeywords.add("continue", Token.KEYWORD1);
- pyKeywords.add("def", Token.KEYWORD2);
- pyKeywords.add("del", Token.KEYWORD2);
- pyKeywords.add("elif", Token.KEYWORD1);
- pyKeywords.add("else", Token.KEYWORD1);
- pyKeywords.add("except", Token.KEYWORD1);
- pyKeywords.add("exec", Token.KEYWORD1);
- pyKeywords.add("finally", Token.KEYWORD1);
- pyKeywords.add("for", Token.KEYWORD3);
- pyKeywords.add("from", Token.KEYWORD2);
- pyKeywords.add("global", Token.KEYWORD2);
- pyKeywords.add("if", Token.KEYWORD1);
- pyKeywords.add("import", Token.KEYWORD2);
- pyKeywords.add("in", Token.KEYWORD2);
- pyKeywords.add("is", Token.KEYWORD2);
- pyKeywords.add("lambda", Token.KEYWORD2);
- pyKeywords.add("not", Token.KEYWORD3);
- pyKeywords.add("or", Token.KEYWORD3);
- pyKeywords.add("pass", Token.KEYWORD2);
- pyKeywords.add("print", Token.KEYWORD2);
- pyKeywords.add("raise", Token.KEYWORD1);
- pyKeywords.add("range", Token.KEYWORD3);
- pyKeywords.add("return", Token.KEYWORD1);
- pyKeywords.add("self", Token.KEYWORD2);
- pyKeywords.add("try", Token.KEYWORD1);
- pyKeywords.add("with", Token.KEYWORD3);
- pyKeywords.add("while", Token.KEYWORD3);
+ pyKeywords.add("__init__", METHOD);
+ pyKeywords.add("and", OPERATOR);
+ pyKeywords.add("as", LOOP_KEYWORD);
+ pyKeywords.add("assert", KEYWORD);
+ pyKeywords.add("break", KEYWORD);
+ pyKeywords.add("chr", FUNCTION);
+ pyKeywords.add("class", FIELD);
+ pyKeywords.add("continue", LOOP_KEYWORD);
+ pyKeywords.add("def", KEYWORD);
+ pyKeywords.add("del", KEYWORD);
+ pyKeywords.add("elif", LOOP_KEYWORD);
+ pyKeywords.add("else", LOOP_KEYWORD);
+ pyKeywords.add("except", KEYWORD);
+ pyKeywords.add("exec", FUNCTION);
+ pyKeywords.add("finally", LOOP_KEYWORD);
+ pyKeywords.add("for", LOOP_KEYWORD);
+ pyKeywords.add("from", FIELD);
+ pyKeywords.add("global", KEYWORD);
+ pyKeywords.add("if", LOOP_KEYWORD);
+ pyKeywords.add("import", KEYWORD);
+ pyKeywords.add("in", LOOP_KEYWORD);
+ pyKeywords.add("is", OPERATOR);
+ pyKeywords.add("lambda", OPERATOR);
+ pyKeywords.add("len", FUNCTION);
+ pyKeywords.add("not", OPERATOR);
+ pyKeywords.add("or", OPERATOR);
+ pyKeywords.add("pass", LOOP_KEYWORD);
+ pyKeywords.add("print", FUNCTION);
+ pyKeywords.add("raise", KEYWORD);
+ pyKeywords.add("range", LOOP_KEYWORD);
+ pyKeywords.add("return", KEYWORD);
+ pyKeywords.add("self", FIELD);
+ pyKeywords.add("try", LOOP_KEYWORD);
+ pyKeywords.add("with", LOOP_KEYWORD);
+ pyKeywords.add("while", LOOP_KEYWORD);
+
+ // These seem to be missing from Processing's token list?
+ pyKeywords.add("fullScreen", BUILTIN_FUNCTION);
+ pyKeywords.add("settings", BUILTIN_FUNCTION);
+ pyKeywords.add("SPAN", CONSTANT);
}
return pyKeywords;
}
diff --git a/runtime/src/jycessing/mode/SyntaxUtilities.java b/runtime/src/jycessing/mode/SyntaxUtilities.java
index 3bb0597f..7e90364d 100644
--- a/runtime/src/jycessing/mode/SyntaxUtilities.java
+++ b/runtime/src/jycessing/mode/SyntaxUtilities.java
@@ -5,8 +5,8 @@
public class SyntaxUtilities {
/**
- * Checks if a subregion of a Segment is equal to a
- * string.
+ * Checks if a subregion of a Segment is equal to a string.
+ *
* @param text The segment
* @param offset The offset into the segment
* @param match The string to match
@@ -26,8 +26,8 @@ public static boolean regionMatches(final Segment text, final int offset, final
}
/**
- * Checks if a subregion of a Segment is equal to a
- * character array.
+ * Checks if a subregion of a Segment is equal to a character array.
+ *
* @param text The segment
* @param offset The offset into the segment
* @param match The character array to match
@@ -45,5 +45,4 @@ public static boolean regionMatches(final Segment text, final int offset, final
}
return true;
}
-
}
diff --git a/runtime/src/jycessing/mode/export/Arch.java b/runtime/src/jycessing/mode/export/Arch.java
index 1839a77d..122cf781 100644
--- a/runtime/src/jycessing/mode/export/Arch.java
+++ b/runtime/src/jycessing/mode/export/Arch.java
@@ -1,13 +1,12 @@
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.)
- *
+ * 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);
+ X86(32),
+ AMD64(64);
public final int bits;
diff --git a/runtime/src/jycessing/mode/export/ExportDialog.java b/runtime/src/jycessing/mode/export/ExportDialog.java
index f8c07342..c92ec1aa 100644
--- a/runtime/src/jycessing/mode/export/ExportDialog.java
+++ b/runtime/src/jycessing/mode/export/ExportDialog.java
@@ -36,12 +36,10 @@
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.
- *
+ * 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 {
@@ -84,29 +82,35 @@ public ExportDialog(final PyEditor editor, final Sketch sketch) {
final String[] options = {"Export", "Cancel"};
optionPane =
- new JOptionPane(center, JOptionPane.PLAIN_MESSAGE, JOptionPane.YES_NO_OPTION, null,
- options, options[0]);
+ 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);
- }
- }
- });
+ 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);
-
+ this.setLocation(
+ bounds.x + (bounds.width - size.width) / 2, bounds.y + (bounds.height - size.height) / 2);
}
public void go() {
@@ -143,30 +147,34 @@ private JPanel createCenterPanel() {
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());
- }
- });
+ 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());
- }
- });
+ 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());
- }
- });
+ 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);
@@ -185,25 +193,27 @@ private JPanel createPresentPanel() {
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.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.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();
@@ -241,37 +251,47 @@ private JPanel createEmbedJavaPanel(final int divWidth) {
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. "
+ ""
+ + "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. " + "
";
+ + "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.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.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);
@@ -292,34 +312,40 @@ private JPanel createMacSigningWarning(final int divWidth) {
+ "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 "
+ 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. "
+ 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/");
+ 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()
- + "
");
+ 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/");
- }
- });
+ area.addMouseListener(
+ new MouseAdapter() {
+ @Override
+ public void mousePressed(final MouseEvent event) {
+ Platform.openURL("https://developer.apple.com/developer-id/");
+ }
+ });
return signPanel;
}
@@ -334,14 +360,15 @@ public ColorPreference(final String pref, final PyEditor editor) {
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();
- }
- });
+ 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
diff --git a/runtime/src/jycessing/mode/export/ExportedSketch.java b/runtime/src/jycessing/mode/export/ExportedSketch.java
index c3eea0e2..5b3fc216 100644
--- a/runtime/src/jycessing/mode/export/ExportedSketch.java
+++ b/runtime/src/jycessing/mode/export/ExportedSketch.java
@@ -14,11 +14,11 @@
import processing.core.PApplet;
/**
- * A sketch that's been exported from the PDE.
- * Runner.main() will create one of these if ARGS_EXPORTED is in argv.
- *
- * This class tries to make sure that the exported sketch is in the right configuration - if it isn't,
- * it will warn the user and fail.
+ * A sketch that's been exported from the PDE. Runner.main() will create one of these if
+ * ARGS_EXPORTED is in argv.
+ *
+ * This class tries to make sure that the exported sketch is in the right configuration - if it
+ * isn't, it will warn the user and fail.
*/
public class ExportedSketch implements RunnableSketch {
@@ -55,7 +55,7 @@ public ExportedSketch(final String[] args) throws Exception {
}
this.code = code.toString();
- if (Arrays.asList(args).contains("fullScreen")) {
+ if (Arrays.asList(args).contains(PApplet.ARGS_PRESENT)) {
this.displayType = DisplayType.PRESENTATION;
} else {
this.displayType = DisplayType.WINDOWED;
@@ -64,7 +64,7 @@ public ExportedSketch(final String[] args) throws Exception {
String backgroundColor = null;
String stopColor = null;
for (final String arg : args) {
- if (arg.contains("BGCOLOR")) {
+ if (arg.contains(PApplet.ARGS_WINDOW_COLOR)) {
backgroundColor = arg.substring(arg.indexOf("=") + 1);
} else if (arg.contains(PApplet.ARGS_STOP_COLOR)) {
stopColor = arg.substring(arg.indexOf("=") + 1);
@@ -106,8 +106,8 @@ public String[] getPAppletArguments() {
final List args = new ArrayList<>();
if (displayType == DisplayType.PRESENTATION) {
- args.add("fullScreen");
- args.add("BGCOLOR" + "=" + backgroundColor);
+ args.add(PApplet.ARGS_PRESENT);
+ args.add(PApplet.ARGS_WINDOW_COLOR + "=" + backgroundColor);
if (stopColor != null) {
args.add(PApplet.ARGS_STOP_COLOR + "=" + stopColor);
@@ -148,5 +148,4 @@ public List getPathEntries() {
return entries;
}
-
}
diff --git a/runtime/src/jycessing/mode/export/Exporter.java b/runtime/src/jycessing/mode/export/Exporter.java
index ba3cf4e0..b4386b36 100644
--- a/runtime/src/jycessing/mode/export/Exporter.java
+++ b/runtime/src/jycessing/mode/export/Exporter.java
@@ -10,12 +10,9 @@
import processing.app.Preferences;
import processing.app.Sketch;
-
/**
- *
- * Class that handles doing the actual exporting.
- * All this currently does is figure out libraries and then hand off to each platform export.
- *
+ * Class that handles doing the actual exporting. All this currently does is figure out libraries
+ * and then hand off to each platform export.
*/
public class Exporter {
@@ -29,6 +26,7 @@ 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;
@@ -39,7 +37,7 @@ private static void log(final String msg) {
private final Sketch sketch;
private final PyEditor editor; // I don't really want to pass this around but there's some
- // functionality
+ // functionality
// I need
diff --git a/runtime/src/jycessing/mode/export/ImportExtractor.java b/runtime/src/jycessing/mode/export/ImportExtractor.java
index e2702281..0f3281ff 100644
--- a/runtime/src/jycessing/mode/export/ImportExtractor.java
+++ b/runtime/src/jycessing/mode/export/ImportExtractor.java
@@ -5,14 +5,13 @@
import java.util.HashSet;
import java.util.Set;
-import jycessing.mode.PythonMode;
-
import org.python.antlr.ast.Call;
import org.python.antlr.ast.Str;
import org.python.antlr.base.mod;
import org.python.core.CompilerFlags;
import org.python.core.ParserFacade;
+import jycessing.mode.PythonMode;
import processing.app.Base;
import processing.app.Library;
import processing.app.Messages;
@@ -21,13 +20,12 @@
import processing.app.SketchCode;
/**
- * Parses pyde source files using jython's ANTLR parser, goes through all
- * function calls with a Visitor, finds all calls to add_library, and finds
- * the libraries that are being imported. This WON'T WORK if add_library
- * is reassigned or passed not-a-string. We currently only warn the user
+ * Parses pyde source files using jython's ANTLR parser, goes through all function calls with a
+ * Visitor, finds all calls to add_library, and finds the libraries that are being imported. This
+ * WON'T WORK if add_library is reassigned or passed not-a-string. We currently only warn the user
* if they don't pass a string.
- *
- * TODO More stringent warnings.
+ *
+ * TODO More stringent warnings.
*/
public class ImportExtractor {
@SuppressWarnings("unused")
@@ -37,8 +35,10 @@ private static void log(final String msg) {
}
}
- private final static File[] libLocations = new File[] {
- Platform.getContentFile("modes/java/libraries"), Base.getSketchbookLibrariesFolder()};
+ private static final File[] libLocations =
+ new File[] {
+ Platform.getContentFile("modes/java/libraries"), Base.getSketchbookLibrariesFolder()
+ };
private final Sketch sketch;
private final Set libraries;
@@ -60,8 +60,8 @@ private void extract() {
final mod ast;
try {
ast =
- ParserFacade.parseExpressionOrModule(new StringReader(code.getProgram()),
- code.getFileName(), new CompilerFlags());
+ ParserFacade.parseExpressionOrModule(
+ new StringReader(code.getProgram()), code.getFileName(), new CompilerFlags());
} catch (final Exception e) {
System.err.println("Couldn't parse " + code.getFileName());
// I don't like this but I'm not sure what else to do
@@ -77,7 +77,8 @@ private void extract() {
}
}
if (visitor.failure) {
- Messages.showWarning("Library Problems",
+ Messages.showWarning(
+ "Library Problems",
"I can't figure out all of the java libraries you're using. "
+ "Your exported sketch might not work.");
}
@@ -104,7 +105,6 @@ private void extract() {
}
}
-
private static class ImportVisitor extends org.python.antlr.Visitor {
public final Set importNames;
public boolean failure;
@@ -123,8 +123,8 @@ public Object visitCall(final Call funcall) throws Exception {
importNames.add(lib);
log("Found library: " + lib);
} else {
- System.err
- .println("I can't figure out what libraries you're using if you don't pass a string to add_library.\n"
+ System.err.println(
+ "I can't figure out what libraries you're using if you don't pass a string to add_library.\n"
+ "Please replace "
+ funcall.getInternalArgs().get(0).getToken().getText()
+ " with the name of the library you're importing.");
@@ -134,5 +134,4 @@ public Object visitCall(final Call funcall) throws Exception {
return super.visitCall(funcall);
}
}
-
}
diff --git a/runtime/src/jycessing/mode/export/LinuxExport.java b/runtime/src/jycessing/mode/export/LinuxExport.java
index 18195ca5..0450f273 100644
--- a/runtime/src/jycessing/mode/export/LinuxExport.java
+++ b/runtime/src/jycessing/mode/export/LinuxExport.java
@@ -20,26 +20,15 @@
import processing.core.PApplet;
import processing.core.PConstants;
-
-
/**
- *
- * Performs an export to Linux (32/64).
- * The linux export folder layout is as follows:
- *
- * $appdir/ (e.g. $sketchname/application.linux32)
- * /$sketchname (executable shell script to run the application)
- * /source/ (the source code of the sketch; used to run it)
- * /lib/ (where java imports are stored)
- * /jycessing/ (where stuff necessary to jycessing is stored;
- * everything in here is added to the classpath.)
- * /$libname/library/ (where resources for $libname - imported with
- * add_library - are stored. Not added to classpath.)
- * /code/ (any other code resources the user wanted to add;
- * copied verbatim.)
- * /data/ (all non-code resources; copied verbatim.)
- *
+ * Performs an export to Linux (32/64). The linux export folder layout is as follows:
*
+ * $appdir/ (e.g. $sketchname/application.linux32) /$sketchname (executable shell script to run
+ * the application) /source/ (the source code of the sketch; used to run it) /lib/ (where java
+ * imports are stored) /jycessing/ (where stuff necessary to jycessing is stored; everything in here
+ * is added to the classpath.) /$libname/library/ (where resources for $libname - imported with
+ * add_library - are stored. Not added to classpath.) /code/ (any other code resources the user
+ * wanted to add; copied verbatim.) /data/ (all non-code resources; copied verbatim.)
*/
public class LinuxExport extends PlatformExport {
@@ -52,8 +41,8 @@ protected void log(final String msg) {
}
}
- public LinuxExport(final Arch arch, final Sketch sketch, final PyEditor editor,
- final Set libraries) {
+ 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;
@@ -66,7 +55,8 @@ public LinuxExport(final Arch arch, final Sketch sketch, final PyEditor editor,
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")
+ (id == PApplet.platform)
+ && Preferences.getBoolean("export.application.embed_java")
&& arch == Exporter.processingArch;
// Work out the folders we'll be (maybe) using
@@ -90,7 +80,6 @@ public void export() throws IOException {
private void buildShellScript(final File destFolder, final boolean embedJava) throws IOException {
log("Creating shell script.");
-
final boolean setMemory = Preferences.getBoolean("run.options.memory");
final boolean presentMode = Preferences.getBoolean("export.application.fullscreen");
final boolean stopButton = Preferences.getBoolean("export.application.stop") && presentMode;
@@ -131,7 +120,8 @@ private void buildShellScript(final File destFolder, final boolean embedJava) th
// Work out classpath - only add core stuff, the rest will be found by add_library
final StringWriter classpath = new StringWriter();
for (final File f : jycessingFolder.listFiles()) {
- if (f.getName().toLowerCase().endsWith(".jar") || f.getName().toLowerCase().endsWith(".zip")) {
+ if (f.getName().toLowerCase().endsWith(".jar")
+ || f.getName().toLowerCase().endsWith(".zip")) {
classpath.append("$APPDIR/lib/jycessing/" + f.getName() + ":");
}
}
@@ -153,9 +143,9 @@ private void buildShellScript(final File destFolder, final boolean embedJava) th
options.add("--exported");
if (presentMode) {
- options.add("fullScreen");
+ options.add(PApplet.ARGS_PRESENT);
- options.add("BGCOLOR" + "=" + Preferences.get("run.present.bgcolor"));
+ options.add(PApplet.ARGS_WINDOW_COLOR + "=" + Preferences.get("run.present.bgcolor"));
}
if (stopButton) {
@@ -175,8 +165,8 @@ private void buildShellScript(final File destFolder, final boolean embedJava) th
log("Setting script executable.");
try {
- Files.setPosixFilePermissions(scriptFile.toPath(),
- PosixFilePermissions.fromString("rwxrwxrwx"));
+ Files.setPosixFilePermissions(
+ scriptFile.toPath(), PosixFilePermissions.fromString("rwxrwxrwx"));
} catch (final UnsupportedOperationException e) {
// Windows, probably
log("Couldn't set script executable... we'll assume whoever gets it can handle it");
diff --git a/runtime/src/jycessing/mode/export/MacExport.java b/runtime/src/jycessing/mode/export/MacExport.java
index 437969ed..948370e0 100644
--- a/runtime/src/jycessing/mode/export/MacExport.java
+++ b/runtime/src/jycessing/mode/export/MacExport.java
@@ -25,36 +25,21 @@
import processing.core.PApplet;
import processing.core.PConstants;
-
/**
- *
* A Mac export.
- *
- * If we embed java, we embed Processing's java, since we know it's been properly appbundled
- * and all symlinks work and stuff.
- *
- * N.B. We use bash for the executable, so that we can easily include a pile of command line arguments,
- * as well as being able to prompt the user to install Java.
*
- * Inspired by: https://github.com/tofi86/universalJavaApplicationStub
- *
- * Layout:
- * $appdir/
- * /$sketch.app/Contents/
- * /MacOS/
- * /$sketch (shell script that cd's to ../Processing and runs the sketch)
- * /Info.plist
- * /Resources/
- * /sketch.icns (pretty icon)
- * /dialogs.applescript (used by main script to show native prompts)
- * /Processing/
- * /source/
- * /lib/
- * /code/
- * /data/
- * /PlugIns/jdk$version.jdk/ (copied from core processing)
- *
+ * If we embed java, we embed Processing's java, since we know it's been properly appbundled and
+ * all symlinks work and stuff.
+ *
+ *
N.B. We use bash for the executable, so that we can easily include a pile of command line
+ * arguments, as well as being able to prompt the user to install Java.
+ *
+ *
Inspired by: https://github.com/tofi86/universalJavaApplicationStub
*
+ *
Layout: $appdir/ /$sketch.app/Contents/ /MacOS/ /$sketch (shell script that cd's to
+ * ../Processing and runs the sketch) /Info.plist /Resources/ /sketch.icns (pretty icon)
+ * /dialogs.applescript (used by main script to show native prompts) /Processing/ /source/ /lib/
+ * /code/ /data/ /PlugIns/jdk$version.jdk/ (copied from core processing)
*/
public class MacExport extends PlatformExport {
@@ -105,17 +90,21 @@ public void export() throws IOException {
/**
* Copy Processing's builtin JDK to the export.
- *
- * (This only makes sense when we're on a Mac, running Processing from a .app bundle.)
+ *
+ *
(This only makes sense when we're on a Mac, running Processing from a .app bundle.)
*/
private void copyJDKPlugin(final File targetPluginsFolder) throws IOException {
// This is how Java Mode finds it... basically
- final File sourceJDKFolder = Platform.getContentFile("../PlugIns").listFiles(new FilenameFilter() {
- @Override
- public boolean accept(final File dir, final String name) {
- return name.endsWith(".jdk") && !name.startsWith(".");
- }
- })[0].getAbsoluteFile();
+ final File sourceJDKFolder =
+ Platform.getContentFile("../PlugIns")
+ .listFiles(
+ new FilenameFilter() {
+ @Override
+ public boolean accept(final File dir, final String name) {
+ return name.endsWith(".jdk") && !name.startsWith(".");
+ }
+ })[0]
+ .getAbsoluteFile();
log("Copying JDK from " + sourceJDKFolder);
@@ -124,12 +113,9 @@ public boolean accept(final File dir, final String name) {
Util.copyDirNative(sourceJDKFolder, targetJDKFolder);
}
+ private static final Pattern sketchPattern = Pattern.compile("@@sketch@@");
- private final static Pattern sketchPattern = Pattern.compile("@@sketch@@");
-
- /**
- * Read in Info.plist.tmpl, modify fields, package away in .app
- */
+ /** Read in Info.plist.tmpl, modify fields, package away in .app */
private void setUpInfoPlist(final File targetInfoPlist, final String sketchName)
throws IOException {
log("Setting up Info.plist.");
@@ -139,29 +125,36 @@ private void setUpInfoPlist(final File targetInfoPlist, final String sketchName)
editor.getModeContentFile("application/macosx/Info.plist.tmpl").toPath();
final String infoPlistTemplateText = new String(Files.readAllBytes(infoPlistTemplate), "UTF-8");
final Matcher sketchNameMatcher = sketchPattern.matcher(infoPlistTemplateText);
- Files.write(targetInfoPlist.toPath(), sketchNameMatcher.replaceAll(sketchName).getBytes(),
- StandardOpenOption.WRITE, StandardOpenOption.CREATE);
+ Files.write(
+ targetInfoPlist.toPath(),
+ sketchNameMatcher.replaceAll(sketchName).getBytes(),
+ StandardOpenOption.WRITE,
+ StandardOpenOption.CREATE);
}
- /**
- * Copy things that don't change between runs.
- */
+ /** Copy things that don't change between runs. */
private void copyStaticResources(final File resourcesFolder) throws IOException {
log("Moving static macosx resources.");
final File osxFolder = editor.getModeContentFile("application/macosx");
resourcesFolder.mkdirs();
Util.copyFile(new File(osxFolder, "sketch.icns"), new File(resourcesFolder, "sketch.icns"));
- Util.copyFile(new File(osxFolder, "dialogs.applescript"), new File(resourcesFolder,
- "dialogs.applescript"));
+ Util.copyFile(
+ new File(osxFolder, "dialogs.applescript"),
+ new File(resourcesFolder, "dialogs.applescript"));
}
/**
* Create the shell script that will run the sketch.
- *
- * Some duplicated code here from LinuxExport, but there's enough differences that I want to keep them separate
+ *
+ *
Some duplicated code here from LinuxExport, but there's enough differences that I want to
+ * keep them separate
*/
- private void setUpExecutable(final File binFolder, final File processingFolder,
- final String sketchName, final boolean embedJava) throws IOException {
+ private void setUpExecutable(
+ final File binFolder,
+ final File processingFolder,
+ final String sketchName,
+ final boolean embedJava)
+ throws IOException {
log("Creating shell script.");
@@ -174,7 +167,6 @@ private void setUpExecutable(final File binFolder, final File processingFolder,
final File scriptFile = new File(binFolder, sketch.getName());
final PrintWriter script = new PrintWriter(scriptFile);
-
// We explicitly use "\n" because PrintWriter.println() uses the system line ending,
// which will confuse Macs if we're running from Windows.
script.print("#!/bin/bash\n");
@@ -188,8 +180,8 @@ private void setUpExecutable(final File binFolder, final File processingFolder,
new String(Files.readAllBytes(findJavaScript), "UTF-8").replaceAll("\\r\\n?", "\n");
script.print(findJava + "\n");
} else {
- script
- .print("JAVA=\"$(find $CONTENTS/PlugIns -maxdepth 1 -type d -name '*jdk')/Contents/Home/jre/bin/java\""
+ script.print(
+ "JAVA=\"$(find $CONTENTS/PlugIns -maxdepth 1 -type d -name '*jdk')/Contents/Home/jre/bin/java\""
+ "\n");
}
@@ -215,7 +207,8 @@ private void setUpExecutable(final File binFolder, final File processingFolder,
// Work out classpath - only add core stuff, the rest will be found by add_library
final StringWriter classpath = new StringWriter();
for (final File f : new File(processingFolder, "lib/jycessing").listFiles()) {
- if (f.getName().toLowerCase().endsWith(".jar") || f.getName().toLowerCase().endsWith(".zip")) {
+ if (f.getName().toLowerCase().endsWith(".jar")
+ || f.getName().toLowerCase().endsWith(".zip")) {
classpath.append("$APPDIR/lib/jycessing/" + f.getName() + ":");
}
}
@@ -248,9 +241,9 @@ private void setUpExecutable(final File binFolder, final File processingFolder,
options.add("--exported");
if (presentMode) {
- options.add("fullScreen");
+ options.add(PApplet.ARGS_PRESENT);
- options.add("BGCOLOR" + "=" + Preferences.get("run.present.bgcolor"));
+ options.add(PApplet.ARGS_WINDOW_COLOR + "=" + Preferences.get("run.present.bgcolor"));
}
if (stopButton) {
@@ -270,8 +263,8 @@ private void setUpExecutable(final File binFolder, final File processingFolder,
log("Setting script executable.");
try {
- Files.setPosixFilePermissions(scriptFile.toPath(),
- PosixFilePermissions.fromString("rwxrwxrwx"));
+ Files.setPosixFilePermissions(
+ scriptFile.toPath(), PosixFilePermissions.fromString("rwxrwxrwx"));
} catch (final UnsupportedOperationException e) {
// Windows, probably
log("Couldn't set script executable... .app should work anyway, though");
diff --git a/runtime/src/jycessing/mode/export/PlatformExport.java b/runtime/src/jycessing/mode/export/PlatformExport.java
index 8440197d..036dd17b 100644
--- a/runtime/src/jycessing/mode/export/PlatformExport.java
+++ b/runtime/src/jycessing/mode/export/PlatformExport.java
@@ -12,11 +12,7 @@
import processing.app.SketchCode;
import processing.app.Util;
-/**
- *
- * Subclass this to add more platforms, if we ever want to add freebsd or haiku or something
- *
- */
+/** 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;
@@ -25,17 +21,12 @@ public abstract class PlatformExport {
protected Set libraries;
protected Arch arch;
- /**
- * Instance so that subclasses can override it.
- */
+ /** Instance so that subclasses can override it. */
protected abstract void log(final String msg);
public abstract void export() throws IOException;
- /**
- * This is the same between platforms.
- *
- */
+ /** This is the same between platforms. */
public void copyBasicStructure(final File destFolder) throws IOException {
final boolean hasData = sketch.hasDataFolder();
final boolean hasCode = sketch.hasCodeFolder();
@@ -85,12 +76,17 @@ 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(id, Integer.toString(arch.bits))) {
log("Exporting: " + exportFile);
final String exportName = exportFile.getName();
if (!exportFile.exists()) {
- System.err.println("The file " + exportName + " is mentioned in the export.txt from "
- + library + " but does not actually exist. Moving on.");
+ System.err.println(
+ "The file "
+ + exportName
+ + " is mentioned in the export.txt from "
+ + library
+ + " but does not actually exist. Moving on.");
continue;
}
if (exportFile.isDirectory()) {
@@ -106,8 +102,9 @@ public void copyBasicStructure(final File destFolder) throws IOException {
{
jycessingFolder.mkdirs();
log("Copying core processing stuff to export");
- for (final File exportFile : new Library(Platform.getContentFile("core")).getApplicationExports(
- id, Integer.toString(arch.bits))) {
+ for (final File exportFile :
+ new Library(Platform.getContentFile("core"))
+ .getApplicationExports(id, Integer.toString(arch.bits))) {
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 684c9ea0..5aa2fd96 100644
--- a/runtime/src/jycessing/mode/export/WindowsExport.java
+++ b/runtime/src/jycessing/mode/export/WindowsExport.java
@@ -21,30 +21,22 @@
import processing.data.XML;
/**
- *
* Perform an export to Windows, using launch4j to generate the Windows executable.
- *
- * N.B. We use relative paths for everything. This is not laziness, this solves problems:
- * - It allows us to specify the location of the sketch consistently
- * - It prevents parent directories with special characters from confusing java / JOGL (which loads libs dynamically)
- * - It is guaranteed to work, since we specify working directory with
- *
- * N.B. We don't use launch4j's splash screen functionality because it apparently only works with a very specific breed of 24-bit BMPs.
- * Java's works just as well.
- *
- * Structure:
- * $appdir/ (e.g. $sketchname/application.windows32)
- * /$sketchname.exe (executable shell script to run the application)
- * /source/ (the source code of the sketch; used to run it)
- * /lib/ (where java imports are stored)
- * /jycessing/ (where stuff necessary to jycessing is stored;
- * everything in here is added to the classpath.)
- * /$libname/library/ (where resources for $libname - imported with
- * add_library - are stored. Not added to classpath.)
- * /code/ (any other code resources the user wanted to add;
- * copied verbatim.)
- * /data/ (all non-code resources; copied verbatim.)
*
+ * N.B. We use relative paths for everything. This is not laziness, this solves problems: - It
+ * allows us to specify the location of the sketch consistently - It prevents parent directories
+ * with special characters from confusing java / JOGL (which loads libs dynamically) - It is
+ * guaranteed to work, since we specify working directory with
+ *
+ *
N.B. We don't use launch4j's splash screen functionality because it apparently only works with
+ * a very specific breed of 24-bit BMPs. Java's works just as well.
+ *
+ *
Structure: $appdir/ (e.g. $sketchname/application.windows32) /$sketchname.exe (executable
+ * shell script to run the application) /source/ (the source code of the sketch; used to run it)
+ * /lib/ (where java imports are stored) /jycessing/ (where stuff necessary to jycessing is stored;
+ * everything in here is added to the classpath.) /$libname/library/ (where resources for $libname -
+ * imported with add_library - are stored. Not added to classpath.) /code/ (any other code resources
+ * the user wanted to add; copied verbatim.) /data/ (all non-code resources; copied verbatim.)
*/
public class WindowsExport extends PlatformExport {
@@ -55,8 +47,8 @@ protected void log(final String msg) {
}
}
- public WindowsExport(final Arch arch, final Sketch sketch, final PyEditor editor,
- final Set libraries) {
+ 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;
@@ -68,7 +60,8 @@ public WindowsExport(final Arch arch, final Sketch sketch, final PyEditor editor
@Override
public void export() throws IOException {
final boolean embedJava =
- (id == PApplet.platform) && Preferences.getBoolean("export.application.embed_java")
+ (id == PApplet.platform)
+ && Preferences.getBoolean("export.application.embed_java")
&& arch == Exporter.processingArch;
final File destFolder = new File(sketch.getFolder(), "application." + name);
@@ -94,8 +87,8 @@ public void export() throws IOException {
}
/**
- * Construct a Processing XML object containing the configuration for launch4j.
- * Config file docs: http://launch4j.sourceforge.net/docs.html
+ * Construct a Processing XML object containing the configuration for launch4j. Config file docs:
+ * http://launch4j.sourceforge.net/docs.html
*/
private XML buildLaunch4jConfig(final File destFolder, final boolean embedJava) {
log("Building launch4j configuration.");
@@ -109,8 +102,9 @@ private XML buildLaunch4jConfig(final File destFolder, final boolean embedJava)
final XML config = new XML("launch4jConfig");
config.addChild("headerType").setContent("gui"); // Not a console application
- config.addChild("outfile").setContent(
- new File(destFolder, sketchName + ".exe").getAbsolutePath());
+ config
+ .addChild("outfile")
+ .setContent(new File(destFolder, sketchName + ".exe").getAbsolutePath());
config.addChild("dontWrapJar").setContent("true"); // We just want a launcher
config.addChild("errTitle").setContent("Sketchy Behavior");
config.addChild("icon").setContent(iconFile.getAbsolutePath());
@@ -123,9 +117,7 @@ private XML buildLaunch4jConfig(final File destFolder, final boolean embedJava)
return config;
}
- /**
- * Run launch4j on a given configuration file
- */
+ /** Run launch4j on a given configuration file */
private void runLaunch4j(final File configFile) throws IOException {
log("Running launch4j.");
@@ -150,9 +142,14 @@ private void runLaunch4j(final File configFile) throws IOException {
final File xstreamJar = new File(launch4jFolder, "lib/xstream.jar");
final ProcessBuilder pb =
- new ProcessBuilder(javaExecutable.getAbsolutePath(), "-cp", launch4jJar.getAbsolutePath()
- + System.getProperty("path.separator") + xstreamJar.getAbsolutePath(),
- "net.sf.launch4j.Main", configFile.getAbsolutePath());
+ new ProcessBuilder(
+ javaExecutable.getAbsolutePath(),
+ "-cp",
+ launch4jJar.getAbsolutePath()
+ + System.getProperty("path.separator")
+ + xstreamJar.getAbsolutePath(),
+ "net.sf.launch4j.Main",
+ configFile.getAbsolutePath());
if (PythonMode.VERBOSE) {
log("Launch4j command:");
@@ -164,20 +161,20 @@ private void runLaunch4j(final File configFile) throws IOException {
final Process launch4jProcess = pb.start();
if (PythonMode.VERBOSE) {
- final Thread captureOutput = new Thread(new Runnable() {
- @Override
- public void run() {
- final BufferedReader stderr =
- new BufferedReader(new InputStreamReader(launch4jProcess.getInputStream()));
- String line;
- try {
- while ((line = stderr.readLine()) != null) {
- log(line);
- }
- } catch (final Exception e) {
- }
- }
- });
+ final Thread captureOutput =
+ new Thread(
+ () -> {
+ final BufferedReader stderr =
+ new BufferedReader(new InputStreamReader(launch4jProcess.getInputStream()));
+ String line;
+ try {
+ while ((line = stderr.readLine()) != null) {
+ log(line);
+ }
+ stderr.close();
+ } catch (final Exception e) {
+ }
+ });
captureOutput.start();
}
@@ -197,7 +194,7 @@ private XML buildJREOptions(final boolean embedJava, final boolean setMemory, fi
if (embedJava) {
// note that "Path" is relative to the output executable at runtime
jre.addChild("path").setContent("\"%EXEDIR%\\java\""); // "java" folder is next to the
- // executable?
+ // executable?
// TODO check
}
// We always add the minVersion tag, which means that the sketch will always try to look for
@@ -240,8 +237,8 @@ private XML buildRunnerOptions(final boolean presentMode, final boolean stopButt
// Options to pass to Runner
final List runnerOptions = new ArrayList<>();
if (presentMode) {
- runnerOptions.add("fullScreen");
- runnerOptions.add("BGCOLOR" + "=" + Preferences.get("run.present.bgcolor"));
+ runnerOptions.add(PApplet.ARGS_PRESENT);
+ runnerOptions.add(PApplet.ARGS_WINDOW_COLOR + "=" + Preferences.get("run.present.bgcolor"));
}
if (stopButton) {
runnerOptions.add(PApplet.ARGS_STOP_COLOR + "=" + Preferences.get("run.present.stop.color"));
@@ -268,7 +265,8 @@ private XML buildClassPathOptions(final File jycessingFolder) {
final XML classPathOptions = new XML("classPath");
classPathOptions.addChild("mainClass").setContent("jycessing.Runner");
for (final File f : jycessingFolder.listFiles()) {
- if (f.getName().toLowerCase().endsWith(".jar") || f.getName().toLowerCase().endsWith(".zip")) {
+ if (f.getName().toLowerCase().endsWith(".jar")
+ || f.getName().toLowerCase().endsWith(".zip")) {
// Don't need to quote classpath entries, launch4j at least handles that for us
classPathOptions.addChild("cp").setContent("./lib/jycessing/" + f.getName());
}
diff --git a/runtime/src/jycessing/mode/run/OSXAdapter.java b/runtime/src/jycessing/mode/run/OSXAdapter.java
index 90ba3679..fd84d349 100644
--- a/runtime/src/jycessing/mode/run/OSXAdapter.java
+++ b/runtime/src/jycessing/mode/run/OSXAdapter.java
@@ -6,16 +6,16 @@
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
+ 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
+ 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
+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
@@ -29,8 +29,8 @@
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.
+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
@@ -62,7 +62,6 @@ AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
-
public class OSXAdapter implements InvocationHandler {
protected Object targetObject;
@@ -88,8 +87,9 @@ public static void setAboutHandler(final Object target, final Method aboutHandle
// com.apple.eawt.Application reflectively
try {
final Method enableAboutMethod =
- macOSXApplication.getClass().getDeclaredMethod("setEnabledAboutMenu",
- new Class[] {boolean.class});
+ 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");
@@ -108,8 +108,9 @@ public static void setPreferencesHandler(final Object target, final Method prefs
// com.apple.eawt.Application reflectively
try {
final Method enablePrefsMethod =
- macOSXApplication.getClass().getDeclaredMethod("setEnabledPreferencesMenu",
- new Class[] {boolean.class});
+ 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);
@@ -120,24 +121,26 @@ public static void setPreferencesHandler(final Object target, final Method prefs
// 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) {
-
+ 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;
}
- }
- return true;
- }
- });
+ });
}
// setHandler creates a Proxy object from the passed OSXAdapter and adds it as an
@@ -147,22 +150,23 @@ public static void setHandler(final OSXAdapter adapter) {
final Class> applicationClass = Class.forName("com.apple.eawt.Application");
if (macOSXApplication == null) {
macOSXApplication =
- applicationClass.getConstructor((Class[])null).newInstance((Object[])null);
+ 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});
+ 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);
+ 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 + ")");
+ 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);
@@ -181,9 +185,9 @@ protected OSXAdapter(final String proxySignature, final Object target, final Met
// 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);
+ public boolean callTarget(final Object appleEvent)
+ throws InvocationTargetException, IllegalAccessException {
+ final Object result = targetMethod.invoke(targetObject, (Object[]) null);
if (result == null) {
return true;
}
diff --git a/runtime/src/jycessing/mode/run/PdeSketch.java b/runtime/src/jycessing/mode/run/PdeSketch.java
index 7e2da531..7bfd808f 100644
--- a/runtime/src/jycessing/mode/run/PdeSketch.java
+++ b/runtime/src/jycessing/mode/run/PdeSketch.java
@@ -4,11 +4,13 @@
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import jycessing.DisplayType;
import jycessing.RunnableSketch;
import jycessing.Runner.LibraryPolicy;
+import jycessing.mode.PythonMode;
import processing.app.Base;
import processing.app.Platform;
import processing.app.Sketch;
@@ -17,37 +19,47 @@
/**
* A sketch run from the PDE.
*
- * This is created in the PDE process, serialized, and then used in the sketch process.
+ * This is created in the PDE process, serialized, and then used in the sketch process.
*/
-
@SuppressWarnings("serial")
public class PdeSketch implements RunnableSketch, Serializable {
private final List libraryDirs;
+ private final List pythonLibraryDirs;
private final File mainFile;
private final String mainCode;
private final File sketchHome;
+ private final File realSketchPath;
private final Point location;
private final LocationType locationType;
private final DisplayType displayType;
+ private final String bgColor;
+ private final String stopColor;
public final String[] codeFileNames; // unique to PdeSketch - leave as public field?
- public PdeSketch(final Sketch sketch, final File sketchPath, final DisplayType displayType,
- final Point location, final LocationType locationType) {
+ public PdeSketch(
+ final Sketch sketch,
+ final File sketchPath,
+ final DisplayType displayType,
+ final Point location,
+ final LocationType locationType, final String bgColor, final String stopColor) {
this.displayType = displayType;
this.location = location;
this.locationType = locationType;
+ this.bgColor = bgColor;
+ this.stopColor = stopColor;
this.mainFile = sketchPath.getAbsoluteFile();
this.mainCode = sketch.getMainProgram();
this.sketchHome = sketch.getFolder().getAbsoluteFile();
+ this.realSketchPath = sketchPath;
final List libraryDirs = new ArrayList<>();
libraryDirs.add(Platform.getContentFile("modes/java/libraries"));
libraryDirs.add(Base.getSketchbookLibrariesFolder());
- libraryDirs.add(sketch.getCodeFolder());
+ libraryDirs.add(sketchPath);
this.libraryDirs = libraryDirs;
final String[] codeFileNames = new String[sketch.getCodeCount()];
@@ -55,10 +67,13 @@ public PdeSketch(final Sketch sketch, final File sketchPath, final DisplayType d
codeFileNames[i] = sketch.getCode(i).getFile().getName();
}
this.codeFileNames = codeFileNames;
+ this.pythonLibraryDirs =
+ Arrays.asList(PythonMode.getSitePackages().getAbsoluteFile());
}
public static enum LocationType {
- EDITOR_LOCATION, SKETCH_LOCATION;
+ EDITOR_LOCATION,
+ SKETCH_LOCATION;
}
@Override
@@ -92,7 +107,9 @@ public String[] getPAppletArguments() {
}
break;
case PRESENTATION:
- args.add("fullScreen");
+ args.add(PApplet.ARGS_PRESENT);
+ args.add(String.join("=", PApplet.ARGS_WINDOW_COLOR, bgColor));
+ args.add(String.join("=", PApplet.ARGS_STOP_COLOR, stopColor));
break;
}
@@ -106,9 +123,6 @@ public List getLibraryDirectories() {
return libraryDirs;
}
- /**
- * PDE sketches should be selective - no need to rush, we can just load as we go.
- */
@Override
public LibraryPolicy getLibraryPolicy() {
return LibraryPolicy.SELECTIVE;
@@ -122,12 +136,14 @@ public boolean shouldRun() {
@Override
public List getPathEntries() {
final List entries = new ArrayList<>();
+ entries.add(realSketchPath.getParentFile());
entries.add(sketchHome);
entries.add(new File(sketchHome, "source"));
final File code = new File(sketchHome, "code");
if (code.exists()) {
entries.add(code);
}
+ entries.addAll(pythonLibraryDirs);
return entries;
}
}
diff --git a/runtime/src/jycessing/mode/run/RMIUtils.java b/runtime/src/jycessing/mode/run/RMIUtils.java
index a01ccaf1..a096b6bf 100644
--- a/runtime/src/jycessing/mode/run/RMIUtils.java
+++ b/runtime/src/jycessing/mode/run/RMIUtils.java
@@ -1,18 +1,38 @@
package jycessing.mode.run;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
+import java.rmi.server.RMIClientSocketFactory;
+import java.rmi.server.RMIServerSocketFactory;
import java.rmi.server.UnicastRemoteObject;
import jycessing.mode.PythonMode;
public class RMIUtils {
- private static final boolean EXTREMELY_VERSBOSE = false;
+ private static final boolean EXTREMELY_VERBOSE = false;
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 RMIServerSocketFactory serverFactory = new RMIServerSocketFactory() {
+ @Override
+ public ServerSocket createServerSocket(final int port) throws IOException {
+ return new ServerSocket(port, 50, InetAddress.getLoopbackAddress());
+ }
+ };
+
static {
System.setProperty("sun.rmi.transport.tcp.localHostNameTimeOut", "1000");
System.setProperty("java.rmi.server.hostname", "127.0.0.1");
@@ -20,7 +40,7 @@ public class RMIUtils {
// Timeout RMI calls after 1.5 seconds. Good for detecting a hanging sketch runner.
System.setProperty("sun.rmi.transport.tcp.responseTimeout", "1500");
- if (EXTREMELY_VERSBOSE) {
+ if (EXTREMELY_VERBOSE) {
System.setProperty("java.rmi.server.logCalls", "true");
System.setProperty("sun.rmi.server.logLevel", "VERBOSE");
System.setProperty("sun.rmi.client.logCalls", "true");
@@ -42,31 +62,35 @@ public static class RMIProblem extends Exception {
private RMIUtils() {}
+ private static Registry registry;
+
public static Registry registry() throws RemoteException {
- try {
- return LocateRegistry.createRegistry(RMI_PORT);
- } catch (final RemoteException e) {
+ if (registry == null) {
+ try {
+ registry = LocateRegistry.createRegistry(RMI_PORT, clientFactory, serverFactory);
+ System.err.println("created registry at port " + RMI_PORT);
+ } catch (final RemoteException e) {
+ System.err.println("could not create registry; assume it's already created");
+ registry = LocateRegistry.getRegistry("127.0.0.1", RMI_PORT, clientFactory);
+ }
}
- return LocateRegistry.getRegistry(RMI_PORT);
+ return registry;
}
public static void bind(final Remote remote, final Class extends Remote> remoteInterface)
throws RMIProblem {
final String registryKey = remoteInterface.getSimpleName();
try {
- final Remote stub = export(remote);
+ export(remote);
log("Attempting to bind instance of " + remote.getClass().getName() + " to registry as "
+ registryKey);
- registry().bind(registryKey, stub);
+ registry().bind(registryKey, remote);
log("Bound.");
- Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- log("Unbinding " + registryKey + " from registry.");
- registry().unbind(registryKey);
- } catch (final Exception e) {
- }
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ try {
+ log("Unbinding " + registryKey + " from registry.");
+ registry().unbind(registryKey);
+ } catch (final Exception e) {
}
}));
} catch (final Exception e) {
@@ -74,9 +98,9 @@ public void run() {
}
}
- public static Remote export(final Remote remote) throws RMIProblem {
+ public static void export(final Remote remote) throws RMIProblem {
try {
- return UnicastRemoteObject.exportObject(remote, 0);
+ UnicastRemoteObject.exportObject(remote, 0);
} catch (final RemoteException e) {
throw new RMIProblem(e);
}
diff --git a/runtime/src/jycessing/mode/run/SketchRunner.java b/runtime/src/jycessing/mode/run/SketchRunner.java
index 373f2335..cb79df20 100644
--- a/runtime/src/jycessing/mode/run/SketchRunner.java
+++ b/runtime/src/jycessing/mode/run/SketchRunner.java
@@ -1,6 +1,5 @@
package jycessing.mode.run;
-import java.awt.Point;
import java.rmi.RemoteException;
import jycessing.Printer;
@@ -36,24 +35,20 @@ public SketchRunner(final String id, final ModeService modeService) {
System.err.println(e.getMessage());
}
}
- new Thread(new Runnable() {
- @Override
- public void run() {
- Runner.warmup();
- }
- }, "SketchRunner Warmup Thread").start();
+ new Thread(() -> Runner.warmup(), "SketchRunner Warmup Thread").start();
}
/**
- * On Mac, even though this app has no menu, there's still a
- * built-in cmd-Q handler that, by default, quits the app.
- * Because starting up the {@link SketchRunner} is expensive,
- * we'd prefer to leave the app running.
- * This function responds to a user cmd-Q by stopping the
- * currently running sketch, and rejecting the attempt to quit.
- *
But if we've received a shutdown request from the
- * {@link SketchServiceProcess} on the PDE VM, then we permit
- * the quit to proceed.
+ * On Mac, even though this app has no menu, there's still a built-in cmd-Q handler that, by
+ * default, quits the app. Because starting up the {@link SketchRunner} is expensive, we'd prefer
+ * to leave the app running.
+ *
+ *
This function responds to a user cmd-Q by stopping the currently running sketch, and
+ * rejecting the attempt to quit.
+ *
+ *
But if we've received a shutdown request from the {@link SketchServiceProcess} on the PDE
+ * VM, then we permit the quit to proceed.
+ *
* @return true iff the SketchRunner should quit.
*/
public boolean preventUserQuit() {
@@ -62,12 +57,7 @@ public boolean preventUserQuit() {
return true;
}
log("Cancelling quit, but stopping sketch.");
- new Thread(new Runnable() {
- @Override
- public void run() {
- stopSketch();
- }
- }).start();
+ new Thread(() -> stopSketch()).start();
return false;
}
@@ -78,64 +68,72 @@ public void shutdown() {
System.exit(0);
}
+ private abstract class RemotePrinter implements Printer {
+ abstract protected void doPrint(String s) throws RemoteException;
+
+ public void print(final Object o) {
+ try {
+ doPrint(String.valueOf(o));
+ } catch (final RemoteException e) {
+ System.err.println(e);
+ }
+ }
+
+ @Override
+ public void flush() {
+ // no-op
+ }
+ }
+
@Override
public void startSketch(final PdeSketch sketch) {
- runner = new Thread(new Runnable() {
- @Override
- public void run() {
+ runner = new Thread(() -> {
+ try {
try {
- try {
- final Printer stdout = new Printer() {
- @Override
- public void print(final Object o) {
- try {
- modeService.printStdOut(id, String.valueOf(o));
- } catch (final RemoteException e) {
- System.err.println(e);
- }
- }
- };
- final Printer stderr = new Printer() {
- @Override
- public void print(final Object o) {
- try {
- modeService.printStdErr(id, String.valueOf(o));
- } catch (final RemoteException e) {
- System.err.println(e);
- }
- }
- };
- final SketchPositionListener sketchPositionListener = new SketchPositionListener() {
- @Override
- public void sketchMoved(final Point leftTop) {
- try {
- modeService.handleSketchMoved(id, leftTop);
- } catch (final RemoteException e) {
- System.err.println(e);
- }
- }
- };
- Runner.runSketchBlocking(sketch, stdout, stderr, sketchPositionListener);
- } catch (final PythonSketchError e) {
- log("Sketch runner caught " + e);
- modeService.handleSketchException(id, convertPythonSketchError(e, sketch.codeFileNames));
- } catch (final Exception e) {
- if (e.getCause() != null && e.getCause() instanceof PythonSketchError) {
- modeService.handleSketchException(id,
- convertPythonSketchError((PythonSketchError)e.getCause(), sketch.codeFileNames));
- } else {
- modeService.handleSketchException(id, e);
+ final Printer stdout = new RemotePrinter() {
+ @Override
+ protected void doPrint(final String s) throws RemoteException {
+ modeService.printStdOut(id, s);
}
- } finally {
- log("Handling sketch stoppage...");
- modeService.handleSketchStopped(id);
+ };
+ final Printer stderr = new RemotePrinter() {
+ @Override
+ protected void doPrint(final String s) throws RemoteException {
+ modeService.printStdErr(id, s);
+ }
+ };
+ final SketchPositionListener sketchPositionListener = leftTop -> {
+ try {
+ modeService.handleSketchMoved(id, leftTop);
+ } catch (final RemoteException e) {
+ System.err.println(e);
+ }
+ };
+ Runner.runSketchBlocking(sketch, stdout, stderr, sketchPositionListener);
+ } catch (final PythonSketchError e1) {
+ log("Sketch runner caught " + e1);
+ if (e1.getMessage().startsWith("SystemExit")) {
+ // Someone called sys.exit(). No-op.
+ } else {
+ modeService.handleSketchException(id,
+ convertPythonSketchError(e1, sketch.codeFileNames));
+ }
+ } catch (final Exception e2) {
+ if (e2.getCause() != null && e2.getCause() instanceof PythonSketchError) {
+ modeService.handleSketchException(id,
+ convertPythonSketchError((PythonSketchError)e2.getCause(), sketch.codeFileNames));
+ } else {
+ modeService.handleSketchException(id, e2);
}
- } catch (final RemoteException e) {
- log(e.toString());
+ } finally {
+ log("Handling sketch stoppage...");
+ modeService.handleSketchStopped(id);
}
- // Exiting; no need to interrupt and join it later.
- runner = null;
+ } catch (final RemoteException e3) {
+ log(e3.toString());
}
+ // Exiting; no need to interrupt and join it later.
+ runner = null;
}, "processing.py mode runner");
runner.start();
}
@@ -162,8 +160,10 @@ public static void main(final String[] args) {
// If env var SKETCH_RUNNER_FIRST=true then SketchRunner will wait for a ping from the Mode
// before registering itself as the sketch runner.
if (PythonMode.SKETCH_RUNNER_FIRST) {
+ log("Waiting for mode with id " + id);
waitForMode(id);
} else {
+ log("Starting sketch runner immediately with id " + id);
startSketchRunner(id);
}
}
@@ -171,12 +171,10 @@ public static void main(final String[] args) {
private static class ModeWaiterImpl implements ModeWaiter {
final String id;
-
public ModeWaiterImpl(final String id) {
this.id = id;
}
-
@Override
public void modeReady(final ModeService modeService) {
try {
@@ -204,21 +202,18 @@ private static void startSketchRunner(final String id) {
}
}
- private static void launch(final String id, final ModeService modeService) throws RMIProblem,
- RemoteException {
+ private static void launch(final String id, final ModeService modeService)
+ throws RMIProblem, RemoteException {
final SketchRunner sketchRunner = new SketchRunner(id, modeService);
- final SketchService stub = (SketchService)RMIUtils.export(sketchRunner);
+ RMIUtils.export(sketchRunner);
log("Calling mode's handleReady().");
- modeService.handleReady(id, stub);
- Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
- @Override
- public void run() {
- log("Exiting; telling modeService.");
- try {
- modeService.handleSketchStopped(id);
- } catch (final RemoteException e) {
- // nothing we can do about it now.
- }
+ modeService.handleReady(id, sketchRunner);
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ log("Exiting; telling modeService.");
+ try {
+ modeService.handleSketchStopped(id);
+ } catch (final RemoteException e) {
+ // nothing we can do about it now.
}
}));
}
@@ -246,5 +241,4 @@ private SketchException convertPythonSketchError(final PythonSketchError e,
}
return new SketchException(e.getMessage(), fileIndex, e.line, e.column);
}
-
}
diff --git a/runtime/src/jycessing/mode/run/SketchServiceManager.java b/runtime/src/jycessing/mode/run/SketchServiceManager.java
index 7d3ad554..7ed4e975 100644
--- a/runtime/src/jycessing/mode/run/SketchServiceManager.java
+++ b/runtime/src/jycessing/mode/run/SketchServiceManager.java
@@ -26,8 +26,8 @@ private static void log(final String msg) {
private volatile boolean isStarted = false;
/**
- * This is used when {@link PythonMode#SKETCH_RUNNER_FIRST} is true. This lets
- * use run the SketchRunner in a debugger, for example.
+ * This is used when {@link PythonMode#SKETCH_RUNNER_FIRST} is true. This lets use run the
+ * SketchRunner in a debugger, for example.
*/
private SketchService debugSketchRunner;
@@ -63,9 +63,8 @@ public void start() {
isStarted = true;
try {
if (PythonMode.SKETCH_RUNNER_FIRST) {
- final ModeService stub = (ModeService)RMIUtils.export(this);
- final ModeWaiter modeWaiter = RMIUtils.lookup(ModeWaiter.class);
- modeWaiter.modeReady(stub);
+ RMIUtils.export(this);
+ RMIUtils.lookup(ModeWaiter.class).modeReady(this);
} else {
RMIUtils.bind(this, ModeService.class);
}
@@ -75,7 +74,6 @@ public void start() {
}
}
-
private SketchServiceProcess processFor(final String editorId) {
if (PythonMode.SKETCH_RUNNER_FIRST) {
return sketchServices.get(DEBUG_SKETCH_RUNNER_KEY);
@@ -83,8 +81,10 @@ private SketchServiceProcess processFor(final String editorId) {
final SketchServiceProcess p = sketchServices.get(editorId);
if (p == null) {
- throw new RuntimeException("I somehow got a message from the sketch runner for " + editorId
- + " but don't have an active service process for it!");
+ throw new RuntimeException(
+ "I somehow got a message from the sketch runner for "
+ + editorId
+ + " but don't have an active service process for it!");
}
return p;
}
diff --git a/runtime/src/jycessing/mode/run/SketchServiceProcess.java b/runtime/src/jycessing/mode/run/SketchServiceProcess.java
index 91f37b5f..b2e018fa 100644
--- a/runtime/src/jycessing/mode/run/SketchServiceProcess.java
+++ b/runtime/src/jycessing/mode/run/SketchServiceProcess.java
@@ -17,6 +17,8 @@
import java.util.List;
import java.util.regex.Pattern;
+import com.google.common.base.Joiner;
+
import jycessing.mode.PyEditor;
import jycessing.mode.PythonMode;
import processing.app.Messages;
@@ -24,8 +26,6 @@
import processing.app.Preferences;
import processing.app.SketchException;
-import com.google.common.base.Joiner;
-
public class SketchServiceProcess {
private static void log(final String msg) {
if (PythonMode.VERBOSE) {
@@ -56,9 +56,8 @@ public SketchServiceProcess(final PythonMode mode, final PyEditor editor) {
}
/**
- * This constructor should only be used while instrumenting or debugging the
- * {@link SketchRunner}, in which case it has already been started in the
- * debugger or such like.
+ * This constructor should only be used while instrumenting or debugging the {@link SketchRunner},
+ * in which case it has already been started in the debugger or such like.
*/
SketchServiceProcess(final PythonMode mode, final PyEditor editor,
final SketchService runningService) {
@@ -121,18 +120,23 @@ private ProcessBuilder createServerCommand() {
command.add("-Xdock:name=Processing");
}
+ // Attempt to address https://github.com/jdf/Processing.py-Bugs/issues/158
+ command.add("-Dpython.console.encoding=UTF-8");
+
if (PythonMode.VERBOSE) {
command.add("-Dverbose=true");
}
- command.add("-Djava.library.path=" + System.getProperty("java.library.path"));
+ command.add("-Djava.library.path=" + System.getProperty("java.library.path")
+ + File.pathSeparator + mode.getContentFile("mode").getAbsolutePath());
final List cp = new ArrayList<>();
cp.addAll(filter(
- Arrays.asList(System.getProperty("java.class.path")
- .split(Pattern.quote(File.pathSeparator))),
+ Arrays
+ .asList(System.getProperty("java.class.path").split(Pattern.quote(File.pathSeparator))),
not(or(
- containsPattern("(ant|ant-launcher|antlr|netbeans.*|osgi.*|jdi.*|ibm\\.icu.*|jna)\\.jar$"),
+ containsPattern(
+ "(ant|ant-launcher|antlr|netbeans.*|osgi.*|jdi.*|ibm\\.icu.*|jna)\\.jar$"),
containsPattern("/processing/app/(test|lib)/")))));
for (final File jar : new File(Platform.getContentFile("core"), "library").listFiles(JARS)) {
cp.add(jar.getAbsolutePath());
@@ -193,7 +197,6 @@ public void run() {
}
}
-
public void stopSketch() throws SketchException {
if (sketchService == null) {
log("Sketch runner apparently not running; can't stop sketch.");
@@ -208,7 +211,6 @@ public void stopSketch() throws SketchException {
}
}
-
public void shutdown() {
if (sketchService != null) {
log("Telling sketch runner to shutdown.");
@@ -246,5 +248,4 @@ public void handleSketchException(final Exception e) {
public void handleSketchMoved(final Point leftTop) {
editor.setSketchLocation(leftTop);
}
-
}
diff --git a/runtime/src/jycessing/mode/run/WrappedPrintStream.java b/runtime/src/jycessing/mode/run/WrappedPrintStream.java
index a403bb18..6a5c0e1e 100644
--- a/runtime/src/jycessing/mode/run/WrappedPrintStream.java
+++ b/runtime/src/jycessing/mode/run/WrappedPrintStream.java
@@ -13,6 +13,10 @@ private PushedOut() {
System.setOut(WrappedPrintStream.this);
}
+ public void open() {
+ System.setOut(WrappedPrintStream.this);
+ }
+
@Override
public void close() {
System.setOut(saved);
@@ -23,13 +27,20 @@ public WrappedPrintStream(final OutputStream out) {
super(out);
}
+ private PushedOut cachedOut = null;
+
/**
- * {@link #pushStdout()} swaps this {@link WrappedPrintStream} in for System.out,
- * and then puts the original stream back when the {@link PushedOut} is closed.
+ * {@link #pushStdout()} swaps this {@link WrappedPrintStream} in for System.out, and then puts
+ * the original stream back when the {@link PushedOut} is closed.
+ *
* @return an AutoCloseable context that restores System.out.
*/
public PushedOut pushStdout() {
- return new PushedOut();
+ if (cachedOut == null) {
+ cachedOut = new PushedOut();
+ }
+ cachedOut.open();
+ return cachedOut;
}
public abstract void doPrint(String s);
@@ -133,5 +144,4 @@ public void println(final long x) {
public void println(final Object x) {
println(String.valueOf(x));
}
-
}
diff --git a/runtime/src/jycessing/primitives/PrimitiveFloat.java b/runtime/src/jycessing/primitives/PrimitiveFloat.java
index 0cca5d72..ca8c3549 100755
--- a/runtime/src/jycessing/primitives/PrimitiveFloat.java
+++ b/runtime/src/jycessing/primitives/PrimitiveFloat.java
@@ -4,7 +4,7 @@
/**
* Primitive float class. Serves as a container for libraries such as Ani.
- *
+ *
* @author Ralf Biedert
*/
@PythonUsage(methodName = "PrimitiveFloat")
diff --git a/runtime/src/jycessing/pyde_preprocessor.py b/runtime/src/jycessing/pyde_preprocessor.py
index a2c6e3c3..b512576c 100644
--- a/runtime/src/jycessing/pyde_preprocessor.py
+++ b/runtime/src/jycessing/pyde_preprocessor.py
@@ -18,6 +18,9 @@
#
# def draw():
#
+#
+# This also renames a "keyPressed" function to "__keyPressed__", to avoid hiding
+# the boolean variable with the same name.
import ast
@@ -29,74 +32,65 @@ def __init__(self):
self.fullScreen = False
self.noSmooth = False
self.smooth = False
+ self.pixelDensity = False
+ def insert(self, body):
+ for attr in ('size', 'fullScreen', 'noSmooth', 'smooth', 'pixelDensity'):
+ if getattr(self, attr):
+ body.insert(0, getattr(self, attr))
+
def pyde_preprocessor(module):
- __program_info__ = Program_info()
+ info = Program_info()
# Walk throught the abstract syntax tree for the original sketch.
for node in module.body:
if isinstance(node, ast.FunctionDef):
+ if (node.name == 'keyPressed'):
+ # rename keyPressed to not clash with the builtin boolean variable
+ node.name = '__keyPressed__'
+ continue
if (node.name == 'setup'):
+ toremove = []
# The user has defined a setup() function. Look through setup() for calls
# to size(), fullScreen(), noSmooth() and smooth().
- __program_info__.found_setup = True
+ info.found_setup = True
for subNode in node.body:
- if isinstance(subNode, ast.Expr):
- if isinstance(subNode.value, ast.Call):
- calledFunc = subNode.value.func.id
- if (calledFunc == "size"):
- __program_info__.size = subNode
- node.body.remove(subNode)
- elif (calledFunc == "fullScreen"):
- __program_info__.fullScreen = subNode
- node.body.remove(subNode)
- elif (calledFunc == "noSmooth"):
- __program_info__.noSmooth = subNode
- node.body.remove(subNode)
- elif (calledFunc == "smooth"):
- __program_info__.smooth = subNode
- node.body.remove(subNode)
+ if not isinstance(subNode, ast.Expr):
+ continue
+ if not isinstance(subNode.value, ast.Call):
+ continue
+ func = subNode.value.func
+ if hasattr(func, 'id') and func.id in (
+ 'size', 'fullScreen', 'noSmooth', 'smooth', 'pixelDensity'):
+ toremove.append(subNode)
+ setattr(info, func.id, subNode)
+ for n in toremove:
+ node.body.remove(n)
elif (node.name == 'settings'):
# The user has defined a settings() function.
- __program_info__.found_settings = True
+ info.found_settings = True
- if (__program_info__.size or __program_info__.fullScreen or __program_info__.noSmooth or __program_info__.smooth):
+ if (info.size or info.fullScreen or info.noSmooth or info.smooth):
# The user called one of the settings() subfunctions inside setup.
- if (__program_info__.found_settings):
+ if (info.found_settings):
# If a settings function was already defined, go through the tree to find it.
# Place all of the special function calls inside settings function body.
for node in module.body:
if (isinstance(node, ast.FunctionDef)):
if (node.name == 'settings'):
- if (__program_info__.smooth):
- node.body.insert(0, __program_info__.smooth)
- if (__program_info__.noSmooth):
- node.body.insert(0, __program_info__.noSmooth)
- if (__program_info__.size):
- node.body.insert(0, __program_info__.size)
- if (__program_info__.fullScreen):
- node.body.insert(0, __program_info__.fullScreen)
- # Don't look through the rest of the tree.
+ info.insert(node.body)
break
else:
# If a settings function is not defined, we define one and place all of
# the special function calls within it.
- settingsArgs = ast.arguments(args = [], vararg = None, kwarg = None, defaults = [])
- settingsFunc = ast.FunctionDef("settings",settingsArgs,[],[])
- if (__program_info__.noSmooth):
- settingsFunc.body.insert(0, __program_info__.noSmooth)
- if (__program_info__.smooth):
- settingsFunc.body.insert(0, __program_info__.smooth)
- if (__program_info__.size):
- settingsFunc.body.insert(0, __program_info__.size)
- if (__program_info__.fullScreen):
- settingsFunc.body.insert(0, __program_info__.fullScreen)
-
- # Place the newly defined settings() function within the module body.
+ settingsArgs = ast.arguments(args=[], vararg=None, kwarg=None, defaults=[])
+ settingsFunc = ast.FunctionDef("settings", settingsArgs, [], [])
+ info.insert(settingsFunc.body)
+ # Place the newly defined settings() function at the end of the module body.
# It's like it's been there the whole time...
- module.body.insert(0, settingsFunc)
+ module.body.insert(len(module.body), settingsFunc)
module = ast.parse(__processing_source__ + "\n\n", filename=__file__)
pyde_preprocessor(module)
-codeobj = compile(module, __file__, mode='exec')
+codeobj = compile(module, __file__, mode='exec')
exec(codeobj)
diff --git a/testing/resources/data/foo.py b/testing/resources/data/foo.py
new file mode 100755
index 00000000..8f062c16
--- /dev/null
+++ b/testing/resources/data/foo.py
@@ -0,0 +1 @@
+victory = 'yes'
diff --git a/testing/resources/test_from_future_with_settings.py b/testing/resources/test_from_future_with_settings.py
new file mode 100644
index 00000000..64339bba
--- /dev/null
+++ b/testing/resources/test_from_future_with_settings.py
@@ -0,0 +1,8 @@
+from __future__ import division
+
+def setup():
+ size(20, 20)
+
+def draw():
+ print 'OK'
+ exit()
\ No newline at end of file
diff --git a/testing/resources/test_g.py b/testing/resources/test_g.py
new file mode 100644
index 00000000..0b303016
--- /dev/null
+++ b/testing/resources/test_g.py
@@ -0,0 +1,12 @@
+import processing.opengl.PGraphics3D
+
+def setup():
+ size(100, 100, P3D)
+
+def draw():
+ # check that the alias cameraMatrix->camera is working as expected
+ g.camera(0, 0, -10, 0, 0, 0, 0, 1, 0)
+ assert(g.cameraMatrix.m03 == 0)
+ assert(g.cameraMatrix.m23 == -10)
+ print 'OK'
+ exit()
diff --git a/testing/resources/test_keyPressed_redefined.py b/testing/resources/test_keyPressed_redefined.py
new file mode 100644
index 00000000..5e7ad921
--- /dev/null
+++ b/testing/resources/test_keyPressed_redefined.py
@@ -0,0 +1,12 @@
+def settings():
+ size(48, 48, P2D)
+
+def draw():
+ if frameCount > 1:
+ raise Exception("Expected to exit.")
+ assert(keyPressed == False)
+ __keyPressed__()
+
+def keyPressed():
+ print "OK"
+ exit()
\ No newline at end of file
diff --git a/testing/resources/test_loadthings.py b/testing/resources/test_loadthings.py
index af3597c2..00643685 100755
--- a/testing/resources/test_loadthings.py
+++ b/testing/resources/test_loadthings.py
@@ -28,5 +28,14 @@
assert a.getString(0) == 'hello'
assert a.getString(1) == 'world'
+expected = ['hello', 'world']
+helloworld = loadStrings(createInput("strings.txt"))
+assert helloworld[0] == 'hello'
+assert helloworld[1] == 'world'
+
+helloworld = loadStrings(createInput(File("testing/resources/data/strings.txt")))
+assert helloworld[0] == 'hello'
+assert helloworld[1] == 'world'
+
print 'OK'
-exit()
\ No newline at end of file
+exit()
diff --git a/testing/resources/test_mixed_smooth_error.py b/testing/resources/test_mixed_smooth_error.py
new file mode 100644
index 00000000..27add5e6
--- /dev/null
+++ b/testing/resources/test_mixed_smooth_error.py
@@ -0,0 +1,8 @@
+# should throw a mixed smooth exception
+
+background(0)
+noStroke()
+smooth()
+ellipse(30, 48, 36, 36)
+noSmooth()
+ellipse(70, 48, 36, 36)
\ No newline at end of file
diff --git a/testing/resources/test_pgraphics_calls.py b/testing/resources/test_pgraphics_calls.py
new file mode 100644
index 00000000..0df4ad06
--- /dev/null
+++ b/testing/resources/test_pgraphics_calls.py
@@ -0,0 +1,16 @@
+# make sure that ellipse, arc, line, and rect all work as functions
+
+size(100, 100)
+pg = createGraphics(40, 40)
+
+pg.beginDraw()
+
+pg.ellipse(20, 20, 10, 10)
+pg.line(30, 30, 40, 40)
+pg.rect(0, 0, 10, 10)
+pg.arc(30, 35, 30, 30, 0, HALF_PI)
+pg.endDraw()
+image(pg, 9, 30)
+
+print 'OK'
+exit()
diff --git a/testing/resources/test_pixels.py b/testing/resources/test_pixels.py
index 1d9a556f..92fc03db 100755
--- a/testing/resources/test_pixels.py
+++ b/testing/resources/test_pixels.py
@@ -5,6 +5,12 @@
rect(10, 10, 10, 10)
assert get(15, 15) == 0xFF0000FF
+square(10, 30, 10)
+assert get(15, 35) == 0xFF0000FF
+
+circle(100, 100, 10)
+assert get(99, 99) == 0xFF0000FF
+
fill(255)
rect(20, 10, 10, 10)
assert get(25, 15) == 0xFFFFFFFF
@@ -36,5 +42,20 @@
set(x, y, '#EEEE00')
assert get(75, 15) == 0xFFEEEE00
+background(100)
+fill('#0000FF')
+rect(0, 0, 10, 10)
+assert get(5, 5) == 0xFF0000FF
+
+with push():
+ translate(20, 0)
+ fill(255)
+ rect(0, 0, 10, 10)
+
+assert get(25, 5) == 0xFFFFFFFF
+
+rect(40, 0, 10, 10)
+assert get(45, 5) == 0xFF0000FF # pop also restores previous style confs
+
print 'OK'
-exit()
\ No newline at end of file
+exit()
diff --git a/testing/resources/test_pmatrixprint.py b/testing/resources/test_pmatrixprint.py
new file mode 100755
index 00000000..c6fb8520
--- /dev/null
+++ b/testing/resources/test_pmatrixprint.py
@@ -0,0 +1,3 @@
+a = PMatrix3D()
+a.print()
+exit()
diff --git a/testing/resources/test_pvector.py b/testing/resources/test_pvector.py
index f2b2d34d..dcf6f06f 100755
--- a/testing/resources/test_pvector.py
+++ b/testing/resources/test_pvector.py
@@ -130,6 +130,35 @@
end.lerp(200, 200, 0, .5)
assert end == PVector(150.0, 150.0)
+# test that instance op returns self
+a = PVector(3, 5, 7)
+b = a * 10
+assert a.mult(10) == b
+
+# test that a vector can do arithmetic with a tuple
+assert PVector(1, 2, 3) == (1, 2, 3)
+assert (PVector(1, 2, 3) + (3, 3, 3)) == (4, 5, 6)
+assert (PVector(5, 5, 5) - (1, 2, 3)) == (4, 3, 2)
+
+# Regression test for https://github.com/jdf/processing.py/issues/317
+r = PVector.random2D() * 10
+assert -10 <= r.x <= 10
+assert -10 <= r.y <= 10
+assert r.z == 0
+
+PVector.random3D(r)
+r += (1, 1, 1)
+assert 0 <= r.x <= 2
+assert 0 <= r.y <= 2
+assert 0 <= r.z <= 2
+
+# Regression test for https://github.com/jdf/processing.py/issues/334
+r = PVector.fromAngle(0) * 10
+assert r.x == 10
+assert r.y == 0
+assert r.z == 0
+
+
print 'OK'
exit()
diff --git a/testing/resources/test_static_size.py b/testing/resources/test_static_size.py
index 497f0032..6e130ba1 100755
--- a/testing/resources/test_static_size.py
+++ b/testing/resources/test_static_size.py
@@ -1,5 +1,5 @@
-def settings():
- size(10, 10)
-
+size(11, 13)
+assert(width == 11)
+assert(height == 13)
print 'OK'
exit()
diff --git a/testing/resources/test_syspathappend.py b/testing/resources/test_syspathappend.py
new file mode 100755
index 00000000..8db98132
--- /dev/null
+++ b/testing/resources/test_syspathappend.py
@@ -0,0 +1,11 @@
+import sys
+sys.path.append('data')
+from foo import victory
+
+def setup():
+ pass
+
+def draw():
+ assert victory == 'yes'
+ print 'OK'
+ exit()
diff --git a/testing/resources/test_thread.py b/testing/resources/test_thread.py
new file mode 100755
index 00000000..6afaf620
--- /dev/null
+++ b/testing/resources/test_thread.py
@@ -0,0 +1,25 @@
+import time
+
+def setup():
+ size(10, 10)
+
+n = 0
+
+def func1():
+ global n
+ n += 1
+
+def func2():
+ global n
+ n += 4
+
+def draw():
+ noLoop()
+ thread("func1")
+ while n < 1:
+ time.sleep(0.02)
+ thread(func2)
+ while n < 5:
+ time.sleep(0.02)
+ print('OK')
+ exit()
diff --git a/testing/resources/test_unittest.py b/testing/resources/test_unittest.py
new file mode 100755
index 00000000..3a6bcd4f
--- /dev/null
+++ b/testing/resources/test_unittest.py
@@ -0,0 +1,13 @@
+import unittest
+
+def return_twelve():
+ return 12
+
+class TestTwelveness(unittest.TestCase):
+ def test_base(self):
+ self.assertEqual(12, return_twelve())
+
+suite = unittest.TestLoader().loadTestsFromTestCase(TestTwelveness)
+unittest.TextTestRunner(verbosity=0).run(suite)
+print 'OK'
+exit()
\ No newline at end of file
diff --git a/testing/resources/test_writethings.py b/testing/resources/test_writethings.py
new file mode 100644
index 00000000..611c6420
--- /dev/null
+++ b/testing/resources/test_writethings.py
@@ -0,0 +1,27 @@
+import tempfile
+from java.io import File
+
+content = "hello"
+
+# guaranteed to be deleted on close and/or garbage collection
+with tempfile.NamedTemporaryFile() as tmpfile:
+
+ w = createOutput(tmpfile.name)
+ saveBytes(w, content)
+ w.close()
+ r = createInput(tmpfile.name)
+ data = loadBytes(r)
+ r.close()
+ assert ''.join([chr(c) for c in data]) == content
+
+ w = createOutput(tmpfile.name)
+ saveBytes(w, content)
+ w.close()
+ r = createInput(tmpfile.name)
+ data = loadBytes(r)
+ r.close()
+ assert ''.join([chr(c) for c in data]) == content
+
+print 'OK'
+exit()
+
diff --git a/testing/src/test/jycessing/JycessingTests.java b/testing/src/test/jycessing/JycessingTests.java
index fdc47b74..7753e109 100755
--- a/testing/src/test/jycessing/JycessingTests.java
+++ b/testing/src/test/jycessing/JycessingTests.java
@@ -12,16 +12,18 @@
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
+import org.junit.Test;
+
import jycessing.MixedModeError;
+import jycessing.MixedSmoothError;
import jycessing.PAppletJythonDriver;
import jycessing.Printer;
import jycessing.PythonSketchError;
import jycessing.Runner;
import jycessing.StreamPrinter;
-import org.junit.Test;
-
public class JycessingTests {
+
private static class CapturingPrinter implements Printer {
private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
private final PrintStream out = new PrintStream(baos, true);
@@ -31,17 +33,22 @@ public void print(final Object o) {
out.print(String.valueOf(o));
}
+ @Override
+ public void flush() {
+ out.flush();
+ }
+
public String getText() {
try {
- return new String(baos.toByteArray(), "utf-8").replaceAll("\r\n", "\n").replaceAll("\r",
- "\n");
+ return new String(baos.toByteArray(), "utf-8")
+ .replaceAll("\r\n", "\n")
+ .replaceAll("\r", "\n");
} catch (final UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}
-
private static String run(final String testResource) throws Exception {
System.err.println("Running " + testResource + " test.");
final Path source = Paths.get("testing/resources/test_" + testResource + ".py");
@@ -61,7 +68,9 @@ private static void testImport(final String module) throws Exception {
final Path src = Paths.get(tmp.toString(), "test_import_" + module + ".pyde");
try {
final String testText = "import " + module + "\nprint 'OK'\nexit()";
- Files.copy(new ByteArrayInputStream(testText.getBytes("utf-8")), src,
+ Files.copy(
+ new ByteArrayInputStream(testText.getBytes("utf-8")),
+ src,
StandardCopyOption.REPLACE_EXISTING);
final CapturingPrinter out = new CapturingPrinter();
System.err.println("Running import " + module + " test.");
@@ -78,7 +87,6 @@ private static void expectOK(final String testName) throws Exception {
assertEquals("OK\n", run(testName));
}
-
@Test
public void inherit_str() throws Exception {
assertEquals("cosmic\n12\n[12, 13]\n", run("inherit_str"));
@@ -210,6 +218,11 @@ public void loadThings() throws Exception {
expectOK("loadthings");
}
+ @Test
+ public void writeThings() throws Exception {
+ expectOK("writethings");
+ }
+
@Test
public void constrain() throws Exception {
expectOK("constrain");
@@ -264,4 +277,68 @@ public void keyDefinedBeforeKeyEvent() throws Exception {
public void randintDomainRegression() throws Exception {
expectOK("randint_domain_regression");
}
+
+ // https://github.com/jdf/processing.py/issues/167
+ @Test
+ public void syspathappend() throws Exception {
+ expectOK("syspathappend");
+ }
+
+ // https://github.com/jdf/Processing.py-Bugs/issues/148
+ @Test
+ public void from_future_with_settings() throws Exception {
+ expectOK("from_future_with_settings");
+ }
+
+ // https://github.com/jdf/processing.py/issues/199
+ @Test
+ public void matrix3d_print() throws Exception {
+ final String actual = run("pmatrixprint");
+ final String expected =
+ " 1.0000 0.0000 0.0000 0.0000\n"
+ + " 0.0000 1.0000 0.0000 0.0000\n"
+ + " 0.0000 0.0000 1.0000 0.0000\n"
+ + " 0.0000 0.0000 0.0000 1.0000\n\n";
+ assertEquals(expected, actual);
+ }
+
+ // https://github.com/jdf/processing.py/issues/264
+ @Test
+ public void unittest() throws Exception {
+ expectOK("unittest");
+ }
+
+ // https://github.com/jdf/processing.py/issues/251
+ @Test
+ public void detectMixedSmooth() throws Exception {
+ try {
+ run("mixed_smooth_error");
+ fail("Expected mixed smooth error.");
+ } catch (final MixedSmoothError expected) {
+ // noop
+ }
+ }
+
+ // https://github.com/jdf/processing.py/issues/280
+ @Test
+ public void g() throws Exception {
+ expectOK("g");
+ }
+
+ @Test
+ public void keyPressed_redefined() throws Exception {
+ expectOK("keyPressed_redefined");
+ }
+
+ // https://github.com/jdf/processing.py/issues/281
+ @Test
+ public void thread() throws Exception {
+ expectOK("thread");
+ }
+
+ // https://github.com/jdf/processing.py/issues/326
+ @Test
+ public void pGraphicsJava2d() throws Exception {
+ expectOK("pgraphics_calls");
+ }
}
diff --git a/testing/src/test/jycessing/TestSketch.java b/testing/src/test/jycessing/TestSketch.java
index 0d89c9a3..a8d08af5 100644
--- a/testing/src/test/jycessing/TestSketch.java
+++ b/testing/src/test/jycessing/TestSketch.java
@@ -6,27 +6,23 @@
import java.util.ArrayList;
import java.util.List;
-import processing.core.PApplet;
import jycessing.RunnableSketch;
import jycessing.Runner.LibraryPolicy;
+import processing.core.PApplet;
-/**
- *
- * Encapsulates a unit test so that it can be run by Runner.
- *
- */
+/** Encapsulates a unit test so that it can be run by Runner. */
public class TestSketch implements RunnableSketch {
private final Path sourcePath;
private final String sourceText;
private final String name;
-
+
public TestSketch(final Path sourcePath, final String sourceText, final String name) {
this.sourcePath = sourcePath;
this.sourceText = sourceText;
this.name = name;
}
-
+
@Override
public File getMainFile() {
return sourcePath.toFile();
@@ -68,5 +64,4 @@ public List getPathEntries() {
entries.add(getHomeDirectory());
return entries;
}
-
}
diff --git a/testmode.bat b/testmode.bat
new file mode 100644
index 00000000..4199e560
--- /dev/null
+++ b/testmode.bat
@@ -0,0 +1,26 @@
+set VERBOSE_PYTHON_MODE=true
+
+set PROCESSINGPY=%CD%
+set PROCESSING=..\processing
+
+for /f "tokens=1,2*" %%A in ('reg query "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" /v "Personal" 2^>nul') do set MY_DOCS_ROOT=%%C
+
+set MODES=%MY_DOCS_ROOT%\Processing\modes
+set RUNPROCESSINGDIR=%PROCESSING%\build\windows\work
+
+cd /d %PROCESSINGPY%
+ant mode.zip
+
+cd /d %MODES%
+del /s /f /q PythonMode
+for /f %%f in ('dir /ad /b PythonMode') do rd /s /q PythonMode\%%f
+
+cd /d %PROCESSINGPY%\work
+powershell Expand-Archive PythonMode.zip -DestinationPath %MODES%
+
+cd /d %PROCESSINGPY%
+
+cd /d %RUNPROCESSINGDIR%
+.\java\bin\java -cp lib\pde.jar;core\library\core.jar;lib\jna.jar;lib\jna-platform.jar;lib\antlr.jar;lib\ant.jar;lib\ant-launcher.jar processing.app.Base
+
+cd %PROCESSINGPY%
\ No newline at end of file
diff --git a/testmode.sh b/testmode.sh
index 434d5f0c..f7356c29 100755
--- a/testmode.sh
+++ b/testmode.sh
@@ -1,23 +1,23 @@
#!/bin/bash
export VERBOSE_PYTHON_MODE=true
-PROCESSING=~/processing
-PROCESSINGPY=~/processing.py
+PROCESSINGPY=$(pwd)
+PROCESSING=../processing
+MODES=~/Documents/Processing/modes;
if [[ $(uname) == 'Darwin' ]]; then
RUNPROCESSING=$PROCESSING/build/macosx/work/Processing.app/Contents/MacOS/Processing
- MODES=~/Documents/Processing/modes;
else
- RUNPROCESSING=$PROCESSING/build/linux/work/processing
+ RUNPROCESSING="$PROCESSING/build/linux/work/processing"
MODES=~/sketchbook/modes;
fi
-cd $PROCESSING/build && \
+cd "$PROCESSING/build" && \
#ant && \
- cd $PROCESSINGPY && \
+ cd "$PROCESSINGPY" && \
ant mode.zip && \
- cd $MODES && \
+ cd "$MODES" && \
rm -rf PythonMode && \
- unzip $PROCESSINGPY/work/PythonMode.zip && \
- cd /tmp && \
- $RUNPROCESSING
+ unzip "$PROCESSINGPY/work/PythonMode.zip" && \
+ cd "$PROCESSINGPY" && \
+ "$RUNPROCESSING"