Atlas - SDL_windowsmouse.c
Home / ext / SDL / src / video / windows Lines: 1 | Size: 26307 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "SDL_internal.h" 22 23#if defined(SDL_VIDEO_DRIVER_WINDOWS) && !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 24 25#include "SDL_windowsvideo.h" 26#include "SDL_windowsevents.h" 27#include "SDL_windowsrawinput.h" 28 29#include "../SDL_video_c.h" 30#include "../../events/SDL_mouse_c.h" 31#include "../../joystick/usb_ids.h" 32#include "../../core/windows/SDL_windows.h" // for checking windows version 33 34#define RIFF_FOURCC(c0, c1, c2, c3) \ 35 ((DWORD)(BYTE)(c0) | ((DWORD)(BYTE)(c1) << 8) | \ 36 ((DWORD)(BYTE)(c2) << 16) | ((DWORD)(BYTE)(c3) << 24)) 37 38#define ANI_FLAG_ICON 0x1 39 40#pragma pack(push, 1) 41 42typedef struct 43{ 44 BYTE bWidth; 45 BYTE bHeight; 46 BYTE bColorCount; 47 BYTE bReserved; 48 WORD xHotspot; 49 WORD yHotspot; 50 DWORD dwImageSize; 51 DWORD dwImageOffset; 52} CURSORICONFILEDIRENTRY; 53 54typedef struct 55{ 56 WORD idReserved; 57 WORD idType; 58 WORD idCount; 59} CURSORICONFILEDIR; 60 61typedef struct 62{ 63 DWORD cbSizeof; // sizeof(ANIHEADER) = 36 bytes. 64 DWORD frames; // Number of frames in the frame list. 65 DWORD steps; // Number of steps in the animation loop. 66 DWORD width; // Width 67 DWORD height; // Height 68 DWORD bpp; // bpp 69 DWORD planes; // Not used 70 DWORD jifRate; // Default display rate, in jiffies (1/60s) 71 DWORD fl; // AF_ICON should be set. AF_SEQUENCE is optional 72} ANIHEADER; 73 74#pragma pack(pop) 75 76typedef struct CachedCursor 77{ 78 float scale; 79 HCURSOR cursor; 80 struct CachedCursor *next; 81} CachedCursor; 82 83struct SDL_CursorData 84{ 85 HCURSOR cursor; 86 87 CachedCursor *cache; 88 int hot_x; 89 int hot_y; 90 int num_frames; 91 SDL_CursorFrameInfo frames[1]; 92}; 93 94typedef struct 95{ 96 Uint64 xs[5]; 97 Uint64 ys[5]; 98 Sint64 residual[2]; 99 Uint32 dpiscale; 100 Uint32 dpidenom; 101 int last_node; 102 bool enhanced; 103 bool dpiaware; 104} WIN_MouseData; 105 106DWORD SDL_last_warp_time = 0; 107HCURSOR SDL_cursor = NULL; 108static SDL_Cursor *SDL_blank_cursor = NULL; 109static WIN_MouseData WIN_system_scale_data; 110 111static SDL_Cursor *WIN_CreateCursorAndData(HCURSOR hcursor) 112{ 113 if (!hcursor) { 114 return NULL; 115 } 116 117 SDL_Cursor *cursor = (SDL_Cursor *)SDL_calloc(1, sizeof(*cursor)); 118 if (!cursor) { 119 return NULL; 120 } 121 122 SDL_CursorData *data = (SDL_CursorData *)SDL_calloc(1, sizeof(*data)); 123 if (!data) { 124 SDL_free(cursor); 125 return NULL; 126 } 127 128 data->cursor = hcursor; 129 cursor->internal = data; 130 return cursor; 131} 132 133static SDL_Cursor *WIN_CreateAnimatedCursorAndData(SDL_CursorFrameInfo *frames, int frame_count, int hot_x, int hot_y) 134{ 135 // Dynamically generate cursors at the appropriate DPI 136 SDL_Cursor *cursor = (SDL_Cursor *)SDL_calloc(1, sizeof(*cursor)); 137 if (!cursor) { 138 return NULL; 139 } 140 141 SDL_CursorData *data = (SDL_CursorData *)SDL_calloc(1, sizeof(*data) + (sizeof(SDL_CursorFrameInfo) * (frame_count - 1))); 142 if (!data) { 143 SDL_free(cursor); 144 return NULL; 145 } 146 147 data->hot_x = hot_x; 148 data->hot_y = hot_y; 149 data->num_frames = frame_count; 150 for (int i = 0; i < frame_count; ++i) { 151 data->frames[i].surface = frames[i].surface; 152 data->frames[i].duration = frames[i].duration; 153 ++frames[i].surface->refcount; 154 } 155 cursor->internal = data; 156 return cursor; 157} 158 159static bool SaveChunkSize(SDL_IOStream* dst, Sint64 offset) 160{ 161 Sint64 here = SDL_TellIO(dst); 162 if (here < 0) { 163 return false; 164 } 165 if (SDL_SeekIO(dst, offset, SDL_IO_SEEK_SET) < 0) { 166 return false; 167 } 168 169 DWORD size = (DWORD)(here - (offset + sizeof(DWORD))); 170 if (!SDL_WriteU32LE(dst, size)) { 171 return false; 172 } 173 return SDL_SeekIO(dst, here, SDL_IO_SEEK_SET); 174} 175 176static bool FillIconEntry(CURSORICONFILEDIRENTRY *entry, SDL_Surface *surface, int hot_x, int hot_y, DWORD dwImageSize, DWORD dwImageOffset) 177{ 178 if (surface->props) { 179 hot_x = (int)SDL_GetNumberProperty(surface->props, SDL_PROP_SURFACE_HOTSPOT_X_NUMBER, hot_x); 180 hot_y = (int)SDL_GetNumberProperty(surface->props, SDL_PROP_SURFACE_HOTSPOT_Y_NUMBER, hot_y); 181 } 182 hot_x = SDL_clamp(hot_x, 0, surface->w - 1); 183 hot_y = SDL_clamp(hot_y, 0, surface->h - 1); 184 185 SDL_zerop(entry); 186 entry->bWidth = surface->w < 256 ? surface->w : 0; // 0 means a width of 256 187 entry->bHeight = surface->h < 256 ? surface->h : 0; // 0 means a height of 256 188 entry->xHotspot = hot_x; 189 entry->yHotspot = hot_y; 190 entry->dwImageSize = dwImageSize; 191 entry->dwImageOffset = dwImageOffset; 192 return true; 193} 194 195#ifdef SAVE_ICON_PNG 196 197static bool WriteIconSurface(SDL_IOStream *dst, SDL_Surface *surface) 198{ 199 if (!SDL_SavePNG_IO(surface, dst, false)) { 200 return false; 201 } 202 203 // Image data offsets must be WORD aligned 204 Sint64 offset = SDL_TellIO(dst); 205 if (offset & 1) { 206 if (!SDL_WriteU8(dst, 0)) { 207 return false; 208 } 209 } 210 return true; 211} 212 213#else 214 215/* For info on the expected mask format see: 216 * https://devblogs.microsoft.com/oldnewthing/20101018-00/?p=12513 217 */ 218static void *CreateIconMask(SDL_Surface *surface, size_t *mask_size) 219{ 220 Uint8 *dst; 221 const int pitch = ((surface->w + 15) & ~15) / 8; 222 const size_t size = pitch * surface->h; 223 static const unsigned char masks[] = { 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1 }; 224 225 void *mask = SDL_malloc(size); 226 if (!mask) { 227 return NULL; 228 } 229 230 dst = (Uint8 *)mask; 231 232 // Make the mask completely transparent. 233 SDL_memset(dst, 0xff, size); 234 for (int y = surface->h - 1; y >= 0; --y, dst += pitch) { 235 for (int x = 0; x < surface->w; ++x) { 236 Uint8 r, g, b, a; 237 SDL_ReadSurfacePixel(surface, x, y, &r, &g, &b, &a); 238 239 if (a != 0) { 240 // Reset bit of an opaque pixel. 241 dst[x >> 3] &= ~masks[x & 7]; 242 } 243 } 244 } 245 *mask_size = size; 246 return mask; 247} 248 249static bool WriteIconSurface(SDL_IOStream *dst, SDL_Surface *surface) 250{ 251 SDL_Surface *temp = NULL; 252 253 if (surface->format != SDL_PIXELFORMAT_ARGB8888) { 254 temp = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888); 255 if (!temp) { 256 return false; 257 } 258 surface = temp; 259 } 260 261 // Cursor data is double height (DIB and mask), stored bottom-up 262 bool ok = true; 263 size_t mask_size = 0; 264 void *mask = CreateIconMask(surface, &mask_size); 265 if (!mask) { 266 ok = false; 267 goto done; 268 } 269 270 BITMAPINFOHEADER bmih; 271 SDL_zero(bmih); 272 DWORD row_size = surface->w * 4; 273 bmih.biSize = sizeof(BITMAPINFOHEADER); 274 bmih.biWidth = surface->w; 275 bmih.biHeight = surface->h * 2; 276 bmih.biPlanes = 1; 277 bmih.biBitCount = 32; 278 bmih.biCompression = BI_RGB; 279 bmih.biSizeImage = (DWORD)(surface->h * row_size + mask_size); 280 ok &= (SDL_WriteIO(dst, &bmih, sizeof(bmih)) == sizeof(bmih)); 281 282 const Uint8 *pix = surface->pixels; 283 pix += (surface->h - 1) * surface->pitch; 284 for (int i = 0; i < surface->h; ++i) { 285 ok &= (SDL_WriteIO(dst, pix, row_size) == row_size); 286 pix -= surface->pitch; 287 } 288 ok &= (SDL_WriteIO(dst, mask, mask_size) == mask_size); 289 290done: 291 SDL_free(mask); 292 SDL_DestroySurface(temp); 293 return ok; 294} 295 296#endif // SAVE_ICON_PNG 297 298static bool WriteIconFrame(SDL_IOStream *dst, SDL_Surface *surface, int hot_x, int hot_y, float scale) 299{ 300#ifdef SAVE_MULTIPLE_ICONS 301 int count = 0; 302 SDL_Surface **surfaces = SDL_GetSurfaceImages(surface, &count); 303 if (!surfaces) { 304 return false; 305 } 306#else 307 surface = SDL_GetSurfaceImage(surface, scale); 308 if (!surface) { 309 return false; 310 } 311 312 int count = 1; 313 SDL_Surface **surfaces = &surface; 314#endif 315 316 // Raymond Chen has more insight into this format at: 317 // https://devblogs.microsoft.com/oldnewthing/20101018-00/?p=12513 318 bool ok = true; 319 ok &= SDL_WriteU32LE(dst, RIFF_FOURCC('i', 'c', 'o', 'n')); 320 Sint64 icon_size_offset = SDL_TellIO(dst); 321 ok &= SDL_WriteU32LE(dst, 0); 322 Sint64 base_offset = icon_size_offset + sizeof(DWORD); 323 324 CURSORICONFILEDIR dir; 325 dir.idReserved = 0; 326 dir.idType = 2; // Cursor 327 dir.idCount = count; 328 ok &= (SDL_WriteIO(dst, &dir, sizeof(dir)) == sizeof(dir)); 329 330 DWORD entries_size = count * sizeof(CURSORICONFILEDIRENTRY); 331 CURSORICONFILEDIRENTRY *entries = (CURSORICONFILEDIRENTRY *)SDL_malloc(entries_size); 332 if (!entries) { 333 ok = false; 334 goto done; 335 } 336 ok &= (SDL_WriteIO(dst, entries, entries_size) == entries_size); 337 338 Sint64 image_offset = SDL_TellIO(dst); 339 for (int i = 0; i < count; ++i) { 340 ok &= WriteIconSurface(dst, surfaces[i]); 341 342 Sint64 next_offset = SDL_TellIO(dst); 343 DWORD dwImageSize = (DWORD)(next_offset - image_offset); 344 DWORD dwImageOffset = (DWORD)(image_offset - base_offset); 345 346 ok &= FillIconEntry(&entries[i], surfaces[i], hot_x, hot_y, dwImageSize, dwImageOffset); 347 348 image_offset = next_offset; 349 } 350 351 // Now that we have the icon entries filled out, rewrite them 352 ok &= (SDL_SeekIO(dst, base_offset + sizeof(dir), SDL_IO_SEEK_SET) >= 0); 353 ok &= (SDL_WriteIO(dst, entries, entries_size) == entries_size); 354 ok &= (SDL_SeekIO(dst, image_offset, SDL_IO_SEEK_SET) >= 0); 355 SDL_free(entries); 356 357 ok &= SaveChunkSize(dst, icon_size_offset); 358 359done: 360#ifdef SAVE_MULTIPLE_ICONS 361 SDL_free(surfaces); 362#else 363 SDL_DestroySurface(surface); 364#endif 365 return ok; 366} 367 368/* Windows doesn't have an API to easily create animated cursors from a sequence of images, 369 * so we have to build an animated cursor resource file in memory and load it. 370 */ 371static HCURSOR WIN_CreateAnimatedCursorInternal(SDL_CursorFrameInfo *frames, int frame_count, int hot_x, int hot_y, float scale) 372{ 373 HCURSOR hcursor = NULL; 374 SDL_IOStream *dst = SDL_IOFromDynamicMem(); 375 if (!dst) { 376 return NULL; 377 } 378 379 int w = (int)SDL_roundf(frames[0].surface->w * scale); 380 int h = (int)SDL_roundf(frames[0].surface->h * scale); 381 382 bool ok = true; 383 // RIFF header 384 ok &= SDL_WriteU32LE(dst, RIFF_FOURCC('R', 'I', 'F', 'F')); 385 Sint64 riff_size_offset = SDL_TellIO(dst); 386 ok &= SDL_WriteU32LE(dst, 0); 387 ok &= SDL_WriteU32LE(dst, RIFF_FOURCC('A', 'C', 'O', 'N')); 388 389 // anih header chunk 390 ok &= SDL_WriteU32LE(dst, RIFF_FOURCC('a', 'n', 'i', 'h')); 391 ok &= SDL_WriteU32LE(dst, sizeof(ANIHEADER)); 392 393 ANIHEADER anih; 394 SDL_zero(anih); 395 anih.cbSizeof = sizeof(anih); 396 anih.frames = frame_count; 397 anih.steps = frame_count; 398 anih.jifRate = 1; 399 anih.fl = ANI_FLAG_ICON; 400 ok &= (SDL_WriteIO(dst, &anih, sizeof(anih)) == sizeof(anih)); 401 402 // Rate chunk 403 ok &= SDL_WriteU32LE(dst, RIFF_FOURCC('r', 'a', 't', 'e')); 404 ok &= SDL_WriteU32LE(dst, sizeof(DWORD) * frame_count); 405 for (int i = 0; i < frame_count; ++i) { 406 // Animated Win32 cursors are in jiffy units, and one jiffy is 1/60 of a second. 407 const double WIN32_JIFFY = 1000.0 / 60.0; 408 DWORD duration = (frames[i].duration ? SDL_lround(frames[i].duration / WIN32_JIFFY) : 0xFFFFFFFF); 409 ok &= SDL_WriteU32LE(dst, duration); 410 } 411 412 // Frame list 413 ok &= SDL_WriteU32LE(dst, RIFF_FOURCC('L', 'I', 'S', 'T')); 414 Sint64 frame_list_size_offset = SDL_TellIO(dst); 415 ok &= SDL_WriteU32LE(dst, 0); 416 ok &= SDL_WriteU32LE(dst, RIFF_FOURCC('f', 'r', 'a', 'm')); 417 418 for (int i = 0; i < frame_count; ++i) { 419 ok &= WriteIconFrame(dst, frames[i].surface, hot_x, hot_y, scale); 420 } 421 ok &= SaveChunkSize(dst, frame_list_size_offset); 422 423 // All done! 424 ok &= SaveChunkSize(dst, riff_size_offset); 425 if (!ok) { 426 // The error has been set above 427 goto done; 428 } 429 430 BYTE *mem = (BYTE *)SDL_GetPointerProperty(SDL_GetIOProperties(dst), SDL_PROP_IOSTREAM_DYNAMIC_MEMORY_POINTER, NULL); 431 DWORD size = (DWORD)SDL_GetIOSize(dst); 432 hcursor = (HCURSOR)CreateIconFromResourceEx(mem, size, FALSE, 0x00030000, w, h, 0); 433 if (!hcursor) { 434 SDL_SetError("CreateIconFromResource failed"); 435 } 436 437done: 438 SDL_CloseIO(dst); 439 440 return hcursor; 441} 442 443static SDL_Cursor *WIN_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y) 444{ 445 SDL_CursorFrameInfo frame = { surface, 0 }; 446 return WIN_CreateAnimatedCursorAndData(&frame, 1, hot_x, hot_y); 447} 448 449static SDL_Cursor *WIN_CreateAnimatedCursor(SDL_CursorFrameInfo *frames, int frame_count, int hot_x, int hot_y) 450{ 451 return WIN_CreateAnimatedCursorAndData(frames, frame_count, hot_x, hot_y); 452} 453 454static SDL_Cursor *WIN_CreateBlankCursor(void) 455{ 456 SDL_Cursor *cursor = NULL; 457 SDL_Surface *surface = SDL_CreateSurface(32, 32, SDL_PIXELFORMAT_ARGB8888); 458 if (surface) { 459 cursor = WIN_CreateCursor(surface, 0, 0); 460 SDL_DestroySurface(surface); 461 } 462 return cursor; 463} 464 465static SDL_Cursor *WIN_CreateSystemCursor(SDL_SystemCursor id) 466{ 467 LPCTSTR name; 468 469 switch (id) { 470 default: 471 SDL_assert(!"Unknown system cursor ID"); 472 return NULL; 473 case SDL_SYSTEM_CURSOR_DEFAULT: 474 name = IDC_ARROW; 475 break; 476 case SDL_SYSTEM_CURSOR_TEXT: 477 name = IDC_IBEAM; 478 break; 479 case SDL_SYSTEM_CURSOR_WAIT: 480 name = IDC_WAIT; 481 break; 482 case SDL_SYSTEM_CURSOR_CROSSHAIR: 483 name = IDC_CROSS; 484 break; 485 case SDL_SYSTEM_CURSOR_PROGRESS: 486 name = IDC_APPSTARTING; 487 break; 488 case SDL_SYSTEM_CURSOR_NWSE_RESIZE: 489 name = IDC_SIZENWSE; 490 break; 491 case SDL_SYSTEM_CURSOR_NESW_RESIZE: 492 name = IDC_SIZENESW; 493 break; 494 case SDL_SYSTEM_CURSOR_EW_RESIZE: 495 name = IDC_SIZEWE; 496 break; 497 case SDL_SYSTEM_CURSOR_NS_RESIZE: 498 name = IDC_SIZENS; 499 break; 500 case SDL_SYSTEM_CURSOR_MOVE: 501 name = IDC_SIZEALL; 502 break; 503 case SDL_SYSTEM_CURSOR_NOT_ALLOWED: 504 name = IDC_NO; 505 break; 506 case SDL_SYSTEM_CURSOR_POINTER: 507 name = IDC_HAND; 508 break; 509 case SDL_SYSTEM_CURSOR_NW_RESIZE: 510 name = IDC_SIZENWSE; 511 break; 512 case SDL_SYSTEM_CURSOR_N_RESIZE: 513 name = IDC_SIZENS; 514 break; 515 case SDL_SYSTEM_CURSOR_NE_RESIZE: 516 name = IDC_SIZENESW; 517 break; 518 case SDL_SYSTEM_CURSOR_E_RESIZE: 519 name = IDC_SIZEWE; 520 break; 521 case SDL_SYSTEM_CURSOR_SE_RESIZE: 522 name = IDC_SIZENWSE; 523 break; 524 case SDL_SYSTEM_CURSOR_S_RESIZE: 525 name = IDC_SIZENS; 526 break; 527 case SDL_SYSTEM_CURSOR_SW_RESIZE: 528 name = IDC_SIZENESW; 529 break; 530 case SDL_SYSTEM_CURSOR_W_RESIZE: 531 name = IDC_SIZEWE; 532 break; 533 } 534 return WIN_CreateCursorAndData(LoadCursor(NULL, name)); 535} 536 537static SDL_Cursor *WIN_CreateDefaultCursor(void) 538{ 539 SDL_SystemCursor id = SDL_GetDefaultSystemCursor(); 540 return WIN_CreateSystemCursor(id); 541} 542 543static void WIN_FreeCursor(SDL_Cursor *cursor) 544{ 545 SDL_CursorData *data = cursor->internal; 546 547 for (int i = 0; i < data->num_frames; ++i) { 548 SDL_DestroySurface(data->frames[i].surface); 549 } 550 while (data->cache) { 551 CachedCursor *entry = data->cache; 552 data->cache = entry->next; 553 if (entry->cursor) { 554 DestroyCursor(entry->cursor); 555 } 556 SDL_free(entry); 557 } 558 if (data->cursor) { 559 DestroyCursor(data->cursor); 560 } 561 SDL_free(data); 562 SDL_free(cursor); 563} 564 565static HCURSOR GetCachedCursor(SDL_Cursor *cursor) 566{ 567 SDL_CursorData *data = cursor->internal; 568 569 float scale = 1.0f; 570 if (SDL_GetHintBoolean(SDL_HINT_MOUSE_DPI_SCALE_CURSORS, false)) { 571 scale = SDL_GetDisplayContentScale(SDL_GetDisplayForWindow(SDL_GetMouseFocus())); 572 if (scale == 0.0f) { 573 scale = 1.0f; 574 } 575 } 576 for (CachedCursor *entry = data->cache; entry; entry = entry->next) { 577 if (scale == entry->scale) { 578 return entry->cursor; 579 } 580 } 581 582 // Need to create a cursor for this content scale 583 HCURSOR hcursor = WIN_CreateAnimatedCursorInternal(data->frames, data->num_frames, data->hot_x, data->hot_y, scale); 584 if (!hcursor) { 585 return NULL; 586 } 587 588 CachedCursor *entry = (CachedCursor *)SDL_calloc(1, sizeof(*entry)); 589 if (!entry) { 590 DestroyCursor(hcursor); 591 return NULL; 592 } 593 entry->cursor = hcursor; 594 entry->scale = scale; 595 entry->next = data->cache; 596 data->cache = entry; 597 598 return hcursor; 599} 600 601static bool WIN_ShowCursor(SDL_Cursor *cursor) 602{ 603 if (!cursor) { 604 if (GetSystemMetrics(SM_REMOTESESSION)) { 605 // Use a blank cursor so we continue to get relative motion over RDP 606 cursor = SDL_blank_cursor; 607 } 608 } 609 if (cursor) { 610 SDL_CursorData *data = cursor->internal; 611 if (data->num_frames > 0) { 612 SDL_cursor = GetCachedCursor(cursor); 613 } else { 614 SDL_cursor = data->cursor; 615 } 616 } else { 617 SDL_cursor = NULL; 618 } 619 if (SDL_GetMouseFocus() != NULL) { 620 SetCursor(SDL_cursor); 621 } 622 return true; 623} 624 625void WIN_SetCursorPos(int x, int y) 626{ 627 // We need to jitter the value because otherwise Windows will occasionally inexplicably ignore the SetCursorPos() or SendInput() 628 SetCursorPos(x, y); 629 SetCursorPos(x + 1, y); 630 SetCursorPos(x, y); 631 632 // Flush any mouse motion prior to or associated with this warp 633#ifdef _MSC_VER // We explicitly want to use GetTickCount(), not GetTickCount64() 634#pragma warning(push) 635#pragma warning(disable : 28159) 636#endif 637 SDL_last_warp_time = GetTickCount(); 638 if (!SDL_last_warp_time) { 639 SDL_last_warp_time = 1; 640 } 641#ifdef _MSC_VER 642#pragma warning(pop) 643#endif 644} 645 646static bool WIN_WarpMouse(SDL_Window *window, float x, float y) 647{ 648 SDL_WindowData *data = window->internal; 649 HWND hwnd = data->hwnd; 650 POINT pt; 651 652 // Don't warp the mouse while we're doing a modal interaction 653 if (data->in_title_click || data->focus_click_pending) { 654 return true; 655 } 656 657 pt.x = (int)SDL_roundf(x); 658 pt.y = (int)SDL_roundf(y); 659 ClientToScreen(hwnd, &pt); 660 WIN_SetCursorPos(pt.x, pt.y); 661 662 // Send the exact mouse motion associated with this warp 663 SDL_SendMouseMotion(0, window, SDL_GLOBAL_MOUSE_ID, false, x, y); 664 return true; 665} 666 667static bool WIN_WarpMouseGlobal(float x, float y) 668{ 669 POINT pt; 670 671 pt.x = (int)SDL_roundf(x); 672 pt.y = (int)SDL_roundf(y); 673 SetCursorPos(pt.x, pt.y); 674 return true; 675} 676 677static bool WIN_SetRelativeMouseMode(bool enabled) 678{ 679 return WIN_SetRawMouseEnabled(SDL_GetVideoDevice(), enabled); 680} 681 682static bool WIN_CaptureMouse(SDL_Window *window) 683{ 684 if (window) { 685 SDL_WindowData *data = window->internal; 686 SetCapture(data->hwnd); 687 } else { 688 SDL_Window *focus_window = SDL_GetMouseFocus(); 689 690 if (focus_window) { 691 SDL_WindowData *data = focus_window->internal; 692 if (!data->mouse_tracked) { 693 SDL_SetMouseFocus(NULL); 694 } 695 } 696 ReleaseCapture(); 697 } 698 699 return true; 700} 701 702static SDL_MouseButtonFlags WIN_GetGlobalMouseState(float *x, float *y) 703{ 704 SDL_MouseButtonFlags result = 0; 705 POINT pt = { 0, 0 }; 706 bool swapButtons = GetSystemMetrics(SM_SWAPBUTTON) != 0; 707 708 GetCursorPos(&pt); 709 *x = (float)pt.x; 710 *y = (float)pt.y; 711 712 result |= GetAsyncKeyState(!swapButtons ? VK_LBUTTON : VK_RBUTTON) & 0x8000 ? SDL_BUTTON_LMASK : 0; 713 result |= GetAsyncKeyState(!swapButtons ? VK_RBUTTON : VK_LBUTTON) & 0x8000 ? SDL_BUTTON_RMASK : 0; 714 result |= GetAsyncKeyState(VK_MBUTTON) & 0x8000 ? SDL_BUTTON_MMASK : 0; 715 result |= GetAsyncKeyState(VK_XBUTTON1) & 0x8000 ? SDL_BUTTON_X1MASK : 0; 716 result |= GetAsyncKeyState(VK_XBUTTON2) & 0x8000 ? SDL_BUTTON_X2MASK : 0; 717 718 return result; 719} 720 721static void WIN_ApplySystemScale(void *internal, Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, float *x, float *y) 722{ 723 if (!internal) { 724 return; 725 } 726 WIN_MouseData *data = (WIN_MouseData *)internal; 727 728 SDL_VideoDisplay *display = window ? SDL_GetVideoDisplayForWindow(window) : SDL_GetVideoDisplay(SDL_GetPrimaryDisplay()); 729 730 Sint64 ix = (Sint64)*x * 65536; 731 Sint64 iy = (Sint64)*y * 65536; 732 Uint32 dpi = display ? (Uint32)(display->content_scale * USER_DEFAULT_SCREEN_DPI) : USER_DEFAULT_SCREEN_DPI; 733 734 if (!data->enhanced) { // early return if flat scale 735 dpi = data->dpiscale * (data->dpiaware ? dpi : USER_DEFAULT_SCREEN_DPI); 736 ix *= dpi; 737 iy *= dpi; 738 ix /= USER_DEFAULT_SCREEN_DPI; 739 iy /= USER_DEFAULT_SCREEN_DPI; 740 ix /= 32; 741 iy /= 32; 742 // data->residual[0] += ix; 743 // data->residual[1] += iy; 744 // ix = 65536 * (data->residual[0] / 65536); 745 // iy = 65536 * (data->residual[1] / 65536); 746 // data->residual[0] -= ix; 747 // data->residual[1] -= iy; 748 *x = (float)ix / 65536.0f; 749 *y = (float)iy / 65536.0f; 750 return; 751 } 752 753 Uint64 *xs = data->xs; 754 Uint64 *ys = data->ys; 755 Uint64 absx = SDL_abs(ix); 756 Uint64 absy = SDL_abs(iy); 757 Uint64 speed = SDL_min(absx, absy) + (SDL_max(absx, absy) << 1); // super cursed approximation used by Windows 758 if (speed == 0) { 759 return; 760 } 761 762 int i, j, k; 763 for (i = 1; i < 5; i++) { 764 j = i; 765 if (speed < xs[j]) { 766 break; 767 } 768 } 769 i -= 1; 770 j -= 1; 771 k = data->last_node; 772 data->last_node = j; 773 774 Uint32 denom = data->dpidenom; 775 Sint64 scale = 0; 776 Sint64 xdiff = xs[j+1] - xs[j]; 777 Sint64 ydiff = ys[j+1] - ys[j]; 778 if (xdiff != 0) { 779 Sint64 slope = ydiff / xdiff; 780 Sint64 inter = slope * xs[i] - ys[i]; 781 scale += slope - inter / speed; 782 } 783 784 if (j > k) { 785 denom <<= 1; 786 xdiff = xs[k+1] - xs[k]; 787 ydiff = ys[k+1] - ys[k]; 788 if (xdiff != 0) { 789 Sint64 slope = ydiff / xdiff; 790 Sint64 inter = slope * xs[k] - ys[k]; 791 scale += slope - inter / speed; 792 } 793 } 794 795 scale *= dpi; 796 ix *= scale; 797 iy *= scale; 798 ix /= denom; 799 iy /= denom; 800 // data->residual[0] += ix; 801 // data->residual[1] += iy; 802 // ix = 65536 * (data->residual[0] / 65536); 803 // iy = 65536 * (data->residual[1] / 65536); 804 // data->residual[0] -= ix; 805 // data->residual[1] -= iy; 806 *x = (float)ix / 65536.0f; 807 *y = (float)iy / 65536.0f; 808} 809 810void WIN_InitMouse(SDL_VideoDevice *_this) 811{ 812 SDL_Mouse *mouse = SDL_GetMouse(); 813 814 mouse->CreateCursor = WIN_CreateCursor; 815 mouse->CreateAnimatedCursor = WIN_CreateAnimatedCursor; 816 mouse->CreateSystemCursor = WIN_CreateSystemCursor; 817 mouse->ShowCursor = WIN_ShowCursor; 818 mouse->FreeCursor = WIN_FreeCursor; 819 mouse->WarpMouse = WIN_WarpMouse; 820 mouse->WarpMouseGlobal = WIN_WarpMouseGlobal; 821 mouse->SetRelativeMouseMode = WIN_SetRelativeMouseMode; 822 mouse->CaptureMouse = WIN_CaptureMouse; 823 mouse->GetGlobalMouseState = WIN_GetGlobalMouseState; 824 mouse->ApplySystemScale = WIN_ApplySystemScale; 825 mouse->system_scale_data = &WIN_system_scale_data; 826 827 SDL_SetDefaultCursor(WIN_CreateDefaultCursor()); 828 829 SDL_blank_cursor = WIN_CreateBlankCursor(); 830 831 WIN_UpdateMouseSystemScale(); 832} 833 834void WIN_QuitMouse(SDL_VideoDevice *_this) 835{ 836 if (SDL_blank_cursor) { 837 WIN_FreeCursor(SDL_blank_cursor); 838 SDL_blank_cursor = NULL; 839 } 840} 841 842static void ReadMouseCurve(int v, Uint64 xs[5], Uint64 ys[5]) 843{ 844 bool win8 = WIN_IsWindows8OrGreater(); 845 DWORD xbuff[10] = { 846 0x00000000, 0, 847 0x00006e15, 0, 848 0x00014000, 0, 849 0x0003dc29, 0, 850 0x00280000, 0 851 }; 852 DWORD ybuff[10] = { 853 0x00000000, 0, 854 win8 ? 0x000111fd : 0x00015eb8, 0, 855 win8 ? 0x00042400 : 0x00054ccd, 0, 856 win8 ? 0x0012fc00 : 0x00184ccd, 0, 857 win8 ? 0x01bbc000 : 0x02380000, 0 858 }; 859 DWORD xsize = sizeof(xbuff); 860 DWORD ysize = sizeof(ybuff); 861 HKEY open_handle; 862 if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Control Panel\\Mouse", 0, KEY_READ, &open_handle) == ERROR_SUCCESS) { 863 RegQueryValueExW(open_handle, L"SmoothMouseXCurve", NULL, NULL, (BYTE*)xbuff, &xsize); 864 RegQueryValueExW(open_handle, L"SmoothMouseYCurve", NULL, NULL, (BYTE*)ybuff, &ysize); 865 RegCloseKey(open_handle); 866 } 867 xs[0] = 0; // first node must always be origin 868 ys[0] = 0; // first node must always be origin 869 int i; 870 for (i = 1; i < 5; i++) { 871 xs[i] = (7 * (Uint64)xbuff[i * 2]); 872 ys[i] = (v * (Uint64)ybuff[i * 2]) << 17; 873 } 874} 875 876void WIN_UpdateMouseSystemScale(void) 877{ 878 SDL_Mouse *mouse = SDL_GetMouse(); 879 880 if (mouse->ApplySystemScale == WIN_ApplySystemScale) { 881 mouse->system_scale_data = &WIN_system_scale_data; 882 } 883 884 // always reinitialize to valid defaults, whether fetch was successful or not. 885 WIN_MouseData *data = &WIN_system_scale_data; 886 data->residual[0] = 0; 887 data->residual[1] = 0; 888 data->dpiscale = 32; 889 data->dpidenom = (10 * (WIN_IsWindows8OrGreater() ? 120 : 150)) << 16; 890 data->dpiaware = WIN_IsPerMonitorV2DPIAware(SDL_GetVideoDevice()); 891 data->enhanced = false; 892 893 int v = 10; 894 if (SystemParametersInfo(SPI_GETMOUSESPEED, 0, &v, 0)) { 895 v = SDL_max(1, SDL_min(v, 20)); 896 data->dpiscale = SDL_max(SDL_max(v, (v - 2) * 4), (v - 6) * 8); 897 } 898 899 int params[3]; 900 if (SystemParametersInfo(SPI_GETMOUSE, 0, ¶ms, 0)) { 901 data->enhanced = params[2] ? true : false; 902 if (params[2]) { 903 ReadMouseCurve(v, data->xs, data->ys); 904 } 905 } 906} 907 908#endif // SDL_VIDEO_DRIVER_WINDOWS 909[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.