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