Cover  \ Build  \ Source  \ Bugs  \ Reference  \ Libraries  \ Tools  
          Overview \ Guidelines \ Template \ Basics
 
   
 

Library Basics

A very basic example to build a Processing library from scratch.

package libraryexample;
import processing.core.*;

public class BasicLibrary {
  PApplet parent;

  public BasicLibrary(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, e.g. graphics methods like line() and stroke() or loadImage().

Using a static initializer such as YourLibrary.init(this) is discouraged because it's not a good way to write OOP code, and makes assumptions about how your library connects to a sketch, and it encourages bad style that implies a single instance of your library. If you need to share information across library instances, use static variables or methods behind the scenes, but don't expose them to the user through the initializer.


Using PConstants

If you'd like to use constants such as RGB, use the following:

public class BoringLibrary implements PConstants {

which will make all the constants available to that class.


Subclassing PGraphics

Release 0148 included many changes to how libraries that implement PGraphics are handled. You can read about the changes in the developer's reference for PGraphics. Libraries that use the old PGraphics constructors will not work with release 0148 or later until these changes have been made to their API.

The changes may be a little confusing at first, but a quick look at the source to other PGraphics implementations, such as PGraphicsOpenGL or PGraphicsPDF should clarify the way the new methods are used.


Library Methods

public void pre()
Method that's called just after beginDraw(), meaning that it can affect drawing.

public void draw()
Method that's called at the end of draw(), but before endDraw().

public void post()
Method called after draw has completed and the frame is done. No drawing allowed.

public void mouseEvent(MouseEvent e) 
Called when a mouse event occurs in the parent applet. Drawing is allowed because mouse events are queued.

public void keyEvent(KeyEvent e)
Called when a key event occurs in the parent applet. Drawing is allowed because key events are queued.

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.

public void dispose()
This should only be called by PApplet. The dispose() method is what gets called when the host applet is being shut down, so this should stop any threads, disconnect from the net, unload memory, etc. Currently, this method is not being called consistently (Bug #77), but we plan to improve its consistency in future releases.

To register any of these methods with the parent, call parent.registerXxxxx(this), replacing Xxxxx with the name of the function (pre, draw, post, etc) that you'd like to use.

Note that making these methods "public" is extremely important. When running inside Processing, anything left blank has public added by the preprocessor, meaning "void draw()" actually becomes "public void draw()".


When drawing (or code that manipulates a sketch) can occur

You can only draw inside of pre(), draw(), mouseEvent(), or keyEvent() otherwise you will run into problems. If using OpenGL to render, for instance, you can easily make your machine (or at least the sketch) crash hard if you mess with the drawing surface when you're not supposed to. 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... forgot
      break;
  }
}


More on mouse handling can be found in Sun's documentation, 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" line will be used. If neither exist (or export.txt doesn't exist), the entire contents of the library folder will be copied during export.


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 'poo' set up a folder as follows:

poo → 
  library →
    poo.jar


Then, the folder should be placed in the libraries folder inside the user's sketchbook folder to be recognized by Processing and its "Import Library" menu. You must first restart Processing in order to use the library.

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).


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 a command line zip utility:
zip -r core.jar processing


The "Import Library" Menu Item

All "Import Library" 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".


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 createInput() 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). Similarly, to get an OutputStream for writing files, use createOutput().

The functions sketchPath(), sketchFile(), savePath(), dataPath(), and createPath() also 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 as part of the developer's reference for PApplet. The variable sketchPath is also available for convenience, but in nearly all cases, the sketchPath() method is a better (and more compatible) route. The sketchFile() method works like sketchPath(), except that it wraps the path as a java.io.File object instead of the full path as a String.


Getting an UnsupportedClassVersionError?

Processing currently uses Java 1.5. If you compile your library code with a later Java release, it may be incompatible with the Java 1.5 class file format, which will give users a message like the following:
java.lang.UnsupportedClassVersionError: 
blah/SomeClass (Unsupported major.minor version 49.0) 
 
This is because later versions of Java use their own class file format that's not backwards compatible.

When compiling a library, use something like:
  javac -source 1.5 -target 1.5 -d . \ 
    -classpath /path/to/core.jar path/to/java/source/*.java


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.











 
  Processing is an open project initiated by Ben Fry and Casey Reas  
  © Info \ Site hosted by Media Temple!