1 /*
2  *  Copyright (C) 2017-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "Seat.h"
10 
11 #include "utils/log.h"
12 
13 #include "platform/posix/utils/FileHandle.h"
14 #include "platform/posix/utils/Mmap.h"
15 
16 #include <cassert>
17 #include <utility>
18 
19 #include <unistd.h>
20 
21 using namespace KODI::WINDOWING::WAYLAND;
22 using namespace std::placeholders;
23 
24 namespace
25 {
26 
27 /**
28  * Handle change of availability of a wl_seat input capability
29  *
30  * This checks whether the capability is currently available with the wl_seat
31  * and whether it was bound to a protocol object. If there is a mismatch between
32  * these two, the protocol proxy is released if a capability was removed or bound
33  * if a capability was added.
34  *
35  * \param caps new capabilities
36  * \param cap capability to check for
37  * \param seatName human-readable name of the seat for log messages
38  * \param capName human-readable name of the capability for log messages
39  * \param proxy proxy object that should be filled with a new instance or reset
40  * \param instanceProvider function that functions as factory for the Wayland
41  *                         protocol instance if the capability has been added
42  */
43 template<typename T, typename InstanceProviderT>
HandleCapabilityChange(const wayland::seat_capability & caps,const wayland::seat_capability & cap,std::string const & seatName,std::string const & capName,T & proxy,InstanceProviderT instanceProvider)44 bool HandleCapabilityChange(const wayland::seat_capability& caps,
45                             const wayland::seat_capability& cap,
46                             std::string const& seatName,
47                             std::string const& capName,
48                             T& proxy,
49                             InstanceProviderT instanceProvider)
50 {
51   bool hasCapability = caps & cap;
52 
53   if ((!!proxy) != hasCapability)
54   {
55     // Capability changed
56 
57     if (hasCapability)
58     {
59       // The capability was added
60       CLog::Log(LOGDEBUG, "Wayland seat {} gained capability {}", seatName, capName);
61       proxy = instanceProvider();
62       return true;
63     }
64     else
65     {
66       // The capability was removed
67       CLog::Log(LOGDEBUG, "Wayland seat {} lost capability {}", seatName, capName);
68       proxy.proxy_release();
69     }
70   }
71 
72   return false;
73 }
74 
75 }
76 
CSeat(std::uint32_t globalName,wayland::seat_t const & seat,CConnection & connection)77 CSeat::CSeat(std::uint32_t globalName, wayland::seat_t const& seat, CConnection& connection)
78 : m_globalName{globalName}, m_seat{seat}, m_selection{connection, seat}
79 {
__anon7da8af680202(std::string name) 80   m_seat.on_name() = [this](std::string name) { m_name = std::move(name); };
81   m_seat.on_capabilities() = std::bind(&CSeat::HandleOnCapabilities, this, std::placeholders::_1);
82 }
83 
84 CSeat::~CSeat() noexcept = default;
85 
AddRawInputHandlerKeyboard(KODI::WINDOWING::WAYLAND::IRawInputHandlerKeyboard * rawKeyboardHandler)86 void CSeat::AddRawInputHandlerKeyboard(KODI::WINDOWING::WAYLAND::IRawInputHandlerKeyboard *rawKeyboardHandler)
87 {
88   assert(rawKeyboardHandler);
89   m_rawKeyboardHandlers.emplace(rawKeyboardHandler);
90 }
91 
RemoveRawInputHandlerKeyboard(KODI::WINDOWING::WAYLAND::IRawInputHandlerKeyboard * rawKeyboardHandler)92 void CSeat::RemoveRawInputHandlerKeyboard(KODI::WINDOWING::WAYLAND::IRawInputHandlerKeyboard *rawKeyboardHandler)
93 {
94   m_rawKeyboardHandlers.erase(rawKeyboardHandler);
95 }
96 
AddRawInputHandlerPointer(IRawInputHandlerPointer * rawPointerHandler)97 void CSeat::AddRawInputHandlerPointer(IRawInputHandlerPointer* rawPointerHandler)
98 {
99   assert(rawPointerHandler);
100   m_rawPointerHandlers.emplace(rawPointerHandler);
101 }
102 
RemoveRawInputHandlerPointer(KODI::WINDOWING::WAYLAND::IRawInputHandlerPointer * rawPointerHandler)103 void CSeat::RemoveRawInputHandlerPointer(KODI::WINDOWING::WAYLAND::IRawInputHandlerPointer *rawPointerHandler)
104 {
105   m_rawPointerHandlers.erase(rawPointerHandler);
106 }
107 
AddRawInputHandlerTouch(IRawInputHandlerTouch * rawTouchHandler)108 void CSeat::AddRawInputHandlerTouch(IRawInputHandlerTouch* rawTouchHandler)
109 {
110   assert(rawTouchHandler);
111   m_rawTouchHandlers.emplace(rawTouchHandler);
112 }
113 
RemoveRawInputHandlerTouch(KODI::WINDOWING::WAYLAND::IRawInputHandlerTouch * rawTouchHandler)114 void CSeat::RemoveRawInputHandlerTouch(KODI::WINDOWING::WAYLAND::IRawInputHandlerTouch *rawTouchHandler)
115 {
116   m_rawTouchHandlers.erase(rawTouchHandler);
117 }
118 
HandleOnCapabilities(const wayland::seat_capability & caps)119 void CSeat::HandleOnCapabilities(const wayland::seat_capability& caps)
120 {
121   if (HandleCapabilityChange(caps, wayland::seat_capability::keyboard, GetName(), "keyboard", m_keyboard, std::bind(&wayland::seat_t::get_keyboard, m_seat)))
122   {
123     HandleKeyboardCapability();
124   }
125   if (HandleCapabilityChange(caps, wayland::seat_capability::pointer, GetName(), "pointer", m_pointer, std::bind(&wayland::seat_t::get_pointer, m_seat)))
126   {
127     HandlePointerCapability();
128   }
129   if (HandleCapabilityChange(caps, wayland::seat_capability::touch, GetName(), "touch", m_touch, std::bind(&wayland::seat_t::get_touch, m_seat)))
130   {
131     HandleTouchCapability();
132   }
133 }
134 
SetCursor(std::uint32_t serial,wayland::surface_t const & surface,std::int32_t hotspotX,std::int32_t hotspotY)135 void CSeat::SetCursor(std::uint32_t serial, wayland::surface_t const &surface, std::int32_t hotspotX, std::int32_t hotspotY)
136 {
137   if (m_pointer)
138   {
139     m_pointer.set_cursor(serial, surface, hotspotX, hotspotY);
140   }
141 }
142 
HandleKeyboardCapability()143 void CSeat::HandleKeyboardCapability()
144 {
145   m_keyboard.on_keymap() = [this](wayland::keyboard_keymap_format format, int fd, std::uint32_t size)
146   {
147     KODI::UTILS::POSIX::CFileHandle fdGuard{fd};
148     KODI::UTILS::POSIX::CMmap mmap{nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0};
149     std::string keymap{static_cast<const char*> (mmap.Data()), size};
150     for (auto handler : m_rawKeyboardHandlers)
151     {
152       handler->OnKeyboardKeymap(this, format, keymap);
153     }
154   };
155   m_keyboard.on_enter() = [this](std::uint32_t serial, const wayland::surface_t& surface,
156                                  const wayland::array_t& keys) {
157     for (auto handler : m_rawKeyboardHandlers)
158     {
159       handler->OnKeyboardEnter(this, serial, surface, keys);
160     }
161   };
162   m_keyboard.on_leave() = [this](std::uint32_t serial, const wayland::surface_t& surface) {
163     for (auto handler : m_rawKeyboardHandlers)
164     {
165       handler->OnKeyboardLeave(this, serial, surface);
166     }
167   };
168   m_keyboard.on_key() = [this](std::uint32_t serial, std::uint32_t time, std::uint32_t key, wayland::keyboard_key_state state)
169   {
170     for (auto handler : m_rawKeyboardHandlers)
171     {
172       handler->OnKeyboardKey(this, serial, time, key, state);
173     }
174   };
175   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)
176   {
177     for (auto handler : m_rawKeyboardHandlers)
178     {
179       handler->OnKeyboardModifiers(this, serial, modsDepressed, modsLatched, modsLocked, group);
180     }
181   };
182   m_keyboard.on_repeat_info() = [this](std::int32_t rate, std::int32_t delay)
183   {
184     for (auto handler : m_rawKeyboardHandlers)
185     {
186       handler->OnKeyboardRepeatInfo(this, rate, delay);
187     }
188   };
189 }
190 
191 
HandlePointerCapability()192 void CSeat::HandlePointerCapability()
193 {
194   m_pointer.on_enter() = [this](std::uint32_t serial, const wayland::surface_t& surface,
195                                 double surfaceX, double surfaceY) {
196     for (auto handler : m_rawPointerHandlers)
197     {
198       handler->OnPointerEnter(this, serial, surface, surfaceX, surfaceY);
199     }
200   };
201   m_pointer.on_leave() = [this](std::uint32_t serial, const wayland::surface_t& surface) {
202     for (auto handler : m_rawPointerHandlers)
203     {
204       handler->OnPointerLeave(this, serial, surface);
205     }
206   };
207   m_pointer.on_motion() = [this](std::uint32_t time, double surfaceX, double surfaceY)
208   {
209     for (auto handler : m_rawPointerHandlers)
210     {
211       handler->OnPointerMotion(this, time, surfaceX, surfaceY);
212     }
213   };
214   m_pointer.on_button() = [this](std::uint32_t serial, std::uint32_t time, std::uint32_t button, wayland::pointer_button_state state)
215   {
216     for (auto handler : m_rawPointerHandlers)
217     {
218       handler->OnPointerButton(this, serial, time, button, state);
219     }
220   };
221   m_pointer.on_axis() = [this](std::uint32_t time, wayland::pointer_axis axis, double value)
222   {
223     for (auto handler : m_rawPointerHandlers)
224     {
225       handler->OnPointerAxis(this, time, axis, value);
226     }
227   };
228   // Wayland groups pointer events, but right now there is no benefit in
229   // treating them in groups. The main use case for doing so seems to be
230   // multi-axis (i.e. diagnoal) scrolling, but we do not support this anyway.
231   /*m_pointer.on_frame() = [this]()
232   {
233 
234   };*/
235 }
236 
HandleTouchCapability()237 void CSeat::HandleTouchCapability()
238 {
239   m_touch.on_down() = [this](std::uint32_t serial, std::uint32_t time,
240                              const wayland::surface_t& surface, std::int32_t id, double x,
241                              double y) {
242     for (auto handler : m_rawTouchHandlers)
243     {
244       handler->OnTouchDown(this, serial, time, surface, id, x, y);
245     }
246   };
247   m_touch.on_up() = [this](std::uint32_t serial, std::uint32_t time, std::int32_t id)
248   {
249     for (auto handler : m_rawTouchHandlers)
250     {
251       handler->OnTouchUp(this, serial, time, id);
252     }
253   };
254   m_touch.on_motion() = [this](std::uint32_t time, std::int32_t id, double x, double y)
255   {
256     for (auto handler : m_rawTouchHandlers)
257     {
258       handler->OnTouchMotion(this, time, id, x, y);
259     }
260   };
261   m_touch.on_cancel() = [this]()
262   {
263     for (auto handler : m_rawTouchHandlers)
264     {
265       handler->OnTouchCancel(this);
266     }
267   };
268   m_touch.on_shape() = [this](std::int32_t id, double major, double minor)
269   {
270     for (auto handler : m_rawTouchHandlers)
271     {
272       handler->OnTouchShape(this, id, major, minor);
273     }
274   };
275 }
276