Ecore_X(cb): Remove extra calls to ecore_x_flush. Move main loop
[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__, void *data, int size, void **data_ret, int *size_ret, Ecore_X_Atom *tprop __UNUSED__, int *count __UNUSED__);
16
17 /* local variables */
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;
25
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;
33
34 void 
35 _ecore_xcb_dnd_init(void) 
36 {
37    LOGFN(__FILE__, __LINE__, __FUNCTION__);
38
39    if (!_ecore_xcb_dnd_init_count) 
40      {
41         _source = calloc(1, sizeof(Ecore_X_DND_Source));
42         if (!_source) return;
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;
48
49         _target = calloc(1, sizeof(Ecore_X_DND_Target));
50         if (!_target) 
51           {
52              free(_source);
53              _source = NULL;
54              return;
55           }
56         _target->win = XCB_NONE;
57         _target->source = XCB_NONE;
58         _target->state = ECORE_X_DND_TARGET_IDLE;
59
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();
66      }
67    _ecore_xcb_dnd_init_count++;
68 }
69
70 void 
71 _ecore_xcb_dnd_shutdown(void) 
72 {
73    LOGFN(__FILE__, __LINE__, __FUNCTION__);
74
75    _ecore_xcb_dnd_init_count--;
76    if (_ecore_xcb_dnd_init_count > 0) return;
77    if (_source) free(_source);
78    _source = NULL;
79    if (_target) free(_target);
80    _target = NULL;
81    _ecore_xcb_dnd_init_count = 0;
82 }
83
84 EAPI void 
85 ecore_x_dnd_send_status(Eina_Bool will_accept, Eina_Bool suppress, Ecore_X_Rectangle rect, Ecore_X_Atom action) 
86 {
87    xcb_client_message_event_t ev;
88
89    LOGFN(__FILE__, __LINE__, __FUNCTION__);
90
91    if (_target->state == ECORE_X_DND_TARGET_IDLE) return;
92
93    DBG("Ecore_X_Dnd_Send_Status");
94    memset(&ev, 0, sizeof(xcb_client_message_event_t));
95
96    _target->will_accept = will_accept;
97
98    ev.response_type = XCB_CLIENT_MESSAGE;
99    ev.type = ECORE_X_ATOM_XDND_STATUS;
100    ev.format = 32;
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;
106
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;
113
114    if (will_accept) 
115      ev.data.data32[4] = action;
116    else 
117      ev.data.data32[4] = XCB_NONE;
118    _target->accepted_action = action;
119
120    xcb_send_event(_ecore_xcb_conn, 0, _target->source, 
121                   XCB_EVENT_MASK_NO_EVENT, (const char *)&ev);
122 //   ecore_x_flush();
123 }
124
125 EAPI Eina_Bool 
126 ecore_x_dnd_drop(void) 
127 {
128    xcb_client_message_event_t ev;
129    Eina_Bool status = EINA_FALSE;
130
131    LOGFN(__FILE__, __LINE__, __FUNCTION__);
132
133    memset(&ev, 0, sizeof(xcb_client_message_event_t));
134
135    DBG("Ecore_X_Dnd_Drop");
136    if (_source->dest) 
137      {
138         ev.response_type = XCB_CLIENT_MESSAGE;
139         ev.format = 32;
140         ev.window = _source->dest;
141
142         if (_source->will_accept) 
143           {
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;
148
149              xcb_send_event(_ecore_xcb_conn, 0, _source->dest, 
150                             XCB_EVENT_MASK_NO_EVENT, (const char *)&ev);
151 //             ecore_x_flush();
152              _source->state = ECORE_X_DND_SOURCE_DROPPED;
153              status = EINA_TRUE;
154           }
155         else 
156           {
157              ev.type = ECORE_X_ATOM_XDND_LEAVE;
158              ev.data.data32[0] = _source->win;
159              ev.data.data32[1] = 0;
160
161              xcb_send_event(_ecore_xcb_conn, 0, _source->dest, 
162                             XCB_EVENT_MASK_NO_EVENT, (const char *)&ev);
163 //             ecore_x_flush();
164              _source->state = ECORE_X_DND_SOURCE_IDLE;
165           }
166      }
167    else 
168      {
169         ecore_x_selection_xdnd_clear();
170         _source->state = ECORE_X_DND_SOURCE_IDLE;
171      }
172
173    ecore_x_window_ignore_set(_source->win, 0);
174    _source->prev.window = 0;
175
176    return status;
177 }
178
179 EAPI void 
180 ecore_x_dnd_aware_set(Ecore_X_Window win, Eina_Bool on) 
181 {
182    Ecore_X_Atom prop_data = ECORE_X_DND_VERSION;
183
184    LOGFN(__FILE__, __LINE__, __FUNCTION__);
185
186    DBG("Ecore_X_Dnd_Aware_Set");
187    if (on)
188      ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_AWARE, 
189                                       ECORE_X_ATOM_ATOM, 32, &prop_data, 1);
190    else
191      ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_AWARE);
192 }
193
194 EAPI int 
195 ecore_x_dnd_version_get(Ecore_X_Window win) 
196 {
197    unsigned char *data;
198    int num = 0;
199    Version_Cache_Item *t;
200
201    LOGFN(__FILE__, __LINE__, __FUNCTION__);
202
203    if (_source->state == ECORE_X_DND_SOURCE_DRAGGING) 
204      {
205         if (_version_cache) 
206           {
207              int i = 0;
208
209              for (i = 0; i < _version_cache_num; i++) 
210                {
211                   if (_version_cache[i].win == win)
212                     return _version_cache[i].ver;
213                }
214           }
215      }
216
217    if (ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_AWARE, 
218                                         ECORE_X_ATOM_ATOM, 32, &data, &num)) 
219      {
220         int version = 0;
221
222         version = (int)*data;
223         free(data);
224         if (_source->state == ECORE_X_DND_SOURCE_DRAGGING) 
225           {
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));
231              if (!t) return 0;
232              _version_cache = t;
233              _version_cache[_version_cache_num - 1].win = win;
234              _version_cache[_version_cache_num - 1].ver = version;
235           }
236         return version;
237      }
238
239    if (_source->state == ECORE_X_DND_SOURCE_DRAGGING) 
240      {
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));
246         if (!t) return 0;
247         _version_cache = t;
248         _version_cache[_version_cache_num - 1].win = win;
249         _version_cache[_version_cache_num - 1].ver = 0;
250      }
251
252    return 0;
253 }
254
255 EAPI Eina_Bool 
256 ecore_x_dnd_type_isset(Ecore_X_Window win, const char *type) 
257 {
258    int num = 0, i = 0;
259    Eina_Bool ret = EINA_FALSE;
260    unsigned char *data;
261    Ecore_X_Atom *atoms, atom;
262
263    LOGFN(__FILE__, __LINE__, __FUNCTION__);
264
265    if (!ecore_x_window_prop_property_get(win, ECORE_X_ATOM_XDND_TYPE_LIST, 
266                                          ECORE_X_ATOM_ATOM, 32, &data, &num))
267      return ret;
268
269    atom = ecore_x_atom_get(type);
270    atoms = (Ecore_X_Atom *)data;
271    for (i = 0; i < num; ++i) 
272      {
273         if (atom == atoms[i]) 
274           {
275              ret = EINA_TRUE;
276              break;
277           }
278      }
279
280    free(data);
281    return ret;
282 }
283
284 EAPI void 
285 ecore_x_dnd_type_set(Ecore_X_Window win, const char *type, Eina_Bool on) 
286 {
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;
290
291    LOGFN(__FILE__, __LINE__, __FUNCTION__);
292
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;
298    if (on) 
299      {
300         if (ecore_x_dnd_type_isset(win, type)) 
301           {
302              free(old_data);
303              return;
304           }
305         newset = calloc(num + 1, sizeof(Ecore_X_Atom));
306         if (!newset) return;
307         data = (unsigned char *)newset;
308         for (i = 0; i < num; i++)
309           newset[i + 1] = oldset[i];
310         newset[0] = atom;
311         ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_TYPE_LIST, 
312                                          ECORE_X_ATOM_ATOM, 32, data, num + 1);
313      }
314    else 
315      {
316         if (!ecore_x_dnd_type_isset(win, type)) 
317           {
318              free(old_data);
319              return;
320           }
321         newset = calloc(num - 1, sizeof(Ecore_X_Atom));
322         if (!newset) 
323           {
324              free(old_data);
325              return;
326           }
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);
333      }
334    free(oldset);
335    free(newset);
336 }
337
338 EAPI void
339 ecore_x_dnd_types_set(Ecore_X_Window win, const char **types, unsigned int num_types)
340 {
341    Ecore_X_Atom *newset = NULL;
342    unsigned int i;
343    unsigned char *data = NULL;
344
345    LOGFN(__FILE__, __LINE__, __FUNCTION__);
346
347    DBG("Ecore_X_Dnd_Types_Set");
348    if (!num_types)
349       ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_TYPE_LIST);
350    else
351      {
352         newset = calloc(num_types, sizeof(Ecore_X_Atom));
353         if (!newset) return;
354
355         data = (unsigned char *)newset;
356         for (i = 0; i < num_types; i++)
357           {
358              newset[i] = ecore_x_atom_get(types[i]);
359              ecore_x_selection_converter_atom_add(newset[i],
360                                                   _ecore_xcb_dnd_converter_copy);
361           }
362         ecore_x_window_prop_property_set(win, ECORE_X_ATOM_XDND_TYPE_LIST,
363                                          ECORE_X_ATOM_ATOM, 32, data, 
364                                          num_types);
365         free(newset);
366      }
367 }
368
369 EAPI void
370 ecore_x_dnd_actions_set(Ecore_X_Window win, Ecore_X_Atom *actions, unsigned int num_actions)
371 {
372    unsigned int i;
373    unsigned char *data = NULL;
374
375    LOGFN(__FILE__, __LINE__, __FUNCTION__);
376
377    DBG("Ecore_X_Dnd_Actions_Set");
378    if (!num_actions)
379       ecore_x_window_prop_property_del(win, ECORE_X_ATOM_XDND_ACTION_LIST);
380    else
381      {
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, 
388                                          num_actions);
389      }
390 }
391
392 /**
393  * The DND position update cb is called Ecore_X sends a DND position to a
394  * client.
395  *
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.
400  *
401  * It is considered good form to clear this when the dnd event finishes.
402  *
403  * @param cb Callback to updated each time ecore_x sends a position update.
404  * @param data User data.
405  */
406 EAPI void
407 ecore_x_dnd_callback_pos_update_set(void (*cb)(void *, Ecore_X_Xdnd_Position *data), const void *data)
408 {
409    _posupdatecb = cb;
410    _posupdatedata = (void *)data;
411 }
412
413 EAPI Eina_Bool 
414 ecore_x_dnd_begin(Ecore_X_Window source, unsigned char *data, int size) 
415 {
416    LOGFN(__FILE__, __LINE__, __FUNCTION__);
417
418    if (!ecore_x_dnd_version_get(source)) return EINA_FALSE;
419
420    DBG("Ecore_X_Dnd_Begin");
421    /* Take ownership of XdndSelection */
422    if (!ecore_x_selection_xdnd_set(source, data, size)) return EINA_FALSE;
423
424    if (_version_cache)
425      {
426         free(_version_cache);
427         _version_cache = NULL;
428         _version_cache_num = 0;
429         _version_cache_alloc = 0;
430      }
431
432    ecore_x_window_shadow_tree_flush();
433
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;
439
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;
444
445    return EINA_TRUE;
446 }
447
448 EAPI void 
449 ecore_x_dnd_send_finished(void) 
450 {
451    xcb_client_message_event_t ev;
452
453    LOGFN(__FILE__, __LINE__, __FUNCTION__);
454
455    if (_target->state == ECORE_X_DND_TARGET_IDLE) return;
456
457    DBG("Ecore_X_Dnd_Send_Finished");
458    memset(&ev, 0, sizeof(xcb_client_message_event_t));
459
460    ev.response_type = XCB_CLIENT_MESSAGE;
461    ev.format = 32;
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) 
468      {
469         ev.data.data32[1] |= 0x1UL;
470         ev.data.data32[2] = _target->accepted_action;
471      }
472
473    xcb_send_event(_ecore_xcb_conn, 0, _target->source, 
474                   XCB_EVENT_MASK_NO_EVENT, (const char *)&ev);
475 //   ecore_x_flush();
476    _target->state = ECORE_X_DND_TARGET_IDLE;
477 }
478
479 EAPI void 
480 ecore_x_dnd_source_action_set(Ecore_X_Atom action) 
481 {
482    LOGFN(__FILE__, __LINE__, __FUNCTION__);
483
484    _source->action = action;
485    if (_source->prev.window)
486      _ecore_xcb_dnd_drag(_source->prev.window, 
487                          _source->prev.x, _source->prev.y);
488 }
489
490 Ecore_X_DND_Source *
491 _ecore_xcb_dnd_source_get(void) 
492 {
493    return _source;
494 }
495
496 Ecore_X_DND_Target *
497 _ecore_xcb_dnd_target_get(void) 
498 {
499    return _target;
500 }
501
502 void 
503 _ecore_xcb_dnd_drag(Ecore_X_Window root, int x, int y) 
504 {
505    xcb_client_message_event_t ev;
506    Ecore_X_Window win, *skip;
507    Ecore_X_Xdnd_Position pos;
508    int num = 0;
509
510    if (_source->state != ECORE_X_DND_SOURCE_DRAGGING) return;
511
512    LOGFN(__FILE__, __LINE__, __FUNCTION__);
513
514    DBG("Ecore_X_Dnd_Drag");
515    memset(&ev, 0, sizeof(xcb_client_message_event_t));
516
517    ev.response_type = XCB_CLIENT_MESSAGE;
518    ev.format = 32;
519
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);
524
525    if ((_source->dest) && (win != _source->dest)) 
526      {
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;
531
532         xcb_send_event(_ecore_xcb_conn, 0, _source->dest, 
533                        XCB_EVENT_MASK_NO_EVENT, (const char *)&ev);
534 //        ecore_x_flush();
535         _source->suppress = 0;
536      }
537
538    if (win) 
539      {
540         int x1, x2, y1, y2;
541
542         _source->version = MIN(ECORE_X_DND_VERSION, 
543                                ecore_x_dnd_version_get(win));
544         if (win != _source->dest) 
545           {
546              int i = 0;
547              unsigned char *data;
548              Ecore_X_Atom *types;
549
550              ecore_x_window_prop_property_get(_source->win, 
551                                               ECORE_X_ATOM_XDND_TYPE_LIST, 
552                                               ECORE_X_ATOM_ATOM, 32, 
553                                               &data, &num);
554              types = (Ecore_X_Atom *)data;
555              ev.window = win;
556              ev.type = ECORE_X_ATOM_XDND_ENTER;
557              ev.data.data32[0] = _source->win;
558              ev.data.data32[1] = 0;
559              if (num > 3)
560                ev.data.data32[1] |= 0x1UL;
561              else
562                ev.data.data32[1] &= 0xfffffffeUL;
563              ev.data.data32[1] |= ((unsigned long)_source->version) << 24;
564
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];
569              free(data);
570
571              xcb_send_event(_ecore_xcb_conn, 0, win, 
572                             XCB_EVENT_MASK_NO_EVENT, (const char *)&ev);
573 //             ecore_x_flush();
574              _source->await_status = 0;
575              _source->will_accept = 0;
576           }
577
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;
582
583         if ((!_source->await_status) || (!_source->suppress) || 
584             ((x < x1) || (x > x2) || (y < y1) || (y > y2))) 
585           {
586              ev.window = win;
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;
593
594              xcb_send_event(_ecore_xcb_conn, 0, win, 
595                             XCB_EVENT_MASK_NO_EVENT, (const char *)&ev);
596 //             ecore_x_flush();
597              _source->await_status = 1;
598           }
599      }
600
601    if (_posupdatecb) 
602      {
603         pos.position.x = x;
604         pos.position.y = y;
605         pos.win = win;
606         pos.prev = _source->dest;
607         _posupdatecb(_posupdatedata, &pos);
608      }
609
610    _source->prev.x = x;
611    _source->prev.y = y;
612    _source->prev.window = root;
613    _source->dest = win;
614 }
615
616 EAPI Ecore_X_Atom 
617 ecore_x_dnd_source_action_get(void) 
618 {
619    return _source->action;
620 }
621
622 /* local functions */
623 static Eina_Bool 
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__) 
625 {
626    Ecore_Xcb_Textproperty text_prop;
627    Ecore_Xcb_Encoding_Style style = XcbTextStyle;
628    char *mystr;
629
630    LOGFN(__FILE__, __LINE__, __FUNCTION__);
631
632    if ((!data) || (!size)) return EINA_FALSE;
633
634    mystr = calloc(1, size + 1);
635    if (!mystr) return EINA_FALSE;
636
637    memcpy(mystr, data, size);
638    if (_ecore_xcb_mb_textlist_to_textproperty(&mystr, 1, style, &text_prop)) 
639      {
640         int len;
641
642         len = strlen((char *)text_prop.value) + 1;
643         if (!(*data_ret = malloc(len))) 
644           {
645              free(mystr);
646              return EINA_FALSE;
647           }
648         memcpy(*data_ret, text_prop.value, len);
649         *size_ret = len;
650         free(text_prop.value);
651         free(mystr);
652         return EINA_TRUE;
653      }
654    else 
655      {
656         free(mystr);
657         return EINA_FALSE;
658      }
659 }