#include #include /************************************************************************** * Some helper function to set DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ***************************************************************************/ #ifndef DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 DECLARE_HANDLE(DPI_AWARENESS_CONTEXT); #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT)-3) typedef DPI_AWARENESS_CONTEXT(WINAPI* SetProcessDpiAwarenessContextFunc)(DPI_AWARENESS_CONTEXT); static void setDPIAwareness() { static SetProcessDpiAwarenessContextFunc setProcessDpiAwarenessContext; HMODULE user32Module = GetModuleHandleA("User32.dll"); setProcessDpiAwarenessContext = (SetProcessDpiAwarenessContextFunc)GetProcAddress(user32Module, "SetProcessDpiAwarenessContext"); if (setProcessDpiAwarenessContext != nullptr) { setProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); } else { fprintf(stderr, "Failed to get SetProcessDpiAwarenessContext\n"); } } #else static void setDPIAwareness() { SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); } #endif /*************************************************************************** * ************************************************************************* * Some helper functions to get the list of monitors and move the mouse * ************************************************************************* ***************************************************************************/ int g_nMonitorCounter; int g_nMonitorLimit; HMONITOR *g_hmpMonitors; // Callback for CountMonitors below BOOL WINAPI clb_fCountMonitors(HMONITOR hMon, HDC hDC, LPRECT rRect, LPARAM lP) { g_nMonitorCounter++; return TRUE; } int WINAPI CountMonitors() { g_nMonitorCounter = 0; ::EnumDisplayMonitors(NULL, NULL, clb_fCountMonitors, 0L); return g_nMonitorCounter; } // Callback for CollectMonitors below BOOL WINAPI clb_fCollectMonitors(HMONITOR hMon, HDC hDC, LPRECT rRect, LPARAM lP) { if ((g_nMonitorCounter < g_nMonitorLimit) && (NULL != g_hmpMonitors)) { g_hmpMonitors[g_nMonitorCounter] = hMon; g_nMonitorCounter++; } return TRUE; } int WINAPI CollectMonitors(HMONITOR *hmpMonitors, int nNum) { int retCode = 0; if (NULL != hmpMonitors) { g_nMonitorCounter = 0; g_nMonitorLimit = nNum; g_hmpMonitors = hmpMonitors; ::EnumDisplayMonitors(NULL, NULL, clb_fCollectMonitors, 0L); retCode = g_nMonitorCounter; g_nMonitorCounter = 0; g_nMonitorLimit = 0; g_hmpMonitors = NULL; } return retCode; } BOOL WINAPI MonitorBounds(HMONITOR hmMonitor, RECT *rpBounds) { BOOL retCode = FALSE; if ((NULL != hmMonitor) && (NULL != rpBounds)) { MONITORINFOEX miInfo; memset((void *)(&miInfo), 0, sizeof(MONITORINFOEX)); miInfo.cbSize = sizeof(MONITORINFOEX); if (TRUE == (retCode = ::GetMonitorInfo(hmMonitor, &miInfo))) { (*rpBounds) = miInfo.rcMonitor; } } return retCode; } static int signum(int i) { // special version of signum which returns 1 when value is 0 return i >= 0 ? 1 : -1; } static void MouseMove(int x, int y) { INPUT mouseInput = { 0 }; mouseInput.type = INPUT_MOUSE; mouseInput.mi.time = 0; mouseInput.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; mouseInput.mi.dx = (x * 65536 / ::GetSystemMetrics(SM_CXSCREEN)) + signum(x); mouseInput.mi.dy = (y * 65536 / ::GetSystemMetrics(SM_CYSCREEN)) + signum(y); ::SendInput(1, &mouseInput, sizeof(mouseInput)); } /********************************************************************* * ******************************************************************* * The actual test. * ******************************************************************* *********************************************************************/ int main(int argc, char *argv[]) { setDPIAwareness(); // if the test will be run on a HiDPI system, mimic java // Fetch information of the monitors, mimic the code in JDK int numScreens = CountMonitors(); HMONITOR *monHds = (HMONITOR *)malloc(numScreens * sizeof(HMONITOR)); if (numScreens != CollectMonitors(monHds, numScreens)) { fprintf(stderr, "Failed to get all monitor handles\n"); free(monHds); return FALSE; } fprintf(stderr, "Number of monitors: %d\n", numScreens); // Validate the system config where the bug is reproduced if (numScreens != 2) { fprintf(stderr, "Need two monitors to reproduce bug\n"); return 1; } // Reminder, the left monitor should be horizontal RECT leftMonitor = { 0, 0, 0, 0 }; MonitorBounds(monHds[0], &leftMonitor); // Reminder, the right monitor should be vertical RECT rightMonitor = { 0, 0, 0, 0 }; MonitorBounds(monHds[1], &rightMonitor); if (leftMonitor.left != 0 && leftMonitor.top != 0) { fprintf(stderr, "Left monitor should be main\n"); return 1; } if (leftMonitor.right > rightMonitor.left) { fprintf(stderr, "Right monitor, wrong location\n"); return 1; } if (rightMonitor.top > -300) { fprintf(stderr, "Right vertical monitor should have negative top\n"); return 1; } // Now reproduce the bug. // set some initial location MouseMove(0, 0); fprintf(stderr, "Left monitor: left=%ld, top=%ld, right=%ld, bottom=%ld\n", leftMonitor.left, leftMonitor.top, leftMonitor.right, leftMonitor.bottom); fprintf(stderr, "Right monitor: left=%ld, top=%ld, right=%ld, bottom=%ld\n", rightMonitor.left, rightMonitor.top, rightMonitor.right, rightMonitor.bottom); // Move the mouse near the top/left corner of the right monitor int x = rightMonitor.left + 100; int y = rightMonitor.top + 100; if (y > 0) { fprintf(stderr, "The Y should be negative to reproduce the bug\n"); return 1; } fprintf(stderr, "Will move to: x=%d, y=%d\n", x, y); MouseMove(x, y); //MouseMove(x, y); // Uncomment to work around the bug Sleep(1000); POINT pt; GetCursorPos(&pt); fprintf(stderr, "The mouse is at: x=%ld, y=%ld\n", pt.x, pt.y); if (pt.x != x || pt.y != y) { fprintf(stderr, "Error\n"); // Do not !trust! this and check the actual mouse position visually return 1; } fprintf(stderr, "Done \n"); return 0; } /************************************************************************** * The output on my setup: *************************************************************************/ // Number of monitors: 2 // Left monitor: left=0, top=0, right=3840, bottom=2160 // Right monitor: left=3840, top=-1671, right=6000, bottom=2169 // Will move to: x=3940, y=-1571 // The mouse is at: x=3840, y=-1571 // Error