Linux: improve cursor position handling (warp behaviour)

This commit is contained in:
Elias Naur 2004-09-08 20:15:55 +00:00
parent caf9b62ce9
commit aa60495bb5
6 changed files with 92 additions and 77 deletions

View File

@ -55,15 +55,12 @@
*/ */
extern bool releaseInput(void); extern bool releaseInput(void);
extern void resetCursor(int x, int y);
extern bool checkXError(JNIEnv *env); extern bool checkXError(JNIEnv *env);
extern Atom getWarpAtom(void); extern Atom getWarpAtom(void);
/* /*
* Various functions to release/acquire keyboard and mouse * Various functions to release/acquire keyboard and mouse
*/ */
extern void handleWarpEvent(XClientMessageEvent *);
extern void handlePointerMotion(XMotionEvent *); extern void handlePointerMotion(XMotionEvent *);
extern void handleButtonPress(XButtonEvent *); extern void handleButtonPress(XButtonEvent *);
extern void handleButtonRelease(XButtonEvent *); extern void handleButtonRelease(XButtonEvent *);

View File

@ -73,6 +73,14 @@ static unsigned short *g_ramp;
static unsigned short *b_ramp; static unsigned short *b_ramp;
static extension current_extension = NONE; static extension current_extension = NONE;
int getScreenModeWidth(void) {
return current_width;
}
int getScreenModeHeight(void) {
return current_height;
}
extension getCurrentDisplayModeExtension(void) { extension getCurrentDisplayModeExtension(void) {
return current_extension; return current_extension;
} }

View File

@ -46,6 +46,8 @@
typedef enum {XRANDR, XF86VIDMODE, NONE} extension; typedef enum {XRANDR, XF86VIDMODE, NONE} extension;
extern int getScreenModeWidth(void);
extern int getScreenModeHeight(void);
extern jobject initDisplay(JNIEnv *env, int screen); extern jobject initDisplay(JNIEnv *env, int screen);
extern void switchDisplayMode(JNIEnv * env, jobject mode, int screen); extern void switchDisplayMode(JNIEnv * env, jobject mode, int screen);
extern void resetDisplayMode(JNIEnv * env, int screen); extern void resetDisplayMode(JNIEnv * env, int screen);

View File

@ -72,8 +72,7 @@ JNIEXPORT void JNICALL Java_org_lwjgl_input_Cursor_nCreateCursor
for (int i = 0; i < num_images; i++) { for (int i = 0; i < num_images; i++) {
XcursorImage *cursor_image = XcursorImageCreate(width, height); XcursorImage *cursor_image = XcursorImageCreate(width, height);
cursor_image->xhot = x_hotspot; cursor_image->xhot = x_hotspot;
// Of some reason, the y hotspot coordinate is offset by 1 cursor_image->yhot = y_hotspot;
cursor_image->yhot = y_hotspot + 1;
cursor_image->pixels = &(pixels[stride*i]); cursor_image->pixels = &(pixels[stride*i]);
if (num_images > 1) if (num_images > 1)
cursor_image->delay = delays[i]; cursor_image->delay = delays[i];

View File

@ -39,17 +39,16 @@
* @version $Revision$ * @version $Revision$
*/ */
#include <X11/X.h> #include <X11/X.h>
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <X11/extensions/xf86vmode.h> #include <X11/extensions/xf86vmode.h>
#include <X11/Xcursor/Xcursor.h>
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include "Window.h" #include "Window.h"
#include "common_tools.h" #include "common_tools.h"
#include "display.h"
#include "org_lwjgl_input_Mouse.h" #include "org_lwjgl_input_Mouse.h"
#include <X11/Xcursor/Xcursor.h>
//#include "extxcursor.h"
#define NUM_BUTTONS 3 #define NUM_BUTTONS 3
@ -61,12 +60,12 @@
static bool pointer_grabbed; static bool pointer_grabbed;
static bool created; static bool created;
static int dx;
static int dy;
static int dz;
static int last_x; static int last_x;
static int last_y; static int last_y;
static int last_z; static int last_z;
static int current_x;
static int current_y;
static int current_z;
static jbyte buttons[NUM_BUTTONS]; static jbyte buttons[NUM_BUTTONS];
static event_queue_t event_queue; static event_queue_t event_queue;
static bool buffer_enabled; static bool buffer_enabled;
@ -84,24 +83,21 @@ static int cap(int val, int min, int max) {
} }
static void setCursorPos(int x, int y) { static void setCursorPos(int x, int y) {
current_x = cap(x, 0, getWindowWidth() - 1); int current_x = x;
current_y = cap(y, 0, getWindowHeight() - 1); 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) { static int transformY(int y) {
return getWindowHeight() - 1 - y; return getWindowHeight() - 1 - y;
} }
static void transformCursorPos(int x, int y) { static void resetCursor(int x, int y) {
// transform to OpenGL coordinate system center last_x = x;
y = transformY(y); last_y = y;
setCursorPos(x, y);
}
void resetCursor(int x, int y) {
transformCursorPos(x, y);
last_x = current_x;
last_y = current_y;
} }
static bool blankCursor(void) { static bool blankCursor(void) {
@ -170,27 +166,21 @@ void updatePointerGrab(void) {
updateCursor(); updateCursor();
} }
static void doWarpPointer(void ) { void handleWarpEvent(XClientMessageEvent *event) {
XEvent ignore_warp_guard; int center_x = event->data.l[0];
ignore_warp_guard.type = ClientMessage; int center_y = event->data.l[1];
ignore_warp_guard.xclient.message_type = getWarpAtom(); resetCursor(center_x, center_y);
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);
} }
static void warpPointer(void) { static void doWarpPointer(int center_x, int center_y) {
if (!pointer_grabbed || !shouldGrab()) XEvent warp_event;
return; warp_event.type = ClientMessage;
// Reset pointer to middle of screen if outside a certain inner border warp_event.xclient.message_type = getWarpAtom();
if (current_x < POINTER_WARP_BORDER || current_y < POINTER_WARP_BORDER || warp_event.xclient.format = 32;
current_x > getWindowWidth() - POINTER_WARP_BORDER || current_y > getWindowHeight() - POINTER_WARP_BORDER) warp_event.xclient.data.l[0] = center_x;
doWarpPointer(); 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 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) if (disp == NULL)
return; return;
int i; int i;
current_z = last_z = 0; last_z = last_y = last_x = dx = dy = dz = 0;
for (i = 0; i < NUM_BUTTONS; i++) for (i = 0; i < NUM_BUTTONS; i++)
buttons[i] = 0; buttons[i] = 0;
if (!blankCursor()) { if (!blankCursor()) {
@ -261,7 +251,7 @@ JNIEXPORT void JNICALL Java_org_lwjgl_input_Mouse_nCreate
buffer_enabled = false; buffer_enabled = false;
updatePointerGrab(); updatePointerGrab();
initEventQueue(&event_queue); initEventQueue(&event_queue);
doWarpPointer(); doWarpPointer(getWindowWidth()/2, getWindowHeight()/2);
} }
JNIEXPORT void JNICALL Java_org_lwjgl_input_Mouse_nDestroy 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) { void handleButtonPress(XButtonEvent *event) {
switch (event->button) { switch (event->button) {
case Button4: case Button4:
current_z += WHEEL_SCALE; dz += WHEEL_SCALE;
break; break;
case Button5: case Button5:
current_z -= WHEEL_SCALE; dz -= WHEEL_SCALE;
break; break;
default: break; default: break;
} }
@ -313,14 +303,44 @@ void handleButtonRelease(XButtonEvent *event) {
handleButton(event, 0); 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) { 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) { 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 = (int *)env->GetDirectBufferAddress(coord_buffer_obj);
int coords_length = env->GetDirectBufferCapacity(coord_buffer_obj); int coords_length = env->GetDirectBufferCapacity(coord_buffer_obj);
unsigned char *buttons_buffer = (unsigned char *)env->GetDirectBufferAddress(button_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); printfDebug("ERROR: Not enough space in coords array: %d < 3\n", coords_length);
return; return;
} }
coords[0] = moved_x; coords[0] = dx;
coords[1] = moved_y; coords[1] = -dy;
coords[2] = moved_z; coords[2] = dz;
last_x = current_x; dx = 0;
last_y = current_y; dy = 0;
last_z = current_z; dz = 0;
int num_buttons = NUM_BUTTONS; int num_buttons = NUM_BUTTONS;
if (num_buttons > buttons_length) if (num_buttons > buttons_length)
num_buttons = buttons_length; num_buttons = buttons_length;
for (int i = 0; i < num_buttons; i++) for (int i = 0; i < num_buttons; i++)
buttons_buffer[i] = buttons[i]; buttons_buffer[i] = buttons[i];
warpPointer();
} }
JNIEXPORT void JNICALL Java_org_lwjgl_input_Mouse_nEnableBuffer(JNIEnv *env, jclass clazz) { JNIEXPORT void JNICALL Java_org_lwjgl_input_Mouse_nEnableBuffer(JNIEnv *env, jclass clazz) {

View File

@ -88,7 +88,6 @@ static bool minimized;
static bool focused; static bool focused;
static bool closerequested; static bool closerequested;
static bool grab; static bool grab;
static bool ignore_motion_events;
static int current_screen; static int current_screen;
static Display *display_connection = NULL; static Display *display_connection = NULL;
@ -105,10 +104,6 @@ GLXContext getCurrentGLXContext(void) {
return context; return context;
} }
Atom getWarpAtom(void) {
return warp_atom;
}
int getCurrentScreen(void) { int getCurrentScreen(void) {
return current_screen; return current_screen;
} }
@ -153,13 +148,17 @@ Display *incDisplay(JNIEnv *env) {
printfDebug("Could not open X display connection\n"); printfDebug("Could not open X display connection\n");
return NULL; return NULL;
} }
warp_atom = XInternAtom(getDisplay(), "ignore_warp_atom", False); warp_atom = XInternAtom(display_connection, "_LWJGL_WARP", False);
} }
async_x_error = false; async_x_error = false;
display_connection_usage++; display_connection_usage++;
return display_connection; return display_connection;
} }
Atom getWarpAtom(void) {
return warp_atom;
}
void decDisplay(void) { void decDisplay(void) {
display_connection_usage--; display_connection_usage--;
if (display_connection_usage == 0) { 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) { static void checkInput(void) {
Window win; Window win;
int revert_mode; int revert_mode;
@ -256,8 +247,8 @@ static void handleMessages() {
XNextEvent(getDisplay(), &event); XNextEvent(getDisplay(), &event);
switch (event.type) { switch (event.type) {
case ClientMessage: case ClientMessage:
if (event.xclient.message_type == getWarpAtom()) { if (event.xclient.message_type == warp_atom) {
ignore_motion_events = event.xclient.data.b[0] == 1 ? true : false; handleWarpEvent(&(event.xclient));
} else if ((event.xclient.format == 32) && ((Atom)event.xclient.data.l[0] == delete_atom)) } else if ((event.xclient.format == 32) && ((Atom)event.xclient.data.l[0] == delete_atom))
closerequested = true; closerequested = true;
break; break;
@ -289,7 +280,7 @@ static void handleMessages() {
handleButtonRelease(&(event.xbutton)); handleButtonRelease(&(event.xbutton));
break; break;
case MotionNotify: case MotionNotify:
handleMotion(&(event.xmotion)); handlePointerMotion(&(event.xmotion));
break; break;
case KeyPress: case KeyPress:
case KeyRelease: case KeyRelease:
@ -351,7 +342,6 @@ static bool createWindow(JNIEnv* env, int width, int height) {
closerequested = false; closerequested = false;
vsync_enabled = false; vsync_enabled = false;
grab = false; grab = false;
ignore_motion_events = false;
Window root_win; Window root_win;
Window win; Window win;
XSetWindowAttributes attribs; XSetWindowAttributes attribs;
@ -367,7 +357,7 @@ static bool createWindow(JNIEnv* env, int width, int height) {
attribs.background_pixel = 0xFF000000; attribs.background_pixel = 0xFF000000;
attribs.win_gravity = NorthWestGravity; attribs.win_gravity = NorthWestGravity;
attribmask = CWColormap | CWBackPixel | CWEventMask | CWWinGravity; attribmask = CWColormap | CWBackPixel | CWEventMask | CWWinGravity;
if (isLegacyFullscreen()/* || undecorated*/) { if (isLegacyFullscreen()) {
attribmask |= CWOverrideRedirect; attribmask |= CWOverrideRedirect;
attribs.override_redirect = True; attribs.override_redirect = True;
} }