1 /*****************************************************************************
2 * interaction.c: User interaction functions
3 *****************************************************************************
4 * Copyright (C) 2005-2006 the VideoLAN team
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 *****************************************************************************/
40 #include <stdlib.h> /* free(), strtol() */
41 #include <stdio.h> /* FILE */
44 #include <vlc_interface.h>
45 #include <vlc_playlist.h>
46 #include "interface.h"
48 /*****************************************************************************
50 *****************************************************************************/
51 static void InteractionInit( playlist_t *p_playlist );
52 static interaction_t * InteractionGet( vlc_object_t *p_this );
53 static void InteractionSearchInterface( interaction_t *
55 static interaction_dialog_t *DialogGetById( interaction_t* , int );
56 static void DialogDestroy( interaction_dialog_t *p_dialog );
57 static int DialogSend( vlc_object_t *p_this, interaction_dialog_t *p_dialog );
60 * Destroy the interaction system
61 * \param The interaction object to destroy
64 void intf_InteractionDestroy( interaction_t *p_interaction )
67 // Remove all dialogs - Interfaces must be able to clean up their data
68 for( i = p_interaction->i_dialogs -1 ; i >= 0; i-- )
70 interaction_dialog_t * p_dialog = p_interaction->pp_dialogs[i];
71 DialogDestroy( p_dialog );
72 REMOVE_ELEM( p_interaction->pp_dialogs, p_interaction->i_dialogs, i );
74 vlc_object_release( p_interaction );
78 * The main interaction processing loop
79 * This function is called from the playlist loop
81 * \param p_playlist the parent playlist
84 void intf_InteractionManage( playlist_t *p_playlist )
88 interaction_t *p_interaction = p_playlist->p_interaction;
91 if( p_interaction->i_dialogs == 0 ) return;
93 vlc_mutex_lock( &p_interaction->object_lock );
95 InteractionSearchInterface( p_interaction );
96 if( !p_interaction->p_intf )
98 // We mark all dialogs as answered with their "default" answer
99 for( i_index = 0 ; i_index < p_interaction->i_dialogs; i_index ++ )
101 interaction_dialog_t *p_dialog = p_interaction->pp_dialogs[i_index];
102 p_dialog->i_return = DIALOG_DEFAULT; // Give default answer
104 // Pretend we have hidden and destroyed it
105 if( p_dialog->i_status == HIDDEN_DIALOG )
106 p_dialog->i_status = DESTROYED_DIALOG;
108 p_dialog->i_status = HIDING_DIALOG;
112 vlc_object_yield( p_interaction->p_intf );
114 for( i_index = 0 ; i_index < p_interaction->i_dialogs; i_index ++ )
116 interaction_dialog_t *p_dialog = p_interaction->pp_dialogs[i_index];
117 switch( p_dialog->i_status )
119 case ANSWERED_DIALOG:
120 // Ask interface to hide it
121 p_dialog->i_action = INTERACT_HIDE;
122 val.p_address = p_dialog;
123 if( p_interaction->p_intf )
124 var_Set( p_interaction->p_intf, "interaction", val );
125 p_dialog->i_status = HIDING_DIALOG;
128 p_dialog->i_action = INTERACT_UPDATE;
129 val.p_address = p_dialog;
130 if( p_interaction->p_intf )
131 var_Set( p_interaction->p_intf, "interaction", val );
132 p_dialog->i_status = SENT_DIALOG;
135 if( !(p_dialog->i_flags & DIALOG_GOT_ANSWER) ) break;
136 p_dialog->i_action = INTERACT_DESTROY;
137 val.p_address = p_dialog;
138 if( p_interaction->p_intf )
139 var_Set( p_interaction->p_intf, "interaction", val );
141 case DESTROYED_DIALOG:
142 // Interface has now destroyed it, remove it
143 REMOVE_ELEM( p_interaction->pp_dialogs, p_interaction->i_dialogs,
146 DialogDestroy( p_dialog );
149 // This is truly a new dialog, send it.
150 p_dialog->i_action = INTERACT_NEW;
151 val.p_address = p_dialog;
152 if( p_interaction->p_intf )
153 var_Set( p_interaction->p_intf, "interaction", val );
154 p_dialog->i_status = SENT_DIALOG;
159 if( p_interaction->p_intf )
161 vlc_object_release( p_interaction->p_intf );
164 vlc_mutex_unlock( &p_playlist->p_interaction->object_lock );
167 #define DIALOG_INIT( type ) \
168 DECMALLOC_ERR( p_new, interaction_dialog_t ); \
169 memset( p_new, 0, sizeof( interaction_dialog_t ) ); \
170 p_new->b_cancelled = VLC_FALSE; \
171 p_new->i_status = NEW_DIALOG; \
172 p_new->i_flags = 0; \
173 p_new->i_type = INTERACT_DIALOG_##type; \
174 p_new->psz_returned[0] = NULL; \
175 p_new->psz_returned[1] = NULL;
177 #define FORMAT_DESC \
178 va_start( args, psz_format ); \
179 vasprintf( &p_new->psz_description, psz_format, args ); \
182 /** Send an error message, both in a blocking and non-blocking way
183 * \param p_this Parent vlc_object
184 * \param b_blocking Is this dialog blocking or not?
185 * \param psz_title Title for the dialog
186 * \param psz_format The message to display
188 int __intf_UserFatal( vlc_object_t *p_this, vlc_bool_t b_blocking,
189 const char *psz_title,
190 const char *psz_format, ... )
193 DIALOG_INIT( ONEWAY );
195 p_new->psz_title = strdup( psz_title );
199 p_new->i_flags = DIALOG_BLOCKING_ERROR;
201 p_new->i_flags = DIALOG_NONBLOCKING_ERROR;
203 return DialogSend( p_this, p_new );
206 /** Helper function to send an warning, which is always shown non-blocking
207 * \param p_this Parent vlc_object
208 * \param psz_title Title for the dialog
209 * \param psz_format The message to display
211 int __intf_UserWarn( vlc_object_t *p_this,
212 const char *psz_title,
213 const char *psz_format, ... )
216 DIALOG_INIT( ONEWAY );
218 p_new->psz_title = strdup( psz_title );
221 p_new->i_flags = DIALOG_WARNING;
223 return DialogSend( p_this, p_new );
226 /** Helper function to ask a yes-no-cancel question
227 * \param p_this Parent vlc_object
228 * \param psz_title Title for the dialog
229 * \param psz_description A description
230 * \param psz_default caption for the default button
231 * \param psz_alternate caption for the alternate button
232 * \param psz_other caption for the optional 3rd button (== cancel)
233 * \return Clicked button code
235 int __intf_UserYesNo( vlc_object_t *p_this,
236 const char *psz_title,
237 const char *psz_description,
238 const char *psz_default,
239 const char *psz_alternate,
240 const char *psz_other )
242 DIALOG_INIT( TWOWAY );
244 p_new->psz_title = strdup( psz_title );
245 p_new->psz_description = strdup( psz_description );
246 p_new->i_flags = DIALOG_YES_NO_CANCEL;
247 p_new->psz_default_button = strdup( psz_default );
248 p_new->psz_alternate_button = strdup( psz_alternate );
250 p_new->psz_other_button = strdup( psz_other );
252 return DialogSend( p_this, p_new );
255 /** Helper function to create a dialogue showing a progress-bar with some info
256 * \param p_this Parent vlc_object
257 * \param psz_title Title for the dialog (NULL implies main intf )
258 * \param psz_status Current status
259 * \param f_position Current position (0.0->100.0)
260 * \param i_timeToGo Time (in sec) to go until process is finished
261 * \return Dialog id, to give to UserProgressUpdate
263 int __intf_Progress( vlc_object_t *p_this, const char *psz_title,
264 const char *psz_status, float f_pos, int i_time )
266 DIALOG_INIT( ONEWAY );
267 p_new->psz_description = strdup( psz_status );
268 p_new->val.f_float = f_pos;
269 p_new->i_timeToGo = i_time;
270 p_new->psz_alternate_button = strdup( _( "Cancel" ) );
274 p_new->psz_title = strdup( psz_title );
275 p_new->i_flags = DIALOG_USER_PROGRESS;
278 p_new->i_flags = DIALOG_INTF_PROGRESS;
280 DialogSend( p_this, p_new );
284 /** Update a progress bar in a dialogue
285 * \param p_this Parent vlc_object
286 * \param i_id Identifier of the dialog
287 * \param psz_status New status
288 * \param f_position New position (0.0->100.0)
289 * \param i_timeToGo Time (in sec) to go until process is finished
292 void __intf_ProgressUpdate( vlc_object_t *p_this, int i_id,
293 const char *psz_status, float f_pos, int i_time )
295 interaction_t *p_interaction = InteractionGet( p_this );
296 interaction_dialog_t *p_dialog;
298 if( !p_interaction ) return;
300 vlc_mutex_lock( &p_interaction->object_lock );
301 p_dialog = DialogGetById( p_interaction, i_id );
305 vlc_mutex_unlock( &p_interaction->object_lock ) ;
309 free( p_dialog->psz_description );
310 p_dialog->psz_description = strdup( psz_status );
312 p_dialog->val.f_float = f_pos;
313 p_dialog->i_timeToGo = i_time;
315 p_dialog->i_status = UPDATED_DIALOG;
316 vlc_mutex_unlock( &p_interaction->object_lock) ;
318 playlist_Signal( pl_Get( p_this ) );
321 /** Helper function to communicate dialogue cancellations between the
322 * interface module and the caller
323 * \param p_this Parent vlc_object
324 * \param i_id Identifier of the dialogue
325 * \return Either true or false
327 vlc_bool_t __intf_UserProgressIsCancelled( vlc_object_t *p_this, int i_id )
329 interaction_t *p_interaction = InteractionGet( p_this );
330 interaction_dialog_t *p_dialog;
333 if( !p_interaction ) return VLC_TRUE;
335 vlc_mutex_lock( &p_interaction->object_lock );
336 p_dialog = DialogGetById( p_interaction, i_id );
339 vlc_mutex_unlock( &p_interaction->object_lock ) ;
343 b_cancel = p_dialog->b_cancelled;
344 vlc_mutex_unlock( &p_interaction->object_lock );
348 /** Helper function to make a login/password dialogue
349 * \param p_this Parent vlc_object
350 * \param psz_title Title for the dialog
351 * \param psz_description A description
352 * \param ppsz_login Returned login
353 * \param ppsz_password Returned password
354 * \return Clicked button code
356 int __intf_UserLoginPassword( vlc_object_t *p_this,
357 const char *psz_title,
358 const char *psz_description,
360 char **ppsz_password )
363 DIALOG_INIT( TWOWAY );
364 p_new->i_type = INTERACT_DIALOG_TWOWAY;
365 p_new->psz_title = strdup( psz_title );
366 p_new->psz_description = strdup( psz_description );
367 p_new->psz_default_button = strdup( _("Ok" ) );
368 p_new->psz_alternate_button = strdup( _("Cancel" ) );
370 p_new->i_flags = DIALOG_LOGIN_PW_OK_CANCEL;
372 i_ret = DialogSend( p_this, p_new );
374 if( i_ret != DIALOG_CANCELLED && i_ret != VLC_EGENERIC )
376 *ppsz_login = p_new->psz_returned[0]?
377 strdup( p_new->psz_returned[0] ) : NULL;
378 *ppsz_password = p_new->psz_returned[1]?
379 strdup( p_new->psz_returned[1] ) : NULL;
384 /** Helper function to make a dialogue asking the user for !password string
385 * \param p_this Parent vlc_object
386 * \param psz_title Title for the dialog
387 * \param psz_description A description
388 * \param ppsz_usersString Returned login
389 * \return Clicked button code
391 int __intf_UserStringInput( vlc_object_t *p_this,
392 const char *psz_title,
393 const char *psz_description,
394 char **ppsz_usersString )
397 DIALOG_INIT( TWOWAY );
398 p_new->i_type = INTERACT_DIALOG_TWOWAY;
399 p_new->psz_title = strdup( psz_title );
400 p_new->psz_description = strdup( psz_description );
402 p_new->i_flags = DIALOG_PSZ_INPUT_OK_CANCEL;
404 i_ret = DialogSend( p_this, p_new );
406 if( i_ret != DIALOG_CANCELLED )
408 *ppsz_usersString = p_new->psz_returned[0]?
409 strdup( p_new->psz_returned[0] ) : NULL;
414 /** Hide an interaction dialog
415 * \param p_this the parent vlc object
416 * \param i_id the id of the item to hide
419 void __intf_UserHide( vlc_object_t *p_this, int i_id )
421 interaction_t *p_interaction = InteractionGet( p_this );
422 interaction_dialog_t *p_dialog;
424 if( !p_interaction ) return;
426 vlc_mutex_lock( &p_interaction->object_lock );
427 p_dialog = DialogGetById( p_interaction, i_id );
431 vlc_mutex_unlock( &p_interaction->object_lock );
435 p_dialog->i_status = ANSWERED_DIALOG;
436 vlc_mutex_unlock( &p_interaction->object_lock );
439 /**********************************************************************
440 * The following functions are local
441 **********************************************************************/
443 /* Get the interaction object. Create it if needed */
444 static interaction_t * InteractionGet( vlc_object_t *p_this )
446 interaction_t *p_interaction;
447 playlist_t *p_playlist = pl_Yield( p_this );
450 if( p_playlist->p_interaction == NULL )
451 InteractionInit( p_playlist );
453 p_interaction = p_playlist->p_interaction;
456 pl_Release( p_this );
457 return p_interaction;
460 /* Create the interaction object in the given playlist object */
461 static void InteractionInit( playlist_t *p_playlist )
463 interaction_t *p_interaction = vlc_object_create( VLC_OBJECT( p_playlist ),
464 sizeof( interaction_t ) );
467 msg_Err( p_playlist,"out of memory" );
471 p_interaction->psz_object_name = "interaction";
472 p_interaction->i_dialogs = 0;
473 p_interaction->pp_dialogs = NULL;
474 p_interaction->p_intf = NULL;
475 p_interaction->i_last_id = 0;
477 vlc_mutex_init( p_interaction , &p_interaction->object_lock );
478 p_playlist->p_interaction = p_interaction;
481 /* Look for an interface suitable for interaction */
482 static void InteractionSearchInterface( interaction_t *p_interaction )
487 p_interaction->p_intf = NULL;
489 p_list = vlc_list_find( p_interaction, VLC_OBJECT_INTF, FIND_ANYWHERE );
492 msg_Err( p_interaction, "unable to create module list" );
496 for( i_index = 0; i_index < p_list->i_count; i_index ++ )
498 intf_thread_t *p_intf = (intf_thread_t *)
499 p_list->p_values[i_index].p_object;
500 if( p_intf->b_interaction )
502 p_interaction->p_intf = p_intf;
506 vlc_list_release ( p_list );
509 /* Find an interaction dialog by its id */
510 static interaction_dialog_t *DialogGetById( interaction_t *p_interaction,
514 for( i = 0 ; i< p_interaction->i_dialogs; i++ )
516 if( p_interaction->pp_dialogs[i]->i_id == i_id )
517 return p_interaction->pp_dialogs[i];
522 /* Destroy a dialog */
523 static void DialogDestroy( interaction_dialog_t *p_dialog )
525 free( p_dialog->psz_title );
526 free( p_dialog->psz_description );
527 free( p_dialog->psz_default_button );
528 free( p_dialog->psz_alternate_button );
529 free( p_dialog->psz_other_button );
533 /* Ask for the dialog to be sent to the user. Wait for answer
535 static int DialogSend( vlc_object_t *p_this, interaction_dialog_t *p_dialog )
537 interaction_t *p_interaction = InteractionGet( p_this );
539 /* Get an id, if we don't already have one */
540 if( p_dialog->i_id == 0 )
541 p_dialog->i_id = ++p_interaction->i_last_id;
543 if( p_this->i_flags & OBJECT_FLAGS_NOINTERACT ) return VLC_EGENERIC;
545 if( config_GetInt(p_this, "interact") ||
546 p_dialog->i_flags & DIALOG_BLOCKING_ERROR ||
547 p_dialog->i_flags & DIALOG_NONBLOCKING_ERROR )
549 vlc_bool_t b_found = VLC_FALSE;
551 p_dialog->p_interaction = p_interaction;
552 p_dialog->p_parent = p_this;
554 /* Check if we have already added this dialog */
555 vlc_mutex_lock( &p_interaction->object_lock );
556 for( i = 0 ; i< p_interaction->i_dialogs; i++ )
558 if( p_interaction->pp_dialogs[i]->i_id == p_dialog->i_id )
561 /* Add it to the queue, the main loop will send the orders to the
565 INSERT_ELEM( p_interaction->pp_dialogs,
566 p_interaction->i_dialogs,
567 p_interaction->i_dialogs,
571 p_dialog->i_status = UPDATED_DIALOG;
573 if( p_dialog->i_type == INTERACT_DIALOG_TWOWAY ) // Wait for answer
575 playlist_Signal( pl_Get( p_this ) );
576 while( p_dialog->i_status != ANSWERED_DIALOG &&
577 p_dialog->i_status != HIDING_DIALOG &&
578 p_dialog->i_status != HIDDEN_DIALOG &&
579 !p_dialog->p_parent->b_die )
581 vlc_mutex_unlock( &p_interaction->object_lock );
583 vlc_mutex_lock( &p_interaction->object_lock );
585 if( p_dialog->p_parent->b_die )
587 p_dialog->i_return = DIALOG_CANCELLED;
588 p_dialog->i_status = ANSWERED_DIALOG;
590 p_dialog->i_flags |= DIALOG_GOT_ANSWER;
591 vlc_mutex_unlock( &p_interaction->object_lock );
592 playlist_Signal( pl_Get( p_this ) );
593 return p_dialog->i_return;
597 // Pretend we already retrieved the "answer"
598 p_dialog->i_flags |= DIALOG_GOT_ANSWER;
599 vlc_mutex_unlock( &p_interaction->object_lock );
600 playlist_Signal( pl_Get( p_this ) );