#ifdef HAVE_CONFIG_H # include "elementary_config.h" #endif #include #include "elm_priv.h" #include "elm_widget_layout.h" #include "elm_widget_player.h" #ifdef HAVE_EMOTION # include #endif EAPI Eo_Op ELM_OBJ_PLAYER_BASE_ID = EO_NOOP; #define MY_CLASS ELM_OBJ_PLAYER_CLASS #define MY_CLASS_NAME "elm_player" static const char SIG_FORWARD_CLICKED[] = "forward,clicked"; static const char SIG_INFO_CLICKED[] = "info,clicked"; static const char SIG_NEXT_CLICKED[] = "next,clicked"; static const char SIG_PAUSE_CLICKED[] = "pause,clicked"; static const char SIG_PLAY_CLICKED[] = "play,clicked"; static const char SIG_PREV_CLICKED[] = "prev,clicked"; static const char SIG_REWIND_CLICKED[] = "rewind,clicked"; static const char SIG_STOP_CLICKED[] = "stop,clicked"; static const Evas_Smart_Cb_Description _smart_callbacks[] = { { SIG_FORWARD_CLICKED, "" }, { SIG_INFO_CLICKED, "" }, { SIG_NEXT_CLICKED, "" }, { SIG_PAUSE_CLICKED, "" }, { SIG_PLAY_CLICKED, "" }, { SIG_PREV_CLICKED, "" }, { SIG_REWIND_CLICKED, "" }, { SIG_STOP_CLICKED, "" }, { NULL, NULL } }; #ifdef HAVE_EMOTION static void _elm_player_smart_event(Eo *obj, void *_pd, va_list *list) { Evas_Object *src = va_arg(*list, Evas_Object *); (void) src; Evas_Callback_Type type = va_arg(*list, Evas_Callback_Type); void *event_info = va_arg(*list, void *); Eina_Bool *ret = va_arg(*list, Eina_Bool *); if (ret) *ret = EINA_FALSE; Evas_Event_Key_Down *ev = event_info; Elm_Player_Smart_Data *sd = _pd; if (elm_widget_disabled_get(obj)) return; if (type != EVAS_CALLBACK_KEY_DOWN) return; if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return; if (!sd->video) return; if ((!strcmp(ev->keyname, "Left")) || ((!strcmp(ev->keyname, "KP_Left")) && (!ev->string))) { double current, last; current = elm_video_play_position_get(sd->video); last = elm_video_play_length_get(sd->video); if (current < last) { current -= last / 100; elm_video_play_position_set(sd->video, current); } ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD; if (ret) *ret = EINA_TRUE; return; } if ((!strcmp(ev->keyname, "Right")) || ((!strcmp(ev->keyname, "KP_Right")) && (!ev->string))) { double current, last; current = elm_video_play_position_get(sd->video); last = elm_video_play_length_get(sd->video); if (current > 0) { current += last / 100; if (current < 0) current = 0; elm_video_play_position_set(sd->video, current); } ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD; if (ret) *ret = EINA_TRUE; return; } if (!strcmp(ev->keyname, "space")) { if (elm_video_is_playing_get(sd->video)) elm_video_pause(sd->video); else elm_video_play(sd->video); ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD; if (ret) *ret = EINA_TRUE; return; } fprintf(stderr, "keyname: '%s' not handle\n", ev->keyname); } static void _elm_player_smart_theme(Eo *obj, void *_pd, va_list *list) { Eina_Bool *ret = va_arg(*list, Eina_Bool *); if (ret) *ret = EINA_FALSE; Eina_Bool int_ret; Elm_Player_Smart_Data *sd = _pd; eo_do_super(obj, MY_CLASS, elm_wdg_theme(&int_ret)); if (!int_ret) return; #define UPDATE_THEME(Target, Name) \ if (Target) \ { \ elm_object_style_set(Target, elm_widget_style_get(obj)); \ if (!elm_layout_content_set(obj, Name, Target)) \ evas_object_hide(Target); \ elm_object_disabled_set(Target, elm_widget_disabled_get(obj)); \ } UPDATE_THEME(sd->forward, "media_player/forward"); UPDATE_THEME(sd->info, "media_player/info"); UPDATE_THEME(sd->next, "media_player/next"); UPDATE_THEME(sd->pause, "media_player/pause"); UPDATE_THEME(sd->play, "media_player/play"); UPDATE_THEME(sd->prev, "media_player/prev"); UPDATE_THEME(sd->rewind, "media_player/rewind"); UPDATE_THEME(sd->next, "media_player/next"); UPDATE_THEME(sd->slider, "media_player/slider"); elm_layout_sizing_eval(obj); if (ret) *ret = EINA_TRUE; } static void _elm_player_smart_sizing_eval(Eo *obj, void *_pd EINA_UNUSED, va_list *list EINA_UNUSED) { Evas_Coord w, h; Elm_Widget_Smart_Data *wd = eo_data_scope_get(obj, ELM_OBJ_WIDGET_CLASS); edje_object_size_min_get(wd->resize_obj, &w, &h); edje_object_size_min_restricted_calc (wd->resize_obj, &w, &h, w, h); evas_object_size_hint_min_set(obj, w, h); } static void _update_slider(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) { double pos, length; Eina_Bool seekable; ELM_PLAYER_DATA_GET(data, sd); if (!sd) return; seekable = elm_video_is_seekable_get(sd->video); length = elm_video_play_length_get(sd->video); pos = elm_video_play_position_get(sd->video); elm_object_disabled_set(sd->slider, !seekable); elm_slider_min_max_set(sd->slider, 0, length); elm_slider_value_set(sd->slider, pos); sd->last_update_time = ecore_loop_time_get(); ELM_SAFE_FREE(sd->delay_update, ecore_timer_del); } static Eina_Bool _update_delay(void *data) { ELM_PLAYER_DATA_GET(data, sd); if (!sd) return EINA_FALSE; sd->delay_update = NULL; _update_slider(data, NULL, NULL); return EINA_FALSE; } static void _update_frame(void *data, Evas_Object *obj, void *event_info) { ELM_PLAYER_DATA_GET(data, sd); if (!sd) return; if ((ecore_loop_time_get() - sd->last_update_time) < 0.25) { if (sd->delay_update) ecore_timer_del(sd->delay_update); sd->delay_update = ecore_timer_add(0.30, _update_delay, data); return; } _update_slider(data, obj, event_info); } static void _update_position(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) { ELM_PLAYER_DATA_GET(data, sd); elm_video_play_position_set(sd->video, elm_slider_value_get(sd->slider)); } static void _forward(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) { double pos, length; ELM_PLAYER_DATA_GET(data, sd); pos = elm_video_play_position_get(sd->video); length = elm_video_play_length_get(sd->video); pos += length * 0.3; elm_video_play_position_set(sd->video, pos); elm_layout_signal_emit(data, "elm,button,forward", "elm"); evas_object_smart_callback_call(data, SIG_FORWARD_CLICKED, NULL); } static void _info(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) { elm_layout_signal_emit(data, "elm,button,info", "elm"); evas_object_smart_callback_call(data, SIG_INFO_CLICKED, NULL); } static void _next(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) { double pos, length; ELM_PLAYER_DATA_GET(data, sd); pos = elm_video_play_position_get(sd->video); length = elm_video_play_length_get(sd->video); pos += length * 0.1; elm_video_play_position_set(sd->video, pos); elm_layout_signal_emit(data, "elm,button,next", "elm"); evas_object_smart_callback_call(data, SIG_NEXT_CLICKED, NULL); } static void _pause(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) { ELM_PLAYER_DATA_GET(data, sd); elm_layout_signal_emit(data, "elm,player,pause", "elm"); elm_video_pause(sd->video); elm_layout_signal_emit(data, "elm,button,pause", "elm"); evas_object_smart_callback_call(data, SIG_PAUSE_CLICKED, NULL); } static void _play(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) { ELM_PLAYER_DATA_GET(data, sd); elm_layout_signal_emit(data, "elm,player,play", "elm"); elm_video_play(sd->video); elm_layout_signal_emit(data, "elm,button,play", "elm"); evas_object_smart_callback_call(data, SIG_PLAY_CLICKED, NULL); } static void _prev(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) { double pos, length; ELM_PLAYER_DATA_GET(data, sd); pos = elm_video_play_position_get(sd->video); length = elm_video_play_length_get(sd->video); pos -= length * 0.1; elm_video_play_position_set(sd->video, pos); evas_object_smart_callback_call(data, SIG_PREV_CLICKED, NULL); elm_layout_signal_emit(data, "elm,button,prev", "elm"); } static void _rewind(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) { ELM_PLAYER_DATA_GET(data, sd); elm_video_play_position_set(sd->video, 0); elm_layout_signal_emit(data, "elm,button,rewind", "elm"); evas_object_smart_callback_call(data, SIG_REWIND_CLICKED, NULL); } static void _stop(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) { elm_layout_signal_emit(data, "elm,button,stop", "elm"); evas_object_smart_callback_call(data, SIG_STOP_CLICKED, NULL); } static void _play_started(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) { elm_layout_signal_emit(data, "elm,player,play", "elm"); } static void _play_finished(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) { elm_layout_signal_emit(data, "elm,player,pause", "elm"); } static void _on_video_del(Elm_Player_Smart_Data *sd) { elm_object_disabled_set(sd->slider, EINA_TRUE); elm_object_disabled_set(sd->forward, EINA_TRUE); elm_object_disabled_set(sd->info, EINA_TRUE); elm_object_disabled_set(sd->next, EINA_TRUE); elm_object_disabled_set(sd->pause, EINA_TRUE); elm_object_disabled_set(sd->play, EINA_TRUE); elm_object_disabled_set(sd->prev, EINA_TRUE); elm_object_disabled_set(sd->rewind, EINA_TRUE); elm_object_disabled_set(sd->next, EINA_TRUE); sd->video = NULL; sd->emotion = NULL; } static void _video_del(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) { _on_video_del(data); } static Evas_Object * _player_button_add(Evas_Object *obj, const char *name, Evas_Smart_Cb func) { Evas_Object *ic; Evas_Object *bt; ic = elm_icon_add(obj); elm_icon_standard_set(ic, name); evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1); bt = elm_button_add(obj); elm_widget_mirrored_automatic_set(bt, EINA_FALSE); elm_object_part_content_set(bt, "icon", ic); evas_object_size_hint_align_set(bt, 0.0, 0.0); elm_object_style_set(bt, "anchor"); evas_object_smart_callback_add(bt, "clicked", func, obj); if (!elm_layout_content_set(obj, name, bt)) { elm_widget_sub_object_add(obj, bt); evas_object_hide(bt); } return bt; } static char * _double_to_time(double value) { char buf[256]; int ph, pm, ps, pf; ph = value / 3600; pm = value / 60 - (ph * 60); ps = value - (pm * 60) - (ph * 3600); pf = value * 100 - (ps * 100) - (pm * 60 * 100) - (ph * 60 * 60 * 100); if (ph) snprintf(buf, sizeof(buf), "%i:%02i:%02i.%02i", ph, pm, ps, pf); else if (pm) snprintf(buf, sizeof(buf), "%02i:%02i.%02i", pm, ps, pf); else snprintf(buf, sizeof(buf), "%02i.%02i", ps, pf); return (char *)eina_stringshare_add(buf); } static void _str_free(char *data) { eina_stringshare_del(data); } /* a video object is never parented by a player one, just tracked. * treating this special case here and delegating other objects to own * layout */ static void _elm_player_smart_content_set(Eo *obj, void *_pd, va_list *list) { const char *part = va_arg(*list, const char *); Evas_Object *content = va_arg(*list, Evas_Object *); Eina_Bool *ret = va_arg(*list, Eina_Bool *); if (ret) *ret = EINA_FALSE; Eina_Bool int_ret = EINA_FALSE; double pos, length; Eina_Bool seekable; if (part && strcmp(part, "video")) { eo_do_super(obj, MY_CLASS, elm_obj_container_content_set(part, content, &int_ret)); if (ret) *ret = int_ret; return; } Elm_Player_Smart_Data *sd = _pd; if (!_elm_video_check(content)) return; if (sd->video == content) goto end; if (sd->video) evas_object_del(sd->video); sd->video = content; if (!content) goto end; elm_object_disabled_set(sd->slider, EINA_FALSE); elm_object_disabled_set(sd->forward, EINA_FALSE); elm_object_disabled_set(sd->info, EINA_FALSE); elm_object_disabled_set(sd->next, EINA_FALSE); elm_object_disabled_set(sd->pause, EINA_FALSE); elm_object_disabled_set(sd->play, EINA_FALSE); elm_object_disabled_set(sd->prev, EINA_FALSE); elm_object_disabled_set(sd->rewind, EINA_FALSE); elm_object_disabled_set(sd->next, EINA_FALSE); sd->emotion = elm_video_emotion_get(sd->video); emotion_object_priority_set(sd->emotion, EINA_TRUE); evas_object_event_callback_add (sd->video, EVAS_CALLBACK_DEL, _video_del, sd); seekable = elm_video_is_seekable_get(sd->video); length = elm_video_play_length_get(sd->video); pos = elm_video_play_position_get(sd->video); elm_object_disabled_set(sd->slider, !seekable); elm_slider_min_max_set(sd->slider, 0, length); elm_slider_value_set(sd->slider, pos); if (elm_video_is_playing_get(sd->video)) elm_layout_signal_emit(obj, "elm,player,play", "elm"); else elm_layout_signal_emit(obj, "elm,player,pause", "elm"); evas_object_smart_callback_add(sd->emotion, "frame_decode", _update_frame, obj); evas_object_smart_callback_add(sd->emotion, "frame_resize", _update_slider, obj); evas_object_smart_callback_add(sd->emotion, "length_change", _update_slider, obj); evas_object_smart_callback_add(sd->emotion, "position_update", _update_frame, obj); evas_object_smart_callback_add(sd->emotion, "playback_started", _play_started, obj); evas_object_smart_callback_add(sd->emotion, "playback_finished", _play_finished, obj); /* FIXME: track info from video */ end: if (ret) *ret = EINA_TRUE; } static void _elm_player_smart_add(Eo *obj, void *_pd, va_list *list EINA_UNUSED) { eo_do_super(obj, MY_CLASS, evas_obj_smart_add()); Elm_Player_Smart_Data *priv = _pd; if (!elm_layout_theme_set(obj, "player", "base", elm_widget_style_get(obj))) CRITICAL("Failed to set layout!"); priv->forward = _player_button_add(obj, "media_player/forward", _forward); priv->info = _player_button_add(obj, "media_player/info", _info); priv->next = _player_button_add(obj, "media_player/next", _next); priv->pause = _player_button_add(obj, "media_player/pause", _pause); priv->play = _player_button_add(obj, "media_player/play", _play); priv->prev = _player_button_add(obj, "media_player/prev", _prev); priv->rewind = _player_button_add(obj, "media_player/rewind", _rewind); priv->stop = _player_button_add(obj, "media_player/stop", _stop); priv->slider = elm_slider_add(obj); elm_slider_indicator_format_function_set (priv->slider, _double_to_time, _str_free); elm_slider_units_format_function_set (priv->slider, _double_to_time, _str_free); elm_slider_min_max_set(priv->slider, 0, 0); elm_slider_value_set(priv->slider, 0); elm_object_disabled_set(priv->slider, EINA_TRUE); evas_object_size_hint_align_set(priv->slider, EVAS_HINT_FILL, 0.5); evas_object_size_hint_weight_set (priv->slider, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); elm_layout_content_set(obj, "media_player/slider", priv->slider); evas_object_smart_callback_add (priv->slider, "changed", _update_position, obj); elm_layout_sizing_eval(obj); elm_widget_can_focus_set(obj, EINA_TRUE); } static void _elm_player_smart_del(Eo *obj, void *_pd, va_list *list EINA_UNUSED) { Elm_Player_Smart_Data *sd = _pd; if (sd->delay_update) ecore_timer_del(sd->delay_update); eo_do_super(obj, MY_CLASS, evas_obj_smart_del()); } #endif EAPI Evas_Object * elm_player_add(Evas_Object *parent) { #ifdef HAVE_EMOTION EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL); Evas_Object *obj = eo_add(MY_CLASS, parent); eo_unref(obj); return obj; #else (void) parent; return NULL; #endif } static void _constructor(Eo *obj, void *_pd EINA_UNUSED, va_list *list EINA_UNUSED) { #ifdef HAVE_EMOTION eo_do_super(obj, MY_CLASS, eo_constructor()); eo_do(obj, evas_obj_type_set(MY_CLASS_NAME), evas_obj_smart_callbacks_descriptions_set(_smart_callbacks, NULL)); elm_widget_sub_object_add(eo_parent_get(obj), obj); #else eo_error_set(obj); #endif } static void _class_constructor(Eo_Class *klass) { const Eo_Op_Func_Description func_desc[] = { EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_CONSTRUCTOR), _constructor), #ifdef HAVE_EMOTION EO_OP_FUNC(EVAS_OBJ_SMART_ID(EVAS_OBJ_SMART_SUB_ID_ADD), _elm_player_smart_add), EO_OP_FUNC(EVAS_OBJ_SMART_ID(EVAS_OBJ_SMART_SUB_ID_DEL), _elm_player_smart_del), EO_OP_FUNC(ELM_WIDGET_ID(ELM_WIDGET_SUB_ID_THEME), _elm_player_smart_theme), EO_OP_FUNC(ELM_WIDGET_ID(ELM_WIDGET_SUB_ID_EVENT), _elm_player_smart_event), EO_OP_FUNC(ELM_OBJ_CONTAINER_ID(ELM_OBJ_CONTAINER_SUB_ID_CONTENT_SET), _elm_player_smart_content_set), EO_OP_FUNC(ELM_OBJ_LAYOUT_ID(ELM_OBJ_LAYOUT_SUB_ID_SIZING_EVAL), _elm_player_smart_sizing_eval), #endif EO_OP_FUNC_SENTINEL }; eo_class_funcs_set(klass, func_desc); evas_smart_legacy_type_register(MY_CLASS_NAME, klass); } static const Eo_Op_Description op_desc[] = { EO_OP_DESCRIPTION_SENTINEL }; static const Eo_Class_Description class_desc = { EO_VERSION, MY_CLASS_NAME, EO_CLASS_TYPE_REGULAR, EO_CLASS_DESCRIPTION_OPS(&ELM_OBJ_PLAYER_BASE_ID, op_desc, ELM_OBJ_PLAYER_SUB_ID_LAST), NULL, sizeof(Elm_Player_Smart_Data), _class_constructor, NULL }; EO_DEFINE_CLASS(elm_obj_player_class_get, &class_desc, ELM_OBJ_LAYOUT_CLASS, NULL);