From 1f319885ef4e486ae291b1d1f50771e43e4351f2 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Cl=C3=A9ment=20Stenac?= Date: Tue, 6 Dec 2005 19:09:20 +0000 Subject: [PATCH] Interaction facility (Refs:#27) --- include/vlc_interaction.h | 53 ++++-- include/vlc_interface.h | 3 +- include/vlc_playlist.h | 1 - include/vlc_symbols.h | 16 +- src/input/input.c | 4 +- src/interface/interaction.c | 329 +++++++++++++++++++++++++++--------- 6 files changed, 303 insertions(+), 103 deletions(-) diff --git a/include/vlc_interaction.h b/include/vlc_interaction.h index 7fabfd27ab..208239bfac 100644 --- a/include/vlc_interaction.h +++ b/include/vlc_interaction.h @@ -58,10 +58,36 @@ struct interaction_dialog_t vlc_bool_t b_have_answer; //< Has an answer been given ? vlc_bool_t b_reusable; //< Do we have to reuse this ? + vlc_bool_t b_updated; //< Update for this one ? + vlc_bool_t b_finished; //< Hidden by interface void * p_private; //< Private interface data }; +/** + * Possible interaction types + */ +enum +{ + INTERACT_PROGRESS, //< Progress bar + INTERACT_WARNING, //< Warning message ("codec not supported") + INTERACT_FATAL, //< Fatal message ("File not found") + INTERACT_FATAL_LIST, //< List of fatal messages ("File not found") + INTERACT_ASK, //< Full-featured dialog box (password) +}; + +/** + * Predefined reusable dialogs + */ +enum +{ + DIALOG_NOACCESS, + DIALOG_NOCODEC, + DIALOG_NOAUDIO, + + DIALOG_LAST_PREDEFINED, +}; + /** * This structure contains the active interaction dialogs, and is * used by teh manager @@ -72,25 +98,30 @@ struct interaction_t int i_dialogs; //< Number of dialogs interaction_dialog_t **pp_dialogs; //< Dialogs -}; + intf_thread_t *p_intf; //< Interface to use + + int i_last_id; //< Last attributed ID +}; /** - * Possible interaction types + * Possible actions */ enum { - INTERACT_PROGRESS, //< Progress bar - INTERACT_WARNING, //< Warning message ("codec not supported") - INTERACT_FATAL, //< Fatal message ("File not found") - INTERACT_ASK, //< Full-featured dialog box (password) + INTERACT_NEW, + INTERACT_UPDATE, + INTERACT_HIDE }; +/*************************************************************************** + * Exported symbols + ***************************************************************************/ + #define intf_Interact( a,b ) __intf_Interact( VLC_OBJECT(a), b ) VLC_EXPORT( int,__intf_Interact,( vlc_object_t *,interaction_dialog_t * ) ); -#if 0 -VLC_NO_EXPORT_YET( int,__intf_InteractionManage,( playlist_t *) ); -#endif - -VLC_EXPORT( void, intf_UserFatal,( vlc_object_t*, const char*, const char*, ...)); +#define intf_UserFatal( a,b, c, d, e... ) __intf_UserFatal( a,b,c,d, ## e ) +VLC_EXPORT( void, __intf_UserFatal,( vlc_object_t*, int, const char*, const char*, ...) ); +VLC_EXPORT( void, intf_InteractionManage,( playlist_t *) ); +VLC_EXPORT( void, intf_InteractionDestroy,( interaction_t *) ); diff --git a/include/vlc_interface.h b/include/vlc_interface.h index 533aff28d3..cbac6828bb 100644 --- a/include/vlc_interface.h +++ b/include/vlc_interface.h @@ -66,8 +66,7 @@ struct intf_thread_t intf_dialog_args_t * ); /** Interaction stuff */ - int i_last_id; - int ( *pf_interact ) ( intf_thread_t *, interaction_dialog_t *, vlc_bool_t ); + int ( *pf_interact ) ( intf_thread_t *, interaction_dialog_t *, int ); /** Video window callbacks */ void * ( *pf_request_window ) ( intf_thread_t *, vout_thread_t *, diff --git a/include/vlc_playlist.h b/include/vlc_playlist.h index 3bd4ebb304..d8256b30c8 100644 --- a/include/vlc_playlist.h +++ b/include/vlc_playlist.h @@ -216,7 +216,6 @@ struct playlist_t // The following members are about user interaction // The playlist manages the user interaction to avoid creating another // thread - vlc_bool_t b_manage_interaction; interaction_t *p_interaction; /*@}*/ diff --git a/include/vlc_symbols.h b/include/vlc_symbols.h index 957eb21a95..b3f6642365 100644 --- a/include/vlc_symbols.h +++ b/include/vlc_symbols.h @@ -121,6 +121,7 @@ char* httpd_ClientIP (httpd_client_t *cl, char *psz_ip); void httpd_FileDelete (httpd_file_t *); module_t * __module_Need (vlc_object_t *, const char *, const char *, vlc_bool_t); const char * VLC_Changeset (void); +void intf_InteractionDestroy (interaction_t *); void LocaleFree (const char *); void __vlc_object_attach (vlc_object_t *, vlc_object_t *); stream_t * __stream_UrlNew (vlc_object_t *p_this, const char *psz_url); @@ -296,6 +297,7 @@ subpicture_t * spu_CreateSubpicture (spu_t *); void httpd_MsgAdd (httpd_message_t *, char *psz_name, char *psz_value, ...); int vout_vaControlDefault (vout_thread_t *, int, va_list); int playlist_NodeEmpty (playlist_t *, playlist_item_t *, vlc_bool_t); +void __intf_UserFatal (vlc_object_t*, int, const char*, const char*, ...); spu_t * __spu_Create (vlc_object_t *); int playlist_NodeRemoveItem (playlist_t *,playlist_item_t*,playlist_item_t *); int __net_Accept (vlc_object_t *, int *, mtime_t); @@ -325,6 +327,7 @@ int playlist_PreparseEnqueue (playlist_t *, input_item_t *); aout_buffer_t * aout_FifoPop (aout_instance_t * p_aout, aout_fifo_t * p_fifo); int __vout_InitPicture (vlc_object_t *p_this, picture_t *p_pic, uint32_t i_chroma, int i_width, int i_height, int i_aspect); int playlist_LockClear (playlist_t *); +void intf_InteractionManage (playlist_t *); char * mstrtime (char *psz_buffer, mtime_t date); void aout_FormatPrepare (audio_sample_format_t * p_format); void spu_DisplaySubpicture (spu_t *, subpicture_t *); @@ -395,7 +398,6 @@ stream_t * __stream_MemoryNew (vlc_object_t *p_obj, uint8_t *p_buffer, int64_t i void mwait (mtime_t date); void __config_ResetAll (vlc_object_t *); httpd_redirect_t * httpd_RedirectNew (httpd_host_t *, const char *psz_url_dst, const char *psz_url_src); -void intf_UserFatal (vlc_object_t*, const char*, const char*, ...); playlist_item_t * playlist_LockItemGetById (playlist_t *, int); mtime_t date_Get (const date_t *); int aout_DecPlay (aout_instance_t *, aout_input_t *, aout_buffer_t *); @@ -854,7 +856,9 @@ struct module_symbols_t void (*__input_Read_inner) (vlc_object_t *, input_item_t *, vlc_bool_t); int (*__net_ConnectUDP_inner) (vlc_object_t *p_this, const char *psz_host, int i_port, int hlim); int (*__intf_Interact_inner) (vlc_object_t *,interaction_dialog_t *); - void (*intf_UserFatal_inner) (vlc_object_t*, const char*, const char*, ...); + void (*intf_InteractionManage_inner) (playlist_t *); + void (*intf_InteractionDestroy_inner) (interaction_t *); + void (*__intf_UserFatal_inner) (vlc_object_t*, int, const char*, const char*, ...); }; # if defined (__PLUGIN__) # define aout_FiltersCreatePipeline (p_symbols)->aout_FiltersCreatePipeline_inner @@ -1267,7 +1271,9 @@ struct module_symbols_t # define __input_Read (p_symbols)->__input_Read_inner # define __net_ConnectUDP (p_symbols)->__net_ConnectUDP_inner # define __intf_Interact (p_symbols)->__intf_Interact_inner -# define intf_UserFatal (p_symbols)->intf_UserFatal_inner +# define intf_InteractionManage (p_symbols)->intf_InteractionManage_inner +# define intf_InteractionDestroy (p_symbols)->intf_InteractionDestroy_inner +# define __intf_UserFatal (p_symbols)->__intf_UserFatal_inner # elif defined (HAVE_DYNAMIC_PLUGINS) && !defined (__BUILTIN__) /****************************************************************** * STORE_SYMBOLS: store VLC APIs into p_symbols for plugin access. @@ -1683,7 +1689,9 @@ struct module_symbols_t ((p_symbols)->__input_Read_inner) = __input_Read; \ ((p_symbols)->__net_ConnectUDP_inner) = __net_ConnectUDP; \ ((p_symbols)->__intf_Interact_inner) = __intf_Interact; \ - ((p_symbols)->intf_UserFatal_inner) = intf_UserFatal; \ + ((p_symbols)->intf_InteractionManage_inner) = intf_InteractionManage; \ + ((p_symbols)->intf_InteractionDestroy_inner) = intf_InteractionDestroy; \ + ((p_symbols)->__intf_UserFatal_inner) = __intf_UserFatal; \ (p_symbols)->net_ConvertIPv4_deprecated = NULL; \ # endif /* __PLUGIN__ */ diff --git a/src/input/input.c b/src/input/input.c index 5d1992523b..96144a6874 100644 --- a/src/input/input.c +++ b/src/input/input.c @@ -38,6 +38,7 @@ #include "stream_output.h" #include "vlc_playlist.h" #include "vlc_interface.h" +#include "vlc_interaction.h" /***************************************************************************** * Local prototypes @@ -2094,7 +2095,8 @@ static int InputSourceInit( input_thread_t *p_input, if( in->p_access == NULL ) { msg_Err( p_input, "no suitable access module for `%s'", psz_mrl ); - intf_UserFatal( p_input, "Unable to open '%s'", psz_mrl ); + intf_UserFatal( VLC_OBJECT( p_input), DIALOG_NOACCESS, + "Unable to open '%s'", psz_mrl ); goto error; } diff --git a/src/interface/interaction.c b/src/interface/interaction.c index 662d7bc721..5103c86617 100644 --- a/src/interface/interaction.c +++ b/src/interface/interaction.c @@ -26,7 +26,6 @@ * This file contains functions related to user interaction management */ - /***************************************************************************** * Preamble *****************************************************************************/ @@ -41,9 +40,18 @@ #include "vlc_interface.h" #include "vlc_playlist.h" -static void intf_InteractionInit( playlist_t *p_playlist ); -static int intf_WaitAnswer( intf_thread_t *p_intf, interaction_dialog_t *p_interact ); -static int intf_Send( intf_thread_t *p_intf, interaction_dialog_t *p_interact ); +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static void intf_InteractionInit( playlist_t *p_playlist ); +static interaction_t * intf_InteractionGet( vlc_object_t *p_this ); +static void intf_InteractionSearchInterface( interaction_t * + p_interaction ); +static int intf_WaitAnswer( interaction_t *p_interact, + interaction_dialog_t *p_dialog ); +static int intf_Send( interaction_t *p_interact, + interaction_dialog_t *p_dialog ); +static interaction_dialog_t *intf_InteractionGetById( vlc_object_t* , int ); /** * Send an interaction element to the user @@ -53,92 +61,216 @@ static int intf_Send( intf_thread_t *p_intf, interaction_dialog_t *p_interact ); * \return VLC_SUCCESS or an error code */ int __intf_Interact( vlc_object_t *p_this, interaction_dialog_t * - p_interact ) + p_dialog ) { - vlc_list_t *p_list; - int i_index; - intf_thread_t *p_chosen_intf; + interaction_t *p_interaction = intf_InteractionGet( p_this ); - /* Search a suitable intf */ - p_list = vlc_list_find( p_this, VLC_OBJECT_INTF, FIND_ANYWHERE ); - if( !p_list ) + /* Get an id, if we don't already have one */ + if( p_dialog->i_id == 0 ) { - msg_Err( p_this, "Unable to create module list" ); - return VLC_FALSE; + p_dialog->i_id = ++p_interaction->i_last_id; } - p_chosen_intf = NULL; - for( i_index = 0; i_index < p_list->i_count; i_index ++ ) + if( p_dialog->i_type == INTERACT_ASK ) { - intf_thread_t *p_intf = (intf_thread_t *) - p_list->p_values[i_index].p_object; - if( p_intf->pf_interact != NULL ) - { - p_chosen_intf = p_intf; - break; - } + return intf_WaitAnswer( p_interaction, p_dialog ); } - if( !p_chosen_intf ) + else { - msg_Dbg( p_this, "No interface suitable for interaction" ); - return VLC_FALSE; + return intf_Send( p_interaction, p_dialog ); } - msg_Dbg( p_this, "found an interface for interaction" ); +} - /* Find id, if we don't already have one */ - if( p_interact->i_id == 0 ) - { - p_interact->i_id = ++p_chosen_intf->i_last_id; - } +/** + * Destroy the interaction system + */ +void intf_InteractionDestroy( interaction_t *p_interaction ) +{ + /// \todo Code this, and call it +} + +/** + * The main interaction processing loop + * This function is called from the playlist loop + * + * \param p_playlist the parent playlist + * \return nothing + */ +void intf_InteractionManage( playlist_t *p_playlist ) +{ + int i_index; + interaction_t *p_interaction; + + p_interaction = p_playlist->p_interaction; + + // Nothing to do + if( p_interaction->i_dialogs == 0 ) return; + + vlc_mutex_lock( &p_interaction->object_lock ); + + intf_InteractionSearchInterface( p_interaction ); - if( p_interact->i_type == INTERACT_ASK ) + if( !p_interaction->p_intf ) { - return intf_WaitAnswer( p_chosen_intf, p_interact ); + vlc_mutex_unlock( &p_interaction->object_lock ); + + /// \todo Remove all dialogs as we can't display them + return; } - else + + vlc_object_yield( p_interaction->p_intf ); + + for( i_index = 0 ; i_index < p_interaction->i_dialogs; i_index ++ ) { - return intf_Send( p_chosen_intf, p_interact ); + interaction_dialog_t *p_dialog = p_interaction->pp_dialogs[i_index]; + + if( p_dialog->b_have_answer ) + { + /// \todo Signal we have an answer + // - If have answer, signal what is waiting + // (vlc_cond ? dangerous in case of pb ?) + + // Ask interface to hide it + p_interaction->p_intf->pf_interact( p_interaction->p_intf, + p_dialog, INTERACT_HIDE ); + + } + + if( p_dialog->b_updated ) + { + p_dialog->b_finished = VLC_FALSE; + p_interaction->p_intf->pf_interact( p_interaction->p_intf, + p_dialog, INTERACT_UPDATE ); + } + + if( p_dialog->b_finished && !p_dialog->b_reusable ) + { + /// \todo Destroy the dialog + } + // This is truly a new dialog, send it. + p_interaction->p_intf->pf_interact( p_interaction->p_intf, + p_dialog, INTERACT_NEW ); } + + vlc_object_release( p_interaction->p_intf ); + + vlc_mutex_unlock( &p_playlist->p_interaction->object_lock ); } -int intf_WaitAnswer( intf_thread_t *p_intf, interaction_dialog_t *p_interact ) + + +#define INTERACT_INIT( new ) \ + new = (interaction_dialog_t*)malloc( \ + sizeof( interaction_dialog_t ) ); \ + new->i_widgets = 0; \ + new->pp_widgets = NULL; \ + new->psz_title = NULL; \ + new->psz_description = NULL; \ + new->i_id = 0; + +#define INTERACT_FREE( new ) \ + if( new->psz_title ) free( new->psz_title ); \ + if( new->psz_description ) free( new->psz_description ); + +/** Helper function to send a fatal message + * \param p_this Parent vlc_object + * \param i_id A predefined ID, 0 if not applicable + * \param psz_title Title for the dialog + * \param psz_format The message to display + * */ +void __intf_UserFatal( vlc_object_t *p_this, int i_id, + const char *psz_title, + const char *psz_format, ... ) { - // TODO: Add to queue, wait for answer - return VLC_SUCCESS; + va_list args; + interaction_dialog_t *p_new = NULL; + user_widget_t *p_widget = NULL; + + if( i_id > 0 ) + { + p_new = intf_InteractionGetById( p_this, i_id ); + } + if( !p_new ) + { + INTERACT_INIT( p_new ); + } + + p_new->i_type = INTERACT_FATAL; + p_new->psz_title = strdup( psz_title ); + + p_widget = (user_widget_t* )malloc( sizeof( user_widget_t ) ); + + p_widget->i_type = WIDGET_TEXT; + + va_start( args, psz_format ); + vasprintf( &p_widget->psz_text, psz_format, args ); + va_end( args ); + + INSERT_ELEM ( p_new->pp_widgets, + p_new->i_widgets, + p_new->i_widgets, + p_widget ); + + intf_Interact( p_this, p_new ); } -int intf_Send( intf_thread_t *p_intf, interaction_dialog_t *p_interact ) +#if 0 +/** Helper function to build a progress bar + * \param p_this Parent vlc object + */ +interaction_dialog_t *__intf_ProgressBuild( vlc_object_t *p_this, + const char *psz_text ) { - // TODO: Add to queue, return - return VLC_SUCCESS; + interaction_dialog_t *p_new = (interaction_dialog_t *)malloc( + sizeof( interaction_dialog_t ) ); + + + return p_new; } +#endif -// The playlist manages the user interaction to avoid creating another thread -void intf_InteractionManage( playlist_t *p_playlist ) + + +/********************************************************************** + * The following functions are local + **********************************************************************/ + +/* Get the interaction object. Create it if needed */ +static interaction_t * intf_InteractionGet( vlc_object_t *p_this ) { + playlist_t *p_playlist; + interaction_t *p_interaction; + + p_playlist = (playlist_t*) vlc_object_find( p_this, VLC_OBJECT_PLAYLIST, + FIND_ANYWHERE ); + + if( !p_playlist ) + { + return NULL; + } + if( p_playlist->p_interaction == NULL ) { intf_InteractionInit( p_playlist ); } - vlc_mutex_lock( &p_playlist->p_interaction->object_lock ); + p_interaction = p_playlist->p_interaction; - /* Todo: - * - Walk the queue - * - If blocking - * - If have answer, signal what is waiting (vlc_cond ? dangerous in case of pb ?) - * And then, if not reusable, destroy - * - If have update, send update - */ + vlc_object_release( p_playlist ); - vlc_mutex_unlock( &p_playlist->p_interaction->object_lock ); + return p_interaction; } +/* Create the interaction object in the given playlist object */ static void intf_InteractionInit( playlist_t *p_playlist ) { interaction_t *p_interaction; - p_interaction = vlc_object_create( VLC_OBJECT( p_playlist ), sizeof( interaction_t ) ); + + msg_Dbg( p_playlist, "initializing interaction system" ); + + p_interaction = vlc_object_create( VLC_OBJECT( p_playlist ), + sizeof( interaction_t ) ); if( !p_interaction ) { msg_Err( p_playlist,"out of memory" ); @@ -147,47 +279,76 @@ static void intf_InteractionInit( playlist_t *p_playlist ) p_interaction->i_dialogs = 0; p_interaction->pp_dialogs = NULL; + p_interaction->p_intf = NULL; + p_interaction->i_last_id = DIALOG_LAST_PREDEFINED + 1; + + vlc_mutex_init( p_interaction , &p_interaction->object_lock ); + + p_playlist->p_interaction = p_interaction; } -/** Helper function to build a progress bar */ -interaction_dialog_t *__intf_ProgressBuild( vlc_object_t *p_this, - const char *psz_text ) +/* Look for an interface suitable for interaction */ +static void intf_InteractionSearchInterface( interaction_t *p_interaction ) { - interaction_dialog_t *p_new = (interaction_dialog_t *)malloc( - sizeof( interaction_dialog_t ) ); + vlc_list_t *p_list; + int i_index; + p_interaction->p_intf = NULL; - return p_new; -} - -#define INTERACT_INIT( new ) \ - interaction_dialog_t *new = (interaction_dialog_t*)malloc( \ - sizeof( interaction_dialog_t ) ); \ - new->i_widgets = 0; \ - new->pp_widgets = NULL; \ - new->psz_title = NULL; \ - new->psz_description = NULL; \ - new->i_id = 0; + p_list = vlc_list_find( p_interaction, VLC_OBJECT_INTF, FIND_ANYWHERE ); + if( !p_list ) + { + msg_Err( p_interaction, "Unable to create module list" ); + return; + } -#define INTERACT_FREE( new ) \ - if( new->psz_title ) free( new->psz_title ); \ - if( new->psz_description ) free( new->psz_description ); + for( i_index = 0; i_index < p_list->i_count; i_index ++ ) + { + intf_thread_t *p_intf = (intf_thread_t *) + p_list->p_values[i_index].p_object; + if( p_intf->pf_interact != NULL ) + { + p_interaction->p_intf = p_intf; + break; + } + } + vlc_list_release ( p_list ); +} -/** Helper function to send a fatal message */ -void intf_UserFatal( vlc_object_t *p_this, const char *psz_title, - const char *psz_format, ... ) +/* Add a dialog to the queue and wait for answer */ +static int intf_WaitAnswer( interaction_t *p_interact, interaction_dialog_t *p_dialog ) { - va_list args; + // TODO: Add to queue, wait for answer + return VLC_SUCCESS; +} - INTERACT_INIT( p_new ); - p_new->i_type = INTERACT_FATAL; - p_new->psz_title = strdup( psz_title ); +/* Add a dialog to the queue and return */ +static int intf_Send( interaction_t *p_interact, interaction_dialog_t *p_dialog ) +{ + vlc_mutex_lock( &p_interact->object_lock ); - va_start( args, psz_format ); - vasprintf( &p_new->psz_description, psz_format, args ); - va_end( args ); + /// \todo Check first it does not exist !!! + INSERT_ELEM( p_interact->pp_dialogs, + p_interact->i_dialogs, + p_interact->i_dialogs, + p_dialog ); + vlc_mutex_unlock( &p_interact->object_lock ); + return VLC_SUCCESS; +} - intf_Interact( p_this, p_new ); +/* Find an interaction dialog by its id */ +static interaction_dialog_t *intf_InteractionGetById( vlc_object_t* p_this, + int i_id ) +{ + interaction_t *p_interaction = intf_InteractionGet( p_this ); + int i; - INTERACT_FREE( p_new ); + for( i = 0 ; i< p_interaction->i_dialogs; i++ ) + { + if( p_interaction->pp_dialogs[i]->i_id == i_id ) + { + return p_interaction->pp_dialogs[i]; + } + } + return NULL; } -- 2.39.2