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 #pragma once
10 
11 #include "Connection.h"
12 #include "Registry.h"
13 #include "Seat.h"
14 #include "ShellSurface.h"
15 #include "Util.h"
16 #include "WindowDecorationHandler.h"
17 #include "threads/CriticalSection.h"
18 #include "utils/Geometry.h"
19 
20 #include "platform/posix/utils/SharedMemory.h"
21 
22 #include <array>
23 #include <memory>
24 #include <set>
25 
26 #include <wayland-client-protocol.hpp>
27 #include <wayland-cursor.hpp>
28 
29 namespace KODI
30 {
31 namespace WINDOWING
32 {
33 namespace WAYLAND
34 {
35 
36 enum SurfaceIndex
37 {
38   SURFACE_TOP = 0,
39   SURFACE_RIGHT,
40   SURFACE_BOTTOM,
41   SURFACE_LEFT,
42   SURFACE_COUNT
43 };
44 
45 /**
46  * Paint decorations around windows with subcompositing
47  *
48  * With Wayland, applications are responsible for drawing borders on their windows
49  * themselves (client-side decorations). To keep the impact on the overall architecture
50  * low, the Wayland platform implementation uses this very simple renderer to
51  * build ugly but fully functional decorations around the Kodi window. Since Kodi as a
52  * media center is usually not used in windowed mode anyway (except maybe for
53  * development), the impact of the visually not-so-pleasing decorations should
54  * be limited. Nice decorations would require more effort and external libraries for
55  * proper 2D drawing.
56  *
57  * If more platforms decide that client-side decorations would be a good idea to
58  * implement, the decorations could also be integrated with the Kodi skin system.
59  * Then this class could be removed.
60  *
61  * The decorations are positioned around the main surface automatically.
62  */
63 class CWindowDecorator final : IRawInputHandlerTouch, IRawInputHandlerPointer
64 {
65 public:
66   /**
67    * Construct window decorator
68    * \param handler handler for window decoration events
69    * \param connection connection to get Wayland globals
70    * \param mainSurface main surface that decorations are constructed around
71    * \param windowSize full size of the window including decorations
72    * \param scale scale to use for buffers
73    * \param state surface state for adjusting decoration appearance
74    */
75   CWindowDecorator(IWindowDecorationHandler& handler, CConnection& connection, wayland::surface_t const& mainSurface);
76 
77   /**
78    * Set decoration state and size by providing full surface size including decorations
79    *
80    * Calculates size of the main surface from size of all surfaces combined (including
81    * window decorations) by subtracting the decoration size
82    *
83    * Decorations will be disabled if state includes STATE_FULLSCREEN
84    *
85    * Call only from main thread
86    */
87   void SetState(CSizeInt size, int scale, IShellSurface::StateBitset state);
88   /**
89    * Get calculated size of main surface
90    */
GetMainSurfaceSize()91   CSizeInt GetMainSurfaceSize() const
92   {
93     return m_mainSurfaceSize;
94   }
95   /**
96    * Get full geometry of the window, including decorations if active
97    */
98   CRectInt GetWindowGeometry() const;
99   /**
100    * Calculate size of main surface given the size of the full area
101    * including decorations and a state
102    */
103   CSizeInt CalculateMainSurfaceSize(CSizeInt size, IShellSurface::StateBitset state) const;
104   /**
105    * Calculate size of full surface including decorations given the size of the
106    * main surface and a state
107    */
108   CSizeInt CalculateFullSurfaceSize(CSizeInt mainSurfaceSize, IShellSurface::StateBitset state) const;
109 
110   bool IsDecorationActive() const;
111 
112   void AddSeat(CSeat* seat);
113   void RemoveSeat(CSeat* seat);
114 
115   struct Buffer
116   {
117     void* data{};
118     std::size_t dataSize{};
119     CSizeInt size{};
120     wayland::buffer_t wlBuffer;
121 
BufferBuffer122     Buffer() noexcept {}
123 
BufferBuffer124     Buffer(void* data, std::size_t dataSize, CSizeInt size, wayland::buffer_t&& buffer)
125     : data{data}, dataSize{dataSize}, size{size}, wlBuffer{std::move(buffer)}
126     {}
127 
RgbaBufferBuffer128     std::uint32_t* RgbaBuffer()
129     {
130       return static_cast<std::uint32_t*> (data);
131     }
132   };
133 
134   struct Surface
135   {
136     wayland::surface_t wlSurface;
137     Buffer buffer;
138     CSizeInt size;
139     int scale{1};
140   };
141 
142 private:
143   CWindowDecorator(CWindowDecorator const& other) = delete;
144   CWindowDecorator& operator=(CWindowDecorator const& other) = delete;
145 
146   // IRawInputHandlerTouch
147   void OnTouchDown(CSeat* seat,
148                    std::uint32_t serial,
149                    std::uint32_t time,
150                    const wayland::surface_t& surface,
151                    std::int32_t id,
152                    double x,
153                    double y) override;
154   // IRawInputHandlerPointer
155   void OnPointerEnter(CSeat* seat,
156                       std::uint32_t serial,
157                       const wayland::surface_t& surface,
158                       double surfaceX,
159                       double surfaceY) override;
160   void OnPointerLeave(CSeat* seat,
161                       std::uint32_t serial,
162                       const wayland::surface_t& surface) override;
163   void OnPointerMotion(CSeat* seat, std::uint32_t time, double surfaceX, double surfaceY) override;
164   void OnPointerButton(CSeat* seat, std::uint32_t serial, std::uint32_t time, std::uint32_t button, wayland::pointer_button_state state) override;
165 
166   void Reset(bool reallocate);
167 
168   // These functions should not be called directly as they may leave internal
169   // structures in an inconsistent state when called individually - only call
170   // Reset().
171   void ResetButtons();
172   void ResetSurfaces();
173   void ResetShm();
174   void ReattachSubsurfaces();
175   void PositionButtons();
176   void AllocateBuffers();
177   void Repaint();
178   void CommitAllBuffers();
179 
180   bool StateHasWindowDecorations(IShellSurface::StateBitset state) const;
181 
182   Buffer GetBuffer(CSizeInt size);
183 
184   IWindowDecorationHandler& m_handler;
185 
186   CSizeInt m_mainSurfaceSize;
187   int m_scale{1};
188   IShellSurface::StateBitset m_windowState;
189 
190   CRegistry m_registry;
191   wayland::shm_t m_shm;
192   wayland::shm_pool_t m_shmPool;
193   wayland::compositor_t m_compositor;
194   wayland::subcompositor_t m_subcompositor;
195   wayland::surface_t m_mainSurface;
196 
197   std::unique_ptr<KODI::UTILS::POSIX::CSharedMemory> m_memory;
198   std::size_t m_memoryAllocatedSize{};
199 
200   struct BorderSurface
201   {
202     Surface surface;
203     wayland::subsurface_t subsurface;
204     CRectInt geometry;
205     /// Region of the surface that should count as being part of the window
206     CRectInt windowRect;
207   };
208   BorderSurface MakeBorderSurface();
209 
210   /**
211    * Mutex for all surface/button state that is accessed from the event pump thread via seat events
212    * and the main thread:
213    * m_surfaces, m_buttons, m_windowSize, m_cursorTheme
214    *
215    * If size etc. is changing, mutex should be locked for the entire duration of the
216    * change until the internal state (surface, button size etc.) is consistent again.
217    */
218   CCriticalSection m_mutex;
219 
220   std::array<BorderSurface, 4> m_borderSurfaces;
221 
222   std::set<wayland::buffer_t, WaylandCPtrCompare> m_pendingBuffers;
223   CCriticalSection m_pendingBuffersMutex;
224 
225   struct SeatState
226   {
227     CSeat* seat;
228     SurfaceIndex currentSurface{SURFACE_COUNT};
229     CPoint pointerPosition;
230 
231     std::uint32_t pointerEnterSerial{};
232     std::string cursorName;
233     wayland::surface_t cursor;
234 
SeatStateSeatState235     explicit SeatState(CSeat* seat)
236     : seat{seat}
237     {}
238   };
239   std::map<std::uint32_t, SeatState> m_seats;
240 
241   struct Button
242   {
243     CRectInt position;
244     bool hovered{};
245     std::function<void(Surface&, CRectInt, bool /* hover */)> draw;
246     std::function<void()> onClick;
247   };
248   std::vector<Button> m_buttons;
249 
250   wayland::cursor_theme_t m_cursorTheme;
251   std::uint32_t m_buttonColor;
252 
253   void LoadCursorTheme();
254 
255   void UpdateSeatCursor(SeatState& seatState);
256   void UpdateButtonHoverState();
257   void HandleSeatClick(SeatState const& seatState, SurfaceIndex surface, std::uint32_t serial, std::uint32_t button, CPoint position);
258 };
259 
260 }
261 }
262 }
263