summaryrefslogtreecommitdiffstats
path: root/remoting
diff options
context:
space:
mode:
authorlambroslambrou@google.com <lambroslambrou@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-09-27 00:24:49 +0000
committerlambroslambrou@google.com <lambroslambrou@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-09-27 00:24:49 +0000
commit070ffb78150f235db8596104958722c19c6d1313 (patch)
tree30d4f6e72d37e78021e33da8c9e35eed251bd75a /remoting
parent4b0bcefe9ddfd0b205d64fc5bd6c5a31f8a05d36 (diff)
downloadchromium_src-070ffb78150f235db8596104958722c19c6d1313.zip
chromium_src-070ffb78150f235db8596104958722c19c6d1313.tar.gz
chromium_src-070ffb78150f235db8596104958722c19c6d1313.tar.bz2
Implement clipboard for Chromoting Linux hosts.
BUG=132454 Review URL: https://chromiumcodereview.appspot.com/10909133 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@158944 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r--remoting/host/chromoting_host_context.cc12
-rw-r--r--remoting/host/clipboard_linux.cc92
-rw-r--r--remoting/host/event_executor_linux.cc29
-rw-r--r--remoting/host/linux/x_server_clipboard.cc359
-rw-r--r--remoting/host/linux/x_server_clipboard.h106
-rw-r--r--remoting/remoting.gyp2
6 files changed, 586 insertions, 14 deletions
diff --git a/remoting/host/chromoting_host_context.cc b/remoting/host/chromoting_host_context.cc
index 37ba9f3..87eeb1b 100644
--- a/remoting/host/chromoting_host_context.cc
+++ b/remoting/host/chromoting_host_context.cc
@@ -40,14 +40,12 @@ void ChromotingHostContext::ReleaseTaskRunners() {
bool ChromotingHostContext::Start() {
// Start all the threads.
+ base::Thread::Options io_thread_options(MessageLoop::TYPE_IO, 0);
bool started = capture_thread_.Start() && encode_thread_.Start() &&
- audio_thread_.StartWithOptions(base::Thread::Options(
- MessageLoop::TYPE_IO, 0)) &&
- network_thread_.StartWithOptions(base::Thread::Options(
- MessageLoop::TYPE_IO, 0)) &&
- desktop_thread_.Start() &&
- file_thread_.StartWithOptions(
- base::Thread::Options(MessageLoop::TYPE_IO, 0));
+ audio_thread_.StartWithOptions(io_thread_options) &&
+ network_thread_.StartWithOptions(io_thread_options) &&
+ desktop_thread_.StartWithOptions(io_thread_options) &&
+ file_thread_.StartWithOptions(io_thread_options);
if (!started)
return false;
diff --git a/remoting/host/clipboard_linux.cc b/remoting/host/clipboard_linux.cc
index 6a17291..1f74689 100644
--- a/remoting/host/clipboard_linux.cc
+++ b/remoting/host/clipboard_linux.cc
@@ -4,37 +4,119 @@
#include "remoting/host/clipboard.h"
+#include <X11/Xlib.h>
+
+#include "base/bind.h"
#include "base/logging.h"
+#include "base/message_loop.h"
+#include "remoting/host/linux/x_server_clipboard.h"
+#include "remoting/proto/event.pb.h"
+#include "remoting/protocol/clipboard_stub.h"
namespace remoting {
-class ClipboardLinux : public Clipboard {
+// This code is expected to be called on the desktop thread only.
+class ClipboardLinux : public Clipboard,
+ public MessageLoopForIO::Watcher {
public:
ClipboardLinux();
+ virtual ~ClipboardLinux();
- // Must be called on the UI thread.
+ // Clipboard interface.
virtual void Start(
scoped_ptr<protocol::ClipboardStub> client_clipboard) OVERRIDE;
virtual void InjectClipboardEvent(
const protocol::ClipboardEvent& event) OVERRIDE;
virtual void Stop() OVERRIDE;
+ // MessageLoopForIO::Watcher interface.
+ virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
+ virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE;
+
private:
+ void OnClipboardChanged(const std::string& mime_type,
+ const std::string& data);
+ void PumpXEvents();
+
+ scoped_ptr<protocol::ClipboardStub> client_clipboard_;
+
+ XServerClipboard x_server_clipboard_;
+ Display* display_;
+
+ MessageLoopForIO::FileDescriptorWatcher x_connection_watcher_;
+
DISALLOW_COPY_AND_ASSIGN(ClipboardLinux);
};
+ClipboardLinux::ClipboardLinux()
+ : display_(NULL) {
+}
+
+ClipboardLinux::~ClipboardLinux() {
+ Stop();
+}
+
void ClipboardLinux::Start(
scoped_ptr<protocol::ClipboardStub> client_clipboard) {
- NOTIMPLEMENTED();
+ // TODO(lambroslambrou): Share the X connection with EventExecutor.
+ display_ = XOpenDisplay(NULL);
+ if (!display_) {
+ LOG(ERROR) << "Couldn't open X display";
+ return;
+ }
+ client_clipboard_.swap(client_clipboard);
+
+ x_server_clipboard_.Init(display_,
+ base::Bind(&ClipboardLinux::OnClipboardChanged,
+ base::Unretained(this)));
+
+ MessageLoopForIO::current()->WatchFileDescriptor(
+ ConnectionNumber(display_), true, MessageLoopForIO::WATCH_READ,
+ &x_connection_watcher_, this);
+ PumpXEvents();
}
void ClipboardLinux::InjectClipboardEvent(
const protocol::ClipboardEvent& event) {
- NOTIMPLEMENTED();
+ x_server_clipboard_.SetClipboard(event.mime_type(), event.data());
}
void ClipboardLinux::Stop() {
- NOTIMPLEMENTED();
+ client_clipboard_.reset();
+ x_connection_watcher_.StopWatchingFileDescriptor();
+
+ if (display_) {
+ XCloseDisplay(display_);
+ display_ = NULL;
+ }
+}
+
+void ClipboardLinux::OnFileCanReadWithoutBlocking(int fd) {
+ PumpXEvents();
+}
+
+void ClipboardLinux::OnFileCanWriteWithoutBlocking(int fd) {
+}
+
+void ClipboardLinux::OnClipboardChanged(const std::string& mime_type,
+ const std::string& data) {
+ protocol::ClipboardEvent event;
+ event.set_mime_type(mime_type);
+ event.set_data(data);
+
+ if (client_clipboard_.get()) {
+ client_clipboard_->InjectClipboardEvent(event);
+ }
+}
+
+void ClipboardLinux::PumpXEvents() {
+ DCHECK(display_);
+
+ while (XPending(display_)) {
+ XEvent event;
+ XNextEvent(display_, &event);
+ x_server_clipboard_.ProcessXEvent(&event);
+ }
}
scoped_ptr<Clipboard> Clipboard::Create() {
diff --git a/remoting/host/event_executor_linux.cc b/remoting/host/event_executor_linux.cc
index 3ac3392..449a524 100644
--- a/remoting/host/event_executor_linux.cc
+++ b/remoting/host/event_executor_linux.cc
@@ -16,6 +16,7 @@
#include "base/location.h"
#include "base/logging.h"
#include "base/single_thread_task_runner.h"
+#include "remoting/host/clipboard.h"
#include "remoting/proto/internal.pb.h"
#include "third_party/skia/include/core/SkPoint.h"
@@ -59,6 +60,8 @@ class EventExecutorLinux : public EventExecutor {
// Left, Right, Middle, VScroll Up/Down, HScroll Left/Right.
static const int kNumPointerButtons = 7;
+ void InitClipboard();
+
// |mode| is one of the AutoRepeatModeOn, AutoRepeatModeOff,
// AutoRepeatModeDefault constants defined by the XChangeKeyboardControl()
// API.
@@ -83,6 +86,9 @@ class EventExecutorLinux : public EventExecutor {
int test_error_base_;
int pointer_button_map_[kNumPointerButtons];
+
+ scoped_ptr<Clipboard> clipboard_;
+
DISALLOW_COPY_AND_ASSIGN(EventExecutorLinux);
};
@@ -92,6 +98,11 @@ EventExecutorLinux::EventExecutorLinux(
latest_mouse_position_(SkIPoint::Make(-1, -1)),
display_(XOpenDisplay(NULL)),
root_window_(BadValue) {
+ if (!task_runner_->BelongsToCurrentThread()) {
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&EventExecutorLinux::InitClipboard, base::Unretained(this)));
+ }
}
EventExecutorLinux::~EventExecutorLinux() {
@@ -120,7 +131,15 @@ bool EventExecutorLinux::Init() {
}
void EventExecutorLinux::InjectClipboardEvent(const ClipboardEvent& event) {
- // TODO(simonmorris): Implement clipboard injection.
+ if (!task_runner_->BelongsToCurrentThread()) {
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&EventExecutorLinux::InjectClipboardEvent,
+ base::Unretained(this), event));
+ return;
+ }
+
+ clipboard_->InjectClipboardEvent(event);
}
void EventExecutorLinux::InjectKeyEvent(const KeyEvent& event) {
@@ -170,6 +189,11 @@ void EventExecutorLinux::InjectKeyEvent(const KeyEvent& event) {
XFlush(display_);
}
+void EventExecutorLinux::InitClipboard() {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ clipboard_ = Clipboard::Create();
+}
+
void EventExecutorLinux::SetAutoRepeatForKey(int keycode, int mode) {
XKeyboardControl control;
control.key = keycode;
@@ -365,8 +389,9 @@ void EventExecutorLinux::Start(
base::Passed(&client_clipboard)));
return;
}
+
InitMouseButtonMap();
- return;
+ clipboard_->Start(client_clipboard.Pass());
}
void EventExecutorLinux::StopAndDelete() {
diff --git a/remoting/host/linux/x_server_clipboard.cc b/remoting/host/linux/x_server_clipboard.cc
new file mode 100644
index 0000000..86bea19
--- /dev/null
+++ b/remoting/host/linux/x_server_clipboard.cc
@@ -0,0 +1,359 @@
+// Copyright (c) 2012 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 "remoting/host/linux/x_server_clipboard.h"
+
+#include <X11/extensions/Xfixes.h>
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "remoting/base/constants.h"
+
+namespace remoting {
+
+XServerClipboard::XServerClipboard()
+ : display_(NULL),
+ clipboard_window_(BadValue),
+ xfixes_event_base_(-1),
+ xfixes_error_base_(-1),
+ clipboard_atom_(None),
+ large_selection_atom_(None),
+ selection_string_atom_(None),
+ targets_atom_(None),
+ timestamp_atom_(None),
+ utf8_string_atom_(None),
+ large_selection_property_(None) {
+}
+
+XServerClipboard::~XServerClipboard() {
+}
+
+void XServerClipboard::Init(Display* display,
+ const ClipboardChangedCallback& callback) {
+ display_ = display;
+ callback_ = callback;
+
+ // If any of these X API calls fail, an X Error will be raised, crashing the
+ // process. This is unlikely to occur in practice, and even if it does, it
+ // would mean the X server is in a bad state, so it's not worth trying to
+ // trap such errors here.
+
+ // TODO(lambroslambrou): Consider using ScopedXErrorHandler here, or consider
+ // placing responsibility for handling X Errors outside this class, since
+ // X Error handlers are global to all X connections.
+ if (!XFixesQueryExtension(display_, &xfixes_event_base_,
+ &xfixes_error_base_)) {
+ LOG(INFO) << "X server does not support XFixes.";
+ return;
+ }
+
+ clipboard_window_ = XCreateSimpleWindow(display_,
+ DefaultRootWindow(display_),
+ 0, 0, 1, 1, // x, y, width, height
+ 0, 0, 0);
+
+ XFixesSelectSelectionInput(display_, clipboard_window_, XA_PRIMARY,
+ XFixesSetSelectionOwnerNotifyMask);
+ XFixesSelectSelectionInput(display_, clipboard_window_, clipboard_atom_,
+ XFixesSetSelectionOwnerNotifyMask);
+
+ // TODO(lambroslambrou): Use ui::X11AtomCache for this, either by adding a
+ // dependency on ui/ or by moving X11AtomCache to base/.
+ const int kNumAtoms = 6;
+ const char* names[kNumAtoms] = {
+ "CLIPBOARD",
+ "INCR",
+ "SELECTION_STRING",
+ "TARGETS",
+ "TIMESTAMP",
+ "UTF8_STRING" };
+ Atom atoms[kNumAtoms];
+ if (XInternAtoms(display_, const_cast<char**>(names), kNumAtoms, False,
+ atoms)) {
+ clipboard_atom_ = atoms[0];
+ large_selection_atom_ = atoms[1];
+ selection_string_atom_ = atoms[2];
+ targets_atom_ = atoms[3];
+ timestamp_atom_ = atoms[4];
+ utf8_string_atom_ = atoms[5];
+ } else {
+ LOG(ERROR) << "XInternAtoms failed";
+ }
+}
+
+void XServerClipboard::SetClipboard(const std::string& mime_type,
+ const std::string& data) {
+ DCHECK(display_);
+
+ if (clipboard_window_ == BadValue) {
+ return;
+ }
+
+ // Currently only UTF-8 is supported.
+ if (mime_type != kMimeTypeTextUtf8) {
+ return;
+ }
+
+ data_ = data;
+
+ AssertSelectionOwnership(XA_PRIMARY);
+ AssertSelectionOwnership(clipboard_atom_);
+}
+
+void XServerClipboard::ProcessXEvent(XEvent* event) {
+ if (clipboard_window_ == BadValue ||
+ event->xany.window != clipboard_window_) {
+ return;
+ }
+
+ switch (event->type) {
+ case PropertyNotify:
+ OnPropertyNotify(event);
+ break;
+ case SelectionNotify:
+ OnSelectionNotify(event);
+ break;
+ case SelectionRequest:
+ OnSelectionRequest(event);
+ break;
+ default:
+ break;
+ }
+
+ if (event->type == xfixes_event_base_ + XFixesSetSelectionOwnerNotify) {
+ XFixesSelectionNotifyEvent* notify_event =
+ reinterpret_cast<XFixesSelectionNotifyEvent*>(event);
+ OnSetSelectionOwnerNotify(notify_event->selection,
+ notify_event->selection_timestamp);
+ }
+}
+
+void XServerClipboard::OnSetSelectionOwnerNotify(Atom selection,
+ Time timestamp) {
+ // Protect against receiving new XFixes selection notifications whilst we're
+ // in the middle of waiting for information from the current selection owner.
+ // A reasonable timeout allows for misbehaving apps that don't respond
+ // quickly to our requests.
+ if (!get_selections_time_.is_null() &&
+ (base::TimeTicks::Now() - get_selections_time_) <
+ base::TimeDelta::FromSeconds(5)) {
+ // TODO(lambroslambrou): Instead of ignoring this notification, cancel any
+ // pending request operations and ignore the resulting events, before
+ // dispatching new requests here.
+ return;
+ }
+
+ if (selection != clipboard_atom_ && selection != XA_PRIMARY) {
+ // Only process PRIMARY and CLIPBOARD selections.
+ return;
+ }
+
+ // If we own the selection, don't request details for it.
+ if (IsSelectionOwner(selection)) {
+ return;
+ }
+
+ get_selections_time_ = base::TimeTicks::Now();
+
+ // Before getting the value of the chosen selection, request the list of
+ // target formats it supports.
+ RequestSelectionTargets(selection);
+}
+
+void XServerClipboard::OnPropertyNotify(XEvent* event) {
+ if (large_selection_property_ != None &&
+ event->xproperty.atom == large_selection_property_ &&
+ event->xproperty.state == PropertyNewValue) {
+ Atom type;
+ int format;
+ unsigned long item_count, after;
+ unsigned char *data;
+ XGetWindowProperty(display_, clipboard_window_, large_selection_property_,
+ 0, ~0L, True, AnyPropertyType, &type, &format,
+ &item_count, &after, &data);
+ if (type != None) {
+ // TODO(lambroslambrou): Properly support large transfers -
+ // http://crbug.com/151447.
+ XFree(data);
+
+ // If the property is zero-length then the large transfer is complete.
+ if (item_count == 0) {
+ large_selection_property_ = None;
+ }
+ }
+ }
+}
+
+void XServerClipboard::OnSelectionNotify(XEvent* event) {
+ if (event->xselection.property != None) {
+ Atom type;
+ int format;
+ unsigned long item_count, after;
+ unsigned char *data;
+ XGetWindowProperty(display_, clipboard_window_,
+ event->xselection.property, 0, ~0L, True,
+ AnyPropertyType, &type, &format,
+ &item_count, &after, &data);
+ if (type == large_selection_atom_) {
+ // Large selection - just read and ignore these for now.
+ large_selection_property_ = event->xselection.property;
+ } else {
+ // Standard selection - call the selection notifier.
+ large_selection_property_ = None;
+ if (type != None) {
+ HandleSelectionNotify(&event->xselection, type, format, item_count,
+ data);
+ XFree(data);
+ return;
+ }
+ }
+ }
+ HandleSelectionNotify(&event->xselection, 0, 0, 0, 0);
+}
+
+void XServerClipboard::OnSelectionRequest(XEvent* event) {
+ XSelectionEvent selection_event;
+ selection_event.type = SelectionNotify;
+ selection_event.display = event->xselectionrequest.display;
+ selection_event.requestor = event->xselectionrequest.requestor;
+ selection_event.selection = event->xselectionrequest.selection;
+ selection_event.time = event->xselectionrequest.time;
+ selection_event.target = event->xselectionrequest.target;
+ if (event->xselectionrequest.property == None) {
+ event->xselectionrequest.property = event->xselectionrequest.target;
+ }
+ if (!IsSelectionOwner(selection_event.selection)) {
+ selection_event.property = None;
+ } else {
+ selection_event.property = event->xselectionrequest.property;
+ if (selection_event.target == targets_atom_) {
+ // Respond advertising XA_STRING, UTF8_STRING and TIMESTAMP data for the
+ // selection.
+ Atom targets[3];
+ targets[0] = timestamp_atom_;
+ targets[1] = utf8_string_atom_;
+ targets[2] = XA_STRING;
+ XChangeProperty(display_, selection_event.requestor,
+ selection_event.property, XA_ATOM, 32, PropModeReplace,
+ reinterpret_cast<unsigned char*>(targets), 3);
+ } else if (selection_event.target == timestamp_atom_) {
+ // Respond with the timestamp of our selection; we always return
+ // CurrentTime since our selections are set by remote clients, so there
+ // is no associated local X event.
+
+ // TODO(lambroslambrou): Should use a proper timestamp here instead of
+ // CurrentTime. ICCCM recommends doing a zero-length property append,
+ // and getting a timestamp from the subsequent PropertyNotify event.
+ Time time = CurrentTime;
+ XChangeProperty(display_, selection_event.requestor,
+ selection_event.property, XA_INTEGER, 32,
+ PropModeReplace, reinterpret_cast<unsigned char*>(&time),
+ 1);
+ } else if (selection_event.target == utf8_string_atom_ ||
+ selection_event.target == XA_STRING) {
+ if (!data_.empty()) {
+ // Return the actual string data; we always return UTF8, regardless of
+ // the configured locale.
+ XChangeProperty(display_, selection_event.requestor,
+ selection_event.property, selection_event.target, 8,
+ PropModeReplace,
+ reinterpret_cast<unsigned char*>(
+ const_cast<char*>(data_.data())),
+ data_.size());
+ }
+ }
+ }
+ XSendEvent(display_, selection_event.requestor, False, 0,
+ reinterpret_cast<XEvent*>(&selection_event));
+}
+
+void XServerClipboard::HandleSelectionNotify(XSelectionEvent* event,
+ Atom type,
+ int format,
+ int item_count,
+ void* data) {
+ bool finished = false;
+
+ if (event->target == targets_atom_) {
+ finished = HandleSelectionTargetsEvent(event, format, item_count, data);
+ } else if (event->target == utf8_string_atom_ ||
+ event->target == XA_STRING) {
+ finished = HandleSelectionStringEvent(event, format, item_count, data);
+ }
+
+ if (finished) {
+ get_selections_time_ = base::TimeTicks();
+ }
+}
+
+bool XServerClipboard::HandleSelectionTargetsEvent(XSelectionEvent* event,
+ int format,
+ int item_count,
+ void* data) {
+ if (event->property == targets_atom_) {
+ if (data && format == 32) {
+ // The XGetWindowProperty man-page specifies that the returned
+ // property data will be an array of |long|s in the case where
+ // |format| == 32. Although the items are 32-bit values (as stored and
+ // sent over the X protocol), Xlib presents the data to the client as an
+ // array of |long|s, with zero-padding on a 64-bit system where |long|
+ // is bigger than 32 bits.
+ const long* targets = static_cast<const long*>(data);
+ for (int i = 0; i < item_count; i++) {
+ if (targets[i] == static_cast<long>(utf8_string_atom_)) {
+ RequestSelectionString(event->selection, utf8_string_atom_);
+ return false;
+ }
+ }
+ }
+ }
+ RequestSelectionString(event->selection, XA_STRING);
+ return false;
+}
+
+bool XServerClipboard::HandleSelectionStringEvent(XSelectionEvent* event,
+ int format,
+ int item_count,
+ void* data) {
+ if (event->property != selection_string_atom_ || !data || format != 8) {
+ return true;
+ }
+
+ std::string text(static_cast<char*>(data), item_count);
+
+ if (event->target == XA_STRING || event->target == utf8_string_atom_) {
+ NotifyClipboardText(text);
+ }
+ return true;
+}
+
+void XServerClipboard::NotifyClipboardText(const std::string& text) {
+ data_ = text;
+ callback_.Run(kMimeTypeTextUtf8, data_);
+}
+
+void XServerClipboard::RequestSelectionTargets(Atom selection) {
+ XConvertSelection(display_, selection, targets_atom_, targets_atom_,
+ clipboard_window_, CurrentTime);
+}
+
+void XServerClipboard::RequestSelectionString(Atom selection, Atom target) {
+ XConvertSelection(display_, selection, target, selection_string_atom_,
+ clipboard_window_, CurrentTime);
+}
+
+void XServerClipboard::AssertSelectionOwnership(Atom selection) {
+ XSetSelectionOwner(display_, selection, clipboard_window_, CurrentTime);
+ if (XGetSelectionOwner(display_, selection) == clipboard_window_) {
+ selections_owned_.insert(selection);
+ } else {
+ LOG(ERROR) << "XSetSelectionOwner failed for selection " << selection;
+ }
+}
+
+bool XServerClipboard::IsSelectionOwner(Atom selection) {
+ return selections_owned_.find(selection) != selections_owned_.end();
+}
+
+} // namespace remoting
diff --git a/remoting/host/linux/x_server_clipboard.h b/remoting/host/linux/x_server_clipboard.h
new file mode 100644
index 0000000..f173351
--- /dev/null
+++ b/remoting/host/linux/x_server_clipboard.h
@@ -0,0 +1,106 @@
+// Copyright (c) 2012 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.
+
+// Don't include this file from any .h files because it pulls in some X headers.
+
+#ifndef REMOTING_HOST_LINUX_X_SERVER_CLIPBOARD_H_
+#define REMOTING_HOST_LINUX_X_SERVER_CLIPBOARD_H_
+
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+
+#include <set>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback_forward.h"
+#include "base/timer.h"
+
+namespace remoting {
+
+// A class to allow manipulation of the X clipboard, using only X API calls.
+// This class is not thread-safe, so all its methods must be called on the
+// application's main event-processing thread.
+class XServerClipboard {
+ public:
+ typedef base::Callback<void(const std::string& mime_type,
+ const std::string& data)>
+ ClipboardChangedCallback;
+
+ XServerClipboard();
+ ~XServerClipboard();
+
+ // Start monitoring |display|'s selections, and invoke |callback| whenever
+ // their content changes. The caller must ensure |display| is still valid
+ // whenever any other methods are called on this object.
+ void Init(Display* display, const ClipboardChangedCallback& callback);
+
+ // Copy data to the X Clipboard. This acquires ownership of the
+ // PRIMARY and CLIPBOARD selections.
+ void SetClipboard(const std::string& mime_type, const std::string& data);
+
+ // Process |event| if it is an X selection notification. The caller should
+ // invoke this for every event it receives from |display|.
+ void ProcessXEvent(XEvent* event);
+
+ private:
+ // Handlers for X selection events.
+ void OnSetSelectionOwnerNotify(Atom selection, Time timestamp);
+ void OnPropertyNotify(XEvent* event);
+ void OnSelectionNotify(XEvent* event);
+ void OnSelectionRequest(XEvent* event);
+
+ // Called when the selection owner has replied to a request for information
+ // about a selection.
+ void HandleSelectionNotify(XSelectionEvent* event,
+ Atom type,
+ int format,
+ int item_count,
+ void* data);
+
+ // These methods return true if selection processing is complete, false
+ // otherwise.
+ bool HandleSelectionTargetsEvent(XSelectionEvent* event,
+ int format,
+ int item_count,
+ void* data);
+ bool HandleSelectionStringEvent(XSelectionEvent* event,
+ int format,
+ int item_count,
+ void* data);
+
+ // Notify the registered callback of new clipboard text.
+ void NotifyClipboardText(const std::string& text);
+
+ // These methods trigger the X server or selection owner to send back an
+ // event containing the requested information.
+ void RequestSelectionTargets(Atom selection);
+ void RequestSelectionString(Atom selection, Atom target);
+
+ // Assert ownership of the specified |selection|.
+ void AssertSelectionOwnership(Atom selection);
+ bool IsSelectionOwner(Atom selection);
+
+ Display* display_;
+ Window clipboard_window_;
+ int xfixes_event_base_;
+ int xfixes_error_base_;
+ Atom clipboard_atom_;
+ Atom large_selection_atom_;
+ Atom selection_string_atom_;
+ Atom targets_atom_;
+ Atom timestamp_atom_;
+ Atom utf8_string_atom_;
+ std::set<Atom> selections_owned_;
+ std::string data_;
+ Atom large_selection_property_;
+ base::TimeTicks get_selections_time_;
+ ClipboardChangedCallback callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(XServerClipboard);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_HOST_LINUX_X_SERVER_CLIPBOARD_H_
diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp
index a448657..5ff1b86 100644
--- a/remoting/remoting.gyp
+++ b/remoting/remoting.gyp
@@ -1349,6 +1349,8 @@
'host/it2me_host_user_interface.h',
'host/json_host_config.cc',
'host/json_host_config.h',
+ 'host/linux/x_server_clipboard.cc',
+ 'host/linux/x_server_clipboard.h',
'host/local_input_monitor.h',
'host/local_input_monitor_linux.cc',
'host/local_input_monitor_mac.mm',