winwndproc.c revision 1b5d61b8
1/*
2 *Copyright (C) 1994-2000 The XFree86 Project, Inc. All Rights Reserved.
3 *
4 *Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 *"Software"), to deal in the Software without restriction, including
7 *without limitation the rights to use, copy, modify, merge, publish,
8 *distribute, sublicense, and/or sell copies of the Software, and to
9 *permit persons to whom the Software is furnished to do so, subject to
10 *the following conditions:
11 *
12 *The above copyright notice and this permission notice shall be
13 *included in all copies or substantial portions of the Software.
14 *
15 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 *EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 *MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 *NONINFRINGEMENT. IN NO EVENT SHALL THE XFREE86 PROJECT BE LIABLE FOR
19 *ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
20 *CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 *WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 *
23 *Except as contained in this notice, the name of the XFree86 Project
24 *shall not be used in advertising or otherwise to promote the sale, use
25 *or other dealings in this Software without prior written authorization
26 *from the XFree86 Project.
27 *
28 * Authors:	Dakshinamurthy Karra
29 *		Suhaib M Siddiqi
30 *		Peter Busch
31 *		Harold L Hunt II
32 *		MATSUZAKI Kensuke
33 */
34
35#ifdef HAVE_XWIN_CONFIG_H
36#include <xwin-config.h>
37#endif
38#include "win.h"
39#include <commctrl.h>
40#include "winprefs.h"
41#include "winconfig.h"
42#include "winmsg.h"
43#include "winmonitors.h"
44#include "inputstr.h"
45#include "winclipboard/winclipboard.h"
46
47/*
48 * Global variables
49 */
50
51Bool g_fCursor = TRUE;
52Bool g_fButton[3] = { FALSE, FALSE, FALSE };
53
54/*
55 * Called by winWakeupHandler
56 * Processes current Windows message
57 */
58
59LRESULT CALLBACK
60winWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
61{
62    static winPrivScreenPtr s_pScreenPriv = NULL;
63    static winScreenInfo *s_pScreenInfo = NULL;
64    static ScreenPtr s_pScreen = NULL;
65    static HWND s_hwndLastPrivates = NULL;
66    static Bool s_fTracking = FALSE;
67    static unsigned long s_ulServerGeneration = 0;
68    static UINT s_uTaskbarRestart = 0;
69    int iScanCode;
70    int i;
71
72#if CYGDEBUG
73    winDebugWin32Message("winWindowProc", hwnd, message, wParam, lParam);
74#endif
75
76    /* Watch for server regeneration */
77    if (g_ulServerGeneration != s_ulServerGeneration) {
78        /* Store new server generation */
79        s_ulServerGeneration = g_ulServerGeneration;
80    }
81
82    /* Only retrieve new privates pointers if window handle is null or changed */
83    if ((s_pScreenPriv == NULL || hwnd != s_hwndLastPrivates)
84        && (s_pScreenPriv = GetProp(hwnd, WIN_SCR_PROP)) != NULL) {
85#if CYGDEBUG
86        winDebug("winWindowProc - Setting privates handle\n");
87#endif
88        s_pScreenInfo = s_pScreenPriv->pScreenInfo;
89        s_pScreen = s_pScreenInfo->pScreen;
90        s_hwndLastPrivates = hwnd;
91    }
92    else if (s_pScreenPriv == NULL) {
93        /* For safety, handle case that should never happen */
94        s_pScreenInfo = NULL;
95        s_pScreen = NULL;
96        s_hwndLastPrivates = NULL;
97    }
98
99    /* Branch on message type */
100    switch (message) {
101    case WM_TRAYICON:
102        return winHandleIconMessage(hwnd, message, wParam, lParam,
103                                    s_pScreenPriv);
104
105    case WM_CREATE:
106#if CYGDEBUG
107        winDebug("winWindowProc - WM_CREATE\n");
108#endif
109
110        /*
111         * Add a property to our display window that references
112         * this screens' privates.
113         *
114         * This allows the window procedure to refer to the
115         * appropriate window DC and shadow DC for the window that
116         * it is processing.  We use this to repaint exposed
117         * areas of our display window.
118         */
119        s_pScreenPriv = ((LPCREATESTRUCT) lParam)->lpCreateParams;
120        s_pScreenInfo = s_pScreenPriv->pScreenInfo;
121        s_pScreen = s_pScreenInfo->pScreen;
122        s_hwndLastPrivates = hwnd;
123        s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
124        SetProp(hwnd, WIN_SCR_PROP, s_pScreenPriv);
125
126        /* Setup tray icon */
127        if (!s_pScreenInfo->fNoTrayIcon) {
128            /*
129             * NOTE: The WM_CREATE message is processed before CreateWindowEx
130             * returns, so s_pScreenPriv->hwndScreen is invalid at this point.
131             * We go ahead and copy our hwnd parameter over top of the screen
132             * privates hwndScreen so that we have a valid value for
133             * that member.  Otherwise, the tray icon will disappear
134             * the first time you move the mouse over top of it.
135             */
136
137            s_pScreenPriv->hwndScreen = hwnd;
138
139            winInitNotifyIcon(s_pScreenPriv);
140        }
141        return 0;
142
143    case WM_DISPLAYCHANGE:
144        /*
145           WM_DISPLAYCHANGE seems to be sent when the monitor layout or
146           any monitor's resolution or depth changes, but it's lParam and
147           wParam always indicate the resolution and bpp for the primary
148           monitor (so ignore that as we could be on any monitor...)
149         */
150
151        /* We cannot handle a display mode change during initialization */
152        if (s_pScreenInfo == NULL)
153            FatalError("winWindowProc - WM_DISPLAYCHANGE - The display "
154                       "mode changed while we were intializing.  This is "
155                       "very bad and unexpected.  Exiting.\n");
156
157        /*
158         * We do not care about display changes with
159         * fullscreen DirectDraw engines, because those engines set
160         * their own mode when they become active.
161         */
162        if (s_pScreenInfo->fFullScreen
163            && (s_pScreenInfo->dwEngine == WIN_SERVER_SHADOW_DDNL)) {
164            break;
165        }
166
167        ErrorF("winWindowProc - WM_DISPLAYCHANGE - new width: %d "
168               "new height: %d new bpp: %d\n",
169               LOWORD(lParam), HIWORD(lParam), (int)wParam);
170
171        /* 0 bpp has no defined meaning, ignore this message */
172        if (wParam == 0)
173            break;
174
175        /*
176         * Check for a disruptive change in depth.
177         * We can only display a message for a disruptive depth change,
178         * we cannot do anything to correct the situation.
179         */
180        /*
181           XXX: maybe we need to check if GetSystemMetrics(SM_SAMEDISPLAYFORMAT)
182           has changed as well...
183         */
184        if (s_pScreenInfo->dwBPP !=
185            GetDeviceCaps(s_pScreenPriv->hdcScreen, BITSPIXEL)) {
186            if (s_pScreenInfo->dwEngine == WIN_SERVER_SHADOW_DDNL) {
187                /* Cannot display the visual until the depth is restored */
188                ErrorF("winWindowProc - Disruptive change in depth\n");
189
190                /* Display depth change dialog */
191                winDisplayDepthChangeDialog(s_pScreenPriv);
192
193                /* Flag that we have an invalid screen depth */
194                s_pScreenPriv->fBadDepth = TRUE;
195
196                /* Minimize the display window */
197                ShowWindow(hwnd, SW_MINIMIZE);
198            }
199            else {
200                /* For GDI, performance may suffer until original depth is restored */
201                ErrorF
202                    ("winWindowProc - Performance may be non-optimal after change in depth\n");
203            }
204        }
205        else {
206            /* Flag that we have a valid screen depth */
207            s_pScreenPriv->fBadDepth = FALSE;
208        }
209
210        /*
211           If we could cheaply check if this WM_DISPLAYCHANGE change
212           affects the monitor(s) which this X screen is displayed on
213           then we should do so here.  For the moment, assume it does.
214           (this is probably usually the case so that might be an
215           overoptimization)
216         */
217        {
218            /*
219               In rootless modes which are monitor or virtual desktop size
220               use RandR to resize the X screen
221             */
222            if ((!s_pScreenInfo->fUserGaveHeightAndWidth) &&
223                (s_pScreenInfo->iResizeMode == resizeWithRandr) && (FALSE
224#ifdef XWIN_MULTIWINDOWEXTWM
225                                                                    ||
226                                                                    s_pScreenInfo->
227                                                                    fMWExtWM
228#endif
229                                                                    ||
230                                                                    s_pScreenInfo->
231                                                                    fRootless
232                                                                    ||
233                                                                    s_pScreenInfo->
234                                                                    fMultiWindow
235                )) {
236                DWORD dwWidth = 0, dwHeight = 0;
237
238                if (s_pScreenInfo->fMultipleMonitors) {
239                    /* resize to new virtual desktop size */
240                    dwWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
241                    dwHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
242                }
243                else {
244                    /* resize to new size of specified monitor */
245                    struct GetMonitorInfoData data;
246
247                    if (QueryMonitor(s_pScreenInfo->iMonitor, &data)) {
248                        if (data.bMonitorSpecifiedExists == TRUE) {
249                            dwWidth = data.monitorWidth;
250                            dwHeight = data.monitorHeight;
251                            /*
252                               XXX: monitor may have changed position,
253                               so we might need to update xinerama data
254                             */
255                        }
256                        else {
257                            ErrorF("Monitor number %d no longer exists!\n",
258                                   s_pScreenInfo->iMonitor);
259                        }
260                    }
261                }
262
263                /*
264                   XXX: probably a small bug here: we don't compute the work area
265                   and allow for task bar
266
267                   XXX: generally, we don't allow for the task bar being moved after
268                   the server is started
269                 */
270
271                /* Set screen size to match new size, if it is different to current */
272                if (((dwWidth != 0) && (dwHeight != 0)) &&
273                    ((s_pScreenInfo->dwWidth != dwWidth) ||
274                     (s_pScreenInfo->dwHeight != dwHeight))) {
275                    winDoRandRScreenSetSize(s_pScreen,
276                                            dwWidth,
277                                            dwHeight,
278                                            (dwWidth * 25.4) /
279                                            monitorResolution,
280                                            (dwHeight * 25.4) /
281                                            monitorResolution);
282                }
283            }
284            else {
285                /*
286                 * We can simply recreate the same-sized primary surface when
287                 * the display dimensions change.
288                 */
289
290                winDebug
291                    ("winWindowProc - WM_DISPLAYCHANGE - Releasing and recreating primary surface\n");
292
293                /* Release the old primary surface */
294                if (*s_pScreenPriv->pwinReleasePrimarySurface)
295                    (*s_pScreenPriv->pwinReleasePrimarySurface) (s_pScreen);
296
297                /* Create the new primary surface */
298                if (*s_pScreenPriv->pwinCreatePrimarySurface)
299                    (*s_pScreenPriv->pwinCreatePrimarySurface) (s_pScreen);
300            }
301        }
302
303        break;
304
305    case WM_SIZE:
306    {
307        SCROLLINFO si;
308        RECT rcWindow;
309        int iWidth, iHeight;
310
311#if CYGDEBUG
312        winDebug("winWindowProc - WM_SIZE\n");
313#endif
314
315        /* Break if we do not allow resizing */
316        if ((s_pScreenInfo->iResizeMode == resizeNotAllowed)
317            || !s_pScreenInfo->fDecoration
318#ifdef XWIN_MULTIWINDOWEXTWM
319            || s_pScreenInfo->fMWExtWM
320#endif
321            || s_pScreenInfo->fRootless
322            || s_pScreenInfo->fMultiWindow
323            || s_pScreenInfo->fFullScreen)
324            break;
325
326        /* No need to resize if we get minimized */
327        if (wParam == SIZE_MINIMIZED)
328            return 0;
329
330        ErrorF("winWindowProc - WM_SIZE - new client area w: %d h: %d\n",
331               LOWORD(lParam), HIWORD(lParam));
332
333        if (s_pScreenInfo->iResizeMode == resizeWithRandr) {
334            /* Actual resizing is done on WM_EXITSIZEMOVE */
335            return 0;
336        }
337
338        /* Otherwise iResizeMode == resizeWithScrollbars */
339
340        /*
341         * Get the size of the whole window, including client area,
342         * scrollbars, and non-client area decorations (caption, borders).
343         * We do this because we need to check if the client area
344         * without scrollbars is large enough to display the whole visual.
345         * The new client area size passed by lParam already subtracts
346         * the size of the scrollbars if they are currently displayed.
347         * So checking is LOWORD(lParam) == visual_width and
348         * HIWORD(lParam) == visual_height will never tell us to hide
349         * the scrollbars because the client area would always be too small.
350         * GetClientRect returns the same sizes given by lParam, so we
351         * cannot use GetClientRect either.
352         */
353        GetWindowRect(hwnd, &rcWindow);
354        iWidth = rcWindow.right - rcWindow.left;
355        iHeight = rcWindow.bottom - rcWindow.top;
356
357        /* Subtract the frame size from the window size. */
358        iWidth -= 2 * GetSystemMetrics(SM_CXSIZEFRAME);
359        iHeight -= (2 * GetSystemMetrics(SM_CYSIZEFRAME)
360                    + GetSystemMetrics(SM_CYCAPTION));
361
362        /*
363         * Update scrollbar page sizes.
364         * NOTE: If page size == range, then the scrollbar is
365         * automatically hidden.
366         */
367
368        /* Is the naked client area large enough to show the whole visual? */
369        if (iWidth < s_pScreenInfo->dwWidth
370            || iHeight < s_pScreenInfo->dwHeight) {
371            /* Client area too small to display visual, use scrollbars */
372            iWidth -= GetSystemMetrics(SM_CXVSCROLL);
373            iHeight -= GetSystemMetrics(SM_CYHSCROLL);
374        }
375
376        /* Set the horizontal scrollbar page size */
377        si.cbSize = sizeof(si);
378        si.fMask = SIF_PAGE | SIF_RANGE;
379        si.nMin = 0;
380        si.nMax = s_pScreenInfo->dwWidth - 1;
381        si.nPage = iWidth;
382        SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
383
384        /* Set the vertical scrollbar page size */
385        si.cbSize = sizeof(si);
386        si.fMask = SIF_PAGE | SIF_RANGE;
387        si.nMin = 0;
388        si.nMax = s_pScreenInfo->dwHeight - 1;
389        si.nPage = iHeight;
390        SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
391
392        /*
393         * NOTE: Scrollbars may have moved if they were at the
394         * far right/bottom, so we query their current position.
395         */
396
397        /* Get the horizontal scrollbar position and set the offset */
398        si.cbSize = sizeof(si);
399        si.fMask = SIF_POS;
400        GetScrollInfo(hwnd, SB_HORZ, &si);
401        s_pScreenInfo->dwXOffset = -si.nPos;
402
403        /* Get the vertical scrollbar position and set the offset */
404        si.cbSize = sizeof(si);
405        si.fMask = SIF_POS;
406        GetScrollInfo(hwnd, SB_VERT, &si);
407        s_pScreenInfo->dwYOffset = -si.nPos;
408    }
409        return 0;
410
411    case WM_SYSCOMMAND:
412        if (s_pScreenInfo->iResizeMode == resizeWithRandr &&
413            ((wParam & 0xfff0) == SC_MAXIMIZE ||
414             (wParam & 0xfff0) == SC_RESTORE))
415            PostMessage(hwnd, WM_EXITSIZEMOVE, 0, 0);
416        break;
417
418    case WM_ENTERSIZEMOVE:
419        ErrorF("winWindowProc - WM_ENTERSIZEMOVE\n");
420        break;
421
422    case WM_EXITSIZEMOVE:
423        ErrorF("winWindowProc - WM_EXITSIZEMOVE\n");
424
425        if (s_pScreenInfo->iResizeMode == resizeWithRandr) {
426            /* Set screen size to match new client area, if it is different to current */
427            RECT rcClient;
428            DWORD dwWidth, dwHeight;
429
430            GetClientRect(hwnd, &rcClient);
431            dwWidth = rcClient.right - rcClient.left;
432            dwHeight = rcClient.bottom - rcClient.top;
433
434            if ((s_pScreenInfo->dwWidth != dwWidth) ||
435                (s_pScreenInfo->dwHeight != dwHeight)) {
436                /* mm = dots * (25.4 mm / inch) / (dots / inch) */
437                winDoRandRScreenSetSize(s_pScreen,
438                                        dwWidth,
439                                        dwHeight,
440                                        (dwWidth * 25.4) / monitorResolution,
441                                        (dwHeight * 25.4) / monitorResolution);
442            }
443        }
444
445        break;
446
447    case WM_VSCROLL:
448    {
449        SCROLLINFO si;
450        int iVertPos;
451
452#if CYGDEBUG
453        winDebug("winWindowProc - WM_VSCROLL\n");
454#endif
455
456        /* Get vertical scroll bar info */
457        si.cbSize = sizeof(si);
458        si.fMask = SIF_ALL;
459        GetScrollInfo(hwnd, SB_VERT, &si);
460
461        /* Save the vertical position for comparison later */
462        iVertPos = si.nPos;
463
464        /*
465         * Don't forget:
466         * moving the scrollbar to the DOWN, scroll the content UP
467         */
468        switch (LOWORD(wParam)) {
469        case SB_TOP:
470            si.nPos = si.nMin;
471            break;
472
473        case SB_BOTTOM:
474            si.nPos = si.nMax - si.nPage + 1;
475            break;
476
477        case SB_LINEUP:
478            si.nPos -= 1;
479            break;
480
481        case SB_LINEDOWN:
482            si.nPos += 1;
483            break;
484
485        case SB_PAGEUP:
486            si.nPos -= si.nPage;
487            break;
488
489        case SB_PAGEDOWN:
490            si.nPos += si.nPage;
491            break;
492
493        case SB_THUMBTRACK:
494            si.nPos = si.nTrackPos;
495            break;
496
497        default:
498            break;
499        }
500
501        /*
502         * We retrieve the position after setting it,
503         * because Windows may adjust it.
504         */
505        si.fMask = SIF_POS;
506        SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
507        GetScrollInfo(hwnd, SB_VERT, &si);
508
509        /* Scroll the window if the position has changed */
510        if (si.nPos != iVertPos) {
511            /* Save the new offset for bit block transfers, etc. */
512            s_pScreenInfo->dwYOffset = -si.nPos;
513
514            /* Change displayed region in the window */
515            ScrollWindowEx(hwnd,
516                           0,
517                           iVertPos - si.nPos,
518                           NULL, NULL, NULL, NULL, SW_INVALIDATE);
519
520            /* Redraw the window contents */
521            UpdateWindow(hwnd);
522        }
523    }
524        return 0;
525
526    case WM_HSCROLL:
527    {
528        SCROLLINFO si;
529        int iHorzPos;
530
531#if CYGDEBUG
532        winDebug("winWindowProc - WM_HSCROLL\n");
533#endif
534
535        /* Get horizontal scroll bar info */
536        si.cbSize = sizeof(si);
537        si.fMask = SIF_ALL;
538        GetScrollInfo(hwnd, SB_HORZ, &si);
539
540        /* Save the horizontal position for comparison later */
541        iHorzPos = si.nPos;
542
543        /*
544         * Don't forget:
545         * moving the scrollbar to the RIGHT, scroll the content LEFT
546         */
547        switch (LOWORD(wParam)) {
548        case SB_LEFT:
549            si.nPos = si.nMin;
550            break;
551
552        case SB_RIGHT:
553            si.nPos = si.nMax - si.nPage + 1;
554            break;
555
556        case SB_LINELEFT:
557            si.nPos -= 1;
558            break;
559
560        case SB_LINERIGHT:
561            si.nPos += 1;
562            break;
563
564        case SB_PAGELEFT:
565            si.nPos -= si.nPage;
566            break;
567
568        case SB_PAGERIGHT:
569            si.nPos += si.nPage;
570            break;
571
572        case SB_THUMBTRACK:
573            si.nPos = si.nTrackPos;
574            break;
575
576        default:
577            break;
578        }
579
580        /*
581         * We retrieve the position after setting it,
582         * because Windows may adjust it.
583         */
584        si.fMask = SIF_POS;
585        SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
586        GetScrollInfo(hwnd, SB_HORZ, &si);
587
588        /* Scroll the window if the position has changed */
589        if (si.nPos != iHorzPos) {
590            /* Save the new offset for bit block transfers, etc. */
591            s_pScreenInfo->dwXOffset = -si.nPos;
592
593            /* Change displayed region in the window */
594            ScrollWindowEx(hwnd,
595                           iHorzPos - si.nPos,
596                           0, NULL, NULL, NULL, NULL, SW_INVALIDATE);
597
598            /* Redraw the window contents */
599            UpdateWindow(hwnd);
600        }
601    }
602        return 0;
603
604    case WM_GETMINMAXINFO:
605    {
606        MINMAXINFO *pMinMaxInfo = (MINMAXINFO *) lParam;
607        int iCaptionHeight;
608        int iBorderHeight, iBorderWidth;
609
610#if CYGDEBUG
611        winDebug("winWindowProc - WM_GETMINMAXINFO - pScreenInfo: %p\n",
612                 s_pScreenInfo);
613#endif
614
615        /* Can't do anything without screen info */
616        if (s_pScreenInfo == NULL
617            || (s_pScreenInfo->iResizeMode != resizeWithScrollbars)
618            || s_pScreenInfo->fFullScreen || !s_pScreenInfo->fDecoration
619#ifdef XWIN_MULTIWINDOWEXTWM
620            || s_pScreenInfo->fMWExtWM
621#endif
622            || s_pScreenInfo->fRootless
623            || s_pScreenInfo->fMultiWindow
624            )
625            break;
626
627        /*
628         * Here we can override the maximum tracking size, which
629         * is the largest size that can be assigned to our window
630         * via the sizing border.
631         */
632
633        /*
634         * FIXME: Do we only need to do this once, since our visual size
635         * does not change?  Does Windows store this value statically
636         * once we have set it once?
637         */
638
639        /* Get the border and caption sizes */
640        iCaptionHeight = GetSystemMetrics(SM_CYCAPTION);
641        iBorderWidth = 2 * GetSystemMetrics(SM_CXSIZEFRAME);
642        iBorderHeight = 2 * GetSystemMetrics(SM_CYSIZEFRAME);
643
644        /* Allow the full visual to be displayed */
645        pMinMaxInfo->ptMaxTrackSize.x = s_pScreenInfo->dwWidth + iBorderWidth;
646        pMinMaxInfo->ptMaxTrackSize.y
647            = s_pScreenInfo->dwHeight + iBorderHeight + iCaptionHeight;
648    }
649        return 0;
650
651    case WM_ERASEBKGND:
652#if CYGDEBUG
653        winDebug("winWindowProc - WM_ERASEBKGND\n");
654#endif
655        /*
656         * Pretend that we did erase the background but we don't care,
657         * the application uses the full window estate. This avoids some
658         * flickering when resizing.
659         */
660        return TRUE;
661
662    case WM_PAINT:
663#if CYGDEBUG
664        winDebug("winWindowProc - WM_PAINT\n");
665#endif
666        /* Only paint if we have privates and the server is enabled */
667        if (s_pScreenPriv == NULL
668            || !s_pScreenPriv->fEnabled
669            || (s_pScreenInfo->fFullScreen && !s_pScreenPriv->fActive)
670            || s_pScreenPriv->fBadDepth) {
671            /* We don't want to paint */
672            break;
673        }
674
675        /* Break out here if we don't have a valid paint routine */
676        if (s_pScreenPriv->pwinBltExposedRegions == NULL)
677            break;
678
679        /* Call the engine dependent repainter */
680        (*s_pScreenPriv->pwinBltExposedRegions) (s_pScreen);
681        return 0;
682
683    case WM_PALETTECHANGED:
684    {
685#if CYGDEBUG
686        winDebug("winWindowProc - WM_PALETTECHANGED\n");
687#endif
688        /*
689         * Don't process if we don't have privates or a colormap,
690         * or if we have an invalid depth.
691         */
692        if (s_pScreenPriv == NULL
693            || s_pScreenPriv->pcmapInstalled == NULL
694            || s_pScreenPriv->fBadDepth)
695            break;
696
697        /* Return if we caused the palette to change */
698        if ((HWND) wParam == hwnd) {
699            /* Redraw the screen */
700            (*s_pScreenPriv->pwinRedrawScreen) (s_pScreen);
701            return 0;
702        }
703
704        /* Reinstall the windows palette */
705        (*s_pScreenPriv->pwinRealizeInstalledPalette) (s_pScreen);
706
707        /* Redraw the screen */
708        (*s_pScreenPriv->pwinRedrawScreen) (s_pScreen);
709        return 0;
710    }
711
712    case WM_MOUSEMOVE:
713        /* We can't do anything without privates */
714        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
715            break;
716
717        /* We can't do anything without g_pwinPointer */
718        if (g_pwinPointer == NULL)
719            break;
720
721        /* Has the mouse pointer crossed screens? */
722        if (s_pScreen != miPointerGetScreen(g_pwinPointer))
723            miPointerSetScreen(g_pwinPointer, s_pScreenInfo->dwScreen,
724                               GET_X_LPARAM(lParam) - s_pScreenInfo->dwXOffset,
725                               GET_Y_LPARAM(lParam) - s_pScreenInfo->dwYOffset);
726
727        /* Are we tracking yet? */
728        if (!s_fTracking) {
729            TRACKMOUSEEVENT tme;
730
731            /* Setup data structure */
732            ZeroMemory(&tme, sizeof(tme));
733            tme.cbSize = sizeof(tme);
734            tme.dwFlags = TME_LEAVE;
735            tme.hwndTrack = hwnd;
736
737            /* Call the tracking function */
738            if (!TrackMouseEvent(&tme))
739                ErrorF("winWindowProc - TrackMouseEvent failed\n");
740
741            /* Flag that we are tracking now */
742            s_fTracking = TRUE;
743        }
744
745        /* Hide or show the Windows mouse cursor */
746        if (g_fSoftwareCursor && g_fCursor &&
747            (s_pScreenPriv->fActive || s_pScreenInfo->fLessPointer)) {
748            /* Hide Windows cursor */
749            g_fCursor = FALSE;
750            ShowCursor(FALSE);
751        }
752        else if (g_fSoftwareCursor && !g_fCursor && !s_pScreenPriv->fActive
753                 && !s_pScreenInfo->fLessPointer) {
754            /* Show Windows cursor */
755            g_fCursor = TRUE;
756            ShowCursor(TRUE);
757        }
758
759        /* Deliver absolute cursor position to X Server */
760        winEnqueueMotion(GET_X_LPARAM(lParam) - s_pScreenInfo->dwXOffset,
761                         GET_Y_LPARAM(lParam) - s_pScreenInfo->dwYOffset);
762        return 0;
763
764    case WM_NCMOUSEMOVE:
765        /*
766         * We break instead of returning 0 since we need to call
767         * DefWindowProc to get the mouse cursor changes
768         * and min/max/close button highlighting in Windows XP.
769         * The Platform SDK says that you should return 0 if you
770         * process this message, but it fails to mention that you
771         * will give up any default functionality if you do return 0.
772         */
773
774        /* We can't do anything without privates */
775        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
776            break;
777
778        /* Non-client mouse movement, show Windows cursor */
779        if (g_fSoftwareCursor && !g_fCursor) {
780            g_fCursor = TRUE;
781            ShowCursor(TRUE);
782        }
783        break;
784
785    case WM_MOUSELEAVE:
786        /* Mouse has left our client area */
787
788        /* Flag that we are no longer tracking */
789        s_fTracking = FALSE;
790
791        /* Show the mouse cursor, if necessary */
792        if (g_fSoftwareCursor && !g_fCursor) {
793            g_fCursor = TRUE;
794            ShowCursor(TRUE);
795        }
796        return 0;
797
798    case WM_LBUTTONDBLCLK:
799    case WM_LBUTTONDOWN:
800        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
801            break;
802        if (s_pScreenInfo->fRootless
803#ifdef XWIN_MULTIWINDOWEXTWM
804            || s_pScreenInfo->fMWExtWM
805#endif
806            )
807            SetCapture(hwnd);
808        return winMouseButtonsHandle(s_pScreen, ButtonPress, Button1, wParam);
809
810    case WM_LBUTTONUP:
811        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
812            break;
813        if (s_pScreenInfo->fRootless
814#ifdef XWIN_MULTIWINDOWEXTWM
815            || s_pScreenInfo->fMWExtWM
816#endif
817            )
818            ReleaseCapture();
819        return winMouseButtonsHandle(s_pScreen, ButtonRelease, Button1, wParam);
820
821    case WM_MBUTTONDBLCLK:
822    case WM_MBUTTONDOWN:
823        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
824            break;
825        if (s_pScreenInfo->fRootless
826#ifdef XWIN_MULTIWINDOWEXTWM
827            || s_pScreenInfo->fMWExtWM
828#endif
829            )
830            SetCapture(hwnd);
831        return winMouseButtonsHandle(s_pScreen, ButtonPress, Button2, wParam);
832
833    case WM_MBUTTONUP:
834        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
835            break;
836        if (s_pScreenInfo->fRootless
837#ifdef XWIN_MULTIWINDOWEXTWM
838            || s_pScreenInfo->fMWExtWM
839#endif
840            )
841            ReleaseCapture();
842        return winMouseButtonsHandle(s_pScreen, ButtonRelease, Button2, wParam);
843
844    case WM_RBUTTONDBLCLK:
845    case WM_RBUTTONDOWN:
846        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
847            break;
848        if (s_pScreenInfo->fRootless
849#ifdef XWIN_MULTIWINDOWEXTWM
850            || s_pScreenInfo->fMWExtWM
851#endif
852            )
853            SetCapture(hwnd);
854        return winMouseButtonsHandle(s_pScreen, ButtonPress, Button3, wParam);
855
856    case WM_RBUTTONUP:
857        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
858            break;
859        if (s_pScreenInfo->fRootless
860#ifdef XWIN_MULTIWINDOWEXTWM
861            || s_pScreenInfo->fMWExtWM
862#endif
863            )
864            ReleaseCapture();
865        return winMouseButtonsHandle(s_pScreen, ButtonRelease, Button3, wParam);
866
867    case WM_XBUTTONDBLCLK:
868    case WM_XBUTTONDOWN:
869        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
870            break;
871        if (s_pScreenInfo->fRootless
872#ifdef XWIN_MULTIWINDOWEXTWM
873            || s_pScreenInfo->fMWExtWM
874#endif
875            )
876            SetCapture(hwnd);
877        return winMouseButtonsHandle(s_pScreen, ButtonPress, HIWORD(wParam) + 7,
878                                     wParam);
879    case WM_XBUTTONUP:
880        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
881            break;
882        if (s_pScreenInfo->fRootless
883#ifdef XWIN_MULTIWINDOWEXTWM
884            || s_pScreenInfo->fMWExtWM
885#endif
886            )
887            ReleaseCapture();
888        return winMouseButtonsHandle(s_pScreen, ButtonRelease,
889                                     HIWORD(wParam) + 7, wParam);
890
891    case WM_TIMER:
892        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
893            break;
894
895        /* Branch on the timer id */
896        switch (wParam) {
897        case WIN_E3B_TIMER_ID:
898            /* Send delayed button press */
899            winMouseButtonsSendEvent(ButtonPress,
900                                     s_pScreenPriv->iE3BCachedPress);
901
902            /* Kill this timer */
903            KillTimer(s_pScreenPriv->hwndScreen, WIN_E3B_TIMER_ID);
904
905            /* Clear screen privates flags */
906            s_pScreenPriv->iE3BCachedPress = 0;
907            break;
908
909        case WIN_POLLING_MOUSE_TIMER_ID:
910        {
911            static POINT last_point;
912            POINT point;
913            WPARAM wL, wM, wR, wShift, wCtrl;
914            LPARAM lPos;
915
916            /* Get the current position of the mouse cursor */
917            GetCursorPos(&point);
918
919            /* Map from screen (-X, -Y) to root (0, 0) */
920            point.x -= GetSystemMetrics(SM_XVIRTUALSCREEN);
921            point.y -= GetSystemMetrics(SM_YVIRTUALSCREEN);
922
923            /* If the mouse pointer has moved, deliver absolute cursor position to X Server */
924            if (last_point.x != point.x || last_point.y != point.y) {
925                winEnqueueMotion(point.x, point.y);
926                last_point.x = point.x;
927                last_point.y = point.y;
928            }
929
930            /* Check if a button was released but we didn't see it */
931            GetCursorPos(&point);
932            wL = (GetKeyState(VK_LBUTTON) & 0x8000) ? MK_LBUTTON : 0;
933            wM = (GetKeyState(VK_MBUTTON) & 0x8000) ? MK_MBUTTON : 0;
934            wR = (GetKeyState(VK_RBUTTON) & 0x8000) ? MK_RBUTTON : 0;
935            wShift = (GetKeyState(VK_SHIFT) & 0x8000) ? MK_SHIFT : 0;
936            wCtrl = (GetKeyState(VK_CONTROL) & 0x8000) ? MK_CONTROL : 0;
937            lPos = MAKELPARAM(point.x, point.y);
938            if (g_fButton[0] && !wL)
939                PostMessage(hwnd, WM_LBUTTONUP, wCtrl | wM | wR | wShift, lPos);
940            if (g_fButton[1] && !wM)
941                PostMessage(hwnd, WM_MBUTTONUP, wCtrl | wL | wR | wShift, lPos);
942            if (g_fButton[2] && !wR)
943                PostMessage(hwnd, WM_RBUTTONUP, wCtrl | wL | wM | wShift, lPos);
944        }
945        }
946        return 0;
947
948    case WM_CTLCOLORSCROLLBAR:
949        FatalError("winWindowProc - WM_CTLCOLORSCROLLBAR - We are not "
950                   "supposed to get this message.  Exiting.\n");
951        return 0;
952
953    case WM_MOUSEWHEEL:
954        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
955            break;
956#if CYGDEBUG
957        winDebug("winWindowProc - WM_MOUSEWHEEL\n");
958#endif
959        /* Button4 = WheelUp */
960        /* Button5 = WheelDown */
961        winMouseWheel(&(s_pScreenPriv->iDeltaZ), GET_WHEEL_DELTA_WPARAM(wParam), Button4, Button5);
962        break;
963
964    case WM_MOUSEHWHEEL:
965        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
966            break;
967#if CYGDEBUG
968        winDebug("winWindowProc - WM_MOUSEHWHEEL\n");
969#endif
970        /* Button7 = TiltRight */
971        /* Button6 = TiltLeft */
972        winMouseWheel(&(s_pScreenPriv->iDeltaV), GET_WHEEL_DELTA_WPARAM(wParam), 7, 6);
973        break;
974
975    case WM_SETFOCUS:
976        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
977            break;
978
979        /* Restore the state of all mode keys */
980        winRestoreModeKeyStates();
981
982        /* Add the keyboard hook if possible */
983        if (g_fKeyboardHookLL)
984            g_fKeyboardHookLL = winInstallKeyboardHookLL();
985        return 0;
986
987    case WM_KILLFOCUS:
988        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
989            break;
990
991        /* Release any pressed keys */
992        winKeybdReleaseKeys();
993
994        /* Remove our keyboard hook if it is installed */
995        winRemoveKeyboardHookLL();
996        return 0;
997
998    case WM_SYSKEYDOWN:
999    case WM_KEYDOWN:
1000        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
1001            break;
1002
1003        /*
1004         * FIXME: Catching Alt-F4 like this is really terrible.  This should
1005         * be generalized to handle other Windows keyboard signals.  Actually,
1006         * the list keys to catch and the actions to perform when caught should
1007         * be configurable; that way user's can customize the keys that they
1008         * need to have passed through to their window manager or apps, or they
1009         * can remap certain actions to new key codes that do not conflict
1010         * with the X apps that they are using.  Yeah, that'll take awhile.
1011         */
1012        if ((s_pScreenInfo->fUseWinKillKey && wParam == VK_F4
1013             && (GetKeyState(VK_MENU) & 0x8000))
1014            || (s_pScreenInfo->fUseUnixKillKey && wParam == VK_BACK
1015                && (GetKeyState(VK_MENU) & 0x8000)
1016                && (GetKeyState(VK_CONTROL) & 0x8000))) {
1017            /*
1018             * Better leave this message here, just in case some unsuspecting
1019             * user enters Alt + F4 and is surprised when the application
1020             * quits.
1021             */
1022            ErrorF("winWindowProc - WM_*KEYDOWN - Closekey hit, quitting\n");
1023
1024            /* Display Exit dialog */
1025            winDisplayExitDialog(s_pScreenPriv);
1026            return 0;
1027        }
1028
1029        /*
1030         * Don't do anything for the Windows keys, as focus will soon
1031         * be returned to Windows.  We may be able to trap the Windows keys,
1032         * but we should determine if that is desirable before doing so.
1033         */
1034        if ((wParam == VK_LWIN || wParam == VK_RWIN) && !g_fKeyboardHookLL)
1035            break;
1036
1037        /* Discard fake Ctrl_L events that precede AltGR on non-US keyboards */
1038        if (winIsFakeCtrl_L(message, wParam, lParam))
1039            return 0;
1040
1041        /*
1042         * Discard presses generated from Windows auto-repeat
1043         */
1044        if (lParam & (1 << 30)) {
1045            switch (wParam) {
1046                /* ago: Pressing LControl while RControl is pressed is
1047                 * Indicated as repeat. Fix this!
1048                 */
1049            case VK_CONTROL:
1050            case VK_SHIFT:
1051                if (winCheckKeyPressed(wParam, lParam))
1052                    return 0;
1053                break;
1054            default:
1055                return 0;
1056            }
1057        }
1058
1059        /* Translate Windows key code to X scan code */
1060        iScanCode = winTranslateKey(wParam, lParam);
1061
1062        /* Ignore repeats for CapsLock */
1063        if (wParam == VK_CAPITAL)
1064            lParam = 1;
1065
1066        /* Send the key event(s) */
1067        for (i = 0; i < LOWORD(lParam); ++i)
1068            winSendKeyEvent(iScanCode, TRUE);
1069        return 0;
1070
1071    case WM_SYSKEYUP:
1072    case WM_KEYUP:
1073        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
1074            break;
1075
1076        /*
1077         * Don't do anything for the Windows keys, as focus will soon
1078         * be returned to Windows.  We may be able to trap the Windows keys,
1079         * but we should determine if that is desirable before doing so.
1080         */
1081        if ((wParam == VK_LWIN || wParam == VK_RWIN) && !g_fKeyboardHookLL)
1082            break;
1083
1084        /* Ignore the fake Ctrl_L that follows an AltGr release */
1085        if (winIsFakeCtrl_L(message, wParam, lParam))
1086            return 0;
1087
1088        /* Enqueue a keyup event */
1089        iScanCode = winTranslateKey(wParam, lParam);
1090        winSendKeyEvent(iScanCode, FALSE);
1091
1092        /* Release all pressed shift keys */
1093        if (wParam == VK_SHIFT)
1094            winFixShiftKeys(iScanCode);
1095        return 0;
1096
1097    case WM_ACTIVATE:
1098        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
1099            break;
1100
1101        /* TODO: Override display of window when we have a bad depth */
1102        if (LOWORD(wParam) != WA_INACTIVE && s_pScreenPriv->fBadDepth) {
1103            ErrorF("winWindowProc - WM_ACTIVATE - Bad depth, trying "
1104                   "to override window activation\n");
1105
1106            /* Minimize the window */
1107            ShowWindow(hwnd, SW_MINIMIZE);
1108
1109            /* Display dialog box */
1110            if (g_hDlgDepthChange != NULL) {
1111                /* Make the existing dialog box active */
1112                SetActiveWindow(g_hDlgDepthChange);
1113            }
1114            else {
1115                /* TODO: Recreate the dialog box and bring to the top */
1116                ShowWindow(g_hDlgDepthChange, SW_SHOWDEFAULT);
1117            }
1118
1119            /* Don't do any other processing of this message */
1120            return 0;
1121        }
1122
1123#if CYGDEBUG
1124        winDebug("winWindowProc - WM_ACTIVATE\n");
1125#endif
1126
1127        /*
1128         * Focus is being changed to another window.
1129         * The other window may or may not belong to
1130         * our process.
1131         */
1132
1133        /* Clear any lingering wheel delta */
1134        s_pScreenPriv->iDeltaZ = 0;
1135        s_pScreenPriv->iDeltaV = 0;
1136
1137        /* Reshow the Windows mouse cursor if we are being deactivated */
1138        if (g_fSoftwareCursor && LOWORD(wParam) == WA_INACTIVE && !g_fCursor) {
1139            /* Show Windows cursor */
1140            g_fCursor = TRUE;
1141            ShowCursor(TRUE);
1142        }
1143        return 0;
1144
1145    case WM_ACTIVATEAPP:
1146        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
1147            break;
1148
1149#if CYGDEBUG || TRUE
1150        winDebug("winWindowProc - WM_ACTIVATEAPP\n");
1151#endif
1152
1153        /* Activate or deactivate */
1154        s_pScreenPriv->fActive = wParam;
1155
1156        /* Reshow the Windows mouse cursor if we are being deactivated */
1157        if (g_fSoftwareCursor && !s_pScreenPriv->fActive && !g_fCursor) {
1158            /* Show Windows cursor */
1159            g_fCursor = TRUE;
1160            ShowCursor(TRUE);
1161        }
1162
1163        /* Make sure the clipboard chain is ok. */
1164        winFixClipboardChain();
1165
1166        /* Call engine specific screen activation/deactivation function */
1167        (*s_pScreenPriv->pwinActivateApp) (s_pScreen);
1168
1169#ifdef XWIN_MULTIWINDOWEXTWM
1170        if (s_pScreenPriv->fActive) {
1171            /* Restack all window unless using built-in wm. */
1172            if (s_pScreenInfo->fMWExtWM)
1173                winMWExtWMRestackWindows(s_pScreen);
1174        }
1175#endif
1176
1177        return 0;
1178
1179    case WM_COMMAND:
1180        switch (LOWORD(wParam)) {
1181        case ID_APP_EXIT:
1182            /* Display Exit dialog */
1183            winDisplayExitDialog(s_pScreenPriv);
1184            return 0;
1185
1186        case ID_APP_HIDE_ROOT:
1187            if (s_pScreenPriv->fRootWindowShown)
1188                ShowWindow(s_pScreenPriv->hwndScreen, SW_HIDE);
1189            else
1190                ShowWindow(s_pScreenPriv->hwndScreen, SW_SHOW);
1191            s_pScreenPriv->fRootWindowShown = !s_pScreenPriv->fRootWindowShown;
1192            return 0;
1193
1194        case ID_APP_MONITOR_PRIMARY:
1195            fPrimarySelection = !fPrimarySelection;
1196            return 0;
1197
1198        case ID_APP_ABOUT:
1199            /* Display the About box */
1200            winDisplayAboutDialog(s_pScreenPriv);
1201            return 0;
1202
1203        default:
1204            /* It's probably one of the custom menus... */
1205            if (HandleCustomWM_COMMAND(0, LOWORD(wParam), s_pScreenPriv))
1206                return 0;
1207        }
1208        break;
1209
1210    case WM_GIVEUP:
1211        /* Tell X that we are giving up */
1212        if (s_pScreenInfo->fMultiWindow)
1213            winDeinitMultiWindowWM();
1214        GiveUp(0);
1215        return 0;
1216
1217    case WM_CLOSE:
1218        /* Display Exit dialog */
1219        winDisplayExitDialog(s_pScreenPriv);
1220        return 0;
1221
1222    case WM_SETCURSOR:
1223        if (LOWORD(lParam) == HTCLIENT) {
1224            if (!g_fSoftwareCursor)
1225                SetCursor(s_pScreenPriv->cursor.handle);
1226            return TRUE;
1227        }
1228        break;
1229
1230    default:
1231        if (message == s_uTaskbarRestart) {
1232            winInitNotifyIcon(s_pScreenPriv);
1233        }
1234        break;
1235    }
1236
1237    return DefWindowProc(hwnd, message, wParam, lParam);
1238}
1239