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
ConstrainSize(WinXSizeHints hints,int * widthp,int * heightp)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
ValidateSizing(HWND hwnd,WindowPtr pWin,WPARAM wParam,LPARAM lParam)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
winRaiseWindow(WindowPtr pWin)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
winStartMousePolling(winPrivScreenPtr s_pScreenPriv)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
CheckForAlpha(HWND hWnd,WindowPtr pWin,winScreenInfo * pScreenInfo)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
winTopLevelWindowProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)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 }
1251