1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h"
6 
7 #include <xdg-shell-client-protocol.h>
8 #include <xdg-shell-unstable-v6-client-protocol.h>
9 
10 #include "base/strings/utf_string_conversions.h"
11 #include "ui/base/hit_test.h"
12 #include "ui/ozone/platform/wayland/common/wayland_util.h"
13 #include "ui/ozone/platform/wayland/host/wayland_connection.h"
14 #include "ui/ozone/platform/wayland/host/wayland_window.h"
15 
16 namespace ui {
17 
XDGSurfaceWrapperImpl(WaylandWindow * wayland_window,WaylandConnection * connection)18 XDGSurfaceWrapperImpl::XDGSurfaceWrapperImpl(WaylandWindow* wayland_window,
19                                              WaylandConnection* connection)
20     : wayland_window_(wayland_window), connection_(connection) {}
21 
~XDGSurfaceWrapperImpl()22 XDGSurfaceWrapperImpl::~XDGSurfaceWrapperImpl() {}
23 
Initialize(bool with_toplevel)24 bool XDGSurfaceWrapperImpl::Initialize(bool with_toplevel) {
25   if (connection_->shell())
26     return InitializeStable(with_toplevel);
27   else if (connection_->shell_v6())
28     return InitializeV6(with_toplevel);
29   NOTREACHED() << "Wrong shell protocol";
30   return false;
31 }
32 
SetMaximized()33 void XDGSurfaceWrapperImpl::SetMaximized() {
34   if (xdg_toplevel_) {
35     xdg_toplevel_set_maximized(xdg_toplevel_.get());
36   } else {
37     DCHECK(zxdg_toplevel_v6_);
38     zxdg_toplevel_v6_set_maximized(zxdg_toplevel_v6_.get());
39   }
40 }
41 
UnSetMaximized()42 void XDGSurfaceWrapperImpl::UnSetMaximized() {
43   if (xdg_toplevel_) {
44     xdg_toplevel_unset_maximized(xdg_toplevel_.get());
45   } else {
46     DCHECK(zxdg_toplevel_v6_);
47     zxdg_toplevel_v6_unset_maximized(zxdg_toplevel_v6_.get());
48   }
49 }
50 
SetFullscreen()51 void XDGSurfaceWrapperImpl::SetFullscreen() {
52   if (xdg_toplevel_) {
53     xdg_toplevel_set_fullscreen(xdg_toplevel_.get(), nullptr);
54   } else {
55     DCHECK(zxdg_toplevel_v6_);
56     zxdg_toplevel_v6_set_fullscreen(zxdg_toplevel_v6_.get(), nullptr);
57   }
58 }
59 
UnSetFullscreen()60 void XDGSurfaceWrapperImpl::UnSetFullscreen() {
61   if (xdg_toplevel_) {
62     xdg_toplevel_unset_fullscreen(xdg_toplevel_.get());
63   } else {
64     DCHECK(zxdg_toplevel_v6_);
65     zxdg_toplevel_v6_unset_fullscreen(zxdg_toplevel_v6_.get());
66   }
67 }
68 
SetMinimized()69 void XDGSurfaceWrapperImpl::SetMinimized() {
70   if (xdg_toplevel_) {
71     xdg_toplevel_set_minimized(xdg_toplevel_.get());
72   } else {
73     DCHECK(zxdg_toplevel_v6_);
74     zxdg_toplevel_v6_set_minimized(zxdg_toplevel_v6_.get());
75   }
76 }
77 
SurfaceMove(WaylandConnection * connection)78 void XDGSurfaceWrapperImpl::SurfaceMove(WaylandConnection* connection) {
79   if (xdg_toplevel_) {
80     xdg_toplevel_move(xdg_toplevel_.get(), connection_->seat(),
81                       connection_->serial());
82   } else {
83     DCHECK(zxdg_toplevel_v6_);
84     zxdg_toplevel_v6_move(zxdg_toplevel_v6_.get(), connection_->seat(),
85                           connection_->serial());
86   }
87 }
88 
SurfaceResize(WaylandConnection * connection,uint32_t hittest)89 void XDGSurfaceWrapperImpl::SurfaceResize(WaylandConnection* connection,
90                                           uint32_t hittest) {
91   if (xdg_toplevel_) {
92     xdg_toplevel_resize(xdg_toplevel_.get(), connection_->seat(),
93                         connection_->serial(),
94                         wl::IdentifyDirection(*connection, hittest));
95   } else {
96     DCHECK(zxdg_toplevel_v6_);
97     zxdg_toplevel_v6_resize(zxdg_toplevel_v6_.get(), connection_->seat(),
98                             connection_->serial(),
99                             wl::IdentifyDirection(*connection, hittest));
100   }
101 }
102 
SetTitle(const base::string16 & title)103 void XDGSurfaceWrapperImpl::SetTitle(const base::string16& title) {
104   if (xdg_toplevel_) {
105     xdg_toplevel_set_title(xdg_toplevel_.get(),
106                            base::UTF16ToUTF8(title).c_str());
107   } else {
108     DCHECK(zxdg_toplevel_v6_);
109     zxdg_toplevel_v6_set_title(zxdg_toplevel_v6_.get(),
110                                base::UTF16ToUTF8(title).c_str());
111   }
112 }
113 
AckConfigure()114 void XDGSurfaceWrapperImpl::AckConfigure() {
115   if (xdg_surface_) {
116     xdg_surface_ack_configure(xdg_surface_.get(), pending_configure_serial_);
117   } else {
118     DCHECK(zxdg_surface_v6_);
119     zxdg_surface_v6_ack_configure(zxdg_surface_v6_.get(),
120                                   pending_configure_serial_);
121   }
122 }
123 
SetWindowGeometry(const gfx::Rect & bounds)124 void XDGSurfaceWrapperImpl::SetWindowGeometry(const gfx::Rect& bounds) {
125   if (xdg_surface_) {
126     xdg_surface_set_window_geometry(xdg_surface_.get(), bounds.x(), bounds.y(),
127                                     bounds.width(), bounds.height());
128   } else {
129     DCHECK(zxdg_surface_v6_);
130     zxdg_surface_v6_set_window_geometry(zxdg_surface_v6_.get(), bounds.x(),
131                                         bounds.y(), bounds.width(),
132                                         bounds.height());
133   }
134 }
135 
SetMinSize(int32_t width,int32_t height)136 void XDGSurfaceWrapperImpl::SetMinSize(int32_t width, int32_t height) {
137   if (xdg_toplevel_) {
138     xdg_toplevel_set_min_size(xdg_toplevel_.get(), width, height);
139   } else {
140     DCHECK(zxdg_toplevel_v6_);
141     zxdg_toplevel_v6_set_min_size(zxdg_toplevel_v6_.get(), width, height);
142   }
143 }
144 
SetMaxSize(int32_t width,int32_t height)145 void XDGSurfaceWrapperImpl::SetMaxSize(int32_t width, int32_t height) {
146   if (xdg_toplevel_) {
147     xdg_toplevel_set_max_size(xdg_toplevel_.get(), width, height);
148   } else {
149     DCHECK(zxdg_toplevel_v6_);
150     zxdg_toplevel_v6_set_max_size(zxdg_toplevel_v6_.get(), width, height);
151   }
152 }
153 
SetAppId(const std::string & app_id)154 void XDGSurfaceWrapperImpl::SetAppId(const std::string& app_id) {
155   if (xdg_toplevel_) {
156     xdg_toplevel_set_app_id(xdg_toplevel_.get(), app_id.c_str());
157   } else {
158     DCHECK(zxdg_toplevel_v6_);
159     zxdg_toplevel_v6_set_app_id(zxdg_toplevel_v6_.get(), app_id.c_str());
160   }
161 }
162 
163 // static
ConfigureStable(void * data,struct xdg_surface * xdg_surface,uint32_t serial)164 void XDGSurfaceWrapperImpl::ConfigureStable(void* data,
165                                             struct xdg_surface* xdg_surface,
166                                             uint32_t serial) {
167   auto* surface = static_cast<XDGSurfaceWrapperImpl*>(data);
168   DCHECK(surface);
169   surface->pending_configure_serial_ = serial;
170 
171   surface->AckConfigure();
172 }
173 
174 // static
ConfigureTopLevelStable(void * data,struct xdg_toplevel * xdg_toplevel,int32_t width,int32_t height,struct wl_array * states)175 void XDGSurfaceWrapperImpl::ConfigureTopLevelStable(
176     void* data,
177     struct xdg_toplevel* xdg_toplevel,
178     int32_t width,
179     int32_t height,
180     struct wl_array* states) {
181   auto* surface = static_cast<XDGSurfaceWrapperImpl*>(data);
182   DCHECK(surface);
183 
184   bool is_maximized =
185       CheckIfWlArrayHasValue(states, XDG_TOPLEVEL_STATE_MAXIMIZED);
186   bool is_fullscreen =
187       CheckIfWlArrayHasValue(states, XDG_TOPLEVEL_STATE_FULLSCREEN);
188   bool is_activated =
189       CheckIfWlArrayHasValue(states, XDG_TOPLEVEL_STATE_ACTIVATED);
190 
191   surface->wayland_window_->HandleSurfaceConfigure(width, height, is_maximized,
192                                                    is_fullscreen, is_activated);
193 }
194 
195 // static
CloseTopLevelStable(void * data,struct xdg_toplevel * xdg_toplevel)196 void XDGSurfaceWrapperImpl::CloseTopLevelStable(
197     void* data,
198     struct xdg_toplevel* xdg_toplevel) {
199   auto* surface = static_cast<XDGSurfaceWrapperImpl*>(data);
200   DCHECK(surface);
201   surface->wayland_window_->OnCloseRequest();
202 }
203 
204 // static
ConfigureV6(void * data,struct zxdg_surface_v6 * zxdg_surface_v6,uint32_t serial)205 void XDGSurfaceWrapperImpl::ConfigureV6(void* data,
206                                         struct zxdg_surface_v6* zxdg_surface_v6,
207                                         uint32_t serial) {
208   auto* surface = static_cast<XDGSurfaceWrapperImpl*>(data);
209   DCHECK(surface);
210   surface->pending_configure_serial_ = serial;
211 
212   surface->AckConfigure();
213 }
214 
215 // static
ConfigureTopLevelV6(void * data,struct zxdg_toplevel_v6 * zxdg_toplevel_v6,int32_t width,int32_t height,struct wl_array * states)216 void XDGSurfaceWrapperImpl::ConfigureTopLevelV6(
217     void* data,
218     struct zxdg_toplevel_v6* zxdg_toplevel_v6,
219     int32_t width,
220     int32_t height,
221     struct wl_array* states) {
222   auto* surface = static_cast<XDGSurfaceWrapperImpl*>(data);
223   DCHECK(surface);
224 
225   bool is_maximized =
226       CheckIfWlArrayHasValue(states, ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED);
227   bool is_fullscreen =
228       CheckIfWlArrayHasValue(states, ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN);
229   bool is_activated =
230       CheckIfWlArrayHasValue(states, ZXDG_TOPLEVEL_V6_STATE_ACTIVATED);
231 
232   surface->wayland_window_->HandleSurfaceConfigure(width, height, is_maximized,
233                                                    is_fullscreen, is_activated);
234 }
235 
236 // static
CloseTopLevelV6(void * data,struct zxdg_toplevel_v6 * zxdg_toplevel_v6)237 void XDGSurfaceWrapperImpl::CloseTopLevelV6(
238     void* data,
239     struct zxdg_toplevel_v6* zxdg_toplevel_v6) {
240   auto* surface = static_cast<XDGSurfaceWrapperImpl*>(data);
241   DCHECK(surface);
242   surface->wayland_window_->OnCloseRequest();
243 }
244 
zxdg_surface() const245 zxdg_surface_v6* XDGSurfaceWrapperImpl::zxdg_surface() const {
246   DCHECK(zxdg_surface_v6_);
247   return zxdg_surface_v6_.get();
248 }
249 
xdg_surface() const250 xdg_surface* XDGSurfaceWrapperImpl::xdg_surface() const {
251   DCHECK(xdg_surface_);
252   return xdg_surface_.get();
253 }
254 
InitializeStable(bool with_toplevel)255 bool XDGSurfaceWrapperImpl::InitializeStable(bool with_toplevel) {
256   static const xdg_surface_listener xdg_surface_listener = {
257       &XDGSurfaceWrapperImpl::ConfigureStable,
258   };
259   static const xdg_toplevel_listener xdg_toplevel_listener = {
260       &XDGSurfaceWrapperImpl::ConfigureTopLevelStable,
261       &XDGSurfaceWrapperImpl::CloseTopLevelStable,
262   };
263 
264   // if this surface is created for the popup role, mark that it requires
265   // configuration acknowledgement on each configure event.
266   surface_for_popup_ = !with_toplevel;
267 
268   xdg_surface_.reset(xdg_wm_base_get_xdg_surface(connection_->shell(),
269                                                  wayland_window_->surface()));
270   if (!xdg_surface_) {
271     LOG(ERROR) << "Failed to create xdg_surface";
272     return false;
273   }
274   xdg_surface_add_listener(xdg_surface_.get(), &xdg_surface_listener, this);
275   // XDGPopup requires a separate surface to be created, so this is just a
276   // request to get an xdg_surface for it.
277   if (surface_for_popup_) {
278     connection_->ScheduleFlush();
279     return true;
280   }
281 
282   xdg_toplevel_.reset(xdg_surface_get_toplevel(xdg_surface_.get()));
283   if (!xdg_toplevel_) {
284     LOG(ERROR) << "Failed to create xdg_toplevel";
285     return false;
286   }
287   xdg_toplevel_add_listener(xdg_toplevel_.get(), &xdg_toplevel_listener, this);
288   wl_surface_commit(wayland_window_->surface());
289 
290   connection_->ScheduleFlush();
291   return true;
292 }
293 
InitializeV6(bool with_toplevel)294 bool XDGSurfaceWrapperImpl::InitializeV6(bool with_toplevel) {
295   static const zxdg_surface_v6_listener zxdg_surface_v6_listener = {
296       &XDGSurfaceWrapperImpl::ConfigureV6,
297   };
298   static const zxdg_toplevel_v6_listener zxdg_toplevel_v6_listener = {
299       &XDGSurfaceWrapperImpl::ConfigureTopLevelV6,
300       &XDGSurfaceWrapperImpl::CloseTopLevelV6,
301   };
302 
303   // if this surface is created for the popup role, mark that it requires
304   // configuration acknowledgement on each configure event.
305   surface_for_popup_ = !with_toplevel;
306 
307   zxdg_surface_v6_.reset(zxdg_shell_v6_get_xdg_surface(
308       connection_->shell_v6(), wayland_window_->surface()));
309   if (!zxdg_surface_v6_) {
310     LOG(ERROR) << "Failed to create zxdg_surface";
311     return false;
312   }
313   zxdg_surface_v6_add_listener(zxdg_surface_v6_.get(),
314                                &zxdg_surface_v6_listener, this);
315   // XDGPopupV6 requires a separate surface to be created, so this is just a
316   // request to get an xdg_surface for it.
317   if (surface_for_popup_) {
318     connection_->ScheduleFlush();
319     return true;
320   }
321 
322   zxdg_toplevel_v6_.reset(zxdg_surface_v6_get_toplevel(zxdg_surface_v6_.get()));
323   if (!zxdg_toplevel_v6_) {
324     LOG(ERROR) << "Failed to create zxdg_toplevel";
325     return false;
326   }
327   zxdg_toplevel_v6_add_listener(zxdg_toplevel_v6_.get(),
328                                 &zxdg_toplevel_v6_listener, this);
329   wl_surface_commit(wayland_window_->surface());
330 
331   connection_->ScheduleFlush();
332   return true;
333 }
334 
335 }  // namespace ui
336