diff --git a/src/java/org/lwjgl/Sys.java b/src/java/org/lwjgl/Sys.java index f8134a57..f4ab1b31 100644 --- a/src/java/org/lwjgl/Sys.java +++ b/src/java/org/lwjgl/Sys.java @@ -57,7 +57,7 @@ public final class Sys { private static final String VERSION = "1.1"; /** Current version of the JNI library */ - static final int JNI_VERSION = 8; + static final int JNI_VERSION = 9; /** The implementation instance to delegate platform specific behavior to */ private final static SysImplementation implementation; diff --git a/src/java/org/lwjgl/opengl/WindowsAWTInput.java b/src/java/org/lwjgl/opengl/WindowsAWTInput.java index 41e95f4e..3a911345 100644 --- a/src/java/org/lwjgl/opengl/WindowsAWTInput.java +++ b/src/java/org/lwjgl/opengl/WindowsAWTInput.java @@ -53,7 +53,7 @@ final class WindowsAWTInput extends AbstractAWTInput { private Cursor cached_cursor; private long cached_hwnd; - private WindowsMouse cached_mouse; + private WindowsDirectInputMouse cached_mouse; // private WindowsKeyboard cached_keyboard; private boolean has_grabbed; @@ -96,7 +96,7 @@ final class WindowsAWTInput extends AbstractAWTInput { cached_keyboard.destroy(); }*/ WindowsDirectInput dinput = WindowsDisplay.createDirectInput(); - cached_mouse = new WindowsMouse(dinput, hwnd); + cached_mouse = new WindowsDirectInputMouse(dinput, hwnd); // cached_keyboard = new WindowsKeyboard(dinput, hwnd); } if (isGrabbed()) { @@ -105,7 +105,7 @@ final class WindowsAWTInput extends AbstractAWTInput { * task bar and clicking on it. So we'll use ClipCursor to * contain it while the cursor is grabbed. */ - WindowsDisplay.setupCursorClipping(hwnd); + WindowsDisplay.setupCursorClipping(hwnd, true); // Just clip it to a fullscreen window if (getCanvas().getCursor() != blank_cursor) { cached_cursor = getCanvas().getCursor(); /** diff --git a/src/java/org/lwjgl/opengl/WindowsDirectInputMouse.java b/src/java/org/lwjgl/opengl/WindowsDirectInputMouse.java new file mode 100644 index 00000000..ce409504 --- /dev/null +++ b/src/java/org/lwjgl/opengl/WindowsDirectInputMouse.java @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2002-2004 LWJGL 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 'LWJGL' 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.opengl; + +/** + * This is the Windows implementation of the Mouse. + * @author elias_naur + */ + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.nio.CharBuffer; + +import org.lwjgl.LWJGLException; +import org.lwjgl.LWJGLUtil; +import org.lwjgl.BufferUtils; +import org.lwjgl.input.Mouse; + +final class WindowsDirectInputMouse { + private final static int BUFFER_SIZE = 50; + private final static int BUTTON_STATES_SIZE = 7; + + private final static int DIMOFS_X = 0; + private final static int DIMOFS_Y = 4; + private final static int DIMOFS_Z = 8; + private final static int DIMOFS_BUTTON0 = 12; + private final static int DIMOFS_BUTTON1 = 13; + private final static int DIMOFS_BUTTON2 = 14; + private final static int DIMOFS_BUTTON3 = 15; + + private final long hwnd; + private final WindowsDirectInput dinput; + private final WindowsDirectInputDevice mouse; + + private final int mouse_button_count; + private final boolean has_wheel; + + private final EventQueue event_queue = new EventQueue(Mouse.EVENT_SIZE); + /* Buffer to hold a DIMOUSESTATE */ + private final ByteBuffer mouse_state; + private final IntBuffer temp_data_buffer; + + private final ByteBuffer mouse_event = ByteBuffer.allocate(Mouse.EVENT_SIZE); + + private boolean mouse_grabbed; + private byte[] win32_message_button_states = new byte[BUTTON_STATES_SIZE]; + private int accum_dwheel; + private int last_x; + private int last_y; + + public WindowsDirectInputMouse(WindowsDirectInput dinput, long hwnd) throws LWJGLException { + this.hwnd = hwnd; + this.dinput = dinput; + try { + mouse = dinput.createDevice(WindowsDirectInput.MOUSE_TYPE); + try { + mouse.setDataFormat(WindowsDirectInput.MOUSE_TYPE); + mouse.setBufferSize(BUFFER_SIZE); + if (!acquireNonExclusive()) + throw new LWJGLException("Failed to acquire mouse non-exclusive"); + } catch (LWJGLException e) { + mouse.release(); + throw e; + } + } catch (LWJGLException e) { + dinput.release(); + throw e; + } + MouseEnumerator enumerator = new MouseEnumerator(); + mouse.enumObjects(enumerator); + this.mouse_button_count = Math.min(enumerator.getButtonCount(), 4); + this.has_wheel = enumerator.hasWheel(); + mouse_state = BufferUtils.createByteBuffer(3*4 + 4); + temp_data_buffer = BufferUtils.createIntBuffer(BUFFER_SIZE*WindowsDirectInputDevice.DATA_SIZE); + } + + public boolean hasWheel() { + return has_wheel; + } + + public int getButtonCount() { + return mouse_button_count; + } + + private boolean acquire(int flags) { + try { + mouse.setCooperateLevel(hwnd, flags); + mouse.acquire(); + return true; + } catch (LWJGLException e) { + LWJGLUtil.log("Failed to acquire mouse: " + e); + return false; + } + } + + private boolean acquireNonExclusive() { + return acquire(WindowsDirectInputDevice.DISCL_NONEXCLUSIVE | WindowsDirectInputDevice.DISCL_FOREGROUND) || + acquire(WindowsDirectInputDevice.DISCL_NONEXCLUSIVE | WindowsDirectInputDevice.DISCL_BACKGROUND); + } + + public void destroy() { + mouse.unacquire(); + mouse.release(); + dinput.release(); + } + + public void poll(IntBuffer coord_buffer, ByteBuffer buttons) { + int ret = mouse.acquire(); + if (ret != WindowsDirectInput.DI_OK && ret != WindowsDirectInput.DI_NOEFFECT) + return; + mouse.poll(); + for (int i = 0; i < coord_buffer.remaining(); i++) + coord_buffer.put(coord_buffer.position() + i, 0); + mouse_state.clear(); + ret = mouse.getDeviceState(mouse_state); + int mouse_state_lx = mouse_state.getInt(); + int mouse_state_ly = mouse_state.getInt(); + int mouse_state_lz = mouse_state.getInt(); + int num_buttons = mouse_button_count; + if (mouse_grabbed || ret == WindowsDirectInput.DI_OK) { + if (ret != WindowsDirectInput.DI_OK) { + LWJGLUtil.log("Error getting mouse state: (0x" + Integer.toHexString(ret) + ")"); + return; + } + + coord_buffer.put(coord_buffer.position() + 2, mouse_state_lz); + if (num_buttons > buttons.remaining()) + num_buttons = buttons.remaining(); + for (int j = 0; j < num_buttons; j++) { + byte button_state = (mouse_state.get() & 0x80) != 0 ? (byte)1 : (byte)0; + buttons.put(buttons.position() + j, button_state); + // track the button state in the windows message buffer state array + // to get accurate button information when releasing a grab + win32_message_button_states[j] = button_state; + } + } else { + coord_buffer.put(coord_buffer.position() + 2, accum_dwheel); + if (num_buttons > win32_message_button_states.length) + num_buttons = win32_message_button_states.length; + for (int j = 0; j < num_buttons; j++) { + buttons.put(buttons.position() + j, win32_message_button_states[j]); + } + } + accum_dwheel = 0; + if (mouse_grabbed) { + coord_buffer.put(coord_buffer.position() + 0, mouse_state_lx); + coord_buffer.put(coord_buffer.position() + 1, -mouse_state_ly); + } else { + coord_buffer.put(coord_buffer.position() + 0, last_x); + coord_buffer.put(coord_buffer.position() + 1, last_y); + } + } + + private void putMouseEventWithCoords(byte button, byte state, int coord1, int coord2, int dz, long nanos) { + mouse_event.clear(); + mouse_event.put(button).put(state).putInt(coord1).putInt(coord2).putInt(dz).putLong(nanos); + mouse_event.flip(); + event_queue.putEvent(mouse_event); + } + + private void putMouseEvent(byte button, byte state, int dz, long nanos) { + if (mouse_grabbed) + putMouseEventWithCoords(button, state, 0, 0, dz, nanos); + else + putMouseEventWithCoords(button, state, last_x, last_y, dz, nanos); + } + + private void copyDXEvents(IntBuffer buffer) { + int buffer_index = 0; + int dx = 0, dy = 0, dwheel = 0; + byte button_state; + int i; + long nanos = 0; + while (buffer.hasRemaining()) { + int dwOfs = buffer.get(); + int dwData = buffer.get(); + long dwTimeStamp = ((long)buffer.get()) & 0xFFFFFFFF; + nanos = dwTimeStamp*1000000; + button_state = (dwData & 0x80) != 0 ? (byte)1 : (byte)0; + switch (dwOfs) { + case DIMOFS_BUTTON0: + putMouseEventWithCoords((byte)0, button_state, dx, -dy, dwheel, nanos); + dx = dy = dwheel = 0; + break; + case DIMOFS_BUTTON1: + putMouseEventWithCoords((byte)1, button_state, dx, -dy, dwheel, nanos); + dx = dy = dwheel = 0; + break; + case DIMOFS_BUTTON2: + putMouseEventWithCoords((byte)2, button_state, dx, -dy, dwheel, nanos); + dx = dy = dwheel = 0; + break; + case DIMOFS_BUTTON3: + putMouseEventWithCoords((byte)3, button_state, dx, -dy, dwheel, nanos); + dx = dy = dwheel = 0; + break; + case DIMOFS_X: + dx += dwData; + break; + case DIMOFS_Y: + dy += dwData; + break; + case DIMOFS_Z: + dwheel += dwData; + break; + } + } + if (dx != 0 || dy != 0 || dwheel != 0) + putMouseEventWithCoords((byte)-1, (byte)0, dx, -dy, dwheel, nanos); + } + + private void readDXBuffer() { + int ret = mouse.acquire(); + if (ret != WindowsDirectInput.DI_OK && ret != WindowsDirectInput.DI_NOEFFECT) + return; + mouse.poll(); + temp_data_buffer.clear(); + ret = mouse.getDeviceData(temp_data_buffer); + + switch (ret) { + case WindowsDirectInput.DI_OK: + break; + case WindowsDirectInput.DI_BUFFEROVERFLOW: + LWJGLUtil.log("Mouse buffer overflowed"); + break; + case WindowsDirectInput.DIERR_INPUTLOST: + LWJGLUtil.log("Mouse input lost"); + break; + case WindowsDirectInput.DIERR_NOTACQUIRED: + LWJGLUtil.log("Mouse not acquired"); + break; + default: + LWJGLUtil.log("unknown mouse error (" + Integer.toHexString(ret) + ")"); + break; + } + } + + public final void flush() { + readDXBuffer(); + temp_data_buffer.clear(); + } + + public void read(ByteBuffer buffer) { + readDXBuffer(); + if (mouse_grabbed) { + temp_data_buffer.flip(); + copyDXEvents(temp_data_buffer); + } + event_queue.copyEvents(buffer); + } + + public void grab(boolean grab) { + if(grab) { + if (!mouse_grabbed) { + flush(); + mouse_grabbed = true; + mouse.unacquire(); + if (!acquire(WindowsDirectInputDevice.DISCL_EXCLUSIVE | WindowsDirectInputDevice.DISCL_FOREGROUND)) + LWJGLUtil.log("Failed to reset cooperative mode"); + } + } else { + if (mouse_grabbed) { + mouse_grabbed = false; + mouse.unacquire(); + acquireNonExclusive(); + } + } + event_queue.clearEvents(); + } + + public void handleMouseScrolled(int event_dwheel, long millis) { + accum_dwheel += event_dwheel; + putMouseEvent((byte)-1, (byte)0, event_dwheel, millis*1000000); + } + + public void handleMouseMoved(int x, int y, long millis) { + int dx; + int dy; + dx = x - last_x; + dy = y - last_y; + last_x = x; + last_y = y; + long nanos = millis*1000000; + if (mouse_grabbed) { + putMouseEventWithCoords((byte)-1, (byte)0, dx, dy, 0, nanos); + } else { + putMouseEventWithCoords((byte)-1, (byte)0, x, y, 0, nanos); + } + } + + public void handleMouseButton(byte button, byte state, long millis) { + putMouseEvent(button, state, 0, millis*1000000); + if (button < BUTTON_STATES_SIZE) + win32_message_button_states[button] = state != 0 ? (byte)1 : (byte)0; + } + + private static class MouseEnumerator implements WindowsDirectInputDeviceObjectCallback { + private int button_count; + private boolean has_wheel; + + public int getButtonCount() { + return button_count; + } + + public boolean hasWheel() { + return has_wheel; + } + + public boolean nextObject(int type, String name) { + LWJGLUtil.log("Found mouse object: " + name); + switch (type) { + case WindowsDirectInputDevice.GUID_ZAxis: + has_wheel = true; + break; + case WindowsDirectInputDevice.GUID_Button: + button_count++; + break; + default: + break; + } + return true; + } + } +} diff --git a/src/java/org/lwjgl/opengl/WindowsDisplay.java b/src/java/org/lwjgl/opengl/WindowsDisplay.java index 3cc59c99..e5fb7c0a 100644 --- a/src/java/org/lwjgl/opengl/WindowsDisplay.java +++ b/src/java/org/lwjgl/opengl/WindowsDisplay.java @@ -44,6 +44,7 @@ import java.nio.IntBuffer; import org.lwjgl.LWJGLException; import org.lwjgl.LWJGLUtil; +import org.lwjgl.BufferUtils; import org.lwjgl.input.Cursor; final class WindowsDisplay implements DisplayImplementation { @@ -85,7 +86,10 @@ final class WindowsDisplay implements DisplayImplementation { private final static int SC_CONTEXTHELP = 0xF180; private final static int SC_SEPARATOR = 0xF00F; - private final static int SM_CXCURSOR = 13; + final static int SM_CXCURSOR = 13; + final static int SM_CYCURSOR = 14; + final static int SM_CMOUSEBUTTONS = 43; + final static int SM_MOUSEWHEELPRESENT = 75; private final static int SIZE_RESTORED = 0; private final static int SIZE_MINIMIZED = 1; @@ -99,9 +103,14 @@ final class WindowsDisplay implements DisplayImplementation { private final static int SW_SHOWDEFAULT = 10; private final static int SW_RESTORE = 9; + private final static IntBuffer rect_buffer = BufferUtils.createIntBuffer(4); + private final static Rect rect = new Rect(); + private final static Rect rect2 = new Rect(); private static WindowsDisplay current_display; + private static boolean cursor_clipped; private WindowsDisplayPeerInfo peer_info; + private Object current_cursor; private WindowsKeyboard keyboard; private WindowsMouse mouse; @@ -141,12 +150,37 @@ final class WindowsDisplay implements DisplayImplementation { public void destroyWindow() { nDestroyWindow(); - if (isFullscreen) - resetCursorClipping(); + resetCursorClipping(); } private static native void nDestroyWindow(); - static native void resetCursorClipping(); - static native void setupCursorClipping(long hwnd) throws LWJGLException; + static void resetCursorClipping() { + if (cursor_clipped) { + try { + clipCursor(0, null); + } catch (LWJGLException e) { + LWJGLUtil.log("Failed to reset cursor clipping: " + e); + } + cursor_clipped = false; + } + } + + private static void getGlobalClientRect(long hwnd, Rect rect) { + rect_buffer.put(0, 0).put(1, 0); + clientToScreen(hwnd, rect_buffer); + int offset_x = rect_buffer.get(0); + int offset_y = rect_buffer.get(1); + getClientRect(hwnd, rect_buffer); + rect.copyFromBuffer(rect_buffer); + rect.offset(offset_x, offset_y); + } + + static void setupCursorClipping(long hwnd) throws LWJGLException { + cursor_clipped = true; + getGlobalClientRect(hwnd, rect); + rect.copyToBuffer(rect_buffer); + clipCursor(hwnd, rect_buffer); + } + private static native void clipCursor(long hwnd, IntBuffer rect) throws LWJGLException; public void switchDisplayMode(DisplayMode mode) throws LWJGLException { nSwitchDisplayMode(mode); @@ -330,11 +364,12 @@ final class WindowsDisplay implements DisplayImplementation { } public void createMouse() throws LWJGLException { - mouse = new WindowsMouse(createDirectInput(), getHwnd()); + mouse = new WindowsMouse(getHwnd()); } public void destroyMouse() { - mouse.destroy(); + if (mouse != null) + mouse.destroy(); mouse = null; } @@ -349,7 +384,12 @@ final class WindowsDisplay implements DisplayImplementation { } public void grabMouse(boolean grab) { - mouse.grab(grab); + mouse.grab(grab, shouldGrab()); + try { + updateCursor(); + } catch (LWJGLException e) { + LWJGLUtil.log("Failed to update cursor: " + e); + } } public int getNativeCursorCapabilities() { @@ -357,11 +397,26 @@ final class WindowsDisplay implements DisplayImplementation { } public void setCursorPosition(int x, int y) { - nSetCursorPosition(x, y, isFullscreen); + getGlobalClientRect(getHwnd(), rect); + int transformed_x = rect.left + x; + int transformed_y = rect.bottom - 1 - y; + nSetCursorPosition(transformed_x, transformed_y); + setMousePosition(x, y); } - private static native void nSetCursorPosition(int x, int y, boolean fullscreen); + private static native void nSetCursorPosition(int x, int y); - public native void setNativeCursor(Object handle) throws LWJGLException; + public void setNativeCursor(Object handle) throws LWJGLException { + current_cursor = handle; + updateCursor(); + } + + private void updateCursor() throws LWJGLException { + if (mouse != null && mouse.isGrabbed()) + nSetNativeCursor(getHwnd(), mouse.getBlankCursor()); + else + nSetNativeCursor(getHwnd(), current_cursor); + } + static native void nSetNativeCursor(long hwnd, Object handle) throws LWJGLException; public int getMinCursorSize() { return getSystemMetrics(SM_CXCURSOR); @@ -371,10 +426,30 @@ final class WindowsDisplay implements DisplayImplementation { return getSystemMetrics(SM_CXCURSOR); } - public native int getSystemMetrics(int index); + static native int getSystemMetrics(int index); private static native long getDllInstance(); private static native long getHwnd(); + private static native long getDesktopWindow(); + static void centerCursor(long hwnd) { + getGlobalClientRect(getHwnd(), rect); + int local_offset_x = rect.left; + int local_offset_y = rect.top; + getGlobalClientRect(getDesktopWindow(), rect2); + Rect.intersect(rect, rect2, rect); + int center_x = (rect.left + rect.right)/2; + int center_y = (rect.top + rect.bottom)/2; + nSetCursorPosition(center_x, center_y); + int local_x = center_x - local_offset_x; + int local_y = center_y - local_offset_y; + if (current_display != null) + current_display.setMousePosition(local_x, transformY(getHwnd(), local_y)); + } + + private void setMousePosition(int x, int y) { + if (mouse != null) + mouse.setPosition(x, y); + } /* Keyboard */ public void createKeyboard() throws LWJGLException { @@ -398,13 +473,21 @@ final class WindowsDisplay implements DisplayImplementation { // public native int isStateKeySet(int key); - public native ByteBuffer nCreateCursor(int width, int height, int xHotspot, int yHotspot, int numImages, IntBuffer images, int images_offset, IntBuffer delays, int delays_offset) throws LWJGLException; + public static native ByteBuffer nCreateCursor(int width, int height, int xHotspot, int yHotspot, int numImages, IntBuffer images, int images_offset, IntBuffer delays, int delays_offset) throws LWJGLException; public Object createCursor(int width, int height, int xHotspot, int yHotspot, int numImages, IntBuffer images, IntBuffer delays) throws LWJGLException { + return doCreateCursor(width, height, xHotspot, yHotspot, numImages, images, delays); + } + + static Object doCreateCursor(int width, int height, int xHotspot, int yHotspot, int numImages, IntBuffer images, IntBuffer delays) throws LWJGLException { return nCreateCursor(width, height, xHotspot, yHotspot, numImages, images, images.position(), delays, delays != null ? delays.position() : -1); } - public native void destroyCursor(Object cursorHandle); + public void destroyCursor(Object cursorHandle) { + doDestroyCursor(cursorHandle); + } + static native void doDestroyCursor(Object cursorHandle); + public int getPbufferCapabilities() { try { // Return the capabilities of a minimum pixel format @@ -483,9 +566,13 @@ final class WindowsDisplay implements DisplayImplementation { mouse.handleMouseButton((byte)button, (byte)state, millis); } + private boolean shouldGrab() { + return !isMinimized && isFocused; + } + private void handleMouseMoved(int x, int y, long millis) { if (mouse != null) - mouse.handleMouseMoved(x, y, millis); + mouse.handleMouseMoved(x, y, millis, shouldGrab()); } private void handleMouseScrolled(int amount, long millis) { @@ -493,7 +580,15 @@ final class WindowsDisplay implements DisplayImplementation { mouse.handleMouseScrolled(amount, millis); } - private static native int transformY(long hwnd, int y); + private static native void getClientRect(long hwnd, IntBuffer rect); + + private static int transformY(long hwnd, int y) { + getClientRect(hwnd, rect_buffer); + rect.copyFromBuffer(rect_buffer); + return (rect.bottom - rect.top) - 1 - y; + } + + private static native void clientToScreen(long hwnd, IntBuffer point); private static boolean handleMessage(long hwnd, int msg, long wParam, long lParam, long millis) { if (current_display != null) @@ -503,12 +598,14 @@ final class WindowsDisplay implements DisplayImplementation { } private boolean doHandleMessage(long hwnd, int msg, long wParam, long lParam, long millis) { - if (isFullscreen && !isMinimized && isFocused) { + if ((isFullscreen || (mouse != null && mouse.isGrabbed())) && !isMinimized && isFocused) { try { setupCursorClipping(getHwnd()); } catch (LWJGLException e) { LWJGLUtil.log("setupCursorClipping failed: " + e.getMessage()); } + } else { + resetCursorClipping(); } switch (msg) { // disable screen saver and monitor power down messages which wreak havoc @@ -602,4 +699,40 @@ final class WindowsDisplay implements DisplayImplementation { public int getHeight() { return Display.getDisplayMode().getHeight(); } + + private static final class Rect { + public int top; + public int bottom; + public int left; + public int right; + + public void copyToBuffer(IntBuffer buffer) { + buffer.put(0, top).put(1, bottom).put(2, left).put(3, right); + } + + public void copyFromBuffer(IntBuffer buffer) { + top = buffer.get(0); + bottom = buffer.get(1); + left = buffer.get(2); + right = buffer.get(3); + } + + public void offset(int offset_x, int offset_y) { + left += offset_x; + right += offset_x; + top += offset_y; + bottom += offset_y; + } + + public static void intersect(Rect r1, Rect r2, Rect dst) { + dst.top = Math.max(r1.top, r2.top); + dst.bottom = Math.min(r1.bottom, r2.bottom); + dst.left = Math.max(r1.left, r2.left); + dst.right = Math.min(r1.right, r2.right); + } + + public String toString() { + return "Rect: top = " + top + " bottom = " + bottom + " left = " + left + " right = " + right; + } + } } diff --git a/src/java/org/lwjgl/opengl/WindowsMouse.java b/src/java/org/lwjgl/opengl/WindowsMouse.java index e82575ea..b0d1e0f1 100644 --- a/src/java/org/lwjgl/opengl/WindowsMouse.java +++ b/src/java/org/lwjgl/opengl/WindowsMouse.java @@ -44,63 +44,44 @@ import org.lwjgl.LWJGLException; import org.lwjgl.LWJGLUtil; import org.lwjgl.BufferUtils; import org.lwjgl.input.Mouse; +import org.lwjgl.input.Cursor; final class WindowsMouse { - private final static int BUFFER_SIZE = 50; - private final static int BUTTON_STATES_SIZE = 7; - - private final static int DIMOFS_X = 0; - private final static int DIMOFS_Y = 4; - private final static int DIMOFS_Z = 8; - private final static int DIMOFS_BUTTON0 = 12; - private final static int DIMOFS_BUTTON1 = 13; - private final static int DIMOFS_BUTTON2 = 14; - private final static int DIMOFS_BUTTON3 = 15; - private final long hwnd; - private final WindowsDirectInput dinput; - private final WindowsDirectInputDevice mouse; private final int mouse_button_count; private final boolean has_wheel; private final EventQueue event_queue = new EventQueue(Mouse.EVENT_SIZE); - /* Buffer to hold a DIMOUSESTATE */ - private final ByteBuffer mouse_state; - private final IntBuffer temp_data_buffer; private final ByteBuffer mouse_event = ByteBuffer.allocate(Mouse.EVENT_SIZE); + private final Object blank_cursor; private boolean mouse_grabbed; - private byte[] win32_message_button_states = new byte[BUTTON_STATES_SIZE]; + private byte[] button_states; + private int accum_dx; + private int accum_dy; private int accum_dwheel; private int last_x; private int last_y; - public WindowsMouse(WindowsDirectInput dinput, long hwnd) throws LWJGLException { + public WindowsMouse(long hwnd) throws LWJGLException { this.hwnd = hwnd; - this.dinput = dinput; - try { - mouse = dinput.createDevice(WindowsDirectInput.MOUSE_TYPE); - try { - mouse.setDataFormat(WindowsDirectInput.MOUSE_TYPE); - mouse.setBufferSize(BUFFER_SIZE); - if (!acquireNonExclusive()) - throw new LWJGLException("Failed to acquire mouse non-exclusive"); - } catch (LWJGLException e) { - mouse.release(); - throw e; - } - } catch (LWJGLException e) { - dinput.release(); - throw e; - } - MouseEnumerator enumerator = new MouseEnumerator(); - mouse.enumObjects(enumerator); - this.mouse_button_count = Math.min(enumerator.getButtonCount(), 4); - this.has_wheel = enumerator.hasWheel(); - mouse_state = BufferUtils.createByteBuffer(3*4 + 4); - temp_data_buffer = BufferUtils.createIntBuffer(BUFFER_SIZE*WindowsDirectInputDevice.DATA_SIZE); + this.mouse_button_count = WindowsDisplay.getSystemMetrics(WindowsDisplay.SM_CMOUSEBUTTONS); + this.has_wheel = WindowsDisplay.getSystemMetrics(WindowsDisplay.SM_MOUSEWHEELPRESENT) != 0; + this.blank_cursor = createBlankCursor(); + this.button_states = new byte[mouse_button_count]; + } + + private Object createBlankCursor() throws LWJGLException { + int width = WindowsDisplay.getSystemMetrics(WindowsDisplay.SM_CXCURSOR); + int height = WindowsDisplay.getSystemMetrics(WindowsDisplay.SM_CYCURSOR); + IntBuffer pixels = BufferUtils.createIntBuffer(width*height); + return WindowsDisplay.doCreateCursor(width, height, 0, 0, 1, pixels, null); + } + + public boolean isGrabbed() { + return mouse_grabbed; } public boolean hasWheel() { @@ -111,73 +92,24 @@ final class WindowsMouse { return mouse_button_count; } - private boolean acquire(int flags) { - try { - mouse.setCooperateLevel(hwnd, flags); - mouse.acquire(); - return true; - } catch (LWJGLException e) { - LWJGLUtil.log("Failed to acquire mouse: " + e); - return false; - } - } - - private boolean acquireNonExclusive() { - return acquire(WindowsDirectInputDevice.DISCL_NONEXCLUSIVE | WindowsDirectInputDevice.DISCL_FOREGROUND) || - acquire(WindowsDirectInputDevice.DISCL_NONEXCLUSIVE | WindowsDirectInputDevice.DISCL_BACKGROUND); - } - - public void destroy() { - mouse.unacquire(); - mouse.release(); - dinput.release(); - } - public void poll(IntBuffer coord_buffer, ByteBuffer buttons) { - int ret = mouse.acquire(); - if (ret != WindowsDirectInput.DI_OK && ret != WindowsDirectInput.DI_NOEFFECT) - return; - mouse.poll(); for (int i = 0; i < coord_buffer.remaining(); i++) coord_buffer.put(coord_buffer.position() + i, 0); - mouse_state.clear(); - ret = mouse.getDeviceState(mouse_state); - int mouse_state_lx = mouse_state.getInt(); - int mouse_state_ly = mouse_state.getInt(); - int mouse_state_lz = mouse_state.getInt(); int num_buttons = mouse_button_count; - if (mouse_grabbed || ret == WindowsDirectInput.DI_OK) { - if (ret != WindowsDirectInput.DI_OK) { - LWJGLUtil.log("Error getting mouse state: (0x" + Integer.toHexString(ret) + ")"); - return; - } - - coord_buffer.put(coord_buffer.position() + 2, mouse_state_lz); - if (num_buttons > buttons.remaining()) - num_buttons = buttons.remaining(); - for (int j = 0; j < num_buttons; j++) { - byte button_state = (mouse_state.get() & 0x80) != 0 ? (byte)1 : (byte)0; - buttons.put(buttons.position() + j, button_state); - // track the button state in the windows message buffer state array - // to get accurate button information when releasing a grab - win32_message_button_states[j] = button_state; - } - } else { - coord_buffer.put(coord_buffer.position() + 2, accum_dwheel); - if (num_buttons > win32_message_button_states.length) - num_buttons = win32_message_button_states.length; - for (int j = 0; j < num_buttons; j++) { - buttons.put(buttons.position() + j, win32_message_button_states[j]); - } + coord_buffer.put(coord_buffer.position() + 2, accum_dwheel); + if (num_buttons > button_states.length) + num_buttons = button_states.length; + for (int j = 0; j < num_buttons; j++) { + buttons.put(buttons.position() + j, button_states[j]); } - accum_dwheel = 0; - if (mouse_grabbed) { - coord_buffer.put(coord_buffer.position() + 0, mouse_state_lx); - coord_buffer.put(coord_buffer.position() + 1, -mouse_state_ly); + if (isGrabbed()) { + coord_buffer.put(coord_buffer.position() + 0, accum_dx); + coord_buffer.put(coord_buffer.position() + 1, accum_dy); } else { coord_buffer.put(coord_buffer.position() + 0, last_x); coord_buffer.put(coord_buffer.position() + 1, last_y); } + accum_dx = accum_dy = accum_dwheel = 0; } private void putMouseEventWithCoords(byte button, byte state, int coord1, int coord2, int dz, long nanos) { @@ -194,104 +126,30 @@ final class WindowsMouse { putMouseEventWithCoords(button, state, last_x, last_y, dz, nanos); } - private void copyDXEvents(IntBuffer buffer) { - int buffer_index = 0; - int dx = 0, dy = 0, dwheel = 0; - byte button_state; - int i; - long nanos = 0; - while (buffer.hasRemaining()) { - int dwOfs = buffer.get(); - int dwData = buffer.get(); - long dwTimeStamp = ((long)buffer.get()) & 0xFFFFFFFF; - nanos = dwTimeStamp*1000000; - button_state = (dwData & 0x80) != 0 ? (byte)1 : (byte)0; - switch (dwOfs) { - case DIMOFS_BUTTON0: - putMouseEventWithCoords((byte)0, button_state, dx, -dy, dwheel, nanos); - dx = dy = dwheel = 0; - break; - case DIMOFS_BUTTON1: - putMouseEventWithCoords((byte)1, button_state, dx, -dy, dwheel, nanos); - dx = dy = dwheel = 0; - break; - case DIMOFS_BUTTON2: - putMouseEventWithCoords((byte)2, button_state, dx, -dy, dwheel, nanos); - dx = dy = dwheel = 0; - break; - case DIMOFS_BUTTON3: - putMouseEventWithCoords((byte)3, button_state, dx, -dy, dwheel, nanos); - dx = dy = dwheel = 0; - break; - case DIMOFS_X: - dx += dwData; - break; - case DIMOFS_Y: - dy += dwData; - break; - case DIMOFS_Z: - dwheel += dwData; - break; - } - } - if (dx != 0 || dy != 0 || dwheel != 0) - putMouseEventWithCoords((byte)-1, (byte)0, dx, -dy, dwheel, nanos); - } - - private void readDXBuffer() { - int ret = mouse.acquire(); - if (ret != WindowsDirectInput.DI_OK && ret != WindowsDirectInput.DI_NOEFFECT) - return; - mouse.poll(); - temp_data_buffer.clear(); - ret = mouse.getDeviceData(temp_data_buffer); - - switch (ret) { - case WindowsDirectInput.DI_OK: - break; - case WindowsDirectInput.DI_BUFFEROVERFLOW: - LWJGLUtil.log("Mouse buffer overflowed"); - break; - case WindowsDirectInput.DIERR_INPUTLOST: - LWJGLUtil.log("Mouse input lost"); - break; - case WindowsDirectInput.DIERR_NOTACQUIRED: - LWJGLUtil.log("Mouse not acquired"); - break; - default: - LWJGLUtil.log("unknown mouse error (" + Integer.toHexString(ret) + ")"); - break; - } - } - - public final void flush() { - readDXBuffer(); - temp_data_buffer.clear(); - } - public void read(ByteBuffer buffer) { - readDXBuffer(); - if (mouse_grabbed) { - temp_data_buffer.flip(); - copyDXEvents(temp_data_buffer); - } event_queue.copyEvents(buffer); } - - public void grab(boolean grab) { - if(grab) { + + public Object getBlankCursor() { + return blank_cursor; + } + + public void grab(boolean grab, boolean should_center) { + if (grab) { if (!mouse_grabbed) { - flush(); mouse_grabbed = true; - mouse.unacquire(); - if (!acquire(WindowsDirectInputDevice.DISCL_EXCLUSIVE | WindowsDirectInputDevice.DISCL_FOREGROUND)) - LWJGLUtil.log("Failed to reset cooperative mode"); + try { + WindowsDisplay.setupCursorClipping(hwnd); + } catch (LWJGLException e) { + LWJGLUtil.log("Failed to setup cursor clipping: " + e); + } + if (should_center) + centerCursor(); } } else { if (mouse_grabbed) { mouse_grabbed = false; - mouse.unacquire(); - acquireNonExclusive(); + WindowsDisplay.resetCursorClipping(); } } event_queue.clearEvents(); @@ -302,52 +160,41 @@ final class WindowsMouse { putMouseEvent((byte)-1, (byte)0, event_dwheel, millis*1000000); } - public void handleMouseMoved(int x, int y, long millis) { - int dx; - int dy; - dx = x - last_x; - dy = y - last_y; - last_x = x; - last_y = y; - long nanos = millis*1000000; - if (mouse_grabbed) { - putMouseEventWithCoords((byte)-1, (byte)0, dx, dy, 0, nanos); - } else { - putMouseEventWithCoords((byte)-1, (byte)0, x, y, 0, nanos); - } + private void centerCursor() { + WindowsDisplay.centerCursor(hwnd); + } + + public void setPosition(int x, int y) { + this.last_x = x; + this.last_y = y; + } + + public void destroy() { + WindowsDisplay.doDestroyCursor(blank_cursor); + } + + public void handleMouseMoved(int x, int y, long millis, boolean should_center) { + int dx = x - last_x; + int dy = y - last_y; + if (dx != 0 || dy != 0) { + accum_dx += dx; + accum_dy += dy; + last_x = x; + last_y = y; + long nanos = millis*1000000; + if (mouse_grabbed) { + putMouseEventWithCoords((byte)-1, (byte)0, dx, dy, 0, nanos); + if (should_center) + centerCursor(); + } else { + putMouseEventWithCoords((byte)-1, (byte)0, x, y, 0, nanos); + } + } } public void handleMouseButton(byte button, byte state, long millis) { putMouseEvent(button, state, 0, millis*1000000); - if (button < BUTTON_STATES_SIZE) - win32_message_button_states[button] = state != 0 ? (byte)1 : (byte)0; - } - - private static class MouseEnumerator implements WindowsDirectInputDeviceObjectCallback { - private int button_count; - private boolean has_wheel; - - public int getButtonCount() { - return button_count; - } - - public boolean hasWheel() { - return has_wheel; - } - - public boolean nextObject(int type, String name) { - LWJGLUtil.log("Found mouse object: " + name); - switch (type) { - case WindowsDirectInputDevice.GUID_ZAxis: - has_wheel = true; - break; - case WindowsDirectInputDevice.GUID_Button: - button_count++; - break; - default: - break; - } - return true; - } + if (button < button_states.length) + button_states[button] = state != 0 ? (byte)1 : (byte)0; } } diff --git a/src/native/common/common_tools.c b/src/native/common/common_tools.c index d17b1cae..f63e68d2 100644 --- a/src/native/common/common_tools.c +++ b/src/native/common/common_tools.c @@ -141,6 +141,13 @@ void throwFMODException(JNIEnv * env, const char * err) { throwGeneralException(env, "org/lwjgl/fmod3/FMODException", err); } +void throwFormattedRuntimeException(JNIEnv * env, const char *format, ...) { + va_list ap; + va_start(ap, format); + throwFormattedGeneralException(env, "java/lang/RuntimeException", format, ap); + va_end(ap); +} + void throwFormattedException(JNIEnv * env, const char *format, ...) { va_list ap; va_start(ap, format); diff --git a/src/native/common/common_tools.h b/src/native/common/common_tools.h index a5d0925a..fd04049d 100644 --- a/src/native/common/common_tools.h +++ b/src/native/common/common_tools.h @@ -130,6 +130,7 @@ extern void putAttrib(attrib_list_t *list, int attrib); extern bool isDebugEnabled(void); extern jstring getVersionString(JNIEnv *env); extern void throwGeneralException(JNIEnv * env, const char *exception_name, const char * err); +extern void throwFormattedRuntimeException(JNIEnv * env, const char *format, ...); extern void throwException(JNIEnv *env, const char *msg); extern void throwFormattedException(JNIEnv * env, const char *format, ...); extern void throwFMODException(JNIEnv * env, const char * err); diff --git a/src/native/windows/org_lwjgl_input_Cursor.c b/src/native/windows/org_lwjgl_input_Cursor.c index 08351f5a..4371e706 100644 --- a/src/native/windows/org_lwjgl_input_Cursor.c +++ b/src/native/windows/org_lwjgl_input_Cursor.c @@ -45,7 +45,7 @@ #include "common_tools.h" JNIEXPORT jobject JNICALL Java_org_lwjgl_opengl_WindowsDisplay_nCreateCursor -(JNIEnv *env, jobject self, 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 unused, 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) { unsigned char col0, col1, col2, col3, col4, col5, col6, col7; unsigned char mask; @@ -154,8 +154,8 @@ JNIEXPORT jobject JNICALL Java_org_lwjgl_opengl_WindowsDisplay_nCreateCursor return handle_buffer; } -JNIEXPORT void JNICALL Java_org_lwjgl_opengl_WindowsDisplay_destroyCursor -(JNIEnv *env, jobject self, jobject handle_buffer) +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_WindowsDisplay_doDestroyCursor +(JNIEnv *env, jclass unused, jobject handle_buffer) { HCURSOR *cursor_handle = (HCURSOR *)(*env)->GetDirectBufferAddress(env, handle_buffer); DestroyCursor(*cursor_handle); diff --git a/src/native/windows/org_lwjgl_opengl_Display.c b/src/native/windows/org_lwjgl_opengl_Display.c index 3746894d..5530457a 100644 --- a/src/native/windows/org_lwjgl_opengl_Display.c +++ b/src/native/windows/org_lwjgl_opengl_Display.c @@ -210,17 +210,63 @@ JNIEXPORT void JNICALL Java_org_lwjgl_opengl_WindowsDisplay_nDestroyWindow(JNIEn freeSmallIcon(); } -JNIEXPORT void JNICALL Java_org_lwjgl_opengl_WindowsDisplay_resetCursorClipping(JNIEnv *env, jclass unused) { - ClipCursor(NULL); +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_WindowsDisplay_clientToScreen(JNIEnv *env, jclass unused, jlong hwnd_int, jobject buffer_handle) { + HWND hwnd = (HWND)(INT_PTR)hwnd_int; + POINT point; + jint *buffer = (jint *)(*env)->GetDirectBufferAddress(env, buffer_handle); + jlong size = (*env)->GetDirectBufferCapacity(env, buffer_handle); + if (size < 2) { + throwFormattedRuntimeException(env, "Buffer size < 2", size); + return; + } + point.x = buffer[0]; + point.y = buffer[1]; + ClientToScreen(hwnd, &point); + buffer[0] = point.x; + buffer[1] = point.y; } -JNIEXPORT void JNICALL Java_org_lwjgl_opengl_WindowsDisplay_setupCursorClipping(JNIEnv *env, jclass unused, jlong hwnd_ptr) { - HWND hwnd = (HWND)(INT_PTR)hwnd_ptr; - RECT hwnd_client; - if (hwnd != NULL && GetWindowRect(hwnd, &hwnd_client) != 0) { - if (ClipCursor(&hwnd_client) == 0) - throwFormattedException(env, "ClipCursor failed (%d)", GetLastError()); +JNIEXPORT jlong JNICALL Java_org_lwjgl_opengl_WindowsDisplay_getDesktopWindow(JNIEnv *env, jclass unused) { + return (INT_PTR)GetDesktopWindow(); +} + +static void copyBufferToRect(JNIEnv *env, jobject buffer_handle, RECT *rect) { + jint *buffer = (jint *)(*env)->GetDirectBufferAddress(env, buffer_handle); + jlong size = (*env)->GetDirectBufferCapacity(env, buffer_handle); + if (size < 4) { + throwFormattedRuntimeException(env, "Buffer size < 4", size); + return; } + rect->top = buffer[0]; + rect->bottom = buffer[1]; + rect->left = buffer[2]; + rect->right = buffer[3]; +} + +static void copyRectToBuffer(JNIEnv *env, RECT *rect, jobject buffer_handle) { + jint *buffer = (jint *)(*env)->GetDirectBufferAddress(env, buffer_handle); + jlong size = (*env)->GetDirectBufferCapacity(env, buffer_handle); + if (size < 4) { + throwFormattedRuntimeException(env, "Buffer size < 4", size); + return; + } + buffer[0] = rect->top; + buffer[1] = rect->bottom; + buffer[2] = rect->left; + buffer[3] = rect->right; +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_WindowsDisplay_clipCursor(JNIEnv *env, jclass unused, jlong hwnd_ptr, jobject handle_buffer) { + HWND hwnd = (HWND)(INT_PTR)hwnd_ptr; + RECT clip_rect; + LPRECT clip_rect_ptr; + if (handle_buffer != NULL) { + copyBufferToRect(env, handle_buffer, &clip_rect); + clip_rect_ptr = &clip_rect; + } else + clip_rect_ptr = NULL; + if (ClipCursor(clip_rect_ptr) == 0) + throwFormattedException(env, "ClipCursor failed (%d)", GetLastError()); } JNIEXPORT void JNICALL Java_org_lwjgl_opengl_WindowsDisplay_nSwitchDisplayMode(JNIEnv *env, jobject self, jobject mode) { @@ -452,64 +498,36 @@ JNIEXPORT jint JNICALL Java_org_lwjgl_opengl_WindowsDisplay_nSetWindowIcon32 } JNIEXPORT void JNICALL Java_org_lwjgl_opengl_WindowsDisplay_nSetCursorPosition -(JNIEnv * env, jclass unused, jint x, jint y, jboolean fullscreen) { - DWORD windowflags, exstyle; - int transformed_x, transformed_y; - RECT window_rect; - RECT client_rect; - RECT adjusted_client_rect; - - int left_border_width; - int bottom_border_width; - - getWindowFlags(&windowflags, &exstyle, fullscreen, getBooleanProperty(env, "org.lwjgl.opengl.Window.undecorated")); - if (!GetClientRect(getCurrentHWND(), &client_rect)) { - printfDebugJava(env, "GetClientRect failed"); - return; - } - - adjusted_client_rect = client_rect; - if (!AdjustWindowRectEx(&adjusted_client_rect, windowflags, FALSE, exstyle)) { - printfDebugJava(env, "AdjustWindowRectEx failed"); - return; - } - - if (!GetWindowRect(getCurrentHWND(), &window_rect)) { - printfDebugJava(env, "GetWindowRect failed"); - return; - } - left_border_width = -adjusted_client_rect.left; - bottom_border_width = adjusted_client_rect.bottom - client_rect.bottom; - - transformed_x = window_rect.left + left_border_width + x; - transformed_y = window_rect.bottom - bottom_border_width - 1 - y; - if (!SetCursorPos(transformed_x, transformed_y)) +(JNIEnv * env, jclass unused, jint x, jint y) { + if (!SetCursorPos(x, y)) printfDebugJava(env, "SetCursorPos failed"); } -JNIEXPORT void JNICALL Java_org_lwjgl_opengl_WindowsDisplay_setNativeCursor - (JNIEnv *env, jobject self, jobject handle_buffer) +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_WindowsDisplay_getClientRect + (JNIEnv *env, jclass unused, jlong hwnd_int, jobject rect_buffer) { + HWND hwnd = (HWND)(INT_PTR)hwnd_int; + RECT clientRect; + GetClientRect(hwnd, &clientRect); + copyRectToBuffer(env, &clientRect, rect_buffer); +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_WindowsDisplay_nSetNativeCursor + (JNIEnv *env, jclass unused, jlong hwnd_int, jobject handle_buffer) { + HWND hwnd = (HWND)(INT_PTR)hwnd_int; HCURSOR *cursor_handle; HCURSOR cursor; if (handle_buffer != NULL) { cursor_handle = (HCURSOR *)(*env)->GetDirectBufferAddress(env, handle_buffer); cursor = *cursor_handle; - SetClassLongPtr(getCurrentHWND(), GCL_HCURSOR, (LONG_PTR)cursor); + SetClassLongPtr(hwnd, GCL_HCURSOR, (LONG_PTR)cursor); SetCursor(cursor); } else { - SetClassLongPtr(getCurrentHWND(), GCL_HCURSOR, (LONG_PTR)NULL); + SetClassLongPtr(hwnd, GCL_HCURSOR, (LONG_PTR)NULL); SetCursor(LoadCursor(NULL, IDC_ARROW)); } } -JNIEXPORT jint JNICALL Java_org_lwjgl_opengl_WindowsDisplay_transformY(JNIEnv *env, jclass unused, jlong hwnd_int, jint y) { - HWND hwnd = (HWND)(INT_PTR)hwnd_int; - RECT clientRect; - GetClientRect(hwnd, &clientRect); - return (clientRect.bottom - clientRect.top) - 1 - y; -} - JNIEXPORT jint JNICALL Java_org_lwjgl_opengl_WindowsDisplay_getSystemMetrics(JNIEnv *env, jclass unused, jint index) { return GetSystemMetrics(index); }