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));
40 _source->version = ECORE_X_DND_VERSION;
43 _source->state = ECORE_X_DND_SOURCE_IDLE;
44 _source->prev.window = 0;
46 _target = calloc(1, sizeof(Ecore_X_DND_Target));
48 _target->source = None;
49 _target->state = ECORE_X_DND_TARGET_IDLE;
51 ECORE_X_EVENT_XDND_ENTER = ecore_event_type_new();
52 ECORE_X_EVENT_XDND_POSITION = ecore_event_type_new();
53 ECORE_X_EVENT_XDND_STATUS = ecore_event_type_new();
54 ECORE_X_EVENT_XDND_LEAVE = ecore_event_type_new();
55 ECORE_X_EVENT_XDND_DROP = ecore_event_type_new();
56 ECORE_X_EVENT_XDND_FINISHED = ecore_event_type_new();
59 _ecore_x_dnd_init_count++;
60 } /* _ecore_x_dnd_init */
63 _ecore_x_dnd_shutdown(void)
65 _ecore_x_dnd_init_count--;
66 if (_ecore_x_dnd_init_count > 0)
79 _ecore_x_dnd_init_count = 0;
80 } /* _ecore_x_dnd_shutdown */
83 _ecore_x_dnd_converter_copy(char *target __UNUSED__,
88 Ecore_X_Atom *tprop __UNUSED__,
89 int *count __UNUSED__)
91 XTextProperty text_prop;
93 XICCEncodingStyle style = XTextStyle;
98 mystr = calloc(1, size + 1);
102 memcpy(mystr, data, size);
104 if (XmbTextListToTextProperty(_ecore_x_disp, &mystr, 1, style,
105 &text_prop) == Success)
107 int bufsize = strlen((char *)text_prop.value) + 1;
108 *data_ret = malloc(bufsize);
109 memcpy(*data_ret, text_prop.value, bufsize);
111 XFree(text_prop.value);
120 } /* _ecore_x_dnd_converter_copy */
123 ecore_x_dnd_aware_set(Ecore_X_Window win, int on)
125 Ecore_X_Atom prop_data = ECORE_X_DND_VERSION;
127 LOGFN(__FILE__, __LINE__, __FUNCTION__);
129 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_AWARE,
130 XA_ATOM, 32, &prop_data, 1);
132 ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_AWARE);
133 } /* ecore_x_dnd_aware_set */
136 ecore_x_dnd_version_get(Ecore_X_Window win)
138 unsigned char *prop_data;
141 LOGFN(__FILE__, __LINE__, __FUNCTION__);
142 // this looks hacky - and it is, but we need a way of caching info about
143 // a window while dragging, because we literally query this every mouse
144 // move and going to and from x multiple times per move is EXPENSIVE
145 // and slows things down, puts lots of load on x etc.
146 if (_source->state == ECORE_X_DND_SOURCE_DRAGGING)
151 for (i = 0; i < _version_cache_num; i++)
153 if (_version_cache[i].win == win)
154 return _version_cache[i].ver;
158 if (ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_AWARE,
159 XA_ATOM, 32, &prop_data, &num))
161 int version = (int)*prop_data;
163 if (_source->state == ECORE_X_DND_SOURCE_DRAGGING)
165 _version_cache_num++;
166 if (_version_cache_num > _version_cache_alloc)
167 _version_cache_alloc += 16;
169 _version_cache = realloc(_version_cache,
170 _version_cache_alloc *
171 sizeof(Version_Cache_Item));
172 _version_cache[_version_cache_num - 1].win = win;
173 _version_cache[_version_cache_num - 1].ver = version;
179 if (_source->state == ECORE_X_DND_SOURCE_DRAGGING)
181 _version_cache_num++;
182 if (_version_cache_num > _version_cache_alloc)
183 _version_cache_alloc += 16;
186 realloc(_version_cache, _version_cache_alloc *
187 sizeof(Version_Cache_Item));
188 _version_cache[_version_cache_num - 1].win = win;
189 _version_cache[_version_cache_num - 1].ver = 0;
193 } /* ecore_x_dnd_version_get */
196 ecore_x_dnd_type_isset(Ecore_X_Window win, const char *type)
200 Ecore_X_Atom *atoms, atom;
202 LOGFN(__FILE__, __LINE__, __FUNCTION__);
203 if (!ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_TYPE_LIST,
204 XA_ATOM, 32, &data, &num))
207 atom = ecore_x_atom_get(type);
208 atoms = (Ecore_X_Atom *)data;
210 for (i = 0; i < num; ++i)
212 if (atom == atoms[i])
221 } /* ecore_x_dnd_type_isset */
224 ecore_x_dnd_type_set(Ecore_X_Window win, const char *type, int on)
227 Ecore_X_Atom *oldset = NULL, *newset = NULL;
228 int i, j = 0, num = 0;
229 unsigned char *data = NULL;
230 unsigned char *old_data = NULL;
232 LOGFN(__FILE__, __LINE__, __FUNCTION__);
233 atom = ecore_x_atom_get(type);
234 ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_TYPE_LIST,
235 XA_ATOM, 32, &old_data, &num);
236 oldset = (Ecore_X_Atom *)old_data;
238 LOGFN(__FILE__, __LINE__, __FUNCTION__);
241 if (ecore_x_dnd_type_isset(win, type))
247 newset = calloc(num + 1, sizeof(Ecore_X_Atom));
251 data = (unsigned char *)newset;
253 for (i = 0; i < num; i++)
254 newset[i + 1] = oldset[i];
255 /* prepend the new type */
258 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_TYPE_LIST,
259 XA_ATOM, 32, data, num + 1);
263 if (!ecore_x_dnd_type_isset(win, type))
269 newset = calloc(num - 1, sizeof(Ecore_X_Atom));
276 data = (unsigned char *)newset;
277 for (i = 0; i < num; i++)
278 if (oldset[i] != atom)
279 newset[j++] = oldset[i];
281 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_TYPE_LIST,
282 XA_ATOM, 32, data, num - 1);
287 } /* ecore_x_dnd_type_set */
290 ecore_x_dnd_types_set(Ecore_X_Window win,
292 unsigned int num_types)
294 Ecore_X_Atom *newset = NULL;
296 unsigned char *data = NULL;
298 LOGFN(__FILE__, __LINE__, __FUNCTION__);
300 ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_TYPE_LIST);
303 newset = calloc(num_types, sizeof(Ecore_X_Atom));
307 data = (unsigned char *)newset;
308 for (i = 0; i < num_types; i++)
310 newset[i] = ecore_x_atom_get(types[i]);
311 ecore_x_selection_converter_atom_add(newset[i],
312 _ecore_x_dnd_converter_copy);
314 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_TYPE_LIST,
315 XA_ATOM, 32, data, num_types);
318 } /* ecore_x_dnd_types_set */
321 ecore_x_dnd_actions_set(Ecore_X_Window win,
322 Ecore_X_Atom *actions,
323 unsigned int num_actions)
326 unsigned char *data = NULL;
328 LOGFN(__FILE__, __LINE__, __FUNCTION__);
330 ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_ACTION_LIST);
333 data = (unsigned char *)actions;
334 for (i = 0; i < num_actions; i++)
336 ecore_x_selection_converter_atom_add(actions[i],
337 _ecore_x_dnd_converter_copy);
339 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_ACTION_LIST,
340 XA_ATOM, 32, data, num_actions);
342 } /* ecore_x_dnd_actions_set */
345 _ecore_x_dnd_source_get(void)
348 } /* _ecore_x_dnd_source_get */
351 _ecore_x_dnd_target_get(void)
354 } /* _ecore_x_dnd_target_get */
357 ecore_x_dnd_begin(Ecore_X_Window source, unsigned char *data, int size)
359 LOGFN(__FILE__, __LINE__, __FUNCTION__);
360 if (!ecore_x_dnd_version_get(source))
363 /* Take ownership of XdndSelection */
364 if (!ecore_x_selection_xdnd_set(source, data, size))
369 free(_version_cache);
370 _version_cache = NULL;
371 _version_cache_num = 0;
372 _version_cache_alloc = 0;
375 ecore_x_window_shadow_tree_flush();
377 _source->win = source;
378 ecore_x_window_ignore_set(_source->win, 1);
379 _source->state = ECORE_X_DND_SOURCE_DRAGGING;
380 _source->time = _ecore_x_event_last_time;
381 _source->prev.window = 0;
383 /* Default Accepted Action: move */
384 _source->action = ECORE_X_ATOM_XDND_ACTION_MOVE;
385 _source->accepted_action = None;
386 _source->dest = None;
389 } /* ecore_x_dnd_begin */
392 ecore_x_dnd_drop(void)
397 LOGFN(__FILE__, __LINE__, __FUNCTION__);
400 xev.xany.type = ClientMessage;
401 xev.xany.display = _ecore_x_disp;
402 xev.xclient.format = 32;
403 xev.xclient.window = _source->dest;
405 if (_source->will_accept)
407 xev.xclient.message_type = ECORE_X_ATOM_XDND_DROP;
408 xev.xclient.data.l[0] = _source->win;
409 xev.xclient.data.l[1] = 0;
410 xev.xclient.data.l[2] = _source->time;
411 XSendEvent(_ecore_x_disp, _source->dest, False, 0, &xev);
412 _source->state = ECORE_X_DND_SOURCE_DROPPED;
417 xev.xclient.message_type = ECORE_X_ATOM_XDND_LEAVE;
418 xev.xclient.data.l[0] = _source->win;
419 xev.xclient.data.l[1] = 0;
420 XSendEvent(_ecore_x_disp, _source->dest, False, 0, &xev);
421 _source->state = ECORE_X_DND_SOURCE_IDLE;
426 /* Dropping on nothing */
427 ecore_x_selection_xdnd_clear();
428 _source->state = ECORE_X_DND_SOURCE_IDLE;
431 ecore_x_window_ignore_set(_source->win, 0);
433 _source->prev.window = 0;
436 } /* ecore_x_dnd_drop */
439 ecore_x_dnd_send_status(int will_accept,
441 Ecore_X_Rectangle rectangle,
446 if (_target->state == ECORE_X_DND_TARGET_IDLE)
449 LOGFN(__FILE__, __LINE__, __FUNCTION__);
450 memset(&xev, 0, sizeof(XEvent));
452 _target->will_accept = will_accept;
454 xev.xclient.type = ClientMessage;
455 xev.xclient.display = _ecore_x_disp;
456 xev.xclient.message_type = ECORE_X_ATOM_XDND_STATUS;
457 xev.xclient.format = 32;
458 xev.xclient.window = _target->source;
460 xev.xclient.data.l[0] = _target->win;
461 xev.xclient.data.l[1] = 0;
463 xev.xclient.data.l[1] |= 0x1UL;
466 xev.xclient.data.l[1] |= 0x2UL;
468 /* Set rectangle information */
469 xev.xclient.data.l[2] = rectangle.x;
470 xev.xclient.data.l[2] <<= 16;
471 xev.xclient.data.l[2] |= rectangle.y;
472 xev.xclient.data.l[3] = rectangle.width;
473 xev.xclient.data.l[3] <<= 16;
474 xev.xclient.data.l[3] |= rectangle.height;
478 xev.xclient.data.l[4] = action;
479 _target->accepted_action = action;
483 xev.xclient.data.l[4] = None;
484 _target->accepted_action = action;
487 XSendEvent(_ecore_x_disp, _target->source, False, 0, &xev);
488 } /* ecore_x_dnd_send_status */
491 ecore_x_dnd_send_finished(void)
495 if (_target->state == ECORE_X_DND_TARGET_IDLE)
498 LOGFN(__FILE__, __LINE__, __FUNCTION__);
499 xev.xany.type = ClientMessage;
500 xev.xany.display = _ecore_x_disp;
501 xev.xclient.message_type = ECORE_X_ATOM_XDND_FINISHED;
502 xev.xclient.format = 32;
503 xev.xclient.window = _target->source;
505 xev.xclient.data.l[0] = _target->win;
506 xev.xclient.data.l[1] = 0;
507 xev.xclient.data.l[2] = 0;
508 if (_target->will_accept)
510 xev.xclient.data.l[1] |= 0x1UL;
511 xev.xclient.data.l[2] = _target->accepted_action;
514 XSendEvent(_ecore_x_disp, _target->source, False, 0, &xev);
516 _target->state = ECORE_X_DND_TARGET_IDLE;
517 } /* ecore_x_dnd_send_finished */
520 ecore_x_dnd_source_action_set(Ecore_X_Atom action)
522 _source->action = action;
523 if (_source->prev.window)
524 _ecore_x_dnd_drag(_source->prev.window, _source->prev.x, _source->prev.y);
525 } /* ecore_x_dnd_source_action_set */
528 ecore_x_dnd_source_action_get(void)
530 return _source->action;
531 } /* ecore_x_dnd_source_action_get */
534 * The DND position update cb is called Ecore_X sends a DND position to a
537 * It essentially mirrors some of the data sent in the position message.
538 * Generally this cb should be set just before position update is called.
539 * Please note well you need to look after your own data pointer if someone
540 * trashes you position update cb set.
542 * It is considered good form to clear this when the dnd event finishes.
544 * @param cb Callback to updated each time ecore_x sends a position update.
545 * @param data User data.
549 ecore_x_dnd_callback_pos_update_set(
550 void (*cb)(void *, Ecore_X_Xdnd_Position *data),
554 _posupdatedata = (void *)data; /* Discard the const early */
558 _ecore_x_dnd_drag(Ecore_X_Window root, int x, int y)
562 Ecore_X_Window *skip;
563 Ecore_X_Xdnd_Position pos;
566 if (_source->state != ECORE_X_DND_SOURCE_DRAGGING)
569 /* Preinitialize XEvent struct */
570 memset(&xev, 0, sizeof(XEvent));
571 xev.xany.type = ClientMessage;
572 xev.xany.display = _ecore_x_disp;
573 xev.xclient.format = 32;
575 /* Attempt to find a DND-capable window under the cursor */
576 skip = ecore_x_window_ignore_list(&num);
577 // WARNING - this function is HEAVY. it goes to and from x a LOT walking the
578 // window tree - use the SHADOW version - makes a 1-off tree copy, then uses
580 // win = ecore_x_window_at_xy_with_skip_get(x, y, skip, num);
581 win = ecore_x_window_shadow_tree_at_xy_with_skip_get(root, x, y, skip, num);
583 // NOTE: This now uses the shadow version to find parent windows
584 // while ((win) && !(ecore_x_dnd_version_get(win)))
585 // win = ecore_x_window_parent_get(win);
586 while ((win) && !(ecore_x_dnd_version_get(win)))
587 win = ecore_x_window_shadow_parent_get(root, win);
589 /* Send XdndLeave to current destination window if we have left it */
590 if ((_source->dest) && (win != _source->dest))
592 xev.xclient.window = _source->dest;
593 xev.xclient.message_type = ECORE_X_ATOM_XDND_LEAVE;
594 xev.xclient.data.l[0] = _source->win;
595 xev.xclient.data.l[1] = 0;
597 XSendEvent(_ecore_x_disp, _source->dest, False, 0, &xev);
598 _source->suppress = 0;
605 _source->version = MIN(ECORE_X_DND_VERSION,
606 ecore_x_dnd_version_get(win));
607 if (win != _source->dest)
613 ecore_x_window_prop_property_get(_source->win,
614 ECORE_X_ATOM_XDND_TYPE_LIST,
619 types = (Ecore_X_Atom *)data;
621 /* Entered new window, send XdndEnter */
622 xev.xclient.window = win;
623 xev.xclient.message_type = ECORE_X_ATOM_XDND_ENTER;
624 xev.xclient.data.l[0] = _source->win;
625 xev.xclient.data.l[1] = 0;
627 xev.xclient.data.l[1] |= 0x1UL;
629 xev.xclient.data.l[1] &= 0xfffffffeUL;
631 xev.xclient.data.l[1] |= ((unsigned long)_source->version) << 24;
633 for (i = 2; i < 5; i++)
634 xev.xclient.data.l[i] = 0;
635 for (i = 0; i < MIN(num, 3); ++i)
636 xev.xclient.data.l[i + 2] = types[i];
638 XSendEvent(_ecore_x_disp, win, False, 0, &xev);
639 _source->await_status = 0;
640 _source->will_accept = 0;
643 /* Determine if we're still in the rectangle from the last status */
644 x1 = _source->rectangle.x;
645 x2 = _source->rectangle.x + _source->rectangle.width;
646 y1 = _source->rectangle.y;
647 y2 = _source->rectangle.y + _source->rectangle.height;
649 if ((!_source->await_status) ||
650 (!_source->suppress) ||
651 ((x < x1) || (x > x2) || (y < y1) || (y > y2)))
653 xev.xclient.window = win;
654 xev.xclient.message_type = ECORE_X_ATOM_XDND_POSITION;
655 xev.xclient.data.l[0] = _source->win;
656 xev.xclient.data.l[1] = 0; /* Reserved */
657 xev.xclient.data.l[2] = ((x << 16) & 0xffff0000) | (y & 0xffff);
658 xev.xclient.data.l[3] = _source->time; /* Version 1 */
659 xev.xclient.data.l[4] = _source->action; /* Version 2, Needs to be pre-set */
660 XSendEvent(_ecore_x_disp, win, False, 0, &xev);
662 _source->await_status = 1;
671 pos.prev = _source->dest;
672 _posupdatecb(_posupdatedata, &pos);
678 _source->prev.window = root;
680 } /* _ecore_x_dnd_drag */