Coverage Report

Created: 2026-06-25 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mpv/video/out/win_state.c
Line
Count
Source
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 = MPMAX(n_w / asp, 1);
61
0
    } else {
62
0
        *w = MPMAX(n_h * asp, 1);
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_pos: force window position even if opts->force_window_position is not
78
//             set. If geometry x/y is not valid, this will center the window.
79
//             Useful for initial window placement.
80
//  size_constraint: if non-NULL, the resulting size will be limited by the size
81
//                   specified in this geometry. This applies to both autofit
82
//                   and geometry options.
83
// Use vo_apply_window_geometry() to copy the result into the vo.
84
// NOTE: currently, all windowing backends do their own handling of window
85
//       geometry additional to this code. This is to deal with initial window
86
//       placement, fullscreen handling, avoiding resize on reconfig() with no
87
//       size change, multi-monitor stuff, and possibly more.
88
void vo_calc_window_geometry(struct vo *vo, struct mp_vo_opts *opts,
89
                             const struct mp_rect *screen,
90
                             const struct mp_rect *monitor, double dpi_scale,
91
                             bool force_pos, struct vo_win_geometry *out_geo,
92
                             struct m_geometry *size_constraint)
93
0
{
94
0
    *out_geo = (struct vo_win_geometry){0};
95
96
    // The case of calling this function even though no video was configured
97
    // yet (i.e. vo->params==NULL) happens when vo_gpu creates a hidden window
98
    // in order to create a rendering context.
99
0
    struct mp_image_params params = { .w = 320, .h = 200 };
100
0
    if (vo->params)
101
0
        params = *vo->params;
102
103
0
    if (!opts->hidpi_window_scale)
104
0
        dpi_scale = 1;
105
106
0
    int d_w, d_h;
107
0
    mp_image_params_get_dsize(&params, &d_w, &d_h);
108
0
    if ((vo->driver->caps & VO_CAP_ROTATE90) && params.rotate % 180 == 90)
109
0
        MPSWAP(int, d_w, d_h);
110
0
    d_w = MPCLAMP(d_w * opts->window_scale * dpi_scale, 1, 16000);
111
0
    d_h = MPCLAMP(d_h * opts->window_scale * dpi_scale, 1, 16000);
112
113
0
    int scr_w = screen->x1 - screen->x0;
114
0
    int scr_h = screen->y1 - screen->y0;
115
116
0
    int mon_w = monitor->x1 - monitor->x0;
117
0
    int mon_h = monitor->y1 - monitor->y0;
118
119
0
    MP_DBG(vo, "max content size: %dx%d\n", scr_w, scr_h);
120
0
    MP_DBG(vo, "monitor size: %dx%d\n", mon_w, mon_h);
121
122
0
    calc_monitor_aspect(opts, mon_w, mon_h, &out_geo->monitor_par, &d_w, &d_h);
123
124
0
    apply_autofit(&d_w, &d_h, scr_w, scr_h, &opts->autofit, true, true);
125
0
    apply_autofit(&d_w, &d_h, scr_w, scr_h, &opts->autofit_smaller, true, false);
126
0
    apply_autofit(&d_w, &d_h, scr_w, scr_h, &opts->autofit_larger, false, true);
127
0
    if (size_constraint)
128
0
        apply_autofit(&d_w, &d_h, scr_w, scr_h, size_constraint, false, true);
129
130
0
    out_geo->win.x0 = (int)(scr_w - d_w) / 2;
131
0
    out_geo->win.y0 = (int)(scr_h - d_h) / 2;
132
133
0
    bool center = (opts->force_window_position || force_pos) && !opts->geometry.xy_valid;
134
0
    m_geometry_apply(&out_geo->win.x0, &out_geo->win.y0, &d_w, &d_h,
135
0
                     scr_w, scr_h, center, &opts->geometry);
136
137
0
    if (size_constraint) {
138
0
        int old_w = d_w, old_h = d_h;
139
0
        apply_autofit(&d_w, &d_h, scr_w, scr_h, size_constraint, false, true);
140
0
        if (!opts->geometry.xy_valid) {
141
0
            out_geo->win.x0 += (old_w - d_w) / 2;
142
0
            out_geo->win.y0 += (old_h - d_h) / 2;
143
0
        }
144
0
    }
145
146
0
    out_geo->win.x0 += screen->x0;
147
0
    out_geo->win.y0 += screen->y0;
148
0
    out_geo->win.x1 = out_geo->win.x0 + d_w;
149
0
    out_geo->win.y1 = out_geo->win.y0 + d_h;
150
151
0
    if (opts->force_window_position || force_pos)
152
0
        out_geo->flags |= VO_WIN_FORCE_POS;
153
0
}
154
155
// Copy the parameters in *geo to the vo fields.
156
// (Doesn't do anything else - windowing backends should trigger VO_EVENT_RESIZE
157
//  to ensure that the VO reinitializes rendering properly.)
158
void vo_apply_window_geometry(struct vo *vo, const struct vo_win_geometry *geo)
159
0
{
160
0
    vo->dwidth = geo->win.x1 - geo->win.x0;
161
0
    vo->dheight = geo->win.y1 - geo->win.y0;
162
0
    vo->monitor_par = geo->monitor_par;
163
0
}