Check XInput extension && touch inertia

(cherry picked from commit cca7fb97f4)
(cherry picked from commit edcb7310d3)
This commit is contained in:
Denis Konoplev
2019-10-16 13:30:48 +03:00
committed by jbrbot
parent ef9a57ef20
commit 3af71f9e55
6 changed files with 221 additions and 94 deletions

View File

@@ -59,7 +59,9 @@ public class XBaseWindow {
VISIBLE = "visible", // whether it is visible by default
SAVE_UNDER = "save under", // save content under this window
BACKING_STORE = "backing store", // enables double buffering
BIT_GRAVITY = "bit gravity"; // copy old content on geometry change
BIT_GRAVITY = "bit gravity", // copy old content on geometry change
XI_EVENT_MASK = "xi event mask", // xi event mask, Long
XI_DEVICE_ID = "xi device id"; // xi device id, Integer
private XCreateWindowParams delayedParams;
Set<Long> children = new HashSet<Long>();
@@ -265,9 +267,6 @@ public class XBaseWindow {
public XBaseWindow (XCreateWindowParams params) {
init(params);
// TODO find propper place for setup
XlibWrapper.SetupXI2(XToolkit.getDisplay(), window);
}
/* This create is used by the XEmbeddedFramePeer since it has to create the window
@@ -405,6 +404,19 @@ public class XBaseWindow {
throw new IllegalStateException("Couldn't create window because of wrong parameters. Run with NOISY_AWT to see details");
}
XToolkit.addToWinMap(window, this);
Long xiEventMask = (Long)params.get(XI_EVENT_MASK);
if (xiEventMask != null && XToolkit.isXInputEnabled()) {
Integer xiDeviceId = (Integer)params.get(XI_DEVICE_ID);
if (xiDeviceId == null) {
xiDeviceId = XConstants.XIAllDevices;
}
int status = XToolkit.XISelectEvents(XToolkit.getDisplay(), window, xiEventMask, xiDeviceId);
if (status != XConstants.Success) {
throw new IllegalStateException("Couldn't select XI events. Status: " + status);
}
}
} finally {
xattr.dispose();
}
@@ -1134,18 +1146,14 @@ public class XBaseWindow {
target = XToolkit.windowToXWindow(ev.get_xany().get_window());
}
if (target == null && ev.get_type() == XConstants.GenericEvent) {
if (XlibWrapper.XGetEventData(ev.get_xgeneric().get_display(), ev.pData)) {
target = XToolkit.windowToXWindow(XlibWrapper.GetXIDeviceEvent(ev.get_xcookie()).get_event());
}
if (target == null && ev.get_type() == XConstants.GenericEvent &&
XlibWrapper.XGetEventData(ev.get_xgeneric().get_display(), ev.pData)) {
target = XToolkit.windowToXWindow(XToolkit.GetXIDeviceEvent(ev.get_xcookie()).get_event());
}
if (target != null && target.checkInitialised()) {
target.dispatchEvent(ev);
}
// finally
XlibWrapper.XFreeEventData(ev.get_xgeneric().get_display(), ev.pData);
}
public void dispatchEvent(XEvent xev) {
@@ -1210,8 +1218,15 @@ public class XBaseWindow {
handleCreateNotify(xev);
break;
case XConstants.GenericEvent:
// TODO add XI switch
handleTouchEvent(xev);
switch (xev.get_xgeneric().get_evtype()) {
case XConstants.XI_TouchBegin:
case XConstants.XI_TouchUpdate:
case XConstants.XI_TouchEnd:
handleTouchEvent(xev);
break;
default:
break;
}
break;
}
}

View File

@@ -682,6 +682,10 @@ public final class XConstants {
public static final long XkbModifierMapMask = (1L<<2);
public static final long XkbVirtualModsMask = (1L<<6); //server map
/* Fake device ID's for event selection */
public static final int XIAllDevices = 0;
public static final int XIAllMasterDevices = 1;
/* XI Event types */
public static final int XI_DeviceChanged = 1;
public static final int XI_KeyPress = 2;
@@ -718,30 +722,30 @@ public final class XConstants {
* Note: the protocol spec defines a mask to be of (1 << type). Clients are
* free to create masks by bitshifting instead of using these defines.
*/
public static final int XI_DeviceChangedMask = 1 << XI_DeviceChanged;
public static final int XI_KeyPressMask = 1 << XI_KeyPress;
public static final int XI_KeyReleaseMask = 1 << XI_KeyRelease;
public static final int XI_ButtonPressMask = 1 << XI_ButtonPress;
public static final int XI_ButtonReleaseMask = 1 << XI_ButtonRelease;
public static final int XI_MotionMask = 1 << XI_Motion;
public static final int XI_EnterMask = 1 << XI_Enter;
public static final int XI_LeaveMask = 1 << XI_Leave;
public static final int XI_FocusInMask = 1 << XI_FocusIn;
public static final int XI_FocusOutMask = 1 << XI_FocusOut;
public static final int XI_HierarchyChangedMask = 1 << XI_HierarchyChanged;
public static final int XI_PropertyEventMask = 1 << XI_PropertyEvent;
public static final int XI_RawKeyPressMask = 1 << XI_RawKeyPress;
public static final int XI_RawKeyReleaseMask = 1 << XI_RawKeyRelease;
public static final int XI_RawButtonPressMask = 1 << XI_RawButtonPress;
public static final int XI_RawButtonReleaseMask = 1 << XI_RawButtonRelease;
public static final int XI_RawMotionMask = 1 << XI_RawMotion;
public static final int XI_TouchBeginMask = 1 << XI_TouchBegin;
public static final int XI_TouchEndMask = 1 << XI_TouchEnd;
public static final int XI_TouchOwnershipChangedMask = 1 << XI_TouchOwnership;
public static final int XI_TouchUpdateMask = 1 << XI_TouchUpdate;
public static final int XI_RawTouchBeginMask = 1 << XI_RawTouchBegin;
public static final int XI_RawTouchEndMask = 1 << XI_RawTouchEnd;
public static final int XI_RawTouchUpdateMask = 1 << XI_RawTouchUpdate;
public static final int XI_BarrierHitMask = 1 << XI_BarrierHit;
public static final int XI_BarrierLeaveMask = 1 << XI_BarrierLeave;
public static final long XI_DeviceChangedMask = 1L << XI_DeviceChanged;
public static final long XI_KeyPressMask = 1L << XI_KeyPress;
public static final long XI_KeyReleaseMask = 1L << XI_KeyRelease;
public static final long XI_ButtonPressMask = 1L << XI_ButtonPress;
public static final long XI_ButtonReleaseMask = 1L << XI_ButtonRelease;
public static final long XI_MotionMask = 1L << XI_Motion;
public static final long XI_EnterMask = 1L << XI_Enter;
public static final long XI_LeaveMask = 1L << XI_Leave;
public static final long XI_FocusInMask = 1L << XI_FocusIn;
public static final long XI_FocusOutMask = 1L << XI_FocusOut;
public static final long XI_HierarchyChangedMask = 1L << XI_HierarchyChanged;
public static final long XI_PropertyEventMask = 1L << XI_PropertyEvent;
public static final long XI_RawKeyPressMask = 1L << XI_RawKeyPress;
public static final long XI_RawKeyReleaseMask = 1L << XI_RawKeyRelease;
public static final long XI_RawButtonPressMask = 1L << XI_RawButtonPress;
public static final long XI_RawButtonReleaseMask = 1L << XI_RawButtonRelease;
public static final long XI_RawMotionMask = 1L << XI_RawMotion;
public static final long XI_TouchBeginMask = 1L << XI_TouchBegin;
public static final long XI_TouchEndMask = 1L << XI_TouchEnd;
public static final long XI_TouchOwnershipChangedMask = 1L << XI_TouchOwnership;
public static final long XI_TouchUpdateMask = 1L << XI_TouchUpdate;
public static final long XI_RawTouchBeginMask = 1L << XI_RawTouchBegin;
public static final long XI_RawTouchEndMask = 1L << XI_RawTouchEnd;
public static final long XI_RawTouchUpdateMask = 1L << XI_RawTouchUpdate;
public static final long XI_BarrierHitMask = 1L << XI_BarrierHit;
public static final long XI_BarrierLeaveMask = 1L << XI_BarrierLeave;
}

View File

@@ -316,6 +316,7 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
log.finer("X locale modifiers are not supported, using default");
}
tryXKB();
checkXInput();
arrowCursor = XlibWrapper.XCreateFontCursor(XToolkit.getDisplay(),
XCursorFontConstants.XC_arrow);
@@ -716,6 +717,8 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
XBaseWindow.ungrabInput();
processException(thr);
} finally {
// free event data if XGetEventData was called
XlibWrapper.XFreeEventData(getDisplay(), ev.pData);
awtUnlock();
}
}
@@ -2368,6 +2371,65 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
}
}
private static boolean hasXInputExtension = false;
public static boolean isXInputEnabled() {
awtLock();
try {
return hasXInputExtension;
} finally {
awtUnlock();
}
}
public static void checkXInput() {
awtLock();
try {
String extensionName = "XInputExtension";
boolean hasExtension = XlibWrapper.XQueryExtension(XToolkit.getDisplay(), extensionName,
XlibWrapper.iarg1, XlibWrapper.iarg2, XlibWrapper.iarg3);
if (!hasExtension) {
log.warning("X Input extension isn't available, error: {0}", Native.getInt(XlibWrapper.iarg1));
return;
}
// checking for 2.2 version
final int requiredMajor = 2;
final int requiredMinor = 2;
Native.putInt(XlibWrapper.iarg1, requiredMajor);
Native.putInt(XlibWrapper.iarg2, requiredMinor);
int status = XlibWrapper.XIQueryVersion(XToolkit.getDisplay(), XlibWrapper.iarg1, XlibWrapper.iarg2);
if (status == XConstants.BadRequest) {
log.warning("X Input2 not supported in the server");
return;
}
int major = Native.getInt(XlibWrapper.iarg1);
int minor = Native.getInt(XlibWrapper.iarg2);
if (major >= requiredMajor && minor >= requiredMinor) {
hasXInputExtension = true;
} else {
log.warning("Desired version is 2.2, server version is {0}.{1}", major, minor);
}
} finally {
awtUnlock();
}
}
public static XIDeviceEvent GetXIDeviceEvent(XGenericEventCookie cookie) {
return new XIDeviceEvent(cookie.get_data());
}
// Use this one instead of native version
public static int XISelectEvents(long display, long window, long mask, int deviceid) {
if (isXInputEnabled()) {
return XlibWrapper.XISelectEvents(display, window, mask, deviceid);
} else {
log.warning("Attempting to select xi events while xinput isn't available");
return XConstants.BadRequest;
}
}
private static long eventNumber;
public static long getEventNumber() {
awtLock();

View File

@@ -79,6 +79,14 @@ class XWindow extends XBaseWindow implements X11ComponentPeer {
static long lastButton = 0;
static WeakReference<XWindow> lastWindowRef = null;
static int clickCount = 0;
static int touchUpdates = 0;
private static int touchBeginX = 0, touchBeginY = 0;
private static final int TOUCH_CLICK_RADIUS = 2;
private static final int TOUCH_UPDATES_THRESHOLD = 2;
// all touch scrolls are measured in pixels
private static final int TOUCH_BEGIN = 2;
private static final int TOUCH_UPDATE = 3;
private static final int TOUCH_END = 4;
// used to check if we need to re-create surfaceData.
int oldWidth = -1;
@@ -239,6 +247,11 @@ class XWindow extends XBaseWindow implements X11ComponentPeer {
params.putIfNull(BACKING_STORE, XToolkit.getBackingStoreType());
params.putIfNull(XI_EVENT_MASK, XConstants.XI_TouchBeginMask |
XConstants.XI_TouchUpdateMask |
XConstants.XI_TouchEndMask);
params.putIfNull(XI_DEVICE_ID, XConstants.XIAllMasterDevices);
XToolkit.awtLock();
try {
if (wm_protocols == null) {
@@ -780,61 +793,92 @@ class XWindow extends XBaseWindow implements X11ComponentPeer {
}
public void handleTouchEvent(XEvent xev) {
XIDeviceEvent dev = XlibWrapper.GetXIDeviceEvent(xev.get_xcookie());
super.handleTouchEvent(xev);
XIDeviceEvent dev = XToolkit.GetXIDeviceEvent(xev.get_xcookie());
int x = scaleDown((int) dev.get_event_x());
int y = scaleDown((int) dev.get_event_y());
// TODO do we need this like in in mouse event handler
if (dev.get_event() != window) {
Point localXY = toLocal(x, y);
x = localXY.x;
y = localXY.y;
}
long when = dev.get_time();
long jWhen = XToolkit.nowMillisUTC_offset(when);
// TODO do we need any other button?
int button = XConstants.buttons[0];
int modifiers = getModifiers(dev.get_mods().get_effective(), button, 0);
// turning off shift modifier
modifiers &= ~InputEvent.SHIFT_DOWN_MASK;
long jWhen = XToolkit.nowMillisUTC_offset(dev.get_time());
switch (dev.get_evtype()) {
case XConstants.XI_TouchBegin:
touchUpdates = 0;
touchBeginX = x;
touchBeginY = y;
sendWheelEventFromTouch(dev, jWhen, modifiers, x, y, TOUCH_BEGIN, 1);
break;
case XConstants.XI_TouchUpdate:
int direction = y >= lastY ? -1 : 1;
int modifiers = 0;
int scrollAmount = Math.abs(lastY - y);
if (scrollAmount < Math.abs(lastX - x)) {
scrollAmount = Math.abs(lastX - x);
modifiers |= InputEvent.SHIFT_DOWN_MASK;
direction = x >= lastX ? -1 : 1;
if (isInsideTouchClickBoundaries(x, y)) {
break;
}
++touchUpdates;
if (scrollAmount < 1) {
// workaround to distinguish touch move and touch click
if (touchUpdates < TOUCH_UPDATES_THRESHOLD) {
break;
}
MouseWheelEvent mwe = new MouseWheelEvent(getEventSource(), MouseEvent.MOUSE_WHEEL, jWhen,
if (lastY - y != 0) {
sendWheelEventFromTouch(dev, jWhen, modifiers, x, y, TOUCH_UPDATE, lastY - y);
}
if (lastX - x != 0) {
// horizontal scroll
modifiers |= InputEvent.SHIFT_DOWN_MASK;
sendWheelEventFromTouch(dev, jWhen, modifiers, x, y, TOUCH_UPDATE, lastX - x);
}
break;
case XConstants.XI_TouchEnd:
if (touchUpdates < TOUCH_UPDATES_THRESHOLD) {
sendMouseEventFromTouch(dev, MouseEvent.MOUSE_PRESSED, jWhen, modifiers, x, y, button);
sendMouseEventFromTouch(dev, MouseEvent.MOUSE_RELEASED, jWhen, modifiers, x, y, button);
sendMouseEventFromTouch(dev, MouseEvent.MOUSE_CLICKED, jWhen, modifiers, x, y, button);
}
sendWheelEventFromTouch(dev, jWhen, modifiers, x, y, TOUCH_END, 1);
break;
}
lastX = x;
lastY = y;
}
private boolean isInsideTouchClickBoundaries(int x, int y) {
return Math.abs(touchBeginX - x) <= TOUCH_CLICK_RADIUS &&
Math.abs(touchBeginY - y) <= TOUCH_CLICK_RADIUS;
}
private void sendWheelEventFromTouch(XIDeviceEvent dev, long jWhen, int modifiers, int x, int y, int type, int delta) {
postEventToEventQueue(
new MouseWheelEvent(getEventSource(), MouseEvent.MOUSE_WHEEL, jWhen,
modifiers,
x, y,
scaleDown((int) dev.get_root_x()),
scaleDown((int) dev.get_root_y()),
1, false, MouseWheelEvent.WHEEL_UNIT_SCROLL,
scrollAmount, direction);
postEventToEventQueue(mwe);
0, false, type,
1, delta));
}
lastX = x;
lastY = y;
break;
case XConstants.XI_TouchBegin:
case XConstants.XI_TouchEnd:
// TODO add click events
lastX = x;
lastY = y;
break;
default:
// TODO remove this
System.out.println("Unknown");
break;
}
private void sendMouseEventFromTouch(XIDeviceEvent dev, int type, long jWhen, int modifiers, int x, int y, int button) {
postEventToEventQueue(
new MouseEvent(getEventSource(), type, jWhen,
modifiers,
x, y,
scaleDown((int) dev.get_root_x()),
scaleDown((int) dev.get_root_y()),
1,
false, button));
}
public void handleMotionNotify(XEvent xev) {

View File

@@ -554,16 +554,14 @@ static native String XSetLocaleModifiers(String modifier_list);
static native void SetZOrder(long display, long window, long above);
// static native void XISetMask(long );
// static native void XISelectEvents(long display, long window);
//TODO replace it with more general func
static native void SetupXI2(long display, long window);
// use wrapped functions from XToolkit
// instead these native functions
// these functions don't check if XInput extension is available
static native int XIQueryVersion(long display, long major_version_iptr, long minor_version_iptr);
static native int XISelectEvents(long display, long window, long mask, int deviceid);
static native boolean XGetEventData(long display, long ptr);
static native void XFreeEventData(long display, long ptr);
static XIDeviceEvent GetXIDeviceEvent(XGenericEventCookie cookie) {
return new XIDeviceEvent(cookie.get_data());
}
/* Global memory area used for X lib parameter passing */

View File

@@ -2320,26 +2320,30 @@ Java_sun_awt_X11_XlibWrapper_SetZOrder
/*
* Class: XlibWrapper
* Method: SetupXI2
* Method: XIQueryVersion
*/
JNIEXPORT void JNICALL
Java_sun_awt_X11_XlibWrapper_SetupXI2
(JNIEnv *env, jclass clazz, jlong display, jlong window)
JNIEXPORT jint JNICALL
Java_sun_awt_X11_XlibWrapper_XIQueryVersion
(JNIEnv *env, jclass clazz, jlong display, jlong major_version_iptr, jlong minor_version_iptr)
{
unsigned char mask[XIMaskLen(XI_LASTEVENT)];
memset(mask, 0, sizeof(mask));
XISetMask(mask, XI_TouchBegin);
XISetMask(mask, XI_TouchUpdate);
XISetMask(mask, XI_TouchEnd);
return XIQueryVersion((Display *)jlong_to_ptr(display),
jlong_to_ptr(major_version_iptr), jlong_to_ptr(minor_version_iptr));
}
/*
* Class: XlibWrapper
* Method: XISelectEvents
*/
JNIEXPORT jint JNICALL
Java_sun_awt_X11_XlibWrapper_XISelectEvents
(JNIEnv *env, jclass clazz, jlong display, jlong window, jlong mask, jint deviceid)
{
XIEventMask evmask;
// TODO make deviceid filtering
evmask.deviceid = XIAllMasterDevices;
evmask.mask_len = sizeof(mask);
evmask.mask = mask;
XISelectEvents((Display *)jlong_to_ptr(display), (Window)jlong_to_ptr(window), &evmask, 1);
XFlush((Display *)jlong_to_ptr(display));
evmask.deviceid = (int)deviceid;
evmask.mask_len = XIMaskLen(XI_LASTEVENT);
// FIXME potential byte order problem
evmask.mask = &mask;
return XISelectEvents((Display *)jlong_to_ptr(display), (Window)jlong_to_ptr(window), &evmask, 1);
}
/*