1 /*****************************************************************************
2 * display.c: "vout display" managment
3 *****************************************************************************
4 * Copyright (C) 2009 Laurent Aimar
7 * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
32 #include <vlc_common.h>
33 #include <vlc_video_splitter.h>
34 #include <vlc_vout_display.h>
36 #include <vlc_block.h>
37 #include <vlc_modules.h>
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
49 static void SplitterClose(vout_display_t *vd);
51 /*****************************************************************************
52 * FIXME/TODO see how to have direct rendering here (interact with vout.c)
53 *****************************************************************************/
54 static picture_t *VideoBufferNew(filter_t *filter)
56 vout_display_t *vd = (vout_display_t*)filter->p_owner;
57 const video_format_t *fmt = &filter->fmt_out.video;
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);
63 picture_pool_t *pool = vout_display_Pool(vd, 3);
66 return picture_pool_Get(pool);
68 static void VideoBufferDelete(filter_t *filter, picture_t *picture)
71 picture_Release(picture);
74 static int FilterAllocationInit(filter_t *filter, void *vd)
76 filter->pf_video_buffer_new = VideoBufferNew;
77 filter->pf_video_buffer_del = VideoBufferDelete;
82 static void FilterAllocationClean(filter_t *filter)
84 filter->pf_video_buffer_new = NULL;
85 filter->pf_video_buffer_del = NULL;
86 filter->p_owner = NULL;
89 /*****************************************************************************
91 *****************************************************************************/
94 * It creates a new vout_display_t using the given configuration.
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)
103 vout_display_t *vd = vlc_custom_create(obj, sizeof(*vd),
104 VLC_OBJECT_GENERIC, "vout display");
107 video_format_Copy(&vd->source, fmt);
109 /* Picture buffer does not have the concept of aspect ratio */
110 video_format_Copy(&vd->fmt, fmt);
111 vd->fmt.i_sar_num = 0;
112 vd->fmt.i_sar_den = 0;
114 vd->info.is_slow = false;
115 vd->info.has_double_click = false;
116 vd->info.has_hide_mouse = false;
117 vd->info.has_pictures_invalid = false;
118 vd->info.has_event_thread = false;
119 vd->info.subpicture_chromas = NULL;
132 vd->module = module_need(vd, "vout display", module, module && *module != '\0');
134 vlc_object_release(vd);
144 * It deletes a vout_display_t
146 static void vout_display_Delete(vout_display_t *vd)
149 module_unneed(vd, vd->module);
151 vlc_object_release(vd);
155 * It controls a vout_display_t
157 static int vout_display_Control(vout_display_t *vd, int query, ...)
162 va_start(args, query);
163 result = vd->control(vd, query, args);
169 static void vout_display_Manage(vout_display_t *vd)
176 void vout_display_GetDefaultDisplaySize(unsigned *width, unsigned *height,
177 const video_format_t *source,
178 const vout_display_cfg_t *cfg)
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;
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;
199 *width = *width * cfg->zoom.num / cfg->zoom.den;
200 *height = *height * cfg->zoom.num / cfg->zoom.den;
204 void vout_display_PlacePicture(vout_display_place_t *place,
205 const video_format_t *source,
206 const vout_display_cfg_t *cfg,
210 memset(place, 0, sizeof(*place));
211 if (cfg->display.width <= 0 || cfg->display.height <= 0)
215 unsigned display_width;
216 unsigned display_height;
218 if (cfg->is_display_filled) {
219 display_width = cfg->display.width;
220 display_height = cfg->display.height;
222 vout_display_cfg_t cfg_tmp = *cfg;
224 cfg_tmp.display.width = 0;
225 cfg_tmp.display.height = 0;
226 vout_display_GetDefaultDisplaySize(&display_width, &display_height,
230 display_width = __MIN(display_width, cfg->display.width);
231 display_height = __MIN(display_height, cfg->display.height);
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;
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;
247 place->width = display_width;
248 place->height = scaled_height;
251 /* Compute position */
252 switch (cfg->align.horizontal) {
253 case VOUT_DISPLAY_ALIGN_LEFT:
256 case VOUT_DISPLAY_ALIGN_RIGHT:
257 place->x = cfg->display.width - place->width;
260 place->x = ((int)cfg->display.width - (int)place->width) / 2;
264 switch (cfg->align.vertical) {
265 case VOUT_DISPLAY_ALIGN_TOP:
268 case VOUT_DISPLAY_ALIGN_BOTTOM:
269 place->y = cfg->display.height - place->height;
272 place->y = ((int)cfg->display.height - (int)place->height) / 2;
277 struct vout_display_owner_sys_t {
279 bool is_wrapper; /* Is the current display a wrapper */
280 vout_display_t *wrapper; /* Vout display wrapper */
283 vout_display_cfg_t cfg;
284 unsigned wm_state_initial;
291 unsigned width_saved;
292 unsigned height_saved;
300 bool ch_display_filled;
301 bool is_display_filled;
329 video_format_t source;
330 filter_chain_t *filters;
332 /* Lock protecting the variables used by
333 * VoutDisplayEvent(ie vout_display_SendEvent) */
340 mtime_t last_pressed;
346 mtime_t double_click_timeout;
347 mtime_t hide_timeout;
355 bool ch_display_size;
358 bool display_is_fullscreen;
359 bool display_is_forced;
368 #ifdef ALLOW_DUMMY_VOUT
369 vlc_mouse_t vout_mouse;
373 static void DummyVoutSendDisplayEventMouse(vout_thread_t *, vlc_mouse_t *fallback, const vlc_mouse_t *m);
375 static void VoutDisplayCreateRender(vout_display_t *vd)
377 vout_display_owner_sys_t *osys = vd->owner.sys;
379 osys->filters = NULL;
381 video_format_t v_src = vd->source;
385 video_format_t v_dst = vd->fmt;
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;
396 const bool convert = memcmp(&v_src, &v_dst_cmp, sizeof(v_src)) != 0;
400 msg_Dbg(vd, "A filter to adapt decoder to display is needed");
402 osys->filters = filter_chain_New(vd, "video filter2", false,
403 FilterAllocationInit,
404 FilterAllocationClean, vd);
405 assert(osys->filters); /* TODO critical */
409 es_format_InitFromVideo(&src, &v_src);
415 for (int i = 0; i < 1 + (v_dst_cmp.i_chroma != v_dst.i_chroma); i++) {
417 es_format_InitFromVideo(&dst, i == 0 ? &v_dst : &v_dst_cmp);
419 filter_chain_Reset(osys->filters, &src, &dst);
420 filter = filter_chain_AppendFilter(osys->filters,
421 NULL, NULL, &src, &dst);
426 msg_Err(vd, "Failed to adapt decoder format to display");
429 static void VoutDisplayDestroyRender(vout_display_t *vd)
431 vout_display_owner_sys_t *osys = vd->owner.sys;
434 filter_chain_Delete(osys->filters);
437 static void VoutDisplayResetRender(vout_display_t *vd)
439 VoutDisplayDestroyRender(vd);
440 VoutDisplayCreateRender(vd);
442 static void VoutDisplayEventMouse(vout_display_t *vd, int event, va_list args)
444 vout_display_owner_sys_t *osys = vd->owner.sys;
446 vlc_mutex_lock(&osys->lock);
449 vlc_mouse_t m = osys->mouse.state;
450 bool is_ignored = false;
453 case VOUT_DISPLAY_EVENT_MOUSE_STATE: {
454 const int x = (int)va_arg(args, int);
455 const int y = (int)va_arg(args, int);
456 const int button_mask = (int)va_arg(args, int);
461 m.i_pressed = button_mask;
464 case VOUT_DISPLAY_EVENT_MOUSE_MOVED: {
465 const int x = (int)va_arg(args, int);
466 const int y = (int)va_arg(args, int);
468 //msg_Dbg(vd, "VoutDisplayEvent 'mouse' @%d,%d", x, y);
472 m.b_double_click = false;
475 case VOUT_DISPLAY_EVENT_MOUSE_PRESSED:
476 case VOUT_DISPLAY_EVENT_MOUSE_RELEASED: {
477 const int button = (int)va_arg(args, int);
478 const int button_mask = 1 << button;
480 /* Ignore inconsistent event */
481 if ((event == VOUT_DISPLAY_EVENT_MOUSE_PRESSED && (osys->mouse.state.i_pressed & button_mask)) ||
482 (event == VOUT_DISPLAY_EVENT_MOUSE_RELEASED && !(osys->mouse.state.i_pressed & button_mask))) {
488 msg_Dbg(vd, "VoutDisplayEvent 'mouse button' %d t=%d", button, event);
490 m.b_double_click = false;
491 if (event == VOUT_DISPLAY_EVENT_MOUSE_PRESSED)
492 m.i_pressed |= button_mask;
494 m.i_pressed &= ~button_mask;
497 case VOUT_DISPLAY_EVENT_MOUSE_DOUBLE_CLICK:
498 msg_Dbg(vd, "VoutDisplayEvent 'double click'");
500 m.b_double_click = true;
507 vlc_mutex_unlock(&osys->lock);
511 /* Emulate double-click if needed */
512 if (!vd->info.has_double_click &&
513 vlc_mouse_HasPressed(&osys->mouse.state, &m, MOUSE_BUTTON_LEFT)) {
514 const mtime_t i_date = mdate();
516 if (i_date - osys->mouse.last_pressed < osys->mouse.double_click_timeout ) {
517 m.b_double_click = true;
518 osys->mouse.last_pressed = 0;
520 osys->mouse.last_pressed = mdate();
525 osys->mouse.state = m;
528 osys->mouse.ch_activity = true;
529 if (!vd->info.has_hide_mouse)
530 osys->mouse.last_moved = mdate();
533 vout_SendEventMouseVisible(osys->vout);
534 #ifdef ALLOW_DUMMY_VOUT
535 DummyVoutSendDisplayEventMouse(osys->vout, &osys->vout_mouse, &m);
537 vout_SendDisplayEventMouse(osys->vout, &m);
539 vlc_mutex_unlock(&osys->lock);
543 static void *VoutDisplayEventKeyDispatch(void *data) __attribute__((noreturn));
545 static void *VoutDisplayEventKeyDispatch(void *data)
547 vout_display_owner_sys_t *osys = data;
550 block_t *event = block_FifoGet(osys->event.fifo);
552 int cancel = vlc_savecancel();
555 memcpy(&key, event->p_buffer, sizeof(key));
556 vout_SendEventKey(osys->vout, key);
557 block_Release(event);
559 vlc_restorecancel(cancel);
563 static void VoutDisplayEventKey(vout_display_t *vd, int key)
565 vout_display_owner_sys_t *osys = vd->owner.sys;
567 if (!osys->event.fifo) {
568 osys->event.fifo = block_FifoNew();
569 if (!osys->event.fifo)
571 if (vlc_clone(&osys->event.thread, VoutDisplayEventKeyDispatch,
572 osys, VLC_THREAD_PRIORITY_LOW)) {
573 block_FifoRelease(osys->event.fifo);
574 osys->event.fifo = NULL;
578 block_t *event = block_Alloc(sizeof(key));
580 memcpy(event->p_buffer, &key, sizeof(key));
581 block_FifoPut(osys->event.fifo, event);
585 static void VoutDisplayEvent(vout_display_t *vd, int event, va_list args)
587 vout_display_owner_sys_t *osys = vd->owner.sys;
590 case VOUT_DISPLAY_EVENT_CLOSE: {
591 msg_Dbg(vd, "VoutDisplayEvent 'close'");
592 vout_SendEventClose(osys->vout);
595 case VOUT_DISPLAY_EVENT_KEY: {
596 const int key = (int)va_arg(args, int);
597 msg_Dbg(vd, "VoutDisplayEvent 'key' 0x%2.2x", key);
598 if (vd->info.has_event_thread)
599 vout_SendEventKey(osys->vout, key);
601 VoutDisplayEventKey(vd, key);
604 case VOUT_DISPLAY_EVENT_MOUSE_STATE:
605 case VOUT_DISPLAY_EVENT_MOUSE_MOVED:
606 case VOUT_DISPLAY_EVENT_MOUSE_PRESSED:
607 case VOUT_DISPLAY_EVENT_MOUSE_RELEASED:
608 case VOUT_DISPLAY_EVENT_MOUSE_DOUBLE_CLICK:
609 VoutDisplayEventMouse(vd, event, args);
612 case VOUT_DISPLAY_EVENT_FULLSCREEN: {
613 const int is_fullscreen = (int)va_arg(args, int);
615 msg_Dbg(vd, "VoutDisplayEvent 'fullscreen' %d", is_fullscreen);
617 vlc_mutex_lock(&osys->lock);
618 if (!is_fullscreen != !osys->is_fullscreen) {
619 osys->ch_fullscreen = true;
620 osys->is_fullscreen = is_fullscreen;
622 vlc_mutex_unlock(&osys->lock);
626 case VOUT_DISPLAY_EVENT_WINDOW_STATE: {
627 const unsigned state = va_arg(args, unsigned);
629 msg_Dbg(vd, "VoutDisplayEvent 'window state' %u", state);
631 vlc_mutex_lock(&osys->lock);
632 if (state != osys->wm_state) {
633 osys->ch_wm_state = true;
634 osys->wm_state = state;
636 vlc_mutex_unlock(&osys->lock);
640 case VOUT_DISPLAY_EVENT_DISPLAY_SIZE: {
641 const int width = (int)va_arg(args, int);
642 const int height = (int)va_arg(args, int);
643 const bool is_fullscreen = (bool)va_arg(args, int);
644 msg_Dbg(vd, "VoutDisplayEvent 'resize' %dx%d %s",
645 width, height, is_fullscreen ? "fullscreen" : "window");
648 vlc_mutex_lock(&osys->lock);
650 osys->ch_display_size = true;
651 osys->display_width = width;
652 osys->display_height = height;
653 osys->display_is_fullscreen = is_fullscreen;
654 osys->display_is_forced = false;
656 vlc_mutex_unlock(&osys->lock);
660 case VOUT_DISPLAY_EVENT_PICTURES_INVALID: {
661 msg_Warn(vd, "VoutDisplayEvent 'pictures invalid'");
664 assert(vd->info.has_pictures_invalid);
666 vlc_mutex_lock(&osys->lock);
667 osys->reset_pictures = true;
668 vlc_mutex_unlock(&osys->lock);
672 msg_Err(vd, "VoutDisplayEvent received event %d", event);
673 /* TODO add an assert when all event are handled */
678 static vout_window_t *VoutDisplayNewWindow(vout_display_t *vd, const vout_window_cfg_t *cfg)
680 vout_display_owner_sys_t *osys = vd->owner.sys;
682 #ifdef ALLOW_DUMMY_VOUT
683 if (!osys->vout->p) {
684 vout_window_cfg_t cfg_override = *cfg;
686 if (!var_InheritBool(osys->vout, "embedded-video"))
687 cfg_override.is_standalone = true;
689 return vout_window_New(VLC_OBJECT(osys->vout), "$window", &cfg_override);
692 return vout_NewDisplayWindow(osys->vout, vd, cfg);
694 static void VoutDisplayDelWindow(vout_display_t *vd, vout_window_t *window)
696 vout_display_owner_sys_t *osys = vd->owner.sys;
698 #ifdef ALLOW_DUMMY_VOUT
699 if (!osys->vout->p) {
701 vout_window_Delete(window);
705 vout_DeleteDisplayWindow(osys->vout, vd, window);
708 static void VoutDisplayFitWindow(vout_display_t *vd, bool default_size)
710 vout_display_owner_sys_t *osys = vd->owner.sys;
711 vout_display_cfg_t cfg = osys->cfg;
713 if (!cfg.is_display_filled)
716 cfg.display.width = 0;
718 cfg.display.height = 0;
720 cfg.display.height = osys->height_saved;
725 unsigned display_width;
726 unsigned display_height;
727 vout_display_GetDefaultDisplaySize(&display_width, &display_height,
730 vlc_mutex_lock(&osys->lock);
732 osys->ch_display_size = true;
733 osys->display_width = display_width;
734 osys->display_height = display_height;
735 osys->display_is_fullscreen = osys->cfg.is_fullscreen;
736 osys->display_is_forced = true;
738 vlc_mutex_unlock(&osys->lock);
741 static void VoutDisplayCropRatio(int *left, int *top, int *right, int *bottom,
742 const video_format_t *source,
743 unsigned num, unsigned den)
745 unsigned scaled_width = (uint64_t)source->i_visible_height * num * source->i_sar_den / den / source->i_sar_num;
746 unsigned scaled_height = (uint64_t)source->i_visible_width * den * source->i_sar_num / num / source->i_sar_den;
748 if (scaled_width < source->i_visible_width) {
749 *left = (source->i_visible_width - scaled_width) / 2;
751 *right = *left + scaled_width;
752 *bottom = *top + source->i_visible_height;
755 *top = (source->i_visible_height - scaled_height) / 2;
756 *right = *left + source->i_visible_width;
757 *bottom = *top + scaled_height;
761 void vout_ManageDisplay(vout_display_t *vd, bool allow_reset_pictures)
763 vout_display_owner_sys_t *osys = vd->owner.sys;
765 vout_display_Manage(vd);
767 /* Handle mouse timeout */
768 const mtime_t date = mdate();
769 bool hide_mouse = false;
771 vlc_mutex_lock(&osys->lock);
773 if (!osys->mouse.is_hidden &&
774 osys->mouse.last_moved + osys->mouse.hide_timeout < date) {
775 osys->mouse.is_hidden = hide_mouse = true;
776 } else if (osys->mouse.ch_activity) {
777 osys->mouse.is_hidden = false;
779 osys->mouse.ch_activity = false;
780 vlc_mutex_unlock(&osys->lock);
783 if (!vd->info.has_hide_mouse) {
784 msg_Dbg(vd, "auto hiding mouse");
785 vout_display_Control(vd, VOUT_DISPLAY_HIDE_MOUSE);
787 vout_SendEventMouseHidden(osys->vout);
790 bool reset_render = false;
793 vlc_mutex_lock(&osys->lock);
795 bool ch_fullscreen = osys->ch_fullscreen;
796 bool is_fullscreen = osys->is_fullscreen;
797 osys->ch_fullscreen = false;
799 bool ch_wm_state = osys->ch_wm_state;
800 unsigned wm_state = osys->wm_state;
801 osys->ch_wm_state = false;
803 bool ch_display_size = osys->ch_display_size;
804 int display_width = osys->display_width;
805 int display_height = osys->display_height;
806 bool display_is_fullscreen = osys->display_is_fullscreen;
807 bool display_is_forced = osys->display_is_forced;
808 osys->ch_display_size = false;
811 if (allow_reset_pictures) {
812 reset_pictures = osys->reset_pictures;
813 osys->reset_pictures = false;
815 reset_pictures = false;
818 vlc_mutex_unlock(&osys->lock);
820 if (!ch_fullscreen &&
823 !osys->ch_display_filled &&
829 if (!osys->cfg.is_fullscreen && osys->fit_window != 0) {
830 VoutDisplayFitWindow(vd, osys->fit_window == -1);
831 osys->fit_window = 0;
839 vout_display_cfg_t cfg = osys->cfg;
841 cfg.is_fullscreen = is_fullscreen;
842 cfg.display.width = cfg.is_fullscreen ? 0 : osys->width_saved;
843 cfg.display.height = cfg.is_fullscreen ? 0 : osys->height_saved;
845 if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_FULLSCREEN, &cfg)) {
846 msg_Err(vd, "Failed to set fullscreen");
847 is_fullscreen = osys->cfg.is_fullscreen;
848 } else if (!is_fullscreen) {
849 vout_display_Control(vd, VOUT_DISPLAY_CHANGE_DISPLAY_SIZE, &cfg, true);
851 osys->cfg.is_fullscreen = is_fullscreen;
854 vout_SendEventFullscreen(osys->vout, osys->cfg.is_fullscreen);
858 if (ch_display_size) {
859 vout_display_cfg_t cfg = osys->cfg;
860 cfg.display.width = display_width;
861 cfg.display.height = display_height;
863 if (!cfg.is_fullscreen != !display_is_fullscreen ||
864 vout_display_Control(vd, VOUT_DISPLAY_CHANGE_DISPLAY_SIZE, &cfg, display_is_forced)) {
865 if (!cfg.is_fullscreen == !display_is_fullscreen)
866 msg_Err(vd, "Failed to resize display");
868 /* We ignore the resized */
869 display_width = osys->cfg.display.width;
870 display_height = osys->cfg.display.height;
872 osys->cfg.display.width = display_width;
873 osys->cfg.display.height = display_height;
875 if (!display_is_fullscreen) {
876 osys->width_saved = display_width;
877 osys->height_saved = display_height;
881 if (osys->ch_display_filled) {
882 vout_display_cfg_t cfg = osys->cfg;
884 cfg.is_display_filled = osys->is_display_filled;
886 if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_DISPLAY_FILLED, &cfg)) {
887 msg_Err(vd, "Failed to change display filled state");
888 osys->is_display_filled = osys->cfg.is_display_filled;
890 osys->cfg.is_display_filled = osys->is_display_filled;
891 osys->ch_display_filled = false;
893 vout_SendEventDisplayFilled(osys->vout, osys->cfg.is_display_filled);
897 vout_display_cfg_t cfg = osys->cfg;
899 cfg.zoom.num = osys->zoom.num;
900 cfg.zoom.den = osys->zoom.den;
902 if (10 * cfg.zoom.num <= cfg.zoom.den) {
905 } else if (cfg.zoom.num >= 10 * cfg.zoom.den) {
910 if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_ZOOM, &cfg)) {
911 msg_Err(vd, "Failed to change zoom");
912 osys->zoom.num = osys->cfg.zoom.num;
913 osys->zoom.den = osys->cfg.zoom.den;
915 osys->fit_window = -1;
918 osys->cfg.zoom.num = osys->zoom.num;
919 osys->cfg.zoom.den = osys->zoom.den;
920 osys->ch_zoom = false;
922 vout_SendEventZoom(osys->vout, osys->cfg.zoom.num, osys->cfg.zoom.den);
926 if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_WINDOW_STATE, wm_state)) {
927 msg_Err(vd, "Failed to set on top");
928 wm_state = osys->wm_state;
930 osys->wm_state_initial = wm_state;
933 vout_SendEventOnTop(osys->vout, osys->wm_state_initial);
937 video_format_t source = vd->source;
939 if (osys->sar.num > 0 && osys->sar.den > 0) {
940 source.i_sar_num = osys->sar.num;
941 source.i_sar_den = osys->sar.den;
943 source.i_sar_num = osys->source.i_sar_num;
944 source.i_sar_den = osys->source.i_sar_den;
947 if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_SOURCE_ASPECT, &source)) {
948 /* There nothing much we can do. The only reason a vout display
949 * does not support it is because it need the core to add black border
950 * to the video for it.
951 * TODO add black borders ?
953 msg_Err(vd, "Failed to change source AR");
955 } else if (!osys->fit_window) {
956 osys->fit_window = 1;
959 osys->sar.num = source.i_sar_num;
960 osys->sar.den = source.i_sar_den;
961 osys->ch_sar = false;
964 if (osys->sar.num == osys->source.i_sar_num &&
965 osys->sar.den == osys->source.i_sar_den)
967 vout_SendEventSourceAspect(osys->vout, 0, 0);
971 unsigned dar_num, dar_den;
972 vlc_ureduce( &dar_num, &dar_den,
973 osys->sar.num * vd->source.i_visible_width,
974 osys->sar.den * vd->source.i_visible_height,
976 vout_SendEventSourceAspect(osys->vout, dar_num, dar_den);
978 /* If a crop ratio is requested, recompute the parameters */
979 if (osys->crop.num > 0 && osys->crop.den > 0)
980 osys->ch_crop = true;
984 video_format_t source = vd->source;
986 unsigned crop_num = osys->crop.num;
987 unsigned crop_den = osys->crop.den;
988 if (crop_num > 0 && crop_den > 0) {
989 video_format_t fmt = osys->source;
990 fmt.i_sar_num = source.i_sar_num;
991 fmt.i_sar_den = source.i_sar_den;
992 VoutDisplayCropRatio(&osys->crop.left, &osys->crop.top,
993 &osys->crop.right, &osys->crop.bottom,
994 &fmt, crop_num, crop_den);
996 const int right_max = osys->source.i_x_offset + osys->source.i_visible_width;
997 const int bottom_max = osys->source.i_y_offset + osys->source.i_visible_height;
998 #define __CLIP(v, a, b) __MAX(__MIN(v, b), a)
999 int left = __CLIP((int)osys->source.i_x_offset + osys->crop.left,
1001 int top = __CLIP((int)osys->source.i_y_offset + osys->crop.top,
1004 if (osys->crop.right <= 0)
1005 right = (int)(osys->source.i_x_offset + osys->source.i_visible_width) + osys->crop.right;
1007 right = (int)osys->source.i_x_offset + osys->crop.right;
1008 right = __CLIP(right, left + 1, right_max);
1009 if (osys->crop.bottom <= 0)
1010 bottom = (int)(osys->source.i_y_offset + osys->source.i_visible_height) + osys->crop.bottom;
1012 bottom = (int)osys->source.i_y_offset + osys->crop.bottom;
1013 bottom = __CLIP(bottom, top + 1, bottom_max);
1015 source.i_x_offset = left;
1016 source.i_y_offset = top;
1017 source.i_visible_width = right - left;
1018 source.i_visible_height = bottom - top;
1020 video_format_Print(VLC_OBJECT(vd), "SOURCE ", &osys->source);
1021 video_format_Print(VLC_OBJECT(vd), "CROPPED", &source);
1022 if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_SOURCE_CROP, &source)) {
1023 msg_Err(vd, "Failed to change source crop TODO implement crop at core");
1025 source = vd->source;
1026 crop_num = osys->crop_saved.num;
1027 crop_den = osys->crop_saved.den;
1028 /* FIXME implement cropping in the core if not supported by the
1029 * vout module (easy)
1031 } else if (!osys->fit_window) {
1032 osys->fit_window = 1;
1034 vd->source = source;
1035 osys->crop.left = source.i_x_offset - osys->source.i_x_offset;
1036 osys->crop.top = source.i_y_offset - osys->source.i_y_offset;
1037 /* FIXME for right/bottom we should keep the 'type' border vs window */
1038 osys->crop.right = (source.i_x_offset + source.i_visible_width) -
1039 (osys->source.i_x_offset + osys->source.i_visible_width);
1040 osys->crop.bottom = (source.i_y_offset + source.i_visible_height) -
1041 (osys->source.i_y_offset + osys->source.i_visible_height);
1042 osys->crop.num = crop_num;
1043 osys->crop.den = crop_den;
1044 osys->ch_crop = false;
1046 vout_SendEventSourceCrop(osys->vout,
1047 osys->crop.num, osys->crop.den,
1048 osys->crop.left, osys->crop.top,
1049 -osys->crop.right, -osys->crop.bottom);
1053 if (reset_pictures) {
1054 if (vout_display_Control(vd, VOUT_DISPLAY_RESET_PICTURES)) {
1055 /* FIXME what to do here ? */
1056 msg_Err(vd, "Failed to reset pictures (probably fatal)");
1058 reset_render = true;
1062 VoutDisplayResetRender(vd);
1065 bool vout_AreDisplayPicturesInvalid(vout_display_t *vd)
1067 vout_display_owner_sys_t *osys = vd->owner.sys;
1069 vlc_mutex_lock(&osys->lock);
1070 const bool reset_pictures = osys->reset_pictures;
1071 vlc_mutex_unlock(&osys->lock);
1073 return reset_pictures;
1076 bool vout_IsDisplayFiltered(vout_display_t *vd)
1078 vout_display_owner_sys_t *osys = vd->owner.sys;
1080 return osys->filters != NULL;
1083 picture_t *vout_FilterDisplay(vout_display_t *vd, picture_t *picture)
1085 vout_display_owner_sys_t *osys = vd->owner.sys;
1087 assert(osys->filters);
1088 if (filter_chain_GetLength(osys->filters) <= 0) {
1089 picture_Release(picture);
1092 return filter_chain_VideoFilter(osys->filters, picture);
1095 void vout_UpdateDisplaySourceProperties(vout_display_t *vd, const video_format_t *source)
1097 vout_display_owner_sys_t *osys = vd->owner.sys;
1099 if (source->i_sar_num * osys->source.i_sar_den !=
1100 source->i_sar_den * osys->source.i_sar_num) {
1102 osys->source.i_sar_num = source->i_sar_num;
1103 osys->source.i_sar_den = source->i_sar_den;
1104 vlc_ureduce(&osys->source.i_sar_num, &osys->source.i_sar_den,
1105 osys->source.i_sar_num, osys->source.i_sar_den, 0);
1107 /* FIXME it will override any AR that the user would have forced */
1108 osys->ch_sar = true;
1109 osys->sar.num = osys->source.i_sar_num;
1110 osys->sar.den = osys->source.i_sar_den;
1112 if (source->i_x_offset != osys->source.i_x_offset ||
1113 source->i_y_offset != osys->source.i_y_offset ||
1114 source->i_visible_width != osys->source.i_visible_width ||
1115 source->i_visible_height != osys->source.i_visible_height) {
1117 video_format_CopyCrop(&osys->source, source);
1119 /* Force the vout to reapply the current user crop settings over the new decoder
1121 osys->ch_crop = true;
1125 void vout_SetDisplayFullscreen(vout_display_t *vd, bool is_fullscreen)
1127 vout_display_owner_sys_t *osys = vd->owner.sys;
1129 vlc_mutex_lock(&osys->lock);
1130 if (!osys->is_fullscreen != !is_fullscreen) {
1131 osys->ch_fullscreen = true;
1132 osys->is_fullscreen = is_fullscreen;
1134 vlc_mutex_unlock(&osys->lock);
1137 void vout_SetDisplayFilled(vout_display_t *vd, bool is_filled)
1139 vout_display_owner_sys_t *osys = vd->owner.sys;
1141 if (!osys->is_display_filled != !is_filled) {
1142 osys->ch_display_filled = true;
1143 osys->is_display_filled = is_filled;
1147 void vout_SetDisplayZoom(vout_display_t *vd, int num, int den)
1149 vout_display_owner_sys_t *osys = vd->owner.sys;
1151 if (osys->is_display_filled ||
1152 osys->zoom.num != num || osys->zoom.den != den) {
1153 osys->ch_zoom = true;
1154 osys->zoom.num = num;
1155 osys->zoom.den = den;
1159 void vout_SetWindowState(vout_display_t *vd, unsigned state)
1161 vout_display_owner_sys_t *osys = vd->owner.sys;
1163 vlc_mutex_lock(&osys->lock);
1164 if (osys->wm_state != state) {
1165 osys->ch_wm_state = true;
1166 osys->wm_state = state;
1168 vlc_mutex_unlock(&osys->lock);
1171 void vout_SetDisplayAspect(vout_display_t *vd, unsigned dar_num, unsigned dar_den)
1173 vout_display_owner_sys_t *osys = vd->owner.sys;
1175 unsigned sar_num, sar_den;
1176 if (dar_num > 0 && dar_den > 0) {
1177 sar_num = dar_num * osys->source.i_visible_height;
1178 sar_den = dar_den * osys->source.i_visible_width;
1179 vlc_ureduce(&sar_num, &sar_den, sar_num, sar_den, 0);
1185 if (osys->sar.num != sar_num || osys->sar.den != sar_den) {
1186 osys->ch_sar = true;
1187 osys->sar.num = sar_num;
1188 osys->sar.den = sar_den;
1191 void vout_SetDisplayCrop(vout_display_t *vd,
1192 unsigned crop_num, unsigned crop_den,
1193 unsigned left, unsigned top, int right, int bottom)
1195 vout_display_owner_sys_t *osys = vd->owner.sys;
1197 if (osys->crop.left != (int)left || osys->crop.top != (int)top ||
1198 osys->crop.right != right || osys->crop.bottom != bottom ||
1199 (crop_num > 0 && crop_den > 0 &&
1200 (crop_num != osys->crop.num || crop_den != osys->crop.den))) {
1202 osys->crop.left = left;
1203 osys->crop.top = top;
1204 osys->crop.right = right;
1205 osys->crop.bottom = bottom;
1206 osys->crop.num = crop_num;
1207 osys->crop.den = crop_den;
1209 osys->ch_crop = true;
1213 struct vlc_gl_t *vout_GetDisplayOpengl(vout_display_t *vd)
1215 struct vlc_gl_t *gl;
1216 if (vout_display_Control(vd, VOUT_DISPLAY_GET_OPENGL, &gl))
1221 static vout_display_t *DisplayNew(vout_thread_t *vout,
1222 const video_format_t *source_org,
1223 const vout_display_state_t *state,
1225 bool is_wrapper, vout_display_t *wrapper,
1226 mtime_t double_click_timeout,
1227 mtime_t hide_timeout,
1228 const vout_display_owner_t *owner_ptr)
1231 vout_display_owner_sys_t *osys = calloc(1, sizeof(*osys));
1232 vout_display_cfg_t *cfg = &osys->cfg;
1235 osys->wm_state_initial = -1;
1236 osys->sar_initial.num = state->sar.num;
1237 osys->sar_initial.den = state->sar.den;
1238 vout_display_GetDefaultDisplaySize(&cfg->display.width, &cfg->display.height,
1242 osys->is_wrapper = is_wrapper;
1243 osys->wrapper = wrapper;
1245 vlc_mutex_init(&osys->lock);
1247 vlc_mouse_Init(&osys->mouse.state);
1248 osys->mouse.last_moved = mdate();
1249 osys->mouse.double_click_timeout = double_click_timeout;
1250 osys->mouse.hide_timeout = hide_timeout;
1251 osys->is_fullscreen = cfg->is_fullscreen;
1252 osys->display_width = cfg->display.width;
1253 osys->display_height = cfg->display.height;
1254 osys->is_display_filled = cfg->is_display_filled;
1255 osys->width_saved = cfg->display.width;
1256 osys->height_saved = cfg->display.height;
1257 if (osys->is_fullscreen) {
1258 vout_display_cfg_t cfg_windowed = *cfg;
1259 cfg_windowed.is_fullscreen = false;
1260 cfg_windowed.display.width = 0;
1261 cfg_windowed.display.height = 0;
1262 vout_display_GetDefaultDisplaySize(&osys->width_saved,
1263 &osys->height_saved,
1264 source_org, &cfg_windowed);
1266 osys->zoom.num = cfg->zoom.num;
1267 osys->zoom.den = cfg->zoom.den;
1268 osys->wm_state = state->wm_state;
1269 osys->fit_window = 0;
1270 osys->event.fifo = NULL;
1272 osys->source = *source_org;
1273 osys->crop.left = 0;
1275 osys->crop.right = 0;
1276 osys->crop.bottom = 0;
1277 osys->crop_saved.num = 0;
1278 osys->crop_saved.den = 0;
1282 osys->sar.num = osys->sar_initial.num ? osys->sar_initial.num : source_org->i_sar_num;
1283 osys->sar.den = osys->sar_initial.den ? osys->sar_initial.den : source_org->i_sar_den;
1284 #ifdef ALLOW_DUMMY_VOUT
1285 vlc_mouse_Init(&osys->vout_mouse);
1288 vout_display_owner_t owner;
1292 owner.event = VoutDisplayEvent;
1293 owner.window_new = VoutDisplayNewWindow;
1294 owner.window_del = VoutDisplayDelWindow;
1299 video_format_t source = *source_org;
1301 source.i_x_offset = 0;
1302 source.i_y_offset = 0;
1303 source.i_visible_width = source.i_width;
1304 source.i_visible_height = source.i_height;
1306 vout_display_t *p_display = vout_display_New(VLC_OBJECT(vout),
1307 module, !is_wrapper,
1308 &source, cfg, &owner);
1314 VoutDisplayCreateRender(p_display);
1316 /* Setup delayed request */
1317 if (osys->sar.num != source.i_sar_num ||
1318 osys->sar.den != source.i_sar_den)
1319 osys->ch_sar = true;
1320 if (osys->wm_state != osys->wm_state_initial)
1321 osys->ch_wm_state = true;
1322 if (source.i_x_offset != source_org->i_x_offset ||
1323 source.i_y_offset != source_org->i_y_offset ||
1324 source.i_visible_width != source_org->i_visible_width ||
1325 source.i_visible_height != source_org->i_visible_height)
1326 osys->ch_crop = true;
1331 void vout_DeleteDisplay(vout_display_t *vd, vout_display_state_t *state)
1333 vout_display_owner_sys_t *osys = vd->owner.sys;
1336 if (!osys->is_wrapper )
1337 state->cfg = osys->cfg;
1338 state->wm_state = osys->wm_state;
1339 state->sar.num = osys->sar_initial.num;
1340 state->sar.den = osys->sar_initial.den;
1343 VoutDisplayDestroyRender(vd);
1344 if (osys->is_wrapper)
1346 vout_display_Delete(vd);
1347 if (osys->event.fifo) {
1348 vlc_cancel(osys->event.thread);
1349 vlc_join(osys->event.thread, NULL);
1350 block_FifoRelease(osys->event.fifo);
1352 vlc_mutex_destroy(&osys->lock);
1356 /*****************************************************************************
1358 *****************************************************************************/
1359 vout_display_t *vout_NewDisplay(vout_thread_t *vout,
1360 const video_format_t *source,
1361 const vout_display_state_t *state,
1363 mtime_t double_click_timeout,
1364 mtime_t hide_timeout)
1366 return DisplayNew(vout, source, state, module, false, NULL,
1367 double_click_timeout, hide_timeout, NULL);
1370 /*****************************************************************************
1372 *****************************************************************************/
1373 struct vout_display_sys_t {
1374 picture_pool_t *pool;
1375 video_splitter_t *splitter;
1379 picture_t **picture;
1380 vout_display_t **display;
1382 struct video_splitter_owner_t {
1383 vout_display_t *wrapper;
1386 static vout_window_t *SplitterNewWindow(vout_display_t *vd, const vout_window_cfg_t *cfg_ptr)
1388 vout_display_owner_sys_t *osys = vd->owner.sys;
1390 vout_window_cfg_t cfg = *cfg_ptr;
1391 cfg.is_standalone = true;
1392 cfg.x += 0;//output->window.i_x; FIXME
1393 cfg.y += 0;//output->window.i_y;
1395 return vout_NewDisplayWindow(osys->vout, vd, &cfg);
1397 static void SplitterDelWindow(vout_display_t *vd, vout_window_t *window)
1399 vout_display_owner_sys_t *osys = vd->owner.sys;
1401 vout_DeleteDisplayWindow(osys->vout, vd, window);
1403 static void SplitterEvent(vout_display_t *vd, int event, va_list args)
1405 //vout_display_owner_sys_t *osys = vd->owner.sys;
1409 case VOUT_DISPLAY_EVENT_MOUSE_STATE:
1410 case VOUT_DISPLAY_EVENT_MOUSE_MOVED:
1411 case VOUT_DISPLAY_EVENT_MOUSE_PRESSED:
1412 case VOUT_DISPLAY_EVENT_MOUSE_RELEASED:
1416 case VOUT_DISPLAY_EVENT_MOUSE_DOUBLE_CLICK:
1417 case VOUT_DISPLAY_EVENT_KEY:
1418 case VOUT_DISPLAY_EVENT_CLOSE:
1419 case VOUT_DISPLAY_EVENT_FULLSCREEN:
1420 case VOUT_DISPLAY_EVENT_DISPLAY_SIZE:
1421 case VOUT_DISPLAY_EVENT_PICTURES_INVALID:
1422 VoutDisplayEvent(vd, event, args);
1426 msg_Err(vd, "SplitterEvent TODO");
1431 static picture_pool_t *SplitterPool(vout_display_t *vd, unsigned count)
1433 vout_display_sys_t *sys = vd->sys;
1435 sys->pool = picture_pool_NewFromFormat(&vd->fmt, count);
1438 static void SplitterPrepare(vout_display_t *vd,
1440 subpicture_t *subpicture)
1442 vout_display_sys_t *sys = vd->sys;
1444 picture_Hold(picture);
1445 assert(!subpicture);
1447 if (video_splitter_Filter(sys->splitter, sys->picture, picture)) {
1448 for (int i = 0; i < sys->count; i++)
1449 sys->picture[i] = NULL;
1450 picture_Release(picture);
1454 for (int i = 0; i < sys->count; i++) {
1455 if (vout_IsDisplayFiltered(sys->display[i]))
1456 sys->picture[i] = vout_FilterDisplay(sys->display[i], sys->picture[i]);
1457 if (sys->picture[i])
1458 vout_display_Prepare(sys->display[i], sys->picture[i], NULL);
1461 static void SplitterDisplay(vout_display_t *vd,
1463 subpicture_t *subpicture)
1465 vout_display_sys_t *sys = vd->sys;
1467 assert(!subpicture);
1468 for (int i = 0; i < sys->count; i++) {
1469 if (sys->picture[i])
1470 vout_display_Display(sys->display[i], sys->picture[i], NULL);
1472 picture_Release(picture);
1474 static int SplitterControl(vout_display_t *vd, int query, va_list args)
1476 (void)vd; (void)query; (void)args;
1477 return VLC_EGENERIC;
1479 static void SplitterManage(vout_display_t *vd)
1481 vout_display_sys_t *sys = vd->sys;
1483 for (int i = 0; i < sys->count; i++)
1484 vout_ManageDisplay(sys->display[i], true);
1487 static int SplitterPictureNew(video_splitter_t *splitter, picture_t *picture[])
1489 vout_display_sys_t *wsys = splitter->p_owner->wrapper->sys;
1491 for (int i = 0; i < wsys->count; i++) {
1492 if (vout_IsDisplayFiltered(wsys->display[i])) {
1493 /* TODO use a pool ? */
1494 picture[i] = picture_NewFromFormat(&wsys->display[i]->source);
1496 picture_pool_t *pool = vout_display_Pool(wsys->display[i], 1);
1497 picture[i] = pool ? picture_pool_Get(pool) : NULL;
1500 for (int j = 0; j < i; j++)
1501 picture_Release(picture[j]);
1502 return VLC_EGENERIC;
1507 static void SplitterPictureDel(video_splitter_t *splitter, picture_t *picture[])
1509 vout_display_sys_t *wsys = splitter->p_owner->wrapper->sys;
1511 for (int i = 0; i < wsys->count; i++)
1512 picture_Release(picture[i]);
1514 static void SplitterClose(vout_display_t *vd)
1516 vout_display_sys_t *sys = vd->sys;
1519 video_splitter_t *splitter = sys->splitter;
1520 free(splitter->p_owner);
1521 video_splitter_Delete(splitter);
1524 picture_pool_Delete(sys->pool);
1527 for (int i = 0; i < sys->count; i++)
1528 vout_DeleteDisplay(sys->display[i], NULL);
1529 TAB_CLEAN(sys->count, sys->display);
1535 vout_display_t *vout_NewSplitter(vout_thread_t *vout,
1536 const video_format_t *source,
1537 const vout_display_state_t *state,
1539 const char *splitter_module,
1540 mtime_t double_click_timeout,
1541 mtime_t hide_timeout)
1543 video_splitter_t *splitter =
1544 video_splitter_New(VLC_OBJECT(vout), splitter_module, source);
1549 vout_display_t *wrapper =
1550 DisplayNew(vout, source, state, module, true, NULL,
1551 double_click_timeout, hide_timeout, NULL);
1553 video_splitter_Delete(splitter);
1556 vout_display_sys_t *sys = malloc(sizeof(*sys));
1559 sys->picture = calloc(splitter->i_output, sizeof(*sys->picture));
1562 sys->splitter = splitter;
1565 wrapper->pool = SplitterPool;
1566 wrapper->prepare = SplitterPrepare;
1567 wrapper->display = SplitterDisplay;
1568 wrapper->control = SplitterControl;
1569 wrapper->manage = SplitterManage;
1573 video_splitter_owner_t *owner = malloc(sizeof(*owner));
1576 owner->wrapper = wrapper;
1577 splitter->p_owner = owner;
1578 splitter->pf_picture_new = SplitterPictureNew;
1579 splitter->pf_picture_del = SplitterPictureDel;
1582 TAB_INIT(sys->count, sys->display);
1583 for (int i = 0; i < splitter->i_output; i++) {
1584 vout_display_owner_t owner;
1586 owner.event = SplitterEvent;
1587 owner.window_new = SplitterNewWindow;
1588 owner.window_del = SplitterDelWindow;
1590 const video_splitter_output_t *output = &splitter->p_output[i];
1591 vout_display_state_t ostate;
1593 memset(&ostate, 0, sizeof(ostate));
1594 ostate.cfg.is_fullscreen = false;
1595 ostate.cfg.display = state->cfg.display;
1596 ostate.cfg.align.horizontal = 0; /* TODO */
1597 ostate.cfg.align.vertical = 0; /* TODO */
1598 ostate.cfg.is_display_filled = true;
1599 ostate.cfg.zoom.num = 1;
1600 ostate.cfg.zoom.den = 1;
1602 vout_display_t *vd = DisplayNew(vout, &output->fmt, &ostate,
1603 output->psz_module ? output->psz_module : module,
1605 double_click_timeout, hide_timeout, &owner);
1607 vout_DeleteDisplay(wrapper, NULL);
1610 TAB_APPEND(sys->count, sys->display, vd);
1616 /*****************************************************************************
1618 *****************************************************************************/
1619 #include "vout_internal.h"
1620 void vout_SendDisplayEventMouse(vout_thread_t *vout, const vlc_mouse_t *m)
1622 vlc_mouse_t tmp1, tmp2;
1624 /* The check on spu is needed as long as ALLOW_DUMMY_VOUT is defined */
1625 if (vout->p->spu && spu_ProcessMouse( vout->p->spu, m, &vout->p->display.vd->source))
1628 vlc_mutex_lock( &vout->p->filter.lock );
1629 if (vout->p->filter.chain_static && vout->p->filter.chain_interactive) {
1630 if (!filter_chain_MouseFilter(vout->p->filter.chain_interactive, &tmp1, m))
1632 if (!filter_chain_MouseFilter(vout->p->filter.chain_static, &tmp2, m))
1635 vlc_mutex_unlock( &vout->p->filter.lock );
1637 if (vlc_mouse_HasMoved(&vout->p->mouse, m)) {
1638 vout_SendEventMouseMoved(vout, m->i_x, m->i_y);
1640 if (vlc_mouse_HasButton(&vout->p->mouse, m)) {
1641 for (unsigned button = 0; button < MOUSE_BUTTON_MAX; button++) {
1642 if (vlc_mouse_HasPressed(&vout->p->mouse, m, button))
1643 vout_SendEventMousePressed(vout, button);
1644 else if (vlc_mouse_HasReleased(&vout->p->mouse, m, button))
1645 vout_SendEventMouseReleased(vout, button);
1648 if (m->b_double_click)
1649 vout_SendEventMouseDoubleClick(vout);
1650 vout->p->mouse = *m;
1652 #ifdef ALLOW_DUMMY_VOUT
1653 static void DummyVoutSendDisplayEventMouse(vout_thread_t *vout, vlc_mouse_t *fallback, const vlc_mouse_t *m)
1655 vout_thread_sys_t p;
1658 p.mouse = *fallback;
1659 vlc_mutex_init(&p.filter.lock);
1660 p.filter.chain_static = NULL;
1661 p.filter.chain_interactive = NULL;
1665 vout_SendDisplayEventMouse(vout, m);
1666 if (vout->p == &p) {
1667 vlc_mutex_destroy(&p.filter.lock);
1668 *fallback = p.mouse;