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 its 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 initializing.  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) && (s_pScreenInfo->
224                                                                    fRootless
225                                                                    ||
226                                                                    s_pScreenInfo->
227                                                                    fMultiWindow
228                )) {
229                DWORD dwWidth = 0, dwHeight = 0;
230
231                if (s_pScreenInfo->fMultipleMonitors) {
232                    /* resize to new virtual desktop size */
233                    dwWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
234                    dwHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
235                }
236                else {
237                    /* resize to new size of specified monitor */
238                    struct GetMonitorInfoData data;
239
240                    if (QueryMonitor(s_pScreenInfo->iMonitor, &data)) {
241                            dwWidth = data.monitorWidth;
242                            dwHeight = data.monitorHeight;
243                            /*
244                               XXX: monitor may have changed position,
245                               so we might need to update xinerama data
246                             */
247                        }
248                        else {
249                            ErrorF("Monitor number %d no longer exists!\n",
250                                   s_pScreenInfo->iMonitor);
251                        }
252                }
253
254                /*
255                   XXX: probably a small bug here: we don't compute the work area
256                   and allow for task bar
257
258                   XXX: generally, we don't allow for the task bar being moved after
259                   the server is started
260                 */
261
262                /* Set screen size to match new size, if it is different to current */
263                if (((dwWidth != 0) && (dwHeight != 0)) &&
264                    ((s_pScreenInfo->dwWidth != dwWidth) ||
265                     (s_pScreenInfo->dwHeight != dwHeight))) {
266                    winDoRandRScreenSetSize(s_pScreen,
267                                            dwWidth,
268                                            dwHeight,
269                                            (dwWidth * 25.4) /
270                                            monitorResolution,
271                                            (dwHeight * 25.4) /
272                                            monitorResolution);
273                }
274            }
275            else {
276                /*
277                 * We can simply recreate the same-sized primary surface when
278                 * the display dimensions change.
279                 */
280
281                winDebug
282                    ("winWindowProc - WM_DISPLAYCHANGE - Releasing and recreating primary surface\n");
283
284                /* Release the old primary surface */
285                if (*s_pScreenPriv->pwinReleasePrimarySurface)
286                    (*s_pScreenPriv->pwinReleasePrimarySurface) (s_pScreen);
287
288                /* Create the new primary surface */
289                if (*s_pScreenPriv->pwinCreatePrimarySurface)
290                    (*s_pScreenPriv->pwinCreatePrimarySurface) (s_pScreen);
291            }
292        }
293
294        break;
295
296    case WM_SIZE:
297    {
298        SCROLLINFO si;
299        RECT rcWindow;
300        int iWidth, iHeight;
301
302#if CYGDEBUG
303        winDebug("winWindowProc - WM_SIZE\n");
304#endif
305
306        /* Break if we do not allow resizing */
307        if ((s_pScreenInfo->iResizeMode == resizeNotAllowed)
308            || !s_pScreenInfo->fDecoration
309            || s_pScreenInfo->fRootless
310            || s_pScreenInfo->fMultiWindow
311            || s_pScreenInfo->fFullScreen)
312            break;
313
314        /* No need to resize if we get minimized */
315        if (wParam == SIZE_MINIMIZED)
316            return 0;
317
318        ErrorF("winWindowProc - WM_SIZE - new client area w: %d h: %d\n",
319               LOWORD(lParam), HIWORD(lParam));
320
321        if (s_pScreenInfo->iResizeMode == resizeWithRandr) {
322            /* Actual resizing is done on WM_EXITSIZEMOVE */
323            return 0;
324        }
325
326        /* Otherwise iResizeMode == resizeWithScrollbars */
327
328        /*
329         * Get the size of the whole window, including client area,
330         * scrollbars, and non-client area decorations (caption, borders).
331         * We do this because we need to check if the client area
332         * without scrollbars is large enough to display the whole visual.
333         * The new client area size passed by lParam already subtracts
334         * the size of the scrollbars if they are currently displayed.
335         * So checking is LOWORD(lParam) == visual_width and
336         * HIWORD(lParam) == visual_height will never tell us to hide
337         * the scrollbars because the client area would always be too small.
338         * GetClientRect returns the same sizes given by lParam, so we
339         * cannot use GetClientRect either.
340         */
341        GetWindowRect(hwnd, &rcWindow);
342        iWidth = rcWindow.right - rcWindow.left;
343        iHeight = rcWindow.bottom - rcWindow.top;
344
345        /* Subtract the frame size from the window size. */
346        iWidth -= 2 * GetSystemMetrics(SM_CXSIZEFRAME);
347        iHeight -= (2 * GetSystemMetrics(SM_CYSIZEFRAME)
348                    + GetSystemMetrics(SM_CYCAPTION));
349
350        /*
351         * Update scrollbar page sizes.
352         * NOTE: If page size == range, then the scrollbar is
353         * automatically hidden.
354         */
355
356        /* Is the naked client area large enough to show the whole visual? */
357        if (iWidth < s_pScreenInfo->dwWidth
358            || iHeight < s_pScreenInfo->dwHeight) {
359            /* Client area too small to display visual, use scrollbars */
360            iWidth -= GetSystemMetrics(SM_CXVSCROLL);
361            iHeight -= GetSystemMetrics(SM_CYHSCROLL);
362        }
363
364        /* Set the horizontal scrollbar page size */
365        si.cbSize = sizeof(si);
366        si.fMask = SIF_PAGE | SIF_RANGE;
367        si.nMin = 0;
368        si.nMax = s_pScreenInfo->dwWidth - 1;
369        si.nPage = iWidth;
370        SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
371
372        /* Set the vertical scrollbar page size */
373        si.cbSize = sizeof(si);
374        si.fMask = SIF_PAGE | SIF_RANGE;
375        si.nMin = 0;
376        si.nMax = s_pScreenInfo->dwHeight - 1;
377        si.nPage = iHeight;
378        SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
379
380        /*
381         * NOTE: Scrollbars may have moved if they were at the
382         * far right/bottom, so we query their current position.
383         */
384
385        /* Get the horizontal scrollbar position and set the offset */
386        si.cbSize = sizeof(si);
387        si.fMask = SIF_POS;
388        GetScrollInfo(hwnd, SB_HORZ, &si);
389        s_pScreenInfo->dwXOffset = -si.nPos;
390
391        /* Get the vertical scrollbar position and set the offset */
392        si.cbSize = sizeof(si);
393        si.fMask = SIF_POS;
394        GetScrollInfo(hwnd, SB_VERT, &si);
395        s_pScreenInfo->dwYOffset = -si.nPos;
396    }
397        return 0;
398
399    case WM_SYSCOMMAND:
400        if (s_pScreenInfo->iResizeMode == resizeWithRandr &&
401            ((wParam & 0xfff0) == SC_MAXIMIZE ||
402             (wParam & 0xfff0) == SC_RESTORE))
403            PostMessage(hwnd, WM_EXITSIZEMOVE, 0, 0);
404        break;
405
406    case WM_ENTERSIZEMOVE:
407        ErrorF("winWindowProc - WM_ENTERSIZEMOVE\n");
408        break;
409
410    case WM_EXITSIZEMOVE:
411        ErrorF("winWindowProc - WM_EXITSIZEMOVE\n");
412
413        if (s_pScreenInfo->iResizeMode == resizeWithRandr) {
414            /* Set screen size to match new client area, if it is different to current */
415            RECT rcClient;
416            DWORD dwWidth, dwHeight;
417
418            GetClientRect(hwnd, &rcClient);
419            dwWidth = rcClient.right - rcClient.left;
420            dwHeight = rcClient.bottom - rcClient.top;
421
422            if ((s_pScreenInfo->dwWidth != dwWidth) ||
423                (s_pScreenInfo->dwHeight != dwHeight)) {
424                /* mm = dots * (25.4 mm / inch) / (dots / inch) */
425                winDoRandRScreenSetSize(s_pScreen,
426                                        dwWidth,
427                                        dwHeight,
428                                        (dwWidth * 25.4) / monitorResolution,
429                                        (dwHeight * 25.4) / monitorResolution);
430            }
431        }
432
433        break;
434
435    case WM_VSCROLL:
436    {
437        SCROLLINFO si;
438        int iVertPos;
439
440#if CYGDEBUG
441        winDebug("winWindowProc - WM_VSCROLL\n");
442#endif
443
444        /* Get vertical scroll bar info */
445        si.cbSize = sizeof(si);
446        si.fMask = SIF_ALL;
447        GetScrollInfo(hwnd, SB_VERT, &si);
448
449        /* Save the vertical position for comparison later */
450        iVertPos = si.nPos;
451
452        /*
453         * Don't forget:
454         * moving the scrollbar to the DOWN, scroll the content UP
455         */
456        switch (LOWORD(wParam)) {
457        case SB_TOP:
458            si.nPos = si.nMin;
459            break;
460
461        case SB_BOTTOM:
462            si.nPos = si.nMax - si.nPage + 1;
463            break;
464
465        case SB_LINEUP:
466            si.nPos -= 1;
467            break;
468
469        case SB_LINEDOWN:
470            si.nPos += 1;
471            break;
472
473        case SB_PAGEUP:
474            si.nPos -= si.nPage;
475            break;
476
477        case SB_PAGEDOWN:
478            si.nPos += si.nPage;
479            break;
480
481        case SB_THUMBTRACK:
482            si.nPos = si.nTrackPos;
483            break;
484
485        default:
486            break;
487        }
488
489        /*
490         * We retrieve the position after setting it,
491         * because Windows may adjust it.
492         */
493        si.fMask = SIF_POS;
494        SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
495        GetScrollInfo(hwnd, SB_VERT, &si);
496
497        /* Scroll the window if the position has changed */
498        if (si.nPos != iVertPos) {
499            /* Save the new offset for bit block transfers, etc. */
500            s_pScreenInfo->dwYOffset = -si.nPos;
501
502            /* Change displayed region in the window */
503            ScrollWindowEx(hwnd,
504                           0,
505                           iVertPos - si.nPos,
506                           NULL, NULL, NULL, NULL, SW_INVALIDATE);
507
508            /* Redraw the window contents */
509            UpdateWindow(hwnd);
510        }
511    }
512        return 0;
513
514    case WM_HSCROLL:
515    {
516        SCROLLINFO si;
517        int iHorzPos;
518
519#if CYGDEBUG
520        winDebug("winWindowProc - WM_HSCROLL\n");
521#endif
522
523        /* Get horizontal scroll bar info */
524        si.cbSize = sizeof(si);
525        si.fMask = SIF_ALL;
526        GetScrollInfo(hwnd, SB_HORZ, &si);
527
528        /* Save the horizontal position for comparison later */
529        iHorzPos = si.nPos;
530
531        /*
532         * Don't forget:
533         * moving the scrollbar to the RIGHT, scroll the content LEFT
534         */
535        switch (LOWORD(wParam)) {
536        case SB_LEFT:
537            si.nPos = si.nMin;
538            break;
539
540        case SB_RIGHT:
541            si.nPos = si.nMax - si.nPage + 1;
542            break;
543
544        case SB_LINELEFT:
545            si.nPos -= 1;
546            break;
547
548        case SB_LINERIGHT:
549            si.nPos += 1;
550            break;
551
552        case SB_PAGELEFT:
553            si.nPos -= si.nPage;
554            break;
555
556        case SB_PAGERIGHT:
557            si.nPos += si.nPage;
558            break;
559
560        case SB_THUMBTRACK:
561            si.nPos = si.nTrackPos;
562            break;
563
564        default:
565            break;
566        }
567
568        /*
569         * We retrieve the position after setting it,
570         * because Windows may adjust it.
571         */
572        si.fMask = SIF_POS;
573        SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
574        GetScrollInfo(hwnd, SB_HORZ, &si);
575
576        /* Scroll the window if the position has changed */
577        if (si.nPos != iHorzPos) {
578            /* Save the new offset for bit block transfers, etc. */
579            s_pScreenInfo->dwXOffset = -si.nPos;
580
581            /* Change displayed region in the window */
582            ScrollWindowEx(hwnd,
583                           iHorzPos - si.nPos,
584                           0, NULL, NULL, NULL, NULL, SW_INVALIDATE);
585
586            /* Redraw the window contents */
587            UpdateWindow(hwnd);
588        }
589    }
590        return 0;
591
592    case WM_GETMINMAXINFO:
593    {
594        MINMAXINFO *pMinMaxInfo = (MINMAXINFO *) lParam;
595        int iCaptionHeight;
596        int iBorderHeight, iBorderWidth;
597
598#if CYGDEBUG
599        winDebug("winWindowProc - WM_GETMINMAXINFO - pScreenInfo: %p\n",
600                 s_pScreenInfo);
601#endif
602
603        /* Can't do anything without screen info */
604        if (s_pScreenInfo == NULL
605            || (s_pScreenInfo->iResizeMode != resizeWithScrollbars)
606            || s_pScreenInfo->fFullScreen || !s_pScreenInfo->fDecoration
607            || s_pScreenInfo->fRootless
608            || s_pScreenInfo->fMultiWindow
609            )
610            break;
611
612        /*
613         * Here we can override the maximum tracking size, which
614         * is the largest size that can be assigned to our window
615         * via the sizing border.
616         */
617
618        /*
619         * FIXME: Do we only need to do this once, since our visual size
620         * does not change?  Does Windows store this value statically
621         * once we have set it once?
622         */
623
624        /* Get the border and caption sizes */
625        iCaptionHeight = GetSystemMetrics(SM_CYCAPTION);
626        iBorderWidth = 2 * GetSystemMetrics(SM_CXSIZEFRAME);
627        iBorderHeight = 2 * GetSystemMetrics(SM_CYSIZEFRAME);
628
629        /* Allow the full visual to be displayed */
630        pMinMaxInfo->ptMaxTrackSize.x = s_pScreenInfo->dwWidth + iBorderWidth;
631        pMinMaxInfo->ptMaxTrackSize.y
632            = s_pScreenInfo->dwHeight + iBorderHeight + iCaptionHeight;
633    }
634        return 0;
635
636    case WM_ERASEBKGND:
637#if CYGDEBUG
638        winDebug("winWindowProc - WM_ERASEBKGND\n");
639#endif
640        /*
641         * Pretend that we did erase the background but we don't care,
642         * the application uses the full window estate. This avoids some
643         * flickering when resizing.
644         */
645        return TRUE;
646
647    case WM_PAINT:
648#if CYGDEBUG
649        winDebug("winWindowProc - WM_PAINT\n");
650#endif
651        /* Only paint if we have privates and the server is enabled */
652        if (s_pScreenPriv == NULL
653            || !s_pScreenPriv->fEnabled
654            || (s_pScreenInfo->fFullScreen && !s_pScreenPriv->fActive)
655            || s_pScreenPriv->fBadDepth) {
656            /* We don't want to paint */
657            break;
658        }
659
660        /* Break out here if we don't have a valid paint routine */
661        if (s_pScreenPriv->pwinBltExposedRegions == NULL)
662            break;
663
664        /* Call the engine dependent repainter */
665        (*s_pScreenPriv->pwinBltExposedRegions) (s_pScreen);
666        return 0;
667
668    case WM_PALETTECHANGED:
669    {
670#if CYGDEBUG
671        winDebug("winWindowProc - WM_PALETTECHANGED\n");
672#endif
673        /*
674         * Don't process if we don't have privates or a colormap,
675         * or if we have an invalid depth.
676         */
677        if (s_pScreenPriv == NULL
678            || s_pScreenPriv->pcmapInstalled == NULL
679            || s_pScreenPriv->fBadDepth)
680            break;
681
682        /* Return if we caused the palette to change */
683        if ((HWND) wParam == hwnd) {
684            /* Redraw the screen */
685            (*s_pScreenPriv->pwinRedrawScreen) (s_pScreen);
686            return 0;
687        }
688
689        /* Reinstall the windows palette */
690        (*s_pScreenPriv->pwinRealizeInstalledPalette) (s_pScreen);
691
692        /* Redraw the screen */
693        (*s_pScreenPriv->pwinRedrawScreen) (s_pScreen);
694        return 0;
695    }
696
697    case WM_MOUSEMOVE:
698        /* We can't do anything without privates */
699        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
700            break;
701
702        /* We can't do anything without g_pwinPointer */
703        if (g_pwinPointer == NULL)
704            break;
705
706        /* Has the mouse pointer crossed screens? */
707        if (s_pScreen != miPointerGetScreen(g_pwinPointer))
708            miPointerSetScreen(g_pwinPointer, s_pScreenInfo->dwScreen,
709                               GET_X_LPARAM(lParam) - s_pScreenInfo->dwXOffset,
710                               GET_Y_LPARAM(lParam) - s_pScreenInfo->dwYOffset);
711
712        /* Are we tracking yet? */
713        if (!s_fTracking) {
714            TRACKMOUSEEVENT tme;
715
716            /* Setup data structure */
717            ZeroMemory(&tme, sizeof(tme));
718            tme.cbSize = sizeof(tme);
719            tme.dwFlags = TME_LEAVE;
720            tme.hwndTrack = hwnd;
721
722            /* Call the tracking function */
723            if (!TrackMouseEvent(&tme))
724                ErrorF("winWindowProc - TrackMouseEvent failed\n");
725
726            /* Flag that we are tracking now */
727            s_fTracking = TRUE;
728        }
729
730        /* Hide or show the Windows mouse cursor */
731        if (g_fSoftwareCursor && g_fCursor &&
732            (s_pScreenPriv->fActive || s_pScreenInfo->fLessPointer)) {
733            /* Hide Windows cursor */
734            g_fCursor = FALSE;
735            ShowCursor(FALSE);
736        }
737        else if (g_fSoftwareCursor && !g_fCursor && !s_pScreenPriv->fActive
738                 && !s_pScreenInfo->fLessPointer) {
739            /* Show Windows cursor */
740            g_fCursor = TRUE;
741            ShowCursor(TRUE);
742        }
743
744        /* Deliver absolute cursor position to X Server */
745        winEnqueueMotion(GET_X_LPARAM(lParam) - s_pScreenInfo->dwXOffset,
746                         GET_Y_LPARAM(lParam) - s_pScreenInfo->dwYOffset);
747        return 0;
748
749    case WM_NCMOUSEMOVE:
750        /*
751         * We break instead of returning 0 since we need to call
752         * DefWindowProc to get the mouse cursor changes
753         * and min/max/close button highlighting in Windows XP.
754         * The Platform SDK says that you should return 0 if you
755         * process this message, but it fails to mention that you
756         * will give up any default functionality if you do return 0.
757         */
758
759        /* We can't do anything without privates */
760        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
761            break;
762
763        /* Non-client mouse movement, show Windows cursor */
764        if (g_fSoftwareCursor && !g_fCursor) {
765            g_fCursor = TRUE;
766            ShowCursor(TRUE);
767        }
768        break;
769
770    case WM_MOUSELEAVE:
771        /* Mouse has left our client area */
772
773        /* Flag that we are no longer tracking */
774        s_fTracking = FALSE;
775
776        /* Show the mouse cursor, if necessary */
777        if (g_fSoftwareCursor && !g_fCursor) {
778            g_fCursor = TRUE;
779            ShowCursor(TRUE);
780        }
781        return 0;
782
783    case WM_LBUTTONDBLCLK:
784    case WM_LBUTTONDOWN:
785        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
786            break;
787        if (s_pScreenInfo->fRootless)
788            SetCapture(hwnd);
789        return winMouseButtonsHandle(s_pScreen, ButtonPress, Button1, wParam);
790
791    case WM_LBUTTONUP:
792        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
793            break;
794        if (s_pScreenInfo->fRootless)
795            ReleaseCapture();
796        return winMouseButtonsHandle(s_pScreen, ButtonRelease, Button1, wParam);
797
798    case WM_MBUTTONDBLCLK:
799    case WM_MBUTTONDOWN:
800        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
801            break;
802        if (s_pScreenInfo->fRootless)
803            SetCapture(hwnd);
804        return winMouseButtonsHandle(s_pScreen, ButtonPress, Button2, wParam);
805
806    case WM_MBUTTONUP:
807        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
808            break;
809        if (s_pScreenInfo->fRootless)
810            ReleaseCapture();
811        return winMouseButtonsHandle(s_pScreen, ButtonRelease, Button2, wParam);
812
813    case WM_RBUTTONDBLCLK:
814    case WM_RBUTTONDOWN:
815        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
816            break;
817        if (s_pScreenInfo->fRootless)
818            SetCapture(hwnd);
819        return winMouseButtonsHandle(s_pScreen, ButtonPress, Button3, wParam);
820
821    case WM_RBUTTONUP:
822        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
823            break;
824        if (s_pScreenInfo->fRootless)
825            ReleaseCapture();
826        return winMouseButtonsHandle(s_pScreen, ButtonRelease, Button3, wParam);
827
828    case WM_XBUTTONDBLCLK:
829    case WM_XBUTTONDOWN:
830        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
831            break;
832        if (s_pScreenInfo->fRootless)
833            SetCapture(hwnd);
834        return winMouseButtonsHandle(s_pScreen, ButtonPress, HIWORD(wParam) + 7,
835                                     wParam);
836    case WM_XBUTTONUP:
837        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
838            break;
839        if (s_pScreenInfo->fRootless)
840            ReleaseCapture();
841        return winMouseButtonsHandle(s_pScreen, ButtonRelease,
842                                     HIWORD(wParam) + 7, wParam);
843
844    case WM_TIMER:
845        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
846            break;
847
848        /* Branch on the timer id */
849        switch (wParam) {
850        case WIN_E3B_TIMER_ID:
851            /* Send delayed button press */
852            winMouseButtonsSendEvent(ButtonPress,
853                                     s_pScreenPriv->iE3BCachedPress);
854
855            /* Kill this timer */
856            KillTimer(s_pScreenPriv->hwndScreen, WIN_E3B_TIMER_ID);
857
858            /* Clear screen privates flags */
859            s_pScreenPriv->iE3BCachedPress = 0;
860            break;
861
862        case WIN_POLLING_MOUSE_TIMER_ID:
863        {
864            static POINT last_point;
865            POINT point;
866            WPARAM wL, wM, wR, wShift, wCtrl;
867            LPARAM lPos;
868
869            /* Get the current position of the mouse cursor */
870            GetCursorPos(&point);
871
872            /* Map from screen (-X, -Y) to root (0, 0) */
873            point.x -= GetSystemMetrics(SM_XVIRTUALSCREEN);
874            point.y -= GetSystemMetrics(SM_YVIRTUALSCREEN);
875
876            /* If the mouse pointer has moved, deliver absolute cursor position to X Server */
877            if (last_point.x != point.x || last_point.y != point.y) {
878                winEnqueueMotion(point.x, point.y);
879                last_point.x = point.x;
880                last_point.y = point.y;
881            }
882
883            /* Check if a button was released but we didn't see it */
884            GetCursorPos(&point);
885            wL = (GetKeyState(VK_LBUTTON) & 0x8000) ? MK_LBUTTON : 0;
886            wM = (GetKeyState(VK_MBUTTON) & 0x8000) ? MK_MBUTTON : 0;
887            wR = (GetKeyState(VK_RBUTTON) & 0x8000) ? MK_RBUTTON : 0;
888            wShift = (GetKeyState(VK_SHIFT) & 0x8000) ? MK_SHIFT : 0;
889            wCtrl = (GetKeyState(VK_CONTROL) & 0x8000) ? MK_CONTROL : 0;
890            lPos = MAKELPARAM(point.x, point.y);
891            if (g_fButton[0] && !wL)
892                PostMessage(hwnd, WM_LBUTTONUP, wCtrl | wM | wR | wShift, lPos);
893            if (g_fButton[1] && !wM)
894                PostMessage(hwnd, WM_MBUTTONUP, wCtrl | wL | wR | wShift, lPos);
895            if (g_fButton[2] && !wR)
896                PostMessage(hwnd, WM_RBUTTONUP, wCtrl | wL | wM | wShift, lPos);
897        }
898        }
899        return 0;
900
901    case WM_CTLCOLORSCROLLBAR:
902        FatalError("winWindowProc - WM_CTLCOLORSCROLLBAR - We are not "
903                   "supposed to get this message.  Exiting.\n");
904        return 0;
905
906    case WM_MOUSEWHEEL:
907        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
908            break;
909#if CYGDEBUG
910        winDebug("winWindowProc - WM_MOUSEWHEEL\n");
911#endif
912        /* Button4 = WheelUp */
913        /* Button5 = WheelDown */
914        winMouseWheel(&(s_pScreenPriv->iDeltaZ), GET_WHEEL_DELTA_WPARAM(wParam), Button4, Button5);
915        break;
916
917    case WM_MOUSEHWHEEL:
918        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
919            break;
920#if CYGDEBUG
921        winDebug("winWindowProc - WM_MOUSEHWHEEL\n");
922#endif
923        /* Button7 = TiltRight */
924        /* Button6 = TiltLeft */
925        winMouseWheel(&(s_pScreenPriv->iDeltaV), GET_WHEEL_DELTA_WPARAM(wParam), 7, 6);
926        break;
927
928    case WM_SETFOCUS:
929        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
930            break;
931
932        /* Restore the state of all mode keys */
933        winRestoreModeKeyStates();
934
935        /* Add the keyboard hook if possible */
936        if (g_fKeyboardHookLL)
937            g_fKeyboardHookLL = winInstallKeyboardHookLL();
938        return 0;
939
940    case WM_KILLFOCUS:
941        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
942            break;
943
944        /* Release any pressed keys */
945        winKeybdReleaseKeys();
946
947        /* Remove our keyboard hook if it is installed */
948        winRemoveKeyboardHookLL();
949        return 0;
950
951    case WM_SYSKEYDOWN:
952    case WM_KEYDOWN:
953        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
954            break;
955
956        /*
957         * FIXME: Catching Alt-F4 like this is really terrible.  This should
958         * be generalized to handle other Windows keyboard signals.  Actually,
959         * the list keys to catch and the actions to perform when caught should
960         * be configurable; that way user's can customize the keys that they
961         * need to have passed through to their window manager or apps, or they
962         * can remap certain actions to new key codes that do not conflict
963         * with the X apps that they are using.  Yeah, that'll take awhile.
964         */
965        if ((s_pScreenInfo->fUseWinKillKey && wParam == VK_F4
966             && (GetKeyState(VK_MENU) & 0x8000))
967            || (s_pScreenInfo->fUseUnixKillKey && wParam == VK_BACK
968                && (GetKeyState(VK_MENU) & 0x8000)
969                && (GetKeyState(VK_CONTROL) & 0x8000))) {
970            /*
971             * Better leave this message here, just in case some unsuspecting
972             * user enters Alt + F4 and is surprised when the application
973             * quits.
974             */
975            ErrorF("winWindowProc - WM_*KEYDOWN - Closekey hit, quitting\n");
976
977            /* Display Exit dialog */
978            winDisplayExitDialog(s_pScreenPriv);
979            return 0;
980        }
981
982        /*
983         * Don't do anything for the Windows keys, as focus will soon
984         * be returned to Windows.  We may be able to trap the Windows keys,
985         * but we should determine if that is desirable before doing so.
986         */
987        if ((wParam == VK_LWIN || wParam == VK_RWIN) && !g_fKeyboardHookLL)
988            break;
989
990        /* Discard fake Ctrl_L events that precede AltGR on non-US keyboards */
991        if (winIsFakeCtrl_L(message, wParam, lParam))
992            return 0;
993
994        /*
995         * Discard presses generated from Windows auto-repeat
996         */
997        if (lParam & (1 << 30)) {
998            switch (wParam) {
999                /* ago: Pressing LControl while RControl is pressed is
1000                 * Indicated as repeat. Fix this!
1001                 */
1002            case VK_CONTROL:
1003            case VK_SHIFT:
1004                if (winCheckKeyPressed(wParam, lParam))
1005                    return 0;
1006                break;
1007            default:
1008                return 0;
1009            }
1010        }
1011
1012        /* Translate Windows key code to X scan code */
1013        iScanCode = winTranslateKey(wParam, lParam);
1014
1015        /* Ignore repeats for CapsLock */
1016        if (wParam == VK_CAPITAL)
1017            lParam = 1;
1018
1019        /* Send the key event(s) */
1020        for (i = 0; i < LOWORD(lParam); ++i)
1021            winSendKeyEvent(iScanCode, TRUE);
1022        return 0;
1023
1024    case WM_SYSKEYUP:
1025    case WM_KEYUP:
1026        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
1027            break;
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        /* Ignore the fake Ctrl_L that follows an AltGr release */
1038        if (winIsFakeCtrl_L(message, wParam, lParam))
1039            return 0;
1040
1041        /* Enqueue a keyup event */
1042        iScanCode = winTranslateKey(wParam, lParam);
1043        winSendKeyEvent(iScanCode, FALSE);
1044
1045        /* Release all pressed shift keys */
1046        if (wParam == VK_SHIFT)
1047            winFixShiftKeys(iScanCode);
1048        return 0;
1049
1050    case WM_ACTIVATE:
1051        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
1052            break;
1053
1054        /* TODO: Override display of window when we have a bad depth */
1055        if (LOWORD(wParam) != WA_INACTIVE && s_pScreenPriv->fBadDepth) {
1056            ErrorF("winWindowProc - WM_ACTIVATE - Bad depth, trying "
1057                   "to override window activation\n");
1058
1059            /* Minimize the window */
1060            ShowWindow(hwnd, SW_MINIMIZE);
1061
1062            /* Display dialog box */
1063            if (g_hDlgDepthChange != NULL) {
1064                /* Make the existing dialog box active */
1065                SetActiveWindow(g_hDlgDepthChange);
1066            }
1067            else {
1068                /* TODO: Recreate the dialog box and bring to the top */
1069                ShowWindow(g_hDlgDepthChange, SW_SHOWDEFAULT);
1070            }
1071
1072            /* Don't do any other processing of this message */
1073            return 0;
1074        }
1075
1076#if CYGDEBUG
1077        winDebug("winWindowProc - WM_ACTIVATE\n");
1078#endif
1079
1080        /*
1081         * Focus is being changed to another window.
1082         * The other window may or may not belong to
1083         * our process.
1084         */
1085
1086        /* Clear any lingering wheel delta */
1087        s_pScreenPriv->iDeltaZ = 0;
1088        s_pScreenPriv->iDeltaV = 0;
1089
1090        /* Reshow the Windows mouse cursor if we are being deactivated */
1091        if (g_fSoftwareCursor && LOWORD(wParam) == WA_INACTIVE && !g_fCursor) {
1092            /* Show Windows cursor */
1093            g_fCursor = TRUE;
1094            ShowCursor(TRUE);
1095        }
1096        return 0;
1097
1098    case WM_ACTIVATEAPP:
1099        if (s_pScreenPriv == NULL || s_pScreenInfo->fIgnoreInput)
1100            break;
1101
1102#if CYGDEBUG || TRUE
1103        winDebug("winWindowProc - WM_ACTIVATEAPP\n");
1104#endif
1105
1106        /* Activate or deactivate */
1107        s_pScreenPriv->fActive = wParam;
1108
1109        /* Reshow the Windows mouse cursor if we are being deactivated */
1110        if (g_fSoftwareCursor && !s_pScreenPriv->fActive && !g_fCursor) {
1111            /* Show Windows cursor */
1112            g_fCursor = TRUE;
1113            ShowCursor(TRUE);
1114        }
1115
1116        /* Call engine specific screen activation/deactivation function */
1117        (*s_pScreenPriv->pwinActivateApp) (s_pScreen);
1118
1119        return 0;
1120
1121    case WM_COMMAND:
1122        switch (LOWORD(wParam)) {
1123        case ID_APP_EXIT:
1124            /* Display Exit dialog */
1125            winDisplayExitDialog(s_pScreenPriv);
1126            return 0;
1127
1128        case ID_APP_HIDE_ROOT:
1129            if (s_pScreenPriv->fRootWindowShown)
1130                ShowWindow(s_pScreenPriv->hwndScreen, SW_HIDE);
1131            else
1132                ShowWindow(s_pScreenPriv->hwndScreen, SW_SHOW);
1133            s_pScreenPriv->fRootWindowShown = !s_pScreenPriv->fRootWindowShown;
1134            return 0;
1135
1136        case ID_APP_MONITOR_PRIMARY:
1137            fPrimarySelection = !fPrimarySelection;
1138            return 0;
1139
1140        case ID_APP_ABOUT:
1141            /* Display the About box */
1142            winDisplayAboutDialog(s_pScreenPriv);
1143            return 0;
1144
1145        default:
1146            /* It's probably one of the custom menus... */
1147            if (HandleCustomWM_COMMAND(0, LOWORD(wParam), s_pScreenPriv))
1148                return 0;
1149        }
1150        break;
1151
1152    case WM_GIVEUP:
1153        /* Tell X that we are giving up */
1154        if (s_pScreenInfo->fMultiWindow)
1155            winDeinitMultiWindowWM();
1156        GiveUp(0);
1157        return 0;
1158
1159    case WM_CLOSE:
1160        /* Display Exit dialog */
1161        winDisplayExitDialog(s_pScreenPriv);
1162        return 0;
1163
1164    case WM_SETCURSOR:
1165        if (LOWORD(lParam) == HTCLIENT) {
1166            if (!g_fSoftwareCursor)
1167                SetCursor(s_pScreenPriv->cursor.handle);
1168            return TRUE;
1169        }
1170        break;
1171
1172    default:
1173        if ((message == s_uTaskbarRestart) && !s_pScreenInfo->fNoTrayIcon)  {
1174            winInitNotifyIcon(s_pScreenPriv);
1175        }
1176        break;
1177    }
1178
1179    return DefWindowProc(hwnd, message, wParam, lParam);
1180}
1181