From aa60495bb5834dd8d8e86ca9d7218730128b7b70 Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Wed, 8 Sep 2004 20:15:55 +0000 Subject: [PATCH] Linux: improve cursor position handling (warp behaviour) --- src/native/linux/Window.h | 5 +- src/native/linux/display.cpp | 8 ++ src/native/linux/display.h | 2 + src/native/linux/org_lwjgl_input_Cursor.cpp | 3 +- src/native/linux/org_lwjgl_input_Mouse.cpp | 123 ++++++++++-------- src/native/linux/org_lwjgl_opengl_Display.cpp | 28 ++-- 6 files changed, 92 insertions(+), 77 deletions(-) diff --git a/src/native/linux/Window.h b/src/native/linux/Window.h index ccc29646..cece8e53 100644 --- a/src/native/linux/Window.h +++ b/src/native/linux/Window.h @@ -55,15 +55,12 @@ */ extern bool releaseInput(void); - extern void resetCursor(int x, int y); - extern bool checkXError(JNIEnv *env); - extern Atom getWarpAtom(void); - /* * Various functions to release/acquire keyboard and mouse */ + extern void handleWarpEvent(XClientMessageEvent *); extern void handlePointerMotion(XMotionEvent *); extern void handleButtonPress(XButtonEvent *); extern void handleButtonRelease(XButtonEvent *); diff --git a/src/native/linux/display.cpp b/src/native/linux/display.cpp index 9febe640..e4be36fe 100644 --- a/src/native/linux/display.cpp +++ b/src/native/linux/display.cpp @@ -73,6 +73,14 @@ static unsigned short *g_ramp; static unsigned short *b_ramp; static extension current_extension = NONE; +int getScreenModeWidth(void) { + return current_width; +} + +int getScreenModeHeight(void) { + return current_height; +} + extension getCurrentDisplayModeExtension(void) { return current_extension; } diff --git a/src/native/linux/display.h b/src/native/linux/display.h index 6d0bd750..f9d0ba18 100644 --- a/src/native/linux/display.h +++ b/src/native/linux/display.h @@ -46,6 +46,8 @@ typedef enum {XRANDR, XF86VIDMODE, NONE} extension; +extern int getScreenModeWidth(void); +extern int getScreenModeHeight(void); extern jobject initDisplay(JNIEnv *env, int screen); extern void switchDisplayMode(JNIEnv * env, jobject mode, int screen); extern void resetDisplayMode(JNIEnv * env, int screen); diff --git a/src/native/linux/org_lwjgl_input_Cursor.cpp b/src/native/linux/org_lwjgl_input_Cursor.cpp index 5db3a7e9..8af11c2a 100644 --- a/src/native/linux/org_lwjgl_input_Cursor.cpp +++ b/src/native/linux/org_lwjgl_input_Cursor.cpp @@ -72,8 +72,7 @@ JNIEXPORT void JNICALL Java_org_lwjgl_input_Cursor_nCreateCursor for (int i = 0; i < num_images; i++) { XcursorImage *cursor_image = XcursorImageCreate(width, height); cursor_image->xhot = x_hotspot; - // Of some reason, the y hotspot coordinate is offset by 1 - cursor_image->yhot = y_hotspot + 1; + cursor_image->yhot = y_hotspot; cursor_image->pixels = &(pixels[stride*i]); if (num_images > 1) cursor_image->delay = delays[i]; diff --git a/src/native/linux/org_lwjgl_input_Mouse.cpp b/src/native/linux/org_lwjgl_input_Mouse.cpp index b7653593..1e041035 100644 --- a/src/native/linux/org_lwjgl_input_Mouse.cpp +++ b/src/native/linux/org_lwjgl_input_Mouse.cpp @@ -39,17 +39,16 @@ * @version $Revision$ */ - #include #include #include +#include #include #include #include "Window.h" #include "common_tools.h" +#include "display.h" #include "org_lwjgl_input_Mouse.h" -#include -//#include "extxcursor.h" #define NUM_BUTTONS 3 @@ -61,12 +60,12 @@ static bool pointer_grabbed; static bool created; +static int dx; +static int dy; +static int dz; static int last_x; static int last_y; static int last_z; -static int current_x; -static int current_y; -static int current_z; static jbyte buttons[NUM_BUTTONS]; static event_queue_t event_queue; static bool buffer_enabled; @@ -84,24 +83,21 @@ static int cap(int val, int min, int max) { } static void setCursorPos(int x, int y) { - current_x = cap(x, 0, getWindowWidth() - 1); - current_y = cap(y, 0, getWindowHeight() - 1); + int current_x = x; + int current_y = y; + dx += current_x - last_x; + dy += current_y - last_y; + last_x = current_x; + last_y = current_y; } static int transformY(int y) { return getWindowHeight() - 1 - y; } -static void transformCursorPos(int x, int y) { - // transform to OpenGL coordinate system center - y = transformY(y); - setCursorPos(x, y); -} - -void resetCursor(int x, int y) { - transformCursorPos(x, y); - last_x = current_x; - last_y = current_y; +static void resetCursor(int x, int y) { + last_x = x; + last_y = y; } static bool blankCursor(void) { @@ -170,27 +166,21 @@ void updatePointerGrab(void) { updateCursor(); } -static void doWarpPointer(void ) { - XEvent ignore_warp_guard; - ignore_warp_guard.type = ClientMessage; - ignore_warp_guard.xclient.message_type = getWarpAtom(); - ignore_warp_guard.xclient.format = 8; - // Tell event loop to start ignoring motion events - ignore_warp_guard.xclient.data.b[0] = 1; - XSendEvent(getDisplay(), getCurrentWindow(), False, 0, &ignore_warp_guard); - XWarpPointer(getDisplay(), None, getCurrentWindow(), 0, 0, 0, 0, getWindowWidth()/2, transformY(getWindowHeight()/2)); - // Tell event loop to stop ignoring motion events - ignore_warp_guard.xclient.data.b[0] = 0; - XSendEvent(getDisplay(), getCurrentWindow(), False, 0, &ignore_warp_guard); +void handleWarpEvent(XClientMessageEvent *event) { + int center_x = event->data.l[0]; + int center_y = event->data.l[1]; + resetCursor(center_x, center_y); } -static void warpPointer(void) { - if (!pointer_grabbed || !shouldGrab()) - return; - // Reset pointer to middle of screen if outside a certain inner border - if (current_x < POINTER_WARP_BORDER || current_y < POINTER_WARP_BORDER || - current_x > getWindowWidth() - POINTER_WARP_BORDER || current_y > getWindowHeight() - POINTER_WARP_BORDER) - doWarpPointer(); +static void doWarpPointer(int center_x, int center_y) { + XEvent warp_event; + warp_event.type = ClientMessage; + warp_event.xclient.message_type = getWarpAtom(); + warp_event.xclient.format = 32; + warp_event.xclient.data.l[0] = center_x; + warp_event.xclient.data.l[1] = center_y; + XSendEvent(getDisplay(), getCurrentWindow(), False, 0, &warp_event); + XWarpPointer(getDisplay(), None, getCurrentWindow(), 0, 0, 0, 0, center_x, center_y); } JNIEXPORT jint JNICALL Java_org_lwjgl_input_Mouse_nGetNativeCursorCaps @@ -247,7 +237,7 @@ JNIEXPORT void JNICALL Java_org_lwjgl_input_Mouse_nCreate if (disp == NULL) return; int i; - current_z = last_z = 0; + last_z = last_y = last_x = dx = dy = dz = 0; for (i = 0; i < NUM_BUTTONS; i++) buttons[i] = 0; if (!blankCursor()) { @@ -261,7 +251,7 @@ JNIEXPORT void JNICALL Java_org_lwjgl_input_Mouse_nCreate buffer_enabled = false; updatePointerGrab(); initEventQueue(&event_queue); - doWarpPointer(); + doWarpPointer(getWindowWidth()/2, getWindowHeight()/2); } JNIEXPORT void JNICALL Java_org_lwjgl_input_Mouse_nDestroy @@ -299,10 +289,10 @@ static void handleButton(XButtonEvent *event, unsigned char state) { void handleButtonPress(XButtonEvent *event) { switch (event->button) { case Button4: - current_z += WHEEL_SCALE; + dz += WHEEL_SCALE; break; case Button5: - current_z -= WHEEL_SCALE; + dz -= WHEEL_SCALE; break; default: break; } @@ -313,14 +303,44 @@ void handleButtonRelease(XButtonEvent *event) { handleButton(event, 0); } +static int max(int v1, int v2) { + return v1 > v2 ? v1 : v2; +} + +static int min(int v1, int v2) { + return v1 < v2 ? v1 : v2; +} + void handlePointerMotion(XMotionEvent *event) { - setCursorPos(event->x, event->y); + int x = event->x; + int y = event->y; + setCursorPos(x, y); + if (!pointer_grabbed || !shouldGrab()) + return; + int x_root = event->x_root; + int y_root = event->y_root; + // find the window position in root coordinates + int win_left = x_root - x; + int win_top = y_root - y; + int win_right = win_left + getWindowWidth(); + int win_bottom = win_top + getWindowHeight(); + // cap the window position to the screen dimensions + int border_left = max(0, win_left); + int border_top = max(0, win_top); + int border_right = min(getScreenModeWidth(), win_right); + int border_bottom = min(getScreenModeHeight(), win_bottom); + // determine whether the cursor is outside the bounds + bool outside_limits = x_root < border_left + POINTER_WARP_BORDER || y_root < border_top + POINTER_WARP_BORDER || + x_root > border_right - POINTER_WARP_BORDER || y_root > border_bottom - POINTER_WARP_BORDER; + if (outside_limits) { + // Find the center of the limits in window coordinates + int center_x = (border_right - border_left)/2; + int center_y = (border_bottom - border_top)/2; + doWarpPointer(center_x, center_y); + } } JNIEXPORT void JNICALL Java_org_lwjgl_input_Mouse_nPoll(JNIEnv * env, jclass clazz, jobject coord_buffer_obj, jobject button_buffer_obj) { - int moved_x = current_x - last_x; - int moved_y = -(current_y - last_y); - int moved_z = current_z - last_z; int *coords = (int *)env->GetDirectBufferAddress(coord_buffer_obj); int coords_length = env->GetDirectBufferCapacity(coord_buffer_obj); unsigned char *buttons_buffer = (unsigned char *)env->GetDirectBufferAddress(button_buffer_obj); @@ -329,18 +349,17 @@ JNIEXPORT void JNICALL Java_org_lwjgl_input_Mouse_nPoll(JNIEnv * env, jclass cla printfDebug("ERROR: Not enough space in coords array: %d < 3\n", coords_length); return; } - coords[0] = moved_x; - coords[1] = moved_y; - coords[2] = moved_z; - last_x = current_x; - last_y = current_y; - last_z = current_z; + coords[0] = dx; + coords[1] = -dy; + coords[2] = dz; + dx = 0; + dy = 0; + dz = 0; int num_buttons = NUM_BUTTONS; if (num_buttons > buttons_length) num_buttons = buttons_length; for (int i = 0; i < num_buttons; i++) buttons_buffer[i] = buttons[i]; - warpPointer(); } JNIEXPORT void JNICALL Java_org_lwjgl_input_Mouse_nEnableBuffer(JNIEnv *env, jclass clazz) { diff --git a/src/native/linux/org_lwjgl_opengl_Display.cpp b/src/native/linux/org_lwjgl_opengl_Display.cpp index 63f0fa35..49d2571f 100644 --- a/src/native/linux/org_lwjgl_opengl_Display.cpp +++ b/src/native/linux/org_lwjgl_opengl_Display.cpp @@ -88,7 +88,6 @@ static bool minimized; static bool focused; static bool closerequested; static bool grab; -static bool ignore_motion_events; static int current_screen; static Display *display_connection = NULL; @@ -105,10 +104,6 @@ GLXContext getCurrentGLXContext(void) { return context; } -Atom getWarpAtom(void) { - return warp_atom; -} - int getCurrentScreen(void) { return current_screen; } @@ -153,13 +148,17 @@ Display *incDisplay(JNIEnv *env) { printfDebug("Could not open X display connection\n"); return NULL; } - warp_atom = XInternAtom(getDisplay(), "ignore_warp_atom", False); + warp_atom = XInternAtom(display_connection, "_LWJGL_WARP", False); } async_x_error = false; display_connection_usage++; return display_connection; } +Atom getWarpAtom(void) { + return warp_atom; +} + void decDisplay(void) { display_connection_usage--; if (display_connection_usage == 0) { @@ -231,14 +230,6 @@ void setGrab(bool new_grab) { } } -static void handleMotion(XMotionEvent *event) { - if (ignore_motion_events) { - resetCursor(event->x, event->y); - } else { - handlePointerMotion(event); - } -} - static void checkInput(void) { Window win; int revert_mode; @@ -256,8 +247,8 @@ static void handleMessages() { XNextEvent(getDisplay(), &event); switch (event.type) { case ClientMessage: - if (event.xclient.message_type == getWarpAtom()) { - ignore_motion_events = event.xclient.data.b[0] == 1 ? true : false; + if (event.xclient.message_type == warp_atom) { + handleWarpEvent(&(event.xclient)); } else if ((event.xclient.format == 32) && ((Atom)event.xclient.data.l[0] == delete_atom)) closerequested = true; break; @@ -289,7 +280,7 @@ static void handleMessages() { handleButtonRelease(&(event.xbutton)); break; case MotionNotify: - handleMotion(&(event.xmotion)); + handlePointerMotion(&(event.xmotion)); break; case KeyPress: case KeyRelease: @@ -351,7 +342,6 @@ static bool createWindow(JNIEnv* env, int width, int height) { closerequested = false; vsync_enabled = false; grab = false; - ignore_motion_events = false; Window root_win; Window win; XSetWindowAttributes attribs; @@ -367,7 +357,7 @@ static bool createWindow(JNIEnv* env, int width, int height) { attribs.background_pixel = 0xFF000000; attribs.win_gravity = NorthWestGravity; attribmask = CWColormap | CWBackPixel | CWEventMask | CWWinGravity; - if (isLegacyFullscreen()/* || undecorated*/) { + if (isLegacyFullscreen()) { attribmask |= CWOverrideRedirect; attribs.override_redirect = True; }