1 /****************************************************************************
2 Copyright © 2020 Roman Gilg <subdiff@gmail.com>
3 
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) version 3, or any
8 later version accepted by the membership of KDE e.V. (or its
9 successor approved by the membership of KDE e.V.), which shall
10 act as a proxy defined in Section 6 of version 3 of the license.
11 
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 Lesser General Public License for more details.
16 
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library.  If not, see <http://www.gnu.org/licenses/>.
19 ****************************************************************************/
20 #include "xdg_decoration.h"
21 
22 #include "display.h"
23 #include "xdg_shell_p.h"
24 
25 #include "wayland/global.h"
26 #include "wayland/resource.h"
27 
28 #include "wayland-xdg-decoration-server-protocol.h"
29 
30 #include <map>
31 
32 namespace Wrapland::Server
33 {
34 
35 constexpr uint32_t XdgDecorationManagerVersion = 1;
36 using XdgDecorationManagerGlobal
37     = Wayland::Global<XdgDecorationManager, XdgDecorationManagerVersion>;
38 using XdgDecorationManagerBind = Wayland::Bind<XdgDecorationManagerGlobal>;
39 
40 class XdgDecorationManager::Private : public XdgDecorationManagerGlobal
41 {
42 public:
43     Private(XdgDecorationManager* q, Display* display, XdgShell* shell);
44 
45     std::map<XdgShellToplevel*, XdgDecoration*> m_decorations;
46 
47 private:
48     static void getToplevelDecorationCallback(XdgDecorationManagerBind* bind,
49                                               uint32_t id,
50                                               wl_resource* wlToplevel);
51 
52     XdgShell* m_shell;
53 
54     static const struct zxdg_decoration_manager_v1_interface s_interface;
55 };
56 
Private(XdgDecorationManager * q,Display * display,XdgShell * shell)57 XdgDecorationManager::Private::Private(XdgDecorationManager* q, Display* display, XdgShell* shell)
58     : Wayland::Global<XdgDecorationManager>(q,
59                                             display,
60                                             &zxdg_decoration_manager_v1_interface,
61                                             &s_interface)
62     , m_shell{shell}
63 {
64 }
65 
66 const struct zxdg_decoration_manager_v1_interface XdgDecorationManager::Private::s_interface = {
67     resourceDestroyCallback,
68     cb<getToplevelDecorationCallback>,
69 };
70 
getToplevelDecorationCallback(XdgDecorationManagerBind * bind,uint32_t id,wl_resource * wlToplevel)71 void XdgDecorationManager::Private::getToplevelDecorationCallback(XdgDecorationManagerBind* bind,
72                                                                   uint32_t id,
73                                                                   wl_resource* wlToplevel)
74 {
75     auto priv = bind->global()->handle()->d_ptr.get();
76 
77     auto toplevel = priv->m_shell->d_ptr->getToplevel(wlToplevel);
78     if (!toplevel) {
79         bind->post_error(ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ORPHANED, "No xdg-toplevel found.");
80         return;
81     }
82     if (priv->m_decorations.count(toplevel) > 0) {
83         bind->post_error(ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ALREADY_CONSTRUCTED,
84                          "xdg decoration already created for this xdg-toplevel.");
85         return;
86     }
87 
88     auto deco = new XdgDecoration(bind->client()->handle(), bind->version(), id, toplevel);
89     // TODO(romangg): check resource
90 
91     priv->m_decorations[toplevel] = deco;
92     QObject::connect(deco, &XdgDecoration::resourceDestroyed, priv->handle(), [toplevel, priv]() {
93         priv->m_decorations.erase(toplevel);
94     });
95     Q_EMIT priv->handle()->decorationCreated(deco);
96 }
97 
XdgDecorationManager(Display * display,XdgShell * shell,QObject * parent)98 XdgDecorationManager::XdgDecorationManager(Display* display, XdgShell* shell, QObject* parent)
99     : QObject(parent)
100     , d_ptr(new Private(this, display, shell))
101 {
102     d_ptr->create();
103 }
104 
105 XdgDecorationManager::~XdgDecorationManager() = default;
106 
107 class XdgDecoration::Private : public Wayland::Resource<XdgDecoration>
108 {
109 public:
110     Private(Client* client,
111             uint32_t version,
112             uint32_t id,
113             XdgShellToplevel* toplevel,
114             XdgDecoration* q);
115 
116     XdgDecoration::Mode m_requestedMode = XdgDecoration::Mode::Undefined;
117     XdgShellToplevel* toplevel;
118 
119 private:
120     static void setModeCallback(wl_client* wlClient, wl_resource* wlResource, uint32_t wlMode);
121     static void unsetModeCallback(wl_client* wlClient, wl_resource* wlResource);
122 
123     static const struct zxdg_toplevel_decoration_v1_interface s_interface;
124 };
125 
Private(Client * client,uint32_t version,uint32_t id,XdgShellToplevel * toplevel,XdgDecoration * q)126 XdgDecoration::Private::Private(Client* client,
127                                 uint32_t version,
128                                 uint32_t id,
129                                 XdgShellToplevel* toplevel,
130                                 XdgDecoration* q)
131     : Wayland::Resource<XdgDecoration>(client,
132                                        version,
133                                        id,
134                                        &zxdg_toplevel_decoration_v1_interface,
135                                        &s_interface,
136                                        q)
137     , toplevel{toplevel}
138 {
139 }
140 
141 const struct zxdg_toplevel_decoration_v1_interface XdgDecoration::Private::s_interface = {
142     destroyCallback,
143     setModeCallback,
144     unsetModeCallback,
145 };
146 
setModeCallback(wl_client * wlClient,wl_resource * wlResource,uint32_t wlMode)147 void XdgDecoration::Private::setModeCallback([[maybe_unused]] wl_client* wlClient,
148                                              wl_resource* wlResource,
149                                              uint32_t wlMode)
150 {
151     auto priv = handle(wlResource)->d_ptr;
152 
153     Mode mode = Mode::Undefined;
154     switch (wlMode) {
155     case ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE:
156         mode = Mode::ClientSide;
157         break;
158     case ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE:
159         mode = Mode::ServerSide;
160         break;
161     default:
162         break;
163     }
164 
165     priv->m_requestedMode = mode;
166     Q_EMIT priv->handle()->modeRequested();
167 }
168 
unsetModeCallback(wl_client * wlClient,wl_resource * wlResource)169 void XdgDecoration::Private::unsetModeCallback([[maybe_unused]] wl_client* wlClient,
170                                                wl_resource* wlResource)
171 {
172     auto priv = handle(wlResource)->d_ptr;
173 
174     priv->m_requestedMode = Mode::Undefined;
175     Q_EMIT priv->handle()->modeRequested();
176 }
177 
XdgDecoration(Client * client,uint32_t version,uint32_t id,XdgShellToplevel * toplevel)178 XdgDecoration::XdgDecoration(Client* client,
179                              uint32_t version,
180                              uint32_t id,
181                              XdgShellToplevel* toplevel)
182     : QObject(nullptr)
183     , d_ptr(new Private(client, version, id, toplevel, this))
184 {
185 }
186 
configure(XdgDecoration::Mode mode)187 void XdgDecoration::configure(XdgDecoration::Mode mode)
188 {
189     switch (mode) {
190     case Mode::ClientSide:
191         d_ptr->send<zxdg_toplevel_decoration_v1_send_configure>(
192             ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE);
193         break;
194     case Mode::ServerSide:
195         d_ptr->send<zxdg_toplevel_decoration_v1_send_configure>(
196             ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
197         break;
198     default:
199         break;
200     }
201 }
202 
requestedMode() const203 XdgDecoration::Mode XdgDecoration::requestedMode() const
204 {
205     return d_ptr->m_requestedMode;
206 }
207 
toplevel() const208 XdgShellToplevel* XdgDecoration::toplevel() const
209 {
210     return d_ptr->toplevel;
211 }
212 
213 }
214