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

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

Powered by Google App Engine
This is Rietveld 408576698