// Copyright 2024 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/test/shell_client_data.h"
#include <memory>
#include "base/memory/ptr_util.h"
#include "components/exo/wayland/test/client_util.h"
#include "extended-drag-unstable-v1-client-protocol.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
namespace exo::wayland::test {
namespace {
// xdg_shell listener callbacks:
void OnXdgToplevelClose(void* data, struct xdg_toplevel* toplevel) {
static_cast<ShellClientData*>(data)->Close();
}
// zcr_remote_surface_v2 listener callbacks:
void OnRemoteSurfaceClose(void* data, zcr_remote_surface_v2*) {
static_cast<ShellClientData*>(data)->Close();
}
gfx::PointF ToPointF(wl_fixed_t x, wl_fixed_t y) {
return gfx::PointF(wl_fixed_to_double(x), wl_fixed_to_double(y));
}
// wl_pointer_listener callbacks:
void OnEnter(void* data,
wl_pointer* obj,
uint32_t serial,
wl_surface* surface,
wl_fixed_t surface_x,
wl_fixed_t surface_y) {
auto* listener = static_cast<ShellClientData*>(data)->input_listener();
if (listener) {
listener->OnEnter(serial, surface, ToPointF(surface_x, surface_y));
}
}
void OnLeave(void* data,
wl_pointer* obj,
uint32_t serial,
wl_surface* surface) {
auto* listener = static_cast<ShellClientData*>(data)->input_listener();
if (listener) {
listener->OnLeave(serial, surface);
}
}
void OnMotion(void* data,
wl_pointer* obj,
uint32_t time,
wl_fixed_t surface_x,
wl_fixed_t surface_y) {
auto* listener = static_cast<ShellClientData*>(data)->input_listener();
if (listener) {
listener->OnMotion(ToPointF(surface_x, surface_y));
}
}
void OnButton(void* data,
wl_pointer* obj,
uint32_t serial,
uint32_t time,
uint32_t button,
uint32_t state) {
auto* listener = static_cast<ShellClientData*>(data)->input_listener();
if (listener) {
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
listener->OnButtonPressed(serial, button);
} else {
listener->OnButtonReleased(serial, button);
}
}
}
// wl_tocuh_listener callback.
void OnTouchDown(void* data,
wl_touch* touch,
uint32_t serial,
uint32_t time,
struct wl_surface* surface,
int32_t id,
wl_fixed_t x,
wl_fixed_t y) {
auto* listener = static_cast<ShellClientData*>(data)->input_listener();
if (listener) {
listener->OnTouchDown(serial, surface, id, ToPointF(x, y));
}
}
void OnTouchUp(void* data,
wl_touch* touch,
uint32_t serial,
uint32_t time,
int32_t id) {
auto* listener = static_cast<ShellClientData*>(data)->input_listener();
if (listener) {
listener->OnTouchUp(serial, id);
}
}
void OnDataOffer(void* data,
wl_data_device* data_device,
wl_data_offer* offer) {
static_cast<ShellClientData*>(data)->set_data_offer(
std::unique_ptr<wl_data_offer, decltype(&wl_data_offer_destroy)>(
offer, &wl_data_offer_destroy));
}
} // namespace
ShellClientData::ShellClientData(test::TestClient* client)
: client_(client),
pointer_(nullptr, &wl_pointer_destroy),
touch_(nullptr, &wl_touch_destroy),
surface_(wl_compositor_create_surface(client->compositor()),
&wl_surface_destroy),
xdg_surface_(nullptr, &xdg_surface_destroy),
xdg_toplevel_(nullptr, &xdg_toplevel_destroy),
aura_toplevel_(nullptr, &zaura_toplevel_destroy),
remote_surface_(nullptr, &zcr_remote_surface_v2_destroy),
data_device_(nullptr, &wl_data_device_destroy),
data_source_(nullptr, &wl_data_source_destroy),
data_offer_(nullptr, &wl_data_offer_destroy) {
data_device_.reset(wl_data_device_manager_get_data_device(
client_->globals().data_device_manager.get(),
client_->globals().seat.get()));
constexpr wl_data_device_listener kDataDeviceListener = {
.data_offer = &OnDataOffer,
.enter = [](void* data, wl_data_device* data_device, uint32_t serial,
wl_surface* surface, wl_fixed_t x, wl_fixed_t y,
wl_data_offer* offer) {},
.leave = [](void*, wl_data_device*) {},
.motion = [](void*, wl_data_device*, uint32_t, wl_fixed_t, wl_fixed_t) {},
.drop = [](void*, wl_data_device*) {},
.selection = [](void*, wl_data_device*, wl_data_offer*) {},
};
wl_data_device_add_listener(data_device_.get(), &kDataDeviceListener, this);
pointer_.reset(wl_seat_get_pointer(client_->globals().seat.get()));
constexpr wl_pointer_listener kPointerListener = {
.enter = &OnEnter,
.leave = &OnLeave,
.motion = &OnMotion,
.button = &OnButton,
.axis = [](void*, wl_pointer*, uint32_t, uint32_t, wl_fixed_t) {},
.frame = [](void*, wl_pointer*) {},
.axis_source = [](void*, wl_pointer*, uint32_t) {},
.axis_stop = [](void*, wl_pointer*, uint32_t, uint32_t) {},
.axis_discrete = [](void*, wl_pointer*, uint32_t, int32_t) {},
.axis_value120 = [](void*, wl_pointer*, uint32_t, int32_t) {},
};
wl_pointer_add_listener(pointer_.get(), &kPointerListener, this);
touch_.reset(wl_seat_get_touch(client_->globals().seat.get()));
constexpr wl_touch_listener kTouchListener = {
.down = &OnTouchDown,
.up = &OnTouchUp,
.motion = [](void*, wl_touch*, uint32_t, int32_t, wl_fixed_t,
wl_fixed_t) {},
.frame = [](void*, wl_touch*) {},
.cancel = [](void*, wl_touch*) {},
.shape = [](void*, wl_touch*, int32_t, wl_fixed_t, wl_fixed_t) {},
.orientation = [](void*, wl_touch*, int32_t, wl_fixed_t) {},
};
wl_touch_add_listener(touch_.get(), &kTouchListener, this);
}
ShellClientData::~ShellClientData() {
Close();
}
void ShellClientData::CreateXdgToplevel() {
constexpr xdg_toplevel_listener kXdgToplevelListener = {
[](void*, xdg_toplevel*, int32_t, int32_t, wl_array*) {},
&OnXdgToplevelClose,
[](void*, xdg_toplevel*, int32_t, int32_t) {},
[](void*, xdg_toplevel*, wl_array*) {},
};
xdg_surface_.reset(
xdg_wm_base_get_xdg_surface(client_->xdg_wm_base(), surface_.get()));
xdg_toplevel_.reset(xdg_surface_get_toplevel(xdg_surface_.get()));
xdg_toplevel_add_listener(xdg_toplevel_.get(), &kXdgToplevelListener, this);
aura_toplevel_.reset(zaura_shell_get_aura_toplevel_for_xdg_toplevel(
client_->aura_shell(), xdg_toplevel_.get()));
}
void ShellClientData::CreateRemoteSurface() {
constexpr zcr_remote_surface_v2_listener kRemoteSurfaceV2Listener = {
&OnRemoteSurfaceClose,
[](void*, zcr_remote_surface_v2*, uint32_t) {},
[](void*, zcr_remote_surface_v2*, int, int, int, int) {},
[](void*, zcr_remote_surface_v2*, uint32_t, uint32_t, int32_t, int32_t,
int32_t, int32_t, uint32_t) {},
[](void*, zcr_remote_surface_v2*, uint32_t) {},
[](void*, zcr_remote_surface_v2*, int32_t, int32_t, int32_t) {},
[](void*, zcr_remote_surface_v2*, int32_t) {},
[](void*, zcr_remote_surface_v2*, wl_output*, int32_t, int32_t, int32_t,
int32_t, uint32_t) {},
};
remote_surface_.reset(zcr_remote_shell_v2_get_remote_surface(
client_->cr_remote_shell_v2(), surface_.get(),
ZCR_REMOTE_SHELL_V2_CONTAINER_DEFAULT));
zcr_remote_surface_v2_add_listener(remote_surface_.get(),
&kRemoteSurfaceV2Listener, this);
}
void ShellClientData::Pin() {
zcr_remote_surface_v2_pin(remote_surface_.get(), /*trusted=*/true);
}
void ShellClientData::UnsetSnap() {
zaura_toplevel_unset_snap(aura_toplevel_.get());
}
void ShellClientData::CreateAndAttachBuffer(const gfx::Size& size) {
buffer_ = client_->shm_buffer_factory()->CreateBuffer(0, size.width(),
size.height());
wl_surface_attach(surface_.get(), buffer_->resource(), 0, 0);
}
void ShellClientData::Commit() {
wl_surface_commit(surface_.get());
}
void ShellClientData::DestroySurface() {
zcr_remote_surface_v2_destroy(remote_surface_.release());
wl_surface_destroy(surface_.release());
}
void ShellClientData::RequestWindowBounds(const gfx::Rect& bounds,
wl_output* target_output) {
zaura_toplevel_set_window_bounds(aura_toplevel_.get(), bounds.x(), bounds.y(),
bounds.width(), bounds.height(),
target_output);
}
void ShellClientData::StartDrag(uint32_t serial) {
DCHECK(!data_source_);
data_source_.reset(wl_data_device_manager_create_data_source(
client_->globals().data_device_manager.get()));
constexpr wl_data_source_listener kDataSourceListener = {
.target = [](void* data, wl_data_source*, const char*) {},
.send = [](void* data, wl_data_source*, const char*, int32_t) {},
.cancelled =
[](void* data, wl_data_source*) {
static_cast<ShellClientData*>(data)->DestroyDataSource();
},
.dnd_drop_performed =
[](void* data, wl_data_source*) {
static_cast<ShellClientData*>(data)->DestroyDataSource();
},
.dnd_finished = [](void* data, wl_data_source*) {},
.action = [](void* data, wl_data_source*, uint32_t) {},
};
wl_data_source_add_listener(data_source_.get(), &kDataSourceListener, this);
wl_data_device_start_drag(data_device_.get(), data_source_.get(),
surface_.get(), nullptr, serial);
}
void ShellClientData::DestroyDataSource() {
data_source_.reset();
}
void ShellClientData::Close() {
close_called_ = true;
if (surface_) {
wl_surface_attach(surface_.get(), nullptr, 0, 0);
}
if (buffer_) {
wl_buffer_destroy(buffer_->GetResourceAndRelease());
}
buffer_.reset();
xdg_toplevel_.reset();
xdg_surface_.reset();
aura_toplevel_.reset();
remote_surface_.reset();
surface_.reset();
pointer_.reset();
touch_.reset();
data_offer_.reset();
data_source_.reset();
data_device_.reset();
}
ResourceKey ShellClientData::GetSurfaceResourceKey() const {
return test::client_util::GetResourceKey(surface_.get());
}
} // namespace exo::wayland::test