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

Side by Side Diff: chrome/browser/gtk/xclipboard.cc

Issue 100145: WIP: X clipboard stuff (Closed)
Patch Set: Created 11 years, 7 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Have the ICCCM in hand when reading this code:
2 // http://tronche.com/gui/x/icccm/sec-2.html#s-2
3
4 struct InflightDownRequest {
5 Window requestor; // requesting window
6 Atom selection; // clipboard requested
7 Atom target; // type of the data requested
8 Atom property; // target property name on |requestor|
9 Time time; // request timestamp
10 };
11
12 struct ReplyReorderBufferKey {
13 Window requestor;
14 Atom selection;
15 Atom target;
16 Time time;
17
18 ReplyReorderBufferKey(const InflightDownRequest& downreq)
19 : requestor(downreq.requestor),
20 selection(downreq.selection),
21 target(downreq.target),
22 time(downreq.time) {
23 }
24
25 bool operator==(const ReplyReorderBufferKey& other) {
26 return other.requestor == requestor &&
27 other.selection == selection &&
28 other.target == target &&
29 other.time == time;
30 }
31 };
32
33 struct PendingReply {
34 explicit PendingReply(const InflightDownRequest& in_downreq)
35 : downreq(in_downreq),
36 data(NULL),
37 length(0) {
38 }
39
40 PendingReply(const InflightDownRequest& in_downreq, const uint8_t* data,
41 size_t in_length)
42 : downreq(in_downreq),
43 data(in_data),
44 length(in_length) {
45 }
46
47 InflightDownRequest downreq;
48 const uint8_t* data; // if NULL for |ReplyWithFailure|
49 size_t length;
50 };
51
52 struct XClipboardImpl {
53 Display* display;
54 Window window;
55 uint64_t current_tag;
56
57 std::map<uint64_t, InflightUpRequest> up_requests;
58 std::map<uint64_t, InflightDownRequest> down_requests;
59
60 // ICCCM says: "If the owner receives more than one SelectionRequest event
61 // with the same requestor, selection, target, and timestamp, it must respond
62 // to them in the same order in which they were received."
63 //
64 // Thus, we may need to reorder the replies when we are sending. We have a
65 // map from |ReplyReorderBufferKey| to a list of uint64_t tags. This list is
66 // the received order and we check that we don't transmit a reply
67 // out-of-order. If we do, we stick the reply in the reorder buffer and it'll
68 // get sent when its predecessor is sent.
69 std::map<ReplyReorderBufferKey, std::deque<uint64_t> > reply_order_map_;
70 std::map<uint64_t, PendingReply> pending_replies_;
71
72 // Some replies to other X clients will take multiple round trips (because of
73 // the size of the data). Sometimes we'll get an async X error from the X
74 // server if it's out of memory. For these reasons, we need to keep track of
75 // the curently uploading replies. This map is indexed by X serial number.
76 std::map<unsigned long, InflightReply> inflight_replies_;
77 };
78
79 XClipboard::XClipboard(void* display, Delegate* delegate)
80 : delegate_(delegate_),
81 pimpl_(new XClipboardImpl) {
82 const Window root_window = XDefaultRootWindow(display);
83 const int default_screen = XDefaultScreen(display);
84 Visual *const default_visual = XDefaultVisual(display, default_screen);
85
86 pimpl_->current_tag = 0;
87 pimpl_->display = reinterpret_cast<Display*>(display);
88
89 pimpl_->window = XCreateWindow(
90 display, root_window, 0, 0 /* x, y */, 1, 1 /* width, height */,
91 0 /* border width */, 0 /* depth */, InputOnly, default_visual,
92 0 /* value mask */, NULL /* values */);
93 CHECK(pimpl_->window);
94
95 XSelectInput(display, pimpl_->window, PropertyChangeMask);
96 }
97
98 XClipboard::~XClipboard() {
99 if (pimpl_->up_requests.size() ||
100 pimpl_->down_requests.size()) {
101 LOG(ERROR) << "XClipboard deleted with outstanding requests.";
102 }
103
104 delete pimpl_;
105 }
106
107 static Atom TypeToAtom(Display* display, XClipboard::Type clipboard_type) {
108 switch(clipboard_type) {
109 case CLIPBOARD_PRIMARY:
110 return XA_PRIMARY;
111 case CLIPBOARD_SECONDARY:
112 return XA_SECONDARY;
113 case CLIPBOARD_CLIPBOARD:
114 return XInternAtom(display, "CLIPBOARD");
115 default:
116 CHECK(false) << "Unknown clipboard type: " << clipboard_type;
117 }
118 }
119
120 bool XClipboard::AssertOwnership(Type clipboard_type) {
121 const Atom clipboard_atom = TypeToAtom(pimpl_->display, clipboard_type);
122 XSetSelectionOwner(pimpl_->display, clipboard_atom, pimpl_->window,
123 CurrentAtom);
124 if (XGetSelectionOwner(pimpl_->display, clipboard_atom) != pimpl_->window)
125 return false;
126
127 return true;
128 }
129
130 static void SendReplyWithFailure(Display* dpy,
131 const InflightDownRequest& req) {
132 XEvent reply;
133 reply.xselection.type = SelectionNotify;
134 reply.xselection.display = dpy;
135 reply.xselection.requestor = req.requestor;
136 reply.xselection.selection = req.selection;
137 reply.xselection.target = req.target;
138 reply.xselection.time = req.time;
139 reply.xselection.property = None;
140 XSendEvent(dpy, req.requestor, 0 /* propagate */, 0 /* event mask */, &reply);
141 }
142
143 static void SendReplyWithContents(Display* dpy,
144 const InflightDownRequest& req,
145 const uint8_t* data, size_t length) {
146 // XMaxRequestSize returns the maximum request size in 4-byte words.
147 const size_t max_request_size = XMaxRequestSize(dpy);
148 // No matter what the X server requests, we won't transfer in chunks larger
149 // than this.
150 static const size_t kMaxUploadSize = 1 * 1024 * 1024;
151
152 // If either of the two bits are set, we'll overflow when shifting.
153 if (max_request_size >> (sizeof(max_request_size) * 8) - 2) {
154 max_request_size = LONG_MAX;
155 } else {
156 max_request_size <<= 2;
157 }
158
159 if (max_request_size > kMaxUploadSize)
160 max_request_size = kMaxUploadSize;
161
162 if (length < kMaxUploadSize) {
163 // We'll try to do the whole thing in one go
164 XChangeProperty(dpy, req.requestor, req.property, req.target,
165 8 /* 8-bit words */, PropModeReplace,
166 data, length);
167
168 // At this point we need to wait to either get an event that the property
169 // was set, or an error. Since the errors are async, we need to get the
170 // next serial number (before calling XChangeProperty) with
171 // |XNextRequest(dpy)| and, in the error handler, fallback to INCR with
172 // smaller chunks (maybe).
173 } else {
174 // INCR protocol
175 }
176
177 XEvent reply;
178 reply.xselection.type = SelectionNotify;
179 reply.xselection.display = dpy;
180 reply.xselection.requestor = req.requestor;
181 reply.xselection.selection = req.selection;
182 reply.xselection.target = req.target;
183 reply.xselection.time = req.time;
184 reply.xselection.property = None;
185 XSendEvent(dpy, req.requestor, 0 /* propagate */, 0 /* event mask */, &reply);
186 }
187
188 void XClipboard::PumpReorderBuffer(std::deque<uint64_t> *reply_order) {
189 for (;;) {
190 reply_order->pop_front();
191 if (reply_order->empty()) {
192 pimpl_->reply_order_map_.erase(j);
193 break;
194 } else {
195 // A pending reply in the reorder buffer might have been blocked on
196 // sending this reply.
197 const std::map<uint64_t, PendingReply>::iterator k =
198 pimpl_->pending_replies_.find(reply_order->front());
199 if (k == pimpl_->pending_replies_.end())
200 break;
201
202 if (k->second.data == NULL) {
203 SendReplyWithFailure(pimpl_->display, k->second.downreq);
204 } else {
205 SendReplyWithContents(pimpl_->display, k->second.downreq,
206 k->second.data, k->second.length);
207 }
208
209 pimpl_->pending_replies_.erase(k);
210 }
211 }
212 }
213
214 void XClipboard::ReplyWithFailure(uint64_t tag) {
215 const std::map<uint64_t, InflightDownRequest>::const_iterator i =
216 pimpl_->down_requests.find(tag);
217
218 if (i == pimpl_->down_requests.end())
219 return;
220
221 const ReplyReorderBufferKey reorder_key(i->second);
222 const std::map<ReplyReorderBufferKey, std::deque<uint64_t> >::iterator j =
223 pimpl_->reply_order_map_.find(reorder_key);
224 DCHECK(j != reply_order_map_.end());
225 DCHECK_GT(j->second.size(), 0u);
226
227 if (j->second.front() != tag) {
228 // This reply is out of order. Enqueue it.
229 PendingReply pending(i->second);
230 pimpl_->pending_replies_[tag] = pending;
231 pimpl_->down_requests.erase(i);
232 return;
233 }
234
235 SendReplyWithFailure(pimpl_->display, i->second);
236 pimpl_->down_requests.erase(i);
237 PumpReorderBuffer();
238 }
239
240 void XClipboard::ReplyWithContents(uint64_t tag, const uint8_t* data,
241 size_t length) {
242 }
OLDNEW
« 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