winmultiwindowwndproc.c (41902B)
1 /* 2 *Copyright (C) 1994-2000 The XFree86 Project, Inc. All Rights Reserved. 3 *Copyright (C) Colin Harrison 2005-2008 4 * 5 *Permission is hereby granted, free of charge, to any person obtaining 6 * a copy of this software and associated documentation files (the 7 *"Software"), to deal in the Software without restriction, including 8 *without limitation the rights to use, copy, modify, merge, publish, 9 *distribute, sublicense, and/or sell copies of the Software, and to 10 *permit persons to whom the Software is furnished to do so, subject to 11 *the following conditions: 12 * 13 *The above copyright notice and this permission notice shall be 14 *included in all copies or substantial portions of the Software. 15 * 16 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 *EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 *MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 *NONINFRINGEMENT. IN NO EVENT SHALL THE XFREE86 PROJECT BE LIABLE FOR 20 *ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 21 *CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 *WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 * 24 *Except as contained in this notice, the name of the XFree86 Project 25 *shall not be used in advertising or otherwise to promote the sale, use 26 *or other dealings in this Software without prior written authorization 27 *from the XFree86 Project. 28 * 29 * Authors: Kensuke Matsuzaki 30 * Earle F. Philhower, III 31 * Harold L Hunt II 32 * Colin Harrison 33 */ 34 35 #ifdef HAVE_XWIN_CONFIG_H 36 #include <xwin-config.h> 37 #endif 38 39 #include "win.h" 40 #include "dixevents.h" 41 #include "winmultiwindowclass.h" 42 #include "winprefs.h" 43 #include "winmsg.h" 44 #include "inputstr.h" 45 #include <dwmapi.h> 46 47 #ifndef WM_DWMCOMPOSITIONCHANGED 48 #define WM_DWMCOMPOSITIONCHANGED 0x031e 49 #endif 50 51 extern void winUpdateWindowPosition(HWND hWnd, HWND * zstyle); 52 53 /* 54 * Local globals 55 */ 56 57 static UINT_PTR g_uipMousePollingTimerID = 0; 58 59 /* 60 * Constant defines 61 */ 62 63 #define WIN_MULTIWINDOW_SHAPE YES 64 65 /* 66 * ConstrainSize - Taken from TWM sources - Respects hints for sizing 67 */ 68 #define makemult(a,b) ((b==1) ? (a) : (((int)((a)/(b))) * (b)) ) 69 static void 70 ConstrainSize(WinXSizeHints hints, int *widthp, int *heightp) 71 { 72 int minWidth, minHeight, maxWidth, maxHeight, xinc, yinc, delta; 73 int baseWidth, baseHeight; 74 int dwidth = *widthp, dheight = *heightp; 75 76 if (hints.flags & PMinSize) { 77 minWidth = hints.min_width; 78 minHeight = hints.min_height; 79 } 80 else if (hints.flags & PBaseSize) { 81 minWidth = hints.base_width; 82 minHeight = hints.base_height; 83 } 84 else 85 minWidth = minHeight = 1; 86 87 if (hints.flags & PBaseSize) { 88 baseWidth = hints.base_width; 89 baseHeight = hints.base_height; 90 } 91 else if (hints.flags & PMinSize) { 92 baseWidth = hints.min_width; 93 baseHeight = hints.min_height; 94 } 95 else 96 baseWidth = baseHeight = 0; 97 98 if (hints.flags & PMaxSize) { 99 maxWidth = hints.max_width; 100 maxHeight = hints.max_height; 101 } 102 else { 103 maxWidth = MAXINT; 104 maxHeight = MAXINT; 105 } 106 107 if (hints.flags & PResizeInc) { 108 xinc = hints.width_inc; 109 yinc = hints.height_inc; 110 } 111 else 112 xinc = yinc = 1; 113 114 /* 115 * First, clamp to min and max values 116 */ 117 if (dwidth < minWidth) 118 dwidth = minWidth; 119 if (dheight < minHeight) 120 dheight = minHeight; 121 122 if (dwidth > maxWidth) 123 dwidth = maxWidth; 124 if (dheight > maxHeight) 125 dheight = maxHeight; 126 127 /* 128 * Second, fit to base + N * inc 129 */ 130 dwidth = ((dwidth - baseWidth) / xinc * xinc) + baseWidth; 131 dheight = ((dheight - baseHeight) / yinc * yinc) + baseHeight; 132 133 /* 134 * Third, adjust for aspect ratio 135 */ 136 137 /* 138 * The math looks like this: 139 * 140 * minAspectX dwidth maxAspectX 141 * ---------- <= ------- <= ---------- 142 * minAspectY dheight maxAspectY 143 * 144 * If that is multiplied out, then the width and height are 145 * invalid in the following situations: 146 * 147 * minAspectX * dheight > minAspectY * dwidth 148 * maxAspectX * dheight < maxAspectY * dwidth 149 * 150 */ 151 152 if (hints.flags & PAspect) { 153 if (hints.min_aspect.x * dheight > hints.min_aspect.y * dwidth) { 154 delta = 155 makemult(hints.min_aspect.x * dheight / hints.min_aspect.y - 156 dwidth, xinc); 157 if (dwidth + delta <= maxWidth) 158 dwidth += delta; 159 else { 160 delta = 161 makemult(dheight - 162 dwidth * hints.min_aspect.y / hints.min_aspect.x, 163 yinc); 164 if (dheight - delta >= minHeight) 165 dheight -= delta; 166 } 167 } 168 169 if (hints.max_aspect.x * dheight < hints.max_aspect.y * dwidth) { 170 delta = 171 makemult(dwidth * hints.max_aspect.y / hints.max_aspect.x - 172 dheight, yinc); 173 if (dheight + delta <= maxHeight) 174 dheight += delta; 175 else { 176 delta = 177 makemult(dwidth - 178 hints.max_aspect.x * dheight / hints.max_aspect.y, 179 xinc); 180 if (dwidth - delta >= minWidth) 181 dwidth -= delta; 182 } 183 } 184 } 185 186 /* Return computed values */ 187 *widthp = dwidth; 188 *heightp = dheight; 189 } 190 191 #undef makemult 192 193 /* 194 * ValidateSizing - Ensures size request respects hints 195 */ 196 static int 197 ValidateSizing(HWND hwnd, WindowPtr pWin, WPARAM wParam, LPARAM lParam) 198 { 199 WinXSizeHints sizeHints; 200 RECT *rect; 201 int iWidth, iHeight; 202 RECT rcClient, rcWindow; 203 int iBorderWidthX, iBorderWidthY; 204 205 /* Invalid input checking */ 206 if (pWin == NULL || lParam == 0) 207 return FALSE; 208 209 /* No size hints, no checking */ 210 if (!winMultiWindowGetWMNormalHints(pWin, &sizeHints)) 211 return FALSE; 212 213 /* Avoid divide-by-zero */ 214 if (sizeHints.flags & PResizeInc) { 215 if (sizeHints.width_inc == 0) 216 sizeHints.width_inc = 1; 217 if (sizeHints.height_inc == 0) 218 sizeHints.height_inc = 1; 219 } 220 221 rect = (RECT *) lParam; 222 223 iWidth = rect->right - rect->left; 224 iHeight = rect->bottom - rect->top; 225 226 /* Now remove size of any borders and title bar */ 227 GetClientRect(hwnd, &rcClient); 228 GetWindowRect(hwnd, &rcWindow); 229 iBorderWidthX = 230 (rcWindow.right - rcWindow.left) - (rcClient.right - rcClient.left); 231 iBorderWidthY = 232 (rcWindow.bottom - rcWindow.top) - (rcClient.bottom - rcClient.top); 233 iWidth -= iBorderWidthX; 234 iHeight -= iBorderWidthY; 235 236 /* Constrain the size to legal values */ 237 ConstrainSize(sizeHints, &iWidth, &iHeight); 238 239 /* Add back the size of borders and title bar */ 240 iWidth += iBorderWidthX; 241 iHeight += iBorderWidthY; 242 243 /* Adjust size according to where we're dragging from */ 244 switch (wParam) { 245 case WMSZ_TOP: 246 case WMSZ_TOPRIGHT: 247 case WMSZ_BOTTOM: 248 case WMSZ_BOTTOMRIGHT: 249 case WMSZ_RIGHT: 250 rect->right = rect->left + iWidth; 251 break; 252 default: 253 rect->left = rect->right - iWidth; 254 break; 255 } 256 switch (wParam) { 257 case WMSZ_BOTTOM: 258 case WMSZ_BOTTOMRIGHT: 259 case WMSZ_BOTTOMLEFT: 260 case WMSZ_RIGHT: 261 case WMSZ_LEFT: 262 rect->bottom = rect->top + iHeight; 263 break; 264 default: 265 rect->top = rect->bottom - iHeight; 266 break; 267 } 268 return TRUE; 269 } 270 271 extern Bool winInDestroyWindowsWindow; 272 static Bool winInRaiseWindow = FALSE; 273 static void 274 winRaiseWindow(WindowPtr pWin) 275 { 276 if (!winInDestroyWindowsWindow && !winInRaiseWindow) { 277 BOOL oldstate = winInRaiseWindow; 278 XID vlist[1] = { 0 }; 279 winInRaiseWindow = TRUE; 280 /* Call configure window directly to make sure it gets processed 281 * in time 282 */ 283 ConfigureWindow(pWin, CWStackMode, vlist, serverClient); 284 winInRaiseWindow = oldstate; 285 } 286 } 287 288 static 289 void 290 winStartMousePolling(winPrivScreenPtr s_pScreenPriv) 291 { 292 /* 293 * Timer to poll mouse position. This is needed to make 294 * programs like xeyes follow the mouse properly when the 295 * mouse pointer is outside of any X window. 296 */ 297 if (g_uipMousePollingTimerID == 0) 298 g_uipMousePollingTimerID = SetTimer(s_pScreenPriv->hwndScreen, 299 WIN_POLLING_MOUSE_TIMER_ID, 300 MOUSE_POLLING_INTERVAL, NULL); 301 } 302 303 /* Undocumented */ 304 typedef struct _ACCENTPOLICY 305 { 306 ULONG AccentState; 307 ULONG AccentFlags; 308 ULONG GradientColor; 309 ULONG AnimationId; 310 } ACCENTPOLICY; 311 312 #define ACCENT_ENABLE_BLURBEHIND 3 313 314 typedef struct _WINCOMPATTR 315 { 316 DWORD attribute; 317 PVOID pData; 318 ULONG dataSize; 319 } WINCOMPATTR; 320 321 #define WCA_ACCENT_POLICY 19 322 323 typedef WINBOOL WINAPI (*PFNSETWINDOWCOMPOSITIONATTRIBUTE)(HWND, WINCOMPATTR *); 324 325 static void 326 CheckForAlpha(HWND hWnd, WindowPtr pWin, winScreenInfo *pScreenInfo) 327 { 328 /* Check (once) which API we should use */ 329 static Bool doOnce = TRUE; 330 static PFNSETWINDOWCOMPOSITIONATTRIBUTE pSetWindowCompositionAttribute = NULL; 331 static Bool useDwmEnableBlurBehindWindow = FALSE; 332 333 if (doOnce) 334 { 335 OSVERSIONINFOEX osvi = {0}; 336 osvi.dwOSVersionInfoSize = sizeof(osvi); 337 GetVersionEx((LPOSVERSIONINFO)&osvi); 338 339 /* SetWindowCompositionAttribute() exists on Windows 7 and later, 340 but doesn't work for this purpose, so first check for Windows 10 341 or later */ 342 if (osvi.dwMajorVersion >= 10) 343 { 344 HMODULE hUser32 = GetModuleHandle("user32"); 345 346 if (hUser32) 347 pSetWindowCompositionAttribute = (PFNSETWINDOWCOMPOSITIONATTRIBUTE) GetProcAddress(hUser32, "SetWindowCompositionAttribute"); 348 winDebug("SetWindowCompositionAttribute %s\n", pSetWindowCompositionAttribute ? "found" : "not found"); 349 } 350 /* On Windows 7 and Windows Vista, use DwmEnableBlurBehindWindow() */ 351 else if ((osvi.dwMajorVersion == 6) && (osvi.dwMinorVersion <= 1)) 352 { 353 useDwmEnableBlurBehindWindow = TRUE; 354 } 355 /* On Windows 8 and Windows 8.1, using the alpha channel on those 356 seems near impossible, so we don't do anything. */ 357 358 doOnce = FALSE; 359 } 360 361 /* alpha-channel use is wanted */ 362 if (!g_fCompositeAlpha || !pScreenInfo->fCompositeWM) 363 return; 364 365 /* Image has alpha ... */ 366 if (pWin->drawable.depth != 32) 367 return; 368 369 /* ... and we can do something useful with it? */ 370 if (pSetWindowCompositionAttribute) 371 { 372 WINBOOL rc; 373 /* Use the (undocumented) SetWindowCompositionAttribute, if 374 available, to turn on alpha channel use on Windows 10. */ 375 ACCENTPOLICY policy = { ACCENT_ENABLE_BLURBEHIND, 0, 0, 0 } ; 376 WINCOMPATTR data = { WCA_ACCENT_POLICY, &policy, sizeof(ACCENTPOLICY) }; 377 378 /* This turns on DWM looking at the alpha-channel of this window */ 379 winDebug("enabling alpha for XID %08x hWnd %p, using SetWindowCompositionAttribute()\n", (unsigned int)pWin->drawable.id, hWnd); 380 rc = pSetWindowCompositionAttribute(hWnd, &data); 381 if (!rc) 382 ErrorF("SetWindowCompositionAttribute failed: %d\n", (int)GetLastError()); 383 } 384 else if (useDwmEnableBlurBehindWindow) 385 { 386 HRESULT rc; 387 WINBOOL enabled; 388 389 rc = DwmIsCompositionEnabled(&enabled); 390 if ((rc == S_OK) && enabled) 391 { 392 /* Use DwmEnableBlurBehindWindow, to turn on alpha channel 393 use on Windows Vista and Windows 7 */ 394 DWM_BLURBEHIND bbh; 395 bbh.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION | DWM_BB_TRANSITIONONMAXIMIZED; 396 bbh.fEnable = TRUE; 397 bbh.hRgnBlur = NULL; 398 bbh.fTransitionOnMaximized = TRUE; /* What does this do ??? */ 399 400 /* This terribly-named function actually controls if DWM 401 looks at the alpha channel of this window */ 402 winDebug("enabling alpha for XID %08x hWnd %p, using DwmEnableBlurBehindWindow()\n", (unsigned int)pWin->drawable.id, hWnd); 403 rc = DwmEnableBlurBehindWindow(hWnd, &bbh); 404 if (rc != S_OK) 405 ErrorF("DwmEnableBlurBehindWindow failed: %x, %d\n", (int)rc, (int)GetLastError()); 406 } 407 } 408 } 409 410 /* 411 * winTopLevelWindowProc - Window procedure for all top-level Windows windows. 412 */ 413 414 LRESULT CALLBACK 415 winTopLevelWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 416 { 417 POINT ptMouse; 418 PAINTSTRUCT ps; 419 WindowPtr pWin = NULL; 420 winPrivWinPtr pWinPriv = NULL; 421 ScreenPtr s_pScreen = NULL; 422 winPrivScreenPtr s_pScreenPriv = NULL; 423 winScreenInfo *s_pScreenInfo = NULL; 424 HWND hwndScreen = NULL; 425 DrawablePtr pDraw = NULL; 426 winWMMessageRec wmMsg; 427 Bool fWMMsgInitialized = FALSE; 428 static Bool s_fTracking = FALSE; 429 Bool needRestack = FALSE; 430 LRESULT ret; 431 static Bool hasEnteredSizeMove = FALSE; 432 433 #if CYGDEBUG 434 winDebugWin32Message("winTopLevelWindowProc", hwnd, message, wParam, 435 lParam); 436 #endif 437 438 /* 439 If this is WM_CREATE, set up the Windows window properties which point to 440 X window information, before we populate local convenience variables... 441 */ 442 if (message == WM_CREATE) { 443 SetProp(hwnd, 444 WIN_WINDOW_PROP, 445 (HANDLE) ((LPCREATESTRUCT) lParam)->lpCreateParams); 446 SetProp(hwnd, 447 WIN_WID_PROP, 448 (HANDLE) (INT_PTR)winGetWindowID(((LPCREATESTRUCT) lParam)-> 449 lpCreateParams)); 450 } 451 452 /* Check if the Windows window property for our X window pointer is valid */ 453 if ((pWin = GetProp(hwnd, WIN_WINDOW_PROP)) != NULL) { 454 /* Our X window pointer is valid */ 455 456 /* Get pointers to the drawable and the screen */ 457 pDraw = &pWin->drawable; 458 s_pScreen = pWin->drawable.pScreen; 459 460 /* Get a pointer to our window privates */ 461 pWinPriv = winGetWindowPriv(pWin); 462 463 /* Get pointers to our screen privates and screen info */ 464 s_pScreenPriv = pWinPriv->pScreenPriv; 465 s_pScreenInfo = s_pScreenPriv->pScreenInfo; 466 467 /* Get the handle for our screen-sized window */ 468 hwndScreen = s_pScreenPriv->hwndScreen; 469 470 /* */ 471 wmMsg.msg = 0; 472 wmMsg.hwndWindow = hwnd; 473 wmMsg.iWindow = (Window) (INT_PTR) GetProp(hwnd, WIN_WID_PROP); 474 475 wmMsg.iX = pDraw->x; 476 wmMsg.iY = pDraw->y; 477 wmMsg.iWidth = pDraw->width; 478 wmMsg.iHeight = pDraw->height; 479 480 fWMMsgInitialized = TRUE; 481 482 #if 0 483 /* 484 * Print some debugging information 485 */ 486 487 ErrorF("hWnd %08X\n", hwnd); 488 ErrorF("pWin %08X\n", pWin); 489 ErrorF("pDraw %08X\n", pDraw); 490 ErrorF("\ttype %08X\n", pWin->drawable.type); 491 ErrorF("\tclass %08X\n", pWin->drawable.class); 492 ErrorF("\tdepth %08X\n", pWin->drawable.depth); 493 ErrorF("\tbitsPerPixel %08X\n", pWin->drawable.bitsPerPixel); 494 ErrorF("\tid %08X\n", pWin->drawable.id); 495 ErrorF("\tx %08X\n", pWin->drawable.x); 496 ErrorF("\ty %08X\n", pWin->drawable.y); 497 ErrorF("\twidth %08X\n", pWin->drawable.width); 498 ErrorF("\thenght %08X\n", pWin->drawable.height); 499 ErrorF("\tpScreen %08X\n", pWin->drawable.pScreen); 500 ErrorF("\tserialNumber %08X\n", pWin->drawable.serialNumber); 501 ErrorF("g_iWindowPrivateKey %p\n", g_iWindowPrivateKey); 502 ErrorF("pWinPriv %08X\n", pWinPriv); 503 ErrorF("s_pScreenPriv %08X\n", s_pScreenPriv); 504 ErrorF("s_pScreenInfo %08X\n", s_pScreenInfo); 505 ErrorF("hwndScreen %08X\n", hwndScreen); 506 #endif 507 } 508 509 /* Branch on message type */ 510 switch (message) { 511 case WM_CREATE: 512 /* 513 * Make X windows' Z orders sync with Windows windows because 514 * there can be AlwaysOnTop windows overlapped on the window 515 * currently being created. 516 */ 517 winReorderWindowsMultiWindow(); 518 519 /* Fix a 'round title bar corner background should be transparent not black' problem when first painted */ 520 { 521 RECT rWindow; 522 HRGN hRgnWindow; 523 524 GetWindowRect(hwnd, &rWindow); 525 hRgnWindow = CreateRectRgnIndirect(&rWindow); 526 SetWindowRgn(hwnd, hRgnWindow, TRUE); 527 DeleteObject(hRgnWindow); 528 } 529 530 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) XMING_SIGNATURE); 531 532 CheckForAlpha(hwnd, pWin, s_pScreenInfo); 533 534 return 0; 535 536 case WM_INIT_SYS_MENU: 537 /* 538 * Add whatever the setup file wants to for this window 539 */ 540 SetupSysMenu(hwnd); 541 return 0; 542 543 case WM_SYSCOMMAND: 544 /* 545 * Any window menu items go through here 546 */ 547 if (HandleCustomWM_COMMAND(hwnd, LOWORD(wParam), s_pScreenPriv)) { 548 /* Don't pass customized menus to DefWindowProc */ 549 return 0; 550 } 551 if (wParam == SC_RESTORE || wParam == SC_MAXIMIZE) { 552 WINDOWPLACEMENT wndpl; 553 554 wndpl.length = sizeof(wndpl); 555 if (GetWindowPlacement(hwnd, &wndpl) && 556 wndpl.showCmd == SW_SHOWMINIMIZED) 557 needRestack = TRUE; 558 } 559 break; 560 561 case WM_INITMENU: 562 /* Checks/Unchecks any menu items before they are displayed */ 563 HandleCustomWM_INITMENU(hwnd, (HMENU)wParam); 564 break; 565 566 case WM_ERASEBKGND: 567 /* 568 * Pretend that we did erase the background but we don't care, 569 * since we repaint the entire region anyhow 570 * This avoids some flickering when resizing. 571 */ 572 return TRUE; 573 574 case WM_PAINT: 575 /* Only paint if our window handle is valid */ 576 if (hwnd == NULL) 577 break; 578 579 #ifdef XWIN_GLX_WINDOWS 580 if (pWinPriv->fWglUsed) { 581 /* 582 For regions which are being drawn by GL, the shadow framebuffer doesn't have the 583 correct bits, so don't bitblt from the shadow framebuffer 584 585 XXX: For now, just leave it alone, but ideally we want to send an expose event to 586 the window so it really redraws the affected region... 587 */ 588 BeginPaint(hwnd, &ps); 589 ValidateRect(hwnd, &(ps.rcPaint)); 590 EndPaint(hwnd, &ps); 591 } 592 else 593 #endif 594 /* Call the engine dependent repainter */ 595 if (*s_pScreenPriv->pwinBltExposedWindowRegion) 596 (*s_pScreenPriv->pwinBltExposedWindowRegion) (s_pScreen, pWin); 597 598 return 0; 599 600 case WM_MOUSEMOVE: 601 /* Unpack the client area mouse coordinates */ 602 ptMouse.x = GET_X_LPARAM(lParam); 603 ptMouse.y = GET_Y_LPARAM(lParam); 604 605 /* Translate the client area mouse coordinates to screen coordinates */ 606 ClientToScreen(hwnd, &ptMouse); 607 608 /* Screen Coords from (-X, -Y) -> Root Window (0, 0) */ 609 ptMouse.x -= GetSystemMetrics(SM_XVIRTUALSCREEN); 610 ptMouse.y -= GetSystemMetrics(SM_YVIRTUALSCREEN); 611 612 /* We can't do anything without privates */ 613 if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput) 614 break; 615 616 /* Has the mouse pointer crossed screens? */ 617 if (s_pScreen != miPointerGetScreen(g_pwinPointer)) 618 miPointerSetScreen(g_pwinPointer, s_pScreenInfo->dwScreen, 619 ptMouse.x - s_pScreenInfo->dwXOffset, 620 ptMouse.y - s_pScreenInfo->dwYOffset); 621 622 /* Are we tracking yet? */ 623 if (!s_fTracking) { 624 TRACKMOUSEEVENT tme; 625 626 /* Setup data structure */ 627 ZeroMemory(&tme, sizeof(tme)); 628 tme.cbSize = sizeof(tme); 629 tme.dwFlags = TME_LEAVE; 630 tme.hwndTrack = hwnd; 631 632 /* Call the tracking function */ 633 if (!TrackMouseEvent(&tme)) 634 ErrorF("winTopLevelWindowProc - TrackMouseEvent failed\n"); 635 636 /* Flag that we are tracking now */ 637 s_fTracking = TRUE; 638 } 639 640 /* Hide or show the Windows mouse cursor */ 641 if (g_fSoftwareCursor && g_fCursor) { 642 /* Hide Windows cursor */ 643 g_fCursor = FALSE; 644 ShowCursor(FALSE); 645 } 646 647 /* Kill the timer used to poll mouse events */ 648 if (g_uipMousePollingTimerID != 0) { 649 KillTimer(s_pScreenPriv->hwndScreen, WIN_POLLING_MOUSE_TIMER_ID); 650 g_uipMousePollingTimerID = 0; 651 } 652 653 /* Deliver absolute cursor position to X Server */ 654 winEnqueueMotion(ptMouse.x - s_pScreenInfo->dwXOffset, 655 ptMouse.y - s_pScreenInfo->dwYOffset); 656 657 return 0; 658 659 case WM_NCMOUSEMOVE: 660 /* 661 * We break instead of returning 0 since we need to call 662 * DefWindowProc to get the mouse cursor changes 663 * and min/max/close button highlighting in Windows XP. 664 * The Platform SDK says that you should return 0 if you 665 * process this message, but it fails to mention that you 666 * will give up any default functionality if you do return 0. 667 */ 668 669 /* We can't do anything without privates */ 670 if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput) 671 break; 672 673 /* Non-client mouse movement, show Windows cursor */ 674 if (g_fSoftwareCursor && !g_fCursor) { 675 g_fCursor = TRUE; 676 ShowCursor(TRUE); 677 } 678 679 winStartMousePolling(s_pScreenPriv); 680 681 break; 682 683 case WM_MOUSELEAVE: 684 /* Mouse has left our client area */ 685 686 /* Flag that we are no longer tracking */ 687 s_fTracking = FALSE; 688 689 /* Show the mouse cursor, if necessary */ 690 if (g_fSoftwareCursor && !g_fCursor) { 691 g_fCursor = TRUE; 692 ShowCursor(TRUE); 693 } 694 695 winStartMousePolling(s_pScreenPriv); 696 697 return 0; 698 699 case WM_LBUTTONDBLCLK: 700 case WM_LBUTTONDOWN: 701 if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput) 702 break; 703 g_fButton[0] = TRUE; 704 SetCapture(hwnd); 705 return winMouseButtonsHandle(s_pScreen, ButtonPress, Button1, wParam); 706 707 case WM_LBUTTONUP: 708 if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput) 709 break; 710 g_fButton[0] = FALSE; 711 ReleaseCapture(); 712 winStartMousePolling(s_pScreenPriv); 713 return winMouseButtonsHandle(s_pScreen, ButtonRelease, Button1, wParam); 714 715 case WM_MBUTTONDBLCLK: 716 case WM_MBUTTONDOWN: 717 if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput) 718 break; 719 g_fButton[1] = TRUE; 720 SetCapture(hwnd); 721 return winMouseButtonsHandle(s_pScreen, ButtonPress, Button2, wParam); 722 723 case WM_MBUTTONUP: 724 if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput) 725 break; 726 g_fButton[1] = FALSE; 727 ReleaseCapture(); 728 winStartMousePolling(s_pScreenPriv); 729 return winMouseButtonsHandle(s_pScreen, ButtonRelease, Button2, wParam); 730 731 case WM_RBUTTONDBLCLK: 732 case WM_RBUTTONDOWN: 733 if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput) 734 break; 735 g_fButton[2] = TRUE; 736 SetCapture(hwnd); 737 return winMouseButtonsHandle(s_pScreen, ButtonPress, Button3, wParam); 738 739 case WM_RBUTTONUP: 740 if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput) 741 break; 742 g_fButton[2] = FALSE; 743 ReleaseCapture(); 744 winStartMousePolling(s_pScreenPriv); 745 return winMouseButtonsHandle(s_pScreen, ButtonRelease, Button3, wParam); 746 747 case WM_XBUTTONDBLCLK: 748 case WM_XBUTTONDOWN: 749 if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput) 750 break; 751 SetCapture(hwnd); 752 return winMouseButtonsHandle(s_pScreen, ButtonPress, HIWORD(wParam) + 7, 753 wParam); 754 755 case WM_XBUTTONUP: 756 if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput) 757 break; 758 ReleaseCapture(); 759 winStartMousePolling(s_pScreenPriv); 760 return winMouseButtonsHandle(s_pScreen, ButtonRelease, 761 HIWORD(wParam) + 7, wParam); 762 763 case WM_MOUSEWHEEL: 764 if (SendMessage 765 (hwnd, WM_NCHITTEST, 0, 766 MAKELONG(GET_X_LPARAM(lParam), 767 GET_Y_LPARAM(lParam))) == HTCLIENT) { 768 /* Pass the message to the root window */ 769 SendMessage(hwndScreen, message, wParam, lParam); 770 return 0; 771 } 772 else 773 break; 774 775 case WM_MOUSEHWHEEL: 776 if (SendMessage 777 (hwnd, WM_NCHITTEST, 0, 778 MAKELONG(GET_X_LPARAM(lParam), 779 GET_Y_LPARAM(lParam))) == HTCLIENT) { 780 /* Pass the message to the root window */ 781 SendMessage(hwndScreen, message, wParam, lParam); 782 return 0; 783 } 784 else 785 break; 786 787 case WM_SETFOCUS: 788 if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput) 789 break; 790 791 { 792 /* Get the parent window for transient handling */ 793 HWND hParent = GetParent(hwnd); 794 795 if (hParent && IsIconic(hParent)) 796 ShowWindow(hParent, SW_RESTORE); 797 } 798 799 winRestoreModeKeyStates(); 800 801 /* Add the keyboard hook if possible */ 802 if (g_fKeyboardHookLL) 803 g_fKeyboardHookLL = winInstallKeyboardHookLL(); 804 return 0; 805 806 case WM_KILLFOCUS: 807 /* Pop any pressed keys since we are losing keyboard focus */ 808 winKeybdReleaseKeys(); 809 810 /* Remove our keyboard hook if it is installed */ 811 winRemoveKeyboardHookLL(); 812 813 /* Revert the X focus as well, but only if the Windows focus is going to another window */ 814 if (!wParam && pWin) 815 DeleteWindowFromAnyEvents(pWin, FALSE); 816 817 return 0; 818 819 case WM_SYSDEADCHAR: 820 case WM_DEADCHAR: 821 /* 822 * NOTE: We do nothing with WM_*CHAR messages, 823 * nor does the root window, so we can just toss these messages. 824 */ 825 return 0; 826 827 case WM_SYSKEYDOWN: 828 case WM_KEYDOWN: 829 830 /* 831 * Don't pass Alt-F4 key combo to root window, 832 * let Windows translate to WM_CLOSE and close this top-level window. 833 * 834 * NOTE: We purposely don't check the fUseWinKillKey setting because 835 * it should only apply to the key handling for the root window, 836 * not for top-level window-manager windows. 837 * 838 * ALSO NOTE: We do pass Ctrl-Alt-Backspace to the root window 839 * because that is a key combo that no X app should be expecting to 840 * receive, since it has historically been used to shutdown the X server. 841 * Passing Ctrl-Alt-Backspace to the root window preserves that 842 * behavior, assuming that -unixkill has been passed as a parameter. 843 */ 844 if (wParam == VK_F4 && (GetKeyState(VK_MENU) & 0x8000)) 845 break; 846 847 #if CYGWINDOWING_DEBUG 848 if (wParam == VK_ESCAPE) { 849 /* Place for debug: put any tests and dumps here */ 850 WINDOWPLACEMENT windPlace; 851 RECT rc; 852 LPRECT pRect; 853 854 windPlace.length = sizeof(WINDOWPLACEMENT); 855 GetWindowPlacement(hwnd, &windPlace); 856 pRect = &windPlace.rcNormalPosition; 857 ErrorF("\nCYGWINDOWING Dump:\n" 858 "\tdrawable: (%hd, %hd) - %hdx%hd\n", pDraw->x, 859 pDraw->y, pDraw->width, pDraw->height); 860 ErrorF("\twindPlace: (%d, %d) - %dx%d\n", (int)pRect->left, 861 (int)pRect->top, (int)(pRect->right - pRect->left), 862 (int)(pRect->bottom - pRect->top)); 863 if (GetClientRect(hwnd, &rc)) { 864 pRect = &rc; 865 ErrorF("\tClientRect: (%d, %d) - %dx%d\n", (int)pRect->left, 866 (int)pRect->top, (int)(pRect->right - pRect->left), 867 (int)(pRect->bottom - pRect->top)); 868 } 869 if (GetWindowRect(hwnd, &rc)) { 870 pRect = &rc; 871 ErrorF("\tWindowRect: (%d, %d) - %dx%d\n", (int)pRect->left, 872 (int)pRect->top, (int)(pRect->right - pRect->left), 873 (int)(pRect->bottom - pRect->top)); 874 } 875 ErrorF("\n"); 876 } 877 #endif 878 879 /* Pass the message to the root window */ 880 return winWindowProc(hwndScreen, message, wParam, lParam); 881 882 case WM_SYSKEYUP: 883 case WM_KEYUP: 884 885 /* Pass the message to the root window */ 886 return winWindowProc(hwndScreen, message, wParam, lParam); 887 888 case WM_HOTKEY: 889 890 /* Pass the message to the root window */ 891 SendMessage(hwndScreen, message, wParam, lParam); 892 return 0; 893 894 case WM_ACTIVATE: 895 896 /* Pass the message to the root window */ 897 SendMessage(hwndScreen, message, wParam, lParam); 898 899 if (LOWORD(wParam) != WA_INACTIVE) { 900 /* Raise the window to the top in Z order */ 901 /* ago: Activate does not mean putting it to front! */ 902 /* 903 wmMsg.msg = WM_WM_RAISE; 904 if (fWMMsgInitialized) 905 winSendMessageToWM (s_pScreenPriv->pWMInfo, &wmMsg); 906 */ 907 908 /* Tell our Window Manager thread to activate the window */ 909 wmMsg.msg = WM_WM_ACTIVATE; 910 if (fWMMsgInitialized) 911 if (!pWin || !pWin->overrideRedirect) /* for OOo menus */ 912 winSendMessageToWM(s_pScreenPriv->pWMInfo, &wmMsg); 913 } 914 /* Prevent the mouse wheel from stalling when another window is minimized */ 915 if (HIWORD(wParam) == 0 && LOWORD(wParam) == WA_ACTIVE && 916 (HWND) lParam != NULL && (HWND) lParam != GetParent(hwnd)) 917 SetFocus(hwnd); 918 return 0; 919 920 case WM_ACTIVATEAPP: 921 /* 922 * This message is also sent to the root window 923 * so we do nothing for individual multiwindow windows 924 */ 925 break; 926 927 case WM_CLOSE: 928 /* Remove AppUserModelID property */ 929 winSetAppUserModelID(hwnd, NULL); 930 /* Branch on if the window was killed in X already */ 931 if (pWinPriv->fXKilled) { 932 /* Window was killed, go ahead and destroy the window */ 933 DestroyWindow(hwnd); 934 } 935 else { 936 /* Tell our Window Manager thread to kill the window */ 937 wmMsg.msg = WM_WM_KILL; 938 if (fWMMsgInitialized) 939 winSendMessageToWM(s_pScreenPriv->pWMInfo, &wmMsg); 940 } 941 return 0; 942 943 case WM_DESTROY: 944 945 /* Branch on if the window was killed in X already */ 946 if (pWinPriv && !pWinPriv->fXKilled) { 947 ErrorF("winTopLevelWindowProc - WM_DESTROY - WM_WM_KILL\n"); 948 949 /* Tell our Window Manager thread to kill the window */ 950 wmMsg.msg = WM_WM_KILL; 951 if (fWMMsgInitialized) 952 winSendMessageToWM(s_pScreenPriv->pWMInfo, &wmMsg); 953 } 954 955 RemoveProp(hwnd, WIN_WINDOW_PROP); 956 RemoveProp(hwnd, WIN_WID_PROP); 957 RemoveProp(hwnd, WIN_NEEDMANAGE_PROP); 958 959 break; 960 961 case WM_MOVE: 962 /* Adjust the X Window to the moved Windows window */ 963 if (!hasEnteredSizeMove) 964 winAdjustXWindow(pWin, hwnd); 965 /* else: Wait for WM_EXITSIZEMOVE */ 966 return 0; 967 968 case WM_SHOWWINDOW: 969 /* Bail out if the window is being hidden */ 970 if (!wParam) 971 return 0; 972 973 /* */ 974 if (!pWin->overrideRedirect) { 975 HWND zstyle = HWND_NOTOPMOST; 976 977 /* Flag that this window needs to be made active when clicked */ 978 SetProp(hwnd, WIN_NEEDMANAGE_PROP, (HANDLE) 1); 979 980 /* Set the transient style flags */ 981 if (GetParent(hwnd)) 982 SetWindowLongPtr(hwnd, GWL_STYLE, 983 WS_POPUP | WS_OVERLAPPED | WS_SYSMENU | 984 WS_CLIPCHILDREN | WS_CLIPSIBLINGS); 985 /* Set the window standard style flags */ 986 else 987 SetWindowLongPtr(hwnd, GWL_STYLE, 988 (WS_POPUP | WS_OVERLAPPEDWINDOW | 989 WS_CLIPCHILDREN | WS_CLIPSIBLINGS) 990 & ~WS_CAPTION & ~WS_SIZEBOX); 991 992 winUpdateWindowPosition(hwnd, &zstyle); 993 994 { 995 WinXWMHints hints; 996 997 if (winMultiWindowGetWMHints(pWin, &hints)) { 998 /* 999 Give the window focus, unless it has an InputHint 1000 which is FALSE (this is used by e.g. glean to 1001 avoid every test window grabbing the focus) 1002 */ 1003 if (!((hints.flags & InputHint) && (!hints.input))) { 1004 SetForegroundWindow(hwnd); 1005 } 1006 } 1007 } 1008 wmMsg.msg = WM_WM_MAP_MANAGED; 1009 } 1010 else { /* It is an overridden window so make it top of Z stack */ 1011 1012 HWND forHwnd = GetForegroundWindow(); 1013 1014 #if CYGWINDOWING_DEBUG 1015 ErrorF("overridden window is shown\n"); 1016 #endif 1017 if (forHwnd != NULL) { 1018 if (GetWindowLongPtr(forHwnd, GWLP_USERDATA) & (LONG_PTR) 1019 XMING_SIGNATURE) { 1020 if (GetWindowLongPtr(forHwnd, GWL_EXSTYLE) & WS_EX_TOPMOST) 1021 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, 1022 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); 1023 else 1024 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, 1025 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); 1026 } 1027 } 1028 wmMsg.msg = WM_WM_MAP_UNMANAGED; 1029 } 1030 1031 /* Tell our Window Manager thread to map the window */ 1032 if (fWMMsgInitialized) 1033 winSendMessageToWM(s_pScreenPriv->pWMInfo, &wmMsg); 1034 1035 winStartMousePolling(s_pScreenPriv); 1036 1037 return 0; 1038 1039 case WM_SIZING: 1040 /* Need to legalize the size according to WM_NORMAL_HINTS */ 1041 /* for applications like xterm */ 1042 return ValidateSizing(hwnd, pWin, wParam, lParam); 1043 1044 case WM_WINDOWPOSCHANGED: 1045 { 1046 LPWINDOWPOS pWinPos = (LPWINDOWPOS) lParam; 1047 1048 if (!(pWinPos->flags & SWP_NOZORDER)) { 1049 #if CYGWINDOWING_DEBUG 1050 winDebug("\twindow z order was changed\n"); 1051 #endif 1052 if (pWinPos->hwndInsertAfter == HWND_TOP 1053 || pWinPos->hwndInsertAfter == HWND_TOPMOST 1054 || pWinPos->hwndInsertAfter == HWND_NOTOPMOST) { 1055 #if CYGWINDOWING_DEBUG 1056 winDebug("\traise to top\n"); 1057 #endif 1058 /* Raise the window to the top in Z order */ 1059 winRaiseWindow(pWin); 1060 } 1061 else if (pWinPos->hwndInsertAfter == HWND_BOTTOM) { 1062 } 1063 else { 1064 /* Check if this window is top of X windows. */ 1065 HWND hWndAbove = NULL; 1066 DWORD dwCurrentProcessID = GetCurrentProcessId(); 1067 DWORD dwWindowProcessID = 0; 1068 1069 for (hWndAbove = pWinPos->hwndInsertAfter; 1070 hWndAbove != NULL; 1071 hWndAbove = GetNextWindow(hWndAbove, GW_HWNDPREV)) { 1072 /* Ignore other XWin process's window */ 1073 GetWindowThreadProcessId(hWndAbove, &dwWindowProcessID); 1074 1075 if ((dwWindowProcessID == dwCurrentProcessID) 1076 && GetProp(hWndAbove, WIN_WINDOW_PROP) 1077 && !IsWindowVisible(hWndAbove) 1078 && !IsIconic(hWndAbove)) /* ignore minimized windows */ 1079 break; 1080 } 1081 /* If this is top of X windows in Windows stack, 1082 raise it in X stack. */ 1083 if (hWndAbove == NULL) { 1084 #if CYGWINDOWING_DEBUG 1085 winDebug("\traise to top\n"); 1086 #endif 1087 winRaiseWindow(pWin); 1088 } 1089 } 1090 } 1091 } 1092 /* 1093 * Pass the message to DefWindowProc to let the function 1094 * break down WM_WINDOWPOSCHANGED to WM_MOVE and WM_SIZE. 1095 */ 1096 break; 1097 1098 case WM_ENTERSIZEMOVE: 1099 hasEnteredSizeMove = TRUE; 1100 return 0; 1101 1102 case WM_EXITSIZEMOVE: 1103 /* Adjust the X Window to the moved Windows window */ 1104 hasEnteredSizeMove = FALSE; 1105 winAdjustXWindow(pWin, hwnd); 1106 return 0; 1107 1108 case WM_SIZE: 1109 /* see dix/window.c */ 1110 #if CYGWINDOWING_DEBUG 1111 { 1112 char buf[64]; 1113 1114 switch (wParam) { 1115 case SIZE_MINIMIZED: 1116 strcpy(buf, "SIZE_MINIMIZED"); 1117 break; 1118 case SIZE_MAXIMIZED: 1119 strcpy(buf, "SIZE_MAXIMIZED"); 1120 break; 1121 case SIZE_RESTORED: 1122 strcpy(buf, "SIZE_RESTORED"); 1123 break; 1124 default: 1125 strcpy(buf, "UNKNOWN_FLAG"); 1126 } 1127 ErrorF("winTopLevelWindowProc - WM_SIZE to %dx%d (%s)\n", 1128 (int) LOWORD(lParam), (int) HIWORD(lParam), buf); 1129 } 1130 #endif 1131 if (!hasEnteredSizeMove) { 1132 /* Adjust the X Window to the moved Windows window */ 1133 winAdjustXWindow(pWin, hwnd); 1134 } 1135 /* else: wait for WM_EXITSIZEMOVE */ 1136 return 0; /* end of WM_SIZE handler */ 1137 1138 case WM_STYLECHANGING: 1139 /* 1140 When the style changes, adjust the Windows window size so the client area remains the same size, 1141 and adjust the Windows window position so that the client area remains in the same place. 1142 */ 1143 { 1144 RECT newWinRect; 1145 DWORD dwExStyle; 1146 DWORD dwStyle; 1147 DWORD newStyle = ((STYLESTRUCT *) lParam)->styleNew; 1148 WINDOWINFO wi; 1149 1150 dwExStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE); 1151 dwStyle = GetWindowLongPtr(hwnd, GWL_STYLE); 1152 1153 winDebug("winTopLevelWindowProc - WM_STYLECHANGING from %08x %08x\n", 1154 (unsigned int)dwStyle, (unsigned int)dwExStyle); 1155 1156 if (wParam == GWL_EXSTYLE) 1157 dwExStyle = newStyle; 1158 1159 if (wParam == GWL_STYLE) 1160 dwStyle = newStyle; 1161 1162 winDebug("winTopLevelWindowProc - WM_STYLECHANGING to %08x %08x\n", 1163 (unsigned int)dwStyle, (unsigned int)dwExStyle); 1164 1165 /* Get client rect in screen coordinates */ 1166 wi.cbSize = sizeof(WINDOWINFO); 1167 GetWindowInfo(hwnd, &wi); 1168 1169 winDebug 1170 ("winTopLevelWindowProc - WM_STYLECHANGING client area {%d, %d, %d, %d}, {%d x %d}\n", 1171 (int)wi.rcClient.left, (int)wi.rcClient.top, (int)wi.rcClient.right, 1172 (int)wi.rcClient.bottom, (int)(wi.rcClient.right - wi.rcClient.left), 1173 (int)(wi.rcClient.bottom - wi.rcClient.top)); 1174 1175 newWinRect = wi.rcClient; 1176 if (!AdjustWindowRectEx(&newWinRect, dwStyle, FALSE, dwExStyle)) 1177 winDebug 1178 ("winTopLevelWindowProc - WM_STYLECHANGING AdjustWindowRectEx failed\n"); 1179 1180 winDebug 1181 ("winTopLevelWindowProc - WM_STYLECHANGING window area should be {%d, %d, %d, %d}, {%d x %d}\n", 1182 (int)newWinRect.left, (int)newWinRect.top, (int)newWinRect.right, 1183 (int)newWinRect.bottom, (int)(newWinRect.right - newWinRect.left), 1184 (int)(newWinRect.bottom - newWinRect.top)); 1185 1186 /* 1187 Style change hasn't happened yet, so we can't adjust the window size yet, as the winAdjustXWindow() 1188 which WM_SIZE does will use the current (unchanged) style. Instead make a note to change it when 1189 WM_STYLECHANGED is received... 1190 */ 1191 pWinPriv->hDwp = BeginDeferWindowPos(1); 1192 pWinPriv->hDwp = 1193 DeferWindowPos(pWinPriv->hDwp, hwnd, NULL, newWinRect.left, 1194 newWinRect.top, newWinRect.right - newWinRect.left, 1195 newWinRect.bottom - newWinRect.top, 1196 SWP_NOACTIVATE | SWP_NOZORDER); 1197 } 1198 return 0; 1199 1200 case WM_STYLECHANGED: 1201 { 1202 if (pWinPriv->hDwp) { 1203 EndDeferWindowPos(pWinPriv->hDwp); 1204 pWinPriv->hDwp = NULL; 1205 } 1206 winDebug("winTopLevelWindowProc - WM_STYLECHANGED done\n"); 1207 } 1208 return 0; 1209 1210 case WM_MOUSEACTIVATE: 1211 1212 /* Check if this window needs to be made active when clicked */ 1213 if (!GetProp(pWinPriv->hWnd, WIN_NEEDMANAGE_PROP)) { 1214 #if CYGMULTIWINDOW_DEBUG 1215 ErrorF("winTopLevelWindowProc - WM_MOUSEACTIVATE - " 1216 "MA_NOACTIVATE\n"); 1217 #endif 1218 1219 /* */ 1220 return MA_NOACTIVATE; 1221 } 1222 break; 1223 1224 case WM_SETCURSOR: 1225 if (LOWORD(lParam) == HTCLIENT) { 1226 if (!g_fSoftwareCursor) 1227 SetCursor(s_pScreenPriv->cursor.handle); 1228 return TRUE; 1229 } 1230 break; 1231 1232 1233 case WM_DWMCOMPOSITIONCHANGED: 1234 /* This message is only sent on Vista/W7 */ 1235 CheckForAlpha(hwnd, pWin, s_pScreenInfo); 1236 1237 return 0; 1238 default: 1239 break; 1240 } 1241 1242 ret = DefWindowProc(hwnd, message, wParam, lParam); 1243 /* 1244 * If the window was minized we get the stack change before the window is restored 1245 * and so it gets lost. Ensure there stacking order is correct. 1246 */ 1247 if (needRestack) 1248 winReorderWindowsMultiWindow(); 1249 return ret; 1250 }