diff --git a/implementations/Crafter.Graphics-Device.cpp b/implementations/Crafter.Graphics-Device.cpp index d2344ce..21c35bc 100644 --- a/implementations/Crafter.Graphics-Device.cpp +++ b/implementations/Crafter.Graphics-Device.cpp @@ -546,12 +546,82 @@ void Device::TickKeyRepeats() { } } +// The first finger stands in for the pointer: it enters the window (so the +// clipboard has a serial and Device::focusedWindow a window), presses the +// left button, moves it and releases it. Every finger is also reported as +// such, in physical pixels. +void Device::touch_down(void*, wl_touch*, std::uint32_t serial, std::uint32_t, wl_surface* surface, std::int32_t id, wl_fixed_t x, wl_fixed_t y) { + Window* window = nullptr; + for (Window* candidate : windows) { + if (candidate->surface == surface) window = candidate; + } + if (window == nullptr) return; + touchWindow = window; + window->lastPointerSerial_ = serial; + const Vector pos = Vector(wl_fixed_to_double(x), wl_fixed_to_double(y)) * window->scale; + touchPositions[id] = { pos.x, pos.y }; + window->onTouchDown.Invoke({ id, pos }); + if (window->primaryTouchId < 0) { + window->primaryTouchId = id; + focusedWindow = window; + window->currentMousePos = pos; + window->mouseLeftHeld = true; + window->onMouseLeftClick.Invoke(); + } +} + +void Device::touch_up(void*, wl_touch*, std::uint32_t, std::uint32_t, std::int32_t id) { + if (touchWindow == nullptr) return; + Window* window = touchWindow; + const auto at = touchPositions.find(id); + const Vector pos = at == touchPositions.end() ? window->currentMousePos : Vector(at->second.first, at->second.second); + touchPositions.erase(id); + window->onTouchUp.Invoke({ id, pos }); + if (id == window->primaryTouchId) { + window->primaryTouchId = -1; + window->mouseLeftHeld = false; + window->onMouseLeftRelease.Invoke(); + } + if (touchPositions.empty()) touchWindow = nullptr; +} + +void Device::touch_motion(void*, wl_touch*, std::uint32_t, std::int32_t id, wl_fixed_t x, wl_fixed_t y) { + if (touchWindow == nullptr) return; + Window* window = touchWindow; + const Vector pos = Vector(wl_fixed_to_double(x), wl_fixed_to_double(y)) * window->scale; + touchPositions[id] = { pos.x, pos.y }; + window->onTouchMove.Invoke({ id, pos }); + if (id == window->primaryTouchId) { + window->currentMousePos = pos; + window->onMouseMove.Invoke(); + } +} + +void Device::touch_frame(void*, wl_touch*) {} + +void Device::touch_cancel(void*, wl_touch*) { + if (touchWindow == nullptr) return; + Window* window = touchWindow; + touchPositions.clear(); + touchWindow = nullptr; + if (window->primaryTouchId >= 0) { + window->primaryTouchId = -1; + window->mouseLeftHeld = false; + window->onMouseLeftRelease.Invoke(); + } + window->onTouchCancel.Invoke(); +} + void Device::seat_handle_capabilities(void* data, wl_seat* seat, uint32_t capabilities) { seat = seat; if (capabilities & WL_SEAT_CAPABILITY_POINTER) { wl_pointer* pointer = wl_seat_get_pointer(seat); wl_pointer_add_listener(pointer, &pointer_listener, nullptr); } + if ((capabilities & WL_SEAT_CAPABILITY_TOUCH) && wlTouch == nullptr) { + wlTouch = wl_seat_get_touch(seat); + wl_touch_add_listener(wlTouch, &touch_listener, nullptr); + } if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { wl_keyboard* keyboard = wl_seat_get_keyboard(seat); wl_keyboard_add_listener(keyboard, &keyboard_listener, nullptr); diff --git a/implementations/Crafter.Graphics-Window.cpp b/implementations/Crafter.Graphics-Window.cpp index dde2e3d..afb1939 100644 --- a/implementations/Crafter.Graphics-Window.cpp +++ b/implementations/Crafter.Graphics-Window.cpp @@ -557,6 +557,10 @@ Window::~Window() { std::erase(Device::windows, this); if (Device::focusedWindow == this) Device::focusedWindow = nullptr; if (Device::keyboardWindow == this) Device::keyboardWindow = nullptr; + if (Device::touchWindow == this) { + Device::touchWindow = nullptr; + Device::touchPositions.clear(); + } #endif } #endif diff --git a/interfaces/Crafter.Graphics-Device.cppm b/interfaces/Crafter.Graphics-Device.cppm index be42e9e..e4b54ab 100644 --- a/interfaces/Crafter.Graphics-Device.cppm +++ b/interfaces/Crafter.Graphics-Device.cppm @@ -60,6 +60,9 @@ export namespace Crafter { inline static xkb_state* xkb_state; inline static std::vector windows; inline static wl_pointer* wlPointer; + inline static wl_touch* wlTouch = nullptr; + inline static Window* touchWindow = nullptr; // the window under the fingers + inline static std::unordered_map> touchPositions; // per finger, physical px // wl_data_device_manager + wl_data_device drive copy/paste. Bound // lazily in handle_global; the data device is created once both // the manager and the seat are present (registry binding order @@ -90,6 +93,11 @@ export namespace Crafter { static void PointerListenerHandleAxis(void*, wl_pointer*, std::uint32_t time, std::uint32_t axis, wl_fixed_t value); static void PointerListenerHandleEnter(void* data, wl_pointer* wl_pointer, std::uint32_t serial, wl_surface* surface, wl_fixed_t surface_x, wl_fixed_t surface_y); static void PointerListenerHandleLeave(void*, wl_pointer*, std::uint32_t, wl_surface*); + static void touch_down(void* data, wl_touch* touch, std::uint32_t serial, std::uint32_t time, wl_surface* surface, std::int32_t id, wl_fixed_t x, wl_fixed_t y); + static void touch_up(void* data, wl_touch* touch, std::uint32_t serial, std::uint32_t time, std::int32_t id); + static void touch_motion(void* data, wl_touch* touch, std::uint32_t time, std::int32_t id, wl_fixed_t x, wl_fixed_t y); + static void touch_frame(void* data, wl_touch* touch); + static void touch_cancel(void* data, wl_touch* touch); constexpr static wl_pointer_listener pointer_listener = { .enter = PointerListenerHandleEnter, @@ -98,6 +106,13 @@ export namespace Crafter { .button = pointer_handle_button, .axis = PointerListenerHandleAxis, }; + constexpr static wl_touch_listener touch_listener = { + .down = touch_down, + .up = touch_up, + .motion = touch_motion, + .frame = touch_frame, + .cancel = touch_cancel, + }; constexpr static wl_keyboard_listener keyboard_listener = { .keymap = keyboard_keymap, .enter = keyboard_enter, diff --git a/interfaces/Crafter.Graphics-Types.cppm b/interfaces/Crafter.Graphics-Types.cppm index a7d5eeb..b13a040 100644 --- a/interfaces/Crafter.Graphics-Types.cppm +++ b/interfaces/Crafter.Graphics-Types.cppm @@ -18,6 +18,13 @@ export namespace Crafter { Vector mouseDelta; }; + // One finger on a touchscreen, in the window's physical pixels; the id is + // the compositor's and stays with the finger until it lifts. + struct TouchPoint { + std::int32_t id; + Vector position; + }; + struct ScaleData2D { Vector position; Vector size; diff --git a/interfaces/Crafter.Graphics-Window.cppm b/interfaces/Crafter.Graphics-Window.cppm index c9f7155..5b1e9dc 100644 --- a/interfaces/Crafter.Graphics-Window.cppm +++ b/interfaces/Crafter.Graphics-Window.cppm @@ -98,6 +98,14 @@ export namespace Crafter { // touchpads, free-spinning wheels) is accumulated backend-side // until a whole detent is reached. Event onMouseScroll; + // Touch, from the seat's wl_touch. The first finger down also drives + // currentMousePos and the left-button events until it lifts, so a + // pointer-only application works on a touchscreen as it is. + Event onTouchDown; + Event onTouchMove; + Event onTouchUp; + Event onTouchCancel; + std::int32_t primaryTouchId = -1; Vector currentMousePos; Vector lastMousePos; Vector mouseDelta;