]> git.sesse.net Git - vlc/blob - src/interface/interaction.c
Remove intf_UserYesNo
[vlc] / src / interface / interaction.c
1 /*****************************************************************************
2  * interaction.c: User interaction functions
3  *****************************************************************************
4  * Copyright © 2005-2008 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Clément Stenac <zorglub@videolan.org>
8  *          Felix Kühne <fkuehne@videolan.org>
9  *
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.
14  *
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.
19  *
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  *****************************************************************************/
24
25 /**
26  *   \file
27  *   This file contains functions related to user interaction management
28  */
29
30 /*****************************************************************************
31  * Preamble
32  *****************************************************************************/
33
34 #ifdef HAVE_CONFIG_H
35 # include "config.h"
36 #endif
37
38 #include <vlc_common.h>
39
40 #include <vlc_interface.h>
41 #include "interface.h"
42 #include "libvlc.h"
43
44 #include <assert.h>
45
46 /*****************************************************************************
47  * Local prototypes
48  *****************************************************************************/
49
50 /**
51  * This structure contains the active interaction dialogs, and is
52  * used by the manager
53  */
54 struct interaction_t
55 {
56     VLC_COMMON_MEMBERS
57
58     vlc_thread_t thread;
59     vlc_mutex_t lock;
60     vlc_cond_t wait;
61
62     int                         i_dialogs;      ///< Number of dialogs
63     interaction_dialog_t      **pp_dialogs;     ///< Dialogs
64     intf_thread_t              *p_intf;         ///< Interface to use
65 };
66
67 static interaction_t *          InteractionGet( vlc_object_t * );
68 static intf_thread_t *          SearchInterface( interaction_t * );
69 static void*                    InteractionLoop( void * );
70 static void                     InteractionManage( interaction_t * );
71
72 static void                     DialogDestroy( interaction_dialog_t * );
73 static int DialogSend( interaction_dialog_t * );
74
75 #define DIALOG_INIT( type, err ) \
76         interaction_dialog_t* p_new = calloc( 1, sizeof( interaction_dialog_t ) ); \
77         if( !p_new ) return err;                        \
78         p_new->p_parent = vlc_object_hold( p_this );    \
79         p_new->b_cancelled = false;                     \
80         p_new->i_status = SENT_DIALOG;                  \
81         p_new->i_flags = 0;                             \
82         p_new->i_type = INTERACT_DIALOG_##type;         \
83         p_new->psz_returned[0] = NULL;                  \
84         p_new->psz_returned[1] = NULL
85
86 /**
87  * Helper function to create a dialogue showing a progress-bar with some info
88  *
89  * \param p_this           Parent vlc_object
90  * \param psz_title        Title for the dialog (NULL implies main intf )
91  * \param psz_status       Current status
92  * \param f_position       Current position (0.0->100.0)
93  * \param i_timeToGo       Time (in sec) to go until process is finished
94  * \return                 Dialog, for use with UserProgressUpdate
95  */
96 interaction_dialog_t *
97 __intf_Progress( vlc_object_t *p_this, const char *psz_title,
98                      const char *psz_status, float f_pos, int i_time )
99 {
100     DIALOG_INIT( ONEWAY, NULL );
101     p_new->psz_description = strdup( psz_status );
102     p_new->val.f_float = f_pos;
103     p_new->i_timeToGo = i_time;
104     p_new->psz_alternate_button = strdup( _( "Cancel" ) );
105
106     if( psz_title )
107     {
108         p_new->psz_title = strdup( psz_title );
109         p_new->i_flags = DIALOG_USER_PROGRESS;
110     }
111     else
112         p_new->i_flags = DIALOG_INTF_PROGRESS;
113
114     if( DialogSend( p_new ) == VLC_EGENERIC )
115     {
116         DialogDestroy( p_new );
117         return NULL;
118     }
119     return p_new;
120 }
121
122 /**
123  * Update a progress bar in a dialogue
124  *
125  * \param p_dialog         Dialog
126  * \param psz_status       New status
127  * \param f_position       New position (0.0->100.0)
128  * \param i_timeToGo       Time (in sec) to go until process is finished
129  * \return                 nothing
130  */
131 void intf_ProgressUpdate( interaction_dialog_t *p_dialog,
132                             const char *psz_status, float f_pos, int i_time )
133 {
134     interaction_t *p_interaction = InteractionGet( p_dialog->p_parent );
135     assert( p_interaction );
136
137     vlc_mutex_lock( &p_interaction->lock );
138     free( p_dialog->psz_description );
139     p_dialog->psz_description = strdup( psz_status );
140
141     p_dialog->val.f_float = f_pos;
142     p_dialog->i_timeToGo = i_time;
143
144     p_dialog->i_status = UPDATED_DIALOG;
145
146     vlc_cond_signal( &p_interaction->wait );
147     vlc_mutex_unlock( &p_interaction->lock );
148     vlc_object_release( p_interaction );
149 }
150
151 /**
152  * Helper function to communicate dialogue cancellations between the
153  * interface module and the caller
154  *
155  * \param p_dialog         Dialog
156  * \return                 Either true or false
157  */
158 bool intf_ProgressIsCancelled( interaction_dialog_t *p_dialog )
159 {
160     interaction_t *p_interaction = InteractionGet( p_dialog->p_parent );
161     bool b_cancel;
162
163     assert( p_interaction );
164     vlc_mutex_lock( &p_interaction->lock );
165     b_cancel = p_dialog->b_cancelled;
166     vlc_mutex_unlock( &p_interaction->lock );
167     vlc_object_release( p_interaction );
168     return b_cancel;
169 }
170
171 /**
172  * Helper function to make a dialogue asking the user for !password string
173  *
174  * \param p_this           Parent vlc_object
175  * \param psz_title        Title for the dialog
176  * \param psz_description  A description
177  * \param ppsz_usersString Returned login
178  * \return                 Clicked button code
179  */
180 int __intf_UserStringInput( vlc_object_t *p_this,
181         const char *psz_title,
182         const char *psz_description,
183         char **ppsz_usersString )
184 {
185     int i_ret;
186     DIALOG_INIT( TWOWAY, VLC_EGENERIC );
187     p_new->i_type = INTERACT_DIALOG_TWOWAY;
188     p_new->psz_title = strdup( psz_title );
189     p_new->psz_description = strdup( psz_description );
190
191     p_new->i_flags = DIALOG_PSZ_INPUT_OK_CANCEL;
192
193     i_ret = DialogSend( p_new );
194
195     if( i_ret == VLC_EGENERIC )
196         DialogDestroy( p_new );
197     else if( i_ret != DIALOG_CANCELLED )
198     {
199         *ppsz_usersString = p_new->psz_returned[0]?
200             strdup( p_new->psz_returned[0] ) : NULL;
201     }
202     return i_ret;
203 }
204
205 /**
206  * Hide an interaction dialog
207  *
208  * \param p_dialog the dialog to hide
209  * \return nothing
210  */
211 void intf_UserHide( interaction_dialog_t *p_dialog )
212 {
213     interaction_t *p_interaction = InteractionGet( p_dialog->p_parent );
214     assert( p_interaction );
215
216     vlc_mutex_lock( &p_interaction->lock );
217     p_dialog->i_status = ANSWERED_DIALOG;
218     vlc_cond_signal( &p_interaction->wait );
219     vlc_mutex_unlock( &p_interaction->lock );
220     vlc_object_release( p_interaction );
221 }
222
223 /**
224  * Create the initial interaction object
225  * (should only be used in libvlc_InternalInit, LibVLC private)
226  *
227  * \return a vlc_object_t that should be freed when done.
228  */
229 interaction_t * interaction_Init( libvlc_int_t *p_libvlc )
230 {
231     interaction_t *p_interaction;
232
233     /* Make sure we haven't yet created an interaction object */
234     assert( libvlc_priv(p_libvlc)->p_interaction == NULL );
235
236     p_interaction = vlc_custom_create( VLC_OBJECT(p_libvlc),
237                                        sizeof( *p_interaction ),
238                                        VLC_OBJECT_GENERIC, "interaction" );
239     if( !p_interaction )
240         return NULL;
241
242     vlc_object_attach( p_interaction, p_libvlc );
243     p_interaction->i_dialogs = 0;
244     p_interaction->pp_dialogs = NULL;
245     p_interaction->p_intf = NULL;
246
247     vlc_mutex_init( &p_interaction->lock );
248     vlc_cond_init( &p_interaction->wait );
249
250     if( vlc_clone( &p_interaction->thread, InteractionLoop, p_interaction,
251                    VLC_THREAD_PRIORITY_LOW ) )
252     {
253         msg_Err( p_interaction, "Interaction control thread creation failed, "
254                  "interaction will not be displayed" );
255         vlc_object_detach( p_interaction );
256         vlc_object_release( p_interaction );
257         return NULL;
258     }
259
260     return p_interaction;
261 }
262
263 void interaction_Destroy( interaction_t *p_interaction )
264 {
265     if( !p_interaction )
266         return;
267
268     vlc_cancel( p_interaction->thread );
269     vlc_join( p_interaction->thread, NULL );
270     vlc_cond_destroy( &p_interaction->wait );
271     vlc_mutex_destroy( &p_interaction->lock );
272
273     /* Remove all dialogs - Interfaces must be able to clean up their data */
274     for( int i = p_interaction->i_dialogs -1 ; i >= 0; i-- )
275     {
276         interaction_dialog_t * p_dialog = p_interaction->pp_dialogs[i];
277         DialogDestroy( p_dialog );
278         REMOVE_ELEM( p_interaction->pp_dialogs, p_interaction->i_dialogs, i );
279     }
280     vlc_object_release( p_interaction );
281 }
282
283 static vlc_mutex_t intf_lock = VLC_STATIC_MUTEX;
284
285 int interaction_Register( intf_thread_t *intf )
286 {
287     libvlc_priv_t *priv = libvlc_priv( intf->p_libvlc );
288     int ret = VLC_EGENERIC;
289
290     vlc_mutex_lock( &intf_lock );
291     if( priv->p_interaction_intf == NULL )
292     {   /* Since the interface is responsible for unregistering itself before
293          * it terminates, an object reference is not needed. */
294         priv->p_interaction_intf = intf;
295         ret = VLC_SUCCESS;
296     }
297     vlc_mutex_unlock( &intf_lock );
298     return ret;
299 }
300
301 int interaction_Unregister( intf_thread_t *intf )
302 {
303     libvlc_priv_t *priv = libvlc_priv( intf->p_libvlc );
304     int ret = VLC_EGENERIC;
305
306     vlc_mutex_lock( &intf_lock );
307     if( priv->p_interaction_intf == intf )
308     {
309         priv->p_interaction_intf = NULL;
310         ret = VLC_SUCCESS;
311     }
312     vlc_mutex_unlock( &intf_lock );
313     return ret;
314 }
315
316 /**********************************************************************
317  * The following functions are local
318  **********************************************************************/
319
320 /* Get the interaction object */
321 static interaction_t * InteractionGet( vlc_object_t *p_this )
322 {
323     interaction_t *obj = libvlc_priv(p_this->p_libvlc)->p_interaction;
324     if( obj )
325         vlc_object_hold( obj );
326     return obj;
327 }
328
329
330 /* Look for an interface suitable for interaction, and hold it. */
331 static intf_thread_t *SearchInterface( interaction_t *p_interaction )
332 {
333     libvlc_priv_t *priv = libvlc_priv( p_interaction->p_libvlc );
334     intf_thread_t *intf;
335
336     vlc_mutex_lock( &intf_lock );
337     intf = priv->p_interaction_intf;
338     if( intf != NULL )
339         vlc_object_hold( intf );
340     vlc_mutex_unlock( &intf_lock );
341
342     return intf;
343 }
344
345 /* Destroy a dialog */
346 static void DialogDestroy( interaction_dialog_t *p_dialog )
347 {
348     free( p_dialog->psz_title );
349     free( p_dialog->psz_description );
350     free( p_dialog->psz_alternate_button );
351     vlc_object_release( p_dialog->p_parent );
352     free( p_dialog );
353 }
354
355 /* Ask for the dialog to be sent to the user. Wait for answer
356  * if required */
357 static int DialogSend( interaction_dialog_t *p_dialog )
358 {
359     interaction_t *p_interaction;
360     intf_thread_t *p_intf;
361
362     if( ( p_dialog->p_parent->i_flags & OBJECT_FLAGS_NOINTERACT )
363      || !config_GetInt( p_dialog->p_parent, "interact" ) )
364         return VLC_EGENERIC;
365
366     p_interaction = InteractionGet( p_dialog->p_parent );
367     if( !p_interaction )
368         return VLC_EGENERIC;
369
370     p_dialog->p_lock = &p_interaction->lock;
371
372     p_intf = SearchInterface( p_interaction );
373     if( p_intf == NULL )
374     {
375         p_dialog->i_return = DIALOG_DEFAULT; /* Give default answer */
376
377         /* Pretend we have hidden and destroyed it */
378         p_dialog->i_status = HIDING_DIALOG;
379         vlc_object_release( p_interaction );
380         return VLC_SUCCESS;
381     }
382     p_dialog->p_interface = p_intf;
383
384     p_dialog->i_action = INTERACT_NEW;
385     var_SetAddress( p_dialog->p_interface, "interaction", p_dialog );
386
387     /* Check if we have already added this dialog */
388     vlc_mutex_lock( &p_interaction->lock );
389     /* Add it to the queue, the main loop will send the orders to the
390      * interface */
391     INSERT_ELEM( p_interaction->pp_dialogs, p_interaction->i_dialogs,
392                  p_interaction->i_dialogs,  p_dialog );
393
394     if( p_dialog->i_type == INTERACT_DIALOG_TWOWAY ) /* Wait for answer */
395     {
396         vlc_cond_signal( &p_interaction->wait );
397         while( p_dialog->i_status != ANSWERED_DIALOG &&
398                p_dialog->i_status != HIDING_DIALOG &&
399                p_dialog->i_status != HIDDEN_DIALOG &&
400                !p_dialog->p_parent->b_die )
401         {
402             vlc_mutex_unlock( &p_interaction->lock );
403             msleep( 100000 );
404             vlc_mutex_lock( &p_interaction->lock );
405         }
406         if( p_dialog->p_parent->b_die )
407         {
408             p_dialog->i_return = DIALOG_CANCELLED;
409             p_dialog->i_status = ANSWERED_DIALOG;
410         }
411     }
412     p_dialog->i_flags |= DIALOG_GOT_ANSWER;
413     vlc_cond_signal( &p_interaction->wait );
414     vlc_mutex_unlock( &p_interaction->lock );
415     vlc_object_release( p_interaction );
416     if( p_dialog->i_type == INTERACT_DIALOG_TWOWAY )
417         return p_dialog->i_return;
418     else
419         return VLC_SUCCESS;
420 }
421
422 static void* InteractionLoop( void *p_this )
423 {
424     interaction_t *p_interaction = p_this;
425
426     vlc_mutex_lock( &p_interaction->lock );
427     mutex_cleanup_push( &p_interaction->lock );
428     for( ;; )
429     {
430         int canc = vlc_savecancel();
431         InteractionManage( p_interaction );
432         vlc_restorecancel( canc );
433
434         vlc_cond_wait( &p_interaction->wait, &p_interaction->lock );
435     }
436     vlc_cleanup_pop( );
437     assert( 0 );
438 }
439
440 /**
441  * The main interaction processing loop
442  *
443  * \param p_interaction the interaction object
444  * \return nothing
445  */
446
447 static void InteractionManage( interaction_t *p_interaction )
448 {
449     vlc_value_t val;
450     int i_index;
451
452     for( i_index = 0 ; i_index < p_interaction->i_dialogs; i_index ++ )
453     {
454         interaction_dialog_t *p_dialog = p_interaction->pp_dialogs[i_index];
455         switch( p_dialog->i_status )
456         {
457         case ANSWERED_DIALOG:
458             /* Ask interface to hide it */
459             p_dialog->i_action = INTERACT_HIDE;
460             val.p_address = p_dialog;
461             var_Set( p_dialog->p_interface, "interaction", val );
462             p_dialog->i_status = HIDING_DIALOG;
463             break;
464         case UPDATED_DIALOG:
465             p_dialog->i_action = INTERACT_UPDATE;
466             val.p_address = p_dialog;
467             var_Set( p_dialog->p_interface, "interaction", val );
468             p_dialog->i_status = SENT_DIALOG;
469             break;
470         case HIDDEN_DIALOG:
471             if( !(p_dialog->i_flags & DIALOG_GOT_ANSWER) ) break;
472             p_dialog->i_action = INTERACT_DESTROY;
473             val.p_address = p_dialog;
474             var_Set( p_dialog->p_interface, "interaction", val );
475             break;
476         case DESTROYED_DIALOG:
477             /* Interface has now destroyed it, remove it */
478             REMOVE_ELEM( p_interaction->pp_dialogs, p_interaction->i_dialogs,
479                          i_index);
480             i_index--;
481             DialogDestroy( p_dialog );
482             break;
483         }
484     }
485 }