summaryrefslogtreecommitdiffstats
path: root/xbmc/windowing/wayland/Registry.h
blob: f44078226604cd3894b48dc5e4f6ce766f501fc3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/*
 *  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 <cstdint>
#include <functional>
#include <map>
#include <utility>

#include <wayland-client-protocol.hpp>

namespace KODI
{
namespace WINDOWING
{
namespace WAYLAND
{

/**
 * Handle Wayland globals
 *
 * Request singletons (bound once) with \ref RequestSingleton, non-singletons
 * such as wl_output with \ref Request, then call \ref Bind once.
 *
 * Make sure to destroy all registries before destroying the \ref CConnection.
 */
class CRegistry
{
public:
  explicit CRegistry(CConnection& connection);

  /**
   * Request a static singleton global to be bound to a proxy
   *
   * You should only use this if the singleton is announced at registry bind time
   * (not dynamically) and you do not need to catch events that are sent immediately
   * in response to the bind. Use \ref Request in that case, even if there is
   * ever only one instance of the object at maximum.
   *
   * Cannot be called after \ref Bind has been called.
   *
   * \param target target of waylandpp proxy type
   * \param minVersion minimum version to bind
   * \param maxVersion maximum version to bind
   * \param required whether to throw an exception when the bind cannot be satisfied
   *                 by the compositor - exception is thrown in \ref Bind
   */
  template<typename T>
  void RequestSingleton(T& target, std::uint32_t minVersion, std::uint32_t maxVersion, bool required = true)
  {
    RequestSingletonInternal(target, T::interface_name, minVersion, maxVersion, required);
  }
  using AddHandler = std::function<void(std::uint32_t /* name */, wayland::proxy_t&& /* object */)>;
  using RemoveHandler = std::function<void(std::uint32_t) /* name */>;
  /**
   * Request a callback when a dynamic global appears or disappears
   *
   * The callbacks may be called from the thread that calls \ref Bind or the
   * global Wayland message pump thread during \ref Bind (but never at the same
   * time) and only from the global thread after \ref Bind returns.
   *
   * Events that occur immediately upon binding are only delivered reliably
   * if \ref Bind is called from the Wayland message pump thread.
   *
   * Cannot be called after \ref Bind has been called.
   *
   * \param minVersion minimum version to bind
   * \param maxVersion maximum version to bind
   * \param addHandler function that is called when a global of the requested
   *                   type is added
   * \param removeHandler function that is called when a global of the requested
   *                      type is removed
   */
  template<typename T>
  void Request(std::uint32_t minVersion,
               std::uint32_t maxVersion,
               const AddHandler& addHandler,
               const RemoveHandler& removeHandler)
  {
    RequestInternal([]{ return T(); }, T::interface_name, minVersion, maxVersion, addHandler, removeHandler);
  }

  /**
   * Create a registry object at the compositor and do an roundtrip to bind
   * objects
   *
   * This function blocks until the initial roundtrip is complete. All statically
   * requested singletons that were available will be bound then.
   *
   * Neither statically nor dynamically requested proxies will be bound before this
   * function is called.
   *
   * May throw std::runtime_error if a required global was not found.
   *
   * Can only be called once for the same \ref CRegistry object.
   */
  void Bind();
  /**
   * Unbind all singletons requested with \ref RequestSingleton
   */
  void UnbindSingletons();

private:
  CRegistry(CRegistry const& other) = delete;
  CRegistry& operator=(CRegistry const& other) = delete;

  void RequestSingletonInternal(wayland::proxy_t& target, std::string const& interfaceName, std::uint32_t minVersion, std::uint32_t maxVersion, bool required);
  void RequestInternal(std::function<wayland::proxy_t()> constructor, std::string const& interfaceName, std::uint32_t minVersion, std::uint32_t maxVersion, AddHandler addHandler, RemoveHandler removeHandler);
  void CheckRequired();

  CConnection& m_connection;
  wayland::registry_t m_registry;

  struct SingletonBindInfo
  {
    wayland::proxy_t& target;
    // Throw exception if trying to bind below this version and required
    std::uint32_t minVersion;
    // Limit bind version to the minimum of this and compositor version
    std::uint32_t maxVersion;
    bool required;
    SingletonBindInfo(wayland::proxy_t& target, std::uint32_t minVersion, std::uint32_t maxVersion, bool required)
    : target{target}, minVersion{minVersion}, maxVersion{maxVersion}, required{required}
    {}
  };
  std::map<std::string, SingletonBindInfo> m_singletonBinds;

  struct BindInfo
  {
    std::function<wayland::proxy_t()> constructor;
    std::uint32_t minVersion;
    std::uint32_t maxVersion;
    AddHandler addHandler;
    RemoveHandler removeHandler;
    BindInfo(std::function<wayland::proxy_t()> constructor,
             std::uint32_t minVersion,
             std::uint32_t maxVersion,
             AddHandler addHandler,
             RemoveHandler removeHandler)
      : constructor{std::move(constructor)},
        minVersion{minVersion},
        maxVersion{maxVersion},
        addHandler{std::move(addHandler)},
        removeHandler{std::move(removeHandler)}
    {}
  };
  std::map<std::string, BindInfo> m_binds;

  std::map<std::uint32_t, std::reference_wrapper<BindInfo>> m_boundNames;
};

}
}
}