Avoid returning an unlocked window pointer from WINPOS_WindowFromPoint.
[wine] / windows / queue.c
1 /*
2  * Message queues related functions
3  *
4  * Copyright 1993, 1994 Alexandre Julliard
5  */
6
7 #include <string.h>
8 #include <signal.h>
9 #include <assert.h>
10 #include "windef.h"
11 #include "wingdi.h"
12 #include "winerror.h"
13 #include "wine/winbase16.h"
14 #include "wine/winuser16.h"
15 #include "queue.h"
16 #include "win.h"
17 #include "user.h"
18 #include "hook.h"
19 #include "thread.h"
20 #include "debugtools.h"
21 #include "server.h"
22 #include "spy.h"
23
24 DEFAULT_DEBUG_CHANNEL(msg);
25
26
27 static HQUEUE16 hExitingQueue = 0;
28 static PERQUEUEDATA *pQDataWin16 = NULL;  /* Global perQData for Win16 tasks */
29
30 HQUEUE16 hActiveQueue = 0;
31
32
33 /***********************************************************************
34  *           PERQDATA_Addref
35  *
36  * Increment reference count for the PERQUEUEDATA instance
37  * Returns reference count for debugging purposes
38  */
39 static void PERQDATA_Addref( PERQUEUEDATA *pQData )
40 {
41     assert(pQData != 0 );
42     TRACE_(msg)("(): current refcount %lu ...\n", pQData->ulRefCount);
43
44     InterlockedIncrement( &pQData->ulRefCount );
45 }
46
47
48 /***********************************************************************
49  *           PERQDATA_Release
50  *
51  * Release a reference to a PERQUEUEDATA instance.
52  * Destroy the instance if no more references exist
53  * Returns reference count for debugging purposes
54  */
55 static void PERQDATA_Release( PERQUEUEDATA *pQData )
56 {
57     assert(pQData != 0 );
58     TRACE_(msg)("(): current refcount %lu ...\n",
59           (LONG)pQData->ulRefCount );
60
61     if (!InterlockedDecrement( &pQData->ulRefCount ))
62     {
63         DeleteCriticalSection( &pQData->cSection );
64
65         TRACE_(msg)("(): deleting PERQUEUEDATA instance ...\n" );
66
67         /* Deleting our global 16 bit perQData? */
68         if ( pQData == pQDataWin16 ) pQDataWin16 = 0;
69
70         /* Free the PERQUEUEDATA instance */
71         HeapFree( GetProcessHeap(), 0, pQData );
72     }
73 }
74
75
76 /***********************************************************************
77  *           PERQDATA_CreateInstance
78  *
79  * Creates an instance of a reference counted PERQUEUEDATA element
80  * for the message queue. perQData is stored globally for 16 bit tasks.
81  *
82  * Note: We don't implement perQdata exactly the same way Windows does.
83  * Each perQData element is reference counted since it may be potentially
84  * shared by multiple message Queues (via AttachThreadInput).
85  * We only store the current values for Active, Capture and focus windows
86  * currently.
87  */
88 static PERQUEUEDATA * PERQDATA_CreateInstance(void)
89 {
90     PERQUEUEDATA *pQData;
91     
92     BOOL16 bIsWin16 = 0;
93     
94     TRACE_(msg)("()\n");
95
96     /* Share a single instance of perQData for all 16 bit tasks */
97     if ( ( bIsWin16 = !(NtCurrentTeb()->tibflags & TEBF_WIN32) ) )
98     {
99         /* If previously allocated, just bump up ref count */
100         if ( pQDataWin16 )
101         {
102             PERQDATA_Addref( pQDataWin16 );
103             return pQDataWin16;
104         }
105     }
106
107     /* Allocate PERQUEUEDATA from the system heap */
108     if (!( pQData = (PERQUEUEDATA *) HeapAlloc( GetProcessHeap(), 0,
109                                                     sizeof(PERQUEUEDATA) ) ))
110         return 0;
111
112     /* Initialize */
113     pQData->hWndCapture = pQData->hWndFocus = pQData->hWndActive = 0;
114     pQData->ulRefCount = 1;
115     pQData->nCaptureHT = HTCLIENT;
116
117     /* Note: We have an independent critical section for the per queue data
118      * since this may be shared by different threads. see AttachThreadInput()
119      */
120     InitializeCriticalSection( &pQData->cSection );
121     /* FIXME: not all per queue data critical sections should be global */
122     MakeCriticalSectionGlobal( &pQData->cSection );
123
124     /* Save perQData globally for 16 bit tasks */
125     if ( bIsWin16 )
126         pQDataWin16 = pQData;
127         
128     return pQData;
129 }
130
131
132 /***********************************************************************
133  *           PERQDATA_GetFocusWnd
134  *
135  * Get the focus hwnd member in a threadsafe manner
136  */
137 HWND PERQDATA_GetFocusWnd( PERQUEUEDATA *pQData )
138 {
139     HWND hWndFocus;
140     assert(pQData != 0 );
141
142     EnterCriticalSection( &pQData->cSection );
143     hWndFocus = pQData->hWndFocus;
144     LeaveCriticalSection( &pQData->cSection );
145
146     return hWndFocus;
147 }
148
149
150 /***********************************************************************
151  *           PERQDATA_SetFocusWnd
152  *
153  * Set the focus hwnd member in a threadsafe manner
154  */
155 HWND PERQDATA_SetFocusWnd( PERQUEUEDATA *pQData, HWND hWndFocus )
156 {
157     HWND hWndFocusPrv;
158     assert(pQData != 0 );
159
160     EnterCriticalSection( &pQData->cSection );
161     hWndFocusPrv = pQData->hWndFocus;
162     pQData->hWndFocus = hWndFocus;
163     LeaveCriticalSection( &pQData->cSection );
164
165     return hWndFocusPrv;
166 }
167
168
169 /***********************************************************************
170  *           PERQDATA_GetActiveWnd
171  *
172  * Get the active hwnd member in a threadsafe manner
173  */
174 HWND PERQDATA_GetActiveWnd( PERQUEUEDATA *pQData )
175 {
176     HWND hWndActive;
177     assert(pQData != 0 );
178
179     EnterCriticalSection( &pQData->cSection );
180     hWndActive = pQData->hWndActive;
181     LeaveCriticalSection( &pQData->cSection );
182
183     return hWndActive;
184 }
185
186
187 /***********************************************************************
188  *           PERQDATA_SetActiveWnd
189  *
190  * Set the active focus hwnd member in a threadsafe manner
191  */
192 HWND PERQDATA_SetActiveWnd( PERQUEUEDATA *pQData, HWND hWndActive )
193 {
194     HWND hWndActivePrv;
195     assert(pQData != 0 );
196
197     EnterCriticalSection( &pQData->cSection );
198     hWndActivePrv = pQData->hWndActive;
199     pQData->hWndActive = hWndActive;
200     LeaveCriticalSection( &pQData->cSection );
201
202     return hWndActivePrv;
203 }
204
205
206 /***********************************************************************
207  *           PERQDATA_GetCaptureWnd
208  *
209  * Get the capture hwnd member in a threadsafe manner
210  */
211 HWND PERQDATA_GetCaptureWnd( INT *hittest )
212 {
213     MESSAGEQUEUE *queue;
214     PERQUEUEDATA *pQData;
215     HWND hWndCapture;
216
217     if (!(queue = QUEUE_Lock( GetFastQueue16() ))) return 0;
218     pQData = queue->pQData;
219
220     EnterCriticalSection( &pQData->cSection );
221     hWndCapture = pQData->hWndCapture;
222     *hittest = pQData->nCaptureHT;
223     LeaveCriticalSection( &pQData->cSection );
224
225     QUEUE_Unlock( queue );
226     return hWndCapture;
227 }
228
229
230 /***********************************************************************
231  *           PERQDATA_SetCaptureWnd
232  *
233  * Set the capture hwnd member in a threadsafe manner
234  */
235 HWND PERQDATA_SetCaptureWnd( HWND hWndCapture, INT hittest )
236 {
237     MESSAGEQUEUE *queue;
238     PERQUEUEDATA *pQData;
239     HWND hWndCapturePrv;
240
241     if (!(queue = QUEUE_Lock( GetFastQueue16() ))) return 0;
242     pQData = queue->pQData;
243
244     EnterCriticalSection( &pQData->cSection );
245     hWndCapturePrv = pQData->hWndCapture;
246     pQData->hWndCapture = hWndCapture;
247     pQData->nCaptureHT = hittest;
248     LeaveCriticalSection( &pQData->cSection );
249
250     QUEUE_Unlock( queue );
251     return hWndCapturePrv;
252 }
253
254
255
256 /***********************************************************************
257  *           QUEUE_Lock
258  *
259  * Function for getting a 32 bit pointer on queue structure. For thread
260  * safeness programmers should use this function instead of GlobalLock to
261  * retrieve a pointer on the structure. QUEUE_Unlock should also be called
262  * when access to the queue structure is not required anymore.
263  */
264 MESSAGEQUEUE *QUEUE_Lock( HQUEUE16 hQueue )
265 {
266     MESSAGEQUEUE *queue;
267
268     HeapLock( GetProcessHeap() );  /* FIXME: a bit overkill */
269     queue = GlobalLock16( hQueue );
270     if ( !queue || (queue->magic != QUEUE_MAGIC) )
271     {
272         HeapUnlock( GetProcessHeap() );
273         return NULL;
274     }
275
276     queue->lockCount++;
277     HeapUnlock( GetProcessHeap() );
278     return queue;
279 }
280
281
282 /***********************************************************************
283  *           QUEUE_Unlock
284  *
285  * Use with QUEUE_Lock to get a thread safe access to message queue
286  * structure
287  */
288 void QUEUE_Unlock( MESSAGEQUEUE *queue )
289 {
290     if (queue)
291     {
292         HeapLock( GetProcessHeap() );  /* FIXME: a bit overkill */
293
294         if ( --queue->lockCount == 0 )
295         {
296             if (queue->server_queue)
297                 CloseHandle( queue->server_queue );
298             GlobalFree16( queue->self );
299         }
300     
301         HeapUnlock( GetProcessHeap() );
302     }
303 }
304
305
306 /***********************************************************************
307  *           QUEUE_IsExitingQueue
308  */
309 BOOL QUEUE_IsExitingQueue( HQUEUE16 hQueue )
310 {
311     return (hExitingQueue && (hQueue == hExitingQueue));
312 }
313
314
315 /***********************************************************************
316  *           QUEUE_SetExitingQueue
317  */
318 void QUEUE_SetExitingQueue( HQUEUE16 hQueue )
319 {
320     hExitingQueue = hQueue;
321 }
322
323
324 /***********************************************************************
325  *           QUEUE_CreateMsgQueue
326  *
327  * Creates a message queue. Doesn't link it into queue list!
328  */
329 static HQUEUE16 QUEUE_CreateMsgQueue( BOOL16 bCreatePerQData )
330 {
331     HQUEUE16 hQueue;
332     HANDLE handle;
333     MESSAGEQUEUE * msgQueue;
334
335     TRACE_(msg)("(): Creating message queue...\n");
336
337     if (!(hQueue = GlobalAlloc16( GMEM_FIXED | GMEM_ZEROINIT,
338                                   sizeof(MESSAGEQUEUE) )))
339         return 0;
340
341     msgQueue = (MESSAGEQUEUE *) GlobalLock16( hQueue );
342     if ( !msgQueue )
343         return 0;
344
345     if (bCreatePerQData)
346     {
347         SERVER_START_REQ( get_msg_queue )
348         {
349             SERVER_CALL_ERR();
350             handle = req->handle;
351         }
352         SERVER_END_REQ;
353         if (!handle)
354         {
355             ERR_(msg)("Cannot get thread queue");
356             GlobalFree16( hQueue );
357             return 0;
358         }
359         msgQueue->server_queue = handle;
360     }
361
362     msgQueue->self = hQueue;
363     msgQueue->lockCount = 1;
364     msgQueue->magic = QUEUE_MAGIC;
365     
366     /* Create and initialize our per queue data */
367     msgQueue->pQData = bCreatePerQData ? PERQDATA_CreateInstance() : NULL;
368     
369     return hQueue;
370 }
371
372
373 /***********************************************************************
374  *           QUEUE_DeleteMsgQueue
375  *
376  * Unlinks and deletes a message queue.
377  *
378  * Note: We need to mask asynchronous events to make sure PostMessage works
379  * even in the signal handler.
380  */
381 BOOL QUEUE_DeleteMsgQueue( HQUEUE16 hQueue )
382 {
383     MESSAGEQUEUE * msgQueue = QUEUE_Lock(hQueue);
384
385     TRACE_(msg)("(): Deleting message queue %04x\n", hQueue);
386
387     if (!hQueue || !msgQueue)
388     {
389         ERR_(msg)("invalid argument.\n");
390         return 0;
391     }
392
393     msgQueue->magic = 0;
394
395     if( hActiveQueue == hQueue ) hActiveQueue = 0;
396     if (hExitingQueue == hQueue) hExitingQueue = 0;
397
398     HeapLock( GetProcessHeap() );  /* FIXME: a bit overkill */
399
400     /* Release per queue data if present */
401     if ( msgQueue->pQData )
402     {
403         PERQDATA_Release( msgQueue->pQData );
404         msgQueue->pQData = 0;
405     }
406
407     msgQueue->self = 0;
408
409     HeapUnlock( GetProcessHeap() );
410
411     /* free up resource used by MESSAGEQUEUE structure */
412     msgQueue->lockCount--;
413     QUEUE_Unlock( msgQueue );
414     
415     return 1;
416 }
417
418
419 /***********************************************************************
420  *           handle_sent_message
421  *
422  * Handle the reception of a sent message by calling the corresponding window proc
423  */
424 static void handle_sent_message( QMSG *msg )
425 {
426     LRESULT result = 0;
427     MESSAGEQUEUE *queue = QUEUE_Lock( GetFastQueue16() );
428     DWORD extraInfo = queue->GetMessageExtraInfoVal; /* save ExtraInfo */
429     WND *wndPtr = WIN_FindWndPtr( msg->msg.hwnd );
430
431     TRACE( "got hwnd %x msg %x (%s) wp %x lp %lx\n",
432            msg->msg.hwnd, msg->msg.message, SPY_GetMsgName(msg->msg.message),
433            msg->msg.wParam, msg->msg.lParam );
434
435     queue->GetMessageExtraInfoVal = msg->extraInfo;
436
437     /* call the right version of CallWindowProcXX */
438     switch(msg->type)
439     {
440     case QMSG_WIN16:
441         result = CallWindowProc16( (WNDPROC16)wndPtr->winproc,
442                                    (HWND16) msg->msg.hwnd,
443                                    (UINT16) msg->msg.message,
444                                    LOWORD(msg->msg.wParam),
445                                    msg->msg.lParam );
446         break;
447     case QMSG_WIN32A:
448         result = CallWindowProcA( wndPtr->winproc, msg->msg.hwnd, msg->msg.message,
449                                   msg->msg.wParam, msg->msg.lParam );
450         break;
451     case QMSG_WIN32W:
452         result = CallWindowProcW( wndPtr->winproc, msg->msg.hwnd, msg->msg.message,
453                                   msg->msg.wParam, msg->msg.lParam );
454         break;
455     }
456
457     queue->GetMessageExtraInfoVal = extraInfo;  /* Restore extra info */
458     WIN_ReleaseWndPtr(wndPtr);
459     QUEUE_Unlock( queue );
460
461     SERVER_START_REQ( reply_message )
462     {
463         req->result = result;
464         req->remove = 1;
465         SERVER_CALL();
466     }
467     SERVER_END_REQ;
468 }
469
470
471 /***********************************************************************
472  *           process_sent_messages
473  *
474  * Process all pending sent messages
475  */
476 static void process_sent_messages(void)
477 {
478     QMSG msg;
479     unsigned int res;
480
481     for (;;)
482     {
483         SERVER_START_REQ( get_message )
484         {
485             req->flags = GET_MSG_REMOVE | GET_MSG_SENT_ONLY;
486             req->get_win   = 0;
487             req->get_first = 0;
488             req->get_last  = ~0;
489             if (!(res = SERVER_CALL()))
490             {
491                 msg.type        = req->type;
492                 msg.msg.hwnd    = req->win;
493                 msg.msg.message = req->msg;
494                 msg.msg.wParam  = req->wparam;
495                 msg.msg.lParam  = req->lparam;
496                 msg.msg.time    = req->time;
497                 msg.msg.pt.x    = req->x;
498                 msg.msg.pt.y    = req->y;
499                 msg.extraInfo   = req->info;
500             }
501         }
502         SERVER_END_REQ;
503
504         if (res) break;
505         handle_sent_message( &msg );
506     }
507 }
508
509
510
511 /***********************************************************************
512  *           QUEUE_WaitBits
513  *
514  * See "Windows Internals", p.447
515  *
516  * return values:
517  *    0 if exit with timeout
518  *    1 otherwise
519  */
520 int QUEUE_WaitBits( WORD bits, DWORD timeout )
521 {
522     MESSAGEQUEUE *queue;
523     HQUEUE16 hQueue;
524
525     TRACE_(msg)("q %04x waiting for %04x\n", GetFastQueue16(), bits);
526
527     hQueue = GetFastQueue16();
528     if (!(queue = QUEUE_Lock( hQueue ))) return 0;
529     
530     for (;;)
531     {
532         unsigned int wake_bits = 0, changed_bits = 0;
533         DWORD dwlc;
534
535         SERVER_START_REQ( set_queue_mask )
536         {
537             req->wake_mask    = QS_SENDMESSAGE;
538             req->changed_mask = bits | QS_SENDMESSAGE;
539             req->skip_wait    = 1;
540             if (!SERVER_CALL())
541             {
542                 wake_bits    = req->wake_bits;
543                 changed_bits = req->changed_bits;
544             }
545         }
546         SERVER_END_REQ;
547
548         if (changed_bits & bits)
549         {
550             /* One of the bits is set; we can return */
551             QUEUE_Unlock( queue );
552             return 1;
553         }
554         if (wake_bits & QS_SENDMESSAGE)
555         {
556             /* Process the sent message immediately */
557             process_sent_messages();
558             continue;  /* nested sm crux */
559         }
560
561         TRACE_(msg)("(%04x) mask=%08x, bits=%08x, changed=%08x, waiting\n",
562                     queue->self, bits, wake_bits, changed_bits );
563
564         ReleaseThunkLock( &dwlc );
565         if (dwlc) TRACE_(msg)("had win16 lock\n");
566
567         if (USER_Driver.pMsgWaitForMultipleObjectsEx)
568             USER_Driver.pMsgWaitForMultipleObjectsEx( 1, &queue->server_queue, timeout, 0, 0 );
569         else
570             WaitForSingleObject( queue->server_queue, timeout );
571         if (dwlc) RestoreThunkLock( dwlc );
572     }
573 }
574
575
576 /***********************************************************************
577  *           QUEUE_FindMsg
578  *
579  * Find a message matching the given parameters. Return FALSE if none available.
580  */
581 BOOL QUEUE_FindMsg( HWND hwnd, UINT first, UINT last, BOOL remove, QMSG *msg )
582 {
583     BOOL ret = FALSE;
584
585     if (!first && !last) last = ~0;
586
587     for (;;)
588     {
589         SERVER_START_REQ( get_message )
590         {
591             req->flags     = remove ? GET_MSG_REMOVE : 0;
592             req->get_win   = hwnd;
593             req->get_first = first;
594             req->get_last  = last;
595             if ((ret = !SERVER_CALL()))
596             {
597                 msg->kind        = req->kind;
598                 msg->type        = req->type;
599                 msg->msg.hwnd    = req->win;
600                 msg->msg.message = req->msg;
601                 msg->msg.wParam  = req->wparam;
602                 msg->msg.lParam  = req->lparam;
603                 msg->msg.time    = req->time;
604                 msg->msg.pt.x    = req->x;
605                 msg->msg.pt.y    = req->y;
606                 msg->extraInfo   = req->info;
607             }
608         }
609         SERVER_END_REQ;
610
611         if (!ret || (msg->kind != SEND_MESSAGE)) break;
612         handle_sent_message( msg );
613     }
614
615     if (ret) TRACE( "got hwnd %x msg %x (%s) wp %x lp %lx\n",
616                     msg->msg.hwnd, msg->msg.message, SPY_GetMsgName(msg->msg.message),
617                     msg->msg.wParam, msg->msg.lParam );
618     return ret;
619 }
620
621
622
623 /***********************************************************************
624  *           QUEUE_CleanupWindow
625  *
626  * Cleanup the queue to account for a window being deleted.
627  */
628 void QUEUE_CleanupWindow( HWND hwnd )
629 {
630     SERVER_START_REQ( cleanup_window_queue )
631     {
632         req->win = hwnd;
633         SERVER_CALL();
634     }
635     SERVER_END_REQ;
636 }
637
638
639 /***********************************************************************
640  *           QUEUE_GetQueueTask
641  */
642 HTASK16 QUEUE_GetQueueTask( HQUEUE16 hQueue )
643 {
644     HTASK16 hTask = 0;
645     
646     MESSAGEQUEUE *queue = QUEUE_Lock( hQueue );
647
648     if (queue)
649     {
650         hTask = queue->teb->htask16;
651         QUEUE_Unlock( queue );
652     }
653
654     return hTask;
655 }
656
657
658 /***********************************************************************
659  *              PostQuitMessage (USER.6)
660  */
661 void WINAPI PostQuitMessage16( INT16 exitCode )
662 {
663     PostQuitMessage( exitCode );
664 }
665
666
667 /***********************************************************************
668  *              PostQuitMessage (USER32.@)
669  *
670  * PostQuitMessage() posts a message to the system requesting an
671  * application to terminate execution. As a result of this function,
672  * the WM_QUIT message is posted to the application, and
673  * PostQuitMessage() returns immediately.  The exitCode parameter
674  * specifies an application-defined exit code, which appears in the
675  * _wParam_ parameter of the WM_QUIT message posted to the application.  
676  *
677  * CONFORMANCE
678  *
679  *  ECMA-234, Win32
680  */
681 void WINAPI PostQuitMessage( INT exitCode )
682 {
683     PostThreadMessageW( GetCurrentThreadId(), WM_QUIT, exitCode, 0 );
684 }
685
686
687 /***********************************************************************
688  *              GetWindowTask (USER.224)
689  */
690 HTASK16 WINAPI GetWindowTask16( HWND16 hwnd )
691 {
692     HTASK16 retvalue;
693     WND *wndPtr = WIN_FindWndPtr( hwnd );
694
695     if (!wndPtr) return 0;
696     retvalue = QUEUE_GetQueueTask( wndPtr->hmemTaskQ );
697     WIN_ReleaseWndPtr(wndPtr);
698     return retvalue;
699 }
700
701 /***********************************************************************
702  *              GetWindowThreadProcessId (USER32.@)
703  */
704 DWORD WINAPI GetWindowThreadProcessId( HWND hwnd, LPDWORD process )
705 {
706     DWORD retvalue;
707     MESSAGEQUEUE *queue;
708
709     WND *wndPtr = WIN_FindWndPtr( hwnd );
710     if (!wndPtr) return 0;
711
712     queue = QUEUE_Lock( wndPtr->hmemTaskQ );
713     WIN_ReleaseWndPtr(wndPtr);
714
715     if (!queue) return 0;
716
717     if ( process ) *process = (DWORD)queue->teb->pid;
718     retvalue = (DWORD)queue->teb->tid;
719
720     QUEUE_Unlock( queue );
721     return retvalue;
722 }
723
724
725 /***********************************************************************
726  *              SetMessageQueue (USER.266)
727  */
728 BOOL16 WINAPI SetMessageQueue16( INT16 size )
729 {
730     return SetMessageQueue( size );
731 }
732
733
734 /***********************************************************************
735  *              SetMessageQueue (USER32.@)
736  */
737 BOOL WINAPI SetMessageQueue( INT size )
738 {
739     /* now obsolete the message queue will be expanded dynamically
740      as necessary */
741
742     /* access the queue to create it if it's not existing */
743     GetFastQueue16();
744
745     return TRUE;
746 }
747
748 /***********************************************************************
749  *              InitThreadInput (USER.409)
750  */
751 HQUEUE16 WINAPI InitThreadInput16( WORD unknown, WORD flags )
752 {
753     HQUEUE16 hQueue;
754     MESSAGEQUEUE *queuePtr;
755
756     TEB *teb = NtCurrentTeb();
757
758     if (!teb)
759         return 0;
760
761     hQueue = teb->queue;
762     
763     if ( !hQueue )
764     {
765         /* Create thread message queue */
766         if( !(hQueue = QUEUE_CreateMsgQueue( TRUE )))
767         {
768             ERR_(msg)("failed!\n");
769             return FALSE;
770         }
771         
772         /* Link new queue into list */
773         queuePtr = QUEUE_Lock( hQueue );
774         queuePtr->teb = NtCurrentTeb();
775
776         HeapLock( GetProcessHeap() );  /* FIXME: a bit overkill */
777         SetThreadQueue16( 0, hQueue );
778         teb->queue = hQueue;
779         HeapUnlock( GetProcessHeap() );
780         
781         QUEUE_Unlock( queuePtr );
782     }
783
784     return hQueue;
785 }
786
787 /***********************************************************************
788  *              GetQueueStatus (USER.334)
789  */
790 DWORD WINAPI GetQueueStatus16( UINT16 flags )
791 {
792     return GetQueueStatus( flags );
793 }
794
795 /***********************************************************************
796  *              GetQueueStatus (USER32.@)
797  */
798 DWORD WINAPI GetQueueStatus( UINT flags )
799 {
800     DWORD ret = 0;
801
802     SERVER_START_REQ( get_queue_status )
803     {
804         req->clear = 1;
805         SERVER_CALL();
806         ret = MAKELONG( req->changed_bits & flags, req->wake_bits & flags );
807     }
808     SERVER_END_REQ;
809     return ret;
810 }
811
812
813 /***********************************************************************
814  *              GetInputState (USER.335)
815  */
816 BOOL16 WINAPI GetInputState16(void)
817 {
818     return GetInputState();
819 }
820
821 /***********************************************************************
822  *              GetInputState   (USER32.@)
823  */
824 BOOL WINAPI GetInputState(void)
825 {
826     DWORD ret = 0;
827
828     SERVER_START_REQ( get_queue_status )
829     {
830         req->clear = 0;
831         SERVER_CALL();
832         ret = req->wake_bits & (QS_KEY | QS_MOUSEBUTTON);
833     }
834     SERVER_END_REQ;
835     return ret;
836 }
837
838 /***********************************************************************
839  *              WaitForInputIdle (USER32.@)
840  */
841 DWORD WINAPI WaitForInputIdle (HANDLE hProcess, DWORD dwTimeOut)
842 {
843     DWORD cur_time, ret;
844     HANDLE idle_event = -1;
845
846     SERVER_START_REQ( wait_input_idle )
847     {
848         req->handle = hProcess;
849         req->timeout = dwTimeOut;
850         if (!(ret = SERVER_CALL_ERR())) idle_event = req->event;
851     }
852     SERVER_END_REQ;
853     if (ret) return 0xffffffff;  /* error */
854     if (!idle_event) return 0;  /* no event to wait on */
855
856     cur_time = GetTickCount();
857
858     TRACE_(msg)("waiting for %x\n", idle_event );
859     while ( dwTimeOut > GetTickCount() - cur_time || dwTimeOut == INFINITE ) 
860     {
861         ret = MsgWaitForMultipleObjects ( 1, &idle_event, FALSE, dwTimeOut, QS_SENDMESSAGE );
862         if ( ret == ( WAIT_OBJECT_0 + 1 )) 
863         {
864             process_sent_messages();
865             continue;
866         }
867         if ( ret == WAIT_TIMEOUT || ret == 0xFFFFFFFF ) 
868         {
869             TRACE_(msg)("timeout or error\n");
870             return ret;
871         }
872         else 
873         {
874             TRACE_(msg)("finished\n");
875             return 0;
876         }
877     }
878
879     return WAIT_TIMEOUT;
880 }
881
882 /***********************************************************************
883  *              UserYield (USER.332)
884  *              UserYield16 (USER32.@)
885  */
886 void WINAPI UserYield16(void)
887 {
888     /* Handle sent messages */
889     process_sent_messages();
890
891     /* Yield */
892     OldYield16();
893
894     /* Handle sent messages again */
895     process_sent_messages();
896 }
897
898 /***********************************************************************
899  *              GetMessagePos (USER.119) (USER32.@)
900  * 
901  * The GetMessagePos() function returns a long value representing a
902  * cursor position, in screen coordinates, when the last message
903  * retrieved by the GetMessage() function occurs. The x-coordinate is
904  * in the low-order word of the return value, the y-coordinate is in
905  * the high-order word. The application can use the MAKEPOINT()
906  * macro to obtain a POINT structure from the return value. 
907  *
908  * For the current cursor position, use GetCursorPos().
909  *
910  * RETURNS
911  *
912  * Cursor position of last message on success, zero on failure.
913  *
914  * CONFORMANCE
915  *
916  * ECMA-234, Win32
917  *
918  */
919 DWORD WINAPI GetMessagePos(void)
920 {
921     MESSAGEQUEUE *queue;
922     DWORD ret;
923
924     if (!(queue = QUEUE_Lock( GetFastQueue16() ))) return 0;
925     ret = queue->GetMessagePosVal;
926     QUEUE_Unlock( queue );
927
928     return ret;
929 }
930
931
932 /***********************************************************************
933  *              GetMessageTime (USER.120) (USER32.@)
934  *
935  * GetMessageTime() returns the message time for the last message
936  * retrieved by the function. The time is measured in milliseconds with
937  * the same offset as GetTickCount().
938  *
939  * Since the tick count wraps, this is only useful for moderately short
940  * relative time comparisons.
941  *
942  * RETURNS
943  *
944  * Time of last message on success, zero on failure.
945  *
946  * CONFORMANCE
947  *
948  * ECMA-234, Win32
949  *  
950  */
951 LONG WINAPI GetMessageTime(void)
952 {
953     MESSAGEQUEUE *queue;
954     LONG ret;
955
956     if (!(queue = QUEUE_Lock( GetFastQueue16() ))) return 0;
957     ret = queue->GetMessageTimeVal;
958     QUEUE_Unlock( queue );
959     
960     return ret;
961 }
962
963
964 /***********************************************************************
965  *              GetMessageExtraInfo (USER.288) (USER32.@)
966  */
967 LONG WINAPI GetMessageExtraInfo(void)
968 {
969     MESSAGEQUEUE *queue;
970     LONG ret;
971
972     if (!(queue = QUEUE_Lock( GetFastQueue16() ))) return 0;
973     ret = queue->GetMessageExtraInfoVal;
974     QUEUE_Unlock( queue );
975
976     return ret;
977 }
978
979
980 /**********************************************************************
981  *              AttachThreadInput (USER32.@) Attaches input of 1 thread to other
982  *
983  * Attaches the input processing mechanism of one thread to that of
984  * another thread.
985  *
986  * RETURNS
987  *    Success: TRUE
988  *    Failure: FALSE
989  *
990  * TODO:
991  *    1. Reset the Key State (currenly per thread key state is not maintained)
992  */
993 BOOL WINAPI AttachThreadInput( 
994     DWORD idAttach,   /* [in] Thread to attach */
995     DWORD idAttachTo, /* [in] Thread to attach to */
996     BOOL fAttach)   /* [in] Attach or detach */
997 {
998     MESSAGEQUEUE *pSrcMsgQ = 0, *pTgtMsgQ = 0;
999     BOOL16 bRet = 0;
1000
1001     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1002
1003     /* A thread cannot attach to itself */
1004     if ( idAttach == idAttachTo )
1005         goto CLEANUP;
1006
1007     /* According to the docs this method should fail if a
1008      * "Journal record" hook is installed. (attaches all input queues together)
1009      */
1010     if ( HOOK_IsHooked( WH_JOURNALRECORD ) )
1011         goto CLEANUP;
1012         
1013     /* Retrieve message queues corresponding to the thread id's */
1014     pTgtMsgQ = QUEUE_Lock( GetThreadQueue16( idAttach ) );
1015     pSrcMsgQ = QUEUE_Lock( GetThreadQueue16( idAttachTo ) );
1016
1017     /* Ensure we have message queues and that Src and Tgt threads
1018      * are not system threads.
1019      */
1020     if ( !pSrcMsgQ || !pTgtMsgQ || !pSrcMsgQ->pQData || !pTgtMsgQ->pQData )
1021         goto CLEANUP;
1022
1023     if (fAttach)   /* Attach threads */
1024     {
1025         /* Only attach if currently detached  */
1026         if ( pTgtMsgQ->pQData != pSrcMsgQ->pQData )
1027         {
1028             /* First release the target threads perQData */
1029             PERQDATA_Release( pTgtMsgQ->pQData );
1030         
1031             /* Share a reference to the source threads perQDATA */
1032             PERQDATA_Addref( pSrcMsgQ->pQData );
1033             pTgtMsgQ->pQData = pSrcMsgQ->pQData;
1034         }
1035     }
1036     else    /* Detach threads */
1037     {
1038         /* Only detach if currently attached */
1039         if ( pTgtMsgQ->pQData == pSrcMsgQ->pQData )
1040         {
1041             /* First release the target threads perQData */
1042             PERQDATA_Release( pTgtMsgQ->pQData );
1043         
1044             /* Give the target thread its own private perQDATA once more */
1045             pTgtMsgQ->pQData = PERQDATA_CreateInstance();
1046         }
1047     }
1048
1049     /* TODO: Reset the Key State */
1050
1051     bRet = 1;      /* Success */
1052     
1053 CLEANUP:
1054
1055     /* Unlock the queues before returning */
1056     if ( pSrcMsgQ )
1057         QUEUE_Unlock( pSrcMsgQ );
1058     if ( pTgtMsgQ )
1059         QUEUE_Unlock( pTgtMsgQ );
1060     
1061     return bRet;
1062 }