1 /*****************************************************************************
2 * interaction.c: User interaction functions
3 *****************************************************************************
4 * Copyright (C) 2005-2006 VideoLAN
7 * Authors: Clément Stenac <zorglub@videolan.org>
8 * Felix Kühne <fkuehne@videolan.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
27 * This file contains functions related to user interaction management
30 /*****************************************************************************
32 *****************************************************************************/
33 #include <stdlib.h> /* free(), strtol() */
34 #include <stdio.h> /* FILE */
35 #include <string.h> /* strerror() */
38 #include <vlc/input.h>
40 #include "vlc_interaction.h"
41 #include "vlc_interface.h"
42 #include "vlc_playlist.h"
44 /*****************************************************************************
46 *****************************************************************************/
47 static void intf_InteractionInit( playlist_t *p_playlist );
48 static interaction_t * intf_InteractionGet( vlc_object_t *p_this );
49 static void intf_InteractionSearchInterface( interaction_t *
51 static int intf_WaitAnswer( interaction_t *p_interact,
52 interaction_dialog_t *p_dialog );
53 static int intf_Send( interaction_t *p_interact,
54 interaction_dialog_t *p_dialog );
55 static interaction_dialog_t *intf_InteractionGetById( vlc_object_t* , int );
56 static void intf_InteractionDialogDestroy(
57 interaction_dialog_t *p_dialog );
60 * Send an interaction element to the user
62 * \param p_this the calling vlc_object_t
63 * \param p_interact the interaction element
64 * \return VLC_SUCCESS or an error code
66 int __intf_Interact( vlc_object_t *p_this, interaction_dialog_t *p_dialog )
68 interaction_t *p_interaction = intf_InteractionGet( p_this );
70 /* Get an id, if we don't already have one */
71 if( p_dialog->i_id == 0 )
73 p_dialog->i_id = ++p_interaction->i_last_id;
76 if( p_this->i_flags & OBJECT_FLAGS_NOINTERACT ) return VLC_EGENERIC;
78 if( config_GetInt(p_this, "interact") ||
79 p_dialog->i_flags & DIALOG_BLOCKING_ERROR ||
80 p_dialog->i_flags & DIALOG_NONBLOCKING_ERROR )
82 p_dialog->p_interaction = p_interaction;
83 p_dialog->p_parent = p_this;
85 if( p_dialog->i_type == INTERACT_DIALOG_TWOWAY )
87 return intf_WaitAnswer( p_interaction, p_dialog );
91 p_dialog->i_flags |= DIALOG_GOT_ANSWER;
92 return intf_Send( p_interaction, p_dialog );
100 * Destroy the interaction system
101 * \param The interaction object to destroy
104 void intf_InteractionDestroy( interaction_t *p_interaction )
108 // Remove all dialogs - Interfaces must be able to clean up their data
110 for( i = p_interaction->i_dialogs -1 ; i >= 0; i-- )
112 interaction_dialog_t * p_dialog = p_interaction->pp_dialogs[i];
113 intf_InteractionDialogDestroy( p_dialog );
114 REMOVE_ELEM( p_interaction->pp_dialogs, p_interaction->i_dialogs, i );
117 vlc_object_destroy( p_interaction );
121 * The main interaction processing loop
122 * This function is called from the playlist loop
124 * \param p_playlist the parent playlist
127 void intf_InteractionManage( playlist_t *p_playlist )
131 interaction_t *p_interaction;
133 p_interaction = p_playlist->p_interaction;
136 if( p_interaction->i_dialogs == 0 ) return;
138 vlc_mutex_lock( &p_interaction->object_lock );
140 intf_InteractionSearchInterface( p_interaction );
142 if( !p_interaction->p_intf )
144 // We mark all dialogs as answered with their "default" answer
145 for( i_index = 0 ; i_index < p_interaction->i_dialogs; i_index ++ )
147 interaction_dialog_t *p_dialog = p_interaction->pp_dialogs[i_index];
149 // Give default answer
150 p_dialog->i_return = DIALOG_DEFAULT;
152 // Pretend we have hidden and destroyed it
153 if( p_dialog->i_status == HIDDEN_DIALOG )
155 p_dialog->i_status = DESTROYED_DIALOG;
159 p_dialog->i_status = HIDING_DIALOG;
165 vlc_object_yield( p_interaction->p_intf );
168 for( i_index = 0 ; i_index < p_interaction->i_dialogs; i_index ++ )
170 interaction_dialog_t *p_dialog = p_interaction->pp_dialogs[i_index];
171 switch( p_dialog->i_status )
173 case ANSWERED_DIALOG:
174 // Ask interface to hide it
175 p_dialog->i_action = INTERACT_HIDE;
176 val.p_address = p_dialog;
177 if( p_interaction->p_intf )
178 var_Set( p_interaction->p_intf, "interaction", val );
179 p_dialog->i_status = HIDING_DIALOG;
182 p_dialog->i_action = INTERACT_UPDATE;
183 val.p_address = p_dialog;
184 if( p_interaction->p_intf )
185 var_Set( p_interaction->p_intf, "interaction", val );
186 p_dialog->i_status = SENT_DIALOG;
189 if( !(p_dialog->i_flags & DIALOG_GOT_ANSWER) ) break;
190 if( !(p_dialog->i_flags & DIALOG_REUSABLE) )
192 p_dialog->i_action = INTERACT_DESTROY;
193 val.p_address = p_dialog;
194 if( p_interaction->p_intf )
195 var_Set( p_interaction->p_intf, "interaction", val );
198 case DESTROYED_DIALOG:
199 // Interface has now destroyed it, remove it
200 REMOVE_ELEM( p_interaction->pp_dialogs, p_interaction->i_dialogs,
203 intf_InteractionDialogDestroy( p_dialog );
206 // This is truly a new dialog, send it.
207 p_dialog->i_action = INTERACT_NEW;
208 val.p_address = p_dialog;
209 if( p_interaction->p_intf )
210 var_Set( p_interaction->p_intf, "interaction", val );
211 p_dialog->i_status = SENT_DIALOG;
216 if( p_interaction->p_intf )
218 vlc_object_release( p_interaction->p_intf );
221 vlc_mutex_unlock( &p_playlist->p_interaction->object_lock );
225 #define INTERACT_INIT( new ) \
226 new = (interaction_dialog_t*)malloc( \
227 sizeof( interaction_dialog_t ) ); \
228 new->psz_title = NULL; \
229 new->psz_description = NULL; \
230 new->psz_defaultButton = NULL; \
231 new->psz_alternateButton = NULL; \
232 new->psz_otherButton = NULL; \
233 new->i_timeToGo = 0; \
234 new->b_cancelled = VLC_FALSE; \
235 new->p_private = NULL; \
238 new->i_status = NEW_DIALOG;
240 #define INTERACT_FREE( new ) \
241 if( new->psz_title ) free( new->psz_title ); \
242 if( new->psz_description ) free( new->psz_description );
244 /** Helper function to send an error message, both in a blocking and non-blocking way
245 * \param p_this Parent vlc_object
246 * \param b_blocking Is this dialog blocking or not?
247 * \param psz_title Title for the dialog
248 * \param psz_format The message to display
250 void __intf_UserFatal( vlc_object_t *p_this,
251 vlc_bool_t b_blocking,
252 const char *psz_title,
253 const char *psz_format, ... )
256 interaction_dialog_t *p_new = NULL;
258 INTERACT_INIT( p_new );
260 p_new->psz_title = strdup( psz_title );
261 p_new->i_type = INTERACT_DIALOG_ONEWAY;
263 va_start( args, psz_format );
264 vasprintf( &p_new->psz_description, psz_format, args );
268 p_new->i_flags = DIALOG_BLOCKING_ERROR;
270 p_new->i_flags = DIALOG_NONBLOCKING_ERROR;
272 intf_Interact( p_this, p_new );
275 /** Helper function to send an warning, which is always shown non-blocking
276 * \param p_this Parent vlc_object
277 * \param psz_title Title for the dialog
278 * \param psz_format The message to display
280 void __intf_UserWarn( vlc_object_t *p_this,
281 const char *psz_title,
282 const char *psz_format, ... )
285 interaction_dialog_t *p_new = NULL;
287 INTERACT_INIT( p_new );
289 p_new->psz_title = strdup( psz_title );
290 p_new->i_type = INTERACT_DIALOG_ONEWAY;
292 va_start( args, psz_format );
293 vasprintf( &p_new->psz_description, psz_format, args );
296 p_new->i_flags = DIALOG_WARNING;
298 intf_Interact( p_this, p_new );
301 /** Helper function to ask a yes-no-cancel question
302 * \param p_this Parent vlc_object
303 * \param psz_title Title for the dialog
304 * \param psz_description A description
305 * \param psz_default caption for the default button
306 * \param psz_alternate caption for the alternate button
307 * \param psz_other caption for the optional 3rd button (== cancel)
308 * \return Clicked button code
310 int __intf_UserYesNo( vlc_object_t *p_this,
311 const char *psz_title,
312 const char *psz_description,
313 const char *psz_default,
314 const char *psz_alternate,
315 const char *psz_other )
318 interaction_dialog_t *p_new = NULL;
320 INTERACT_INIT( p_new );
322 p_new->i_type = INTERACT_DIALOG_TWOWAY;
323 p_new->psz_title = strdup( psz_title );
324 p_new->psz_description = strdup( psz_description );
325 p_new->i_flags = DIALOG_YES_NO_CANCEL;
326 p_new->psz_defaultButton = strdup( psz_default );
327 p_new->psz_alternateButton = strdup( psz_alternate );
329 p_new->psz_otherButton = strdup( psz_other );
331 p_new->psz_otherButton = NULL;
333 i_ret = intf_Interact( p_this, p_new );
338 /** Helper function to create a dialogue showing a progress-bar with some info
339 * \param p_this Parent vlc_object
340 * \param psz_title Title for the dialog
341 * \param psz_status Current status
342 * \param f_position Current position (0.0->100.0)
343 * \param i_timeToGo Time (in sec) to go until process is finished
344 * \return Dialog id, to give to UserProgressUpdate
346 int __intf_UserProgress( vlc_object_t *p_this,
347 const char *psz_title,
348 const char *psz_status,
353 interaction_dialog_t *p_new = NULL;
355 INTERACT_INIT( p_new );
357 p_new->i_type = INTERACT_DIALOG_ONEWAY;
358 p_new->psz_title = strdup( psz_title );
359 p_new->psz_description = strdup( psz_status );
360 p_new->val.f_float = f_pos;
361 p_new->i_timeToGo = i_time;
363 p_new->i_flags = DIALOG_USER_PROGRESS;
365 i_ret = intf_Interact( p_this, p_new );
370 /** Update a progress bar in a dialogue
371 * \param p_this Parent vlc_object
372 * \param i_id Identifier of the dialog
373 * \param psz_status New status
374 * \param f_position New position (0.0->100.0)
375 * \param i_timeToGo Time (in sec) to go until process is finished
378 void __intf_UserProgressUpdate( vlc_object_t *p_this, int i_id,
379 const char *psz_status, float f_pos,
382 interaction_t *p_interaction = intf_InteractionGet( p_this );
383 interaction_dialog_t *p_dialog;
385 if( !p_interaction ) return;
387 vlc_mutex_lock( &p_interaction->object_lock );
388 p_dialog = intf_InteractionGetById( p_this, i_id );
392 vlc_mutex_unlock( &p_interaction->object_lock ) ;
396 if( p_dialog->psz_description )
397 free( p_dialog->psz_description );
398 p_dialog->psz_description = strdup( psz_status );
400 p_dialog->val.f_float = f_pos;
401 p_dialog->i_timeToGo = i_time;
403 p_dialog->i_status = UPDATED_DIALOG;
404 vlc_mutex_unlock( &p_interaction->object_lock) ;
407 /** Helper function to communicate dialogue cancellations between the intf-module and caller
408 * \param p_this Parent vlc_object
409 * \param i_id Identifier of the dialogue
410 * \return Either true or false
412 vlc_bool_t __intf_UserProgressIsCancelled( vlc_object_t *p_this, int i_id )
414 interaction_t *p_interaction = intf_InteractionGet( p_this );
415 interaction_dialog_t *p_dialog;
417 if( !p_interaction ) return VLC_TRUE;
419 vlc_mutex_lock( &p_interaction->object_lock );
420 p_dialog = intf_InteractionGetById( p_this, i_id );
424 vlc_mutex_unlock( &p_interaction->object_lock ) ;
428 vlc_mutex_unlock( &p_interaction->object_lock) ;
429 return p_dialog->b_cancelled;
432 /** Helper function to make a login/password dialogue
433 * \param p_this Parent vlc_object
434 * \param psz_title Title for the dialog
435 * \param psz_description A description
436 * \param ppsz_login Returned login
437 * \param ppsz_password Returned password
438 * \return Clicked button code
440 int __intf_UserLoginPassword( vlc_object_t *p_this,
441 const char *psz_title,
442 const char *psz_description,
444 char **ppsz_password )
448 interaction_dialog_t *p_new = NULL;
450 INTERACT_INIT( p_new );
452 p_new->i_type = INTERACT_DIALOG_TWOWAY;
453 p_new->psz_title = strdup( psz_title );
454 p_new->psz_description = strdup( psz_description );
456 p_new->i_flags = DIALOG_LOGIN_PW_OK_CANCEL;
458 i_ret = intf_Interact( p_this, p_new );
460 if( i_ret != DIALOG_CANCELLED )
462 *ppsz_login = strdup( p_new->psz_returned[0] );
463 *ppsz_password = strdup( p_new->psz_returned[1] );
468 /** Helper function to make a dialogue asking the user for !password string
469 * \param p_this Parent vlc_object
470 * \param psz_title Title for the dialog
471 * \param psz_description A description
472 * \param ppsz_usersString Returned login
473 * \return Clicked button code
475 int __intf_UserStringInput( vlc_object_t *p_this,
476 const char *psz_title,
477 const char *psz_description,
478 char **ppsz_usersString )
482 interaction_dialog_t *p_new = NULL;
484 INTERACT_INIT( p_new );
486 p_new->i_type = INTERACT_DIALOG_TWOWAY;
487 p_new->psz_title = strdup( psz_title );
488 p_new->psz_description = strdup( psz_description );
490 p_new->i_flags = DIALOG_PSZ_INPUT_OK_CANCEL;
492 i_ret = intf_Interact( p_this, p_new );
494 if( i_ret != DIALOG_CANCELLED )
496 *ppsz_usersString = strdup( p_new->psz_returned[0] );
501 /** Helper function to create a progress-bar in the main interface with a
502 * single-line description
503 * \param p_this Parent vlc_object
504 * \param psz_status Current status
505 * \param f_position Current position (0.0->100.0)
506 * \return Dialog id, to give to IntfProgressUpdate
508 int __intf_IntfProgress( vlc_object_t *p_this,
509 const char *psz_status,
513 interaction_dialog_t *p_new = NULL;
515 INTERACT_INIT( p_new );
517 p_new->i_type = INTERACT_DIALOG_ONEWAY;
518 p_new->psz_description = strdup( psz_status );
519 p_new->val.f_float = f_pos;
521 p_new->i_flags = DIALOG_INTF_PROGRESS;
523 i_ret = intf_Interact( p_this, p_new );
528 /** Update the progress bar in the main interface
529 * \param p_this Parent vlc_object
530 * \param i_id Identifier of the dialog
531 * \param psz_status New status
532 * \param f_position New position (0.0->100.0)
535 void __intf_IntfProgressUpdate( vlc_object_t *p_this, int i_id,
536 const char *psz_status, float f_pos )
538 interaction_t *p_interaction = intf_InteractionGet( p_this );
539 interaction_dialog_t *p_dialog;
541 if( !p_interaction ) return;
543 vlc_mutex_lock( &p_interaction->object_lock );
544 p_dialog = intf_InteractionGetById( p_this, i_id );
548 vlc_mutex_unlock( &p_interaction->object_lock ) ;
552 if( p_dialog->psz_description )
553 free( p_dialog->psz_description );
554 p_dialog->psz_description = strdup( psz_status );
556 p_dialog->val.f_float = f_pos;
558 p_dialog->i_status = UPDATED_DIALOG;
559 vlc_mutex_unlock( &p_interaction->object_lock) ;
562 /** Hide an interaction dialog
563 * \param p_this the parent vlc object
564 * \param i_id the id of the item to hide
567 void __intf_UserHide( vlc_object_t *p_this, int i_id )
569 interaction_t *p_interaction = intf_InteractionGet( p_this );
570 interaction_dialog_t *p_dialog;
572 if( !p_interaction ) return;
574 vlc_mutex_lock( &p_interaction->object_lock );
575 p_dialog = intf_InteractionGetById( p_this, i_id );
579 vlc_mutex_unlock( &p_interaction->object_lock );
583 p_dialog->i_status = ANSWERED_DIALOG;
584 vlc_mutex_unlock( &p_interaction->object_lock );
589 /**********************************************************************
590 * The following functions are local
591 **********************************************************************/
593 /* Get the interaction object. Create it if needed */
594 static interaction_t * intf_InteractionGet( vlc_object_t *p_this )
596 playlist_t *p_playlist;
597 interaction_t *p_interaction;
599 p_playlist = (playlist_t*) vlc_object_find( p_this, VLC_OBJECT_PLAYLIST,
607 if( p_playlist->p_interaction == NULL )
609 intf_InteractionInit( p_playlist );
612 p_interaction = p_playlist->p_interaction;
614 vlc_object_release( p_playlist );
616 return p_interaction;
619 /* Create the interaction object in the given playlist object */
620 static void intf_InteractionInit( playlist_t *p_playlist )
622 interaction_t *p_interaction;
624 p_interaction = vlc_object_create( VLC_OBJECT( p_playlist ),
625 sizeof( interaction_t ) );
628 msg_Err( p_playlist,"out of memory" );
632 p_interaction->i_dialogs = 0;
633 p_interaction->pp_dialogs = NULL;
634 p_interaction->p_intf = NULL;
635 p_interaction->i_last_id = DIALOG_LAST_PREDEFINED + 1;
637 vlc_mutex_init( p_interaction , &p_interaction->object_lock );
639 p_playlist->p_interaction = p_interaction;
642 /* Look for an interface suitable for interaction */
643 static void intf_InteractionSearchInterface( interaction_t *p_interaction )
648 p_interaction->p_intf = NULL;
650 p_list = vlc_list_find( p_interaction, VLC_OBJECT_INTF, FIND_ANYWHERE );
653 msg_Err( p_interaction, "unable to create module list" );
657 for( i_index = 0; i_index < p_list->i_count; i_index ++ )
659 intf_thread_t *p_intf = (intf_thread_t *)
660 p_list->p_values[i_index].p_object;
661 if( p_intf->b_interaction )
663 p_interaction->p_intf = p_intf;
667 vlc_list_release ( p_list );
670 /* Add a dialog to the queue and wait for answer */
671 static int intf_WaitAnswer( interaction_t *p_interact,
672 interaction_dialog_t *p_dialog )
675 vlc_bool_t b_found = VLC_FALSE;
676 vlc_mutex_lock( &p_interact->object_lock );
677 for( i = 0 ; i< p_interact->i_dialogs; i++ )
679 if( p_interact->pp_dialogs[i]->i_id == p_dialog->i_id )
686 INSERT_ELEM( p_interact->pp_dialogs,
687 p_interact->i_dialogs,
688 p_interact->i_dialogs,
692 p_dialog->i_status = UPDATED_DIALOG;
693 vlc_mutex_unlock( &p_interact->object_lock );
695 /// \todo Check that the initiating object is not dying
696 while( p_dialog->i_status != ANSWERED_DIALOG &&
697 p_dialog->i_status != HIDING_DIALOG &&
698 p_dialog->i_status != HIDDEN_DIALOG &&
699 !p_dialog->p_parent->b_die )
704 if( p_dialog->p_parent->b_die )
706 p_dialog->i_return = DIALOG_CANCELLED;
707 p_dialog->i_status = ANSWERED_DIALOG;
709 p_dialog->i_flags |= DIALOG_GOT_ANSWER;
710 return p_dialog->i_return;
713 /* Add a dialog to the queue and return */
714 static int intf_Send( interaction_t *p_interact,
715 interaction_dialog_t *p_dialog )
718 vlc_bool_t b_found = VLC_FALSE;
719 if( p_interact == NULL ) return VLC_ENOOBJ;
720 vlc_mutex_lock( &p_interact->object_lock );
722 for( i = 0 ; i< p_interact->i_dialogs; i++ )
724 if( p_interact->pp_dialogs[i]->i_id == p_dialog->i_id )
731 INSERT_ELEM( p_interact->pp_dialogs,
732 p_interact->i_dialogs,
733 p_interact->i_dialogs,
737 p_dialog->i_status = UPDATED_DIALOG;
738 // Pretend we already retrieved the "answer"
739 p_dialog->i_flags |= DIALOG_GOT_ANSWER;
740 vlc_mutex_unlock( &p_interact->object_lock );
744 /* Find an interaction dialog by its id */
745 static interaction_dialog_t *intf_InteractionGetById( vlc_object_t* p_this,
748 interaction_t *p_interaction = intf_InteractionGet( p_this );
751 if( !p_interaction ) return NULL;
753 for( i = 0 ; i< p_interaction->i_dialogs; i++ )
755 if( p_interaction->pp_dialogs[i]->i_id == i_id )
757 return p_interaction->pp_dialogs[i];
763 #define FREE( i ) { if( i ) free( i ); i = NULL; }
765 static void intf_InteractionDialogDestroy( interaction_dialog_t *p_dialog )
767 FREE( p_dialog->psz_title );
768 FREE( p_dialog->psz_description );
769 FREE( p_dialog->psz_defaultButton );
770 FREE( p_dialog->psz_alternateButton );
771 FREE( p_dialog->psz_otherButton );