7405b4635f6b8377d3d142e55d3e7301a1b5c515
[framework/uifw/e17.git] / src / bin / e_pointer.c
1 #include "e.h"
2
3 /*
4  * TODO
5  * - Make fallback user controlable.
6  * - Define the allowed signals?
7  */
8
9 typedef struct _E_Pointer_Stack E_Pointer_Stack;
10
11 struct _E_Pointer_Stack
12 {
13    void *obj;
14    const char *type;
15 };
16
17 static Eina_List *_e_pointers = NULL;
18 static Eina_List *handlers = NULL;
19
20 static void _e_pointer_canvas_add(E_Pointer *p);
21 static void _e_pointer_canvas_del(E_Pointer *p);
22 static void _e_pointer_cb_move(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info);
23 static void _e_pointer_free(E_Pointer *p);
24 static void _e_pointer_stack_free(E_Pointer_Stack *elem);
25 static void  _e_pointer_type_set(E_Pointer *p, const char *type);
26 static void _e_pointer_active_handle(E_Pointer *p);
27
28 static Eina_Bool _e_pointer_cb_mouse_down(void *data, int type, void *event);
29 static Eina_Bool _e_pointer_cb_mouse_up(void *data, int type, void *event);
30 static Eina_Bool _e_pointer_cb_mouse_move(void *data, int type, void *event);
31 static Eina_Bool _e_pointer_cb_mouse_wheel(void *data, int type, void *event);
32 static Eina_Bool _e_pointer_cb_idle_timer_pre(void *data);
33 static Eina_Bool _e_pointer_cb_idle_timer_wait(void *data);
34 static Eina_Bool _e_pointer_cb_idle_poller(void *data);
35
36 /* externally accessible functions */
37 EINTERN int
38 e_pointer_init(void)
39 {
40    handlers = 
41      eina_list_append(handlers, 
42                       ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_DOWN, 
43                                               _e_pointer_cb_mouse_down, NULL));
44    handlers = 
45      eina_list_append(handlers, 
46                       ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_UP, 
47                                               _e_pointer_cb_mouse_up, NULL));
48    handlers = 
49      eina_list_append(handlers, 
50                       ecore_event_handler_add(ECORE_EVENT_MOUSE_MOVE, 
51                                               _e_pointer_cb_mouse_move, NULL));
52    handlers = 
53      eina_list_append(handlers, 
54                       ecore_event_handler_add(ECORE_EVENT_MOUSE_WHEEL, 
55                                               _e_pointer_cb_mouse_wheel, NULL));
56    return 1;
57 }
58
59 EINTERN int
60 e_pointer_shutdown(void)
61 {
62    E_FREE_LIST(handlers, ecore_event_handler_del);
63    return 1;
64 }
65
66 EAPI E_Pointer *
67 e_pointer_window_new(Ecore_X_Window win, int filled)
68 {
69    E_Pointer *p = NULL;
70
71    p = E_OBJECT_ALLOC(E_Pointer, E_POINTER_TYPE, _e_pointer_free);
72    if (!p) return NULL;
73
74    p->e_cursor = e_config->use_e_cursor;
75    p->win = win;
76    p->color = 0;
77    if (e_config->use_e_cursor)
78      if (ecore_x_cursor_color_supported_get()) p->color = 1;
79
80    ecore_x_cursor_size_set(e_config->cursor_size * 3 / 4);
81    if (filled) e_pointer_type_push(p, p, "default");
82    _e_pointers = eina_list_append(_e_pointers, p); 
83    return p;
84 }
85
86 EAPI void
87 e_pointers_size_set(int size)
88 {
89    Eina_List *l;
90    E_Pointer *p;
91
92    if (!e_config->show_cursor) return;
93    EINA_LIST_FOREACH(_e_pointers, l, p)
94      {
95         Evas_Engine_Info_Buffer *einfo;
96
97         if (p->evas)
98           {
99              p->w = p->h = size;
100              evas_output_size_set(p->evas, p->w, p->h);
101              evas_output_viewport_set(p->evas, 0, 0, p->w, p->h);
102
103              p->pixels = realloc(p->pixels, p->w * p->h * sizeof(int));
104
105              einfo = (Evas_Engine_Info_Buffer *)evas_engine_info_get(p->evas);
106              if (einfo)
107                {
108                   einfo->info.dest_buffer = p->pixels;
109                   einfo->info.dest_buffer_row_bytes = p->w * sizeof(int);
110                   evas_engine_info_set(p->evas, (Evas_Engine_Info *)einfo);
111                }
112
113              evas_object_move(p->pointer_object, 0, 0);
114              evas_object_resize(p->pointer_object, p->w, p->h);
115           }
116         else
117           {
118              const char *type;
119
120              ecore_x_cursor_size_set(e_config->cursor_size * 3 / 4);
121              type = p->type;
122              if (type)
123                {
124                   p->type = NULL;
125                   _e_pointer_type_set(p, type);
126                   eina_stringshare_del(type);
127                }
128           }
129      }
130 }
131
132 EAPI void
133 e_pointer_hide(E_Pointer *p)
134 {
135     if (p->win) ecore_x_window_cursor_set(p->win, 0);
136     if (p->evas) _e_pointer_canvas_del(p);
137 }
138
139 EAPI void
140 e_pointer_type_push(E_Pointer *p, void *obj, const char *type)
141 {
142    E_Pointer_Stack *stack;
143
144    p->e_cursor = e_config->use_e_cursor;
145
146    _e_pointer_type_set(p, type);
147
148    p->obj = obj;
149
150    stack = E_NEW(E_Pointer_Stack, 1);
151    if (stack)
152      {
153         stack->type = eina_stringshare_add(p->type);
154         stack->obj = p->obj;
155         p->stack = eina_list_prepend(p->stack, stack);
156      }
157 }
158
159 EAPI void
160 e_pointer_type_pop(E_Pointer *p, void *obj, const char *type)
161 {
162    Eina_List *l;
163    E_Pointer_Stack *stack;
164
165    EINA_LIST_FOREACH(p->stack, l, stack)
166      {
167         if ((stack->obj == obj) && ((!type) || (!strcmp(stack->type, type))))
168           {
169              _e_pointer_stack_free(stack);
170              p->stack = eina_list_remove_list(p->stack, l);
171              if (type) break;
172           }
173      }
174
175    if (!p->stack)
176      {
177         if (p->evas) _e_pointer_canvas_del(p);
178         ecore_x_window_cursor_set(p->win, 0);
179         if (p->type) eina_stringshare_del(p->type);
180         p->type = NULL;
181         return;
182      }
183
184    stack = eina_list_data_get(p->stack);
185    _e_pointer_type_set(p, stack->type);
186
187    if (p->type) eina_stringshare_del(p->type);
188    p->type = eina_stringshare_add(stack->type);
189    p->obj = stack->obj;
190
191    /* try the default cursor next time */
192    p->e_cursor = e_config->use_e_cursor;
193 }
194
195 EAPI void
196 e_pointer_idler_before(void)
197 {
198    Eina_List *l;
199    E_Pointer *p;
200
201    if (!e_config->show_cursor) return;
202    EINA_LIST_FOREACH(_e_pointers, l, p)
203      {
204         Eina_List *updates;
205
206         if (!p->e_cursor) continue;
207         if (!p->evas) continue;
208
209         updates = evas_render_updates(p->evas);
210         if ((updates) || (p->hot.update))
211           {
212              Ecore_X_Cursor cur;
213
214              cur = ecore_x_cursor_new(p->win, p->pixels, p->w, p->h, 
215                                       p->hot.x, p->hot.y);
216              ecore_x_window_cursor_set(p->win, cur);
217              ecore_x_cursor_free(cur);
218              evas_render_updates_free(updates);
219              p->hot.update = 0;
220           }
221      }
222 }
223
224 /* local subsystem functions */
225 static void
226 _e_pointer_canvas_add(E_Pointer *p)
227 {
228    Evas_Engine_Info_Buffer *einfo;
229    Evas_Object *o;
230    int rmethod;
231
232    p->w = e_config->cursor_size;
233    p->h = e_config->cursor_size;
234
235    /* create evas */
236    p->evas = evas_new();
237    if (!p->evas)
238      {
239         e_object_del(E_OBJECT(p));
240         return;
241      }
242    rmethod = evas_render_method_lookup("buffer");
243    evas_output_method_set(p->evas, rmethod);
244    evas_output_size_set(p->evas, p->w, p->h);
245    evas_output_viewport_set(p->evas, 0, 0, p->w, p->h);
246
247    p->pixels = malloc(p->w * p->h * sizeof(int));
248    if (!p->pixels)
249      {
250         _e_pointer_canvas_del(p);
251         return;
252      }
253    einfo = (Evas_Engine_Info_Buffer *)evas_engine_info_get(p->evas);
254    if (!einfo)
255      {
256         _e_pointer_canvas_del(p);
257         return;
258      }
259    einfo->info.depth_type = EVAS_ENGINE_BUFFER_DEPTH_ARGB32;
260    einfo->info.dest_buffer = p->pixels;
261    einfo->info.dest_buffer_row_bytes = p->w * sizeof(int);
262    einfo->info.use_color_key = 0;
263    einfo->info.alpha_threshold = 0;
264    einfo->info.func.new_update_region = NULL;
265    einfo->info.func.free_update_region = NULL;
266    evas_engine_info_set(p->evas, (Evas_Engine_Info *)einfo);
267
268    /* set the pointer edje */
269    o = edje_object_add(p->evas);
270    p->pointer_object = o;
271    /* Create the hotspot object */
272    o = evas_object_rectangle_add(p->evas);
273    evas_object_color_set(o, 0, 0, 0, 0);
274    p->hot_object = o;   
275    evas_object_event_callback_add(o, EVAS_CALLBACK_MOVE,
276                                   _e_pointer_cb_move, p);
277    /* init edje */
278    evas_object_move(p->pointer_object, 0, 0);
279    evas_object_resize(p->pointer_object, p->w, p->h);
280    evas_object_show(p->pointer_object);
281 }
282
283 static void
284 _e_pointer_canvas_del(E_Pointer *p)
285 {
286    if (p->pointer_object) evas_object_del(p->pointer_object);
287    if (p->hot_object) evas_object_del(p->hot_object);
288    if (p->evas) evas_free(p->evas);
289    if (p->pixels) free(p->pixels);
290    p->pointer_object = NULL;
291    p->hot_object = NULL;
292    p->evas = NULL;
293    p->pixels = NULL;
294 }
295
296 static void
297 _e_pointer_cb_move(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
298 {
299    E_Pointer *p;
300    Evas_Coord x, y;
301
302    if (!e_config->show_cursor) return;
303
304    p = data;
305    if (!p->e_cursor) return;
306    edje_object_part_geometry_get(p->pointer_object, "e.swallow.hotspot",
307                                  &x, &y, NULL, NULL);
308    if ((p->hot.x != x) || (p->hot.y != y))
309      {
310         p->hot.x = x;
311         p->hot.y = y;
312         p->hot.update = 1;
313      }
314 }
315
316 static void
317 _e_pointer_free(E_Pointer *p)
318 {
319    _e_pointers = eina_list_remove(_e_pointers, p);
320
321    _e_pointer_canvas_del(p);
322
323    E_FREE_LIST(p->stack, _e_pointer_stack_free);
324
325    if (p->type) eina_stringshare_del(p->type);
326    if (p->idle_timer) ecore_timer_del(p->idle_timer);
327    if (p->idle_poller) ecore_poller_del(p->idle_poller);
328
329    p->type = NULL;
330    p->idle_timer = NULL;
331    p->idle_poller = NULL;
332    E_FREE(p);
333 }
334
335 static void
336 _e_pointer_stack_free(E_Pointer_Stack *elem)
337 {
338    if (elem->type) eina_stringshare_del(elem->type);
339    free(elem);
340 }
341
342 static void 
343 _e_pointer_type_set(E_Pointer *p, const char *type)
344 {
345    /* Check if this pointer is already set */
346    if ((p->type) && (!strcmp(p->type, type))) return;
347
348    if (p->type) eina_stringshare_del(p->type);
349    p->type = eina_stringshare_add(type);
350
351    /* Do not set type if in "hidden mode" */
352    if (!e_config->show_cursor)
353      {
354         ecore_x_window_cursor_set(p->win, 0);
355         return;
356      }
357
358    if (p->e_cursor)
359      {
360         char cursor[1024];
361         Evas_Coord x, y;
362
363         if (!p->evas) _e_pointer_canvas_add(p);
364         if (p->color)
365           snprintf(cursor, sizeof(cursor), 
366                    "e/pointer/enlightenment/%s/color", type);
367         else
368           snprintf(cursor, sizeof(cursor), 
369                    "e/pointer/enlightenment/%s/mono", type);
370         if (!e_theme_edje_object_set(p->pointer_object, 
371                                      "base/theme/pointer", cursor))
372           goto fallback;
373         edje_object_part_swallow(p->pointer_object, "e.swallow.hotspot", 
374                                  p->hot_object);
375         edje_object_part_geometry_get(p->pointer_object, "e.swallow.hotspot", 
376                                       &x, &y, NULL, NULL);
377         if ((p->hot.x != x) || (p->hot.y != y))
378           {
379              p->hot.x = x;
380              p->hot.y = y;
381           }
382         p->hot.update = 1;
383         return;
384      }
385    fallback:
386      {
387         Ecore_X_Cursor cursor = 0;
388
389         if (p->evas) _e_pointer_canvas_del(p);
390         if (!strcmp(type, "move"))
391           cursor = ecore_x_cursor_shape_get(ECORE_X_CURSOR_FLEUR);
392 #if 0
393         else if (!strcmp(type, "resize"))
394           cursor = ecore_x_cursor_shape_get(ECORE_X_CURSOR_SIZING);
395 #endif
396         else if (!strcmp(type, "resize_tl"))
397           cursor = ecore_x_cursor_shape_get(ECORE_X_CURSOR_TOP_LEFT_CORNER);
398         else if (!strcmp(type, "resize_t"))
399           cursor = ecore_x_cursor_shape_get(ECORE_X_CURSOR_TOP_SIDE);
400         else if (!strcmp(type, "resize_tr"))
401           cursor = ecore_x_cursor_shape_get(ECORE_X_CURSOR_TOP_RIGHT_CORNER);
402         else if (!strcmp(type, "resize_r"))
403           cursor = ecore_x_cursor_shape_get(ECORE_X_CURSOR_RIGHT_SIDE);
404         else if (!strcmp(type, "resize_br"))
405           cursor = ecore_x_cursor_shape_get(ECORE_X_CURSOR_BOTTOM_RIGHT_CORNER);
406         else if (!strcmp(type, "resize_b"))
407           cursor = ecore_x_cursor_shape_get(ECORE_X_CURSOR_BOTTOM_SIDE);
408         else if (!strcmp(type, "resize_bl"))
409           cursor = ecore_x_cursor_shape_get(ECORE_X_CURSOR_BOTTOM_LEFT_CORNER);
410         else if (!strcmp(type, "resize_l"))
411           cursor = ecore_x_cursor_shape_get(ECORE_X_CURSOR_LEFT_SIDE);
412         else if (!strcmp(type, "entry"))
413           cursor = ecore_x_cursor_shape_get(ECORE_X_CURSOR_XTERM);
414         else if (!strcmp(type, "default"))
415           cursor = ecore_x_cursor_shape_get(ECORE_X_CURSOR_LEFT_PTR);
416         else if (!strcmp(type, "plus"))
417           cursor = ecore_x_cursor_shape_get(ECORE_X_CURSOR_PLUS);
418         else
419           {
420              printf("Unknown pointer type: %s\n", type);
421              cursor = ecore_x_cursor_shape_get(ECORE_X_CURSOR_ARROW);
422           }
423         if (!cursor) printf("X Cursor for %s is missing\n", type);
424         ecore_x_window_cursor_set(p->win, cursor);
425         if (cursor) ecore_x_cursor_free(cursor);
426      }
427 }
428
429 static void
430 _e_pointer_active_handle(E_Pointer *p)
431 {
432    /* we got some mouse event - if there was an idle timer emit an active
433     * signal as we WERE idle, NOW we are active */
434    if (p->idle_timer)
435      {
436         ecore_timer_del(p->idle_timer);
437         p->idle_timer = NULL;
438      }
439    if (p->idle_poller)
440      {
441         ecore_poller_del(p->idle_poller);
442         p->idle_poller = NULL;
443      }
444    if (p->idle)
445      {
446         if (p->pointer_object)
447           edje_object_signal_emit(p->pointer_object, "e,state,mouse,active", "e");
448         p->idle = 0;
449      }
450    if (e_powersave_mode_get() >= E_POWERSAVE_MODE_MEDIUM) return;
451    /* and schedule a pre-idle check in 1 second if no more events happen */
452    if (!e_config->idle_cursor) return;
453    p->idle_timer = ecore_timer_loop_add(1.0, _e_pointer_cb_idle_timer_pre, p);
454 }
455
456 static Eina_Bool
457 _e_pointer_cb_mouse_down(void *data __UNUSED__, int type __UNUSED__, void *event __UNUSED__)
458 {
459    Eina_List *l;
460    E_Pointer *p;
461
462    EINA_LIST_FOREACH(_e_pointers, l, p)
463      {
464         _e_pointer_active_handle(p);
465         if (e_powersave_mode_get() < E_POWERSAVE_MODE_EXTREME)
466           {
467              if (p->pointer_object)
468                edje_object_signal_emit(p->pointer_object, 
469                                        "e,action,mouse,down", "e");
470           }
471      }
472    return ECORE_CALLBACK_PASS_ON;
473 }
474
475 static Eina_Bool
476 _e_pointer_cb_mouse_up(void *data __UNUSED__, int type __UNUSED__, void *event __UNUSED__)
477 {
478    Eina_List *l;
479    E_Pointer *p;
480
481    EINA_LIST_FOREACH(_e_pointers, l, p)
482      {
483         _e_pointer_active_handle(p);
484         if (e_powersave_mode_get() < E_POWERSAVE_MODE_EXTREME)
485           {
486              if (p->pointer_object)
487                edje_object_signal_emit(p->pointer_object, 
488                                        "e,action,mouse,up", "e");
489           }
490      }
491    return ECORE_CALLBACK_PASS_ON;
492 }
493
494 static Eina_Bool
495 _e_pointer_cb_mouse_move(void *data __UNUSED__, int type __UNUSED__, void *event __UNUSED__)
496 {
497    Eina_List *l;
498    E_Pointer *p;
499
500    EINA_LIST_FOREACH(_e_pointers, l, p)
501      {
502         _e_pointer_active_handle(p);
503         if (e_powersave_mode_get() < E_POWERSAVE_MODE_HIGH)
504           {
505              if (p->pointer_object)
506                edje_object_signal_emit(p->pointer_object, 
507                                        "e,action,mouse,move", "e");
508           }
509      }
510    return ECORE_CALLBACK_PASS_ON;
511 }
512
513 static Eina_Bool
514 _e_pointer_cb_mouse_wheel(void *data __UNUSED__, int type __UNUSED__, void *event __UNUSED__)
515 {
516    Eina_List *l;
517    E_Pointer *p;
518
519    EINA_LIST_FOREACH(_e_pointers, l, p)
520      {
521         _e_pointer_active_handle(p);
522         if (e_powersave_mode_get() < E_POWERSAVE_MODE_EXTREME)
523           {
524              if (p->pointer_object)
525                edje_object_signal_emit(p->pointer_object, 
526                                        "e,action,mouse,wheel", "e");
527           }
528      }
529    return ECORE_CALLBACK_PASS_ON;
530 }
531
532 static Eina_Bool
533 _e_pointer_cb_idle_timer_pre(void *data)
534 {
535    E_Pointer *p;
536    int x, y;
537
538    p = data;
539    ecore_x_pointer_xy_get(p->win, &x, &y);
540    p->x = x;
541    p->y = y;
542    p->idle_timer = ecore_timer_loop_add(4.0, _e_pointer_cb_idle_timer_wait, p);
543    return ECORE_CALLBACK_CANCEL;
544 }
545
546 static Eina_Bool
547 _e_pointer_cb_idle_timer_wait(void *data)
548 {
549    E_Pointer *p;
550
551    p = data;
552    if ((e_powersave_mode_get() >= E_POWERSAVE_MODE_MEDIUM) ||
553        (!e_config->idle_cursor))
554      {
555         if (p->idle_poller) ecore_poller_del(p->idle_poller);
556         p->idle_poller = NULL;
557         p->idle_timer = NULL;
558         return ECORE_CALLBACK_CANCEL;
559      }
560    if (!p->idle_poller)
561      p->idle_poller = ecore_poller_add(ECORE_POLLER_CORE, 64,
562                                        _e_pointer_cb_idle_poller, p);
563    p->idle_timer = NULL;
564    return ECORE_CALLBACK_CANCEL;
565 }
566
567 static Eina_Bool
568 _e_pointer_cb_idle_poller(void *data)
569 {
570    E_Pointer *p;
571    int x, y;
572
573    p = data;
574    if ((e_powersave_mode_get() >= E_POWERSAVE_MODE_MEDIUM) ||
575        (!e_config->idle_cursor))
576      {
577         p->idle_poller = NULL;
578         return ECORE_CALLBACK_CANCEL;
579      }
580    /* check if pointer actually moved since the 1 second post-mouse move idle
581     * pre-timer that fetches the position */
582    ecore_x_pointer_xy_get(p->win, &x, &y);
583    if ((x != p->x) || (y != p->y))
584      {
585         /* it moved - so we are not idle yet - record position and wait 
586          * 4 secons more */
587         p->x = x;
588         p->y = y;
589         if (p->idle)
590           {
591              if (p->pointer_object)
592                edje_object_signal_emit(p->pointer_object, 
593                                        "e,state,mouse,active", "e");
594              p->idle = 0;
595           }
596         /* use poller to check from now on */
597         return ECORE_CALLBACK_RENEW;
598      }
599    /* we are idle - report it if not idle before */
600    if (!p->idle)
601      {
602         if (p->pointer_object)
603           edje_object_signal_emit(p->pointer_object, "e,state,mouse,idle", "e");
604         p->idle = 1;
605      }
606    return ECORE_CALLBACK_RENEW;
607 }