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 @@ ...@@ -4,6 +4,12 @@
import("//third_party/wayland/wayland_protocol.gni") 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") { wayland_protocol("xdg_shell_protocol") {
sources = [ sources = [
"src/unstable/xdg-shell/xdg-shell-unstable-v5.xml", "src/unstable/xdg-shell/xdg-shell-unstable-v5.xml",
......
GTK primary selection protocol
Maintainers:
Alexander Dunaev <adunaev@igalia.com>
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
namespace ui { namespace ui {
const char kMimeTypeText[] = "text/plain"; const char kMimeTypeText[] = "text/plain";
const char kMimeTypeTextUtf8[] = "text/plain;charset=utf-8";
const char kMimeTypeURIList[] = "text/uri-list"; const char kMimeTypeURIList[] = "text/uri-list";
const char kMimeTypeMozillaURL[] = "text/x-moz-url"; const char kMimeTypeMozillaURL[] = "text/x-moz-url";
const char kMimeTypeDownloadURL[] = "downloadurl"; const char kMimeTypeDownloadURL[] = "downloadurl";
......
...@@ -27,6 +27,7 @@ extern NSString* const kWebCustomDataPboardType; ...@@ -27,6 +27,7 @@ extern NSString* const kWebCustomDataPboardType;
// MIME type constants. // MIME type constants.
COMPONENT_EXPORT(BASE_CLIPBOARD_TYPES) extern const char kMimeTypeText[]; 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 kMimeTypeURIList[];
COMPONENT_EXPORT(BASE_CLIPBOARD_TYPES) extern const char kMimeTypeDownloadURL[]; COMPONENT_EXPORT(BASE_CLIPBOARD_TYPES) extern const char kMimeTypeDownloadURL[];
COMPONENT_EXPORT(BASE_CLIPBOARD_TYPES) extern const char kMimeTypeMozillaURL[]; COMPONENT_EXPORT(BASE_CLIPBOARD_TYPES) extern const char kMimeTypeMozillaURL[];
......
...@@ -31,6 +31,20 @@ source_set("wayland") { ...@@ -31,6 +31,20 @@ source_set("wayland") {
"gpu/wayland_surface_factory.cc", "gpu/wayland_surface_factory.cc",
"gpu/wayland_surface_factory.h", "gpu/wayland_surface_factory.h",
"gpu/wayland_surface_gpu.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.cc",
"host/wayland_buffer_manager_connector.h", "host/wayland_buffer_manager_connector.h",
"host/wayland_buffer_manager_host.cc", "host/wayland_buffer_manager_host.cc",
...@@ -109,6 +123,7 @@ source_set("wayland") { ...@@ -109,6 +123,7 @@ source_set("wayland") {
"//mojo/public/cpp/system", "//mojo/public/cpp/system",
"//skia", "//skia",
"//third_party/wayland:wayland_client", "//third_party/wayland:wayland_client",
"//third_party/wayland-protocols:gtk_primary_selection_protocol",
"//third_party/wayland-protocols:linux_dmabuf_protocol", "//third_party/wayland-protocols:linux_dmabuf_protocol",
"//third_party/wayland-protocols:presentation_time_protocol", "//third_party/wayland-protocols:presentation_time_protocol",
"//third_party/wayland-protocols:text_input_protocol", "//third_party/wayland-protocols:text_input_protocol",
......
...@@ -3,6 +3,7 @@ include_rules = [ ...@@ -3,6 +3,7 @@ include_rules = [
"+ui/base/hit_test.h", # UI hit test doesn't bring in all of ui/base. "+ui/base/hit_test.h", # UI hit test doesn't bring in all of ui/base.
"+ui/base/ui_base_features.h", "+ui/base/ui_base_features.h",
"+mojo/public", "+mojo/public",
"+ui/base/clipboard/clipboard_constants.h",
"+ui/base/dragdrop/drag_drop_types.h", "+ui/base/dragdrop/drag_drop_types.h",
"+ui/base/dragdrop/os_exchange_data.h", "+ui/base/dragdrop/os_exchange_data.h",
"+ui/base/dragdrop/os_exchange_data_provider_aura.h", "+ui/base/dragdrop/os_exchange_data_provider_aura.h",
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "ui/ozone/platform/wayland/common/wayland_object.h" #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 <linux-dmabuf-unstable-v1-client-protocol.h>
#include <presentation-time-client-protocol.h> #include <presentation-time-client-protocol.h>
#include <text-input-unstable-v1-client-protocol.h> #include <text-input-unstable-v1-client-protocol.h>
...@@ -53,6 +54,28 @@ void delete_data_device(wl_data_device* data_device) { ...@@ -53,6 +54,28 @@ void delete_data_device(wl_data_device* data_device) {
} // namespace } // 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; const wl_interface* ObjectTraits<wl_buffer>::interface = &wl_buffer_interface;
void (*ObjectTraits<wl_buffer>::deleter)(wl_buffer*) = &wl_buffer_destroy; void (*ObjectTraits<wl_buffer>::deleter)(wl_buffer*) = &wl_buffer_destroy;
......
...@@ -8,6 +8,10 @@ ...@@ -8,6 +8,10 @@
#include <wayland-client-core.h> #include <wayland-client-core.h>
#include <memory> #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_buffer;
struct wl_callback; struct wl_callback;
struct wl_compositor; struct wl_compositor;
...@@ -46,6 +50,30 @@ namespace wl { ...@@ -46,6 +50,30 @@ namespace wl {
template <typename T> template <typename T>
struct ObjectTraits; 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 <> template <>
struct ObjectTraits<wl_buffer> { struct ObjectTraits<wl_buffer> {
static const wl_interface* interface; static const wl_interface* interface;
......
...@@ -131,4 +131,12 @@ bool DrawBitmap(const SkBitmap& bitmap, ui::WaylandShmBuffer* out_buffer) { ...@@ -131,4 +131,12 @@ bool DrawBitmap(const SkBitmap& bitmap, ui::WaylandShmBuffer* out_buffer) {
return true; 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 } // namespace wl
...@@ -5,9 +5,12 @@ ...@@ -5,9 +5,12 @@
#ifndef UI_OZONE_PLATFORM_WAYLAND_COMMON_WAYLAND_UTIL_H_ #ifndef UI_OZONE_PLATFORM_WAYLAND_COMMON_WAYLAND_UTIL_H_
#define UI_OZONE_PLATFORM_WAYLAND_COMMON_WAYLAND_UTIL_H_ #define UI_OZONE_PLATFORM_WAYLAND_COMMON_WAYLAND_UTIL_H_
#include <string>
#include <wayland-client.h> #include <wayland-client.h>
#include "base/callback.h" #include "base/callback.h"
#include "base/files/scoped_file.h"
#include "base/macros.h" #include "base/macros.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h" #include "ui/ozone/platform/wayland/common/wayland_object.h"
...@@ -39,6 +42,9 @@ uint32_t IdentifyDirection(const ui::WaylandConnection& connection, ...@@ -39,6 +42,9 @@ uint32_t IdentifyDirection(const ui::WaylandConnection& connection,
// currently mmap'ed in memory address space. // currently mmap'ed in memory address space.
bool DrawBitmap(const SkBitmap& bitmap, ui::WaylandShmBuffer* out_buffer); 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 } // namespace wl
#endif // UI_OZONE_PLATFORM_WAYLAND_COMMON_WAYLAND_UTIL_H_ #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 @@ ...@@ -3,6 +3,11 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "ui/ozone/platform/wayland/host/wayland_clipboard.h" #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.h"
#include "ui/ozone/platform/wayland/host/wayland_data_device_manager.h" #include "ui/ozone/platform/wayland/host/wayland_data_device_manager.h"
...@@ -10,8 +15,13 @@ namespace ui { ...@@ -10,8 +15,13 @@ namespace ui {
WaylandClipboard::WaylandClipboard( WaylandClipboard::WaylandClipboard(
WaylandDataDeviceManager* data_device_manager, WaylandDataDeviceManager* data_device_manager,
WaylandDataDevice* data_device) WaylandDataDevice* data_device,
: data_device_manager_(data_device_manager), data_device_(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_manager_);
DCHECK(data_device_); DCHECK(data_device_);
} }
...@@ -22,17 +32,25 @@ void WaylandClipboard::OfferClipboardData( ...@@ -22,17 +32,25 @@ void WaylandClipboard::OfferClipboardData(
ClipboardBuffer buffer, ClipboardBuffer buffer,
const PlatformClipboard::DataMap& data_map, const PlatformClipboard::DataMap& data_map,
PlatformClipboard::OfferDataClosure callback) { PlatformClipboard::OfferDataClosure callback) {
// TODO(https://crbug.com/921950): Implement primary selection. internal::WaylandDataSourceBase* data_source = nullptr;
if (buffer != ClipboardBuffer::kCopyPaste) { if (buffer == ClipboardBuffer::kCopyPaste) {
std::move(callback).Run(); if (!clipboard_data_source_)
return; 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_) { DCHECK(data_source);
clipboard_data_source_ = data_device_manager_->CreateSource(); data_source->WriteToClipboard(data_map);
clipboard_data_source_->WriteToClipboard(data_map); data_source->set_data_map(data_map);
}
clipboard_data_source_->UpdateDataMap(data_map);
std::move(callback).Run(); std::move(callback).Run();
} }
...@@ -41,26 +59,23 @@ void WaylandClipboard::RequestClipboardData( ...@@ -41,26 +59,23 @@ void WaylandClipboard::RequestClipboardData(
const std::string& mime_type, const std::string& mime_type,
PlatformClipboard::DataMap* data_map, PlatformClipboard::DataMap* data_map,
PlatformClipboard::RequestDataClosure callback) { 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); read_clipboard_closure_ = std::move(callback);
DCHECK(data_map); DCHECK(data_map);
data_map_ = data_map; data_map_ = data_map;
if (!data_device_->RequestSelectionData(mime_type)) if (buffer == ClipboardBuffer::kCopyPaste) {
SetData({}, mime_type); 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) { bool WaylandClipboard::IsSelectionOwner(ClipboardBuffer buffer) {
// TODO(https://crbug.com/921950): Implement primary selection. if (buffer == ClipboardBuffer::kCopyPaste)
if (buffer != ClipboardBuffer::kCopyPaste) return !!clipboard_data_source_;
return false; else
return !!primary_data_source_;
return !!clipboard_data_source_;
} }
void WaylandClipboard::SetSequenceNumberUpdateCb( void WaylandClipboard::SetSequenceNumberUpdateCb(
...@@ -73,19 +88,24 @@ void WaylandClipboard::SetSequenceNumberUpdateCb( ...@@ -73,19 +88,24 @@ void WaylandClipboard::SetSequenceNumberUpdateCb(
void WaylandClipboard::GetAvailableMimeTypes( void WaylandClipboard::GetAvailableMimeTypes(
ClipboardBuffer buffer, ClipboardBuffer buffer,
PlatformClipboard::GetMimeTypesClosure callback) { PlatformClipboard::GetMimeTypesClosure callback) {
// TODO(https://crbug.com/921950): Implement primary selection. if (buffer == ClipboardBuffer::kCopyPaste) {
if (buffer != ClipboardBuffer::kCopyPaste) { std::move(callback).Run(data_device_->GetAvailableMimeTypes());
std::move(callback).Run({}); } else {
return; DCHECK(primary_selection_device_);
std::move(callback).Run(primary_selection_device_->GetAvailableMimeTypes());
} }
std::move(callback).Run(data_device_->GetAvailableMimeTypes());
} }
void WaylandClipboard::DataSourceCancelled() { void WaylandClipboard::DataSourceCancelled(ClipboardBuffer buffer) {
DCHECK(clipboard_data_source_); if (buffer == ClipboardBuffer::kCopyPaste) {
SetData({}, {}); DCHECK(clipboard_data_source_);
clipboard_data_source_.reset(); SetData({}, {});
clipboard_data_source_.reset();
} else {
DCHECK(primary_data_source_);
SetData({}, {});
primary_data_source_.reset();
}
} }
void WaylandClipboard::SetData(const std::string& contents, void WaylandClipboard::SetData(const std::string& contents,
...@@ -105,10 +125,6 @@ void WaylandClipboard::SetData(const std::string& contents, ...@@ -105,10 +125,6 @@ void WaylandClipboard::SetData(const std::string& contents,
} }
void WaylandClipboard::UpdateSequenceNumber(ClipboardBuffer buffer) { void WaylandClipboard::UpdateSequenceNumber(ClipboardBuffer buffer) {
// TODO(https://crbug.com/921950): Implement primary selection.
if (buffer != ClipboardBuffer::kCopyPaste)
return;
if (!update_sequence_cb_.is_null()) if (!update_sequence_cb_.is_null())
update_sequence_cb_.Run(buffer); update_sequence_cb_.Run(buffer);
} }
......
...@@ -6,11 +6,15 @@ ...@@ -6,11 +6,15 @@
#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_CLIPBOARD_H_ #define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_CLIPBOARD_H_
#include "base/callback.h" #include "base/callback.h"
#include "base/macros.h"
#include "ui/ozone/platform/wayland/host/wayland_data_source.h" #include "ui/ozone/platform/wayland/host/wayland_data_source.h"
#include "ui/ozone/public/platform_clipboard.h" #include "ui/ozone/public/platform_clipboard.h"
namespace ui { namespace ui {
class GtkPrimarySelectionDevice;
class GtkPrimarySelectionDeviceManager;
class GtkPrimarySelectionSource;
class WaylandDataDevice; class WaylandDataDevice;
class WaylandDataDeviceManager; class WaylandDataDeviceManager;
...@@ -20,8 +24,11 @@ class WaylandDataDeviceManager; ...@@ -20,8 +24,11 @@ class WaylandDataDeviceManager;
// manager. // manager.
class WaylandClipboard : public PlatformClipboard { class WaylandClipboard : public PlatformClipboard {
public: public:
WaylandClipboard(WaylandDataDeviceManager* data_device_manager, WaylandClipboard(
WaylandDataDevice* data_device); WaylandDataDeviceManager* data_device_manager,
WaylandDataDevice* data_device,
GtkPrimarySelectionDeviceManager* primary_selection_device_manager,
GtkPrimarySelectionDevice* primary_selection_device);
~WaylandClipboard() override; ~WaylandClipboard() override;
// PlatformClipboard. // PlatformClipboard.
...@@ -41,7 +48,7 @@ class WaylandClipboard : public PlatformClipboard { ...@@ -41,7 +48,7 @@ class WaylandClipboard : public PlatformClipboard {
void SetSequenceNumberUpdateCb( void SetSequenceNumberUpdateCb(
PlatformClipboard::SequenceNumberUpdateCb cb) override; PlatformClipboard::SequenceNumberUpdateCb cb) override;
void DataSourceCancelled(); void DataSourceCancelled(ClipboardBuffer buffer);
void SetData(const std::string& contents, const std::string& mime_type); void SetData(const std::string& contents, const std::string& mime_type);
void UpdateSequenceNumber(ClipboardBuffer buffer); void UpdateSequenceNumber(ClipboardBuffer buffer);
...@@ -58,10 +65,13 @@ class WaylandClipboard : public PlatformClipboard { ...@@ -58,10 +65,13 @@ class WaylandClipboard : public PlatformClipboard {
PlatformClipboard::RequestDataClosure read_clipboard_closure_; PlatformClipboard::RequestDataClosure read_clipboard_closure_;
std::unique_ptr<WaylandDataSource> clipboard_data_source_; std::unique_ptr<WaylandDataSource> clipboard_data_source_;
std::unique_ptr<GtkPrimarySelectionSource> primary_data_source_;
// These two instances are owned by the connection. // These four instances are owned by the connection.
WaylandDataDeviceManager* const data_device_manager_ = nullptr; WaylandDataDeviceManager* const data_device_manager_;
WaylandDataDevice* const data_device_ = nullptr; WaylandDataDevice* const data_device_;
GtkPrimarySelectionDeviceManager* const primary_selection_device_manager_;
GtkPrimarySelectionDevice* const primary_selection_device_;
DISALLOW_COPY_AND_ASSIGN(WaylandClipboard); DISALLOW_COPY_AND_ASSIGN(WaylandClipboard);
}; };
......
...@@ -34,6 +34,7 @@ namespace ui { ...@@ -34,6 +34,7 @@ namespace ui {
namespace { namespace {
constexpr uint32_t kMaxCompositorVersion = 4; constexpr uint32_t kMaxCompositorVersion = 4;
constexpr uint32_t kMaxGtkPrimarySelectionDeviceManagerVersion = 1;
constexpr uint32_t kMaxLinuxDmabufVersion = 3; constexpr uint32_t kMaxLinuxDmabufVersion = 3;
constexpr uint32_t kMaxSeatVersion = 4; constexpr uint32_t kMaxSeatVersion = 4;
constexpr uint32_t kMaxShmVersion = 1; constexpr uint32_t kMaxShmVersion = 1;
...@@ -234,8 +235,15 @@ void WaylandConnection::EnsureDataDevice() { ...@@ -234,8 +235,15 @@ void WaylandConnection::EnsureDataDevice() {
DCHECK(!data_device_); DCHECK(!data_device_);
wl_data_device* data_device = data_device_manager_->GetDevice(); wl_data_device* data_device = data_device_manager_->GetDevice();
data_device_ = std::make_unique<WaylandDataDevice>(this, data_device); 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( bool WaylandConnection::BeginWatchingFd(
...@@ -352,6 +360,14 @@ void WaylandConnection::Global(void* data, ...@@ -352,6 +360,14 @@ void WaylandConnection::Global(void* data,
std::make_unique<WaylandDataDeviceManager>( std::make_unique<WaylandDataDeviceManager>(
data_device_manager.release(), connection); data_device_manager.release(), connection);
connection->EnsureDataDevice(); 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_ && } else if (!connection->zwp_dmabuf_ &&
(strcmp(interface, "zwp_linux_dmabuf_v1") == 0)) { (strcmp(interface, "zwp_linux_dmabuf_v1") == 0)) {
wl::Object<zwp_linux_dmabuf_v1> zwp_linux_dmabuf = wl::Object<zwp_linux_dmabuf_v1> zwp_linux_dmabuf =
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
#include "ui/gfx/buffer_types.h" #include "ui/gfx/buffer_types.h"
#include "ui/gfx/native_widget_types.h" #include "ui/gfx/native_widget_types.h"
#include "ui/ozone/platform/wayland/common/wayland_object.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_clipboard.h"
#include "ui/ozone/platform/wayland/host/wayland_cursor_position.h" #include "ui/ozone/platform/wayland/host/wayland_cursor_position.h"
#include "ui/ozone/platform/wayland/host/wayland_data_device.h" #include "ui/ozone/platform/wayland/host/wayland_data_device.h"
...@@ -54,6 +56,9 @@ class WaylandConnection : public PlatformEventSource, ...@@ -54,6 +56,9 @@ class WaylandConnection : public PlatformEventSource,
zxdg_shell_v6* shell_v6() const { return shell_v6_.get(); } zxdg_shell_v6* shell_v6() const { return shell_v6_.get(); }
wl_seat* seat() const { return seat_.get(); } wl_seat* seat() const { return seat_.get(); }
wl_data_device* data_device() const { return data_device_->data_device(); } 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(); } wp_presentation* presentation() const { return presentation_.get(); }
zwp_text_input_manager_v1* text_input_manager_v1() const { zwp_text_input_manager_v1* text_input_manager_v1() const {
return text_input_manager_v1_.get(); return text_input_manager_v1_.get();
...@@ -188,6 +193,10 @@ class WaylandConnection : public PlatformEventSource, ...@@ -188,6 +193,10 @@ class WaylandConnection : public PlatformEventSource,
std::unique_ptr<WaylandShm> shm_; std::unique_ptr<WaylandShm> shm_;
std::unique_ptr<WaylandBufferManagerHost> buffer_manager_host_; 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. // Manages Wayland windows.
WaylandWindowManager wayland_window_manager_; WaylandWindowManager wayland_window_manager_;
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.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/drag_drop_types.h"
#include "ui/base/dragdrop/os_exchange_data.h" #include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/base/dragdrop/os_exchange_data_provider_aura.h" #include "ui/base/dragdrop/os_exchange_data_provider_aura.h"
...@@ -23,9 +24,6 @@ namespace ui { ...@@ -23,9 +24,6 @@ namespace ui {
namespace { namespace {
constexpr char kMimeTypeText[] = "text/plain";
constexpr char kMimeTypeTextUTF8[] = "text/plain;charset=utf-8";
int GetOperation(uint32_t source_actions, uint32_t dnd_action) { int GetOperation(uint32_t source_actions, uint32_t dnd_action) {
uint32_t action = dnd_action != WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE uint32_t action = dnd_action != WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE
? dnd_action ? dnd_action
...@@ -56,7 +54,7 @@ void AddToOSExchangeData(const std::string& data, ...@@ -56,7 +54,7 @@ void AddToOSExchangeData(const std::string& data,
const std::string& mime_type, const std::string& mime_type,
OSExchangeData* os_exchange_data) { OSExchangeData* os_exchange_data) {
DCHECK(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()); DCHECK(!os_exchange_data->HasString());
AddStringToOSExchangeData(data, os_exchange_data); AddStringToOSExchangeData(data, os_exchange_data);
return; return;
...@@ -70,7 +68,7 @@ void AddToOSExchangeData(const std::string& data, ...@@ -70,7 +68,7 @@ void AddToOSExchangeData(const std::string& data,
// static // static
WaylandDataDevice::WaylandDataDevice(WaylandConnection* connection, WaylandDataDevice::WaylandDataDevice(WaylandConnection* connection,
wl_data_device* data_device) 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 = { static const struct wl_data_device_listener kDataDeviceListener = {
WaylandDataDevice::OnDataOffer, WaylandDataDevice::OnEnter, WaylandDataDevice::OnDataOffer, WaylandDataDevice::OnEnter,
WaylandDataDevice::OnLeave, WaylandDataDevice::OnMotion, WaylandDataDevice::OnLeave, WaylandDataDevice::OnMotion,
...@@ -80,25 +78,6 @@ WaylandDataDevice::WaylandDataDevice(WaylandConnection* connection, ...@@ -80,25 +78,6 @@ WaylandDataDevice::WaylandDataDevice(WaylandConnection* connection,
WaylandDataDevice::~WaylandDataDevice() = default; 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( void WaylandDataDevice::RequestDragData(
const std::string& mime_type, const std::string& mime_type,
base::OnceCallback<void(const std::string&)> callback) { base::OnceCallback<void(const std::string&)> callback) {
...@@ -110,9 +89,9 @@ void WaylandDataDevice::RequestDragData( ...@@ -110,9 +89,9 @@ void WaylandDataDevice::RequestDragData(
// Ensure there is not pending operation to be performed by the compositor, // Ensure there is not pending operation to be performed by the compositor,
// otherwise read(..) can block awaiting data to be sent to pipe. // otherwise read(..) can block awaiting data to be sent to pipe.
deferred_read_closure_ = base::BindOnce( RegisterDeferredReadClosure(base::BindOnce(
&WaylandDataDevice::ReadDragDataFromFD, base::Unretained(this), &WaylandDataDevice::ReadDragDataFromFD, base::Unretained(this),
std::move(fd), std::move(callback)); std::move(fd), std::move(callback)));
RegisterDeferredReadCallback(); RegisterDeferredReadCallback();
} }
...@@ -121,7 +100,7 @@ void WaylandDataDevice::DeliverDragData(const std::string& mime_type, ...@@ -121,7 +100,7 @@ void WaylandDataDevice::DeliverDragData(const std::string& mime_type,
DCHECK(buffer); DCHECK(buffer);
DCHECK(source_data_); DCHECK(source_data_);
if (mime_type != kMimeTypeText && mime_type != kMimeTypeTextUTF8) if (mime_type != kMimeTypeText && mime_type != kMimeTypeTextUtf8)
return; return;
const OSExchangeData::FilenameToURLPolicy policy = const OSExchangeData::FilenameToURLPolicy policy =
...@@ -148,7 +127,7 @@ void WaylandDataDevice::StartDrag(wl_data_source* data_source, ...@@ -148,7 +127,7 @@ void WaylandDataDevice::StartDrag(wl_data_source* data_source,
DCHECK(data_source); DCHECK(data_source);
WaylandWindow* window = WaylandWindow* window =
connection_->wayland_window_manager()->GetCurrentFocusedWindow(); connection()->wayland_window_manager()->GetCurrentFocusedWindow();
if (!window) { if (!window) {
LOG(ERROR) << "Failed to get focused window."; LOG(ERROR) << "Failed to get focused window.";
return; return;
...@@ -156,47 +135,24 @@ void WaylandDataDevice::StartDrag(wl_data_source* data_source, ...@@ -156,47 +135,24 @@ void WaylandDataDevice::StartDrag(wl_data_source* data_source,
const SkBitmap* icon = PrepareDragIcon(data); const SkBitmap* icon = PrepareDragIcon(data);
source_data_ = std::make_unique<ui::OSExchangeData>(data.provider().Clone()); source_data_ = std::make_unique<ui::OSExchangeData>(data.provider().Clone());
wl_data_device_start_drag(data_device_.get(), data_source, window->surface(), wl_data_device_start_drag(data_device_.get(), data_source, window->surface(),
icon_surface_.get(), connection_->serial()); icon_surface_.get(), connection()->serial());
if (icon) if (icon)
DrawDragIcon(icon); DrawDragIcon(icon);
connection_->ScheduleFlush(); connection()->ScheduleFlush();
} }
void WaylandDataDevice::ResetSourceData() { void WaylandDataDevice::ResetSourceData() {
source_data_.reset(); 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( void WaylandDataDevice::ReadDragDataFromFD(
base::ScopedFD fd, base::ScopedFD fd,
base::OnceCallback<void(const std::string&)> callback) { base::OnceCallback<void(const std::string&)> callback) {
std::string contents; std::string contents;
ReadDataFromFD(std::move(fd), &contents); wl::ReadDataFromFD(std::move(fd), &contents);
std::move(callback).Run(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() { void WaylandDataDevice::HandleDeferredLeaveIfNeeded() {
if (!is_leaving_) if (!is_leaving_)
return; return;
...@@ -210,7 +166,7 @@ void WaylandDataDevice::OnDataOffer(void* data, ...@@ -210,7 +166,7 @@ void WaylandDataDevice::OnDataOffer(void* data,
wl_data_offer* offer) { wl_data_offer* offer) {
auto* self = static_cast<WaylandDataDevice*>(data); auto* self = static_cast<WaylandDataDevice*>(data);
self->connection_->clipboard()->UpdateSequenceNumber( self->connection()->clipboard()->UpdateSequenceNumber(
ClipboardBuffer::kCopyPaste); ClipboardBuffer::kCopyPaste);
DCHECK(!self->new_offer_); DCHECK(!self->new_offer_);
...@@ -241,7 +197,7 @@ void WaylandDataDevice::OnEnter(void* data, ...@@ -241,7 +197,7 @@ void WaylandDataDevice::OnEnter(void* data,
// all mime types offered because current implementation doesn't decide // all mime types offered because current implementation doesn't decide
// action based on mime type. // action based on mime type.
self->unprocessed_mime_types_.clear(); 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->unprocessed_mime_types_.push_back(mime);
self->drag_offer_->Accept(serial, mime); self->drag_offer_->Accept(serial, mime);
} }
...@@ -336,45 +292,24 @@ void WaylandDataDevice::OnSelection(void* data, ...@@ -336,45 +292,24 @@ void WaylandDataDevice::OnSelection(void* data,
// 'offer' will be null to indicate that the selection is no longer valid, // 'offer' will be null to indicate that the selection is no longer valid,
// i.e. there is no longer clipboard data available to paste. // i.e. there is no longer clipboard data available to paste.
if (!offer) { if (!offer) {
self->selection_offer_.reset(); self->ResetDataOffer();
// Clear Clipboard cache. // Clear Clipboard cache.
self->connection_->clipboard()->SetData(std::string(), std::string()); self->connection()->clipboard()->SetData({}, {});
return; return;
} }
DCHECK(self->new_offer_); DCHECK(self->new_offer_);
self->selection_offer_ = std::move(self->new_offer_); self->set_data_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();
}
void WaylandDataDevice::DeferredReadCallback(void* data, self->data_offer()->EnsureTextMimeTypeIfNeeded();
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();
} }
const SkBitmap* WaylandDataDevice::PrepareDragIcon(const OSExchangeData& data) { const SkBitmap* WaylandDataDevice::PrepareDragIcon(const OSExchangeData& data) {
const SkBitmap* icon_bitmap = data.provider().GetDragImage().bitmap(); const SkBitmap* icon_bitmap = data.provider().GetDragImage().bitmap();
if (!icon_bitmap || icon_bitmap->empty()) if (!icon_bitmap || icon_bitmap->empty())
return nullptr; return nullptr;
icon_surface_.reset(wl_compositor_create_surface(connection_->compositor())); icon_surface_.reset(wl_compositor_create_surface(connection()->compositor()));
DCHECK(icon_surface_); DCHECK(icon_surface_);
return icon_bitmap; return icon_bitmap;
} }
...@@ -385,7 +320,7 @@ void WaylandDataDevice::DrawDragIcon(const SkBitmap* icon_bitmap) { ...@@ -385,7 +320,7 @@ void WaylandDataDevice::DrawDragIcon(const SkBitmap* icon_bitmap) {
gfx::Size size(icon_bitmap->width(), icon_bitmap->height()); gfx::Size size(icon_bitmap->width(), icon_bitmap->height());
if (!shm_buffer_ || shm_buffer_->size() != size) { 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()) { if (!shm_buffer_->IsValid()) {
LOG(ERROR) << "Failed to create drag icon buffer."; LOG(ERROR) << "Failed to create drag icon buffer.";
return; return;
...@@ -436,7 +371,7 @@ void WaylandDataDevice::HandleReceivedData( ...@@ -436,7 +371,7 @@ void WaylandDataDevice::HandleReceivedData(
std::string WaylandDataDevice::SelectNextMimeType() { std::string WaylandDataDevice::SelectNextMimeType() {
while (!unprocessed_mime_types_.empty()) { while (!unprocessed_mime_types_.empty()) {
std::string& mime_type = unprocessed_mime_types_.front(); 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()) { !received_data_->HasString()) {
return mime_type; return mime_type;
} }
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "base/macros.h" #include "base/macros.h"
#include "ui/gfx/geometry/size.h" #include "ui/gfx/geometry/size.h"
#include "ui/ozone/platform/wayland/common/wayland_object.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_data_offer.h"
#include "ui/ozone/platform/wayland/host/wayland_shm_buffer.h" #include "ui/ozone/platform/wayland/host/wayland_shm_buffer.h"
...@@ -31,12 +32,10 @@ class WaylandWindow; ...@@ -31,12 +32,10 @@ class WaylandWindow;
// This class provides access to inter-client data transfer mechanisms // This class provides access to inter-client data transfer mechanisms
// such as copy-and-paste and drag-and-drop mechanisms. // such as copy-and-paste and drag-and-drop mechanisms.
class WaylandDataDevice { class WaylandDataDevice : public internal::WaylandDataDeviceBase {
public: public:
WaylandDataDevice(WaylandConnection* connection, wl_data_device* data_device); WaylandDataDevice(WaylandConnection* connection, wl_data_device* data_device);
~WaylandDataDevice(); ~WaylandDataDevice() override;
bool RequestSelectionData(const std::string& mime_type);
// Requests the data to the platform when Chromium gets drag-and-drop started // 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 // by others. Once reading the data from platform is done, |callback| should
...@@ -53,22 +52,15 @@ class WaylandDataDevice { ...@@ -53,22 +52,15 @@ class WaylandDataDevice {
// Resets |source_data_| when the dragging is finished. // Resets |source_data_| when the dragging is finished.
void ResetSourceData(); void ResetSourceData();
std::vector<std::string> GetAvailableMimeTypes();
wl_data_device* data_device() const { return data_device_.get(); } wl_data_device* data_device() const { return data_device_.get(); }
bool IsDragEntered() { return drag_offer_ != nullptr; } bool IsDragEntered() { return drag_offer_ != nullptr; }
private: private:
void ReadClipboardDataFromFD(base::ScopedFD fd, const std::string& mime_type);
void ReadDragDataFromFD( void ReadDragDataFromFD(
base::ScopedFD fd, base::ScopedFD fd,
base::OnceCallback<void(const std::string&)> callback); 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 // If source_data_ is not set, data is being dragged from an external
// application (non-chromium). // application (non-chromium).
bool IsDraggingExternalData() const { return !source_data_; } bool IsDraggingExternalData() const { return !source_data_; }
...@@ -108,15 +100,6 @@ class WaylandDataDevice { ...@@ -108,15 +100,6 @@ class WaylandDataDevice {
wl_data_device* data_device, wl_data_device* data_device,
wl_data_offer* id); 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 // 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. // on, if a valid drag image is present in |data|; otherwise returns null.
const SkBitmap* PrepareDragIcon(const OSExchangeData& data); const SkBitmap* PrepareDragIcon(const OSExchangeData& data);
...@@ -141,10 +124,6 @@ class WaylandDataDevice { ...@@ -141,10 +124,6 @@ class WaylandDataDevice {
// The wl_data_device wrapped by this WaylandDataDevice. // The wl_data_device wrapped by this WaylandDataDevice.
wl::Object<wl_data_device> data_device_; 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 // 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 // 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 // offer, it is not immediately possible to know whether the new offer is the
...@@ -152,20 +131,12 @@ class WaylandDataDevice { ...@@ -152,20 +131,12 @@ class WaylandDataDevice {
// of new data offers temporarily until its identity becomes known. // of new data offers temporarily until its identity becomes known.
std::unique_ptr<WaylandDataOffer> new_offer_; 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 // Offer to receive data from another process via drag-and-drop, or null if no
// drag-and-drop from another process is in progress. // drag-and-drop from another process is in progress.
std::unique_ptr<WaylandDataOffer> drag_offer_; std::unique_ptr<WaylandDataOffer> drag_offer_;
WaylandWindow* window_ = nullptr; 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_handling_dropped_data_ = false;
bool is_leaving_ = false; bool is_leaving_ = false;
......
...@@ -10,19 +10,10 @@ ...@@ -10,19 +10,10 @@
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "ui/base/clipboard/clipboard_constants.h"
namespace ui { 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) WaylandDataOffer::WaylandDataOffer(wl_data_offer* data_offer)
: data_offer_(data_offer), : data_offer_(data_offer),
source_actions_(WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE), source_actions_(WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE),
...@@ -54,23 +45,8 @@ void WaylandDataOffer::Reject(uint32_t serial) { ...@@ -54,23 +45,8 @@ void WaylandDataOffer::Reject(uint32_t serial) {
wl_data_offer_accept(data_offer_.get(), serial, nullptr); 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) { 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(); return base::ScopedFD();
base::ScopedFD read_fd; base::ScopedFD read_fd;
...@@ -81,9 +57,8 @@ base::ScopedFD WaylandDataOffer::Receive(const std::string& mime_type) { ...@@ -81,9 +57,8 @@ base::ScopedFD WaylandDataOffer::Receive(const std::string& mime_type) {
// mimetype, then it is safer to "read" the clipboard data with // mimetype, then it is safer to "read" the clipboard data with
// a mimetype mime_type known to be available. // a mimetype mime_type known to be available.
std::string effective_mime_type = mime_type; std::string effective_mime_type = mime_type;
if (mime_type == kTextPlain && text_plain_mime_type_inserted_) { if (mime_type == kMimeTypeText && text_plain_mime_type_inserted())
effective_mime_type = kTextPlainUtf8; effective_mime_type = kMimeTypeTextUtf8;
}
wl_data_offer_receive(data_offer_.get(), effective_mime_type.data(), wl_data_offer_receive(data_offer_.get(), effective_mime_type.data(),
write_fd.get()); write_fd.get());
...@@ -109,7 +84,7 @@ void WaylandDataOffer::OnOffer(void* data, ...@@ -109,7 +84,7 @@ void WaylandDataOffer::OnOffer(void* data,
wl_data_offer* data_offer, wl_data_offer* data_offer,
const char* mime_type) { const char* mime_type) {
auto* self = static_cast<WaylandDataOffer*>(data); auto* self = static_cast<WaylandDataOffer*>(data);
self->mime_types_.push_back(mime_type); self->AddMimeType(mime_type);
} }
void WaylandDataOffer::OnSourceAction(void* data, void WaylandDataOffer::OnSourceAction(void* data,
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "base/files/scoped_file.h" #include "base/files/scoped_file.h"
#include "base/macros.h" #include "base/macros.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h" #include "ui/ozone/platform/wayland/common/wayland_object.h"
#include "ui/ozone/platform/wayland/host/internal/wayland_data_offer_base.h"
namespace ui { namespace ui {
...@@ -23,31 +24,19 @@ namespace ui { ...@@ -23,31 +24,19 @@ namespace ui {
// The offer describes the different mime types that the data can be // The offer describes the different mime types that the data can be
// converted to and provides the mechanism for transferring the data // converted to and provides the mechanism for transferring the data
// directly from the source client. // directly from the source client.
class WaylandDataOffer { class WaylandDataOffer : public internal::WaylandDataOfferBase {
public: public:
// Takes ownership of data_offer. // Takes ownership of data_offer.
explicit WaylandDataOffer(wl_data_offer* 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 SetAction(uint32_t dnd_actions, uint32_t preferred_action);
void Accept(uint32_t serial, const std::string& mime_type); void Accept(uint32_t serial, const std::string& mime_type);
void Reject(uint32_t serial); void Reject(uint32_t serial);
// Creates a pipe (read & write FDs), passing the write-end of to pipe // internal::WaylandDataOfferBase overrides:
// to the compositor (via wl_data_offer_receive) and returning the base::ScopedFD Receive(const std::string& mime_type) override;
// read-end to the pipe.
base::ScopedFD Receive(const std::string& mime_type);
void FinishOffer(); void FinishOffer();
uint32_t source_actions() const; uint32_t source_actions() const;
uint32_t dnd_action() const; uint32_t dnd_action() const;
...@@ -65,14 +54,11 @@ class WaylandDataOffer { ...@@ -65,14 +54,11 @@ class WaylandDataOffer {
static void OnAction(void* data, wl_data_offer* offer, uint32_t dnd_action); static void OnAction(void* data, wl_data_offer* offer, uint32_t dnd_action);
wl::Object<wl_data_offer> data_offer_; wl::Object<wl_data_offer> data_offer_;
std::vector<std::string> mime_types_;
// Actions offered by the data source // Actions offered by the data source
uint32_t source_actions_; uint32_t source_actions_;
// Action selected by the compositor // Action selected by the compositor
uint32_t dnd_action_; uint32_t dnd_action_;
bool text_plain_mime_type_inserted_ = false;
DISALLOW_COPY_AND_ASSIGN(WaylandDataOffer); DISALLOW_COPY_AND_ASSIGN(WaylandDataOffer);
}; };
......
...@@ -5,15 +5,13 @@ ...@@ -5,15 +5,13 @@
#include "ui/ozone/platform/wayland/host/wayland_data_source.h" #include "ui/ozone/platform/wayland/host/wayland_data_source.h"
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "ui/base/clipboard/clipboard_constants.h"
#include "ui/base/dragdrop/drag_drop_types.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_connection.h"
#include "ui/ozone/platform/wayland/host/wayland_window.h" #include "ui/ozone/platform/wayland/host/wayland_window.h"
namespace ui { namespace ui {
constexpr char kTextMimeType[] = "text/plain";
constexpr char kTextMimeTypeUtf8[] = "text/plain;charset=utf-8";
WaylandDataSource::WaylandDataSource(wl_data_source* data_source, WaylandDataSource::WaylandDataSource(wl_data_source* data_source,
WaylandConnection* connection) WaylandConnection* connection)
: data_source_(data_source), connection_(connection) { : data_source_(data_source), connection_(connection) {
...@@ -30,8 +28,8 @@ void WaylandDataSource::WriteToClipboard( ...@@ -30,8 +28,8 @@ void WaylandDataSource::WriteToClipboard(
const PlatformClipboard::DataMap& data_map) { const PlatformClipboard::DataMap& data_map) {
for (const auto& data : data_map) { for (const auto& data : data_map) {
wl_data_source_offer(data_source_.get(), data.first.c_str()); wl_data_source_offer(data_source_.get(), data.first.c_str());
if (strcmp(data.first.c_str(), kTextMimeType) == 0) if (strcmp(data.first.c_str(), kMimeTypeText) == 0)
wl_data_source_offer(data_source_.get(), kTextMimeTypeUtf8); wl_data_source_offer(data_source_.get(), kMimeTypeTextUtf8);
} }
wl_data_device_set_selection(connection_->data_device(), data_source_.get(), wl_data_device_set_selection(connection_->data_device(), data_source_.get(),
connection_->serial()); connection_->serial());
...@@ -39,16 +37,11 @@ void WaylandDataSource::WriteToClipboard( ...@@ -39,16 +37,11 @@ void WaylandDataSource::WriteToClipboard(
connection_->ScheduleFlush(); connection_->ScheduleFlush();
} }
void WaylandDataSource::UpdateDataMap(
const PlatformClipboard::DataMap& data_map) {
data_map_ = data_map;
}
void WaylandDataSource::Offer(const ui::OSExchangeData& data) { void WaylandDataSource::Offer(const ui::OSExchangeData& data) {
// TODO(jkim): Handle mime types based on data. // TODO(jkim): Handle mime types based on data.
std::vector<std::string> mime_types; std::vector<std::string> mime_types;
mime_types.push_back(kTextMimeType); mime_types.push_back(kMimeTypeText);
mime_types.push_back(kTextMimeTypeUtf8); mime_types.push_back(kMimeTypeTextUtf8);
source_window_ = source_window_ =
connection_->wayland_window_manager()->GetCurrentFocusedWindow(); connection_->wayland_window_manager()->GetCurrentFocusedWindow();
...@@ -94,8 +87,8 @@ void WaylandDataSource::OnSend(void* data, ...@@ -94,8 +87,8 @@ void WaylandDataSource::OnSend(void* data,
} else { } else {
base::Optional<std::vector<uint8_t>> mime_data; base::Optional<std::vector<uint8_t>> mime_data;
self->GetClipboardData(mime_type, &mime_data); self->GetClipboardData(mime_type, &mime_data);
if (!mime_data.has_value() && strcmp(mime_type, kTextMimeTypeUtf8) == 0) if (!mime_data.has_value() && strcmp(mime_type, kMimeTypeTextUtf8) == 0)
self->GetClipboardData(kTextMimeType, &mime_data); self->GetClipboardData(kMimeTypeText, &mime_data);
contents.assign(mime_data->begin(), mime_data->end()); contents.assign(mime_data->begin(), mime_data->end());
} }
bool result = bool result =
...@@ -113,7 +106,8 @@ void WaylandDataSource::OnCancel(void* data, wl_data_source* source) { ...@@ -113,7 +106,8 @@ void WaylandDataSource::OnCancel(void* data, wl_data_source* source) {
self->connection_->FinishDragSession(self->dnd_action_, self->connection_->FinishDragSession(self->dnd_action_,
self->source_window_); self->source_window_);
} else { } else {
self->connection_->clipboard()->DataSourceCancelled(); self->connection_->clipboard()->DataSourceCancelled(
ClipboardBuffer::kCopyPaste);
} }
} }
...@@ -133,17 +127,6 @@ void WaylandDataSource::OnAction(void* data, ...@@ -133,17 +127,6 @@ void WaylandDataSource::OnAction(void* data,
self->dnd_action_ = dnd_action; 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, void WaylandDataSource::GetDragData(const std::string& mime_type,
std::string* contents) { std::string* contents) {
auto it = drag_data_map_.find(mime_type); auto it = drag_data_map_.find(mime_type);
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/optional.h" #include "base/optional.h"
#include "ui/ozone/platform/wayland/common/wayland_object.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" #include "ui/ozone/public/platform_clipboard.h"
namespace ui { namespace ui {
...@@ -28,22 +29,21 @@ class WaylandWindow; ...@@ -28,22 +29,21 @@ class WaylandWindow;
// transfer and provides a way to describe the offered data // transfer and provides a way to describe the offered data
// (wl_data_source_offer) // and a way to respond to requests to // (wl_data_source_offer) // and a way to respond to requests to
// transfer the data (OnSend listener). // transfer the data (OnSend listener).
class WaylandDataSource { class WaylandDataSource : public internal::WaylandDataSourceBase {
public: public:
using DragDataMap = std::map<std::string, std::string>; using DragDataMap = std::map<std::string, std::string>;
// Takes ownership of data_source. // Takes ownership of data_source.
explicit WaylandDataSource(wl_data_source* data_source, explicit WaylandDataSource(wl_data_source* data_source,
WaylandConnection* connection); WaylandConnection* connection);
~WaylandDataSource(); ~WaylandDataSource() override;
void set_connection(WaylandConnection* connection) { void set_connection(WaylandConnection* connection) {
DCHECK(connection); DCHECK(connection);
connection_ = connection; connection_ = connection;
} }
void WriteToClipboard(const PlatformClipboard::DataMap& data_map); void WriteToClipboard(const PlatformClipboard::DataMap& data_map) override;
void UpdateDataMap(const PlatformClipboard::DataMap& data_map);
void Offer(const ui::OSExchangeData& data); void Offer(const ui::OSExchangeData& data);
void SetAction(int operation); void SetAction(int operation);
void SetDragData(const DragDataMap& data_map); void SetDragData(const DragDataMap& data_map);
...@@ -63,15 +63,12 @@ class WaylandDataSource { ...@@ -63,15 +63,12 @@ class WaylandDataSource {
static void OnDnDFinished(void* data, wl_data_source* source); static void OnDnDFinished(void* data, wl_data_source* source);
static void OnAction(void* data, wl_data_source* source, uint32_t dnd_action); 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); void GetDragData(const std::string& mime_type, std::string* contents);
wl::Object<wl_data_source> data_source_; wl::Object<wl_data_source> data_source_;
WaylandConnection* connection_ = nullptr; WaylandConnection* connection_ = nullptr;
WaylandWindow* source_window_ = nullptr; WaylandWindow* source_window_ = nullptr;
PlatformClipboard::DataMap data_map_;
DragDataMap drag_data_map_; DragDataMap drag_data_map_;
// Action selected by the compositor // Action selected by the compositor
uint32_t dnd_action_; uint32_t dnd_action_;
......
...@@ -21,7 +21,6 @@ namespace { ...@@ -21,7 +21,6 @@ namespace {
const char kChromeSelection[] = "CHROME_SELECTION"; const char kChromeSelection[] = "CHROME_SELECTION";
const char kClipboard[] = "CLIPBOARD"; const char kClipboard[] = "CLIPBOARD";
const char kMimeTypeUtf8[] = "text/plain;charset=utf-8";
const char kString[] = "STRING"; const char kString[] = "STRING";
const char kTargets[] = "TARGETS"; const char kTargets[] = "TARGETS";
const char kTimestamp[] = "TIMESTAMP"; const char kTimestamp[] = "TIMESTAMP";
...@@ -31,7 +30,7 @@ const char kUtf8String[] = "UTF8_STRING"; ...@@ -31,7 +30,7 @@ const char kUtf8String[] = "UTF8_STRING";
void ExpandTypes(std::vector<std::string>* list) { void ExpandTypes(std::vector<std::string>* list) {
bool has_mime_type_text = Contains(*list, ui::kMimeTypeText); bool has_mime_type_text = Contains(*list, ui::kMimeTypeText);
bool has_string = Contains(*list, kString); 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); bool has_utf8_string = Contains(*list, kUtf8String);
if (has_mime_type_text && !has_string) if (has_mime_type_text && !has_string)
list->push_back(kString); list->push_back(kString);
...@@ -40,7 +39,7 @@ void ExpandTypes(std::vector<std::string>* list) { ...@@ -40,7 +39,7 @@ void ExpandTypes(std::vector<std::string>* list) {
if (has_mime_type_utf8 && !has_utf8_string) if (has_mime_type_utf8 && !has_utf8_string)
list->push_back(kUtf8String); list->push_back(kUtf8String);
if (has_utf8_string && !has_mime_type_utf8) if (has_utf8_string && !has_mime_type_utf8)
list->push_back(kMimeTypeUtf8); list->push_back(kMimeTypeTextUtf8);
} }
XID FindXEventTarget(const XEvent& xev) { XID FindXEventTarget(const XEvent& xev) {
...@@ -188,7 +187,7 @@ bool X11ClipboardOzone::OnSelectionRequest( ...@@ -188,7 +187,7 @@ bool X11ClipboardOzone::OnSelectionRequest(
std::string key = target_name; std::string key = target_name;
// Allow conversions for text/plain[;charset=utf-8] <=> [UTF8_]STRING. // Allow conversions for text/plain[;charset=utf-8] <=> [UTF8_]STRING.
if (key == kUtf8String && !Contains(offer_data_map, kUtf8String)) { if (key == kUtf8String && !Contains(offer_data_map, kUtf8String)) {
key = kMimeTypeUtf8; key = kMimeTypeTextUtf8;
} else if (key == kString && !Contains(offer_data_map, kString)) { } else if (key == kString && !Contains(offer_data_map, kString)) {
key = kMimeTypeText; key = kMimeTypeText;
} }
...@@ -355,7 +354,7 @@ void X11ClipboardOzone::ReadRemoteClipboard(XAtom selection) { ...@@ -355,7 +354,7 @@ void X11ClipboardOzone::ReadRemoteClipboard(XAtom selection) {
if (!Contains(selection_state.mime_types, target)) { if (!Contains(selection_state.mime_types, target)) {
if (target == kMimeTypeText) { if (target == kMimeTypeText) {
target = kString; target = kString;
} else if (target == kMimeTypeUtf8) { } else if (target == kMimeTypeTextUtf8) {
target = kUtf8String; 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