From 2f970a963af867f8d92bd861c3bb5ea0c4f6260c Mon Sep 17 00:00:00 2001 From: alex2772-mi Date: Thu, 29 Jul 2021 19:06:23 +0300 Subject: [PATCH] scroll on linux (closes #65) --- .github/workflows/build.yml | 2 +- README.md | 1 + linux/CMakeLists.txt | 2 +- linux/cc/WindowManagerX11.cc | 133 +++++++++++++++++++++++++++++++++++ linux/cc/WindowManagerX11.hh | 20 ++++++ linux/cc/WindowX11.cc | 2 +- 6 files changed, 157 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e8144819..1ce3c118 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -69,7 +69,7 @@ jobs: with: fetch-depth: 0 - uses: seanmiddleditch/gha-setup-ninja@master - - run: sudo apt-get install libxcomposite-dev libxrandr-dev libgl1-mesa-dev + - run: sudo apt-get install libxcomposite-dev libxrandr-dev libgl1-mesa-dev libxi-dev - run: python3 script/deploy_native.py --arch=x64 env: SPACE_TOKEN: ${{ secrets.SPACE_TOKEN }} \ No newline at end of file diff --git a/README.md b/README.md index edbf7cb9..98510790 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,7 @@ x64 Native Tools Command Prompt for VS libxcomposite-dev libxrandr-dev libgl1-mesa-dev +libxi-dev Build: diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index 746be2a3..4923f4f9 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -37,5 +37,5 @@ target_include_directories(jwm PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../shared/cc ${ set_target_properties(jwm PROPERTIES OUTPUT_NAME "jwm_${JWM_ARCH}") -target_link_libraries(jwm PRIVATE X11::X11 X11::Xrandr) +target_link_libraries(jwm PRIVATE X11::X11 X11::Xrandr X11::Xi) target_link_libraries(jwm PRIVATE OpenGL::GL) \ No newline at end of file diff --git a/linux/cc/WindowManagerX11.cc b/linux/cc/WindowManagerX11.cc index b4d326a4..3dbde513 100644 --- a/linux/cc/WindowManagerX11.cc +++ b/linux/cc/WindowManagerX11.cc @@ -6,6 +6,7 @@ #include #include "AppX11.hh" #include +#include #include "KeyX11.hh" #include "MouseButtonX11.hh" @@ -119,6 +120,46 @@ WindowManagerX11::WindowManagerX11(): x11SWA.override_redirect = true; } + // XInput2 + { + int opcode, firstevent, firsterror; + if (XQueryExtension(display, "XInputExtension", &opcode, &firstevent, &firsterror)) { + int major = 2, minor = 3; + if (XIQueryVersion(display, &major, &minor) != BadRequest) { + _xi2 = std::make_unique(XInput2{ opcode }); + _xi2IterateDevices(); + } + } + } +} + +void WindowManagerX11::_xi2IterateDevices() { + int deviceCount; + XIDeviceInfo* devices = XIQueryDevice(display, XIAllDevices, &deviceCount); + for (int deviceId = 0; deviceId < deviceCount; ++deviceId) { + XIDeviceInfo& device = devices[deviceId]; + if (device.use != XIMasterPointer) { + continue; + } + XInput2::Device& myDevice = _xi2->deviceById[device.deviceid]; + for (int classId = 0; classId < device.num_classes; ++classId) { + XIAnyClassInfo* classInfo = device.classes[classId]; + + switch (classInfo->type) + { + case XIScrollClass: { + XIScrollClassInfo* scroll = reinterpret_cast(classInfo); + myDevice.scroll.push_back(XInput2::Device::ScrollValuator{ + scroll->scroll_type == XIScrollTypeHorizontal, + scroll->number, + scroll->increment + }); + break; + } + } + } + } + XIFreeDeviceInfo(devices); } ::Window WindowManagerX11::getRootWindow() const { @@ -138,11 +179,90 @@ void WindowManagerX11::runLoop() { myWindow = it->second; } if (myWindow == nullptr) { + // probably an XI2 event + if (ev.type == GenericEvent && _xi2 && _xi2->opcode == ev.xcookie.extension) { + if (XGetEventData(display, &ev.xcookie)) { + XIEvent* xiEvent = reinterpret_cast(ev.xcookie.data); + switch (xiEvent->evtype) { + case XI_DeviceChanged: + _xi2->deviceById.clear(); + _xi2IterateDevices(); + break; + + case XI_Motion: { + XIDeviceEvent* deviceEvent = reinterpret_cast(xiEvent); + + it = _nativeWindowToMy.find(deviceEvent->event); + + if (it != _nativeWindowToMy.end()) { + myWindow = it->second; + } else { + break; + } + + if (myWindow->_layer) { + myWindow->_layer->makeCurrent(); + } + + auto itMyDevice = _xi2->deviceById.find(deviceEvent->deviceid); + if (itMyDevice == _xi2->deviceById.end()) { + break; + } + + XInput2::Device& myDevice = itMyDevice->second; + double dX = 0, dY = 0; + for (int i = 0, valuatorIndex = 0; i < deviceEvent->valuators.mask_len * 8; ++i) { + if (!XIMaskIsSet(deviceEvent->valuators.mask, i)) { + continue; + } + + for (auto& valuator : myDevice.scroll) { + if (valuator.number == i) { + double value = reinterpret_cast(deviceEvent->valuators.values)[valuatorIndex]; + if (valuator.previousValue == 0) { + valuator.previousValue = value; + value = 0; + } else { + double delta = value - valuator.previousValue; + valuator.previousValue = value; + value = delta; + } + if (valuator.isHorizontal) { + dX = value; + } else { + dY = value; + } + break; + } + } + ++valuatorIndex; + } + + if (dX != 0 || dY != 0) { + jwm::JNILocal eventScroll( + app.getJniEnv(), + EventScroll::make( + app.getJniEnv(), + -dX, + -dY, + jwm::KeyX11::getModifiers() + ) + ); + myWindow->dispatch(eventScroll.get()); + } + + break; + } + } + XFreeEventData(display, &ev.xcookie); + } + } continue; } if (myWindow->_layer) { myWindow->_layer->makeCurrent(); } + switch (ev.type) { case ClientMessage: { if (ev.xclient.message_type == _atoms.WM_PROTOCOLS) { @@ -322,6 +442,19 @@ void WindowManagerX11::runLoop() { void WindowManagerX11::registerWindow(WindowX11* window) { _nativeWindowToMy[window->_x11Window] = window; + + // XInput + if (_xi2) { + XIEventMask eventMask; + unsigned char mask[2] = { 0 }; + XISetMask(mask, XI_DeviceChanged); + XISetMask(mask, XI_Motion); + eventMask.deviceid = XIAllDevices; + eventMask.mask_len = sizeof(mask); + eventMask.mask = mask; + + XISelectEvents(display, window->_x11Window, &eventMask, 1); + } } void WindowManagerX11::unregisterWindow(WindowX11* window) { diff --git a/linux/cc/WindowManagerX11.hh b/linux/cc/WindowManagerX11.hh index 7fd04692..89934f63 100644 --- a/linux/cc/WindowManagerX11.hh +++ b/linux/cc/WindowManagerX11.hh @@ -4,6 +4,8 @@ #include #include #include +#include +#include namespace jwm { class WindowX11; @@ -19,6 +21,7 @@ namespace jwm { XVisualInfo* pickVisual(); static int _xerrorhandler(Display* dsp, XErrorEvent* error); + void _xi2IterateDevices(); Display* getDisplay() const { return display; } ::Window getRootWindow() const; @@ -40,6 +43,23 @@ namespace jwm { */ XIM _im; + struct XInput2 { + int opcode; + + struct Device { + struct ScrollValuator { + bool isHorizontal; + int number; + double increment; + double previousValue = 0; + }; + std::vector scroll; + }; + std::map deviceById; + }; + + std::unique_ptr _xi2; + struct Atoms { Atoms(Display* display): _display(display) {} diff --git a/linux/cc/WindowX11.cc b/linux/cc/WindowX11.cc index c6680a8b..6d545d15 100644 --- a/linux/cc/WindowX11.cc +++ b/linux/cc/WindowX11.cc @@ -81,7 +81,6 @@ bool WindowX11::init() CWColormap | CWEventMask | CWCursor, &_windowManager.getSWA() ); - _windowManager.registerWindow(this); XSetWMProtocols(_windowManager.getDisplay(), _x11Window, &_windowManager.getAtoms().WM_DELETE_WINDOW, @@ -115,6 +114,7 @@ bool WindowX11::init() (const unsigned char*)&_xsyncRequestCounter.counter, 1); } + _windowManager.registerWindow(this); return true; }