| // Copyright 2018 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/exo/wayland/xdg_shell.h" |
| |
| #include <wayland-server-core.h> |
| #include <wayland-server-protocol-core.h> |
| #include <xdg-decoration-unstable-v1-server-protocol.h> |
| #include <xdg-shell-server-protocol.h> |
| |
| #include <optional> |
| |
| #include "ash/public/cpp/shell_window_ids.h" |
| #include "ash/public/cpp/window_properties.h" |
| #include "base/functional/bind.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "chromeos/ui/base/window_state_type.h" |
| #include "components/exo/display.h" |
| #include "components/exo/shell_surface_util.h" |
| #include "components/exo/wayland/serial_tracker.h" |
| #include "components/exo/wayland/server_util.h" |
| #include "components/exo/wayland/wayland_display_observer.h" |
| #include "components/exo/wayland/wayland_positioner.h" |
| #include "components/exo/xdg_shell_surface.h" |
| #include "ui/aura/window_observer.h" |
| #include "ui/base/hit_test.h" |
| #include "ui/display/screen.h" |
| #include "ui/display/types/display_constants.h" |
| #include "ui/views/widget/widget.h" |
| #include "ui/wm/core/coordinate_conversion.h" |
| |
| namespace exo { |
| namespace wayland { |
| namespace { |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // xdg_positioner_interface: |
| |
| void xdg_positioner_destroy(wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| void xdg_positioner_set_size(wl_client* client, |
| wl_resource* resource, |
| int32_t width, |
| int32_t height) { |
| if (width < 1 || height < 1) { |
| wl_resource_post_error(resource, XDG_POSITIONER_ERROR_INVALID_INPUT, |
| "width and height must be positive and non-zero"); |
| return; |
| } |
| |
| GetUserDataAs<WaylandPositioner>(resource)->SetSize(gfx::Size(width, height)); |
| } |
| |
| void xdg_positioner_set_anchor_rect(wl_client* client, |
| wl_resource* resource, |
| int32_t x, |
| int32_t y, |
| int32_t width, |
| int32_t height) { |
| if (width < 1 || height < 1) { |
| wl_resource_post_error(resource, XDG_POSITIONER_ERROR_INVALID_INPUT, |
| "width and height must be positive and non-zero"); |
| return; |
| } |
| |
| GetUserDataAs<WaylandPositioner>(resource)->SetAnchorRect( |
| gfx::Rect(x, y, width, height)); |
| } |
| |
| void xdg_positioner_set_anchor(wl_client* client, |
| wl_resource* resource, |
| uint32_t anchor) { |
| GetUserDataAs<WaylandPositioner>(resource)->SetAnchor(anchor); |
| } |
| |
| void xdg_positioner_set_gravity(wl_client* client, |
| wl_resource* resource, |
| uint32_t gravity) { |
| GetUserDataAs<WaylandPositioner>(resource)->SetGravity(gravity); |
| } |
| |
| void xdg_positioner_set_constraint_adjustment(wl_client* client, |
| wl_resource* resource, |
| uint32_t adjustment) { |
| GetUserDataAs<WaylandPositioner>(resource)->SetAdjustment(adjustment); |
| } |
| |
| void xdg_positioner_set_offset(wl_client* client, |
| wl_resource* resource, |
| int32_t x, |
| int32_t y) { |
| GetUserDataAs<WaylandPositioner>(resource)->SetOffset(gfx::Vector2d(x, y)); |
| } |
| |
| // Version 3 xdg_positioner |
| // Empty since Weston doesn't implement this. |
| // Ref: |
| // https://gitlab.freedesktop.org/wayland/weston/-/blob/main/libweston/desktop/xdg-shell.c#L312 |
| void xdg_positioner_set_reactive(wl_client* client, wl_resource* resource) {} |
| |
| void xdg_positioner_set_parent_size(wl_client* client, |
| wl_resource* resource, |
| int32_t parent_width, |
| int32_t parent_height) {} |
| |
| void xdg_positioner_set_parent_configure(wl_client* client, |
| wl_resource* resource, |
| uint32_t serial) {} |
| |
| const struct xdg_positioner_interface xdg_positioner_implementation = { |
| xdg_positioner_destroy, xdg_positioner_set_size, |
| xdg_positioner_set_anchor_rect, xdg_positioner_set_anchor, |
| xdg_positioner_set_gravity, xdg_positioner_set_constraint_adjustment, |
| xdg_positioner_set_offset, xdg_positioner_set_reactive, |
| xdg_positioner_set_parent_size, xdg_positioner_set_parent_configure}; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // xdg_toplevel_interface: |
| |
| int XdgToplevelResizeComponent(uint32_t edges) { |
| switch (edges) { |
| case XDG_TOPLEVEL_RESIZE_EDGE_TOP: |
| return HTTOP; |
| case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM: |
| return HTBOTTOM; |
| case XDG_TOPLEVEL_RESIZE_EDGE_LEFT: |
| return HTLEFT; |
| case XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT: |
| return HTTOPLEFT; |
| case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT: |
| return HTBOTTOMLEFT; |
| case XDG_TOPLEVEL_RESIZE_EDGE_RIGHT: |
| return HTRIGHT; |
| case XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT: |
| return HTTOPRIGHT; |
| case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT: |
| return HTBOTTOMRIGHT; |
| default: |
| return HTBOTTOMRIGHT; |
| } |
| } |
| |
| using XdgSurfaceConfigureCallback = base::RepeatingCallback<void( |
| const gfx::Size& size, |
| chromeos::WindowStateType state_type, |
| bool resizing, |
| bool activated, |
| std::optional<chromeos::WindowStateType> restore_state_type)>; |
| |
| uint32_t HandleXdgSurfaceConfigureCallback( |
| wl_resource* resource, |
| SerialTracker* serial_tracker, |
| const XdgSurfaceConfigureCallback& callback, |
| const gfx::Rect& bounds, |
| chromeos::WindowStateType state_type, |
| bool resizing, |
| bool activated, |
| const gfx::Vector2d& origin_offset, |
| float raster_scale, |
| aura::Window::OcclusionState occlusion_state, |
| std::optional<chromeos::WindowStateType> restore_state_type) { |
| uint32_t serial = |
| serial_tracker->GetNextSerial(SerialTracker::EventType::OTHER_EVENT); |
| callback.Run(bounds.size(), state_type, resizing, activated, |
| restore_state_type); |
| xdg_surface_send_configure(resource, serial); |
| wl_client_flush(wl_resource_get_client(resource)); |
| return serial; |
| } |
| |
| // Wrapper around shell surface that allows us to handle the case where the |
| // xdg surface resource is destroyed before the toplevel resource. |
| class WaylandToplevel : public aura::WindowObserver { |
| public: |
| WaylandToplevel(wl_resource* xdg_toplevel_resource, |
| wl_resource* xdg_surface_resource) |
| : xdg_toplevel_resource_(xdg_toplevel_resource), |
| xdg_surface_resource_(xdg_surface_resource), |
| shell_surface_data_( |
| GetUserDataAs<WaylandXdgSurface>(xdg_surface_resource)) { |
| shell_surface_data_->shell_surface->host_window()->AddObserver(this); |
| shell_surface_data_->shell_surface->set_close_callback(base::BindRepeating( |
| &WaylandToplevel::OnClose, weak_ptr_factory_.GetWeakPtr())); |
| shell_surface_data_->shell_surface->set_configure_callback( |
| base::BindRepeating( |
| &HandleXdgSurfaceConfigureCallback, xdg_surface_resource, |
| shell_surface_data_->serial_tracker, |
| base::BindRepeating(&WaylandToplevel::OnConfigure, |
| weak_ptr_factory_.GetWeakPtr()))); |
| } |
| |
| WaylandToplevel(const WaylandToplevel&) = delete; |
| WaylandToplevel& operator=(const WaylandToplevel&) = delete; |
| |
| ~WaylandToplevel() override { |
| if (shell_surface_data_) |
| shell_surface_data_->shell_surface->host_window()->RemoveObserver(this); |
| } |
| |
| // Overridden from aura::WindowObserver: |
| void OnWindowDestroying(aura::Window* window) override { |
| window->RemoveObserver(this); |
| shell_surface_data_ = nullptr; |
| } |
| |
| void SetParent(WaylandToplevel* parent) { |
| if (!shell_surface_data_) |
| return; |
| |
| if (!parent) { |
| shell_surface_data_->shell_surface->SetParent(nullptr); |
| return; |
| } |
| |
| // This is a no-op if parent is not mapped. |
| if (parent->shell_surface_data_ && |
| parent->shell_surface_data_->shell_surface->GetWidget()) |
| shell_surface_data_->shell_surface->SetParent( |
| parent->shell_surface_data_->shell_surface.get()); |
| } |
| |
| void SetTitle(const std::u16string& title) { |
| if (shell_surface_data_) |
| shell_surface_data_->shell_surface->SetTitle(title); |
| } |
| |
| void SetApplicationId(const char* application_id) { |
| if (shell_surface_data_) |
| shell_surface_data_->shell_surface->SetApplicationId(application_id); |
| } |
| |
| void Move() { |
| if (shell_surface_data_) |
| shell_surface_data_->shell_surface->StartMove(); |
| } |
| |
| void Resize(int component) { |
| if (!shell_surface_data_) |
| return; |
| |
| if (component != HTNOWHERE) |
| shell_surface_data_->shell_surface->StartResize(component); |
| } |
| |
| void SetMaximumSize(const gfx::Size& size) { |
| if (shell_surface_data_) |
| shell_surface_data_->shell_surface->SetMaximumSize(size); |
| } |
| |
| void SetMinimumSize(const gfx::Size& size) { |
| if (shell_surface_data_) |
| shell_surface_data_->shell_surface->SetMinimumSize(size); |
| } |
| |
| void Maximize() { |
| if (shell_surface_data_) |
| shell_surface_data_->shell_surface->Maximize(); |
| } |
| |
| void Restore() { |
| if (shell_surface_data_) |
| shell_surface_data_->shell_surface->Restore(); |
| } |
| |
| void SetFullscreen(bool fullscreen, |
| int64_t display_id = display::kInvalidDisplayId) { |
| if (shell_surface_data_) |
| shell_surface_data_->shell_surface->SetFullscreen(fullscreen, display_id); |
| } |
| |
| void Minimize() { |
| if (shell_surface_data_) |
| shell_surface_data_->shell_surface->Minimize(); |
| } |
| |
| void SetFrame(SurfaceFrameType type) { |
| if (shell_surface_data_) |
| shell_surface_data_->shell_surface->OnSetFrame(type); |
| } |
| |
| ShellSurfaceData GetShellSurfaceData() { |
| return ShellSurfaceData(shell_surface_data_->shell_surface.get(), |
| shell_surface_data_->serial_tracker, |
| shell_surface_data_->rotation_serial_tracker, |
| xdg_surface_resource_); |
| } |
| |
| private: |
| void OnClose() { |
| xdg_toplevel_send_close(xdg_toplevel_resource_); |
| wl_client_flush(wl_resource_get_client(xdg_toplevel_resource_)); |
| } |
| |
| static void AddState(wl_array* states, xdg_toplevel_state state) { |
| xdg_toplevel_state* value = static_cast<xdg_toplevel_state*>( |
| wl_array_add(states, sizeof(xdg_toplevel_state))); |
| DCHECK(value); |
| *value = state; |
| } |
| |
| void OnConfigure( |
| const gfx::Size& size, |
| chromeos::WindowStateType state_type, |
| bool resizing, |
| bool activated, |
| std::optional<chromeos::WindowStateType> restore_state_type) { |
| wl_array states; |
| wl_array_init(&states); |
| if (state_type == chromeos::WindowStateType::kMaximized) |
| AddState(&states, XDG_TOPLEVEL_STATE_MAXIMIZED); |
| // TODO(crbug/1250129): Pinned states need to be handled properly. |
| if (IsFullscreenOrPinnedWindowStateType(state_type)) { |
| AddState(&states, XDG_TOPLEVEL_STATE_FULLSCREEN); |
| // If the window was maxmized before it is fullscreened, we should |
| // keep this state while it is fullscreened. This is what X11 apps, and |
| // thus standard wayland apps expect, and they may rely on this behavior |
| // even though this is not explicitly specified in the protocol spec. |
| if (restore_state_type.has_value() && |
| restore_state_type.value() == chromeos::WindowStateType::kMaximized) { |
| AddState(&states, XDG_TOPLEVEL_STATE_MAXIMIZED); |
| } |
| } |
| if (resizing) |
| AddState(&states, XDG_TOPLEVEL_STATE_RESIZING); |
| if (activated) |
| AddState(&states, XDG_TOPLEVEL_STATE_ACTIVATED); |
| xdg_toplevel_send_configure(xdg_toplevel_resource_, size.width(), |
| size.height(), &states); |
| wl_array_release(&states); |
| } |
| |
| const raw_ptr<wl_resource> xdg_toplevel_resource_; |
| const raw_ptr<wl_resource, DanglingUntriaged> xdg_surface_resource_; |
| raw_ptr<WaylandXdgSurface> shell_surface_data_; |
| base::WeakPtrFactory<WaylandToplevel> weak_ptr_factory_{this}; |
| }; |
| |
| void xdg_toplevel_destroy(wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| void xdg_toplevel_set_parent(wl_client* client, |
| wl_resource* resource, |
| wl_resource* parent) { |
| WaylandToplevel* parent_surface = nullptr; |
| if (parent) |
| parent_surface = GetUserDataAs<WaylandToplevel>(parent); |
| |
| GetUserDataAs<WaylandToplevel>(resource)->SetParent(parent_surface); |
| } |
| |
| void xdg_toplevel_set_title(wl_client* client, |
| wl_resource* resource, |
| const char* title) { |
| GetUserDataAs<WaylandToplevel>(resource)->SetTitle( |
| std::u16string(base::UTF8ToUTF16(title))); |
| } |
| |
| void xdg_toplevel_set_app_id(wl_client* client, |
| wl_resource* resource, |
| const char* app_id) { |
| GetUserDataAs<WaylandToplevel>(resource)->SetApplicationId(app_id); |
| } |
| |
| void xdg_toplevel_show_window_menu(wl_client* client, |
| wl_resource* resource, |
| wl_resource* seat, |
| uint32_t serial, |
| int32_t x, |
| int32_t y) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void xdg_toplevel_move(wl_client* client, |
| wl_resource* resource, |
| wl_resource* seat, |
| uint32_t serial) { |
| GetUserDataAs<WaylandToplevel>(resource)->Move(); |
| } |
| |
| void xdg_toplevel_resize(wl_client* client, |
| wl_resource* resource, |
| wl_resource* seat, |
| uint32_t serial, |
| uint32_t edges) { |
| GetUserDataAs<WaylandToplevel>(resource)->Resize( |
| XdgToplevelResizeComponent(edges)); |
| } |
| |
| void xdg_toplevel_set_max_size(wl_client* client, |
| wl_resource* resource, |
| int32_t width, |
| int32_t height) { |
| GetUserDataAs<WaylandToplevel>(resource)->SetMaximumSize( |
| gfx::Size(width, height)); |
| } |
| |
| void xdg_toplevel_set_min_size(wl_client* client, |
| wl_resource* resource, |
| int32_t width, |
| int32_t height) { |
| GetUserDataAs<WaylandToplevel>(resource)->SetMinimumSize( |
| gfx::Size(width, height)); |
| } |
| |
| void xdg_toplevel_set_maximized(wl_client* client, wl_resource* resource) { |
| GetUserDataAs<WaylandToplevel>(resource)->Maximize(); |
| } |
| |
| void xdg_toplevel_unset_maximized(wl_client* client, wl_resource* resource) { |
| GetUserDataAs<WaylandToplevel>(resource)->Restore(); |
| } |
| |
| void xdg_toplevel_set_fullscreen(wl_client* client, |
| wl_resource* resource, |
| wl_resource* output) { |
| int64_t display_id = output |
| ? GetUserDataAs<WaylandDisplayHandler>(output)->id() |
| : display::kInvalidDisplayId; |
| GetUserDataAs<WaylandToplevel>(resource)->SetFullscreen(true, display_id); |
| } |
| |
| void xdg_toplevel_unset_fullscreen(wl_client* client, wl_resource* resource) { |
| GetUserDataAs<WaylandToplevel>(resource)->SetFullscreen(false); |
| } |
| |
| void xdg_toplevel_set_minimized(wl_client* client, wl_resource* resource) { |
| GetUserDataAs<WaylandToplevel>(resource)->Minimize(); |
| } |
| |
| const struct xdg_toplevel_interface xdg_toplevel_implementation = { |
| xdg_toplevel_destroy, xdg_toplevel_set_parent, |
| xdg_toplevel_set_title, xdg_toplevel_set_app_id, |
| xdg_toplevel_show_window_menu, xdg_toplevel_move, |
| xdg_toplevel_resize, xdg_toplevel_set_max_size, |
| xdg_toplevel_set_min_size, xdg_toplevel_set_maximized, |
| xdg_toplevel_unset_maximized, xdg_toplevel_set_fullscreen, |
| xdg_toplevel_unset_fullscreen, xdg_toplevel_set_minimized}; |
| |
| class WaylandXdgToplevelDecoration { |
| public: |
| WaylandXdgToplevelDecoration(wl_resource* resource, |
| wl_resource* toplevel_resource) |
| : resource_(resource), |
| top_level_(GetUserDataAs<WaylandToplevel>(toplevel_resource)) {} |
| |
| WaylandXdgToplevelDecoration(const WaylandXdgToplevelDecoration&) = delete; |
| WaylandXdgToplevelDecoration& operator=(const WaylandXdgToplevelDecoration&) = |
| delete; |
| |
| uint32_t decoration_mode() const { return default_mode_; } |
| void SetDecorationMode(uint32_t mode) { |
| if (default_mode_ != mode) { |
| default_mode_ = mode; |
| OnConfigure(mode); |
| } |
| } |
| |
| private: |
| void OnConfigure(uint32_t mode) { |
| switch (mode) { |
| case ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE: |
| top_level_->SetFrame(SurfaceFrameType::NONE); |
| break; |
| case ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE: |
| top_level_->SetFrame(SurfaceFrameType::NORMAL); |
| break; |
| } |
| zxdg_toplevel_decoration_v1_send_configure(resource_, mode); |
| } |
| |
| const raw_ptr<wl_resource> resource_; |
| raw_ptr<WaylandToplevel, DanglingUntriaged> top_level_; |
| // Keeps track of the xdg-decoration mode on server side. |
| uint32_t default_mode_ = ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // xdg_popup_interface: |
| |
| // Wrapper around shell surface that allows us to handle the case where the |
| // xdg surface resource is destroyed before the popup resource. |
| class WaylandPopup : aura::WindowObserver { |
| public: |
| WaylandPopup(wl_resource* resource, wl_resource* surface_resource) |
| : resource_(resource), |
| surface_resource_(surface_resource), |
| shell_surface_data_( |
| GetUserDataAs<WaylandXdgSurface>(surface_resource)) { |
| shell_surface_data_->shell_surface->host_window()->AddObserver(this); |
| shell_surface_data_->shell_surface->set_close_callback(base::BindRepeating( |
| &WaylandPopup::OnClose, weak_ptr_factory_.GetWeakPtr())); |
| shell_surface_data_->shell_surface->set_configure_callback( |
| base::BindRepeating( |
| &HandleXdgSurfaceConfigureCallback, surface_resource, |
| shell_surface_data_->serial_tracker, |
| base::BindRepeating(&WaylandPopup::OnConfigure, |
| weak_ptr_factory_.GetWeakPtr()))); |
| } |
| |
| WaylandPopup(const WaylandPopup&) = delete; |
| WaylandPopup& operator=(const WaylandPopup&) = delete; |
| |
| ~WaylandPopup() override { |
| if (shell_surface_data_) |
| shell_surface_data_->shell_surface->host_window()->RemoveObserver(this); |
| } |
| |
| ShellSurfaceBase* GetShellSurface() { |
| return shell_surface_data_->shell_surface.get(); |
| } |
| |
| void Grab() { |
| if (!shell_surface_data_) { |
| wl_resource_post_error(resource_.get(), XDG_POPUP_ERROR_INVALID_GRAB, |
| "the surface has already been destroyed"); |
| return; |
| } |
| if (shell_surface_data_->shell_surface->GetWidget()) { |
| wl_resource_post_error(resource_.get(), XDG_POPUP_ERROR_INVALID_GRAB, |
| "grab must be called before construction"); |
| return; |
| } |
| shell_surface_data_->shell_surface->Grab(); |
| } |
| |
| void Reposition(WaylandPositioner* positioner, uint32_t token) { |
| if (wl_resource_get_version(resource_) < |
| XDG_POPUP_REPOSITIONED_SINCE_VERSION) { |
| return; |
| } |
| xdg_popup_send_repositioned(resource_, token); |
| |
| display::Display display = |
| display::Screen::GetScreen()->GetDisplayNearestWindow( |
| shell_surface_data_->shell_surface->GetWidget() |
| ->parent() |
| ->GetNativeWindow()); |
| gfx::Rect work_area = display.work_area(); |
| wm::ConvertRectFromScreen(shell_surface_data_->shell_surface->GetWidget() |
| ->parent() |
| ->GetNativeWindow(), |
| &work_area); |
| WaylandPositioner::Result position = positioner->CalculateBounds(work_area); |
| gfx::Point origin = position.origin; |
| views::View::ConvertPointToScreen( |
| shell_surface_data_->shell_surface->GetWidget() |
| ->parent() |
| ->widget_delegate() |
| ->GetContentsView(), |
| &origin); |
| |
| shell_surface_data_->shell_surface->SetOrigin(origin); |
| shell_surface_data_->shell_surface->SetSize(position.size); |
| |
| xdg_popup_send_configure(resource_, position.origin.x(), |
| position.origin.y(), position.size.width(), |
| position.size.height()); |
| xdg_surface_send_configure( |
| surface_resource_, shell_surface_data_->serial_tracker->GetNextSerial( |
| SerialTracker::EventType::OTHER_EVENT)); |
| } |
| |
| // Overridden from aura::WindowObserver: |
| void OnWindowDestroying(aura::Window* window) override { |
| window->RemoveObserver(this); |
| shell_surface_data_ = nullptr; |
| } |
| |
| private: |
| void OnClose() { |
| xdg_popup_send_popup_done(resource_); |
| wl_client_flush(wl_resource_get_client(resource_)); |
| } |
| |
| void OnConfigure( |
| const gfx::Size& size, |
| chromeos::WindowStateType state_type, |
| bool resizing, |
| bool activated, |
| std::optional<chromeos::WindowStateType> restore_state_type) { |
| // Nothing to do here as popups don't have additional configure state. |
| } |
| |
| const raw_ptr<wl_resource> resource_; |
| const raw_ptr<wl_resource, DanglingUntriaged> surface_resource_; |
| raw_ptr<WaylandXdgSurface> shell_surface_data_; |
| base::WeakPtrFactory<WaylandPopup> weak_ptr_factory_{this}; |
| }; |
| |
| void xdg_popup_destroy(wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| void xdg_popup_grab(wl_client* client, |
| wl_resource* resource, |
| wl_resource* seat, |
| uint32_t serial) { |
| GetUserDataAs<WaylandPopup>(resource)->Grab(); |
| } |
| |
| void xdg_popup_reposition(wl_client* client, |
| wl_resource* resource, |
| wl_resource* positioner, |
| uint32_t token) { |
| GetUserDataAs<WaylandPopup>(resource)->Reposition( |
| GetUserDataAs<WaylandPositioner>(positioner), token); |
| } |
| |
| const struct xdg_popup_interface xdg_popup_implementation = { |
| xdg_popup_destroy, xdg_popup_grab, xdg_popup_reposition}; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // xdg_surface_interface: |
| |
| void xdg_surface_destroy(wl_client* client, wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| void xdg_surface_get_toplevel(wl_client* client, |
| wl_resource* xdg_surface_resource, |
| uint32_t id) { |
| auto* shell_surface_data = |
| GetUserDataAs<WaylandXdgSurface>(xdg_surface_resource); |
| if (shell_surface_data->shell_surface->GetEnabled()) { |
| wl_resource_post_error(xdg_surface_resource, |
| XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED, |
| "surface has already been constructed"); |
| return; |
| } |
| |
| shell_surface_data->shell_surface->SetCanMinimize(true); |
| shell_surface_data->shell_surface->SetEnabled(true); |
| |
| wl_resource* xdg_toplevel_resource = |
| wl_resource_create(client, &xdg_toplevel_interface, 1, id); |
| |
| SetImplementation(xdg_toplevel_resource, &xdg_toplevel_implementation, |
| std::make_unique<WaylandToplevel>(xdg_toplevel_resource, |
| xdg_surface_resource)); |
| } |
| |
| void xdg_surface_get_popup(wl_client* client, |
| wl_resource* resource, |
| uint32_t id, |
| wl_resource* parent_resource, |
| wl_resource* positioner_resource) { |
| auto* shell_surface_data = GetUserDataAs<WaylandXdgSurface>(resource); |
| if (shell_surface_data->shell_surface->GetEnabled()) { |
| wl_resource_post_error(resource, XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED, |
| "surface has already been constructed"); |
| return; |
| } |
| |
| if (!parent_resource) { |
| wl_resource_post_error(resource, XDG_SURFACE_ERROR_NOT_CONSTRUCTED, |
| "popup parent not supplied"); |
| return; |
| } |
| |
| auto* parent_data = GetUserDataAs<WaylandXdgSurface>(parent_resource); |
| if (!parent_data->shell_surface->GetWidget()) { |
| wl_resource_post_error(resource, XDG_SURFACE_ERROR_NOT_CONSTRUCTED, |
| "popup parent not constructed"); |
| return; |
| } |
| |
| if (shell_surface_data->shell_surface->GetWidget()) { |
| wl_resource_post_error(resource, XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED, |
| "get_popup is called after constructed"); |
| return; |
| } |
| |
| display::Display display = |
| display::Screen::GetScreen()->GetDisplayNearestWindow( |
| parent_data->shell_surface->GetWidget()->GetNativeWindow()); |
| gfx::Rect work_area = display.work_area(); |
| wm::ConvertRectFromScreen( |
| parent_data->shell_surface->GetWidget()->GetNativeWindow(), &work_area); |
| |
| // Try layout using parent's flip state. |
| WaylandPositioner* positioner = |
| GetUserDataAs<WaylandPositioner>(positioner_resource); |
| WaylandPositioner::Result position = positioner->CalculateBounds(work_area); |
| |
| // |position| is relative to the parent's contents view origin, and |origin| |
| // is in screen coordinates. |
| gfx::Point origin = position.origin; |
| views::View::ConvertPointToScreen(parent_data->shell_surface->GetWidget() |
| ->widget_delegate() |
| ->GetContentsView(), |
| &origin); |
| shell_surface_data->shell_surface->SetOrigin(origin); |
| shell_surface_data->shell_surface->SetSize(position.size); |
| shell_surface_data->shell_surface->DisableMovement(); |
| shell_surface_data->shell_surface->SetActivatable(false); |
| shell_surface_data->shell_surface->SetCanMinimize(false); |
| shell_surface_data->shell_surface->SetParent( |
| parent_data->shell_surface.get()); |
| shell_surface_data->shell_surface->SetPopup(); |
| shell_surface_data->shell_surface->SetEnabled(true); |
| |
| wl_resource* xdg_popup_resource = wl_resource_create( |
| client, &xdg_popup_interface, wl_resource_get_version(resource), id); |
| |
| SetImplementation( |
| xdg_popup_resource, &xdg_popup_implementation, |
| std::make_unique<WaylandPopup>(xdg_popup_resource, resource)); |
| |
| // We send the configure event here as this event needs x,y coordinates |
| // relative to the parent window. |
| xdg_popup_send_configure(xdg_popup_resource, position.origin.x(), |
| position.origin.y(), position.size.width(), |
| position.size.height()); |
| } |
| |
| void xdg_surface_set_window_geometry(wl_client* client, |
| wl_resource* resource, |
| int32_t x, |
| int32_t y, |
| int32_t width, |
| int32_t height) { |
| GetUserDataAs<WaylandXdgSurface>(resource)->shell_surface->SetGeometry( |
| gfx::Rect(x, y, width, height)); |
| } |
| |
| void xdg_surface_ack_configure(wl_client* client, |
| wl_resource* resource, |
| uint32_t serial) { |
| GetUserDataAs<WaylandXdgSurface>(resource) |
| ->shell_surface->AcknowledgeConfigure(serial); |
| } |
| |
| const struct xdg_surface_interface xdg_surface_implementation = { |
| xdg_surface_destroy, xdg_surface_get_toplevel, xdg_surface_get_popup, |
| xdg_surface_set_window_geometry, xdg_surface_ack_configure}; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // xdg_wm_base_interface: |
| |
| void xdg_wm_base_destroy(wl_client* client, wl_resource* resource) { |
| // Nothing to do here. |
| } |
| |
| void xdg_wm_base_create_positioner(wl_client* client, |
| wl_resource* resource, |
| uint32_t id) { |
| wl_resource* positioner_resource = wl_resource_create( |
| client, &xdg_positioner_interface, wl_resource_get_version(resource), id); |
| |
| SetImplementation(positioner_resource, &xdg_positioner_implementation, |
| std::make_unique<WaylandPositioner>()); |
| } |
| |
| void xdg_wm_base_get_xdg_surface(wl_client* client, |
| wl_resource* resource, |
| uint32_t id, |
| wl_resource* surface) { |
| auto* data = GetUserDataAs<WaylandXdgShell>(resource); |
| std::unique_ptr<XdgShellSurface> shell_surface = |
| data->display->CreateXdgShellSurface(GetUserDataAs<Surface>(surface)); |
| if (!shell_surface) { |
| wl_resource_post_error(resource, XDG_WM_BASE_ERROR_ROLE, |
| "surface has already been assigned a role"); |
| return; |
| } |
| |
| // Xdg shell surfaces are initially disabled and needs to be explicitly mapped |
| // before they are enabled and can become visible. |
| shell_surface->SetEnabled(false); |
| |
| shell_surface->SetSecurityDelegate(GetSecurityDelegate(client)); |
| |
| std::unique_ptr<WaylandXdgSurface> wayland_shell_surface = |
| std::make_unique<WaylandXdgSurface>(std::move(shell_surface), |
| data->serial_tracker, |
| data->rotation_serial_tracker); |
| |
| wl_resource* xdg_surface_resource = wl_resource_create( |
| client, &xdg_surface_interface, wl_resource_get_version(resource), id); |
| |
| SetImplementation(xdg_surface_resource, &xdg_surface_implementation, |
| std::move(wayland_shell_surface)); |
| } |
| |
| void xdg_wm_base_pong(wl_client* client, |
| wl_resource* resource, |
| uint32_t serial) { |
| NOTIMPLEMENTED(); |
| } |
| |
| const struct xdg_wm_base_interface xdg_wm_base_implementation = { |
| xdg_wm_base_destroy, xdg_wm_base_create_positioner, |
| xdg_wm_base_get_xdg_surface, xdg_wm_base_pong}; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Top level decoration |
| void toplevel_decoration_handle_destroy(wl_client* client, |
| wl_resource* resource) { |
| wl_resource_destroy(resource); |
| } |
| |
| void toplevel_decoration_handle_set_mode(wl_client* client, |
| wl_resource* resource, |
| uint32_t mode) { |
| GetUserDataAs<WaylandXdgToplevelDecoration>(resource)->SetDecorationMode( |
| mode); |
| } |
| |
| void toplevel_decoration_handle_unset_mode(wl_client* client, |
| wl_resource* resource) { |
| NOTIMPLEMENTED(); |
| } |
| |
| const struct zxdg_toplevel_decoration_v1_interface toplevel_decoration_impl = { |
| .destroy = toplevel_decoration_handle_destroy, |
| .set_mode = toplevel_decoration_handle_set_mode, |
| .unset_mode = toplevel_decoration_handle_unset_mode, |
| }; |
| |
| // Decoration manager |
| void decoration_manager_handle_destroy(wl_client* client, |
| wl_resource* manager_resource) { |
| wl_resource_destroy(manager_resource); |
| } |
| |
| void decoration_manager_handle_get_toplevel_decoration( |
| wl_client* client, |
| wl_resource* manager_resource, |
| uint32_t id, |
| wl_resource* toplevel_resource) { |
| uint32_t version = wl_resource_get_version(manager_resource); |
| wl_resource* decoration_resource = wl_resource_create( |
| client, &zxdg_toplevel_decoration_v1_interface, version, id); |
| if (!decoration_resource) { |
| wl_client_post_no_memory(client); |
| return; |
| } |
| |
| auto xdg_toplevel_decoration = std::make_unique<WaylandXdgToplevelDecoration>( |
| decoration_resource, toplevel_resource); |
| |
| SetImplementation(decoration_resource, &toplevel_decoration_impl, |
| std::move(xdg_toplevel_decoration)); |
| } |
| |
| static const struct zxdg_decoration_manager_v1_interface |
| decoration_manager_impl = { |
| .destroy = decoration_manager_handle_destroy, |
| .get_toplevel_decoration = |
| decoration_manager_handle_get_toplevel_decoration, |
| }; |
| |
| } // namespace |
| |
| WaylandXdgSurface ::WaylandXdgSurface( |
| std::unique_ptr<XdgShellSurface> shell_surface, |
| SerialTracker* const serial_tracker, |
| SerialTracker* const rotation_serial_tracker) |
| : shell_surface(std::move(shell_surface)), |
| serial_tracker(serial_tracker), |
| rotation_serial_tracker(rotation_serial_tracker) {} |
| |
| WaylandXdgSurface::~WaylandXdgSurface() = default; |
| |
| void bind_zxdg_decoration_manager(wl_client* client, |
| void* data, |
| uint32_t version, |
| uint32_t id) { |
| wl_resource* resource = wl_resource_create( |
| client, &zxdg_decoration_manager_v1_interface, version, id); |
| |
| wl_resource_set_implementation(resource, &decoration_manager_impl, data, |
| nullptr); |
| } |
| |
| void bind_xdg_shell(wl_client* client, |
| void* data, |
| uint32_t version, |
| uint32_t id) { |
| wl_resource* resource = wl_resource_create(client, &xdg_wm_base_interface, |
| std::min(3u, version), id); |
| |
| wl_resource_set_implementation(resource, &xdg_wm_base_implementation, data, |
| nullptr); |
| } |
| |
| ShellSurfaceData GetShellSurfaceFromToplevelResource(wl_resource* resource) { |
| auto* toplevel = GetUserDataAs<WaylandToplevel>(resource); |
| return toplevel->GetShellSurfaceData(); |
| } |
| |
| ShellSurfaceBase* GetShellSurfaceFromPopupResource(wl_resource* resource) { |
| auto* popup = GetUserDataAs<WaylandPopup>(resource); |
| return popup->GetShellSurface(); |
| } |
| |
| } // namespace wayland |
| } // namespace exo |