Linux: improve cursor position handling (warp behaviour)
This commit is contained in:
parent
caf9b62ce9
commit
aa60495bb5
|
@ -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 *);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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];
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue