winmultiwindowwm.c revision 1b5d61b8
1/*
2 *Copyright (C) 1994-2000 The XFree86 Project, Inc. All Rights Reserved.
3 *Copyright (C) Colin Harrison 2005-2009
4 *
5 *Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 *"Software"), to deal in the Software without restriction, including
8 *without limitation the rights to use, copy, modify, merge, publish,
9 *distribute, sublicense, and/or sell copies of the Software, and to
10 *permit persons to whom the Software is furnished to do so, subject to
11 *the following conditions:
12 *
13 *The above copyright notice and this permission notice shall be
14 *included in all copies or substantial portions of the Software.
15 *
16 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 *EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 *MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 *NONINFRINGEMENT. IN NO EVENT SHALL THE XFREE86 PROJECT BE LIABLE FOR
20 *ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
21 *CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 *WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 *
24 *Except as contained in this notice, the name of the XFree86 Project
25 *shall not be used in advertising or otherwise to promote the sale, use
26 *or other dealings in this Software without prior written authorization
27 *from the XFree86 Project.
28 *
29 * Authors:	Kensuke Matsuzaki
30 *              Colin Harrison
31 */
32
33/* X headers */
34#ifdef HAVE_XWIN_CONFIG_H
35#include <xwin-config.h>
36#endif
37#include <stdio.h>
38#include <stdlib.h>
39#include <unistd.h>
40#ifdef __CYGWIN__
41#include <sys/select.h>
42#endif
43#include <fcntl.h>
44#include <setjmp.h>
45#define HANDLE void *
46#include <pthread.h>
47#undef HANDLE
48#include <xcb/xcb.h>
49#include <xcb/xcb_icccm.h>
50#include <xcb/xcb_ewmh.h>
51#include <xcb/xcb_aux.h>
52
53#include <X11/Xwindows.h>
54
55/* Local headers */
56#include "X11/Xdefs.h" // for Bool type
57#include "winwindow.h"
58#include "winprefs.h"
59#include "window.h"
60#include "pixmapstr.h"
61#include "windowstr.h"
62#include "winglobals.h"
63#include "windisplay.h"
64#include "winmultiwindowicons.h"
65
66/* We need the native HWND atom for intWM, so for consistency use the
67   same name as extWM does */
68#define WINDOWSWM_NATIVE_HWND "_WINDOWSWM_NATIVE_HWND"
69
70#ifndef HOST_NAME_MAX
71#define HOST_NAME_MAX 255
72#endif
73
74extern void winDebug(const char *format, ...);
75extern void winReshapeMultiWindow(WindowPtr pWin);
76extern void winUpdateRgnMultiWindow(WindowPtr pWin);
77extern xcb_auth_info_t *winGetXcbAuthInfo(void);
78
79#ifndef CYGDEBUG
80#define CYGDEBUG NO
81#endif
82
83/*
84 * Constant defines
85 */
86
87#define WIN_CONNECT_RETRIES	5
88#define WIN_CONNECT_DELAY	5
89#ifdef HAS_DEVWINDOWS
90#define WIN_MSG_QUEUE_FNAME	"/dev/windows"
91#endif
92
93/*
94 * Local structures
95 */
96
97typedef struct _WMMsgNodeRec {
98    winWMMessageRec msg;
99    struct _WMMsgNodeRec *pNext;
100} WMMsgNodeRec, *WMMsgNodePtr;
101
102typedef struct _WMMsgQueueRec {
103    struct _WMMsgNodeRec *pHead;
104    struct _WMMsgNodeRec *pTail;
105    pthread_mutex_t pmMutex;
106    pthread_cond_t pcNotEmpty;
107} WMMsgQueueRec, *WMMsgQueuePtr;
108
109typedef struct _WMInfo {
110    xcb_connection_t *conn;
111    WMMsgQueueRec wmMsgQueue;
112    xcb_atom_t atmWmProtos;
113    xcb_atom_t atmWmDelete;
114    xcb_atom_t atmWmTakeFocus;
115    xcb_atom_t atmPrivMap;
116    xcb_atom_t atmUtf8String;
117    xcb_atom_t atmNetWmName;
118    xcb_ewmh_connection_t ewmh;
119} WMInfoRec, *WMInfoPtr;
120
121typedef struct _WMProcArgRec {
122    DWORD dwScreen;
123    WMInfoPtr pWMInfo;
124    pthread_mutex_t *ppmServerStarted;
125} WMProcArgRec, *WMProcArgPtr;
126
127typedef struct _XMsgProcArgRec {
128    xcb_connection_t *conn;
129    DWORD dwScreen;
130    WMInfoPtr pWMInfo;
131    pthread_mutex_t *ppmServerStarted;
132    HWND hwndScreen;
133} XMsgProcArgRec, *XMsgProcArgPtr;
134
135/*
136 * Prototypes for local functions
137 */
138
139static void
140 PushMessage(WMMsgQueuePtr pQueue, WMMsgNodePtr pNode);
141
142static WMMsgNodePtr PopMessage(WMMsgQueuePtr pQueue, WMInfoPtr pWMInfo);
143
144static Bool
145 InitQueue(WMMsgQueuePtr pQueue);
146
147static void
148 GetWindowName(WMInfoPtr pWMInfo, xcb_window_t iWin, char **ppWindowName);
149
150static void
151 SendXMessage(xcb_connection_t *conn, xcb_window_t iWin, xcb_atom_t atmType, long nData);
152
153static void
154 UpdateName(WMInfoPtr pWMInfo, xcb_window_t iWindow);
155
156static void *winMultiWindowWMProc(void *pArg);
157
158static void *winMultiWindowXMsgProc(void *pArg);
159
160static void
161 winInitMultiWindowWM(WMInfoPtr pWMInfo, WMProcArgPtr pProcArg);
162
163#if 0
164static void
165 PreserveWin32Stack(WMInfoPtr pWMInfo, xcb_window_t iWindow, UINT direction);
166#endif
167
168static Bool
169CheckAnotherWindowManager(xcb_connection_t *conn, DWORD dwScreen);
170
171static void
172 winApplyHints(WMInfoPtr pWMInfo, xcb_window_t iWindow, HWND hWnd, HWND * zstyle);
173
174void
175 winUpdateWindowPosition(HWND hWnd, HWND * zstyle);
176
177/*
178 * Local globals
179 */
180
181static Bool g_shutdown = FALSE;
182
183/*
184 * Translate msg id to text, for debug purposes
185 */
186
187#if CYGMULTIWINDOW_DEBUG
188static const char *
189MessageName(winWMMessagePtr msg)
190{
191  switch (msg->msg)
192    {
193    case WM_WM_MOVE:
194      return "WM_WM_MOVE";
195      break;
196    case WM_WM_SIZE:
197      return "WM_WM_SIZE";
198      break;
199    case WM_WM_RAISE:
200      return "WM_WM_RAISE";
201      break;
202    case WM_WM_LOWER:
203      return "WM_WM_LOWER";
204      break;
205    case WM_WM_UNMAP:
206      return "WM_WM_UNMAP";
207      break;
208    case WM_WM_KILL:
209      return "WM_WM_KILL";
210      break;
211    case WM_WM_ACTIVATE:
212      return "WM_WM_ACTIVATE";
213      break;
214    case WM_WM_NAME_EVENT:
215      return "WM_WM_NAME_EVENT";
216      break;
217    case WM_WM_ICON_EVENT:
218      return "WM_WM_ICON_EVENT";
219      break;
220    case WM_WM_CHANGE_STATE:
221      return "WM_WM_CHANGE_STATE";
222      break;
223    case WM_WM_MAP2:
224      return "WM_WM_MAP2";
225      break;
226    case WM_WM_MAP3:
227      return "WM_WM_MAP3";
228      break;
229    case WM_WM_HINTS_EVENT:
230      return "WM_WM_HINTS_EVENT";
231      break;
232    default:
233      return "Unknown Message";
234      break;
235    }
236}
237#endif
238
239
240/*
241 * PushMessage - Push a message onto the queue
242 */
243
244static void
245PushMessage(WMMsgQueuePtr pQueue, WMMsgNodePtr pNode)
246{
247
248    /* Lock the queue mutex */
249    pthread_mutex_lock(&pQueue->pmMutex);
250
251    pNode->pNext = NULL;
252
253    if (pQueue->pTail != NULL) {
254        pQueue->pTail->pNext = pNode;
255    }
256    pQueue->pTail = pNode;
257
258    if (pQueue->pHead == NULL) {
259        pQueue->pHead = pNode;
260    }
261
262    /* Release the queue mutex */
263    pthread_mutex_unlock(&pQueue->pmMutex);
264
265    /* Signal that the queue is not empty */
266    pthread_cond_signal(&pQueue->pcNotEmpty);
267}
268
269/*
270 * PopMessage - Pop a message from the queue
271 */
272
273static WMMsgNodePtr
274PopMessage(WMMsgQueuePtr pQueue, WMInfoPtr pWMInfo)
275{
276    WMMsgNodePtr pNode;
277
278    /* Lock the queue mutex */
279    pthread_mutex_lock(&pQueue->pmMutex);
280
281    /* Wait for --- */
282    while (pQueue->pHead == NULL) {
283        pthread_cond_wait(&pQueue->pcNotEmpty, &pQueue->pmMutex);
284    }
285
286    pNode = pQueue->pHead;
287    if (pQueue->pHead != NULL) {
288        pQueue->pHead = pQueue->pHead->pNext;
289    }
290
291    if (pQueue->pTail == pNode) {
292        pQueue->pTail = NULL;
293    }
294
295    /* Release the queue mutex */
296    pthread_mutex_unlock(&pQueue->pmMutex);
297
298    return pNode;
299}
300
301#if 0
302/*
303 * HaveMessage -
304 */
305
306static Bool
307HaveMessage(WMMsgQueuePtr pQueue, UINT msg, xcb_window_t iWindow)
308{
309    WMMsgNodePtr pNode;
310
311    for (pNode = pQueue->pHead; pNode != NULL; pNode = pNode->pNext) {
312        if (pNode->msg.msg == msg && pNode->msg.iWindow == iWindow)
313            return True;
314    }
315
316    return False;
317}
318#endif
319
320/*
321 * InitQueue - Initialize the Window Manager message queue
322 */
323
324static
325    Bool
326InitQueue(WMMsgQueuePtr pQueue)
327{
328    /* Check if the pQueue pointer is NULL */
329    if (pQueue == NULL) {
330        ErrorF("InitQueue - pQueue is NULL.  Exiting.\n");
331        return FALSE;
332    }
333
334    /* Set the head and tail to NULL */
335    pQueue->pHead = NULL;
336    pQueue->pTail = NULL;
337
338    winDebug("InitQueue - Calling pthread_mutex_init\n");
339
340    /* Create synchronization objects */
341    pthread_mutex_init(&pQueue->pmMutex, NULL);
342
343    winDebug("InitQueue - pthread_mutex_init returned\n");
344    winDebug("InitQueue - Calling pthread_cond_init\n");
345
346    pthread_cond_init(&pQueue->pcNotEmpty, NULL);
347
348    winDebug("InitQueue - pthread_cond_init returned\n");
349
350    return TRUE;
351}
352
353static
354char *
355Xutf8TextPropertyToString(WMInfoPtr pWMInfo, xcb_icccm_get_text_property_reply_t *xtp)
356{
357    char *pszReturnData;
358
359    if ((xtp->encoding == XCB_ATOM_STRING) ||        // Latin1 ISO 8859-1
360        (xtp->encoding == pWMInfo->atmUtf8String)) { // UTF-8  ISO 10646
361        pszReturnData = strndup(xtp->name, xtp->name_len);
362    }
363    else {
364        // Converting from COMPOUND_TEXT to UTF-8 properly is complex to
365        // implement, and not very much use unless you have an old
366        // application which isn't UTF-8 aware.
367        ErrorF("Xutf8TextPropertyToString: text encoding %d is not implemented\n", xtp->encoding);
368        pszReturnData = strdup("");
369    }
370
371    return pszReturnData;
372}
373
374/*
375 * GetWindowName - Retrieve the title of an X Window
376 */
377
378static void
379GetWindowName(WMInfoPtr pWMInfo, xcb_window_t iWin, char **ppWindowName)
380{
381    xcb_connection_t *conn = pWMInfo->conn;
382    char *pszWindowName = NULL;
383
384#if CYGMULTIWINDOW_DEBUG
385    ErrorF("GetWindowName\n");
386#endif
387
388    /* Try to get window name from _NET_WM_NAME */
389    {
390        xcb_get_property_cookie_t cookie;
391        xcb_get_property_reply_t *reply;
392
393        cookie = xcb_get_property(pWMInfo->conn, FALSE, iWin,
394                                  pWMInfo->atmNetWmName,
395                                  XCB_GET_PROPERTY_TYPE_ANY, 0, INT_MAX);
396        reply = xcb_get_property_reply(pWMInfo->conn, cookie, NULL);
397        if (reply && (reply->type != XCB_NONE)) {
398            pszWindowName = strndup(xcb_get_property_value(reply),
399                                    xcb_get_property_value_length(reply));
400            free(reply);
401        }
402    }
403
404    /* Otherwise, try to get window name from WM_NAME */
405    if (!pszWindowName)
406        {
407            xcb_get_property_cookie_t cookie;
408            xcb_icccm_get_text_property_reply_t reply;
409
410            cookie = xcb_icccm_get_wm_name(conn, iWin);
411            if (!xcb_icccm_get_wm_name_reply(conn, cookie, &reply, NULL)) {
412                ErrorF("GetWindowName - xcb_icccm_get_wm_name_reply failed.  No name.\n");
413                *ppWindowName = NULL;
414                return;
415            }
416
417            pszWindowName = Xutf8TextPropertyToString(pWMInfo, &reply);
418            xcb_icccm_get_text_property_reply_wipe(&reply);
419        }
420
421    /* return the window name, unless... */
422    *ppWindowName = pszWindowName;
423
424    if (g_fHostInTitle) {
425        xcb_get_property_cookie_t cookie;
426        xcb_icccm_get_text_property_reply_t reply;
427
428        /* Try to get client machine name */
429        cookie = xcb_icccm_get_wm_client_machine(conn, iWin);
430        if (xcb_icccm_get_wm_client_machine_reply(conn, cookie, &reply, NULL)) {
431            char *pszClientMachine;
432            char *pszClientHostname;
433            char *dot;
434            char hostname[HOST_NAME_MAX + 1];
435
436            pszClientMachine = Xutf8TextPropertyToString(pWMInfo, &reply);
437            xcb_icccm_get_text_property_reply_wipe(&reply);
438
439            /* If client machine name looks like a FQDN, find the hostname */
440            pszClientHostname = strdup(pszClientMachine);
441            dot = strchr(pszClientHostname, '.');
442            if (dot)
443                *dot = '\0';
444
445            /*
446               If we have a client machine hostname
447               and it's not the local hostname
448               and it's not already in the window title...
449             */
450            if (strlen(pszClientHostname) &&
451                !gethostname(hostname, HOST_NAME_MAX + 1) &&
452                strcmp(hostname, pszClientHostname) &&
453                (strstr(pszWindowName, pszClientHostname) == 0)) {
454                /* ... add '@<clientmachine>' to end of window name */
455                *ppWindowName =
456                    malloc(strlen(pszWindowName) +
457                           strlen(pszClientMachine) + 2);
458                strcpy(*ppWindowName, pszWindowName);
459                strcat(*ppWindowName, "@");
460                strcat(*ppWindowName, pszClientMachine);
461
462                free(pszWindowName);
463            }
464
465            free(pszClientMachine);
466            free(pszClientHostname);
467        }
468    }
469}
470
471/*
472 * Does the client support the specified WM_PROTOCOLS protocol?
473 */
474
475static Bool
476IsWmProtocolAvailable(WMInfoPtr pWMInfo, xcb_window_t iWindow, xcb_atom_t atmProtocol)
477{
478  int i, found = 0;
479  xcb_get_property_cookie_t cookie;
480  xcb_icccm_get_wm_protocols_reply_t reply;
481  xcb_connection_t *conn = pWMInfo->conn;
482
483  cookie = xcb_icccm_get_wm_protocols(conn, iWindow, pWMInfo->ewmh.WM_PROTOCOLS);
484  if (xcb_icccm_get_wm_protocols_reply(conn, cookie, &reply, NULL)) {
485    for (i = 0; i < reply.atoms_len; ++i)
486      if (reply.atoms[i] == atmProtocol) {
487              ++found;
488              break;
489      }
490    xcb_icccm_get_wm_protocols_reply_wipe(&reply);
491  }
492
493  return found > 0;
494}
495
496/*
497 * Send a message to the X server from the WM thread
498 */
499
500static void
501SendXMessage(xcb_connection_t *conn, xcb_window_t iWin, xcb_atom_t atmType, long nData)
502{
503    xcb_client_message_event_t e;
504
505    /* Prepare the X event structure */
506    memset(&e, 0, sizeof(e));
507    e.response_type = XCB_CLIENT_MESSAGE;
508    e.window = iWin;
509    e.type = atmType;
510    e.format = 32;
511    e.data.data32[0] = nData;
512    e.data.data32[1] = XCB_CURRENT_TIME;
513
514    /* Send the event to X */
515    xcb_send_event(conn, FALSE, iWin, XCB_EVENT_MASK_NO_EVENT, (const char *)&e);
516}
517
518/*
519 * See if we can get the stored HWND for this window...
520 */
521static HWND
522getHwnd(WMInfoPtr pWMInfo, xcb_window_t iWindow)
523{
524    HWND hWnd = NULL;
525    xcb_get_property_cookie_t cookie;
526    xcb_get_property_reply_t *reply;
527
528    cookie = xcb_get_property(pWMInfo->conn, FALSE, iWindow, pWMInfo->atmPrivMap,
529                              XCB_ATOM_INTEGER, 0L, sizeof(HWND)/4L);
530    reply = xcb_get_property_reply(pWMInfo->conn, cookie, NULL);
531
532    if (reply) {
533        int length = xcb_get_property_value_length(reply);
534        HWND *value = xcb_get_property_value(reply);
535
536        if (value && (length == sizeof(HWND))) {
537            hWnd = *value;
538        }
539        free(reply);
540    }
541
542    /* Some sanity checks */
543    if (!hWnd)
544        return NULL;
545    if (!IsWindow(hWnd))
546        return NULL;
547
548    return hWnd;
549}
550
551/*
552 * Helper function to check for override-redirect
553 */
554static Bool
555IsOverrideRedirect(xcb_connection_t *conn, xcb_window_t iWin)
556{
557    Bool result = FALSE;
558    xcb_get_window_attributes_reply_t *reply;
559    xcb_get_window_attributes_cookie_t cookie;
560
561    cookie = xcb_get_window_attributes(conn, iWin);
562    reply = xcb_get_window_attributes_reply(conn, cookie, NULL);
563    if (reply) {
564        result = (reply->override_redirect != 0);
565        free(reply);
566    }
567    else {
568        ErrorF("IsOverrideRedirect: Failed to get window attributes\n");
569    }
570
571    return result;
572}
573
574/*
575 * Helper function to get class and window names
576*/
577static void
578GetClassNames(WMInfoPtr pWMInfo, xcb_window_t iWindow, char **res_name,
579              char **res_class, char **window_name)
580{
581    xcb_get_property_cookie_t cookie1;
582    xcb_icccm_get_wm_class_reply_t reply1;
583    xcb_get_property_cookie_t cookie2;
584    xcb_icccm_get_text_property_reply_t reply2;
585
586    cookie1 = xcb_icccm_get_wm_class(pWMInfo->conn, iWindow);
587    if (xcb_icccm_get_wm_class_reply(pWMInfo->conn, cookie1, &reply1,
588                                     NULL)) {
589        *res_name = strdup(reply1.instance_name);
590        *res_class = strdup(reply1.class_name);
591        xcb_icccm_get_wm_class_reply_wipe(&reply1);
592    }
593    else {
594        *res_name = strdup("");
595        *res_class = strdup("");
596    }
597
598    cookie2 = xcb_icccm_get_wm_name(pWMInfo->conn, iWindow);
599    if (xcb_icccm_get_wm_name_reply(pWMInfo->conn, cookie2, &reply2, NULL)) {
600        *window_name = strndup(reply2.name, reply2.name_len);
601        xcb_icccm_get_text_property_reply_wipe(&reply2);
602    }
603    else {
604        *window_name = strdup("");
605    }
606}
607
608/*
609 * Updates the name of a HWND according to its X WM_NAME property
610 */
611
612static void
613UpdateName(WMInfoPtr pWMInfo, xcb_window_t iWindow)
614{
615    HWND hWnd;
616
617    hWnd = getHwnd(pWMInfo, iWindow);
618    if (!hWnd)
619        return;
620
621    /* If window isn't override-redirect */
622    if (!IsOverrideRedirect(pWMInfo->conn, iWindow)) {
623        char *pszWindowName;
624
625        /* Get the X windows window name */
626        GetWindowName(pWMInfo, iWindow, &pszWindowName);
627
628        if (pszWindowName) {
629            /* Convert from UTF-8 to wide char */
630            int iLen =
631                MultiByteToWideChar(CP_UTF8, 0, pszWindowName, -1, NULL, 0);
632            wchar_t *pwszWideWindowName =
633                malloc(sizeof(wchar_t)*(iLen + 1));
634            MultiByteToWideChar(CP_UTF8, 0, pszWindowName, -1,
635                                pwszWideWindowName, iLen);
636
637            /* Set the Windows window name */
638            SetWindowTextW(hWnd, pwszWideWindowName);
639
640            free(pwszWideWindowName);
641            free(pszWindowName);
642        }
643    }
644}
645
646/*
647 * Updates the icon of a HWND according to its X icon properties
648 */
649
650static void
651UpdateIcon(WMInfoPtr pWMInfo, xcb_window_t iWindow)
652{
653    HWND hWnd;
654    HICON hIconNew = NULL;
655
656    hWnd = getHwnd(pWMInfo, iWindow);
657    if (!hWnd)
658        return;
659
660    /* If window isn't override-redirect */
661    if (!IsOverrideRedirect(pWMInfo->conn, iWindow)) {
662        char *window_name = 0;
663        char *res_name = 0;
664        char *res_class = 0;
665
666        GetClassNames(pWMInfo, iWindow, &res_name, &res_class, &window_name);
667
668        hIconNew = winOverrideIcon(res_name, res_class, window_name);
669
670        free(res_name);
671        free(res_class);
672        free(window_name);
673        winUpdateIcon(hWnd, pWMInfo->conn, iWindow, hIconNew);
674    }
675}
676
677/*
678 * Updates the style of a HWND according to its X style properties
679 */
680
681static void
682UpdateStyle(WMInfoPtr pWMInfo, xcb_window_t iWindow)
683{
684    HWND hWnd;
685    HWND zstyle = HWND_NOTOPMOST;
686    UINT flags;
687
688    hWnd = getHwnd(pWMInfo, iWindow);
689    if (!hWnd)
690        return;
691
692    /* Determine the Window style, which determines borders and clipping region... */
693    winApplyHints(pWMInfo, iWindow, hWnd, &zstyle);
694    winUpdateWindowPosition(hWnd, &zstyle);
695
696    /* Apply the updated window style, without changing it's show or activation state */
697    flags = SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE;
698    if (zstyle == HWND_NOTOPMOST)
699        flags |= SWP_NOZORDER | SWP_NOOWNERZORDER;
700    SetWindowPos(hWnd, NULL, 0, 0, 0, 0, flags);
701
702    /*
703       Use the WS_EX_TOOLWINDOW style to remove window from Alt-Tab window switcher
704
705       According to MSDN, this is supposed to remove the window from the taskbar as well,
706       if we SW_HIDE before changing the style followed by SW_SHOW afterwards.
707
708       But that doesn't seem to work reliably, and causes the window to flicker, so use
709       the iTaskbarList interface to tell the taskbar to show or hide this window.
710     */
711    winShowWindowOnTaskbar(hWnd,
712                           (GetWindowLongPtr(hWnd, GWL_EXSTYLE) &
713                            WS_EX_APPWINDOW) ? TRUE : FALSE);
714}
715
716/*
717 * Updates the state of a HWND
718 * (only minimization supported at the moment)
719 */
720
721static void
722UpdateState(WMInfoPtr pWMInfo, xcb_window_t iWindow)
723{
724    HWND hWnd;
725
726    winDebug("UpdateState: iWindow 0x%08x\n", (int)iWindow);
727
728    hWnd = getHwnd(pWMInfo, iWindow);
729    if (!hWnd)
730        return;
731
732    ShowWindow(hWnd, SW_MINIMIZE);
733}
734
735#if 0
736/*
737 * Fix up any differences between the X11 and Win32 window stacks
738 * starting at the window passed in
739 */
740static void
741PreserveWin32Stack(WMInfoPtr pWMInfo, xcb_window_t iWindow, UINT direction)
742{
743    HWND hWnd;
744    DWORD myWinProcID, winProcID;
745    xcb_window_t xWindow;
746    WINDOWPLACEMENT wndPlace;
747
748    hWnd = getHwnd(pWMInfo, iWindow);
749    if (!hWnd)
750        return;
751
752    GetWindowThreadProcessId(hWnd, &myWinProcID);
753    hWnd = GetNextWindow(hWnd, direction);
754
755    while (hWnd) {
756        GetWindowThreadProcessId(hWnd, &winProcID);
757        if (winProcID == myWinProcID) {
758            wndPlace.length = sizeof(WINDOWPLACEMENT);
759            GetWindowPlacement(hWnd, &wndPlace);
760            if (!(wndPlace.showCmd == SW_HIDE ||
761                  wndPlace.showCmd == SW_MINIMIZE)) {
762                xWindow = (Window) GetProp(hWnd, WIN_WID_PROP);
763                if (xWindow) {
764                    if (direction == GW_HWNDPREV)
765                        XRaiseWindow(pWMInfo->pDisplay, xWindow);
766                    else
767                        XLowerWindow(pWMInfo->pDisplay, xWindow);
768                }
769            }
770        }
771        hWnd = GetNextWindow(hWnd, direction);
772    }
773}
774#endif                          /* PreserveWin32Stack */
775
776/*
777 * winMultiWindowWMProc
778 */
779
780static void *
781winMultiWindowWMProc(void *pArg)
782{
783    WMProcArgPtr pProcArg = (WMProcArgPtr) pArg;
784    WMInfoPtr pWMInfo = pProcArg->pWMInfo;
785
786    /* Initialize the Window Manager */
787    winInitMultiWindowWM(pWMInfo, pProcArg);
788
789#if CYGMULTIWINDOW_DEBUG
790    ErrorF("winMultiWindowWMProc ()\n");
791#endif
792
793    /* Loop until we explicitly break out */
794    for (;;) {
795        WMMsgNodePtr pNode;
796
797        /* Pop a message off of our queue */
798        pNode = PopMessage(&pWMInfo->wmMsgQueue, pWMInfo);
799        if (pNode == NULL) {
800            /* Bail if PopMessage returns without a message */
801            /* NOTE: Remember that PopMessage is a blocking function. */
802            ErrorF("winMultiWindowWMProc - Queue is Empty?  Exiting.\n");
803            pthread_exit(NULL);
804        }
805
806#if CYGMULTIWINDOW_DEBUG
807        ErrorF("winMultiWindowWMProc - MSG: %s (%d) ID: %d\n",
808               MessageName(&(pNode->msg)), (int)pNode->msg.msg, (int)pNode->msg.dwID);
809#endif
810
811        /* Branch on the message type */
812        switch (pNode->msg.msg) {
813#if 0
814        case WM_WM_MOVE:
815            break;
816
817        case WM_WM_SIZE:
818            break;
819#endif
820
821        case WM_WM_RAISE:
822            /* Raise the window */
823            {
824                const static uint32_t values[] = { XCB_STACK_MODE_ABOVE };
825                xcb_configure_window(pWMInfo->conn, pNode->msg.iWindow,
826                                     XCB_CONFIG_WINDOW_STACK_MODE, values);
827            }
828
829#if 0
830            PreserveWin32Stack(pWMInfo, pNode->msg.iWindow, GW_HWNDPREV);
831#endif
832            break;
833
834        case WM_WM_LOWER:
835            /* Lower the window */
836            {
837                const static uint32_t values[] = { XCB_STACK_MODE_BELOW };
838                xcb_configure_window(pWMInfo->conn, pNode->msg.iWindow,
839                                     XCB_CONFIG_WINDOW_STACK_MODE, values);
840            }
841            break;
842
843        case WM_WM_MAP2:
844            /* Put a note as to the HWND associated with this Window */
845            xcb_change_property(pWMInfo->conn, XCB_PROP_MODE_REPLACE,
846                                pNode->msg.iWindow, pWMInfo->atmPrivMap,
847                                XCB_ATOM_INTEGER, 32,
848                                sizeof(HWND)/4, &(pNode->msg.hwndWindow));
849
850            break;
851
852        case WM_WM_MAP3:
853            /* Put a note as to the HWND associated with this Window */
854            xcb_change_property(pWMInfo->conn, XCB_PROP_MODE_REPLACE,
855                                pNode->msg.iWindow, pWMInfo->atmPrivMap,
856                                XCB_ATOM_INTEGER, 32,
857                                sizeof(HWND)/4, &(pNode->msg.hwndWindow));
858
859            UpdateName(pWMInfo, pNode->msg.iWindow);
860            UpdateIcon(pWMInfo, pNode->msg.iWindow);
861            UpdateStyle(pWMInfo, pNode->msg.iWindow);
862
863
864            /* Reshape */
865            {
866                WindowPtr pWin =
867                    GetProp(pNode->msg.hwndWindow, WIN_WINDOW_PROP);
868                if (pWin) {
869                    winReshapeMultiWindow(pWin);
870                    winUpdateRgnMultiWindow(pWin);
871                }
872            }
873
874            break;
875
876        case WM_WM_UNMAP:
877
878            /* Unmap the window */
879            xcb_unmap_window(pWMInfo->conn, pNode->msg.iWindow);
880            break;
881
882        case WM_WM_KILL:
883            {
884                /* --- */
885                if (IsWmProtocolAvailable(pWMInfo,
886                                          pNode->msg.iWindow,
887                                          pWMInfo->atmWmDelete))
888                    SendXMessage(pWMInfo->conn,
889                                 pNode->msg.iWindow,
890                                 pWMInfo->atmWmProtos, pWMInfo->atmWmDelete);
891                else
892                    xcb_kill_client(pWMInfo->conn, pNode->msg.iWindow);
893            }
894            break;
895
896        case WM_WM_ACTIVATE:
897            /* Set the input focus */
898
899            /*
900               ICCCM 4.1.7 is pretty opaque, but it appears that the rules are
901               actually quite simple:
902               -- the WM_HINTS input field determines whether the WM should call
903               XSetInputFocus()
904               -- independently, the WM_TAKE_FOCUS protocol determines whether
905               the WM should send a WM_TAKE_FOCUS ClientMessage.
906            */
907            {
908              Bool neverFocus = FALSE;
909              xcb_get_property_cookie_t cookie;
910              xcb_icccm_wm_hints_t hints;
911
912              cookie = xcb_icccm_get_wm_hints(pWMInfo->conn, pNode->msg.iWindow);
913              if (xcb_icccm_get_wm_hints_reply(pWMInfo->conn, cookie, &hints,
914                                               NULL)) {
915                if (hints.flags & XCB_ICCCM_WM_HINT_INPUT)
916                  neverFocus = !hints.input;
917              }
918
919              if (!neverFocus)
920                xcb_set_input_focus(pWMInfo->conn, XCB_INPUT_FOCUS_POINTER_ROOT,
921                                    pNode->msg.iWindow, XCB_CURRENT_TIME);
922
923              if (IsWmProtocolAvailable(pWMInfo,
924                                        pNode->msg.iWindow,
925                                        pWMInfo->atmWmTakeFocus))
926                SendXMessage(pWMInfo->conn,
927                             pNode->msg.iWindow,
928                             pWMInfo->atmWmProtos, pWMInfo->atmWmTakeFocus);
929
930            }
931            break;
932
933        case WM_WM_NAME_EVENT:
934            UpdateName(pWMInfo, pNode->msg.iWindow);
935            break;
936
937        case WM_WM_ICON_EVENT:
938            UpdateIcon(pWMInfo, pNode->msg.iWindow);
939            break;
940
941        case WM_WM_HINTS_EVENT:
942            {
943            /* Don't do anything if this is an override-redirect window */
944            if (IsOverrideRedirect(pWMInfo->conn, pNode->msg.iWindow))
945              break;
946
947            UpdateStyle(pWMInfo, pNode->msg.iWindow);
948            }
949            break;
950
951        case WM_WM_CHANGE_STATE:
952            UpdateState(pWMInfo, pNode->msg.iWindow);
953            break;
954
955        default:
956            ErrorF("winMultiWindowWMProc - Unknown Message.  Exiting.\n");
957            pthread_exit(NULL);
958            break;
959        }
960
961        /* Free the retrieved message */
962        free(pNode);
963
964        /* Flush any pending events on our display */
965        xcb_flush(pWMInfo->conn);
966
967        /* This is just laziness rather than making sure we used _checked everywhere */
968        {
969            xcb_generic_event_t *event = xcb_poll_for_event(pWMInfo->conn);
970            if (event) {
971                if ((event->response_type & ~0x80) == 0) {
972                    xcb_generic_error_t *err = (xcb_generic_error_t *)event;
973                    ErrorF("winMultiWindowWMProc - Error code: %i, ID: 0x%08x, "
974                           "Major opcode: %i, Minor opcode: %i\n",
975                           err->error_code, err->resource_id,
976                           err->major_code, err->minor_code);
977                }
978            }
979        }
980
981        /* I/O errors etc. */
982        {
983            int e = xcb_connection_has_error(pWMInfo->conn);
984            if (e) {
985                ErrorF("winMultiWindowWMProc - Fatal error %d on xcb connection\n", e);
986                break;
987            }
988        }
989    }
990
991    /* Free the condition variable */
992    pthread_cond_destroy(&pWMInfo->wmMsgQueue.pcNotEmpty);
993
994    /* Free the mutex variable */
995    pthread_mutex_destroy(&pWMInfo->wmMsgQueue.pmMutex);
996
997    /* Free the passed-in argument */
998    free(pProcArg);
999
1000#if CYGMULTIWINDOW_DEBUG
1001    ErrorF("-winMultiWindowWMProc ()\n");
1002#endif
1003    return NULL;
1004}
1005
1006static xcb_atom_t
1007intern_atom(xcb_connection_t *conn, const char *atomName)
1008{
1009  xcb_intern_atom_reply_t *atom_reply;
1010  xcb_intern_atom_cookie_t atom_cookie;
1011  xcb_atom_t atom = XCB_ATOM_NONE;
1012
1013  atom_cookie = xcb_intern_atom(conn, 0, strlen(atomName), atomName);
1014  atom_reply = xcb_intern_atom_reply(conn, atom_cookie, NULL);
1015  if (atom_reply) {
1016    atom = atom_reply->atom;
1017    free(atom_reply);
1018  }
1019  return atom;
1020}
1021
1022/*
1023 * X message procedure
1024 */
1025
1026static void *
1027winMultiWindowXMsgProc(void *pArg)
1028{
1029    winWMMessageRec msg;
1030    XMsgProcArgPtr pProcArg = (XMsgProcArgPtr) pArg;
1031    char pszDisplay[512];
1032    int iRetries;
1033    xcb_atom_t atmWmName;
1034    xcb_atom_t atmNetWmName;
1035    xcb_atom_t atmWmHints;
1036    xcb_atom_t atmWmChange;
1037    xcb_atom_t atmNetWmIcon;
1038    xcb_atom_t atmWindowState, atmMotifWmHints, atmWindowType, atmNormalHints;
1039    int iReturn;
1040    xcb_auth_info_t *auth_info;
1041
1042    winDebug("winMultiWindowXMsgProc - Hello\n");
1043
1044    /* Check that argument pointer is not invalid */
1045    if (pProcArg == NULL) {
1046        ErrorF("winMultiWindowXMsgProc - pProcArg is NULL.  Exiting.\n");
1047        pthread_exit(NULL);
1048    }
1049
1050    winDebug("winMultiWindowXMsgProc - Calling pthread_mutex_lock ()\n");
1051
1052    /* Grab the server started mutex - pause until we get it */
1053    iReturn = pthread_mutex_lock(pProcArg->ppmServerStarted);
1054    if (iReturn != 0) {
1055        ErrorF("winMultiWindowXMsgProc - pthread_mutex_lock () failed: %d.  "
1056               "Exiting.\n", iReturn);
1057        pthread_exit(NULL);
1058    }
1059
1060    winDebug("winMultiWindowXMsgProc - pthread_mutex_lock () returned.\n");
1061
1062    /* Release the server started mutex */
1063    pthread_mutex_unlock(pProcArg->ppmServerStarted);
1064
1065    winDebug("winMultiWindowXMsgProc - pthread_mutex_unlock () returned.\n");
1066
1067    /* Setup the display connection string x */
1068    winGetDisplayName(pszDisplay, (int) pProcArg->dwScreen);
1069
1070    /* Print the display connection string */
1071    ErrorF("winMultiWindowXMsgProc - DISPLAY=%s\n", pszDisplay);
1072
1073    /* Use our generated cookie for authentication */
1074    auth_info = winGetXcbAuthInfo();
1075
1076    /* Initialize retry count */
1077    iRetries = 0;
1078
1079    /* Open the X display */
1080    do {
1081        /* Try to open the display */
1082        pProcArg->conn = xcb_connect_to_display_with_auth_info(pszDisplay,
1083                                                               auth_info, NULL);
1084        if (xcb_connection_has_error(pProcArg->conn)) {
1085            ErrorF("winMultiWindowXMsgProc - Could not open display, try: %d, "
1086                   "sleeping: %d\n", iRetries + 1, WIN_CONNECT_DELAY);
1087            ++iRetries;
1088            sleep(WIN_CONNECT_DELAY);
1089            continue;
1090        }
1091        else
1092            break;
1093    }
1094    while (xcb_connection_has_error(pProcArg->conn) && iRetries < WIN_CONNECT_RETRIES);
1095
1096    /* Make sure that the display opened */
1097    if (xcb_connection_has_error(pProcArg->conn)) {
1098        ErrorF("winMultiWindowXMsgProc - Failed opening the display.  "
1099               "Exiting.\n");
1100        pthread_exit(NULL);
1101    }
1102
1103    ErrorF("winMultiWindowXMsgProc - xcb_connect() returned and "
1104           "successfully opened the display.\n");
1105
1106    /* Check if another window manager is already running */
1107    if (CheckAnotherWindowManager(pProcArg->conn, pProcArg->dwScreen)) {
1108        ErrorF("winMultiWindowXMsgProc - "
1109               "another window manager is running.  Exiting.\n");
1110        pthread_exit(NULL);
1111    }
1112
1113    {
1114        /* Get root window id */
1115        xcb_screen_t *root_screen = xcb_aux_get_screen(pProcArg->conn, pProcArg->dwScreen);
1116        xcb_window_t root_window_id = root_screen->root;
1117
1118        /* Set WM_ICON_SIZE property indicating desired icon sizes */
1119        typedef struct {
1120            uint32_t min_width, min_height;
1121            uint32_t max_width, max_height;
1122            int32_t width_inc, height_inc;
1123        } xcb_wm_icon_size_hints_hints_t;
1124
1125        xcb_wm_icon_size_hints_hints_t xis;
1126        xis.min_width = xis.min_height = 16;
1127        xis.max_width = xis.max_height = 48;
1128        xis.width_inc = xis.height_inc = 16;
1129
1130        xcb_change_property(pProcArg->conn, XCB_PROP_MODE_REPLACE, root_window_id,
1131                            XCB_ATOM_WM_ICON_SIZE, XCB_ATOM_WM_ICON_SIZE, 32,
1132                            sizeof(xis)/4, &xis);
1133    }
1134
1135    atmWmName = intern_atom(pProcArg->conn, "WM_NAME");
1136    atmNetWmName = intern_atom(pProcArg->conn, "_NET_WM_NAME");
1137    atmWmHints = intern_atom(pProcArg->conn, "WM_HINTS");
1138    atmWmChange = intern_atom(pProcArg->conn, "WM_CHANGE_STATE");
1139    atmNetWmIcon = intern_atom(pProcArg->conn, "_NET_WM_ICON");
1140    atmWindowState = intern_atom(pProcArg->conn, "_NET_WM_STATE");
1141    atmMotifWmHints = intern_atom(pProcArg->conn, "_MOTIF_WM_HINTS");
1142    atmWindowType = intern_atom(pProcArg->conn, "_NET_WM_WINDOW_TYPE");
1143    atmNormalHints = intern_atom(pProcArg->conn, "WM_NORMAL_HINTS");
1144
1145    /*
1146       iiimxcf had a bug until 2009-04-27, assuming that the
1147       WM_STATE atom exists, causing clients to fail with
1148       a BadAtom X error if it doesn't.
1149
1150       Since this is on in the default Solaris 10 install,
1151       workaround this by making sure it does exist...
1152     */
1153    intern_atom(pProcArg->conn, "WM_STATE");
1154
1155    /* Loop until we explicitly break out */
1156    while (1) {
1157        xcb_generic_event_t *event;
1158        uint8_t type;
1159        Bool send_event;
1160
1161        if (g_shutdown)
1162            break;
1163
1164        /* Fetch next event */
1165        event = xcb_wait_for_event(pProcArg->conn);
1166        if (!event) { // returns NULL on I/O error
1167            int e = xcb_connection_has_error(pProcArg->conn);
1168            ErrorF("winMultiWindowXMsgProc - Fatal error %d on xcb connection\n", e);
1169            break;
1170        }
1171
1172        type = event->response_type & ~0x80;
1173        send_event = event->response_type & 0x80;
1174
1175        winDebug("winMultiWindowXMsgProc - event %d\n", type);
1176
1177        /* Branch on event type */
1178        if (type == 0) {
1179            xcb_generic_error_t *err = (xcb_generic_error_t *)event;
1180            ErrorF("winMultiWindowXMsgProc - Error code: %i, ID: 0x%08x, "
1181                   "Major opcode: %i, Minor opcode: %i\n",
1182                   err->error_code, err->resource_id,
1183                   err->major_code, err->minor_code);
1184            }
1185        else if (type == XCB_CREATE_NOTIFY) {
1186            xcb_create_notify_event_t *notify = (xcb_create_notify_event_t *)event;
1187
1188            /* Request property change events */
1189            const static uint32_t mask_value[] = { XCB_EVENT_MASK_PROPERTY_CHANGE };
1190            xcb_change_window_attributes (pProcArg->conn, notify->window,
1191                                          XCB_CW_EVENT_MASK, mask_value);
1192
1193            /* If it's not override-redirect, set the border-width to 0 */
1194            if (!IsOverrideRedirect(pProcArg->conn, notify->window)) {
1195                const static uint32_t width_value[] = { 0 };
1196                xcb_configure_window(pProcArg->conn, notify->window,
1197                                     XCB_CONFIG_WINDOW_BORDER_WIDTH, width_value);
1198            }
1199        }
1200        else if (type == XCB_MAP_NOTIFY) {
1201            /* Fake a reparentNotify event as SWT/Motif expects a
1202               Window Manager to reparent a top-level window when
1203               it is mapped and waits until they do.
1204
1205               We don't actually need to reparent, as the frame is
1206               a native window, not an X window
1207
1208               We do this on MapNotify, not MapRequest like a real
1209               Window Manager would, so we don't have do get involved
1210               in actually mapping the window via it's (non-existent)
1211               parent...
1212
1213               See sourceware bugzilla #9848
1214             */
1215
1216            xcb_map_notify_event_t *notify = (xcb_map_notify_event_t *)event;
1217
1218            xcb_get_geometry_cookie_t cookie;
1219            xcb_get_geometry_reply_t *reply;
1220            xcb_query_tree_cookie_t cookie_qt;
1221            xcb_query_tree_reply_t *reply_qt;
1222
1223            cookie = xcb_get_geometry(pProcArg->conn, notify->window);
1224            cookie_qt = xcb_query_tree(pProcArg->conn, notify->window);
1225            reply = xcb_get_geometry_reply(pProcArg->conn, cookie, NULL);
1226            reply_qt = xcb_query_tree_reply(pProcArg->conn, cookie_qt, NULL);
1227
1228            if (reply && reply_qt) {
1229                /*
1230                   It's a top-level window if the parent window is a root window
1231                   Only non-override_redirect windows can get reparented
1232                 */
1233                if ((reply->root == reply_qt->parent) && !notify->override_redirect) {
1234                    xcb_reparent_notify_event_t event_send;
1235
1236                    event_send.response_type = ReparentNotify;
1237                    event_send.event = notify->window;
1238                    event_send.window = notify->window;
1239                    event_send.parent = reply_qt->parent;
1240                    event_send.x = reply->x;
1241                    event_send.y = reply->y;
1242
1243                    xcb_send_event (pProcArg->conn, TRUE, notify->window,
1244                                    XCB_EVENT_MASK_STRUCTURE_NOTIFY,
1245                                    (const char *)&event_send);
1246
1247                    free(reply_qt);
1248                    free(reply);
1249                }
1250            }
1251        }
1252        else if (type == XCB_CONFIGURE_NOTIFY) {
1253            if (!send_event) {
1254                /*
1255                   Java applications using AWT on JRE 1.6.0 break with non-reparenting WMs AWT
1256                   doesn't explicitly know about (See sun bug #6434227)
1257
1258                   XDecoratedPeer.handleConfigureNotifyEvent() only processes non-synthetic
1259                   ConfigureNotify events to update window location if it's identified the
1260                   WM as a non-reparenting WM it knows about (compiz or lookingglass)
1261
1262                   Rather than tell all sorts of lies to get XWM to recognize us as one of
1263                   those, simply send a synthetic ConfigureNotify for every non-synthetic one
1264                 */
1265                xcb_configure_notify_event_t *notify = (xcb_configure_notify_event_t *)event;
1266                xcb_configure_notify_event_t event_send = *notify;
1267
1268                event_send.event = notify->window;
1269
1270                xcb_send_event(pProcArg->conn, TRUE, notify->window,
1271                               XCB_EVENT_MASK_STRUCTURE_NOTIFY,
1272                               (const char *)&event_send);
1273            }
1274        }
1275        else if (type ==  XCB_PROPERTY_NOTIFY) {
1276            xcb_property_notify_event_t *notify = (xcb_property_notify_event_t *)event;
1277
1278            if ((notify->atom == atmWmName) ||
1279                (notify->atom == atmNetWmName)) {
1280                memset(&msg, 0, sizeof(msg));
1281
1282                msg.msg = WM_WM_NAME_EVENT;
1283                msg.iWindow = notify->window;
1284
1285                /* Other fields ignored */
1286                winSendMessageToWM(pProcArg->pWMInfo, &msg);
1287            }
1288            else {
1289                /*
1290                   Several properties are considered for WM hints, check if this property change affects any of them...
1291                   (this list needs to be kept in sync with winApplyHints())
1292                 */
1293                if ((notify->atom == atmWmHints) ||
1294                    (notify->atom == atmWindowState) ||
1295                    (notify->atom == atmMotifWmHints) ||
1296                    (notify->atom == atmWindowType) ||
1297                    (notify->atom == atmNormalHints)) {
1298                    memset(&msg, 0, sizeof(msg));
1299                    msg.msg = WM_WM_HINTS_EVENT;
1300                    msg.iWindow = notify->window;
1301
1302                    /* Other fields ignored */
1303                    winSendMessageToWM(pProcArg->pWMInfo, &msg);
1304                }
1305
1306                /* Not an else as WM_HINTS affects both style and icon */
1307                if ((notify->atom == atmWmHints) ||
1308                    (notify->atom == atmNetWmIcon)) {
1309                    memset(&msg, 0, sizeof(msg));
1310                    msg.msg = WM_WM_ICON_EVENT;
1311                    msg.iWindow = notify->window;
1312
1313                    /* Other fields ignored */
1314                    winSendMessageToWM(pProcArg->pWMInfo, &msg);
1315                }
1316            }
1317        }
1318        else if (type == XCB_CLIENT_MESSAGE) {
1319            xcb_client_message_event_t *client_msg = (xcb_client_message_event_t *)event;
1320
1321            if (client_msg->type == atmWmChange
1322                 && client_msg->data.data32[0] == XCB_ICCCM_WM_STATE_ICONIC) {
1323                ErrorF("winMultiWindowXMsgProc - WM_CHANGE_STATE - IconicState\n");
1324
1325                memset(&msg, 0, sizeof(msg));
1326
1327                msg.msg = WM_WM_CHANGE_STATE;
1328                msg.iWindow = client_msg->window;
1329
1330                winSendMessageToWM(pProcArg->pWMInfo, &msg);
1331            }
1332        }
1333
1334        /* Free the event */
1335        free(event);
1336    }
1337
1338    xcb_disconnect(pProcArg->conn);
1339    pthread_exit(NULL);
1340    return NULL;
1341}
1342
1343/*
1344 * winInitWM - Entry point for the X server to spawn
1345 * the Window Manager thread.  Called from
1346 * winscrinit.c/winFinishScreenInitFB ().
1347 */
1348
1349Bool
1350winInitWM(void **ppWMInfo,
1351          pthread_t * ptWMProc,
1352          pthread_t * ptXMsgProc,
1353          pthread_mutex_t * ppmServerStarted,
1354          int dwScreen, HWND hwndScreen)
1355{
1356    WMProcArgPtr pArg = malloc(sizeof(WMProcArgRec));
1357    WMInfoPtr pWMInfo = malloc(sizeof(WMInfoRec));
1358    XMsgProcArgPtr pXMsgArg = malloc(sizeof(XMsgProcArgRec));
1359
1360    /* Bail if the input parameters are bad */
1361    if (pArg == NULL || pWMInfo == NULL || pXMsgArg == NULL) {
1362        ErrorF("winInitWM - malloc failed.\n");
1363        free(pArg);
1364        free(pWMInfo);
1365        free(pXMsgArg);
1366        return FALSE;
1367    }
1368
1369    /* Zero the allocated memory */
1370    ZeroMemory(pArg, sizeof(WMProcArgRec));
1371    ZeroMemory(pWMInfo, sizeof(WMInfoRec));
1372    ZeroMemory(pXMsgArg, sizeof(XMsgProcArgRec));
1373
1374    /* Set a return pointer to the Window Manager info structure */
1375    *ppWMInfo = pWMInfo;
1376
1377    /* Setup the argument structure for the thread function */
1378    pArg->dwScreen = dwScreen;
1379    pArg->pWMInfo = pWMInfo;
1380    pArg->ppmServerStarted = ppmServerStarted;
1381
1382    /* Intialize the message queue */
1383    if (!InitQueue(&pWMInfo->wmMsgQueue)) {
1384        ErrorF("winInitWM - InitQueue () failed.\n");
1385        return FALSE;
1386    }
1387
1388    /* Spawn a thread for the Window Manager */
1389    if (pthread_create(ptWMProc, NULL, winMultiWindowWMProc, pArg)) {
1390        /* Bail if thread creation failed */
1391        ErrorF("winInitWM - pthread_create failed for Window Manager.\n");
1392        return FALSE;
1393    }
1394
1395    /* Spawn the XNextEvent thread, will send messages to WM */
1396    pXMsgArg->dwScreen = dwScreen;
1397    pXMsgArg->pWMInfo = pWMInfo;
1398    pXMsgArg->ppmServerStarted = ppmServerStarted;
1399    pXMsgArg->hwndScreen = hwndScreen;
1400    if (pthread_create(ptXMsgProc, NULL, winMultiWindowXMsgProc, pXMsgArg)) {
1401        /* Bail if thread creation failed */
1402        ErrorF("winInitWM - pthread_create failed on XMSG.\n");
1403        return FALSE;
1404    }
1405
1406#if CYGDEBUG || YES
1407    winDebug("winInitWM - Returning.\n");
1408#endif
1409
1410    return TRUE;
1411}
1412
1413/*
1414 * Window manager thread - setup
1415 */
1416
1417static void
1418winInitMultiWindowWM(WMInfoPtr pWMInfo, WMProcArgPtr pProcArg)
1419{
1420    int iRetries = 0;
1421    char pszDisplay[512];
1422    int iReturn;
1423    xcb_auth_info_t *auth_info;
1424
1425    winDebug("winInitMultiWindowWM - Hello\n");
1426
1427    /* Check that argument pointer is not invalid */
1428    if (pProcArg == NULL) {
1429        ErrorF("winInitMultiWindowWM - pProcArg is NULL.  Exiting.\n");
1430        pthread_exit(NULL);
1431    }
1432
1433    winDebug("winInitMultiWindowWM - Calling pthread_mutex_lock ()\n");
1434
1435    /* Grab our garbage mutex to satisfy pthread_cond_wait */
1436    iReturn = pthread_mutex_lock(pProcArg->ppmServerStarted);
1437    if (iReturn != 0) {
1438        ErrorF("winInitMultiWindowWM - pthread_mutex_lock () failed: %d.  "
1439               "Exiting.\n", iReturn);
1440        pthread_exit(NULL);
1441    }
1442
1443    winDebug("winInitMultiWindowWM - pthread_mutex_lock () returned.\n");
1444
1445    /* Release the server started mutex */
1446    pthread_mutex_unlock(pProcArg->ppmServerStarted);
1447
1448    winDebug("winInitMultiWindowWM - pthread_mutex_unlock () returned.\n");
1449
1450    /* Setup the display connection string x */
1451    winGetDisplayName(pszDisplay, (int) pProcArg->dwScreen);
1452
1453    /* Print the display connection string */
1454    ErrorF("winInitMultiWindowWM - DISPLAY=%s\n", pszDisplay);
1455
1456    /* Use our generated cookie for authentication */
1457    auth_info = winGetXcbAuthInfo();
1458
1459    /* Open the X display */
1460    do {
1461        /* Try to open the display */
1462        pWMInfo->conn = xcb_connect_to_display_with_auth_info(pszDisplay,
1463                                                              auth_info, NULL);
1464        if (xcb_connection_has_error(pWMInfo->conn)) {
1465            ErrorF("winInitMultiWindowWM - Could not open display, try: %d, "
1466                   "sleeping: %d\n", iRetries + 1, WIN_CONNECT_DELAY);
1467            ++iRetries;
1468            sleep(WIN_CONNECT_DELAY);
1469            continue;
1470        }
1471        else
1472            break;
1473    }
1474    while (xcb_connection_has_error(pWMInfo->conn) && iRetries < WIN_CONNECT_RETRIES);
1475
1476    /* Make sure that the display opened */
1477    if (xcb_connection_has_error(pWMInfo->conn)) {
1478        ErrorF("winInitMultiWindowWM - Failed opening the display.  "
1479               "Exiting.\n");
1480        pthread_exit(NULL);
1481    }
1482
1483    ErrorF("winInitMultiWindowWM - xcb_connect () returned and "
1484           "successfully opened the display.\n");
1485
1486    /* Create some atoms */
1487    pWMInfo->atmWmProtos = intern_atom(pWMInfo->conn, "WM_PROTOCOLS");
1488    pWMInfo->atmWmDelete = intern_atom(pWMInfo->conn, "WM_DELETE_WINDOW");
1489    pWMInfo->atmWmTakeFocus = intern_atom(pWMInfo->conn, "WM_TAKE_FOCUS");
1490    pWMInfo->atmPrivMap = intern_atom(pWMInfo->conn, WINDOWSWM_NATIVE_HWND);
1491    pWMInfo->atmUtf8String = intern_atom(pWMInfo->conn, "UTF8_STRING");
1492    pWMInfo->atmNetWmName = intern_atom(pWMInfo->conn, "_NET_WM_NAME");
1493
1494    /* Initialization for the xcb_ewmh and EWMH atoms */
1495    {
1496        xcb_intern_atom_cookie_t *atoms_cookie;
1497        atoms_cookie = xcb_ewmh_init_atoms(pWMInfo->conn, &pWMInfo->ewmh);
1498        if (xcb_ewmh_init_atoms_replies(&pWMInfo->ewmh, atoms_cookie, NULL)) {
1499            /* Set the _NET_SUPPORTED atom for this context.
1500
1501               TODO: Audit to ensure we implement everything defined as MUSTs
1502               for window managers in the EWMH standard.*/
1503            xcb_atom_t supported[] =
1504                {
1505                    pWMInfo->ewmh.WM_PROTOCOLS,
1506                    pWMInfo->ewmh._NET_SUPPORTED,
1507                    pWMInfo->ewmh._NET_SUPPORTING_WM_CHECK,
1508                    pWMInfo->ewmh._NET_CLOSE_WINDOW,
1509                    pWMInfo->ewmh._NET_WM_WINDOW_TYPE,
1510                    pWMInfo->ewmh._NET_WM_WINDOW_TYPE_DOCK,
1511                    pWMInfo->ewmh._NET_WM_WINDOW_TYPE_SPLASH,
1512                    pWMInfo->ewmh._NET_WM_STATE,
1513                    pWMInfo->ewmh._NET_WM_STATE_HIDDEN,
1514                    pWMInfo->ewmh._NET_WM_STATE_ABOVE,
1515                    pWMInfo->ewmh._NET_WM_STATE_BELOW,
1516                    pWMInfo->ewmh._NET_WM_STATE_SKIP_TASKBAR,
1517                };
1518
1519            xcb_ewmh_set_supported(&pWMInfo->ewmh, pProcArg->dwScreen,
1520                                   ARRAY_SIZE(supported), supported);
1521        }
1522        else {
1523            ErrorF("winInitMultiWindowWM - xcb_ewmh_init_atoms() failed\n");
1524        }
1525    }
1526
1527    /*
1528      Set the root window cursor to left_ptr (this controls the cursor an
1529      application gets over it's windows when it doesn't set one)
1530    */
1531    {
1532#define XC_left_ptr 68
1533        xcb_cursor_t cursor = xcb_generate_id(pWMInfo->conn);
1534        xcb_font_t font = xcb_generate_id(pWMInfo->conn);
1535        xcb_font_t *mask_font = &font; /* An alias to clarify */
1536        int shape = XC_left_ptr;
1537        uint32_t mask = XCB_CW_CURSOR;
1538        uint32_t value_list = cursor;
1539
1540        xcb_screen_t *root_screen = xcb_aux_get_screen(pWMInfo->conn, pProcArg->dwScreen);
1541        xcb_window_t window = root_screen->root;
1542
1543        static const uint16_t fgred = 0, fggreen = 0, fgblue = 0;
1544        static const uint16_t bgred = 0xFFFF, bggreen = 0xFFFF, bgblue = 0xFFFF;
1545
1546        xcb_open_font(pWMInfo->conn, font, sizeof("cursor"), "cursor");
1547
1548        xcb_create_glyph_cursor(pWMInfo->conn, cursor, font, *mask_font,
1549                                shape, shape + 1,
1550                                fgred, fggreen, fgblue, bgred, bggreen, bgblue);
1551
1552        xcb_change_window_attributes(pWMInfo->conn, window, mask, &value_list);
1553
1554        xcb_free_cursor(pWMInfo->conn, cursor);
1555        xcb_close_font(pWMInfo->conn, font);
1556    }
1557}
1558
1559/*
1560 * winSendMessageToWM - Send a message from the X thread to the WM thread
1561 */
1562
1563void
1564winSendMessageToWM(void *pWMInfo, winWMMessagePtr pMsg)
1565{
1566    WMMsgNodePtr pNode;
1567
1568#if CYGMULTIWINDOW_DEBUG
1569    ErrorF("winSendMessageToWM %s\n", MessageName(pMsg));
1570#endif
1571
1572    pNode = malloc(sizeof(WMMsgNodeRec));
1573    if (pNode != NULL) {
1574        memcpy(&pNode->msg, pMsg, sizeof(winWMMessageRec));
1575        PushMessage(&((WMInfoPtr) pWMInfo)->wmMsgQueue, pNode);
1576    }
1577}
1578
1579/*
1580 * Check if another window manager is running
1581 */
1582
1583static Bool
1584CheckAnotherWindowManager(xcb_connection_t *conn, DWORD dwScreen)
1585{
1586    Bool redirectError = FALSE;
1587
1588    /* Get root window id */
1589    xcb_screen_t *root_screen = xcb_aux_get_screen(conn, dwScreen);
1590    xcb_window_t root_window_id = root_screen->root;
1591
1592    /*
1593       Try to select the events which only one client at a time is allowed to select.
1594       If this causes an error, another window manager is already running...
1595     */
1596    const static uint32_t test_mask[] = { XCB_EVENT_MASK_RESIZE_REDIRECT |
1597                                       XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
1598                                       XCB_EVENT_MASK_BUTTON_PRESS };
1599
1600    xcb_void_cookie_t cookie = xcb_change_window_attributes_checked(conn,
1601                                                                    root_window_id,
1602                                                                    XCB_CW_EVENT_MASK,
1603                                                                    test_mask);
1604    xcb_generic_error_t *error;
1605    if ((error = xcb_request_check(conn, cookie)))
1606        {
1607            redirectError = TRUE;
1608            free(error);
1609        }
1610
1611    /*
1612       Side effect: select the events we are actually interested in...
1613
1614       Other WMs are not allowed, also select one of the events which only one client
1615       at a time is allowed to select, so other window managers won't start...
1616     */
1617    {
1618        const uint32_t mask[] = { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
1619                                  XCB_EVENT_MASK_BUTTON_PRESS };
1620
1621        xcb_change_window_attributes(conn, root_window_id, XCB_CW_EVENT_MASK, mask);
1622    }
1623
1624    return redirectError;
1625}
1626
1627/*
1628 * Notify the MWM thread we're exiting and not to reconnect
1629 */
1630
1631void
1632winDeinitMultiWindowWM(void)
1633{
1634    ErrorF("winDeinitMultiWindowWM - Noting shutdown in progress\n");
1635    g_shutdown = TRUE;
1636}
1637
1638/* Windows window styles */
1639#define HINT_NOFRAME	(1L<<0)
1640#define HINT_BORDER	(1L<<1)
1641#define HINT_SIZEBOX	(1L<<2)
1642#define HINT_CAPTION	(1L<<3)
1643#define HINT_NOMAXIMIZE (1L<<4)
1644#define HINT_NOMINIMIZE (1L<<5)
1645#define HINT_NOSYSMENU  (1L<<6)
1646#define HINT_SKIPTASKBAR (1L<<7)
1647/* These two are used on their own */
1648#define HINT_MAX	(1L<<0)
1649#define HINT_MIN	(1L<<1)
1650
1651static void
1652winApplyHints(WMInfoPtr pWMInfo, xcb_window_t iWindow, HWND hWnd, HWND * zstyle)
1653{
1654
1655    xcb_connection_t *conn = pWMInfo->conn;
1656    static xcb_atom_t windowState, motif_wm_hints;
1657    static xcb_atom_t hiddenState, fullscreenState, belowState, aboveState,
1658        skiptaskbarState;
1659    static xcb_atom_t splashType;
1660    static int generation;
1661
1662    unsigned long hint = 0, maxmin = 0;
1663    unsigned long style, exStyle;
1664
1665    if (!hWnd)
1666        return;
1667    if (!IsWindow(hWnd))
1668        return;
1669
1670    if (generation != serverGeneration) {
1671        generation = serverGeneration;
1672        windowState = intern_atom(conn, "_NET_WM_STATE");
1673        motif_wm_hints = intern_atom(conn, "_MOTIF_WM_HINTS");
1674        hiddenState = intern_atom(conn, "_NET_WM_STATE_HIDDEN");
1675        fullscreenState = intern_atom(conn, "_NET_WM_STATE_FULLSCREEN");
1676        belowState = intern_atom(conn, "_NET_WM_STATE_BELOW");
1677        aboveState = intern_atom(conn, "_NET_WM_STATE_ABOVE");
1678        skiptaskbarState = intern_atom(conn, "_NET_WM_STATE_SKIP_TASKBAR");
1679        splashType = intern_atom(conn, "_NET_WM_WINDOW_TYPE_SPLASHSCREEN");
1680    }
1681
1682    {
1683      xcb_get_property_cookie_t cookie_wm_state = xcb_get_property(conn, FALSE, iWindow, windowState, XCB_ATOM_ATOM, 0L, INT_MAX);
1684      xcb_get_property_reply_t *reply = xcb_get_property_reply(conn, cookie_wm_state, NULL);
1685      if (reply) {
1686        int i;
1687        int nitems = xcb_get_property_value_length(reply)/sizeof(xcb_atom_t);
1688        xcb_atom_t *pAtom = xcb_get_property_value(reply);
1689
1690            for (i = 0; i < nitems; i++) {
1691                if (pAtom[i] == skiptaskbarState)
1692                    hint |= HINT_SKIPTASKBAR;
1693                if (pAtom[i] == hiddenState)
1694                    maxmin |= HINT_MIN;
1695                else if (pAtom[i] == fullscreenState)
1696                    maxmin |= HINT_MAX;
1697                if (pAtom[i] == belowState)
1698                    *zstyle = HWND_BOTTOM;
1699                else if (pAtom[i] == aboveState)
1700                    *zstyle = HWND_TOPMOST;
1701            }
1702
1703            free(reply);
1704      }
1705    }
1706
1707    {
1708      xcb_get_property_cookie_t cookie_mwm_hint = xcb_get_property(conn, FALSE, iWindow, motif_wm_hints, motif_wm_hints, 0L, sizeof(MwmHints));
1709      xcb_get_property_reply_t *reply =  xcb_get_property_reply(conn, cookie_mwm_hint, NULL);
1710      if (reply) {
1711        int nitems = xcb_get_property_value_length(reply)/4;
1712        MwmHints *mwm_hint = xcb_get_property_value(reply);
1713        if (mwm_hint && (nitems >= PropMwmHintsElements) &&
1714            (mwm_hint->flags & MwmHintsDecorations)) {
1715            if (!mwm_hint->decorations)
1716                hint |= (HINT_NOFRAME | HINT_NOSYSMENU | HINT_NOMINIMIZE | HINT_NOMAXIMIZE);
1717            else if (!(mwm_hint->decorations & MwmDecorAll)) {
1718                if (mwm_hint->decorations & MwmDecorBorder)
1719                    hint |= HINT_BORDER;
1720                if (mwm_hint->decorations & MwmDecorHandle)
1721                    hint |= HINT_SIZEBOX;
1722                if (mwm_hint->decorations & MwmDecorTitle)
1723                    hint |= HINT_CAPTION;
1724                if (!(mwm_hint->decorations & MwmDecorMenu))
1725                    hint |= HINT_NOSYSMENU;
1726                if (!(mwm_hint->decorations & MwmDecorMinimize))
1727                    hint |= HINT_NOMINIMIZE;
1728                if (!(mwm_hint->decorations & MwmDecorMaximize))
1729                    hint |= HINT_NOMAXIMIZE;
1730            }
1731            else {
1732                /*
1733                   MwmDecorAll means all decorations *except* those specified by other flag
1734                   bits that are set.  Not yet implemented.
1735                 */
1736            }
1737        }
1738        free(reply);
1739      }
1740    }
1741
1742    {
1743      int i;
1744      xcb_ewmh_get_atoms_reply_t type;
1745      xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_window_type(&pWMInfo->ewmh, iWindow);
1746      if (xcb_ewmh_get_wm_window_type_reply(&pWMInfo->ewmh, cookie, &type, NULL)) {
1747        for (i = 0; i < type.atoms_len; i++) {
1748            if (type.atoms[i] ==  pWMInfo->ewmh._NET_WM_WINDOW_TYPE_DOCK) {
1749                hint = (hint & ~HINT_NOFRAME) | HINT_SKIPTASKBAR | HINT_SIZEBOX;
1750                *zstyle = HWND_TOPMOST;
1751            }
1752            else if ((type.atoms[i] == pWMInfo->ewmh._NET_WM_WINDOW_TYPE_SPLASH)
1753                     || (type.atoms[i] == splashType)) {
1754                hint |= (HINT_SKIPTASKBAR | HINT_NOSYSMENU | HINT_NOMINIMIZE | HINT_NOMAXIMIZE);
1755                *zstyle = HWND_TOPMOST;
1756            }
1757        }
1758      }
1759    }
1760
1761    {
1762        xcb_size_hints_t size_hints;
1763        xcb_get_property_cookie_t cookie;
1764
1765        cookie = xcb_icccm_get_wm_normal_hints(conn, iWindow);
1766        if (xcb_icccm_get_wm_normal_hints_reply(conn, cookie, &size_hints, NULL)) {
1767            if (size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE) {
1768
1769                /* Not maximizable if a maximum size is specified, and that size
1770                   is smaller (in either dimension) than the screen size */
1771                if ((size_hints.max_width < GetSystemMetrics(SM_CXVIRTUALSCREEN))
1772                    || (size_hints.max_height < GetSystemMetrics(SM_CYVIRTUALSCREEN)))
1773                    hint |= HINT_NOMAXIMIZE;
1774
1775                if (size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE) {
1776                    /*
1777                       If both minimum size and maximum size are specified and are the same,
1778                       don't bother with a resizing frame
1779                     */
1780                    if ((size_hints.min_width == size_hints.max_width)
1781                        && (size_hints.min_height == size_hints.max_height))
1782                        hint = (hint & ~HINT_SIZEBOX);
1783                }
1784            }
1785        }
1786    }
1787
1788    /*
1789       Override hint settings from above with settings from config file and set
1790       application id for grouping.
1791     */
1792    {
1793        char *application_id = 0;
1794        char *window_name = 0;
1795        char *res_name = 0;
1796        char *res_class = 0;
1797
1798        GetClassNames(pWMInfo, iWindow, &res_name, &res_class, &window_name);
1799
1800        style = STYLE_NONE;
1801        style = winOverrideStyle(res_name, res_class, window_name);
1802
1803#define APPLICATION_ID_FORMAT	"%s.xwin.%s"
1804#define APPLICATION_ID_UNKNOWN "unknown"
1805        if (res_class) {
1806            asprintf(&application_id, APPLICATION_ID_FORMAT, XVENDORNAME,
1807                     res_class);
1808        }
1809        else {
1810            asprintf(&application_id, APPLICATION_ID_FORMAT, XVENDORNAME,
1811                     APPLICATION_ID_UNKNOWN);
1812        }
1813        winSetAppUserModelID(hWnd, application_id);
1814
1815        free(application_id);
1816        free(res_name);
1817        free(res_class);
1818        free(window_name);
1819    }
1820
1821    if (style & STYLE_TOPMOST)
1822        *zstyle = HWND_TOPMOST;
1823    else if (style & STYLE_MAXIMIZE)
1824        maxmin = (hint & ~HINT_MIN) | HINT_MAX;
1825    else if (style & STYLE_MINIMIZE)
1826        maxmin = (hint & ~HINT_MAX) | HINT_MIN;
1827    else if (style & STYLE_BOTTOM)
1828        *zstyle = HWND_BOTTOM;
1829
1830    if (maxmin & HINT_MAX)
1831        SendMessage(hWnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
1832    else if (maxmin & HINT_MIN)
1833        SendMessage(hWnd, WM_SYSCOMMAND, SC_MINIMIZE, 0);
1834
1835    if (style & STYLE_NOTITLE)
1836        hint =
1837            (hint & ~HINT_NOFRAME & ~HINT_BORDER & ~HINT_CAPTION) |
1838            HINT_SIZEBOX;
1839    else if (style & STYLE_OUTLINE)
1840        hint =
1841            (hint & ~HINT_NOFRAME & ~HINT_SIZEBOX & ~HINT_CAPTION) |
1842            HINT_BORDER;
1843    else if (style & STYLE_NOFRAME)
1844        hint =
1845            (hint & ~HINT_BORDER & ~HINT_CAPTION & ~HINT_SIZEBOX) |
1846            HINT_NOFRAME;
1847
1848    /* Now apply styles to window */
1849    style = GetWindowLongPtr(hWnd, GWL_STYLE);
1850    if (!style)
1851        return;                 /* GetWindowLongPointer returns 0 on failure, we hope this isn't a valid style */
1852
1853    style &= ~WS_CAPTION & ~WS_SIZEBOX; /* Just in case */
1854
1855    if (!(hint & ~HINT_SKIPTASKBAR))    /* No hints, default */
1856        style = style | WS_CAPTION | WS_SIZEBOX;
1857    else if (hint & HINT_NOFRAME)       /* No frame, no decorations */
1858        style = style & ~WS_CAPTION & ~WS_SIZEBOX;
1859    else
1860        style = style | ((hint & HINT_BORDER) ? WS_BORDER : 0) |
1861            ((hint & HINT_SIZEBOX) ? WS_SIZEBOX : 0) |
1862            ((hint & HINT_CAPTION) ? WS_CAPTION : 0);
1863
1864    if (hint & HINT_NOMAXIMIZE)
1865        style = style & ~WS_MAXIMIZEBOX;
1866
1867    if (hint & HINT_NOMINIMIZE)
1868        style = style & ~WS_MINIMIZEBOX;
1869
1870    if (hint & HINT_NOSYSMENU)
1871        style = style & ~WS_SYSMENU;
1872
1873    if (hint & HINT_SKIPTASKBAR)
1874        style = style & ~WS_MINIMIZEBOX;        /* window will become lost if minimized */
1875
1876    SetWindowLongPtr(hWnd, GWL_STYLE, style);
1877
1878    exStyle = GetWindowLongPtr(hWnd, GWL_EXSTYLE);
1879    if (hint & HINT_SKIPTASKBAR)
1880        exStyle = (exStyle & ~WS_EX_APPWINDOW) | WS_EX_TOOLWINDOW;
1881    else
1882        exStyle = (exStyle & ~WS_EX_TOOLWINDOW) | WS_EX_APPWINDOW;
1883    SetWindowLongPtr(hWnd, GWL_EXSTYLE, exStyle);
1884
1885    winDebug
1886        ("winApplyHints: iWindow 0x%08x hints 0x%08x style 0x%08x exstyle 0x%08x\n",
1887         iWindow, hint, style, exStyle);
1888}
1889
1890void
1891winUpdateWindowPosition(HWND hWnd, HWND * zstyle)
1892{
1893    int iX, iY, iWidth, iHeight;
1894    int iDx, iDy;
1895    RECT rcNew;
1896    WindowPtr pWin = GetProp(hWnd, WIN_WINDOW_PROP);
1897    DrawablePtr pDraw = NULL;
1898
1899    if (!pWin)
1900        return;
1901    pDraw = &pWin->drawable;
1902    if (!pDraw)
1903        return;
1904
1905    /* Get the X and Y location of the X window */
1906    iX = pWin->drawable.x + GetSystemMetrics(SM_XVIRTUALSCREEN);
1907    iY = pWin->drawable.y + GetSystemMetrics(SM_YVIRTUALSCREEN);
1908
1909    /* Get the height and width of the X window */
1910    iWidth = pWin->drawable.width;
1911    iHeight = pWin->drawable.height;
1912
1913    /* Setup a rectangle with the X window position and size */
1914    SetRect(&rcNew, iX, iY, iX + iWidth, iY + iHeight);
1915
1916    winDebug("winUpdateWindowPosition - drawable extent (%d, %d)-(%d, %d)\n",
1917             rcNew.left, rcNew.top, rcNew.right, rcNew.bottom);
1918
1919    AdjustWindowRectEx(&rcNew, GetWindowLongPtr(hWnd, GWL_STYLE), FALSE,
1920                       GetWindowLongPtr(hWnd, GWL_EXSTYLE));
1921
1922    /* Don't allow window decoration to disappear off to top-left as a result of this adjustment */
1923    if (rcNew.left < GetSystemMetrics(SM_XVIRTUALSCREEN)) {
1924        iDx = GetSystemMetrics(SM_XVIRTUALSCREEN) - rcNew.left;
1925        rcNew.left += iDx;
1926        rcNew.right += iDx;
1927    }
1928
1929    if (rcNew.top < GetSystemMetrics(SM_YVIRTUALSCREEN)) {
1930        iDy = GetSystemMetrics(SM_YVIRTUALSCREEN) - rcNew.top;
1931        rcNew.top += iDy;
1932        rcNew.bottom += iDy;
1933    }
1934
1935    winDebug("winUpdateWindowPosition - Window extent (%d, %d)-(%d, %d)\n",
1936             rcNew.left, rcNew.top, rcNew.right, rcNew.bottom);
1937
1938    /* Position the Windows window */
1939    SetWindowPos(hWnd, *zstyle, rcNew.left, rcNew.top,
1940                 rcNew.right - rcNew.left, rcNew.bottom - rcNew.top, 0);
1941
1942}
1943