diff options
author | pkerling <pkerling@casix.org> | 2018-12-23 19:03:13 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-12-23 19:03:13 +0100 |
commit | 85164815bcaffb76ba804a614d9d5c1714fe6dce (patch) | |
tree | fc3a537fc4ad1eb2b5b371881cc2271f9d168d06 | |
parent | 44fb5111e7aef1e1649e49b9940171a0598733f3 (diff) | |
parent | b2a848ebf257965c2f04c1ff30f393a7ce71e293 (diff) |
Merge pull request #15052 from pkerling/wayland-unify-set-handling
[wayland] Centralize seat handling
-rw-r--r-- | xbmc/windowing/wayland/CMakeLists.txt | 2 | ||||
-rw-r--r-- | xbmc/windowing/wayland/InputProcessorKeyboard.cpp | 118 | ||||
-rw-r--r-- | xbmc/windowing/wayland/InputProcessorKeyboard.h | 15 | ||||
-rw-r--r-- | xbmc/windowing/wayland/InputProcessorPointer.cpp | 115 | ||||
-rw-r--r-- | xbmc/windowing/wayland/InputProcessorPointer.h | 19 | ||||
-rw-r--r-- | xbmc/windowing/wayland/InputProcessorTouch.cpp | 125 | ||||
-rw-r--r-- | xbmc/windowing/wayland/InputProcessorTouch.h | 12 | ||||
-rw-r--r-- | xbmc/windowing/wayland/Seat.cpp | 260 | ||||
-rw-r--r-- | xbmc/windowing/wayland/Seat.h | 169 | ||||
-rw-r--r-- | xbmc/windowing/wayland/SeatInputProcessing.cpp | 83 | ||||
-rw-r--r-- | xbmc/windowing/wayland/SeatInputProcessing.h | 135 | ||||
-rw-r--r-- | xbmc/windowing/wayland/WinSystemWayland.cpp | 65 | ||||
-rw-r--r-- | xbmc/windowing/wayland/WinSystemWayland.h | 14 | ||||
-rw-r--r-- | xbmc/windowing/wayland/WindowDecorator.cpp | 170 | ||||
-rw-r--r-- | xbmc/windowing/wayland/WindowDecorator.h | 39 | ||||
-rw-r--r-- | xbmc/windowing/wayland/XkbcommonKeymap.cpp | 13 | ||||
-rw-r--r-- | xbmc/windowing/wayland/XkbcommonKeymap.h | 2 |
17 files changed, 874 insertions, 482 deletions
diff --git a/xbmc/windowing/wayland/CMakeLists.txt b/xbmc/windowing/wayland/CMakeLists.txt index f7cbd30b16..1a68845ecc 100644 --- a/xbmc/windowing/wayland/CMakeLists.txt +++ b/xbmc/windowing/wayland/CMakeLists.txt @@ -12,6 +12,7 @@ set(SOURCES Connection.cpp OSScreenSaverIdleInhibitUnstableV1.cpp Registry.cpp Seat.cpp + SeatInputProcessing.cpp SeatSelection.cpp ShellSurface.cpp ShellSurfaceWlShell.cpp @@ -35,6 +36,7 @@ set(HEADERS Connection.h OSScreenSaverIdleInhibitUnstableV1.h Registry.h Seat.h + SeatInputProcessing.h SeatSelection.h ShellSurface.h ShellSurfaceWlShell.h diff --git a/xbmc/windowing/wayland/InputProcessorKeyboard.cpp b/xbmc/windowing/wayland/InputProcessorKeyboard.cpp index d9de97c11e..db6ece37ef 100644 --- a/xbmc/windowing/wayland/InputProcessorKeyboard.cpp +++ b/xbmc/windowing/wayland/InputProcessorKeyboard.cpp @@ -8,13 +8,10 @@ #include "InputProcessorKeyboard.h" -#include <unistd.h> - #include <cassert> #include <limits> #include "utils/log.h" -#include "platform/posix/utils/FileHandle.h" using namespace KODI::WINDOWING::WAYLAND; @@ -24,74 +21,77 @@ namespace constexpr int WL_KEYBOARD_XKB_CODE_OFFSET{8}; } -CInputProcessorKeyboard::CInputProcessorKeyboard(wayland::keyboard_t const& keyboard, IInputHandlerKeyboard& handler) -: m_keyboard{keyboard}, m_handler{handler}, m_keyRepeatTimer{std::bind(&CInputProcessorKeyboard::KeyRepeatTimeout, this)} +CInputProcessorKeyboard::CInputProcessorKeyboard(IInputHandlerKeyboard& handler) +: m_handler{handler}, m_keyRepeatTimer{std::bind(&CInputProcessorKeyboard::KeyRepeatTimeout, this)} { - m_keyboard.on_enter() = [this](std::uint32_t, wayland::surface_t, wayland::array_t) - { - m_handler.OnKeyboardEnter(); - }; - m_keyboard.on_leave() = [this](std::uint32_t, wayland::surface_t) - { - m_keyRepeatTimer.Stop(); - m_handler.OnKeyboardLeave(); - }; - m_keyboard.on_repeat_info() = [this](std::int32_t rate, std::int32_t delay) - { - CLog::Log(LOGDEBUG, "Key repeat rate: %d cps, delay %d ms", rate, delay); - // rate is in characters per second, so convert to msec interval - m_keyRepeatInterval = (rate != 0) ? static_cast<int> (1000.0f / rate) : 0; - m_keyRepeatDelay = delay; - }; - m_keyboard.on_keymap() = [this](wayland::keyboard_keymap_format format, int fd, std::uint32_t size) +} + +void CInputProcessorKeyboard::OnKeyboardKeymap(CSeat* seat, wayland::keyboard_keymap_format format, std::string const &keymap) +{ + if (format != wayland::keyboard_keymap_format::xkb_v1) { - KODI::UTILS::POSIX::CFileHandle fdGuard{fd}; + CLog::Log(LOGWARNING, "Wayland compositor sent keymap in format %u, but we only understand xkbv1 - keyboard input will not work", static_cast<unsigned int>(format)); + return; + } - if (format != wayland::keyboard_keymap_format::xkb_v1) + m_keyRepeatTimer.Stop(); + + try + { + if (!m_xkbContext) { - CLog::Log(LOGWARNING, "Wayland compositor sent keymap in format %u, but we only understand xkbv1 - keyboard input will not work", - static_cast<unsigned int>(format)); - return; + // Lazily initialize XkbcommonContext + m_xkbContext.reset(new CXkbcommonContext); } - m_keyRepeatTimer.Stop(); + m_keymap = m_xkbContext->KeymapFromString(keymap); + } + catch(std::exception const& e) + { + CLog::Log(LOGERROR, "Could not parse keymap from compositor: %s - continuing without keymap", e.what()); + } +} - try - { - if (!m_xkbContext) - { - // Lazily initialize XkbcommonContext - m_xkbContext.reset(new CXkbcommonContext); - } +void CInputProcessorKeyboard::OnKeyboardEnter(CSeat* seat, std::uint32_t serial, wayland::surface_t surface, wayland::array_t keys) +{ + m_handler.OnKeyboardEnter(); +} - m_keymap = m_xkbContext->KeymapFromSharedMemory(fd, size); - } - catch(std::exception const& e) - { - CLog::Log(LOGERROR, "Could not parse keymap from compositor: %s - continuing without keymap", e.what()); - } - }; - m_keyboard.on_key() = [this](std::uint32_t, std::uint32_t, std::uint32_t key, wayland::keyboard_key_state state) +void CInputProcessorKeyboard::OnKeyboardLeave(CSeat* seat, std::uint32_t serial, wayland::surface_t surface) +{ + m_keyRepeatTimer.Stop(); + m_handler.OnKeyboardLeave(); +} + +void CInputProcessorKeyboard::OnKeyboardKey(CSeat* seat, std::uint32_t serial, std::uint32_t time, std::uint32_t key, wayland::keyboard_key_state state) +{ + if (!m_keymap) { - if (!m_keymap) - { - CLog::Log(LOGWARNING, "Key event for code %u without valid keymap, ignoring", key); - return; - } + CLog::Log(LOGWARNING, "Key event for code %u without valid keymap, ignoring", key); + return; + } - ConvertAndSendKey(key, state == wayland::keyboard_key_state::pressed); - }; - m_keyboard.on_modifiers() = [this](std::uint32_t, std::uint32_t modsDepressed, std::uint32_t modsLatched, std::uint32_t modsLocked, std::uint32_t group) + ConvertAndSendKey(key, state == wayland::keyboard_key_state::pressed); +} + +void CInputProcessorKeyboard::OnKeyboardModifiers(CSeat* seat, std::uint32_t serial, std::uint32_t modsDepressed, std::uint32_t modsLatched, std::uint32_t modsLocked, std::uint32_t group) +{ + if (!m_keymap) { - if (!m_keymap) - { - CLog::Log(LOGWARNING, "Modifier event without valid keymap, ignoring"); - return; - } + CLog::Log(LOGWARNING, "Modifier event without valid keymap, ignoring"); + return; + } - m_keyRepeatTimer.Stop(); - m_keymap->UpdateMask(modsDepressed, modsLatched, modsLocked, group); - }; + m_keyRepeatTimer.Stop(); + m_keymap->UpdateMask(modsDepressed, modsLatched, modsLocked, group); +} + +void CInputProcessorKeyboard::OnKeyboardRepeatInfo(CSeat* seat, std::int32_t rate, std::int32_t delay) +{ + CLog::Log(LOGDEBUG, "Key repeat rate: %d cps, delay %d ms", rate, delay); + // rate is in characters per second, so convert to msec interval + m_keyRepeatInterval = (rate != 0) ? static_cast<int> (1000.0f / rate) : 0; + m_keyRepeatDelay = delay; } void CInputProcessorKeyboard::ConvertAndSendKey(std::uint32_t scancode, bool pressed) diff --git a/xbmc/windowing/wayland/InputProcessorKeyboard.h b/xbmc/windowing/wayland/InputProcessorKeyboard.h index 74051a6636..81eed5874b 100644 --- a/xbmc/windowing/wayland/InputProcessorKeyboard.h +++ b/xbmc/windowing/wayland/InputProcessorKeyboard.h @@ -12,9 +12,8 @@ #include <cstdint> #include <memory> -#include <wayland-client-protocol.hpp> - #include "input/XBMC_keysym.h" +#include "Seat.h" #include "threads/Timer.h" #include "windowing/XBMC_events.h" #include "XkbcommonKeymap.h" @@ -35,10 +34,17 @@ public: virtual ~IInputHandlerKeyboard() = default; }; -class CInputProcessorKeyboard +class CInputProcessorKeyboard final : public IRawInputHandlerKeyboard { public: - CInputProcessorKeyboard(wayland::keyboard_t const& keyboard, IInputHandlerKeyboard& handler); + CInputProcessorKeyboard(IInputHandlerKeyboard& handler); + + void OnKeyboardKeymap(CSeat* seat, wayland::keyboard_keymap_format format, std::string const& keymap) override; + void OnKeyboardEnter(CSeat* seat, std::uint32_t serial, wayland::surface_t surface, wayland::array_t keys) override; + void OnKeyboardLeave(CSeat* seat, std::uint32_t serial, wayland::surface_t surface) override; + void OnKeyboardKey(CSeat* seat, std::uint32_t serial, std::uint32_t time, std::uint32_t key, wayland::keyboard_key_state state) override; + void OnKeyboardModifiers(CSeat* seat, std::uint32_t serial, std::uint32_t modsDepressed, std::uint32_t modsLatched, std::uint32_t modsLocked, std::uint32_t group) override; + void OnKeyboardRepeatInfo(CSeat* seat, std::int32_t rate, std::int32_t delay) override; private: CInputProcessorKeyboard(CInputProcessorKeyboard const& other) = delete; @@ -48,7 +54,6 @@ private: XBMC_Event SendKey(unsigned char scancode, XBMCKey key, std::uint16_t unicodeCodepoint, bool pressed); void KeyRepeatTimeout(); - wayland::keyboard_t m_keyboard; IInputHandlerKeyboard& m_handler; std::unique_ptr<CXkbcommonContext> m_xkbContext; diff --git a/xbmc/windowing/wayland/InputProcessorPointer.cpp b/xbmc/windowing/wayland/InputProcessorPointer.cpp index d81f7f0972..5114071614 100644 --- a/xbmc/windowing/wayland/InputProcessorPointer.cpp +++ b/xbmc/windowing/wayland/InputProcessorPointer.cpp @@ -37,73 +37,70 @@ int WaylandToXbmcButton(std::uint32_t button) } -CInputProcessorPointer::CInputProcessorPointer(wayland::pointer_t const& pointer, wayland::surface_t const& surface, IInputHandlerPointer& handler) -: m_pointer{pointer}, m_surface{surface}, m_handler{handler} +CInputProcessorPointer::CInputProcessorPointer(wayland::surface_t const& surface, IInputHandlerPointer& handler) +: m_surface{surface}, m_handler{handler} { - m_pointer.on_enter() = [this](std::uint32_t serial, wayland::surface_t surface, double surfaceX, double surfaceY) - { - if (surface == m_surface) - { - m_pointerOnSurface = true; - m_handler.OnPointerEnter(m_pointer, serial); - SetMousePosFromSurface({surfaceX, surfaceY}); - SendMouseMotion(); - } - }; - m_pointer.on_leave() = [this](std::uint32_t serial, wayland::surface_t surface) +} + +void CInputProcessorPointer::OnPointerEnter(CSeat* seat, std::uint32_t serial, wayland::surface_t surface, double surfaceX, double surfaceY) +{ + if (surface == m_surface) { - if (m_pointerOnSurface) - { - m_handler.OnPointerLeave(); - m_pointerOnSurface = false; - } - }; - m_pointer.on_motion() = [this](std::uint32_t time, double surfaceX, double surfaceY) + m_pointerOnSurface = true; + m_handler.OnPointerEnter(seat->GetGlobalName(), serial); + SetMousePosFromSurface({surfaceX, surfaceY}); + SendMouseMotion(); + } +} + +void CInputProcessorPointer::OnPointerLeave(CSeat* seat, std::uint32_t serial, wayland::surface_t surface) +{ + if (m_pointerOnSurface) { - if (m_pointerOnSurface) - { - SetMousePosFromSurface({surfaceX, surfaceY}); - SendMouseMotion(); - } - }; - m_pointer.on_button() = [this](std::uint32_t serial, std::uint32_t time, std::uint32_t button, wayland::pointer_button_state state) + m_handler.OnPointerLeave(); + m_pointerOnSurface = false; + } +} + +void CInputProcessorPointer::OnPointerMotion(CSeat* seat, std::uint32_t time, double surfaceX, double surfaceY) +{ + if (m_pointerOnSurface) { - if (m_pointerOnSurface) - { - int xbmcButton = WaylandToXbmcButton(button); - if (xbmcButton < 0) - { - // Button is unmapped - return; - } - - bool pressed = (state == wayland::pointer_button_state::pressed); - SendMouseButton(xbmcButton, pressed); - } - }; - m_pointer.on_axis() = [this](std::uint32_t, wayland::pointer_axis, double value) + SetMousePosFromSurface({surfaceX, surfaceY}); + SendMouseMotion(); + } +} + +void CInputProcessorPointer::OnPointerButton(CSeat* seat, std::uint32_t serial, std::uint32_t time, std::uint32_t button, wayland::pointer_button_state state) +{ + if (m_pointerOnSurface) { - if (m_pointerOnSurface) + int xbmcButton = WaylandToXbmcButton(button); + if (xbmcButton < 0) { - // For axis events we only care about the vector direction - // and not the scalar magnitude. Every axis event callback - // generates one scroll button event for XBMC - - // Negative is up - auto xbmcButton = static_cast<unsigned char> ((value < 0.0) ? XBMC_BUTTON_WHEELUP : XBMC_BUTTON_WHEELDOWN); - // Simulate a single click of the wheel-equivalent "button" - SendMouseButton(xbmcButton, true); - SendMouseButton(xbmcButton, false); + // Button is unmapped + return; } - }; - // Wayland groups pointer events, but right now there is no benefit in - // treating them in groups. The main use case for doing so seems to be - // multi-axis (i.e. diagnoal) scrolling, but we do not support this anyway. - /*m_pointer.on_frame() = [this]() - { + bool pressed = (state == wayland::pointer_button_state::pressed); + SendMouseButton(xbmcButton, pressed); + } +} - };*/ +void CInputProcessorPointer::OnPointerAxis(CSeat* seat, std::uint32_t time, wayland::pointer_axis axis, double value) +{ + if (m_pointerOnSurface) + { + // For axis events we only care about the vector direction + // and not the scalar magnitude. Every axis event callback + // generates one scroll button event for XBMC + + // Negative is up + auto xbmcButton = static_cast<unsigned char> ((value < 0.0) ? XBMC_BUTTON_WHEELUP : XBMC_BUTTON_WHEELDOWN); + // Simulate a single click of the wheel-equivalent "button" + SendMouseButton(xbmcButton, true); + SendMouseButton(xbmcButton, false); + } } std::uint16_t CInputProcessorPointer::ConvertMouseCoordinate(double coord) const @@ -128,4 +125,4 @@ void CInputProcessorPointer::SendMouseButton(unsigned char button, bool pressed) XBMC_Event event{static_cast<unsigned char> (pressed ? XBMC_MOUSEBUTTONDOWN : XBMC_MOUSEBUTTONUP)}; event.button = {button, m_pointerPosition.x, m_pointerPosition.y}; m_handler.OnPointerEvent(event); -}
\ No newline at end of file +} diff --git a/xbmc/windowing/wayland/InputProcessorPointer.h b/xbmc/windowing/wayland/InputProcessorPointer.h index 20ab2e4a07..afae6d2176 100644 --- a/xbmc/windowing/wayland/InputProcessorPointer.h +++ b/xbmc/windowing/wayland/InputProcessorPointer.h @@ -14,6 +14,7 @@ #include "input/XBMC_keysym.h" #include "utils/Geometry.h" +#include "Seat.h" #include "windowing/XBMC_events.h" namespace KODI @@ -26,18 +27,25 @@ namespace WAYLAND class IInputHandlerPointer { public: - virtual void OnPointerEnter(wayland::pointer_t& pointer, std::uint32_t serial) {}; - virtual void OnPointerLeave() {}; + virtual void OnPointerEnter(std::uint32_t seatGlobalName, std::uint32_t serial) {} + virtual void OnPointerLeave() {} virtual void OnPointerEvent(XBMC_Event& event) = 0; - virtual ~IInputHandlerPointer() = default; +protected: + ~IInputHandlerPointer() {} }; -class CInputProcessorPointer +class CInputProcessorPointer final : public IRawInputHandlerPointer { public: - CInputProcessorPointer(wayland::pointer_t const& pointer, wayland::surface_t const& surface, IInputHandlerPointer& handler); + CInputProcessorPointer(wayland::surface_t const& surface, IInputHandlerPointer& handler); void SetCoordinateScale(std::int32_t scale) { m_coordinateScale = scale; } + void OnPointerEnter(CSeat* seat, std::uint32_t serial, wayland::surface_t surface, double surfaceX, double surfaceY) override; + void OnPointerLeave(CSeat* seat, std::uint32_t serial, wayland::surface_t surface) override; + void OnPointerMotion(CSeat* seat, std::uint32_t time, double surfaceX, double surfaceY) override; + void OnPointerButton(CSeat* seat, std::uint32_t serial, std::uint32_t time, std::uint32_t button, wayland::pointer_button_state state) override; + void OnPointerAxis(CSeat* seat, std::uint32_t time, wayland::pointer_axis axis, double value) override; + private: CInputProcessorPointer(CInputProcessorPointer const& other) = delete; CInputProcessorPointer& operator=(CInputProcessorPointer const& other) = delete; @@ -47,7 +55,6 @@ private: void SendMouseMotion(); void SendMouseButton(unsigned char button, bool pressed); - wayland::pointer_t m_pointer; wayland::surface_t m_surface; IInputHandlerPointer& m_handler; diff --git a/xbmc/windowing/wayland/InputProcessorTouch.cpp b/xbmc/windowing/wayland/InputProcessorTouch.cpp index 3fa3a559da..8f1208b9d9 100644 --- a/xbmc/windowing/wayland/InputProcessorTouch.cpp +++ b/xbmc/windowing/wayland/InputProcessorTouch.cpp @@ -12,76 +12,81 @@ using namespace KODI::WINDOWING::WAYLAND; -CInputProcessorTouch::CInputProcessorTouch(wayland::touch_t const& touch, wayland::surface_t const& surface) -: m_touch{touch}, m_surface{surface} +CInputProcessorTouch::CInputProcessorTouch(wayland::surface_t const& surface) +: m_surface{surface} { - m_touch.on_down() = [this](std::uint32_t, std::uint32_t time, wayland::surface_t surface, std::int32_t id, double x, double y) +} + +void CInputProcessorTouch::OnTouchDown(CSeat* seat, std::uint32_t serial, std::uint32_t time, wayland::surface_t surface, std::int32_t id, double x, double y) +{ + if (surface != m_surface) { - if (surface != m_surface) - { - return; - } + return; + } - // Find free Kodi pointer number - int kodiPointer{-1}; - // Not optimal, but irrelevant for the small number of iterations - for (int testPointer{0}; testPointer < CGenericTouchInputHandler::MAX_POINTERS; testPointer++) + // Find free Kodi pointer number + int kodiPointer{-1}; + // Not optimal, but irrelevant for the small number of iterations + for (int testPointer{0}; testPointer < CGenericTouchInputHandler::MAX_POINTERS; testPointer++) + { + if (std::all_of(m_touchPoints.cbegin(), m_touchPoints.cend(), + [=](decltype(m_touchPoints)::value_type const& pair) + { + return (pair.second.kodiPointerNumber != testPointer); + })) { - if (std::all_of(m_touchPoints.cbegin(), m_touchPoints.cend(), - [=](decltype(m_touchPoints)::value_type const& pair) - { - return (pair.second.kodiPointerNumber != testPointer); - })) - { - kodiPointer = testPointer; - break; - } + kodiPointer = testPointer; + break; } + } - if (kodiPointer != -1) - { - auto it = m_touchPoints.emplace(std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple(time, kodiPointer, x * m_coordinateScale, y * m_coordinateScale, 0.0f)).first; - SendTouchPointEvent(TouchInputDown, it->second); - } - }; - m_touch.on_up() = [this](std::uint32_t, std::uint32_t time, std::int32_t id) + if (kodiPointer != -1) { - auto it = m_touchPoints.find(id); - if (it != m_touchPoints.end()) - { - auto& point = it->second; - point.lastEventTime = time; - SendTouchPointEvent(TouchInputUp, point); - m_touchPoints.erase(it); - } - }; - m_touch.on_motion() = [this](std::uint32_t time, std::int32_t id, double x, double y) + auto it = m_touchPoints.emplace(std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple(time, kodiPointer, x * m_coordinateScale, y * m_coordinateScale, 0.0f)).first; + SendTouchPointEvent(TouchInputDown, it->second); + } +} + +void CInputProcessorTouch::OnTouchUp(CSeat* seat, std::uint32_t serial, std::uint32_t time, std::int32_t id) +{ + auto it = m_touchPoints.find(id); + if (it != m_touchPoints.end()) { - auto it = m_touchPoints.find(id); - if (it != m_touchPoints.end()) - { - auto& point = it->second; - point.x = x * m_coordinateScale; - point.y = y * m_coordinateScale; - point.lastEventTime = time; - SendTouchPointEvent(TouchInputMove, point); - } - }; - m_touch.on_cancel() = [this]() + auto& point = it->second; + point.lastEventTime = time; + SendTouchPointEvent(TouchInputUp, point); + m_touchPoints.erase(it); + } +} + +void CInputProcessorTouch::OnTouchMotion(CSeat* seat, std::uint32_t time, std::int32_t id, double x, double y) +{ + auto it = m_touchPoints.find(id); + if (it != m_touchPoints.end()) { - AbortTouches(); - }; - m_touch.on_shape() = [this](std::int32_t id, double major, double minor) + auto& point = it->second; + point.x = x * m_coordinateScale; + point.y = y * m_coordinateScale; + point.lastEventTime = time; + SendTouchPointEvent(TouchInputMove, point); + } +} + +void CInputProcessorTouch::OnTouchCancel(CSeat* seat) +{ + AbortTouches(); +} + +void CInputProcessorTouch::OnTouchShape(CSeat* seat, std::int32_t id, double major, double minor) +{ + auto it = m_touchPoints.find(id); + if (it != m_touchPoints.end()) { - auto it = m_touchPoints.find(id); - if (it != m_touchPoints.end()) - { - auto& point = it->second; - // Kodi only supports size without shape, so use average of both axes - point.size = ((major + minor) / 2.0) * m_coordinateScale; - UpdateTouchPoint(point); - } - }; + auto& point = it->second; + // Kodi only supports size without shape, so use average of both axes + point.size = ((major + minor) / 2.0) * m_coordinateScale; + UpdateTouchPoint(point); + } } CInputProcessorTouch::~CInputProcessorTouch() noexcept diff --git a/xbmc/windowing/wayland/InputProcessorTouch.h b/xbmc/windowing/wayland/InputProcessorTouch.h index 63f589600d..a71d88d455 100644 --- a/xbmc/windowing/wayland/InputProcessorTouch.h +++ b/xbmc/windowing/wayland/InputProcessorTouch.h @@ -14,6 +14,7 @@ #include <wayland-client-protocol.hpp> #include "input/touch/ITouchInputHandler.h" +#include "Seat.h" namespace KODI { @@ -27,13 +28,19 @@ namespace WAYLAND * * Events go directly to \ref CGenericTouchInputHandler, so no callbacks here */ -class CInputProcessorTouch +class CInputProcessorTouch final : public IRawInputHandlerTouch { public: - CInputProcessorTouch(wayland::touch_t const& touch, wayland::surface_t const& surface); + CInputProcessorTouch(wayland::surface_t const& surface); ~CInputProcessorTouch() noexcept; void SetCoordinateScale(std::int32_t scale) { m_coordinateScale = scale; } + void OnTouchDown(CSeat* seat, std::uint32_t serial, std::uint32_t time, wayland::surface_t surface, std::int32_t id, double x, double y) override; + void OnTouchUp(CSeat* seat, std::uint32_t serial, std::uint32_t time, std::int32_t id) override; + void OnTouchMotion(CSeat* seat, std::uint32_t time, std::int32_t id, double x, double y) override; + void OnTouchCancel(CSeat* seat) override; + void OnTouchShape(CSeat* seat, std::int32_t id, double major, double minor) override; + private: CInputProcessorTouch(CInputProcessorTouch const& other) = delete; CInputProcessorTouch& operator=(CInputProcessorTouch const& other) = delete; @@ -57,7 +64,6 @@ private: void UpdateTouchPoint(TouchPoint const& point); void AbortTouches(); - wayland::touch_t m_touch; wayland::surface_t m_surface; std::int32_t m_coordinateScale{1}; diff --git a/xbmc/windowing/wayland/Seat.cpp b/xbmc/windowing/wayland/Seat.cpp index 621fea0099..601cc3d433 100644 --- a/xbmc/windowing/wayland/Seat.cpp +++ b/xbmc/windowing/wayland/Seat.cpp @@ -8,6 +8,11 @@ #include "Seat.h" +#include <cassert> +#include <unistd.h> + +#include "platform/posix/utils/FileHandle.h" +#include "platform/posix/utils/Mmap.h" #include "utils/log.h" using namespace KODI::WINDOWING::WAYLAND; @@ -20,55 +25,54 @@ namespace * Handle change of availability of a wl_seat input capability * * This checks whether the capability is currently available with the wl_seat - * and whether it was bound to a processor. If there is a mismatch between - * these two, the processor is destroyed if a capability was removed or created + * and whether it was bound to a protocol object. If there is a mismatch between + * these two, the protocol proxy is released if a capability was removed or bound * if a capability was added. * - * \param handler CSeat instance * \param caps new capabilities * \param cap capability to check for + * \param seatName human-readable name of the seat for log messages * \param capName human-readable name of the capability for log messages - * \param processor reference to a smart pointer that holds the - * processor corresponding to the capability + * \param proxy proxy object that should be filled with a new instance or reset * \param instanceProvider function that functions as factory for the Wayland * protocol instance if the capability has been added - * \param onNewCapability function that is called after setting the new capability - * instance when it was added */ -template<typename T, typename ProcessorPtrT, typename InstanceProviderT, typename OnNewCapabilityT> -void HandleCapabilityChange(CSeat* handler, - wayland::seat_capability caps, +template<typename T, typename InstanceProviderT> +bool HandleCapabilityChange(wayland::seat_capability caps, wayland::seat_capability cap, - std::string const & capName, - ProcessorPtrT& processor, - InstanceProviderT instanceProvider, - OnNewCapabilityT onNewCapability) + std::string const& seatName, + std::string const& capName, + T& proxy, + InstanceProviderT instanceProvider) { bool hasCapability = caps & cap; - if ((!!processor) != hasCapability) + if ((!!proxy) != hasCapability) { // Capability changed if (hasCapability) { // The capability was added - CLog::Log(LOGDEBUG, "Wayland seat %s gained capability %s", handler->GetName().c_str(), capName.c_str()); - onNewCapability(instanceProvider()); + CLog::Log(LOGDEBUG, "Wayland seat {} gained capability {}", seatName, capName); + proxy = instanceProvider(); + return true; } else { // The capability was removed - CLog::Log(LOGDEBUG, "Wayland seat %s lost capability %s", handler->GetName().c_str(), capName.c_str()); - processor.reset(); + CLog::Log(LOGDEBUG, "Wayland seat {} lost capability {}", seatName, capName); + proxy.proxy_release(); } } + + return false; } } -CSeat::CSeat(std::uint32_t globalName, wayland::seat_t const& seat, wayland::surface_t const& inputSurface, CConnection& connection, IInputHandler& handler) -: m_globalName{globalName}, m_seat{seat}, m_inputSurface{inputSurface}, m_handler{handler}, m_selection{connection, seat} +CSeat::CSeat(std::uint32_t globalName, wayland::seat_t const& seat, CConnection& connection) +: m_globalName{globalName}, m_seat{seat}, m_selection{connection, seat} { m_seat.on_name() = [this](std::string name) { @@ -79,96 +83,194 @@ CSeat::CSeat(std::uint32_t globalName, wayland::seat_t const& seat, wayland::sur CSeat::~CSeat() noexcept = default; -void CSeat::HandleOnCapabilities(wayland::seat_capability caps) +void CSeat::AddRawInputHandlerKeyboard(KODI::WINDOWING::WAYLAND::IRawInputHandlerKeyboard *rawKeyboardHandler) { - HandleCapabilityChange<wayland::pointer_t> - (this, - caps, - wayland::seat_capability::pointer, - "pointer", - m_pointer, - std::bind(&wayland::seat_t::get_pointer, &m_seat), - std::bind(&CSeat::HandlePointerCapability, this, _1)); - HandleCapabilityChange<wayland::keyboard_t> - (this, - caps, - wayland::seat_capability::keyboard, - "keyboard", - m_keyboard, - std::bind(&wayland::seat_t::get_keyboard, &m_seat), - std::bind(&CSeat::HandleKeyboardCapability, this, _1)); - HandleCapabilityChange<wayland::touch_t> - (this, - caps, - wayland::seat_capability::touch, - "touch", - m_touch, - std::bind(&wayland::seat_t::get_touch, &m_seat), - std::bind(&CSeat::HandleTouchCapability, this, _1)); + assert(rawKeyboardHandler); + m_rawKeyboardHandlers.emplace(rawKeyboardHandler); } -void CSeat::HandlePointerCapability(wayland::pointer_t const& pointer) +void CSeat::RemoveRawInputHandlerKeyboard(KODI::WINDOWING::WAYLAND::IRawInputHandlerKeyboard *rawKeyboardHandler) { - m_pointer.reset(new CInputProcessorPointer(pointer, m_inputSurface, static_cast<IInputHandlerPointer&> (*this))); - UpdateCoordinateScale(); + m_rawKeyboardHandlers.erase(rawKeyboardHandler); } -void CSeat::OnPointerEnter(wayland::pointer_t& pointer, std::uint32_t serial) +void CSeat::AddRawInputHandlerPointer(IRawInputHandlerPointer* rawPointerHandler) { - m_handler.OnSetCursor(pointer, serial); - m_handler.OnEnter(m_globalName, InputType::POINTER); + assert(rawPointerHandler); + m_rawPointerHandlers.emplace(rawPointerHandler); } -void CSeat::OnPointerLeave() +void CSeat::RemoveRawInputHandlerPointer(KODI::WINDOWING::WAYLAND::IRawInputHandlerPointer *rawPointerHandler) { - m_handler.OnLeave(m_globalName, InputType::POINTER); + m_rawPointerHandlers.erase(rawPointerHandler); } -void CSeat::OnPointerEvent(XBMC_Event& event) +void CSeat::AddRawInputHandlerTouch(IRawInputHandlerTouch* rawTouchHandler) { - m_handler.OnEvent(m_globalName, InputType::POINTER, event); + assert(rawTouchHandler); + m_rawTouchHandlers.emplace(rawTouchHandler); } -void CSeat::HandleKeyboardCapability(wayland::keyboard_t const& keyboard) +void CSeat::RemoveRawInputHandlerTouch(KODI::WINDOWING::WAYLAND::IRawInputHandlerTouch *rawTouchHandler) { - m_keyboard.reset(new CInputProcessorKeyboard(keyboard, static_cast<IInputHandlerKeyboard&> (*this))); + m_rawTouchHandlers.erase(rawTouchHandler); } -void CSeat::OnKeyboardEnter() +void CSeat::HandleOnCapabilities(wayland::seat_capability caps) { - m_handler.OnEnter(m_globalName, InputType::KEYBOARD); + if (HandleCapabilityChange(caps, wayland::seat_capability::keyboard, GetName(), "keyboard", m_keyboard, std::bind(&wayland::seat_t::get_keyboard, m_seat))) + { + HandleKeyboardCapability(); + } + if (HandleCapabilityChange(caps, wayland::seat_capability::pointer, GetName(), "pointer", m_pointer, std::bind(&wayland::seat_t::get_pointer, m_seat))) + { + HandlePointerCapability(); + } + if (HandleCapabilityChange(caps, wayland::seat_capability::touch, GetName(), "touch", m_touch, std::bind(&wayland::seat_t::get_touch, m_seat))) + { + HandleTouchCapability(); + } } -void CSeat::OnKeyboardLeave() +void CSeat::SetCursor(std::uint32_t serial, wayland::surface_t const &surface, std::int32_t hotspotX, std::int32_t hotspotY) { - m_handler.OnLeave(m_globalName, InputType::KEYBOARD); + if (m_pointer) + { + m_pointer.set_cursor(serial, surface, hotspotX, hotspotY); + } } -void CSeat::OnKeyboardEvent(XBMC_Event& event) +void CSeat::HandleKeyboardCapability() { - m_handler.OnEvent(m_globalName, InputType::KEYBOARD, event); + m_keyboard.on_keymap() = [this](wayland::keyboard_keymap_format format, int fd, std::uint32_t size) + { + KODI::UTILS::POSIX::CFileHandle fdGuard{fd}; + KODI::UTILS::POSIX::CMmap mmap{nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0}; + std::string keymap{static_cast<const char*> (mmap.Data()), size}; + for (auto handler : m_rawKeyboardHandlers) + { + handler->OnKeyboardKeymap(this, format, keymap); + } + }; + m_keyboard.on_enter() = [this](std::uint32_t serial, wayland::surface_t surface, wayland::array_t keys) + { + for (auto handler : m_rawKeyboardHandlers) + { + handler->OnKeyboardEnter(this, serial, surface, keys); + } + }; + m_keyboard.on_leave() = [this](std::uint32_t serial, wayland::surface_t surface) + { + for (auto handler : m_rawKeyboardHandlers) + { + handler->OnKeyboardLeave(this, serial, surface); + } + }; + m_keyboard.on_key() = [this](std::uint32_t serial, std::uint32_t time, std::uint32_t key, wayland::keyboard_key_state state) + { + for (auto handler : m_rawKeyboardHandlers) + { + handler->OnKeyboardKey(this, serial, time, key, state); + } + }; + m_keyboard.on_modifiers() = [this](std::uint32_t serial, std::uint32_t modsDepressed, std::uint32_t modsLatched, std::uint32_t modsLocked, std::uint32_t group) + { + for (auto handler : m_rawKeyboardHandlers) + { + handler->OnKeyboardModifiers(this, serial, modsDepressed, modsLatched, modsLocked, group); + } + }; + m_keyboard.on_repeat_info() = [this](std::int32_t rate, std::int32_t delay) + { + for (auto handler : m_rawKeyboardHandlers) + { + handler->OnKeyboardRepeatInfo(this, rate, delay); + } + }; } -void CSeat::HandleTouchCapability(wayland::touch_t const& touch) -{ - m_touch.reset(new CInputProcessorTouch(touch, m_inputSurface)); - UpdateCoordinateScale(); -} -void CSeat::SetCoordinateScale(std::int32_t scale) +void CSeat::HandlePointerCapability() { - m_coordinateScale = scale; - UpdateCoordinateScale(); + m_pointer.on_enter() = [this](std::uint32_t serial, wayland::surface_t surface, double surfaceX, double surfaceY) + { + for (auto handler : m_rawPointerHandlers) + { + handler->OnPointerEnter(this, serial, surface, surfaceX, surfaceY); + } + }; + m_pointer.on_leave() = [this](std::uint32_t serial, wayland::surface_t surface) + { + for (auto handler : m_rawPointerHandlers) + { + handler->OnPointerLeave(this, serial, surface); + } + }; + m_pointer.on_motion() = [this](std::uint32_t time, double surfaceX, double surfaceY) + { + for (auto handler : m_rawPointerHandlers) + { + handler->OnPointerMotion(this, time, surfaceX, surfaceY); + } + }; + m_pointer.on_button() = [this](std::uint32_t serial, std::uint32_t time, std::uint32_t button, wayland::pointer_button_state state) + { + for (auto handler : m_rawPointerHandlers) + { + handler->OnPointerButton(this, serial, time, button, state); + } + }; + m_pointer.on_axis() = [this](std::uint32_t time, wayland::pointer_axis axis, double value) + { + for (auto handler : m_rawPointerHandlers) + { + handler->OnPointerAxis(this, time, axis, value); + } + }; + // Wayland groups pointer events, but right now there is no benefit in + // treating them in groups. The main use case for doing so seems to be + // multi-axis (i.e. diagnoal) scrolling, but we do not support this anyway. + /*m_pointer.on_frame() = [this]() + { + + };*/ } -void CSeat::UpdateCoordinateScale() +void CSeat::HandleTouchCapability() { - if (m_pointer) + m_touch.on_down() = [this](std::uint32_t serial, std::uint32_t time, wayland::surface_t surface, std::int32_t id, double x, double y) { - m_pointer->SetCoordinateScale(m_coordinateScale); - } - if (m_touch) + for (auto handler : m_rawTouchHandlers) + { + handler->OnTouchDown(this, serial, time, surface, id, x, y); + } + }; + m_touch.on_up() = [this](std::uint32_t serial, std::uint32_t time, std::int32_t id) { - m_touch->SetCoordinateScale(m_coordinateScale); - } + for (auto handler : m_rawTouchHandlers) + { + handler->OnTouchUp(this, serial, time, id); + } + }; + m_touch.on_motion() = [this](std::uint32_t time, std::int32_t id, double x, double y) + { + for (auto handler : m_rawTouchHandlers) + { + handler->OnTouchMotion(this, time, id, x, y); + } + }; + m_touch.on_cancel() = [this]() + { + for (auto handler : m_rawTouchHandlers) + { + handler->OnTouchCancel(this); + } + }; + m_touch.on_shape() = [this](std::int32_t id, double major, double minor) + { + for (auto handler : m_rawTouchHandlers) + { + handler->OnTouchShape(this, id, major, minor); + } + }; } diff --git a/xbmc/windowing/wayland/Seat.h b/xbmc/windowing/wayland/Seat.h index adb083a25a..91c0017e4c 100644 --- a/xbmc/windowing/wayland/Seat.h +++ b/xbmc/windowing/wayland/Seat.h @@ -8,18 +8,12 @@ #pragma once -#include <map> -#include <memory> +#include <cstdint> +#include <set> #include <wayland-client-protocol.hpp> -#include "input/touch/ITouchInputHandler.h" -#include "InputProcessorPointer.h" -#include "InputProcessorKeyboard.h" -#include "InputProcessorTouch.h" #include "SeatSelection.h" -#include "threads/Timer.h" -#include "windowing/XBMC_events.h" namespace KODI { @@ -28,73 +22,91 @@ namespace WINDOWING namespace WAYLAND { -enum class InputType +class CSeat; + +/** + * Handler for raw wl_keyboard events + * + * All functions are identical to wl_keyboard, except for the keymap which is + * retrieved from its fd and put into a string + */ +class IRawInputHandlerKeyboard { - POINTER, - KEYBOARD, - TOUCH +public: + virtual void OnKeyboardKeymap(CSeat* seat, wayland::keyboard_keymap_format format, std::string const& keymap) {} + virtual void OnKeyboardEnter(CSeat* seat, std::uint32_t serial, wayland::surface_t surface, wayland::array_t keys) {} + virtual void OnKeyboardLeave(CSeat* seat, std::uint32_t serial, wayland::surface_t surface) {} + virtual void OnKeyboardKey(CSeat* seat, std::uint32_t serial, std::uint32_t time, std::uint32_t key, wayland::keyboard_key_state state) {} + virtual void OnKeyboardModifiers(CSeat* seat, std::uint32_t serial, std::uint32_t modsDepressed, std::uint32_t modsLatched, std::uint32_t modsLocked, std::uint32_t group) {} + virtual void OnKeyboardRepeatInfo(CSeat* seat, std::int32_t rate, std::int32_t delay) {} +protected: + ~IRawInputHandlerKeyboard() {} }; /** - * Handler interface for input events from \ref CSeatInputProcessor + * Handler for raw wl_pointer events + * + * All functions are identical to wl_pointer */ -class IInputHandler +class IRawInputHandlerPointer { public: - /** - * Handle input event - * \param seatGlobalName numeric Wayland global name of the seat the event occured on - * \param type input device type that caused the event - * \param event XBMC event data - */ - virtual void OnEvent(std::uint32_t seatGlobalName, InputType type, XBMC_Event& event) {} - /** - * Handle focus enter - * \param seatGlobalName numeric Wayland global name of the seat the event occured on - * \param type input device type for which the surface has gained the focus - */ - virtual void OnEnter(std::uint32_t seatGlobalName, InputType type) {} - /** - * Handle focus leave - * \param seatGlobalName numeric Wayland global name of the seat the event occured on - * \param type input device type for which the surface has lost the focus - */ - virtual void OnLeave(std::uint32_t seatGlobalName, InputType type) {} - /** - * Handle request for setting the cursor - * - * When the client gains pointer focus for a surface, a cursor image must be - * attached to the pointer. Otherwise the previous pointer image would - * be used. - * - * This request is sent in addition to \ref OnEnter for \ref InputType::POINTER. - * - * \param pointer pointer instance that needs its cursor set - * \param serial Wayland protocol message serial that must be sent back in set_cursor - */ - virtual void OnSetCursor(wayland::pointer_t& pointer, std::uint32_t serial) {} + virtual void OnPointerEnter(CSeat* seat, std::uint32_t serial, wayland::surface_t surface, double surfaceX, double surfaceY) {} + virtual void OnPointerLeave(CSeat* seat, std::uint32_t serial, wayland::surface_t surface) {} + virtual void OnPointerMotion(CSeat* seat, std::uint32_t time, double surfaceX, double surfaceY) {} + virtual void OnPointerButton(CSeat* seat, std::uint32_t serial, std::uint32_t time, std::uint32_t button, wayland::pointer_button_state state) {} + virtual void OnPointerAxis(CSeat* seat, std::uint32_t time, wayland::pointer_axis axis, double value) {} +protected: + ~IRawInputHandlerPointer() {} +}; - virtual ~IInputHandler() = default; +/** + * Handler for raw wl_touch events + * + * All functions are identical to wl_touch + */ +class IRawInputHandlerTouch +{ +public: + virtual void OnTouchDown(CSeat* seat, std::uint32_t serial, std::uint32_t time, wayland::surface_t surface, std::int32_t id, double x, double y) {} + virtual void OnTouchUp(CSeat* seat, std::uint32_t serial, std::uint32_t time, std::int32_t id) {} + virtual void OnTouchMotion(CSeat* seat, std::uint32_t time, std::int32_t id, double x, double y) {} + virtual void OnTouchCancel(CSeat* seat) {} + virtual void OnTouchShape(CSeat* seat, std::int32_t id, double major, double minor) {} +protected: + ~IRawInputHandlerTouch() {}; }; /** - * Handle all wl_seat-related events and process them into Kodi events + * Handle all events and requests related to one seat (including input and selection) + * + * The primary purpose of this class is to act as entry point of Wayland events into + * the Kodi world and distribute them further as necessary. + * Input events are forwarded to (potentially multiple) handlers. As the Wayland + * protocol is not very specific on having multiple wl_seat/wl_pointer instances + * and how they interact, having one central instance and then handling everything + * in Kodi with multiple handlers is better than each handler having its own + * protocol object instance. */ -class CSeat : IInputHandlerPointer, IInputHandlerKeyboard +class CSeat { public: /** * Construct seat handler * \param globalName Wayland numeric global name of the seat * \param seat bound seat_t instance - * \param inputSurface surface that receives the input, used for matching - * pointer focus enter/leave events * \param connection connection for retrieving additional globals - * \param handler handler that receives events from this seat, must not be null */ - CSeat(std::uint32_t globalName, wayland::seat_t const& seat, wayland::surface_t const& inputSurface, CConnection& connection, IInputHandler& handler); + CSeat(std::uint32_t globalName, wayland::seat_t const& seat, CConnection& connection); ~CSeat() noexcept; + void AddRawInputHandlerKeyboard(IRawInputHandlerKeyboard* rawKeyboardHandler); + void RemoveRawInputHandlerKeyboard(IRawInputHandlerKeyboard* rawKeyboardHandler); + void AddRawInputHandlerPointer(IRawInputHandlerPointer* rawPointerHandler); + void RemoveRawInputHandlerPointer(IRawInputHandlerPointer* rawPointerHandler); + void AddRawInputHandlerTouch(IRawInputHandlerTouch* rawTouchHandler); + void RemoveRawInputHandlerTouch(IRawInputHandlerTouch* rawTouchHandler); + std::uint32_t GetGlobalName() const { return m_globalName; @@ -119,38 +131,47 @@ public: { return m_selection.GetSelectionText(); } - void SetCoordinateScale(std::int32_t scale); + /** + * Get the wl_seat underlying this seat + * + * The wl_seat should only be used when strictly necessary, e.g. when + * starting a move operation with shell interfaces. + * It may not be used to derive further wl_pointer etc. instances. + */ + wayland::seat_t const& GetWlSeat() + { + return m_seat; + } + + /** + * Set the cursor of the pointer of this seat + * + * Parameters are identical wo wl_pointer.set_cursor(). + * If the seat does not currently have the pointer capability, this is a no-op. + */ + void SetCursor(std::uint32_t serial, wayland::surface_t const& surface, std::int32_t hotspotX, std::int32_t hotspotY); private: CSeat(CSeat const& other) = delete; CSeat& operator=(CSeat const& other) = delete; void HandleOnCapabilities(wayland::seat_capability caps); - void HandlePointerCapability(wayland::pointer_t const& pointer); - void HandleKeyboardCapability(wayland::keyboard_t const& keyboard); - void HandleTouchCapability(wayland::touch_t const& touch); - - void OnKeyboardEnter() override; - void OnKeyboardLeave() override; - void OnKeyboardEvent(XBMC_Event& event) override; - - void OnPointerEnter(wayland::pointer_t& pointer, std::uint32_t serial) override; - void OnPointerLeave() override; - void OnPointerEvent(XBMC_Event& event) override; - - void UpdateCoordinateScale(); + void HandlePointerCapability(); + void HandleKeyboardCapability(); + void HandleTouchCapability(); std::uint32_t m_globalName; - wayland::seat_t m_seat; - wayland::surface_t m_inputSurface; std::string m_name{"<unknown>"}; - std::int32_t m_coordinateScale{1}; - IInputHandler& m_handler; + wayland::seat_t m_seat; + wayland::pointer_t m_pointer; + wayland::keyboard_t m_keyboard; + wayland::touch_t m_touch; + + std::set<IRawInputHandlerKeyboard*> m_rawKeyboardHandlers; + std::set<IRawInputHandlerPointer*> m_rawPointerHandlers; + std::set<IRawInputHandlerTouch*> m_rawTouchHandlers; - std::unique_ptr<CInputProcessorPointer> m_pointer; - std::unique_ptr<CInputProcessorKeyboard> m_keyboard; - std::unique_ptr<CInputProcessorTouch> m_touch; CSeatSelection m_selection; }; diff --git a/xbmc/windowing/wayland/SeatInputProcessing.cpp b/xbmc/windowing/wayland/SeatInputProcessing.cpp new file mode 100644 index 0000000000..6430aad20c --- /dev/null +++ b/xbmc/windowing/wayland/SeatInputProcessing.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "SeatInputProcessing.h" + +#include <cassert> + +using namespace KODI::WINDOWING::WAYLAND; + +CSeatInputProcessing::CSeatInputProcessing(wayland::surface_t const& inputSurface, IInputHandler& handler) +: m_inputSurface{inputSurface}, m_handler{handler} +{ +} + +void CSeatInputProcessing::AddSeat(CSeat* seat) +{ + assert(m_seats.find(seat->GetGlobalName()) == m_seats.end()); + auto& seatState = m_seats.emplace(seat->GetGlobalName(), seat).first->second; + + seatState.keyboardProcessor.reset(new CInputProcessorKeyboard(*this)); + seat->AddRawInputHandlerKeyboard(seatState.keyboardProcessor.get()); + seatState.pointerProcessor.reset(new CInputProcessorPointer(m_inputSurface, *this)); + seat->AddRawInputHandlerPointer(seatState.pointerProcessor.get()); + seatState.touchProcessor.reset(new CInputProcessorTouch(m_inputSurface)); + seat->AddRawInputHandlerTouch(seatState.touchProcessor.get()); +} + +void CSeatInputProcessing::RemoveSeat(CSeat* seat) +{ + auto seatStateI = m_seats.find(seat->GetGlobalName()); + if (seatStateI != m_seats.end()) + { + seat->RemoveRawInputHandlerKeyboard(seatStateI->second.keyboardProcessor.get()); + seat->RemoveRawInputHandlerPointer(seatStateI->second.pointerProcessor.get()); + seat->RemoveRawInputHandlerTouch(seatStateI->second.touchProcessor.get()); + m_seats.erase(seatStateI); + } +} + +void CSeatInputProcessing::OnPointerEnter(std::uint32_t seatGlobalName, std::uint32_t serial) +{ + m_handler.OnSetCursor(seatGlobalName, serial); + m_handler.OnEnter(InputType::POINTER); +} + +void CSeatInputProcessing::OnPointerLeave() +{ + m_handler.OnLeave(InputType::POINTER); +} + +void CSeatInputProcessing::OnPointerEvent(XBMC_Event& event) +{ + m_handler.OnEvent(InputType::POINTER, event); +} + +void CSeatInputProcessing::OnKeyboardEnter() +{ + m_handler.OnEnter(InputType::KEYBOARD); +} + +void CSeatInputProcessing::OnKeyboardLeave() +{ + m_handler.OnLeave(InputType::KEYBOARD); +} + +void CSeatInputProcessing::OnKeyboardEvent(XBMC_Event& event) +{ + m_handler.OnEvent(InputType::KEYBOARD, event); +} + +void CSeatInputProcessing::SetCoordinateScale(std::int32_t scale) +{ + for (auto& seatPair : m_seats) + { + seatPair.second.touchProcessor->SetCoordinateScale(scale); + seatPair.second.pointerProcessor->SetCoordinateScale(scale); + } +} diff --git a/xbmc/windowing/wayland/SeatInputProcessing.h b/xbmc/windowing/wayland/SeatInputProcessing.h new file mode 100644 index 0000000000..46a00912be --- /dev/null +++ b/xbmc/windowing/wayland/SeatInputProcessing.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include <cstdint> +#include <map> +#include <memory> + +#include <wayland-client-protocol.hpp> + +#include "InputProcessorKeyboard.h" +#include "InputProcessorPointer.h" +#include "InputProcessorTouch.h" +#include "Seat.h" + +namespace KODI +{ +namespace WINDOWING +{ +namespace WAYLAND +{ + +enum class InputType +{ + POINTER, + KEYBOARD, + TOUCH +}; + +/** + * Handler interface for input events from \ref CSeatInputProcessor + */ +class IInputHandler +{ +public: + /** + * Handle input event + * \param type input device type that caused the event + * \param event XBMC event data + */ + virtual void OnEvent(InputType type, XBMC_Event& event) {} + /** + * Handle focus enter + * \param type input device type for which the surface has gained the focus + */ + virtual void OnEnter(InputType type) {} + /** + * Handle focus leave + * \param type input device type for which the surface has lost the focus + */ + virtual void OnLeave(InputType type) {} + /** + * Handle request for setting the cursor + * + * When the client gains pointer focus for a surface, a cursor image must be + * attached to the pointer. Otherwise the previous pointer image would + * be used. + * + * This request is sent in addition to \ref OnEnter for \ref InputType::POINTER. + * + * \param seatGlobalName numeric Wayland global name of the seat the event occured on + * \param pointer pointer instance that needs its cursor set + * \param serial Wayland protocol message serial that must be sent back in set_cursor + */ + virtual void OnSetCursor(std::uint32_t seatGlobalName, std::uint32_t serial) {} + + virtual ~IInputHandler() = default; +}; + +/** + * Receive events from all registered wl_seats and process them into Kodi events + * + * Multi-seat support is not currently implemented completely, but each seat has + * separate state. + */ +class CSeatInputProcessing final : IInputHandlerPointer, IInputHandlerKeyboard +{ +public: + /** + * Construct a seat input processor + * + * \param inputSurface Surface that events should be processed on (all other surfaces are ignored) + * \param handler Mandatory handler for processed input events + */ + CSeatInputProcessing(wayland::surface_t const& inputSurface, IInputHandler& handler); + void AddSeat(CSeat* seat); + void RemoveSeat(CSeat* seat); + + /** + * Set the scale the coordinates should be interpreted at + * + * Wayland input events are always in surface coordinates, but Kodi only uses + * buffer coordinates internally. Use this function to set the scaling + * factor between the two and multiply the surface coordinates accordingly + * for Kodi events. + * + * \param scale new buffer-to-surface pixel ratio + */ + void SetCoordinateScale(std::int32_t scale); + +private: + wayland::surface_t m_inputSurface; + IInputHandler& m_handler; + + void OnPointerEnter(std::uint32_t seatGlobalName, std::uint32_t serial) override; + void OnPointerLeave() override; + void OnPointerEvent(XBMC_Event& event) override; + + void OnKeyboardEnter() override; + void OnKeyboardLeave() override; + void OnKeyboardEvent(XBMC_Event& event) override; + + struct SeatState + { + CSeat* seat; + std::unique_ptr<CInputProcessorKeyboard> keyboardProcessor; + std::unique_ptr<CInputProcessorPointer> pointerProcessor; + std::unique_ptr<CInputProcessorTouch> touchProcessor; + + SeatState(CSeat* seat) + : seat{seat} + {} + }; + std::map<std::uint32_t, SeatState> m_seats; +}; + +} +} +} diff --git a/xbmc/windowing/wayland/WinSystemWayland.cpp b/xbmc/windowing/wayland/WinSystemWayland.cpp index 5746645649..23f34906af 100644 --- a/xbmc/windowing/wayland/WinSystemWayland.cpp +++ b/xbmc/windowing/wayland/WinSystemWayland.cpp @@ -241,6 +241,9 @@ bool CWinSystemWayland::DestroyWindowSystem() m_outputsInPreparation.clear(); m_outputs.clear(); m_frameCallback = wayland::callback_t{}; + m_screenSaverManager.reset(); + + m_seatInputProcessing.reset(); if (m_registry) { @@ -347,6 +350,7 @@ bool CWinSystemWayland::CreateNewWindow(const std::string& name, UpdateDesktopResolution(res, m_bufferSize.Width(), m_bufferSize.Height(), res.fRefreshRate, 0); res.bFullScreen = fullScreen; + m_seatInputProcessing.reset(new CSeatInputProcessing(m_surface, *this)); m_seatRegistry.reset(new CRegistry{*m_connection}); // version 2 adds name event -> optional // version 4 adds wl_keyboard repeat_info -> optional @@ -354,7 +358,7 @@ bool CWinSystemWayland::CreateNewWindow(const std::string& name, m_seatRegistry->Request<wayland::seat_t>(1, 5, std::bind(&CWinSystemWayland::OnSeatAdded, this, _1, _2), std::bind(&CWinSystemWayland::OnSeatRemoved, this, _1)); m_seatRegistry->Bind(); - if (m_seatProcessors.empty()) + if (m_seats.empty()) { CLog::Log(LOGWARNING, "Wayland compositor did not announce a wl_seat - you will not have any input devices for the time being"); } @@ -399,7 +403,7 @@ bool CWinSystemWayland::DestroyWindow() // waylandpp automatically calls wl_surface_destroy when the last reference is removed m_surface = wayland::surface_t(); m_windowDecorator.reset(); - m_seatProcessors.clear(); + m_seats.clear(); m_lastSetOutput.proxy_release(); m_surfaceOutputs.clear(); m_surfaceSubmissions.clear(); @@ -1056,9 +1060,9 @@ bool CWinSystemWayland::Minimize() bool CWinSystemWayland::HasCursor() { - CSingleLock lock(m_seatProcessorsMutex); - return std::any_of(m_seatProcessors.cbegin(), m_seatProcessors.cend(), - [](decltype(m_seatProcessors)::value_type const& entry) + CSingleLock lock(m_seatsMutex); + return std::any_of(m_seats.cbegin(), m_seats.cend(), + [](decltype(m_seats)::value_type const& entry) { return entry.second.HasPointerCapability(); }); @@ -1112,20 +1116,29 @@ void CWinSystemWayland::Unregister(IDispResource* resource) void CWinSystemWayland::OnSeatAdded(std::uint32_t name, wayland::proxy_t&& proxy) { - CSingleLock lock(m_seatProcessorsMutex); + CSingleLock lock(m_seatsMutex); wayland::seat_t seat(proxy); - auto newSeatEmplace = m_seatProcessors.emplace(std::piecewise_construct, - std::forward_as_tuple(name), - std::forward_as_tuple(name, seat, m_surface, *m_connection, static_cast<IInputHandler&> (*this))); - newSeatEmplace.first->second.SetCoordinateScale(m_scale); + auto newSeatEmplace = m_seats.emplace(std::piecewise_construct, + std::forward_as_tuple(name), + std::forward_as_tuple(name, seat, *m_connection)); + + auto& seatInst = newSeatEmplace.first->second; + m_seatInputProcessing->AddSeat(&seatInst); + m_windowDecorator->AddSeat(&seatInst); } void CWinSystemWayland::OnSeatRemoved(std::uint32_t name) { - CSingleLock lock(m_seatProcessorsMutex); + CSingleLock lock(m_seatsMutex); - m_seatProcessors.erase(name); + auto seatI = m_seats.find(name); + if (seatI != m_seats.end()) + { + m_seatInputProcessing->RemoveSeat(&seatI->second); + m_windowDecorator->RemoveSeat(&seatI->second); + m_seats.erase(name); + } } void CWinSystemWayland::OnOutputAdded(std::uint32_t name, wayland::proxy_t&& proxy) @@ -1179,7 +1192,7 @@ void CWinSystemWayland::SendFocusChange(bool focus) } } -void CWinSystemWayland::OnEnter(std::uint32_t seatGlobalName, InputType type) +void CWinSystemWayland::OnEnter(InputType type) { // Couple to keyboard focus if (type == InputType::KEYBOARD) @@ -1192,7 +1205,7 @@ void CWinSystemWayland::OnEnter(std::uint32_t seatGlobalName, InputType type) } } -void CWinSystemWayland::OnLeave(std::uint32_t seatGlobalName, InputType type) +void CWinSystemWayland::OnLeave(InputType type) { // Couple to keyboard focus if (type == InputType::KEYBOARD) @@ -1205,25 +1218,31 @@ void CWinSystemWayland::OnLeave(std::uint32_t seatGlobalName, InputType type) } } -void CWinSystemWayland::OnEvent(std::uint32_t seatGlobalName, InputType type, XBMC_Event& event) +void CWinSystemWayland::OnEvent(InputType type, XBMC_Event& event) { // FIXME dynamic_cast<CWinEventsWayland&>(*m_winEvents).MessagePush(&event); } -void CWinSystemWayland::OnSetCursor(wayland::pointer_t& pointer, std::uint32_t serial) +void CWinSystemWayland::OnSetCursor(std::uint32_t seatGlobalName, std::uint32_t serial) { + auto seatI = m_seats.find(seatGlobalName); + if (seatI == m_seats.end()) + { + return; + } + if (m_osCursorVisible) { LoadDefaultCursor(); if (m_cursorSurface) // Cursor loading could have failed { - pointer.set_cursor(serial, m_cursorSurface, m_cursorImage.hotspot_x(), m_cursorImage.hotspot_y()); + seatI->second.SetCursor(serial, m_cursorSurface, m_cursorImage.hotspot_x(), m_cursorImage.hotspot_y()); } } else { - pointer.set_cursor(serial, wayland::surface_t{}, 0, 0); + seatI->second.SetCursor(serial, wayland::surface_t{}, 0, 0); } } @@ -1244,11 +1263,7 @@ void CWinSystemWayland::ApplyBufferScale() CLog::LogF(LOGINFO, "Setting Wayland buffer scale to %d", m_scale); m_surface.set_buffer_scale(m_scale); m_windowDecorator->SetState(m_configuredSize, m_scale, m_shellSurfaceState); - CSingleLock lock(m_seatProcessorsMutex); - for (auto& seatProcessor : m_seatProcessors) - { - seatProcessor.second.SetCoordinateScale(m_scale); - } + m_seatInputProcessing->SetCoordinateScale(m_scale); } void CWinSystemWayland::UpdateTouchDpi() @@ -1458,12 +1473,12 @@ std::unique_ptr<IOSScreenSaver> CWinSystemWayland::GetOSScreenSaverImpl() std::string CWinSystemWayland::GetClipboardText() { - CSingleLock lock(m_seatProcessorsMutex); + CSingleLock lock(m_seatsMutex); // Get text of first seat with non-empty selection // Actually, the value of the seat that received the Ctrl+V keypress should be used, // but this would need a workaround or proper multi-seat support in Kodi - it's // probably just not that relevant in practice - for (auto const& seat : m_seatProcessors) + for (auto const& seat : m_seats) { auto text = seat.second.GetSelectionText(); if (text != "") diff --git a/xbmc/windowing/wayland/WinSystemWayland.h b/xbmc/windowing/wayland/WinSystemWayland.h index 12ef8370d1..704985e69d 100644 --- a/xbmc/windowing/wayland/WinSystemWayland.h +++ b/xbmc/windowing/wayland/WinSystemWayland.h @@ -23,6 +23,7 @@ #include "Connection.h" #include "Output.h" #include "Seat.h" +#include "SeatInputProcessing.h" #include "Signals.h" #include "ShellSurface.h" #include "platform/linux/OptionalsReg.h" @@ -114,10 +115,10 @@ protected: private: // IInputHandler - void OnEnter(std::uint32_t seatGlobalName, InputType type) override; - void OnLeave(std::uint32_t seatGlobalName, InputType type) override; - void OnEvent(std::uint32_t seatGlobalName, InputType type, XBMC_Event& event) override; - void OnSetCursor(wayland::pointer_t& pointer, std::uint32_t serial) override; + void OnEnter(InputType type) override; + void OnLeave(InputType type) override; + void OnEvent(InputType type, XBMC_Event& event) override; + void OnSetCursor(std::uint32_t seatGlobalName, std::uint32_t serial) override; // IWindowDecorationHandler void OnWindowMove(const wayland::seat_t& seat, std::uint32_t serial) override; @@ -202,8 +203,9 @@ private: // Seat handling // ------------- - std::map<std::uint32_t, CSeat> m_seatProcessors; - CCriticalSection m_seatProcessorsMutex; + std::map<std::uint32_t, CSeat> m_seats; + CCriticalSection m_seatsMutex; + std::unique_ptr<CSeatInputProcessing> m_seatInputProcessing; std::map<std::uint32_t, std::shared_ptr<COutput>> m_outputs; /// outputs that did not receive their done event yet std::map<std::uint32_t, std::shared_ptr<COutput>> m_outputsInPreparation; diff --git a/xbmc/windowing/wayland/WindowDecorator.cpp b/xbmc/windowing/wayland/WindowDecorator.cpp index a44c6357b7..ce5f8cf38b 100644 --- a/xbmc/windowing/wayland/WindowDecorator.cpp +++ b/xbmc/windowing/wayland/WindowDecorator.cpp @@ -420,102 +420,116 @@ CWindowDecorator::CWindowDecorator(IWindowDecorationHandler& handler, CConnectio m_registry.RequestSingleton(m_compositor, 1, 4); m_registry.RequestSingleton(m_subcompositor, 1, 1, false); m_registry.RequestSingleton(m_shm, 1, 1); - m_registry.Request<wayland::seat_t>(1, 5, std::bind(&CWindowDecorator::OnSeatAdded, this, _1, _2), std::bind(&CWindowDecorator::OnSeatRemoved, this, _1)); m_registry.Bind(); } -void CWindowDecorator::OnSeatAdded(std::uint32_t name, wayland::proxy_t&& proxy) +void CWindowDecorator::AddSeat(CSeat* seat) { - wayland::seat_t seat{proxy}; - seat.on_capabilities() = std::bind(&CWindowDecorator::OnSeatCapabilities, this, name, _1); - m_seats.emplace(std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple(std::move(seat))); + m_seats.emplace(std::piecewise_construct, std::forward_as_tuple(seat->GetGlobalName()), std::forward_as_tuple(seat)); + seat->AddRawInputHandlerTouch(this); + seat->AddRawInputHandlerPointer(this); } -void CWindowDecorator::OnSeatRemoved(std::uint32_t name) +void CWindowDecorator::RemoveSeat(CSeat* seat) { - m_seats.erase(name); + seat->RemoveRawInputHandlerTouch(this); + seat->RemoveRawInputHandlerPointer(this); + m_seats.erase(seat->GetGlobalName()); UpdateButtonHoverState(); } -void CWindowDecorator::OnSeatCapabilities(std::uint32_t name, wayland::seat_capability capabilities) +void CWindowDecorator::OnPointerEnter(CSeat* seat, std::uint32_t serial, wayland::surface_t surface, double surfaceX, double surfaceY) { - auto& seat = m_seats.at(name); - if (HandleCapabilityChange(capabilities, wayland::seat_capability::pointer, seat.pointer, std::bind(&wayland::seat_t::get_pointer, seat.seat))) + auto seatStateI = m_seats.find(seat->GetGlobalName()); + if (seatStateI == m_seats.end()) { - HandleSeatPointer(seat); + return; } - if (HandleCapabilityChange(capabilities, wayland::seat_capability::touch, seat.touch, std::bind(&wayland::seat_t::get_touch, seat.seat))) + auto& seatState = seatStateI->second; + // Reset first so we ignore events for surfaces we don't handle + seatState.currentSurface = SURFACE_COUNT; + CSingleLock lock(m_mutex); + for (std::size_t i{0}; i < m_borderSurfaces.size(); i++) { - HandleSeatTouch(seat); + if (m_borderSurfaces[i].surface.wlSurface == surface) + { + seatState.pointerEnterSerial = serial; + seatState.currentSurface = static_cast<SurfaceIndex> (i); + seatState.pointerPosition = {static_cast<float> (surfaceX), static_cast<float> (surfaceY)}; + UpdateSeatCursor(seatState); + UpdateButtonHoverState(); + break; + } } +} + +void CWindowDecorator::OnPointerLeave(CSeat* seat, std::uint32_t serial, wayland::surface_t surface) +{ + auto seatStateI = m_seats.find(seat->GetGlobalName()); + if (seatStateI == m_seats.end()) + { + return; + } + auto& seatState = seatStateI->second; + seatState.currentSurface = SURFACE_COUNT; + // Recreate cursor surface on reenter + seatState.cursorName.clear(); + seatState.cursor.proxy_release(); UpdateButtonHoverState(); } -void CWindowDecorator::HandleSeatPointer(Seat& seat) +void CWindowDecorator::OnPointerMotion(CSeat* seat, std::uint32_t time, double surfaceX, double surfaceY) { - seat.pointer.on_enter() = [this, &seat](std::uint32_t serial, wayland::surface_t surface, float x, float y) + auto seatStateI = m_seats.find(seat->GetGlobalName()); + if (seatStateI == m_seats.end()) { - // Reset first so we ignore events for surfaces we don't handle - seat.currentSurface = SURFACE_COUNT; - CSingleLock lock(m_mutex); - for (std::size_t i{0}; i < m_borderSurfaces.size(); i++) - { - if (m_borderSurfaces[i].surface.wlSurface == surface) - { - seat.pointerEnterSerial = serial; - seat.currentSurface = static_cast<SurfaceIndex> (i); - seat.pointerPosition = {x, y}; - UpdateSeatCursor(seat); - UpdateButtonHoverState(); - break; - } - } - }; - seat.pointer.on_leave() = [this, &seat](std::uint32_t, wayland::surface_t) + return; + } + auto& seatState = seatStateI->second; + if (seatState.currentSurface != SURFACE_COUNT) { - seat.currentSurface = SURFACE_COUNT; - // Recreate cursor surface on reenter - seat.cursorName.clear(); - seat.cursor.proxy_release(); + seatState.pointerPosition = {static_cast<float> (surfaceX), static_cast<float> (surfaceY)}; + UpdateSeatCursor(seatState); UpdateButtonHoverState(); - }; - seat.pointer.on_motion() = [this, &seat](std::uint32_t, float x, float y) + } +} + +void CWindowDecorator::OnPointerButton(CSeat* seat, std::uint32_t serial, std::uint32_t time, std::uint32_t button, wayland::pointer_button_state state) +{ + auto seatStateI = m_seats.find(seat->GetGlobalName()); + if (seatStateI == m_seats.end()) { - if (seat.currentSurface != SURFACE_COUNT) - { - seat.pointerPosition = {x, y}; - UpdateSeatCursor(seat); - UpdateButtonHoverState(); - } - }; - seat.pointer.on_button() = [this, &seat](std::uint32_t serial, std::uint32_t, std::uint32_t button, wayland::pointer_button_state state) + return; + } + auto& seatState = seatStateI->second; + if (seatState.currentSurface != SURFACE_COUNT && state == wayland::pointer_button_state::pressed) { - if (seat.currentSurface != SURFACE_COUNT && state == wayland::pointer_button_state::pressed) - { - HandleSeatClick(seat.seat, seat.currentSurface, serial, button, seat.pointerPosition); - } - }; + HandleSeatClick(seatState, seatState.currentSurface, serial, button, seatState.pointerPosition); + } } -void CWindowDecorator::HandleSeatTouch(Seat& seat) +void CWindowDecorator::OnTouchDown(CSeat* seat, std::uint32_t serial, std::uint32_t time, wayland::surface_t surface, std::int32_t id, double x, double y) { - seat.touch.on_down() = [this, &seat](std::uint32_t serial, std::uint32_t, wayland::surface_t surface, std::int32_t id, float x, float y) + auto seatStateI = m_seats.find(seat->GetGlobalName()); + if (seatStateI == m_seats.end()) { - CSingleLock lock(m_mutex); - for (std::size_t i{0}; i < m_borderSurfaces.size(); i++) + return; + } + auto& seatState = seatStateI->second; + CSingleLock lock(m_mutex); + for (std::size_t i{0}; i < m_borderSurfaces.size(); i++) + { + if (m_borderSurfaces[i].surface.wlSurface == surface) { - if (m_borderSurfaces[i].surface.wlSurface == surface) - { - HandleSeatClick(seat.seat, static_cast<SurfaceIndex> (i), serial, BTN_LEFT, {x, y}); - } + HandleSeatClick(seatState, static_cast<SurfaceIndex> (i), serial, BTN_LEFT, {static_cast<float> (x), static_cast<float> (y)}); } - }; + } } -void CWindowDecorator::UpdateSeatCursor(Seat& seat) +void CWindowDecorator::UpdateSeatCursor(SeatState& seatState) { - if (seat.currentSurface == SURFACE_COUNT) + if (seatState.currentSurface == SURFACE_COUNT) { // Don't set anything if not on any surface return; @@ -527,19 +541,19 @@ void CWindowDecorator::UpdateSeatCursor(Seat& seat) { CSingleLock lock(m_mutex); - auto resizeEdge = ResizeEdgeForPosition(seat.currentSurface, SurfaceGeometry(seat.currentSurface, m_mainSurfaceSize).ToSize(), CPointInt{seat.pointerPosition}); + auto resizeEdge = ResizeEdgeForPosition(seatState.currentSurface, SurfaceGeometry(seatState.currentSurface, m_mainSurfaceSize).ToSize(), CPointInt{seatState.pointerPosition}); if (resizeEdge != wayland::shell_surface_resize::none) { cursorName = CursorForResizeEdge(resizeEdge); } } - if (cursorName == seat.cursorName) + if (cursorName == seatState.cursorName) { // Don't reload cursor all the time when nothing is changing return; } - seat.cursorName = cursorName; + seatState.cursorName = cursorName; wayland::cursor_t cursor; try @@ -553,20 +567,20 @@ void CWindowDecorator::UpdateSeatCursor(Seat& seat) } auto cursorImage = cursor.image(0); - if (!seat.cursor) + if (!seatState.cursor) { - seat.cursor = m_compositor.create_surface(); + seatState.cursor = m_compositor.create_surface(); } - int calcScale{seat.cursor.can_set_buffer_scale() ? m_scale : 1}; + int calcScale{seatState.cursor.can_set_buffer_scale() ? m_scale : 1}; - seat.pointer.set_cursor(seat.pointerEnterSerial, seat.cursor, cursorImage.hotspot_x() / calcScale, cursorImage.hotspot_y() / calcScale); - seat.cursor.attach(cursorImage.get_buffer(), 0, 0); - seat.cursor.damage(0, 0, cursorImage.width() / calcScale, cursorImage.height() / calcScale); - if (seat.cursor.can_set_buffer_scale()) + seatState.seat->SetCursor(seatState.pointerEnterSerial, seatState.cursor, cursorImage.hotspot_x() / calcScale, cursorImage.hotspot_y() / calcScale); + seatState.cursor.attach(cursorImage.get_buffer(), 0, 0); + seatState.cursor.damage(0, 0, cursorImage.width() / calcScale, cursorImage.height() / calcScale); + if (seatState.cursor.can_set_buffer_scale()) { - seat.cursor.set_buffer_scale(m_scale); + seatState.cursor.set_buffer_scale(m_scale); } - seat.cursor.commit(); + seatState.cursor.commit(); } void CWindowDecorator::UpdateButtonHoverState() @@ -599,7 +613,7 @@ void CWindowDecorator::UpdateButtonHoverState() } } -void CWindowDecorator::HandleSeatClick(wayland::seat_t seat, SurfaceIndex surface, std::uint32_t serial, std::uint32_t button, CPoint position) +void CWindowDecorator::HandleSeatClick(SeatState const& seatState, SurfaceIndex surface, std::uint32_t serial, std::uint32_t button, CPoint position) { switch (button) { @@ -618,18 +632,18 @@ void CWindowDecorator::HandleSeatClick(wayland::seat_t seat, SurfaceIndex surfac } } - m_handler.OnWindowMove(seat, serial); + m_handler.OnWindowMove(seatState.seat->GetWlSeat(), serial); } else { - m_handler.OnWindowResize(seat, serial, resizeEdge); + m_handler.OnWindowResize(seatState.seat->GetWlSeat(), serial, resizeEdge); } } break; case BTN_RIGHT: if (surface == SURFACE_TOP) { - m_handler.OnWindowShowContextMenu(seat, serial, CPointInt{position} - CPointInt{BORDER_WIDTH, BORDER_WIDTH + TOP_BAR_HEIGHT}); + m_handler.OnWindowShowContextMenu(seatState.seat->GetWlSeat(), serial, CPointInt{position} - CPointInt{BORDER_WIDTH, BORDER_WIDTH + TOP_BAR_HEIGHT}); } break; } diff --git a/xbmc/windowing/wayland/WindowDecorator.h b/xbmc/windowing/wayland/WindowDecorator.h index f42d274d7b..ff0c8383b1 100644 --- a/xbmc/windowing/wayland/WindowDecorator.h +++ b/xbmc/windowing/wayland/WindowDecorator.h @@ -17,6 +17,7 @@ #include "Connection.h" #include "Registry.h" +#include "Seat.h" #include "ShellSurface.h" #include "threads/CriticalSection.h" #include "Util.h" @@ -58,7 +59,7 @@ enum SurfaceIndex * * The decorations are positioned around the main surface automatically. */ -class CWindowDecorator +class CWindowDecorator final : IRawInputHandlerTouch, IRawInputHandlerPointer { public: /** @@ -107,6 +108,9 @@ public: bool IsDecorationActive() const; + void AddSeat(CSeat* seat); + void RemoveSeat(CSeat* seat); + struct Buffer { void* data{}; @@ -138,6 +142,14 @@ private: CWindowDecorator(CWindowDecorator const& other) = delete; CWindowDecorator& operator=(CWindowDecorator const& other) = delete; + // IRawInputHandlerTouch + void OnTouchDown(CSeat* seat, std::uint32_t serial, std::uint32_t time, wayland::surface_t surface, std::int32_t id, double x, double y) override; + // IRawInputHandlerPointer + void OnPointerEnter(CSeat* seat, std::uint32_t serial, wayland::surface_t surface, double surfaceX, double surfaceY) override; + void OnPointerLeave(CSeat* seat, std::uint32_t serial, wayland::surface_t surface) override; + void OnPointerMotion(CSeat* seat, std::uint32_t time, double surfaceX, double surfaceY) override; + void OnPointerButton(CSeat* seat, std::uint32_t serial, std::uint32_t time, std::uint32_t button, wayland::pointer_button_state state) override; + void Reset(bool reallocate); // These functions should not be called directly as they may leave internal @@ -197,24 +209,21 @@ private: std::set<wayland::buffer_t, WaylandCPtrCompare> m_pendingBuffers; CCriticalSection m_pendingBuffersMutex; - struct Seat + struct SeatState { - wayland::seat_t seat; - wayland::pointer_t pointer; - wayland::touch_t touch; - + CSeat* seat; SurfaceIndex currentSurface{SURFACE_COUNT}; CPoint pointerPosition; - std::uint32_t pointerEnterSerial; + std::uint32_t pointerEnterSerial{}; std::string cursorName; wayland::surface_t cursor; - explicit Seat(wayland::seat_t seat) - : seat{std::move(seat)} + explicit SeatState(CSeat* seat) + : seat{seat} {} }; - std::map<std::uint32_t, Seat> m_seats; + std::map<std::uint32_t, SeatState> m_seats; struct Button { @@ -230,15 +239,9 @@ private: void LoadCursorTheme(); - void OnSeatAdded(std::uint32_t name, wayland::proxy_t&& seat); - void OnSeatRemoved(std::uint32_t name); - void OnSeatCapabilities(std::uint32_t name, wayland::seat_capability capability); - void HandleSeatPointer(Seat& seat); - void HandleSeatTouch(Seat& seat); - - void UpdateSeatCursor(Seat& seat); + void UpdateSeatCursor(SeatState& seatState); void UpdateButtonHoverState(); - void HandleSeatClick(wayland::seat_t seat, SurfaceIndex surface, std::uint32_t serial, std::uint32_t button, CPoint position); + void HandleSeatClick(SeatState const& seatState, SurfaceIndex surface, std::uint32_t serial, std::uint32_t button, CPoint position); }; } diff --git a/xbmc/windowing/wayland/XkbcommonKeymap.cpp b/xbmc/windowing/wayland/XkbcommonKeymap.cpp index 1fb2e5f741..34a9bf6d2e 100644 --- a/xbmc/windowing/wayland/XkbcommonKeymap.cpp +++ b/xbmc/windowing/wayland/XkbcommonKeymap.cpp @@ -16,10 +16,8 @@ #include "Application.h" #include "Util.h" #include "utils/log.h" -#include "platform/posix/utils/Mmap.h" using namespace KODI::WINDOWING::WAYLAND; -using namespace KODI::UTILS::POSIX; namespace { @@ -195,19 +193,16 @@ void CXkbcommonContext::XkbContextDeleter::operator()(xkb_context* ctx) const xkb_context_unref(ctx); } -std::unique_ptr<CXkbcommonKeymap> CXkbcommonContext::KeymapFromSharedMemory(int fd, std::size_t size) +std::unique_ptr<CXkbcommonKeymap> CXkbcommonContext::KeymapFromString(std::string const& keymap) { - CMmap mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0); - auto keymapString = static_cast<const char *> (mmap.Data()); + std::unique_ptr<xkb_keymap, CXkbcommonKeymap::XkbKeymapDeleter> xkbKeymap{xkb_keymap_new_from_string(m_context.get(), keymap.c_str(), XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS), CXkbcommonKeymap::XkbKeymapDeleter()}; - std::unique_ptr<xkb_keymap, CXkbcommonKeymap::XkbKeymapDeleter> keymap{xkb_keymap_new_from_string(m_context.get(), keymapString, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS), CXkbcommonKeymap::XkbKeymapDeleter()}; - - if (!keymap) + if (!xkbKeymap) { throw std::runtime_error("Failed to compile keymap"); } - return std::unique_ptr<CXkbcommonKeymap>{new CXkbcommonKeymap(std::move(keymap))}; + return std::unique_ptr<CXkbcommonKeymap>{new CXkbcommonKeymap(std::move(xkbKeymap))}; } std::unique_ptr<CXkbcommonKeymap> CXkbcommonContext::KeymapFromNames(const std::string& rules, const std::string& model, const std::string& layout, const std::string& variant, const std::string& options) diff --git a/xbmc/windowing/wayland/XkbcommonKeymap.h b/xbmc/windowing/wayland/XkbcommonKeymap.h index 694bc06306..997d64fea6 100644 --- a/xbmc/windowing/wayland/XkbcommonKeymap.h +++ b/xbmc/windowing/wayland/XkbcommonKeymap.h @@ -123,7 +123,7 @@ public: * This function does not own the file descriptor. It must not be closed * from this function. */ - std::unique_ptr<CXkbcommonKeymap> KeymapFromSharedMemory(int fd, std::size_t size); + std::unique_ptr<CXkbcommonKeymap> KeymapFromString(std::string const& keymap); std::unique_ptr<CXkbcommonKeymap> KeymapFromNames(const std::string &rules, const std::string &model, const std::string &layout, const std::string &variant, const std::string &options); private: |