From patchwork Fri Jul 5 19:45:01 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Dilshod Mukhtarov X-Patchwork-Id: 13825 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 095AF44A0C0 for ; Fri, 5 Jul 2019 22:53:06 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id D7AB368AD29; Fri, 5 Jul 2019 22:53:05 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-lf1-f43.google.com (mail-lf1-f43.google.com [209.85.167.43]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 1FD9368AD02 for ; Fri, 5 Jul 2019 22:52:59 +0300 (EEST) Received: by mail-lf1-f43.google.com with SMTP id 62so7008380lfa.8 for ; Fri, 05 Jul 2019 12:52:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=to:from:subject:message-id:date:user-agent:mime-version :content-language; bh=2TzaboEC9xEvFpPH/R4j7XcHs8Bu6xJtVZfNX4bcQsc=; b=ihNYcMc3p5U7PdK//JUIAqXR8IRJpPU4a9oGXPrndbsmdnBJ2dxYizhO+41QXrmTYB T1m2tcSZMoO8+xJn2BW+rH6WUSnVsbEPsVd1MEGIpI1NIBc5pIeie5QHh/ZZ+XhWyQfz cyRkcMuDZ6ww+NvPmDnwJTJLc88iMa/Unl3/Jjh9hc17t36xv4H+T+bsdMERKaGoMB4/ NEIgFDYbZAPsxzFjcrU5YZ9ruokhkzktnfp+G0nS+wc2ajvcJ2ZSWYHru1bKyNBHU/oV PiK+cdBdN5XdzqL1SHpy9C3SrVu19TPjqnMhM5kx6DGOya/LryU0Ci2/ISGNMEC6QX/I HFDA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:to:from:subject:message-id:date:user-agent :mime-version:content-language; bh=2TzaboEC9xEvFpPH/R4j7XcHs8Bu6xJtVZfNX4bcQsc=; b=jKgJft/UbxzG0xO3q131SePoWQbtl51qbDLooth7gjwkidVspYkzU7Lz6wxQCReRRA Vf3RverOvCrG2KzyHI1wm1Y2cDoif8MheZcZ5F4r2wcdtJHyHmAxEUmRsiwf8W2aCcD9 IEWbNCLeE5ppMxFsgLLyhF1isvhbVyKsXRrl+EitrJ7e+divbShWMquenyxDeF2KN7Qs D3UXpoFMzBc/xrBhAuIClTHWj9IbvO3xjxYLfukMpQM+Q4BUMSgxPinoGPEdPvPT4EyX /ZK0cdL9amKbwG3T7l6Mn5ffWxI/xlQKg2dsA8Pretob5HzFtgImUiILTq7l9VvJslWB N+DQ== X-Gm-Message-State: APjAAAX75IwtM9bTKTuA1+MsZDoAyUYJSp3N+tBlWmRXn+zHoD4+4VYa 4x3LGR9KK+oJuIrWzpqEO6/iX3msjcM= X-Google-Smtp-Source: APXvYqxukFrRK3a21LD8caRi3LWK8/J9+uZtYQ7gBoZoQnOrbnCGnSaCKgLG0h/GwfaO8s9/Or/QOw== X-Received: by 2002:a19:9c54:: with SMTP id f81mr2719957lfe.146.1562355904597; Fri, 05 Jul 2019 12:45:04 -0700 (PDT) Received: from [192.168.101.101] ([5.30.115.13]) by smtp.googlemail.com with ESMTPSA id v17sm2135127ljg.36.2019.07.05.12.45.03 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 05 Jul 2019 12:45:03 -0700 (PDT) To: ffmpeg-devel@ffmpeg.org From: Dilshod Mukhtarov Message-ID: <32df4703-873d-88a7-c492-14f6cf4ccc99@gmail.com> Date: Fri, 5 Jul 2019 23:45:01 +0400 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.7.2 MIME-Version: 1.0 Content-Language: en-US X-Content-Filtered-By: Mailman/MimeDel 2.1.20 Subject: [FFmpeg-devel] gdigrab: fix HIDPI support for multi-monitor setup with different scale settings X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Hi, In Windows multi-monitor setup if there are different scales settings on different monitors then coordinates for videorecording  calculated not correct. Here is the patch that fixes this problem From 34ac7244b23ac2347a8e48b2ea2966cf69e1cf27 Mon Sep 17 00:00:00 2001 From: Dilshod Muktharov Date: Thu, 4 Jul 2019 21:52:09 +0400 Subject: [PATCH 1/2] HighDPI support for recording Windows multi-monitor mode In windows multi-monitor mode with different UI scales it was not correctly calculated total area of recording on non-primary monitor --- libavdevice/gdigrab.c | 210 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 195 insertions(+), 15 deletions(-) diff --git a/libavdevice/gdigrab.c b/libavdevice/gdigrab.c index f4444406fa..276b448692 100644 --- a/libavdevice/gdigrab.c +++ b/libavdevice/gdigrab.c @@ -212,6 +212,165 @@ gdigrab_region_wnd_update(AVFormatContext *s1, struct gdigrab *gdigrab) } } +typedef struct tagRESOLUTION { + int x; + int y; +} RESOLUTION; + +typedef struct tagMONITOR { + RESOLUTION logical; + RESOLUTION physical; + RECT rect; +} MONITOR; + +#define MY_MAX_MONITORS 4 +static MONITOR monitors[MY_MAX_MONITORS]; +static int monitorsNum = 0; +static RECT rcCombined; + +/** + * Callback function which is used by EnumDisplayMonitors() + * + * @param hMon + * @param hdc + * @param lprcMonitor - rectangle of the given monitor + * @param pData - arbitrary user data passed to EnumDisplayMonitors + * @return TRUE + */ +static BOOL CALLBACK +monitor_enum(HMONITOR hMon, HDC hdc, LPRECT lprcMonitor, LPARAM pData) +{ + monitors[monitorsNum].rect = *lprcMonitor; + UnionRect(&rcCombined, &rcCombined, lprcMonitor); + + MONITORINFOEXA monitor_info; + monitor_info.cbSize = sizeof (monitor_info); + GetMonitorInfoA(hMon, &monitor_info); + + HDC monitor_hdc = CreateDCA(NULL, monitor_info.szDevice, NULL, NULL); + + monitors[monitorsNum].logical.x = GetDeviceCaps(monitor_hdc, HORZRES); + monitors[monitorsNum].logical.y = GetDeviceCaps(monitor_hdc, VERTRES); + monitors[monitorsNum].physical.x = GetDeviceCaps(monitor_hdc, DESKTOPHORZRES); + monitors[monitorsNum].physical.y = GetDeviceCaps(monitor_hdc, DESKTOPVERTRES); + + DeleteDC(monitor_hdc); + + ++monitorsNum; + return TRUE; +} + +/** + * Returns the monitor id by given logical coordinates + * of a pixel + * + * @param x Logical coordinate x + * @param y Logical coordinate y + * @return id of monitor (in monitors) on success, -1 on error + */ +static int +get_monitor_id_by_logical_point(int x, int y) +{ + for (int i = 0; i < monitorsNum; ++i) { + if (monitors[i].rect.left <= x && x < monitors[i].rect.right && + monitors[i].rect.top <= y && y < monitors[i].rect.bottom) + return i; + } + + return -1; +} + +/** + * Returns the monitor id by given logical coordinates + * of a rectangle. The center of rectangle is used to get monitor + * + * @param rect Logical coordinates of rect + * @return id of monitor (in monitors) on success, -1 on error + */ +static int +get_monitor_id_by_logical_rectangle(const RECT *rect) +{ + int x = rect->left + (rect->right - rect->left) / 2; + int y = rect->top + (rect->bottom - rect->top) / 2; + return get_monitor_id_by_logical_point(x, y); +} + +/** + * Returns the monitor id by given x coordinate + * + * @param x Logical coordinate x + * @return id of monitor (in monitors) on success, -1 on error + */ +static int +get_monitor_id_by_logical_x(int x) +{ + for (int i = 0; i < monitorsNum; ++i) { + if (monitors[i].rect.left <= x && x < monitors[i].rect.right) + return i; + } + + return -1; +} + +/** + * Returns the monitor id by given y coordinate + * + * @param y Logical coordinate y + * @return id of monitor (in monitors) on success, -1 on error + */ +static int +get_monitor_id_by_logical_y(int y) +{ + for (int i = 0; i < monitorsNum; ++i) { + if (monitors[i].rect.top <= y && y < monitors[i].rect.bottom) + return i; + } + + return -1; +} + +#define LOGICAL_TO_PHYSICAL_X(val, i) ((val) * monitors[i].physical.x / monitors[i].logical.x) +#define LOGICAL_TO_PHYSICAL_Y(val, i) ((val) * monitors[i].physical.y / monitors[i].logical.y) + +/** + * Converts given rect from logical to physical pixel coordinates + * + * @param rect in logical coordinates, it will be modified to physical coordinates + * @return void + */ +static void +convert_logical_rect_to_physical(RECT *rect) +{ + int indX; + int indY; + int ind; + + /* convert top-left corner */ + ind = get_monitor_id_by_logical_point(rect->left, rect->top); + if (ind >= 0) { + indX = indY = ind; + } else { + /* monitor not found, let's search by x and y separately */ + indX = get_monitor_id_by_logical_x(rect->left); + indY = get_monitor_id_by_logical_y(rect->top); + } + rect->left = LOGICAL_TO_PHYSICAL_X(rect->left, indX); + rect->top = LOGICAL_TO_PHYSICAL_Y(rect->top, indY); + + /* convert bottom-right corner, we have to make -1 because bottom-right + * corner is not incluse */ + ind = get_monitor_id_by_logical_point(rect->right - 1, rect->bottom - 1); + if (ind >= 0) { + indX = indY = ind; + } else { + /* monitor not found, let's search by x and y separately */ + indX = get_monitor_id_by_logical_x(rect->right - 1); + indY = get_monitor_id_by_logical_y(rect->bottom - 1); + } + rect->right = LOGICAL_TO_PHYSICAL_X(rect->right, indX); + rect->bottom = LOGICAL_TO_PHYSICAL_Y(rect->bottom, indY); +} + /** * Initializes the gdi grab device demuxer (public device demuxer API). * @@ -235,10 +394,6 @@ gdigrab_read_header(AVFormatContext *s1) AVStream *st = NULL; int bpp; - int horzres; - int vertres; - int desktophorzres; - int desktopvertres; RECT virtual_rect; RECT clip_rect; BITMAP bmp; @@ -277,24 +432,49 @@ gdigrab_read_header(AVFormatContext *s1) } bpp = GetDeviceCaps(source_hdc, BITSPIXEL); - horzres = GetDeviceCaps(source_hdc, HORZRES); - vertres = GetDeviceCaps(source_hdc, VERTRES); - desktophorzres = GetDeviceCaps(source_hdc, DESKTOPHORZRES); - desktopvertres = GetDeviceCaps(source_hdc, DESKTOPVERTRES); + /* Get resolution and coordinates for all monitors */ + SetRectEmpty(&rcCombined); + monitorsNum = 0; + EnumDisplayMonitors(0, 0, monitor_enum, 0); + + for (int i = 0; i < monitorsNum; ++i) { + av_log(s1, AV_LOG_DEBUG, + "Monitor %i (%li,%li) (%li,%li), logical res:(%li,%li), physical res:(%li,%li)\n", + i, monitors[i].rect.left, monitors[i].rect.top, + monitors[i].rect.right, monitors[i].rect.bottom, + monitors[i].logical.x, monitors[i].logical.y, + monitors[i].physical.x, monitors[i].physical.y); + } if (hwnd) { + /* get actual window coordinates to retrieve it's monitor index */ + GetWindowRect(hwnd, &virtual_rect); + int ind = get_monitor_id_by_logical_rectangle(&virtual_rect); + GetClientRect(hwnd, &virtual_rect); + av_log(s1, AV_LOG_DEBUG, "Window rect logical (%li,%li)x(%li,%li)", + virtual_rect.left, virtual_rect.top, virtual_rect.right, virtual_rect.bottom); + /* window -- get the right height and width for scaling DPI */ - virtual_rect.left = virtual_rect.left * desktophorzres / horzres; - virtual_rect.right = virtual_rect.right * desktophorzres / horzres; - virtual_rect.top = virtual_rect.top * desktopvertres / vertres; - virtual_rect.bottom = virtual_rect.bottom * desktopvertres / vertres; + virtual_rect.left = LOGICAL_TO_PHYSICAL_X(virtual_rect.left, ind); + virtual_rect.right = LOGICAL_TO_PHYSICAL_X(virtual_rect.right, ind); + virtual_rect.top = LOGICAL_TO_PHYSICAL_Y(virtual_rect.top, ind); + virtual_rect.bottom = LOGICAL_TO_PHYSICAL_Y(virtual_rect.bottom, ind); + av_log(s1, AV_LOG_DEBUG, ", physical (%li,%li)x(%li,%li)\n", + virtual_rect.left, virtual_rect.top, virtual_rect.right, virtual_rect.bottom); } else { - /* desktop -- get the right height and width for scaling DPI */ virtual_rect.left = GetSystemMetrics(SM_XVIRTUALSCREEN); virtual_rect.top = GetSystemMetrics(SM_YVIRTUALSCREEN); - virtual_rect.right = (virtual_rect.left + GetSystemMetrics(SM_CXVIRTUALSCREEN)) * desktophorzres / horzres; - virtual_rect.bottom = (virtual_rect.top + GetSystemMetrics(SM_CYVIRTUALSCREEN)) * desktopvertres / vertres; + virtual_rect.right = (virtual_rect.left + GetSystemMetrics(SM_CXVIRTUALSCREEN)) ; + virtual_rect.bottom = (virtual_rect.top + GetSystemMetrics(SM_CYVIRTUALSCREEN)) ; + + av_log(s1, AV_LOG_DEBUG, "Virtual desktop logical (%li,%li)x(%li,%li)", + virtual_rect.left, virtual_rect.top, virtual_rect.right, virtual_rect.bottom); + + /* desktop -- get the right height and width for scaling DPI */ + convert_logical_rect_to_physical(&virtual_rect); + av_log(s1, AV_LOG_DEBUG, ", physical (%li,%li)x(%li,%li)\n", + virtual_rect.left, virtual_rect.top, virtual_rect.right, virtual_rect.bottom); } /* If no width or height set, use full screen/window area */ -- 2.21.0.windows.1