[vlc-devel] [PATCH 1/9] core: move all actions handling in actions.c

Hugo Beauzée-Luyssen hugo at beauzee.fr
Fri Aug 11 12:02:15 CEST 2017


From: Thomas Guillem <thomas at gllm.fr>

---
 include/vlc_actions.h     |  205 +++---
 include/vlc_plugin.h      |    4 +-
 modules/control/hotkeys.c | 1263 +----------------------------------
 src/libvlccore.sym        |    1 +
 src/misc/actions.c        | 1593 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 1730 insertions(+), 1336 deletions(-)

diff --git a/include/vlc_actions.h b/include/vlc_actions.h
index 404c1662a8..1da5a538c0 100644
--- a/include/vlc_actions.h
+++ b/include/vlc_actions.h
@@ -118,46 +118,16 @@ VLC_API char *vlc_keycode2str(uint_fast32_t i_key, bool locale) VLC_USED;
 VLC_API uint_fast32_t vlc_str2keycode(const char *str) VLC_USED;
 
 typedef enum vlc_action_id {
+    /* Takes no extra parameter */
     ACTIONID_NONE = 0,
     ACTIONID_QUIT,
-    ACTIONID_PLAY_PAUSE,
-    ACTIONID_PLAY,
-    ACTIONID_PAUSE,
-    ACTIONID_STOP,
-    ACTIONID_PREV,
-    ACTIONID_NEXT,
-    ACTIONID_SLOWER,
-    ACTIONID_FASTER,
-    ACTIONID_TOGGLE_FULLSCREEN,
-    ACTIONID_VOL_UP,
-    ACTIONID_VOL_DOWN,
-    ACTIONID_NAV_ACTIVATE,
-    ACTIONID_NAV_UP,
-    ACTIONID_NAV_DOWN,
-    ACTIONID_NAV_LEFT,
-    ACTIONID_NAV_RIGHT,
-    ACTIONID_JUMP_BACKWARD_EXTRASHORT,
-    ACTIONID_JUMP_FORWARD_EXTRASHORT,
-    ACTIONID_JUMP_BACKWARD_SHORT,
-    ACTIONID_JUMP_FORWARD_SHORT,
-    ACTIONID_JUMP_BACKWARD_MEDIUM,
-    ACTIONID_JUMP_FORWARD_MEDIUM,
-    ACTIONID_JUMP_BACKWARD_LONG,
-    ACTIONID_JUMP_FORWARD_LONG,
-    ACTIONID_FRAME_NEXT,
-    ACTIONID_POSITION,
-    ACTIONID_VOL_MUTE,
-/* let ACTIONID_SET_BOOMARK* and ACTIONID_PLAY_BOOKMARK* be contiguous */
-    ACTIONID_SET_BOOKMARK1,
-    ACTIONID_SET_BOOKMARK2,
-    ACTIONID_SET_BOOKMARK3,
-    ACTIONID_SET_BOOKMARK4,
-    ACTIONID_SET_BOOKMARK5,
-    ACTIONID_SET_BOOKMARK6,
-    ACTIONID_SET_BOOKMARK7,
-    ACTIONID_SET_BOOKMARK8,
-    ACTIONID_SET_BOOKMARK9,
-    ACTIONID_SET_BOOKMARK10,
+    ACTIONID_INTF_TOGGLE_FSC,
+    ACTIONID_INTF_HIDE,
+    ACTIONID_INTF_BOSS,
+    ACTIONID_INTF_POPUP_MENU,
+
+    /* Playlist actions don't take extra parameter */
+    /* let ACTIONID_SET_BOOMARK* and ACTIONID_PLAY_BOOKMARK* be contiguous */
     ACTIONID_PLAY_BOOKMARK1,
     ACTIONID_PLAY_BOOKMARK2,
     ACTIONID_PLAY_BOOKMARK3,
@@ -168,83 +138,141 @@ typedef enum vlc_action_id {
     ACTIONID_PLAY_BOOKMARK8,
     ACTIONID_PLAY_BOOKMARK9,
     ACTIONID_PLAY_BOOKMARK10,
+    ACTIONID_SET_BOOKMARK1,
+    ACTIONID_SET_BOOKMARK2,
+    ACTIONID_SET_BOOKMARK3,
+    ACTIONID_SET_BOOKMARK4,
+    ACTIONID_SET_BOOKMARK5,
+    ACTIONID_SET_BOOKMARK6,
+    ACTIONID_SET_BOOKMARK7,
+    ACTIONID_SET_BOOKMARK8,
+    ACTIONID_SET_BOOKMARK9,
+    ACTIONID_SET_BOOKMARK10,
     /* end of contiguous zone */
+    ACTIONID_LOOP,
+    ACTIONID_RANDOM,
+    ACTIONID_NEXT,
+    ACTIONID_PREV,
+    ACTIONID_STOP,
+    ACTIONID_RATE_NORMAL,
+    ACTIONID_FASTER,
+    ACTIONID_SLOWER,
+    ACTIONID_RATE_FASTER_FINE,
+    ACTIONID_RATE_SLOWER_FINE,
     ACTIONID_PLAY_CLEAR,
-    ACTIONID_SUBDELAY_UP,
-    ACTIONID_SUBDELAY_DOWN,
+    ACTIONID_VOL_UP,
+    ACTIONID_VOL_DOWN,
+    ACTIONID_VOL_MUTE,
+    /* End of playlist related actions */
+
+    /*
+     * Audio output actions may take an audio_output_t* as 1st and only
+     * extra parameter. Omiting it or passing NULL will fallback to the current
+     * audio output
+     */
+    /* Cycle Through Audio Devices */
+    ACTIONID_AUDIODEVICE_CYCLE,
+    /* End of audio output related actions */
+
+    ACTIONID_PLAY_PAUSE,
+    ACTIONID_PLAY,
+    ACTIONID_PAUSE,
+    ACTIONID_RECORD,
+    ACTIONID_FRAME_NEXT,
     ACTIONID_SUBSYNC_MARKAUDIO,
     ACTIONID_SUBSYNC_MARKSUB,
     ACTIONID_SUBSYNC_APPLY,
     ACTIONID_SUBSYNC_RESET,
-    ACTIONID_SUBPOS_UP,
-    ACTIONID_SUBPOS_DOWN,
+    ACTIONID_SUBDELAY_DOWN,
+    ACTIONID_SUBDELAY_UP,
+    ACTIONID_AUDIODELAY_DOWN,
+    ACTIONID_AUDIODELAY_UP,
     ACTIONID_AUDIO_TRACK,
-    ACTIONID_SUBTITLE_REVERSE_TRACK,
     ACTIONID_SUBTITLE_TRACK,
+    ACTIONID_SUBTITLE_REVERSE_TRACK,
     ACTIONID_SUBTITLE_TOGGLE,
-    ACTIONID_SUBTITLE_TEXT_SCALE_NORMAL,
-    ACTIONID_SUBTITLE_TEXT_SCALE_UP,
-    ACTIONID_SUBTITLE_TEXT_SCALE_DOWN,
-    ACTIONID_INTF_TOGGLE_FSC,
-    ACTIONID_INTF_HIDE,
-    ACTIONID_INTF_BOSS,
+    /* Cycle Through Program Service IDs */
+    ACTIONID_PROGRAM_SID_NEXT,
+    ACTIONID_PROGRAM_SID_PREV,
+    ACTIONID_JUMP_BACKWARD_EXTRASHORT,
+    ACTIONID_JUMP_FORWARD_EXTRASHORT,
+    ACTIONID_JUMP_BACKWARD_SHORT,
+    ACTIONID_JUMP_FORWARD_SHORT,
+    ACTIONID_JUMP_BACKWARD_MEDIUM,
+    ACTIONID_JUMP_FORWARD_MEDIUM,
+    ACTIONID_JUMP_BACKWARD_LONG,
+    ACTIONID_JUMP_FORWARD_LONG,
     /* chapter and title navigation */
     ACTIONID_TITLE_PREV,
     ACTIONID_TITLE_NEXT,
     ACTIONID_CHAPTER_PREV,
     ACTIONID_CHAPTER_NEXT,
     /* end of chapter and title navigation */
-    ACTIONID_AUDIODELAY_UP,
-    ACTIONID_AUDIODELAY_DOWN,
-    ACTIONID_SNAPSHOT,
-    ACTIONID_RECORD,
     ACTIONID_DISC_MENU,
+    ACTIONID_NAV_ACTIVATE,
+
+    /*
+     * Video output actions
+     * Those actions may take a vout_thread_t* as 1st and only extra parameter.
+     * Omiting it or passing NULL will fallback to the current video output
+     */
+    ACTIONID_WALLPAPER,
+    ACTIONID_SNAPSHOT,
+    ACTIONID_TOGGLE_FULLSCREEN,
+    ACTIONID_LEAVE_FULLSCREEN,
     ACTIONID_ASPECT_RATIO,
     ACTIONID_CROP,
-    ACTIONID_DEINTERLACE,
-    ACTIONID_DEINTERLACE_MODE,
-    ACTIONID_ZOOM,
-    ACTIONID_UNZOOM,
     ACTIONID_CROP_TOP,
     ACTIONID_UNCROP_TOP,
-    ACTIONID_CROP_LEFT,
-    ACTIONID_UNCROP_LEFT,
     ACTIONID_CROP_BOTTOM,
     ACTIONID_UNCROP_BOTTOM,
+    ACTIONID_CROP_LEFT,
+    ACTIONID_UNCROP_LEFT,
     ACTIONID_CROP_RIGHT,
-    ACTIONID_UNCROP_RIGHT,
-    ACTIONID_RANDOM,
-    ACTIONID_LOOP,
-    ACTIONID_WALLPAPER,
-    ACTIONID_LEAVE_FULLSCREEN,
+    ACTIONID_UNCROP_RIGHT,\
+    /* scaling */
+    ACTIONID_TOGGLE_AUTOSCALE,
+    ACTIONID_SCALE_UP,
+    ACTIONID_SCALE_DOWN,
     /* Zoom */
     ACTIONID_ZOOM_QUARTER,
     ACTIONID_ZOOM_HALF,
     ACTIONID_ZOOM_ORIGINAL,
     ACTIONID_ZOOM_DOUBLE,
-    /* Cycle Through Audio Devices */
-    ACTIONID_AUDIODEVICE_CYCLE,
-    /* scaling */
-    ACTIONID_TOGGLE_AUTOSCALE,
-    ACTIONID_SCALE_UP,
-    ACTIONID_SCALE_DOWN,
-    /* */
-    ACTIONID_RATE_NORMAL,
-    ACTIONID_RATE_SLOWER_FINE,
-    ACTIONID_RATE_FASTER_FINE,
-    /* Cycle Through Program Service IDs */
-    ACTIONID_PROGRAM_SID_NEXT,
-    ACTIONID_PROGRAM_SID_PREV,
-    ACTIONID_INTF_POPUP_MENU,
+    ACTIONID_ZOOM,
+    ACTIONID_UNZOOM,
+    /* Deinterlace */
+    ACTIONID_DEINTERLACE,
+    ACTIONID_DEINTERLACE_MODE,
+    /* Subtitles */
+    ACTIONID_SUBTITLE_TEXT_SCALE_DOWN,
+    ACTIONID_SUBTITLE_TEXT_SCALE_UP,
+    ACTIONID_SUBTITLE_TEXT_SCALE_NORMAL,
+
+    /*
+     * Input & video_output actions
+     * Those actions may take at least 2 extra parameters:
+     * - An input_thread_t*
+     * - A vout_thread_t*
+     * Omiting them or passing NULL will fallback to the current input/vout
+     */
+    ACTIONID_POSITION,
+    /* Combo Actions */
+    ACTIONID_COMBO_VOL_FOV_UP,
+    ACTIONID_COMBO_VOL_FOV_DOWN,
+    /* Navigation */
+    ACTIONID_NAV_UP,
+    ACTIONID_NAV_DOWN,
+    ACTIONID_NAV_LEFT,
+    ACTIONID_NAV_RIGHT,
     /* Viewpoint */
     ACTIONID_VIEWPOINT_FOV_IN,
     ACTIONID_VIEWPOINT_FOV_OUT,
     ACTIONID_VIEWPOINT_ROLL_CLOCK,
     ACTIONID_VIEWPOINT_ROLL_ANTICLOCK,
-    /* Combo Actions */
-    ACTIONID_COMBO_VOL_FOV_UP,
-    ACTIONID_COMBO_VOL_FOV_DOWN,
-
+    ACTIONID_VIEWPOINT_UPDATE, /* Arg 3: vlc_viewpoint_t* */
+    ACTIONID_SUBPOS_DOWN,
+    ACTIONID_SUBPOS_UP
 } vlc_action_id_t;
 
 /**
@@ -272,4 +300,17 @@ VLC_API const char* const*
 vlc_actions_get_key_names(vlc_object_t *p_obj);
 #define vlc_actions_get_key_names(x) vlc_actions_get_key_names(VLC_OBJECT(x))
 
+/**
+ * Execute an action
+ *
+ * \param a valid vlc object
+ * \param i_action action to execute
+ * \param b_notify notify a new state via the OSD of the current vout
+ * \return VLC_SUCCESS or a VLC error
+ */
+VLC_API int
+vlc_actions_do(vlc_object_t *p_obj, vlc_action_id_t i_action,
+               bool b_notify, unsigned int i_nb_args, ...);
+#define vlc_actions_do(a,b,c,...) vlc_actions_do(VLC_OBJECT(a),b,c, __VA_ARGS__)
+
 #endif
diff --git a/include/vlc_plugin.h b/include/vlc_plugin.h
index 7e9e447d04..339d6b445f 100644
--- a/include/vlc_plugin.h
+++ b/include/vlc_plugin.h
@@ -188,8 +188,8 @@ enum vlc_module_properties
 /**
  * Current plugin ABI version
  */
-# define MODULE_SYMBOL 3_0_0f
-# define MODULE_SUFFIX "__3_0_0f"
+# define MODULE_SYMBOL 3_0_0g
+# define MODULE_SUFFIX "__3_0_0g"
 
 /*****************************************************************************
  * Add a few defines. You do not want to read this section. Really.
diff --git a/modules/control/hotkeys.c b/modules/control/hotkeys.c
index 26520bd59c..c3913eefdc 100644
--- a/modules/control/hotkeys.c
+++ b/modules/control/hotkeys.c
@@ -1,7 +1,7 @@
 /*****************************************************************************
  * hotkeys.c: Hotkey handling for vlc
  *****************************************************************************
- * Copyright (C) 2005-2009 the VideoLAN team
+ * Copyright (C) 2005-2016 the VideoLAN team
  * $Id$
  *
  * Authors: Sigmund Augdal Helberg <dnumgis at videolan.org>
@@ -33,14 +33,10 @@
 #define VLC_MODULE_LICENSE VLC_LICENSE_GPL_2_PLUS
 #include <vlc_common.h>
 #include <vlc_plugin.h>
+#include <vlc_actions.h>
 #include <vlc_interface.h>
 #include <vlc_input.h>
-#include <vlc_aout.h>
-#include <vlc_viewpoint.h>
-#include <vlc_vout_osd.h>
 #include <vlc_playlist.h>
-#include <vlc_actions.h>
-#include "math.h"
 
 #include <assert.h>
 
@@ -52,14 +48,6 @@ struct intf_sys_t
     vlc_mutex_t         lock;
     vout_thread_t      *p_vout;
     input_thread_t     *p_input;
-    int slider_chan;
-
-    /*subtitle_delaybookmarks: placeholder for storing subtitle sync timestamps*/
-    struct
-    {
-        int64_t i_time_subtitle;
-        int64_t i_time_audio;
-    } subtitle_delaybookmarks;
 
     struct
     {
@@ -74,23 +62,8 @@ struct intf_sys_t
  *****************************************************************************/
 static int  Open    ( vlc_object_t * );
 static void Close   ( vlc_object_t * );
-static int  ActionEvent( vlc_object_t *, char const *,
-                         vlc_value_t, vlc_value_t, void * );
-static void PlayBookmark( intf_thread_t *, int );
-static void SetBookmark ( intf_thread_t *, int );
-static void DisplayPosition( vout_thread_t *, int,  input_thread_t * );
-static void DisplayVolume( vout_thread_t *, int, float );
-static void DisplayRate ( vout_thread_t *, float );
-static float AdjustRateFine( vlc_object_t *, const int );
-static void ClearChannels  ( vout_thread_t *, int );
-
-#define DisplayMessage(vout, ...) \
-    do { \
-        if (vout) \
-            vout_OSDMessage(vout, VOUT_SPU_CHANNEL_OSD, __VA_ARGS__); \
-    } while(0)
-#define DisplayIcon(vout, icon) \
-    do { if(vout) vout_OSDIcon(vout, VOUT_SPU_CHANNEL_OSD, icon); } while(0)
+static int ActionEvent( vlc_object_t *libvlc, char const *psz_var,
+                        vlc_value_t oldval, vlc_value_t newval, void *p_data );
 
 /*****************************************************************************
  * Module descriptor
@@ -124,7 +97,8 @@ static int MovedEvent( vlc_object_t *p_this, char const *psz_var,
             .pitch = -i_vertical   * 0.05f,
         };
 
-        input_UpdateViewpoint( p_sys->p_input, &viewpoint, false );
+        vlc_actions_do( p_this, ACTIONID_VIEWPOINT_UPDATE, false, 3, NULL, NULL,
+                        &viewpoint );
 
         p_sys->vrnav.x = newval.coords.x;
         p_sys->vrnav.y = newval.coords.y;
@@ -137,12 +111,10 @@ static int ViewpointMovedEvent( vlc_object_t *p_this, char const *psz_var,
                                 vlc_value_t oldval, vlc_value_t newval,
                                 void *p_data )
 {
-    intf_thread_t *p_intf = (intf_thread_t *)p_data;
-    intf_sys_t    *p_sys = p_intf->p_sys;
+    (void) psz_var; (void) oldval; (void) p_data;
 
-    (void) p_this; (void) psz_var; (void) oldval;
-
-    input_UpdateViewpoint( p_sys->p_input, newval.p_address, false );
+    vlc_actions_do( p_this, ACTIONID_VIEWPOINT_UPDATE, false, 3, NULL, NULL,
+                    newval.p_address );
 
     return VLC_SUCCESS;
 }
@@ -174,23 +146,16 @@ static void ChangeVout( intf_thread_t *p_intf, vout_thread_t *p_vout )
 {
     intf_sys_t *p_sys = p_intf->p_sys;
 
-    int slider_chan;
     bool b_vrnav_can_change;
     if( p_vout != NULL )
-    {
-        slider_chan = vout_RegisterSubpictureChannel( p_vout );
         b_vrnav_can_change = var_GetBool( p_vout, "viewpoint-changeable" );
-    }
 
     vlc_mutex_lock( &p_sys->lock );
     vout_thread_t *p_old_vout = p_sys->p_vout;
     bool b_vrnav_could_change = p_sys->vrnav.b_can_change;
     p_sys->p_vout = p_vout;
     if( p_vout != NULL )
-    {
-        p_sys->slider_chan = slider_chan;
         p_sys->vrnav.b_can_change = b_vrnav_can_change;
-    }
     else
         p_sys->vrnav.b_can_change = false;
     vlc_mutex_unlock( &p_sys->lock );
@@ -310,8 +275,6 @@ static int Open( vlc_object_t *p_this )
     p_sys->p_input = NULL;
     p_sys->vrnav.b_can_change = false;
     p_sys->vrnav.b_button_pressed = false;
-    p_sys->subtitle_delaybookmarks.i_time_audio = 0;
-    p_sys->subtitle_delaybookmarks.i_time_subtitle = 0;
 
     vlc_mutex_init( &p_sys->lock );
 
@@ -342,1220 +305,16 @@ static void Close( vlc_object_t *p_this )
     free( p_sys );
 }
 
-static int PutAction( intf_thread_t *p_intf, input_thread_t *p_input,
-                      vout_thread_t *p_vout, int slider_chan, bool b_vrnav,
-                      int i_action )
-{
-#define DO_ACTION(x) PutAction( p_intf, p_input, p_vout, slider_chan, b_vrnav, x)
-    intf_sys_t *p_sys = p_intf->p_sys;
-    playlist_t *p_playlist = pl_Get( p_intf );
-
-    /* Quit */
-    switch( i_action )
-    {
-        /* Libvlc / interface actions */
-        case ACTIONID_QUIT:
-            libvlc_Quit( p_intf->obj.libvlc );
-
-            ClearChannels( p_vout, slider_chan );
-            DisplayMessage( p_vout, _( "Quit" ) );
-            break;
-
-        case ACTIONID_INTF_TOGGLE_FSC:
-        case ACTIONID_INTF_HIDE:
-            var_TriggerCallback( p_intf->obj.libvlc, "intf-toggle-fscontrol" );
-            break;
-        case ACTIONID_INTF_BOSS:
-            var_TriggerCallback( p_intf->obj.libvlc, "intf-boss" );
-            break;
-        case ACTIONID_INTF_POPUP_MENU:
-            var_TriggerCallback( p_intf->obj.libvlc, "intf-popupmenu" );
-            break;
-
-        /* Playlist actions (including audio) */
-        case ACTIONID_LOOP:
-        {
-            /* Toggle Normal -> Loop -> Repeat -> Normal ... */
-            const char *mode;
-            if( var_GetBool( p_playlist, "repeat" ) )
-            {
-                var_SetBool( p_playlist, "repeat", false );
-                mode = N_("Off");
-            }
-            else
-            if( var_GetBool( p_playlist, "loop" ) )
-            { /* FIXME: this is not atomic, we should use a real tristate */
-                var_SetBool( p_playlist, "loop", false );
-                var_SetBool( p_playlist, "repeat", true );
-                mode = N_("One");
-            }
-            else
-            {
-                var_SetBool( p_playlist, "loop", true );
-                mode = N_("All");
-            }
-            DisplayMessage( p_vout, _("Loop: %s"), vlc_gettext(mode) );
-            break;
-        }
-
-        case ACTIONID_RANDOM:
-        {
-            const bool state = var_ToggleBool( p_playlist, "random" );
-            DisplayMessage( p_vout, _("Random: %s"),
-                            vlc_gettext( state ? N_("On") : N_("Off") ) );
-            break;
-        }
-
-        case ACTIONID_NEXT:
-            DisplayMessage( p_vout, _("Next") );
-            playlist_Next( p_playlist );
-            break;
-        case ACTIONID_PREV:
-            DisplayMessage( p_vout, _("Previous") );
-            playlist_Prev( p_playlist );
-            break;
-
-        case ACTIONID_STOP:
-            playlist_Stop( p_playlist );
-            break;
-
-        case ACTIONID_RATE_NORMAL:
-            var_SetFloat( p_playlist, "rate", 1.f );
-            DisplayRate( p_vout, 1.f );
-            break;
-        case ACTIONID_FASTER:
-            var_TriggerCallback( p_playlist, "rate-faster" );
-            DisplayRate( p_vout, var_GetFloat( p_playlist, "rate" ) );
-            break;
-        case ACTIONID_SLOWER:
-            var_TriggerCallback( p_playlist, "rate-slower" );
-            DisplayRate( p_vout, var_GetFloat( p_playlist, "rate" ) );
-            break;
-        case ACTIONID_RATE_FASTER_FINE:
-        case ACTIONID_RATE_SLOWER_FINE:
-        {
-            const int i_dir = i_action == ACTIONID_RATE_FASTER_FINE ? 1 : -1;
-            float rate = AdjustRateFine( VLC_OBJECT(p_playlist), i_dir );
-
-            var_SetFloat( p_playlist, "rate", rate );
-            DisplayRate( p_vout, rate );
-            break;
-        }
-
-        case ACTIONID_PLAY_BOOKMARK1:
-        case ACTIONID_PLAY_BOOKMARK2:
-        case ACTIONID_PLAY_BOOKMARK3:
-        case ACTIONID_PLAY_BOOKMARK4:
-        case ACTIONID_PLAY_BOOKMARK5:
-        case ACTIONID_PLAY_BOOKMARK6:
-        case ACTIONID_PLAY_BOOKMARK7:
-        case ACTIONID_PLAY_BOOKMARK8:
-        case ACTIONID_PLAY_BOOKMARK9:
-        case ACTIONID_PLAY_BOOKMARK10:
-            PlayBookmark( p_intf, i_action - ACTIONID_PLAY_BOOKMARK1 + 1 );
-            break;
-
-        case ACTIONID_SET_BOOKMARK1:
-        case ACTIONID_SET_BOOKMARK2:
-        case ACTIONID_SET_BOOKMARK3:
-        case ACTIONID_SET_BOOKMARK4:
-        case ACTIONID_SET_BOOKMARK5:
-        case ACTIONID_SET_BOOKMARK6:
-        case ACTIONID_SET_BOOKMARK7:
-        case ACTIONID_SET_BOOKMARK8:
-        case ACTIONID_SET_BOOKMARK9:
-        case ACTIONID_SET_BOOKMARK10:
-            SetBookmark( p_intf, i_action - ACTIONID_SET_BOOKMARK1 + 1 );
-            break;
-        case ACTIONID_PLAY_CLEAR:
-            playlist_Clear( p_playlist, pl_Unlocked );
-            break;
-        case ACTIONID_VOL_UP:
-        {
-            float vol;
-            if( playlist_VolumeUp( p_playlist, 1, &vol ) == 0 )
-                DisplayVolume( p_vout, slider_chan, vol );
-            break;
-        }
-        case ACTIONID_VOL_DOWN:
-        {
-            float vol;
-            if( playlist_VolumeDown( p_playlist, 1, &vol ) == 0 )
-                DisplayVolume( p_vout, slider_chan, vol );
-            break;
-        }
-        case ACTIONID_VOL_MUTE:
-        {
-            int mute = playlist_MuteGet( p_playlist );
-            if( mute < 0 )
-                break;
-            mute = !mute;
-            if( playlist_MuteSet( p_playlist, mute ) )
-                break;
-
-            float vol = playlist_VolumeGet( p_playlist );
-            if( mute || vol == 0.f )
-            {
-                ClearChannels( p_vout, slider_chan );
-                DisplayIcon( p_vout, OSD_MUTE_ICON );
-            }
-            else
-                DisplayVolume( p_vout, slider_chan, vol );
-            break;
-        }
-
-        case ACTIONID_AUDIODEVICE_CYCLE:
-        {
-            audio_output_t *p_aout = playlist_GetAout( p_playlist );
-            if( p_aout == NULL )
-                break;
-
-            char **ids, **names;
-            int n = aout_DevicesList( p_aout, &ids, &names );
-            if( n == -1 )
-                break;
-
-            char *dev = aout_DeviceGet( p_aout );
-            const char *devstr = (dev != NULL) ? dev : "";
-
-            int idx = 0;
-            for( int i = 0; i < n; i++ )
-            {
-                if( !strcmp(devstr, ids[i]) )
-                    idx = (i + 1) % n;
-            }
-            free( dev );
-
-            if( !aout_DeviceSet( p_aout, ids[idx] ) )
-                DisplayMessage( p_vout, _("Audio Device: %s"), names[idx] );
-            vlc_object_release( p_aout );
-
-            for( int i = 0; i < n; i++ )
-            {
-                free( ids[i] );
-                free( names[i] );
-            }
-            free( ids );
-            free( names );
-            break;
-        }
-
-        /* Playlist + input actions */
-        case ACTIONID_PLAY_PAUSE:
-            if( p_input )
-            {
-                ClearChannels( p_vout, slider_chan );
-
-                int state = var_GetInteger( p_input, "state" );
-                DisplayIcon( p_vout, state != PAUSE_S ? OSD_PAUSE_ICON : OSD_PLAY_ICON );
-            }
-            playlist_TogglePause( p_playlist );
-            break;
-
-        case ACTIONID_PLAY:
-            if( p_input && var_GetFloat( p_input, "rate" ) != 1.f )
-                /* Return to normal speed */
-                var_SetFloat( p_input, "rate", 1.f );
-            else
-            {
-                ClearChannels( p_vout, slider_chan );
-                DisplayIcon( p_vout, OSD_PLAY_ICON );
-                playlist_Play( p_playlist );
-            }
-            break;
-
-        /* Playlist + video output actions */
-        case ACTIONID_WALLPAPER:
-        {
-            bool wp = var_ToggleBool( p_playlist, "video-wallpaper" );
-            if( p_vout )
-                var_SetBool( p_vout, "video-wallpaper", wp );
-            break;
-        }
-
-        /* Input actions */
-        case ACTIONID_PAUSE:
-            if( p_input && var_GetInteger( p_input, "state" ) != PAUSE_S )
-            {
-                ClearChannels( p_vout, slider_chan );
-                DisplayIcon( p_vout, OSD_PAUSE_ICON );
-                var_SetInteger( p_input, "state", PAUSE_S );
-            }
-            break;
-
-        case ACTIONID_RECORD:
-            if( p_input && var_GetBool( p_input, "can-record" ) )
-            {
-                const bool on = var_ToggleBool( p_input, "record" );
-                DisplayMessage( p_vout, vlc_gettext(on
-                                   ? N_("Recording") : N_("Recording done")) );
-            }
-            break;
-
-        case ACTIONID_FRAME_NEXT:
-            if( p_input )
-            {
-                var_TriggerCallback( p_input, "frame-next" );
-                DisplayMessage( p_vout, _("Next frame") );
-            }
-            break;
-
-        case ACTIONID_SUBSYNC_MARKAUDIO:
-        {
-            p_sys->subtitle_delaybookmarks.i_time_audio = mdate();
-            DisplayMessage( p_vout, _("Sub sync: bookmarked audio time"));
-            break;
-        }
-        case ACTIONID_SUBSYNC_MARKSUB:
-            if( p_input )
-            {
-                vlc_value_t val, list, list2;
-                int i_count;
-                var_Get( p_input, "spu-es", &val );
-
-                var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
-                            &list, &list2 );
-                i_count = list.p_list->i_count;
-                if( i_count < 1 || val.i_int < 0 )
-                {
-                    DisplayMessage( p_vout, _("No active subtitle") );
-                    var_FreeList( &list, &list2 );
-                    break;
-                }
-                p_sys->subtitle_delaybookmarks.i_time_subtitle = mdate();
-                DisplayMessage( p_vout,
-                                _("Sub sync: bookmarked subtitle time"));
-                var_FreeList( &list, &list2 );
-            }
-            break;
-        case ACTIONID_SUBSYNC_APPLY:
-        {
-            /* Warning! Can yield a pause in the playback.
-             * For example, the following succession of actions will yield a 5 second delay :
-             * - Pressing Shift-H (ACTIONID_SUBSYNC_MARKAUDIO)
-             * - wait 5 second
-             * - Press Shift-J (ACTIONID_SUBSYNC_MARKSUB)
-             * - Press Shift-K (ACTIONID_SUBSYNC_APPLY)
-             * --> 5 seconds pause
-             * This is due to var_SetTime() (and ultimately UpdatePtsDelay())
-             * which causes the video to pause for an equivalent duration
-             * (This problem is also present in the "Track synchronization" window) */
-            if ( p_input )
-            {
-                if ( (p_sys->subtitle_delaybookmarks.i_time_audio == 0) || (p_sys->subtitle_delaybookmarks.i_time_subtitle == 0) )
-                {
-                    DisplayMessage( p_vout, _( "Sub sync: set bookmarks first!" ) );
-                }
-                else
-                {
-                    int64_t i_current_subdelay = var_GetInteger( p_input, "spu-delay" );
-                    int64_t i_additional_subdelay = p_sys->subtitle_delaybookmarks.i_time_audio - p_sys->subtitle_delaybookmarks.i_time_subtitle;
-                    int64_t i_total_subdelay = i_current_subdelay + i_additional_subdelay;
-                    var_SetInteger( p_input, "spu-delay", i_total_subdelay);
-                    ClearChannels( p_vout, slider_chan );
-                    DisplayMessage( p_vout, _( "Sub sync: corrected %i ms (total delay = %i ms)" ),
-                                            (int)(i_additional_subdelay / 1000),
-                                            (int)(i_total_subdelay / 1000) );
-                    p_sys->subtitle_delaybookmarks.i_time_audio = 0;
-                    p_sys->subtitle_delaybookmarks.i_time_subtitle = 0;
-                }
-            }
-            break;
-        }
-        case ACTIONID_SUBSYNC_RESET:
-        {
-            var_SetInteger( p_input, "spu-delay", 0);
-            ClearChannels( p_vout, slider_chan );
-            DisplayMessage( p_vout, _( "Sub sync: delay reset" ) );
-            p_sys->subtitle_delaybookmarks.i_time_audio = 0;
-            p_sys->subtitle_delaybookmarks.i_time_subtitle = 0;
-            break;
-        }
-
-        case ACTIONID_SUBDELAY_DOWN:
-        case ACTIONID_SUBDELAY_UP:
-        {
-            int diff = (i_action == ACTIONID_SUBDELAY_UP) ? 50000 : -50000;
-            if( p_input )
-            {
-                vlc_value_t val, list, list2;
-                int i_count;
-                var_Get( p_input, "spu-es", &val );
-
-                var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
-                            &list, &list2 );
-                i_count = list.p_list->i_count;
-                if( i_count < 1 || val.i_int < 0 )
-                {
-                    DisplayMessage( p_vout, _("No active subtitle") );
-                    var_FreeList( &list, &list2 );
-                    break;
-                }
-                int64_t i_delay = var_GetInteger( p_input, "spu-delay" ) + diff;
-
-                var_SetInteger( p_input, "spu-delay", i_delay );
-                ClearChannels( p_vout, slider_chan );
-                DisplayMessage( p_vout, _( "Subtitle delay %i ms" ),
-                                (int)(i_delay/1000) );
-                var_FreeList( &list, &list2 );
-            }
-            break;
-        }
-        case ACTIONID_AUDIODELAY_DOWN:
-        case ACTIONID_AUDIODELAY_UP:
-        {
-            int diff = (i_action == ACTIONID_AUDIODELAY_UP) ? 50000 : -50000;
-            if( p_input )
-            {
-                int64_t i_delay = var_GetInteger( p_input, "audio-delay" )
-                                  + diff;
-
-                var_SetInteger( p_input, "audio-delay", i_delay );
-                ClearChannels( p_vout, slider_chan );
-                DisplayMessage( p_vout, _( "Audio delay %i ms" ),
-                                 (int)(i_delay/1000) );
-            }
-            break;
-        }
-
-        case ACTIONID_AUDIO_TRACK:
-            if( p_input )
-            {
-                vlc_value_t val, list, list2;
-                int i_count, i;
-                var_Get( p_input, "audio-es", &val );
-                var_Change( p_input, "audio-es", VLC_VAR_GETCHOICES,
-                            &list, &list2 );
-                i_count = list.p_list->i_count;
-                if( i_count > 1 )
-                {
-                    for( i = 0; i < i_count; i++ )
-                    {
-                        if( val.i_int == list.p_list->p_values[i].i_int )
-                        {
-                            break;
-                        }
-                    }
-                    /* value of audio-es was not in choices list */
-                    if( i == i_count )
-                    {
-                        msg_Warn( p_input,
-                                  "invalid current audio track, selecting 0" );
-                        i = 0;
-                    }
-                    else if( i == i_count - 1 )
-                        i = 1;
-                    else
-                        i++;
-                    var_Set( p_input, "audio-es", list.p_list->p_values[i] );
-                    DisplayMessage( p_vout, _("Audio track: %s"),
-                                    list2.p_list->p_values[i].psz_string );
-                }
-                var_FreeList( &list, &list2 );
-            }
-            break;
-
-        case ACTIONID_SUBTITLE_TRACK:
-        case ACTIONID_SUBTITLE_REVERSE_TRACK:
-            if( p_input )
-            {
-                vlc_value_t val, list, list2;
-                int i_count, i;
-                var_Get( p_input, "spu-es", &val );
-
-                var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
-                            &list, &list2 );
-                i_count = list.p_list->i_count;
-                if( i_count <= 1 )
-                {
-                    DisplayMessage( p_vout, _("Subtitle track: %s"),
-                                    _("N/A") );
-                    var_FreeList( &list, &list2 );
-                    break;
-                }
-                for( i = 0; i < i_count; i++ )
-                {
-                    if( val.i_int == list.p_list->p_values[i].i_int )
-                    {
-                        break;
-                    }
-                }
-                /* value of spu-es was not in choices list */
-                if( i == i_count )
-                {
-                    msg_Warn( p_input,
-                              "invalid current subtitle track, selecting 0" );
-                    i = 0;
-                }
-                else if ((i == i_count - 1) && (i_action == ACTIONID_SUBTITLE_TRACK))
-                    i = 0;
-                else if ((i == 0) && (i_action == ACTIONID_SUBTITLE_REVERSE_TRACK))
-                    i = i_count - 1;
-                else
-                    i = (i_action == ACTIONID_SUBTITLE_TRACK) ? i+1 : i-1;
-                var_SetInteger( p_input, "spu-es", list.p_list->p_values[i].i_int );
-                var_SetInteger( p_input, "spu-choice", list.p_list->p_values[i].i_int );
-                DisplayMessage( p_vout, _("Subtitle track: %s"),
-                                list2.p_list->p_values[i].psz_string );
-                var_FreeList( &list, &list2 );
-            }
-            break;
-        case ACTIONID_SUBTITLE_TOGGLE:
-            if( p_input )
-            {
-                vlc_value_t list, list2;
-                int i_count, i_sel_index, i_sel_id, i_old_id, i_new_index;
-                i_old_id = var_GetInteger( p_input, "spu-es" );
-                i_sel_id = var_GetInteger( p_input, "spu-choice" );
-
-                var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
-                            &list, &list2 );
-                i_count = list.p_list->i_count;
-                if( i_count <= 1 )
-                {
-                    DisplayMessage( p_vout, _("Subtitle track: %s"),
-                                    _("N/A") );
-                    var_FreeList( &list, &list2 );
-                    break;
-                }
-                for( i_sel_index = 0; i_sel_index < i_count; i_sel_index++ )
-                {
-                    if( i_sel_id == list.p_list->p_values[i_sel_index].i_int )
-                    {
-                        break;
-                    }
-                }
-                /* if there is nothing to toggle choose the first track */
-                if( !i_sel_index ) {
-                    i_sel_index = 1;
-                    i_sel_id = list.p_list->p_values[1].i_int;
-                    var_SetInteger( p_input, "spu-choice", i_sel_id );
-                }
-
-                i_new_index = 0;
-                if( i_old_id != i_sel_id )
-                {
-                    if( i_sel_index >= i_count )
-                    {
-                        var_SetInteger( p_input, "spu-choice", list.p_list->p_values[0].i_int );
-                    }
-                    else
-                    {
-                        i_new_index = i_sel_index;
-                    }
-                }
-                var_SetInteger( p_input, "spu-es", list.p_list->p_values[i_new_index].i_int );
-                DisplayMessage( p_vout, _("Subtitle track: %s"),
-                                list2.p_list->p_values[i_new_index].psz_string );
-                var_FreeList( &list, &list2 );
-            }
-            break;
-        case ACTIONID_PROGRAM_SID_NEXT:
-        case ACTIONID_PROGRAM_SID_PREV:
-            if( p_input )
-            {
-                vlc_value_t val, list, list2;
-                int i_count, i;
-                var_Get( p_input, "program", &val );
-
-                var_Change( p_input, "program", VLC_VAR_GETCHOICES,
-                            &list, &list2 );
-                i_count = list.p_list->i_count;
-                if( i_count <= 1 )
-                {
-                    DisplayMessage( p_vout, _("Program Service ID: %s"),
-                                    _("N/A") );
-                    var_FreeList( &list, &list2 );
-                    break;
-                }
-                for( i = 0; i < i_count; i++ )
-                {
-                    if( val.i_int == list.p_list->p_values[i].i_int )
-                    {
-                        break;
-                    }
-                }
-                /* value of program sid was not in choices list */
-                if( i == i_count )
-                {
-                    msg_Warn( p_input,
-                              "invalid current program SID, selecting 0" );
-                    i = 0;
-                }
-                else if( i_action == ACTIONID_PROGRAM_SID_NEXT ) {
-                    if( i == i_count - 1 )
-                        i = 0;
-                    else
-                        i++;
-                    }
-                else { /* ACTIONID_PROGRAM_SID_PREV */
-                    if( i == 0 )
-                        i = i_count - 1;
-                    else
-                        i--;
-                    }
-                var_Set( p_input, "program", list.p_list->p_values[i] );
-                DisplayMessage( p_vout, _("Program Service ID: %s"),
-                                list2.p_list->p_values[i].psz_string );
-                var_FreeList( &list, &list2 );
-            }
-            break;
-
-        case ACTIONID_JUMP_BACKWARD_EXTRASHORT:
-        case ACTIONID_JUMP_FORWARD_EXTRASHORT:
-        case ACTIONID_JUMP_BACKWARD_SHORT:
-        case ACTIONID_JUMP_FORWARD_SHORT:
-        case ACTIONID_JUMP_BACKWARD_MEDIUM:
-        case ACTIONID_JUMP_FORWARD_MEDIUM:
-        case ACTIONID_JUMP_BACKWARD_LONG:
-        case ACTIONID_JUMP_FORWARD_LONG:
-        {
-            if( p_input == NULL || !var_GetBool( p_input, "can-seek" ) )
-                break;
-
-            const char *varname;
-            int sign = +1;
-            switch( i_action )
-            {
-                case ACTIONID_JUMP_BACKWARD_EXTRASHORT:
-                    sign = -1;
-                    /* fall through */
-                case ACTIONID_JUMP_FORWARD_EXTRASHORT:
-                    varname = "extrashort-jump-size";
-                    break;
-                case ACTIONID_JUMP_BACKWARD_SHORT:
-                    sign = -1;
-                    /* fall through */
-                case ACTIONID_JUMP_FORWARD_SHORT:
-                    varname = "short-jump-size";
-                    break;
-                case ACTIONID_JUMP_BACKWARD_MEDIUM:
-                    sign = -1;
-                    /* fall through */
-                case ACTIONID_JUMP_FORWARD_MEDIUM:
-                    varname = "medium-jump-size";
-                    break;
-                case ACTIONID_JUMP_BACKWARD_LONG:
-                    sign = -1;
-                    /* fall through */
-                case ACTIONID_JUMP_FORWARD_LONG:
-                    varname = "long-jump-size";
-                    break;
-            }
-
-            mtime_t it = var_InheritInteger( p_input, varname );
-            if( it < 0 )
-                break;
-            var_SetInteger( p_input, "time-offset", it * sign * CLOCK_FREQ );
-            DisplayPosition( p_vout, slider_chan, p_input );
-            break;
-        }
-
-        /* Input navigation */
-        case ACTIONID_TITLE_PREV:
-            if( p_input )
-                var_TriggerCallback( p_input, "prev-title" );
-            break;
-        case ACTIONID_TITLE_NEXT:
-            if( p_input )
-                var_TriggerCallback( p_input, "next-title" );
-            break;
-        case ACTIONID_CHAPTER_PREV:
-            if( p_input )
-                var_TriggerCallback( p_input, "prev-chapter" );
-            break;
-        case ACTIONID_CHAPTER_NEXT:
-            if( p_input )
-                var_TriggerCallback( p_input, "next-chapter" );
-            break;
-        case ACTIONID_DISC_MENU:
-            if( p_input )
-                var_SetInteger( p_input, "title  0", 2 );
-            break;
-        case ACTIONID_NAV_ACTIVATE:
-            if( p_input )
-                input_Control( p_input, INPUT_NAV_ACTIVATE, NULL );
-            break;
-        case ACTIONID_NAV_UP:
-            if( p_vout )
-                input_UpdateViewpoint( p_input,
-                                       &(vlc_viewpoint_t) { .pitch = -1.f },
-                                       false );
-            if( p_input )
-                input_Control( p_input, INPUT_NAV_UP, NULL );
-            break;
-        case ACTIONID_NAV_DOWN:
-            if( p_vout )
-                input_UpdateViewpoint( p_input,
-                                       &(vlc_viewpoint_t) { .pitch = 1.f },
-                                       false );
-            if( p_input )
-                input_Control( p_input, INPUT_NAV_DOWN, NULL );
-            break;
-        case ACTIONID_NAV_LEFT:
-            if( p_vout )
-                input_UpdateViewpoint( p_input,
-                                       &(vlc_viewpoint_t) { .yaw = -1.f },
-                                       false );
-            if( p_input )
-                input_Control( p_input, INPUT_NAV_LEFT, NULL );
-            break;
-        case ACTIONID_NAV_RIGHT:
-            if( p_vout )
-                input_UpdateViewpoint( p_input,
-                                       &(vlc_viewpoint_t) { .yaw = 1.f },
-                                       false );
-            if( p_input )
-                input_Control( p_input, INPUT_NAV_RIGHT, NULL );
-            break;
-
-        /* Video Output actions */
-        case ACTIONID_SNAPSHOT:
-            if( p_vout )
-                var_TriggerCallback( p_vout, "video-snapshot" );
-            break;
-
-        case ACTIONID_TOGGLE_FULLSCREEN:
-        {
-            if( p_vout )
-            {
-                bool fs = var_ToggleBool( p_vout, "fullscreen" );
-                var_SetBool( p_playlist, "fullscreen", fs );
-            }
-            else
-                var_ToggleBool( p_playlist, "fullscreen" );
-            break;
-        }
-
-        case ACTIONID_LEAVE_FULLSCREEN:
-            if( p_vout )
-                var_SetBool( p_vout, "fullscreen", false );
-            var_SetBool( p_playlist, "fullscreen", false );
-            break;
-
-        case ACTIONID_ASPECT_RATIO:
-            if( p_vout )
-            {
-                vlc_value_t val={0}, val_list, text_list;
-                var_Get( p_vout, "aspect-ratio", &val );
-                if( var_Change( p_vout, "aspect-ratio", VLC_VAR_GETCHOICES,
-                                &val_list, &text_list ) >= 0 )
-                {
-                    int i;
-                    for( i = 0; i < val_list.p_list->i_count; i++ )
-                    {
-                        if( !strcmp( val_list.p_list->p_values[i].psz_string,
-                                     val.psz_string ) )
-                        {
-                            i++;
-                            break;
-                        }
-                    }
-                    if( i == val_list.p_list->i_count ) i = 0;
-                    var_SetString( p_vout, "aspect-ratio",
-                                   val_list.p_list->p_values[i].psz_string );
-                    DisplayMessage( p_vout, _("Aspect ratio: %s"),
-                                    text_list.p_list->p_values[i].psz_string );
-
-                    var_FreeList( &val_list, &text_list );
-                }
-                free( val.psz_string );
-            }
-            break;
-
-        case ACTIONID_CROP:
-            if( p_vout )
-            {
-                vlc_value_t val={0}, val_list, text_list;
-                var_Get( p_vout, "crop", &val );
-                if( var_Change( p_vout, "crop", VLC_VAR_GETCHOICES,
-                                &val_list, &text_list ) >= 0 )
-                {
-                    int i;
-                    for( i = 0; i < val_list.p_list->i_count; i++ )
-                    {
-                        if( !strcmp( val_list.p_list->p_values[i].psz_string,
-                                     val.psz_string ) )
-                        {
-                            i++;
-                            break;
-                        }
-                    }
-                    if( i == val_list.p_list->i_count ) i = 0;
-                    var_SetString( p_vout, "crop",
-                                   val_list.p_list->p_values[i].psz_string );
-                    DisplayMessage( p_vout, _("Crop: %s"),
-                                    text_list.p_list->p_values[i].psz_string );
-
-                    var_FreeList( &val_list, &text_list );
-                }
-                free( val.psz_string );
-            }
-            break;
-        case ACTIONID_CROP_TOP:
-            if( p_vout )
-                var_IncInteger( p_vout, "crop-top" );
-            break;
-        case ACTIONID_UNCROP_TOP:
-            if( p_vout )
-                var_DecInteger( p_vout, "crop-top" );
-            break;
-        case ACTIONID_CROP_BOTTOM:
-            if( p_vout )
-                var_IncInteger( p_vout, "crop-bottom" );
-            break;
-        case ACTIONID_UNCROP_BOTTOM:
-            if( p_vout )
-                var_DecInteger( p_vout, "crop-bottom" );
-            break;
-        case ACTIONID_CROP_LEFT:
-            if( p_vout )
-                var_IncInteger( p_vout, "crop-left" );
-            break;
-        case ACTIONID_UNCROP_LEFT:
-            if( p_vout )
-                var_DecInteger( p_vout, "crop-left" );
-            break;
-        case ACTIONID_CROP_RIGHT:
-            if( p_vout )
-                var_IncInteger( p_vout, "crop-right" );
-            break;
-        case ACTIONID_UNCROP_RIGHT:
-            if( p_vout )
-                var_DecInteger( p_vout, "crop-right" );
-            break;
-
-        case ACTIONID_VIEWPOINT_FOV_IN:
-            if( p_vout )
-                input_UpdateViewpoint( p_input,
-                                       &(vlc_viewpoint_t) { .fov = -1.f },
-                                       false );
-            break;
-        case ACTIONID_VIEWPOINT_FOV_OUT:
-            if( p_vout )
-                input_UpdateViewpoint( p_input,
-                                       &(vlc_viewpoint_t) { .fov = 1.f },
-                                       false );
-            break;
-
-        case ACTIONID_VIEWPOINT_ROLL_CLOCK:
-            if( p_vout )
-                input_UpdateViewpoint( p_input,
-                                       &(vlc_viewpoint_t) { .roll = -1.f },
-                                       false );
-            break;
-        case ACTIONID_VIEWPOINT_ROLL_ANTICLOCK:
-            if( p_vout )
-                input_UpdateViewpoint( p_input,
-                                       &(vlc_viewpoint_t) { .roll = 1.f },
-                                       false );
-            break;
-
-         case ACTIONID_TOGGLE_AUTOSCALE:
-            if( p_vout )
-            {
-                float f_scalefactor = var_GetFloat( p_vout, "zoom" );
-                if ( f_scalefactor != 1.f )
-                {
-                    var_SetFloat( p_vout, "zoom", 1.f );
-                    DisplayMessage( p_vout, _("Zooming reset") );
-                }
-                else
-                {
-                    bool b_autoscale = !var_GetBool( p_vout, "autoscale" );
-                    var_SetBool( p_vout, "autoscale", b_autoscale );
-                    if( b_autoscale )
-                        DisplayMessage( p_vout, _("Scaled to screen") );
-                    else
-                        DisplayMessage( p_vout, _("Original Size") );
-                }
-            }
-            break;
-        case ACTIONID_SCALE_UP:
-            if( p_vout )
-            {
-               float f_scalefactor = var_GetFloat( p_vout, "zoom" );
-
-               if( f_scalefactor < 10.f )
-                   f_scalefactor += .1f;
-               var_SetFloat( p_vout, "zoom", f_scalefactor );
-            }
-            break;
-        case ACTIONID_SCALE_DOWN:
-            if( p_vout )
-            {
-               float f_scalefactor = var_GetFloat( p_vout, "zoom" );
-
-               if( f_scalefactor > .3f )
-                   f_scalefactor -= .1f;
-               var_SetFloat( p_vout, "zoom", f_scalefactor );
-            }
-            break;
-
-        case ACTIONID_ZOOM_QUARTER:
-        case ACTIONID_ZOOM_HALF:
-        case ACTIONID_ZOOM_ORIGINAL:
-        case ACTIONID_ZOOM_DOUBLE:
-            if( p_vout )
-            {
-                float f;
-                switch( i_action )
-                {
-                    case ACTIONID_ZOOM_QUARTER:  f = 0.25; break;
-                    case ACTIONID_ZOOM_HALF:     f = 0.5;  break;
-                    case ACTIONID_ZOOM_ORIGINAL: f = 1.;   break;
-                     /*case ACTIONID_ZOOM_DOUBLE:*/
-                    default:                     f = 2.;   break;
-                }
-                var_SetFloat( p_vout, "zoom", f );
-            }
-            break;
-        case ACTIONID_ZOOM:
-        case ACTIONID_UNZOOM:
-            if( p_vout )
-            {
-                vlc_value_t val={0}, val_list, text_list;
-                var_Get( p_vout, "zoom", &val );
-                if( var_Change( p_vout, "zoom", VLC_VAR_GETCHOICES,
-                                &val_list, &text_list ) >= 0 )
-                {
-                    int i;
-                    for( i = 0; i < val_list.p_list->i_count; i++ )
-                    {
-                        if( val_list.p_list->p_values[i].f_float
-                           == val.f_float )
-                        {
-                            if( i_action == ACTIONID_ZOOM )
-                                i++;
-                            else /* ACTIONID_UNZOOM */
-                                i--;
-                            break;
-                        }
-                    }
-                    if( i == val_list.p_list->i_count ) i = 0;
-                    if( i == -1 ) i = val_list.p_list->i_count-1;
-                    var_SetFloat( p_vout, "zoom",
-                                  val_list.p_list->p_values[i].f_float );
-                    DisplayMessage( p_vout, _("Zoom mode: %s"),
-                                    text_list.p_list->p_values[i].psz_string );
-
-                    var_FreeList( &val_list, &text_list );
-                }
-            }
-            break;
-
-        case ACTIONID_DEINTERLACE:
-            if( p_vout )
-            {
-                int i_deinterlace = var_GetInteger( p_vout, "deinterlace" );
-                if( i_deinterlace != 0 )
-                {
-                    var_SetInteger( p_vout, "deinterlace", 0 );
-                    DisplayMessage( p_vout, _("Deinterlace off") );
-                }
-                else
-                {
-                    var_SetInteger( p_vout, "deinterlace", 1 );
-
-                    char *psz_mode = var_GetString( p_vout, "deinterlace-mode" );
-                    vlc_value_t vlist, tlist;
-                    if( psz_mode && !var_Change( p_vout, "deinterlace-mode", VLC_VAR_GETCHOICES, &vlist, &tlist ) )
-                    {
-                        const char *psz_text = NULL;
-                        for( int i = 0; i < vlist.p_list->i_count; i++ )
-                        {
-                            if( !strcmp( vlist.p_list->p_values[i].psz_string, psz_mode ) )
-                            {
-                                psz_text = tlist.p_list->p_values[i].psz_string;
-                                break;
-                            }
-                        }
-                        DisplayMessage( p_vout, "%s (%s)", _("Deinterlace on"),
-                                        psz_text ? psz_text : psz_mode );
-
-                        var_FreeList( &vlist, &tlist );
-                    }
-                    free( psz_mode );
-                }
-            }
-            break;
-        case ACTIONID_DEINTERLACE_MODE:
-            if( p_vout )
-            {
-                char *psz_mode = var_GetString( p_vout, "deinterlace-mode" );
-                vlc_value_t vlist, tlist;
-                if( psz_mode && !var_Change( p_vout, "deinterlace-mode", VLC_VAR_GETCHOICES, &vlist, &tlist ))
-                {
-                    const char *psz_text = NULL;
-                    int i;
-                    for( i = 0; i < vlist.p_list->i_count; i++ )
-                    {
-                        if( !strcmp( vlist.p_list->p_values[i].psz_string, psz_mode ) )
-                        {
-                            i++;
-                            break;
-                        }
-                    }
-                    if( i == vlist.p_list->i_count ) i = 0;
-                    psz_text = tlist.p_list->p_values[i].psz_string;
-                    var_SetString( p_vout, "deinterlace-mode", vlist.p_list->p_values[i].psz_string );
-
-                    int i_deinterlace = var_GetInteger( p_vout, "deinterlace" );
-                    if( i_deinterlace != 0 )
-                    {
-                      DisplayMessage( p_vout, "%s (%s)", _("Deinterlace on"),
-                                      psz_text ? psz_text : psz_mode );
-                    }
-                    else
-                    {
-                      DisplayMessage( p_vout, "%s (%s)", _("Deinterlace off"),
-                                      psz_text ? psz_text : psz_mode );
-                    }
-
-                    var_FreeList( &vlist, &tlist );
-                }
-                free( psz_mode );
-            }
-            break;
-
-        case ACTIONID_SUBPOS_DOWN:
-        case ACTIONID_SUBPOS_UP:
-        {
-            if( p_input )
-            {
-                vlc_value_t val, list, list2;
-                int i_count;
-                var_Get( p_input, "spu-es", &val );
-
-                var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
-                            &list, &list2 );
-                i_count = list.p_list->i_count;
-                if( i_count < 1 || val.i_int < 0 )
-                {
-                    DisplayMessage( p_vout,
-                                    _("Subtitle position: no active subtitle") );
-                    var_FreeList( &list, &list2 );
-                    break;
-                }
-
-                int i_pos;
-                if( i_action == ACTIONID_SUBPOS_DOWN )
-                    i_pos = var_DecInteger( p_vout, "sub-margin" );
-                else
-                    i_pos = var_IncInteger( p_vout, "sub-margin" );
-
-                ClearChannels( p_vout, slider_chan );
-                DisplayMessage( p_vout, _( "Subtitle position %d px" ), i_pos );
-                var_FreeList( &list, &list2 );
-            }
-            break;
-        }
-
-        case ACTIONID_SUBTITLE_TEXT_SCALE_DOWN:
-        case ACTIONID_SUBTITLE_TEXT_SCALE_UP:
-        case ACTIONID_SUBTITLE_TEXT_SCALE_NORMAL:
-            if( p_vout )
-            {
-                int i_scale;
-                if( i_action == ACTIONID_SUBTITLE_TEXT_SCALE_NORMAL )
-                {
-                    i_scale = 100;
-                }
-                else
-                {
-                    i_scale = var_GetInteger( p_playlist, "sub-text-scale" );
-                    i_scale += ((i_action == ACTIONID_SUBTITLE_TEXT_SCALE_UP) ? 1 : -1) * 25;
-                    i_scale = VLC_CLIP( i_scale, 10, 500 );
-                }
-                var_SetInteger( p_playlist, "sub-text-scale", i_scale );
-                DisplayMessage( p_vout, _( "Subtitle text scale %d%%" ), i_scale );
-            }
-            break;
-
-        /* Input + video output */
-        case ACTIONID_POSITION:
-            if( p_vout && vout_OSDEpg( p_vout, input_GetItem( p_input ) ) )
-                DisplayPosition( p_vout, slider_chan, p_input );
-            break;
-
-        case ACTIONID_COMBO_VOL_FOV_UP:
-            if( b_vrnav )
-                DO_ACTION( ACTIONID_VIEWPOINT_FOV_IN );
-            else
-                DO_ACTION( ACTIONID_VOL_UP );
-            break;
-        case ACTIONID_COMBO_VOL_FOV_DOWN:
-            if( b_vrnav )
-                DO_ACTION( ACTIONID_VIEWPOINT_FOV_OUT );
-            else
-                DO_ACTION( ACTIONID_VOL_DOWN );
-            break;
-    }
-
-    return VLC_SUCCESS;
-}
-
 /*****************************************************************************
  * ActionEvent: callback for hotkey actions
  *****************************************************************************/
 static int ActionEvent( vlc_object_t *libvlc, char const *psz_var,
                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
 {
-    intf_thread_t *p_intf = (intf_thread_t *)p_data;
-    intf_sys_t *p_sys = p_intf->p_sys;
-
     (void)libvlc;
     (void)psz_var;
     (void)oldval;
 
-    vlc_mutex_lock( &p_intf->p_sys->lock );
-    input_thread_t *p_input = p_sys->p_input ? vlc_object_hold( p_sys->p_input )
-                                             : NULL;
-    vout_thread_t *p_vout = p_sys->p_vout ? vlc_object_hold( p_sys->p_vout )
-                                          : NULL;
-    int slider_chan = p_sys->slider_chan;
-    bool b_vrnav = p_sys->vrnav.b_can_change;
-    vlc_mutex_unlock( &p_intf->p_sys->lock );
-
-    int i_ret = PutAction( p_intf, p_input, p_vout, slider_chan, b_vrnav,
-                           newval.i_int );
-
-    if( p_input != NULL )
-        vlc_object_release( p_input );
-    if( p_vout != NULL )
-        vlc_object_release( p_vout );
-
-    return i_ret;
-}
-
-static void PlayBookmark( intf_thread_t *p_intf, int i_num )
-{
-    char *psz_bookmark_name;
-    if( asprintf( &psz_bookmark_name, "bookmark%i", i_num ) == -1 )
-        return;
-
-    playlist_t *p_playlist = pl_Get( p_intf );
-    char *psz_bookmark = var_CreateGetString( p_intf, psz_bookmark_name );
-
-    PL_LOCK;
-    FOREACH_ARRAY( playlist_item_t *p_item, p_playlist->items )
-        char *psz_uri = input_item_GetURI( p_item->p_input );
-        if( !strcmp( psz_bookmark, psz_uri ) )
-        {
-            free( psz_uri );
-            playlist_ViewPlay( p_playlist, NULL, p_item );
-            break;
-        }
-        else
-            free( psz_uri );
-    FOREACH_END();
-    PL_UNLOCK;
-
-    free( psz_bookmark );
-    free( psz_bookmark_name );
-}
-
-static void SetBookmark( intf_thread_t *p_intf, int i_num )
-{
-    char *psz_bookmark_name;
-    char *psz_uri = NULL;
-    if( asprintf( &psz_bookmark_name, "bookmark%i", i_num ) == -1 )
-        return;
-
-    playlist_t *p_playlist = pl_Get( p_intf );
-    var_Create( p_intf, psz_bookmark_name,
-                VLC_VAR_STRING|VLC_VAR_DOINHERIT );
-
-    PL_LOCK;
-    playlist_item_t * p_item = playlist_CurrentPlayingItem( p_playlist );
-    if( p_item ) psz_uri = input_item_GetURI( p_item->p_input );
-    PL_UNLOCK;
-
-    if( p_item )
-    {
-        config_PutPsz( p_intf, psz_bookmark_name, psz_uri);
-        msg_Info( p_intf, "setting playlist bookmark %i to %s", i_num, psz_uri);
-    }
-
-    free( psz_uri );
-    free( psz_bookmark_name );
-}
-
-static void DisplayPosition( vout_thread_t *p_vout, int slider_chan,
-                             input_thread_t *p_input )
-{
-    char psz_duration[MSTRTIME_MAX_SIZE];
-    char psz_time[MSTRTIME_MAX_SIZE];
-
-    if( p_vout == NULL ) return;
-
-    ClearChannels( p_vout, slider_chan );
-
-    int64_t t = var_GetInteger( p_input, "time" ) / CLOCK_FREQ;
-    int64_t l = var_GetInteger( p_input, "length" ) / CLOCK_FREQ;
-
-    secstotimestr( psz_time, t );
-
-    if( l > 0 )
-    {
-        secstotimestr( psz_duration, l );
-        DisplayMessage( p_vout, "%s / %s", psz_time, psz_duration );
-    }
-    else if( t > 0 )
-    {
-        DisplayMessage( p_vout, "%s", psz_time );
-    }
-
-    if( var_GetBool( p_vout, "fullscreen" ) )
-    {
-        vlc_value_t pos;
-        var_Get( p_input, "position", &pos );
-        vout_OSDSlider( p_vout, slider_chan,
-                        pos.f_float * 100, OSD_HOR_SLIDER );
-    }
-}
-
-static void DisplayVolume( vout_thread_t *p_vout, int slider_chan, float vol )
-{
-    if( p_vout == NULL )
-        return;
-    ClearChannels( p_vout, slider_chan );
-
-    if( var_GetBool( p_vout, "fullscreen" ) )
-        vout_OSDSlider( p_vout, slider_chan,
-                        lroundf(vol * 100.f), OSD_VERT_SLIDER );
-    DisplayMessage( p_vout, _( "Volume %ld%%" ), lroundf(vol * 100.f) );
-}
-
-static void DisplayRate( vout_thread_t *p_vout, float f_rate )
-{
-    DisplayMessage( p_vout, _("Speed: %.2fx"), (double) f_rate );
-}
-
-static float AdjustRateFine( vlc_object_t *p_obj, const int i_dir )
-{
-    const float f_rate_min = (float)INPUT_RATE_DEFAULT / INPUT_RATE_MAX;
-    const float f_rate_max = (float)INPUT_RATE_DEFAULT / INPUT_RATE_MIN;
-    float f_rate = var_GetFloat( p_obj, "rate" );
-
-    int i_sign = f_rate < 0 ? -1 : 1;
-
-    f_rate = floor( fabs(f_rate) / 0.1 + i_dir + 0.05 ) * 0.1;
-
-    if( f_rate < f_rate_min )
-        f_rate = f_rate_min;
-    else if( f_rate > f_rate_max )
-        f_rate = f_rate_max;
-    f_rate *= i_sign;
-
-    return f_rate;
-}
-
-static void ClearChannels( vout_thread_t *p_vout, int slider_chan )
-{
-    if( p_vout )
-    {
-        vout_FlushSubpictureChannel( p_vout, VOUT_SPU_CHANNEL_OSD );
-        vout_FlushSubpictureChannel( p_vout, slider_chan );
-    }
+    vlc_object_t *p_this = p_data;
+    return vlc_actions_do( p_this, newval.i_int, true, 0 );
 }
diff --git a/src/libvlccore.sym b/src/libvlccore.sym
index f9a82d7a51..2273b7dad1 100644
--- a/src/libvlccore.sym
+++ b/src/libvlccore.sym
@@ -513,6 +513,7 @@ video_format_Print
 vlc_actions_get_id
 vlc_actions_get_key_names
 vlc_actions_get_keycodes
+vlc_actions_do
 vlc_b64_decode
 vlc_b64_decode_binary
 vlc_b64_decode_binary_to_buffer
diff --git a/src/misc/actions.c b/src/misc/actions.c
index 74585f6c70..50b10382f5 100644
--- a/src/misc/actions.c
+++ b/src/misc/actions.c
@@ -4,6 +4,7 @@
  * Copyright (C) 2003-2016 VLC authors and VideoLAN
  *
  * Authors: Sigmund Augdal Helberg <dnumgis at videolan.org>
+ *          Jean-Paul Saman <jpsaman #_at_# m2x.nl>
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
@@ -36,10 +37,17 @@
 # include <search.h>
 #endif
 #include <errno.h>
+#include <math.h>
 
 #include <vlc_common.h>
 #include <vlc_actions.h>
 #include <vlc_charset.h>
+#include <vlc_interface.h>
+#include <vlc_input.h>
+#include <vlc_playlist.h>
+#include <vlc_aout.h>
+#include <vlc_vout.h>
+#include <vlc_vout_osd.h>
 #include "config/configuration.h"
 #include "libvlc.h"
 
@@ -393,6 +401,16 @@ static int keycmp (const void *a, const void *b)
 
 struct vlc_actions_t
 {
+    /*subtitle_delaybookmarks: placeholder for storing subtitle sync timestamps*/
+    struct
+    {
+        int64_t i_time_subtitle;
+        int64_t i_time_audio;
+    } subtitle_delaybookmarks;
+
+    vout_thread_t *p_last_vout;
+    int slider_chan;
+
     void *map; /* Key map */
     void *global_map; /* Grabbed/global key map */
     const char *ppsz_keys[];
@@ -509,6 +527,9 @@ int libvlc_InternalActionsInit (libvlc_int_t *libvlc)
         return VLC_ENOMEM;
     as->map = NULL;
     as->global_map = NULL;
+    as->p_last_vout = NULL;
+    as->subtitle_delaybookmarks.i_time_audio = 0;
+    as->subtitle_delaybookmarks.i_time_subtitle = 0;
 
     var_Create (obj, "key-pressed", VLC_VAR_INTEGER);
     var_Create (obj, "global-key-pressed", VLC_VAR_INTEGER);
@@ -640,3 +661,1575 @@ vlc_actions_get_key_names(vlc_object_t *p_obj)
     vlc_actions_t *as = libvlc_priv(p_obj->obj.libvlc)->actions;
     return as->ppsz_keys;
 }
+
+#define display_message(vout, ...) \
+    do { \
+        if (vout) \
+            vout_OSDMessage(vout, VOUT_SPU_CHANNEL_OSD, __VA_ARGS__); \
+    } while(0)
+#define display_icon(vout, icon) \
+    do { if(vout) vout_OSDIcon(vout, VOUT_SPU_CHANNEL_OSD, icon); } while(0)
+
+static void do_play_bookmark( playlist_t *p_playlist, int i_num )
+{
+    char *psz_bookmark_name;
+    if( asprintf( &psz_bookmark_name, "bookmark%i", i_num ) == -1 )
+        return;
+
+    char *psz_bookmark = var_CreateGetString( p_playlist->obj.libvlc,
+                                              psz_bookmark_name );
+
+    PL_LOCK;
+    FOREACH_ARRAY( playlist_item_t *p_item, p_playlist->items )
+        char *psz_uri = input_item_GetURI( p_item->p_input );
+        if( !strcmp( psz_bookmark, psz_uri ) )
+        {
+            free( psz_uri );
+            playlist_ViewPlay( p_playlist, NULL, p_item );
+            break;
+        }
+        else
+            free( psz_uri );
+    FOREACH_END();
+    PL_UNLOCK;
+
+    free( psz_bookmark );
+    free( psz_bookmark_name );
+}
+
+static void do_set_bookmark( playlist_t *p_playlist, int i_num )
+{
+    char *psz_bookmark_name;
+    char *psz_uri = NULL;
+    if( asprintf( &psz_bookmark_name, "bookmark%i", i_num ) == -1 )
+        return;
+
+    var_Create( p_playlist->obj.libvlc, psz_bookmark_name,
+                VLC_VAR_STRING|VLC_VAR_DOINHERIT );
+
+    PL_LOCK;
+    playlist_item_t * p_item = playlist_CurrentPlayingItem( p_playlist );
+    if( p_item ) psz_uri = input_item_GetURI( p_item->p_input );
+    PL_UNLOCK;
+
+    if( p_item )
+    {
+        config_PutPsz( p_playlist->obj.libvlc, psz_bookmark_name, psz_uri);
+        msg_Info( p_playlist, "setting playlist bookmark %i to %s", i_num, psz_uri);
+    }
+
+    free( psz_uri );
+    free( psz_bookmark_name );
+}
+
+static void clear_channels( vlc_actions_t *p_as, vout_thread_t *p_vout  )
+{
+    if( p_vout )
+    {
+        vout_FlushSubpictureChannel( p_vout, VOUT_SPU_CHANNEL_OSD );
+        vout_FlushSubpictureChannel( p_vout, p_as->slider_chan );
+    }
+}
+
+static void display_position( vlc_actions_t *p_as, vout_thread_t *p_vout,
+                              input_thread_t *p_input )
+{
+    char psz_duration[MSTRTIME_MAX_SIZE];
+    char psz_time[MSTRTIME_MAX_SIZE];
+
+    if( p_vout == NULL ) return;
+
+    clear_channels( p_as, p_vout );
+
+    int64_t t = var_GetInteger( p_input, "time" ) / CLOCK_FREQ;
+    int64_t l = var_GetInteger( p_input, "length" ) / CLOCK_FREQ;
+
+    secstotimestr( psz_time, t );
+
+    if( l > 0 )
+    {
+        secstotimestr( psz_duration, l );
+        display_message( p_vout, "%s / %s", psz_time, psz_duration );
+    }
+    else if( t > 0 )
+    {
+        display_message( p_vout, "%s", psz_time );
+    }
+
+    if( var_GetBool( p_vout, "fullscreen" ) )
+    {
+        vlc_value_t pos;
+        var_Get( p_input, "position", &pos );
+        vout_OSDSlider( p_vout, p_as->slider_chan,
+                        pos.f_float * 100, OSD_HOR_SLIDER );
+    }
+}
+
+static void display_volume( vlc_actions_t *p_as, vout_thread_t *p_vout, float vol )
+{
+    if( p_vout == NULL )
+        return;
+    clear_channels( p_as, p_vout );
+
+    if( var_GetBool( p_vout, "fullscreen" ) )
+        vout_OSDSlider( p_vout, p_as->slider_chan,
+                        lroundf(vol * 100.f), OSD_VERT_SLIDER );
+    display_message( p_vout, _( "Volume %ld%%" ), lroundf(vol * 100.f) );
+}
+
+static void display_rate( vout_thread_t *p_vout, float f_rate )
+{
+    display_message( p_vout, _("Speed: %.2fx"), (double) f_rate );
+}
+
+static float adjust_rate_fine( playlist_t *p_playlist, const int i_dir )
+{
+    const float f_rate_min = (float)INPUT_RATE_DEFAULT / INPUT_RATE_MAX;
+    const float f_rate_max = (float)INPUT_RATE_DEFAULT / INPUT_RATE_MIN;
+    float f_rate = var_GetFloat( p_playlist, "rate" );
+
+    int i_sign = f_rate < 0 ? -1 : 1;
+
+    f_rate = floor( fabs(f_rate) / 0.1 + i_dir + 0.05 ) * 0.1;
+
+    if( f_rate < f_rate_min )
+        f_rate = f_rate_min;
+    else if( f_rate > f_rate_max )
+        f_rate = f_rate_max;
+    f_rate *= i_sign;
+
+    return f_rate;
+}
+
+static void register_osd_channels( vlc_actions_t *p_as, vout_thread_t *p_vout )
+{
+    /* Register OSD channels */
+    /* FIXME: this check can fail if the new vout is reallocated at the same
+     * address as the old one... We should rather listen to vout events.
+     * Alternatively, we should keep a reference to the vout thread. */
+    if( p_vout && p_vout != p_as->p_last_vout )
+        p_as->slider_chan = vout_RegisterSubpictureChannel( p_vout );
+    p_as->p_last_vout = p_vout;
+}
+
+static void
+handle_playlist_actions( playlist_t *p_playlist, vlc_action_id_t i_action,
+                         bool b_notify, vlc_actions_t *p_as )
+{
+    vout_thread_t *p_notify_vout = NULL;
+    if( b_notify == true )
+    {
+        input_thread_t *p_input = playlist_CurrentInput( p_playlist );
+        if( p_input != NULL )
+        {
+            p_notify_vout = input_GetVout( p_input );
+            vlc_object_release( p_input );
+        }
+    }
+    register_osd_channels( p_as, p_notify_vout );
+
+    switch( i_action )
+    {
+        case ACTIONID_PLAY_BOOKMARK1:
+        case ACTIONID_PLAY_BOOKMARK2:
+        case ACTIONID_PLAY_BOOKMARK3:
+        case ACTIONID_PLAY_BOOKMARK4:
+        case ACTIONID_PLAY_BOOKMARK5:
+        case ACTIONID_PLAY_BOOKMARK6:
+        case ACTIONID_PLAY_BOOKMARK7:
+        case ACTIONID_PLAY_BOOKMARK8:
+        case ACTIONID_PLAY_BOOKMARK9:
+        case ACTIONID_PLAY_BOOKMARK10:
+            do_play_bookmark( p_playlist, i_action - ACTIONID_PLAY_BOOKMARK1 + 1 );
+            break;
+
+        case ACTIONID_SET_BOOKMARK1:
+        case ACTIONID_SET_BOOKMARK2:
+        case ACTIONID_SET_BOOKMARK3:
+        case ACTIONID_SET_BOOKMARK4:
+        case ACTIONID_SET_BOOKMARK5:
+        case ACTIONID_SET_BOOKMARK6:
+        case ACTIONID_SET_BOOKMARK7:
+        case ACTIONID_SET_BOOKMARK8:
+        case ACTIONID_SET_BOOKMARK9:
+        case ACTIONID_SET_BOOKMARK10:
+            do_set_bookmark( p_playlist, i_action - ACTIONID_SET_BOOKMARK1 + 1 );
+            break;
+        case ACTIONID_LOOP:
+        {
+            /* Toggle Normal -> Loop -> Repeat -> Normal ... */
+            const char *mode;
+            if( var_GetBool( p_playlist, "repeat" ) )
+            {
+                var_SetBool( p_playlist, "repeat", false );
+                mode = N_("Off");
+            }
+            else
+            if( var_GetBool( p_playlist, "loop" ) )
+            { /* FIXME: this is not atomic, we should use a real tristate */
+                var_SetBool( p_playlist, "loop", false );
+                var_SetBool( p_playlist, "repeat", true );
+                mode = N_("One");
+            }
+            else
+            {
+                var_SetBool( p_playlist, "loop", true );
+                mode = N_("All");
+            }
+            display_message( p_notify_vout, _("Loop: %s"), vlc_gettext(mode) );
+            break;
+        }
+        case ACTIONID_RANDOM:
+        {
+            const bool state = var_ToggleBool( p_playlist, "random" );
+            display_message( p_notify_vout, _("Random: %s"),
+                            vlc_gettext( state ? N_("On") : N_("Off") ) );
+            break;
+        }
+        case ACTIONID_NEXT:
+            display_message( p_notify_vout, _("Next") );
+            playlist_Next( p_playlist );
+            break;
+        case ACTIONID_PREV:
+            display_message( p_notify_vout, _("Previous") );
+            playlist_Prev( p_playlist );
+            break;
+
+        case ACTIONID_STOP:
+            playlist_Stop( p_playlist );
+            break;
+
+        case ACTIONID_RATE_NORMAL:
+            var_SetFloat( p_playlist, "rate", 1.f );
+            display_rate( p_notify_vout, 1.f );
+            break;
+        case ACTIONID_FASTER:
+            var_TriggerCallback( p_playlist, "rate-faster" );
+            display_rate( p_notify_vout, var_GetFloat( p_playlist, "rate" ) );
+            break;
+        case ACTIONID_SLOWER:
+            var_TriggerCallback( p_playlist, "rate-slower" );
+            display_rate( p_notify_vout, var_GetFloat( p_playlist, "rate" ) );
+            break;
+        case ACTIONID_RATE_FASTER_FINE:
+        case ACTIONID_RATE_SLOWER_FINE:
+        {
+            const int i_dir = i_action == ACTIONID_RATE_FASTER_FINE ? 1 : -1;
+            float rate = adjust_rate_fine( p_playlist, i_dir );
+
+            var_SetFloat( p_playlist, "rate", rate );
+            display_rate( p_notify_vout, rate );
+            break;
+        }
+        case ACTIONID_PLAY_CLEAR:
+        {
+            playlist_Clear( p_playlist, pl_Unlocked );
+            break;
+        }
+        case ACTIONID_VOL_UP:
+        {
+            float vol;
+            if( playlist_VolumeUp( p_playlist, 1, &vol ) == 0 )
+                display_volume( p_as, p_notify_vout, vol );
+            break;
+        }
+        case ACTIONID_VOL_DOWN:
+        {
+            float vol;
+            if( playlist_VolumeDown( p_playlist, 1, &vol ) == 0 )
+                display_volume( p_as, p_notify_vout, vol );
+            break;
+        }
+        case ACTIONID_VOL_MUTE:
+        {
+            int mute = playlist_MuteGet( p_playlist );
+            if( mute < 0 )
+                break;
+            mute = !mute;
+            if( playlist_MuteSet( p_playlist, mute ) )
+                break;
+
+            float vol = playlist_VolumeGet( p_playlist );
+            if( mute || vol == 0.f )
+            {
+                clear_channels( p_as, p_notify_vout );
+                display_icon( p_notify_vout, OSD_MUTE_ICON );
+            }
+            else
+                display_volume( p_as, p_notify_vout,  vol );
+            break;
+        }
+        default:
+            vlc_assert_unreachable();
+    }
+    if( p_notify_vout )
+        vlc_object_release( p_notify_vout );
+}
+
+static void handle_input_actions( playlist_t *p_playlist, vlc_action_id_t i_action,
+                                  bool b_notify, vlc_actions_t *p_as,
+                                  unsigned int i_args, va_list args )
+{
+    input_thread_t *p_input = NULL;
+    bool b_release_input = false;
+    assert( i_args <= 1 );
+    if( i_args > 0 )
+        p_input = va_arg( args, input_thread_t* );
+    if( p_input == NULL )
+    {
+        p_input = playlist_CurrentInput( p_playlist );
+        b_release_input = p_input != NULL;
+    }
+    vout_thread_t *p_notify_vout  = NULL;
+    if( b_notify && p_input )
+        p_notify_vout = input_GetVout( p_input );
+    register_osd_channels( p_as, p_notify_vout );
+
+    switch( i_action )
+    {
+        case ACTIONID_PLAY_PAUSE:
+            if( p_input )
+            {
+                clear_channels( p_as, p_notify_vout );
+
+                int state = var_GetInteger( p_input, "state" );
+                display_icon( p_notify_vout, state != PAUSE_S ? OSD_PAUSE_ICON : OSD_PLAY_ICON );
+            }
+            playlist_TogglePause( p_playlist );
+            break;
+
+        case ACTIONID_PLAY:
+            if( p_input && var_GetFloat( p_input, "rate" ) != 1.f )
+                /* Return to normal speed */
+                var_SetFloat( p_input, "rate", 1.f );
+            else
+            {
+                clear_channels( p_as, p_notify_vout );
+                display_icon( p_notify_vout, OSD_PLAY_ICON );
+                playlist_Play( p_playlist );
+            }
+            break;
+        case ACTIONID_PAUSE:
+            if( p_input && var_GetInteger( p_input, "state" ) != PAUSE_S )
+            {
+                clear_channels( p_as, p_notify_vout );
+                display_icon( p_notify_vout, OSD_PAUSE_ICON );
+                var_SetInteger( p_input, "state", PAUSE_S );
+            }
+            break;
+
+        case ACTIONID_RECORD:
+            if( p_input && var_GetBool( p_input, "can-record" ) )
+            {
+                const bool on = var_ToggleBool( p_input, "record" );
+                display_message( p_notify_vout, vlc_gettext(on
+                                   ? N_("Recording") : N_("Recording done")) );
+            }
+            break;
+
+        case ACTIONID_FRAME_NEXT:
+            if( p_input )
+            {
+                var_TriggerCallback( p_input, "frame-next" );
+                display_message( p_notify_vout, _("Next frame") );
+            }
+            break;
+
+        case ACTIONID_SUBSYNC_MARKAUDIO:
+        {
+            p_as->subtitle_delaybookmarks.i_time_audio = mdate();
+            display_message( p_notify_vout, _("Sub sync: bookmarked audio time"));
+            break;
+        }
+        case ACTIONID_SUBSYNC_MARKSUB:
+            if( p_input )
+            {
+                vlc_value_t val, list, list2;
+                int i_count;
+                var_Get( p_input, "spu-es", &val );
+
+                var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
+                            &list, &list2 );
+                i_count = list.p_list->i_count;
+                if( i_count < 1 || val.i_int < 0 )
+                {
+                    display_message( p_notify_vout, _("No active subtitle") );
+                    var_FreeList( &list, &list2 );
+                    break;
+                }
+                p_as->subtitle_delaybookmarks.i_time_subtitle = mdate();
+                display_message( p_notify_vout,
+                                _("Sub sync: bookmarked subtitle time"));
+                var_FreeList( &list, &list2 );
+            }
+            break;
+        case ACTIONID_SUBSYNC_APPLY:
+        {
+            /* Warning! Can yield a pause in the playback.
+             * For example, the following succession of actions will yield a 5 second delay :
+             * - Pressing Shift-H (ACTIONID_SUBSYNC_MARKAUDIO)
+             * - wait 5 second
+             * - Press Shift-J (ACTIONID_SUBSYNC_MARKSUB)
+             * - Press Shift-K (ACTIONID_SUBSYNC_APPLY)
+             * --> 5 seconds pause
+             * This is due to var_SetTime() (and ultimately UpdatePtsDelay())
+             * which causes the video to pause for an equivalent duration
+             * (This problem is also present in the "Track synchronization" window) */
+            if ( p_input )
+            {
+                if ( (p_as->subtitle_delaybookmarks.i_time_audio == 0)
+                  || (p_as->subtitle_delaybookmarks.i_time_subtitle == 0) )
+                {
+                    display_message( p_notify_vout, _( "Sub sync: set bookmarks first!" ) );
+                }
+                else
+                {
+                    int64_t i_current_subdelay = var_GetInteger( p_input, "spu-delay" );
+                    int64_t i_additional_subdelay =
+                        p_as->subtitle_delaybookmarks.i_time_audio
+                        - p_as->subtitle_delaybookmarks.i_time_subtitle;
+                    int64_t i_total_subdelay = i_current_subdelay + i_additional_subdelay;
+                    var_SetInteger( p_input, "spu-delay", i_total_subdelay);
+                    clear_channels( p_as, p_notify_vout );
+                    display_message( p_notify_vout, _( "Sub sync: corrected %i ms (total delay = %i ms)" ),
+                                            (int)(i_additional_subdelay / 1000),
+                                            (int)(i_total_subdelay / 1000) );
+                    p_as->subtitle_delaybookmarks.i_time_audio = 0;
+                    p_as->subtitle_delaybookmarks.i_time_subtitle = 0;
+                }
+            }
+        }
+        case ACTIONID_SUBSYNC_RESET:
+        {
+            var_SetInteger( p_input, "spu-delay", 0);
+            clear_channels( p_as, p_notify_vout );
+            display_message( p_notify_vout, _( "Sub sync: delay reset" ) );
+            p_as->subtitle_delaybookmarks.i_time_audio = 0;
+            p_as->subtitle_delaybookmarks.i_time_subtitle = 0;
+            break;
+        }
+
+        case ACTIONID_SUBDELAY_DOWN:
+        case ACTIONID_SUBDELAY_UP:
+        {
+            int diff = (i_action == ACTIONID_SUBDELAY_UP) ? 50000 : -50000;
+            if( p_input )
+            {
+                vlc_value_t val, list, list2;
+                int i_count;
+                var_Get( p_input, "spu-es", &val );
+
+                var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
+                            &list, &list2 );
+                i_count = list.p_list->i_count;
+                if( i_count < 1 || val.i_int < 0 )
+                {
+                    display_message( p_notify_vout, _("No active subtitle") );
+                    var_FreeList( &list, &list2 );
+                    break;
+                }
+                int64_t i_delay = var_GetInteger( p_input, "spu-delay" ) + diff;
+
+                var_SetInteger( p_input, "spu-delay", i_delay );
+                clear_channels( p_as, p_notify_vout );
+                display_message( p_notify_vout, _( "Subtitle delay %i ms" ),
+                                (int)(i_delay/1000) );
+                var_FreeList( &list, &list2 );
+            }
+            break;
+        }
+        case ACTIONID_AUDIODELAY_DOWN:
+        case ACTIONID_AUDIODELAY_UP:
+        {
+            int diff = (i_action == ACTIONID_AUDIODELAY_UP) ? 50000 : -50000;
+            if( p_input )
+            {
+                int64_t i_delay = var_GetInteger( p_input, "audio-delay" )
+                                  + diff;
+
+                var_SetInteger( p_input, "audio-delay", i_delay );
+                clear_channels( p_as, p_notify_vout );
+                display_message( p_notify_vout, _( "Audio delay %i ms" ),
+                                 (int)(i_delay/1000) );
+            }
+            break;
+        }
+
+        case ACTIONID_AUDIO_TRACK:
+            if( p_input )
+            {
+                vlc_value_t val, list, list2;
+                int i_count, i;
+                var_Get( p_input, "audio-es", &val );
+                var_Change( p_input, "audio-es", VLC_VAR_GETCHOICES,
+                            &list, &list2 );
+                i_count = list.p_list->i_count;
+                if( i_count > 1 )
+                {
+                    for( i = 0; i < i_count; i++ )
+                    {
+                        if( val.i_int == list.p_list->p_values[i].i_int )
+                        {
+                            break;
+                        }
+                    }
+                    /* value of audio-es was not in choices list */
+                    if( i == i_count )
+                    {
+                        msg_Warn( p_input,
+                                  "invalid current audio track, selecting 0" );
+                        i = 0;
+                    }
+                    else if( i == i_count - 1 )
+                        i = 1;
+                    else
+                        i++;
+                    var_Set( p_input, "audio-es", list.p_list->p_values[i] );
+                    display_message( p_notify_vout, _("Audio track: %s"),
+                                    list2.p_list->p_values[i].psz_string );
+                }
+                var_FreeList( &list, &list2 );
+            }
+            break;
+        case ACTIONID_SUBTITLE_TRACK:
+        case ACTIONID_SUBTITLE_REVERSE_TRACK:
+            if( p_input )
+            {
+                vlc_value_t val, list, list2;
+                int i_count, i;
+                var_Get( p_input, "spu-es", &val );
+
+                var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
+                            &list, &list2 );
+                i_count = list.p_list->i_count;
+                if( i_count <= 1 )
+                {
+                    display_message( p_notify_vout, _("Subtitle track: %s"),
+                                    _("N/A") );
+                    var_FreeList( &list, &list2 );
+                    break;
+                }
+                for( i = 0; i < i_count; i++ )
+                {
+                    if( val.i_int == list.p_list->p_values[i].i_int )
+                    {
+                        break;
+                    }
+                }
+                /* value of spu-es was not in choices list */
+                if( i == i_count )
+                {
+                    msg_Warn( p_input,
+                              "invalid current subtitle track, selecting 0" );
+                    i = 0;
+                }
+                else if ((i == i_count - 1) && (i_action == ACTIONID_SUBTITLE_TRACK))
+                    i = 0;
+                else if ((i == 0) && (i_action == ACTIONID_SUBTITLE_REVERSE_TRACK))
+                    i = i_count - 1;
+                else
+                    i = (i_action == ACTIONID_SUBTITLE_TRACK) ? i+1 : i-1;
+                var_SetInteger( p_input, "spu-es", list.p_list->p_values[i].i_int );
+                var_SetInteger( p_input, "spu-choice", list.p_list->p_values[i].i_int );
+                display_message( p_notify_vout, _("Subtitle track: %s"),
+                                list2.p_list->p_values[i].psz_string );
+                var_FreeList( &list, &list2 );
+            }
+            break;
+        case ACTIONID_SUBTITLE_TOGGLE:
+            if( p_input )
+            {
+                vlc_value_t list, list2;
+                int i_count, i_sel_index, i_sel_id, i_old_id, i_new_index;
+                i_old_id = var_GetInteger( p_input, "spu-es" );
+                i_sel_id = var_GetInteger( p_input, "spu-choice" );
+
+                var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
+                            &list, &list2 );
+                i_count = list.p_list->i_count;
+                if( i_count <= 1 )
+                {
+                    display_message( p_notify_vout, _("Subtitle track: %s"),
+                                    _("N/A") );
+                    var_FreeList( &list, &list2 );
+                    break;
+                }
+                for( i_sel_index = 0; i_sel_index < i_count; i_sel_index++ )
+                {
+                    if( i_sel_id == list.p_list->p_values[i_sel_index].i_int )
+                    {
+                        break;
+                    }
+                }
+                /* if there is nothing to toggle choose the first track */
+                if( !i_sel_index ) {
+                    i_sel_index = 1;
+                    i_sel_id = list.p_list->p_values[1].i_int;
+                    var_SetInteger( p_input, "spu-choice", i_sel_id );
+                }
+
+                i_new_index = 0;
+                if( i_old_id != i_sel_id )
+                {
+                    if( i_sel_index >= i_count )
+                    {
+                        var_SetInteger( p_input, "spu-choice", list.p_list->p_values[0].i_int );
+                    }
+                    else
+                    {
+                        i_new_index = i_sel_index;
+                    }
+                }
+                var_SetInteger( p_input, "spu-es", list.p_list->p_values[i_new_index].i_int );
+                display_message( p_notify_vout, _("Subtitle track: %s"),
+                                list2.p_list->p_values[i_new_index].psz_string );
+                var_FreeList( &list, &list2 );
+            }
+            break;
+        case ACTIONID_PROGRAM_SID_NEXT:
+        case ACTIONID_PROGRAM_SID_PREV:
+            if( p_input )
+            {
+                vlc_value_t val, list, list2;
+                int i_count, i;
+                var_Get( p_input, "program", &val );
+
+                var_Change( p_input, "program", VLC_VAR_GETCHOICES,
+                            &list, &list2 );
+                i_count = list.p_list->i_count;
+                if( i_count <= 1 )
+                {
+                    display_message( p_notify_vout, _("Program Service ID: %s"),
+                                    _("N/A") );
+                    var_FreeList( &list, &list2 );
+                    break;
+                }
+                for( i = 0; i < i_count; i++ )
+                {
+                    if( val.i_int == list.p_list->p_values[i].i_int )
+                    {
+                        break;
+                    }
+                }
+                /* value of program sid was not in choices list */
+                if( i == i_count )
+                {
+                    msg_Warn( p_input,
+                              "invalid current program SID, selecting 0" );
+                    i = 0;
+                }
+                else if( i_action == ACTIONID_PROGRAM_SID_NEXT ) {
+                    if( i == i_count - 1 )
+                        i = 0;
+                    else
+                        i++;
+                    }
+                else { /* ACTIONID_PROGRAM_SID_PREV */
+                    if( i == 0 )
+                        i = i_count - 1;
+                    else
+                        i--;
+                    }
+                var_Set( p_input, "program", list.p_list->p_values[i] );
+                display_message( p_notify_vout, _("Program Service ID: %s"),
+                                list2.p_list->p_values[i].psz_string );
+                var_FreeList( &list, &list2 );
+            }
+            break;
+
+        case ACTIONID_JUMP_BACKWARD_EXTRASHORT:
+        case ACTIONID_JUMP_FORWARD_EXTRASHORT:
+        case ACTIONID_JUMP_BACKWARD_SHORT:
+        case ACTIONID_JUMP_FORWARD_SHORT:
+        case ACTIONID_JUMP_BACKWARD_MEDIUM:
+        case ACTIONID_JUMP_FORWARD_MEDIUM:
+        case ACTIONID_JUMP_BACKWARD_LONG:
+        case ACTIONID_JUMP_FORWARD_LONG:
+        {
+            if( p_input == NULL || !var_GetBool( p_input, "can-seek" ) )
+                break;
+
+            const char *varname;
+            int sign = +1;
+            switch( i_action )
+            {
+                case ACTIONID_JUMP_BACKWARD_EXTRASHORT:
+                    sign = -1;
+                    /* fall through */
+                case ACTIONID_JUMP_FORWARD_EXTRASHORT:
+                    varname = "extrashort-jump-size";
+                    break;
+                case ACTIONID_JUMP_BACKWARD_SHORT:
+                    sign = -1;
+                    /* fall through */
+                case ACTIONID_JUMP_FORWARD_SHORT:
+                    varname = "short-jump-size";
+                    break;
+                case ACTIONID_JUMP_BACKWARD_MEDIUM:
+                    sign = -1;
+                    /* fall through */
+                case ACTIONID_JUMP_FORWARD_MEDIUM:
+                    varname = "medium-jump-size";
+                    break;
+                case ACTIONID_JUMP_BACKWARD_LONG:
+                    sign = -1;
+                    /* fall through */
+                case ACTIONID_JUMP_FORWARD_LONG:
+                    varname = "long-jump-size";
+                    break;
+                default:
+                    vlc_assert_unreachable();
+            }
+
+            mtime_t it = var_InheritInteger( p_input, varname );
+            if( it < 0 )
+                break;
+            var_SetInteger( p_input, "time-offset", it * sign * CLOCK_FREQ );
+            display_position( p_as, p_notify_vout, p_input );
+            break;
+        }
+
+        /* Input navigation */
+        case ACTIONID_TITLE_PREV:
+            if( p_input )
+                var_TriggerCallback( p_input, "prev-title" );
+            break;
+        case ACTIONID_TITLE_NEXT:
+            if( p_input )
+                var_TriggerCallback( p_input, "next-title" );
+            break;
+        case ACTIONID_CHAPTER_PREV:
+            if( p_input )
+                var_TriggerCallback( p_input, "prev-chapter" );
+            break;
+        case ACTIONID_CHAPTER_NEXT:
+            if( p_input )
+                var_TriggerCallback( p_input, "next-chapter" );
+            break;
+        case ACTIONID_DISC_MENU:
+            if( p_input )
+                var_SetInteger( p_input, "title  0", 2 );
+            break;
+        case ACTIONID_NAV_ACTIVATE:
+            if( p_input )
+                input_Control( p_input, INPUT_NAV_ACTIVATE, NULL );
+            break;
+        default:
+            vlc_assert_unreachable();
+    }
+    if( p_notify_vout != NULL )
+        vlc_object_release( p_notify_vout );
+    if( b_release_input == true )
+        vlc_object_release( p_input );
+}
+
+static void
+handle_vout_actions( playlist_t *p_playlist, vlc_action_id_t i_action,
+                     bool b_notify, vlc_actions_t *p_as,
+                     unsigned int i_args, va_list args )
+{
+    vout_thread_t *p_vout = NULL;
+    bool b_release_vout = false;
+    assert( i_args <= 1);
+    if( i_args > 0 )
+        p_vout = va_arg( args, vout_thread_t* );
+    if( p_vout == NULL )
+    {
+        input_thread_t *p_input = playlist_CurrentInput( p_playlist );
+        if( p_input != NULL )
+        {
+            p_vout = input_GetVout( p_input );
+            b_release_vout = p_vout != NULL;
+            vlc_object_release( p_input );
+        }
+    }
+    vout_thread_t *p_notify_vout = b_notify ? p_vout : NULL;
+    register_osd_channels( p_as, p_notify_vout );
+
+    switch( i_action )
+    {
+        case ACTIONID_WALLPAPER:
+        {
+            bool wp = var_ToggleBool( p_playlist, "video-wallpaper" );
+            if( p_vout )
+                var_SetBool( p_vout, "video-wallpaper", wp );
+            break;
+        }
+        case ACTIONID_SNAPSHOT:
+            if( p_vout )
+                var_TriggerCallback( p_vout, "video-snapshot" );
+            break;
+        case ACTIONID_TOGGLE_FULLSCREEN:
+        {
+            if( p_vout )
+            {
+                bool fs = var_ToggleBool( p_vout, "fullscreen" );
+                var_SetBool( p_playlist, "fullscreen", fs );
+            }
+            else
+                var_ToggleBool( p_playlist, "fullscreen" );
+            break;
+        }
+        case ACTIONID_LEAVE_FULLSCREEN:
+            if( p_vout )
+                var_SetBool( p_vout, "fullscreen", false );
+            var_SetBool( p_playlist, "fullscreen", false );
+            break;
+
+        case ACTIONID_ASPECT_RATIO:
+            if( p_vout )
+            {
+                vlc_value_t val={0}, val_list, text_list;
+                var_Get( p_vout, "aspect-ratio", &val );
+                if( var_Change( p_vout, "aspect-ratio", VLC_VAR_GETCHOICES,
+                                &val_list, &text_list ) >= 0 )
+                {
+                    int i;
+                    for( i = 0; i < val_list.p_list->i_count; i++ )
+                    {
+                        if( !strcmp( val_list.p_list->p_values[i].psz_string,
+                                     val.psz_string ) )
+                        {
+                            i++;
+                            break;
+                        }
+                    }
+                    if( i == val_list.p_list->i_count ) i = 0;
+                    var_SetString( p_vout, "aspect-ratio",
+                                   val_list.p_list->p_values[i].psz_string );
+                    display_message( p_notify_vout, _("Aspect ratio: %s"),
+                                    text_list.p_list->p_values[i].psz_string );
+
+                    var_FreeList( &val_list, &text_list );
+                }
+                free( val.psz_string );
+            }
+            break;
+
+        case ACTIONID_CROP:
+            if( p_vout )
+            {
+                vlc_value_t val={0}, val_list, text_list;
+                var_Get( p_vout, "crop", &val );
+                if( var_Change( p_vout, "crop", VLC_VAR_GETCHOICES,
+                                &val_list, &text_list ) >= 0 )
+                {
+                    int i;
+                    for( i = 0; i < val_list.p_list->i_count; i++ )
+                    {
+                        if( !strcmp( val_list.p_list->p_values[i].psz_string,
+                                     val.psz_string ) )
+                        {
+                            i++;
+                            break;
+                        }
+                    }
+                    if( i == val_list.p_list->i_count ) i = 0;
+                    var_SetString( p_vout, "crop",
+                                   val_list.p_list->p_values[i].psz_string );
+                    display_message( p_notify_vout, _("Crop: %s"),
+                                    text_list.p_list->p_values[i].psz_string );
+
+                    var_FreeList( &val_list, &text_list );
+                }
+                free( val.psz_string );
+            }
+            break;
+        case ACTIONID_CROP_TOP:
+            if( p_vout )
+                var_IncInteger( p_vout, "crop-top" );
+            break;
+        case ACTIONID_UNCROP_TOP:
+            if( p_vout )
+                var_DecInteger( p_vout, "crop-top" );
+            break;
+        case ACTIONID_CROP_BOTTOM:
+            if( p_vout )
+                var_IncInteger( p_vout, "crop-bottom" );
+            break;
+        case ACTIONID_UNCROP_BOTTOM:
+            if( p_vout )
+                var_DecInteger( p_vout, "crop-bottom" );
+            break;
+        case ACTIONID_CROP_LEFT:
+            if( p_vout )
+                var_IncInteger( p_vout, "crop-left" );
+            break;
+        case ACTIONID_UNCROP_LEFT:
+            if( p_vout )
+                var_DecInteger( p_vout, "crop-left" );
+            break;
+        case ACTIONID_CROP_RIGHT:
+            if( p_vout )
+                var_IncInteger( p_vout, "crop-right" );
+            break;
+        case ACTIONID_UNCROP_RIGHT:
+            if( p_vout )
+                var_DecInteger( p_vout, "crop-right" );
+            break;
+        case ACTIONID_TOGGLE_AUTOSCALE:
+            if( p_vout )
+            {
+                float f_scalefactor = var_GetFloat( p_vout, "zoom" );
+                if ( f_scalefactor != 1.f )
+                {
+                    var_SetFloat( p_vout, "zoom", 1.f );
+                    display_message( p_notify_vout, _("Zooming reset") );
+                }
+                else
+                {
+                    bool b_autoscale = !var_GetBool( p_vout, "autoscale" );
+                    var_SetBool( p_vout, "autoscale", b_autoscale );
+                    if( b_autoscale )
+                        display_message( p_notify_vout, _("Scaled to screen") );
+                    else
+                        display_message( p_notify_vout, _("Original Size") );
+                }
+            }
+            break;
+        case ACTIONID_SCALE_UP:
+            if( p_vout )
+            {
+               float f_scalefactor = var_GetFloat( p_vout, "zoom" );
+
+               if( f_scalefactor < 10.f )
+                   f_scalefactor += .1f;
+               var_SetFloat( p_vout, "zoom", f_scalefactor );
+            }
+            break;
+        case ACTIONID_SCALE_DOWN:
+            if( p_vout )
+            {
+               float f_scalefactor = var_GetFloat( p_vout, "zoom" );
+
+               if( f_scalefactor > .3f )
+                   f_scalefactor -= .1f;
+               var_SetFloat( p_vout, "zoom", f_scalefactor );
+            }
+            break;
+
+        case ACTIONID_ZOOM_QUARTER:
+        case ACTIONID_ZOOM_HALF:
+        case ACTIONID_ZOOM_ORIGINAL:
+        case ACTIONID_ZOOM_DOUBLE:
+            if( p_vout )
+            {
+                float f;
+                switch( i_action )
+                {
+                    case ACTIONID_ZOOM_QUARTER:  f = 0.25; break;
+                    case ACTIONID_ZOOM_HALF:     f = 0.5;  break;
+                    case ACTIONID_ZOOM_ORIGINAL: f = 1.;   break;
+                     /*case ACTIONID_ZOOM_DOUBLE:*/
+                    default:                     f = 2.;   break;
+                }
+                var_SetFloat( p_vout, "zoom", f );
+            }
+            break;
+        case ACTIONID_ZOOM:
+        case ACTIONID_UNZOOM:
+            if( p_vout )
+            {
+                vlc_value_t val={0}, val_list, text_list;
+                var_Get( p_vout, "zoom", &val );
+                if( var_Change( p_vout, "zoom", VLC_VAR_GETCHOICES,
+                                &val_list, &text_list ) >= 0 )
+                {
+                    int i;
+                    for( i = 0; i < val_list.p_list->i_count; i++ )
+                    {
+                        if( val_list.p_list->p_values[i].f_float
+                           == val.f_float )
+                        {
+                            if( i_action == ACTIONID_ZOOM )
+                                i++;
+                            else /* ACTIONID_UNZOOM */
+                                i--;
+                            break;
+                        }
+                    }
+                    if( i == val_list.p_list->i_count ) i = 0;
+                    if( i == -1 ) i = val_list.p_list->i_count-1;
+                    var_SetFloat( p_vout, "zoom",
+                                  val_list.p_list->p_values[i].f_float );
+                    display_message( p_notify_vout, _("Zoom mode: %s"),
+                                    text_list.p_list->p_values[i].psz_string );
+
+                    var_FreeList( &val_list, &text_list );
+                }
+            }
+            break;
+        case ACTIONID_DEINTERLACE:
+            if( p_vout )
+            {
+                int i_deinterlace = var_GetInteger( p_vout, "deinterlace" );
+                if( i_deinterlace != 0 )
+                {
+                    var_SetInteger( p_vout, "deinterlace", 0 );
+                    display_message( p_notify_vout, _("Deinterlace off") );
+                }
+                else
+                {
+                    var_SetInteger( p_vout, "deinterlace", 1 );
+
+                    char *psz_mode = var_GetString( p_vout, "deinterlace-mode" );
+                    vlc_value_t vlist, tlist;
+                    if( psz_mode && !var_Change( p_vout, "deinterlace-mode", VLC_VAR_GETCHOICES, &vlist, &tlist ) )
+                    {
+                        const char *psz_text = NULL;
+                        for( int i = 0; i < vlist.p_list->i_count; i++ )
+                        {
+                            if( !strcmp( vlist.p_list->p_values[i].psz_string, psz_mode ) )
+                            {
+                                psz_text = tlist.p_list->p_values[i].psz_string;
+                                break;
+                            }
+                        }
+                        display_message( p_notify_vout, "%s (%s)", _("Deinterlace on"),
+                                        psz_text ? psz_text : psz_mode );
+
+                        var_FreeList( &vlist, &tlist );
+                    }
+                    free( psz_mode );
+                }
+            }
+            break;
+        case ACTIONID_DEINTERLACE_MODE:
+            if( p_vout )
+            {
+                char *psz_mode = var_GetString( p_vout, "deinterlace-mode" );
+                vlc_value_t vlist, tlist;
+                if( psz_mode && !var_Change( p_vout, "deinterlace-mode", VLC_VAR_GETCHOICES, &vlist, &tlist ))
+                {
+                    const char *psz_text = NULL;
+                    int i;
+                    for( i = 0; i < vlist.p_list->i_count; i++ )
+                    {
+                        if( !strcmp( vlist.p_list->p_values[i].psz_string, psz_mode ) )
+                        {
+                            i++;
+                            break;
+                        }
+                    }
+                    if( i == vlist.p_list->i_count ) i = 0;
+                    psz_text = tlist.p_list->p_values[i].psz_string;
+                    var_SetString( p_vout, "deinterlace-mode", vlist.p_list->p_values[i].psz_string );
+
+                    int i_deinterlace = var_GetInteger( p_vout, "deinterlace" );
+                    if( i_deinterlace != 0 )
+                    {
+                      display_message( p_notify_vout, "%s (%s)", _("Deinterlace on"),
+                                      psz_text ? psz_text : psz_mode );
+                    }
+                    else
+                    {
+                      display_message( p_notify_vout, "%s (%s)", _("Deinterlace off"),
+                                      psz_text ? psz_text : psz_mode );
+                    }
+
+                    var_FreeList( &vlist, &tlist );
+                }
+                free( psz_mode );
+            }
+            break;
+
+        case ACTIONID_SUBTITLE_TEXT_SCALE_DOWN:
+        case ACTIONID_SUBTITLE_TEXT_SCALE_UP:
+        case ACTIONID_SUBTITLE_TEXT_SCALE_NORMAL:
+            if( p_vout )
+            {
+                int i_scale;
+                if( i_action == ACTIONID_SUBTITLE_TEXT_SCALE_NORMAL )
+                {
+                    i_scale = 100;
+                }
+                else
+                {
+                    i_scale = var_GetInteger( p_playlist, "sub-text-scale" );
+                    i_scale += ((i_action == ACTIONID_SUBTITLE_TEXT_SCALE_UP) ? 1 : -1) * 25;
+                    i_scale = VLC_CLIP( i_scale, 10, 500 );
+                }
+                var_SetInteger( p_playlist, "sub-text-scale", i_scale );
+                display_message( p_notify_vout, _( "Subtitle text scale %d%%" ), i_scale );
+            }
+            break;
+        default:
+            vlc_assert_unreachable();
+    }
+    if( b_release_vout == true )
+        vlc_object_release( p_vout );
+}
+
+static void
+handle_input_vout_actions( playlist_t *p_playlist, vlc_action_id_t i_action,
+                           bool b_notify, vlc_actions_t *p_as,
+                           unsigned int i_args, va_list args )
+{
+    vout_thread_t *p_vout = NULL;
+    bool b_release_vout = false;
+    input_thread_t *p_input = NULL;
+    bool b_release_input = false;
+    if( i_args > 0 )
+    {
+        p_input = va_arg( args, input_thread_t* );
+        if( i_args > 1 )
+            p_vout = va_arg( args, vout_thread_t* );
+    }
+    if( p_input == NULL )
+    {
+        p_input = playlist_CurrentInput( p_playlist );
+        b_release_input = p_input != NULL;
+    }
+    if( p_vout == NULL && p_input != NULL )
+    {
+        p_vout = input_GetVout( p_input );
+        b_release_vout = p_vout != NULL;
+    }
+    vout_thread_t *p_notify_vout = b_notify ? p_vout : NULL;
+    register_osd_channels( p_as, p_notify_vout );
+
+    switch( i_action )
+    {
+        case ACTIONID_POSITION:
+        {
+            if( p_vout && vout_OSDEpg( p_vout, input_GetItem( p_input ) ) )
+                display_position( p_as, p_notify_vout, p_input );
+            break;
+        }
+        case ACTIONID_COMBO_VOL_FOV_UP:
+        {
+            bool b_vrnav = p_vout && var_GetBool( p_vout,
+                                                  "viewpoint-changeable" );
+            if( b_vrnav )
+                vlc_actions_do( p_playlist, ACTIONID_VIEWPOINT_FOV_IN, b_notify,
+                                i_args, p_input, p_vout );
+            else
+                vlc_actions_do( p_playlist, ACTIONID_VOL_UP, b_notify, 0 );
+            break;
+        }
+        case ACTIONID_COMBO_VOL_FOV_DOWN:
+        {
+            bool b_vrnav = p_vout && var_GetBool( p_vout,
+                                              "viewpoint-changeable" );
+            if( b_vrnav )
+                vlc_actions_do( p_playlist, ACTIONID_VIEWPOINT_FOV_OUT, b_notify,
+                                i_args, p_input, p_vout );
+            else
+                vlc_actions_do( p_playlist, ACTIONID_VOL_DOWN, b_notify, 0 );
+            break;
+        }
+        case ACTIONID_NAV_UP:
+        {
+            if( p_vout )
+                input_UpdateViewpoint( p_input,
+                                       &(vlc_viewpoint_t) { .pitch = -1.f },
+                                       false );
+            if( p_input )
+                input_Control( p_input, INPUT_NAV_UP, NULL );
+            break;
+        }
+        case ACTIONID_NAV_DOWN:
+        {
+            if( p_vout )
+                input_UpdateViewpoint( p_input,
+                                       &(vlc_viewpoint_t) { .pitch = 1.f },
+                                       false );
+            if( p_input )
+                input_Control( p_input, INPUT_NAV_DOWN, NULL );
+            break;
+        }
+        case ACTIONID_NAV_LEFT:
+        {
+            if( p_vout )
+                input_UpdateViewpoint( p_input,
+                                       &(vlc_viewpoint_t) { .yaw = -1.f },
+                                       false );
+            if( p_input )
+                input_Control( p_input, INPUT_NAV_LEFT, NULL );
+            break;
+        }
+        case ACTIONID_NAV_RIGHT:
+        {
+            if( p_vout )
+                input_UpdateViewpoint( p_input,
+                                       &(vlc_viewpoint_t) { .yaw = 1.f },
+                                       false );
+            if( p_input )
+                input_Control( p_input, INPUT_NAV_RIGHT, NULL );
+            break;
+        }
+        case ACTIONID_VIEWPOINT_FOV_IN:
+        {
+            if( p_vout )
+                input_UpdateViewpoint( p_input,
+                                       &(vlc_viewpoint_t) { .fov = -1.f },
+                                       false );
+            break;
+        }
+        case ACTIONID_VIEWPOINT_FOV_OUT:
+        {
+            if( p_vout )
+                input_UpdateViewpoint( p_input,
+                                       &(vlc_viewpoint_t) { .fov = 1.f },
+                                       false );
+            break;
+        }
+
+        case ACTIONID_VIEWPOINT_ROLL_CLOCK:
+        {
+            if( p_vout )
+                input_UpdateViewpoint( p_input,
+                                       &(vlc_viewpoint_t) { .roll = -1.f },
+                                       false );
+            break;
+        }
+        case ACTIONID_VIEWPOINT_ROLL_ANTICLOCK:
+        {
+            if( p_vout )
+                input_UpdateViewpoint( p_input,
+                                       &(vlc_viewpoint_t) { .roll = 1.f },
+                                       false );
+            break;
+        }
+        case ACTIONID_VIEWPOINT_UPDATE:
+        {
+            if ( p_vout )
+            {
+                assert( i_args == 3 );
+                vlc_viewpoint_t* viewpoint = va_arg( args, vlc_viewpoint_t* );
+                input_UpdateViewpoint( p_input, viewpoint, false );
+            }
+            break;
+        }
+        case ACTIONID_SUBPOS_DOWN:
+        case ACTIONID_SUBPOS_UP:
+        {
+            if( p_input && p_vout )
+            {
+                vlc_value_t val, list, list2;
+                int i_count;
+                var_Get( p_input, "spu-es", &val );
+
+                var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
+                            &list, &list2 );
+                i_count = list.p_list->i_count;
+                if( i_count < 1 || val.i_int < 0 )
+                {
+                    display_message( p_notify_vout,
+                                    _("Subtitle position: no active subtitle") );
+                    var_FreeList( &list, &list2 );
+                    break;
+                }
+
+                int i_pos;
+                if( i_action == ACTIONID_SUBPOS_DOWN )
+                    i_pos = var_DecInteger( p_vout, "sub-margin" );
+                else
+                    i_pos = var_IncInteger( p_vout, "sub-margin" );
+
+                clear_channels( p_as, p_notify_vout );
+                display_message( p_notify_vout, _( "Subtitle position %d px" ), i_pos );
+                var_FreeList( &list, &list2 );
+            }
+            break;
+        }
+        default:
+            vlc_assert_unreachable();
+    }
+    if( b_release_vout == true )
+        vlc_object_release( p_vout );
+    if( b_release_input == true )
+        vlc_object_release( p_input );
+}
+
+static void
+handle_aout_actions( playlist_t *p_playlist, vlc_action_id_t i_action,
+                     bool b_notify, vlc_actions_t* p_as,
+                     unsigned int i_args, va_list args )
+{
+    audio_output_t *p_aout = NULL;
+    bool b_release_aout = false;
+    assert( i_args <= 1 );
+    if( i_args > 0 )
+        p_aout = va_arg( args, audio_output_t* );
+    if( p_aout == NULL )
+    {
+        p_aout = playlist_GetAout( p_playlist );
+        b_release_aout = true;
+    }
+    vout_thread_t *p_notify_vout = NULL;
+    if( b_notify == true )
+    {
+        input_thread_t *p_input = playlist_CurrentInput( p_playlist );
+        if( p_input != NULL )
+        {
+            p_notify_vout = input_GetVout( p_input );
+            vlc_object_release( p_input );
+        }
+    }
+    register_osd_channels( p_as, p_notify_vout );
+
+    switch( i_action )
+    {
+        case ACTIONID_AUDIODEVICE_CYCLE:
+        {
+            if( p_aout == NULL )
+                break;
+
+            char **ids, **names;
+            int n = aout_DevicesList( p_aout, &ids, &names );
+            if( n == -1 )
+                break;
+
+            char *dev = aout_DeviceGet( p_aout );
+            const char *devstr = (dev != NULL) ? dev : "";
+
+            int idx = 0;
+            for( int i = 0; i < n; i++ )
+            {
+                if( !strcmp(devstr, ids[i]) )
+                    idx = (i + 1) % n;
+            }
+            free( dev );
+
+            if( !aout_DeviceSet( p_aout, ids[idx] ) )
+                display_message( p_notify_vout, _("Audio Device: %s"), names[idx] );
+
+            for( int i = 0; i < n; i++ )
+            {
+                free( ids[i] );
+                free( names[i] );
+            }
+            free( ids );
+            free( names );
+            break;
+        }
+        default:
+            vlc_assert_unreachable();
+    }
+    if( p_notify_vout != NULL )
+        vlc_object_release( p_notify_vout );
+    if( b_release_aout == true )
+        vlc_object_release( p_aout );
+}
+
+static void
+handle_misc_actions( playlist_t *p_playlist, vlc_action_id_t i_action,
+                     bool b_notify, vlc_actions_t* p_as )
+{
+    libvlc_int_t *p_libvlc = p_playlist->obj.libvlc;
+    switch( i_action )
+    {
+        /* Libvlc / interface actions */
+        case ACTIONID_QUIT:
+        {
+            vout_thread_t *p_notify_vout = NULL;
+            if( b_notify )
+            {
+                input_thread_t *p_input = playlist_CurrentInput( p_playlist );
+                if( p_input )
+                {
+                    p_notify_vout = input_GetVout( p_input );
+                    vlc_object_release( p_input );
+                }
+            }
+
+            libvlc_Quit( p_libvlc );
+
+            clear_channels( p_as, p_notify_vout );
+            display_message( p_notify_vout, _( "Quit" ) );
+            if( p_notify_vout )
+                vlc_object_release( p_notify_vout );
+            break;
+        }
+        case ACTIONID_INTF_TOGGLE_FSC:
+        case ACTIONID_INTF_HIDE:
+            var_TriggerCallback( p_libvlc, "intf-toggle-fscontrol" );
+            break;
+        case ACTIONID_INTF_BOSS:
+            var_TriggerCallback( p_libvlc, "intf-boss" );
+            break;
+        case ACTIONID_INTF_POPUP_MENU:
+            var_TriggerCallback( p_libvlc, "intf-popupmenu" );
+            break;
+        default:
+            vlc_assert_unreachable();
+    }
+}
+
+static int
+vlc_actions_do_va( vlc_object_t *p_obj, vlc_action_id_t i_action, bool b_notify,
+                   unsigned int i_args, va_list args )
+{
+    libvlc_int_t *p_libvlc = p_obj->obj.libvlc;
+    vlc_actions_t *p_as = libvlc_priv(p_libvlc)->actions;
+    playlist_t *p_playlist = libvlc_priv(p_obj->obj.libvlc)->playlist;
+
+    assert(p_as != NULL);
+
+    if( !p_playlist )
+        return VLC_EGENERIC;
+
+    /* Quit */
+    switch( i_action )
+    {
+        /* Libvlc / interface actions */
+        case ACTIONID_QUIT:
+        case ACTIONID_INTF_TOGGLE_FSC:
+        case ACTIONID_INTF_HIDE:
+        case ACTIONID_INTF_BOSS:
+        case ACTIONID_INTF_POPUP_MENU:
+            handle_misc_actions( p_playlist, i_action, b_notify, p_as );
+            break;
+
+        /* Playlist actions (including audio) */
+        case ACTIONID_PLAY_BOOKMARK1:
+        case ACTIONID_PLAY_BOOKMARK2:
+        case ACTIONID_PLAY_BOOKMARK3:
+        case ACTIONID_PLAY_BOOKMARK4:
+        case ACTIONID_PLAY_BOOKMARK5:
+        case ACTIONID_PLAY_BOOKMARK6:
+        case ACTIONID_PLAY_BOOKMARK7:
+        case ACTIONID_PLAY_BOOKMARK8:
+        case ACTIONID_PLAY_BOOKMARK9:
+        case ACTIONID_PLAY_BOOKMARK10:
+        case ACTIONID_SET_BOOKMARK1:
+        case ACTIONID_SET_BOOKMARK2:
+        case ACTIONID_SET_BOOKMARK3:
+        case ACTIONID_SET_BOOKMARK4:
+        case ACTIONID_SET_BOOKMARK5:
+        case ACTIONID_SET_BOOKMARK6:
+        case ACTIONID_SET_BOOKMARK7:
+        case ACTIONID_SET_BOOKMARK8:
+        case ACTIONID_SET_BOOKMARK9:
+        case ACTIONID_SET_BOOKMARK10:
+        case ACTIONID_LOOP:
+        case ACTIONID_RANDOM:
+        case ACTIONID_NEXT:
+        case ACTIONID_PREV:
+        case ACTIONID_STOP:
+        case ACTIONID_RATE_NORMAL:
+        case ACTIONID_FASTER:
+        case ACTIONID_SLOWER:
+        case ACTIONID_RATE_FASTER_FINE:
+        case ACTIONID_RATE_SLOWER_FINE:
+        case ACTIONID_PLAY_CLEAR:
+        case ACTIONID_VOL_UP:
+        case ACTIONID_VOL_DOWN:
+        case ACTIONID_VOL_MUTE:
+            handle_playlist_actions( p_playlist, i_action, b_notify, p_as );
+            break;
+
+        case ACTIONID_AUDIODEVICE_CYCLE:
+            handle_aout_actions( p_playlist, i_action, b_notify, p_as,
+                                 i_args, args );
+            break;
+
+        /* Input actions */
+        case ACTIONID_PLAY_PAUSE:
+        case ACTIONID_PLAY:
+        case ACTIONID_PAUSE:
+        case ACTIONID_RECORD:
+        case ACTIONID_FRAME_NEXT:
+        case ACTIONID_SUBSYNC_MARKAUDIO:
+        case ACTIONID_SUBSYNC_MARKSUB:
+        case ACTIONID_SUBSYNC_APPLY:
+        case ACTIONID_SUBSYNC_RESET:
+        case ACTIONID_SUBDELAY_DOWN:
+        case ACTIONID_SUBDELAY_UP:
+        case ACTIONID_AUDIODELAY_DOWN:
+        case ACTIONID_AUDIODELAY_UP:
+        case ACTIONID_AUDIO_TRACK:
+        case ACTIONID_SUBTITLE_TRACK:
+        case ACTIONID_SUBTITLE_REVERSE_TRACK:
+        case ACTIONID_SUBTITLE_TOGGLE:
+        case ACTIONID_PROGRAM_SID_NEXT:
+        case ACTIONID_PROGRAM_SID_PREV:
+        case ACTIONID_JUMP_BACKWARD_EXTRASHORT:
+        case ACTIONID_JUMP_FORWARD_EXTRASHORT:
+        case ACTIONID_JUMP_BACKWARD_SHORT:
+        case ACTIONID_JUMP_FORWARD_SHORT:
+        case ACTIONID_JUMP_BACKWARD_MEDIUM:
+        case ACTIONID_JUMP_FORWARD_MEDIUM:
+        case ACTIONID_JUMP_BACKWARD_LONG:
+        case ACTIONID_JUMP_FORWARD_LONG:
+        case ACTIONID_TITLE_PREV:
+        case ACTIONID_TITLE_NEXT:
+        case ACTIONID_CHAPTER_PREV:
+        case ACTIONID_CHAPTER_NEXT:
+        case ACTIONID_DISC_MENU:
+        case ACTIONID_NAV_ACTIVATE:
+            handle_input_actions( p_playlist, i_action, b_notify,
+                                  p_as, i_args, args );
+            break;
+
+        /* Video Output actions */
+        case ACTIONID_WALLPAPER:
+        case ACTIONID_SNAPSHOT:
+        case ACTIONID_TOGGLE_FULLSCREEN:
+        case ACTIONID_LEAVE_FULLSCREEN:
+        case ACTIONID_ASPECT_RATIO:
+        case ACTIONID_CROP:
+        case ACTIONID_CROP_TOP:
+        case ACTIONID_UNCROP_TOP:
+        case ACTIONID_CROP_BOTTOM:
+        case ACTIONID_UNCROP_BOTTOM:
+        case ACTIONID_CROP_LEFT:
+        case ACTIONID_UNCROP_LEFT:
+        case ACTIONID_CROP_RIGHT:
+        case ACTIONID_UNCROP_RIGHT:
+        case ACTIONID_TOGGLE_AUTOSCALE:
+        case ACTIONID_SCALE_UP:
+        case ACTIONID_SCALE_DOWN:
+        case ACTIONID_ZOOM_QUARTER:
+        case ACTIONID_ZOOM_HALF:
+        case ACTIONID_ZOOM_ORIGINAL:
+        case ACTIONID_ZOOM_DOUBLE:
+        case ACTIONID_ZOOM:
+        case ACTIONID_UNZOOM:
+        case ACTIONID_DEINTERLACE:
+        case ACTIONID_DEINTERLACE_MODE:
+        case ACTIONID_SUBTITLE_TEXT_SCALE_DOWN:
+        case ACTIONID_SUBTITLE_TEXT_SCALE_UP:
+        case ACTIONID_SUBTITLE_TEXT_SCALE_NORMAL:
+            handle_vout_actions( p_playlist, i_action, b_notify,
+                                 p_as, i_args, args );
+            break;
+
+        /* Input + video output */
+        case ACTIONID_POSITION:
+        case ACTIONID_COMBO_VOL_FOV_UP:
+        case ACTIONID_COMBO_VOL_FOV_DOWN:
+        case ACTIONID_NAV_UP:
+        case ACTIONID_NAV_DOWN:
+        case ACTIONID_NAV_LEFT:
+        case ACTIONID_NAV_RIGHT:
+        case ACTIONID_VIEWPOINT_FOV_IN:
+        case ACTIONID_VIEWPOINT_FOV_OUT:
+        case ACTIONID_VIEWPOINT_ROLL_CLOCK:
+        case ACTIONID_VIEWPOINT_ROLL_ANTICLOCK:
+        case ACTIONID_VIEWPOINT_UPDATE:
+        case ACTIONID_SUBPOS_DOWN:
+        case ACTIONID_SUBPOS_UP:
+            handle_input_vout_actions( p_playlist, i_action, b_notify,
+                                       p_as, i_args, args );
+            break;
+
+        case ACTIONID_NONE:
+            return VLC_EGENERIC;
+    }
+    return VLC_SUCCESS;
+}
+
+#undef vlc_actions_do
+int vlc_actions_do(vlc_object_t *p_obj, vlc_action_id_t i_action,
+                   bool b_notify, unsigned int i_nb_args, ...)
+{
+    va_list args;
+    int     i_result;
+
+    va_start( args, i_nb_args );
+    i_result = vlc_actions_do_va( p_obj, i_action, b_notify, i_nb_args, args );
+    va_end( args );
+    return i_result;
+}
-- 
2.11.0



More information about the vlc-devel mailing list