#ifndef RUBY_INPUT_MOUSE_XLIB #define RUBY_INPUT_MOUSE_XLIB namespace ruby { struct InputMouseXlib { HID::Mouse hid; uintptr_t handle = 0; Display* display = nullptr; Window rootWindow; Cursor invisibleCursor; unsigned screenWidth = 0; unsigned screenHeight = 0; struct Mouse { bool acquired = false; signed numerator = 0; signed denominator = 0; signed threshold = 0; unsigned relativeX = 0; unsigned relativeY = 0; } ms; bool acquire() { if(acquired()) return true; if(XGrabPointer(display, handle, True, 0, GrabModeAsync, GrabModeAsync, rootWindow, invisibleCursor, CurrentTime) == GrabSuccess) { //backup existing cursor acceleration settings XGetPointerControl(display, &ms.numerator, &ms.denominator, &ms.threshold); //disable cursor acceleration XChangePointerControl(display, True, False, 1, 1, 0); //center cursor (so that first relative poll returns 0, 0 if mouse has not moved) XWarpPointer(display, None, rootWindow, 0, 0, 0, 0, screenWidth / 2, screenHeight / 2); return ms.acquired = true; } else { return ms.acquired = false; } } bool unacquire() { if(acquired()) { //restore cursor acceleration and release cursor XChangePointerControl(display, True, True, ms.numerator, ms.denominator, ms.threshold); XUngrabPointer(display, CurrentTime); ms.acquired = false; } return true; } bool acquired() { return ms.acquired; } void assign(unsigned groupID, unsigned inputID, int16_t value) { auto& group = hid.group[groupID]; if(group.input[inputID].value == value) return; if(input.onChange) input.onChange(hid, groupID, inputID, group.input[inputID].value, value); group.input[inputID].value = value; } void poll(vector& devices) { Window rootReturn; Window childReturn; signed rootXReturn = 0; signed rootYReturn = 0; signed windowXReturn = 0; signed windowYReturn = 0; unsigned maskReturn = 0; XQueryPointer(display, handle, &rootReturn, &childReturn, &rootXReturn, &rootYReturn, &windowXReturn, &windowYReturn, &maskReturn); if(acquired()) { XWindowAttributes attributes; XGetWindowAttributes(display, handle, &attributes); //absolute -> relative conversion assign(HID::Mouse::GroupID::Axis, 0, (int16_t)(rootXReturn - screenWidth / 2)); assign(HID::Mouse::GroupID::Axis, 1, (int16_t)(rootYReturn - screenHeight / 2)); if(hid.axis().input[0].value != 0 || hid.axis().input[1].value != 0) { //if mouse moved, re-center mouse for next poll XWarpPointer(display, None, rootWindow, 0, 0, 0, 0, screenWidth / 2, screenHeight / 2); } } else { assign(HID::Mouse::GroupID::Axis, 0, (int16_t)(rootXReturn - ms.relativeX)); assign(HID::Mouse::GroupID::Axis, 1, (int16_t)(rootYReturn - ms.relativeY)); ms.relativeX = rootXReturn; ms.relativeY = rootYReturn; } assign(HID::Mouse::GroupID::Button, 0, (bool)(maskReturn & Button1Mask)); assign(HID::Mouse::GroupID::Button, 1, (bool)(maskReturn & Button2Mask)); assign(HID::Mouse::GroupID::Button, 2, (bool)(maskReturn & Button3Mask)); assign(HID::Mouse::GroupID::Button, 3, (bool)(maskReturn & Button4Mask)); assign(HID::Mouse::GroupID::Button, 4, (bool)(maskReturn & Button5Mask)); devices.append(&hid); } bool init(uintptr_t handle) { this->handle = handle; display = XOpenDisplay(0); rootWindow = DefaultRootWindow(display); XWindowAttributes attributes; XGetWindowAttributes(display, rootWindow, &attributes); screenWidth = attributes.width; screenHeight = attributes.height; //Xlib: because XShowCursor(display, false) would just be too easy //create invisible cursor for use when mouse is acquired Pixmap pixmap; XColor black, unused; static char invisibleData[8] = {0}; Colormap colormap = DefaultColormap(display, DefaultScreen(display)); XAllocNamedColor(display, colormap, "black", &black, &unused); pixmap = XCreateBitmapFromData(display, handle, invisibleData, 8, 8); invisibleCursor = XCreatePixmapCursor(display, pixmap, pixmap, &black, &black, 0, 0); XFreePixmap(display, pixmap); XFreeColors(display, colormap, &black.pixel, 1, 0); ms.acquired = false; ms.relativeX = 0; ms.relativeY = 0; hid.id = 2; hid.axis().append({"X"}); hid.axis().append({"Y"}); hid.button().append({"Left"}); hid.button().append({"Middle"}); hid.button().append({"Right"}); hid.button().append({"Up"}); hid.button().append({"Down"}); return true; } void term() { unacquire(); XFreeCursor(display, invisibleCursor); XCloseDisplay(display); } }; } #endif