diff --git a/include/windowsys/IWindow.hpp b/include/windowsys/IWindow.hpp index 0179f0d..6418f87 100644 --- a/include/windowsys/IWindow.hpp +++ b/include/windowsys/IWindow.hpp @@ -25,10 +25,15 @@ public: unsigned virtualPixel[2]; float norm[2]; }; + + struct STouchCoord + { + double coord[2]; + }; struct SScrollDelta { - float delta[2]; + double delta[2]; bool isFine; /* Use system-scale fine-scroll (for scrollable-trackpads) */ }; @@ -78,13 +83,13 @@ public: virtual void mouseMove(const SWindowCoord& coord) {(void)coord;} virtual void scroll(const SWindowCoord& coord, const SScrollDelta& scroll) - {(void)scroll;} - - virtual void touchDown(const SWindowCoord& coord, uintptr_t tid) + {(void)coord;(void)scroll;} + + virtual void touchDown(const STouchCoord& coord, uintptr_t tid) {(void)coord;(void)tid;} - virtual void touchUp(const SWindowCoord& coord, uintptr_t tid) + virtual void touchUp(const STouchCoord& coord, uintptr_t tid) {(void)coord;(void)tid;} - virtual void touchMove(const SWindowCoord& coord, uintptr_t tid) + virtual void touchMove(const STouchCoord& coord, uintptr_t tid) {(void)coord;(void)tid;} virtual void charKeyDown(unsigned long charCode, EModifierKey mods, bool isRepeat) diff --git a/src/CApplicationXCB.hpp b/src/CApplicationXCB.hpp index 91bb205..26b1e6b 100644 --- a/src/CApplicationXCB.hpp +++ b/src/CApplicationXCB.hpp @@ -15,7 +15,6 @@ namespace boo { - int XINPUT_OPCODE = 0; static xcb_window_t getWindowOfEvent(xcb_generic_event_t* event, bool& windowEvent) @@ -75,30 +74,38 @@ static xcb_window_t getWindowOfEvent(xcb_generic_event_t* event, bool& windowEve xcb_ge_event_t* gev = (xcb_ge_event_t*)event; if (gev->pad0 == XINPUT_OPCODE) { - fprintf(stderr, "INPUTEVENT\n"); - return 0; - switch (XCB_EVENT_RESPONSE_TYPE(gev)) + switch (gev->event_type) { - case XCB_INPUT_DEVICE_CHANGED: + case XCB_INPUT_MOTION: { - xcb_input_device_changed_event_t* ev = (xcb_input_device_changed_event_t*)event; - return 0; - } - case XCB_INPUT_DEVICE_MOTION_NOTIFY: - { - xcb_input_device_motion_notify_event_t* ev = (xcb_input_device_motion_notify_event_t*)event; + xcb_input_motion_event_t* ev = (xcb_input_motion_event_t*)event; + windowEvent = true; + return ev->event; + } + case XCB_INPUT_TOUCH_BEGIN: + { + xcb_input_touch_begin_event_t* ev = (xcb_input_touch_begin_event_t*)event; + windowEvent = true; + return ev->event; + } + case XCB_INPUT_TOUCH_UPDATE: + { + xcb_input_touch_update_event_t* ev = (xcb_input_touch_update_event_t*)event; + windowEvent = true; + return ev->event; + } + case XCB_INPUT_TOUCH_END: + { + xcb_input_touch_end_event_t* ev = (xcb_input_touch_end_event_t*)event; windowEvent = true; return ev->event; } - default: - return 0; } } } - default: - windowEvent = false; - return 0; } + windowEvent = false; + return 0; } IWindow* _CWindowXCBNew(const std::string& title, xcb_connection_t* conn); @@ -133,7 +140,7 @@ public: { m_xcbConn = xcb_connect(NULL, NULL); - /* This convoluted xkb extension requests that the X server does not + /* The xkb extension requests that the X server does not * send repeated keydown events when a key is held */ xkb_x11_setup_xkb_extension(m_xcbConn, XKB_X11_MIN_MAJOR_XKB_VERSION, @@ -144,6 +151,12 @@ public: XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT, XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT, 0, 0, 0); + /* Xinput major opcode */ + const xcb_query_extension_reply_t* xiReply = + xcb_get_extension_data(m_xcbConn, &xcb_input_id); + if (xiReply) + XINPUT_OPCODE = xiReply->major_opcode; + @@ -165,11 +178,12 @@ public: m_running = true; m_callback.appLaunched(this); xcb_flush(m_xcbConn); + while (m_running && (event = xcb_wait_for_event(m_xcbConn))) { bool windowEvent; xcb_window_t evWindow = getWindowOfEvent(event, windowEvent); - fprintf(stderr, "EVENT %d\n", XCB_EVENT_RESPONSE_TYPE(event)); + //fprintf(stderr, "EVENT %d\n", XCB_EVENT_RESPONSE_TYPE(event)); if (windowEvent) { auto window = m_windows.find(evWindow); diff --git a/src/inputdev/CHIDListenerUdev.cpp b/src/inputdev/CHIDListenerUdev.cpp index 5fbbffb..b2de124 100644 --- a/src/inputdev/CHIDListenerUdev.cpp +++ b/src/inputdev/CHIDListenerUdev.cpp @@ -166,7 +166,6 @@ public: ~CHIDListenerUdev() { m_udevRunning = false; - //raise(SIGINT); pthread_kill(m_udevThread->native_handle(), SIGINT); m_udevThread->join(); delete m_udevThread; diff --git a/src/windowsys/CWindowXCB.cpp b/src/windowsys/CWindowXCB.cpp index 64fce35..2be7629 100644 --- a/src/windowsys/CWindowXCB.cpp +++ b/src/windowsys/CWindowXCB.cpp @@ -24,6 +24,13 @@ namespace boo { +extern int XINPUT_OPCODE; + +static inline double fp3232val(xcb_input_fp3232_t* val) +{ + return val->integral + val->frac / (double)UINT_MAX; +} + static uint32_t translateKeysym(xcb_keysym_t sym, int& specialSym, int& modifierSym) { specialSym = IWindowCallback::KEY_NONE; @@ -103,7 +110,7 @@ do {\ xcb_intern_atom_reply_t* reply = \ xcb_intern_atom_reply(conn, cookie, NULL); \ var = reply->atom; \ - /*free(reply);*/ \ + free(reply); \ } while(0) struct SXCBAtoms @@ -149,6 +156,16 @@ class CWindowXCB final : public IWindow IGraphicsContext* m_gfxCtx; IWindowCallback* m_callback; + /* Last known input device id (0xffff if not yet set) */ + xcb_input_device_id_t m_lastInputID = 0xffff; + ETouchType m_touchType = TOUCH_NONE; + + /* Scroll valuators */ + int m_hScrollValuator = -1; + int m_vScrollValuator = -1; + double m_hScrollLast = 0.0; + double m_vScrollLast = 0.0; + /* Cached window rectangle (to avoid repeated X queries) */ int m_wx, m_wy, m_ww, m_wh; float m_pixelFactor; @@ -194,18 +211,31 @@ public: XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP, valueMasks); - /* The XInput extension enables per-pixel smooth scrolling trackpads */ - struct + /* The XInput 2.1 extension enables per-pixel smooth scrolling trackpads */ + xcb_generic_error_t* xiErr = NULL; + xcb_input_xi_query_version_reply_t* xiReply = + xcb_input_xi_query_version_reply(m_xcbConn, + xcb_input_xi_query_version(m_xcbConn, 2, 1), &xiErr); + if (!xiErr) { - xcb_input_event_mask_t mask; - uint32_t maskVal; - } masks = - { - {XCB_INPUT_DEVICE_ALL_MASTER, 1}, - XCB_INPUT_XI_EVENT_MASK_MOTION - }; - xcb_input_xi_select_events(m_xcbConn, m_windowId, 1, &masks.mask); + struct + { + xcb_input_event_mask_t mask; + uint32_t maskVal; + } masks = + { + {XCB_INPUT_DEVICE_ALL_MASTER, 1}, + XCB_INPUT_XI_EVENT_MASK_MOTION | + XCB_INPUT_XI_EVENT_MASK_TOUCH_BEGIN | + XCB_INPUT_XI_EVENT_MASK_TOUCH_UPDATE | + XCB_INPUT_XI_EVENT_MASK_TOUCH_END + }; + xcb_input_xi_select_events(m_xcbConn, m_windowId, 1, &masks.mask); + } + free(xiReply); + /* Register netwm extension atom for window closing */ +#if 0 xcb_change_property(m_xcbConn, XCB_PROP_MODE_REPLACE, m_windowId, S_ATOMS->m_wmProtocols, XCB_ATOM_ATOM, 32, 1, &S_ATOMS->m_wmDeleteWindow); const xcb_atom_t wm_protocols[1] = { @@ -214,6 +244,7 @@ public: xcb_change_property(m_xcbConn, XCB_PROP_MODE_REPLACE, m_windowId, S_ATOMS->m_wmProtocols, 4, 32, 1, wm_protocols); +#endif /* Set the title of the window */ const char* c_title = title.c_str(); @@ -356,19 +387,78 @@ public: return (uintptr_t)m_windowId; } + void _pointingDeviceChanged(xcb_input_device_id_t deviceId) + { + xcb_input_xi_query_device_reply_t* reply = + xcb_input_xi_query_device_reply(m_xcbConn, xcb_input_xi_query_device(m_xcbConn, deviceId), NULL); + + xcb_input_xi_device_info_iterator_t infoIter = xcb_input_xi_query_device_infos_iterator(reply); + while (infoIter.rem) + { + /* First iterate classes for scrollables */ + xcb_input_device_class_iterator_t classIter = + xcb_input_xi_device_info_classes_iterator(infoIter.data); + int hScroll = -1; + int vScroll = -1; + m_hScrollLast = 0.0; + m_vScrollLast = 0.0; + m_hScrollValuator = -1; + m_vScrollValuator = -1; + while (classIter.rem) + { + if (classIter.data->type == XCB_INPUT_DEVICE_CLASS_TYPE_SCROLL) + { + xcb_input_scroll_class_t* scrollClass = (xcb_input_scroll_class_t*)classIter.data; + if (scrollClass->scroll_type == XCB_INPUT_SCROLL_TYPE_VERTICAL) + vScroll = scrollClass->number; + else if (scrollClass->scroll_type == XCB_INPUT_SCROLL_TYPE_HORIZONTAL) + hScroll = scrollClass->number; + } + xcb_input_device_class_next(&classIter); + } + + /* Next iterate for touch and scroll valuators */ + classIter = xcb_input_xi_device_info_classes_iterator(infoIter.data); + while (classIter.rem) + { + if (classIter.data->type == XCB_INPUT_DEVICE_CLASS_TYPE_VALUATOR) + { + xcb_input_valuator_class_t* valClass = (xcb_input_valuator_class_t*)classIter.data; + if (valClass->number == vScroll) + { + m_vScrollLast = fp3232val(&valClass->value); + m_vScrollValuator = vScroll; + } + else if (valClass->number == hScroll) + { + m_hScrollLast = fp3232val(&valClass->value); + m_hScrollValuator = hScroll; + } + } + else if (classIter.data->type == XCB_INPUT_DEVICE_CLASS_TYPE_TOUCH) + { + xcb_input_touch_class_t* touchClass = (xcb_input_touch_class_t*)classIter.data; + if (touchClass->mode == XCB_INPUT_TOUCH_MODE_DIRECT) + m_touchType = TOUCH_DISPLAY; + else if (touchClass->mode == XCB_INPUT_TOUCH_MODE_DEPENDENT) + m_touchType = TOUCH_TRACKPAD; + else + m_touchType = TOUCH_NONE; + } + xcb_input_device_class_next(&classIter); + } + xcb_input_xi_device_info_next(&infoIter); + } + + free(reply); + m_lastInputID = deviceId; + } + void _incomingEvent(void* e) { xcb_generic_event_t* event = (xcb_generic_event_t*)e; switch (XCB_EVENT_RESPONSE_TYPE(event)) { - case XCB_CLIENT_MESSAGE: - { - xcb_client_message_event_t* ev = (xcb_client_message_event_t*)event; - if (ev->data.data32[0] == S_ATOMS->m_wmDeleteWindow) - { - fprintf(stderr, "CLOSED\n"); - } - } case XCB_EXPOSE: { xcb_expose_event_t* ev = (xcb_expose_event_t*)event; @@ -376,6 +466,7 @@ public: m_wy = ev->y; m_ww = ev->width; m_wh = ev->height; + return; } case XCB_CONFIGURE_NOTIFY: { @@ -387,6 +478,7 @@ public: m_ww = ev->width; m_wh = ev->height; } + return; } case XCB_KEY_PRESS: { @@ -407,6 +499,7 @@ public: else if (modifierKey) m_callback->modKeyDown((IWindowCallback::EModifierKey)modifierKey, false); } + return; } case XCB_KEY_RELEASE: { @@ -427,40 +520,75 @@ public: else if (modifierKey) m_callback->modKeyUp((IWindowCallback::EModifierKey)modifierKey); } + return; } case XCB_BUTTON_PRESS: { xcb_button_press_event_t* ev = (xcb_button_press_event_t*)event; - int button = translateButton(ev->detail); - if (m_callback && button) + if (m_callback) { - int modifierMask = translateModifiers(ev->state); - IWindowCallback::SWindowCoord coord = + int button = translateButton(ev->detail); + if (button) { - {(unsigned)ev->event_x, (unsigned)ev->event_y}, - {(unsigned)(ev->event_x / m_pixelFactor), (unsigned)(ev->event_y / m_pixelFactor)}, - {ev->event_x / (float)m_ww, ev->event_y / (float)m_wh} - }; - m_callback->mouseDown(coord, (IWindowCallback::EMouseButton)button, - (IWindowCallback::EModifierKey)modifierMask); + int modifierMask = translateModifiers(ev->state); + IWindowCallback::SWindowCoord coord = + { + {(unsigned)ev->event_x, (unsigned)ev->event_y}, + {(unsigned)(ev->event_x / m_pixelFactor), (unsigned)(ev->event_y / m_pixelFactor)}, + {ev->event_x / (float)m_ww, ev->event_y / (float)m_wh} + }; + m_callback->mouseDown(coord, (IWindowCallback::EMouseButton)button, + (IWindowCallback::EModifierKey)modifierMask); + } + + /* Also handle legacy scroll events here */ + if (ev->detail >= 4 && ev->detail <= 7 && + m_hScrollValuator == -1 && m_vScrollValuator == -1) + { + IWindowCallback::SWindowCoord coord = + { + {(unsigned)ev->event_x, (unsigned)ev->event_y}, + {(unsigned)(ev->event_x / m_pixelFactor), (unsigned)(ev->event_y / m_pixelFactor)}, + {ev->event_x / (float)m_ww, ev->event_y / (float)m_wh} + }; + IWindowCallback::SScrollDelta scrollDelta = + { + {0.0, 0.0}, + false + }; + if (ev->detail == 4) + scrollDelta.delta[1] = 1.0; + else if (ev->detail == 5) + scrollDelta.delta[1] = -1.0; + else if (ev->detail == 6) + scrollDelta.delta[0] = 1.0; + else if (ev->detail == 7) + scrollDelta.delta[0] = -1.0; + m_callback->scroll(coord, scrollDelta); + } } + return; } case XCB_BUTTON_RELEASE: { xcb_button_release_event_t* ev = (xcb_button_release_event_t*)event; - int button = translateButton(ev->detail); - if (m_callback && button) + if (m_callback) { - int modifierMask = translateModifiers(ev->state); - IWindowCallback::SWindowCoord coord = + int button = translateButton(ev->detail); + if (button) { - {(unsigned)ev->event_x, (unsigned)ev->event_y}, - {(unsigned)(ev->event_x / m_pixelFactor), (unsigned)(ev->event_y / m_pixelFactor)}, - {ev->event_x / (float)m_ww, ev->event_y / (float)m_wh} - }; - m_callback->mouseUp(coord, (IWindowCallback::EMouseButton)button, - (IWindowCallback::EModifierKey)modifierMask); + int modifierMask = translateModifiers(ev->state); + IWindowCallback::SWindowCoord coord = + { + {(unsigned)ev->event_x, (unsigned)ev->event_y}, + {(unsigned)(ev->event_x / m_pixelFactor), (unsigned)(ev->event_y / m_pixelFactor)}, + {ev->event_x / (float)m_ww, ev->event_y / (float)m_wh} + }; + m_callback->mouseUp(coord, (IWindowCallback::EMouseButton)button, + (IWindowCallback::EModifierKey)modifierMask); + } } + return; } case XCB_MOTION_NOTIFY: { @@ -475,13 +603,160 @@ public: }; m_callback->mouseMove(coord); } + return; + } + case XCB_GE_GENERIC: + { + xcb_ge_event_t* gev = (xcb_ge_event_t*)event; + if (gev->pad0 == XINPUT_OPCODE) + { + switch (gev->event_type) + { + case XCB_INPUT_MOTION: + { + xcb_input_motion_event_t* ev = (xcb_input_motion_event_t*)event; + if (m_lastInputID != ev->deviceid) + _pointingDeviceChanged(ev->deviceid); + + uint32_t* valuators = (uint32_t*)(((char*)ev) + sizeof(xcb_input_motion_event_t) + sizeof(uint32_t) * ev->buttons_len); + xcb_input_fp3232_t* valuatorVals = (xcb_input_fp3232_t*)(((char*)valuators) + sizeof(uint32_t) * ev->valuators_len); + int cv = 0; + double newScroll[2] = {m_hScrollLast, m_vScrollLast}; + bool didScroll = false; + for (int i=0 ; i<32 ; ++i) + { + if (valuators[0] & (1<event_x >> 16; + unsigned event_y = ev->event_y >> 16; + IWindowCallback::SWindowCoord coord = + { + {event_x, event_y}, + {(unsigned)(event_x / m_pixelFactor), (unsigned)(event_y / m_pixelFactor)}, + {event_x / (float)m_ww, event_y / (float)m_wh} + }; + m_callback->scroll(coord, scrollDelta); + } + return; + } + case XCB_INPUT_TOUCH_BEGIN: + { + xcb_input_touch_begin_event_t* ev = (xcb_input_touch_begin_event_t*)event; + if (m_lastInputID != ev->deviceid) + _pointingDeviceChanged(ev->deviceid); + + uint32_t* valuators = (uint32_t*)(((char*)ev) + sizeof(xcb_input_motion_event_t) + sizeof(uint32_t) * ev->buttons_len); + xcb_input_fp3232_t* valuatorVals = (xcb_input_fp3232_t*)(((char*)valuators) + sizeof(uint32_t) * ev->valuators_len); + int cv = 0; + double vals[32] = {}; + for (int i=0 ; i<32 ; ++i) + { + if (valuators[0] & (1<touchDown(coord, ev->detail); + return; + } + case XCB_INPUT_TOUCH_UPDATE: + { + xcb_input_touch_update_event_t* ev = (xcb_input_touch_update_event_t*)event; + if (m_lastInputID != ev->deviceid) + _pointingDeviceChanged(ev->deviceid); + + uint32_t* valuators = (uint32_t*)(((char*)ev) + sizeof(xcb_input_motion_event_t) + sizeof(uint32_t) * ev->buttons_len); + xcb_input_fp3232_t* valuatorVals = (xcb_input_fp3232_t*)(((char*)valuators) + sizeof(uint32_t) * ev->valuators_len); + int cv = 0; + double vals[32] = {}; + for (int i=0 ; i<32 ; ++i) + { + if (valuators[0] & (1<touchMove(coord, ev->detail); + return; + } + case XCB_INPUT_TOUCH_END: + { + xcb_input_touch_end_event_t* ev = (xcb_input_touch_end_event_t*)event; + if (m_lastInputID != ev->deviceid) + _pointingDeviceChanged(ev->deviceid); + + uint32_t* valuators = (uint32_t*)(((char*)ev) + sizeof(xcb_input_motion_event_t) + sizeof(uint32_t) * ev->buttons_len); + xcb_input_fp3232_t* valuatorVals = (xcb_input_fp3232_t*)(((char*)valuators) + sizeof(uint32_t) * ev->valuators_len); + int cv = 0; + double vals[32] = {}; + for (int i=0 ; i<32 ; ++i) + { + if (valuators[0] & (1<touchUp(coord, ev->detail); + return; + } + } + } } } } ETouchType getTouchType() const { - return TOUCH_NONE; + return m_touchType; } }; diff --git a/test/main.cpp b/test/main.cpp index 12ed06f..f81e620 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -63,20 +63,20 @@ struct CTestWindowCallback : public IWindowCallback } void scroll(const SWindowCoord& coord, const SScrollDelta& scroll) { - fprintf(stderr, "Mouse Move (%f,%f)\n", coord.norm[0], coord.norm[1]); + fprintf(stderr, "Mouse Scroll (%f,%f) (%f,%f)\n", coord.norm[0], coord.norm[1], scroll.delta[0], scroll.delta[1]); } - void touchDown(const SWindowCoord& coord, uintptr_t tid) + void touchDown(const STouchCoord& coord, uintptr_t tid) { - + //fprintf(stderr, "Touch Down %16lX (%f,%f)\n", tid, coord.coord[0], coord.coord[1]); } - void touchUp(const SWindowCoord& coord, uintptr_t tid) + void touchUp(const STouchCoord& coord, uintptr_t tid) { - + //fprintf(stderr, "Touch Up %16lX (%f,%f)\n", tid, coord.coord[0], coord.coord[1]); } - void touchMove(const SWindowCoord& coord, uintptr_t tid) + void touchMove(const STouchCoord& coord, uintptr_t tid) { - + //fprintf(stderr, "Touch Move %16lX (%f,%f)\n", tid, coord.coord[0], coord.coord[1]); } void charKeyDown(unsigned long charCode, EModifierKey mods, bool isRepeat)