]> git.sesse.net Git - vlc/blob - src/video_output/display.c
Fixed a small memleak (vout).
[vlc] / src / video_output / display.c
1 /*****************************************************************************
2  * display.c: "vout display" managment
3  *****************************************************************************
4  * Copyright (C) 2009 Laurent Aimar
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30 #include <assert.h>
31
32 #include <vlc_common.h>
33 #include <vlc_video_splitter.h>
34 #include <vlc_vout_display.h>
35 #include <vlc_vout.h>
36 #include <vlc_block.h>
37 #include <vlc_modules.h>
38
39 #include <libvlc.h>
40
41 #include "display.h"
42
43 #include "event.h"
44
45 /* It must be present as long as a vout_display_t must be created using a dummy
46  * vout (as an opengl provider) */
47 #define ALLOW_DUMMY_VOUT
48
49 static void SplitterClose(vout_display_t *vd);
50
51 /*****************************************************************************
52  * FIXME/TODO see how to have direct rendering here (interact with vout.c)
53  *****************************************************************************/
54 static picture_t *VideoBufferNew(filter_t *filter)
55 {
56     vout_display_t *vd = (vout_display_t*)filter->p_owner;
57     const video_format_t *fmt = &filter->fmt_out.video;
58
59     assert(vd->fmt.i_chroma == fmt->i_chroma &&
60            vd->fmt.i_width  == fmt->i_width  &&
61            vd->fmt.i_height == fmt->i_height);
62
63     picture_pool_t *pool = vout_display_Pool(vd, 3);
64     if (!pool)
65         return NULL;
66     return picture_pool_Get(pool);
67 }
68 static void VideoBufferDelete(filter_t *filter, picture_t *picture)
69 {
70     VLC_UNUSED(filter);
71     picture_Release(picture);
72 }
73
74 static int  FilterAllocationInit(filter_t *filter, void *vd)
75 {
76     filter->pf_video_buffer_new = VideoBufferNew;
77     filter->pf_video_buffer_del = VideoBufferDelete;
78     filter->p_owner             = vd;
79
80     return VLC_SUCCESS;
81 }
82 static void FilterAllocationClean(filter_t *filter)
83 {
84     filter->pf_video_buffer_new = NULL;
85     filter->pf_video_buffer_del = NULL;
86     filter->p_owner             = NULL;
87 }
88
89 /*****************************************************************************
90  *
91  *****************************************************************************/
92
93 /**
94  * It creates a new vout_display_t using the given configuration.
95  */
96 static vout_display_t *vout_display_New(vlc_object_t *obj,
97                                         const char *module, bool load_module,
98                                         const video_format_t *fmt,
99                                         const vout_display_cfg_t *cfg,
100                                         vout_display_owner_t *owner)
101 {
102     /* */
103     vout_display_t *vd = vlc_object_create(obj, sizeof(*vd));
104
105     /* */
106     video_format_Copy(&vd->source, fmt);
107
108     /* Picture buffer does not have the concept of aspect ratio */
109     video_format_Copy(&vd->fmt, fmt);
110     vd->fmt.i_sar_num = 0;
111     vd->fmt.i_sar_den = 0;
112
113     vd->info.is_slow = false;
114     vd->info.has_double_click = false;
115     vd->info.has_hide_mouse = false;
116     vd->info.has_pictures_invalid = false;
117     vd->info.has_event_thread = false;
118
119     vd->cfg = cfg;
120     vd->pool = NULL;
121     vd->prepare = NULL;
122     vd->display = NULL;
123     vd->control = NULL;
124     vd->manage = NULL;
125     vd->sys = NULL;
126
127     vd->owner = *owner;
128
129     vlc_object_attach(vd, obj);
130
131     if (load_module) {
132         vd->module = module_need(vd, "vout display", module, module && *module != '\0');
133         if (!vd->module) {
134             vlc_object_release(vd);
135             return NULL;
136         }
137     } else {
138         vd->module = NULL;
139     }
140     return vd;
141 }
142
143 /**
144  * It deletes a vout_display_t
145  */
146 static void vout_display_Delete(vout_display_t *vd)
147 {
148     if (vd->module)
149         module_unneed(vd, vd->module);
150
151     vlc_object_release(vd);
152 }
153
154 /**
155  * It controls a vout_display_t
156  */
157 static int vout_display_Control(vout_display_t *vd, int query, ...)
158 {
159     va_list args;
160     int result;
161
162     va_start(args, query);
163     result = vd->control(vd, query, args);
164     va_end(args);
165
166     return result;
167 }
168
169 static void vout_display_Manage(vout_display_t *vd)
170 {
171     if (vd->manage)
172         vd->manage(vd);
173 }
174
175 /* */
176 void vout_display_GetDefaultDisplaySize(unsigned *width, unsigned *height,
177                                         const video_format_t *source,
178                                         const vout_display_cfg_t *cfg)
179 {
180     if (cfg->display.width > 0 && cfg->display.height > 0) {
181         *width  = cfg->display.width;
182         *height = cfg->display.height;
183     } else if (cfg->display.width > 0) {
184         *width  = cfg->display.width;
185         *height = (int64_t)source->i_visible_height * source->i_sar_den * cfg->display.width * cfg->display.sar.num /
186             source->i_visible_width / source->i_sar_num / cfg->display.sar.den;
187     } else if (cfg->display.height > 0) {
188         *width  = (int64_t)source->i_visible_width * source->i_sar_num * cfg->display.height * cfg->display.sar.den /
189             source->i_visible_height / source->i_sar_den / cfg->display.sar.num;
190         *height = cfg->display.height;
191     } else if (source->i_sar_num >= source->i_sar_den) {
192         *width  = (int64_t)source->i_visible_width * source->i_sar_num * cfg->display.sar.den / source->i_sar_den / cfg->display.sar.num;
193         *height = source->i_visible_height;
194     } else {
195         *width  = source->i_visible_width;
196         *height = (int64_t)source->i_visible_height * source->i_sar_den * cfg->display.sar.num / source->i_sar_num / cfg->display.sar.den;
197     }
198
199     *width  = *width  * cfg->zoom.num / cfg->zoom.den;
200     *height = *height * cfg->zoom.num / cfg->zoom.den;
201 }
202
203 /* */
204 void vout_display_PlacePicture(vout_display_place_t *place,
205                                const video_format_t *source,
206                                const vout_display_cfg_t *cfg,
207                                bool do_clipping)
208 {
209     /* */
210     memset(place, 0, sizeof(*place));
211     if (cfg->display.width <= 0 || cfg->display.height <= 0)
212         return;
213
214     /* */
215     unsigned display_width;
216     unsigned display_height;
217
218     if (cfg->is_display_filled) {
219         display_width  = cfg->display.width;
220         display_height = cfg->display.height;
221     } else {
222         vout_display_cfg_t cfg_tmp = *cfg;
223
224         cfg_tmp.display.width  = 0;
225         cfg_tmp.display.height = 0;
226         vout_display_GetDefaultDisplaySize(&display_width, &display_height,
227                                            source, &cfg_tmp);
228
229         if (do_clipping) {
230             display_width  = __MIN(display_width,  cfg->display.width);
231             display_height = __MIN(display_height, cfg->display.height);
232         }
233     }
234
235     const unsigned width  = source->i_visible_width;
236     const unsigned height = source->i_visible_height;
237     /* Compute the height if we use the width to fill up display_width */
238     const int64_t scaled_height = (int64_t)height * display_width  * cfg->display.sar.num * source->i_sar_den / width  / source->i_sar_num / cfg->display.sar.den;
239     /* And the same but switching width/height */
240     const int64_t scaled_width  = (int64_t)width  * display_height * cfg->display.sar.den * source->i_sar_num / height / source->i_sar_den / cfg->display.sar.num;
241
242     /* We keep the solution that avoid filling outside the display */
243     if (scaled_width <= cfg->display.width) {
244         place->width  = scaled_width;
245         place->height = display_height;
246     } else {
247         place->width  = display_width;
248         place->height = scaled_height;
249     }
250
251     /*  Compute position */
252     switch (cfg->align.horizontal) {
253     case VOUT_DISPLAY_ALIGN_LEFT:
254         place->x = 0;
255         break;
256     case VOUT_DISPLAY_ALIGN_RIGHT:
257         place->x = cfg->display.width - place->width;
258         break;
259     default:
260         place->x = ((int)cfg->display.width - (int)place->width) / 2;
261         break;
262     }
263
264     switch (cfg->align.vertical) {
265     case VOUT_DISPLAY_ALIGN_TOP:
266         place->y = 0;
267         break;
268     case VOUT_DISPLAY_ALIGN_BOTTOM:
269         place->y = cfg->display.height - place->height;
270         break;
271     default:
272         place->y = ((int)cfg->display.height - (int)place->height) / 2;
273         break;
274     }
275 }
276
277 struct vout_display_owner_sys_t {
278     vout_thread_t   *vout;
279     bool            is_wrapper;  /* Is the current display a wrapper */
280     vout_display_t  *wrapper; /* Vout display wrapper */
281
282     /* */
283     vout_display_cfg_t cfg;
284     unsigned     wm_state_initial;
285     struct {
286         unsigned num;
287         unsigned den;
288     } sar_initial;
289
290     /* */
291     unsigned width_saved;
292     unsigned height_saved;
293
294     struct {
295         unsigned num;
296         unsigned den;
297     } crop_saved;
298
299     /* */
300     bool ch_display_filled;
301     bool is_display_filled;
302
303     bool ch_zoom;
304     struct {
305         int  num;
306         int  den;
307     } zoom;
308
309     bool ch_wm_state;
310     unsigned wm_state;
311
312     bool ch_sar;
313     struct {
314         unsigned num;
315         unsigned den;
316     } sar;
317
318     bool ch_crop;
319     struct {
320         int      left;
321         int      top;
322         int      right;
323         int      bottom;
324         unsigned num;
325         unsigned den;
326     } crop;
327
328     /* */
329     video_format_t source;
330     filter_chain_t *filters;
331
332     /* Lock protecting the variables used by
333      * VoutDisplayEvent(ie vout_display_SendEvent) */
334     vlc_mutex_t lock;
335
336     /* mouse state */
337     struct {
338         vlc_mouse_t state;
339
340         mtime_t last_pressed;
341         mtime_t last_moved;
342         bool    is_hidden;
343         bool    ch_activity;
344
345         /* */
346         mtime_t double_click_timeout;
347         mtime_t hide_timeout;
348     } mouse;
349
350     bool reset_pictures;
351
352     bool ch_fullscreen;
353     bool is_fullscreen;
354
355     bool ch_display_size;
356     int  display_width;
357     int  display_height;
358     bool display_is_fullscreen;
359     bool display_is_forced;
360
361     int  fit_window;
362
363     struct {
364         vlc_thread_t thread;
365         block_fifo_t *fifo;
366     } event;
367
368 #ifdef ALLOW_DUMMY_VOUT
369     vlc_mouse_t vout_mouse;
370 #endif
371 };
372
373 static void DummyVoutSendDisplayEventMouse(vout_thread_t *, vlc_mouse_t *fallback, const vlc_mouse_t *m);
374
375 static void VoutDisplayCreateRender(vout_display_t *vd)
376 {
377     vout_display_owner_sys_t *osys = vd->owner.sys;
378
379     osys->filters = NULL;
380
381     video_format_t v_src = vd->source;
382     v_src.i_sar_num = 0;
383     v_src.i_sar_den = 0;
384
385     video_format_t v_dst = vd->fmt;
386     v_dst.i_sar_num = 0;
387     v_dst.i_sar_den = 0;
388
389     video_format_t v_dst_cmp = v_dst;
390     if ((v_src.i_chroma == VLC_CODEC_J420 && v_dst.i_chroma == VLC_CODEC_I420) ||
391         (v_src.i_chroma == VLC_CODEC_J422 && v_dst.i_chroma == VLC_CODEC_I422) ||
392         (v_src.i_chroma == VLC_CODEC_J440 && v_dst.i_chroma == VLC_CODEC_I440) ||
393         (v_src.i_chroma == VLC_CODEC_J444 && v_dst.i_chroma == VLC_CODEC_I444))
394         v_dst_cmp.i_chroma = v_src.i_chroma;
395
396     const bool convert = memcmp(&v_src, &v_dst_cmp, sizeof(v_src)) != 0;
397     if (!convert)
398         return;
399
400     msg_Dbg(vd, "A filter to adapt decoder to display is needed");
401
402     osys->filters = filter_chain_New(vd, "video filter2", false,
403                                      FilterAllocationInit,
404                                      FilterAllocationClean, vd);
405     assert(osys->filters); /* TODO critical */
406
407     /* */
408     es_format_t src;
409     es_format_InitFromVideo(&src, &v_src);
410
411     /* */
412     es_format_t dst;
413
414     filter_t *filter;
415     for (int i = 0; i < 1 + (v_dst_cmp.i_chroma != v_dst.i_chroma); i++) {
416
417         es_format_InitFromVideo(&dst, i == 0 ? &v_dst : &v_dst_cmp);
418
419         filter_chain_Reset(osys->filters, &src, &dst);
420         filter = filter_chain_AppendFilter(osys->filters,
421                                            NULL, NULL, &src, &dst);
422         if (filter)
423             break;
424     }
425     if (!filter)
426     {
427         msg_Err(vd, "VoutDisplayCreateRender FAILED");
428         /* TODO */
429         assert(0);
430     }
431 }
432
433 static void VoutDisplayDestroyRender(vout_display_t *vd)
434 {
435     vout_display_owner_sys_t *osys = vd->owner.sys;
436
437     if (osys->filters)
438         filter_chain_Delete(osys->filters);
439 }
440
441 static void VoutDisplayResetRender(vout_display_t *vd)
442 {
443     VoutDisplayDestroyRender(vd);
444     VoutDisplayCreateRender(vd);
445 }
446 static void VoutDisplayEventMouse(vout_display_t *vd, int event, va_list args)
447 {
448     vout_display_owner_sys_t *osys = vd->owner.sys;
449
450     vlc_mutex_lock(&osys->lock);
451
452     /* */
453     vlc_mouse_t m = osys->mouse.state;
454     bool is_ignored = false;
455
456     switch (event) {
457     case VOUT_DISPLAY_EVENT_MOUSE_STATE: {
458         const int x = (int)va_arg(args, int);
459         const int y = (int)va_arg(args, int);
460         const int button_mask = (int)va_arg(args, int);
461
462         vlc_mouse_Init(&m);
463         m.i_x = x;
464         m.i_y = y;
465         m.i_pressed = button_mask;
466         break;
467     }
468     case VOUT_DISPLAY_EVENT_MOUSE_MOVED: {
469         const int x = (int)va_arg(args, int);
470         const int y = (int)va_arg(args, int);
471
472         //msg_Dbg(vd, "VoutDisplayEvent 'mouse' @%d,%d", x, y);
473
474         m.i_x = x;
475         m.i_y = y;
476         m.b_double_click = false;
477         break;
478     }
479     case VOUT_DISPLAY_EVENT_MOUSE_PRESSED:
480     case VOUT_DISPLAY_EVENT_MOUSE_RELEASED: {
481         const int button = (int)va_arg(args, int);
482         const int button_mask = 1 << button;
483
484         /* Ignore inconsistent event */
485         if ((event == VOUT_DISPLAY_EVENT_MOUSE_PRESSED  &&  (osys->mouse.state.i_pressed & button_mask)) ||
486             (event == VOUT_DISPLAY_EVENT_MOUSE_RELEASED && !(osys->mouse.state.i_pressed & button_mask))) {
487             is_ignored = true;
488             break;
489         }
490
491         /* */
492         msg_Dbg(vd, "VoutDisplayEvent 'mouse button' %d t=%d", button, event);
493
494         m.b_double_click = false;
495         if (event == VOUT_DISPLAY_EVENT_MOUSE_PRESSED)
496             m.i_pressed |= button_mask;
497         else
498             m.i_pressed &= ~button_mask;
499         break;
500     }
501     case VOUT_DISPLAY_EVENT_MOUSE_DOUBLE_CLICK:
502         msg_Dbg(vd, "VoutDisplayEvent 'double click'");
503
504         m.b_double_click = true;
505         break;
506     default:
507         assert(0);
508     }
509
510     if (is_ignored) {
511         vlc_mutex_unlock(&osys->lock);
512         return;
513     }
514
515     /* Emulate double-click if needed */
516     if (!vd->info.has_double_click &&
517         vlc_mouse_HasPressed(&osys->mouse.state, &m, MOUSE_BUTTON_LEFT)) {
518         const mtime_t i_date = mdate();
519
520         if (i_date - osys->mouse.last_pressed < osys->mouse.double_click_timeout ) {
521             m.b_double_click = true;
522             osys->mouse.last_pressed = 0;
523         } else {
524             osys->mouse.last_pressed = mdate();
525         }
526     }
527
528     /* */
529     osys->mouse.state = m;
530
531     /* */
532     osys->mouse.ch_activity = true;
533     if (!vd->info.has_hide_mouse)
534         osys->mouse.last_moved = mdate();
535
536     /* */
537     vout_SendEventMouseVisible(osys->vout);
538 #ifdef ALLOW_DUMMY_VOUT
539     DummyVoutSendDisplayEventMouse(osys->vout, &osys->vout_mouse, &m);
540 #else
541     vout_SendDisplayEventMouse(osys->vout, &m);
542 #endif
543     vlc_mutex_unlock(&osys->lock);
544 }
545
546 static void *VoutDisplayEventKeyDispatch(void *data)
547 {
548     vout_display_owner_sys_t *osys = data;
549
550     for (;;) {
551         block_t *event = block_FifoGet(osys->event.fifo);
552
553         int cancel = vlc_savecancel();
554
555         int key;
556         memcpy(&key, event->p_buffer, sizeof(key));
557         vout_SendEventKey(osys->vout, key);
558         block_Release(event);
559
560         vlc_restorecancel(cancel);
561     }
562 }
563
564 static void VoutDisplayEventKey(vout_display_t *vd, int key)
565 {
566     vout_display_owner_sys_t *osys = vd->owner.sys;
567
568     if (!osys->event.fifo) {
569         osys->event.fifo = block_FifoNew();
570         if (!osys->event.fifo)
571             return;
572         if (vlc_clone(&osys->event.thread, VoutDisplayEventKeyDispatch,
573                       osys, VLC_THREAD_PRIORITY_LOW)) {
574             block_FifoRelease(osys->event.fifo);
575             osys->event.fifo = NULL;
576             return;
577         }
578     }
579     block_t *event = block_Alloc(sizeof(key));
580     if (event) {
581         memcpy(event->p_buffer, &key, sizeof(key));
582         block_FifoPut(osys->event.fifo, event);
583     }
584 }
585
586 static void VoutDisplayEvent(vout_display_t *vd, int event, va_list args)
587 {
588     vout_display_owner_sys_t *osys = vd->owner.sys;
589
590     switch (event) {
591     case VOUT_DISPLAY_EVENT_CLOSE: {
592         msg_Dbg(vd, "VoutDisplayEvent 'close'");
593         vout_SendEventClose(osys->vout);
594         break;
595     }
596     case VOUT_DISPLAY_EVENT_KEY: {
597         const int key = (int)va_arg(args, int);
598         msg_Dbg(vd, "VoutDisplayEvent 'key' 0x%2.2x", key);
599         if (vd->info.has_event_thread)
600             vout_SendEventKey(osys->vout, key);
601         else
602             VoutDisplayEventKey(vd, key);
603         break;
604     }
605     case VOUT_DISPLAY_EVENT_MOUSE_STATE:
606     case VOUT_DISPLAY_EVENT_MOUSE_MOVED:
607     case VOUT_DISPLAY_EVENT_MOUSE_PRESSED:
608     case VOUT_DISPLAY_EVENT_MOUSE_RELEASED:
609     case VOUT_DISPLAY_EVENT_MOUSE_DOUBLE_CLICK:
610         VoutDisplayEventMouse(vd, event, args);
611         break;
612
613     case VOUT_DISPLAY_EVENT_FULLSCREEN: {
614         const int is_fullscreen = (int)va_arg(args, int);
615
616         msg_Dbg(vd, "VoutDisplayEvent 'fullscreen' %d", is_fullscreen);
617
618         vlc_mutex_lock(&osys->lock);
619         if (!is_fullscreen != !osys->is_fullscreen) {
620             osys->ch_fullscreen = true;
621             osys->is_fullscreen = is_fullscreen;
622         }
623         vlc_mutex_unlock(&osys->lock);
624         break;
625     }
626
627     case VOUT_DISPLAY_EVENT_WINDOW_STATE: {
628         const unsigned state = va_arg(args, unsigned);
629
630         msg_Dbg(vd, "VoutDisplayEvent 'window state' %u", state);
631
632         vlc_mutex_lock(&osys->lock);
633         if (state != osys->wm_state) {
634             osys->ch_wm_state = true;
635             osys->wm_state = state;
636         }
637         vlc_mutex_unlock(&osys->lock);
638         break;
639     }
640
641     case VOUT_DISPLAY_EVENT_DISPLAY_SIZE: {
642         const int width  = (int)va_arg(args, int);
643         const int height = (int)va_arg(args, int);
644         const bool is_fullscreen = (bool)va_arg(args, int);
645         msg_Dbg(vd, "VoutDisplayEvent 'resize' %dx%d %s",
646                 width, height, is_fullscreen ? "fullscreen" : "window");
647
648         /* */
649         vlc_mutex_lock(&osys->lock);
650
651         osys->ch_display_size       = true;
652         osys->display_width         = width;
653         osys->display_height        = height;
654         osys->display_is_fullscreen = is_fullscreen;
655         osys->display_is_forced     = false;
656
657         vlc_mutex_unlock(&osys->lock);
658         break;
659     }
660
661     case VOUT_DISPLAY_EVENT_PICTURES_INVALID: {
662         msg_Warn(vd, "VoutDisplayEvent 'pictures invalid'");
663
664         /* */
665         assert(vd->info.has_pictures_invalid);
666
667         vlc_mutex_lock(&osys->lock);
668         osys->reset_pictures = true;
669         vlc_mutex_unlock(&osys->lock);
670         break;
671     }
672     default:
673         msg_Err(vd, "VoutDisplayEvent received event %d", event);
674         /* TODO add an assert when all event are handled */
675         break;
676     }
677 }
678
679 static vout_window_t *VoutDisplayNewWindow(vout_display_t *vd, const vout_window_cfg_t *cfg)
680 {
681     vout_display_owner_sys_t *osys = vd->owner.sys;
682
683 #ifdef ALLOW_DUMMY_VOUT
684     if (!osys->vout->p) {
685         vout_window_cfg_t cfg_override = *cfg;
686
687         if (!var_InheritBool(osys->vout, "embedded-video"))
688             cfg_override.is_standalone = true;
689
690         return vout_window_New(VLC_OBJECT(osys->vout), "$window", &cfg_override);
691     }
692 #endif
693     return vout_NewDisplayWindow(osys->vout, vd, cfg);
694 }
695 static void VoutDisplayDelWindow(vout_display_t *vd, vout_window_t *window)
696 {
697     vout_display_owner_sys_t *osys = vd->owner.sys;
698
699 #ifdef ALLOW_DUMMY_VOUT
700     if (!osys->vout->p) {
701         if( window)
702             vout_window_Delete(window);
703         return;
704     }
705 #endif
706     vout_DeleteDisplayWindow(osys->vout, vd, window);
707 }
708
709 static void VoutDisplayFitWindow(vout_display_t *vd, bool default_size)
710 {
711     vout_display_owner_sys_t *osys = vd->owner.sys;
712     vout_display_cfg_t cfg = osys->cfg;
713
714     if (!cfg.is_display_filled)
715         return;
716
717     cfg.display.width = 0;
718     if (default_size) {
719         cfg.display.height = 0;
720     } else {
721         cfg.display.height = osys->height_saved;
722         cfg.zoom.num = 1;
723         cfg.zoom.den = 1;
724     }
725
726     unsigned display_width;
727     unsigned display_height;
728     vout_display_GetDefaultDisplaySize(&display_width, &display_height,
729                                        &vd->source, &cfg);
730
731     vlc_mutex_lock(&osys->lock);
732
733     osys->ch_display_size       = true;
734     osys->display_width         = display_width;
735     osys->display_height        = display_height;
736     osys->display_is_fullscreen = osys->cfg.is_fullscreen;
737     osys->display_is_forced     = true;
738
739     vlc_mutex_unlock(&osys->lock);
740 }
741
742 static void VoutDisplayCropRatio(int *left, int *top, int *right, int *bottom,
743                                  const video_format_t *source,
744                                  unsigned num, unsigned den)
745 {
746     unsigned scaled_width  = (uint64_t)source->i_visible_height * num * source->i_sar_den / den / source->i_sar_num;
747     unsigned scaled_height = (uint64_t)source->i_visible_width  * den * source->i_sar_num / num / source->i_sar_den;
748
749     if (scaled_width < source->i_visible_width) {
750         *left   = (source->i_visible_width - scaled_width) / 2;
751         *top    = 0;
752         *right  = *left + scaled_width;
753         *bottom = *top  + source->i_visible_height;
754     } else {
755         *left   = 0;
756         *top    = (source->i_visible_height - scaled_height) / 2;
757         *right  = *left + source->i_visible_width;
758         *bottom = *top  + scaled_height;
759     }
760 }
761
762 void vout_ManageDisplay(vout_display_t *vd, bool allow_reset_pictures)
763 {
764     vout_display_owner_sys_t *osys = vd->owner.sys;
765
766     vout_display_Manage(vd);
767
768     /* Handle mouse timeout */
769     const mtime_t date = mdate();
770     bool  hide_mouse = false;
771
772     vlc_mutex_lock(&osys->lock);
773
774     if (!osys->mouse.is_hidden &&
775         osys->mouse.last_moved + osys->mouse.hide_timeout < date) {
776         osys->mouse.is_hidden = hide_mouse = true;
777     } else if (osys->mouse.ch_activity) {
778         osys->mouse.is_hidden = false;
779     }
780     osys->mouse.ch_activity = false;
781     vlc_mutex_unlock(&osys->lock);
782
783     if (hide_mouse) {
784         if (!vd->info.has_hide_mouse) {
785             msg_Dbg(vd, "auto hiding mouse");
786             vout_display_Control(vd, VOUT_DISPLAY_HIDE_MOUSE);
787         }
788         vout_SendEventMouseHidden(osys->vout);
789     }
790
791     bool reset_render = false;
792     for (;;) {
793
794         vlc_mutex_lock(&osys->lock);
795
796         bool ch_fullscreen  = osys->ch_fullscreen;
797         bool is_fullscreen  = osys->is_fullscreen;
798         osys->ch_fullscreen = false;
799
800         bool ch_wm_state  = osys->ch_wm_state;
801         unsigned wm_state  = osys->wm_state;
802         osys->ch_wm_state = false;
803
804         bool ch_display_size       = osys->ch_display_size;
805         int  display_width         = osys->display_width;
806         int  display_height        = osys->display_height;
807         bool display_is_fullscreen = osys->display_is_fullscreen;
808         bool display_is_forced     = osys->display_is_forced;
809         osys->ch_display_size = false;
810
811         bool reset_pictures;
812         if (allow_reset_pictures) {
813             reset_pictures = osys->reset_pictures;
814             osys->reset_pictures = false;
815         } else {
816             reset_pictures = false;
817         }
818
819         vlc_mutex_unlock(&osys->lock);
820
821         if (!ch_fullscreen &&
822             !ch_display_size &&
823             !reset_pictures &&
824             !osys->ch_display_filled &&
825             !osys->ch_zoom &&
826             !ch_wm_state &&
827             !osys->ch_sar &&
828             !osys->ch_crop) {
829
830             if (!osys->cfg.is_fullscreen && osys->fit_window != 0) {
831                 VoutDisplayFitWindow(vd, osys->fit_window == -1);
832                 osys->fit_window = 0;
833                 continue;
834             }
835             break;
836         }
837
838         /* */
839         if (ch_fullscreen) {
840             vout_display_cfg_t cfg = osys->cfg;
841
842             cfg.is_fullscreen  = is_fullscreen;
843             cfg.display.width  = cfg.is_fullscreen ? 0 : osys->width_saved;
844             cfg.display.height = cfg.is_fullscreen ? 0 : osys->height_saved;
845
846             if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_FULLSCREEN, &cfg)) {
847                 msg_Err(vd, "Failed to set fullscreen");
848                 is_fullscreen = osys->cfg.is_fullscreen;
849             } else if (!is_fullscreen) {
850                 vout_display_Control(vd, VOUT_DISPLAY_CHANGE_DISPLAY_SIZE, &cfg, true);
851             }
852             osys->cfg.is_fullscreen = is_fullscreen;
853
854             /* */
855             vout_SendEventFullscreen(osys->vout, osys->cfg.is_fullscreen);
856         }
857
858         /* */
859         if (ch_display_size) {
860             vout_display_cfg_t cfg = osys->cfg;
861             cfg.display.width  = display_width;
862             cfg.display.height = display_height;
863
864             if (!cfg.is_fullscreen != !display_is_fullscreen ||
865                 vout_display_Control(vd, VOUT_DISPLAY_CHANGE_DISPLAY_SIZE, &cfg, display_is_forced)) {
866                 if (!cfg.is_fullscreen == !display_is_fullscreen)
867                     msg_Err(vd, "Failed to resize display");
868
869                 /* We ignore the resized */
870                 display_width  = osys->cfg.display.width;
871                 display_height = osys->cfg.display.height;
872             }
873             osys->cfg.display.width  = display_width;
874             osys->cfg.display.height = display_height;
875
876             if (!display_is_fullscreen) {
877                 osys->width_saved  = display_width;
878                 osys->height_saved = display_height;
879             }
880         }
881         /* */
882         if (osys->ch_display_filled) {
883             vout_display_cfg_t cfg = osys->cfg;
884
885             cfg.is_display_filled = osys->is_display_filled;
886
887             if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_DISPLAY_FILLED, &cfg)) {
888                 msg_Err(vd, "Failed to change display filled state");
889                 osys->is_display_filled = osys->cfg.is_display_filled;
890             }
891             osys->cfg.is_display_filled = osys->is_display_filled;
892             osys->ch_display_filled = false;
893
894             vout_SendEventDisplayFilled(osys->vout, osys->cfg.is_display_filled);
895         }
896         /* */
897         if (osys->ch_zoom) {
898             vout_display_cfg_t cfg = osys->cfg;
899
900             cfg.zoom.num = osys->zoom.num;
901             cfg.zoom.den = osys->zoom.den;
902
903             if (10 * cfg.zoom.num <= cfg.zoom.den) {
904                 cfg.zoom.num = 1;
905                 cfg.zoom.den = 10;
906             } else if (cfg.zoom.num >= 10 * cfg.zoom.den) {
907                 cfg.zoom.num = 10;
908                 cfg.zoom.den = 1;
909             }
910
911             if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_ZOOM, &cfg)) {
912                 msg_Err(vd, "Failed to change zoom");
913                 osys->zoom.num = osys->cfg.zoom.num;
914                 osys->zoom.den = osys->cfg.zoom.den;
915             } else {
916                 osys->fit_window = -1;
917             }
918
919             osys->cfg.zoom.num = osys->zoom.num;
920             osys->cfg.zoom.den = osys->zoom.den;
921             osys->ch_zoom = false;
922
923             vout_SendEventZoom(osys->vout, osys->cfg.zoom.num, osys->cfg.zoom.den);
924         }
925         /* */
926         if (ch_wm_state) {
927             if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_WINDOW_STATE, wm_state)) {
928                 msg_Err(vd, "Failed to set on top");
929                 wm_state = osys->wm_state;
930             }
931             osys->wm_state_initial = wm_state;
932
933             /* */
934             vout_SendEventOnTop(osys->vout, osys->wm_state_initial);
935         }
936         /* */
937         if (osys->ch_sar) {
938             video_format_t source = vd->source;
939
940             if (osys->sar.num > 0 && osys->sar.den > 0) {
941                 source.i_sar_num = osys->sar.num;
942                 source.i_sar_den = osys->sar.den;
943             } else {
944                 source.i_sar_num = osys->source.i_sar_num;
945                 source.i_sar_den = osys->source.i_sar_den;
946             }
947
948             if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_SOURCE_ASPECT, &source)) {
949                 /* There nothing much we can do. The only reason a vout display
950                  * does not support it is because it need the core to add black border
951                  * to the video for it.
952                  * TODO add black borders ?
953                  */
954                 msg_Err(vd, "Failed to change source AR");
955                 source = vd->source;
956             } else if (!osys->fit_window) {
957                 osys->fit_window = 1;
958             }
959             vd->source = source;
960             osys->sar.num = source.i_sar_num;
961             osys->sar.den = source.i_sar_den;
962             osys->ch_sar  = false;
963
964             /* */
965             if (osys->sar.num == osys->source.i_sar_num &&
966                 osys->sar.den == osys->source.i_sar_den)
967             {
968                 vout_SendEventSourceAspect(osys->vout, 0, 0);
969             }
970             else
971             {
972                 unsigned dar_num, dar_den;
973                 vlc_ureduce( &dar_num, &dar_den,
974                              osys->sar.num * vd->source.i_visible_width,
975                              osys->sar.den * vd->source.i_visible_height,
976                              65536);
977                 vout_SendEventSourceAspect(osys->vout, dar_num, dar_den);
978             }
979             /* If a crop ratio is requested, recompute the parameters */
980             if (osys->crop.num > 0 && osys->crop.den > 0)
981                 osys->ch_crop = true;
982         }
983         /* */
984         if (osys->ch_crop) {
985             video_format_t source = vd->source;
986
987             unsigned crop_num = osys->crop.num;
988             unsigned crop_den = osys->crop.den;
989             if (crop_num > 0 && crop_den > 0) {
990                 video_format_t fmt = osys->source;
991                 fmt.i_sar_num = source.i_sar_num;
992                 fmt.i_sar_den = source.i_sar_den;
993                 VoutDisplayCropRatio(&osys->crop.left,  &osys->crop.top,
994                                      &osys->crop.right, &osys->crop.bottom,
995                                      &fmt, crop_num, crop_den);
996             }
997             const int right_max  = osys->source.i_x_offset + osys->source.i_visible_width;
998             const int bottom_max = osys->source.i_y_offset + osys->source.i_visible_height;
999 #define __CLIP(v, a, b) __MAX(__MIN(v, b), a)
1000             int left   = __CLIP((int)osys->source.i_x_offset + osys->crop.left,
1001                                 0, right_max - 1);
1002             int top    = __CLIP((int)osys->source.i_y_offset + osys->crop.top,
1003                                 0, bottom_max - 1);
1004             int right, bottom;
1005             if (osys->crop.right <= 0)
1006                 right = (int)(osys->source.i_x_offset + osys->source.i_visible_width) + osys->crop.right;
1007             else
1008                 right = (int)osys->source.i_x_offset + osys->crop.right;
1009             right = __CLIP(right, left + 1, right_max);
1010             if (osys->crop.bottom <= 0)
1011                 bottom = (int)(osys->source.i_y_offset + osys->source.i_visible_height) + osys->crop.bottom;
1012             else
1013                 bottom = (int)osys->source.i_y_offset + osys->crop.bottom;
1014             bottom = __CLIP(bottom, top + 1, bottom_max);
1015
1016             source.i_x_offset       = left;
1017             source.i_y_offset       = top;
1018             source.i_visible_width  = right - left;
1019             source.i_visible_height = bottom - top;
1020 #undef __CLIP
1021             video_format_Print(VLC_OBJECT(vd), "SOURCE ", &osys->source);
1022             video_format_Print(VLC_OBJECT(vd), "CROPPED", &source);
1023             if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_SOURCE_CROP, &source)) {
1024                 msg_Err(vd, "Failed to change source crop TODO implement crop at core");
1025
1026                 source = vd->source;
1027                 crop_num = osys->crop_saved.num;
1028                 crop_den = osys->crop_saved.den;
1029                 /* FIXME implement cropping in the core if not supported by the
1030                  * vout module (easy)
1031                  */
1032             } else if (!osys->fit_window) {
1033                 osys->fit_window = 1;
1034             }
1035             vd->source = source;
1036             osys->crop.left   = source.i_x_offset - osys->source.i_x_offset;
1037             osys->crop.top    = source.i_y_offset - osys->source.i_y_offset;
1038             /* FIXME for right/bottom we should keep the 'type' border vs window */
1039             osys->crop.right  = (source.i_x_offset + source.i_visible_width) -
1040                                 (osys->source.i_x_offset + osys->source.i_visible_width);
1041             osys->crop.bottom = (source.i_y_offset + source.i_visible_height) -
1042                                 (osys->source.i_y_offset + osys->source.i_visible_height);
1043             osys->crop.num    = crop_num;
1044             osys->crop.den    = crop_den;
1045             osys->ch_crop = false;
1046
1047             vout_SendEventSourceCrop(osys->vout,
1048                                      osys->crop.num, osys->crop.den,
1049                                      osys->crop.left, osys->crop.top,
1050                                      -osys->crop.right, -osys->crop.bottom);
1051         }
1052
1053         /* */
1054         if (reset_pictures) {
1055             if (vout_display_Control(vd, VOUT_DISPLAY_RESET_PICTURES)) {
1056                 /* FIXME what to do here ? */
1057                 msg_Err(vd, "Failed to reset pictures (probably fatal)");
1058             }
1059             reset_render = true;
1060         }
1061     }
1062     if (reset_render)
1063         VoutDisplayResetRender(vd);
1064 }
1065
1066 bool vout_AreDisplayPicturesInvalid(vout_display_t *vd)
1067 {
1068     vout_display_owner_sys_t *osys = vd->owner.sys;
1069
1070     vlc_mutex_lock(&osys->lock);
1071     const bool reset_pictures = osys->reset_pictures;
1072     vlc_mutex_unlock(&osys->lock);
1073
1074     return reset_pictures;
1075 }
1076
1077 bool vout_IsDisplayFiltered(vout_display_t *vd)
1078 {
1079     vout_display_owner_sys_t *osys = vd->owner.sys;
1080
1081     return osys->filters != NULL;
1082 }
1083
1084 picture_t *vout_FilterDisplay(vout_display_t *vd, picture_t *picture)
1085 {
1086     vout_display_owner_sys_t *osys = vd->owner.sys;
1087
1088     assert(osys->filters);
1089     return filter_chain_VideoFilter(osys->filters, picture);
1090 }
1091
1092 void vout_UpdateDisplaySourceProperties(vout_display_t *vd, const video_format_t *source)
1093 {
1094     vout_display_owner_sys_t *osys = vd->owner.sys;
1095
1096     if (source->i_sar_num * osys->source.i_sar_den !=
1097         source->i_sar_den * osys->source.i_sar_num) {
1098
1099         osys->source.i_sar_num = source->i_sar_num;
1100         osys->source.i_sar_den = source->i_sar_den;
1101         vlc_ureduce(&osys->source.i_sar_num, &osys->source.i_sar_den,
1102                     osys->source.i_sar_num, osys->source.i_sar_den, 0);
1103
1104         /* FIXME it will override any AR that the user would have forced */
1105         osys->ch_sar = true;
1106         osys->sar.num = osys->source.i_sar_num;
1107         osys->sar.den = osys->source.i_sar_den;
1108     }
1109     if (source->i_x_offset       != osys->source.i_x_offset ||
1110         source->i_y_offset       != osys->source.i_y_offset ||
1111         source->i_visible_width  != osys->source.i_visible_width ||
1112         source->i_visible_height != osys->source.i_visible_height) {
1113
1114         video_format_CopyCrop(&osys->source, source);
1115
1116         /* Force the vout to reapply the current user crop settings over the new decoder
1117          * crop settings. */
1118         osys->ch_crop = true;
1119     }
1120 }
1121
1122 void vout_SetDisplayFullscreen(vout_display_t *vd, bool is_fullscreen)
1123 {
1124     vout_display_owner_sys_t *osys = vd->owner.sys;
1125
1126     vlc_mutex_lock(&osys->lock);
1127     if (!osys->is_fullscreen != !is_fullscreen) {
1128         osys->ch_fullscreen = true;
1129         osys->is_fullscreen = is_fullscreen;
1130     }
1131     vlc_mutex_unlock(&osys->lock);
1132 }
1133
1134 void vout_SetDisplayFilled(vout_display_t *vd, bool is_filled)
1135 {
1136     vout_display_owner_sys_t *osys = vd->owner.sys;
1137
1138     if (!osys->is_display_filled != !is_filled) {
1139         osys->ch_display_filled = true;
1140         osys->is_display_filled = is_filled;
1141     }
1142 }
1143
1144 void vout_SetDisplayZoom(vout_display_t *vd, int num, int den)
1145 {
1146     vout_display_owner_sys_t *osys = vd->owner.sys;
1147
1148     if (osys->is_display_filled ||
1149         osys->zoom.num != num || osys->zoom.den != den) {
1150         osys->ch_zoom = true;
1151         osys->zoom.num = num;
1152         osys->zoom.den = den;
1153     }
1154 }
1155
1156 void vout_SetWindowState(vout_display_t *vd, unsigned state)
1157 {
1158     vout_display_owner_sys_t *osys = vd->owner.sys;
1159
1160     vlc_mutex_lock(&osys->lock);
1161     if (osys->wm_state != state) {
1162         osys->ch_wm_state = true;
1163         osys->wm_state = state;
1164     }
1165     vlc_mutex_unlock(&osys->lock);
1166 }
1167
1168 void vout_SetDisplayAspect(vout_display_t *vd, unsigned dar_num, unsigned dar_den)
1169 {
1170     vout_display_owner_sys_t *osys = vd->owner.sys;
1171
1172     unsigned sar_num, sar_den;
1173     if (dar_num > 0 && dar_den > 0) {
1174         sar_num = dar_num * osys->source.i_visible_height;
1175         sar_den = dar_den * osys->source.i_visible_width;
1176         vlc_ureduce(&sar_num, &sar_den, sar_num, sar_den, 0);
1177     } else {
1178         sar_num = 0;
1179         sar_den = 0;
1180     }
1181
1182     if (osys->sar.num != sar_num || osys->sar.den != sar_den) {
1183         osys->ch_sar = true;
1184         osys->sar.num = sar_num;
1185         osys->sar.den = sar_den;
1186     }
1187 }
1188 void vout_SetDisplayCrop(vout_display_t *vd,
1189                          unsigned crop_num, unsigned crop_den,
1190                          unsigned left, unsigned top, int right, int bottom)
1191 {
1192     vout_display_owner_sys_t *osys = vd->owner.sys;
1193
1194     if (osys->crop.left  != (int)left  || osys->crop.top != (int)top ||
1195         osys->crop.right != right || osys->crop.bottom != bottom ||
1196         (crop_num > 0 && crop_den > 0 &&
1197          (crop_num != osys->crop.num || crop_den != osys->crop.den))) {
1198
1199         osys->crop.left   = left;
1200         osys->crop.top    = top;
1201         osys->crop.right  = right;
1202         osys->crop.bottom = bottom;
1203         osys->crop.num    = crop_num;
1204         osys->crop.den    = crop_den;
1205
1206         osys->ch_crop = true;
1207     }
1208 }
1209 vout_opengl_t *vout_GetDisplayOpengl(vout_display_t *vd)
1210 {
1211     vout_opengl_t *gl;
1212     if (vout_display_Control(vd, VOUT_DISPLAY_GET_OPENGL, &gl))
1213         return NULL;
1214     return gl;
1215 }
1216
1217 static vout_display_t *DisplayNew(vout_thread_t *vout,
1218                                   const video_format_t *source_org,
1219                                   const vout_display_state_t *state,
1220                                   const char *module,
1221                                   bool is_wrapper, vout_display_t *wrapper,
1222                                   mtime_t double_click_timeout,
1223                                   mtime_t hide_timeout,
1224                                   const vout_display_owner_t *owner_ptr)
1225 {
1226     /* */
1227     vout_display_owner_sys_t *osys = calloc(1, sizeof(*osys));
1228     vout_display_cfg_t *cfg = &osys->cfg;
1229
1230     *cfg = state->cfg;
1231     osys->wm_state_initial = -1;
1232     osys->sar_initial.num = state->sar.num;
1233     osys->sar_initial.den = state->sar.den;
1234     vout_display_GetDefaultDisplaySize(&cfg->display.width, &cfg->display.height,
1235                                        source_org, cfg);
1236
1237     osys->vout = vout;
1238     osys->is_wrapper = is_wrapper;
1239     osys->wrapper = wrapper;
1240
1241     vlc_mutex_init(&osys->lock);
1242
1243     vlc_mouse_Init(&osys->mouse.state);
1244     osys->mouse.last_moved = mdate();
1245     osys->mouse.double_click_timeout = double_click_timeout;
1246     osys->mouse.hide_timeout = hide_timeout;
1247     osys->is_fullscreen  = cfg->is_fullscreen;
1248     osys->display_width  = cfg->display.width;
1249     osys->display_height = cfg->display.height;
1250     osys->is_display_filled = cfg->is_display_filled;
1251     osys->width_saved    = cfg->display.width;
1252     osys->height_saved   = cfg->display.height;
1253     if (osys->is_fullscreen) {
1254         vout_display_cfg_t cfg_windowed = *cfg;
1255         cfg_windowed.is_fullscreen  = false;
1256         cfg_windowed.display.width  = 0;
1257         cfg_windowed.display.height = 0;
1258         vout_display_GetDefaultDisplaySize(&osys->width_saved,
1259                                            &osys->height_saved,
1260                                            source_org, &cfg_windowed);
1261     }
1262     osys->zoom.num = cfg->zoom.num;
1263     osys->zoom.den = cfg->zoom.den;
1264     osys->wm_state = state->wm_state;
1265     osys->fit_window = 0;
1266     osys->event.fifo = NULL;
1267
1268     osys->source = *source_org;
1269     osys->crop.left   = 0;
1270     osys->crop.top    = 0;
1271     osys->crop.right  = 0;
1272     osys->crop.bottom = 0;
1273     osys->crop_saved.num = 0;
1274     osys->crop_saved.den = 0;
1275     osys->crop.num = 0;
1276     osys->crop.den = 0;
1277
1278     osys->sar.num = osys->sar_initial.num ? osys->sar_initial.num : source_org->i_sar_num;
1279     osys->sar.den = osys->sar_initial.den ? osys->sar_initial.den : source_org->i_sar_den;
1280 #ifdef ALLOW_DUMMY_VOUT
1281     vlc_mouse_Init(&osys->vout_mouse);
1282 #endif
1283
1284     vout_display_owner_t owner;
1285     if (owner_ptr) {
1286         owner = *owner_ptr;
1287     } else {
1288         owner.event      = VoutDisplayEvent;
1289         owner.window_new = VoutDisplayNewWindow;
1290         owner.window_del = VoutDisplayDelWindow;
1291     }
1292     owner.sys = osys;
1293
1294     /* */
1295     video_format_t source = *source_org;
1296
1297     source.i_x_offset = 0;
1298     source.i_y_offset = 0;
1299     source.i_visible_width  = source.i_width;
1300     source.i_visible_height = source.i_height;
1301
1302     vout_display_t *p_display = vout_display_New(VLC_OBJECT(vout),
1303                                                  module, !is_wrapper,
1304                                                  &source, cfg, &owner);
1305     if (!p_display) {
1306         free(osys);
1307         return NULL;
1308     }
1309
1310     VoutDisplayCreateRender(p_display);
1311
1312     /* Setup delayed request */
1313     if (osys->sar.num != source.i_sar_num ||
1314         osys->sar.den != source.i_sar_den)
1315         osys->ch_sar = true;
1316     if (osys->wm_state != osys->wm_state_initial)
1317         osys->ch_wm_state = true;
1318     if (source.i_x_offset       != source_org->i_x_offset ||
1319         source.i_y_offset       != source_org->i_y_offset ||
1320         source.i_visible_width  != source_org->i_visible_width ||
1321         source.i_visible_height != source_org->i_visible_height)
1322         osys->ch_crop = true;
1323
1324     return p_display;
1325 }
1326
1327 void vout_DeleteDisplay(vout_display_t *vd, vout_display_state_t *state)
1328 {
1329     vout_display_owner_sys_t *osys = vd->owner.sys;
1330
1331     if (state) {
1332         if (!osys->is_wrapper )
1333             state->cfg = osys->cfg;
1334         state->wm_state = osys->wm_state;
1335         state->sar.num  = osys->sar_initial.num;
1336         state->sar.den  = osys->sar_initial.den;
1337     }
1338
1339     VoutDisplayDestroyRender(vd);
1340     if (osys->is_wrapper)
1341         SplitterClose(vd);
1342     vout_display_Delete(vd);
1343     if (osys->event.fifo) {
1344         vlc_cancel(osys->event.thread);
1345         vlc_join(osys->event.thread, NULL);
1346         block_FifoRelease(osys->event.fifo);
1347     }
1348     vlc_mutex_destroy(&osys->lock);
1349     free(osys);
1350 }
1351
1352 /*****************************************************************************
1353  *
1354  *****************************************************************************/
1355 vout_display_t *vout_NewDisplay(vout_thread_t *vout,
1356                                 const video_format_t *source,
1357                                 const vout_display_state_t *state,
1358                                 const char *module,
1359                                 mtime_t double_click_timeout,
1360                                 mtime_t hide_timeout)
1361 {
1362     return DisplayNew(vout, source, state, module, false, NULL,
1363                       double_click_timeout, hide_timeout, NULL);
1364 }
1365
1366 /*****************************************************************************
1367  *
1368  *****************************************************************************/
1369 struct vout_display_sys_t {
1370     picture_pool_t   *pool;
1371     video_splitter_t *splitter;
1372
1373     /* */
1374     int            count;
1375     picture_t      **picture;
1376     vout_display_t **display;
1377 };
1378 struct video_splitter_owner_t {
1379     vout_display_t *wrapper;
1380 };
1381
1382 static vout_window_t *SplitterNewWindow(vout_display_t *vd, const vout_window_cfg_t *cfg_ptr)
1383 {
1384     vout_display_owner_sys_t *osys = vd->owner.sys;
1385
1386     vout_window_cfg_t cfg = *cfg_ptr;
1387     cfg.is_standalone = true;
1388     cfg.x += 0;//output->window.i_x; FIXME
1389     cfg.y += 0;//output->window.i_y;
1390
1391     return vout_NewDisplayWindow(osys->vout, vd, &cfg);
1392 }
1393 static void SplitterDelWindow(vout_display_t *vd, vout_window_t *window)
1394 {
1395     vout_display_owner_sys_t *osys = vd->owner.sys;
1396
1397     vout_DeleteDisplayWindow(osys->vout, vd, window);
1398 }
1399 static void SplitterEvent(vout_display_t *vd, int event, va_list args)
1400 {
1401     //vout_display_owner_sys_t *osys = vd->owner.sys;
1402
1403     switch (event) {
1404 #if 0
1405     case VOUT_DISPLAY_EVENT_MOUSE_STATE:
1406     case VOUT_DISPLAY_EVENT_MOUSE_MOVED:
1407     case VOUT_DISPLAY_EVENT_MOUSE_PRESSED:
1408     case VOUT_DISPLAY_EVENT_MOUSE_RELEASED:
1409         /* TODO */
1410         break;
1411 #endif
1412     case VOUT_DISPLAY_EVENT_MOUSE_DOUBLE_CLICK:
1413     case VOUT_DISPLAY_EVENT_KEY:
1414     case VOUT_DISPLAY_EVENT_CLOSE:
1415     case VOUT_DISPLAY_EVENT_FULLSCREEN:
1416     case VOUT_DISPLAY_EVENT_DISPLAY_SIZE:
1417     case VOUT_DISPLAY_EVENT_PICTURES_INVALID:
1418         VoutDisplayEvent(vd, event, args);
1419         break;
1420
1421     default:
1422         msg_Err(vd, "SplitterEvent TODO");
1423         break;
1424     }
1425 }
1426
1427 static picture_pool_t *SplitterPool(vout_display_t *vd, unsigned count)
1428 {
1429     vout_display_sys_t *sys = vd->sys;
1430     if (!sys->pool)
1431         sys->pool = picture_pool_NewFromFormat(&vd->fmt, count);
1432     return sys->pool;
1433 }
1434 static void SplitterPrepare(vout_display_t *vd, picture_t *picture)
1435 {
1436     vout_display_sys_t *sys = vd->sys;
1437
1438     picture_Hold(picture);
1439
1440     if (video_splitter_Filter(sys->splitter, sys->picture, picture)) {
1441         for (int i = 0; i < sys->count; i++)
1442             sys->picture[i] = NULL;
1443         picture_Release(picture);
1444         return;
1445     }
1446
1447     for (int i = 0; i < sys->count; i++) {
1448         if (vout_IsDisplayFiltered(sys->display[i]))
1449             sys->picture[i] = vout_FilterDisplay(sys->display[i], sys->picture[i]);
1450         if (sys->picture[i])
1451             vout_display_Prepare(sys->display[i], sys->picture[i]);
1452     }
1453 }
1454 static void SplitterDisplay(vout_display_t *vd, picture_t *picture)
1455 {
1456     vout_display_sys_t *sys = vd->sys;
1457
1458     for (int i = 0; i < sys->count; i++) {
1459         if (sys->picture[i])
1460             vout_display_Display(sys->display[i], sys->picture[i]);
1461     }
1462     picture_Release(picture);
1463 }
1464 static int SplitterControl(vout_display_t *vd, int query, va_list args)
1465 {
1466     return VLC_EGENERIC;
1467 }
1468 static void SplitterManage(vout_display_t *vd)
1469 {
1470     vout_display_sys_t *sys = vd->sys;
1471
1472     for (int i = 0; i < sys->count; i++)
1473         vout_ManageDisplay(sys->display[i], true);
1474 }
1475
1476 static int SplitterPictureNew(video_splitter_t *splitter, picture_t *picture[])
1477 {
1478     vout_display_sys_t *wsys = splitter->p_owner->wrapper->sys;
1479
1480     for (int i = 0; i < wsys->count; i++) {
1481         if (vout_IsDisplayFiltered(wsys->display[i])) {
1482             /* TODO use a pool ? */
1483             picture[i] = picture_NewFromFormat(&wsys->display[i]->source);
1484         } else {
1485             picture_pool_t *pool = vout_display_Pool(wsys->display[i], 1);
1486             picture[i] = pool ? picture_pool_Get(pool) : NULL;
1487         }
1488         if (!picture[i]) {
1489             for (int j = 0; j < i; j++)
1490                 picture_Release(picture[j]);
1491             return VLC_EGENERIC;
1492         }
1493     }
1494     return VLC_SUCCESS;
1495 }
1496 static void SplitterPictureDel(video_splitter_t *splitter, picture_t *picture[])
1497 {
1498     vout_display_sys_t *wsys = splitter->p_owner->wrapper->sys;
1499
1500     for (int i = 0; i < wsys->count; i++)
1501         picture_Release(picture[i]);
1502 }
1503 static void SplitterClose(vout_display_t *vd)
1504 {
1505     vout_display_sys_t *sys = vd->sys;
1506
1507     /* */
1508     video_splitter_t *splitter = sys->splitter;
1509     free(splitter->p_owner);
1510     video_splitter_Delete(splitter);
1511
1512     if (sys->pool)
1513         picture_pool_Delete(sys->pool);
1514
1515     /* */
1516     for (int i = 0; i < sys->count; i++)
1517         vout_DeleteDisplay(sys->display[i], NULL);
1518     TAB_CLEAN(sys->count, sys->display);
1519     free(sys->picture);
1520
1521     free(sys);
1522 }
1523
1524 vout_display_t *vout_NewSplitter(vout_thread_t *vout,
1525                                  const video_format_t *source,
1526                                  const vout_display_state_t *state,
1527                                  const char *module,
1528                                  const char *splitter_module,
1529                                  mtime_t double_click_timeout,
1530                                  mtime_t hide_timeout)
1531 {
1532     video_splitter_t *splitter =
1533         video_splitter_New(VLC_OBJECT(vout), splitter_module, source);
1534     if (!splitter)
1535         return NULL;
1536
1537     /* */
1538     vout_display_t *wrapper =
1539         DisplayNew(vout, source, state, module, true, NULL,
1540                     double_click_timeout, hide_timeout, NULL);
1541     if (!wrapper) {
1542         video_splitter_Delete(splitter);
1543         return NULL;
1544     }
1545     vout_display_sys_t *sys = malloc(sizeof(*sys));
1546     if (!sys)
1547         abort();
1548     sys->picture = calloc(splitter->i_output, sizeof(*sys->picture));
1549     if (!sys->picture )
1550         abort();
1551     sys->splitter = splitter;
1552     sys->pool     = NULL;
1553
1554     wrapper->pool    = SplitterPool;
1555     wrapper->prepare = SplitterPrepare;
1556     wrapper->display = SplitterDisplay;
1557     wrapper->control = SplitterControl;
1558     wrapper->manage  = SplitterManage;
1559     wrapper->sys     = sys;
1560
1561     /* */
1562     video_splitter_owner_t *owner = malloc(sizeof(*owner));
1563     if (!owner)
1564         abort();
1565     owner->wrapper = wrapper;
1566     splitter->p_owner = owner;
1567     splitter->pf_picture_new = SplitterPictureNew;
1568     splitter->pf_picture_del = SplitterPictureDel;
1569
1570     /* */
1571     TAB_INIT(sys->count, sys->display);
1572     for (int i = 0; i < splitter->i_output; i++) {
1573         vout_display_owner_t owner;
1574
1575         owner.event      = SplitterEvent;
1576         owner.window_new = SplitterNewWindow;
1577         owner.window_del = SplitterDelWindow;
1578
1579         const video_splitter_output_t *output = &splitter->p_output[i];
1580         vout_display_state_t ostate;
1581
1582         memset(&ostate, 0, sizeof(ostate));
1583         ostate.cfg.is_fullscreen = false;
1584         ostate.cfg.display = state->cfg.display;
1585         ostate.cfg.align.horizontal = 0; /* TODO */
1586         ostate.cfg.align.vertical = 0; /* TODO */
1587         ostate.cfg.is_display_filled = true;
1588         ostate.cfg.zoom.num = 1;
1589         ostate.cfg.zoom.den = 1;
1590
1591         vout_display_t *vd = DisplayNew(vout, &output->fmt, &ostate,
1592                                            output->psz_module ? output->psz_module : module,
1593                                            false, wrapper,
1594                                            double_click_timeout, hide_timeout, &owner);
1595         if (!vd) {
1596             vout_DeleteDisplay(wrapper, NULL);
1597             return NULL;
1598         }
1599         TAB_APPEND(sys->count, sys->display, vd);
1600     }
1601
1602     return wrapper;
1603 }
1604
1605 /*****************************************************************************
1606  * TODO move out
1607  *****************************************************************************/
1608 #include "vout_internal.h"
1609 void vout_SendDisplayEventMouse(vout_thread_t *vout, const vlc_mouse_t *m)
1610 {
1611     vlc_mouse_t tmp1, tmp2;
1612
1613     /* The check on spu is needed as long as ALLOW_DUMMY_VOUT is defined */
1614     if (vout->p->spu && spu_ProcessMouse( vout->p->spu, m, &vout->p->display.vd->source))
1615         return;
1616
1617     vlc_mutex_lock( &vout->p->filter.lock );
1618     if (vout->p->filter.chain_static && vout->p->filter.chain_interactive) {
1619         if (!filter_chain_MouseFilter(vout->p->filter.chain_interactive, &tmp1, m))
1620             m = &tmp1;
1621         if (!filter_chain_MouseFilter(vout->p->filter.chain_static,      &tmp2, m))
1622             m = &tmp2;
1623     }
1624     vlc_mutex_unlock( &vout->p->filter.lock );
1625
1626     if (vlc_mouse_HasMoved(&vout->p->mouse, m)) {
1627         vout_SendEventMouseMoved(vout, m->i_x, m->i_y);
1628     }
1629     if (vlc_mouse_HasButton(&vout->p->mouse, m)) {
1630         static const int buttons[] = {
1631             MOUSE_BUTTON_LEFT,
1632             MOUSE_BUTTON_CENTER,
1633             MOUSE_BUTTON_RIGHT,
1634             MOUSE_BUTTON_WHEEL_UP,
1635             MOUSE_BUTTON_WHEEL_DOWN,
1636             -1
1637         };
1638         for (int i = 0; buttons[i] >= 0; i++) {
1639             const int button = buttons[i];
1640             if (vlc_mouse_HasPressed(&vout->p->mouse, m, button))
1641                 vout_SendEventMousePressed(vout, button);
1642             else if (vlc_mouse_HasReleased(&vout->p->mouse, m, button))
1643                 vout_SendEventMouseReleased(vout, button);
1644         }
1645     }
1646     if (m->b_double_click)
1647         vout_SendEventMouseDoubleClick(vout);
1648     vout->p->mouse = *m;
1649 }
1650 #ifdef ALLOW_DUMMY_VOUT
1651 static void DummyVoutSendDisplayEventMouse(vout_thread_t *vout, vlc_mouse_t *fallback, const vlc_mouse_t *m)
1652 {
1653     vout_thread_sys_t p;
1654
1655     if (!vout->p) {
1656         p.mouse = *fallback;
1657         vlc_mutex_init(&p.filter.lock);
1658         p.filter.chain_static = NULL;
1659         p.filter.chain_interactive = NULL;
1660         p.spu = NULL;
1661         vout->p = &p;
1662     }
1663     vout_SendDisplayEventMouse(vout, m);
1664     if (vout->p == &p) {
1665         vlc_mutex_destroy(&p.filter.lock);
1666         *fallback = p.mouse;
1667         vout->p = NULL;
1668     }
1669 }
1670 #endif
1671