]> git.sesse.net Git - vlc/blob - src/interface/interaction.c
dialog_Progress replacement for intf_UserProgress
[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  * Hide an interaction dialog
173  *
174  * \param p_dialog the dialog to hide
175  * \return nothing
176  */
177 void intf_UserHide( interaction_dialog_t *p_dialog )
178 {
179     interaction_t *p_interaction = InteractionGet( p_dialog->p_parent );
180     assert( p_interaction );
181
182     vlc_mutex_lock( &p_interaction->lock );
183     p_dialog->i_status = ANSWERED_DIALOG;
184     vlc_cond_signal( &p_interaction->wait );
185     vlc_mutex_unlock( &p_interaction->lock );
186     vlc_object_release( p_interaction );
187 }
188
189 /**
190  * Create the initial interaction object
191  * (should only be used in libvlc_InternalInit, LibVLC private)
192  *
193  * \return a vlc_object_t that should be freed when done.
194  */
195 interaction_t * interaction_Init( libvlc_int_t *p_libvlc )
196 {
197     interaction_t *p_interaction;
198
199     /* Make sure we haven't yet created an interaction object */
200     assert( libvlc_priv(p_libvlc)->p_interaction == NULL );
201
202     p_interaction = vlc_custom_create( VLC_OBJECT(p_libvlc),
203                                        sizeof( *p_interaction ),
204                                        VLC_OBJECT_GENERIC, "interaction" );
205     if( !p_interaction )
206         return NULL;
207
208     vlc_object_attach( p_interaction, p_libvlc );
209     p_interaction->i_dialogs = 0;
210     p_interaction->pp_dialogs = NULL;
211     p_interaction->p_intf = NULL;
212
213     vlc_mutex_init( &p_interaction->lock );
214     vlc_cond_init( &p_interaction->wait );
215
216     if( vlc_clone( &p_interaction->thread, InteractionLoop, p_interaction,
217                    VLC_THREAD_PRIORITY_LOW ) )
218     {
219         msg_Err( p_interaction, "Interaction control thread creation failed, "
220                  "interaction will not be displayed" );
221         vlc_object_detach( p_interaction );
222         vlc_object_release( p_interaction );
223         return NULL;
224     }
225
226     return p_interaction;
227 }
228
229 void interaction_Destroy( interaction_t *p_interaction )
230 {
231     if( !p_interaction )
232         return;
233
234     vlc_cancel( p_interaction->thread );
235     vlc_join( p_interaction->thread, NULL );
236     vlc_cond_destroy( &p_interaction->wait );
237     vlc_mutex_destroy( &p_interaction->lock );
238
239     /* Remove all dialogs - Interfaces must be able to clean up their data */
240     for( int i = p_interaction->i_dialogs -1 ; i >= 0; i-- )
241     {
242         interaction_dialog_t * p_dialog = p_interaction->pp_dialogs[i];
243         DialogDestroy( p_dialog );
244         REMOVE_ELEM( p_interaction->pp_dialogs, p_interaction->i_dialogs, i );
245     }
246     vlc_object_release( p_interaction );
247 }
248
249 static vlc_mutex_t intf_lock = VLC_STATIC_MUTEX;
250
251 int interaction_Register( intf_thread_t *intf )
252 {
253     libvlc_priv_t *priv = libvlc_priv( intf->p_libvlc );
254     int ret = VLC_EGENERIC;
255
256     vlc_mutex_lock( &intf_lock );
257     if( priv->p_interaction_intf == NULL )
258     {   /* Since the interface is responsible for unregistering itself before
259          * it terminates, an object reference is not needed. */
260         priv->p_interaction_intf = intf;
261         ret = VLC_SUCCESS;
262     }
263     vlc_mutex_unlock( &intf_lock );
264     return ret;
265 }
266
267 int interaction_Unregister( intf_thread_t *intf )
268 {
269     libvlc_priv_t *priv = libvlc_priv( intf->p_libvlc );
270     int ret = VLC_EGENERIC;
271
272     vlc_mutex_lock( &intf_lock );
273     if( priv->p_interaction_intf == intf )
274     {
275         priv->p_interaction_intf = NULL;
276         ret = VLC_SUCCESS;
277     }
278     vlc_mutex_unlock( &intf_lock );
279     return ret;
280 }
281
282 /**********************************************************************
283  * The following functions are local
284  **********************************************************************/
285
286 /* Get the interaction object */
287 static interaction_t * InteractionGet( vlc_object_t *p_this )
288 {
289     interaction_t *obj = libvlc_priv(p_this->p_libvlc)->p_interaction;
290     if( obj )
291         vlc_object_hold( obj );
292     return obj;
293 }
294
295
296 /* Look for an interface suitable for interaction, and hold it. */
297 static intf_thread_t *SearchInterface( interaction_t *p_interaction )
298 {
299     libvlc_priv_t *priv = libvlc_priv( p_interaction->p_libvlc );
300     intf_thread_t *intf;
301
302     vlc_mutex_lock( &intf_lock );
303     intf = priv->p_interaction_intf;
304     if( intf != NULL )
305         vlc_object_hold( intf );
306     vlc_mutex_unlock( &intf_lock );
307
308     return intf;
309 }
310
311 /* Destroy a dialog */
312 static void DialogDestroy( interaction_dialog_t *p_dialog )
313 {
314     free( p_dialog->psz_title );
315     free( p_dialog->psz_description );
316     free( p_dialog->psz_alternate_button );
317     vlc_object_release( p_dialog->p_parent );
318     free( p_dialog );
319 }
320
321 /* Ask for the dialog to be sent to the user. Wait for answer
322  * if required */
323 static int DialogSend( interaction_dialog_t *p_dialog )
324 {
325     interaction_t *p_interaction;
326     intf_thread_t *p_intf;
327
328     if( ( p_dialog->p_parent->i_flags & OBJECT_FLAGS_NOINTERACT )
329      || !config_GetInt( p_dialog->p_parent, "interact" ) )
330         return VLC_EGENERIC;
331
332     p_interaction = InteractionGet( p_dialog->p_parent );
333     if( !p_interaction )
334         return VLC_EGENERIC;
335
336     p_dialog->p_lock = &p_interaction->lock;
337
338     p_intf = SearchInterface( p_interaction );
339     if( p_intf == NULL )
340     {
341         p_dialog->i_return = DIALOG_DEFAULT; /* Give default answer */
342
343         /* Pretend we have hidden and destroyed it */
344         p_dialog->i_status = HIDING_DIALOG;
345         vlc_object_release( p_interaction );
346         return VLC_SUCCESS;
347     }
348     p_dialog->p_interface = p_intf;
349
350     p_dialog->i_action = INTERACT_NEW;
351     var_SetAddress( p_dialog->p_interface, "interaction", p_dialog );
352
353     /* Check if we have already added this dialog */
354     vlc_mutex_lock( &p_interaction->lock );
355     /* Add it to the queue, the main loop will send the orders to the
356      * interface */
357     INSERT_ELEM( p_interaction->pp_dialogs, p_interaction->i_dialogs,
358                  p_interaction->i_dialogs,  p_dialog );
359
360     if( p_dialog->i_type == INTERACT_DIALOG_TWOWAY ) /* Wait for answer */
361     {
362         vlc_cond_signal( &p_interaction->wait );
363         while( p_dialog->i_status != ANSWERED_DIALOG &&
364                p_dialog->i_status != HIDING_DIALOG &&
365                p_dialog->i_status != HIDDEN_DIALOG &&
366                !p_dialog->p_parent->b_die )
367         {
368             vlc_mutex_unlock( &p_interaction->lock );
369             msleep( 100000 );
370             vlc_mutex_lock( &p_interaction->lock );
371         }
372         if( p_dialog->p_parent->b_die )
373         {
374             p_dialog->i_return = DIALOG_CANCELLED;
375             p_dialog->i_status = ANSWERED_DIALOG;
376         }
377     }
378     p_dialog->i_flags |= DIALOG_GOT_ANSWER;
379     vlc_cond_signal( &p_interaction->wait );
380     vlc_mutex_unlock( &p_interaction->lock );
381     vlc_object_release( p_interaction );
382     if( p_dialog->i_type == INTERACT_DIALOG_TWOWAY )
383         return p_dialog->i_return;
384     else
385         return VLC_SUCCESS;
386 }
387
388 static void* InteractionLoop( void *p_this )
389 {
390     interaction_t *p_interaction = p_this;
391
392     vlc_mutex_lock( &p_interaction->lock );
393     mutex_cleanup_push( &p_interaction->lock );
394     for( ;; )
395     {
396         int canc = vlc_savecancel();
397         InteractionManage( p_interaction );
398         vlc_restorecancel( canc );
399
400         vlc_cond_wait( &p_interaction->wait, &p_interaction->lock );
401     }
402     vlc_cleanup_pop( );
403     assert( 0 );
404 }
405
406 /**
407  * The main interaction processing loop
408  *
409  * \param p_interaction the interaction object
410  * \return nothing
411  */
412
413 static void InteractionManage( interaction_t *p_interaction )
414 {
415     vlc_value_t val;
416     int i_index;
417
418     for( i_index = 0 ; i_index < p_interaction->i_dialogs; i_index ++ )
419     {
420         interaction_dialog_t *p_dialog = p_interaction->pp_dialogs[i_index];
421         switch( p_dialog->i_status )
422         {
423         case ANSWERED_DIALOG:
424             /* Ask interface to hide it */
425             p_dialog->i_action = INTERACT_HIDE;
426             val.p_address = p_dialog;
427             var_Set( p_dialog->p_interface, "interaction", val );
428             p_dialog->i_status = HIDING_DIALOG;
429             break;
430         case UPDATED_DIALOG:
431             p_dialog->i_action = INTERACT_UPDATE;
432             val.p_address = p_dialog;
433             var_Set( p_dialog->p_interface, "interaction", val );
434             p_dialog->i_status = SENT_DIALOG;
435             break;
436         case HIDDEN_DIALOG:
437             if( !(p_dialog->i_flags & DIALOG_GOT_ANSWER) ) break;
438             p_dialog->i_action = INTERACT_DESTROY;
439             val.p_address = p_dialog;
440             var_Set( p_dialog->p_interface, "interaction", val );
441             break;
442         case DESTROYED_DIALOG:
443             /* Interface has now destroyed it, remove it */
444             REMOVE_ELEM( p_interaction->pp_dialogs, p_interaction->i_dialogs,
445                          i_index);
446             i_index--;
447             DialogDestroy( p_dialog );
448             break;
449         }
450     }
451 }