https: add support for HTTPS proxies
[vlc.git] / src / video_output / display.c
blobe9f27fd84175ca615ac46cb8d0ef21c098eefc6b
1 /*****************************************************************************
2 * display.c: "vout display" managment
3 *****************************************************************************
4 * Copyright (C) 2009 Laurent Aimar
5 * $Id$
7 * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
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 Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
25 * Preamble
26 *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30 #include <assert.h>
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 #include <vlc_filter.h>
39 #include <vlc_picture_pool.h>
41 #include <libvlc.h>
43 #include "display.h"
44 #include "window.h"
46 #include "event.h"
48 static void SplitterClose(vout_display_t *vd);
50 /*****************************************************************************
51 * FIXME/TODO see how to have direct rendering here (interact with vout.c)
52 *****************************************************************************/
53 static picture_t *VideoBufferNew(filter_t *filter)
55 vout_display_t *vd = filter->owner.sys;
56 const video_format_t *fmt = &filter->fmt_out.video;
58 assert(vd->fmt.i_chroma == fmt->i_chroma &&
59 vd->fmt.i_width == fmt->i_width &&
60 vd->fmt.i_height == fmt->i_height);
62 picture_pool_t *pool = vout_display_Pool(vd, 3);
63 if (!pool)
64 return NULL;
65 return picture_pool_Get(pool);
68 /*****************************************************************************
70 *****************************************************************************/
72 /**
73 * It creates a new vout_display_t using the given configuration.
75 static vout_display_t *vout_display_New(vlc_object_t *obj,
76 const char *module, bool load_module,
77 const video_format_t *fmt,
78 const vout_display_cfg_t *cfg,
79 vout_display_owner_t *owner)
81 /* */
82 vout_display_t *vd = vlc_custom_create(obj, sizeof(*vd), "vout display" );
84 /* */
85 video_format_Copy(&vd->source, fmt);
87 /* Picture buffer does not have the concept of aspect ratio */
88 video_format_Copy(&vd->fmt, fmt);
89 vd->fmt.i_sar_num = 0;
90 vd->fmt.i_sar_den = 0;
92 vd->info.is_slow = false;
93 vd->info.has_double_click = false;
94 vd->info.has_hide_mouse = false;
95 vd->info.has_pictures_invalid = false;
96 vd->info.has_event_thread = false;
97 vd->info.subpicture_chromas = NULL;
99 vd->cfg = cfg;
100 vd->pool = NULL;
101 vd->prepare = NULL;
102 vd->display = NULL;
103 vd->control = NULL;
104 vd->manage = NULL;
105 vd->sys = NULL;
107 vd->owner = *owner;
109 if (load_module) {
110 vd->module = module_need(vd, "vout display", module, module && *module != '\0');
111 if (!vd->module) {
112 vlc_object_release(vd);
113 return NULL;
115 } else {
116 vd->module = NULL;
118 return vd;
122 * It deletes a vout_display_t
124 static void vout_display_Delete(vout_display_t *vd)
126 if (vd->module)
127 module_unneed(vd, vd->module);
129 video_format_Clean(&vd->source);
130 video_format_Clean(&vd->fmt);
132 vlc_object_release(vd);
136 * It controls a vout_display_t
138 static int vout_display_Control(vout_display_t *vd, int query, ...)
140 va_list args;
141 int result;
143 va_start(args, query);
144 result = vd->control(vd, query, args);
145 va_end(args);
147 return result;
150 static void vout_display_Manage(vout_display_t *vd)
152 if (vd->manage)
153 vd->manage(vd);
156 /* */
157 void vout_display_GetDefaultDisplaySize(unsigned *width, unsigned *height,
158 const video_format_t *source,
159 const vout_display_cfg_t *cfg)
161 if (cfg->display.width > 0 && cfg->display.height > 0) {
162 *width = cfg->display.width;
163 *height = cfg->display.height;
164 } else if (cfg->display.width > 0) {
165 *width = cfg->display.width;
166 *height = (int64_t)source->i_visible_height * source->i_sar_den * cfg->display.width * cfg->display.sar.num /
167 source->i_visible_width / source->i_sar_num / cfg->display.sar.den;
168 } else if (cfg->display.height > 0) {
169 *width = (int64_t)source->i_visible_width * source->i_sar_num * cfg->display.height * cfg->display.sar.den /
170 source->i_visible_height / source->i_sar_den / cfg->display.sar.num;
171 *height = cfg->display.height;
172 } else if (source->i_sar_num >= source->i_sar_den) {
173 *width = (int64_t)source->i_visible_width * source->i_sar_num * cfg->display.sar.den / source->i_sar_den / cfg->display.sar.num;
174 *height = source->i_visible_height;
175 } else {
176 *width = source->i_visible_width;
177 *height = (int64_t)source->i_visible_height * source->i_sar_den * cfg->display.sar.num / source->i_sar_num / cfg->display.sar.den;
180 *width = *width * cfg->zoom.num / cfg->zoom.den;
181 *height = *height * cfg->zoom.num / cfg->zoom.den;
183 if (ORIENT_IS_SWAP(source->orientation)) {
185 unsigned store = *width;
186 *width = *height;
187 *height = store;
191 /* */
192 void vout_display_PlacePicture(vout_display_place_t *place,
193 const video_format_t *source,
194 const vout_display_cfg_t *cfg,
195 bool do_clipping)
197 /* */
198 memset(place, 0, sizeof(*place));
199 if (cfg->display.width <= 0 || cfg->display.height <= 0)
200 return;
202 /* */
203 unsigned display_width;
204 unsigned display_height;
206 video_format_t source_rot;
207 video_format_ApplyRotation(&source_rot, source);
208 source = &source_rot;
210 if (cfg->is_display_filled) {
211 display_width = cfg->display.width;
212 display_height = cfg->display.height;
213 } else {
214 vout_display_cfg_t cfg_tmp = *cfg;
216 cfg_tmp.display.width = 0;
217 cfg_tmp.display.height = 0;
218 vout_display_GetDefaultDisplaySize(&display_width, &display_height,
219 source, &cfg_tmp);
221 if (do_clipping) {
222 display_width = __MIN(display_width, cfg->display.width);
223 display_height = __MIN(display_height, cfg->display.height);
227 const unsigned width = source->i_visible_width;
228 const unsigned height = source->i_visible_height;
229 /* Compute the height if we use the width to fill up display_width */
230 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;
231 /* And the same but switching width/height */
232 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;
234 /* We keep the solution that avoid filling outside the display */
235 if (scaled_width <= cfg->display.width) {
236 place->width = scaled_width;
237 place->height = display_height;
238 } else {
239 place->width = display_width;
240 place->height = scaled_height;
243 /* Compute position */
244 switch (cfg->align.horizontal) {
245 case VOUT_DISPLAY_ALIGN_LEFT:
246 place->x = 0;
247 break;
248 case VOUT_DISPLAY_ALIGN_RIGHT:
249 place->x = cfg->display.width - place->width;
250 break;
251 default:
252 place->x = ((int)cfg->display.width - (int)place->width) / 2;
253 break;
256 switch (cfg->align.vertical) {
257 case VOUT_DISPLAY_ALIGN_TOP:
258 place->y = 0;
259 break;
260 case VOUT_DISPLAY_ALIGN_BOTTOM:
261 place->y = cfg->display.height - place->height;
262 break;
263 default:
264 place->y = ((int)cfg->display.height - (int)place->height) / 2;
265 break;
269 void vout_display_SendMouseMovedDisplayCoordinates(vout_display_t *vd, video_orientation_t orient_display, int m_x, int m_y, vout_display_place_t *place)
271 video_format_t source_rot = vd->source;
272 video_format_TransformTo(&source_rot, orient_display);
274 if (place->width > 0 && place->height > 0) {
276 int x = (int)(source_rot.i_x_offset +
277 (int64_t)(m_x - place->x) * source_rot.i_visible_width / place->width);
278 int y = (int)(source_rot.i_y_offset +
279 (int64_t)(m_y - place->y) * source_rot.i_visible_height/ place->height);
281 video_transform_t transform = video_format_GetTransform(vd->source.orientation, orient_display);
283 int store;
285 switch (transform) {
287 case TRANSFORM_R90:
288 store = x;
289 x = y;
290 y = vd->source.i_visible_height - store;
291 break;
292 case TRANSFORM_R180:
293 x = vd->source.i_visible_width - x;
294 y = vd->source.i_visible_height - y;
295 break;
296 case TRANSFORM_R270:
297 store = x;
298 x = vd->source.i_visible_width - y;
299 y = store;
300 break;
301 case TRANSFORM_HFLIP:
302 x = vd->source.i_visible_width - x;
303 break;
304 case TRANSFORM_VFLIP:
305 y = vd->source.i_visible_height - y;
306 break;
307 case TRANSFORM_TRANSPOSE:
308 store = x;
309 x = y;
310 y = store;
311 break;
312 case TRANSFORM_ANTI_TRANSPOSE:
313 store = x;
314 x = vd->source.i_visible_width - y;
315 y = vd->source.i_visible_height - store;
316 break;
317 default:
318 break;
321 vout_display_SendEventMouseMoved (vd, x, y);
325 struct vout_display_owner_sys_t {
326 vout_thread_t *vout;
327 bool is_wrapper; /* Is the current display a wrapper */
328 vout_display_t *wrapper; /* Vout display wrapper */
330 /* */
331 vout_display_cfg_t cfg;
332 struct {
333 unsigned num;
334 unsigned den;
335 } sar_initial;
337 /* */
338 unsigned width_saved;
339 unsigned height_saved;
341 struct {
342 unsigned num;
343 unsigned den;
344 } crop_saved;
346 /* */
347 bool ch_display_filled;
348 bool is_display_filled;
350 bool ch_zoom;
351 struct {
352 unsigned num;
353 unsigned den;
354 } zoom;
355 #if defined(_WIN32) || defined(__OS2__)
356 bool ch_wm_state;
357 unsigned wm_state;
358 unsigned wm_state_initial;
359 #endif
360 bool ch_sar;
361 struct {
362 unsigned num;
363 unsigned den;
364 } sar;
366 bool ch_crop;
367 struct {
368 int left;
369 int top;
370 int right;
371 int bottom;
372 unsigned num;
373 unsigned den;
374 } crop;
376 /* */
377 video_format_t source;
378 filter_chain_t *filters;
380 /* Lock protecting the variables used by
381 * VoutDisplayEvent(ie vout_display_SendEvent) */
382 vlc_mutex_t lock;
384 /* mouse state */
385 struct {
386 vlc_mouse_t state;
388 mtime_t last_pressed;
389 mtime_t last_moved;
390 bool is_hidden;
391 bool ch_activity;
393 /* */
394 mtime_t double_click_timeout;
395 mtime_t hide_timeout;
396 } mouse;
398 bool reset_pictures;
400 bool ch_fullscreen;
401 bool is_fullscreen;
403 bool ch_display_size;
404 int display_width;
405 int display_height;
407 int fit_window;
409 struct {
410 vlc_thread_t thread;
411 block_fifo_t *fifo;
412 } event;
415 static int VoutDisplayCreateRender(vout_display_t *vd)
417 vout_display_owner_sys_t *osys = vd->owner.sys;
419 osys->filters = NULL;
421 video_format_t v_src = vd->source;
422 v_src.i_sar_num = 0;
423 v_src.i_sar_den = 0;
425 video_format_t v_dst = vd->fmt;
426 v_dst.i_sar_num = 0;
427 v_dst.i_sar_den = 0;
429 video_format_t v_dst_cmp = v_dst;
430 if ((v_src.i_chroma == VLC_CODEC_J420 && v_dst.i_chroma == VLC_CODEC_I420) ||
431 (v_src.i_chroma == VLC_CODEC_J422 && v_dst.i_chroma == VLC_CODEC_I422) ||
432 (v_src.i_chroma == VLC_CODEC_J440 && v_dst.i_chroma == VLC_CODEC_I440) ||
433 (v_src.i_chroma == VLC_CODEC_J444 && v_dst.i_chroma == VLC_CODEC_I444))
434 v_dst_cmp.i_chroma = v_src.i_chroma;
436 const bool convert = memcmp(&v_src, &v_dst_cmp, sizeof(v_src)) != 0;
437 if (!convert)
438 return 0;
440 msg_Dbg(vd, "A filter to adapt decoder to display is needed");
442 filter_owner_t owner = {
443 .sys = vd,
444 .video = {
445 .buffer_new = VideoBufferNew,
449 osys->filters = filter_chain_NewVideo(vd, false, &owner);
450 if (unlikely(osys->filters == NULL))
451 abort(); /* TODO critical */
453 /* */
454 es_format_t src;
455 es_format_InitFromVideo(&src, &v_src);
457 /* */
458 filter_t *filter;
459 for (int i = 0; i < 1 + (v_dst_cmp.i_chroma != v_dst.i_chroma); i++) {
460 es_format_t dst;
462 es_format_InitFromVideo(&dst, i == 0 ? &v_dst : &v_dst_cmp);
464 filter_chain_Reset(osys->filters, &src, &dst);
465 filter = filter_chain_AppendFilter(osys->filters,
466 NULL, NULL, &src, &dst);
467 es_format_Clean(&dst);
468 if (filter)
469 break;
471 es_format_Clean(&src);
473 if (filter == NULL) {
474 msg_Err(vd, "Failed to adapt decoder format to display");
475 filter_chain_Delete(osys->filters);
476 osys->filters = NULL;
477 return -1;
479 return 0;
482 static void VoutDisplayDestroyRender(vout_display_t *vd)
484 vout_display_owner_sys_t *osys = vd->owner.sys;
486 if (osys->filters)
487 filter_chain_Delete(osys->filters);
490 static int VoutDisplayResetRender(vout_display_t *vd)
492 VoutDisplayDestroyRender(vd);
493 return VoutDisplayCreateRender(vd);
496 static void VoutDisplayEventMouse(vout_display_t *vd, int event, va_list args)
498 vout_display_owner_sys_t *osys = vd->owner.sys;
500 vlc_mutex_lock(&osys->lock);
502 /* */
503 vlc_mouse_t m = osys->mouse.state;
504 bool is_ignored = false;
506 switch (event) {
507 case VOUT_DISPLAY_EVENT_MOUSE_STATE: {
508 const int x = (int)va_arg(args, int);
509 const int y = (int)va_arg(args, int);
510 const int button_mask = (int)va_arg(args, int);
512 vlc_mouse_Init(&m);
513 m.i_x = x;
514 m.i_y = y;
515 m.i_pressed = button_mask;
516 break;
518 case VOUT_DISPLAY_EVENT_MOUSE_MOVED: {
519 const int x = (int)va_arg(args, int);
520 const int y = (int)va_arg(args, int);
522 //msg_Dbg(vd, "VoutDisplayEvent 'mouse' @%d,%d", x, y);
524 m.i_x = x;
525 m.i_y = y;
526 m.b_double_click = false;
527 break;
529 case VOUT_DISPLAY_EVENT_MOUSE_PRESSED:
530 case VOUT_DISPLAY_EVENT_MOUSE_RELEASED: {
531 const int button = (int)va_arg(args, int);
532 const int button_mask = 1 << button;
534 /* Ignore inconsistent event */
535 if ((event == VOUT_DISPLAY_EVENT_MOUSE_PRESSED && (osys->mouse.state.i_pressed & button_mask)) ||
536 (event == VOUT_DISPLAY_EVENT_MOUSE_RELEASED && !(osys->mouse.state.i_pressed & button_mask))) {
537 is_ignored = true;
538 break;
541 /* */
542 msg_Dbg(vd, "VoutDisplayEvent 'mouse button' %d t=%d", button, event);
544 m.b_double_click = false;
545 if (event == VOUT_DISPLAY_EVENT_MOUSE_PRESSED)
546 m.i_pressed |= button_mask;
547 else
548 m.i_pressed &= ~button_mask;
549 break;
551 case VOUT_DISPLAY_EVENT_MOUSE_DOUBLE_CLICK:
552 msg_Dbg(vd, "VoutDisplayEvent 'double click'");
554 m.b_double_click = true;
555 break;
556 default:
557 vlc_assert_unreachable();
560 if (is_ignored) {
561 vlc_mutex_unlock(&osys->lock);
562 return;
565 /* Emulate double-click if needed */
566 if (!vd->info.has_double_click &&
567 vlc_mouse_HasPressed(&osys->mouse.state, &m, MOUSE_BUTTON_LEFT)) {
568 const mtime_t i_date = mdate();
570 if (i_date - osys->mouse.last_pressed < osys->mouse.double_click_timeout ) {
571 m.b_double_click = true;
572 osys->mouse.last_pressed = 0;
573 } else {
574 osys->mouse.last_pressed = mdate();
578 /* */
579 osys->mouse.state = m;
581 /* */
582 osys->mouse.ch_activity = true;
583 if (!vd->info.has_hide_mouse)
584 osys->mouse.last_moved = mdate();
586 /* */
587 vout_SendEventMouseVisible(osys->vout);
588 vout_SendDisplayEventMouse(osys->vout, &m);
589 vlc_mutex_unlock(&osys->lock);
592 VLC_NORETURN
593 static void *VoutDisplayEventKeyDispatch(void *data)
595 vout_display_owner_sys_t *osys = data;
597 for (;;) {
598 block_t *event = block_FifoGet(osys->event.fifo);
600 int cancel = vlc_savecancel();
602 int key;
603 memcpy(&key, event->p_buffer, sizeof(key));
604 vout_SendEventKey(osys->vout, key);
605 block_Release(event);
607 vlc_restorecancel(cancel);
611 static void VoutDisplayEventKey(vout_display_t *vd, int key)
613 vout_display_owner_sys_t *osys = vd->owner.sys;
615 if (!osys->event.fifo) {
616 osys->event.fifo = block_FifoNew();
617 if (!osys->event.fifo)
618 return;
619 if (vlc_clone(&osys->event.thread, VoutDisplayEventKeyDispatch,
620 osys, VLC_THREAD_PRIORITY_LOW)) {
621 block_FifoRelease(osys->event.fifo);
622 osys->event.fifo = NULL;
623 return;
626 block_t *event = block_Alloc(sizeof(key));
627 if (event) {
628 memcpy(event->p_buffer, &key, sizeof(key));
629 block_FifoPut(osys->event.fifo, event);
633 static void VoutDisplayEvent(vout_display_t *vd, int event, va_list args)
635 vout_display_owner_sys_t *osys = vd->owner.sys;
637 switch (event) {
638 case VOUT_DISPLAY_EVENT_CLOSE: {
639 msg_Dbg(vd, "VoutDisplayEvent 'close'");
640 vout_SendEventClose(osys->vout);
641 break;
643 case VOUT_DISPLAY_EVENT_KEY: {
644 const int key = (int)va_arg(args, int);
645 msg_Dbg(vd, "VoutDisplayEvent 'key' 0x%2.2x", key);
646 if (vd->info.has_event_thread)
647 vout_SendEventKey(osys->vout, key);
648 else
649 VoutDisplayEventKey(vd, key);
650 break;
652 case VOUT_DISPLAY_EVENT_MOUSE_STATE:
653 case VOUT_DISPLAY_EVENT_MOUSE_MOVED:
654 case VOUT_DISPLAY_EVENT_MOUSE_PRESSED:
655 case VOUT_DISPLAY_EVENT_MOUSE_RELEASED:
656 case VOUT_DISPLAY_EVENT_MOUSE_DOUBLE_CLICK:
657 VoutDisplayEventMouse(vd, event, args);
658 break;
660 case VOUT_DISPLAY_EVENT_FULLSCREEN: {
661 const int is_fullscreen = (int)va_arg(args, int);
663 msg_Dbg(vd, "VoutDisplayEvent 'fullscreen' %d", is_fullscreen);
665 vlc_mutex_lock(&osys->lock);
666 if (!is_fullscreen != !osys->is_fullscreen) {
667 osys->ch_fullscreen = true;
668 osys->is_fullscreen = is_fullscreen;
670 vlc_mutex_unlock(&osys->lock);
671 break;
673 #if defined(_WIN32) || defined(__OS2__)
674 case VOUT_DISPLAY_EVENT_WINDOW_STATE: {
675 const unsigned state = va_arg(args, unsigned);
677 msg_Dbg(vd, "VoutDisplayEvent 'window state' %u", state);
679 vlc_mutex_lock(&osys->lock);
680 if (state != osys->wm_state) {
681 osys->ch_wm_state = true;
682 osys->wm_state = state;
684 vlc_mutex_unlock(&osys->lock);
685 break;
687 #endif
688 case VOUT_DISPLAY_EVENT_DISPLAY_SIZE: {
689 const int width = (int)va_arg(args, int);
690 const int height = (int)va_arg(args, int);
691 msg_Dbg(vd, "VoutDisplayEvent 'resize' %dx%d", width, height);
693 /* */
694 vlc_mutex_lock(&osys->lock);
696 osys->ch_display_size = true;
697 osys->display_width = width;
698 osys->display_height = height;
700 vlc_mutex_unlock(&osys->lock);
701 break;
704 case VOUT_DISPLAY_EVENT_PICTURES_INVALID: {
705 msg_Warn(vd, "VoutDisplayEvent 'pictures invalid'");
707 /* */
708 assert(vd->info.has_pictures_invalid);
710 vlc_mutex_lock(&osys->lock);
711 osys->reset_pictures = true;
712 vlc_mutex_unlock(&osys->lock);
713 break;
715 default:
716 msg_Err(vd, "VoutDisplayEvent received event %d", event);
717 /* TODO add an assert when all event are handled */
718 break;
722 static vout_window_t *VoutDisplayNewWindow(vout_display_t *vd, unsigned type)
724 vout_display_owner_sys_t *osys = vd->owner.sys;
725 vout_window_t *window = vout_NewDisplayWindow(osys->vout, type);
726 if (window != NULL)
727 vout_display_window_Attach(window, vd);
728 return window;
731 static void VoutDisplayDelWindow(vout_display_t *vd, vout_window_t *window)
733 vout_display_owner_sys_t *osys = vd->owner.sys;
735 if (window != NULL)
736 vout_display_window_Detach(window);
737 vout_DeleteDisplayWindow(osys->vout, window);
740 static void VoutDisplayFitWindow(vout_display_t *vd, bool default_size)
742 vout_display_owner_sys_t *osys = vd->owner.sys;
743 vout_display_cfg_t cfg = osys->cfg;
745 if (!cfg.is_display_filled)
746 return;
748 cfg.display.width = 0;
749 if (default_size) {
750 cfg.display.height = 0;
751 } else {
752 cfg.display.height = osys->height_saved;
753 cfg.zoom.num = 1;
754 cfg.zoom.den = 1;
757 unsigned display_width;
758 unsigned display_height;
759 vout_display_GetDefaultDisplaySize(&display_width, &display_height,
760 &vd->source, &cfg);
761 vout_SetDisplayWindowSize(osys->vout, display_width, display_height);
764 static void VoutDisplayCropRatio(int *left, int *top, int *right, int *bottom,
765 const video_format_t *source,
766 unsigned num, unsigned den)
768 unsigned scaled_width = (uint64_t)source->i_visible_height * num * source->i_sar_den / den / source->i_sar_num;
769 unsigned scaled_height = (uint64_t)source->i_visible_width * den * source->i_sar_num / num / source->i_sar_den;
771 if (scaled_width < source->i_visible_width) {
772 *left = (source->i_visible_width - scaled_width) / 2;
773 *top = 0;
774 *right = *left + scaled_width;
775 *bottom = *top + source->i_visible_height;
776 } else {
777 *left = 0;
778 *top = (source->i_visible_height - scaled_height) / 2;
779 *right = *left + source->i_visible_width;
780 *bottom = *top + scaled_height;
784 bool vout_ManageDisplay(vout_display_t *vd, bool allow_reset_pictures)
786 vout_display_owner_sys_t *osys = vd->owner.sys;
788 vout_display_Manage(vd);
790 /* Handle mouse timeout */
791 const mtime_t date = mdate();
792 bool hide_mouse = false;
794 vlc_mutex_lock(&osys->lock);
796 if (!osys->mouse.is_hidden &&
797 osys->mouse.last_moved + osys->mouse.hide_timeout < date) {
798 osys->mouse.is_hidden = hide_mouse = true;
799 } else if (osys->mouse.ch_activity) {
800 osys->mouse.is_hidden = false;
802 osys->mouse.ch_activity = false;
803 vlc_mutex_unlock(&osys->lock);
805 if (hide_mouse) {
806 if (!vd->info.has_hide_mouse) {
807 msg_Dbg(vd, "auto hiding mouse cursor");
808 vout_display_Control(vd, VOUT_DISPLAY_HIDE_MOUSE);
810 vout_SendEventMouseHidden(osys->vout);
813 bool reset_render = false;
814 for (;;) {
816 vlc_mutex_lock(&osys->lock);
818 bool ch_fullscreen = osys->ch_fullscreen;
819 bool is_fullscreen = osys->is_fullscreen;
820 osys->ch_fullscreen = false;
822 #if defined(_WIN32) || defined(__OS2__)
823 bool ch_wm_state = osys->ch_wm_state;
824 unsigned wm_state = osys->wm_state;
825 osys->ch_wm_state = false;
826 #endif
828 bool ch_display_size = osys->ch_display_size;
829 int display_width = osys->display_width;
830 int display_height = osys->display_height;
831 osys->ch_display_size = false;
833 bool reset_pictures;
834 if (allow_reset_pictures) {
835 reset_pictures = osys->reset_pictures;
836 osys->reset_pictures = false;
837 } else {
838 reset_pictures = false;
841 vlc_mutex_unlock(&osys->lock);
843 if (!ch_fullscreen &&
844 !ch_display_size &&
845 !reset_pictures &&
846 !osys->ch_display_filled &&
847 !osys->ch_zoom &&
848 #if defined(_WIN32) || defined(__OS2__)
849 !ch_wm_state &&
850 #endif
851 !osys->ch_sar &&
852 !osys->ch_crop) {
854 if (!osys->cfg.is_fullscreen && osys->fit_window != 0) {
855 VoutDisplayFitWindow(vd, osys->fit_window == -1);
856 osys->fit_window = 0;
857 continue;
859 break;
862 /* */
863 if (ch_fullscreen) {
864 if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_FULLSCREEN,
865 is_fullscreen) == VLC_SUCCESS) {
866 osys->cfg.is_fullscreen = is_fullscreen;
868 if (!is_fullscreen)
869 vout_SetDisplayWindowSize(osys->vout, osys->width_saved,
870 osys->height_saved);
871 } else {
872 is_fullscreen = osys->cfg.is_fullscreen;
874 msg_Err(vd, "Failed to set fullscreen");
878 /* */
879 if (ch_display_size) {
880 vout_display_cfg_t cfg = osys->cfg;
881 cfg.display.width = display_width;
882 cfg.display.height = display_height;
884 osys->width_saved = osys->cfg.display.width;
885 osys->height_saved = osys->cfg.display.height;
887 vout_display_Control(vd, VOUT_DISPLAY_CHANGE_DISPLAY_SIZE, &cfg);
889 osys->cfg.display.width = display_width;
890 osys->cfg.display.height = display_height;
892 /* */
893 if (osys->ch_display_filled) {
894 vout_display_cfg_t cfg = osys->cfg;
896 cfg.is_display_filled = osys->is_display_filled;
898 if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_DISPLAY_FILLED, &cfg)) {
899 msg_Err(vd, "Failed to change display filled state");
900 osys->is_display_filled = osys->cfg.is_display_filled;
902 osys->cfg.is_display_filled = osys->is_display_filled;
903 osys->ch_display_filled = false;
905 /* */
906 if (osys->ch_zoom) {
907 vout_display_cfg_t cfg = osys->cfg;
909 cfg.zoom.num = osys->zoom.num;
910 cfg.zoom.den = osys->zoom.den;
912 if (10 * cfg.zoom.num <= cfg.zoom.den) {
913 cfg.zoom.num = 1;
914 cfg.zoom.den = 10;
915 } else if (cfg.zoom.num >= 10 * cfg.zoom.den) {
916 cfg.zoom.num = 10;
917 cfg.zoom.den = 1;
920 if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_ZOOM, &cfg)) {
921 msg_Err(vd, "Failed to change zoom");
922 osys->zoom.num = osys->cfg.zoom.num;
923 osys->zoom.den = osys->cfg.zoom.den;
924 } else {
925 osys->fit_window = -1;
928 osys->cfg.zoom.num = osys->zoom.num;
929 osys->cfg.zoom.den = osys->zoom.den;
930 osys->ch_zoom = false;
932 #if defined(_WIN32) || defined(__OS2__)
933 /* */
934 if (ch_wm_state) {
935 if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_WINDOW_STATE, wm_state)) {
936 msg_Err(vd, "Failed to set on top");
937 wm_state = osys->wm_state;
939 osys->wm_state_initial = wm_state;
941 #endif
942 /* */
943 if (osys->ch_sar) {
944 video_format_t source = vd->source;
946 if (osys->sar.num > 0 && osys->sar.den > 0) {
947 source.i_sar_num = osys->sar.num;
948 source.i_sar_den = osys->sar.den;
949 } else {
950 source.i_sar_num = osys->source.i_sar_num;
951 source.i_sar_den = osys->source.i_sar_den;
954 if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_SOURCE_ASPECT, &source)) {
955 /* There nothing much we can do. The only reason a vout display
956 * does not support it is because it need the core to add black border
957 * to the video for it.
958 * TODO add black borders ?
960 msg_Err(vd, "Failed to change source AR");
961 source = vd->source;
962 } else if (!osys->fit_window) {
963 osys->fit_window = 1;
965 vd->source = source;
966 osys->sar.num = source.i_sar_num;
967 osys->sar.den = source.i_sar_den;
968 osys->ch_sar = false;
970 /* If a crop ratio is requested, recompute the parameters */
971 if (osys->crop.num > 0 && osys->crop.den > 0)
972 osys->ch_crop = true;
974 /* */
975 if (osys->ch_crop) {
976 video_format_t source = vd->source;
978 unsigned crop_num = osys->crop.num;
979 unsigned crop_den = osys->crop.den;
980 if (crop_num > 0 && crop_den > 0) {
981 video_format_t fmt = osys->source;
982 fmt.i_sar_num = source.i_sar_num;
983 fmt.i_sar_den = source.i_sar_den;
984 VoutDisplayCropRatio(&osys->crop.left, &osys->crop.top,
985 &osys->crop.right, &osys->crop.bottom,
986 &fmt, crop_num, crop_den);
988 const int right_max = osys->source.i_x_offset + osys->source.i_visible_width;
989 const int bottom_max = osys->source.i_y_offset + osys->source.i_visible_height;
990 int left = VLC_CLIP((int)osys->source.i_x_offset + osys->crop.left,
991 0, right_max - 1);
992 int top = VLC_CLIP((int)osys->source.i_y_offset + osys->crop.top,
993 0, bottom_max - 1);
994 int right, bottom;
995 if (osys->crop.right <= 0)
996 right = (int)(osys->source.i_x_offset + osys->source.i_visible_width) + osys->crop.right;
997 else
998 right = (int)osys->source.i_x_offset + osys->crop.right;
999 right = VLC_CLIP(right, left + 1, right_max);
1000 if (osys->crop.bottom <= 0)
1001 bottom = (int)(osys->source.i_y_offset + osys->source.i_visible_height) + osys->crop.bottom;
1002 else
1003 bottom = (int)osys->source.i_y_offset + osys->crop.bottom;
1004 bottom = VLC_CLIP(bottom, top + 1, bottom_max);
1006 source.i_x_offset = left;
1007 source.i_y_offset = top;
1008 source.i_visible_width = right - left;
1009 source.i_visible_height = bottom - top;
1010 video_format_Print(VLC_OBJECT(vd), "SOURCE ", &osys->source);
1011 video_format_Print(VLC_OBJECT(vd), "CROPPED", &source);
1012 if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_SOURCE_CROP, &source)) {
1013 msg_Err(vd, "Failed to change source crop TODO implement crop at core");
1015 source = vd->source;
1016 crop_num = osys->crop_saved.num;
1017 crop_den = osys->crop_saved.den;
1018 /* FIXME implement cropping in the core if not supported by the
1019 * vout module (easy)
1021 } else if (!osys->fit_window) {
1022 osys->fit_window = 1;
1024 vd->source = source;
1025 osys->crop.left = source.i_x_offset - osys->source.i_x_offset;
1026 osys->crop.top = source.i_y_offset - osys->source.i_y_offset;
1027 /* FIXME for right/bottom we should keep the 'type' border vs window */
1028 osys->crop.right = (source.i_x_offset + source.i_visible_width) -
1029 (osys->source.i_x_offset + osys->source.i_visible_width);
1030 osys->crop.bottom = (source.i_y_offset + source.i_visible_height) -
1031 (osys->source.i_y_offset + osys->source.i_visible_height);
1032 osys->crop.num = crop_num;
1033 osys->crop.den = crop_den;
1034 osys->ch_crop = false;
1037 /* */
1038 if (reset_pictures) {
1039 if (vout_display_Control(vd, VOUT_DISPLAY_RESET_PICTURES)) {
1040 /* FIXME what to do here ? */
1041 msg_Err(vd, "Failed to reset pictures (probably fatal)");
1043 reset_render = true;
1046 if (reset_render)
1047 VoutDisplayResetRender(vd);
1049 return reset_render;
1052 bool vout_AreDisplayPicturesInvalid(vout_display_t *vd)
1054 vout_display_owner_sys_t *osys = vd->owner.sys;
1056 vlc_mutex_lock(&osys->lock);
1057 const bool reset_pictures = osys->reset_pictures;
1058 vlc_mutex_unlock(&osys->lock);
1060 return reset_pictures;
1063 bool vout_IsDisplayFiltered(vout_display_t *vd)
1065 vout_display_owner_sys_t *osys = vd->owner.sys;
1067 return osys->filters != NULL;
1070 picture_t *vout_FilterDisplay(vout_display_t *vd, picture_t *picture)
1072 vout_display_owner_sys_t *osys = vd->owner.sys;
1074 assert(osys->filters);
1075 if (filter_chain_GetLength(osys->filters) <= 0) {
1076 picture_Release(picture);
1077 return NULL;
1079 return filter_chain_VideoFilter(osys->filters, picture);
1082 void vout_UpdateDisplaySourceProperties(vout_display_t *vd, const video_format_t *source)
1084 vout_display_owner_sys_t *osys = vd->owner.sys;
1086 if (source->i_sar_num * osys->source.i_sar_den !=
1087 source->i_sar_den * osys->source.i_sar_num) {
1089 osys->source.i_sar_num = source->i_sar_num;
1090 osys->source.i_sar_den = source->i_sar_den;
1091 vlc_ureduce(&osys->source.i_sar_num, &osys->source.i_sar_den,
1092 osys->source.i_sar_num, osys->source.i_sar_den, 0);
1094 /* FIXME it will override any AR that the user would have forced */
1095 osys->ch_sar = true;
1096 osys->sar.num = osys->source.i_sar_num;
1097 osys->sar.den = osys->source.i_sar_den;
1099 if (source->i_x_offset != osys->source.i_x_offset ||
1100 source->i_y_offset != osys->source.i_y_offset ||
1101 source->i_visible_width != osys->source.i_visible_width ||
1102 source->i_visible_height != osys->source.i_visible_height) {
1104 video_format_CopyCrop(&osys->source, source);
1106 /* Force the vout to reapply the current user crop settings over the new decoder
1107 * crop settings. */
1108 osys->ch_crop = true;
1112 void vout_SetDisplayFullscreen(vout_display_t *vd, bool is_fullscreen)
1114 vout_display_owner_sys_t *osys = vd->owner.sys;
1116 vlc_mutex_lock(&osys->lock);
1117 if (!osys->is_fullscreen != !is_fullscreen) {
1118 osys->ch_fullscreen = true;
1119 osys->is_fullscreen = is_fullscreen;
1121 vlc_mutex_unlock(&osys->lock);
1124 void vout_SetDisplayFilled(vout_display_t *vd, bool is_filled)
1126 vout_display_owner_sys_t *osys = vd->owner.sys;
1128 if (!osys->is_display_filled != !is_filled) {
1129 osys->ch_display_filled = true;
1130 osys->is_display_filled = is_filled;
1134 void vout_SetDisplayZoom(vout_display_t *vd, unsigned num, unsigned den)
1136 vout_display_owner_sys_t *osys = vd->owner.sys;
1138 if (num > 0 && den > 0) {
1139 vlc_ureduce(&num, &den, num, den, 0);
1140 } else {
1141 num = 1;
1142 den = 1;
1145 if (osys->is_display_filled ||
1146 osys->zoom.num != num || osys->zoom.den != den) {
1147 osys->ch_zoom = true;
1148 osys->zoom.num = num;
1149 osys->zoom.den = den;
1153 void vout_SetDisplayAspect(vout_display_t *vd, unsigned dar_num, unsigned dar_den)
1155 vout_display_owner_sys_t *osys = vd->owner.sys;
1157 unsigned sar_num, sar_den;
1158 if (dar_num > 0 && dar_den > 0) {
1159 sar_num = dar_num * osys->source.i_visible_height;
1160 sar_den = dar_den * osys->source.i_visible_width;
1161 vlc_ureduce(&sar_num, &sar_den, sar_num, sar_den, 0);
1162 } else {
1163 sar_num = 0;
1164 sar_den = 0;
1167 if (osys->sar.num != sar_num || osys->sar.den != sar_den) {
1168 osys->ch_sar = true;
1169 osys->sar.num = sar_num;
1170 osys->sar.den = sar_den;
1173 void vout_SetDisplayCrop(vout_display_t *vd,
1174 unsigned crop_num, unsigned crop_den,
1175 unsigned left, unsigned top, int right, int bottom)
1177 vout_display_owner_sys_t *osys = vd->owner.sys;
1179 if (osys->crop.left != (int)left || osys->crop.top != (int)top ||
1180 osys->crop.right != right || osys->crop.bottom != bottom ||
1181 (crop_num > 0 && crop_den > 0 &&
1182 (crop_num != osys->crop.num || crop_den != osys->crop.den))) {
1184 osys->crop.left = left;
1185 osys->crop.top = top;
1186 osys->crop.right = right;
1187 osys->crop.bottom = bottom;
1188 osys->crop.num = crop_num;
1189 osys->crop.den = crop_den;
1191 osys->ch_crop = true;
1195 static vout_display_t *DisplayNew(vout_thread_t *vout,
1196 const video_format_t *source,
1197 const vout_display_state_t *state,
1198 const char *module,
1199 bool is_wrapper, vout_display_t *wrapper,
1200 mtime_t double_click_timeout,
1201 mtime_t hide_timeout,
1202 const vout_display_owner_t *owner_ptr)
1204 /* */
1205 vout_display_owner_sys_t *osys = calloc(1, sizeof(*osys));
1206 vout_display_cfg_t *cfg = &osys->cfg;
1208 *cfg = state->cfg;
1209 osys->sar_initial.num = state->sar.num;
1210 osys->sar_initial.den = state->sar.den;
1211 vout_display_GetDefaultDisplaySize(&cfg->display.width, &cfg->display.height,
1212 source, cfg);
1214 osys->vout = vout;
1215 osys->is_wrapper = is_wrapper;
1216 osys->wrapper = wrapper;
1218 vlc_mutex_init(&osys->lock);
1220 vlc_mouse_Init(&osys->mouse.state);
1221 osys->mouse.last_moved = mdate();
1222 osys->mouse.double_click_timeout = double_click_timeout;
1223 osys->mouse.hide_timeout = hide_timeout;
1224 osys->is_fullscreen = cfg->is_fullscreen;
1225 osys->display_width = cfg->display.width;
1226 osys->display_height = cfg->display.height;
1227 osys->is_display_filled = cfg->is_display_filled;
1228 osys->width_saved = cfg->display.width;
1229 osys->height_saved = cfg->display.height;
1230 if (osys->is_fullscreen) {
1231 vout_display_cfg_t cfg_windowed = *cfg;
1232 cfg_windowed.is_fullscreen = false;
1233 cfg_windowed.display.width = 0;
1234 cfg_windowed.display.height = 0;
1235 vout_display_GetDefaultDisplaySize(&osys->width_saved,
1236 &osys->height_saved,
1237 source, &cfg_windowed);
1240 osys->zoom.num = cfg->zoom.num;
1241 osys->zoom.den = cfg->zoom.den;
1242 #if defined(_WIN32) || defined(__OS2__)
1243 osys->wm_state_initial = VOUT_WINDOW_STATE_NORMAL;
1244 osys->wm_state = state->wm_state;
1245 osys->ch_wm_state = true;
1246 #endif
1247 osys->fit_window = 0;
1248 osys->event.fifo = NULL;
1250 osys->source = *source;
1251 osys->crop.left = 0;
1252 osys->crop.top = 0;
1253 osys->crop.right = 0;
1254 osys->crop.bottom = 0;
1255 osys->crop_saved.num = 0;
1256 osys->crop_saved.den = 0;
1257 osys->crop.num = 0;
1258 osys->crop.den = 0;
1260 osys->sar.num = osys->sar_initial.num ? osys->sar_initial.num : source->i_sar_num;
1261 osys->sar.den = osys->sar_initial.den ? osys->sar_initial.den : source->i_sar_den;
1263 vout_display_owner_t owner;
1264 if (owner_ptr) {
1265 owner = *owner_ptr;
1266 } else {
1267 owner.event = VoutDisplayEvent;
1268 owner.window_new = VoutDisplayNewWindow;
1269 owner.window_del = VoutDisplayDelWindow;
1271 owner.sys = osys;
1273 vout_display_t *p_display = vout_display_New(VLC_OBJECT(vout),
1274 module, !is_wrapper,
1275 source, cfg, &owner);
1276 if (!p_display)
1277 goto error;
1279 if (VoutDisplayCreateRender(p_display)) {
1280 vout_display_Delete(p_display);
1281 goto error;
1284 /* Setup delayed request */
1285 if (osys->sar.num != source->i_sar_num ||
1286 osys->sar.den != source->i_sar_den)
1287 osys->ch_sar = true;
1289 return p_display;
1290 error:
1291 vlc_mutex_destroy(&osys->lock);
1292 free(osys);
1293 return NULL;
1296 void vout_DeleteDisplay(vout_display_t *vd, vout_display_state_t *state)
1298 vout_display_owner_sys_t *osys = vd->owner.sys;
1300 if (state) {
1301 if (!osys->is_wrapper )
1302 state->cfg = osys->cfg;
1303 #if defined(_WIN32) || defined(__OS2__)
1304 state->wm_state = osys->wm_state;
1305 #endif
1306 state->sar.num = osys->sar_initial.num;
1307 state->sar.den = osys->sar_initial.den;
1310 VoutDisplayDestroyRender(vd);
1311 if (osys->is_wrapper)
1312 SplitterClose(vd);
1313 vout_display_Delete(vd);
1314 if (osys->event.fifo) {
1315 vlc_cancel(osys->event.thread);
1316 vlc_join(osys->event.thread, NULL);
1317 block_FifoRelease(osys->event.fifo);
1319 vlc_mutex_destroy(&osys->lock);
1320 free(osys);
1323 /*****************************************************************************
1325 *****************************************************************************/
1326 vout_display_t *vout_NewDisplay(vout_thread_t *vout,
1327 const video_format_t *source,
1328 const vout_display_state_t *state,
1329 const char *module,
1330 mtime_t double_click_timeout,
1331 mtime_t hide_timeout)
1333 return DisplayNew(vout, source, state, module, false, NULL,
1334 double_click_timeout, hide_timeout, NULL);
1337 /*****************************************************************************
1339 *****************************************************************************/
1340 struct vout_display_sys_t {
1341 picture_pool_t *pool;
1342 video_splitter_t *splitter;
1344 /* */
1345 int count;
1346 picture_t **picture;
1347 vout_display_t **display;
1349 struct video_splitter_owner_t {
1350 vout_display_t *wrapper;
1353 static vout_window_t *SplitterNewWindow(vout_display_t *vd, unsigned type)
1355 vout_display_owner_sys_t *osys = vd->owner.sys;
1356 vout_window_t *window;
1357 vout_window_cfg_t cfg = {
1358 .type = type,
1359 .width = vd->cfg->display.width,
1360 .height = vd->cfg->display.height,
1361 .is_standalone = true,
1364 window = vout_display_window_New(osys->vout, &cfg);
1365 if (window != NULL)
1366 vout_display_window_Attach(window, vd);
1367 return window;
1370 static void SplitterDelWindow(vout_display_t *vd, vout_window_t *window)
1372 if (window != NULL) {
1373 vout_display_window_Detach(window);
1374 vout_display_window_Delete(window);
1376 (void) vd;
1379 static void SplitterEvent(vout_display_t *vd, int event, va_list args)
1381 //vout_display_owner_sys_t *osys = vd->owner.sys;
1383 switch (event) {
1384 #if 0
1385 case VOUT_DISPLAY_EVENT_MOUSE_STATE:
1386 case VOUT_DISPLAY_EVENT_MOUSE_MOVED:
1387 case VOUT_DISPLAY_EVENT_MOUSE_PRESSED:
1388 case VOUT_DISPLAY_EVENT_MOUSE_RELEASED:
1389 /* TODO */
1390 break;
1391 #endif
1392 case VOUT_DISPLAY_EVENT_MOUSE_DOUBLE_CLICK:
1393 case VOUT_DISPLAY_EVENT_KEY:
1394 case VOUT_DISPLAY_EVENT_CLOSE:
1395 case VOUT_DISPLAY_EVENT_FULLSCREEN:
1396 case VOUT_DISPLAY_EVENT_DISPLAY_SIZE:
1397 case VOUT_DISPLAY_EVENT_PICTURES_INVALID:
1398 VoutDisplayEvent(vd, event, args);
1399 break;
1401 default:
1402 msg_Err(vd, "splitter event not implemented: %d", event);
1403 break;
1407 static picture_pool_t *SplitterPool(vout_display_t *vd, unsigned count)
1409 vout_display_sys_t *sys = vd->sys;
1410 if (!sys->pool)
1411 sys->pool = picture_pool_NewFromFormat(&vd->fmt, count);
1412 return sys->pool;
1414 static void SplitterPrepare(vout_display_t *vd,
1415 picture_t *picture,
1416 subpicture_t *subpicture)
1418 vout_display_sys_t *sys = vd->sys;
1420 picture_Hold(picture);
1421 assert(!subpicture);
1423 if (video_splitter_Filter(sys->splitter, sys->picture, picture)) {
1424 for (int i = 0; i < sys->count; i++)
1425 sys->picture[i] = NULL;
1426 picture_Release(picture);
1427 return;
1430 for (int i = 0; i < sys->count; i++) {
1431 if (vout_IsDisplayFiltered(sys->display[i]))
1432 sys->picture[i] = vout_FilterDisplay(sys->display[i], sys->picture[i]);
1433 if (sys->picture[i])
1434 vout_display_Prepare(sys->display[i], sys->picture[i], NULL);
1437 static void SplitterDisplay(vout_display_t *vd,
1438 picture_t *picture,
1439 subpicture_t *subpicture)
1441 vout_display_sys_t *sys = vd->sys;
1443 assert(!subpicture);
1444 for (int i = 0; i < sys->count; i++) {
1445 if (sys->picture[i])
1446 vout_display_Display(sys->display[i], sys->picture[i], NULL);
1448 picture_Release(picture);
1450 static int SplitterControl(vout_display_t *vd, int query, va_list args)
1452 (void)vd; (void)query; (void)args;
1453 return VLC_EGENERIC;
1455 static void SplitterManage(vout_display_t *vd)
1457 vout_display_sys_t *sys = vd->sys;
1459 for (int i = 0; i < sys->count; i++)
1460 vout_ManageDisplay(sys->display[i], true);
1463 static int SplitterPictureNew(video_splitter_t *splitter, picture_t *picture[])
1465 vout_display_sys_t *wsys = splitter->p_owner->wrapper->sys;
1467 for (int i = 0; i < wsys->count; i++) {
1468 if (vout_IsDisplayFiltered(wsys->display[i])) {
1469 /* TODO use a pool ? */
1470 picture[i] = picture_NewFromFormat(&wsys->display[i]->source);
1471 } else {
1472 picture_pool_t *pool = vout_display_Pool(wsys->display[i], 1);
1473 picture[i] = pool ? picture_pool_Get(pool) : NULL;
1475 if (!picture[i]) {
1476 for (int j = 0; j < i; j++)
1477 picture_Release(picture[j]);
1478 return VLC_EGENERIC;
1481 return VLC_SUCCESS;
1483 static void SplitterPictureDel(video_splitter_t *splitter, picture_t *picture[])
1485 vout_display_sys_t *wsys = splitter->p_owner->wrapper->sys;
1487 for (int i = 0; i < wsys->count; i++)
1488 picture_Release(picture[i]);
1490 static void SplitterClose(vout_display_t *vd)
1492 vout_display_sys_t *sys = vd->sys;
1494 /* */
1495 video_splitter_t *splitter = sys->splitter;
1496 free(splitter->p_owner);
1497 video_splitter_Delete(splitter);
1499 if (sys->pool)
1500 picture_pool_Release(sys->pool);
1502 /* */
1503 for (int i = 0; i < sys->count; i++)
1504 vout_DeleteDisplay(sys->display[i], NULL);
1505 TAB_CLEAN(sys->count, sys->display);
1506 free(sys->picture);
1508 free(sys);
1511 vout_display_t *vout_NewSplitter(vout_thread_t *vout,
1512 const video_format_t *source,
1513 const vout_display_state_t *state,
1514 const char *module,
1515 const char *splitter_module,
1516 mtime_t double_click_timeout,
1517 mtime_t hide_timeout)
1519 video_splitter_t *splitter =
1520 video_splitter_New(VLC_OBJECT(vout), splitter_module, source);
1521 if (!splitter)
1522 return NULL;
1524 /* */
1525 vout_display_t *wrapper =
1526 DisplayNew(vout, source, state, module, true, NULL,
1527 double_click_timeout, hide_timeout, NULL);
1528 if (!wrapper) {
1529 video_splitter_Delete(splitter);
1530 return NULL;
1532 vout_display_sys_t *sys = malloc(sizeof(*sys));
1533 if (!sys)
1534 abort();
1535 sys->picture = calloc(splitter->i_output, sizeof(*sys->picture));
1536 if (!sys->picture )
1537 abort();
1538 sys->splitter = splitter;
1539 sys->pool = NULL;
1541 wrapper->pool = SplitterPool;
1542 wrapper->prepare = SplitterPrepare;
1543 wrapper->display = SplitterDisplay;
1544 wrapper->control = SplitterControl;
1545 wrapper->manage = SplitterManage;
1546 wrapper->sys = sys;
1548 /* */
1549 video_splitter_owner_t *vso = xmalloc(sizeof(*vso));
1550 vso->wrapper = wrapper;
1551 splitter->p_owner = vso;
1552 splitter->pf_picture_new = SplitterPictureNew;
1553 splitter->pf_picture_del = SplitterPictureDel;
1555 /* */
1556 TAB_INIT(sys->count, sys->display);
1557 for (int i = 0; i < splitter->i_output; i++) {
1558 vout_display_owner_t vdo = {
1559 .event = SplitterEvent,
1560 .window_new = SplitterNewWindow,
1561 .window_del = SplitterDelWindow,
1563 const video_splitter_output_t *output = &splitter->p_output[i];
1564 vout_display_state_t ostate;
1566 memset(&ostate, 0, sizeof(ostate));
1567 ostate.cfg.is_fullscreen = false;
1568 ostate.cfg.display = state->cfg.display;
1569 ostate.cfg.align.horizontal = 0; /* TODO */
1570 ostate.cfg.align.vertical = 0; /* TODO */
1571 ostate.cfg.is_display_filled = true;
1572 ostate.cfg.zoom.num = 1;
1573 ostate.cfg.zoom.den = 1;
1575 vout_display_t *vd = DisplayNew(vout, &output->fmt, &ostate,
1576 output->psz_module ? output->psz_module : module,
1577 false, wrapper,
1578 double_click_timeout, hide_timeout, &vdo);
1579 if (!vd) {
1580 vout_DeleteDisplay(wrapper, NULL);
1581 return NULL;
1583 TAB_APPEND(sys->count, sys->display, vd);
1586 return wrapper;
1589 /*****************************************************************************
1590 * TODO move out
1591 *****************************************************************************/
1592 #include "vout_internal.h"
1593 void vout_SendDisplayEventMouse(vout_thread_t *vout, const vlc_mouse_t *m)
1595 vlc_mouse_t tmp1, tmp2;
1597 /* The check on spu is needed as long as ALLOW_DUMMY_VOUT is defined */
1598 if (vout->p->spu && spu_ProcessMouse( vout->p->spu, m, &vout->p->display.vd->source))
1599 return;
1601 vlc_mutex_lock( &vout->p->filter.lock );
1602 if (vout->p->filter.chain_static && vout->p->filter.chain_interactive) {
1603 if (!filter_chain_MouseFilter(vout->p->filter.chain_interactive, &tmp1, m))
1604 m = &tmp1;
1605 if (!filter_chain_MouseFilter(vout->p->filter.chain_static, &tmp2, m))
1606 m = &tmp2;
1608 vlc_mutex_unlock( &vout->p->filter.lock );
1610 if (vlc_mouse_HasMoved(&vout->p->mouse, m)) {
1611 vout_SendEventMouseMoved(vout, m->i_x, m->i_y);
1613 if (vlc_mouse_HasButton(&vout->p->mouse, m)) {
1614 for (unsigned button = 0; button < MOUSE_BUTTON_MAX; button++) {
1615 if (vlc_mouse_HasPressed(&vout->p->mouse, m, button))
1616 vout_SendEventMousePressed(vout, button);
1617 else if (vlc_mouse_HasReleased(&vout->p->mouse, m, button))
1618 vout_SendEventMouseReleased(vout, button);
1621 if (m->b_double_click)
1622 vout_SendEventMouseDoubleClick(vout);
1623 vout->p->mouse = *m;