Implement X11 clipboard for ozone x11
Supports only CLIPBOARD selection and not PRIMARY (for now) since ozone only supports a single clipboard. Regisers to receive XFixesSetSelectionOwnerNotify events and prefetches and caches supported mime types whenever ownership changes. Also prefetches clipboard contents as 'text/plain' since that is the most likely type to be requested. If clients request a different type, it will be fetched at the time of requesting. Converts text/plain[;charset=utf-8] <=> [UTF8_]STRING to allow interoperability with other applications which do not use the same mime types as chrome. This code can be patched into linux-chromeos (chrome os running on a linux desktop) by modifying the build file: sed -i s/is_desktop_linux/is_linux/ ui/base/clipboard/BUILD.gn Bug: 985187 Change-Id: I4277f903ab8f11e0f5ad14d095eb099cf775f771 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1736619 Reviewed-by: kylechar <kylechar@chromium.org> Commit-Queue: Joel Hockey <joelhockey@chromium.org> Cr-Commit-Position: refs/heads/master@{#691892}
This commit is contained in:
@ -20,8 +20,8 @@ source_set("x11") {
|
||||
"gl_surface_glx_ozone.h",
|
||||
"ozone_platform_x11.cc",
|
||||
"ozone_platform_x11.h",
|
||||
"x11_clipboard.cc",
|
||||
"x11_clipboard.h",
|
||||
"x11_clipboard_ozone.cc",
|
||||
"x11_clipboard_ozone.h",
|
||||
"x11_cursor_factory_ozone.cc",
|
||||
"x11_cursor_factory_ozone.h",
|
||||
"x11_cursor_ozone.cc",
|
||||
@ -41,6 +41,7 @@ source_set("x11") {
|
||||
"//gpu/vulkan:buildflags",
|
||||
"//skia",
|
||||
"//ui/base",
|
||||
"//ui/base/clipboard:clipboard_types",
|
||||
"//ui/base/ime",
|
||||
"//ui/base/x",
|
||||
"//ui/display/fake",
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include "ui/events/platform/x11/x11_event_source_default.h"
|
||||
#include "ui/gfx/x/x11.h"
|
||||
#include "ui/ozone/common/stub_overlay_manager.h"
|
||||
#include "ui/ozone/platform/x11/x11_clipboard.h"
|
||||
#include "ui/ozone/platform/x11/x11_clipboard_ozone.h"
|
||||
#include "ui/ozone/platform/x11/x11_cursor_factory_ozone.h"
|
||||
#include "ui/ozone/platform/x11/x11_screen_ozone.h"
|
||||
#include "ui/ozone/platform/x11/x11_surface_factory.h"
|
||||
@ -126,7 +126,7 @@ class OzonePlatformX11 : public OzonePlatform {
|
||||
window_manager_ = std::make_unique<X11WindowManagerOzone>();
|
||||
overlay_manager_ = std::make_unique<StubOverlayManager>();
|
||||
input_controller_ = CreateStubInputController();
|
||||
clipboard_ = std::make_unique<X11Clipboard>();
|
||||
clipboard_ = std::make_unique<X11ClipboardOzone>();
|
||||
cursor_factory_ozone_ = std::make_unique<X11CursorFactoryOzone>();
|
||||
gpu_platform_support_host_.reset(CreateStubGpuPlatformSupportHost());
|
||||
|
||||
@ -179,7 +179,7 @@ class OzonePlatformX11 : public OzonePlatform {
|
||||
std::unique_ptr<X11WindowManagerOzone> window_manager_;
|
||||
std::unique_ptr<OverlayManagerOzone> overlay_manager_;
|
||||
std::unique_ptr<InputController> input_controller_;
|
||||
std::unique_ptr<X11Clipboard> clipboard_;
|
||||
std::unique_ptr<X11ClipboardOzone> clipboard_;
|
||||
std::unique_ptr<X11CursorFactoryOzone> cursor_factory_ozone_;
|
||||
std::unique_ptr<GpuPlatformSupportHost> gpu_platform_support_host_;
|
||||
|
||||
|
@ -1,56 +0,0 @@
|
||||
// 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 <utility>
|
||||
|
||||
#include "ui/ozone/platform/x11/x11_clipboard.h"
|
||||
|
||||
namespace ui {
|
||||
|
||||
X11Clipboard::X11Clipboard() = default;
|
||||
X11Clipboard::~X11Clipboard() = default;
|
||||
|
||||
void X11Clipboard::OfferClipboardData(
|
||||
const PlatformClipboard::DataMap& data_map,
|
||||
PlatformClipboard::OfferDataClosure callback) {
|
||||
// TODO(crbug.com/973295): Implement X11Clipboard
|
||||
NOTIMPLEMENTED_LOG_ONCE();
|
||||
std::move(callback).Run();
|
||||
}
|
||||
|
||||
void X11Clipboard::RequestClipboardData(
|
||||
const std::string& mime_type,
|
||||
PlatformClipboard::DataMap* data_map,
|
||||
PlatformClipboard::RequestDataClosure callback) {
|
||||
// TODO(crbug.com/973295): Implement X11Clipboard
|
||||
NOTIMPLEMENTED_LOG_ONCE();
|
||||
std::move(callback).Run({});
|
||||
}
|
||||
|
||||
void X11Clipboard::GetAvailableMimeTypes(
|
||||
PlatformClipboard::GetMimeTypesClosure callback) {
|
||||
// TODO(crbug.com/973295): Implement X11Clipboard
|
||||
NOTIMPLEMENTED_LOG_ONCE();
|
||||
std::move(callback).Run({});
|
||||
}
|
||||
|
||||
bool X11Clipboard::IsSelectionOwner() {
|
||||
// TODO(crbug.com/973295): Implement X11Clipboard
|
||||
NOTIMPLEMENTED_LOG_ONCE();
|
||||
return false;
|
||||
}
|
||||
|
||||
void X11Clipboard::SetSequenceNumberUpdateCb(
|
||||
PlatformClipboard::SequenceNumberUpdateCb cb) {
|
||||
DCHECK(update_sequence_cb_.is_null())
|
||||
<< " The callback can be installed only once.";
|
||||
update_sequence_cb_ = std::move(cb);
|
||||
}
|
||||
|
||||
void X11Clipboard::UpdateSequenceNumber() {
|
||||
if (!update_sequence_cb_.is_null())
|
||||
update_sequence_cb_.Run();
|
||||
}
|
||||
|
||||
} // namespace ui
|
@ -1,46 +0,0 @@
|
||||
// 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_X11_X11_CLIPBOARD_H_
|
||||
#define UI_OZONE_PLATFORM_X11_X11_CLIPBOARD_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "ui/ozone/public/platform_clipboard.h"
|
||||
|
||||
namespace ui {
|
||||
|
||||
// Handles clipboard operations for X11 Platform
|
||||
class X11Clipboard : public PlatformClipboard {
|
||||
public:
|
||||
X11Clipboard();
|
||||
~X11Clipboard() override;
|
||||
|
||||
// PlatformClipboard.
|
||||
void OfferClipboardData(
|
||||
const PlatformClipboard::DataMap& data_map,
|
||||
PlatformClipboard::OfferDataClosure callback) override;
|
||||
void RequestClipboardData(
|
||||
const std::string& mime_type,
|
||||
PlatformClipboard::DataMap* data_map,
|
||||
PlatformClipboard::RequestDataClosure callback) override;
|
||||
void GetAvailableMimeTypes(
|
||||
PlatformClipboard::GetMimeTypesClosure callback) override;
|
||||
bool IsSelectionOwner() override;
|
||||
void SetSequenceNumberUpdateCb(
|
||||
PlatformClipboard::SequenceNumberUpdateCb cb) override;
|
||||
|
||||
private:
|
||||
void UpdateSequenceNumber();
|
||||
|
||||
// Notifies whenever clipboard sequence number is changed.
|
||||
PlatformClipboard::SequenceNumberUpdateCb update_sequence_cb_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(X11Clipboard);
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
|
||||
#endif // UI_OZONE_PLATFORM_X11_X11_CLIPBOARD_H_
|
357
ui/ozone/platform/x11/x11_clipboard_ozone.cc
Normal file
357
ui/ozone/platform/x11/x11_clipboard_ozone.cc
Normal file
@ -0,0 +1,357 @@
|
||||
// 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/x11/x11_clipboard_ozone.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "base/containers/span.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "ui/base/clipboard/clipboard_constants.h"
|
||||
#include "ui/gfx/x/x11_atom_cache.h"
|
||||
#include "ui/gfx/x/x11_types.h"
|
||||
|
||||
using base::Contains;
|
||||
|
||||
namespace ui {
|
||||
|
||||
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";
|
||||
const char kUtf8String[] = "UTF8_STRING";
|
||||
|
||||
// Helps to allow conversions for text/plain[;charset=utf-8] <=> [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_utf8_string = Contains(*list, kUtf8String);
|
||||
if (has_mime_type_text && !has_string)
|
||||
list->push_back(kString);
|
||||
if (has_string && !has_mime_type_text)
|
||||
list->push_back(ui::kMimeTypeText);
|
||||
if (has_mime_type_utf8 && !has_utf8_string)
|
||||
list->push_back(kUtf8String);
|
||||
if (has_utf8_string && !has_mime_type_utf8)
|
||||
list->push_back(kMimeTypeUtf8);
|
||||
}
|
||||
|
||||
XID FindXEventTarget(const XEvent& xev) {
|
||||
XID target = xev.xany.window;
|
||||
if (xev.type == GenericEvent)
|
||||
target = static_cast<XIDeviceEvent*>(xev.xcookie.data)->event;
|
||||
return target;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
X11ClipboardOzone::X11ClipboardOzone()
|
||||
: atom_clipboard_(gfx::GetAtom(kClipboard)),
|
||||
atom_targets_(gfx::GetAtom(kTargets)),
|
||||
atom_timestamp_(gfx::GetAtom(kTimestamp)),
|
||||
x_property_(gfx::GetAtom(kChromeSelection)),
|
||||
x_display_(gfx::GetXDisplay()),
|
||||
x_window_(XCreateSimpleWindow(x_display_,
|
||||
DefaultRootWindow(x_display_),
|
||||
/*x=*/-100,
|
||||
/*y=*/-100,
|
||||
/*width=*/10,
|
||||
/*height=*/10,
|
||||
/*border_width=*/0,
|
||||
/*border=*/0,
|
||||
/*background=*/0)) {
|
||||
int ignored; // xfixes_error_base.
|
||||
if (!XFixesQueryExtension(x_display_, &xfixes_event_base_, &ignored)) {
|
||||
LOG(ERROR) << "X server does not support XFixes.";
|
||||
return;
|
||||
}
|
||||
using_xfixes_ = true;
|
||||
|
||||
// Register to receive standard X11 events.
|
||||
X11EventSource::GetInstance()->AddXEventDispatcher(this);
|
||||
|
||||
// Register to receive XFixes notification when selection owner changes.
|
||||
XFixesSelectSelectionInput(x_display_, x_window_, atom_clipboard_,
|
||||
XFixesSetSelectionOwnerNotifyMask);
|
||||
|
||||
// Prefetch the current remote clipboard contents.
|
||||
QueryTargets();
|
||||
}
|
||||
|
||||
X11ClipboardOzone::~X11ClipboardOzone() {
|
||||
X11EventSource::GetInstance()->RemoveXEventDispatcher(this);
|
||||
}
|
||||
|
||||
bool X11ClipboardOzone::DispatchXEvent(XEvent* xev) {
|
||||
if (FindXEventTarget(*xev) != x_window_)
|
||||
return false;
|
||||
|
||||
switch (xev->type) {
|
||||
case SelectionRequest:
|
||||
return OnSelectionRequest(xev);
|
||||
case SelectionNotify:
|
||||
return OnSelectionNotify(xev);
|
||||
}
|
||||
|
||||
if (using_xfixes_ &&
|
||||
xev->type == xfixes_event_base_ + XFixesSetSelectionOwnerNotify) {
|
||||
return OnSetSelectionOwnerNotify(xev);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// We are the clipboard owner, and a remote peer has requested either:
|
||||
// TARGETS: List of mime types that we support for the clipboard.
|
||||
// TIMESTAMP: Time when we took ownership of the clipboard.
|
||||
// <mime-type>: Mime type to receive clipboard as.
|
||||
bool X11ClipboardOzone::OnSelectionRequest(XEvent* xev) {
|
||||
// We only support selection=CLIPBOARD, and property must be set.
|
||||
if (xev->xselectionrequest.selection != atom_clipboard_ ||
|
||||
xev->xselectionrequest.property == x11::None) {
|
||||
return false;
|
||||
}
|
||||
|
||||
XSelectionEvent selection_event;
|
||||
selection_event.type = SelectionNotify;
|
||||
selection_event.display = xev->xselectionrequest.display;
|
||||
selection_event.requestor = xev->xselectionrequest.requestor;
|
||||
selection_event.selection = xev->xselectionrequest.selection;
|
||||
selection_event.target = xev->xselectionrequest.target;
|
||||
selection_event.property = xev->xselectionrequest.property;
|
||||
selection_event.time = xev->xselectionrequest.time;
|
||||
|
||||
selection_event.property = xev->xselectionrequest.property;
|
||||
|
||||
// target=TARGETS.
|
||||
if (selection_event.target == atom_targets_) {
|
||||
std::vector<std::string> targets;
|
||||
// Add TIMESTAMP.
|
||||
targets.push_back(kTimestamp);
|
||||
for (auto& entry : offer_data_map_) {
|
||||
targets.push_back(entry.first);
|
||||
}
|
||||
// Expand types, then convert from string to atom.
|
||||
ExpandTypes(&targets);
|
||||
std::vector<XAtom> atoms;
|
||||
for (auto& entry : targets) {
|
||||
atoms.push_back(gfx::GetAtom(entry.c_str()));
|
||||
}
|
||||
XChangeProperty(
|
||||
x_display_, selection_event.requestor, selection_event.property,
|
||||
XA_ATOM, /*format=*/32, PropModeReplace,
|
||||
reinterpret_cast<unsigned char*>(atoms.data()), atoms.size());
|
||||
|
||||
} else if (selection_event.target == atom_timestamp_) {
|
||||
// target=TIMESTAMP.
|
||||
XChangeProperty(
|
||||
x_display_, selection_event.requestor, selection_event.property,
|
||||
XA_INTEGER, /*format=*/32, PropModeReplace,
|
||||
reinterpret_cast<unsigned char*>(&acquired_selection_timestamp_), 1);
|
||||
|
||||
} else {
|
||||
// Send clipboard data.
|
||||
char* target_name = XGetAtomName(x_display_, selection_event.target);
|
||||
|
||||
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;
|
||||
} else if (key == kString && !Contains(offer_data_map_, kString)) {
|
||||
key = kMimeTypeText;
|
||||
}
|
||||
auto it = offer_data_map_.find(key);
|
||||
if (it != offer_data_map_.end()) {
|
||||
XChangeProperty(
|
||||
x_display_, selection_event.requestor, selection_event.property,
|
||||
selection_event.target, /*format=*/8, PropModeReplace,
|
||||
const_cast<unsigned char*>(it->second.data()), it->second.size());
|
||||
}
|
||||
XFree(target_name);
|
||||
}
|
||||
|
||||
// Notify remote peer that clipboard has been sent.
|
||||
XSendEvent(x_display_, selection_event.requestor, /*propagate=*/x11::False,
|
||||
/*event_mask=*/0, reinterpret_cast<XEvent*>(&selection_event));
|
||||
return true;
|
||||
}
|
||||
|
||||
// A remote peer owns the clipboard. This event is received in response to
|
||||
// our request for TARGETS (GetAvailableMimeTypes), or a specific mime type
|
||||
// (RequestClipboardData).
|
||||
bool X11ClipboardOzone::OnSelectionNotify(XEvent* xev) {
|
||||
// GetAvailableMimeTypes.
|
||||
if (xev->xselection.target == atom_targets_) {
|
||||
XAtom type;
|
||||
int format;
|
||||
unsigned long item_count, after;
|
||||
unsigned char* data = nullptr;
|
||||
|
||||
if (XGetWindowProperty(x_display_, x_window_, x_property_,
|
||||
/*long_offset=*/0,
|
||||
/*long_length=*/256 * sizeof(XAtom),
|
||||
/*delete=*/x11::False, XA_ATOM, &type, &format,
|
||||
&item_count, &after, &data) != x11::Success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mime_types_.clear();
|
||||
base::span<XAtom> targets(reinterpret_cast<XAtom*>(data), item_count);
|
||||
for (auto target : targets) {
|
||||
char* atom_name = XGetAtomName(x_display_, target);
|
||||
if (atom_name) {
|
||||
mime_types_.push_back(atom_name);
|
||||
XFree(atom_name);
|
||||
}
|
||||
}
|
||||
XFree(data);
|
||||
|
||||
// If we have a saved callback, invoke it now with expanded types, otherwise
|
||||
// guess that we will want 'text/plain' and fetch it now.
|
||||
if (get_available_mime_types_callback_) {
|
||||
std::vector<std::string> result(mime_types_);
|
||||
ExpandTypes(&result);
|
||||
std::move(get_available_mime_types_callback_).Run(std::move(result));
|
||||
} else {
|
||||
data_mime_type_ = kMimeTypeText;
|
||||
ReadRemoteClipboard();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// RequestClipboardData.
|
||||
if (xev->xselection.property == x_property_) {
|
||||
XAtom type;
|
||||
int format;
|
||||
unsigned long item_count, after;
|
||||
unsigned char* data;
|
||||
XGetWindowProperty(x_display_, x_window_, x_property_,
|
||||
/*long_offset=*/0, /*long_length=*/~0L,
|
||||
/*delete=*/x11::True, AnyPropertyType, &type, &format,
|
||||
&item_count, &after, &data);
|
||||
if (type != x11::None && format == 8) {
|
||||
std::vector<unsigned char> tmp(data, data + item_count);
|
||||
data_ = tmp;
|
||||
}
|
||||
XFree(data);
|
||||
|
||||
// If we have a saved callback, invoke it now, otherwise this was a prefetch
|
||||
// and we have already saved |data_| for the next call to
|
||||
// |RequestClipboardData|.
|
||||
if (request_clipboard_data_callback_) {
|
||||
request_data_map_->emplace(data_mime_type_, data_);
|
||||
std::move(request_clipboard_data_callback_).Run(data_);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool X11ClipboardOzone::OnSetSelectionOwnerNotify(XEvent* xev) {
|
||||
// Reset state and fetch remote clipboard if there is a new remote owner.
|
||||
if (!IsSelectionOwner()) {
|
||||
mime_types_.clear();
|
||||
data_mime_type_.clear();
|
||||
data_.clear();
|
||||
QueryTargets();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void X11ClipboardOzone::QueryTargets() {
|
||||
mime_types_.clear();
|
||||
XConvertSelection(x_display_, atom_clipboard_, atom_targets_, x_property_,
|
||||
x_window_, x11::CurrentTime);
|
||||
}
|
||||
|
||||
void X11ClipboardOzone::ReadRemoteClipboard() {
|
||||
data_.clear();
|
||||
// Allow conversions for text/plain[;charset=utf-8] <=> [UTF8_]STRING.
|
||||
std::string target = data_mime_type_;
|
||||
if (!Contains(mime_types_, target)) {
|
||||
if (target == kMimeTypeText) {
|
||||
target = kString;
|
||||
} else if (target == kMimeTypeUtf8) {
|
||||
target = kUtf8String;
|
||||
}
|
||||
}
|
||||
|
||||
XConvertSelection(x_display_, atom_clipboard_, gfx::GetAtom(target.c_str()),
|
||||
x_property_, x_window_, x11::CurrentTime);
|
||||
}
|
||||
|
||||
void X11ClipboardOzone::OfferClipboardData(
|
||||
const PlatformClipboard::DataMap& data_map,
|
||||
PlatformClipboard::OfferDataClosure callback) {
|
||||
acquired_selection_timestamp_ = X11EventSource::GetInstance()->GetTimestamp();
|
||||
offer_data_map_ = data_map;
|
||||
// Only take ownership if we are using xfixes.
|
||||
// TODO(joelhockey): Make clipboard work without xfixes.
|
||||
if (using_xfixes_) {
|
||||
XSetSelectionOwner(x_display_, atom_clipboard_, x_window_,
|
||||
acquired_selection_timestamp_);
|
||||
}
|
||||
std::move(callback).Run();
|
||||
}
|
||||
|
||||
void X11ClipboardOzone::RequestClipboardData(
|
||||
const std::string& mime_type,
|
||||
PlatformClipboard::DataMap* data_map,
|
||||
PlatformClipboard::RequestDataClosure callback) {
|
||||
// If we are not using xfixes, return empty data.
|
||||
// TODO(joelhockey): Make clipboard work without xfixes.
|
||||
// If we have already prefetched the clipboard for the correct mime type,
|
||||
// then send it right away, otherwise save the callback and attempt to get the
|
||||
// requested mime type from the remote clipboard.
|
||||
if (!using_xfixes_ || (data_mime_type_ == mime_type && !data_.empty())) {
|
||||
data_map->emplace(mime_type, data_);
|
||||
std::move(callback).Run(data_);
|
||||
return;
|
||||
}
|
||||
data_mime_type_ = mime_type;
|
||||
request_data_map_ = data_map;
|
||||
DCHECK(request_clipboard_data_callback_.is_null());
|
||||
request_clipboard_data_callback_ = std::move(callback);
|
||||
ReadRemoteClipboard();
|
||||
}
|
||||
|
||||
void X11ClipboardOzone::GetAvailableMimeTypes(
|
||||
PlatformClipboard::GetMimeTypesClosure callback) {
|
||||
// If we are not using xfixes, return empty data.
|
||||
// TODO(joelhockey): Make clipboard work without xfixes.
|
||||
// If we already have the list of supported mime types, send the expanded list
|
||||
// of types right away, otherwise save the callback and get the list of
|
||||
// TARGETS from the remote clipboard.
|
||||
if (!using_xfixes_ || !mime_types_.empty()) {
|
||||
std::vector<std::string> result(mime_types_);
|
||||
ExpandTypes(&result);
|
||||
std::move(callback).Run(std::move(result));
|
||||
return;
|
||||
}
|
||||
DCHECK(get_available_mime_types_callback_.is_null());
|
||||
get_available_mime_types_callback_ = std::move(callback);
|
||||
QueryTargets();
|
||||
}
|
||||
|
||||
bool X11ClipboardOzone::IsSelectionOwner() {
|
||||
// If we are not using xfixes, then we are always the owner.
|
||||
// TODO(joelhockey): Make clipboard work without xfixes.
|
||||
return !using_xfixes_ ||
|
||||
XGetSelectionOwner(x_display_, atom_clipboard_) == x_window_;
|
||||
}
|
||||
|
||||
void X11ClipboardOzone::SetSequenceNumberUpdateCb(
|
||||
PlatformClipboard::SequenceNumberUpdateCb cb) {
|
||||
update_sequence_cb_ = std::move(cb);
|
||||
}
|
||||
|
||||
} // namespace ui
|
121
ui/ozone/platform/x11/x11_clipboard_ozone.h
Normal file
121
ui/ozone/platform/x11/x11_clipboard_ozone.h
Normal file
@ -0,0 +1,121 @@
|
||||
// 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_X11_X11_CLIPBOARD_OZONE_H_
|
||||
#define UI_OZONE_PLATFORM_X11_X11_CLIPBOARD_OZONE_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "ui/events/platform/x11/x11_event_source.h"
|
||||
#include "ui/gfx/x/x11.h"
|
||||
#include "ui/gfx/x/x11_types.h"
|
||||
#include "ui/ozone/public/platform_clipboard.h"
|
||||
|
||||
namespace ui {
|
||||
|
||||
// Handles clipboard operations for X11.
|
||||
// Registers to receive standard X11 events, as well as
|
||||
// XFixesSetSelectionOwnerNotify. When the remote owner changes, TARGETS and
|
||||
// text/plain are preemptively fetched. They can then be provided immediately
|
||||
// to GetAvailableMimeTypes, and RequestClipboardData when mime_type is
|
||||
// text/plain. Otherwise GetAvailableMimeTypes and RequestClipboardData call
|
||||
// the appropriate X11 functions and invoke callbacks when the associated events
|
||||
// are received.
|
||||
class X11ClipboardOzone : public PlatformClipboard, public XEventDispatcher {
|
||||
public:
|
||||
X11ClipboardOzone();
|
||||
~X11ClipboardOzone() override;
|
||||
|
||||
// PlatformClipboard:
|
||||
void OfferClipboardData(
|
||||
const PlatformClipboard::DataMap& data_map,
|
||||
PlatformClipboard::OfferDataClosure callback) override;
|
||||
void RequestClipboardData(
|
||||
const std::string& mime_type,
|
||||
PlatformClipboard::DataMap* data_map,
|
||||
PlatformClipboard::RequestDataClosure callback) override;
|
||||
void GetAvailableMimeTypes(
|
||||
PlatformClipboard::GetMimeTypesClosure callback) override;
|
||||
bool IsSelectionOwner() override;
|
||||
void SetSequenceNumberUpdateCb(
|
||||
PlatformClipboard::SequenceNumberUpdateCb cb) override;
|
||||
|
||||
private:
|
||||
// XEventDispatcher:
|
||||
bool DispatchXEvent(XEvent* xev) override;
|
||||
|
||||
bool OnSelectionRequest(XEvent* xev);
|
||||
bool OnSelectionNotify(XEvent* xev);
|
||||
bool OnSetSelectionOwnerNotify(XEvent* xev);
|
||||
|
||||
// Queries the current clipboard owner for what mime types are available by
|
||||
// sending XConvertSelection with target=TARGETS. After sending this, we
|
||||
// will receive a SelectionNotify event with xselection.target=TARGETS which
|
||||
// is processed in |OnSelectionNotify|.
|
||||
void QueryTargets();
|
||||
|
||||
// Reads the contents of the remote clipboard by sending XConvertSelection
|
||||
// with target=<mime-type>. After sending this, we will receive a
|
||||
// SelectionNotify event with xselection.target=<mime-type> which is processed
|
||||
// in |OnSelectionNotify|.
|
||||
void ReadRemoteClipboard();
|
||||
|
||||
// Notifies whenever clipboard sequence number is changed.
|
||||
PlatformClipboard::SequenceNumberUpdateCb update_sequence_cb_;
|
||||
|
||||
// DataMap we keep from |OfferClipboardData| to service remote requests for
|
||||
// the clipboard.
|
||||
PlatformClipboard::DataMap offer_data_map_;
|
||||
|
||||
// DataMap from |RequestClipboardData| that we write remote clipboard contents
|
||||
// to before calling the completion callback.
|
||||
PlatformClipboard::DataMap* request_data_map_ = nullptr;
|
||||
|
||||
// Mime types supported by remote clipboard.
|
||||
std::vector<std::string> mime_types_;
|
||||
|
||||
// Data most recently read from remote clipboard.
|
||||
std::vector<unsigned char> data_;
|
||||
|
||||
// Mime type of most recently read data from remote clipboard.
|
||||
std::string data_mime_type_;
|
||||
|
||||
// If XFixes is unavailable, this clipboard window will not register to
|
||||
// receive events and no processing will take place.
|
||||
// TODO(joelhockey): Make clipboard work without xfixes.
|
||||
bool using_xfixes_ = false;
|
||||
|
||||
// The event base returned by XFixesQueryExtension().
|
||||
int xfixes_event_base_;
|
||||
|
||||
// Callbacks are stored when we haven't already prefetched the remote
|
||||
// clipboard.
|
||||
PlatformClipboard::GetMimeTypesClosure get_available_mime_types_callback_;
|
||||
PlatformClipboard::RequestDataClosure request_clipboard_data_callback_;
|
||||
|
||||
// Local cache of atoms.
|
||||
const XAtom atom_clipboard_;
|
||||
const XAtom atom_targets_;
|
||||
const XAtom atom_timestamp_;
|
||||
|
||||
// The property on |x_window_| which will receive remote clipboard contents.
|
||||
const XAtom x_property_;
|
||||
|
||||
// Our X11 state.
|
||||
Display* const x_display_;
|
||||
|
||||
// Input-only window used as a selection owner.
|
||||
const XID x_window_;
|
||||
|
||||
// The time that this instance took ownership of the clipboard.
|
||||
Time acquired_selection_timestamp_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(X11ClipboardOzone);
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
|
||||
#endif // UI_OZONE_PLATFORM_X11_X11_CLIPBOARD_OZONE_H_
|
Reference in New Issue
Block a user