Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1652)

Unified Diff: chrome/browser/gtk/xclipboard.cc

Issue 100145: WIP: X clipboard stuff (Closed)
Patch Set: Created 11 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/gtk/xclipboard.h ('k') | chrome/browser/renderer_host/resource_message_filter_gtk.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/gtk/xclipboard.cc
diff --git a/chrome/browser/gtk/xclipboard.cc b/chrome/browser/gtk/xclipboard.cc
new file mode 100644
index 0000000000000000000000000000000000000000..57c426f21456d1e01d944ac8e92ee485babcc850
--- /dev/null
+++ b/chrome/browser/gtk/xclipboard.cc
@@ -0,0 +1,242 @@
+// Have the ICCCM in hand when reading this code:
+// http://tronche.com/gui/x/icccm/sec-2.html#s-2
+
+struct InflightDownRequest {
+ Window requestor; // requesting window
+ Atom selection; // clipboard requested
+ Atom target; // type of the data requested
+ Atom property; // target property name on |requestor|
+ Time time; // request timestamp
+};
+
+struct ReplyReorderBufferKey {
+ Window requestor;
+ Atom selection;
+ Atom target;
+ Time time;
+
+ ReplyReorderBufferKey(const InflightDownRequest& downreq)
+ : requestor(downreq.requestor),
+ selection(downreq.selection),
+ target(downreq.target),
+ time(downreq.time) {
+ }
+
+ bool operator==(const ReplyReorderBufferKey& other) {
+ return other.requestor == requestor &&
+ other.selection == selection &&
+ other.target == target &&
+ other.time == time;
+ }
+};
+
+struct PendingReply {
+ explicit PendingReply(const InflightDownRequest& in_downreq)
+ : downreq(in_downreq),
+ data(NULL),
+ length(0) {
+ }
+
+ PendingReply(const InflightDownRequest& in_downreq, const uint8_t* data,
+ size_t in_length)
+ : downreq(in_downreq),
+ data(in_data),
+ length(in_length) {
+ }
+
+ InflightDownRequest downreq;
+ const uint8_t* data; // if NULL for |ReplyWithFailure|
+ size_t length;
+};
+
+struct XClipboardImpl {
+ Display* display;
+ Window window;
+ uint64_t current_tag;
+
+ std::map<uint64_t, InflightUpRequest> up_requests;
+ std::map<uint64_t, InflightDownRequest> down_requests;
+
+ // ICCCM says: "If the owner receives more than one SelectionRequest event
+ // with the same requestor, selection, target, and timestamp, it must respond
+ // to them in the same order in which they were received."
+ //
+ // Thus, we may need to reorder the replies when we are sending. We have a
+ // map from |ReplyReorderBufferKey| to a list of uint64_t tags. This list is
+ // the received order and we check that we don't transmit a reply
+ // out-of-order. If we do, we stick the reply in the reorder buffer and it'll
+ // get sent when its predecessor is sent.
+ std::map<ReplyReorderBufferKey, std::deque<uint64_t> > reply_order_map_;
+ std::map<uint64_t, PendingReply> pending_replies_;
+
+ // Some replies to other X clients will take multiple round trips (because of
+ // the size of the data). Sometimes we'll get an async X error from the X
+ // server if it's out of memory. For these reasons, we need to keep track of
+ // the curently uploading replies. This map is indexed by X serial number.
+ std::map<unsigned long, InflightReply> inflight_replies_;
+};
+
+XClipboard::XClipboard(void* display, Delegate* delegate)
+ : delegate_(delegate_),
+ pimpl_(new XClipboardImpl) {
+ const Window root_window = XDefaultRootWindow(display);
+ const int default_screen = XDefaultScreen(display);
+ Visual *const default_visual = XDefaultVisual(display, default_screen);
+
+ pimpl_->current_tag = 0;
+ pimpl_->display = reinterpret_cast<Display*>(display);
+
+ pimpl_->window = XCreateWindow(
+ display, root_window, 0, 0 /* x, y */, 1, 1 /* width, height */,
+ 0 /* border width */, 0 /* depth */, InputOnly, default_visual,
+ 0 /* value mask */, NULL /* values */);
+ CHECK(pimpl_->window);
+
+ XSelectInput(display, pimpl_->window, PropertyChangeMask);
+}
+
+XClipboard::~XClipboard() {
+ if (pimpl_->up_requests.size() ||
+ pimpl_->down_requests.size()) {
+ LOG(ERROR) << "XClipboard deleted with outstanding requests.";
+ }
+
+ delete pimpl_;
+}
+
+static Atom TypeToAtom(Display* display, XClipboard::Type clipboard_type) {
+ switch(clipboard_type) {
+ case CLIPBOARD_PRIMARY:
+ return XA_PRIMARY;
+ case CLIPBOARD_SECONDARY:
+ return XA_SECONDARY;
+ case CLIPBOARD_CLIPBOARD:
+ return XInternAtom(display, "CLIPBOARD");
+ default:
+ CHECK(false) << "Unknown clipboard type: " << clipboard_type;
+ }
+}
+
+bool XClipboard::AssertOwnership(Type clipboard_type) {
+ const Atom clipboard_atom = TypeToAtom(pimpl_->display, clipboard_type);
+ XSetSelectionOwner(pimpl_->display, clipboard_atom, pimpl_->window,
+ CurrentAtom);
+ if (XGetSelectionOwner(pimpl_->display, clipboard_atom) != pimpl_->window)
+ return false;
+
+ return true;
+}
+
+static void SendReplyWithFailure(Display* dpy,
+ const InflightDownRequest& req) {
+ XEvent reply;
+ reply.xselection.type = SelectionNotify;
+ reply.xselection.display = dpy;
+ reply.xselection.requestor = req.requestor;
+ reply.xselection.selection = req.selection;
+ reply.xselection.target = req.target;
+ reply.xselection.time = req.time;
+ reply.xselection.property = None;
+ XSendEvent(dpy, req.requestor, 0 /* propagate */, 0 /* event mask */, &reply);
+}
+
+static void SendReplyWithContents(Display* dpy,
+ const InflightDownRequest& req,
+ const uint8_t* data, size_t length) {
+ // XMaxRequestSize returns the maximum request size in 4-byte words.
+ const size_t max_request_size = XMaxRequestSize(dpy);
+ // No matter what the X server requests, we won't transfer in chunks larger
+ // than this.
+ static const size_t kMaxUploadSize = 1 * 1024 * 1024;
+
+ // If either of the two bits are set, we'll overflow when shifting.
+ if (max_request_size >> (sizeof(max_request_size) * 8) - 2) {
+ max_request_size = LONG_MAX;
+ } else {
+ max_request_size <<= 2;
+ }
+
+ if (max_request_size > kMaxUploadSize)
+ max_request_size = kMaxUploadSize;
+
+ if (length < kMaxUploadSize) {
+ // We'll try to do the whole thing in one go
+ XChangeProperty(dpy, req.requestor, req.property, req.target,
+ 8 /* 8-bit words */, PropModeReplace,
+ data, length);
+
+ // At this point we need to wait to either get an event that the property
+ // was set, or an error. Since the errors are async, we need to get the
+ // next serial number (before calling XChangeProperty) with
+ // |XNextRequest(dpy)| and, in the error handler, fallback to INCR with
+ // smaller chunks (maybe).
+ } else {
+ // INCR protocol
+ }
+
+ XEvent reply;
+ reply.xselection.type = SelectionNotify;
+ reply.xselection.display = dpy;
+ reply.xselection.requestor = req.requestor;
+ reply.xselection.selection = req.selection;
+ reply.xselection.target = req.target;
+ reply.xselection.time = req.time;
+ reply.xselection.property = None;
+ XSendEvent(dpy, req.requestor, 0 /* propagate */, 0 /* event mask */, &reply);
+}
+
+void XClipboard::PumpReorderBuffer(std::deque<uint64_t> *reply_order) {
+ for (;;) {
+ reply_order->pop_front();
+ if (reply_order->empty()) {
+ pimpl_->reply_order_map_.erase(j);
+ break;
+ } else {
+ // A pending reply in the reorder buffer might have been blocked on
+ // sending this reply.
+ const std::map<uint64_t, PendingReply>::iterator k =
+ pimpl_->pending_replies_.find(reply_order->front());
+ if (k == pimpl_->pending_replies_.end())
+ break;
+
+ if (k->second.data == NULL) {
+ SendReplyWithFailure(pimpl_->display, k->second.downreq);
+ } else {
+ SendReplyWithContents(pimpl_->display, k->second.downreq,
+ k->second.data, k->second.length);
+ }
+
+ pimpl_->pending_replies_.erase(k);
+ }
+ }
+}
+
+void XClipboard::ReplyWithFailure(uint64_t tag) {
+ const std::map<uint64_t, InflightDownRequest>::const_iterator i =
+ pimpl_->down_requests.find(tag);
+
+ if (i == pimpl_->down_requests.end())
+ return;
+
+ const ReplyReorderBufferKey reorder_key(i->second);
+ const std::map<ReplyReorderBufferKey, std::deque<uint64_t> >::iterator j =
+ pimpl_->reply_order_map_.find(reorder_key);
+ DCHECK(j != reply_order_map_.end());
+ DCHECK_GT(j->second.size(), 0u);
+
+ if (j->second.front() != tag) {
+ // This reply is out of order. Enqueue it.
+ PendingReply pending(i->second);
+ pimpl_->pending_replies_[tag] = pending;
+ pimpl_->down_requests.erase(i);
+ return;
+ }
+
+ SendReplyWithFailure(pimpl_->display, i->second);
+ pimpl_->down_requests.erase(i);
+ PumpReorderBuffer();
+}
+
+void XClipboard::ReplyWithContents(uint64_t tag, const uint8_t* data,
+ size_t length) {
+}
« no previous file with comments | « chrome/browser/gtk/xclipboard.h ('k') | chrome/browser/renderer_host/resource_message_filter_gtk.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698