Coverage Report

Created: 2025-08-11 06:44

/src/mpv/video/out/win_state.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * This file is part of mpv.
3
 *
4
 * mpv is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6
 * License as published by the Free Software Foundation; either
7
 * version 2.1 of the License, or (at your option) any later version.
8
 *
9
 * mpv is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15
 * License along with mpv.  If not, see <http://www.gnu.org/licenses/>.
16
 */
17
18
#include "win_state.h"
19
#include "vo.h"
20
21
#include "video/mp_image.h"
22
23
static void calc_monitor_aspect(struct mp_vo_opts *opts, int scr_w, int scr_h,
24
                                double *pixelaspect, int *w, int *h)
25
0
{
26
0
    *pixelaspect = 1.0 / opts->monitor_pixel_aspect;
27
28
0
    if (scr_w > 0 && scr_h > 0 && opts->force_monitor_aspect)
29
0
        *pixelaspect = 1.0 / (opts->force_monitor_aspect * scr_h / scr_w);
30
31
0
    if (*pixelaspect < 1) {
32
0
        *h /= *pixelaspect;
33
0
    } else {
34
0
        *w *= *pixelaspect;
35
0
    }
36
0
}
37
38
// Fit *w/*h into the size specified by geo.
39
static void apply_autofit(int *w, int *h, int scr_w, int scr_h,
40
                          struct m_geometry *geo, bool allow_up, bool allow_down)
41
0
{
42
0
    if (!geo->wh_valid)
43
0
        return;
44
45
0
    int dummy = 0;
46
0
    int n_w = *w, n_h = *h;
47
0
    m_geometry_apply(&dummy, &dummy, &n_w, &n_h, scr_w, scr_h, true, geo);
48
49
0
    if (!allow_up && *w <= n_w && *h <= n_h)
50
0
        return;
51
0
    if (!allow_down && *w >= n_w && *h >= n_h)
52
0
        return;
53
54
    // If aspect mismatches, always make the window smaller than the fit box
55
    // (Or larger, if allow_down==false.)
56
0
    double asp = (double)*w / *h;
57
0
    double n_asp = (double)n_w / n_h;
58
0
    if ((n_asp <= asp) == allow_down) {
59
0
        *w = n_w;
60
0
        *h = n_w / asp;
61
0
    } else {
62
0
        *w = n_h * asp;
63
0
        *h = n_h;
64
0
    }
65
0
}
66
67
// Compute the "suggested" window size and position and return it in *out_geo.
68
// screen is the bounding box of the current screen within the virtual desktop.
69
// Does not change *vo.
70
//  screen: position of the area on virtual desktop on which the video-content
71
//          should be placed (maybe after excluding decorations, taskbars, etc)
72
//          can be the same as monitor for platforms that don't try to take into
73
//          account decorations, taskbars, etc.
74
//  monitor: position of the monitor on virtual desktop (used for pixelaspect).
75
//  dpi_scale: the DPI multiplier to get from virtual to real coordinates
76
//             (>1 for "hidpi")
77
//  force_center: force centering x/y in the middle of the given screen even if
78
//                opts->force_window_position is not set. ignored if the user
79
//                supplies valid x/y coordinates on their own.
80
// Use vo_apply_window_geometry() to copy the result into the vo.
81
// NOTE: currently, all windowing backends do their own handling of window
82
//       geometry additional to this code. This is to deal with initial window
83
//       placement, fullscreen handling, avoiding resize on reconfig() with no
84
//       size change, multi-monitor stuff, and possibly more.
85
void vo_calc_window_geometry(struct vo *vo, const struct mp_rect *screen,
86
                             const struct mp_rect *monitor, double dpi_scale,
87
                             bool force_center, struct vo_win_geometry *out_geo)
88
0
{
89
0
    struct mp_vo_opts *opts = vo->opts;
90
91
0
    *out_geo = (struct vo_win_geometry){0};
92
93
    // The case of calling this function even though no video was configured
94
    // yet (i.e. vo->params==NULL) happens when vo_gpu creates a hidden window
95
    // in order to create a rendering context.
96
0
    struct mp_image_params params = { .w = 320, .h = 200 };
97
0
    if (vo->params)
98
0
        params = *vo->params;
99
100
0
    if (!opts->hidpi_window_scale)
101
0
        dpi_scale = 1;
102
103
0
    int d_w, d_h;
104
0
    mp_image_params_get_dsize(&params, &d_w, &d_h);
105
0
    if ((vo->driver->caps & VO_CAP_ROTATE90) && params.rotate % 180 == 90)
106
0
        MPSWAP(int, d_w, d_h);
107
0
    d_w = MPCLAMP(d_w * opts->window_scale * dpi_scale, 1, 16000);
108
0
    d_h = MPCLAMP(d_h * opts->window_scale * dpi_scale, 1, 16000);
109
110
0
    int scr_w = screen->x1 - screen->x0;
111
0
    int scr_h = screen->y1 - screen->y0;
112
113
0
    int mon_w = monitor->x1 - monitor->x0;
114
0
    int mon_h = monitor->y1 - monitor->y0;
115
116
0
    MP_DBG(vo, "max content size: %dx%d\n", scr_w, scr_h);
117
0
    MP_DBG(vo, "monitor size: %dx%d\n", mon_w, mon_h);
118
119
0
    calc_monitor_aspect(opts, mon_w, mon_h, &out_geo->monitor_par, &d_w, &d_h);
120
121
0
    apply_autofit(&d_w, &d_h, scr_w, scr_h, &opts->autofit, true, true);
122
0
    apply_autofit(&d_w, &d_h, scr_w, scr_h, &opts->autofit_smaller, true, false);
123
0
    apply_autofit(&d_w, &d_h, scr_w, scr_h, &opts->autofit_larger, false, true);
124
125
0
    out_geo->win.x0 = (int)(scr_w - d_w) / 2;
126
0
    out_geo->win.y0 = (int)(scr_h - d_h) / 2;
127
128
0
    bool center = (opts->force_window_position || force_center) && !opts->geometry.xy_valid;
129
0
    m_geometry_apply(&out_geo->win.x0, &out_geo->win.y0, &d_w, &d_h,
130
0
                     scr_w, scr_h, center, &opts->geometry);
131
132
0
    out_geo->win.x0 += screen->x0;
133
0
    out_geo->win.y0 += screen->y0;
134
0
    out_geo->win.x1 = out_geo->win.x0 + d_w;
135
0
    out_geo->win.y1 = out_geo->win.y0 + d_h;
136
137
0
    if (opts->geometry.xy_valid || opts->force_window_position || force_center)
138
0
        out_geo->flags |= VO_WIN_FORCE_POS;
139
0
}
140
141
// Copy the parameters in *geo to the vo fields.
142
// (Doesn't do anything else - windowing backends should trigger VO_EVENT_RESIZE
143
//  to ensure that the VO reinitializes rendering properly.)
144
void vo_apply_window_geometry(struct vo *vo, const struct vo_win_geometry *geo)
145
0
{
146
0
    vo->dwidth = geo->win.x1 - geo->win.x0;
147
0
    vo->dheight = geo->win.y1 - geo->win.y0;
148
0
    vo->monitor_par = geo->monitor_par;
149
0
}