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