/*  Part of XPCE --- The SWI-Prolog GUI toolkit

    Author:        Jan Wielemaker and Anjo Anjewierden
    E-mail:        jan@swi.psy.uva.nl
    WWW:           http://www.swi.psy.uva.nl/projects/xpce/
    Copyright (c)  1995-2013, University of Amsterdam
    All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions
    are met:

    1. Redistributions of source code must retain the above copyright
       notice, this list of conditions and the following disclaimer.

    2. Redistributions in binary form must reproduce the above copyright
       notice, this list of conditions and the following disclaimer in
       the documentation and/or other materials provided with the
       distribution.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    POSSIBILITY OF SUCH DAMAGE.
*/

#include "include.h"

static LRESULT WINAPI window_wnd_proc(HWND w, UINT m, WPARAM wP, LPARAM lP);

static int clearing_update;		/* from ws_redraw_window() */
static int invert_window = FALSE;	/* invert the window */

static char *
WinWindowClass()
{ static Name winclassname = NULL;
  static WNDCLASS wndClass;

  if ( !winclassname )
  { char buf[50];

    sprintf(buf, "PceWindow%d", (int)(intptr_t)PceHInstance);
    winclassname = CtoName(buf);

    wndClass.style		= 0/*CS_HREDRAW|CS_VREDRAW*/;
    wndClass.lpfnWndProc	= (LPVOID) window_wnd_proc;
    wndClass.cbClsExtra		= 0;
    wndClass.cbWndExtra		= 0;
    wndClass.hInstance		= PceHInstance;
    wndClass.hIcon		= NULL; /*LoadIcon(NULL, IDI_APPLICATION); */
    wndClass.hCursor		= NULL;
    wndClass.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
    wndClass.lpszMenuName	= NULL;
    wndClass.lpszClassName	= strName(winclassname);

    RegisterClass(&wndClass);
  }

  return strName(winclassname);
}


HPALETTE
window_palette(PceWindow sw)
{ FrameObj fr = getFrameWindow(sw, DEFAULT);

  if ( fr )
    return frame_palette(fr);

  return NULL;
}


static FrameObj
getExistingFrameWindow(PceWindow sw)
{ PceWindow root = (PceWindow) getRootGraphical((Graphical) sw);

  if ( instanceOfObject(root, ClassWindow) &&
       instanceOfObject(root->frame, ClassFrame) )
    answer(root->frame);

  fail;
}

#define WM_PCE_REDRAW (WM_USER+25)

static int WINAPI
do_window_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{ PceWindow sw = getObjectFromHWND(hwnd);
  FrameObj fr;
  WsFrame wfr;
  WsWindow wsw;

  DEBUG(NAME_event,
	Cprintf("%s(0x%04x): MS-Windows event 0x%04x with 0x%04x/0x%08lx\n",
		pp(sw), hwnd, message, wParam, lParam));

  if ( !sw )
    return DefWindowProc(hwnd, message, wParam, lParam);
  wsw = sw->ws_ref;

  switch(message)
  { case WM_PCE_REDRAW:
      pceRedrawWindow(sw);
      return 0;

    case WM_DROPFILES:
    { HDROP hdrop = (HDROP) wParam;
      POINT pt;
      int nfiles;

      if ( DragQueryPoint(hdrop, &pt)  &&
	   (nfiles = DragQueryFile(hdrop, (UINT)-1, NULL, 0)) >= 0 )
      { Chain files;
	Point pos;
	wchar_t buf[PATH_MAX];
	AnswerMark mark;
	int i;

	ServiceMode(is_service_window(sw),
		    { markAnswerStack(mark);
		      files = answerObject(ClassChain, EAV);
		      pos   = answerObject(ClassPoint,
					   toInt(pt.x), toInt(pt.y), EAV);

		      for(i=0; i<nfiles; i++)
		      { int namlen;

			namlen = DragQueryFileW(hdrop, i, buf, PATH_MAX);
			appendChain(files, WCToName(buf, namlen));
		      }

		      DragFinish(hdrop);		/* reclaims memory */

		      send(sw, NAME_dropFiles, files, pos, EAV);
		      RedrawDisplayManager(TheDisplayManager());
		      rewindAnswerStack(mark, NIL);
		    })
      }

      return 0;
    }

    case WM_SIZE:			/* window changed size */
    { int w = LOWORD(lParam);
      int h = HIWORD(lParam);
      Area a = sw->area;
      Int ow = a->w, oh = a->h;

      if ( notNil(sw->device) )		/* subwindow */
      { int p2 = valInt(sw->pen) * 2;

	w += p2;
	h += p2;
      }

      ServiceMode(is_service_window(sw),
		  assign(a, w, toInt(w));
		  assign(a, h, toInt(h));
		  qadSendv(sw, NAME_resize, 0, NULL);
		  changedUnionWindow(sw, a->x, a->y, ow, oh));

      return 0;
    }

    case WM_MOVE:			/* window moved */
    { int x = LOWORD(lParam);
      int y = HIWORD(lParam);

      if ( isNil(sw->device) )		/* Window is not used as graphical */
      { ServiceMode(is_service_window(sw),
		    assign(sw->area, x, toInt(x));
		    assign(sw->area, y, toInt(y)));
      }

      return 0;
    }

    case WM_SHOWWINDOW:
    { if ( !wParam && wsw && wsw->hwnd )
	PceWhDeleteWindow(wsw->hwnd);

      goto cascade;
    }

    case WM_SETFOCUS:
    case WM_KILLFOCUS:
    { BoolObj val = (message == WM_SETFOCUS ? ON : OFF);

      ServiceMode(is_service_window(sw),
		  DEBUG(NAME_focus,
			Cprintf("Received Focus %s on %s\n", pp(val), pp(sw)));
		  send(sw, NAME_inputFocus, val, EAV);
		  RedrawDisplayManager(TheDisplayManager()));
		  /*assign(sw, input_focus, val)*/

      DEBUG(NAME_focus, Cprintf("\tFocus Request Handled!\n"));

      goto cascade;
    }

    case WM_ERASEBKGND:
    { HDC hdc = (HDC) wParam;
      RECT rect;

      d_hdc(hdc, NIL, sw->background, TRUE);
      GetClipBox(hdc, &rect);
      r_clear(rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top);
      d_done();

      return 1;				/* non-zero: I've erased it */
    }

    case WM_PAINT:
    if ( invert_window )		/* see ws_flash_window() */
    { HWND hwnd = getHwndWindow(sw);
      PAINTSTRUCT ps;
      HDC hdc = BeginPaint(hwnd, &ps);

      InvertRect(hdc, &ps.rcPaint);

      EndPaint(hwnd, &ps);
    } else
    { iarea a;

      ServiceMode(is_service_window(sw),
		  DEBUG(NAME_redraw,
			Cprintf("%s (%p) received WM_PAINT (%s clear)\n",
				pp(sw), hwnd,
				clearing_update ? "" : "no"));

		  if ( sw->displayed == OFF )
		    send(sw, NAME_displayed, ON, EAV);

		  if ( d_mswindow(sw, &a, clearing_update) )
		  { DEBUG(NAME_redraw,
			  Cprintf("Redrawing %d %d %d %d\n",
				  a.x, a.y, a.w, a.h));

		    RedrawAreaWindow(sw, &a, clearing_update);
		  } else
		  { DEBUG(NAME_redraw,
			  Cprintf("d_mswindow() failed: empty area\n"));
		  }
		  d_done());
    }
    return 0;

    case WM_DESTROY:
    { HWND hwnd = getHwndWindow(sw);

      if ( hwnd )
      { WNDPROC oproc = wsw->saved_window_procedure;

	DEBUG(NAME_window,
	      Cprintf("WM_DESTROY on %s, hwnd 0x%x\n",
		      pp(sw), hwnd));

	if ( wsw->drop )
	  DragAcceptFiles(hwnd, FALSE);
	PceWhDeleteWindow(hwnd);
	setHwndWindow(sw, 0);
	assocObjectToHWND(hwnd, NIL);

	if ( oproc )			/* refining alien window */
					/* see winHandleWindow() below */
	{ SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) oproc);

	  return CallWindowProc(oproc, hwnd, message, wParam, lParam);
	}

	return 0;
      }

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


    case WM_SETCURSOR:
    { WsWindow w;
      WsFrame wfr;

      if ( (fr = getExistingFrameWindow(sw)) &&
	   (wfr = fr->ws_ref) &&
	   wfr->hbusy_cursor )
      { DEBUG(NAME_busyCursor,
	      Cprintf("Setting busy cursor for %s to %p\n",
		      pp(fr), wfr->hbusy_cursor));
	ZSetCursor(wfr->hbusy_cursor);
      } else
      { if ( (w = sw->ws_ref) )
	{ if ( !w->hcursor )
	    w->hcursor = LoadCursor(NULL, IDC_ARROW);
	  ZSetCursor(w->hcursor);
	}
      }

      return 1;
    }
  }

  if ( (fr = getExistingFrameWindow(sw)) &&
       (wfr = fr->ws_ref) &&
       wfr->hbusy_cursor )
  { ZSetCursor(wfr->hbusy_cursor);
  } else
  { EventObj ev;
    AnswerMark mark;
    status rval = FALSE;
    FrameObj bfr;

    if ( sw->sensitive == OFF )
      goto cascade;

    if ( (bfr=blockedByModalFrame(fr)) )
    { switch(message)
      {	case WM_KEYDOWN:
	case WM_SYSCHAR:
	case WM_CHAR:
	case WM_LBUTTONUP:
	case WM_MBUTTONUP:
	case WM_RBUTTONUP:
	case WM_LBUTTONDOWN:
	case WM_MBUTTONDOWN:
	case WM_RBUTTONDOWN:
	  send(fr, NAME_bell, EAV);
#ifdef WM_MOUSEWHEEL
	case WM_MOUSEWHEEL:
	  send(bfr, NAME_expose, EAV);
#endif
      }

      goto cascade;
    }

    ServiceMode(is_service_window(sw),
		{ markAnswerStack(mark);

		  if ( (ev = messageToEvent(hwnd, message, wParam, lParam)) )
		  { WsWindow w = sw->ws_ref;

		    if ( message != WM_WINENTER && message != WM_WINEXIT )
		      PceEventInWindow(hwnd);

		    addCodeReference(ev);
		    if ( isDownEvent(ev) && !w->capture )
		      SetCapture(hwnd);
		    else if ( isUpEvent(ev) && !w->capture )
		      ReleaseCapture();
		    rval = postNamedEvent(ev, (Graphical) sw, DEFAULT, NAME_postEvent);
		    delCodeReference(ev);
		    freeableObj(ev);
		  }

		  rewindAnswerStack(mark, NIL);

		  if ( ev )		/* rval will not update on failing */
		    RedrawDisplayManager(TheDisplayManager());
		});

    move_big_cursor();			/* only if we have one */

    if ( rval )
      return 0;
  }

cascade:
  if ( wsw && wsw->saved_window_procedure )
    return CallWindowProc(wsw->saved_window_procedure,
			  hwnd, message, wParam, lParam);
  else
    return DefWindowProc(hwnd, message, wParam, lParam);
}


static LRESULT WINAPI
window_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{ int rval;

  if ( InSendMessage() )
  { if ( pceMTTryLock(LOCK_PCE) )
    { rval = do_window_wnd_proc(hwnd, message, wParam, lParam);
      pceMTUnlock(LOCK_PCE);
    } else
    { PceWindow sw = getObjectFromHWND(hwnd);
      DEBUG(NAME_thread,
	    Cprintf("[Thread 0x%x] %s: message 0x%04x: could not lock\n",
		    GetCurrentThreadId(), pp(sw), message));
      return DefWindowProc(hwnd, message, wParam, lParam);
    }
  } else
  { pceMTLock(LOCK_PCE);
    rval = do_window_wnd_proc(hwnd, message, wParam, lParam);
    pceMTUnlock(LOCK_PCE);
  }

  return rval;
}


status
ws_created_window(PceWindow sw)
{ if ( getHwndWindow(sw) )
    succeed;

  fail;
}


void
ws_uncreate_window(PceWindow sw)
{ HWND hwnd;

  if ( (hwnd = getHwndWindow(sw)) )
  { DEBUG(NAME_window,
	  Cprintf("ws_uncreate_window(%s) (=0x%04x)\n", pp(sw), hwnd));
    setHwndWindow(sw, 0);
    assocObjectToHWND(hwnd, NIL);
    PceWhDeleteWindow(hwnd);
    DestroyWindow(hwnd);
    DEBUG(NAME_window,
	  Cprintf("ws_uncreate_window(%s) (=0x%04x) completed\n",
		  pp(sw), hwnd));
  }
}


status
ws_create_window(PceWindow sw, PceWindow parent)
{ HWND hwnd;
  HWND parent_handle;
  DWORD style = WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_VISIBLE;
  DWORD tid;

  DEBUG(NAME_window, Cprintf("ws_create_window(%s %s)\n", pp(sw), pp(parent)));

  if ( isDefault(parent) )		/* window in frame */
  { parent_handle = getHwndFrame(sw->frame);
  } else				/* sub-window */
  { parent_handle = getHwndWindow(parent);
  }

  if ( sw->pen != ZERO )
    style |= WS_BORDER;
  if ( sw->sensitive == OFF )
    style |= WS_DISABLED;

  hwnd = CreateWindow(WinWindowClass(),
		      strName(sw->name),
		      style,
		      valInt(sw->area->x), valInt(sw->area->y),
		      valInt(sw->area->w), valInt(sw->area->h),
		      parent_handle, NULL, PceHInstance, NULL);
  if ( !hwnd )
    return errorPce(sw, NAME_createFailed);

  if ( ThePceThread && (tid=GetCurrentThreadId()) != ThePceThread )
  { int rc = AttachThreadInput(tid, ThePceThread, TRUE);

    DEBUG(NAME_thread, Cprintf("AttachThreadInput(%d, %d) --> %d\n",
			       (int)tid, (int)ThePceThread, rc));
  }

  DEBUG(NAME_window, Cprintf("Windows hwnd = %p\n", hwnd));

  setHwndWindow(sw, hwnd);
  assocObjectToHWND(hwnd, sw);

  if ( hasSendMethodObject(sw, NAME_dropFiles) )
  { WsWindow wsw = sw->ws_ref;

    wsw->drop = TRUE;
    DragAcceptFiles(hwnd, TRUE);
  };

  if ( notDefault(parent) )		/* make a sub-window */
    send(sw, NAME_displayed, ON, EAV);

  succeed;
}


void
ws_manage_window(PceWindow sw)
{
}


void
ws_unmanage_window(PceWindow sw)
{
}


void
ws_reassociate_ws_window(PceWindow from, PceWindow to)
{ HWND win = getHwndWindow(from);

  setHwndWindow(to, win);
  if ( win )
    assocObjectToHWND(win, to);

  setHwndWindow(from, NULL);
}


void
ws_geometry_window(PceWindow sw, int x, int y, int w, int h, int pen)
{ SetWindowPos(getHwndWindow(sw),
	       HWND_TOP,		/* ignored */
	       x, y, w, h,
	       SWP_NOACTIVATE|SWP_NOZORDER);
}


void
ws_topmost_window(PceWindow sw, BoolObj topmost)
{ HWND hwnd;

  if ( (hwnd = getHwndWindow(sw)) )
  {
#if 0					/* somehow doesn't work */
    SetWindowPos(hwnd,
		 topmost == ON ? HWND_TOPMOST : HWND_NOTOPMOST,
		 0, 0, 0, 0,
		 SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
#else
    if ( topmost == ON )
      BringWindowToTop(hwnd);
#endif
  }
}


void
ws_invalidate_window(PceWindow sw, Area a)
{ int clear = FALSE;
  HWND hwnd;

  pceMTLock(LOCK_PCE);
  hwnd = getHwndWindow(sw);
  clearing_update = FALSE;

  if ( hwnd && IsWindowVisible(hwnd) )
  { DEBUG(NAME_redraw, Cprintf("%s: InvalidateRect(%p)\n", pp(sw), hwnd));

    if ( isDefault(a) )
      InvalidateRect(hwnd, NULL, TRUE);
    else				/* actually not used ... */
    { RECT rect;

      rect.left   = valInt(a->x) + valInt(sw->scroll_offset->x);
      rect.right  = rect.left    + valInt(a->w);
      rect.top    = valInt(a->y) + valInt(sw->scroll_offset->y);
      rect.bottom = rect.top     + valInt(a->h);

      InvalidateRect(hwnd, &rect, clear);
    }
  } else
  { DEBUG(NAME_redraw, Cprintf("%s: hwnd %p invisible\n", pp(sw), hwnd));
  }
  pceMTUnlock(LOCK_PCE);
}


Int
ws_window_thread(PceWindow sw)
{ HWND hwnd;

  if ( (hwnd = getHwndWindow(sw)) )
  { DWORD owner = GetWindowThreadProcessId(hwnd, NULL);

    return toInt(owner);
  }

  fail;
}


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Called from pceRedrawWindow(). If the window is owned by another thread,
we post a message to this thread asking for the the redraw.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

int
ws_delayed_redraw_window(PceWindow sw)
{
#ifdef _REENTRANT
  HWND hwnd;

  if ( (hwnd = getHwndWindow(sw)) )
  { DWORD owner = GetWindowThreadProcessId(hwnd, NULL);
    DWORD me = GetCurrentThreadId();

    if ( owner == me )
      return FALSE;

    DEBUG(NAME_thread,
	  Cprintf("Delaying redraw of %s from thread 0x%x (me=0x%x)\n",
		  pp(sw), owner, me));

    PostMessage(hwnd, WM_PCE_REDRAW, 0, 0L);
    return TRUE;
  }
#endif

  return FALSE;
}


void
ws_redraw_window(PceWindow sw, IArea a, int clear)
{ WsWindow wsw = sw->ws_ref;

  if ( wsw && wsw->hwnd && IsWindowVisible(wsw->hwnd) )
  { RECT rect;

    rect.left   = a->x      + valInt(sw->scroll_offset->x);
    rect.right  = rect.left + a->w;
    rect.top    = a->y      + valInt(sw->scroll_offset->y);
    rect.bottom = rect.top  + a->h;

    if ( has_big_cursor() )
      restore_big_cursor_background();
    InvalidateRect(wsw->hwnd, &rect, FALSE);
    clearing_update = clear;
    UpdateWindow(wsw->hwnd);		/* will start WM_PAINT */
    clearing_update = FALSE;		/* ok for normal WM_PAINT call */
    if ( has_big_cursor() )
    { save_big_cursor_background();
      paint_big_cursor();
    }
  } else
  { DEBUG(NAME_redraw, Cprintf("ws_redraw_window(%s): invisible\n", pp(sw)));
  }
}


void
ws_flash_window(PceWindow sw, int msecs)
{ HWND hwnd = getHwndWindow(sw);

  if ( hwnd && sw->displayed == ON )
  { invert_window = TRUE;
    InvalidateRect(hwnd, NULL, FALSE);
    UpdateWindow(hwnd);
    msleep(msecs);
    InvalidateRect(hwnd, NULL, FALSE);
    UpdateWindow(hwnd);
    invert_window = FALSE;
  }
}


void
ws_flash_area_window(PceWindow sw, int x, int y, int w, int h, int msecs)
{ HWND hwnd = getHwndWindow(sw);

  if ( hwnd && sw->displayed == ON )
  { RECT rect;

    rect.left   = x + valInt(sw->scroll_offset->x);
    rect.right  = rect.left + w;
    rect.top    = y + valInt(sw->scroll_offset->y);
    rect.bottom = rect.top  + h;

    invert_window = TRUE;
    InvalidateRect(hwnd, &rect, FALSE);
    UpdateWindow(hwnd);
    msleep(msecs);
    InvalidateRect(hwnd, &rect, FALSE);
    UpdateWindow(hwnd);
    invert_window = FALSE;
  }
}


void
ws_scroll_window(PceWindow sw, int dx, int dy)
{ HWND hwnd;

  if ( (hwnd = getHwndWindow(sw)) && sw->displayed == ON )
    ScrollWindowEx(hwnd, dx, dy, NULL, NULL, NULL, NULL,
		   SW_ERASE|SW_INVALIDATE);
}


void
ws_grab_keyboard_window(PceWindow sw, BoolObj val)
{
}


static void
do_grab_window(PceWindow sw, BoolObj val)
{ HWND win;

  if ( (win = getHwndWindow(sw)) )
  { WsWindow w = sw->ws_ref;

    if ( val == ON )
      SetCapture(win);
    else
      ReleaseCapture();

    w->capture = (val == ON);
  }
}


void
ws_grab_pointer_window(PceWindow sw, BoolObj val)
{ if (  getHwndWindow(sw) )
  { if ( val == ON )
    { if ( getHeadChain(grabbedWindows) != sw )
      { do_grab_window(sw, ON);
	prependChain(grabbedWindows, sw);
      }
    } else
    { do_grab_window(sw, OFF);
      deleteChain(grabbedWindows, sw);
      if ( notNil(grabbedWindows->head) )
        do_grab_window(grabbedWindows->head->value, ON);
    }
  }
}


void
ws_ungrab_all(void)
{ if ( grabbedWindows )
    clearChain(grabbedWindows);

  ReleaseCapture();
}


void
ws_move_pointer(PceWindow sw, int x, int y)
{ if ( ws_created_window(sw) )
  { int ox, oy;
    FrameObj fr;

    if ( frame_offset_window(sw, &fr, &ox, &oy) )
    { x += ox + valInt(fr->area->x);
      y += oy + valInt(fr->area->y);

      SetCursorPos(x, y);
    }
  }
}


void
ws_window_cursor(PceWindow sw, CursorObj c)
{ if ( ws_created_window(sw) )
  { WsWindow w = sw->ws_ref;
    static int cursor_width = 0;
    static int cursor_height = 0;

    if ( !cursor_width )
    { cursor_width  = GetSystemMetrics(SM_CXCURSOR);
      cursor_height = GetSystemMetrics(SM_CYCURSOR);
    }

    exit_big_cursor();			/* should there be one */

    if ( isNil(c) )
      c = getClassVariableValueObject(sw, NAME_cursor);

    if ( notNil(c->image) &&
	 (valInt(c->image->size->w) > cursor_width ||
	  valInt(c->image->size->h) > cursor_height) &&
	 sw->focus_cursor == c )
    { start_big_cursor(c);
      w->hcursor = NULL;
    } else
    { POINT pt;

      w->hcursor = (HCURSOR)getXrefObject(c,
					  getDisplayGraphical((Graphical)sw));

      GetCursorPos(&pt);
      if ( getHwndWindow(sw) == WindowFromPoint(pt) )
	ZSetCursor(w->hcursor);
    }
  }
}


void
ws_window_background(PceWindow sw, Any c)
{ HWND hwnd;

  if ( (hwnd = getHwndWindow(sw)) && sw->displayed == ON )
  { DEBUG(NAME_background, Cprintf("Invalidating %s for clear\n", pp(sw)));
    InvalidateRect(hwnd, NULL, TRUE);
  }
}


void
ws_raise_window(PceWindow sw)
{ ShowWindow(getHwndWindow(sw), SW_SHOW);
}


void
ws_lower_window(PceWindow sw)
{ ShowWindow(getHwndWindow(sw), SW_HIDE);
}


int
ws_enable_window(PceWindow sw, int enable)
{ HWND hwnd;

  if ( (hwnd=getHwndWindow(sw)) )
  { if ( EnableWindow(hwnd, enable) )
      succeed;
  }

  fail;
}


		 /*******************************
		 *     EMBEDDING WINDOWS	*
		 *******************************/

Int
getWinHandleWindow(PceWindow sw)
{ HWND hwnd;

  if ( (hwnd = getHwndWindow(sw)) )
    answer(toInt(hwnd));

  fail;
}


status
winHandleWindow(PceWindow sw, Int handle)
{ HWND hwnd = (HWND)valInt(handle);
  WsWindow w;
  RECT rect;

  while( notNil(sw->decoration) )
    sw = sw->decoration;

  setHwndWindow(sw, hwnd);
  assocObjectToHWND(hwnd, sw);
  w = sw->ws_ref;
  w->saved_window_procedure = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
  SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) window_wnd_proc);
  GetWindowRect(hwnd, &rect);
  ServiceMode(is_service_window(sw),
	      { Area a = sw->area;
		int p = 1;		/* TBD */
		int w = rect.right - rect.left;
		int h = rect.bottom - rect.top;
		int x = rect.left;
		int y = rect.top;
		Int ow = a->w;
		Int oh = a->h;

		x += p;
		y += p;
		w -= 2*p;
		h -= 2*p;
		assign(a, x, toInt(x));
		assign(a, y, toInt(y));
		assign(a, w, toInt(w));
		assign(a, h, toInt(h));
		qadSendv(sw, NAME_resize, 0, NULL);
		changedUnionWindow(sw, a->x, a->y, ow, oh);
	      });

  send(sw, NAME_displayed, ON, EAV);

  succeed;
}
