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 
calc_monitor_aspect(struct mp_vo_opts * opts,int scr_w,int scr_h,double * pixelaspect,int * w,int * h)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 {
26     *pixelaspect = 1.0 / opts->monitor_pixel_aspect;
27 
28     if (scr_w > 0 && scr_h > 0 && opts->force_monitor_aspect)
29         *pixelaspect = 1.0 / (opts->force_monitor_aspect * scr_h / scr_w);
30 
31     if (*pixelaspect < 1) {
32         *h /= *pixelaspect;
33     } else {
34         *w *= *pixelaspect;
35     }
36 }
37 
38 // Fit *w/*h into the size specified by geo.
apply_autofit(int * w,int * h,int scr_w,int scr_h,struct m_geometry * geo,bool allow_up,bool allow_down)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 {
42     if (!geo->wh_valid)
43         return;
44 
45     int dummy = 0;
46     int n_w = *w, n_h = *h;
47     m_geometry_apply(&dummy, &dummy, &n_w, &n_h, scr_w, scr_h, geo);
48 
49     if (!allow_up && *w <= n_w && *h <= n_h)
50         return;
51     if (!allow_down && *w >= n_w && *h >= n_h)
52         return;
53 
54     // If aspect mismatches, always make the window smaller than the fit box
55     // (Or larger, if allow_down==false.)
56     double asp = (double)*w / *h;
57     double n_asp = (double)n_w / n_h;
58     if ((n_asp <= asp) == allow_down) {
59         *w = n_w;
60         *h = n_w / asp;
61     } else {
62         *w = n_h * asp;
63         *h = n_h;
64     }
65 }
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 //  monitor: position of the monitor on virtual desktop (used for pixelaspect).
73 //  dpi_scale: the DPI multiplier to get from virtual to real coordinates
74 //             (>1 for "hidpi")
75 // Use vo_apply_window_geometry() to copy the result into the vo.
76 // NOTE: currently, all windowing backends do their own handling of window
77 //       geometry additional to this code. This is to deal with initial window
78 //       placement, fullscreen handling, avoiding resize on reconfig() with no
79 //       size change, multi-monitor stuff, and possibly more.
vo_calc_window_geometry3(struct vo * vo,const struct mp_rect * screen,const struct mp_rect * monitor,double dpi_scale,struct vo_win_geometry * out_geo)80 void vo_calc_window_geometry3(struct vo *vo, const struct mp_rect *screen,
81                               const struct mp_rect *monitor,
82                               double dpi_scale, struct vo_win_geometry *out_geo)
83 {
84     struct mp_vo_opts *opts = vo->opts;
85 
86     *out_geo = (struct vo_win_geometry){0};
87 
88     // The case of calling this function even though no video was configured
89     // yet (i.e. vo->params==NULL) happens when vo_gpu creates a hidden window
90     // in order to create a rendering context.
91     struct mp_image_params params = { .w = 320, .h = 200 };
92     if (vo->params)
93         params = *vo->params;
94 
95     if (!opts->hidpi_window_scale)
96         dpi_scale = 1;
97 
98     int d_w, d_h;
99     mp_image_params_get_dsize(&params, &d_w, &d_h);
100     if ((vo->driver->caps & VO_CAP_ROTATE90) && params.rotate % 180 == 90)
101         MPSWAP(int, d_w, d_h);
102     d_w = MPCLAMP(d_w * opts->window_scale * dpi_scale, 1, 16000);
103     d_h = MPCLAMP(d_h * opts->window_scale * dpi_scale, 1, 16000);
104 
105     int scr_w = screen->x1 - screen->x0;
106     int scr_h = screen->y1 - screen->y0;
107 
108     int mon_w = monitor->x1 - monitor->x0;
109     int mon_h = monitor->y1 - monitor->y0;
110 
111     MP_DBG(vo, "max content size: %dx%d\n", scr_w, scr_h);
112     MP_DBG(vo, "monitor size: %dx%d\n", mon_w, mon_h);
113 
114     calc_monitor_aspect(opts, mon_w, mon_h, &out_geo->monitor_par, &d_w, &d_h);
115 
116     apply_autofit(&d_w, &d_h, scr_w, scr_h, &opts->autofit, true, true);
117     apply_autofit(&d_w, &d_h, scr_w, scr_h, &opts->autofit_smaller, true, false);
118     apply_autofit(&d_w, &d_h, scr_w, scr_h, &opts->autofit_larger, false, true);
119 
120     out_geo->win.x0 = (int)(scr_w - d_w) / 2;
121     out_geo->win.y0 = (int)(scr_h - d_h) / 2;
122     m_geometry_apply(&out_geo->win.x0, &out_geo->win.y0, &d_w, &d_h,
123                      scr_w, scr_h, &opts->geometry);
124 
125     out_geo->win.x0 += screen->x0;
126     out_geo->win.y0 += screen->y0;
127     out_geo->win.x1 = out_geo->win.x0 + d_w;
128     out_geo->win.y1 = out_geo->win.y0 + d_h;
129 
130     if (opts->geometry.xy_valid || opts->force_window_position)
131         out_geo->flags |= VO_WIN_FORCE_POS;
132 }
133 
134 // same as vo_calc_window_geometry3 with monitor assumed same as screen
vo_calc_window_geometry2(struct vo * vo,const struct mp_rect * screen,double dpi_scale,struct vo_win_geometry * out_geo)135 void vo_calc_window_geometry2(struct vo *vo, const struct mp_rect *screen,
136                               double dpi_scale, struct vo_win_geometry *out_geo)
137 {
138     vo_calc_window_geometry3(vo, screen, screen, dpi_scale, out_geo);
139 }
140 
vo_calc_window_geometry(struct vo * vo,const struct mp_rect * screen,struct vo_win_geometry * out_geo)141 void vo_calc_window_geometry(struct vo *vo, const struct mp_rect *screen,
142                              struct vo_win_geometry *out_geo)
143 {
144     vo_calc_window_geometry2(vo, screen, 1.0, out_geo);
145 }
146 
147 // Copy the parameters in *geo to the vo fields.
148 // (Doesn't do anything else - windowing backends should trigger VO_EVENT_RESIZE
149 //  to ensure that the VO reinitializes rendering properly.)
vo_apply_window_geometry(struct vo * vo,const struct vo_win_geometry * geo)150 void vo_apply_window_geometry(struct vo *vo, const struct vo_win_geometry *geo)
151 {
152     vo->dwidth = geo->win.x1 - geo->win.x0;
153     vo->dheight = geo->win.y1 - geo->win.y0;
154     vo->monitor_par = geo->monitor_par;
155 }
156