SDL_windowswindow.c (32126B)
1 /* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org> 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 SDL_VIDEO_DRIVER_WINDOWS 24 25 #include "../../core/windows/SDL_windows.h" 26 27 #include "../SDL_sysvideo.h" 28 #include "../SDL_pixels_c.h" 29 #include "../../events/SDL_keyboard_c.h" 30 #include "../../events/SDL_mouse_c.h" 31 32 #include "SDL_windowsvideo.h" 33 #include "SDL_windowswindow.h" 34 #include "SDL_hints.h" 35 #include "SDL_timer.h" 36 37 /* Dropfile support */ 38 #include <shellapi.h> 39 40 /* This is included after SDL_windowsvideo.h, which includes windows.h */ 41 #include "SDL_syswm.h" 42 43 /* Windows CE compatibility */ 44 #ifndef SWP_NOCOPYBITS 45 #define SWP_NOCOPYBITS 0 46 #endif 47 48 /* Fake window to help with DirectInput events. */ 49 HWND SDL_HelperWindow = NULL; 50 static WCHAR *SDL_HelperWindowClassName = TEXT("SDLHelperWindowInputCatcher"); 51 static WCHAR *SDL_HelperWindowName = TEXT("SDLHelperWindowInputMsgWindow"); 52 static ATOM SDL_HelperWindowClass = 0; 53 54 /* For borderless Windows, still want the following flags: 55 - WS_CAPTION: this seems to enable the Windows minimize animation 56 - WS_SYSMENU: enables system context menu on task bar 57 - WS_MINIMIZEBOX: window will respond to Windows minimize commands sent to all windows, such as windows key + m, shaking title bar, etc. 58 This will also cause the task bar to overlap the window and other windowed behaviors, so only use this for windows that shouldn't appear to be fullscreen 59 */ 60 61 #define STYLE_BASIC (WS_CLIPSIBLINGS | WS_CLIPCHILDREN) 62 #define STYLE_FULLSCREEN (WS_POPUP) 63 #define STYLE_BORDERLESS (WS_POPUP) 64 #define STYLE_BORDERLESS_WINDOWED (WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX) 65 #define STYLE_NORMAL (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX) 66 #define STYLE_RESIZABLE (WS_THICKFRAME | WS_MAXIMIZEBOX) 67 #define STYLE_MASK (STYLE_FULLSCREEN | STYLE_BORDERLESS | STYLE_NORMAL | STYLE_RESIZABLE) 68 69 static DWORD 70 GetWindowStyle(SDL_Window * window) 71 { 72 DWORD style = 0; 73 74 if (window->flags & SDL_WINDOW_FULLSCREEN) { 75 style |= STYLE_FULLSCREEN; 76 } else { 77 if (window->flags & SDL_WINDOW_BORDERLESS) { 78 /* SDL 2.1: 79 This behavior more closely matches other platform where the window is borderless 80 but still interacts with the window manager (e.g. task bar shows above it, it can 81 be resized to fit within usable desktop area, etc.) so this should be the behavior 82 for a future SDL release. 83 84 If you want a borderless window the size of the desktop that looks like a fullscreen 85 window, then you should use the SDL_WINDOW_FULLSCREEN_DESKTOP flag. 86 */ 87 if (SDL_GetHintBoolean("SDL_BORDERLESS_WINDOWED_STYLE", SDL_FALSE)) { 88 style |= STYLE_BORDERLESS_WINDOWED; 89 } else { 90 style |= STYLE_BORDERLESS; 91 } 92 } else { 93 style |= STYLE_NORMAL; 94 } 95 96 if (window->flags & SDL_WINDOW_RESIZABLE) { 97 /* You can have a borderless resizable window, but Windows doesn't always draw it correctly, 98 see https://bugzilla.libsdl.org/show_bug.cgi?id=4466 99 */ 100 if (!(window->flags & SDL_WINDOW_BORDERLESS) || 101 SDL_GetHintBoolean("SDL_BORDERLESS_RESIZABLE_STYLE", SDL_FALSE)) { 102 style |= STYLE_RESIZABLE; 103 } 104 } 105 106 /* Need to set initialize minimize style, or when we call ShowWindow with WS_MINIMIZE it will activate a random window */ 107 if (window->flags & SDL_WINDOW_MINIMIZED) { 108 style |= WS_MINIMIZE; 109 } 110 } 111 return style; 112 } 113 114 static void 115 WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL menu, int *x, int *y, int *width, int *height, SDL_bool use_current) 116 { 117 RECT rect; 118 119 rect.left = 0; 120 rect.top = 0; 121 rect.right = (use_current ? window->w : window->windowed.w); 122 rect.bottom = (use_current ? window->h : window->windowed.h); 123 124 /* borderless windows will have WM_NCCALCSIZE return 0 for the non-client area. When this happens, it looks like windows will send a resize message 125 expanding the window client area to the previous window + chrome size, so shouldn't need to adjust the window size for the set styles. 126 */ 127 if (!(window->flags & SDL_WINDOW_BORDERLESS)) 128 AdjustWindowRectEx(&rect, style, menu, 0); 129 130 *x = (use_current ? window->x : window->windowed.x) + rect.left; 131 *y = (use_current ? window->y : window->windowed.y) + rect.top; 132 *width = (rect.right - rect.left); 133 *height = (rect.bottom - rect.top); 134 } 135 136 static void 137 WIN_AdjustWindowRect(SDL_Window *window, int *x, int *y, int *width, int *height, SDL_bool use_current) 138 { 139 SDL_WindowData *data = (SDL_WindowData *)window->driverdata; 140 HWND hwnd = data->hwnd; 141 DWORD style; 142 BOOL menu; 143 144 style = GetWindowLong(hwnd, GWL_STYLE); 145 menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL); 146 WIN_AdjustWindowRectWithStyle(window, style, menu, x, y, width, height, use_current); 147 } 148 149 static void 150 WIN_SetWindowPositionInternal(_THIS, SDL_Window * window, UINT flags) 151 { 152 SDL_WindowData *data = (SDL_WindowData *)window->driverdata; 153 HWND hwnd = data->hwnd; 154 HWND top; 155 int x, y; 156 int w, h; 157 158 /* Figure out what the window area will be */ 159 if (SDL_ShouldAllowTopmost() && ((window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_INPUT_FOCUS)) == (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_INPUT_FOCUS) || (window->flags & SDL_WINDOW_ALWAYS_ON_TOP))) { 160 top = HWND_TOPMOST; 161 } else { 162 top = HWND_NOTOPMOST; 163 } 164 165 WIN_AdjustWindowRect(window, &x, &y, &w, &h, SDL_TRUE); 166 167 data->expected_resize = SDL_TRUE; 168 SetWindowPos(hwnd, top, x, y, w, h, flags); 169 data->expected_resize = SDL_FALSE; 170 } 171 172 static int 173 SetupWindowData(_THIS, SDL_Window * window, HWND hwnd, HWND parent, SDL_bool created) 174 { 175 SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata; 176 SDL_WindowData *data; 177 178 /* Allocate the window data */ 179 data = (SDL_WindowData *) SDL_calloc(1, sizeof(*data)); 180 if (!data) { 181 return SDL_OutOfMemory(); 182 } 183 data->window = window; 184 data->hwnd = hwnd; 185 data->parent = parent; 186 data->hdc = GetDC(hwnd); 187 data->hinstance = (HINSTANCE) GetWindowLongPtr(hwnd, GWLP_HINSTANCE); 188 data->created = created; 189 data->mouse_button_flags = 0; 190 data->last_pointer_update = (LPARAM)-1; 191 data->videodata = videodata; 192 data->initializing = SDL_TRUE; 193 194 window->driverdata = data; 195 196 /* Associate the data with the window */ 197 if (!SetProp(hwnd, TEXT("SDL_WindowData"), data)) { 198 ReleaseDC(hwnd, data->hdc); 199 SDL_free(data); 200 return WIN_SetError("SetProp() failed"); 201 } 202 203 /* Set up the window proc function */ 204 #ifdef GWLP_WNDPROC 205 data->wndproc = (WNDPROC) GetWindowLongPtr(hwnd, GWLP_WNDPROC); 206 if (data->wndproc == WIN_WindowProc) { 207 data->wndproc = NULL; 208 } else { 209 SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) WIN_WindowProc); 210 } 211 #else 212 data->wndproc = (WNDPROC) GetWindowLong(hwnd, GWL_WNDPROC); 213 if (data->wndproc == WIN_WindowProc) { 214 data->wndproc = NULL; 215 } else { 216 SetWindowLong(hwnd, GWL_WNDPROC, (LONG_PTR) WIN_WindowProc); 217 } 218 #endif 219 220 /* Fill in the SDL window with the window data */ 221 { 222 RECT rect; 223 if (GetClientRect(hwnd, &rect)) { 224 int w = rect.right; 225 int h = rect.bottom; 226 if ((window->windowed.w && window->windowed.w != w) || (window->windowed.h && window->windowed.h != h)) { 227 /* We tried to create a window larger than the desktop and Windows didn't allow it. Override! */ 228 int x, y; 229 /* Figure out what the window area will be */ 230 WIN_AdjustWindowRect(window, &x, &y, &w, &h, SDL_FALSE); 231 SetWindowPos(hwnd, HWND_NOTOPMOST, x, y, w, h, SWP_NOCOPYBITS | SWP_NOZORDER | SWP_NOACTIVATE); 232 } else { 233 window->w = w; 234 window->h = h; 235 } 236 } 237 } 238 { 239 POINT point; 240 point.x = 0; 241 point.y = 0; 242 if (ClientToScreen(hwnd, &point)) { 243 window->x = point.x; 244 window->y = point.y; 245 } 246 } 247 { 248 DWORD style = GetWindowLong(hwnd, GWL_STYLE); 249 if (style & WS_VISIBLE) { 250 window->flags |= SDL_WINDOW_SHOWN; 251 } else { 252 window->flags &= ~SDL_WINDOW_SHOWN; 253 } 254 if (style & WS_POPUP) { 255 window->flags |= SDL_WINDOW_BORDERLESS; 256 } else { 257 window->flags &= ~SDL_WINDOW_BORDERLESS; 258 } 259 if (style & WS_THICKFRAME) { 260 window->flags |= SDL_WINDOW_RESIZABLE; 261 } else { 262 window->flags &= ~SDL_WINDOW_RESIZABLE; 263 } 264 #ifdef WS_MAXIMIZE 265 if (style & WS_MAXIMIZE) { 266 window->flags |= SDL_WINDOW_MAXIMIZED; 267 } else 268 #endif 269 { 270 window->flags &= ~SDL_WINDOW_MAXIMIZED; 271 } 272 #ifdef WS_MINIMIZE 273 if (style & WS_MINIMIZE) { 274 window->flags |= SDL_WINDOW_MINIMIZED; 275 } else 276 #endif 277 { 278 window->flags &= ~SDL_WINDOW_MINIMIZED; 279 } 280 } 281 if (GetFocus() == hwnd) { 282 window->flags |= SDL_WINDOW_INPUT_FOCUS; 283 SDL_SetKeyboardFocus(data->window); 284 285 if (window->flags & SDL_WINDOW_INPUT_GRABBED) { 286 RECT rect; 287 GetClientRect(hwnd, &rect); 288 ClientToScreen(hwnd, (LPPOINT) & rect); 289 ClientToScreen(hwnd, (LPPOINT) & rect + 1); 290 ClipCursor(&rect); 291 } 292 } 293 294 /* Enable multi-touch */ 295 if (videodata->RegisterTouchWindow) { 296 videodata->RegisterTouchWindow(hwnd, (TWF_FINETOUCH|TWF_WANTPALM)); 297 } 298 299 data->initializing = SDL_FALSE; 300 301 /* All done! */ 302 return 0; 303 } 304 305 306 307 int 308 WIN_CreateWindow(_THIS, SDL_Window * window) 309 { 310 HWND hwnd, parent = NULL; 311 DWORD style = STYLE_BASIC; 312 int x, y; 313 int w, h; 314 315 if (window->flags & SDL_WINDOW_SKIP_TASKBAR) { 316 parent = CreateWindow(SDL_Appname, TEXT(""), STYLE_BASIC, 0, 0, 32, 32, NULL, NULL, SDL_Instance, NULL); 317 } 318 319 style |= GetWindowStyle(window); 320 321 /* Figure out what the window area will be */ 322 WIN_AdjustWindowRectWithStyle(window, style, FALSE, &x, &y, &w, &h, SDL_FALSE); 323 324 hwnd = 325 CreateWindow(SDL_Appname, TEXT(""), style, x, y, w, h, parent, NULL, 326 SDL_Instance, NULL); 327 if (!hwnd) { 328 return WIN_SetError("Couldn't create window"); 329 } 330 331 WIN_PumpEvents(_this); 332 333 if (SetupWindowData(_this, window, hwnd, parent, SDL_TRUE) < 0) { 334 DestroyWindow(hwnd); 335 if (parent) { 336 DestroyWindow(parent); 337 } 338 return -1; 339 } 340 341 /* Inform Windows of the frame change so we can respond to WM_NCCALCSIZE */ 342 SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE); 343 344 if (window->flags & SDL_WINDOW_MINIMIZED) { 345 ShowWindow(hwnd, SW_SHOWMINNOACTIVE); 346 } 347 348 if (!(window->flags & SDL_WINDOW_OPENGL)) { 349 return 0; 350 } 351 352 /* The rest of this macro mess is for OpenGL or OpenGL ES windows */ 353 #if SDL_VIDEO_OPENGL_ES2 354 if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES 355 #if SDL_VIDEO_OPENGL_WGL 356 && (!_this->gl_data || WIN_GL_UseEGL(_this)) 357 #endif /* SDL_VIDEO_OPENGL_WGL */ 358 ) { 359 #if SDL_VIDEO_OPENGL_EGL 360 if (WIN_GLES_SetupWindow(_this, window) < 0) { 361 WIN_DestroyWindow(_this, window); 362 return -1; 363 } 364 return 0; 365 #else 366 return SDL_SetError("Could not create GLES window surface (EGL support not configured)"); 367 #endif /* SDL_VIDEO_OPENGL_EGL */ 368 } 369 #endif /* SDL_VIDEO_OPENGL_ES2 */ 370 371 #if SDL_VIDEO_OPENGL_WGL 372 if (WIN_GL_SetupWindow(_this, window) < 0) { 373 WIN_DestroyWindow(_this, window); 374 return -1; 375 } 376 #else 377 return SDL_SetError("Could not create GL window (WGL support not configured)"); 378 #endif 379 380 return 0; 381 } 382 383 int 384 WIN_CreateWindowFrom(_THIS, SDL_Window * window, const void *data) 385 { 386 HWND hwnd = (HWND) data; 387 LPTSTR title; 388 int titleLen; 389 SDL_bool isstack; 390 391 /* Query the title from the existing window */ 392 titleLen = GetWindowTextLength(hwnd); 393 title = SDL_small_alloc(TCHAR, titleLen + 1, &isstack); 394 if (title) { 395 titleLen = GetWindowText(hwnd, title, titleLen + 1); 396 } else { 397 titleLen = 0; 398 } 399 if (titleLen > 0) { 400 window->title = WIN_StringToUTF8(title); 401 } 402 if (title) { 403 SDL_small_free(title, isstack); 404 } 405 406 if (SetupWindowData(_this, window, hwnd, GetParent(hwnd), SDL_FALSE) < 0) { 407 return -1; 408 } 409 410 #if SDL_VIDEO_OPENGL_WGL 411 { 412 const char *hint = SDL_GetHint(SDL_HINT_VIDEO_WINDOW_SHARE_PIXEL_FORMAT); 413 if (hint) { 414 /* This hint is a pointer (in string form) of the address of 415 the window to share a pixel format with 416 */ 417 SDL_Window *otherWindow = NULL; 418 SDL_sscanf(hint, "%p", (void**)&otherWindow); 419 420 /* Do some error checking on the pointer */ 421 if (otherWindow != NULL && otherWindow->magic == &_this->window_magic) { 422 /* If the otherWindow has SDL_WINDOW_OPENGL set, set it for the new window as well */ 423 if (otherWindow->flags & SDL_WINDOW_OPENGL) { 424 window->flags |= SDL_WINDOW_OPENGL; 425 if (!WIN_GL_SetPixelFormatFrom(_this, otherWindow, window)) { 426 return -1; 427 } 428 } 429 } 430 } 431 } 432 #endif 433 return 0; 434 } 435 436 void 437 WIN_SetWindowTitle(_THIS, SDL_Window * window) 438 { 439 HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd; 440 LPTSTR title = WIN_UTF8ToString(window->title); 441 SetWindowText(hwnd, title); 442 SDL_free(title); 443 } 444 445 void 446 WIN_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon) 447 { 448 HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd; 449 HICON hicon = NULL; 450 BYTE *icon_bmp; 451 int icon_len, mask_len, y; 452 SDL_RWops *dst; 453 SDL_bool isstack; 454 455 /* Create temporary buffer for ICONIMAGE structure */ 456 mask_len = (icon->h * (icon->w + 7)/8); 457 icon_len = 40 + icon->h * icon->w * sizeof(Uint32) + mask_len; 458 icon_bmp = SDL_small_alloc(BYTE, icon_len, &isstack); 459 dst = SDL_RWFromMem(icon_bmp, icon_len); 460 if (!dst) { 461 SDL_small_free(icon_bmp, isstack); 462 return; 463 } 464 465 /* Write the BITMAPINFO header */ 466 SDL_WriteLE32(dst, 40); 467 SDL_WriteLE32(dst, icon->w); 468 SDL_WriteLE32(dst, icon->h * 2); 469 SDL_WriteLE16(dst, 1); 470 SDL_WriteLE16(dst, 32); 471 SDL_WriteLE32(dst, BI_RGB); 472 SDL_WriteLE32(dst, icon->h * icon->w * sizeof(Uint32)); 473 SDL_WriteLE32(dst, 0); 474 SDL_WriteLE32(dst, 0); 475 SDL_WriteLE32(dst, 0); 476 SDL_WriteLE32(dst, 0); 477 478 /* Write the pixels upside down into the bitmap buffer */ 479 SDL_assert(icon->format->format == SDL_PIXELFORMAT_ARGB8888); 480 y = icon->h; 481 while (y--) { 482 Uint8 *src = (Uint8 *) icon->pixels + y * icon->pitch; 483 SDL_RWwrite(dst, src, icon->w * sizeof(Uint32), 1); 484 } 485 486 /* Write the mask */ 487 SDL_memset(icon_bmp + icon_len - mask_len, 0xFF, mask_len); 488 489 hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000); 490 491 SDL_RWclose(dst); 492 SDL_small_free(icon_bmp, isstack); 493 494 /* Set the icon for the window */ 495 SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM) hicon); 496 497 /* Set the icon in the task manager (should we do this?) */ 498 SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM) hicon); 499 } 500 501 void 502 WIN_SetWindowPosition(_THIS, SDL_Window * window) 503 { 504 WIN_SetWindowPositionInternal(_this, window, SWP_NOCOPYBITS | SWP_NOSIZE | SWP_NOACTIVATE); 505 } 506 507 void 508 WIN_SetWindowSize(_THIS, SDL_Window * window) 509 { 510 WIN_SetWindowPositionInternal(_this, window, SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOACTIVATE); 511 } 512 513 int 514 WIN_GetWindowBordersSize(_THIS, SDL_Window * window, int *top, int *left, int *bottom, int *right) 515 { 516 HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd; 517 RECT rcClient, rcWindow; 518 POINT ptDiff; 519 520 /* rcClient stores the size of the inner window, while rcWindow stores the outer size relative to the top-left 521 * screen position; so the top/left values of rcClient are always {0,0} and bottom/right are {height,width} */ 522 GetClientRect(hwnd, &rcClient); 523 GetWindowRect(hwnd, &rcWindow); 524 525 /* convert the top/left values to make them relative to 526 * the window; they will end up being slightly negative */ 527 ptDiff.y = rcWindow.top; 528 ptDiff.x = rcWindow.left; 529 530 ScreenToClient(hwnd, &ptDiff); 531 532 rcWindow.top = ptDiff.y; 533 rcWindow.left = ptDiff.x; 534 535 /* convert the bottom/right values to make them relative to the window, 536 * these will be slightly bigger than the inner width/height */ 537 ptDiff.y = rcWindow.bottom; 538 ptDiff.x = rcWindow.right; 539 540 ScreenToClient(hwnd, &ptDiff); 541 542 rcWindow.bottom = ptDiff.y; 543 rcWindow.right = ptDiff.x; 544 545 /* Now that both the inner and outer rects use the same coordinate system we can substract them to get the border size. 546 * Keep in mind that the top/left coordinates of rcWindow are negative because the border lies slightly before {0,0}, 547 * so switch them around because SDL2 wants them in positive. */ 548 *top = rcClient.top - rcWindow.top; 549 *left = rcClient.left - rcWindow.left; 550 *bottom = rcWindow.bottom - rcClient.bottom; 551 *right = rcWindow.right - rcClient.right; 552 553 return 0; 554 } 555 556 void 557 WIN_ShowWindow(_THIS, SDL_Window * window) 558 { 559 DWORD style; 560 HWND hwnd; 561 int nCmdShow; 562 563 hwnd = ((SDL_WindowData *)window->driverdata)->hwnd; 564 nCmdShow = SW_SHOW; 565 style = GetWindowLong(hwnd, GWL_EXSTYLE); 566 if (style & WS_EX_NOACTIVATE) { 567 nCmdShow = SW_SHOWNOACTIVATE; 568 } 569 ShowWindow(hwnd, nCmdShow); 570 } 571 572 void 573 WIN_HideWindow(_THIS, SDL_Window * window) 574 { 575 HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd; 576 ShowWindow(hwnd, SW_HIDE); 577 } 578 579 void 580 WIN_RaiseWindow(_THIS, SDL_Window * window) 581 { 582 HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd; 583 SetForegroundWindow(hwnd); 584 } 585 586 void 587 WIN_MaximizeWindow(_THIS, SDL_Window * window) 588 { 589 SDL_WindowData *data = (SDL_WindowData *)window->driverdata; 590 HWND hwnd = data->hwnd; 591 data->expected_resize = SDL_TRUE; 592 ShowWindow(hwnd, SW_MAXIMIZE); 593 data->expected_resize = SDL_FALSE; 594 } 595 596 void 597 WIN_MinimizeWindow(_THIS, SDL_Window * window) 598 { 599 HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd; 600 ShowWindow(hwnd, SW_MINIMIZE); 601 } 602 603 void 604 WIN_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered) 605 { 606 SDL_WindowData *data = (SDL_WindowData *)window->driverdata; 607 HWND hwnd = data->hwnd; 608 DWORD style; 609 610 style = GetWindowLong(hwnd, GWL_STYLE); 611 style &= ~STYLE_MASK; 612 style |= GetWindowStyle(window); 613 614 data->in_border_change = SDL_TRUE; 615 SetWindowLong(hwnd, GWL_STYLE, style); 616 WIN_SetWindowPositionInternal(_this, window, SWP_NOCOPYBITS | SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE); 617 data->in_border_change = SDL_FALSE; 618 } 619 620 void 621 WIN_SetWindowResizable(_THIS, SDL_Window * window, SDL_bool resizable) 622 { 623 SDL_WindowData *data = (SDL_WindowData *)window->driverdata; 624 HWND hwnd = data->hwnd; 625 DWORD style; 626 627 style = GetWindowLong(hwnd, GWL_STYLE); 628 style &= ~STYLE_MASK; 629 style |= GetWindowStyle(window); 630 631 SetWindowLong(hwnd, GWL_STYLE, style); 632 } 633 634 void 635 WIN_RestoreWindow(_THIS, SDL_Window * window) 636 { 637 SDL_WindowData *data = (SDL_WindowData *)window->driverdata; 638 HWND hwnd = data->hwnd; 639 data->expected_resize = SDL_TRUE; 640 ShowWindow(hwnd, SW_RESTORE); 641 data->expected_resize = SDL_FALSE; 642 } 643 644 void 645 WIN_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen) 646 { 647 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 648 HWND hwnd = data->hwnd; 649 SDL_Rect bounds; 650 DWORD style; 651 HWND top; 652 int x, y; 653 int w, h; 654 655 if (SDL_ShouldAllowTopmost() && ((window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_INPUT_FOCUS)) == (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_INPUT_FOCUS) || window->flags & SDL_WINDOW_ALWAYS_ON_TOP)) { 656 top = HWND_TOPMOST; 657 } else { 658 top = HWND_NOTOPMOST; 659 } 660 661 style = GetWindowLong(hwnd, GWL_STYLE); 662 style &= ~STYLE_MASK; 663 style |= GetWindowStyle(window); 664 665 WIN_GetDisplayBounds(_this, display, &bounds); 666 667 if (fullscreen) { 668 x = bounds.x; 669 y = bounds.y; 670 w = bounds.w; 671 h = bounds.h; 672 673 /* Unset the maximized flag. This fixes 674 https://bugzilla.libsdl.org/show_bug.cgi?id=3215 675 */ 676 if (style & WS_MAXIMIZE) { 677 data->windowed_mode_was_maximized = SDL_TRUE; 678 style &= ~WS_MAXIMIZE; 679 } 680 } else { 681 BOOL menu; 682 683 /* Restore window-maximization state, as applicable. 684 Special care is taken to *not* do this if and when we're 685 alt-tab'ing away (to some other window; as indicated by 686 in_window_deactivation), otherwise 687 https://bugzilla.libsdl.org/show_bug.cgi?id=3215 can reproduce! 688 */ 689 if (data->windowed_mode_was_maximized && !data->in_window_deactivation) { 690 style |= WS_MAXIMIZE; 691 data->windowed_mode_was_maximized = SDL_FALSE; 692 } 693 694 menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL); 695 WIN_AdjustWindowRectWithStyle(window, style, menu, &x, &y, &w, &h, SDL_FALSE); 696 } 697 SetWindowLong(hwnd, GWL_STYLE, style); 698 data->expected_resize = SDL_TRUE; 699 SetWindowPos(hwnd, top, x, y, w, h, SWP_NOCOPYBITS | SWP_NOACTIVATE); 700 data->expected_resize = SDL_FALSE; 701 } 702 703 int 704 WIN_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp) 705 { 706 SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); 707 SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata; 708 HDC hdc; 709 BOOL succeeded = FALSE; 710 711 hdc = CreateDC(data->DeviceName, NULL, NULL, NULL); 712 if (hdc) { 713 succeeded = SetDeviceGammaRamp(hdc, (LPVOID)ramp); 714 if (!succeeded) { 715 WIN_SetError("SetDeviceGammaRamp()"); 716 } 717 DeleteDC(hdc); 718 } 719 return succeeded ? 0 : -1; 720 } 721 722 int 723 WIN_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp) 724 { 725 SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); 726 SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata; 727 HDC hdc; 728 BOOL succeeded = FALSE; 729 730 hdc = CreateDC(data->DeviceName, NULL, NULL, NULL); 731 if (hdc) { 732 succeeded = GetDeviceGammaRamp(hdc, (LPVOID)ramp); 733 if (!succeeded) { 734 WIN_SetError("GetDeviceGammaRamp()"); 735 } 736 DeleteDC(hdc); 737 } 738 return succeeded ? 0 : -1; 739 } 740 741 void 742 WIN_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed) 743 { 744 WIN_UpdateClipCursor(window); 745 746 if (window->flags & SDL_WINDOW_FULLSCREEN) { 747 UINT flags = SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOSIZE; 748 749 if (!(window->flags & SDL_WINDOW_SHOWN)) { 750 flags |= SWP_NOACTIVATE; 751 } 752 WIN_SetWindowPositionInternal(_this, window, flags); 753 } 754 } 755 756 void 757 WIN_DestroyWindow(_THIS, SDL_Window * window) 758 { 759 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 760 761 if (data) { 762 ReleaseDC(data->hwnd, data->hdc); 763 RemoveProp(data->hwnd, TEXT("SDL_WindowData")); 764 if (data->created) { 765 DestroyWindow(data->hwnd); 766 if (data->parent) { 767 DestroyWindow(data->parent); 768 } 769 } else { 770 /* Restore any original event handler... */ 771 if (data->wndproc != NULL) { 772 #ifdef GWLP_WNDPROC 773 SetWindowLongPtr(data->hwnd, GWLP_WNDPROC, 774 (LONG_PTR) data->wndproc); 775 #else 776 SetWindowLong(data->hwnd, GWL_WNDPROC, 777 (LONG_PTR) data->wndproc); 778 #endif 779 } 780 } 781 SDL_free(data); 782 } 783 window->driverdata = NULL; 784 } 785 786 SDL_bool 787 WIN_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info) 788 { 789 const SDL_WindowData *data = (const SDL_WindowData *) window->driverdata; 790 if (info->version.major <= SDL_MAJOR_VERSION) { 791 int versionnum = SDL_VERSIONNUM(info->version.major, info->version.minor, info->version.patch); 792 793 info->subsystem = SDL_SYSWM_WINDOWS; 794 info->info.win.window = data->hwnd; 795 796 if (versionnum >= SDL_VERSIONNUM(2, 0, 4)) { 797 info->info.win.hdc = data->hdc; 798 } 799 800 if (versionnum >= SDL_VERSIONNUM(2, 0, 5)) { 801 info->info.win.hinstance = data->hinstance; 802 } 803 804 return SDL_TRUE; 805 } else { 806 SDL_SetError("Application not compiled with SDL %d.%d", 807 SDL_MAJOR_VERSION, SDL_MINOR_VERSION); 808 return SDL_FALSE; 809 } 810 } 811 812 /* 813 * Creates a HelperWindow used for DirectInput. 814 */ 815 int 816 SDL_HelperWindowCreate(void) 817 { 818 HINSTANCE hInstance = GetModuleHandle(NULL); 819 WNDCLASS wce; 820 821 /* Make sure window isn't created twice. */ 822 if (SDL_HelperWindow != NULL) { 823 return 0; 824 } 825 826 /* Create the class. */ 827 SDL_zero(wce); 828 wce.lpfnWndProc = DefWindowProc; 829 wce.lpszClassName = (LPCWSTR) SDL_HelperWindowClassName; 830 wce.hInstance = hInstance; 831 832 /* Register the class. */ 833 SDL_HelperWindowClass = RegisterClass(&wce); 834 if (SDL_HelperWindowClass == 0 && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) { 835 return WIN_SetError("Unable to create Helper Window Class"); 836 } 837 838 /* Create the window. */ 839 SDL_HelperWindow = CreateWindowEx(0, SDL_HelperWindowClassName, 840 SDL_HelperWindowName, 841 WS_OVERLAPPED, CW_USEDEFAULT, 842 CW_USEDEFAULT, CW_USEDEFAULT, 843 CW_USEDEFAULT, HWND_MESSAGE, NULL, 844 hInstance, NULL); 845 if (SDL_HelperWindow == NULL) { 846 UnregisterClass(SDL_HelperWindowClassName, hInstance); 847 return WIN_SetError("Unable to create Helper Window"); 848 } 849 850 return 0; 851 } 852 853 854 /* 855 * Destroys the HelperWindow previously created with SDL_HelperWindowCreate. 856 */ 857 void 858 SDL_HelperWindowDestroy(void) 859 { 860 HINSTANCE hInstance = GetModuleHandle(NULL); 861 862 /* Destroy the window. */ 863 if (SDL_HelperWindow != NULL) { 864 if (DestroyWindow(SDL_HelperWindow) == 0) { 865 WIN_SetError("Unable to destroy Helper Window"); 866 return; 867 } 868 SDL_HelperWindow = NULL; 869 } 870 871 /* Unregister the class. */ 872 if (SDL_HelperWindowClass != 0) { 873 if ((UnregisterClass(SDL_HelperWindowClassName, hInstance)) == 0) { 874 WIN_SetError("Unable to destroy Helper Window Class"); 875 return; 876 } 877 SDL_HelperWindowClass = 0; 878 } 879 } 880 881 void WIN_OnWindowEnter(_THIS, SDL_Window * window) 882 { 883 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 884 885 if (!data || !data->hwnd) { 886 /* The window wasn't fully initialized */ 887 return; 888 } 889 890 if (window->flags & SDL_WINDOW_ALWAYS_ON_TOP) { 891 WIN_SetWindowPositionInternal(_this, window, SWP_NOCOPYBITS | SWP_NOSIZE | SWP_NOACTIVATE); 892 } 893 894 #ifdef WM_MOUSELEAVE 895 { 896 TRACKMOUSEEVENT trackMouseEvent; 897 898 trackMouseEvent.cbSize = sizeof(TRACKMOUSEEVENT); 899 trackMouseEvent.dwFlags = TME_LEAVE; 900 trackMouseEvent.hwndTrack = data->hwnd; 901 902 TrackMouseEvent(&trackMouseEvent); 903 } 904 #endif /* WM_MOUSELEAVE */ 905 } 906 907 void 908 WIN_UpdateClipCursor(SDL_Window *window) 909 { 910 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 911 SDL_Mouse *mouse = SDL_GetMouse(); 912 RECT rect, clipped_rect; 913 914 if (data->in_title_click || data->focus_click_pending) { 915 return; 916 } 917 if (data->skip_update_clipcursor) { 918 return; 919 } 920 if (!GetClipCursor(&clipped_rect)) { 921 return; 922 } 923 924 if ((mouse->relative_mode || (window->flags & SDL_WINDOW_INPUT_GRABBED)) && 925 (window->flags & SDL_WINDOW_INPUT_FOCUS)) { 926 if (mouse->relative_mode && !mouse->relative_mode_warp) { 927 if (GetWindowRect(data->hwnd, &rect)) { 928 LONG cx, cy; 929 930 cx = (rect.left + rect.right) / 2; 931 cy = (rect.top + rect.bottom) / 2; 932 933 /* Make an absurdly small clip rect */ 934 rect.left = cx - 1; 935 rect.right = cx + 1; 936 rect.top = cy - 1; 937 rect.bottom = cy + 1; 938 939 if (SDL_memcmp(&rect, &clipped_rect, sizeof(rect)) != 0) { 940 if (ClipCursor(&rect)) { 941 data->cursor_clipped_rect = rect; 942 } 943 } 944 } 945 } else { 946 if (GetClientRect(data->hwnd, &rect) && !IsRectEmpty(&rect)) { 947 ClientToScreen(data->hwnd, (LPPOINT) & rect); 948 ClientToScreen(data->hwnd, (LPPOINT) & rect + 1); 949 if (SDL_memcmp(&rect, &clipped_rect, sizeof(rect)) != 0) { 950 if (ClipCursor(&rect)) { 951 data->cursor_clipped_rect = rect; 952 } 953 } 954 } 955 } 956 } else { 957 POINT first, second; 958 959 first.x = clipped_rect.left; 960 first.y = clipped_rect.top; 961 second.x = clipped_rect.right - 1; 962 second.y = clipped_rect.bottom - 1; 963 if (PtInRect(&data->cursor_clipped_rect, first) && 964 PtInRect(&data->cursor_clipped_rect, second)) { 965 ClipCursor(NULL); 966 SDL_zero(data->cursor_clipped_rect); 967 } 968 } 969 data->last_updated_clipcursor = SDL_GetTicks(); 970 } 971 972 int 973 WIN_SetWindowHitTest(SDL_Window *window, SDL_bool enabled) 974 { 975 return 0; /* just succeed, the real work is done elsewhere. */ 976 } 977 978 int 979 WIN_SetWindowOpacity(_THIS, SDL_Window * window, float opacity) 980 { 981 const SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 982 const HWND hwnd = data->hwnd; 983 const LONG style = GetWindowLong(hwnd, GWL_EXSTYLE); 984 985 SDL_assert(style != 0); 986 987 if (opacity == 1.0f) { 988 /* want it fully opaque, just mark it unlayered if necessary. */ 989 if (style & WS_EX_LAYERED) { 990 if (SetWindowLong(hwnd, GWL_EXSTYLE, style & ~WS_EX_LAYERED) == 0) { 991 return WIN_SetError("SetWindowLong()"); 992 } 993 } 994 } else { 995 const BYTE alpha = (BYTE) ((int) (opacity * 255.0f)); 996 /* want it transparent, mark it layered if necessary. */ 997 if ((style & WS_EX_LAYERED) == 0) { 998 if (SetWindowLong(hwnd, GWL_EXSTYLE, style | WS_EX_LAYERED) == 0) { 999 return WIN_SetError("SetWindowLong()"); 1000 } 1001 } 1002 1003 if (SetLayeredWindowAttributes(hwnd, 0, alpha, LWA_ALPHA) == 0) { 1004 return WIN_SetError("SetLayeredWindowAttributes()"); 1005 } 1006 } 1007 1008 return 0; 1009 } 1010 1011 void 1012 WIN_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept) 1013 { 1014 const SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 1015 DragAcceptFiles(data->hwnd, accept ? TRUE : FALSE); 1016 } 1017 1018 #endif /* SDL_VIDEO_DRIVER_WINDOWS */ 1019 1020 /* vi: set ts=4 sw=4 expandtab: */