2 * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
13 #include "ecore_x_private.h"
15 #include "Ecore_X_Atoms.h"
17 EAPI int ECORE_X_EVENT_XDND_ENTER = 0;
18 EAPI int ECORE_X_EVENT_XDND_POSITION = 0;
19 EAPI int ECORE_X_EVENT_XDND_STATUS = 0;
20 EAPI int ECORE_X_EVENT_XDND_LEAVE = 0;
21 EAPI int ECORE_X_EVENT_XDND_DROP = 0;
22 EAPI int ECORE_X_EVENT_XDND_FINISHED = 0;
24 static Ecore_X_DND_Source *_source = NULL;
25 static Ecore_X_DND_Target *_target = NULL;
26 static int _ecore_x_dnd_init_count = 0;
28 typedef struct _Version_Cache_Item
33 static Version_Cache_Item *_version_cache = NULL;
34 static int _version_cache_num = 0, _version_cache_alloc = 0;
37 _ecore_x_dnd_init(void)
39 if (!_ecore_x_dnd_init_count)
41 _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));
50 _target->source = None;
51 _target->state = ECORE_X_DND_TARGET_IDLE;
53 ECORE_X_EVENT_XDND_ENTER = ecore_event_type_new();
54 ECORE_X_EVENT_XDND_POSITION = ecore_event_type_new();
55 ECORE_X_EVENT_XDND_STATUS = ecore_event_type_new();
56 ECORE_X_EVENT_XDND_LEAVE = ecore_event_type_new();
57 ECORE_X_EVENT_XDND_DROP = ecore_event_type_new();
58 ECORE_X_EVENT_XDND_FINISHED = ecore_event_type_new();
61 _ecore_x_dnd_init_count++;
65 _ecore_x_dnd_shutdown(void)
67 _ecore_x_dnd_init_count--;
68 if (_ecore_x_dnd_init_count > 0)
79 _ecore_x_dnd_init_count = 0;
83 _ecore_x_dnd_converter_copy(char *target __UNUSED__, void *data, int size, void **data_ret, int *size_ret)
85 XTextProperty text_prop;
87 XICCEncodingStyle style = XTextStyle;
92 mystr = calloc(1, size + 1);
94 memcpy(mystr, data, size);
96 if (XmbTextListToTextProperty(_ecore_x_disp, &mystr, 1, style, &text_prop) == Success)
98 int bufsize = strlen((char *)text_prop.value) + 1;
99 *data_ret = malloc(bufsize);
100 memcpy(*data_ret, text_prop.value, bufsize);
102 XFree(text_prop.value);
114 ecore_x_dnd_aware_set(Ecore_X_Window win, int on)
116 Ecore_X_Atom prop_data = ECORE_X_DND_VERSION;
118 LOGFN(__FILE__, __LINE__, __FUNCTION__);
120 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_AWARE,
121 XA_ATOM, 32, &prop_data, 1);
123 ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_AWARE);
127 ecore_x_dnd_version_get(Ecore_X_Window win)
129 unsigned char *prop_data;
132 LOGFN(__FILE__, __LINE__, __FUNCTION__);
133 // this looks hacky - and it is, but we need a way of caching info about
134 // a window while dragging, because we literally query this every mouse
135 // move and going to and from x multiple times per move is EXPENSIVE
136 // and slows things down, puts lots of load on x etc.
137 if (_source->state == ECORE_X_DND_SOURCE_DRAGGING)
143 for (i = 0; i < _version_cache_num; i++)
145 if (_version_cache[i].win == win)
146 return _version_cache[i].ver;
151 if (ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_AWARE,
152 XA_ATOM, 32, &prop_data, &num))
154 int version = (int) *prop_data;
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 = version;
167 if (_source->state == ECORE_X_DND_SOURCE_DRAGGING)
169 _version_cache_num++;
170 if (_version_cache_num > _version_cache_alloc)
171 _version_cache_alloc += 16;
172 _version_cache = realloc(_version_cache, _version_cache_alloc * sizeof(Version_Cache_Item));
173 _version_cache[_version_cache_num - 1].win = win;
174 _version_cache[_version_cache_num - 1].ver = 0;
180 ecore_x_dnd_type_isset(Ecore_X_Window win, const char *type)
184 Ecore_X_Atom *atoms, atom;
186 LOGFN(__FILE__, __LINE__, __FUNCTION__);
187 if (!ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_TYPE_LIST,
188 XA_ATOM, 32, &data, &num))
191 atom = ecore_x_atom_get(type);
192 atoms = (Ecore_X_Atom *)data;
194 for (i = 0; i < num; ++i)
196 if (atom == atoms[i])
208 ecore_x_dnd_type_set(Ecore_X_Window win, const char *type, int on)
211 Ecore_X_Atom *oldset = NULL, *newset = NULL;
212 int i, j = 0, num = 0;
213 unsigned char *data = NULL;
214 unsigned char *old_data = NULL;
216 LOGFN(__FILE__, __LINE__, __FUNCTION__);
217 atom = ecore_x_atom_get(type);
218 ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_TYPE_LIST,
219 XA_ATOM, 32, &old_data, &num);
220 oldset = (Ecore_X_Atom *)old_data;
222 LOGFN(__FILE__, __LINE__, __FUNCTION__);
225 if (ecore_x_dnd_type_isset(win, type))
230 newset = calloc(num + 1, sizeof(Ecore_X_Atom));
232 data = (unsigned char *)newset;
234 for (i = 0; i < num; i++)
235 newset[i + 1] = oldset[i];
236 /* prepend the new type */
239 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_TYPE_LIST,
240 XA_ATOM, 32, data, num + 1);
244 if (!ecore_x_dnd_type_isset(win, type))
249 newset = calloc(num - 1, sizeof(Ecore_X_Atom));
255 data = (unsigned char *)newset;
256 for (i = 0; i < num; i++)
257 if (oldset[i] != atom)
258 newset[j++] = oldset[i];
260 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_TYPE_LIST,
261 XA_ATOM, 32, data, num - 1);
268 ecore_x_dnd_types_set(Ecore_X_Window win, const char **types, unsigned int num_types)
270 Ecore_X_Atom *newset = NULL;
272 unsigned char *data = NULL;
274 LOGFN(__FILE__, __LINE__, __FUNCTION__);
277 ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_TYPE_LIST);
281 newset = calloc(num_types, sizeof(Ecore_X_Atom));
283 data = (unsigned char *)newset;
284 for (i = 0; i < num_types; i++)
286 newset[i] = ecore_x_atom_get(types[i]);
287 ecore_x_selection_converter_atom_add(newset[i], _ecore_x_dnd_converter_copy);
289 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_TYPE_LIST,
290 XA_ATOM, 32, data, num_types);
296 ecore_x_dnd_actions_set(Ecore_X_Window win, Ecore_X_Atom *actions, unsigned int num_actions)
299 unsigned char *data = NULL;
301 LOGFN(__FILE__, __LINE__, __FUNCTION__);
304 ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_ACTION_LIST);
308 data = (unsigned char *)actions;
309 for (i = 0; i < num_actions; i++)
311 ecore_x_selection_converter_atom_add(actions[i], _ecore_x_dnd_converter_copy);
313 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_ACTION_LIST,
314 XA_ATOM, 32, data, num_actions);
319 _ecore_x_dnd_source_get(void)
325 _ecore_x_dnd_target_get(void)
331 ecore_x_dnd_begin(Ecore_X_Window source, unsigned char *data, int size)
334 LOGFN(__FILE__, __LINE__, __FUNCTION__);
335 if (!ecore_x_dnd_version_get(source))
338 /* Take ownership of XdndSelection */
339 if (!ecore_x_selection_xdnd_set(source, data, size))
344 free(_version_cache);
345 _version_cache = NULL;
346 _version_cache_num = 0;
347 _version_cache_alloc = 0;
349 ecore_x_window_shadow_tree_flush();
351 _source->win = source;
352 ecore_x_window_ignore_set(_source->win, 1);
353 _source->state = ECORE_X_DND_SOURCE_DRAGGING;
354 _source->time = _ecore_x_event_last_time;
355 _source->prev.window = 0;
357 /* Default Accepted Action: move */
358 _source->action = ECORE_X_ATOM_XDND_ACTION_MOVE;
359 _source->accepted_action = None;
360 _source->dest = None;
366 ecore_x_dnd_drop(void)
371 LOGFN(__FILE__, __LINE__, __FUNCTION__);
374 xev.xany.type = ClientMessage;
375 xev.xany.display = _ecore_x_disp;
376 xev.xclient.format = 32;
377 xev.xclient.window = _source->dest;
379 if (_source->will_accept)
381 xev.xclient.message_type = ECORE_X_ATOM_XDND_DROP;
382 xev.xclient.data.l[0] = _source->win;
383 xev.xclient.data.l[1] = 0;
384 xev.xclient.data.l[2] = _source->time;
385 XSendEvent(_ecore_x_disp, _source->dest, False, 0, &xev);
386 _source->state = ECORE_X_DND_SOURCE_DROPPED;
391 xev.xclient.message_type = ECORE_X_ATOM_XDND_LEAVE;
392 xev.xclient.data.l[0] = _source->win;
393 xev.xclient.data.l[1] = 0;
394 XSendEvent(_ecore_x_disp, _source->dest, False, 0, &xev);
395 _source->state = ECORE_X_DND_SOURCE_IDLE;
400 /* Dropping on nothing */
401 ecore_x_selection_xdnd_clear();
402 _source->state = ECORE_X_DND_SOURCE_IDLE;
404 ecore_x_window_ignore_set(_source->win, 0);
406 _source->prev.window = 0;
412 ecore_x_dnd_send_status(int will_accept, int suppress, Ecore_X_Rectangle rectangle, Ecore_X_Atom action)
416 if (_target->state == ECORE_X_DND_TARGET_IDLE)
419 LOGFN(__FILE__, __LINE__, __FUNCTION__);
420 memset(&xev, 0, sizeof(XEvent));
422 _target->will_accept = will_accept;
424 xev.xclient.type = ClientMessage;
425 xev.xclient.display = _ecore_x_disp;
426 xev.xclient.message_type = ECORE_X_ATOM_XDND_STATUS;
427 xev.xclient.format = 32;
428 xev.xclient.window = _target->source;
430 xev.xclient.data.l[0] = _target->win;
431 xev.xclient.data.l[1] = 0;
433 xev.xclient.data.l[1] |= 0x1UL;
435 xev.xclient.data.l[1] |= 0x2UL;
437 /* Set rectangle information */
438 xev.xclient.data.l[2] = rectangle.x;
439 xev.xclient.data.l[2] <<= 16;
440 xev.xclient.data.l[2] |= rectangle.y;
441 xev.xclient.data.l[3] = rectangle.width;
442 xev.xclient.data.l[3] <<= 16;
443 xev.xclient.data.l[3] |= rectangle.height;
447 xev.xclient.data.l[4] = action;
448 _target->accepted_action = action;
452 xev.xclient.data.l[4] = None;
453 _target->accepted_action = action;
456 XSendEvent(_ecore_x_disp, _target->source, False, 0, &xev);
460 ecore_x_dnd_send_finished(void)
464 if (_target->state == ECORE_X_DND_TARGET_IDLE)
467 LOGFN(__FILE__, __LINE__, __FUNCTION__);
468 xev.xany.type = ClientMessage;
469 xev.xany.display = _ecore_x_disp;
470 xev.xclient.message_type = ECORE_X_ATOM_XDND_FINISHED;
471 xev.xclient.format = 32;
472 xev.xclient.window = _target->source;
474 xev.xclient.data.l[0] = _target->win;
475 xev.xclient.data.l[1] = 0;
476 xev.xclient.data.l[2] = 0;
477 if (_target->will_accept)
479 xev.xclient.data.l[1] |= 0x1UL;
480 xev.xclient.data.l[2] = _target->accepted_action;
482 XSendEvent(_ecore_x_disp, _target->source, False, 0, &xev);
484 _target->state = ECORE_X_DND_TARGET_IDLE;
488 ecore_x_dnd_source_action_set(Ecore_X_Atom action)
490 _source->action = action;
491 if (_source->prev.window)
492 _ecore_x_dnd_drag(_source->prev.window, _source->prev.x, _source->prev.y);
496 ecore_x_dnd_source_action_get(void)
498 return _source->action;
502 _ecore_x_dnd_drag(Ecore_X_Window root, int x, int y)
506 Ecore_X_Window *skip;
509 if (_source->state != ECORE_X_DND_SOURCE_DRAGGING)
512 /* Preinitialize XEvent struct */
513 memset(&xev, 0, sizeof(XEvent));
514 xev.xany.type = ClientMessage;
515 xev.xany.display = _ecore_x_disp;
516 xev.xclient.format = 32;
518 /* Attempt to find a DND-capable window under the cursor */
519 skip = ecore_x_window_ignore_list(&num);
520 // WARNING - this function is HEAVY. it goes to and from x a LOT walking the
521 // window tree - use the SHADOW version - makes a 1-off tree copy, then uses
523 // win = ecore_x_window_at_xy_with_skip_get(x, y, skip, num);
524 win = ecore_x_window_shadow_tree_at_xy_with_skip_get(root, x, y, skip, num);
526 // NOTE: This now uses the shadow version to find parent windows
527 // while ((win) && !(ecore_x_dnd_version_get(win)))
528 // win = ecore_x_window_parent_get(win);
529 while ((win) && !(ecore_x_dnd_version_get(win)))
530 win = ecore_x_window_shadow_parent_get(root, win);
532 /* Send XdndLeave to current destination window if we have left it */
533 if ((_source->dest) && (win != _source->dest))
535 xev.xclient.window = _source->dest;
536 xev.xclient.message_type = ECORE_X_ATOM_XDND_LEAVE;
537 xev.xclient.data.l[0] = _source->win;
538 xev.xclient.data.l[1] = 0;
540 XSendEvent(_ecore_x_disp, _source->dest, False, 0, &xev);
541 _source->suppress = 0;
548 _source->version = MIN(ECORE_X_DND_VERSION,
549 ecore_x_dnd_version_get(win));
550 if (win != _source->dest)
556 ecore_x_window_prop_property_get(_source->win, ECORE_X_ATOM_XDND_TYPE_LIST,
557 XA_ATOM, 32, &data, &num);
558 types = (Ecore_X_Atom *)data;
560 /* Entered new window, send XdndEnter */
561 xev.xclient.window = win;
562 xev.xclient.message_type = ECORE_X_ATOM_XDND_ENTER;
563 xev.xclient.data.l[0] = _source->win;
564 xev.xclient.data.l[1] = 0;
566 xev.xclient.data.l[1] |= 0x1UL;
568 xev.xclient.data.l[1] &= 0xfffffffeUL;
569 xev.xclient.data.l[1] |= ((unsigned long) _source->version) << 24;
571 for (i = 2; i < 5; i++)
572 xev.xclient.data.l[i] = 0;
573 for (i = 0; i < MIN(num, 3); ++i)
574 xev.xclient.data.l[i + 2] = types[i];
576 XSendEvent(_ecore_x_disp, win, False, 0, &xev);
577 _source->await_status = 0;
578 _source->will_accept = 0;
581 /* Determine if we're still in the rectangle from the last status */
582 x1 = _source->rectangle.x;
583 x2 = _source->rectangle.x + _source->rectangle.width;
584 y1 = _source->rectangle.y;
585 y2 = _source->rectangle.y + _source->rectangle.height;
587 if ((!_source->await_status) ||
588 (!_source->suppress) ||
589 ((x < x1) || (x > x2) || (y < y1) || (y > y2)))
591 xev.xclient.window = win;
592 xev.xclient.message_type = ECORE_X_ATOM_XDND_POSITION;
593 xev.xclient.data.l[0] = _source->win;
594 xev.xclient.data.l[1] = 0; /* Reserved */
595 xev.xclient.data.l[2] = ((x << 16) & 0xffff0000) | (y & 0xffff);
596 xev.xclient.data.l[3] = _source->time; /* Version 1 */
597 xev.xclient.data.l[4] = _source->action; /* Version 2, Needs to be pre-set */
598 XSendEvent(_ecore_x_disp, win, False, 0, &xev);
600 _source->await_status = 1;
606 _source->prev.window = root;