Authors: Guy Albertelli <guy@codeweavers.com>, Mike McCormack <mike_mccormack@start...
[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 "wine/server.h"
22 #include "spy.h"
23
24 DEFAULT_DEBUG_CHANNEL(msg);
25
26
27 static PERQUEUEDATA *pQDataWin16 = NULL;  /* Global perQData for Win16 tasks */
28
29 HQUEUE16 hActiveQueue = 0;
30
31
32 /***********************************************************************
33  *           PERQDATA_Addref
34  *
35  * Increment reference count for the PERQUEUEDATA instance
36  * Returns reference count for debugging purposes
37  */
38 static void PERQDATA_Addref( PERQUEUEDATA *pQData )
39 {
40     assert(pQData != 0 );
41     TRACE_(msg)("(): current refcount %lu ...\n", pQData->ulRefCount);
42
43     InterlockedIncrement( &pQData->ulRefCount );
44 }
45
46
47 /***********************************************************************
48  *           PERQDATA_Release
49  *
50  * Release a reference to a PERQUEUEDATA instance.
51  * Destroy the instance if no more references exist
52  * Returns reference count for debugging purposes
53  */
54 static void PERQDATA_Release( PERQUEUEDATA *pQData )
55 {
56     assert(pQData != 0 );
57     TRACE_(msg)("(): current refcount %lu ...\n",
58           (LONG)pQData->ulRefCount );
59
60     if (!InterlockedDecrement( &pQData->ulRefCount ))
61     {
62         DeleteCriticalSection( &pQData->cSection );
63
64         TRACE_(msg)("(): deleting PERQUEUEDATA instance ...\n" );
65
66         /* Deleting our global 16 bit perQData? */
67         if ( pQData == pQDataWin16 ) pQDataWin16 = 0;
68
69         /* Free the PERQUEUEDATA instance */
70         HeapFree( GetProcessHeap(), 0, pQData );
71     }
72 }
73
74
75 /***********************************************************************
76  *           PERQDATA_CreateInstance
77  *
78  * Creates an instance of a reference counted PERQUEUEDATA element
79  * for the message queue. perQData is stored globally for 16 bit tasks.
80  *
81  * Note: We don't implement perQdata exactly the same way Windows does.
82  * Each perQData element is reference counted since it may be potentially
83  * shared by multiple message Queues (via AttachThreadInput).
84  * We only store the current values for Active, Capture and focus windows
85  * currently.
86  */
87 static PERQUEUEDATA * PERQDATA_CreateInstance(void)
88 {
89     PERQUEUEDATA *pQData;
90     
91     BOOL16 bIsWin16 = 0;
92     
93     TRACE_(msg)("()\n");
94
95     /* Share a single instance of perQData for all 16 bit tasks */
96     if ( ( bIsWin16 = !(NtCurrentTeb()->tibflags & TEBF_WIN32) ) )
97     {
98         /* If previously allocated, just bump up ref count */
99         if ( pQDataWin16 )
100         {
101             PERQDATA_Addref( pQDataWin16 );
102             return pQDataWin16;
103         }
104     }
105
106     /* Allocate PERQUEUEDATA from the system heap */
107     if (!( pQData = (PERQUEUEDATA *) HeapAlloc( GetProcessHeap(), 0,
108                                                     sizeof(PERQUEUEDATA) ) ))
109         return 0;
110
111     /* Initialize */
112     pQData->hWndCapture = pQData->hWndFocus = pQData->hWndActive = 0;
113     pQData->ulRefCount = 1;
114     pQData->nCaptureHT = HTCLIENT;
115
116     /* Note: We have an independent critical section for the per queue data
117      * since this may be shared by different threads. see AttachThreadInput()
118      */
119     InitializeCriticalSection( &pQData->cSection );
120     /* FIXME: not all per queue data critical sections should be global */
121     MakeCriticalSectionGlobal( &pQData->cSection );
122
123     /* Save perQData globally for 16 bit tasks */
124     if ( bIsWin16 )
125         pQDataWin16 = pQData;
126         
127     return pQData;
128 }
129
130
131 /***********************************************************************
132  *           PERQDATA_GetFocusWnd
133  *
134  * Get the focus hwnd member in a threadsafe manner
135  */
136 HWND PERQDATA_GetFocusWnd( PERQUEUEDATA *pQData )
137 {
138     HWND hWndFocus;
139     assert(pQData != 0 );
140
141     EnterCriticalSection( &pQData->cSection );
142     hWndFocus = pQData->hWndFocus;
143     LeaveCriticalSection( &pQData->cSection );
144
145     return hWndFocus;
146 }
147
148
149 /***********************************************************************
150  *           PERQDATA_SetFocusWnd
151  *
152  * Set the focus hwnd member in a threadsafe manner
153  */
154 HWND PERQDATA_SetFocusWnd( PERQUEUEDATA *pQData, HWND hWndFocus )
155 {
156     HWND hWndFocusPrv;
157     assert(pQData != 0 );
158
159     EnterCriticalSection( &pQData->cSection );
160     hWndFocusPrv = pQData->hWndFocus;
161     pQData->hWndFocus = hWndFocus;
162     LeaveCriticalSection( &pQData->cSection );
163
164     return hWndFocusPrv;
165 }
166
167
168 /***********************************************************************
169  *           PERQDATA_GetActiveWnd
170  *
171  * Get the active hwnd member in a threadsafe manner
172  */
173 HWND PERQDATA_GetActiveWnd( PERQUEUEDATA *pQData )
174 {
175     HWND hWndActive;
176     assert(pQData != 0 );
177
178     EnterCriticalSection( &pQData->cSection );
179     hWndActive = pQData->hWndActive;
180     LeaveCriticalSection( &pQData->cSection );
181
182     return hWndActive;
183 }
184
185
186 /***********************************************************************
187  *           PERQDATA_SetActiveWnd
188  *
189  * Set the active focus hwnd member in a threadsafe manner
190  */
191 HWND PERQDATA_SetActiveWnd( PERQUEUEDATA *pQData, HWND hWndActive )
192 {
193     HWND hWndActivePrv;
194     assert(pQData != 0 );
195
196     EnterCriticalSection( &pQData->cSection );
197     hWndActivePrv = pQData->hWndActive;
198     pQData->hWndActive = hWndActive;
199     LeaveCriticalSection( &pQData->cSection );
200
201     return hWndActivePrv;
202 }
203
204
205 /***********************************************************************
206  *           PERQDATA_GetCaptureWnd
207  *
208  * Get the capture hwnd member in a threadsafe manner
209  */
210 HWND PERQDATA_GetCaptureWnd( INT *hittest )
211 {
212     MESSAGEQUEUE *queue;
213     PERQUEUEDATA *pQData;
214     HWND hWndCapture;
215
216     if (!(queue = QUEUE_Current())) return 0;
217     pQData = queue->pQData;
218
219     EnterCriticalSection( &pQData->cSection );
220     hWndCapture = pQData->hWndCapture;
221     *hittest = pQData->nCaptureHT;
222     LeaveCriticalSection( &pQData->cSection );
223     return hWndCapture;
224 }
225
226
227 /***********************************************************************
228  *           PERQDATA_SetCaptureWnd
229  *
230  * Set the capture hwnd member in a threadsafe manner
231  */
232 HWND PERQDATA_SetCaptureWnd( HWND hWndCapture, INT hittest )
233 {
234     MESSAGEQUEUE *queue;
235     PERQUEUEDATA *pQData;
236     HWND hWndCapturePrv;
237
238     if (!(queue = QUEUE_Current())) return 0;
239     pQData = queue->pQData;
240
241     EnterCriticalSection( &pQData->cSection );
242     hWndCapturePrv = pQData->hWndCapture;
243     pQData->hWndCapture = hWndCapture;
244     pQData->nCaptureHT = hittest;
245     LeaveCriticalSection( &pQData->cSection );
246     return hWndCapturePrv;
247 }
248
249
250
251 /***********************************************************************
252  *           QUEUE_Lock
253  *
254  * Function for getting a 32 bit pointer on queue structure. For thread
255  * safeness programmers should use this function instead of GlobalLock to
256  * retrieve a pointer on the structure. QUEUE_Unlock should also be called
257  * when access to the queue structure is not required anymore.
258  */
259 MESSAGEQUEUE *QUEUE_Lock( HQUEUE16 hQueue )
260 {
261     MESSAGEQUEUE *queue;
262
263     HeapLock( GetProcessHeap() );  /* FIXME: a bit overkill */
264     queue = GlobalLock16( hQueue );
265     if ( !queue || (queue->magic != QUEUE_MAGIC) )
266     {
267         HeapUnlock( GetProcessHeap() );
268         return NULL;
269     }
270
271     queue->lockCount++;
272     HeapUnlock( GetProcessHeap() );
273     return queue;
274 }
275
276
277 /***********************************************************************
278  *           QUEUE_Current
279  *
280  * Get the current thread queue, creating it if required.
281  * QUEUE_Unlock is not needed since the queue can only be deleted by
282  * the current thread anyway.
283  */
284 MESSAGEQUEUE *QUEUE_Current(void)
285 {
286     MESSAGEQUEUE *queue;
287     HQUEUE16 hQueue;
288
289     if (!(hQueue = GetThreadQueue16(0)))
290     {
291         if (!(hQueue = InitThreadInput16( 0, 0 ))) return NULL;
292     }
293
294     if ((queue = GlobalLock16( hQueue )))
295     {
296         if (queue->magic != QUEUE_MAGIC) queue = NULL;
297     }
298     return queue;
299 }
300
301
302 /***********************************************************************
303  *           QUEUE_Unlock
304  *
305  * Use with QUEUE_Lock to get a thread safe access to message queue
306  * structure
307  */
308 void QUEUE_Unlock( MESSAGEQUEUE *queue )
309 {
310     if (queue)
311     {
312         HeapLock( GetProcessHeap() );  /* FIXME: a bit overkill */
313
314         if ( --queue->lockCount == 0 )
315         {
316             if (queue->server_queue)
317                 CloseHandle( queue->server_queue );
318             GlobalFree16( queue->self );
319         }
320     
321         HeapUnlock( GetProcessHeap() );
322     }
323 }
324
325
326 /***********************************************************************
327  *           QUEUE_CreateMsgQueue
328  *
329  * Creates a message queue. Doesn't link it into queue list!
330  */
331 static HQUEUE16 QUEUE_CreateMsgQueue( BOOL16 bCreatePerQData )
332 {
333     HQUEUE16 hQueue;
334     HANDLE handle;
335     MESSAGEQUEUE * msgQueue;
336
337     TRACE_(msg)("(): Creating message queue...\n");
338
339     if (!(hQueue = GlobalAlloc16( GMEM_FIXED | GMEM_ZEROINIT,
340                                   sizeof(MESSAGEQUEUE) )))
341         return 0;
342
343     msgQueue = (MESSAGEQUEUE *) GlobalLock16( hQueue );
344     if ( !msgQueue )
345         return 0;
346
347     if (bCreatePerQData)
348     {
349         SERVER_START_REQ( get_msg_queue )
350         {
351             wine_server_call_err( req );
352             handle = reply->handle;
353         }
354         SERVER_END_REQ;
355         if (!handle)
356         {
357             ERR_(msg)("Cannot get thread queue");
358             GlobalFree16( hQueue );
359             return 0;
360         }
361         msgQueue->server_queue = handle;
362     }
363
364     msgQueue->self = hQueue;
365     msgQueue->lockCount = 1;
366     msgQueue->magic = QUEUE_MAGIC;
367     
368     /* Create and initialize our per queue data */
369     msgQueue->pQData = bCreatePerQData ? PERQDATA_CreateInstance() : NULL;
370     
371     return hQueue;
372 }
373
374
375 /***********************************************************************
376  *           QUEUE_DeleteMsgQueue
377  *
378  * Unlinks and deletes a message queue.
379  *
380  * Note: We need to mask asynchronous events to make sure PostMessage works
381  * even in the signal handler.
382  */
383 void QUEUE_DeleteMsgQueue(void)
384 {
385     HQUEUE16 hQueue = GetThreadQueue16(0);
386     MESSAGEQUEUE * msgQueue;
387
388     if (!hQueue) return;  /* thread doesn't have a queue */
389
390     TRACE("(): Deleting message queue %04x\n", hQueue);
391
392     if (!(msgQueue = QUEUE_Lock(hQueue)))
393     {
394         ERR("invalid thread queue\n");
395         return;
396     }
397
398     msgQueue->magic = 0;
399
400     if( hActiveQueue == hQueue ) hActiveQueue = 0;
401
402     HeapLock( GetProcessHeap() );  /* FIXME: a bit overkill */
403
404     /* Release per queue data if present */
405     if ( msgQueue->pQData )
406     {
407         PERQDATA_Release( msgQueue->pQData );
408         msgQueue->pQData = 0;
409     }
410
411     msgQueue->self = 0;
412
413     HeapUnlock( GetProcessHeap() );
414     SetThreadQueue16( 0, 0 );
415
416     /* free up resource used by MESSAGEQUEUE structure */
417     msgQueue->lockCount--;
418     QUEUE_Unlock( msgQueue );
419 }
420
421
422 /***********************************************************************
423  *              GetWindowTask (USER.224)
424  */
425 HTASK16 WINAPI GetWindowTask16( HWND16 hwnd )
426 {
427     HTASK16 retvalue;
428     MESSAGEQUEUE *queue;
429
430     WND *wndPtr = WIN_FindWndPtr16( hwnd );
431     if (!wndPtr) return 0;
432
433     queue = QUEUE_Lock( wndPtr->hmemTaskQ );
434     WIN_ReleaseWndPtr(wndPtr);
435
436     if (!queue) return 0;
437
438     retvalue = queue->teb->htask16;
439     QUEUE_Unlock( queue );
440
441     return retvalue;
442 }
443
444 /***********************************************************************
445  *              InitThreadInput (USER.409)
446  */
447 HQUEUE16 WINAPI InitThreadInput16( WORD unknown, WORD flags )
448 {
449     MESSAGEQUEUE *queuePtr;
450     HQUEUE16 hQueue = NtCurrentTeb()->queue;
451
452     if ( !hQueue )
453     {
454         /* Create thread message queue */
455         if( !(hQueue = QUEUE_CreateMsgQueue( TRUE )))
456         {
457             ERR_(msg)("failed!\n");
458             return FALSE;
459         }
460         
461         /* Link new queue into list */
462         queuePtr = QUEUE_Lock( hQueue );
463         queuePtr->teb = NtCurrentTeb();
464
465         HeapLock( GetProcessHeap() );  /* FIXME: a bit overkill */
466         SetThreadQueue16( 0, hQueue );
467         NtCurrentTeb()->queue = hQueue;
468         HeapUnlock( GetProcessHeap() );
469         
470         QUEUE_Unlock( queuePtr );
471     }
472
473     return hQueue;
474 }
475
476 /***********************************************************************
477  *              GetQueueStatus (USER32.@)
478  */
479 DWORD WINAPI GetQueueStatus( UINT flags )
480 {
481     DWORD ret = 0;
482
483     SERVER_START_REQ( get_queue_status )
484     {
485         req->clear = 1;
486         wine_server_call( req );
487         ret = MAKELONG( reply->changed_bits & flags, reply->wake_bits & flags );
488     }
489     SERVER_END_REQ;
490     return ret;
491 }
492
493
494 /***********************************************************************
495  *              GetInputState   (USER32.@)
496  */
497 BOOL WINAPI GetInputState(void)
498 {
499     DWORD ret = 0;
500
501     SERVER_START_REQ( get_queue_status )
502     {
503         req->clear = 0;
504         wine_server_call( req );
505         ret = reply->wake_bits & (QS_KEY | QS_MOUSEBUTTON);
506     }
507     SERVER_END_REQ;
508     return ret;
509 }
510
511 /***********************************************************************
512  *              GetMessagePos (USER.119)
513  *              GetMessagePos (USER32.@)
514  * 
515  * The GetMessagePos() function returns a long value representing a
516  * cursor position, in screen coordinates, when the last message
517  * retrieved by the GetMessage() function occurs. The x-coordinate is
518  * in the low-order word of the return value, the y-coordinate is in
519  * the high-order word. The application can use the MAKEPOINT()
520  * macro to obtain a POINT structure from the return value. 
521  *
522  * For the current cursor position, use GetCursorPos().
523  *
524  * RETURNS
525  *
526  * Cursor position of last message on success, zero on failure.
527  *
528  * CONFORMANCE
529  *
530  * ECMA-234, Win32
531  *
532  */
533 DWORD WINAPI GetMessagePos(void)
534 {
535     MESSAGEQUEUE *queue;
536
537     if (!(queue = QUEUE_Current())) return 0;
538     return queue->GetMessagePosVal;
539 }
540
541
542 /***********************************************************************
543  *              GetMessageTime (USER.120)
544  *              GetMessageTime (USER32.@)
545  *
546  * GetMessageTime() returns the message time for the last message
547  * retrieved by the function. The time is measured in milliseconds with
548  * the same offset as GetTickCount().
549  *
550  * Since the tick count wraps, this is only useful for moderately short
551  * relative time comparisons.
552  *
553  * RETURNS
554  *
555  * Time of last message on success, zero on failure.
556  *
557  * CONFORMANCE
558  *
559  * ECMA-234, Win32
560  *  
561  */
562 LONG WINAPI GetMessageTime(void)
563 {
564     MESSAGEQUEUE *queue;
565
566     if (!(queue = QUEUE_Current())) return 0;
567     return queue->GetMessageTimeVal;
568 }
569
570
571 /***********************************************************************
572  *              GetMessageExtraInfo (USER.288)
573  *              GetMessageExtraInfo (USER32.@)
574  */
575 LONG WINAPI GetMessageExtraInfo(void)
576 {
577     MESSAGEQUEUE *queue;
578
579     if (!(queue = QUEUE_Current())) return 0;
580     return queue->GetMessageExtraInfoVal;
581 }
582
583
584 /**********************************************************************
585  *              AttachThreadInput (USER32.@) Attaches input of 1 thread to other
586  *
587  * Attaches the input processing mechanism of one thread to that of
588  * another thread.
589  *
590  * RETURNS
591  *    Success: TRUE
592  *    Failure: FALSE
593  *
594  * TODO:
595  *    1. Reset the Key State (currenly per thread key state is not maintained)
596  */
597 BOOL WINAPI AttachThreadInput( 
598     DWORD idAttach,   /* [in] Thread to attach */
599     DWORD idAttachTo, /* [in] Thread to attach to */
600     BOOL fAttach)   /* [in] Attach or detach */
601 {
602     MESSAGEQUEUE *pSrcMsgQ = 0, *pTgtMsgQ = 0;
603     BOOL16 bRet = 0;
604
605     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
606
607     /* A thread cannot attach to itself */
608     if ( idAttach == idAttachTo )
609         goto CLEANUP;
610
611     /* According to the docs this method should fail if a
612      * "Journal record" hook is installed. (attaches all input queues together)
613      */
614     if ( HOOK_IsHooked( WH_JOURNALRECORD ) )
615         goto CLEANUP;
616         
617     /* Retrieve message queues corresponding to the thread id's */
618     pTgtMsgQ = QUEUE_Lock( GetThreadQueue16( idAttach ) );
619     pSrcMsgQ = QUEUE_Lock( GetThreadQueue16( idAttachTo ) );
620
621     /* Ensure we have message queues and that Src and Tgt threads
622      * are not system threads.
623      */
624     if ( !pSrcMsgQ || !pTgtMsgQ || !pSrcMsgQ->pQData || !pTgtMsgQ->pQData )
625         goto CLEANUP;
626
627     if (fAttach)   /* Attach threads */
628     {
629         /* Only attach if currently detached  */
630         if ( pTgtMsgQ->pQData != pSrcMsgQ->pQData )
631         {
632             /* First release the target threads perQData */
633             PERQDATA_Release( pTgtMsgQ->pQData );
634         
635             /* Share a reference to the source threads perQDATA */
636             PERQDATA_Addref( pSrcMsgQ->pQData );
637             pTgtMsgQ->pQData = pSrcMsgQ->pQData;
638         }
639     }
640     else    /* Detach threads */
641     {
642         /* Only detach if currently attached */
643         if ( pTgtMsgQ->pQData == pSrcMsgQ->pQData )
644         {
645             /* First release the target threads perQData */
646             PERQDATA_Release( pTgtMsgQ->pQData );
647         
648             /* Give the target thread its own private perQDATA once more */
649             pTgtMsgQ->pQData = PERQDATA_CreateInstance();
650         }
651     }
652
653     /* TODO: Reset the Key State */
654
655     bRet = 1;      /* Success */
656     
657 CLEANUP:
658
659     /* Unlock the queues before returning */
660     if ( pSrcMsgQ )
661         QUEUE_Unlock( pSrcMsgQ );
662     if ( pTgtMsgQ )
663         QUEUE_Unlock( pTgtMsgQ );
664     
665     return bRet;
666 }