mirror of
https://github.com/processing/processing4.git
synced 2026-02-13 18:35:37 +01:00
478 lines
17 KiB
Plaintext
478 lines
17 KiB
Plaintext
LIBRARIES IN PROCESSING
|
|
|
|
Some basic instructions on how libraries work with Processing.
|
|
(I'm also adding to this as people have trouble with it..
|
|
Pretty soon it's gonna be an outrageous mess!)
|
|
|
|
Libraries are a new feature that are present only in revisions 70
|
|
and higher. Before revision 70, users could place any sort of code
|
|
inside the 'code' folder of their sketch, but this meant too many
|
|
copies of each library. The code folder is still an option, but the
|
|
use of the new "library" system is encouraged as a simple packaging
|
|
mechanism for your projects.
|
|
|
|
A Processing library can be any sort of Java code that's been
|
|
given a package name and packed into a jar file. It can also register
|
|
itself with the parent applet to get notification of when events
|
|
happen in the sketch, for instance whenever draw() is called or a
|
|
key is pressed.
|
|
|
|
Most libraries may not need that much functionality, but they
|
|
may want to implement the dispose() call, which is called as the
|
|
applet is closed. Many libraries, especially those with native code,
|
|
need this to properly shut down.
|
|
|
|
It is not possible to build libraries from within Processing itself.
|
|
In fact, creating a library with Processing will cause problems
|
|
because when exported as an applet, the jar file will contain the
|
|
current version of the processing.core classes, which will cause
|
|
major conflicts when trying to use any other code.
|
|
|
|
The PDE is built with the sole purpose of creating short sketches that
|
|
are part of PApplet that have a few files at most. We don't plan to
|
|
extend the PDE to also support developing libraries (or "tools," once
|
|
those are enabled) because then it simply becomes like any other IDE
|
|
that is quickly too general for our target audience. Users who are
|
|
advanced enough in their programming skills to build libraries will
|
|
almost always be skilled enough to use another IDE like Eclipse
|
|
(if they aren't already) to build their library.
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
|
Notes on Library distribution
|
|
|
|
There are two categories of libraries. The "core" libraries
|
|
(Video, OpenGL, Serial, Net) are part of the Processing distribution,
|
|
and "contributed libraries" are developed, owned, and maintained
|
|
by members of the Processing community.
|
|
|
|
It's very possible that contributed libraries might make their way
|
|
into the regular distribution if it makes sense for all involved.
|
|
After the 1.0 release, we'll re-evaluate if some libraries should be
|
|
made a part of the standard distribution. For now, we don't have the
|
|
people resources to support this, because it would require us to
|
|
debug the libraries with each release.
|
|
|
|
We try to place a strong focus on the importance of clear
|
|
documentation for the Processing project, so please attempt similar
|
|
effort into communicating your library's features to potential users
|
|
by hosting a descriptive web site.
|
|
|
|
If you'd like to have your library posted on the Processing website
|
|
(http://processing.org/reference/libraries) please email
|
|
reas at processing.org and we'll make a decision about its inclusion.
|
|
We strongly encourage (and may someday require as a stipulation for
|
|
placement on the site) that the source to your library be included.
|
|
We're giving away all our stuff, and we want others to do so as well
|
|
because it's good for the community.
|
|
|
|
The contributed libraries are one of the most important aspects of
|
|
the Processing project and have an enormous impact on how people
|
|
understand Processing. Libraries have been designed into the larger
|
|
Processing plan to enable simple extensions of the core API in new,
|
|
innovative, and unexpected directions. The libraries are the future
|
|
of the project as we plan for processing.core.* to remain very
|
|
minimal.
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
|
A Very Basic Example
|
|
|
|
|
|
package libraryexample;
|
|
import processing.core.*;
|
|
|
|
public class BoringLibrary {
|
|
PApplet parent;
|
|
|
|
public BoringLibrary(PApplet parent) {
|
|
this.parent = parent;
|
|
parent.registerDispose(this);
|
|
}
|
|
|
|
public void dispose() {
|
|
// anything in here will be called automatically when
|
|
// the parent applet shuts down. for instance, this might
|
|
// shut down a thread used by this library.
|
|
// note that this currently has issues, see bug #183
|
|
// http://dev.processing.org/bugs/show_bug.cgi?id=183
|
|
}
|
|
}
|
|
|
|
|
|
Usually you'll need to pass "this" to a library's constructor so that
|
|
the library can access functions from PApplet, i.e graphics methods
|
|
like line() and stroke() or loadImage(). See later in the document for
|
|
information about reading and writing files.
|
|
|
|
If you'd like to use constants such as "RGB", use the following:
|
|
public class BoringLibrary implements PConstants {
|
|
which will allow all the constants to be used by that class.
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
|
Library Methods
|
|
|
|
public void pre()
|
|
method that's called just after beginFrame(), meaning that it
|
|
can affect drawing.
|
|
|
|
public void draw()
|
|
method that's called at the end of draw(), but before endFrame().
|
|
|
|
public void mouseEvent(MouseEvent e)
|
|
called when a mouse event occurs in the parent applet
|
|
|
|
public void keyEvent(KeyEvent e)
|
|
called when a key event occurs in the parent applet
|
|
|
|
public void post()
|
|
method called after draw has completed and the frame is done.
|
|
no drawing allowed.
|
|
|
|
public void size(int width, int height)
|
|
this will be called the first time an applet sets its size,
|
|
but also any time that it's called while the PApplet is running.
|
|
no drawing should occur inside of this method, because it may
|
|
not be the case that the new renderer is yet valid and ready.
|
|
use this only to flag the new size and prepare for the next frame.
|
|
|
|
public void stop()
|
|
can be called by users, for instance movie.stop() will shut down
|
|
a movie that's being played, or camera.stop() stops capturing
|
|
video. server.stop() will shut down the server and shut it down
|
|
completely, which is identical to its "dispose" function.
|
|
|
|
public void dispose()
|
|
this should only be called by PApplet. dispose() is what gets
|
|
called when the host applet is stopped, so this should shut down
|
|
any threads, disconnect from the net, unload memory, etc.
|
|
currently, this method is not being called consistently:
|
|
http://dev.processing.org/bugs/show_bug.cgi?id=77
|
|
|
|
To register any of these methods with the parent, call
|
|
parent.registerPre(this) or whatever the name of the function
|
|
is that you'd like to use.
|
|
|
|
Note that making things "public" is extremely important. When running
|
|
inside Processing, anything left blank has public added by the
|
|
preprocessor, meaning "void draw()" becomes "public void draw()".
|
|
|
|
You can only draw inside of pre(), draw(), mouseEvent(), or keyEvent()
|
|
otherwise you may run into trouble. pre() and draw() happen while
|
|
legitimate drawing is taking place, and the mouse/key events happen
|
|
just before draw() events are called, they're queued up by the host
|
|
applet until it's safe to draw.
|
|
|
|
For this reason, you should use registerMouseEvent() and mouseEvent()
|
|
(and same for the keys) to handle your events, rather than your class
|
|
implementing MouseListener. For instance, to figure out what the mouse
|
|
event is throwing back at you, this would be an example handler:
|
|
|
|
public void mouseEvent(MouseEvent event) {
|
|
int x = event.getX();
|
|
int y = event.getY();
|
|
|
|
switch (event.getID()) {
|
|
case MouseEvent.MOUSE_PRESSED:
|
|
// do something for the mouse being pressed
|
|
break;
|
|
case MouseEvent.MOUSE_RELEASED:
|
|
// do something for mouse released
|
|
break;
|
|
case MouseEvent.MOUSE_CLICKED:
|
|
// do something for mouse clicked
|
|
break;
|
|
case MouseEvent.MOUSE_DRAGGED:
|
|
// do something for mouse dragged
|
|
break;
|
|
case MouseEvent.MOUSE_MOVED:
|
|
// umm...
|
|
break;
|
|
}
|
|
}
|
|
|
|
More on mouse handling can be found in Sun's Java documentation:
|
|
http://java.sun.com/j2se/1.4.2/docs/api/java/awt/event/MouseEvent.html
|
|
which also covers things like modifiers (shift-click) and whatnot.
|
|
Also check out the code for PApplet to see how ctrl-click is handled
|
|
on Mac OS X so that it properly registers as a right-click.
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
|
Structure of a Library Folder
|
|
|
|
The Sonia library by Amit Pitaru is a good example here. To make a
|
|
library called sonia, you create a folder called "sonia" and within
|
|
that, a subfolder named "library". The sonia folder should be placed
|
|
inside the Processing "libraries" folder, or a user can place it
|
|
inside their sketchbook folder.
|
|
|
|
Inside "library", you'll find "sonia.jar". Anything that is found
|
|
inside library will be exported with your sketch.
|
|
|
|
If different sets of files should be exported with applets versus
|
|
applications, a file called "export.txt" should be included. For
|
|
sonia, this looks like:
|
|
|
|
# only export the jar file for applets..
|
|
# everything else is installed as a separate browser plugin
|
|
applet=sonia.jar
|
|
# application needs everything
|
|
application=sonia.jar,JSynClasses.jar,JSynV142.dll,libJSynV142.jnilib
|
|
|
|
This will include sonia.jar for applets, because in a web browser, the
|
|
DLL files must be installed separately along with JSynClasses.jar.
|
|
The # sign in front of a line means that the line is a comment,
|
|
and it'll be ignored by the PDE.
|
|
|
|
As of revision 0097, you can also specify what to export for other
|
|
platforms as well (at least Mac OS X, Windows, Linux). For the example
|
|
above, the application line could instead be changed to:
|
|
|
|
application.macosx=sonia.jar,JSynClasses.jar,libJSynV142.jnilib
|
|
application.windows=sonia.jar,JSynClasses.jar,JSynV142.dll
|
|
|
|
Platform-specific exports will be checked first, and if they don't
|
|
exist, the "application" will be used. If neither exist (or export.txt
|
|
doesn't exist), the entire contents of the library folder will be
|
|
copied.
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
|
Using Other Java Code As A Library
|
|
|
|
So long as the code is inside a package, it can be set up for use as
|
|
a library. For instance, if you want to make a library called 'poopy'
|
|
set up a folder as follows:
|
|
|
|
poopy ->
|
|
library ->
|
|
poopy.jar
|
|
|
|
Then, the folder should be placed in the Processing 'libraries' folder
|
|
or inside the user's sketch folder to be recognized by Processing and
|
|
its "Import Library" menu. As of now, you may need to restart
|
|
Processing in order to get the library to show up.
|
|
|
|
While this process may sound a little complicated, the intent is to
|
|
make it easier for users than a typical Java IDE. A little added
|
|
complexity for the developers of library code (who will generally be
|
|
more advanced users) is traded for great simplicity by the users,
|
|
since Processing is intended to target beginning programmers.
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
|
Import Statements and How They Work
|
|
|
|
If your library is sonia.jar, found at sonia/library/sonia.jar, all
|
|
the packages found in sonia.jar will be added as imports into the
|
|
user's sketch when they selected "Import Library".
|
|
|
|
In the case of Sonia, an additional .jar file can be found in the
|
|
sonia/library/ folder, jsyn.jar. The contents of jsyn.jar will not be
|
|
added to the import statements. This is to avoid every library having
|
|
a ridiculously large number of import statements. For instance, if you
|
|
want to use the "video" library, you don't want all 15-20 packages for
|
|
the QuickTime libraries listed there to confuse your users.
|
|
|
|
Bottom line, if you want packages from the other .jar to be loaded by
|
|
Processing, then you need to put those .class files into the main .jar
|
|
file for the library (sonia/library/sonia.jar in this case).
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
|
Import Statements and the Code Folder
|
|
|
|
The code folder works differently, and every package inside every .jar
|
|
found in the code folder is simply added to the import statements by
|
|
the preprocessor. The user never sees this, it just happens magically.
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
|
Creating .jar Files For Your Library
|
|
|
|
Since your code is inside a package, you need to make sure that it's
|
|
inside subfolders in the .jar file. It should be noted that jar files
|
|
are simply .zip files (they can be opened with WinZip or Stuffit) with
|
|
a "manifest" file.
|
|
|
|
In the past, you may have used:
|
|
javac *.java
|
|
to compile your files. Once they're inside a packages, you must use:
|
|
javac -d . *.java
|
|
which will create folders and subfolders for the packages. For
|
|
instance, for all the stuff in processing.core.* it would create:
|
|
|
|
processing/ ->
|
|
core/ ->
|
|
PApplet.class
|
|
PGraphics.class
|
|
..etc
|
|
|
|
then you can jar that stuff up using:
|
|
jar -cf core.jar processing
|
|
or with the command line info-zip utility:
|
|
zip -r core.jar processing
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
|
The "Import Library" Menu Item
|
|
|
|
All this does is add the "import yourlibrary.*;" statement to the top
|
|
of your sketch. If you've handwritten the import statements, then
|
|
there's no need to use "Import Library".
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
|
Getting a UnsupportedClassVersionError? (especially with Java 1.5)
|
|
|
|
When I compiled blah.jar (using the successful method mentioned
|
|
earlier) under Java 1.5, I get the following error from Processing:
|
|
|
|
java.lang.UnsupportedClassVersionError: blah/SomeClass
|
|
(Unsupported major.minor version 49.0)
|
|
|
|
This is because more recent versions of Java like to use their own
|
|
class file format that's not backwards compatible. Pretty annoying,
|
|
since it's rare that newer language features are actually used (and
|
|
shouldn't be with Processing anyway, since most likely you want to
|
|
make things work on Java 1.1)
|
|
|
|
The fix is to compile with "-target 1.1" which will create class
|
|
files that are compatible with Java 1.1. This is necessary for any
|
|
code that will run in a browser, since approximately 30-40% of web
|
|
users are still using Microsoft's Java 1.1.4 JVM (as of April 2005).
|
|
|
|
K. Damkjer adds: Depending on the version of Java that you're using,
|
|
specifying -target 1.1 when compiling libraries for distribution may
|
|
not be enough to get a 1.1 compatible class file. You may also need to
|
|
indicate source compatibility. It seems that -source 1.3 is the most
|
|
recent Java release that is capable of creating 1.1 targeted .class
|
|
files. All that said, here's a typical compile string for me:
|
|
|
|
javac -source 1.3 -target 1.1 -d .
|
|
-classpath %P5_HOME%\lib\core.jar package/dirs/*.java
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
|
Adding Your Own Library Events
|
|
|
|
So that your library can notify the host applet that something
|
|
interesting has happened, this is how you implement an event
|
|
method in the style of serialEvent, serverEvent, etc.
|
|
|
|
|
|
public class FancyLibrary {
|
|
Method fancyEventMethod;
|
|
|
|
public YourLibrary(PApplet parent) {
|
|
// your library init code here...
|
|
|
|
// check to see if the host applet implements
|
|
// public void fancyEvent(FancyLibrary f)
|
|
try {
|
|
fancyEventMethod =
|
|
parent.getClass().getMethod("fancyEvent",
|
|
new Class[] { FancyLibrary.class });
|
|
} catch (Exception e) {
|
|
// no such method, or an error.. which is fine, just ignore
|
|
}
|
|
}
|
|
|
|
// then later, to fire that event
|
|
public void makeEvent() {
|
|
if (fancyEventMethod != null) {
|
|
try {
|
|
fancyEventMethod.invoke(parent, new Object[] { this });
|
|
} catch (Exception e) {
|
|
System.err.println("Disabling fancyEvent() for " + name +
|
|
" because of an error.");
|
|
e.printStackTrace();
|
|
fancyEventMethod = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
|
Using built-in functions from processing.core
|
|
|
|
Many methods in PApplet are made static for use by other libraries
|
|
and code that interfaces with Processing. For instance, openStream()
|
|
requires an applet object, but loadStrings() is a static method
|
|
that can be run on any InputStream. See the developer's reference
|
|
for more information about which methods are available.
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
|
Accessing files from inside a library
|
|
|
|
To open files for use with a library, use the openStream() method.
|
|
This is the most compatible means for loading data, and makes use
|
|
of many hours of headaches that were the result of attempts to
|
|
create functions that loaded data across platforms (Mac, Windows,
|
|
and Linux) and circumstances (applet, application, and other).
|
|
|
|
The functions sketchPath(), savePath(), dataPath(), and createPath()
|
|
all facilitate reading and writing files relative to the sketch
|
|
folder. They should be used to ensure that file I/O works consistently
|
|
between your library and functions like loadImage() or loadStrings().
|
|
Their documentation can be seen in the online javadoc reference found
|
|
on dev.processing.org. The variable sketchPath is available for
|
|
convenience, but in nearly all cases, the sketchPath() method is
|
|
a better (and more compatible) route.
|
|
|
|
The xxxxPath() functions were finalized in revision 0096.
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
|
Library Naming Rules
|
|
|
|
Libraries, or classes inside them, should not be prefixed with "P"
|
|
the way that the core Processing classes are (PImage, PGraphics, etc).
|
|
It's tempting to prefix everything that way to identify it with
|
|
Processing, but we'd like to reserve that naming for "official"
|
|
things that are inside processing.core and other associated classes.
|
|
|
|
Same goes for using "Processing", "Pro", or "P5" just like "P",
|
|
or whether it's a prefix or a suffix.
|
|
|
|
Similarly, please don't using processing.* as the prefix for your
|
|
library packages. We'd like to keep that name space clear for
|
|
official things as well.
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
|
Ben Fry, Last updated 26 June 2006
|