/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(¶ms, &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 | } |