1 /********************************************************************
2 Copyright 2020  Adrien Faveraux <ad1rie3@hotmail.fr>
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 #pragma once
21 
22 #include "shadow.h"
23 
24 #include <QMarginsF>
25 #include <QObject>
26 
27 #include <cassert>
28 #include <type_traits>
29 #include <wayland-shadow-server-protocol.h>
30 
31 #include <wayland-server.h>
32 
33 namespace Wrapland::Server
34 {
35 
36 class Buffer;
37 class Display;
38 
39 constexpr uint32_t ShadowManagerVersion = 2;
40 using ShadowManagerGlobal = Wayland::Global<ShadowManager, ShadowManagerVersion>;
41 using ShadowManagerBind = Wayland::Bind<ShadowManagerGlobal>;
42 
43 class ShadowManager::Private : public ShadowManagerGlobal
44 {
45 public:
46     Private(Display* display, ShadowManager* qptr);
47 
48 private:
49     static void createCallback(ShadowManagerBind* bind, uint32_t id, wl_resource* surface);
50     static void unsetCallback(ShadowManagerBind* bind, wl_resource* surface);
51 
52     static const struct org_kde_kwin_shadow_manager_interface s_interface;
53 };
54 
55 class Shadow::Private : public Wayland::Resource<Shadow>
56 {
57 public:
58     Private(Client* client, uint32_t version, uint32_t id, Shadow* q);
59     ~Private() override;
60 
61     enum class AttachSide {
62         Left,
63         TopLeft,
64         Top,
65         TopRight,
66         Right,
67         BottomRight,
68         Bottom,
69         BottomLeft,
70     };
71 
72     enum class OffsetSide {
73         Left,
74         Top,
75         Right,
76         Bottom,
77     };
78 
79     struct State {
80         template<AttachSide side>
getState81         std::shared_ptr<Buffer>& get()
82         {
83             if constexpr (side == AttachSide::Left) {
84                 return left;
85             } else if constexpr (side == AttachSide::TopLeft) {
86                 return topLeft;
87             } else if constexpr (side == AttachSide::Top) {
88                 return top;
89             } else if constexpr (side == AttachSide::TopRight) {
90                 return topRight;
91             } else if constexpr (side == AttachSide::Right) {
92                 return right;
93             } else if constexpr (side == AttachSide::BottomRight) {
94                 return bottomRight;
95             } else if constexpr (side == AttachSide::Bottom) {
96                 return bottom;
97             } else {
98                 static_assert(side == AttachSide::BottomLeft);
99                 return bottomLeft;
100             }
101         }
102 
103         // We need this for our QObject connections. Once we use a signal system with good template
104         // support it can be replaced by above templated getter.
getState105         std::shared_ptr<Buffer>& get(AttachSide side)
106         {
107             if (side == AttachSide::Left) {
108                 return left;
109             }
110             if (side == AttachSide::TopLeft) {
111                 return topLeft;
112             }
113             if (side == AttachSide::Top) {
114                 return top;
115             }
116             if (side == AttachSide::TopRight) {
117                 return topRight;
118             }
119             if (side == AttachSide::Right) {
120                 return right;
121             }
122             if (side == AttachSide::BottomRight) {
123                 return bottomRight;
124             }
125             if (side == AttachSide::Bottom) {
126                 return bottom;
127             }
128             assert(side == AttachSide::BottomLeft);
129             return bottomLeft;
130         }
131 
132         template<OffsetSide side>
setOffsetState133         void setOffset(double _offset)
134         {
135             if constexpr (side == OffsetSide::Left) {
136                 offset.setLeft(_offset);
137             } else if constexpr (side == OffsetSide::Top) {
138                 offset.setTop(_offset);
139             } else if constexpr (side == OffsetSide::Right) {
140                 offset.setRight(_offset);
141             } else {
142                 static_assert(side == OffsetSide::Bottom);
143                 offset.setBottom(_offset);
144             }
145             offsetIsSet = true;
146         }
147 
148         template<AttachSide side>
commitState149         void commit(State& pending)
150         {
151             auto& currentBuf = get<side>();
152             auto& pendingBuf = pending.get<side>();
153 
154             currentBuf = pendingBuf;
155             pendingBuf.reset();
156         }
157 
158         template<AttachSide side>
unrefState159         void unref()
160         {
161             if (auto buf = get<side>()) {
162                 buf.reset();
163             }
164         }
165 
166         // TODO(romangg): unique_ptr?
167         std::shared_ptr<Buffer> left = nullptr;
168         std::shared_ptr<Buffer> topLeft = nullptr;
169         std::shared_ptr<Buffer> top = nullptr;
170         std::shared_ptr<Buffer> topRight = nullptr;
171         std::shared_ptr<Buffer> right = nullptr;
172         std::shared_ptr<Buffer> bottomRight = nullptr;
173         std::shared_ptr<Buffer> bottom = nullptr;
174         std::shared_ptr<Buffer> bottomLeft = nullptr;
175 
176         QMarginsF offset;
177         bool offsetIsSet = false;
178     };
179     State current;
180     State pending;
181 
182 private:
183     template<AttachSide side>
attachCallback(wl_client * wlClient,wl_resource * wlResource,wl_resource * wlBuffer)184     static void attachCallback([[maybe_unused]] wl_client* wlClient,
185                                wl_resource* wlResource,
186                                wl_resource* wlBuffer)
187     {
188         auto priv = handle(wlResource)->d_ptr;
189         priv->attach<side>(wlBuffer);
190     }
191 
192     template<OffsetSide side>
offsetCallback(wl_client * wlClient,wl_resource * wlResource,wl_fixed_t wlOffset)193     static void offsetCallback([[maybe_unused]] wl_client* wlClient,
194                                wl_resource* wlResource,
195                                wl_fixed_t wlOffset)
196     {
197         auto priv = handle(wlResource)->d_ptr;
198         priv->pending.setOffset<side>(wl_fixed_to_double(wlOffset));
199     }
200 
201     static void commitCallback(wl_client* client, wl_resource* resource);
202 
203     template<AttachSide side>
attach(wl_resource * wlBuffer)204     void attach(wl_resource* wlBuffer)
205     {
206         auto display = client()->display()->handle();
207         auto buffer = Buffer::get(display, wlBuffer);
208 
209         attachConnect(side, buffer.get());
210         pending.get<side>() = buffer;
211     }
212 
213     void attachConnect(AttachSide side, Buffer* buffer);
214     void commit();
215 
216     static const struct org_kde_kwin_shadow_interface s_interface;
217 };
218 
219 }
220