]> git.sesse.net Git - vlc/blob - modules/gui/qt4/input_manager.cpp
57774c4d327aeb524740c74d1b44fbbf75f7b22b
[vlc] / modules / gui / qt4 / input_manager.cpp
1 /*****************************************************************************
2  * input_manager.cpp : Manage an input and interact with its GUI elements
3  ****************************************************************************
4  * Copyright (C) 2006-2008 the VideoLAN team
5  * $Id$
6  *
7  * Authors: ClĂ©ment Stenac <zorglub@videolan.org>
8  *          Ilkka Ollakka  <ileoo@videolan.org>
9  *          Jean-Baptiste <jb@videolan.org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include "qt4.hpp"
30 #include "input_manager.hpp"
31 #include "dialogs_provider.hpp"
32
33 static int ChangeSPU( vlc_object_t *p_this, const char *var, vlc_value_t o,
34                       vlc_value_t n, void *param );
35 static int ItemChanged( vlc_object_t *, const char *,
36                         vlc_value_t, vlc_value_t, void * );
37 static int PLItemChanged( vlc_object_t *, const char *,
38                         vlc_value_t, vlc_value_t, void * );
39 static int InterfaceChanged( vlc_object_t *, const char *,
40                             vlc_value_t, vlc_value_t, void * );
41 static int ItemStateChanged( vlc_object_t *, const char *,
42                         vlc_value_t, vlc_value_t, void * );
43 static int ItemRateChanged( vlc_object_t *, const char *,
44                         vlc_value_t, vlc_value_t, void * );
45 static int ItemTitleChanged( vlc_object_t *, const char *,
46                         vlc_value_t, vlc_value_t, void * );
47 static int VolumeChanged( vlc_object_t *, const char *,
48                         vlc_value_t, vlc_value_t, void * );
49
50 /**********************************************************************
51  * InputManager implementation
52  **********************************************************************
53  * The Input Manager can be the main one around the playlist
54  * But can also be used for VLM dialog or similar
55  **********************************************************************/
56
57 InputManager::InputManager( QObject *parent, intf_thread_t *_p_intf) :
58                            QObject( parent ), p_intf( _p_intf )
59 {
60     i_old_playing_status = END_S;
61     old_name     = "";
62     artUrl       = "";
63     p_input      = NULL;
64     i_rate       = 0;
65     i_input_id   = 0;
66 }
67
68 InputManager::~InputManager()
69 {
70     delInput();
71 }
72
73 /* Define the Input used.
74    Add the callbacks on input
75    p_input is yield once here */
76 void InputManager::setInput( input_thread_t *_p_input )
77 {
78     delInput();
79     p_input = _p_input;
80     if( p_input && !( p_input->b_dead || p_input->b_die ) )
81     {
82         vlc_object_yield( p_input );
83         emit statusChanged( PLAYING_S );
84         UpdateMeta();
85         UpdateNavigation();
86         UpdateArt();
87         UpdateSPU();
88         addCallbacks();
89         i_input_id = input_GetItem( p_input )->i_id;
90     }
91     else
92     {
93         p_input = NULL;
94         i_input_id = 0;
95         emit rateChanged( INPUT_RATE_DEFAULT );
96         emit inputUnset();
97     }
98 }
99
100 /* delete Input if it ever existed.
101    Delete the callbacls on input
102    p_input is released once here */
103 void InputManager::delInput()
104 {
105     if( p_input )
106     {
107         delCallbacks();
108         i_old_playing_status = END_S;
109         i_input_id = 0;
110         old_name   = "";
111         artUrl     = "";
112         emit positionUpdated( 0.0, 0 ,0 );
113         emit statusChanged( END_S );
114         emit nameChanged( "" );
115         emit artChanged( "" );
116         emit rateChanged( INPUT_RATE_DEFAULT );
117         vlc_object_release( p_input );
118         p_input = NULL;
119         UpdateSPU();
120     }
121 }
122
123 /* Add the callbacks on Input. Self explanatory */
124 void InputManager::addCallbacks()
125 {
126     /* We don't care about:
127        - chapter
128        - programs
129        - audio-delay
130        - spu-delay
131        - bookmark
132        - position, time, length, because they are included in intf-change
133      */
134     /* src/input/input.c:1629 */
135     var_AddCallback( p_input, "state", ItemStateChanged, this );
136     /* src/input/es-out.c:552 */
137     var_AddCallback( p_input, "spu-es", ChangeSPU, this );
138     /* src/input/input.c:1765 */
139     var_AddCallback( p_input, "rate-change", ItemRateChanged, this );
140     /* src/input/input.c:2003 */
141     var_AddCallback( p_input, "title", ItemTitleChanged, this );
142     /* src/input/input.c:734 for timers update*/
143     var_AddCallback( p_input, "intf-change", InterfaceChanged, this );
144 }
145
146 /* Delete the callbacks on Input. Self explanatory */
147 void InputManager::delCallbacks()
148 {
149     var_DelCallback( p_input, "spu-es", ChangeSPU, this );
150     var_DelCallback( p_input, "state", ItemStateChanged, this );
151     var_DelCallback( p_input, "rate-change", ItemRateChanged, this );
152     var_DelCallback( p_input, "title", ItemTitleChanged, this );
153     var_DelCallback( p_input, "intf-change", InterfaceChanged, this );
154 }
155
156 /* Convert the event from the callbacks in actions */
157 void InputManager::customEvent( QEvent *event )
158 {
159     int type = event->type();
160     IMEvent *ple = static_cast<IMEvent *>(event);
161
162     if ( type != PositionUpdate_Type &&
163          type != ItemChanged_Type &&
164          type != ItemRateChanged_Type &&
165          type != ItemTitleChanged_Type &&
166          type != ItemSpuChanged_Type &&
167          type != ItemStateChanged_Type )
168         return;
169
170     if( !hasInput() ) return;
171
172     if( ( type != PositionUpdate_Type  &&
173           type != ItemRateChanged_Type &&
174           type != ItemSpuChanged_Type &&
175           type != ItemStateChanged_Type
176         )
177         && ( i_input_id != ple->i_id ) )
178         return;
179
180     if( type != PositionUpdate_Type )
181         msg_Dbg( p_intf, "New Event: type %i", type );
182
183     /* Actions */
184     switch( type )
185     {
186     case PositionUpdate_Type:
187         UpdatePosition();
188         break;
189     case ItemChanged_Type:
190         UpdateMeta();
191         UpdateNavigation();
192         UpdateStatus();
193         UpdateArt();
194         break;
195     case ItemRateChanged_Type:
196         UpdateRate();
197         break;
198     case ItemTitleChanged_Type:
199         UpdateNavigation();
200         UpdateMeta();
201         break;
202     case ItemStateChanged_Type:
203         UpdateStatus();
204     case ItemSpuChanged_Type:
205         UpdateSPU();
206         break;
207     }
208 }
209
210 void InputManager::UpdatePosition()
211 {
212     /* Update position */
213     int i_length, i_time; /* Int is enough, since we store seconds */
214     float f_pos;
215     i_length = var_GetTime(  p_input , "length" ) / 1000000;
216     i_time = var_GetTime(  p_input , "time") / 1000000;
217     f_pos = var_GetFloat(  p_input , "position" );
218     emit positionUpdated( f_pos, i_time, i_length );
219 }
220
221 void InputManager::UpdateNavigation()
222 {
223     /* Update navigation status */
224     vlc_value_t val; val.i_int = 0;
225     var_Change( p_input, "title", VLC_VAR_CHOICESCOUNT, &val, NULL );
226     if( val.i_int > 0 )
227     {
228         val.i_int = 0;
229         var_Change( p_input, "chapter", VLC_VAR_CHOICESCOUNT, &val, NULL );
230         emit navigationChanged( (val.i_int > 0) ? 1 : 2 );
231     }
232     else
233     {
234         emit navigationChanged( 0 );
235     }
236 }
237
238 void InputManager::UpdateStatus()
239 {
240     /* Update playing status */
241     vlc_value_t val; val.i_int = 0;
242     var_Get( p_input, "state", &val );
243     if( i_old_playing_status != val.i_int )
244     {
245         i_old_playing_status = val.i_int;
246         emit statusChanged( val.i_int );
247     }
248 }
249
250 void InputManager::UpdateRate()
251 {
252     /* Update Rate */
253     int i_new_rate = var_GetInteger( p_input, "rate");
254     if( i_new_rate != i_rate )
255     {
256         i_rate = i_new_rate;
257         /* Update rate */
258         emit rateChanged( i_rate );
259     }
260 }
261
262 void InputManager::UpdateMeta()
263 {
264     /* Update text, name and nowplaying */
265     QString text;
266
267     char *psz_name = input_item_GetTitle( input_GetItem( p_input ) );
268     if( EMPTY_STR( psz_name ) )
269     {
270         free( psz_name );
271         psz_name = input_item_GetName( input_GetItem( p_input ) );
272     }
273
274     char *psz_nowplaying =
275         input_item_GetNowPlaying( input_GetItem( p_input ) );
276     if( !EMPTY_STR( psz_nowplaying ) )
277     {
278         text.sprintf( "%s - %s", psz_nowplaying, psz_name );
279     }
280     else
281     {
282         char *psz_artist = input_item_GetArtist( input_GetItem( p_input ) );
283         if( !EMPTY_STR( psz_artist ) )
284         {
285             text.sprintf( "%s - %s", psz_artist, psz_name );
286         }
287         else
288         {
289             text.sprintf( "%s", psz_name );
290         }
291         free( psz_artist );
292     }
293     free( psz_name );
294     free( psz_nowplaying );
295
296     if( old_name != text )
297     {
298         emit nameChanged( text );
299         old_name=text;
300     }
301 }
302
303 bool InputManager::hasAudio()
304 {
305     if( hasInput() )
306     {
307         vlc_value_t val;
308         var_Change( p_input, "audio-es", VLC_VAR_CHOICESCOUNT, &val, NULL );
309         return val.i_int > 0;
310     }
311     return false;
312 }
313
314 bool InputManager::hasVideo()
315 {
316     if( hasInput() )
317     {
318         vlc_value_t val;
319         var_Change( p_input, "video-es", VLC_VAR_CHOICESCOUNT, &val, NULL );
320         return val.i_int > 0;
321     }
322     return false;
323
324 }
325
326 void InputManager::UpdateSPU()
327 {
328 #ifdef ZVBI_COMPILED
329     if( hasInput() )
330     {
331         vlc_value_t val;
332         var_Change( p_input, "spu-es", VLC_VAR_CHOICESCOUNT, &val, NULL );
333
334         /* Update teletext status*/
335         emit teletextEnabled( val.i_int > 0 );/* FIXME */
336         telexToggle( true );
337     }
338     else
339     {
340         emit teletextEnabled( false );
341         telexToggle( false );
342     }
343 #endif
344 }
345
346 void InputManager::UpdateArt()
347 {
348     /* Update Art meta */
349     QString url;
350     char *psz_art = input_item_GetArtURL( input_GetItem( p_input ) );
351     url.sprintf("%s", psz_art );
352     free( psz_art );
353     if( artUrl != url )
354     {
355         artUrl = url.replace( "file://",QString("" ) );
356         /* Taglib seems to define a attachment://, It won't work yet */
357         artUrl = url.replace( "attachment://",QString("" ) );
358         emit artChanged( artUrl );
359         msg_Dbg( p_intf, "Art:  %s", qtu( artUrl ) );
360     }
361 }
362
363 /* User update of the slider */
364 void InputManager::sliderUpdate( float new_pos )
365 {
366     if( hasInput() )
367         var_SetFloat( p_input, "position", new_pos );
368 }
369
370 /* User togglePlayPause */
371 void InputManager::togglePlayPause()
372 {
373     vlc_value_t state;
374     var_Get( p_input, "state", &state );
375     state.i_int = ( state.i_int != PLAYING_S ) ? PLAYING_S : PAUSE_S;
376     var_Set( p_input, "state", state );
377     emit statusChanged( state.i_int );
378 }
379
380 void InputManager::sectionPrev()
381 {
382     if( hasInput() )
383     {
384         int i_type = var_Type( p_input, "next-chapter" );
385         vlc_value_t val; val.b_bool = true;
386         var_Set( p_input, (i_type & VLC_VAR_TYPE) != 0 ?
387                             "prev-chapter":"prev-title", val );
388     }
389 }
390
391 void InputManager::sectionNext()
392 {
393     if( hasInput() )
394     {
395         int i_type = var_Type( p_input, "next-chapter" );
396         vlc_value_t val; val.b_bool = true;
397         var_Set( p_input, (i_type & VLC_VAR_TYPE) != 0 ?
398                             "next-chapter":"next-title", val );
399     }
400 }
401
402 void InputManager::sectionMenu()
403 {
404     if( hasInput() )
405     {
406         vlc_value_t val; val.i_int = 2;
407         var_Set( p_input, "title  0", val );
408     }
409 }
410
411 #ifdef ZVBI_COMPILED
412 void InputManager::telexGotoPage( int page )
413 {
414     if( hasInput() )
415     {
416         vlc_object_t *p_vbi;
417         p_vbi = (vlc_object_t *) vlc_object_find_name( p_input,
418                     "zvbi", FIND_ANYWHERE );
419         if( p_vbi )
420         {
421             var_SetInteger( p_vbi, "vbi-page", page );
422             vlc_object_release( p_vbi );
423         }
424     }
425 }
426
427 void InputManager::telexToggle( bool b_enabled )
428 {
429     int i_page = 100;
430
431     if( hasInput() )
432     {
433         vlc_object_t *p_vbi;
434         p_vbi = (vlc_object_t *) vlc_object_find_name( p_input,
435                     "zvbi", FIND_ANYWHERE );
436         if( p_vbi )
437         {
438             i_page = var_GetInteger( p_vbi, "vbi-page" );
439             vlc_object_release( p_vbi );
440         }
441     }
442     i_page = b_enabled ? i_page : 0;
443     telexGotoPage( i_page );
444 }
445
446 void InputManager::telexSetTransparency( bool b_transp )
447 {
448     if( hasInput() )
449     {
450         vlc_object_t *p_vbi;
451         p_vbi = (vlc_object_t *) vlc_object_find_name( p_input,
452                     "zvbi", FIND_ANYWHERE );
453         if( p_vbi )
454         {
455             var_SetBool( p_input->p_libvlc, "vbi-opaque", b_transp );
456             vlc_object_release( p_vbi );
457         }
458     }
459 }
460 #endif
461
462 void InputManager::slower()
463 {
464     if( hasInput() )
465         var_SetVoid( p_input, "rate-slower" );
466 }
467
468 void InputManager::faster()
469 {
470     if( hasInput() )
471         var_SetVoid( p_input, "rate-faster" );
472 }
473
474 void InputManager::normalRate()
475 {
476     if( hasInput() )
477         var_SetInteger( p_input, "rate", INPUT_RATE_DEFAULT );
478 }
479
480 void InputManager::setRate( int new_rate )
481 {
482     if( hasInput() )
483         var_SetInteger( p_input, "rate", new_rate );
484 }
485
486 /**********************************************************************
487  * MainInputManager implementation. Wrap an input manager and
488  * take care of updating the main playlist input.
489  * Used in the main playlist Dialog
490  **********************************************************************/
491 MainInputManager * MainInputManager::instance = NULL;
492
493 MainInputManager::MainInputManager( intf_thread_t *_p_intf )
494                  : QObject(NULL), p_intf( _p_intf )
495 {
496     p_input = NULL;
497     im = new InputManager( this, p_intf );
498
499 //    var_AddCallback( THEPL, "item-change", PLItemChanged, this );
500     var_AddCallback( THEPL, "item-change", ItemChanged, im );
501     var_AddCallback( THEPL, "playlist-current", PLItemChanged, this );
502     var_AddCallback( THEPL, "activity", PLItemChanged, this );
503
504     var_AddCallback( p_intf->p_libvlc, "volume-change", VolumeChanged, this );
505
506     // No necessary, I think TODO REMOVE ME at the end
507     //var_AddCallback( THEPL, "intf-change", ItemChanged, im );
508
509     /* Warn our embedded IM about input changes */
510     CONNECT( this, inputChanged( input_thread_t * ),
511              im, setInput( input_thread_t * ) );
512 }
513
514 MainInputManager::~MainInputManager()
515 {
516     if( p_input )
517     {
518        var_DelCallback( p_input, "state", PLItemChanged, this );
519        vlc_object_release( p_input );
520        emit inputChanged( NULL );
521     }
522
523     var_DelCallback( p_intf->p_libvlc, "volume-change", VolumeChanged, this );
524
525     var_DelCallback( THEPL, "activity", PLItemChanged, this );
526     var_DelCallback( THEPL, "item-change", ItemChanged, im );
527 //    var_DelCallback( THEPL, "item-change", PLItemChanged, this );
528
529     var_DelCallback( THEPL, "playlist-current", PLItemChanged, this );
530 }
531
532 void MainInputManager::customEvent( QEvent *event )
533 {
534     int type = event->type();
535     if ( type != ItemChanged_Type && type != VolumeChanged_Type )
536         return;
537
538     // msg_Dbg( p_intf, "New MainIM Event of type: %i", type );
539     if( type == VolumeChanged_Type )
540     {
541         emit volumeChanged();
542         return;
543     }
544
545     /* Should be PLItemChanged Event */
546     if( VLC_OBJECT_INTF == p_intf->i_object_type ) /* FIXME: don't use object type */
547     {
548         vlc_mutex_lock( &p_intf->change_lock );
549         if( p_input && ( p_input->b_dead || p_input->b_die ) )
550         {
551             var_DelCallback( p_input, "state", PLItemChanged, this );
552             vlc_object_release( p_input );
553             emit inputChanged( NULL );
554             p_input = NULL;
555             vlc_mutex_unlock( &p_intf->change_lock );
556             return;
557         }
558
559         if( !p_input )
560         {
561             QPL_LOCK;
562             p_input = THEPL->p_input;
563             if( p_input && !( p_input->b_die || p_input->b_dead) )
564             {
565                 vlc_object_yield( p_input );
566                 var_AddCallback( p_input, "state", PLItemChanged, this );
567                 emit inputChanged( p_input );
568             }
569             else
570                 p_input = NULL;
571             QPL_UNLOCK;
572         }
573         vlc_mutex_unlock( &p_intf->change_lock );
574     }
575     else
576     {
577         /* we are working as a dialogs provider */
578         playlist_t *p_playlist = pl_Yield( p_intf );
579         p_input = p_playlist->p_input;
580         emit inputChanged( p_input );
581     }
582 }
583
584 /* Playlist Control functions */
585 void MainInputManager::stop()
586 {
587    playlist_Stop( THEPL );
588 }
589
590 void MainInputManager::next()
591 {
592    playlist_Next( THEPL );
593 }
594
595 void MainInputManager::prev()
596 {
597    playlist_Prev( THEPL );
598 }
599
600 void MainInputManager::togglePlayPause()
601 {
602     if( p_input == NULL )
603     {
604         playlist_Play( THEPL );
605         return;
606     }
607     getIM()->togglePlayPause();
608 }
609
610 /* Static callbacks */
611
612 /* IM */
613 static int InterfaceChanged( vlc_object_t *p_this, const char *psz_var,
614                            vlc_value_t oldval, vlc_value_t newval, void *param )
615 {
616     static int counter = 0;
617     InputManager *im = (InputManager*)param;
618
619     counter = ++counter % 4;
620     if(!counter)
621         return VLC_SUCCESS;
622     IMEvent *event = new IMEvent( PositionUpdate_Type, 0 );
623     QApplication::postEvent( im, static_cast<QEvent*>(event) );
624     return VLC_SUCCESS;
625 }
626
627 static int ItemStateChanged( vlc_object_t *p_this, const char *psz_var,
628                            vlc_value_t oldval, vlc_value_t newval, void *param )
629 {
630     InputManager *im = (InputManager*)param;
631
632     IMEvent *event = new IMEvent( ItemStateChanged_Type, 0 );
633     QApplication::postEvent( im, static_cast<QEvent*>(event) );
634     return VLC_SUCCESS;
635 }
636
637 static int ItemRateChanged( vlc_object_t *p_this, const char *psz_var,
638                            vlc_value_t oldval, vlc_value_t newval, void *param )
639 {
640     InputManager *im = (InputManager*)param;
641
642     IMEvent *event = new IMEvent( ItemRateChanged_Type, 0 );
643     QApplication::postEvent( im, static_cast<QEvent*>(event) );
644     return VLC_SUCCESS;
645 }
646
647 static int ItemTitleChanged( vlc_object_t *p_this, const char *psz_var,
648                            vlc_value_t oldval, vlc_value_t newval, void *param )
649 {
650     InputManager *im = (InputManager*)param;
651
652     IMEvent *event = new IMEvent( ItemTitleChanged_Type, 0 );
653     QApplication::postEvent( im, static_cast<QEvent*>(event) );
654     return VLC_SUCCESS;
655 }
656
657 static int ItemChanged( vlc_object_t *p_this, const char *psz_var,
658                         vlc_value_t oldval, vlc_value_t newval, void *param )
659 {
660     InputManager *im = (InputManager*)param;
661
662     IMEvent *event = new IMEvent( ItemChanged_Type, newval.i_int );
663     QApplication::postEvent( im, static_cast<QEvent*>(event) );
664     return VLC_SUCCESS;
665 }
666
667 static int ChangeSPU( vlc_object_t *p_this, const char *var, vlc_value_t o,
668                         vlc_value_t n, void *param )
669 {
670     InputManager *im = (InputManager*)param;
671     IMEvent *event = new IMEvent( ItemSpuChanged_Type, 0 );
672     QApplication::postEvent( im, static_cast<QEvent*>(event) );
673     return VLC_SUCCESS;
674
675 }
676
677 /* MIM */
678 static int PLItemChanged( vlc_object_t *p_this, const char *psz_var,
679                         vlc_value_t oldval, vlc_value_t newval, void *param )
680 {
681     MainInputManager *mim = (MainInputManager*)param;
682
683     IMEvent *event = new IMEvent( ItemChanged_Type, newval.i_int );
684     QApplication::postEvent( mim, static_cast<QEvent*>(event) );
685     return VLC_SUCCESS;
686 }
687
688 static int VolumeChanged( vlc_object_t *p_this, const char *psz_var,
689                         vlc_value_t oldval, vlc_value_t newval, void *param )
690 {
691     MainInputManager *mim = (MainInputManager*)param;
692
693     IMEvent *event = new IMEvent( VolumeChanged_Type, newval.i_int );
694     QApplication::postEvent( mim, static_cast<QEvent*>(event) );
695     return VLC_SUCCESS;
696 }
697