OLD | NEW |
---|---|
(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 | |
OLD | NEW |