Elementary glview: Fixed formatting.
[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 static Eina_Bool
131 _render_cb(void *obj)
132 {
133    Widget_Data *wd = elm_widget_data_get(obj);
134    if (!wd) return EINA_FALSE;
135
136    // Do a make current
137    if (!evas_gl_make_current(wd->evasgl, wd->surface, wd->context))
138      {
139         wd->render_idle_enterer = NULL;
140         return EINA_FALSE;
141      }
142
143    // Call the render function
144    if (wd->render_func) wd->render_func(obj);
145
146    // Depending on the policy return true or false
147    if (wd->render_policy == ELM_GLVIEW_RENDER_POLICY_ON_DEMAND)
148      return EINA_TRUE;
149    else if (wd->render_policy == ELM_GLVIEW_RENDER_POLICY_ALWAYS)
150      {
151         // Return false so it only runs once
152         wd->render_idle_enterer = NULL;
153         return EINA_FALSE;
154      }
155    else
156      {
157         ERR("Invalid Render Policy.\n");
158         wd->render_idle_enterer = NULL;
159         return EINA_FALSE;
160      }
161    return EINA_TRUE;
162 }
163
164 static void
165 _set_render_policy_callback(Evas_Object *obj)
166 {
167    Widget_Data *wd = elm_widget_data_get(obj);
168
169    switch (wd->render_policy)
170      {
171       case ELM_GLVIEW_RENDER_POLICY_ON_DEMAND:
172          // Delete idle_enterer if it for some reason is around
173          if (wd->render_idle_enterer)
174            {
175               ecore_idle_enterer_del(wd->render_idle_enterer);
176               wd->render_idle_enterer = NULL;
177            }
178
179          // Set pixel getter callback
180          evas_object_image_pixels_get_callback_set
181             (wd->glview_image, (Evas_Object_Image_Pixels_Get_Cb)_render_cb, obj);
182          break;
183       case ELM_GLVIEW_RENDER_POLICY_ALWAYS:
184          // Unset the pixel getter callback if set already
185          evas_object_image_pixels_get_callback_set(wd->glview_image, NULL, NULL);
186          break;
187       default:
188          ERR("Invalid Render Policy.\n");
189          return;
190      }
191 }
192
193 /**
194  * Add a new glview to the parent
195  *
196  * @param parent The parent object
197  * @return The new object or NULL if it cannot be created
198  *
199  * @ingroup GLView
200  */
201 EAPI Evas_Object *
202 elm_glview_add(Evas_Object *parent)
203 {
204    Evas_Object *obj;
205    Evas *e;
206    Widget_Data *wd;
207    Evas_GL_Config cfg = { EVAS_GL_RGB_8,
208                           EVAS_GL_DEPTH_NONE,
209                           EVAS_GL_STENCIL_NONE };
210
211    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
212
213    ELM_SET_WIDTYPE(widtype, "glview");
214    elm_widget_type_set(obj, "glview");
215    elm_widget_sub_object_add(parent, obj);
216    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
217    elm_widget_data_set(obj, wd);
218    elm_widget_del_hook_set(obj, _del_hook);
219
220    // Evas_GL
221    wd->evasgl = evas_gl_new(e);
222    if (!wd->evasgl)
223      {
224         ERR("Failed Creating an Evas GL Object.\n");
225         return NULL;
226      }
227
228    // Create image to render Evas_GL Surface
229    wd->glview_image = evas_object_image_filled_add(e);
230    evas_object_image_size_set(wd->glview_image, 1, 1);
231    evas_object_event_callback_add(wd->glview_image, EVAS_CALLBACK_RESIZE,
232                                   _glview_resize, obj);
233    elm_widget_resize_object_set(obj, wd->glview_image);
234    evas_object_show(wd->glview_image);
235
236    // Initialize variables
237    wd->mode          = 0;
238    wd->scale_policy  = ELM_GLVIEW_RESIZE_POLICY_RECREATE;
239    wd->render_policy = ELM_GLVIEW_RENDER_POLICY_ON_DEMAND;
240    wd->config        = cfg;
241    wd->surface       = NULL;
242
243    wd->w             = 64;
244    wd->h             = 64;
245
246    wd->render_idle_enterer = NULL;
247
248    // Create Context
249    if (!wd->context)
250      {
251         wd->context = evas_gl_context_create(wd->evasgl, NULL);
252         if (!wd->context)
253           {
254              ERR("Error Creating an Evas_GL Context.\n");
255              return NULL;
256           }
257      }
258    return obj;
259 }
260
261 /**
262  * Gets the gl api struct for gl rendering
263  *
264  * @param obj The glview object
265  * @return The api object or NULL if it cannot be created
266  *
267  * @ingroup GLView
268  */
269 EAPI Evas_GL_API *
270 elm_glview_gl_api_get(const Evas_Object *obj)
271 {
272    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
273    Widget_Data *wd = elm_widget_data_get(obj);
274    if (!wd) return NULL;
275
276    return evas_gl_api_get(wd->evasgl);
277 }
278
279
280 /**
281  * Set the mode of the GLView. Supports Three simple modes.
282  *
283  * @param obj The glview object
284  * @param mode The mode Options OR'ed enabling Alpha, Depth, Stencil.
285  * @return True if set properly.
286  *
287  * @ingroup GLView
288  */
289 EAPI Eina_Bool
290 elm_glview_mode_set(Evas_Object *obj, Elm_GLView_Mode mode)
291 {
292    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
293    Widget_Data *wd = elm_widget_data_get(obj);
294    Evas_GL_Config cfg = { EVAS_GL_RGBA_8,
295                           EVAS_GL_DEPTH_NONE,
296                           EVAS_GL_STENCIL_NONE };
297    if (!wd) return EINA_FALSE;
298
299    // Set the configs
300    if (mode & ELM_GLVIEW_ALPHA)
301      cfg.color_format = EVAS_GL_RGBA_8;
302
303    if (mode & ELM_GLVIEW_DEPTH)
304      cfg.depth_bits = EVAS_GL_DEPTH_BIT_24;
305
306    if (mode & ELM_GLVIEW_STENCIL)
307      cfg.stencil_bits = EVAS_GL_STENCIL_BIT_8;
308
309    // Check for Alpha Channel and enable it
310    if (mode & ELM_GLVIEW_ALPHA)
311      evas_object_image_alpha_set(wd->glview_image, EINA_TRUE);
312    else
313      evas_object_image_alpha_set(wd->glview_image, EINA_FALSE);
314
315    wd->mode   = mode;
316    wd->config = cfg;
317
318    elm_glview_changed_set(obj);
319
320    return EINA_TRUE;
321 }
322
323 /**
324  * Set the scaling policy for the glview object.
325  *
326  * @param obj The glview object.
327  * @param policy The scaling policy.
328  *
329  * By default, the scaling policy is set to ELM_GLVIEW_RESIZE_POLICY_RECREATE.
330  * When resize is called it destroys the previous surface and recreates the newly
331  * specified size. If the policy is set to ELM_GLVIEW_RESIZE_POLICY_SCALE, however,
332  * glview only scales the image object and not the underlying GL Surface.
333  *
334  * @ingroup GLView
335  */
336 EAPI Eina_Bool
337 elm_glview_scale_policy_set(Evas_Object *obj, Elm_GLView_Resize_Policy policy)
338 {
339    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
340    Widget_Data *wd = elm_widget_data_get(obj);
341    if (!wd) return EINA_FALSE;
342
343    if (policy == wd->scale_policy) return EINA_TRUE;
344    switch (policy)
345      {
346       case ELM_GLVIEW_RESIZE_POLICY_RECREATE:
347       case ELM_GLVIEW_RESIZE_POLICY_SCALE:
348          wd->scale_policy = policy;
349          return EINA_TRUE;
350       default:
351          ERR("Invalid Scale Policy.\n");
352          return EINA_FALSE;
353      }
354    _glview_update_surface(obj);
355    elm_glview_changed_set(obj);
356 }
357
358 /**
359  * Set the render policy for the glview object.
360  *
361  * @param obj The glview object.
362  * @param policy The render policy.
363  *
364  * By default, the render policy is set to ELM_GLVIEW_RENDER_POLICY_ON_DEMAND.
365  * This policy is set such that during the render loop, glview is only redrawn
366  * if it needs to be redrawn. (i.e. When it is visible) If the policy is set
367  * to ELM_GLVIEWW_RENDER_POLICY_ALWAYS, it redraws regardless of whether it is
368  * visible/need redrawing or not.
369  *
370  * @ingroup GLView
371  */
372 EAPI Eina_Bool
373 elm_glview_render_policy_set(Evas_Object *obj, Elm_GLView_Render_Policy policy)
374 {
375    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
376    Widget_Data *wd = elm_widget_data_get(obj);
377    if (!wd) return EINA_FALSE;
378
379    if ((policy != ELM_GLVIEW_RENDER_POLICY_ON_DEMAND) &&
380        (policy != ELM_GLVIEW_RENDER_POLICY_ALWAYS))
381      {
382         ERR("Invalid Render Policy.\n");
383         return EINA_FALSE;
384      }
385    if (wd->render_policy == policy) return EINA_TRUE;
386    wd->render_policy = policy;
387    _set_render_policy_callback(obj);
388    _glview_update_surface(obj);
389    return EINA_TRUE;
390 }
391
392 /**
393  * Sets the size of the glview
394  *
395  * @param obj The glview object
396  * @param width width of the glview object
397  * @param height height of the glview object
398  *
399  * @ingroup GLView
400  */
401 EAPI void
402 elm_glview_size_set(Evas_Object *obj, int width, int height)
403 {
404    ELM_CHECK_WIDTYPE(obj, widtype);
405    Widget_Data *wd = elm_widget_data_get(obj);
406    if (!wd) return;
407
408    if ((width == wd->w) && (height == wd->h)) return;
409    wd->w = width;
410    wd->h = height;
411    _glview_update_surface(obj);
412    elm_glview_changed_set(obj);
413 }
414
415 /**
416  * Gets the size of the glview.
417  *
418  * @param obj The glview object
419  * @param width width of the glview object
420  * @param height height of the glview object
421  *
422  * Note that this function returns the actual image size of the glview.
423  * This means that when the scale policy is set to ELM_GLVIEW_RESIZE_POLICY_SCALE,
424  * it'll return the non-scaled size.
425  *
426  * @ingroup GLView
427  */
428 EAPI void
429 elm_glview_size_get(const Evas_Object *obj, int *width, int *height)
430 {
431    ELM_CHECK_WIDTYPE(obj, widtype);
432    Widget_Data *wd = elm_widget_data_get(obj);
433    if (!wd) return;
434
435    if (width) *width = wd->w;
436    if (height) *height = wd->h;
437 }
438
439 /**
440  * Set the render function that runs in the main loop.
441  *
442  * @param obj The glview object.
443  * @param func The render function to be registered.
444  *
445  * @ingroup GLView
446  */
447 EAPI void
448 elm_glview_render_func_set(Evas_Object *obj, Elm_GLView_Func func)
449 {
450    ELM_CHECK_WIDTYPE(obj, widtype);
451    Widget_Data *wd = elm_widget_data_get(obj);
452    if (!wd) return;
453
454    wd->render_func = func;
455    _set_render_policy_callback(obj);
456 }
457
458 /**
459  * Notifies that there has been changes in the GLView.
460  *
461  * @param obj The glview object.
462  *
463  * @ingroup GLView
464  */
465 EAPI void
466 elm_glview_changed_set(Evas_Object *obj)
467 {
468    ELM_CHECK_WIDTYPE(obj, widtype);
469    Widget_Data *wd = elm_widget_data_get(obj);
470    if (!wd) return;
471
472    evas_object_image_pixels_dirty_set(wd->glview_image, EINA_TRUE);
473    if (wd->render_policy == ELM_GLVIEW_RENDER_POLICY_ALWAYS)
474      {
475         if (!wd->render_idle_enterer)
476           wd->render_idle_enterer = ecore_idle_enterer_before_add((Ecore_Task_Cb)_render_cb, obj);
477      }
478 }
479
480 /* vim:set ts=8 sw=3 sts=3 expandtab cino=>5n-2f0^-2{2(0W1st0 :*/