Commit f33dae96 authored by Alexander Dunaev's avatar Alexander Dunaev Committed by Commit Bot

[ozone/wayland] Added the primary selection support.

This CL implements primary (aka middle click) selection in Wayland.

R=dcheng@chromium.org, kylechar@chromium.org, msisov@igalia.com
TBR=oshima@chromium.org, dcastagna@chromium.org

Bug: 921950
Change-Id: I4aa02ffbd34541979e30dcefdecc9087d7c949c7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1795825Reviewed-by: default avatarMitsuru Oshima <oshima@chromium.org>
Reviewed-by: default avatarMaksim Sisov <msisov@igalia.com>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarkylechar <kylechar@chromium.org>
Commit-Queue: Alexander Dunaev <adunaev@igalia.com>
Cr-Commit-Position: refs/heads/master@{#701927}
parent 6704b606
......@@ -4,6 +4,12 @@
import("//third_party/wayland/wayland_protocol.gni")
wayland_protocol("gtk_primary_selection_protocol") {
sources = [
"unstable/gtk-primary-selection/gtk-primary-selection.xml",
]
}
wayland_protocol("xdg_shell_protocol") {
sources = [
"src/unstable/xdg-shell/xdg-shell-unstable-v5.xml",
......
GTK primary selection protocol
Maintainers:
Alexander Dunaev <adunaev@igalia.com>
......@@ -7,6 +7,7 @@
namespace ui {
const char kMimeTypeText[] = "text/plain";
const char kMimeTypeTextUtf8[] = "text/plain;charset=utf-8";
const char kMimeTypeURIList[] = "text/uri-list";
const char kMimeTypeMozillaURL[] = "text/x-moz-url";
const char kMimeTypeDownloadURL[] = "downloadurl";
......
......@@ -27,6 +27,7 @@ extern NSString* const kWebCustomDataPboardType;
// MIME type constants.
COMPONENT_EXPORT(BASE_CLIPBOARD_TYPES) extern const char kMimeTypeText[];
COMPONENT_EXPORT(BASE_CLIPBOARD_TYPES) extern const char kMimeTypeTextUtf8[];
COMPONENT_EXPORT(BASE_CLIPBOARD_TYPES) extern const char kMimeTypeURIList[];
COMPONENT_EXPORT(BASE_CLIPBOARD_TYPES) extern const char kMimeTypeDownloadURL[];
COMPONENT_EXPORT(BASE_CLIPBOARD_TYPES) extern const char kMimeTypeMozillaURL[];
......
......@@ -31,6 +31,20 @@ source_set("wayland") {
"gpu/wayland_surface_factory.cc",
"gpu/wayland_surface_factory.h",
"gpu/wayland_surface_gpu.h",
"host/gtk_primary_selection_device.cc",
"host/gtk_primary_selection_device.h",
"host/gtk_primary_selection_device_manager.cc",
"host/gtk_primary_selection_device_manager.h",
"host/gtk_primary_selection_offer.cc",
"host/gtk_primary_selection_offer.h",
"host/gtk_primary_selection_source.cc",
"host/gtk_primary_selection_source.h",
"host/internal/wayland_data_device_base.cc",
"host/internal/wayland_data_device_base.h",
"host/internal/wayland_data_offer_base.cc",
"host/internal/wayland_data_offer_base.h",
"host/internal/wayland_data_source_base.cc",
"host/internal/wayland_data_source_base.h",
"host/wayland_buffer_manager_connector.cc",
"host/wayland_buffer_manager_connector.h",
"host/wayland_buffer_manager_host.cc",
......@@ -109,6 +123,7 @@ source_set("wayland") {
"//mojo/public/cpp/system",
"//skia",
"//third_party/wayland:wayland_client",
"//third_party/wayland-protocols:gtk_primary_selection_protocol",
"//third_party/wayland-protocols:linux_dmabuf_protocol",
"//third_party/wayland-protocols:presentation_time_protocol",
"//third_party/wayland-protocols:text_input_protocol",
......
......@@ -3,6 +3,7 @@ include_rules = [
"+ui/base/hit_test.h", # UI hit test doesn't bring in all of ui/base.
"+ui/base/ui_base_features.h",
"+mojo/public",
"+ui/base/clipboard/clipboard_constants.h",
"+ui/base/dragdrop/drag_drop_types.h",
"+ui/base/dragdrop/os_exchange_data.h",
"+ui/base/dragdrop/os_exchange_data_provider_aura.h",
......
......@@ -4,6 +4,7 @@
#include "ui/ozone/platform/wayland/common/wayland_object.h"
#include <gtk-primary-selection-client-protocol.h>
#include <linux-dmabuf-unstable-v1-client-protocol.h>
#include <presentation-time-client-protocol.h>
#include <text-input-unstable-v1-client-protocol.h>
......@@ -53,6 +54,28 @@ void delete_data_device(wl_data_device* data_device) {
} // namespace
const wl_interface*
ObjectTraits<gtk_primary_selection_device_manager>::interface =
&gtk_primary_selection_device_manager_interface;
void (*ObjectTraits<gtk_primary_selection_device_manager>::deleter)(
gtk_primary_selection_device_manager*) =
&gtk_primary_selection_device_manager_destroy;
const wl_interface* ObjectTraits<gtk_primary_selection_device>::interface =
&gtk_primary_selection_device_interface;
void (*ObjectTraits<gtk_primary_selection_device>::deleter)(
gtk_primary_selection_device*) = &gtk_primary_selection_device_destroy;
const wl_interface* ObjectTraits<gtk_primary_selection_offer>::interface =
&gtk_primary_selection_offer_interface;
void (*ObjectTraits<gtk_primary_selection_offer>::deleter)(
gtk_primary_selection_offer*) = &gtk_primary_selection_offer_destroy;
const wl_interface* ObjectTraits<gtk_primary_selection_source>::interface =
&gtk_primary_selection_source_interface;
void (*ObjectTraits<gtk_primary_selection_source>::deleter)(
gtk_primary_selection_source*) = &gtk_primary_selection_source_destroy;
const wl_interface* ObjectTraits<wl_buffer>::interface = &wl_buffer_interface;
void (*ObjectTraits<wl_buffer>::deleter)(wl_buffer*) = &wl_buffer_destroy;
......
......@@ -8,6 +8,10 @@
#include <wayland-client-core.h>
#include <memory>
struct gtk_primary_selection_device;
struct gtk_primary_selection_device_manager;
struct gtk_primary_selection_offer;
struct gtk_primary_selection_source;
struct wl_buffer;
struct wl_callback;
struct wl_compositor;
......@@ -46,6 +50,30 @@ namespace wl {
template <typename T>
struct ObjectTraits;
template <>
struct ObjectTraits<gtk_primary_selection_device_manager> {
static const wl_interface* interface;
static void (*deleter)(gtk_primary_selection_device_manager*);
};
template <>
struct ObjectTraits<gtk_primary_selection_device> {
static const wl_interface* interface;
static void (*deleter)(gtk_primary_selection_device*);
};
template <>
struct ObjectTraits<gtk_primary_selection_offer> {
static const wl_interface* interface;
static void (*deleter)(gtk_primary_selection_offer*);
};
template <>
struct ObjectTraits<gtk_primary_selection_source> {
static const wl_interface* interface;
static void (*deleter)(gtk_primary_selection_source*);
};
template <>
struct ObjectTraits<wl_buffer> {
static const wl_interface* interface;
......
......@@ -131,4 +131,12 @@ bool DrawBitmap(const SkBitmap& bitmap, ui::WaylandShmBuffer* out_buffer) {
return true;
}
void ReadDataFromFD(base::ScopedFD fd, std::string* contents) {
DCHECK(contents);
char buffer[1 << 10]; // 1 kB in bytes.
ssize_t length;
while ((length = read(fd.get(), buffer, sizeof(buffer))) > 0)
contents->append(buffer, length);
}
} // namespace wl
......@@ -5,9 +5,12 @@
#ifndef UI_OZONE_PLATFORM_WAYLAND_COMMON_WAYLAND_UTIL_H_
#define UI_OZONE_PLATFORM_WAYLAND_COMMON_WAYLAND_UTIL_H_
#include <string>
#include <wayland-client.h>
#include "base/callback.h"
#include "base/files/scoped_file.h"
#include "base/macros.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h"
......@@ -39,6 +42,9 @@ uint32_t IdentifyDirection(const ui::WaylandConnection& connection,
// currently mmap'ed in memory address space.
bool DrawBitmap(const SkBitmap& bitmap, ui::WaylandShmBuffer* out_buffer);
// Helper function to read data from a file.
void ReadDataFromFD(base::ScopedFD fd, std::string* contents);
} // namespace wl
#endif // UI_OZONE_PLATFORM_WAYLAND_COMMON_WAYLAND_UTIL_H_
// 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 "ui/ozone/platform/wayland/host/gtk_primary_selection_device.h"
#include <gtk-primary-selection-client-protocol.h>
#include "ui/ozone/platform/wayland/host/gtk_primary_selection_offer.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
namespace ui {
// static
GtkPrimarySelectionDevice::GtkPrimarySelectionDevice(
WaylandConnection* connection,
gtk_primary_selection_device* data_device)
: internal::WaylandDataDeviceBase(connection), data_device_(data_device) {
static const struct gtk_primary_selection_device_listener kListener = {
GtkPrimarySelectionDevice::OnDataOffer,
GtkPrimarySelectionDevice::OnSelection};
gtk_primary_selection_device_add_listener(data_device_.get(), &kListener,
this);
}
GtkPrimarySelectionDevice::~GtkPrimarySelectionDevice() = default;
// static
void GtkPrimarySelectionDevice::OnDataOffer(
void* data,
gtk_primary_selection_device* data_device,
gtk_primary_selection_offer* offer) {
auto* self = static_cast<GtkPrimarySelectionDevice*>(data);
DCHECK(self);
self->connection()->clipboard()->UpdateSequenceNumber(
ClipboardBuffer::kCopyPaste);
self->set_data_offer(std::make_unique<GtkPrimarySelectionOffer>(offer));
}
// static
void GtkPrimarySelectionDevice::OnSelection(
void* data,
gtk_primary_selection_device* data_device,
gtk_primary_selection_offer* offer) {
auto* self = static_cast<GtkPrimarySelectionDevice*>(data);
DCHECK(self);
// 'offer' will be null to indicate that the selection is no longer valid,
// i.e. there is no longer clipboard data available to paste.
if (!offer) {
self->ResetDataOffer();
// Clear Clipboard cache.
self->connection()->clipboard()->SetData(std::string(), std::string());
return;
}
DCHECK(self->data_offer());
self->data_offer()->EnsureTextMimeTypeIfNeeded();
}
} // namespace ui
// 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.
#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_GTK_PRIMARY_SELECTION_DEVICE_H_
#define UI_OZONE_PLATFORM_WAYLAND_HOST_GTK_PRIMARY_SELECTION_DEVICE_H_
#include <memory>
#include <string>
#include <vector>
#include "base/callback.h"
#include "base/macros.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h"
#include "ui/ozone/platform/wayland/host/internal/wayland_data_device_base.h"
struct gtk_primary_selection_device;
namespace ui {
class WaylandConnection;
// This class provides access to primary selection clipboard available on GTK.
class GtkPrimarySelectionDevice : public internal::WaylandDataDeviceBase {
public:
GtkPrimarySelectionDevice(WaylandConnection* connection,
gtk_primary_selection_device* data_device);
~GtkPrimarySelectionDevice() override;
gtk_primary_selection_device* data_device() const {
return data_device_.get();
}
private:
// gtk_primary_selection_device_listener callbacks
static void OnDataOffer(void* data,
gtk_primary_selection_device* data_device,
gtk_primary_selection_offer* offer);
static void OnSelection(void* data,
gtk_primary_selection_device* data_device,
gtk_primary_selection_offer* offer);
// The Wayland object wrapped by this instance.
wl::Object<gtk_primary_selection_device> data_device_;
DISALLOW_COPY_AND_ASSIGN(GtkPrimarySelectionDevice);
};
} // namespace ui
#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_GTK_PRIMARY_SELECTION_DEVICE_H_
// 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 "ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.h"
#include <gtk-primary-selection-client-protocol.h>
#include "ui/ozone/platform/wayland/host/gtk_primary_selection_source.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
namespace ui {
GtkPrimarySelectionDeviceManager::GtkPrimarySelectionDeviceManager(
gtk_primary_selection_device_manager* manager,
WaylandConnection* connection)
: gtk_primary_selection_device_manager_(manager), connection_(connection) {
DCHECK(connection_);
DCHECK(gtk_primary_selection_device_manager_);
}
GtkPrimarySelectionDeviceManager::~GtkPrimarySelectionDeviceManager() = default;
gtk_primary_selection_device* GtkPrimarySelectionDeviceManager::GetDevice() {
DCHECK(connection_->seat());
return gtk_primary_selection_device_manager_get_device(
gtk_primary_selection_device_manager_.get(), connection_->seat());
}
std::unique_ptr<GtkPrimarySelectionSource>
GtkPrimarySelectionDeviceManager::CreateSource() {
gtk_primary_selection_source* data_source =
gtk_primary_selection_device_manager_create_source(
gtk_primary_selection_device_manager_.get());
return std::make_unique<GtkPrimarySelectionSource>(data_source, connection_);
}
} // namespace ui
// 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.
#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_GTK_PRIMARY_SELECTION_DEVICE_MANAGER_H_
#define UI_OZONE_PLATFORM_WAYLAND_HOST_GTK_PRIMARY_SELECTION_DEVICE_MANAGER_H_
#include <memory>
#include "base/macros.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h"
struct gtk_primary_selection_device_manager;
struct gtk_primary_selection_device;
namespace ui {
class GtkPrimarySelectionSource;
class WaylandConnection;
class GtkPrimarySelectionDeviceManager {
public:
GtkPrimarySelectionDeviceManager(
gtk_primary_selection_device_manager* manager,
WaylandConnection* connection);
~GtkPrimarySelectionDeviceManager();
gtk_primary_selection_device* GetDevice();
std::unique_ptr<GtkPrimarySelectionSource> CreateSource();
private:
wl::Object<gtk_primary_selection_device_manager>
gtk_primary_selection_device_manager_;
WaylandConnection* connection_;
DISALLOW_COPY_AND_ASSIGN(GtkPrimarySelectionDeviceManager);
};
} // namespace ui
#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_GTK_PRIMARY_SELECTION_DEVICE_MANAGER_H_
// 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 "ui/ozone/platform/wayland/host/gtk_primary_selection_offer.h"
#include <gtk-primary-selection-client-protocol.h>
#include <fcntl.h>
#include <algorithm>
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "ui/base/clipboard/clipboard_constants.h"
namespace ui {
GtkPrimarySelectionOffer::GtkPrimarySelectionOffer(
gtk_primary_selection_offer* data_offer)
: data_offer_(data_offer) {
static const struct gtk_primary_selection_offer_listener kListener = {
GtkPrimarySelectionOffer::OnOffer};
gtk_primary_selection_offer_add_listener(data_offer, &kListener, this);
}
GtkPrimarySelectionOffer::~GtkPrimarySelectionOffer() {
data_offer_.reset();
}
base::ScopedFD GtkPrimarySelectionOffer::Receive(const std::string& mime_type) {
if (!base::Contains(mime_types(), mime_type))
return base::ScopedFD();
base::ScopedFD read_fd;
base::ScopedFD write_fd;
PCHECK(base::CreatePipe(&read_fd, &write_fd));
// If we needed to forcibly write "text/plain" as an available
// mimetype, then it is safer to "read" the clipboard data with
// a mimetype mime_type known to be available.
std::string effective_mime_type = mime_type;
if (mime_type == kMimeTypeText && text_plain_mime_type_inserted())
effective_mime_type = kMimeTypeTextUtf8;
gtk_primary_selection_offer_receive(
data_offer_.get(), effective_mime_type.data(), write_fd.get());
return read_fd;
}
// static
void GtkPrimarySelectionOffer::OnOffer(void* data,
gtk_primary_selection_offer* data_offer,
const char* mime_type) {
auto* self = static_cast<GtkPrimarySelectionOffer*>(data);
self->AddMimeType(mime_type);
}
} // namespace ui
// 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.
#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_GTK_PRIMARY_SELECTION_OFFER_H_
#define UI_OZONE_PLATFORM_WAYLAND_HOST_GTK_PRIMARY_SELECTION_OFFER_H_
#include <string>
#include <vector>
#include "base/files/scoped_file.h"
#include "base/macros.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h"
#include "ui/ozone/platform/wayland/host/internal/wayland_data_offer_base.h"
struct gtk_primary_selection_offer;
namespace ui {
// This class represents a piece of data offered for transfer by another
// client, the source client (see GtkPrimarySelectionSource for more).
// It is used by the primary selection mechanism.
//
// The offer describes MIME types that the data can be converted to and provides
// the mechanism for transferring the data directly from the source client.
class GtkPrimarySelectionOffer : public internal::WaylandDataOfferBase {
public:
// Takes ownership of data_offer.
explicit GtkPrimarySelectionOffer(gtk_primary_selection_offer* data_offer);
~GtkPrimarySelectionOffer() override;
// internal::WaylandDataOfferBase overrides:
base::ScopedFD Receive(const std::string& mime_type) override;
private:
// gtk_primary_selection_offer_listener callbacks.
static void OnOffer(void* data,
gtk_primary_selection_offer* data_offer,
const char* mime_type);
// The Wayland object wrapped by this instance.
wl::Object<gtk_primary_selection_offer> data_offer_;
DISALLOW_COPY_AND_ASSIGN(GtkPrimarySelectionOffer);
};
} // namespace ui
#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_GTK_PRIMARY_SELECTION_OFFER_H_
// 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 "ui/ozone/platform/wayland/host/gtk_primary_selection_source.h"
#include <gtk-primary-selection-client-protocol.h>
#include "base/files/file_util.h"
#include "base/logging.h"
#include "ui/base/clipboard/clipboard_constants.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
namespace ui {
GtkPrimarySelectionSource::GtkPrimarySelectionSource(
gtk_primary_selection_source* data_source,
WaylandConnection* connection)
: data_source_(data_source), connection_(connection) {
DCHECK(connection_);
DCHECK(data_source_);
static const struct gtk_primary_selection_source_listener
kDataSourceListener = {GtkPrimarySelectionSource::OnSend,
GtkPrimarySelectionSource::OnCancelled};
gtk_primary_selection_source_add_listener(data_source_.get(),
&kDataSourceListener, this);
}
GtkPrimarySelectionSource::~GtkPrimarySelectionSource() = default;
// static
void GtkPrimarySelectionSource::OnSend(void* data,
gtk_primary_selection_source* source,
const char* mime_type,
int32_t fd) {
GtkPrimarySelectionSource* self =
static_cast<GtkPrimarySelectionSource*>(data);
std::string contents;
base::Optional<std::vector<uint8_t>> mime_data;
self->GetClipboardData(mime_type, &mime_data);
if (!mime_data.has_value() && strcmp(mime_type, kMimeTypeTextUtf8) == 0)
self->GetClipboardData(kMimeTypeText, &mime_data);
contents.assign(mime_data->begin(), mime_data->end());
bool result =
base::WriteFileDescriptor(fd, contents.data(), contents.length());
DCHECK(result);
close(fd);
}
// static
void GtkPrimarySelectionSource::OnCancelled(
void* data,
gtk_primary_selection_source* source) {
GtkPrimarySelectionSource* self =
static_cast<GtkPrimarySelectionSource*>(data);
self->connection_->clipboard()->DataSourceCancelled(
ClipboardBuffer::kSelection);
}
void GtkPrimarySelectionSource::WriteToClipboard(
const PlatformClipboard::DataMap& data_map) {
for (const auto& data : data_map) {
gtk_primary_selection_source_offer(data_source_.get(), data.first.c_str());
if (strcmp(data.first.c_str(), kMimeTypeText) == 0)
gtk_primary_selection_source_offer(data_source_.get(), kMimeTypeTextUtf8);
}
gtk_primary_selection_device_set_selection(
connection_->primary_selection_device(), data_source_.get(),
connection_->serial());
connection_->ScheduleFlush();
}
} // namespace ui
// 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.
#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_GTK_PRIMARY_SELECTION_SOURCE_H_
#define UI_OZONE_PLATFORM_WAYLAND_HOST_GTK_PRIMARY_SELECTION_SOURCE_H_
#include "base/macros.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h"
#include "ui/ozone/platform/wayland/host/internal/wayland_data_source_base.h"
#include "ui/ozone/public/platform_clipboard.h"
struct gtk_primary_selection_source;
namespace ui {
class WaylandConnection;
class GtkPrimarySelectionSource : public internal::WaylandDataSourceBase {
public:
// Takes ownership of data_source.
GtkPrimarySelectionSource(gtk_primary_selection_source* data_source,
WaylandConnection* connection);
~GtkPrimarySelectionSource() override;
void WriteToClipboard(const PlatformClipboard::DataMap& data_map) override;
private:
// gtk_primary_selection_source_listener callbacks
static void OnSend(void* data,
gtk_primary_selection_source* source,
const char* mime_type,
int32_t fd);
static void OnCancelled(void* data, gtk_primary_selection_source* source);
// The gtk_primary_selection_source wrapped by this instance.
wl::Object<gtk_primary_selection_source> data_source_;
WaylandConnection* connection_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(GtkPrimarySelectionSource);
};
} // namespace ui
#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_GTK_PRIMARY_SELECTION_SOURCE_H_
// 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 "ui/ozone/platform/wayland/host/internal/wayland_data_device_base.h"
#include <utility>
#include "base/bind.h"
#include "ui/ozone/platform/wayland/common/wayland_util.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
namespace ui {
namespace internal {
WaylandDataDeviceBase::WaylandDataDeviceBase(WaylandConnection* connection)
: connection_(connection) {}
WaylandDataDeviceBase::~WaylandDataDeviceBase() = default;
const std::vector<std::string>& WaylandDataDeviceBase::GetAvailableMimeTypes()
const {
if (!data_offer_) {
static std::vector<std::string> dummy;
return dummy;
}
return data_offer_->mime_types();
}
bool WaylandDataDeviceBase::RequestSelectionData(const std::string& mime_type) {
if (!data_offer_)
return false;
base::ScopedFD fd = data_offer_->Receive(mime_type);
if (!fd.is_valid()) {
LOG(ERROR) << "Failed to open file descriptor.";
return false;
}
// Ensure there is not pending operation to be performed by the compositor,
// otherwise read(..) can block awaiting data to be sent to pipe.
RegisterDeferredReadClosure(
base::BindOnce(&GtkPrimarySelectionDevice::ReadClipboardDataFromFD,
base::Unretained(this), std::move(fd), mime_type));
RegisterDeferredReadCallback();
return true;
}
void WaylandDataDeviceBase::ResetDataOffer() {
data_offer_.reset();
}
void WaylandDataDeviceBase::ReadClipboardDataFromFD(
base::ScopedFD fd,
const std::string& mime_type) {
std::string contents;
wl::ReadDataFromFD(std::move(fd), &contents);
connection_->clipboard()->SetData(contents, mime_type);
}
void WaylandDataDeviceBase::RegisterDeferredReadCallback() {
DCHECK(!deferred_read_callback_);
deferred_read_callback_.reset(wl_display_sync(connection_->display()));
static const wl_callback_listener kListener = {
GtkPrimarySelectionDevice::DeferredReadCallback};
wl_callback_add_listener(deferred_read_callback_.get(), &kListener, this);
connection_->ScheduleFlush();
}
void WaylandDataDeviceBase::RegisterDeferredReadClosure(
base::OnceClosure closure) {
deferred_read_closure_ = std::move(closure);
}
// static
void WaylandDataDeviceBase::DeferredReadCallback(void* data,
struct wl_callback* cb,
uint32_t time) {
auto* data_device = static_cast<WaylandDataDeviceBase*>(data);
DCHECK(data_device);
data_device->DeferredReadCallbackInternal(cb, time);
}
void WaylandDataDeviceBase::DeferredReadCallbackInternal(struct wl_callback* cb,
uint32_t time) {
DCHECK(!deferred_read_closure_.is_null());
std::move(deferred_read_closure_).Run();
deferred_read_callback_.reset();
}
} // namespace internal
} // namespace ui
// 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.
#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_INTERNAL_WAYLAND_DATA_DEVICE_BASE_H_
#define UI_OZONE_PLATFORM_WAYLAND_HOST_INTERNAL_WAYLAND_DATA_DEVICE_BASE_H_
#include <string>
#include "base/callback.h"
#include "base/files/scoped_file.h"
#include "base/macros.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h"
namespace ui {
class WaylandConnection;
namespace internal {
class WaylandDataOfferBase;
// Implements high level (protocol-agnostic) interface to a Wayland data device.
class WaylandDataDeviceBase {
public:
explicit WaylandDataDeviceBase(WaylandConnection* connection);
virtual ~WaylandDataDeviceBase();
// Returns MIME types given by the current data offer.
const std::vector<std::string>& GetAvailableMimeTypes() const;
// Extracts data of the specified MIME type from the data offer.
bool RequestSelectionData(const std::string& mime_type);
protected:
WaylandConnection* connection() const { return connection_; }
WaylandDataOfferBase* data_offer() { return data_offer_.get(); }
void set_data_offer(std::unique_ptr<WaylandDataOfferBase> data_offer) {
data_offer_ = std::move(data_offer);
}
// Resets the data offer.
void ResetDataOffer();
// Reads data of the requested MIME type from the data offer and gives it to
// the clipboard linked to the Wayland connection.
void ReadClipboardDataFromFD(base::ScopedFD fd, const std::string& mime_type);
// Registers DeferredReadCallback as display sync callback listener, to
// ensure there is no pending operation to be performed by the compositor,
// otherwise read(..) could block awaiting data to be sent to pipe. It is
// reset once it's called.
void RegisterDeferredReadCallback();
void RegisterDeferredReadClosure(base::OnceClosure closure);
private:
// wl_callback_listener callback
static void DeferredReadCallback(void* data,
struct wl_callback* cb,
uint32_t time);
void DeferredReadCallbackInternal(struct wl_callback* cb,
uint32_t time);
// Used to call out to WaylandConnection once clipboard data
// has been successfully read.
WaylandConnection* const connection_ = nullptr;
// Offer that holds the most-recent clipboard selection, or null if no
// clipboard data is available.
std::unique_ptr<WaylandDataOfferBase> data_offer_;
// Before blocking on read(), make sure server has written data on the pipe.
base::OnceClosure deferred_read_closure_;
wl::Object<wl_callback> deferred_read_callback_;
DISALLOW_COPY_AND_ASSIGN(WaylandDataDeviceBase);
};
} // namespace internal
} // namespace ui
#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_INTERNAL_WAYLAND_DATA_DEVICE_BASE_H_
// 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 "ui/ozone/platform/wayland/host/internal/wayland_data_offer_base.h"
#include "base/stl_util.h"
#include "ui/base/clipboard/clipboard_constants.h"
namespace ui {
namespace internal {
namespace {
const char kString[] = "STRING";
const char kText[] = "TEXT";
const char kUtf8String[] = "UTF8_STRING";
} // namespace
WaylandDataOfferBase::WaylandDataOfferBase() = default;
WaylandDataOfferBase::~WaylandDataOfferBase() = default;
void WaylandDataOfferBase::EnsureTextMimeTypeIfNeeded() {
if (base::Contains(mime_types_, kMimeTypeText))
return;
if (std::any_of(mime_types_.begin(), mime_types_.end(),
[](const std::string& mime_type) {
return mime_type == kString || mime_type == kText ||
mime_type == kMimeTypeTextUtf8 ||
mime_type == kUtf8String;
})) {
mime_types_.push_back(kMimeTypeText);
text_plain_mime_type_inserted_ = true;
}
}
void WaylandDataOfferBase::AddMimeType(const char* mime_type) {
mime_types_.push_back(mime_type);
}
} // namespace internal
} // namespace ui
// 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.
#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_INTERNAL_WAYLAND_DATA_OFFER_BASE_H_
#define UI_OZONE_PLATFORM_WAYLAND_HOST_INTERNAL_WAYLAND_DATA_OFFER_BASE_H_
#include <string>
#include <vector>
#include "base/files/scoped_file.h"
#include "base/macros.h"
namespace ui {
namespace internal {
// Implements common part of WaylandDataOffer and GtkPrimarySelectionOffer
// (which is handling of the clipboard data).
class WaylandDataOfferBase {
public:
WaylandDataOfferBase();
virtual ~WaylandDataOfferBase();
const std::vector<std::string>& mime_types() const { return mime_types_; }
bool text_plain_mime_type_inserted() const {
return text_plain_mime_type_inserted_;
}
// Some X11 applications on Gnome/Wayland (running through XWayland)
// do not send the "text/plain" MIME type that Chrome relies on, but
// instead they send types like "text/plain;charset=utf-8".
// When it happens, this method forcibly injects "text/plain" into the
// list of provided MIME types so that Chrome clipboard's machinery
// works fine.
void EnsureTextMimeTypeIfNeeded();
// Inserts the specified MIME type into the internal list.
void AddMimeType(const char* mime_type);
// Creates a pipe (read & write FDs), passes the write end of the pipe
// to the compositor and returns the read end.
virtual base::ScopedFD Receive(const std::string& mime_type) = 0;
private:
// MIME types provided in this offer.
std::vector<std::string> mime_types_;
// Whether "text/plain" had been inserted forcibly.
bool text_plain_mime_type_inserted_ = false;
DISALLOW_COPY_AND_ASSIGN(WaylandDataOfferBase);
};
} // namespace internal
} // namespace ui
#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_INTERNAL_WAYLAND_DATA_OFFER_BASE_H_
// 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 "ui/ozone/platform/wayland/host/internal/wayland_data_source_base.h"
namespace ui {
namespace internal {
WaylandDataSourceBase::WaylandDataSourceBase() = default;
WaylandDataSourceBase::~WaylandDataSourceBase() = default;
void WaylandDataSourceBase::GetClipboardData(
const std::string& mime_type,
base::Optional<std::vector<uint8_t>>* data) const {
auto it = data_map_.find(mime_type);
if (it == data_map_.end())
return;
data->emplace(it->second);
}
} // namespace internal
} // namespace ui
// 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.
#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_INTERNAL_WAYLAND_DATA_SOURCE_BASE_H_
#define UI_OZONE_PLATFORM_WAYLAND_HOST_INTERNAL_WAYLAND_DATA_SOURCE_BASE_H_
#include "base/macros.h"
#include "ui/ozone/public/platform_clipboard.h"
namespace ui {
namespace internal {
// Implements high level (protocol-agnostic) interface to a Wayland data source.
class WaylandDataSourceBase {
public:
WaylandDataSourceBase();
virtual ~WaylandDataSourceBase();
void set_data_map(const PlatformClipboard::DataMap& data_map) {
data_map_ = data_map;
}
// Writes data to the system clipboard using the protocol-defined data source.
virtual void WriteToClipboard(const PlatformClipboard::DataMap& data_map) = 0;
protected:
void GetClipboardData(const std::string& mime_type,
base::Optional<std::vector<uint8_t>>* data) const;
private:
PlatformClipboard::DataMap data_map_;
DISALLOW_COPY_AND_ASSIGN(WaylandDataSourceBase);
};
} // namespace internal
} // namespace ui
#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_INTERNAL_WAYLAND_DATA_SOURCE_BASE_H_
......@@ -3,6 +3,11 @@
// found in the LICENSE file.
#include "ui/ozone/platform/wayland/host/wayland_clipboard.h"
#include "ui/ozone/platform/wayland/host/gtk_primary_selection_device.h"
#include "ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.h"
#include "ui/ozone/platform/wayland/host/gtk_primary_selection_source.h"
#include "ui/ozone/platform/wayland/host/internal/wayland_data_source_base.h"
#include "ui/ozone/platform/wayland/host/wayland_data_device.h"
#include "ui/ozone/platform/wayland/host/wayland_data_device_manager.h"
......@@ -10,8 +15,13 @@ namespace ui {
WaylandClipboard::WaylandClipboard(
WaylandDataDeviceManager* data_device_manager,
WaylandDataDevice* data_device)
: data_device_manager_(data_device_manager), data_device_(data_device) {
WaylandDataDevice* data_device,
GtkPrimarySelectionDeviceManager* primary_selection_device_manager,
GtkPrimarySelectionDevice* primary_selection_device)
: data_device_manager_(data_device_manager),
data_device_(data_device),
primary_selection_device_manager_(primary_selection_device_manager),
primary_selection_device_(primary_selection_device) {
DCHECK(data_device_manager_);
DCHECK(data_device_);
}
......@@ -22,17 +32,25 @@ void WaylandClipboard::OfferClipboardData(
ClipboardBuffer buffer,
const PlatformClipboard::DataMap& data_map,
PlatformClipboard::OfferDataClosure callback) {
// TODO(https://crbug.com/921950): Implement primary selection.
if (buffer != ClipboardBuffer::kCopyPaste) {
std::move(callback).Run();
return;
internal::WaylandDataSourceBase* data_source = nullptr;
if (buffer == ClipboardBuffer::kCopyPaste) {
if (!clipboard_data_source_)
clipboard_data_source_ = data_device_manager_->CreateSource();
data_source = clipboard_data_source_.get();
} else {
if (!primary_selection_device_manager_) {
std::move(callback).Run();
return;
}
if (!primary_data_source_)
primary_data_source_ = primary_selection_device_manager_->CreateSource();
data_source = primary_data_source_.get();
}
if (!clipboard_data_source_) {
clipboard_data_source_ = data_device_manager_->CreateSource();
clipboard_data_source_->WriteToClipboard(data_map);
}
clipboard_data_source_->UpdateDataMap(data_map);
DCHECK(data_source);
data_source->WriteToClipboard(data_map);
data_source->set_data_map(data_map);
std::move(callback).Run();
}
......@@ -41,26 +59,23 @@ void WaylandClipboard::RequestClipboardData(
const std::string& mime_type,
PlatformClipboard::DataMap* data_map,
PlatformClipboard::RequestDataClosure callback) {
// TODO(https://crbug.com/921950): Implement primary selection.
if (buffer != ClipboardBuffer::kCopyPaste) {
std::move(callback).Run({});
return;
}
read_clipboard_closure_ = std::move(callback);
DCHECK(data_map);
data_map_ = data_map;
if (!data_device_->RequestSelectionData(mime_type))
SetData({}, mime_type);
if (buffer == ClipboardBuffer::kCopyPaste) {
if (!data_device_->RequestSelectionData(mime_type))
SetData({}, mime_type);
} else {
if (!primary_selection_device_->RequestSelectionData(mime_type))
SetData({}, mime_type);
}
}
bool WaylandClipboard::IsSelectionOwner(ClipboardBuffer buffer) {
// TODO(https://crbug.com/921950): Implement primary selection.
if (buffer != ClipboardBuffer::kCopyPaste)
return false;
return !!clipboard_data_source_;
if (buffer == ClipboardBuffer::kCopyPaste)
return !!clipboard_data_source_;
else
return !!primary_data_source_;
}
void WaylandClipboard::SetSequenceNumberUpdateCb(
......@@ -73,19 +88,24 @@ void WaylandClipboard::SetSequenceNumberUpdateCb(
void WaylandClipboard::GetAvailableMimeTypes(
ClipboardBuffer buffer,
PlatformClipboard::GetMimeTypesClosure callback) {
// TODO(https://crbug.com/921950): Implement primary selection.
if (buffer != ClipboardBuffer::kCopyPaste) {
std::move(callback).Run({});
return;
if (buffer == ClipboardBuffer::kCopyPaste) {
std::move(callback).Run(data_device_->GetAvailableMimeTypes());
} else {
DCHECK(primary_selection_device_);
std::move(callback).Run(primary_selection_device_->GetAvailableMimeTypes());
}
std::move(callback).Run(data_device_->GetAvailableMimeTypes());
}
void WaylandClipboard::DataSourceCancelled() {
DCHECK(clipboard_data_source_);
SetData({}, {});
clipboard_data_source_.reset();
void WaylandClipboard::DataSourceCancelled(ClipboardBuffer buffer) {
if (buffer == ClipboardBuffer::kCopyPaste) {
DCHECK(clipboard_data_source_);
SetData({}, {});
clipboard_data_source_.reset();
} else {
DCHECK(primary_data_source_);
SetData({}, {});
primary_data_source_.reset();
}
}
void WaylandClipboard::SetData(const std::string& contents,
......@@ -105,10 +125,6 @@ void WaylandClipboard::SetData(const std::string& contents,
}
void WaylandClipboard::UpdateSequenceNumber(ClipboardBuffer buffer) {
// TODO(https://crbug.com/921950): Implement primary selection.
if (buffer != ClipboardBuffer::kCopyPaste)
return;
if (!update_sequence_cb_.is_null())
update_sequence_cb_.Run(buffer);
}
......
......@@ -6,11 +6,15 @@
#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_CLIPBOARD_H_
#include "base/callback.h"
#include "base/macros.h"
#include "ui/ozone/platform/wayland/host/wayland_data_source.h"
#include "ui/ozone/public/platform_clipboard.h"
namespace ui {
class GtkPrimarySelectionDevice;
class GtkPrimarySelectionDeviceManager;
class GtkPrimarySelectionSource;
class WaylandDataDevice;
class WaylandDataDeviceManager;
......@@ -20,8 +24,11 @@ class WaylandDataDeviceManager;
// manager.
class WaylandClipboard : public PlatformClipboard {
public:
WaylandClipboard(WaylandDataDeviceManager* data_device_manager,
WaylandDataDevice* data_device);
WaylandClipboard(
WaylandDataDeviceManager* data_device_manager,
WaylandDataDevice* data_device,
GtkPrimarySelectionDeviceManager* primary_selection_device_manager,
GtkPrimarySelectionDevice* primary_selection_device);
~WaylandClipboard() override;
// PlatformClipboard.
......@@ -41,7 +48,7 @@ class WaylandClipboard : public PlatformClipboard {
void SetSequenceNumberUpdateCb(
PlatformClipboard::SequenceNumberUpdateCb cb) override;
void DataSourceCancelled();
void DataSourceCancelled(ClipboardBuffer buffer);
void SetData(const std::string& contents, const std::string& mime_type);
void UpdateSequenceNumber(ClipboardBuffer buffer);
......@@ -58,10 +65,13 @@ class WaylandClipboard : public PlatformClipboard {
PlatformClipboard::RequestDataClosure read_clipboard_closure_;
std::unique_ptr<WaylandDataSource> clipboard_data_source_;
std::unique_ptr<GtkPrimarySelectionSource> primary_data_source_;
// These two instances are owned by the connection.
WaylandDataDeviceManager* const data_device_manager_ = nullptr;
WaylandDataDevice* const data_device_ = nullptr;
// These four instances are owned by the connection.
WaylandDataDeviceManager* const data_device_manager_;
WaylandDataDevice* const data_device_;
GtkPrimarySelectionDeviceManager* const primary_selection_device_manager_;
GtkPrimarySelectionDevice* const primary_selection_device_;
DISALLOW_COPY_AND_ASSIGN(WaylandClipboard);
};
......
......@@ -34,6 +34,7 @@ namespace ui {
namespace {
constexpr uint32_t kMaxCompositorVersion = 4;
constexpr uint32_t kMaxGtkPrimarySelectionDeviceManagerVersion = 1;
constexpr uint32_t kMaxLinuxDmabufVersion = 3;
constexpr uint32_t kMaxSeatVersion = 4;
constexpr uint32_t kMaxShmVersion = 1;
......@@ -234,8 +235,15 @@ void WaylandConnection::EnsureDataDevice() {
DCHECK(!data_device_);
wl_data_device* data_device = data_device_manager_->GetDevice();
data_device_ = std::make_unique<WaylandDataDevice>(this, data_device);
clipboard_ = std::make_unique<WaylandClipboard>(data_device_manager_.get(),
data_device_.get());
if (primary_selection_device_manager_) {
primary_selection_device_ = std::make_unique<GtkPrimarySelectionDevice>(
this, primary_selection_device_manager_->GetDevice());
}
clipboard_ = std::make_unique<WaylandClipboard>(
data_device_manager_.get(), data_device_.get(),
primary_selection_device_manager_.get(), primary_selection_device_.get());
}
bool WaylandConnection::BeginWatchingFd(
......@@ -352,6 +360,14 @@ void WaylandConnection::Global(void* data,
std::make_unique<WaylandDataDeviceManager>(
data_device_manager.release(), connection);
connection->EnsureDataDevice();
} else if (!connection->primary_selection_device_manager_ &&
strcmp(interface, "gtk_primary_selection_device_manager") == 0) {
wl::Object<gtk_primary_selection_device_manager> manager =
wl::Bind<gtk_primary_selection_device_manager>(
registry, name, kMaxGtkPrimarySelectionDeviceManagerVersion);
connection->primary_selection_device_manager_ =
std::make_unique<GtkPrimarySelectionDeviceManager>(manager.release(),
connection);
} else if (!connection->zwp_dmabuf_ &&
(strcmp(interface, "zwp_linux_dmabuf_v1") == 0)) {
wl::Object<zwp_linux_dmabuf_v1> zwp_linux_dmabuf =
......
......@@ -16,6 +16,8 @@
#include "ui/gfx/buffer_types.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h"
#include "ui/ozone/platform/wayland/host/gtk_primary_selection_device.h"
#include "ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.h"
#include "ui/ozone/platform/wayland/host/wayland_clipboard.h"
#include "ui/ozone/platform/wayland/host/wayland_cursor_position.h"
#include "ui/ozone/platform/wayland/host/wayland_data_device.h"
......@@ -54,6 +56,9 @@ class WaylandConnection : public PlatformEventSource,
zxdg_shell_v6* shell_v6() const { return shell_v6_.get(); }
wl_seat* seat() const { return seat_.get(); }
wl_data_device* data_device() const { return data_device_->data_device(); }
gtk_primary_selection_device* primary_selection_device() const {
return primary_selection_device_->data_device();
}
wp_presentation* presentation() const { return presentation_.get(); }
zwp_text_input_manager_v1* text_input_manager_v1() const {
return text_input_manager_v1_.get();
......@@ -188,6 +193,10 @@ class WaylandConnection : public PlatformEventSource,
std::unique_ptr<WaylandShm> shm_;
std::unique_ptr<WaylandBufferManagerHost> buffer_manager_host_;
std::unique_ptr<GtkPrimarySelectionDeviceManager>
primary_selection_device_manager_;
std::unique_ptr<GtkPrimarySelectionDevice> primary_selection_device_;
// Manages Wayland windows.
WaylandWindowManager wayland_window_manager_;
......
......@@ -12,6 +12,7 @@
#include "base/strings/utf_string_conversions.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "ui/base/clipboard/clipboard_constants.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/base/dragdrop/os_exchange_data_provider_aura.h"
......@@ -23,9 +24,6 @@ namespace ui {
namespace {
constexpr char kMimeTypeText[] = "text/plain";
constexpr char kMimeTypeTextUTF8[] = "text/plain;charset=utf-8";
int GetOperation(uint32_t source_actions, uint32_t dnd_action) {
uint32_t action = dnd_action != WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE
? dnd_action
......@@ -56,7 +54,7 @@ void AddToOSExchangeData(const std::string& data,
const std::string& mime_type,
OSExchangeData* os_exchange_data) {
DCHECK(os_exchange_data);
if ((mime_type == kMimeTypeText || mime_type == kMimeTypeTextUTF8)) {
if ((mime_type == kMimeTypeText || mime_type == kMimeTypeTextUtf8)) {
DCHECK(!os_exchange_data->HasString());
AddStringToOSExchangeData(data, os_exchange_data);
return;
......@@ -70,7 +68,7 @@ void AddToOSExchangeData(const std::string& data,
// static
WaylandDataDevice::WaylandDataDevice(WaylandConnection* connection,
wl_data_device* data_device)
: data_device_(data_device), connection_(connection) {
: internal::WaylandDataDeviceBase(connection), data_device_(data_device) {
static const struct wl_data_device_listener kDataDeviceListener = {
WaylandDataDevice::OnDataOffer, WaylandDataDevice::OnEnter,
WaylandDataDevice::OnLeave, WaylandDataDevice::OnMotion,
......@@ -80,25 +78,6 @@ WaylandDataDevice::WaylandDataDevice(WaylandConnection* connection,
WaylandDataDevice::~WaylandDataDevice() = default;
bool WaylandDataDevice::RequestSelectionData(const std::string& mime_type) {
if (!selection_offer_)
return false;
base::ScopedFD fd = selection_offer_->Receive(mime_type);
if (!fd.is_valid()) {
LOG(ERROR) << "Failed to open file descriptor.";
return false;
}
// Ensure there is not pending operation to be performed by the compositor,
// otherwise read(..) can block awaiting data to be sent to pipe.
deferred_read_closure_ =
base::BindOnce(&WaylandDataDevice::ReadClipboardDataFromFD,
base::Unretained(this), std::move(fd), mime_type);
RegisterDeferredReadCallback();
return true;
}
void WaylandDataDevice::RequestDragData(
const std::string& mime_type,
base::OnceCallback<void(const std::string&)> callback) {
......@@ -110,9 +89,9 @@ void WaylandDataDevice::RequestDragData(
// Ensure there is not pending operation to be performed by the compositor,
// otherwise read(..) can block awaiting data to be sent to pipe.
deferred_read_closure_ = base::BindOnce(
RegisterDeferredReadClosure(base::BindOnce(
&WaylandDataDevice::ReadDragDataFromFD, base::Unretained(this),
std::move(fd), std::move(callback));
std::move(fd), std::move(callback)));
RegisterDeferredReadCallback();
}
......@@ -121,7 +100,7 @@ void WaylandDataDevice::DeliverDragData(const std::string& mime_type,
DCHECK(buffer);
DCHECK(source_data_);
if (mime_type != kMimeTypeText && mime_type != kMimeTypeTextUTF8)
if (mime_type != kMimeTypeText && mime_type != kMimeTypeTextUtf8)
return;
const OSExchangeData::FilenameToURLPolicy policy =
......@@ -148,7 +127,7 @@ void WaylandDataDevice::StartDrag(wl_data_source* data_source,
DCHECK(data_source);
WaylandWindow* window =
connection_->wayland_window_manager()->GetCurrentFocusedWindow();
connection()->wayland_window_manager()->GetCurrentFocusedWindow();
if (!window) {
LOG(ERROR) << "Failed to get focused window.";
return;
......@@ -156,47 +135,24 @@ void WaylandDataDevice::StartDrag(wl_data_source* data_source,
const SkBitmap* icon = PrepareDragIcon(data);
source_data_ = std::make_unique<ui::OSExchangeData>(data.provider().Clone());
wl_data_device_start_drag(data_device_.get(), data_source, window->surface(),
icon_surface_.get(), connection_->serial());
icon_surface_.get(), connection()->serial());
if (icon)
DrawDragIcon(icon);
connection_->ScheduleFlush();
connection()->ScheduleFlush();
}
void WaylandDataDevice::ResetSourceData() {
source_data_.reset();
}
std::vector<std::string> WaylandDataDevice::GetAvailableMimeTypes() {
if (selection_offer_)
return selection_offer_->GetAvailableMimeTypes();
return std::vector<std::string>();
}
void WaylandDataDevice::ReadClipboardDataFromFD(base::ScopedFD fd,
const std::string& mime_type) {
std::string contents;
ReadDataFromFD(std::move(fd), &contents);
connection_->clipboard()->SetData(contents, mime_type);
}
void WaylandDataDevice::ReadDragDataFromFD(
base::ScopedFD fd,
base::OnceCallback<void(const std::string&)> callback) {
std::string contents;
ReadDataFromFD(std::move(fd), &contents);
wl::ReadDataFromFD(std::move(fd), &contents);
std::move(callback).Run(contents);
}
void WaylandDataDevice::ReadDataFromFD(base::ScopedFD fd,
std::string* contents) {
DCHECK(contents);
char buffer[1 << 10]; // 1 kB in bytes.
ssize_t length;
while ((length = read(fd.get(), buffer, sizeof(buffer))) > 0)
contents->append(buffer, length);
}
void WaylandDataDevice::HandleDeferredLeaveIfNeeded() {
if (!is_leaving_)
return;
......@@ -210,7 +166,7 @@ void WaylandDataDevice::OnDataOffer(void* data,
wl_data_offer* offer) {
auto* self = static_cast<WaylandDataDevice*>(data);
self->connection_->clipboard()->UpdateSequenceNumber(
self->connection()->clipboard()->UpdateSequenceNumber(
ClipboardBuffer::kCopyPaste);
DCHECK(!self->new_offer_);
......@@ -241,7 +197,7 @@ void WaylandDataDevice::OnEnter(void* data,
// all mime types offered because current implementation doesn't decide
// action based on mime type.
self->unprocessed_mime_types_.clear();
for (auto mime : self->drag_offer_->GetAvailableMimeTypes()) {
for (auto mime : self->drag_offer_->mime_types()) {
self->unprocessed_mime_types_.push_back(mime);
self->drag_offer_->Accept(serial, mime);
}
......@@ -336,45 +292,24 @@ void WaylandDataDevice::OnSelection(void* data,
// 'offer' will be null to indicate that the selection is no longer valid,
// i.e. there is no longer clipboard data available to paste.
if (!offer) {
self->selection_offer_.reset();
self->ResetDataOffer();
// Clear Clipboard cache.
self->connection_->clipboard()->SetData(std::string(), std::string());
self->connection()->clipboard()->SetData({}, {});
return;
}
DCHECK(self->new_offer_);
self->selection_offer_ = std::move(self->new_offer_);
self->selection_offer_->EnsureTextMimeTypeIfNeeded();
}
void WaylandDataDevice::RegisterDeferredReadCallback() {
static const wl_callback_listener kDeferredReadListener = {
WaylandDataDevice::DeferredReadCallback};
DCHECK(!deferred_read_callback_);
deferred_read_callback_.reset(wl_display_sync(connection_->display()));
wl_callback_add_listener(deferred_read_callback_.get(),
&kDeferredReadListener, this);
connection_->ScheduleFlush();
}
self->set_data_offer(std::move(self->new_offer_));
void WaylandDataDevice::DeferredReadCallback(void* data,
struct wl_callback* cb,
uint32_t time) {
auto* data_device = static_cast<WaylandDataDevice*>(data);
DCHECK(data_device);
DCHECK(!data_device->deferred_read_closure_.is_null());
std::move(data_device->deferred_read_closure_).Run();
data_device->deferred_read_callback_.reset();
self->data_offer()->EnsureTextMimeTypeIfNeeded();
}
const SkBitmap* WaylandDataDevice::PrepareDragIcon(const OSExchangeData& data) {
const SkBitmap* icon_bitmap = data.provider().GetDragImage().bitmap();
if (!icon_bitmap || icon_bitmap->empty())
return nullptr;
icon_surface_.reset(wl_compositor_create_surface(connection_->compositor()));
icon_surface_.reset(wl_compositor_create_surface(connection()->compositor()));
DCHECK(icon_surface_);
return icon_bitmap;
}
......@@ -385,7 +320,7 @@ void WaylandDataDevice::DrawDragIcon(const SkBitmap* icon_bitmap) {
gfx::Size size(icon_bitmap->width(), icon_bitmap->height());
if (!shm_buffer_ || shm_buffer_->size() != size) {
shm_buffer_ = std::make_unique<WaylandShmBuffer>(connection_->shm(), size);
shm_buffer_ = std::make_unique<WaylandShmBuffer>(connection()->shm(), size);
if (!shm_buffer_->IsValid()) {
LOG(ERROR) << "Failed to create drag icon buffer.";
return;
......@@ -436,7 +371,7 @@ void WaylandDataDevice::HandleReceivedData(
std::string WaylandDataDevice::SelectNextMimeType() {
while (!unprocessed_mime_types_.empty()) {
std::string& mime_type = unprocessed_mime_types_.front();
if ((mime_type == kMimeTypeText || mime_type == kMimeTypeTextUTF8) &&
if ((mime_type == kMimeTypeText || mime_type == kMimeTypeTextUtf8) &&
!received_data_->HasString()) {
return mime_type;
}
......
......@@ -17,6 +17,7 @@
#include "base/macros.h"
#include "ui/gfx/geometry/size.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h"
#include "ui/ozone/platform/wayland/host/internal/wayland_data_device_base.h"
#include "ui/ozone/platform/wayland/host/wayland_data_offer.h"
#include "ui/ozone/platform/wayland/host/wayland_shm_buffer.h"
......@@ -31,12 +32,10 @@ class WaylandWindow;
// This class provides access to inter-client data transfer mechanisms
// such as copy-and-paste and drag-and-drop mechanisms.
class WaylandDataDevice {
class WaylandDataDevice : public internal::WaylandDataDeviceBase {
public:
WaylandDataDevice(WaylandConnection* connection, wl_data_device* data_device);
~WaylandDataDevice();
bool RequestSelectionData(const std::string& mime_type);
~WaylandDataDevice() override;
// Requests the data to the platform when Chromium gets drag-and-drop started
// by others. Once reading the data from platform is done, |callback| should
......@@ -53,22 +52,15 @@ class WaylandDataDevice {
// Resets |source_data_| when the dragging is finished.
void ResetSourceData();
std::vector<std::string> GetAvailableMimeTypes();
wl_data_device* data_device() const { return data_device_.get(); }
bool IsDragEntered() { return drag_offer_ != nullptr; }
private:
void ReadClipboardDataFromFD(base::ScopedFD fd, const std::string& mime_type);
void ReadDragDataFromFD(
base::ScopedFD fd,
base::OnceCallback<void(const std::string&)> callback);
// Helper function to read data from fd.
void ReadDataFromFD(base::ScopedFD fd, std::string* contents);
// If source_data_ is not set, data is being dragged from an external
// application (non-chromium).
bool IsDraggingExternalData() const { return !source_data_; }
......@@ -108,15 +100,6 @@ class WaylandDataDevice {
wl_data_device* data_device,
wl_data_offer* id);
// Registers DeferredReadCallback as display sync callback listener, to
// ensure there is no pending operation to be performed by the compositor,
// otherwise read(..) could block awaiting data to be sent to pipe. It is
// reset once it's called.
void RegisterDeferredReadCallback();
static void DeferredReadCallback(void* data,
struct wl_callback* cb,
uint32_t time);
// Returns the drag icon bitmap and creates and wayland surface to draw it
// on, if a valid drag image is present in |data|; otherwise returns null.
const SkBitmap* PrepareDragIcon(const OSExchangeData& data);
......@@ -141,10 +124,6 @@ class WaylandDataDevice {
// The wl_data_device wrapped by this WaylandDataDevice.
wl::Object<wl_data_device> data_device_;
// Used to call out to WaylandConnection once clipboard data
// has been successfully read.
WaylandConnection* connection_ = nullptr;
// There are two separate data offers at a time, the drag offer and the
// selection offer, each with independent lifetimes. When we receive a new
// offer, it is not immediately possible to know whether the new offer is the
......@@ -152,20 +131,12 @@ class WaylandDataDevice {
// of new data offers temporarily until its identity becomes known.
std::unique_ptr<WaylandDataOffer> new_offer_;
// Offer that holds the most-recent clipboard selection, or null if no
// clipboard data is available.
std::unique_ptr<WaylandDataOffer> selection_offer_;
// Offer to receive data from another process via drag-and-drop, or null if no
// drag-and-drop from another process is in progress.
std::unique_ptr<WaylandDataOffer> drag_offer_;
WaylandWindow* window_ = nullptr;
// Make sure server has written data on the pipe, before block on read().
base::OnceClosure deferred_read_closure_;
wl::Object<wl_callback> deferred_read_callback_;
bool is_handling_dropped_data_ = false;
bool is_leaving_ = false;
......
......@@ -10,19 +10,10 @@
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "ui/base/clipboard/clipboard_constants.h"
namespace ui {
namespace {
const char kString[] = "STRING";
const char kText[] = "TEXT";
const char kTextPlain[] = "text/plain";
const char kTextPlainUtf8[] = "text/plain;charset=utf-8";
const char kUtf8String[] = "UTF8_STRING";
} // namespace
WaylandDataOffer::WaylandDataOffer(wl_data_offer* data_offer)
: data_offer_(data_offer),
source_actions_(WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE),
......@@ -54,23 +45,8 @@ void WaylandDataOffer::Reject(uint32_t serial) {
wl_data_offer_accept(data_offer_.get(), serial, nullptr);
}
void WaylandDataOffer::EnsureTextMimeTypeIfNeeded() {
if (base::Contains(mime_types_, kTextPlain))
return;
if (std::any_of(mime_types_.begin(), mime_types_.end(),
[](const std::string& mime_type) {
return mime_type == kString || mime_type == kText ||
mime_type == kTextPlainUtf8 ||
mime_type == kUtf8String;
})) {
mime_types_.push_back(kTextPlain);
text_plain_mime_type_inserted_ = true;
}
}
base::ScopedFD WaylandDataOffer::Receive(const std::string& mime_type) {
if (!base::Contains(mime_types_, mime_type))
if (!base::Contains(mime_types(), mime_type))
return base::ScopedFD();
base::ScopedFD read_fd;
......@@ -81,9 +57,8 @@ base::ScopedFD WaylandDataOffer::Receive(const std::string& mime_type) {
// mimetype, then it is safer to "read" the clipboard data with
// a mimetype mime_type known to be available.
std::string effective_mime_type = mime_type;
if (mime_type == kTextPlain && text_plain_mime_type_inserted_) {
effective_mime_type = kTextPlainUtf8;
}
if (mime_type == kMimeTypeText && text_plain_mime_type_inserted())
effective_mime_type = kMimeTypeTextUtf8;
wl_data_offer_receive(data_offer_.get(), effective_mime_type.data(),
write_fd.get());
......@@ -109,7 +84,7 @@ void WaylandDataOffer::OnOffer(void* data,
wl_data_offer* data_offer,
const char* mime_type) {
auto* self = static_cast<WaylandDataOffer*>(data);
self->mime_types_.push_back(mime_type);
self->AddMimeType(mime_type);
}
void WaylandDataOffer::OnSourceAction(void* data,
......
......@@ -13,6 +13,7 @@
#include "base/files/scoped_file.h"
#include "base/macros.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h"
#include "ui/ozone/platform/wayland/host/internal/wayland_data_offer_base.h"
namespace ui {
......@@ -23,31 +24,19 @@ namespace ui {
// The offer describes the different mime types that the data can be
// converted to and provides the mechanism for transferring the data
// directly from the source client.
class WaylandDataOffer {
class WaylandDataOffer : public internal::WaylandDataOfferBase {
public:
// Takes ownership of data_offer.
explicit WaylandDataOffer(wl_data_offer* data_offer);
~WaylandDataOffer();
~WaylandDataOffer() override;
const std::vector<std::string>& GetAvailableMimeTypes() const {
return mime_types_;
}
// Some X11 applications on Gnome/Wayland (running through XWayland)
// do not send the "text/plain" mime type that Chrome relies on, but
// instead they send mime types like "text/plain;charset=utf-8".
// When it happens, this method forcibly injects "text/plain" to the
// list of provided mime types so that Chrome clipboard's machinery
// works fine.
void EnsureTextMimeTypeIfNeeded();
void SetAction(uint32_t dnd_actions, uint32_t preferred_action);
void Accept(uint32_t serial, const std::string& mime_type);
void Reject(uint32_t serial);
// Creates a pipe (read & write FDs), passing the write-end of to pipe
// to the compositor (via wl_data_offer_receive) and returning the
// read-end to the pipe.
base::ScopedFD Receive(const std::string& mime_type);
// internal::WaylandDataOfferBase overrides:
base::ScopedFD Receive(const std::string& mime_type) override;
void FinishOffer();
uint32_t source_actions() const;
uint32_t dnd_action() const;
......@@ -65,14 +54,11 @@ class WaylandDataOffer {
static void OnAction(void* data, wl_data_offer* offer, uint32_t dnd_action);
wl::Object<wl_data_offer> data_offer_;
std::vector<std::string> mime_types_;
// Actions offered by the data source
uint32_t source_actions_;
// Action selected by the compositor
uint32_t dnd_action_;
bool text_plain_mime_type_inserted_ = false;
DISALLOW_COPY_AND_ASSIGN(WaylandDataOffer);
};
......
......@@ -5,15 +5,13 @@
#include "ui/ozone/platform/wayland/host/wayland_data_source.h"
#include "base/files/file_util.h"
#include "ui/base/clipboard/clipboard_constants.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
#include "ui/ozone/platform/wayland/host/wayland_window.h"
namespace ui {
constexpr char kTextMimeType[] = "text/plain";
constexpr char kTextMimeTypeUtf8[] = "text/plain;charset=utf-8";
WaylandDataSource::WaylandDataSource(wl_data_source* data_source,
WaylandConnection* connection)
: data_source_(data_source), connection_(connection) {
......@@ -30,8 +28,8 @@ void WaylandDataSource::WriteToClipboard(
const PlatformClipboard::DataMap& data_map) {
for (const auto& data : data_map) {
wl_data_source_offer(data_source_.get(), data.first.c_str());
if (strcmp(data.first.c_str(), kTextMimeType) == 0)
wl_data_source_offer(data_source_.get(), kTextMimeTypeUtf8);
if (strcmp(data.first.c_str(), kMimeTypeText) == 0)
wl_data_source_offer(data_source_.get(), kMimeTypeTextUtf8);
}
wl_data_device_set_selection(connection_->data_device(), data_source_.get(),
connection_->serial());
......@@ -39,16 +37,11 @@ void WaylandDataSource::WriteToClipboard(
connection_->ScheduleFlush();
}
void WaylandDataSource::UpdateDataMap(
const PlatformClipboard::DataMap& data_map) {
data_map_ = data_map;
}
void WaylandDataSource::Offer(const ui::OSExchangeData& data) {
// TODO(jkim): Handle mime types based on data.
std::vector<std::string> mime_types;
mime_types.push_back(kTextMimeType);
mime_types.push_back(kTextMimeTypeUtf8);
mime_types.push_back(kMimeTypeText);
mime_types.push_back(kMimeTypeTextUtf8);
source_window_ =
connection_->wayland_window_manager()->GetCurrentFocusedWindow();
......@@ -94,8 +87,8 @@ void WaylandDataSource::OnSend(void* data,
} else {
base::Optional<std::vector<uint8_t>> mime_data;
self->GetClipboardData(mime_type, &mime_data);
if (!mime_data.has_value() && strcmp(mime_type, kTextMimeTypeUtf8) == 0)
self->GetClipboardData(kTextMimeType, &mime_data);
if (!mime_data.has_value() && strcmp(mime_type, kMimeTypeTextUtf8) == 0)
self->GetClipboardData(kMimeTypeText, &mime_data);
contents.assign(mime_data->begin(), mime_data->end());
}
bool result =
......@@ -113,7 +106,8 @@ void WaylandDataSource::OnCancel(void* data, wl_data_source* source) {
self->connection_->FinishDragSession(self->dnd_action_,
self->source_window_);
} else {
self->connection_->clipboard()->DataSourceCancelled();
self->connection_->clipboard()->DataSourceCancelled(
ClipboardBuffer::kCopyPaste);
}
}
......@@ -133,17 +127,6 @@ void WaylandDataSource::OnAction(void* data,
self->dnd_action_ = dnd_action;
}
void WaylandDataSource::GetClipboardData(
const std::string& mime_type,
base::Optional<std::vector<uint8_t>>* data) {
auto it = data_map_.find(mime_type);
if (it != data_map_.end()) {
data->emplace(it->second);
// TODO: return here?
return;
}
}
void WaylandDataSource::GetDragData(const std::string& mime_type,
std::string* contents) {
auto it = drag_data_map_.find(mime_type);
......
......@@ -15,6 +15,7 @@
#include "base/macros.h"
#include "base/optional.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h"
#include "ui/ozone/platform/wayland/host/internal/wayland_data_source_base.h"
#include "ui/ozone/public/platform_clipboard.h"
namespace ui {
......@@ -28,22 +29,21 @@ class WaylandWindow;
// transfer and provides a way to describe the offered data
// (wl_data_source_offer) // and a way to respond to requests to
// transfer the data (OnSend listener).
class WaylandDataSource {
class WaylandDataSource : public internal::WaylandDataSourceBase {
public:
using DragDataMap = std::map<std::string, std::string>;
// Takes ownership of data_source.
explicit WaylandDataSource(wl_data_source* data_source,
WaylandConnection* connection);
~WaylandDataSource();
~WaylandDataSource() override;
void set_connection(WaylandConnection* connection) {
DCHECK(connection);
connection_ = connection;
}
void WriteToClipboard(const PlatformClipboard::DataMap& data_map);
void UpdateDataMap(const PlatformClipboard::DataMap& data_map);
void WriteToClipboard(const PlatformClipboard::DataMap& data_map) override;
void Offer(const ui::OSExchangeData& data);
void SetAction(int operation);
void SetDragData(const DragDataMap& data_map);
......@@ -63,15 +63,12 @@ class WaylandDataSource {
static void OnDnDFinished(void* data, wl_data_source* source);
static void OnAction(void* data, wl_data_source* source, uint32_t dnd_action);
void GetClipboardData(const std::string& mime_type,
base::Optional<std::vector<uint8_t>>* data);
void GetDragData(const std::string& mime_type, std::string* contents);
wl::Object<wl_data_source> data_source_;
WaylandConnection* connection_ = nullptr;
WaylandWindow* source_window_ = nullptr;
PlatformClipboard::DataMap data_map_;
DragDataMap drag_data_map_;
// Action selected by the compositor
uint32_t dnd_action_;
......
......@@ -21,7 +21,6 @@ namespace {
const char kChromeSelection[] = "CHROME_SELECTION";
const char kClipboard[] = "CLIPBOARD";
const char kMimeTypeUtf8[] = "text/plain;charset=utf-8";
const char kString[] = "STRING";
const char kTargets[] = "TARGETS";
const char kTimestamp[] = "TIMESTAMP";
......@@ -31,7 +30,7 @@ const char kUtf8String[] = "UTF8_STRING";
void ExpandTypes(std::vector<std::string>* list) {
bool has_mime_type_text = Contains(*list, ui::kMimeTypeText);
bool has_string = Contains(*list, kString);
bool has_mime_type_utf8 = Contains(*list, kMimeTypeUtf8);
bool has_mime_type_utf8 = Contains(*list, kMimeTypeTextUtf8);
bool has_utf8_string = Contains(*list, kUtf8String);
if (has_mime_type_text && !has_string)
list->push_back(kString);
......@@ -40,7 +39,7 @@ void ExpandTypes(std::vector<std::string>* list) {
if (has_mime_type_utf8 && !has_utf8_string)
list->push_back(kUtf8String);
if (has_utf8_string && !has_mime_type_utf8)
list->push_back(kMimeTypeUtf8);
list->push_back(kMimeTypeTextUtf8);
}
XID FindXEventTarget(const XEvent& xev) {
......@@ -188,7 +187,7 @@ bool X11ClipboardOzone::OnSelectionRequest(
std::string key = target_name;
// Allow conversions for text/plain[;charset=utf-8] <=> [UTF8_]STRING.
if (key == kUtf8String && !Contains(offer_data_map, kUtf8String)) {
key = kMimeTypeUtf8;
key = kMimeTypeTextUtf8;
} else if (key == kString && !Contains(offer_data_map, kString)) {
key = kMimeTypeText;
}
......@@ -355,7 +354,7 @@ void X11ClipboardOzone::ReadRemoteClipboard(XAtom selection) {
if (!Contains(selection_state.mime_types, target)) {
if (target == kMimeTypeText) {
target = kString;
} else if (target == kMimeTypeUtf8) {
} else if (target == kMimeTypeTextUtf8) {
target = kUtf8String;
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment