/*
 *Copyright (C) 1994-2000 The XFree86 Project, Inc. All Rights Reserved.
 *
 *Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 *"Software"), to deal in the Software without restriction, including
 *without limitation the rights to use, copy, modify, merge, publish,
 *distribute, sublicense, and/or sell copies of the Software, and to
 *permit persons to whom the Software is furnished to do so, subject to
 *the following conditions:
 *
 *The above copyright notice and this permission notice shall be
 *included in all copies or substantial portions of the Software.
 *
 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *NONINFRINGEMENT. IN NO EVENT SHALL THE XFREE86 PROJECT BE LIABLE FOR
 *ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 *CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 *WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 *Except as contained in this notice, the name of the XFree86 Project
 *shall not be used in advertising or otherwise to promote the sale, use
 *or other dealings in this Software without prior written authorization
 *from the XFree86 Project.
 *
 * Authors:	Kensuke Matsuzaki
 *		Earle F. Philhower, III
 *		Harold L Hunt II
 */
#ifdef HAVE_XWIN_CONFIG_H
#include <xwin-config.h>
#endif
#include "win.h"
#include <winuser.h>
#define _WINDOWSWM_SERVER_
#include "windowswmstr.h"
#include "dixevents.h"
#include "propertyst.h"
#include <X11/Xatom.h>
#include "winmultiwindowclass.h"
#include "winmsg.h"


/*
 * Constant defines
 */

#define MOUSE_POLLING_INTERVAL		500
#define MOUSE_ACTIVATE_DEFAULT		TRUE
#define RAISE_ON_CLICK_DEFAULT		FALSE


/*
 * Global variables
 */

extern Bool			g_fNoConfigureWindow;
extern Bool			g_fSoftwareCursor;


/*
 * Local globals
 */

static UINT_PTR		g_uipMousePollingTimerID = 0;


/*
 * Local function
 */

DEFINE_ATOM_HELPER(AtmWindowsWmRaiseOnClick, WINDOWSWM_RAISE_ON_CLICK)
DEFINE_ATOM_HELPER(AtmWindowsWMMouseActivate, WINDOWSWM_MOUSE_ACTIVATE)
/* DEFINE_ATOM_HELPER(AtmWindowsWMClientWindow, WINDOWSWM_CLIENT_WINDOW) */

/*
 * ConstrainSize - Taken from TWM sources - Respects hints for sizing
 */
#define makemult(a,b) ((b==1) ? (a) : (((int)((a)/(b))) * (b)) )
static void
ConstrainSize (WinXSizeHints hints, int *widthp, int *heightp)
{
  int minWidth, minHeight, maxWidth, maxHeight, xinc, yinc, delta;
  int baseWidth, baseHeight;
  int dwidth = *widthp, dheight = *heightp;
  
  if (hints.flags & PMinSize)
    {
      minWidth = hints.min_width;
      minHeight = hints.min_height;
    }
  else if (hints.flags & PBaseSize)
    {
      minWidth = hints.base_width;
      minHeight = hints.base_height;
    }
  else
    minWidth = minHeight = 1;
  
  if (hints.flags & PBaseSize)
    {
      baseWidth = hints.base_width;
      baseHeight = hints.base_height;
    } 
  else if (hints.flags & PMinSize)
    {
      baseWidth = hints.min_width;
      baseHeight = hints.min_height;
    }
  else
    baseWidth = baseHeight = 0;

  if (hints.flags & PMaxSize)
    {
      maxWidth = hints.max_width;
      maxHeight = hints.max_height;
    }
  else
    {
      maxWidth = MAXINT;
      maxHeight = MAXINT;
    }

  if (hints.flags & PResizeInc)
    {
      xinc = hints.width_inc;
      yinc = hints.height_inc;
    }
  else
    xinc = yinc = 1;

  /*
   * First, clamp to min and max values
   */
  if (dwidth < minWidth)
    dwidth = minWidth;
  if (dheight < minHeight)
    dheight = minHeight;

  if (dwidth > maxWidth)
    dwidth = maxWidth;
  if (dheight > maxHeight)
    dheight = maxHeight;

  /*
   * Second, fit to base + N * inc
   */
  dwidth = ((dwidth - baseWidth) / xinc * xinc) + baseWidth;
  dheight = ((dheight - baseHeight) / yinc * yinc) + baseHeight;
  
  /*
   * Third, adjust for aspect ratio
   */

  /*
   * The math looks like this:
   *
   * minAspectX    dwidth     maxAspectX
   * ---------- <= ------- <= ----------
   * minAspectY    dheight    maxAspectY
   *
   * If that is multiplied out, then the width and height are
   * invalid in the following situations:
   *
   * minAspectX * dheight > minAspectY * dwidth
   * maxAspectX * dheight < maxAspectY * dwidth
   * 
   */
  
  if (hints.flags & PAspect)
    {
      if (hints.min_aspect.x * dheight > hints.min_aspect.y * dwidth)
        {
	  delta = makemult(hints.min_aspect.x * dheight / hints.min_aspect.y - dwidth, xinc);
	  if (dwidth + delta <= maxWidth)
	    dwidth += delta;
	  else
            {
	      delta = makemult(dheight - dwidth*hints.min_aspect.y/hints.min_aspect.x, yinc);
	      if (dheight - delta >= minHeight)
		dheight -= delta;
            }
        }
      
      if (hints.max_aspect.x * dheight < hints.max_aspect.y * dwidth)
        {
	  delta = makemult(dwidth * hints.max_aspect.y / hints.max_aspect.x - dheight, yinc);
	  if (dheight + delta <= maxHeight)
	    dheight += delta;
	  else
            {
	      delta = makemult(dwidth - hints.max_aspect.x*dheight/hints.max_aspect.y, xinc);
	      if (dwidth - delta >= minWidth)
		dwidth -= delta;
            }
        }
    }
  
  /* Return computed values */
  *widthp = dwidth;
  *heightp = dheight;
}
#undef makemult



/*
 * ValidateSizing - Ensures size request respects hints
 */
static int
ValidateSizing (HWND hwnd, WindowPtr pWin,
		WPARAM wParam, LPARAM lParam)
{
  WinXSizeHints sizeHints;
  RECT *rect;
  int iWidth, iHeight, iTopBorder;
  POINT pt;

  /* Invalid input checking */
  if (pWin==NULL || lParam==0)
    {
      ErrorF ("Invalid input checking\n");
      return FALSE;
    }

  /* No size hints, no checking */
  if (!winMultiWindowGetWMNormalHints (pWin, &sizeHints))
    {
      ErrorF ("No size hints, no checking\n");
      return FALSE;
    }
  
  /* Avoid divide-by-zero */
  if (sizeHints.flags & PResizeInc)
    {
      if (sizeHints.width_inc == 0) sizeHints.width_inc = 1;
      if (sizeHints.height_inc == 0) sizeHints.height_inc = 1;
    }
  
  rect = (RECT*)lParam;
  
  iWidth = rect->right - rect->left;
  iHeight = rect->bottom - rect->top;

  /* Get title bar height, there must be an easier way?! */
  pt.x = pt.y = 0;
  ClientToScreen(hwnd, &pt);
  iTopBorder = pt.y - rect->top;
  
  /* Now remove size of any borders */
  iWidth -= 2 * GetSystemMetrics(SM_CXSIZEFRAME);
  iHeight -= GetSystemMetrics(SM_CYSIZEFRAME) + iTopBorder;

  /* Constrain the size to legal values */
  ConstrainSize (sizeHints, &iWidth, &iHeight);

  /* Add back the borders */
  iWidth += 2 * GetSystemMetrics(SM_CXSIZEFRAME);
  iHeight += GetSystemMetrics(SM_CYSIZEFRAME) + iTopBorder;

  /* Adjust size according to where we're dragging from */
  switch(wParam) {
  case WMSZ_TOP:
  case WMSZ_TOPRIGHT:
  case WMSZ_BOTTOM:
  case WMSZ_BOTTOMRIGHT:
  case WMSZ_RIGHT:
    rect->right = rect->left + iWidth;
    break;
  default:
    rect->left = rect->right - iWidth;
    break;
  }
  switch(wParam) {
  case WMSZ_BOTTOM:
  case WMSZ_BOTTOMRIGHT:
  case WMSZ_BOTTOMLEFT:
  case WMSZ_RIGHT:
  case WMSZ_LEFT:
    rect->bottom = rect->top + iHeight;
    break;
  default:
    rect->top = rect->bottom - iHeight;
    break;
  }
  return TRUE;
}


/*
 * IsRaiseOnClick
 */

static Bool
IsRaiseOnClick (WindowPtr pWin)
{

  struct _Window	*pwin;
  struct _Property	*prop;  
  WindowPtr		pRoot = GetCurrentRootWindow ();

  if (!pWin)
    {
      ErrorF ("IsRaiseOnClick - no prop use default value:%d\n",
	      RAISE_ON_CLICK_DEFAULT);
      return RAISE_ON_CLICK_DEFAULT;
    } 

  pwin = (struct _Window*) pWin;

  if (pwin->optional)
    prop = (struct _Property *) pwin->optional->userProps;
  else
    prop = NULL;

  while (prop)
    {
      if (prop->propertyName == AtmWindowsWmRaiseOnClick ()
	  && prop->type == XA_INTEGER
	  && prop->format == 32)
	{
	  return *(int*)prop->data;
	}
      else
	prop = prop->next;
    }

  if (pWin != pRoot)
    {
      return IsRaiseOnClick (pRoot);
    }
  else
    {
#if CYGMULTIWINDOW_DEBUG
      winDebug ("IsRaiseOnClick - no prop use default value:%d\n",
		RAISE_ON_CLICK_DEFAULT);
#endif
      return RAISE_ON_CLICK_DEFAULT;
    }
}


/*
 * IsMouseActive
 */

static Bool
IsMouseActive (WindowPtr pWin)
{

  struct _Window	*pwin;
  struct _Property	*prop;
  WindowPtr		pRoot = GetCurrentRootWindow ();

  if (!pWin)
    {
      ErrorF ("IsMouseActive - pWin was NULL use default value:%d\n",
	      MOUSE_ACTIVATE_DEFAULT);
      return MOUSE_ACTIVATE_DEFAULT;
    } 

  pwin = (struct _Window*) pWin;

  if (pwin->optional)
    prop = (struct _Property *) pwin->optional->userProps;
  else
    prop = NULL;

  while (prop)
    {
      if (prop->propertyName == AtmWindowsWMMouseActivate ()
	  && prop->type == XA_INTEGER
	  && prop->format == 32)
	{
	  return *(int*)prop->data;
	}
      else
	prop = prop->next;
    }

  if (pWin != pRoot)
    {
      return IsMouseActive (pRoot);
    }
  else
    {
#if CYGMULTIWINDOW_DEBUG
      winDebug ("IsMouseActive - no prop use default value:%d\n",
		MOUSE_ACTIVATE_DEFAULT);
#endif
      return MOUSE_ACTIVATE_DEFAULT;
    }
}


/*
 * winMWExtWMWindowProc - Window procedure
 */

LRESULT CALLBACK
winMWExtWMWindowProc (HWND hwnd, UINT message, 
			    WPARAM wParam, LPARAM lParam)
{
  WindowPtr		pWin = NULL;
  win32RootlessWindowPtr pRLWinPriv = NULL;
  ScreenPtr		pScreen = NULL;
  winPrivScreenPtr	pScreenPriv = NULL;
  winScreenInfo		*pScreenInfo = NULL;
  HWND			hwndScreen = NULL;
  POINT			ptMouse;
  static Bool		s_fTracking = FALSE;
  HDC			hdcUpdate;
  PAINTSTRUCT		ps;
  LPWINDOWPOS		pWinPos = NULL;
  RECT			rcClient;
  winWMMessageRec	wmMsg;
  Bool			fWMMsgInitialized = FALSE;

  /* Check if the Windows window property for our X window pointer is valid */
  if ((pRLWinPriv = (win32RootlessWindowPtr)GetProp (hwnd, WIN_WINDOW_PROP)) != NULL)
    {
      pWin = pRLWinPriv->pFrame->win;
      pScreen				= pWin->drawable.pScreen;
      if (pScreen) pScreenPriv		= winGetScreenPriv(pScreen);
      if (pScreenPriv) pScreenInfo	= pScreenPriv->pScreenInfo;
      if (pScreenPriv) hwndScreen	= pScreenPriv->hwndScreen;

      wmMsg.msg		= 0;
      wmMsg.hwndWindow	= hwnd;
      wmMsg.iWindow	= (Window)pWin->drawable.id;

      wmMsg.iX		= pRLWinPriv->pFrame->x;
      wmMsg.iY		= pRLWinPriv->pFrame->y;
      wmMsg.iWidth	= pRLWinPriv->pFrame->width;
      wmMsg.iHeight	= pRLWinPriv->pFrame->height;

      fWMMsgInitialized = TRUE;
#if CYGDEBUG
      winDebugWin32Message("winMWExtWMWindowProc", hwnd, message, wParam, lParam);

      winDebug ("\thWnd %08X\n", hwnd);
      winDebug ("\tpScreenPriv %08X\n", pScreenPriv);
      winDebug ("\tpScreenInfo %08X\n", pScreenInfo);
      winDebug ("\thwndScreen %08X\n", hwndScreen);
      winDebug ("winMWExtWMWindowProc (%08x) %08x %08x %08x\n",
	      pRLWinPriv, message, wParam, lParam);
#endif
    }
  /* Branch on message type */
  switch (message)
    {
    case WM_CREATE:
#if CYGMULTIWINDOW_DEBUG
      winDebug ("winMWExtWMWindowProc - WM_CREATE\n");
#endif
      /* */
      SetProp (hwnd,
	       WIN_WINDOW_PROP,
	       (HANDLE)((LPCREATESTRUCT) lParam)->lpCreateParams);
      return 0;

    case WM_CLOSE:
#if CYGMULTIWINDOW_DEBUG
      winDebug ("winMWExtWMWindowProc - WM_CLOSE %d\n", pRLWinPriv->fClose);
#endif
      /* Tell window-manager to close window */
      if (pRLWinPriv->fClose)
	{
	  DestroyWindow (hwnd);
	}
      else
	{
	  if (winIsInternalWMRunning(pScreenInfo))
	    {
	      /* Tell our Window Manager thread to kill the window */
	      wmMsg.msg = WM_WM_KILL;
	      if (fWMMsgInitialized)
		winSendMessageToWM (pScreenPriv->pWMInfo, &wmMsg);
	    }
	  winWindowsWMSendEvent(WindowsWMControllerNotify,
				WindowsWMControllerNotifyMask,
				1,
				WindowsWMCloseWindow,
				pWin->drawable.id,
				0, 0, 0, 0);
	}
      return 0;

    case WM_DESTROY:
#if CYGMULTIWINDOW_DEBUG
      winDebug ("winMWExtWMWindowProc - WM_DESTROY\n");
#endif
      /* Free the shaodw DC; which allows the bitmap to be freed */
      DeleteDC (pRLWinPriv->hdcShadow);
      pRLWinPriv->hdcShadow = NULL;
      
      /* Free the shadow bitmap */
      DeleteObject (pRLWinPriv->hbmpShadow);
      pRLWinPriv->hbmpShadow = NULL;
      
      /* Free the screen DC */
      ReleaseDC (pRLWinPriv->hWnd, pRLWinPriv->hdcScreen);
      pRLWinPriv->hdcScreen = NULL;

      /* Free shadow buffer info header */
      free (pRLWinPriv->pbmihShadow);
      pRLWinPriv->pbmihShadow = NULL;
      
      pRLWinPriv->fResized = FALSE;
      pRLWinPriv->pfb = NULL;
      free (pRLWinPriv);
      RemoveProp (hwnd, WIN_WINDOW_PROP);
      break;

    case WM_MOUSEMOVE:
#if CYGMULTIWINDOW_DEBUG && 0
      winDebug ("winMWExtWMWindowProc - WM_MOUSEMOVE\n");
#endif
      /* Unpack the client area mouse coordinates */
      ptMouse.x = GET_X_LPARAM(lParam);
      ptMouse.y = GET_Y_LPARAM(lParam);

      /* Translate the client area mouse coordinates to screen coordinates */
      ClientToScreen (hwnd, &ptMouse);

      /* Screen Coords from (-X, -Y) -> Root Window (0, 0) */
      ptMouse.x -= GetSystemMetrics (SM_XVIRTUALSCREEN);
      ptMouse.y -= GetSystemMetrics (SM_YVIRTUALSCREEN);

      /* We can't do anything without privates */
      if (pScreenPriv == NULL || pScreenInfo->fIgnoreInput)
	break;

      /* Has the mouse pointer crossed screens? */
      if (pScreen != miPointerCurrentScreen ())
	miPointerSetNewScreen (pScreenInfo->dwScreen,
			       ptMouse.x - pScreenInfo->dwXOffset,
			       ptMouse.y - pScreenInfo->dwYOffset);

      /* Are we tracking yet? */
      if (!s_fTracking)
	{
	  TRACKMOUSEEVENT		tme;
	  
	  /* Setup data structure */
	  ZeroMemory (&tme, sizeof (tme));
	  tme.cbSize = sizeof (tme);
	  tme.dwFlags = TME_LEAVE;
	  tme.hwndTrack = hwnd;

	  /* Call the tracking function */
	  if (!(*g_fpTrackMouseEvent) (&tme))
	    ErrorF ("winMWExtWMWindowProc - _TrackMouseEvent failed\n");

	  /* Flag that we are tracking now */
	  s_fTracking = TRUE;
	}
      
      /* Kill the timer used to poll mouse events */
      if (g_uipMousePollingTimerID != 0)
	{
	  KillTimer (pScreenPriv->hwndScreen, WIN_POLLING_MOUSE_TIMER_ID);
	  g_uipMousePollingTimerID = 0;
	}

      /* Deliver absolute cursor position to X Server */
      miPointerAbsoluteCursor (ptMouse.x - pScreenInfo->dwXOffset,
			       ptMouse.y - pScreenInfo->dwYOffset,
			       g_c32LastInputEventTime = GetTickCount ());
      return 0;
      
    case WM_NCMOUSEMOVE:
#if CYGMULTIWINDOW_DEBUG && 0
      winDebug ("winMWExtWMWindowProc - WM_NCMOUSEMOVE\n");
#endif
      /*
       * We break instead of returning 0 since we need to call
       * DefWindowProc to get the mouse cursor changes
       * and min/max/close button highlighting in Windows XP.
       * The Platform SDK says that you should return 0 if you
       * process this message, but it fails to mention that you
       * will give up any default functionality if you do return 0.
       */
      
      /* We can't do anything without privates */
      if (pScreenPriv == NULL || pScreenInfo->fIgnoreInput)
	break;

      /*
       * Timer to poll mouse events.  This is needed to make
       * programs like xeyes follow the mouse properly.
       */
      if (g_uipMousePollingTimerID == 0)
	g_uipMousePollingTimerID = SetTimer (pScreenPriv->hwndScreen,
					     WIN_POLLING_MOUSE_TIMER_ID,
					     MOUSE_POLLING_INTERVAL,
					     NULL);
      break;

    case WM_MOUSELEAVE:
#if CYGMULTIWINDOW_DEBUG
      winDebug ("winMWExtWMWindowProc - WM_MOUSELEAVE\n");
#endif
      /* Mouse has left our client area */

      /* Flag that we are no longer tracking */
      s_fTracking = FALSE;

      /*
       * Timer to poll mouse events.  This is needed to make
       * programs like xeyes follow the mouse properly.
       */
      if (g_uipMousePollingTimerID == 0)
	g_uipMousePollingTimerID = SetTimer (pScreenPriv->hwndScreen,
					     WIN_POLLING_MOUSE_TIMER_ID,
					     MOUSE_POLLING_INTERVAL,
					     NULL);
      return 0;

    case WM_LBUTTONDBLCLK:
    case WM_LBUTTONDOWN:
#if CYGMULTIWINDOW_DEBUG
      winDebug ("winMWExtWMWindowProc - WM_LBUTTONDBLCLK\n");
#endif
      if (pScreenPriv == NULL || pScreenInfo->fIgnoreInput)
	break;
      SetCapture (hwnd);
      return winMouseButtonsHandle (pScreen, ButtonPress, Button1, wParam);
      
    case WM_LBUTTONUP:
#if CYGMULTIWINDOW_DEBUG
      winDebug ("winMWExtWMWindowProc - WM_LBUTTONUP\n");
#endif
      if (pScreenPriv == NULL || pScreenInfo->fIgnoreInput)
	break;
      ReleaseCapture ();
      return winMouseButtonsHandle (pScreen, ButtonRelease, Button1, wParam);

    case WM_MBUTTONDBLCLK:
    case WM_MBUTTONDOWN:
#if CYGMULTIWINDOW_DEBUG
      winDebug ("winMWExtWMWindowProc - WM_MBUTTONDBLCLK\n");
#endif
      if (pScreenPriv == NULL || pScreenInfo->fIgnoreInput)
	break;
      SetCapture (hwnd);
      return winMouseButtonsHandle (pScreen, ButtonPress, Button2, wParam);
      
    case WM_MBUTTONUP:
#if CYGMULTIWINDOW_DEBUG
      winDebug ("winMWExtWMWindowProc - WM_MBUTTONUP\n");
#endif
      if (pScreenPriv == NULL || pScreenInfo->fIgnoreInput)
	break;
      ReleaseCapture ();
      return winMouseButtonsHandle (pScreen, ButtonRelease, Button2, wParam);
      
    case WM_RBUTTONDBLCLK:
    case WM_RBUTTONDOWN:
#if CYGMULTIWINDOW_DEBUG
      winDebug ("winMWExtWMWindowProc - WM_RBUTTONDBLCLK\n");
#endif
      if (pScreenPriv == NULL || pScreenInfo->fIgnoreInput)
	break;
      SetCapture (hwnd);
      return winMouseButtonsHandle (pScreen, ButtonPress, Button3, wParam);
      
    case WM_RBUTTONUP:
#if CYGMULTIWINDOW_DEBUG
      winDebug ("winMWExtWMWindowProc - WM_RBUTTONUP\n");
#endif
      if (pScreenPriv == NULL || pScreenInfo->fIgnoreInput)
	break;
      ReleaseCapture ();
      return winMouseButtonsHandle (pScreen, ButtonRelease, Button3, wParam);

    case WM_XBUTTONDBLCLK:
    case WM_XBUTTONDOWN:
      if (pScreenPriv == NULL || pScreenInfo->fIgnoreInput)
	break;
      SetCapture (hwnd);
      return winMouseButtonsHandle (pScreen, ButtonPress, HIWORD(wParam) + 5, wParam);
    case WM_XBUTTONUP:
      if (pScreenPriv == NULL || pScreenInfo->fIgnoreInput)
	break;
      ReleaseCapture ();
      return winMouseButtonsHandle (pScreen, ButtonRelease, HIWORD(wParam) + 5, wParam);

    case WM_MOUSEWHEEL:
#if CYGMULTIWINDOW_DEBUG
      winDebug ("winMWExtWMWindowProc - WM_MOUSEWHEEL\n");
#endif
      
      /* Pass the message to the root window */
      SendMessage (hwndScreen, message, wParam, lParam);
      return 0;

    case WM_MOUSEACTIVATE:
#if CYGMULTIWINDOW_DEBUG
      winDebug ("winMWExtWMWindowProc - WM_MOUSEACTIVATE\n");
#endif
#if 1
      /* Check if this window needs to be made active when clicked */
      if (winIsInternalWMRunning(pScreenInfo) && pWin->overrideRedirect)
	{
#if CYGMULTIWINDOW_DEBUG
	  winDebug ("winMWExtWMWindowProc - WM_MOUSEACTIVATE - "
		    "MA_NOACTIVATE\n");
#endif

	  /* */
	  return MA_NOACTIVATE;
	}
#endif
      if (!winIsInternalWMRunning(pScreenInfo) && !IsMouseActive (pWin))
	return MA_NOACTIVATE;

      break;

    case WM_KILLFOCUS:
      /* Pop any pressed keys since we are losing keyboard focus */
      winKeybdReleaseKeys ();
      return 0;

    case WM_SYSDEADCHAR:
    case WM_DEADCHAR:
      /*
       * NOTE: We do nothing with WM_*CHAR messages,
       * nor does the root window, so we can just toss these messages.
       */
      return 0;

    case WM_SYSKEYDOWN:
    case WM_KEYDOWN:
#if CYGMULTIWINDOW_DEBUG
      winDebug ("winMWExtWMWindowProc - WM_*KEYDOWN\n");
#endif

      /*
       * Don't pass Alt-F4 key combo to root window,
       * let Windows translate to WM_CLOSE and close this top-level window.
       *
       * NOTE: We purposely don't check the fUseWinKillKey setting because
       * it should only apply to the key handling for the root window,
       * not for top-level window-manager windows.
       *
       * ALSO NOTE: We do pass Ctrl-Alt-Backspace to the root window
       * because that is a key combo that no X app should be expecting to
       * receive, since it has historically been used to shutdown the X server.
       * Passing Ctrl-Alt-Backspace to the root window preserves that
       * behavior, assuming that -unixkill has been passed as a parameter.
       */
      if (wParam == VK_F4 && (GetKeyState (VK_MENU) & 0x8000))
	  break;

      /* Pass the message to the root window */
      SendMessage (hwndScreen, message, wParam, lParam);
      return 0;

    case WM_SYSKEYUP:
    case WM_KEYUP:

#if CYGMULTIWINDOW_DEBUG
      winDebug ("winMWExtWMWindowProc - WM_*KEYUP\n");
#endif

      /* Pass the message to the root window */
      SendMessage (hwndScreen, message, wParam, lParam);
      return 0;

    case WM_HOTKEY:
#if CYGMULTIWINDOW_DEBUG
      winDebug ("winMWExtWMWindowProc - WM_HOTKEY\n");
#endif

      /* Pass the message to the root window */
      SendMessage (hwndScreen, message, wParam, lParam);
      return 0;

    case WM_PAINT:
    
      /* BeginPaint gives us an hdc that clips to the invalidated region */
      hdcUpdate = BeginPaint (hwnd, &ps);

      /* Try to copy from the shadow buffer */
      if (!BitBlt (hdcUpdate,
		   ps.rcPaint.left, ps.rcPaint.top,
		   ps.rcPaint.right - ps.rcPaint.left,
		   ps.rcPaint.bottom - ps.rcPaint.top,
		   pRLWinPriv->hdcShadow,
		   ps.rcPaint.left, ps.rcPaint.top,
		   SRCCOPY))
	{
	  LPVOID lpMsgBuf;
	  
	  /* Display a fancy error message */
	  FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | 
			 FORMAT_MESSAGE_FROM_SYSTEM | 
			 FORMAT_MESSAGE_IGNORE_INSERTS,
			 NULL,
			 GetLastError (),
			 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
			 (LPTSTR) &lpMsgBuf,
			 0, NULL);

	  ErrorF ("winMWExtWMWindowProc - BitBlt failed: %s\n",
		  (LPSTR)lpMsgBuf);
	  LocalFree (lpMsgBuf);
	}

      /* EndPaint frees the DC */
      EndPaint (hwnd, &ps);
      break;

    case WM_ACTIVATE:
#if CYGMULTIWINDOW_DEBUG
      winDebug ("winMWExtWMWindowProc - WM_ACTIVATE\n");
#endif
      if (LOWORD(wParam) != WA_INACTIVE)
	{
	  if (winIsInternalWMRunning(pScreenInfo))
	    {
#if 0
	      /* Raise the window to the top in Z order */
	      wmMsg.msg = WM_WM_RAISE;
	      if (fWMMsgInitialized)
		winSendMessageToWM (pScreenPriv->pWMInfo, &wmMsg);
#endif
	      /* Tell our Window Manager thread to activate the window */
	      wmMsg.msg = WM_WM_ACTIVATE;
	      if (fWMMsgInitialized)
		if (!pWin || !pWin->overrideRedirect) /* for OOo menus */
		  winSendMessageToWM (pScreenPriv->pWMInfo, &wmMsg);
	    }
	  winWindowsWMSendEvent(WindowsWMControllerNotify,
				WindowsWMControllerNotifyMask,
				1,
				WindowsWMActivateWindow,
				pWin->drawable.id,
				0, 0,
				0, 0);
	}
      return 0;

#if 1
    case WM_WINDOWPOSCHANGING:
      pWinPos = (LPWINDOWPOS)lParam;
      if (!(pWinPos->flags & SWP_NOZORDER))
	{
	  if (pRLWinPriv->fRestackingNow || pScreenPriv->fRestacking)
	    {
#if CYGMULTIWINDOW_DEBUG
	      winDebug ("Win %08x is now restacking.\n", (unsigned int)pRLWinPriv);
#endif
	      break;
	    }

	  if (winIsInternalWMRunning(pScreenInfo) || IsRaiseOnClick (pWin))
	    {
#if CYGMULTIWINDOW_DEBUG
	      winDebug ("Win %08x has WINDOWSWM_RAISE_ON_CLICK.\n", (unsigned int)pRLWinPriv);
#endif
	      break;
	    }

#if CYGMULTIWINDOW_DEBUG
	  winDebug ("Win %08x forbid to change z order (%08x).\n",
		    (unsigned int)pRLWinPriv, (unsigned int)pWinPos->hwndInsertAfter);
#endif
	  pWinPos->flags |= SWP_NOZORDER;
	}
      break;
#endif

    case WM_MOVE:
#if CYGMULTIWINDOW_DEBUG
      winDebug ("winMWExtWMWindowProc - WM_MOVE - %d ms\n",
		(unsigned int)GetTickCount ());
#endif
      if (g_fNoConfigureWindow) break;
#if 0
      /* Bail if Windows window is not actually moving */
      if (pRLWinPriv->dwX == (short) LOWORD(lParam)
	  && pRLWinPriv->dwY == (short) HIWORD(lParam))
	break;

      /* Also bail if we're maximizing, we'll do the whole thing in WM_SIZE */
      {
	WINDOWPLACEMENT windPlace;
	windPlace.length = sizeof (WINDOWPLACEMENT);

	/* Get current window placement */
	GetWindowPlacement (hwnd, &windPlace);

	/* Bail if maximizing */
	if (windPlace.showCmd == SW_MAXIMIZE
	    || windPlace.showCmd == SW_SHOWMAXIMIZED)
	  break;
      }
#endif

#if CYGMULTIWINDOW_DEBUG
      winDebug ("\t(%d, %d)\n", (short) LOWORD(lParam), (short) HIWORD(lParam));
#endif
      if (!pRLWinPriv->fMovingOrSizing)
	{
	  if (winIsInternalWMRunning(pScreenInfo))
	    winAdjustXWindow (pWin, hwnd);

	  winMWExtWMMoveXWindow (pWin,
				 (LOWORD(lParam) - wBorderWidth (pWin)
				  - GetSystemMetrics (SM_XVIRTUALSCREEN)),
				 (HIWORD(lParam) - wBorderWidth (pWin)
				  - GetSystemMetrics (SM_YVIRTUALSCREEN)));
	}
      return 0;

    case WM_SHOWWINDOW:
#if CYGMULTIWINDOW_DEBUG || TRUE
      winDebug ("winMWExtWMWindowProc - WM_SHOWWINDOW - %d ms\n",
		(unsigned int)GetTickCount ());
#endif
      /* Bail out if the window is being hidden */
      if (!wParam)
	return 0;

      if (!pScreenInfo->fInternalWM)//XXXX
	return 0;

      winMWExtWMUpdateWindowDecoration (pRLWinPriv, pScreenInfo);

      if (winIsInternalWMRunning(pScreenInfo))
	{
#if CYGMULTIWINDOW_DEBUG || TRUE
	  winDebug ("\tMapWindow\n");
#endif
	  /* Tell X to map the window */
	   MapWindow (pWin, wClient(pWin));

	  if (!pRLWinPriv->pFrame->win->overrideRedirect)
	    /* Bring the Windows window to the foreground */
	    SetForegroundWindow (hwnd);

	  /* Setup the Window Manager message */
	  wmMsg.msg = WM_WM_MAP;
	  wmMsg.iWidth = pRLWinPriv->pFrame->width;
	  wmMsg.iHeight = pRLWinPriv->pFrame->height;

	  /* Tell our Window Manager thread to map the window */
	  if (fWMMsgInitialized)
	    winSendMessageToWM (pScreenPriv->pWMInfo, &wmMsg);
	}
      break;

    case WM_SIZING:
      /* Need to legalize the size according to WM_NORMAL_HINTS */
      /* for applications like xterm */
      return ValidateSizing (hwnd, pWin, wParam, lParam);

    case WM_WINDOWPOSCHANGED:
      {
	pWinPos = (LPWINDOWPOS) lParam;
#if CYGMULTIWINDOW_DEBUG
        winDebug("winMWExtWMWindowProc - WM_WINDOWPOSCHANGED\n");
	winDebug("\tflags: %s%s%s%s%s%s%s%s%s%s%s%s\n",
	(pWinPos->flags & SWP_DRAWFRAME)?"SWP_DRAWFRAME ":"",
	(pWinPos->flags & SWP_FRAMECHANGED)?"SWP_FRAMECHANGED ":"",
	(pWinPos->flags & SWP_HIDEWINDOW)?"SWP_HIDEWINDOW ":"",
	(pWinPos->flags & SWP_NOACTIVATE)?"SWP_NOACTIVATE ":"",
	(pWinPos->flags & SWP_NOCOPYBITS)?"SWP_NOCOPYBITS ":"",
	(pWinPos->flags & SWP_NOMOVE)?"SWP_NOMOVE ":"",
	(pWinPos->flags & SWP_NOOWNERZORDER)?"SWP_NOOWNERZORDER ":"",
	(pWinPos->flags & SWP_NOSIZE)?"SWP_NOSIZE ":"",
	(pWinPos->flags & SWP_NOREDRAW)?"SWP_NOREDRAW ":"",
	(pWinPos->flags & SWP_NOSENDCHANGING)?"SWP_NOSENDCHANGING ":"",
	(pWinPos->flags & SWP_NOZORDER)?"SWP_NOZORDER ":"",
	(pWinPos->flags & SWP_SHOWWINDOW)?"SWP_SHOWWINDOW ":"");
	winDebug("\tno_configure: %s\n", (g_fNoConfigureWindow?"Yes":"No"));
	winDebug("\textend: (%d, %d, %d, %d)\n",
            pWinPos->x, pWinPos->y, pWinPos->cx, pWinPos->cy);

#endif
	if (pWinPos->flags & SWP_HIDEWINDOW) break;

	/* Reorder if window z order was changed */
	if ((pScreenPriv != NULL)
	    && !(pWinPos->flags & SWP_NOZORDER)
	    && !(pWinPos->flags & SWP_SHOWWINDOW)
	    && winIsInternalWMRunning(pScreenInfo))
	  {
#if CYGMULTIWINDOW_DEBUG
	    winDebug ("\twindow z order was changed\n");
#endif
	    if (pWinPos->hwndInsertAfter == HWND_TOP
		||pWinPos->hwndInsertAfter == HWND_TOPMOST
		||pWinPos->hwndInsertAfter == HWND_NOTOPMOST)
	      {
#if CYGMULTIWINDOW_DEBUG
		winDebug ("\traise to top\n");
#endif
		/* Raise the window to the top in Z order */
		wmMsg.msg = WM_WM_RAISE;
		if (fWMMsgInitialized)
		  winSendMessageToWM (pScreenPriv->pWMInfo, &wmMsg);
	      }
#if 1
	    else if (pWinPos->hwndInsertAfter == HWND_BOTTOM)
	      {
	      }
	    else
	      {
		/* Check if this window is top of X windows. */
		HWND hWndAbove = NULL;
		DWORD dwCurrentProcessID = GetCurrentProcessId ();
		DWORD dwWindowProcessID = 0;

		for (hWndAbove = pWinPos->hwndInsertAfter;
		     hWndAbove != NULL;
		     hWndAbove = GetNextWindow (hWndAbove, GW_HWNDPREV))
		  {
		    /* Ignore other XWin process's window */
		    GetWindowThreadProcessId (hWndAbove, &dwWindowProcessID);

		    if ((dwWindowProcessID == dwCurrentProcessID)
			&& GetProp (hWndAbove, WIN_WINDOW_PROP)
			&& !IsWindowVisible (hWndAbove)
			&& !IsIconic (hWndAbove) ) /* ignore minimized windows */
		      break;
		  }
		/* If this is top of X windows in Windows stack,
		   raise it in X stack. */
		if (hWndAbove == NULL)
		  {
#if CYGMULTIWINDOW_DEBUG
		    winDebug ("\traise to top\n");
#endif
		    /* Raise the window to the top in Z order */
		    wmMsg.msg = WM_WM_RAISE;
		    if (fWMMsgInitialized)
		      winSendMessageToWM (pScreenPriv->pWMInfo, &wmMsg);
		  }
	      }
#endif
	  }

	if (!(pWinPos->flags & SWP_NOSIZE)) {
	  if (IsIconic(hwnd)){
#if CYGMULTIWINDOW_DEBUG
	    winDebug ("\tIconic -> MINIMIZED\n");
#endif
	    if (winIsInternalWMRunning(pScreenInfo))
	      {
	      /* Raise the window to the top in Z order */
		wmMsg.msg = WM_WM_LOWER;
		if (fWMMsgInitialized)
		  winSendMessageToWM (pScreenPriv->pWMInfo, &wmMsg);
	      }
	    winWindowsWMSendEvent(WindowsWMControllerNotify,
				  WindowsWMControllerNotifyMask,
				  1,
				  WindowsWMMinimizeWindow,
				  pWin->drawable.id,
				  0, 0, 0, 0);
	  } else if (IsZoomed(hwnd)){
#if CYGMULTIWINDOW_DEBUG
	    winDebug ("\tZoomed -> MAXIMIZED\n");
#endif
	    winWindowsWMSendEvent(WindowsWMControllerNotify,
				  WindowsWMControllerNotifyMask,
				  1,
				  WindowsWMMaximizeWindow,
				  pWin->drawable.id,
				  0, 0, 0, 0);
	  } else {
#if CYGMULTIWINDOW_DEBUG
	    winDebug ("\tnone -> RESTORED\n");
#endif
	    winWindowsWMSendEvent(WindowsWMControllerNotify,
				  WindowsWMControllerNotifyMask,
				  1,
				  WindowsWMRestoreWindow,
				  pWin->drawable.id,
				  0, 0, 0, 0);
	  }
	}
	if (!g_fNoConfigureWindow ) {

	  if (!pRLWinPriv->fMovingOrSizing
	      /*&& (pWinPos->flags & SWP_SHOWWINDOW)*/) {
	    GetClientRect (hwnd, &rcClient);
	    MapWindowPoints (hwnd, HWND_DESKTOP, (LPPOINT)&rcClient, 2);

	    if (!(pWinPos->flags & SWP_NOMOVE)
		&&!(pWinPos->flags & SWP_NOSIZE)) {
#if CYGMULTIWINDOW_DEBUG
	      winDebug ("\tmove & resize\n");
#endif
	      if (winIsInternalWMRunning(pScreenInfo))
                winAdjustXWindow (pWin, hwnd);

	      winMWExtWMMoveResizeXWindow (pWin,
					   rcClient.left - wBorderWidth (pWin)
					   - GetSystemMetrics (SM_XVIRTUALSCREEN),
					   rcClient.top - wBorderWidth (pWin)
					   - GetSystemMetrics (SM_YVIRTUALSCREEN),
					   rcClient.right - rcClient.left
					   - wBorderWidth (pWin)*2,
					   rcClient.bottom - rcClient.top
					   - wBorderWidth (pWin)*2);
	    } else if (!(pWinPos->flags & SWP_NOMOVE)) {
#if CYGMULTIWINDOW_DEBUG
	      winDebug ("\tmove\n");
#endif
	      if (winIsInternalWMRunning(pScreenInfo))
                winAdjustXWindow (pWin, hwnd);

	      winMWExtWMMoveResizeXWindow (pWin,
					   rcClient.left - wBorderWidth (pWin)
					   - GetSystemMetrics (SM_XVIRTUALSCREEN),
					   rcClient.top - wBorderWidth (pWin)
					   - GetSystemMetrics (SM_YVIRTUALSCREEN),
					   rcClient.right - rcClient.left
					   - wBorderWidth (pWin)*2,
					   rcClient.bottom - rcClient.top
					   - wBorderWidth (pWin)*2);
	    } else if (!(pWinPos->flags & SWP_NOMOVE)) {
#if CYGMULTIWINDOW_DEBUG
	      winDebug ("\tmove\n");
#endif
	      if (winIsInternalWMRunning(pScreenInfo))
                winAdjustXWindow (pWin, hwnd); 

	      winMWExtWMMoveXWindow (pWin,
				     rcClient.left - wBorderWidth (pWin)
				     - GetSystemMetrics (SM_XVIRTUALSCREEN),
				     rcClient.top - wBorderWidth (pWin)
				     - GetSystemMetrics (SM_YVIRTUALSCREEN));
	    } else if (!(pWinPos->flags & SWP_NOSIZE)) {
#if CYGMULTIWINDOW_DEBUG
	      winDebug ("\tresize\n");
#endif
	      if (winIsInternalWMRunning(pScreenInfo))
                winAdjustXWindow (pWin, hwnd); 

	      winMWExtWMResizeXWindow (pWin,
				       rcClient.right - rcClient.left
				       - wBorderWidth (pWin)*2,
				       rcClient.bottom - rcClient.top
				       - wBorderWidth (pWin)*2);
	    }
	  }
	}
      }
#if CYGMULTIWINDOW_DEBUG
      winDebug ("winMWExtWMWindowProc - WM_WINDOWPOSCHANGED - done.\n");
#endif
      return 0;

    case WM_SIZE:
      /* see dix/window.c */
      /* FIXME: Maximize/Restore? */
#if CYGMULTIWINDOW_DEBUG
      winDebug ("winMWExtWMWindowProc - WM_SIZE - %d ms\n",
		(unsigned int)GetTickCount ());
#endif
#if CYGMULTIWINDOW_DEBUG
      winDebug ("\t(%d, %d) %d\n", (short) LOWORD(lParam), (short) HIWORD(lParam), g_fNoConfigureWindow);
#endif
      if (g_fNoConfigureWindow) break;

      /* Branch on type of resizing occurring */
      switch (wParam)
	{
	case SIZE_MINIMIZED:
#if CYGMULTIWINDOW_DEBUG
	  winDebug ("\tSIZE_MINIMIZED\n");
#endif
	  if (winIsInternalWMRunning(pScreenInfo))
	    {
	      /* Raise the window to the top in Z order */
	      wmMsg.msg = WM_WM_LOWER;
	      if (fWMMsgInitialized)
		winSendMessageToWM (pScreenPriv->pWMInfo, &wmMsg);
	    }
	  winWindowsWMSendEvent(WindowsWMControllerNotify,
				WindowsWMControllerNotifyMask,
				1,
				WindowsWMMinimizeWindow,
				pWin->drawable.id,
				0, 0,
				LOWORD(lParam), HIWORD(lParam));
	  break;

	case SIZE_RESTORED:
#if CYGMULTIWINDOW_DEBUG
	  winDebug ("\tSIZE_RESTORED\n");
#endif
	  winWindowsWMSendEvent(WindowsWMControllerNotify,
				WindowsWMControllerNotifyMask,
				1,
				WindowsWMRestoreWindow,
				pWin->drawable.id,
				0, 0,
				LOWORD(lParam), HIWORD(lParam));
	  break;

	case SIZE_MAXIMIZED:
#if CYGMULTIWINDOW_DEBUG
	  winDebug ("\tSIZE_MAXIMIZED\n");
#endif
	  winWindowsWMSendEvent(WindowsWMControllerNotify,
				WindowsWMControllerNotifyMask,
				1,
				WindowsWMMaximizeWindow,
				pWin->drawable.id,
				0, 0,
				LOWORD(lParam), HIWORD(lParam));
	  break;
	}

      /* Perform the resize and notify the X client */
      if (!pRLWinPriv->fMovingOrSizing)
	{
	  if (winIsInternalWMRunning(pScreenInfo))
            winAdjustXWindow (pWin, hwnd);

	  winMWExtWMResizeXWindow (pWin,
				   (short) LOWORD(lParam)
				   - wBorderWidth (pWin)*2,
				   (short) HIWORD(lParam)
				   - wBorderWidth (pWin)*2);
	}
      break;

    case WM_ACTIVATEAPP:
#if CYGMULTIWINDOW_DEBUG
      winDebug ("winMWExtWMWindowProc - WM_ACTIVATEAPP - %d ms\n",
		(unsigned int)GetTickCount ());
#endif
      if (wParam)
	{
	  if (winIsInternalWMRunning(pScreenInfo))
	    {
	    }
	  else
	    {
	    }
	  winWindowsWMSendEvent(WindowsWMActivationNotify,
				WindowsWMActivationNotifyMask,
				1,
				WindowsWMIsActive,
				pWin->drawable.id,
				0, 0,
				0, 0);
	}
      else
	{
	  winWindowsWMSendEvent(WindowsWMActivationNotify,
				WindowsWMActivationNotifyMask,
				1,
				WindowsWMIsInactive,
				pWin->drawable.id,
				0, 0,
				0, 0);
	}
      break;

    case WM_SETCURSOR:
      if (LOWORD(lParam) == HTCLIENT)
	{
	  if (!g_fSoftwareCursor) SetCursor (pScreenPriv->cursor.handle);
	  return TRUE;
	}
      break;

    case WM_ENTERSIZEMOVE:
#if CYGMULTIWINDOW_DEBUG
      winDebug ("winMWExtWMWindowProc - WM_ENTERSIZEMOVE - %d ms\n",
		(unsigned int)GetTickCount ());
#endif
      pRLWinPriv->fMovingOrSizing = TRUE;
      break;

    case WM_EXITSIZEMOVE:
#if CYGMULTIWINDOW_DEBUG
      winDebug ("winMWExtWMWindowProc - WM_EXITSIZEMOVE - %d ms\n",
		(unsigned int)GetTickCount ());
#endif
      pRLWinPriv->fMovingOrSizing = FALSE;

      GetClientRect (hwnd, &rcClient);

      MapWindowPoints (hwnd, HWND_DESKTOP, (LPPOINT)&rcClient, 2);

      if (winIsInternalWMRunning(pScreenInfo))
        winAdjustXWindow (pWin, hwnd); 

      winMWExtWMMoveResizeXWindow (pWin,
				   rcClient.left - wBorderWidth (pWin)
				   - GetSystemMetrics (SM_XVIRTUALSCREEN),
				   rcClient.top - wBorderWidth (pWin)
				   - GetSystemMetrics (SM_YVIRTUALSCREEN),
				   rcClient.right - rcClient.left
				   - wBorderWidth (pWin)*2,
				   rcClient.bottom - rcClient.top
				   - wBorderWidth (pWin)*2);
      break;

    case WM_MANAGE:
      ErrorF ("winMWExtWMWindowProc - WM_MANAGE\n");
      break;

    case WM_UNMANAGE:
      ErrorF ("winMWExtWMWindowProc - WM_UNMANAGE\n");
      break;

    default:
      break;
    }

  return DefWindowProc (hwnd, message, wParam, lParam);
}
