OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h" | 5 #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h" |
6 | 6 |
7 #include <X11/Xatom.h> | 7 #include <X11/Xatom.h> |
8 | 8 |
9 #include "base/event_types.h" | 9 #include "base/event_types.h" |
10 #include "base/message_loop.h" | 10 #include "base/message_loop.h" |
11 #include "ui/aura/client/drag_drop_client.h" | 11 #include "ui/aura/client/drag_drop_client.h" |
12 #include "ui/aura/client/drag_drop_delegate.h" | 12 #include "ui/aura/client/drag_drop_delegate.h" |
13 #include "ui/aura/root_window.h" | 13 #include "ui/aura/root_window.h" |
14 #include "ui/aura/window.h" | 14 #include "ui/aura/window.h" |
15 #include "ui/base/dragdrop/drag_drop_types.h" | 15 #include "ui/base/dragdrop/drag_drop_types.h" |
16 #include "ui/base/dragdrop/os_exchange_data.h" | 16 #include "ui/base/dragdrop/os_exchange_data.h" |
17 #include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h" | 17 #include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h" |
18 #include "ui/base/events/event.h" | 18 #include "ui/base/events/event.h" |
19 #include "ui/base/x/selection_utils.h" | |
19 #include "ui/base/x/x11_util.h" | 20 #include "ui/base/x/x11_util.h" |
20 #include "ui/views/widget/desktop_aura/desktop_root_window_host_x11.h" | 21 #include "ui/views/widget/desktop_aura/desktop_root_window_host_x11.h" |
21 | 22 |
22 using aura::client::DragDropDelegate; | 23 using aura::client::DragDropDelegate; |
23 using ui::OSExchangeData; | 24 using ui::OSExchangeData; |
24 | 25 |
25 namespace { | 26 namespace { |
26 | 27 |
28 const int kMinXdndVersion = 5; | |
29 | |
27 const char kXdndActionCopy[] = "XdndActionCopy"; | 30 const char kXdndActionCopy[] = "XdndActionCopy"; |
28 const char kXdndActionMove[] = "XdndActionMove"; | 31 const char kXdndActionMove[] = "XdndActionMove"; |
29 const char kXdndActionLink[] = "XdndActionLink"; | 32 const char kXdndActionLink[] = "XdndActionLink"; |
30 | 33 |
34 const char kChromiumDragReciever[] = "CHROMIUM_DRAG_RECEIVER"; | |
Daniel Erat
2013/06/17 22:09:57
nit: prefix vendor-specific atoms with underscores
| |
35 const char kXdndSelection[] = "XdndSelection"; | |
36 | |
31 const char* kAtomsToCache[] = { | 37 const char* kAtomsToCache[] = { |
38 kChromiumDragReciever, | |
32 "XdndActionAsk", | 39 "XdndActionAsk", |
33 kXdndActionCopy, | 40 kXdndActionCopy, |
34 kXdndActionLink, | 41 kXdndActionLink, |
35 "XdndActionList", | 42 "XdndActionList", |
36 kXdndActionMove, | 43 kXdndActionMove, |
37 "XdndActionPrivate", | 44 "XdndActionPrivate", |
38 "XdndAware", | 45 "XdndAware", |
39 "XdndDrop", | 46 "XdndDrop", |
40 "XdndEnter", | 47 "XdndEnter", |
41 "XdndFinished", | 48 "XdndFinished", |
42 "XdndLeave", | 49 "XdndLeave", |
43 "XdndPosition", | 50 "XdndPosition", |
44 "XdndProxy", // Proxy windows? | 51 "XdndProxy", // Proxy windows? |
45 "XdndSelection", | 52 kXdndSelection, |
46 "XdndStatus", | 53 "XdndStatus", |
47 "XdndTypeList", | 54 "XdndTypeList", |
48 NULL | 55 NULL |
49 }; | 56 }; |
50 | 57 |
58 class DragTargetWindowFinder : public ui::EnumerateWindowsDelegate { | |
Daniel Erat
2013/06/17 22:09:57
please add a comment describing what this class do
| |
59 public: | |
60 DragTargetWindowFinder(XID ignored_icon_window, | |
61 gfx::Point screen_loc) | |
62 : ignored_icon_window_(ignored_icon_window), | |
63 output_window_(None), | |
64 screen_loc_(screen_loc) { | |
65 ui::EnumerateTopLevelWindows(this); | |
66 } | |
67 | |
68 virtual ~DragTargetWindowFinder() {} | |
69 | |
70 XID window() const { return output_window_; } | |
71 | |
72 protected: | |
73 virtual bool ShouldStopIterating(XID window) OVERRIDE { | |
74 if (window == ignored_icon_window_) | |
75 return false; | |
76 | |
77 if (!ui::IsWindowVisible(window)) | |
78 return false; | |
79 | |
80 if (!ui::WindowContainsPoint(window, screen_loc_)) | |
81 return false; | |
82 | |
83 if (ui::PropertyExists(window, "WM_STATE")) { | |
84 output_window_ = window; | |
85 return true; | |
86 } | |
87 | |
88 return false; | |
89 } | |
90 | |
91 private: | |
92 XID ignored_icon_window_; | |
93 XID output_window_; | |
94 gfx::Point screen_loc_; | |
95 | |
96 DISALLOW_COPY_AND_ASSIGN(DragTargetWindowFinder); | |
97 }; | |
98 | |
99 // Returns the topmost X11 window at |screen_point| if it is advertising that | |
100 // is supports the Xdnd protocol. Will return the window under the pointer as | |
101 // |mouse_window|. If there's a Xdnd aware window, it will be returned in | |
102 // |dest_window|. | |
103 void FindWindowFor(const gfx::Point& screen_point, | |
104 ::Window* mouse_window, ::Window* dest_window) { | |
105 DragTargetWindowFinder finder(None, screen_point); | |
106 *mouse_window = finder.window(); | |
107 | |
108 if (finder.window() == None) { | |
109 NOTIMPLEMENTED(); | |
Daniel Erat
2013/06/17 22:09:57
this NOTIMPLEMENTED() seems weird to me -- what do
Elliot Glaysher
2013/06/18 00:06:39
I wanted a nonfatal log. Deleted.
| |
110 *dest_window = None; | |
Daniel Erat
2013/06/17 22:09:57
how about clearing dest_window at the beginning of
| |
111 return; | |
112 } | |
113 | |
114 // Figure out which window we should test as XdndAware. If mouse_window has | |
115 // XdndProxy, it will set that proxy on target, and if not, |target|'s | |
116 // original value will remain. | |
117 XID target = *mouse_window; | |
118 ui::GetXIDProperty(*mouse_window, "XdndProxy", &target); | |
119 | |
120 int version; | |
121 if (ui::GetIntProperty(target, "XdndAware", &version) && | |
122 version >= kMinXdndVersion) { | |
123 *dest_window = target; | |
124 } else { | |
125 *dest_window = None; | |
126 } | |
127 } | |
128 | |
51 } // namespace | 129 } // namespace |
52 | 130 |
53 namespace views { | 131 namespace views { |
54 | 132 |
133 std::map< ::Window, DesktopDragDropClientAuraX11*> | |
134 DesktopDragDropClientAuraX11::g_live_client_map; | |
135 | |
55 class DesktopDragDropClientAuraX11::X11DragContext : | 136 class DesktopDragDropClientAuraX11::X11DragContext : |
56 public base::MessageLoop::Dispatcher { | 137 public base::MessageLoop::Dispatcher { |
57 public: | 138 public: |
58 X11DragContext(ui::X11AtomCache* atom_cache, | 139 X11DragContext(ui::X11AtomCache* atom_cache, |
140 ::Window local_window, | |
59 const XClientMessageEvent& event); | 141 const XClientMessageEvent& event); |
60 virtual ~X11DragContext(); | 142 virtual ~X11DragContext(); |
61 | 143 |
62 const std::vector<Atom>& targets() { return targets_; } | 144 // When we receive an XdndPosition message, we need to have all the data |
145 // copied from the other window before we process the XdndPosition | |
146 // message. If we have that data already, dispatch immediately. Otherwise, | |
147 // delay dispatching until we do. | |
148 void OnStartXdndPositionMessage(DesktopDragDropClientAuraX11* client, | |
149 ::Window source_window, | |
150 const gfx::Point& screen_point); | |
151 | |
152 // Called to request the next target from the source window. This is only | |
153 // done on the first XdndPosition; after that, we cache the data offered by | |
154 // the source window. | |
155 void RequestNextTarget(); | |
156 | |
157 // Called when XSelection data has been copied to our process. | |
158 void OnSelectionNotify(const XSelectionEvent& xselection); | |
159 | |
160 // Clones the fetched targets. | |
161 ui::SelectionFormatMap* CloneFetchedTargets() { | |
162 DCHECK(fetched_targets_); | |
163 return fetched_targets_->Clone(); | |
164 } | |
63 | 165 |
64 // Reads the "XdndActionList" property from |source_window| and copies it | 166 // Reads the "XdndActionList" property from |source_window| and copies it |
65 // into |actions|. | 167 // into |actions|. |
66 void ReadActions(); | 168 void ReadActions(); |
67 | 169 |
68 // Creates a ui::DragDropTypes::DragOperation representation of the current | 170 // Creates a ui::DragDropTypes::DragOperation representation of the current |
69 // action list. | 171 // action list. |
70 int GetDragOperation() const; | 172 int GetDragOperation() const; |
71 | 173 |
72 private: | 174 private: |
73 // Overridden from MessageLoop::Dispatcher: | 175 // Overridden from MessageLoop::Dispatcher: |
74 virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE; | 176 virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE; |
75 | 177 |
76 // The atom cache owned by our parent. | 178 // The atom cache owned by our parent. |
77 ui::X11AtomCache* atom_cache_; | 179 ui::X11AtomCache* atom_cache_; |
78 | 180 |
181 // The XID of our chrome local aura window handling our events. | |
182 ::Window local_window_; | |
183 | |
79 // The XID of the window that's initiated the drag. | 184 // The XID of the window that's initiated the drag. |
80 unsigned long source_window_; | 185 unsigned long source_window_; |
81 | 186 |
82 // targets. | 187 // The client we inform once we're done with requesting data. |
83 std::vector<Atom> targets_; | 188 DesktopDragDropClientAuraX11* drag_drop_client_; |
189 | |
190 // Whether we're blocking the handling of an XdndPosition message by waiting | |
191 // for |unfetched_targets_| to be fetched. | |
192 bool waiting_to_handle_position_; | |
193 | |
194 // Where the cursor is on screen | |
Daniel Erat
2013/06/17 22:09:57
nit: add trailing period
| |
195 gfx::Point screen_point_; | |
196 | |
197 // A SelectionFormatMap of data that we have in our process. | |
198 scoped_ptr<ui::SelectionFormatMap> fetched_targets_; | |
199 | |
200 // The names of various data types offered by the other window that we | |
201 // haven't fetched and put in |fetched_targets_| yet. | |
202 std::vector<Atom> unfetched_targets_; | |
84 | 203 |
85 // supplied actions | 204 // supplied actions |
86 std::vector<Atom> actions_; | 205 std::vector<Atom> actions_; |
87 | 206 |
88 DISALLOW_COPY_AND_ASSIGN(X11DragContext); | 207 DISALLOW_COPY_AND_ASSIGN(X11DragContext); |
89 }; | 208 }; |
90 | 209 |
91 DesktopDragDropClientAuraX11::X11DragContext::X11DragContext( | 210 DesktopDragDropClientAuraX11::X11DragContext::X11DragContext( |
92 ui::X11AtomCache* atom_cache, | 211 ui::X11AtomCache* atom_cache, |
212 ::Window local_window, | |
93 const XClientMessageEvent& event) | 213 const XClientMessageEvent& event) |
94 : atom_cache_(atom_cache), | 214 : atom_cache_(atom_cache), |
95 source_window_(event.data.l[0]) { | 215 local_window_(local_window), |
216 source_window_(event.data.l[0]), | |
217 drag_drop_client_(NULL), | |
218 waiting_to_handle_position_(false) { | |
96 bool get_types = ((event.data.l[1] & 1) != 0); | 219 bool get_types = ((event.data.l[1] & 1) != 0); |
97 | 220 |
98 if (get_types) { | 221 if (get_types) { |
99 if (!ui::GetAtomArrayProperty(source_window_, | 222 if (!ui::GetAtomArrayProperty(source_window_, |
100 "XdndTypeList", | 223 "XdndTypeList", |
101 &targets_)) { | 224 &unfetched_targets_)) { |
102 return; | 225 return; |
103 } | 226 } |
104 } else { | 227 } else { |
105 // data.l[2,3,4] contain the first three types. Unused slots can be None. | 228 // data.l[2,3,4] contain the first three types. Unused slots can be None. |
106 for (int i = 0; i < 3; ++i) { | 229 for (int i = 0; i < 3; ++i) { |
107 if (event.data.l[2+i] != None) { | 230 if (event.data.l[2+i] != None) { |
108 targets_.push_back(event.data.l[2+i]); | 231 unfetched_targets_.push_back(event.data.l[2+i]); |
109 } | 232 } |
110 } | 233 } |
111 } | 234 } |
112 | 235 |
113 // TODO(erg): If this window is part of our process, don't listen through the | 236 DesktopDragDropClientAuraX11* client = |
114 // MessagePump, but instead listen to the DesktopDragDropClientAuraX11 | 237 DesktopDragDropClientAuraX11::GetForWindow(source_window_); |
115 // associated with that window. | 238 if (!client) { |
116 base::MessagePumpAuraX11::Current()->AddDispatcherForWindow( | 239 // The window doesn't have a DesktopDragDropClientAuraX11, that means its |
Daniel Erat
2013/06/17 22:09:57
nit: s/its/it's/
| |
117 this, source_window_); | 240 // created by some other process. Listen for messages on it. |
118 XSelectInput(base::MessagePumpAuraX11::GetDefaultXDisplay(), | 241 base::MessagePumpAuraX11::Current()->AddDispatcherForWindow( |
119 source_window_, PropertyChangeMask); | 242 this, source_window_); |
243 XSelectInput(base::MessagePumpAuraX11::GetDefaultXDisplay(), | |
244 source_window_, PropertyChangeMask); | |
120 | 245 |
121 // We must perform a full sync here because we could be racing | 246 // We must perform a full sync here because we could be racing |
122 // |source_window_|. | 247 // |source_window_|. |
123 XSync(base::MessagePumpAuraX11::GetDefaultXDisplay(), False); | 248 XSync(base::MessagePumpAuraX11::GetDefaultXDisplay(), False); |
249 } else { | |
250 // This drag originates from an aura window within our process. This means | |
251 // that we can shortcut the X11 server and ask the owning SelectionOwner | |
252 // for the data its offering. | |
Daniel Erat
2013/06/17 22:09:57
nit: s/its/it's/
| |
253 fetched_targets_.reset(client->CloneFormatMap()); | |
254 unfetched_targets_.clear(); | |
255 } | |
124 | 256 |
125 ReadActions(); | 257 ReadActions(); |
126 } | 258 } |
127 | 259 |
128 DesktopDragDropClientAuraX11::X11DragContext::~X11DragContext() { | 260 DesktopDragDropClientAuraX11::X11DragContext::~X11DragContext() { |
129 // Unsubscribe to message events. | 261 DesktopDragDropClientAuraX11* client = |
130 base::MessagePumpAuraX11::Current()->RemoveDispatcherForWindow( | 262 DesktopDragDropClientAuraX11::GetForWindow(source_window_); |
131 source_window_); | 263 if (!client) { |
264 // Unsubscribe to message events. | |
Daniel Erat
2013/06/17 22:09:57
nit: s/to/from/
| |
265 base::MessagePumpAuraX11::Current()->RemoveDispatcherForWindow( | |
266 source_window_); | |
267 } | |
268 } | |
269 | |
270 void DesktopDragDropClientAuraX11::X11DragContext::OnStartXdndPositionMessage( | |
271 DesktopDragDropClientAuraX11* client, | |
272 ::Window source_window, | |
273 const gfx::Point& screen_point) { | |
274 DCHECK_EQ(source_window_, source_window); | |
275 | |
276 if (!unfetched_targets_.empty()) { | |
277 // We have unfetched targets. That means we need to pause the handling of | |
278 // the position message and ask the other window for its data. | |
279 screen_point_ = screen_point; | |
280 drag_drop_client_ = client; | |
281 waiting_to_handle_position_ = true; | |
282 | |
283 // At this point, we need to start | |
Daniel Erat
2013/06/17 22:09:57
fix this comment
| |
284 fetched_targets_.reset(new ui::SelectionFormatMap); | |
285 RequestNextTarget(); | |
286 } else { | |
287 client->CompleteXdndPosition(source_window, screen_point); | |
288 } | |
289 } | |
290 | |
291 void DesktopDragDropClientAuraX11::X11DragContext::RequestNextTarget() { | |
292 ::Atom target = unfetched_targets_.back(); | |
293 unfetched_targets_.pop_back(); | |
294 | |
295 XConvertSelection(base::MessagePumpAuraX11::GetDefaultXDisplay(), | |
296 atom_cache_->GetAtom(kXdndSelection), | |
297 target, | |
298 atom_cache_->GetAtom(kChromiumDragReciever), | |
299 local_window_, | |
300 CurrentTime); | |
301 } | |
302 | |
303 void DesktopDragDropClientAuraX11::X11DragContext::OnSelectionNotify( | |
304 const XSelectionEvent& event) { | |
305 DCHECK(waiting_to_handle_position_); | |
306 DCHECK(drag_drop_client_); | |
307 | |
308 unsigned char* data = NULL; | |
309 size_t data_bytes = 0; | |
310 ::Atom type = None; | |
311 if (ui::GetRawBytesOfProperty(local_window_, event.property, | |
312 &data, &data_bytes, NULL, &type)) { | |
313 char* copied_data = new char[data_bytes]; | |
314 memcpy(copied_data, data, data_bytes); | |
315 fetched_targets_->Insert(event.target, copied_data, data_bytes); | |
316 XFree(data); | |
317 } | |
318 | |
319 if (!unfetched_targets_.empty()) { | |
320 RequestNextTarget(); | |
321 } else { | |
322 waiting_to_handle_position_ = false; | |
323 drag_drop_client_->CompleteXdndPosition(source_window_, screen_point_); | |
324 drag_drop_client_ = NULL; | |
325 } | |
132 } | 326 } |
133 | 327 |
134 void DesktopDragDropClientAuraX11::X11DragContext::ReadActions() { | 328 void DesktopDragDropClientAuraX11::X11DragContext::ReadActions() { |
135 std::vector<Atom> atom_array; | 329 DesktopDragDropClientAuraX11* client = |
136 | 330 DesktopDragDropClientAuraX11::GetForWindow(source_window_); |
137 // TODO(erg): The GTK+ code has a fast path that short circuits talking over | 331 if (!client) { |
138 // X11 for local windows. When we become a drop source, we should have a | 332 std::vector<Atom> atom_array; |
139 // similar fast path. | 333 if (!ui::GetAtomArrayProperty(source_window_, |
140 | 334 "XdndActionList", |
141 if (!ui::GetAtomArrayProperty(source_window_, | 335 &atom_array)) { |
142 "XdndActionList", | 336 actions_.clear(); |
143 &atom_array)) { | 337 } else { |
144 actions_.clear(); | 338 actions_.swap(atom_array); |
339 } | |
145 } else { | 340 } else { |
146 actions_.swap(atom_array); | 341 // We have a property notify set up for other windows in case they change |
342 // their action list. Thankfully, the views interface is static and you | |
343 // can't change the action list after you enter StartDragAndDrop(). | |
344 actions_ = client->GetOfferedDragOperations(); | |
147 } | 345 } |
148 } | 346 } |
149 | 347 |
150 int DesktopDragDropClientAuraX11::X11DragContext::GetDragOperation() const { | 348 int DesktopDragDropClientAuraX11::X11DragContext::GetDragOperation() const { |
151 int drag_operation = ui::DragDropTypes::DRAG_NONE; | 349 int drag_operation = ui::DragDropTypes::DRAG_NONE; |
152 for (std::vector<Atom>::const_iterator it = actions_.begin(); | 350 for (std::vector<Atom>::const_iterator it = actions_.begin(); |
153 it != actions_.end(); ++it) { | 351 it != actions_.end(); ++it) { |
154 if (*it == atom_cache_->GetAtom(kXdndActionCopy)) | 352 if (*it == atom_cache_->GetAtom(kXdndActionCopy)) |
155 drag_operation |= ui::DragDropTypes::DRAG_COPY; | 353 drag_operation |= ui::DragDropTypes::DRAG_COPY; |
156 else if (*it == atom_cache_->GetAtom(kXdndActionMove)) | 354 else if (*it == atom_cache_->GetAtom(kXdndActionMove)) |
(...skipping 14 matching lines...) Expand all Loading... | |
171 return true; | 369 return true; |
172 } | 370 } |
173 | 371 |
174 /////////////////////////////////////////////////////////////////////////////// | 372 /////////////////////////////////////////////////////////////////////////////// |
175 | 373 |
176 DesktopDragDropClientAuraX11::DesktopDragDropClientAuraX11( | 374 DesktopDragDropClientAuraX11::DesktopDragDropClientAuraX11( |
177 views::DesktopRootWindowHostX11* root_window_host, | 375 views::DesktopRootWindowHostX11* root_window_host, |
178 aura::RootWindow* root_window, | 376 aura::RootWindow* root_window, |
179 Display* xdisplay, | 377 Display* xdisplay, |
180 ::Window xwindow) | 378 ::Window xwindow) |
181 : root_window_host_(root_window_host), | 379 : move_loop_(this), |
380 root_window_host_(root_window_host), | |
182 root_window_(root_window), | 381 root_window_(root_window), |
183 xdisplay_(xdisplay), | 382 xdisplay_(xdisplay), |
184 xwindow_(xwindow), | 383 xwindow_(xwindow), |
185 atom_cache_(xdisplay_, kAtomsToCache), | 384 atom_cache_(xdisplay_, kAtomsToCache), |
186 target_window_(NULL), | 385 target_window_(NULL), |
386 source_provider_(NULL), | |
387 source_current_window_(None), | |
187 drag_drop_in_progress_(false), | 388 drag_drop_in_progress_(false), |
188 drag_operation_(0) { | 389 drag_operation_(0), |
390 resulting_operation_(0) { | |
391 DCHECK(g_live_client_map.find(xwindow) == g_live_client_map.end()); | |
Daniel Erat
2013/06/17 22:09:57
nit: can you use DCHECK_EQ() here?
Elliot Glaysher
2013/06/18 00:06:39
You can't use DCHECK_EQ with iterators.
| |
392 g_live_client_map.insert(std::make_pair(xwindow, this)); | |
393 | |
189 // Mark that we are aware of drag and drop concepts. | 394 // Mark that we are aware of drag and drop concepts. |
190 unsigned long xdnd_version = 5; | 395 unsigned long xdnd_version = kMinXdndVersion; |
191 XChangeProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndAware"), | 396 XChangeProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndAware"), |
192 XA_ATOM, 32, PropModeReplace, | 397 XA_ATOM, 32, PropModeReplace, |
193 reinterpret_cast<unsigned char*>(&xdnd_version), 1); | 398 reinterpret_cast<unsigned char*>(&xdnd_version), 1); |
194 } | 399 } |
195 | 400 |
196 DesktopDragDropClientAuraX11::~DesktopDragDropClientAuraX11() { | 401 DesktopDragDropClientAuraX11::~DesktopDragDropClientAuraX11() { |
402 g_live_client_map.erase(xwindow_); | |
403 } | |
404 | |
405 // static | |
406 DesktopDragDropClientAuraX11* DesktopDragDropClientAuraX11::GetForWindow( | |
407 ::Window window) { | |
408 std::map< ::Window, DesktopDragDropClientAuraX11*>::const_iterator it = | |
409 g_live_client_map.find(window); | |
410 if (it == g_live_client_map.end()) | |
411 return NULL; | |
412 return it->second; | |
197 } | 413 } |
198 | 414 |
199 void DesktopDragDropClientAuraX11::OnXdndEnter( | 415 void DesktopDragDropClientAuraX11::OnXdndEnter( |
200 const XClientMessageEvent& event) { | 416 const XClientMessageEvent& event) { |
201 DVLOG(1) << "XdndEnter"; | 417 DVLOG(1) << "XdndEnter"; |
202 | 418 |
203 int version = (event.data.l[1] & 0xff000000) >> 24; | 419 int version = (event.data.l[1] & 0xff000000) >> 24; |
204 if (version < 3) { | 420 if (version < 3) { |
205 LOG(ERROR) << "Received old XdndEnter message."; | 421 LOG(ERROR) << "Received old XdndEnter message."; |
206 return; | 422 return; |
207 } | 423 } |
208 | 424 |
209 // Make sure that we've run ~X11DragContext() before creating another one. | 425 // Make sure that we've run ~X11DragContext() before creating another one. |
210 current_context_.reset(); | 426 target_current_context_.reset(); |
211 current_context_.reset(new X11DragContext(&atom_cache_, event)); | 427 target_current_context_.reset( |
428 new X11DragContext(&atom_cache_, xwindow_, event)); | |
212 | 429 |
213 // In the Windows implementation, we immediately call DesktopDropTargetWin:: | 430 // In the Windows implementation, we immediately call DesktopDropTargetWin:: |
214 // Translate(). Here, we wait until we receive an XdndPosition message | 431 // Translate(). Here, we wait until we receive an XdndPosition message |
215 // because the enter message doesn't convey any positioning | 432 // because the enter message doesn't convey any positioning |
216 // information. | 433 // information. |
217 } | 434 } |
218 | 435 |
219 void DesktopDragDropClientAuraX11::OnXdndLeave( | 436 void DesktopDragDropClientAuraX11::OnXdndLeave( |
220 const XClientMessageEvent& event) { | 437 const XClientMessageEvent& event) { |
438 DVLOG(1) << "XdndLeave"; | |
221 NotifyDragLeave(); | 439 NotifyDragLeave(); |
222 current_context_.reset(); | 440 target_current_context_.reset(); |
223 } | 441 } |
224 | 442 |
225 void DesktopDragDropClientAuraX11::OnXdndPosition( | 443 void DesktopDragDropClientAuraX11::OnXdndPosition( |
226 const XClientMessageEvent& event) { | 444 const XClientMessageEvent& event) { |
227 DVLOG(1) << "XdndPosition"; | 445 DVLOG(1) << "XdndPosition"; |
228 | 446 |
229 unsigned long source_window = event.data.l[0]; | 447 unsigned long source_window = event.data.l[0]; |
230 int x_root_window = event.data.l[2] >> 16; | 448 int x_root_window = event.data.l[2] >> 16; |
231 int y_root_window = event.data.l[2] & 0xffff; | 449 int y_root_window = event.data.l[2] & 0xffff; |
232 | 450 |
233 if (!current_context_.get()) { | 451 if (!target_current_context_.get()) { |
234 NOTREACHED(); | 452 NOTREACHED(); |
235 return; | 453 return; |
236 } | 454 } |
237 | 455 |
238 int drag_operation = ui::DragDropTypes::DRAG_NONE; | 456 // If we already have all the data from this drag, we complete it |
239 scoped_ptr<ui::OSExchangeData> data; | 457 // immediately. |
240 scoped_ptr<ui::DropTargetEvent> drop_target_event; | 458 target_current_context_->OnStartXdndPositionMessage( |
241 DragDropDelegate* delegate; | 459 this, source_window, gfx::Point(x_root_window, y_root_window)); |
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 } | 460 } |
266 | 461 |
267 void DesktopDragDropClientAuraX11::OnXdndStatus( | 462 void DesktopDragDropClientAuraX11::OnXdndStatus( |
268 const XClientMessageEvent& event) { | 463 const XClientMessageEvent& event) { |
269 DVLOG(1) << "XdndStatus"; | 464 DVLOG(1) << "XdndStatus"; |
465 | |
466 unsigned long source_window = event.data.l[0]; | |
467 if (event.data.l[1] & 1) | |
468 negotiated_operation_[source_window] = event.data.l[4]; | |
469 | |
470 // TODO(erg): event.data.[2,3] specify a rectangle. It is a request by the | |
Daniel Erat
2013/06/17 22:09:57
is this really a TODO, or just a note? sounds like
| |
471 // other window to not send further XdndPosition messages while the cursor is | |
472 // within it. However, it is considered advisory and (at least according to | |
473 // the spec) the other side must handle further position messages within | |
474 // it. GTK+ doesn't bother with this, so neither should we. | |
475 | |
476 waiting_on_status_.erase(source_window); | |
477 | |
478 // TODO(erg): We should be using the response to try to update the cursor or | |
479 // something. | |
480 | |
481 if (ContainsKey(pending_drop_, source_window)) { | |
482 // We were waiting on the status message so we could send the XdndDrop. | |
483 SendXdndDrop(source_window); | |
484 return; | |
485 } | |
486 | |
487 NextPositionMap::iterator it = next_position_message_.find(source_window); | |
488 if (it != next_position_message_.end()) { | |
489 // We were waiting on the status message so we could send off the next | |
490 // position message we queued up. | |
491 gfx::Point p = it->second.first; | |
492 unsigned long time = it->second.second; | |
493 next_position_message_.erase(it); | |
494 | |
495 SendXdndPosition(source_window, p, time); | |
496 } | |
270 } | 497 } |
271 | 498 |
272 void DesktopDragDropClientAuraX11::OnXdndFinished( | 499 void DesktopDragDropClientAuraX11::OnXdndFinished( |
273 const XClientMessageEvent& event) { | 500 const XClientMessageEvent& event) { |
274 DVLOG(1) << "XdndFinished"; | 501 DVLOG(1) << "XdndFinished"; |
502 resulting_operation_ = AtomToDragOperation( | |
503 negotiated_operation_[event.data.l[0]]); | |
504 move_loop_.EndMoveLoop(); | |
275 } | 505 } |
276 | 506 |
277 void DesktopDragDropClientAuraX11::OnXdndDrop( | 507 void DesktopDragDropClientAuraX11::OnXdndDrop( |
278 const XClientMessageEvent& event) { | 508 const XClientMessageEvent& event) { |
279 DVLOG(1) << "XdndDrop"; | 509 DVLOG(1) << "XdndDrop"; |
280 | 510 |
281 unsigned long source_window = event.data.l[0]; | 511 unsigned long source_window = event.data.l[0]; |
282 | 512 |
283 int drag_operation = ui::DragDropTypes::DRAG_NONE; | 513 int drag_operation = ui::DragDropTypes::DRAG_NONE; |
284 if (target_window_) { | 514 if (target_window_) { |
285 aura::client::DragDropDelegate* delegate = | 515 aura::client::DragDropDelegate* delegate = |
286 aura::client::GetDragDropDelegate(target_window_); | 516 aura::client::GetDragDropDelegate(target_window_); |
287 if (delegate) { | 517 if (delegate) { |
288 ui::OSExchangeData data(new ui::OSExchangeDataProviderAuraX11( | 518 ui::OSExchangeData data(new ui::OSExchangeDataProviderAuraX11( |
289 root_window_host_, xwindow_, current_context_->targets())); | 519 xwindow_, target_current_context_->CloneFetchedTargets())); |
520 | |
290 ui::DropTargetEvent event(data, | 521 ui::DropTargetEvent event(data, |
291 target_window_location_, | 522 target_window_location_, |
292 target_window_root_location_, | 523 target_window_root_location_, |
293 current_context_->GetDragOperation()); | 524 target_current_context_->GetDragOperation()); |
294 drag_operation = delegate->OnPerformDrop(event); | 525 drag_operation = delegate->OnPerformDrop(event); |
295 } | 526 } |
296 | 527 |
297 target_window_->RemoveObserver(this); | 528 target_window_->RemoveObserver(this); |
298 target_window_ = NULL; | 529 target_window_ = NULL; |
299 } | 530 } |
300 | 531 |
301 XEvent xev; | 532 XEvent xev; |
302 xev.xclient.type = ClientMessage; | 533 xev.xclient.type = ClientMessage; |
303 xev.xclient.message_type = atom_cache_.GetAtom("XdndFinished"); | 534 xev.xclient.message_type = atom_cache_.GetAtom("XdndFinished"); |
304 xev.xclient.format = 32; | 535 xev.xclient.format = 32; |
305 xev.xclient.window = source_window; | 536 xev.xclient.window = source_window; |
306 xev.xclient.data.l[0] = xwindow_; | 537 xev.xclient.data.l[0] = xwindow_; |
307 xev.xclient.data.l[1] = (drag_operation != 0) ? 1 : 0; | 538 xev.xclient.data.l[1] = (drag_operation != 0) ? 1 : 0; |
308 xev.xclient.data.l[2] = DragOperationToAtom(drag_operation); | 539 xev.xclient.data.l[2] = DragOperationToAtom(drag_operation); |
309 | 540 |
310 SendXClientEvent(source_window, &xev); | 541 SendXClientEvent(source_window, &xev); |
311 } | 542 } |
312 | 543 |
544 void DesktopDragDropClientAuraX11::OnSelectionNotify( | |
545 const XSelectionEvent& xselection) { | |
546 if (!target_current_context_) { | |
547 NOTIMPLEMENTED(); | |
548 return; | |
549 } | |
550 | |
551 target_current_context_->OnSelectionNotify(xselection); | |
552 } | |
553 | |
313 int DesktopDragDropClientAuraX11::StartDragAndDrop( | 554 int DesktopDragDropClientAuraX11::StartDragAndDrop( |
314 const ui::OSExchangeData& data, | 555 const ui::OSExchangeData& data, |
315 aura::RootWindow* root_window, | 556 aura::RootWindow* root_window, |
316 aura::Window* source_window, | 557 aura::Window* source_window, |
317 const gfx::Point& root_location, | 558 const gfx::Point& root_location, |
318 int operation, | 559 int operation, |
319 ui::DragDropTypes::DragEventSource source) { | 560 ui::DragDropTypes::DragEventSource source) { |
561 source_current_window_ = None; | |
562 drag_drop_in_progress_ = true; | |
563 drag_operation_ = operation; | |
564 resulting_operation_ = ui::DragDropTypes::DRAG_NONE; | |
565 | |
566 const ui::OSExchangeData::Provider* provider = &data.provider(); | |
567 source_provider_ = static_cast<const ui::OSExchangeDataProviderAuraX11*>( | |
568 provider); | |
569 | |
570 source_provider_->TakeOwnershipOfSelection(); | |
571 | |
572 std::vector< ::Atom> actions = GetOfferedDragOperations(); | |
573 ui::SetAtomArrayProperty(xwindow_, "XdndActionList", "ATOM", actions); | |
574 | |
320 // Windows has a specific method, DoDragDrop(), which performs the entire | 575 // Windows has a specific method, DoDragDrop(), which performs the entire |
321 // drag. We have to emulate this, so we spin off a nested runloop which will | 576 // drag. We have to emulate this, so we spin off a nested runloop which will |
322 // track all cursor movement and reroute events to a specific handler. | 577 // track all cursor movement and reroute events to a specific handler. |
578 move_loop_.RunMoveLoop(source_window); | |
323 | 579 |
324 NOTIMPLEMENTED(); | 580 source_provider_ = NULL; |
581 drag_drop_in_progress_ = false; | |
582 drag_operation_ = 0; | |
583 XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndActionList")); | |
325 | 584 |
326 // TODO(erg): Once this is implemented, make sure to reenable the | 585 return resulting_operation_; |
327 // NativeTextfieldViewsTest_DragAndDrop* tests. | |
328 | |
329 return ui::DragDropTypes::DRAG_NONE; | |
330 } | 586 } |
331 | 587 |
332 void DesktopDragDropClientAuraX11::DragUpdate(aura::Window* target, | 588 void DesktopDragDropClientAuraX11::DragUpdate(aura::Window* target, |
333 const ui::LocatedEvent& event) { | 589 const ui::LocatedEvent& event) { |
334 NOTIMPLEMENTED(); | 590 NOTIMPLEMENTED(); |
335 } | 591 } |
336 | 592 |
337 void DesktopDragDropClientAuraX11::Drop(aura::Window* target, | 593 void DesktopDragDropClientAuraX11::Drop(aura::Window* target, |
338 const ui::LocatedEvent& event) { | 594 const ui::LocatedEvent& event) { |
339 NOTIMPLEMENTED(); | 595 NOTIMPLEMENTED(); |
340 } | 596 } |
341 | 597 |
342 void DesktopDragDropClientAuraX11::DragCancel() { | 598 void DesktopDragDropClientAuraX11::DragCancel() { |
343 NOTIMPLEMENTED(); | 599 move_loop_.EndMoveLoop(); |
344 } | 600 } |
345 | 601 |
346 bool DesktopDragDropClientAuraX11::IsDragDropInProgress() { | 602 bool DesktopDragDropClientAuraX11::IsDragDropInProgress() { |
347 return drag_drop_in_progress_; | 603 return drag_drop_in_progress_; |
348 } | 604 } |
349 | 605 |
350 void DesktopDragDropClientAuraX11::OnWindowDestroyed(aura::Window* window) { | 606 void DesktopDragDropClientAuraX11::OnWindowDestroyed(aura::Window* window) { |
351 DCHECK_EQ(target_window_, window); | 607 DCHECK_EQ(target_window_, window); |
352 target_window_ = NULL; | 608 target_window_ = NULL; |
353 } | 609 } |
354 | 610 |
611 void DesktopDragDropClientAuraX11::OnMouseMovement(XMotionEvent* event) { | |
612 gfx::Point screen_point(event->x_root, event->y_root); | |
613 | |
614 // Find the current window the cursor is over. | |
615 ::Window mouse_window = None; | |
616 ::Window dest_window = None; | |
617 FindWindowFor(screen_point, &mouse_window, &dest_window); | |
618 | |
619 if (source_current_window_ != dest_window) { | |
620 if (source_current_window_ != None) | |
621 SendXdndLeave(source_current_window_); | |
622 | |
623 source_current_window_ = dest_window; | |
624 | |
625 if (source_current_window_ != None) { | |
626 negotiated_operation_.erase(source_current_window_); | |
627 SendXdndEnter(source_current_window_); | |
628 } | |
629 } | |
630 | |
631 if (source_current_window_ != None) { | |
632 if (ContainsKey(waiting_on_status_, dest_window)) { | |
633 next_position_message_[dest_window] = | |
634 std::make_pair(screen_point, event->time); | |
635 } else { | |
636 SendXdndPosition(dest_window, screen_point, event->time); | |
637 } | |
638 } | |
639 } | |
640 | |
641 void DesktopDragDropClientAuraX11::OnMouseReleased() { | |
642 if (source_current_window_ != None) { | |
643 if (ContainsKey(waiting_on_status_, source_current_window_)) { | |
644 // If we are waiting for an XdndStatus message, we need to wait for it to | |
645 // complete. | |
646 pending_drop_.insert(source_current_window_); | |
647 return; | |
648 } | |
649 | |
650 std::map< ::Window, ::Atom>::iterator it = | |
651 negotiated_operation_.find(source_current_window_); | |
652 if (it != negotiated_operation_.end() && it->second != None) { | |
653 // We have negotiated an action with the other end. | |
654 SendXdndDrop(source_current_window_); | |
655 return; | |
656 } | |
657 | |
658 SendXdndLeave(source_current_window_); | |
659 source_current_window_ = None; | |
660 } | |
661 | |
662 move_loop_.EndMoveLoop(); | |
663 } | |
664 | |
665 void DesktopDragDropClientAuraX11::OnMoveLoopEnded() { | |
666 target_current_context_.reset(); | |
667 } | |
668 | |
355 void DesktopDragDropClientAuraX11::DragTranslate( | 669 void DesktopDragDropClientAuraX11::DragTranslate( |
356 const gfx::Point& root_window_location, | 670 const gfx::Point& root_window_location, |
357 scoped_ptr<ui::OSExchangeData>* data, | 671 scoped_ptr<ui::OSExchangeData>* data, |
358 scoped_ptr<ui::DropTargetEvent>* event, | 672 scoped_ptr<ui::DropTargetEvent>* event, |
359 aura::client::DragDropDelegate** delegate) { | 673 aura::client::DragDropDelegate** delegate) { |
360 gfx::Point root_location = root_window_location; | 674 gfx::Point root_location = root_window_location; |
361 root_window_->ConvertPointFromNativeScreen(&root_location); | 675 root_window_->ConvertPointFromNativeScreen(&root_location); |
362 aura::Window* target_window = | 676 aura::Window* target_window = |
363 root_window_->GetEventHandlerForPoint(root_location); | 677 root_window_->GetEventHandlerForPoint(root_location); |
364 bool target_window_changed = false; | 678 bool target_window_changed = false; |
365 if (target_window != target_window_) { | 679 if (target_window != target_window_) { |
366 if (target_window_) | 680 if (target_window_) |
367 NotifyDragLeave(); | 681 NotifyDragLeave(); |
368 target_window_ = target_window; | 682 target_window_ = target_window; |
369 if (target_window_) | 683 if (target_window_) |
370 target_window_->AddObserver(this); | 684 target_window_->AddObserver(this); |
371 target_window_changed = true; | 685 target_window_changed = true; |
372 } | 686 } |
373 *delegate = NULL; | 687 *delegate = NULL; |
374 if (!target_window_) | 688 if (!target_window_) |
375 return; | 689 return; |
376 *delegate = aura::client::GetDragDropDelegate(target_window_); | 690 *delegate = aura::client::GetDragDropDelegate(target_window_); |
377 if (!*delegate) | 691 if (!*delegate) |
378 return; | 692 return; |
379 | 693 |
380 data->reset(new OSExchangeData(new ui::OSExchangeDataProviderAuraX11( | 694 data->reset(new OSExchangeData(new ui::OSExchangeDataProviderAuraX11( |
381 root_window_host_, xwindow_, current_context_->targets()))); | 695 xwindow_, target_current_context_->CloneFetchedTargets()))); |
382 gfx::Point location = root_location; | 696 gfx::Point location = root_location; |
383 aura::Window::ConvertPointToTarget(root_window_, target_window_, &location); | 697 aura::Window::ConvertPointToTarget(root_window_, target_window_, &location); |
384 | 698 |
385 target_window_location_ = location; | 699 target_window_location_ = location; |
386 target_window_root_location_ = root_location; | 700 target_window_root_location_ = root_location; |
387 | 701 |
388 event->reset(new ui::DropTargetEvent( | 702 event->reset(new ui::DropTargetEvent( |
389 *(data->get()), | 703 *(data->get()), |
390 location, | 704 location, |
391 root_location, | 705 root_location, |
392 current_context_->GetDragOperation())); | 706 target_current_context_->GetDragOperation())); |
393 if (target_window_changed) | 707 if (target_window_changed) |
394 (*delegate)->OnDragEntered(*event->get()); | 708 (*delegate)->OnDragEntered(*event->get()); |
395 } | 709 } |
396 | 710 |
397 void DesktopDragDropClientAuraX11::NotifyDragLeave() { | 711 void DesktopDragDropClientAuraX11::NotifyDragLeave() { |
398 if (!target_window_) | 712 if (!target_window_) |
399 return; | 713 return; |
400 DragDropDelegate* delegate = | 714 DragDropDelegate* delegate = |
401 aura::client::GetDragDropDelegate(target_window_); | 715 aura::client::GetDragDropDelegate(target_window_); |
402 if (delegate) | 716 if (delegate) |
403 delegate->OnDragExited(); | 717 delegate->OnDragExited(); |
404 target_window_->RemoveObserver(this); | 718 target_window_->RemoveObserver(this); |
405 target_window_ = NULL; | 719 target_window_ = NULL; |
406 } | 720 } |
407 | 721 |
408 unsigned long DesktopDragDropClientAuraX11::DragOperationToAtom( | 722 ::Atom DesktopDragDropClientAuraX11::DragOperationToAtom( |
409 int drag_operation) { | 723 int drag_operation) { |
410 if (drag_operation & ui::DragDropTypes::DRAG_COPY) | 724 if (drag_operation & ui::DragDropTypes::DRAG_COPY) |
411 return atom_cache_.GetAtom(kXdndActionCopy); | 725 return atom_cache_.GetAtom(kXdndActionCopy); |
412 if (drag_operation & ui::DragDropTypes::DRAG_MOVE) | 726 if (drag_operation & ui::DragDropTypes::DRAG_MOVE) |
413 return atom_cache_.GetAtom(kXdndActionMove); | 727 return atom_cache_.GetAtom(kXdndActionMove); |
414 if (drag_operation & ui::DragDropTypes::DRAG_LINK) | 728 if (drag_operation & ui::DragDropTypes::DRAG_LINK) |
415 return atom_cache_.GetAtom(kXdndActionLink); | 729 return atom_cache_.GetAtom(kXdndActionLink); |
416 | 730 |
417 return None; | 731 return None; |
418 } | 732 } |
419 | 733 |
420 void DesktopDragDropClientAuraX11::SendXClientEvent(unsigned long xid, | 734 int DesktopDragDropClientAuraX11::AtomToDragOperation(::Atom atom) { |
735 int drag_operation = ui::DragDropTypes::DRAG_NONE; | |
736 if (atom == atom_cache_.GetAtom(kXdndActionCopy)) | |
737 drag_operation = ui::DragDropTypes::DRAG_COPY; | |
Daniel Erat
2013/06/17 22:09:57
nit: remove drag_operation and just return from ea
| |
738 else if (atom == atom_cache_.GetAtom(kXdndActionMove)) | |
739 drag_operation = ui::DragDropTypes::DRAG_MOVE; | |
740 else if (atom == atom_cache_.GetAtom(kXdndActionLink)) | |
741 drag_operation = ui::DragDropTypes::DRAG_LINK; | |
742 return drag_operation; | |
743 } | |
744 | |
745 std::vector< ::Atom> DesktopDragDropClientAuraX11::GetOfferedDragOperations() { | |
746 std::vector< ::Atom> operations; | |
747 if (drag_operation_ & ui::DragDropTypes::DRAG_COPY) | |
748 operations.push_back(atom_cache_.GetAtom(kXdndActionCopy)); | |
749 if (drag_operation_ & ui::DragDropTypes::DRAG_MOVE) | |
750 operations.push_back(atom_cache_.GetAtom(kXdndActionMove)); | |
751 if (drag_operation_ & ui::DragDropTypes::DRAG_LINK) | |
752 operations.push_back(atom_cache_.GetAtom(kXdndActionLink)); | |
753 return operations; | |
754 } | |
755 | |
756 ui::SelectionFormatMap* DesktopDragDropClientAuraX11::CloneFormatMap() const { | |
757 return source_provider_ ? source_provider_->CloneFormatMap() : NULL; | |
758 } | |
759 | |
760 void DesktopDragDropClientAuraX11::CompleteXdndPosition( | |
761 ::Window source_window, | |
762 const gfx::Point& screen_point) { | |
763 int drag_operation = ui::DragDropTypes::DRAG_NONE; | |
764 scoped_ptr<ui::OSExchangeData> data; | |
765 scoped_ptr<ui::DropTargetEvent> drop_target_event; | |
766 DragDropDelegate* delegate; | |
Daniel Erat
2013/06/17 22:09:57
nit: initialize to NULL
| |
767 DragTranslate(screen_point, &data, &drop_target_event, &delegate); | |
768 if (delegate) | |
769 drag_operation = delegate->OnDragUpdated(*drop_target_event); | |
770 | |
771 // Sends an XdndStatus message back to the source_window. l[2,3] | |
772 // theoretically represent an area in the window where the current action is | |
773 // the same as what we're returning, but I can't find any implementation that | |
774 // actually making use of this. A client can return (0, 0) and/or set the | |
775 // first bit of l[1] to disable the feature, and it appears that gtk neither | |
776 // sets this nor respects it if set. | |
777 XEvent xev; | |
778 xev.xclient.type = ClientMessage; | |
779 xev.xclient.message_type = atom_cache_.GetAtom("XdndStatus"); | |
780 xev.xclient.format = 32; | |
781 xev.xclient.window = source_window; | |
782 xev.xclient.data.l[0] = xwindow_; | |
783 xev.xclient.data.l[1] = (drag_operation != 0) ? (2 | 1) : 0; | |
Daniel Erat
2013/06/17 22:09:57
nit: document what this means in more detail
| |
784 xev.xclient.data.l[2] = 0; | |
785 xev.xclient.data.l[3] = 0; | |
786 xev.xclient.data.l[4] = DragOperationToAtom(drag_operation); | |
787 | |
788 SendXClientEvent(source_window, &xev); | |
789 } | |
790 | |
791 void DesktopDragDropClientAuraX11::SendXdndEnter(::Window dest_window) { | |
792 XEvent xev; | |
793 xev.xclient.type = ClientMessage; | |
794 xev.xclient.message_type = atom_cache_.GetAtom("XdndEnter"); | |
795 xev.xclient.format = 32; | |
796 xev.xclient.window = dest_window; | |
797 xev.xclient.data.l[0] = xwindow_; | |
798 xev.xclient.data.l[1] = (kMinXdndVersion << 24); // The version number. | |
799 xev.xclient.data.l[2] = 0; | |
800 xev.xclient.data.l[3] = 0; | |
801 xev.xclient.data.l[4] = 0; | |
802 | |
803 std::vector<Atom> targets; | |
804 source_provider_->RetrieveTargets(&targets); | |
805 | |
806 if (targets.size() > 3) { | |
807 xev.xclient.data.l[1] |= 1; | |
808 ui::SetAtomArrayProperty(xwindow_, "XdndTypeList", "ATOM", targets); | |
809 } else { | |
810 // Pack the targets into the enter message. | |
811 for (size_t i = 0; i < targets.size(); ++i) | |
812 xev.xclient.data.l[2 + i] = targets[i]; | |
813 } | |
814 | |
815 SendXClientEvent(dest_window, &xev); | |
816 } | |
817 | |
818 void DesktopDragDropClientAuraX11::SendXdndLeave(::Window dest_window) { | |
819 // If we're sending a leave message, don't wait for status messages anymore. | |
820 waiting_on_status_.erase(dest_window); | |
821 NextPositionMap::iterator it = next_position_message_.find(dest_window); | |
822 if (it != next_position_message_.end()) | |
823 next_position_message_.erase(it); | |
824 | |
825 XEvent xev; | |
826 xev.xclient.type = ClientMessage; | |
827 xev.xclient.message_type = atom_cache_.GetAtom("XdndLeave"); | |
828 xev.xclient.format = 32; | |
829 xev.xclient.window = dest_window; | |
830 xev.xclient.data.l[0] = xwindow_; | |
831 xev.xclient.data.l[1] = 0; | |
832 xev.xclient.data.l[2] = 0; | |
833 xev.xclient.data.l[3] = 0; | |
834 xev.xclient.data.l[4] = 0; | |
835 SendXClientEvent(dest_window, &xev); | |
836 } | |
837 | |
838 void DesktopDragDropClientAuraX11::SendXdndPosition( | |
839 ::Window dest_window, | |
840 const gfx::Point& screen_point, | |
841 unsigned long time) { | |
842 waiting_on_status_.insert(dest_window); | |
843 | |
844 XEvent xev; | |
845 xev.xclient.type = ClientMessage; | |
846 xev.xclient.message_type = atom_cache_.GetAtom("XdndPosition"); | |
847 xev.xclient.format = 32; | |
848 xev.xclient.window = dest_window; | |
849 xev.xclient.data.l[0] = xwindow_; | |
850 xev.xclient.data.l[1] = 0; | |
851 xev.xclient.data.l[2] = (screen_point.x() << 16) | screen_point.y(); | |
852 xev.xclient.data.l[3] = time; | |
853 xev.xclient.data.l[4] = DragOperationToAtom(drag_operation_); | |
854 SendXClientEvent(dest_window, &xev); | |
855 } | |
856 | |
857 void DesktopDragDropClientAuraX11::SendXdndDrop(::Window dest_window) { | |
858 XEvent xev; | |
859 xev.xclient.type = ClientMessage; | |
860 xev.xclient.message_type = atom_cache_.GetAtom("XdndDrop"); | |
861 xev.xclient.format = 32; | |
862 xev.xclient.window = dest_window; | |
863 xev.xclient.data.l[0] = xwindow_; | |
864 xev.xclient.data.l[1] = 0; | |
865 xev.xclient.data.l[2] = CurrentTime; | |
866 xev.xclient.data.l[3] = None; | |
867 xev.xclient.data.l[4] = None; | |
868 SendXClientEvent(dest_window, &xev); | |
869 } | |
870 | |
871 void DesktopDragDropClientAuraX11::SendXClientEvent(::Window xid, | |
421 XEvent* xev) { | 872 XEvent* xev) { |
422 DCHECK_EQ(ClientMessage, xev->type); | 873 DCHECK_EQ(ClientMessage, xev->type); |
423 | 874 |
424 // TODO(erg): When I get drag receiving working, shortcut messages to the X | 875 // Don't send messages to the X11 message queue if we can help it. |
425 // server and call the receivers DesktopDragDropClientAuraX11 instance | 876 DesktopDragDropClientAuraX11* short_circuit = GetForWindow(xid); |
426 // instead. | 877 if (short_circuit) { |
427 // | 878 Atom message_type = xev->xclient.message_type; |
879 if (message_type == atom_cache_.GetAtom("XdndEnter")) { | |
880 short_circuit->OnXdndEnter(xev->xclient); | |
881 return; | |
882 } else if (message_type == atom_cache_.GetAtom("XdndLeave")) { | |
883 short_circuit->OnXdndLeave(xev->xclient); | |
884 return; | |
885 } else if (message_type == atom_cache_.GetAtom("XdndPosition")) { | |
886 short_circuit->OnXdndPosition(xev->xclient); | |
887 return; | |
888 } else if (message_type == atom_cache_.GetAtom("XdndStatus")) { | |
889 short_circuit->OnXdndStatus(xev->xclient); | |
890 return; | |
891 } else if (message_type == atom_cache_.GetAtom("XdndFinished")) { | |
892 short_circuit->OnXdndFinished(xev->xclient); | |
893 return; | |
894 } else if (message_type == atom_cache_.GetAtom("XdndDrop")) { | |
895 short_circuit->OnXdndDrop(xev->xclient); | |
896 return; | |
897 } | |
898 } | |
899 | |
428 // I don't understand why the GTK+ code is doing what it's doing here. It | 900 // I don't understand why the GTK+ code is doing what it's doing here. It |
429 // goes out of its way to send the XEvent so that it receives a callback on | 901 // goes out of its way to send the XEvent so that it receives a callback on |
430 // success or failure, and when it fails, it then sends an internal GdkEvent | 902 // success or failure, and when it fails, it then sends an internal |
431 // about the failed drag. (And sending this message doesn't appear to go | 903 // GdkEvent about the failed drag. (And sending this message doesn't appear |
432 // through normal xlib machinery, but instead passes through the low level | 904 // to go through normal xlib machinery, but instead passes through the low |
433 // xProto (the x11 wire format) that I don't understand. | 905 // level xProto (the x11 wire format) that I don't understand. |
434 // | 906 // |
435 // I'm unsure if I have to jump through those hoops, or if XSendEvent is | 907 // I'm unsure if I have to jump through those hoops, or if XSendEvent is |
436 // sufficient. | 908 // sufficient. |
437 | |
438 XSendEvent(xdisplay_, xid, False, 0, xev); | 909 XSendEvent(xdisplay_, xid, False, 0, xev); |
439 } | 910 } |
440 | 911 |
441 } // namespace views | 912 } // namespace views |
OLD | NEW |