1 // Copyright 2018 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 "components/exo/wayland/zxdg_shell.h"
6 
7 #include <wayland-server-core.h>
8 #include <wayland-server-protocol-core.h>
9 #include <xdg-shell-unstable-v6-server-protocol.h>
10 
11 #include "ash/public/cpp/shell_window_ids.h"
12 #include "ash/public/cpp/window_properties.h"
13 #include "ash/public/cpp/window_state_type.h"
14 #include "base/bind.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "components/exo/display.h"
17 #include "components/exo/wayland/serial_tracker.h"
18 #include "components/exo/wayland/server_util.h"
19 #include "components/exo/wayland/wayland_positioner.h"
20 #include "components/exo/xdg_shell_surface.h"
21 #include "ui/aura/window_observer.h"
22 #include "ui/base/hit_test.h"
23 #include "ui/display/screen.h"
24 #include "ui/views/widget/widget.h"
25 #include "ui/wm/core/coordinate_conversion.h"
26 
27 namespace exo {
28 namespace wayland {
29 namespace {
30 
31 ////////////////////////////////////////////////////////////////////////////////
32 // xdg_positioner_interface:
33 
xdg_positioner_v6_destroy(wl_client * client,wl_resource * resource)34 void xdg_positioner_v6_destroy(wl_client* client, wl_resource* resource) {
35   wl_resource_destroy(resource);
36 }
37 
xdg_positioner_v6_set_size(wl_client * client,wl_resource * resource,int32_t width,int32_t height)38 void xdg_positioner_v6_set_size(wl_client* client,
39                                 wl_resource* resource,
40                                 int32_t width,
41                                 int32_t height) {
42   if (width < 1 || height < 1) {
43     wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
44                            "width and height must be positive and non-zero");
45     return;
46   }
47 
48   GetUserDataAs<WaylandPositioner>(resource)->SetSize(gfx::Size(width, height));
49 }
50 
xdg_positioner_v6_set_anchor_rect(wl_client * client,wl_resource * resource,int32_t x,int32_t y,int32_t width,int32_t height)51 void xdg_positioner_v6_set_anchor_rect(wl_client* client,
52                                        wl_resource* resource,
53                                        int32_t x,
54                                        int32_t y,
55                                        int32_t width,
56                                        int32_t height) {
57   if (width < 1 || height < 1) {
58     wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
59                            "width and height must be positive and non-zero");
60     return;
61   }
62 
63   GetUserDataAs<WaylandPositioner>(resource)->SetAnchorRect(
64       gfx::Rect(x, y, width, height));
65 }
66 
xdg_positioner_v6_set_anchor(wl_client * client,wl_resource * resource,uint32_t anchor)67 void xdg_positioner_v6_set_anchor(wl_client* client,
68                                   wl_resource* resource,
69                                   uint32_t anchor) {
70   if (((anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT) &&
71        (anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT)) ||
72       ((anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP) &&
73        (anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM))) {
74     wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
75                            "same-axis values are not allowed");
76     return;
77   }
78 
79   GetUserDataAs<WaylandPositioner>(resource)->SetAnchor(anchor);
80 }
81 
xdg_positioner_v6_set_gravity(wl_client * client,wl_resource * resource,uint32_t gravity)82 void xdg_positioner_v6_set_gravity(wl_client* client,
83                                    wl_resource* resource,
84                                    uint32_t gravity) {
85   if (((gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) &&
86        (gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT)) ||
87       ((gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) &&
88        (gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM))) {
89     wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
90                            "same-axis values are not allowed");
91     return;
92   }
93 
94   GetUserDataAs<WaylandPositioner>(resource)->SetGravity(gravity);
95 }
96 
xdg_positioner_v6_set_constraint_adjustment(wl_client * client,wl_resource * resource,uint32_t adjustment)97 void xdg_positioner_v6_set_constraint_adjustment(wl_client* client,
98                                                  wl_resource* resource,
99                                                  uint32_t adjustment) {
100   GetUserDataAs<WaylandPositioner>(resource)->SetAdjustment(adjustment);
101 }
102 
xdg_positioner_v6_set_offset(wl_client * client,wl_resource * resource,int32_t x,int32_t y)103 void xdg_positioner_v6_set_offset(wl_client* client,
104                                   wl_resource* resource,
105                                   int32_t x,
106                                   int32_t y) {
107   GetUserDataAs<WaylandPositioner>(resource)->SetOffset(gfx::Vector2d(x, y));
108 }
109 
110 const struct zxdg_positioner_v6_interface xdg_positioner_v6_implementation = {
111     xdg_positioner_v6_destroy,
112     xdg_positioner_v6_set_size,
113     xdg_positioner_v6_set_anchor_rect,
114     xdg_positioner_v6_set_anchor,
115     xdg_positioner_v6_set_gravity,
116     xdg_positioner_v6_set_constraint_adjustment,
117     xdg_positioner_v6_set_offset};
118 
119 ////////////////////////////////////////////////////////////////////////////////
120 // xdg_toplevel_interface:
121 
XdgToplevelV6ResizeComponent(uint32_t edges)122 int XdgToplevelV6ResizeComponent(uint32_t edges) {
123   switch (edges) {
124     case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP:
125       return HTTOP;
126     case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM:
127       return HTBOTTOM;
128     case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_LEFT:
129       return HTLEFT;
130     case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_LEFT:
131       return HTTOPLEFT;
132     case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_LEFT:
133       return HTBOTTOMLEFT;
134     case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_RIGHT:
135       return HTRIGHT;
136     case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_RIGHT:
137       return HTTOPRIGHT;
138     case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_RIGHT:
139       return HTBOTTOMRIGHT;
140     default:
141       return HTBOTTOMRIGHT;
142   }
143 }
144 
145 using XdgSurfaceConfigureCallback =
146     base::RepeatingCallback<void(const gfx::Size& size,
147                                  ash::WindowStateType state_type,
148                                  bool resizing,
149                                  bool activated)>;
150 
HandleXdgSurfaceV6ConfigureCallback(wl_resource * resource,SerialTracker * serial_tracker,const XdgSurfaceConfigureCallback & callback,const gfx::Size & size,ash::WindowStateType state_type,bool resizing,bool activated,const gfx::Vector2d & origin_offset)151 uint32_t HandleXdgSurfaceV6ConfigureCallback(
152     wl_resource* resource,
153     SerialTracker* serial_tracker,
154     const XdgSurfaceConfigureCallback& callback,
155     const gfx::Size& size,
156     ash::WindowStateType state_type,
157     bool resizing,
158     bool activated,
159     const gfx::Vector2d& origin_offset) {
160   uint32_t serial =
161       serial_tracker->GetNextSerial(SerialTracker::EventType::OTHER_EVENT);
162   callback.Run(size, state_type, resizing, activated);
163   zxdg_surface_v6_send_configure(resource, serial);
164   wl_client_flush(wl_resource_get_client(resource));
165   return serial;
166 }
167 
168 struct WaylandXdgSurface {
WaylandXdgSurfaceexo::wayland::__anon01ddaa170111::WaylandXdgSurface169   WaylandXdgSurface(std::unique_ptr<XdgShellSurface> shell_surface,
170                     SerialTracker* const serial_tracker)
171       : shell_surface(std::move(shell_surface)),
172         serial_tracker(serial_tracker) {}
173 
174   std::unique_ptr<XdgShellSurface> shell_surface;
175 
176   // Owned by Server, which always outlives this surface.
177   SerialTracker* const serial_tracker;
178 
179   DISALLOW_COPY_AND_ASSIGN(WaylandXdgSurface);
180 };
181 
182 // Wrapper around shell surface that allows us to handle the case where the
183 // xdg surface resource is destroyed before the toplevel resource.
184 class WaylandToplevel : public aura::WindowObserver {
185  public:
WaylandToplevel(wl_resource * resource,wl_resource * surface_resource)186   WaylandToplevel(wl_resource* resource, wl_resource* surface_resource)
187       : resource_(resource),
188         shell_surface_data_(
189             GetUserDataAs<WaylandXdgSurface>(surface_resource)) {
190     shell_surface_data_->shell_surface->host_window()->AddObserver(this);
191     shell_surface_data_->shell_surface->set_close_callback(base::BindRepeating(
192         &WaylandToplevel::OnClose, weak_ptr_factory_.GetWeakPtr()));
193     shell_surface_data_->shell_surface->set_configure_callback(
194         base::BindRepeating(
195             &HandleXdgSurfaceV6ConfigureCallback, surface_resource,
196             shell_surface_data_->serial_tracker,
197             base::BindRepeating(&WaylandToplevel::OnConfigure,
198                                 weak_ptr_factory_.GetWeakPtr())));
199   }
~WaylandToplevel()200   ~WaylandToplevel() override {
201     if (shell_surface_data_)
202       shell_surface_data_->shell_surface->host_window()->RemoveObserver(this);
203   }
204 
205   // Overridden from aura::WindowObserver:
OnWindowDestroying(aura::Window * window)206   void OnWindowDestroying(aura::Window* window) override {
207     shell_surface_data_ = nullptr;
208   }
209 
SetParent(WaylandToplevel * parent)210   void SetParent(WaylandToplevel* parent) {
211     if (!shell_surface_data_)
212       return;
213 
214     if (!parent) {
215       shell_surface_data_->shell_surface->SetParent(nullptr);
216       return;
217     }
218 
219     // This is a no-op if parent is not mapped.
220     if (parent->shell_surface_data_ &&
221         parent->shell_surface_data_->shell_surface->GetWidget())
222       shell_surface_data_->shell_surface->SetParent(
223           parent->shell_surface_data_->shell_surface.get());
224   }
225 
SetTitle(const base::string16 & title)226   void SetTitle(const base::string16& title) {
227     if (shell_surface_data_)
228       shell_surface_data_->shell_surface->SetTitle(title);
229   }
230 
SetApplicationId(const char * application_id)231   void SetApplicationId(const char* application_id) {
232     if (shell_surface_data_)
233       shell_surface_data_->shell_surface->SetApplicationId(application_id);
234   }
235 
Move()236   void Move() {
237     if (shell_surface_data_)
238       shell_surface_data_->shell_surface->StartMove();
239   }
240 
Resize(int component)241   void Resize(int component) {
242     if (!shell_surface_data_)
243       return;
244 
245     if (component != HTNOWHERE)
246       shell_surface_data_->shell_surface->StartResize(component);
247   }
248 
SetMaximumSize(const gfx::Size & size)249   void SetMaximumSize(const gfx::Size& size) {
250     if (shell_surface_data_)
251       shell_surface_data_->shell_surface->SetMaximumSize(size);
252   }
253 
SetMinimumSize(const gfx::Size & size)254   void SetMinimumSize(const gfx::Size& size) {
255     if (shell_surface_data_)
256       shell_surface_data_->shell_surface->SetMinimumSize(size);
257   }
258 
Maximize()259   void Maximize() {
260     if (shell_surface_data_)
261       shell_surface_data_->shell_surface->Maximize();
262   }
263 
Restore()264   void Restore() {
265     if (shell_surface_data_)
266       shell_surface_data_->shell_surface->Restore();
267   }
268 
SetFullscreen(bool fullscreen)269   void SetFullscreen(bool fullscreen) {
270     if (shell_surface_data_)
271       shell_surface_data_->shell_surface->SetFullscreen(fullscreen);
272   }
273 
Minimize()274   void Minimize() {
275     if (shell_surface_data_)
276       shell_surface_data_->shell_surface->Minimize();
277   }
278 
279  private:
OnClose()280   void OnClose() {
281     zxdg_toplevel_v6_send_close(resource_);
282     wl_client_flush(wl_resource_get_client(resource_));
283   }
284 
AddState(wl_array * states,zxdg_toplevel_v6_state state)285   static void AddState(wl_array* states, zxdg_toplevel_v6_state state) {
286     zxdg_toplevel_v6_state* value = static_cast<zxdg_toplevel_v6_state*>(
287         wl_array_add(states, sizeof(zxdg_toplevel_v6_state)));
288     DCHECK(value);
289     *value = state;
290   }
291 
OnConfigure(const gfx::Size & size,ash::WindowStateType state_type,bool resizing,bool activated)292   void OnConfigure(const gfx::Size& size,
293                    ash::WindowStateType state_type,
294                    bool resizing,
295                    bool activated) {
296     wl_array states;
297     wl_array_init(&states);
298     if (state_type == ash::WindowStateType::kMaximized)
299       AddState(&states, ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED);
300     if (state_type == ash::WindowStateType::kFullscreen)
301       AddState(&states, ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN);
302     if (resizing)
303       AddState(&states, ZXDG_TOPLEVEL_V6_STATE_RESIZING);
304     if (activated)
305       AddState(&states, ZXDG_TOPLEVEL_V6_STATE_ACTIVATED);
306     zxdg_toplevel_v6_send_configure(resource_, size.width(), size.height(),
307                                     &states);
308     wl_array_release(&states);
309   }
310 
311   wl_resource* const resource_;
312   WaylandXdgSurface* shell_surface_data_;
313   base::WeakPtrFactory<WaylandToplevel> weak_ptr_factory_{this};
314 
315   DISALLOW_COPY_AND_ASSIGN(WaylandToplevel);
316 };
317 
xdg_toplevel_v6_destroy(wl_client * client,wl_resource * resource)318 void xdg_toplevel_v6_destroy(wl_client* client, wl_resource* resource) {
319   wl_resource_destroy(resource);
320 }
321 
xdg_toplevel_v6_set_parent(wl_client * client,wl_resource * resource,wl_resource * parent)322 void xdg_toplevel_v6_set_parent(wl_client* client,
323                                 wl_resource* resource,
324                                 wl_resource* parent) {
325   WaylandToplevel* parent_surface = nullptr;
326   if (parent)
327     parent_surface = GetUserDataAs<WaylandToplevel>(parent);
328 
329   GetUserDataAs<WaylandToplevel>(resource)->SetParent(parent_surface);
330 }
331 
xdg_toplevel_v6_set_title(wl_client * client,wl_resource * resource,const char * title)332 void xdg_toplevel_v6_set_title(wl_client* client,
333                                wl_resource* resource,
334                                const char* title) {
335   GetUserDataAs<WaylandToplevel>(resource)->SetTitle(
336       base::string16(base::UTF8ToUTF16(title)));
337 }
338 
xdg_toplevel_v6_set_app_id(wl_client * client,wl_resource * resource,const char * app_id)339 void xdg_toplevel_v6_set_app_id(wl_client* client,
340                                 wl_resource* resource,
341                                 const char* app_id) {
342   GetUserDataAs<WaylandToplevel>(resource)->SetApplicationId(app_id);
343 }
344 
xdg_toplevel_v6_show_window_menu(wl_client * client,wl_resource * resource,wl_resource * seat,uint32_t serial,int32_t x,int32_t y)345 void xdg_toplevel_v6_show_window_menu(wl_client* client,
346                                       wl_resource* resource,
347                                       wl_resource* seat,
348                                       uint32_t serial,
349                                       int32_t x,
350                                       int32_t y) {
351   NOTIMPLEMENTED();
352 }
353 
xdg_toplevel_v6_move(wl_client * client,wl_resource * resource,wl_resource * seat,uint32_t serial)354 void xdg_toplevel_v6_move(wl_client* client,
355                           wl_resource* resource,
356                           wl_resource* seat,
357                           uint32_t serial) {
358   GetUserDataAs<WaylandToplevel>(resource)->Move();
359 }
360 
xdg_toplevel_v6_resize(wl_client * client,wl_resource * resource,wl_resource * seat,uint32_t serial,uint32_t edges)361 void xdg_toplevel_v6_resize(wl_client* client,
362                             wl_resource* resource,
363                             wl_resource* seat,
364                             uint32_t serial,
365                             uint32_t edges) {
366   GetUserDataAs<WaylandToplevel>(resource)->Resize(
367       XdgToplevelV6ResizeComponent(edges));
368 }
369 
xdg_toplevel_v6_set_max_size(wl_client * client,wl_resource * resource,int32_t width,int32_t height)370 void xdg_toplevel_v6_set_max_size(wl_client* client,
371                                   wl_resource* resource,
372                                   int32_t width,
373                                   int32_t height) {
374   GetUserDataAs<WaylandToplevel>(resource)->SetMaximumSize(
375       gfx::Size(width, height));
376 }
377 
xdg_toplevel_v6_set_min_size(wl_client * client,wl_resource * resource,int32_t width,int32_t height)378 void xdg_toplevel_v6_set_min_size(wl_client* client,
379                                   wl_resource* resource,
380                                   int32_t width,
381                                   int32_t height) {
382   GetUserDataAs<WaylandToplevel>(resource)->SetMinimumSize(
383       gfx::Size(width, height));
384 }
385 
xdg_toplevel_v6_set_maximized(wl_client * client,wl_resource * resource)386 void xdg_toplevel_v6_set_maximized(wl_client* client, wl_resource* resource) {
387   GetUserDataAs<WaylandToplevel>(resource)->Maximize();
388 }
389 
xdg_toplevel_v6_unset_maximized(wl_client * client,wl_resource * resource)390 void xdg_toplevel_v6_unset_maximized(wl_client* client, wl_resource* resource) {
391   GetUserDataAs<WaylandToplevel>(resource)->Restore();
392 }
393 
xdg_toplevel_v6_set_fullscreen(wl_client * client,wl_resource * resource,wl_resource * output)394 void xdg_toplevel_v6_set_fullscreen(wl_client* client,
395                                     wl_resource* resource,
396                                     wl_resource* output) {
397   GetUserDataAs<WaylandToplevel>(resource)->SetFullscreen(true);
398 }
399 
xdg_toplevel_v6_unset_fullscreen(wl_client * client,wl_resource * resource)400 void xdg_toplevel_v6_unset_fullscreen(wl_client* client,
401                                       wl_resource* resource) {
402   GetUserDataAs<WaylandToplevel>(resource)->SetFullscreen(false);
403 }
404 
xdg_toplevel_v6_set_minimized(wl_client * client,wl_resource * resource)405 void xdg_toplevel_v6_set_minimized(wl_client* client, wl_resource* resource) {
406   GetUserDataAs<WaylandToplevel>(resource)->Minimize();
407 }
408 
409 const struct zxdg_toplevel_v6_interface xdg_toplevel_v6_implementation = {
410     xdg_toplevel_v6_destroy,          xdg_toplevel_v6_set_parent,
411     xdg_toplevel_v6_set_title,        xdg_toplevel_v6_set_app_id,
412     xdg_toplevel_v6_show_window_menu, xdg_toplevel_v6_move,
413     xdg_toplevel_v6_resize,           xdg_toplevel_v6_set_max_size,
414     xdg_toplevel_v6_set_min_size,     xdg_toplevel_v6_set_maximized,
415     xdg_toplevel_v6_unset_maximized,  xdg_toplevel_v6_set_fullscreen,
416     xdg_toplevel_v6_unset_fullscreen, xdg_toplevel_v6_set_minimized};
417 
418 ////////////////////////////////////////////////////////////////////////////////
419 // xdg_popup_interface:
420 
421 // Wrapper around shell surface that allows us to handle the case where the
422 // xdg surface resource is destroyed before the popup resource.
423 class WaylandPopup : aura::WindowObserver {
424  public:
WaylandPopup(wl_resource * resource,wl_resource * surface_resource)425   WaylandPopup(wl_resource* resource, wl_resource* surface_resource)
426       : resource_(resource),
427         shell_surface_data_(
428             GetUserDataAs<WaylandXdgSurface>(surface_resource)) {
429     shell_surface_data_->shell_surface->host_window()->AddObserver(this);
430     shell_surface_data_->shell_surface->set_close_callback(base::BindRepeating(
431         &WaylandPopup::OnClose, weak_ptr_factory_.GetWeakPtr()));
432     shell_surface_data_->shell_surface->set_configure_callback(
433         base::BindRepeating(
434             &HandleXdgSurfaceV6ConfigureCallback, surface_resource,
435             shell_surface_data_->serial_tracker,
436             base::BindRepeating(&WaylandPopup::OnConfigure,
437                                 weak_ptr_factory_.GetWeakPtr())));
438   }
~WaylandPopup()439   ~WaylandPopup() override {
440     if (shell_surface_data_)
441       shell_surface_data_->shell_surface->host_window()->RemoveObserver(this);
442   }
443 
Grab()444   void Grab() {
445     if (!shell_surface_data_) {
446       wl_resource_post_error(resource_, ZXDG_POPUP_V6_ERROR_INVALID_GRAB,
447                              "the surface has already been destroyed");
448       return;
449     }
450     if (shell_surface_data_->shell_surface->GetWidget()) {
451       wl_resource_post_error(resource_, ZXDG_POPUP_V6_ERROR_INVALID_GRAB,
452                              "grab must be called before construction");
453       return;
454     }
455     shell_surface_data_->shell_surface->Grab();
456   }
457 
458   // Overridden from aura::WindowObserver:
OnWindowDestroying(aura::Window * window)459   void OnWindowDestroying(aura::Window* window) override {
460     shell_surface_data_ = nullptr;
461   }
462 
463  private:
OnClose()464   void OnClose() {
465     zxdg_popup_v6_send_popup_done(resource_);
466     wl_client_flush(wl_resource_get_client(resource_));
467   }
468 
OnConfigure(const gfx::Size & size,ash::WindowStateType state_type,bool resizing,bool activated)469   void OnConfigure(const gfx::Size& size,
470                    ash::WindowStateType state_type,
471                    bool resizing,
472                    bool activated) {
473     // Nothing to do here as popups don't have additional configure state.
474   }
475 
476   wl_resource* const resource_;
477   WaylandXdgSurface* shell_surface_data_;
478   base::WeakPtrFactory<WaylandPopup> weak_ptr_factory_{this};
479 
480   DISALLOW_COPY_AND_ASSIGN(WaylandPopup);
481 };
482 
xdg_popup_v6_destroy(wl_client * client,wl_resource * resource)483 void xdg_popup_v6_destroy(wl_client* client, wl_resource* resource) {
484   wl_resource_destroy(resource);
485 }
486 
xdg_popup_v6_grab(wl_client * client,wl_resource * resource,wl_resource * seat,uint32_t serial)487 void xdg_popup_v6_grab(wl_client* client,
488                        wl_resource* resource,
489                        wl_resource* seat,
490                        uint32_t serial) {
491   GetUserDataAs<WaylandPopup>(resource)->Grab();
492 }
493 
494 const struct zxdg_popup_v6_interface xdg_popup_v6_implementation = {
495     xdg_popup_v6_destroy, xdg_popup_v6_grab};
496 
497 ////////////////////////////////////////////////////////////////////////////////
498 // xdg_surface_interface:
499 
xdg_surface_v6_destroy(wl_client * client,wl_resource * resource)500 void xdg_surface_v6_destroy(wl_client* client, wl_resource* resource) {
501   wl_resource_destroy(resource);
502 }
503 
xdg_surface_v6_get_toplevel(wl_client * client,wl_resource * resource,uint32_t id)504 void xdg_surface_v6_get_toplevel(wl_client* client,
505                                  wl_resource* resource,
506                                  uint32_t id) {
507   auto* shell_surface_data = GetUserDataAs<WaylandXdgSurface>(resource);
508   if (shell_surface_data->shell_surface->GetEnabled()) {
509     wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_ALREADY_CONSTRUCTED,
510                            "surface has already been constructed");
511     return;
512   }
513 
514   shell_surface_data->shell_surface->SetCanMinimize(true);
515   shell_surface_data->shell_surface->SetEnabled(true);
516 
517   wl_resource* xdg_toplevel_resource =
518       wl_resource_create(client, &zxdg_toplevel_v6_interface, 1, id);
519 
520   SetImplementation(
521       xdg_toplevel_resource, &xdg_toplevel_v6_implementation,
522       std::make_unique<WaylandToplevel>(xdg_toplevel_resource, resource));
523 }
524 
xdg_surface_v6_get_popup(wl_client * client,wl_resource * resource,uint32_t id,wl_resource * parent_resource,wl_resource * positioner_resource)525 void xdg_surface_v6_get_popup(wl_client* client,
526                               wl_resource* resource,
527                               uint32_t id,
528                               wl_resource* parent_resource,
529                               wl_resource* positioner_resource) {
530   auto* shell_surface_data = GetUserDataAs<WaylandXdgSurface>(resource);
531   if (shell_surface_data->shell_surface->GetEnabled()) {
532     wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_ALREADY_CONSTRUCTED,
533                            "surface has already been constructed");
534     return;
535   }
536 
537   auto* parent_data = GetUserDataAs<WaylandXdgSurface>(parent_resource);
538   if (!parent_data->shell_surface->GetWidget()) {
539     wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED,
540                            "popup parent not constructed");
541     return;
542   }
543 
544   if (shell_surface_data->shell_surface->GetWidget()) {
545     wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_ALREADY_CONSTRUCTED,
546                            "get_popup is called after constructed");
547     return;
548   }
549 
550   display::Display display =
551       display::Screen::GetScreen()->GetDisplayNearestWindow(
552           parent_data->shell_surface->GetWidget()->GetNativeWindow());
553   gfx::Rect work_area = display.work_area();
554   wm::ConvertRectFromScreen(
555       parent_data->shell_surface->GetWidget()->GetNativeWindow(), &work_area);
556 
557   // Try layout using parent's flip state.
558   WaylandPositioner* positioner =
559       GetUserDataAs<WaylandPositioner>(positioner_resource);
560   WaylandPositioner::Result position = positioner->CalculatePosition(
561       work_area, parent_data->shell_surface->x_flipped(),
562       parent_data->shell_surface->y_flipped());
563 
564   // Remember the new flip state for its child popups.
565   shell_surface_data->shell_surface->set_x_flipped(position.x_flipped);
566   shell_surface_data->shell_surface->set_y_flipped(position.y_flipped);
567 
568   // |position| is relative to the parent's contents view origin, and |origin|
569   // is in screen coordinates.
570   gfx::Point origin = position.origin;
571   views::View::ConvertPointToScreen(parent_data->shell_surface->GetWidget()
572                                         ->widget_delegate()
573                                         ->GetContentsView(),
574                                     &origin);
575   shell_surface_data->shell_surface->SetOrigin(origin);
576   shell_surface_data->shell_surface->SetSize(position.size);
577   shell_surface_data->shell_surface->DisableMovement();
578   shell_surface_data->shell_surface->SetActivatable(false);
579   shell_surface_data->shell_surface->SetCanMinimize(false);
580   shell_surface_data->shell_surface->SetParent(
581       parent_data->shell_surface.get());
582   shell_surface_data->shell_surface->SetPopup();
583   shell_surface_data->shell_surface->SetEnabled(true);
584 
585   wl_resource* xdg_popup_resource =
586       wl_resource_create(client, &zxdg_popup_v6_interface, 1, id);
587 
588   SetImplementation(
589       xdg_popup_resource, &xdg_popup_v6_implementation,
590       std::make_unique<WaylandPopup>(xdg_popup_resource, resource));
591 
592   // We send the configure event here as this event needs x,y coordinates
593   // relative to the parent window.
594   zxdg_popup_v6_send_configure(xdg_popup_resource, position.origin.x(),
595                                position.origin.y(), position.size.width(),
596                                position.size.height());
597 }
598 
xdg_surface_v6_set_window_geometry(wl_client * client,wl_resource * resource,int32_t x,int32_t y,int32_t width,int32_t height)599 void xdg_surface_v6_set_window_geometry(wl_client* client,
600                                         wl_resource* resource,
601                                         int32_t x,
602                                         int32_t y,
603                                         int32_t width,
604                                         int32_t height) {
605   GetUserDataAs<WaylandXdgSurface>(resource)->shell_surface->SetGeometry(
606       gfx::Rect(x, y, width, height));
607 }
608 
xdg_surface_v6_ack_configure(wl_client * client,wl_resource * resource,uint32_t serial)609 void xdg_surface_v6_ack_configure(wl_client* client,
610                                   wl_resource* resource,
611                                   uint32_t serial) {
612   GetUserDataAs<WaylandXdgSurface>(resource)
613       ->shell_surface->AcknowledgeConfigure(serial);
614 }
615 
616 const struct zxdg_surface_v6_interface xdg_surface_v6_implementation = {
617     xdg_surface_v6_destroy, xdg_surface_v6_get_toplevel,
618     xdg_surface_v6_get_popup, xdg_surface_v6_set_window_geometry,
619     xdg_surface_v6_ack_configure};
620 
621 ////////////////////////////////////////////////////////////////////////////////
622 // xdg_shell_interface:
623 
xdg_shell_v6_destroy(wl_client * client,wl_resource * resource)624 void xdg_shell_v6_destroy(wl_client* client, wl_resource* resource) {
625   // Nothing to do here.
626 }
627 
xdg_shell_v6_create_positioner(wl_client * client,wl_resource * resource,uint32_t id)628 void xdg_shell_v6_create_positioner(wl_client* client,
629                                     wl_resource* resource,
630                                     uint32_t id) {
631   wl_resource* positioner_resource =
632       wl_resource_create(client, &zxdg_positioner_v6_interface, 1, id);
633 
634   SetImplementation(positioner_resource, &xdg_positioner_v6_implementation,
635                     std::make_unique<WaylandPositioner>());
636 }
637 
xdg_shell_v6_get_xdg_surface(wl_client * client,wl_resource * resource,uint32_t id,wl_resource * surface)638 void xdg_shell_v6_get_xdg_surface(wl_client* client,
639                                   wl_resource* resource,
640                                   uint32_t id,
641                                   wl_resource* surface) {
642   auto* data = GetUserDataAs<WaylandXdgShell>(resource);
643   std::unique_ptr<XdgShellSurface> shell_surface =
644       data->display->CreateXdgShellSurface(GetUserDataAs<Surface>(surface));
645   if (!shell_surface) {
646     wl_resource_post_error(resource, ZXDG_SHELL_V6_ERROR_ROLE,
647                            "surface has already been assigned a role");
648     return;
649   }
650 
651   // Xdg shell v6 surfaces are initially disabled and needs to be explicitly
652   // mapped before they are enabled and can become visible.
653   shell_surface->SetEnabled(false);
654 
655   std::unique_ptr<WaylandXdgSurface> wayland_shell_surface =
656       std::make_unique<WaylandXdgSurface>(std::move(shell_surface),
657                                           data->serial_tracker);
658 
659   wl_resource* xdg_surface_resource =
660       wl_resource_create(client, &zxdg_surface_v6_interface, 1, id);
661 
662   SetImplementation(xdg_surface_resource, &xdg_surface_v6_implementation,
663                     std::move(wayland_shell_surface));
664 }
665 
xdg_shell_v6_pong(wl_client * client,wl_resource * resource,uint32_t serial)666 void xdg_shell_v6_pong(wl_client* client,
667                        wl_resource* resource,
668                        uint32_t serial) {
669   NOTIMPLEMENTED();
670 }
671 
672 const struct zxdg_shell_v6_interface xdg_shell_v6_implementation = {
673     xdg_shell_v6_destroy, xdg_shell_v6_create_positioner,
674     xdg_shell_v6_get_xdg_surface, xdg_shell_v6_pong};
675 
676 }  // namespace
677 
bind_xdg_shell_v6(wl_client * client,void * data,uint32_t version,uint32_t id)678 void bind_xdg_shell_v6(wl_client* client,
679                        void* data,
680                        uint32_t version,
681                        uint32_t id) {
682   wl_resource* resource =
683       wl_resource_create(client, &zxdg_shell_v6_interface, 1, id);
684 
685   wl_resource_set_implementation(resource, &xdg_shell_v6_implementation, data,
686                                  nullptr);
687 }
688 
689 }  // namespace wayland
690 }  // namespace exo
691