/* * Copyright (C) 2017-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 "Connection.h" #include "Registry.h" #include "Seat.h" #include "ShellSurface.h" #include "Util.h" #include "WindowDecorationHandler.h" #include "threads/CriticalSection.h" #include "utils/Geometry.h" #include "platform/posix/utils/SharedMemory.h" #include #include #include #include #include namespace KODI { namespace WINDOWING { namespace WAYLAND { enum SurfaceIndex { SURFACE_TOP = 0, SURFACE_RIGHT, SURFACE_BOTTOM, SURFACE_LEFT, SURFACE_COUNT }; /** * Paint decorations around windows with subcompositing * * With Wayland, applications are responsible for drawing borders on their windows * themselves (client-side decorations). To keep the impact on the overall architecture * low, the Wayland platform implementation uses this very simple renderer to * build ugly but fully functional decorations around the Kodi window. Since Kodi as a * media center is usually not used in windowed mode anyway (except maybe for * development), the impact of the visually not-so-pleasing decorations should * be limited. Nice decorations would require more effort and external libraries for * proper 2D drawing. * * If more platforms decide that client-side decorations would be a good idea to * implement, the decorations could also be integrated with the Kodi skin system. * Then this class could be removed. * * The decorations are positioned around the main surface automatically. */ class CWindowDecorator final : IRawInputHandlerTouch, IRawInputHandlerPointer { public: /** * Construct window decorator * \param handler handler for window decoration events * \param connection connection to get Wayland globals * \param mainSurface main surface that decorations are constructed around * \param windowSize full size of the window including decorations * \param scale scale to use for buffers * \param state surface state for adjusting decoration appearance */ CWindowDecorator(IWindowDecorationHandler& handler, CConnection& connection, wayland::surface_t const& mainSurface); /** * Set decoration state and size by providing full surface size including decorations * * Calculates size of the main surface from size of all surfaces combined (including * window decorations) by subtracting the decoration size * * Decorations will be disabled if state includes STATE_FULLSCREEN * * Call only from main thread */ void SetState(CSizeInt size, int scale, IShellSurface::StateBitset state); /** * Get calculated size of main surface */ CSizeInt GetMainSurfaceSize() const { return m_mainSurfaceSize; } /** * Get full geometry of the window, including decorations if active */ CRectInt GetWindowGeometry() const; /** * Calculate size of main surface given the size of the full area * including decorations and a state */ CSizeInt CalculateMainSurfaceSize(CSizeInt size, IShellSurface::StateBitset state) const; /** * Calculate size of full surface including decorations given the size of the * main surface and a state */ CSizeInt CalculateFullSurfaceSize(CSizeInt mainSurfaceSize, IShellSurface::StateBitset state) const; bool IsDecorationActive() const; void AddSeat(CSeat* seat); void RemoveSeat(CSeat* seat); struct Buffer { void* data{}; std::size_t dataSize{}; CSizeInt size{}; wayland::buffer_t wlBuffer; Buffer() noexcept {} Buffer(void* data, std::size_t dataSize, CSizeInt size, wayland::buffer_t&& buffer) : data{data}, dataSize{dataSize}, size{size}, wlBuffer{std::move(buffer)} {} std::uint32_t* RgbaBuffer() { return static_cast (data); } }; struct Surface { wayland::surface_t wlSurface; Buffer buffer; CSizeInt size; int scale{1}; }; 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, const wayland::surface_t& surface, std::int32_t id, double x, double y) override; // IRawInputHandlerPointer void OnPointerEnter(CSeat* seat, std::uint32_t serial, const wayland::surface_t& surface, double surfaceX, double surfaceY) override; void OnPointerLeave(CSeat* seat, std::uint32_t serial, const 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 // structures in an inconsistent state when called individually - only call // Reset(). void ResetButtons(); void ResetSurfaces(); void ResetShm(); void ReattachSubsurfaces(); void PositionButtons(); void AllocateBuffers(); void Repaint(); void CommitAllBuffers(); bool StateHasWindowDecorations(IShellSurface::StateBitset state) const; Buffer GetBuffer(CSizeInt size); IWindowDecorationHandler& m_handler; CSizeInt m_mainSurfaceSize; int m_scale{1}; IShellSurface::StateBitset m_windowState; CRegistry m_registry; wayland::shm_t m_shm; wayland::shm_pool_t m_shmPool; wayland::compositor_t m_compositor; wayland::subcompositor_t m_subcompositor; wayland::surface_t m_mainSurface; std::unique_ptr m_memory; std::size_t m_memoryAllocatedSize{}; struct BorderSurface { Surface surface; wayland::subsurface_t subsurface; CRectInt geometry; /// Region of the surface that should count as being part of the window CRectInt windowRect; }; BorderSurface MakeBorderSurface(); /** * Mutex for all surface/button state that is accessed from the event pump thread via seat events * and the main thread: * m_surfaces, m_buttons, m_windowSize, m_cursorTheme * * If size etc. is changing, mutex should be locked for the entire duration of the * change until the internal state (surface, button size etc.) is consistent again. */ CCriticalSection m_mutex; std::array m_borderSurfaces; std::set m_pendingBuffers; CCriticalSection m_pendingBuffersMutex; struct SeatState { CSeat* seat; SurfaceIndex currentSurface{SURFACE_COUNT}; CPoint pointerPosition; std::uint32_t pointerEnterSerial{}; std::string cursorName; wayland::surface_t cursor; explicit SeatState(CSeat* seat) : seat{seat} {} }; std::map m_seats; struct Button { CRectInt position; bool hovered{}; std::function draw; std::function onClick; }; std::vector