From: Conor McCarthy cmccarthy@codeweavers.com
Requires display mode emulation for failures to manifest. --- dlls/user32/tests/monitor.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
diff --git a/dlls/user32/tests/monitor.c b/dlls/user32/tests/monitor.c index 206d9505705..7ad3970a646 100644 --- a/dlls/user32/tests/monitor.c +++ b/dlls/user32/tests/monitor.c @@ -3611,6 +3611,8 @@ static void test_monitor_dpi(void) DPI_AWARENESS_CONTEXT old_ctx; float scale_x, scale_y; BOOL ret, is_virtual; + INT x, y; + POINT pt;
if (!pGetDpiForMonitorInternal || !pSetThreadDpiAwarenessContext) { @@ -3722,6 +3724,19 @@ static void test_monitor_dpi(void) ok( fabs( dpi_y - system_dpi * scale_y ) < system_dpi * 0.05, "got MDT_RAW_DPI y %u\n", dpi_y ); }
+ /* Test for cursor position rounding errors. To be meaninful, this requires display + * mode emulation. To enable it, run 'wine control desk.cpl' and set the checkbox. */ + for (x = infos[i].rect.left; x < infos[i].rect.left + 64; ++x) + { + y = x - infos[i].rect.left + infos[i].rect.top; + SetCursorPos(x, y); + GetCursorPos(&pt); + todo_wine_if(pt.x != x) + ok(pt.x == x, "got x %ld, expected %d.\n", pt.x, x); + todo_wine_if(pt.y != y) + ok(pt.y == y, "got y %ld, expected %d.\n", pt.y, y); + } + pSetThreadDpiAwarenessContext( old_ctx ); } }
From: Conor McCarthy cmccarthy@codeweavers.com
Rounding errors occur during the multiple steps, so that after a call to SetCursorPos(), a subsequent call to GetCursorPos() may not return the exact coordinates set, which an app may interpret as mouse movement. --- dlls/user32/tests/monitor.c | 2 - dlls/win32u/input.c | 13 ++-- dlls/win32u/message.c | 10 +-- dlls/win32u/sysparams.c | 120 +++++++++++++++++++++++++++++++++++ dlls/win32u/win32u_private.h | 2 + 5 files changed, 129 insertions(+), 18 deletions(-)
diff --git a/dlls/user32/tests/monitor.c b/dlls/user32/tests/monitor.c index 7ad3970a646..f76971c1b53 100644 --- a/dlls/user32/tests/monitor.c +++ b/dlls/user32/tests/monitor.c @@ -3731,9 +3731,7 @@ static void test_monitor_dpi(void) y = x - infos[i].rect.left + infos[i].rect.top; SetCursorPos(x, y); GetCursorPos(&pt); - todo_wine_if(pt.x != x) ok(pt.x == x, "got x %ld, expected %d.\n", pt.x, x); - todo_wine_if(pt.y != y) ok(pt.y == y, "got y %ld, expected %d.\n", pt.y, y); }
diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 252ae78e6a0..5b3c8c13da4 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -706,16 +706,16 @@ UINT WINAPI NtUserSendInput( UINT count, INPUT *inputs, int size ) */ BOOL WINAPI NtUserSetCursorPos( INT x, INT y ) { - RECT rect = {x, y, x, y}; + POINT pt = {x, y}; BOOL ret; INT prev_x, prev_y, new_x, new_y;
- rect = map_rect_virt_to_raw( rect, get_thread_dpi() ); + pt = map_point_virt_to_raw( pt, get_thread_dpi() ); SERVER_START_REQ( set_cursor ) { req->flags = SET_CURSOR_POS; - req->x = rect.left; - req->y = rect.top; + req->x = pt.x; + req->y = pt.y; if ((ret = !wine_server_call( req ))) { prev_x = reply->prev_x; @@ -739,7 +739,6 @@ BOOL WINAPI NtUserGetCursorPos( POINT *pt ) BOOL ret = TRUE; DWORD last_change = 0; NTSTATUS status; - RECT rect;
if (!pt) return FALSE;
@@ -755,9 +754,7 @@ BOOL WINAPI NtUserGetCursorPos( POINT *pt ) if (NtGetTickCount() - last_change > 100) ret = user_driver->pGetCursorPos( pt ); if (!ret) return FALSE;
- SetRect( &rect, pt->x, pt->y, pt->x, pt->y ); - rect = map_rect_raw_to_virt( rect, get_thread_dpi() ); - *pt = *(POINT *)&rect.left; + *pt = map_point_raw_to_virt( *pt, get_thread_dpi() ); return ret; }
diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index 9e8f9d54af8..ae56f3632ff 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -2771,7 +2771,6 @@ static BOOL process_hardware_message( MSG *msg, UINT hw_id, const struct hardwar HWND hwnd_filter, UINT first, UINT last, BOOL remove ) { struct ntuser_thread_info *thread_info = NtUserGetThreadInfo(); - RECT rect = {msg->pt.x, msg->pt.y, msg->pt.x, msg->pt.y}; UINT context; BOOL ret = FALSE;
@@ -2780,9 +2779,7 @@ static BOOL process_hardware_message( MSG *msg, UINT hw_id, const struct hardwar
/* hardware messages are always in raw physical coords */ context = set_thread_dpi_awareness_context( NTUSER_DPI_PER_MONITOR_AWARE ); - rect = map_rect_raw_to_virt( rect, get_thread_dpi() ); - msg->pt.x = rect.left; - msg->pt.y = rect.top; + msg->pt = map_point_raw_to_virt( msg->pt, get_thread_dpi() );
if (msg->message == WM_INPUT || msg->message == WM_INPUT_DEVICE_CHANGE) ret = process_rawinput_message( msg, hw_id, msg_data ); @@ -3007,12 +3004,9 @@ static int peek_message( MSG *msg, const struct peek_message_filter *filter ) } else if (info.msg.message == WH_MOUSE_LL && size >= sizeof(msg_data->hardware)) { - RECT rect = {info.msg.pt.x, info.msg.pt.y, info.msg.pt.x, info.msg.pt.y}; MSLLHOOKSTRUCT hook;
- rect = map_rect_raw_to_virt( rect, 0 ); - info.msg.pt.x = rect.left; - info.msg.pt.y = rect.top; + info.msg.pt = map_point_raw_to_virt( info.msg.pt, 0 );
hook.pt = info.msg.pt; hook.mouseData = info.msg.lParam; diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 13b49a9f12c..8f28c910ecd 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -1913,6 +1913,90 @@ static UINT monitor_get_dpi( struct monitor *monitor, MONITOR_DPI_TYPE type, UIN return min( *dpi_x, *dpi_y ); }
+struct monitor_map_info +{ + BOOL monitor_scaling; + float centre_from_x; + float centre_from_y; + float centre_to_x; + float centre_to_y; + float dpi_ratio_from; + float scale_ratio; + float dpi_ratio_to; +}; + +static POINT map_monitor_point( POINT pt, const struct monitor_map_info *info) +{ + float x = pt.x, y = pt.y; + + if (info->monitor_scaling) + { + x *= info->dpi_ratio_from; + y *= info->dpi_ratio_from; + x -= info->centre_from_x; + y -= info->centre_from_y; + x *= info->scale_ratio; + y *= info->scale_ratio; + x += info->centre_to_x; + y += info->centre_to_y; + } + x *= info->dpi_ratio_to; + y *= info->dpi_ratio_to; + + pt.x = x + 0.5f; + pt.y = y + 0.5f; + return pt; +} + +/* display_lock must be held */ +static void monitor_get_map_info( struct monitor *monitor, UINT dpi_from, MONITOR_DPI_TYPE type_from, + UINT dpi_to, MONITOR_DPI_TYPE type_to, struct monitor_map_info *info ) +{ + UINT dpi_x, dpi_y; + + assert( type_from != type_to ); + + if (monitor->source) + { + DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)}, *mode_from, *mode_to; + UINT num, den, dpi; + + source_get_current_settings( monitor->source, ¤t_mode ); + + dpi = monitor_get_dpi( monitor, MDT_DEFAULT, &dpi_x, &dpi_y ); + if (!dpi_from) dpi_from = dpi; + if (!dpi_to) dpi_to = dpi; + + if (type_from == MDT_RAW_DPI) + { + monitor_virt_to_raw_ratio( monitor, &den, &num ); + mode_from = &monitor->source->physical; + mode_to = ¤t_mode; + } + else + { + monitor_virt_to_raw_ratio( monitor, &num, &den ); + mode_from = ¤t_mode; + mode_to = &monitor->source->physical; + } + + info->monitor_scaling = TRUE; + info->dpi_ratio_from = (float)dpi / dpi_from; + info->centre_from_x = mode_from->dmPosition.x + mode_from->dmPelsWidth / 2.0f; + info->centre_from_y = mode_from->dmPosition.y + mode_from->dmPelsHeight / 2.0f; + info->scale_ratio = (float)num / den; + info->centre_to_x = mode_to->dmPosition.x + mode_to->dmPelsWidth / 2.0f; + info->centre_to_y = mode_to->dmPosition.y + mode_to->dmPelsHeight / 2.0f; + info->dpi_ratio_to = (float)dpi_to / dpi; + return; + } + + if (!dpi_from) dpi_from = monitor_get_dpi( monitor, type_from, &dpi_x, &dpi_y ); + if (!dpi_to) dpi_to = monitor_get_dpi( monitor, type_to, &dpi_x, &dpi_y ); + memset( info, 0, sizeof(*info)); + info->dpi_ratio_to = (float)dpi_to / dpi_from; +} + /* display_lock must be held */ static RECT map_monitor_rect( struct monitor *monitor, RECT rect, UINT dpi_from, MONITOR_DPI_TYPE type_from, UINT dpi_to, MONITOR_DPI_TYPE type_to ) @@ -2604,6 +2688,24 @@ static RECT monitors_get_union_rect( UINT dpi, MONITOR_DPI_TYPE type ) return rect; }
+/* map a monitor point from MDT_RAW_DPI to MDT_DEFAULT coordinates */ +POINT map_point_raw_to_virt( POINT pt, UINT dpi_to ) +{ + RECT pos = {pt.x, pt.y, pt.x, pt.y}; + struct monitor_map_info info; + struct monitor *monitor; + + if (!lock_display_devices( FALSE )) return pt; + if ((monitor = get_monitor_from_rect( pos, MONITOR_DEFAULTTONEAREST, 0, MDT_RAW_DPI ))) + { + monitor_get_map_info(monitor, 0, MDT_RAW_DPI, dpi_to, MDT_DEFAULT, &info); + pt = map_monitor_point( pt, &info ); + } + unlock_display_devices(); + + return pt; +} + /* map a monitor rect from MDT_RAW_DPI to MDT_DEFAULT coordinates */ RECT map_rect_raw_to_virt( RECT rect, UINT dpi_to ) { @@ -2618,6 +2720,24 @@ RECT map_rect_raw_to_virt( RECT rect, UINT dpi_to ) return rect; }
+/* map a monitor point from MDT_DEFAULT to MDT_RAW_DPI coordinates */ +POINT map_point_virt_to_raw( POINT pt, UINT dpi_from ) +{ + RECT pos = {pt.x, pt.y, pt.x, pt.y}; + struct monitor_map_info info; + struct monitor *monitor; + + if (!lock_display_devices( FALSE )) return pt; + if ((monitor = get_monitor_from_rect( pos, MONITOR_DEFAULTTONEAREST, dpi_from, MDT_DEFAULT ))) + { + monitor_get_map_info( monitor, dpi_from, MDT_DEFAULT, 0, MDT_RAW_DPI, &info ); + pt = map_monitor_point( pt, &info ); + } + unlock_display_devices(); + + return pt; +} + /* map a monitor rect from MDT_DEFAULT to MDT_RAW_DPI coordinates */ RECT map_rect_virt_to_raw( RECT rect, UINT dpi_from ) { diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index 4d4570f06f5..402cd0174c1 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -174,7 +174,9 @@ extern POINT map_dpi_point( POINT pt, UINT dpi_from, UINT dpi_to ); extern RECT map_dpi_rect( RECT rect, UINT dpi_from, UINT dpi_to ); extern HRGN map_dpi_region( HRGN region, UINT dpi_from, UINT dpi_to ); extern struct window_rects map_dpi_window_rects( struct window_rects rects, UINT dpi_from, UINT dpi_to ); +extern POINT map_point_raw_to_virt( POINT pt, UINT dpi_to ); extern RECT map_rect_raw_to_virt( RECT rect, UINT dpi_to ); +extern POINT map_point_virt_to_raw( POINT pt, UINT dpi_from ); extern RECT map_rect_virt_to_raw( RECT rect, UINT dpi_from ); extern struct window_rects map_window_rects_virt_to_raw( struct window_rects rects, UINT dpi_from ); extern POINT point_phys_to_win_dpi( HWND hwnd, POINT pt );
From: Conor McCarthy cmccarthy@codeweavers.com
To eliminate rounding errors, and for consistency with cursor mapping. --- dlls/win32u/sysparams.c | 52 +++++++++++------------------------------ 1 file changed, 13 insertions(+), 39 deletions(-)
diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 8f28c910ecd..34df99f1238 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -1997,50 +1997,24 @@ static void monitor_get_map_info( struct monitor *monitor, UINT dpi_from, MONITO info->dpi_ratio_to = (float)dpi_to / dpi_from; }
-/* display_lock must be held */ -static RECT map_monitor_rect( struct monitor *monitor, RECT rect, UINT dpi_from, MONITOR_DPI_TYPE type_from, - UINT dpi_to, MONITOR_DPI_TYPE type_to ) +static RECT map_monitor_rect_from_info( RECT rect, const struct monitor_map_info *info ) { - UINT x, y; - - assert( type_from != type_to ); + POINT *left_top = (POINT *)&rect.left, *right_bottom = (POINT *)&rect.right;
- if (monitor->source) - { - DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)}, *mode_from, *mode_to; - UINT num, den, dpi; + *left_top = map_monitor_point( *left_top, info ); + *right_bottom = map_monitor_point( *right_bottom, info );
- source_get_current_settings( monitor->source, ¤t_mode ); - - dpi = monitor_get_dpi( monitor, MDT_DEFAULT, &x, &y ); - if (!dpi_from) dpi_from = dpi; - if (!dpi_to) dpi_to = dpi; - - if (type_from == MDT_RAW_DPI) - { - monitor_virt_to_raw_ratio( monitor, &den, &num ); - mode_from = &monitor->source->physical; - mode_to = ¤t_mode; - } - else - { - monitor_virt_to_raw_ratio( monitor, &num, &den ); - mode_from = ¤t_mode; - mode_to = &monitor->source->physical; - } + return rect; +}
- rect = map_dpi_rect( rect, dpi_from, dpi * 2 ); - OffsetRect( &rect, -mode_from->dmPosition.x * 2 - mode_from->dmPelsWidth, - -mode_from->dmPosition.y * 2 - mode_from->dmPelsHeight ); - rect = map_dpi_rect( rect, den, num ); - OffsetRect( &rect, mode_to->dmPosition.x * 2 + mode_to->dmPelsWidth, - mode_to->dmPosition.y * 2 + mode_to->dmPelsHeight ); - return map_dpi_rect( rect, dpi * 2, dpi_to ); - } +/* display_lock must be held */ +static RECT map_monitor_rect( struct monitor *monitor, RECT rect, UINT dpi_from, MONITOR_DPI_TYPE type_from, + UINT dpi_to, MONITOR_DPI_TYPE type_to ) +{ + struct monitor_map_info info;
- if (!dpi_from) dpi_from = monitor_get_dpi( monitor, type_from, &x, &y ); - if (!dpi_to) dpi_to = monitor_get_dpi( monitor, type_to, &x, &y ); - return map_dpi_rect( rect, dpi_from, dpi_to ); + monitor_get_map_info( monitor, dpi_from, type_from, dpi_to, type_to, &info ); + return map_monitor_rect_from_info( rect, &info ); }
/* display_lock must be held */
From: Conor McCarthy cmccarthy@codeweavers.com
To avoid getting the same info three times. --- dlls/win32u/sysparams.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 34df99f1238..902516bd8bf 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -2736,9 +2736,12 @@ struct window_rects map_window_rects_virt_to_raw( struct window_rects rects, UIN if (!lock_display_devices( FALSE )) return rects; if ((monitor = get_monitor_from_rect( rects.window, MONITOR_DEFAULTTONEAREST, dpi_from, MDT_DEFAULT ))) { - rects.visible = map_monitor_rect( monitor, rects.visible, dpi_from, MDT_DEFAULT, 0, MDT_RAW_DPI ); - rects.window = map_monitor_rect( monitor, rects.window, dpi_from, MDT_DEFAULT, 0, MDT_RAW_DPI ); - rects.client = map_monitor_rect( monitor, rects.client, dpi_from, MDT_DEFAULT, 0, MDT_RAW_DPI ); + struct monitor_map_info info; + + monitor_get_map_info( monitor, dpi_from, MDT_DEFAULT, 0, MDT_RAW_DPI, &info ); + rects.visible = map_monitor_rect_from_info( rects.visible, &info ); + rects.window = map_monitor_rect_from_info( rects.window, &info ); + rects.client = map_monitor_rect_from_info( rects.client, &info ); } /* if the visible rect is fullscreen, make it cover the full raw monitor, regardless of aspect ratio */ LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)