e_pointer: do scale with same output resolution ratio of width and height
[platform/upstream/enlightenment.git] / src / bin / e_pointer.c
1 #include "e.h"
2
3 /* local variables */
4 static Eina_List *_ptrs = NULL;
5 static Eina_Bool _initted = EINA_FALSE;
6
7 static int _e_pointer_hooks_delete = 0;
8 static int _e_pointer_hooks_walking = 0;
9 static Eina_Inlist *_e_pointer_hooks[] =
10 {
11    [E_POINTER_HOOK_TOUCH_MOVE] = NULL,
12    [E_POINTER_HOOK_MOUSE_MOVE] = NULL,
13    [E_POINTER_HOOK_SHOW] = NULL,
14    [E_POINTER_HOOK_HIDE] = NULL,
15 };
16
17 static void
18 _e_pointer_configured_output_resolution_ratio_get(E_Client *ec, double *ratio)
19 {
20    E_Output *output;
21    E_Zone *zone;
22    double cal_ratio_w, cal_ratio_h, cal_ratio;
23
24    if (!ratio) return;
25
26    *ratio = 1.0;
27
28    if (!e_config->cursor_configured_output_resolution.use) return;
29    if (e_config->cursor_configured_output_resolution.w == 0) return;
30    if (e_config->cursor_configured_output_resolution.h == 0) return;
31
32    if (!ec) return;
33
34    zone = e_comp_zone_find_by_ec(ec);
35    if (!zone) return;
36
37    output = e_output_find(zone->output_id);
38    if (!output) return;
39    if (output->config.geom.w == 0) return;
40    if (output->config.geom.h == 0) return;
41
42    cal_ratio_w = (double)output->config.geom.w /
43                  (double)e_config->cursor_configured_output_resolution.w;
44
45    cal_ratio_h = (double)output->config.geom.h /
46                  (double)e_config->cursor_configured_output_resolution.h;
47
48    cal_ratio = MIN(cal_ratio_w, cal_ratio_h);
49
50    *ratio = cal_ratio;
51 }
52
53 /* move the cursor image with the calcaultion of the hot spot */
54 static void
55 _e_pointer_position_update(E_Pointer *ptr)
56 {
57    int nx, ny;
58    int rotation;
59    int cursor_w, cursor_h;
60    E_Client *ec;
61    double ratio = 1.0;
62    int hot_x = 0, hot_y = 0;
63
64    if (!ptr->o_ptr) return;
65
66    ec = e_comp_object_client_get(ptr->o_ptr);
67    EINA_SAFETY_ON_NULL_RETURN(ec);
68
69    rotation = ptr->rotation;
70
71    evas_object_geometry_get(ec->frame, NULL, NULL, &cursor_w, &cursor_h);
72    _e_pointer_configured_output_resolution_ratio_get(ec, &ratio);
73
74    if (ratio != 1.0)
75      {
76         hot_x = (int)((double)ptr->hot.x * ratio);
77         hot_y = (int)((double)ptr->hot.y * ratio);
78      }
79    else
80      {
81         hot_x = ptr->hot.x;
82         hot_y = ptr->hot.y;
83      }
84
85    switch (rotation)
86      {
87       case 0:
88         nx = ptr->x - hot_x;
89         ny = ptr->y - hot_y;
90         break;
91       case 90:
92         nx = ptr->x - hot_y;
93         ny = ptr->y + hot_x;
94         break;
95       case 180:
96         nx = ptr->x + hot_x;
97         ny = ptr->y + hot_y;
98         break;
99       case 270:
100         nx = ptr->x + hot_y;
101         ny = ptr->y - hot_x;
102         break;
103       default:
104         nx = ptr->x - hot_x;
105         ny = ptr->y - hot_y;
106         break;
107      }
108
109    evas_object_move(ptr->o_ptr, nx, ny);
110 }
111
112 static void
113 _e_pointer_map_apply(E_Pointer *ptr)
114 {
115    E_Map *map;
116    int x, y, w, h;
117    E_Client *ec;
118    int rotation = 0;
119    double ratio = 1.0;
120
121    EINA_SAFETY_ON_NULL_RETURN(ptr);
122    if (!ptr->o_ptr) return;
123
124    ec = e_comp_object_client_get(ptr->o_ptr);
125    EINA_SAFETY_ON_NULL_RETURN(ec);
126
127    _e_pointer_configured_output_resolution_ratio_get(ec, &ratio);
128    rotation = ptr->rotation;
129
130    if ((ratio == 1.0) &&
131        ((rotation == 0) || (rotation % 90 != 0) || (rotation / 90 > 3)))
132      {
133         e_client_map_enable_set(ec, EINA_FALSE);
134         e_client_map_set(ec, NULL);
135         return;
136      }
137
138    evas_object_geometry_get(ec->frame, &x, &y, &w, &h);
139
140    map = e_map_new();
141    EINA_SAFETY_ON_NULL_RETURN(map);
142
143    e_map_util_points_populate_from_object_full(map, ec->frame, 0);
144
145    if (ratio != 1.0)
146      {
147         e_map_point_coord_set(map, 1, x + (int)((double)w * ratio), y, 0);
148         e_map_point_coord_set(map, 2, x + (int)((double)w * ratio),
149                              y + (int)((double)h * ratio), 0);
150         e_map_point_coord_set(map, 3, x, y + (int)((double)h * ratio), 0);
151      }
152
153    if (rotation)
154      {
155         if (rotation == 90)
156           rotation = 270;
157         else if (rotation == 270)
158           rotation = 90;
159
160         e_map_util_rotate(map, rotation, x, y);
161      }
162
163    e_map_util_points_color_set(map, 255, 255, 255, 255);
164    e_map_util_object_move_sync_set(map, EINA_TRUE);
165
166    e_client_map_set(ec, map);
167    e_client_map_enable_set(ec, EINA_TRUE);
168
169    e_map_free(map);
170 }
171
172 static void
173 _e_pointer_cb_object_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
174 {
175    E_Pointer *ptr = (E_Pointer *)data;
176
177    ptr->o_ptr = NULL;
178    ptr->device = E_POINTER_NONE;
179    ptr->hot.x = 0;
180    ptr->hot.y = 0;
181 }
182
183 static void
184 _e_pointer_object_set(E_Pointer *ptr, Evas_Object *obj)
185 {
186    if (ptr->o_ptr == obj) return;
187
188    if (ptr->o_ptr)
189      {
190          evas_object_event_callback_del_full(ptr->o_ptr, EVAS_CALLBACK_DEL, _e_pointer_cb_object_del, ptr);
191          ptr->o_ptr = NULL;
192      }
193
194    if (obj)
195      {
196         evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, _e_pointer_cb_object_del, ptr);
197         ptr->o_ptr = obj;
198      }
199 }
200
201 static void
202 _e_pointer_cb_free(E_Pointer *ptr)
203 {
204    _e_pointer_object_set(ptr, NULL);
205
206    _ptrs = eina_list_remove(_ptrs, ptr);
207
208    free(ptr);
209 }
210
211 static void
212 _e_pointer_hooks_clean(void)
213 {
214    Eina_Inlist *l;
215    E_Pointer_Hook *ph;
216    unsigned int x;
217
218    for (x = 0; x < E_POINTER_HOOK_LAST; x++)
219      EINA_INLIST_FOREACH_SAFE(_e_pointer_hooks[x], l, ph)
220        {
221           if (!ph->delete_me) continue;
222           _e_pointer_hooks[x] = eina_inlist_remove(_e_pointer_hooks[x], EINA_INLIST_GET(ph));
223           free(ph);
224        }
225
226    _e_pointer_hooks_delete = 0;
227 }
228
229 static Eina_Bool
230 _e_pointer_hook_call(E_Pointer_Hook_Point hookpoint, E_Pointer *ptr)
231 {
232    E_Pointer_Hook *ph;
233
234    e_object_ref(E_OBJECT(ptr));
235    _e_pointer_hooks_walking++;
236    EINA_INLIST_FOREACH(_e_pointer_hooks[hookpoint], ph)
237      {
238         if (ph->delete_me) continue;
239         ph->func(ph->data, ptr);
240         if (e_object_is_del(E_OBJECT(ptr)))
241           break;
242      }
243    _e_pointer_hooks_walking--;
244    if ((_e_pointer_hooks_walking == 0) && (_e_pointer_hooks_delete > 0))
245      _e_pointer_hooks_clean();
246    return !!e_object_unref(E_OBJECT(ptr));
247 }
248
249 EINTERN int
250 e_pointer_init(void)
251 {
252    _initted = EINA_TRUE;
253    return 1;
254 }
255
256 EINTERN int
257 e_pointer_shutdown(void)
258 {
259    _initted = EINA_FALSE;
260    return 1;
261 }
262
263 EINTERN E_Pointer *
264 e_pointer_canvas_new(Ecore_Evas *ee, Eina_Bool filled)
265 {
266    E_Pointer *ptr = NULL;
267    E_Output *output = NULL;
268
269    EINA_SAFETY_ON_FALSE_RETURN_VAL(ee, NULL);
270    if (!_initted) return NULL;
271
272    /* allocate space for new pointer */
273    if (!(ptr = E_OBJECT_ALLOC(E_Pointer, E_POINTER_TYPE, _e_pointer_cb_free)))
274      return NULL;
275
276    /* set default pointer properties */
277    ptr->canvas = EINA_TRUE;
278    ptr->w = ptr->h = e_config->cursor_size;
279    ptr->e_cursor = e_config->use_e_cursor;
280    output = e_output_find_by_index(0);
281    if (output)
282      {
283         ptr->x = output->config.mode.w / 2;
284         ptr->y = output->config.mode.h / 2;
285      }
286
287    ptr->ee = ee;
288    ptr->evas = ecore_evas_get(ee);
289
290    /* append this pointer to the list */
291    _ptrs = eina_list_append(_ptrs, ptr);
292
293    return ptr;
294 }
295
296 static Eina_Bool
297 _e_pointer_object_hide(E_Pointer *ptr, Evas_Object *obj)
298 {
299    E_Client *ec;
300    Eina_Bool res = EINA_FALSE;
301
302    ec = e_comp_object_client_get(obj);
303    if ((ec) && (!e_object_is_del(E_OBJECT(ec))))
304      {
305         ec->hidden = 1;
306         ec->visible = EINA_FALSE;
307         ec->visibility.obscured = E_VISIBILITY_FULLY_OBSCURED;
308         ec->comp_data->mapped = EINA_FALSE;
309         ec->override = 1; /* ignore the previous cursor_ec */
310      }
311
312    /* hide cursor object */
313    if (evas_object_visible_get(obj))
314      {
315         res = EINA_TRUE;
316         evas_object_hide(obj);
317      }
318
319    _e_pointer_object_set(ptr, NULL);
320    ptr->device = E_POINTER_NONE;
321
322    return res;
323 }
324
325 static Eina_Bool
326 _e_pointer_object_show(E_Pointer *ptr, Evas_Object *obj)
327 {
328    E_Client *ec;
329    Eina_Bool res = EINA_FALSE;
330
331    ec = e_comp_object_client_get(obj);
332    if (ec && e_pixmap_usable_get(ec->pixmap))
333      {
334         ec->hidden = 0;
335         ec->visible = EINA_TRUE;
336         ec->visibility.obscured = E_VISIBILITY_UNOBSCURED;
337         evas_object_geometry_set(ec->frame, ec->x, ec->y, ec->w, ec->h);
338         ec->comp_data->mapped = EINA_TRUE;
339         ec->override = 0; /* do not ignore the cursor_ec to set the image object */
340      }
341
342    /* show cursor object */
343    if (!evas_object_visible_get(obj))
344      {
345         res = EINA_TRUE;
346         evas_object_show(obj);
347      }
348
349    _e_pointer_object_set(ptr, obj);
350
351    return res;
352 }
353
354 EINTERN void
355 e_pointer_object_set(E_Pointer *ptr, Evas_Object *obj, int x, int y)
356 {
357    Eina_Bool need_call_hide = EINA_FALSE;
358
359    EINA_SAFETY_ON_NULL_RETURN(ptr);
360
361    /* don't show cursor if in hidden mode */
362    if ((!e_config->show_cursor) || (!e_comp_wl->ptr.enabled))
363      {
364         if (ptr->o_ptr && _e_pointer_object_hide(ptr, ptr->o_ptr))
365           _e_pointer_hook_call(E_POINTER_HOOK_HIDE, ptr);
366
367         return;
368      }
369
370    /* hide and unset the existed ptr->o_ptr */
371    if (ptr->o_ptr && (ptr->o_ptr != obj))
372      {
373         if (_e_pointer_object_hide(ptr, ptr->o_ptr))
374           need_call_hide = EINA_TRUE;
375      }
376
377    /* update the hot spot of the cursor */
378    ptr->hot.x = x;
379    ptr->hot.y = y;
380
381    /* if obj is not null, set the obj to ptr->o_ptr */
382    if (obj && (ptr->o_ptr != obj))
383      {
384         if (_e_pointer_object_show(ptr, obj))
385           {
386              need_call_hide = EINA_FALSE;
387              _e_pointer_hook_call(E_POINTER_HOOK_SHOW, ptr);
388           }
389
390         /* apply the cursor obj map */
391         _e_pointer_map_apply(ptr);
392
393         /* move the pointer to the current position */
394         _e_pointer_position_update(ptr);
395      }
396
397    if (need_call_hide)
398      _e_pointer_hook_call(E_POINTER_HOOK_HIDE, ptr);
399 }
400
401 EINTERN void
402 e_pointer_touch_move(E_Pointer *ptr, int x, int y)
403 {
404    EINA_SAFETY_ON_NULL_RETURN(ptr);
405
406    if (!e_config->show_cursor) return;
407
408    /* save the current position */
409    ptr->x = x;
410    ptr->y = y;
411
412    if (ptr->device != E_POINTER_TOUCH) ptr->device = E_POINTER_TOUCH;
413
414    _e_pointer_position_update(ptr);
415
416    _e_pointer_hook_call(E_POINTER_HOOK_TOUCH_MOVE, ptr);
417 }
418
419 E_API void
420 e_pointer_mouse_move(E_Pointer *ptr, int x, int y)
421 {
422    EINA_SAFETY_ON_NULL_RETURN(ptr);
423
424    if (!e_config->show_cursor) return;
425
426    /* save the current position */
427    ptr->x = x;
428    ptr->y = y;
429
430    if (ptr->device != E_POINTER_MOUSE) ptr->device = E_POINTER_MOUSE;
431
432    _e_pointer_position_update(ptr);
433
434    _e_pointer_hook_call(E_POINTER_HOOK_MOUSE_MOVE, ptr);
435 }
436
437 EINTERN void
438 e_pointer_hide(E_Pointer *ptr)
439 {
440    EINA_SAFETY_ON_NULL_RETURN(ptr);
441    if (!ptr->o_ptr) return;
442    if (!evas_object_visible_get(ptr->o_ptr)) return;
443
444    evas_object_hide(ptr->o_ptr);
445 }
446
447 E_API Eina_Bool
448 e_pointer_is_hidden(E_Pointer *ptr)
449 {
450    EINA_SAFETY_ON_NULL_RETURN_VAL(ptr, EINA_TRUE);
451
452    if (!e_config->show_cursor) return EINA_TRUE;
453    if (ptr->o_ptr && evas_object_visible_get(ptr->o_ptr)) return EINA_FALSE;
454
455    return EINA_TRUE;
456 }
457
458 E_API void
459 e_pointer_rotation_set(E_Pointer *ptr, int rotation)
460 {
461    const Eina_List *l;
462    E_Input_Device *dev;
463
464    EINA_SAFETY_ON_NULL_RETURN(ptr);
465    if (ptr->rotation == rotation) return;
466
467    ptr->rotation = rotation;
468
469    _e_pointer_map_apply(ptr);
470    _e_pointer_position_update(ptr);
471
472    EINA_LIST_FOREACH(e_input_devices_get(), l, dev)
473      e_input_device_pointer_rotation_set(dev, rotation);
474 }
475
476 EINTERN void
477 e_pointer_position_get(E_Pointer *ptr, int *x, int *y)
478 {
479    EINA_SAFETY_ON_NULL_RETURN(ptr);
480
481    if (!e_config->show_cursor) return;
482    if (!ptr->o_ptr) return;
483    if (!evas_object_visible_get(ptr->o_ptr)) return;
484
485    *x = ptr->x;
486    *y = ptr->y;
487 }
488
489 EINTERN E_Pointer *
490 e_pointer_get(E_Client *ec)
491 {
492    const Eina_List *l;
493    E_Pointer *ptr;
494    E_Client *ptr_ec = NULL;
495
496    if ((!ec) || (e_object_is_del(E_OBJECT(ec)))) return NULL;
497
498    EINA_LIST_FOREACH(_ptrs, l, ptr)
499      {
500         if (ptr->o_ptr)
501           {
502              ptr_ec = e_comp_object_client_get(ptr->o_ptr);
503              if (ptr_ec == ec)
504                 return ptr;
505           }
506      }
507
508   return NULL;
509 }
510
511 E_API E_Pointer_Hook *
512 e_pointer_hook_add(E_Pointer_Hook_Point hookpoint, E_Pointer_Hook_Cb func, const void *data)
513 {
514    E_Pointer_Hook *ph;
515
516    EINA_SAFETY_ON_TRUE_RETURN_VAL(hookpoint >= E_POINTER_HOOK_LAST, NULL);
517    ph = E_NEW(E_Pointer_Hook, 1);
518    if (!ph) return NULL;
519    ph->hookpoint = hookpoint;
520    ph->func = func;
521    ph->data = (void*)data;
522    _e_pointer_hooks[hookpoint] = eina_inlist_append(_e_pointer_hooks[hookpoint], EINA_INLIST_GET(ph));
523    return ph;
524 }
525
526 E_API void
527 e_pointer_hook_del(E_Pointer_Hook *ph)
528 {
529    ph->delete_me = 1;
530    if (_e_pointer_hooks_walking == 0)
531      {
532         _e_pointer_hooks[ph->hookpoint] = eina_inlist_remove(_e_pointer_hooks[ph->hookpoint], EINA_INLIST_GET(ph));
533         free(ph);
534      }
535    else
536      _e_pointer_hooks_delete++;
537 }