From 1433028519e48c211a4bb6730a3574dc410317e1 Mon Sep 17 00:00:00 2001 From: Justin Huang Date: Wed, 29 Mar 2023 05:52:19 +0000 Subject: [PATCH] cherrypick to M113: Revert "Removes zxdg_shell" This reverts commit 37647e0b546a4d2ba210019f7232d5038f3c2f1f. Reason for revert: b/273702996. Parallels uses zxdg_shell :/ Bug: b:273702996 Fixes: b:273702996 Original change's description: > Removes zxdg_shell > > Sommelier has stopped using this and Lacros doesn't use it so it doesn't seem like it's being used anywhere > > Bug: N/A > Change-Id: I4e9daf3b4a39de3d19cac83c3f89c16ec4b93cca > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4305659 > Commit-Queue: Justin Huang > Reviewed-by: Mitsuru Oshima > Cr-Commit-Position: refs/heads/main@{#1113197} (cherry picked from commit cee6ba358ac097a8361e7840443f61ce64eb54b3) Bug: N/A Change-Id: I9ac23cafd88f1717ed78ec60fb4b04d3101f4365 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4369248 Commit-Queue: Justin Huang Reviewed-by: Mitsuru Oshima Cr-Original-Commit-Position: refs/heads/main@{#1122780} Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4378700 Cr-Commit-Position: refs/branch-heads/5672@{#92} Cr-Branched-From: 5f2a72468eda1eb945b3b5a2298b5d1cd678521e-refs/heads/main@{#1121455} --- components/exo/wayland/BUILD.gn | 2 + components/exo/wayland/clients/client_base.cc | 80 +- components/exo/wayland/clients/client_base.h | 2 + .../exo/wayland/clients/client_helper.cc | 3 + .../exo/wayland/clients/client_helper.h | 4 + components/exo/wayland/clients/globals.cc | 1 + components/exo/wayland/clients/globals.h | 1 + .../clients/security_delegate_binding_test.cc | 38 + .../clients/test/client_version_test.cc | 3 + .../exo/wayland/compatibility_test/BUILD.gn | 1 + components/exo/wayland/fuzzer/BUILD.gn | 1 + .../exo/wayland/fuzzer/wayland_sequencer.py | 20 +- components/exo/wayland/server.cc | 7 + components/exo/wayland/server.h | 2 + components/exo/wayland/test/test_client.h | 1 + components/exo/wayland/wayland_positioner.cc | 64 +- components/exo/wayland/wayland_positioner.h | 13 +- .../wayland/wayland_positioner_unittest.cc | 224 +++++- components/exo/wayland/xdg_shell.cc | 5 +- components/exo/wayland/zxdg_shell.cc | 690 ++++++++++++++++++ components/exo/wayland/zxdg_shell.h | 40 + 21 files changed, 1153 insertions(+), 49 deletions(-) create mode 100644 components/exo/wayland/zxdg_shell.cc create mode 100644 components/exo/wayland/zxdg_shell.h diff --git a/components/exo/wayland/BUILD.gn b/components/exo/wayland/BUILD.gn index fbcfad36c643a..c1ad58c1d6b24 100644 --- a/components/exo/wayland/BUILD.gn +++ b/components/exo/wayland/BUILD.gn @@ -132,6 +132,8 @@ source_set("wayland") { "zxdg_decoration_manager.h", "zxdg_output_manager.cc", "zxdg_output_manager.h", + "zxdg_shell.cc", + "zxdg_shell.h", ] defines = [ "EXO_IMPLEMENTATION" ] diff --git a/components/exo/wayland/clients/client_base.cc b/components/exo/wayland/clients/client_base.cc index 7a58b2cde47b2..695c09ec7b8a7 100644 --- a/components/exo/wayland/clients/client_base.cc +++ b/components/exo/wayland/clients/client_base.cc @@ -619,29 +619,65 @@ bool ClientBase::Init(const InitParams& params) { wl_shell_surface_set_toplevel(shell_surface.get()); } } else { - xdg_surface_.reset(xdg_wm_base_get_xdg_surface(globals_.xdg_wm_base.get(), - surface_.get())); - if (!xdg_surface_) { - LOG(ERROR) << "Can't get xdg surface"; - return false; - } - static const xdg_surface_listener xdg_surface_listener = { - [](void* data, struct xdg_surface* xdg_surface, uint32_t layout_mode) { - xdg_surface_ack_configure(xdg_surface, layout_mode); - }, - }; - xdg_surface_add_listener(xdg_surface_.get(), &xdg_surface_listener, this); - xdg_toplevel_.reset(xdg_surface_get_toplevel(xdg_surface_.get())); - if (!xdg_toplevel_) { - LOG(ERROR) << "Can't get xdg toplevel"; - return false; + if (!globals_.xdg_wm_base) { + // use zxdg + if (!globals_.xdg_shell_v6) { + LOG(ERROR) << "Can't find xdg_shell or zxdg_shell_v6 interface"; + return false; + } + + zxdg_surface_.reset(zxdg_shell_v6_get_xdg_surface( + globals_.xdg_shell_v6.get(), surface_.get())); + if (!zxdg_surface_) { + LOG(ERROR) << "Can't get zxdg surface"; + return false; + } + static const zxdg_surface_v6_listener zxdg_surface_v6_listener = { + [](void* data, struct zxdg_surface_v6* zxdg_surface_v6, + uint32_t layout_mode) { + zxdg_surface_v6_ack_configure(zxdg_surface_v6, layout_mode); + }, + }; + zxdg_surface_v6_add_listener(zxdg_surface_.get(), + &zxdg_surface_v6_listener, this); + zxdg_toplevel_.reset(zxdg_surface_v6_get_toplevel(zxdg_surface_.get())); + if (!zxdg_toplevel_) { + LOG(ERROR) << "Can't get zxdg toplevel"; + return false; + } + static const zxdg_toplevel_v6_listener zxdg_toplevel_v6_listener = { + [](void* data, struct zxdg_toplevel_v6* zxdg_toplevel_v6, + int32_t width, int32_t height, struct wl_array* states) {}, + [](void* data, struct zxdg_toplevel_v6* zxdg_toplevel_v6) {}}; + zxdg_toplevel_v6_add_listener(zxdg_toplevel_.get(), + &zxdg_toplevel_v6_listener, this); + } else { + // use xdg + xdg_surface_.reset(xdg_wm_base_get_xdg_surface(globals_.xdg_wm_base.get(), + surface_.get())); + if (!xdg_surface_) { + LOG(ERROR) << "Can't get xdg surface"; + return false; + } + static const xdg_surface_listener xdg_surface_listener = { + [](void* data, struct xdg_surface* xdg_surface, + uint32_t layout_mode) { + xdg_surface_ack_configure(xdg_surface, layout_mode); + }, + }; + xdg_surface_add_listener(xdg_surface_.get(), &xdg_surface_listener, this); + xdg_toplevel_.reset(xdg_surface_get_toplevel(xdg_surface_.get())); + if (!xdg_toplevel_) { + LOG(ERROR) << "Can't get xdg toplevel"; + return false; + } + static const xdg_toplevel_listener xdg_toplevel_listener = { + [](void* data, struct xdg_toplevel* xdg_toplevel, int32_t width, + int32_t height, struct wl_array* states) {}, + [](void* data, struct xdg_toplevel* xdg_toplevel) {}}; + xdg_toplevel_add_listener(xdg_toplevel_.get(), &xdg_toplevel_listener, + this); } - static const xdg_toplevel_listener xdg_toplevel_listener = { - [](void* data, struct xdg_toplevel* xdg_toplevel, int32_t width, - int32_t height, struct wl_array* states) {}, - [](void* data, struct xdg_toplevel* xdg_toplevel) {}}; - xdg_toplevel_add_listener(xdg_toplevel_.get(), &xdg_toplevel_listener, - this); if (fullscreen_) { LOG(ERROR) << "full screen not supported yet."; diff --git a/components/exo/wayland/clients/client_base.h b/components/exo/wayland/clients/client_base.h index a2f92a0a0c541..fab7537d065c8 100644 --- a/components/exo/wayland/clients/client_base.h +++ b/components/exo/wayland/clients/client_base.h @@ -203,6 +203,8 @@ class ClientBase { std::unique_ptr shell_surface_; std::unique_ptr xdg_surface_; std::unique_ptr xdg_toplevel_; + std::unique_ptr zxdg_surface_; + std::unique_ptr zxdg_toplevel_; std::unique_ptr wl_pointer_; std::unique_ptr zcr_pointer_stylus_; Globals globals_; diff --git a/components/exo/wayland/clients/client_helper.cc b/components/exo/wayland/clients/client_helper.cc index 3659802e6f4f6..1dffd0e6bd04c 100644 --- a/components/exo/wayland/clients/client_helper.cc +++ b/components/exo/wayland/clients/client_helper.cc @@ -83,6 +83,7 @@ DEFAULT_DELETER(wl_data_device_manager, wl_data_device_manager_destroy) DEFAULT_DELETER(wp_content_type_manager_v1, wp_content_type_manager_v1_destroy) DEFAULT_DELETER(wp_content_type_v1, wp_content_type_v1_destroy) DEFAULT_DELETER(wp_viewporter, wp_viewporter_destroy) +DEFAULT_DELETER(zxdg_shell_v6, zxdg_shell_v6_destroy) DEFAULT_DELETER(xdg_wm_base, xdg_wm_base_destroy) DEFAULT_DELETER(zwp_text_input_manager_v1, zwp_text_input_manager_v1_destroy) DEFAULT_DELETER(zcr_secure_output_v1, zcr_secure_output_v1_destroy) @@ -111,6 +112,8 @@ DEFAULT_DELETER(zxdg_decoration_manager_v1, zxdg_decoration_manager_v1_destroy) DEFAULT_DELETER(zcr_extended_drag_v1, zcr_extended_drag_v1_destroy) DEFAULT_DELETER(xdg_surface, xdg_surface_destroy) DEFAULT_DELETER(xdg_toplevel, xdg_toplevel_destroy) +DEFAULT_DELETER(zxdg_surface_v6, zxdg_surface_v6_destroy) +DEFAULT_DELETER(zxdg_toplevel_v6, zxdg_toplevel_v6_destroy) DEFAULT_DELETER(zxdg_output_manager_v1, zxdg_output_manager_v1_destroy) DEFAULT_DELETER(weston_test, weston_test_destroy) DEFAULT_DELETER(zwp_idle_inhibit_manager_v1, diff --git a/components/exo/wayland/clients/client_helper.h b/components/exo/wayland/clients/client_helper.h index cc2371a6b1b6b..c8c719fe083e4 100644 --- a/components/exo/wayland/clients/client_helper.h +++ b/components/exo/wayland/clients/client_helper.h @@ -43,6 +43,7 @@ #include #include #include +#include #include "base/scoped_generic.h" #if defined(USE_GBM) @@ -105,6 +106,7 @@ DEFAULT_DELETER_FDECL(wl_data_device_manager) DEFAULT_DELETER_FDECL(wp_content_type_manager_v1) DEFAULT_DELETER_FDECL(wp_content_type_v1) DEFAULT_DELETER_FDECL(wp_viewporter) +DEFAULT_DELETER_FDECL(zxdg_shell_v6) DEFAULT_DELETER_FDECL(xdg_wm_base) DEFAULT_DELETER_FDECL(zwp_text_input_manager_v1) DEFAULT_DELETER_FDECL(zcr_secure_output_v1) @@ -129,6 +131,8 @@ DEFAULT_DELETER_FDECL(zxdg_decoration_manager_v1) DEFAULT_DELETER_FDECL(zcr_extended_drag_v1) DEFAULT_DELETER_FDECL(xdg_surface) DEFAULT_DELETER_FDECL(xdg_toplevel) +DEFAULT_DELETER_FDECL(zxdg_surface_v6) +DEFAULT_DELETER_FDECL(zxdg_toplevel_v6) DEFAULT_DELETER_FDECL(zxdg_output_manager_v1) DEFAULT_DELETER_FDECL(weston_test) DEFAULT_DELETER_FDECL(zwp_idle_inhibit_manager_v1) diff --git a/components/exo/wayland/clients/globals.cc b/components/exo/wayland/clients/globals.cc index 8f96511417fd3..aab2033cd6471 100644 --- a/components/exo/wayland/clients/globals.cc +++ b/components/exo/wayland/clients/globals.cc @@ -53,6 +53,7 @@ void RegistryHandler(void* data, BIND(wl_output, output) BIND(zwp_linux_explicit_synchronization_v1, linux_explicit_synchronization) BIND(zcr_vsync_feedback_v1, vsync_feedback) + BIND(zxdg_shell_v6, xdg_shell_v6) BIND(xdg_wm_base, xdg_wm_base) BIND(zcr_stylus_v2, stylus) BIND(zcr_remote_shell_v1, cr_remote_shell_v1) diff --git a/components/exo/wayland/clients/globals.h b/components/exo/wayland/clients/globals.h index 654d1a2ea9f9e..31c9d41810750 100644 --- a/components/exo/wayland/clients/globals.h +++ b/components/exo/wayland/clients/globals.h @@ -59,6 +59,7 @@ struct Globals { Object touch; Object aura_shell; Object aura_output; + Object xdg_shell_v6; Object xdg_wm_base; Object fullscreen_shell; Object input_timestamps_manager; diff --git a/components/exo/wayland/clients/security_delegate_binding_test.cc b/components/exo/wayland/clients/security_delegate_binding_test.cc index f5be74750cbc4..3cd36cd77cb3e 100644 --- a/components/exo/wayland/clients/security_delegate_binding_test.cc +++ b/components/exo/wayland/clients/security_delegate_binding_test.cc @@ -7,6 +7,7 @@ #include #include #include +#include #include "components/exo/client_controlled_shell_surface.h" #include "components/exo/shell_surface.h" @@ -109,6 +110,43 @@ TEST_F(SecurityDelegateBindingTest, XdgSurfaceHasSecurityDelegate) { nullptr); } +TEST_F(SecurityDelegateBindingTest, ZxdgSurfaceV6HasSecurityDelegate) { + class ClientData : public test::TestClient::CustomData { + public: + std::unique_ptr surface; + std::unique_ptr zxdg_surface; + }; + + test::ResourceKey zxdg_surface_key; + + PostToClientAndWait([&](test::TestClient* client) { + auto data = std::make_unique(); + + data->surface.reset(wl_compositor_create_surface(client->compositor())); + data->zxdg_surface.reset(zxdg_shell_v6_get_xdg_surface( + client->xdg_shell_v6(), data->surface.get())); + + zxdg_surface_key = + test::client_util::GetResourceKey(data->zxdg_surface.get()); + + client->set_data(std::move(data)); + }); + + EXPECT_EQ(test::server_util::GetUserDataForResource( + server_.get(), zxdg_surface_key) + ->shell_surface->GetSecurityDelegate(), + server_security_delegate_); + + PostToClientAndWait([](test::TestClient* client) { + // Destroy the client objects. + client->set_data(nullptr); + }); + + EXPECT_EQ(test::server_util::GetUserDataForResource( + server_.get(), zxdg_surface_key), + nullptr); +} + TEST_F(SecurityDelegateBindingTest, ZcrRemoteSurfaceV1HasSecurityDelegate) { class ClientData : public test::TestClient::CustomData { public: diff --git a/components/exo/wayland/clients/test/client_version_test.cc b/components/exo/wayland/clients/test/client_version_test.cc index 441f7cfd74b17..e8c3f52f3e6b0 100644 --- a/components/exo/wayland/clients/test/client_version_test.cc +++ b/components/exo/wayland/clients/test/client_version_test.cc @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -80,6 +81,7 @@ struct Globals { std::unique_ptr wl_data_device_manager; std::unique_ptr wp_content_type_manager_v1; std::unique_ptr wp_viewporter; + std::unique_ptr zxdg_shell_v6; std::unique_ptr xdg_wm_base; std::unique_ptr zwp_text_input_manager_v1; std::unique_ptr zcr_secure_output_v1; @@ -174,6 +176,7 @@ void RegistryHandler(void* data, REGISTRY_CALLBACK(wp_content_type_manager_v1, wp_content_type_manager_v1), REGISTRY_CALLBACK(wp_viewporter, wp_viewporter), + REGISTRY_CALLBACK(zxdg_shell_v6, zxdg_shell_v6), REGISTRY_CALLBACK(xdg_wm_base, xdg_wm_base), REGISTRY_CALLBACK(zwp_text_input_manager_v1, zwp_text_input_manager_v1), diff --git a/components/exo/wayland/compatibility_test/BUILD.gn b/components/exo/wayland/compatibility_test/BUILD.gn index ac1441f701435..61498f5164d0b 100644 --- a/components/exo/wayland/compatibility_test/BUILD.gn +++ b/components/exo/wayland/compatibility_test/BUILD.gn @@ -20,6 +20,7 @@ server_wayland_protocols = [ "//third_party/wayland-protocols/src/unstable/pointer-gestures/pointer-gestures-unstable-v1.xml", "//third_party/wayland-protocols/src/unstable/relative-pointer/relative-pointer-unstable-v1.xml", "//third_party/wayland-protocols/src/unstable/text-input/text-input-unstable-v1.xml", + "//third_party/wayland-protocols/src/unstable/xdg-shell/xdg-shell-unstable-v6.xml", "//third_party/wayland-protocols/unstable/alpha-compositing/alpha-compositing-unstable-v1.xml", "//third_party/wayland-protocols/unstable/cursor-shapes/cursor-shapes-unstable-v1.xml", "//third_party/wayland-protocols/unstable/gaming-input/gaming-input-unstable-v2.xml", diff --git a/components/exo/wayland/fuzzer/BUILD.gn b/components/exo/wayland/fuzzer/BUILD.gn index b485480020cb2..68e86414d15f2 100644 --- a/components/exo/wayland/fuzzer/BUILD.gn +++ b/components/exo/wayland/fuzzer/BUILD.gn @@ -23,6 +23,7 @@ kDefaultWaylandProtocols = [ "//third_party/wayland-protocols/src/unstable/pointer-gestures/pointer-gestures-unstable-v1.xml", "//third_party/wayland-protocols/src/unstable/relative-pointer/relative-pointer-unstable-v1.xml", "//third_party/wayland-protocols/src/unstable/text-input/text-input-unstable-v1.xml", + "//third_party/wayland-protocols/src/unstable/xdg-shell/xdg-shell-unstable-v6.xml", "//third_party/wayland-protocols/unstable/alpha-compositing/alpha-compositing-unstable-v1.xml", "//third_party/wayland-protocols/unstable/cursor-shapes/cursor-shapes-unstable-v1.xml", "//third_party/wayland-protocols/unstable/gaming-input/gaming-input-unstable-v2.xml", diff --git a/components/exo/wayland/fuzzer/wayland_sequencer.py b/components/exo/wayland/fuzzer/wayland_sequencer.py index 87f5256ff5608..c2e46f87ae5fd 100644 --- a/components/exo/wayland/fuzzer/wayland_sequencer.py +++ b/components/exo/wayland/fuzzer/wayland_sequencer.py @@ -181,7 +181,25 @@ def GetManualSequences(dep_graph): c.RecordInterfaceCreated('wl_data_offer') e = SequenceBuilder('empty', dep_graph) - return [c, e] + + p = SequenceBuilder('popup_configuration', dep_graph) + p_positioner = p.BuildInterface('zxdg_positioner_v6') + p_parent = p.BuildInterface('zxdg_toplevel_v6') + p.AppendCall('zxdg_surface_v6', 'set_window_geometry', + [('receiver', p_parent), ('x', 0), ('y', 0), ('width', 10), + ('height', 10)]) + p.AppendRoundTrip() + p.AppendCall('wl_surface', 'commit', [('receiver', p_parent)]) + p.AppendRoundTrip() + p.AppendCall('zxdg_surface_v6', 'ack_configure', [('receiver', p_parent), + ('serial', 1)]) + p_child = p.BuildInterface('zxdg_surface_v6') + p.AppendCall('zxdg_surface_v6', 'get_popup', [('receiver', p_child), + ('parent', p_parent), + ('positioner', p_positioner)]) + p.AppendRoundTrip() + + return [c, e, p] def SequenceToTemplate(parsed_arguments, builder): diff --git a/components/exo/wayland/server.cc b/components/exo/wayland/server.cc index dbcd6857b88ec..554eeccb198c4 100644 --- a/components/exo/wayland/server.cc +++ b/components/exo/wayland/server.cc @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -106,6 +107,7 @@ #include "components/exo/wayland/zwp_text_input_manager.h" #include "components/exo/wayland/zxdg_decoration_manager.h" #include "components/exo/wayland/zxdg_output_manager.h" +#include "components/exo/wayland/zxdg_shell.h" #include "ui/display/display.h" #include "ui/display/screen.h" #include "ui/ozone/public/ozone_platform.h" @@ -430,6 +432,11 @@ void Server::Initialize() { zcr_text_input_extension_data_.get(), bind_text_input_extension); + zxdg_shell_data_ = + std::make_unique(display_, serial_tracker_.get()); + wl_global_create(wl_display_.get(), &zxdg_shell_v6_interface, 1, + zxdg_shell_data_.get(), bind_zxdg_shell_v6); + xdg_shell_data_ = std::make_unique(display_, serial_tracker_.get()); wl_global_create(wl_display_.get(), &xdg_wm_base_interface, 3, diff --git a/components/exo/wayland/server.h b/components/exo/wayland/server.h index 65d4e58e4726c..82b2a787a8cd4 100644 --- a/components/exo/wayland/server.h +++ b/components/exo/wayland/server.h @@ -35,6 +35,7 @@ struct WaylandSeat; struct WaylandTextInputExtension; struct WaylandTextInputManager; struct WaylandXdgShell; +struct WaylandZxdgShell; struct WaylandRemoteShellData; class WaylandDmabufFeedbackManager; class WestonTest; @@ -142,6 +143,7 @@ class Server : public display::DisplayObserver { std::unique_ptr zcr_keyboard_extension_data_; std::unique_ptr zwp_text_manager_data_; std::unique_ptr zcr_text_input_extension_data_; + std::unique_ptr zxdg_shell_data_; std::unique_ptr xdg_shell_data_; std::unique_ptr remote_shell_data_; std::unique_ptr weston_test_holder_; diff --git a/components/exo/wayland/test/test_client.h b/components/exo/wayland/test/test_client.h index 199549f287269..3cedf4ab8e2be 100644 --- a/components/exo/wayland/test/test_client.h +++ b/components/exo/wayland/test/test_client.h @@ -62,6 +62,7 @@ class TestClient { wl_touch* touch() { return globals().touch.get(); } zaura_shell* aura_shell() { return globals().aura_shell.get(); } zaura_output* aura_output() { return globals().aura_output.get(); } + zxdg_shell_v6* xdg_shell_v6() { return globals().xdg_shell_v6.get(); } xdg_wm_base* xdg_wm_base() { return globals().xdg_wm_base.get(); } zwp_fullscreen_shell_v1* fullscreen_shell() { return globals().fullscreen_shell.get(); diff --git a/components/exo/wayland/wayland_positioner.cc b/components/exo/wayland/wayland_positioner.cc index 5b282f08c34a3..874a896ffa25a 100644 --- a/components/exo/wayland/wayland_positioner.cc +++ b/components/exo/wayland/wayland_positioner.cc @@ -6,12 +6,37 @@ #include +#include + namespace exo::wayland { namespace { std::pair -DecomposeAnchor(uint32_t anchor) { +DecomposeUnstableAnchor(uint32_t anchor) { + WaylandPositioner::Direction x, y; + + if (anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT) { + x = WaylandPositioner::Direction::kNegative; + } else if (anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT) { + x = WaylandPositioner::Direction::kPositive; + } else { + x = WaylandPositioner::Direction::kNeutral; + } + + if (anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP) { + y = WaylandPositioner::Direction::kNegative; + } else if (anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM) { + y = WaylandPositioner::Direction::kPositive; + } else { + y = WaylandPositioner::Direction::kNeutral; + } + + return std::make_pair(x, y); +} + +std::pair +DecomposeStableAnchor(uint32_t anchor) { switch (anchor) { default: case XDG_POSITIONER_ANCHOR_NONE: @@ -45,7 +70,30 @@ DecomposeAnchor(uint32_t anchor) { } std::pair -DecomposeGravity(uint32_t gravity) { +DecomposeUnstableGravity(uint32_t gravity) { + WaylandPositioner::Direction x, y; + + if (gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) { + x = WaylandPositioner::Direction::kNegative; + } else if (gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT) { + x = WaylandPositioner::Direction::kPositive; + } else { + x = WaylandPositioner::Direction::kNeutral; + } + + if (gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) { + y = WaylandPositioner::Direction::kNegative; + } else if (gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM) { + y = WaylandPositioner::Direction::kPositive; + } else { + y = WaylandPositioner::Direction::kNeutral; + } + + return std::make_pair(x, y); +} + +std::pair +DecomposeStableGravity(uint32_t gravity) { switch (gravity) { default: case XDG_POSITIONER_GRAVITY_NONE: @@ -289,7 +337,11 @@ std::pair DetermineBestConstraintAdjustment( void WaylandPositioner::SetAnchor(uint32_t anchor) { std::pair decompose; - decompose = DecomposeAnchor(anchor); + if (version_ == UNSTABLE) { + decompose = DecomposeUnstableAnchor(anchor); + } else { + decompose = DecomposeStableAnchor(anchor); + } anchor_x_ = decompose.first; anchor_y_ = decompose.second; } @@ -297,7 +349,11 @@ void WaylandPositioner::SetAnchor(uint32_t anchor) { void WaylandPositioner::SetGravity(uint32_t gravity) { std::pair decompose; - decompose = DecomposeGravity(gravity); + if (version_ == UNSTABLE) { + decompose = DecomposeUnstableGravity(gravity); + } else { + decompose = DecomposeStableGravity(gravity); + } gravity_x_ = decompose.first; gravity_y_ = decompose.second; } diff --git a/components/exo/wayland/wayland_positioner.h b/components/exo/wayland/wayland_positioner.h index 8f53d16dda500..7ff5143a0360c 100644 --- a/components/exo/wayland/wayland_positioner.h +++ b/components/exo/wayland/wayland_positioner.h @@ -26,7 +26,12 @@ class WaylandPositioner { // Represents the 1-dimensional projection of the gravity/anchor values. enum Direction { kNegative = -1, kNeutral = 0, kPositive = 1 }; - WaylandPositioner() = default; + // Controls whether anchor and gravity are set using the unstable bitfields or + // the stable enums. + enum Version { UNSTABLE, STABLE }; + + WaylandPositioner(Version v) : version_(v) {} + WaylandPositioner(const WaylandPositioner&) = delete; WaylandPositioner& operator=(const WaylandPositioner&) = delete; @@ -48,6 +53,8 @@ class WaylandPositioner { void SetOffset(gfx::Vector2d offset) { offset_ = std::move(offset); } private: + Version version_; + gfx::Size size_; gfx::Rect anchor_rect_; @@ -59,13 +66,13 @@ class WaylandPositioner { Direction gravity_y_ = kNeutral; // A bitmask that defines the subset of modifications to the position/size - // that are allowed, see xdg_positioner.constraint_adjustment() for more + // that are allowed, see zxdg_positioner.constraint_adjustment() for more // details. uint32_t adjustment_ = XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE; // Defines an absolute translation (i.e. unaffected by flipping, scaling or // resizing) for the placement of the window relative to the |anchor_rect_|. - // See xdg_positioner.set_offset() for more details. + // See zxdg_positioner.set_offset() for more details. gfx::Vector2d offset_; }; diff --git a/components/exo/wayland/wayland_positioner_unittest.cc b/components/exo/wayland/wayland_positioner_unittest.cc index e44e474a4a1b4..fcb283b311d7a 100644 --- a/components/exo/wayland/wayland_positioner_unittest.cc +++ b/components/exo/wayland/wayland_positioner_unittest.cc @@ -7,6 +7,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/geometry/rect.h" #include "xdg-shell-server-protocol.h" +#include "xdg-shell-unstable-v6-server-protocol.h" namespace exo { namespace wayland { @@ -20,7 +21,9 @@ class WaylandPositionerTest : public testing::Test { WaylandPositioner positioner; gfx::Rect work_area = {0, 0, 5, 5}; - explicit TestCaseBuilder() { positioner.SetAnchorRect({2, 2, 1, 1}); } + explicit TestCaseBuilder(WaylandPositioner::Version v) : positioner(v) { + positioner.SetAnchorRect({2, 2, 1, 1}); + } TestCaseBuilder& SetFlipState(bool x, bool y) { return *this; @@ -68,31 +71,218 @@ class WaylandPositionerTest : public testing::Test { }; }; +// Tests for the unstable protocol. + +TEST_F(WaylandPositionerTest, UnconstrainedCasesUnstable) { + // No gravity or anchor. + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetSize(1, 1) + .SolveToRect(), + gfx::Rect(2, 2, 1, 1)); + + // Anchor without gravity. + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetSize(2, 1) + .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_RIGHT) + .SolveToRect(), + gfx::Rect(2, 2, 2, 1)); + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetSize(2, 1) + .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_LEFT) + .SolveToRect(), + gfx::Rect(1, 2, 2, 1)); + + // Gravity without anchor. + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetSize(1, 2) + .SetAnchorRect(2, 2, 0, 0) + .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_TOP) + .SolveToRect(), + gfx::Rect(2, 0, 1, 2)); + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetSize(1, 2) + .SetAnchorRect(2, 2, 0, 0) + .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM) + .SolveToRect(), + gfx::Rect(2, 2, 1, 2)); + + // Gravity + anchor in the same direction. + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetSize(2, 2) + .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_LEFT) + .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | + ZXDG_POSITIONER_V6_ANCHOR_LEFT) + .SolveToRect(), + gfx::Rect(0, 3, 2, 2)); + + // Gravity + anchor in opposing directions. + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetSize(2, 2) + .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_LEFT) + .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_TOP | + ZXDG_POSITIONER_V6_ANCHOR_RIGHT) + .SolveToRect(), + gfx::Rect(1, 2, 2, 2)); +} + +TEST_F(WaylandPositionerTest, FlipSlideResizePriorityUnstable) { + TestCaseBuilder builder{WaylandPositioner::Version::UNSTABLE}; + builder.SetAnchorRect(4, 4, 0, 0) + .SetSize(2, 2) + .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_RIGHT) + .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | + ZXDG_POSITIONER_V6_ANCHOR_RIGHT); + // Flip is enabled, so the result will be at 2,2 (i.e. flipping a 2-wide + // square around 4,4). + EXPECT_EQ( + builder.SetAdjustment(~ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE) + .SolveToRect(), + gfx::Rect(2, 2, 2, 2)); + // If we cant flip on an axis, that axis will slide to 3 instead. + EXPECT_EQ( + builder.SetAdjustment(~ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X) + .SolveToRect(), + gfx::Rect(3, 2, 2, 2)); + EXPECT_EQ( + builder.SetAdjustment(~ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y) + .SolveToRect(), + gfx::Rect(2, 3, 2, 2)); + // If we cant flip or slide, we resize. + EXPECT_EQ( + builder + .SetAdjustment(ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X | + ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y) + .SolveToRect(), + gfx::Rect(4, 4, 1, 1)); +} + +TEST_F(WaylandPositionerTest, TriesToMaximizeAreaUnstable) { + // The size is too large to fit where the anchor is. + WaylandPositioner::Result result = + TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetAnchorRect(2, 4, 0, 0) + .SetSize(4, 10) + .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_RIGHT) + .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | + ZXDG_POSITIONER_V6_ANCHOR_RIGHT) + .SetAdjustment(~ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE) + .Solve(); + // We can slide to 1 on x, but we must resize on y (after sliding to 0). + EXPECT_EQ(result.origin, gfx::Point(1, 0)); + // The x size will be preserved but y shrinks to the work area. + EXPECT_EQ(result.size, gfx::Size(4, 5)); +} + +TEST_F(WaylandPositionerTest, PropagatesAnInitialFlipUnstable) { + WaylandPositioner::Result result = + TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetAnchorRect(3, 1, 0, 0) + .SetSize(2, 2) + .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_RIGHT) + .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | + ZXDG_POSITIONER_V6_ANCHOR_RIGHT) + .SetAdjustment(~ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE) + .SetFlipState(true, true) + .Solve(); + // With a propagated flip state: + // - X and Y remain flipped to be positioned by the client. + EXPECT_EQ(result.origin, gfx::Point(3, 1)); + EXPECT_EQ(result.size, gfx::Size(2, 2)); +} + +// This is a common case for dropdown menus. In ChromeOS we do not let them +// slide if they might occlude the anchor rectangle. For this case, x axis does +// slide but the y axis resized instead. +TEST_F(WaylandPositionerTest, PreventsSlidingThatOccludesAnchorRectUnstable) { + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetSize(3, 3) + .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_RIGHT) + .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | + ZXDG_POSITIONER_V6_ANCHOR_LEFT) + .SetAdjustment(~ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE) + .SolveToRect(), + gfx::Rect(2, 3, 3, 2)); + + // Here we ensure that the 4x4 popup does slide, which is allowed because + // the anchor rect is already occluded. + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetSize(4, 4) + .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_RIGHT) + .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_TOP | + ZXDG_POSITIONER_V6_ANCHOR_LEFT) + .SetAdjustment(~ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE) + .SolveToRect(), + gfx::Rect(1, 1, 4, 4)); +} + +// Allowing sliding which will occlude the anchor if there are no other +// positioning options which do not result in a constrained view available. +TEST_F(WaylandPositionerTest, + AllowsSlidingThatOccludesWhenThereAreNoOtherOptionsUnstable) { + EXPECT_EQ( + TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetSize(4, 4) + .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT) + .SetAnchor(XDG_POSITIONER_ANCHOR_BOTTOM_LEFT) + // Disable resizing in both axes which will force sliding. + .SetAdjustment(~(ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X | + ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y)) + .SolveToRect(), + gfx::Rect(1, 1, 4, 4)); +} + +TEST_F(WaylandPositionerTest, + AllowsAdditionalAdjustmentsIfNoSolutionCanBeFoundUnstable) { + EXPECT_EQ( + TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetWorkArea(gfx::Rect(5, 5)) + .SetSize(10, 10) + .SetAnchorRect(0, 0, 0, 0) + .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_RIGHT) + // No solution should forcibly allow resize + .SetAdjustment(ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X | + ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y) + .SolveToRect(), + gfx::Rect(0, 0, 5, 5)); +} + +// Tests for the stable protocol. + TEST_F(WaylandPositionerTest, UnconstrainedCases) { // No gravity or anchor. - EXPECT_EQ(TestCaseBuilder().SetSize(1, 1).SolveToRect(), + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) + .SetSize(1, 1) + .SolveToRect(), gfx::Rect(2, 2, 1, 1)); // Anchor without gravity. - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetSize(2, 1) .SetAnchor(XDG_POSITIONER_ANCHOR_RIGHT) .SolveToRect(), gfx::Rect(2, 2, 2, 1)); - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetSize(2, 1) .SetAnchor(XDG_POSITIONER_ANCHOR_LEFT) .SolveToRect(), gfx::Rect(1, 2, 2, 1)); // Gravity without anchor. - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetSize(1, 2) .SetAnchorRect(2, 2, 0, 0) .SetGravity(XDG_POSITIONER_GRAVITY_TOP) .SolveToRect(), gfx::Rect(2, 0, 1, 2)); - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetSize(1, 2) .SetAnchorRect(2, 2, 0, 0) .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM) @@ -100,7 +290,7 @@ TEST_F(WaylandPositionerTest, UnconstrainedCases) { gfx::Rect(2, 2, 1, 2)); // Gravity + anchor in the same direction. - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetSize(2, 2) .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_LEFT) .SetAnchor(XDG_POSITIONER_ANCHOR_BOTTOM_LEFT) @@ -108,7 +298,7 @@ TEST_F(WaylandPositionerTest, UnconstrainedCases) { gfx::Rect(0, 3, 2, 2)); // Gravity + anchor in opposing directions. - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetSize(2, 2) .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_LEFT) .SetAnchor(XDG_POSITIONER_ANCHOR_TOP_RIGHT) @@ -117,7 +307,7 @@ TEST_F(WaylandPositionerTest, UnconstrainedCases) { } TEST_F(WaylandPositionerTest, FlipSlideResizePriority) { - TestCaseBuilder builder; + TestCaseBuilder builder{WaylandPositioner::Version::STABLE}; builder.SetAnchorRect(4, 4, 0, 0) .SetSize(2, 2) .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT) @@ -145,7 +335,7 @@ TEST_F(WaylandPositionerTest, FlipSlideResizePriority) { TEST_F(WaylandPositionerTest, TriesToMaximizeArea) { // The size is too large to fit where the anchor is. WaylandPositioner::Result result = - TestCaseBuilder() + TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetAnchorRect(2, 4, 0, 0) .SetSize(4, 10) .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT) @@ -160,7 +350,7 @@ TEST_F(WaylandPositionerTest, TriesToMaximizeArea) { TEST_F(WaylandPositionerTest, PropagatesAnInitialFlip) { WaylandPositioner::Result result = - TestCaseBuilder() + TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetAnchorRect(3, 1, 0, 0) .SetSize(2, 2) .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT) @@ -178,7 +368,7 @@ TEST_F(WaylandPositionerTest, PropagatesAnInitialFlip) { // slide if they might occlude the anchor rectangle. For this case, x axis does // slide but the y axis resized instead. TEST_F(WaylandPositionerTest, PreventsSlidingThatOccludesAnchorRect) { - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetSize(3, 3) .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT) .SetAnchor(XDG_POSITIONER_ANCHOR_BOTTOM_LEFT) @@ -188,7 +378,7 @@ TEST_F(WaylandPositionerTest, PreventsSlidingThatOccludesAnchorRect) { // Here we ensure that the 4x4 popup does slide, which is allowed because // the anchor rect is already occluded. - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetSize(4, 4) .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT) .SetAnchor(XDG_POSITIONER_ANCHOR_TOP_LEFT) @@ -201,7 +391,7 @@ TEST_F(WaylandPositionerTest, PreventsSlidingThatOccludesAnchorRect) { // positioning options which do not result in a constrained view available. TEST_F(WaylandPositionerTest, AllowsSlidingThatOccludesWhenThereAreNoOtherOptions) { - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetSize(4, 4) .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT) .SetAnchor(XDG_POSITIONER_ANCHOR_BOTTOM_LEFT) @@ -215,7 +405,7 @@ TEST_F(WaylandPositionerTest, // Make sure that the size should never be an empty even if the constraints // resulted in empty size. TEST_F(WaylandPositionerTest, ResizableShouldNotBeEmpty) { - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetSize(3, 3) .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM) .SetAnchor(XDG_POSITIONER_ANCHOR_BOTTOM) @@ -223,7 +413,7 @@ TEST_F(WaylandPositionerTest, ResizableShouldNotBeEmpty) { .SetAnchorRect(1, -10, 4, 4) .SolveToRect(), gfx::Rect(2, 0, 3, 1)); - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetSize(3, 3) .SetGravity(XDG_POSITIONER_GRAVITY_RIGHT) .SetAnchor(XDG_POSITIONER_ANCHOR_RIGHT) @@ -235,7 +425,7 @@ TEST_F(WaylandPositionerTest, ResizableShouldNotBeEmpty) { TEST_F(WaylandPositionerTest, AllowsAdditionalAdjustmentsIfNoSolutionCanBeFound) { - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) .SetWorkArea(gfx::Rect(5, 5)) .SetSize(10, 10) .SetAnchorRect(0, 0, 0, 0) diff --git a/components/exo/wayland/xdg_shell.cc b/components/exo/wayland/xdg_shell.cc index 560e959750dba..0fc7cf8db5c48 100644 --- a/components/exo/wayland/xdg_shell.cc +++ b/components/exo/wayland/xdg_shell.cc @@ -730,8 +730,9 @@ void xdg_wm_base_create_positioner(wl_client* client, 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()); + SetImplementation( + positioner_resource, &xdg_positioner_implementation, + std::make_unique(WaylandPositioner::Version::STABLE)); } void xdg_wm_base_get_xdg_surface(wl_client* client, diff --git a/components/exo/wayland/zxdg_shell.cc b/components/exo/wayland/zxdg_shell.cc new file mode 100644 index 0000000000000..80ad3db6d85d3 --- /dev/null +++ b/components/exo/wayland/zxdg_shell.cc @@ -0,0 +1,690 @@ +// 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/zxdg_shell.h" + +#include +#include +#include + +#include "ash/public/cpp/shell_window_ids.h" +#include "ash/public/cpp/window_properties.h" +#include "base/functional/bind.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_positioner.h" +#include "components/exo/wayland/xdg_shell.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/views/widget/widget.h" +#include "ui/wm/core/coordinate_conversion.h" + +namespace exo { +namespace wayland { +namespace { + +//////////////////////////////////////////////////////////////////////////////// +// xdg_positioner_interface: + +void xdg_positioner_v6_destroy(wl_client* client, wl_resource* resource) { + wl_resource_destroy(resource); +} + +void xdg_positioner_v6_set_size(wl_client* client, + wl_resource* resource, + int32_t width, + int32_t height) { + if (width < 1 || height < 1) { + wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, + "width and height must be positive and non-zero"); + return; + } + + GetUserDataAs(resource)->SetSize(gfx::Size(width, height)); +} + +void xdg_positioner_v6_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, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, + "width and height must be positive and non-zero"); + return; + } + + GetUserDataAs(resource)->SetAnchorRect( + gfx::Rect(x, y, width, height)); +} + +void xdg_positioner_v6_set_anchor(wl_client* client, + wl_resource* resource, + uint32_t anchor) { + if (((anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT) && + (anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT)) || + ((anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP) && + (anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM))) { + wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, + "same-axis values are not allowed"); + return; + } + + GetUserDataAs(resource)->SetAnchor(anchor); +} + +void xdg_positioner_v6_set_gravity(wl_client* client, + wl_resource* resource, + uint32_t gravity) { + if (((gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) && + (gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT)) || + ((gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) && + (gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM))) { + wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, + "same-axis values are not allowed"); + return; + } + + GetUserDataAs(resource)->SetGravity(gravity); +} + +void xdg_positioner_v6_set_constraint_adjustment(wl_client* client, + wl_resource* resource, + uint32_t adjustment) { + GetUserDataAs(resource)->SetAdjustment(adjustment); +} + +void xdg_positioner_v6_set_offset(wl_client* client, + wl_resource* resource, + int32_t x, + int32_t y) { + GetUserDataAs(resource)->SetOffset(gfx::Vector2d(x, y)); +} + +const struct zxdg_positioner_v6_interface xdg_positioner_v6_implementation = { + xdg_positioner_v6_destroy, + xdg_positioner_v6_set_size, + xdg_positioner_v6_set_anchor_rect, + xdg_positioner_v6_set_anchor, + xdg_positioner_v6_set_gravity, + xdg_positioner_v6_set_constraint_adjustment, + xdg_positioner_v6_set_offset}; + +//////////////////////////////////////////////////////////////////////////////// +// xdg_toplevel_interface: + +int XdgToplevelV6ResizeComponent(uint32_t edges) { + switch (edges) { + case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP: + return HTTOP; + case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM: + return HTBOTTOM; + case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_LEFT: + return HTLEFT; + case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_LEFT: + return HTTOPLEFT; + case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_LEFT: + return HTBOTTOMLEFT; + case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_RIGHT: + return HTRIGHT; + case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_RIGHT: + return HTTOPRIGHT; + case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_RIGHT: + return HTBOTTOMRIGHT; + default: + return HTBOTTOMRIGHT; + } +} + +using XdgSurfaceConfigureCallback = + base::RepeatingCallback; + +uint32_t HandleXdgSurfaceV6ConfigureCallback( + 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) { + uint32_t serial = + serial_tracker->GetNextSerial(SerialTracker::EventType::OTHER_EVENT); + callback.Run(bounds.size(), state_type, resizing, activated); + zxdg_surface_v6_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* resource, wl_resource* surface_resource) + : resource_(resource), + shell_surface_data_( + GetUserDataAs(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( + &HandleXdgSurfaceV6ConfigureCallback, 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 { + shell_surface_data_ = nullptr; + } + + void SetParent(WaylandToplevel* parent) { + if (!shell_surface_data_) + return; + + if (!parent) { + shell_surface_data_->shell_surface->SetParent(nullptr); + return; + } + + if (this == parent) { + // Some apps e.g. crbug/1210235 try to be their own parent. Ignore them. + auto* app_id = GetShellApplicationId( + shell_surface_data_->shell_surface->host_window()); + LOG(WARNING) + << "Client attempts to add itself as a transient parent: app_id=" + << app_id; + 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) { + if (shell_surface_data_) + shell_surface_data_->shell_surface->SetFullscreen(fullscreen); + } + + void Minimize() { + if (shell_surface_data_) + shell_surface_data_->shell_surface->Minimize(); + } + + private: + void OnClose() { + zxdg_toplevel_v6_send_close(resource_); + wl_client_flush(wl_resource_get_client(resource_)); + } + + static void AddState(wl_array* states, zxdg_toplevel_v6_state state) { + zxdg_toplevel_v6_state* value = static_cast( + wl_array_add(states, sizeof(zxdg_toplevel_v6_state))); + DCHECK(value); + *value = state; + } + + void OnConfigure(const gfx::Size& size, + chromeos::WindowStateType state_type, + bool resizing, + bool activated) { + wl_array states; + wl_array_init(&states); + if (state_type == chromeos::WindowStateType::kMaximized) + AddState(&states, ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED); + if (state_type == chromeos::WindowStateType::kFullscreen) + AddState(&states, ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN); + if (resizing) + AddState(&states, ZXDG_TOPLEVEL_V6_STATE_RESIZING); + if (activated) + AddState(&states, ZXDG_TOPLEVEL_V6_STATE_ACTIVATED); + zxdg_toplevel_v6_send_configure(resource_, size.width(), size.height(), + &states); + wl_array_release(&states); + } + + wl_resource* const resource_; + WaylandXdgSurface* shell_surface_data_; + base::WeakPtrFactory weak_ptr_factory_{this}; +}; + +void xdg_toplevel_v6_destroy(wl_client* client, wl_resource* resource) { + wl_resource_destroy(resource); +} + +void xdg_toplevel_v6_set_parent(wl_client* client, + wl_resource* resource, + wl_resource* parent) { + WaylandToplevel* parent_surface = nullptr; + if (parent) + parent_surface = GetUserDataAs(parent); + + GetUserDataAs(resource)->SetParent(parent_surface); +} + +void xdg_toplevel_v6_set_title(wl_client* client, + wl_resource* resource, + const char* title) { + GetUserDataAs(resource)->SetTitle( + std::u16string(base::UTF8ToUTF16(title))); +} + +void xdg_toplevel_v6_set_app_id(wl_client* client, + wl_resource* resource, + const char* app_id) { + GetUserDataAs(resource)->SetApplicationId(app_id); +} + +void xdg_toplevel_v6_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_v6_move(wl_client* client, + wl_resource* resource, + wl_resource* seat, + uint32_t serial) { + GetUserDataAs(resource)->Move(); +} + +void xdg_toplevel_v6_resize(wl_client* client, + wl_resource* resource, + wl_resource* seat, + uint32_t serial, + uint32_t edges) { + GetUserDataAs(resource)->Resize( + XdgToplevelV6ResizeComponent(edges)); +} + +void xdg_toplevel_v6_set_max_size(wl_client* client, + wl_resource* resource, + int32_t width, + int32_t height) { + GetUserDataAs(resource)->SetMaximumSize( + gfx::Size(width, height)); +} + +void xdg_toplevel_v6_set_min_size(wl_client* client, + wl_resource* resource, + int32_t width, + int32_t height) { + GetUserDataAs(resource)->SetMinimumSize( + gfx::Size(width, height)); +} + +void xdg_toplevel_v6_set_maximized(wl_client* client, wl_resource* resource) { + GetUserDataAs(resource)->Maximize(); +} + +void xdg_toplevel_v6_unset_maximized(wl_client* client, wl_resource* resource) { + GetUserDataAs(resource)->Restore(); +} + +void xdg_toplevel_v6_set_fullscreen(wl_client* client, + wl_resource* resource, + wl_resource* output) { + GetUserDataAs(resource)->SetFullscreen(true); +} + +void xdg_toplevel_v6_unset_fullscreen(wl_client* client, + wl_resource* resource) { + GetUserDataAs(resource)->SetFullscreen(false); +} + +void xdg_toplevel_v6_set_minimized(wl_client* client, wl_resource* resource) { + GetUserDataAs(resource)->Minimize(); +} + +const struct zxdg_toplevel_v6_interface xdg_toplevel_v6_implementation = { + xdg_toplevel_v6_destroy, xdg_toplevel_v6_set_parent, + xdg_toplevel_v6_set_title, xdg_toplevel_v6_set_app_id, + xdg_toplevel_v6_show_window_menu, xdg_toplevel_v6_move, + xdg_toplevel_v6_resize, xdg_toplevel_v6_set_max_size, + xdg_toplevel_v6_set_min_size, xdg_toplevel_v6_set_maximized, + xdg_toplevel_v6_unset_maximized, xdg_toplevel_v6_set_fullscreen, + xdg_toplevel_v6_unset_fullscreen, xdg_toplevel_v6_set_minimized}; + +//////////////////////////////////////////////////////////////////////////////// +// 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), + shell_surface_data_( + GetUserDataAs(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( + &HandleXdgSurfaceV6ConfigureCallback, 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); + } + + void Grab() { + if (!shell_surface_data_) { + wl_resource_post_error(resource_, ZXDG_POPUP_V6_ERROR_INVALID_GRAB, + "the surface has already been destroyed"); + return; + } + if (shell_surface_data_->shell_surface->GetWidget()) { + wl_resource_post_error(resource_, ZXDG_POPUP_V6_ERROR_INVALID_GRAB, + "grab must be called before construction"); + return; + } + shell_surface_data_->shell_surface->Grab(); + } + + // Overridden from aura::WindowObserver: + void OnWindowDestroying(aura::Window* window) override { + shell_surface_data_ = nullptr; + } + + private: + void OnClose() { + zxdg_popup_v6_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) { + // Nothing to do here as popups don't have additional configure state. + } + + wl_resource* const resource_; + WaylandXdgSurface* shell_surface_data_; + base::WeakPtrFactory weak_ptr_factory_{this}; +}; + +void xdg_popup_v6_destroy(wl_client* client, wl_resource* resource) { + wl_resource_destroy(resource); +} + +void xdg_popup_v6_grab(wl_client* client, + wl_resource* resource, + wl_resource* seat, + uint32_t serial) { + GetUserDataAs(resource)->Grab(); +} + +const struct zxdg_popup_v6_interface xdg_popup_v6_implementation = { + xdg_popup_v6_destroy, xdg_popup_v6_grab}; + +//////////////////////////////////////////////////////////////////////////////// +// xdg_surface_interface: + +void xdg_surface_v6_destroy(wl_client* client, wl_resource* resource) { + wl_resource_destroy(resource); +} + +void xdg_surface_v6_get_toplevel(wl_client* client, + wl_resource* resource, + uint32_t id) { + auto* shell_surface_data = GetUserDataAs(resource); + if (shell_surface_data->shell_surface->GetEnabled()) { + wl_resource_post_error(resource, ZXDG_SURFACE_V6_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, &zxdg_toplevel_v6_interface, 1, id); + + SetImplementation( + xdg_toplevel_resource, &xdg_toplevel_v6_implementation, + std::make_unique(xdg_toplevel_resource, resource)); +} + +void xdg_surface_v6_get_popup(wl_client* client, + wl_resource* resource, + uint32_t id, + wl_resource* parent_resource, + wl_resource* positioner_resource) { + auto* shell_surface_data = GetUserDataAs(resource); + if (shell_surface_data->shell_surface->GetEnabled()) { + wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_ALREADY_CONSTRUCTED, + "surface has already been constructed"); + return; + } + + auto* parent_data = GetUserDataAs(parent_resource); + if (!parent_data->shell_surface->GetWidget()) { + wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED, + "popup parent not constructed"); + return; + } + + if (shell_surface_data->shell_surface->GetWidget()) { + wl_resource_post_error(resource, ZXDG_SURFACE_V6_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(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, &zxdg_popup_v6_interface, 1, id); + + SetImplementation( + xdg_popup_resource, &xdg_popup_v6_implementation, + std::make_unique(xdg_popup_resource, resource)); + + // We send the configure event here as this event needs x,y coordinates + // relative to the parent window. + zxdg_popup_v6_send_configure(xdg_popup_resource, position.origin.x(), + position.origin.y(), position.size.width(), + position.size.height()); +} + +void xdg_surface_v6_set_window_geometry(wl_client* client, + wl_resource* resource, + int32_t x, + int32_t y, + int32_t width, + int32_t height) { + GetUserDataAs(resource)->shell_surface->SetGeometry( + gfx::Rect(x, y, width, height)); +} + +void xdg_surface_v6_ack_configure(wl_client* client, + wl_resource* resource, + uint32_t serial) { + GetUserDataAs(resource) + ->shell_surface->AcknowledgeConfigure(serial); +} + +const struct zxdg_surface_v6_interface xdg_surface_v6_implementation = { + xdg_surface_v6_destroy, xdg_surface_v6_get_toplevel, + xdg_surface_v6_get_popup, xdg_surface_v6_set_window_geometry, + xdg_surface_v6_ack_configure}; + +//////////////////////////////////////////////////////////////////////////////// +// xdg_shell_interface: + +void xdg_shell_v6_destroy(wl_client* client, wl_resource* resource) { + // Nothing to do here. +} + +void xdg_shell_v6_create_positioner(wl_client* client, + wl_resource* resource, + uint32_t id) { + wl_resource* positioner_resource = + wl_resource_create(client, &zxdg_positioner_v6_interface, 1, id); + + SetImplementation(positioner_resource, &xdg_positioner_v6_implementation, + std::make_unique( + WaylandPositioner::Version::UNSTABLE)); +} + +void xdg_shell_v6_get_xdg_surface(wl_client* client, + wl_resource* resource, + uint32_t id, + wl_resource* surface) { + auto* data = GetUserDataAs(resource); + std::unique_ptr shell_surface = + data->display->CreateXdgShellSurface(GetUserDataAs(surface)); + if (!shell_surface) { + wl_resource_post_error(resource, ZXDG_SHELL_V6_ERROR_ROLE, + "surface has already been assigned a role"); + return; + } + + // Xdg shell v6 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 wayland_shell_surface = + std::make_unique(std::move(shell_surface), + data->serial_tracker); + + wl_resource* xdg_surface_resource = + wl_resource_create(client, &zxdg_surface_v6_interface, 1, id); + + SetImplementation(xdg_surface_resource, &xdg_surface_v6_implementation, + std::move(wayland_shell_surface)); +} + +void xdg_shell_v6_pong(wl_client* client, + wl_resource* resource, + uint32_t serial) { + NOTIMPLEMENTED(); +} + +const struct zxdg_shell_v6_interface xdg_shell_v6_implementation = { + xdg_shell_v6_destroy, xdg_shell_v6_create_positioner, + xdg_shell_v6_get_xdg_surface, xdg_shell_v6_pong}; + +} // namespace + +void bind_zxdg_shell_v6(wl_client* client, + void* data, + uint32_t version, + uint32_t id) { + wl_resource* resource = + wl_resource_create(client, &zxdg_shell_v6_interface, 1, id); + + wl_resource_set_implementation(resource, &xdg_shell_v6_implementation, data, + nullptr); +} + +} // namespace wayland +} // namespace exo diff --git a/components/exo/wayland/zxdg_shell.h b/components/exo/wayland/zxdg_shell.h new file mode 100644 index 0000000000000..4eadfdd2b7529 --- /dev/null +++ b/components/exo/wayland/zxdg_shell.h @@ -0,0 +1,40 @@ +// 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. + +#ifndef COMPONENTS_EXO_WAYLAND_ZXDG_SHELL_H_ +#define COMPONENTS_EXO_WAYLAND_ZXDG_SHELL_H_ + +#include + +struct wl_client; + +namespace exo { +class Display; + +namespace wayland { +class SerialTracker; + +struct WaylandZxdgShell { + WaylandZxdgShell(Display* display, SerialTracker* serial_tracker) + : display(display), serial_tracker(serial_tracker) {} + + WaylandZxdgShell(const WaylandZxdgShell&) = delete; + WaylandZxdgShell& operator=(const WaylandZxdgShell&) = delete; + + // Owned by WaylandServerController, which always outlives zxdg_shell. + Display* const display; + + // Owned by Server, which always outlives zxdg_shell. + SerialTracker* const serial_tracker; +}; + +void bind_zxdg_shell_v6(wl_client* client, + void* data, + uint32_t version, + uint32_t id); + +} // namespace wayland +} // namespace exo + +#endif // COMPONENTS_EXO_WAYLAND_ZXDG_SHELL_H_