]> git.sesse.net Git - vlc/blob - src/interface/interaction.c
90575699837b33c5941773ce8452ee64759cfb31
[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 void*                    InteractionLoop( void * );
68 static void                     InteractionManage( interaction_t * );
69
70 static void                     DialogDestroy( interaction_dialog_t * );
71
72 #define DIALOG_INIT( type, err ) \
73         interaction_dialog_t* p_new = calloc( 1, sizeof( interaction_dialog_t ) ); \
74         if( !p_new ) return err;                        \
75         p_new->p_parent = vlc_object_hold( p_this );    \
76         p_new->b_cancelled = false;                     \
77         p_new->i_status = SENT_DIALOG;                  \
78         p_new->i_flags = 0;                             \
79         p_new->i_type = INTERACT_DIALOG_##type;         \
80         p_new->psz_returned[0] = NULL;                  \
81         p_new->psz_returned[1] = NULL
82
83 /**
84  * Create the initial interaction object
85  * (should only be used in libvlc_InternalInit, LibVLC private)
86  *
87  * \return a vlc_object_t that should be freed when done.
88  */
89 interaction_t * interaction_Init( libvlc_int_t *p_libvlc )
90 {
91     interaction_t *p_interaction;
92
93     /* Make sure we haven't yet created an interaction object */
94     assert( libvlc_priv(p_libvlc)->p_interaction == NULL );
95
96     p_interaction = vlc_custom_create( VLC_OBJECT(p_libvlc),
97                                        sizeof( *p_interaction ),
98                                        VLC_OBJECT_GENERIC, "interaction" );
99     if( !p_interaction )
100         return NULL;
101
102     vlc_object_attach( p_interaction, p_libvlc );
103     p_interaction->i_dialogs = 0;
104     p_interaction->pp_dialogs = NULL;
105     p_interaction->p_intf = NULL;
106
107     vlc_mutex_init( &p_interaction->lock );
108     vlc_cond_init( &p_interaction->wait );
109
110     if( vlc_clone( &p_interaction->thread, InteractionLoop, p_interaction,
111                    VLC_THREAD_PRIORITY_LOW ) )
112     {
113         msg_Err( p_interaction, "Interaction control thread creation failed, "
114                  "interaction will not be displayed" );
115         vlc_object_detach( p_interaction );
116         vlc_object_release( p_interaction );
117         return NULL;
118     }
119
120     return p_interaction;
121 }
122
123 void interaction_Destroy( interaction_t *p_interaction )
124 {
125     if( !p_interaction )
126         return;
127
128     vlc_cancel( p_interaction->thread );
129     vlc_join( p_interaction->thread, NULL );
130     vlc_cond_destroy( &p_interaction->wait );
131     vlc_mutex_destroy( &p_interaction->lock );
132
133     /* Remove all dialogs - Interfaces must be able to clean up their data */
134     for( int i = p_interaction->i_dialogs -1 ; i >= 0; i-- )
135     {
136         interaction_dialog_t * p_dialog = p_interaction->pp_dialogs[i];
137         DialogDestroy( p_dialog );
138         REMOVE_ELEM( p_interaction->pp_dialogs, p_interaction->i_dialogs, i );
139     }
140     vlc_object_release( p_interaction );
141 }
142
143 static vlc_mutex_t intf_lock = VLC_STATIC_MUTEX;
144
145 int interaction_Register( intf_thread_t *intf )
146 {
147     libvlc_priv_t *priv = libvlc_priv( intf->p_libvlc );
148     int ret = VLC_EGENERIC;
149
150     vlc_mutex_lock( &intf_lock );
151     if( priv->p_interaction_intf == NULL )
152     {   /* Since the interface is responsible for unregistering itself before
153          * it terminates, an object reference is not needed. */
154         priv->p_interaction_intf = intf;
155         ret = VLC_SUCCESS;
156     }
157     vlc_mutex_unlock( &intf_lock );
158     return ret;
159 }
160
161 int interaction_Unregister( intf_thread_t *intf )
162 {
163     libvlc_priv_t *priv = libvlc_priv( intf->p_libvlc );
164     int ret = VLC_EGENERIC;
165
166     vlc_mutex_lock( &intf_lock );
167     if( priv->p_interaction_intf == intf )
168     {
169         priv->p_interaction_intf = NULL;
170         ret = VLC_SUCCESS;
171     }
172     vlc_mutex_unlock( &intf_lock );
173     return ret;
174 }
175
176 /**********************************************************************
177  * The following functions are local
178  **********************************************************************/
179
180 /* Destroy a dialog */
181 static void DialogDestroy( interaction_dialog_t *p_dialog )
182 {
183     free( p_dialog->psz_title );
184     free( p_dialog->psz_description );
185     free( p_dialog->psz_alternate_button );
186     vlc_object_release( p_dialog->p_parent );
187     free( p_dialog );
188 }
189
190 static void* InteractionLoop( void *p_this )
191 {
192     interaction_t *p_interaction = p_this;
193
194     vlc_mutex_lock( &p_interaction->lock );
195     mutex_cleanup_push( &p_interaction->lock );
196     for( ;; )
197     {
198         int canc = vlc_savecancel();
199         InteractionManage( p_interaction );
200         vlc_restorecancel( canc );
201
202         vlc_cond_wait( &p_interaction->wait, &p_interaction->lock );
203     }
204     vlc_cleanup_pop( );
205     assert( 0 );
206 }
207
208 /**
209  * The main interaction processing loop
210  *
211  * \param p_interaction the interaction object
212  * \return nothing
213  */
214
215 static void InteractionManage( interaction_t *p_interaction )
216 {
217     vlc_value_t val;
218     int i_index;
219
220     for( i_index = 0 ; i_index < p_interaction->i_dialogs; i_index ++ )
221     {
222         interaction_dialog_t *p_dialog = p_interaction->pp_dialogs[i_index];
223         switch( p_dialog->i_status )
224         {
225         case ANSWERED_DIALOG:
226             /* Ask interface to hide it */
227             p_dialog->i_action = INTERACT_HIDE;
228             val.p_address = p_dialog;
229             var_Set( p_dialog->p_interface, "interaction", val );
230             p_dialog->i_status = HIDING_DIALOG;
231             break;
232         case UPDATED_DIALOG:
233             p_dialog->i_action = INTERACT_UPDATE;
234             val.p_address = p_dialog;
235             var_Set( p_dialog->p_interface, "interaction", val );
236             p_dialog->i_status = SENT_DIALOG;
237             break;
238         case HIDDEN_DIALOG:
239             if( !(p_dialog->i_flags & DIALOG_GOT_ANSWER) ) break;
240             p_dialog->i_action = INTERACT_DESTROY;
241             val.p_address = p_dialog;
242             var_Set( p_dialog->p_interface, "interaction", val );
243             break;
244         case DESTROYED_DIALOG:
245             /* Interface has now destroyed it, remove it */
246             REMOVE_ELEM( p_interaction->pp_dialogs, p_interaction->i_dialogs,
247                          i_index);
248             i_index--;
249             DialogDestroy( p_dialog );
250             break;
251         }
252     }
253 }