msg_Unsubscribe: race condition memory leak
[vlc.git] / 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
36 static int ChangeTeletext( vlc_object_t *p_this, const char *var, vlc_value_t o,
37                            vlc_value_t n, void *param );
38
39 static int ItemChanged( vlc_object_t *, const char *,
40                         vlc_value_t, vlc_value_t, void * );
41 static int PLItemChanged( vlc_object_t *, const char *,
42                         vlc_value_t, vlc_value_t, void * );
43 static int InterfaceChanged( vlc_object_t *, const char *,
44                             vlc_value_t, vlc_value_t, void * );
45 static int StatisticsUpdated( vlc_object_t *, const char *,
46                             vlc_value_t, vlc_value_t, void * );
47 static int InterfaceVoutChanged( vlc_object_t *, const char *,
48                                  vlc_value_t, vlc_value_t, void * );
49 static int ItemStateChanged( vlc_object_t *, const char *,
50                         vlc_value_t, vlc_value_t, void * );
51 static int ItemRateChanged( vlc_object_t *, const char *,
52                         vlc_value_t, vlc_value_t, void * );
53 static int ItemTitleChanged( vlc_object_t *, const char *,
54                         vlc_value_t, vlc_value_t, void * );
55 static int VolumeChanged( vlc_object_t *, const char *,
56                         vlc_value_t, vlc_value_t, void * );
57
58 /**********************************************************************
59  * InputManager implementation
60  **********************************************************************
61  * The Input Manager can be the main one around the playlist
62  * But can also be used for VLM dialog or similar
63  **********************************************************************/
64
65 InputManager::InputManager( QObject *parent, intf_thread_t *_p_intf) :
66                            QObject( parent ), p_intf( _p_intf )
67 {
68     i_old_playing_status = END_S;
69     old_name     = "";
70     artUrl       = "";
71     p_input      = NULL;
72     i_rate       = 0;
73     i_input_id   = 0;
74     b_video      = false;
75     timeA        = 0;
76     timeB        = 0;
77
78 }
79
80 InputManager::~InputManager()
81 {
82     delInput();
83 }
84
85 /* Define the Input used.
86    Add the callbacks on input
87    p_input is held once here */
88 void InputManager::setInput( input_thread_t *_p_input )
89 {
90     delInput();
91     p_input = _p_input;
92     if( p_input && !( p_input->b_dead || !vlc_object_alive (p_input) ) )
93     {
94         vlc_object_hold( p_input );
95         emit statusChanged( PLAYING_S );
96         UpdateMeta();
97         UpdateArt();
98         UpdateSPU();
99         UpdateTeletext();
100         UpdateNavigation();
101         UpdateVout();
102         addCallbacks();
103         i_input_id = input_GetItem( p_input )->i_id;
104     }
105     else
106     {
107         p_input = NULL;
108         i_input_id = 0;
109         emit rateChanged( INPUT_RATE_DEFAULT );
110     }
111 }
112
113 /* delete Input if it ever existed.
114    Delete the callbacls on input
115    p_input is released once here */
116 void InputManager::delInput()
117 {
118     if( p_input )
119     {
120         delCallbacks();
121         i_old_playing_status = END_S;
122         i_input_id = 0;
123         old_name   = "";
124         artUrl     = "";
125         b_video    = false;
126         timeA      = 0;
127         timeB      = 0;
128         emit positionUpdated( -1.0, 0 ,0 );
129         emit statusChanged( END_S );
130         emit nameChanged( "" );
131         emit artChanged( NULL );
132         emit rateChanged( INPUT_RATE_DEFAULT );
133         emit voutChanged( false );
134         vlc_object_release( p_input );
135         p_input = NULL;
136         UpdateSPU();
137         UpdateTeletext();
138     }
139 }
140
141 /* Add the callbacks on Input. Self explanatory */
142 void InputManager::addCallbacks()
143 {
144     /* We don't care about:
145        - chapter
146        - programs
147        - audio-delay
148        - spu-delay
149        - bookmark
150        - position, time, length, because they are included in intf-change
151      */
152     /* src/input/input.c:1629 */
153     var_AddCallback( p_input, "state", ItemStateChanged, this );
154     /* src/input/es-out.c:552 */
155     var_AddCallback( p_input, "spu-es", ChangeSPU, this );
156     /* emit UpdateStatus so that main_interface updates controls 
157      * if there is new videotracks (mpeg-ts)*/
158     var_AddCallback( p_input, "video-es", ItemStateChanged, this );
159     /* src/input/es-out.c: */
160     var_AddCallback( p_input, "teletext-es", ChangeTeletext, this );
161     /* src/input/input.c:1765 */
162     var_AddCallback( p_input, "rate-change", ItemRateChanged, this );
163     /* src/input/input.c:2003 */
164     var_AddCallback( p_input, "title", ItemTitleChanged, this );
165     /* src/input/input.c:734 for timers update*/
166     var_AddCallback( p_input, "intf-change", InterfaceChanged, this );
167     /* src/input/input.c:710 for statistics update*/
168     var_AddCallback( p_input, "stats-change", StatisticsUpdated, this );
169     /* src/input/input.c for vout creation/destruction */
170     var_AddCallback( p_input, "intf-change-vout", InterfaceVoutChanged, this );
171 }
172
173 /* Delete the callbacks on Input. Self explanatory */
174 void InputManager::delCallbacks()
175 {
176     var_DelCallback( p_input, "spu-es", ChangeSPU, this );
177     var_DelCallback( p_input, "video-es", ItemStateChanged, this );
178     var_DelCallback( p_input, "teletext-es", ChangeTeletext, this );
179     var_DelCallback( p_input, "state", ItemStateChanged, this );
180     var_DelCallback( p_input, "rate-change", ItemRateChanged, this );
181     var_DelCallback( p_input, "title", ItemTitleChanged, this );
182     var_DelCallback( p_input, "intf-change", InterfaceChanged, this );
183     var_DelCallback( p_input, "stats-change", StatisticsUpdated, this );
184     var_DelCallback( p_input, "intf-change-vout", InterfaceVoutChanged, this );
185 }
186
187 /* Convert the event from the callbacks in actions */
188 void InputManager::customEvent( QEvent *event )
189 {
190     int type = event->type();
191     IMEvent *ple = static_cast<IMEvent *>(event);
192
193     if ( type != PositionUpdate_Type &&
194          type != ItemChanged_Type &&
195          type != ItemRateChanged_Type &&
196          type != ItemTitleChanged_Type &&
197          type != ItemSpuChanged_Type &&
198          type != ItemTeletextChanged_Type &&
199          type != ItemStateChanged_Type &&
200          type != StatisticsUpdate_Type &&
201          type != InterfaceVoutUpdate_Type )
202         return;
203
204     if( type == ItemStateChanged_Type )
205     {
206         UpdateNavigation();
207         UpdateTeletext();
208     }
209
210     if( !hasInput() ) return;
211
212     if( ( type != PositionUpdate_Type  &&
213           type != ItemRateChanged_Type &&
214           type != ItemSpuChanged_Type &&
215           type != ItemTeletextChanged_Type &&
216           type != ItemStateChanged_Type &&
217           type != StatisticsUpdate_Type &&
218           type != InterfaceVoutUpdate_Type
219         )
220         && ( i_input_id != ple->i_id ) )
221         return;
222
223     if( type != PositionUpdate_Type &&
224         type != StatisticsUpdate_Type )
225         msg_Dbg( p_intf, "New Event: type %i", type );
226
227     /* Actions */
228     switch( type )
229     {
230     case PositionUpdate_Type:
231         UpdatePosition();
232         break;
233     case StatisticsUpdate_Type:
234         UpdateStats();
235         break;
236     case ItemChanged_Type:
237         UpdateMeta();
238         UpdateStatus();
239         UpdateArt();
240         break;
241     case ItemStateChanged_Type:
242         UpdateStatus();
243         UpdateNavigation();
244         UpdateMeta();
245         break;
246     case ItemTitleChanged_Type:
247         UpdateNavigation();
248         UpdateMeta();
249         break;
250     case ItemRateChanged_Type:
251         UpdateRate();
252         break;
253     case ItemSpuChanged_Type:
254         UpdateSPU();
255         break;
256     case ItemTeletextChanged_Type:
257         UpdateTeletext();
258         break;
259     case InterfaceVoutUpdate_Type:
260         UpdateVout();
261         break;
262     }
263 }
264
265 void InputManager::UpdateStats()
266 {
267     emit statisticsUpdated( input_GetItem( p_input ) );
268 }
269
270 void InputManager::UpdatePosition()
271 {
272     /* Update position */
273     int i_length, i_time; /* Int is enough, since we store seconds */
274     float f_pos;
275     i_length = var_GetTime(  p_input , "length" ) / 1000000;
276     i_time = var_GetTime(  p_input , "time") / 1000000;
277     f_pos = var_GetFloat(  p_input , "position" );
278     emit positionUpdated( f_pos, i_time, i_length );
279 }
280
281 void InputManager::UpdateNavigation()
282 {
283     /* Update navigation status */
284     vlc_value_t val; val.i_int = 0;
285
286     if( hasInput() )
287         var_Change( p_input, "title", VLC_VAR_CHOICESCOUNT, &val, NULL );
288
289     if( val.i_int > 0 )
290     {
291         emit titleChanged( true );
292         val.i_int = 0;
293         var_Change( p_input, "chapter", VLC_VAR_CHOICESCOUNT, &val, NULL );
294         emit chapterChanged( (val.i_int > 0) );
295     }
296     else
297         emit titleChanged( false );
298 }
299
300 void InputManager::UpdateStatus()
301 {
302     /* Update playing status */
303     vlc_value_t val; val.i_int = 0;
304     var_Get( p_input, "state", &val );
305     if( i_old_playing_status != val.i_int )
306     {
307         i_old_playing_status = val.i_int;
308         emit statusChanged( val.i_int );
309     }
310 }
311
312 void InputManager::UpdateRate()
313 {
314     /* Update Rate */
315     int i_new_rate = var_GetInteger( p_input, "rate");
316     if( i_new_rate != i_rate )
317     {
318         i_rate = i_new_rate;
319         /* Update rate */
320         emit rateChanged( i_rate );
321     }
322 }
323
324 void InputManager::UpdateMeta()
325 {
326     /* Update text, name and nowplaying */
327     QString text;
328
329     /* Try to get the Title, then the Name */
330     char *psz_name = input_item_GetTitle( input_GetItem( p_input ) );
331     if( EMPTY_STR( psz_name ) )
332     {
333         free( psz_name );
334         psz_name = input_item_GetName( input_GetItem( p_input ) );
335     }
336
337     /* Try to get the nowplaying */
338     char *psz_nowplaying =
339         input_item_GetNowPlaying( input_GetItem( p_input ) );
340     if( !EMPTY_STR( psz_nowplaying ) )
341     {
342         text.sprintf( "%s - %s", psz_nowplaying, psz_name );
343     }
344     else  /* Do it ourself */
345     {
346         char *psz_artist = input_item_GetArtist( input_GetItem( p_input ) );
347
348         if( !EMPTY_STR( psz_artist ) )
349             text.sprintf( "%s - %s", psz_artist, psz_name );
350         else
351             text.sprintf( "%s", psz_name );
352
353         free( psz_artist );
354     }
355     /* Free everything */
356     free( psz_name );
357     free( psz_nowplaying );
358
359     /* If we have Nothing */
360     if( text.isEmpty() )
361     {
362         psz_name = input_item_GetURI( input_GetItem( p_input ) );
363         text.sprintf( "%s", psz_name );
364         text = text.remove( 0, text.lastIndexOf( DIR_SEP ) + 1 );
365         free( psz_name );
366     }
367
368     if( old_name != text )
369     {
370         emit nameChanged( text );
371         old_name=text;
372     }
373 }
374
375 bool InputManager::hasAudio()
376 {
377     if( hasInput() )
378     {
379         vlc_value_t val;
380         var_Change( p_input, "audio-es", VLC_VAR_CHOICESCOUNT, &val, NULL );
381         return val.i_int > 0;
382     }
383     return false;
384 }
385
386 void InputManager::UpdateSPU()
387 {
388     UpdateTeletext();
389 }
390
391 void InputManager::UpdateTeletext()
392 {
393     if( hasInput() )
394         telexActivation( var_GetInteger( p_input, "teletext-es" ) >= 0 );
395     else
396         telexActivation( false );
397 }
398
399 void InputManager::UpdateVout()
400 {
401     if( hasInput() )
402     {
403         bool b_old_video = b_video;
404
405         vlc_object_t *p_vout = (vlc_object_t*)vlc_object_find( p_input, VLC_OBJECT_VOUT, FIND_CHILD );
406         b_video = p_vout != NULL;
407         if( p_vout )
408             vlc_object_release( p_vout );
409         if( !!b_old_video != !!b_video )
410             emit voutChanged( b_video );
411     }
412 }
413
414 void InputManager::UpdateArt()
415 {
416     /* Update Art meta */
417     emit artChanged( input_GetItem( p_input ) );
418 }
419
420 /* User update of the slider */
421 void InputManager::sliderUpdate( float new_pos )
422 {
423     if( hasInput() )
424         var_SetFloat( p_input, "position", new_pos );
425 }
426
427 /* User togglePlayPause */
428 void InputManager::togglePlayPause()
429 {
430     vlc_value_t state;
431     var_Get( p_input, "state", &state );
432     state.i_int = ( state.i_int != PLAYING_S ) ? PLAYING_S : PAUSE_S;
433     var_Set( p_input, "state", state );
434     emit statusChanged( state.i_int );
435 }
436
437 void InputManager::sectionPrev()
438 {
439     if( hasInput() )
440     {
441         int i_type = var_Type( p_input, "next-chapter" );
442         vlc_value_t val; val.b_bool = true;
443         var_Set( p_input, (i_type & VLC_VAR_TYPE) != 0 ?
444                             "prev-chapter":"prev-title", val );
445     }
446 }
447
448 void InputManager::sectionNext()
449 {
450     if( hasInput() )
451     {
452         int i_type = var_Type( p_input, "next-chapter" );
453         vlc_value_t val; val.b_bool = true;
454         var_Set( p_input, (i_type & VLC_VAR_TYPE) != 0 ?
455                             "next-chapter":"next-title", val );
456     }
457 }
458
459 void InputManager::sectionMenu()
460 {
461     if( hasInput() )
462     {
463         vlc_value_t val, text;
464         vlc_value_t root;
465
466         if( var_Change( p_input, "title  0", VLC_VAR_GETLIST, &val, &text ) < 0 )
467             return;
468
469         /* XXX is it "Root" or "Title" we want here ?" (set 0 by default) */
470         root.i_int = 0;
471         for( int i = 0; i < val.p_list->i_count; i++ )
472         {
473             if( !strcmp( text.p_list->p_values[i].psz_string, "Title" ) )
474                 root.i_int = i;
475         }
476         var_Change( p_input, "title  0", VLC_VAR_FREELIST, &val, &text );
477
478         var_Set( p_input, "title  0", root );
479     }
480 }
481
482 /*
483  *  Teletext Functions
484  */
485
486 /* Set a new Teletext Page */
487 void InputManager::telexSetPage( int page )
488 {
489     if( hasInput() )
490     {
491         const int i_teletext_es = var_GetInteger( p_input, "teletext-es" );
492         const int i_spu_es = var_GetInteger( p_input, "spu-es" );
493
494         if( i_teletext_es >= 0 && i_teletext_es == i_spu_es )
495         {
496             vlc_object_t *p_vbi = (vlc_object_t *) vlc_object_find_name( p_input,
497                         "zvbi", FIND_ANYWHERE );
498             if( p_vbi )
499             {
500                 var_SetInteger( p_vbi, "vbi-page", page );
501                 vlc_object_release( p_vbi );
502                 emit newTelexPageSet( page );
503             }
504         }
505     }
506 }
507
508 /* Set the transparency on teletext */
509 void InputManager::telexSetTransparency( bool b_transparentTelextext )
510 {
511     if( hasInput() )
512     {
513         vlc_object_t *p_vbi = (vlc_object_t *) vlc_object_find_name( p_input,
514                     "zvbi", FIND_ANYWHERE );
515         if( p_vbi )
516         {
517             var_SetBool( p_vbi, "vbi-opaque", b_transparentTelextext );
518             vlc_object_release( p_vbi );
519             emit teletextTransparencyActivated( b_transparentTelextext );
520         }
521     }
522 }
523
524 void InputManager::telexActivation( bool b_enabled )
525 {
526     if( hasInput() )
527     {
528         const int i_teletext_es = var_GetInteger( p_input, "teletext-es" );
529         const int i_spu_es = var_GetInteger( p_input, "spu-es" );
530
531         /* Teletext is possible. Show the buttons */
532         b_enabled = (i_teletext_es >= 0);
533         emit teletextPossible( b_enabled );
534         if( !b_enabled ) return;
535
536         /* If Teletext is selected */
537         if( i_teletext_es == i_spu_es )
538         {
539             /* Activate the buttons */
540             teletextActivated( true );
541
542             /* Then, find the current page */
543             int i_page = 100;
544             vlc_object_t *p_vbi = (vlc_object_t *)
545                 vlc_object_find_name( p_input, "zvbi", FIND_ANYWHERE );
546             if( p_vbi )
547             {
548                 i_page = var_GetInteger( p_vbi, "vbi-page" );
549                 vlc_object_release( p_vbi );
550                 emit newTelexPageSet( i_page );
551             }
552         }
553     }
554     else
555         emit teletextPossible( b_enabled );
556 }
557
558 void InputManager::activateTeletext( bool b_enable )
559 {
560     if( hasInput() )
561     {
562         const int i_teletext_es = var_GetInteger( p_input, "teletext-es" );
563         if( i_teletext_es >= 0 )
564         {
565             var_SetInteger( p_input, "spu-es", b_enable ? i_teletext_es : -1 );
566         }
567     }
568 }
569
570 void InputManager::slower()
571 {
572     if( hasInput() )
573         var_SetVoid( p_input, "rate-slower" );
574 }
575
576 void InputManager::faster()
577 {
578     if( hasInput() )
579         var_SetVoid( p_input, "rate-faster" );
580 }
581
582 void InputManager::normalRate()
583 {
584     if( hasInput() )
585         var_SetInteger( p_input, "rate", INPUT_RATE_DEFAULT );
586 }
587
588 void InputManager::setRate( int new_rate )
589 {
590     if( hasInput() )
591         var_SetInteger( p_input, "rate", new_rate );
592 }
593
594 void InputManager::setAtoB()
595 {
596     if( !timeA )
597     {
598         timeA = var_GetTime( THEMIM->getInput(), "time"  );
599     }
600     else if( !timeB )
601     {
602         timeB = var_GetTime( THEMIM->getInput(), "time"  );
603         var_SetTime( THEMIM->getInput(), "time" , timeA );
604     }
605     else
606     {
607         timeA = 0;
608         timeB = 0;
609     }
610     emit AtoBchanged( (timeA != 0 ), (timeB != 0 ) );
611 }
612
613 /* Function called regularly when in an AtoB loop */
614 void InputManager::AtoBLoop( int i_time )
615 {
616     if( timeB )
617     {
618         if( ( i_time >= (int)( timeB/1000000 ) )
619             || ( i_time < (int)( timeA/1000000 ) ) )
620             var_SetTime( THEMIM->getInput(), "time" , timeA );
621     }
622 }
623
624 /**********************************************************************
625  * MainInputManager implementation. Wrap an input manager and
626  * take care of updating the main playlist input.
627  * Used in the main playlist Dialog
628  **********************************************************************/
629 MainInputManager * MainInputManager::instance = NULL;
630
631 MainInputManager::MainInputManager( intf_thread_t *_p_intf )
632                  : QObject(NULL), p_intf( _p_intf )
633 {
634     p_input = NULL;
635     im = new InputManager( this, p_intf );
636
637 //    var_AddCallback( THEPL, "item-change", PLItemChanged, this );
638     var_AddCallback( THEPL, "item-change", ItemChanged, im );
639     var_AddCallback( THEPL, "playlist-current", PLItemChanged, this );
640     var_AddCallback( THEPL, "activity", PLItemChanged, this );
641
642     var_AddCallback( p_intf->p_libvlc, "volume-change", VolumeChanged, this );
643
644     /* Warn our embedded IM about input changes */
645     CONNECT( this, inputChanged( input_thread_t * ),
646              im, setInput( input_thread_t * ) );
647
648     /* emit check if playlist has allready started playing */
649     vlc_value_t val;
650     var_Change( THEPL, "playlist-current", VLC_VAR_CHOICESCOUNT, &val, NULL );
651     IMEvent *event = new IMEvent( ItemChanged_Type, val.i_int);
652     QApplication::postEvent( this, static_cast<QEvent*>(event) );
653
654 }
655
656 MainInputManager::~MainInputManager()
657 {
658     if( p_input )
659     {
660        var_DelCallback( p_input, "state", PLItemChanged, this );
661        vlc_object_release( p_input );
662        emit inputChanged( NULL );
663     }
664
665     var_DelCallback( p_intf->p_libvlc, "volume-change", VolumeChanged, this );
666
667     var_DelCallback( THEPL, "activity", PLItemChanged, this );
668     var_DelCallback( THEPL, "item-change", ItemChanged, im );
669 //    var_DelCallback( THEPL, "item-change", PLItemChanged, this );
670
671     var_DelCallback( THEPL, "playlist-current", PLItemChanged, this );
672 }
673
674 void MainInputManager::customEvent( QEvent *event )
675 {
676     int type = event->type();
677     if ( type != ItemChanged_Type && type != VolumeChanged_Type )
678         return;
679
680     // msg_Dbg( p_intf, "New MainIM Event of type: %i", type );
681     if( type == VolumeChanged_Type )
682     {
683         emit volumeChanged();
684         return;
685     }
686
687     /* Should be PLItemChanged Event */
688     if( VLC_OBJECT_INTF == p_intf->i_object_type ) /* FIXME: don't use object type */
689     {
690         vlc_mutex_lock( &p_intf->change_lock );
691         if( p_input && ( p_input->b_dead || !vlc_object_alive (p_input) ) )
692         {
693             var_DelCallback( p_input, "state", PLItemChanged, this );
694             vlc_object_release( p_input );
695             emit inputChanged( NULL );
696             p_input = NULL;
697             vlc_mutex_unlock( &p_intf->change_lock );
698             return;
699         }
700
701         if( !p_input )
702         {
703             p_input = playlist_CurrentInput(THEPL);
704             if( p_input )
705             {
706                 var_AddCallback( p_input, "state", PLItemChanged, this );
707                 emit inputChanged( p_input );
708             }
709         }
710         vlc_mutex_unlock( &p_intf->change_lock );
711     }
712     else
713     {
714         /* we are working as a dialogs provider */
715         playlist_t *p_playlist = pl_Hold( p_intf );
716         p_input = playlist_CurrentInput( p_playlist );
717         if( p_input )
718         {
719             emit inputChanged( p_input );
720             vlc_object_release( p_input );
721         }
722         pl_Release( p_intf );
723     }
724 }
725
726 /* Playlist Control functions */
727 void MainInputManager::stop()
728 {
729    playlist_Stop( THEPL );
730 }
731
732 void MainInputManager::next()
733 {
734    playlist_Next( THEPL );
735 }
736
737 void MainInputManager::prev()
738 {
739    playlist_Prev( THEPL );
740 }
741
742 void MainInputManager::togglePlayPause()
743 {
744     if( p_input == NULL )
745     {
746         playlist_Play( THEPL );
747         return;
748     }
749     getIM()->togglePlayPause();
750 }
751
752 bool MainInputManager::teletextState()
753 {
754     im = getIM();
755     if( im->hasInput() )
756     {
757         const int i_teletext_es = var_GetInteger( getInput(), "teletext-es" );
758         const int i_spu_es = var_GetInteger( getInput(), "spu-es" );
759
760         return i_teletext_es >= 0 && i_teletext_es == i_spu_es;
761     }
762     return false;
763 }
764
765 /* Static callbacks */
766
767 /* IM */
768 static int InterfaceChanged( vlc_object_t *p_this, const char *psz_var,
769                            vlc_value_t oldval, vlc_value_t newval, void *param )
770 {
771     /* FIXME remove that static variable */
772     static int counter = 0;
773     InputManager *im = (InputManager*)param;
774
775     counter = ++counter % 4;
776     if(!counter)
777         return VLC_SUCCESS;
778     IMEvent *event = new IMEvent( PositionUpdate_Type, 0 );
779     QApplication::postEvent( im, static_cast<QEvent*>(event) );
780     return VLC_SUCCESS;
781 }
782
783 static int StatisticsUpdated( vlc_object_t *p_this, const char *psz_var,
784                            vlc_value_t oldval, vlc_value_t newval, void *param )
785 {
786     InputManager *im = (InputManager*)param;
787
788     IMEvent *event = new IMEvent( StatisticsUpdate_Type, 0 );
789     QApplication::postEvent( im, static_cast<QEvent*>(event) );
790     return VLC_SUCCESS;
791 }
792
793 static int InterfaceVoutChanged( vlc_object_t *p_this, const char *psz_var,
794                                  vlc_value_t oldval, vlc_value_t newval, void *param )
795 {
796     InputManager *im = (InputManager*)param;
797
798     IMEvent *event = new IMEvent( InterfaceVoutUpdate_Type, 0 );
799     QApplication::postEvent( im, static_cast<QEvent*>(event) );
800     return VLC_SUCCESS;
801 }
802
803 static int ItemStateChanged( vlc_object_t *p_this, const char *psz_var,
804                            vlc_value_t oldval, vlc_value_t newval, void *param )
805 {
806     InputManager *im = (InputManager*)param;
807
808     IMEvent *event = new IMEvent( ItemStateChanged_Type, 0 );
809     QApplication::postEvent( im, static_cast<QEvent*>(event) );
810     return VLC_SUCCESS;
811 }
812
813 static int ItemRateChanged( vlc_object_t *p_this, const char *psz_var,
814                            vlc_value_t oldval, vlc_value_t newval, void *param )
815 {
816     InputManager *im = (InputManager*)param;
817
818     IMEvent *event = new IMEvent( ItemRateChanged_Type, 0 );
819     QApplication::postEvent( im, static_cast<QEvent*>(event) );
820     return VLC_SUCCESS;
821 }
822
823 static int ItemTitleChanged( vlc_object_t *p_this, const char *psz_var,
824                            vlc_value_t oldval, vlc_value_t newval, void *param )
825 {
826     InputManager *im = (InputManager*)param;
827
828     IMEvent *event = new IMEvent( ItemTitleChanged_Type, 0 );
829     QApplication::postEvent( im, static_cast<QEvent*>(event) );
830     return VLC_SUCCESS;
831 }
832
833 static int ItemChanged( vlc_object_t *p_this, const char *psz_var,
834                         vlc_value_t oldval, vlc_value_t newval, void *param )
835 {
836     InputManager *im = (InputManager*)param;
837
838     IMEvent *event = new IMEvent( ItemChanged_Type, newval.i_int );
839     QApplication::postEvent( im, static_cast<QEvent*>(event) );
840     return VLC_SUCCESS;
841 }
842
843 static int ChangeSPU( vlc_object_t *p_this, const char *var, vlc_value_t o,
844                         vlc_value_t n, void *param )
845 {
846     InputManager *im = (InputManager*)param;
847     IMEvent *event = new IMEvent( ItemSpuChanged_Type, 0 );
848     QApplication::postEvent( im, static_cast<QEvent*>(event) );
849     return VLC_SUCCESS;
850 }
851
852 static int ChangeTeletext( vlc_object_t *p_this, const char *var, vlc_value_t o,
853                            vlc_value_t n, void *param )
854 {
855
856     InputManager *im = (InputManager*)param;
857     IMEvent *event = new IMEvent( ItemTeletextChanged_Type, 0 );
858     QApplication::postEvent( im, static_cast<QEvent*>(event) );
859     return VLC_SUCCESS;
860 }
861
862 /* MIM */
863 static int PLItemChanged( vlc_object_t *p_this, const char *psz_var,
864                         vlc_value_t oldval, vlc_value_t newval, void *param )
865 {
866     MainInputManager *mim = (MainInputManager*)param;
867
868     IMEvent *event = new IMEvent( ItemChanged_Type, newval.i_int );
869     QApplication::postEvent( mim, static_cast<QEvent*>(event) );
870     return VLC_SUCCESS;
871 }
872
873 static int VolumeChanged( vlc_object_t *p_this, const char *psz_var,
874                         vlc_value_t oldval, vlc_value_t newval, void *param )
875 {
876     MainInputManager *mim = (MainInputManager*)param;
877
878     IMEvent *event = new IMEvent( VolumeChanged_Type, newval.i_int );
879     QApplication::postEvent( mim, static_cast<QEvent*>(event) );
880     return VLC_SUCCESS;
881 }
882