diff --git a/src/java/org/lwjgl/input/Cursor.java b/src/java/org/lwjgl/input/Cursor.java index a10e69f2..c2714616 100644 --- a/src/java/org/lwjgl/input/Cursor.java +++ b/src/java/org/lwjgl/input/Cursor.java @@ -49,92 +49,186 @@ import org.lwjgl.Sys; */ public class Cursor { + + /** Lazy initialization */ + private static boolean initialized = false; + + /** First element to display */ + private CursorElement[] cursors = null; + + /** Index into list of cursors */ + private int index = -1; + + /** + * Constructs a new Cursor, with the given parameters. Mouse must have been created before you can create + * Cursor objects. Cursor images are in ARGB format, but only one bit transparancy is guaranteed to be supported. + * So to maximize portability, lwjgl applications should only create cursor images with 0x00 or 0xff as alpha values. + * The constructor will copy the images and delays, so there's no need to keep them around. + * + * @param width cursor image width + * @param height cursor image height + * @param xHotspot the x coordinate of the cursor hotspot + * @param yHotspot the y coordinate of the cursor hotspot + * @param numImages number of cursor images specified. Must be 1 if animations are not supported. + * @param images A buffer containing the images. The origin is at the lower left corner, like OpenGL. + * @param delays An int buffer of animation frame delays, if numImages is greater than 1, else null + * @throws Exception if the cursor could not be created for any reason + */ + public Cursor(int width, int height, int xHotspot, int yHotspot, int numImages, IntBuffer images, IntBuffer delays) throws Exception { + assert Mouse.isCreated(); + assert width*height*numImages <= images.remaining() : "width*height*numImages > images.remaining()"; + assert delays == null || numImages <= delays.remaining() : "delays != null && numImages > delays.remaining()"; + assert xHotspot < width && xHotspot >= 0 : "xHotspot > width || xHotspot < 0"; + assert yHotspot < height && yHotspot >= 0 : "yHotspot > height || yHotspot < 0"; + + // initialize + if (!initialized) { + initialize(); + } + + // Hmm + yHotspot = height - 1 - yHotspot; + + // create cursor (or cursors if multiple images supplied) + createCursors(width, height, xHotspot, yHotspot, numImages, images, delays); + } + + /** + * Initializes the cursor class + */ + private static void initialize() { + System.loadLibrary(Sys.getLibraryName()); + initialized = true; + } - /** Lazy initialization */ - private static boolean initialized = false; + /** + * Creates the actual cursor, using a platform specific class + */ + private void createCursors(int width, int height, int xHotspot, int yHotspot, int numImages, IntBuffer images, IntBuffer delays) throws Exception { + // create copy and flip images to match ogl + IntBuffer images_copy = ByteBuffer.allocateDirect(images.remaining()*4).order(ByteOrder.nativeOrder()).asIntBuffer(); + flipImages(width, height, numImages, images, images_copy); + + // create our cursor elements + cursors = new CursorElement[numImages]; + for(int i=0; i>1; y++) { + int index_y_1 = y*width + start_index; + int index_y_2 = (height - y - 1)*width + start_index; + for (int x = 0; x < width; x++) { + int index1 = index_y_1 + x; + int index2 = index_y_2 + x; + int temp_pixel = images.get(index1 + images.position()); + images_copy.put(index1, images.get(index2 + images.position())); + images_copy.put(index2, temp_pixel); + } + } + } + + /** + * Gets the native handle associated with the cursor object. + */ + public long getHandle() { + return cursors[index].cursorHandle; + } + + /** + * Destroy the native cursor. Cursor must not be current. + */ + public void destroy() { + for(int i=0; i images.remaining()"; - assert delays == null || numImages <= delays.remaining(): "delays != null && numImages > delays.remaining()"; - assert xHotspot < width && xHotspot >= 0: "xHotspot > width || xHotspot < 0"; - assert yHotspot < height && yHotspot >= 0: "yHotspot > height || yHotspot < 0"; - IntBuffer images_copy = ByteBuffer.allocateDirect(images.remaining()*4).order(ByteOrder.nativeOrder()).asIntBuffer(); - flipImages(width, height, numImages, images, images_copy); - nativeHandle = nCreateCursor(width, height, xHotspot, yHotspot, numImages, images_copy, 0, delays, delays != null ? delays.position() : 0); - } - - private static void initialize() { - System.loadLibrary(Sys.getLibraryName()); - initialized = true; - } + /** + * Sets the timout property to the time it should be changed + */ + protected void setTimeout() { + cursors[index].timeout = System.currentTimeMillis() + cursors[index].delay; + } - private static void flipImages(int width, int height, int numImages, IntBuffer images, IntBuffer images_copy) { - for (int i = 0; i < numImages; i++) { - int start_index = i*width*height; - flipImage(width, height, start_index, images, images_copy); - } - } + /** + * Determines whether this cursor has timed out + * @return true if the this cursor has timed out, false if not + */ + protected boolean hasTimedOut() { + return cursors.length > 1 && cursors[index].timeout < System.currentTimeMillis(); + } - private static void flipImage(int width, int height, int start_index, IntBuffer images, IntBuffer images_copy) { - for (int y = 0; y < height>>1; y++) { - int index_y_1 = y*width + start_index; - int index_y_2 = (height - y - 1)*width + start_index; - for (int x = 0; x < width; x++) { - int index1 = index_y_1 + x; - int index2 = index_y_2 + x; - int temp_pixel = images.get(index1 + images.position()); - images_copy.put(index1, images.get(index2 + images.position())); - images_copy.put(index2, temp_pixel); - } - } - } + /** + * Changes to the next cursor + */ + protected void nextCursor() { + index = ++index % cursors.length; + } - /** - * Destroy the native cursor. Cursor must not be current. - */ - public void destroy() { - nDestroyCursor(nativeHandle); - } + /** + * Resets the index of the cursor animation to the first in the list. + */ + public void resetAnimation() { + index = 0; + } + + /** + * Native method to create a native cursor + */ + private static native long nCreateCursor(int width, int height, int xHotspot, int yHotspot, int numImages, IntBuffer images, int images_offset); - /** - * Gets the native handle associated with the cursor object. - */ - public long getHandle() { - return nativeHandle; - } - - /** - * Native method to create a native cursor - */ - private static native long nCreateCursor(int width, int height, int xHotspot, int yHotspot, int numImages, IntBuffer images, int images_offset, IntBuffer delays, int delays_offset); - - /** - * Native method to destroy a native cursor - */ - private static native void nDestroyCursor(long handle); + /** + * Native method to destroy a native cursor + */ + private static native void nDestroyCursor(long cursorHandle); + + /** + * A single cursor element, used when animating + */ + protected class CursorElement { + /** Handle to cursor */ + long cursorHandle; + + /** How long a delay this element should have */ + long delay; + + /** Absolute time this element times out */ + long timeout; + } } diff --git a/src/java/org/lwjgl/input/Mouse.java b/src/java/org/lwjgl/input/Mouse.java index ed1d25c5..5011f547 100644 --- a/src/java/org/lwjgl/input/Mouse.java +++ b/src/java/org/lwjgl/input/Mouse.java @@ -169,6 +169,7 @@ public class Mouse { currentCursor = cursor; if (currentCursor != null) { nSetNativeCursor(currentCursor.getHandle()); + currentCursor.setTimeout(); } else { nSetNativeCursor(0); } @@ -511,4 +512,18 @@ public class Mouse { public static boolean hasWheel() { return hasWheel; } + + /** + * Updates the cursor, so that animation can be changed if needed. + * This method is called automatically by the window on its update. + */ + public static void updateCursor() { + if(currentCursor != null && currentCursor.hasTimedOut()) { + currentCursor.nextCursor(); + try { + setNativeCursor(currentCursor); + } catch (Exception e) { + } + } + } } diff --git a/src/java/org/lwjgl/opengl/Window.java b/src/java/org/lwjgl/opengl/Window.java index c08cda24..23f7c0ff 100644 --- a/src/java/org/lwjgl/opengl/Window.java +++ b/src/java/org/lwjgl/opengl/Window.java @@ -49,6 +49,7 @@ package org.lwjgl.opengl; import org.lwjgl.Display; import org.lwjgl.Sys; +import org.lwjgl.input.Mouse; public final class Window { @@ -352,7 +353,15 @@ public final class Window { * Updates the windows internal state. This must be called at least once per video frame * to handle window close requests, moves, paints, etc. */ - public static native void update(); + public static void update() { + nUpdate(); + + // notify Mouse that we've had an update, so that it may update its cursor if needed + if(Mouse.isCreated()) { + Mouse.updateCursor(); + } + } + public static native void nUpdate(); /** * Determines to the best of the platform's ability whether monitor vysnc is enabled on diff --git a/src/java/org/lwjgl/test/input/HWCursorTest.java b/src/java/org/lwjgl/test/input/HWCursorTest.java index 63d238b3..205889a7 100644 --- a/src/java/org/lwjgl/test/input/HWCursorTest.java +++ b/src/java/org/lwjgl/test/input/HWCursorTest.java @@ -58,11 +58,12 @@ public class HWCursorTest { private DisplayMode mode; /** The native cursor */ - private static Cursor cursor = null; + private static Cursor[] cursor = null; /** The mouse cursor position */ private static int mouse_x; private static int mouse_y; + private static int mouse_btn = 0; /** * Executes the test @@ -89,62 +90,116 @@ public class HWCursorTest { glInit(); Keyboard.create(); - initNativeCursor(); + Mouse.create(); + initNativeCursors(); } catch (Exception e) { e.printStackTrace(); } } - private static void initNativeCursor() { - try { - Mouse.create(); - } catch (Exception e) { - e.printStackTrace(); - } + private static void initNativeCursors() throws Exception { if ((Mouse.getNativeCursorCaps() & Mouse.CURSOR_ONE_BIT_TRANSPARENCY) == 0) { System.out.println("No HW cursor support!"); System.exit(0); } - System.out.println("Maximum native cursor size: " + Mouse.getMaxCursorSize() + ", min size: " + Mouse.getMinCursorSize()); + + cursor = new Cursor[3]; + + // center mouse_x = 400; mouse_y = 300; - int num_images = 3; - int image_size = Mouse.getMaxCursorSize()*Mouse.getMaxCursorSize(); - IntBuffer cursor_images = ByteBuffer.allocateDirect(num_images*image_size*4).order(ByteOrder.nativeOrder()).asIntBuffer(); - IntBuffer delays = ByteBuffer.allocateDirect(num_images*4).order(ByteOrder.nativeOrder()).asIntBuffer(); - delays.put(0, 500); - delays.put(1, 500); - delays.put(2, 500); - int color_scale = 255/Mouse.getMaxCursorSize(); - int bit_mask = 0x81000000; - for (int j = 0; j < image_size; j++) { - if (j % 4 == 0) - bit_mask = (~bit_mask) & 0x81000000; - int color = (j*color_scale/Mouse.getMaxCursorSize()) << 16; - cursor_images.put(0*image_size + j, 0x00000020 | color | bit_mask); + + int cursorImageCount = 1; + int cursorWidth = Mouse.getMaxCursorSize(); + int cursorHeight = cursorWidth; + IntBuffer cursorImages; + IntBuffer cursorDelays; + + + // Create a single cursor + // ================================== + cursorImages = ByteBuffer.allocateDirect(cursorWidth*cursorHeight*cursorImageCount*4).order(ByteOrder.nativeOrder()).asIntBuffer(); + cursorDelays = null; + for(int j=0; j= centerLeft && j < centerRight && l >= centerLeft && l < centerRight) { + cursorImages.put(offColor); + } else { + cursorImages.put(onColor); + } + } + } } - for (int j = 0; j < image_size; j++) { - if (j % 4 == 0) - bit_mask = (~bit_mask) & 0x81000000; - int color = (j*color_scale/Mouse.getMaxCursorSize()); - cursor_images.put(2*image_size + j, 0x00000020 | color | bit_mask); - } - try { - if ((Mouse.getNativeCursorCaps() | Mouse.CURSOR_ANIMATION) == 0) - num_images = 1; - cursor = new Cursor(Mouse.getMaxCursorSize(), Mouse.getMaxCursorSize(), Mouse.getMaxCursorSize()/2, Mouse.getMaxCursorSize()/2, num_images, cursor_images, delays); - Mouse.setNativeCursor(cursor); - } catch (Exception e) { - e.printStackTrace(); - System.exit(1); + cursorDelays.put(2000).put(2000).put(2000); + cursorDelays.flip(); + cursorImages.flip(); + + cursor[1] = new Cursor(Mouse.getMaxCursorSize(), Mouse.getMaxCursorSize(), Mouse.getMaxCursorSize()/2, Mouse.getMaxCursorSize()/2, cursorImageCount, cursorImages, cursorDelays); + // ---------------------------------- + + + // Create a 20 piece animation + // ================================== + cursorImageCount = 20; + cursorImages = ByteBuffer.allocateDirect(cursorWidth*cursorHeight*cursorImageCount*4).order(ByteOrder.nativeOrder()).asIntBuffer(); + cursorDelays = ByteBuffer.allocateDirect(cursorImageCount*4).order(ByteOrder.nativeOrder()).asIntBuffer(); + cursorDelays.put( + new int[] { + 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100 + }); + + float step = 0xffffffff / 20.0f; + for(int i=0; iGetDirectBufferAddress(delay_buffer) + delays_offset; XcursorPixel *pixels = (XcursorPixel *)env->GetDirectBufferAddress(image_buffer) + images_offset; int stride = width*height; XcursorImages *cursor_images = XcursorImagesCreate(num_images); @@ -66,8 +63,6 @@ JNIEXPORT jlong JNICALL Java_org_lwjgl_input_Cursor_nCreateCursor cursor_image->xhot = x_hotspot; cursor_image->yhot = y_hotspot; cursor_image->pixels = &(pixels[stride*i]); - if (num_images > 1) - cursor_image->delay = delays[i]; cursor_images->images[i] = cursor_image; } Cursor cursor = XcursorImagesLoadCursor(getCurrentDisplay(), cursor_images); diff --git a/src/native/linux/org_lwjgl_opengl_Window.cpp b/src/native/linux/org_lwjgl_opengl_Window.cpp index 4dd86b42..4a23f8ea 100644 --- a/src/native/linux/org_lwjgl_opengl_Window.cpp +++ b/src/native/linux/org_lwjgl_opengl_Window.cpp @@ -260,7 +260,7 @@ int getWindowHeight(void) { * Method: update * Signature: ()V */ -JNIEXPORT void JNICALL Java_org_lwjgl_opengl_Window_update +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_Window_nUpdate (JNIEnv *env, jclass clazz) { handleMessages(); diff --git a/src/native/macosx/org_lwjgl_input_Cursor.cpp b/src/native/macosx/org_lwjgl_input_Cursor.cpp index 14979aa9..c44ca570 100644 --- a/src/native/macosx/org_lwjgl_input_Cursor.cpp +++ b/src/native/macosx/org_lwjgl_input_Cursor.cpp @@ -41,7 +41,7 @@ #include "org_lwjgl_input_Cursor.h" -JNIEXPORT jlong JNICALL Java_org_lwjgl_input_Cursor_nCreateCursor(JNIEnv *env, jclass clazz, jint width, jint height, jint x_hotspot, jint y_hotspot, jint num_images, jobject image_buffer, jint images_offset, jobject delay_buffer, jint delays_offset) { +JNIEXPORT jlong JNICALL Java_org_lwjgl_input_Cursor_nCreateCursor(JNIEnv *env, jclass clazz, jint width, jint height, jint x_hotspot, jint y_hotspot, jint num_images, jobject image_buffer, jint images_offset) { } JNIEXPORT void JNICALL Java_org_lwjgl_input_Cursor_nDestroyCursor(JNIEnv *env, jclass clazz, jlong cursor_handle) { diff --git a/src/native/macosx/org_lwjgl_opengl_Window.cpp b/src/native/macosx/org_lwjgl_opengl_Window.cpp index fa022150..76c3f342 100644 --- a/src/native/macosx/org_lwjgl_opengl_Window.cpp +++ b/src/native/macosx/org_lwjgl_opengl_Window.cpp @@ -131,7 +131,7 @@ JNIEXPORT void JNICALL Java_org_lwjgl_opengl_Window_nCreate(JNIEnv *env, jclass JNIEXPORT void JNICALL Java_org_lwjgl_opengl_Window_nSetTitle(JNIEnv * env, jclass clazz, jstring title_obj) { } -JNIEXPORT void JNICALL Java_org_lwjgl_opengl_Window_update(JNIEnv *env, jclass clazz) { +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_Window_nUpdate(JNIEnv *env, jclass clazz) { EventRef event; OSStatus err = ReceiveNextEvent(0, NULL, 0, true, &event); if (err == noErr) { diff --git a/src/native/win32/org_lwjgl_input_Cursor.cpp b/src/native/win32/org_lwjgl_input_Cursor.cpp index 12b6ee41..625375bd 100755 --- a/src/native/win32/org_lwjgl_input_Cursor.cpp +++ b/src/native/win32/org_lwjgl_input_Cursor.cpp @@ -50,7 +50,7 @@ * Signature: (IIIIIIIII)I */ JNIEXPORT jlong JNICALL Java_org_lwjgl_input_Cursor_nCreateCursor - (JNIEnv *env, jclass clazz, jint width, jint height, jint x_hotspot, jint y_hotspot, jint num_images, jobject image_buffer, jint images_offset, jobject delay_buffer, jint delays_offset) + (JNIEnv *env, jclass clazz, jint width, jint height, jint x_hotspot, jint y_hotspot, jint num_images, jobject image_buffer, jint images_offset) { int *pixels = (int *)env->GetDirectBufferAddress(image_buffer) + images_offset; diff --git a/src/native/win32/org_lwjgl_opengl_Window.cpp b/src/native/win32/org_lwjgl_opengl_Window.cpp index 3ab86b13..2f32dcc6 100755 --- a/src/native/win32/org_lwjgl_opengl_Window.cpp +++ b/src/native/win32/org_lwjgl_opengl_Window.cpp @@ -470,7 +470,7 @@ JNIEXPORT void JNICALL Java_org_lwjgl_opengl_Window_nSetTitle * Method: update * Signature: ()V */ -JNIEXPORT void JNICALL Java_org_lwjgl_opengl_Window_update +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_Window_nUpdate (JNIEnv * env, jclass clazz) { handleMessages(env, clazz);