3 #endif /* ifdef HAVE_CONFIG_H */
9 #include "ecore_x_private.h"
11 #include "Ecore_X_Atoms.h"
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;
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;
24 typedef struct _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;
36 _ecore_x_dnd_init(void)
38 if (!_ecore_x_dnd_init_count)
40 _source = calloc(1, sizeof(Ecore_X_DND_Source));
42 _source->version = ECORE_X_DND_VERSION;
45 _source->state = ECORE_X_DND_SOURCE_IDLE;
46 _source->prev.window = 0;
48 _target = calloc(1, sizeof(Ecore_X_DND_Target));
56 _target->source = None;
57 _target->state = ECORE_X_DND_TARGET_IDLE;
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();
67 _ecore_x_dnd_init_count++;
71 _ecore_x_dnd_shutdown(void)
73 _ecore_x_dnd_init_count--;
74 if (_ecore_x_dnd_init_count > 0)
87 _ecore_x_dnd_init_count = 0;
91 _ecore_x_dnd_converter_copy(char *target EINA_UNUSED,
96 Ecore_X_Atom *tprop EINA_UNUSED,
97 int *count EINA_UNUSED)
99 XTextProperty text_prop;
101 XICCEncodingStyle style = XTextStyle;
106 mystr = calloc(1, size + 1);
110 memcpy(mystr, data, size);
112 if (XmbTextListToTextProperty(_ecore_x_disp, &mystr, 1, style,
113 &text_prop) == Success)
115 int bufsize = strlen((char *)text_prop.value) + 1;
116 if (_ecore_xlib_sync) ecore_x_sync();
117 *data_ret = malloc(bufsize);
123 memcpy(*data_ret, text_prop.value, bufsize);
125 XFree(text_prop.value);
131 if (_ecore_xlib_sync) ecore_x_sync();
138 ecore_x_dnd_aware_set(Ecore_X_Window win,
141 Ecore_X_Atom prop_data = ECORE_X_DND_VERSION;
143 LOGFN(__FILE__, __LINE__, __FUNCTION__);
145 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_AWARE,
146 XA_ATOM, 32, &prop_data, 1);
148 ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_AWARE);
152 ecore_x_dnd_version_get(Ecore_X_Window win)
154 unsigned char *prop_data;
156 Version_Cache_Item *t;
158 LOGFN(__FILE__, __LINE__, __FUNCTION__);
159 // this looks hacky - and it is, but we need a way of caching info about
160 // a window while dragging, because we literally query this every mouse
161 // move and going to and from x multiple times per move is EXPENSIVE
162 // and slows things down, puts lots of load on x etc.
163 if (_source->state == ECORE_X_DND_SOURCE_DRAGGING)
168 for (i = 0; i < _version_cache_num; i++)
170 if (_version_cache[i].win == win)
171 return _version_cache[i].ver;
175 if (ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_AWARE,
176 XA_ATOM, 32, &prop_data, &num))
178 int version = (int)*prop_data;
180 if (_source->state == ECORE_X_DND_SOURCE_DRAGGING)
182 _version_cache_num++;
183 if (_version_cache_num > _version_cache_alloc)
184 _version_cache_alloc += 16;
186 t = realloc(_version_cache,
187 _version_cache_alloc *
188 sizeof(Version_Cache_Item));
191 _version_cache[_version_cache_num - 1].win = win;
192 _version_cache[_version_cache_num - 1].ver = version;
198 if (_source->state == ECORE_X_DND_SOURCE_DRAGGING)
200 _version_cache_num++;
201 if (_version_cache_num > _version_cache_alloc)
202 _version_cache_alloc += 16;
204 t = realloc(_version_cache, _version_cache_alloc *
205 sizeof(Version_Cache_Item));
208 if (prop_data) free(prop_data);
213 _version_cache[_version_cache_num - 1].win = win;
214 _version_cache[_version_cache_num - 1].ver = 0;
217 if (prop_data) free(prop_data);
223 ecore_x_dnd_type_isset(Ecore_X_Window win,
226 int num, i, ret = EINA_FALSE;
228 Ecore_X_Atom *atoms, atom;
230 LOGFN(__FILE__, __LINE__, __FUNCTION__);
231 if (!ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_TYPE_LIST,
232 XA_ATOM, 32, &data, &num))
235 atom = ecore_x_atom_get(type);
236 atoms = (Ecore_X_Atom *)data;
238 for (i = 0; i < num; ++i)
240 if (atom == atoms[i])
247 if (data) free(data);
252 ecore_x_dnd_type_set(Ecore_X_Window win,
257 Ecore_X_Atom *oldset = NULL, *newset = NULL;
258 int i, j = 0, num = 0;
259 unsigned char *data = NULL;
260 unsigned char *old_data = NULL;
262 LOGFN(__FILE__, __LINE__, __FUNCTION__);
263 atom = ecore_x_atom_get(type);
265 LOGFN(__FILE__, __LINE__, __FUNCTION__);
268 if (ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_TYPE_LIST,
269 XA_ATOM, 32, &old_data, &num) > 0)
271 if (ecore_x_dnd_type_isset(win, type))
273 if (old_data) free(old_data);
278 newset = calloc(num + 1, sizeof(Ecore_X_Atom));
281 if (old_data) free(old_data);
285 oldset = (Ecore_X_Atom *)old_data;
286 data = (unsigned char *)newset;
288 for (i = 0; i < num; i++)
289 newset[i + 1] = oldset[i];
290 /* prepend the new type */
293 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_TYPE_LIST,
294 XA_ATOM, 32, data, num + 1);
298 if (ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_TYPE_LIST,
299 XA_ATOM, 32, &old_data, &num) == 0)
301 if (!ecore_x_dnd_type_isset(win, type))
303 if (old_data) free(old_data);
307 newset = calloc(num - 1, sizeof(Ecore_X_Atom));
310 if (old_data) free(old_data);
314 oldset = (Ecore_X_Atom *)old_data;
315 data = (unsigned char *)newset;
316 for (i = 0; i < num; i++)
317 if (oldset[i] != atom)
318 newset[j++] = oldset[i];
320 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_TYPE_LIST,
321 XA_ATOM, 32, data, num - 1);
324 if (oldset) XFree(oldset);
329 ecore_x_dnd_types_set(Ecore_X_Window win,
331 unsigned int num_types)
333 Ecore_X_Atom *newset = NULL;
335 unsigned char *data = NULL;
337 LOGFN(__FILE__, __LINE__, __FUNCTION__);
339 ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_TYPE_LIST);
342 newset = calloc(num_types, sizeof(Ecore_X_Atom));
346 data = (unsigned char *)newset;
347 for (i = 0; i < num_types; i++)
349 newset[i] = ecore_x_atom_get(types[i]);
350 ecore_x_selection_converter_atom_add(newset[i],
351 _ecore_x_dnd_converter_copy);
353 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_TYPE_LIST,
354 XA_ATOM, 32, data, num_types);
360 ecore_x_dnd_actions_set(Ecore_X_Window win,
361 Ecore_X_Atom *actions,
362 unsigned int num_actions)
365 unsigned char *data = NULL;
367 LOGFN(__FILE__, __LINE__, __FUNCTION__);
369 ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_ACTION_LIST);
372 data = (unsigned char *)actions;
373 for (i = 0; i < num_actions; i++)
375 ecore_x_selection_converter_atom_add(actions[i],
376 _ecore_x_dnd_converter_copy);
378 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_ACTION_LIST,
379 XA_ATOM, 32, data, num_actions);
384 * The DND position update cb is called Ecore_X sends a DND position to a
387 * It essentially mirrors some of the data sent in the position message.
388 * Generally this cb should be set just before position update is called.
389 * Please note well you need to look after your own data pointer if someone
390 * trashes you position update cb set.
392 * It is considered good form to clear this when the dnd event finishes.
394 * @param cb Callback to updated each time ecore_x sends a position update.
395 * @param data User data.
398 ecore_x_dnd_callback_pos_update_set(
400 Ecore_X_Xdnd_Position *data),
404 _posupdatedata = (void *)data; /* Discard the const early */
408 _ecore_x_dnd_source_get(void)
414 _ecore_x_dnd_target_get(void)
422 _ecore_x_dnd_begin(Ecore_X_Window source,
427 LOGFN(__FILE__, __LINE__, __FUNCTION__);
428 if (!ecore_x_dnd_version_get(source))
431 /* Take ownership of XdndSelection */
432 if (!ecore_x_selection_xdnd_set(source, data, size))
437 free(_version_cache);
438 _version_cache = NULL;
439 _version_cache_num = 0;
440 _version_cache_alloc = 0;
443 ecore_x_window_shadow_tree_flush();
445 _source->win = source;
446 if (!self) ecore_x_window_ignore_set(_source->win, 1);
447 _source->state = ECORE_X_DND_SOURCE_DRAGGING;
448 _source->time = _ecore_x_event_last_time;
449 _source->prev.window = 0;
451 /* Default Accepted Action: move */
452 _source->action = ECORE_X_ATOM_XDND_ACTION_MOVE;
453 _source->accepted_action = None;
454 _source->dest = None;
460 _ecore_x_dnd_drop(Eina_Bool self)
463 int status = EINA_FALSE;
465 LOGFN(__FILE__, __LINE__, __FUNCTION__);
468 xev.xany.type = ClientMessage;
469 xev.xany.display = _ecore_x_disp;
470 xev.xclient.format = 32;
471 xev.xclient.window = _source->dest;
473 if (_source->will_accept)
475 xev.xclient.message_type = ECORE_X_ATOM_XDND_DROP;
476 xev.xclient.data.l[0] = _source->win;
477 xev.xclient.data.l[1] = 0;
478 xev.xclient.data.l[2] = _source->time;
479 XSendEvent(_ecore_x_disp, _source->dest, False, 0, &xev);
480 if (_ecore_xlib_sync) ecore_x_sync();
481 _source->state = ECORE_X_DND_SOURCE_DROPPED;
486 xev.xclient.message_type = ECORE_X_ATOM_XDND_LEAVE;
487 xev.xclient.data.l[0] = _source->win;
488 xev.xclient.data.l[1] = 0;
489 XSendEvent(_ecore_x_disp, _source->dest, False, 0, &xev);
490 if (_ecore_xlib_sync) ecore_x_sync();
491 _source->state = ECORE_X_DND_SOURCE_IDLE;
496 /* Dropping on nothing */
497 ecore_x_selection_xdnd_clear();
498 _source->state = ECORE_X_DND_SOURCE_IDLE;
501 if (!self) ecore_x_window_ignore_set(_source->win, 0);
503 _source->prev.window = 0;
509 ecore_x_dnd_begin(Ecore_X_Window source,
513 return _ecore_x_dnd_begin(source, EINA_FALSE, data, size);
517 ecore_x_dnd_drop(void)
519 return _ecore_x_dnd_drop(EINA_FALSE);
523 ecore_x_dnd_self_begin(Ecore_X_Window source,
527 return _ecore_x_dnd_begin(source, EINA_TRUE, data, size);
531 ecore_x_dnd_self_drop(void)
533 return _ecore_x_dnd_drop(EINA_TRUE);
537 ecore_x_dnd_send_status(Eina_Bool will_accept,
539 Ecore_X_Rectangle rectangle,
544 EINA_SAFETY_ON_NULL_RETURN(_ecore_x_disp);
546 if (_target->state == ECORE_X_DND_TARGET_IDLE)
549 LOGFN(__FILE__, __LINE__, __FUNCTION__);
550 memset(&xev, 0, sizeof(XEvent));
552 _target->will_accept = will_accept;
554 xev.xclient.type = ClientMessage;
555 xev.xclient.display = _ecore_x_disp;
556 xev.xclient.message_type = ECORE_X_ATOM_XDND_STATUS;
557 xev.xclient.format = 32;
558 xev.xclient.window = _target->source;
560 xev.xclient.data.l[0] = _target->win;
561 xev.xclient.data.l[1] = 0;
563 xev.xclient.data.l[1] |= 0x1UL;
566 xev.xclient.data.l[1] |= 0x2UL;
568 /* Set rectangle information */
569 xev.xclient.data.l[2] = rectangle.x;
570 xev.xclient.data.l[2] <<= 16;
571 xev.xclient.data.l[2] |= rectangle.y;
572 xev.xclient.data.l[3] = rectangle.width;
573 xev.xclient.data.l[3] <<= 16;
574 xev.xclient.data.l[3] |= rectangle.height;
578 xev.xclient.data.l[4] = action;
579 _target->accepted_action = action;
583 xev.xclient.data.l[4] = None;
584 _target->accepted_action = action;
587 XSendEvent(_ecore_x_disp, _target->source, False, 0, &xev);
588 if (_ecore_xlib_sync) ecore_x_sync();
592 ecore_x_dnd_send_finished(void)
596 EINA_SAFETY_ON_NULL_RETURN(_ecore_x_disp);
598 if (_target->state == ECORE_X_DND_TARGET_IDLE)
601 LOGFN(__FILE__, __LINE__, __FUNCTION__);
602 xev.xany.type = ClientMessage;
603 xev.xany.display = _ecore_x_disp;
604 xev.xclient.message_type = ECORE_X_ATOM_XDND_FINISHED;
605 xev.xclient.format = 32;
606 xev.xclient.window = _target->source;
608 xev.xclient.data.l[0] = _target->win;
609 xev.xclient.data.l[1] = 0;
610 xev.xclient.data.l[2] = 0;
611 if (_target->will_accept)
613 xev.xclient.data.l[1] |= 0x1UL;
614 xev.xclient.data.l[2] = _target->accepted_action;
617 XSendEvent(_ecore_x_disp, _target->source, False, 0, &xev);
618 if (_ecore_xlib_sync) ecore_x_sync();
620 _target->state = ECORE_X_DND_TARGET_IDLE;
624 ecore_x_dnd_source_action_set(Ecore_X_Atom action)
626 _source->action = action;
627 if (_source->prev.window)
628 _ecore_x_dnd_drag(_source->prev.window, _source->prev.x, _source->prev.y);
632 ecore_x_dnd_source_action_get(void)
634 return _source->action;
638 _ecore_x_dnd_drag(Ecore_X_Window root,
644 Ecore_X_Window *skip;
645 Ecore_X_Xdnd_Position pos;
648 if (_source->state != ECORE_X_DND_SOURCE_DRAGGING)
651 /* Preinitialize XEvent struct */
652 memset(&xev, 0, sizeof(XEvent));
653 xev.xany.type = ClientMessage;
654 xev.xany.display = _ecore_x_disp;
655 xev.xclient.format = 32;
657 /* Attempt to find a DND-capable window under the cursor */
658 skip = ecore_x_window_ignore_list(&num);
659 // WARNING - this function is HEAVY. it goes to and from x a LOT walking the
660 // window tree - use the SHADOW version - makes a 1-off tree copy, then uses
662 // win = ecore_x_window_at_xy_with_skip_get(x, y, skip, num);
663 win = ecore_x_window_shadow_tree_at_xy_with_skip_get(root, x, y, skip, num);
664 // NOTE: This now uses the shadow version to find parent windows
665 // while ((win) && !(ecore_x_dnd_version_get(win)))
666 // win = ecore_x_window_parent_get(win);
667 while ((win) && !(ecore_x_dnd_version_get(win)))
668 win = ecore_x_window_shadow_parent_get(root, win);
670 /* Send XdndLeave to current destination window if we have left it */
671 if ((_source->dest) && (win != _source->dest))
673 xev.xclient.window = _source->dest;
674 xev.xclient.message_type = ECORE_X_ATOM_XDND_LEAVE;
675 xev.xclient.data.l[0] = _source->win;
676 xev.xclient.data.l[1] = 0;
678 XSendEvent(_ecore_x_disp, _source->dest, False, 0, &xev);
679 if (_ecore_xlib_sync) ecore_x_sync();
680 _source->suppress = 0;
687 _source->version = MIN(ECORE_X_DND_VERSION,
688 ecore_x_dnd_version_get(win));
689 if (win != _source->dest)
695 if (ecore_x_window_prop_property_get(_source->win,
696 ECORE_X_ATOM_XDND_TYPE_LIST,
702 types = (Ecore_X_Atom *)data;
704 /* Entered new window, send XdndEnter */
705 xev.xclient.window = win;
706 xev.xclient.message_type = ECORE_X_ATOM_XDND_ENTER;
707 xev.xclient.data.l[0] = _source->win;
708 xev.xclient.data.l[1] = 0;
710 xev.xclient.data.l[1] |= 0x1UL;
712 xev.xclient.data.l[1] &= 0xfffffffeUL;
714 xev.xclient.data.l[1] |= ((unsigned long)_source->version) << 24;
716 for (i = 2; i < 5; i++)
717 xev.xclient.data.l[i] = 0;
718 for (i = 0; i < MIN(num, 3); ++i)
719 xev.xclient.data.l[i + 2] = types[i];
721 XSendEvent(_ecore_x_disp, win, False, 0, &xev);
722 if (_ecore_xlib_sync) ecore_x_sync();
724 _source->await_status = 0;
725 _source->will_accept = 0;
728 /* Determine if we're still in the rectangle from the last status */
729 x1 = _source->rectangle.x;
730 x2 = _source->rectangle.x + _source->rectangle.width;
731 y1 = _source->rectangle.y;
732 y2 = _source->rectangle.y + _source->rectangle.height;
734 if ((!_source->await_status) ||
735 (!_source->suppress) ||
736 ((x < x1) || (x > x2) || (y < y1) || (y > y2)))
738 xev.xclient.window = win;
739 xev.xclient.message_type = ECORE_X_ATOM_XDND_POSITION;
740 xev.xclient.data.l[0] = _source->win;
741 xev.xclient.data.l[1] = 0; /* Reserved */
742 xev.xclient.data.l[2] = ((x << 16) & 0xffff0000) | (y & 0xffff);
743 xev.xclient.data.l[3] = _source->time; /* Version 1 */
744 xev.xclient.data.l[4] = _source->action; /* Version 2, Needs to be pre-set */
745 XSendEvent(_ecore_x_disp, win, False, 0, &xev);
746 if (_ecore_xlib_sync) ecore_x_sync();
748 _source->await_status = 1;
757 pos.prev = _source->dest;
758 _posupdatecb(_posupdatedata, &pos);
763 _source->prev.window = root;
768 ecore_x_dnd_abort(Ecore_X_Window xwin_source)
770 if (xwin_source == _source->win)
772 _source->will_accept = 0;
773 return ecore_x_dnd_self_drop();
775 else return EINA_FALSE;
778 /* vim:set ts=8 sw=3 sts=3 expandtab cino=>5n-2f0^-2{2(0W1st0 :*/