From e886d951225c8641850c2267bb274df0f9a78a5d Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Fri, 16 May 2003 18:39:46 +0000 Subject: [PATCH] Native cursors implemented --- src/java/org/lwjgl/input/Cursor.java | 91 +++++ src/java/org/lwjgl/input/Mouse.java | 90 ++++- .../org/lwjgl/test/input/HWCursorTest.java | 361 ++++++++++++++++++ src/native/common/extgl.c | 1 - src/native/common/org_lwjgl_input_Cursor.h | 31 ++ src/native/common/org_lwjgl_input_Mouse.h | 35 +- src/native/linux/Makefile.am | 5 +- src/native/linux/Window.h | 15 + src/native/linux/extxcursor.cpp | 63 +++ src/native/linux/extxcursor.h | 51 +++ src/native/linux/org_lwjgl_Window.cpp | 35 +- src/native/linux/org_lwjgl_input_Cursor.cpp | 45 +++ src/native/linux/org_lwjgl_input_Keyboard.cpp | 36 +- src/native/linux/org_lwjgl_input_Mouse.cpp | 201 +++++++--- src/native/win32/Window.h | 4 +- src/native/win32/org_lwjgl_Window.cpp | 20 +- .../win32/org_lwjgl_input_Controller.cpp | 68 ++-- src/native/win32/org_lwjgl_input_Cursor.cpp | 111 ++++++ src/native/win32/org_lwjgl_input_Keyboard.cpp | 9 +- src/native/win32/org_lwjgl_input_Mouse.cpp | 317 ++++++++++----- 20 files changed, 1351 insertions(+), 238 deletions(-) create mode 100644 src/java/org/lwjgl/input/Cursor.java create mode 100644 src/java/org/lwjgl/test/input/HWCursorTest.java create mode 100644 src/native/common/org_lwjgl_input_Cursor.h create mode 100644 src/native/linux/extxcursor.cpp create mode 100644 src/native/linux/extxcursor.h create mode 100644 src/native/linux/org_lwjgl_input_Cursor.cpp create mode 100755 src/native/win32/org_lwjgl_input_Cursor.cpp diff --git a/src/java/org/lwjgl/input/Cursor.java b/src/java/org/lwjgl/input/Cursor.java new file mode 100644 index 00000000..8a9bcc64 --- /dev/null +++ b/src/java/org/lwjgl/input/Cursor.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2002 Lightweight Java Game Library Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'Light Weight Java Game Library' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.lwjgl.input; + +/** + * $Id$ + * + * A class representing a native cursor. Instances of this + * class can be used with Mouse.setCursor(), if available. + * + * @author elias_naur + * @version $Revision$ + */ + +public class Cursor { + /** + * The native handle to the cursor + */ + private final int nativeHandle; + + /** + * 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. + * + * @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 cursorAddress the address of an int array containing the cursor image + * @throws Exception if the cursor could not be created for any reason + */ + public Cursor(int width, int height, int xHotspot, int yHotspot, int imageAddress) throws Exception { + assert Mouse.isCreated(); + nativeHandle = nCreateCursor(width, height, xHotspot, yHotspot, imageAddress); + } + + /** + * Destroy the native cursor. Cursor must not be current. + */ + public void destroy() { + nDestroyCursor(nativeHandle); + } + + /** + * Gets the native handle associated with the cursor object. + */ + public int getHandle() { + return nativeHandle; + } + + /** + * Native method to create a native cursor + */ + private static native int nCreateCursor(int width, int height, int xHotspot, int yHotspot, int imageAddresses); + + /** + * Native method to destroy a native cursor + */ + private static native void nDestroyCursor(int handle); +} diff --git a/src/java/org/lwjgl/input/Mouse.java b/src/java/org/lwjgl/input/Mouse.java index 306ec6a8..2e346a5b 100644 --- a/src/java/org/lwjgl/input/Mouse.java +++ b/src/java/org/lwjgl/input/Mouse.java @@ -70,6 +70,9 @@ public class Mouse { /** Does this mouse support a scroll wheel */ public static boolean hasWheel = false; + /** The current native cursor, if any */ + private static Cursor currentCursor; + static { initialize(); } @@ -80,6 +83,83 @@ public class Mouse { private Mouse() { } + /** + * Gets the currently bound native cursor, if any. + * + * @return the currently bound native cursor, if any. + public Cursor getNativeCursor() { + return currentCursor; + } + + /** + * Check native cursor support + * @return true if native cursors are supported + */ + public static boolean isNativeCursorSupported() { + return nIsNativeCursorSupported(); + } + + /** + * Native function to determine native cursor support + */ + private static native boolean nIsNativeCursorSupported(); + + /** + * Binds a native cursor. If the cursor argument is null, the + * native cursor is disabled, as if native cursors were not supported. + * The Mouse must be created before a native cursor can be bound. + * + * NOTE: The native cursor is not constrained to the window, but + * relative events will not be generated if the cursor is outside. + * The initial position of the cursor is in the upper left corner of + * the window, and the cursor will be moved to this origin when a + * native cursor is set and the previous cursor is null. + * + * @param cursor the native cursor object to bind. May be null. + * @return The previous Cursor object set, or null. + * @throws Exception if the cursor could not be set for any reason + */ + public static Cursor setNativeCursor(Cursor cursor) throws Exception { + assert created && isNativeCursorSupported(); + Cursor oldCursor = currentCursor; + currentCursor = cursor; + if (currentCursor != null) { + nSetNativeCursor(currentCursor.getHandle()); + } else { + nSetNativeCursor(Sys.NULL); + } + return oldCursor; + } + + /** Native method to set the native cursor */ + private static native void nSetNativeCursor(int handle); + + /** + * Gets the minimum size of a native cursor. Can only be called if + * The Mouse is created and isNativeCursorSupported() returns true + * + * @return the maximum size of a native cursor + */ + public static int getMinCursorSize() { + return nGetMinCursorSize(); + } + + /** Native method returning the minimum cursor size */ + private static native int nGetMinCursorSize(); + + /** + * Gets the maximum size of a native cursor. Can only be called if + * The Mouse is created and isNativeCursorSupported() returns true + * + * @return the maximum size of a native cursor + */ + public static int getMaxCursorSize() { + return nGetMaxCursorSize(); + } + + /** Native method returning the maximum cursor size */ + private static native int nGetMaxCursorSize(); + /** * Static initialization */ @@ -104,13 +184,14 @@ public class Mouse { if (!nCreate()) throw new Exception("The mouse could not be created."); created = true; + currentCursor = null; //set mouse buttons buttons = new boolean[buttonCount]; } /** - * Native method to create the mouse + * Native method to create the mouse. * * @return true if the mouse was created */ @@ -124,13 +205,16 @@ public class Mouse { } /** - * "Destroy" the mouse + * "Destroy" the mouse. Remember to reset the native cursor if + * setNativeCursor() has been called with anything else than null. */ public static void destroy() { + assert currentCursor == null; if (!created) return; created = false; buttons = null; + currentCursor = null; nDestroy(); } @@ -163,4 +247,4 @@ public class Mouse { assert created : "The mouse has not been created."; return buttons[button]; } -} \ No newline at end of file +} diff --git a/src/java/org/lwjgl/test/input/HWCursorTest.java b/src/java/org/lwjgl/test/input/HWCursorTest.java new file mode 100644 index 00000000..3ba059d8 --- /dev/null +++ b/src/java/org/lwjgl/test/input/HWCursorTest.java @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2003 Lightweight Java Game Library Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'Lightweight Java Game Library' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.lwjgl.test.input; + +import org.lwjgl.*; +import org.lwjgl.input.*; +import org.lwjgl.opengl.*; +import org.lwjgl.vector.Vector2f; + +import java.nio.*; + +/** + * $Id$ + * + * Tests switching between windowed and fullscreen - including hardware cursor test + * + * @author Brian Matzon + * @version $Revision$ + */ +public class HWCursorTest { + + /** Intended deiplay mode */ + private DisplayMode mode; + + /** GL instance */ + private GL gl; + + /** GLU instance */ + private GLU glu; + + /** The native cursor */ + private static Cursor cursor = null; + + /** The mouse cursor position */ + private static int mouse_x; + private static int mouse_y; + + /** + * Executes the test + */ + public void execute() { + initialize(); + + mainLoop(); + + cleanup(); + } + + /** + * Initializes the test + */ + private void initialize() { + try { + //find displaymode + mode = findDisplayMode(800, 600, 16); + + // start of in windowed mode + gl = new GL("Test", 50, 50, mode.width, mode.height, mode.bpp, 0, 0, 0); + gl.create(); + glu = new GLU(gl); + + glInit(); + + Keyboard.create(); + initNativeCursor(); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static void initNativeCursor() { + try { + Mouse.create(); + } catch (Exception e) { + e.printStackTrace(); + } + if (!Mouse.isNativeCursorSupported()) { + System.out.println("No HW cursor support!"); + System.exit(0); + } + System.out.println("Maximum native cursor size: " + Mouse.getMaxCursorSize() + ", min size: " + Mouse.getMinCursorSize()); + mouse_x = mouse_y = 0; +// 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); + } +/* for (int j = 0; j < image_size; j++) { + int color = (j*color_scale/Mouse.getMaxCursorSize()) << 8; + cursor_images.put(1*image_size + j, 0x80000000 | color); + } + for (int j = 0; j < image_size; j++) { + int color = j*color_scale/Mouse.getMaxCursorSize(); + cursor_images.put(2*image_size + j, 0x80000000 | color); + }*/ + try { + cursor = new Cursor(Mouse.getMaxCursorSize(), Mouse.getMaxCursorSize(), Mouse.getMaxCursorSize()/2, Mouse.getMaxCursorSize()/2/*, num_images*/, Sys.getDirectBufferAddress(cursor_images)/*, Sys.getDirectBufferAddress(delays)*/); + Mouse.setNativeCursor(cursor); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } + + /** + * Runs the main loop of the "test" + */ + private void mainLoop() { + while (!Keyboard.isKeyDown(Keyboard.KEY_ESCAPE) + && !gl.isCloseRequested()) { + // allow subsystem to get a chance to run too + gl.tick(); + + if (!gl.isMinimized()) { + // check keyboard input + processKeyboard(); + + render(); + + // paint window + gl.paint(); + } else { + + // no need to render/paint if nothing has changed (ie. window dragged over) + if (gl.isDirty()) { + render(); + gl.paint(); + } + + // don't waste cpu time, sleep more + try { + Thread.sleep(100); + } catch (InterruptedException inte) { + } + } + } + } + + /** + * Performs the logic + */ + private void render() { + //clear background + gl.clear(GL.COLOR_BUFFER_BIT); + + // draw white quad + gl.pushMatrix(); + { + gl.translatef(mouse_x, 600 - mouse_y, 0); + gl.color3f(1.0f, 1.0f, 1.0f); + gl.begin(GL.QUADS); + { + gl.vertex2i(-50, -50); + gl.vertex2i(50, -50); + gl.vertex2i(50, 50); + gl.vertex2i(-50, 50); + } + gl.end(); + } + gl.popMatrix(); + } + + /** + * Processes keyboard input + */ + private void processKeyboard() { + Keyboard.poll(); + Mouse.poll(); + + if (Mouse.dx != 0 || Mouse.dy != 0) { + mouse_x += Mouse.dx; + mouse_y += Mouse.dy; + System.out.println("mouse_x " + mouse_x + " mouse_y " + mouse_y); + } + + //check for fullscreen key + if (Keyboard.isKeyDown(Keyboard.KEY_F)) { + + try { + Keyboard.destroy(); + try { + Mouse.setNativeCursor(null); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + cursor.destroy(); + Mouse.destroy(); + gl.destroy(); + + Display.setDisplayMode(mode); + gl = new GL("Test", mode.bpp, 0, 0, 0); + gl.create(); + glu = new GLU(gl); + + glInit(); + + Keyboard.create(); + initNativeCursor(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + //check for window key + if (Keyboard.isKeyDown(Keyboard.KEY_W)) { + try { + Keyboard.destroy(); + try { + Mouse.setNativeCursor(null); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + cursor.destroy(); + Mouse.destroy(); + gl.destroy(); + + Display.resetDisplayMode(); + gl = new GL("Test", 50, 50, mode.width, mode.height, mode.bpp, 0, 0, 0); + gl.create(); + glu = new GLU(gl); + + glInit(); + + Keyboard.create(); + initNativeCursor(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + if (Keyboard.isKeyDown(Keyboard.KEY_M)) { + try { + Mouse.setNativeCursor(null); + } catch (Exception e) { + e.printStackTrace(); + } + } + + if (Keyboard.isKeyDown(Keyboard.KEY_N)) { + try { + Mouse.setNativeCursor(cursor); + mouse_x = mouse_y = 0; + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + /** + * Cleans up the test + */ + private void cleanup() { + Keyboard.destroy(); + try { + Mouse.setNativeCursor(null); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + cursor.destroy(); + Mouse.destroy(); + Display.resetDisplayMode(); + gl.destroy(); + } + + /** + * Retrieves a displaymode, if one such is available + * + * @param width Required width + * @param height Required height + * @param bpp Minimum required bits per pixel + * @return + */ + private DisplayMode findDisplayMode(int width, int height, int bpp) { + DisplayMode[] modes = Display.getAvailableDisplayModes(); + for (int i = 0; i < modes.length; i++) { + if (modes[i].width == width + && modes[i].height == height + && modes[i].bpp >= bpp) { + return modes[i]; + } + } + return null; + } + + /** + * Initializes OGL + */ + private void glInit() { + // Go into orthographic projection mode. + gl.determineAvailableExtensions(); + gl.matrixMode(GL.PROJECTION); + gl.loadIdentity(); + glu.ortho2D(0, mode.width, 0, mode.height); + gl.matrixMode(GL.MODELVIEW); + gl.loadIdentity(); + gl.viewport(0, 0, mode.width, mode.height); + + //set clear color to black + gl.clearColor(0.0f, 0.0f, 0.0f, 0.0f); + + //sync frame (only works on windows) + if (GL.WGL_EXT_swap_control) { + GL.wglSwapIntervalEXT(1); + } + } + + /** + * Test entry point + */ + public static void main(String[] args) { + System.out.println( + "Change between fullscreen and windowed mode, by pressing F and W respectively. Enable hw cursor with N and disable it with M."); + System.out.println( + "Move quad using arrowkeys, and change rotation using +/-"); + HWCursorTest cursorTest = new HWCursorTest(); + cursorTest.execute(); + } +} diff --git a/src/native/common/extgl.c b/src/native/common/extgl.c index b760787e..aac5ec07 100644 --- a/src/native/common/extgl.c +++ b/src/native/common/extgl.c @@ -3331,7 +3331,6 @@ int extgl_Open(void) void extgl_Close(void) { #ifdef _X11 - printf("closing handles\n"); dlclose(lib_glu_handle); dlclose(lib_gl_handle); #endif diff --git a/src/native/common/org_lwjgl_input_Cursor.h b/src/native/common/org_lwjgl_input_Cursor.h new file mode 100644 index 00000000..645b1efc --- /dev/null +++ b/src/native/common/org_lwjgl_input_Cursor.h @@ -0,0 +1,31 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class org_lwjgl_input_Cursor */ + +#ifndef _Included_org_lwjgl_input_Cursor +#define _Included_org_lwjgl_input_Cursor +#ifdef __cplusplus +extern "C" { +#endif +/* Inaccessible static: _00024assertionsDisabled */ +/* Inaccessible static: class_00024org_00024lwjgl_00024input_00024Cursor */ +/* + * Class: org_lwjgl_input_Cursor + * Method: nCreateCursor + * Signature: (IIIII)I + */ +JNIEXPORT jint JNICALL Java_org_lwjgl_input_Cursor_nCreateCursor + (JNIEnv *, jclass, jint, jint, jint, jint, jint); + +/* + * Class: org_lwjgl_input_Cursor + * Method: nDestroyCursor + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_org_lwjgl_input_Cursor_nDestroyCursor + (JNIEnv *, jclass, jint); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/native/common/org_lwjgl_input_Mouse.h b/src/native/common/org_lwjgl_input_Mouse.h index 7e3e3cfd..d5dc07ca 100644 --- a/src/native/common/org_lwjgl_input_Mouse.h +++ b/src/native/common/org_lwjgl_input_Mouse.h @@ -15,7 +15,40 @@ extern "C" { /* Inaccessible static: dwheel */ /* Inaccessible static: buttonCount */ /* Inaccessible static: hasWheel */ -/* Inaccessible static: class_000240 */ +/* Inaccessible static: currentCursor */ +/* Inaccessible static: class_00024org_00024lwjgl_00024input_00024Mouse */ +/* + * Class: org_lwjgl_input_Mouse + * Method: nIsNativeCursorSupported + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_org_lwjgl_input_Mouse_nIsNativeCursorSupported + (JNIEnv *, jclass); + +/* + * Class: org_lwjgl_input_Mouse + * Method: nSetNativeCursor + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_org_lwjgl_input_Mouse_nSetNativeCursor + (JNIEnv *, jclass, jint); + +/* + * Class: org_lwjgl_input_Mouse + * Method: nGetMinCursorSize + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_org_lwjgl_input_Mouse_nGetMinCursorSize + (JNIEnv *, jclass); + +/* + * Class: org_lwjgl_input_Mouse + * Method: nGetMaxCursorSize + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_org_lwjgl_input_Mouse_nGetMaxCursorSize + (JNIEnv *, jclass); + /* * Class: org_lwjgl_input_Mouse * Method: initIDs diff --git a/src/native/linux/Makefile.am b/src/native/linux/Makefile.am index 000dff51..e9908814 100644 --- a/src/native/linux/Makefile.am +++ b/src/native/linux/Makefile.am @@ -9,7 +9,8 @@ NATIVE = \ org_lwjgl_input_Controller.cpp \ org_lwjgl_input_Keyboard.cpp \ org_lwjgl_input_Mouse.cpp \ + org_lwjgl_input_Cursor.cpp \ org_lwjgl_opengl_BaseGL.cpp \ - org_lwjgl_Window.cpp - + org_lwjgl_Window.cpp \ + extxcursor.cpp diff --git a/src/native/linux/Window.h b/src/native/linux/Window.h index f2e16af8..d898ba23 100644 --- a/src/native/linux/Window.h +++ b/src/native/linux/Window.h @@ -47,6 +47,11 @@ #include #include + /* + * update input grabbing(keyboard, mouse) + */ + extern void updateInput(void); + /* * release input (keyboard, mouse) */ @@ -99,6 +104,16 @@ */ extern Window getCurrentWindow(void); + /* + * Return true if a native cursor is active + */ + extern bool isNativeCursor(void); + + /* + * Return true if we are in fullscreen mode + */ + extern bool isFullscreen(void); + /* * Utility function to throw an Exception */ diff --git a/src/native/linux/extxcursor.cpp b/src/native/linux/extxcursor.cpp new file mode 100644 index 00000000..d8d53a22 --- /dev/null +++ b/src/native/linux/extxcursor.cpp @@ -0,0 +1,63 @@ +#include +#include +#include "extxcursor.h" + +static void * xcursor_handle = NULL; +static const char *xcursor_lib_name = "libXcursor.so.1"; +static bool load_success; + +XcursorSupportsARGBPROC XcursorSupportsARGB = NULL; +XcursorSupportsAnimPROC XcursorSupportsAnim = NULL; +XcursorImageCreatePROC XcursorImageCreate = NULL; +XcursorImageDestroyPROC XcursorImageDestroy = NULL; +XcursorImagesCreatePROC XcursorImagesCreate = NULL; +XcursorImagesDestroyPROC XcursorImagesDestroy = NULL; +XcursorImagesLoadCursorPROC XcursorImagesLoadCursor = NULL; + +static void * loadHandle(const char * func_name) { + void * func_pointer = dlsym(xcursor_handle, func_name); + if (func_pointer == NULL) { + load_success = false; + return NULL; + } + return func_pointer; +} + +static bool loadFunctionPointers(void) { + XcursorSupportsARGB = (XcursorSupportsARGBPROC)loadHandle("XcursorSupportsARGB"); + XcursorSupportsAnim = (XcursorSupportsAnimPROC)loadHandle("XcursorSupportsAnim"); + XcursorImageCreate = (XcursorImageCreatePROC)loadHandle("XcursorImageCreate"); + XcursorImageDestroy = (XcursorImageDestroyPROC)loadHandle("XcursorImageDestroy"); + XcursorImagesCreate = (XcursorImagesCreatePROC)loadHandle("XcursorImagesCreate"); + XcursorImagesDestroy = (XcursorImagesDestroyPROC)loadHandle("XcursorImagesDestroy"); + XcursorImagesLoadCursor = (XcursorImagesLoadCursorPROC)loadHandle("XcursorImagesLoadCursor"); + return load_success; +} + +bool loadXcursor(void) { + load_success = true; + xcursor_handle = dlopen(xcursor_lib_name, RTLD_GLOBAL | RTLD_LAZY); + if (xcursor_handle == NULL) { + printf("Could not load %s: %s\n", xcursor_lib_name, dlerror()); + return false; + } + loadFunctionPointers(); + return load_success; +} + +bool isXcursorLoaded(void) { + return load_success; +} + +void closeXcursor(void) { + load_success = false; + dlclose(xcursor_handle); + xcursor_handle = NULL; + XcursorSupportsARGB = NULL; + XcursorSupportsAnim = NULL; + XcursorImageCreate = NULL; + XcursorImageDestroy = NULL; + XcursorImagesCreate = NULL; + XcursorImagesDestroy = NULL; + XcursorImagesLoadCursor = NULL; +} diff --git a/src/native/linux/extxcursor.h b/src/native/linux/extxcursor.h new file mode 100644 index 00000000..ba7fc2e0 --- /dev/null +++ b/src/native/linux/extxcursor.h @@ -0,0 +1,51 @@ +#include + +#define XcursorTrue 1 +#define XcursorFalse 0 + +typedef int XcursorBool; + +typedef int XcursorBool; +typedef unsigned int XcursorUInt; + +typedef XcursorUInt XcursorDim; +typedef XcursorUInt XcursorPixel; + +typedef struct _XcursorImage { + XcursorUInt version; /* version of the image data */ + XcursorDim size; /* nominal size for matching */ + XcursorDim width; /* actual width */ + XcursorDim height; /* actual height */ + XcursorDim xhot; /* hot spot x (must be inside image) */ + XcursorDim yhot; /* hot spot y (must be inside image) */ + XcursorUInt delay; /* animation delay to next frame (ms) */ + XcursorPixel *pixels; /* pointer to pixels */ +} XcursorImage; + +/* + * Other data structures exposed by the library API + */ +typedef struct _XcursorImages { + int nimage; /* number of images */ + XcursorImage **images; /* array of XcursorImage pointers */ +} XcursorImages; + +typedef XcursorBool (* XcursorSupportsARGBPROC ) (Display *dpy); +typedef XcursorBool (* XcursorSupportsAnimPROC ) (Display *dpy); +typedef XcursorImage * (* XcursorImageCreatePROC) (int width, int height); +typedef void (* XcursorImageDestroyPROC) (XcursorImage *image); +typedef XcursorImages * (* XcursorImagesCreatePROC) (int size); +typedef void (* XcursorImagesDestroyPROC) (XcursorImages *images); +typedef Cursor (* XcursorImagesLoadCursorPROC) (Display *dpy, const XcursorImages *images); + +extern XcursorSupportsARGBPROC XcursorSupportsARGB; +extern XcursorSupportsAnimPROC XcursorSupportsAnim; +extern XcursorImageCreatePROC XcursorImageCreate; +extern XcursorImageDestroyPROC XcursorImageDestroy; +extern XcursorImagesCreatePROC XcursorImagesCreate; +extern XcursorImagesDestroyPROC XcursorImagesDestroy; +extern XcursorImagesLoadCursorPROC XcursorImagesLoadCursor; + +bool loadXcursor(void); +bool isXcursorLoaded(void); +void closeXcursor(void); diff --git a/src/native/linux/org_lwjgl_Window.cpp b/src/native/linux/org_lwjgl_Window.cpp index 704d03fa..617d7a5e 100644 --- a/src/native/linux/org_lwjgl_Window.cpp +++ b/src/native/linux/org_lwjgl_Window.cpp @@ -68,15 +68,6 @@ static void waitMapped(Display *disp, Window win) { } while ((event.type != MapNotify) || (event.xmap.event != win)); } -bool releaseInput(void) { - if (current_fullscreen || input_released) - return false; - releaseKeyboard(); - releasePointer(); - input_released = true; - return true; -} - static void acquireInput(void) { if (input_released) { acquireKeyboard(); @@ -85,6 +76,30 @@ static void acquireInput(void) { } } +static void doReleaseInput(void) { + releaseKeyboard(); + releasePointer(); + input_released = true; +} + +void updateInput(void) { + if (!input_released) { + doReleaseInput(); + acquireInput(); + } +} + +bool releaseInput(void) { + if (current_fullscreen || input_released) + return false; + doReleaseInput(); + return true; +} + +bool isFullscreen(void) { + return current_fullscreen; +} + static void handleMessages(JNIEnv *env, jobject window_obj) { XEvent event; while (XPending(current_disp) > 0) { @@ -108,6 +123,7 @@ static void handleMessages(JNIEnv *env, jobject window_obj) { env->SetBooleanField(window_obj, env->GetFieldID(env->GetObjectClass(window_obj), "minimized", "Z"), JNI_TRUE); break; case Expose: +// XSetInputFocus(current_disp, current_win, RevertToParent, CurrentTime); env->SetBooleanField(window_obj, env->GetFieldID(env->GetObjectClass(window_obj), "dirty", "Z"), JNI_TRUE); break; case ButtonPress: @@ -183,6 +199,7 @@ void createWindow(JNIEnv* env, Display *disp, int screen, XVisualInfo *vis_info, XMapRaised(disp, win); waitMapped(disp, win); XClearWindow(disp, win); + XSetInputFocus(current_disp, current_win, RevertToParent, CurrentTime); XSync(disp, True); } diff --git a/src/native/linux/org_lwjgl_input_Cursor.cpp b/src/native/linux/org_lwjgl_input_Cursor.cpp new file mode 100644 index 00000000..2a1d483e --- /dev/null +++ b/src/native/linux/org_lwjgl_input_Cursor.cpp @@ -0,0 +1,45 @@ +#include "org_lwjgl_input_Cursor.h" +#include "extxcursor.h" +#include "Window.h" + +/* + * Class: org_lwjgl_input_Cursor + * Method: nCreateCursor + * Signature: (IIIIIII)I + */ +JNIEXPORT jint JNICALL Java_org_lwjgl_input_Cursor_nCreateCursor + (JNIEnv *env, jclass clazz, jint width, jint height, jint x_hotspot, jint y_hotspot, /*jint num_cursors,*/ jint image_address/*, jint delay_addresses*/) +{ + XcursorPixel *pixels = (XcursorPixel *)image_address; +/* int *delays = (int *)delay_addresses; + int stride = width*height;*/ + int num_cursors = 1; + XcursorImages *cursor_images = XcursorImagesCreate(num_cursors); + if (cursor_images == NULL) + throwException(env, "Could not allocate cursor."); + cursor_images->nimage = num_cursors; +// for (int i = 0; i < num_cursors; i++) { + XcursorImage *cursor_image = XcursorImageCreate(width, height); + cursor_image->xhot = x_hotspot; + cursor_image->yhot = y_hotspot; + cursor_image->pixels = pixels; +// cursor_image->pixels = &(pixels[stride*i]); + cursor_image->delay = 0/*delays[i]*/; + cursor_images->images[0] = cursor_image; +// } + Cursor cursor = XcursorImagesLoadCursor(getCurrentDisplay(), cursor_images); + XcursorImagesDestroy(cursor_images); + return (jint)cursor; +} + +/* + * Class: org_lwjgl_input_Cursor + * Method: nDestroyCursor + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_org_lwjgl_input_Cursor_nDestroyCursor + (JNIEnv *env, jclass clazz, jint cursor_handle) +{ + Cursor cursor = (Cursor)cursor_handle; + XFreeCursor(getCurrentDisplay(), cursor); +} diff --git a/src/native/linux/org_lwjgl_input_Keyboard.cpp b/src/native/linux/org_lwjgl_input_Keyboard.cpp index dbddef30..02a4e00d 100644 --- a/src/native/linux/org_lwjgl_input_Keyboard.cpp +++ b/src/native/linux/org_lwjgl_input_Keyboard.cpp @@ -83,28 +83,34 @@ static void setRepeatMode(int mode) { XChangeKeyboardControl(getCurrentDisplay(), KBAutoRepeatMode, &repeat_mode); } -static int grabKeyboard(void) { - int result = XGrabKeyboard(getCurrentDisplay(), getCurrentWindow(), False, GrabModeAsync, GrabModeAsync, CurrentTime); - if (result == GrabSuccess) { - keyboard_grabbed = true; +static void grabKeyboard(void) { + if (isFullscreen() || !isNativeCursor()) { + if (!keyboard_grabbed) { + int result = XGrabKeyboard(getCurrentDisplay(), getCurrentWindow(), False, GrabModeAsync, GrabModeAsync, CurrentTime); + if (result == GrabSuccess) { + keyboard_grabbed = true; + setRepeatMode(AutoRepeatModeOff); + } + } + } else setRepeatMode(AutoRepeatModeOff); - } - return result; } static void ungrabKeyboard(void) { - keyboard_grabbed = false; - XUngrabKeyboard(getCurrentDisplay(), CurrentTime); + if (keyboard_grabbed) { + keyboard_grabbed = false; + XUngrabKeyboard(getCurrentDisplay(), CurrentTime); + } setRepeatMode(AutoRepeatModeDefault); } static void updateGrab(void) { + if (!created) + return; if (should_grab) { - if (!keyboard_grabbed) - grabKeyboard(); + grabKeyboard(); } else { - if (keyboard_grabbed) - ungrabKeyboard(); + ungrabKeyboard(); } } @@ -165,8 +171,7 @@ JNIEXPORT jboolean JNICALL Java_org_lwjgl_input_Keyboard_nCreate JNIEXPORT void JNICALL Java_org_lwjgl_input_Keyboard_nDestroy (JNIEnv * env, jclass clazz) { - if (keyboard_grabbed) - ungrabKeyboard(); + ungrabKeyboard(); created = false; } @@ -259,7 +264,6 @@ void handleKeyEvent(XKeyEvent *event) { JNIEXPORT void JNICALL Java_org_lwjgl_input_Keyboard_nPoll (JNIEnv * env, jclass clazz, jint buf) { - updateGrab(); memcpy((unsigned char*)buf, key_buf, KEYBOARD_SIZE*sizeof(unsigned char)); } @@ -275,7 +279,6 @@ JNIEXPORT int JNICALL Java_org_lwjgl_input_Keyboard_nRead int buf_count = 0; int num_events = 0; - updateGrab(); while (buf_count < KEYBOARD_BUFFER_SIZE * 2 && (key_event = nextEventElement()) != NULL) { num_events++; unsigned char keycode = getKeycode(key_event); @@ -286,7 +289,6 @@ JNIEXPORT int JNICALL Java_org_lwjgl_input_Keyboard_nRead if (translation_enabled) num_events += translateEvent(&buf_count, key_event); } - return num_events; } diff --git a/src/native/linux/org_lwjgl_input_Mouse.cpp b/src/native/linux/org_lwjgl_input_Mouse.cpp index e6724602..da906db2 100644 --- a/src/native/linux/org_lwjgl_input_Mouse.cpp +++ b/src/native/linux/org_lwjgl_input_Mouse.cpp @@ -47,6 +47,7 @@ #include #include #include "org_lwjgl_input_Mouse.h" +#include "extxcursor.h" #define NUM_BUTTONS 3 @@ -55,9 +56,10 @@ // scale the mouse wheel according to win32 #define WHEEL_SCALE 120 -static bool pointer_grabbed; +static bool pointer_grabbed = false; static bool created = false; static bool should_grab = false; +static bool native_cursor = false; static jfieldID fid_has_wheel = NULL; static jfieldID fid_button_count = NULL; @@ -75,6 +77,9 @@ static int current_z; static unsigned char buttons[NUM_BUTTONS]; static Cursor blank_cursor; +static Cursor current_cursor; + +static int grab_mask = FocusChangeMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask; /* * Class: org_lwjgl_input_Mouse @@ -118,30 +123,39 @@ static int blankCursor(void) { return 1; } -static int grabPointer(void) { - int result; - int mask = FocusChangeMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask; - result = XGrabPointer(getCurrentDisplay(), getCurrentWindow(), False, mask, GrabModeAsync, GrabModeAsync, getCurrentWindow(), blank_cursor, CurrentTime); - if (result == GrabSuccess) { - pointer_grabbed = true; - XWarpPointer(getCurrentDisplay(), None, getCurrentWindow(), 0, 0, 0, 0, current_x, current_y); - XF86VidModeSetViewPort(getCurrentDisplay(), getCurrentScreen(), 0, 0); // make sure we have a centered window +bool isNativeCursor(void) { + return native_cursor; +} + +static void grabPointer(void) { + if (isFullscreen() || !native_cursor) { + if (!pointer_grabbed) { + int result; + result = XGrabPointer(getCurrentDisplay(), getCurrentWindow(), False, grab_mask, GrabModeAsync, + GrabModeAsync, getCurrentWindow(), current_cursor, CurrentTime); + if (result == GrabSuccess) { + pointer_grabbed = true; + // make sure we have a centered window + XF86VidModeSetViewPort(getCurrentDisplay(), getCurrentScreen(), 0, 0); + } + } } - return result; } static void ungrabPointer(void) { - pointer_grabbed = false; - XUngrabPointer(getCurrentDisplay(), CurrentTime); + if (pointer_grabbed) { + pointer_grabbed = false; + XUngrabPointer(getCurrentDisplay(), CurrentTime); + } } static void updateGrab(void) { + if (!created) + return; if (should_grab) { - if (!pointer_grabbed) - grabPointer(); + grabPointer(); } else { - if (pointer_grabbed) - ungrabPointer(); + ungrabPointer(); } } @@ -155,6 +169,73 @@ void releasePointer(void) { updateGrab(); } +/* + * Class: org_lwjgl_input_Mouse + * Method: nIsNativeCursorSupported + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_org_lwjgl_input_Mouse_nIsNativeCursorSupported + (JNIEnv *env, jclass clazz) { + if (!isXcursorLoaded()) + return false; + XcursorBool argb_supported = XcursorSupportsARGB(getCurrentDisplay()); + XcursorBool anim_supported = XcursorSupportsAnim(getCurrentDisplay()); + return argb_supported && anim_supported ? JNI_TRUE : JNI_FALSE; +} + +/* + * Class: org_lwjgl_input_Mouse + * Method: nSetNativeCursor + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_org_lwjgl_input_Mouse_nSetNativeCursor + (JNIEnv *env, jclass clazz, jint cursor_handle) +{ + Cursor cursor = (Cursor)cursor_handle; + if (cursor != None) { + if (!native_cursor) { + current_x = current_y = 0; + XWarpPointer(getCurrentDisplay(), None, getCurrentWindow(), 0, 0, 0, 0, current_x, current_y); + native_cursor = true; + } + XDefineCursor(getCurrentDisplay(), getCurrentWindow(), cursor); + current_cursor = cursor; + updateInput(); + } else { + if (native_cursor) { + current_cursor = blank_cursor; + XUndefineCursor(getCurrentDisplay(), getCurrentWindow()); + native_cursor = false; + updateInput(); + } + } +} + +/* + * Class: org_lwjgl_input_Mouse + * Method: nGetMaxCursorSize + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_org_lwjgl_input_Mouse_nGetMinCursorSize + (JNIEnv *env, jclass clazz) +{ + return 0; +} + +/* + * Class: org_lwjgl_input_Mouse + * Method: nGetMaxCursorSize + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_org_lwjgl_input_Mouse_nGetMaxCursorSize + (JNIEnv *env, jclass clazz) +{ + unsigned int width_return = 0; + unsigned int height_return = 0; + XQueryBestCursor(getCurrentDisplay(), getCurrentWindow(), 0xffffffff, 0xffffffff, &width_return, &height_return); + return width_return > height_return ? height_return : width_return; +} + /* * Class: org_lwjgl_input_Mouse * Method: nCreate @@ -164,21 +245,24 @@ JNIEXPORT jboolean JNICALL Java_org_lwjgl_input_Mouse_nCreate (JNIEnv * env, jclass clazz) { int i; - env->SetStaticIntField(clazz, fid_button_count, NUM_BUTTONS); env->SetStaticBooleanField(clazz, fid_has_wheel, JNI_TRUE); - current_x = current_y = current_z = last_x = last_y = last_z = pointer_grabbed = 0; + current_x = current_y = current_z = last_x = last_y = last_z; for (i = 0; i < NUM_BUTTONS; i++) buttons[i] = JNI_FALSE; if (!blankCursor()) { #ifdef _DEBUG - printf("Could create blank cursor\n"); + printf("Could not create blank cursor\n"); #endif return JNI_FALSE; } + current_cursor = blank_cursor; + native_cursor = false; created = true; should_grab = true; + pointer_grabbed = false; updateGrab(); + loadXcursor(); return JNI_TRUE; } @@ -190,60 +274,66 @@ JNIEXPORT jboolean JNICALL Java_org_lwjgl_input_Mouse_nCreate JNIEXPORT void JNICALL Java_org_lwjgl_input_Mouse_nDestroy (JNIEnv * env, jclass clazz) { - if (pointer_grabbed) - ungrabPointer(); + closeXcursor(); + ungrabPointer(); XFreeCursor(getCurrentDisplay(), blank_cursor); created = false; should_grab = false; } void handleButtonPress(XButtonEvent *event) { - switch (event->button) { - case Button1: - buttons[0] = JNI_TRUE; - break; - case Button2: - buttons[2] = JNI_TRUE; - break; - case Button3: - buttons[1] = JNI_TRUE; - break; - case Button4: - current_z += WHEEL_SCALE; - break; - case Button5: - current_z -= WHEEL_SCALE; - break; - default: assert(0); + if (pointer_grabbed || native_cursor) { + switch (event->button) { + case Button1: + buttons[0] = JNI_TRUE; + break; + case Button2: + buttons[2] = JNI_TRUE; + break; + case Button3: + buttons[1] = JNI_TRUE; + break; + case Button4: + current_z += WHEEL_SCALE; + break; + case Button5: + current_z -= WHEEL_SCALE; + break; + default: assert(0); + } } } void handleButtonRelease(XButtonEvent *event) { - switch (event->button) { - case Button1: - buttons[0] = JNI_FALSE; - break; - case Button2: - buttons[2] = JNI_FALSE; - break; - case Button3: - buttons[1] = JNI_FALSE; - break; - case Button4: /* Fall through */ - case Button5: - break; - default: assert(0); + if (pointer_grabbed || native_cursor) { + switch (event->button) { + case Button1: + buttons[0] = JNI_FALSE; + break; + case Button2: + buttons[2] = JNI_FALSE; + break; + case Button3: + buttons[1] = JNI_FALSE; + break; + case Button4: /* Fall through */ + case Button5: + break; + default: assert(0); + } } } void handlePointerMotion(XMotionEvent *event) { - current_x = event->x; - current_y = event->y; + if (pointer_grabbed || native_cursor) { + current_x = event->x; + current_y = event->y; + } } static void warpPointer(void) { int i; - if (!pointer_grabbed) + if (!pointer_grabbed || native_cursor) return; // Reset pointer to middle of screen if inside a certain inner border if (current_x < POINTER_WARP_BORDER || current_y < POINTER_WARP_BORDER || @@ -279,7 +369,6 @@ static void warpPointer(void) { JNIEXPORT void JNICALL Java_org_lwjgl_input_Mouse_nPoll (JNIEnv * env, jclass clazz) { - updateGrab(); int moved_x = current_x - last_x; int moved_y = current_y - last_y; int moved_z = current_z - last_z; diff --git a/src/native/win32/Window.h b/src/native/win32/Window.h index d90573dd..64df0c10 100644 --- a/src/native/win32/Window.h +++ b/src/native/win32/Window.h @@ -59,6 +59,7 @@ extern LPDIRECTINPUT lpdi; // DirectInput extern bool isFullScreen; // Whether we're fullscreen or not extern bool isMinimized; // Whether we're minimized or not + extern RECT clientSize; #endif /* _PRIVATE_WINDOW_H_ */ /* @@ -82,7 +83,6 @@ */ WINDOW_H_API void closeWindow(); - /* * Utility function to throw an Exception */ @@ -93,4 +93,4 @@ */ WINDOW_H_API void throwRuntimeException(JNIEnv * env, const char * err); -#endif /* _LWJGL_WINDOW_H_INCLUDED_ */ \ No newline at end of file +#endif /* _LWJGL_WINDOW_H_INCLUDED_ */ diff --git a/src/native/win32/org_lwjgl_Window.cpp b/src/native/win32/org_lwjgl_Window.cpp index f814c950..510f6f68 100644 --- a/src/native/win32/org_lwjgl_Window.cpp +++ b/src/native/win32/org_lwjgl_Window.cpp @@ -52,6 +52,7 @@ bool isMinimized = false; // Whether we're minimized or not JNIEnv * environment = NULL; // Cached environment jobject window; // Cached Java Window instance handle extern HINSTANCE dll_handle; // Handle to the LWJGL dll +RECT clientSize; #define WINDOWCLASSNAME "LWJGL" @@ -140,9 +141,7 @@ void closeWindow() } // Show the mouse - if (isFullScreen) { - ShowCursor(TRUE); - } + ShowCursor(TRUE); } /* @@ -153,8 +152,9 @@ void appActivate(bool active) if (active) { SetForegroundWindow(hwnd); ShowWindow(hwnd, SW_RESTORE); - } else if (isFullScreen) + } else if (isFullScreen) { ShowWindow(hwnd, SW_MINIMIZE); + } } /* @@ -239,7 +239,7 @@ bool registerWindow() windowClass.cbWndExtra = 0; windowClass.hInstance = dll_handle; windowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); - windowClass.hCursor = LoadCursor(NULL, IDC_ARROW); + windowClass.hCursor = NULL/*LoadCursor(NULL, IDC_ARROW)*/; windowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); windowClass.lpszMenuName = NULL; windowClass.lpszClassName = WINDOWCLASSNAME; @@ -283,7 +283,6 @@ bool createWindow(const char * title, int x, int y, int width, int height, bool // If we're not a fullscreen window, adjust the height to account for the // height of the title bar: - RECT clientSize; clientSize.bottom = height; clientSize.left = 0; clientSize.right = width; @@ -296,16 +295,13 @@ bool createWindow(const char * title, int x, int y, int width, int height, bool exstyle // extended window style ); - clientSize.bottom -= clientSize.top; - clientSize.right -= clientSize.left; - // Create the window now, using that class: hwnd = CreateWindowEx ( exstyle, WINDOWCLASSNAME, title, windowflags, - x, y, clientSize.right, clientSize.bottom, + x, y, clientSize.right - clientSize.left, clientSize.bottom - clientSize.top, NULL, NULL, dll_handle, @@ -332,9 +328,7 @@ bool createWindow(const char * title, int x, int y, int width, int height, bool // 3. Hide the mouse if necessary isFullScreen = fullscreen == JNI_TRUE; - if (isFullScreen) { - ShowCursor(FALSE); - } + ShowCursor(FALSE); // 4. Create DirectInput if (!createDirectInput()) { diff --git a/src/native/win32/org_lwjgl_input_Controller.cpp b/src/native/win32/org_lwjgl_input_Controller.cpp index 429596c0..893b90fe 100644 --- a/src/native/win32/org_lwjgl_input_Controller.cpp +++ b/src/native/win32/org_lwjgl_input_Controller.cpp @@ -51,45 +51,45 @@ extern HINSTANCE dll_handle; extern HWND hwnd; -IDirectInput* cDI; // DI instance -IDirectInputDevice2* cDIDevice; // DI Device instance -DIJOYSTATE2 cJS; // State of Controller +static IDirectInput* cDI; // DI instance +static IDirectInputDevice2* cDIDevice; // DI Device instance +static DIJOYSTATE2 cJS; // State of Controller -int cButtoncount = 0; // Temporary buttoncount -bool cHasx; // Temporary xaxis check -bool cHasrx; // Temporary rotational xaxis check -bool cHasy; // Temporary yaxis check -bool cHasry; // Temporary rotational yaxis check -bool cHasz; // Temporary zaxis check -bool cHasrz; // Temporary rotational zaxis check -bool cHaspov; // Temporary pov check -bool cHasslider; // Temporary slider check +static int cButtoncount = 0; // Temporary buttoncount +static bool cHasx; // Temporary xaxis check +static bool cHasrx; // Temporary rotational xaxis check +static bool cHasy; // Temporary yaxis check +static bool cHasry; // Temporary rotational yaxis check +static bool cHasz; // Temporary zaxis check +static bool cHasrz; // Temporary rotational zaxis check +static bool cHaspov; // Temporary pov check +static bool cHasslider; // Temporary slider check -JNIEnv* cEnvironment; // JNIEnvironment copy +static JNIEnv* cEnvironment; // JNIEnvironment copy -bool cCreate_success; // bool used to determine successfull creation -bool cFirstTimeInitialization = true; // boolean to determine first time initialization +static bool cCreate_success; // bool used to determine successfull creation +static bool cFirstTimeInitialization = true; // boolean to determine first time initialization // Cached fields of Controller.java -jclass clsController; -jfieldID fidCButtonCount; -jfieldID fidCHasXAxis; -jfieldID fidCHasRXAxis; -jfieldID fidCHasYAxis; -jfieldID fidCHasRYAxis; -jfieldID fidCHasZAxis; -jfieldID fidCHasRZAxis; -jfieldID fidCHasPOV; -jfieldID fidCHasSlider; -jfieldID fidCButtons; -jfieldID fidCX; -jfieldID fidCRX; -jfieldID fidCY; -jfieldID fidCRY; -jfieldID fidCZ; -jfieldID fidCRZ; -jfieldID fidCPOV; -jfieldID fidCSlider; +static jclass clsController; +static jfieldID fidCButtonCount; +static jfieldID fidCHasXAxis; +static jfieldID fidCHasRXAxis; +static jfieldID fidCHasYAxis; +static jfieldID fidCHasRYAxis; +static jfieldID fidCHasZAxis; +static jfieldID fidCHasRZAxis; +static jfieldID fidCHasPOV; +static jfieldID fidCHasSlider; +static jfieldID fidCButtons; +static jfieldID fidCX; +static jfieldID fidCRX; +static jfieldID fidCY; +static jfieldID fidCRY; +static jfieldID fidCZ; +static jfieldID fidCRZ; +static jfieldID fidCPOV; +static jfieldID fidCSlider; // Function prototypes (defined in the cpp file, since header file is generic across platforms void EnumerateControllerCapabilities(); diff --git a/src/native/win32/org_lwjgl_input_Cursor.cpp b/src/native/win32/org_lwjgl_input_Cursor.cpp new file mode 100755 index 00000000..5047d0e3 --- /dev/null +++ b/src/native/win32/org_lwjgl_input_Cursor.cpp @@ -0,0 +1,111 @@ +#include +#include "org_lwjgl_input_Cursor.h" +#include "Window.h" + +/* + * Class: org_lwjgl_input_Cursor + * Method: nCreateCursor + * Signature: (IIIIIII)I + */ +JNIEXPORT jint JNICALL Java_org_lwjgl_input_Cursor_nCreateCursor + (JNIEnv *env, jclass clazz, jint width, jint height, jint x_hotspot, jint y_hotspot, jint image_address) +{ + int *pixels = (int *)image_address; + + BITMAPINFO bitmapInfo; + + char *ptrCursorImage; + HBITMAP colorDIB; + HBITMAP colorBitmap; + int x, y; + + memset(&bitmapInfo, 0, sizeof(BITMAPINFO)); + bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bitmapInfo.bmiHeader.biWidth = width; + bitmapInfo.bmiHeader.biHeight = -height; + bitmapInfo.bmiHeader.biPlanes = 1; + + bitmapInfo.bmiHeader.biBitCount = 24; + bitmapInfo.bmiHeader.biCompression = BI_RGB; + + colorDIB = CreateDIBSection(hdc, (BITMAPINFO*)&(bitmapInfo), + DIB_RGB_COLORS, + (void**)&(ptrCursorImage), + NULL, 0); + int *srcPtr = pixels; + char *dstPtr = ptrCursorImage; + if (!dstPtr) { + throwException(env, "Could not allocate DIB section."); + return 0; + } + for (y = 0; y < height; y++ ) { + for (x = 0; x < width; x++ ) { + dstPtr[2] = (*srcPtr >> 0x10) & 0xFF; + dstPtr[1] = (*srcPtr >> 0x08) & 0xFF; + dstPtr[0] = *srcPtr & 0xFF; + + srcPtr++; + dstPtr += 3; + } + } + + colorBitmap = CreateDIBitmap(hdc, + (BITMAPINFOHEADER*)&bitmapInfo.bmiHeader, + CBM_INIT, + (void *)ptrCursorImage, + (BITMAPINFO*)&bitmapInfo, + DIB_RGB_COLORS); + + DeleteObject(colorDIB); + + // Convert alpha map to pixel packed mask + int bitWidth = width >> 3; + int scanlinePad = bitWidth & (sizeof(WORD) - 1); + int imageSize = (bitWidth + scanlinePad)*height; // Size in bits + unsigned char *maskPixels = new unsigned char[imageSize]; + memset(maskPixels, 0, imageSize); + int pixelCount = 0; + int maskCount = 0; + for (y = 0; y < height; y++) + for (x = 0; x < bitWidth; x++) { + unsigned char col0 = (pixels[pixelCount++] & 0x01000000) >> 17; + unsigned char col1 = (pixels[pixelCount++] & 0x01000000) >> 18; + unsigned char col2 = (pixels[pixelCount++] & 0x01000000) >> 19; + unsigned char col3 = (pixels[pixelCount++] & 0x01000000) >> 20; + unsigned char col4 = (pixels[pixelCount++] & 0x01000000) >> 21; + unsigned char col5 = (pixels[pixelCount++] & 0x01000000) >> 22; + unsigned char col6 = (pixels[pixelCount++] & 0x01000000) >> 23; + unsigned char col7 = (pixels[pixelCount++] & 0x01000000) >> 24; + unsigned char mask = col0 | col1 | col2 | col3 | col4 | col5 | col6 | col7; + maskPixels[maskCount++] = ~mask; // 1 is tranparant, 0 opaque + } + + HBITMAP cursorMask = CreateBitmap(width, height, 1, 1, maskPixels); + + HCURSOR cursor = NULL; + ICONINFO iconInfo; + memset(&iconInfo, 0, sizeof(ICONINFO)); + iconInfo.hbmMask = cursorMask; + iconInfo.hbmColor = colorBitmap; + iconInfo.fIcon = FALSE; + iconInfo.xHotspot = x_hotspot; + iconInfo.yHotspot = y_hotspot; + cursor = CreateIconIndirect(&iconInfo); + DeleteObject(colorBitmap); + DeleteObject(cursorMask); + delete[] maskPixels; + + return (jint)cursor; +} + +/* + * Class: org_lwjgl_input_Cursor + * Method: nDestroyCursor + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_org_lwjgl_input_Cursor_nDestroyCursor + (JNIEnv *env, jclass clazz, jint cursor_handle) +{ + HCURSOR cursor = (HCURSOR)cursor_handle; + DestroyCursor(cursor); +} diff --git a/src/native/win32/org_lwjgl_input_Keyboard.cpp b/src/native/win32/org_lwjgl_input_Keyboard.cpp index 599fa099..d50e7cdd 100644 --- a/src/native/win32/org_lwjgl_input_Keyboard.cpp +++ b/src/native/win32/org_lwjgl_input_Keyboard.cpp @@ -48,10 +48,11 @@ #include "Window.h" #define KEYBOARD_BUFFER_SIZE 50 -BYTE readBuffer[KEYBOARD_BUFFER_SIZE*4]; -LPDIRECTINPUTDEVICE lpdiKeyboard = NULL; -jfieldID fid_readBuffer; -bool translationEnabled; + +static BYTE readBuffer[KEYBOARD_BUFFER_SIZE*4]; +static LPDIRECTINPUTDEVICE lpdiKeyboard = NULL; +static jfieldID fid_readBuffer; +static bool translationEnabled; /* diff --git a/src/native/win32/org_lwjgl_input_Mouse.cpp b/src/native/win32/org_lwjgl_input_Mouse.cpp index 8dd98416..f155c65c 100644 --- a/src/native/win32/org_lwjgl_input_Mouse.cpp +++ b/src/native/win32/org_lwjgl_input_Mouse.cpp @@ -46,25 +46,26 @@ #include "Window.h" #include -extern HINSTANCE dll_handle; -LPDIRECTINPUTDEVICE mDIDevice; // DI Device instance -DIMOUSESTATE diMouseState; // State of Mouse +static LPDIRECTINPUTDEVICE mDIDevice; // DI Device instance +static int mButtoncount = 0; // Temporary buttoncount +static bool mHaswheel; // Temporary wheel check +static JNIEnv* mEnvironment; // JNIEnvironment copy -int mButtoncount = 0; // Temporary buttoncount -bool mHaswheel; // Temporary wheel check -JNIEnv* mEnvironment; // JNIEnvironment copy - -bool mCreate_success; // bool used to determine successfull creation -bool mFirstTimeInitialization = true; // boolean to determine first time initialization +static bool mCreate_success; // bool used to determine successfull creation +static bool mFirstTimeInitialization = true; // boolean to determine first time initialization // Cached fields of Mouse.java -jclass clsMouse; -jfieldID fidMButtonCount; -jfieldID fidMButtons; -jfieldID fidMDX; -jfieldID fidMDY; -jfieldID fidMDWheel; -jfieldID fidMHasWheel; +static jclass clsMouse; +static jfieldID fidMButtonCount; +static jfieldID fidMButtons; +static jfieldID fidMDX; +static jfieldID fidMDY; +static jfieldID fidMDWheel; +static jfieldID fidMHasWheel; + +static POINT cursorPos; +static RECT windowRect; +static bool usingNativeCursor; // Function prototypes (defined in the cpp file, since header file is generic across platforms void EnumerateMouseCapabilities(); @@ -77,6 +78,16 @@ void CacheMouseFields(); void UpdateMouseFields(); void SetMouseCapabilities(); +static void getScreenClientRect(RECT* clientRect, RECT* windowRect) +{ + GetClientRect(hwnd, clientRect); + // transform clientRect to screen coordinates + clientRect->top = -clientSize.top + windowRect->top; + clientRect->left = -clientSize.left + windowRect->left; + clientRect->bottom += clientRect->top; + clientRect->right += clientRect->left; +} + /** * Initializes any field ids */ @@ -92,51 +103,131 @@ JNIEXPORT void JNICALL Java_org_lwjgl_input_Mouse_initIDs(JNIEnv * env, jclass c * Called when the Mouse instance is to be created */ JNIEXPORT jboolean JNICALL Java_org_lwjgl_input_Mouse_nCreate(JNIEnv *env, jclass clazz) { - HRESULT hr; + HRESULT hr; - mEnvironment = env; - clsMouse = clazz; + mEnvironment = env; + clsMouse = clazz; - CacheMouseFields(); + CacheMouseFields(); - /* skip enumeration, since we only want system mouse */ - CreateMouse(); + /* skip enumeration, since we only want system mouse */ + CreateMouse(); - //check for first time initialization - need to detect capabilities - if (mFirstTimeInitialization) { - mFirstTimeInitialization = false; - - /* Enumerate capabilities of Mouse */ - EnumerateMouseCapabilities(); - if (!mCreate_success) { - #if _DEBUG - printf("EnumerateMouseCapabilities failed\n"); - #endif - ShutdownMouse(); - return JNI_FALSE; - } - - /* Do setup of Mouse */ - SetupMouse(); - - /* Set capabilities */ - SetMouseCapabilities(); - } else { - if(mCreate_success) { - /* Do setup of Mouse */ - SetupMouse(); - } - } - - /* Aquire the Mouse */ - hr = mDIDevice->Acquire(); - if(FAILED(hr)) { + //check for first time initialization - need to detect capabilities + if (mFirstTimeInitialization) { + mFirstTimeInitialization = false; + /* Enumerate capabilities of Mouse */ + EnumerateMouseCapabilities(); + if (!mCreate_success) { #if _DEBUG - printf("Failed to acquire mouse\n"); + printf("EnumerateMouseCapabilities failed\n"); #endif - } + ShutdownMouse(); + return JNI_FALSE; + } + /* Do setup of Mouse */ + SetupMouse(); - return mCreate_success ? JNI_TRUE : JNI_FALSE; + /* Set capabilities */ + SetMouseCapabilities(); + } else { + if(mCreate_success) { + /* Do setup of Mouse */ + SetupMouse(); + } + } + /* Aquire the Mouse */ + hr = mDIDevice->Acquire(); + if(FAILED(hr)) { +#if _DEBUG + printf("Failed to acquire mouse\n"); +#endif + } + return mCreate_success ? JNI_TRUE : JNI_FALSE; +} + +/* + * Class: org_lwjgl_input_Mouse + * Method: nIsNativeCursorSupported + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_org_lwjgl_input_Mouse_nIsNativeCursorSupported + (JNIEnv *env, jclass clazz) +{ + return JNI_TRUE; +} + +/* + * Class: org_lwjgl_input_Mouse + * Method: nSetNativeCursor + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_org_lwjgl_input_Mouse_nSetNativeCursor + (JNIEnv *env, jclass clazz, jint cursor_handle) +{ + if (cursor_handle != NULL) { + if (mDIDevice == NULL) + throwException(env, "null device!"); + mDIDevice->Unacquire(); + if(mDIDevice->SetCooperativeLevel(hwnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND) != DI_OK) { +#if _DEBUG + printf("SetCooperativeLevel failed\n"); +#endif + throwException(env, "Could not set the CooperativeLevel."); + return; + } + HCURSOR cursor = (HCURSOR)cursor_handle; + SetClassLong(hwnd, GCL_HCURSOR, (LONG)cursor); + SetCursor(cursor); + if (!usingNativeCursor) { + /* Reset cursor position to 0, 0 */ + RECT clientRect; + GetWindowRect(hwnd, &windowRect); + getScreenClientRect(&clientRect, &windowRect); + SetCursorPos(clientRect.left, clientRect.top); + cursorPos.x = clientRect.left; + cursorPos.y = clientRect.top; + while (ShowCursor(TRUE) < 0) + ; + usingNativeCursor = true; + } + } else { + while (ShowCursor(FALSE) >= 0) + ; + SetClassLong(hwnd, GCL_HCURSOR, (LONG)NULL); + SetCursor(NULL); + mDIDevice->Unacquire(); + if(mDIDevice->SetCooperativeLevel(hwnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND) != DI_OK) { +#if _DEBUG + printf("SetCooperativeLevel failed\n"); +#endif + throwException(env, "Could not set the CooperativeLevel."); + return; + } + usingNativeCursor = false; + } +} + +/* + * Class: org_lwjgl_input_Mouse + * Method: nGetMaxCursorSize + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_org_lwjgl_input_Mouse_nGetMaxCursorSize + (JNIEnv *env, jclass clazz) +{ + return GetSystemMetrics(SM_CXCURSOR); +} + +/* + * Class: org_lwjgl_input_Mouse + * Method: nGetMaxCursorSize + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_org_lwjgl_input_Mouse_nGetMinCursorSize + (JNIEnv *env, jclass clazz) +{ + return GetSystemMetrics(SM_CXCURSOR); } /* @@ -177,25 +268,25 @@ void ShutdownMouse() { * Enumerates the capabilities of the Mouse attached to the system */ void EnumerateMouseCapabilities() { - HRESULT hr; - hr = mDIDevice->EnumObjects(EnumMouseObjectsCallback, NULL, DIDFT_ALL); - if FAILED(hr) { + HRESULT hr; + hr = mDIDevice->EnumObjects(EnumMouseObjectsCallback, NULL, DIDFT_ALL); + if FAILED(hr) { #if _DEBUG - printf("EnumObjects failed\n"); + printf("EnumObjects failed\n"); #endif - mCreate_success = false; - return; - } + mCreate_success = false; + return; + } - //check for > 4 buttons - need to clamp since we're using dx 5 - if(mButtoncount > 4) { - mButtoncount = 4; + //check for > 4 buttons - need to clamp since we're using dx 5 + if(mButtoncount > 4) { + mButtoncount = 4; #ifdef _DEBUG - printf("WARNING: Clamping to 4 mouse buttons"); + printf("WARNING: Clamping to 4 mouse buttons\n"); #endif - } + } - mCreate_success = true; + mCreate_success = true; } /** @@ -259,46 +350,80 @@ void SetupMouse() { mCreate_success = true; } +static void getGDICursorDelta(int* return_dx, int* return_dy) { + int dx = 0; + int dy = 0; + + POINT newCursorPos; + GetCursorPos(&newCursorPos); + RECT clientRect; + RECT newWindowRect; + GetWindowRect(hwnd, &newWindowRect); + cursorPos.x += newWindowRect.left - windowRect.left; + cursorPos.y += newWindowRect.top - windowRect.top; + windowRect = newWindowRect; + getScreenClientRect(&clientRect, &windowRect); + // Clip the position to the client rect + if (newCursorPos.x < clientRect.right && newCursorPos.x >= clientRect.left && + newCursorPos.y < clientRect.bottom && newCursorPos.y >= clientRect.top) { + dx = newCursorPos.x - cursorPos.x; + dy = newCursorPos.y - cursorPos.y; + cursorPos.x += dx; + cursorPos.y += dy; + } + *return_dx = dx; + *return_dy = dy; +} + /** * Updates the fields on the Mouse */ void UpdateMouseFields() { - HRESULT hRes; + HRESULT hRes; + DIMOUSESTATE diMouseState; // State of Mouse + int dx, dy; - // get data from the Mouse - hRes = mDIDevice->GetDeviceState(sizeof(DIMOUSESTATE), &diMouseState); - if (hRes != DI_OK) { - // Don't allow the mouse to drift when failed - diMouseState.lX = 0; - diMouseState.lY = 0; - diMouseState.lZ = 0; - // did the read fail because we lost input for some reason? - // if so, then attempt to reacquire. - if(hRes == DIERR_INPUTLOST || hRes == DIERR_NOTACQUIRED) { - mDIDevice->Acquire(); + // get data from the Mouse + hRes = mDIDevice->GetDeviceState(sizeof(DIMOUSESTATE), &diMouseState); + if (hRes != DI_OK) { + // Don't allow the mouse to drift when failed + diMouseState.lX = 0; + diMouseState.lY = 0; + diMouseState.lZ = 0; + // did the read fail because we lost input for some reason? + // if so, then attempt to reacquire. + if(hRes == DIERR_INPUTLOST || hRes == DIERR_NOTACQUIRED) { + mDIDevice->Acquire(); #if _DEBUG - printf("DIERR_INPUTLOST, reaquiring input : mCreate_success=%d\n", mCreate_success); + printf("DIERR_INPUTLOST, reaquiring input : mCreate_success=%d\n", mCreate_success); #endif - } else { + } else { #if _DEBUG - printf("Error getting mouse state: %d\n", hRes); + printf("Error getting mouse state: %d\n", hRes); #endif - } - } + } + } - mEnvironment->SetStaticIntField(clsMouse, fidMDX, (jint) diMouseState.lX); - mEnvironment->SetStaticIntField(clsMouse, fidMDY, (jint) diMouseState.lY); - mEnvironment->SetStaticIntField(clsMouse, fidMDWheel, (jint) diMouseState.lZ); + if (usingNativeCursor) { + getGDICursorDelta(&dx, &dy); + } else { + dx = diMouseState.lX; + dy = diMouseState.lY; + } + + mEnvironment->SetStaticIntField(clsMouse, fidMDX, (jint)dx); + mEnvironment->SetStaticIntField(clsMouse, fidMDY, (jint)dy); + mEnvironment->SetStaticIntField(clsMouse, fidMDWheel, (jint)diMouseState.lZ); - for (int i = 0; i < mButtoncount; i++) { - if (diMouseState.rgbButtons[i] != 0) { - diMouseState.rgbButtons[i] = JNI_TRUE; - } else { - diMouseState.rgbButtons[i] = JNI_FALSE; - } - } - jbooleanArray mButtonsArray = (jbooleanArray) mEnvironment->GetStaticObjectField(clsMouse, fidMButtons); - mEnvironment->SetBooleanArrayRegion(mButtonsArray, 0, mButtoncount, diMouseState.rgbButtons); + for (int i = 0; i < mButtoncount; i++) { + if (diMouseState.rgbButtons[i] != 0) { + diMouseState.rgbButtons[i] = JNI_TRUE; + } else { + diMouseState.rgbButtons[i] = JNI_FALSE; + } + } + jbooleanArray mButtonsArray = (jbooleanArray) mEnvironment->GetStaticObjectField(clsMouse, fidMButtons); + mEnvironment->SetBooleanArrayRegion(mButtonsArray, 0, mButtoncount, diMouseState.rgbButtons); } /**