Release 960616

Sun Jun 16 16:51:31 1996  Alexandre Julliard  <julliard@lrc.epfl.ch>

	* [memory/heap.c]
	Fixed bug in HeapRealloc (thanks to Bruce Milner).

	* [misc/wsprintf.c]
	Fixed argument size for %c format in wsprintf16().

	* [objects/dc.c]
	Don't free hFirstBitmap for saved DCs.

	* [windows/event.c]
	Added timer handling in EVENT_WaitXEvent().

	* [windows/message.c]
	In MSG_TranslateMouseMsg and MSG_TranslateKbdMsg, check if the
	event is for the current task; if not, wake the other task.

	* [windows/queue.c] [include/queue.h]
	Added 'self' handle in queue structure.

	* [windows/timer.c]
	Added TIMER_ExpireTimers() function to mark expired timers and
	wake up the corresponding tasks.

Thu Jun 13 01:46:33 EDT 1996  William Magro <wmagro@tc.cornell.edu>

	* [windows/mapping.c]
	First point in list was mapped multiple times in DPtoLP and
 	LPtoDP.  Other points were not mapped.

Wed Jun 12 18:08:45 1996  Alex Korobka <alex@phm30.pharm.sunysb.edu>

	* [misc/shell.c]
	Some fixes for ExtractIcon function family.

	* [documentation/user_module]
	Chapter about windowing and messaging subsystems.
diff --git a/ANNOUNCE b/ANNOUNCE
index b4f14d3..9196873 100644
--- a/ANNOUNCE
+++ b/ANNOUNCE
@@ -1,12 +1,12 @@
-This is release 960611 of Wine, the MS Windows emulator.  This is still a
+This is release 960616 of Wine, the MS Windows emulator.  This is still a
 developer's only release.  There are many bugs and many unimplemented API
 features.  Most applications still do not work correctly.
 
 Patches should be submitted to "julliard@lrc.epfl.ch".  Please don't
 forget to include a ChangeLog entry.
 
-WHAT'S NEW with Wine-960611: (see ChangeLog for details)
-	- (surprise) More Win32 code.
+WHAT'S NEW with Wine-960616: (see ChangeLog for details)
+	- Inter-task messaging begins to work.
 	- Lots of bug fixes.
 
 See the README file in the distribution for installation instructions.
@@ -15,10 +15,10 @@
 the release is available at the ftp sites.  The sources will be available
 from the following locations:
 
-    sunsite.unc.edu:/pub/Linux/ALPHA/wine/development/Wine-960611.tar.gz
-    tsx-11.mit.edu:/pub/linux/ALPHA/Wine/development/Wine-960611.tar.gz
-    ftp.infomagic.com:/pub/mirrors/linux/wine/development/Wine-960611.tar.gz
-    aris.com:/pub/linux/ALPHA/Wine/development/Wine-960611.tar.gz
+    sunsite.unc.edu:/pub/Linux/ALPHA/wine/development/Wine-960616.tar.gz
+    tsx-11.mit.edu:/pub/linux/ALPHA/Wine/development/Wine-960616.tar.gz
+    ftp.infomagic.com:/pub/mirrors/linux/wine/development/Wine-960616.tar.gz
+    aris.com:/pub/linux/ALPHA/Wine/development/Wine-960616.tar.gz
 
 It should also be available from any site that mirrors tsx-11 or sunsite.
 
diff --git a/ChangeLog b/ChangeLog
index 0d6d6c1..855adb2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,44 @@
 ----------------------------------------------------------------------
+Sun Jun 16 16:51:31 1996  Alexandre Julliard  <julliard@lrc.epfl.ch>
+
+	* [memory/heap.c]
+	Fixed bug in HeapRealloc (thanks to Bruce Milner).
+
+	* [misc/wsprintf.c]
+	Fixed argument size for %c format in wsprintf16().
+
+	* [objects/dc.c]
+	Don't free hFirstBitmap for saved DCs.
+
+	* [windows/event.c]
+	Added timer handling in EVENT_WaitXEvent().
+
+	* [windows/message.c]
+	In MSG_TranslateMouseMsg and MSG_TranslateKbdMsg, check if the
+	event is for the current task; if not, wake the other task.
+
+	* [windows/queue.c] [include/queue.h]
+	Added 'self' handle in queue structure.
+
+	* [windows/timer.c]
+	Added TIMER_ExpireTimers() function to mark expired timers and
+	wake up the corresponding tasks.
+
+Thu Jun 13 01:46:33 EDT 1996  William Magro <wmagro@tc.cornell.edu>
+
+	* [windows/mapping.c]
+	First point in list was mapped multiple times in DPtoLP and
+ 	LPtoDP.  Other points were not mapped.
+
+Wed Jun 12 18:08:45 1996  Alex Korobka <alex@phm30.pharm.sunysb.edu>
+
+	* [misc/shell.c]
+	Some fixes for ExtractIcon function family.
+
+	* [documentation/user_module]
+	Chapter about windowing and messaging subsystems.
+
+----------------------------------------------------------------------
 Tue Jun 11 15:20:43 1996  Alexandre Julliard  <julliard@lrc.epfl.ch>
 
 	* [debugger/break.c] [loader/signal.c]
diff --git a/documentation/user_module b/documentation/user_module
new file mode 100644
index 0000000..6b6bfd7
--- /dev/null
+++ b/documentation/user_module
@@ -0,0 +1,217 @@
+USER MODULE
+===========
+
+USER implements windowing and messaging subsystems. It also 
+contains code for common controls and for other miscellaneous 
+stuff (rectangles, clipboard, WNet, etc). Wine USER code is 
+located in windows/, controls/, and misc/ directories.
+
+1. Windowing subsystem
+
+   Windows are arranged into parent/child hierarchy with one
+   common ancestor for all windows (desktop window). Each window
+   structure contains a pointer to the immediate ancestor (parent 
+   window if WS_CHILD style bit is set), a pointer to the sibling 
+   (returned by GetWindow(..., GW_NEXT)), a pointer to the owner 
+   window (set only for popup window if it was created with valid 
+   hwndParent parameter), and a pointer to the first child
+   window (GetWindow(.., GW_CHILD)). All popup and non-child windows
+   are therefore placed in the first level of this hierarchy and their
+   ancestor link (wnd->parent) points to the desktop window.
+
+   Desktop window			- root window
+    |     \      '-.
+    |      \        '-.
+   popup -> wnd1  ->  wnd2		- top level windows    
+    |       \   '-.      '-.
+    |        \     '-.      '-.
+   child1  child2 -> child3  child4     - child windows
+  
+   Horizontal arrows denote sibling relationship, vertical lines
+   - ancestor/child. To summarize, all windows with the same immediate 
+   ancestor are sibling windows, all windows which do not have desktop 
+   as their immediate ancestor are child windows. Popup windows behave
+   as topmost top-level windows unless they are owned. In this case the
+   only requirement is that they must precede their owners in the top-level 
+   sibling list (they are not topmost). Child windows are confined to the
+   client area of their parent windows (client area is where window gets
+   to do its own drawing, non-client area consists of caption, menu, borders,
+   intrinsic scrollbars, and minimize/maximize/close buttons). 
+  
+   Another fairly important concept is "z-order". It is derived from
+   the ancestor/child hierarchy and is used to determine "above/below"
+   relationship. For instance, in the example above, z-order is
+   child1->popup->child2->child3->wnd1->child4->wnd2->desktop. Current 
+   active window ("foreground window" in Win32) is moved to the front
+   of z-order unless its top-level ancestor owns popup windows.
+
+   All these issues are dealt with (or supposed to be) in 
+   windows/winpos.c
+
+   Wine specifics: in default and managed mode each top-level window
+   gets its own X counterpart with desktop window being basically a 
+   fake stub. In desktop mode, however, only desktop window has X
+   window associated with it.
+
+2. Messaging subsystem
+
+   Each Windows task/thread has its own message queue - this is where
+   it gets messages from. Messages can be generated on the fly
+   (WM_PAINT, WM_NCPAINT, WM_TIMER), they can be created by the system
+   (hardware messages), they can be posted by other tasks/threads
+   (PostMessage), or they can be sent by other tasks/threads (SendMessage).
+   
+   Message priority:
+
+   First the system looks for sent messages, then for posted messages,
+   then for hardware messages, then it checks if the queue has the
+   "dirty window" bit set, and, finally, it checks for expired
+   timers. See windows/message.c.
+
+   From all these different types of messages, only posted messages go
+   directly into the private message queue. System messages (even in
+   Win95) are first collected in the system message queue and then
+   they either sit there until Get/PeekMessage gets to process them
+   or, as in Win95, if system queue is getting clobbered, a special
+   thread ("raw input thread") assigns them to the private
+   queues. Sent messages are queued separately and the sender sleeps
+   until it gets a reply. Special messages are generated on the fly
+   depending on the window/queue state. If the window update region is
+   not empty, the system sets the QS_PAINT bit in the owning queue and
+   eventually this window receives a WM_PAINT message (WM_NCPAINT too
+   if the update region intersects with the non-client area). A timer
+   event is raised when one of the queue timers expire. Depending on
+   the timer parameters DispatchMessage either calls the callback
+   function or the window procedure. If there are no messages pending
+   the task/thread sleeps until messages appear.
+
+   There are several tricky moments (open for discussion) - 
+
+    a) System message order has to be honored and messages should be
+       processed within correct task/thread context. Therefore when
+       Get/PeekMessage encounters unassigned system message and this
+       message appears not to be for the current task/thread it should
+       either skip it (or get rid of it by moving it into the private
+       message queue of the target task/thread - Win95, AFAIK) and
+       look further or roll back and then yield until this message
+       gets processed when system switches to the correct context
+       (Win16). In the first case we lose correct message ordering, in
+       the second case we have the infamous synchronous system message
+       queue. Here is a post to one of the OS/2 newsgroup I found to
+       be relevant:
+
+	" Here's the problem in a nutshell, and there is no good solution.
+	Every possible solution creates a different problem.
+
+	With a windowing system, events can go to many different windows.
+	Most are sent by applications or by the OS when things relating to
+	that window happen (like repainting, timers, etc.)
+
+	Mouse input events go to the window you click on (unless some window
+	captures the mouse).
+
+	So far, no problem.  Whenever an event happens, you put a message on
+	the target window's message queue.  Every process has a message
+	queue.  If the process queue fills up, the messages back up onto the
+	system queue.
+
+	This is the first cause of apps hanging the GUI.  If an app doesn't
+	handle messages and they back up into the system queue, other apps
+	can't get any more messages.  The reason is that the next message in
+	line can't go anywhere, and the system won't skip over it.
+	
+	This can be fixed by making apps have bigger private message queues.
+	The SIQ fix does this.  PMQSIZE does this for systems without the SIQ
+	fix.  Applications can also request large queues on their own.
+
+	Another source of the problem, however, happens when you include
+	keyboard events.  When you press a key, there's no easy way to know
+	what window the keystroke message should be delivered to.
+
+	Most windowing systems use a concept known as "focus".  The window
+	with focus gets all incoming keyboard messages.  Focus can be changed
+	from window to window by apps or by users clicking on winodws.
+
+	This is the second source of the problem.  Suppose window A has focus.
+	You click on window B and start typing before the window gets focus.
+	Where should the keystrokes go?  On the one hand, they should go to A
+	until the focus actually changes to B.  On the other hand, you
+	probably want the keystrokes to go to B, since you clicked there
+	first.
+
+	OS/2's solution is that when a focus-changing event happens (like
+	clicking on a window), OS/2 holds all messages in the system queue
+	until the focus change actually happens.  This way, subsequent
+	keystrokes go to the window you clicked on, even if it takes a while
+	for that window to get focus.
+
+	The downside is that if the window takes a real long time to get focus
+	(maybe it's not handling events, or maybe the window losing focus
+	isn't handling events), everything backs up in the system queue and
+	the system appears hung.
+
+	There are a few solutions to this problem.
+
+	One is to make focus policy asynchronous.  That is, focus changing has
+	absolutely nothing to do with the keyboard.  If you click on a window
+	and start typing before the focus actually changes, the keystrokes go
+	to the first window until focus changes, then they go to the second.
+	This is what X-windows does.
+
+	Another is what NT does.  When focus changes, keyboard events are held
+	in the system message queue, but other events are allowed through.
+	This is "asynchronous" because the messages in the system queue are
+	delivered to the application queues in a different order from that
+	with which they were posted.  If a bad app won't handle the "lose
+	focus" message, it's of no consequence - the app receiving focus will
+	get its "gain focus" message, and the keystrokes will go to it.
+
+	The NT solution also takes care of the application queue filling up
+	problem.  Since the system delivers messages asynchronously, messages
+	waiting in the system queue will just sit there and the rest of the
+	messages will be delivered to their apps.
+
+	The OS/2 SIQ solution is this:  When a focus-changing event happens,
+	in addition to blocking further messages from the application queues,
+	a timer is started.  When the timer goes off, if the focus change has
+	not yet happened, the bad app has its focus taken away and all
+	messages targetted at that window are skipped.  When the bad app
+	finally handles the focus change message, OS/2 will detect this and
+	stop skipping its messages.
+
+
+	As for the pros and cons:
+
+	The X-windows solution is probably the easiest.  The problem is that
+	users generally don't like having to wait for the focus to change
+	before they start typing.  On many occasions, you can type and the
+	characters end up in the wrong window because something (usually heavy
+	system load) is preventing the focus change from happening in a timely
+	manner.
+	
+	The NT solution seems pretty nice, but making the system message queue
+	asynchronous can cause similar problems to the X-windows problem.
+	Since messages can be delivered out of order, programs must not assume
+	that two messages posted in a particular order will be delivered in
+	that same order.  This can break legacy apps, but since Win32 always
+	had an asynchronous queue, it is fair to simply tell app designers
+	"don't do that".  It's harder to tell app designers something like
+	that on OS/2 - they'll complain "you changed the rules and our apps
+	are breaking."
+	
+	The OS/2 solution's problem is that nothing happens until you try to
+	change window focus, and then wait for the timeout.  Until then, the
+	bad app is not detected and nothing is done." (by David Charlap)
+	
+
+    b) Intertask/interthread SendMessage. The system has to inform the
+       target queue about the forthcoming message, then it has to
+       carry out the context switch and wait until the result is
+       available.  In Win16 it is done by putting necessary parameters
+       into the queue structure and do a DirectedYield() call.
+       However, in Win32 there could be several messages pending sent
+       by preemptively executing threads, and in this case SendMessage
+       has to build some sort of message queue for sent
+       messages. Another issue is what to do with messages sent to the
+       sender when it is blocked inside its own SendMessage. At this
+       point Wine does not address any of these problems.
diff --git a/include/message.h b/include/message.h
index 72f64ad..365ee59 100644
--- a/include/message.h
+++ b/include/message.h
@@ -13,7 +13,6 @@
 extern DWORD MSG_WineStartTicks;  /* Ticks at Wine startup */
 
 /* message.c */
-extern BOOL MSG_GetHardwareMessage( LPMSG16 msg );
 extern BOOL MSG_InternalGetMessage( SEGPTR msg, HWND hwnd, HWND hwndOwner,
 				    short code, WORD flags, BOOL sendIdle );
 
@@ -21,13 +20,16 @@
 extern void TIMER_RemoveWindowTimers( HWND hwnd );
 extern void TIMER_RemoveQueueTimers( HQUEUE hqueue );
 extern void TIMER_SwitchQueue( HQUEUE hOldQueue, HQUEUE hNewQueue );
-extern LONG TIMER_GetNextExp(void);
+extern LONG TIMER_GetNextExpiration(void);
+extern void TIMER_ExpireTimers(void);
+extern BOOL TIMER_GetTimerMsg( MSG16 *msg, HWND hwnd,
+                               HQUEUE hQueue, BOOL remove );
 
 /* event.c */
-extern BOOL EVENT_WaitXEvent( LONG maxWait );
+extern BOOL EVENT_WaitXEvent( BOOL sleep );
 extern void EVENT_Synchronize(void);
 extern void EVENT_ProcessEvent( XEvent *event );
-extern void EVENT_RegisterWindow( Window w, HWND hwnd );
+extern void EVENT_RegisterWindow( WND *pWnd );
 extern void EVENT_DummyMotionNotify(void);
 
 #endif  /* __WINE_MESSAGE_H */
diff --git a/include/queue.h b/include/queue.h
index 553f7a4..cffd7e3 100644
--- a/include/queue.h
+++ b/include/queue.h
@@ -32,7 +32,7 @@
   WORD      queueSize;              /* 0c Size of the queue */
   DWORD     GetMessageTimeVal WINE_PACKED;  /* 0e Value for GetMessageTime */
   DWORD     GetMessagePosVal WINE_PACKED;   /* 12 Value for GetMessagePos */
-  WORD      reserved1;              /* 16 Unknown */
+  HQUEUE    self;                   /* 16 Handle to self (was: reserved) */
   DWORD     GetMessageExtraInfoVal; /* 18 Value for GetMessageExtraInfo */
   WORD      reserved2;              /* 1c Unknown */
   LPARAM    lParam WINE_PACKED;     /* 1e Next 4 values set by SendMessage */
@@ -75,6 +75,7 @@
 extern void QUEUE_WalkQueues(void);
 extern MESSAGEQUEUE *QUEUE_GetSysQueue(void);
 extern void QUEUE_SetWakeBit( MESSAGEQUEUE *queue, WORD bit );
+extern void QUEUE_ClearWakeBit( MESSAGEQUEUE *queue, WORD bit );
 extern void QUEUE_ReceiveMessage( MESSAGEQUEUE *queue );
 extern void QUEUE_WaitBits( WORD bits );
 extern void QUEUE_IncPaintCount( HQUEUE hQueue );
diff --git a/include/windows.h b/include/windows.h
index 64363f0..d60ce19 100644
--- a/include/windows.h
+++ b/include/windows.h
@@ -469,6 +469,7 @@
 
   /* CallMsgFilter() values */
 #define MSGF_DIALOGBOX      0
+#define MSGF_MESSAGEBOX     1
 #define MSGF_MENU           2
 #define MSGF_MOVE           3
 #define MSGF_SIZE           4
diff --git a/library/miscstubs.c b/library/miscstubs.c
index ed8aaa7..fa59b22 100644
--- a/library/miscstubs.c
+++ b/library/miscstubs.c
@@ -84,7 +84,7 @@
   MAP_STR_TO_PROC("ColorDlgProc",ColorDlgProc);
   MAP_STR_TO_PROC("ComboBoxWndProc",ComboBoxWndProc);
   MAP_STR_TO_PROC("ComboLBoxWndProc",ComboLBoxWndProc);
-  MAP_STR_TO_PROC("DefDlgProc",DefDlgProc);
+  MAP_STR_TO_PROC("DefDlgProc",DefDlgProc16);
   MAP_STR_TO_PROC("EditWndProc",EditWndProc);
   MAP_STR_TO_PROC("FileOpenDlgProc",FileOpenDlgProc);
   MAP_STR_TO_PROC("FileSaveDlgProc",FileSaveDlgProc);
diff --git a/loader/task.c b/loader/task.c
index 8132b9d..b422d76 100644
--- a/loader/task.c
+++ b/loader/task.c
@@ -677,7 +677,7 @@
 
     /* Flush any X events that happened in the meantime */
 
-    EVENT_WaitXEvent( 0 );
+    EVENT_WaitXEvent( FALSE );
 
     /* Find a task to yield to */
 
@@ -707,7 +707,7 @@
 
         /* No task found, wait for some events to come in */
 
-        EVENT_WaitXEvent( TIMER_GetNextExp() );
+        EVENT_WaitXEvent( TRUE );
     }
 
     if (hTask == hCurrentTask) return;  /* Nothing to do */
diff --git a/memory/heap.c b/memory/heap.c
index 462cf77..2411161 100644
--- a/memory/heap.c
+++ b/memory/heap.c
@@ -1005,6 +1005,7 @@
                 SetLastError( ERROR_OUTOFMEMORY );
                 return NULL;
             }
+            HEAP_ShrinkBlock( subheap, pArena, size );
         }
         else  /* Do it the hard way */
         {
@@ -1029,6 +1030,7 @@
                                + sizeof(ARENA_FREE) - sizeof(ARENA_INUSE);
             pInUse->threadId = GetCurrentTask();
             pInUse->magic    = ARENA_INUSE_MAGIC;
+            HEAP_ShrinkBlock( subheap, pInUse, size );
             memcpy( pInUse + 1, pArena + 1, oldSize );
 
             /* Free the previous block */
@@ -1038,11 +1040,7 @@
             pArena  = pInUse;
         }
     }
-
-
-    /* Shrink the block */
-
-    HEAP_ShrinkBlock( subheap, pArena, size );
+    else HEAP_ShrinkBlock( subheap, pArena, size );  /* Shrink the block */
 
     /* Clear the extra bytes if needed */
 
diff --git a/misc/clipboard.c b/misc/clipboard.c
index 4ff267c..d25775c 100644
--- a/misc/clipboard.c
+++ b/misc/clipboard.c
@@ -129,7 +129,7 @@
                     WIN_GetXWindow(hWnd),CurrentTime);
 
   /* TODO: need time-out for broken clients */
-  while(wait_for_selection) EVENT_WaitXEvent(-1);
+  while(wait_for_selection) EVENT_WaitXEvent( TRUE );
 
   return (BOOL)ClipFormats[0].wDataPresent;
 }
diff --git a/misc/shell.c b/misc/shell.c
index 1eb5f54..a8eb3f9 100644
--- a/misc/shell.c
+++ b/misc/shell.c
@@ -501,8 +501,8 @@
 
 		  for( i = nIconIndex; i < nIconIndex + n; i++ ) 
 		     {
-		       hIcon = SHELL_LoadResource( hInstance, hFile, pIconDir + (i - nIconIndex), 
-										 *(WORD*)pData );
+		       hIcon = SHELL_LoadResource( hInstance, hFile, pIconDir + i, 
+								  *(WORD*)pData );
 		       RetPtr[i-nIconIndex] = GetIconID( hIcon, 3 );
 		       GlobalFree16(hIcon); 
 		     }
@@ -547,18 +547,40 @@
 
 /*************************************************************************
  *				ExtractAssociatedIcon	[SHELL.36]
+ * 
+ * Return icon for given file (either from file itself or from associated
+ * executable) and patch parameters if needed.
  */
 HICON ExtractAssociatedIcon(HINSTANCE hInst,LPSTR lpIconPath, LPWORD lpiIcon)
 {
     HICON hIcon = ExtractIcon(hInst, lpIconPath, *lpiIcon);
 
-    /* MAKEINTRESOURCE(2) seems to be "default" icon according to Progman 
-     *
-     * For data files it probably should call FindExecutable and load
-     * icon from there. As of now FindExecutable is empty stub.
-     */
+    if( hIcon < 2 )
+      {
 
-    if( hIcon < 2 ) hIcon = LoadIcon( hInst, MAKEINTRESOURCE(2));
+	if( hIcon == 1 ) /* no icons found in given file */
+	  {
+	    char  tempPath[0x80];
+	    UINT  uRet = FindExecutable(lpIconPath,NULL,tempPath);
+
+	    if( uRet > 32 && tempPath[0] )
+	      {
+		strcpy(lpIconPath,tempPath);
+	        hIcon = ExtractIcon(hInst, lpIconPath, *lpiIcon);
+
+		if( hIcon > 2 ) return hIcon;
+	      }
+	    else hIcon = 0;
+	  }
+	
+	if( hIcon == 1 ) 
+	  *lpiIcon = 2;   /* MSDOS icon - we found .exe but no icons in it */
+	else
+	  *lpiIcon = 6;   /* generic icon - found nothing */
+
+        GetModuleFileName(hInst, lpIconPath, 0x80);
+	hIcon = LoadIcon( hInst, MAKEINTRESOURCE(*lpiIcon));
+      }
 
     return hIcon;
 }
diff --git a/misc/wsprintf.c b/misc/wsprintf.c
index ac3b8bc..f8f77aa 100644
--- a/misc/wsprintf.c
+++ b/misc/wsprintf.c
@@ -248,7 +248,7 @@
         {
         case WPR_CHAR:
             cur_arg = (DWORD)*(CHAR *)args;
-            args = (CHAR *)args + 1;
+            args = (WORD *)args + 1;
             break;
         case WPR_STRING:
             cur_arg = (DWORD)PTR_SEG_TO_LIN( *(SEGPTR *)args );
diff --git a/objects/dc.c b/objects/dc.c
index 6349560..d305001 100644
--- a/objects/dc.c
+++ b/objects/dc.c
@@ -621,9 +621,9 @@
 	SelectObject( hdc, STOCK_WHITE_BRUSH );
 	SelectObject( hdc, STOCK_SYSTEM_FONT );
 	XFreeGC( display, dc->u.x.gc );
+        if (dc->w.flags & DC_MEMORY) DeleteObject( dc->w.hFirstBitmap );
     }
 
-    if (dc->w.flags & DC_MEMORY) DeleteObject( dc->w.hFirstBitmap );
     if (dc->w.hClipRgn) DeleteObject( dc->w.hClipRgn );
     if (dc->w.hVisRgn) DeleteObject( dc->w.hVisRgn );
     if (dc->w.hGCClipRgn) DeleteObject( dc->w.hGCClipRgn );
diff --git a/windows/class.c b/windows/class.c
index dc9865c..a2a37a2 100644
--- a/windows/class.c
+++ b/windows/class.c
@@ -200,7 +200,7 @@
     if (classPtr->hbrBackground) DeleteObject( classPtr->hbrBackground );
     GlobalDeleteAtom( classPtr->atomName );
     CLASS_SetMenuNameA( classPtr, NULL );
-    CLASS_SetWndProc( classPtr, (WNDPROC16)0, WIN_PROC_16 );
+    CLASS_SetWndProc( classPtr, (HANDLE32)0, WIN_PROC_16 );
     HeapFree( SystemHeap, 0, classPtr );
     return TRUE;
 }
diff --git a/windows/dce.c b/windows/dce.c
index 8a809c9..f334bb9 100644
--- a/windows/dce.c
+++ b/windows/dce.c
@@ -159,9 +159,8 @@
 
 	        dprintf_dc(stddeb,"\tgot hwnd %04x\n", wndCurrent->hwndSelf);
   
-	        if( wndCurrent->parent != wndScope )
-	            MapWindowPoints16(wndCurrent->parent->hwndSelf, wndScope->hwndSelf,
-			 				         (LPPOINT16)&wndRect, 2);
+	        MapWindowPoints16(wndCurrent->parent->hwndSelf, wndScope->hwndSelf,
+			 				       (LPPOINT16)&wndRect, 2);
 	        if( IntersectRect16(&wndRect,&wndRect,pRectUpdate) )
 	            SetHookFlags(dce->hDC, DCHF_INVALIDATEVISRGN);
 	        break;
diff --git a/windows/event.c b/windows/event.c
index 66fb819..4f421a4 100644
--- a/windows/event.c
+++ b/windows/event.c
@@ -25,6 +25,7 @@
 #include "class.h"
 #include "clipboard.h"
 #include "debugger.h"
+#include "message.h"
 #include "module.h"
 #include "options.h"
 #include "queue.h"
@@ -35,19 +36,6 @@
 #include "dde_proc.h"
 
 
-#ifdef ndef
-#ifndef FamilyAmoeba
-typedef char *XPointer;
-#endif
-#endif
-
-#ifdef WHO_NEEDS_DIRTY_HACKS
-#ifdef sparc
-/* Dirty hack to compile with Sun's OpenWindows */
-typedef char *XPointer;
-#endif
-#endif
-
 #define NB_BUTTONS      3     /* Windows can handle 3 buttons */
 
   /* X context to associate a hwnd to an X window */
@@ -152,13 +140,13 @@
 static void EVENT_MotionNotify( XMotionEvent *event );
 static void EVENT_FocusIn( HWND hwnd, XFocusChangeEvent *event );
 static void EVENT_FocusOut( HWND hwnd, XFocusChangeEvent *event );
-static void EVENT_Expose( HWND hwnd, XExposeEvent *event );
-static void EVENT_GraphicsExpose( HWND hwnd, XGraphicsExposeEvent *event );
+static void EVENT_Expose( WND *pWnd, XExposeEvent *event );
+static void EVENT_GraphicsExpose( WND *pWnd, XGraphicsExposeEvent *event );
 static void EVENT_ConfigureNotify( HWND hwnd, XConfigureEvent *event );
-static void EVENT_SelectionRequest( HWND hwnd, XSelectionRequestEvent *event);
-static void EVENT_SelectionNotify( HWND hwnd, XSelectionEvent *event);
-static void EVENT_SelectionClear( HWND hwnd, XSelectionClearEvent *event);
-static void EVENT_ClientMessage( HWND hwnd, XClientMessageEvent *event );
+static void EVENT_SelectionRequest( WND *pWnd, XSelectionRequestEvent *event);
+static void EVENT_SelectionNotify( XSelectionEvent *event);
+static void EVENT_SelectionClear( WND *pWnd, XSelectionClearEvent *event);
+static void EVENT_ClientMessage( WND *pWnd, XClientMessageEvent *event );
 
 
 /***********************************************************************
@@ -168,14 +156,14 @@
  */
 void EVENT_ProcessEvent( XEvent *event )
 {
-    HWND hwnd;
-    XPointer ptr;
+    WND *pWnd;
     
-    XFindContext( display, ((XAnyEvent *)event)->window, winContext, &ptr );
-    hwnd = (HWND) (int)ptr;
+    if (XFindContext( display, ((XAnyEvent *)event)->window, winContext,
+                      (char **)&pWnd ) != 0)
+        return;  /* Not for a registered window */
 
-    dprintf_event(stddeb, "Got event %s for hwnd %04x\n",
-                  event_names[event->type], hwnd );
+    dprintf_event( stddeb, "Got event %s for hwnd %04x\n",
+                   event_names[event->type], pWnd->hwndSelf );
 
     switch(event->type)
     {
@@ -207,46 +195,47 @@
 	break;
 
     case FocusIn:
-        EVENT_FocusIn( hwnd, (XFocusChangeEvent*)event );
+        EVENT_FocusIn( pWnd->hwndSelf, (XFocusChangeEvent*)event );
 	break;
 
     case FocusOut:
-	EVENT_FocusOut( hwnd, (XFocusChangeEvent*)event );
+	EVENT_FocusOut( pWnd->hwndSelf, (XFocusChangeEvent*)event );
 	break;
 
     case Expose:
-	EVENT_Expose( hwnd, (XExposeEvent*)event );
-	break;
-
-    case ConfigureNotify:
-	EVENT_ConfigureNotify( hwnd, (XConfigureEvent*)event );
-	break;
-
-    case SelectionRequest:
-	EVENT_SelectionRequest( hwnd, (XSelectionRequestEvent*)event );
-	break;
-
-    case SelectionNotify:
-	EVENT_SelectionNotify( hwnd, (XSelectionEvent*)event );
-	break;
-
-    case SelectionClear:
-	EVENT_SelectionClear( hwnd, (XSelectionClearEvent*) event );
-	break;
-
-    case ClientMessage:
-	EVENT_ClientMessage( hwnd, (XClientMessageEvent *) event );
+	EVENT_Expose( pWnd, (XExposeEvent *)event );
 	break;
 
     case GraphicsExpose:
-	EVENT_GraphicsExpose( hwnd, (XGraphicsExposeEvent *) event );
+	EVENT_GraphicsExpose( pWnd, (XGraphicsExposeEvent *)event );
+        break;
+
+    case ConfigureNotify:
+	EVENT_ConfigureNotify( pWnd->hwndSelf, (XConfigureEvent*)event );
+	break;
+
+    case SelectionRequest:
+	EVENT_SelectionRequest( pWnd, (XSelectionRequestEvent *)event );
+	break;
+
+    case SelectionNotify:
+	EVENT_SelectionNotify( (XSelectionEvent *)event );
+	break;
+
+    case SelectionClear:
+	EVENT_SelectionClear( pWnd, (XSelectionClearEvent*) event );
+	break;
+
+    case ClientMessage:
+	EVENT_ClientMessage( pWnd, (XClientMessageEvent *) event );
+	break;
 
     case NoExpose:
 	break;   
 
     default:    
 	dprintf_event(stddeb, "Unprocessed event %s for hwnd %04x\n",
-	        event_names[event->type], hwnd );
+	        event_names[event->type], pWnd->hwndSelf );
 	break;
     }
 }
@@ -257,29 +246,31 @@
  *
  * Associate an X window to a HWND.
  */
-void EVENT_RegisterWindow( Window w, HWND hwnd )
+void EVENT_RegisterWindow( WND *pWnd )
 {
     if (!winContext) winContext = XUniqueContext();
-    XSaveContext( display, w, winContext, (XPointer)(int)hwnd );
+    XSaveContext( display, pWnd->window, winContext, (char *)pWnd );
 }
 
 
 /***********************************************************************
  *           EVENT_WaitXEvent
  *
- * Wait for an X event, but at most maxWait milliseconds (-1 for no timeout).
+ * Wait for an X event, optionally sleeping until one arrives.
  * Return TRUE if an event is pending, FALSE on timeout or error
  * (for instance lost connection with the server).
  */
-BOOL EVENT_WaitXEvent( LONG maxWait )
+BOOL EVENT_WaitXEvent( BOOL sleep )
 {
     fd_set read_set;
     struct timeval timeout;
     XEvent event;
     int fd = ConnectionNumber(display);
 
-    if (!XPending(display) && (maxWait != -1))
+    if (!XPending(display))
     {
+        LONG maxWait = sleep ? TIMER_GetNextExpiration() : 0;
+
         FD_ZERO( &read_set );
         FD_SET( fd, &read_set );
 
@@ -295,18 +286,23 @@
 		;
 	    return TRUE;
 	}
-	stop_wait_op= STOP_WAIT_X;
-	/* The code up to the next "stop_wait_op= CONT" must be reentrant  */
+	stop_wait_op = STOP_WAIT_X;
+	/* The code up to the next "stop_wait_op = CONT" must be reentrant */
 	if (select( fd+1, &read_set, NULL, NULL, &timeout ) != 1 &&
-	    !XPending(display)) {
-	    stop_wait_op= CONT;
+	    !XPending(display))
+        {
+	    stop_wait_op = CONT;
+            TIMER_ExpireTimers();
 	    return FALSE;
-	} else {
-	    stop_wait_op= CONT;
 	}
+        else stop_wait_op = CONT;
 #else  /* CONFIG_IPC */
 	if (select( fd+1, &read_set, NULL, NULL, &timeout ) != 1)
-            return FALSE;  /* Timeout or error */
+        {
+            /* Timeout or error */
+            TIMER_ExpireTimers();
+            return FALSE;
+        }
 #endif  /* CONFIG_IPC */
 
     }
@@ -371,19 +367,17 @@
 /***********************************************************************
  *           EVENT_Expose
  */
-static void EVENT_Expose( HWND hwnd, XExposeEvent *event )
+static void EVENT_Expose( WND *pWnd, XExposeEvent *event )
 {
     RECT32 rect;
-    WND * wndPtr = WIN_FindWndPtr( hwnd );
-    if (!wndPtr) return;
 
-      /* Make position relative to client area instead of window */
-    rect.left = event->x - (wndPtr->rectClient.left - wndPtr->rectWindow.left);
-    rect.top  = event->y - (wndPtr->rectClient.top - wndPtr->rectWindow.top);
+    /* Make position relative to client area instead of window */
+    rect.left   = event->x - (pWnd->rectClient.left - pWnd->rectWindow.left);
+    rect.top    = event->y - (pWnd->rectClient.top - pWnd->rectWindow.top);
     rect.right  = rect.left + event->width;
     rect.bottom = rect.top + event->height;
 
-    RedrawWindow32( hwnd, &rect, 0,
+    RedrawWindow32( pWnd->hwndSelf, &rect, 0,
                     RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN | RDW_ERASE |
                     (event->count ? 0 : RDW_ERASENOW) );
 }
@@ -391,22 +385,21 @@
 
 /***********************************************************************
  *           EVENT_GraphicsExpose
+ *
  * This is needed when scrolling area is partially obscured
  * by non-Wine X window.
  */
-static void EVENT_GraphicsExpose( HWND hwnd, XGraphicsExposeEvent *event )
+static void EVENT_GraphicsExpose( WND *pWnd, XGraphicsExposeEvent *event )
 {
-    RECT16 rect;
-    WND * wndPtr = WIN_FindWndPtr( hwnd );
-    if (!wndPtr) return;
+    RECT32 rect;
 
-      /* Make position relative to client area instead of window */
-    rect.left = event->x - (wndPtr->rectClient.left - wndPtr->rectWindow.left);
-    rect.top  = event->y - (wndPtr->rectClient.top - wndPtr->rectWindow.top);
+    /* Make position relative to client area instead of window */
+    rect.left   = event->x - (pWnd->rectClient.left - pWnd->rectWindow.left);
+    rect.top    = event->y - (pWnd->rectClient.top - pWnd->rectWindow.top);
     rect.right  = rect.left + event->width;
     rect.bottom = rect.top + event->height;
 
-    RedrawWindow16( hwnd, &rect, 0,
+    RedrawWindow32( pWnd->hwndSelf, &rect, 0,
                     RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_ERASE |
                     (event->count ? 0 : RDW_ERASENOW) );
 }
@@ -712,7 +705,7 @@
 /***********************************************************************
  *           EVENT_SelectionRequest
  */
-static void EVENT_SelectionRequest( HWND hwnd, XSelectionRequestEvent *event )
+static void EVENT_SelectionRequest( WND *pWnd, XSelectionRequestEvent *event )
 {
     XSelectionEvent result;
     Atom 	    rprop = None;
@@ -731,7 +724,7 @@
         else if(!CLIPBOARD_IsPresent(CF_TEXT)) rprop = None;
 	else{
             /* Don't worry if we can't open */
-	    BOOL couldOpen=OpenClipboard(hwnd);
+	    BOOL couldOpen=OpenClipboard( pWnd->hwndSelf );
 	    hText=GetClipboardData(CF_TEXT);
 	    text=GlobalLock16(hText);
 	    XChangeProperty(display,request,rprop,XA_STRING,
@@ -759,28 +752,28 @@
 /***********************************************************************
  *           EVENT_SelectionNotify
  */
-static void EVENT_SelectionNotify(HWND hwnd, XSelectionEvent *event)
+static void EVENT_SelectionNotify( XSelectionEvent *event )
 {
-    if(event->selection!=XA_PRIMARY)return;
-    if(event->target!=XA_STRING)CLIPBOARD_ReadSelection(0,None);
-    CLIPBOARD_ReadSelection(event->requestor,event->property);
+    if (event->selection != XA_PRIMARY) return;
+    if (event->target != XA_STRING) CLIPBOARD_ReadSelection( 0, None );
+    CLIPBOARD_ReadSelection( event->requestor, event->property );
 }
 
 
 /***********************************************************************
  *           EVENT_SelectionClear
  */
-static void EVENT_SelectionClear(HWND hwnd, XSelectionClearEvent *event)
+static void EVENT_SelectionClear( WND *pWnd, XSelectionClearEvent *event )
 {
-    if(event->selection!=XA_PRIMARY)return;
-    CLIPBOARD_ReleaseSelection(hwnd); 
+    if (event->selection != XA_PRIMARY) return;
+    CLIPBOARD_ReleaseSelection( pWnd->hwndSelf ); 
 }
 
 
 /**********************************************************************
  *           EVENT_ClientMessage
  */
-static void EVENT_ClientMessage (HWND hwnd, XClientMessageEvent *event )
+static void EVENT_ClientMessage( WND *pWnd, XClientMessageEvent *event )
 {
     static Atom wmProtocols = None;
     static Atom wmDeleteWindow = None;
@@ -796,7 +789,7 @@
 	dprintf_event( stddeb, "unrecognized ClientMessage\n" );
 	return;
     }
-    SendMessage16( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
+    SendMessage16( pWnd->hwndSelf, WM_SYSCOMMAND, SC_CLOSE, 0 );
 }
 
 
diff --git a/windows/mapping.c b/windows/mapping.c
index 9ae2b69..3d2be8a 100644
--- a/windows/mapping.c
+++ b/windows/mapping.c
@@ -48,6 +48,7 @@
     {
 	points->x = XDPTOLP( dc, points->x );
 	points->y = YDPTOLP( dc, points->y );
+        points++;
     }
     return TRUE;
 }
@@ -65,6 +66,7 @@
     {
 	points->x = XDPTOLP( dc, points->x );
 	points->y = YDPTOLP( dc, points->y );
+        points++;
     }
     return TRUE;
 }
@@ -82,6 +84,7 @@
     {
 	points->x = XLPTODP( dc, points->x );
 	points->y = YLPTODP( dc, points->y );
+        points++;
     }
     return TRUE;
 }
@@ -99,6 +102,7 @@
     {
 	points->x = XLPTODP( dc, points->x );
 	points->y = YLPTODP( dc, points->y );
+        points++;
     }
     return TRUE;
 }
diff --git a/windows/message.c b/windows/message.c
index 8ff4af2..c777683 100644
--- a/windows/message.c
+++ b/windows/message.c
@@ -29,10 +29,7 @@
 extern BYTE* 	KeyStateTable;				 /* event.c */
 extern WPARAM	lastEventChar;				 /* event.c */
 
-extern BOOL TIMER_CheckTimer( LONG *next, MSG16 *msg,
-			      HWND hwnd, BOOL remove );  /* timer.c */
-
-DWORD MSG_WineStartTicks;  				 /* Ticks at Wine startup */
+DWORD MSG_WineStartTicks; /* Ticks at Wine startup */
 
 static WORD doubleClickSpeed = 452;
 
@@ -68,9 +65,8 @@
 
       /* Find the window */
 
-    if (GetCapture())
+    if ((msg->hwnd = GetCapture()) != 0)
     {
-	msg->hwnd = GetCapture();
 	ScreenToClient16( msg->hwnd, &pt );
 	msg->lParam = MAKELONG( pt.x, pt.y );
         /* No need to further process the message */
@@ -80,6 +76,16 @@
     }
    
     hittest = WINPOS_WindowFromPoint( msg->pt, &pWnd );
+    if (pWnd->hmemTaskQ != GetTaskQueue(0))
+    {
+        /* Not for the current task */
+        MESSAGEQUEUE *queue = (MESSAGEQUEUE *)GlobalLock16( GetTaskQueue(0) );
+        if (queue) QUEUE_ClearWakeBit( queue, QS_MOUSE );
+        /* Wake up the other task */
+        queue = (MESSAGEQUEUE *)GlobalLock16( pWnd->hmemTaskQ );
+        if (queue) QUEUE_SetWakeBit( queue, QS_MOUSE );
+        return FALSE;
+    }
     msg->hwnd = pWnd->hwndSelf;
     if ((hittest != HTERROR) && mouseClick)
     {
@@ -172,6 +178,8 @@
  */
 static BOOL MSG_TranslateKeyboardMsg( MSG16 *msg, BOOL remove )
 {
+    WND *pWnd;
+
       /* Should check Ctrl-Esc and PrintScreen here */
 
     msg->hwnd = GetFocus();
@@ -185,6 +193,17 @@
 	if( msg->message < WM_SYSKEYDOWN )
 	    msg->message += WM_SYSKEYDOWN - WM_KEYDOWN;
     }
+    pWnd = WIN_FindWndPtr( msg->hwnd );
+    if (pWnd && (pWnd->hmemTaskQ != GetTaskQueue(0)))
+    {
+        /* Not for the current task */
+        MESSAGEQUEUE *queue = (MESSAGEQUEUE *)GlobalLock16( GetTaskQueue(0) );
+        if (queue) QUEUE_ClearWakeBit( queue, QS_KEY );
+        /* Wake up the other task */
+        queue = (MESSAGEQUEUE *)GlobalLock16( pWnd->hmemTaskQ );
+        if (queue) QUEUE_SetWakeBit( queue, QS_KEY );
+        return FALSE;
+    }
     return !HOOK_CallHooks( WH_KEYBOARD, remove ? HC_ACTION : HC_NOREMOVE,
                             msg->wParam, msg->lParam );
 }
@@ -202,7 +221,7 @@
     int i, pos = sysMsgQueue->nextMessage;
 
     /* If the queue is empty, attempt to fill it */
-    if (!sysMsgQueue->msgCount && XPending(display)) EVENT_WaitXEvent( 0 );
+    if (!sysMsgQueue->msgCount && XPending(display)) EVENT_WaitXEvent( FALSE );
 
     for (i = 0; i < sysMsgQueue->msgCount; i++, pos++)
     {
@@ -267,38 +286,6 @@
 
 
 /***********************************************************************
- *           MSG_GetHardwareMessage
- *
- * Like GetMessage(), but only return mouse and keyboard events.
- * Used internally for window moving and resizing. Mouse messages
- * are not translated.
- * Warning: msg->hwnd is always 0.
- */
-BOOL MSG_GetHardwareMessage( LPMSG16 msg )
-{
-#if 0
-    int pos;
-    XEvent event;
-    MESSAGEQUEUE *sysMsgQueue = QUEUE_GetSysQueue();
-
-    while(1)
-    {    
-	if ((pos = QUEUE_FindMsg( sysMsgQueue, 0, 0, 0 )) != -1)
-	{
-	    *msg = sysMsgQueue->messages[pos].msg;
-	    QUEUE_RemoveMsg( sysMsgQueue, pos );
-	    break;
-	}
-	XNextEvent( display, &event );
-	EVENT_ProcessEvent( &event );
-    }
-#endif
-    MSG_PeekMessage( msg, 0, WM_KEYFIRST, WM_MOUSELAST, PM_REMOVE, 0 );
-    return TRUE;
-}
-
-
-/***********************************************************************
  *           MSG_SendMessage
  *
  * Implementation of an inter-task SendMessage.
@@ -381,7 +368,6 @@
     int pos, mask;
     MESSAGEQUEUE *msgQueue;
     HQUEUE	  hQueue;
-    LONG nextExp;  /* Next timer expiration time */
 
 #ifdef CONFIG_IPC
     DDE_TestDDE(hwnd);	/* do we have dde handling in the window ?*/
@@ -490,10 +476,8 @@
         }
 	if ((msgQueue->wakeBits & mask) & QS_TIMER)
 	{
-	    if (TIMER_CheckTimer( &nextExp, msg, hwnd, flags & PM_REMOVE ))
-		break;  /* Got a timer msg */
+	    if (TIMER_GetTimerMsg(msg, hwnd, hQueue, flags & PM_REMOVE)) break;
 	}
-	else nextExp = -1;  /* No timeout needed */
 
         if (peek)
         {
@@ -792,24 +776,7 @@
  */
 void WaitMessage( void )
 {
-    MSG16 msg;
-    MESSAGEQUEUE *queue;
-    LONG nextExp = -1;  /* Next timer expiration time */
-
-#ifdef CONFIG_IPC
-    DDE_GetRemoteMessage();
-#endif  /* CONFIG_IPC */
-    
-    if (!(queue = (MESSAGEQUEUE *)GlobalLock16( GetTaskQueue(0) ))) return;
-    if ((queue->wPostQMsg) || 
-        (queue->wakeBits & (QS_SENDMESSAGE | QS_PAINT)) ||
-        (queue->msgCount) || (QUEUE_GetSysQueue()->msgCount) )
-        return;
-    if ((queue->wakeBits & QS_TIMER) && 
-        TIMER_CheckTimer( &nextExp, &msg, 0, FALSE))
-        return;
-    /* FIXME: (dde) must check DDE & X-events simultaneously */
-    EVENT_WaitXEvent( nextExp );
+    QUEUE_WaitBits( QS_ALLINPUT );
 }
 
 
diff --git a/windows/nonclient.c b/windows/nonclient.c
index a51ae71..67b052d 100644
--- a/windows/nonclient.c
+++ b/windows/nonclient.c
@@ -16,6 +16,7 @@
 #include "menu.h"
 #include "winpos.h"
 #include "scroll.h"
+#include "stackframe.h"
 #include "nonclient.h"
 #include "graphics.h"
 #include "queue.h"
@@ -907,7 +908,8 @@
 	SetCapture(hwnd);
 	while(!hittest)
 	{
-	    MSG_GetHardwareMessage( &msg );
+            MSG_InternalGetMessage( MAKE_SEGPTR(&msg), 0, 0, MSGF_SIZE,
+                                    PM_REMOVE, FALSE );
 	    switch(msg.message)
 	    {
 	    case WM_MOUSEMOVE:
@@ -1046,7 +1048,8 @@
     {
         int dx = 0, dy = 0;
 
-	MSG_GetHardwareMessage( &msg );
+        MSG_InternalGetMessage( MAKE_SEGPTR(&msg), 0, 0, MSGF_SIZE,
+                                PM_REMOVE, FALSE );
 
 	  /* Exit on button-up, Return, or Esc */
 	if ((msg.message == WM_LBUTTONUP) ||
@@ -1148,7 +1151,7 @@
     do
     {
 	BOOL oldstate = pressed;
-	MSG_GetHardwareMessage( &msg );
+        MSG_InternalGetMessage( MAKE_SEGPTR(&msg), 0, 0, 0, PM_REMOVE, FALSE );
 
 	pressed = (NC_HandleNCHitTest( hwnd, msg.pt ) == wParam);
 	if (pressed != oldstate)
diff --git a/windows/queue.c b/windows/queue.c
index eb457ed..c1406c1 100644
--- a/windows/queue.c
+++ b/windows/queue.c
@@ -16,6 +16,8 @@
 
 static HQUEUE hFirstQueue = 0;
 static HQUEUE hmemSysMsgQueue = 0;
+static MESSAGEQUEUE *pMouseQueue = NULL;  /* Queue for last mouse message */
+static MESSAGEQUEUE *pKbdQueue = NULL;    /* Queue for last kbd message */
 static HQUEUE hDoomedQueue = 0;
 static MESSAGEQUEUE *sysMsgQueue = NULL;
 
@@ -111,6 +113,7 @@
     if (!(hQueue = GlobalAlloc16( GMEM_FIXED | GMEM_ZEROINIT, queueSize )))
         return 0;
     msgQueue = (MESSAGEQUEUE *) GlobalLock16( hQueue );
+    msgQueue->self = hQueue;
     msgQueue->msgSize = sizeof(QMSG);
     msgQueue->queueSize = size;
     msgQueue->wWinVersion = pTask ? pTask->version : 0;
@@ -144,6 +147,7 @@
         pPrev = &msgQ->next;
     }
     if (*pPrev) *pPrev = msgQueue->next;
+    msgQueue->self = 0;
     GlobalFree16( hQueue );
     return 1;
 }
@@ -181,6 +185,8 @@
  */
 void QUEUE_SetWakeBit( MESSAGEQUEUE *queue, WORD bit )
 {
+    if (bit & QS_MOUSE) pMouseQueue = queue;
+    if (bit & QS_KEY) pKbdQueue = queue;
     queue->changeBits |= bit;
     queue->wakeBits   |= bit;
     if (queue->wakeMask & bit)
@@ -192,6 +198,16 @@
 
 
 /***********************************************************************
+ *           QUEUE_ClearWakeBit
+ */
+void QUEUE_ClearWakeBit( MESSAGEQUEUE *queue, WORD bit )
+{
+    queue->changeBits &= ~bit;
+    queue->wakeBits   &= ~bit;
+}
+
+
+/***********************************************************************
  *           QUEUE_WaitBits
  *
  * See "Windows Internals", p.447
@@ -233,16 +249,22 @@
     WPARAM wParam;
     LPARAM lParam;
     LRESULT result = 0;
+    HQUEUE oldSender;
 
     printf( "ReceiveMessage\n" );
     if (!(queue->wakeBits & QS_SENDMESSAGE)) return;
     if (!(senderQ = (MESSAGEQUEUE*)GlobalLock16( queue->hSendingTask))) return;
 
     /* Remove sending queue from the list */
+    oldSender                  = queue->InSendMessageHandle;
     queue->InSendMessageHandle = queue->hSendingTask;
     queue->hSendingTask        = senderQ->hPrevSendingTask;
     senderQ->hPrevSendingTask  = 0;
-    if (!queue->hSendingTask) queue->wakeBits &= ~QS_SENDMESSAGE;
+    if (!queue->hSendingTask)
+    {
+        queue->wakeBits &= ~QS_SENDMESSAGE;
+        queue->changeBits &= ~QS_SENDMESSAGE;
+    }
 
     /* Get the parameters from the sending task */
     hwnd   = senderQ->hWnd;
@@ -257,13 +279,21 @@
 
     /* Call the window procedure */
     /* FIXME: should we use CallWindowProc here? */
-    if (IsWindow( hwnd )) result = SendMessage16( hwnd, msg, wParam, lParam );
+    if (IsWindow( hwnd ))
+    {
+        DWORD extraInfo = queue->GetMessageExtraInfoVal;
+        queue->GetMessageExtraInfoVal = senderQ->GetMessageExtraInfoVal;
+        result = SendMessage16( hwnd, msg, wParam, lParam );
+        queue->GetMessageExtraInfoVal = extraInfo;  /* Restore extra info */
+    }
 
     printf( "ReceiveMessage: wnd proc %04x %04x %04x %08x ret = %08x\n",
             hwnd, msg, wParam, lParam, result );
 
     /* Return the result to the sender task */
     ReplyMessage( result );
+
+    queue->InSendMessageHandle = oldSender;
 }
 
 
@@ -373,15 +403,18 @@
 
     if (!(hwnd = GetSysModalWindow()))
     {
-        hwnd = (wakeBit == QS_KEY) ? GetFocus() : GetCapture();
-        if (!hwnd) hwnd = GetActiveWindow();
+        if (wakeBit == QS_KEY)
+        {
+            if (!(hwnd = GetFocus())) hwnd = GetActiveWindow();
+        }
+        else hwnd = GetCapture();
     }
     if (hwnd)
     {
         WND *wndPtr = WIN_FindWndPtr( hwnd );
         if (wndPtr) queue = (MESSAGEQUEUE *)GlobalLock16( wndPtr->hmemTaskQ );
     }
-    else
+    else if (!(queue = pMouseQueue))
     {
         hQueue = hFirstQueue;
         while (hQueue)
diff --git a/windows/timer.c b/windows/timer.c
index 53156d2..31e3940 100644
--- a/windows/timer.c
+++ b/windows/timer.c
@@ -19,7 +19,7 @@
     WORD             id;
     WORD             timeout;
     struct tagTIMER *next;
-    DWORD            expires;
+    DWORD            expires;  /* Next expiration, or 0 if already expired */
     FARPROC          proc;
 } TIMER;
 
@@ -70,6 +70,7 @@
     while (*ppTimer && (*ppTimer != pTimer)) ppTimer = &(*ppTimer)->next;
     if (*ppTimer) *ppTimer = pTimer->next;
     pTimer->next = NULL;
+    if (!pTimer->expires) QUEUE_DecTimerCount( pTimer->hq );
 }
 
 
@@ -81,7 +82,6 @@
 static void TIMER_ClearTimer( TIMER * pTimer )
 {
     TIMER_RemoveTimer( pTimer );
-    QUEUE_DecTimerCount( pTimer->hq );
     pTimer->hwnd    = 0;
     pTimer->msg     = 0;
     pTimer->id      = 0;
@@ -151,44 +151,58 @@
 
 			       
 /***********************************************************************
- *           TIMER_GetNextExp
+ *           TIMER_GetNextExpiration
  *
  * Return next timer expiration time, or -1 if none.
  */
-LONG TIMER_GetNextExp(void)
+LONG TIMER_GetNextExpiration(void)
 {
     return pNextTimer ? EXPIRE_TIME( pNextTimer, GetTickCount() ) : -1;
 }
 
 
 /***********************************************************************
- *           TIMER_CheckTimer
+ *           TIMER_ExpireTimers
  *
- * Check whether a timer has expired, and create a message if necessary.
- * Otherwise, return time until next timer expiration in 'next'.
- * If 'hwnd' is not NULL, only consider timers for this window.
- * If 'remove' is TRUE, remove all expired timers up to the returned one.
+ * Mark expired timers and wake the appropriate queues.
  */
-BOOL TIMER_CheckTimer( LONG *next, MSG16 *msg, HWND hwnd, BOOL remove )
+void TIMER_ExpireTimers(void)
 {
-    TIMER * pTimer = pNextTimer;
+    TIMER *pTimer = pNextTimer;
+    DWORD curTime = GetTickCount();
+
+    while (pTimer && !pTimer->expires)  /* Skip already expired timers */
+        pTimer = pTimer->next;
+    while (pTimer && (pTimer->expires <= curTime))
+    {
+        pTimer->expires = 0;
+        QUEUE_IncTimerCount( pTimer->hq );
+        pTimer = pTimer->next;
+    }
+}
+
+
+/***********************************************************************
+ *           TIMER_GetTimerMsg
+ *
+ * Build a message for an expired timer.
+ */
+BOOL TIMER_GetTimerMsg( MSG16 *msg, HWND hwnd, HQUEUE hQueue, BOOL remove )
+{
+    TIMER *pTimer = pNextTimer;
     DWORD curTime = GetTickCount();
 
     if (hwnd)  /* Find first timer for this window */
 	while (pTimer && (pTimer->hwnd != hwnd)) pTimer = pTimer->next;
+    else   /* Find first timer for this queue */
+	while (pTimer && (pTimer->hq != hQueue)) pTimer = pTimer->next;
 
-    if (!pTimer) *next = -1;
-    else *next = EXPIRE_TIME( pTimer, curTime );
-    if (*next != 0) return FALSE;  /* No timer expired */
+    if (!pTimer || (pTimer->expires > curTime)) return FALSE; /* No timer */
+    if (remove)	TIMER_RestartTimer( pTimer, curTime );  /* Restart it */
 
-    if (remove)	/* Restart all timers before pTimer, and then pTimer itself */
-    {
-	while (pNextTimer != pTimer) TIMER_RestartTimer( pNextTimer, curTime );
-	TIMER_RestartTimer( pTimer, curTime );
-    }
+    dprintf_timer( stddeb, "Timer expired: %04x, %04x, %04x, %08lx\n", 
+		   pTimer->hwnd, pTimer->msg, pTimer->id, (DWORD)pTimer->proc);
 
-    dprintf_timer(stddeb, "Timer expired: %p, %04x, %04x, %04x, %08lx\n", 
-		  pTimer, pTimer->hwnd, pTimer->msg, pTimer->id, (DWORD)pTimer->proc);
       /* Build the message */
     msg->hwnd    = pTimer->hwnd;
     msg->message = pTimer->msg;
@@ -218,10 +232,10 @@
             (pTimer->timeout != 0))
         {
               /* Got one: set new values and return */
-            pTimer->timeout = timeout;
-            pTimer->expires = GetTickCount() + timeout;
-            pTimer->proc    = proc;
             TIMER_RemoveTimer( pTimer );
+            pTimer->timeout = timeout;
+            pTimer->proc    = proc;
+            pTimer->expires = GetTickCount() + timeout;
             TIMER_InsertTimer( pTimer );
             return id;
         }
@@ -248,7 +262,6 @@
     dprintf_timer(stddeb, "Timer added: %p, %04x, %04x, %04x, %08lx\n", 
 		  pTimer, pTimer->hwnd, pTimer->msg, pTimer->id, (DWORD)pTimer->proc);
     TIMER_InsertTimer( pTimer );
-    QUEUE_IncTimerCount( pTimer->hq );
     if (!id)
 	return TRUE;
     else
diff --git a/windows/win.c b/windows/win.c
index 8c23889..c6e5188 100644
--- a/windows/win.c
+++ b/windows/win.c
@@ -347,7 +347,7 @@
     if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
     if (wndPtr->window) XDestroyWindow( display, wndPtr->window );
     if (wndPtr->class->style & CS_OWNDC) DCE_FreeDCE( wndPtr->hdce );
-    WIN_SetWndProc( wndPtr, (WNDPROC16)0, WIN_PROC_16 );
+    WIN_SetWndProc( wndPtr, (HANDLE32)0, WIN_PROC_16 );
     wndPtr->class->cWindows--;
     USER_HEAP_FREE( hwnd );
 }
@@ -426,7 +426,7 @@
     pWndDesktop->hProp             = 0;
     pWndDesktop->userdata          = 0;
 
-    EVENT_RegisterWindow( pWndDesktop->window, hwndDesktop );
+    EVENT_RegisterWindow( pWndDesktop );
     SendMessage32A( hwndDesktop, WM_NCCREATE, 0, 0 );
     if ((hdc = GetDC( hwndDesktop )) != 0)
     {
@@ -628,7 +628,7 @@
             Window win = WIN_GetXWindow( cs->hwndParent );
             if (win) XSetTransientForHint( display, wndPtr->window, win );
 	}
-        EVENT_RegisterWindow( wndPtr->window, hwnd );
+        EVENT_RegisterWindow( wndPtr );
     }
 
     /* Set the window menu */