winmultiwindowwm.c revision 05b261ec
1/*
2 *Copyright (C) 1994-2000 The XFree86 Project, Inc. All Rights Reserved.
3 *
4 *Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 *"Software"), to deal in the Software without restriction, including
7 *without limitation the rights to use, copy, modify, merge, publish,
8 *distribute, sublicense, and/or sell copies of the Software, and to
9 *permit persons to whom the Software is furnished to do so, subject to
10 *the following conditions:
11 *
12 *The above copyright notice and this permission notice shall be
13 *included in all copies or substantial portions of the Software.
14 *
15 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 *EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 *MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 *NONINFRINGEMENT. IN NO EVENT SHALL THE XFREE86 PROJECT BE LIABLE FOR
19 *ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
20 *CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 *WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 *
23 *Except as contained in this notice, the name of the XFree86 Project
24 *shall not be used in advertising or otherwise to promote the sale, use
25 *or other dealings in this Software without prior written authorization
26 *from the XFree86 Project.
27 *
28 * Authors:	Kensuke Matsuzaki
29 */
30
31/* X headers */
32#ifdef HAVE_XWIN_CONFIG_H
33#include <xwin-config.h>
34#endif
35#include <stdio.h>
36#include <stdlib.h>
37#include <unistd.h>
38#ifdef __CYGWIN__
39#include <sys/select.h>
40#endif
41#include <fcntl.h>
42#include <setjmp.h>
43#define HANDLE void *
44#include <pthread.h>
45#undef HANDLE
46#include <X11/X.h>
47#include <X11/Xatom.h>
48#include <X11/Xlib.h>
49#include <X11/Xlocale.h>
50#include <X11/Xproto.h>
51#include <X11/Xutil.h>
52#include <X11/cursorfont.h>
53
54/* Windows headers */
55#ifdef __CYGWIN__
56/* Fixups to prevent collisions between Windows and X headers */
57#define ATOM DWORD
58
59#include <windows.h>
60#else
61#include <Xwindows.h>
62#endif
63
64/* Local headers */
65#include "objbase.h"
66#include "ddraw.h"
67#include "winwindow.h"
68#ifdef XWIN_MULTIWINDOWEXTWM
69#include "windowswmstr.h"
70#endif
71
72extern void winDebug(const char *format, ...);
73
74#ifndef CYGDEBUG
75#define CYGDEBUG NO
76#endif
77
78/*
79 * Constant defines
80 */
81
82#define WIN_CONNECT_RETRIES	5
83#define WIN_CONNECT_DELAY	5
84#ifdef HAS_DEVWINDOWS
85# define WIN_MSG_QUEUE_FNAME	"/dev/windows"
86#endif
87#define WIN_JMP_OKAY		0
88#define WIN_JMP_ERROR_IO	2
89
90
91/*
92 * Local structures
93 */
94
95typedef struct _WMMsgNodeRec {
96  winWMMessageRec	msg;
97  struct _WMMsgNodeRec	*pNext;
98} WMMsgNodeRec, *WMMsgNodePtr;
99
100typedef struct _WMMsgQueueRec {
101  struct _WMMsgNodeRec	*pHead;
102  struct _WMMsgNodeRec	*pTail;
103  pthread_mutex_t	pmMutex;
104  pthread_cond_t	pcNotEmpty;
105  int			nQueueSize;
106} WMMsgQueueRec, *WMMsgQueuePtr;
107
108typedef struct _WMInfo {
109  Display		*pDisplay;
110  WMMsgQueueRec		wmMsgQueue;
111  Atom			atmWmProtos;
112  Atom			atmWmDelete;
113  Atom			atmPrivMap;
114  Bool			fAllowOtherWM;
115} WMInfoRec, *WMInfoPtr;
116
117typedef struct _WMProcArgRec {
118  DWORD			dwScreen;
119  WMInfoPtr		pWMInfo;
120  pthread_mutex_t	*ppmServerStarted;
121} WMProcArgRec, *WMProcArgPtr;
122
123typedef struct _XMsgProcArgRec {
124  Display		*pDisplay;
125  DWORD			dwScreen;
126  WMInfoPtr		pWMInfo;
127  pthread_mutex_t	*ppmServerStarted;
128  HWND			hwndScreen;
129} XMsgProcArgRec, *XMsgProcArgPtr;
130
131
132/*
133 * References to external symbols
134 */
135
136extern char *display;
137extern void ErrorF (const char* /*f*/, ...);
138
139
140/*
141 * Prototypes for local functions
142 */
143
144static void
145PushMessage (WMMsgQueuePtr pQueue, WMMsgNodePtr pNode);
146
147static WMMsgNodePtr
148PopMessage (WMMsgQueuePtr pQueue, WMInfoPtr pWMInfo);
149
150static Bool
151InitQueue (WMMsgQueuePtr pQueue);
152
153static void
154GetWindowName (Display * pDpy, Window iWin, char **ppName);
155
156static int
157SendXMessage (Display *pDisplay, Window iWin, Atom atmType, long nData);
158
159static void
160UpdateName (WMInfoPtr pWMInfo, Window iWindow);
161
162static void*
163winMultiWindowWMProc (void* pArg);
164
165static int
166winMultiWindowWMErrorHandler (Display *pDisplay, XErrorEvent *pErr);
167
168static int
169winMultiWindowWMIOErrorHandler (Display *pDisplay);
170
171static void *
172winMultiWindowXMsgProc (void *pArg);
173
174static int
175winMultiWindowXMsgProcErrorHandler (Display *pDisplay, XErrorEvent *pErr);
176
177static int
178winMultiWindowXMsgProcIOErrorHandler (Display *pDisplay);
179
180static int
181winRedirectErrorHandler (Display *pDisplay, XErrorEvent *pErr);
182
183static void
184winInitMultiWindowWM (WMInfoPtr pWMInfo, WMProcArgPtr pProcArg);
185
186#if 0
187static void
188PreserveWin32Stack(WMInfoPtr pWMInfo, Window iWindow, UINT direction);
189#endif
190
191static Bool
192CheckAnotherWindowManager (Display *pDisplay, DWORD dwScreen);
193
194
195/*
196 * Local globals
197 */
198
199static jmp_buf			g_jmpWMEntry;
200static jmp_buf			g_jmpXMsgProcEntry;
201static Bool			g_shutdown = FALSE;
202static Bool			redirectError = FALSE;
203static Bool			g_fAnotherWMRunnig = FALSE;
204
205/*
206 * PushMessage - Push a message onto the queue
207 */
208
209static void
210PushMessage (WMMsgQueuePtr pQueue, WMMsgNodePtr pNode)
211{
212
213  /* Lock the queue mutex */
214  pthread_mutex_lock (&pQueue->pmMutex);
215
216  pNode->pNext = NULL;
217
218  if (pQueue->pTail != NULL)
219    {
220      pQueue->pTail->pNext = pNode;
221    }
222  pQueue->pTail = pNode;
223
224  if (pQueue->pHead == NULL)
225    {
226      pQueue->pHead = pNode;
227    }
228
229
230#if 0
231  switch (pNode->msg.msg)
232    {
233    case WM_WM_MOVE:
234      ErrorF ("\tWM_WM_MOVE\n");
235      break;
236    case WM_WM_SIZE:
237      ErrorF ("\tWM_WM_SIZE\n");
238      break;
239    case WM_WM_RAISE:
240      ErrorF ("\tWM_WM_RAISE\n");
241      break;
242    case WM_WM_LOWER:
243      ErrorF ("\tWM_WM_LOWER\n");
244      break;
245    case WM_WM_MAP:
246      ErrorF ("\tWM_WM_MAP\n");
247      break;
248    case WM_WM_UNMAP:
249      ErrorF ("\tWM_WM_UNMAP\n");
250      break;
251    case WM_WM_KILL:
252      ErrorF ("\tWM_WM_KILL\n");
253      break;
254    case WM_WM_ACTIVATE:
255      ErrorF ("\tWM_WM_ACTIVATE\n");
256      break;
257    default:
258      ErrorF ("\tUnknown Message.\n");
259      break;
260    }
261#endif
262
263  /* Increase the count of elements in the queue by one */
264  ++(pQueue->nQueueSize);
265
266  /* Release the queue mutex */
267  pthread_mutex_unlock (&pQueue->pmMutex);
268
269  /* Signal that the queue is not empty */
270  pthread_cond_signal (&pQueue->pcNotEmpty);
271}
272
273
274#if CYGMULTIWINDOW_DEBUG
275/*
276 * QueueSize - Return the size of the queue
277 */
278
279static int
280QueueSize (WMMsgQueuePtr pQueue)
281{
282  WMMsgNodePtr		pNode;
283  int			nSize = 0;
284
285  /* Loop through all elements in the queue */
286  for (pNode = pQueue->pHead; pNode != NULL; pNode = pNode->pNext)
287    ++nSize;
288
289  return nSize;
290}
291#endif
292
293
294/*
295 * PopMessage - Pop a message from the queue
296 */
297
298static WMMsgNodePtr
299PopMessage (WMMsgQueuePtr pQueue, WMInfoPtr pWMInfo)
300{
301  WMMsgNodePtr		pNode;
302
303  /* Lock the queue mutex */
304  pthread_mutex_lock (&pQueue->pmMutex);
305
306  /* Wait for --- */
307  while (pQueue->pHead == NULL)
308    {
309      pthread_cond_wait (&pQueue->pcNotEmpty, &pQueue->pmMutex);
310    }
311
312  pNode = pQueue->pHead;
313  if (pQueue->pHead != NULL)
314    {
315      pQueue->pHead = pQueue->pHead->pNext;
316    }
317
318  if (pQueue->pTail == pNode)
319    {
320      pQueue->pTail = NULL;
321    }
322
323  /* Drop the number of elements in the queue by one */
324  --(pQueue->nQueueSize);
325
326#if CYGMULTIWINDOW_DEBUG
327  ErrorF ("Queue Size %d %d\n", pQueue->nQueueSize, QueueSize(pQueue));
328#endif
329
330  /* Release the queue mutex */
331  pthread_mutex_unlock (&pQueue->pmMutex);
332
333  return pNode;
334}
335
336
337#if 0
338/*
339 * HaveMessage -
340 */
341
342static Bool
343HaveMessage (WMMsgQueuePtr pQueue, UINT msg, Window iWindow)
344{
345  WMMsgNodePtr pNode;
346
347  for (pNode = pQueue->pHead; pNode != NULL; pNode = pNode->pNext)
348    {
349      if (pNode->msg.msg==msg && pNode->msg.iWindow==iWindow)
350	return True;
351    }
352
353  return False;
354}
355#endif
356
357
358/*
359 * InitQueue - Initialize the Window Manager message queue
360 */
361
362static
363Bool
364InitQueue (WMMsgQueuePtr pQueue)
365{
366  /* Check if the pQueue pointer is NULL */
367  if (pQueue == NULL)
368    {
369      ErrorF ("InitQueue - pQueue is NULL.  Exiting.\n");
370      return FALSE;
371    }
372
373  /* Set the head and tail to NULL */
374  pQueue->pHead = NULL;
375  pQueue->pTail = NULL;
376
377  /* There are no elements initially */
378  pQueue->nQueueSize = 0;
379
380#if CYGMULTIWINDOW_DEBUG
381  ErrorF ("InitQueue - Queue Size %d %d\n", pQueue->nQueueSize,
382	  QueueSize(pQueue));
383#endif
384
385  ErrorF ("InitQueue - Calling pthread_mutex_init\n");
386
387  /* Create synchronization objects */
388  pthread_mutex_init (&pQueue->pmMutex, NULL);
389
390  ErrorF ("InitQueue - pthread_mutex_init returned\n");
391  ErrorF ("InitQueue - Calling pthread_cond_init\n");
392
393  pthread_cond_init (&pQueue->pcNotEmpty, NULL);
394
395  ErrorF ("InitQueue - pthread_cond_init returned\n");
396
397  return TRUE;
398}
399
400
401/*
402 * GetWindowName - Retrieve the title of an X Window
403 */
404
405static void
406GetWindowName (Display *pDisplay, Window iWin, char **ppName)
407{
408  int			nResult, nNum;
409  char			**ppList;
410  XTextProperty		xtpName;
411
412#if CYGMULTIWINDOW_DEBUG
413  ErrorF ("GetWindowName\n");
414#endif
415
416  /* Intialize ppName to NULL */
417  *ppName = NULL;
418
419  /* Try to get --- */
420  nResult = XGetWMName (pDisplay, iWin, &xtpName);
421  if (!nResult || !xtpName.value || !xtpName.nitems)
422    {
423#if CYGMULTIWINDOW_DEBUG
424      ErrorF ("GetWindowName - XGetWMName failed.  No name.\n");
425#endif
426      return;
427    }
428
429  /* */
430  if (xtpName.encoding == XA_STRING)
431    {
432      /* */
433      if (xtpName.value)
434	{
435	  int size = xtpName.nitems * (xtpName.format >> 3);
436	  *ppName = malloc(size + 1);
437	  strncpy(*ppName, xtpName.value, size);
438	  (*ppName)[size] = 0;
439	  XFree (xtpName.value);
440	}
441
442#if CYGMULTIWINDOW_DEBUG
443      ErrorF ("GetWindowName - XA_STRING %s\n", *ppName);
444#endif
445    }
446  else
447    {
448      if (XmbTextPropertyToTextList (pDisplay, &xtpName, &ppList, &nNum) >= Success && nNum > 0 && *ppList)
449	{
450	  *ppName = strdup (*ppList);
451	  XFreeStringList (ppList);
452	}
453      XFree (xtpName.value);
454
455#if CYGMULTIWINDOW_DEBUG
456      ErrorF ("GetWindowName - %s %s\n",
457	      XGetAtomName (pDisplay, xtpName.encoding), *ppName);
458#endif
459    }
460
461#if CYGMULTIWINDOW_DEBUG
462  ErrorF ("GetWindowName - Returning\n");
463#endif
464}
465
466
467/*
468 * Send a message to the X server from the WM thread
469 */
470
471static int
472SendXMessage (Display *pDisplay, Window iWin, Atom atmType, long nData)
473{
474  XEvent		e;
475
476  /* Prepare the X event structure */
477  e.type = ClientMessage;
478  e.xclient.window = iWin;
479  e.xclient.message_type = atmType;
480  e.xclient.format = 32;
481  e.xclient.data.l[0] = nData;
482  e.xclient.data.l[1] = CurrentTime;
483
484  /* Send the event to X */
485  return XSendEvent (pDisplay, iWin, False, NoEventMask, &e);
486}
487
488
489/*
490 * Updates the name of a HWND according to its X WM_NAME property
491 */
492
493static void
494UpdateName (WMInfoPtr pWMInfo, Window iWindow)
495{
496  char			*pszName;
497  Atom			atmType;
498  int			fmtRet;
499  unsigned long		items, remain;
500  HWND			*retHwnd, hWnd;
501  XWindowAttributes	attr;
502
503  hWnd = 0;
504
505  /* See if we can get the cached HWND for this window... */
506  if (XGetWindowProperty (pWMInfo->pDisplay,
507			  iWindow,
508			  pWMInfo->atmPrivMap,
509			  0,
510			  1,
511			  False,
512			  XA_INTEGER,//pWMInfo->atmPrivMap,
513			  &atmType,
514			  &fmtRet,
515			  &items,
516			  &remain,
517			  (unsigned char **) &retHwnd) == Success)
518    {
519      if (retHwnd)
520	{
521	  hWnd = *retHwnd;
522	  XFree (retHwnd);
523	}
524    }
525
526  /* Some sanity checks */
527  if (!hWnd) return;
528  if (!IsWindow (hWnd)) return;
529
530  /* Set the Windows window name */
531  GetWindowName (pWMInfo->pDisplay, iWindow, &pszName);
532  if (pszName)
533    {
534      /* Get the window attributes */
535      XGetWindowAttributes (pWMInfo->pDisplay,
536			    iWindow,
537			    &attr);
538      if (!attr.override_redirect)
539	{
540	  SetWindowText (hWnd, pszName);
541	  winUpdateIcon (iWindow);
542	}
543
544      free (pszName);
545    }
546}
547
548
549#if 0
550/*
551 * Fix up any differences between the X11 and Win32 window stacks
552 * starting at the window passed in
553 */
554static void
555PreserveWin32Stack(WMInfoPtr pWMInfo, Window iWindow, UINT direction)
556{
557  Atom                  atmType;
558  int                   fmtRet;
559  unsigned long         items, remain;
560  HWND                  hWnd, *retHwnd;
561  DWORD                 myWinProcID, winProcID;
562  Window                xWindow;
563  WINDOWPLACEMENT       wndPlace;
564
565  hWnd = NULL;
566  /* See if we can get the cached HWND for this window... */
567  if (XGetWindowProperty (pWMInfo->pDisplay,
568			  iWindow,
569			  pWMInfo->atmPrivMap,
570			  0,
571			  1,
572			  False,
573			  XA_INTEGER,//pWMInfo->atmPrivMap,
574			  &atmType,
575			  &fmtRet,
576			  &items,
577			  &remain,
578			  (unsigned char **) &retHwnd) == Success)
579    {
580      if (retHwnd)
581	{
582	  hWnd = *retHwnd;
583	  XFree (retHwnd);
584	}
585    }
586
587  if (!hWnd) return;
588
589  GetWindowThreadProcessId (hWnd, &myWinProcID);
590  hWnd = GetNextWindow (hWnd, direction);
591
592  while (hWnd) {
593    GetWindowThreadProcessId (hWnd, &winProcID);
594    if (winProcID == myWinProcID)
595      {
596	wndPlace.length = sizeof(WINDOWPLACEMENT);
597	GetWindowPlacement (hWnd, &wndPlace);
598	if ( !(wndPlace.showCmd==SW_HIDE ||
599	       wndPlace.showCmd==SW_MINIMIZE) )
600	  {
601	    xWindow = (Window)GetProp (hWnd, WIN_WID_PROP);
602	    if (xWindow)
603	      {
604		if (direction==GW_HWNDPREV)
605		  XRaiseWindow (pWMInfo->pDisplay, xWindow);
606		else
607		  XLowerWindow (pWMInfo->pDisplay, xWindow);
608	      }
609	  }
610      }
611    hWnd = GetNextWindow(hWnd, direction);
612  }
613}
614#endif /* PreserveWin32Stack */
615
616
617/*
618 * winMultiWindowWMProc
619 */
620
621static void *
622winMultiWindowWMProc (void *pArg)
623{
624  WMProcArgPtr		pProcArg = (WMProcArgPtr)pArg;
625  WMInfoPtr		pWMInfo = pProcArg->pWMInfo;
626
627  /* Initialize the Window Manager */
628  winInitMultiWindowWM (pWMInfo, pProcArg);
629
630#if CYGMULTIWINDOW_DEBUG
631  ErrorF ("winMultiWindowWMProc ()\n");
632#endif
633
634  /* Loop until we explicity break out */
635  for (;;)
636    {
637      WMMsgNodePtr	pNode;
638
639      if(g_fAnotherWMRunnig)/* Another Window manager exists. */
640	{
641	  Sleep (1000);
642	  continue;
643	}
644
645      /* Pop a message off of our queue */
646      pNode = PopMessage (&pWMInfo->wmMsgQueue, pWMInfo);
647      if (pNode == NULL)
648	{
649	  /* Bail if PopMessage returns without a message */
650	  /* NOTE: Remember that PopMessage is a blocking function. */
651	  ErrorF ("winMultiWindowWMProc - Queue is Empty?  Exiting.\n");
652	  pthread_exit (NULL);
653	}
654
655#if CYGMULTIWINDOW_DEBUG
656      ErrorF ("winMultiWindowWMProc - %d ms MSG: %d ID: %d\n",
657	      GetTickCount (), (int)pNode->msg.msg, (int)pNode->msg.dwID);
658#endif
659
660      /* Branch on the message type */
661      switch (pNode->msg.msg)
662	{
663#if 0
664	case WM_WM_MOVE:
665	  ErrorF ("\tWM_WM_MOVE\n");
666	  break;
667
668	case WM_WM_SIZE:
669	  ErrorF ("\tWM_WM_SIZE\n");
670	  break;
671#endif
672
673	case WM_WM_RAISE:
674#if CYGMULTIWINDOW_DEBUG
675	  ErrorF ("\tWM_WM_RAISE\n");
676#endif
677	  /* Raise the window */
678	  XRaiseWindow (pWMInfo->pDisplay, pNode->msg.iWindow);
679#if 0
680	  PreserveWin32Stack (pWMInfo, pNode->msg.iWindow, GW_HWNDPREV);
681#endif
682	  break;
683
684	case WM_WM_LOWER:
685#if CYGMULTIWINDOW_DEBUG
686	  ErrorF ("\tWM_WM_LOWER\n");
687#endif
688
689	  /* Lower the window */
690	  XLowerWindow (pWMInfo->pDisplay, pNode->msg.iWindow);
691	  break;
692
693	case WM_WM_MAP:
694#if CYGMULTIWINDOW_DEBUG
695	  ErrorF ("\tWM_WM_MAP\n");
696#endif
697	  /* Put a note as to the HWND associated with this Window */
698	  XChangeProperty (pWMInfo->pDisplay,
699			   pNode->msg.iWindow,
700			   pWMInfo->atmPrivMap,
701			   XA_INTEGER,//pWMInfo->atmPrivMap,
702			   32,
703			   PropModeReplace,
704			   (unsigned char *) &(pNode->msg.hwndWindow),
705			   1);
706	  UpdateName (pWMInfo, pNode->msg.iWindow);
707	  winUpdateIcon (pNode->msg.iWindow);
708#if 0
709	  /* Handles the case where there are AOT windows above it in W32 */
710	  PreserveWin32Stack (pWMInfo, pNode->msg.iWindow, GW_HWNDPREV);
711#endif
712	  break;
713
714	case WM_WM_UNMAP:
715#if CYGMULTIWINDOW_DEBUG
716	  ErrorF ("\tWM_WM_UNMAP\n");
717#endif
718
719	  /* Unmap the window */
720	  XUnmapWindow (pWMInfo->pDisplay, pNode->msg.iWindow);
721	  break;
722
723	case WM_WM_KILL:
724#if CYGMULTIWINDOW_DEBUG
725	  ErrorF ("\tWM_WM_KILL\n");
726#endif
727	  {
728	    int				i, n, found = 0;
729	    Atom			*protocols;
730
731	    /* --- */
732	    if (XGetWMProtocols (pWMInfo->pDisplay,
733				 pNode->msg.iWindow,
734				 &protocols,
735				 &n))
736	      {
737		for (i = 0; i < n; ++i)
738		  if (protocols[i] == pWMInfo->atmWmDelete)
739		    ++found;
740
741		XFree (protocols);
742	      }
743
744	    /* --- */
745	    if (found)
746	      SendXMessage (pWMInfo->pDisplay,
747			    pNode->msg.iWindow,
748			    pWMInfo->atmWmProtos,
749			    pWMInfo->atmWmDelete);
750	    else
751	      XKillClient (pWMInfo->pDisplay,
752			   pNode->msg.iWindow);
753	  }
754	  break;
755
756	case WM_WM_ACTIVATE:
757#if CYGMULTIWINDOW_DEBUG
758	  ErrorF ("\tWM_WM_ACTIVATE\n");
759#endif
760
761	  /* Set the input focus */
762	  XSetInputFocus (pWMInfo->pDisplay,
763			  pNode->msg.iWindow,
764			  RevertToPointerRoot,
765			  CurrentTime);
766	  break;
767
768	case WM_WM_NAME_EVENT:
769	  UpdateName (pWMInfo, pNode->msg.iWindow);
770	  break;
771
772	case WM_WM_HINTS_EVENT:
773	  winUpdateIcon (pNode->msg.iWindow);
774	  break;
775
776	case WM_WM_CHANGE_STATE:
777	  /* Minimize the window in Windows */
778	  winMinimizeWindow (pNode->msg.iWindow);
779	  break;
780
781	default:
782	  ErrorF ("winMultiWindowWMProc - Unknown Message.  Exiting.\n");
783	  pthread_exit (NULL);
784	  break;
785	}
786
787      /* Free the retrieved message */
788      free (pNode);
789
790      /* Flush any pending events on our display */
791      XFlush (pWMInfo->pDisplay);
792    }
793
794  /* Free the condition variable */
795  pthread_cond_destroy (&pWMInfo->wmMsgQueue.pcNotEmpty);
796
797  /* Free the mutex variable */
798  pthread_mutex_destroy (&pWMInfo->wmMsgQueue.pmMutex);
799
800  /* Free the passed-in argument */
801  free (pProcArg);
802
803#if CYGMULTIWINDOW_DEBUG
804  ErrorF("-winMultiWindowWMProc ()\n");
805#endif
806}
807
808
809/*
810 * X message procedure
811 */
812
813static void *
814winMultiWindowXMsgProc (void *pArg)
815{
816  winWMMessageRec       msg;
817  XMsgProcArgPtr	pProcArg = (XMsgProcArgPtr) pArg;
818  char			pszDisplay[512];
819  int                   iRetries;
820  XEvent		event;
821  Atom                  atmWmName;
822  Atom                  atmWmHints;
823  Atom			atmWmChange;
824  int			iReturn;
825  XIconSize		*xis;
826
827  ErrorF ("winMultiWindowXMsgProc - Hello\n");
828
829  /* Check that argument pointer is not invalid */
830  if (pProcArg == NULL)
831    {
832      ErrorF ("winMultiWindowXMsgProc - pProcArg is NULL.  Exiting.\n");
833      pthread_exit (NULL);
834    }
835
836  ErrorF ("winMultiWindowXMsgProc - Calling pthread_mutex_lock ()\n");
837
838  /* Grab the server started mutex - pause until we get it */
839  iReturn = pthread_mutex_lock (pProcArg->ppmServerStarted);
840  if (iReturn != 0)
841    {
842      ErrorF ("winMultiWindowXMsgProc - pthread_mutex_lock () failed: %d.  "
843	      "Exiting.\n",
844	      iReturn);
845      pthread_exit (NULL);
846    }
847
848  ErrorF ("winMultiWindowXMsgProc - pthread_mutex_lock () returned.\n");
849
850  /* Allow multiple threads to access Xlib */
851  if (XInitThreads () == 0)
852    {
853      ErrorF ("winMultiWindowXMsgProc - XInitThreads () failed.  Exiting.\n");
854      pthread_exit (NULL);
855    }
856
857  /* See if X supports the current locale */
858  if (XSupportsLocale () == False)
859    {
860      ErrorF ("winMultiWindowXMsgProc - Locale not supported by X.  "
861	      "Exiting.\n");
862      pthread_exit (NULL);
863    }
864
865  /* Release the server started mutex */
866  pthread_mutex_unlock (pProcArg->ppmServerStarted);
867
868  ErrorF ("winMultiWindowXMsgProc - pthread_mutex_unlock () returned.\n");
869
870  /* Set jump point for IO Error exits */
871  iReturn = setjmp (g_jmpXMsgProcEntry);
872
873  /* Check if we should continue operations */
874  if (iReturn != WIN_JMP_ERROR_IO
875      && iReturn != WIN_JMP_OKAY)
876    {
877      /* setjmp returned an unknown value, exit */
878      ErrorF ("winInitMultiWindowXMsgProc - setjmp returned: %d.  Exiting.\n",
879	      iReturn);
880      pthread_exit (NULL);
881    }
882  else if (iReturn == WIN_JMP_ERROR_IO)
883    {
884      ErrorF ("winInitMultiWindowXMsgProc - Caught IO Error.  Exiting.\n");
885      pthread_exit (NULL);
886    }
887
888  /* Install our error handler */
889  XSetErrorHandler (winMultiWindowXMsgProcErrorHandler);
890  XSetIOErrorHandler (winMultiWindowXMsgProcIOErrorHandler);
891
892  /* Setup the display connection string x */
893  snprintf (pszDisplay,
894	    512, "127.0.0.1:%s.%d", display, (int)pProcArg->dwScreen);
895
896  /* Print the display connection string */
897  ErrorF ("winMultiWindowXMsgProc - DISPLAY=%s\n", pszDisplay);
898
899  /* Initialize retry count */
900  iRetries = 0;
901
902  /* Open the X display */
903  do
904    {
905      /* Try to open the display */
906      pProcArg->pDisplay = XOpenDisplay (pszDisplay);
907      if (pProcArg->pDisplay == NULL)
908	{
909	  ErrorF ("winMultiWindowXMsgProc - Could not open display, try: %d, "
910		  "sleeping: %d\n\f",
911		  iRetries + 1, WIN_CONNECT_DELAY);
912	  ++iRetries;
913	  sleep (WIN_CONNECT_DELAY);
914	  continue;
915	}
916      else
917	break;
918    }
919  while (pProcArg->pDisplay == NULL && iRetries < WIN_CONNECT_RETRIES);
920
921  /* Make sure that the display opened */
922  if (pProcArg->pDisplay == NULL)
923    {
924      ErrorF ("winMultiWindowXMsgProc - Failed opening the display.  "
925	      "Exiting.\n");
926      pthread_exit (NULL);
927    }
928
929  ErrorF ("winMultiWindowXMsgProc - XOpenDisplay () returned and "
930	  "successfully opened the display.\n");
931
932  /* Check if another window manager is already running */
933  if (pProcArg->pWMInfo->fAllowOtherWM)
934  {
935    g_fAnotherWMRunnig = CheckAnotherWindowManager (pProcArg->pDisplay, pProcArg->dwScreen);
936  } else {
937    redirectError = FALSE;
938    XSetErrorHandler (winRedirectErrorHandler);
939    XSelectInput(pProcArg->pDisplay,
940        RootWindow (pProcArg->pDisplay, pProcArg->dwScreen),
941        SubstructureNotifyMask | ButtonPressMask);
942    XSync (pProcArg->pDisplay, 0);
943    XSetErrorHandler (winMultiWindowXMsgProcErrorHandler);
944    if (redirectError)
945    {
946      ErrorF ("winMultiWindowXMsgProc - "
947          "another window manager is running.  Exiting.\n");
948      pthread_exit (NULL);
949    }
950    g_fAnotherWMRunnig = FALSE;
951  }
952
953  /* Set up the supported icon sizes */
954  xis = XAllocIconSize ();
955  if (xis)
956    {
957      xis->min_width = xis->min_height = 16;
958      xis->max_width = xis->max_height = 48;
959      xis->width_inc = xis->height_inc = 16;
960      XSetIconSizes (pProcArg->pDisplay,
961		     RootWindow (pProcArg->pDisplay, pProcArg->dwScreen),
962		     xis,
963		     1);
964      XFree (xis);
965    }
966
967  atmWmName   = XInternAtom (pProcArg->pDisplay,
968			     "WM_NAME",
969			     False);
970  atmWmHints   = XInternAtom (pProcArg->pDisplay,
971			      "WM_HINTS",
972			      False);
973  atmWmChange  = XInternAtom (pProcArg->pDisplay,
974			      "WM_CHANGE_STATE",
975			      False);
976
977  /* Loop until we explicitly break out */
978  while (1)
979    {
980      if (g_shutdown)
981        break;
982
983      if (pProcArg->pWMInfo->fAllowOtherWM && !XPending (pProcArg->pDisplay))
984	{
985	  if (CheckAnotherWindowManager (pProcArg->pDisplay, pProcArg->dwScreen))
986	    {
987	      if (!g_fAnotherWMRunnig)
988		{
989		  g_fAnotherWMRunnig = TRUE;
990		  SendMessage(*(HWND*)pProcArg->hwndScreen, WM_UNMANAGE, 0, 0);
991		}
992	    }
993	  else
994	    {
995	      if (g_fAnotherWMRunnig)
996		{
997		  g_fAnotherWMRunnig = FALSE;
998		  SendMessage(*(HWND*)pProcArg->hwndScreen, WM_MANAGE, 0, 0);
999		}
1000	    }
1001	  Sleep (500);
1002	  continue;
1003	}
1004
1005      /* Fetch next event */
1006      XNextEvent (pProcArg->pDisplay, &event);
1007
1008      /* Branch on event type */
1009      if (event.type == CreateNotify)
1010	{
1011	  XWindowAttributes	attr;
1012
1013	  XSelectInput (pProcArg->pDisplay,
1014			event.xcreatewindow.window,
1015			PropertyChangeMask);
1016
1017	  /* Get the window attributes */
1018	  XGetWindowAttributes (pProcArg->pDisplay,
1019				event.xcreatewindow.window,
1020				&attr);
1021
1022	  if (!attr.override_redirect)
1023	    XSetWindowBorderWidth(pProcArg->pDisplay,
1024				  event.xcreatewindow.window,
1025				  0);
1026	}
1027      else if (event.type == PropertyNotify
1028	       && event.xproperty.atom == atmWmName)
1029	{
1030	  memset (&msg, 0, sizeof (msg));
1031
1032	  msg.msg = WM_WM_NAME_EVENT;
1033	  msg.iWindow = event.xproperty.window;
1034
1035	  /* Other fields ignored */
1036	  winSendMessageToWM (pProcArg->pWMInfo, &msg);
1037	}
1038      else if (event.type == PropertyNotify
1039	       && event.xproperty.atom == atmWmHints)
1040	{
1041	  memset (&msg, 0, sizeof (msg));
1042
1043	  msg.msg = WM_WM_HINTS_EVENT;
1044	  msg.iWindow = event.xproperty.window;
1045
1046	  /* Other fields ignored */
1047	  winSendMessageToWM (pProcArg->pWMInfo, &msg);
1048	}
1049      else if (event.type == ClientMessage
1050	       && event.xclient.message_type == atmWmChange
1051	       && event.xclient.data.l[0] == IconicState)
1052	{
1053	  ErrorF ("winMultiWindowXMsgProc - WM_CHANGE_STATE - IconicState\n");
1054
1055	  memset (&msg, 0, sizeof (msg));
1056
1057	  msg.msg = WM_WM_CHANGE_STATE;
1058	  msg.iWindow = event.xclient.window;
1059
1060	  winSendMessageToWM (pProcArg->pWMInfo, &msg);
1061	}
1062    }
1063
1064  XCloseDisplay (pProcArg->pDisplay);
1065  pthread_exit (NULL);
1066
1067}
1068
1069
1070/*
1071 * winInitWM - Entry point for the X server to spawn
1072 * the Window Manager thread.  Called from
1073 * winscrinit.c/winFinishScreenInitFB ().
1074 */
1075
1076Bool
1077winInitWM (void **ppWMInfo,
1078	   pthread_t *ptWMProc,
1079	   pthread_t *ptXMsgProc,
1080	   pthread_mutex_t *ppmServerStarted,
1081	   int dwScreen,
1082	   HWND hwndScreen,
1083	   BOOL allowOtherWM)
1084{
1085  WMProcArgPtr		pArg = (WMProcArgPtr) malloc (sizeof(WMProcArgRec));
1086  WMInfoPtr		pWMInfo = (WMInfoPtr) malloc (sizeof(WMInfoRec));
1087  XMsgProcArgPtr	pXMsgArg = (XMsgProcArgPtr) malloc (sizeof(XMsgProcArgRec));
1088
1089  /* Bail if the input parameters are bad */
1090  if (pArg == NULL || pWMInfo == NULL)
1091    {
1092      ErrorF ("winInitWM - malloc failed.\n");
1093      return FALSE;
1094    }
1095
1096  /* Zero the allocated memory */
1097  ZeroMemory (pArg, sizeof (WMProcArgRec));
1098  ZeroMemory (pWMInfo, sizeof (WMInfoRec));
1099  ZeroMemory (pXMsgArg, sizeof (XMsgProcArgRec));
1100
1101  /* Set a return pointer to the Window Manager info structure */
1102  *ppWMInfo = pWMInfo;
1103  pWMInfo->fAllowOtherWM = allowOtherWM;
1104
1105  /* Setup the argument structure for the thread function */
1106  pArg->dwScreen = dwScreen;
1107  pArg->pWMInfo = pWMInfo;
1108  pArg->ppmServerStarted = ppmServerStarted;
1109
1110  /* Intialize the message queue */
1111  if (!InitQueue (&pWMInfo->wmMsgQueue))
1112    {
1113      ErrorF ("winInitWM - InitQueue () failed.\n");
1114      return FALSE;
1115    }
1116
1117  /* Spawn a thread for the Window Manager */
1118  if (pthread_create (ptWMProc, NULL, winMultiWindowWMProc, pArg))
1119    {
1120      /* Bail if thread creation failed */
1121      ErrorF ("winInitWM - pthread_create failed for Window Manager.\n");
1122      return FALSE;
1123    }
1124
1125  /* Spawn the XNextEvent thread, will send messages to WM */
1126  pXMsgArg->dwScreen = dwScreen;
1127  pXMsgArg->pWMInfo = pWMInfo;
1128  pXMsgArg->ppmServerStarted = ppmServerStarted;
1129  pXMsgArg->hwndScreen = hwndScreen;
1130  if (pthread_create (ptXMsgProc, NULL, winMultiWindowXMsgProc, pXMsgArg))
1131    {
1132      /* Bail if thread creation failed */
1133      ErrorF ("winInitWM - pthread_create failed on XMSG.\n");
1134      return FALSE;
1135    }
1136
1137#if CYGDEBUG || YES
1138  winDebug ("winInitWM - Returning.\n");
1139#endif
1140
1141  return TRUE;
1142}
1143
1144
1145/*
1146 * Window manager thread - setup
1147 */
1148
1149static void
1150winInitMultiWindowWM (WMInfoPtr pWMInfo, WMProcArgPtr pProcArg)
1151{
1152  int                   iRetries = 0;
1153  char			pszDisplay[512];
1154  int			iReturn;
1155
1156  ErrorF ("winInitMultiWindowWM - Hello\n");
1157
1158  /* Check that argument pointer is not invalid */
1159  if (pProcArg == NULL)
1160    {
1161      ErrorF ("winInitMultiWindowWM - pProcArg is NULL.  Exiting.\n");
1162      pthread_exit (NULL);
1163    }
1164
1165  ErrorF ("winInitMultiWindowWM - Calling pthread_mutex_lock ()\n");
1166
1167  /* Grab our garbage mutex to satisfy pthread_cond_wait */
1168  iReturn = pthread_mutex_lock (pProcArg->ppmServerStarted);
1169  if (iReturn != 0)
1170    {
1171      ErrorF ("winInitMultiWindowWM - pthread_mutex_lock () failed: %d.  "
1172	      "Exiting.\n",
1173	      iReturn);
1174      pthread_exit (NULL);
1175    }
1176
1177  ErrorF ("winInitMultiWindowWM - pthread_mutex_lock () returned.\n");
1178
1179  /* Allow multiple threads to access Xlib */
1180  if (XInitThreads () == 0)
1181    {
1182      ErrorF ("winInitMultiWindowWM - XInitThreads () failed.  Exiting.\n");
1183      pthread_exit (NULL);
1184    }
1185
1186  /* See if X supports the current locale */
1187  if (XSupportsLocale () == False)
1188    {
1189      ErrorF ("winInitMultiWindowWM - Locale not supported by X.  Exiting.\n");
1190      pthread_exit (NULL);
1191    }
1192
1193  /* Release the server started mutex */
1194  pthread_mutex_unlock (pProcArg->ppmServerStarted);
1195
1196  ErrorF ("winInitMultiWindowWM - pthread_mutex_unlock () returned.\n");
1197
1198  /* Set jump point for IO Error exits */
1199  iReturn = setjmp (g_jmpWMEntry);
1200
1201  /* Check if we should continue operations */
1202  if (iReturn != WIN_JMP_ERROR_IO
1203      && iReturn != WIN_JMP_OKAY)
1204    {
1205      /* setjmp returned an unknown value, exit */
1206      ErrorF ("winInitMultiWindowWM - setjmp returned: %d.  Exiting.\n",
1207	      iReturn);
1208      pthread_exit (NULL);
1209    }
1210  else if (iReturn == WIN_JMP_ERROR_IO)
1211    {
1212      ErrorF ("winInitMultiWindowWM - Caught IO Error.  Exiting.\n");
1213      pthread_exit (NULL);
1214    }
1215
1216  /* Install our error handler */
1217  XSetErrorHandler (winMultiWindowWMErrorHandler);
1218  XSetIOErrorHandler (winMultiWindowWMIOErrorHandler);
1219
1220  /* Setup the display connection string x */
1221  snprintf (pszDisplay,
1222	    512,
1223	    "127.0.0.1:%s.%d",
1224	    display,
1225	    (int) pProcArg->dwScreen);
1226
1227  /* Print the display connection string */
1228  ErrorF ("winInitMultiWindowWM - DISPLAY=%s\n", pszDisplay);
1229
1230  /* Open the X display */
1231  do
1232    {
1233      /* Try to open the display */
1234      pWMInfo->pDisplay = XOpenDisplay (pszDisplay);
1235      if (pWMInfo->pDisplay == NULL)
1236	{
1237	  ErrorF ("winInitMultiWindowWM - Could not open display, try: %d, "
1238		  "sleeping: %d\n\f",
1239		  iRetries + 1, WIN_CONNECT_DELAY);
1240	  ++iRetries;
1241	  sleep (WIN_CONNECT_DELAY);
1242	  continue;
1243	}
1244      else
1245	break;
1246    }
1247  while (pWMInfo->pDisplay == NULL && iRetries < WIN_CONNECT_RETRIES);
1248
1249  /* Make sure that the display opened */
1250  if (pWMInfo->pDisplay == NULL)
1251    {
1252      ErrorF ("winInitMultiWindowWM - Failed opening the display.  "
1253	      "Exiting.\n");
1254      pthread_exit (NULL);
1255    }
1256
1257  ErrorF ("winInitMultiWindowWM - XOpenDisplay () returned and "
1258	  "successfully opened the display.\n");
1259
1260
1261  /* Create some atoms */
1262  pWMInfo->atmWmProtos = XInternAtom (pWMInfo->pDisplay,
1263				      "WM_PROTOCOLS",
1264				      False);
1265  pWMInfo->atmWmDelete = XInternAtom (pWMInfo->pDisplay,
1266				      "WM_DELETE_WINDOW",
1267				      False);
1268#ifdef XWIN_MULTIWINDOWEXTWM
1269  pWMInfo->atmPrivMap  = XInternAtom (pWMInfo->pDisplay,
1270				      WINDOWSWM_NATIVE_HWND,
1271				      False);
1272#endif
1273
1274
1275  if (1) {
1276    Cursor cursor = XCreateFontCursor (pWMInfo->pDisplay, XC_left_ptr);
1277    if (cursor)
1278    {
1279      XDefineCursor (pWMInfo->pDisplay, DefaultRootWindow(pWMInfo->pDisplay), cursor);
1280      XFreeCursor (pWMInfo->pDisplay, cursor);
1281    }
1282  }
1283}
1284
1285
1286/*
1287 * winSendMessageToWM - Send a message from the X thread to the WM thread
1288 */
1289
1290void
1291winSendMessageToWM (void *pWMInfo, winWMMessagePtr pMsg)
1292{
1293  WMMsgNodePtr pNode;
1294
1295#if CYGMULTIWINDOW_DEBUG
1296  ErrorF ("winSendMessageToWM ()\n");
1297#endif
1298
1299  pNode = (WMMsgNodePtr)malloc(sizeof(WMMsgNodeRec));
1300  if (pNode != NULL)
1301    {
1302      memcpy (&pNode->msg, pMsg, sizeof(winWMMessageRec));
1303      PushMessage (&((WMInfoPtr)pWMInfo)->wmMsgQueue, pNode);
1304    }
1305}
1306
1307
1308/*
1309 * Window manager error handler
1310 */
1311
1312static int
1313winMultiWindowWMErrorHandler (Display *pDisplay, XErrorEvent *pErr)
1314{
1315  char pszErrorMsg[100];
1316
1317  if (pErr->request_code == X_ChangeWindowAttributes
1318      && pErr->error_code == BadAccess)
1319    {
1320      ErrorF ("winMultiWindowWMErrorHandler - ChangeWindowAttributes "
1321	      "BadAccess.\n");
1322      return 0;
1323    }
1324
1325  XGetErrorText (pDisplay,
1326		 pErr->error_code,
1327		 pszErrorMsg,
1328		 sizeof (pszErrorMsg));
1329  ErrorF ("winMultiWindowWMErrorHandler - ERROR: %s\n", pszErrorMsg);
1330
1331  return 0;
1332}
1333
1334
1335/*
1336 * Window manager IO error handler
1337 */
1338
1339static int
1340winMultiWindowWMIOErrorHandler (Display *pDisplay)
1341{
1342  ErrorF ("\nwinMultiWindowWMIOErrorHandler!\n\n");
1343
1344  if (g_shutdown)
1345    pthread_exit(NULL);
1346
1347  /* Restart at the main entry point */
1348  longjmp (g_jmpWMEntry, WIN_JMP_ERROR_IO);
1349
1350  return 0;
1351}
1352
1353
1354/*
1355 * X message procedure error handler
1356 */
1357
1358static int
1359winMultiWindowXMsgProcErrorHandler (Display *pDisplay, XErrorEvent *pErr)
1360{
1361  char pszErrorMsg[100];
1362
1363  XGetErrorText (pDisplay,
1364		 pErr->error_code,
1365		 pszErrorMsg,
1366		 sizeof (pszErrorMsg));
1367  ErrorF ("winMultiWindowXMsgProcErrorHandler - ERROR: %s\n", pszErrorMsg);
1368
1369  return 0;
1370}
1371
1372
1373/*
1374 * X message procedure IO error handler
1375 */
1376
1377static int
1378winMultiWindowXMsgProcIOErrorHandler (Display *pDisplay)
1379{
1380  ErrorF ("\nwinMultiWindowXMsgProcIOErrorHandler!\n\n");
1381
1382  /* Restart at the main entry point */
1383  longjmp (g_jmpXMsgProcEntry, WIN_JMP_ERROR_IO);
1384
1385  return 0;
1386}
1387
1388
1389/*
1390 * Catch RedirectError to detect other window manager running
1391 */
1392
1393static int
1394winRedirectErrorHandler (Display *pDisplay, XErrorEvent *pErr)
1395{
1396  redirectError = TRUE;
1397  return 0;
1398}
1399
1400
1401/*
1402 * Check if another window manager is running
1403 */
1404
1405static Bool
1406CheckAnotherWindowManager (Display *pDisplay, DWORD dwScreen)
1407{
1408  redirectError = FALSE;
1409  XSetErrorHandler (winRedirectErrorHandler);
1410  XSelectInput(pDisplay, RootWindow (pDisplay, dwScreen),
1411	       // SubstructureNotifyMask | ButtonPressMask
1412	       ColormapChangeMask | EnterWindowMask | PropertyChangeMask |
1413	       SubstructureRedirectMask | KeyPressMask |
1414	       ButtonPressMask | ButtonReleaseMask);
1415  XSync (pDisplay, 0);
1416  XSetErrorHandler (winMultiWindowXMsgProcErrorHandler);
1417  XSelectInput(pDisplay, RootWindow (pDisplay, dwScreen),
1418	       SubstructureNotifyMask);
1419  XSync (pDisplay, 0);
1420  if (redirectError)
1421    {
1422      //ErrorF ("CheckAnotherWindowManager() - another window manager is running.  Exiting.\n");
1423      return TRUE;
1424    }
1425  else
1426    {
1427      return FALSE;
1428    }
1429}
1430
1431/*
1432 * Notify the MWM thread we're exiting and not to reconnect
1433 */
1434
1435void
1436winDeinitMultiWindowWM ()
1437{
1438  ErrorF ("winDeinitMultiWindowWM - Noting shutdown in progress\n");
1439  g_shutdown = TRUE;
1440}
1441