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