Atlas - SDL_windowsmouse.c

Home / ext / SDL / src / video / windows Lines: 1 | Size: 26382 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 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 w = (surface->w + 7) / 8; 222 const int pad = (((w) % 4) ? (4 - ((w) % 4)) : 0); 223 const int pitch = (w + pad); 224 const size_t size = pitch * surface->h; 225 static const unsigned char masks[] = { 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1 }; 226 227 void *mask = SDL_malloc(size); 228 if (!mask) { 229 return NULL; 230 } 231 232 dst = (Uint8 *)mask; 233 234 // Make the mask completely transparent. 235 SDL_memset(dst, 0xff, size); 236 for (int y = surface->h - 1; y >= 0; --y, dst += pitch) { 237 for (int x = 0; x < surface->w; ++x) { 238 Uint8 r, g, b, a; 239 SDL_ReadSurfacePixel(surface, x, y, &r, &g, &b, &a); 240 241 if (a != 0) { 242 // Reset bit of an opaque pixel. 243 dst[x >> 3] &= ~masks[x & 7]; 244 } 245 } 246 } 247 *mask_size = size; 248 return mask; 249} 250 251static bool WriteIconSurface(SDL_IOStream *dst, SDL_Surface *surface) 252{ 253 SDL_Surface *temp = NULL; 254 255 if (surface->format != SDL_PIXELFORMAT_ARGB8888) { 256 temp = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888); 257 if (!temp) { 258 return false; 259 } 260 surface = temp; 261 } 262 263 // Cursor data is double height (DIB and mask), stored bottom-up 264 bool ok = true; 265 size_t mask_size = 0; 266 void *mask = CreateIconMask(surface, &mask_size); 267 if (!mask) { 268 ok = false; 269 goto done; 270 } 271 272 BITMAPINFOHEADER bmih; 273 SDL_zero(bmih); 274 DWORD row_size = surface->w * 4; 275 bmih.biSize = sizeof(BITMAPINFOHEADER); 276 bmih.biWidth = surface->w; 277 bmih.biHeight = surface->h * 2; 278 bmih.biPlanes = 1; 279 bmih.biBitCount = 32; 280 bmih.biCompression = BI_RGB; 281 bmih.biSizeImage = (DWORD)(surface->h * row_size + mask_size); 282 ok &= (SDL_WriteIO(dst, &bmih, sizeof(bmih)) == sizeof(bmih)); 283 284 const Uint8 *pix = surface->pixels; 285 pix += (surface->h - 1) * surface->pitch; 286 for (int i = 0; i < surface->h; ++i) { 287 ok &= (SDL_WriteIO(dst, pix, row_size) == row_size); 288 pix -= surface->pitch; 289 } 290 ok &= (SDL_WriteIO(dst, mask, mask_size) == mask_size); 291 292done: 293 SDL_free(mask); 294 SDL_DestroySurface(temp); 295 return ok; 296} 297 298#endif // SAVE_ICON_PNG 299 300static bool WriteIconFrame(SDL_IOStream *dst, SDL_Surface *surface, int hot_x, int hot_y, float scale) 301{ 302#ifdef SAVE_MULTIPLE_ICONS 303 int count = 0; 304 SDL_Surface **surfaces = SDL_GetSurfaceImages(surface, &count); 305 if (!surfaces) { 306 return false; 307 } 308#else 309 surface = SDL_GetSurfaceImage(surface, scale); 310 if (!surface) { 311 return false; 312 } 313 314 int count = 1; 315 SDL_Surface **surfaces = &surface; 316#endif 317 318 // Raymond Chen has more insight into this format at: 319 // https://devblogs.microsoft.com/oldnewthing/20101018-00/?p=12513 320 bool ok = true; 321 ok &= SDL_WriteU32LE(dst, RIFF_FOURCC('i', 'c', 'o', 'n')); 322 Sint64 icon_size_offset = SDL_TellIO(dst); 323 ok &= SDL_WriteU32LE(dst, 0); 324 Sint64 base_offset = icon_size_offset + sizeof(DWORD); 325 326 CURSORICONFILEDIR dir; 327 dir.idReserved = 0; 328 dir.idType = 2; // Cursor 329 dir.idCount = count; 330 ok &= (SDL_WriteIO(dst, &dir, sizeof(dir)) == sizeof(dir)); 331 332 DWORD entries_size = count * sizeof(CURSORICONFILEDIRENTRY); 333 CURSORICONFILEDIRENTRY *entries = (CURSORICONFILEDIRENTRY *)SDL_malloc(entries_size); 334 if (!entries) { 335 ok = false; 336 goto done; 337 } 338 ok &= (SDL_WriteIO(dst, entries, entries_size) == entries_size); 339 340 Sint64 image_offset = SDL_TellIO(dst); 341 for (int i = 0; i < count; ++i) { 342 ok &= WriteIconSurface(dst, surfaces[i]); 343 344 Sint64 next_offset = SDL_TellIO(dst); 345 DWORD dwImageSize = (DWORD)(next_offset - image_offset); 346 DWORD dwImageOffset = (DWORD)(image_offset - base_offset); 347 348 ok &= FillIconEntry(&entries[i], surfaces[i], hot_x, hot_y, dwImageSize, dwImageOffset); 349 350 image_offset = next_offset; 351 } 352 353 // Now that we have the icon entries filled out, rewrite them 354 ok &= (SDL_SeekIO(dst, base_offset + sizeof(dir), SDL_IO_SEEK_SET) >= 0); 355 ok &= (SDL_WriteIO(dst, entries, entries_size) == entries_size); 356 ok &= (SDL_SeekIO(dst, image_offset, SDL_IO_SEEK_SET) >= 0); 357 SDL_free(entries); 358 359 ok &= SaveChunkSize(dst, icon_size_offset); 360 361done: 362#ifdef SAVE_MULTIPLE_ICONS 363 SDL_free(surfaces); 364#else 365 SDL_DestroySurface(surface); 366#endif 367 return ok; 368} 369 370/* Windows doesn't have an API to easily create animated cursors from a sequence of images, 371 * so we have to build an animated cursor resource file in memory and load it. 372 */ 373static HCURSOR WIN_CreateAnimatedCursorInternal(SDL_CursorFrameInfo *frames, int frame_count, int hot_x, int hot_y, float scale) 374{ 375 HCURSOR hcursor = NULL; 376 SDL_IOStream *dst = SDL_IOFromDynamicMem(); 377 if (!dst) { 378 return NULL; 379 } 380 381 int w = (int)SDL_roundf(frames[0].surface->w * scale); 382 int h = (int)SDL_roundf(frames[0].surface->h * scale); 383 384 bool ok = true; 385 // RIFF header 386 ok &= SDL_WriteU32LE(dst, RIFF_FOURCC('R', 'I', 'F', 'F')); 387 Sint64 riff_size_offset = SDL_TellIO(dst); 388 ok &= SDL_WriteU32LE(dst, 0); 389 ok &= SDL_WriteU32LE(dst, RIFF_FOURCC('A', 'C', 'O', 'N')); 390 391 // anih header chunk 392 ok &= SDL_WriteU32LE(dst, RIFF_FOURCC('a', 'n', 'i', 'h')); 393 ok &= SDL_WriteU32LE(dst, sizeof(ANIHEADER)); 394 395 ANIHEADER anih; 396 SDL_zero(anih); 397 anih.cbSizeof = sizeof(anih); 398 anih.frames = frame_count; 399 anih.steps = frame_count; 400 anih.jifRate = 1; 401 anih.fl = ANI_FLAG_ICON; 402 ok &= (SDL_WriteIO(dst, &anih, sizeof(anih)) == sizeof(anih)); 403 404 // Rate chunk 405 ok &= SDL_WriteU32LE(dst, RIFF_FOURCC('r', 'a', 't', 'e')); 406 ok &= SDL_WriteU32LE(dst, sizeof(DWORD) * frame_count); 407 for (int i = 0; i < frame_count; ++i) { 408 // Animated Win32 cursors are in jiffy units, and one jiffy is 1/60 of a second. 409 const double WIN32_JIFFY = 1000.0 / 60.0; 410 DWORD duration = (frames[i].duration ? SDL_lround(frames[i].duration / WIN32_JIFFY) : 0xFFFFFFFF); 411 ok &= SDL_WriteU32LE(dst, duration); 412 } 413 414 // Frame list 415 ok &= SDL_WriteU32LE(dst, RIFF_FOURCC('L', 'I', 'S', 'T')); 416 Sint64 frame_list_size_offset = SDL_TellIO(dst); 417 ok &= SDL_WriteU32LE(dst, 0); 418 ok &= SDL_WriteU32LE(dst, RIFF_FOURCC('f', 'r', 'a', 'm')); 419 420 for (int i = 0; i < frame_count; ++i) { 421 ok &= WriteIconFrame(dst, frames[i].surface, hot_x, hot_y, scale); 422 } 423 ok &= SaveChunkSize(dst, frame_list_size_offset); 424 425 // All done! 426 ok &= SaveChunkSize(dst, riff_size_offset); 427 if (!ok) { 428 // The error has been set above 429 goto done; 430 } 431 432 BYTE *mem = (BYTE *)SDL_GetPointerProperty(SDL_GetIOProperties(dst), SDL_PROP_IOSTREAM_DYNAMIC_MEMORY_POINTER, NULL); 433 DWORD size = (DWORD)SDL_GetIOSize(dst); 434 hcursor = (HCURSOR)CreateIconFromResourceEx(mem, size, FALSE, 0x00030000, w, h, 0); 435 if (!hcursor) { 436 SDL_SetError("CreateIconFromResource failed"); 437 } 438 439done: 440 SDL_CloseIO(dst); 441 442 return hcursor; 443} 444 445static SDL_Cursor *WIN_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y) 446{ 447 SDL_CursorFrameInfo frame = { surface, 0 }; 448 return WIN_CreateAnimatedCursorAndData(&frame, 1, hot_x, hot_y); 449} 450 451static SDL_Cursor *WIN_CreateAnimatedCursor(SDL_CursorFrameInfo *frames, int frame_count, int hot_x, int hot_y) 452{ 453 return WIN_CreateAnimatedCursorAndData(frames, frame_count, hot_x, hot_y); 454} 455 456static SDL_Cursor *WIN_CreateBlankCursor(void) 457{ 458 SDL_Cursor *cursor = NULL; 459 SDL_Surface *surface = SDL_CreateSurface(32, 32, SDL_PIXELFORMAT_ARGB8888); 460 if (surface) { 461 cursor = WIN_CreateCursor(surface, 0, 0); 462 SDL_DestroySurface(surface); 463 } 464 return cursor; 465} 466 467static SDL_Cursor *WIN_CreateSystemCursor(SDL_SystemCursor id) 468{ 469 LPCTSTR name; 470 471 switch (id) { 472 default: 473 SDL_assert(!"Unknown system cursor ID"); 474 return NULL; 475 case SDL_SYSTEM_CURSOR_DEFAULT: 476 name = IDC_ARROW; 477 break; 478 case SDL_SYSTEM_CURSOR_TEXT: 479 name = IDC_IBEAM; 480 break; 481 case SDL_SYSTEM_CURSOR_WAIT: 482 name = IDC_WAIT; 483 break; 484 case SDL_SYSTEM_CURSOR_CROSSHAIR: 485 name = IDC_CROSS; 486 break; 487 case SDL_SYSTEM_CURSOR_PROGRESS: 488 name = IDC_APPSTARTING; 489 break; 490 case SDL_SYSTEM_CURSOR_NWSE_RESIZE: 491 name = IDC_SIZENWSE; 492 break; 493 case SDL_SYSTEM_CURSOR_NESW_RESIZE: 494 name = IDC_SIZENESW; 495 break; 496 case SDL_SYSTEM_CURSOR_EW_RESIZE: 497 name = IDC_SIZEWE; 498 break; 499 case SDL_SYSTEM_CURSOR_NS_RESIZE: 500 name = IDC_SIZENS; 501 break; 502 case SDL_SYSTEM_CURSOR_MOVE: 503 name = IDC_SIZEALL; 504 break; 505 case SDL_SYSTEM_CURSOR_NOT_ALLOWED: 506 name = IDC_NO; 507 break; 508 case SDL_SYSTEM_CURSOR_POINTER: 509 name = IDC_HAND; 510 break; 511 case SDL_SYSTEM_CURSOR_NW_RESIZE: 512 name = IDC_SIZENWSE; 513 break; 514 case SDL_SYSTEM_CURSOR_N_RESIZE: 515 name = IDC_SIZENS; 516 break; 517 case SDL_SYSTEM_CURSOR_NE_RESIZE: 518 name = IDC_SIZENESW; 519 break; 520 case SDL_SYSTEM_CURSOR_E_RESIZE: 521 name = IDC_SIZEWE; 522 break; 523 case SDL_SYSTEM_CURSOR_SE_RESIZE: 524 name = IDC_SIZENWSE; 525 break; 526 case SDL_SYSTEM_CURSOR_S_RESIZE: 527 name = IDC_SIZENS; 528 break; 529 case SDL_SYSTEM_CURSOR_SW_RESIZE: 530 name = IDC_SIZENESW; 531 break; 532 case SDL_SYSTEM_CURSOR_W_RESIZE: 533 name = IDC_SIZEWE; 534 break; 535 } 536 return WIN_CreateCursorAndData(LoadCursor(NULL, name)); 537} 538 539static SDL_Cursor *WIN_CreateDefaultCursor(void) 540{ 541 SDL_SystemCursor id = SDL_GetDefaultSystemCursor(); 542 return WIN_CreateSystemCursor(id); 543} 544 545static void WIN_FreeCursor(SDL_Cursor *cursor) 546{ 547 SDL_CursorData *data = cursor->internal; 548 549 for (int i = 0; i < data->num_frames; ++i) { 550 SDL_DestroySurface(data->frames[i].surface); 551 } 552 while (data->cache) { 553 CachedCursor *entry = data->cache; 554 data->cache = entry->next; 555 if (entry->cursor) { 556 DestroyCursor(entry->cursor); 557 } 558 SDL_free(entry); 559 } 560 if (data->cursor) { 561 DestroyCursor(data->cursor); 562 } 563 SDL_free(data); 564 SDL_free(cursor); 565} 566 567static HCURSOR GetCachedCursor(SDL_Cursor *cursor) 568{ 569 SDL_CursorData *data = cursor->internal; 570 571 float scale = 1.0f; 572 if (SDL_GetHintBoolean(SDL_HINT_MOUSE_DPI_SCALE_CURSORS, false)) { 573 scale = SDL_GetDisplayContentScale(SDL_GetDisplayForWindow(SDL_GetMouseFocus())); 574 if (scale == 0.0f) { 575 scale = 1.0f; 576 } 577 } 578 for (CachedCursor *entry = data->cache; entry; entry = entry->next) { 579 if (scale == entry->scale) { 580 return entry->cursor; 581 } 582 } 583 584 // Need to create a cursor for this content scale 585 HCURSOR hcursor = WIN_CreateAnimatedCursorInternal(data->frames, data->num_frames, data->hot_x, data->hot_y, scale); 586 if (!hcursor) { 587 return NULL; 588 } 589 590 CachedCursor *entry = (CachedCursor *)SDL_calloc(1, sizeof(*entry)); 591 if (!entry) { 592 DestroyCursor(hcursor); 593 return NULL; 594 } 595 entry->cursor = hcursor; 596 entry->scale = scale; 597 entry->next = data->cache; 598 data->cache = entry; 599 600 return hcursor; 601} 602 603static bool WIN_ShowCursor(SDL_Cursor *cursor) 604{ 605 if (!cursor) { 606 if (GetSystemMetrics(SM_REMOTESESSION)) { 607 // Use a blank cursor so we continue to get relative motion over RDP 608 cursor = SDL_blank_cursor; 609 } 610 } 611 if (cursor) { 612 SDL_CursorData *data = cursor->internal; 613 if (data->num_frames > 0) { 614 SDL_cursor = GetCachedCursor(cursor); 615 } else { 616 SDL_cursor = data->cursor; 617 } 618 } else { 619 SDL_cursor = NULL; 620 } 621 if (SDL_GetMouseFocus() != NULL) { 622 SetCursor(SDL_cursor); 623 } 624 return true; 625} 626 627void WIN_SetCursorPos(int x, int y) 628{ 629 // We need to jitter the value because otherwise Windows will occasionally inexplicably ignore the SetCursorPos() or SendInput() 630 SetCursorPos(x, y); 631 SetCursorPos(x + 1, y); 632 SetCursorPos(x, y); 633 634 // Flush any mouse motion prior to or associated with this warp 635#ifdef _MSC_VER // We explicitly want to use GetTickCount(), not GetTickCount64() 636#pragma warning(push) 637#pragma warning(disable : 28159) 638#endif 639 SDL_last_warp_time = GetTickCount(); 640 if (!SDL_last_warp_time) { 641 SDL_last_warp_time = 1; 642 } 643#ifdef _MSC_VER 644#pragma warning(pop) 645#endif 646} 647 648static bool WIN_WarpMouse(SDL_Window *window, float x, float y) 649{ 650 SDL_WindowData *data = window->internal; 651 HWND hwnd = data->hwnd; 652 POINT pt; 653 654 // Don't warp the mouse while we're doing a modal interaction 655 if (data->in_title_click || data->focus_click_pending) { 656 return true; 657 } 658 659 pt.x = (int)SDL_roundf(x); 660 pt.y = (int)SDL_roundf(y); 661 ClientToScreen(hwnd, &pt); 662 WIN_SetCursorPos(pt.x, pt.y); 663 664 // Send the exact mouse motion associated with this warp 665 SDL_SendMouseMotion(0, window, SDL_GLOBAL_MOUSE_ID, false, x, y); 666 return true; 667} 668 669static bool WIN_WarpMouseGlobal(float x, float y) 670{ 671 POINT pt; 672 673 pt.x = (int)SDL_roundf(x); 674 pt.y = (int)SDL_roundf(y); 675 SetCursorPos(pt.x, pt.y); 676 return true; 677} 678 679static bool WIN_SetRelativeMouseMode(bool enabled) 680{ 681 return WIN_SetRawMouseEnabled(SDL_GetVideoDevice(), enabled); 682} 683 684static bool WIN_CaptureMouse(SDL_Window *window) 685{ 686 if (window) { 687 SDL_WindowData *data = window->internal; 688 SetCapture(data->hwnd); 689 } else { 690 SDL_Window *focus_window = SDL_GetMouseFocus(); 691 692 if (focus_window) { 693 SDL_WindowData *data = focus_window->internal; 694 if (!data->mouse_tracked) { 695 SDL_SetMouseFocus(NULL); 696 } 697 } 698 ReleaseCapture(); 699 } 700 701 return true; 702} 703 704static SDL_MouseButtonFlags WIN_GetGlobalMouseState(float *x, float *y) 705{ 706 SDL_MouseButtonFlags result = 0; 707 POINT pt = { 0, 0 }; 708 bool swapButtons = GetSystemMetrics(SM_SWAPBUTTON) != 0; 709 710 GetCursorPos(&pt); 711 *x = (float)pt.x; 712 *y = (float)pt.y; 713 714 result |= GetAsyncKeyState(!swapButtons ? VK_LBUTTON : VK_RBUTTON) & 0x8000 ? SDL_BUTTON_LMASK : 0; 715 result |= GetAsyncKeyState(!swapButtons ? VK_RBUTTON : VK_LBUTTON) & 0x8000 ? SDL_BUTTON_RMASK : 0; 716 result |= GetAsyncKeyState(VK_MBUTTON) & 0x8000 ? SDL_BUTTON_MMASK : 0; 717 result |= GetAsyncKeyState(VK_XBUTTON1) & 0x8000 ? SDL_BUTTON_X1MASK : 0; 718 result |= GetAsyncKeyState(VK_XBUTTON2) & 0x8000 ? SDL_BUTTON_X2MASK : 0; 719 720 return result; 721} 722 723static void WIN_ApplySystemScale(void *internal, Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, float *x, float *y) 724{ 725 if (!internal) { 726 return; 727 } 728 WIN_MouseData *data = (WIN_MouseData *)internal; 729 730 SDL_VideoDisplay *display = window ? SDL_GetVideoDisplayForWindow(window) : SDL_GetVideoDisplay(SDL_GetPrimaryDisplay()); 731 732 Sint64 ix = (Sint64)*x * 65536; 733 Sint64 iy = (Sint64)*y * 65536; 734 Uint32 dpi = display ? (Uint32)(display->content_scale * USER_DEFAULT_SCREEN_DPI) : USER_DEFAULT_SCREEN_DPI; 735 736 if (!data->enhanced) { // early return if flat scale 737 dpi = data->dpiscale * (data->dpiaware ? dpi : USER_DEFAULT_SCREEN_DPI); 738 ix *= dpi; 739 iy *= dpi; 740 ix /= USER_DEFAULT_SCREEN_DPI; 741 iy /= USER_DEFAULT_SCREEN_DPI; 742 ix /= 32; 743 iy /= 32; 744 // data->residual[0] += ix; 745 // data->residual[1] += iy; 746 // ix = 65536 * (data->residual[0] / 65536); 747 // iy = 65536 * (data->residual[1] / 65536); 748 // data->residual[0] -= ix; 749 // data->residual[1] -= iy; 750 *x = (float)ix / 65536.0f; 751 *y = (float)iy / 65536.0f; 752 return; 753 } 754 755 Uint64 *xs = data->xs; 756 Uint64 *ys = data->ys; 757 Uint64 absx = SDL_abs(ix); 758 Uint64 absy = SDL_abs(iy); 759 Uint64 speed = SDL_min(absx, absy) + (SDL_max(absx, absy) << 1); // super cursed approximation used by Windows 760 if (speed == 0) { 761 return; 762 } 763 764 int i, j, k; 765 for (i = 1; i < 5; i++) { 766 j = i; 767 if (speed < xs[j]) { 768 break; 769 } 770 } 771 i -= 1; 772 j -= 1; 773 k = data->last_node; 774 data->last_node = j; 775 776 Uint32 denom = data->dpidenom; 777 Sint64 scale = 0; 778 Sint64 xdiff = xs[j+1] - xs[j]; 779 Sint64 ydiff = ys[j+1] - ys[j]; 780 if (xdiff != 0) { 781 Sint64 slope = ydiff / xdiff; 782 Sint64 inter = slope * xs[i] - ys[i]; 783 scale += slope - inter / speed; 784 } 785 786 if (j > k) { 787 denom <<= 1; 788 xdiff = xs[k+1] - xs[k]; 789 ydiff = ys[k+1] - ys[k]; 790 if (xdiff != 0) { 791 Sint64 slope = ydiff / xdiff; 792 Sint64 inter = slope * xs[k] - ys[k]; 793 scale += slope - inter / speed; 794 } 795 } 796 797 scale *= dpi; 798 ix *= scale; 799 iy *= scale; 800 ix /= denom; 801 iy /= denom; 802 // data->residual[0] += ix; 803 // data->residual[1] += iy; 804 // ix = 65536 * (data->residual[0] / 65536); 805 // iy = 65536 * (data->residual[1] / 65536); 806 // data->residual[0] -= ix; 807 // data->residual[1] -= iy; 808 *x = (float)ix / 65536.0f; 809 *y = (float)iy / 65536.0f; 810} 811 812void WIN_InitMouse(SDL_VideoDevice *_this) 813{ 814 SDL_Mouse *mouse = SDL_GetMouse(); 815 816 mouse->CreateCursor = WIN_CreateCursor; 817 mouse->CreateAnimatedCursor = WIN_CreateAnimatedCursor; 818 mouse->CreateSystemCursor = WIN_CreateSystemCursor; 819 mouse->ShowCursor = WIN_ShowCursor; 820 mouse->FreeCursor = WIN_FreeCursor; 821 mouse->WarpMouse = WIN_WarpMouse; 822 mouse->WarpMouseGlobal = WIN_WarpMouseGlobal; 823 mouse->SetRelativeMouseMode = WIN_SetRelativeMouseMode; 824 mouse->CaptureMouse = WIN_CaptureMouse; 825 mouse->GetGlobalMouseState = WIN_GetGlobalMouseState; 826 mouse->ApplySystemScale = WIN_ApplySystemScale; 827 mouse->system_scale_data = &WIN_system_scale_data; 828 829 SDL_SetDefaultCursor(WIN_CreateDefaultCursor()); 830 831 SDL_blank_cursor = WIN_CreateBlankCursor(); 832 833 WIN_UpdateMouseSystemScale(); 834} 835 836void WIN_QuitMouse(SDL_VideoDevice *_this) 837{ 838 if (SDL_blank_cursor) { 839 WIN_FreeCursor(SDL_blank_cursor); 840 SDL_blank_cursor = NULL; 841 } 842} 843 844static void ReadMouseCurve(int v, Uint64 xs[5], Uint64 ys[5]) 845{ 846 bool win8 = WIN_IsWindows8OrGreater(); 847 DWORD xbuff[10] = { 848 0x00000000, 0, 849 0x00006e15, 0, 850 0x00014000, 0, 851 0x0003dc29, 0, 852 0x00280000, 0 853 }; 854 DWORD ybuff[10] = { 855 0x00000000, 0, 856 win8 ? 0x000111fd : 0x00015eb8, 0, 857 win8 ? 0x00042400 : 0x00054ccd, 0, 858 win8 ? 0x0012fc00 : 0x00184ccd, 0, 859 win8 ? 0x01bbc000 : 0x02380000, 0 860 }; 861 DWORD xsize = sizeof(xbuff); 862 DWORD ysize = sizeof(ybuff); 863 HKEY open_handle; 864 if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Control Panel\\Mouse", 0, KEY_READ, &open_handle) == ERROR_SUCCESS) { 865 RegQueryValueExW(open_handle, L"SmoothMouseXCurve", NULL, NULL, (BYTE*)xbuff, &xsize); 866 RegQueryValueExW(open_handle, L"SmoothMouseYCurve", NULL, NULL, (BYTE*)ybuff, &ysize); 867 RegCloseKey(open_handle); 868 } 869 xs[0] = 0; // first node must always be origin 870 ys[0] = 0; // first node must always be origin 871 int i; 872 for (i = 1; i < 5; i++) { 873 xs[i] = (7 * (Uint64)xbuff[i * 2]); 874 ys[i] = (v * (Uint64)ybuff[i * 2]) << 17; 875 } 876} 877 878void WIN_UpdateMouseSystemScale(void) 879{ 880 SDL_Mouse *mouse = SDL_GetMouse(); 881 882 if (mouse->ApplySystemScale == WIN_ApplySystemScale) { 883 mouse->system_scale_data = &WIN_system_scale_data; 884 } 885 886 // always reinitialize to valid defaults, whether fetch was successful or not. 887 WIN_MouseData *data = &WIN_system_scale_data; 888 data->residual[0] = 0; 889 data->residual[1] = 0; 890 data->dpiscale = 32; 891 data->dpidenom = (10 * (WIN_IsWindows8OrGreater() ? 120 : 150)) << 16; 892 data->dpiaware = WIN_IsPerMonitorV2DPIAware(SDL_GetVideoDevice()); 893 data->enhanced = false; 894 895 int v = 10; 896 if (SystemParametersInfo(SPI_GETMOUSESPEED, 0, &v, 0)) { 897 v = SDL_max(1, SDL_min(v, 20)); 898 data->dpiscale = SDL_max(SDL_max(v, (v - 2) * 4), (v - 6) * 8); 899 } 900 901 int params[3]; 902 if (SystemParametersInfo(SPI_GETMOUSE, 0, &params, 0)) { 903 data->enhanced = params[2] ? true : false; 904 if (params[2]) { 905 ReadMouseCurve(v, data->xs, data->ys); 906 } 907 } 908} 909 910#endif // SDL_VIDEO_DRIVER_WINDOWS 911
[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.