4 #include "ecore_xcb_private.h"
5 #include "Ecore_X_Atoms.h"
7 EAPI int ECORE_X_EVENT_XDND_ENTER = 0;
8 EAPI int ECORE_X_EVENT_XDND_POSITION = 0;
9 EAPI int ECORE_X_EVENT_XDND_STATUS = 0;
10 EAPI int ECORE_X_EVENT_XDND_LEAVE = 0;
11 EAPI int ECORE_X_EVENT_XDND_DROP = 0;
12 EAPI int ECORE_X_EVENT_XDND_FINISHED = 0;
14 static Ecore_X_DND_Source *_source = NULL;
15 static Ecore_X_DND_Target *_target = NULL;
16 static int _ecore_x_dnd_init_count = 0;
19 _ecore_x_dnd_init(void)
21 if (!_ecore_x_dnd_init_count)
23 _source = calloc(1, sizeof(Ecore_X_DND_Source));
24 _source->version = ECORE_X_DND_VERSION;
25 _source->win = XCB_NONE;
26 _source->dest = XCB_NONE;
27 _source->state = ECORE_X_DND_SOURCE_IDLE;
28 _source->prev.window = 0;
30 _target = calloc(1, sizeof(Ecore_X_DND_Target));
31 _target->win = XCB_NONE;
32 _target->source = XCB_NONE;
33 _target->state = ECORE_X_DND_TARGET_IDLE;
35 ECORE_X_EVENT_XDND_ENTER = ecore_event_type_new();
36 ECORE_X_EVENT_XDND_POSITION = ecore_event_type_new();
37 ECORE_X_EVENT_XDND_STATUS = ecore_event_type_new();
38 ECORE_X_EVENT_XDND_LEAVE = ecore_event_type_new();
39 ECORE_X_EVENT_XDND_DROP = ecore_event_type_new();
40 ECORE_X_EVENT_XDND_FINISHED = ecore_event_type_new();
43 _ecore_x_dnd_init_count++;
44 } /* _ecore_x_dnd_init */
47 _ecore_x_dnd_shutdown(void)
49 _ecore_x_dnd_init_count--;
50 if (_ecore_x_dnd_init_count > 0)
63 _ecore_x_dnd_init_count = 0;
64 } /* _ecore_x_dnd_shutdown */
67 ecore_x_dnd_aware_set(Ecore_X_Window window,
70 Ecore_X_Atom prop_data = ECORE_X_DND_VERSION;
73 ecore_x_window_prop_property_set(window, ECORE_X_ATOM_XDND_AWARE,
74 ECORE_X_ATOM_ATOM, 32, &prop_data, 1);
76 ecore_x_window_prop_property_del(window, ECORE_X_ATOM_XDND_AWARE);
77 } /* ecore_x_dnd_aware_set */
80 * Sends the GetProperty request.
81 * @param window Window whose properties are requested.
84 ecore_x_dnd_version_get_prefetch(Ecore_X_Window window)
86 xcb_get_property_cookie_t cookie;
88 cookie = xcb_get_property_unchecked(_ecore_xcb_conn, 0,
89 window ? window : ((xcb_screen_t *)_ecore_xcb_screen)->root,
90 ECORE_X_ATOM_XDND_AWARE,
93 _ecore_xcb_cookie_cache(cookie.sequence);
94 } /* ecore_x_dnd_version_get_prefetch */
97 * Gets the reply of the GetProperty request sent by ecore_x_dnd_version_get_prefetch().
100 ecore_x_dnd_version_get_fetch(void)
102 xcb_get_property_cookie_t cookie;
103 xcb_get_property_reply_t *reply;
105 cookie.sequence = _ecore_xcb_cookie_get();
106 reply = xcb_get_property_reply(_ecore_xcb_conn, cookie, NULL);
107 _ecore_xcb_reply_cache(reply);
108 } /* ecore_x_dnd_version_get_fetch */
111 * Get the DnD version.
112 * @param window Unused.
113 * @return 0 on failure, the version otherwise.
115 * Get the DnD version. Returns 0 on failure, the version otherwise.
117 * To use this function, you must call before, and in order,
118 * ecore_x_dnd_version_get_prefetch(), which sends the GetProperty request,
119 * then ecore_x_dnd_version_get_fetch(), which gets the reply.
122 ecore_x_dnd_version_get(Ecore_X_Window window)
124 unsigned char *prop_data;
127 if (ecore_x_window_prop_property_get(window, ECORE_X_ATOM_XDND_AWARE,
128 ECORE_X_ATOM_ATOM, 32, &prop_data, &num))
130 int version = (int)*prop_data;
136 } /* ecore_x_dnd_version_get */
139 * Sends the GetProperty request.
140 * @param window Window whose properties are requested.
143 ecore_x_dnd_type_get_prefetch(Ecore_X_Window window)
145 xcb_get_property_cookie_t cookie;
147 cookie = xcb_get_property_unchecked(_ecore_xcb_conn, 0,
148 window ? window : ((xcb_screen_t *)_ecore_xcb_screen)->root,
149 ECORE_X_ATOM_XDND_TYPE_LIST,
152 _ecore_xcb_cookie_cache(cookie.sequence);
153 } /* ecore_x_dnd_type_get_prefetch */
156 * Gets the reply of the GetProperty request sent by ecore_x_dnd_type_get_prefetch().
159 ecore_x_dnd_type_get_fetch(void)
161 xcb_get_property_cookie_t cookie;
162 xcb_get_property_reply_t *reply;
164 cookie.sequence = _ecore_xcb_cookie_get();
165 reply = xcb_get_property_reply(_ecore_xcb_conn, cookie, NULL);
166 _ecore_xcb_reply_cache(reply);
167 } /* ecore_x_dnd_type_get_fetch */
169 /* FIXME: round trip (InternAtomGet request) */
172 * Check if the type is set.
173 * @param window Unused.
174 * @param type The type to check
175 * @return 0 on failure, 1 otherwise.
177 * Check if the type is set. 0 on failure, 1 otherwise.
179 * To use this function, you must call before, and in order,
180 * ecore_x_dnd_type_get_prefetch(), which sends the GetProperty request,
181 * then ecore_x_dnd_type_get_fetch(), which gets the reply.
184 ecore_x_dnd_type_isset(Ecore_X_Window window,
187 xcb_intern_atom_cookie_t cookie;
188 xcb_intern_atom_reply_t *reply;
195 cookie = xcb_intern_atom_unchecked(_ecore_xcb_conn, 0,
198 if (!ecore_x_window_prop_property_get(window, ECORE_X_ATOM_XDND_TYPE_LIST,
199 ECORE_X_ATOM_ATOM, 32, &data, &num))
201 reply = xcb_intern_atom_reply(_ecore_xcb_conn, cookie, NULL);
208 reply = xcb_intern_atom_reply(_ecore_xcb_conn, cookie, NULL);
215 atoms = (Ecore_X_Atom *)data;
217 for (i = 0; i < num; ++i)
219 if (reply->atom == atoms[i])
230 } /* ecore_x_dnd_type_isset */
232 /* FIXME: round trip (InternAtomGet request) */
236 * @param window Unused.
237 * @param type The type to set
238 * @param on 0 or non 0...
242 * To use this function, you must call before, and in order,
243 * ecore_x_dnd_type_get_prefetch(), which sends the GetProperty request,
244 * then ecore_x_dnd_type_get_fetch(), which gets the reply.
247 ecore_x_dnd_type_set(Ecore_X_Window window,
251 xcb_intern_atom_cookie_t cookie;
252 xcb_intern_atom_reply_t *reply;
253 Ecore_X_Atom *oldset = NULL;
254 Ecore_X_Atom *newset = NULL;
255 unsigned char *data = NULL;
256 unsigned char *old_data = NULL;
258 int i, j = 0, num = 0;
260 cookie = xcb_intern_atom_unchecked(_ecore_xcb_conn, 0,
263 atom = ecore_x_atom_get(type);
264 if (!ecore_x_window_prop_property_get(window, ECORE_X_ATOM_XDND_TYPE_LIST,
266 32, &old_data, &num))
268 reply = xcb_intern_atom_reply(_ecore_xcb_conn, cookie, NULL);
275 oldset = (Ecore_X_Atom *)old_data;
279 if (ecore_x_dnd_type_isset(window, type))
282 reply = xcb_intern_atom_reply(_ecore_xcb_conn, cookie, NULL);
289 data = calloc(num + 1, sizeof(Ecore_X_Atom));
293 reply = xcb_intern_atom_reply(_ecore_xcb_conn, cookie, NULL);
300 newset = (Ecore_X_Atom *)data;
302 for (i = 0; i < num; i++)
303 newset[i + 1] = oldset[i];
304 /* prepend the new type */
306 reply = xcb_intern_atom_reply(_ecore_xcb_conn, cookie, NULL);
313 newset[0] = reply->atom;
316 ecore_x_window_prop_property_set(window,
317 ECORE_X_ATOM_XDND_TYPE_LIST,
323 if (!ecore_x_dnd_type_isset(window, type))
329 newset = calloc(num - 1, sizeof(Ecore_X_Atom));
336 data = (unsigned char *)newset;
337 for (i = 0; i < num; i++)
338 if (oldset[i] != atom)
339 newset[j++] = oldset[i];
341 ecore_x_window_prop_property_set(window,
342 ECORE_X_ATOM_XDND_TYPE_LIST,
349 } /* ecore_x_dnd_type_set */
351 /* FIXME: round trips, but I don't think we can do much, here */
355 * @param window Unused.
356 * @param types The types to set
357 * @param num_types The number of types
361 * To use this function, you must call before, and in order,
362 * ecore_x_dnd_type_get_prefetch(), which sends the GetProperty request,
363 * then ecore_x_dnd_type_get_fetch(), which gets the reply.
366 ecore_x_dnd_types_set(Ecore_X_Window window,
368 unsigned int num_types)
370 Ecore_X_Atom *newset = NULL;
376 ecore_x_window_prop_property_del(window, ECORE_X_ATOM_XDND_TYPE_LIST);
380 xcb_intern_atom_cookie_t *cookies;
381 xcb_intern_atom_reply_t *reply;
383 cookies = (xcb_intern_atom_cookie_t *)malloc(sizeof(xcb_intern_atom_cookie_t));
387 for (i = 0; i < num_types; i++)
388 cookies[i] = xcb_intern_atom_unchecked(_ecore_xcb_conn, 0,
389 strlen(types[i]), types[i]);
390 data = calloc(num_types, sizeof(Ecore_X_Atom));
393 for (i = 0; i < num_types; i++)
395 reply = xcb_intern_atom_reply(_ecore_xcb_conn, cookies[i], NULL);
404 for (i = 0; i < num_types; i++)
406 reply = xcb_intern_atom_reply(_ecore_xcb_conn, cookies[i], NULL);
409 newset[i] = reply->atom;
413 newset[i] = XCB_NONE;
416 ecore_x_window_prop_property_set(window, ECORE_X_ATOM_XDND_TYPE_LIST,
417 ECORE_X_ATOM_ATOM, 32, data, num_types);
420 } /* ecore_x_dnd_types_set */
423 _ecore_x_dnd_source_get(void)
426 } /* _ecore_x_dnd_source_get */
429 _ecore_x_dnd_target_get(void)
432 } /* _ecore_x_dnd_target_get */
435 * Sends the GetProperty request.
436 * @param source Window whose properties are requested.
439 ecore_x_dnd_begin_prefetch(Ecore_X_Window source)
441 xcb_get_property_cookie_t cookie;
443 cookie = xcb_get_property_unchecked(_ecore_xcb_conn, 0,
444 source ? source : ((xcb_screen_t *)_ecore_xcb_screen)->root,
445 ECORE_X_ATOM_XDND_AWARE,
448 _ecore_xcb_cookie_cache(cookie.sequence);
449 } /* ecore_x_dnd_begin_prefetch */
452 * Gets the reply of the GetProperty request sent by ecore_x_dnd_begin_prefetch().
455 ecore_x_dnd_begin_fetch(void)
457 xcb_get_property_cookie_t cookie;
458 xcb_get_property_reply_t *reply;
460 cookie.sequence = _ecore_xcb_cookie_get();
461 reply = xcb_get_property_reply(_ecore_xcb_conn, cookie, NULL);
462 _ecore_xcb_reply_cache(reply);
463 } /* ecore_x_dnd_begin_fetch */
465 /* FIXME: round trip */
469 * @param source Unused.
470 * @param data The data.
471 * @param size The size of the data.
472 * @return 0 on failure, 1 otherwise.
474 * Begins the DnD. Returns 0 on failure, 1 otherwise.
476 * To use this function, you must call before, and in order,
477 * ecore_x_dnd_begin_prefetch(), which sends the GetProperty request,
478 * then ecore_x_dnd_begin_fetch(), which gets the reply.
481 ecore_x_dnd_begin(Ecore_X_Window source,
485 ecore_x_selection_xdnd_prefetch();
486 if (!ecore_x_dnd_version_get(source))
488 ecore_x_selection_xdnd_fetch();
492 /* Take ownership of XdndSelection */
493 ecore_x_selection_xdnd_prefetch();
494 ecore_x_selection_xdnd_fetch();
495 if (!ecore_x_selection_xdnd_set(source, data, size))
498 _source->win = source;
499 ecore_x_window_ignore_set(_source->win, 1);
500 _source->state = ECORE_X_DND_SOURCE_DRAGGING;
501 _source->time = _ecore_xcb_event_last_time;
502 _source->prev.window = 0;
504 /* Default Accepted Action: ask */
505 _source->action = ECORE_X_ATOM_XDND_ACTION_COPY;
506 _source->accepted_action = XCB_NONE;
508 } /* ecore_x_dnd_begin */
511 ecore_x_dnd_drop(void)
517 xcb_client_message_event_t ev;
519 ev.response_type = XCB_CLIENT_MESSAGE;
521 ev.window = _source->dest;
523 if (_source->will_accept)
525 ev.type = ECORE_X_ATOM_XDND_DROP;
526 ev.data.data32[0] = _source->win;
527 ev.data.data32[1] = 0;
528 ev.data.data32[2] = _source->time;
529 xcb_send_event(_ecore_xcb_conn, 0, _source->dest, 0, (const char *)&ev);
530 _source->state = ECORE_X_DND_SOURCE_DROPPED;
535 ev.type = ECORE_X_ATOM_XDND_LEAVE;
536 ev.data.data32[0] = _source->win;
537 ev.data.data32[1] = 0;
538 xcb_send_event(_ecore_xcb_conn, 0, _source->dest, 0, (const char *)&ev);
539 _source->state = ECORE_X_DND_SOURCE_IDLE;
544 /* Dropping on nothing */
545 ecore_x_selection_xdnd_clear();
546 _source->state = ECORE_X_DND_SOURCE_IDLE;
549 ecore_x_window_ignore_set(_source->win, 0);
551 _source->prev.window = 0;
552 _source->dest = XCB_NONE;
555 } /* ecore_x_dnd_drop */
558 ecore_x_dnd_send_status(int will_accept,
560 Ecore_X_Rectangle rectangle,
563 xcb_client_message_event_t ev;
565 if (_target->state == ECORE_X_DND_TARGET_IDLE)
568 _target->will_accept = will_accept;
570 ev.response_type = XCB_CLIENT_MESSAGE;
572 ev.window = _target->source;
573 ev.type = ECORE_X_ATOM_XDND_STATUS;
575 ev.data.data32[0] = _target->win;
576 ev.data.data32[1] = 0;
578 ev.data.data32[1] |= 0x1UL;
581 ev.data.data32[1] |= 0x2UL;
583 /* Set rectangle information */
584 ev.data.data32[2] = rectangle.x;
585 ev.data.data32[2] <<= 16;
586 ev.data.data32[2] |= rectangle.y;
587 ev.data.data32[3] = rectangle.width;
588 ev.data.data32[3] <<= 16;
589 ev.data.data32[3] |= rectangle.height;
593 ev.data.data32[4] = action;
594 _target->accepted_action = action;
598 ev.data.data32[4] = XCB_NONE;
599 _target->accepted_action = action;
602 xcb_send_event(_ecore_xcb_conn, 0, _target->source, 0, (const char *)&ev);
603 } /* ecore_x_dnd_send_status */
606 ecore_x_dnd_send_finished(void)
608 xcb_client_message_event_t ev;
610 if (_target->state == ECORE_X_DND_TARGET_IDLE)
613 ev.response_type = XCB_CLIENT_MESSAGE;
615 ev.window = _target->source;
616 ev.type = ECORE_X_ATOM_XDND_FINISHED;
618 ev.data.data32[0] = _target->win;
619 ev.data.data32[1] = 0;
620 ev.data.data32[2] = 0;
621 if (_target->will_accept)
623 ev.data.data32[1] |= 0x1UL;
624 ev.data.data32[2] = _target->accepted_action;
627 xcb_send_event(_ecore_xcb_conn, 0, _target->source, 0, (const char *)&ev);
629 _target->state = ECORE_X_DND_TARGET_IDLE;
630 } /* ecore_x_dnd_send_finished */
633 ecore_x_dnd_source_action_set(Ecore_X_Atom action)
635 _source->action = action;
636 if (_source->prev.window)
637 _ecore_x_dnd_drag(_source->prev.window, _source->prev.x, _source->prev.y);
638 } /* ecore_x_dnd_source_action_set */
641 ecore_x_dnd_source_action_get(void)
643 return _source->action;
644 } /* ecore_x_dnd_source_action_get */
647 _ecore_x_dnd_drag(Ecore_X_Window root,
651 xcb_client_message_event_t ev;
653 Ecore_X_Window *skip;
656 if (_source->state != ECORE_X_DND_SOURCE_DRAGGING)
659 ev.response_type = XCB_CLIENT_MESSAGE;
662 /* Attempt to find a DND-capable window under the cursor */
663 skip = ecore_x_window_ignore_list(&num);
664 // win = ecore_x_window_at_xy_with_skip_get(x, y, skip, num);
665 win = ecore_x_window_shadow_tree_at_xy_with_skip_get(root, x, y, skip, num);
668 xcb_query_tree_cookie_t cookie_tree;
669 xcb_query_tree_reply_t *reply_tree;
671 ecore_x_dnd_version_get_prefetch(win);
672 cookie_tree = xcb_query_tree_unchecked(_ecore_xcb_conn, win);
674 ecore_x_dnd_version_get_fetch();
675 /* We found the correct window ? */
676 if (ecore_x_dnd_version_get(win))
678 reply_tree = xcb_query_tree_reply(_ecore_xcb_conn, cookie_tree, NULL);
685 reply_tree = xcb_query_tree_reply(_ecore_xcb_conn, cookie_tree, NULL);
688 win = reply_tree->parent;
693 /* Send XdndLeave to current destination window if we have left it */
694 if ((_source->dest) && (win != _source->dest))
696 ev.window = _source->dest;
697 ev.type = ECORE_X_ATOM_XDND_LEAVE;
698 ev.data.data32[0] = _source->win;
699 ev.data.data32[1] = 0;
701 xcb_send_event(_ecore_xcb_conn, 0, _source->dest, 0, (const char *)&ev);
702 _source->suppress = 0;
712 ecore_x_dnd_version_get_prefetch(win);
713 ecore_x_dnd_type_get_prefetch(_source->win);
715 ecore_x_dnd_version_get_fetch();
716 if (!ecore_x_dnd_version_get(win))
718 ecore_x_dnd_type_get_fetch();
722 _source->version = MIN(ECORE_X_DND_VERSION,
723 ecore_x_dnd_version_get(win));
724 if (win != _source->dest)
731 ecore_x_dnd_type_get_fetch();
732 if (!ecore_x_window_prop_property_get(_source->win,
733 ECORE_X_ATOM_XDND_TYPE_LIST,
738 types = (Ecore_X_Atom *)data;
740 /* Entered new window, send XdndEnter */
742 ev.type = ECORE_X_ATOM_XDND_ENTER;
743 ev.data.data32[0] = _source->win;
744 ev.data.data32[1] = 0;
746 ev.data.data32[1] |= 0x1UL;
748 ev.data.data32[1] &= 0xfffffffeUL;
750 ev.data.data32[1] |= ((unsigned long)_source->version) << 24;
752 for (i = 2; i < 5; i++)
753 ev.data.data32[i] = 0;
754 for (i = 0; i < MIN(num, 3); ++i)
755 ev.data.data32[i + 2] = types[i];
757 xcb_send_event(_ecore_xcb_conn, 0, win, 0, (const char *)&ev);
758 _source->await_status = 0;
759 _source->will_accept = 0;
762 ecore_x_dnd_type_get_fetch();
764 /* Determine if we're still in the rectangle from the last status */
765 x1 = _source->rectangle.x;
766 x2 = _source->rectangle.x + _source->rectangle.width;
767 y1 = _source->rectangle.y;
768 y2 = _source->rectangle.y + _source->rectangle.height;
770 if ((!_source->await_status) ||
771 (!_source->suppress) ||
772 ((x < x1) || (x > x2) || (y < y1) || (y > y2)))
775 ev.type = ECORE_X_ATOM_XDND_POSITION;
776 ev.data.data32[0] = _source->win;
777 ev.data.data32[1] = 0; /* Reserved */
778 ev.data.data32[2] = ((x << 16) & 0xffff0000) | (y & 0xffff);
779 ev.data.data32[3] = _source->time; /* Version 1 */
780 ev.data.data32[4] = _source->action; /* Version 2, Needs to be pre-set */
781 xcb_send_event(_ecore_xcb_conn, 0, win, 0, (const char *)&ev);
783 _source->await_status = 1;
789 _source->prev.window = root;
791 } /* _ecore_x_dnd_drag */