chroma: cvpx: always use cached copy
[vlc.git] / src / video_output / display.c
blob909cb670014e61b6c907dcb16993ef55e5397d63
1 /*****************************************************************************
2 * display.c: "vout display" management
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.needs_hide_mouse = false;
95 vd->info.has_pictures_invalid = false;
96 vd->info.subpicture_chromas = NULL;
98 vd->cfg = cfg;
99 vd->pool = NULL;
100 vd->prepare = NULL;
101 vd->display = NULL;
102 vd->control = NULL;
103 vd->manage = NULL;
104 vd->sys = NULL;
106 vd->owner = *owner;
108 if (load_module) {
109 vd->module = module_need(vd, "vout display", module, module && *module != '\0');
110 if (!vd->module) {
111 vlc_object_release(vd);
112 return NULL;
114 } else {
115 vd->module = NULL;
117 return vd;
121 * It deletes a vout_display_t
123 static void vout_display_Delete(vout_display_t *vd)
125 if (vd->module)
126 module_unneed(vd, vd->module);
128 video_format_Clean(&vd->source);
129 video_format_Clean(&vd->fmt);
131 vlc_object_release(vd);
135 * It controls a vout_display_t
137 static int vout_display_Control(vout_display_t *vd, int query, ...)
139 va_list args;
140 int result;
142 va_start(args, query);
143 result = vd->control(vd, query, args);
144 va_end(args);
146 return result;
149 static void vout_display_Manage(vout_display_t *vd)
151 if (vd->manage)
152 vd->manage(vd);
155 /* */
156 void vout_display_GetDefaultDisplaySize(unsigned *width, unsigned *height,
157 const video_format_t *source,
158 const vout_display_cfg_t *cfg)
160 if (cfg->display.width != 0 && cfg->display.height != 0) {
161 *width = cfg->display.width;
162 *height = cfg->display.height;
163 } else if (cfg->display.width != 0) {
164 *width = cfg->display.width;
165 *height = (int64_t)source->i_visible_height * source->i_sar_den * cfg->display.width * cfg->display.sar.num /
166 source->i_visible_width / source->i_sar_num / cfg->display.sar.den;
167 } else if (cfg->display.height != 0) {
168 *width = (int64_t)source->i_visible_width * source->i_sar_num * cfg->display.height * cfg->display.sar.den /
169 source->i_visible_height / source->i_sar_den / cfg->display.sar.num;
170 *height = cfg->display.height;
171 } else if (source->i_sar_num >= source->i_sar_den) {
172 *width = (int64_t)source->i_visible_width * source->i_sar_num * cfg->display.sar.den / source->i_sar_den / cfg->display.sar.num;
173 *height = source->i_visible_height;
174 } else {
175 *width = source->i_visible_width;
176 *height = (int64_t)source->i_visible_height * source->i_sar_den * cfg->display.sar.num / source->i_sar_num / cfg->display.sar.den;
179 *width = *width * cfg->zoom.num / cfg->zoom.den;
180 *height = *height * cfg->zoom.num / cfg->zoom.den;
182 if (ORIENT_IS_SWAP(source->orientation)) {
184 unsigned store = *width;
185 *width = *height;
186 *height = store;
190 /* */
191 void vout_display_PlacePicture(vout_display_place_t *place,
192 const video_format_t *source,
193 const vout_display_cfg_t *cfg,
194 bool do_clipping)
196 /* */
197 memset(place, 0, sizeof(*place));
198 if (cfg->display.width == 0 || cfg->display.height == 0)
199 return;
201 /* */
202 unsigned display_width;
203 unsigned display_height;
205 video_format_t source_rot;
206 video_format_ApplyRotation(&source_rot, source);
207 source = &source_rot;
209 if (cfg->is_display_filled) {
210 display_width = cfg->display.width;
211 display_height = cfg->display.height;
212 } else {
213 vout_display_cfg_t cfg_tmp = *cfg;
215 cfg_tmp.display.width = 0;
216 cfg_tmp.display.height = 0;
217 vout_display_GetDefaultDisplaySize(&display_width, &display_height,
218 source, &cfg_tmp);
220 if (do_clipping) {
221 display_width = __MIN(display_width, cfg->display.width);
222 display_height = __MIN(display_height, cfg->display.height);
226 const unsigned width = source->i_visible_width;
227 const unsigned height = source->i_visible_height;
228 /* Compute the height if we use the width to fill up display_width */
229 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);
230 /* And the same but switching width/height */
231 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);
233 if (source->projection_mode == PROJECTION_MODE_RECTANGULAR) {
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;
242 } else {
243 /* No need to preserve an aspect ratio for 360 video.
244 * They can fill the display. */
245 place->width = display_width;
246 place->height = display_height;
249 /* Compute position */
250 switch (cfg->align.horizontal) {
251 case VOUT_DISPLAY_ALIGN_LEFT:
252 place->x = 0;
253 break;
254 case VOUT_DISPLAY_ALIGN_RIGHT:
255 place->x = cfg->display.width - place->width;
256 break;
257 default:
258 place->x = ((int)cfg->display.width - (int)place->width) / 2;
259 break;
262 switch (cfg->align.vertical) {
263 case VOUT_DISPLAY_ALIGN_TOP:
264 place->y = 0;
265 break;
266 case VOUT_DISPLAY_ALIGN_BOTTOM:
267 place->y = cfg->display.height - place->height;
268 break;
269 default:
270 place->y = ((int)cfg->display.height - (int)place->height) / 2;
271 break;
275 void vout_display_SendMouseMovedDisplayCoordinates(vout_display_t *vd, video_orientation_t orient_display, int m_x, int m_y, vout_display_place_t *place)
277 video_format_t source_rot = vd->source;
278 video_format_TransformTo(&source_rot, orient_display);
280 if (place->width > 0 && place->height > 0) {
282 int x = (int)(source_rot.i_x_offset +
283 (int64_t)(m_x - place->x) * source_rot.i_visible_width / place->width);
284 int y = (int)(source_rot.i_y_offset +
285 (int64_t)(m_y - place->y) * source_rot.i_visible_height/ place->height);
287 video_transform_t transform = video_format_GetTransform(vd->source.orientation, orient_display);
289 int store;
291 switch (transform) {
293 case TRANSFORM_R90:
294 store = x;
295 x = y;
296 y = vd->source.i_visible_height - store;
297 break;
298 case TRANSFORM_R180:
299 x = vd->source.i_visible_width - x;
300 y = vd->source.i_visible_height - y;
301 break;
302 case TRANSFORM_R270:
303 store = x;
304 x = vd->source.i_visible_width - y;
305 y = store;
306 break;
307 case TRANSFORM_HFLIP:
308 x = vd->source.i_visible_width - x;
309 break;
310 case TRANSFORM_VFLIP:
311 y = vd->source.i_visible_height - y;
312 break;
313 case TRANSFORM_TRANSPOSE:
314 store = x;
315 x = y;
316 y = store;
317 break;
318 case TRANSFORM_ANTI_TRANSPOSE:
319 store = x;
320 x = vd->source.i_visible_width - y;
321 y = vd->source.i_visible_height - store;
322 break;
323 default:
324 break;
327 vout_display_SendEventMouseMoved (vd, x, y);
331 typedef struct {
332 vout_thread_t *vout;
333 bool is_splitter; /* Is this a video splitter */
335 /* */
336 vout_display_cfg_t cfg;
337 vlc_rational_t sar_initial;
339 /* */
340 bool is_display_filled;
342 bool ch_zoom;
343 vlc_rational_t zoom;
344 #if defined(_WIN32) || defined(__OS2__)
345 unsigned width_saved;
346 unsigned height_saved;
347 bool ch_fullscreen;
348 bool is_fullscreen;
349 bool window_fullscreen;
350 bool ch_wm_state;
351 unsigned wm_state;
352 unsigned wm_state_initial;
353 #endif
354 bool ch_sar;
355 vlc_rational_t sar;
357 bool ch_crop;
358 struct {
359 int left;
360 int top;
361 int right;
362 int bottom;
363 unsigned num;
364 unsigned den;
365 } crop;
367 bool ch_viewpoint;
368 vlc_viewpoint_t viewpoint;
369 vlc_viewpoint_t display_viewpoint;
371 /* */
372 video_format_t source;
373 filter_chain_t *filters;
375 /* Lock protecting the variables used by
376 * VoutDisplayEvent(ie vout_display_SendEvent) */
377 vlc_mutex_t lock;
379 /* mouse state */
380 struct {
381 vlc_mouse_t state;
383 mtime_t last_pressed;
384 mtime_t last_moved;
385 bool is_hidden;
386 bool ch_activity;
388 /* */
389 mtime_t double_click_timeout;
390 mtime_t hide_timeout;
391 } mouse;
393 bool reset_pictures;
395 signed char fit_window;
397 bool ch_display_size;
398 int display_width;
399 int display_height;
400 } vout_display_owner_sys_t;
402 static int VoutDisplayCreateRender(vout_display_t *vd)
404 vout_display_owner_sys_t *osys = vd->owner.sys;
405 filter_owner_t owner = {
406 .sys = vd,
407 .video = {
408 .buffer_new = VideoBufferNew,
412 osys->filters = filter_chain_NewVideo(vd, false, &owner);
413 if (unlikely(osys->filters == NULL))
414 return -1;
416 video_format_t v_src = vd->source;
417 v_src.i_sar_num = 0;
418 v_src.i_sar_den = 0;
420 video_format_t v_dst = vd->fmt;
421 v_dst.i_sar_num = 0;
422 v_dst.i_sar_den = 0;
424 video_format_t v_dst_cmp = v_dst;
425 if ((v_src.i_chroma == VLC_CODEC_J420 && v_dst.i_chroma == VLC_CODEC_I420) ||
426 (v_src.i_chroma == VLC_CODEC_J422 && v_dst.i_chroma == VLC_CODEC_I422) ||
427 (v_src.i_chroma == VLC_CODEC_J440 && v_dst.i_chroma == VLC_CODEC_I440) ||
428 (v_src.i_chroma == VLC_CODEC_J444 && v_dst.i_chroma == VLC_CODEC_I444))
429 v_dst_cmp.i_chroma = v_src.i_chroma;
431 const bool convert = memcmp(&v_src, &v_dst_cmp, sizeof(v_src)) != 0;
432 if (!convert)
433 return 0;
435 msg_Dbg(vd, "A filter to adapt decoder %4.4s to display %4.4s is needed",
436 (const char *)&v_src.i_chroma, (const char *)&v_dst.i_chroma);
438 /* */
439 es_format_t src;
440 es_format_InitFromVideo(&src, &v_src);
442 /* */
443 int ret;
445 for (int i = 0; i < 1 + (v_dst_cmp.i_chroma != v_dst.i_chroma); i++) {
446 es_format_t dst;
448 es_format_InitFromVideo(&dst, i == 0 ? &v_dst : &v_dst_cmp);
450 filter_chain_Reset(osys->filters, &src, &dst);
451 ret = filter_chain_AppendConverter(osys->filters, &src, &dst);
452 es_format_Clean(&dst);
453 if (ret == 0)
454 break;
456 es_format_Clean(&src);
458 if (ret != 0) {
459 msg_Err(vd, "Failed to adapt decoder format to display");
460 filter_chain_Delete(osys->filters);
461 osys->filters = NULL;
463 return ret;
466 static void VoutDisplayDestroyRender(vout_display_t *vd)
468 vout_display_owner_sys_t *osys = vd->owner.sys;
470 if (osys->filters)
471 filter_chain_Delete(osys->filters);
474 static int VoutDisplayResetRender(vout_display_t *vd)
476 VoutDisplayDestroyRender(vd);
477 return VoutDisplayCreateRender(vd);
480 static void VoutDisplayEventMouse(vout_display_t *vd, int event, va_list args)
482 vout_display_owner_sys_t *osys = vd->owner.sys;
484 vlc_mutex_lock(&osys->lock);
486 /* */
487 vlc_mouse_t m = osys->mouse.state;
488 bool is_ignored = false;
490 switch (event) {
491 case VOUT_DISPLAY_EVENT_MOUSE_STATE: {
492 const int x = (int)va_arg(args, int);
493 const int y = (int)va_arg(args, int);
494 const int button_mask = (int)va_arg(args, int);
496 vlc_mouse_Init(&m);
497 m.i_x = x;
498 m.i_y = y;
499 m.i_pressed = button_mask;
500 break;
502 case VOUT_DISPLAY_EVENT_MOUSE_MOVED: {
503 const int x = (int)va_arg(args, int);
504 const int y = (int)va_arg(args, int);
506 //msg_Dbg(vd, "VoutDisplayEvent 'mouse' @%d,%d", x, y);
508 m.i_x = x;
509 m.i_y = y;
510 m.b_double_click = false;
511 break;
513 case VOUT_DISPLAY_EVENT_MOUSE_PRESSED:
514 case VOUT_DISPLAY_EVENT_MOUSE_RELEASED: {
515 const int button = (int)va_arg(args, int);
516 const int button_mask = 1 << button;
518 /* Ignore inconsistent event */
519 if ((event == VOUT_DISPLAY_EVENT_MOUSE_PRESSED && (osys->mouse.state.i_pressed & button_mask)) ||
520 (event == VOUT_DISPLAY_EVENT_MOUSE_RELEASED && !(osys->mouse.state.i_pressed & button_mask))) {
521 is_ignored = true;
522 break;
525 /* */
526 msg_Dbg(vd, "VoutDisplayEvent 'mouse button' %d t=%d", button, event);
528 m.b_double_click = false;
529 if (event == VOUT_DISPLAY_EVENT_MOUSE_PRESSED)
530 m.i_pressed |= button_mask;
531 else
532 m.i_pressed &= ~button_mask;
533 break;
535 case VOUT_DISPLAY_EVENT_MOUSE_DOUBLE_CLICK:
536 msg_Dbg(vd, "VoutDisplayEvent 'double click'");
538 m.b_double_click = true;
539 break;
540 default:
541 vlc_assert_unreachable();
544 if (is_ignored) {
545 vlc_mutex_unlock(&osys->lock);
546 return;
549 /* Emulate double-click if needed */
550 if (!vd->info.has_double_click &&
551 vlc_mouse_HasPressed(&osys->mouse.state, &m, MOUSE_BUTTON_LEFT)) {
552 const mtime_t i_date = mdate();
554 if (i_date - osys->mouse.last_pressed < osys->mouse.double_click_timeout ) {
555 m.b_double_click = true;
556 osys->mouse.last_pressed = 0;
557 } else {
558 osys->mouse.last_pressed = mdate();
562 /* */
563 osys->mouse.state = m;
565 /* */
566 osys->mouse.ch_activity = true;
567 osys->mouse.last_moved = mdate();
569 /* */
570 vout_SendDisplayEventMouse(osys->vout, &m);
571 vlc_mutex_unlock(&osys->lock);
574 static void VoutDisplayEvent(vout_display_t *vd, int event, va_list args)
576 vout_display_owner_sys_t *osys = vd->owner.sys;
578 switch (event) {
579 case VOUT_DISPLAY_EVENT_CLOSE: {
580 msg_Dbg(vd, "VoutDisplayEvent 'close'");
581 vout_SendEventClose(osys->vout);
582 break;
584 case VOUT_DISPLAY_EVENT_KEY: {
585 const int key = (int)va_arg(args, int);
586 msg_Dbg(vd, "VoutDisplayEvent 'key' 0x%2.2x", key);
587 vout_SendEventKey(osys->vout, key);
588 break;
590 case VOUT_DISPLAY_EVENT_MOUSE_STATE:
591 case VOUT_DISPLAY_EVENT_MOUSE_MOVED:
592 case VOUT_DISPLAY_EVENT_MOUSE_PRESSED:
593 case VOUT_DISPLAY_EVENT_MOUSE_RELEASED:
594 case VOUT_DISPLAY_EVENT_MOUSE_DOUBLE_CLICK:
595 VoutDisplayEventMouse(vd, event, args);
596 break;
598 case VOUT_DISPLAY_EVENT_VIEWPOINT_MOVED:
599 vout_SendEventViewpointMoved(osys->vout,
600 va_arg(args, const vlc_viewpoint_t *));
601 break;
603 #if defined(_WIN32) || defined(__OS2__)
604 case VOUT_DISPLAY_EVENT_FULLSCREEN: {
605 const int is_fullscreen = (int)va_arg(args, int);
606 const bool window_fullscreen = va_arg(args, int);
608 msg_Dbg(vd, "VoutDisplayEvent 'fullscreen' %d", is_fullscreen);
610 vlc_mutex_lock(&osys->lock);
611 if (!is_fullscreen != !osys->is_fullscreen) {
612 osys->ch_fullscreen = true;
613 osys->is_fullscreen = is_fullscreen;
614 osys->window_fullscreen = window_fullscreen;
616 vlc_mutex_unlock(&osys->lock);
617 break;
620 case VOUT_DISPLAY_EVENT_WINDOW_STATE: {
621 const unsigned state = va_arg(args, unsigned);
623 msg_Dbg(vd, "VoutDisplayEvent 'window state' %u", state);
625 vlc_mutex_lock(&osys->lock);
626 if (state != osys->wm_state) {
627 osys->ch_wm_state = true;
628 osys->wm_state = state;
630 vlc_mutex_unlock(&osys->lock);
631 break;
633 #endif
634 case VOUT_DISPLAY_EVENT_DISPLAY_SIZE: {
635 const int width = (int)va_arg(args, int);
636 const int height = (int)va_arg(args, int);
637 msg_Dbg(vd, "VoutDisplayEvent 'resize' %dx%d", width, height);
639 /* */
640 vlc_mutex_lock(&osys->lock);
642 osys->ch_display_size = true;
643 osys->display_width = width;
644 osys->display_height = height;
646 vlc_mutex_unlock(&osys->lock);
647 break;
650 case VOUT_DISPLAY_EVENT_PICTURES_INVALID: {
651 msg_Warn(vd, "VoutDisplayEvent 'pictures invalid'");
653 /* */
654 assert(vd->info.has_pictures_invalid);
656 vlc_mutex_lock(&osys->lock);
657 osys->reset_pictures = true;
658 vlc_mutex_unlock(&osys->lock);
659 break;
661 default:
662 msg_Err(vd, "VoutDisplayEvent received event %d", event);
663 /* TODO add an assert when all event are handled */
664 break;
668 static vout_window_t *VoutDisplayNewWindow(vout_display_t *vd, unsigned type)
670 vout_display_owner_sys_t *osys = vd->owner.sys;
671 vout_window_t *window = vout_NewDisplayWindow(osys->vout, type);
672 if (window != NULL)
673 vout_display_window_Attach(window, vd);
674 return window;
677 static void VoutDisplayDelWindow(vout_display_t *vd, vout_window_t *window)
679 vout_display_owner_sys_t *osys = vd->owner.sys;
681 if (window != NULL)
682 vout_display_window_Detach(window);
683 vout_DeleteDisplayWindow(osys->vout, window);
686 static void VoutDisplayFitWindow(vout_display_t *vd, bool default_size)
688 vout_display_owner_sys_t *osys = vd->owner.sys;
689 vout_display_cfg_t cfg = osys->cfg;
691 if (!cfg.is_display_filled)
692 return;
694 cfg.display.width = 0;
695 if (default_size) {
696 cfg.display.height = 0;
697 } else {
698 cfg.zoom.num = 1;
699 cfg.zoom.den = 1;
702 unsigned display_width;
703 unsigned display_height;
704 vout_display_GetDefaultDisplaySize(&display_width, &display_height,
705 &vd->source, &cfg);
706 vout_SetDisplayWindowSize(osys->vout, display_width, display_height);
709 static void VoutDisplayCropRatio(int *left, int *top, int *right, int *bottom,
710 const video_format_t *source,
711 unsigned num, unsigned den)
713 unsigned scaled_width = (uint64_t)source->i_visible_height * num * source->i_sar_den / den / source->i_sar_num;
714 unsigned scaled_height = (uint64_t)source->i_visible_width * den * source->i_sar_num / num / source->i_sar_den;
716 if (scaled_width < source->i_visible_width) {
717 *left = (source->i_visible_width - scaled_width) / 2;
718 *top = 0;
719 *right = *left + scaled_width;
720 *bottom = *top + source->i_visible_height;
721 } else {
722 *left = 0;
723 *top = (source->i_visible_height - scaled_height) / 2;
724 *right = *left + source->i_visible_width;
725 *bottom = *top + scaled_height;
729 bool vout_ManageDisplay(vout_display_t *vd, bool allow_reset_pictures)
731 vout_display_owner_sys_t *osys = vd->owner.sys;
733 vout_display_Manage(vd);
735 /* Handle mouse timeout */
736 const mtime_t date = mdate();
737 bool hide_mouse = false;
739 vlc_mutex_lock(&osys->lock);
741 if (!osys->mouse.is_hidden &&
742 osys->mouse.last_moved + osys->mouse.hide_timeout < date) {
743 osys->mouse.is_hidden = hide_mouse = true;
744 } else if (osys->mouse.ch_activity) {
745 if (osys->mouse.is_hidden)
746 vout_HideWindowMouse(osys->vout, false);
747 osys->mouse.is_hidden = false;
749 osys->mouse.ch_activity = false;
750 vlc_mutex_unlock(&osys->lock);
752 if (hide_mouse) {
753 msg_Dbg(vd, "auto hiding mouse cursor");
754 if (vout_HideWindowMouse(osys->vout, true) != VLC_SUCCESS
755 && vd->info.needs_hide_mouse)
756 vout_display_Control(vd, VOUT_DISPLAY_HIDE_MOUSE);
759 bool reset_render = false;
760 for (;;) {
762 vlc_mutex_lock(&osys->lock);
763 #if defined(_WIN32) || defined(__OS2__)
764 bool ch_fullscreen = osys->ch_fullscreen;
765 bool is_fullscreen = osys->is_fullscreen;
766 osys->ch_fullscreen = false;
768 bool ch_wm_state = osys->ch_wm_state;
769 unsigned wm_state = osys->wm_state;
770 osys->ch_wm_state = false;
771 #endif
773 bool ch_display_size = osys->ch_display_size;
774 int display_width = osys->display_width;
775 int display_height = osys->display_height;
776 osys->ch_display_size = false;
778 bool reset_pictures;
779 if (allow_reset_pictures) {
780 reset_pictures = osys->reset_pictures;
781 osys->reset_pictures = false;
782 } else {
783 reset_pictures = false;
786 vlc_mutex_unlock(&osys->lock);
788 if (!ch_display_size &&
789 !reset_pictures &&
790 osys->is_display_filled == osys->cfg.is_display_filled &&
791 !osys->ch_zoom &&
792 #if defined(_WIN32) || defined(__OS2__)
793 !ch_fullscreen &&
794 !ch_wm_state &&
795 #endif
796 !osys->ch_sar &&
797 !osys->ch_crop &&
798 !osys->ch_viewpoint) {
800 if (osys->fit_window != 0) {
801 VoutDisplayFitWindow(vd, osys->fit_window == -1);
802 osys->fit_window = 0;
803 continue;
805 break;
808 /* */
809 #if defined(_WIN32) || defined(__OS2__)
810 if (ch_fullscreen) {
811 if (osys->window_fullscreen
812 || vout_display_Control(vd, VOUT_DISPLAY_CHANGE_FULLSCREEN,
813 is_fullscreen) == VLC_SUCCESS) {
814 osys->cfg.is_fullscreen = is_fullscreen;
816 if (!is_fullscreen && !osys->window_fullscreen)
817 vout_SetDisplayWindowSize(osys->vout, osys->width_saved,
818 osys->height_saved);
819 } else {
820 is_fullscreen = osys->cfg.is_fullscreen;
822 msg_Err(vd, "Failed to set fullscreen");
825 #endif
827 /* */
828 if (ch_display_size) {
829 #if defined(_WIN32) || defined(__OS2__)
830 osys->width_saved = osys->cfg.display.width;
831 osys->height_saved = osys->cfg.display.height;
832 #endif
833 osys->cfg.display.width = display_width;
834 osys->cfg.display.height = display_height;
836 vout_display_Control(vd, VOUT_DISPLAY_CHANGE_DISPLAY_SIZE,
837 &osys->cfg);
839 /* */
840 if (osys->is_display_filled != osys->cfg.is_display_filled) {
841 osys->cfg.is_display_filled = osys->is_display_filled;
843 vout_display_Control(vd, VOUT_DISPLAY_CHANGE_DISPLAY_FILLED,
844 &osys->cfg);
846 /* */
847 if (osys->ch_zoom) {
848 osys->fit_window = -1;
849 osys->cfg.zoom.num = osys->zoom.num;
850 osys->cfg.zoom.den = osys->zoom.den;
851 osys->ch_zoom = false;
853 vout_display_Control(vd, VOUT_DISPLAY_CHANGE_ZOOM, &osys->cfg);
855 #if defined(_WIN32) || defined(__OS2__)
856 /* */
857 if (ch_wm_state) {
858 if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_WINDOW_STATE, wm_state)) {
859 msg_Err(vd, "Failed to set on top");
860 wm_state = osys->wm_state;
862 osys->wm_state_initial = wm_state;
864 #endif
865 /* */
866 if (osys->ch_sar) {
867 if (osys->sar.num > 0 && osys->sar.den > 0) {
868 vd->source.i_sar_num = osys->sar.num;
869 vd->source.i_sar_den = osys->sar.den;
870 } else {
871 vd->source.i_sar_num = osys->source.i_sar_num;
872 vd->source.i_sar_den = osys->source.i_sar_den;
875 vout_display_Control(vd, VOUT_DISPLAY_CHANGE_SOURCE_ASPECT);
876 if (!osys->fit_window)
877 osys->fit_window = 1;
878 osys->sar.num = vd->source.i_sar_num;
879 osys->sar.den = vd->source.i_sar_den;
880 osys->ch_sar = false;
882 /* If a crop ratio is requested, recompute the parameters */
883 if (osys->crop.num != 0 && osys->crop.den != 0)
884 osys->ch_crop = true;
886 /* */
887 if (osys->ch_crop) {
888 unsigned crop_num = osys->crop.num;
889 unsigned crop_den = osys->crop.den;
890 if (crop_num != 0 && crop_den != 0) {
891 video_format_t fmt = osys->source;
892 fmt.i_sar_num = vd->source.i_sar_num;
893 fmt.i_sar_den = vd->source.i_sar_den;
894 VoutDisplayCropRatio(&osys->crop.left, &osys->crop.top,
895 &osys->crop.right, &osys->crop.bottom,
896 &fmt, crop_num, crop_den);
898 const int right_max = osys->source.i_x_offset + osys->source.i_visible_width;
899 const int bottom_max = osys->source.i_y_offset + osys->source.i_visible_height;
900 int left = VLC_CLIP((int)osys->source.i_x_offset + osys->crop.left,
901 0, right_max - 1);
902 int top = VLC_CLIP((int)osys->source.i_y_offset + osys->crop.top,
903 0, bottom_max - 1);
904 int right, bottom;
905 if (osys->crop.right <= 0)
906 right = (int)(osys->source.i_x_offset + osys->source.i_visible_width) + osys->crop.right;
907 else
908 right = (int)osys->source.i_x_offset + osys->crop.right;
909 right = VLC_CLIP(right, left + 1, right_max);
910 if (osys->crop.bottom <= 0)
911 bottom = (int)(osys->source.i_y_offset + osys->source.i_visible_height) + osys->crop.bottom;
912 else
913 bottom = (int)osys->source.i_y_offset + osys->crop.bottom;
914 bottom = VLC_CLIP(bottom, top + 1, bottom_max);
916 vd->source.i_x_offset = left;
917 vd->source.i_y_offset = top;
918 vd->source.i_visible_width = right - left;
919 vd->source.i_visible_height = bottom - top;
920 video_format_Print(VLC_OBJECT(vd), "SOURCE ", &osys->source);
921 video_format_Print(VLC_OBJECT(vd), "CROPPED", &vd->source);
922 vout_display_Control(vd, VOUT_DISPLAY_CHANGE_SOURCE_CROP);
924 if (!osys->fit_window)
925 osys->fit_window = 1;
926 osys->crop.left = left - osys->source.i_x_offset;
927 osys->crop.top = top - osys->source.i_y_offset;
928 /* FIXME for right/bottom we should keep the 'type' border vs window */
929 osys->crop.right = right -
930 (osys->source.i_x_offset + osys->source.i_visible_width);
931 osys->crop.bottom = bottom -
932 (osys->source.i_y_offset + osys->source.i_visible_height);
933 osys->crop.num = crop_num;
934 osys->crop.den = crop_den;
935 osys->ch_crop = false;
937 if (osys->ch_viewpoint) {
938 vout_display_cfg_t cfg = osys->cfg;
940 cfg.viewpoint = osys->viewpoint;
942 if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_VIEWPOINT, &cfg)) {
943 msg_Err(vd, "Failed to change Viewpoint");
944 osys->viewpoint = osys->cfg.viewpoint;
946 osys->cfg.viewpoint = osys->viewpoint;
947 osys->ch_viewpoint = false;
950 /* */
951 if (reset_pictures) {
952 if (vout_display_Control(vd, VOUT_DISPLAY_RESET_PICTURES)) {
953 /* FIXME what to do here ? */
954 msg_Err(vd, "Failed to reset pictures (probably fatal)");
956 reset_render = true;
959 if (reset_render)
960 VoutDisplayResetRender(vd);
962 return reset_render;
965 bool vout_AreDisplayPicturesInvalid(vout_display_t *vd)
967 vout_display_owner_sys_t *osys = vd->owner.sys;
969 vlc_mutex_lock(&osys->lock);
970 const bool reset_pictures = osys->reset_pictures;
971 vlc_mutex_unlock(&osys->lock);
973 return reset_pictures;
976 bool vout_IsDisplayFiltered(vout_display_t *vd)
978 vout_display_owner_sys_t *osys = vd->owner.sys;
980 return osys->filters == NULL || !filter_chain_IsEmpty(osys->filters);
983 picture_t *vout_FilterDisplay(vout_display_t *vd, picture_t *picture)
985 vout_display_owner_sys_t *osys = vd->owner.sys;
987 if (osys->filters == NULL) {
988 picture_Release(picture);
989 return NULL;
992 return filter_chain_VideoFilter(osys->filters, picture);
995 void vout_FilterFlush(vout_display_t *vd)
997 vout_display_owner_sys_t *osys = vd->owner.sys;
999 if (osys->filters != NULL)
1000 filter_chain_VideoFlush(osys->filters);
1003 void vout_UpdateDisplaySourceProperties(vout_display_t *vd, const video_format_t *source)
1005 vout_display_owner_sys_t *osys = vd->owner.sys;
1007 if (source->i_sar_num * osys->source.i_sar_den !=
1008 source->i_sar_den * osys->source.i_sar_num) {
1010 osys->source.i_sar_num = source->i_sar_num;
1011 osys->source.i_sar_den = source->i_sar_den;
1012 vlc_ureduce(&osys->source.i_sar_num, &osys->source.i_sar_den,
1013 osys->source.i_sar_num, osys->source.i_sar_den, 0);
1015 /* FIXME it will override any AR that the user would have forced */
1016 osys->ch_sar = true;
1017 osys->sar.num = osys->source.i_sar_num;
1018 osys->sar.den = osys->source.i_sar_den;
1020 if (source->i_x_offset != osys->source.i_x_offset ||
1021 source->i_y_offset != osys->source.i_y_offset ||
1022 source->i_visible_width != osys->source.i_visible_width ||
1023 source->i_visible_height != osys->source.i_visible_height) {
1025 video_format_CopyCrop(&osys->source, source);
1027 /* Force the vout to reapply the current user crop settings over the new decoder
1028 * crop settings. */
1029 osys->ch_crop = true;
1033 void vout_SetDisplayFilled(vout_display_t *vd, bool is_filled)
1035 vout_display_owner_sys_t *osys = vd->owner.sys;
1037 osys->is_display_filled = is_filled;
1040 void vout_SetDisplayZoom(vout_display_t *vd, unsigned num, unsigned den)
1042 vout_display_owner_sys_t *osys = vd->owner.sys;
1044 if (num != 0 && den != 0) {
1045 vlc_ureduce(&num, &den, num, den, 0);
1046 } else {
1047 num = 1;
1048 den = 1;
1051 if (10 * num <= den) {
1052 num = 1;
1053 den = 10;
1054 } else if (num >= 10 * den) {
1055 num = 10;
1056 den = 1;
1059 if (osys->is_display_filled ||
1060 osys->zoom.num != num || osys->zoom.den != den) {
1061 osys->ch_zoom = true;
1062 osys->zoom.num = num;
1063 osys->zoom.den = den;
1067 void vout_SetDisplayAspect(vout_display_t *vd, unsigned dar_num, unsigned dar_den)
1069 vout_display_owner_sys_t *osys = vd->owner.sys;
1071 unsigned sar_num, sar_den;
1072 if (dar_num > 0 && dar_den > 0) {
1073 sar_num = dar_num * osys->source.i_visible_height;
1074 sar_den = dar_den * osys->source.i_visible_width;
1075 vlc_ureduce(&sar_num, &sar_den, sar_num, sar_den, 0);
1076 } else {
1077 sar_num = 0;
1078 sar_den = 0;
1081 if (osys->sar.num != sar_num || osys->sar.den != sar_den) {
1082 osys->ch_sar = true;
1083 osys->sar.num = sar_num;
1084 osys->sar.den = sar_den;
1087 void vout_SetDisplayCrop(vout_display_t *vd,
1088 unsigned crop_num, unsigned crop_den,
1089 unsigned left, unsigned top, int right, int bottom)
1091 vout_display_owner_sys_t *osys = vd->owner.sys;
1093 if (osys->crop.left != (int)left || osys->crop.top != (int)top ||
1094 osys->crop.right != right || osys->crop.bottom != bottom ||
1095 (crop_num != 0 && crop_den != 0 &&
1096 (crop_num != osys->crop.num || crop_den != osys->crop.den))) {
1098 osys->crop.left = left;
1099 osys->crop.top = top;
1100 osys->crop.right = right;
1101 osys->crop.bottom = bottom;
1102 osys->crop.num = crop_num;
1103 osys->crop.den = crop_den;
1105 osys->ch_crop = true;
1109 void vout_SetDisplayViewpoint(vout_display_t *vd,
1110 const vlc_viewpoint_t *p_viewpoint)
1112 vout_display_owner_sys_t *osys = vd->owner.sys;
1114 if (osys->viewpoint.yaw != p_viewpoint->yaw ||
1115 osys->viewpoint.pitch != p_viewpoint->pitch ||
1116 osys->viewpoint.roll != p_viewpoint->roll ||
1117 osys->viewpoint.fov != p_viewpoint->fov) {
1118 osys->viewpoint = *p_viewpoint;
1120 osys->ch_viewpoint = true;
1124 static vout_display_t *DisplayNew(vout_thread_t *vout,
1125 const video_format_t *source,
1126 const vout_display_state_t *state,
1127 const char *module, bool is_splitter,
1128 mtime_t double_click_timeout,
1129 mtime_t hide_timeout,
1130 const vout_display_owner_t *owner_ptr)
1132 /* */
1133 vout_display_owner_sys_t *osys = calloc(1, sizeof(*osys));
1134 vout_display_cfg_t *cfg = &osys->cfg;
1136 *cfg = state->cfg;
1137 osys->sar_initial = state->sar;
1138 vout_display_GetDefaultDisplaySize(&cfg->display.width, &cfg->display.height,
1139 source, cfg);
1141 osys->vout = vout;
1142 osys->is_splitter = is_splitter;
1144 vlc_mutex_init(&osys->lock);
1146 vlc_mouse_Init(&osys->mouse.state);
1147 osys->mouse.last_moved = mdate();
1148 osys->mouse.double_click_timeout = double_click_timeout;
1149 osys->mouse.hide_timeout = hide_timeout;
1150 osys->display_width = cfg->display.width;
1151 osys->display_height = cfg->display.height;
1152 osys->is_display_filled = cfg->is_display_filled;
1153 osys->viewpoint = cfg->viewpoint;
1155 osys->zoom.num = cfg->zoom.num;
1156 osys->zoom.den = cfg->zoom.den;
1157 #if defined(_WIN32) || defined(__OS2__)
1158 osys->is_fullscreen = cfg->is_fullscreen;
1159 osys->width_saved = cfg->display.width;
1160 osys->height_saved = cfg->display.height;
1161 if (osys->is_fullscreen) {
1162 vout_display_cfg_t cfg_windowed = *cfg;
1163 cfg_windowed.is_fullscreen = false;
1164 cfg_windowed.display.width = 0;
1165 cfg_windowed.display.height = 0;
1166 vout_display_GetDefaultDisplaySize(&osys->width_saved,
1167 &osys->height_saved,
1168 source, &cfg_windowed);
1171 osys->wm_state_initial = VOUT_WINDOW_STATE_NORMAL;
1172 osys->wm_state = state->wm_state;
1173 osys->ch_wm_state = true;
1174 #endif
1175 osys->fit_window = 0;
1177 osys->source = *source;
1178 osys->crop.left = 0;
1179 osys->crop.top = 0;
1180 osys->crop.right = 0;
1181 osys->crop.bottom = 0;
1182 osys->crop.num = 0;
1183 osys->crop.den = 0;
1185 osys->sar.num = osys->sar_initial.num ? osys->sar_initial.num : source->i_sar_num;
1186 osys->sar.den = osys->sar_initial.den ? osys->sar_initial.den : source->i_sar_den;
1188 vout_display_owner_t owner;
1189 if (owner_ptr) {
1190 owner = *owner_ptr;
1191 } else {
1192 owner.event = VoutDisplayEvent;
1193 owner.window_new = VoutDisplayNewWindow;
1194 owner.window_del = VoutDisplayDelWindow;
1196 owner.sys = osys;
1198 vout_display_t *p_display = vout_display_New(VLC_OBJECT(vout),
1199 module, !is_splitter,
1200 source, cfg, &owner);
1201 if (!p_display)
1202 goto error;
1204 if (VoutDisplayCreateRender(p_display)) {
1205 vout_display_Delete(p_display);
1206 goto error;
1209 /* Setup delayed request */
1210 if (osys->sar.num != source->i_sar_num ||
1211 osys->sar.den != source->i_sar_den)
1212 osys->ch_sar = true;
1214 vout_SendEventViewpointChangeable(osys->vout,
1215 p_display->fmt.projection_mode != PROJECTION_MODE_RECTANGULAR);
1217 return p_display;
1218 error:
1219 vlc_mutex_destroy(&osys->lock);
1220 free(osys);
1221 return NULL;
1224 void vout_DeleteDisplay(vout_display_t *vd, vout_display_state_t *state)
1226 vout_display_owner_sys_t *osys = vd->owner.sys;
1228 if (state) {
1229 if (!osys->is_splitter)
1230 state->cfg = osys->cfg;
1231 #if defined(_WIN32) || defined(__OS2__)
1232 state->wm_state = osys->wm_state;
1233 #endif
1234 state->sar = osys->sar_initial;
1237 VoutDisplayDestroyRender(vd);
1238 if (osys->is_splitter)
1239 SplitterClose(vd);
1240 vout_display_Delete(vd);
1241 vlc_mutex_destroy(&osys->lock);
1242 free(osys);
1245 /*****************************************************************************
1247 *****************************************************************************/
1248 vout_display_t *vout_NewDisplay(vout_thread_t *vout,
1249 const video_format_t *source,
1250 const vout_display_state_t *state,
1251 const char *module,
1252 mtime_t double_click_timeout,
1253 mtime_t hide_timeout)
1255 return DisplayNew(vout, source, state, module, false,
1256 double_click_timeout, hide_timeout, NULL);
1259 /*****************************************************************************
1261 *****************************************************************************/
1262 struct vout_display_sys_t {
1263 picture_pool_t *pool;
1264 video_splitter_t *splitter;
1266 /* */
1267 int count;
1268 picture_t **picture;
1269 vout_display_t **display;
1271 struct video_splitter_owner_t {
1272 vout_display_t *wrapper;
1275 static vout_window_t *SplitterNewWindow(vout_display_t *vd, unsigned type)
1277 vout_display_owner_sys_t *osys = vd->owner.sys;
1278 vout_window_t *window;
1279 vout_window_cfg_t cfg = {
1280 .type = type,
1281 .width = vd->cfg->display.width,
1282 .height = vd->cfg->display.height,
1283 .is_standalone = true,
1286 window = vout_display_window_New(osys->vout, &cfg);
1287 if (window != NULL)
1288 vout_display_window_Attach(window, vd);
1289 return window;
1292 static void SplitterDelWindow(vout_display_t *vd, vout_window_t *window)
1294 if (window != NULL) {
1295 vout_display_window_Detach(window);
1296 vout_display_window_Delete(window);
1298 (void) vd;
1301 static void SplitterEvent(vout_display_t *vd, int event, va_list args)
1303 //vout_display_owner_sys_t *osys = vd->owner.sys;
1305 switch (event) {
1306 #if 0
1307 case VOUT_DISPLAY_EVENT_MOUSE_STATE:
1308 case VOUT_DISPLAY_EVENT_MOUSE_MOVED:
1309 case VOUT_DISPLAY_EVENT_MOUSE_PRESSED:
1310 case VOUT_DISPLAY_EVENT_MOUSE_RELEASED:
1311 /* TODO */
1312 break;
1313 #endif
1314 case VOUT_DISPLAY_EVENT_MOUSE_DOUBLE_CLICK:
1315 case VOUT_DISPLAY_EVENT_KEY:
1316 case VOUT_DISPLAY_EVENT_CLOSE:
1317 case VOUT_DISPLAY_EVENT_DISPLAY_SIZE:
1318 case VOUT_DISPLAY_EVENT_PICTURES_INVALID:
1319 VoutDisplayEvent(vd, event, args);
1320 break;
1322 default:
1323 msg_Err(vd, "splitter event not implemented: %d", event);
1324 break;
1328 static picture_pool_t *SplitterPool(vout_display_t *vd, unsigned count)
1330 vout_display_sys_t *sys = vd->sys;
1331 if (!sys->pool)
1332 sys->pool = picture_pool_NewFromFormat(&vd->fmt, count);
1333 return sys->pool;
1335 static void SplitterPrepare(vout_display_t *vd,
1336 picture_t *picture,
1337 subpicture_t *subpicture)
1339 vout_display_sys_t *sys = vd->sys;
1341 picture_Hold(picture);
1342 assert(!subpicture);
1344 if (video_splitter_Filter(sys->splitter, sys->picture, picture)) {
1345 for (int i = 0; i < sys->count; i++)
1346 sys->picture[i] = NULL;
1347 return;
1350 for (int i = 0; i < sys->count; i++) {
1351 sys->picture[i] = vout_FilterDisplay(sys->display[i], sys->picture[i]);
1352 if (sys->picture[i])
1353 vout_display_Prepare(sys->display[i], sys->picture[i], NULL);
1356 static void SplitterDisplay(vout_display_t *vd,
1357 picture_t *picture,
1358 subpicture_t *subpicture)
1360 vout_display_sys_t *sys = vd->sys;
1362 assert(!subpicture);
1363 for (int i = 0; i < sys->count; i++) {
1364 if (sys->picture[i])
1365 vout_display_Display(sys->display[i], sys->picture[i], NULL);
1367 picture_Release(picture);
1369 static int SplitterControl(vout_display_t *vd, int query, va_list args)
1371 (void)vd; (void)query; (void)args;
1372 return VLC_EGENERIC;
1374 static void SplitterManage(vout_display_t *vd)
1376 vout_display_sys_t *sys = vd->sys;
1378 for (int i = 0; i < sys->count; i++)
1379 vout_ManageDisplay(sys->display[i], true);
1382 static int SplitterPictureNew(video_splitter_t *splitter, picture_t *picture[])
1384 vout_display_sys_t *wsys = splitter->p_owner->wrapper->sys;
1386 for (int i = 0; i < wsys->count; i++) {
1387 if (vout_IsDisplayFiltered(wsys->display[i])) {
1388 /* TODO use a pool ? */
1389 picture[i] = picture_NewFromFormat(&wsys->display[i]->source);
1390 } else {
1391 picture_pool_t *pool = vout_display_Pool(wsys->display[i], 3);
1392 picture[i] = pool ? picture_pool_Get(pool) : NULL;
1394 if (!picture[i]) {
1395 for (int j = 0; j < i; j++)
1396 picture_Release(picture[j]);
1397 return VLC_EGENERIC;
1400 return VLC_SUCCESS;
1402 static void SplitterPictureDel(video_splitter_t *splitter, picture_t *picture[])
1404 vout_display_sys_t *wsys = splitter->p_owner->wrapper->sys;
1406 for (int i = 0; i < wsys->count; i++)
1407 picture_Release(picture[i]);
1409 static void SplitterClose(vout_display_t *vd)
1411 vout_display_sys_t *sys = vd->sys;
1413 /* */
1414 video_splitter_t *splitter = sys->splitter;
1415 free(splitter->p_owner);
1416 video_splitter_Delete(splitter);
1418 if (sys->pool)
1419 picture_pool_Release(sys->pool);
1421 /* */
1422 for (int i = 0; i < sys->count; i++)
1423 vout_DeleteDisplay(sys->display[i], NULL);
1424 TAB_CLEAN(sys->count, sys->display);
1425 free(sys->picture);
1427 free(sys);
1430 vout_display_t *vout_NewSplitter(vout_thread_t *vout,
1431 const video_format_t *source,
1432 const vout_display_state_t *state,
1433 const char *module,
1434 const char *splitter_module,
1435 mtime_t double_click_timeout,
1436 mtime_t hide_timeout)
1438 video_splitter_t *splitter =
1439 video_splitter_New(VLC_OBJECT(vout), splitter_module, source);
1440 if (!splitter)
1441 return NULL;
1443 /* */
1444 vout_display_t *wrapper =
1445 DisplayNew(vout, source, state, module, true,
1446 double_click_timeout, hide_timeout, NULL);
1447 if (!wrapper) {
1448 video_splitter_Delete(splitter);
1449 return NULL;
1451 vout_display_sys_t *sys = malloc(sizeof(*sys));
1452 if (!sys)
1453 abort();
1454 sys->picture = calloc(splitter->i_output, sizeof(*sys->picture));
1455 if (!sys->picture )
1456 abort();
1457 sys->splitter = splitter;
1458 sys->pool = NULL;
1460 wrapper->pool = SplitterPool;
1461 wrapper->prepare = SplitterPrepare;
1462 wrapper->display = SplitterDisplay;
1463 wrapper->control = SplitterControl;
1464 wrapper->manage = SplitterManage;
1465 wrapper->sys = sys;
1467 /* */
1468 video_splitter_owner_t *vso = xmalloc(sizeof(*vso));
1469 vso->wrapper = wrapper;
1470 splitter->p_owner = vso;
1471 splitter->pf_picture_new = SplitterPictureNew;
1472 splitter->pf_picture_del = SplitterPictureDel;
1474 /* */
1475 TAB_INIT(sys->count, sys->display);
1476 for (int i = 0; i < splitter->i_output; i++) {
1477 vout_display_owner_t vdo = {
1478 .event = SplitterEvent,
1479 .window_new = SplitterNewWindow,
1480 .window_del = SplitterDelWindow,
1482 const video_splitter_output_t *output = &splitter->p_output[i];
1483 vout_display_state_t ostate;
1485 memset(&ostate, 0, sizeof(ostate));
1486 ostate.cfg.display = state->cfg.display;
1487 ostate.cfg.align.horizontal = 0; /* TODO */
1488 ostate.cfg.align.vertical = 0; /* TODO */
1489 ostate.cfg.is_display_filled = true;
1490 ostate.cfg.zoom.num = 1;
1491 ostate.cfg.zoom.den = 1;
1493 vout_display_t *vd = DisplayNew(vout, &output->fmt, &ostate,
1494 output->psz_module ? output->psz_module : module,
1495 false,
1496 double_click_timeout, hide_timeout, &vdo);
1497 if (!vd) {
1498 vout_DeleteDisplay(wrapper, NULL);
1499 return NULL;
1501 TAB_APPEND(sys->count, sys->display, vd);
1504 return wrapper;
1507 /*****************************************************************************
1508 * TODO move out
1509 *****************************************************************************/
1510 #include "vout_internal.h"
1511 void vout_SendDisplayEventMouse(vout_thread_t *vout, const vlc_mouse_t *m)
1513 vlc_mouse_t tmp1, tmp2;
1515 /* The check on spu is needed as long as ALLOW_DUMMY_VOUT is defined */
1516 if (vout->p->spu && spu_ProcessMouse( vout->p->spu, m, &vout->p->display.vd->source))
1517 return;
1519 vlc_mutex_lock( &vout->p->filter.lock );
1520 if (vout->p->filter.chain_static && vout->p->filter.chain_interactive) {
1521 if (!filter_chain_MouseFilter(vout->p->filter.chain_interactive, &tmp1, m))
1522 m = &tmp1;
1523 if (!filter_chain_MouseFilter(vout->p->filter.chain_static, &tmp2, m))
1524 m = &tmp2;
1526 vlc_mutex_unlock( &vout->p->filter.lock );
1528 if (vlc_mouse_HasMoved(&vout->p->mouse, m)) {
1529 vout_SendEventMouseMoved(vout, m->i_x, m->i_y);
1531 if (vlc_mouse_HasButton(&vout->p->mouse, m)) {
1532 for (unsigned button = 0; button < MOUSE_BUTTON_MAX; button++) {
1533 if (vlc_mouse_HasPressed(&vout->p->mouse, m, button))
1534 vout_SendEventMousePressed(vout, button);
1535 else if (vlc_mouse_HasReleased(&vout->p->mouse, m, button))
1536 vout_SendEventMouseReleased(vout, button);
1539 if (m->b_double_click)
1540 vout_SendEventMouseDoubleClick(vout);
1541 vout->p->mouse = *m;