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