diff options
Diffstat (limited to '')
-rw-r--r-- | libraries/ecore/src/lib/ecore_x/xlib/ecore_x_dnd.c | 706 |
1 files changed, 706 insertions, 0 deletions
diff --git a/libraries/ecore/src/lib/ecore_x/xlib/ecore_x_dnd.c b/libraries/ecore/src/lib/ecore_x/xlib/ecore_x_dnd.c new file mode 100644 index 0000000..7908584 --- /dev/null +++ b/libraries/ecore/src/lib/ecore_x/xlib/ecore_x_dnd.c | |||
@@ -0,0 +1,706 @@ | |||
1 | #ifdef HAVE_CONFIG_H | ||
2 | # include <config.h> | ||
3 | #endif /* ifdef HAVE_CONFIG_H */ | ||
4 | |||
5 | #include <stdlib.h> | ||
6 | #include <string.h> | ||
7 | |||
8 | #include "Ecore.h" | ||
9 | #include "ecore_x_private.h" | ||
10 | #include "Ecore_X.h" | ||
11 | #include "Ecore_X_Atoms.h" | ||
12 | |||
13 | EAPI int ECORE_X_EVENT_XDND_ENTER = 0; | ||
14 | EAPI int ECORE_X_EVENT_XDND_POSITION = 0; | ||
15 | EAPI int ECORE_X_EVENT_XDND_STATUS = 0; | ||
16 | EAPI int ECORE_X_EVENT_XDND_LEAVE = 0; | ||
17 | EAPI int ECORE_X_EVENT_XDND_DROP = 0; | ||
18 | EAPI int ECORE_X_EVENT_XDND_FINISHED = 0; | ||
19 | |||
20 | static Ecore_X_DND_Source *_source = NULL; | ||
21 | static Ecore_X_DND_Target *_target = NULL; | ||
22 | static int _ecore_x_dnd_init_count = 0; | ||
23 | |||
24 | typedef struct _Version_Cache_Item | ||
25 | { | ||
26 | Ecore_X_Window win; | ||
27 | int ver; | ||
28 | } Version_Cache_Item; | ||
29 | static Version_Cache_Item *_version_cache = NULL; | ||
30 | static int _version_cache_num = 0, _version_cache_alloc = 0; | ||
31 | static void (*_posupdatecb)(void *, | ||
32 | Ecore_X_Xdnd_Position *); | ||
33 | static void *_posupdatedata; | ||
34 | |||
35 | void | ||
36 | _ecore_x_dnd_init(void) | ||
37 | { | ||
38 | if (!_ecore_x_dnd_init_count) | ||
39 | { | ||
40 | _source = calloc(1, sizeof(Ecore_X_DND_Source)); | ||
41 | if (!_source) return; | ||
42 | _source->version = ECORE_X_DND_VERSION; | ||
43 | _source->win = None; | ||
44 | _source->dest = None; | ||
45 | _source->state = ECORE_X_DND_SOURCE_IDLE; | ||
46 | _source->prev.window = 0; | ||
47 | |||
48 | _target = calloc(1, sizeof(Ecore_X_DND_Target)); | ||
49 | if (!_target) | ||
50 | { | ||
51 | free(_source); | ||
52 | _source = NULL; | ||
53 | return; | ||
54 | } | ||
55 | _target->win = None; | ||
56 | _target->source = None; | ||
57 | _target->state = ECORE_X_DND_TARGET_IDLE; | ||
58 | |||
59 | ECORE_X_EVENT_XDND_ENTER = ecore_event_type_new(); | ||
60 | ECORE_X_EVENT_XDND_POSITION = ecore_event_type_new(); | ||
61 | ECORE_X_EVENT_XDND_STATUS = ecore_event_type_new(); | ||
62 | ECORE_X_EVENT_XDND_LEAVE = ecore_event_type_new(); | ||
63 | ECORE_X_EVENT_XDND_DROP = ecore_event_type_new(); | ||
64 | ECORE_X_EVENT_XDND_FINISHED = ecore_event_type_new(); | ||
65 | } | ||
66 | |||
67 | _ecore_x_dnd_init_count++; | ||
68 | } /* _ecore_x_dnd_init */ | ||
69 | |||
70 | void | ||
71 | _ecore_x_dnd_shutdown(void) | ||
72 | { | ||
73 | _ecore_x_dnd_init_count--; | ||
74 | if (_ecore_x_dnd_init_count > 0) | ||
75 | return; | ||
76 | |||
77 | if (_source) | ||
78 | free(_source); | ||
79 | |||
80 | _source = NULL; | ||
81 | |||
82 | if (_target) | ||
83 | free(_target); | ||
84 | |||
85 | _target = NULL; | ||
86 | |||
87 | _ecore_x_dnd_init_count = 0; | ||
88 | } /* _ecore_x_dnd_shutdown */ | ||
89 | |||
90 | static Eina_Bool | ||
91 | _ecore_x_dnd_converter_copy(char *target __UNUSED__, | ||
92 | void *data, | ||
93 | int size, | ||
94 | void **data_ret, | ||
95 | int *size_ret, | ||
96 | Ecore_X_Atom *tprop __UNUSED__, | ||
97 | int *count __UNUSED__) | ||
98 | { | ||
99 | XTextProperty text_prop; | ||
100 | char *mystr; | ||
101 | XICCEncodingStyle style = XTextStyle; | ||
102 | |||
103 | if (!data || !size) | ||
104 | return EINA_FALSE; | ||
105 | |||
106 | mystr = calloc(1, size + 1); | ||
107 | if (!mystr) | ||
108 | return EINA_FALSE; | ||
109 | |||
110 | memcpy(mystr, data, size); | ||
111 | |||
112 | if (XmbTextListToTextProperty(_ecore_x_disp, &mystr, 1, style, | ||
113 | &text_prop) == Success) | ||
114 | { | ||
115 | int bufsize = strlen((char *)text_prop.value) + 1; | ||
116 | *data_ret = malloc(bufsize); | ||
117 | if (!*data_ret) | ||
118 | { | ||
119 | free(mystr); | ||
120 | return EINA_FALSE; | ||
121 | } | ||
122 | memcpy(*data_ret, text_prop.value, bufsize); | ||
123 | *size_ret = bufsize; | ||
124 | XFree(text_prop.value); | ||
125 | free(mystr); | ||
126 | return EINA_TRUE; | ||
127 | } | ||
128 | else | ||
129 | { | ||
130 | free(mystr); | ||
131 | return EINA_FALSE; | ||
132 | } | ||
133 | } /* _ecore_x_dnd_converter_copy */ | ||
134 | |||
135 | EAPI void | ||
136 | ecore_x_dnd_aware_set(Ecore_X_Window win, | ||
137 | Eina_Bool on) | ||
138 | { | ||
139 | Ecore_X_Atom prop_data = ECORE_X_DND_VERSION; | ||
140 | |||
141 | LOGFN(__FILE__, __LINE__, __FUNCTION__); | ||
142 | if (on) | ||
143 | ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_AWARE, | ||
144 | XA_ATOM, 32, &prop_data, 1); | ||
145 | else | ||
146 | ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_AWARE); | ||
147 | } /* ecore_x_dnd_aware_set */ | ||
148 | |||
149 | EAPI int | ||
150 | ecore_x_dnd_version_get(Ecore_X_Window win) | ||
151 | { | ||
152 | unsigned char *prop_data; | ||
153 | int num; | ||
154 | Version_Cache_Item *t; | ||
155 | |||
156 | LOGFN(__FILE__, __LINE__, __FUNCTION__); | ||
157 | // this looks hacky - and it is, but we need a way of caching info about | ||
158 | // a window while dragging, because we literally query this every mouse | ||
159 | // move and going to and from x multiple times per move is EXPENSIVE | ||
160 | // and slows things down, puts lots of load on x etc. | ||
161 | if (_source->state == ECORE_X_DND_SOURCE_DRAGGING) | ||
162 | if (_version_cache) | ||
163 | { | ||
164 | int i; | ||
165 | |||
166 | for (i = 0; i < _version_cache_num; i++) | ||
167 | { | ||
168 | if (_version_cache[i].win == win) | ||
169 | return _version_cache[i].ver; | ||
170 | } | ||
171 | } | ||
172 | |||
173 | if (ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_AWARE, | ||
174 | XA_ATOM, 32, &prop_data, &num)) | ||
175 | { | ||
176 | int version = (int)*prop_data; | ||
177 | free(prop_data); | ||
178 | if (_source->state == ECORE_X_DND_SOURCE_DRAGGING) | ||
179 | { | ||
180 | _version_cache_num++; | ||
181 | if (_version_cache_num > _version_cache_alloc) | ||
182 | _version_cache_alloc += 16; | ||
183 | |||
184 | t = realloc(_version_cache, | ||
185 | _version_cache_alloc * | ||
186 | sizeof(Version_Cache_Item)); | ||
187 | if (!t) return 0; | ||
188 | _version_cache = t; | ||
189 | _version_cache[_version_cache_num - 1].win = win; | ||
190 | _version_cache[_version_cache_num - 1].ver = version; | ||
191 | } | ||
192 | |||
193 | return version; | ||
194 | } | ||
195 | |||
196 | if (_source->state == ECORE_X_DND_SOURCE_DRAGGING) | ||
197 | { | ||
198 | _version_cache_num++; | ||
199 | if (_version_cache_num > _version_cache_alloc) | ||
200 | _version_cache_alloc += 16; | ||
201 | |||
202 | t = realloc(_version_cache, _version_cache_alloc * | ||
203 | sizeof(Version_Cache_Item)); | ||
204 | if (!t) return 0; | ||
205 | _version_cache = t; | ||
206 | _version_cache[_version_cache_num - 1].win = win; | ||
207 | _version_cache[_version_cache_num - 1].ver = 0; | ||
208 | } | ||
209 | |||
210 | return 0; | ||
211 | } /* ecore_x_dnd_version_get */ | ||
212 | |||
213 | EAPI Eina_Bool | ||
214 | ecore_x_dnd_type_isset(Ecore_X_Window win, | ||
215 | const char *type) | ||
216 | { | ||
217 | int num, i, ret = EINA_FALSE; | ||
218 | unsigned char *data; | ||
219 | Ecore_X_Atom *atoms, atom; | ||
220 | |||
221 | LOGFN(__FILE__, __LINE__, __FUNCTION__); | ||
222 | if (!ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_TYPE_LIST, | ||
223 | XA_ATOM, 32, &data, &num)) | ||
224 | return ret; | ||
225 | |||
226 | atom = ecore_x_atom_get(type); | ||
227 | atoms = (Ecore_X_Atom *)data; | ||
228 | |||
229 | for (i = 0; i < num; ++i) | ||
230 | { | ||
231 | if (atom == atoms[i]) | ||
232 | { | ||
233 | ret = EINA_TRUE; | ||
234 | break; | ||
235 | } | ||
236 | } | ||
237 | |||
238 | XFree(data); | ||
239 | return ret; | ||
240 | } /* ecore_x_dnd_type_isset */ | ||
241 | |||
242 | EAPI void | ||
243 | ecore_x_dnd_type_set(Ecore_X_Window win, | ||
244 | const char *type, | ||
245 | Eina_Bool on) | ||
246 | { | ||
247 | Ecore_X_Atom atom; | ||
248 | Ecore_X_Atom *oldset = NULL, *newset = NULL; | ||
249 | int i, j = 0, num = 0; | ||
250 | unsigned char *data = NULL; | ||
251 | unsigned char *old_data = NULL; | ||
252 | |||
253 | LOGFN(__FILE__, __LINE__, __FUNCTION__); | ||
254 | atom = ecore_x_atom_get(type); | ||
255 | ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_TYPE_LIST, | ||
256 | XA_ATOM, 32, &old_data, &num); | ||
257 | oldset = (Ecore_X_Atom *)old_data; | ||
258 | |||
259 | LOGFN(__FILE__, __LINE__, __FUNCTION__); | ||
260 | if (on) | ||
261 | { | ||
262 | if (ecore_x_dnd_type_isset(win, type)) | ||
263 | { | ||
264 | XFree(old_data); | ||
265 | return; | ||
266 | } | ||
267 | |||
268 | newset = calloc(num + 1, sizeof(Ecore_X_Atom)); | ||
269 | if (!newset) | ||
270 | return; | ||
271 | |||
272 | data = (unsigned char *)newset; | ||
273 | |||
274 | for (i = 0; i < num; i++) | ||
275 | newset[i + 1] = oldset[i]; | ||
276 | /* prepend the new type */ | ||
277 | newset[0] = atom; | ||
278 | |||
279 | ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_TYPE_LIST, | ||
280 | XA_ATOM, 32, data, num + 1); | ||
281 | } | ||
282 | else | ||
283 | { | ||
284 | if (!ecore_x_dnd_type_isset(win, type)) | ||
285 | { | ||
286 | XFree(old_data); | ||
287 | return; | ||
288 | } | ||
289 | |||
290 | newset = calloc(num - 1, sizeof(Ecore_X_Atom)); | ||
291 | if (!newset) | ||
292 | { | ||
293 | XFree(old_data); | ||
294 | return; | ||
295 | } | ||
296 | |||
297 | data = (unsigned char *)newset; | ||
298 | for (i = 0; i < num; i++) | ||
299 | if (oldset[i] != atom) | ||
300 | newset[j++] = oldset[i]; | ||
301 | |||
302 | ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_TYPE_LIST, | ||
303 | XA_ATOM, 32, data, num - 1); | ||
304 | } | ||
305 | |||
306 | XFree(oldset); | ||
307 | free(newset); | ||
308 | } /* ecore_x_dnd_type_set */ | ||
309 | |||
310 | EAPI void | ||
311 | ecore_x_dnd_types_set(Ecore_X_Window win, | ||
312 | const char **types, | ||
313 | unsigned int num_types) | ||
314 | { | ||
315 | Ecore_X_Atom *newset = NULL; | ||
316 | unsigned int i; | ||
317 | unsigned char *data = NULL; | ||
318 | |||
319 | LOGFN(__FILE__, __LINE__, __FUNCTION__); | ||
320 | if (!num_types) | ||
321 | ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_TYPE_LIST); | ||
322 | else | ||
323 | { | ||
324 | newset = calloc(num_types, sizeof(Ecore_X_Atom)); | ||
325 | if (!newset) | ||
326 | return; | ||
327 | |||
328 | data = (unsigned char *)newset; | ||
329 | for (i = 0; i < num_types; i++) | ||
330 | { | ||
331 | newset[i] = ecore_x_atom_get(types[i]); | ||
332 | ecore_x_selection_converter_atom_add(newset[i], | ||
333 | _ecore_x_dnd_converter_copy); | ||
334 | } | ||
335 | ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_TYPE_LIST, | ||
336 | XA_ATOM, 32, data, num_types); | ||
337 | free(newset); | ||
338 | } | ||
339 | } /* ecore_x_dnd_types_set */ | ||
340 | |||
341 | EAPI void | ||
342 | ecore_x_dnd_actions_set(Ecore_X_Window win, | ||
343 | Ecore_X_Atom *actions, | ||
344 | unsigned int num_actions) | ||
345 | { | ||
346 | unsigned int i; | ||
347 | unsigned char *data = NULL; | ||
348 | |||
349 | LOGFN(__FILE__, __LINE__, __FUNCTION__); | ||
350 | if (!num_actions) | ||
351 | ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_ACTION_LIST); | ||
352 | else | ||
353 | { | ||
354 | data = (unsigned char *)actions; | ||
355 | for (i = 0; i < num_actions; i++) | ||
356 | { | ||
357 | ecore_x_selection_converter_atom_add(actions[i], | ||
358 | _ecore_x_dnd_converter_copy); | ||
359 | } | ||
360 | ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_ACTION_LIST, | ||
361 | XA_ATOM, 32, data, num_actions); | ||
362 | } | ||
363 | } /* ecore_x_dnd_actions_set */ | ||
364 | |||
365 | /** | ||
366 | * The DND position update cb is called Ecore_X sends a DND position to a | ||
367 | * client. | ||
368 | * | ||
369 | * It essentially mirrors some of the data sent in the position message. | ||
370 | * Generally this cb should be set just before position update is called. | ||
371 | * Please note well you need to look after your own data pointer if someone | ||
372 | * trashes you position update cb set. | ||
373 | * | ||
374 | * It is considered good form to clear this when the dnd event finishes. | ||
375 | * | ||
376 | * @param cb Callback to updated each time ecore_x sends a position update. | ||
377 | * @param data User data. | ||
378 | */ | ||
379 | EAPI void | ||
380 | ecore_x_dnd_callback_pos_update_set( | ||
381 | void (*cb)(void *, | ||
382 | Ecore_X_Xdnd_Position *data), | ||
383 | const void *data) | ||
384 | { | ||
385 | _posupdatecb = cb; | ||
386 | _posupdatedata = (void *)data; /* Discard the const early */ | ||
387 | } | ||
388 | |||
389 | Ecore_X_DND_Source * | ||
390 | _ecore_x_dnd_source_get(void) | ||
391 | { | ||
392 | return _source; | ||
393 | } /* _ecore_x_dnd_source_get */ | ||
394 | |||
395 | Ecore_X_DND_Target * | ||
396 | _ecore_x_dnd_target_get(void) | ||
397 | { | ||
398 | return _target; | ||
399 | } /* _ecore_x_dnd_target_get */ | ||
400 | |||
401 | EAPI Eina_Bool | ||
402 | ecore_x_dnd_begin(Ecore_X_Window source, | ||
403 | unsigned char *data, | ||
404 | int size) | ||
405 | { | ||
406 | LOGFN(__FILE__, __LINE__, __FUNCTION__); | ||
407 | if (!ecore_x_dnd_version_get(source)) | ||
408 | return EINA_FALSE; | ||
409 | |||
410 | /* Take ownership of XdndSelection */ | ||
411 | if (!ecore_x_selection_xdnd_set(source, data, size)) | ||
412 | return EINA_FALSE; | ||
413 | |||
414 | if (_version_cache) | ||
415 | { | ||
416 | free(_version_cache); | ||
417 | _version_cache = NULL; | ||
418 | _version_cache_num = 0; | ||
419 | _version_cache_alloc = 0; | ||
420 | } | ||
421 | |||
422 | ecore_x_window_shadow_tree_flush(); | ||
423 | |||
424 | _source->win = source; | ||
425 | ecore_x_window_ignore_set(_source->win, 1); | ||
426 | _source->state = ECORE_X_DND_SOURCE_DRAGGING; | ||
427 | _source->time = _ecore_x_event_last_time; | ||
428 | _source->prev.window = 0; | ||
429 | |||
430 | /* Default Accepted Action: move */ | ||
431 | _source->action = ECORE_X_ATOM_XDND_ACTION_MOVE; | ||
432 | _source->accepted_action = None; | ||
433 | _source->dest = None; | ||
434 | |||
435 | return EINA_TRUE; | ||
436 | } /* ecore_x_dnd_begin */ | ||
437 | |||
438 | EAPI Eina_Bool | ||
439 | ecore_x_dnd_drop(void) | ||
440 | { | ||
441 | XEvent xev; | ||
442 | int status = EINA_FALSE; | ||
443 | |||
444 | LOGFN(__FILE__, __LINE__, __FUNCTION__); | ||
445 | if (_source->dest) | ||
446 | { | ||
447 | xev.xany.type = ClientMessage; | ||
448 | xev.xany.display = _ecore_x_disp; | ||
449 | xev.xclient.format = 32; | ||
450 | xev.xclient.window = _source->dest; | ||
451 | |||
452 | if (_source->will_accept) | ||
453 | { | ||
454 | xev.xclient.message_type = ECORE_X_ATOM_XDND_DROP; | ||
455 | xev.xclient.data.l[0] = _source->win; | ||
456 | xev.xclient.data.l[1] = 0; | ||
457 | xev.xclient.data.l[2] = _source->time; | ||
458 | XSendEvent(_ecore_x_disp, _source->dest, False, 0, &xev); | ||
459 | _source->state = ECORE_X_DND_SOURCE_DROPPED; | ||
460 | status = EINA_TRUE; | ||
461 | } | ||
462 | else | ||
463 | { | ||
464 | xev.xclient.message_type = ECORE_X_ATOM_XDND_LEAVE; | ||
465 | xev.xclient.data.l[0] = _source->win; | ||
466 | xev.xclient.data.l[1] = 0; | ||
467 | XSendEvent(_ecore_x_disp, _source->dest, False, 0, &xev); | ||
468 | _source->state = ECORE_X_DND_SOURCE_IDLE; | ||
469 | } | ||
470 | } | ||
471 | else | ||
472 | { | ||
473 | /* Dropping on nothing */ | ||
474 | ecore_x_selection_xdnd_clear(); | ||
475 | _source->state = ECORE_X_DND_SOURCE_IDLE; | ||
476 | } | ||
477 | |||
478 | ecore_x_window_ignore_set(_source->win, 0); | ||
479 | |||
480 | _source->prev.window = 0; | ||
481 | |||
482 | return status; | ||
483 | } /* ecore_x_dnd_drop */ | ||
484 | |||
485 | EAPI void | ||
486 | ecore_x_dnd_send_status(Eina_Bool will_accept, | ||
487 | Eina_Bool suppress, | ||
488 | Ecore_X_Rectangle rectangle, | ||
489 | Ecore_X_Atom action) | ||
490 | { | ||
491 | XEvent xev; | ||
492 | |||
493 | if (_target->state == ECORE_X_DND_TARGET_IDLE) | ||
494 | return; | ||
495 | |||
496 | LOGFN(__FILE__, __LINE__, __FUNCTION__); | ||
497 | memset(&xev, 0, sizeof(XEvent)); | ||
498 | |||
499 | _target->will_accept = will_accept; | ||
500 | |||
501 | xev.xclient.type = ClientMessage; | ||
502 | xev.xclient.display = _ecore_x_disp; | ||
503 | xev.xclient.message_type = ECORE_X_ATOM_XDND_STATUS; | ||
504 | xev.xclient.format = 32; | ||
505 | xev.xclient.window = _target->source; | ||
506 | |||
507 | xev.xclient.data.l[0] = _target->win; | ||
508 | xev.xclient.data.l[1] = 0; | ||
509 | if (will_accept) | ||
510 | xev.xclient.data.l[1] |= 0x1UL; | ||
511 | |||
512 | if (!suppress) | ||
513 | xev.xclient.data.l[1] |= 0x2UL; | ||
514 | |||
515 | /* Set rectangle information */ | ||
516 | xev.xclient.data.l[2] = rectangle.x; | ||
517 | xev.xclient.data.l[2] <<= 16; | ||
518 | xev.xclient.data.l[2] |= rectangle.y; | ||
519 | xev.xclient.data.l[3] = rectangle.width; | ||
520 | xev.xclient.data.l[3] <<= 16; | ||
521 | xev.xclient.data.l[3] |= rectangle.height; | ||
522 | |||
523 | if (will_accept) | ||
524 | { | ||
525 | xev.xclient.data.l[4] = action; | ||
526 | _target->accepted_action = action; | ||
527 | } | ||
528 | else | ||
529 | { | ||
530 | xev.xclient.data.l[4] = None; | ||
531 | _target->accepted_action = action; | ||
532 | } | ||
533 | |||
534 | XSendEvent(_ecore_x_disp, _target->source, False, 0, &xev); | ||
535 | } /* ecore_x_dnd_send_status */ | ||
536 | |||
537 | EAPI void | ||
538 | ecore_x_dnd_send_finished(void) | ||
539 | { | ||
540 | XEvent xev; | ||
541 | |||
542 | if (_target->state == ECORE_X_DND_TARGET_IDLE) | ||
543 | return; | ||
544 | |||
545 | LOGFN(__FILE__, __LINE__, __FUNCTION__); | ||
546 | xev.xany.type = ClientMessage; | ||
547 | xev.xany.display = _ecore_x_disp; | ||
548 | xev.xclient.message_type = ECORE_X_ATOM_XDND_FINISHED; | ||
549 | xev.xclient.format = 32; | ||
550 | xev.xclient.window = _target->source; | ||
551 | |||
552 | xev.xclient.data.l[0] = _target->win; | ||
553 | xev.xclient.data.l[1] = 0; | ||
554 | xev.xclient.data.l[2] = 0; | ||
555 | if (_target->will_accept) | ||
556 | { | ||
557 | xev.xclient.data.l[1] |= 0x1UL; | ||
558 | xev.xclient.data.l[2] = _target->accepted_action; | ||
559 | } | ||
560 | |||
561 | XSendEvent(_ecore_x_disp, _target->source, False, 0, &xev); | ||
562 | |||
563 | _target->state = ECORE_X_DND_TARGET_IDLE; | ||
564 | } /* ecore_x_dnd_send_finished */ | ||
565 | |||
566 | EAPI void | ||
567 | ecore_x_dnd_source_action_set(Ecore_X_Atom action) | ||
568 | { | ||
569 | _source->action = action; | ||
570 | if (_source->prev.window) | ||
571 | _ecore_x_dnd_drag(_source->prev.window, _source->prev.x, _source->prev.y); | ||
572 | } /* ecore_x_dnd_source_action_set */ | ||
573 | |||
574 | EAPI Ecore_X_Atom | ||
575 | ecore_x_dnd_source_action_get(void) | ||
576 | { | ||
577 | return _source->action; | ||
578 | } /* ecore_x_dnd_source_action_get */ | ||
579 | |||
580 | void | ||
581 | _ecore_x_dnd_drag(Ecore_X_Window root, | ||
582 | int x, | ||
583 | int y) | ||
584 | { | ||
585 | XEvent xev; | ||
586 | Ecore_X_Window win; | ||
587 | Ecore_X_Window *skip; | ||
588 | Ecore_X_Xdnd_Position pos; | ||
589 | int num; | ||
590 | |||
591 | if (_source->state != ECORE_X_DND_SOURCE_DRAGGING) | ||
592 | return; | ||
593 | |||
594 | /* Preinitialize XEvent struct */ | ||
595 | memset(&xev, 0, sizeof(XEvent)); | ||
596 | xev.xany.type = ClientMessage; | ||
597 | xev.xany.display = _ecore_x_disp; | ||
598 | xev.xclient.format = 32; | ||
599 | |||
600 | /* Attempt to find a DND-capable window under the cursor */ | ||
601 | skip = ecore_x_window_ignore_list(&num); | ||
602 | // WARNING - this function is HEAVY. it goes to and from x a LOT walking the | ||
603 | // window tree - use the SHADOW version - makes a 1-off tree copy, then uses | ||
604 | // that instead. | ||
605 | // win = ecore_x_window_at_xy_with_skip_get(x, y, skip, num); | ||
606 | win = ecore_x_window_shadow_tree_at_xy_with_skip_get(root, x, y, skip, num); | ||
607 | |||
608 | // NOTE: This now uses the shadow version to find parent windows | ||
609 | // while ((win) && !(ecore_x_dnd_version_get(win))) | ||
610 | // win = ecore_x_window_parent_get(win); | ||
611 | while ((win) && !(ecore_x_dnd_version_get(win))) | ||
612 | win = ecore_x_window_shadow_parent_get(root, win); | ||
613 | |||
614 | /* Send XdndLeave to current destination window if we have left it */ | ||
615 | if ((_source->dest) && (win != _source->dest)) | ||
616 | { | ||
617 | xev.xclient.window = _source->dest; | ||
618 | xev.xclient.message_type = ECORE_X_ATOM_XDND_LEAVE; | ||
619 | xev.xclient.data.l[0] = _source->win; | ||
620 | xev.xclient.data.l[1] = 0; | ||
621 | |||
622 | XSendEvent(_ecore_x_disp, _source->dest, False, 0, &xev); | ||
623 | _source->suppress = 0; | ||
624 | } | ||
625 | |||
626 | if (win) | ||
627 | { | ||
628 | int x1, x2, y1, y2; | ||
629 | |||
630 | _source->version = MIN(ECORE_X_DND_VERSION, | ||
631 | ecore_x_dnd_version_get(win)); | ||
632 | if (win != _source->dest) | ||
633 | { | ||
634 | int i; | ||
635 | unsigned char *data; | ||
636 | Ecore_X_Atom *types; | ||
637 | |||
638 | ecore_x_window_prop_property_get(_source->win, | ||
639 | ECORE_X_ATOM_XDND_TYPE_LIST, | ||
640 | XA_ATOM, | ||
641 | 32, | ||
642 | &data, | ||
643 | &num); | ||
644 | types = (Ecore_X_Atom *)data; | ||
645 | |||
646 | /* Entered new window, send XdndEnter */ | ||
647 | xev.xclient.window = win; | ||
648 | xev.xclient.message_type = ECORE_X_ATOM_XDND_ENTER; | ||
649 | xev.xclient.data.l[0] = _source->win; | ||
650 | xev.xclient.data.l[1] = 0; | ||
651 | if (num > 3) | ||
652 | xev.xclient.data.l[1] |= 0x1UL; | ||
653 | else | ||
654 | xev.xclient.data.l[1] &= 0xfffffffeUL; | ||
655 | |||
656 | xev.xclient.data.l[1] |= ((unsigned long)_source->version) << 24; | ||
657 | |||
658 | for (i = 2; i < 5; i++) | ||
659 | xev.xclient.data.l[i] = 0; | ||
660 | for (i = 0; i < MIN(num, 3); ++i) | ||
661 | xev.xclient.data.l[i + 2] = types[i]; | ||
662 | XFree(data); | ||
663 | XSendEvent(_ecore_x_disp, win, False, 0, &xev); | ||
664 | _source->await_status = 0; | ||
665 | _source->will_accept = 0; | ||
666 | } | ||
667 | |||
668 | /* Determine if we're still in the rectangle from the last status */ | ||
669 | x1 = _source->rectangle.x; | ||
670 | x2 = _source->rectangle.x + _source->rectangle.width; | ||
671 | y1 = _source->rectangle.y; | ||
672 | y2 = _source->rectangle.y + _source->rectangle.height; | ||
673 | |||
674 | if ((!_source->await_status) || | ||
675 | (!_source->suppress) || | ||
676 | ((x < x1) || (x > x2) || (y < y1) || (y > y2))) | ||
677 | { | ||
678 | xev.xclient.window = win; | ||
679 | xev.xclient.message_type = ECORE_X_ATOM_XDND_POSITION; | ||
680 | xev.xclient.data.l[0] = _source->win; | ||
681 | xev.xclient.data.l[1] = 0; /* Reserved */ | ||
682 | xev.xclient.data.l[2] = ((x << 16) & 0xffff0000) | (y & 0xffff); | ||
683 | xev.xclient.data.l[3] = _source->time; /* Version 1 */ | ||
684 | xev.xclient.data.l[4] = _source->action; /* Version 2, Needs to be pre-set */ | ||
685 | XSendEvent(_ecore_x_disp, win, False, 0, &xev); | ||
686 | |||
687 | _source->await_status = 1; | ||
688 | } | ||
689 | } | ||
690 | |||
691 | if (_posupdatecb) | ||
692 | { | ||
693 | pos.position.x = x; | ||
694 | pos.position.y = y; | ||
695 | pos.win = win; | ||
696 | pos.prev = _source->dest; | ||
697 | _posupdatecb(_posupdatedata, &pos); | ||
698 | } | ||
699 | |||
700 | _source->prev.x = x; | ||
701 | _source->prev.y = y; | ||
702 | _source->prev.window = root; | ||
703 | _source->dest = win; | ||
704 | } /* _ecore_x_dnd_drag */ | ||
705 | |||
706 | /* vim:set ts=8 sw=3 sts=3 expandtab cino=>5n-2f0^-2{2(0W1st0 :*/ | ||