2 * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
5 #include "ecore_x_private.h"
7 #include "Ecore_X_Atoms.h"
9 EAPI int ECORE_X_EVENT_XDND_ENTER = 0;
10 EAPI int ECORE_X_EVENT_XDND_POSITION = 0;
11 EAPI int ECORE_X_EVENT_XDND_STATUS = 0;
12 EAPI int ECORE_X_EVENT_XDND_LEAVE = 0;
13 EAPI int ECORE_X_EVENT_XDND_DROP = 0;
14 EAPI int ECORE_X_EVENT_XDND_FINISHED = 0;
16 static Ecore_X_DND_Source *_source = NULL;
17 static Ecore_X_DND_Target *_target = NULL;
18 static int _ecore_x_dnd_init_count = 0;
20 typedef struct _Version_Cache_Item
25 static Version_Cache_Item *_version_cache = NULL;
26 static int _version_cache_num = 0, _version_cache_alloc = 0;
29 _ecore_x_dnd_init(void)
31 if (!_ecore_x_dnd_init_count)
33 _source = calloc(1, sizeof(Ecore_X_DND_Source));
34 _source->version = ECORE_X_DND_VERSION;
37 _source->state = ECORE_X_DND_SOURCE_IDLE;
39 _target = calloc(1, sizeof(Ecore_X_DND_Target));
41 _target->source = None;
42 _target->state = ECORE_X_DND_TARGET_IDLE;
44 ECORE_X_EVENT_XDND_ENTER = ecore_event_type_new();
45 ECORE_X_EVENT_XDND_POSITION = ecore_event_type_new();
46 ECORE_X_EVENT_XDND_STATUS = ecore_event_type_new();
47 ECORE_X_EVENT_XDND_LEAVE = ecore_event_type_new();
48 ECORE_X_EVENT_XDND_DROP = ecore_event_type_new();
49 ECORE_X_EVENT_XDND_FINISHED = ecore_event_type_new();
52 _ecore_x_dnd_init_count++;
56 _ecore_x_dnd_shutdown(void)
58 _ecore_x_dnd_init_count--;
59 if (_ecore_x_dnd_init_count > 0)
70 _ecore_x_dnd_init_count = 0;
74 _ecore_x_dnd_converter_copy(char *target, void *data, int size, void **data_ret, int *size_ret)
76 XTextProperty text_prop;
78 XICCEncodingStyle style = XTextStyle;
83 mystr = calloc(1, size + 1);
85 memcpy(mystr, data, size);
87 if (XmbTextListToTextProperty(_ecore_x_disp, &mystr, 1, style, &text_prop) == Success)
89 int bufsize = strlen(text_prop.value) + 1;
90 *data_ret = malloc(bufsize);
91 memcpy(*data_ret, text_prop.value, bufsize);
93 XFree(text_prop.value);
105 ecore_x_dnd_aware_set(Ecore_X_Window win, int on)
107 Ecore_X_Atom prop_data = ECORE_X_DND_VERSION;
110 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_AWARE,
111 XA_ATOM, 32, &prop_data, 1);
113 ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_AWARE);
117 ecore_x_dnd_version_get(Ecore_X_Window win)
119 unsigned char *prop_data;
122 // this looks hacky - and it is, but we need a way of caching info about
123 // a window while dragging, because we literally query this every mouse
124 // move and going to and from x multiple times per move is EXPENSIVE
125 // and slows things down, puts lots of load on x etc.
126 if (_source->state == ECORE_X_DND_SOURCE_DRAGGING)
132 for (i = 0; i < _version_cache_num; i++)
134 if (_version_cache[i].win == win)
135 return _version_cache[i].ver;
140 if (ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_AWARE,
141 XA_ATOM, 32, &prop_data, &num))
143 int version = (int) *prop_data;
145 if (_source->state == ECORE_X_DND_SOURCE_DRAGGING)
147 _version_cache_num++;
148 if (_version_cache_num > _version_cache_alloc)
149 _version_cache_alloc += 16;
150 _version_cache = realloc(_version_cache, _version_cache_alloc * sizeof(Version_Cache_Item));
151 _version_cache[_version_cache_num - 1].win = win;
152 _version_cache[_version_cache_num - 1].ver = version;
156 if (_source->state == ECORE_X_DND_SOURCE_DRAGGING)
158 _version_cache_num++;
159 if (_version_cache_num > _version_cache_alloc)
160 _version_cache_alloc += 16;
161 _version_cache = realloc(_version_cache, _version_cache_alloc * sizeof(Version_Cache_Item));
162 _version_cache[_version_cache_num - 1].win = win;
163 _version_cache[_version_cache_num - 1].ver = 0;
169 ecore_x_dnd_type_isset(Ecore_X_Window win, const char *type)
173 Ecore_X_Atom *atoms, atom;
175 if (!ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_TYPE_LIST,
176 XA_ATOM, 32, &data, &num))
179 atom = ecore_x_atom_get(type);
180 atoms = (Ecore_X_Atom *)data;
182 for (i = 0; i < num; ++i)
184 if (atom == atoms[i])
196 ecore_x_dnd_type_set(Ecore_X_Window win, const char *type, int on)
199 Ecore_X_Atom *oldset = NULL, *newset = NULL;
200 int i, j = 0, num = 0;
201 unsigned char *data = NULL;
202 unsigned char *old_data = NULL;
204 atom = ecore_x_atom_get(type);
205 ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_TYPE_LIST,
206 XA_ATOM, 32, &old_data, &num);
207 oldset = (Ecore_X_Atom *)old_data;
211 if (ecore_x_dnd_type_isset(win, type))
216 newset = calloc(num + 1, sizeof(Ecore_X_Atom));
218 data = (unsigned char *)newset;
220 for (i = 0; i < num; i++)
221 newset[i + 1] = oldset[i];
222 /* prepend the new type */
225 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_TYPE_LIST,
226 XA_ATOM, 32, data, num + 1);
230 if (!ecore_x_dnd_type_isset(win, type))
235 newset = calloc(num - 1, sizeof(Ecore_X_Atom));
241 data = (unsigned char *)newset;
242 for (i = 0; i < num; i++)
243 if (oldset[i] != atom)
244 newset[j++] = oldset[i];
246 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_TYPE_LIST,
247 XA_ATOM, 32, data, num - 1);
254 ecore_x_dnd_types_set(Ecore_X_Window win, char **types, unsigned int num_types)
256 Ecore_X_Atom *newset = NULL;
258 unsigned char *data = NULL;
262 ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_TYPE_LIST);
266 newset = calloc(num_types, sizeof(Ecore_X_Atom));
268 data = (unsigned char *)newset;
269 for (i = 0; i < num_types; i++)
271 newset[i] = ecore_x_atom_get(types[i]);
272 ecore_x_selection_converter_atom_add(newset[i], _ecore_x_dnd_converter_copy);
274 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_TYPE_LIST,
275 XA_ATOM, 32, data, num_types);
281 ecore_x_dnd_actions_set(Ecore_X_Window win, Ecore_X_Atom *actions, unsigned int num_actions)
284 unsigned char *data = NULL;
288 ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_ACTION_LIST);
292 data = (unsigned char *)actions;
293 for (i = 0; i < num_actions; i++)
295 ecore_x_selection_converter_atom_add(actions[i], _ecore_x_dnd_converter_copy);
297 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_ACTION_LIST,
298 XA_ATOM, 32, data, num_actions);
303 _ecore_x_dnd_source_get(void)
309 _ecore_x_dnd_target_get(void)
315 ecore_x_dnd_begin(Ecore_X_Window source, unsigned char *data, int size)
318 if (!ecore_x_dnd_version_get(source))
321 /* Take ownership of XdndSelection */
322 if (!ecore_x_selection_xdnd_set(source, data, size))
327 free(_version_cache);
328 _version_cache = NULL;
329 _version_cache_num = 0;
330 _version_cache_alloc = 0;
332 ecore_x_window_shadow_tree_flush();
334 _source->win = source;
335 ecore_x_window_ignore_set(_source->win, 1);
336 _source->state = ECORE_X_DND_SOURCE_DRAGGING;
337 _source->time = _ecore_x_event_last_time;
339 /* Default Accepted Action: move */
340 _source->action = ECORE_X_ATOM_XDND_ACTION_MOVE;
341 _source->accepted_action = None;
342 _source->dest = None;
348 ecore_x_dnd_drop(void)
355 xev.xany.type = ClientMessage;
356 xev.xany.display = _ecore_x_disp;
357 xev.xclient.format = 32;
358 xev.xclient.window = _source->dest;
360 if (_source->will_accept)
362 xev.xclient.message_type = ECORE_X_ATOM_XDND_DROP;
363 xev.xclient.data.l[0] = _source->win;
364 xev.xclient.data.l[1] = 0;
365 xev.xclient.data.l[2] = _source->time;
366 XSendEvent(_ecore_x_disp, _source->dest, False, 0, &xev);
367 _source->state = ECORE_X_DND_SOURCE_DROPPED;
372 xev.xclient.message_type = ECORE_X_ATOM_XDND_LEAVE;
373 xev.xclient.data.l[0] = _source->win;
374 xev.xclient.data.l[1] = 0;
375 XSendEvent(_ecore_x_disp, _source->dest, False, 0, &xev);
376 _source->state = ECORE_X_DND_SOURCE_IDLE;
381 /* Dropping on nothing */
382 ecore_x_selection_xdnd_clear();
383 _source->state = ECORE_X_DND_SOURCE_IDLE;
385 ecore_x_window_ignore_set(_source->win, 0);
391 ecore_x_dnd_send_status(int will_accept, int suppress, Ecore_X_Rectangle rectangle, Ecore_X_Atom action)
395 if (_target->state == ECORE_X_DND_TARGET_IDLE)
398 memset(&xev, 0, sizeof(XEvent));
400 _target->will_accept = will_accept;
402 xev.xclient.type = ClientMessage;
403 xev.xclient.display = _ecore_x_disp;
404 xev.xclient.message_type = ECORE_X_ATOM_XDND_STATUS;
405 xev.xclient.format = 32;
406 xev.xclient.window = _target->source;
408 xev.xclient.data.l[0] = _target->win;
409 xev.xclient.data.l[1] = 0;
411 xev.xclient.data.l[1] |= 0x1UL;
413 xev.xclient.data.l[1] |= 0x2UL;
415 /* Set rectangle information */
416 xev.xclient.data.l[2] = rectangle.x;
417 xev.xclient.data.l[2] <<= 16;
418 xev.xclient.data.l[2] |= rectangle.y;
419 xev.xclient.data.l[3] = rectangle.width;
420 xev.xclient.data.l[3] <<= 16;
421 xev.xclient.data.l[3] |= rectangle.height;
425 xev.xclient.data.l[4] = action;
426 _target->accepted_action = action;
430 xev.xclient.data.l[4] = None;
431 _target->accepted_action = action;
434 XSendEvent(_ecore_x_disp, _target->source, False, 0, &xev);
438 ecore_x_dnd_send_finished(void)
442 if (_target->state == ECORE_X_DND_TARGET_IDLE)
445 xev.xany.type = ClientMessage;
446 xev.xany.display = _ecore_x_disp;
447 xev.xclient.message_type = ECORE_X_ATOM_XDND_FINISHED;
448 xev.xclient.format = 32;
449 xev.xclient.window = _target->source;
451 xev.xclient.data.l[0] = _target->win;
452 xev.xclient.data.l[1] = 0;
453 xev.xclient.data.l[2] = 0;
454 if (_target->will_accept)
456 xev.xclient.data.l[1] |= 0x1UL;
457 xev.xclient.data.l[2] = _target->accepted_action;
459 XSendEvent(_ecore_x_disp, _target->source, False, 0, &xev);
461 _target->state = ECORE_X_DND_TARGET_IDLE;
465 _ecore_x_dnd_drag(Ecore_X_Window root, int x, int y)
469 Ecore_X_Window *skip;
472 if (_source->state != ECORE_X_DND_SOURCE_DRAGGING)
475 /* Preinitialize XEvent struct */
476 memset(&xev, 0, sizeof(XEvent));
477 xev.xany.type = ClientMessage;
478 xev.xany.display = _ecore_x_disp;
479 xev.xclient.format = 32;
481 /* Attempt to find a DND-capable window under the cursor */
482 skip = ecore_x_window_ignore_list(&num);
483 // WARNING - this function is HEAVY. it goes to and from x a LOT walking the
484 // window tree - use the SHADOW version - makes a 1-off tree copy, then uses
486 // win = ecore_x_window_at_xy_with_skip_get(x, y, skip, num);
487 win = ecore_x_window_shadow_tree_at_xy_with_skip_get(root, x, y, skip, num);
489 // NOTE: This now uses the shadow version to find parent windows
490 // while ((win) && !(ecore_x_dnd_version_get(win)))
491 // win = ecore_x_window_parent_get(win);
492 while ((win) && !(ecore_x_dnd_version_get(win)))
493 win = ecore_x_window_shadow_parent_get(root, win);
495 /* Send XdndLeave to current destination window if we have left it */
496 if ((_source->dest) && (win != _source->dest))
498 xev.xclient.window = _source->dest;
499 xev.xclient.message_type = ECORE_X_ATOM_XDND_LEAVE;
500 xev.xclient.data.l[0] = _source->win;
501 xev.xclient.data.l[1] = 0;
503 XSendEvent(_ecore_x_disp, _source->dest, False, 0, &xev);
504 _source->suppress = 0;
511 _source->version = MIN(ECORE_X_DND_VERSION,
512 ecore_x_dnd_version_get(win));
513 if (win != _source->dest)
519 ecore_x_window_prop_property_get(_source->win, ECORE_X_ATOM_XDND_TYPE_LIST,
520 XA_ATOM, 32, &data, &num);
521 types = (Ecore_X_Atom *)data;
523 /* Entered new window, send XdndEnter */
524 xev.xclient.window = win;
525 xev.xclient.message_type = ECORE_X_ATOM_XDND_ENTER;
526 xev.xclient.data.l[0] = _source->win;
527 xev.xclient.data.l[1] = 0;
529 xev.xclient.data.l[1] |= 0x1UL;
531 xev.xclient.data.l[1] &= 0xfffffffeUL;
532 xev.xclient.data.l[1] |= ((unsigned long) _source->version) << 24;
534 for (i = 2; i < 5; i++)
535 xev.xclient.data.l[i] = 0;
536 for (i = 0; i < MIN(num, 3); ++i)
537 xev.xclient.data.l[i + 2] = types[i];
539 XSendEvent(_ecore_x_disp, win, False, 0, &xev);
540 _source->await_status = 0;
541 _source->will_accept = 0;
544 /* Determine if we're still in the rectangle from the last status */
545 x1 = _source->rectangle.x;
546 x2 = _source->rectangle.x + _source->rectangle.width;
547 y1 = _source->rectangle.y;
548 y2 = _source->rectangle.y + _source->rectangle.height;
550 if ((!_source->await_status) ||
551 (!_source->suppress) ||
552 ((x < x1) || (x > x2) || (y < y1) || (y > y2)))
554 xev.xclient.window = win;
555 xev.xclient.message_type = ECORE_X_ATOM_XDND_POSITION;
556 xev.xclient.data.l[0] = _source->win;
557 xev.xclient.data.l[1] = 0; /* Reserved */
558 xev.xclient.data.l[2] = ((x << 16) & 0xffff0000) | (y & 0xffff);
559 xev.xclient.data.l[3] = _source->time; /* Version 1 */
560 xev.xclient.data.l[4] = _source->action; /* Version 2, Needs to be pre-set */
561 XSendEvent(_ecore_x_disp, win, False, 0, &xev);
563 _source->await_status = 1;