Files
processing4/build/shared/libraries/howto.txt

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