Processing: Locking the mouse
As part of an example for my game development students (all of my examples are in Processing), I needed to be able to get not just the mouse position, but mouse movement deltas, whenever the mouse was moved (I wanted to build an FPS-style mouselook system for them to play with). In some systems, you can access the raw offsets that are coming from the mouse, but Java (AWT and JOGL) are not one of those systems. Instead, the accepted method is to constrain the mouse cursor to the window, hide the mouse cursor, and then, every time the mouse moves, reset its position to the the center of the window. So long as the user never moves the mouse fast enough to hit the edges of the window, this will have the same effect as getting the raw data.
For a full-screen sketch, this is relatively easy, regardless of the renderer
in use. You can
use a java.awt.Robot
to set the mouse position to
(displayWidth/2,displayHeight/2)
in the mouseMoved
handler.
For a windowed sketch, things are more difficult. A robot will set the mouse position in screen coordinates, not window coordinates, so in order to center the mouse cursor in the window, you have to know the screen position of it (along with the size of any chrome around the edges of the window). This turns out to be surprisingly difficult to get, particularly if you’re using the JAVA2D renderer.
(I should mention that this method has only been tested with Processing 3.0.1 248. Due to the propensity of Processing’s developers for breaking backwards compatibility, it’s entirely possible that everything I’m saying here will be obsolete next week.)
Processing sketches used to have a global frame
variable containing a
reference to the AWT frame containing the sketch. As of P3.0, the variable
still exists, but it’s not a real frame. A few of its methods have been
overloaded to give error messages (intended to let the user know that its
use is deprecated), but getLocation()
always returns (0,0)
, while
getLocationOnScreen()
throws an exception (because the frame is not part of
a displayed window hierarchy). In my mind, it would have been better to leave
frame
set to null
, so that any use of it would trigger an error.
The trick then is to figure out how to talk to the “real” window containing the sketch. The method varies depending on whether you are using P2D or P3D:
For JAVA2D, the value of the global surface
variable will be a PSurfaceAWT
, which
exposes a getFrame()
method returning the normal AWT frame containing the
sketch. Thus, you can do:
Point p = surface.getFrame().getLocationOnScreen();
robot.setMousePosition(p.x + width/2, p.y + height/2);
(There’s also a new (?) 2D renderer based on JavaFX; I haven’t looked at what kind of window it sets up.)
For P3D (and possibly P2D?) we’re in better luck, as the window is actually handled by JOGL, which
has some support routines that do what we want directly. surface.getNative()
will return a GLWindow
which exposes public methods:
confinePointer(boolean)
– to restrict the pointer to the windowwarpPointer(int,int)
– to re-center the pointer position (note that this takes window coordinates!)setPointerVisible(boolean)
– to hide the mouse cursor
So we can just do
import com.jogamp.newt.opengl.GLWindow;
...
GLWindow r = (GLWindow)surface.getNative();
r.confinePointer(true);
r.setPointerVisible(false);
...
r.warpPointer(width/2,height/2);
Processing has a “libraries” browser that lets you search for libraries other people have written, but what it really needs is an examples browser, with some kind of automated testing to make sure that examples continue to work in newer versions.