WinWorkstation.st
author Claus Gittinger <cg@exept.de>
Sat, 15 Sep 2001 16:06:46 +0200
changeset 3497 2d43fa327c07
parent 3496 bc0065e67f3f
child 3498 890cfcba79a5
permissions -rw-r--r--
*** empty log message ***

"
COPYRIGHT (c) 1996 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"

DeviceWorkstation subclass:#WinWorkstation
	instanceVariableNames:'blackpixel whitepixel listOfFonts rootWin rootDC buttonsPressed
		eventTrace eventBuffer isWin95'
	classVariableNames:'BeepDuration NativeWindows NativeWindowClassTable
		StandardColorValues IgnoreSysColorChanges IgnoreFontChanges
		SystemColorValues ClipBoardObject'
	poolDictionaries:''
	category:'Interface-Graphics'
!

!WinWorkstation primitiveDefinitions!
%{

#define COUNT_RESOURCES       /* */
#define COUNT_BMP_RESOURCES   /* */
#define DEBUG_DELETEOBJECT    /* */
#define DEBUG_DC_REUSE    /* */

#define ADJUSTWINDOW
#define ALWAYSTRUECOLOR
#define WIN32THREADS
/* #define DEBUGMASK            /* */
#define SET_FOCUS_IN_WINTHREAD
#define SET_CURSOR_IN_WINTHREAD
#define DELAY_ENTER_LEAVE_WHILE_IN_SIZE_MOVE /* */
/* #define BEEP_IN_WINTHREAD    /* */
/* #define KEEP_STD_CURSORS    /* not useful - those are allocated only once, in any case */
/* #define HANDLE_VIEWGRAVITY  /* not yet working */

#define LATE_WM_PAINT                   /* fill-bg in ST/X thread (instead of in event-thread) */
					/* seems to be needed to avoid DC conflicts */

#define CACHE_LAST_DC                   /* remember last DC in gcData */
#define CACHE_LAST_PEN                  /* remember last pen in gcData */
#define CACHE_LAST_BRUSH                /* remember last brush in gcData */
#define CACHE_LAST_WM_PAINT_BRUSH       /* remember last viewBackground brush */
#define CACHE_LAST_WM_PAINT_DC          /* remember last viewBackground paints dc */
#define CACHE_LAST_TMP_FONT
#define NUM_PEN_CACHED  8               /* that many solid pens are globally remembered */
#define NUM_BRUSH_CACHED 8              /* that many brushes are globally remembered */

#undef INT
#define INT WIN_INT
#undef Array
#define Array WIN_Array
#undef Number
#define Number WIN_Number
#undef Method
#define Method WIN_Method
#undef Point
#define Point WIN_Point
#undef Rectangle
/* #define Rectangle WIN_Rectangle*/
#undef True
#define True WIN_True
#undef False
#define False WIN_False
#undef Block
#define Block WIN_Block
#undef Context
#define Context WIN_Context
#undef Date
#define Date WIN_Date
#undef Time
#define Time WIN_Time
#undef Delay
#define Delay WIN_Delay
#undef Signal
#define Signal WIN_Signal
#undef Set
#define Set WIN_Set

#include <stdio.h>
/* #include <malloc.h> */
/* #include <math.h> */
/* #include <string.h> */

/*#include <stdarg.h>   */

#ifdef __BORLANDC__
# define NOATOM
# define NOGDICAPMASKS
# define NOMETAFILE
# define NOMINMAX
# define NOOPENFILE
# define NOSOUND
# define NOWH
# define NOCOMM
# define NOKANJI
# define NOCRYPT
# define NOMCX
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include <shellapi.h>
# include <sys\timeb.h>
# include <dir.h>
#else
# define _USERENTRY /**/
# define NOATOM
# define NOGDICAPMASKS
# define NOMETAFILE
# define NOMINMAX
# define NOOPENFILE
# define NOSOUND
# define NOWH
# define NOCOMM
# define NOKANJI
# define NOCRYPT
# define NOMCX
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include <sys\timeb.h>
#endif

#include <process.h>

#ifdef __DEF_Array
# undef Array
# define Array __DEF_Array
#endif
#ifdef __DEF_Number
# undef Number
# define Number __DEF_Number
#endif
#ifdef __DEF_Method
# undef Method
# define Method __DEF_Method
#endif
#ifdef __DEF_Point
# undef Point
# define Point __DEF_Point
#endif
#ifdef __DEF_Block
# undef Block
# define Block __DEF_Block
#endif
#ifdef __DEF_Context
# undef Context
# define Context __DEF_Context
#endif
#ifdef __DEF_Date
# undef Date
# define Date __DEF_Date
#endif
#ifdef __DEF_Time
# undef Time
# define Time __DEF_Time
#endif
# ifdef __DEF_Set
#  undef Set
#  define Set __DEF_Set
# endif
# ifdef __DEF_Signal
#  undef Signal
#  define Signal __DEF_Signal
# endif
# ifdef __DEF_Delay
#  undef Delay
#  define Delay __DEF_Delay
# endif
#undef INT

#define INT int

/*
 * some defines - tired of typing ...
 */
#define _HANDLEVal(o)        (HANDLE)(__MKCP(o))
#define _HBITMAPVAL(o)       (HBITMAP)(__MKCP(o))
#define _HWNDVal(o)          (HWND)(__MKCP(o))
#define _HPALETTEVal(o)      (HPALETTE)(__MKCP(o))
#define _HCURSORVal(o)       (HCURSOR)(__MKCP(o))
#define _HGDIOBJVal(o)       (HGDIOBJ)(__MKCP(o))
#define _LOGPALETTEVal(o)    (LOGPALETTE *)(__MKCP(o))
#define _COLORREFVal(o)      (COLORREF)(__MKCP(o))

#define _GCDATA(o)           ((struct gcData *)(__MKCP(o)))

#define WIDECHAR unsigned char

#ifdef LATER
  /* for multi-WIN display support, we need this as an instance var (as below)
   * however, there are many more places, where a single static datum is
   * used currently - therefore dont care for this - at least now.
   */
# define __rootDC             (HDC)(__MKCP(__INST(rootDC)))
#endif

#define ISCONNECTED         1

struct gcData {
    union {
	HDC         __hDC;
	struct gcData *__nextFree;
    } u;
    HWND        hWnd;
    HBITMAP     hBitmap;
    HBITMAP     save_hBitmap;
    HBITMAP     hMask;
    short       clipX;
    short       clipY;
    short       clipW;
    short       clipH;
    char        clipping;
    char        clipByChildren;
    char        bkMode;
    char        bitmapColorBitCount;
    short       maskOrgX;
    short       maskOrgY;
    short       lineWidth;
    short       fontAscent;
    int         lStyle;   /* is lineStyle | joinStyle | capStyle */
    int         rop2;
    int         bitbltrop2;
    COLORREF    fgColor;
    COLORREF    bgColor;
    HFONT       hFont;
    HFONT       save_hFont;
    HPEN        hPen;
    HPEN        save_hPen;
    HBRUSH      hBrush;
    HBRUSH      save_hBrush;
};
#define _hDC u.__hDC

/*
 * some synthetic values
 */
/*****************************************************************
 * EVENT DEFINITIONS
 *****************************************************************/

/* Input Event Masks. Used as event-mask window attribute and as arguments
   to Grab requests.  Not to be confused with event names.  */

#define NoEventMask                     0L
#define KeyPressMask                    (1L<<0)
#define KeyReleaseMask                  (1L<<1)
#define ButtonPressMask                 (1L<<2)
#define ButtonReleaseMask               (1L<<3)
#define EnterWindowMask                 (1L<<4)
#define LeaveWindowMask                 (1L<<5)
#define PointerMotionMask               (1L<<6)
#define PointerMotionHintMask           (1L<<7)
#define Button1MotionMask               (1L<<8)
#define Button2MotionMask               (1L<<9)
#define Button3MotionMask               (1L<<10)
#if 0
# define Button4MotionMask               (1L<<11)
# define Button5MotionMask               (1L<<12)
#endif
#define ButtonMotionMask                (1L<<13)
#define KeymapStateMask                 (1L<<14)
#define ExposureMask                    (1L<<15)
#define VisibilityChangeMask            (1L<<16)
#define StructureNotifyMask             (1L<<17)
#define ResizeRedirectMask              (1L<<18)
#define SubstructureNotifyMask          (1L<<19)
#define SubstructureRedirectMask        (1L<<20)
#define FocusChangeMask                 (1L<<21)
#define PropertyChangeMask              (1L<<22)
#define ColormapChangeMask              (1L<<23)
#define OwnerGrabButtonMask             (1L<<24)

/* Event names.  Used in "type" field in XEvent structures.  Not to be
confused with event masks above.  They start from 2 because 0 and 1
are reserved in the protocol for errors and replies. */

#define KeyPress                2
#define KeyRelease              3
#define ButtonPress             4
#define ButtonRelease           5
#define MotionNotify            6
#define EnterNotify             7
#define LeaveNotify             8
#define FocusIn                 9
#define FocusOut                10
#define KeymapNotify            11
#define Expose                  12
#define GraphicsExpose          13
#define NoExpose                14
#define VisibilityNotify        15
#define CreateNotify            16
#define DestroyNotify           17
#define UnmapNotify             18
#define MapNotify               19
#define MapRequest              20
#define ReparentNotify          21
#define ConfigureNotify         22
#define ConfigureRequest        23
#define GravityNotify           24
#define ResizeRequest           25
#define CirculateNotify         26
#define CirculateRequest        27
#define PropertyNotify          28
#define SelectionClear          29
#define SelectionRequest        30
#define SelectionNotify         31
#define ColormapNotify          32
#define ClientMessage           33
#define MappingNotify           34
#define LASTEvent               35      /* must be bigger than any event # */

/* Key masks. Used as modifiers to GrabButton and GrabKey, results of QueryPointer,
   state in various key-, mouse-, and button-related events. */

#define ShiftMask               (1<<0)
#define LockMask                (1<<1)
#define ControlMask             (1<<2)
#define Mod1Mask                (1<<3)
#define Mod2Mask                (1<<4)
#define Mod3Mask                (1<<5)
#define Mod4Mask                (1<<6)
#define Mod5Mask                (1<<7)

/* modifier names.  Used to build a SetModifierMapping request or
   to read a GetModifierMapping request.  These correspond to the
   masks defined above. */
#define ShiftMapIndex           0
#define LockMapIndex            1
#define ControlMapIndex         2
#define Mod1MapIndex            3
#define Mod2MapIndex            4
#define Mod3MapIndex            5
#define Mod4MapIndex            6
#define Mod5MapIndex            7


/* button masks.  Used in same manner as Key masks above. Not to be confused
   with button names below. */

#define Button1Mask            Button1MotionMask
#define Button2Mask            Button2MotionMask
#define Button3Mask            Button3MotionMask
#if 0
# define Button4Mask            Button4MotionMask
# define Button5Mask            Button5MotionMask
#endif
#define AnyButtonMask           (Button1Mask|Button2Mask|Button3Mask)

#if 0
# define AnyModifier             (1<<15)  /* used in GrabButton, GrabKey */
#endif

/* button names. Used as arguments to GrabButton and as detail in ButtonPress
   and ButtonRelease events.  Not to be confused with button masks above.
   Note that 0 is already defined above as "AnyButton".  */

#define Button1                 1
#define Button2                 2
#define Button3                 3
#if 0
# define Button4                 4
# define Button5                 5
#endif

#define CAPTURE_NONE     0
#define CAPTURE_IMPLICIT 7
#define CAPTURE_EXPLICIT 8

#define BK_UNDEF         0
#define BK_TRANSPARENT   1
#define BK_OPAQUE        2

#define GRAVITY_NONE        0
#define GRAVITY_S           1
#define GRAVITY_SW          2
#define GRAVITY_SE          3
#define GRAVITY_N           4
#define GRAVITY_NW          5
#define GRAVITY_NE          6
#define GRAVITY_E           7
#define GRAVITY_W           8

/*#define ControlMask            8
#define ShiftMask              16*/
#define LeftAltMask            Mod1Mask
#define RightAltMask           Mod2Mask
#define TRANSLATED_KEY         Mod5Mask

#define WIN32PADDING 32
/*
 * synthetic events
 */
#define __WM_MOUSEENTER        0x10001
#define __WM_MOUSELEAVE        0x10002
#define __WM_GEXPOSE           0x10003
#define __WM_NOGEXPOSE         0x10004
#define __WM_ICONIFIED         0x10005

static int AltMask = RightAltMask;
static int MetaMask = LeftAltMask;

#define WhitePixel     RGB(0xFF, 0xFF, 0xFF)
#define BlackPixel     RGB(0, 0, 0)

/* #undef DEBUG /* */
/* #define DEBUG_WM_MOUSEENTER  /* */
/* #define DEBUG_WM_MOUSELEAVE  /* */

#ifdef DEBUG
# define PRINTF(x)              { printf x;}
# define CPRINTF(x)             { if (__debug__ > 1) printf x;}
# define RESPRINTF(x)           /*{ if (__debug__) printf x;}*/
# define RES1PRINTF(x)          /*{ if (__debug__) printf x;}*/
# define RES_BMP_PRINTF(x)      /*{ if (__debug__) printf x;}*/
# define TH_DPRINTF(x)          /*{ if (__debug__) printf x;}*/
# define DPRINTF(x)             { if (__debug__) printf x;}
# define DDPRINTF(x)            { if (__debug__ > 1) printf x;}
# define EVENT_PRINTF(x)        { if (__debug__) printf x;}
# define EVENT_PRINTF2(x)       { if (__debug__ > 1) printf x;}
# define EVENT_PRINTF3(x)       /* */
# define UNHANDLED_EVENT_PRINTF(x)  { if (__debug__) printf x;}
#else
# define PRINTF(x)              /* */
# define CPRINTF(x)             /* */
# define RES_BMP_PRINTF(x)      /* */
# define RESPRINTF(x)           /* */
# define RES1PRINTF(x)          /* */
# define TH_DPRINTF(x)          /* */
# define DPRINTF(x)             /* */
# define DDPRINTF(x)            /* */
# define EVENT_PRINTF(x)        /* */
# define EVENT_PRINTF2(x)       /* */
# define EVENT_PRINTF3(x)       /* */
# define UNHANDLED_EVENT_PRINTF(x)        /* */
#endif

#define INFOPRINT(__x__)                 \
    if (@global(InfoPrinting) == true) { \
	fprintf __x__;           \
    }

#define DEBUGPRINT(__x__)                 \
    if (@global(InfoPrinting) == true) { \
	fprintf __x__;           \
    }

#define BR_SOLID       0
#define BR_PATTERN     1

#define LINE_SOLID     0
#define LINE_DASH      1
#define LINE_DDASH     2

#ifdef KEEP_STD_CURSORS
# define _C_ARROW       1
# define _C_CROSS       2
# define _C_IBEAM       3
# define _C_ICON        4
# define _C_NO          5
# define _C_SIZE        6
# define _C_SIZEALL     7
# define _C_SIZENESW    8
# define _C_SIZENS      9
# define _C_SIZENWSE    10
# define _C_UPARROW     11
# define _C_WAIT        12

 static HCURSOR H_C_ARROW = (HCURSOR)0;
 static HCURSOR H_C_CROSS = (HCURSOR)0;
 static HCURSOR H_C_IBEAM = (HCURSOR)0;
 static HCURSOR H_C_ICON = (HCURSOR)0;
 static HCURSOR H_C_NO = (HCURSOR)0;
 static HCURSOR H_C_SIZE = (HCURSOR)0;
 static HCURSOR H_C_SIZEALL = (HCURSOR)0;
 static HCURSOR H_C_SIZENESW = (HCURSOR)0;
 static HCURSOR H_C_SIZENS = (HCURSOR)0;
 static HCURSOR H_C_SIZENWSE = (HCURSOR)0;
 static HCURSOR H_C_UPARROW = (HCURSOR)0;
 static HCURSOR H_C_WAIT = (HCURSOR)0;
 static HCURSOR H_C_APPSTARTING = (HCURSOR)0;
#endif /* KEEP_STD_CURSORS */

#ifdef EXIT_WITH_3_CTRL_Cs
static int ctrl_c_count = 0;            /* CTRL-C count */
#endif
#ifdef RELEASE_CAPTURE_WITH_3_ESCAPEs
static int escape_count = 0;            /* ESCAPE count */
#endif

static int evRootX, evRootY;
static int multiClickState;
static UINT lastMSGTime;
static UINT lastMouseWheelTime = 0;
static int lastClickX, lastClickY;
static int deltaDoubleClickX = -999;
static int deltaDoubleClickY = -999;
static UINT nextMultiClickTime;
static int inSizeMove = 0;
static int inSize = 0;
static int inMove = 0;
static HWND needDelayedMouseLeaveWindow = 0;
static HWND needDelayedMouseEnterWindow = 0;
static int delayedMouseEnterX, delayedMouseEnterY;
static int destroyWin;
static HDC   __tmpDC;
#ifdef CACHE_LAST_TMP_FONT
static HFONT __tmpDC_hfont = 0, __tmpDC_prev_hfont = 0;
#endif
static int __isWinNT = 0;
static int __logPixelSY;

#ifdef CACHE_LAST_DC
static struct gcData *lastGcData = 0;
static int            lastGcOwnerThreadID = 0;
static HWND           lastGcHWIN;
static HBITMAP        lastGcHBITMAP;
#endif

#ifndef __rootDC
 static HDC __rootDC;
#endif

#ifdef NUM_PEN_CACHED
struct __penCache {
    HPEN            pen;
    int             clr;
} __penCache[NUM_PEN_CACHED];
#endif

#ifdef NUM_BRUSH_CACHED
struct __brushCache {
    HBRUSH          brush;
    int             clr;
} __brushCache[NUM_BRUSH_CACHED];
#endif

/*
 * globally cached - never released
 */
static HPEN __blackPen = 0;
static HPEN __whitePen = 0;
static HPEN __nullPen = 0;
static HBRUSH __blackBrush = 0;
static HBRUSH __whiteBrush = 0;

static HPEN __bgPen = 0;
static int __bgPenColor = 0;

#define WM_THREAD_CREATEWINDOW   WM_USER + 0x101
#define WM_THREAD_DESTROYWINDOW  WM_USER + 0x102
#define WM_THREAD_SETFOCUS       WM_USER + 0x103
#define WM_THREAD_SETCAPTURE     WM_USER + 0x104
#define WM_THREAD_SETCURSOR      WM_USER + 0x105
#define WM_THREAD_BEEP           WM_USER + 0x106

#define hasEventQueued() (eventQueueHead != NULL)
#define EVENT_THREAD_STACKSIZE  (4096*4)

typedef int (*intf)(int);

/*
 * keep the windows cursor and others local in the HWND struct.
 */
#define LI_TOPWIN       0x0001
#define LI_INPUTWIN     0x0002

typedef struct {
	HCURSOR  hCursor;
	HBRUSH   viewBgBrush;  /* if that is nil, it has a solid bg color */
	COLORREF viewBgColor;
	short    flag;
	short    iconified;
	short    minWidth;
	short    maxWidth;
	short    minHeight;
	short    maxHeight;
	int      eventMask;
	short    bw;
	short    viewGravity;
	short    bgOffsX;      /* bg-pattern offset */
	short    bgOffsY;      /* bg-pattern offset */
	COLORREF bdColor;
} localWindowInfo;
typedef localWindowInfo *plocalWindowInfo;

typedef struct createWindowInfo {
	localWindowInfo *localWindowInfo;  /* in param */
	HANDLE          newWinHandle;      /* out param */
	char            *windowName;       /* in params */
	char            *className;
	int             winStyleBits;
	int             winStyleBitsEx;
	HANDLE          parentHandle;
	int             x;
	int             y;
	int             dx;
	int             dy;
	HANDLE          hCreateEvent;
	short           infoWasRead;
	unsigned short  sequenceNr;
} createWindowInfo;

#define INVALIDATED_CWI 0xFFFF             /* seqNr for invalidated CWI */

static createWindowInfo *pendingCREATEWINDOWInfo = 0;
static unsigned short pendingSequenceNr = INVALIDATED_CWI;

#define N_WINDOW_PRIVATE (sizeof(plocalWindowInfo))

#define GETLOCALWINDOWINFOPTR(__hWnd__) \
	((localWindowInfo *)GetWindowLong(__hWnd__, 0))

#define SETLOCALWINDOWINFOPTR(__hWnd__,__ptr__) \
	SetWindowLong(__hWnd__, 0 , (DWORD)__ptr__)

#define SetWindow_Cursor(__hWnd__, __hCurs__) \
	(GETLOCALWINDOWINFOPTR(__hWnd__) ? (GETLOCALWINDOWINFOPTR(__hWnd__)->hCursor = __hCurs__) : 0)

#define GetWindow_Cursor(__hWnd__) \
	(GETLOCALWINDOWINFOPTR(__hWnd__) ? GETLOCALWINDOWINFOPTR(__hWnd__)->hCursor : 0)

#define SetWindow_viewBgColor(__hWnd__, __viewBgColor__) \
	(GETLOCALWINDOWINFOPTR(__hWnd__) ? (GETLOCALWINDOWINFOPTR(__hWnd__)->viewBgColor = __viewBgColor__) : 0)

#define GetWindow_viewBgColor(__hWnd__) \
	(GETLOCALWINDOWINFOPTR(__hWnd__) ? GETLOCALWINDOWINFOPTR(__hWnd__)->viewBgColor : 0)

#define SetWindow_bdColor(__hWnd__, __bdColor__) \
	(GETLOCALWINDOWINFOPTR(__hWnd__) ? (GETLOCALWINDOWINFOPTR(__hWnd__)->bdColor = __bdColor__) : 0)

#define GetWindow_bdColor(__hWnd__) \
	(GETLOCALWINDOWINFOPTR(__hWnd__) ? GETLOCALWINDOWINFOPTR(__hWnd__)->bdColor : 0)

#define SetWindow_iconified(__hWnd__, __iconified__) \
	(GETLOCALWINDOWINFOPTR(__hWnd__) ? (GETLOCALWINDOWINFOPTR(__hWnd__)->iconified = __iconified__) : 0)

#define GetWindow_iconified(__hWnd__) \
	(GETLOCALWINDOWINFOPTR(__hWnd__) ? GETLOCALWINDOWINFOPTR(__hWnd__)->iconified : 0)

#define SetWindow_viewBgBrush(__hWnd__, __viewBgBrush__) \
	(GETLOCALWINDOWINFOPTR(__hWnd__) ? (GETLOCALWINDOWINFOPTR(__hWnd__)->viewBgBrush = __viewBgBrush__) : 0)

#define GetWindow_viewBgBrush(__hWnd__) \
	(GETLOCALWINDOWINFOPTR(__hWnd__) ? GETLOCALWINDOWINFOPTR(__hWnd__)->viewBgBrush : 0)

#define SetWindow_bw(__hWnd__, __bw__) \
	(GETLOCALWINDOWINFOPTR(__hWnd__) ? (GETLOCALWINDOWINFOPTR(__hWnd__)->bw = __bw__) : 0)

#define GetWindow_bw(__hWnd__) \
	(GETLOCALWINDOWINFOPTR(__hWnd__) ? GETLOCALWINDOWINFOPTR(__hWnd__)->bw : 0)

#define GetWindow_paintInfoPtr(__hWnd__) \
	(GETLOCALWINDOWINFOPTR(__hWnd__) ? &(GETLOCALWINDOWINFOPTR(__hWnd__)->paintInfo) : 0)

#define GetWindow_sizeInfoPtr(__hWnd__) \
	(GETLOCALWINDOWINFOPTR(__hWnd__) ? &(GETLOCALWINDOWINFOPTR(__hWnd__)->sizeInfo) : 0)

#define SetWindow_flag(__hWnd__, __flag__) \
	(GETLOCALWINDOWINFOPTR(__hWnd__) ? (GETLOCALWINDOWINFOPTR(__hWnd__)->flag = __flag__) : 0)

#define GetWindow_flag(__hWnd__) \
	(GETLOCALWINDOWINFOPTR(__hWnd__) ? GETLOCALWINDOWINFOPTR(__hWnd__)->flag : 0)

#define GetWindow_eventMask(__hWnd__) \
	(GETLOCALWINDOWINFOPTR(__hWnd__) ? (GETLOCALWINDOWINFOPTR(__hWnd__)->eventMask) : 0)

struct queuedEvent {
	struct queuedEvent     *ev_next;
	int                     count;
	int                     ev_flag;
	HWND                    ev_hWnd;
	UINT                    ev_message;
	UINT                    ev_wParam;
	int                     ev_arg1;
	int                     ev_arg2;
	int                     ev_arg3;
	int                     ev_arg4;
	UINT                    ev_time;
};

#define ev_x    ev_arg1
#define ev_y    ev_arg2
#define ev_w    ev_arg3
#define ev_h    ev_arg4

#define ev_keyCode   ev_wParam
#define ev_scanCode  ev_arg3
#define ev_modifiers ev_arg4

#define EV_CHUNK_CNT    10000
#define EV_CHUNK_SZ     (EV_CHUNK_CNT*sizeof(struct queuedEvent))

#define BITBLT_COPY             SRCCOPY
#define BITBLT_COPYINVERTED     NOTSRCCOPY
#define BITBLT_XOR              SRCINVERT
#define BITBLT_AND              SRCAND
#define BITBLT_OR               SRCPAINT

%}
! !

!WinWorkstation primitiveVariables!
%{

static int firstInstance = 1;
static char *app_name = "ST/X";
static char *app_nameRoot = "ST/X:Root";
static char *app_namePopup = "ST/X:Popup";
static int __debug__ = 0;
static int __depth;
static int __realDepth;
static HWND __rootWin = NULL;
static HWND __rootWinSpezial = NULL;
#if 0
static HDESK __rootDesk;
#endif

static int __activateOnClick = 1;
static int __focusFollowsMouse = 0;
static int __eatingMouseEvents = 0;
static int __ignoreButtonPressOnActivate = 0;
static int __rightButtonIsLowerWindow = 1;
static int __shiftedLeftButtonIsLowerWindow = 1;
static int __currentCapture = CAPTURE_NONE;

static HANDLE __currentPointerView = (HANDLE)0;

/* MessageDispatchThread */

#ifndef WIN32THREADS
static DWORD _masterThreadId;
static HANDLE _masterThread;
#endif
static DWORD _dispatchThreadId = 0;
#ifdef LOCK_DEBUG
static int lockcountQ = 0;
static int lockcountF = 0;
#endif
static HANDLE hEventQMutex;
static HANDLE hEventFreeListMutex;
#ifdef USE_PAINT_MUTEX
static HANDLE hPaintMutex;
#endif
static HANDLE hCreateEvent;
static HANDLE hNeverTriggered;
static HANDLE hInputEvent;

#ifdef COUNT_RESOURCES
 static int __cnt_gcData;
 static int __cnt_createWindows;
 static int __cnt_cur;
 static int __cnt_font;
#endif
#ifdef COUNT_BMP_RESOURCES
 static int __cnt_bitmap;
#endif

static struct queuedEvent *eventFreeList = (struct queuedEvent *)0;

/*
 * up to NUMQUICKFREE events can be allocated and freed without
 * a need to access the Mutex
 */
#define NUMQUICKFREE_EV    32
static struct queuedEvent *quickFreeEvent[NUMQUICKFREE_EV];
static struct queuedEvent *eventQueueHead = (struct queuedEvent *)0;
static struct queuedEvent *eventQueueTail = (struct queuedEvent *)0;
static int eventsendcount = 0;
static int eventempfcount = 0;

/*
 * up to NUM_FREE_GC gc's are kept in a local list
 * (away from free/malloc)
 */
#define NUM_FREE_GC     50
static struct gcData *gcDataFreeList = (struct gcData *)0;
static int gcDataNumFree = 0;

/*
 * remember the last background-paint brush
 * will be reused by next bg-paint, if it needs
 * the same color. Otherwise, the next bg-erase
 * will destroy this brush.
 */
#ifdef CACHE_LAST_WM_PAINT_BRUSH
 static HBRUSH last_wm_paint_brush = (HBRUSH) 0;
 static int    last_wm_paint_brush_clr = (HWND) 0;
#endif
#ifdef CACHE_LAST_WM_PAINT_DC
 static HWND last_wm_paint_win = 0;
 static HDC  last_wm_paint_dc = 0;
#endif

static HWND lastPos_win = 0;
static int lastPos_w;
static int lastPos_h;
static int lastPos_x;
static int lastPos_y;

%}
! !

!WinWorkstation primitiveFunctions!
%{

#ifdef xxWIN32THREADS
extern void __suspendAktThread();
extern void __resumeAktThread();
#endif

__debugEvent__() {}

#ifdef DEBUGMASK
static void
printMask(int mask) {
	if (mask & KeyPressMask)        printf("KeyPressMask\n");
	if (mask & KeyReleaseMask)      printf("KeyReleaseMask\n");
	if (mask & ButtonPressMask)     printf("ButtonPressMask\n");
	if (mask & ButtonReleaseMask)   printf("ButtonReleaseMask\n");
	if (mask & ButtonMotionMask)    printf("ButtonMotionMask\n");
	if (mask & PointerMotionMask)   printf("PointerMotionMask\n");
	if (mask & ExposureMask)        printf("ExposureMask\n");
	if (mask & FocusChangeMask)     printf("FocusChangeMask\n");
	if (mask & EnterWindowMask)     printf("EnterWindowMask\n");
	if (mask & LeaveWindowMask)     printf("LeaveWindowMask\n");
	if (mask & KeymapStateMask)     printf("KeymapStateMask\n");
	if (mask & VisibilityChangeMask)printf("VisibilityChangeMask\n");
	if (mask & StructureNotifyMask) printf("StructureNotifyMask\n");
	if (mask & ResizeRedirectMask)  printf("ResizeRedirectMask\n");
	if (mask & PropertyChangeMask)  printf("PropertyChangeMask\n");
	if (mask & ColormapChangeMask)  printf("ColormapChangeMask\n");
}
#endif

static int
st2RGB(color, gcData)
    int color;
    struct gcData *gcData;
{
	int ir,ig,ib;

	if (gcData) {
	    if (gcData->bitmapColorBitCount == 1) {
	      if (color)
		  return WhitePixel;
	      else
		  return BlackPixel;
	    }
	}
#ifdef ALWAYSTRUECOLOR
	return (color & 0xffffff);
#else
	if (__depth < 15) {
	    return 0;
	} else {
	    if ((__depth == 16) || (__depth == 15)) {
		ib = (color & 0x1f) << 3;
		ig = ((color >> 5) & 0x3f) << 2;
		ir = ((color >> 11) & 0x1f) << 3;
		ir |= 7;
		ig |= 3;
		ib |= 7;
		return RGB(ir,ig,ib);
	    }
	    return (color & 0xffffff);
	}
#endif
}

static int
RGB2st(r, g, b)
    int r, g, b;
{
	int ir,ig,ib,id;

#ifdef ALWAYSTRUECOLOR
	ir = (r >> 8) & 0xff;
	ig = (g >> 8) & 0xff;
	ib = (b >> 8) & 0xff;

	id = RGB( ir, ig, ib);
#else
	if (__depth < 15) {
	    id = 0;
	} else {
	    if ((__depth == 16) || (__depth == 15)) {
		ir = (r >> 11) & 0x1f;
		ig = (g >> 10) & 0x3f;
		ib = (b >> 11) & 0x1f;

		id = ( ir << 11 ) | ( ig << 5 ) | ib;
	    } else {
		ir = (r >> 8) & 0xff;
		ig = (g >> 8) & 0xff;
		ib = (b >> 8) & 0xff;

		id = RGB( ir, ig, ib);
	    }
	}
#endif
	return id;
}

#if 0
/*
 * return a windows top-window
 */
static HWND
GetTopParent(hWnd)
    HWND hwnd;
{
	HWND lastParent = hWnd;
	HWND nextParent;

	while (nextParent = GetParent(lastParent)) {
	    lastParent = nextParent;
	}
	return lastParent;
}
#endif

#ifdef DEBUG_DELETEOBJECT

static int
_DeleteObject(o, lineNr)
    HANDLE o;
{
    int r = DeleteObject(o);

    if (r == 0)
	fprintf(stderr, "WinWorkstation [warning]: ERROR in DeleteObject %x [%d]\n", o, lineNr);
    return r;
}

#else
# define _DeleteObject(o,lnr)  DeleteObject(o)
#endif

static void
_DeleteFont(f, lineNr)
    HFONT f;
{
#ifdef CACHE_LAST_TMP_FONT
    if (f == __tmpDC_hfont) {
	SelectObject(__tmpDC, __tmpDC_prev_hfont);
	__tmpDC_hfont = NULL;
    }
#endif
    _DeleteObject(f, lineNr);
}

static int
_DeleteBrush(br, lineNr)
    HBRUSH br;
{
    int r;

    if ((br != __whiteBrush)
     && (br != __blackBrush)) {
	r = DeleteObject(br);

	if (r == 0)
	    fprintf(stderr, "WinWorkstation [warning]: ERROR in DeleteBrush %x [%d]\n", br, lineNr);
	return r;
    }
    return 1;
}

static int
_DeletePen(p, lineNr)
    HPEN p;
{
    int r;

    if ((p != __whitePen)
     && (p != __blackPen)) {
	r = DeleteObject(p);

	if (r == 0)
	    fprintf(stderr, "WinWorkstation [warning]: ERROR in DeletePen %x [%d]\n", p, lineNr);
	return r;
    }
    return 1;
}

#ifdef NUM_PEN_CACHED

static int
_DeletePenIfNotInCache(p, lineNr)
    HPEN p;
{
    int i, r;

    if ((p == __whitePen)
     || (p == __blackPen)) {
	return 1; /* not deleted, but OK */
    }
    for (i=0; i<NUM_PEN_CACHED;i++) { 
	if (__penCache[i].pen == p) { 
	    return 1; /* not deleted, but OK */
	}
    } 
    r = DeleteObject(p);

    if (r == 0)
	fprintf(stderr, "WinWorkstation [warning]: ERROR in DeletePen2 %x [%d]\n", p, lineNr);
    return r;
}

#else
# define _DeletePenIfNotInCache(p, lineNr)   _DeletePen(p, lineNr)
#endif

#ifdef NUM_BRUSH_CACHED

static int
_DeleteBrushIfNotInCache(p, lineNr)
    HPEN p;
{
    int i, r;

    for (i=0; i<NUM_BRUSH_CACHED;i++) { 
	if (__brushCache[i].brush == p) { 
	    return 1; /* not deleted, but OK */
	}
    } 
    if ((p == __whiteBrush)
     || (p == __blackBrush)) {
	return 1; /* not deleted, but OK */
    }
    r = DeleteObject(p);

    if (r == 0)
	fprintf(stderr, "WinWorkstation [warning]: ERROR in DeleteBrush2 %x [%d]\n", p, lineNr);
    return r;
}

#else
# define _DeleteBrushIfNotInCache(p, lineNr)   _DeleteBrush(p, lineNr)
#endif

static struct gcData *
newGcData() {
    struct gcData *gcData;

    if (gcDataFreeList) {
	gcData = gcDataFreeList;
	gcDataFreeList = gcData->u.__nextFree;
	gcDataNumFree--;
    } else {
	gcData = (struct gcData *) malloc(sizeof(struct gcData));
	if (! gcData) {
	    fprintf(stderr, "WinWorkstat [warning]: failed to allocate gcData\n");
	    return 0;
	}
    }

    memset(gcData, 0, sizeof(struct gcData));
    gcData->fgColor = 0xffffff;
    /* gcData->bgColor = 0; - not needed - memset does it */
    gcData->clipping = FALSE;
    gcData->clipByChildren = TRUE;
    gcData->bitbltrop2 = BITBLT_COPY;
    gcData->lineWidth = 1;
    gcData->lStyle = PS_SOLID | PS_JOIN_MITER | PS_ENDCAP_FLAT;
    /* gcData->brushOrgX = gcData->brushOrgY = 0; - not needed - memset does it */
    /* gcData->hPen = 0; - not needed - memset does it */
    /* gcData->hBrush = 0; - not needed - memset does it */
    return gcData;
}

static void
freeGcData(gcData)
    struct gcData *gcData;
{
    if (gcDataNumFree < NUM_FREE_GC) {
	gcData->u.__nextFree = gcDataFreeList;
	gcDataFreeList = gcData;
	gcDataNumFree++;
    } else {
	free(gcData);
    }
}

#ifdef CACHE_LAST_DC
# define FLUSH_CACHED_DC(__gcData__) \
    if (lastGcData == __gcData__) {  \
	_releaseDC(lastGcData);      \
    }
#else
# define FLUSH_CACHED_DC(x)     /* */
#endif

#ifdef CACHE_LAST_PEN
# define FLUSH_CACHED_PEN(__gcData__) \
    if (__gcData__->hPen) {           \
	if (__gcData__->save_hPen) {  \
	    SelectObject(__gcData__->_hDC, __gcData__->save_hPen); \
	    __gcData__->save_hPen = 0;   \
	} \
	_DeletePenIfNotInCache(__gcData__->hPen, __LINE__); \
	__gcData__->hPen = 0; \
    }
#else
# define FLUSH_CACHED_PEN(x)    /* */
#endif

#ifdef CACHE_LAST_BRUSH
# define FLUSH_CACHED_BRUSH(__gcData__) \
    if (__gcData__->hBrush) {           \
	if (__gcData__->save_hBrush) {  \
	    SelectObject(__gcData__->_hDC, __gcData__->save_hBrush); \
	    __gcData__->save_hBrush = 0;   \
	} \
	_DeleteBrushIfNotInCache(__gcData__->hBrush, __LINE__); \
	__gcData__->hBrush = 0; \
    }
#else
# define FLUSH_CACHED_BRUSH(x)    /* */
#endif

static void
_releaseDC(gcData)
    struct gcData *gcData;
{
    HDC hDC = gcData->_hDC;

    if (gcData->save_hPen) {
	SelectObject(hDC, gcData->save_hPen);
	gcData->save_hPen = 0;
    }
    if (gcData->save_hBrush) {
	SelectObject(hDC, gcData->save_hBrush);
	gcData->save_hBrush = 0;
    }
    if (gcData->save_hFont) {
	SelectObject(hDC, gcData->save_hFont);
	gcData->save_hFont = 0;
    }

    if (gcData->hWnd) {
	if (hDC) {
	    ReleaseDC(gcData->hWnd, hDC);
#ifdef CACHE_LAST_WM_PAINT_DC
	    if (hDC == last_wm_paint_dc) {
		last_wm_paint_dc = last_wm_paint_win = 0;
# ifdef DEBUG_DC_REUSE
		fprintf(stderr, "WinWorkstation [info]: Oops - releasing bg_paint-dc\n");
# endif
	    }
#endif
	}
    } else if (gcData->hBitmap) {
	SelectObject(hDC, gcData->save_hBitmap);
	DeleteDC(hDC);
    }
    gcData->_hDC = 0;

#ifdef CACHE_LAST_DC
    if (lastGcData == gcData) {
	lastGcData = 0;
    }
#endif
#ifdef CACHE_LAST_PEN
    if (gcData->hPen) {
	_DeletePenIfNotInCache(gcData->hPen, __LINE__);
	gcData->hPen = 0;
    }
#endif
#ifdef CACHE_LAST_BRUSH
    if (gcData->hBrush) {
	_DeleteBrushIfNotInCache(gcData->hBrush, __LINE__);
	gcData->hBrush = 0;
    }
#endif
}

static HDC
_getDC(gcData)
    struct gcData *gcData;
{
    HDC hDC;

#ifdef CACHE_LAST_DC
    int currThreadId = GetCurrentThreadId();

    if (lastGcData) {
	if ((lastGcData == gcData)
	 && (lastGcHWIN == gcData->hWnd)
	 && (lastGcHBITMAP == gcData->hBitmap)
	 && (lastGcOwnerThreadID == currThreadId) 
	 && gcData->_hDC 
	) {
	    //printf(".");

# ifdef CACHE_LAST_WM_PAINT_DC /* DDD */
	    if (last_wm_paint_dc == gcData->_hDC) {
#  ifdef DEBUG_DC_REUSE
		fprintf(stderr, "WinWorkstation [info]: Oops wm_paint_dc reuse [%d]\n", __LINE__);
#  endif
		last_wm_paint_dc = last_wm_paint_win = 0;
	    }
# endif
	    return gcData->_hDC;
	}

	_releaseDC(lastGcData);
	lastGcData = 0;
    }
#endif

    hDC = 0;
    if (gcData->hWnd != 0) {
	if (__isWinNT && (gcData->hWnd == __rootWin)) {
	    hDC = gcData->_hDC = GetDCEx(gcData->hWnd, 0, DCX_WINDOW);
	    //gcData->_hDC = GetDC(gcData->hWnd);
	} else {
	    if (gcData->clipByChildren) {
		//gcData->_hDC = GetDCEx(gcData->hWnd, 0, DCX_CLIPCHILDREN);
		hDC = gcData->_hDC = GetDC(gcData->hWnd);
	    } else
		hDC = gcData->_hDC = GetDCEx(gcData->hWnd, 0, DCX_PARENTCLIP);
	}
    } else if (gcData->hBitmap) {
	hDC = gcData->_hDC = CreateCompatibleDC(__tmpDC);
	gcData->save_hBitmap = SelectObject(gcData->_hDC,gcData->hBitmap);
    }
    gcData->bkMode = BK_UNDEF;

    if (hDC) {
#if 0
	SetTextColor(hDC, gcData->bgColor);
	SetBkColor(hDC, gcData->fgColor);
#else
	SetTextColor(hDC, gcData->fgColor);
	SetBkColor(hDC, gcData->bgColor);
#endif
	if (gcData->rop2) 
	    SetROP2(hDC, gcData->rop2);

	if (gcData->clipping) {
	    HRGN region = CreateRectRgn( gcData->clipX, gcData->clipY,
					 gcData->clipX + gcData->clipW,
					 gcData->clipY + gcData->clipH);
	    if (region == NULL ) {
		fprintf(stderr, "WinWorkstat [warning]: clipping region creation failed\n");
	    } else {
		if (SelectClipRgn(hDC, region) == ERROR ) {
		    DPRINTF(("WinWorkstat [warning]: select clipping region failed\n" ));
		}
		_DeleteObject(region, __LINE__);
	    }
	} else {
	    SelectClipRgn(hDC, NULL);
	}

	if (gcData->hFont) {
	    HFONT prevFont;

	    prevFont = SelectObject(hDC, gcData->hFont);
	    if (! gcData->save_hFont) {
		gcData->save_hFont = prevFont;
	    }
	}
    }

#ifdef CACHE_LAST_DC
    lastGcData = gcData;
    lastGcOwnerThreadID = currThreadId;
    lastGcHWIN = gcData->hWnd;
    lastGcHBITMAP = gcData->hBitmap;
    gcData->_hDC = hDC;

# ifdef CACHE_LAST_WM_PAINT_DC /* DDD */
    if (last_wm_paint_dc == hDC) {
#  ifdef DEBUG_DC_REUSE
	fprintf(stderr, "WinWorkstation [info]: Oops wm_paint_dc reuse [%d]\n", __LINE__);
#  endif
	last_wm_paint_dc = last_wm_paint_win = 0;
    }
# endif
#endif

    return hDC;
}

static HBRUSH
GcDataGetBrush(hDC, gcData)
    HDC hDC;
    struct gcData *gcData;
{
    HBRUSH hBrush = 0;
    HBRUSH prevBrush;

#ifdef CACHE_LAST_BRUSH
    if (hBrush = gcData->hBrush) {
	if (! gcData->save_hBrush) {
	    gcData->save_hBrush = SelectObject(gcData->_hDC, hBrush);
	}
	return hBrush;
    }
#endif
    if (gcData->hMask) {
	hBrush = CreatePatternBrush(gcData->hMask);
	SetBrushOrgEx(hDC, gcData->maskOrgX, gcData->maskOrgY, 0);       
	RESPRINTF(("CreatePatternBrush %x\n",gcData->hMask));
    } else {
	if (gcData->fgColor == BlackPixel) {
	    hBrush = __blackBrush;
	} else if (gcData->fgColor == WhitePixel) {
	    hBrush = __whiteBrush;
	} else {
#ifdef NUM_BRUSH_CACHED
	    {
		int i;

		for (i=0; i<NUM_BRUSH_CACHED; i++) {
		    if (__brushCache[i].clr == gcData->fgColor) {
			hBrush = __brushCache[i].brush;
			/*
			 * move it up in the cache
			 */
			if (i > 0) {
			    HBRUSH t = __brushCache[i-1].brush;
			    int c = __brushCache[i-1].clr;

			    __brushCache[i-1].brush = hBrush;
			    __brushCache[i-1].clr = gcData->fgColor;
			    __brushCache[i].brush = t;
			    __brushCache[i].clr = c;
			}
# ifdef CACHE_LAST_BRUSH
			prevBrush = SelectObject(gcData->_hDC, hBrush);
			if (! gcData->save_hBrush) {
			    gcData->save_hBrush = prevBrush;
			}
			gcData->hBrush = hBrush;
# endif
			return hBrush;
		    }
		}
	    }
#endif /* BRUSH_CACHE */
	    hBrush = CreateSolidBrush(gcData->fgColor);
	    RESPRINTF(("CreateSolidBrush %x\n",gcData->fgColor));
	}
    }
    gcData->hBrush = hBrush;

    prevBrush = SelectObject(gcData->_hDC, hBrush);
    if (! gcData->save_hBrush) {
	gcData->save_hBrush = prevBrush;
    }
    return hBrush;
}

#ifdef CACHE_LAST_BRUSH
# define GcDataReleaseBrush(hDC, gcData)  /* nothing*/
#else
static void
GcDataReleaseBrush(hDC, gcData)
    HDC hDC;
    struct gcData *gcData;
{
    HBRUSH hBrush;

    hBrush = gcData->hBrush;
    if (gcData->save_hBrush) {
	SelectObject(hDC, gcData->save_hBrush);
	gcData->save_hBrush = NULL;
    }
    _DeleteBrushIfNotInCache(hBrush, __LINE__); 
    gcData->hBrush = 0;
}
#endif

static HPEN
GcDataGetPen(hDC, gcData)
    HDC hDC;
    struct gcData *gcData;
{
    HPEN hPen = 0;
    HPEN prevPen;
    LOGBRUSH Brush;

#ifdef CACHE_LAST_PEN
    if (hPen = gcData->hPen) {
	if (! gcData->save_hPen) {
	    gcData->save_hPen = SelectObject(gcData->_hDC, hPen);
	}
	return hPen;
    }
#endif

    if (((gcData->lStyle & PS_STYLE_MASK) == PS_SOLID)
     && (gcData->hMask == 0)
     && (gcData->lineWidth <= 1)) {
	if (gcData->fgColor == BlackPixel) {
	    gcData->hPen = __blackPen;
	    prevPen = SelectObject(hDC, __blackPen);
	    if (! gcData->save_hPen) {
		gcData->save_hPen = prevPen;
	    }
	    return __blackPen;
	} else if (gcData->fgColor == WhitePixel) {
	    gcData->hPen = __whitePen;
	    prevPen = SelectObject(hDC, __whitePen);
	    if (! gcData->save_hPen) {
		gcData->save_hPen = prevPen;
	    }
	    return __whitePen;
	}

#ifdef NUM_PEN_CACHED
	{
	    int i;

	    for (i=0; i<NUM_PEN_CACHED; i++) {
		if (__penCache[i].clr == gcData->fgColor) {
		    hPen = __penCache[i].pen;
		    /*
		     * move it up in the cache
		     */
		    if (i > 0) {
			HPEN t = __penCache[i-1].pen;
			int c = __penCache[i-1].clr;

			__penCache[i-1].pen = hPen;
			__penCache[i-1].clr = gcData->fgColor;
			__penCache[i].pen = t;
			__penCache[i].clr = c;
		    }

		    prevPen = SelectObject(hDC, hPen);
		    if (! gcData->save_hPen) {
			gcData->save_hPen = prevPen;
		    }
		    gcData->hPen = hPen;

		    return hPen;
		}
	    }
	}
#endif /* PEN_CACHE */
    }

    if (__isWinNT) {
	int lw = gcData->lineWidth;

	if (lw == 0) {
	    lw = 1;
	}

	/*
	 * NT supports masked drawing with any lineStyle,
	 * and also non-solid lines with any lineWidth.
	 */
	if (gcData->hMask) {
	    Brush.lbStyle = BS_PATTERN;
	    Brush.lbHatch = (DWORD)gcData->hMask;
	    Brush.lbColor = gcData->fgColor;
	} else {
	    Brush.lbStyle = BS_SOLID;
	    Brush.lbHatch = 0;
	    Brush.lbColor = gcData->fgColor;
	}

	hPen = ExtCreatePen(PS_GEOMETRIC | gcData->lStyle,
			    lw, /* gcData->lineWidth, */
			    &Brush,
			    0, 0);
	RESPRINTF(("ExtCreatePen1 %x %d(%d) %x %x\n",
			gcData->lStyle,
			lw, gcData->lineWidth,
			gcData->fgColor, gcData->hMask));

	if (gcData->hMask) {
	    SetBrushOrgEx(hDC, gcData->maskOrgX, gcData->maskOrgY, 0);
	}
    } else {
	/*
	 * W95 only supports masked drawing with SOLID lines
	 * also, we should use COSMETIC pens if possible
	 * with non-solid lineStyles.
	 */
	if ((gcData->lStyle & PS_STYLE_MASK) == PS_SOLID) {
	    int ps = PS_GEOMETRIC;

	    if (gcData->hMask) {
		Brush.lbStyle = BS_PATTERN;
		Brush.lbHatch = (DWORD)gcData->hMask;
		Brush.lbColor = gcData->fgColor;
	    } else {
		Brush.lbStyle = BS_SOLID;
		Brush.lbHatch = 0;
		Brush.lbColor = gcData->fgColor;
		if (gcData->lineWidth <= 1) {
		    ps = PS_COSMETIC;
		}
	    }

	    hPen = ExtCreatePen(ps | gcData->lStyle,
				gcData->lineWidth,
				&Brush,
				0, 0);

	    RESPRINTF(("ExtCreatePen1 %x %d %x %x\n",
			    gcData->lStyle,
			    gcData->lineWidth,
			    gcData->fgColor, gcData->hMask));

	    if (gcData->hMask) {
		SetBrushOrgEx(hDC, gcData->maskOrgX, gcData->maskOrgY, 0);
	    }
	} else {
	    int lw = gcData->lineWidth;

	    if (lw == 1) {
		lw = 0;
	    }
	    /*
	     * dashes only supported with lineWidth 0
	     */
	    hPen = CreatePen((gcData->lStyle & PS_STYLE_MASK),
			     lw,
			     gcData->fgColor);

	    RESPRINTF(("CreatePen %x %d %x\n",
				(gcData->lStyle & PS_STYLE_MASK),
				gcData->lineWidth,
				gcData->fgColor));

	    //
	    // CG: wrong; must set to opaque, if doubleDashed
	    //    
	    SetBkMode(hDC, TRANSPARENT);
	    gcData->bkMode = BK_TRANSPARENT;
	}
    }

    gcData->hPen = hPen;
    prevPen = SelectObject(hDC, hPen);
    if (! gcData->save_hPen) {
	gcData->save_hPen = prevPen;
    }

#ifdef NUM_PEN_CACHED
    /*
     * remember in penCache
     */
    if (((gcData->lStyle & PS_STYLE_MASK) == PS_SOLID)
     && (gcData->hMask == 0)
     && (gcData->lineWidth <= 1)) {
	int i;

	/*
	 * search for an empty slot
	 */
	for (i=0; i<NUM_PEN_CACHED; i++) {
	    if (__penCache[i].pen == 0) {
		__penCache[i].clr = gcData->fgColor;
		__penCache[i].pen = hPen;
		break;
	    }
	}
	if (i == NUM_PEN_CACHED) {
	    /* 
	     * replace last in penCache
	     */
	    HPEN t = __penCache[NUM_PEN_CACHED - 1].pen;

	    __penCache[NUM_PEN_CACHED - 1].pen = hPen;
	    __penCache[NUM_PEN_CACHED - 1].clr = gcData->fgColor;
	    _DeletePen(t, __LINE__);
	}
    }
#endif

    return hPen;
}

#ifdef CACHE_LAST_PEN
# define GcDataReleasePen(hDC, gcData)  /* nothing*/
#else
static void
GcDataReleasePen(hDC, gcData)
    HDC hDC;
    struct gcData *gcData;
{
    HPEN hPen;

    if (gcData->save_hPen) {
	SelectObject(hDC, gcData->save_hPen);
	gcData->save_hPen = NULL;
    }
    hPen = gcData->hPen;
    _DeletePenIfNotInCache(hPen, __LINE__); 
    gcData->hPen = 0;
}
#endif /* CACHE_LAST_PEN */

static int
lockEventQ() {
    DWORD dwWaitResult;

    /* Request ownership of mutex. */

    dwWaitResult = WaitForSingleObject(hEventQMutex, 5000L);

#ifdef LOCK_DEBUG
    lockcountQ++;
    if (lockcountQ != 1) {
	fprintf(stderr, "WinWorkstation [info]: lockcountQ (%d) != 1\n", lockcountQ);
	lockcountQ = 1;
    }
#endif
    switch (dwWaitResult) {
	// The thread got mutex ownership.
	case WAIT_OBJECT_0:
	    break;

	// Cannot get mutex ownership due to time-out.
	case WAIT_TIMEOUT:
	    fprintf(stderr, "WinWorkstation [info]: lockEventQ timeout\n");
	    return FALSE;

	// Got ownership of the abandoned mutex object.
	default:
	    fprintf(stderr, "WinWorkstation [info]: lockEventQ abandoned\n");
	    return FALSE;
    }
    return TRUE;
}

static void
unlockEventQ() {
#ifdef LOCK_DEBUG
    lockcountQ--;
    if (lockcountQ != 0) {
	fprintf(stderr, "WinWorkstation [info]: lockcountQ (%d) != 0\n", lockcountQ);
	lockcountQ = 0;
    }
#endif
    /* Release ownership of the mutex object. */
    if (! ReleaseMutex(hEventQMutex)) {
       /* Deal with error. */
       printf("unlock Error\n");
    }
}

static int
lockEventFreeList() {
    DWORD dwWaitResult;

    /* Request ownership of mutex. */

    dwWaitResult = WaitForSingleObject(hEventFreeListMutex, 1000L); 

#ifdef LOCK_DEBUG
    lockcountF++;
    if (lockcountF != 1) {
	fprintf(stderr, "WinWorkstation [info]: lockcountF (%d) != 1\n", lockcountF);
	lockcountF = 1;
    }
#endif
    switch (dwWaitResult) {
	// The thread got mutex ownership.
	case WAIT_OBJECT_0:
	    break;

	// Cannot get mutex ownership due to time-out.
	case WAIT_TIMEOUT:
	    fprintf(stderr, "WinWorkstation [info]: lockEventF timeout\n");
	    return FALSE;

	// Got ownership of the abandoned mutex object.
	default:
	    fprintf(stderr, "WinWorkstation [info]: lockEventF abandoned\n");
	    return FALSE;
    }
    return TRUE;
}

static void
unlockEventFreeList() {
#ifdef LOCK_DEBUG
    lockcountF--;
    if (lockcountF != 0) {
	fprintf(stderr, "WinWorkstation [info]: lockcountF (%d) != 0\n", lockcountF);
	lockcountF = 0;
    }
#endif
    /* Release ownership of the mutex object. */
    if (! ReleaseMutex(hEventFreeListMutex)) {
       /* Deal with error. */
       printf("unlock Error\n");
    }
}


static int
initEventqueue() {
    struct queuedEvent *bulk;
    int i;

    bulk = (struct queuedEvent *) malloc(EV_CHUNK_SZ);
    if (bulk == 0) {
	fprintf(stderr, "WinWorkstation [warning]: no memory for dispatchqueue\n");
	return 0;
    }
    for (i=1; i<EV_CHUNK_CNT; i++) {
	bulk[i-1].ev_next = &(bulk[i]);
    }
    bulk[EV_CHUNK_CNT-1].ev_next = (struct queuedEvent *)0;
    eventFreeList = bulk;

    for (i=0; i<NUMQUICKFREE_EV; i++) {
	quickFreeEvent[i] = (struct queuedEvent *)0;
    }

    return 1;
}

#define ENQ_AT_END      0
#define ENQ_AT_FRONT    1

#define EV_NOTIME       0

#define enqEvent(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) \
    __enqEvent(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)

static void
__enqEvent(atFront, flag, hWnd, message, wParam, arg1, arg2, arg3, arg4, evTime)
    HWND hWnd;
    UINT message;
    UINT wParam;
    UINT evTime;
{
    struct queuedEvent *ev = 0;
    int repeatCount = 0;
    int needSetEvent, i;

    /*
     * get an event ...
     */
    for (;;) {
	for (i=0; i<NUMQUICKFREE_EV; i++) {
	    if (ev = quickFreeEvent[i]) {
		quickFreeEvent[i] = (struct queuedEvent *)0;
		break;
	    }
	}

	if (ev) {
	    break;
	}

	if (! lockEventFreeList()) {
	    fprintf(stderr, "WinWorkstation [info]: could not lock evFreeList\n");
	    return;
	}

	ev = eventFreeList;
	if (ev) {
	    eventFreeList = ev->ev_next;
	    unlockEventFreeList();
	    break;
	}

	unlockEventFreeList();

#if 0
	SetEvent(hInputEvent);
#endif
	if (repeatCount++ >= 5) {
	    /* throw away sorry */
	    fprintf(stderr, "WinWorkstation [info]: event throw away\n");
	    return;
	}

	fprintf(stderr, "WinWorkstation [info]: wait for event memory (%d)\n", repeatCount);
	WaitForSingleObject(hNeverTriggered, 100L);
    }

    ev->ev_flag = flag;
    ev->ev_hWnd = hWnd;
    ev->ev_message = message;
    ev->ev_wParam = wParam;
    ev->ev_arg1 = arg1;
    ev->ev_arg2 = arg2;
    ev->ev_arg3 = arg3;
    ev->ev_arg4 = arg4;
    ev->ev_time = evTime /* lastMSGTime */;

    lockEventQ();

    needSetEvent = (eventQueueHead == NULL);

    if (atFront) {
	ev->count = -1;
	ev->ev_next = eventQueueHead;
	eventQueueHead = ev;
	if (eventQueueTail == 0) {
	    eventQueueTail = ev;
	}
    } else {
	ev->count = eventsendcount++;

	ev->ev_next = (struct queuedEvent *)0;
	if (eventQueueTail) {
	    eventQueueTail->ev_next = ev;
	} else {
	    eventQueueHead = ev;
	}
	eventQueueTail = ev;
    }

    unlockEventQ();

    if (needSetEvent) {
	SetEvent(hInputEvent);
    }

#if 0
    SetThreadPriority(_masterThread,THREAD_PRIORITY_HIGHEST);
#endif
}

static int
deqEvent(struct queuedEvent *ret_ev, HWND hWnd, int mask)
{
    struct queuedEvent *ev = NULL;

    if (mask != ~0) {
	PRINTF(("deqEvent mask:%x\n",mask));
    }
    if (hWnd) {
	PRINTF(("deqEvent hWnd:%x\n",hWnd));
    }

  again: ;
    if (lockEventQ()) {
	TH_DPRINTF(("TDEQ\n"));
	ev = eventQueueHead;
	if (ev != NULL) {
	    eventQueueHead = ev->ev_next;
	    if (! eventQueueHead) {
		eventQueueTail = (struct queuedEvent *)0;
	    }
	}
	unlockEventQ();

	if (ev != NULL) {
	    if (ev->count != -1) {
		static int anyGoodEventReceived = 1;

		if (eventempfcount != ev->count) {
		    if (anyGoodEventReceived) {    
			fprintf(stderr, "WinWorkstation [warning]: lost event (eventcount error %d <-> %d)\n",
					 eventempfcount, ev->count);
		    }
		    eventempfcount = ev->count;
		    anyGoodEventReceived = 0;
		} else {
		    anyGoodEventReceived = 1;
		}
		eventempfcount++;
	    }

	    if (ev->ev_message == WM_DESTROY) {
		/*
		 * arg2 is the localInfoPtr 
		 * (which is already cleared in the wind-structure)
		 * it must be freed by the stx-process ('cause it was malloc'd by it)
		 */
		if (ev->ev_arg2) {
		    free((char*)ev->ev_arg2);
		    ev->ev_arg2 = 0;
		}

#ifdef TOPWINDOWCLASS
		if (ev->ev_arg1) {
		    if (UnregisterClass((char*)ev->ev_arg1,(HANDLE) __getHInstance())) {
			/*printf("UnregisterClass %s ok.\n",(char*)ev->ev_arg1);*/
			free((char*)ev->ev_arg1);
			ev->ev_arg1 = 0;
		    } else {
			/* noch einmal in die queue */
			if (ev->ev_arg2++ < 100) {
			    lockEventQ();
			    ev->ev_next = (struct queuedEvent *)0;
			    ev->count = eventsendcount++;

			    if (eventQueueTail) {
				eventQueueTail->ev_next = ev;
			    } else {
				eventQueueHead = ev;
			    }
			    eventQueueTail = ev;
			    unlockEventQ();

			    if (ev->ev_arg2 > 98) {
				fprintf(stderr, "WinWorkstation [info]: UnregisterClass %s failed. Wait 1 sec.\n",(char*)ev->ev_arg1);
				sleep(1);
			    } else {
				sleep(0);
			    }
			    goto again;
			} else {
			    /* fail evtl. später ändern und in st verzögert aufrufen */
			    fprintf(stderr, "WinWorkstation [info]: UnregisterClass %s failed.\n",(char*)ev->ev_arg1);
			    free((char*)ev->ev_arg1);
			    ev->ev_arg1 = 0;
			}
		    }
		}
#endif
	    }
	}

	if (ev) {
	    int i;

	    *ret_ev = *ev;

	    for (i=0; i<NUMQUICKFREE_EV; i++) {
		if (quickFreeEvent[i] == 0) {
		    quickFreeEvent[i] = ev;
		    break;
		}
	    }
	    if (i == NUMQUICKFREE_EV) {
		lockEventFreeList();
		ev->ev_next = eventFreeList;
		eventFreeList = ev;
		unlockEventFreeList();
	    }
	}
    }

#if 0
    if (!ev)
	SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_NORMAL);
#endif

    return (ev != 0);
}

/*
 * generate expose events from hWnd's update region or the passedIn region.
 * return the number of events (>0), if any was generated.
 * 0, if no event was generated, -1 if none was generated with error.
 * if a region is passed in, it is not destroyed.
 */
static void
__clearWindow(hWnd, x, y, w, h)
    HWND hWnd;
{
	HDC dc;
	HBRUSH br;
	int bgClr;
	int isPrivateBrush = 0;

#ifdef CACHE_LAST_WM_PAINT_DC
# ifdef USE_PAINT_MUTEX
	WaitForSingleObject(hPaintMutex, 100L);
# endif

	if (last_wm_paint_dc && (last_wm_paint_win == hWnd)) {
	    dc = last_wm_paint_dc;
	} else {
	    if (last_wm_paint_dc) {
# ifdef CACHE_LAST_DC
		if (lastGcData && (lastGcData->_hDC == last_wm_paint_dc)) {
#  ifdef DEBUG_DC_REUSE
		    fprintf(stderr, "WinWorkstation [info]: Oops - release lastGcData [%d]\n", __LINE__);
#  endif
		    _releaseDC(lastGcData);
		} else {
		    ReleaseDC(last_wm_paint_win, last_wm_paint_dc);
		}
# else
		ReleaseDC(last_wm_paint_win, last_wm_paint_dc);
# endif
	    }
	    dc = GetDC(hWnd);
	    last_wm_paint_dc = dc;
	    last_wm_paint_win = hWnd;
	}
#else
	dc = (HDC)GetDC(hWnd);
#endif

#ifdef CACHE_LAST_DC
	if (lastGcData && (lastGcData->_hDC == dc)) {
# ifdef DEBUG_DC_REUSE
	    fprintf(stderr, "WinWorkstation [info]: Oops wm_paint_dc reuse [%d]\n", __LINE__);
# endif
	    if ((lastGcData->rop2 != R2_COPYPEN)
	     || (lastGcData->bitbltrop2 != BITBLT_COPY)) {
		fprintf(stderr, "WinWorkstation [info]: Oops wm_paint_dc not in Copy mode\n");
	    }
	    lastGcData = 0;
	}
#endif

	br = GetWindow_viewBgBrush(hWnd);
	if (! br) {
	    bgClr = GetWindow_viewBgColor(hWnd);

#ifdef CACHE_LAST_WM_PAINT_BRUSH
	    if (last_wm_paint_brush
	     && (last_wm_paint_brush_clr == bgClr)) {
		br = last_wm_paint_brush;
	    } else {
		if (bgClr == WhitePixel) {
		    br = __whiteBrush;
		} else if (bgClr == BlackPixel) {
		    br = __blackBrush;
		} else {
		    if (last_wm_paint_brush) {
			_DeleteBrush(last_wm_paint_brush, __LINE__);
		    }
		    last_wm_paint_brush = br = CreateSolidBrush(bgClr);
		    last_wm_paint_brush_clr = bgClr;
		    isPrivateBrush = 1;
		}
	    }
#else
	    br = CreateSolidBrush(bgClr);
	    isPrivateBrush = 1;
#endif
	}

	if (! br) {
	    fprintf(stderr, "WinWorkstation [info]: no brush\n");
	}

	if (br) {
	    RECT rect;

	    SelectClipRgn(dc, NULL);

	    rect.left = x;
	    rect.top = y;
	    rect.right = x + w;
	    rect.bottom = y + h;
	    FillRect(dc, &rect, br);
	}

#ifndef CACHE_LAST_WM_PAINT_BRUSH
	if (isPrivateBrush) {
	    _DeleteBrush(br, __LINE__);
	}
#endif

#ifndef CACHE_LAST_WM_PAINT_DC
	ReleaseDC(hWnd, dc);
#endif

#if defined(CACHE_LAST_WM_PAINT_DC) && defined(USE_PAINT_MUTEX)
	ReleaseMutex(hPaintMutex);
#endif
}

static int
__generateExposes(hWnd, hRgnInOrNull, msgType, doClear)
    HWND hWnd;
    int msgType;
    HRGN hRgnInOrNull;
{
	RECT updRect;
	HRGN updRgn = 0;
	int numRects, ret, n, i;
	int wantExpose;
	RECT *pRect;
	union {
	    RGNDATA rgnData;
	    char    bytes[256];
	} data;

	wantExpose = (GetWindow_eventMask(hWnd) & ExposureMask);

	if (hRgnInOrNull) {
	    updRgn = hRgnInOrNull;
	} else {
	    /*
	     * fetch the update region, even if ExposureMask is empty.
	     */
	    updRgn = CreateRectRgn(0, 0, 0, 0);
	    ret = GetUpdateRgn(hWnd, updRgn, FALSE);
        
	    switch (ret) {
		case ERROR:
		    fprintf(stderr, "WinWorkstation [info]: errregion\n");
		    return -1;
		case NULLREGION:
		    DPRINTF(("nullregion\n"));
		    _DeleteObject(updRgn, __LINE__);
		    return 0;
		case SIMPLEREGION:
		case COMPLEXREGION:
		    break;
	    }
	}
	n = GetRegionData(updRgn, sizeof(data), &data);

	/* 
	 * the MS-doc states: n==1
	 * but this is not true
	 */
	if ((n >= sizeof(RGNDATA))
	 && (n <= sizeof(data))
	 && (data.rgnData.rdh.iType == RDH_RECTANGLES))  {
	    /* ok, got the region */
	    numRects = data.rgnData.rdh.nCount;
	    pRect = data.rgnData.Buffer;
	    DPRINTF(("region numRects=%d\n", numRects));
	} else{
	    /* a big region ... */
	    GetRgnBox(updRgn, &updRect);
	    numRects = 1;
	    pRect = &updRect;
	    DPRINTF(("very complex region (need:%d have:%d) bounds=%d,%d -> %d,%d\n", 
		     n, sizeof(data),
		     updRect.left, updRect.top, 
		     updRect.right, updRect.bottom));
	    if (((updRect.right - updRect.left) <= 0)
	     || ((updRect.bottom - updRect.top) <= 0)) {
		numRects = 0;
	    }
	}

	if (updRgn && (updRgn != hRgnInOrNull)) {
	    _DeleteObject(updRgn, __LINE__);
	}

	if (numRects) {
	    HDC dc;
	    HBRUSH br;
	    int bgClr;
	    int isPrivateBrush = 0;

	    if (doClear) {
#ifdef CACHE_LAST_WM_PAINT_DC
# ifdef USE_PAINT_MUTEX
		WaitForSingleObject(hPaintMutex, 100L);
# endif
		if (last_wm_paint_dc && (last_wm_paint_win == hWnd)) {
		    dc = last_wm_paint_dc;
		} else {
		    if (last_wm_paint_dc) {
# ifdef CACHE_LAST_DC
			if (lastGcData && (lastGcData->_hDC == last_wm_paint_dc)) {
#  ifdef DEBUG_DC_REUSE
			    fprintf(stderr, "WinWorkstation [info]: Oops - release lastGcData [%d]\n", __LINE__);
#  endif
			    _releaseDC(lastGcData);
			} else {
			    ReleaseDC(last_wm_paint_win, last_wm_paint_dc);
			}
# else
			ReleaseDC(last_wm_paint_win, last_wm_paint_dc);
# endif
		    }
		    dc = GetDC(hWnd);
		    last_wm_paint_dc = dc;
		    last_wm_paint_win = hWnd;
		}
#else
		dc = (HDC)GetDC(hWnd);
#endif

#ifdef CACHE_LAST_DC
		if (lastGcData && (lastGcData->_hDC == dc)) {
# ifdef DEBUG_DC_REUSE
		    fprintf(stderr, "WinWorkstation [info]: Oops wm_paint_dc reuse [%d]\n", __LINE__);
# endif
		    if ((lastGcData->rop2 != R2_COPYPEN)
		     || (lastGcData->bitbltrop2 != BITBLT_COPY)) {
			fprintf(stderr, "WinWorkstation [info]: Oops wm_paint_dc not in Copy mode\n");
		    }
		    lastGcData = 0;
		}
#endif

		br = GetWindow_viewBgBrush(hWnd);
		if (! br) {
		    bgClr = GetWindow_viewBgColor(hWnd);

#ifdef CACHE_LAST_WM_PAINT_BRUSH
		    if (last_wm_paint_brush
		     && (last_wm_paint_brush_clr == bgClr)) {
			br = last_wm_paint_brush;
		    } else {
			if (bgClr == WhitePixel) {
			    br = __whiteBrush;
			} else if (bgClr == BlackPixel) {
			    br = __blackBrush;
			} else {
			    if (last_wm_paint_brush) {
				_DeleteBrush(last_wm_paint_brush, __LINE__);
			    }
			    last_wm_paint_brush = br = CreateSolidBrush(bgClr);
			    last_wm_paint_brush_clr = bgClr;
			    isPrivateBrush = 1;
			}
		    }
#else
		    br = CreateSolidBrush(bgClr);
		    isPrivateBrush = 1;
#endif
		}

		if (! br) {
		    fprintf(stderr, "WinWorkstation [info]: no brush\n");
		}

		if (br) {
		    SelectClipRgn(dc, NULL);

		    for (i=0; i<numRects; i++) {
			/*
			 * always clear - even if not interested in exposes
			 */
			FillRect(dc, &(pRect[i]), br);
		    }
		}
#ifndef CACHE_LAST_WM_PAINT_BRUSH
		if (isPrivateBrush) {
		    _DeleteBrush(br, __LINE__);
		}
#endif

#ifndef CACHE_LAST_WM_PAINT_DC
		ReleaseDC(hWnd, dc);
#endif

#if defined(CACHE_LAST_WM_PAINT_DC) && defined(USE_PAINT_MUTEX)
		ReleaseMutex(hPaintMutex);
#endif
	    }

	    if (wantExpose) {
		for (i=0; i<numRects; i++) {
		    int final = 0;

		    if ((msgType == __WM_GEXPOSE)
		     && (i == (numRects - 1))) {
			final = 1;
		    }
		    enqEvent(ENQ_AT_END, ExposureMask, hWnd, msgType, final, 
			     pRect[i].left, pRect[i].top, 
			     pRect[i].right - pRect[i].left,
			     pRect[i].bottom - pRect[i].top,
			     EV_NOTIME);
		}
	    }
	}

	return numRects;
}

#ifdef THIS_DOES_NOT_AVOID_DOUBLE_REPAINTS

BOOL CALLBACK recursiveExposeGenerator(hChild, param)
    HWND hChild;
    LPARAM param;
{
	__generateExposes(hChild, NULL, param, 0);
	return TRUE;
}

static void
__generateRecursiveExposes(hWnd, msgType)
    HWND hWnd;
{
	EnumChildWindows(hWnd, recursiveExposeGenerator, msgType);
}

#endif /* sigh */


static
getModifiers()
{
    int modifiers = 0;

    if (GetKeyState(VK_SHIFT) & 0x8000)
	modifiers |= ShiftMask;
    if (GetKeyState(VK_CONTROL) & 0x8000)
	modifiers |= ControlMask;
#if 0
    if (GetKeyState(VK_RMENU) & 0x8000)
	modifiers |= RightAltMask;
    if (GetKeyState(VK_LMENU) & 0x8000)
	modifiers |= LeftAltMask;
#else
    if (GetKeyState(VK_MENU) & 0x8000)
	modifiers |= LeftAltMask;
#endif
    if (GetKeyState(VK_LBUTTON) & 0x8000)
	modifiers |= Button1Mask;
    if (GetKeyState(VK_MBUTTON) & 0x8000)
	modifiers |= Button2Mask;
    if (GetKeyState(VK_RBUTTON) & 0x8000)
	modifiers |= Button3Mask;

    if ((modifiers & AnyButtonMask) == 0) {
	__eatingMouseEvents = 0;
    }
    return modifiers;
}

#ifdef HANDLE_VIEWGRAVITY

struct gravityCallBackInfo {
    HWND parent;
    int  currW, currH;
    int  newW, newH;
    int  parentWinX, parentWinY;
};

static BOOL CALLBACK
gravityEnumeratorCallBack(hChild, iP)
    HWND hChild;
    struct gravityCallBackInfo *iP;
{
    localWindowInfo *lI;
    int gravity;

    if (lI = GETLOCALWINDOWINFOPTR(hChild)) {
	if ((gravity = lI->viewGravity) != GRAVITY_NONE) {
	    RECT oldChildRct, oldChildWinRct;
	    RECT newChildRct;
	    int deltaW, deltaH;
	    int anyChange = 0;

	    /*
	     * get its current bounds ...
	     */
	    GetClientRect(hChild, &oldChildRct);
	    deltaW = iP->newW - iP->currW;
	    deltaH = iP->newH - iP->currH;

	    GetWindowRect(hChild, &oldChildWinRct);
	    oldChildRct.left = oldChildWinRct.left - iP->parentWinX;
	    oldChildRct.top =  oldChildWinRct.top - iP->parentWinY;

	    newChildRct = oldChildRct;
	    switch (gravity) {
#if 0
		case GRAVITY_N:
		    fprintf(stderr, "WinWorkstation [info]: Child %x: North gravity\n", hChild);
		    /*
		     * shift to the bottom, for a constant
		     * distance from the bottom edge;
		     */
		    newChildRct.bottom += deltaH;
		    anyChange = 1;
		    break;

		case GRAVITY_W:
		    fprintf(stderr, "WinWorkstation [info]: Child %x: West gravity\n", hChild);
		    /*
		     * shift to the right, for a constant
		     * distance from the right edge;
		     */
		    newChildRct.right += deltaW;
		    anyChange = 1;
		    break;
#endif
		case GRAVITY_S:
		    fprintf(stderr, "WinWorkstation [info]: Child %x: South gravity\n", hChild);
		    /*
		     * shift to the bottom, for a constant
		     * distance from the bottom edge;
		     */
		    newChildRct.top += deltaH;
		    newChildRct.bottom  += deltaH;
		    anyChange = 1;
		    break;

		case GRAVITY_E:
		    fprintf(stderr, "WinWorkstation [info]: Child %x: East gravity\n", hChild);
		    /*
		     * shift to the right, for a constant
		     * distance from the right edge;
		     */
		    newChildRct.right += deltaW;
		    newChildRct.left  += deltaW;
		    anyChange = 1;
		    break;

		case GRAVITY_SE:
		    fprintf(stderr, "WinWorkstation [info]: Child %x: SouthEast gravity\n", hChild);
		    /*
		     * shift to the bottom-right, for a constant
		     * distance from the bottom-right edge;
		     */
		    newChildRct.right += deltaW;
		    newChildRct.left  += deltaW;
		    newChildRct.top += deltaH;
		    newChildRct.bottom  += deltaH;
		    anyChange = 1;
		    break;

		default:
		    DPRINTF(("Child %x: other gravity\n", hChild));
		    break;
	    }

	    if (anyChange) {
		if ((newChildRct.left < 0)
		 || (newChildRct.top < 0)
		 || (newChildRct.right >= iP->newW)
		 || (newChildRct.bottom >= iP->newH)) {
		    fprintf(stderr, "WinWorkstation [info]: dont move child to: %d/%d -> %d/%d\n",
				    newChildRct.left, newChildRct.top,
				    newChildRct.right, newChildRct.bottom);
		} else {
		    fprintf(stderr, "WinWorkstation [info]: move child from %d/%d->%d/%d to: %d/%d->%d/%d\n",
				    oldChildRct.left, oldChildRct.top,
				    oldChildRct.right, oldChildRct.bottom,
				    newChildRct.left, newChildRct.top,
				    newChildRct.right, newChildRct.bottom);

		    SetWindowPos(hChild, (HWND)0,
				 newChildRct.left, newChildRct.top,
				 0, 0,
				 /* SWP_NOSENDCHANGING | */
				 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
		}
	    }
	}
    }
    return TRUE;
}
#endif /* HANDLE_VIEWGRAVITY */

static
winEventProcessing(hWnd, message, wParam, lParam, pDefault)
    HWND hWnd;                /* window handle                   */
    UINT message;             /* type of message                 */
    UINT wParam;              /* additional information          */
    LONG lParam;              /* additional information          */
    int *pDefault;
{
    int curButton;
    int x, y, w, h;
    int isNative = 0;
    UINT evTime;

    /* kludge - info is in msg-struct,
     * but lost here. (slightly inexact ... but who cares)
     */
    evTime = lastMSGTime;
    lastMSGTime = 0;
    if (evTime == 0) {
	lastMSGTime = GetTickCount();
    }

/*
    EVENT_PRINTF(("winEvent hWin=0x%x message=0x%x wP=0x%x lP=0x%x\n",
			hWnd, message, wParam, lParam));
*/

    /*
     * messages which are enqueued to be handled by the view thread
     */
    switch (message) {
	case WM_THREAD_CREATEWINDOW:
	    EVENT_PRINTF(("*WM_THREAD_CREATEWINDOW\n"));

	    {
		createWindowInfo *cwi = (createWindowInfo *)(lParam);

		if ((cwi->sequenceNr == wParam) 
		 && (cwi->sequenceNr != INVALIDATED_CWI)) {
		    cwi->sequenceNr = INVALIDATED_CWI;
		    cwi->infoWasRead = 1;
		    if (cwi->newWinHandle == NULL) {
			HANDLE ev;

			cwi->newWinHandle = CreateWindowEx(
						cwi->winStyleBitsEx,
						cwi->className,
						cwi->windowName,
						cwi->winStyleBits,
						cwi->x, cwi->y,
						cwi->dx, cwi->dy,
						cwi->parentHandle,
						NULL,           /* menu */
						(HANDLE) __getHInstance(),
						cwi->localWindowInfo 
					       );

			if (cwi->newWinHandle == NULL) {
			    fprintf(stderr, "WinWorkstation [info]: CreateWindow failed: %d\n", GetLastError());
			}
			ev = cwi->hCreateEvent;
			if (ev) {
			    SetEvent(ev);
			}
		    }
		} else {
		    DPRINTF(("obsolete createWindow message %x ignored\n", cwi->sequenceNr));
		}
	    }
	    *pDefault = 0;
	    return 0;

	case WM_THREAD_DESTROYWINDOW:
	    {
		HBRUSH br;

		if (br = GetWindow_viewBgBrush(hWnd)) {
		    SetWindow_viewBgBrush(hWnd, 0);
		    _DeleteBrushIfNotInCache(br, __LINE__);
		}
	    }

#ifdef CACHE_LAST_DC
	    if (lastGcData && (lastGcHWIN == hWnd)) {
		_releaseDC(lastGcData);
	    }
#endif
#ifdef CACHE_LAST_WM_PAINT_DC
	    if (last_wm_paint_dc && (last_wm_paint_win == hWnd)) {
		ReleaseDC(hWnd, last_wm_paint_dc);
		last_wm_paint_win = last_wm_paint_dc = 0;
	    }
#endif
	    if (! DestroyWindow(hWnd)) {
		DPRINTF(("DestroyWindow ERROR\n"));
	    } else {
#ifdef COUNT_RESOURCES
		__cnt_createWindows--;
		RESPRINTF(("DestroyWindow -> %d\n", __cnt_createWindows));
#endif
	    }
	    *pDefault = 0;
	    return 0;

	case WM_CREATE:
	    {
		CREATESTRUCT *lpcs = (LPCREATESTRUCT) lParam; // structure with creation data

		EVENT_PRINTF(("WM_CREATE\n"));

		SETLOCALWINDOWINFOPTR(hWnd, lpcs->lpCreateParams);
#ifdef SUPERDEBUG
		if (GETLOCALWINDOWINFOPTR(hWnd) != lpcs->lpCreateParams) {
		    PRINTF(("SETLOCALWINDOWINFOPTR ERROR\n"));
		}
#endif
		*pDefault = 0;
#ifdef COUNT_RESOURCES
		__cnt_createWindows++;
		RESPRINTF(("CreateWindows %d\n",__cnt_createWindows));
#endif
	    }
	    break;

	case WM_CLOSE:
	    EVENT_PRINTF(("WM_CLOSE\n"));
	    if (! destroyWin) {
		enqEvent(ENQ_AT_END, 0, hWnd, WM_CLOSE, wParam, 0, 0, 0, 0, EV_NOTIME);
	    } else {
		destroyWin = 0;
		{
		    HBRUSH br;

		    if (br = GetWindow_viewBgBrush(hWnd)) {
			SetWindow_viewBgBrush(hWnd, 0);
			_DeleteBrushIfNotInCache(br, __LINE__);
		    }
		}
#ifdef CACHE_LAST_DC
		if (lastGcData && (lastGcHWIN == hWnd)) {
		    _releaseDC(lastGcData);
		}
#endif
#ifdef CACHE_LAST_WM_PAINT_DC
		if (last_wm_paint_dc && (last_wm_paint_win == hWnd)) {
		    ReleaseDC(hWnd, last_wm_paint_dc);
		    last_wm_paint_win = last_wm_paint_dc = 0;
		}
#endif
		if (! DestroyWindow(hWnd)) {
		    DPRINTF(("DestroyWindow ERROR\n"));
		} else {
#ifdef COUNT_RESOURCES
		    __cnt_createWindows--;
		    RESPRINTF(("DestroyWindow -> %d\n", __cnt_createWindows));
#endif
		}
	    }
	    *pDefault = 0;
	    return 0;
	    break;

	case WM_DESTROY:
	    {
		char *n = 0;
		localWindowInfo *lI;

		lI = GETLOCALWINDOWINFOPTR(hWnd);
		EVENT_PRINTF(("WM_DESTROY %x li=%x\n", hWnd, lI));

#ifdef TOPWINDOWCLASS
		if (lI) {
		    if (lI->flag & LI_TOPWIN) {
			n = (char*) malloc(200);
			GetClassName(hWnd,n,200);
		    }
		    /* freeing now done in other thread */
		}
#endif
		SETLOCALWINDOWINFOPTR(hWnd, 0);

#ifdef FREE_LI_IN_STX_PROCESS
		enqEvent(ENQ_AT_END, 0, hWnd, WM_DESTROY, wParam, (int)n, (int)lI, 0, 0, EV_NOTIME);
#else
		if (lI) {
		    free(lI);
		}
		enqEvent(ENQ_AT_END, 0, hWnd, WM_DESTROY, wParam, (int)n, 0, 0, 0, EV_NOTIME);
#endif
		*pDefault = 0;
		return 0;
	    }
	    break;

	case WM_GETMINMAXINFO:
	    {
		int minW, maxW;
		localWindowInfo *lI;
		LPMINMAXINFO lpmmi;

		lpmmi = (LPMINMAXINFO) lParam;

		EVENT_PRINTF2(("WM_GETMINMAXINFO handle=%x got min: %d/%d\n",
			    hWnd,
			    lpmmi->ptMinTrackSize.x,
			    lpmmi->ptMinTrackSize.y));

		lI = GETLOCALWINDOWINFOPTR(hWnd);
		if (lI && (lI->minWidth) && (lI->maxWidth)) {
		  lpmmi->ptMaxSize.x = lI->maxWidth;
		  lpmmi->ptMaxSize.y = lI->maxHeight;
		  lpmmi->ptMaxTrackSize.x = lI->maxWidth;
		  lpmmi->ptMaxTrackSize.y = lI->maxHeight;
		  lpmmi->ptMinTrackSize.x = lI->minWidth;
		  lpmmi->ptMinTrackSize.y = lI->minHeight;

		  EVENT_PRINTF3(("WM_GETMINMAXINFO handle=%x return min: %d/%d max: %d/%d\n",
				  hWnd,
				  lI->minWidth, lI->minHeight,
				  lI->maxWidth, lI->maxHeight));
		}
		*pDefault = 0;
	    }
	    break;

	case WM_SETCURSOR:
	    {
		HCURSOR curs;
		int hitCode;

		hitCode = LOWORD(lParam);
		if (hitCode != HTCLIENT) {
		    /*
		     * not in client area ...
		     */
		    return 0;
		}
		DDPRINTF(("WM_SETCURSOR\n"));
		curs = GetWindow_Cursor(hWnd);
		if (curs) {
		    SetCursor(curs);
		    *pDefault = 0;
		    return 1;
		}
	    }
	    return 0;

	case WM_WINDOWPOSCHANGED:
	    if (hWnd == __rootWinSpezial) {
		/* can this happen ? */
		*pDefault = 1;
		return 0;
	    }

	    if (! inMove && (__currentCapture == CAPTURE_NONE)) {
		POINT p;
		HWND hWndChild,hWndTemp;

		p.x = evRootX;
		p.y = evRootY;
		ScreenToClient(hWnd, &p);
		hWndChild = hWnd; //hWndChild = ChildWindowFromPoint(hWnd,p);
		do {
		    hWndTemp = hWndChild;
		    hWndChild = ChildWindowFromPointEx(hWndTemp,p,CWP_SKIPINVISIBLE|CWP_SKIPDISABLED|CWP_SKIPTRANSPARENT);
		} while ((hWndChild) && (hWndChild != hWndTemp));

		if (hWndChild /*&& (hWndChild != hWnd)*/) {
		    if (hWndChild != __currentPointerView) {
			int modifiers;

			modifiers = getModifiers();
			if (__currentPointerView) {
			    if (inSizeMove) {
				if (needDelayedMouseLeaveWindow == NULL) {
				    needDelayedMouseLeaveWindow = __currentPointerView;
				}
			    } else {
				if (GetWindow_eventMask(__currentPointerView) & LeaveWindowMask)
				    enqEvent(ENQ_AT_END, LeaveWindowMask, __currentPointerView, __WM_MOUSELEAVE, 0, -1, -1, 0, modifiers, EV_NOTIME);
			    }
			    __currentPointerView = 0;
			}
			if (inSizeMove) {
			    needDelayedMouseEnterWindow = hWndChild;
			    delayedMouseEnterX = evRootX;
			    delayedMouseEnterY = evRootY;
			} else {
			    if (GetWindow_eventMask(hWndChild) & EnterWindowMask)
				enqEvent(ENQ_AT_END, EnterWindowMask, hWndChild, __WM_MOUSEENTER, 0, evRootX, evRootY, 0, modifiers, EV_NOTIME);
			    //SetFocus(hWndChild);
			}
			__currentPointerView = hWndChild;
		    }
		}
	    }

	    /*
	     * ignore child window messages ...
	     */
	    if (GetParent(hWnd) != NULL) {
		break;
	    }

	    {
		WINDOWPOS *wp = (WINDOWPOS *)lParam;
		RECT rct, dRect;
		int x, y, w, h;
		int dW, dH;

#if 0
		dRect.left = 0;
		dRect.top = 0;
		dRect.right = 100;
		dRect.bottom = 100;
		winStyleBits = GetWindowLong(hWnd, GWL_STYLE);
		winExStyleBits = GetWindowLong(hWnd, GWL_EXSTYLE);
		AdjustWindowRectEx(&dRect, winStyleBits, 0, winExStyleBits);
		dW = dRect.right - dRect.left;
		dH = dRect.bottom - dRect.top;
#endif
		GetClientRect(hWnd, &rct);

		x = rct.left;
		y = rct.top;
		w = rct.right - rct.left;
		h = rct.bottom - rct.top;

		EVENT_PRINTF(("WM_WINDOWPOSCHANGED ["));

		if (wp->flags & SWP_NOSIZE) {
		    EVENT_PRINTF(("SWP_NOSIZE "));
		}
		if (wp->flags & SWP_NOMOVE) {
		    EVENT_PRINTF(("SWP_NOMOVE "));
		}
		if (wp->flags & SWP_HIDEWINDOW) {
		    EVENT_PRINTF(("SWP_HIDEWINDOW "));
		}
		if (wp->flags & SWP_SHOWWINDOW) {
		    EVENT_PRINTF(("SWP_SHOWWINDOW "));
		}

		EVENT_PRINTF(("] %d/%d %d/%d flags:%x ->",
				wp->x, wp->y, wp->cx, wp->cy, wp->flags));
		EVENT_PRINTF(("  client rect now: %d/%d %d/%d\n",x, y, w, h));

		if ((w == 0) && (h == 0)) {
		    /*
		     * iconified ...
		     */
		    if (! GetWindow_iconified(hWnd)) {
			SetWindow_iconified(hWnd, 1);
			DPRINTF(("WM_WINDOWPOSCHANGED to iconified flags:%x\n", wp->flags));
			enqEvent(ENQ_AT_END, 0, hWnd, __WM_ICONIFIED, 1, 0, 0, 0, 0, EV_NOTIME);
		    }
		} else {
		    if (GetWindow_iconified(hWnd)) {
			SetWindow_iconified(hWnd, 0);
			DPRINTF(("WM_WINDOWPOSCHANGED to deiconified flags:%x\n", wp->flags));
			enqEvent(ENQ_AT_END, 0, hWnd, __WM_ICONIFIED, 0, 0, 0, 0, 0, EV_NOTIME);
		    }
		    if (!(wp->flags & SWP_NOSIZE) 
		     || !(wp->flags & SWP_NOMOVE)) {
			enqEvent(ENQ_AT_END, 0, hWnd, WM_WINDOWPOSCHANGED, wParam, x, y, w, h, EV_NOTIME);
			/*
			 * remember the current
			 * window and size;
			 * This allows the backend to ignore
			 * intermediate events.
			 */
			lastPos_win = 0;
			lastPos_w = w;
			lastPos_h = h;
			lastPos_x = x;
			lastPos_y = y;
			lastPos_win = hWnd;
		    }
		}
	    }
	    *pDefault = 0;
	    return 0;

	case WM_ERASEBKGND:
	    EVENT_PRINTF(("WM_ERASEBKGND\n"));
	    if (isNative) {
		return 0;
	    }
	    if (GetWindow_flag(hWnd) & LI_INPUTWIN) {
		*pDefault = 0;
		return 0;
	    }
	    if (hWnd == __rootWinSpezial) {
		*pDefault = 0;
		return 0;
	    }
#if 0
	    /*
	     * seems to solve the activate-no-update problem,
	     * but leads to double-repaint ...
	     */
	    __generateExposes(hWnd, NULL, WM_PAINT, 1);
	    *pDefault = 0;
	    return 0;
#endif
	    /* THIS_WORKS */
	    *pDefault = 0;
	    return 1;
	    break;

	case WM_PAINT:
	    EVENT_PRINTF(("WM_PAINT\n"));
	    if (isNative) {
		return 0;
	    }

	    if (((GetWindow_flag(hWnd) & LI_INPUTWIN) == 0)
	     && (hWnd != __rootWinSpezial)) {
#ifdef LATE_WM_PAINT
		switch (__generateExposes(hWnd, NULL, WM_PAINT, 0)) 
#else
		switch (__generateExposes(hWnd, NULL, WM_PAINT, 1)) 
#endif
		{
		    case -1:  /* error */
			*pDefault = 0;
			break;
		    case 0:   /* nothing generated */
		    default:  /* generated events */
			break;
		}
# if 0
		*pDefault = 0;
		return 1;
# endif
	    } else {
# ifdef THIS_MAKES_GUI_PAINTER_START_SLOW
		*pDefault = 0;
# endif
	    }
	    return 0;

	case WM_SIZE:
	    EVENT_PRINTF(("WM_SIZE\n"));
	    *pDefault = 0;
	    return 0;
	    break;

	case WM_DROPFILES:
	    EVENT_PRINTF(("WM_DROPFILES\n"));
	    enqEvent(ENQ_AT_END, 0, hWnd, message, wParam, 0, 0, 0, 0, EV_NOTIME);
	    break;

	case WM_SHOWWINDOW:
	    EVENT_PRINTF(("WM_SHOWWINDOW\n"));
	    enqEvent(ENQ_AT_END, 0, hWnd, message, wParam, 0, 0, 0, 0, EV_NOTIME);
	    if (isNative) {
		return 0;
	    }
	    *pDefault = 0;
	    break;

	case WM_MOUSEACTIVATE :
	    EVENT_PRINTF(("WM_MOUSEACTIVATE h=%x parent=%x\n", hWnd, GetParent(hWnd)));
	    /*
	     * ignore child window messages ...
	     */
	    if (GetParent(hWnd)) {
		return 0;
	    }

	    *pDefault = 0;
	    if ((hWnd == __rootWinSpezial) || (hWnd == GetActiveWindow())) {
		DDPRINTF(("NOACTIVATE (root or already)\n"));
		return MA_NOACTIVATE;
	    }
	    if (LOWORD(lParam) != HTCLIENT) {
		DDPRINTF(("ACTIVATE (not client)\n"));
		return MA_ACTIVATE;
	    }
	    if (__activateOnClick) {
		if (__ignoreButtonPressOnActivate) {
		    DDPRINTF(("ACTIVATE_EAT\n"));
		    /*
		     * windows is sooo stupid:
		     * it will send me a MOTION soon after, with the
		     * to the click location, with the buttonState down.
		     * The app will interpret that as a pressed motion and
		     * do a selection (if in a textview).
		     * To prevent this, we set a flag here, that the following
		     * motion is to be suppressed.
		     * Until the mouse is released.
		     */
		    __eatingMouseEvents = 1;
		    return MA_ACTIVATEANDEAT;
		}
		DDPRINTF(("ACTIVATE\n"));
		return MA_ACTIVATE;
	    }
	    DDPRINTF(("NO_ACTIVATE\n"));
	    return MA_NOACTIVATE;

	case WM_NCHITTEST:
	    EVENT_PRINTF2(("WM_NCHITTEST\n"));
	    if (hWnd == __rootWinSpezial) {
		*pDefault = 0;
		return HTCLIENT;
	    }
	    break;

	case WM_NCACTIVATE:
	    EVENT_PRINTF(("WM_NCACTIVATE\n"));
	    break;

	case WM_ACTIVATE:
	    CPRINTF(("WM_ACTIVATE %s h=%x\n",
			    LOWORD(wParam) ? "active" : "inactive",
			    hWnd));
	    //if (! __activateOnClick)
	    {
		switch (LOWORD(wParam)) {
		    case WA_INACTIVE:
			EVENT_PRINTF2(("WM_ACTIVATE inactive h=%x\n", hWnd));
			break;

		    case WA_ACTIVE:
			EVENT_PRINTF2(("WM_ACTIVATE active h=%x\n", hWnd));

#ifdef THIS_DOES_NOT_AVOID_DOUBLE_REPAINTS
			__generateRecursiveExposes(hWnd, WM_PAINT);
#endif /* sigh */

#if 0
			if (__currentCapture == CAPTURE_NONE) {
			    POINT p;
			    HWND hWndChild,hWndTemp;

			    p.x = evRootX;
			    p.y = evRootY;
			    ScreenToClient(hWnd, &p);
			    hWndChild = hWnd; //hWndChild = ChildWindowFromPoint(hWnd,p);
			    do {
				hWndTemp = hWndChild;
				hWndChild = ChildWindowFromPointEx(hWndTemp,p,CWP_SKIPINVISIBLE|CWP_SKIPDISABLED|CWP_SKIPTRANSPARENT);
			    } while ((hWndChild) && (hWndChild != hWndTemp));

			    /*printf("WM_ACTIVATE active h=%x p=%d.%d in %x\n", hWnd,p.x,p.y,hWndChild);*/
			    if (hWndChild && (hWndChild != __currentPointerView)) {
				int modifiers;

				modifiers = getModifiers();
				if (__currentPointerView) {
				    if (GetWindow_eventMask(__currentPointerView) & LeaveWindowMask)
					enqEvent(ENQ_AT_END, LeaveWindowMask, __currentPointerView, __WM_MOUSELEAVE, 0, -1, -1, 0, modifiers, EV_NOTIME);
				    __currentPointerView = 0;
				}
				if (GetWindow_eventMask(hWndChild) & EnterWindowMask)
				    enqEvent(ENQ_AT_END, EnterWindowMask,hWndChild, __WM_MOUSEENTER, 0, evRootX, evRootY, 0, modifiers, EV_NOTIME);
				//SetFocus(hWndChild);
				__currentPointerView = hWndChild;
			    }
			}
#endif
			break;

		    case WA_CLICKACTIVE:
			EVENT_PRINTF2(("WM_ACTIVATE clkactive h=%x\n", hWnd));
			break;

		    default:
			EVENT_PRINTF2(("WM_ACTIVATE ? h=%x\n", hWnd));
			break;
		}
		enqEvent(ENQ_AT_END, 0, hWnd, WM_ACTIVATE, wParam, 0, 0, 0, 0, EV_NOTIME);
		if (isNative) {
		    *pDefault = 1;
		    return 0;
		}
		/*
		 * mhmh - seems to suppress WM_PAINTS for the activated view
		 */
#if 0
		*pDefault = 0;
		return 0;
#endif
#if 0
		*pDefault = 0;
		return 1;
#endif
	    }
	    break;

	case WM_SYSCHAR:
	    EVENT_PRINTF2(("WM_SYSCHAR h=%x %x\n", hWnd, wParam));
	    goto commonChar;

	case WM_CHAR:
	    EVENT_PRINTF2(("WM_CHAR h=%x %x\n", hWnd, wParam));
	commonChar:
	    if (isNative) {
		return 0;
	    }

	    {
		HWND destWindow;
		int evMask;

		if (__focusFollowsMouse) {
		    destWindow = __currentPointerView;
		    if (destWindow == 0) {
			destWindow = hWnd;
		    }
		} else {
		    destWindow = hWnd;
		}

		evMask = GetWindow_eventMask(destWindow);
		if (evMask & (KeyPressMask | KeyReleaseMask)) {
		    POINT p;
		    int modifiers;

		    modifiers = getModifiers();

		    p.x = evRootX;
		    p.y = evRootY;
		    ScreenToClient(destWindow, &p);
		    x = p.x;
		    y = p.y;

		    if (evMask & KeyPressMask)
			enqEvent(ENQ_AT_END, KeyPressMask, destWindow, WM_KEYDOWN, wParam, x, y, lParam, modifiers | TRANSLATED_KEY, evTime);
		    if (evMask & KeyReleaseMask)
			enqEvent(ENQ_AT_END, KeyReleaseMask, destWindow, WM_KEYUP, wParam, x, y, lParam, modifiers | TRANSLATED_KEY, evTime);
		}
	    }
	    *pDefault = 0;
	    return 0;

	case WM_KEYUP:
	    EVENT_PRINTF2(("WM_KEYUP h=%x %x\n", hWnd, wParam));
	    goto commonKeyUp;

	case WM_SYSKEYUP:
	    EVENT_PRINTF2(("WM_SYSKEYUP %x\n, wParam"));

	commonKeyUp:
	    if (isNative) {
		return 0;
	    }
	    {
		HWND destWindow;

		if (__focusFollowsMouse) {
		    destWindow = __currentPointerView;
		    if (destWindow == 0) {
			destWindow = hWnd;
		    }
		} else {
		    destWindow = hWnd;
		}

		if (GetWindow_eventMask(destWindow) & KeyReleaseMask) {
		    POINT p;
		    int modifiers;

		    p.x = evRootX;
		    p.y = evRootY;
		    ScreenToClient(destWindow, &p);
		    x = p.x;
		    y = p.y;

		    modifiers = getModifiers();
		    enqEvent(ENQ_AT_END, KeyReleaseMask, destWindow, message, wParam, x, y, lParam, modifiers, evTime);
		}
	    }
	    *pDefault = 0;
	    return 0;

	case WM_SYSKEYDOWN:
	    EVENT_PRINTF2(("WM_SYSKEYDOWN %x\n, wParam"));
	    goto commonKey;

	case WM_KEYDOWN:
	    EVENT_PRINTF2(("WM_KEYDOWN h=%x %x\n", hWnd, wParam));

	commonKey:
	    if (isNative) {
		return 0;
	    }

#ifdef EXIT_WITH_3_CTRL_Cs
	    if ((msg.wParam == 'c')
	     && (GetKeyState(VK_CONTROL) & 0x8000)) {
		/* CTRL-C */
		if (ctrl_c_count++ > 3)  /* for debugging */
		    exit(1);
	    } else {
		if (msg.wParam != VK_CONTROL)
		    ctrl_c_count = 0;
	    }
#endif
#ifdef RELEASE_CAPTURE_WITH_3_ESCAPESs
	    if (msg.wParam == VK_ESCAPE)) {
		/* ESCAPE */
		if (escape_count++ > 3) { 
		    __currentCapture = CAPTURE_NONE;
		    ReleaseCapture();
		    CPRINTF(("ReleaseCapture <ESC>\n"));
		}
	    } else {
		escape_count = 0;
	    }
#endif
	    /* FALL INTO */

	    {
		HWND destWindow;

		if (__focusFollowsMouse) {
		    destWindow = __currentPointerView;
		    if (destWindow == 0) {
			destWindow = hWnd;
		    }
		} else {
		    destWindow = hWnd;
		}

		if (GetWindow_eventMask(destWindow) & KeyPressMask) {
		    POINT p;
		    int modifiers;

		    p.x = evRootX;
		    p.y = evRootY;
		    ScreenToClient(destWindow, &p);
		    x = p.x;
		    y = p.y;

		    modifiers = getModifiers();
		    enqEvent(ENQ_AT_END, KeyPressMask, destWindow, message, wParam, x, y, lParam, modifiers, evTime);
		}
	    }
	    *pDefault = 0;
	    return 0;

#ifdef WM_MOUSEWHEEL
	case WM_MOUSEWHEEL:
	    {
		HWND destWindow;
		int modifiers;
		int delta;
		POINT p;
                
		if (__focusFollowsMouse) {
		    destWindow = __currentPointerView;
		    if (destWindow == 0) {
			destWindow = hWnd;
		    }
		} else {
		    destWindow = hWnd;
		}


		EVENT_PRINTF(("WM_MOUSEWHEEL h=%x wP=%x lP=%x\n", hWnd, wParam, lParam));
		if (GetWindow_eventMask(destWindow) & KeyPressMask) {
		    modifiers = getModifiers();
		    p.x = evRootX;
		    p.y = evRootY;
		    ScreenToClient(destWindow, &p);
		    x = p.x;
		    y = p.y;
		    delta = evTime - lastMouseWheelTime;
		    if (evTime < lastMouseWheelTime) {
			delta = (0xFFFFFFFF - lastMouseWheelTime) + evTime;
		    }
		    enqEvent(ENQ_AT_END, 0, destWindow, message, wParam, x, y, lParam, modifiers, delta);
		}
		lastMouseWheelTime = evTime;
	    }
	    *pDefault = 0;
	    return 0;
#endif

	case WM_MOUSEMOVE:
	    EVENT_PRINTF3(("WM_MOUSEMOVE h=%x\n", hWnd));
	    if (isNative) {
		return 0;
	    }

	    {
		short x, y;
		int modifiers;

		x = (int)(short)LOWORD(lParam);
		y = (int)(short)HIWORD(lParam);

		modifiers = getModifiers();

		if (__currentCapture == CAPTURE_NONE) {
		    if (hWnd != __currentPointerView) {
			HWND prevPointerView;

			prevPointerView = __currentPointerView;
			if (prevPointerView) {
			    if (inSizeMove) {
				if (needDelayedMouseLeaveWindow == NULL) {
				    needDelayedMouseLeaveWindow = __currentPointerView;
				}
			    } else {
				if (GetWindow_eventMask(__currentPointerView) & LeaveWindowMask)
				    enqEvent(ENQ_AT_END, LeaveWindowMask, __currentPointerView, __WM_MOUSELEAVE, 0, -1, -1, 0, modifiers, evTime);
			    }
			    __currentPointerView = 0;
			}

#if 0
			if (__activateOnClick) {
			    /*
			     * only send mouseEnter if the window belongs to the current
			     * active window
			     */
			    if (GetTopParent(prevPointerView) == GetTopParent(hWnd)) {
			    }
			}
#endif
			if (inSizeMove) {
			    needDelayedMouseEnterWindow = hWnd;
			    delayedMouseEnterX = x;
			    delayedMouseEnterY = y;
			} else {
			    if (GetWindow_eventMask(hWnd) & EnterWindowMask)
				enqEvent(ENQ_AT_END, EnterWindowMask, hWnd, __WM_MOUSEENTER, 0, x, y, 0, modifiers, evTime);
			}
			__currentPointerView = hWnd;
		    }
		    DDPRINTF(("MouseMove %x\n", __currentPointerView));
		} else {
		    CPRINTF(("MouseMove Capture %x\n", __currentPointerView));
		}

		if ((GetWindow_eventMask(__currentPointerView) & PointerMotionMask)
		 || (modifiers & AnyButtonMask)) {
		    if (! __eatingMouseEvents) {
			enqEvent(ENQ_AT_END, PointerMotionMask, __currentPointerView, WM_MOUSEMOVE, wParam, x, y, 0, modifiers, evTime);
		    }
		}
	    }
	    *pDefault = 0;
	    return 0;

	case WM_LBUTTONUP:
	    curButton = Button1;
	    goto commonButtonUp;
	case WM_MBUTTONUP:
	    curButton = Button2;
	    goto commonButtonUp;
	case WM_RBUTTONUP:
	    curButton = Button3;
commonButtonUp:
	    if (isNative) {
		return 0;
	    }

	    if (__currentCapture == curButton /* CAPTURE_IMPLICIT*/) {
		__currentCapture = CAPTURE_NONE;
		ReleaseCapture();
		CPRINTF(("ReleaseCapture <BUTTONUP>\n"));
	    }
	    EVENT_PRINTF3(("WM_BUTTONUP h=%x pos=%d/%d\n",
			   hWnd, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam)));


	    if (GetWindow_eventMask(hWnd) & ButtonReleaseMask) {
		int modifiers;

		if (__eatingMouseEvents) {
		    __eatingMouseEvents = 0;
		} else {
		    modifiers = getModifiers();
		    enqEvent(ENQ_AT_END, ButtonReleaseMask, hWnd, 
			     message, wParam, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam),
			     wParam, modifiers, evTime);
		}
	    }
	    *pDefault = 0;
	    break;

	case WM_LBUTTONDOWN:
	    curButton = Button1;
	    goto commonButtonDown;
	case WM_MBUTTONDOWN:
	    curButton = Button2;
	    goto commonButtonDown;
	case WM_RBUTTONDOWN:
	    curButton = Button3;
commonButtonDown:
	    if (isNative) {
		return 0;
	    }

	    if (__currentCapture == CAPTURE_NONE) {
		__currentCapture = curButton /*CAPTURE_IMPLICIT*/;
		//SetFocus(hWnd);
		SetCapture(hWnd);
		CPRINTF(("SetCapture <BUTTONDOWN>%x\n",hWnd));
	    }
	    goto commonButton;

	case WM_LBUTTONDBLCLK:
	case WM_MBUTTONDBLCLK:
	case WM_RBUTTONDBLCLK:
	    if (isNative) {
		return 0;
	    }
	commonButton:
	    EVENT_PRINTF3(("WM_BUTTONDOWN h=%x pos=%d/%d\n",
			   hWnd, LOWORD(lParam), HIWORD(lParam)));

	    if (GetWindow_eventMask(hWnd) & ButtonPressMask) {
		int modifiers;

		if (__eatingMouseEvents) {
		    __eatingMouseEvents = 0;
		} else {
		    modifiers = getModifiers();
		    enqEvent(ENQ_AT_END, ButtonPressMask, hWnd, 
			     message, wParam, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam),
			     wParam, modifiers, evTime);
		}
	    }
	    *pDefault = 0;
	    break;

	case WM_CAPTURECHANGED:
	    CPRINTF(("WM_CAPTURECHANGED %x to %x\n",hWnd,lParam));
#if 0
	    if ((__currentCapture != CAPTURE_NONE) && (__currentCapture != CAPTURE_EXPLICIT))
	    {
	    }
#endif
	    break;

	case WM_KILLFOCUS:
	    CPRINTF(("WM_KILLFOCUS\n"));
	    //enqEvent(ENQ_AT_END, 0,hWnd, message, wParam, 0, 0, 0, 0, EV_NOTIME);
	    if (isNative) {
		*pDefault = 1;
		return 0;
	    }
	    *pDefault = 0;
	    break;

	case WM_SETFOCUS:
	    CPRINTF(("WM_SETFOCUS %x\n",hWnd));
#if 0
	    //enqEvent(ENQ_AT_END, 0,hWnd, message, wParam, 0, 0, 0, 0, EV_NOTIME);
	    {
		POINT p;
		HWND hWndChild,hWndTemp;

		p.x = evRootX;
		p.y = evRootY;
		ScreenToClient(hWnd, &p);
		hWndChild = hWnd; //hWndChild = ChildWindowFromPoint(hWnd,p);
		do {
		    hWndTemp = hWndChild;
		    hWndChild = ChildWindowFromPointEx(hWndTemp,p,CWP_SKIPINVISIBLE|CWP_SKIPDISABLED|CWP_SKIPTRANSPARENT);
		} while ((hWndChild) && (hWndChild != hWndTemp));

		/*printf("WM_ACTIVATE active h=%x p=%d.%d in %x\n", hWnd,p.x,p.y,hWndChild);*/
		if (hWndChild /*&& (hWndChild != hWnd)*/) {
		    if (hWndChild != __currentPointerView) {
			int modifiers;

			modifiers = getModifiers();
			if (__currentPointerView) {
			    if (GetWindow_eventMask(__currentPointerView) & LeaveWindowMask)
				enqEvent(ENQ_AT_END, LeaveWindowMask, __currentPointerView, __WM_MOUSELEAVE, 0, -1, -1, 0, modifiers, EV_NOTIME);
			    __currentPointerView = 0;
			}
			if (GetWindow_eventMask(hWndChild) & EnterWindowMask)
			    enqEvent(ENQ_AT_END, EnterWindowMask,hWndChild, __WM_MOUSEENTER, 0, evRootX, evRootY, 0, modifiers, EV_NOTIME);
			//SetFocus(hWndChild);
			__currentPointerView = hWndChild;
		    }
		}
	    }

	    if (isNative) {
		*pDefault = 1;
		return 0;
	    }
	    *pDefault = 0;
#endif
	    break;

	case WM_STYLECHANGING:
	    UNHANDLED_EVENT_PRINTF(("WM_STYLECHANGING\n"));
	    break;

	case WM_STYLECHANGED:
	    UNHANDLED_EVENT_PRINTF(("WM_STYLECHANGED\n"));
	    break;

	case WM_GETTEXT:
	    /*EVENT_PRINTF(("WM_GETTEXT %x\n", message));*/
	    break;

	case WM_GETTEXTLENGTH:
	    EVENT_PRINTF(("WM_GETTEXTLENGTH %x\n", message));
	    break;

	case WM_NCCREATE:
	    EVENT_PRINTF(("WM_NCCREATE %x\n", message));
	    break;

	case WM_NCMOUSEMOVE:
	    if (__currentCapture == CAPTURE_NONE) {
		if (__currentPointerView && (hWnd != __currentPointerView)) {
		    if (inSizeMove) {
			if (needDelayedMouseLeaveWindow == NULL) {
			    needDelayedMouseLeaveWindow = __currentPointerView;
			}
		    } else {
			if (GetWindow_eventMask(__currentPointerView) & LeaveWindowMask) {
			    int modifiers;

			    modifiers = getModifiers();
			    enqEvent(ENQ_AT_END, LeaveWindowMask, __currentPointerView, __WM_MOUSELEAVE, 0, -1, -1, 0, modifiers, evTime);
			}
		    }
		    __currentPointerView = 0;
		}
	    }
	    *pDefault = 0;
	    break;

	case WM_PARENTNOTIFY:
	    EVENT_PRINTF2(("WM_PARENTNOTIFY h=%x hChild=%x %d\n",
				hWnd, lParam, LOWORD(wParam)));
	    break;

	case WM_NCLBUTTONDOWN:
	    EVENT_PRINTF(("WM_NCLBUTTONDOWN\n"));
	    if (__shiftedLeftButtonIsLowerWindow) {
		/*
		 * with shift, this is a lower-operation
		 */
		if (GetKeyState(VK_SHIFT) & 0x8000) {
		    SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, 
				 SWP_NOSENDCHANGING |
				 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
		    *pDefault = 0;
		}
	    }
	    break;

	case WM_NCLBUTTONUP:
	    EVENT_PRINTF(("WM_NCLBUTTONUP\n"));
	    break;

	case WM_NCRBUTTONDOWN:
	    EVENT_PRINTF(("WM_NCRBUTTONDOWN\n"));
	    if (__rightButtonIsLowerWindow) {
		/*
		 * this is a lower-operation
		 */
		SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, 
			     SWP_NOSENDCHANGING |
			     SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
		*pDefault = 0;
	    }
	    break;

	case WM_NCRBUTTONUP:
	    EVENT_PRINTF(("WM_NCRBUTTONUP\n"));
	    break;

	case WM_NCMBUTTONDOWN:
	    EVENT_PRINTF(("WM_NCMBUTTONDOWN\n"));
	    break;

	case WM_NCMBUTTONUP:
	    EVENT_PRINTF(("WM_NCMBUTTONUP\n"));
	    break;

	case WM_NCLBUTTONDBLCLK:
	    EVENT_PRINTF(("WM_NCLBUTTONDBLCLK\n"));
	    break;

	case WM_NCRBUTTONDBLCLK:
	    EVENT_PRINTF(("WM_NCRBUTTONDBLCLK\n"));
	    break;

	case WM_NCMBUTTONDBLCLK:
	    EVENT_PRINTF(("WM_NCMBUTTONDBLCLK\n"));
	    break;

	case WM_SETTEXT:
	    EVENT_PRINTF(("WM_SETTEXT\n"));
	    break;

	case 0x88:
	    EVENT_PRINTF(("0x88 (undoc)\n"));
	    break;

	case WM_NCCALCSIZE:
	    EVENT_PRINTF(("WM_NCCALCSIZE\n"));
	    break;

	case WM_NCPAINT:
	    EVENT_PRINTF3(("WM_NCPAINT\n"));

#ifdef THIS_DOES_NOT_WORK
	    /* mhmh - something is drawn, but clipped wrong
	     * and with wrong colors ....
	     */
	    /*
	     * child windows only ...
	     */
	    if (GetParent(hWnd)) {
		int clr;

		clr = GetWindow_bdColor(hWnd);
		if (clr != BlackPixel) {
		    /*
		     * draw it here
		     */
		    HBRUSH br;
		    RECT rect, lineRect;
		    HDC hDC;

		    if (clr == WhitePixel) {
			br = __whiteBrush;
		    } else {
			br = CreateSolidBrush(clr);
		    }

		    GetWindowRect(hWnd, &rect);
		    hDC = GetWindowDC(hWnd);
		    SelectClipRgn(hDC, NULL);

		    lineRect.left = rect.left;
		    lineRect.right = rect.right;
		    lineRect.top = rect.top;
		    lineRect.bottom = rect.top + 1;
		    FillRect(hDC, &lineRect, br);

		    lineRect.bottom = rect.bottom - 1;
		    lineRect.bottom = rect.bottom;
		    FillRect(hDC, &lineRect, br);

		    lineRect.top = rect.top;
		    lineRect.bottom = rect.bottom;
		    lineRect.left = rect.left;
		    lineRect.right = rect.left + 1;
		    FillRect(hDC, &lineRect, br);

		    lineRect.left = rect.right - 1;
		    lineRect.right = rect.right;
		    FillRect(hDC, &lineRect, br);

		    _DeleteBrush(br, __LINE__);
		    ReleaseDC(hWnd, hDC);
		    *pDefault = 0;
		    return 0;
		}
	    }
#endif /* THIS_DOES_NOT_WORK */
	    break;

	case WM_SYSCOMMAND:
	    switch (wParam & ~0x000F) {
		case SC_CLOSE:
		    EVENT_PRINTF2(("WM_SYSCOMMAND SC_CLOSE\n"));
		    break;

		case SC_MOVE:
		    EVENT_PRINTF2(("WM_SYSCOMMAND SC_MOVE\n"));
		    break;

		case SC_RESTORE:
		    EVENT_PRINTF2(("WM_SYSCOMMAND SC_RESTORE\n"));
		    break;

		case SC_SIZE:
		    EVENT_PRINTF2(("WM_SYSCOMMAND SC_SIZE\n"));
		    break;

		default:
		    EVENT_PRINTF2(("WM_SYSCOMMAND %x\n", wParam));
		    break;
	    }
	    break;

	case WM_INITMENU:
	    EVENT_PRINTF(("WM_INITMENU\n"));
	    break;

	case WM_INITMENUPOPUP:
	    EVENT_PRINTF(("WM_INITMENUPOPUP\n"));
	    break;

	case WM_ENTERIDLE:
	    EVENT_PRINTF(("WM_ENTERIDLE\n"));
	    break;

	case WM_ENTERMENULOOP:
	    EVENT_PRINTF(("WM_ENTERMENULOOP\n"));
	    break;

	case WM_EXITMENULOOP:
	    EVENT_PRINTF(("WM_EXITMENULOOP\n"));
	    break;

	case WM_MENUSELECT:
	    EVENT_PRINTF(("WM_MENUSELECT\n"));
	    break;

	case WM_QUIT:
	    UNHANDLED_EVENT_PRINTF(("WM_QUIT\n"));
	    break;

	case WM_NCDESTROY:
	    EVENT_PRINTF(("WM_NCDESTROY\n"));
	    break;

	case WM_QUERYNEWPALETTE:
	    EVENT_PRINTF(("WM_QUERYNEWPALETTE\n"));
	    break;

	case WM_PALETTECHANGED:
	    EVENT_PRINTF(("WM_PALETTECHANGED\n"));
	    break;

	case WM_ACTIVATEAPP:
	    EVENT_PRINTF2(("WM_ACTIVATEAPP %s\n", wParam ? "active" : "inactive"));
	    break;

	case WM_SYSDEADCHAR:
	    EVENT_PRINTF2(("WM_SYSDEADCHAR %x\n, wParam"));
	    break;

	case WM_DEADCHAR:
	    EVENT_PRINTF2(("WM_DEADCHAR %x\n, wParam"));
	    break;

	case WM_PAINTICON:
	    EVENT_PRINTF(("WM_PAINTICON\n"));
	    break;

	case WM_ICONERASEBKGND:
	    EVENT_PRINTF(("WM_ICONERASEBKGND\n"));
	    break;

	case WM_WINDOWPOSCHANGING:
	    EVENT_PRINTF(("WM_WINDOWPOSCHANGING\n"));

#ifdef HANDLE_VIEWGRAVITY
	    /*
	     * any child with a viewGravity ?
	     * (only care for topViews here; 
	     *  child views do it on the smalltalk level ...)
	     */
	    if (GetParent(hWnd) == 0) {
		if (GetParent(hWnd) == 0) {
		    struct gravityCallBackInfo i;
		    RECT rct, dRect;
		    int dW, dH;
		    WINDOWPOS *wp = (WINDOWPOS *)lParam;
		    int winStyleBits, winExStyleBits;

		    if (! (wp->flags & SWP_NOSIZE)) {
			dRect.left = 0;
			dRect.top = 0;
			dRect.right = 100;
			dRect.bottom = 100;
			winStyleBits = GetWindowLong(hWnd, GWL_STYLE);
			winExStyleBits = GetWindowLong(hWnd, GWL_EXSTYLE);
			AdjustWindowRectEx(&dRect, winStyleBits, 0, winExStyleBits);
			dW = dRect.right - dRect.left;
			dH = dRect.bottom - dRect.top;

			GetClientRect(hWnd, &rct);   

			i.parent = hWnd;

			i.currW = rct.right - rct.left;
			i.currH = rct.bottom - rct.top;

			/* wp gives us the new outer (frame) bounds */
			i.newW = wp->cx - (dW - 100);
			i.newH = wp->cy - (dH - 100);

			if ((i.currW != i.newW)
			 || (i.currH != i.newH)) {
			    DPRINTF((stderr, "about to sizeChange old: %d/%d new: %d/%d\n",
				    i.currW, i.currH, i.newW, i.newH));

			    GetWindowRect(hWnd, &rct);
			    i.parentWinX = rct.left - dRect.left;
			    i.parentWinY = rct.top - dRect.top;
			    EnumChildWindows(hWnd, gravityEnumeratorCallBack, (int)(&i));
			}
		    }
		}
	    }
#endif /* HANDLE_VIEWGRAVITY */

	    break;

	case WM_QUERYOPEN:
	    EVENT_PRINTF(("WM_QUERYOPEN\n"));
	    break;

	case WM_QUERYENDSESSION:
	    EVENT_PRINTF(("WM_QUERYENDSESSION\n"));
	    enqEvent(ENQ_AT_END, 0, hWnd, message, wParam, 0, 0, 0, 0, EV_NOTIME);
	    // TODO: enter new event loop
	    // which is left when an endSessionConfirmation
	    // arrives from smalltalk
	    // (either positive or negative).
	    // for now, do not allow leaving
	    // windows as long as ST/X is open.

	    *pDefault = 0;
	    break;

	case WM_DISPLAYCHANGE:
	    EVENT_PRINTF(("WM_DISPLAYCHANGE\n"));
	    enqEvent(ENQ_AT_END, 0, hWnd, message, wParam, 0, 0, 0, 0, EV_NOTIME);
	    break;

	case WM_FONTCHANGE:
	    EVENT_PRINTF(("WM_FONTCHANGE\n"));
	    enqEvent(ENQ_AT_END, 0, hWnd, message, wParam, 0, 0, 0, 0, EV_NOTIME);
	    break;

	case WM_WININICHANGE:
	    EVENT_PRINTF(("WM_WININICHANGE\n"));
	    enqEvent(ENQ_AT_END, 0, hWnd, message, wParam, 0, 0, 0, 0, EV_NOTIME);
	    break;

	case WM_SYSCOLORCHANGE:
	    EVENT_PRINTF(("WM_SYSCOLORCHANGE\n"));
	    enqEvent(ENQ_AT_END, 0, hWnd, message, wParam, 0, 0, 0, 0, EV_NOTIME);
	    break;

	case WM_MOVING:
	    EVENT_PRINTF(("WM_MOVING\n"));
#ifdef DELAY_ENTER_LEAVE_WHILE_IN_SIZE_MOVE
	    inMove = 1;    
#endif
	    break;

	case WM_SIZING:
	    EVENT_PRINTF(("WM_SIZING\n"));
#ifdef DELAY_ENTER_LEAVE_WHILE_IN_SIZE_MOVE
	    inSize = 1;    
#endif
	    break;

	case WM_ENTERSIZEMOVE:
	    EVENT_PRINTF(("WM_ENTERSIZEMOVE\n"));
#ifdef DELAY_ENTER_LEAVE_WHILE_IN_SIZE_MOVE
	    inSizeMove = 1;
#endif
	    break;

	case WM_EXITSIZEMOVE:
	    EVENT_PRINTF(("WM_EXITSIZEMOVE\n"));
	    inSizeMove = inMove = inSize = 0;
	    {
		int modifiers;

		if (needDelayedMouseLeaveWindow || needDelayedMouseEnterWindow) {
		    modifiers = getModifiers();
		}

		if (needDelayedMouseLeaveWindow) {
		    if (GetWindow_eventMask(needDelayedMouseLeaveWindow) & LeaveWindowMask) {
			enqEvent(ENQ_AT_END, LeaveWindowMask, needDelayedMouseLeaveWindow, __WM_MOUSELEAVE, 0, -1, -1, 0, modifiers, EV_NOTIME);
		    }
		    needDelayedMouseLeaveWindow = NULL;
		}
		if (needDelayedMouseEnterWindow) {
		    if (GetWindow_eventMask(needDelayedMouseEnterWindow) & EnterWindowMask) {
			enqEvent(ENQ_AT_END, EnterWindowMask, needDelayedMouseEnterWindow, __WM_MOUSEENTER, 0, delayedMouseEnterX, delayedMouseEnterY, 0, modifiers, EV_NOTIME);
		    }
		    needDelayedMouseEnterWindow = NULL;
		}
	    }
	    break;

	case WM_MOVE:
	    EVENT_PRINTF(("WM_MOVE\n"));
	    break;

	case WM_POWER:
	    UNHANDLED_EVENT_PRINTF(("WM_POWER\n"));
	    enqEvent(ENQ_AT_END, 0, hWnd, message, wParam, 0, 0, 0, 0, EV_NOTIME);
	    break;

	default:
	    UNHANDLED_EVENT_PRINTF(("WinWorkstat [info]: unhandled msg = %x\n", message));
	    break;
    }
    return 0;
}

LONG APIENTRY
MainWndProc(HWND hWnd,UINT message,UINT wParam,LONG lParam);

static void _USERENTRY
dispatchThread(void *arg)
{
    static int th_calls;
    RECT rect;
    MSG msg;
    localWindowInfo *lI;

    _dispatchThreadId = GetCurrentThreadId();
    TH_DPRINTF(("TS %d\n", th_calls++));
    PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);   /* must be */

#ifndef WIN32THREADS
    AttachThreadInput(_dispatchThreadId, _masterThreadId, TRUE);
# if 0
    SetThreadPriority(_masterThread,THREAD_PRIORITY_HIGHEST);
    SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_ABOVE_NORMAL);
# endif
#endif

    GetWindowRect(GetDesktopWindow(), &rect);
    /* allocate localMemory for Window */
    lI = (localWindowInfo *)malloc(sizeof(localWindowInfo));
    if (lI) {
	memset(lI, 0, sizeof(*lI));
    }

    __rootWinSpezial = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT,
				      app_nameRoot, app_nameRoot,
				      WS_POPUP | WS_DISABLED,
				      0, 0,
				      rect.right - rect.left, rect.bottom - rect.top,
				      0, 0, (HANDLE) __getHInstance(), lI);

#if 0
    printf("__rootWinSpezial %x\n",__rootWinSpezial);
#endif
    //ShowWindow(__rootWinSpezial,SW_SHOWNOACTIVATE);
    SetWindowPos(__rootWinSpezial, HWND_BOTTOM, 0, 0, 0, 0, 
		 SWP_NOSENDCHANGING |
		 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
    SetEvent(hCreateEvent);

    for (;;) {
	HANDLE dummy;
	TH_DPRINTF(("TG %d\n", th_calls++));

	/*
	 * if there is no current capture (pointer-grab),
	 * make certain that we do not get stuck in the GetMessage
	 * but instead poll the mouse in 200ms intervals.
	 * This is required to synthesize the MOUSELEAVE message,
	 */
	if (__currentPointerView && (__currentCapture == CAPTURE_NONE)) {
	    while (PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE) == 0) {
		/*
		 * wait for an event; timeout after 200 millis
		 */
		if (MsgWaitForMultipleObjects(0,&dummy,FALSE,200,QS_ALLINPUT) == WAIT_TIMEOUT) {
		    if (! inSizeMove) {
			/*
			 * timeout - see where mouse pointer is
			 * for synthetic leave events.
			 */
			POINT point;

			if (GetCursorPos(&point)) {
			    HWND hWnd = WindowFromPoint(point);

			    if (hWnd) {
				if (GetWindowThreadProcessId(hWnd,0) != GetCurrentThreadId()) {
				    if (inSizeMove) {
					if (needDelayedMouseLeaveWindow == NULL) {
					    needDelayedMouseLeaveWindow = __currentPointerView;
					}
				    } else {
					if (GetWindow_eventMask(__currentPointerView) & LeaveWindowMask)
					    enqEvent(ENQ_AT_END, LeaveWindowMask, __currentPointerView, __WM_MOUSELEAVE, 0, -1, -1, 0, getModifiers(), EV_NOTIME);
				    }
				    __currentPointerView = 0;
				    break;
				}
			    }
			}
		    }
		}
	    }
	}

	GetMessage(&msg, NULL, 0, 0);
	TH_DPRINTF(("TD %d\n", th_calls++));
	destroyWin = 0;

	switch (msg.message) {
#if 0
	    case WM_THREAD_DESTROYWINDOW:
		destroyWin = TRUE;
		msg.message = WM_CLOSE;
		EVENT_PRINTF(("thread WM_THREAD_DESTROYWINDOW %x\n",msg.hwnd));
		break;
#endif

#ifdef SET_FOCUS_IN_WINTHREAD
	    case WM_THREAD_SETFOCUS:
# ifdef xxWIN32THREADS
		if (msg.lParam) {
		    if (AttachThreadInput(_dispatchThreadId,msg.lParam,TRUE) == FALSE)
			PRINTF(("SetFocus AttachThreadInput error %d\n", GetLastError()));
		}
# endif
		EVENT_PRINTF(("threadSetfocus %x\n",msg.wParam));
		if (SetFocus((HWND)msg.wParam) == 0) {
		    DDPRINTF(("SetFocus to %x failed.\n",msg.wParam));
		}
		continue;
#endif

#ifdef SET_CURSOR_IN_WINTHREAD
	    case WM_THREAD_SETCURSOR:
		if (msg.lParam) {
		    POINT p;

		    GetCursorPos(&p);
		    if (WindowFromPoint(p) == msg.hwnd) {
			EVENT_PRINTF(("threadSetCursor %x\n",msg.lParam));
			SetCursor((HCURSOR)msg.lParam);
		    }
		}
		continue;
#endif

#ifdef BEEP_IN_WINTHREAD
	    case WM_THREAD_BEEP:
		MessageBeep( MB_ICONEXCLAMATION);
		continue;
#endif

	    case WM_THREAD_SETCAPTURE:
		if (msg.wParam) {
		    if (__currentPointerView) {
			if (inSizeMove) {
			    if (needDelayedMouseLeaveWindow == NULL) {
				needDelayedMouseLeaveWindow = __currentPointerView;
			    }
			} else {
			    if (GetWindow_eventMask(__currentPointerView) & LeaveWindowMask)
				enqEvent(ENQ_AT_END, LeaveWindowMask, __currentPointerView, __WM_MOUSELEAVE, 0, -1, -1, 0, getModifiers(), EV_NOTIME);
			}
		    }
		    __currentPointerView = msg.wParam;
		    __currentCapture = CAPTURE_EXPLICIT;
		    EVENT_PRINTF(("threadSetCapture %x\n",msg.wParam));
		    SetCapture(__currentPointerView);
		} else {
		    if (__currentPointerView  == __rootWinSpezial) {
			//ShowWindow(__rootWinSpezial, SW_HIDE);
			SetWindowPos(__rootWinSpezial, HWND_BOTTOM, 0, 0, 0, 0,
				     SWP_NOREDRAW | SWP_NOSENDCHANGING | SWP_NOCOPYBITS
				     | SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE
				   /*| SWP_NOZORDER | SWP_NOOWNERZORDER */);
		    }
		    __currentPointerView = 0;
		    __currentCapture = CAPTURE_NONE;
		    EVENT_PRINTF(("threadReleaseCapture\n"));
		    ReleaseCapture();
		}
		if (msg.lParam) {
		    DDPRINTF(("threadSetCursor %x\n",msg.lParam));
		    SetCursor((HCURSOR)msg.lParam);
		}
		continue;

	    case WM_THREAD_CREATEWINDOW:
		EVENT_PRINTF(("*WM_THREAD_CREATEWINDOW %d\n", th_calls));

		{
		    createWindowInfo *cwi = (createWindowInfo *)(msg.lParam);

		    if ((cwi->sequenceNr == msg.wParam) 
		     && (cwi->sequenceNr != INVALIDATED_CWI)) {
			cwi->sequenceNr = INVALIDATED_CWI;
			cwi->infoWasRead = 1;
			if (cwi->newWinHandle == NULL) {
			    HANDLE ev;

			    cwi->newWinHandle = CreateWindowEx(
						    cwi->winStyleBitsEx,
						    cwi->className,
						    cwi->windowName,
						    cwi->winStyleBits,
						    cwi->x, cwi->y,
						    cwi->dx, cwi->dy,
						    cwi->parentHandle,
						    NULL,           /* menu */
						    (HANDLE) __getHInstance(),
						    cwi->localWindowInfo 
						   );
                    
			    if (cwi->newWinHandle == NULL) {
				fprintf(stderr, "WinWorkstation [info]: CreateWindow failed: %d\n", GetLastError());
			    }
			    ev = cwi->hCreateEvent;
			    if (ev) {
				SetEvent(ev);
			    }
			}
		    } else {
			DPRINTF(("obsolete createWindow message %x ignored\n", cwi->sequenceNr));
		    }
		}
		continue;

	    case WM_KEYDOWN:
	    case WM_KEYUP:
	    case WM_SYSKEYDOWN:
	    case WM_SYSKEYUP:
		EVENT_PRINTF(("*WM_keymsg %x\n", msg.wParam));
		if (((msg.wParam >= '0') && (msg.wParam <= 'Z'))
		 || ((msg.wParam >= VK_MULTIPLY) && (msg.wParam <= VK_DIVIDE))
		 || (msg.wParam == VK_SPACE)
		 || (msg.wParam == VK_CAPITAL)
		 /*|| (msg.wParam == VK_SHIFT)*/
		 || (msg.wParam >= 0xB0)) {
		    /*
		     * translate to a WM_CHAR message
		     */
		    if (TranslateMessage(&msg))
			continue;
		}
		break;

	    /* short cirquit some messages */
#ifndef SUPPORT_NATIVE_WINDOWS
	    /*
	     * some can simply be ignored ...
	     */
	    case WM_ERASEBKGND:
		EVENT_PRINTF(("*WM_ERASEBKGND\n"));
		continue;
	    case WM_KILLFOCUS:
		EVENT_PRINTF(("*WM_KILLFOCUS\n"));
		continue;
#endif
	    case WM_SIZE:
		EVENT_PRINTF(("*WM_SIZE\n"));
		continue;
	}

	/*
	 * common ...
	 */
	evRootX = msg.pt.x;
	evRootY = msg.pt.y;
#if 1
	{
	    POINT p;

	    if (GetCursorPos(&p)) {
		evRootX = p.x;
		evRootY = p.y;
	    }
	}
#endif
	lastMSGTime = msg.time;
	DispatchMessage(&msg);   /* Dispatches message to window */
    }
    return 0;
}

LONG APIENTRY
MainWndProc(HWND hWnd,UINT message,UINT wParam,LONG lParam)
{
    int wantDefault = 1;
    int retVal;
    createWindowInfo *cwi;

    TH_DPRINTF(("TMW %d\n",message));

    /*
     * kludge while in sizeMove loop, which
     * is performed directly by DefWindowProc
     * even more of a kludge: to prevent the message in the PostThreadMessage-queue
     * from being processed twice, we use a sequenceNr, which is passed as wParam.
     */
    if (cwi = pendingCREATEWINDOWInfo) {
	int dummyWantDefault;
	int seqNr = pendingSequenceNr;

	pendingCREATEWINDOWInfo = 0;
	pendingSequenceNr = INVALIDATED_CWI;
	DPRINTF(("### THREAD_CREATEWINDOW\n"));
	winEventProcessing(0, WM_THREAD_CREATEWINDOW, seqNr, cwi, &dummyWantDefault);        
    }

    retVal = winEventProcessing(hWnd, message, wParam, lParam, &wantDefault);
    if (wantDefault) {
	DDPRINTF((">>DefWindowProc\n"));
	retVal = DefWindowProc(hWnd, message, wParam, lParam);
	DDPRINTF(("<<DefWindowProc\n"));
    }
    return retVal;
}

static int CALLBACK
EnumFPTypeFaceProc( lplf, lptm, dwType, lpData )
	LOGFONT*        lplf;
	TEXTMETRIC*     lptm;
	DWORD           dwType;
	LPARAM          lpData;
{
	OBJ t;
	OBJ* refToList;
	OBJ  typeFaceList;

	if (lplf) {
	    refToList = (OBJ*) lpData;
	    __PROTECT__(*refToList);
	    t = __MKSTRING( lplf->lfFaceName );
	    __UNPROTECT__(*refToList);
	    typeFaceList = *refToList;
	    __SSEND1( typeFaceList, @symbol(add:), 0, t );
	}
	return 1;
}

static int CALLBACK
EnumFontsProc( lplf, lptm, dwType, lpData )
	LOGFONT*      lplf;     /* address of logical-font data */
	TEXTMETRIC*   lptm;     /* address of physical font data */
	DWORD         dwType;   /* font type */
	LPARAM        lpData;   /* address of application supplied data */
{
	OBJ newArray, t;
	OBJ *refToList;
	OBJ list;
	char *s;

	DPRINTF(("EnumFontProc\n\n\n\n"));

	if( lplf ) {
	    refToList = (OBJ*) lpData;
	    __PROTECT__(*refToList);
	    DPRINTF((" lfHeight          %d\n", lplf->lfHeight ));
	    DPRINTF((" lfWidth           %d\n", lplf->lfWidth  ));
	    DPRINTF((" lfEscapement      %d\n", lplf->lfEscapement  ));
	    DPRINTF((" lfOrientation     %d\n", lplf->lfOrientation  ));
	    DPRINTF((" lfWeight          %d\n", lplf->lfWeight  ));
	    DPRINTF((" lfItalic          %d\n", lplf->lfItalic  ));
	    DPRINTF((" lfUnderline       %d\n", lplf->lfUnderline  ));
	    DPRINTF((" lfStrikeOut       %d\n", lplf->lfStrikeOut  ));
	    DPRINTF((" lfCharSet         %d\n", lplf->lfCharSet  ));
	    DPRINTF((" lfOutPrecision    %d\n", lplf->lfOutPrecision  ));
	    DPRINTF((" lfClipPrecision   %d\n", lplf->lfClipPrecision  ));
	    DPRINTF((" lfQuality         %d\n", lplf->lfQuality  ));
	    DPRINTF((" lfPitchAndFamily  %d\n", lplf->lfPitchAndFamily  ));
	    DPRINTF((" lfFaceName        %s\n\n", lplf->lfFaceName  ));

	    newArray = __ARRAY_NEW_INT(16);
	    __UNPROTECT__(*refToList);

	    if ( dwType & TRUETYPE_FONTTYPE )
		__ArrayInstPtr(newArray)->a_element[0] = __MKSMALLINT(0);
	    else {
		//__ArrayInstPtr(newArray)->a_element[0] = __MKSMALLINT(MulDiv(__logPixelSY,lplf->lfHeight, 72));
		__ArrayInstPtr(newArray)->a_element[0] = __MKSMALLINT(lplf->lfHeight);
	    }
	    __ArrayInstPtr(newArray)->a_element[1] = __MKSMALLINT(lplf->lfWidth);
	    __ArrayInstPtr(newArray)->a_element[2] = __MKSMALLINT(lplf->lfEscapement);
	    __ArrayInstPtr(newArray)->a_element[3] = __MKSMALLINT(lplf->lfOrientation);
	    switch (lplf->lfWeight) {
		case FW_NORMAL:
		    s = "normal";
		    break;
		case FW_BOLD:
		    s = "bold";
		    break;
		case FW_MEDIUM:
		    s = "medium";
		    break;
		case FW_LIGHT:
		    s = "demi";
		    break;
		default:
		    s = "other";
		    break;
	    }
	    __PROTECT__(*refToList);
	    __PROTECT__(newArray);
	    t = __MKSTRING(s);
	    __UNPROTECT__(newArray);
	    __UNPROTECT__(*refToList);
	    __ArrayInstPtr(newArray)->a_element[4] = t; __STORE(newArray, t);

	    if( lplf->lfItalic == TRUE ) {
		if( lplf->lfUnderline == TRUE ) {
		    if( lplf->lfStrikeOut == TRUE ) {
			s = "italic-underline-strikeOut";
		    } else {
			s = "italic-underline";
		    }
		} else {
		    if( lplf->lfStrikeOut == TRUE ) {
			s = "italic-strikeOut";
		    } else {
			s = "italic";
		    }
		}
	    } else {
		if( lplf->lfUnderline == TRUE ) {
		    if( lplf->lfStrikeOut == TRUE ) {
			s = "roman-underline-strikeOut";
		    } else {
			s = "roman-underline";
		    }
		} else {
		    if( lplf->lfStrikeOut == TRUE ) {
			s = "roman-strikeOut";
		    } else {
			s = "roman";
		    }
		}
	    }
	    __PROTECT__(*refToList);
	    __PROTECT__(newArray);
	    t = __MKSTRING(s);
	    __UNPROTECT__(newArray);
	    __UNPROTECT__(*refToList);
	    __ArrayInstPtr(newArray)->a_element[15] = t; __STORE(newArray, t);

	    __ArrayInstPtr(newArray)->a_element[5] = __MKSMALLINT(lplf->lfItalic);
	    __ArrayInstPtr(newArray)->a_element[6] = __MKSMALLINT(lplf->lfUnderline);
	    __ArrayInstPtr(newArray)->a_element[7] = __MKSMALLINT(lplf->lfStrikeOut);
	    __ArrayInstPtr(newArray)->a_element[8] = __MKSMALLINT(lplf->lfCharSet);
	    __ArrayInstPtr(newArray)->a_element[9] = __MKSMALLINT(lplf->lfOutPrecision);
	    __ArrayInstPtr(newArray)->a_element[10] = __MKSMALLINT(lplf->lfClipPrecision);
	    __ArrayInstPtr(newArray)->a_element[11] = __MKSMALLINT(lplf->lfQuality);
	    __ArrayInstPtr(newArray)->a_element[12] = __MKSMALLINT(lplf->lfPitchAndFamily);
	    /* ... */
	    __PROTECT__(*refToList);
	    __PROTECT__(newArray);
	    t = __MKSTRING(lplf->lfFaceName);
	    __UNPROTECT__(newArray);
	    __UNPROTECT__(*refToList);
	    __ArrayInstPtr(newArray)->a_element[13] = t; __STORE(newArray, t);

	    switch (lplf->lfCharSet) {
		case ANSI_CHARSET:
		    s = "ansi";
		    break;
		case DEFAULT_CHARSET:
		    s = "default";
		    break;
		case SYMBOL_CHARSET:
		    s = "symbol";
		    break;
		case SHIFTJIS_CHARSET:
		    s = "sjis";
		    break;
		case GB2312_CHARSET:
		    s = "gb";
		    break;
		case HANGEUL_CHARSET:
		    s = "hangeul";
		    break;
		case CHINESEBIG5_CHARSET:
		    s = "big5";
		    break;
		case OEM_CHARSET:
		    s = "oem";
		    break;
# ifdef JOHAB_CHARSET
		case JOHAB_CHARSET:
		    s = "johab";
		    break;
# endif
# ifdef HEBREW_CHARSET
		case HEBREW_CHARSET:
		    s = "hebrew";
		    break;
# endif
# ifdef ARABIC_CHARSET
		case ARABIC_CHARSET:
		    s = "arabic";
		    break;
# endif
# ifdef GREEK_CHARSET
		case GREEK_CHARSET:
		    s = "greek";
		    break;
# endif
# ifdef TURKISH_CHARSET
		case TURKISH_CHARSET:
		    s = "turkish";
		    break;
# endif
# ifdef RUSSIAN_CHARSET
		case RUSSIAN_CHARSET:
		    s = "russian";
		    break;
# endif
# ifdef EASTEUROPE_CHARSET
		case EASTEUROPE_CHARSET:
		    s = "easteurope";
		    break;
# endif
# ifdef BALTIC_CHARSET
		case BALTIC_CHARSET:
		    s = "baltic";
		    break;
# endif
# ifdef VIETNAMESE_CHARSET
		case VIETNAMESE_CHARSET:
		    s = "vietnamese";
		    break;
# endif
# ifdef THAI_CHARSET
		case THAI_CHARSET:
		    s = "thai";
		    break;
# endif
# ifdef MAC_CHARSET
		case MAC_CHARSET:
		    s = "mac";
		    break;
# endif
		default:
		    s = "unknown";
		    break;
	    }
	    __PROTECT__(*refToList);
	    __PROTECT__(newArray);
	    t = __MKSTRING(s);
	    __UNPROTECT__(newArray);
	    __UNPROTECT__(*refToList);

	    __ArrayInstPtr(newArray)->a_element[14] = t; __STORE(newArray, t);

	    list = *refToList;
	    __SSEND1(list, @symbol(add:), 0, newArray);
	}

	DPRINTF((" dwType            %d\n", dwType ));
	return 1;
}

%}
! !

!WinWorkstation class methodsFor:'documentation'!

copyright
"
COPYRIGHT (c) 1996 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

documentation
"
    See more documentation in my superclass, DeviceWorkstation.

    [author:]
	Claus Gittinger (initial port & final fixups)
	Manfred Dierolf (many, many changes & fixes, multithreading etc.)
"
! !

!WinWorkstation class methodsFor:'initialization'!

initialize
    |swap|

    super initialize.

    self initializeStandardColorNames.

    NativeWindows := false.

    BeepDuration := 200.        "/ millis

    ButtonTranslation := #(1 2 2).

    "/ these are reported *very* often when exceed is running,
    "/ but are also needed to update my view colors when the settings change.
    "/ I dont know what exceed is doing there ...
    "/ IgnoreSysColorChanges := true.

    "/ translation table from ST/X windowType symbol (system-independent)
    "/ to Windows windowClass (windows-specific).

    NativeWindowClassTable := IdentityDictionary new.
    NativeWindowClassTable at:#ScrollBar     put:'SCROLLBAR'.
    NativeWindowClassTable at:#CheckBox      put:'BUTTON'.
    NativeWindowClassTable at:#RadioButton   put:'BUTTON'.
    NativeWindowClassTable at:#Button        put:'BUTTON'.
    NativeWindowClassTable at:#DefaultButton put:'BUTTON'.
    NativeWindowClassTable at:#ComboBox      put:'COMBOBOX'.
    NativeWindowClassTable at:#EditField     put:'EDIT'.
    NativeWindowClassTable at:#ListBox       put:'LISTBOX'.
!

initializeStandardColorNames
    "{ Pragma: +optSpace }"

    "setup standard color names (X-color names)"

    StandardColorValues := Dictionary new.
    #(
	(240 248 255)   'aliceblue'
	(250 235 215)   'antiquewhite'
	(255 239 219)   'AntiqueWhite1'
	(238 223 204)   'AntiqueWhite2'
	(205 192 176)   'AntiqueWhite3'
	(139 131 120)   'AntiqueWhite4'
	(127 255 212)   'aquamarine'
	(127 255 212)   'aquamarine1'
	(118 238 198)   'aquamarine2'
	(102 205 170)   'aquamarine3'
	( 69 139 116)   'aquamarine4'
	(240 255 255)   'azure'
	(240 255 255)   'azure1'
	(224 238 238)   'azure2'
	(193 205 205)   'azure3'
	(131 139 139)   'azure4'
	(245 245 220)   'beige'
	(255 228 196)   'bisque'
	(255 228 196)   'bisque1'
	(238 213 183)   'bisque2'
	(205 183 158)   'bisque3'
	(139 125 107)   'bisque4'
	(  0   0   0)   'black'
	(255 235 205)   'blanchedalmond'
	(  0   0 255)   'blue'
	(138  43 226)   'blueviolet'
	(  0   0 255)   'blue1'
	(  0   0 238)   'blue2'
	(  0   0 205)   'blue3'
	(  0   0 139)   'blue4'
	(165  42  42)   'brown'
	(255  64  64)   'brown1'
	(238  59  59)   'brown2'
	(205  51  51)   'brown3'
	(139  35  35)   'brown4'
	(222 184 135)   'burlywood'
	(255 211 155)   'burlywood1'
	(238 197 145)   'burlywood2'
	(205 170 125)   'burlywood3'
	(139 115  85)   'burlywood4'
	( 95 158 160)   'cadetblue'
	(152 245 255)   'CadetBlue1'
	(142 229 238)   'CadetBlue2'
	(122 197 205)   'CadetBlue3'
	( 83 134 139)   'CadetBlue4'
	(127 255   0)   'chartreuse'
	(127 255   0)   'chartreuse1'
	(118 238   0)   'chartreuse2'
	(102 205   0)   'chartreuse3'
	( 69 139   0)   'chartreuse4'
	(210 105  30)   'chocolate'
	(255 127  36)   'chocolate1'
	(238 118  33)   'chocolate2'
	(205 102  29)   'chocolate3'
	(139  69  19)   'chocolate4'
	(255 127  80)   'coral'
	(255 114  86)   'coral1'
	(238 106  80)   'coral2'
	(205  91  69)   'coral3'
	(139  62  47)   'coral4'
	(100 149 237)   'cornflowerblue'
	(255 248 220)   'cornsilk'
	(255 248 220)   'cornsilk1'
	(238 232 205)   'cornsilk2'
	(205 200 177)   'cornsilk3'
	(139 136 120)   'cornsilk4'
	(  0 255 255)   'cyan'
	(  0 255 255)   'cyan1'
	(  0 238 238)   'cyan2'
	(  0 205 205)   'cyan3'
	(  0 139 139)   'cyan4'
	(  0   0 139)   'darkblue'
	(  0 139 139)   'darkcyan'
	(184 134  11)   'darkgoldenrod'
	(169 169 169)   'darkgray'
	(  0 100   0)   'darkgreen'
	(169 169 169)   'darkgrey'
	(189 183 107)   'darkkhaki'
	(139   0 139)   'darkmagenta'
	( 85 107  47)   'darkolivegreen'
	(255 140   0)   'darkorange'
	(153  50 204)   'darkorchid'
	(139   0   0)   'darkred'
	(233 150 122)   'darksalmon'
	(143 188 143)   'darkseagreen'
	( 72  61 139)   'darkslateblue'
	( 47  79  79)   'darkslategray'
	( 47  79  79)   'darkslategrey'
	(  0 206 209)   'darkturquoise'
	(148   0 211)   'darkviolet'
	(255 185  15)   'DarkGoldenrod1'
	(238 173  14)   'DarkGoldenrod2'
	(205 149  12)   'DarkGoldenrod3'
	(139 101   8)   'DarkGoldenrod4'
	(202 255 112)   'DarkOliveGreen1'
	(188 238 104)   'DarkOliveGreen2'
	(162 205  90)   'DarkOliveGreen3'
	(110 139  61)   'DarkOliveGreen4'
	(255 127   0)   'DarkOrange1'
	(238 118   0)   'DarkOrange2'
	(205 102   0)   'DarkOrange3'
	(139  69   0)   'DarkOrange4'
	(191  62 255)   'DarkOrchid1'
	(178  58 238)   'DarkOrchid2'
	(154  50 205)   'DarkOrchid3'
	(104  34 139)   'DarkOrchid4'
	(193 255 193)   'DarkSeaGreen1'
	(180 238 180)   'DarkSeaGreen2'
	(155 205 155)   'DarkSeaGreen3'
	(105 139 105)   'DarkSeaGreen4'
	(151 255 255)   'DarkSlateGray1'
	(141 238 238)   'DarkSlateGray2'
	(121 205 205)   'DarkSlateGray3'
	( 82 139 139)   'DarkSlateGray4'
	(255  20 147)   'deeppink'
	(  0 191 255)   'deepskyblue'
	(255  20 147)   'DeepPink'
	(255  20 147)   'DeepPink1'
	(238  18 137)   'DeepPink2'
	(205  16 118)   'DeepPink3'
	(139  10  80)   'DeepPink4'
	(  0 191 255)   'DeepSkyBlue1'
	(  0 178 238)   'DeepSkyBlue2'
	(  0 154 205)   'DeepSkyBlue3'
	(  0 104 139)   'DeepSkyBlue4'
	(105 105 105)   'dimgray'
	(105 105 105)   'dimgrey'
	( 30 144 255)   'dodgerblue'
	( 30 144 255)   'DodgerBlue1'
	( 28 134 238)   'DodgerBlue2'
	( 24 116 205)   'DodgerBlue3'
	( 16  78 139)   'DodgerBlue4'
	(178  34  34)   'firebrick'
	(255  48  48)   'firebrick1'
	(238  44  44)   'firebrick2'
	(205  38  38)   'firebrick3'
	(139  26  26)   'firebrick4'
	(255 250 240)   'floralwhite'
	( 34 139  34)   'forestgreen'
	(220 220 220)   'gainsboro'
	(248 248 255)   'ghostwhite'
	(255 215   0)   'gold'
	(255 215   0)   'gold1'
	(238 201   0)   'gold2'
	(205 173   0)   'gold3'
	(139 117   0)   'gold4'
	(218 165  32)   'goldenrod'
	(255 193  37)   'goldenrod1'
	(238 180  34)   'goldenrod2'
	(205 155  29)   'goldenrod3'
	(139 105  20)   'goldenrod4'
	(192 192 192)   'grey'
	(  0   0   0)   'grey0'
	(  3   3   3)   'grey1'
	( 26  26  26)   'grey10'
	(255 255 255)   'grey100'
	( 28  28  28)   'grey11'
	( 31  31  31)   'grey12'
	( 33  33  33)   'grey13'
	( 36  36  36)   'grey14'
	( 38  38  38)   'grey15'
	( 41  41  41)   'grey16'
	( 43  43  43)   'grey17'
	( 46  46  46)   'grey18'
	( 48  48  48)   'grey19'
	(  5   5   5)   'grey2'
	( 51  51  51)   'grey20'
	( 54  54  54)   'grey21'
	( 56  56  56)   'grey22'
	( 59  59  59)   'grey23'
	( 61  61  61)   'grey24'
	( 64  64  64)   'grey25'
	( 66  66  66)   'grey26'
	( 69  69  69)   'grey27'
	( 71  71  71)   'grey28'
	( 74  74  74)   'grey29'
	(  8   8   8)   'grey3'
	( 77  77  77)   'grey30'
	( 79  79  79)   'grey31'
	( 82  82  82)   'grey32'
	( 84  84  84)   'grey33'
	( 87  87  87)   'grey34'
	( 89  89  89)   'grey35'
	( 92  92  92)   'grey36'
	( 94  94  94)   'grey37'
	( 97  97  97)   'grey38'
	( 99  99  99)   'grey39'
	( 10  10  10)   'grey4'
	(102 102 102)   'grey40'
	(105 105 105)   'grey41'
	(107 107 107)   'grey42'
	(110 110 110)   'grey43'
	(112 112 112)   'grey44'
	(115 115 115)   'grey45'
	(117 117 117)   'grey46'
	(120 120 120)   'grey47'
	(122 122 122)   'grey48'
	(125 125 125)   'grey49'
	( 13  13  13)   'grey5'
	(127 127 127)   'grey50'
	(130 130 130)   'grey51'
	(133 133 133)   'grey52'
	(135 135 135)   'grey53'
	(138 138 138)   'grey54'
	(140 140 140)   'grey55'
	(143 143 143)   'grey56'
	(145 145 145)   'grey57'
	(148 148 148)   'grey58'
	(150 150 150)   'grey59'
	( 15  15  15)   'grey6'
	(153 153 153)   'grey60'
	(156 156 156)   'grey61'
	(158 158 158)   'grey62'
	(161 161 161)   'grey63'
	(163 163 163)   'grey64'
	(166 166 166)   'grey65'
	(168 168 168)   'grey66'
	(171 171 171)   'grey67'
	(173 173 173)   'grey68'
	(176 176 176)   'grey69'
	( 18  18  18)   'grey7'
	(179 179 179)   'grey70'
	(181 181 181)   'grey71'
	(184 184 184)   'grey72'
	(186 186 186)   'grey73'
	(189 189 189)   'grey74'
	(191 191 191)   'grey75'
	(194 194 194)   'grey76'
	(196 196 196)   'grey77'
	(199 199 199)   'grey78'
	(201 201 201)   'grey79'
	( 20  20  20)   'grey8'
	(204 204 204)   'grey80'
	(207 207 207)   'grey81'
	(209 209 209)   'grey82'
	(212 212 212)   'grey83'
	(214 214 214)   'grey84'
	(217 217 217)   'grey85'
	(219 219 219)   'grey86'
	(222 222 222)   'grey87'
	(224 224 224)   'grey88'
	(227 227 227)   'grey89'
	( 23  23  23)   'grey9'
	(229 229 229)   'grey90'
	(232 232 232)   'grey91'
	(235 235 235)   'grey92'
	(237 237 237)   'grey93'
	(240 240 240)   'grey94'
	(242 242 242)   'grey95'
	(245 245 245)   'grey96'
	(247 247 247)   'grey97'
	(250 250 250)   'grey98'
	(252 252 252)   'grey99'

	(192 192 192)   'gray'
	(  0   0   0)   'gray0'
	(  3   3   3)   'gray1'
	( 26  26  26)   'gray10'
	(255 255 255)   'gray100'
	( 28  28  28)   'gray11'
	( 31  31  31)   'gray12'
	( 33  33  33)   'gray13'
	( 36  36  36)   'gray14'
	( 38  38  38)   'gray15'
	( 41  41  41)   'gray16'
	( 43  43  43)   'gray17'
	( 46  46  46)   'gray18'
	( 48  48  48)   'gray19'
	(  5   5   5)   'gray2'
	( 51  51  51)   'gray20'
	( 54  54  54)   'gray21'
	( 56  56  56)   'gray22'
	( 59  59  59)   'gray23'
	( 61  61  61)   'gray24'
	( 64  64  64)   'gray25'
	( 66  66  66)   'gray26'
	( 69  69  69)   'gray27'
	( 71  71  71)   'gray28'
	( 74  74  74)   'gray29'
	(  8   8   8)   'gray3'
	( 77  77  77)   'gray30'
	( 79  79  79)   'gray31'
	( 82  82  82)   'gray32'
	( 84  84  84)   'gray33'
	( 87  87  87)   'gray34'
	( 89  89  89)   'gray35'
	( 92  92  92)   'gray36'
	( 94  94  94)   'gray37'
	( 97  97  97)   'gray38'
	( 99  99  99)   'gray39'
	( 10  10  10)   'gray4'
	(102 102 102)   'gray40'
	(105 105 105)   'gray41'
	(107 107 107)   'gray42'
	(110 110 110)   'gray43'
	(112 112 112)   'gray44'
	(115 115 115)   'gray45'
	(117 117 117)   'gray46'
	(120 120 120)   'gray47'
	(122 122 122)   'gray48'
	(125 125 125)   'gray49'
	( 13  13  13)   'gray5'
	(127 127 127)   'gray50'
	(130 130 130)   'gray51'
	(133 133 133)   'gray52'
	(135 135 135)   'gray53'
	(138 138 138)   'gray54'
	(140 140 140)   'gray55'
	(143 143 143)   'gray56'
	(145 145 145)   'gray57'
	(148 148 148)   'gray58'
	(150 150 150)   'gray59'
	( 15  15  15)   'gray6'
	(153 153 153)   'gray60'
	(156 156 156)   'gray61'
	(158 158 158)   'gray62'
	(161 161 161)   'gray63'
	(163 163 163)   'gray64'
	(166 166 166)   'gray65'
	(168 168 168)   'gray66'
	(171 171 171)   'gray67'
	(173 173 173)   'gray68'
	(176 176 176)   'gray69'
	( 18  18  18)   'gray7'
	(179 179 179)   'gray70'
	(181 181 181)   'gray71'
	(184 184 184)   'gray72'
	(186 186 186)   'gray73'
	(189 189 189)   'gray74'
	(191 191 191)   'gray75'
	(194 194 194)   'gray76'
	(196 196 196)   'gray77'
	(199 199 199)   'gray78'
	(201 201 201)   'gray79'
	( 20  20  20)   'gray8'
	(204 204 204)   'gray80'
	(207 207 207)   'gray81'
	(209 209 209)   'gray82'
	(212 212 212)   'gray83'
	(214 214 214)   'gray84'
	(217 217 217)   'gray85'
	(219 219 219)   'gray86'
	(222 222 222)   'gray87'
	(224 224 224)   'gray88'
	(227 227 227)   'gray89'
	( 23  23  23)   'gray9'
	(229 229 229)   'gray90'
	(232 232 232)   'gray91'
	(235 235 235)   'gray92'
	(237 237 237)   'gray93'
	(240 240 240)   'gray94'
	(242 242 242)   'gray95'
	(245 245 245)   'gray96'
	(247 247 247)   'gray97'
	(250 250 250)   'gray98'
	(252 252 252)   'gray99'
	(  0 255   0)   'green'
	(173 255  47)   'greenyellow'
	(  0 255   0)   'green1'
	(  0 238   0)   'green2'
	(  0 205   0)   'green3'
	(  0 139   0)   'green4'
	(240 255 240)   'honeydew'
	(240 255 240)   'honeydew1'
	(224 238 224)   'honeydew2'
	(193 205 193)   'honeydew3'
	(131 139 131)   'honeydew4'
	(255 105 180)   'hotpink'
	(255 110 180)   'HotPink1'
	(238 106 167)   'HotPink2'
	(205  96 144)   'HotPink3'
	(139  58  98)   'HotPink4'
	(205  92  92)   'indianred'
	(255 106 106)   'IndianRed1'
	(238  99  99)   'IndianRed2'
	(205  85  85)   'IndianRed3'
	(139  58  58)   'IndianRed4'
	(255 255 240)   'ivory'
	(255 255 240)   'ivory1'
	(238 238 224)   'ivory2'
	(205 205 193)   'ivory3'
	(139 139 131)   'ivory4'
	(240 230 140)   'khaki'
	(255 246 143)   'khaki1'
	(238 230 133)   'khaki2'
	(205 198 115)   'khaki3'
	(139 134  78)   'khaki4'
	(230 230 250)   'lavender'
	(255 240 245)   'lavenderblush'
	(255 240 245)   'LavenderBlush1'
	(238 224 229)   'LavenderBlush2'
	(205 193 197)   'LavenderBlush3'
	(139 131 134)   'LavenderBlush4'
	(124 252   0)   'lawngreen'
	(255 250 205)   'lemonchiffon'
	(255 250 205)   'LemonChiffon1'
	(238 233 191)   'LemonChiffon2'
	(205 201 165)   'LemonChiffon3'
	(139 137 112)   'LemonChiffon4'
	(173 216 230)   'lightblue'
	(240 128 128)   'lightcoral'
	(224 255 255)   'lightcyan'
	(238 221 130)   'lightgoldenrod'
	(250 250 210)   'lightgoldenrodyellow'
	(211 211 211)   'lightgray'
	(144 238 144)   'lightgreen'
	(211 211 211)   'lightgrey'
	(255 182 193)   'lightpink'
	(255 160 122)   'lightsalmon'
	( 32 178 170)   'lightseagreen'
	(135 206 250)   'lightskyblue'
	(132 112 255)   'lightslateblue'
	(119 136 153)   'lightslategray'
	(119 136 153)   'lightslategrey'
	(176 196 222)   'lightsteelblue'
	(255 255 224)   'lightyellow'
	(191 239 255)   'LightBlue1'
	(178 223 238)   'LightBlue2'
	(154 192 205)   'LightBlue3'
	(104 131 139)   'LightBlue4'
	(224 255 255)   'LightCyan1'
	(209 238 238)   'LightCyan2'
	(180 205 205)   'LightCyan3'
	(122 139 139)   'LightCyan4'
	(255 236 139)   'LightGoldenrod1'
	(238 220 130)   'LightGoldenrod2'
	(205 190 112)   'LightGoldenrod3'
	(139 129  76)   'LightGoldenrod4'
	(255 174 185)   'LightPink1'
	(238 162 173)   'LightPink2'
	(205 140 149)   'LightPink3'
	(139  95 101)   'LightPink4'
	(255 160 122)   'LightSalmon1'
	(238 149 114)   'LightSalmon2'
	(205 129  98)   'LightSalmon3'
	(139  87  66)   'LightSalmon4'
	(176 226 255)   'LightSkyBlue1'
	(164 211 238)   'LightSkyBlue2'
	(141 182 205)   'LightSkyBlue3'
	( 96 123 139)   'LightSkyBlue4'
	(202 225 255)   'LightSteelBlue1'
	(188 210 238)   'LightSteelBlue2'
	(162 181 205)   'LightSteelBlue3'
	(110 123 139)   'LightSteelBlue4'
	(255 255 224)   'LightYellow1'
	(238 238 209)   'LightYellow2'
	(205 205 180)   'LightYellow3'
	(139 139 122)   'LightYellow4'
	( 50 205  50)   'limegreen'
	(250 240 230)   'linen'
	(255   0 255)   'magenta'
	(255   0 255)   'magenta1'
	(238   0 238)   'magenta2'
	(205   0 205)   'magenta3'
	(139   0 139)   'magenta4'
	(176  48  96)   'maroon'
	(255  52 179)   'maroon1'
	(238  48 167)   'maroon2'
	(205  41 144)   'maroon3'
	(139  28  98)   'maroon4'
	(102 205 170)   'mediumaquamarine'
	(  0   0 205)   'mediumblue'
	(186  85 211)   'mediumorchid'
	(147 112 219)   'mediumpurple'
	( 60 179 113)   'mediumseagreen'
	(123 104 238)   'mediumslateblue'
	(  0 250 154)   'mediumspringgreen'
	( 72 209 204)   'mediumturquoise'
	(199  21 133)   'mediumvioletred'
	(224 102 255)   'MediumOrchid1'
	(209  95 238)   'MediumOrchid2'
	(180  82 205)   'MediumOrchid3'
	(122  55 139)   'MediumOrchid4'
	(171 130 255)   'MediumPurple1'
	(159 121 238)   'MediumPurple2'
	(137 104 205)   'MediumPurple3'
	( 93  71 139)   'MediumPurple4'
	( 25  25 112)   'midnightblue'
	(245 255 250)   'mintcream'
	(255 228 225)   'mistyrose'
	(255 228 225)   'MistyRose1'
	(238 213 210)   'MistyRose2'
	(205 183 181)   'MistyRose3'
	(139 125 123)   'MistyRose4'
	(255 228 181)   'moccasin'
	(255 222 173)   'navajowhite'
	(255 222 173)   'NavajoWhite1'
	(238 207 161)   'NavajoWhite2'
	(205 179 139)   'NavajoWhite3'
	(139 121  94)   'NavajoWhite4'
	(  0   0 128)   'navy'
	(  0   0 128)   'navyblue'
	(253 245 230)   'oldlace'
	(107 142  35)   'olivedrab'
	(192 255  62)   'OliveDrab1'
	(179 238  58)   'OliveDrab2'
	(154 205  50)   'OliveDrab3'
	(105 139  34)   'OliveDrab4'
	(255 165   0)   'orange'
	(255 165   0)   'orange1'
	(238 154   0)   'orange2'
	(205 133   0)   'orange3'
	(139  90   0)   'orange4'
	(255  69   0)   'orangered'
	(255  69   0)   'OrangeRed1'
	(238  64   0)   'OrangeRed2'
	(205  55   0)   'OrangeRed3'
	(139  37   0)   'OrangeRed4'
	(218 112 214)   'orchid'
	(255 131 250)   'orchid1'
	(238 122 233)   'orchid2'
	(205 105 201)   'orchid3'
	(139  71 137)   'orchid4'
	(238 232 170)   'palegoldenrod'
	(152 251 152)   'palegreen'
	(175 238 238)   'paleturquoise'
	(219 112 147)   'palevioletred'
	(154 255 154)   'PaleGreen1'
	(144 238 144)   'PaleGreen2'
	(124 205 124)   'PaleGreen3'
	( 84 139  84)   'PaleGreen4'
	(187 255 255)   'PaleTurquoise1'
	(174 238 238)   'PaleTurquoise2'
	(150 205 205)   'PaleTurquoise3'
	(102 139 139)   'PaleTurquoise4'
	(255 130 171)   'PaleVioletRed1'
	(238 121 159)   'PaleVioletRed2'
	(205 104 137)   'PaleVioletRed3'
	(139  71  93)   'PaleVioletRed4'
	(255 239 213)   'papayawhip'
	(255 218 185)   'peachpuff'
	(255 218 185)   'PeachPuff1'
	(238 203 173)   'PeachPuff2'
	(205 175 149)   'PeachPuff3'
	(139 119 101)   'PeachPuff4'
	(205 133  63)   'peru'
	(255 192 203)   'pink'
	(255 181 197)   'pink1'
	(238 169 184)   'pink2'
	(205 145 158)   'pink3'
	(139  99 108)   'pink4'
	(221 160 221)   'plum'
	(255 187 255)   'plum1'
	(238 174 238)   'plum2'
	(205 150 205)   'plum3'
	(139 102 139)   'plum4'
	(176 224 230)   'powderblue'
	(160  32 240)   'purple'
	(155  48 255)   'purple1'
	(145  44 238)   'purple2'
	(125  38 205)   'purple3'
	( 85  26 139)   'purple4'
	(255   0   0)   'red'
	(255   0   0)   'red1'
	(238   0   0)   'red2'
	(205   0   0)   'red3'
	(139   0   0)   'red4'
	(188 143 143)   'rosybrown'
	(255 193 193)   'RosyBrown1'
	(238 180 180)   'RosyBrown2'
	(205 155 155)   'RosyBrown3'
	(139 105 105)   'RosyBrown4'
	( 65 105 225)   'royalblue'
	( 72 118 255)   'RoyalBlue1'
	( 67 110 238)   'RoyalBlue2'
	( 58  95 205)   'RoyalBlue3'
	( 39  64 139)   'RoyalBlue4'
	(139  69  19)   'saddlebrown'
	(250 128 114)   'salmon'
	(255 140 105)   'salmon1'
	(238 130  98)   'salmon2'
	(205 112  84)   'salmon3'
	(139  76  57)   'salmon4'
	(244 164  96)   'sandybrown'
	( 255 206 137)  'scoActiveBackground'
	( 43  45  49)   'scoActiveForeground'
	( 254 222 255)  'scoActiveTopShadow'
	( 172 186 204)  'scoAltBackground'
	( 203 203 192)  'scoBackground'
	( 11   0 113)   'scoForeground'
	( 141 178 215)  'scoHighlight'
	( 255 240 248)  'scoTopShadow'
	( 46 139  87)   'seagreen'
	( 84 255 159)   'SeaGreen1'
	( 78 238 148)   'SeaGreen2'
	( 67 205 128)   'SeaGreen3'
	( 46 139  87)   'SeaGreen4'
	(255 245 238)   'seashell'
	(255 245 238)   'seashell1'
	(238 229 222)   'seashell2'
	(205 197 191)   'seashell3'
	(139 134 130)   'seashell4'
	(142 56 142)    'sgi beet'
	(197 193 170)   'sgi bright gray'
	(197 193 170)   'sgi bright grey'
	(113 198 113)   'sgi chartreuse'
	( 85  85  85)   'sgi dark gray'
	( 85  85  85)   'sgi dark grey'
	(  0   0   0)   'sgi gray 0'
	(255 255 255)   'sgi gray 100'
	( 30  30  30)   'sgi gray 12'
	( 40  40  40)   'sgi gray 16'
	( 51  51  51)   'sgi gray 20'
	( 61  61  61)   'sgi gray 24'
	( 71  71  71)   'sgi gray 28'
	( 81  81  81)   'sgi gray 32'
	( 91  91  91)   'sgi gray 36'
	( 10  10  10)   'sgi gray 4'
	(102 102 102)   'sgi gray 40'
	(112 112 112)   'sgi gray 44'
	(122 122 122)   'sgi gray 48'
	(132 132 132)   'sgi gray 52'
	(142 142 142)   'sgi gray 56'
	(153 153 153)   'sgi gray 60'
	(163 163 163)   'sgi gray 64'
	(173 173 173)   'sgi gray 68'
	(183 183 183)   'sgi gray 72'
	(193 193 193)   'sgi gray 76'
	( 20  20  20)   'sgi gray 8'
	(204 204 204)   'sgi gray 80'
	(214 214 214)   'sgi gray 84'
	(224 224 224)   'sgi gray 88'
	(234 234 234)   'sgi gray 92'
	(244 244 244)   'sgi gray 96'
	(  0   0   0)   'sgi grey 0'
	(255 255 255)   'sgi grey 100'
	( 30  30  30)   'sgi grey 12'
	( 40  40  40)   'sgi grey 16'
	( 51  51  51)   'sgi grey 20'
	( 61  61  61)   'sgi grey 24'
	( 71  71  71)   'sgi grey 28'
	( 81  81  81)   'sgi grey 32'
	( 91  91  91)   'sgi grey 36'
	( 10  10  10)   'sgi grey 4'
	(102 102 102)   'sgi grey 40'
	(112 112 112)   'sgi grey 44'
	(122 122 122)   'sgi grey 48'
	(132 132 132)   'sgi grey 52'
	(142 142 142)   'sgi grey 56'
	(153 153 153)   'sgi grey 60'
	(163 163 163)   'sgi grey 64'
	(173 173 173)   'sgi grey 68'
	(183 183 183)   'sgi grey 72'
	(193 193 193)   'sgi grey 76'
	( 20  20  20)   'sgi grey 8'
	(204 204 204)   'sgi grey 80'
	(214 214 214)   'sgi grey 84'
	(224 224 224)   'sgi grey 88'
	(234 234 234)   'sgi grey 92'
	(244 244 244)   'sgi grey 96'
	(125 158 192)   'sgi light blue'
	(170 170 170)   'sgi light gray'
	(170 170 170)   'sgi light grey'
	(132 132 132)   'sgi medium gray'
	(132 132 132)   'sgi medium grey'
	(142 142  56)   'sgi olive drab'
	(198 113 113)   'sgi salmon'
	(113 113 198)   'sgi slate blue'
	( 56 142 142)   'sgi teal'
	( 40  40  40)   'sgi very dark gray'
	( 40  40  40)   'sgi very dark grey'
	(214 214 214)   'sgi very light gray'
	(214 214 214)   'sgi very light grey'
	(142 56 142)    'SGIBeet'
	(197 193 170)   'SGIBrightGray'
	(197 193 170)   'SGIBrightGrey'
	(113 198 113)   'SGIChartreuse'
	( 85  85  85)   'SGIDarkGray'
	( 85  85  85)   'SGIDarkGrey'
	(  0   0   0)   'SGIGray0'
	(255 255 255)   'SGIGray100'
	( 30  30  30)   'SGIGray12'
	( 40  40  40)   'SGIGray16'
	( 51  51  51)   'SGIGray20'
	( 61  61  61)   'SGIGray24'
	( 71  71  71)   'SGIGray28'
	( 81  81  81)   'SGIGray32'
	( 91  91  91)   'SGIGray36'
	( 10  10  10)   'SGIGray4'
	(102 102 102)   'SGIGray40'
	(112 112 112)   'SGIGray44'
	(122 122 122)   'SGIGray48'
	(132 132 132)   'SGIGray52'
	(142 142 142)   'SGIGray56'
	(153 153 153)   'SGIGray60'
	(163 163 163)   'SGIGray64'
	(173 173 173)   'SGIGray68'
	(183 183 183)   'SGIGray72'
	(193 193 193)   'SGIGray76'
	( 20  20  20)   'SGIGray8'
	(204 204 204)   'SGIGray80'
	(214 214 214)   'SGIGray84'
	(224 224 224)   'SGIGray88'
	(234 234 234)   'SGIGray92'
	(244 244 244)   'SGIGray96'
	(  0   0   0)   'SGIGrey0'
	(255 255 255)   'SGIGrey100'
	( 30  30  30)   'SGIGrey12'
	( 40  40  40)   'SGIGrey16'
	( 51  51  51)   'SGIGrey20'
	( 61  61  61)   'SGIGrey24'
	( 71  71  71)   'SGIGrey28'
	( 81  81  81)   'SGIGrey32'
	( 91  91  91)   'SGIGrey36'
	( 10  10  10)   'SGIGrey4'
	(102 102 102)   'SGIGrey40'
	(112 112 112)   'SGIGrey44'
	(122 122 122)   'SGIGrey48'
	(132 132 132)   'SGIGrey52'
	(142 142 142)   'SGIGrey56'
	(153 153 153)   'SGIGrey60'
	(163 163 163)   'SGIGrey64'
	(173 173 173)   'SGIGrey68'
	(183 183 183)   'SGIGrey72'
	(193 193 193)   'SGIGrey76'
	( 20  20  20)   'SGIGrey8'
	(204 204 204)   'SGIGrey80'
	(214 214 214)   'SGIGrey84'
	(224 224 224)   'SGIGrey88'
	(234 234 234)   'SGIGrey92'
	(244 244 244)   'SGIGrey96'
	(125 158 192)   'SGILightBlue'
	(170 170 170)   'SGILightGray'
	(170 170 170)   'SGILightGrey'
	(132 132 132)   'SGIMediumGray'
	(132 132 132)   'SGIMediumGrey'
	(142 142  56)   'SGIOliveDrab'
	(198 113 113)   'SGISalmon'
	(113 113 198)   'SGISlateBlue'
	( 56 142 142)   'SGITeal'
	( 40  40  40)   'SGIVeryDarkGray'
	( 40  40  40)   'SGIVeryDarkGrey'
	(214 214 214)   'SGIVeryLightGray'
	(214 214 214)   'SGIVeryLightGrey'
	(160  82  45)   'sienna'
	(255 130  71)   'sienna1'
	(238 121  66)   'sienna2'
	(205 104  57)   'sienna3'
	(139  71  38)   'sienna4'
	(135 206 235)   'skyblue'
	(135 206 255)   'SkyBlue1'
	(126 192 238)   'SkyBlue2'
	(108 166 205)   'SkyBlue3'
	( 74 112 139)   'SkyBlue4'
	(112 128 144)   'slategray'
	(112 128 144)   'slategrey'
	(106  90 205)   'slateblue'
	(131 111 255)   'SlateBlue1'
	(122 103 238)   'SlateBlue2'
	(105  89 205)   'SlateBlue3'
	( 71  60 139)   'SlateBlue4'
	(198 226 255)   'SlateGray1'
	(185 211 238)   'SlateGray2'
	(159 182 205)   'SlateGray3'
	(108 123 139)   'SlateGray4'
	(255 250 250)   'snow'
	(255 250 250)   'snow1'
	(238 233 233)   'snow2'
	(205 201 201)   'snow3'
	(139 137 137)   'snow4'
	(  0 255 127)   'springgreen'
	(  0 255 127)   'SpringGreen1'
	(  0 238 118)   'SpringGreen2'
	(  0 205 102)   'SpringGreen3'
	(  0 139  69)   'SpringGreen4'
	( 70 130 180)   'steelblue'
	( 99 184 255)   'SteelBlue1'
	( 92 172 238)   'SteelBlue2'
	( 79 148 205)   'SteelBlue3'
	( 54 100 139)   'SteelBlue4'
	(210 180 140)   'tan'
	(255 165  79)   'tan1'
	(238 154  73)   'tan2'
	(205 133  63)   'tan3'
	(139  90  43)   'tan4'
	(216 191 216)   'thistle'
	(255 225 255)   'thistle1'
	(238 210 238)   'thistle2'
	(205 181 205)   'thistle3'
	(139 123 139)   'thistle4'
	(255  99  71)   'tomato'
	(255  99  71)   'tomato1'
	(238  92  66)   'tomato2'
	(205  79  57)   'tomato3'
	(139  54  38)   'tomato4'
	( 64 224 208)   'turquoise'
	(  0 245 255)   'turquoise1'
	(  0 229 238)   'turquoise2'
	(  0 197 205)   'turquoise3'
	(  0 134 139)   'turquoise4'
	(238 130 238)   'violet'
	(208  32 144)   'violetred'
	(255  62 150)   'VioletRed1'
	(238  58 140)   'VioletRed2'
	(205  50 120)   'VioletRed3'
	(139  34  82)   'VioletRed4'
	(245 222 179)   'wheat'
	(255 231 186)   'wheat1'
	(238 216 174)   'wheat2'
	(205 186 150)   'wheat3'
	(139 126 102)   'wheat4'
	(255 255 255)   'white'
	(245 245 245)   'whitesmoke'
	(255 255   0)   'yellow'
	(154 205  50)   'yellowgreen'
	(255 255   0)   'yellow1'
	(238 238   0)   'yellow2'
	(205 205   0)   'yellow3'
	(139 139   0)   'yellow4'
    ) pairWiseDo:[ :value :name |
	StandardColorValues at:name put:value
    ].
    "
     WinWorkstation initializeStandardColorNames
    "
!

nativeWindows:aBoolean
    "enable / disable use of native windows"

    NativeWindows := aBoolean
! !

!WinWorkstation class methodsFor:'debugging'!

bitmapHandleCounts
    "for resource debugging only - will vanish"

%{  /* NOCONTEXT */
#ifdef COUNT_BMP_RESOURCES
    RETURN ( __MKSMALLINT(__cnt_bitmap));
#else
    RETURN (nil);
#endif
%}
!

cursorHandleCounts
    "for resource debugging only - will vanish"

%{  /* NOCONTEXT */
#ifdef COUNT_RESOURCES
    RETURN ( __MKSMALLINT(__cnt_cur));
#else
    RETURN (nil);
#endif
%}
!

debug2
    "enable more debug prints - this will vanish"

%{  /* NOCONTEXT */

    __debug__ = 2;
%}
!

debug:aBoolean
    "enable/disable debug prints - this will vanish"

%{  /* NOCONTEXT */

    __debug__ = (aBoolean == true) ? 1 : 0;
%}
!

fontHandleCounts
    "for resource debugging only - will vanish"

%{  /* NOCONTEXT */
#ifdef COUNT_RESOURCES
    RETURN ( __MKSMALLINT(__cnt_font));
#else
    RETURN (nil);
#endif
%}
!

gcDataHandleCounts
    "for resource debugging only - will vanish"

%{  /* NOCONTEXT */
#ifdef COUNT_RESOURCES
    RETURN ( __MKSMALLINT(__cnt_gcData));
#else
    RETURN (nil);
#endif
%}
!

printHandleCounts
   "show prim values"

   ('WINWORKSTATION: pW=', self windowHandleCounts printString,
    'pB=',self bitmapHandleCounts printString,
    'pGc=',self gcDataHandleCounts printString,
    'pbmpdc=',self bmpDcHandleCounts printString) errorPrintNL
!

windowHandleCounts
    "for resource debugging only - will vanish"

%{  /* NOCONTEXT */
#ifdef COUNT_RESOURCES
    RETURN ( __MKSMALLINT(__cnt_createWindows));
#else
    RETURN (nil);
#endif
%}
! !

!WinWorkstation class methodsFor:'queries'!

platformName
    "ST-80 compatibility.
     Return a string describing the display systems platform.
     WinWorkstation always returns 'WIN32'."

    ^ 'WIN32'  

    "Modified: 26.5.1996 / 15:32:46 / cg"
! !

!WinWorkstation methodsFor:'accessing & queries'!

activateOnClick:aBoolean
    "set/clear the activateOnClick behavior.
     If on, a click into a window raises and activates
     the window.
     Windows users typically enable this;
     users which are used to the X-Window system typically prefer
     it disabled.
     Returns the previous setting.
     The default is true."

%{  /* NOCONTEXT */
    OBJ rslt = __activateOnClick ? true : false;

    if (aBoolean == true) {
       __activateOnClick = 1;
    } else {
       if (aBoolean == false) {
	   __activateOnClick = 0;
       }
    }
    RETURN (rslt);
%}
    "
     Display activateOnClick:true
     Display activateOnClick:false
    "
!

anyButtonMotionMask
    "return the state-mask for any button in motion events' state-field.
     This is the devices mask."

%{  /* NOCONTEXT */
    RETURN ( __MKSMALLINT(Button1MotionMask | Button2MotionMask | Button3MotionMask));
%}
!

blackpixel
    "return the colornumber of black"

    ^ blackpixel
!

button1MotionMask
    "return the state-mask for button1 in motion events' state-field.
     For backward compatibility."

%{  /* NOCONTEXT */
    RETURN (__MKSMALLINT(Button1MotionMask));
%}

    "
     Display button1MotionMask
    "
!

button2MotionMask
    "return the state-mask for button2 in motion events' state-field
     For backward compatibility."

%{  /* NOCONTEXT */
    RETURN (__MKSMALLINT(Button2MotionMask)); 
%}
!

button3MotionMask
    "return the state-mask for button3 in motion events' state-field
     For backward compatibility."

%{  /* NOCONTEXT */
    RETURN (__MKSMALLINT(Button3MotionMask)); 
%}
!

buttonMotionMask:aButton
    "return the state-mask for button1 in motion events state-field.
     This is the devices mask."

%{  /* NOCONTEXT */
    if (aButton == __MKSMALLINT(1)) {
	RETURN (__MKSMALLINT(Button1MotionMask));    
    }
    if (aButton == __MKSMALLINT(2)) {
	RETURN (__MKSMALLINT(Button2MotionMask));    
    }
    if (aButton == __MKSMALLINT(3)) {
	RETURN (__MKSMALLINT(Button3MotionMask));    
    }
%}.
    ^ nil
!

controlMask
    "return the state-mask for the CTRL modified in motion events' state-field.
     Obsolete"

%{  /* NOCONTEXT */
    RETURN (__MKSMALLINT(ControlMask));
%}
!

ctrlModifierMask
    "return the state-mask for the CTRL modified in motion events' state-field."

%{  /* NOCONTEXT */
    RETURN (__MKSMALLINT(ControlMask));
%}
!

defaultEventMask
    "return a mask to enable some events by default."

%{  /* NOCONTEXT */
    RETURN (__MKSMALLINT( ExposureMask | StructureNotifyMask |
			 KeyPressMask | KeyReleaseMask |
			 EnterWindowMask | LeaveWindowMask |
			 ButtonPressMask | ButtonMotionMask | ButtonReleaseMask ));
%}
!

displayFileDescriptor
    "return the displays fileNumber for select, if any"

%{  /* NOCONTEXT */
   /* RETURN (nil);*/
   RETURN ( __MKEXTERNALADDRESS(hInputEvent));
%}
!

displayName
    "return the display-connections display name.
     For Windows, a dummy name is returned"

    ^ 'local'
!

focusFollowsMouse:aBoolean
    "set/clear the focusFollowsMouse behavior.
     If on, the view under the mouse gets the keyboard input
     (however, within a windowGroup, ST/X can still forward the event 
      the groups focusView...).
     Windows users typically disable this;
     users which are used to the X-Window system typically prefer
     it enabled.
     Returns the previous setting.
     The default is false."

%{  /* NOCONTEXT */
    OBJ rslt = __focusFollowsMouse ? true : false;

    if (aBoolean == true) {
       __focusFollowsMouse = 1;
    } else {
       if (aBoolean == false) {
	   __focusFollowsMouse = 0;
       }
    }
    RETURN (rslt);
%}
    "
     WinWorkstation focusFollowsMouse:true
     WinWorkstation focusFollowsMouse:false
    "
!

getSystemColor:aKey
    "retrieve a windows system color.
     The styleSheet/View classes may use this to setup default colors"

    |rgb|

    rgb := self primGetSystemColor:aKey.
    rgb notNil ifTrue:[
	^ Color
	    redByte:((rgb bitShift:-16) bitAnd:16rFF)
	    greenByte:((rgb bitShift:-8) bitAnd:16rFF)
	    blueByte:(rgb bitAnd:16rFF)
    ].
    ^ nil

    "
     Display getSystemColor:#COLOR_WINDOW
     Display getSystemColor:#COLOR_HIGHLIGHT
    "
!

getSystemMetrics:aNumberOrSymbol
    "get a system metrics value; the argument is a #define-like symbol
     as described in the windows API;
     However, to allow for future values to be retrieved, also allow an integer
     which must be the define-value."

%{  /* NOCONTEXT */
    int info = 0;
    int isBool = 0;
    int arg;

    if (__isSmallInteger(aNumberOrSymbol)) {
	arg = __intVal(aNumberOrSymbol);
    } else if ((aNumberOrSymbol == @symbol(swapButton)) || (aNumberOrSymbol == @symbol(SM_SWAPBUTTON))) {
	arg = SM_SWAPBUTTON;
	isBool = 1;
    } else if ((aNumberOrSymbol == @symbol(mousePresent)) || (aNumberOrSymbol == @symbol(SM_MOUSEPRESENT))) {
	arg = SM_MOUSEPRESENT;
	isBool = 1;
    } else if ((aNumberOrSymbol == @symbol(mouseButtons)) || (aNumberOrSymbol == @symbol(SM_CMOUSEBUTTONS))) {
	arg = SM_CMOUSEBUTTONS;
    } else if ((aNumberOrSymbol == @symbol(iconWidth)) || (aNumberOrSymbol == @symbol(SM_CXICON))) {
	arg = SM_CXICON;
    } else if ((aNumberOrSymbol == @symbol(iconHeight)) || (aNumberOrSymbol == @symbol(SM_CYICON))) {
	arg = SM_CYICON;
    } else if ((aNumberOrSymbol == @symbol(iconWidth)) || (aNumberOrSymbol == @symbol(SM_CXDOUBLECLK))) {
	arg = SM_CXDOUBLECLK;
    } else if ((aNumberOrSymbol == @symbol(iconHeight)) || (aNumberOrSymbol == @symbol(SM_CYDOUBLECLK))) {
	arg = SM_CYDOUBLECLK;
    } else if ((aNumberOrSymbol == @symbol(cursorWidth)) || (aNumberOrSymbol == @symbol(SM_CXCURSOR))) {
	arg = SM_CXCURSOR;
    } else if ((aNumberOrSymbol == @symbol(cursorHeight)) || (aNumberOrSymbol == @symbol(SM_CYCURSOR))) {
	arg = SM_CYCURSOR;
    } else if ((aNumberOrSymbol == @symbol(captionHeight)) || (aNumberOrSymbol == @symbol(SM_CYCAPTION))) {
	arg = SM_CYCAPTION;
    } else if ((aNumberOrSymbol == @symbol(resizeFrameWidth)) || (aNumberOrSymbol == @symbol(SM_CXFRAME))) {
	arg = SM_CXFRAME;
    } else if ((aNumberOrSymbol == @symbol(resizeFrameHeight)) || (aNumberOrSymbol == @symbol(SM_CYFRAME))) {
	arg = SM_CYFRAME;
    } else if ((aNumberOrSymbol == @symbol(borderFrameWidth)) || (aNumberOrSymbol == @symbol(SM_CXBORDER))) {
	arg = SM_CXBORDER;
    } else if ((aNumberOrSymbol == @symbol(borderFrameHeight)) || (aNumberOrSymbol == @symbol(SM_CYBORDER))) {
	arg = SM_CYBORDER;
    } else if ((aNumberOrSymbol == @symbol(fullScreenWindowWidth)) || (aNumberOrSymbol == @symbol(SM_CXFULLSCREEN))) {
	arg = SM_CXFULLSCREEN;
    } else if ((aNumberOrSymbol == @symbol(fullScreenWindowHeight)) || (aNumberOrSymbol == @symbol(SM_CYFULLSCREEN))) {
	arg = SM_CYFULLSCREEN;
    } else if ((aNumberOrSymbol == @symbol(screenWidth)) || (aNumberOrSymbol == @symbol(SM_CXSCREEN))) {
	arg = SM_CXSCREEN;
    } else if ((aNumberOrSymbol == @symbol(screenHeight)) || (aNumberOrSymbol == @symbol(SM_CYSCREEN))) {
	arg = SM_CYSCREEN;
    } else if ((aNumberOrSymbol == @symbol(minWindowWidth)) || (aNumberOrSymbol == @symbol(SM_CXMIN))) {
	arg = SM_CXMIN;
    } else if ((aNumberOrSymbol == @symbol(minWindowHeight)) || (aNumberOrSymbol == @symbol(SM_CYMIN))) {
	arg = SM_CYMIN;
    } else if ((aNumberOrSymbol == @symbol(vScrollbarWidth)) || (aNumberOrSymbol == @symbol(SM_CXVSCROLL))) {
	arg = SM_CXVSCROLL;
    } else if ((aNumberOrSymbol == @symbol(hScrollbarHeight)) || (aNumberOrSymbol == @symbol(SM_CYHSCROLL))) {
	arg = SM_CYHSCROLL;
    } else if ((aNumberOrSymbol == @symbol(vThumbHeight)) || (aNumberOrSymbol == @symbol(SM_CYVTHUMB))) {
	arg = SM_CYVTHUMB;
    } else if ((aNumberOrSymbol == @symbol(hThumbWidth)) || (aNumberOrSymbol == @symbol(SM_CXHTHUMB))) {
	arg = SM_CXHTHUMB;
    } else if ((aNumberOrSymbol == @symbol(SM_CXSIZE))) {
	arg = SM_CXSIZE;
    } else if ((aNumberOrSymbol == @symbol(SM_CYSIZE))) {
	arg = SM_CYSIZE;
    } else {
	RETURN (nil);
    }
    info = GetSystemMetrics(arg);
    if (isBool) {
	RETURN (info ? true : false);
    }
    RETURN (__MKSMALLINT(info));
%}
!

ignoreButtonPressOnActivate:aBoolean
    "set/clear the ignoreButtonPressOnActivate behavior.
     If on, the button-press which activated a window is ignored (eaten),
     and not passed to the window.
     If off, the window receives the buttonPress after the window is activated.
     The default is true, since it avoids unwanted selections due to window
     activation.
     Returns the previous setting."

%{  /* NOCONTEXT */
    OBJ rslt = __ignoreButtonPressOnActivate ? true : false;

    if (aBoolean == true) {
       __ignoreButtonPressOnActivate = 1;
    } else {
       if (aBoolean == false) {
	   __ignoreButtonPressOnActivate = 0;
       }
    }
    RETURN (rslt);
%}
    "
     WinWorkstation ignoreButtonPressOnActivate:true
     WinWorkstation ignoreButtonPressOnActivate:false
    "
!

primGetSystemColor:aKey
%{  /* NOCONTEXT */
    int p;
    int rgb, r, g, b;

    if (aKey == @symbol(COLOR_WINDOW)) {
      p = COLOR_WINDOW;
    } else if (aKey == @symbol(COLOR_WINDOWTEXT)) {
      p = COLOR_WINDOWTEXT;
    } else if (aKey == @symbol(COLOR_MENU)) {
      p = COLOR_MENU;
    } else if (aKey == @symbol(COLOR_MENUTEXT)) {
      p = COLOR_MENUTEXT;
    } else if (aKey == @symbol(COLOR_BTNFACE)) {
      p = COLOR_BTNFACE;
    } else if (aKey == @symbol(COLOR_BTNSHADOW)) {
      p = COLOR_BTNSHADOW;
    } else if (aKey == @symbol(COLOR_BTNTEXT)) {
      p = COLOR_BTNTEXT;
    } else if (aKey == @symbol(COLOR_GRAYTEXT)) {
      p = COLOR_GRAYTEXT;
    } else if (aKey == @symbol(COLOR_HIGHLIGHT)) {
      p = COLOR_HIGHLIGHT;
    } else if (aKey == @symbol(COLOR_HIGHLIGHTTEXT)) {
      p = COLOR_HIGHLIGHTTEXT;
    } else if (aKey == @symbol(COLOR_MENU)) {
      p = COLOR_MENU;
    } else if (aKey == @symbol(COLOR_MENUTEXT)) {
      p = COLOR_MENUTEXT;
    } else if (aKey == @symbol(COLOR_SCROLLBAR)) {
      p = COLOR_SCROLLBAR;
#ifdef COLOR_SHADOW
    } else if (aKey == @symbol(COLOR_SHADOW)) {
      p = COLOR_SHADOW;
#endif
    } else {
      RETURN (nil);
    }
    rgb = GetSysColor(p);
    /* win uses BGR order */
    r = rgb & 0xFF;
    g = (rgb >> 8) & 0xFF;
    b = (rgb >> 16) & 0xFF;
    rgb = (((r << 8) | g) << 8) | b;
    RETURN (__MKSMALLINT(rgb));
%}.
!

primViewIdFromPoint:aPoint in:windowId
    "given a point in rootWindow, return the viewId of the subview of windowId
     hit by this coordinate. Return nil if no view was hit.
     The returned id may be the id of a non ST view.
     - used to find the window to drop objects after a cross-view drag."

%{

    OBJ xp, yp;
    int xpos, ypos;
    HWND child_return;

    if (ISCONNECTED
     && __isExternalAddress(windowId)
     && __isPoint(aPoint))
    {
	xp = _point_X(aPoint);
	yp = _point_Y(aPoint);
	if (__bothSmallInteger(xp, yp))
	{
	    POINT p;
	    HWND hWnd = _HWNDVal(windowId);
	    p.x = __intVal(xp);
	    p.y = __intVal(yp);
	    ScreenToClient(hWnd, &p);
	    child_return = ChildWindowFromPointEx(hWnd,p,CWP_SKIPINVISIBLE|CWP_SKIPDISABLED|CWP_SKIPTRANSPARENT);
	    /*printf("ChildWindow From %x Point %d.%d = %x\n",hWnd,p.x,p.y,child_return);*/
	    if ((child_return) && (child_return != hWnd))
	    {
		RETURN ( __MKEXTERNALADDRESS(child_return) );
	    }
	    RETURN ( nil );
	}
    }
%}.
!

protocolVersion
    "return the displays protocol version.
     For Windows, a dummy number is returned"

    ^ '1'

    "
     Display protocolVersion
    "
!

rightButtonIsLowerWindow:aBoolean
    "set/clear the rightButtonIsLowerWindow behavior.
     If on, a right-button press in the window-title area lowers the
     window.
     The default is true, since a lower-window operation is very useful,
     and not provided by the standard windows applications.
     Returns the previous setting."

%{  /* NOCONTEXT */
    OBJ rslt = __rightButtonIsLowerWindow ? true : false;

    if (aBoolean == true) {
       __rightButtonIsLowerWindow = 1;
    } else {
       if (aBoolean == false) {
	   __rightButtonIsLowerWindow = 0;
       }
    }
    RETURN (rslt);
%}
    "
     WinWorkstation rightButtonIsLowerWindow:true
     WinWorkstation rightButtonIsLowerWindow:false
    "
!

serverVendor
    "return the server vendor string. 
     For Windows, a dummy name is returned"

    ^ 'microsoft'

    "
     Display serverVendor
    "
!

shiftMask
    "return the state-mask for the SHIFT modified in motion events' state-field.
     Obsolete"

%{  /* NOCONTEXT */
    RETURN (__MKSMALLINT(ShiftMask));
%}
!

shiftModifierMask
    "return the state-mask for the SHIFT modified in motion events' state-field."

%{  /* NOCONTEXT */
    RETURN (__MKSMALLINT(ShiftMask));
%}
!

shiftedLeftButtonIsLowerWindow:aBoolean
    "set/clear the shiftedLeftButtonIsLowerWindow behavior.
     If on, a shift-left-button press in the window-title area lowers the
     window.
     The default is true, since a lower-window operation is very useful,
     and not provided by the standard windows applications.
     Notice, this duplicates the rightButtonIsLowerWindow behavior,
     but is useful with single-button mice.
     Returns the previous setting."

%{  /* NOCONTEXT */
    OBJ rslt = __shiftedLeftButtonIsLowerWindow ? true : false;

    if (aBoolean == true) {
       __shiftedLeftButtonIsLowerWindow = 1;
    } else {
       if (aBoolean == false) {
	   __shiftedLeftButtonIsLowerWindow = 0;
       }
    }
    RETURN (rslt);
%}
    "
     WinWorkstation shiftedLeftButtonIsLowerWindow:true
     WinWorkstation shiftedLeftButtonIsLowerWindow:false
    "
!

supportedSystemColorKeys
    "return a collection of supported systemColor keys"

    ^#( 
	COLOR_WINDOW
	COLOR_WINDOWTEXT
	COLOR_MENU
	COLOR_MENUTEXT
	COLOR_BTNFACE
	COLOR_BTNSHADOW
	COLOR_BTNTEXT
	COLOR_GRAYTEXT
	COLOR_HIGHLIGHT
	COLOR_HIGHLIGHTTEXT
	COLOR_MENU
	COLOR_MENUTEXT
	COLOR_SCROLLBAR
	COLOR_SHADOW
    )
!

translatePoint:aPoint from:windowId1 to:windowId2
    "given a point in window1, return the coordinate in window2.
     This expects a device coordinate (relative to the first views origin)
     in aPoint and returns a device coordinate relative to the 2nd views origin.
     - use to xlate points from a window to rootwindow"

    |x1 y1 x2 y2|

    x1 := x2 := aPoint x truncated.
    y1 := y2 := aPoint y truncated.

%{
    HWND w1, w2;
    POINT point;

    if (__isExternalAddress(windowId1)
     && __isExternalAddress(windowId2)
     && __bothSmallInteger(x1, y1)) {
	w1 = _HWNDVal(windowId1);
	w2 = _HWNDVal(windowId2);
	point.x = __intVal(x1);
	point.y = __intVal(y1);
	CPRINTF(("TransPoint %x %d/%d ->",w1,point.x,point.y));
	if (ClientToScreen(w1,&point) == FALSE) {
	    RETURN (nil);
	}
	if (ScreenToClient(w2,&point) == FALSE) {
	    RETURN (nil);
	}
	CPRINTF((" %x %d/%d\n",w2,point.x,point.y));
	RETURN (__MKPOINT_INT(point.x, point.y));
    }
    RETURN (nil);
%}.
!

vendorRelease
    "return the display-servers vendor release 
     - should normally not be of any interest, 
     but may be usefule for special cases.
     (to avoid bugs in certain implementations / releases)"

    ^ 1

    "
     Display vendorRelease
    "
!

viewIdFromPoint:aPoint in:windowId
    "given a point in rootWindow, return the viewId of the subview of windowId
     hit by this coordinate. Return nil if no view was hit.
     The returned id may be the id of a non ST view.
     - used to find the window to drop objects after a cross-view drag."
     
    windowId notNil ifTrue:[
	aPoint isPoint ifTrue:[
	    "/(aPoint x > 30000) ifTrue:[self halt.].
	    "/(aPoint y > 30000) ifTrue:[self halt.].
	    "/aPoint printString errorPrintCR.
	    ^ self primViewIdFromPoint:aPoint asPoint truncated in:windowId
	]
    ].

    ^ nil
!

whitepixel
    "return the colornumber of white"

    ^ whitepixel
! !

!WinWorkstation methodsFor:'accessing display capabilities'!

captionHeight
    "return the height in pixels of any caption (title-bar) in
     standard topWindows."

    ^ self getSystemMetrics:#captionHeight
!

iconSizes
    "Get the preferred/supported icon sizes."

    |d w h|

    w := self getSystemMetrics:#iconWidth.
    h := self getSystemMetrics:#iconHeight.

    d := IdentityDictionary new.
    d at:#minWidth put:w.
    d at:#maxWidth put:h.
    d at:#widthStep put:1.
    d at:#minHeight put:w.
    d at:#maxHeight put:h.
    d at:#heightStep put:1.

    ^ OrderedCollection with:d

    "
     Display iconSizes
    "
!

preferredIconSize
    "Get the preferrered icon size. 
     Here, workaround windows (bug?) which returns 32@32
     although, 20@20 is much nicer"

    ^ 20@20

    "
     Display preferredIconSize  
    "

    "Modified: / 28.9.1998 / 13:49:54 / cg"
!

scrollsAsynchronous
    "return true, if this display asynchronously sends expose events after a
     scroll operation. False otherwise. Asynchronous expose events are an X
     speciality, which affects a few methods outside of the display class (sorry)"

    ^ true "/ false
!

supportedImageFormats
    "return an array with supported image formats; each array entry
     is another array, consisting of depth and bitsPerPixel values."

    |info|

    info := IdentityDictionary new.
    info at:#depth put:depth.
    info at:#bitsPerPixel put:depth.
    info at:#padding put:32.
    ^ Array with:info

    "
     Disply supportedImageFormats
    "

    "Modified: / 10.9.1998 / 23:14:05 / cg"
!

supportsDeepIcons
    "return true, if this device supports deepicons."

    ^ true
!

supportsIconMasks
    "return true, if this device supports masked icons.
     For now, return false since somehow icons are inverted
     if we use masks (and I dont know yet why)."

    ^ false

    "
     Display supportsIconMasks 
    "

    "Modified: / 28.4.1999 / 19:59:17 / cg"
!

supportsIconViews
    "return true, if this device supports views as icons."

    ^ false "/ true

    "
     Display supportsIconViews
    "

    "Modified: 10.6.1996 / 20:11:48 / cg"
    "Created: 10.6.1996 / 21:08:18 / cg"
!

supportsMaskedDrawingWith:aForm
    "return true, if the device allows the given form pixmap
     to be used as paint color. 
     Sorry - win95 only supports 8x8 masks."

    isWin95 ifFalse:[
	^ true
    ].
    ^ (aForm width == 8) and:[aForm height == 8]

    "Created: / 4.5.1999 / 12:48:49 / cg"
    "Modified: / 4.5.1999 / 12:58:31 / cg"
!

supportsViewBackgroundPixmap:aForm
    "return true, if the device allows the given pixmap as
     viewBackground. If false is returned,
     drawing is done by (possibly) slower smalltalk code.
     Sorry - win95 only supports 8x8 masks."

    isWin95 ifFalse:[
	^ true
    ].
    ^ (aForm width == 8) and:[aForm height == 8]
!

supportsWindowBorder:aNumber
    "return true, if this device supports bordered windows.
     Right now, some drawing stuff depends on (at least) a border of 1 pixel
     to be supported by the device.
     ST/X's views are being rewritten to draw the border manually in the
     future. (Windows only supports borders 1 and noBorder)"

    ^ (aNumber ? 0) <= 1

    "
     Display supportsWindowBorder:1 
    "
!

usableExtent
    "return the usable extent of the display (in pixels).
     Normally, the same as extent, but may be smaller, in
     case some menu space is taken up by the window manager (windows)"

    ^ (self getSystemMetrics:#fullScreenWindowWidth)
      @
      (self getSystemMetrics:#fullScreenWindowHeight)

    "
     Display usableExtent
    "
! !

!WinWorkstation methodsFor:'bitmap/window creation'!

addMenuItemWithLabel:aString toWindowID:aWindowId
    "not yet"
%{
#ifdef NOT_YET_IMPLEMENTED
    if (__isExternalAddress(aWindowId)) {
	HWND hWin = _HWNDVal(aWindowId);
	HMENU hMenu;

	hMenu = GetMenu(hWin);
	if (hMenu) {
        
	}
    }
#endif
%}
!

createBitmapFromArray:anArray width:w height:h
    |bitmapId|

    bitmapId := self primCreateBitmapFromArray:anArray width:w height:h.

    bitmapId isNil ifTrue:[
	'WINWORKSTATION: cannot create bitmap' errorPrintCR.
    ].
    ^ bitmapId
!

createBitmapWidth:w height:h

%{
    HANDLE newBitmapHandle;
    int b_width, b_height;

    if (__bothSmallInteger(w, h)) {
	b_width = __intVal(w);
	b_height = __intVal(h);
	newBitmapHandle = CreateBitmap(b_width, b_height, 1, 1, NULL);

	if (newBitmapHandle) {
#ifdef COUNT_BMP_RESOURCES
	    __cnt_bitmap++;
	    RES_BMP_PRINTF(("CreateBitmap %x %d\n",newBitmapHandle, __cnt_bitmap));
#endif
	    RETURN ( __MKOBJ(newBitmapHandle));
	}
	DPRINTF(("empty bitmap handle = %x\n", newBitmapHandle));
    }
    RETURN (nil);
%}
!

createPixmapWidth:w height:h depth:d
    "allocate a pixmap on the Xserver, the contents is undefined
     (i.e. random). Return a bitmap id or nil"

%{
    HANDLE newBitmapHandle;

    /*printf("CreateBitmap Color\n");*/
    if (__bothSmallInteger(w, h) && __isSmallInteger(d) && ISCONNECTED) {
	if (__intVal(d) == 1) {
	    newBitmapHandle = CreateBitmap(__intVal(w), __intVal(h) , 1, 1, NULL);
	} else {
#if 0
	    if (__intVal(d) != __depth) {
		printf("invalid depth\n");
		RETURN (nil);
	    } 
#endif
	    newBitmapHandle = CreateCompatibleBitmap(__rootDC, __intVal(w), __intVal(h) );
	}

	if (newBitmapHandle) {
#ifdef COUNT_BMP_RESOURCES
	    __cnt_bitmap++;
	    RES_BMP_PRINTF(("CreatePixmap %x %d\n",newBitmapHandle, __cnt_bitmap));
#endif
	    RETURN ( __MKOBJ(newBitmapHandle));
	}
	DPRINTF(("empty bitmap handle = %x\n", newBitmapHandle));
    }
    RETURN (nil);
%}
!

createWindowFor:aView type:typeSymbol
		 origin:origin
		 extent:extent
		 minExtent:minExt
		 maxExtent:maxExt
		 borderWidth:bWidth
		 subViewOf:wsuperView
		 style:wStyle
		 inputOnly:winputOnly
		 label:wlabel
		 owner:wowner
		 icon:wicon
		 iconMask:wiconMask
		 iconView:wiconView


    |xpos ypos wwidth wheight minWidth minHeight maxWidth maxHeight
     wsuperViewId wiconId wiconMaskId windowId
     weventMask wiconHeight
     wiconWidth windowType windowClass moreArgs|

    "/ bColorId wiconViewId bitGravity vBgColor vBgForm deepForm
    "/  preferredVisual preferredDepth  viewGravity wcursorId

    displayId isNil ifTrue:[
	self primitiveFailed.
	^ nil
    ].

    origin notNil ifTrue:[
	xpos := origin x.
	ypos := origin y.
    ] ifFalse:[
	xpos := ypos := 0.
    ].
    extent notNil ifTrue:[
	wwidth := extent x.
	wheight := extent y.
    ] ifFalse:[
	wwidth := 100.
	wheight := 40.
    ].
    minExt notNil ifTrue:[
	minWidth := minExt x.
	minHeight := minExt y
    ].
    maxExt notNil ifTrue:[
	maxWidth := maxExt x.
	maxHeight := maxExt y
    ].

    wsuperView notNil ifTrue:[
	wsuperViewId := wsuperView id
    ].

    wicon notNil ifTrue:[
	wiconId := wicon id.
	wiconHeight := wicon height.
	wiconWidth  := wicon width.
	wiconMask notNil ifTrue:[
	    wiconMaskId := wiconMask id
	]
    ].
"/    wiconView notNil ifTrue:[
"/        wiconViewId := wiconView id
"/    ].
    weventMask := aView eventMask.

    NativeWindows ifTrue:[
	windowType := aView nativeWindowType.

	"/ if it is already a string, take it as it is;
	"/ otherwise, it must be a symbol and is used
	"/ as a key into the nativeClass translation map.

	windowType isString ifTrue:[
	    windowClass := windowType
	] ifFalse:[
	    windowClass := NativeWindowClassTable at:windowType ifAbsent:nil
	]
    ].

    moreArgs := Array new:20.
    moreArgs at:1 put:xpos.
    moreArgs at:2 put:ypos.
    moreArgs at:3 put:wwidth.
    moreArgs at:4 put:wheight.
    moreArgs at:5 put:minWidth.
    moreArgs at:6 put:minHeight.
    moreArgs at:7 put:maxWidth.
    moreArgs at:8 put:maxHeight.
    moreArgs at:9 put:wsuperViewId.
    moreArgs at:10 put:nil. "/ wcursorId.
    moreArgs at:11 put:wiconId.
    moreArgs at:12 put:wiconMaskId.
    moreArgs at:13 put:wiconWidth.
    moreArgs at:14 put:wiconHeight.
    moreArgs at:15 put:windowType.
    moreArgs at:16 put:windowClass.
    moreArgs at:17 put:weventMask.
    moreArgs at:18 put:(aView className asString).

    [
      windowId := self
		  primCreateWindowFor:aView
		  type:typeSymbol
		  borderWidth:bWidth
		  subViewOf:wsuperView
		  style:wStyle
		  inputOnly:winputOnly
		  label:wlabel
		  owner:wowner
		  icon:wicon
		  iconMask:wiconMask
		  moreArgs:moreArgs.
      aView setId:windowId.
    ] valueUninterruptably.

    windowId notNil ifTrue:[self addKnownView:aView withId:windowId].
    ^ windowId
!

destroyGC:aGCId
%{  /* NOCONTEXT */
    if (__isExternalAddress(aGCId)) {
	struct gcData *gcData = _GCDATA(aGCId);

	if (gcData == NULL) {
	    fprintf(stderr, "WinWorkstation [info]: oops - destroying GC twice\n");
	    RETURN(self);
	}

#ifdef COUNT_RESOURCES
	 __cnt_gcData--;
	RESPRINTF(("DestroyGcData %d\n",__cnt_gcData));
#endif

#ifdef CACHE_LAST_DC
	if (lastGcData == gcData) {
	    _releaseDC(gcData);
	}
#endif
	__ExternalAddressInstPtr(aGCId)->e_address = NULL;

	freeGcData(gcData);
    }
%}
!

destroyPixmap:aDrawableId

%{  /* NOCONTEXT */
    if (__isExternalAddress(aDrawableId) && ISCONNECTED) {
	HANDLE bitmapHandle = _HANDLEVal(aDrawableId);

	if (bitmapHandle) {
#ifdef COUNT_BMP_RESOURCES
	    __cnt_bitmap--;
	    RES_BMP_PRINTF(("DestroyPixmap %x %d\n", bitmapHandle, __cnt_bitmap));
#endif
	    _DeleteObject(bitmapHandle, __LINE__);
	}
    }
%}
!

destroyView:aView withId:aWindowId
    self primDestroyView:aView withId:aWindowId.
    self removeKnownView:aView withId:aWindowId
!

gcFor:aDrawableId

%{  /* NOCONTEXT */
    HWND hWnd;
    struct gcData *gcData;

    if (__isExternalAddress(aDrawableId)) {
	hWnd = _HWNDVal(aDrawableId);

	if (! hWnd) {
	    RETURN (nil);
	}

	gcData = newGcData();
	if (! gcData) {
	    RETURN (nil);
	}

#ifdef COUNT_RESOURCES
	__cnt_gcData++;
	RESPRINTF(("CreateGcData %d\n",__cnt_gcData));
#endif
	gcData->hWnd = hWnd;
	gcData->_hDC = 0;
	CPRINTF(("gcFor hDC=%x hWnd=%x\n",gcData->_hDC,gcData->hWnd));
	RETURN ( __MKOBJ(gcData) );
    }
    RETURN (nil);
%}
!

gcForBitmap:aDrawableId

%{  /* NOCONTEXT */

    if (__isExternalAddress(aDrawableId)) {  /* HBITMAP */
	struct gcData *gcData;
	BITMAP bitmap;
	HBITMAP hBitmap = _HBITMAPVAL(aDrawableId);
	HDC hDC;

	if (! hBitmap) {
	    RETURN (nil);
	}
	gcData = newGcData();
	if (! gcData) {
	    RETURN (nil);
	}

	if (GetObject(hBitmap, sizeof(bitmap), &bitmap)) {
	    DPRINTF(("bitmap info:%d\n", bitmap.bmBitsPixel));
	} else {
	    DPRINTF(("noinfo returned\n"));
	    /* mhmh - can this happen ? */
	    bitmap.bmBitsPixel = 1;
	}
	gcData->hBitmap = hBitmap;
	gcData->bitmapColorBitCount = bitmap.bmBitsPixel;

#ifdef COUNT_RESOURCES
	__cnt_gcData++;
	RESPRINTF(("CreateGcData %d\n", __cnt_gcData));
#endif
	RETURN ( __MKOBJ(gcData) );
    }
    RETURN (nil);
%}
!

primCreateBitmapFromArray:anArray width:w height:h

%{ 

    HBITMAP newBitmapHandle;
    int b_width, b_height, bytesPerRowST, bytesPerRowWN, padding;
    int row, col;
    unsigned char *cp, *bPits;
    unsigned char *b_bits = 0;
    int index;
    OBJ num;
    unsigned char *allocatedBits = 0;
    unsigned char fastBits[10000];

    if (__bothSmallInteger(w, h)
     && _isNonNilObject(anArray)) {
	OBJ cls = __qClass(anArray);

	b_width = __intVal(w);
	b_height = __intVal(h);
	bytesPerRowST = (b_width + 7) / 8;
	bytesPerRowWN = ((b_width + 15) / 16) * 2;
	padding = bytesPerRowWN - bytesPerRowST;

	if ((padding == 0) && (cls == @global(ByteArray))) {
	    b_bits = __ByteArrayInstPtr(anArray)->ba_element;
	    cp = 0;
	} else {
	    int nBytes = b_height * bytesPerRowWN;

	    if (nBytes < sizeof(fastBits)) {
		cp = b_bits = fastBits;
	    } else {
		cp = b_bits = allocatedBits = (unsigned char *) malloc(nBytes);
		if (! cp) goto fail;
	    }
	}
	if (cp) {
	    if (cls == @global(Array)) {
		OBJ *op;

		index = 1;
		op = &(__ArrayInstPtr(anArray)->a_element[index - 1]);
		for (row = b_height; row; row--) {
		    for (col = bytesPerRowST; col; col--) {
			num = *op++;
			if (! __isSmallInteger(num))
			    goto fail;
			*cp++ = __intVal(num);
		    }
		    cp += padding;
		}
	    } else if (cls == @global(ByteArray)) {
		unsigned char *pBits;

		pBits = __ByteArrayInstPtr(anArray)->ba_element;
		for (row = b_height; row; row--) {
		    for (col = bytesPerRowST; col; col--) {
			*cp++ = ( *pBits++ /*^ 0xFF*/ );
		    }
		    cp += padding;
		}
	    } else {
		goto fail;
	    }
	}
	CPRINTF(("create bitmap ...\n"));

	newBitmapHandle = CreateBitmap(b_width, b_height, 1, 1, b_bits );

	if (newBitmapHandle ) {
#ifdef COUNT_BMP_RESOURCES
	    __cnt_bitmap++;
	    RES_BMP_PRINTF(("CreateBitmap %x %d\n",newBitmapHandle,__cnt_bitmap));
#endif
	    DDPRINTF(("returning bitmap %x ...\n", newBitmapHandle));
	    if (allocatedBits) {
		free(allocatedBits);
	    }
	    RETURN ( __MKOBJ(newBitmapHandle));
	}
    }
fail: ;
    DDPRINTF(("create bitmap FAILED!!!\n"));
    if (allocatedBits) {
	CPRINTF(("freeing up bitmap bits ...\n"));
	free(allocatedBits);
    }
    CPRINTF(("returning nil ...\n"));
    RETURN ( nil );
%}
!

primCreateWindowFor:aView type:aSymbol
	  borderWidth:bWidth subViewOf:wsuperView
	  style:wStyle inputOnly:winputOnly
	  label:wlabel owner:wowner
	  icon:wicon iconMask:wiconMask
	  moreArgs:moreArgs

%{

    WNDCLASS wc;
    long bg, bd, bw;
    int winStyleBits, winEXStyleBits;
    int w, h, x, y;
    int min_width, min_height;
    int max_width, max_height;
    OBJ eventMask;
    RECT rec;
    HANDLE parentHandle;
    HWND newWindowHandle;
    OBJ xpos, ypos, wwidth, wheight;
    OBJ minWidth, minHeight, maxWidth, maxHeight;
    OBJ wsuperViewId, wcursorId, wiconId, wiconMaskId;
    OBJ wiconWidth, wiconHeight;
    OBJ windowType, windowClass, windowId;
    OBJ stClassName;
    int isTopWindow = 0;
    localWindowInfo *lI;
    static createWindowInfo cwi;
    static nextSequenceNr = 1;
    unsigned char* cp;
    HBITMAP        xBitMap, maskBitmap;
    HICON          xIcon = (HICON)0;
    ICONINFO       iconInfo;
    char *className;
    char nameBuffer[256];
    unsigned char fastBits[10000];

    xpos = __ArrayInstPtr(moreArgs)->a_element[0];
    ypos = __ArrayInstPtr(moreArgs)->a_element[1];
    wwidth = __ArrayInstPtr(moreArgs)->a_element[2];
    wheight = __ArrayInstPtr(moreArgs)->a_element[3];
    minWidth = __ArrayInstPtr(moreArgs)->a_element[4];
    minHeight = __ArrayInstPtr(moreArgs)->a_element[5];
    maxWidth = __ArrayInstPtr(moreArgs)->a_element[6];
    maxHeight = __ArrayInstPtr(moreArgs)->a_element[7];
    wsuperViewId = __ArrayInstPtr(moreArgs)->a_element[8];
    wcursorId = __ArrayInstPtr(moreArgs)->a_element[9];
    wiconId = __ArrayInstPtr(moreArgs)->a_element[10];
    wiconMaskId = __ArrayInstPtr(moreArgs)->a_element[11];
    wiconWidth = __ArrayInstPtr(moreArgs)->a_element[12];
    wiconHeight = __ArrayInstPtr(moreArgs)->a_element[13];
    windowType = __ArrayInstPtr(moreArgs)->a_element[14];
    windowClass = __ArrayInstPtr(moreArgs)->a_element[15];
    eventMask = __ArrayInstPtr(moreArgs)->a_element[16];
    stClassName = __ArrayInstPtr(moreArgs)->a_element[17];
    bg = WhitePixel;
    bd = BlackPixel;

    /* get bitmap for icon */
    if ( __isExternalAddress(wiconId) ) {
	xBitMap = _HBITMAPVAL( wiconId );
	if ( xBitMap != 0 ) {
	    BITMAP bm;

	    if (GetObject(xBitMap, sizeof(bm), (LPSTR)&bm)) {
		int d = bm.bmPlanes * bm.bmBitsPixel;
		BYTE *ep;
		int height, width;
		int nBytes;
		unsigned char *allocatedBits = 0;
		int privateMask = 0;

		if ( __isExternalAddress(wiconMaskId) ) {
		    maskBitmap = _HBITMAPVAL( wiconMaskId );
		} else {
		    if (wiconMaskId != nil) {
			PRINTF(("WinWorkstat [warning]: wiconMaskId is not an external address\n"));
		    }
		    maskBitmap = 0;
		}

		/*
		 * if there is no mask, generate one
		 */
		if (maskBitmap == 0) {
		    DPRINTF(("Bitmap w:%d h:%d p:%d d:%d\n",bm.bmWidth,bm.bmHeight,bm.bmPlanes,bm.bmBitsPixel));
		    height = __intVal( wiconHeight );
		    width  = __intVal( wiconWidth  );
		    nBytes = ( width + 15 ) / 8;
		    nBytes = height * nBytes;
		    if (nBytes < sizeof(fastBits)) {
			ep = fastBits;
		    } else {
			ep = allocatedBits = (unsigned char *) malloc(nBytes);
		    }
		    if ( ep == 0 ) {
			PRINTF(( "WinWorkstat [warning]: malloc failed\n" ));
		    } else {
			memset(ep, 0, nBytes);
			maskBitmap = CreateBitmap(width, height, 1, 1, ep );
			if ( maskBitmap == NULL ) {
			    PRINTF(( "WinWorkstat [warning]: mask CREATION failed\n" ));
			} else {
			    privateMask = 1;
			}
		    }
		}
		if ( maskBitmap != NULL ) {
		    DPRINTF(( "BITMAP mask CREATED!!!\n" ));
		    iconInfo.fIcon = TRUE;
		    iconInfo.hbmMask  = maskBitmap;
		    iconInfo.hbmColor = xBitMap;
		    xIcon = CreateIconIndirect( &iconInfo );
		    if (privateMask) {
			_DeleteObject( maskBitmap, __LINE__ );
		    }
		} else {
		    PRINTF(( "WinWorkstat [warning]: no mask for icon !\n" ));
		}

		if (allocatedBits) {
		    free(allocatedBits);
		}
	    }
	} else {
	    DPRINTF(("WinWorkstat [warning]: xBitMap is zero \n" ));
	}
    } else {
	if (wiconId != nil) {
	    PRINTF(("WinWorkstat [warning]: wiconId is not an external address\n"));
	}
    }

    if (__bothSmallInteger(wwidth, wheight)) {
	w = __intVal(wwidth);
	h = __intVal(wheight);
    } else {
	w = h = 100;
    }

    if (__bothSmallInteger(xpos, ypos)) {
	x = __intVal(xpos);
	y = __intVal(ypos);
    } else {
	x = y = 0;
    }

    if (__bothSmallInteger(minWidth, minHeight)) {
	min_width = __intVal(minWidth);
	min_height = __intVal(minHeight);
    } else {
	min_width = min_height = 0;
    }

    if (__bothSmallInteger(maxWidth, maxHeight)) {
	max_width = __intVal(maxWidth);
	max_height = __intVal(maxHeight);
    } else {
	max_width = max_height = 10000;
    }

    winStyleBits = winEXStyleBits = 0;

    if (__isSmallInteger(bWidth)) {
	bw = __intVal(bWidth);
	if (bw) {
	    winStyleBits |= WS_BORDER;
	    bw = 1;
	}
    } else {
	bw = 0;
    }

    className = app_name;

#ifdef LATER
    if (__isString(windowClass)) {
	className = __stringVal(windowClass);
    }

    if (__isString(windowClass)) {
	if (__isInteger(wStyle)) {
	    winStyleBits |= __longIntVal(wStyle);
	} else {
	    if (windowType == @symbol(RadioButton)) {
		winStyleBits |= BS_RADIOBUTTON;
	    } else if (windowType == @symbol(CheckBox)) {
		winStyleBits |= BS_CHECKBOX;
	    } else if (windowType == @symbol(HorizontalScrollbar)) {
		winStyleBits |= SBS_HORZ;
	    } else if (windowType == @symbol(VerticalScrollBar)) {
		winStyleBits |= SBS_VERT;
	    }
	}
    }
#endif /* LATER */

    if (__isExternalAddress(wsuperViewId)) {
	/*
	 * a child window
	 */
	parentHandle = _HANDLEVal(wsuperViewId);
	winStyleBits |= WS_CHILD;
	if (winputOnly != true) {
	    winStyleBits |= (WS_CLIPSIBLINGS|WS_CLIPCHILDREN);
	    /*winStyleBits |= WS_CLIPCHILDREN;*/
	    DPRINTF(("parent handle=%x\n", parentHandle));
	} else {
	    winEXStyleBits |= WS_EX_TRANSPARENT;
	    DPRINTF(("inputview parent handle=%x\n", parentHandle));
	}
#if 0
	winEXStyleBits |= WS_EX_NOPARENTNOTIFY;
#endif
    } else {
	/*
	 * a top window (regular / popUp or dialog)
	 */
	parentHandle = NULL;
	isTopWindow = 1;

#ifdef TOPWINDOWCLASS
	{
	    this could fail, since a pointer to local buf is passed
	    to the window-creation code (in the other thread ...)
        
	    static winCount = 1;
	    char buf[300];
	    char *stName;

# ifdef __BORLANDC__
	    struct timeb timebuffer;
	    ftime(&timebuffer);
# else
	    struct _timeb timebuffer;
	    _ftime(&timebuffer);
# endif

	    if (__isString(stClassName)) {
		stName = __stringVal(stClassName);
	    } else {
		stName = app_name;
	    }
	    sprintf(buf, "S%d.%d%s", HIWORD(timebuffer.time), ++winCount, stName);
	    DPRINTF(("topview - registerClass:%s\n",buf));

	    wc.style = /* CS_HREDRAW | CS_VREDRAW | CS_OWNDC  |*/ CS_DBLCLKS;
	    wc.lpfnWndProc = (WNDPROC) MainWndProc;
	    wc.cbClsExtra = 0;
	    wc.cbWndExtra = N_WINDOW_PRIVATE;
	    wc.hInstance = (HANDLE) __getHInstance();
	    if (xIcon) {
		wc.hIcon   = xIcon;
		/* wc.hIconSm = wiconId; In 4.x there are large and small icons */
	    } else {
		wc.hIcon = NULL;        /* THIS MUST BE NULL TO DELETE ICONS */
	    }
	    wc.hCursor = 0 /* LoadCursor(NULL, IDC_ARROW) */;
	    wc.hbrBackground = 0; /*CreateSolidBrush (bg);*/

	    wc.lpszMenuName =  NULL;
	    wc.lpszClassName = buf;

	    if (!RegisterClass(&wc)) {
		PRINTF(("RegisterClass failed\n"));
		RETURN( nil );
	    }

	    className = buf;
	}
#endif

	if (wStyle == @symbol(popUp)) {
	    winStyleBits |= WS_POPUP;
	    winStyleBits |= WS_DISABLED;
	    winEXStyleBits |= WS_EX_TOOLWINDOW;
	    CPRINTF(("Create popUpWindow\n"));
#if 0
	    parentHandle = GetActiveWindow();
#endif

	} else if (wStyle == @symbol(dialog)) {
/*
	    winStyleBits |= WS_OVERLAPPED | WS_CAPTION | WS_THICKFRAME;
*/
	    winStyleBits |= WS_OVERLAPPED | WS_CAPTION | DS_MODALFRAME | WS_SYSMENU;
	    winStyleBits |= WS_POPUP;
	    winStyleBits |= DS_NOIDLEMSG;
	    //winEXStyleBits |= WS_EX_TOOLWINDOW;
	    if ((min_width || min_height)
	     && (min_width == max_width)
	     && (min_height == max_height)) {
		winStyleBits &= ~WS_THICKFRAME;
	    }
	} else {
	    //winStyleBits |= WS_OVERLAPPEDWINDOW;
	    winStyleBits |= WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |WS_MAXIMIZEBOX | WS_SIZEBOX;
	    //winEXStyleBits |= WS_EX_CLIENTEDGE;
	    winEXStyleBits |= WS_EX_WINDOWEDGE;
	    //winEXStyleBits |= WS_EX_ACCEPTFILES;
	}
	winStyleBits |= WS_CLIPCHILDREN;
    }

    rec.left = x;
    rec.top = y;
    rec.right = x + w;
    rec.bottom = y+ h;

    /* use static memory for the createwindowInfo;
     * this is required, since we cannot pass the address of a local struct,
     * due to the possibility of late eventProcessing (after the method is left)
     * when in a sizeMove-loop.
     * (in this case, the message remains in the evQ, but is processed
     *  early via the pending- mechanism)
     * in order to detect such an alrady processed message, a sequenceNumber
     * is passed with the message and also stored in the cwi.
     * If this sequence number differes, we got a late message.
     */

    /*
     * check if previous request has been properly processed ...
     */
    if (cwi.sequenceNr && (cwi.sequenceNr != INVALIDATED_CWI)) {
	fprintf(stderr, "WinWorkstation [info]: oops - unprocessed createWindow still pending\n");
	/* this prevents the event processor from interpreting this message */
	cwi.sequenceNr = nextSequenceNr;
    }

    cwi.winStyleBitsEx = WS_EX_LEFT | WS_EX_NOPARENTNOTIFY | winEXStyleBits;

#ifdef ADJUSTWINDOW
    AdjustWindowRectEx(&rec, winStyleBits, 0, cwi.winStyleBitsEx);
    w = rec.right - rec.left;
    h = rec.bottom - rec.top;
#endif

    DPRINTF(("create%s pos==%d/%d size=%d/%d bw=%d parent %x class='%s'...\n",
		((wStyle == @symbol(popUp)) ? " popUp" : ""), x, y, w, h, bw, parentHandle, className));

    /* allocate localMemory for Window */
    lI = (localWindowInfo *) malloc(sizeof(localWindowInfo));
    if (! lI) {
	fprintf(stderr, "WinWorkstation [error]: malloc failed in CreateWindow\n");
	RETURN ( nil );
    }

    memset(lI, 0, sizeof(localWindowInfo));
    if (isTopWindow) {
	if (rec.left < 0) {
	    rec.left = 0;
	    rec.right = w;
	}
	if (rec.top < 0) {
	    rec.top = 0;
	    rec.bottom = h;
	}
	lI->flag = LI_TOPWIN;
    } else {
	if (bw) {
	    // adjust for border
	    rec.left++;
	    rec.top++;
	}
    }

    if (winputOnly == true)
	lI->flag |= LI_INPUTWIN;

#if 0
    /*
     * this leads to leftOver pixel-garbage (views)
     * under win95 ...
     */
    if (winStyleBits & WS_POPUP) 
#else
    if (wStyle == @symbol(popUp))
#endif
    {
	cwi.className = app_namePopup;
    } else {
	cwi.className = className;
    }

    cwi.winStyleBits = winStyleBits;
    cwi.parentHandle = parentHandle;
    cwi.x = rec.left;
    cwi.y = rec.top;
    cwi.dx = rec.right - rec.left;
    cwi.dy = rec.bottom - rec.top;

    if (isTopWindow) {
	rec.left = 0;
	rec.top = 0;
	rec.right = min_width;
	rec.bottom = min_height;
#ifdef ADJUSTWINDOW
	AdjustWindowRectEx(&rec, winStyleBits, 0, cwi.winStyleBitsEx);
#endif
	lI->minWidth = rec.right - rec.left;
	lI->minHeight = rec.bottom - rec.top;

	rec.left = 0;
	rec.top = 0;
	rec.right = max_width;
	rec.bottom = max_height;
#ifdef ADJUSTWINDOW
	AdjustWindowRectEx(&rec, winStyleBits, 0, cwi.winStyleBitsEx);
#endif
	lI->maxWidth = rec.right - rec.left;
	lI->maxHeight = rec.bottom - rec.top;
    } else {
	lI->minWidth = lI->minHeight = 0;
	lI->maxWidth = lI->maxHeight = 9999;
    }

    lI->viewBgBrush = __whiteBrush;
    lI->viewBgColor = WhitePixel;
    lI->bdColor = BlackPixel;

    if (__isSmallInteger(eventMask))
	lI->eventMask = __intVal(eventMask);
    else
	lI->eventMask = 0xffffffff;
    DPRINTF(("eventMask is %x\n"));
#ifdef DEBUGMASK1
    printMask(lI->eventMask);
#endif

    lI->bw = bw;

    cwi.newWinHandle = newWindowHandle = NULL;
    cwi.windowName = 0;

    if (isTopWindow) {
	if (__isString(wlabel) || __isSymbol(wlabel)) {
	    strcpy(nameBuffer, (char *) __stringVal(wlabel));
	    cwi.windowName = nameBuffer;
	    DPRINTF(("title = %s\n", cwi.windowName));
	}
    }

    cwi.infoWasRead = 0;
    cwi.localWindowInfo = lI;
    cwi.sequenceNr = nextSequenceNr;
    nextSequenceNr = (nextSequenceNr + 1) & 0x7FFF;

#ifndef NEW_CREATE_EVENT 
    cwi.hCreateEvent = hCreateEvent;
    ResetEvent(cwi.hCreateEvent);
#else
    /* creating a new event does not work - why ? */
    cwi.hCreateEvent = CreateEvent(
	NULL,        /* no security attributes */
	FALSE,
	FALSE,
	NULL);       /* name of mutex */

    if (cwi.hCreateEvent == NULL) {
	fprintf(stderr, "WinWorkstation [error]: oops - CreateEvent failed in CreateWindow: %d\n", GetLastError() );
	cwi.sequenceNr = INVALIDATED_CWI;
	RETURN ( nil );
    }
#endif

    /*
     * kludge to allow for createWindow while in sizeMove processing loop
     */
    if (pendingCREATEWINDOWInfo) {
	fprintf(stderr, "WinWorkstation [error]: oops - pending create\n");
    }
    pendingCREATEWINDOWInfo = &cwi;
    pendingSequenceNr = cwi.sequenceNr;

    if (PostThreadMessage(_dispatchThreadId, WM_THREAD_CREATEWINDOW, cwi.sequenceNr, (INT)(&cwi)) == FALSE) {
	fprintf(stderr, "WinWorkstation [error]: oops - PostThreadMessage failed in CreateWindow: %d\n", GetLastError() );
	free(lI);
	RETURN (nil);
    }

    if (cwi.newWinHandle != NULL) {
	cwi.sequenceNr = INVALIDATED_CWI;
	cwi.hCreateEvent = NULL;
	/* wow - that was quick ... */
    } else {
	DWORD dwWaitResult;

	/* wait for the window to be created ... */
	dwWaitResult = WaitForSingleObject(hCreateEvent, 1000L);

	/* no longer want to get informed ... */
	cwi.hCreateEvent = NULL;
	cwi.sequenceNr = INVALIDATED_CWI;

	switch (dwWaitResult) {
	    // The thread got mutex ownership.
	    case WAIT_OBJECT_0:
		break;

	    // Cannot get mutex ownership due to time-out.
	    case WAIT_TIMEOUT:
		DEBUGPRINT((stderr, "WinWorkstation [error]: oops - timeout in CreateWindow\n"));
		break;

	    // Got ownership of the abandoned mutex object.
	    default:
		DEBUGPRINT((stderr, "WinWorkstation [warning]: CreateEvent abandoned\n"));
		break;
	}

#ifdef NEW_CREATE_EVENT
	CloseHandle(cwi.hCreateEvent);
#endif
    }

    pendingCREATEWINDOWInfo = 0;
    newWindowHandle = cwi.newWinHandle;
    DPRINTF(("handle = %x\n", newWindowHandle));

    if (! newWindowHandle) {
	fprintf(stderr, "WinWorkstation [error]: CreateWindow failed\n");
	if (cwi.infoWasRead) {
	    free(lI);
	}
	RETURN ( nil );
    }

#ifdef CACHE_LAST_DC
    if (lastGcData && (lastGcHWIN == newWindowHandle)) {
	_releaseDC(lastGcData);
    }
#endif
#ifdef CACHE_LAST_WM_PAINT_DC
    if (last_wm_paint_dc && (last_wm_paint_win == newWindowHandle)) {
	ReleaseDC(newWindowHandle, last_wm_paint_dc);
	last_wm_paint_win = last_wm_paint_dc = 0;
    }
#endif

    windowId = __MKOBJ(newWindowHandle);

    if (1 /* isTopWindow */) {
	/*
	 * mhmh - what is that raise for ?
	 */
	SetWindowPos(newWindowHandle, HWND_TOP, 0, 0, 0, 0, 
		     SWP_NOSENDCHANGING |
		     SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
    }
    if (isTopWindow) {
	BringWindowToTop(newWindowHandle);
    }

#ifndef TOPWINDOWCLASS
    if (xIcon) {
	SendMessage(newWindowHandle, WM_SETICON, ICON_SMALL, (LPARAM)xIcon);
	SendMessage(newWindowHandle, WM_SETICON, ICON_BIG, (LPARAM)xIcon);
    }
#endif      

    DPRINTF(("done - create\n"));
    RETURN (windowId);
%}
!

primDestroyView:aView withId:aWindowId

%{  /* NOCONTEXT */
    HICON oldIcon;

    if (__isExternalAddress(aWindowId)) {
	HWND win = _HWNDVal(aWindowId);
        
	if (win && IsWindow(win)) {
#ifndef TOPWINDOWCLASS
	    oldIcon = (HICON) GetClassLong(win, GCL_HICON);
	    SetClassLong(win, GCL_HICON, (DWORD)0);
	    /* It has to be checked whether this can be deleted!!!!! */
	    if ( oldIcon ) {
		if ( DestroyIcon( oldIcon )) {
		    DPRINTF(( "Old icon deleted\n" ));
		} else {
		    fprintf(stderr, "failed to destroy icon\n");
		}
	    }
#endif
	   /*printf("post WM_THREAD_DESTROYWINDOW\n");*/
	    PostMessage(win, WM_THREAD_DESTROYWINDOW, 0, 0);
	}
    }
%}
!

rootWindowId
    "return the id of the root window.
     This is the window you see as background,
     however, it may or may not be the real physical root window,
     since some window managers install a virtual root window on top
     of the real one. If this is the case, that views id is returned here."

    ^ rootWin
! !

!WinWorkstation methodsFor:'color stuff'!

colorRed:redVal green:greenVal blue:blueVal
    "allocate a color with rgb values (0..100) - return the color index (i.e. colorID).
     This method is obsoleted by #colorScaledRed:scaledGreen:scaledBlue:"

    |r g b|

    r := self percentToDeviceColorValue:redVal.
    g := self percentToDeviceColorValue:greenVal.
    b := self percentToDeviceColorValue:blueVal.
    ^ self colorScaledRed:r scaledGreen:g scaledBlue:b
!

colorScaledRed:r scaledGreen:g scaledBlue:b
    "allocate a color with rgb values (0..16rFFFF) - return the color index
     (i.e. colorID)"

%{  /* NOCONTEXT */
    int id, ir, ig, ib;

    if (__bothSmallInteger(r, g) && __isSmallInteger(b)) {
	id = RGB2st(__intVal(r),__intVal(g),__intVal(b));
	CPRINTF(("alloc color %d/%d/%d -> %x\n", ir, ig, ib, id));
	RETURN ( __MKSMALLINT(id) );
    }
%}.
    self primitiveFailed.
    ^ nil
!

freeColor:colorIndex
    "free a display color when its no longer needed.
     A dummy here, since RGB colors are never freed."

    'free color' printCR.
    ^ self
!

getRGBFrom:index into:aBlock
    "get rgb components (0..100) of color in map at:index,
     and evaluate the 3-arg block, aBlock with them"

    |val|

    self getScaledRGBFrom:index into:[:r :g :b |
	val := aBlock
		value:(r * 100.0 / 16rFFFF)
		value:(g * 100.0 / 16rFFFF)
		value:(b * 100.0 / 16rFFFF)
    ].
    'get RGB' printCR.
    ^ val
!

getRGBFromName:colorName into:aBlock
    "get rgb components (0..100) of color named colorName,
     and evaluate the 3-arg block, aBlock with them"

    |val|

    self getScaledRGBFromName:colorName into:[:r :g :b |
	r isNil ifTrue:[^nil].
	val := aBlock
		value:(r * 100.0 / 16rFFFF)
		value:(g * 100.0 / 16rFFFF)
		value:(b * 100.0 / 16rFFFF)
    ].
    ^val
!

getScaledRGBFrom:index into:aBlock
    "get rgb components (0 .. 16rFFFF) of color in map at:index,
     and evaluate the 3-arg block, aBlock with them"

    |r g b|
%{
    int id = __intVal(index);
    int iR, iG, iB;

    id = st2RGB(id,0);
    iR = id & 0xFF;
    iG = (id >> 8) & 0xFF;
    iB = (id >> 16) & 0xFF;
    iR = (iR << 8) | iR;
    iG = (iG << 8) | iG;
    iB = (iB << 8) | iB;

    r = __MKSMALLINT(iR);
    g = __MKSMALLINT(iG);
    b = __MKSMALLINT(iB);
    /*printf("get color %x -> %x/%x/%x\n",id, iR, iG, iB);*/
%}.
    ^ aBlock value:r value:g value:b
!

getScaledRGBFromName:colorName into:aBlock
    "get scaled rgb components (0..16rFFFF) of color named colorName,
     and evaluate the 3-arg block, aBlock with them"

    |triple r g b found cName|

    r := g := b := 0.
    found := false.
    (colorName startsWith:$#) ifTrue:[
	"/ color in r/g/b hex notation
	r := Integer readFrom:(colorName copyFrom:2 to:3) radix:16.
	g := Integer readFrom:(colorName copyFrom:4 to:5) radix:16.
	b := Integer readFrom:(colorName copyFrom:6 to:7) radix:16.
	found := true.
    ] ifFalse:[
	cName := colorName asString.
	triple := StandardColorValues at:cName ifAbsent:nil.
	triple isNil ifTrue:[
	    "/ try lowercase name
	    cName := cName asLowercase.
	    triple := StandardColorValues at:cName ifAbsent:nil.
	    triple isNil ifTrue:[
		"/ try lowercase without intermixed spaces
		cName := cName asCollectionOfWords asStringWith:nil.
		triple := StandardColorValues at:cName ifAbsent:nil.
	    ].
	].
	triple notNil ifTrue:[
	    r := triple at:1.
	    g := triple at:2.
	    b := triple at:3.
	    found := true.
	].
    ].
    found ifFalse:[
	('WinWorkstation: unknown color: ' , colorName) infoPrintCR.
    ].

    ^ aBlock value:((r * 256) + r) value:((g * 256) + g) value:((b * 256) + b)
!

listOfAvailableColors
    "return a list of all available colornames.
     This should not be used, since colornames are very
     display-specific (here X-specific)."

    |aStream list line index colorName|

    aStream := FileStream readonlyFileNamed:'/usr/lib/X11/rgb.txt'.
    aStream isNil ifTrue:[^ nil].
    list := OrderedCollection new.
    [aStream atEnd] whileFalse:[
	line := aStream nextLine.
	line notNil ifTrue:[
	    "skip the r/g/b numbers"
	    index := 1.
	    [(line at:index) isSeparator] whileTrue:[index := index + 1].
	    [(line at:index) isDigit] whileTrue:[index := index + 1].
	    [(line at:index) isSeparator] whileTrue:[index := index + 1].
	    [(line at:index) isDigit] whileTrue:[index := index + 1].
	    [(line at:index) isSeparator] whileTrue:[index := index + 1].
	    [(line at:index) isDigit] whileTrue:[index := index + 1].
	    [(line at:index) isSeparator] whileTrue:[index := index + 1].
	    colorName := line copyFrom:index.
	    ((colorName occurrencesOf:(Character space)) == 0) ifTrue:[
		list add:colorName
	    ]
	]
    ].
    aStream close.
    ^ list sort

    "
     Screen current listOfAvailableColors
    "

    "Modified: 11.9.1996 / 15:26:28 / stefan"
!

percentToDeviceColorValue:aPercentage
    "given a color-component value in percent (0..100), return the corresponding
     WIN-component value (0..16rFFFF) as an integer"

%{  /* NOCONTEXT */

    if (__isSmallInteger(aPercentage)) {
	RETURN ( __MKSMALLINT(0xFFFF * __intVal(aPercentage) / 100) );
    }
    if (__isFloat(aPercentage)) {
	RETURN ( __MKSMALLINT(0xFFFF * (int)(__floatVal(aPercentage)) / 100) );
    }
%}.
    ^ (16rFFFF * aPercentage / 100) rounded
!

setColor:index scaledRed:sred scaledGreen:sgreen scaledBlue:sblue
    "change a palette index.
     This is not yet supported in the Windows Device 
     - we assume and depend upon transparent 24bit trueColor mode."

     'WinWorkstation [info]: #setColor:scaledRed:scaledGreen:scaledBlue: ignored' infoPrintCR.
     ^ self.

    "Created: 30.1.1998 / 09:27:48 / md"
    "Modified: 30.1.1998 / 09:30:22 / md"
! !

!WinWorkstation methodsFor:'cursor stuff'!

builtInCursorShapes
    "return a collection of standard cursor names.
     Those are built into Windows and need not be created as
     user cursors.
     (actually, there are more than those below ...)"

    "/ if you add something here, also add to #shapeNumberFromCursor ...

    ^ #(
	#upLeftArrow            "/ IDC_ARROW
	#upDownArrow            "/ IDC_SIZENS
	#leftRightArrow         "/ IDC_SIZEWE
	#text                   "/ IDC_IBEAM
	#wait                   "/ IDC_WAIT
	#execute                "/ IDC_APPSTARTING
	#crossHair              "/ IDC_CROSS
	#fourWay                "/ IDC_SIZEALL
	#upRightHand            "/ IDC_ARROW
	#noDrop                 "/ IDC_NO
	#stop                   "/ IDC_NO
       )

    "Modified: / 10.12.1999 / 15:16:26 / cg"
!

createCursorShape:aShape
    "create a cursor given a shape-symbol"

    |number|

    number := self shapeNumberFromSymbol:aShape.
    number isNil ifTrue:[^ nil].
    ^ self primCreateCursorShapeNr:number
!

createCursorSourceForm:sourceBytes maskForm:maskBytes hotX:hx hotY:hy width:w height:h
    "create a cursor given 2 bitmaps (source, mask) and a hotspot"

    |padding bpl bplPadded srcOffs dstOffs
     src mask srcPadded maskPadded cW cH nB nR cursor |

    cW := self getSystemMetrics:#cursorWidth.
    cH := self getSystemMetrics:#cursorHeight.

    "/ ('cursorWidth ',cW printString,'cursorHeight ',cH printString) infoPrintCR.
    src := sourceBytes.
    mask := maskBytes.
    padding := self cursorBitmapPadding.

    "/ repadding and/or resizing required ?
    bpl := sourceBytes size / h.
    bplPadded := (cW + padding - 1) // padding * 2.

    bpl ~~ bplPadded ifTrue:[
	 "/     'repad cursor bits' infoPrintCR.
	srcPadded := ByteArray new:(bplPadded * cH).
	nB := bpl min: bplPadded.
	nR := cH min: h.

	dstOffs := srcOffs := 1.
	1 to:nR do:[:row |
	    srcPadded
		replaceFrom:dstOffs
		to:(dstOffs+nB-1)
		with:src
		startingAt:srcOffs.
	    dstOffs := dstOffs + bplPadded.
	    srcOffs := srcOffs + bpl.
	].

	maskPadded := ByteArray new:(bplPadded * cH).
	dstOffs := srcOffs := 1.
	1 to:nR do:[:row |
	    maskPadded
		replaceFrom:dstOffs
		to:(dstOffs+nB-1)
		with:mask
		startingAt:srcOffs.
	    dstOffs := dstOffs + bplPadded.
	    srcOffs := srcOffs + bpl.
	].
	src := srcPadded.
	mask := maskPadded.

    ].

    src invert.
    1 to:src size do:[:index |
	src byteAt:index put:((src byteAt:index) bitAnd:(mask byteAt:index)).
    ].
    mask invert.
    cursor := self
	primCreateCursorSourceBits:src
	maskBits:mask
	hotX:hx hotY:hy
	width:cW height:cH.
    cursor notNil ifTrue:[^ cursor ].
    'WinWorkstation [warning]: could not create bitmap cursor' infoPrintCR.
!

cursorBitmapPadding
    ^ 16 "/ windows requires short-padded rows
!

destroyCursor:aCursorId
    "destroy a cursor"

%{  /* NOCONTEXT */

    if (ISCONNECTED) {
	if (__isExternalAddress(aCursorId)) {
	    HCURSOR curs = _HCURSORVal(aCursorId);

	    if (curs) {
#ifdef KEEP_STD_CURSORS
		if ((curs != H_C_ARROW)
		 && (curs != H_C_CROSS)
		 && (curs != H_C_IBEAM)
		 && (curs != H_C_ICON)
		 && (curs != H_C_NO)
		 && (curs != H_C_SIZE)
		 && (curs != H_C_SIZEALL)
		 && (curs != H_C_SIZENESW)
		 && (curs != H_C_SIZENS)
		 && (curs != H_C_SIZENWSE)
		 && (curs != H_C_UPARROW)
		 && (curs != H_C_APPSTARTING)
		 && (curs != H_C_WAIT)) 
#endif /* KEEP_STD_CURSORS */
		{
		    DestroyCursor(curs);
#ifdef COUNT_RESOURCES
		    __cnt_cur--;
		    RESPRINTF(("DestroyCursor %d\n",__cnt_cur));
#endif
		}
	    }
	    RETURN ( self );
	}
    }
%}
!

needDeviceFormsForCursor
    ^ false
!

primCreateCursorShapeNr:number
    " create a standard (shape) cursor"

%{  /* NOCONTEXT */
    HCURSOR newCursor;
    LPCTSTR cId;

    if (__isSmallInteger(number)) {
	cId = (LPCTSTR)(__intVal(number));
	newCursor = LoadCursor(NULL, cId);
	if (newCursor) {
#ifdef KEEP_STD_CURSORS
	    if (cId == IDC_ARROW)
		H_C_ARROW = newCursor;
	    else if (cId == IDC_CROSS)
		H_C_CROSS = newCursor;
	    else if (cId == IDC_IBEAM)
		H_C_IBEAM = newCursor;
	    else if (cId == IDC_ICON)
		H_C_ICON = newCursor;
	    else if (cId == IDC_NO)
		H_C_NO = newCursor;
	    else if (cId == IDC_SIZE)
		H_C_SIZE = newCursor;
	    else if (cId == IDC_SIZEALL)
		H_C_SIZEALL = newCursor;
	    else if (cId == IDC_SIZENESW)
		H_C_SIZENESW = newCursor;
	    else if (cId == IDC_SIZENS)
		H_C_SIZENS = newCursor;
	    else if (cId == IDC_SIZENWSE)
		H_C_SIZENWSE = newCursor;
	    else if (cId == IDC_UPARROW)
		H_C_UPARROW = newCursor;
	    else if (cId == IDC_WAIT)
		H_C_WAIT = newCursor;
	    else if (cId == IDC_APPSTARTING)
		H_C_APPSTARTING = newCursor;
#endif /* KEEP_STD_CURSORS */
	    RETURN ( __MKOBJ(newCursor) );
	}
    }
    RETURN (nil);
%}
!

primCreateCursorSourceBits:src maskBits:mask hotX:hx hotY:hy width:w height:h
    "create a cursor from given pixels"

%{  
    HCURSOR newCursor;

    if (__isByteArray(src)
     && __isByteArray(mask)
     && __bothSmallInteger(hx, hy)
     && __bothSmallInteger(w, h)) {

	newCursor = CreateCursor((HANDLE) __getHInstance(),
				 __intVal(hx), __intVal(hy),
				 __intVal(w), __intVal(h),
				 __ByteArrayInstPtr(mask)->ba_element,
				 __ByteArrayInstPtr(src)->ba_element);
	if (newCursor) {
#ifdef COUNT_RESOURCES
	    __cnt_cur++;
	    RESPRINTF(("CreateCursor %d\n",__cnt_cur));
#endif
	    RETURN ( __MKOBJ(newCursor));
	}
    }
    RETURN (nil);
%}
!

shapeNumberFromSymbol:shape
    "given a shape-symbol, return the corresponding cursor-number,
     or nil if no such standard cursor exists."

    "this is pure Win-knowlegde - but you may easily add more"

%{  /* NOCONTEXT */
    if (shape == @symbol(upRightHand)) {
	RETURN ( __MKSMALLINT( (INT)IDC_ARROW));
    }
    if (shape == @symbol(upLeftArrow)) {
	RETURN ( __MKSMALLINT( (INT)IDC_ARROW));
    }
    if (shape == @symbol(upDownArrow)) {
	RETURN ( __MKSMALLINT( (INT)IDC_SIZENS));
    }
    if (shape == @symbol(leftRightArrow)) {
	RETURN ( __MKSMALLINT( (INT)IDC_SIZEWE));
    }
    if (shape == @symbol(text)) {
	RETURN ( __MKSMALLINT( (INT)IDC_IBEAM));
    }
    if (shape == @symbol(wait)) {
	RETURN ( __MKSMALLINT( (INT)IDC_WAIT));
    }
    if (shape == @symbol(crossHair)) {
	RETURN ( __MKSMALLINT( (INT)IDC_CROSS));
    }
    if (shape == @symbol(fourWay)) {
	RETURN ( __MKSMALLINT( (INT)IDC_SIZEALL));
    }
    if (shape == @symbol(execute)) {
	RETURN ( __MKSMALLINT( (INT)IDC_APPSTARTING));
    }
    if (shape == @symbol(noDrop)) {
	RETURN ( __MKSMALLINT( (INT)IDC_NO));
    }
    if (shape == @symbol(stop)) {
	RETURN ( __MKSMALLINT( (INT)IDC_NO));
    }

    if (shape == @symbol(IDC_UPARROW)) {
	RETURN ( __MKSMALLINT( (INT)IDC_UPARROW));
    }

    if (shape == @symbol(IDC_SIZE)) {
	RETURN ( __MKSMALLINT( (INT)IDC_SIZE));
    }
    if (shape == @symbol(IDC_SIZENWSE)) {
	RETURN ( __MKSMALLINT( (INT)IDC_SIZENWSE));
    }
    if (shape == @symbol(IDC_SIZENESW)) {
	RETURN ( __MKSMALLINT( (INT)IDC_SIZENESW));
    }
    if (shape == @symbol(IDC_NO)) {
	RETURN ( __MKSMALLINT( (INT)IDC_NO));
    }
#ifdef IDC_HELP
    if (shape == @symbol(IDC_HELP)) {
	RETURN ( __MKSMALLINT( (INT)IDC_HELP));
    }
#endif
%}.
"/    ('WINWORKSTATION: invalid cursorShape:' , shape printString) infoPrintNL.
    ^  nil
! !

!WinWorkstation methodsFor:'drawing'!

copyFromId:sourceId x:srcX y:srcY gc:srcGCId to:destId x:dstX y:dstY gc:dstGCId
		width:w height:h
    "do a bit-blt; copy bits from the rectangle defined by
     srcX/srcY and w/h from the sourceId drawable to the rectangle
     below dstX/dstY in the destId drawable. Trigger an error if any
     argument is not integer."

%{  
    struct gcData *dstGcData = 0;
    struct gcData *srcGcData = 0;
    HDC srcDC = (HDC)0;
    HDC dstDC = (HDC)0;
    int     dstGcOwnerThreadID;
    HWND    dstGcHWIN;
    HBITMAP dstGcHBITMAP;

    if (! __isExternalAddress(srcGCId)
     || ! __isExternalAddress(dstGCId)) {
	goto fail;
    }
    srcGcData = _GCDATA(srcGCId);
    dstGcData = _GCDATA(dstGCId);

    if (__bothSmallInteger(w, h)
     && __bothSmallInteger(srcX, srcY)
     && __bothSmallInteger(dstX, dstY)
     && srcGcData
     && dstGcData) {
	int fun = BITBLT_COPY;
	int src_fg, src_bg, dst_fg, dst_bg;

	fun = dstGcData->bitbltrop2;
#if 0
	switch (fun) {
	  case BITBLT_COPY:
	    printf("BITBLT_COPY\n");
	    break;
	  case BITBLT_COPYINVERTED:
	    printf("BITBLT_COPYINVERTED\n");
	    break;
	  case BITBLT_XOR:
	    printf("BITBLT_XOR\n");
	    break;
	  case BITBLT_AND:
	    printf("BITBLT_AND\n");
	    break;
	  case BITBLT_OR:
	    printf("BITBLT_OR\n");
	    break;
	}
#endif

	/*
	 * a scroll operation ?
	 */
	if (srcGcData->hWnd 
	 && ((srcGcData->hWnd == dstGcData->hWnd)
	 && (fun == BITBLT_COPY))) {
	    RECT rec, updRect;
	    RECT invRec;
	    HRGN updRgn = 0, dstRgn = 0, nUpdRgn = 0;
	    int _srcX = __intVal(srcX);
	    int _srcY = __intVal(srcY);
	    int _dstX = __intVal(dstX);
	    int _dstY = __intVal(dstY);
	    int _w = __intVal(w);
	    int _h = __intVal(h);
	    int noExpose = 0;
	    int _deltaX, _deltaY;

#ifdef CACHE_LAST_DC
	    if (lastGcData) {
		_releaseDC(lastGcData);     
	    }
#endif

	    rec.left = _srcX;
	    rec.top  = _srcY;
	    rec.right = rec.left + _w;
	    rec.bottom = rec.top + _h;

	    _deltaX = _dstX - _srcX;
	    _deltaY = _dstY - _srcY;

	    DPRINTF(("dst and src is HWND %x fun == BITBLT_COPY --> scroll %d/%d\n",
		    srcGcData->hWnd,
		    _deltaX,
		    _deltaY));

	    if ((_deltaX == 0) && (_deltaY == 0)) {
		noExpose = 1;
	    } else {
		updRgn = CreateRectRgn(0, 0, 0, 0);
		ScrollWindowEx(srcGcData->hWnd,
			    _deltaX,   
			    _deltaY,   
			    &rec,            /* area to scroll */
			    0,               /* clip region */
			    updRgn, 
			    NULL, /* &invRec, */
			    0 /* SW_ERASE | SW_INVALIDATE */);

		/*
		 * we are not interested in the source-area;
		 * only care for exposed areas in the dst area.
		 */
		dstRgn = CreateRectRgn(_dstX, _dstY, _dstX+_w, _dstY+_h); 
		nUpdRgn = CreateRectRgn(0, 0, 0, 0); 
		switch (CombineRgn(nUpdRgn, updRgn, dstRgn, RGN_AND)) {
		    case ERROR:
			_DeleteObject(nUpdRgn, __LINE__);
			nUpdRgn = updRgn;
			noExpose = 1;
			break;
		    case NULLREGION:
			noExpose = 1;
			break;
		    default:
			if (__generateExposes(srcGcData->hWnd, nUpdRgn, __WM_GEXPOSE, 1) <= 0) {
			    noExpose = 1;
			}
			break;
		}
	    }
	    if (noExpose) {
		/*
		 * no exposes generated - must send a NOEXPOSE
		 */
		enqEvent(ENQ_AT_END, ExposureMask, srcGcData->hWnd, __WM_NOGEXPOSE, 0, 0, 0, 0, 0, EV_NOTIME);
	    }

	    if (dstRgn) {
		_DeleteObject(dstRgn, __LINE__);
	    }
	    if (nUpdRgn && (nUpdRgn != updRgn)) {
		_DeleteObject(nUpdRgn, __LINE__);
	    }
	    if (updRgn) {
		_DeleteObject(updRgn, __LINE__);
	    }
	    RETURN ( self );
	}

	fun = dstGcData->bitbltrop2;

	if (0 /* fun == BITBLT_COPY */) {
	    src_fg = dst_fg = 0xFFFFFF;
	    src_bg = dst_bg = 0x000000;
	} else {
	    src_fg = srcGcData->fgColor;
	    src_bg = srcGcData->bgColor;
	    dst_fg = dstGcData->fgColor;
	    dst_bg = dstGcData->bgColor;
	}

	/*
	 * a simple copy (like scroll, but in a bitmap) ?
	 */
	if ((srcGcData == dstGcData)
	 && (fun == BITBLT_COPY)) {
	    RECT rec, uprec;
	    int _srcX, _srcY;

	    rec.left = _srcX = __intVal(srcX);
	    rec.top = _srcY = __intVal(srcY);
	    rec.right = rec.left + __intVal(w);
	    rec.bottom = rec.top + __intVal(h);
	    srcDC = _getDC(srcGcData);
	    SetBkColor(srcDC, src_fg);
	    SetTextColor(srcDC, src_bg);

	    DPRINTF(("dst and src is DC %x fun == BITBLT_COPY  --> scrolling %d %d\n",srcDC,__intVal(dstX) - __intVal(srcX),__intVal(dstY) - __intVal(srcY)));
	    ScrollDC(srcDC, __intVal(dstX)-_srcX, 
			    __intVal(dstY)-_srcY, 
			    &rec, 0, 0, &uprec);

	    SetBkColor(srcDC, srcGcData->bgColor);
	    SetTextColor(srcDC, srcGcData->fgColor);
#ifndef CACHE_LAST_DC
	    _releaseDC(srcGcData);
#endif
	    RETURN ( self );
	}

#ifdef xxCACHE_LAST_DC
/* mhmh - this should not be needed */
	if (lastGcData) {
	    _releaseDC(lastGcData);     
	}
#endif

	if (dstGcData == srcGcData) {
	    srcDC = dstDC = _getDC(dstGcData);
	} else {
	    srcDC = _getDC(srcGcData);

	    /* 
	     * kludge - pretent nothing is cached for a moment,
	     * to avoid release in getDC below ...
	     */
#ifdef CACHE_LAST_DC
	    dstGcOwnerThreadID = lastGcOwnerThreadID;
	    dstGcHWIN = lastGcHWIN;
	    dstGcHBITMAP = lastGcHBITMAP;
	    lastGcData = 0;  
#endif

	    dstDC = _getDC(dstGcData);
	    SetBkColor(dstDC, dst_fg);
	    SetTextColor(dstDC, dst_bg);
	}

	SetBkColor(srcDC, src_fg);
	SetTextColor(srcDC, src_bg); 

	CPRINTF(("bitblt src f:%x b:%x",GetTextColor(srcDC),GetBkColor(srcDC)));
	CPRINTF(("dst f:%x b:%x\n",GetTextColor(dstDC),GetBkColor(dstDC)));
	if (BitBlt(dstDC,
	     __intVal(dstX), __intVal(dstY),
	     __intVal(w), __intVal(h),
	     srcDC,
	     __intVal(srcX), __intVal(srcY),
	     fun)
	   == 0
	  ) {
	    fprintf(stderr, "WinWorkstation [info]: ERROR in BitBlt\n");
	}

	if (dstGcData != srcGcData) {
	    SetBkColor(dstDC, dstGcData->bgColor);
	    SetTextColor(dstDC, dstGcData->fgColor);
	}
	SetBkColor(srcDC, srcGcData->bgColor);
	SetTextColor(srcDC, srcGcData->fgColor);

	if (srcGcData != dstGcData) {
	    _releaseDC(srcGcData);
	}

#ifdef CACHE_LAST_DC
	/*
	 * kludge again - restore last-DC cache 
	 */
	lastGcData = dstGcData;
	lastGcOwnerThreadID = dstGcOwnerThreadID;
	lastGcHWIN = dstGcHWIN;
	lastGcHBITMAP = dstGcHBITMAP;
#else
	_releaseDC(dstGcData);
#endif
	RETURN ( self );
    }

 fail: ;
%}.
    self primitiveFailed.
    ^ nil
!

copyPlaneFromId:sourceId x:srcX y:srcY gc:srcDCId to:destId x:dstX y:dstY gc:dstDCId
		width:w height:h
    "do a bit-blt, but only copy the low-bit plane;
     copy bits from the rectangle defined by
     srcX/srcY and w/h from the sourceId drawable to the rectangle
     below dstX/dstY in the destId drawable. Trigger an error if any
     argument is not integer."

    ^ self
	copyFromId:sourceId
		 x:srcX y:srcY gc:srcDCId
		to:destId x:dstX y:dstY gc:dstDCId
	     width:w height:h
!

displayArcX:x y:y width:width height:height from:startAngle angle:angle in:aDrawableId with:aGCId
    "draw an arc. If any of x,y, w or h is not an integer, an error is triggered.
     The angles may be floats or integer - they are given in degrees."

%{
    int __x, __y, w, h;
    float angle1, angle2;
    double f;

    if (__isSmallInteger(startAngle))
	angle1 = (float)(__intVal(startAngle));
    else if (__isFloat(startAngle)) {
	angle1 = (float) __floatVal(startAngle);
    } else if (__isShortFloat(startAngle)) {
	angle1 = __shortFloatVal(startAngle);
    } else goto bad;

    if (__isSmallInteger(angle))
	angle2 = (float)(__intVal(angle));
    else if (__isFloat(angle)) {
	angle2 = (float) __floatVal(angle);
    } else if (__isShortFloat(angle)) {
	angle2 = __shortFloatVal(angle);
    } else goto bad;

    if (angle2 <= 0) {
	RETURN (self);
    }

    if (__isExternalAddress(aGCId)
     && __isExternalAddress(aDrawableId)
     && __bothSmallInteger(x, y)
     && __bothSmallInteger(width, height)) 
     {
	struct gcData *gcData = _GCDATA(aGCId);
	HDC hDC;
	POINT p;

	w = __intVal(width);
	h = __intVal(height);
	__x = __intVal(x);
	__y = __intVal(y);

	hDC = _getDC(gcData);

	if (! GcDataGetPen(hDC, gcData)) {
	    DPRINTF(("displayArc: no pen\n"));
	} else {
	    double xB, yB, xE, yE, xR, yR;

	    xR = w / 2;
	    yR = h / 2;
	    if (angle2 - angle1 >= 360) {
		xB = xE = __x + xR + 0.5;
		yB = yE = __y /*+ yR + 0.5*/;
	    } else {
		double sin(), cos();
		float rad1, rad2;

		if (angle1 <= 180)
		  angle1 = 180 - angle1;
		else
		  angle1 = 360 + 180 - angle1;
		angle2 = angle1 - angle2;
		/* sigh - compute the intersections ... */
		rad1 = (angle1 * 3.14159265359) / 180.0;
		rad2 = (angle2 * 3.14159265359) / 180.0;
		xB = cos(rad1) * xR;
		yB = sin(rad1) * yR;
		xE = cos(rad2) * xR;
		yE = sin(rad2) * yR;
		xB = __x + xR - xB + 0.5;
		yB = __y + yR - yB + 0.5;
		xE = __x + xR - xE + 0.5;
		yE = __y + yR - yE + 0.5;
	    }
	    DPRINTF(("Arc x=%d y=%d w=%d h=%d xB=%d xE=%d yB=%d yE=%d a1=%f a2=%f\n",__x,__y,w,h,(int)xB,(int)xE,(int)yB,(int)yE,angle1,angle2));
	    Arc(hDC,
		__x, __y,
		__x + w, __y + h,
		(int)xB, (int)yB,
		(int)xE, (int)yE);

	    GcDataReleasePen(hDC, gcData);
	}
#ifndef CACHE_LAST_DC
	_releaseDC(gcData);
#endif
	RETURN ( self );
    }
    bad: ;
%}.
    self primitiveFailed
!

displayLineFromX:x0 y:y0 toX:x1 y:y1 in:aDrawableId with:aGCId
    "draw a line. If the coordinates are not integers, an error is triggered."

%{  /* NOCONTEXT */
    if (__isExternalAddress(aGCId)
     /*&& __isExternalAddress(aDrawableId)*/
     && __bothSmallInteger(x0, y0)
     && __bothSmallInteger(x1, y1)) {
	struct gcData *gcData = _GCDATA(aGCId);
	int __x1 = __intVal(x1), __y1 = __intVal(y1);
	HDC hDC = _getDC(gcData);

/*      DPRINTF(("displayLine: %d/%d -> %d/%d in %x\n",
		    __intVal(x0), __intVal(y0),
		    __x1, __y1,_HWNDVal(aDrawableId)));
*/
	if (! GcDataGetPen(hDC, gcData)) {
	    DPRINTF(("displayLine: no pen\n"));
	} else {
	    MoveToEx(hDC, __intVal(x0), __intVal(y0), NULL);

	    LineTo(hDC, __x1, __y1);

	    /*
	     * end-point ...
	     */
	    LineTo(hDC, __x1+1, __y1);

	    GcDataReleasePen(hDC, gcData);
	}
#ifndef CACHE_LAST_DC
	_releaseDC(gcData);
#endif
	RETURN ( self );
    }
%}
!

displayPointX:px y:py in:aDrawableId with:aGCId
    "draw a point. If x/y are not integers, an error is triggered."

%{  /* NOCONTEXT */
    if (__isExternalAddress(aGCId)
     /* && __isExternalAddress(aDrawableId) */
     && __bothSmallInteger(px, py)) {
	struct gcData *gcData = _GCDATA(aGCId);
	HDC hDC;
	POINT p;
	int __x = __intVal(px), __y = __intVal(py);

#ifdef OLD
	int savedLStyle = gcData->lStyle;
	int savedLWidth = gcData->lineWidth;

	/*
	 * a point is a point - no matter what lineWidth we have set before
	 */
	if ((gcData->lStyle != PS_SOLID)
	 || (gcData->lineWidth > 1)) {
	    FLUSH_CACHED_DC(gcData);
	    gcData->lStyle = PS_SOLID;
	    gcData->lineWidth = 0;
	}

	hDC = _getDC(gcData);

	if (! GcDataGetPen(hDC,gcData)) {
	    DPRINTF(("displayPoint: no pen\n"));
	} else {
	    MoveToEx(hDC, __x, __y, NULL);
	    /*
	     * end-point ...
	     */
	    LineTo(hDC, __x+1, __y);

	    GcDataReleasePen(hDC, gcData);
	}

# ifndef CACHE_LAST_DC
	_releaseDC(gcData);
# endif
	if ((gcData->lStyle != savedLStyle)
	 || (gcData->lineWidth != savedLWidth)) {
	    FLUSH_CACHED_DC(gcData);
	    gcData->lStyle = savedLStyle;
	    gcData->lineWidth = savedLWidth;
	}
#else /* NEW */

	hDC = _getDC(gcData);
	SetPixelV(hDC, __x, __y, gcData->fgColor);
# ifndef CACHE_LAST_DC
	_releaseDC(gcData);
# endif

#endif /* NEW */
	RETURN ( self );
    }
%}
!

displayPolygon:aPolygon in:aDrawableId with:aGCId
    "draw a polygon, the argument aPolygon is a Collection of individual points,
     which define the polygon.
     If any coordinate is not integer, an error is triggered."

    |numberOfPoints|

    numberOfPoints := aPolygon size.

%{
    OBJ point, px, py;
    int i, num;

    if (__isExternalAddress(aGCId)
     /* && __isExternalAddress(aDrawableId) */
     && __isSmallInteger(numberOfPoints)) {
	struct gcData *gcData = _GCDATA(aGCId);
	HDC hDC = 0;
	POINT p;

	num = __intVal(numberOfPoints);

	for (i=0; i<num; i++) {
	    point = __AT_(aPolygon, __MKSMALLINT(i+1));
	    if (! __isPoint(point)) goto fail;
	    px = _point_X(point);
	    py = _point_Y(point);
	    if (! __bothSmallInteger(px, py)) {
		goto fail;
	    }
	}

	hDC = _getDC(gcData);
	if (! GcDataGetPen(hDC,gcData)) {
	    DPRINTF(("displayPolygon: no pen\n"));
	} else {
	    for (i=0; i<num; i++) {
		point = __AT_(aPolygon, __MKSMALLINT(i+1));
		px = _point_X(point);
		py = _point_Y(point);
		p.x = __intVal(px);
		p.y = __intVal(py);
		if (i == 0) {
		    MoveToEx(hDC, p.x, p.y, NULL);
		} else {
		    if (i == (num-1)) {
			PolylineTo(hDC, &p, 1);
		    } else {
			LineTo(hDC, p.x, p.y);
			/*
			 * end-point ...
			 */
			LineTo(hDC, p.x+1, p.y);
		    }
		}
	    }
	    GcDataReleasePen(hDC, gcData);
	}

#ifndef CACHE_LAST_DC
	_releaseDC(gcData);
#endif
	RETURN ( self );
    }
fail: ;
%}
!

displayPolylines:aPolyline in:aDrawableId with:aGCId
    "draw a polyline, the argument aPolyline is a collection of individual points,
     which define the lines (p1/p2 pairs); must be even in size.
     If any coordinate is not integer, an error is triggered."

    |numberOfPoints|

    numberOfPoints := aPolyline size.

%{
    OBJ point, px, py;
    int i, num;

    if (__isExternalAddress(aGCId)
     /* && __isExternalAddress(aDrawableId) */
     && __isSmallInteger(numberOfPoints)) {
	struct gcData *gcData = _GCDATA(aGCId);
	HDC hDC = 0;
	POINT p;

	num = __intVal(numberOfPoints);

	for (i=0; i<num; i++) {
	    point = __AT_(aPolyline, __MKSMALLINT(i+1));
	    if (! __isPoint(point)) goto fail;
	    px = _point_X(point);
	    py = _point_Y(point);
	    if (! __bothSmallInteger(px, py)) {
		goto fail;
	    }
	}

	hDC = _getDC(gcData);
	if (! GcDataGetPen(hDC,gcData)) {
	    DPRINTF(("displayPolygon: no pen\n"));
	} else {
	    for (i=0; i<num; i++) {
		point = __AT_(aPolyline, __MKSMALLINT(i+1));
		px = _point_X(point);
		py = _point_Y(point);
		p.x = __intVal(px);
		p.y = __intVal(py);
		if ((i & 1) == 0) {
		    MoveToEx(hDC, p.x, p.y, NULL);
		} else {
		    LineTo(hDC, p.x, p.y);
		    /*
		     * end-point ...
		     */
		    LineTo(hDC, p.x+1, p.y);
		}
	    }
	    GcDataReleasePen(hDC, gcData);
	}

#ifndef CACHE_LAST_DC
	_releaseDC(gcData);
#endif
	RETURN ( self );
    }
fail: ;
%}
!

displayRectangleX:x y:y width:width height:height in:aDrawableId with:aGCId
    "draw a rectangle. If the coordinates are not integers, an error is triggered."

%{  /* NOCONTEXT */
    int w, h;
    int xL, yT;
    if (__isExternalAddress(aGCId)
     /* && __isExternalAddress(aDrawableId) */
     && __bothSmallInteger(x, y)
     && __bothSmallInteger(width, height)) {
	struct gcData *gcData = _GCDATA(aGCId);
	HDC hDC = _getDC(gcData);

	xL = __intVal(x);
	yT = __intVal(y);
	w = __intVal(width);
	h = __intVal(height);

	DPRINTF(("displayRectangle: %d/%d -> %d/%d\n",
			xL, yT, w, h));

	if ((w >= 0) && (h >= 0)) {
	    if (! GcDataGetPen(hDC,gcData)) {
		DPRINTF(("displayRect: no pen\n"));
	    } else {
		MoveToEx(hDC, xL, yT, NULL);
		LineTo(hDC, xL+w, yT);
		LineTo(hDC, xL+w, yT+h);
		LineTo(hDC, xL, yT+h);
		LineTo(hDC, xL, yT);

		GcDataReleasePen(hDC, gcData);
	    }
	}
#ifndef CACHE_LAST_DC
	_releaseDC(gcData);
#endif
	RETURN ( self );
    }
%}
!

displayString:aString from:index1 to:index2 x:x y:y in:aDrawableId with:aGCId opaque:opaque
    "draw a sub-string - if opaque is false, draw foreground only; otherwise, draw both
     foreground and background characters.
     If the coordinates are not integers, an error is triggered."

%{  /* NOCONTEXT */
    unsigned char *cp;
    OBJ cls;
    int  i1, i2, l, n;
    int nInstBytes;

    if (__isExternalAddress(aGCId)
     && __isExternalAddress(aDrawableId)
     && __isNonNilObject(aString)
     && __bothSmallInteger(index1, index2)
     && __bothSmallInteger(x, y)) 
    {
	struct gcData *gcData = _GCDATA(aGCId);
	int pX, pY;
	HDC hDC = _getDC(gcData);
	HFONT hOldFont;
	pX = __intVal(x);
	pY = __intVal(y);
	pY -= gcData->fontAscent;

	if (opaque == true) {
	    if (gcData->bkMode != BK_OPAQUE) {
		SetBkMode(hDC, OPAQUE);
		gcData->bkMode = BK_OPAQUE;
	    }
	} else {
	    if (gcData->bkMode != BK_TRANSPARENT) {
		SetBkMode(hDC, TRANSPARENT);
		gcData->bkMode = BK_TRANSPARENT;
	    }
	}
#if 0
	hOldFont = SelectObject(hDC, gcData->hFont);
	SetTextColor(hDC, gcData->fgColor);
	SetBkColor(hDC, gcData->bgColor);
#endif

	cls = __qClass(aString);

	i1 = __intVal(index1) - 1;
	if (i1 >= 0) {
	    i2 = __intVal(index2) - 1;
	    if (i2 < i1) {
		goto ret; 
	    }

	    cp = _stringVal(aString);
	    l = i2 - i1 + 1;

	    if ((cls == @global(String)) || (cls == @global(Symbol))) {
		n = _stringSize(aString);
		if (i2 < n) {
		    cp += i1;
		    CPRINTF(("string1: %s pos=%d/%d l=%d hDC=%x hWnd=%x\n", cp, pX, pY,l,hDC,_HWNDVal(aDrawableId)));

		    if (l > 32767) {
			l = 32767;
		    }
		    if (! TextOut(hDC, pX, pY, (char *)cp, l)) {
			PRINTF(("Textout failed. %d\n", GetLastError()));
		    }
		    goto ret; 
		}
	    }

	    nInstBytes = __OBJS2BYTES__(__intVal(__ClassInstPtr(cls)->c_ninstvars));
	    cp += nInstBytes;
	    n = __byteArraySize(aString) - nInstBytes;

	    if (__isBytes(aString)) {
		if (i2 < n) {
		    cp += i1;
		    CPRINTF(("string: %s pos=%d/%d\n", cp, pX, pY));
		    if (l > 32767) {
			l = 32767;
		    }
		    if (! TextOut(hDC, pX, pY, (char *)cp, l)) {
			PRINTF(("Textout failed. %d\n", GetLastError()));
		    }
		    goto ret; 
		}
	    }

	    /* Unicode */
	    if (__isWords(aString)) {
		n = n / 2;
		if (i2 < n) {
		    WIDECHAR *w_cp = (WIDECHAR *)cp;

		    w_cp += i1;

		    if (! TextOutW(hDC, pX, pY, w_cp, l)) {
			PRINTF(("TextoutW failed. %d\n", GetLastError()));
		    }
		    printf("TextoutW ok.\n");
		    goto ret;
		}
	    }
	}
ret:   
#if 0
	SelectObject(hDC, hOldFont);
#endif
#ifndef CACHE_LAST_DC
	_releaseDC(gcData);
#endif
    }
%}
!

drawBits:imageBits bitsPerPixel:bitsPerPixel depth:imageDepth padding:padd
			  width:imageWidth height:imageHeight
			      x:srcx y:srcy
			   into:aDrawableId
			      x:dstx y:dsty
			  width:w height:h
			   with:aGCId

    "draw a bitImage which has depth id, width iw and height ih into
     the drawable. draw a region of w/h pixels from srcx/srcy to dstx/dsty.
     Individual source pixels have bitsPerPixel bits, allowing to draw
     depth and pixel-units to be different.
     It has to be checked elsewhere, that the server can do it with the given
     depth - otherwise, primitive failure will be signalled.
     Also it is assumed, that the colormap is setup correctly and the
     colors are allocated - otherwise the colors may be wrong."

    "
     sorry; I had to separate it into 2 methods, since XPutImage needs
     an unlimited stack, and thus cannot send primitiveFailed
    "
    (self primDrawBits:imageBits bitsPerPixel:bitsPerPixel depth:imageDepth padding:padd
					width:imageWidth height:imageHeight
					     x:srcx y:srcy
					  into:aDrawableId
					     x:dstx y:dsty
					 width:w height:h
					  with:aGCId)
    ifFalse:[
	"
	 also happens, if a segmentation violation occurs in the
	 XPutImage ...
	"
	self primitiveFailed
    ].
!

fillArcX:x y:y width:width height:height from:startAngle angle:angle
	       in:aDrawableId with:aGCId
    "fill an arc. If any coordinate is not integer, an error is triggered.
     The angles may be floats or integer - they are given in degrees."

%{
    int __x, __y, w, h;
    float angle1, angle2;

    if (__isSmallInteger(startAngle))
	angle1 = (float)(__intVal(startAngle));
    else if (__isFloat(startAngle)) {
	angle1 = __floatVal(startAngle);
    } else if (__isShortFloat(startAngle)) {
	angle1 = __shortFloatVal(startAngle);
    } else goto bad;

    if (__isSmallInteger(angle))
	angle2 = (float)(__intVal(angle));
    else if (__isFloat(angle)) {
	angle2 = __floatVal(angle);
    } else if (__isShortFloat(angle)) {
	angle2 = __shortFloatVal(angle);
    } else goto bad;

    if (angle2 <= 0) {
	RETURN (self);
    }

    if (__isExternalAddress(aGCId)
     /* && __isExternalAddress(aDrawableId) */
     && __bothSmallInteger(x, y)
     && __bothSmallInteger(width, height)) 
     {
	struct gcData *gcData = _GCDATA(aGCId);
	HDC hDC;
	HBRUSH hBrush;
	HPEN prevPen = 0;

	w = __intVal(width);
	h = __intVal(height);
	__x = __intVal(x);
	__y = __intVal(y);

	hDC = _getDC(gcData);

	if (!(hBrush = GcDataGetBrush(hDC, gcData))) {
	    DPRINTF(("fillArc: no brush\n"));
	} else {
	    HPEN hPen = 0;

	    if (0 /* __isWinNT */) {
		if (! (hPen = GcDataGetPen(hDC, gcData))) {
		    DPRINTF(("fillArc: no pen\n"));
		    goto failpen;
		}
	    } else {
		prevPen = SelectObject(hDC, __nullPen);
		w++;
		h++;
	    }

	    {
		double xB, yB, xE, yE, xR, yR;

		xR = w / 2;
		yR = h / 2;
		if (angle2 - angle1 >= 360) {
		    xB = xE = __x + xR + 0.5;
		    yB = yE = __y /*+ yR + 0.5*/;
		} else {
		    double sin(), cos();
		    float rad1, rad2;

		    if (angle1 <= 180)
			angle1 = 180 - angle1;
		    else
			angle1 = 360 + 180 - angle1;
		    angle2 = angle1 - angle2;
		    /* sigh - compute the intersections ... */
		    rad1 = (angle1 * 3.14159265359) / 180.0;
		    rad2 = (angle2 * 3.14159265359) / 180.0;
		    xB = cos(rad1) * xR;
		    yB = sin(rad1) * yR;
		    xE = cos(rad2) * xR;
		    yE = sin(rad2) * yR;
		    xB = __x + xR - xB + 0.5;
		    yB = __y + yR - yB + 0.5;
		    xE = __x + xR - xE + 0.5;
		    yE = __y + yR - yE + 0.5;
		}
		DPRINTF(("fillArc x=%d y=%d w=%d h=%d xB=%d xE=%d yB=%d yE=%d a1=%f a2=%f\n",__x,__y,w,h,(int)xB,(int)xE,(int)yB,(int)yE,angle1,angle2));

		Pie(hDC,
		    __x, __y,
		    __x + w + 1, __y + h + 1,
		    (int)xB, (int)yB,
		    (int)xE, (int)yE);

		if (hPen) {
		    GcDataReleasePen(hDC, gcData);
		}
	    }
failpen:
#ifdef CACHE_LAST_CG
	    if (prevPen) SelectObject(hDC, prevPen);
#endif
	    GcDataReleaseBrush(hDC, gcData);
	}
#ifndef CACHE_LAST_DC
	_releaseDC(gcData);
#endif
	RETURN ( self );
    }
    bad: ;
%}.
    self primitiveFailed
!

fillPolygon:aPolygon in:aDrawableId with:aGCId
    "fill a polygon given by its points.
     If any coordinate is not integer, an error is triggered."

    |numberOfPoints|

    numberOfPoints := aPolygon size.
    self
	primFillPolygon:aPolygon n:numberOfPoints 
	in:aDrawableId with:aGCId
!

fillRectangleX:x y:y width:width height:height in:aDrawableId with:aGCId
    "fill a rectangle. If any coordinate is not integer, an error is triggered."

%{  /* NOCONTEXT */

    int w, h;
    if (__isExternalAddress(aGCId)
     && __isExternalAddress(aDrawableId)
     && __bothSmallInteger(x, y)
     && __bothSmallInteger(width, height)) {
	w = __intVal(width);
	h = __intVal(height);

	if ((w >= 0) && (h >= 0)) {
	    struct gcData *gcData = _GCDATA(aGCId);
	    HDC hDC;
	    HBRUSH hBrush;
	    RECT rct;

	    hDC = _getDC(gcData);

	    hBrush = GcDataGetBrush(hDC, gcData);
	    if (! hBrush) {
		PRINTF(("fillRectangle: no brush\n"));
	    } else {
		rct.left = __intVal(x);
		rct.top = __intVal(y);
		rct.right = rct.left + w + 1;    /* definitiv ! */
		rct.bottom = rct.top + h + 1;

		CPRINTF(("fillRectangle:%d.%d -> %d.%d\n",rct.left,rct.top,rct.right,rct.bottom));

		/*
		 * for solid draws (without function), can use FillRect,
		 * which is faster and does not need a pen.
		 */
		if ((gcData->rop2 == R2_COPYPEN)
		 && (gcData->bitbltrop2 == BITBLT_COPY)) {
		    FillRect(hDC, &rct, hBrush);
		    GcDataReleaseBrush(hDC, gcData);
		} else {                              
		    HPEN prevPen;

		    prevPen = SelectObject(hDC, __nullPen);
		    Rectangle(hDC, rct.left, rct.top, rct.right, rct.bottom);
		    GcDataReleaseBrush(hDC, gcData);
#ifdef CACHE_LAST_DC
		    SelectObject(hDC, prevPen);
#endif
		}
	    }
#ifndef CACHE_LAST_DC
	    _releaseDC(gcData);
#endif
	}
	RETURN ( self );
    }
%}
!

primDrawBits:imageBits bitsPerPixel:bitsPerPixel depth:imageDepth padding:padd
			      width:imageWidth height:imageHeight
				  x:srcx y:srcy
			       into:aDrawableId
				  x:dstx y:dsty
			      width:w height:h
			       with:aGCId

    "since XPutImage may allocate huge amount of stack space
     (some implementations use alloca), this must run with unlimited stack."

%{
    unsigned char fastBits[10000];
    unsigned char *b_bits = 0;
    unsigned char *allocatedBits = 0;
    struct gcData *gcData = 0;
    HDC hDC = (HDC)0;
    unsigned char *__imageBits = 0;

    if (__isByteArray(imageBits)) {
	__imageBits = __ByteArrayInstPtr(imageBits)->ba_element;
    } else if (__isExternalBytes(imageBits)) {
	__imageBits = (unsigned char *)(__externalBytesAddress(imageBits));
    }

    if (ISCONNECTED
     && __isExternalAddress(aGCId)
     && __isExternalAddress(aDrawableId)
     && __bothSmallInteger(srcx, srcy)
     && __bothSmallInteger(dstx, dsty)
     && __bothSmallInteger(w, h)
     && __bothSmallInteger(imageWidth, imageHeight)
     && __bothSmallInteger(imageDepth, bitsPerPixel)
     && __isSmallInteger(padd)
     && __imageBits)
     {
	struct
	{
	  BITMAPINFOHEADER bmiHeader;
	  DWORD r;
	  DWORD g;
	  DWORD b;
	} bitmap;

	gcData = _GCDATA(aGCId);
	if (! gcData )
	    goto fail;
	hDC = _getDC(gcData);
	DDPRINTF(("hDC = %x\n", hDC));

	if (__intVal(padd) != WIN32PADDING) {
	    int row, col;
	    unsigned char *cp;
	    unsigned char *pBits;
	    int b_width, b_height, bytesPerRowST, bytesPerRowWN, padding, nBytes;
	    int bi = __intVal(bitsPerPixel);

	    b_width = __intVal(w);
	    b_height = __intVal(h);
	    bytesPerRowST = (b_width * bi + (__intVal(padd)-1)) / __intVal(padd);
	    bytesPerRowWN = (b_width * bi + (WIN32PADDING-1)) / WIN32PADDING * (WIN32PADDING/8);
	    padding = bytesPerRowWN - bytesPerRowST;
	    nBytes = b_height * bytesPerRowWN;
	    /*printf("padd %d bs %d bw %d p %d\n",__intVal(padd),bytesPerRowST,bytesPerRowWN,padding);*/
	    if (padding) {
		if (nBytes < sizeof(fastBits)) {
		    cp = b_bits = fastBits;
		} else {
		    cp = b_bits = allocatedBits = (unsigned char *) malloc(nBytes);
		}
		if (cp) {
		    pBits = __imageBits;
		    for (row = b_height; row; row--) {
			for (col = bytesPerRowST; col; col--) {
			    *cp++ = *pBits++;
			}
			cp += padding;
		    }
		} else
		    goto fail;
	    }
	}

	if (b_bits == 0) {
	    b_bits = __imageBits;
	}

	bitmap.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	bitmap.bmiHeader.biPlanes = 1;
	if (__intVal(imageDepth) == 24) {
	    /*bitmap.bmiHeader.biCompression = BI_BITFIELDS;
	    bitmap.r = 0xff0000;
	    bitmap.g = 0x00ff00;
	    bitmap.b = 0x0000ff;*/
	    bitmap.bmiHeader.biCompression = BI_RGB;
	} else if (__intVal(imageDepth) == 16) {
	    /*bitmap.bmiHeader.biCompression = BI_RGB;
	    bitmap.bmiHeader.biCompression = BI_BITFIELDS;
	    bitmap.b = 0x001f;
	    bitmap.g = 0x07e0;
	    bitmap.r = 0xf800;*/
	    bitmap.b = 0;
	    bitmap.g = 0;
	    bitmap.r = 0;
	    bitmap.bmiHeader.biCompression = BI_RGB;
	}
	bitmap.bmiHeader.biSizeImage = 0;
	bitmap.bmiHeader.biXPelsPerMeter = 0;
	bitmap.bmiHeader.biYPelsPerMeter = 0;
	bitmap.bmiHeader.biClrUsed = 0;
	bitmap.bmiHeader.biClrImportant = 0;
	bitmap.bmiHeader.biWidth = __intVal(imageWidth);
	bitmap.bmiHeader.biHeight = -(__intVal(imageHeight));
	bitmap.bmiHeader.biBitCount = __intVal(bitsPerPixel);
	/*printf("drawBits depth:%d bitsPerPixel:%d IW%d W:%d H:%d\n",__intVal(imageDepth),bitmap.bmiHeader.biBitCount,bitmap.bmiHeader.biWidth,__intVal(w),bitmap.bmiHeader.biHeight);*/
	SetDIBitsToDevice(hDC,__intVal(dstx),__intVal(dsty),
			      __intVal(w), __intVal(h),
			      __intVal(srcx), __intVal(srcy),
			      0,__intVal(h),
			      (void *)b_bits,
			      (BITMAPINFO*)&bitmap,DIB_RGB_COLORS);
	if (allocatedBits) {
	    free(allocatedBits);
	}
#ifndef CACHE_LAST_DC
	_releaseDC(gcData);
#endif
	RETURN ( true );
    }

fail: ;
    PRINTF(("create temp bitmap FAILED!!!\n"));
    if (allocatedBits) {
	PRINTF(("freeing up temp bitmap bits ...\n"));
	free(allocatedBits);
    }
#ifndef CACHE_LAST_DC
    if (hDC) {
	_releaseDC(gcData);
    }
#endif
%}
.
    ^ false
!

primFillPolygon:aPolygon n:numberOfPoints in:aDrawableId with:aGCId

%{
    OBJ point, px, py;
    int i, num;

    if (__isExternalAddress(aGCId)
     && __isExternalAddress(aDrawableId)
     && __isSmallInteger(numberOfPoints)) {
	struct gcData *gcData = _GCDATA(aGCId);
	HDC hDC;
	POINT p;
	HBRUSH hBrush;

	num = __intVal(numberOfPoints);
	if (num < 3) {
	    RETURN ( self );
	}
	for (i=0; i<num; i++) {
	    point = __AT_(aPolygon, __MKSMALLINT(i+1));
	    if (! __isPoint(point)) goto fail;
	    px = _point_X(point);
	    py = _point_Y(point);
	    if (! __bothSmallInteger(px, py))
		goto fail;
	}

	hDC = _getDC(gcData);

	if (!(hBrush = GcDataGetBrush(hDC,gcData))) {
	    DPRINTF(("fillPolygon: no brush\n"));
	} else {
	    HPEN prevPen;

	    prevPen = SelectObject(hDC, __nullPen);

	    BeginPath(hDC);

	    for (i=0; i<num; i++) {
		point = __AT_(aPolygon, __MKSMALLINT(i+1));
		px = _point_X(point);
		py = _point_Y(point);
		if (i == 0) {
		    MoveToEx(hDC, __intVal(px), __intVal(py), NULL);
		} else {
		    if (i == (num-1)) {
			p.x = __intVal(px);
			p.y = __intVal(py);
			PolylineTo(hDC, &p, 1);
		    } else {
			LineTo(hDC, __intVal(px), __intVal(py));
		    }
		}
	    }

	    EndPath(hDC);
	    FillPath(hDC);
	    GcDataReleaseBrush(hDC, gcData);
#ifdef CACHE_LAST_DC
	    SelectObject(hDC, prevPen);
#endif
	}
#ifndef CACHE_LAST_DC
	_releaseDC(gcData);
#endif
	RETURN ( self );

fail: ;
    }
%}
! !

!WinWorkstation methodsFor:'event forwarding'!

activate:aBoolean view:aView
    "some view was activated/deactivated.
     For compatibility we send a focus-event to that topView"

    aBoolean ifFalse:[
	activeView := nil.
	self focusOutView:aView.
    ] ifTrue:[
	activeView := aView.
	self focusInView:aView.
    ].

    "Created: / 28.4.1999 / 14:51:51 / cg"
    "Modified: / 28.4.1999 / 15:07:09 / cg"
!

configureX:x y:y width:w height:h view:aView
    "forward a configure for some view"

    "/ windows sends funny size changes (origin 3000@3000)
    "/ when the view is iconified.
    "/ ignore those ...

    aView realized ifTrue:[
	super configureX:x y:y width:w height:h view:aView
    ]
!

displayChange
    "the display metrics/settings have changed.
     This is a speciality of windows.
     TODO: Tell the viewStyle to update itself,
	   and tell all views to reinit their style.
     For now,this is ignored, except for updating my metrics."

    self initializeVariableScreenProperties.
!

fontChange
    "the system fonts (users preferences) have changed.
     This is a speciality of windows.
     TODO: Tell the viewStyle to update itself,
	   and tell all views to reinit their style.
     For now,this is ignored."

    IgnoreFontChanges ~~ true ifTrue:[
"/        'WinWorkstation [info]: fontChange ignored for now.' infoPrintCR
    ]

    "Modified: / 19.5.1999 / 23:37:17 / cg"
!

queryEndSession
    "system is about to be shut down.
     This is a speciality of windows.
     TODO: Tell all views to close themself.
     For now, this is ignored."

    'WinWorkstation [info]: queryEndSession ignored for now.' infoPrintCR.
    ^ true
!

settingsChange
    "some system settings (users preferences) have changed.
     This is a speciality of windows.
     For now, only a few attributes are reinitialized."

    self initializeVariableSettingsProperties.
!

systemColorChange:aWindow
    "the system colors (users preferences) have changed.
     This is a speciality of windows.
     Tell the viewStyle to update itself,
     and tell all view to reinit its style."

    |anyChange|

"/    'WinWorkstation [info]: systemColorChange for ' infoPrint.
"/    aWindow printCR.

    IgnoreSysColorChanges ~~ true ifTrue:[
	"/ first check, if there was really a change
	"/ (when exceed is running, we get plenty of those messages ...)

	SystemColorValues isNil ifTrue:[
	    SystemColorValues := IdentityDictionary new.
	].
	anyChange := false.
	self supportedSystemColorKeys do:[:key |
	    |oldValue newValue|

	    oldValue := SystemColorValues at:key ifAbsent:nil.
	    newValue := self getSystemColor:key.
	    oldValue ~= newValue ifTrue:[
		SystemColorValues at:key put:newValue.
		anyChange := true.
	    ]
	].
	anyChange ifTrue:[
	    "/ TODO: this should go through the sensor ...
	    "/ ...and handled as an event by the views thread.
	    View updateAllStyleCaches.

	    aWindow withAllSubViewsDo:[:aView |
		aView reinitStyle
	    ]
	]
    ]

    "Modified: / 10.9.1998 / 21:53:34 / cg"
! !

!WinWorkstation methodsFor:'event handling'!

dispatchEventFor:aViewIdOrNil withMask:eventMask
    "central event handling method:
     get next event and send appropriate message to the sensor or view.
     If the argument aViewIdOrNil is nil, events for any view are processed,
     otherwise only events for the view with given id are processed.
     If the argument aMask is nonNil, only events for this eventMask are
     handled.
     WARNING: this may block to wait for an event - you better check for a
	      pending event before calling this."

    (self getEventFor:aViewIdOrNil withMask:eventMask) ifTrue:[
	AbortSignal handle:[:ex |
	    ex return
	] do:[
	    self dispatchLastEvent.
	]
    ].

    "Modified: 19.8.1997 / 17:10:42 / cg"
!

dispatchExposeEventFor:aViewIdOrNil
    "get next expose event and send appropriate message to the sensor or view.
     If the argument aViewIdOrNil is nil, events for any view are processed,
     otherwise only events for the view with given id are processed."

    self dispatchEventFor:aViewIdOrNil withMask:(self eventMaskFor:#expose)
!

dispatchLastEvent
    |theView symS arg butt sibling windowID siblingID propertyID selectionID targetID requestorID
     eventType|

%{  /* xxSTACK: 8000 */
    struct queuedEvent *ev = 0;
    struct queuedEvent _ev_buf;
    struct inlineCache *ipS;

    static struct inlineCache vid = _ILC1;
    static struct inlineCache conf = _ILC5;
    static struct inlineCache skp = _ILC4;
    static struct inlineCache skr = _ILC4;
    static struct inlineCache exp = _ILC5;
    static struct inlineCache gexpS = _ILC6;
    static struct inlineCache nexpS = _ILC1;
    static struct inlineCache clr = _ILC5;
    static struct inlineCache bp = _ILC4;
    static struct inlineCache br = _ILC4;
    static struct inlineCache bmp = _ILC4;
    static struct inlineCache bsp = _ILC4;
    static struct inlineCache mot = _ILC4;
    static struct inlineCache mwh = _ILC6;
    static struct inlineCache unmap = _ILC1;
    static struct inlineCache map = _ILC1;
    static struct inlineCache termS = _ILC1;
    static struct inlineCache destr = _ILC1;
    static struct inlineCache setCurs = _ILC1;
    static struct inlineCache focOut = _ILC1;
    static struct inlineCache focIn = _ILC1;
    static struct inlineCache act = _ILC2;
    static struct inlineCache pe = _ILC4;
    static struct inlineCache pl = _ILC2;
    static struct inlineCache vis = _ILC1;
    static struct inlineCache sysClrChg = _ILC1;
    static struct inlineCache fontChg = _ILC0;
    static struct inlineCache settingChg = _ILC0;
    static struct inlineCache qEndSess = _ILC0;
    static struct inlineCache powerDown = _ILC0;

    int x, y, w, h;
    int keyCode, modifiers, isDoubleClick = 0;
    int isDown = 1;
    int state, dir;
    OBJ upDown;
    OBJ eB;
    char nameBuffer[100];
    HWND hWnd = 0;
    DDPRINTF(("dispatchLast\n"));

    eB = __INST(eventBuffer);

    if (__isByteArray(eB)) {
	ev = (struct queuedEvent *)(__ByteArrayInstPtr(eB)->ba_element);
    } else {
	fprintf(stderr, "WinWorkstation [error]: no eventBuffer\n");
	RETURN (false);
    }
    if (ev) {
	_ev_buf = *ev;
	ev = &_ev_buf;
    }
    hWnd = ev->ev_hWnd;
    if (!(hWnd /*&& IsWindow(hWnd)*/)) {
	DPRINTF(("wrong hWnd in event in dispatchLastEvent\n"));
	RETURN (false);
    }
    {
	OBJ t;

	/*
	 * very often, its another event for the same view ...
	 * avoid creation & lookup then.
	 */
#if 1
	if ((t = __INST(lastId)) != nil) {
	    if (__isExternalAddress(t)) {
		if (_HWNDVal(t) == hWnd) {
		    theView = __INST(lastView);
		    if (__isNonNilObject(theView)) {
			if (__qClass(theView) == nil) {
			    theView = nil;
			}
		    }
		}
	    }
	}
#endif
	if (theView == nil) {
	    windowID = __MKOBJ(ev->ev_hWnd);
	    theView = (*vid.ilc_func)(self, @symbol(viewFromId:), nil, &vid, windowID);
	}
    }

    if (theView == nil) {
	DPRINTF(("nil view [hWnd=%x msg=0x%x] in dispatchEvent\n", 
		 ev->ev_hWnd, ev->ev_message));

	RETURN (false);
    }

    switch (ev->ev_message) {
	    case WM_WINDOWPOSCHANGED:
		{
		    RECT rct;

		    x = ev->ev_x;
		    y = ev->ev_y;
		    w = ev->ev_w;
		    h = ev->ev_h;

		    DPRINTF((">>> WM_WINDOWPOSCHANGED -> configure %d/%d , %d/%d\n", x,y,w,h));

		    (*conf.ilc_func)(self,
				     @symbol(configureX:y:width:height:view:),
				     nil, &conf,
				     __MKSMALLINT(x),
				     __MKSMALLINT(y),
				     __MKSMALLINT(w),
				     __MKSMALLINT(h),
				     theView);
		}
		break;

	    case WM_DROPFILES:
		{
		  int i;
		  char buf[MAXPATH];
		  HDROP hDrop = (HDROP)ev->ev_wParam;
		  int count = DragQueryFile(hDrop,0xffffffff,0,0);

		  PRINTF((">>> WM_DROPFILES %d Files\n",count));
		  for (i = 0;i < count;i++) {
		      DragQueryFile(hDrop,i,buf,sizeof(buf));
		      PRINTF(("Filename:%s\n",buf));
		  }
		  DragFinish(hDrop);
		}
		break;

	    case WM_SHOWWINDOW:
		if (ev->ev_wParam == TRUE) {
		    DPRINTF((">>> WM_SHOWWINDOW -> mappedView:\n"));
		    arg = @symbol(unobscured);
		    (*vis.ilc_func)(theView, 
				    @symbol(visibilityChange:), 
				    nil, &vis, arg);
		    (*map.ilc_func)(self,
				    @symbol(mappedView:),
				    nil, &map, theView);
		} else {
		    DPRINTF((">>> WM_SHOWWINDOW -> unMappedView:\n"));
#if 0
		    arg = @symbol(fullyObscured);
		    (*vis.ilc_func)(theView, 
				    @symbol(visibilityChange:), 
				    nil, &vis, arg);
#endif
		    (*unmap.ilc_func)(self,
				      @symbol(unmappedView:),
				      nil, &unmap, theView);
		}
		break;

	    case __WM_ICONIFIED:
		if (ev->ev_wParam) {
		    DPRINTF((">>> __WM_ICONIFIED -> unMappedView:\n"));
#if 0
		    arg = @symbol(fullyObscured);
		    (*vis.ilc_func)(theView, 
				    @symbol(visibilityChange:), 
				    nil, &vis, arg);
#endif
		    (*unmap.ilc_func)(self,
				      @symbol(unmappedView:),
				      nil, &unmap, theView);
		} else {
		    DPRINTF((">>> __WM_DEICONIFIED -> mappedView:\n"));
		    arg = @symbol(unobscured);
		    (*vis.ilc_func)(theView, 
				    @symbol(visibilityChange:), 
				    nil, &vis, arg);
		    (*map.ilc_func)(self,
				    @symbol(mappedView:),
				    nil, &map, theView);
		}
		break;

	    case WM_CLOSE:
		DPRINTF((">>> WM_CLOSE -> terminateView:\n"));
		(*termS.ilc_func)(self,
				  @symbol(terminateView:), 
				  nil, &termS, theView);
		break;

	    case WM_DESTROY:
		DPRINTF((">>> WM_DESTROY -> destroyedView\n"));
		(*destr.ilc_func)(self,
				  @symbol(destroyedView:),
				  nil, &destr, theView);
		break;

	    case WM_ACTIVATE:

		switch (LOWORD(ev->ev_wParam)) {
		    case WA_INACTIVE:
			DPRINTF((">>> WM_ACTIVATE inactive h=%x\n", ev->ev_hWnd));
			(*act.ilc_func)(self,
					@symbol(activate:view:),
					nil, &act,
					false, theView);
			break;

		    case WA_ACTIVE:
		    case WA_CLICKACTIVE:
			DPRINTF((">>> WM_ACTIVATE active h=%x\n", ev->ev_hWnd));
			(*act.ilc_func)(self,
					@symbol(activate:view:),
					nil, &act,
					true, theView);
			break;

		    default:
			break;
		}
		break;

	    case WM_MOUSEACTIVATE:
		DPRINTF((">>> WM_MOUSEACTIVATE h=%x -> focusInView:\n", ev->ev_hWnd));

		(*focIn.ilc_func)(self,
				  @symbol(focusInView:),
				  nil, &focIn, theView);
		break;

	    case WM_SETFOCUS:
		DPRINTF((">>> WM_SETFOCUS h=%x -> focusInView:\n", ev->ev_hWnd));
		(*focIn.ilc_func)(self,
				  @symbol(focusInView:),
				  nil, &focIn, theView);
		break;

	    case WM_KILLFOCUS:
		DPRINTF((">>> WM_KILLFOCUS h=%x -> focusOutOfView\n", ev->ev_hWnd));
		(*focOut.ilc_func)(self,
				   @symbol(focusOutView:),
				   nil, &focOut, theView);
		break;

	    case __WM_GEXPOSE:
		x = ev->ev_x;
		y = ev->ev_y;
		w = ev->ev_w;
		h = ev->ev_h;
		DPRINTF((">>> __WM_GEXPOSE -> gExpose %d/%d -> %d/%d\n", x, y, w, h));
		(*gexpS.ilc_func)(self,
				  @symbol(graphicsExposeX:y:width:height:final:view:), 
				  nil, &gexpS,
				  __MKSMALLINT(x),
				  __MKSMALLINT(y),
				  __MKSMALLINT(w),
				  __MKSMALLINT(h),
				  ev->ev_wParam ? true : false, 
				  theView);
		break;

	    case __WM_NOGEXPOSE:
		DPRINTF((">>> __WM_NOGEXPOSE -> noExpose\n"));
		 (*nexpS.ilc_func)(self,
				   @symbol(noExposeView:), nil, &nexpS,
				   theView);
		break;

	    case WM_PAINT:
		x = ev->ev_x;
		y = ev->ev_y;
		w = ev->ev_w;
		h = ev->ev_h;
#ifdef LATE_WM_PAINT
		__clearWindow(hWnd, x, y, w, h);
#endif
		DPRINTF((">>> WM_PAINT -> expose %d/%d -> %d/%d\n", x, y, w, h));
		(*exp.ilc_func)(self,
			 @symbol(exposeX:y:width:height:view:),
			 nil, &exp,
			 __MKSMALLINT(x),
			 __MKSMALLINT(y),
			 __MKSMALLINT(w),
			 __MKSMALLINT(h),
			 theView);
		break;

	    case WM_LBUTTONDBLCLK:
		isDoubleClick = 1;
		butt = __MKSMALLINT(Button1);
		goto commonButtonDown;

	    case WM_LBUTTONUP:
		isDown = 0;
		butt = __MKSMALLINT(Button1);
		goto commonButtonDown;

	    case WM_LBUTTONDOWN:
		butt = __MKSMALLINT(Button1);
		goto commonButtonDown;

	    case WM_MBUTTONDBLCLK:
		isDoubleClick = 1;
		butt = __MKSMALLINT(Button2);
		goto commonButtonDown;

	    case WM_MBUTTONUP:
		isDown = 0;
		butt = __MKSMALLINT(Button2);
		goto commonButtonDown;

	    case WM_MBUTTONDOWN:
		butt = __MKSMALLINT(Button2);
		goto commonButtonDown;

	    case WM_RBUTTONDBLCLK:
		isDoubleClick = 1;
		butt = __MKSMALLINT(Button3);
		goto commonButtonDown;

	    case WM_RBUTTONUP:
		isDown = 0;
		butt = __MKSMALLINT(Button3);
		goto commonButtonDown;

	    case WM_RBUTTONDOWN:
		butt = __MKSMALLINT(Button3);
		goto commonButtonDown;

	    commonButtonDown:
		x = ev->ev_x;
		y = ev->ev_y;
		modifiers = ev->ev_modifiers;

		if (__INST(buttonTranslation) != nil) {
		    butt = __AT_(__INST(buttonTranslation), butt);
		}
		arg = butt;

		__INST(altDown) = (modifiers & AltMask) ? true : false;
		__INST(metaDown) = (modifiers & MetaMask) ? true : false;
		__INST(shiftDown) = (modifiers & ShiftMask) ? true : false;
		__INST(ctrlDown) = (modifiers & ControlMask) ? true : false;

		if (deltaDoubleClickX == -999) {
		    deltaDoubleClickX = GetSystemMetrics(SM_CXDOUBLECLK);
		    deltaDoubleClickY = GetSystemMetrics(SM_CYDOUBLECLK);
		}

		if (isDoubleClick) {
		    multiClickState = 1;
		    nextMultiClickTime = ev->ev_time + __intVal(__INST(multiClickTimeDelta));
		    ipS = &bmp;
		    symS = @symbol(buttonMultiPress:x:y:view:);
		} else {
		    if (isDown) {
			if (multiClickState 
			 && (ev->ev_time < nextMultiClickTime)
			 && (x >= (lastClickX - (deltaDoubleClickX / 2)))
			 && (x <= (lastClickX + (deltaDoubleClickX / 2)))
			 && (y >= (lastClickY - (deltaDoubleClickY / 2)))
			 && (y <= (lastClickY + (deltaDoubleClickY / 2)))
			) {
			    nextMultiClickTime = ev->ev_time + __intVal(__INST(multiClickTimeDelta));
			    ipS = &bmp;
			    symS = @symbol(buttonMultiPress:x:y:view:);
			} else {
			    multiClickState = 0;
			    ipS = &bp;
			    symS = @symbol(buttonPress:x:y:view:);
			}
		    } else {
			ipS = &br;
			symS = @symbol(buttonRelease:x:y:view:);
		    }
		}

		if (isDown || isDoubleClick) {
		    lastClickX = x;
		    lastClickY = y;
		}

		if (__isSymbol(arg)) {
		    DPRINTF(("buttonPress/buttonRelease: %s %d/%d\n",
				__stringVal(arg), x, y));
		} else {
		    DPRINTF(("buttonPress/buttonRelease: %d %d/%d\n",
				__intVal(arg), x, y));
		}

		(*(*ipS).ilc_func)(self, symS, nil, ipS,
				   arg,
				   __MKSMALLINT(x),
				   __MKSMALLINT(y),
				   theView);
		break;

	    case __WM_MOUSEENTER:
		x = ev->ev_x;
		y = ev->ev_y;
		state = ev->ev_modifiers;
#ifdef DEBUG_WM_MOUSEENTER
		PRINTF((">>> WM_MOUSEENTER: %x %d/%d state:%x\n", ev->ev_hWnd, x, y, state));
#else
		DPRINTF((">>> WM_MOUSEENTER: %d/%d %x\n", x, y, state));
#endif
		(*pe.ilc_func)(self, @symbol(pointerEnter:x:y:view:),
				    nil, &pe,
				    __MKSMALLINT(state),
				    __MKSMALLINT(x),
				    __MKSMALLINT(y),
				    theView);
		break;

	    case __WM_MOUSELEAVE:
		state = ev->ev_modifiers;
#ifdef DEBUG_WM_MOUSELEAVE
		PRINTF((">>> WM_MOUSELEAVE: %x state:%x\n", ev->ev_hWnd, state));
#else
		DPRINTF((">>> WM_MOUSELEAVE: %x\n", state));
#endif
		(*pl.ilc_func)(self, @symbol(pointerLeave:view:),
				    nil, &pl,
				    __MKSMALLINT(state),
				    theView);
		break;

	    case WM_MOUSEMOVE:
		x = ev->ev_x;
		y = ev->ev_y;
		state = ev->ev_modifiers;
		DPRINTF((">>> WM_MOUSEMOVE: %d/%d %x\n", x, y, state));

		(*mot.ilc_func)(self, @symbol(buttonMotion:x:y:view:),
				    nil, &mot,
				    __MKSMALLINT(state),
				    __MKSMALLINT(x),
				    __MKSMALLINT(y),
				    theView);
		break;

#ifdef WM_MOUSEWHEEL
	    case WM_MOUSEWHEEL:
		state = ev->ev_modifiers;
		dir = ev->ev_wParam;
		DPRINTF((">>> WM_MOUSEWHEEL: %d %x\n", dir, state));

		(*mwh.ilc_func)(self, @symbol(mouseWheelMotion:x:y:amount:deltaTime:view:),
				    nil, &mwh,
				    __MKSMALLINT(state),
				    __MKSMALLINT(x),
				    __MKSMALLINT(y),
				    __MKSMALLINT(dir),
				    __MKUINT(ev->ev_time),
				    theView);
		break;
#endif

	    case WM_CHAR:
		DPRINTF((">>> WM_CHAR: %d/%d\n", x, y));
		symS = @symbol(keyPress:x:y:view:);
		ipS = &skp;
		upDown = true;
		goto keyPressAndRelease;

	    case WM_SYSKEYUP:
	    case WM_KEYUP:
		DPRINTF((">>> WM_KEYUP / SYSKEYUP: %d/%d\n", x, y));
		symS = @symbol(keyRelease:x:y:view:);
		ipS = &skr;
		upDown = false;
		goto keyPressAndRelease;

	    case WM_SYSKEYDOWN:
	    case WM_KEYDOWN:
		DPRINTF((">>> WM_KEYDOWN / SYSKEYDOWN: %d/%d\n", x, y));
		symS = @symbol(keyPress:x:y:view:);
		ipS = &skp;
		upDown = true;
		/* FALL INTO */

	    keyPressAndRelease: ;

		x = ev->ev_x;
		y = ev->ev_y;

#ifdef NOTDEF
		{
		    BYTE vKeyState[256];
		    char buff[5];

		    GetKeyboardState(vKeyState);
		    ToAscii(ev->ev_keyCode, ev->ev_scanCode, vKeyState, &buff; 0);
		}
#endif

		keyCode = ev->ev_keyCode;
		modifiers = ev->ev_modifiers;

		if (modifiers & TRANSLATED_KEY) {
		    if (modifiers & ControlMask) {
			if (keyCode < 0x20) {
			    keyCode = keyCode + 'a' - 1;
			    if (modifiers & ShiftMask) {
				keyCode = keyCode - 'a' + 'A';
			    }
			}
		    } else if (modifiers & (MetaMask | AltMask)) {
			if (! (modifiers & ShiftMask)) {
			    if ((keyCode >= 'A') && (keyCode <= 'Z')) {
				keyCode = keyCode - 'A' + 'a';
			    }
			}
		    }
		    arg = __MKCHARACTER(keyCode & 0xFF);
		} else {
		    switch (keyCode) {
			case VK_F1:
			    arg = @symbol(F1);
			    break;
			case VK_F2:
			    arg = @symbol(F2);
			    break;
			case VK_F3:
			    arg = @symbol(F3);
			    break;
			case VK_F4:
			    arg = @symbol(F4);
			    break;
			case VK_F5:
			    arg = @symbol(F5);
			    break;
			case VK_F6:
			    arg = @symbol(F6);
			    break;
			case VK_F7:
			    arg = @symbol(F7);
			    break;
			case VK_F8:
			    arg = @symbol(F8);
			    break;
			case VK_F9:
			    arg = @symbol(F9);
			    break;
			case VK_F10:
			    arg = @symbol(F10);
			    break;
			case VK_F11:
			    arg = @symbol(F11);
			    break;
			case VK_F12:
			    arg = @symbol(F12);
			    break;
			case VK_PRIOR:
			    arg = @symbol(Prior);
			    break;
			case VK_NEXT:
			    arg = @symbol(Next);
			    break;
			case VK_END:
			    arg = @symbol(End);
			    break;
			case VK_HOME:
			    arg = @symbol(Home);
			    break;
			case VK_LEFT:
			    arg = @symbol(CursorLeft);
			    break;
			case VK_RIGHT:
			    arg = @symbol(CursorRight);
			    break;
			case VK_UP:
			    arg = @symbol(CursorUp);
			    break;
			case VK_DOWN:
			    arg = @symbol(CursorDown);
			    break;
			case VK_MENU:  /*alt key with w95 ???*/
			    arg = @symbol(Menu);
			    break;
			case VK_LMENU:
			    arg = @symbol(Menu_L);
			    break;
			case VK_RMENU:
			    arg = @symbol(Menu_R);
			    break;
			case VK_PAUSE:
			    arg = @symbol(Pause);
			    break;
			case VK_HELP:
			    arg = @symbol(Help);
			    break;
			case VK_EXECUTE:
			    arg = @symbol(Execute);
			    break;
			case VK_CANCEL:
			    arg = @symbol(Cancel);
			    break;
			case VK_SELECT:
			    arg = @symbol(Select);
			    break;
			case VK_PRINT:
			    arg = @symbol(Print);
			    break;
			case VK_SNAPSHOT:
			    arg = @symbol(Snapshot);
			    break;
			case VK_INSERT:
			    arg = @symbol(Insert);
			    break;
			case VK_DELETE:
			    arg = @symbol(Delete);
			    break;
			case VK_BACK:
			    arg = @symbol(BackSpace);
			    break;
			case VK_LWIN:
			    arg = @symbol(Win_L);
			    break;
			case VK_RWIN:
			    arg = @symbol(Win_R);
			    break;
			case VK_APPS:
			    arg = @symbol(Appl);
			    break;
			case VK_NUMPAD0:
			    arg = @symbol(KeyPad0);
			    break;
			case VK_NUMPAD1:
			    arg = @symbol(KeyPad1);
			    break;
			case VK_NUMPAD2:
			    arg = @symbol(KeyPad2);
			    break;
			case VK_NUMPAD3:
			    arg = @symbol(KeyPad3);
			    break;
			case VK_NUMPAD4:
			    arg = @symbol(KeyPad4);
			    break;
			case VK_NUMPAD5:
			    arg = @symbol(KeyPad5);
			    break;
			case VK_NUMPAD6:
			    arg = @symbol(KeyPad6);
			    break;
			case VK_NUMPAD7:
			    arg = @symbol(KeyPad7);
			    break;
			case VK_NUMPAD8:
			    arg = @symbol(KeyPad8);
			    break;
			case VK_NUMPAD9:
			    arg = @symbol(KeyPad9);
			    break;
			case VK_LSHIFT:
			    arg = @symbol(Shift_L);
			    break;
			case VK_RSHIFT:
			    arg = @symbol(Shift_R);
			    break;
			case VK_LCONTROL:
			    arg = @symbol(Ctrl_L);
			    break;
			case VK_RCONTROL:
			    arg = @symbol(Ctrl_R);
			    break;
			case VK_CONTROL:
			    arg = @symbol(Ctrl);
			    break;
			case VK_SHIFT:
			    arg = @symbol(Shift);
			    break;
			case VK_TAB:
			    arg = @symbol(Tab);
			    break;
			case VK_ESCAPE:
			    arg = @symbol(Escape);
			    break;
			case VK_NUMLOCK:
			    arg = @symbol(NumLock);
			    break;
			case VK_SCROLL:
			    arg = @symbol(ScrollLock);
			    break;
			case VK_RETURN:
			    arg = @symbol(Return);
			    break;
			default:
			    nameBuffer[0] = 0;
			    /*GetKeyNameText(ev->ev_scanCode, nameBuffer, sizeof(nameBuffer));*/
			    DPRINTF(("char is <%d>\n", keyCode));
			    arg = __MKCHARACTER(keyCode & 0xFF);
			    break;
		    }
		 }

		DPRINTF(("%s: code=%x mod=%x\n", __stringVal(symS),
				keyCode, modifiers));

		__INST(altDown) = (modifiers & AltMask) ? true : false;
		__INST(metaDown) = (modifiers & MetaMask) ? true : false;
		__INST(shiftDown) = (modifiers & ShiftMask) ? true : false;
		__INST(ctrlDown) = (modifiers & ControlMask) ? true : false;

		(*(*ipS).ilc_func)(self, symS, nil, ipS,
				   arg,
				   __MKSMALLINT(x),
				   __MKSMALLINT(y),
				   theView);
		break;

	   case WM_SYSCOLORCHANGE:
		DPRINTF((">>> WM_SYSCOLORCHANGE\n"));
		(*sysClrChg.ilc_func)(self, @symbol(systemColorChange:), nil, &sysClrChg, theView);
		break;

	   case WM_FONTCHANGE:
		DPRINTF((">>> WM_FONTCHANGE\n"));
		(*fontChg.ilc_func)(self, @symbol(fontChange), nil, &fontChg);
		break;

	   case WM_WININICHANGE:
		DPRINTF((">>> WM_WININICHANGE\n"));
		(*settingChg.ilc_func)(self, @symbol(settingsChange), nil, &settingChg);
		break;

	   case WM_DISPLAYCHANGE:
		DPRINTF((">>> WM_DISPLAYCHANGE\n"));
		(*fontChg.ilc_func)(self, @symbol(displayChange), nil, &fontChg);
		break;

	   case WM_QUERYENDSESSION:
		DPRINTF((">>> WM_QUERYENDSESSION\n"));
		(*qEndSess.ilc_func)(self, @symbol(queryEndSession), nil, &qEndSess);
		break;

#ifdef LATER
	   case WM_POWER:
		DPRINTF((">>> WM_POWER\n"));
		(*power.ilc_func)(self, @symbol(powerDown), nil, &power);
	       break;
#endif

	    default:
		UNHANDLED_EVENT_PRINTF(("WinWorkstat [info]: >>> unhandled event: %x\n", ev->ev_message));
		break;
    }
%}.
    ^ true
!

dispatchPendingEvents
    "central event handling method for modal operation.
     (i.e. this is now only used in the modal debugger)
     Dispatch any pending events; return when no more are pending.
     This code is somewhat special, since X has a concept of graphic
     expose events (which are sent after a bitblt). After such a bitblt,
     we only handle exposes until the graphicsExpose arrives.
     Other systems may not need such a kludge"

    [self eventPendingWithSync:false] whileTrue:[
	(self getEventFor:nil withMask:nil) ifTrue:[
	    AbortSignal handle:[:ex |
		ex return
	    ] do:[
		self dispatchLastEvent
	    ]
	].
    ]

    "Modified: 19.8.1997 / 17:11:18 / cg"
!

disposeEventsWithMask:aMask for:aWindowIdOrNil
    "dispose (throw away) specific events. If aWindowId is nil,
     events matching the mask are thrown away regardless of which
     view they are for. Otherwise, only matching events for that
     view are flushed."

%{  /* NOCONTEXT */
   DPRINTF(("WinWorkstation: disposeEventsWithMask:for: not yet implemented.\n"));
   RETURN ( self );
%}
!

eventMaskFor:anEventSymbol
    "return the eventMask bit-constant corresponding to an event symbol"

%{  /* NOCONTEXT */

    int m = 0;

    if (anEventSymbol == @symbol(keyPress)) m = KeyPressMask;
    else if (anEventSymbol == @symbol(keyRelease)) m = KeyReleaseMask;
    else if (anEventSymbol == @symbol(buttonPress)) m = ButtonPressMask;
    else if (anEventSymbol == @symbol(buttonRelease)) m = ButtonReleaseMask;
    else if (anEventSymbol == @symbol(buttonMotion)) m = ButtonMotionMask;
    else if (anEventSymbol == @symbol(pointerMotion)) m = PointerMotionMask;
    else if (anEventSymbol == @symbol(expose)) m = ExposureMask;
    else if (anEventSymbol == @symbol(focusChange)) m = FocusChangeMask;
    else if (anEventSymbol == @symbol(enter)) m = EnterWindowMask;
    else if (anEventSymbol == @symbol(leave)) m = LeaveWindowMask;
    else if (anEventSymbol == @symbol(keymapState)) m = KeymapStateMask;
    else if (anEventSymbol == @symbol(visibilityChange)) m = VisibilityChangeMask;
    else if (anEventSymbol == @symbol(structureNotify)) m = StructureNotifyMask;
    else if (anEventSymbol == @symbol(resizeRedirect)) m = ResizeRedirectMask;
    else if (anEventSymbol == @symbol(propertyChange)) m = PropertyChangeMask;
    else if (anEventSymbol == @symbol(colormapChange)) m = ColormapChangeMask;
    RETURN (__MKSMALLINT(m));
%}
!

eventPending
    "return true, if any event is pending.
     This looks for both the internal queue and the display connection."

    ^ self eventPendingWithSync:false
!

eventPending:anEventSymbol for:aWindowIdOrNil
    "return true, if a specific event is pending"

    ^ self eventsPending:(self eventMaskFor:anEventSymbol) for:aWindowIdOrNil withSync:false
!

eventPendingWithSync:doSync
    "return true, if any event is pending.
     The doSync argument is ignored here - in windows, all drawing is synchronous."

    doSync notNil ifTrue:[self flush].

%{
    if (hasEventQueued()) {
	RETURN (true);
    }
%}.
    ^ false
!

eventQueued
    "return true, if any event is queued"

    ^ self eventQueuedAlready

    "Created: 12.12.1995 / 21:43:00 / stefan"
!

eventQueuedAlready
    "return true, if any event is queued internally.
     (i.e. in X's internal event queue, which is both filled by explicit
      nextEvent calls AND whenever drawing is done and events are pending on
      the display connection)."

%{  /* NOCONTEXT */

    DDPRINTF(("peek q - "));
    if (hasEventQueued()) {
	DDPRINTF(("true\n"));
	RETURN (true);
    }
    DDPRINTF(("false\n"));
%}.
    ^ false
!

eventsPending:anEventMask for:aWindowIdOrNil withSync:doSync
    "return true, if any of the masked events is pending.
     The doSync argument is ignored here - in windows, all drawing is synchronous."

%{  /* NOCONTEXT */

    DDPRINTF(("eventsPending mask %x - not implemented\n", __intVal(anEventMask)));
%}.
    ^ false
!

exposeEventPendingFor:aWindowIdOrNil withSync:doSync
    "return true, if any expose event is pending for a specific view,
     or any view (if the arg is nil).
     This is an X specific interface - not used on windows (scrolling is synchronous)"

    ^ false
!

getEventFor:aViewIdOrNil withMask:eventMask
    "read next event - put into local eventBuffer.
     If aViewIdOrNil is nil, events for any view are fetched;
     otherwise only events for that specific view will be fetched.
     Returns true, if there was an event, false otherwise."

%{  /* NOCONTEXT */
    HWND win, wWanted;
    int evMask;
    OBJ eB;
    struct queuedEvent *ev;
    struct queuedEvent *qev;

    eB = __INST(eventBuffer);
    if (! __isByteArray(eB)) {
	fprintf(stderr, "WinWorkstation [error]: no eventBuffer\n");
	/* RETURN (false); */
    } else {
	ev = (struct queuedEvent *)(__ByteArrayInstPtr(eB)->ba_element);
	ev->ev_message = 0;

	if (__isSmallInteger(eventMask)) {
	    evMask = __intVal(eventMask);
	} else {
	    evMask = ~0;
	}
	if (__isExternalAddress(aViewIdOrNil)) {
	    wWanted = _HWNDVal(aViewIdOrNil);
	} else{
	    wWanted = 0;
	}

	do { /* only to allow continue */
	    if (deqEvent(ev, wWanted, evMask)) {
		if ((ev->ev_hWnd == lastPos_win)
		 && (ev->ev_message == WM_WINDOWPOSCHANGED)
		 && ((ev->ev_arg1 != lastPos_x)
		     || (ev->ev_arg2 != lastPos_y)
		     || (ev->ev_arg3 != lastPos_w)
		     || (ev->ev_arg4 != lastPos_h))) {
		    /*
		     * ignore resize 
		     * (that event is an intermediate one)
		     */
		    continue;
		}
		RETURN ( true );
	    }
	} while (0);
    }
%}.
    ^ false
!

handleAllEvents
    "from now on, handle any kind of event.
     Always with win32."

!

handleExposeOnlyFor:aView
    "from now on, handle expose events only.
     Never with win32."

!

setEventMask:aMask in:aWindowId
    "tell the display, that we are only interested in events from aMask, which
     is the bitwise-or of the eventMask bits (see 'eventMaskFor:')"

%{  /* NOCONTEXT */

    if (__isExternalAddress(aWindowId)
     && __isSmallInteger(aMask)) {
	HWND hWnd = _HWNDVal(aWindowId);
	int mask = __intVal(aMask);
	localWindowInfo *lI;

	if (lI = GETLOCALWINDOWINFOPTR(hWnd)) {
#ifdef DEBUGMASK
	    PRINTF(("new eventMask %x\n",mask));
	    printMask(mask);
#endif
	    lI->eventMask = mask;
	    RETURN ( self );
	}
    }
%}.
    self primitiveFailed
! !

!WinWorkstation methodsFor:'event sending'!

sendKeyOrButtonEvent:typeSymbol x:xPos y:yPos keyOrButton:keySymCodeOrButtonNr state:stateMask toViewId:targetId
    "send a keyPress/Release or buttonPress/Release event to some (possibly alien) view.
     TypeSymbol must be one of: #keyPress, #keyRelease, #buttonPress , #buttonRelease.
     For buttonEvents, the keySymCodeOrButtonNr must be the buttons number (1, 2 ...);
     for key events, it can be either a symbol (as listen in X's keySyms)
     or a numeric keysym code. If state is nil, the modifier bits (shift & control)
     are computed from the keyboardMap - if non-nil, these are passed as modifierbits.
     The non-nil case is the lowlevel entry, where state must include any shift/ctrl information
     (not very user friendly)"

    'WinWorkstation [warning]: sendKeyOrButtonEvent unimplemented' infoPrintCR.
    ^ false
! !

!WinWorkstation methodsFor:'font stuff'!

createFontFor:aFontName
    "a basic method for font allocation; this method allows
     any font to be aquired (even those not conforming to
     standard naming conventions, such as cursor, fixed or k14)"

%{  
    HGDIOBJ hFont;
    char *fn;

    if (__isString(aFontName) || __isSymbol(aFontName)) {
	fn = __stringVal(aFontName);
	if ((strcmp(fn, "fixed") == 0) || (strcmp(fn, "ANSI_FIXED_FONT") == 0)) {
	    hFont = GetStockObject(ANSI_FIXED_FONT);
	} else if ((strcmp(fn, "variable") == 0) || (strcmp(fn, "ANSI_VAR_FONT") == 0)) {
	    hFont = GetStockObject(ANSI_VAR_FONT);
	} else if ((strcmp(fn, "system") == 0) || (strcmp(fn, "SYSTEM_FONT") == 0)) {
	    hFont = GetStockObject(SYSTEM_FONT);
	} else if ((strcmp(fn, "systemFixed") == 0) || (strcmp(fn, "SYSTEM_FIXED_FONT") == 0)) {
	    hFont = GetStockObject(SYSTEM_FIXED_FONT);
	} else if ((strcmp(fn, "deviceDefault") == 0) || (strcmp(fn, "DEVICE_DEFAULT_FONT") == 0)) {
	    hFont = GetStockObject(DEVICE_DEFAULT_FONT);
	} else {
	    hFont = GetStockObject(ANSI_FIXED_FONT);
	}
	if (hFont) {
	    DPRINTF(("createFontFor:%s -> %x\n", fn, hFont));
	    RETURN ( __MKOBJ(hFont) );
	}
    }
%}.
    ^ nil
!

fontMetricsOf:fontId
    "return a fonts metrics info object"

    |rawData info|

    rawData := Array new:10.
    (self primFontMetricsOf:fontId intoArray:rawData) isNil ifTrue:[
	self primitiveFailed.
	^ self
    ].

    info := DeviceWorkstation::DeviceFontMetrics new.
    info
      ascent:(rawData at:1)
      descent:(rawData at:2)
      maxAscent:(rawData at:3)
      maxDescent:(rawData at:4)
      minWidth:(rawData at:5)
      maxWidth:(rawData at:6)
      avgWidth:(rawData at:7).

    ^ info
!

fontsInFamily:aFamilyName face:aFaceName filtering:filter
    "return a set of all available fonts in aFamily/aFace on this display.
     On WinWorkStations there is curently Face
     But only thise matching filter (if nonNil)."

    |allFonts fonts|

    allFonts := self listOfAvailableFonts.
    allFonts isNil ifTrue:[^ nil].

    fonts := Set new.
    allFonts do:[:fntDescr |
	(aFamilyName sameAs:(fntDescr family)) ifTrue:[
	    (filter isNil or:[filter value:fntDescr]) ifTrue:[
		fonts add:fntDescr
	    ]
	]
    ].
    ^ fonts
!

fontsInFamily:aFamilyName face:aFaceName style:aStyleName filtering:filter
    "return a set of all available font in aFamily/aFace/aStyle
     on this display.
     On WinWorkStations there is curently no style or Face
     But only those matching filter (if nonNIl)."

    |allFonts fonts|

    allFonts := self listOfAvailableFonts.
    allFonts isNil ifTrue:[^ nil].

    fonts := Set new.
    allFonts do:[:fntDescr |
	(aFamilyName sameAs:(fntDescr family)) ifTrue:[
	    (filter isNil or:[filter value:fntDescr]) ifTrue:[
		fonts add:fntDescr
	    ]
	]
    ].
    ^ fonts
!

getAvailableFontsMatching:pattern
    "return an Array filled with font names matching aPattern"
    self halt.
    ^ nil
!

getDefaultFont
    "return a default font id - used when class Font cannot
     find anything usable"

     ^ self createFontFor:'fixed'
!

getFontWithFamily:familyString face:faceString
	    style:styleArgString size:sizeArg encoding:encodingSym

    "try to get the specified font, if not available, try the next smaller
     font."

    |styleString theName theId xlatedStyle id spacing|

    styleString := styleArgString.

    "special: if face is nil, allow access to X-fonts"
    faceString isNil ifTrue:[
	sizeArg notNil ifTrue:[
	    theName := familyString , '-' , sizeArg printString
	] ifFalse:[
	    theName := familyString
	].
	theName isNil ifTrue:[
	    "
	     mhmh - fall back to the default font
	    "
	    theName := 'fixed'
	].
	theId := self createFontFor:theName.
	theId isNil ifTrue:[
	    theId := self getDefaultFont
	].
	^ theId
    ].

    "/ spacing other than 'normal' is contained as last component
    "/ in style
    styleString notNil ifTrue:[
	((styleString endsWith:'-narrow')
	 or:[styleString endsWith:'-semicondensed']) ifTrue:[
	    |i|
	    i := styleString lastIndexOf:$-.
	    spacing := styleString copyFrom:(i+1).
	    styleString := styleString copyTo:(i-1).
	] ifFalse:[
	    spacing := 'normal'.
	].
    ].

    xlatedStyle := styleString.
    xlatedStyle notNil ifTrue:[
	xlatedStyle := xlatedStyle first asString
    ].

    id := self
	    getFontWithFoundry:'*'
	    family:familyString asLowercase
	    weight:faceString
	    slant:styleString "/ xlatedStyle
	    spacing:spacing
	    pixelSize:nil
	    size:sizeArg
	    registry:encodingSym
	    encoding:'*'.

    id isNil ifTrue:[
	(encodingSym notNil and:[encodingSym ~= '*']) ifTrue:[
	    "/ too stupid: encodings come in both cases
	    "/
	    id := self
		    getFontWithFoundry:'*'
		    family:familyString asLowercase
		    weight:faceString
		    slant:styleString "/ xlatedStyle
		    spacing:spacing
		    pixelSize:nil
		    size:sizeArg
		    registry:encodingSym asUppercase
		    encoding:'*'.
	    id isNil ifTrue:[
		id := self
			getFontWithFoundry:'*'
			family:familyString asLowercase
			weight:faceString
			slant:styleString "/ xlatedStyle
			spacing:spacing
			pixelSize:nil
			size:sizeArg
			registry:encodingSym asLowercase
			encoding:'*'.

		id isNil ifTrue:[
		    id := self
			    getFontWithFoundry:'*'
			    family:familyString asLowercase
			    weight:faceString asLowercase
			    slant:styleString asLowercase
			    spacing:spacing
			    pixelSize:nil
			    size:sizeArg
			    registry:encodingSym asLowercase
			    encoding:'*'.
		]
	    ]
	]
    ].
    ^ id

    "Modified: 24.2.1996 / 22:37:24 / cg"
    "Modified: 4.7.1996 / 11:38:47 / stefan"
!

getFontWithFoundry:foundry family:family weight:weight
	      slant:slant spacing:spc pixelSize:pSize size:size
	      registry:registry encoding:encoding

    "get the specified font, if not available, return nil.
     For now, this is a poor (incomplete) emulation of the X code ...
     Individual attributes can be left empty (i.e. '') or nil to match any.

     foundry:   'adobe', 'misc', 'dec', 'schumacher' ... usually '*'
     family:    'helvetica' 'courier' 'times' ...
     weight:    'bold' 'medium' 'demi' ...
     slant:     'r(oman)' 'i(talic)' 'o(blique)'
     spacing:   'narrow' 'normal' semicondensed' ... usually '*'
     pixelSize: 16,18 ... usually left empty
     size:      size in point (1/72th of an inch)
     registry:  iso8859, sgi ... '*'
     encoding:  vendor specific encoding (usually '*')
    "

    "
     Windows-NT/95 allows the creation of a font with the following parameters

	nHeight
	nWidth
	nEscapement
	nOrientation
	fnWeight        FW_DONTCARE, FW_NORMAL, FW_MEDIUM, FW_BOLD, ...
	fdwItalic       TRUE or FALSE
	fdwUnderline    TRUE or FALSE
	fdwStrikeOut    TRUE or FALSE
	fdwCharSet      ANSI_CHARSET, UNICODE_, SYMBOL_, SHIFTJIS_,...
	fdwOutputPrecision      DEFAULT, STRING, CHAR, ...
	fdwClipPrecision        DEFAULT, CHAR, STROKE, MASK, ...
	fdwQuality      DEFAULT, DRAFT, or PROOF.
	fdwPitchAndFamily
		DEFAULT, FIXED or VARIABLE pitch
		DECORATIVE, DONTCASE, MODERN, ROMAN, SCRIPT, or SWISS.
	lpszFace
		Typeface Name

      These two above descriptions will be matched as follows:

	foundry   - ignored
	family    - mapped to type face name.
	weight    - mapped to fnWeight
	slant     - used for style
	spacing   - NOT USED INITIALLY
	pixelSize - NOT USED INITIALLY
	size      - mapped to nHeight
	registry  - NOT USED INITIALLY
	encoding  - NOT USED INITIALLY used for dwType device, raster or truetype
     "

%{ 
    HGDIOBJ hFont;
    int  nHeight, nWidth, nEscapement, nOrientation;
    char* work;
    char* work2;
    DWORD fnWeight;
    DWORD fdwItalic;
    DWORD fdwUnderline;
    DWORD fdwStrikeOut;
    DWORD fdwCharSet;
    DWORD fdwOutputPrecision;
    DWORD fdwClipPrecision;
    DWORD fdwQuality;
    DWORD fdwPitchAndFamily;
    LPCTSTR lpszFace;
    static char temp[33];

/* INITIALIZE */
    strcpy( temp, "                           " );
    lpszFace = &temp[0];
    strcpy( (char *)lpszFace, "NULL" );
    nHeight  = 0;
    nWidth   = 0;
    nEscapement = 0;
    nOrientation = 0;
    fnWeight = FW_NORMAL;
    fdwItalic = FALSE;
    fdwUnderline = FALSE;
    fdwStrikeOut = FALSE;
    fdwCharSet   = ANSI_CHARSET;
    fdwOutputPrecision = OUT_DEFAULT_PRECIS;
    fdwClipPrecision   = CLIP_DEFAULT_PRECIS;
    fdwQuality         = DEFAULT_QUALITY;
    fdwPitchAndFamily  = FF_DONTCARE;

/* SET VALUES */
    if ( __isString( family ) ) {
	work = __stringVal( family );
	if (strcmp( work, "nil" ) != 0 ) {
	    strncpy( (char *)lpszFace, work, 32 );
	}
    }

    fnWeight = FW_MEDIUM;
    if( __isString( weight ) ) {
	work = __stringVal( weight );
	if (strcmp( work, "bold" ) == 0 ) {
	    fnWeight = FW_BOLD;
	} else {
	    if (strcmp( work, "medium" ) == 0 ) {
		fnWeight = FW_MEDIUM;
	    } else {
		if (strcmp( work, "demi" ) == 0 )
		    fnWeight = FW_LIGHT;
	    }
	}
    }

    if(__isSmallInteger( size )) {
	nHeight = __intVal( size );
    }

    if (__isString(slant)) {
	work2 = __stringVal( slant );
	work  = __stringVal( slant );

	if (strncmp(work2, "italic", 6) == 0)  {
	    fdwItalic = TRUE;
	    if ( work2[6] = '-' )
		strncpy( work, &work2[7], ( strlen( work2) - 7) );
	} else {
	    if (strncmp(work2, "oblique", 7) == 0)  {
		fdwItalic = TRUE;
		if ( work2[7] = '-' )
		    strncpy( work, &work2[8], ( strlen( work2) - 8) );
	    }
	}
	if (strncmp( work, "underline", 9 ) == 0 ) {
	    fdwUnderline = TRUE;
	    if( work[10] == '-' )
		strncpy( work2, &work[11], ( strlen( work ) - 10 ) );
	}
	if (strncmp( work2, "strikeOut", 9 ) == 0 ) {
	    fdwStrikeOut = TRUE;
	}
    }

    nHeight = -MulDiv(nHeight, __logPixelSY, 72);

    DPRINTF(("CreateFont face:%s h=%d\n", lpszFace, nHeight));
    hFont = CreateFont( nHeight,
			nWidth,
			nEscapement,
			nOrientation,
			fnWeight,
			fdwItalic,
			fdwUnderline,
			fdwStrikeOut,
			fdwCharSet,
			fdwOutputPrecision,
			fdwClipPrecision,
			fdwQuality,
			fdwPitchAndFamily,
			lpszFace );

    if (hFont != NULL) {
	DPRINTF(("createFont: %x\n", hFont));
#ifdef COUNT_RESOURCES
	__cnt_font++;
	RES1PRINTF(("CreateFont %d\n", __cnt_font));
#endif
	RETURN ( __MKOBJ(hFont) );
    }

    DPRINTF(("***** ERROR createFontWithFoundry failed ERROR *****\n" ));
%}.
    ^ nil

    "
     Display getFontWithFoundry:'*'
			 family:'courier'
			 weight:'medium'
			  slant:'r'
			spacing:nil
		      pixelSize:nil
			   size:13
		       registry:'iso8859'
		       encoding:'*'
    "

    "new NT Version: 20.2.1997 / 22:33:29 / dq"
!

listOfAvailableFonts
    "return a list with all available fonts on this display.
     Since this takes a long time, keep the result of the query for the
     next time. The elements of the returned collection are instances of
     FontDescription."

    |list typeFaceList|

    listOfFonts notNil ifTrue:[^ listOfFonts].

    list := OrderedCollection new.
    typeFaceList := OrderedCollection new.

    [
      self primEnumFontTypesInto:typeFaceList.

      "/Transcript showCR:typeFaceList.

      typeFaceList do:[:typeFace |
	  self primEnumFontsIn:typeFace into:list.
	  0
      ].
    ] valueUninterruptably.

    "/Transcript showCR:list.
    listOfFonts := OrderedCollection new.
    list do:[:anInfoArray |
	| fntDescr family face style size encoding |

	family := anInfoArray at:14.
	face   := anInfoArray at:5.
	style  := anInfoArray at:16.
	size   := anInfoArray at:1.
	encoding := anInfoArray at:15.

	fntDescr := FontDescription
			family:family
			face:face
			style:style
			size:size
			encoding:encoding.
	listOfFonts add:fntDescr.

	"/ manually generate italic version (any font can be)
	(style startsWith:'roman') ifTrue:[
	    fntDescr := FontDescription
			    family:family
			    face:face
			    style:('italic' , (style copyFrom:'roman' size+1))
			    size:size
			    encoding:encoding.
	    listOfFonts add:fntDescr.
	].
    ].

    ^ listOfFonts

    "
     Display listOfAvailableFonts.

     Display getAvailableFontsMatching:'*'.
    "

    "Modified: 27.9.1995 / 10:54:47 / stefan"
    "Modified: 17.4.1996 / 15:27:57 / cg"
!

primEnumFontTypesInto:typeFaceList
%{  
    if (__tmpDC) {
	if ( EnumFontFamilies( __tmpDC, NULL, EnumFPTypeFaceProc, (DWORD)&(typeFaceList))) {
	    DPRINTF(("EnumFonts successful\n"));
	}
    }
%}
!

primEnumFontsIn:typeFace into:fontList
%{  
    char *cp;

    if (__isString(typeFace)) {
	if (__tmpDC) {
	    if (EnumFontFamilies(__tmpDC, __stringVal(typeFace), EnumFontsProc, (DWORD)&(fontList))) {
		DPRINTF(("EnumFonts Successful\n"));
	    }
	}
    }
%}.
!

primFontMetricsOf:fontId intoArray:rawData
    "evaluate aBlock, passing a fonts metrics as arguments.
     fill passed array as:
      ascent     -> (data at:1)
      descent    -> (data at:2)
      maxAscent  -> (data at:3)
      maxDescent -> (data at:4)
      minWidth   -> (data at:5)
      maxWidth   -> (data at:6)
      avgWidth   -> (data at:7).
"

%{  

    if (__isExternalAddress(fontId)
     && __isArray(rawData)
     && (__arraySize(rawData) >= 7)) {
	SIZE size;
	int avgWidth;
	HGDIOBJ hFont;
	HGDIOBJ prevFont;
	TEXTMETRIC tmet;
	static char *s = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
	static int len;

	hFont = _HGDIOBJVal(fontId);

	/*
	 * temporarily set this font in the tmpDC (root-) context
	 */
#ifdef CACHE_LAST_TMP_FONT
	if (__tmpDC_hfont != hFont) {
	    prevFont = SelectObject(__tmpDC, hFont);
	    if (__tmpDC_prev_hfont == NULL) {
		__tmpDC_prev_hfont = prevFont;
	    }
	    __tmpDC_hfont = hFont;
	}
#else
	prevFont = SelectObject(__tmpDC, hFont);
#endif
	GetTextMetrics(__tmpDC, &tmet);
	if (len == 0) {
	    len = strlen(s);
	}
	GetTextExtentPoint32(__tmpDC, s, len, &size);
#ifndef CACHE_LAST_TMP_FONT
	SelectObject(__tmpDC, prevFont);
#endif
	avgWidth = (size.cx / (len / 2) + 1) / 2;

	__ArrayInstPtr(rawData)->a_element[0] = __MKSMALLINT(tmet.tmAscent);        /* ascent     -> (data at:1) */
	__ArrayInstPtr(rawData)->a_element[1] = __MKSMALLINT(tmet.tmDescent);       /* descent    -> (data at:2) */
	__ArrayInstPtr(rawData)->a_element[2] = __MKSMALLINT(tmet.tmAscent);        /* maxAscent  -> (data at:3) */
	__ArrayInstPtr(rawData)->a_element[3] = __MKSMALLINT(tmet.tmDescent);       /* maxDescent -> (data at:4) */
	__ArrayInstPtr(rawData)->a_element[4] = __MKSMALLINT(avgWidth);             /* minWidth   -> (data at:5) */
	__ArrayInstPtr(rawData)->a_element[5] = __MKSMALLINT(tmet.tmMaxCharWidth);  /* maxWidth   -> (data at:6) */
	__ArrayInstPtr(rawData)->a_element[6] = __MKSMALLINT(avgWidth);             /* avgWidth   -> (data at:7) */

	DPRINTF(("textMetrics h=%x  avgAsc=%d avgDesc=%d minW=%d maxW=%d avgW=%d\n",
		    hFont, tmet.tmAscent, tmet.tmDescent, avgWidth, tmet.tmMaxCharWidth,
		    tmet.tmAveCharWidth));
	RETURN (self);
    }
    RETURN (nil);
%}
!

releaseFont:aFontId

%{  /* NOCONTEXT */
    if (__isExternalAddress(aFontId)) {
	HGDIOBJ hFont = _HGDIOBJVal(aFontId);

	if (hFont) {
#ifdef COUNT_RESOURCES
	    __cnt_font--;
	   RES1PRINTF(("DestroyFont %d\n",__cnt_font));
#endif
	   DPRINTF(("ReleaseFont: %x\n", hFont));
	   _DeleteFont(hFont, __LINE__);
	}
    }
%}
!

sizesInFamily:aFamilyName face:aFaceName style:aStyleName filtering:filter
    "return a set of all available font sizes in aFamily/aFace/aStyle
     on this display.
     Redefined to handle X's special case of 0-size (which stands for any)"

    |sizes|

    sizes := super sizesInFamily:aFamilyName face:aFaceName style:aStyleName filtering:filter.
    (sizes notNil and:[sizes includes:0]) ifTrue:[
	"special: in X11R5 and above, size 0 means:
	 there are scaled versions in all sizes available"

	^ #(4 5 6 7 8 9 10 11 12 14 16 18 20 22 24 28 32 48 64)
    ].
    ^ sizes

    "
     Display sizesInFamily:'courier' face:'bold' style:'roman'
    "

    "Created: 27.2.1996 / 01:38:15 / cg"
!

widthOf:aString from:index1 to:index2 inFont:aFontId

%{  /* NOCONTEXT */
    char *cp;
    int len, n, i1, i2, l;
    OBJ cls;
    int nInstBytes;

    if (__bothSmallInteger(index1, index2)
     && __isExternalAddress(aFontId)
     && __isNonNilObject(aString)) {
	HGDIOBJ hFont,prevFont;
	SIZE tsize;

	hFont = _HGDIOBJVal(aFontId);

#ifdef CACHE_LAST_TMP_FONT
	if (__tmpDC_hfont != hFont) {
	    prevFont = SelectObject(__tmpDC, hFont);
	    if (__tmpDC_prev_hfont == NULL) {
		__tmpDC_prev_hfont = prevFont;
	    }
	    __tmpDC_hfont = hFont;
	}
#else
	prevFont = SelectObject(__tmpDC, hFont);
#endif

	i1 = __intVal(index1) - 1;
	cls = __qClass(aString);

	if (i1 >= 0) {
	    i2 = __intVal(index2) - 1;
	    if (i2 < i1) {
		RETURN ( __MKSMALLINT( 0 ) );
	    }

	    cp = (char *) _stringVal(aString);
	    l = i2 - i1 + 1;

	    if ((cls == @global(String)) || (cls == @global(Symbol))) {
		n = _stringSize(aString);
		if (i2 < n) {
		    cp += i1;

		    GetTextExtentPoint32(__tmpDC, cp, l, &tsize);

#ifdef SUPERDEBUG
		    if (__debug__) {
		      char buf[80];

		      GetTextFace(__tmpDC,80,buf);
		      printf("font1 %x %s >%s< l=%d dx=%d\n",hFont,buf,cp,l,tsize.cx);
		    }
#endif
#ifndef CACHE_LAST_TMP_FONT
		    SelectObject(__tmpDC, prevFont);
#endif
		    RETURN ( __MKSMALLINT(tsize.cx) );
		}
		RETURN (__MKSMALLINT(0));
	    }

	    nInstBytes = __OBJS2BYTES__(__intVal(__ClassInstPtr(cls)->c_ninstvars));
	    cp += nInstBytes;
	    n = __byteArraySize(aString) - nInstBytes;

	    if (__isBytes(aString)) {
		if (i2 < n) {
		    cp += i1;

		    GetTextExtentPoint32(__tmpDC, cp, l, &tsize);
#ifdef SUPERDEBUG
		    if (__debug__) {
		      char buf[80];

		      GetTextFace(__tmpDC,80,buf);
		      printf("font1 %x %s >%s< l=%d dx=%d\n",hFont,buf,cp,l,tsize.cx);
		    }
#endif
#ifndef CACHE_LAST_TMP_FONT
		    SelectObject(__tmpDC, prevFont);
#endif
		    RETURN ( __MKSMALLINT(tsize.cx) );
		}
		RETURN (__MKSMALLINT(0));
	    }

	    /* Unicode */
	    if (__isWords(aString)) {
		n = n / 2;
		if (i2 < n) {
		    WIDECHAR *w_cp = (WIDECHAR *)cp;

		    w_cp += i1;

		    GetTextExtentPoint32W(__tmpDC, w_cp, l, &tsize);
		    printf("GetTextExtentPoint32W OK\n");
#ifndef CACHE_LAST_TMP_FONT
		    SelectObject(__tmpDC, prevFont);
#endif
		    RETURN ( __MKSMALLINT(tsize.cx) );
		}
		RETURN (__MKSMALLINT(0));
	    }
	}
    }
%}.
    ^ nil
! !

!WinWorkstation methodsFor:'grabbing '!

allowEvents:mode
!

grabKeyboardIn:aWindowId
    "grab the keyboard"
%{    
#ifdef NOT_YET_IMPLEMENTED
    if (__isExternalAddress(aWindowId)) {
      HWND hWnd = _HWNDVal(aWindowId);

      CPRINTF(("grabKeyboard in %x\n",hWnd));
    }
#endif
%}
!

grabPointerIn:aWindowId withCursor:aCursorId pointerMode:pMode keyboardMode:kMode confineTo:confineId
    "grap the pointer - return true if ok"

%{  
    if (__isExternalAddress(aWindowId)) {
	HWND hWnd = _HWNDVal(aWindowId);
	HCURSOR hCursor = 0;

	if (__currentCapture != CAPTURE_NONE) {
	    PostMessage(__currentPointerView, WM_THREAD_SETCAPTURE, 0, 0);
	    //ReleaseCapture();
	}
#if 1
	/*
	 * place a special, invisible view above the root
	 */
	if (hWnd == __rootWin) {
	    hWnd = __rootWinSpezial;
	    ShowWindow(hWnd, SW_SHOWNOACTIVATE);
	    //EnableWindow(hWnd,TRUE);
	    SetWindowPos(hWnd, HWND_TOP,
			 0, 0, 0, 0, 
			 SWP_NOREDRAW | SWP_NOSENDCHANGING | SWP_NOCOPYBITS
			 | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
	    CPRINTF(("setRootCapture %x\n",hWnd));
	}
#endif
	if (__isExternalAddress(aCursorId)) {
	    hCursor = _HCURSORVal(aCursorId);
	    CPRINTF(("grabPointerIn  SetCapture %x\n",hWnd));
	}
	PostMessage(hWnd, WM_THREAD_SETCAPTURE, (INT)hWnd, (INT)hCursor);
	RETURN (true);
    }
%}.
    ^ false
!

ungrabKeyboard
    "release the keyboard"

    activeKeyboardGrab := nil.
%{    
#ifdef NOT_YET_IMPLEMENTED
    CPRINTF(("ungrabKeyboard\n"));
#endif
%}
!

ungrabPointer
    "release the pointer"

%{  /* NOCONTEXT */
    CPRINTF(("ungrabPointer 1\n"));
    PostThreadMessage(_dispatchThreadId, WM_THREAD_SETCAPTURE, 0, 0);
%}.
    activePointerGrab := nil
! !

!WinWorkstation methodsFor:'graphic context stuff'!

noClipIn:aWindowId gc:aGCId
    "disable clipping rectangle"

%{  /* NOCONTEXT */

    if (__isExternalAddress(aWindowId)
     && __isExternalAddress(aGCId)) {
	struct gcData *gcData = _GCDATA(aGCId);

	if (gcData->clipping != FALSE) {
	    gcData->clipping = FALSE;
#ifdef CACHE_LAST_DC
	    if (lastGcData == gcData) {
		SelectClipRgn(gcData->_hDC, NULL);
	    }
#endif
	}
	RETURN (self);
    }
%}
!

setBackground:bgColorIndex in:aGCId
    "set background color to be drawn with"

%{  /* NOCONTEXT */

    if (__isExternalAddress(aGCId)) {
	struct gcData *gcData = _GCDATA(aGCId);
	COLORREF bgColor;

	bgColor = (COLORREF)st2RGB(__intVal(bgColorIndex),gcData);
	if (bgColor != gcData->bgColor) {
	    gcData->bgColor = bgColor;
#ifdef CACHE_LAST_DC
	    if (lastGcData == gcData) {
		SetBkColor(gcData->_hDC, bgColor);
		/*
		 * no need to flush pen - it does not depend upon
		 * the bg color
		 */
	    }
#endif
	}

	CPRINTF(("setBackground: %x\n", bgColor));
	RETURN (self);
    }
%}
!

setBitmapMask:aBitmapId in:aGCId
    "set or clear the drawing mask - a bitmap mask using current fg/bg"

%{  /* NOCONTEXT */

    if (__isExternalAddress(aGCId)) {
	struct gcData *gcData = _GCDATA(aGCId);
	HBITMAP oldM;

	oldM = gcData->hMask;
	if (__isExternalAddress(aBitmapId))
	    gcData->hMask = _HBITMAPVAL(aBitmapId);
	else
	    gcData->hMask = 0;

	if (oldM != gcData->hMask) {
	    FLUSH_CACHED_DC(gcData);
	    CPRINTF(("masks set to %x\n",gcData->hMask));
	}
	RETURN (self);
    }
%}
!

setClipByChildren:aBool in:aWindowId gc:aGCId
    "enable/disable drawing into child views"

%{  /* NOCONTEXT */

    if (ISCONNECTED && __isExternalAddress(aGCId)) {
	struct gcData *gcData = _GCDATA(aGCId);

	if (gcData && gcData->hWnd) {
	    int newClip;

	    newClip = (aBool == true) ? TRUE : FALSE;
	    if (newClip != gcData->clipByChildren) {
		/* set/clear clip by children */
		gcData->clipByChildren = newClip;
		FLUSH_CACHED_DC(gcData);
	    }
	} else {
	    DPRINTF(("clipping by child failed - invalid window\n" ));
	}
    }
%}.
    ^ self
!

setClipX:clipX y:clipY width:clipWidth height:clipHeight in:aDrawableId gc:aGCId
    "clip to a rectangle"

"
      p--w---
      |     |
      h     |  the clipping rectangle
      |     |
      -------
	  where p = ( clipX, clipY ), w = clipWidth, h = clipHeight
"

%{  /* NOCONTEXT */

    if (  __isExternalAddress(aGCId)
       && __bothSmallInteger(clipX, clipY)
       && __bothSmallInteger(clipWidth, clipHeight) ) {
	struct gcData *gcData = _GCDATA(aGCId);
	int cX, cY, cW, cH;

	cX = __intVal(clipX);
	cY = __intVal(clipY);
	cW = __intVal(clipWidth);
	cH = __intVal(clipHeight);

	if ((gcData->clipping != TRUE)
	 || (cX != gcData->clipX)
	 || (cY != gcData->clipY)
	 || (cW != gcData->clipW)
	 || (cH != gcData->clipH)) {
	    gcData->clipping = TRUE;
	    gcData->clipX = cX; 
	    gcData->clipY = cY; 
	    gcData->clipW = cW; 
	    gcData->clipH = cH; 

#ifdef CACHE_LAST_DC
	    /*
	     * must update current cached DC
	     */
	    if (lastGcData == gcData) {
		HRGN region = CreateRectRgn(cX, cY, cX + cW, cY + cH); 

		if (region == NULL ) {
		    fprintf(stderr, "WinWorkstat [warning]: clipping region creation failed\n");
		    FLUSH_CACHED_DC(gcData);
		} else {
		    if (SelectClipRgn(gcData->_hDC, region) == ERROR ) {
			fprintf(stderr, "WinWorkstat [warning]: select clipping region failed\n");
			FLUSH_CACHED_DC(gcData);
		    }
		    _DeleteObject(region, __LINE__);
		}
	    }
#endif
	}
	RETURN (self);
    }
%}
!

setDashes:dashList dashOffset:offset in:aGCId
    "set line attributes"

%{  /* NOCONTEXT */

    if (__isExternalAddress(aGCId)) {
	PRINTF(("WinWorkstat [warning]: dashes not (yet) implemented\n"));
    }
%}
!

setFont:aFontId in:aGCId
    "set font to be drawn in"

%{  /* NOCONTEXT */

    if (__isExternalAddress(aGCId)
     && __isExternalAddress(aFontId))
    {
	struct gcData *gcData = _GCDATA(aGCId);
	HGDIOBJ prevFont, hFont;
	TEXTMETRIC tmet;

	hFont = _HGDIOBJVal(aFontId);
	if (gcData->hFont != hFont) {
	    gcData->hFont = hFont;

#ifdef CACHE_LAST_TMP_FONT
	    if (__tmpDC_hfont != hFont) {
		prevFont = SelectObject(__tmpDC, hFont);
		if (__tmpDC_prev_hfont == NULL) {
		    __tmpDC_prev_hfont = prevFont;
		}
		__tmpDC_hfont = hFont;
	    }
#else
	    prevFont = SelectObject(__tmpDC, hFont);
#endif

	    GetTextMetrics(__tmpDC, &tmet);
	    gcData->fontAscent = tmet.tmAscent;
#ifdef SUPERDEBUG
	    if (__debug__) {
		char buf[80];

		GetTextFace(__tmpDC, 80, buf);
		PRINTF(("setFont: %x %s\n", hFont, buf));
	    }
#endif
#ifndef CACHE_LAST_TMP_FONT
	    SelectObject(__tmpDC, prevFont);
#endif

#ifdef CACHE_LAST_DC
	    /*
	     * must update current cached DC
	     */
	    if (lastGcData == gcData) {
		prevFont = SelectObject(gcData->_hDC, hFont);
		if (! gcData->save_hFont) {
		    gcData->save_hFont = prevFont;
		}
	    }
#endif
	    RETURN ( self );
	}
    }
%}
!

setForeground:fgColorIndex background:bgColorIndex in:aGCId
    "set foreground and background colors to be drawn with"

%{  /* NOCONTEXT */

    if (__isExternalAddress(aGCId)) {
	struct gcData *gcData = _GCDATA(aGCId);
	COLORREF fg, bg, oldFg, oldBg;

	fg = (COLORREF)st2RGB(__intVal(fgColorIndex),gcData);
	bg = (COLORREF)st2RGB(__intVal(bgColorIndex),gcData);
	oldFg = gcData->fgColor;
	oldBg = gcData->bgColor;

	if ((fg != oldFg) || (bg != oldBg)) {
	    gcData->fgColor = fg;
	    gcData->bgColor = bg;

#ifdef CACHE_LAST_DC
	    /*
	     * must update or flush current cached DC
	     */
	    if (lastGcData == gcData) {
		/* Pen only depends upon fg-color */
		if (fg != oldFg) {
		    SetTextColor(gcData->_hDC, fg);
		    FLUSH_CACHED_PEN(gcData);
		}
		if (bg != oldBg) {
		    SetBkColor(gcData->_hDC, bg);
		}
		FLUSH_CACHED_BRUSH(gcData);
	    }
#endif
	    CPRINTF(("setForeground: %x background: %x\n", gcData->fgColor, gcData->bgColor));
	}
	RETURN (self);
    }
%}
!

setForeground:fgColorIndex background:bgColorIndex mask:aBitmapId in:aGCId
    "set foreground and background colors to be drawn with using mask or
     solid (if aBitmapId is nil)"

%{  /* NOCONTEXT */

    if (__isExternalAddress(aGCId)) {
	struct gcData *gcData = _GCDATA(aGCId);
	COLORREF fg, bg, oldFg, oldBg;
	HBITMAP m, oldM;

	fg = (COLORREF)st2RGB(__intVal(fgColorIndex),gcData);
	bg = (COLORREF)st2RGB(__intVal(bgColorIndex),gcData);
	if (__isExternalAddress(aBitmapId))
	    m = _HBITMAPVAL(aBitmapId);
	else
	    m = 0;

	oldFg = gcData->fgColor;
	oldBg = gcData->bgColor;
	oldM = gcData->hMask;

	if ((oldFg != fg) || (oldBg != bg) || (oldM != m)) {
	    gcData->fgColor = fg;
	    gcData->bgColor = bg;
	    gcData->hMask = m;

#ifdef CACHE_LAST_DC
	    /*
	     * must update or flush current cached DC
	     */
	    if (lastGcData == gcData) {
		if (oldM == m) {
		    /*
		     * only fg/bg changed
		     */
		    /* Pen only depends upon fg-color */
		    if (fg != oldFg) {
			SetTextColor(gcData->_hDC, fg);
			FLUSH_CACHED_PEN(gcData);
		    }
		    if (bg != oldBg) {
			SetBkColor(gcData->_hDC, bg);
		    }
		    FLUSH_CACHED_BRUSH(gcData);
		} else {
		    _releaseDC(gcData);
		}
	    }
#endif
	    CPRINTF(("setForeground: %x background: %x mask: %x\n", gcData->fgColor, gcData->bgColor,gcData->hMask));
	}
	RETURN (self);
    }
%}
!

setForeground:fgColorIndex background:bgColorIndex mask:aBitmapId lineWidth:aLineWidth in:aGCId
    "set foreground and background colors to be drawn with using mask or
     solid (if aBitmapId is nil); also set lineWidth"

%{  /* NOCONTEXT */

    if (__isExternalAddress(aGCId)) {
	struct gcData *gcData = _GCDATA(aGCId);
	COLORREF fg, bg, oldFg, oldBg;
	HBITMAP m, oldM;
	int lw, oldLw;

	fg = (COLORREF)st2RGB(__intVal(fgColorIndex),gcData);
	bg = (COLORREF)st2RGB(__intVal(bgColorIndex),gcData);
	if (__isExternalAddress(aBitmapId))
	    m = _HBITMAPVAL(aBitmapId);
	else
	    m = 0;
	lw = __intVal(aLineWidth);

	oldFg = gcData->fgColor;
	oldBg = gcData->bgColor;
	oldLw = gcData->lineWidth;
	oldM = gcData->hMask;

	if ((oldFg != fg) || (oldBg != bg)
	 || (oldLw != lw) || (oldM != m)) {

	    gcData->fgColor = fg;
	    gcData->bgColor = bg;
	    gcData->lineWidth = lw;
	    gcData->hMask = m;

#ifdef CACHE_LAST_DC
	    /*
	     * must update or flush current cached DC
	     */
	    if (lastGcData == gcData) {
		if ((oldM == m) && (oldLw == lw)) {
		    /*
		     * only fg/bg changed
		     */
		    /* Pen only depends upon fg-color */
		    if (fg != oldFg) {
			SetTextColor(gcData->_hDC, fg);
			FLUSH_CACHED_PEN(gcData);
		    }
		    if (bg != oldBg) {
			SetBkColor(gcData->_hDC, bg);
		    }
		    FLUSH_CACHED_BRUSH(gcData);
		} else {
		    _releaseDC(gcData);
		}
	    }
#endif
	    CPRINTF(("setForeground: %x background: %x mask: %x linewidth: %d\n", 
			gcData->fgColor, gcData->bgColor,
			gcData->hMask, gcData->lineWidth));
	}
	RETURN (self);
    }
%}
!

setForeground:fgColorIndex in:aGCId
    "set foreground color to be drawn with"

%{  /* NOCONTEXT */

    HDC hDC;

    if (__isExternalAddress(aGCId)) {
	struct gcData *gcData = _GCDATA(aGCId);
	COLORREF fg;

	fg = (COLORREF)st2RGB(__intVal(fgColorIndex),gcData);
	if (fg != gcData->fgColor) {
	    gcData->fgColor = fg;

#ifdef CACHE_LAST_DC
	    /*
	     * must update or flush current cached DC
	     */
	    if (lastGcData == gcData) {
		/* Pen depends upon fg-color */
		SetTextColor(gcData->_hDC, fg);
		FLUSH_CACHED_PEN(gcData);
		FLUSH_CACHED_BRUSH(gcData);
	    }
#endif
	    CPRINTF(("setForeground: %x\n", gcData->fgColor));
	}
	RETURN (self);
    }
%}
!

setFunction:aFunctionSymbol in:aGCId
    "set alu function to be drawn with"

%{  /* NOCONTEXT */

    if (__isExternalAddress(aGCId)) {
	struct gcData *gcData = _GCDATA(aGCId);
	int fun = -1;
	int bfun = -1;

	if (aFunctionSymbol == @symbol(copy)) {
	    fun = R2_COPYPEN;
	    bfun = BITBLT_COPY;
	} else if (aFunctionSymbol == @symbol(copyInverted)) {
	    fun = R2_NOTCOPYPEN;
	    bfun = BITBLT_COPYINVERTED;
	} else if (aFunctionSymbol == @symbol(xor)) {
	    fun = R2_XORPEN;
	    bfun = BITBLT_XOR;
	} else if (aFunctionSymbol == @symbol(and)) {
	    fun = R2_MASKPEN;
	    bfun = BITBLT_AND;
	} else if (aFunctionSymbol == @symbol(or)) {
	    fun = R2_MERGEPEN;
	    bfun = BITBLT_OR;
	}

	if (fun != -1) {
#if 0
	    printf("set func to");
	    switch (bfun) {
		case BITBLT_COPY:
		    printf("BITBLT_COPY\n");
		    break;
		case BITBLT_COPYINVERTED:
		    printf("BITBLT_COPYINVERTED\n");
		    break;
		case BITBLT_XOR:
		    printf("BITBLT_XOR\n");
		    break;
		case BITBLT_AND:
		    printf("BITBLT_AND\n");
		    break;
		case BITBLT_OR:
		    printf("BITBLT_OR\n");
		    break;
	    }
#endif
	} else {
	    INFOPRINT( (stderr, "WinWorkstat [warning]: unsuported Rasterfunction\n") );
	    fun = R2_COPYPEN;
	    bfun = BITBLT_COPY;
	}

	if ((fun != gcData->rop2)
	 || (bfun != gcData->bitbltrop2)) {
	    gcData->rop2 = fun;
	    gcData->bitbltrop2 = bfun;
#ifdef CACHE_LAST_DC
	    /*
	     * must update current cached DC
	     */
	    if (gcData == lastGcData) {
		SetROP2(gcData->_hDC, fun);
	    }
#endif
	}
    } else {
	INFOPRINT((stderr, "WinWorkstation [warning]: Rasterfunction no GC\n"));
    }
%}
!

setGraphicsExposures:aBoolean in:aGCId
    "set or clear the graphics exposures flag"

!

setLineWidth:aNumber style:lineStyle cap:capStyle join:joinStyle in:aGCId
    "set line attributes"

%{  /* NOCONTEXT */

    HDC hDC;

    if (__isExternalAddress(aGCId)
     && __isSmallInteger(aNumber)) {
	struct gcData *gcData = _GCDATA(aGCId);
	int style;

	gcData->lineWidth = __intVal(aNumber);
	if (lineStyle == @symbol(solid)) {
	    style = PS_SOLID;
	} else if (lineStyle == @symbol(dashed)) {
	    style= PS_DASH;
	} else if (lineStyle == @symbol(dotted)) {
	    style= PS_DOT;
	} else if (lineStyle == @symbol(dashDot)) {
	    style= PS_DASHDOT;
	} else if (lineStyle == @symbol(dashDotDot)) {
	    style= PS_DASHDOTDOT;
	} else
	    style= PS_SOLID;
	gcData->lStyle &= ~PS_STYLE_MASK;
	gcData->lStyle |= style;


	if (capStyle == @symbol(round)) {
	    style = PS_ENDCAP_ROUND;
	} else if (capStyle == @symbol(square)) {
	    style = PS_ENDCAP_SQUARE;
	} else if (capStyle == @symbol(flat)) {
	    style = PS_ENDCAP_FLAT;
	} else
	    style = PS_ENDCAP_FLAT;
	gcData->lStyle &= ~PS_ENDCAP_MASK;
	gcData->lStyle |= style;
        
	if (joinStyle == @symbol(bevel)) {
	    style = PS_JOIN_BEVEL;
	} else if (joinStyle == @symbol(miter)) {
	    style = PS_JOIN_MITER;
	} else if (joinStyle == @symbol(round)) {
	    style = PS_JOIN_ROUND;
	} else
	    style = PS_JOIN_MITER;
	gcData->lStyle &= ~PS_JOIN_MASK;
	gcData->lStyle |= style;

	FLUSH_CACHED_DC(gcData);
	RETURN (self);
    }
%}
!

setMaskOriginX:orgX y:orgY in:aGCId
    "set the mask origin"

%{  /* NOCONTEXT */

    if (__isExternalAddress(aGCId)
     && __bothSmallInteger(orgX,orgY)) {
	struct gcData *gcData = _GCDATA(aGCId);
	int oX, oY;

	oX = __intVal(orgX);
	oY = __intVal(orgY);
	if ((oX != gcData->maskOrgX)
	 || (oY != gcData->maskOrgY)) {
	    gcData->maskOrgX = __intVal(orgX);
	    gcData->maskOrgY = __intVal(orgY);;
	    FLUSH_CACHED_DC(gcData);
	}
	RETURN (self);
    }
%}
!

setPixmapMask:aPixmapId in:aGCId
    "set or clear the drawing mask - a pixmap mask providing full color"

%{  /* NOCONTEXT */
    if (__isExternalAddress(aGCId)) {
	struct gcData *gcData = _GCDATA(aGCId);
	HBITMAP oldM;

	oldM = gcData->hMask;
	if (__isExternalAddress(aPixmapId))
	    gcData->hMask = _HBITMAPVAL(aPixmapId);
	else
	    gcData->hMask = 0;

	if (oldM != gcData->hMask) {
	    FLUSH_CACHED_DC(gcData);
	    DPRINTF(("PixmapMasks set to %x\n",gcData->hMask));
	}
	RETURN (self);
    }
%}
! !

!WinWorkstation methodsFor:'initialize / release'!

close
    "close down the connection to the display"

!

initializeDefaultValues
    focusMode := #activeWindow.

"/    self activateOnClick:true.
"/    self ignoreButtonPressOnActivate:false.
"/    self rightButtonIsLowerWindow:true.
"/    self shiftedLeftButtonIsLowerWindow:true.

    buttonTranslation := ButtonTranslation.

    self initializeModifierMappings
!

initializeDevice
%{  

    int scr;
    int maxRGBDepth;
    int rgbRedMask, rgbGreenMask, rgbBlueMask;
    int nvi, i;
    char *type, *nm;
    int dummy;
    int mask, shift, nBits;
    HDC _rootDC;
    OBJ id;

    if (firstInstance) {
	OSVERSIONINFO osvi;
	WNDCLASS wc;
	firstInstance = 0;
	DPRINTF(("first create - registerClass\n"));

	memset(&osvi, 0, sizeof(OSVERSIONINFO));
	osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
	GetVersionEx (&osvi);

	if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
	    __isWinNT = 1;
	    __INST(isWin95) = false;
	} else {
	    __isWinNT = 0;
	    __INST(isWin95) = true;
	}

	/*
	 * register class: ST/X
	 */
	wc.style = 0 
		   /* | CS_OWNDC */
		   /* | CS_HREDRAW */
		   /* | CS_VREDRAW */ 
		  | CS_DBLCLKS;
	wc.lpfnWndProc = (WNDPROC) MainWndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = N_WINDOW_PRIVATE;
	wc.hInstance = (HANDLE) __getHInstance();
	wc.hIcon = NULL;
	wc.hCursor = 0;
	wc.hbrBackground = 0;

	wc.lpszMenuName =  NULL;
	wc.lpszClassName = app_name;

	if (!RegisterClass(&wc)) {
	    DPRINTF(("RegisterClass failed\n"));
	}

	/*
	 * register class: ST/X:Root
	 */
	wc.style = 0;
	wc.lpfnWndProc = (WNDPROC) MainWndProc;;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = N_WINDOW_PRIVATE;
	wc.hInstance = (HANDLE) __getHInstance();
	wc.hIcon = NULL;
	wc.hCursor = 0;
	wc.hbrBackground = GetStockObject(HOLLOW_BRUSH);

	wc.lpszMenuName =  NULL;
	wc.lpszClassName = app_nameRoot;

	if (! RegisterClass(&wc)) {
	    DPRINTF(("RegisterClass failed\n"));
	}

	/*
	 * register class: ST/X:Popup
	 */
	wc.style = CS_SAVEBITS;
	wc.lpfnWndProc = (WNDPROC) MainWndProc;;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = N_WINDOW_PRIVATE;
	wc.hInstance = (HANDLE) __getHInstance();
	wc.hIcon = NULL;
	wc.hCursor = 0;
	wc.hbrBackground = GetStockObject(HOLLOW_BRUSH);

	wc.lpszMenuName =  NULL;
	wc.lpszClassName = app_namePopup;

	if (! RegisterClass(&wc)) {
	    DPRINTF(("RegisterClass failed\n"));
	}

	/*
	 * generate my events
	 */
	hNeverTriggered = CreateEvent(
				NULL,            /* no security attributes */
				FALSE,
				FALSE,
				"Never"); 

	if (hNeverTriggered == NULL) {
	    fprintf(stderr, "WinWorkstation [fatal]: CreateEvent-1 failed\n");
	    exit(1);
	}

	hCreateEvent = CreateEvent(
				NULL,            /* no security attributes */
				FALSE,
				FALSE,
				"CreateEvents"); /* name of event */

	if (hCreateEvent == NULL) {
	    fprintf(stderr, "WinWorkstation [fatal]: CreateEvent-2 failed\n");
	    exit(1);
	}

	hInputEvent = CreateEvent(
				NULL,            /* no security attributes */
				FALSE,
				FALSE,
				"InputEvents");  /* name of event */

	if (hInputEvent == NULL) {
	    fprintf(stderr, "WinWorkstation [fatal]: CreateEvent-3 failed\n");
	    exit(1);    
	}

	hEventQMutex = CreateMutex(
				NULL,         /* no security attributes */
				FALSE,        /* initially not owned */
				"EvQMutex");  /* name of mutex */

	if (hEventQMutex == NULL) {
	    fprintf(stderr, "WinWorkstation [fatal]: CreateMutex-1 failed\n");
	    exit(1);    
	}
	DuplicateHandle(GetCurrentProcess(), 
			hEventQMutex, 
			GetCurrentProcess(), 
			&hEventQMutex, 
			DUPLICATE_SAME_ACCESS, 
			FALSE, 
			DUPLICATE_SAME_ACCESS);

	hEventFreeListMutex = CreateMutex(
				NULL,          /* no security attributes */
				FALSE,         /* initially not owned */
				"EvFMutex");   /* name of mutex */

	if (hEventFreeListMutex == NULL) {
	    fprintf(stderr, "WinWorkstation [fatal]: CreateMutex-2 failed\n");
	    exit(1);    
	}
	DuplicateHandle(GetCurrentProcess(), 
			hEventFreeListMutex, 
			GetCurrentProcess(), 
			&hEventFreeListMutex, 
			DUPLICATE_SAME_ACCESS, 
			FALSE, 
			DUPLICATE_SAME_ACCESS);

#ifdef USE_PAINT_MUTEX
	hPaintMutex = CreateMutex(
				NULL,           /* no security attributes */
				FALSE,          /* initially not owned */
				"PaintMutex");  /* name of mutex */

	if (hPaintMutex == NULL) {
	    fprintf(stderr, "WinWorkstation [fatal]: CreateMutex-3 failed\n");
	    exit(1);    
	}
	DuplicateHandle(GetCurrentProcess(), 
			hPaintMutex, 
			GetCurrentProcess(), 
			&hPaintMutex, 
			DUPLICATE_SAME_ACCESS, 
			FALSE, 
			DUPLICATE_SAME_ACCESS);
#endif

#ifndef WIN32THREADS
	_masterThreadId = GetCurrentThreadId();
	DuplicateHandle(GetCurrentProcess(),
			GetCurrentThread(),
			GetCurrentProcess(),
			&_masterThread,
			DUPLICATE_SAME_ACCESS,
			FALSE,
			DUPLICATE_SAME_ACCESS);
#endif

	initEventqueue();

	/*
	 * start the event dispatcher thread and wait for it to
	 * be ready
	 */
	__beginthread(dispatchThread, EVENT_THREAD_STACKSIZE, 0);

	if (WaitForSingleObject(hCreateEvent, 5000L) != WAIT_OBJECT_0) {
	    fprintf(stderr, "WinWorkstation [error]: oops - timeout waiting for eventThread to start\n");
	}
    }

#if 0
    __rootDesk = OpenDesktop ("Desktop0", 0, FALSE, GENERIC_ALL);
    if (!__rootDesk) {
	PRINTF(("OpenDesktop fail\n"));
	__rootDesk = CreateDesktop ("Desktop0", NULL, NULL, 0, DESKTOP_WRITEOBJECTS, NULL);
	if (!__rootDesk) {
	    PRINTF(("CreateDesktop fail\n"));
	}
    }
#endif

    __rootWin = GetDesktopWindow();
    __PROTECT__(self);
    id = __MKOBJ(__rootWin);
    __UNPROTECT__(self);
    __INST(rootWin) = id; __STORE(self, id);

    _rootDC = __rootDC = CreateDC("DISPLAY", NULL, NULL, NULL);

    __tmpDC = CreateCompatibleDC(_rootDC);

    __nullPen = GetStockObject(NULL_PEN);
    __blackPen = GetStockObject(BLACK_PEN);
    __whitePen = GetStockObject(WHITE_PEN);
    __blackBrush = GetStockObject(BLACK_BRUSH);
    __whiteBrush = GetStockObject(WHITE_BRUSH);

    __PROTECT__(self);
    id = __MKOBJ(_rootDC);
    __UNPROTECT__(self);
    __INST(rootDC) = id; __STORE(self, id);
%}.
!

initializeEventBuffer
    |sz|

%{
    sz = __MKSMALLINT(sizeof(struct queuedEvent) + 100);
%}.
    eventBuffer isNil ifTrue:[
	eventBuffer := ByteArray new:sz.
    ].
!

initializeFor:aDisplayName
    "initialize the receiver for a connection to the display;
     the argument, aDisplayName is ignored under Win32.
     (who knows - it may be used in the future to support multiple
      windows displays ...)"

    displayId notNil ifTrue:[
	"/ already connected - trying to trick me ?
	^ self
    ].

    displayId := self primInitializeFor:aDisplayName.
    displayId isNil ifTrue:[^ self].

    dispatching := false.
    isSlow := false.
    shiftDown := false.
    ctrlDown := false.
    metaDown := false.
    altDown := false.
    motionEventCompression := true.
    buttonsPressed := 0.

    self initializeScreenProperties.
    self initializeDevice.
    self initializeVariableScreenProperties.
    self initializeVariableSettingsProperties.
    self initializeDeviceResourceTables.

    self initializeDefaultValues.
    self initializeEventBuffer.
    self initializeSpecialFlags.
    self initializeKeyboardMap.
!

initializeModifierMappings
    shiftModifiers := #(#'Shift_L' #'Shift_R' #'Shift').
    ctrlModifiers := #(#'Ctrl_L' #'Ctrl_R' #'Ctrl').
    metaModifiers := #(#'Alt_L' #'Alt').
    altModifiers := #(#'Alt_R').
!

initializeSpecialFlags
    "perform additional special server implementation flags"
!

initializeVariableScreenProperties
    "fetch properties which can be changed via the users display-settings.
     Invoked initially and as a result of the displayChange event."

%{
    int nvi, i, val, capabilities, planes, numcolors, numpens;
    RECT rect;

    GetWindowRect(__rootWin, &rect);
    __INST(width) = __MKSMALLINT(rect.right - rect.left);
    __INST(height) = __MKSMALLINT(rect.bottom - rect.top);
    DPRINTF(("screen is %d/%d\n", __intVal(__INST(width)), __intVal(__INST(height))));

    __realDepth = GetDeviceCaps(__rootDC, BITSPIXEL);

#ifdef ALWAYSTRUECOLOR
    __depth = 24;  /* its a hack */
#else
    __depth = __realDepth;
    if (__depth == 15)
	__depth = 16;
#endif

#if 0
    numcolors = GetDeviceCaps(__rootDC, NUMCOLORS);
    numpens = GetDeviceCaps(__rootDC, NUMPENS);
    planes = GetDeviceCaps(__rootDC, PLANES);

    DPRINTF(("screen has %d planes\n", planes));
    DPRINTF(("numcolors is %d\n", numcolors));
    DPRINTF(("numpens is %d\n", numpens));
#endif

    __INST(depth) = __MKSMALLINT(__depth);
    __INST(ncells) = __MKSMALLINT(1<<__depth);

    val = GetDeviceCaps(__rootDC, HORZSIZE);
    DPRINTF(("HORSIZE=%d\n",val));
    __INST(widthMM) = __MKSMALLINT(val);
    val = GetDeviceCaps(__rootDC, VERTSIZE);
    DPRINTF(("VERTSIZE=%d\n",val));
    __INST(heightMM) = __MKSMALLINT(val);

    __logPixelSY = GetDeviceCaps(__rootDC, LOGPIXELSY);
    capabilities = GetDeviceCaps(__rootDC, RASTERCAPS);

#ifdef LATER
    printf("device support:\n");
    if (capabilities & RC_BANDING)
	printf(" RC_BANDING");

    if (capabilities & RC_BITBLT)
	printf(" RC_BITBLT");

    if (capabilities & RC_BITMAP64)
	printf(" RC_BITMAP64");

    if (capabilities & RC_DI_BITMAP)
	printf(" RC_DI_BITMAP");

    if (capabilities & RC_DIBTODEV)
	printf(" RC_DIBTODEV");

    if (capabilities & RC_FLOODFILL)
	printf(" RC_FLOODFILL");

    if (capabilities & RC_PALETTE)
	printf(" RC_PALETTE");

    if (capabilities & RC_SCALING)
	printf(" RC_SCALING");

    if (capabilities & RC_STRETCHBLT)
	printf(" RC_STRETCHBLT");

    if (capabilities & RC_STRETCHDIB)
	printf(" RC_STRETCHDIB");

    printf("\n");
    printf("cursor size %d %d\n", GetSystemMetrics(SM_CXCURSOR), GetSystemMetrics(SM_CYCURSOR));
#endif

    __INST(whitepixel) = __MKSMALLINT(WhitePixel);
    __INST(blackpixel) = __MKSMALLINT(BlackPixel);

    __INST(monitorType) = @symbol(unknown);

#ifdef ALWAYSTRUECOLOR
    __INST(hasColors) = true;
    __INST(hasGreyscales) = true;
    __INST(visualType) = @symbol(TrueColor);
    __INST(redShift) = __MKSMALLINT(0);
    __INST(greenShift) = __MKSMALLINT(8);
    __INST(blueShift) = __MKSMALLINT(16);
    switch (__realDepth) {
	case 15:
	case 16:
	    __INST(bitsPerRGB) = __MKSMALLINT(5);
	    break;
	default:
	    __INST(bitsPerRGB) = __MKSMALLINT(8);
    }
    __INST(bitsRed) = __INST(bitsGreen) = __INST(bitsBlue) = __MKSMALLINT(8);
    __INST(redMask) = __MKSMALLINT(0xFF);
    __INST(greenMask) = __MKSMALLINT(0xFF00);
    __INST(blueMask) = __MKSMALLINT(0xFF0000);
#else
    if (! (capabilities & RC_PALETTE)) {
	DPRINTF(("no palette\n"));
	if (__depth == 1) {
	    __INST(hasColors) = false;
	    __INST(hasGreyscales) = false;
	    __INST(visualType) = @symbol(GrayScale);
	    __INST(monitorType) = @symbol(monochrome);
	} else {
	    __INST(hasColors) = true;
	    __INST(hasGreyscales) = true;
	    if ((__depth == 16) || (__depth == 15)) {
	      __INST(visualType) = @symbol(TrueColor);
	      __INST(blueShift) = __MKSMALLINT(0);
	      __INST(greenShift) = __MKSMALLINT(5);
	      __INST(redShift) = __MKSMALLINT(11);
	      __INST(bitsPerRGB) = __MKSMALLINT(5);
	      __INST(bitsRed) = __INST(bitsGreen) = __INST(bitsBlue) = __MKSMALLINT(5);
	      __INST(blueMask) = __MKSMALLINT(0x1f);
	      __INST(greenMask) = __MKSMALLINT(0x7e0);
	      __INST(redMask) = __MKSMALLINT(0xf800);
	    } else {
	      __INST(visualType) = @symbol(TrueColor);
	      __INST(redShift) = __MKSMALLINT(0);
	      __INST(greenShift) = __MKSMALLINT(8);
	      __INST(blueShift) = __MKSMALLINT(16);
	      __INST(bitsPerRGB) = __MKSMALLINT(8);
	      __INST(bitsRed) = __INST(bitsGreen) = __INST(bitsBlue) = __MKSMALLINT(8);
	      __INST(redMask) = __MKSMALLINT(0xFF);
	      __INST(greenMask) = __MKSMALLINT(0xFF00);
	      __INST(blueMask) = __MKSMALLINT(0xFF0000);
	    }
	}
    } else {
	val = GetDeviceCaps(__rootDC, SIZEPALETTE); /* First two entries are black and white. */
	// printf("SizeofPalette %d\n",val);
	__INST(ncells) = __MKSMALLINT(val);
	__INST(blackpixel) = __MKSMALLINT(0);
	__INST(whitepixel) = __MKSMALLINT(1);
	__INST(hasColors) = true;
	__INST(hasGreyscales) = true;
	// __INST(usingSystemPalette) = true;

	__INST(redShift) = __MKSMALLINT(0);
	__INST(greenShift) = __MKSMALLINT(2);
	__INST(blueShift) = __MKSMALLINT(4);
	__INST(bitsPerRGB) = __MKSMALLINT(2);
	__INST(bitsRed) = __INST(bitsGreen) = __INST(bitsBlue) = __MKSMALLINT(2);
	__INST(redMask) = __MKSMALLINT(0x3);
	__INST(greenMask) = __MKSMALLINT(0xc); 
	__INST(blueMask) = __MKSMALLINT(0x30);
    }
#endif /* ! ALWAYSTRUECOLOR */
%}
!

initializeVariableSettingsProperties
    "fetch properties which can be changed via the user settings.
     Invoked initially and as a result of the settingsChange event."

%{
    UINT multiClickTime;

    multiClickTime = GetDoubleClickTime();
    DPRINTF(("multiClickTime = %d\n", multiClickTime));
    __INST(multiClickTimeDelta) = __MKSMALLINT(multiClickTime);
%}
!

primInitializeFor:aDisplayName
    "initialize the receiver for a connection to a display;
     the argument, aDisplayName may be nil (for the default display)
     or the name of the display server as hostname:number
     (not yet under WIN32)"

%{  /* NOCONTEXT */

    RETURN ( __MKSMALLINT(1) );
%}.
!

reinitialize
    rootWin := rootDC := nil.
    super reinitialize.
! !

!WinWorkstation methodsFor:'keyboard mapping'!

altModifierMask
    "return the mask (in motionEvents) for the alt-key modifier.
     Notice: ST/X may use the left ALT key as CMD/Meta key,
     therefore return a variable here, which can be changed during startup."

%{  /* NOCONTEXT */
    RETURN (__MKSMALLINT(AltMask));
%}

    "Created: 23.3.1996 / 12:43:22 / cg"
    "Modified: 23.3.1996 / 12:44:56 / cg"
!

leftAltMask
    "return the mask bit for the left Alt modifier key.
     See comment in altModifierMask: / metaModifierMask: for what
     this could be used."

%{  /* NOCONTEXT */
    RETURN (__MKSMALLINT(LeftAltMask));
%}
!

metaModifierMask
    "return the mask (in motionEvents) for the meta-key modifier.
     Notice: ST/X may use the left ALT key as CMD/Meta key,
     therefore return a variable here, which can be changed during startup."

%{  /* NOCONTEXT */
    RETURN (__MKSMALLINT(MetaMask));
%}

    "Created: 23.3.1996 / 12:43:39 / cg"
    "Modified: 23.3.1996 / 12:45:09 / cg"
!

modifierMapping
    "Get the Modifier Mapping.
     We return an array of arrays of keycodes"

    ^ nil
!

rightAltMask
    "return the mask bit for the right Alt modifier key.
     See comment in altModifierMask: / metaModifierMask: for what
     this could be used."

%{  /* NOCONTEXT */
    RETURN (__MKSMALLINT(RightAltMask));
%}
!

stringFromKeycode:code
    "Get a KeySymbol (a smalltalk symbol) from the keycode."

    ^ ''

    "
	Display stringFromKeycode:28
    "
! !

!WinWorkstation methodsFor:'misc'!

beep
    "output an audible beep"

    UserPreferences current beepEnabled ifFalse:[^self].
%{  
#ifdef BEEP_IN_WINTHREAD
    PostThreadMessage(_dispatchThreadId, WM_THREAD_BEEP, 0, 0);
#else
    MessageBeep( MB_ICONEXCLAMATION);
#endif
%}
!

flush
    "send all buffered drawing to the display.
     This may be required to make certain, that all previous operations
     are really sent to the display before continuing. For example,
     after a cursor-change with a followup long computation.
     (otherwise, the cursor change request may still be in the output buffer)
     See also #sync, which even waits until the request has been processed."

%{  /* NOCONTEXT */
     GdiFlush();
%}
!

setInputFocusTo:aWindowId
    self setInputFocusTo:aWindowId revertTo:nil
!

setInputFocusTo:aWindowId revertTo:revertSymbol
    "set the focus to the view as defined by aWindowId.
     Passing nil set the focus to no window and lets the display discard all
     input until a new focus is set.
     RevertSymbol specifies what should happen if the view becomes invisible;
     passing one of #parent, #root or nil specifies that the focus should be
     given to the parent view, the root view or no view."

%{
    if (__isExternalAddress(aWindowId)) {
	HWND hWnd = _HWNDVal(aWindowId);
	int r = 0;

	if (revertSymbol == @symbol(parent)) {
	    r = 1;
	} else if (revertSymbol == @symbol(root)) {
	    r = 2;
	}
	if (hWnd) {
	    if (GetFocus() != hWnd) {
		CPRINTF(("setInputFocusTo %x revertTo %d\n",hWnd,r));
#ifdef SET_FOCUS_IN_WINTHREAD
		PostMessage(hWnd, WM_THREAD_SETFOCUS, (INT)hWnd, GetCurrentThreadId());
#else
		if (SetFocus(hWnd) == 0) {
		    PRINTF(("SetFocus to %x failed.\n",hWnd));
		}
#endif
	    } else {
		CPRINTF(("setInputFocusTo %x revertTo %d\n",hWnd,r));
	    }
	}
	RETURN ( self );
    }
%}
! !

!WinWorkstation methodsFor:'pointer queries '!

anyButtonStateMask
    "return an integer for masking out any button from a
     buttonStates value."

    "/ should use ``Display buttonXMotionMask bitOr:....''

%{  /* NOCONTEXT */
    RETURN ( __MKSMALLINT(AnyButtonMask) );
%}

    "Modified: 23.3.1996 / 12:41:33 / cg"
    "Created: 23.3.1996 / 12:46:35 / cg"
!

buttonStates
    "return an integer representing the state of the pointer buttons;
     a one-bit in positions 0.. represent a pressed button.
     See the button1Mask/button2Mask/button3Mask,
     shiftMask/controlMask and modifierMask methods for the meaning of the bits."

%{  /* NOCONTEXT */
    int modifiers = 0;
    int b1m = Button1Mask;
    int b3m = Button3Mask;

    if (GetSystemMetrics(SM_SWAPBUTTON)) {
	b3m = Button1Mask;
	b1m = Button3Mask;
    }

    if (GetAsyncKeyState(VK_LBUTTON) & 0x8000)
	modifiers |= b1m;
    if (GetAsyncKeyState(VK_MBUTTON) & 0x8000)
	modifiers |= Button2Mask;
    if (GetAsyncKeyState(VK_RBUTTON) & 0x8000)
	modifiers |= b3m;

    RETURN (__MKSMALLINT(modifiers));
%}

    "
     Display buttonStates
    "

    "is the control-key pressed ?

     Display buttonStates bitTest:(Display controlMask)
    "

    "is the alt/meta-key pressed ?

     Display buttonStates bitTest:(Display altModifierMask)
     Display buttonStates bitTest:(Display metaModifierMask)
    "
!

leftButtonStateMask
    "return an integer for masking out the left button from a
     buttonStates value"

    "/ should use ``Display button1MotionMask''

%{  /* NOCONTEXT */
    RETURN ( __MKSMALLINT(Button1Mask) );
%}.

    "Modified: 23.3.1996 / 12:41:33 / cg"
!

middleButtonStateMask
    "return an integer for masking out the middle button from a
     buttonStates value"

    "/ should use ``Display button2MotionMask''

%{  /* NOCONTEXT */
    RETURN ( __MKSMALLINT(Button2Mask) );
%}.

    "Modified: 23.3.1996 / 12:41:43 / cg"
!

pointerPosition
    "return the current pointer position in root-window (i.e. screen) coordinates"

%{  /* NOCONTEXT */
    POINT p;

    if (! GetCursorPos(&p)) {
	p.x = 0;
	p.y = 0;
    }
    RETURN (__MKPOINT_INT(p.x, p.y));
%}
!

rightButtonStateMask
    "return an integer for masking out the right button from a
     buttonStates value"

    "/ should use ``Display button3MotionMask''

%{  /* NOCONTEXT */
    RETURN ( __MKSMALLINT(Button3Mask) );
%}.

    "Modified: 23.3.1996 / 12:41:52 / cg"
!

rootPositionOfLastEvent
    "return the position in root-window coordinates
     of the last button, key or pointer event"

%{  /* NOCONTEXT */
    RETURN (__MKPOINT_INT(evRootX, evRootY));
%}
!

setPointerPosition:newPosition
    "change the pointer position in root-window coordinates."

%{  /* NOCONTEXT */
    if (__isPoint(newPosition)) {
	OBJ xpos, ypos;

	xpos = __point_X(newPosition);
	ypos = __point_Y(newPosition);
	if (__bothSmallInteger(xpos, ypos)) {
	    SetCursorPos(__intVal(xpos), __intVal(ypos));
	}
    }
%}
!

setPointerPosition:newPosition in:aWindowId
    "change the pointer position to a new position relative to the
     given windows origin (which may be the rootWindow).
     Be careful with this - its usually not very ergonimically
     to change the mousePointer position.
     This interface is provided for special applications (presentation
     playback) and should not be used in normal applications."

    self setCursorPosition:(self translatePoint:newPosition from:aWindowId to:rootView id).
! !

!WinWorkstation methodsFor:'retrieving pixels'!

getBitsFromId:aDrawableId x:srcx y:srcy width:w height:h into:imageBits
    "get bits from a drawable into the imageBits. The storage for the bits
     must be big enough for the data to fit. If ok, returns an array with some
     info and the bits in imageBits. The info contains the depth, bitOrder and
     number of bytes per scanline. The number of bytes per scanline is not known
     in advance, since the X-server is free to return whatever it thinks is a good padding."

    |rawInfo info|

    ((w <= 0) or:[h <= 0]) ifTrue:[
	self primitiveFailed.
	^ nil
    ].

    rawInfo := Array new:8.
		  "1 -> bit order"
		  "2 -> depth"
		  "3 -> bytes_per_line"
		  "4 -> byte_order"
		  "5 -> format"
		  "6 -> bitmap_unit"
		  "7 -> bitmap_pad"
		  "8 -> bits_per_pixel"

    "/ had to extract the getPixel call into a separate method, to specify
    "/ unlimitedStack (some implementations use alloca and require huge amounts
    "/ of temporary stack space

    (self primGetBitsFrom:aDrawableId x:srcx y:srcy width:w height:h into:imageBits infoInto:rawInfo) ifTrue:[
	info := IdentityDictionary new.
	info at:#bitOrder put:(rawInfo at:1).
	info at:#depth put:(rawInfo at:2).
	info at:#bytesPerLine put:(rawInfo at:3).
	info at:#byteOrder put:(rawInfo at:4).
	info at:#format put:(rawInfo at:5).
	info at:#bitmapUnit put:(rawInfo at:6).
	info at:#bitmapPad put:(rawInfo at:7).
	info at:#bitsPerPixel put:(rawInfo at:8).
	^ info
    ].
    "
     some error occured - either args are not smallintegers, imageBits is not a ByteArray
     or is too small to hold the bits
    "
    ^ self primitiveFailed
!

getBitsFromPixmapId:aDrawableId x:srcx y:srcy width:w height:h into:imageBits
    "get bits from a drawable into the imageBits. The storage for the bits
     must be big enough for the data to fit. If ok, returns an array with some
     info and the bits in imageBits. The info contains the depth, bitOrder and
     number of bytes per scanline. The number of bytes per scanline is not known
     in advance, since the X-server is free to return whatever it thinks is a good padding."

    |rawInfo info|

    ((w <= 0) or:[h <= 0]) ifTrue:[
	self primitiveFailed.
	^ nil
    ].

    rawInfo := Array new:8.
		  "1 -> bit order"
		  "2 -> depth"
		  "3 -> bytes_per_line"
		  "4 -> byte_order"
		  "5 -> format"
		  "6 -> bitmap_unit"
		  "7 -> bitmap_pad"
		  "8 -> bits_per_pixel"

    "/ had to extract the getPixel call into a separate method, to specify
    "/ unlimitedStack (some implementations use alloca and require huge amounts
    "/ of temporary stack space

    (self primGetBitsFromPixmap:aDrawableId x:srcx y:srcy width:w height:h into:imageBits infoInto:rawInfo) ifTrue:[
	info := IdentityDictionary new.
	info at:#bitOrder put:(rawInfo at:1).
	info at:#depth put:(rawInfo at:2).
	info at:#bytesPerLine put:(rawInfo at:3).
	info at:#byteOrder put:(rawInfo at:4).
	info at:#format put:(rawInfo at:5).
	info at:#bitmapUnit put:(rawInfo at:6).
	info at:#bitmapPad put:(rawInfo at:7).
	info at:#bitsPerPixel put:(rawInfo at:8).
	^ info
    ].
    "
     some error occured - either args are not smallintegers, imageBits is not a ByteArray
     or is too small to hold the bits
    "
    ^ self primitiveFailed
!

getPixelX:px y:py from:aDrawableId with:aGCId
    "return the pixel value at x/y; coordinates start at 0/0 for the upper left.
     Nil is returned for invalid coordinates or if any other problem arises."

%{  /* NOCONTEXT */
    if (__isExternalAddress(aGCId)
     && __bothSmallInteger(px, py)) {
	struct gcData *gcData = _GCDATA(aGCId);
	HDC hDC;
	int __x = __intVal(px), __y = __intVal(py);
	int pixel;

	hDC = _getDC(gcData);
	pixel = GetPixel(hDC, __x, __y);
#ifndef CACHE_LAST_DC
	_releaseDC(gcData);
#endif
	/*
	 * for compatibility, return the pixelValue
	 * from a monochrome bitmap
	 */
	if (gcData->hBitmap) {
	    if (gcData->bitmapColorBitCount == 1) {
		pixel = (pixel == 0) ? 0 : 1;
	    }
	}

	RETURN ( __MKSMALLINT(pixel & 0xFFFFFF) );
    }
%}.
    ^ nil
!

primGetBitsFrom:aDrawableId x:srcX y:srcY width:w height:h into:imageBits infoInto:info

%{  
    int     height, width;
    int     numBytes;
    HWND    hWnd;
    HBITMAP hBitmap = 0;
    HDC     bDC = 0;
    struct {
	BITMAPINFOHEADER bmiHeader;
	DWORD r;
	DWORD g;
	DWORD b;
    } bitmap;

    if (__isExternalAddress(aDrawableId)
     && __bothSmallInteger(srcX, srcY)
     && __bothSmallInteger(w, h)
     && __isArray(info)
     && __isByteArray(imageBits))
    {
	hWnd = _HWNDVal( aDrawableId );
	DPRINTF(("primGetBits %x\n",hWnd));
	if( hWnd != 0 ) {
	    HDC wDC;
	    HANDLE prevBitmap;

	    bDC = CreateCompatibleDC(__rootDC);

	    DPRINTF(("primGetBits srcX %d srcY %d w %d h %d\n",__intVal(srcX),__intVal(srcY),__intVal(w),__intVal(h)));
	    height =  __intVal(h);
	    width  =  __intVal(w);
	    DPRINTF(("width %d height %d\n",width,height));
	    hBitmap = CreateCompatibleBitmap(__rootDC,(width + 3 )/ 4 * 4 /*width*/,height);
	    if (!hBitmap)
		goto fail;

	    bitmap.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	    bitmap.bmiHeader.biPlanes = 1;
#ifdef ALWAYSTRUECOLOR
	    bitmap.bmiHeader.biCompression = BI_RGB;
#else
	    if (__depth == 24) {
		bitmap.bmiHeader.biCompression = BI_RGB;
	    } else if (__depth == 16) {
# if 0
		bitmap.bmiHeader.biCompression = BI_BITFIELDS;
		bitmap.b = 0x001f;
		bitmap.g = 0x07e0;
		bitmap.r = 0xf800;
# else
		bitmap.b = 0;
		bitmap.g = 0;
		bitmap.r = 0;
		bitmap.bmiHeader.biCompression = BI_RGB;
# endif
	    }
#endif /* ALWAYSTRUECOLOR */
	    bitmap.bmiHeader.biSizeImage = 0;
	    bitmap.bmiHeader.biXPelsPerMeter = 0;
	    bitmap.bmiHeader.biYPelsPerMeter = 0;
	    bitmap.bmiHeader.biClrUsed = 0;
	    bitmap.bmiHeader.biClrImportant = 0;
	    bitmap.bmiHeader.biBitCount = __depth;

	    bitmap.bmiHeader.biWidth = (width + 3 )/ 4 * 4; // width;
	    bitmap.bmiHeader.biHeight = -height;

	    wDC = GetDC(hWnd);

	    SelectObject(bDC, hBitmap);
	    if (BitBlt(bDC,
		   0,0,
		   width,height,
		   wDC,
		   __intVal(srcX), __intVal(srcY),
		   SRCCOPY)
		 == 0
		)
	    {
		INFOPRINT((stderr, "WinWorkstation [warning]: in primGetBitsFrom: BitBlt\n"));
	    }

#ifdef CACHE_LAST_DC
	    if (lastGcData && (lastGcData->_hDC == wDC)) {
# ifdef DEBUG_DC_REUSE
		fprintf(stderr, "WinWorkstation [info]: Oops - dont release - cachedDC reuse\n", __LINE__);
# endif
	    } else
#endif
#ifdef CACHE_LAST_WM_PAINT_DC
	    if (last_wm_paint_dc && (last_wm_paint_dc == wDC)) {
# ifdef DEBUG_DC_REUSE
		fprintf(stderr, "WinWorkstation [info]: Oops - dont release - last_wm_paint_dc reuse\n", __LINE__);
# endif
	    } else
#endif
	    ReleaseDC(hWnd, wDC);

	    if (GetDIBits(bDC,hBitmap,0,height,0,(struct tagBITMAPINFO *)&bitmap,DIB_RGB_COLORS) == 0)
	    {
		INFOPRINT((stderr, "WinWorkstation [warning]: noinfo returned in primGetBits\n"));
		goto fail;
	    }
	    DPRINTF(("bitmap info:%d %d %d %d\n",bitmap.bmiHeader.biWidth,bitmap.bmiHeader.biHeight,bitmap.bmiHeader.biBitCount,bitmap.bmiHeader.biSizeImage));
	    numBytes = bitmap.bmiHeader.biSizeImage;
	    if( numBytes != 0 ) {
		char *cp = __ByteArrayInstPtr(imageBits)->ba_element;
		int n = numBytes;

		if (numBytes > __byteArraySize(imageBits)) {
		    /* imageBits too small */
		    INFOPRINT((stderr, "WinWorkstation [warning]: primGetBits - provided byteArray too small\n"));
		    goto fail;
		}
		DPRINTF(("numBytes %d\n",numBytes));

		bitmap.bmiHeader.biHeight = -height;
		if (GetDIBits(bDC,hBitmap,0,height,__ByteArrayInstPtr(imageBits)->ba_element,(struct tagBITMAPINFO *)&bitmap,DIB_RGB_COLORS) == 0)
		{
		    INFOPRINT((stderr, "WinWorkstation [warning]: zero bits returned in primGetBits\n"));
		    goto fail;
		}

		/* swap red and blue (windows delivers BGR) */
		for ( ;n > 0; n -= 3, cp += 3) {
		      char b = cp[0];
		      cp[0] = cp[2];
		      cp[2] = b;
		}
	    } else {
		INFOPRINT((stderr, "WinWorkstation [warning]: unacceptable bitmap in primGetBits\n"));
		goto fail;
	    }
	} else {
	    INFOPRINT((stderr, "WinWorkstation [warning]: unacceptable bitmap in primGetBits\n"));
	    goto fail;
	}

	__ArrayInstPtr(info)->a_element[0] = @symbol(msbFirst);
	__ArrayInstPtr(info)->a_element[1] = __MKSMALLINT(1);
	__ArrayInstPtr(info)->a_element[2] = __MKSMALLINT(numBytes/height); //__MKSMALLINT((width * 3 + 3 )/ 4 * 4);
	__ArrayInstPtr(info)->a_element[3] = @symbol(msbFirst);
	__ArrayInstPtr(info)->a_element[4] = @symbol(XYPixmap);
	__ArrayInstPtr(info)->a_element[5] = __MKSMALLINT(0);
	__ArrayInstPtr(info)->a_element[6] = __MKSMALLINT(WIN32PADDING);
	__ArrayInstPtr(info)->a_element[7] = __MKSMALLINT(bitmap.bmiHeader.biBitCount);
	if (bDC)
	    DeleteDC(bDC);
	if (hBitmap)
	    _DeleteObject(hBitmap, __LINE__);
	RETURN ( true );
    }
fail: ;
    if (bDC)
	DeleteDC(bDC);
    if (hBitmap)
	_DeleteObject(hBitmap, __LINE__);
%}.
    ^ false
!

primGetBitsFromPixmap:aDrawableId x:srcX y:srcY width:w height:h into:imageBits infoInto:info

%{  
    int            height, width;
    int            numBytes;
    unsigned char* ep = 0;
    HBITMAP        xBitmap;
    HBITMAP hBitmap = 0;
    HDC bDC = 0;
    HDC xDC = 0;
    struct
    {
	BITMAPINFOHEADER bmiHeader;
	DWORD rgb[2];
    } bitmap;
    BITMAP bitmapInfo;

    if (__isExternalAddress(aDrawableId)
     && __bothSmallInteger(srcX, srcY)
     && __bothSmallInteger(w, h)
     && __isArray(info)
     && __isByteArray(imageBits))
    {
	xBitmap = _HBITMAPVAL( aDrawableId );
	DPRINTF(("primGetBitsFromPixmap %x\n",xBitmap));
	if (xBitmap != 0) {
	    xDC = GetDC(0);
	    SelectObject(xDC, xBitmap);
	    GetObject(xBitmap,sizeof(bitmapInfo),&bitmapInfo);

	    bDC = CreateCompatibleDC(__rootDC);
	    DPRINTF(("srcX %d srcY %d w %d h %d\n",__intVal(srcX),__intVal(srcY),__intVal(w),__intVal(h)));
	    height =  __intVal(h);
	    width  =  __intVal(w);
	    DPRINTF(("width %d height %d\n",width,height));

	    hBitmap = CreateCompatibleBitmap(xDC,width,height);
	    if (!hBitmap)
		goto fail;

	    SelectObject(bDC,hBitmap);
	    if (BitBlt(bDC,
		   0,0,
		   width,height,
		   xDC,
		   __intVal(srcX), __intVal(srcY),
		   SRCCOPY)
		 == 0
		)
	    {
		 DPRINTF(("ERROR in primGetBitsFromPixmap: BitBlt\n"));
		 goto fail;
	    }
	    bitmap.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	    bitmap.bmiHeader.biPlanes = 1;
#ifdef ALWAYSTRUECOLOR
	    bitmap.bmiHeader.biCompression = BI_RGB;
#else
	    if (__depth == 24) {
		/*bitmap.bmiHeader.biCompression = BI_BITFIELDS;
		bitmap.r = 0xff0000;
		bitmap.g = 0x00ff00;
		bitmap.b = 0x0000ff;*/
		bitmap.bmiHeader.biCompression = BI_RGB;
	    } else if (__depth == 16) {
		/*bitmap.bmiHeader.biCompression = BI_RGB;
		bitmap.bmiHeader.biCompression = BI_BITFIELDS;
		bitmap.b = 0x001f;
		bitmap.g = 0x07e0;
		bitmap.r = 0xf800;*/
		bitmap.bmiHeader.biCompression = BI_RGB;
	    } else {
		DPRINTF(("primGetBitsFromPixmap: unsupported depth\n"));
		got fail;
	    }
#endif /* ALWAYSTRUECOLOR */
	    bitmap.bmiHeader.biSizeImage = 0;
	    bitmap.bmiHeader.biXPelsPerMeter = 0;
	    bitmap.bmiHeader.biYPelsPerMeter = 0;
	    bitmap.bmiHeader.biClrUsed = 0;
	    bitmap.bmiHeader.biClrImportant = 0;

	    bitmap.bmiHeader.biWidth = width;
	    bitmap.bmiHeader.biHeight = -height;
	    bitmap.bmiHeader.biBitCount = bitmapInfo.bmBitsPixel;

	    if (GetDIBits(bDC,hBitmap,0,height,0,(struct tagBITMAPINFO *)&bitmap,DIB_RGB_COLORS) == 0)
	    {
		DPRINTF(("noinfo returned\n"));
		goto fail;
	    }
	    DPRINTF(("bitmap info:%d %d %d %d\n",bitmap.bmiHeader.biWidth,bitmap.bmiHeader.biHeight,bitmap.bmiHeader.biBitCount,bitmap.bmiHeader.biSizeImage));
	    numBytes = bitmap.bmiHeader.biSizeImage;
	    if ( numBytes != 0 ) {
		if (numBytes > __byteArraySize(imageBits)) {
		    /* imageBits too small */
		    DPRINTF(("provided byteArray too small\n"));
		    goto fail;
		}
		DPRINTF(("numBytes %d\n",numBytes));

		bitmap.bmiHeader.biHeight = -height;
		bitmap.bmiHeader.biBitCount = bitmapInfo.bmBitsPixel; /*__depth;*/
		bitmap.rgb[0] = 0;
		bitmap.rgb[1] = 0xffffff;
		if (GetDIBits(xDC,xBitmap,0,height,__ByteArrayInstPtr(imageBits)->ba_element,(struct tagBITMAPINFO *)&bitmap,DIB_RGB_COLORS) == 0)
		{
		    DPRINTF(("zero bits returned\n"));
		    goto fail;
		}
	    } else {
		DPRINTF(("unacceptable bitmap\n"));
		goto fail;
	    }
	} else {
	    DPRINTF(("unacceptable bitmap\n"));
	    goto fail;
	}
	__ArrayInstPtr(info)->a_element[0] = @symbol(msbFirst);
	__ArrayInstPtr(info)->a_element[1] = __MKSMALLINT(1);
	__ArrayInstPtr(info)->a_element[2] = __MKSMALLINT(numBytes/height);
	__ArrayInstPtr(info)->a_element[3] = @symbol(msbFirst);
	__ArrayInstPtr(info)->a_element[4] = (bitmap.bmiHeader.biBitCount == 1) ? @symbol(ZPixmap) : @symbol(XYPixmap);
	__ArrayInstPtr(info)->a_element[5] = __MKSMALLINT(0);
	__ArrayInstPtr(info)->a_element[6] = __MKSMALLINT(WIN32PADDING);
	__ArrayInstPtr(info)->a_element[7] = __MKSMALLINT(bitmap.bmiHeader.biBitCount);
	if (bDC)
	    DeleteDC(bDC);
	if (xDC)
	    ReleaseDC(0, xDC); //DeleteDC(xDC);
	if (hBitmap)
	    _DeleteObject(hBitmap, __LINE__);
	RETURN ( true );
    }
fail:
    if (bDC)
	DeleteDC(bDC);
    if (xDC)
	ReleaseDC(0, xDC); //DeleteDC(xDC);
    if (hBitmap)
	_DeleteObject(hBitmap, __LINE__);

%}.
    ^ false
! !

!WinWorkstation methodsFor:'selections'!

getClipboardData
    "caveat: for now, only Text is supported"

%{
    HANDLE hClip;
    HANDLE hData;

    /* check for format CF_TEXT */
    if (IsClipboardFormatAvailable(CF_TEXT)) {
        OBJ s;
        unsigned char *src, *p;
        int n, len, realLen;

	hClip = OpenClipboard(NULL);
	if (hClip) {
	    hData = GetClipboardData(CF_TEXT);
	    src = GlobalLock(hData);
	    len = src ? strlen(src) : 0;

	    /* see how much we really need (when CRLF is replaced by LF) */
	    for (realLen = 0, n=len, p=src; n; ) {
		unsigned char ch;

		ch = *p++; n--; realLen++;
		if (ch == 0x0D) {
		    if (n && (*p == 0x0A)) {
			p++; n--;
		    }
		}
	    }
	    s = __MKEMPTYSTRING(realLen);
	    for (n=0, p=__stringVal(s); len; ) {
		unsigned char ch;

		*p = ch = *src++;
		len--;
		if (len && (ch == 0x0D) && (*src == 0x0A)) {
		    *p = 0x0A;
		    src++;
		    len--;
		}
		p++;
	    }
	    GlobalUnlock(hData);
	    CloseClipboard();
	    DDPRINTF((stderr, "WinWorkstation [info]: clipBoard data is <%s>\n", (char *)hData));
	    RETURN(s);
	}
    }

    if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
        OBJ s;
        WIDECHAR *w_src, *w_p;
        int n, len, realLen;

	hClip = OpenClipboard(NULL);
	if (hClip) {
	    hData = GetClipboardData(CF_UNICODETEXT);
	    w_src = GlobalLock(hData);
	    len = w_src ? wcslen(w_src) : 0;

	    /* see how much we really need (when CRLF is replaced by LF) */
	    for (realLen = 0, n=len, w_p=w_src; n; ) {
		WIDECHAR w_ch;

		w_ch = *w_p++; n--; realLen++;
		if (w_ch == 0x0D) {
		    if (n && (*w_p == 0x0A)) {
			w_p++; n--;
		    }
		}
	    }
	    s = __MKBYTEARRAY(realLen * 2);
	    for (n=0, w_p=__stringVal(s); len; ) {
		WIDECHAR w_ch;

		*w_p = w_ch = *w_src++;
		len--;
		if (len && (w_ch == 0x0D) && (*w_src == 0x0A)) {
		    *w_p = 0x0A;
		    w_src++;
		    len--;
		}
		w_p++;
	    }
	    GlobalUnlock(hData);
	    CloseClipboard();
	    __qClass(s) = @global(UnicodeString);
	    RETURN(s);
	}
    }
    RETURN(nil);
%}
!

getCopyBuffer
    "return the copyBuffers contents."

    |clip|

    ClipBoardObject notNil ifTrue:[
	^ ClipBoardObject
    ].

    (clip := (self getSelectionFor:nil)) notNil ifTrue:[
	^ clip
    ].
    ^ copyBuffer

    "Created: / 13.7.1999 / 13:15:35 / cg"
    "Modified: / 30.1.2000 / 12:01:13 / cg"
!

getSelectionFor:drawableId
    |s lines|

    s := self getClipboardData.
^ s.

    (s isString and:[s includes:Character cr]) ifTrue:[
	"/ must make it a string-collection
	lines := s asStringCollection.
"/        (s endsWith:Character cr) ifTrue:[
"/            lines := lines copyWith: ''
"/        ].
	^ lines
    ].
    ^ s

    "Modified: / 30.1.2000 / 02:26:47 / cg"
!

setClipboardData:aString  
    "caveat: for now, only Text is supported"

%{
    HANDLE hClip;
    HANDLE hData;

    if (__isString(aString)) {
        char *s, *p, *src, *dst;
        int n, len, realLen;

	hClip = OpenClipboard(NULL);
	if (hClip) {
	    s = __stringVal(aString);
	    /* must replace CR by CRLF */
	    len = realLen = __stringSize(aString);
	    for (src=s, n=len; n; n--) {
		if ((src[0] == 0x0A) && ((src == s) || (src[-1] != 0x0D))) {
		    realLen++;
		}
		src++;
	    }

	    hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, realLen+1);
	    if (hData) {
		char *t = GlobalLock(hData);

		if (t) {
		    for (src=s, dst=t, n=len; n;) {
			unsigned char ch;

			ch = src[0]; n--;
			if ((ch == 0x0A) && ((src == s) || (src[-1] != 0x0D))) {
			    *dst++ = 0x0D;
			    *dst++ = 0x0A;
			} else {
			    *dst++ = ch;
			}
			src++;
		    }
		    *dst = 0;

		    EmptyClipboard();
		    if (SetClipboardData(CF_TEXT, hData) != 0) {
			/* Note: After setting clipboard data, 
			 * the memory block previously allocated belongs to
			 * the clipboard - not to the app. 
			 */
			CloseClipboard();
			RETURN (true);
		    }
		    DPRINTF(("SetClipboardData error:%d\n", GetLastError()));
		    GlobalUnlock(hData);
		}
	    }
	    CloseClipboard();
	}
	RETURN(false);
    }

    /* Unicode */
    if (__isWords(aString)) {
        WIDECHAR *w_s, *w_p, *w_src, *w_dst;
        int n, len, realLen;

	hClip = OpenClipboard(NULL);
	if (hClip) {
	    OBJ cls;
	    int nInstBytes;

	    cls = __qClass(aString);
	    nInstBytes = __OBJS2BYTES__(__intVal(__ClassInstPtr(cls)->c_ninstvars));
	    w_s = (WIDECHAR *)(__stringVal(aString) + nInstBytes);

	    /* must replace CR by CRLF */
	    len = realLen = (__byteArraySize(aString) - nInstBytes) / 2;
	    for (w_src=w_s, n=len; n; n--) {
		if ((w_src[0] == 0x0A) && ((w_src == w_s) || (w_src[-1] != 0x0D))) {
		    realLen++;
		}
		w_src++;
	    }

	    hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, (realLen+1)*sizeof(WIDECHAR));
	    if (hData) {
		char *t = GlobalLock(hData);

		if (t) {
		    for (w_src=w_s, w_dst=t, n=len; n;) {
			WIDECHAR w_ch;

			w_ch = w_src[0]; n--;
			if ((w_ch == 0x0A) && ((w_src == w_s) || (w_src[-1] != 0x0D))) {
			    *w_dst++ = 0x0D;
			    *w_dst++ = 0x0A;
			} else {
			    *w_dst++ = w_ch;
			}
			w_src++;
		    }
		    *w_dst = 0;

		    EmptyClipboard();
		    if (SetClipboardData(CF_UNICODETEXT, hData) != 0) {
			/* Note: After setting clipboard data, 
			 * the memory block previously allocated belongs to
			 * the clipboard - not to the app. 
			 */
			CloseClipboard();
			RETURN (true);
		    }
		    DPRINTF(("SetClipboardData error:%d\n", GetLastError()));
		    GlobalUnlock(hData);
		}
	    }
	    CloseClipboard();
	}
	RETURN(false);
    }
%}.
    self primitiveFailed.
!

setSelection:something owner:drawableId
    something isString ifTrue:[
	ClipBoardObject := nil.
	^ self setClipboardData:something
    ] ifFalse:[
	ClipBoardObject := something.
    ]

    "Created: / 13.7.1999 / 13:30:37 / cg"
    "Modified: / 30.1.2000 / 11:59:41 / cg"
!

setTextSelection:something owner:drawableId
    ClipBoardObject := nil.
    ^ self setClipboardData:something

    "Created: / 13.7.1999 / 13:36:43 / cg"
    "Modified: / 30.1.2000 / 12:12:57 / cg"
! !

!WinWorkstation methodsFor:'style defaults'!

defaultStyleValueFor:aKey
    "return a default style value, given a key.
     These defaults are used if nothing is specified
     in the style sheet
     This allows for empty values in style sheets, and defaults
     being provided by the display (which makes sense with Windows,
     where the systemDefaults are used ..."

    |clr|

"/    aKey == #shadowColor ifTrue:[
"/        ^ Color black
"/    ].
"/    aKey == #lightColor ifTrue:[
"/        ^ Color white
"/    ].

    aKey == #viewBackgroundColor ifTrue:[
	clr := self getSystemColor:#COLOR_WINDOW.
    ].
    aKey == #textForegroundColor ifTrue:[
	clr := self getSystemColor:#COLOR_WINDOWTEXT
    ].


"/    aKey == #scrollerViewBackgroundColor ifTrue:[
"/        ^ Color white
"/    ].

"/    aKey == #textBackgroundColor ifTrue:[
"/        ^ Color white.
"/    ].

    clr notNil ifTrue:[^ clr].
    ^ super defaultStyleValueFor:aKey
! !

!WinWorkstation methodsFor:'window stuff'!

activateWindow:aWindowId
    "make a window active"

%{  /* NOCONTEXT */
    if (__isExternalAddress(aWindowId)) {
	HWND win = _HWNDVal(aWindowId);

	if (win) {
	    CPRINTF(("activateWindow %x\n",win));

#if 0
/* does not help the low-licenceBox bug ... */
	    SetWindowPos(win, HWND_TOP, 0, 0, 0, 0, 
			 SWP_NOSENDCHANGING |
			 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
#endif

	    ShowWindowAsync(win, SW_SHOWNORMAL);

//            ShowWindow(win, SW_SHOW);
//            SetWindowPos(win, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW|/*SWP_NOACTIVATE|*/SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER);
//            enqEvent(ENQ_AT_END, 0,win, WM_SHOWWINDOW, TRUE, 0, 0, 0, 0, EV_NOTIME);
	}
	RETURN ( self );
    }
%}
!

configureWindow:aWindowId sibling:siblingId stackMode:modeSymbol
    "configure stacking operation of aWindowId w.r.t siblingId"

!

dropFilesWindow:aWindowId accept:doAccept
    "window accept Files from Shell"

%{  /*  */

    if (__isExternalAddress(aWindowId)) {
	HWND hWnd = _HWNDVal(aWindowId);

	if (hWnd) {
	    if (doAccept == true)
	    DragAcceptFiles(hWnd, (doAccept == true) ? TRUE : FALSE);
	}
	RETURN ( self );
    }
%}
!

lowerWindow:aWindowId
    "bring a window to back"

%{  /* NOCONTEXT */

    if (__isExternalAddress(aWindowId)) {
	HWND win = _HWNDVal(aWindowId);

	if (win) {
	    CPRINTF(("lowerWindow %x\n",win));
	    SetWindowPos(win, HWND_BOTTOM, 0, 0, 0, 0, 
			 SWP_NOSENDCHANGING |
			 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
	}
	RETURN ( self );
    }
%}
!

mapView:aView id:aWindowId iconified:aBoolean atX:x y:y width:w height:h minExtent:minExt maxExtent:maxExt
    "make a window visible - either as icon or as a real view 
     in addition, allow change of extend, position, minExtend and maxExtent.
     Needed for restart, to allow recreating a view as iconified,
     and to collaps/expand windows."

    |minW minH maxW maxH|

    minExt notNil ifTrue:[
	minW := minExt x.
	minH := minExt y.
    ].
    maxExt notNil ifTrue:[
	maxW := maxExt x.
	maxH := maxExt y.
    ].

%{ 
    if (__isExternalAddress(aWindowId)) {
	HWND win = _HWNDVal(aWindowId);

	if (win) {
	    int bw;
	    int flag = 0;
	    int __left, __top, __width, __height;
	    int winStyleBits;
	    int winExStyleBits;
	    localWindowInfo *lI;

	    bw = GetWindow_bw(win);
	    winStyleBits = GetWindowLong(win, GWL_STYLE);
	    winExStyleBits = GetWindowLong(win, GWL_EXSTYLE);
	    lI = GETLOCALWINDOWINFOPTR(win);
	    if (! lI) {
		fprintf(stderr, "WinWorkstation [info]: oops - no localInfo in mapView\n");
		RETURN (self);
	    }

	    if (__bothSmallInteger(x, y)) {
		__left = __intVal(x) + bw;
		__top = __intVal(y) + bw;
	    } else {
		__left = __top = 0;
		flag |= SWP_NOMOVE;
	    }
	    if (__bothSmallInteger(w, h)) {
		__width = __intVal(w) - bw - bw;
		__height = __intVal(h) - bw - bw;
	    } else {
		__width = __height = 100;
		flag |= SWP_NOSIZE;
	    }

	    if (__bothSmallInteger(minW, minH)) {
		RECT rec;

		rec.left = 0;
		rec.top = 0;
		rec.right = __intVal(minW);
		rec.bottom = __intVal(minH);
#ifdef ADJUSTWINDOW
		AdjustWindowRectEx(&rec, winStyleBits, 0, winExStyleBits);
#endif
		lI->minWidth = rec.right - rec.left;
		lI->minHeight = rec.bottom - rec.top;
	    }
	    if (__bothSmallInteger(maxW, maxH)) {
		RECT rec;

		rec.left = 0;
		rec.top = 0;
		rec.right = __intVal(maxW);
		rec.bottom = __intVal(maxH);
#ifdef ADJUSTWINDOW
		AdjustWindowRectEx(&rec, winStyleBits, 0, winExStyleBits);
#endif
		lI->maxWidth = rec.right - rec.left;
		lI->maxHeight = rec.bottom - rec.top;
	    }

#ifdef ADJUSTWINDOW
	    if (flag != (SWP_NOMOVE | SWP_NOSIZE)) {
		RECT rec;
		int __left0, __top0, __width0, __height0;

		__left0   = __left;
		__top0    = __top;
		__width0  = __width;
		__height0 = __height;

		rec.left = __left;
		rec.top = __top;
		rec.right = __left + __width;
		rec.bottom = __top + __height;
		AdjustWindowRectEx(&rec, winStyleBits, 0, winExStyleBits);

		__left = rec.left;
		__top = rec.top;
		__width = rec.right - rec.left;
		__height = rec.bottom - rec.top;

		CPRINTF(("mapView %x %d/%d %d/%d -> %d/%d %d/%d\n",
			 win, 
			 __left0, __top0, __width0, __height0,
			 __left, __top, __width, __height));
	    }
#else
	    CPRINTF(("mapView %x %d/%d %d/%d\n",
		     win, 
		     __left, __top, __width, __height));
#endif                              

	    flag |= SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_NOZORDER | SWP_NOOWNERZORDER;

	    if (aBoolean == true) {
		/* iconified */
//              SetWindowPos(win, (HWND)0,
//                           __left, __top, __width, __height,
//                           flag);
//                ShowWindow(win, SW_SHOWMINIMIZED);
		ShowWindowAsync(win, SW_SHOWMINNOACTIVE);
/*
		enqEvent(ENQ_AT_END, 0, win, WM_SHOWWINDOW, FALSE, 0, 0, 0, 0, EV_NOTIME);
*/
	    } else {
		/* normal */
		SetWindowPos(win, (HWND)0,
			     __left, __top, __width, __height,
			     flag);
/*
		enqEvent(ENQ_AT_END, 0, win, WM_SHOWWINDOW, TRUE, 0, 0, 0, 0, EV_NOTIME);
*/
		//ShowWindow(win, SW_RESTORE);
		// ShowWindowAsync(win, SW_RESTORE);
		ShowWindowAsync(win, SW_SHOWNOACTIVATE);
	    }
	}
	RETURN ( self );
    }
%}
!

mapWindow:aWindowId
    "make a window visible"

%{  /* NOCONTEXT */

    if (__isExternalAddress(aWindowId)) {
	HWND hWnd = _HWNDVal(aWindowId);

	if (hWnd) {
	    CPRINTF(("mapWindow %x\n", hWnd));
	    if (GetParent(hWnd) == 0) {
		ShowWindowAsync(hWnd, SW_SHOWNOACTIVATE);
	    } else {
		ShowWindowAsync(hWnd, SW_SHOWNORMAL);
	    }
	}
	RETURN ( self );
    }
%}
!

moveResizeWindow:aWindowId x:x y:y width:w height:h
    "move and resize a window"

%{  /* NOCONTEXT */
    RECT rec;
    if (__isExternalAddress(aWindowId)
     && __bothSmallInteger(x, y)
     && __bothSmallInteger(w, h)) {
	HWND win = _HWNDVal(aWindowId);
	int bw;

	if (win) {
	    bw = GetWindow_bw(win);

	    rec.left = __intVal(x) + bw;
	    rec.top = __intVal(y) + bw;
	    rec.right = rec.left + __intVal(w) - bw - bw;
	    rec.bottom = rec.top + __intVal(h) - bw - bw;
	    if (GetParent(win) == 0) {
#ifdef ADJUSTWINDOW
		AdjustWindowRectEx(&rec,GetWindowLong(win,GWL_STYLE),0,GetWindowLong(win,GWL_EXSTYLE));
#endif
	    }
	    CPRINTF(("moveresize %x x:%d y:%d w:%d h:%d -> x:%d y:%d w:%d h:%d\n",win,__intVal(x),__intVal(y),__intVal(w),__intVal(h),rec.left,rec.top,
			 rec.right - rec.left, rec.bottom - rec.top));
	    SetWindowPos(win, (HWND)0,
			 rec.left, rec.top,
			 rec.right - rec.left, rec.bottom - rec.top,
			 SWP_NOSENDCHANGING | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER);
	}
	RETURN ( self );
    }
%}
!

moveWindow:aWindowId x:x y:y
    "move a window"

%{  /* NOCONTEXT */
    RECT rec;

    if (__isExternalAddress(aWindowId)
     && __bothSmallInteger(x, y)) {
	HWND win = _HWNDVal(aWindowId);
	int bw;

	if (win) {
	    bw = GetWindow_bw(win);

	    rec.left = __intVal(x) + bw;
	    rec.top = __intVal(y) + bw;
	    if (GetParent(win) == 0) {
		rec.right = rec.left + 10;  /* only needed to give adjust some valid numbers */
		rec.bottom = rec.top + 10;
#ifdef ADJUSTWINDOW
		AdjustWindowRectEx(&rec, GetWindowLong(win,GWL_STYLE), 0, GetWindowLong(win,GWL_EXSTYLE));
#endif
	    }
	    CPRINTF(("move %x x:%d y:%d -> x:%d y:%d\n", win,
			__intVal(x), __intVal(y), rec.left, rec.top));
	    SetWindowPos(win, (HWND)0,
			 rec.left, rec.top,
			 0, 0,
			 SWP_NOSENDCHANGING | SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
	}
	RETURN ( self );
    }
%}
!

primSetWindowIconId:wiconId maskId:wmaskId width:wiconWidth height:wiconHeight in:aWindowId
%{  

    HBITMAP        xBitMap, maskBitmap;
    ICONINFO       iconInfo;
    int            height, width;
    HICON          xIcon = (HICON)0;
    unsigned char  fastBits[10000];

    DPRINTF(("primSetWindowIconId\n"));
    if (__isExternalAddress(aWindowId)
     && __isExternalAddress(wiconId)
     && __bothSmallInteger(wiconWidth, wiconHeight)) {
	HWND win = _HWNDVal(aWindowId);

	xBitMap = _HBITMAPVAL( wiconId );
	if ( xBitMap != 0 ) {
	    BITMAP bm;

	    if (GetObject(xBitMap, sizeof(bm), (LPSTR)&bm)) {
		BYTE *ep;
		int privateMask = 0;
		unsigned char  *allocatedBits = 0;

		DPRINTF(("Bitmap w:%d h:%d p:%d d:%d\n",bm.bmWidth,bm.bmHeight,bm.bmPlanes,bm.bmBitsPixel));
		height = __intVal( wiconHeight );
		width  = __intVal( wiconWidth  );

		if ( __isExternalAddress(wmaskId) ) {
		    maskBitmap = _HBITMAPVAL( wmaskId );
		} else {
		    if (wmaskId != nil) {
			PRINTF(("WinWorkstat [warning]: wmaskId is not an external address\n"));
		    }
		    maskBitmap = 0;
		}

		/*
		 * if there is no mask, generate one
		 */
		if (maskBitmap == 0) {
		    int nBytes;

		    nBytes = ( width + 31 ) / 8;
		    nBytes = height * nBytes;
		    if (nBytes < sizeof(fastBits)) {
			ep = fastBits;
		    } else {
			ep = allocatedBits = (unsigned char *) malloc(nBytes);
		    }
		    if ( ep == 0 ) {
			PRINTF(( "WinWorkstat [warning]: malloc failed\n" ));
		    } else {
			memset(ep, 0, nBytes);
			maskBitmap = CreateBitmap(width, height, 1, 1, ep );
			if ( maskBitmap == NULL ) {
			    PRINTF(( "WinWorkstat [warning]: mask CREATION failed\n" ));
			} else {
			    privateMask = 1;
			}
		    }
		}
		if ( maskBitmap != NULL ) {
		    DPRINTF(( "BITMAP mask CREATED!!!\n" ));
		    iconInfo.fIcon = TRUE;
		    iconInfo.hbmMask  =maskBitmap;
		    iconInfo.hbmColor = xBitMap;
		    xIcon = CreateIconIndirect( &iconInfo );
		    DPRINTF(( "ICON CREATED!!!\n" ));
		    if (privateMask) {
			_DeleteObject( maskBitmap, __LINE__ );
		    } 
		} else {
		    PRINTF(( "WinWorkstat [warning]: no mask for icon !\n" ));
		}

		if (allocatedBits) {
		    free(allocatedBits);
		}
	    }
	} else {
	    DPRINTF(("WinWorkstat [warning]: xBitMap is zero \n" ));
	}

	if (xIcon) {
#ifndef TOPWINDOWCLASS
	    HICON oldIcon = (HICON)0;

	    oldIcon = (HICON) GetClassLong(win, GCL_HICON);
	    SetClassLong(win, GCL_HICON, (DWORD)xIcon);
/* It has to be checked whether this can be deleted!!!!! */
	    if ( oldIcon ) {
		if (DestroyIcon( oldIcon )) {
		    DPRINTF(( "Old icon deleted\n" ));
		} else {
		    fprintf(stderr, "failed to delete old icon\n");
		}
	    }
#else
	    SendMessage(win, WM_SETICON,ICON_SMALL, (LPARAM)xIcon);
	    SendMessage(win, WM_SETICON,ICON_BIG, (LPARAM)xIcon);
#endif
	}
	RETURN (self);
    }
%}
!

raiseWindow:aWindowId
    "bring a window to front"

%{  /* NOCONTEXT */

    if (__isExternalAddress(aWindowId)) {
	HWND hWnd = _HWNDVal(aWindowId);

	if (hWnd) {
	    CPRINTF(("raiseWindow %x\n",hWnd));
#if 1
	    SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, 
			 /* SWP_NOOWNERZORDER |*/ 
			 SWP_NOSENDCHANGING | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
#else
	    BringWindowToTop(hWnd);
#endif
	}
	RETURN ( self );
    }
%}
!

resizeWindow:aWindowId width:w height:h
    "resize a window"

%{  /* NOCONTEXT */
    RECT rec;

    if (__isExternalAddress(aWindowId)
     && __bothSmallInteger(w, h)) {
	HWND hWnd = _HWNDVal(aWindowId);
	int bw;

	if (hWnd) {
	    bw = GetWindow_bw(hWnd);

	    rec.left = 0;  /* only needed to have a valid origin in adjust */
	    rec.top = 0;   /* only needed to have a valid origin in adjust */
	    rec.right = __intVal(w) - bw - bw;
	    rec.bottom = __intVal(h) - bw - bw;
#ifdef ADJUSTWINDOW
	    AdjustWindowRectEx(&rec, GetWindowLong(hWnd, GWL_STYLE), 0, GetWindowLong(hWnd, GWL_EXSTYLE));
#endif
	    CPRINTF(("resize %x w:%d h:%d -> w:%d h:%d\n",hWnd,__intVal(w),__intVal(h),rec.right - rec.left, rec.bottom - rec.top));
	    SetWindowPos(hWnd, (HWND)0,
			 0, 0,
			 rec.right - rec.left, rec.bottom - rec.top,
			 SWP_NOSENDCHANGING | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER);
	}
	RETURN ( self );
    }
%}
!

setBackingStore:how in:aWindowId
    "turn on/off backing-store for a window"

    "/ 'WinWorkstation [info]: setBackingStore:in: not yet implemented' infoPrintCR.
!

setBitGravity:how in:aWindowId
    "set bit gravity for a window"

    "/ 'WinWorkstation [info]: setBitGravity:in: not yet implemented' infoPrintCR.
!

setCursor:aCursorId in:aWindowId
    "define a windows cursor
     Ignored here, since in windows, an application
     has to manage the cursor itself."

%{  /* NOCONTEXT */

    HCURSOR newCursor;

    if (ISCONNECTED
     && __isExternalAddress(aWindowId)
     && __isExternalAddress(aCursorId)) {
	HWND win = _HWNDVal(aWindowId);
	POINT p;

	/*printf("setCursor:in:\n");*/
	SetWindow_Cursor(win, _HCURSORVal(aCursorId));

	/*
	 * if the pointer is currently in that window ...
	 */
	GetCursorPos(&p);
	if (WindowFromPoint(p) == win) {
#ifdef SET_CURSOR_IN_WINTHREAD
	    PostMessage(win, WM_THREAD_SETCURSOR, 0, (INT)_HCURSORVal(aCursorId));
#else
	    SetCursor(_HCURSORVal(aCursorId));
#endif
	}
	RETURN ( self );
    }
%}
!

setIconName:aString in:aWindowId
    "define a windows iconname"

%{  /* NOCONTEXT */
#if 0
    if (__isExternalAddress(aWindowId)
     && (__isString(aString) || __isSymbol(aString))) {
	HWND win = _HWNDVal(aWindowId);

	SetWindowText(win, __stringVal(aString));
	RETURN (self);
    }
#endif
%}
!

setSaveUnder:yesOrNo in:aWindowId
    "turn on/off save-under for a window"

%{  /* NOCONTEXT */
    if (yesOrNo != false) {
	DPRINTF(("WinWorkstation [info]: setSaveUnder:in: not (yet) implemented\n"));
    }
%}.
!

setTransient:aWindowId for:aMainWindowId
    "set aWindowId to be a transient of aMainWindow.
     This will make the transient window to be iconified/deiconified
     together with the mainWindow.
     Typically, this is set for modal dialogs."

%{  /* NOCONTEXT */

#ifdef NOT_YET_SUPPORTED
    if (__isExternalAddress(aWindowId)) {
	HWND w;
	HWND cw = _HWNDVal(aWindowId);

	if ((aMainWindowId == nil) || (aMainWindowId == __MKSMALLINT(0))) {
	    w = (HWND) 0;
	} else {
	    if (__isExternalAddress(aMainWindowId)) {
		w = _HWNDVal(aMainWindowId);
	    } else {
		goto getOutOfHere;
	    }
	}
	CPRINTF(("setTransient:%x for:%x\n",cw,w));
	//SetParent(cw, w);
	RETURN ( self );
    }
 getOutOfHere: ;
#else
    RETURN (self);
#endif
%}.
    self primitiveFailed
!

setWindowBackground:aColorIndex in:aWindowId
    "set the windows background color. This is the color with which
     the view is filled whenever exposed. Do not confuse this with
     the background drawing color, which is used with opaque drawing."

%{  /* NOCONTEXT */

    if (__isExternalAddress(aWindowId)
     && (__isSmallInteger(aColorIndex))) {
	HWND hWnd = _HWNDVal(aWindowId);
	int clr;
	HBRUSH oldBrush;

	if (oldBrush = GetWindow_viewBgBrush(hWnd)) {
	    SetWindow_viewBgBrush(hWnd, 0);
	    _DeleteBrushIfNotInCache(oldBrush, __LINE__);
	}
	clr = st2RGB(__intVal(aColorIndex), 0);
	SetWindow_viewBgColor(hWnd, clr);

	RETURN (self);
    }
%}
!

setWindowBackgroundPixmap:aPixmapId in:aWindowId
    "set the windows background pattern to be a form.
     This is the pattern with which the view is filled whenever exposed.
     Do not confuse this with the background drawing color, which is used
     with opaque drawing.
     NOTICE: due to the limitations of WIN95, 
	     background pixmaps which are not 8x8 pixels in size, 
	     are handled by smalltalk code in the WIN95 version."

%{  
    if (__isExternalAddress(aWindowId)) {
	HWND hWnd = _HWNDVal(aWindowId);
	HBITMAP pixmap;
	HBRUSH oldBrush, newBrush;

	if (__isExternalAddress(aPixmapId))
	    pixmap = _HBITMAPVAL(aPixmapId);
	else
	    pixmap = 0;

	if (oldBrush = GetWindow_viewBgBrush(hWnd)) {
	    SetWindow_viewBgBrush(hWnd, 0);
	    _DeleteBrushIfNotInCache(oldBrush, __LINE__);
	}
	if (pixmap) {
	    newBrush = CreatePatternBrush(pixmap);
	    SetWindow_viewBgBrush(hWnd, newBrush);
/*
 *            SetBrushOrgEx(hDC, gcData->maskOrgX, gcData->maskOrgY, 0);       
 */
	}

	RETURN (self);
    }
%}.
!

setWindowBorderColor:aColorIndex in:aWindowId
    "set the windows border color.
     NOTICE: borders are not supported by the win32 version."

%{  /* NOCONTEXT */
    DPRINTF(("WinWorkstation [info]: setWindowBorderColor:in: not (yet) implemented\n"));

    if (__isExternalAddress(aWindowId)
     && __isSmallInteger(aColorIndex)) {
	HWND win = _HWNDVal(aWindowId);
	int clr;

	if (GetParent(win) != 0) {
	    clr = st2RGB(__intVal(aColorIndex), 0);
	    SetWindow_bdColor(win, clr);

	    {
		/*
		 * force a redraw - even if we do not support
		 * border colors ... (see UIPainter code)
		 */
		RECT rect;
		HRGN rgn;

		GetWindowRect(win, &rect);
		rgn = CreateRectRgn(rect.left, rect.top, rect.right, rect.bottom);
		DefWindowProc(win, WM_NCPAINT, (WPARAM)rgn, 0);
		_DeleteObject(rgn, __LINE__);
	    }
	}
    }
%}.
!

setWindowBorderPixmap:aPixmapId in:aWindowId
    "set the windows border pattern.
     NOTICE: border pixmaps are not supported by the win32 version."

%{  /* NOCONTEXT */
    DPRINTF(("WinWorkstation [info]: setWindowBorderPixmap:in: not (yet) implemented\n"));
%}.
!

setWindowBorderWidth:aNumber in:aWindowId
    "set the windows border width.
     NOTICE: borderWidths other than 1 are not supported by the win32 version."

%{  /* NOCONTEXT */
    int bw = (aNumber == __MKSMALLINT(0)) ? 0 : 1;

    if (__isExternalAddress(aWindowId)) {
	HWND win = _HWNDVal(aWindowId);
	int gwl;

	gwl = GetWindowLong(win, GWL_STYLE);
	if (gwl & WS_CHILD) {
	    SetWindow_bw(win, bw);

	    if (bw) {
		gwl |= WS_BORDER;
	    } else {
		gwl &= ~WS_BORDER;
	    }
	    SetWindowLong(win, GWL_STYLE, gwl);

	    /*
	     * force a redraw - even if we do not support
	     * border colors ... (see UIPainter code)
	     */
#if 0
	    PostMessage(win, WM_NCPAINT, 0, 0);
#endif
	    {
		RECT rect;
		HRGN rgn;

		GetWindowRect(win, &rect);
		rgn = CreateRectRgn(rect.left, rect.top, rect.right, rect.bottom);
		DefWindowProc(win, WM_NCPAINT, (WPARAM)rgn, 0);
		_DeleteObject(rgn, __LINE__);
	    }
	}
    }
%}.
!

setWindowClass:wClass name:wName in:aWindowId
    "define class and name of a window.
     This may be used by the window manager to
     select client specific resources."

%{  /* NOCONTEXT */
    DPRINTF(("WinWorkstation [info]: setWindowClass:name:in: not (yet) implemented\n"));
%}.
!

setWindowGravity:how in:aWindowId
    "set the window gravity - this makes the view to move
     automatically and can avoid redraws (if used properly)"

%{  /* NOCONTEXT */
    int gr = GRAVITY_NONE;

    if (__isExternalAddress(aWindowId)) {
	HWND hWnd = _HWNDVal(aWindowId);
	localWindowInfo *lI;

	if (how == @symbol(South)) {
	    gr = GRAVITY_S;
	} else if (how == @symbol(SouthWest)) {
	    gr = GRAVITY_SW;
	} else if (how == @symbol(SouthEast)) {
	    gr = GRAVITY_SE;
	} else if (how == @symbol(North)) {
	    gr = GRAVITY_N;
	} else if (how == @symbol(NorthWest)) {
	    gr = GRAVITY_NW;
	} else if (how == @symbol(NorthEast)) {
	    gr = GRAVITY_NE;
	} else if (how == @symbol(East)) {
	    gr = GRAVITY_E;
	} else if (how == @symbol(West)) {
	    gr = GRAVITY_W;
	}
	if (lI = GETLOCALWINDOWINFOPTR(hWnd)) {
	    lI->viewGravity = gr;
	}
    }
%}.
!

setWindowIcon:aForm mask:aMaskForm in:aWindowId
    "set a windows icon & iconMask"

    |wiconId wmaskId wiconHeight wiconWidth|

    aForm notNil ifTrue:[
	wiconId := aForm id.
	wiconHeight := aForm height.
	wiconWidth  := aForm width.
	aMaskForm notNil ifTrue:[
	    wmaskId := aMaskForm id
	].
	self
	    primSetWindowIconId:wiconId
	    maskId:wmaskId
	    width:wiconWidth
	    height:wiconHeight
	    in:aWindowId
    ].
!

setWindowIconWindow:aView in:aWindowId
    "define a window to be used as icon.
     This is not supported with Windows (is it possible at all ?)."

    'WinWorkstation [info]: setWindowIconWindow:in: not implemented' infoPrintCR.
!

setWindowMinExtent:minExt maxExtent:maxExt in:aWindowId
    "set a windows minimum & max extents.
     nil arguments are ignored."

    |minW minH maxW maxH|

    minExt notNil ifTrue:[
	minW := minExt x.
	minH := minExt y.
    ].
    maxExt notNil ifTrue:[
	maxW := maxExt x.
	maxH := maxExt y.
    ].
%{ 
    if (__isExternalAddress(aWindowId)) {
	HWND win = _HWNDVal(aWindowId);
	RECT rec;
	int winStyleBits;
	int winExStyleBits;
	localWindowInfo *lI;

	if (win) {
	    winStyleBits = GetWindowLong(win, GWL_STYLE);
	    winExStyleBits = GetWindowLong(win, GWL_EXSTYLE);
	    lI = GETLOCALWINDOWINFOPTR(win);
	    if (! lI) {
		fprintf(stderr, "WinWorkstation [info]: oops - no localInfo in setMinMaxExt\n");
		RETURN (self);
	    }

	    if (__bothSmallInteger(minW, minH)) {
		RECT rec;

		rec.left = 0;
		rec.top = 0;
		rec.right = __intVal(minW);
		rec.bottom = __intVal(minH);
#ifdef ADJUSTWINDOW
		AdjustWindowRectEx(&rec, winStyleBits, 0, winExStyleBits);
#endif
		lI->minWidth = rec.right - rec.left;
		lI->minHeight = rec.bottom - rec.top;
	    }
	    if (__bothSmallInteger(maxW, maxH)) {
		RECT rec;

		rec.left = 0;
		rec.top = 0;
		rec.right = __intVal(maxW);
		rec.bottom = __intVal(maxH);
#ifdef ADJUSTWINDOW
		AdjustWindowRectEx(&rec, winStyleBits, 0, winExStyleBits);
#endif
		lI->maxWidth = rec.right - rec.left;
		lI->maxHeight = rec.bottom - rec.top;
	    }
	}
    }
%}
!

setWindowName:aString in:aWindowId
    "define a windows name"

%{  /* NOCONTEXT */

    if (__isExternalAddress(aWindowId)
     && (__isString(aString) || __isSymbol(aString))) {
	HWND win = _HWNDVal(aWindowId);

	SetWindowText(win, __stringVal(aString));
	RETURN (self);
    }
%}
!

setWindowShapeEllipticalX:x Y:y width:w height:h in:aWindowId
    "set the windows shape to an elliptical region"

%{
    if (__isExternalAddress(aWindowId)
     && __bothSmallInteger(x, y)
     && __bothSmallInteger(w, h)) {
	HWND win = _HWNDVal(aWindowId);
	int left = __intVal(x);
	int top = __intVal(y);
	int right = left + __intVal(w);
	int bottom = top + __intVal(h);
	HRGN rgn;
	HDC winDC;

	rgn = CreateEllipticRgn(left, top, right, bottom);
	if (rgn) {
	    winDC = GetDCEx(win, 0, DCX_WINDOW);
	    if (winDC) {
		if (SelectClipRgn(winDC, rgn) == ERROR ) {
		    fprintf(stderr, "select clipping region failed\n");
		}
		ReleaseDC(win, winDC);
	    }
	    else
		fprintf(stderr, "getDC failed\n");

	    _DeleteObject(rgn, __LINE__);
	}
	else
	    fprintf(stderr, "region creation failed\n");
	RETURN ( self );
    }
%}.
    self primitiveFailed
!

unmapWindow:aWindowId
    "make a window invisible"

%{  /* NOCONTEXT */

    if (__isExternalAddress(aWindowId)) {
	HWND win = _HWNDVal(aWindowId);

	if (win) {
	    CPRINTF(("unmapWindow %x\n",win));
	    ShowWindowAsync(win, SW_HIDE);
	}
	RETURN ( self );
    }
%}
!

windowIsIconified:aWindowId
    "return true, if some window is iconified."

%{  /* NOCONTEXT */
    if (__isExternalAddress(aWindowId)) {
	HWND win = _HWNDVal(aWindowId);

	if (win) {
	    if (GetWindow_iconified(win)) {
		RETURN (true);
	    }
	}
    }
%}.
    ^ false
! !

!WinWorkstation class methodsFor:'documentation'!

version
    ^ '$Header: /cvs/stx/stx/libview/WinWorkstation.st,v 1.189 2001-09-15 14:06:46 cg Exp $'
! !
WinWorkstation initialize!