~ubuntu-branches/ubuntu/natty/vlc/natty

« back to all changes in this revision

Viewing changes to src/video_output/display.c

  • Committer: Bazaar Package Importer
  • Author(s): Benjamin Drung
  • Date: 2010-06-25 01:09:16 UTC
  • mfrom: (1.1.30 upstream)
  • Revision ID: james.westby@ubuntu.com-20100625010916-asxhep2mutg6g6pd
Tags: 1.1.0-1ubuntu1
* Merge from Debian unstable, remaining changes:
  - build and install the libx264 plugin
  - add Xb-Npp header to vlc package
  - Add apport hook to include more vlc dependencies in bug reports
* Drop xulrunner patches.
* Drop 502_xulrunner_191.diff.

Show diffs side-by-side

added added

removed removed

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