Implement proper scroll event type detection in X11
Uses libinput scroll mode property to detect high res devices.
This commit is contained in:
parent
15206eda0b
commit
813c560e29
3 changed files with 128 additions and 125 deletions
128
glfw/x11_init.c
vendored
128
glfw/x11_init.c
vendored
|
|
@ -148,6 +148,75 @@ static void detectEWMH(void)
|
|||
XFree(supportedAtoms);
|
||||
}
|
||||
|
||||
static void
|
||||
read_xi_scroll_devices(void) {
|
||||
#define xi _glfw.x11.xi
|
||||
xi.num_scroll_devices = 0;
|
||||
// Require XI2.1 or later for smooth scrolling
|
||||
if (!xi.available || xi.major < 2 || (xi.major == 2 && xi.minor < 1) || !xi.LIBINPUT_SCROLL_METHOD_ENABLED) return;
|
||||
#undef xi
|
||||
int deviceCount;
|
||||
XIDeviceInfo* devices = XIQueryDevice(_glfw.x11.display, XIAllDevices, &deviceCount);
|
||||
if (!devices) return;
|
||||
for (int i = 0; i < deviceCount; i++) {
|
||||
XIDeviceInfo* device = &devices[i];
|
||||
if (device->use != XISlavePointer || !device->enabled) continue;
|
||||
Atom actual_type;
|
||||
int actual_format;
|
||||
unsigned long nitems, bytes_after;
|
||||
unsigned char *data = NULL;
|
||||
bool is_highres = false;
|
||||
|
||||
XIGetProperty(_glfw.x11.display, device->deviceid, _glfw.x11.xi.LIBINPUT_SCROLL_METHOD_ENABLED, 0, 2, False, XA_INTEGER, &actual_type, &actual_format, &nitems, &bytes_after, &data);
|
||||
if (data) {
|
||||
if (nitems > 1) is_highres = data[0] || data[1];
|
||||
XFree(data);
|
||||
}
|
||||
for (int j = 0; j < device->num_classes; j++) {
|
||||
if (device->classes[j]->type != XIScrollClass) continue;
|
||||
XIScrollClassInfo* scroll = (XIScrollClassInfo*)device->classes[j];
|
||||
XIScrollDevice *d = NULL;
|
||||
for (unsigned k = 0; k < _glfw.x11.xi.num_scroll_devices; k++) {
|
||||
XIScrollDevice *t = _glfw.x11.xi.scroll_devices + k;
|
||||
if (t->deviceid == device->deviceid && t->sourceid == scroll->sourceid) { d = t; break; }
|
||||
}
|
||||
if (!d) {
|
||||
if (_glfw.x11.xi.num_scroll_devices >= arraysz(_glfw.x11.xi.scroll_devices)) continue;
|
||||
d = &_glfw.x11.xi.scroll_devices[_glfw.x11.xi.num_scroll_devices++];
|
||||
*d = (XIScrollDevice){
|
||||
.is_highres=is_highres, .deviceid=device->deviceid, .sourceid=scroll->sourceid,
|
||||
};
|
||||
memcpy(d->name, device->name, MIN(sizeof(d->name)-1, strlen(device->name)));
|
||||
}
|
||||
if (d->num_valuators >= arraysz(d->valuators)) continue;
|
||||
XIScrollValuator *v = d->valuators + d->num_valuators++;
|
||||
*v = (XIScrollValuator){
|
||||
.number=scroll->number, .is_vertical=scroll->scroll_type == XIScrollTypeVertical,
|
||||
.increment=scroll->increment,
|
||||
};
|
||||
}
|
||||
for (int j = 0; j < device->num_classes; j++) {
|
||||
if (device->classes[j]->type != XIValuatorClass) continue;
|
||||
XIValuatorClassInfo* vi = (XIValuatorClassInfo*)device->classes[j];
|
||||
XIScrollDevice *d = NULL;
|
||||
for (unsigned k = 0; k < _glfw.x11.xi.num_scroll_devices; k++) {
|
||||
XIScrollDevice *t = _glfw.x11.xi.scroll_devices + k;
|
||||
if (t->deviceid == device->deviceid && t->sourceid == vi->sourceid) { d = t; break; }
|
||||
}
|
||||
if (!d) continue;
|
||||
XIScrollValuator *v = NULL;
|
||||
for (unsigned k = 0; k < d->num_valuators; k++) {
|
||||
XIScrollValuator *t = d->valuators + k;
|
||||
if (t->number == vi->number) { v = t; break; }
|
||||
}
|
||||
if (!v) continue;
|
||||
v->value = vi->value; v->mode = vi->mode; v->resolution = vi->resolution;
|
||||
v->min = vi->min; v->max = vi->max;
|
||||
}
|
||||
}
|
||||
XIFreeDeviceInfo(devices);
|
||||
}
|
||||
|
||||
// Look for and initialize supported X11 extensions
|
||||
//
|
||||
static bool initExtensions(void)
|
||||
|
|
@ -193,62 +262,6 @@ static bool initExtensions(void)
|
|||
&_glfw.x11.xi.minor) == Success)
|
||||
{
|
||||
_glfw.x11.xi.available = true;
|
||||
|
||||
// Detect smooth scrolling support once globally
|
||||
_glfw.x11.xi.smoothScroll.available = false;
|
||||
_glfw.x11.xi.smoothScroll.verticalAxis = -1;
|
||||
_glfw.x11.xi.smoothScroll.horizontalAxis = -1;
|
||||
_glfw.x11.xi.smoothScroll.verticalIncrement = 0.0;
|
||||
_glfw.x11.xi.smoothScroll.horizontalIncrement = 0.0;
|
||||
|
||||
// Require XI2.1 or later for smooth scrolling
|
||||
if (_glfw.x11.xi.major >= 2 && (_glfw.x11.xi.major > 2 || _glfw.x11.xi.minor >= 1))
|
||||
{
|
||||
if (XIQueryDevice && XIFreeDeviceInfo)
|
||||
{
|
||||
int deviceCount;
|
||||
XIDeviceInfo* devices = XIQueryDevice(_glfw.x11.display, XIAllMasterDevices, &deviceCount);
|
||||
if (devices)
|
||||
{
|
||||
for (int i = 0; i < deviceCount; i++)
|
||||
{
|
||||
XIDeviceInfo* device = &devices[i];
|
||||
|
||||
// Only process master pointer devices
|
||||
if (device->use != XIMasterPointer)
|
||||
continue;
|
||||
|
||||
for (int j = 0; j < device->num_classes; j++)
|
||||
{
|
||||
if (device->classes[j]->type != XIScrollClass)
|
||||
continue;
|
||||
|
||||
XIScrollClassInfo* scroll = (XIScrollClassInfo*)device->classes[j];
|
||||
|
||||
if (scroll->scroll_type == XIScrollTypeVertical)
|
||||
{
|
||||
_glfw.x11.xi.smoothScroll.verticalAxis = scroll->number;
|
||||
_glfw.x11.xi.smoothScroll.verticalIncrement = scroll->increment;
|
||||
}
|
||||
else if (scroll->scroll_type == XIScrollTypeHorizontal)
|
||||
{
|
||||
_glfw.x11.xi.smoothScroll.horizontalAxis = scroll->number;
|
||||
_glfw.x11.xi.smoothScroll.horizontalIncrement = scroll->increment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
XIFreeDeviceInfo(devices);
|
||||
|
||||
// Enable smooth scrolling if we found at least one scroll axis
|
||||
if (_glfw.x11.xi.smoothScroll.verticalAxis >= 0 ||
|
||||
_glfw.x11.xi.smoothScroll.horizontalAxis >= 0)
|
||||
{
|
||||
_glfw.x11.xi.smoothScroll.available = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -496,6 +509,9 @@ static bool initExtensions(void)
|
|||
_glfw.x11.MOTIF_WM_HINTS =
|
||||
XInternAtom(_glfw.x11.display, "_MOTIF_WM_HINTS", False);
|
||||
|
||||
_glfw.x11.xi.LIBINPUT_SCROLL_METHOD_ENABLED = XInternAtom(_glfw.x11.display, "libinput Scroll Method Enabled", False);
|
||||
read_xi_scroll_devices();
|
||||
|
||||
// The compositing manager selection name contains the screen number
|
||||
{
|
||||
char name[32];
|
||||
|
|
|
|||
23
glfw/x11_platform.h
vendored
23
glfw/x11_platform.h
vendored
|
|
@ -234,6 +234,18 @@ typedef struct AtomArray {
|
|||
size_t sz, capacity;
|
||||
} AtomArray;
|
||||
|
||||
typedef struct XIScrollValuator {
|
||||
double increment, value, min, max; int number, resolution, mode; bool is_vertical;
|
||||
} XIScrollValuator;
|
||||
|
||||
typedef struct XIScrollDevice {
|
||||
bool is_highres;
|
||||
int deviceid, sourceid;
|
||||
XIScrollValuator valuators[8];
|
||||
unsigned num_valuators;
|
||||
char name[32];
|
||||
} XIScrollDevice;
|
||||
|
||||
// X11-specific global data
|
||||
//
|
||||
typedef struct _GLFWlibraryX11
|
||||
|
|
@ -411,14 +423,9 @@ typedef struct _GLFWlibraryX11
|
|||
PFN_XIQueryDevice QueryDevice;
|
||||
PFN_XIFreeDeviceInfo FreeDeviceInfo;
|
||||
PFN_XIGetProperty GetProperty;
|
||||
// Smooth scrolling support
|
||||
struct {
|
||||
bool available;
|
||||
int verticalAxis;
|
||||
int horizontalAxis;
|
||||
double verticalIncrement;
|
||||
double horizontalIncrement;
|
||||
} smoothScroll;
|
||||
XIScrollDevice scroll_devices[16];
|
||||
unsigned num_scroll_devices;
|
||||
Atom LIBINPUT_SCROLL_METHOD_ENABLED;
|
||||
} xi;
|
||||
|
||||
struct {
|
||||
|
|
|
|||
102
glfw/x11_window.c
vendored
102
glfw/x11_window.c
vendored
|
|
@ -499,18 +499,12 @@ static void disableRawMouseMotion(_GLFWwindow* window UNUSED)
|
|||
//
|
||||
static void enableSmoothScrolling(_GLFWwindow* window)
|
||||
{
|
||||
if (!_glfw.x11.xi.smoothScroll.available)
|
||||
return;
|
||||
|
||||
// Initialize scroll valuator tracking for this window
|
||||
window->x11.smoothScroll.verticalValue = 0.0;
|
||||
window->x11.smoothScroll.horizontalValue = 0.0;
|
||||
|
||||
if (!_glfw.x11.xi.num_scroll_devices) return;
|
||||
// Select XI_Motion events on the window
|
||||
XIEventMask em;
|
||||
unsigned char mask[XIMaskLen(XI_Motion)] = { 0 };
|
||||
|
||||
em.deviceid = XIAllMasterDevices;
|
||||
em.deviceid = XIAllDevices;
|
||||
em.mask_len = sizeof(mask);
|
||||
em.mask = mask;
|
||||
XISetMask(mask, XI_Motion);
|
||||
|
|
@ -1343,71 +1337,57 @@ static void processEvent(XEvent *event)
|
|||
else if (event->xcookie.evtype == XI_Motion)
|
||||
{
|
||||
XIDeviceEvent* de = (XIDeviceEvent*)event->xcookie.data;
|
||||
|
||||
XIScrollDevice *d = NULL;
|
||||
for (unsigned i = 0; i < _glfw.x11.xi.num_scroll_devices; i++) {
|
||||
XIScrollDevice *t = &_glfw.x11.xi.scroll_devices[i];
|
||||
if (t->deviceid == de->deviceid && t->sourceid == de->sourceid) {
|
||||
d = t; break;
|
||||
}
|
||||
}
|
||||
// Find the window for this event
|
||||
_GLFWwindow* window = NULL;
|
||||
if (XFindContext(_glfw.x11.display, de->event, _glfw.x11.context,
|
||||
(XPointer*)&window) == 0 &&
|
||||
_glfw.x11.xi.smoothScroll.available)
|
||||
{
|
||||
double xOffset = 0.0;
|
||||
double yOffset = 0.0;
|
||||
bool hasScroll = false;
|
||||
|
||||
if (d && de->valuators.mask_len && XFindContext(
|
||||
_glfw.x11.display, de->event, _glfw.x11.context, (XPointer*)&window) == 0) {
|
||||
double xOffset = 0, yOffset = 0;
|
||||
// Process valuators to detect scroll events
|
||||
if (de->valuators.mask_len)
|
||||
{
|
||||
const double* values = de->valuators.values;
|
||||
|
||||
for (int i = 0; i < de->valuators.mask_len * 8; i++)
|
||||
{
|
||||
if (!XIMaskIsSet(de->valuators.mask, i))
|
||||
continue;
|
||||
|
||||
if (i == _glfw.x11.xi.smoothScroll.verticalAxis)
|
||||
{
|
||||
double delta = *values - window->x11.smoothScroll.verticalValue;
|
||||
window->x11.smoothScroll.verticalValue = *values;
|
||||
|
||||
if (_glfw.x11.xi.smoothScroll.verticalIncrement != 0.0)
|
||||
{
|
||||
yOffset = -delta / _glfw.x11.xi.smoothScroll.verticalIncrement;
|
||||
hasScroll = true;
|
||||
}
|
||||
GLFWOffsetType type = GLFW_SCROLL_OFFEST_HIGHRES;
|
||||
unsigned vidx = 0;
|
||||
for (int i = 0; i < de->valuators.mask_len * 8; i++) {
|
||||
if (!XIMaskIsSet(de->valuators.mask, i)) continue;
|
||||
const double value = de->valuators.values[vidx++];
|
||||
XIScrollValuator *v = NULL;
|
||||
for (unsigned k = 0; k < d->num_valuators; k++) {
|
||||
XIScrollValuator *t = d->valuators + k;
|
||||
if (t->number == i) { v = t; break; }
|
||||
}
|
||||
if (!v) continue;
|
||||
double delta = value - v->value;
|
||||
v->value = value;
|
||||
if (v->is_vertical) delta *= -1;
|
||||
double *off = v->is_vertical ? &yOffset : &xOffset;
|
||||
*off = delta;
|
||||
if (!d->is_highres) {
|
||||
if (v->increment == 120.) type = GLFW_SCROLL_OFFEST_V120;
|
||||
else {
|
||||
type = GLFW_SCROLL_OFFSET_LINES;
|
||||
if (v->increment != 0) *off /= v->increment;
|
||||
}
|
||||
else if (i == _glfw.x11.xi.smoothScroll.horizontalAxis)
|
||||
{
|
||||
double delta = *values - window->x11.smoothScroll.horizontalValue;
|
||||
window->x11.smoothScroll.horizontalValue = *values;
|
||||
|
||||
if (_glfw.x11.xi.smoothScroll.horizontalIncrement != 0.0)
|
||||
{
|
||||
xOffset = delta / _glfw.x11.xi.smoothScroll.horizontalIncrement;
|
||||
hasScroll = true;
|
||||
}
|
||||
}
|
||||
|
||||
values++;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasScroll)
|
||||
{
|
||||
if (xOffset != 0 || yOffset != 0) {
|
||||
// Get keyboard modifiers
|
||||
int mods = translateState(de->mods.effective);
|
||||
|
||||
// Scale offsets by content scale
|
||||
_glfwInputScroll(window, &(GLFWScrollEvent){
|
||||
.keyboard_modifiers = mods,
|
||||
.x_offset = xOffset * _glfw.x11.contentScaleX,
|
||||
.y_offset = yOffset * _glfw.x11.contentScaleY,
|
||||
.x_offset = xOffset * (type == GLFW_SCROLL_OFFEST_HIGHRES ? _glfw.x11.contentScaleX : 1),
|
||||
.y_offset = yOffset * (type == GLFW_SCROLL_OFFEST_HIGHRES ? _glfw.x11.contentScaleY : 1),
|
||||
.unscaled = {.x = xOffset, .y = yOffset},
|
||||
.offset_type = GLFW_SCROLL_OFFEST_HIGHRES
|
||||
.offset_type = type,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
XFreeEventData(_glfw.x11.display, &event->xcookie);
|
||||
}
|
||||
}
|
||||
|
|
@ -1544,22 +1524,22 @@ static void processEvent(XEvent *event)
|
|||
// Only use these if smooth scrolling is not available
|
||||
else if (event->xbutton.button == Button4)
|
||||
{
|
||||
if (!_glfw.x11.xi.smoothScroll.available)
|
||||
if (!_glfw.x11.xi.num_scroll_devices)
|
||||
_glfwInputScroll(window, &(GLFWScrollEvent){.keyboard_modifiers=mods, .y_offset=1, .unscaled.y=1});
|
||||
}
|
||||
else if (event->xbutton.button == Button5)
|
||||
{
|
||||
if (!_glfw.x11.xi.smoothScroll.available)
|
||||
if (!_glfw.x11.xi.num_scroll_devices)
|
||||
_glfwInputScroll(window, &(GLFWScrollEvent){.keyboard_modifiers=mods, .y_offset=-1, .unscaled.y=-1});
|
||||
}
|
||||
else if (event->xbutton.button == Button6)
|
||||
{
|
||||
if (!_glfw.x11.xi.smoothScroll.available)
|
||||
if (!_glfw.x11.xi.num_scroll_devices)
|
||||
_glfwInputScroll(window, &(GLFWScrollEvent){.keyboard_modifiers=mods, .x_offset=1, .unscaled.x=1});
|
||||
}
|
||||
else if (event->xbutton.button == Button7)
|
||||
{
|
||||
if (!_glfw.x11.xi.smoothScroll.available)
|
||||
if (!_glfw.x11.xi.num_scroll_devices)
|
||||
_glfwInputScroll(window, &(GLFWScrollEvent){.keyboard_modifiers=mods, .x_offset=-1, .unscaled.x=-1});
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue