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 *, Ecore_X_Xdnd_Position *);
32 static void *_posupdatedata;
35 _ecore_x_dnd_init(void)
37 if (!_ecore_x_dnd_init_count)
39 _source = calloc(1, sizeof(Ecore_X_DND_Source));
41 _source->version = ECORE_X_DND_VERSION;
44 _source->state = ECORE_X_DND_SOURCE_IDLE;
45 _source->prev.window = 0;
47 _target = calloc(1, sizeof(Ecore_X_DND_Target));
55 _target->source = None;
56 _target->state = ECORE_X_DND_TARGET_IDLE;
58 ECORE_X_EVENT_XDND_ENTER = ecore_event_type_new();
59 ECORE_X_EVENT_XDND_POSITION = ecore_event_type_new();
60 ECORE_X_EVENT_XDND_STATUS = ecore_event_type_new();
61 ECORE_X_EVENT_XDND_LEAVE = ecore_event_type_new();
62 ECORE_X_EVENT_XDND_DROP = ecore_event_type_new();
63 ECORE_X_EVENT_XDND_FINISHED = ecore_event_type_new();
66 _ecore_x_dnd_init_count++;
67 } /* _ecore_x_dnd_init */
70 _ecore_x_dnd_shutdown(void)
72 _ecore_x_dnd_init_count--;
73 if (_ecore_x_dnd_init_count > 0)
86 _ecore_x_dnd_init_count = 0;
87 } /* _ecore_x_dnd_shutdown */
90 _ecore_x_dnd_converter_copy(char *target __UNUSED__,
95 Ecore_X_Atom *tprop __UNUSED__,
96 int *count __UNUSED__)
98 XTextProperty text_prop;
100 XICCEncodingStyle style = XTextStyle;
105 mystr = calloc(1, size + 1);
109 memcpy(mystr, data, size);
111 if (XmbTextListToTextProperty(_ecore_x_disp, &mystr, 1, style,
112 &text_prop) == Success)
114 int bufsize = strlen((char *)text_prop.value) + 1;
115 *data_ret = malloc(bufsize);
121 memcpy(*data_ret, text_prop.value, bufsize);
123 XFree(text_prop.value);
132 } /* _ecore_x_dnd_converter_copy */
135 ecore_x_dnd_aware_set(Ecore_X_Window win, Eina_Bool on)
137 Ecore_X_Atom prop_data = ECORE_X_DND_VERSION;
139 LOGFN(__FILE__, __LINE__, __FUNCTION__);
141 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_AWARE,
142 XA_ATOM, 32, &prop_data, 1);
144 ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_AWARE);
145 } /* ecore_x_dnd_aware_set */
148 ecore_x_dnd_version_get(Ecore_X_Window win)
150 unsigned char *prop_data;
152 Version_Cache_Item *t;
154 LOGFN(__FILE__, __LINE__, __FUNCTION__);
155 // this looks hacky - and it is, but we need a way of caching info about
156 // a window while dragging, because we literally query this every mouse
157 // move and going to and from x multiple times per move is EXPENSIVE
158 // and slows things down, puts lots of load on x etc.
159 if (_source->state == ECORE_X_DND_SOURCE_DRAGGING)
164 for (i = 0; i < _version_cache_num; i++)
166 if (_version_cache[i].win == win)
167 return _version_cache[i].ver;
171 if (ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_AWARE,
172 XA_ATOM, 32, &prop_data, &num))
174 int version = (int)*prop_data;
176 if (_source->state == ECORE_X_DND_SOURCE_DRAGGING)
178 _version_cache_num++;
179 if (_version_cache_num > _version_cache_alloc)
180 _version_cache_alloc += 16;
182 t = realloc(_version_cache,
183 _version_cache_alloc *
184 sizeof(Version_Cache_Item));
187 _version_cache[_version_cache_num - 1].win = win;
188 _version_cache[_version_cache_num - 1].ver = version;
194 if (_source->state == ECORE_X_DND_SOURCE_DRAGGING)
196 _version_cache_num++;
197 if (_version_cache_num > _version_cache_alloc)
198 _version_cache_alloc += 16;
200 t = realloc(_version_cache, _version_cache_alloc *
201 sizeof(Version_Cache_Item));
204 _version_cache[_version_cache_num - 1].win = win;
205 _version_cache[_version_cache_num - 1].ver = 0;
209 } /* ecore_x_dnd_version_get */
212 ecore_x_dnd_type_isset(Ecore_X_Window win, const char *type)
214 int num, i, ret = EINA_FALSE;
216 Ecore_X_Atom *atoms, atom;
218 LOGFN(__FILE__, __LINE__, __FUNCTION__);
219 if (!ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_TYPE_LIST,
220 XA_ATOM, 32, &data, &num))
223 atom = ecore_x_atom_get(type);
224 atoms = (Ecore_X_Atom *)data;
226 for (i = 0; i < num; ++i)
228 if (atom == atoms[i])
237 } /* ecore_x_dnd_type_isset */
240 ecore_x_dnd_type_set(Ecore_X_Window win, const char *type, Eina_Bool on)
243 Ecore_X_Atom *oldset = NULL, *newset = NULL;
244 int i, j = 0, num = 0;
245 unsigned char *data = NULL;
246 unsigned char *old_data = NULL;
248 LOGFN(__FILE__, __LINE__, __FUNCTION__);
249 atom = ecore_x_atom_get(type);
250 ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_TYPE_LIST,
251 XA_ATOM, 32, &old_data, &num);
252 oldset = (Ecore_X_Atom *)old_data;
254 LOGFN(__FILE__, __LINE__, __FUNCTION__);
257 if (ecore_x_dnd_type_isset(win, type))
263 newset = calloc(num + 1, sizeof(Ecore_X_Atom));
267 data = (unsigned char *)newset;
269 for (i = 0; i < num; i++)
270 newset[i + 1] = oldset[i];
271 /* prepend the new type */
274 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_TYPE_LIST,
275 XA_ATOM, 32, data, num + 1);
279 if (!ecore_x_dnd_type_isset(win, type))
285 newset = calloc(num - 1, sizeof(Ecore_X_Atom));
292 data = (unsigned char *)newset;
293 for (i = 0; i < num; i++)
294 if (oldset[i] != atom)
295 newset[j++] = oldset[i];
297 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_TYPE_LIST,
298 XA_ATOM, 32, data, num - 1);
303 } /* ecore_x_dnd_type_set */
306 ecore_x_dnd_types_set(Ecore_X_Window win,
308 unsigned int num_types)
310 Ecore_X_Atom *newset = NULL;
312 unsigned char *data = NULL;
314 LOGFN(__FILE__, __LINE__, __FUNCTION__);
316 ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_TYPE_LIST);
319 newset = calloc(num_types, sizeof(Ecore_X_Atom));
323 data = (unsigned char *)newset;
324 for (i = 0; i < num_types; i++)
326 newset[i] = ecore_x_atom_get(types[i]);
327 ecore_x_selection_converter_atom_add(newset[i],
328 _ecore_x_dnd_converter_copy);
330 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_TYPE_LIST,
331 XA_ATOM, 32, data, num_types);
334 } /* ecore_x_dnd_types_set */
337 ecore_x_dnd_actions_set(Ecore_X_Window win,
338 Ecore_X_Atom *actions,
339 unsigned int num_actions)
342 unsigned char *data = NULL;
344 LOGFN(__FILE__, __LINE__, __FUNCTION__);
346 ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_ACTION_LIST);
349 data = (unsigned char *)actions;
350 for (i = 0; i < num_actions; i++)
352 ecore_x_selection_converter_atom_add(actions[i],
353 _ecore_x_dnd_converter_copy);
355 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_ACTION_LIST,
356 XA_ATOM, 32, data, num_actions);
358 } /* ecore_x_dnd_actions_set */
361 * The DND position update cb is called Ecore_X sends a DND position to a
364 * It essentially mirrors some of the data sent in the position message.
365 * Generally this cb should be set just before position update is called.
366 * Please note well you need to look after your own data pointer if someone
367 * trashes you position update cb set.
369 * It is considered good form to clear this when the dnd event finishes.
371 * @param cb Callback to updated each time ecore_x sends a position update.
372 * @param data User data.
375 ecore_x_dnd_callback_pos_update_set(
376 void (*cb)(void *, Ecore_X_Xdnd_Position *data),
380 _posupdatedata = (void *)data; /* Discard the const early */
384 _ecore_x_dnd_source_get(void)
387 } /* _ecore_x_dnd_source_get */
390 _ecore_x_dnd_target_get(void)
393 } /* _ecore_x_dnd_target_get */
396 ecore_x_dnd_begin(Ecore_X_Window source, unsigned char *data, int size)
398 LOGFN(__FILE__, __LINE__, __FUNCTION__);
399 if (!ecore_x_dnd_version_get(source))
402 /* Take ownership of XdndSelection */
403 if (!ecore_x_selection_xdnd_set(source, data, size))
408 free(_version_cache);
409 _version_cache = NULL;
410 _version_cache_num = 0;
411 _version_cache_alloc = 0;
414 ecore_x_window_shadow_tree_flush();
416 _source->win = source;
417 ecore_x_window_ignore_set(_source->win, 1);
418 _source->state = ECORE_X_DND_SOURCE_DRAGGING;
419 _source->time = _ecore_x_event_last_time;
420 _source->prev.window = 0;
422 /* Default Accepted Action: move */
423 _source->action = ECORE_X_ATOM_XDND_ACTION_MOVE;
424 _source->accepted_action = None;
425 _source->dest = None;
428 } /* ecore_x_dnd_begin */
431 ecore_x_dnd_drop(void)
434 int status = EINA_FALSE;
436 LOGFN(__FILE__, __LINE__, __FUNCTION__);
439 xev.xany.type = ClientMessage;
440 xev.xany.display = _ecore_x_disp;
441 xev.xclient.format = 32;
442 xev.xclient.window = _source->dest;
444 if (_source->will_accept)
446 xev.xclient.message_type = ECORE_X_ATOM_XDND_DROP;
447 xev.xclient.data.l[0] = _source->win;
448 xev.xclient.data.l[1] = 0;
449 xev.xclient.data.l[2] = _source->time;
450 XSendEvent(_ecore_x_disp, _source->dest, False, 0, &xev);
451 _source->state = ECORE_X_DND_SOURCE_DROPPED;
456 xev.xclient.message_type = ECORE_X_ATOM_XDND_LEAVE;
457 xev.xclient.data.l[0] = _source->win;
458 xev.xclient.data.l[1] = 0;
459 XSendEvent(_ecore_x_disp, _source->dest, False, 0, &xev);
460 _source->state = ECORE_X_DND_SOURCE_IDLE;
465 /* Dropping on nothing */
466 ecore_x_selection_xdnd_clear();
467 _source->state = ECORE_X_DND_SOURCE_IDLE;
470 ecore_x_window_ignore_set(_source->win, 0);
472 _source->prev.window = 0;
475 } /* ecore_x_dnd_drop */
478 ecore_x_dnd_send_status(Eina_Bool will_accept,
480 Ecore_X_Rectangle rectangle,
485 if (_target->state == ECORE_X_DND_TARGET_IDLE)
488 LOGFN(__FILE__, __LINE__, __FUNCTION__);
489 memset(&xev, 0, sizeof(XEvent));
491 _target->will_accept = will_accept;
493 xev.xclient.type = ClientMessage;
494 xev.xclient.display = _ecore_x_disp;
495 xev.xclient.message_type = ECORE_X_ATOM_XDND_STATUS;
496 xev.xclient.format = 32;
497 xev.xclient.window = _target->source;
499 xev.xclient.data.l[0] = _target->win;
500 xev.xclient.data.l[1] = 0;
502 xev.xclient.data.l[1] |= 0x1UL;
505 xev.xclient.data.l[1] |= 0x2UL;
507 /* Set rectangle information */
508 xev.xclient.data.l[2] = rectangle.x;
509 xev.xclient.data.l[2] <<= 16;
510 xev.xclient.data.l[2] |= rectangle.y;
511 xev.xclient.data.l[3] = rectangle.width;
512 xev.xclient.data.l[3] <<= 16;
513 xev.xclient.data.l[3] |= rectangle.height;
517 xev.xclient.data.l[4] = action;
518 _target->accepted_action = action;
522 xev.xclient.data.l[4] = None;
523 _target->accepted_action = action;
526 XSendEvent(_ecore_x_disp, _target->source, False, 0, &xev);
527 } /* ecore_x_dnd_send_status */
530 ecore_x_dnd_send_finished(void)
534 if (_target->state == ECORE_X_DND_TARGET_IDLE)
537 LOGFN(__FILE__, __LINE__, __FUNCTION__);
538 xev.xany.type = ClientMessage;
539 xev.xany.display = _ecore_x_disp;
540 xev.xclient.message_type = ECORE_X_ATOM_XDND_FINISHED;
541 xev.xclient.format = 32;
542 xev.xclient.window = _target->source;
544 xev.xclient.data.l[0] = _target->win;
545 xev.xclient.data.l[1] = 0;
546 xev.xclient.data.l[2] = 0;
547 if (_target->will_accept)
549 xev.xclient.data.l[1] |= 0x1UL;
550 xev.xclient.data.l[2] = _target->accepted_action;
553 XSendEvent(_ecore_x_disp, _target->source, False, 0, &xev);
555 _target->state = ECORE_X_DND_TARGET_IDLE;
556 } /* ecore_x_dnd_send_finished */
559 ecore_x_dnd_source_action_set(Ecore_X_Atom action)
561 _source->action = action;
562 if (_source->prev.window)
563 _ecore_x_dnd_drag(_source->prev.window, _source->prev.x, _source->prev.y);
564 } /* ecore_x_dnd_source_action_set */
567 ecore_x_dnd_source_action_get(void)
569 return _source->action;
570 } /* ecore_x_dnd_source_action_get */
573 _ecore_x_dnd_drag(Ecore_X_Window root, int x, int y)
577 Ecore_X_Window *skip;
578 Ecore_X_Xdnd_Position pos;
581 if (_source->state != ECORE_X_DND_SOURCE_DRAGGING)
584 /* Preinitialize XEvent struct */
585 memset(&xev, 0, sizeof(XEvent));
586 xev.xany.type = ClientMessage;
587 xev.xany.display = _ecore_x_disp;
588 xev.xclient.format = 32;
590 /* Attempt to find a DND-capable window under the cursor */
591 skip = ecore_x_window_ignore_list(&num);
592 // WARNING - this function is HEAVY. it goes to and from x a LOT walking the
593 // window tree - use the SHADOW version - makes a 1-off tree copy, then uses
595 // win = ecore_x_window_at_xy_with_skip_get(x, y, skip, num);
596 win = ecore_x_window_shadow_tree_at_xy_with_skip_get(root, x, y, skip, num);
598 // NOTE: This now uses the shadow version to find parent windows
599 // while ((win) && !(ecore_x_dnd_version_get(win)))
600 // win = ecore_x_window_parent_get(win);
601 while ((win) && !(ecore_x_dnd_version_get(win)))
602 win = ecore_x_window_shadow_parent_get(root, win);
604 /* Send XdndLeave to current destination window if we have left it */
605 if ((_source->dest) && (win != _source->dest))
607 xev.xclient.window = _source->dest;
608 xev.xclient.message_type = ECORE_X_ATOM_XDND_LEAVE;
609 xev.xclient.data.l[0] = _source->win;
610 xev.xclient.data.l[1] = 0;
612 XSendEvent(_ecore_x_disp, _source->dest, False, 0, &xev);
613 _source->suppress = 0;
620 _source->version = MIN(ECORE_X_DND_VERSION,
621 ecore_x_dnd_version_get(win));
622 if (win != _source->dest)
628 ecore_x_window_prop_property_get(_source->win,
629 ECORE_X_ATOM_XDND_TYPE_LIST,
634 types = (Ecore_X_Atom *)data;
636 /* Entered new window, send XdndEnter */
637 xev.xclient.window = win;
638 xev.xclient.message_type = ECORE_X_ATOM_XDND_ENTER;
639 xev.xclient.data.l[0] = _source->win;
640 xev.xclient.data.l[1] = 0;
642 xev.xclient.data.l[1] |= 0x1UL;
644 xev.xclient.data.l[1] &= 0xfffffffeUL;
646 xev.xclient.data.l[1] |= ((unsigned long)_source->version) << 24;
648 for (i = 2; i < 5; i++)
649 xev.xclient.data.l[i] = 0;
650 for (i = 0; i < MIN(num, 3); ++i)
651 xev.xclient.data.l[i + 2] = types[i];
653 XSendEvent(_ecore_x_disp, win, False, 0, &xev);
654 _source->await_status = 0;
655 _source->will_accept = 0;
658 /* Determine if we're still in the rectangle from the last status */
659 x1 = _source->rectangle.x;
660 x2 = _source->rectangle.x + _source->rectangle.width;
661 y1 = _source->rectangle.y;
662 y2 = _source->rectangle.y + _source->rectangle.height;
664 if ((!_source->await_status) ||
665 (!_source->suppress) ||
666 ((x < x1) || (x > x2) || (y < y1) || (y > y2)))
668 xev.xclient.window = win;
669 xev.xclient.message_type = ECORE_X_ATOM_XDND_POSITION;
670 xev.xclient.data.l[0] = _source->win;
671 xev.xclient.data.l[1] = 0; /* Reserved */
672 xev.xclient.data.l[2] = ((x << 16) & 0xffff0000) | (y & 0xffff);
673 xev.xclient.data.l[3] = _source->time; /* Version 1 */
674 xev.xclient.data.l[4] = _source->action; /* Version 2, Needs to be pre-set */
675 XSendEvent(_ecore_x_disp, win, False, 0, &xev);
677 _source->await_status = 1;
686 pos.prev = _source->dest;
687 _posupdatecb(_posupdatedata, &pos);
692 _source->prev.window = root;
696 } /* _ecore_x_dnd_drag */
700 /* vim:set ts=8 sw=3 sts=3 expandtab cino=>5n-2f0^-2{2(0W1st0 :*/