blob: 846d79447c7755c5715c0e01b75729e63be22a97 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/exo/wayland/wl_data_device_manager.h"
#include <wayland-server-protocol-core.h>
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include "components/exo/data_device.h"
#include "components/exo/data_device_delegate.h"
#include "components/exo/data_offer.h"
#include "components/exo/data_offer_delegate.h"
#include "components/exo/data_source.h"
#include "components/exo/data_source_delegate.h"
#include "components/exo/display.h"
#include "components/exo/wayland/serial_tracker.h"
#include "components/exo/wayland/server_util.h"
namespace exo {
namespace wayland {
namespace {
uint32_t WaylandDataDeviceManagerDndAction(DndAction action) {
switch (action) {
case DndAction::kNone:
return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
case DndAction::kCopy:
return WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
case DndAction::kMove:
return WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
case DndAction::kAsk:
return WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
}
NOTREACHED();
}
uint32_t WaylandDataDeviceManagerDndActions(
const base::flat_set<DndAction>& dnd_actions) {
uint32_t actions = 0;
for (DndAction action : dnd_actions)
actions |= WaylandDataDeviceManagerDndAction(action);
return actions;
}
DndAction DataDeviceManagerDndAction(uint32_t value) {
switch (value) {
case WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE:
return DndAction::kNone;
case WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY:
return DndAction::kCopy;
case WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE:
return DndAction::kMove;
case WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK:
return DndAction::kAsk;
default:
NOTREACHED();
return DndAction::kNone;
}
}
base::flat_set<DndAction> DataDeviceManagerDndActions(uint32_t value) {
base::flat_set<DndAction> actions;
if (value & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)
actions.insert(DndAction::kCopy);
if (value & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
actions.insert(DndAction::kMove);
if (value & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)
actions.insert(DndAction::kAsk);
return actions;
}
////////////////////////////////////////////////////////////////////////////////
// wl_data_source_interface:
class WaylandDataSourceDelegate : public DataSourceDelegate {
public:
explicit WaylandDataSourceDelegate(wl_resource* source)
: data_source_resource_(source) {}
// Overridden from DataSourceDelegate:
void OnDataSourceDestroying(DataSource* device) override { delete this; }
void OnTarget(const base::Optional<std::string>& mime_type) override {
wl_data_source_send_target(data_source_resource_,
mime_type ? mime_type->c_str() : nullptr);
wl_client_flush(wl_resource_get_client(data_source_resource_));
}
void OnSend(const std::string& mime_type, base::ScopedFD fd) override {
wl_data_source_send_send(data_source_resource_, mime_type.c_str(),
fd.get());
wl_client_flush(wl_resource_get_client(data_source_resource_));
}
void OnCancelled() override {
wl_data_source_send_cancelled(data_source_resource_);
wl_client_flush(wl_resource_get_client(data_source_resource_));
}
void OnDndDropPerformed() override {
if (wl_resource_get_version(data_source_resource_) >=
WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION) {
wl_data_source_send_dnd_drop_performed(data_source_resource_);
wl_client_flush(wl_resource_get_client(data_source_resource_));
}
}
void OnDndFinished() override {
if (wl_resource_get_version(data_source_resource_) >=
WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) {
wl_data_source_send_dnd_finished(data_source_resource_);
wl_client_flush(wl_resource_get_client(data_source_resource_));
}
}
void OnAction(DndAction dnd_action) override {
if (wl_resource_get_version(data_source_resource_) >=
WL_DATA_SOURCE_ACTION_SINCE_VERSION) {
wl_data_source_send_action(data_source_resource_,
WaylandDataDeviceManagerDndAction(dnd_action));
wl_client_flush(wl_resource_get_client(data_source_resource_));
}
}
private:
wl_resource* const data_source_resource_;
DISALLOW_COPY_AND_ASSIGN(WaylandDataSourceDelegate);
};
void data_source_offer(wl_client* client,
wl_resource* resource,
const char* mime_type) {
GetUserDataAs<DataSource>(resource)->Offer(mime_type);
}
void data_source_destroy(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
void data_source_set_actions(wl_client* client,
wl_resource* resource,
uint32_t dnd_actions) {
GetUserDataAs<DataSource>(resource)->SetActions(
DataDeviceManagerDndActions(dnd_actions));
}
const struct wl_data_source_interface data_source_implementation = {
data_source_offer, data_source_destroy, data_source_set_actions};
////////////////////////////////////////////////////////////////////////////////
// wl_data_offer_interface:
class WaylandDataOfferDelegate : public DataOfferDelegate {
public:
explicit WaylandDataOfferDelegate(wl_resource* offer)
: data_offer_resource_(offer) {}
// Overridden from DataOfferDelegate:
void OnDataOfferDestroying(DataOffer* device) override { delete this; }
void OnOffer(const std::string& mime_type) override {
wl_data_offer_send_offer(data_offer_resource_, mime_type.c_str());
wl_client_flush(wl_resource_get_client(data_offer_resource_));
}
void OnSourceActions(
const base::flat_set<DndAction>& source_actions) override {
if (wl_resource_get_version(data_offer_resource_) >=
WL_DATA_OFFER_SOURCE_ACTIONS_SINCE_VERSION) {
wl_data_offer_send_source_actions(
data_offer_resource_,
WaylandDataDeviceManagerDndActions(source_actions));
wl_client_flush(wl_resource_get_client(data_offer_resource_));
}
}
void OnAction(DndAction action) override {
if (wl_resource_get_version(data_offer_resource_) >=
WL_DATA_OFFER_ACTION_SINCE_VERSION) {
wl_data_offer_send_action(data_offer_resource_,
WaylandDataDeviceManagerDndAction(action));
wl_client_flush(wl_resource_get_client(data_offer_resource_));
}
}
private:
wl_resource* const data_offer_resource_;
DISALLOW_COPY_AND_ASSIGN(WaylandDataOfferDelegate);
};
void data_offer_accept(wl_client* client,
wl_resource* resource,
uint32_t serial,
const char* mime_type) {
if (mime_type == nullptr) {
GetUserDataAs<DataOffer>(resource)->Accept(nullptr);
return;
}
const std::string mime_type_string(mime_type);
GetUserDataAs<DataOffer>(resource)->Accept(&mime_type_string);
}
void data_offer_receive(wl_client* client,
wl_resource* resource,
const char* mime_type,
int fd) {
GetUserDataAs<DataOffer>(resource)->Receive(mime_type, base::ScopedFD(fd));
}
void data_offer_destroy(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
void data_offer_finish(wl_client* client, wl_resource* resource) {
GetUserDataAs<DataOffer>(resource)->Finish();
}
void data_offer_set_actions(wl_client* client,
wl_resource* resource,
uint32_t dnd_actions,
uint32_t preferred_action) {
GetUserDataAs<DataOffer>(resource)->SetActions(
DataDeviceManagerDndActions(dnd_actions),
DataDeviceManagerDndAction(preferred_action));
}
const struct wl_data_offer_interface data_offer_implementation = {
data_offer_accept, data_offer_receive, data_offer_destroy,
data_offer_finish, data_offer_set_actions};
////////////////////////////////////////////////////////////////////////////////
// wl_data_device_interface:
class WaylandDataDeviceDelegate : public DataDeviceDelegate {
public:
WaylandDataDeviceDelegate(wl_client* client,
wl_resource* device_resource,
SerialTracker* serial_tracker)
: client_(client),
data_device_resource_(device_resource),
serial_tracker_(serial_tracker) {}
// Overridden from DataDeviceDelegate:
void OnDataDeviceDestroying(DataDevice* device) override { delete this; }
bool CanAcceptDataEventsForSurface(Surface* surface) override {
return surface &&
wl_resource_get_client(GetSurfaceResource(surface)) == client_;
}
DataOffer* OnDataOffer(DataOffer::Purpose purpose) override {
wl_resource* data_offer_resource =
wl_resource_create(client_, &wl_data_offer_interface,
wl_resource_get_version(data_device_resource_), 0);
std::unique_ptr<DataOffer> data_offer = std::make_unique<DataOffer>(
new WaylandDataOfferDelegate(data_offer_resource), purpose);
SetDataOfferResource(data_offer.get(), data_offer_resource);
SetImplementation(data_offer_resource, &data_offer_implementation,
std::move(data_offer));
wl_data_device_send_data_offer(data_device_resource_, data_offer_resource);
wl_client_flush(client_);
return GetUserDataAs<DataOffer>(data_offer_resource);
}
void OnEnter(Surface* surface,
const gfx::PointF& point,
const DataOffer& data_offer) override {
wl_data_device_send_enter(
data_device_resource_,
serial_tracker_->GetNextSerial(SerialTracker::EventType::OTHER_EVENT),
GetSurfaceResource(surface), wl_fixed_from_double(point.x()),
wl_fixed_from_double(point.y()), GetDataOfferResource(&data_offer));
wl_client_flush(client_);
}
void OnLeave() override {
wl_data_device_send_leave(data_device_resource_);
wl_client_flush(client_);
}
void OnMotion(base::TimeTicks time_stamp, const gfx::PointF& point) override {
wl_data_device_send_motion(
data_device_resource_, TimeTicksToMilliseconds(time_stamp),
wl_fixed_from_double(point.x()), wl_fixed_from_double(point.y()));
wl_client_flush(client_);
}
void OnDrop() override {
wl_data_device_send_drop(data_device_resource_);
wl_client_flush(client_);
}
void OnSelection(const DataOffer& data_offer) override {
wl_data_device_send_selection(data_device_resource_,
GetDataOfferResource(&data_offer));
wl_client_flush(client_);
}
void StartDrag(DataDevice* data_device,
DataSource* source,
Surface* origin,
Surface* icon,
uint32_t serial) {
base::Optional<wayland::SerialTracker::EventType> event_type =
serial_tracker_->GetEventType(serial);
if (event_type == wayland::SerialTracker::EventType::POINTER_BUTTON_DOWN &&
serial_tracker_->GetPointerDownSerial() == serial) {
data_device->StartDrag(
source, origin, icon,
ui::DragDropTypes::DragEventSource::DRAG_EVENT_SOURCE_MOUSE);
} else if (event_type == wayland::SerialTracker::EventType::TOUCH_DOWN &&
serial_tracker_->GetTouchDownSerial() == serial) {
data_device->StartDrag(
source, origin, icon,
ui::DragDropTypes::DragEventSource::DRAG_EVENT_SOURCE_TOUCH);
} else {
source->Cancelled();
}
}
private:
wl_client* const client_;
wl_resource* const data_device_resource_;
// Owned by Server, which always outlives this delegate.
SerialTracker* const serial_tracker_;
DISALLOW_COPY_AND_ASSIGN(WaylandDataDeviceDelegate);
};
void data_device_start_drag(wl_client* client,
wl_resource* resource,
wl_resource* source_resource,
wl_resource* origin_resource,
wl_resource* icon_resource,
uint32_t serial) {
DataDevice* data_device = GetUserDataAs<DataDevice>(resource);
static_cast<WaylandDataDeviceDelegate*>(data_device->get_delegate())
->StartDrag(
data_device,
source_resource ? GetUserDataAs<DataSource>(source_resource)
: nullptr,
GetUserDataAs<Surface>(origin_resource),
icon_resource ? GetUserDataAs<Surface>(icon_resource) : nullptr,
serial);
}
void data_device_set_selection(wl_client* client,
wl_resource* resource,
wl_resource* data_source,
uint32_t serial) {
GetUserDataAs<DataDevice>(resource)->SetSelection(
data_source ? GetUserDataAs<DataSource>(data_source) : nullptr, serial);
}
void data_device_release(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
const struct wl_data_device_interface data_device_implementation = {
data_device_start_drag, data_device_set_selection, data_device_release};
////////////////////////////////////////////////////////////////////////////////
// wl_data_device_manager_interface:
void data_device_manager_create_data_source(wl_client* client,
wl_resource* resource,
uint32_t id) {
wl_resource* data_source_resource = wl_resource_create(
client, &wl_data_source_interface, wl_resource_get_version(resource), id);
SetImplementation(data_source_resource, &data_source_implementation,
std::make_unique<DataSource>(
new WaylandDataSourceDelegate(data_source_resource)));
}
void data_device_manager_get_data_device(wl_client* client,
wl_resource* resource,
uint32_t id,
wl_resource* seat_resource) {
auto* data = GetUserDataAs<WaylandDataDeviceManager>(resource);
wl_resource* data_device_resource = wl_resource_create(
client, &wl_data_device_interface, wl_resource_get_version(resource), id);
SetImplementation(
data_device_resource, &data_device_implementation,
data->display->CreateDataDevice(new WaylandDataDeviceDelegate(
client, data_device_resource, data->serial_tracker)));
}
const struct wl_data_device_manager_interface
data_device_manager_implementation = {
data_device_manager_create_data_source,
data_device_manager_get_data_device};
} // namespace
void bind_data_device_manager(wl_client* client,
void* data,
uint32_t version,
uint32_t id) {
wl_resource* resource =
wl_resource_create(client, &wl_data_device_manager_interface,
std::min(version, kWlDataDeviceManagerVersion), id);
wl_resource_set_implementation(resource, &data_device_manager_implementation,
data, nullptr);
}
} // namespace wayland
} // namespace exo