]> git.sesse.net Git - vlc/blobdiff - src/video_output/display.c
contrib: uncompress directx and dshow headers to the right place
[vlc] / src / video_output / display.c
index 4774ba91906de850c03ec5f124c6624d2c3ba58e..fe524d33a149d91b386763f69ab66e0cbd49bf0f 100644 (file)
 
 #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);
 
 /*****************************************************************************
@@ -47,14 +51,17 @@ static void SplitterClose(vout_display_t *vd);
  *****************************************************************************/
 static picture_t *VideoBufferNew(filter_t *filter)
 {
+    vout_display_t *vd = (vout_display_t*)filter->p_owner;
     const video_format_t *fmt = &filter->fmt_out.video;
 
-    picture_t *picture = picture_New(fmt->i_chroma,
-                                     fmt->i_width, fmt->i_height,
-                                     fmt->i_aspect);
-    if (!picture)
-        msg_Err(filter, "Failed to allocate picture");
-    return picture;
+    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);
+    if (!pool)
+        return NULL;
+    return picture_pool_Get(pool);
 }
 static void VideoBufferDelete(filter_t *filter, picture_t *picture)
 {
@@ -62,12 +69,11 @@ static void VideoBufferDelete(filter_t *filter, picture_t *picture)
     picture_Release(picture);
 }
 
-static int  FilterAllocationInit(filter_t *filter, void *data)
+static int  FilterAllocationInit(filter_t *filter, void *vd)
 {
-    VLC_UNUSED(data);
-
     filter->pf_vout_buffer_new = VideoBufferNew;
     filter->pf_vout_buffer_del = VideoBufferDelete;
+    filter->p_owner            = vd;
 
     return VLC_SUCCESS;
 }
@@ -75,6 +81,7 @@ static void FilterAllocationClean(filter_t *filter)
 {
     filter->pf_vout_buffer_new = NULL;
     filter->pf_vout_buffer_del = NULL;
+    filter->p_owner            = NULL;
 }
 
 /*****************************************************************************
@@ -98,7 +105,6 @@ static vout_display_t *vout_display_New(vlc_object_t *obj,
 
     /* Picture buffer does not have the concept of aspect ratio */
     video_format_Copy(&vd->fmt, fmt);
-    vd->fmt.i_aspect = 0;
     vd->fmt.i_sar_num = 0;
     vd->fmt.i_sar_den = 0;
 
@@ -108,7 +114,7 @@ static vout_display_t *vout_display_New(vlc_object_t *obj,
     vd->info.has_pictures_invalid = false;
 
     vd->cfg = cfg;
-    vd->get = NULL;
+    vd->pool = NULL;
     vd->prepare = NULL;
     vd->display = NULL;
     vd->control = NULL;
@@ -161,7 +167,8 @@ static int vout_display_Control(vout_display_t *vd, int query, ...)
 }
 static void vout_display_Manage(vout_display_t *vd)
 {
-    vd->manage(vd);
+    if (vd->manage)
+        vd->manage(vd);
 }
 
 /* */
@@ -275,22 +282,9 @@ struct vout_display_owner_sys_t {
     bool            is_wrapper;  /* Is the current display a wrapper */
     vout_display_t  *wrapper; /* Vout display wrapper */
 
-    /* mouse state */
-    struct {
-        vlc_mouse_t state;
-
-        mtime_t last_pressed;
-        mtime_t last_moved;
-        bool    is_hidden;
-
-        /* */
-        mtime_t double_click_timeout;
-        mtime_t hide_timeout;
-    } mouse;
-
     /* */
     vout_display_cfg_t cfg;
-    bool     is_on_top_initial;
+    unsigned     wm_state_initial;
     struct {
         unsigned num;
         unsigned den;
@@ -306,15 +300,6 @@ struct vout_display_owner_sys_t {
     } crop_saved;
 
     /* */
-    bool reset_pictures;
-
-    bool ch_fullscreen;
-    bool is_fullscreen;
-
-    bool ch_display_size;
-    int  display_width;
-    int  display_height;
-
     bool ch_display_filled;
     bool is_display_filled;
 
@@ -324,8 +309,8 @@ struct vout_display_owner_sys_t {
         int  den;
     } zoom;
 
-    bool ch_on_top;
-    bool is_on_top;
+    bool ch_wm_state;
+    unsigned wm_state;
 
     bool ch_sar;
     struct {
@@ -346,8 +331,43 @@ struct vout_display_owner_sys_t {
     /* */
     video_format_t source;
     filter_chain_t *filters;
+
+    /* Lock protecting the variables used by
+     * VoutDisplayEvent(ie vout_display_SendEvent) */
+    vlc_mutex_t lock;
+
+    /* mouse state */
+    struct {
+        vlc_mouse_t state;
+
+        mtime_t last_pressed;
+        mtime_t last_moved;
+        bool    is_hidden;
+        bool    ch_activity;
+
+        /* */
+        mtime_t double_click_timeout;
+        mtime_t hide_timeout;
+    } mouse;
+
+    bool reset_pictures;
+
+    bool ch_fullscreen;
+    bool is_fullscreen;
+
+    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
 };
 
+static void DummyVoutSendDisplayEventMouse(vout_thread_t *, vlc_mouse_t *fallback, const vlc_mouse_t *m);
+
 static void VoutDisplayCreateRender(vout_display_t *vd)
 {
     vout_display_owner_sys_t *osys = vd->owner.sys;
@@ -355,22 +375,29 @@ static void VoutDisplayCreateRender(vout_display_t *vd)
     osys->filters = NULL;
 
     video_format_t v_src = vd->source;
-    v_src.i_aspect  = 0;
     v_src.i_sar_num = 0;
     v_src.i_sar_den = 0;
 
     video_format_t v_dst = vd->fmt;
-    v_dst.i_aspect  = 0;
     v_dst.i_sar_num = 0;
     v_dst.i_sar_den = 0;
 
-    const bool convert = memcmp(&v_src, &v_dst, sizeof(v_src)) != 0;
+    video_format_t v_dst_cmp = v_dst;
+    if ((v_src.i_chroma == VLC_CODEC_J420 && v_dst.i_chroma == VLC_CODEC_I420) ||
+        (v_src.i_chroma == VLC_CODEC_J422 && v_dst.i_chroma == VLC_CODEC_I422) ||
+        (v_src.i_chroma == VLC_CODEC_J440 && v_dst.i_chroma == VLC_CODEC_I440) ||
+        (v_src.i_chroma == VLC_CODEC_J444 && v_dst.i_chroma == VLC_CODEC_I444))
+        v_dst_cmp.i_chroma = v_src.i_chroma;
+
+    const bool convert = memcmp(&v_src, &v_dst_cmp, sizeof(v_src)) != 0;
     if (!convert)
         return;
 
+    msg_Err(vd, "A filter to adapt decoder to display is needed");
+
     osys->filters = filter_chain_New(vd, "video filter2", false,
                                      FilterAllocationInit,
-                                     FilterAllocationClean, NULL);
+                                     FilterAllocationClean, vd);
     assert(osys->filters); /* TODO critical */
 
     /* */
@@ -379,13 +406,18 @@ static void VoutDisplayCreateRender(vout_display_t *vd)
 
     /* */
     es_format_t dst;
-    es_format_InitFromVideo(&dst, &v_dst);
 
-    filter_chain_Reset(osys->filters, &src, &dst);
+    filter_t *filter;
+    for (int i = 0; i < 1 + (v_dst_cmp.i_chroma != v_dst.i_chroma); i++) {
 
-    msg_Err(vd, "A filter to adapt decoder to display is needed");
-    filter_t *filter = filter_chain_AppendFilter(osys->filters,
-                                                 NULL, NULL, &src, &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);
+        if (filter)
+            break;
+    }
     if (!filter)
     {
         msg_Err(vd, "VoutDisplayCreateRender FAILED");
@@ -407,81 +439,39 @@ static void VoutDisplayResetRender(vout_display_t *vd)
     VoutDisplayDestroyRender(vd);
     VoutDisplayCreateRender(vd);
 }
-static void VoutDisplayEventMouse(vout_display_t *vd, const vlc_mouse_t *mouse)
+static void VoutDisplayEventMouse(vout_display_t *vd, int event, va_list args)
 {
     vout_display_owner_sys_t *osys = vd->owner.sys;
 
-    /* */
-    vlc_mouse_t m = *mouse;
-
-    /* Emulate double-click if needed */
-    if (!vd->info.has_double_click &&
-        vlc_mouse_HasPressed(&osys->mouse.state, &m, MOUSE_BUTTON_LEFT)) {
-        const mtime_t i_date = mdate();
-
-        if (i_date - osys->mouse.last_pressed < osys->mouse.double_click_timeout ) {
-            m.b_double_click = true;
-            osys->mouse.last_pressed = 0;
-        } else {
-            osys->mouse.last_pressed = mdate();
-        }
-    }
+    vlc_mutex_lock(&osys->lock);
 
     /* */
-    osys->mouse.state = m;
-
-    vout_SendDisplayEventMouse(osys->vout, &m);
-}
-
-static void VoutDisplayEvent(vout_display_t *vd, int event, va_list args)
-{
-    vout_display_owner_sys_t *osys = vd->owner.sys;
+    vlc_mouse_t m = osys->mouse.state;
+    bool is_ignored = false;
 
     switch (event) {
-    case VOUT_DISPLAY_EVENT_CLOSE: {
-        msg_Dbg(vd, "VoutDisplayEvent 'close'");
-        vout_SendEventClose(osys->vout);
-        break;
-    }
-    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);
-        break;
-    }
     case VOUT_DISPLAY_EVENT_MOUSE_STATE: {
         const int x = (int)va_arg(args, int);
         const int y = (int)va_arg(args, int);
         const int button_mask = (int)va_arg(args, int);
 
-        vlc_mouse_t m;
-
         vlc_mouse_Init(&m);
         m.i_x = x;
         m.i_y = y;
         m.i_pressed = button_mask;
-
-        VoutDisplayEventMouse(vd, &m);
         break;
     }
     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);
+            //msg_Dbg(vd, "VoutDisplayEvent 'mouse' @%d,%d", x, y);
 
-            /* */
-            osys->mouse.is_hidden = false;
-            if (!vd->info.has_hide_mouse)
-                osys->mouse.last_moved = mdate();
-            vout_SendEventMouseVisible(osys->vout);
-
-            vlc_mouse_t m = osys->mouse.state;
             m.i_x = x;
             m.i_y = y;
             m.b_double_click = false;
-
-            VoutDisplayEventMouse(vd, &m);
+        } else {
+            is_ignored = true;
         }
         break;
     }
@@ -490,60 +480,139 @@ static void VoutDisplayEvent(vout_display_t *vd, int event, va_list args)
         const int button = (int)va_arg(args, int);
         const int button_mask = 1 << button;
 
-        /* Ignore inconistent event */
-        if (event == VOUT_DISPLAY_EVENT_MOUSE_PRESSED && (osys->mouse.state.i_pressed & button_mask))
-            break;
-        if (event == VOUT_DISPLAY_EVENT_MOUSE_RELEASED && !(osys->mouse.state.i_pressed & button_mask))
+        /* Ignore inconsistent event */
+        if ((event == VOUT_DISPLAY_EVENT_MOUSE_PRESSED  &&  (osys->mouse.state.i_pressed & button_mask)) ||
+            (event == VOUT_DISPLAY_EVENT_MOUSE_RELEASED && !(osys->mouse.state.i_pressed & button_mask))) {
+            is_ignored = true;
             break;
+        }
 
         /* */
         msg_Dbg(vd, "VoutDisplayEvent 'mouse button' %d t=%d", button, event);
 
-        osys->mouse.is_hidden = false;
-        if (!vd->info.has_hide_mouse)
-            osys->mouse.last_moved = mdate();
-        vout_SendEventMouseVisible(osys->vout);
-
-        vlc_mouse_t m = osys->mouse.state;
         m.b_double_click = false;
         if (event == VOUT_DISPLAY_EVENT_MOUSE_PRESSED)
             m.i_pressed |= button_mask;
         else
             m.i_pressed &= ~button_mask;
-
-        VoutDisplayEventMouse(vd, &m);
         break;
     }
-    case VOUT_DISPLAY_EVENT_MOUSE_DOUBLE_CLICK: {
+    case VOUT_DISPLAY_EVENT_MOUSE_DOUBLE_CLICK:
         msg_Dbg(vd, "VoutDisplayEvent 'double click'");
 
-        vlc_mouse_t m = osys->mouse.state;
         m.b_double_click = true;
-        VoutDisplayEventMouse(vd, &m);
         break;
+    default:
+        assert(0);
+    }
+
+    if (is_ignored) {
+        vlc_mutex_unlock(&osys->lock);
+        return;
+    }
+
+    /* Emulate double-click if needed */
+    if (!vd->info.has_double_click &&
+        vlc_mouse_HasPressed(&osys->mouse.state, &m, MOUSE_BUTTON_LEFT)) {
+        const mtime_t i_date = mdate();
+
+        if (i_date - osys->mouse.last_pressed < osys->mouse.double_click_timeout ) {
+            m.b_double_click = true;
+            osys->mouse.last_pressed = 0;
+        } else {
+            osys->mouse.last_pressed = mdate();
+        }
     }
 
+    /* */
+    osys->mouse.state = m;
+
+    /* */
+    osys->mouse.ch_activity = true;
+    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
+}
+
+static void VoutDisplayEvent(vout_display_t *vd, int event, va_list args)
+{
+    vout_display_owner_sys_t *osys = vd->owner.sys;
+
+    switch (event) {
+    case VOUT_DISPLAY_EVENT_CLOSE: {
+        msg_Dbg(vd, "VoutDisplayEvent 'close'");
+        vout_SendEventClose(osys->vout);
+        break;
+    }
+    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);
+        break;
+    }
+    case VOUT_DISPLAY_EVENT_MOUSE_STATE:
+    case VOUT_DISPLAY_EVENT_MOUSE_MOVED:
+    case VOUT_DISPLAY_EVENT_MOUSE_PRESSED:
+    case VOUT_DISPLAY_EVENT_MOUSE_RELEASED:
+    case VOUT_DISPLAY_EVENT_MOUSE_DOUBLE_CLICK:
+        VoutDisplayEventMouse(vd, event, args);
+        break;
+
     case VOUT_DISPLAY_EVENT_FULLSCREEN: {
         const int is_fullscreen = (int)va_arg(args, int);
 
         msg_Dbg(vd, "VoutDisplayEvent 'fullscreen' %d", is_fullscreen);
 
-        if (!is_fullscreen == !osys->is_fullscreen)
-            break;
-        osys->ch_fullscreen = true;
-        osys->is_fullscreen = is_fullscreen;
+        vlc_mutex_lock(&osys->lock);
+        if (!is_fullscreen != !osys->is_fullscreen) {
+            osys->ch_fullscreen = true;
+            osys->is_fullscreen = is_fullscreen;
+        }
+        vlc_mutex_unlock(&osys->lock);
+        break;
+    }
+
+    case VOUT_DISPLAY_EVENT_WINDOW_STATE: {
+        const unsigned state = va_arg(args, unsigned);
+
+        msg_Dbg(vd, "VoutDisplayEvent 'window state' %u", state);
+
+        vlc_mutex_lock(&osys->lock);
+        if (state != osys->wm_state) {
+            osys->ch_wm_state = true;
+            osys->wm_state = state;
+        }
+        vlc_mutex_unlock(&osys->lock);
         break;
     }
 
     case VOUT_DISPLAY_EVENT_DISPLAY_SIZE: {
         const int width  = (int)va_arg(args, int);
         const int height = (int)va_arg(args, int);
-        msg_Dbg(vd, "VoutDisplayEvent 'resize' %dx%d", width, height);
+        const bool is_fullscreen = (bool)va_arg(args, int);
+        msg_Dbg(vd, "VoutDisplayEvent 'resize' %dx%d %s",
+                width, height, is_fullscreen ? "fullscreen" : "window");
 
         /* */
-        osys->ch_display_size = true;
-        osys->display_width   = width;
-        osys->display_height  = 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;
+
+        vlc_mutex_unlock(&osys->lock);
         break;
     }
 
@@ -552,7 +621,10 @@ static void VoutDisplayEvent(vout_display_t *vd, int event, va_list args)
 
         /* */
         assert(vd->info.has_pictures_invalid);
+
+        vlc_mutex_lock(&osys->lock);
         osys->reset_pictures = true;
+        vlc_mutex_unlock(&osys->lock);
         break;
     }
     default:
@@ -575,7 +647,7 @@ static void VoutDisplayDelWindow(vout_display_t *vd, vout_window_t *window)
     vout_DeleteDisplayWindow(osys->vout, vd, window);
 }
 
-void vout_ManageDisplay(vout_display_t *vd)
+void vout_ManageDisplay(vout_display_t *vd, bool allow_reset_pictures)
 {
     vout_display_owner_sys_t *osys = vd->owner.sys;
 
@@ -583,69 +655,107 @@ void vout_ManageDisplay(vout_display_t *vd)
 
     /* Handle mouse timeout */
     const mtime_t date = mdate();
+    bool  hide_mouse = false;
+
+    vlc_mutex_lock(&osys->lock);
+
     if (!osys->mouse.is_hidden &&
         osys->mouse.last_moved + osys->mouse.hide_timeout < date) {
+        osys->mouse.is_hidden = hide_mouse = true;
+    } else if (osys->mouse.ch_activity) {
+        osys->mouse.is_hidden = false;
+    }
+    osys->mouse.ch_activity = false;
+    vlc_mutex_unlock(&osys->lock);
+
+    if (hide_mouse) {
         if (!vd->info.has_hide_mouse) {
             msg_Dbg(vd, "auto hidding mouse");
             vout_display_Control(vd, VOUT_DISPLAY_HIDE_MOUSE);
         }
-        osys->mouse.is_hidden = true;
-
         vout_SendEventMouseHidden(osys->vout);
     }
 
-    bool reset_pictures = false;
+    bool reset_render = false;
     for (;;) {
-        if (!osys->ch_fullscreen &&
-            !osys->ch_display_size &&
-            !osys->reset_pictures &&
+
+        vlc_mutex_lock(&osys->lock);
+
+        bool ch_fullscreen  = osys->ch_fullscreen;
+        bool is_fullscreen  = osys->is_fullscreen;
+        osys->ch_fullscreen = false;
+
+        bool ch_wm_state  = osys->ch_wm_state;
+        unsigned wm_state  = osys->wm_state;
+        osys->ch_wm_state = false;
+
+        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;
+        if (allow_reset_pictures) {
+            reset_pictures = osys->reset_pictures;
+            osys->reset_pictures = false;
+        } else {
+            reset_pictures = false;
+        }
+
+        vlc_mutex_unlock(&osys->lock);
+
+        if (!ch_fullscreen &&
+            !ch_display_size &&
+            !reset_pictures &&
             !osys->ch_display_filled &&
             !osys->ch_zoom &&
-            !osys->ch_on_top &&
+            !ch_wm_state &&
             !osys->ch_sar &&
             !osys->ch_crop)
             break;
 
         /* */
-        if (osys->ch_fullscreen) {
+        if (ch_fullscreen) {
             vout_display_cfg_t cfg = osys->cfg;
 
-            cfg.is_fullscreen = osys->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 (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_FULLSCREEN, &cfg)) {
                 msg_Err(vd, "Failed to set fullscreen");
-                osys->is_fullscreen = osys->cfg.is_fullscreen;
+                is_fullscreen = osys->cfg.is_fullscreen;
             }
-            osys->cfg.is_fullscreen = osys->is_fullscreen;
-            osys->ch_fullscreen = false;
+            osys->cfg.is_fullscreen = is_fullscreen;
 
             /* */
             vout_SendEventFullscreen(osys->vout, osys->cfg.is_fullscreen);
         }
 
         /* */
-        if (osys->ch_display_size) {
+        if (ch_display_size) {
             vout_display_cfg_t cfg = osys->cfg;
-            cfg.display.width  = osys->display_width;
-            cfg.display.height = osys->display_height;
+            cfg.display.width  = display_width;
+            cfg.display.height = display_height;
 
-            if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_DISPLAY_SIZE, &cfg)) {
-                msg_Err(vd, "Failed to resize display");
+            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");
 
                 /* We ignore the resized */
-                osys->display_width  = osys->cfg.display.width;
-                osys->display_height = osys->cfg.display.height;
+                display_width  = osys->cfg.display.width;
+                display_height = osys->cfg.display.height;
             }
-            osys->cfg.display.width  = osys->display_width;
-            osys->cfg.display.height = osys->display_height;
+            osys->cfg.display.width  = display_width;
+            osys->cfg.display.height = display_height;
 
-            if (!osys->is_fullscreen) {
-                osys->width_saved  = osys->display_width;
-                osys->height_saved = osys->display_height;
+            if (!display_is_fullscreen) {
+                osys->width_saved  = display_width;
+                osys->height_saved = display_height;
             }
-            osys->ch_display_size = false;
         }
         /* */
         if (osys->ch_display_filled) {
@@ -682,9 +792,18 @@ void vout_ManageDisplay(vout_display_t *vd)
                 osys->zoom.num = osys->cfg.zoom.num;
                 osys->zoom.den = osys->cfg.zoom.den;
             } else if (cfg.is_display_filled) {
-                osys->ch_display_size = true;
-                osys->display_width  = (int64_t)vd->source.i_width  * osys->zoom.num / osys->zoom.den;
-                osys->display_height = (int64_t)vd->source.i_height * osys->zoom.num / osys->zoom.den;
+                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);
             }
 
             osys->cfg.zoom.num = osys->zoom.num;
@@ -694,19 +813,15 @@ void vout_ManageDisplay(vout_display_t *vd)
             vout_SendEventZoom(osys->vout, osys->cfg.zoom.num, osys->cfg.zoom.den);
         }
         /* */
-        if (osys->ch_on_top) {
-            bool is_on_top = osys->is_on_top;
-
-            if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_ON_TOP, is_on_top)) {
+        if (ch_wm_state) {
+            if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_WINDOW_STATE, wm_state)) {
                 msg_Err(vd, "Failed to set on top");
-                is_on_top = osys->is_on_top_initial;
+                wm_state = osys->wm_state;
             }
-            osys->is_on_top_initial =
-            osys->is_on_top         = is_on_top;
-            osys->ch_on_top = false;
+            osys->wm_state_initial = wm_state;
 
             /* */
-            vout_SendEventOnTop(osys->vout, osys->is_on_top_initial);
+            vout_SendEventOnTop(osys->vout, osys->wm_state_initial);
         }
         /* */
         if (osys->ch_sar) {
@@ -801,17 +916,15 @@ void vout_ManageDisplay(vout_display_t *vd)
         }
 
         /* */
-        if (osys->reset_pictures) {
+        if (reset_pictures) {
             if (vout_display_Control(vd, VOUT_DISPLAY_RESET_PICTURES)) {
                 /* FIXME what to do here ? */
                 msg_Err(vd, "Failed to reset pictures (probably fatal)");
             }
-            reset_pictures = true;
-
-            osys->reset_pictures = false;
+            reset_render = true;
         }
     }
-    if (reset_pictures)
+    if (reset_render)
         VoutDisplayResetRender(vd);
 }
 
@@ -819,7 +932,11 @@ bool vout_AreDisplayPicturesInvalid(vout_display_t *vd)
 {
     vout_display_owner_sys_t *osys = vd->owner.sys;
 
-    return osys->reset_pictures;
+    vlc_mutex_lock(&osys->lock);
+    const bool reset_pictures = osys->reset_pictures;
+    vlc_mutex_unlock(&osys->lock);
+
+    return reset_pictures;
 }
 
 bool vout_IsDisplayFiltered(vout_display_t *vd)
@@ -833,8 +950,7 @@ picture_t *vout_FilterDisplay(vout_display_t *vd, picture_t *picture)
 {
     vout_display_owner_sys_t *osys = vd->owner.sys;
 
-    if (!osys->filters)
-        return picture;
+    assert(osys->filters);
     return filter_chain_VideoFilter(osys->filters, picture);
 }
 
@@ -842,10 +958,12 @@ void vout_SetDisplayFullscreen(vout_display_t *vd, bool is_fullscreen)
 {
     vout_display_owner_sys_t *osys = vd->owner.sys;
 
+    vlc_mutex_lock(&osys->lock);
     if (!osys->is_fullscreen != !is_fullscreen) {
         osys->ch_fullscreen = true;
         osys->is_fullscreen = is_fullscreen;
     }
+    vlc_mutex_unlock(&osys->lock);
 }
 
 void vout_SetDisplayFilled(vout_display_t *vd, bool is_filled)
@@ -868,15 +986,19 @@ void vout_SetDisplayZoom(vout_display_t *vd, int num, int den)
         osys->zoom.den = den;
     }
 }
-void vout_SetDisplayOnTop(vout_display_t *vd, bool is_on_top)
+
+void vout_SetWindowState(vout_display_t *vd, unsigned state)
 {
     vout_display_owner_sys_t *osys = vd->owner.sys;
 
-    if (!osys->is_on_top != !is_on_top) {
-        osys->ch_on_top = true;
-        osys->is_on_top = is_on_top;
+    vlc_mutex_lock(&osys->lock);
+    if (osys->wm_state != state) {
+        osys->ch_wm_state = true;
+        osys->wm_state = state;
     }
+    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;
@@ -906,7 +1028,13 @@ void vout_SetDisplayCrop(vout_display_t *vd,
         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,
@@ -922,7 +1050,8 @@ static vout_display_t *DisplayNew(vout_thread_t *vout,
     vout_display_cfg_t *cfg = &osys->cfg;
 
     *cfg = state->cfg;
-    osys->is_on_top_initial = state->is_on_top;;
+    osys->wm_state_initial = state->is_on_top
+        ? VOUT_WINDOW_STATE_ABOVE : 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,
@@ -932,6 +1061,8 @@ static vout_display_t *DisplayNew(vout_thread_t *vout,
     osys->is_wrapper = is_wrapper;
     osys->wrapper = wrapper;
 
+    vlc_mutex_init(&osys->lock);
+
     vlc_mouse_Init(&osys->mouse.state);
     osys->mouse.last_moved = mdate();
     osys->mouse.double_click_timeout = double_click_timeout;
@@ -964,6 +1095,9 @@ static vout_display_t *DisplayNew(vout_thread_t *vout,
 
     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
 
     vout_display_owner_t owner;
     if (owner_ptr) {
@@ -990,8 +1124,8 @@ static vout_display_t *DisplayNew(vout_thread_t *vout,
     if (osys->sar.num != source_org->i_sar_num ||
         osys->sar.den != source_org->i_sar_den)
         osys->ch_sar = true;
-    if (osys->is_on_top)
-        osys->ch_on_top = true;
+    if (osys->wm_state != VOUT_WINDOW_STATE_NORMAL)
+        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 ||
@@ -1008,7 +1142,7 @@ 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->is_on_top_initial;
+        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;
     }
@@ -1017,6 +1151,7 @@ void vout_DeleteDisplay(vout_display_t *vd, vout_display_state_t *state)
     if (osys->is_wrapper)
         SplitterClose(vd);
     vout_display_Delete(vd);
+    vlc_mutex_destroy(&osys->lock);
     free(osys);
 }
 
@@ -1121,6 +1256,7 @@ 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;
@@ -1315,6 +1451,22 @@ 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);