common.c 25.2 KB
Newer Older
1
/*****************************************************************************
2
 * common.c: Windows video output common code
3
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2001-2009 VLC authors and VideoLAN
5 6 7
 * $Id$
 *
 * Authors: Gildas Bazin <gbazin@videolan.org>
Martell Malone's avatar
Martell Malone committed
8
 *          Martell Malone <martellmalone@gmail.com>
9
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
10 11 12
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
13 14 15 16
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
17 18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
19
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
20 21 22
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 24 25
 *****************************************************************************/

/*****************************************************************************
26 27 28
 * Preamble: This file contains the functions related to the init of the vout
 *           structure, the common display code, the screensaver, but not the
 *           events and the Window Creation (events.c)
29
 *****************************************************************************/
30

31 32 33 34 35
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <vlc_common.h>
36
#include <vlc_vout_display.h>
37 38

#include <windows.h>
39
#include <assert.h>
40

41
#define COBJMACROS
42
#include <shobjidl.h>
43

44
#include "common.h"
45

46
static void CommonChangeThumbnailClip(vout_display_t *, bool show);
47
#if !VLC_WINSTORE_APP
48
static int  CommonControlSetFullscreen(vout_display_t *, bool is_fullscreen);
49 50 51

static void DisableScreensaver(vout_display_t *);
static void RestoreScreensaver(vout_display_t *);
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
52
#endif
53

54
/* */
55
int CommonInit(vout_display_t *vd)
56
{
57 58 59 60 61 62 63
    vout_display_sys_t *sys = vd->sys;

    sys->hwnd      = NULL;
    sys->hvideownd = NULL;
    sys->hparent   = NULL;
    sys->hfswnd    = NULL;
    sys->changes   = 0;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
64 65 66 67
    sys->is_first_display = true;
    sys->is_on_top        = false;

#if !VLC_WINSTORE_APP
68 69 70 71
    SetRectEmpty(&sys->rect_display);
    SetRectEmpty(&sys->rect_parent);

    var_Create(vd, "video-deco", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
72
    var_Create(vd, "disable-screensaver", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
73 74 75 76

    /* */
    sys->event = EventThreadCreate(vd);
    if (!sys->event)
77
        return VLC_EGENERIC;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
78
#endif
79 80 81

    event_cfg_t cfg;
    memset(&cfg, 0, sizeof(cfg));
82
#ifdef MODULE_NAME_IS_direct3d9
83
    cfg.use_desktop = sys->use_desktop;
84
#endif
85
#ifdef MODULE_NAME_IS_directdraw
86
    cfg.use_overlay = sys->use_overlay;
87
#endif
88 89 90 91
    cfg.x      = var_InheritInteger(vd, "video-x");
    cfg.y      = var_InheritInteger(vd, "video-y");
    cfg.width  = vd->cfg->display.width;
    cfg.height = vd->cfg->display.height;
92

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
93
#if !VLC_WINSTORE_APP
94
    event_hwnd_t hwnd;
95
    if (EventThreadStart(sys->event, &hwnd, &cfg))
96 97
        return VLC_EGENERIC;

98 99 100 101 102
    sys->parent_window = hwnd.parent_window;
    sys->hparent       = hwnd.hparent;
    sys->hwnd          = hwnd.hwnd;
    sys->hvideownd     = hwnd.hvideownd;
    sys->hfswnd        = hwnd.hfswnd;
103

104 105 106 107
    if (vd->cfg->is_fullscreen) {
        if (CommonControlSetFullscreen(vd, true))
            vout_display_SendEventFullscreen(vd, false);
    }
108

109
    DisableScreensaver (vd);
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
110
#endif
111 112 113 114

    return VLC_SUCCESS;
}

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
115 116 117 118 119 120 121
/* */
picture_pool_t *CommonPool(vout_display_t *vd, unsigned count)
{
    VLC_UNUSED(count);
    return vd->sys->pool;
}

122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
/*****************************************************************************
* UpdateRects: update clipping rectangles
*****************************************************************************
* This function is called when the window position or size are changed, and
* its job is to update the source and destination RECTs used to display the
* picture.
*****************************************************************************/
void UpdateRects(vout_display_t *vd,
    const vout_display_cfg_t *cfg,
    const video_format_t *source,
    bool is_forced)
{
    vout_display_sys_t *sys = vd->sys;
#define rect_src sys->rect_src
#define rect_src_clipped sys->rect_src_clipped
#define rect_dest sys->rect_dest
#define rect_dest_clipped sys->rect_dest_clipped

    RECT  rect;
    POINT point;

    /* */
    if (!cfg)
        cfg = vd->cfg;
    if (!source)
        source = &vd->source;

    /* Retrieve the window size */
150
#if VLC_WINSTORE_APP && MODULE_NAME_IS_direct3d11
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
    rect.left   = 0;
    rect.top    = 0;
    uint32_t i_width;
    uint32_t i_height;
    UINT dataSize = sizeof(i_width);
    HRESULT hr = IDXGISwapChain_GetPrivateData(sys->dxgiswapChain, &GUID_SWAPCHAIN_WIDTH, &dataSize, &i_width);
    if (FAILED(hr)) {
        msg_Err(vd, "Can't get swapchain width, size %d. (hr=0x%lX)", hr, dataSize);
        return;
    }
    dataSize = sizeof(i_height);
    hr = IDXGISwapChain_GetPrivateData(sys->dxgiswapChain, &GUID_SWAPCHAIN_HEIGHT, &dataSize, &i_height);
    if (FAILED(hr)) {
        msg_Err(vd, "Can't get swapchain height, size %d. (hr=0x%lX)", hr, dataSize);
        return;
    }
    rect.right  = i_width;
    rect.bottom = i_height;
#else
    GetClientRect(sys->hwnd, &rect);
#endif

    /* Retrieve the window position */
    point.x = point.y = 0;
#if !VLC_WINSTORE_APP
    ClientToScreen(sys->hwnd, &point);
#endif

    /* If nothing changed, we can return */
    bool has_moved;
    bool is_resized;
#if VLC_WINSTORE_APP
    has_moved = false;
    is_resized = rect.right != (sys->rect_display.right - sys->rect_display.left) ||
        rect.bottom != (sys->rect_display.bottom - sys->rect_display.top);
    sys->rect_display = rect;
#else
    EventThreadUpdateWindowPosition(sys->event, &has_moved, &is_resized,
        point.x, point.y,
        rect.right, rect.bottom);
#endif
    if (is_resized)
        vout_display_SendEventDisplaySize(vd, rect.right, rect.bottom);
    if (!is_forced && !has_moved && !is_resized)
        return;

    /* Update the window position and size */
    vout_display_cfg_t place_cfg = *cfg;
    place_cfg.display.width = rect.right;
    place_cfg.display.height = rect.bottom;

    vout_display_place_t place;
    vout_display_PlacePicture(&place, source, &place_cfg, false);

#if !VLC_WINSTORE_APP
    EventThreadUpdateSourceAndPlace(sys->event, source, &place);

    if (sys->hvideownd)
        SetWindowPos(sys->hvideownd, 0,
            place.x, place.y, place.width, place.height,
            SWP_NOCOPYBITS | SWP_NOZORDER | SWP_ASYNCWINDOWPOS);
#endif

    /* Destination image position and dimensions */
#if (defined(MODULE_NAME_IS_direct3d9) || defined(MODULE_NAME_IS_direct3d11) || defined(MODULE_NAME_IS_direct2d)) && !VLC_WINSTORE_APP
    rect_dest.left = 0;
    rect_dest.right = place.width;
    rect_dest.top = 0;
    rect_dest.bottom = place.height;
#else
    rect_dest.left = point.x + place.x;
    rect_dest.right = rect_dest.left + place.width;
    rect_dest.top = point.y + place.y;
    rect_dest.bottom = rect_dest.top + place.height;

#ifdef MODULE_NAME_IS_directdraw
    /* Apply overlay hardware constraints */
    if (sys->use_overlay)
        AlignRect(&rect_dest, sys->i_align_dest_boundary, sys->i_align_dest_size);
#endif

#endif

#if defined(MODULE_NAME_IS_directdraw)
    /* UpdateOverlay directdraw function doesn't automatically clip to the
    * display size so we need to do it otherwise it will fail */

    /* Clip the destination window */
    if (!IntersectRect(&rect_dest_clipped, &rect_dest,
        &sys->rect_display)) {
        SetRectEmpty(&rect_src_clipped);
        goto exit;
    }

#ifndef NDEBUG
    msg_Dbg(vd, "DirectXUpdateRects image_dst_clipped coords:"
        " %li,%li,%li,%li",
        rect_dest_clipped.left, rect_dest_clipped.top,
        rect_dest_clipped.right, rect_dest_clipped.bottom);
#endif

#else

    /* AFAIK, there are no clipping constraints in Direct3D, OpenGL and GDI */
    rect_dest_clipped = rect_dest;

#endif

    /* the 2 following lines are to fix a bug when clicking on the desktop */
    if ((rect_dest_clipped.right - rect_dest_clipped.left) == 0 ||
        (rect_dest_clipped.bottom - rect_dest_clipped.top) == 0) {
#if !VLC_WINSTORE_APP
        SetRectEmpty(&rect_src_clipped);
#endif
        goto exit;
    }

    /* src image dimensions */
    rect_src.left = 0;
    rect_src.top = 0;
271 272
    rect_src.right = vd->fmt.i_width;
    rect_src.bottom = vd->fmt.i_height;
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344

    /* Clip the source image */
    rect_src_clipped.left = source->i_x_offset +
        (rect_dest_clipped.left - rect_dest.left) *
        source->i_visible_width / (rect_dest.right - rect_dest.left);
    rect_src_clipped.right = source->i_x_offset +
        source->i_visible_width -
        (rect_dest.right - rect_dest_clipped.right) *
        source->i_visible_width / (rect_dest.right - rect_dest.left);
    rect_src_clipped.top = source->i_y_offset +
        (rect_dest_clipped.top - rect_dest.top) *
        source->i_visible_height / (rect_dest.bottom - rect_dest.top);
    rect_src_clipped.bottom = source->i_y_offset +
        source->i_visible_height -
        (rect_dest.bottom - rect_dest_clipped.bottom) *
        source->i_visible_height / (rect_dest.bottom - rect_dest.top);

#ifdef MODULE_NAME_IS_directdraw
    /* Apply overlay hardware constraints */
    if (sys->use_overlay)
        AlignRect(&rect_src_clipped, sys->i_align_src_boundary, sys->i_align_src_size);
#elif defined(MODULE_NAME_IS_direct3d9) || defined(MODULE_NAME_IS_direct3d11) || defined(MODULE_NAME_IS_direct2d)
    /* Needed at least with YUV content */
    rect_src_clipped.left &= ~1;
    rect_src_clipped.right &= ~1;
    rect_src_clipped.top &= ~1;
    rect_src_clipped.bottom &= ~1;
#endif

#ifndef NDEBUG
    msg_Dbg(vd, "DirectXUpdateRects souce"
        " offset: %i,%i visible: %ix%i",
        source->i_x_offset, source->i_y_offset,
        source->i_visible_width, source->i_visible_height);
    msg_Dbg(vd, "DirectXUpdateRects image_src"
        " coords: %li,%li,%li,%li",
        rect_src.left, rect_src.top,
        rect_src.right, rect_src.bottom);
    msg_Dbg(vd, "DirectXUpdateRects image_src_clipped"
        " coords: %li,%li,%li,%li",
        rect_src_clipped.left, rect_src_clipped.top,
        rect_src_clipped.right, rect_src_clipped.bottom);
    msg_Dbg(vd, "DirectXUpdateRects image_dst"
        " coords: %li,%li,%li,%li",
        rect_dest.left, rect_dest.top,
        rect_dest.right, rect_dest.bottom);
    msg_Dbg(vd, "DirectXUpdateRects image_dst_clipped"
        " coords: %li,%li,%li,%li",
        rect_dest_clipped.left, rect_dest_clipped.top,
        rect_dest_clipped.right, rect_dest_clipped.bottom);
#endif

#ifdef MODULE_NAME_IS_directdraw
    /* The destination coordinates need to be relative to the current
    * directdraw primary surface (display) */
    rect_dest_clipped.left -= sys->rect_display.left;
    rect_dest_clipped.right -= sys->rect_display.left;
    rect_dest_clipped.top -= sys->rect_display.top;
    rect_dest_clipped.bottom -= sys->rect_display.top;
#endif

    CommonChangeThumbnailClip(vd, true);

exit:
    /* Signal the change in size/position */
    sys->changes |= DX_POSITION_CHANGE;

#undef rect_src
#undef rect_src_clipped
#undef rect_dest
#undef rect_dest_clipped
}
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
345 346

#if !VLC_WINSTORE_APP
347
/* */
348
void CommonClean(vout_display_t *vd)
349
{
350 351
    vout_display_sys_t *sys = vd->sys;
    if (sys->event) {
352
        CommonChangeThumbnailClip(vd, false);
353 354
        EventThreadStop(sys->event);
        EventThreadDestroy(sys->event);
355 356
    }

357
    RestoreScreensaver(vd);
358 359
}

360
void CommonManage(vout_display_t *vd)
361
{
362 363 364 365 366 367
    vout_display_sys_t *sys = vd->sys;

    /* We used to call the Win32 PeekMessage function here to read the window
     * messages. But since window can stay blocked into this function for a
     * long time (for example when you move your window on the screen), I
     * decided to isolate PeekMessage in another thread. */
368 369
    /* If we do not control our window, we check for geometry changes
     * ourselves because the parent might not send us its events. */
370
    if (sys->hparent) {
371 372 373
        RECT rect_parent;
        POINT point;

374
        /* Check if the parent window has resized or moved */
375
        GetClientRect(sys->hparent, &rect_parent);
376
        point.x = point.y = 0;
377 378
        ClientToScreen(sys->hparent, &point);
        OffsetRect(&rect_parent, point.x, point.y);
379

380 381
        if (!EqualRect(&rect_parent, &sys->rect_parent)) {
            sys->rect_parent = rect_parent;
382

383 384
            /* This code deals with both resize and move
             *
385
             * For most drivers(direct3d9, gdi, opengl), move is never
386 387 388
             * an issue. The surface automatically gets moved together
             * with the associated window (hvideownd)
             *
389
             * For directdraw, it is still important to call UpdateRects
Konstantin Pavlov's avatar
Konstantin Pavlov committed
390
             * on a move of the parent window, even if no resize occurred
391
             */
392 393 394 395
            SetWindowPos(sys->hwnd, 0, 0, 0,
                         rect_parent.right - rect_parent.left,
                         rect_parent.bottom - rect_parent.top,
                         SWP_NOZORDER);
396

397
            UpdateRects(vd, NULL, NULL, true);
398 399 400
        }
    }

401
    /* HasMoved means here resize or move */
402 403
    if (EventThreadGetAndResetHasMoved(sys->event))
        UpdateRects(vd, NULL, NULL, false);
404 405
}

Laurent Aimar's avatar
Laurent Aimar committed
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
/**
 * It ensures that the video window is shown after the first picture
 * is displayed.
 */
void CommonDisplay(vout_display_t *vd)
{
    vout_display_sys_t *sys = vd->sys;
    if (!sys->is_first_display)
        return;

    /* Video window is initially hidden, show it now since we got a
     * picture to show.
     */
    SetWindowPos(sys->hvideownd, 0, 0, 0, 0, 0,
                 SWP_ASYNCWINDOWPOS|
                 SWP_FRAMECHANGED|
                 SWP_SHOWWINDOW|
                 SWP_NOMOVE|
                 SWP_NOSIZE|
                 SWP_NOZORDER);
    sys->is_first_display = false;
}
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
428
#endif
Laurent Aimar's avatar
Laurent Aimar committed
429

430 431 432
/**
 * It updates a picture data/pitches.
 */
433 434
int CommonUpdatePicture(picture_t *picture, picture_t **fallback,
                        uint8_t *data, unsigned pitch)
435
{
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
    if (fallback) {
        if (*fallback == NULL) {
            *fallback = picture_NewFromFormat(&picture->format);
            if (*fallback == NULL)
                return VLC_EGENERIC;
        }
        for (int n = 0; n < picture->i_planes; n++) {
            const plane_t *src = &(*fallback)->p[n];
            plane_t       *dst = &picture->p[n];
            dst->p_pixels = src->p_pixels;
            dst->i_pitch  = src->i_pitch;
            dst->i_lines  = src->i_lines;
        }
        return VLC_SUCCESS;
    }
451 452 453
    /* fill in buffer info in first plane */
    picture->p->p_pixels = data;
    picture->p->i_pitch  = pitch;
454
    picture->p->i_lines  = picture->format.i_height;
455

Martell Malone's avatar
Martell Malone committed
456 457 458 459 460 461 462 463 464 465
    /*  Fill chroma planes for biplanar YUV */
    if (picture->format.i_chroma == VLC_CODEC_NV12 ||
        picture->format.i_chroma == VLC_CODEC_NV21) {

        for (int n = 1; n < picture->i_planes; n++) {
            const plane_t *o = &picture->p[n-1];
            plane_t *p = &picture->p[n];

            p->p_pixels = o->p_pixels + o->i_lines * o->i_pitch;
            p->i_pitch  = pitch;
466
            p->i_lines  = picture->format.i_height;
Martell Malone's avatar
Martell Malone committed
467 468 469 470 471 472 473 474
        }
        /* The dx/d3d buffer is always allocated as NV12 */
        if (vlc_fourcc_AreUVPlanesSwapped(picture->format.i_chroma, VLC_CODEC_NV12)) {
            /* TODO : Swap NV21 UV planes to match NV12 */
            return VLC_EGENERIC;
        }
    }

475 476 477 478 479 480 481 482 483 484 485
    /*  Fill chroma planes for planar YUV */
    if (picture->format.i_chroma == VLC_CODEC_I420 ||
        picture->format.i_chroma == VLC_CODEC_J420 ||
        picture->format.i_chroma == VLC_CODEC_YV12) {

        for (int n = 1; n < picture->i_planes; n++) {
            const plane_t *o = &picture->p[n-1];
            plane_t *p = &picture->p[n];

            p->p_pixels = o->p_pixels + o->i_lines * o->i_pitch;
            p->i_pitch  = pitch / 2;
486
            p->i_lines  = picture->format.i_height / 2;
487 488 489 490 491 492 493 494
        }
        /* The dx/d3d buffer is always allocated as YV12 */
        if (vlc_fourcc_AreUVPlanesSwapped(picture->format.i_chroma, VLC_CODEC_YV12)) {
            uint8_t *p_tmp = picture->p[1].p_pixels;
            picture->p[1].p_pixels = picture->p[2].p_pixels;
            picture->p[2].p_pixels = p_tmp;
        }
    }
495
    return VLC_SUCCESS;
496 497
}

Laurent Aimar's avatar
Laurent Aimar committed
498 499 500 501 502 503 504 505
void AlignRect(RECT *r, int align_boundary, int align_size)
{
    if (align_boundary)
        r->left = (r->left + align_boundary/2) & ~align_boundary;
    if (align_size)
        r->right = ((r->right - r->left + align_size/2) & ~align_size) + r->left;
}

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
506
#if !VLC_WINSTORE_APP
507 508 509 510 511 512 513 514 515 516 517
/* */
static void CommonChangeThumbnailClip(vout_display_t *vd, bool show)
{
    vout_display_sys_t *sys = vd->sys;

    /* Windows 7 taskbar thumbnail code */
    OSVERSIONINFO winVer;
    winVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    if (!GetVersionEx(&winVer) || winVer.dwMajorVersion <= 5)
        return;

518
    if( FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED)) )
519
        vlc_assert_unreachable();
520

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
521
    void *ptr;
522
    if (S_OK == CoCreateInstance(&CLSID_TaskbarList,
523 524
                                 NULL, CLSCTX_INPROC_SERVER,
                                 &IID_ITaskbarList3,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
525
                                 &ptr)) {
526 527
        ITaskbarList3 *taskbl = ptr;
        taskbl->lpVtbl->HrInit(taskbl);
528 529 530 531 532 533 534 535 536 537 538 539 540

        HWND hroot = GetAncestor(sys->hwnd,GA_ROOT);
        RECT relative;
        if (show) {
            RECT video, parent;
            GetWindowRect(sys->hvideownd, &video);
            GetWindowRect(hroot, &parent);
            relative.left   = video.left   - parent.left - 8;
            relative.top    = video.top    - parent.top - 10;

            relative.right  = video.right  - video.left + relative.left;
            relative.bottom = video.bottom - video.top  + relative.top - 25;
        }
541
        if (S_OK != taskbl->lpVtbl->SetThumbnailClip(taskbl, hroot,
542 543 544
                                                 show ? &relative : NULL))
            msg_Err(vd, "SetThumbNailClip failed");

545
        taskbl->lpVtbl->Release(taskbl);
546 547 548 549
    }
    CoUninitialize();
}

550
static int CommonControlSetFullscreen(vout_display_t *vd, bool is_fullscreen)
551
{
552
    vout_display_sys_t *sys = vd->sys;
553

554
#ifdef MODULE_NAME_IS_direct3d9
555
    if (sys->use_desktop && is_fullscreen)
556 557 558
        return VLC_EGENERIC;
#endif

559 560
    /* */
    if (sys->parent_window)
561
        return VLC_EGENERIC;
562

563 564
    /* */
    HWND hwnd = sys->hparent && sys->hfswnd ? sys->hfswnd : sys->hwnd;
565 566 567

    /* Save the current windows placement/placement to restore
       when fullscreen is over */
568 569 570
    WINDOWPLACEMENT window_placement;
    window_placement.length = sizeof(WINDOWPLACEMENT);
    GetWindowPlacement(hwnd, &window_placement);
571

572 573
    if (is_fullscreen) {
        msg_Dbg(vd, "entering fullscreen mode");
574 575

        /* Change window style, no borders and no title bar */
576
        SetWindowLong(hwnd, GWL_STYLE, WS_CLIPCHILDREN | WS_VISIBLE);
577

578
        if (sys->hparent) {
579 580
            /* Retrieve current window position so fullscreen will happen
            *on the right screen */
581 582
            HMONITOR hmon = MonitorFromWindow(sys->hparent,
                                              MONITOR_DEFAULTTONEAREST);
583
            MONITORINFO mi;
584
            mi.cbSize = sizeof(MONITORINFO);
585
            if (GetMonitorInfo(hmon, &mi))
586 587 588 589 590 591 592
                SetWindowPos(hwnd, 0,
                             mi.rcMonitor.left,
                             mi.rcMonitor.top,
                             mi.rcMonitor.right - mi.rcMonitor.left,
                             mi.rcMonitor.bottom - mi.rcMonitor.top,
                             SWP_NOZORDER|SWP_FRAMECHANGED);
        } else {
593
            /* Maximize non embedded window */
594
            ShowWindow(hwnd, SW_SHOWMAXIMIZED);
595 596
        }

597
        if (sys->hparent) {
598 599
            /* Hide the previous window */
            RECT rect;
600 601 602 603 604
            GetClientRect(hwnd, &rect);
            SetParent(sys->hwnd, hwnd);
            SetWindowPos(sys->hwnd, 0, 0, 0,
                         rect.right, rect.bottom,
                         SWP_NOZORDER|SWP_FRAMECHANGED);
605

606 607
            HWND topLevelParent = GetAncestor(sys->hparent, GA_ROOT);
            ShowWindow(topLevelParent, SW_HIDE);
608
        }
609 610 611
        SetForegroundWindow(hwnd);
    } else {
        msg_Dbg(vd, "leaving fullscreen mode");
612 613

        /* Change window style, no borders and no title bar */
614
        SetWindowLong(hwnd, GWL_STYLE, EventThreadGetWindowStyle(sys->event));
615

616
        if (sys->hparent) {
617
            RECT rect;
618 619 620 621 622
            GetClientRect(sys->hparent, &rect);
            SetParent(sys->hwnd, sys->hparent);
            SetWindowPos(sys->hwnd, 0, 0, 0,
                         rect.right, rect.bottom,
                         SWP_NOZORDER|SWP_FRAMECHANGED);
623

624 625 626 627 628
            HWND topLevelParent = GetAncestor(sys->hparent, GA_ROOT);
            ShowWindow(topLevelParent, SW_SHOW);
            SetForegroundWindow(sys->hparent);
            ShowWindow(hwnd, SW_HIDE);
        } else {
629
            /* return to normal window for non embedded vout */
630 631
            SetWindowPlacement(hwnd, &window_placement);
            ShowWindow(hwnd, SW_SHOWNORMAL);
632 633
        }
    }
634 635
    return VLC_SUCCESS;
}
636

637 638 639 640 641
int CommonControl(vout_display_t *vd, int query, va_list args)
{
    vout_display_sys_t *sys = vd->sys;

    switch (query) {
642 643 644 645 646 647 648 649 650 651 652 653 654 655
    case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:   /* const vout_display_cfg_t *p_cfg */
    {   /* Update dimensions */
        const vout_display_cfg_t *cfg = va_arg(args, const vout_display_cfg_t *);
        RECT rect_window = {
            .top    = 0,
            .left   = 0,
            .right  = cfg->display.width,
            .bottom = cfg->display.height,
        };

        AdjustWindowRect(&rect_window, EventThreadGetWindowStyle(sys->event), 0);
        SetWindowPos(sys->hwnd, 0, 0, 0,
                     rect_window.right - rect_window.left,
                     rect_window.bottom - rect_window.top, SWP_NOMOVE);
656
        UpdateRects(vd, cfg, NULL, false);
657 658
        return VLC_SUCCESS;
    }
659 660 661 662 663
    case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED: /* const vout_display_cfg_t *p_cfg */
    case VOUT_DISPLAY_CHANGE_ZOOM:           /* const vout_display_cfg_t *p_cfg */
    case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:  /* const video_format_t *p_source */
    case VOUT_DISPLAY_CHANGE_SOURCE_CROP: {  /* const video_format_t *p_source */
        const vout_display_cfg_t *cfg;
664

665 666
        if (query == VOUT_DISPLAY_CHANGE_SOURCE_CROP ||
            query == VOUT_DISPLAY_CHANGE_SOURCE_ASPECT) {
667
            const video_format_t *source = va_arg(args, const video_format_t *);
668
            cfg    = vd->cfg;
669
            UpdateRects(vd, cfg, source, true);
670 671
        } else {
            cfg    = va_arg(args, const vout_display_cfg_t *);
672
            UpdateRects(vd, cfg, NULL, false);
673 674 675
        }
        return VLC_SUCCESS;
    }
676 677 678
    case VOUT_DISPLAY_CHANGE_WINDOW_STATE: {       /* unsigned state */
        const unsigned state = va_arg(args, unsigned);
        const bool is_on_top = (state & VOUT_WINDOW_STATE_ABOVE) != 0;
679
#ifdef MODULE_NAME_IS_direct3d9
680 681 682
        if (sys->use_desktop && is_on_top)
            return VLC_EGENERIC;
#endif
683 684 685 686 687 688 689 690
        HMENU hMenu = GetSystemMenu(sys->hwnd, FALSE);

        if (is_on_top && !(GetWindowLong(sys->hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST)) {
            CheckMenuItem(hMenu, IDM_TOGGLE_ON_TOP, MF_BYCOMMAND | MFS_CHECKED);
            SetWindowPos(sys->hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
        } else if (!is_on_top && (GetWindowLong(sys->hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST)) {
            CheckMenuItem(hMenu, IDM_TOGGLE_ON_TOP, MF_BYCOMMAND | MFS_UNCHECKED);
            SetWindowPos(sys->hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);
691 692 693 694
        }
        sys->is_on_top = is_on_top;
        return VLC_SUCCESS;
    }
695 696 697
    case VOUT_DISPLAY_CHANGE_FULLSCREEN: {
        bool fs = va_arg(args, int);
        if (CommonControlSetFullscreen(vd, fs))
698 699 700
            return VLC_EGENERIC;
        UpdateRects(vd, NULL, NULL, false);
        return VLC_SUCCESS;
701 702 703
    }

    case VOUT_DISPLAY_HIDE_MOUSE:
704 705 706
        EventThreadMouseHide(sys->event);
        return VLC_SUCCESS;
    case VOUT_DISPLAY_RESET_PICTURES:
707
        vlc_assert_unreachable();
708 709 710
    default:
        return VLC_EGENERIC;
    }
711 712
}

713
static void DisableScreensaver(vout_display_t *vd)
714
{
715 716
    vout_display_sys_t *sys = vd->sys;

717
    /* disable screensaver by temporarily changing system settings */
718
    sys->i_spi_screensaveactive = 0;
719 720
    if (var_GetBool(vd, "disable-screensaver")) {
        msg_Dbg(vd, "disabling screen saver");
721 722 723 724 725
        SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0,
                             &sys->i_spi_screensaveactive, 0);

        if (FALSE != sys->i_spi_screensaveactive) {
            SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, 0, NULL, 0);
726 727 728 729
        }
    }
}

730
static void RestoreScreensaver(vout_display_t *vd)
731
{
732 733
    vout_display_sys_t *sys = vd->sys;

734
    /* restore screensaver system settings */
735 736 737
    if (0 != sys->i_spi_screensaveactive) {
        SystemParametersInfo(SPI_SETSCREENSAVEACTIVE,
                             sys->i_spi_screensaveactive, NULL, 0);
738 739
    }
}
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
740 741 742 743 744 745 746 747 748 749 750

#else

int CommonControl(vout_display_t *vd, int query, va_list args)
{
    switch (query) {
    default:
        return VLC_EGENERIC;
    }
}

751 752 753 754 755 756
void CommonManage(vout_display_t *vd) {
    UpdateRects(vd, NULL, NULL, false);
}
void CommonClean(vout_display_t *vd) {}
void CommonDisplay(vout_display_t *vd) {}
void CommonChangeThumbnailClip(vout_display_t *vd, bool show) {}
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
757
#endif