EFL migration revision 67547
[framework/uifw/elementary.git] / src / lib / elm_glview.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 typedef struct _Widget_Data Widget_Data;
5
6 struct _Widget_Data
7 {
8    Evas_Object              *glview_image;
9
10    Elm_GLView_Mode           mode;
11    Elm_GLView_Resize_Policy  scale_policy;
12    Elm_GLView_Render_Policy  render_policy;
13
14    Evas_GL                  *evasgl;
15    Evas_GL_Config           *config;
16    Evas_GL_Surface          *surface;
17    Evas_GL_Context          *context;
18
19    Evas_Coord                w, h;
20
21    Elm_GLView_Func_Cb        init_func;
22    Elm_GLView_Func_Cb        del_func;
23    Elm_GLView_Func_Cb        resize_func;
24    Elm_GLView_Func_Cb        render_func;
25
26    Ecore_Idle_Enterer       *render_idle_enterer;
27
28    Eina_Bool                 initialized;
29    Eina_Bool                 resized;
30 };
31
32 static const char *widtype = NULL;
33 static void _del_hook(Evas_Object *obj);
34 static void _on_focus_hook(void *data, Evas_Object *obj);
35
36 static const char SIG_FOCUSED[] = "focused";
37 static const char SIG_UNFOCUSED[] = "unfocused";
38
39 static void
40 _del_hook(Evas_Object *obj)
41 {
42    Widget_Data *wd = elm_widget_data_get(obj);
43    if (!wd) return;
44
45    // Call delete func if it's registered
46    if (wd->del_func)
47      {
48         evas_gl_make_current(wd->evasgl, wd->surface, wd->context);
49         wd->del_func(obj);
50      }
51
52    if (wd->render_idle_enterer) ecore_idle_enterer_del(wd->render_idle_enterer);
53
54    if (wd->surface) evas_gl_surface_destroy(wd->evasgl, wd->surface);
55    if (wd->context) evas_gl_context_destroy(wd->evasgl, wd->context);
56    if (wd->config) evas_gl_config_free(wd->config);
57    if (wd->evasgl) evas_gl_free(wd->evasgl);
58
59    free(wd);
60 }
61
62 static void
63 _on_focus_hook(void *data __UNUSED__, Evas_Object *obj)
64 {
65    Widget_Data *wd = elm_widget_data_get(obj);
66    if (!wd) return;
67
68    if (elm_widget_focus_get(obj))
69      {
70         evas_object_focus_set(wd->glview_image, EINA_TRUE);
71         evas_object_smart_callback_call(obj, SIG_FOCUSED, NULL);
72      }
73    else
74      {
75         evas_object_focus_set(wd->glview_image, EINA_FALSE);
76         evas_object_smart_callback_call(obj, SIG_UNFOCUSED, NULL);
77      }
78 }
79
80 static void
81 _glview_update_surface(Evas_Object *obj)
82 {
83    Widget_Data *wd = elm_widget_data_get(obj);
84    if (!wd) return;
85
86    if (wd->surface)
87      {
88         evas_object_image_native_surface_set(wd->glview_image, NULL);
89         evas_gl_surface_destroy(wd->evasgl, wd->surface);
90         wd->surface = NULL;
91      }
92
93    evas_object_image_size_set(wd->glview_image, wd->w, wd->h);
94
95    if (!wd->surface)
96      {
97         Evas_Native_Surface ns;
98
99         wd->surface = evas_gl_surface_create(wd->evasgl, wd->config,
100                                              wd->w, wd->h);
101         evas_gl_native_surface_get(wd->evasgl, wd->surface, &ns);
102         evas_object_image_native_surface_set(wd->glview_image, &ns);
103         elm_glview_changed_set(obj);
104      }
105 }
106
107 static void
108 _glview_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
109 {
110    Widget_Data *wd = elm_widget_data_get(data);
111    Evas_Coord w, h;
112
113    if (!wd) return;
114
115    wd->resized = EINA_TRUE;
116
117    if (wd->scale_policy == ELM_GLVIEW_RESIZE_POLICY_RECREATE)
118      {
119         evas_object_geometry_get(wd->glview_image, NULL, NULL, &w, &h);
120         if ((w == 0) || (h == 0))
121           {
122              w = 64;
123              h = 64;
124           }
125         if ((wd->w == w) && (wd->h == h)) return;
126         wd->w = w;
127         wd->h = h;
128         _glview_update_surface(data);
129         /*
130         if (wd->render_func)
131           {
132              evas_gl_make_current(wd->evasgl, wd->surface, wd->context);
133              wd->render_func(data);
134           }
135           */
136      }
137 }
138
139 static Eina_Bool
140 _render_cb(void *obj)
141 {
142    Widget_Data *wd = elm_widget_data_get(obj);
143    if (!wd) return EINA_FALSE;
144
145    // Do a make current
146    if (!evas_gl_make_current(wd->evasgl, wd->surface, wd->context))
147      {
148         wd->render_idle_enterer = NULL;
149         ERR("Failed doing make current.\n");
150         return EINA_FALSE;
151      }
152
153    // Call the init function if it hasn't been called already
154    if (!wd->initialized)
155      {
156         if (wd->init_func) wd->init_func(obj);
157         wd->initialized = EINA_TRUE;
158      }
159
160    if (wd->resized)
161      {
162         if (wd->resize_func) wd->resize_func(obj);
163         wd->resized = EINA_FALSE;
164      }
165
166    // Call the render function
167    if (wd->render_func) wd->render_func(obj);
168
169    // Depending on the policy return true or false
170    if (wd->render_policy == ELM_GLVIEW_RENDER_POLICY_ON_DEMAND)
171      return EINA_TRUE;
172    else if (wd->render_policy == ELM_GLVIEW_RENDER_POLICY_ALWAYS)
173      {
174         // Return false so it only runs once
175         wd->render_idle_enterer = NULL;
176         return EINA_FALSE;
177      }
178    else
179      {
180         ERR("Invalid Render Policy.\n");
181         wd->render_idle_enterer = NULL;
182         return EINA_FALSE;
183      }
184    return EINA_TRUE;
185 }
186
187 static void
188 _set_render_policy_callback(Evas_Object *obj)
189 {
190    Widget_Data *wd = elm_widget_data_get(obj);
191
192    switch (wd->render_policy)
193      {
194       case ELM_GLVIEW_RENDER_POLICY_ON_DEMAND:
195          // Delete idle_enterer if it for some reason is around
196          if (wd->render_idle_enterer)
197            {
198               ecore_idle_enterer_del(wd->render_idle_enterer);
199               wd->render_idle_enterer = NULL;
200            }
201
202          // Set pixel getter callback
203          evas_object_image_pixels_get_callback_set
204             (wd->glview_image, (Evas_Object_Image_Pixels_Get_Cb)_render_cb, obj);
205          break;
206       case ELM_GLVIEW_RENDER_POLICY_ALWAYS:
207          // Unset the pixel getter callback if set already
208          evas_object_image_pixels_get_callback_set(wd->glview_image, NULL, NULL);
209
210          break;
211       default:
212          ERR("Invalid Render Policy.\n");
213          return;
214      }
215 }
216
217 EAPI Evas_Object *
218 elm_glview_add(Evas_Object *parent)
219 {
220    Evas_Object *obj;
221    Evas *e;
222    Widget_Data *wd;
223
224    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
225
226    ELM_SET_WIDTYPE(widtype, "glview");
227    elm_widget_type_set(obj, "glview");
228    elm_widget_sub_object_add(parent, obj);
229    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
230    elm_widget_data_set(obj, wd);
231    elm_widget_del_hook_set(obj, _del_hook);
232
233    // Evas_GL
234    wd->evasgl = evas_gl_new(e);
235    if (!wd->evasgl)
236      {
237         ERR("Failed Creating an Evas GL Object.\n");
238         return NULL;
239      }
240
241    // Create a default config
242    wd->config = evas_gl_config_new();
243    if (!wd->config)
244      {
245         ERR("Failed Creating a Config Object.\n");
246         evas_gl_free(wd->evasgl);
247         return NULL;
248      }
249    wd->config->color_format = EVAS_GL_RGB_888;
250
251    // Create image to render Evas_GL Surface
252    wd->glview_image = evas_object_image_filled_add(e);
253    evas_object_image_size_set(wd->glview_image, 1, 1);
254    evas_object_event_callback_add(wd->glview_image, EVAS_CALLBACK_RESIZE,
255                                   _glview_resize, obj);
256    elm_widget_resize_object_set(obj, wd->glview_image);
257    evas_object_show(wd->glview_image);
258
259    // Initialize variables
260    wd->mode                = 0;
261    wd->scale_policy        = ELM_GLVIEW_RESIZE_POLICY_RECREATE;
262    wd->render_policy       = ELM_GLVIEW_RENDER_POLICY_ON_DEMAND;
263    wd->surface             = NULL;
264
265    // Initialize it to (64,64)  (It's an arbitrary value)
266    wd->w                   = 64;
267    wd->h                   = 64;
268
269    // Initialize the rest of the values
270    wd->init_func           = NULL;
271    wd->del_func            = NULL;
272    wd->render_func         = NULL;
273    wd->render_idle_enterer = NULL;
274    wd->initialized         = EINA_FALSE;
275    wd->resized             = EINA_FALSE;
276
277    // Create Context
278    if (!wd->context)
279      {
280         wd->context = evas_gl_context_create(wd->evasgl, NULL);
281         if (!wd->context)
282           {
283              ERR("Error Creating an Evas_GL Context.\n");
284              return NULL;
285           }
286      }
287    return obj;
288 }
289
290 EAPI Evas_GL_API *
291 elm_glview_gl_api_get(const Evas_Object *obj)
292 {
293    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
294    Widget_Data *wd = elm_widget_data_get(obj);
295    if (!wd) return NULL;
296
297    return evas_gl_api_get(wd->evasgl);
298 }
299
300 EAPI Eina_Bool
301 elm_glview_mode_set(Evas_Object *obj, Elm_GLView_Mode mode)
302 {
303    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
304    Widget_Data *wd = elm_widget_data_get(obj);
305    if (!wd) return EINA_FALSE;
306
307    // Set the configs
308    if (mode & ELM_GLVIEW_ALPHA)
309       wd->config->color_format = EVAS_GL_RGBA_8888;
310    else
311       wd->config->color_format = EVAS_GL_RGB_888;
312
313    if (mode & ELM_GLVIEW_DEPTH)
314       wd->config->depth_bits = EVAS_GL_DEPTH_BIT_24;
315    else
316       wd->config->depth_bits = EVAS_GL_DEPTH_NONE;
317
318    if (mode & ELM_GLVIEW_STENCIL)
319       wd->config->stencil_bits = EVAS_GL_STENCIL_BIT_8;
320    else
321       wd->config->stencil_bits = EVAS_GL_STENCIL_NONE;
322
323    if (mode & ELM_GLVIEW_DIRECT)
324       wd->config->options_bits = EVAS_GL_OPTIONS_DIRECT;
325    else
326       wd->config->options_bits = EVAS_GL_OPTIONS_NONE;
327
328    // Check for Alpha Channel and enable it
329    if (mode & ELM_GLVIEW_ALPHA)
330      evas_object_image_alpha_set(wd->glview_image, EINA_TRUE);
331    else
332      evas_object_image_alpha_set(wd->glview_image, EINA_FALSE);
333
334    wd->mode = mode;
335
336    elm_glview_changed_set(obj);
337
338    return EINA_TRUE;
339 }
340
341 EAPI Eina_Bool
342 elm_glview_resize_policy_set(Evas_Object *obj, Elm_GLView_Resize_Policy policy)
343 {
344    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
345    Widget_Data *wd = elm_widget_data_get(obj);
346    if (!wd) return EINA_FALSE;
347
348    if (policy == wd->scale_policy) return EINA_TRUE;
349    switch (policy)
350      {
351       case ELM_GLVIEW_RESIZE_POLICY_RECREATE:
352       case ELM_GLVIEW_RESIZE_POLICY_SCALE:
353          wd->scale_policy = policy;
354          _glview_update_surface(obj);
355          elm_glview_changed_set(obj);
356          return EINA_TRUE;
357       default:
358          ERR("Invalid Scale Policy.\n");
359          return EINA_FALSE;
360      }
361 }
362
363 EAPI Eina_Bool
364 elm_glview_render_policy_set(Evas_Object *obj, Elm_GLView_Render_Policy policy)
365 {
366    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
367    Widget_Data *wd = elm_widget_data_get(obj);
368    if (!wd) return EINA_FALSE;
369
370    if ((policy != ELM_GLVIEW_RENDER_POLICY_ON_DEMAND) &&
371        (policy != ELM_GLVIEW_RENDER_POLICY_ALWAYS))
372      {
373         ERR("Invalid Render Policy.\n");
374         return EINA_FALSE;
375      }
376    if (wd->render_policy == policy) return EINA_TRUE;
377    wd->render_policy = policy;
378    _set_render_policy_callback(obj);
379    _glview_update_surface(obj);
380    return EINA_TRUE;
381 }
382
383 EAPI void
384 elm_glview_size_set(Evas_Object *obj, int width, int height)
385 {
386    ELM_CHECK_WIDTYPE(obj, widtype);
387    Widget_Data *wd = elm_widget_data_get(obj);
388    if (!wd) return;
389
390    if ((width == wd->w) && (height == wd->h)) return;
391    wd->w = width;
392    wd->h = height;
393    _glview_update_surface(obj);
394    elm_glview_changed_set(obj);
395 }
396
397 EAPI void
398 elm_glview_size_get(const Evas_Object *obj, int *width, int *height)
399 {
400    ELM_CHECK_WIDTYPE(obj, widtype);
401    Widget_Data *wd = elm_widget_data_get(obj);
402    if (!wd) return;
403
404    if (width) *width = wd->w;
405    if (height) *height = wd->h;
406 }
407
408 EAPI void
409 elm_glview_init_func_set(Evas_Object *obj, Elm_GLView_Func_Cb func)
410 {
411    ELM_CHECK_WIDTYPE(obj, widtype);
412    Widget_Data *wd = elm_widget_data_get(obj);
413    if (!wd) return;
414
415    wd->initialized = EINA_FALSE;
416    wd->init_func = func;
417 }
418
419 EAPI void
420 elm_glview_del_func_set(Evas_Object *obj, Elm_GLView_Func_Cb func)
421 {
422    ELM_CHECK_WIDTYPE(obj, widtype);
423    Widget_Data *wd = elm_widget_data_get(obj);
424     if (!wd) return;
425
426    wd->del_func = func;
427 }
428
429 EAPI void
430 elm_glview_resize_func_set(Evas_Object *obj, Elm_GLView_Func_Cb func)
431 {
432    ELM_CHECK_WIDTYPE(obj, widtype);
433    Widget_Data *wd = elm_widget_data_get(obj);
434     if (!wd)
435      {
436         ERR("Invalid Widget Object.\n");
437         return;
438      }
439
440    wd->resize_func = func;
441 }
442
443 EAPI void
444 elm_glview_render_func_set(Evas_Object *obj, Elm_GLView_Func_Cb func)
445 {
446    ELM_CHECK_WIDTYPE(obj, widtype);
447    Widget_Data *wd = elm_widget_data_get(obj);
448    if (!wd) return;
449
450    wd->render_func = func;
451    _set_render_policy_callback(obj);
452 }
453
454 EAPI void
455 elm_glview_changed_set(Evas_Object *obj)
456 {
457    ELM_CHECK_WIDTYPE(obj, widtype);
458    Widget_Data *wd = elm_widget_data_get(obj);
459    if (!wd) return;
460
461    evas_object_image_pixels_dirty_set(wd->glview_image, EINA_TRUE);
462    if (wd->render_policy == ELM_GLVIEW_RENDER_POLICY_ALWAYS)
463      {
464         if (!wd->render_idle_enterer)
465           wd->render_idle_enterer = ecore_idle_enterer_before_add((Ecore_Task_Cb)_render_cb, obj);
466      }
467 }
468
469 /* vim:set ts=8 sw=3 sts=3 expandtab cino=>5n-3f0^-2{2(0W1st0 :*/