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