Directory: | ./ |
---|---|
File: | server/xdg_shell_surface.cpp |
Date: | 2024-01-22 17:25:27 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 90 | 111 | 81.1% |
Branches: | 34 | 52 | 65.4% |
Line | Branch | Exec | Source |
---|---|---|---|
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_shell_surface.h" | ||
21 | #include "xdg_shell_surface_p.h" | ||
22 | |||
23 | #include "surface_p.h" | ||
24 | #include "xdg_shell.h" | ||
25 | #include "xdg_shell_p.h" | ||
26 | #include "xdg_shell_popup.h" | ||
27 | #include "xdg_shell_popup_p.h" | ||
28 | #include "xdg_shell_positioner_p.h" | ||
29 | #include "xdg_shell_toplevel.h" | ||
30 | #include "xdg_shell_toplevel_p.h" | ||
31 | |||
32 | #include "wayland/resource.h" | ||
33 | |||
34 | namespace Wrapland::Server | ||
35 | { | ||
36 | |||
37 | const struct xdg_surface_interface XdgShellSurface::Private::s_interface = { | ||
38 | destroyCallback, | ||
39 | getTopLevelCallback, | ||
40 | getPopupCallback, | ||
41 | setWindowGeometryCallback, | ||
42 | ackConfigureCallback, | ||
43 | }; | ||
44 | |||
45 |
2/4✓ Branch 0 taken 81 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 81 times.
✗ Branch 3 not taken.
|
162 | XdgShellSurface::Private::Private(Client* client, |
46 | uint32_t version, | ||
47 | uint32_t id, | ||
48 | XdgShell* shell, | ||
49 | Surface* surface, | ||
50 | XdgShellSurface* q_ptr) | ||
51 | 162 | : Wayland::Resource<XdgShellSurface>(client, | |
52 | 81 | version, | |
53 | 81 | id, | |
54 | &xdg_surface_interface, | ||
55 | &s_interface, | ||
56 | 81 | q_ptr) | |
57 | 81 | , m_shell(shell) | |
58 | 81 | , m_surface(surface) | |
59 | 162 | { | |
60 | 81 | } | |
61 | |||
62 | 71 | void XdgShellSurface::Private::getTopLevelCallback([[maybe_unused]] wl_client* wlClient, | |
63 | wl_resource* wlResource, | ||
64 | uint32_t id) | ||
65 | { | ||
66 | 71 | auto priv = get_handle(wlResource)->d_ptr; | |
67 | |||
68 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 69 times.
|
71 | if (!priv->check_creation_error()) { |
69 | 2 | return; | |
70 | } | ||
71 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 69 times.
|
69 | auto topLevel = new XdgShellToplevel(priv->version, id, priv->handle); |
72 | 69 | priv->toplevel = topLevel; | |
73 | |||
74 | 69 | priv->m_surface->d_ptr->shellSurface = priv->handle; | |
75 | 138 | QObject::connect(topLevel, | |
76 | &XdgShellToplevel::resourceDestroyed, | ||
77 | 69 | priv->m_surface, | |
78 | 71 | [surface = priv->m_surface] { surface->d_ptr->shellSurface = nullptr; }); | |
79 | |||
80 | 69 | Q_EMIT priv->m_shell->toplevelCreated(topLevel); | |
81 | 71 | } | |
82 | |||
83 | 12 | void XdgShellSurface::Private::getPopupCallback([[maybe_unused]] wl_client* wlClient, | |
84 | wl_resource* wlResource, | ||
85 | uint32_t id, | ||
86 | wl_resource* wlParent, | ||
87 | wl_resource* wlPositioner) | ||
88 | { | ||
89 | 12 | auto priv = get_handle(wlResource)->d_ptr; | |
90 | |||
91 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 11 times.
|
12 | if (!priv->check_creation_error()) { |
92 | 1 | return; | |
93 | } | ||
94 | |||
95 | 11 | auto positioner = priv->m_shell->d_ptr->getPositioner(wlPositioner); | |
96 |
1/2✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
|
11 | if (!positioner) { |
97 | ✗ | priv->postError(XDG_WM_BASE_ERROR_INVALID_POSITIONER, "Invalid positioner"); | |
98 | ✗ | return; | |
99 | } | ||
100 | |||
101 | // TODO(romangg): Allow to set parent surface via side-channel (see protocol description). | ||
102 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 1 times.
|
11 | auto parent = wlParent ? priv->m_shell->d_ptr->getSurface(wlParent) : nullptr; |
103 |
3/4✓ Branch 0 taken 10 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
|
11 | if (wlParent && !parent) { |
104 | ✗ | priv->postError(XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT, "Invalid popup parent"); | |
105 | ✗ | return; | |
106 | } | ||
107 | |||
108 |
1/2✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
|
11 | auto popup = new XdgShellPopup(priv->version, id, priv->handle, parent); |
109 | |||
110 | 11 | popup->d_ptr->parent = parent; | |
111 | 11 | popup->d_ptr->positioner = positioner->get_data(); | |
112 | |||
113 | 11 | priv->popup = popup; | |
114 | |||
115 | 11 | priv->m_surface->d_ptr->shellSurface = priv->handle; | |
116 | 22 | QObject::connect(popup, | |
117 | &XdgShellPopup::resourceDestroyed, | ||
118 | 11 | priv->m_surface, | |
119 | 11 | [surface = priv->m_surface] { surface->d_ptr->shellSurface = nullptr; }); | |
120 | |||
121 | 11 | Q_EMIT priv->m_shell->popupCreated(popup); | |
122 | 12 | } | |
123 | |||
124 | 2 | void XdgShellSurface::Private::setWindowGeometryCallback([[maybe_unused]] wl_client* wlClient, | |
125 | wl_resource* wlResource, | ||
126 | int32_t pos_x, | ||
127 | int32_t pos_y, | ||
128 | int32_t width, | ||
129 | int32_t height) | ||
130 | { | ||
131 | 2 | auto priv = get_handle(wlResource)->d_ptr; | |
132 | |||
133 |
3/4✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
2 | if (!priv->toplevel && !priv->popup) { |
134 | ✗ | priv->postError(XDG_SURFACE_ERROR_NOT_CONSTRUCTED, "No role object constructed."); | |
135 | ✗ | return; | |
136 | } | ||
137 | |||
138 |
2/4✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
2 | if (width < 0 || height < 0) { |
139 | ✗ | priv->postError(XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE, | |
140 | "Tried to set invalid xdg-surface geometry"); | ||
141 | ✗ | return; | |
142 | } | ||
143 | |||
144 | 2 | priv->pending_state.window_geometry = QRect(pos_x, pos_y, width, height); | |
145 | 2 | priv->pending_state.window_geometry_set = true; | |
146 | 2 | } | |
147 | |||
148 | 30 | void XdgShellSurface::Private::ackConfigureCallback([[maybe_unused]] wl_client* wlClient, | |
149 | wl_resource* wlResource, | ||
150 | uint32_t serial) | ||
151 | { | ||
152 | 30 | auto priv = get_handle(wlResource)->d_ptr; | |
153 | |||
154 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
30 | if (!priv->toplevel && !priv->popup) { |
155 | ✗ | priv->postError(XDG_SURFACE_ERROR_NOT_CONSTRUCTED, "No role object constructed."); | |
156 | ✗ | return; | |
157 | } | ||
158 | |||
159 |
1/2✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
|
30 | if (priv->toplevel) { |
160 | 30 | priv->toplevel->d_ptr->ackConfigure(serial); | |
161 |
0/2✗ Branch 0 not taken.
✗ Branch 1 not taken.
|
30 | } else if (priv->popup) { |
162 | ✗ | priv->popup->d_ptr->ackConfigure(serial); | |
163 | } | ||
164 | 30 | } | |
165 | |||
166 | 83 | bool XdgShellSurface::Private::check_creation_error() | |
167 | { | ||
168 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 81 times.
|
83 | if (m_surface->d_ptr->has_role()) { |
169 | 2 | postError(XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED, "Surface already has a role."); | |
170 | 2 | return false; | |
171 | } | ||
172 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 80 times.
|
81 | if (m_surface->d_ptr->had_buffer_attached) { |
173 | 1 | postError(XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED, | |
174 | "Creation after a buffer was already attached."); | ||
175 | 1 | return false; | |
176 | } | ||
177 | 80 | return true; | |
178 | 83 | } | |
179 | |||
180 | 81 | XdgShellSurface::XdgShellSurface(Client* client, | |
181 | uint32_t version, | ||
182 | uint32_t id, | ||
183 | XdgShell* shell, | ||
184 | Surface* surface) | ||
185 | 81 | : QObject(nullptr) | |
186 |
2/4✓ Branch 0 taken 81 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 81 times.
|
81 | , d_ptr(new Private(client, version, id, shell, surface, this)) |
187 | 81 | { | |
188 | 81 | } | |
189 | |||
190 | 1 | bool XdgShellSurface::configurePending() const | |
191 | { | ||
192 | 1 | return !d_ptr->configureSerials.empty(); | |
193 | } | ||
194 | |||
195 | 23 | Surface* XdgShellSurface::surface() const | |
196 | { | ||
197 | 23 | return d_ptr->m_surface; | |
198 | } | ||
199 | |||
200 | 9 | void XdgShellSurface::commit() | |
201 | { | ||
202 | 9 | auto const geo_set = d_ptr->pending_state.window_geometry_set; | |
203 | |||
204 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 2 times.
|
9 | if (geo_set) { |
205 | 2 | d_ptr->current_state.window_geometry = d_ptr->pending_state.window_geometry; | |
206 | 2 | d_ptr->current_state.window_geometry_set = true; | |
207 | 2 | } | |
208 | |||
209 | 9 | d_ptr->pending_state = Private::state{}; | |
210 | |||
211 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 7 times.
|
9 | if (d_ptr->toplevel) { |
212 | 7 | d_ptr->toplevel->d_ptr->commit(); | |
213 | 7 | } | |
214 | |||
215 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 2 times.
|
9 | if (geo_set) { |
216 | 2 | Q_EMIT window_geometry_changed(d_ptr->current_state.window_geometry); | |
217 | 2 | } | |
218 | 9 | } | |
219 | |||
220 | 5 | QRect XdgShellSurface::window_geometry() const | |
221 | { | ||
222 | 5 | auto const bounds_geo = surface()->expanse(); | |
223 | |||
224 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | if (!d_ptr->current_state.window_geometry_set) { |
225 | ✗ | return bounds_geo; | |
226 | } | ||
227 | |||
228 | 5 | return d_ptr->current_state.window_geometry.intersected(bounds_geo); | |
229 | 5 | } | |
230 | |||
231 | ✗ | QMargins XdgShellSurface::window_margins() const | |
232 | { | ||
233 | ✗ | auto const window_geo = window_geometry(); | |
234 | |||
235 | ✗ | QMargins margins; | |
236 | |||
237 | ✗ | margins.setLeft(window_geo.left()); | |
238 | ✗ | margins.setTop(window_geo.top()); | |
239 | |||
240 | ✗ | auto const surface_size = surface()->size(); | |
241 | |||
242 | ✗ | margins.setRight(surface_size.width() - window_geo.right() - 1); | |
243 | ✗ | margins.setBottom(surface_size.height() - window_geo.bottom() - 1); | |
244 | |||
245 | ✗ | return margins; | |
246 | } | ||
247 | |||
248 | } | ||
249 |