Added per-window profile to support multi-head display.
[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 }
131
132 static Eina_Bool
133 _render_cb(void *obj)
134 {
135    Widget_Data *wd = elm_widget_data_get(obj);
136    if (!wd) return EINA_FALSE;
137
138    // Do a make current
139    if (!evas_gl_make_current(wd->evasgl, wd->surface, wd->context))
140      {
141         wd->render_idle_enterer = NULL;
142         ERR("Failed doing make current.\n");
143         return EINA_FALSE;
144      }
145
146    // Call the init function if it hasn't been called already
147    if (!wd->initialized)
148      {
149         if (wd->init_func) wd->init_func(obj);
150         wd->initialized = EINA_TRUE;
151      }
152
153    if (wd->resized)
154      {
155         if (wd->resize_func) wd->resize_func(obj);
156         wd->resized = EINA_FALSE;
157      }
158
159    // Call the render function
160    if (wd->render_func) wd->render_func(obj);
161
162    // Depending on the policy return true or false
163    if (wd->render_policy == ELM_GLVIEW_RENDER_POLICY_ON_DEMAND)
164      return EINA_TRUE;
165    else if (wd->render_policy == ELM_GLVIEW_RENDER_POLICY_ALWAYS)
166      {
167         // Return false so it only runs once
168         wd->render_idle_enterer = NULL;
169         return EINA_FALSE;
170      }
171    else
172      {
173         ERR("Invalid Render Policy.\n");
174         wd->render_idle_enterer = NULL;
175         return EINA_FALSE;
176      }
177    return EINA_TRUE;
178 }
179
180 static void
181 _set_render_policy_callback(Evas_Object *obj)
182 {
183    Widget_Data *wd = elm_widget_data_get(obj);
184
185    switch (wd->render_policy)
186      {
187       case ELM_GLVIEW_RENDER_POLICY_ON_DEMAND:
188          // Delete idle_enterer if it for some reason is around
189          if (wd->render_idle_enterer)
190            {
191               ecore_idle_enterer_del(wd->render_idle_enterer);
192               wd->render_idle_enterer = NULL;
193            }
194
195          // Set pixel getter callback
196          evas_object_image_pixels_get_callback_set
197             (wd->glview_image, (Evas_Object_Image_Pixels_Get_Cb)_render_cb, obj);
198          break;
199       case ELM_GLVIEW_RENDER_POLICY_ALWAYS:
200          // Unset the pixel getter callback if set already
201          evas_object_image_pixels_get_callback_set(wd->glview_image, NULL, NULL);
202
203          break;
204       default:
205          ERR("Invalid Render Policy.\n");
206          return;
207      }
208 }
209
210 EAPI Evas_Object *
211 elm_glview_add(Evas_Object *parent)
212 {
213    Evas_Object *obj;
214    Evas *e;
215    Widget_Data *wd;
216
217    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
218
219    ELM_SET_WIDTYPE(widtype, "glview");
220    elm_widget_type_set(obj, "glview");
221    elm_widget_sub_object_add(parent, obj);
222    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
223    elm_widget_data_set(obj, wd);
224    elm_widget_del_hook_set(obj, _del_hook);
225
226    // Evas_GL
227    wd->evasgl = evas_gl_new(e);
228    if (!wd->evasgl)
229      {
230         ERR("Failed Creating an Evas GL Object.\n");
231         return NULL;
232      }
233
234    // Create a default config
235    wd->config = evas_gl_config_new();
236    if (!wd->config)
237      {
238         ERR("Failed Creating a Config Object.\n");
239         evas_gl_free(wd->evasgl);
240         return NULL;
241      }
242    wd->config->color_format = EVAS_GL_RGB_888;
243
244    // Create image to render Evas_GL Surface
245    wd->glview_image = evas_object_image_filled_add(e);
246    evas_object_image_size_set(wd->glview_image, 1, 1);
247    evas_object_event_callback_add(wd->glview_image, EVAS_CALLBACK_RESIZE,
248                                   _glview_resize, obj);
249    elm_widget_resize_object_set(obj, wd->glview_image);
250    evas_object_show(wd->glview_image);
251
252    // Initialize variables
253    wd->mode                = 0;
254    wd->scale_policy        = ELM_GLVIEW_RESIZE_POLICY_RECREATE;
255    wd->render_policy       = ELM_GLVIEW_RENDER_POLICY_ON_DEMAND;
256    wd->surface             = NULL;
257
258    // Initialize it to (64,64)  (It's an arbitrary value)
259    wd->w                   = 64;
260    wd->h                   = 64;
261
262    // Initialize the rest of the values
263    wd->init_func           = NULL;
264    wd->del_func            = NULL;
265    wd->render_func         = NULL;
266    wd->render_idle_enterer = NULL;
267    wd->initialized         = EINA_FALSE;
268    wd->resized             = EINA_FALSE;
269
270    // Create Context
271    if (!wd->context)
272      {
273         wd->context = evas_gl_context_create(wd->evasgl, NULL);
274         if (!wd->context)
275           {
276              ERR("Error Creating an Evas_GL Context.\n");
277              return NULL;
278           }
279      }
280    return obj;
281 }
282
283 EAPI Evas_GL_API *
284 elm_glview_gl_api_get(const Evas_Object *obj)
285 {
286    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
287    Widget_Data *wd = elm_widget_data_get(obj);
288    if (!wd) return NULL;
289
290    return evas_gl_api_get(wd->evasgl);
291 }
292
293 EAPI Eina_Bool
294 elm_glview_mode_set(Evas_Object *obj, Elm_GLView_Mode mode)
295 {
296    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
297    Widget_Data *wd = elm_widget_data_get(obj);
298    if (!wd) return EINA_FALSE;
299
300    // Set the configs
301    if (mode & ELM_GLVIEW_ALPHA)
302       wd->config->color_format = EVAS_GL_RGBA_8888;
303    else
304       wd->config->color_format = EVAS_GL_RGB_888;
305
306    if (mode & ELM_GLVIEW_DEPTH)
307       wd->config->depth_bits = EVAS_GL_DEPTH_BIT_24;
308    else
309       wd->config->depth_bits = EVAS_GL_DEPTH_NONE;
310
311    if (mode & ELM_GLVIEW_STENCIL)
312       wd->config->stencil_bits = EVAS_GL_STENCIL_BIT_8;
313    else
314       wd->config->stencil_bits = EVAS_GL_STENCIL_NONE;
315
316    if (mode & ELM_GLVIEW_DIRECT)
317       wd->config->options_bits = EVAS_GL_OPTIONS_DIRECT;
318    else
319       wd->config->options_bits = EVAS_GL_OPTIONS_NONE;
320
321    // Check for Alpha Channel and enable it
322    if (mode & ELM_GLVIEW_ALPHA)
323      evas_object_image_alpha_set(wd->glview_image, EINA_TRUE);
324    else
325      evas_object_image_alpha_set(wd->glview_image, EINA_FALSE);
326
327    wd->mode = mode;
328
329    elm_glview_changed_set(obj);
330
331    return EINA_TRUE;
332 }
333
334 EAPI Eina_Bool
335 elm_glview_resize_policy_set(Evas_Object *obj, Elm_GLView_Resize_Policy policy)
336 {
337    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
338    Widget_Data *wd = elm_widget_data_get(obj);
339    if (!wd) return EINA_FALSE;
340
341    if (policy == wd->scale_policy) return EINA_TRUE;
342    switch (policy)
343      {
344       case ELM_GLVIEW_RESIZE_POLICY_RECREATE:
345       case ELM_GLVIEW_RESIZE_POLICY_SCALE:
346          wd->scale_policy = policy;
347          _glview_update_surface(obj);
348          elm_glview_changed_set(obj);
349          return EINA_TRUE;
350       default:
351          ERR("Invalid Scale Policy.\n");
352          return EINA_FALSE;
353      }
354 }
355
356 EAPI Eina_Bool
357 elm_glview_render_policy_set(Evas_Object *obj, Elm_GLView_Render_Policy policy)
358 {
359    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
360    Widget_Data *wd = elm_widget_data_get(obj);
361    if (!wd) return EINA_FALSE;
362
363    if ((policy != ELM_GLVIEW_RENDER_POLICY_ON_DEMAND) &&
364        (policy != ELM_GLVIEW_RENDER_POLICY_ALWAYS))
365      {
366         ERR("Invalid Render Policy.\n");
367         return EINA_FALSE;
368      }
369    if (wd->render_policy == policy) return EINA_TRUE;
370    wd->render_policy = policy;
371    _set_render_policy_callback(obj);
372    _glview_update_surface(obj);
373    return EINA_TRUE;
374 }
375
376 EAPI void
377 elm_glview_size_set(Evas_Object *obj, int w, int h)
378 {
379    ELM_CHECK_WIDTYPE(obj, widtype);
380    Widget_Data *wd = elm_widget_data_get(obj);
381    if (!wd) return;
382
383    if ((w == wd->w) && (h == wd->h)) return;
384    wd->w = w;
385    wd->h = h;
386    _glview_update_surface(obj);
387    elm_glview_changed_set(obj);
388 }
389
390 EAPI void
391 elm_glview_size_get(const Evas_Object *obj, int *w, int *h)
392 {
393    ELM_CHECK_WIDTYPE(obj, widtype);
394    Widget_Data *wd = elm_widget_data_get(obj);
395    if (!wd) return;
396
397    if (w) *w = wd->w;
398    if (h) *h = wd->h;
399 }
400
401 EAPI void
402 elm_glview_init_func_set(Evas_Object *obj, Elm_GLView_Func_Cb func)
403 {
404    ELM_CHECK_WIDTYPE(obj, widtype);
405    Widget_Data *wd = elm_widget_data_get(obj);
406    if (!wd) return;
407
408    wd->initialized = EINA_FALSE;
409    wd->init_func = func;
410 }
411
412 EAPI void
413 elm_glview_del_func_set(Evas_Object *obj, Elm_GLView_Func_Cb func)
414 {
415    ELM_CHECK_WIDTYPE(obj, widtype);
416    Widget_Data *wd = elm_widget_data_get(obj);
417     if (!wd) return;
418
419    wd->del_func = func;
420 }
421
422 EAPI void
423 elm_glview_resize_func_set(Evas_Object *obj, Elm_GLView_Func_Cb func)
424 {
425    ELM_CHECK_WIDTYPE(obj, widtype);
426    Widget_Data *wd = elm_widget_data_get(obj);
427     if (!wd)
428      {
429         ERR("Invalid Widget Object.\n");
430         return;
431      }
432
433    wd->resize_func = func;
434 }
435
436 EAPI void
437 elm_glview_render_func_set(Evas_Object *obj, Elm_GLView_Func_Cb func)
438 {
439    ELM_CHECK_WIDTYPE(obj, widtype);
440    Widget_Data *wd = elm_widget_data_get(obj);
441    if (!wd) return;
442
443    wd->render_func = func;
444    _set_render_policy_callback(obj);
445 }
446
447 EAPI void
448 elm_glview_changed_set(Evas_Object *obj)
449 {
450    ELM_CHECK_WIDTYPE(obj, widtype);
451    Widget_Data *wd = elm_widget_data_get(obj);
452    if (!wd) return;
453
454    evas_object_image_pixels_dirty_set(wd->glview_image, EINA_TRUE);
455    if (wd->render_policy == ELM_GLVIEW_RENDER_POLICY_ALWAYS)
456      {
457         if (!wd->render_idle_enterer)
458           wd->render_idle_enterer = ecore_idle_enterer_before_add((Ecore_Task_Cb)_render_cb, obj);
459      }
460 }
461
462 /* vim:set ts=8 sw=3 sts=3 expandtab cino=>5n-3f0^-2{2(0W1st0 :*/