revert async compositior
[framework/uifw/ecore.git] / src / lib / ecore_x / xcb / ecore_xcb_dnd.c
1 #include "ecore_xcb_private.h"
2
3 #ifndef MIN
4 # define MIN(a, b) (((a) < (b)) ? (a) : (b))
5 #endif
6
7 /* local structures */
8 typedef struct _Version_Cache_Item
9 {
10    Ecore_X_Window win;
11    int            ver;
12 } Version_Cache_Item;
13
14 /* local function prototypes */
15 static Eina_Bool _ecore_xcb_dnd_converter_copy(char         *target __UNUSED__,
16                                                void         *data,
17                                                int           size,
18                                                void        **data_ret,
19                                                int          *size_ret,
20                                                Ecore_X_Atom *tprop __UNUSED__,
21                                                int          *count __UNUSED__);
22
23 /* local variables */
24 static int _ecore_xcb_dnd_init_count = 0;
25 static Ecore_X_DND_Source *_source = NULL;
26 static Ecore_X_DND_Target *_target = NULL;
27 static Version_Cache_Item *_version_cache = NULL;
28 static int _version_cache_num = 0, _version_cache_alloc = 0;
29 static void (*_posupdatecb)(void *,
30                             Ecore_X_Xdnd_Position *);
31 static void *_posupdatedata;
32
33 /* external variables */
34 EAPI int ECORE_X_EVENT_XDND_ENTER = 0;
35 EAPI int ECORE_X_EVENT_XDND_POSITION = 0;
36 EAPI int ECORE_X_EVENT_XDND_STATUS = 0;
37 EAPI int ECORE_X_EVENT_XDND_LEAVE = 0;
38 EAPI int ECORE_X_EVENT_XDND_DROP = 0;
39 EAPI int ECORE_X_EVENT_XDND_FINISHED = 0;
40
41 void
42 _ecore_xcb_dnd_init(void)
43 {
44    LOGFN(__FILE__, __LINE__, __FUNCTION__);
45
46    if (!_ecore_xcb_dnd_init_count)
47      {
48         _source = calloc(1, sizeof(Ecore_X_DND_Source));
49         if (!_source) return;
50         _source->version = ECORE_X_DND_VERSION;
51         _source->win = XCB_NONE;
52         _source->dest = XCB_NONE;
53         _source->state = ECORE_X_DND_SOURCE_IDLE;
54         _source->prev.window = 0;
55
56         _target = calloc(1, sizeof(Ecore_X_DND_Target));
57         if (!_target)
58           {
59              free(_source);
60              _source = NULL;
61              return;
62           }
63         _target->win = XCB_NONE;
64         _target->source = XCB_NONE;
65         _target->state = ECORE_X_DND_TARGET_IDLE;
66
67         ECORE_X_EVENT_XDND_ENTER = ecore_event_type_new();
68         ECORE_X_EVENT_XDND_POSITION = ecore_event_type_new();
69         ECORE_X_EVENT_XDND_STATUS = ecore_event_type_new();
70         ECORE_X_EVENT_XDND_LEAVE = ecore_event_type_new();
71         ECORE_X_EVENT_XDND_DROP = ecore_event_type_new();
72         ECORE_X_EVENT_XDND_FINISHED = ecore_event_type_new();
73      }
74    _ecore_xcb_dnd_init_count++;
75 }
76
77 void
78 _ecore_xcb_dnd_shutdown(void)
79 {
80    LOGFN(__FILE__, __LINE__, __FUNCTION__);
81
82    _ecore_xcb_dnd_init_count--;
83    if (_ecore_xcb_dnd_init_count > 0) return;
84    if (_source) free(_source);
85    _source = NULL;
86    if (_target) free(_target);
87    _target = NULL;
88    _ecore_xcb_dnd_init_count = 0;
89 }
90
91 EAPI void
92 ecore_x_dnd_send_status(Eina_Bool         will_accept,
93                         Eina_Bool         suppress,
94                         Ecore_X_Rectangle rect,
95                         Ecore_X_Atom      action)
96 {
97    xcb_client_message_event_t ev;
98
99    LOGFN(__FILE__, __LINE__, __FUNCTION__);
100    CHECK_XCB_CONN;
101
102    if (_target->state == ECORE_X_DND_TARGET_IDLE) return;
103
104    memset(&ev, 0, sizeof(xcb_client_message_event_t));
105
106    _target->will_accept = will_accept;
107
108    ev.response_type = XCB_CLIENT_MESSAGE;
109    ev.type = ECORE_X_ATOM_XDND_STATUS;
110    ev.format = 32;
111    ev.window = _target->source;
112    ev.data.data32[0] = _target->win;
113    ev.data.data32[1] = 0;
114    if (will_accept) ev.data.data32[1] |= 0x1UL;
115    if (!suppress) ev.data.data32[1] |= 0x2UL;
116
117    ev.data.data32[2] = rect.x;
118    ev.data.data32[2] <<= 16;
119    ev.data.data32[2] |= rect.y;
120    ev.data.data32[3] = rect.width;
121    ev.data.data32[3] <<= 16;
122    ev.data.data32[3] |= rect.height;
123
124    if (will_accept)
125      ev.data.data32[4] = action;
126    else
127      ev.data.data32[4] = XCB_NONE;
128    _target->accepted_action = action;
129
130    xcb_send_event(_ecore_xcb_conn, 0, _target->source,
131                   XCB_EVENT_MASK_NO_EVENT, (const char *)&ev);
132 //   ecore_x_flush();
133 }
134
135 EAPI Eina_Bool
136 ecore_x_dnd_drop(void)
137 {
138    xcb_client_message_event_t ev;
139    Eina_Bool status = EINA_FALSE;
140
141    LOGFN(__FILE__, __LINE__, __FUNCTION__);
142    CHECK_XCB_CONN;
143
144    memset(&ev, 0, sizeof(xcb_client_message_event_t));
145
146    if (_source->dest)
147      {
148         ev.response_type = XCB_CLIENT_MESSAGE;
149         ev.format = 32;
150         ev.window = _source->dest;
151
152         if (_source->will_accept)
153           {
154              ev.type = ECORE_X_ATOM_XDND_DROP;
155              ev.data.data32[0] = _source->win;
156              ev.data.data32[1] = 0;
157              ev.data.data32[2] = _source->time;
158
159              xcb_send_event(_ecore_xcb_conn, 0, _source->dest,
160                             XCB_EVENT_MASK_NO_EVENT, (const char *)&ev);
161 //             ecore_x_flush();
162              _source->state = ECORE_X_DND_SOURCE_DROPPED;
163              status = EINA_TRUE;
164           }
165         else
166           {
167              ev.type = ECORE_X_ATOM_XDND_LEAVE;
168              ev.data.data32[0] = _source->win;
169              ev.data.data32[1] = 0;
170
171              xcb_send_event(_ecore_xcb_conn, 0, _source->dest,
172                             XCB_EVENT_MASK_NO_EVENT, (const char *)&ev);
173 //             ecore_x_flush();
174              _source->state = ECORE_X_DND_SOURCE_IDLE;
175           }
176      }
177    else
178      {
179         ecore_x_selection_xdnd_clear();
180         _source->state = ECORE_X_DND_SOURCE_IDLE;
181      }
182
183    ecore_x_window_ignore_set(_source->win, 0);
184    _source->prev.window = 0;
185
186    return status;
187 }
188
189 EAPI void
190 ecore_x_dnd_aware_set(Ecore_X_Window win,
191                       Eina_Bool      on)
192 {
193    Ecore_X_Atom prop_data = ECORE_X_DND_VERSION;
194
195    LOGFN(__FILE__, __LINE__, __FUNCTION__);
196
197    if (on)
198      ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_AWARE,
199                                       ECORE_X_ATOM_ATOM, 32, &prop_data, 1);
200    else
201      ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_AWARE);
202 }
203
204 EAPI int
205 ecore_x_dnd_version_get(Ecore_X_Window win)
206 {
207    unsigned char *data;
208    int num = 0;
209    Version_Cache_Item *t;
210
211    LOGFN(__FILE__, __LINE__, __FUNCTION__);
212
213    if (_source->state == ECORE_X_DND_SOURCE_DRAGGING)
214      {
215         if (_version_cache)
216           {
217              int i = 0;
218
219              for (i = 0; i < _version_cache_num; i++)
220                {
221                   if (_version_cache[i].win == win)
222                     return _version_cache[i].ver;
223                }
224           }
225      }
226
227    if (ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_AWARE,
228                                         ECORE_X_ATOM_ATOM, 32, &data, &num))
229      {
230         int version = 0;
231
232         version = (int)*data;
233         free(data);
234         if (_source->state == ECORE_X_DND_SOURCE_DRAGGING)
235           {
236              _version_cache_num++;
237              if (_version_cache_num > _version_cache_alloc)
238                _version_cache_alloc += 16;
239              t = realloc(_version_cache,
240                          _version_cache_alloc * sizeof(Version_Cache_Item));
241              if (!t) return 0;
242              _version_cache = t;
243              _version_cache[_version_cache_num - 1].win = win;
244              _version_cache[_version_cache_num - 1].ver = version;
245           }
246         return version;
247      }
248
249    if (_source->state == ECORE_X_DND_SOURCE_DRAGGING)
250      {
251         _version_cache_num++;
252         if (_version_cache_num > _version_cache_alloc)
253           _version_cache_alloc += 16;
254         t = realloc(_version_cache,
255                     _version_cache_alloc * sizeof(Version_Cache_Item));
256         if (!t) return 0;
257         _version_cache = t;
258         _version_cache[_version_cache_num - 1].win = win;
259         _version_cache[_version_cache_num - 1].ver = 0;
260      }
261
262    return 0;
263 }
264
265 EAPI Eina_Bool
266 ecore_x_dnd_type_isset(Ecore_X_Window win,
267                        const char    *type)
268 {
269    int num = 0, i = 0;
270    Eina_Bool ret = EINA_FALSE;
271    unsigned char *data;
272    Ecore_X_Atom *atoms, atom;
273
274    LOGFN(__FILE__, __LINE__, __FUNCTION__);
275    CHECK_XCB_CONN;
276
277    if (!ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_TYPE_LIST,
278                                          ECORE_X_ATOM_ATOM, 32, &data, &num))
279      return ret;
280
281    atom = ecore_x_atom_get(type);
282    atoms = (Ecore_X_Atom *)data;
283    for (i = 0; i < num; ++i)
284      {
285         if (atom == atoms[i])
286           {
287              ret = EINA_TRUE;
288              break;
289           }
290      }
291
292    free(data);
293    return ret;
294 }
295
296 EAPI void
297 ecore_x_dnd_type_set(Ecore_X_Window win,
298                      const char    *type,
299                      Eina_Bool      on)
300 {
301    Ecore_X_Atom atom, *oldset = NULL, *newset = NULL;
302    int i = 0, j = 0, num = 0;
303    unsigned char *data = NULL, *old_data = NULL;
304
305    LOGFN(__FILE__, __LINE__, __FUNCTION__);
306    CHECK_XCB_CONN;
307
308    atom = ecore_x_atom_get(type);
309    ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_TYPE_LIST,
310                                     ECORE_X_ATOM_ATOM, 32, &old_data, &num);
311    oldset = (Ecore_X_Atom *)old_data;
312    if (on)
313      {
314         if (ecore_x_dnd_type_isset(win, type))
315           {
316              free(old_data);
317              return;
318           }
319         newset = calloc(num + 1, sizeof(Ecore_X_Atom));
320         if (!newset) return;
321         data = (unsigned char *)newset;
322         for (i = 0; i < num; i++)
323           newset[i + 1] = oldset[i];
324         newset[0] = atom;
325         ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_TYPE_LIST,
326                                          ECORE_X_ATOM_ATOM, 32, data, num + 1);
327      }
328    else
329      {
330         if (!ecore_x_dnd_type_isset(win, type))
331           {
332              free(old_data);
333              return;
334           }
335         newset = calloc(num - 1, sizeof(Ecore_X_Atom));
336         if (!newset)
337           {
338              free(old_data);
339              return;
340           }
341         data = (unsigned char *)newset;
342         for (i = 0; i < num; i++)
343           if (oldset[i] != atom)
344             newset[j++] = oldset[i];
345         ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_TYPE_LIST,
346                                          ECORE_X_ATOM_ATOM, 32, data, num - 1);
347      }
348    free(oldset);
349    free(newset);
350 }
351
352 EAPI void
353 ecore_x_dnd_types_set(Ecore_X_Window win,
354                       const char   **types,
355                       unsigned int   num_types)
356 {
357    Ecore_X_Atom *newset = NULL;
358    unsigned int i;
359    unsigned char *data = NULL;
360
361    LOGFN(__FILE__, __LINE__, __FUNCTION__);
362    CHECK_XCB_CONN;
363
364    if (!num_types)
365      ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_TYPE_LIST);
366    else
367      {
368         newset = calloc(num_types, sizeof(Ecore_X_Atom));
369         if (!newset) return;
370
371         data = (unsigned char *)newset;
372         for (i = 0; i < num_types; i++)
373           {
374              newset[i] = ecore_x_atom_get(types[i]);
375              ecore_x_selection_converter_atom_add(newset[i],
376                                                   _ecore_xcb_dnd_converter_copy);
377           }
378         ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_TYPE_LIST,
379                                          ECORE_X_ATOM_ATOM, 32, data,
380                                          num_types);
381         free(newset);
382      }
383 }
384
385 EAPI void
386 ecore_x_dnd_actions_set(Ecore_X_Window win,
387                         Ecore_X_Atom  *actions,
388                         unsigned int   num_actions)
389 {
390    unsigned int i;
391    unsigned char *data = NULL;
392
393    LOGFN(__FILE__, __LINE__, __FUNCTION__);
394    CHECK_XCB_CONN;
395
396    if (!num_actions)
397      ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_ACTION_LIST);
398    else
399      {
400         data = (unsigned char *)actions;
401         for (i = 0; i < num_actions; i++)
402           ecore_x_selection_converter_atom_add(actions[i],
403                                                _ecore_xcb_dnd_converter_copy);
404         ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_ACTION_LIST,
405                                          ECORE_X_ATOM_ATOM, 32, data,
406                                          num_actions);
407      }
408 }
409
410 /**
411  * The DND position update cb is called Ecore_X sends a DND position to a
412  * client.
413  *
414  * It essentially mirrors some of the data sent in the position message.
415  * Generally this cb should be set just before position update is called.
416  * Please note well you need to look after your own data pointer if someone
417  * trashes you position update cb set.
418  *
419  * It is considered good form to clear this when the dnd event finishes.
420  *
421  * @param cb Callback to updated each time ecore_x sends a position update.
422  * @param data User data.
423  */
424 EAPI void
425 ecore_x_dnd_callback_pos_update_set(void (*cb)(void *, Ecore_X_Xdnd_Position *data),
426                                     const void *data)
427 {
428    _posupdatecb = cb;
429    _posupdatedata = (void *)data;
430 }
431
432 EAPI Eina_Bool
433 ecore_x_dnd_begin(Ecore_X_Window source,
434                   unsigned char *data,
435                   int            size)
436 {
437    LOGFN(__FILE__, __LINE__, __FUNCTION__);
438
439    if (!ecore_x_dnd_version_get(source)) return EINA_FALSE;
440
441    /* Take ownership of XdndSelection */
442    if (!ecore_x_selection_xdnd_set(source, data, size)) return EINA_FALSE;
443
444    if (_version_cache)
445      {
446         free(_version_cache);
447         _version_cache = NULL;
448         _version_cache_num = 0;
449         _version_cache_alloc = 0;
450      }
451
452    ecore_x_window_shadow_tree_flush();
453
454    _source->win = source;
455    ecore_x_window_ignore_set(_source->win, 1);
456    _source->state = ECORE_X_DND_SOURCE_DRAGGING;
457    _source->time = _ecore_xcb_events_last_time_get();
458    _source->prev.window = 0;
459
460    /* Default Accepted Action: move */
461    _source->action = ECORE_X_ATOM_XDND_ACTION_MOVE;
462    _source->accepted_action = XCB_NONE;
463    _source->dest = XCB_NONE;
464
465    return EINA_TRUE;
466 }
467
468 EAPI void
469 ecore_x_dnd_send_finished(void)
470 {
471    xcb_client_message_event_t ev;
472
473    LOGFN(__FILE__, __LINE__, __FUNCTION__);
474    CHECK_XCB_CONN;
475
476    if (_target->state == ECORE_X_DND_TARGET_IDLE) return;
477
478    memset(&ev, 0, sizeof(xcb_client_message_event_t));
479
480    ev.response_type = XCB_CLIENT_MESSAGE;
481    ev.format = 32;
482    ev.type = ECORE_X_ATOM_XDND_FINISHED;
483    ev.window = _target->source;
484    ev.data.data32[0] = _target->win;
485    ev.data.data32[1] = 0;
486    ev.data.data32[2] = 0;
487    if (_target->will_accept)
488      {
489         ev.data.data32[1] |= 0x1UL;
490         ev.data.data32[2] = _target->accepted_action;
491      }
492
493    xcb_send_event(_ecore_xcb_conn, 0, _target->source,
494                   XCB_EVENT_MASK_NO_EVENT, (const char *)&ev);
495 //   ecore_x_flush();
496    _target->state = ECORE_X_DND_TARGET_IDLE;
497 }
498
499 EAPI void
500 ecore_x_dnd_source_action_set(Ecore_X_Atom action)
501 {
502    LOGFN(__FILE__, __LINE__, __FUNCTION__);
503
504    _source->action = action;
505    if (_source->prev.window)
506      _ecore_xcb_dnd_drag(_source->prev.window,
507                          _source->prev.x, _source->prev.y);
508 }
509
510 Ecore_X_DND_Source *
511 _ecore_xcb_dnd_source_get(void)
512 {
513    return _source;
514 }
515
516 Ecore_X_DND_Target *
517 _ecore_xcb_dnd_target_get(void)
518 {
519    return _target;
520 }
521
522 void
523 _ecore_xcb_dnd_drag(Ecore_X_Window root,
524                     int            x,
525                     int            y)
526 {
527    xcb_client_message_event_t ev;
528    Ecore_X_Window win, *skip;
529    Ecore_X_Xdnd_Position pos;
530    int num = 0;
531
532    if (_source->state != ECORE_X_DND_SOURCE_DRAGGING) return;
533
534    LOGFN(__FILE__, __LINE__, __FUNCTION__);
535    CHECK_XCB_CONN;
536
537    memset(&ev, 0, sizeof(xcb_client_message_event_t));
538
539    ev.response_type = XCB_CLIENT_MESSAGE;
540    ev.format = 32;
541
542    skip = ecore_x_window_ignore_list(&num);
543    win = ecore_x_window_shadow_tree_at_xy_with_skip_get(root, x, y, skip, num);
544    while ((win) && !(ecore_x_dnd_version_get(win)))
545      win = ecore_x_window_shadow_parent_get(root, win);
546
547    if ((_source->dest) && (win != _source->dest))
548      {
549         ev.window = _source->dest;
550         ev.type = ECORE_X_ATOM_XDND_LEAVE;
551         ev.data.data32[0] = _source->win;
552         ev.data.data32[1] = 0;
553
554         xcb_send_event(_ecore_xcb_conn, 0, _source->dest,
555                        XCB_EVENT_MASK_NO_EVENT, (const char *)&ev);
556 //        ecore_x_flush();
557         _source->suppress = 0;
558      }
559
560    if (win)
561      {
562         int x1, x2, y1, y2;
563
564         _source->version = MIN(ECORE_X_DND_VERSION,
565                                ecore_x_dnd_version_get(win));
566         if (win != _source->dest)
567           {
568              int i = 0;
569              unsigned char *data;
570              Ecore_X_Atom *types;
571
572              ecore_x_window_prop_property_get(_source->win,
573                                               ECORE_X_ATOM_XDND_TYPE_LIST,
574                                               ECORE_X_ATOM_ATOM, 32,
575                                               &data, &num);
576              types = (Ecore_X_Atom *)data;
577              ev.window = win;
578              ev.type = ECORE_X_ATOM_XDND_ENTER;
579              ev.data.data32[0] = _source->win;
580              ev.data.data32[1] = 0;
581              if (num > 3)
582                ev.data.data32[1] |= 0x1UL;
583              else
584                ev.data.data32[1] &= 0xfffffffeUL;
585              ev.data.data32[1] |= ((unsigned long)_source->version) << 24;
586
587              for (i = 2; i < 5; i++)
588                ev.data.data32[i] = 0;
589              for (i = 0; i < MIN(num, 3); ++i)
590                ev.data.data32[i + 2] = types[i];
591              free(data);
592
593              xcb_send_event(_ecore_xcb_conn, 0, win,
594                             XCB_EVENT_MASK_NO_EVENT, (const char *)&ev);
595 //             ecore_x_flush();
596              _source->await_status = 0;
597              _source->will_accept = 0;
598           }
599
600         x1 = _source->rectangle.x;
601         x2 = _source->rectangle.x + _source->rectangle.width;
602         y1 = _source->rectangle.y;
603         y2 = _source->rectangle.y + _source->rectangle.height;
604
605         if ((!_source->await_status) || (!_source->suppress) ||
606             ((x < x1) || (x > x2) || (y < y1) || (y > y2)))
607           {
608              ev.window = win;
609              ev.type = ECORE_X_ATOM_XDND_POSITION;
610              ev.data.data32[0] = _source->win;
611              ev.data.data32[1] = 0;
612              ev.data.data32[2] = ((x << 16) & 0xffff0000) | (y & 0xffff);
613              ev.data.data32[3] = _source->time;
614              ev.data.data32[4] = _source->action;
615
616              xcb_send_event(_ecore_xcb_conn, 0, win,
617                             XCB_EVENT_MASK_NO_EVENT, (const char *)&ev);
618 //             ecore_x_flush();
619              _source->await_status = 1;
620           }
621      }
622
623    if (_posupdatecb)
624      {
625         pos.position.x = x;
626         pos.position.y = y;
627         pos.win = win;
628         pos.prev = _source->dest;
629         _posupdatecb(_posupdatedata, &pos);
630      }
631
632    _source->prev.x = x;
633    _source->prev.y = y;
634    _source->prev.window = root;
635    _source->dest = win;
636 }
637
638 EAPI Ecore_X_Atom
639 ecore_x_dnd_source_action_get(void)
640 {
641    return _source->action;
642 }
643
644 /* local functions */
645 static Eina_Bool
646 _ecore_xcb_dnd_converter_copy(char         *target __UNUSED__,
647                               void         *data,
648                               int           size,
649                               void        **data_ret,
650                               int          *size_ret,
651                               Ecore_X_Atom *tprop __UNUSED__,
652                               int          *count __UNUSED__)
653 {
654    Ecore_Xcb_Textproperty text_prop;
655    Ecore_Xcb_Encoding_Style style = XcbTextStyle;
656    char *mystr;
657
658    LOGFN(__FILE__, __LINE__, __FUNCTION__);
659
660    if ((!data) || (!size)) return EINA_FALSE;
661
662    mystr = calloc(1, size + 1);
663    if (!mystr) return EINA_FALSE;
664
665    memcpy(mystr, data, size);
666    if (_ecore_xcb_mb_textlist_to_textproperty(&mystr, 1, style, &text_prop))
667      {
668         int len;
669
670         len = strlen((char *)text_prop.value) + 1;
671         if (!(*data_ret = malloc(len)))
672           {
673              free(mystr);
674              return EINA_FALSE;
675           }
676         memcpy(*data_ret, text_prop.value, len);
677         *size_ret = len;
678         free(text_prop.value);
679         free(mystr);
680         return EINA_TRUE;
681      }
682    else
683      {
684         free(mystr);
685         return EINA_FALSE;
686      }
687 }
688