From: Hyoyoung Chang <hyoyoung@gmail.com>
[framework/uifw/elementary.git] / src / lib / elm_video.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 #ifdef HAVE_EMOTION
5 # include <Emotion.h>
6 #endif
7
8 /* TODO: add buffering support to Emotion and display buffering progression in the theme when needed */
9
10 typedef struct _Widget_Data Widget_Data;
11 struct _Widget_Data
12 {
13    Evas_Object *layout;
14    Evas_Object *emotion;
15
16    Ecore_Timer *timer;
17
18    Eina_Bool stop : 1;
19    Eina_Bool remember : 1;
20 };
21
22 #ifdef HAVE_EMOTION
23 static const char *widtype = NULL;
24
25 static const Evas_Smart_Cb_Description _signals[] = {
26   { NULL, NULL }
27 };
28
29 static void _del_hook(Evas_Object *obj);
30 static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl);
31 static void _theme_hook(Evas_Object *obj);
32 static void _sizing_eval(Evas_Object *obj);
33 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
34 static void _on_focus_hook(void *data, Evas_Object *obj);
35 static Eina_Bool _event_hook(Evas_Object *obj, Evas_Object *src,
36                              Evas_Callback_Type type, void *event_info);
37
38 static Eina_Bool
39 _event_hook(Evas_Object *obj, Evas_Object *src __UNUSED__, Evas_Callback_Type type, void *event_info)
40 {
41    if (type != EVAS_CALLBACK_KEY_DOWN) return EINA_FALSE;
42    Evas_Event_Key_Down *ev = event_info;
43    Widget_Data *wd = elm_widget_data_get(obj);
44    if (!wd) return EINA_FALSE;
45    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
46    if (elm_widget_disabled_get(obj)) return EINA_FALSE;
47    if ((!strcmp(ev->keyname, "Left")) || (!strcmp(ev->keyname, "KP_Left")))
48      {
49         double current, last;
50
51         current = elm_video_play_position_get(obj);
52         last = elm_video_play_length_get(obj);
53
54         if (current < last)
55           {
56              current += last / 100;
57              elm_video_play_position_set(obj, current);
58           }
59
60         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
61         return EINA_TRUE;
62      }
63    if ((!strcmp(ev->keyname, "Right")) || (!strcmp(ev->keyname, "KP_Right")))
64      {
65         double current, last;
66
67         current = elm_video_play_position_get(obj);
68         last = elm_video_play_length_get(obj);
69
70         if (current > 0)
71           {
72              current -= last / 100;
73              if (current < 0) current = 0;
74              elm_video_play_position_set(obj, current);
75           }
76
77         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
78         return EINA_TRUE;
79      }
80    if (!strcmp(ev->keyname, "space"))
81      {
82         if (elm_video_is_playing_get(obj))
83           elm_video_pause(obj);
84         else
85           elm_video_play(obj);
86         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
87         return EINA_TRUE;
88      }
89    fprintf(stderr, "keyname: '%s' not handle\n", ev->keyname);
90    return EINA_FALSE;
91 }
92
93 static void
94 _del_hook(Evas_Object *obj)
95 {
96    Widget_Data *wd = elm_widget_data_get(obj);
97
98    if (!wd) return;
99    if (wd->timer) ecore_timer_del(wd->timer);
100    free(wd);
101 }
102
103 static void
104 _on_focus_hook(void *data __UNUSED__, Evas_Object *obj)
105 {
106    Widget_Data *wd = elm_widget_data_get(obj);
107    if (!wd) return;
108    if (elm_widget_focus_get(obj))
109      {
110         edje_object_signal_emit(wd->layout, "elm,action,focus", "elm");
111         evas_object_focus_set(wd->layout, EINA_TRUE);
112      }
113    else
114      {
115         edje_object_signal_emit(wd->layout, "elm,action,unfocus", "elm");
116         evas_object_focus_set(wd->layout, EINA_FALSE);
117      }
118 }
119
120 static void
121 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
122 {
123    Widget_Data *wd = elm_widget_data_get(obj);
124    if (!wd) return;
125    edje_object_mirrored_set(wd->layout, rtl);
126 }
127
128 static void
129 _theme_hook(Evas_Object *obj)
130 {
131    Widget_Data *wd = elm_widget_data_get(obj);
132    if (!wd) return;
133    _elm_widget_mirrored_reload(obj);
134    _mirrored_set(obj, elm_widget_mirrored_get(obj));
135    _elm_theme_object_set(obj, wd->layout, "video", "base", elm_widget_style_get(obj));
136    edje_object_scale_set(wd->layout, elm_widget_scale_get(obj) *
137                          _elm_config->scale);
138    _sizing_eval(obj);
139 }
140
141 static void
142 _sizing_eval(Evas_Object *obj)
143 {
144    Widget_Data *wd = elm_widget_data_get(obj);
145    Evas_Coord minw = -1, minh = -1;
146    Evas_Coord w, h;
147
148    if (!wd) return;
149    evas_object_size_hint_request_get(wd->emotion, &minw, &minh);
150    evas_object_size_hint_aspect_set(wd->emotion, EVAS_ASPECT_CONTROL_BOTH, minw, minh);
151    edje_object_size_min_calc(wd->layout, &w, &h);
152
153    if (w != 0 && h != 0)
154      {
155         minw = w;
156         minh = h;
157      }
158    evas_object_size_hint_aspect_set(obj, EVAS_ASPECT_CONTROL_BOTH, minw, minh);
159 }
160
161 static void
162 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
163 {
164    _sizing_eval(data);
165 }
166
167 static void
168 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
169 {
170    Widget_Data *wd = elm_widget_data_get(obj);
171
172    if (wd->remember) emotion_object_last_position_save(wd->emotion);
173 }
174
175 static void
176 _open_done(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
177 {
178    Widget_Data *wd = elm_widget_data_get(data);
179
180    edje_object_signal_emit(wd->layout, "elm,video,open", "elm");
181 }
182
183 static void
184 _playback_started(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
185 {
186    Widget_Data *wd = elm_widget_data_get(data);
187
188    edje_object_signal_emit(wd->layout, "elm,video,play", "elm");
189 }
190
191 static void
192 _playback_finished(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
193 {
194    Widget_Data *wd = elm_widget_data_get(data);
195
196    edje_object_signal_emit(wd->layout, "elm,video,end", "elm");
197 }
198
199 static void
200 _update_aspect_ratio(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
201 {
202    _sizing_eval(data);
203 }
204
205 static void
206 _title_change(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
207 {
208    Widget_Data *wd = elm_widget_data_get(data);
209    const char *title;
210
211    title = emotion_object_title_get(wd->emotion);
212    edje_object_part_text_set(wd->layout, "elm,title", title);
213    edje_object_signal_emit(wd->layout, "elm,video,title", "elm");
214 }
215
216 static void
217 _audio_level_change(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
218 {
219    (void) data;
220 }
221
222 static Eina_Bool
223 _suspend_cb(void *data)
224 {
225    Widget_Data *wd = elm_widget_data_get(data);
226    double interval;
227
228    interval = ecore_timer_interval_get(wd->timer);
229    if (interval <= 20)
230      emotion_object_suspend_set(wd->emotion, EMOTION_SLEEP);
231    else if (interval <= 30)
232      emotion_object_suspend_set(wd->emotion, EMOTION_DEEP_SLEEP);
233    else
234      {
235         emotion_object_suspend_set(wd->emotion, EMOTION_HIBERNATE);
236         wd->timer = NULL;
237         return ECORE_CALLBACK_CANCEL;
238      }
239
240    ecore_timer_interval_set(wd->timer, interval + 10);
241    return ECORE_CALLBACK_RENEW;
242 }
243 #endif
244
245 Eina_Bool
246 _elm_video_check(Evas_Object *video)
247 {
248 #ifdef HAVE_EMOTION
249   ELM_CHECK_WIDTYPE(video, widtype) EINA_FALSE;
250   return EINA_TRUE;
251 #else
252   (void) video;
253   return EINA_FALSE;
254 #endif
255 }
256
257 EAPI Evas_Object *
258 elm_video_add(Evas_Object *parent)
259 {
260 #ifdef HAVE_EMOTION
261    Evas_Object *obj;
262    Evas *e;
263    Widget_Data *wd;
264
265    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
266    ELM_SET_WIDTYPE(widtype, "video");
267    elm_widget_type_set(obj, "video");
268    elm_widget_sub_object_add(parent, obj);
269    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
270    elm_widget_data_set(obj, wd);
271    elm_widget_del_hook_set(obj, _del_hook);
272    elm_widget_theme_hook_set(obj, _theme_hook);
273    elm_widget_can_focus_set(obj, EINA_TRUE);
274    elm_widget_event_hook_set(obj, _event_hook);
275
276    wd->stop = EINA_FALSE;
277    wd->remember = EINA_FALSE;
278
279    wd->layout = edje_object_add(e);
280    _elm_theme_object_set(obj, wd->layout, "video", "base", "default");
281    elm_widget_resize_object_set(obj, wd->layout);
282    evas_object_show(wd->layout);
283    evas_object_size_hint_weight_set(wd->layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
284
285    wd->emotion = emotion_object_add(e);
286    emotion_object_init(wd->emotion, NULL);
287    elm_widget_sub_object_add(obj, wd->emotion);
288    edje_object_part_swallow(wd->layout, "elm.swallow.video", wd->emotion);
289
290    evas_object_smart_callback_add(wd->emotion, "open_done", _open_done, obj);
291    evas_object_smart_callback_add(wd->emotion, "playback_started", _playback_started, obj);
292    evas_object_smart_callback_add(wd->emotion, "playback_finished", _playback_finished, obj);
293    evas_object_smart_callback_add(wd->emotion, "frame_resize", _update_aspect_ratio, obj);
294    evas_object_smart_callback_add(wd->emotion, "title_change", _title_change, obj);
295    evas_object_smart_callback_add(wd->emotion, "audio_level_change", _audio_level_change, obj);
296
297    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
298    evas_object_event_callback_add(obj, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _changed_size_hints, NULL);
299    evas_object_smart_callbacks_descriptions_set(obj, _signals);
300
301    _mirrored_set(obj, elm_widget_mirrored_get(obj));
302    _sizing_eval(obj);
303
304    wd->timer = ecore_timer_add(20.0, _suspend_cb, obj);
305
306    return obj;
307 #else
308    (void) parent;
309    return NULL;
310 #endif
311 }
312
313 EAPI Eina_Bool
314 elm_video_file_set(Evas_Object *video, const char *filename)
315 {
316 #ifdef HAVE_EMOTION
317    ELM_CHECK_WIDTYPE(video, widtype) EINA_FALSE;
318    Widget_Data *wd = elm_widget_data_get(video);
319
320    if (wd->remember) emotion_object_last_position_save(wd->emotion);
321    wd->stop = EINA_FALSE;
322    if (!emotion_object_file_set(wd->emotion, filename)) return EINA_FALSE;
323
324    if ((!strncmp(filename, "file://", 7)) || (!strstr(filename, "://")))
325      emotion_object_last_position_load(wd->emotion);
326
327    edje_object_signal_emit(wd->layout, "elm,video,load", "elm");
328
329    return EINA_TRUE;
330 #else
331    (void) video;
332    (void) filename;
333
334    return EINA_FALSE;
335 #endif
336 }
337
338 EINA_DEPRECATED EAPI void
339 elm_video_uri_set(Evas_Object *video, const char *uri)
340 {
341 #ifdef HAVE_EMOTION
342    ELM_CHECK_WIDTYPE(video, widtype);
343    Widget_Data *wd = elm_widget_data_get(video);
344
345    if (wd->remember) emotion_object_last_position_save(wd->emotion);
346    wd->stop = EINA_FALSE;
347    emotion_object_file_set(wd->emotion, uri);
348    edje_object_signal_emit(wd->layout, "elm,video,load", "elm");
349 #else
350    (void) video;
351    (void) uri;
352 #endif
353 }
354
355 EAPI Evas_Object *
356 elm_video_emotion_get(const Evas_Object *video)
357 {
358 #ifdef HAVE_EMOTION
359    ELM_CHECK_WIDTYPE(video, widtype) NULL;
360    Widget_Data *wd = elm_widget_data_get(video);
361
362    return wd->emotion;
363 #else
364    (void) video;
365    return NULL;
366 #endif
367 }
368
369 EAPI void
370 elm_video_play(Evas_Object *video)
371 {
372 #ifdef HAVE_EMOTION
373    ELM_CHECK_WIDTYPE(video, widtype);
374    Widget_Data *wd = elm_widget_data_get(video);
375
376    if (emotion_object_play_get(wd->emotion)) return ;
377
378    if (wd->timer) ecore_timer_del(wd->timer);
379    wd->timer = NULL;
380    wd->stop = EINA_FALSE;
381    emotion_object_play_set(wd->emotion, EINA_TRUE);
382 #else
383    (void) video;
384 #endif
385 }
386
387 /* FIXME: pause will setup timer and go into sleep or
388  * hibernate after a while without activity.
389  */
390
391 EAPI void
392 elm_video_pause(Evas_Object *video)
393 {
394 #ifdef HAVE_EMOTION
395    ELM_CHECK_WIDTYPE(video, widtype);
396    Widget_Data *wd = elm_widget_data_get(video);
397
398    if (!emotion_object_play_get(wd->emotion)) return ;
399
400    if (!wd->timer) wd->timer = ecore_timer_add(20.0, _suspend_cb, video);
401    emotion_object_play_set(wd->emotion, EINA_FALSE);
402    edje_object_signal_emit(wd->layout, "elm,video,pause", "elm");
403 #else
404    (void) video;
405 #endif
406 }
407
408 /* FIXME: stop should go into hibernate state directly.
409  */
410 EAPI void
411 elm_video_stop(Evas_Object *video)
412 {
413 #ifdef HAVE_EMOTION
414    ELM_CHECK_WIDTYPE(video, widtype);
415    Widget_Data *wd = elm_widget_data_get(video);
416
417    if (!emotion_object_play_get(wd->emotion) && wd->stop) return ;
418
419    if (wd->timer) ecore_timer_del(wd->timer);
420    wd->timer = NULL;
421    wd->stop = EINA_TRUE;
422    emotion_object_play_set(wd->emotion, EINA_FALSE);
423    edje_object_signal_emit(wd->layout, "elm,video,stop", "elm");
424    emotion_object_suspend_set(wd->emotion, EMOTION_HIBERNATE);
425 #else
426    (void) video;
427 #endif
428 }
429
430 EAPI Eina_Bool
431 elm_video_is_playing_get(const Evas_Object *video)
432 {
433 #ifdef HAVE_EMOTION
434    ELM_CHECK_WIDTYPE(video, widtype) EINA_FALSE;
435    Widget_Data *wd = elm_widget_data_get(video);
436
437    return emotion_object_play_get(wd->emotion);
438 #else
439    (void) video;
440    return EINA_FALSE;
441 #endif
442 }
443
444 EAPI Eina_Bool
445 elm_video_is_seekable_get(const Evas_Object *video)
446 {
447 #ifdef HAVE_EMOTION
448    ELM_CHECK_WIDTYPE(video, widtype) EINA_FALSE;
449    Widget_Data *wd = elm_widget_data_get(video);
450
451    return emotion_object_seekable_get(wd->emotion);
452 #else
453    (void) video;
454    return EINA_FALSE;
455 #endif
456 }
457
458 EAPI Eina_Bool
459 elm_video_audio_mute_get(const Evas_Object *video)
460 {
461 #ifdef HAVE_EMOTION
462    ELM_CHECK_WIDTYPE(video, widtype) EINA_FALSE;
463    Widget_Data *wd = elm_widget_data_get(video);
464
465    return emotion_object_audio_mute_get(wd->emotion);
466 #else
467    (void) video;
468    return EINA_FALSE;
469 #endif
470 }
471
472 EAPI void
473 elm_video_audio_mute_set(Evas_Object *video, Eina_Bool mute)
474 {
475 #ifdef HAVE_EMOTION
476    ELM_CHECK_WIDTYPE(video, widtype);
477    Widget_Data *wd = elm_widget_data_get(video);
478
479    emotion_object_audio_mute_set(wd->emotion, mute);
480 #else
481    (void) video;
482    (void) mute;
483 #endif
484 }
485
486 EAPI double
487 elm_video_audio_level_get(const Evas_Object *video)
488 {
489 #ifdef HAVE_EMOTION
490    ELM_CHECK_WIDTYPE(video, widtype) 0.0;
491    Widget_Data *wd = elm_widget_data_get(video);
492
493    return emotion_object_audio_volume_get(wd->emotion);
494 #else
495    (void) video;
496    return 0.0;
497 #endif
498 }
499
500 EAPI void
501 elm_video_audio_level_set(Evas_Object *video, double volume)
502 {
503 #ifdef HAVE_EMOTION
504    ELM_CHECK_WIDTYPE(video, widtype);
505    Widget_Data *wd = elm_widget_data_get(video);
506
507    emotion_object_audio_volume_set(wd->emotion, volume);
508 #else
509    (void) video;
510    (void) volume;
511 #endif
512 }
513
514 EAPI double
515 elm_video_play_position_get(const Evas_Object *video)
516 {
517 #ifdef HAVE_EMOTION
518    ELM_CHECK_WIDTYPE(video, widtype) 0.0;
519    Widget_Data *wd = elm_widget_data_get(video);
520
521    return emotion_object_position_get(wd->emotion);
522 #else
523    (void) video;
524    return 0.0;
525 #endif
526 }
527
528 EAPI void
529 elm_video_play_position_set(Evas_Object *video, double position)
530 {
531 #ifdef HAVE_EMOTION
532    ELM_CHECK_WIDTYPE(video, widtype);
533    Widget_Data *wd = elm_widget_data_get(video);
534
535    emotion_object_position_set(wd->emotion, position);
536 #else
537    (void) video;
538    (void) position;
539 #endif
540 }
541
542 EAPI double
543 elm_video_play_length_get(const Evas_Object *video)
544 {
545 #ifdef HAVE_EMOTION
546    ELM_CHECK_WIDTYPE(video, widtype) 0.0;
547    Widget_Data *wd = elm_widget_data_get(video);
548
549    return emotion_object_play_length_get(wd->emotion);
550 #else
551    (void) video;
552    return 0.0;
553 #endif
554 }
555
556 EAPI const char *
557 elm_video_title_get(const Evas_Object *video)
558 {
559 #ifdef HAVE_EMOTION
560    ELM_CHECK_WIDTYPE(video, widtype) NULL;
561    Widget_Data *wd = elm_widget_data_get(video);
562
563    return emotion_object_title_get(wd->emotion);
564 #else
565    (void) video;
566    return NULL;
567 #endif
568 }
569
570 EAPI void
571 elm_video_remember_position_set(Evas_Object *video, Eina_Bool remember)
572 {
573 #ifdef HAVE_EMOTION
574    ELM_CHECK_WIDTYPE(video, widtype);
575    Widget_Data *wd = elm_widget_data_get(video);
576
577    wd->remember = remember;
578 #else
579    (void) video;
580    (void) remember;
581 #endif
582 }
583
584 EAPI Eina_Bool
585 elm_video_remember_position_get(const Evas_Object *video)
586 {
587 #ifdef HAVE_EMOTION
588    ELM_CHECK_WIDTYPE(video, widtype) EINA_FALSE;
589    Widget_Data *wd = elm_widget_data_get(video);
590
591    return wd->remember;
592 #else
593    (void) video;
594    return EINA_FALSE;
595 #endif
596 }