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