1/*
2 *Copyright (C) 2001-2004 Harold L Hunt II 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 HAROLD L HUNT II 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 Harold L Hunt II
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 Harold L Hunt II.
27 *
28 * Authors:	Harold L Hunt II
29 */
30
31#ifdef HAVE_XWIN_CONFIG_H
32#include <xwin-config.h>
33#endif
34#include "win.h"
35#include "shellapi.h"
36
37/*
38 * Local function prototypes
39 */
40
41static Bool
42 winGetWorkArea(RECT * prcWorkArea, winScreenInfo * pScreenInfo);
43
44static Bool
45 winAdjustForAutoHide(RECT * prcWorkArea, winScreenInfo * pScreenInfo);
46
47/*
48 * Create a full screen window
49 */
50
51Bool
52winCreateBoundingWindowFullScreen(ScreenPtr pScreen)
53{
54    winScreenPriv(pScreen);
55    winScreenInfo *pScreenInfo = pScreenPriv->pScreenInfo;
56    int iX = pScreenInfo->dwInitialX;
57    int iY = pScreenInfo->dwInitialY;
58    int iWidth = pScreenInfo->dwWidth;
59    int iHeight = pScreenInfo->dwHeight;
60    HWND *phwnd = &pScreenPriv->hwndScreen;
61    WNDCLASSEX wc;
62    char szTitle[256];
63
64#if CYGDEBUG
65    winDebug("winCreateBoundingWindowFullScreen\n");
66#endif
67
68    /* Setup our window class */
69    wc.cbSize = sizeof(WNDCLASSEX);
70    wc.style = CS_HREDRAW | CS_VREDRAW;
71    wc.lpfnWndProc = winWindowProc;
72    wc.cbClsExtra = 0;
73    wc.cbWndExtra = 0;
74    wc.hInstance = g_hInstance;
75    wc.hIcon = pScreenInfo->hIcon;
76    wc.hCursor = 0;
77    wc.hbrBackground = 0;
78    wc.lpszMenuName = NULL;
79    wc.lpszClassName = WINDOW_CLASS;
80    wc.hIconSm = pScreenInfo->hIconSm;
81    RegisterClassEx(&wc);
82
83    /* Set display and screen-specific tooltip text */
84    if (g_pszQueryHost != NULL)
85        snprintf(szTitle,
86                 sizeof(szTitle),
87                 WINDOW_TITLE_XDMCP,
88                 g_pszQueryHost, display, (int) pScreenInfo->dwScreen);
89    else
90        snprintf(szTitle,
91                 sizeof(szTitle),
92                 WINDOW_TITLE, display, (int) pScreenInfo->dwScreen);
93
94    /* Create the window */
95    *phwnd = CreateWindowExA(0, /* Extended styles */
96                             WINDOW_CLASS,      /* Class name */
97                             szTitle,   /* Window name */
98                             WS_POPUP, iX,      /* Horizontal position */
99                             iY,        /* Vertical position */
100                             iWidth,    /* Right edge */
101                             iHeight,   /* Bottom edge */
102                             (HWND) NULL,       /* No parent or owner window */
103                             (HMENU) NULL,      /* No menu */
104                             GetModuleHandle(NULL),     /* Instance handle */
105                             pScreenPriv);      /* ScreenPrivates */
106
107    /* Hide the window */
108    ShowWindow(*phwnd, SW_SHOWNORMAL);
109
110    /* Send first paint message */
111    UpdateWindow(*phwnd);
112
113    /* Attempt to bring our window to the top of the display */
114    BringWindowToTop(*phwnd);
115
116    return TRUE;
117}
118
119/*
120 * Create our primary Windows display window
121 */
122
123Bool
124winCreateBoundingWindowWindowed(ScreenPtr pScreen)
125{
126    winScreenPriv(pScreen);
127    winScreenInfo *pScreenInfo = pScreenPriv->pScreenInfo;
128    int iWidth = pScreenInfo->dwUserWidth;
129    int iHeight = pScreenInfo->dwUserHeight;
130    int iPosX;
131    int iPosY;
132    HWND *phwnd = &pScreenPriv->hwndScreen;
133    WNDCLASSEX wc;
134    RECT rcClient, rcWorkArea;
135    DWORD dwWindowStyle;
136    BOOL fForceShowWindow = FALSE;
137    char szTitle[256];
138
139    winDebug("winCreateBoundingWindowWindowed - User w: %d h: %d\n",
140             (int) pScreenInfo->dwUserWidth, (int) pScreenInfo->dwUserHeight);
141    winDebug("winCreateBoundingWindowWindowed - Current w: %d h: %d\n",
142             (int) pScreenInfo->dwWidth, (int) pScreenInfo->dwHeight);
143
144    /* Set the common window style flags */
145    dwWindowStyle = WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX;
146
147    /* Decorated or undecorated window */
148    if (pScreenInfo->fDecoration
149        && !pScreenInfo->fRootless
150        && !pScreenInfo->fMultiWindow
151        ) {
152        /* Try to handle startup via run.exe. run.exe instructs Windows to
153         * hide all created windows. Detect this case and make sure the
154         * window is shown nevertheless */
155        STARTUPINFO startupInfo;
156
157        GetStartupInfo(&startupInfo);
158        if (startupInfo.dwFlags & STARTF_USESHOWWINDOW &&
159            startupInfo.wShowWindow == SW_HIDE) {
160            fForceShowWindow = TRUE;
161        }
162        dwWindowStyle |= WS_CAPTION;
163        if (pScreenInfo->iResizeMode != resizeNotAllowed)
164            dwWindowStyle |= WS_THICKFRAME | WS_MAXIMIZEBOX;
165    }
166    else
167        dwWindowStyle |= WS_POPUP;
168
169    /* Setup our window class */
170    wc.cbSize = sizeof(WNDCLASSEX);
171    wc.style = CS_HREDRAW | CS_VREDRAW;
172    wc.lpfnWndProc = winWindowProc;
173    wc.cbClsExtra = 0;
174    wc.cbWndExtra = 0;
175    wc.hInstance = g_hInstance;
176    wc.hIcon = pScreenInfo->hIcon;
177    wc.hCursor = 0;
178    wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
179    wc.lpszMenuName = NULL;
180    wc.lpszClassName = WINDOW_CLASS;
181    wc.hIconSm = pScreenInfo->hIconSm;
182    RegisterClassEx(&wc);
183
184    /* Get size of work area */
185    winGetWorkArea(&rcWorkArea, pScreenInfo);
186
187    /* Adjust for auto-hide taskbars */
188    winAdjustForAutoHide(&rcWorkArea, pScreenInfo);
189
190    /* Did the user specify a position? */
191    if (pScreenInfo->fUserGavePosition) {
192        iPosX = pScreenInfo->dwInitialX;
193        iPosY = pScreenInfo->dwInitialY;
194    }
195    else {
196        iPosX = rcWorkArea.left;
197        iPosY = rcWorkArea.top;
198    }
199
200    /* Clean up the scrollbars flag, if necessary */
201    if ((!pScreenInfo->fDecoration
202         || pScreenInfo->fRootless
203         || pScreenInfo->fMultiWindow
204        )
205        && (pScreenInfo->iResizeMode == resizeWithScrollbars)) {
206        /* We cannot have scrollbars if we do not have a window border */
207        pScreenInfo->iResizeMode = resizeNotAllowed;
208    }
209
210    /* Did the user specify a height and width? */
211    if (pScreenInfo->fUserGaveHeightAndWidth) {
212        /* User gave a desired height and width, try to accommodate */
213#if CYGDEBUG
214        winDebug("winCreateBoundingWindowWindowed - User gave height "
215                 "and width\n");
216#endif
217
218        /* Adjust the window width and height for borders and title bar */
219        if (pScreenInfo->fDecoration
220            && !pScreenInfo->fRootless
221            && !pScreenInfo->fMultiWindow
222            ) {
223#if CYGDEBUG
224            winDebug
225                ("winCreateBoundingWindowWindowed - Window has decoration\n");
226#endif
227
228            /* Are we resizable */
229            if (pScreenInfo->iResizeMode != resizeNotAllowed) {
230#if CYGDEBUG
231                winDebug
232                    ("winCreateBoundingWindowWindowed - Window is resizable\n");
233#endif
234
235                iWidth += 2 * GetSystemMetrics(SM_CXSIZEFRAME);
236                iHeight += 2 * GetSystemMetrics(SM_CYSIZEFRAME)
237                    + GetSystemMetrics(SM_CYCAPTION);
238            }
239            else {
240#if CYGDEBUG
241                winDebug
242                    ("winCreateBoundingWindowWindowed - Window is not resizable\n");
243#endif
244
245                iWidth += 2 * GetSystemMetrics(SM_CXFIXEDFRAME);
246                iHeight += 2 * GetSystemMetrics(SM_CYFIXEDFRAME)
247                    + GetSystemMetrics(SM_CYCAPTION);
248            }
249        }
250    }
251    else {
252        /* By default, we are creating a window that is as large as possible */
253#if CYGDEBUG
254        winDebug("winCreateBoundingWindowWindowed - User did not give "
255                 "height and width\n");
256#endif
257        /* Defaults are wrong if we have multiple monitors */
258        if (pScreenInfo->fMultipleMonitors) {
259            iWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
260            iHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
261        }
262    }
263
264    /* Make sure window is no bigger than work area */
265    if (TRUE
266        && !pScreenInfo->fMultiWindow
267        ) {
268        /* Trim window width to fit work area */
269        if (iWidth > (rcWorkArea.right - rcWorkArea.left))
270            iWidth = rcWorkArea.right - rcWorkArea.left;
271
272        /* Trim window height to fit work area */
273        if (iHeight >= (rcWorkArea.bottom - rcWorkArea.top))
274            iHeight = rcWorkArea.bottom - rcWorkArea.top;
275
276#if CYGDEBUG
277        winDebug("winCreateBoundingWindowWindowed - Adjusted width: %d "
278                 "height: %d\n", iWidth, iHeight);
279#endif
280    }
281
282    /* Set display and screen-specific tooltip text */
283    if (g_pszQueryHost != NULL)
284        snprintf(szTitle,
285                 sizeof(szTitle),
286                 WINDOW_TITLE_XDMCP,
287                 g_pszQueryHost, display, (int) pScreenInfo->dwScreen);
288    else
289        snprintf(szTitle,
290                 sizeof(szTitle),
291                 WINDOW_TITLE, display, (int) pScreenInfo->dwScreen);
292
293    /* Create the window */
294    *phwnd = CreateWindowExA(0, /* Extended styles */
295                             WINDOW_CLASS,      /* Class name */
296                             szTitle,   /* Window name */
297                             dwWindowStyle, iPosX,      /* Horizontal position */
298                             iPosY,     /* Vertical position */
299                             iWidth,    /* Right edge */
300                             iHeight,   /* Bottom edge */
301                             (HWND) NULL,       /* No parent or owner window */
302                             (HMENU) NULL,      /* No menu */
303                             GetModuleHandle(NULL),     /* Instance handle */
304                             pScreenPriv);      /* ScreenPrivates */
305    if (*phwnd == NULL) {
306        ErrorF("winCreateBoundingWindowWindowed - CreateWindowEx () failed\n");
307        return FALSE;
308    }
309
310#if CYGDEBUG
311    winDebug("winCreateBoundingWindowWindowed - CreateWindowEx () returned\n");
312#endif
313
314    if (fForceShowWindow) {
315        ErrorF
316            ("winCreateBoundingWindowWindowed - Setting normal windowstyle\n");
317        ShowWindow(*phwnd, SW_SHOW);
318    }
319
320    /* Get the client area coordinates */
321    if (!GetClientRect(*phwnd, &rcClient)) {
322        ErrorF("winCreateBoundingWindowWindowed - GetClientRect () "
323               "failed\n");
324        return FALSE;
325    }
326
327    winDebug("winCreateBoundingWindowWindowed - WindowClient "
328             "w %d  h %d r %d l %d b %d t %d\n",
329             (int)(rcClient.right - rcClient.left),
330             (int)(rcClient.bottom - rcClient.top),
331             (int)rcClient.right, (int)rcClient.left,
332             (int)rcClient.bottom, (int)rcClient.top);
333
334    /* We adjust the visual size if the user did not specify it */
335    if (!
336        ((pScreenInfo->iResizeMode == resizeWithScrollbars) &&
337         pScreenInfo->fUserGaveHeightAndWidth)) {
338        /*
339         * User did not give a height and width with scrollbars enabled,
340         * so we will resize the underlying visual to be as large as
341         * the initial view port (page size).  This way scrollbars will
342         * not appear until the user shrinks the window, if they ever do.
343         *
344         * NOTE: We have to store the viewport size here because
345         * the user may have an autohide taskbar, which would
346         * cause the viewport size to be one less in one dimension
347         * than the viewport size that we calculated by subtracting
348         * the size of the borders and caption.
349         */
350        pScreenInfo->dwWidth = rcClient.right - rcClient.left;
351        pScreenInfo->dwHeight = rcClient.bottom - rcClient.top;
352    }
353
354#if 0
355    /*
356     * NOTE: For the uninitiated, the page size is the number of pixels
357     * that we can display in the x or y direction at a time and the
358     * range is the total number of pixels in the x or y direction that we
359     * have available to display.  In other words, the page size is the
360     * size of the window area minus the space the caption, borders, and
361     * scrollbars (if any) occupy, and the range is the size of the
362     * underlying X visual.  Notice that, contrary to what some of the
363     * MSDN Library arcticles lead you to believe, the windows
364     * ``client area'' size does not include the scrollbars.  In other words,
365     * the whole client area size that is reported to you is drawable by
366     * you; you do not have to subtract the size of the scrollbars from
367     * the client area size, and if you did it would result in the size
368     * of the scrollbars being double counted.
369     */
370
371    /* Setup scrollbar page and range, if scrollbars are enabled */
372    if (pScreenInfo->fScrollbars) {
373        SCROLLINFO si;
374
375        /* Initialize the scrollbar info structure */
376        si.cbSize = sizeof(si);
377        si.fMask = SIF_RANGE | SIF_PAGE;
378        si.nMin = 0;
379
380        /* Setup the width range and page size */
381        si.nMax = pScreenInfo->dwWidth - 1;
382        si.nPage = rcClient.right - rcClient.left;
383        winDebug("winCreateBoundingWindowWindowed - HORZ nMax: %d nPage :%d\n",
384                 si.nMax, si.nPage);
385        SetScrollInfo(*phwnd, SB_HORZ, &si, TRUE);
386
387        /* Setup the height range and page size */
388        si.nMax = pScreenInfo->dwHeight - 1;
389        si.nPage = rcClient.bottom - rcClient.top;
390        winDebug("winCreateBoundingWindowWindowed - VERT nMax: %d nPage :%d\n",
391                 si.nMax, si.nPage);
392        SetScrollInfo(*phwnd, SB_VERT, &si, TRUE);
393    }
394#endif
395
396    /* Show the window */
397    if (FALSE
398        || pScreenInfo->fMultiWindow
399        ) {
400        pScreenPriv->fRootWindowShown = FALSE;
401        ShowWindow(*phwnd, SW_HIDE);
402    }
403    else
404        ShowWindow(*phwnd, SW_SHOWNORMAL);
405    if (!UpdateWindow(*phwnd)) {
406        ErrorF("winCreateBoundingWindowWindowed - UpdateWindow () failed\n");
407        return FALSE;
408    }
409
410    /* Attempt to bring our window to the top of the display */
411    if (TRUE
412        && !pScreenInfo->fRootless
413        && !pScreenInfo->fMultiWindow
414        ) {
415        if (!BringWindowToTop(*phwnd)) {
416            ErrorF("winCreateBoundingWindowWindowed - BringWindowToTop () "
417                   "failed\n");
418            return FALSE;
419        }
420    }
421
422    winDebug("winCreateBoundingWindowWindowed -  Returning\n");
423
424    return TRUE;
425}
426
427/*
428 * Find the work area of all attached monitors
429 */
430
431static Bool
432winGetWorkArea(RECT * prcWorkArea, winScreenInfo * pScreenInfo)
433{
434    int iPrimaryWidth, iPrimaryHeight;
435    int iWidth, iHeight;
436    int iLeft, iTop;
437    int iPrimaryNonWorkAreaWidth, iPrimaryNonWorkAreaHeight;
438
439    /* Use GetMonitorInfo to get work area for monitor */
440    if (!pScreenInfo->fMultipleMonitors) {
441        MONITORINFO mi;
442
443        mi.cbSize = sizeof(MONITORINFO);
444        if (GetMonitorInfo(pScreenInfo->hMonitor, &mi)) {
445            *prcWorkArea = mi.rcWork;
446
447            winDebug("winGetWorkArea - Monitor %d WorkArea: %d %d %d %d\n",
448                     pScreenInfo->iMonitor,
449                     (int) prcWorkArea->top, (int) prcWorkArea->left,
450                     (int) prcWorkArea->bottom, (int) prcWorkArea->right);
451        }
452        else {
453            ErrorF("winGetWorkArea - GetMonitorInfo() failed for monitor %d\n",
454                   pScreenInfo->iMonitor);
455        }
456
457        /* Bail out here if we aren't using multiple monitors */
458        return TRUE;
459    }
460
461    /* SPI_GETWORKAREA only gets the work area of the primary screen. */
462    SystemParametersInfo(SPI_GETWORKAREA, 0, prcWorkArea, 0);
463
464    winDebug("winGetWorkArea - Primary Monitor WorkArea: %d %d %d %d\n",
465             (int) prcWorkArea->top, (int) prcWorkArea->left,
466             (int) prcWorkArea->bottom, (int) prcWorkArea->right);
467
468    /* Get size of full virtual screen */
469    iWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
470    iHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
471
472    winDebug("winGetWorkArea - Virtual screen is %d x %d\n", iWidth, iHeight);
473
474    /* Get origin of full virtual screen */
475    iLeft = GetSystemMetrics(SM_XVIRTUALSCREEN);
476    iTop = GetSystemMetrics(SM_YVIRTUALSCREEN);
477
478    winDebug("winGetWorkArea - Virtual screen origin is %d, %d\n", iLeft, iTop);
479
480    /* Get size of primary screen */
481    iPrimaryWidth = GetSystemMetrics(SM_CXSCREEN);
482    iPrimaryHeight = GetSystemMetrics(SM_CYSCREEN);
483
484    winDebug("winGetWorkArea - Primary screen is %d x %d\n",
485             iPrimaryWidth, iPrimaryHeight);
486
487    /* Work out how much of the primary screen we aren't using */
488    iPrimaryNonWorkAreaWidth = iPrimaryWidth - (prcWorkArea->right -
489                                                prcWorkArea->left);
490    iPrimaryNonWorkAreaHeight = iPrimaryHeight - (prcWorkArea->bottom
491                                                  - prcWorkArea->top);
492
493    /* Update the rectangle to include all monitors */
494    if (iLeft < 0) {
495        prcWorkArea->left = iLeft;
496    }
497    if (iTop < 0) {
498        prcWorkArea->top = iTop;
499    }
500    prcWorkArea->right = prcWorkArea->left + iWidth - iPrimaryNonWorkAreaWidth;
501    prcWorkArea->bottom = prcWorkArea->top + iHeight -
502        iPrimaryNonWorkAreaHeight;
503
504    winDebug("winGetWorkArea - Adjusted WorkArea for multiple "
505             "monitors: %d %d %d %d\n",
506             (int) prcWorkArea->top, (int) prcWorkArea->left,
507             (int) prcWorkArea->bottom, (int) prcWorkArea->right);
508
509    return TRUE;
510}
511
512static Bool
513winTaskbarOnScreenEdge(unsigned int uEdge, winScreenInfo * pScreenInfo)
514{
515    APPBARDATA abd;
516    HWND hwndAutoHide;
517
518    ZeroMemory(&abd, sizeof(abd));
519    abd.cbSize = sizeof(abd);
520    abd.uEdge = uEdge;
521
522    hwndAutoHide = (HWND) SHAppBarMessage(ABM_GETAUTOHIDEBAR, &abd);
523    if (hwndAutoHide != NULL) {
524        /*
525           Found an autohide taskbar on that edge, but is it on the
526           same monitor as the screen window?
527         */
528        if (pScreenInfo->fMultipleMonitors ||
529            (MonitorFromWindow(hwndAutoHide, MONITOR_DEFAULTTONULL) ==
530             pScreenInfo->hMonitor))
531            return TRUE;
532    }
533    return FALSE;
534}
535
536/*
537 * Adjust the client area so that any auto-hide toolbars
538 * will work correctly.
539 */
540
541static Bool
542winAdjustForAutoHide(RECT * prcWorkArea, winScreenInfo * pScreenInfo)
543{
544    APPBARDATA abd;
545
546    winDebug("winAdjustForAutoHide - Original WorkArea: %d %d %d %d\n",
547             (int) prcWorkArea->top, (int) prcWorkArea->left,
548             (int) prcWorkArea->bottom, (int) prcWorkArea->right);
549
550    /* Find out if the Windows taskbar is set to auto-hide */
551    ZeroMemory(&abd, sizeof(abd));
552    abd.cbSize = sizeof(abd);
553    if (SHAppBarMessage(ABM_GETSTATE, &abd) & ABS_AUTOHIDE)
554        winDebug("winAdjustForAutoHide - Taskbar is auto hide\n");
555
556    /*
557       Despite the forgoing, we are checking for any AppBar
558       hiding along a monitor edge, not just the Windows TaskBar.
559     */
560
561    /* Look for a TOP auto-hide taskbar */
562    if (winTaskbarOnScreenEdge(ABE_TOP, pScreenInfo)) {
563        winDebug("winAdjustForAutoHide - Found TOP auto-hide taskbar\n");
564        prcWorkArea->top += 1;
565    }
566
567    /* Look for a LEFT auto-hide taskbar */
568    if (winTaskbarOnScreenEdge(ABE_LEFT, pScreenInfo)) {
569        winDebug("winAdjustForAutoHide - Found LEFT auto-hide taskbar\n");
570        prcWorkArea->left += 1;
571    }
572
573    /* Look for a BOTTOM auto-hide taskbar */
574    if (winTaskbarOnScreenEdge(ABE_BOTTOM, pScreenInfo)) {
575        winDebug("winAdjustForAutoHide - Found BOTTOM auto-hide taskbar\n");
576        prcWorkArea->bottom -= 1;
577    }
578
579    /* Look for a RIGHT auto-hide taskbar */
580    if (winTaskbarOnScreenEdge(ABE_RIGHT, pScreenInfo)) {
581        winDebug("winAdjustForAutoHide - Found RIGHT auto-hide taskbar\n");
582        prcWorkArea->right -= 1;
583    }
584
585    winDebug("winAdjustForAutoHide - Adjusted WorkArea: %d %d %d %d\n",
586             (int) prcWorkArea->top, (int) prcWorkArea->left,
587             (int) prcWorkArea->bottom, (int) prcWorkArea->right);
588
589    return TRUE;
590}
591