diff options
Diffstat (limited to 'video/out/win32/displayconfig.c')
-rw-r--r-- | video/out/win32/displayconfig.c | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/video/out/win32/displayconfig.c b/video/out/win32/displayconfig.c new file mode 100644 index 0000000..9844afd --- /dev/null +++ b/video/out/win32/displayconfig.c @@ -0,0 +1,140 @@ +/* + * This file is part of mpv. + * + * mpv 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 (at your option) any later version. + * + * mpv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with mpv. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <windows.h> +#include <stdbool.h> +#include <string.h> + +#include "displayconfig.h" + +#include "mpv_talloc.h" + +static bool is_valid_refresh_rate(DISPLAYCONFIG_RATIONAL rr) +{ + // DisplayConfig sometimes reports a rate of 1 when the rate is not known + return rr.Denominator != 0 && rr.Numerator / rr.Denominator > 1; +} + +static int get_config(void *ctx, + UINT32 *num_paths, DISPLAYCONFIG_PATH_INFO** paths, + UINT32 *num_modes, DISPLAYCONFIG_MODE_INFO** modes) +{ + LONG res; + *paths = NULL; + *modes = NULL; + + // The display configuration could change between the call to + // GetDisplayConfigBufferSizes and the call to QueryDisplayConfig, so call + // them in a loop until the correct buffer size is chosen + do { + res = GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, num_paths, + num_modes); + if (res != ERROR_SUCCESS) + goto fail; + + // Free old buffers if they exist and allocate new ones + talloc_free(*paths); + talloc_free(*modes); + *paths = talloc_array(ctx, DISPLAYCONFIG_PATH_INFO, *num_paths); + *modes = talloc_array(ctx, DISPLAYCONFIG_MODE_INFO, *num_modes); + + res = QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, num_paths, *paths, + num_modes, *modes, NULL); + } while (res == ERROR_INSUFFICIENT_BUFFER); + if (res != ERROR_SUCCESS) + goto fail; + + return 0; +fail: + talloc_free(*paths); + talloc_free(*modes); + return -1; +} + +static DISPLAYCONFIG_PATH_INFO *get_path(UINT32 num_paths, + DISPLAYCONFIG_PATH_INFO* paths, + const wchar_t *device) +{ + // Search for a path with a matching device name + for (UINT32 i = 0; i < num_paths; i++) { + // Send a GET_SOURCE_NAME request + DISPLAYCONFIG_SOURCE_DEVICE_NAME source = { + .header = { + .size = sizeof source, + .type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME, + .adapterId = paths[i].sourceInfo.adapterId, + .id = paths[i].sourceInfo.id, + } + }; + if (DisplayConfigGetDeviceInfo(&source.header) != ERROR_SUCCESS) + return NULL; + + // Check if the device name matches + if (!wcscmp(device, source.viewGdiDeviceName)) + return &paths[i]; + } + + return NULL; +} + +static double get_refresh_rate_from_mode(DISPLAYCONFIG_MODE_INFO *mode) +{ + if (mode->infoType != DISPLAYCONFIG_MODE_INFO_TYPE_TARGET) + return 0.0; + + DISPLAYCONFIG_VIDEO_SIGNAL_INFO *info = + &mode->targetMode.targetVideoSignalInfo; + if (!is_valid_refresh_rate(info->vSyncFreq)) + return 0.0; + + return ((double)info->vSyncFreq.Numerator) / + ((double)info->vSyncFreq.Denominator); +} + +double mp_w32_displayconfig_get_refresh_rate(const wchar_t *device) +{ + void *ctx = talloc_new(NULL); + double freq = 0.0; + + // Get the current display configuration + UINT32 num_paths; + DISPLAYCONFIG_PATH_INFO* paths; + UINT32 num_modes; + DISPLAYCONFIG_MODE_INFO* modes; + if (get_config(ctx, &num_paths, &paths, &num_modes, &modes)) + goto end; + + // Get the path for the specified monitor + DISPLAYCONFIG_PATH_INFO* path; + if (!(path = get_path(num_paths, paths, device))) + goto end; + + // Try getting the refresh rate from the mode first. The value in the mode + // overrides the value in the path. + if (path->targetInfo.modeInfoIdx != DISPLAYCONFIG_PATH_MODE_IDX_INVALID) + freq = get_refresh_rate_from_mode(&modes[path->targetInfo.modeInfoIdx]); + + // If the mode didn't contain a valid refresh rate, try the path + if (freq == 0.0 && is_valid_refresh_rate(path->targetInfo.refreshRate)) { + freq = ((double)path->targetInfo.refreshRate.Numerator) / + ((double)path->targetInfo.refreshRate.Denominator); + } + +end: + talloc_free(ctx); + return freq; +} |