]> git.sesse.net Git - vlc/blobdiff - src/video_output/display.c
shine: avoid double free
[vlc] / src / video_output / display.c
index 6d64e34ad04e5f8406e841c9982a0a433606b6cd..3e3a3d5c7a599fcc30c04f0b73a9d3c86e81d388 100644 (file)
@@ -6,19 +6,19 @@
  *
  * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * 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
+ * the Free Software Foundation; either version 2.1 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
 
 /*****************************************************************************
 #include <vlc_video_splitter.h>
 #include <vlc_vout_display.h>
 #include <vlc_vout.h>
+#include <vlc_block.h>
+#include <vlc_modules.h>
 
 #include <libvlc.h>
 
 #include "display.h"
+#include "window.h"
 
 #include "event.h"
 
-/* It must be present as long as a vout_display_t must be created using a dummy
- * vout (as an opengl provider) */
-#define ALLOW_DUMMY_VOUT
-
 static void SplitterClose(vout_display_t *vd);
 
 /*****************************************************************************
@@ -51,38 +50,18 @@ static void SplitterClose(vout_display_t *vd);
  *****************************************************************************/
 static picture_t *VideoBufferNew(filter_t *filter)
 {
-    vout_display_t *vd = (vout_display_t*)filter->p_owner;
+    vout_display_t *vd = filter->owner.sys;
     const video_format_t *fmt = &filter->fmt_out.video;
 
     assert(vd->fmt.i_chroma == fmt->i_chroma &&
            vd->fmt.i_width  == fmt->i_width  &&
            vd->fmt.i_height == fmt->i_height);
 
-    picture_pool_t *pool = vout_display_Pool(vd, 1);
+    picture_pool_t *pool = vout_display_Pool(vd, 3);
     if (!pool)
         return NULL;
     return picture_pool_Get(pool);
 }
-static void VideoBufferDelete(filter_t *filter, picture_t *picture)
-{
-    VLC_UNUSED(filter);
-    picture_Release(picture);
-}
-
-static int  FilterAllocationInit(filter_t *filter, void *vd)
-{
-    filter->pf_video_buffer_new = VideoBufferNew;
-    filter->pf_video_buffer_del = VideoBufferDelete;
-    filter->p_owner             = vd;
-
-    return VLC_SUCCESS;
-}
-static void FilterAllocationClean(filter_t *filter)
-{
-    filter->pf_video_buffer_new = NULL;
-    filter->pf_video_buffer_del = NULL;
-    filter->p_owner             = NULL;
-}
 
 /*****************************************************************************
  *
@@ -98,7 +77,7 @@ static vout_display_t *vout_display_New(vlc_object_t *obj,
                                         vout_display_owner_t *owner)
 {
     /* */
-    vout_display_t *vd = vlc_object_create(obj, sizeof(*vd));
+    vout_display_t *vd = vlc_custom_create(obj, sizeof(*vd), "vout display" );
 
     /* */
     video_format_Copy(&vd->source, fmt);
@@ -112,6 +91,8 @@ static vout_display_t *vout_display_New(vlc_object_t *obj,
     vd->info.has_double_click = false;
     vd->info.has_hide_mouse = false;
     vd->info.has_pictures_invalid = false;
+    vd->info.has_event_thread = false;
+    vd->info.subpicture_chromas = NULL;
 
     vd->cfg = cfg;
     vd->pool = NULL;
@@ -123,8 +104,6 @@ static vout_display_t *vout_display_New(vlc_object_t *obj,
 
     vd->owner = *owner;
 
-    vlc_object_attach(vd, obj);
-
     if (load_module) {
         vd->module = module_need(vd, "vout display", module, module && *module != '\0');
         if (!vd->module) {
@@ -145,6 +124,9 @@ static void vout_display_Delete(vout_display_t *vd)
     if (vd->module)
         module_unneed(vd, vd->module);
 
+    video_format_Clean(&vd->source);
+    video_format_Clean(&vd->fmt);
+
     vlc_object_release(vd);
 }
 
@@ -162,6 +144,7 @@ static int vout_display_Control(vout_display_t *vd, int query, ...)
 
     return result;
 }
+
 static void vout_display_Manage(vout_display_t *vd)
 {
     if (vd->manage)
@@ -194,6 +177,13 @@ void vout_display_GetDefaultDisplaySize(unsigned *width, unsigned *height,
 
     *width  = *width  * cfg->zoom.num / cfg->zoom.den;
     *height = *height * cfg->zoom.num / cfg->zoom.den;
+
+    if (ORIENT_IS_SWAP(source->orientation)) {
+
+        unsigned store = *width;
+        *width = *height;
+        *height = store;
+    }
 }
 
 /* */
@@ -211,6 +201,10 @@ void vout_display_PlacePicture(vout_display_place_t *place,
     unsigned display_width;
     unsigned display_height;
 
+    video_format_t source_rot;
+    video_format_ApplyRotation(&source_rot, source);
+    source = &source_rot;
+
     if (cfg->is_display_filled) {
         display_width  = cfg->display.width;
         display_height = cfg->display.height;
@@ -253,7 +247,7 @@ void vout_display_PlacePicture(vout_display_place_t *place,
         place->x = cfg->display.width - place->width;
         break;
     default:
-        place->x = (cfg->display.width - place->width) / 2;
+        place->x = ((int)cfg->display.width - (int)place->width) / 2;
         break;
     }
 
@@ -265,11 +259,67 @@ void vout_display_PlacePicture(vout_display_place_t *place,
         place->y = cfg->display.height - place->height;
         break;
     default:
-        place->y = (cfg->display.height - place->height) / 2;
+        place->y = ((int)cfg->display.height - (int)place->height) / 2;
         break;
     }
 }
 
+void vout_display_SendMouseMovedDisplayCoordinates(vout_display_t *vd, video_orientation_t orient_display, int m_x, int m_y, vout_display_place_t *place)
+{
+    video_format_t source_rot = vd->source;
+    video_format_TransformTo(&source_rot, orient_display);
+
+    if (place->width > 0 && place->height > 0) {
+
+        int x = (int)(source_rot.i_x_offset +
+                            (int64_t)(m_x - place->x) * source_rot.i_visible_width / place->width);
+        int y = (int)(source_rot.i_y_offset +
+                            (int64_t)(m_y - place->y) * source_rot.i_visible_height/ place->height);
+
+        video_transform_t transform = video_format_GetTransform(vd->source.orientation, orient_display);
+
+        int store;
+
+        switch (transform) {
+
+            case TRANSFORM_R90:
+                store = x;
+                x = y;
+                y = vd->source.i_visible_height - store;
+                break;
+            case TRANSFORM_R180:
+                x = vd->source.i_visible_width - x;
+                y = vd->source.i_visible_height - y;
+                break;
+            case TRANSFORM_R270:
+                store = x;
+                x = vd->source.i_visible_width - y;
+                y = store;
+                break;
+            case TRANSFORM_HFLIP:
+                x = vd->source.i_visible_width - x;
+                break;
+            case TRANSFORM_VFLIP:
+                y = vd->source.i_visible_height - y;
+                break;
+            case TRANSFORM_TRANSPOSE:
+                store = x;
+                x = y;
+                y = store;
+                break;
+            case TRANSFORM_ANTI_TRANSPOSE:
+                store = x;
+                x = vd->source.i_visible_width - y;
+                y = vd->source.i_visible_height - store;
+                break;
+            default:
+                break;
+        }
+
+        vout_display_SendEventMouseMoved (vd, x, y);
+    }
+}
+
 struct vout_display_owner_sys_t {
     vout_thread_t   *vout;
     bool            is_wrapper;  /* Is the current display a wrapper */
@@ -277,15 +327,14 @@ struct vout_display_owner_sys_t {
 
     /* */
     vout_display_cfg_t cfg;
-    unsigned     wm_state_initial;
     struct {
         unsigned num;
         unsigned den;
     } sar_initial;
 
     /* */
-    int  width_saved;
-    int  height_saved;
+    unsigned width_saved;
+    unsigned height_saved;
 
     struct {
         unsigned num;
@@ -298,13 +347,14 @@ struct vout_display_owner_sys_t {
 
     bool ch_zoom;
     struct {
-        int  num;
-        int  den;
+        unsigned num;
+        unsigned den;
     } zoom;
-
+#if defined(_WIN32) || defined(__OS2__)
     bool ch_wm_state;
     unsigned wm_state;
-
+    unsigned wm_state_initial;
+#endif
     bool ch_sar;
     struct {
         unsigned num;
@@ -313,10 +363,10 @@ struct vout_display_owner_sys_t {
 
     bool ch_crop;
     struct {
-        unsigned x;
-        unsigned y;
-        unsigned width;
-        unsigned height;
+        int      left;
+        int      top;
+        int      right;
+        int      bottom;
         unsigned num;
         unsigned den;
     } crop;
@@ -351,15 +401,14 @@ struct vout_display_owner_sys_t {
     bool ch_display_size;
     int  display_width;
     int  display_height;
-    bool display_is_fullscreen;
-    bool display_is_forced;
 
-#ifdef ALLOW_DUMMY_VOUT
-    vlc_mouse_t vout_mouse;
-#endif
-};
+    int  fit_window;
 
-static void DummyVoutSendDisplayEventMouse(vout_thread_t *, vlc_mouse_t *fallback, const vlc_mouse_t *m);
+    struct {
+        vlc_thread_t thread;
+        block_fifo_t *fifo;
+    } event;
+};
 
 static void VoutDisplayCreateRender(vout_display_t *vd)
 {
@@ -388,9 +437,14 @@ static void VoutDisplayCreateRender(vout_display_t *vd)
 
     msg_Dbg(vd, "A filter to adapt decoder to display is needed");
 
-    osys->filters = filter_chain_New(vd, "video filter2", false,
-                                     FilterAllocationInit,
-                                     FilterAllocationClean, vd);
+    filter_owner_t owner = {
+        .sys = vd,
+        .video = {
+            .buffer_new = VideoBufferNew,
+        },
+    };
+
+    osys->filters = filter_chain_NewVideo(vd, false, &owner);
     assert(osys->filters); /* TODO critical */
 
     /* */
@@ -398,25 +452,22 @@ static void VoutDisplayCreateRender(vout_display_t *vd)
     es_format_InitFromVideo(&src, &v_src);
 
     /* */
-    es_format_t dst;
-
     filter_t *filter;
     for (int i = 0; i < 1 + (v_dst_cmp.i_chroma != v_dst.i_chroma); i++) {
+        es_format_t dst;
 
         es_format_InitFromVideo(&dst, i == 0 ? &v_dst : &v_dst_cmp);
 
         filter_chain_Reset(osys->filters, &src, &dst);
         filter = filter_chain_AppendFilter(osys->filters,
                                            NULL, NULL, &src, &dst);
+        es_format_Clean(&dst);
         if (filter)
             break;
     }
+    es_format_Clean(&src);
     if (!filter)
-    {
-        msg_Err(vd, "VoutDisplayCreateRender FAILED");
-        /* TODO */
-        assert(0);
-    }
+        msg_Err(vd, "Failed to adapt decoder format to display");
 }
 
 static void VoutDisplayDestroyRender(vout_display_t *vd)
@@ -457,15 +508,12 @@ static void VoutDisplayEventMouse(vout_display_t *vd, int event, va_list args)
     case VOUT_DISPLAY_EVENT_MOUSE_MOVED: {
         const int x = (int)va_arg(args, int);
         const int y = (int)va_arg(args, int);
-        if (x != osys->mouse.state.i_x || y != osys->mouse.state.i_y) {
-            //msg_Dbg(vd, "VoutDisplayEvent 'mouse' @%d,%d", x, y);
 
-            m.i_x = x;
-            m.i_y = y;
-            m.b_double_click = false;
-        } else {
-            is_ignored = true;
-        }
+        //msg_Dbg(vd, "VoutDisplayEvent 'mouse' @%d,%d", x, y);
+
+        m.i_x = x;
+        m.i_y = y;
+        m.b_double_click = false;
         break;
     }
     case VOUT_DISPLAY_EVENT_MOUSE_PRESSED:
@@ -496,7 +544,7 @@ static void VoutDisplayEventMouse(vout_display_t *vd, int event, va_list args)
         m.b_double_click = true;
         break;
     default:
-        assert(0);
+        vlc_assert_unreachable();
     }
 
     if (is_ignored) {
@@ -525,16 +573,51 @@ static void VoutDisplayEventMouse(vout_display_t *vd, int event, va_list args)
     if (!vd->info.has_hide_mouse)
         osys->mouse.last_moved = mdate();
 
-    /* */
-    vlc_mutex_unlock(&osys->lock);
-
     /* */
     vout_SendEventMouseVisible(osys->vout);
-#ifdef ALLOW_DUMMY_VOUT
-    DummyVoutSendDisplayEventMouse(osys->vout, &osys->vout_mouse, &m);
-#else
     vout_SendDisplayEventMouse(osys->vout, &m);
-#endif
+    vlc_mutex_unlock(&osys->lock);
+}
+
+VLC_NORETURN
+static void *VoutDisplayEventKeyDispatch(void *data)
+{
+    vout_display_owner_sys_t *osys = data;
+
+    for (;;) {
+        block_t *event = block_FifoGet(osys->event.fifo);
+
+        int cancel = vlc_savecancel();
+
+        int key;
+        memcpy(&key, event->p_buffer, sizeof(key));
+        vout_SendEventKey(osys->vout, key);
+        block_Release(event);
+
+        vlc_restorecancel(cancel);
+    }
+}
+
+static void VoutDisplayEventKey(vout_display_t *vd, int key)
+{
+    vout_display_owner_sys_t *osys = vd->owner.sys;
+
+    if (!osys->event.fifo) {
+        osys->event.fifo = block_FifoNew();
+        if (!osys->event.fifo)
+            return;
+        if (vlc_clone(&osys->event.thread, VoutDisplayEventKeyDispatch,
+                      osys, VLC_THREAD_PRIORITY_LOW)) {
+            block_FifoRelease(osys->event.fifo);
+            osys->event.fifo = NULL;
+            return;
+        }
+    }
+    block_t *event = block_Alloc(sizeof(key));
+    if (event) {
+        memcpy(event->p_buffer, &key, sizeof(key));
+        block_FifoPut(osys->event.fifo, event);
+    }
 }
 
 static void VoutDisplayEvent(vout_display_t *vd, int event, va_list args)
@@ -550,7 +633,10 @@ static void VoutDisplayEvent(vout_display_t *vd, int event, va_list args)
     case VOUT_DISPLAY_EVENT_KEY: {
         const int key = (int)va_arg(args, int);
         msg_Dbg(vd, "VoutDisplayEvent 'key' 0x%2.2x", key);
-        vout_SendEventKey(osys->vout, key);
+        if (vd->info.has_event_thread)
+            vout_SendEventKey(osys->vout, key);
+        else
+            VoutDisplayEventKey(vd, key);
         break;
     }
     case VOUT_DISPLAY_EVENT_MOUSE_STATE:
@@ -574,7 +660,7 @@ static void VoutDisplayEvent(vout_display_t *vd, int event, va_list args)
         vlc_mutex_unlock(&osys->lock);
         break;
     }
-
+#if defined(_WIN32) || defined(__OS2__)
     case VOUT_DISPLAY_EVENT_WINDOW_STATE: {
         const unsigned state = va_arg(args, unsigned);
 
@@ -588,22 +674,18 @@ static void VoutDisplayEvent(vout_display_t *vd, int event, va_list args)
         vlc_mutex_unlock(&osys->lock);
         break;
     }
-
+#endif
     case VOUT_DISPLAY_EVENT_DISPLAY_SIZE: {
         const int width  = (int)va_arg(args, int);
         const int height = (int)va_arg(args, int);
-        const bool is_fullscreen = (bool)va_arg(args, int);
-        msg_Dbg(vd, "VoutDisplayEvent 'resize' %dx%d %s",
-                width, height, is_fullscreen ? "fullscreen" : "window");
+        msg_Dbg(vd, "VoutDisplayEvent 'resize' %dx%d", width, height);
 
         /* */
         vlc_mutex_lock(&osys->lock);
 
-        osys->ch_display_size       = true;
-        osys->display_width         = width;
-        osys->display_height        = height;
-        osys->display_is_fullscreen = is_fullscreen;
-        osys->display_is_forced     = false;
+        osys->ch_display_size   = true;
+        osys->display_width     = width;
+        osys->display_height    = height;
 
         vlc_mutex_unlock(&osys->lock);
         break;
@@ -627,20 +709,69 @@ static void VoutDisplayEvent(vout_display_t *vd, int event, va_list args)
     }
 }
 
-static vout_window_t *VoutDisplayNewWindow(vout_display_t *vd, const vout_window_cfg_t *cfg)
+static vout_window_t *VoutDisplayNewWindow(vout_display_t *vd, unsigned type)
 {
     vout_display_owner_sys_t *osys = vd->owner.sys;
-
-    return vout_NewDisplayWindow(osys->vout, vd, cfg);
+    vout_window_t *window = vout_NewDisplayWindow(osys->vout, type);
+    if (window != NULL)
+        vout_display_window_Attach(window, vd);
+    return window;
 }
+
 static void VoutDisplayDelWindow(vout_display_t *vd, vout_window_t *window)
 {
     vout_display_owner_sys_t *osys = vd->owner.sys;
 
-    vout_DeleteDisplayWindow(osys->vout, vd, window);
+    if (window != NULL)
+        vout_display_window_Detach(window);
+    vout_DeleteDisplayWindow(osys->vout, window);
+}
+
+static void VoutDisplayFitWindow(vout_display_t *vd, bool default_size)
+{
+    vout_display_owner_sys_t *osys = vd->owner.sys;
+    vout_display_cfg_t cfg = osys->cfg;
+
+    if (!cfg.is_display_filled)
+        return;
+
+    cfg.display.width = 0;
+    if (default_size) {
+        cfg.display.height = 0;
+    } else {
+        cfg.display.height = osys->height_saved;
+        cfg.zoom.num = 1;
+        cfg.zoom.den = 1;
+    }
+
+    unsigned display_width;
+    unsigned display_height;
+    vout_display_GetDefaultDisplaySize(&display_width, &display_height,
+                                       &vd->source, &cfg);
+    vout_SetDisplayWindowSize(osys->vout, display_width, display_height);
+}
+
+static void VoutDisplayCropRatio(int *left, int *top, int *right, int *bottom,
+                                 const video_format_t *source,
+                                 unsigned num, unsigned den)
+{
+    unsigned scaled_width  = (uint64_t)source->i_visible_height * num * source->i_sar_den / den / source->i_sar_num;
+    unsigned scaled_height = (uint64_t)source->i_visible_width  * den * source->i_sar_num / num / source->i_sar_den;
+
+    if (scaled_width < source->i_visible_width) {
+        *left   = (source->i_visible_width - scaled_width) / 2;
+        *top    = 0;
+        *right  = *left + scaled_width;
+        *bottom = *top  + source->i_visible_height;
+    } else {
+        *left   = 0;
+        *top    = (source->i_visible_height - scaled_height) / 2;
+        *right  = *left + source->i_visible_width;
+        *bottom = *top  + scaled_height;
+    }
 }
 
-void vout_ManageDisplay(vout_display_t *vd, bool allow_reset_pictures)
+bool vout_ManageDisplay(vout_display_t *vd, bool allow_reset_pictures)
 {
     vout_display_owner_sys_t *osys = vd->owner.sys;
 
@@ -663,7 +794,7 @@ void vout_ManageDisplay(vout_display_t *vd, bool allow_reset_pictures)
 
     if (hide_mouse) {
         if (!vd->info.has_hide_mouse) {
-            msg_Dbg(vd, "auto hidding mouse");
+            msg_Dbg(vd, "auto hiding mouse cursor");
             vout_display_Control(vd, VOUT_DISPLAY_HIDE_MOUSE);
         }
         vout_SendEventMouseHidden(osys->vout);
@@ -678,15 +809,15 @@ void vout_ManageDisplay(vout_display_t *vd, bool allow_reset_pictures)
         bool is_fullscreen  = osys->is_fullscreen;
         osys->ch_fullscreen = false;
 
+#if defined(_WIN32) || defined(__OS2__)
         bool ch_wm_state  = osys->ch_wm_state;
         unsigned wm_state  = osys->wm_state;
         osys->ch_wm_state = false;
+#endif
 
         bool ch_display_size       = osys->ch_display_size;
         int  display_width         = osys->display_width;
         int  display_height        = osys->display_height;
-        bool display_is_fullscreen = osys->display_is_fullscreen;
-        bool display_is_forced     = osys->display_is_forced;
         osys->ch_display_size = false;
 
         bool reset_pictures;
@@ -704,27 +835,34 @@ void vout_ManageDisplay(vout_display_t *vd, bool allow_reset_pictures)
             !reset_pictures &&
             !osys->ch_display_filled &&
             !osys->ch_zoom &&
+#if defined(_WIN32) || defined(__OS2__)
             !ch_wm_state &&
+#endif
             !osys->ch_sar &&
-            !osys->ch_crop)
+            !osys->ch_crop) {
+
+            if (!osys->cfg.is_fullscreen && osys->fit_window != 0) {
+                VoutDisplayFitWindow(vd, osys->fit_window == -1);
+                osys->fit_window = 0;
+                continue;
+            }
             break;
+        }
 
         /* */
         if (ch_fullscreen) {
-            vout_display_cfg_t cfg = osys->cfg;
+            if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_FULLSCREEN,
+                                     is_fullscreen) == VLC_SUCCESS) {
+                osys->cfg.is_fullscreen = is_fullscreen;
 
-            cfg.is_fullscreen  = is_fullscreen;
-            cfg.display.width  = cfg.is_fullscreen ? 0 : osys->width_saved;
-            cfg.display.height = cfg.is_fullscreen ? 0 : osys->height_saved;
+                if (!is_fullscreen)
+                    vout_SetDisplayWindowSize(osys->vout, osys->width_saved,
+                                              osys->height_saved);
+            } else {
+                is_fullscreen = osys->cfg.is_fullscreen;
 
-            if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_FULLSCREEN, &cfg)) {
                 msg_Err(vd, "Failed to set fullscreen");
-                is_fullscreen = osys->cfg.is_fullscreen;
             }
-            osys->cfg.is_fullscreen = is_fullscreen;
-
-            /* */
-            vout_SendEventFullscreen(osys->vout, osys->cfg.is_fullscreen);
         }
 
         /* */
@@ -733,22 +871,13 @@ void vout_ManageDisplay(vout_display_t *vd, bool allow_reset_pictures)
             cfg.display.width  = display_width;
             cfg.display.height = display_height;
 
-            if (!cfg.is_fullscreen != !display_is_fullscreen ||
-                vout_display_Control(vd, VOUT_DISPLAY_CHANGE_DISPLAY_SIZE, &cfg, display_is_forced)) {
-                if (!cfg.is_fullscreen == !display_is_fullscreen)
-                    msg_Err(vd, "Failed to resize display");
+            osys->width_saved  = osys->cfg.display.width;
+            osys->height_saved = osys->cfg.display.height;
+
+            vout_display_Control(vd, VOUT_DISPLAY_CHANGE_DISPLAY_SIZE, &cfg);
 
-                /* We ignore the resized */
-                display_width  = osys->cfg.display.width;
-                display_height = osys->cfg.display.height;
-            }
             osys->cfg.display.width  = display_width;
             osys->cfg.display.height = display_height;
-
-            if (!display_is_fullscreen) {
-                osys->width_saved  = display_width;
-                osys->height_saved = display_height;
-            }
         }
         /* */
         if (osys->ch_display_filled) {
@@ -762,8 +891,6 @@ void vout_ManageDisplay(vout_display_t *vd, bool allow_reset_pictures)
             }
             osys->cfg.is_display_filled = osys->is_display_filled;
             osys->ch_display_filled = false;
-
-            vout_SendEventDisplayFilled(osys->vout, osys->cfg.is_display_filled);
         }
         /* */
         if (osys->ch_zoom) {
@@ -784,27 +911,15 @@ void vout_ManageDisplay(vout_display_t *vd, bool allow_reset_pictures)
                 msg_Err(vd, "Failed to change zoom");
                 osys->zoom.num = osys->cfg.zoom.num;
                 osys->zoom.den = osys->cfg.zoom.den;
-            } else if (cfg.is_display_filled) {
-                const int display_width  = (int64_t)vd->source.i_width  * osys->zoom.num / osys->zoom.den;
-                const int display_height = (int64_t)vd->source.i_height * osys->zoom.num / osys->zoom.den;
-
-                vlc_mutex_lock(&osys->lock);
-
-                osys->ch_display_size       = true;
-                osys->display_width         = display_width;
-                osys->display_height        = display_height;
-                osys->display_is_fullscreen = osys->cfg.is_fullscreen;
-                osys->display_is_forced     = true;
-
-                vlc_mutex_unlock(&osys->lock);
+            } else {
+                osys->fit_window = -1;
             }
 
             osys->cfg.zoom.num = osys->zoom.num;
             osys->cfg.zoom.den = osys->zoom.den;
             osys->ch_zoom = false;
-
-            vout_SendEventZoom(osys->vout, osys->cfg.zoom.num, osys->cfg.zoom.den);
         }
+#if defined(_WIN32) || defined(__OS2__)
         /* */
         if (ch_wm_state) {
             if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_WINDOW_STATE, wm_state)) {
@@ -812,10 +927,8 @@ void vout_ManageDisplay(vout_display_t *vd, bool allow_reset_pictures)
                 wm_state = osys->wm_state;
             }
             osys->wm_state_initial = wm_state;
-
-            /* */
-            vout_SendEventOnTop(osys->vout, osys->wm_state_initial);
         }
+#endif
         /* */
         if (osys->ch_sar) {
             video_format_t source = vd->source;
@@ -836,51 +949,58 @@ void vout_ManageDisplay(vout_display_t *vd, bool allow_reset_pictures)
                  */
                 msg_Err(vd, "Failed to change source AR");
                 source = vd->source;
+            } else if (!osys->fit_window) {
+                osys->fit_window = 1;
             }
             vd->source = source;
             osys->sar.num = source.i_sar_num;
             osys->sar.den = source.i_sar_den;
             osys->ch_sar  = false;
 
-            /* */
-            if (osys->sar.num == osys->source.i_sar_num &&
-                osys->sar.den == osys->source.i_sar_den)
-            {
-                vout_SendEventSourceAspect(osys->vout, 0, 0);
-            }
-            else
-            {
-                unsigned dar_num, dar_den;
-                vlc_ureduce( &dar_num, &dar_den,
-                             osys->sar.num * vd->source.i_visible_width,
-                             osys->sar.den * vd->source.i_visible_height,
-                             65536);
-                vout_SendEventSourceAspect(osys->vout, dar_num, dar_den);
-            }
+            /* If a crop ratio is requested, recompute the parameters */
+            if (osys->crop.num > 0 && osys->crop.den > 0)
+                osys->ch_crop = true;
         }
         /* */
         if (osys->ch_crop) {
             video_format_t source = vd->source;
+
             unsigned crop_num = osys->crop.num;
             unsigned crop_den = osys->crop.den;
-
-            source.i_x_offset       = osys->crop.x;
-            source.i_y_offset       = osys->crop.y;
-            source.i_visible_width  = osys->crop.width;
-            source.i_visible_height = osys->crop.height;
-
-            /* */
-            const bool is_valid = source.i_x_offset < source.i_width &&
-                                  source.i_y_offset < source.i_height &&
-                                  source.i_x_offset + source.i_visible_width  <= source.i_width &&
-                                  source.i_y_offset + source.i_visible_height <= source.i_height &&
-                                  source.i_visible_width > 0 && source.i_visible_height > 0;
-
-            if (!is_valid || vout_display_Control(vd, VOUT_DISPLAY_CHANGE_SOURCE_CROP, &source)) {
-                if (is_valid)
-                    msg_Err(vd, "Failed to change source crop TODO implement crop at core");
-                else
-                    msg_Err(vd, "Invalid crop requested");
+            if (crop_num > 0 && crop_den > 0) {
+                video_format_t fmt = osys->source;
+                fmt.i_sar_num = source.i_sar_num;
+                fmt.i_sar_den = source.i_sar_den;
+                VoutDisplayCropRatio(&osys->crop.left,  &osys->crop.top,
+                                     &osys->crop.right, &osys->crop.bottom,
+                                     &fmt, crop_num, crop_den);
+            }
+            const int right_max  = osys->source.i_x_offset + osys->source.i_visible_width;
+            const int bottom_max = osys->source.i_y_offset + osys->source.i_visible_height;
+            int left   = VLC_CLIP((int)osys->source.i_x_offset + osys->crop.left,
+                                0, right_max - 1);
+            int top    = VLC_CLIP((int)osys->source.i_y_offset + osys->crop.top,
+                                0, bottom_max - 1);
+            int right, bottom;
+            if (osys->crop.right <= 0)
+                right = (int)(osys->source.i_x_offset + osys->source.i_visible_width) + osys->crop.right;
+            else
+                right = (int)osys->source.i_x_offset + osys->crop.right;
+            right = VLC_CLIP(right, left + 1, right_max);
+            if (osys->crop.bottom <= 0)
+                bottom = (int)(osys->source.i_y_offset + osys->source.i_visible_height) + osys->crop.bottom;
+            else
+                bottom = (int)osys->source.i_y_offset + osys->crop.bottom;
+            bottom = VLC_CLIP(bottom, top + 1, bottom_max);
+
+            source.i_x_offset       = left;
+            source.i_y_offset       = top;
+            source.i_visible_width  = right - left;
+            source.i_visible_height = bottom - top;
+            video_format_Print(VLC_OBJECT(vd), "SOURCE ", &osys->source);
+            video_format_Print(VLC_OBJECT(vd), "CROPPED", &source);
+            if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_SOURCE_CROP, &source)) {
+                msg_Err(vd, "Failed to change source crop TODO implement crop at core");
 
                 source = vd->source;
                 crop_num = osys->crop_saved.num;
@@ -888,24 +1008,20 @@ void vout_ManageDisplay(vout_display_t *vd, bool allow_reset_pictures)
                 /* FIXME implement cropping in the core if not supported by the
                  * vout module (easy)
                  */
+            } else if (!osys->fit_window) {
+                osys->fit_window = 1;
             }
             vd->source = source;
-            osys->crop.x      = source.i_x_offset;
-            osys->crop.y      = source.i_y_offset;
-            osys->crop.width  = source.i_visible_width;
-            osys->crop.height = source.i_visible_height;
+            osys->crop.left   = source.i_x_offset - osys->source.i_x_offset;
+            osys->crop.top    = source.i_y_offset - osys->source.i_y_offset;
+            /* FIXME for right/bottom we should keep the 'type' border vs window */
+            osys->crop.right  = (source.i_x_offset + source.i_visible_width) -
+                                (osys->source.i_x_offset + osys->source.i_visible_width);
+            osys->crop.bottom = (source.i_y_offset + source.i_visible_height) -
+                                (osys->source.i_y_offset + osys->source.i_visible_height);
             osys->crop.num    = crop_num;
             osys->crop.den    = crop_den;
             osys->ch_crop = false;
-
-            /* TODO fix when a ratio is used (complicated). */
-            const unsigned left   = osys->crop.x - osys->source.i_x_offset;
-            const unsigned top    = osys->crop.y - osys->source.i_y_offset;
-            const unsigned right  = osys->source.i_visible_width  - (osys->crop.width  + osys->crop.x);
-            const unsigned bottom = osys->source.i_visible_height - (osys->crop.height + osys->crop.y);
-            vout_SendEventSourceCrop(osys->vout,
-                                     osys->crop.num, osys->crop.den,
-                                     left, top, right, bottom);
         }
 
         /* */
@@ -919,6 +1035,8 @@ void vout_ManageDisplay(vout_display_t *vd, bool allow_reset_pictures)
     }
     if (reset_render)
         VoutDisplayResetRender(vd);
+
+    return reset_render;
 }
 
 bool vout_AreDisplayPicturesInvalid(vout_display_t *vd)
@@ -944,9 +1062,43 @@ picture_t *vout_FilterDisplay(vout_display_t *vd, picture_t *picture)
     vout_display_owner_sys_t *osys = vd->owner.sys;
 
     assert(osys->filters);
+    if (filter_chain_GetLength(osys->filters) <= 0) {
+        picture_Release(picture);
+        return NULL;
+    }
     return filter_chain_VideoFilter(osys->filters, picture);
 }
 
+void vout_UpdateDisplaySourceProperties(vout_display_t *vd, const video_format_t *source)
+{
+    vout_display_owner_sys_t *osys = vd->owner.sys;
+
+    if (source->i_sar_num * osys->source.i_sar_den !=
+        source->i_sar_den * osys->source.i_sar_num) {
+
+        osys->source.i_sar_num = source->i_sar_num;
+        osys->source.i_sar_den = source->i_sar_den;
+        vlc_ureduce(&osys->source.i_sar_num, &osys->source.i_sar_den,
+                    osys->source.i_sar_num, osys->source.i_sar_den, 0);
+
+        /* FIXME it will override any AR that the user would have forced */
+        osys->ch_sar = true;
+        osys->sar.num = osys->source.i_sar_num;
+        osys->sar.den = osys->source.i_sar_den;
+    }
+    if (source->i_x_offset       != osys->source.i_x_offset ||
+        source->i_y_offset       != osys->source.i_y_offset ||
+        source->i_visible_width  != osys->source.i_visible_width ||
+        source->i_visible_height != osys->source.i_visible_height) {
+
+        video_format_CopyCrop(&osys->source, source);
+
+        /* Force the vout to reapply the current user crop settings over the new decoder
+         * crop settings. */
+        osys->ch_crop = true;
+    }
+}
+
 void vout_SetDisplayFullscreen(vout_display_t *vd, bool is_fullscreen)
 {
     vout_display_owner_sys_t *osys = vd->owner.sys;
@@ -969,10 +1121,17 @@ void vout_SetDisplayFilled(vout_display_t *vd, bool is_filled)
     }
 }
 
-void vout_SetDisplayZoom(vout_display_t *vd, int num, int den)
+void vout_SetDisplayZoom(vout_display_t *vd, unsigned num, unsigned den)
 {
     vout_display_owner_sys_t *osys = vd->owner.sys;
 
+    if (num > 0 && den > 0) {
+        vlc_ureduce(&num, &den, num, den, 0);
+    } else {
+        num = 1;
+        den = 1;
+    }
+
     if (osys->is_display_filled ||
         osys->zoom.num != num || osys->zoom.den != den) {
         osys->ch_zoom = true;
@@ -981,21 +1140,19 @@ void vout_SetDisplayZoom(vout_display_t *vd, int num, int den)
     }
 }
 
-void vout_SetWindowState(vout_display_t *vd, unsigned state)
+void vout_SetDisplayAspect(vout_display_t *vd, unsigned dar_num, unsigned dar_den)
 {
     vout_display_owner_sys_t *osys = vd->owner.sys;
 
-    vlc_mutex_lock(&osys->lock);
-    if (osys->wm_state != state) {
-        osys->ch_wm_state = true;
-        osys->wm_state = state;
+    unsigned sar_num, sar_den;
+    if (dar_num > 0 && dar_den > 0) {
+        sar_num = dar_num * osys->source.i_visible_height;
+        sar_den = dar_den * osys->source.i_visible_width;
+        vlc_ureduce(&sar_num, &sar_den, sar_num, sar_den, 0);
+    } else {
+        sar_num = 0;
+        sar_den = 0;
     }
-    vlc_mutex_unlock(&osys->lock);
-}
-
-void vout_SetDisplayAspect(vout_display_t *vd, unsigned sar_num, unsigned sar_den)
-{
-    vout_display_owner_sys_t *osys = vd->owner.sys;
 
     if (osys->sar.num != sar_num || osys->sar.den != sar_den) {
         osys->ch_sar = true;
@@ -1005,33 +1162,28 @@ void vout_SetDisplayAspect(vout_display_t *vd, unsigned sar_num, unsigned sar_de
 }
 void vout_SetDisplayCrop(vout_display_t *vd,
                          unsigned crop_num, unsigned crop_den,
-                         unsigned x, unsigned y, unsigned width, unsigned height)
+                         unsigned left, unsigned top, int right, int bottom)
 {
     vout_display_owner_sys_t *osys = vd->owner.sys;
 
-    if (osys->crop.x != x || osys->crop.y != y ||
-        osys->crop.width  != width || osys->crop.height != height) {
+    if (osys->crop.left  != (int)left  || osys->crop.top != (int)top ||
+        osys->crop.right != right || osys->crop.bottom != bottom ||
+        (crop_num > 0 && crop_den > 0 &&
+         (crop_num != osys->crop.num || crop_den != osys->crop.den))) {
 
-        osys->crop.x      = x;
-        osys->crop.y      = y;
-        osys->crop.width  = width;
-        osys->crop.height = height;
+        osys->crop.left   = left;
+        osys->crop.top    = top;
+        osys->crop.right  = right;
+        osys->crop.bottom = bottom;
         osys->crop.num    = crop_num;
         osys->crop.den    = crop_den;
 
         osys->ch_crop = true;
     }
 }
-vout_opengl_t *vout_GetDisplayOpengl(vout_display_t *vd)
-{
-    vout_opengl_t *gl;
-    if (vout_display_Control(vd, VOUT_DISPLAY_GET_OPENGL, &gl))
-        return NULL;
-    return gl;
-}
 
 static vout_display_t *DisplayNew(vout_thread_t *vout,
-                                  const video_format_t *source_org,
+                                  const video_format_t *source,
                                   const vout_display_state_t *state,
                                   const char *module,
                                   bool is_wrapper, vout_display_t *wrapper,
@@ -1044,11 +1196,10 @@ static vout_display_t *DisplayNew(vout_thread_t *vout,
     vout_display_cfg_t *cfg = &osys->cfg;
 
     *cfg = state->cfg;
-    osys->wm_state_initial = VOUT_WINDOW_STATE_NORMAL;
     osys->sar_initial.num = state->sar.num;
     osys->sar_initial.den = state->sar.den;
     vout_display_GetDefaultDisplaySize(&cfg->display.width, &cfg->display.height,
-                                       source_org, cfg);
+                                       source, cfg);
 
     osys->vout = vout;
     osys->is_wrapper = is_wrapper;
@@ -1061,38 +1212,43 @@ static vout_display_t *DisplayNew(vout_thread_t *vout,
     osys->mouse.double_click_timeout = double_click_timeout;
     osys->mouse.hide_timeout = hide_timeout;
     osys->is_fullscreen  = cfg->is_fullscreen;
-    osys->width_saved    =
     osys->display_width  = cfg->display.width;
-    osys->height_saved   =
     osys->display_height = cfg->display.height;
     osys->is_display_filled = cfg->is_display_filled;
+    osys->width_saved    = cfg->display.width;
+    osys->height_saved   = cfg->display.height;
+    if (osys->is_fullscreen) {
+        vout_display_cfg_t cfg_windowed = *cfg;
+        cfg_windowed.is_fullscreen  = false;
+        cfg_windowed.display.width  = 0;
+        cfg_windowed.display.height = 0;
+        vout_display_GetDefaultDisplaySize(&osys->width_saved,
+                                           &osys->height_saved,
+                                           source, &cfg_windowed);
+    }
+
     osys->zoom.num = cfg->zoom.num;
     osys->zoom.den = cfg->zoom.den;
-    osys->wm_state = state->is_on_top ? VOUT_WINDOW_STATE_ABOVE
-                                      : VOUT_WINDOW_STATE_NORMAL;
-
-    osys->source = *source_org;
-
-    video_format_t source = *source_org;
-
-    source.i_x_offset =
-    osys->crop.x  = 0;
-    source.i_y_offset =
-    osys->crop.y  = 0;
-    source.i_visible_width  =
-    osys->crop.width    = source.i_width;
-    source.i_visible_height =
-    osys->crop.height   = source.i_height;
+#if defined(_WIN32) || defined(__OS2__)
+    osys->wm_state_initial = VOUT_WINDOW_STATE_NORMAL;
+    osys->wm_state = state->wm_state;
+    osys->ch_wm_state = true;
+#endif
+    osys->fit_window = 0;
+    osys->event.fifo = NULL;
+
+    osys->source = *source;
+    osys->crop.left   = 0;
+    osys->crop.top    = 0;
+    osys->crop.right  = 0;
+    osys->crop.bottom = 0;
     osys->crop_saved.num = 0;
     osys->crop_saved.den = 0;
     osys->crop.num = 0;
     osys->crop.den = 0;
 
-    osys->sar.num = osys->sar_initial.num ? osys->sar_initial.num : source.i_sar_num;
-    osys->sar.den = osys->sar_initial.den ? osys->sar_initial.den : source.i_sar_den;
-#ifdef ALLOW_DUMMY_VOUT
-    vlc_mouse_Init(&osys->vout_mouse);
-#endif
+    osys->sar.num = osys->sar_initial.num ? osys->sar_initial.num : source->i_sar_num;
+    osys->sar.den = osys->sar_initial.den ? osys->sar_initial.den : source->i_sar_den;
 
     vout_display_owner_t owner;
     if (owner_ptr) {
@@ -1104,10 +1260,9 @@ static vout_display_t *DisplayNew(vout_thread_t *vout,
     }
     owner.sys = osys;
 
-    /* */
     vout_display_t *p_display = vout_display_New(VLC_OBJECT(vout),
                                                  module, !is_wrapper,
-                                                 &source, cfg, &owner);
+                                                 source, cfg, &owner);
     if (!p_display) {
         free(osys);
         return NULL;
@@ -1116,16 +1271,9 @@ static vout_display_t *DisplayNew(vout_thread_t *vout,
     VoutDisplayCreateRender(p_display);
 
     /* Setup delayed request */
-    if (osys->sar.num != source_org->i_sar_num ||
-        osys->sar.den != source_org->i_sar_den)
+    if (osys->sar.num != source->i_sar_num ||
+        osys->sar.den != source->i_sar_den)
         osys->ch_sar = true;
-    if (osys->wm_state != osys->wm_state_initial)
-        osys->ch_wm_state = true;
-    if (osys->crop.x      != source_org->i_x_offset ||
-        osys->crop.y      != source_org->i_y_offset ||
-        osys->crop.width  != source_org->i_visible_width ||
-        osys->crop.height != source_org->i_visible_height)
-        osys->ch_crop = true;
 
     return p_display;
 }
@@ -1137,15 +1285,22 @@ void vout_DeleteDisplay(vout_display_t *vd, vout_display_state_t *state)
     if (state) {
         if (!osys->is_wrapper )
             state->cfg = osys->cfg;
-        state->is_on_top = (osys->wm_state & VOUT_WINDOW_STATE_ABOVE) != 0;
-        state->sar.num   = osys->sar_initial.num;
-        state->sar.den   = osys->sar_initial.den;
+#if defined(_WIN32) || defined(__OS2__)
+        state->wm_state = osys->wm_state;
+#endif
+        state->sar.num  = osys->sar_initial.num;
+        state->sar.den  = osys->sar_initial.den;
     }
 
     VoutDisplayDestroyRender(vd);
     if (osys->is_wrapper)
         SplitterClose(vd);
     vout_display_Delete(vd);
+    if (osys->event.fifo) {
+        vlc_cancel(osys->event.thread);
+        vlc_join(osys->event.thread, NULL);
+        block_FifoRelease(osys->event.fifo);
+    }
     vlc_mutex_destroy(&osys->lock);
     free(osys);
 }
@@ -1164,17 +1319,11 @@ vout_display_t *vout_NewDisplay(vout_thread_t *vout,
                       double_click_timeout, hide_timeout, NULL);
 }
 
-static void SplitterClose(vout_display_t *vd)
-{
-    VLC_UNUSED(vd);
-    assert(0);
-}
-
-#if 0
 /*****************************************************************************
  *
  *****************************************************************************/
 struct vout_display_sys_t {
+    picture_pool_t   *pool;
     video_splitter_t *splitter;
 
     /* */
@@ -1186,26 +1335,35 @@ struct video_splitter_owner_t {
     vout_display_t *wrapper;
 };
 
-static vout_window_t *SplitterNewWindow(vout_display_t *vd, const vout_window_cfg_t *cfg_ptr)
+static vout_window_t *SplitterNewWindow(vout_display_t *vd, unsigned type)
 {
     vout_display_owner_sys_t *osys = vd->owner.sys;
-
-    vout_window_cfg_t cfg = *cfg_ptr;
-    cfg.is_standalone = true;
-    cfg.x += 0;//output->window.i_x; FIXME
-    cfg.y += 0;//output->window.i_y;
-
-    return vout_NewDisplayWindow(osys->vout, vd, &cfg);
+    vout_window_t *window;
+    vout_window_cfg_t cfg = {
+        .type = type,
+        .width = vd->cfg->display.width,
+        .height = vd->cfg->display.height,
+        .is_standalone = true,
+    };
+
+    window = vout_display_window_New(osys->vout, &cfg);
+    if (window != NULL)
+        vout_display_window_Attach(window, vd);
+    return window;
 }
+
 static void SplitterDelWindow(vout_display_t *vd, vout_window_t *window)
 {
-    vout_display_owner_sys_t *osys = vd->owner.sys;
-
-    vout_DeleteDisplayWindow(osys->vout, vd, window);
+    if (window != NULL) {
+        vout_display_window_Detach(window);
+        vout_display_window_Delete(window);
+    }
+    (void) vd;
 }
+
 static void SplitterEvent(vout_display_t *vd, int event, va_list args)
 {
-    vout_display_owner_sys_t *osys = vd->owner.sys;
+    //vout_display_owner_sys_t *osys = vd->owner.sys;
 
     switch (event) {
 #if 0
@@ -1226,21 +1384,26 @@ static void SplitterEvent(vout_display_t *vd, int event, va_list args)
         break;
 
     default:
-        msg_Err(vd, "SplitterEvent TODO");
+        msg_Err(vd, "splitter event not implemented: %d", event);
         break;
     }
 }
 
-static picture_t *SplitterGet(vout_display_t *vd)
+static picture_pool_t *SplitterPool(vout_display_t *vd, unsigned count)
 {
-    /* TODO pool ? */
-    return picture_NewFromFormat(&vd->fmt);
+    vout_display_sys_t *sys = vd->sys;
+    if (!sys->pool)
+        sys->pool = picture_pool_NewFromFormat(&vd->fmt, count);
+    return sys->pool;
 }
-static void SplitterPrepare(vout_display_t *vd, picture_t *picture)
+static void SplitterPrepare(vout_display_t *vd,
+                            picture_t *picture,
+                            subpicture_t *subpicture)
 {
     vout_display_sys_t *sys = vd->sys;
 
     picture_Hold(picture);
+    assert(!subpicture);
 
     if (video_splitter_Filter(sys->splitter, sys->picture, picture)) {
         for (int i = 0; i < sys->count; i++)
@@ -1250,41 +1413,28 @@ static void SplitterPrepare(vout_display_t *vd, picture_t *picture)
     }
 
     for (int i = 0; i < sys->count; i++) {
-        /* */
-        /* FIXME now vout_FilterDisplay already return a direct buffer FIXME */
-        sys->picture[i] = vout_FilterDisplay(sys->display[i], sys->picture[i]);
-        if (!sys->picture[i])
-            continue;
-
-        /* */
-        picture_t *direct = vout_display_Get(sys->display[i]);
-        if (!direct) {
-            msg_Err(vd, "Failed to get a direct buffer");
-            picture_Release(sys->picture[i]);
-            sys->picture[i] = NULL;
-            continue;
-        }
-
-        /* FIXME not always needed (easy when there is a osys->filters) */
-        picture_Copy(direct, sys->picture[i]);
-        picture_Release(sys->picture[i]);
-        sys->picture[i] = direct;
-
-        vout_display_Prepare(sys->display[i], sys->picture[i]);
+        if (vout_IsDisplayFiltered(sys->display[i]))
+            sys->picture[i] = vout_FilterDisplay(sys->display[i], sys->picture[i]);
+        if (sys->picture[i])
+            vout_display_Prepare(sys->display[i], sys->picture[i], NULL);
     }
 }
-static void SplitterDisplay(vout_display_t *vd, picture_t *picture)
+static void SplitterDisplay(vout_display_t *vd,
+                            picture_t *picture,
+                            subpicture_t *subpicture)
 {
     vout_display_sys_t *sys = vd->sys;
 
+    assert(!subpicture);
     for (int i = 0; i < sys->count; i++) {
         if (sys->picture[i])
-            vout_display_Display(sys->display[i], sys->picture[i]);
+            vout_display_Display(sys->display[i], sys->picture[i], NULL);
     }
     picture_Release(picture);
 }
 static int SplitterControl(vout_display_t *vd, int query, va_list args)
 {
+    (void)vd; (void)query; (void)args;
     return VLC_EGENERIC;
 }
 static void SplitterManage(vout_display_t *vd)
@@ -1292,7 +1442,7 @@ static void SplitterManage(vout_display_t *vd)
     vout_display_sys_t *sys = vd->sys;
 
     for (int i = 0; i < sys->count; i++)
-        vout_ManageDisplay(sys->display[i]);
+        vout_ManageDisplay(sys->display[i], true);
 }
 
 static int SplitterPictureNew(video_splitter_t *splitter, picture_t *picture[])
@@ -1300,8 +1450,13 @@ static int SplitterPictureNew(video_splitter_t *splitter, picture_t *picture[])
     vout_display_sys_t *wsys = splitter->p_owner->wrapper->sys;
 
     for (int i = 0; i < wsys->count; i++) {
-        /* TODO pool ? */
-        picture[i] = picture_NewFromFormat(&wsys->display[i]->source);
+        if (vout_IsDisplayFiltered(wsys->display[i])) {
+            /* TODO use a pool ? */
+            picture[i] = picture_NewFromFormat(&wsys->display[i]->source);
+        } else {
+            picture_pool_t *pool = vout_display_Pool(wsys->display[i], 1);
+            picture[i] = pool ? picture_pool_Get(pool) : NULL;
+        }
         if (!picture[i]) {
             for (int j = 0; j < i; j++)
                 picture_Release(picture[j]);
@@ -1326,6 +1481,9 @@ static void SplitterClose(vout_display_t *vd)
     free(splitter->p_owner);
     video_splitter_Delete(splitter);
 
+    if (sys->pool)
+        picture_pool_Release(sys->pool);
+
     /* */
     for (int i = 0; i < sys->count; i++)
         vout_DeleteDisplay(sys->display[i], NULL);
@@ -1363,8 +1521,9 @@ vout_display_t *vout_NewSplitter(vout_thread_t *vout,
     if (!sys->picture )
         abort();
     sys->splitter = splitter;
+    sys->pool     = NULL;
 
-    wrapper->get     = SplitterGet;
+    wrapper->pool    = SplitterPool;
     wrapper->prepare = SplitterPrepare;
     wrapper->display = SplitterDisplay;
     wrapper->control = SplitterControl;
@@ -1372,23 +1531,20 @@ vout_display_t *vout_NewSplitter(vout_thread_t *vout,
     wrapper->sys     = sys;
 
     /* */
-    video_splitter_owner_t *owner = malloc(sizeof(*owner));
-    if (!owner)
-        abort();
-    owner->wrapper = wrapper;
-    splitter->p_owner = owner;
+    video_splitter_owner_t *vso = xmalloc(sizeof(*vso));
+    vso->wrapper = wrapper;
+    splitter->p_owner = vso;
     splitter->pf_picture_new = SplitterPictureNew;
     splitter->pf_picture_del = SplitterPictureDel;
 
     /* */
     TAB_INIT(sys->count, sys->display);
     for (int i = 0; i < splitter->i_output; i++) {
-        vout_display_owner_t owner;
-
-        owner.event      = SplitterEvent;
-        owner.window_new = SplitterNewWindow;
-        owner.window_del = SplitterDelWindow;
-
+        vout_display_owner_t vdo = {
+            .event      = SplitterEvent,
+            .window_new = SplitterNewWindow,
+            .window_del = SplitterDelWindow,
+        };
         const video_splitter_output_t *output = &splitter->p_output[i];
         vout_display_state_t ostate;
 
@@ -1402,9 +1558,9 @@ vout_display_t *vout_NewSplitter(vout_thread_t *vout,
         ostate.cfg.zoom.den = 1;
 
         vout_display_t *vd = DisplayNew(vout, &output->fmt, &ostate,
-                                           output->psz_module ? output->psz_module : module,
-                                           false, wrapper,
-                                           double_click_timeout, hide_timeout, &owner);
+                                        output->psz_module ? output->psz_module : module,
+                                        false, wrapper,
+                                        double_click_timeout, hide_timeout, &vdo);
         if (!vd) {
             vout_DeleteDisplay(wrapper, NULL);
             return NULL;
@@ -1414,7 +1570,6 @@ vout_display_t *vout_NewSplitter(vout_thread_t *vout,
 
     return wrapper;
 }
-#endif
 
 /*****************************************************************************
  * TODO move out
@@ -1422,20 +1577,26 @@ vout_display_t *vout_NewSplitter(vout_thread_t *vout,
 #include "vout_internal.h"
 void vout_SendDisplayEventMouse(vout_thread_t *vout, const vlc_mouse_t *m)
 {
+    vlc_mouse_t tmp1, tmp2;
+
+    /* The check on spu is needed as long as ALLOW_DUMMY_VOUT is defined */
+    if (vout->p->spu && spu_ProcessMouse( vout->p->spu, m, &vout->p->display.vd->source))
+        return;
+
+    vlc_mutex_lock( &vout->p->filter.lock );
+    if (vout->p->filter.chain_static && vout->p->filter.chain_interactive) {
+        if (!filter_chain_MouseFilter(vout->p->filter.chain_interactive, &tmp1, m))
+            m = &tmp1;
+        if (!filter_chain_MouseFilter(vout->p->filter.chain_static,      &tmp2, m))
+            m = &tmp2;
+    }
+    vlc_mutex_unlock( &vout->p->filter.lock );
+
     if (vlc_mouse_HasMoved(&vout->p->mouse, m)) {
         vout_SendEventMouseMoved(vout, m->i_x, m->i_y);
     }
     if (vlc_mouse_HasButton(&vout->p->mouse, m)) {
-        static const int buttons[] = {
-            MOUSE_BUTTON_LEFT,
-            MOUSE_BUTTON_CENTER,
-            MOUSE_BUTTON_RIGHT,
-            MOUSE_BUTTON_WHEEL_UP,
-            MOUSE_BUTTON_WHEEL_DOWN,
-            -1
-        };
-        for (int i = 0; buttons[i] >= 0; i++) {
-            const int button = buttons[i];
+        for (unsigned button = 0; button < MOUSE_BUTTON_MAX; button++) {
             if (vlc_mouse_HasPressed(&vout->p->mouse, m, button))
                 vout_SendEventMousePressed(vout, button);
             else if (vlc_mouse_HasReleased(&vout->p->mouse, m, button))
@@ -1446,36 +1607,3 @@ void vout_SendDisplayEventMouse(vout_thread_t *vout, const vlc_mouse_t *m)
         vout_SendEventMouseDoubleClick(vout);
     vout->p->mouse = *m;
 }
-#ifdef ALLOW_DUMMY_VOUT
-static void DummyVoutSendDisplayEventMouse(vout_thread_t *vout, vlc_mouse_t *fallback, const vlc_mouse_t *m)
-{
-    vout_thread_sys_t p;
-
-    if (!vout->p) {
-        p.mouse = *fallback;
-        vout->p = &p;
-    }
-    vout_SendDisplayEventMouse(vout, m);
-    if (vout->p == &p) {
-        *fallback = p.mouse;
-        vout->p = NULL;
-    }
-}
-#endif
-vout_window_t * vout_NewDisplayWindow(vout_thread_t *vout, vout_display_t *vd, const vout_window_cfg_t *cfg)
-{
-    VLC_UNUSED(vd);
-    vout_window_cfg_t cfg_override = *cfg;
-
-    if( !var_InheritBool( vout, "embedded-video" ) )
-        cfg_override.is_standalone = true;
-
-    return vout_window_New(VLC_OBJECT(vout), NULL, &cfg_override);
-}
-void vout_DeleteDisplayWindow(vout_thread_t *vout, vout_display_t *vd, vout_window_t *window)
-{
-    VLC_UNUSED(vout);
-    VLC_UNUSED(vd);
-    vout_window_Delete(window);
-}
-