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