1 #include "ecore_xcb_private.h"
4 # define MIN(a, b) (((a) < (b)) ? (a) : (b))
8 typedef struct _Version_Cache_Item
14 /* local function prototypes */
15 static Eina_Bool _ecore_xcb_dnd_converter_copy(char *target __UNUSED__, void *data, int size, void **data_ret, int *size_ret, Ecore_X_Atom *tprop __UNUSED__, int *count __UNUSED__);
18 static int _ecore_xcb_dnd_init_count = 0;
19 static Ecore_X_DND_Source *_source = NULL;
20 static Ecore_X_DND_Target *_target = NULL;
21 static Version_Cache_Item *_version_cache = NULL;
22 static int _version_cache_num = 0, _version_cache_alloc = 0;
23 static void (*_posupdatecb)(void *, Ecore_X_Xdnd_Position *);
24 static void *_posupdatedata;
26 /* external variables */
27 EAPI int ECORE_X_EVENT_XDND_ENTER = 0;
28 EAPI int ECORE_X_EVENT_XDND_POSITION = 0;
29 EAPI int ECORE_X_EVENT_XDND_STATUS = 0;
30 EAPI int ECORE_X_EVENT_XDND_LEAVE = 0;
31 EAPI int ECORE_X_EVENT_XDND_DROP = 0;
32 EAPI int ECORE_X_EVENT_XDND_FINISHED = 0;
35 _ecore_xcb_dnd_init(void)
37 LOGFN(__FILE__, __LINE__, __FUNCTION__);
39 if (!_ecore_xcb_dnd_init_count)
41 _source = calloc(1, sizeof(Ecore_X_DND_Source));
43 _source->version = ECORE_X_DND_VERSION;
44 _source->win = XCB_NONE;
45 _source->dest = XCB_NONE;
46 _source->state = ECORE_X_DND_SOURCE_IDLE;
47 _source->prev.window = 0;
49 _target = calloc(1, sizeof(Ecore_X_DND_Target));
56 _target->win = XCB_NONE;
57 _target->source = XCB_NONE;
58 _target->state = ECORE_X_DND_TARGET_IDLE;
60 ECORE_X_EVENT_XDND_ENTER = ecore_event_type_new();
61 ECORE_X_EVENT_XDND_POSITION = ecore_event_type_new();
62 ECORE_X_EVENT_XDND_STATUS = ecore_event_type_new();
63 ECORE_X_EVENT_XDND_LEAVE = ecore_event_type_new();
64 ECORE_X_EVENT_XDND_DROP = ecore_event_type_new();
65 ECORE_X_EVENT_XDND_FINISHED = ecore_event_type_new();
67 _ecore_xcb_dnd_init_count++;
71 _ecore_xcb_dnd_shutdown(void)
73 LOGFN(__FILE__, __LINE__, __FUNCTION__);
75 _ecore_xcb_dnd_init_count--;
76 if (_ecore_xcb_dnd_init_count > 0) return;
77 if (_source) free(_source);
79 if (_target) free(_target);
81 _ecore_xcb_dnd_init_count = 0;
85 ecore_x_dnd_send_status(Eina_Bool will_accept, Eina_Bool suppress, Ecore_X_Rectangle rect, Ecore_X_Atom action)
87 xcb_client_message_event_t ev;
89 LOGFN(__FILE__, __LINE__, __FUNCTION__);
91 if (_target->state == ECORE_X_DND_TARGET_IDLE) return;
93 DBG("Ecore_X_Dnd_Send_Status");
94 memset(&ev, 0, sizeof(xcb_client_message_event_t));
96 _target->will_accept = will_accept;
98 ev.response_type = XCB_CLIENT_MESSAGE;
99 ev.type = ECORE_X_ATOM_XDND_STATUS;
101 ev.window = _target->source;
102 ev.data.data32[0] = _target->win;
103 ev.data.data32[1] = 0;
104 if (will_accept) ev.data.data32[1] |= 0x1UL;
105 if (!suppress) ev.data.data32[1] |= 0x2UL;
107 ev.data.data32[2] = rect.x;
108 ev.data.data32[2] <<= 16;
109 ev.data.data32[2] |= rect.y;
110 ev.data.data32[3] = rect.width;
111 ev.data.data32[3] <<= 16;
112 ev.data.data32[3] |= rect.height;
115 ev.data.data32[4] = action;
117 ev.data.data32[4] = XCB_NONE;
118 _target->accepted_action = action;
120 xcb_send_event(_ecore_xcb_conn, 0, _target->source,
121 XCB_EVENT_MASK_NO_EVENT, (const char *)&ev);
126 ecore_x_dnd_drop(void)
128 xcb_client_message_event_t ev;
129 Eina_Bool status = EINA_FALSE;
131 LOGFN(__FILE__, __LINE__, __FUNCTION__);
133 memset(&ev, 0, sizeof(xcb_client_message_event_t));
135 DBG("Ecore_X_Dnd_Drop");
138 ev.response_type = XCB_CLIENT_MESSAGE;
140 ev.window = _source->dest;
142 if (_source->will_accept)
144 ev.type = ECORE_X_ATOM_XDND_DROP;
145 ev.data.data32[0] = _source->win;
146 ev.data.data32[1] = 0;
147 ev.data.data32[2] = _source->time;
149 xcb_send_event(_ecore_xcb_conn, 0, _source->dest,
150 XCB_EVENT_MASK_NO_EVENT, (const char *)&ev);
152 _source->state = ECORE_X_DND_SOURCE_DROPPED;
157 ev.type = ECORE_X_ATOM_XDND_LEAVE;
158 ev.data.data32[0] = _source->win;
159 ev.data.data32[1] = 0;
161 xcb_send_event(_ecore_xcb_conn, 0, _source->dest,
162 XCB_EVENT_MASK_NO_EVENT, (const char *)&ev);
164 _source->state = ECORE_X_DND_SOURCE_IDLE;
169 ecore_x_selection_xdnd_clear();
170 _source->state = ECORE_X_DND_SOURCE_IDLE;
173 ecore_x_window_ignore_set(_source->win, 0);
174 _source->prev.window = 0;
180 ecore_x_dnd_aware_set(Ecore_X_Window win, Eina_Bool on)
182 Ecore_X_Atom prop_data = ECORE_X_DND_VERSION;
184 LOGFN(__FILE__, __LINE__, __FUNCTION__);
186 DBG("Ecore_X_Dnd_Aware_Set");
188 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_AWARE,
189 ECORE_X_ATOM_ATOM, 32, &prop_data, 1);
191 ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_AWARE);
195 ecore_x_dnd_version_get(Ecore_X_Window win)
199 Version_Cache_Item *t;
201 LOGFN(__FILE__, __LINE__, __FUNCTION__);
203 if (_source->state == ECORE_X_DND_SOURCE_DRAGGING)
209 for (i = 0; i < _version_cache_num; i++)
211 if (_version_cache[i].win == win)
212 return _version_cache[i].ver;
217 if (ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_AWARE,
218 ECORE_X_ATOM_ATOM, 32, &data, &num))
222 version = (int)*data;
224 if (_source->state == ECORE_X_DND_SOURCE_DRAGGING)
226 _version_cache_num++;
227 if (_version_cache_num > _version_cache_alloc)
228 _version_cache_alloc += 16;
229 t = realloc(_version_cache,
230 _version_cache_alloc * sizeof(Version_Cache_Item));
233 _version_cache[_version_cache_num - 1].win = win;
234 _version_cache[_version_cache_num - 1].ver = version;
239 if (_source->state == ECORE_X_DND_SOURCE_DRAGGING)
241 _version_cache_num++;
242 if (_version_cache_num > _version_cache_alloc)
243 _version_cache_alloc += 16;
244 t = realloc(_version_cache,
245 _version_cache_alloc * sizeof(Version_Cache_Item));
248 _version_cache[_version_cache_num - 1].win = win;
249 _version_cache[_version_cache_num - 1].ver = 0;
256 ecore_x_dnd_type_isset(Ecore_X_Window win, const char *type)
259 Eina_Bool ret = EINA_FALSE;
261 Ecore_X_Atom *atoms, atom;
263 LOGFN(__FILE__, __LINE__, __FUNCTION__);
265 if (!ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_TYPE_LIST,
266 ECORE_X_ATOM_ATOM, 32, &data, &num))
269 atom = ecore_x_atom_get(type);
270 atoms = (Ecore_X_Atom *)data;
271 for (i = 0; i < num; ++i)
273 if (atom == atoms[i])
285 ecore_x_dnd_type_set(Ecore_X_Window win, const char *type, Eina_Bool on)
287 Ecore_X_Atom atom, *oldset = NULL, *newset = NULL;
288 int i = 0, j = 0, num = 0;
289 unsigned char *data = NULL, *old_data = NULL;
291 LOGFN(__FILE__, __LINE__, __FUNCTION__);
293 DBG("Ecore_X_Dnd_Type_Set");
294 atom = ecore_x_atom_get(type);
295 ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_TYPE_LIST,
296 ECORE_X_ATOM_ATOM, 32, &old_data, &num);
297 oldset = (Ecore_X_Atom *)old_data;
300 if (ecore_x_dnd_type_isset(win, type))
305 newset = calloc(num + 1, sizeof(Ecore_X_Atom));
307 data = (unsigned char *)newset;
308 for (i = 0; i < num; i++)
309 newset[i + 1] = oldset[i];
311 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_TYPE_LIST,
312 ECORE_X_ATOM_ATOM, 32, data, num + 1);
316 if (!ecore_x_dnd_type_isset(win, type))
321 newset = calloc(num - 1, sizeof(Ecore_X_Atom));
327 data = (unsigned char *)newset;
328 for (i = 0; i < num; i++)
329 if (oldset[i] != atom)
330 newset[j++] = oldset[i];
331 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_TYPE_LIST,
332 ECORE_X_ATOM_ATOM, 32, data, num - 1);
339 ecore_x_dnd_types_set(Ecore_X_Window win, const char **types, unsigned int num_types)
341 Ecore_X_Atom *newset = NULL;
343 unsigned char *data = NULL;
345 LOGFN(__FILE__, __LINE__, __FUNCTION__);
347 DBG("Ecore_X_Dnd_Types_Set");
349 ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_TYPE_LIST);
352 newset = calloc(num_types, sizeof(Ecore_X_Atom));
355 data = (unsigned char *)newset;
356 for (i = 0; i < num_types; i++)
358 newset[i] = ecore_x_atom_get(types[i]);
359 ecore_x_selection_converter_atom_add(newset[i],
360 _ecore_xcb_dnd_converter_copy);
362 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_TYPE_LIST,
363 ECORE_X_ATOM_ATOM, 32, data,
370 ecore_x_dnd_actions_set(Ecore_X_Window win, Ecore_X_Atom *actions, unsigned int num_actions)
373 unsigned char *data = NULL;
375 LOGFN(__FILE__, __LINE__, __FUNCTION__);
377 DBG("Ecore_X_Dnd_Actions_Set");
379 ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_ACTION_LIST);
382 data = (unsigned char *)actions;
383 for (i = 0; i < num_actions; i++)
384 ecore_x_selection_converter_atom_add(actions[i],
385 _ecore_xcb_dnd_converter_copy);
386 ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_ACTION_LIST,
387 ECORE_X_ATOM_ATOM, 32, data,
393 * The DND position update cb is called Ecore_X sends a DND position to a
396 * It essentially mirrors some of the data sent in the position message.
397 * Generally this cb should be set just before position update is called.
398 * Please note well you need to look after your own data pointer if someone
399 * trashes you position update cb set.
401 * It is considered good form to clear this when the dnd event finishes.
403 * @param cb Callback to updated each time ecore_x sends a position update.
404 * @param data User data.
407 ecore_x_dnd_callback_pos_update_set(void (*cb)(void *, Ecore_X_Xdnd_Position *data), const void *data)
410 _posupdatedata = (void *)data;
414 ecore_x_dnd_begin(Ecore_X_Window source, unsigned char *data, int size)
416 LOGFN(__FILE__, __LINE__, __FUNCTION__);
418 if (!ecore_x_dnd_version_get(source)) return EINA_FALSE;
420 DBG("Ecore_X_Dnd_Begin");
421 /* Take ownership of XdndSelection */
422 if (!ecore_x_selection_xdnd_set(source, data, size)) return EINA_FALSE;
426 free(_version_cache);
427 _version_cache = NULL;
428 _version_cache_num = 0;
429 _version_cache_alloc = 0;
432 ecore_x_window_shadow_tree_flush();
434 _source->win = source;
435 ecore_x_window_ignore_set(_source->win, 1);
436 _source->state = ECORE_X_DND_SOURCE_DRAGGING;
437 _source->time = _ecore_xcb_events_last_time_get();
438 _source->prev.window = 0;
440 /* Default Accepted Action: move */
441 _source->action = ECORE_X_ATOM_XDND_ACTION_MOVE;
442 _source->accepted_action = XCB_NONE;
443 _source->dest = XCB_NONE;
449 ecore_x_dnd_send_finished(void)
451 xcb_client_message_event_t ev;
453 LOGFN(__FILE__, __LINE__, __FUNCTION__);
455 if (_target->state == ECORE_X_DND_TARGET_IDLE) return;
457 DBG("Ecore_X_Dnd_Send_Finished");
458 memset(&ev, 0, sizeof(xcb_client_message_event_t));
460 ev.response_type = XCB_CLIENT_MESSAGE;
462 ev.type = ECORE_X_ATOM_XDND_FINISHED;
463 ev.window = _target->source;
464 ev.data.data32[0] = _target->win;
465 ev.data.data32[1] = 0;
466 ev.data.data32[2] = 0;
467 if (_target->will_accept)
469 ev.data.data32[1] |= 0x1UL;
470 ev.data.data32[2] = _target->accepted_action;
473 xcb_send_event(_ecore_xcb_conn, 0, _target->source,
474 XCB_EVENT_MASK_NO_EVENT, (const char *)&ev);
476 _target->state = ECORE_X_DND_TARGET_IDLE;
480 ecore_x_dnd_source_action_set(Ecore_X_Atom action)
482 LOGFN(__FILE__, __LINE__, __FUNCTION__);
484 _source->action = action;
485 if (_source->prev.window)
486 _ecore_xcb_dnd_drag(_source->prev.window,
487 _source->prev.x, _source->prev.y);
491 _ecore_xcb_dnd_source_get(void)
497 _ecore_xcb_dnd_target_get(void)
503 _ecore_xcb_dnd_drag(Ecore_X_Window root, int x, int y)
505 xcb_client_message_event_t ev;
506 Ecore_X_Window win, *skip;
507 Ecore_X_Xdnd_Position pos;
510 if (_source->state != ECORE_X_DND_SOURCE_DRAGGING) return;
512 LOGFN(__FILE__, __LINE__, __FUNCTION__);
514 DBG("Ecore_X_Dnd_Drag");
515 memset(&ev, 0, sizeof(xcb_client_message_event_t));
517 ev.response_type = XCB_CLIENT_MESSAGE;
520 skip = ecore_x_window_ignore_list(&num);
521 win = ecore_x_window_shadow_tree_at_xy_with_skip_get(root, x, y, skip, num);
522 while ((win) && !(ecore_x_dnd_version_get(win)))
523 win = ecore_x_window_shadow_parent_get(root, win);
525 if ((_source->dest) && (win != _source->dest))
527 ev.window = _source->dest;
528 ev.type = ECORE_X_ATOM_XDND_LEAVE;
529 ev.data.data32[0] = _source->win;
530 ev.data.data32[1] = 0;
532 xcb_send_event(_ecore_xcb_conn, 0, _source->dest,
533 XCB_EVENT_MASK_NO_EVENT, (const char *)&ev);
535 _source->suppress = 0;
542 _source->version = MIN(ECORE_X_DND_VERSION,
543 ecore_x_dnd_version_get(win));
544 if (win != _source->dest)
550 ecore_x_window_prop_property_get(_source->win,
551 ECORE_X_ATOM_XDND_TYPE_LIST,
552 ECORE_X_ATOM_ATOM, 32,
554 types = (Ecore_X_Atom *)data;
556 ev.type = ECORE_X_ATOM_XDND_ENTER;
557 ev.data.data32[0] = _source->win;
558 ev.data.data32[1] = 0;
560 ev.data.data32[1] |= 0x1UL;
562 ev.data.data32[1] &= 0xfffffffeUL;
563 ev.data.data32[1] |= ((unsigned long)_source->version) << 24;
565 for (i = 2; i < 5; i++)
566 ev.data.data32[i] = 0;
567 for (i = 0; i < MIN(num, 3); ++i)
568 ev.data.data32[i + 2] = types[i];
571 xcb_send_event(_ecore_xcb_conn, 0, win,
572 XCB_EVENT_MASK_NO_EVENT, (const char *)&ev);
574 _source->await_status = 0;
575 _source->will_accept = 0;
578 x1 = _source->rectangle.x;
579 x2 = _source->rectangle.x + _source->rectangle.width;
580 y1 = _source->rectangle.y;
581 y2 = _source->rectangle.y + _source->rectangle.height;
583 if ((!_source->await_status) || (!_source->suppress) ||
584 ((x < x1) || (x > x2) || (y < y1) || (y > y2)))
587 ev.type = ECORE_X_ATOM_XDND_POSITION;
588 ev.data.data32[0] = _source->win;
589 ev.data.data32[1] = 0;
590 ev.data.data32[2] = ((x << 16) & 0xffff0000) | (y & 0xffff);
591 ev.data.data32[3] = _source->time;
592 ev.data.data32[4] = _source->action;
594 xcb_send_event(_ecore_xcb_conn, 0, win,
595 XCB_EVENT_MASK_NO_EVENT, (const char *)&ev);
597 _source->await_status = 1;
606 pos.prev = _source->dest;
607 _posupdatecb(_posupdatedata, &pos);
612 _source->prev.window = root;
617 ecore_x_dnd_source_action_get(void)
619 return _source->action;
622 /* local functions */
624 _ecore_xcb_dnd_converter_copy(char *target __UNUSED__, void *data, int size, void **data_ret, int *size_ret, Ecore_X_Atom *tprop __UNUSED__, int *count __UNUSED__)
626 Ecore_Xcb_Textproperty text_prop;
627 Ecore_Xcb_Encoding_Style style = XcbTextStyle;
630 LOGFN(__FILE__, __LINE__, __FUNCTION__);
632 if ((!data) || (!size)) return EINA_FALSE;
634 mystr = calloc(1, size + 1);
635 if (!mystr) return EINA_FALSE;
637 memcpy(mystr, data, size);
638 if (_ecore_xcb_mb_textlist_to_textproperty(&mystr, 1, style, &text_prop))
642 len = strlen((char *)text_prop.value) + 1;
643 if (!(*data_ret = malloc(len)))
648 memcpy(*data_ret, text_prop.value, len);
650 free(text_prop.value);