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

Side by Side Diff: ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc

Issue 14189002: linux_aura: Implement dropping in chrome. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: sky nits Created 7 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h"
6
7 #include <X11/Xatom.h>
8
9 #include "base/event_types.h"
10 #include "base/message_loop.h"
11 #include "ui/aura/client/drag_drop_client.h"
12 #include "ui/aura/client/drag_drop_delegate.h"
13 #include "ui/aura/root_window.h"
14 #include "ui/aura/window.h"
15 #include "ui/base/dragdrop/drag_drop_types.h"
16 #include "ui/base/dragdrop/os_exchange_data.h"
17 #include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h"
18 #include "ui/base/events/event.h"
19 #include "ui/base/x/x11_util.h"
20 #include "ui/views/widget/desktop_aura/desktop_root_window_host_x11.h"
21
22 using aura::client::DragDropDelegate;
23 using ui::OSExchangeData;
24
25 namespace {
26
27 const char* kAtomsToCache[] = {
28 "XdndActionAsk",
29 "XdndActionCopy",
30 "XdndActionLink",
31 "XdndActionList",
32 "XdndActionMove",
33 "XdndActionPrivate",
34 "XdndAware",
35 "XdndDrop",
36 "XdndEnter",
37 "XdndFinished",
38 "XdndLeave",
39 "XdndPosition",
40 "XdndProxy", // Proxy windows?
41 "XdndSelection",
42 "XdndStatus",
43 "XdndTypeList",
44 NULL
45 };
46
47 } // namespace
48
49 namespace views {
50
51 class DesktopDragDropClientAuraX11::X11DragContext
52 : public MessageLoop::Dispatcher {
53 public:
54 X11DragContext(ui::X11AtomCache* atom_cache,
55 const XClientMessageEvent& event);
56 ~X11DragContext();
Daniel Erat 2013/04/13 02:15:00 virtual
57
58 const std::vector<Atom>& targets() { return targets_; }
59
60 // Reads the "XdndActionList" property from |source_window| and copies it
61 // into |actions|.
62 void ReadActions();
63
64 // Creates a ui::DragDropTypes::DragOperation representation of the current
65 // action list.
66 int GetDragOperation() const;
67
68 private:
69 // Overridden from MessageLoop::Dispatcher:
70 virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE;
71
72 // The atom cache owned by our parent.
73 ui::X11AtomCache* atom_cache_;
74
75 // The XID of the window that's initiated the drag.
76 unsigned long source_window_;
77
78 // targets.
79 std::vector<Atom> targets_;
80
81 // supplied actions
82 std::vector<Atom> actions_;
83 };
Daniel Erat 2013/04/13 02:15:00 DISALLOW_COPY_AND_ASSIGN (unless this needs to be
84
85 DesktopDragDropClientAuraX11::X11DragContext::X11DragContext(
86 ui::X11AtomCache* atom_cache,
87 const XClientMessageEvent& event)
88 : atom_cache_(atom_cache),
89 source_window_(event.data.l[0]) {
90 bool get_types = ((event.data.l[1] & 1) != 0);
91
92 if (get_types) {
93 if (!ui::GetAtomArrayProperty(source_window_,
94 "XdndTypeList",
95 &targets_)) {
96 return;
97 }
98 } else {
99 // data.l[2,3,4] contain the first three types. Unused slots can be None.
100 for (int i = 0; i < 3; ++i) {
101 if (event.data.l[2+i] != None) {
102 targets_.push_back(event.data.l[2+i]);
103 }
104 }
105 }
106
107 // TODO(erg): If this window is part of our process, don't listen through the
108 // MessagePump, but instead listen to the DesktopDragDropClientAuraX11
109 // associated with that window.
110 base::MessagePumpAuraX11::Current()->AddDispatcherForWindow(
111 this, source_window_);
112 XSelectInput(base::MessagePumpAuraX11::GetDefaultXDisplay(),
113 source_window_, PropertyChangeMask);
114
115 // We must perform a full sync here because we could be racing
116 // |source_window_|.
117 XSync(base::MessagePumpAuraX11::GetDefaultXDisplay(), False);
118
119 ReadActions();
120 }
121
122 DesktopDragDropClientAuraX11::X11DragContext::~X11DragContext() {
123 // Unsubscribe to message events.
124 base::MessagePumpAuraX11::Current()->RemoveDispatcherForWindow(
125 source_window_);
126 }
127
128 void DesktopDragDropClientAuraX11::X11DragContext::ReadActions() {
129 std::vector<Atom> atom_array;
130
131 // TODO(erg): The GTK+ code has a fast path that short circuits talking over
132 // X11 for local windows. When we become a drop source, we should have a
133 // similar fast path.
134
135 if (!ui::GetAtomArrayProperty(source_window_,
136 "XdndActionList",
137 &atom_array)) {
138 actions_.clear();
139 } else {
140 actions_.swap(atom_array);
141 }
142 }
143
144 int DesktopDragDropClientAuraX11::X11DragContext::GetDragOperation() const {
145 int drag_operation = ui::DragDropTypes::DRAG_NONE;
146 for (std::vector<Atom>::const_iterator it = actions_.begin();
147 it != actions_.end(); ++it) {
148 if (*it == atom_cache_->GetAtom("XdndActionCopy"))
149 drag_operation |= ui::DragDropTypes::DRAG_COPY;
150 else if (*it == atom_cache_->GetAtom("XdndActionMove"))
151 drag_operation |= ui::DragDropTypes::DRAG_MOVE;
152 else if (*it == atom_cache_->GetAtom("XdndActionLink"))
153 drag_operation |= ui::DragDropTypes::DRAG_LINK;
154 }
155
156 return drag_operation;
157 }
158
159 bool DesktopDragDropClientAuraX11::X11DragContext::Dispatch(
160 const base::NativeEvent& event) {
161 if (event->type == PropertyNotify &&
162 event->xproperty.atom == atom_cache_->GetAtom("XdndActionList")) {
163 ReadActions();
164 }
165 return true;
166 }
167
168 ///////////////////////////////////////////////////////////////////////////////
169
170 DesktopDragDropClientAuraX11::DesktopDragDropClientAuraX11(
171 views::DesktopRootWindowHostX11* root_window_host,
172 aura::RootWindow* root_window,
173 Display* xdisplay,
174 ::Window xwindow)
175 : root_window_host_(root_window_host),
176 root_window_(root_window),
177 xdisplay_(xdisplay),
178 xwindow_(xwindow),
179 atom_cache_(xdisplay_, kAtomsToCache),
180 target_window_(NULL),
181 drag_drop_in_progress_(false),
182 drag_operation_(0) {
183 // Mark that we are aware of drag and drop concepts.
184 unsigned long xdnd_version = 5;
185 XChangeProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndAware"),
186 XA_ATOM, 32, PropModeReplace,
187 reinterpret_cast<unsigned char*>(&xdnd_version), 1);
188 }
189
190 DesktopDragDropClientAuraX11::~DesktopDragDropClientAuraX11() {
191 }
192
193 void DesktopDragDropClientAuraX11::OnXdndEnter(
194 const XClientMessageEvent& event) {
195 DVLOG(1) << "XdndEnter";
196
197 int version = (event.data.l[1] & 0xff000000) >> 24;
198 if (version < 3) {
199 LOG(ERROR) << "Received old XdndEnter message.";
200 return;
201 }
202
203 // Make sure that we've run ~X11DragContext() before creating another one.
204 current_context_.reset();
205 current_context_.reset(new X11DragContext(&atom_cache_, event));
206
207 // In the Windows implementation, we immediately call DesktopDropTargetWin::
208 // Translate(). Here, we wait until we receive an XdndPosition message
209 // because the enter message doesn't convey any positioning
210 // information.
211 }
212
213 void DesktopDragDropClientAuraX11::OnXdndLeave(
214 const XClientMessageEvent& event) {
215 NotifyDragLeave();
216 current_context_.reset();
217 }
218
219 void DesktopDragDropClientAuraX11::OnXdndPosition(
220 const XClientMessageEvent& event) {
221 DVLOG(1) << "XdndPosition";
222
223 unsigned long source_window = event.data.l[0];
224 int x_root_window = event.data.l[2] >> 16;
225 int y_root_window = event.data.l[2] & 0xffff;
226
227 if (!current_context_.get()) {
228 NOTREACHED();
229 return;
230 }
231
232 int drag_operation = ui::DragDropTypes::DRAG_NONE;
233 scoped_ptr<ui::OSExchangeData> data;
234 scoped_ptr<ui::DropTargetEvent> drop_target_event;
235 DragDropDelegate* delegate;
236 DragTranslate(gfx::Point(x_root_window, y_root_window),
237 &data, &drop_target_event, &delegate);
238 if (delegate)
239 drag_operation = delegate->OnDragUpdated(*drop_target_event);
240
241 // Sends an XdndStatus message back to the source_window. l[2,3]
242 // theoretically represent an area in the window where the current action is
243 // the same as what we're returning, but I can't find any implementation that
244 // actually making use of this. A client can return (0, 0) and/or set the
245 // first bit of l[1] to disable the feature, and it appears that gtk neither
246 // sets this nor respects it if set.
247 XEvent xev;
248 xev.xclient.type = ClientMessage;
249 xev.xclient.message_type = atom_cache_.GetAtom("XdndStatus");
250 xev.xclient.format = 32;
251 xev.xclient.window = source_window;
252 xev.xclient.data.l[0] = xwindow_;
253 xev.xclient.data.l[1] = (drag_operation != 0) ? (2 | 1) : 0;
254 xev.xclient.data.l[2] = 0;
255 xev.xclient.data.l[3] = 0;
256 xev.xclient.data.l[4] = DragOperationToAtom(drag_operation);
257
258 SendXClientEvent(source_window, &xev);
259 }
260
261 void DesktopDragDropClientAuraX11::OnXdndStatus(
262 const XClientMessageEvent& event) {
263 DVLOG(1) << "XdndStatus";
264 }
265
266 void DesktopDragDropClientAuraX11::OnXdndFinished(
267 const XClientMessageEvent& event) {
268 DVLOG(1) << "XdndFinished";
269 }
270
271 void DesktopDragDropClientAuraX11::OnXdndDrop(
272 const XClientMessageEvent& event) {
273 DVLOG(1) << "XdndDrop";
274
275 unsigned long source_window = event.data.l[0];
276
277 int drag_operation = ui::DragDropTypes::DRAG_NONE;
278 if (target_window_) {
279 aura::client::DragDropDelegate* delegate =
280 aura::client::GetDragDropDelegate(target_window_);
281 if (delegate) {
282 ui::OSExchangeData data(new ui::OSExchangeDataProviderAuraX11(
283 root_window_host_, xwindow_, current_context_->targets()));
284 ui::DropTargetEvent event(data,
285 target_window_location_,
286 target_window_root_location_,
287 current_context_->GetDragOperation());
288 drag_operation = delegate->OnPerformDrop(event);
289 }
290
291 target_window_->RemoveObserver(this);
292 target_window_ = NULL;
293 }
294
295 XEvent xev;
296 xev.xclient.type = ClientMessage;
297 xev.xclient.message_type = atom_cache_.GetAtom("XdndFinished");
298 xev.xclient.format = 32;
299 xev.xclient.window = source_window;
300 xev.xclient.data.l[0] = xwindow_;
301 xev.xclient.data.l[1] = (drag_operation != 0) ? 1 : 0;
302 xev.xclient.data.l[2] = DragOperationToAtom(drag_operation);
303
304 SendXClientEvent(source_window, &xev);
305 }
306
307 int DesktopDragDropClientAuraX11::StartDragAndDrop(
308 const ui::OSExchangeData& data,
309 aura::RootWindow* root_window,
310 aura::Window* source_window,
311 const gfx::Point& root_location,
312 int operation,
313 ui::DragDropTypes::DragEventSource source) {
314 NOTIMPLEMENTED();
315
316 // TODO(erg): Once this is implemented, make sure to reenable the
317 // NativeTextfieldViewsTest_DragAndDrop* tests.
318
319 return ui::DragDropTypes::DRAG_NONE;
320 }
321
322 void DesktopDragDropClientAuraX11::DragUpdate(aura::Window* target,
323 const ui::LocatedEvent& event) {
324 NOTIMPLEMENTED();
325 }
326
327 void DesktopDragDropClientAuraX11::Drop(aura::Window* target,
328 const ui::LocatedEvent& event) {
329 NOTIMPLEMENTED();
330 }
331
332 void DesktopDragDropClientAuraX11::DragCancel() {
333 NOTIMPLEMENTED();
334 }
335
336 bool DesktopDragDropClientAuraX11::IsDragDropInProgress() {
337 return drag_drop_in_progress_;
338 }
339
340 void DesktopDragDropClientAuraX11::OnWindowDestroyed(aura::Window* window) {
341 DCHECK(window == target_window_);
Daniel Erat 2013/04/13 02:15:00 DCHECK_EQ?
342 target_window_ = NULL;
343 }
344
345 void DesktopDragDropClientAuraX11::DragTranslate(
346 const gfx::Point& root_window_location,
347 scoped_ptr<ui::OSExchangeData>* data,
348 scoped_ptr<ui::DropTargetEvent>* event,
349 aura::client::DragDropDelegate** delegate) {
350 gfx::Point root_location = root_window_location;
351 root_window_->ConvertPointFromNativeScreen(&root_location);
352 aura::Window* target_window =
353 root_window_->GetEventHandlerForPoint(root_location);
354 bool target_window_changed = false;
355 if (target_window != target_window_) {
356 if (target_window_)
357 NotifyDragLeave();
358 target_window_ = target_window;
359 if (target_window_)
360 target_window_->AddObserver(this);
361 target_window_changed = true;
362 }
363 *delegate = NULL;
364 if (!target_window_)
365 return;
366 *delegate = aura::client::GetDragDropDelegate(target_window_);
367 if (!*delegate)
368 return;
369
370 data->reset(new OSExchangeData(new ui::OSExchangeDataProviderAuraX11(
371 root_window_host_, xwindow_, current_context_->targets())));
372 gfx::Point location = root_location;
373 aura::Window::ConvertPointToTarget(root_window_, target_window_, &location);
374
375 target_window_location_ = location;
376 target_window_root_location_ = root_location;
377
378 event->reset(new ui::DropTargetEvent(
379 *(data->get()),
380 location,
381 root_location,
382 current_context_->GetDragOperation()));
383 if (target_window_changed)
384 (*delegate)->OnDragEntered(*event->get());
385 }
386
387 void DesktopDragDropClientAuraX11::NotifyDragLeave() {
388 if (!target_window_)
389 return;
390 DragDropDelegate* delegate =
391 aura::client::GetDragDropDelegate(target_window_);
392 if (delegate)
393 delegate->OnDragExited();
394 target_window_->RemoveObserver(this);
395 target_window_ = NULL;
396 }
397
398 unsigned long DesktopDragDropClientAuraX11::DragOperationToAtom(
399 int drag_operation) {
400 if (drag_operation & ui::DragDropTypes::DRAG_COPY)
401 return atom_cache_.GetAtom("XdndActionCopy");
Daniel Erat 2013/04/13 02:15:00 mind moving these to constants? they look like th
402 if (drag_operation & ui::DragDropTypes::DRAG_MOVE)
403 return atom_cache_.GetAtom("XdndActionMove");
404 if (drag_operation & ui::DragDropTypes::DRAG_LINK)
405 return atom_cache_.GetAtom("XdndActionLink");
406
407 return None;
408 }
409
410 void DesktopDragDropClientAuraX11::SendXClientEvent(unsigned long xid,
411 XEvent* xev) {
412 DCHECK_EQ(ClientMessage, xev->type);
413
414 // TODO(erg): When I get drag receiving working, shortcut messages to the X
415 // server and call the receivers DesktopDragDropClientAuraX11 instance
416 // instead.
417 //
418 // I don't understand why the GTK+ code is doing what it's doing here. It
419 // goes out of its way to send the XEvent so that it receives a callback on
420 // success or failure, and when it fails, it then sends an internal GdkEvent
421 // about the failed drag. (And sending this message doesn't appear to go
422 // through normal xlib machinery, but instead passes through the low level
423 // xProto (the x11 wire format) that I don't understand.
424 //
425 // I'm unsure if I have to jump through those hoops, or if XSendEvent is
426 // sufficient.
427
428 XSendEvent(xdisplay_, xid, False, 0, xev);
429 }
430
431 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698