1 #ifdef HAVE_CONFIG_H
2 # include "elementary_config.h"
3 #endif
4 
5 #include <Elementary.h>
6 #include <Elementary_Cursor.h>
7 
8 #include "elm_priv.h"
9 
10 #ifdef HAVE_ELEMENTARY_X
11 #include <Ecore_X.h>
12 #include <Ecore_X_Cursor.h>
13 #endif
14 
15 #ifdef HAVE_ELEMENTARY_WIN32
16 #include <Ecore_Win32.h>
17 #endif
18 
19 #define _cursor_key "_elm_cursor"
20 
21 struct _Cursor_Id
22 {
23    const char *name;
24 
25 #if defined(HAVE_ELEMENTARY_X) || defined(HAVE_ELEMENTARY_COCOA) || defined(HAVE_ELEMENTARY_WIN32)
26    int id; /* For X */
27    int cid; /* For Cocoa */
28 #endif
29 };
30 
31 #if defined(HAVE_ELEMENTARY_X)
32 # if defined(HAVE_ELEMENTARY_COCOA)
33 #  define CURSOR(_name, _id, _cid) {_name, ECORE_X_CURSOR_##_id, _cid}
34 # else
35 #  define CURSOR(_name, _id, _cid) { _name, ECORE_X_CURSOR_##_id, -1 }
36 # endif
37 #elif defined(HAVE_ELEMENTARY_COCOA)
38 #  define CURSOR(_name, _id, _cid) {_name, -1, _cid}
39 #elif defined(HAVE_ELEMENTARY_WIN32)
40 #  define CURSOR(_name, _id, _cid) {_name, ECORE_WIN32_CURSOR_X11_SHAPE_##_id, -1 }
41 #else
42 #  define CURSOR(_name, _id, _cid) { _name }
43 #endif
44 
45 #if defined(HAVE_ELEMENTARY_X) || defined(HAVE_ELEMENTARY_COCOA) || defined(HAVE_ELEMENTARY_WIN32)
46 /* Please keep order in sync with Ecore_X_Cursor.h values! */
47 static struct _Cursor_Id _cursors[] =
48 {
49    CURSOR(ELM_CURSOR_X                  , X                  , ECORE_COCOA_CURSOR_CROSSHAIR),
50    CURSOR(ELM_CURSOR_ARROW              , ARROW              , ECORE_COCOA_CURSOR_ARROW),
51    CURSOR(ELM_CURSOR_BASED_ARROW_DOWN   , BASED_ARROW_DOWN   , ECORE_COCOA_CURSOR_DEFAULT),
52    CURSOR(ELM_CURSOR_BASED_ARROW_UP     , UP                 , ECORE_COCOA_CURSOR_DEFAULT),
53    CURSOR(ELM_CURSOR_BOAT               , BOAT               , ECORE_COCOA_CURSOR_DEFAULT),
54    CURSOR(ELM_CURSOR_BOGOSITY           , BOGOSITY           , ECORE_COCOA_CURSOR_DEFAULT),
55    CURSOR(ELM_CURSOR_BOTTOM_LEFT_CORNER , BOTTOM_LEFT_CORNER , ECORE_COCOA_CURSOR_DEFAULT),
56    CURSOR(ELM_CURSOR_BOTTOM_RIGHT_CORNER, BOTTOM_RIGHT_CORNER, ECORE_COCOA_CURSOR_DEFAULT),
57    CURSOR(ELM_CURSOR_BOTTOM_SIDE        , BOTTOM_SIDE        , ECORE_COCOA_CURSOR_RESIZE_DOWN),
58    CURSOR(ELM_CURSOR_BOTTOM_TEE         , BOTTOM_TEE         , ECORE_COCOA_CURSOR_RESIZE_DOWN),
59    CURSOR(ELM_CURSOR_BOX_SPIRAL         , BOX_SPIRAL         , ECORE_COCOA_CURSOR_DEFAULT),
60    CURSOR(ELM_CURSOR_CENTER_PTR         , CENTER_PTR         , ECORE_COCOA_CURSOR_DEFAULT),
61    CURSOR(ELM_CURSOR_CIRCLE             , CIRCLE             , ECORE_COCOA_CURSOR_DEFAULT),
62    CURSOR(ELM_CURSOR_CLOCK              , CLOCK              , ECORE_COCOA_CURSOR_DEFAULT),
63    CURSOR(ELM_CURSOR_COFFEE_MUG         , COFFEE_MUG         , ECORE_COCOA_CURSOR_DEFAULT),
64    CURSOR(ELM_CURSOR_CROSS              , CROSS              , ECORE_COCOA_CURSOR_CROSSHAIR),
65    CURSOR(ELM_CURSOR_CROSS_REVERSE      , CROSS_REVERSE      , ECORE_COCOA_CURSOR_CROSSHAIR),
66    CURSOR(ELM_CURSOR_CROSSHAIR          , CROSSHAIR          , ECORE_COCOA_CURSOR_CROSSHAIR),
67    CURSOR(ELM_CURSOR_DIAMOND_CROSS      , DIAMOND_CROSS      , ECORE_COCOA_CURSOR_DEFAULT),
68    CURSOR(ELM_CURSOR_DOT                , DOT                , ECORE_COCOA_CURSOR_DEFAULT),
69    CURSOR(ELM_CURSOR_DOT_BOX_MASK       , DOT_BOX_MASK       , ECORE_COCOA_CURSOR_DEFAULT),
70    CURSOR(ELM_CURSOR_DOUBLE_ARROW       , DOUBLE_ARROW       , ECORE_COCOA_CURSOR_RESIZE_UP_DOWN),
71    CURSOR(ELM_CURSOR_DRAFT_LARGE        , DRAFT_LARGE        , ECORE_COCOA_CURSOR_DEFAULT),
72    CURSOR(ELM_CURSOR_DRAFT_SMALL        , DRAFT_SMALL        , ECORE_COCOA_CURSOR_DEFAULT),
73    CURSOR(ELM_CURSOR_DRAPED_BOX         , DRAPED_BOX         , ECORE_COCOA_CURSOR_DEFAULT),
74    CURSOR(ELM_CURSOR_EXCHANGE           , EXCHANGE           , ECORE_COCOA_CURSOR_DEFAULT),
75    CURSOR(ELM_CURSOR_FLEUR              , FLEUR              , ECORE_COCOA_CURSOR_CLOSED_HAND),
76    CURSOR(ELM_CURSOR_GOBBLER            , GOBBLER            , ECORE_COCOA_CURSOR_DEFAULT),
77    CURSOR(ELM_CURSOR_GUMBY              , GUMBY              , ECORE_COCOA_CURSOR_DEFAULT),
78    CURSOR(ELM_CURSOR_HAND1              , HAND1              , ECORE_COCOA_CURSOR_POINTING_HAND),
79    CURSOR(ELM_CURSOR_HAND2              , HAND2              , ECORE_COCOA_CURSOR_POINTING_HAND),
80    CURSOR(ELM_CURSOR_HEART              , HEART              , ECORE_COCOA_CURSOR_DEFAULT),
81    CURSOR(ELM_CURSOR_ICON               , ICON               , ECORE_COCOA_CURSOR_DEFAULT),
82    CURSOR(ELM_CURSOR_IRON_CROSS         , IRON_CROSS         , ECORE_COCOA_CURSOR_DEFAULT),
83    CURSOR(ELM_CURSOR_LEFT_PTR           , LEFT_PTR           , ECORE_COCOA_CURSOR_ARROW),
84    CURSOR(ELM_CURSOR_LEFT_SIDE          , LEFT_SIDE          , ECORE_COCOA_CURSOR_RESIZE_LEFT),
85    CURSOR(ELM_CURSOR_LEFT_TEE           , LEFT_TEE           , ECORE_COCOA_CURSOR_RESIZE_LEFT),
86    CURSOR(ELM_CURSOR_LEFTBUTTON         , LEFTBUTTON         , ECORE_COCOA_CURSOR_DEFAULT),
87    CURSOR(ELM_CURSOR_LL_ANGLE           , LL_ANGLE           , ECORE_COCOA_CURSOR_DEFAULT),
88    CURSOR(ELM_CURSOR_LR_ANGLE           , LR_ANGLE           , ECORE_COCOA_CURSOR_DEFAULT),
89    CURSOR(ELM_CURSOR_MAN                , MAN                , ECORE_COCOA_CURSOR_DEFAULT),
90    CURSOR(ELM_CURSOR_MIDDLEBUTTON       , MIDDLEBUTTON       , ECORE_COCOA_CURSOR_DEFAULT),
91    CURSOR(ELM_CURSOR_MOUSE              , MOUSE              , ECORE_COCOA_CURSOR_DEFAULT),
92    CURSOR(ELM_CURSOR_PENCIL             , PENCIL             , ECORE_COCOA_CURSOR_DEFAULT),
93    CURSOR(ELM_CURSOR_PIRATE             , PIRATE             , ECORE_COCOA_CURSOR_DEFAULT),
94    CURSOR(ELM_CURSOR_PLUS               , PLUS               , ECORE_COCOA_CURSOR_CROSSHAIR),
95    CURSOR(ELM_CURSOR_QUESTION_ARROW     , QUESTION_ARROW     , ECORE_COCOA_CURSOR_DEFAULT),
96    CURSOR(ELM_CURSOR_RIGHT_PTR          , RIGHT_PTR          , ECORE_COCOA_CURSOR_DEFAULT),
97    CURSOR(ELM_CURSOR_RIGHT_SIDE         , RIGHT_SIDE         , ECORE_COCOA_CURSOR_RESIZE_RIGHT),
98    CURSOR(ELM_CURSOR_RIGHT_TEE          , RIGHT_TEE          , ECORE_COCOA_CURSOR_RESIZE_RIGHT),
99    CURSOR(ELM_CURSOR_RIGHTBUTTON        , RIGHTBUTTON        , ECORE_COCOA_CURSOR_DEFAULT),
100    CURSOR(ELM_CURSOR_RTL_LOGO           , RTL_LOGO           , ECORE_COCOA_CURSOR_DEFAULT),
101    CURSOR(ELM_CURSOR_SAILBOAT           , SAILBOAT           , ECORE_COCOA_CURSOR_DEFAULT),
102    CURSOR(ELM_CURSOR_SB_DOWN_ARROW      , SB_DOWN_ARROW      , ECORE_COCOA_CURSOR_RESIZE_DOWN),
103    CURSOR(ELM_CURSOR_SB_H_DOUBLE_ARROW  , SB_H_DOUBLE_ARROW  , ECORE_COCOA_CURSOR_RESIZE_LEFT_RIGHT),
104    CURSOR(ELM_CURSOR_SB_LEFT_ARROW      , SB_LEFT_ARROW      , ECORE_COCOA_CURSOR_RESIZE_LEFT),
105    CURSOR(ELM_CURSOR_SB_RIGHT_ARROW     , SB_RIGHT_ARROW     , ECORE_COCOA_CURSOR_RESIZE_RIGHT),
106    CURSOR(ELM_CURSOR_SB_UP_ARROW        , SB_UP_ARROW        , ECORE_COCOA_CURSOR_RESIZE_UP),
107    CURSOR(ELM_CURSOR_SB_V_DOUBLE_ARROW  , SB_V_DOUBLE_ARROW  , ECORE_COCOA_CURSOR_RESIZE_UP_DOWN),
108    CURSOR(ELM_CURSOR_SHUTTLE            , SHUTTLE            , ECORE_COCOA_CURSOR_DEFAULT),
109    CURSOR(ELM_CURSOR_SIZING             , SIZING             , ECORE_COCOA_CURSOR_DEFAULT),
110    CURSOR(ELM_CURSOR_SPIDER             , SPIDER             , ECORE_COCOA_CURSOR_DEFAULT),
111    CURSOR(ELM_CURSOR_SPRAYCAN           , SPRAYCAN           , ECORE_COCOA_CURSOR_DEFAULT),
112    CURSOR(ELM_CURSOR_STAR               , STAR               , ECORE_COCOA_CURSOR_DEFAULT),
113    CURSOR(ELM_CURSOR_TARGET             , TARGET             , ECORE_COCOA_CURSOR_DEFAULT),
114    CURSOR(ELM_CURSOR_TCROSS             , TCROSS             , ECORE_COCOA_CURSOR_CROSSHAIR),
115    CURSOR(ELM_CURSOR_TOP_LEFT_ARROW     , TOP_LEFT_ARROW     , ECORE_COCOA_CURSOR_DEFAULT),
116    CURSOR(ELM_CURSOR_TOP_LEFT_CORNER    , TOP_LEFT_CORNER    , ECORE_COCOA_CURSOR_DEFAULT),
117    CURSOR(ELM_CURSOR_TOP_RIGHT_CORNER   , TOP_RIGHT_CORNER   , ECORE_COCOA_CURSOR_DEFAULT),
118    CURSOR(ELM_CURSOR_TOP_SIDE           , TOP_SIDE           , ECORE_COCOA_CURSOR_RESIZE_UP),
119    CURSOR(ELM_CURSOR_TOP_TEE            , TOP_TEE            , ECORE_COCOA_CURSOR_RESIZE_UP),
120    CURSOR(ELM_CURSOR_TREK               , TREK               , ECORE_COCOA_CURSOR_DEFAULT),
121    CURSOR(ELM_CURSOR_UL_ANGLE           , UL_ANGLE           , ECORE_COCOA_CURSOR_DEFAULT),
122    CURSOR(ELM_CURSOR_UMBRELLA           , UMBRELLA           , ECORE_COCOA_CURSOR_DEFAULT),
123    CURSOR(ELM_CURSOR_UR_ANGLE           , UR_ANGLE           , ECORE_COCOA_CURSOR_DEFAULT),
124    CURSOR(ELM_CURSOR_WATCH              , WATCH              , ECORE_COCOA_CURSOR_DEFAULT),
125    CURSOR(ELM_CURSOR_XTERM              , XTERM              , ECORE_COCOA_CURSOR_IBEAM)
126 };
127 
128 static const int _cursors_count = sizeof(_cursors)/sizeof(struct _Cursor_Id);
129 #endif
130 
131 #define ELM_CURSOR_GET_OR_RETURN(cur, obj, ...)         \
132   Elm_Cursor *cur;                                      \
133   do                                                    \
134     {                                                   \
135        if (!(obj))                                      \
136          {                                              \
137             CRI("Null pointer: " #obj);            \
138             return __VA_ARGS__;                         \
139          }                                              \
140        cur = evas_object_data_get((obj), _cursor_key);  \
141        if (!cur)                                        \
142          {                                              \
143             ERR("Object does not have cursor: " #obj);  \
144             return __VA_ARGS__;                         \
145          }                                              \
146     }                                                   \
147   while (0)
148 
149 struct _Elm_Cursor
150 {
151    Evas_Object *obj, *hotobj;
152    Evas_Object *eventarea, *owner;
153    const char *style, *cursor_name;
154    int hot_x, hot_y;
155    Ecore_Evas *ee;
156    Evas *evas;
157    Ecore_Job *hotupdate_job;
158 #ifdef HAVE_ELEMENTARY_X
159    struct {
160      Ecore_X_Cursor cursor;
161      Ecore_X_Window win;
162    } x;
163 #endif
164 #ifdef HAVE_ELEMENTARY_WL2
165    struct {
166      Ecore_Wl2_Window *win;
167    } wl;
168 #endif
169 #ifdef HAVE_ELEMENTARY_WIN32
170    struct {
171      Ecore_Win32_Cursor *cursor;
172      Ecore_Win32_Window *win;
173    } win32;
174 #endif
175 
176 #ifdef HAVE_ELEMENTARY_COCOA
177    struct {
178       Ecore_Cocoa_Cursor  cursor;
179       Ecore_Cocoa_Window *win;
180    } cocoa;
181 #endif
182    struct
183    {
184       Evas_Object *obj;
185       int layer;
186       int x, y;
187    } prev;
188 
189    Eina_Bool visible:1;
190    Eina_Bool use_engine:1;
191    Eina_Bool theme_search:1;
192 };
193 
194 static void
_elm_cursor_obj_hints(void * data,Evas * evas EINA_UNUSED,Evas_Object * obj EINA_UNUSED,void * event_info EINA_UNUSED)195 _elm_cursor_obj_hints(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
196 {
197    Elm_Cursor *cur = data;
198    int x, y;
199 
200    evas_object_size_hint_min_get(cur->obj, &x, &y);
201    if ((x < 8) || (y < 8))
202      {
203         x = 8;
204         y = 8;
205      }
206    evas_object_resize(cur->obj, x, y);
207 }
208 
209 static void
_elm_cursor_obj_del(void * data,Evas * evas EINA_UNUSED,Evas_Object * obj EINA_UNUSED,void * event_info EINA_UNUSED)210 _elm_cursor_obj_del(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
211 {
212    Elm_Cursor *cur = data;
213 
214    if (cur)
215      {
216         evas_object_event_callback_del_full(cur->obj, EVAS_CALLBACK_DEL,
217                                             _elm_cursor_obj_del, cur);
218         evas_object_event_callback_del_full(cur->obj, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
219                                             _elm_cursor_obj_hints, cur);
220         cur->obj = NULL;
221         ELM_SAFE_FREE(cur->hotobj, evas_object_del);
222      }
223 }
224 
225 static void
_elm_cursor_set_hot_spots(Elm_Cursor * cur)226 _elm_cursor_set_hot_spots(Elm_Cursor *cur)
227 {
228    const char *str;
229    Evas_Coord cx, cy, cw, ch, x, y, w, h;
230    int prev_hot_x, prev_hot_y;
231 
232    if (!cur->visible) return;
233 
234    prev_hot_x = cur->hot_x;
235    prev_hot_y = cur->hot_y;
236 
237    evas_object_geometry_get(cur->obj, &cx, &cy, &cw, &ch);
238    evas_object_geometry_get(cur->hotobj, &x, &y, &w, &h);
239    cur->hot_x = (x + (w / 2)) - cx;
240    cur->hot_y = (y + (h / 2)) - cy;
241 
242    str = edje_object_data_get(cur->obj, "hot_x");
243    if (str) cur->hot_x = atoi(str);
244    str = edje_object_data_get(cur->obj, "hot_y");
245    if (str) cur->hot_y = atoi(str);
246 
247    if ((cur->visible) &&
248        ((prev_hot_x != cur->hot_x) || (prev_hot_y != cur->hot_y)))
249      {
250         ecore_evas_object_cursor_set(cur->ee, cur->obj,
251                                      ELM_OBJECT_LAYER_CURSOR,
252                                      cur->hot_x, cur->hot_y);
253      }
254 }
255 
256 static void
_elm_cursor_set_hot_spots_job(void * data)257 _elm_cursor_set_hot_spots_job(void *data)
258 {
259    Elm_Cursor *cur = data;
260 
261    cur->hotupdate_job = NULL;
262 
263    _elm_cursor_set_hot_spots(cur);
264 }
265 
266 static void
_elm_cursor_hot_change(void * data,Evas * evas EINA_UNUSED,Evas_Object * obj EINA_UNUSED,void * event_info EINA_UNUSED)267 _elm_cursor_hot_change(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
268 {
269    Elm_Cursor *cur = data;
270    if (cur->hotupdate_job) ecore_job_del(cur->hotupdate_job);
271    cur->hotupdate_job = ecore_job_add(_elm_cursor_set_hot_spots_job, data);
272 }
273 
274 static Eina_Bool
_elm_cursor_obj_add(Evas_Object * obj,Elm_Cursor * cur)275 _elm_cursor_obj_add(Evas_Object *obj, Elm_Cursor *cur)
276 {
277 #ifdef HAVE_ELEMENTARY_WL2
278    const char *engine_name;
279 
280    engine_name = ecore_evas_engine_name_get(cur->ee);
281    if ((engine_name) &&
282        ((!strcmp(engine_name, ELM_WAYLAND_SHM)) ||
283            (!strcmp(engine_name, ELM_WAYLAND_EGL))))
284      return EINA_FALSE;
285 #endif
286 
287    cur->obj = edje_object_add(cur->evas);
288    if (!cur->obj) return EINA_FALSE;
289    edje_object_freeze(cur->obj);
290    edje_object_update_hints_set(cur->obj, 1);
291 
292    if (elm_widget_theme_object_set(obj, cur->obj, "cursor", cur->cursor_name,
293                              cur->style ? cur->style : "default") == EFL_UI_THEME_APPLY_ERROR_GENERIC)
294      {
295         ELM_SAFE_FREE(cur->obj, evas_object_del);
296         return EINA_FALSE;
297      }
298    evas_object_data_set(cur->obj, "elm-cursor", (void*)1);
299    cur->hotobj = evas_object_rectangle_add(cur->evas);
300    evas_object_color_set(cur->hotobj, 0, 0, 0, 0);
301    evas_object_event_callback_add(cur->obj, EVAS_CALLBACK_MOVE,
302                                   _elm_cursor_hot_change, cur);
303    evas_object_event_callback_add(cur->obj, EVAS_CALLBACK_RESIZE,
304                                   _elm_cursor_hot_change, cur);
305    evas_object_event_callback_add(cur->hotobj, EVAS_CALLBACK_MOVE,
306                                   _elm_cursor_hot_change, cur);
307    evas_object_event_callback_add(cur->hotobj, EVAS_CALLBACK_RESIZE,
308                                   _elm_cursor_hot_change, cur);
309 
310    if (elm_widget_is_legacy(obj))
311      {
312         if (edje_object_part_exists(cur->obj, "elm.swallow.hotspot"))
313           edje_object_part_swallow(cur->obj, "elm.swallow.hotspot", cur->hotobj);
314         else if (edje_object_part_exists(cur->obj, "elm.content.hotspot"))
315           edje_object_part_swallow(cur->obj, "elm.content.hotspot", cur->hotobj);
316         else
317           {
318              ELM_SAFE_FREE(cur->hotobj, evas_object_del);
319              ELM_SAFE_FREE(cur->obj, evas_object_del);
320              return EINA_FALSE;
321           }
322      }
323    else
324      {
325         if (edje_object_part_exists(cur->obj, "efl.hotspot"))
326           edje_object_part_swallow(cur->obj, "efl.hotspot", cur->hotobj);
327         else if (edje_object_part_exists(cur->obj, "efl.content.hotspot"))
328           edje_object_part_swallow(cur->obj, "efl.content.hotspot", cur->hotobj);
329         else
330           {
331              ELM_SAFE_FREE(cur->hotobj, evas_object_del);
332              ELM_SAFE_FREE(cur->obj, evas_object_del);
333              return EINA_FALSE;
334           }
335      }
336 
337    evas_object_event_callback_add(cur->obj, EVAS_CALLBACK_DEL,
338                                   _elm_cursor_obj_del, cur);
339    evas_object_event_callback_add(cur->obj, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
340                                   _elm_cursor_obj_hints, cur);
341    edje_object_thaw(cur->obj);
342 
343    return EINA_TRUE;
344 }
345 
346 static void
_elm_cursor_set(Elm_Cursor * cur)347 _elm_cursor_set(Elm_Cursor *cur)
348 {
349    evas_event_freeze(cur->evas);
350    if (!cur->use_engine)
351      {
352         if (cur->visible) goto end;
353 
354         if (!cur->obj)
355           _elm_cursor_obj_add(cur->owner, cur);
356         if (cur->obj)
357           {
358              ecore_evas_cursor_get(cur->ee, &cur->prev.obj, &cur->prev.layer, &cur->prev.x, &cur->prev.y);
359              if (cur->prev.obj)
360                {
361                   if (evas_object_data_get(cur->prev.obj, "elm-cursor"))
362                     memset(&cur->prev, 0, sizeof(cur->prev));
363                   else
364                     ecore_evas_cursor_unset(cur->ee);
365                }
366              ecore_evas_object_cursor_set(cur->ee, cur->obj,
367                                        ELM_OBJECT_LAYER_CURSOR, cur->hot_x,
368                                        cur->hot_y);
369           }
370         cur->visible = !!cur->obj;
371      }
372    else
373      {
374         cur->visible = EINA_TRUE;
375         if (cur->obj)
376           {
377              evas_object_del(cur->obj);
378              cur->obj = NULL;
379              ELM_SAFE_FREE(cur->hotobj, evas_object_del);
380           }
381 #ifdef HAVE_ELEMENTARY_X
382         if (cur->x.win)
383           ecore_x_window_cursor_set(cur->x.win, cur->x.cursor);
384 #endif
385 #ifdef HAVE_ELEMENTARY_WL2
386         if (cur->wl.win)
387           {
388              Evas_Object *top;
389 
390              top = elm_widget_top_get(cur->owner);
391              if ((top) && (efl_isa(top, EFL_UI_WIN_CLASS)))
392                _elm_win_wl_cursor_set(top, cur->cursor_name);
393           }
394 #endif
395 
396 #ifdef HAVE_ELEMENTARY_COCOA
397         if (cur->cocoa.win)
398           ecore_cocoa_window_cursor_set(cur->cocoa.win, cur->cocoa.cursor);
399 #endif
400 
401 #ifdef HAVE_ELEMENTARY_WIN32
402         if (cur->win32.win)
403           ecore_win32_window_cursor_set(cur->win32.win, cur->win32.cursor);
404 #endif
405      }
406 end:
407    evas_event_thaw(cur->evas);
408 }
409 
410 static void
_elm_cursor_mouse_in(void * data,Evas * evas EINA_UNUSED,Evas_Object * obj EINA_UNUSED,void * event_info)411 _elm_cursor_mouse_in(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
412 {
413    Elm_Cursor *cur = data;
414 
415    Evas_Event_Mouse_In *ev = event_info;
416    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
417 
418    _elm_cursor_set(cur);
419 }
420 
421 static void
_elm_cursor_mouse_out(void * data,Evas * evas EINA_UNUSED,Evas_Object * obj EINA_UNUSED,void * event_info)422 _elm_cursor_mouse_out(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
423 {
424    Evas_Object *sobj_parent;
425    Elm_Cursor *pcur = NULL;
426    Elm_Cursor *cur = data;
427 
428    Evas_Event_Mouse_Out *ev = event_info;
429    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
430 
431    if (!cur->visible) return;
432    evas_event_freeze(cur->evas);
433    cur->visible = EINA_FALSE;
434 
435    sobj_parent = evas_object_data_get(cur->eventarea, "elm-parent");
436    while (sobj_parent)
437      {
438         pcur = evas_object_data_get((sobj_parent), _cursor_key);
439         if ((pcur) && (pcur->visible)) break;
440         sobj_parent = evas_object_data_get(sobj_parent, "elm-parent");
441      }
442 
443    if (pcur)
444      {
445         pcur->visible = EINA_FALSE;
446         evas_event_thaw(cur->evas);
447         _elm_cursor_set(pcur);
448         return;
449      }
450 
451    if (!cur->use_engine)
452      {
453         if (cur->prev.obj)
454           ecore_evas_object_cursor_set(cur->ee, cur->prev.obj, cur->prev.layer,
455                                        cur->prev.x, cur->prev.y);
456         else
457           ecore_evas_object_cursor_set(cur->ee, NULL, ELM_OBJECT_LAYER_CURSOR,
458                                        cur->hot_x, cur->hot_y);
459         memset(&cur->prev, 0, sizeof(cur->prev));
460      }
461    else
462      {
463 #ifdef HAVE_ELEMENTARY_X
464         if (cur->x.win)
465           ecore_x_window_cursor_set(cur->x.win, ECORE_X_CURSOR_X);
466 #endif
467 #ifdef HAVE_ELEMENTARY_WL2
468         if (cur->wl.win)
469           {
470              Evas_Object *top;
471 
472              top = elm_widget_top_get(cur->owner);
473              if ((top) && (efl_isa(top, EFL_UI_WIN_CLASS)))
474                _elm_win_wl_cursor_set(top, NULL);
475           }
476 #endif
477 
478 #ifdef HAVE_ELEMENTARY_COCOA
479         if (cur->cocoa.win)
480           ecore_cocoa_window_cursor_set(cur->cocoa.win, ECORE_COCOA_CURSOR_DEFAULT);
481 #endif
482 
483 #ifdef HAVE_ELEMENTARY_WIN32
484         if (cur->win32.win)
485           ecore_win32_window_cursor_set(cur->win32.win, ecore_win32_cursor_shaped_new(ECORE_WIN32_CURSOR_SHAPE_ARROW));
486 #endif
487      }
488    evas_event_thaw(cur->evas);
489 }
490 
491 static void
_elm_cursor_del(void * data EINA_UNUSED,Evas * evas EINA_UNUSED,Evas_Object * obj,void * event_info EINA_UNUSED)492 _elm_cursor_del(void *data EINA_UNUSED, Evas *evas EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
493 {
494    elm_object_cursor_unset(obj);
495 }
496 
497 #if defined(HAVE_ELEMENTARY_X) || defined(HAVE_ELEMENTARY_COCOA) || defined(HAVE_ELEMENTARY_WIN32)
498 static int
_elm_cursor_strcmp(const void * data1,const void * data2)499 _elm_cursor_strcmp(const void *data1, const void *data2)
500 {
501    const struct _Cursor_Id *c1 = data1;
502    const struct _Cursor_Id *c2 = data2;
503    return strcmp (c1->name, c2->name);
504 }
505 #endif
506 
507 static void
_elm_cursor_cur_set(Elm_Cursor * cur)508 _elm_cursor_cur_set(Elm_Cursor *cur)
509 {
510    if (!cur->theme_search)
511      {
512         INF("Using only engine cursors");
513         cur->use_engine = EINA_TRUE;
514      }
515    else if (_elm_cursor_obj_add(cur->owner, cur))
516      {
517         _elm_cursor_set_hot_spots(cur);
518         cur->use_engine = EINA_FALSE;
519         elm_widget_cursor_add(cur->owner, cur);
520      }
521    else
522      {
523         INF("Cursor couldn't be found on theme: %s", cur->cursor_name);
524         cur->use_engine = EINA_TRUE;
525      }
526 
527 #ifdef HAVE_ELEMENTARY_DRM
528    const char *engine_name;
529 
530    engine_name = ecore_evas_engine_name_get(cur->ee);
531    if ((engine_name) && (!strcmp(engine_name, ELM_DRM)))
532      cur->use_engine = EINA_FALSE;
533 #endif
534 
535    if (cur->use_engine)
536      {
537         Evas_Object *top;
538 
539         top = elm_widget_top_get(cur->owner);
540         if ((top) && (efl_isa(top, EFL_UI_WIN_CLASS)))
541           {
542 #ifdef HAVE_ELEMENTARY_X
543              cur->x.win = elm_win_xwindow_get(top);
544              if (cur->x.win)
545                {
546                   struct _Cursor_Id *cur_id;
547 
548                   cur_id = bsearch(&(cur->cursor_name), _cursors, _cursors_count,
549                                    sizeof(struct _Cursor_Id), _elm_cursor_strcmp);
550 
551                   if (!cur_id)
552                     {
553                        INF("X cursor couldn't be found: %s. Using default.",
554                            cur->cursor_name);
555                        cur->x.cursor = ecore_x_cursor_shape_get(ECORE_X_CURSOR_X);
556                     }
557                   else
558                     cur->x.cursor = ecore_x_cursor_shape_get(cur_id->id);
559                }
560 #endif
561 
562 #ifdef HAVE_ELEMENTARY_COCOA
563              cur->cocoa.win = elm_win_cocoa_window_get(top);
564              if (cur->cocoa.win)
565                {
566                   struct _Cursor_Id *cur_id;
567 
568                   cur_id = bsearch(&(cur->cursor_name), _cursors, _cursors_count,
569                                    sizeof(struct _Cursor_Id), _elm_cursor_strcmp);
570                   if (!cur_id)
571                     {
572                        INF("Cocoa Cursor couldn't be found: %s. Using default...",
573                            cur->cursor_name);
574                        cur->cocoa.cursor = ECORE_COCOA_CURSOR_DEFAULT;
575                     }
576                   else
577                     cur->cocoa.cursor = cur_id->cid;
578                }
579 #endif
580 #ifdef HAVE_ELEMENTARY_WL2
581              cur->wl.win = elm_win_wl_window_get(top);
582 #endif
583 #ifdef HAVE_ELEMENTARY_WIN32
584              cur->win32.win = elm_win_win32_window_get(top);
585              if (cur->win32.win)
586                {
587                   struct _Cursor_Id *cur_id;
588 
589                   cur_id = bsearch(&(cur->cursor_name), _cursors, _cursors_count,
590                                    sizeof(struct _Cursor_Id), _elm_cursor_strcmp);
591 
592                   if (!cur_id)
593                     {
594                        INF("Win32 X cursor couldn't be found: %s. Using default.",
595                            cur->cursor_name);
596                        cur->win32.cursor = ecore_win32_cursor_shaped_new(ECORE_WIN32_CURSOR_SHAPE_ARROW);
597                     }
598                   else
599                     cur->win32.cursor = (Ecore_Win32_Cursor *)ecore_win32_cursor_x11_shaped_get(cur_id->id);
600                }
601 #endif
602           }
603      }
604 
605    if (efl_canvas_pointer_inside_get(cur->eventarea, NULL))
606      _elm_cursor_set(cur);
607 }
608 
609 /**
610  * Set the cursor to be shown when mouse is over the object
611  *
612  * Set the cursor that will be displayed when mouse is over the
613  * object. The object can have only one cursor set to it, so if
614  * this function is called twice for an object, the previous set
615  * will be unset.
616  * If using X cursors, a definition of all the valid cursor names
617  * is listed on Elementary_Cursors.h. If an invalid name is set
618  * the default cursor will be used.
619  *
620  * This is an internal function that is used by objects with sub-items
621  * that want to provide different cursors for each of them. The @a
622  * owner object should be an elm_widget and will be used to track
623  * theme changes and to feed @a func and @a del_cb. The @a eventarea
624  * may be any object and is the one that should be used later on with
625  * elm_object_cursor apis, such as elm_object_cursor_unset().
626  *
627  * @param eventarea the object being attached a cursor.
628  * @param owner the elm_widget that owns this object, will be used to
629  *        track theme changes and to be used in @a func or @a del_cb.
630  * @param cursor the cursor name to be used.
631  *
632  * @internal
633  * @ingroup Elm_Cursors
634  */
635 void
elm_object_sub_cursor_set(Evas_Object * eventarea,Evas_Object * owner,const char * cursor)636 elm_object_sub_cursor_set(Evas_Object *eventarea, Evas_Object *owner, const char *cursor)
637 {
638    Elm_Cursor *cur = NULL;
639 
640    cur = evas_object_data_get(eventarea, _cursor_key);
641    if (cur)
642      elm_object_cursor_unset(eventarea);
643 
644    if (!cursor) return;
645 
646    cur = ELM_NEW(Elm_Cursor);
647    if (!cur) return;
648 
649    cur->owner = owner;
650    cur->eventarea = eventarea;
651    cur->theme_search = !_elm_config->cursor_engine_only;
652    cur->visible = EINA_FALSE;
653    cur->style = eina_stringshare_add("default");
654 
655    cur->cursor_name = eina_stringshare_add(cursor);
656    if (!cur->cursor_name)
657      ERR("Could not store cursor name %s", cursor);
658 
659    cur->evas = evas_object_evas_get(eventarea);
660    cur->ee = ecore_evas_ecore_evas_get(cur->evas);
661 
662    _elm_cursor_cur_set(cur);
663 
664    evas_object_data_set(eventarea, _cursor_key, cur);
665 
666    evas_object_event_callback_add(eventarea, EVAS_CALLBACK_MOUSE_IN,
667                                   _elm_cursor_mouse_in, cur);
668    evas_object_event_callback_add(eventarea, EVAS_CALLBACK_MOUSE_OUT,
669                                   _elm_cursor_mouse_out, cur);
670    evas_object_event_callback_add(eventarea, EVAS_CALLBACK_DEL,
671                                   _elm_cursor_del, cur);
672 }
673 
674 EOLIAN Eina_Bool
_efl_ui_widget_cursor_set(Evas_Object * obj,Elm_Widget_Smart_Data * pd EINA_UNUSED,const char * cursor)675 _efl_ui_widget_cursor_set(Evas_Object *obj, Elm_Widget_Smart_Data *pd EINA_UNUSED,
676                                      const char *cursor)
677 {
678    EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EINA_FALSE);
679    elm_object_sub_cursor_set(obj, obj, cursor);
680    return EINA_TRUE;
681 }
682 
683 const char *
elm_object_sub_cursor_get(const Evas_Object * obj)684 elm_object_sub_cursor_get(const Evas_Object *obj)
685 {
686    ELM_CURSOR_GET_OR_RETURN(cur, obj, NULL);
687    return cur->cursor_name;
688 }
689 
690 EOLIAN const char *
_efl_ui_widget_cursor_get(const Evas_Object * obj,Elm_Widget_Smart_Data * pd EINA_UNUSED)691 _efl_ui_widget_cursor_get(const Evas_Object *obj, Elm_Widget_Smart_Data *pd EINA_UNUSED)
692 {
693    return elm_object_sub_cursor_get(obj);
694 }
695 
696 EAPI void
elm_object_cursor_unset(Evas_Object * obj)697 elm_object_cursor_unset(Evas_Object *obj)
698 {
699    ELM_CURSOR_GET_OR_RETURN(cur, obj);
700 
701    eina_stringshare_del(cur->cursor_name);
702    cur->cursor_name = NULL;
703    eina_stringshare_del(cur->style);
704    cur->style = NULL;
705 
706    if (cur->owner)
707      elm_widget_cursor_del(cur->owner, cur);
708 
709    if (cur->obj)
710      {
711         evas_object_event_callback_del_full(cur->obj, EVAS_CALLBACK_DEL,
712                                             _elm_cursor_obj_del, cur);
713         evas_object_event_callback_del_full(cur->obj, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
714                                             _elm_cursor_obj_hints, cur);
715         ELM_SAFE_FREE(cur->obj, evas_object_del);
716      }
717 
718    if (cur->visible)
719      {
720         if (!cur->use_engine)
721           ecore_evas_object_cursor_set(cur->ee, NULL, ELM_OBJECT_LAYER_CURSOR,
722                                        cur->hot_x, cur->hot_y);
723 #ifdef HAVE_ELEMENTARY_X
724         else if (cur->x.win)
725           ecore_x_window_cursor_set(cur->x.win, ECORE_X_CURSOR_X);
726 #endif
727 #ifdef HAVE_ELEMENTARY_COCOA
728         else if (cur->cocoa.win)
729           ecore_cocoa_window_cursor_set(cur->cocoa.win, ECORE_COCOA_CURSOR_DEFAULT);
730 #endif
731 #ifdef HAVE_ELEMENTARY_WL2
732         else if (cur->wl.win)
733           {
734              Evas_Object *top;
735 
736              top = elm_widget_top_get(cur->owner);
737              if ((top) && (efl_isa(top, EFL_UI_WIN_CLASS)))
738                _elm_win_wl_cursor_set(top, NULL);
739           }
740 #endif
741 #ifdef HAVE_ELEMENTARY_WIN32
742         else
743           ecore_win32_window_cursor_set(cur->win32.win, ecore_win32_cursor_shaped_new(ECORE_WIN32_CURSOR_SHAPE_ARROW));
744 #endif
745      }
746 
747    evas_object_event_callback_del_full(obj, EVAS_CALLBACK_MOUSE_IN,
748                                   _elm_cursor_mouse_in, cur);
749    evas_object_event_callback_del_full(obj, EVAS_CALLBACK_MOUSE_OUT,
750                                   _elm_cursor_mouse_out, cur);
751    evas_object_event_callback_del_full(obj, EVAS_CALLBACK_DEL,
752                                        _elm_cursor_del, cur);
753    evas_object_data_del(obj, _cursor_key);
754 
755    if (cur->hotupdate_job) ecore_job_del(cur->hotupdate_job);
756    cur->hotupdate_job = NULL;
757 
758    free(cur);
759 }
760 
761 Eina_Bool
elm_object_sub_cursor_style_set(Evas_Object * obj,const char * style)762 elm_object_sub_cursor_style_set(Evas_Object *obj, const char *style)
763 {
764    ELM_CURSOR_GET_OR_RETURN(cur, obj, EINA_FALSE);
765 
766    if (!eina_stringshare_replace(&cur->style, style))
767      ERR("Could not set current style=%s", style);
768 
769    if (cur->use_engine) return EINA_FALSE;
770 
771    if (!cur->obj)
772      {
773         if (!_elm_cursor_obj_add(cur->owner, cur))
774           {
775              ERR("Could not create cursor object");
776              return EINA_FALSE;
777           }
778         else
779           _elm_cursor_set_hot_spots(cur);
780      }
781    else
782      {
783         if (elm_widget_theme_object_set(obj, cur->obj, "cursor", cur->cursor_name,
784                                    style) == EFL_UI_THEME_APPLY_ERROR_GENERIC)
785           {
786              ERR("Could not apply the theme to the cursor style=%s", style);
787              return EINA_FALSE;
788           }
789         else
790           _elm_cursor_set_hot_spots(cur);
791      }
792 
793    return EINA_TRUE;
794 }
795 
796 EOLIAN Eina_Bool
_efl_ui_widget_cursor_style_set(Evas_Object * obj,Elm_Widget_Smart_Data * pd EINA_UNUSED,const char * style)797 _efl_ui_widget_cursor_style_set(Evas_Object *obj, Elm_Widget_Smart_Data *pd EINA_UNUSED,
798                                            const char *style)
799 {
800    return elm_object_sub_cursor_style_set(obj, style);
801 }
802 
803 const char *
elm_object_sub_cursor_style_get(const Evas_Object * obj)804 elm_object_sub_cursor_style_get(const Evas_Object *obj)
805 {
806    ELM_CURSOR_GET_OR_RETURN(cur, obj, NULL);
807    return cur->style ? cur->style : "default";
808 }
809 
810 EOLIAN const char *
_efl_ui_widget_cursor_style_get(const Evas_Object * obj,Elm_Widget_Smart_Data * pd EINA_UNUSED)811 _efl_ui_widget_cursor_style_get(const Evas_Object *obj, Elm_Widget_Smart_Data *pd EINA_UNUSED)
812 {
813    return elm_object_sub_cursor_style_get(obj);
814 }
815 
816 /**
817  * Notify cursor should recalculate its theme.
818  * @internal
819  */
820 void
elm_cursor_theme(Elm_Cursor * cur)821 elm_cursor_theme(Elm_Cursor *cur)
822 {
823    if ((!cur) || (!cur->obj)) return;
824    if (elm_widget_theme_object_set(cur->owner, cur->obj, "cursor",
825                               cur->cursor_name, cur->style) == EFL_UI_THEME_APPLY_ERROR_GENERIC)
826      ERR("Could not apply the theme to the cursor style=%s", cur->style);
827    else
828      _elm_cursor_set_hot_spots(cur);
829 }
830 
831 Eina_Bool
elm_object_sub_cursor_theme_search_enabled_set(Evas_Object * obj,Eina_Bool theme_search)832 elm_object_sub_cursor_theme_search_enabled_set(Evas_Object *obj, Eina_Bool theme_search)
833 {
834    ELM_CURSOR_GET_OR_RETURN(cur, obj, EINA_FALSE);
835    cur->theme_search = theme_search;
836    ELM_SAFE_FREE(cur->obj, evas_object_del);
837    _elm_cursor_cur_set(cur);
838    return EINA_TRUE;
839 }
840 
841 EOLIAN Eina_Bool
_efl_ui_widget_cursor_theme_search_enabled_set(Evas_Object * obj,Elm_Widget_Smart_Data * pd EINA_UNUSED,Eina_Bool theme_search)842 _efl_ui_widget_cursor_theme_search_enabled_set(Evas_Object *obj, Elm_Widget_Smart_Data *pd EINA_UNUSED,
843                                                              Eina_Bool theme_search)
844 {
845    return elm_object_sub_cursor_theme_search_enabled_set(obj, theme_search);
846 }
847 
848 Eina_Bool
elm_object_sub_cursor_theme_search_enabled_get(const Evas_Object * obj)849 elm_object_sub_cursor_theme_search_enabled_get(const Evas_Object *obj)
850 {
851    ELM_CURSOR_GET_OR_RETURN(cur, obj, EINA_FALSE);
852    return cur->theme_search;
853 }
854 
855 EOLIAN Eina_Bool
_efl_ui_widget_cursor_theme_search_enabled_get(const Evas_Object * obj,Elm_Widget_Smart_Data * pd EINA_UNUSED)856 _efl_ui_widget_cursor_theme_search_enabled_get(const Evas_Object *obj, Elm_Widget_Smart_Data *pd EINA_UNUSED)
857 {
858    return elm_object_sub_cursor_theme_search_enabled_get(obj);
859 }
860