]> git.sesse.net Git - vlc/blob - src/interface/interaction.c
* Handle dialogs needing answer
[vlc] / src / interface / interaction.c
1 /*****************************************************************************
2  * interaction.c: User interaction functions
3  *****************************************************************************
4  * Copyright (C) 1998-2004 VideoLAN
5  * $Id: interface.c 10147 2005-03-05 17:18:30Z gbazin $
6  *
7  * Authors: ClĂ©ment Stenac <zorglub@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /**
25  *   \file
26  *   This file contains functions related to user interaction management
27  */
28
29 /*****************************************************************************
30  * Preamble
31  *****************************************************************************/
32 #include <stdlib.h>                                      /* free(), strtol() */
33 #include <stdio.h>                                                   /* FILE */
34 #include <string.h>                                            /* strerror() */
35
36 #include <vlc/vlc.h>
37 #include <vlc/input.h>
38
39 #include "vlc_interaction.h"
40 #include "vlc_interface.h"
41 #include "vlc_playlist.h"
42
43 /*****************************************************************************
44  * Local prototypes
45  *****************************************************************************/
46 static void                  intf_InteractionInit( playlist_t *p_playlist );
47 static interaction_t *       intf_InteractionGet( vlc_object_t *p_this );
48 static void                  intf_InteractionSearchInterface( interaction_t *
49                                                           p_interaction );
50 static int                   intf_WaitAnswer( interaction_t *p_interact,
51                              interaction_dialog_t *p_dialog );
52 static int                   intf_Send( interaction_t *p_interact,
53                              interaction_dialog_t *p_dialog );
54 static interaction_dialog_t *intf_InteractionGetById( vlc_object_t* , int );
55
56 /**
57  * Send an interaction element to the user
58  *
59  * \param p_this the calling vlc_object_t
60  * \param p_interact the interaction element
61  * \return VLC_SUCCESS or an error code
62  */
63 int  __intf_Interact( vlc_object_t *p_this, interaction_dialog_t *
64                                     p_dialog )
65 {
66     interaction_t *p_interaction = intf_InteractionGet( p_this );
67
68     /* Get an id, if we don't already have one */
69     if( p_dialog->i_id == 0 )
70     {
71         p_dialog->i_id = ++p_interaction->i_last_id;
72     }
73
74     p_dialog->p_interaction = p_interaction;
75     p_dialog->p_parent = p_this;
76
77     if( p_dialog->i_type == INTERACT_DIALOG_TWOWAY )
78     {
79         return intf_WaitAnswer( p_interaction, p_dialog );
80     }
81     else
82     {
83         p_dialog->i_flags |=  DIALOG_GOT_ANSWER;
84         return intf_Send( p_interaction, p_dialog );
85     }
86 }
87
88 /**
89  * Destroy the interaction system
90  */
91 void intf_InteractionDestroy( interaction_t *p_interaction )
92 {
93     /// \todo Code this, and call it
94 }
95
96 /**
97  * The main interaction processing loop
98  * This function is called from the playlist loop
99  *
100  * \param p_playlist the parent playlist
101  * \return nothing
102  */
103 void intf_InteractionManage( playlist_t *p_playlist )
104 {
105     vlc_value_t val;
106     int i_index;
107     interaction_t *p_interaction;
108
109     p_interaction = p_playlist->p_interaction;
110
111     // Nothing to do
112     if( p_interaction->i_dialogs == 0 ) return;
113
114     vlc_mutex_lock( &p_interaction->object_lock );
115
116     intf_InteractionSearchInterface( p_interaction );
117
118     if( !p_interaction->p_intf )
119     {
120         // We mark all dialogs as answered with their "default" answer
121         for( i_index = 0 ; i_index < p_interaction->i_dialogs; i_index ++ )
122         {
123             interaction_dialog_t *p_dialog = p_interaction->pp_dialogs[i_index];
124             p_dialog->i_return = DIALOG_DEFAULT;
125             if( p_dialog->i_flags & DIALOG_OK_CANCEL )
126                 p_dialog->i_return = DIALOG_CANCELLED;
127             if( p_dialog->i_flags & DIALOG_YES_NO_CANCEL )
128                 p_dialog->i_return = DIALOG_CANCELLED;
129             p_dialog->i_status = ANSWERED_DIALOG;
130         }
131     }
132     else
133     {
134         vlc_object_yield( p_interaction->p_intf );
135     }
136
137     for( i_index = 0 ; i_index < p_interaction->i_dialogs; i_index ++ )
138     {
139         interaction_dialog_t *p_dialog = p_interaction->pp_dialogs[i_index];
140         switch( p_dialog->i_status )
141         {
142         case ANSWERED_DIALOG:
143             // Ask interface to hide it
144             msg_Dbg( p_interaction, "Hiding dialog %i", p_dialog->i_id );
145             p_dialog->i_action = INTERACT_HIDE;
146             val.p_address = p_dialog;
147             if( p_interaction->p_intf )
148                 var_Set( p_interaction->p_intf, "interaction", val );
149             p_dialog->i_status = HIDING_DIALOG;
150             break;
151         case UPDATED_DIALOG:
152             p_dialog->i_action = INTERACT_UPDATE;
153             val.p_address = p_dialog;
154             if( p_interaction->p_intf )
155                 var_Set( p_interaction->p_intf, "interaction", val );
156             p_dialog->i_status = SENT_DIALOG;
157             msg_Dbg( p_interaction, "Updating dialog %i, %i widgets",
158                                     p_dialog->i_id, p_dialog->i_widgets );
159             break;
160         case HIDDEN_DIALOG:
161             if( !(p_dialog->i_flags & DIALOG_GOT_ANSWER) ) break;
162             if( !(p_dialog->i_flags & DIALOG_REUSABLE) )
163             {
164                 msg_Dbg( p_interaction, "Destroying dialog %i",
165                                                 p_dialog->i_id );
166                 p_dialog->i_action = INTERACT_DESTROY;
167                 val.p_address = p_dialog;
168                 if( p_interaction->p_intf )
169                     var_Set( p_interaction->p_intf, "interaction", val );
170             }
171             break;
172         case DESTROYED_DIALOG:
173             // Interface has now destroyed it, remove it
174             /// \todo Remove it from the list
175             /// \todo Free data fields
176             free( p_dialog );
177         case NEW_DIALOG:
178             // This is truly a new dialog, send it.
179             p_dialog->i_action = INTERACT_NEW;
180             val.p_address = p_dialog;
181             if( p_interaction->p_intf )
182                 var_Set( p_interaction->p_intf, "interaction", val );
183             p_dialog->i_status = SENT_DIALOG;
184             break;
185         }
186     }
187
188     if( p_interaction->p_intf )
189     {
190         vlc_object_release( p_interaction->p_intf );
191     }
192
193     vlc_mutex_unlock( &p_playlist->p_interaction->object_lock );
194 }
195
196
197
198 #define INTERACT_INIT( new )                                            \
199         new = (interaction_dialog_t*)malloc(                            \
200                         sizeof( interaction_dialog_t ) );               \
201         new->i_widgets = 0;                                             \
202         new->pp_widgets = NULL;                                         \
203         new->psz_title = NULL;                                          \
204         new->psz_description = NULL;                                    \
205         new->i_id = 0;                                                  \
206         new->i_status = NEW_DIALOG;
207
208 #define INTERACT_FREE( new )                                            \
209         if( new->psz_title ) free( new->psz_title );                    \
210         if( new->psz_description ) free( new->psz_description );
211
212 /** Helper function to send an error message
213  *  \param p_this     Parent vlc_object
214  *  \param i_id       A predefined ID, 0 if not applicable
215  *  \param psz_title  Title for the dialog
216  *  \param psz_format The message to display
217  *  */
218 void __intf_UserFatal( vlc_object_t *p_this,
219                        const char *psz_title,
220                        const char *psz_format, ... )
221 {
222     va_list args;
223     interaction_dialog_t *p_new = NULL;
224     user_widget_t *p_widget = NULL;
225     int i_id = DIALOG_ERRORS;
226
227     if( i_id > 0 )
228     {
229         p_new = intf_InteractionGetById( p_this, i_id );
230     }
231     if( !p_new )
232     {
233         INTERACT_INIT( p_new );
234         if( i_id > 0 ) p_new->i_id = i_id ;
235     }
236     else
237     {
238         p_new->i_status = UPDATED_DIALOG;
239     }
240
241     p_new->i_flags |= DIALOG_REUSABLE;
242
243     p_new->i_type = INTERACT_DIALOG_ONEWAY;
244     p_new->psz_title = strdup( psz_title );
245
246     p_widget = (user_widget_t* )malloc( sizeof( user_widget_t ) );
247
248     p_widget->i_type = WIDGET_TEXT;
249
250     va_start( args, psz_format );
251     vasprintf( &p_widget->psz_text, psz_format, args );
252     va_end( args );
253
254     INSERT_ELEM ( p_new->pp_widgets,
255                   p_new->i_widgets,
256                   p_new->i_widgets,
257                   p_widget );
258
259     intf_Interact( p_this, p_new );
260 }
261
262 /** Helper function to make a login/password box
263  *  \param p_this           Parent vlc_object
264  *  \param psz_title        Title for the dialog
265  *  \param psz_description  A description
266  *  \param ppsz_login       Returned login
267  *  \param ppsz_password    Returned password
268  *  \return                 1 if user clicked Cancel, 0 if OK
269  */
270 int __intf_UserLoginPassword( vlc_object_t *p_this,
271                               const char *psz_title,
272                               const char *psz_description,
273                               char **ppsz_login,
274                               char **ppsz_password )
275 {
276     int i_ret;
277     interaction_dialog_t *p_new = NULL;
278     user_widget_t *p_widget = NULL;
279
280     INTERACT_INIT( p_new );
281
282     p_new->i_type = INTERACT_DIALOG_TWOWAY;
283     p_new->psz_title = strdup( psz_title );
284
285     /* Text */
286     p_widget = (user_widget_t* )malloc( sizeof( user_widget_t ) );
287     p_widget->i_type = WIDGET_TEXT;
288     p_widget->psz_text = strdup( psz_description );
289     INSERT_ELEM ( p_new->pp_widgets, p_new->i_widgets,
290                   p_new->i_widgets,  p_widget );
291
292     /* Login */
293     p_widget = (user_widget_t* )malloc( sizeof( user_widget_t ) );
294     p_widget->i_type = WIDGET_INPUT_TEXT;
295     p_widget->psz_text = strdup( _("Login") );
296     p_widget->val.psz_string = NULL;
297     INSERT_ELEM ( p_new->pp_widgets, p_new->i_widgets,
298                   p_new->i_widgets,  p_widget );
299
300     /* Password */
301     p_widget = (user_widget_t* )malloc( sizeof( user_widget_t ) );
302     p_widget->i_type = WIDGET_INPUT_TEXT;
303     p_widget->psz_text = strdup( _("Password") );
304     p_widget->val.psz_string = NULL;
305     INSERT_ELEM ( p_new->pp_widgets, p_new->i_widgets,
306                   p_new->i_widgets,  p_widget );
307
308     p_new->i_flags = DIALOG_OK_CANCEL;
309
310     i_ret = intf_Interact( p_this, p_new );
311
312     if( i_ret == DIALOG_OK_YES )
313     {
314         *ppsz_login = strdup( p_new->pp_widgets[1]->val.psz_string );
315         *ppsz_password = strdup( p_new->pp_widgets[2]->val.psz_string );
316     }
317     return i_ret;
318 }
319
320 /**********************************************************************
321  * The following functions are local
322  **********************************************************************/
323
324 /* Get the interaction object. Create it if needed */
325 static interaction_t * intf_InteractionGet( vlc_object_t *p_this )
326 {
327     playlist_t *p_playlist;
328     interaction_t *p_interaction;
329
330     p_playlist = (playlist_t*) vlc_object_find( p_this, VLC_OBJECT_PLAYLIST,
331                                                 FIND_ANYWHERE );
332
333     if( !p_playlist )
334     {
335         return NULL;
336     }
337
338     if( p_playlist->p_interaction == NULL )
339     {
340         intf_InteractionInit( p_playlist );
341     }
342
343     p_interaction = p_playlist->p_interaction;
344
345     vlc_object_release( p_playlist );
346
347     return p_interaction;
348 }
349
350 /* Create the interaction object in the given playlist object */
351 static void intf_InteractionInit( playlist_t *p_playlist )
352 {
353     interaction_t *p_interaction;
354
355     msg_Dbg( p_playlist, "initializing interaction system" );
356
357     p_interaction = vlc_object_create( VLC_OBJECT( p_playlist ),
358                                        sizeof( interaction_t ) );
359     if( !p_interaction )
360     {
361         msg_Err( p_playlist,"out of memory" );
362         return;
363     }
364
365     p_interaction->i_dialogs = 0;
366     p_interaction->pp_dialogs = NULL;
367     p_interaction->p_intf = NULL;
368     p_interaction->i_last_id = DIALOG_LAST_PREDEFINED + 1;
369
370     vlc_mutex_init( p_interaction , &p_interaction->object_lock );
371
372     p_playlist->p_interaction  = p_interaction;
373 }
374
375 /* Look for an interface suitable for interaction */
376 static void intf_InteractionSearchInterface( interaction_t *p_interaction )
377 {
378     vlc_list_t  *p_list;
379     int          i_index;
380
381     p_interaction->p_intf = NULL;
382
383     p_list = vlc_list_find( p_interaction, VLC_OBJECT_INTF, FIND_ANYWHERE );
384     if( !p_list )
385     {
386         msg_Err( p_interaction, "Unable to create module list" );
387         return;
388     }
389
390     for( i_index = 0; i_index < p_list->i_count; i_index ++ )
391     {
392         intf_thread_t *p_intf = (intf_thread_t *)
393                                         p_list->p_values[i_index].p_object;
394         if( p_intf->b_interaction )
395         {
396             p_interaction->p_intf = p_intf;
397             break;
398         }
399     }
400     vlc_list_release ( p_list );
401 }
402
403 /* Add a dialog to the queue and wait for answer */
404 static int intf_WaitAnswer( interaction_t *p_interact, interaction_dialog_t *p_dialog )
405 {
406     int i;
407     vlc_bool_t b_found = VLC_FALSE;
408     vlc_mutex_lock( &p_interact->object_lock );
409     for( i = 0 ; i< p_interact->i_dialogs; i++ )
410     {
411         if( p_interact->pp_dialogs[i]->i_id == p_dialog->i_id )
412         {
413             b_found = VLC_TRUE;
414         }
415     }
416     if( ! b_found )
417     {
418         INSERT_ELEM( p_interact->pp_dialogs,
419                      p_interact->i_dialogs,
420                      p_interact->i_dialogs,
421                      p_dialog );
422     }
423     else
424         p_dialog->i_status = UPDATED_DIALOG;
425     vlc_mutex_unlock( &p_interact->object_lock );
426
427     /// \todo Check that the initiating object is not dying
428     while( p_dialog->i_status != ANSWERED_DIALOG &&
429            p_dialog->i_status != HIDING_DIALOG &&
430            !p_dialog->p_parent->b_die )
431     {
432         msleep( 100000 );
433     }
434     /// \todo locking
435     if( p_dialog->p_parent->b_die )
436     {
437         p_dialog->i_return = DIALOG_CANCELLED;
438         p_dialog->i_status = ANSWERED_DIALOG;
439     }
440     p_dialog->i_flags |= DIALOG_GOT_ANSWER;
441     return p_dialog->i_return;
442 }
443
444 /* Add a dialog to the queue and return */
445 static int intf_Send( interaction_t *p_interact, interaction_dialog_t *p_dialog )
446 {
447     int i;
448     vlc_bool_t b_found = VLC_FALSE;
449     vlc_mutex_lock( &p_interact->object_lock );
450
451     for( i = 0 ; i< p_interact->i_dialogs; i++ )
452     {
453         if( p_interact->pp_dialogs[i]->i_id == p_dialog->i_id )
454         {
455             b_found = VLC_TRUE;
456         }
457     }
458     if( !b_found )
459     {
460         INSERT_ELEM( p_interact->pp_dialogs,
461                      p_interact->i_dialogs,
462                      p_interact->i_dialogs,
463                      p_dialog );
464     }
465     else
466         p_dialog->i_status = UPDATED_DIALOG;
467     vlc_mutex_unlock( &p_interact->object_lock );
468     return VLC_SUCCESS;
469 }
470
471 /* Find an interaction dialog by its id */
472 static interaction_dialog_t *intf_InteractionGetById( vlc_object_t* p_this,
473                                                        int i_id )
474 {
475     interaction_t *p_interaction = intf_InteractionGet( p_this );
476     int i;
477
478     if( !p_interaction ) return NULL;
479
480     for( i = 0 ; i< p_interaction->i_dialogs; i++ )
481     {
482         if( p_interaction->pp_dialogs[i]->i_id == i_id )
483         {
484             return p_interaction->pp_dialogs[i];
485         }
486     }
487     return NULL;
488 }