2 * Message queues related functions
4 * Copyright 1993, 1994 Alexandre Julliard
13 #include "wine/winbase16.h"
14 #include "wine/winuser16.h"
20 #include "debugtools.h"
24 DEFAULT_DEBUG_CHANNEL(msg);
26 #define MAX_QUEUE_SIZE 120 /* Max. size of a message queue */
28 static HQUEUE16 hExitingQueue = 0;
29 static PERQUEUEDATA *pQDataWin16 = NULL; /* Global perQData for Win16 tasks */
31 HQUEUE16 hActiveQueue = 0;
34 /***********************************************************************
35 * PERQDATA_CreateInstance
37 * Creates an instance of a reference counted PERQUEUEDATA element
38 * for the message queue. perQData is stored globally for 16 bit tasks.
40 * Note: We don't implement perQdata exactly the same way Windows does.
41 * Each perQData element is reference counted since it may be potentially
42 * shared by multiple message Queues (via AttachThreadInput).
43 * We only store the current values for Active, Capture and focus windows
46 PERQUEUEDATA * PERQDATA_CreateInstance( )
54 /* Share a single instance of perQData for all 16 bit tasks */
55 if ( ( bIsWin16 = !(NtCurrentTeb()->tibflags & TEBF_WIN32) ) )
57 /* If previously allocated, just bump up ref count */
60 PERQDATA_Addref( pQDataWin16 );
65 /* Allocate PERQUEUEDATA from the system heap */
66 if (!( pQData = (PERQUEUEDATA *) HeapAlloc( GetProcessHeap(), 0,
67 sizeof(PERQUEUEDATA) ) ))
71 pQData->hWndCapture = pQData->hWndFocus = pQData->hWndActive = 0;
72 pQData->ulRefCount = 1;
73 pQData->nCaptureHT = HTCLIENT;
75 /* Note: We have an independent critical section for the per queue data
76 * since this may be shared by different threads. see AttachThreadInput()
78 InitializeCriticalSection( &pQData->cSection );
79 /* FIXME: not all per queue data critical sections should be global */
80 MakeCriticalSectionGlobal( &pQData->cSection );
82 /* Save perQData globally for 16 bit tasks */
90 /***********************************************************************
93 * Increment reference count for the PERQUEUEDATA instance
94 * Returns reference count for debugging purposes
96 ULONG PERQDATA_Addref( PERQUEUEDATA *pQData )
99 TRACE_(msg)("(): current refcount %lu ...\n", pQData->ulRefCount);
101 EnterCriticalSection( &pQData->cSection );
102 ++pQData->ulRefCount;
103 LeaveCriticalSection( &pQData->cSection );
105 return pQData->ulRefCount;
109 /***********************************************************************
112 * Release a reference to a PERQUEUEDATA instance.
113 * Destroy the instance if no more references exist
114 * Returns reference count for debugging purposes
116 ULONG PERQDATA_Release( PERQUEUEDATA *pQData )
118 assert(pQData != 0 );
119 TRACE_(msg)("(): current refcount %lu ...\n",
120 (LONG)pQData->ulRefCount );
122 EnterCriticalSection( &pQData->cSection );
123 if ( --pQData->ulRefCount == 0 )
125 LeaveCriticalSection( &pQData->cSection );
126 DeleteCriticalSection( &pQData->cSection );
128 TRACE_(msg)("(): deleting PERQUEUEDATA instance ...\n" );
130 /* Deleting our global 16 bit perQData? */
131 if ( pQData == pQDataWin16 )
134 /* Free the PERQUEUEDATA instance */
135 HeapFree( GetProcessHeap(), 0, pQData );
139 LeaveCriticalSection( &pQData->cSection );
141 return pQData->ulRefCount;
145 /***********************************************************************
146 * PERQDATA_GetFocusWnd
148 * Get the focus hwnd member in a threadsafe manner
150 HWND PERQDATA_GetFocusWnd( PERQUEUEDATA *pQData )
153 assert(pQData != 0 );
155 EnterCriticalSection( &pQData->cSection );
156 hWndFocus = pQData->hWndFocus;
157 LeaveCriticalSection( &pQData->cSection );
163 /***********************************************************************
164 * PERQDATA_SetFocusWnd
166 * Set the focus hwnd member in a threadsafe manner
168 HWND PERQDATA_SetFocusWnd( PERQUEUEDATA *pQData, HWND hWndFocus )
171 assert(pQData != 0 );
173 EnterCriticalSection( &pQData->cSection );
174 hWndFocusPrv = pQData->hWndFocus;
175 pQData->hWndFocus = hWndFocus;
176 LeaveCriticalSection( &pQData->cSection );
182 /***********************************************************************
183 * PERQDATA_GetActiveWnd
185 * Get the active hwnd member in a threadsafe manner
187 HWND PERQDATA_GetActiveWnd( PERQUEUEDATA *pQData )
190 assert(pQData != 0 );
192 EnterCriticalSection( &pQData->cSection );
193 hWndActive = pQData->hWndActive;
194 LeaveCriticalSection( &pQData->cSection );
200 /***********************************************************************
201 * PERQDATA_SetActiveWnd
203 * Set the active focus hwnd member in a threadsafe manner
205 HWND PERQDATA_SetActiveWnd( PERQUEUEDATA *pQData, HWND hWndActive )
208 assert(pQData != 0 );
210 EnterCriticalSection( &pQData->cSection );
211 hWndActivePrv = pQData->hWndActive;
212 pQData->hWndActive = hWndActive;
213 LeaveCriticalSection( &pQData->cSection );
215 return hWndActivePrv;
219 /***********************************************************************
220 * PERQDATA_GetCaptureWnd
222 * Get the capture hwnd member in a threadsafe manner
224 HWND PERQDATA_GetCaptureWnd( PERQUEUEDATA *pQData )
227 assert(pQData != 0 );
229 EnterCriticalSection( &pQData->cSection );
230 hWndCapture = pQData->hWndCapture;
231 LeaveCriticalSection( &pQData->cSection );
237 /***********************************************************************
238 * PERQDATA_SetCaptureWnd
240 * Set the capture hwnd member in a threadsafe manner
242 HWND PERQDATA_SetCaptureWnd( PERQUEUEDATA *pQData, HWND hWndCapture )
245 assert(pQData != 0 );
247 EnterCriticalSection( &pQData->cSection );
248 hWndCapturePrv = pQData->hWndCapture;
249 pQData->hWndCapture = hWndCapture;
250 LeaveCriticalSection( &pQData->cSection );
252 return hWndCapturePrv;
256 /***********************************************************************
257 * PERQDATA_GetCaptureInfo
259 * Get the capture info member in a threadsafe manner
261 INT16 PERQDATA_GetCaptureInfo( PERQUEUEDATA *pQData )
264 assert(pQData != 0 );
266 EnterCriticalSection( &pQData->cSection );
267 nCaptureHT = pQData->nCaptureHT;
268 LeaveCriticalSection( &pQData->cSection );
274 /***********************************************************************
275 * PERQDATA_SetCaptureInfo
277 * Set the capture info member in a threadsafe manner
279 INT16 PERQDATA_SetCaptureInfo( PERQUEUEDATA *pQData, INT16 nCaptureHT )
282 assert(pQData != 0 );
284 EnterCriticalSection( &pQData->cSection );
285 nCaptureHTPrv = pQData->nCaptureHT;
286 pQData->nCaptureHT = nCaptureHT;
287 LeaveCriticalSection( &pQData->cSection );
289 return nCaptureHTPrv;
293 /***********************************************************************
296 * Function for getting a 32 bit pointer on queue structure. For thread
297 * safeness programmers should use this function instead of GlobalLock to
298 * retrieve a pointer on the structure. QUEUE_Unlock should also be called
299 * when access to the queue structure is not required anymore.
301 MESSAGEQUEUE *QUEUE_Lock( HQUEUE16 hQueue )
305 HeapLock( GetProcessHeap() ); /* FIXME: a bit overkill */
306 queue = GlobalLock16( hQueue );
307 if ( !queue || (queue->magic != QUEUE_MAGIC) )
309 HeapUnlock( GetProcessHeap() );
314 HeapUnlock( GetProcessHeap() );
319 /***********************************************************************
322 * Use with QUEUE_Lock to get a thread safe access to message queue
325 void QUEUE_Unlock( MESSAGEQUEUE *queue )
329 HeapLock( GetProcessHeap() ); /* FIXME: a bit overkill */
331 if ( --queue->lockCount == 0 )
333 DeleteCriticalSection ( &queue->cSection );
334 if (queue->server_queue)
335 CloseHandle( queue->server_queue );
336 GlobalFree16( queue->self );
339 HeapUnlock( GetProcessHeap() );
344 /***********************************************************************
347 void QUEUE_DumpQueue( HQUEUE16 hQueue )
351 if (!(pq = QUEUE_Lock( hQueue )) )
353 WARN_(msg)("%04x is not a queue handle\n", hQueue );
357 EnterCriticalSection( &pq->cSection );
359 DPRINTF( "thread: %10p Intertask SendMessage:\n"
360 "firstMsg: %8p lastMsg: %8p\n"
364 pq->teb, pq->firstMsg, pq->lastMsg,
365 (unsigned)pq->lockCount, pq->wPaintCount,
368 LeaveCriticalSection( &pq->cSection );
374 /***********************************************************************
375 * QUEUE_IsExitingQueue
377 BOOL QUEUE_IsExitingQueue( HQUEUE16 hQueue )
379 return (hExitingQueue && (hQueue == hExitingQueue));
383 /***********************************************************************
384 * QUEUE_SetExitingQueue
386 void QUEUE_SetExitingQueue( HQUEUE16 hQueue )
388 hExitingQueue = hQueue;
392 /***********************************************************************
393 * QUEUE_CreateMsgQueue
395 * Creates a message queue. Doesn't link it into queue list!
397 static HQUEUE16 QUEUE_CreateMsgQueue( BOOL16 bCreatePerQData )
401 MESSAGEQUEUE * msgQueue;
403 TRACE_(msg)("(): Creating message queue...\n");
405 if (!(hQueue = GlobalAlloc16( GMEM_FIXED | GMEM_ZEROINIT,
406 sizeof(MESSAGEQUEUE) )))
409 msgQueue = (MESSAGEQUEUE *) GlobalLock16( hQueue );
415 SERVER_START_REQ( get_msg_queue )
418 handle = req->handle;
423 ERR_(msg)("Cannot get thread queue");
424 GlobalFree16( hQueue );
427 msgQueue->server_queue = handle;
430 msgQueue->self = hQueue;
432 InitializeCriticalSection( &msgQueue->cSection );
433 MakeCriticalSectionGlobal( &msgQueue->cSection );
435 msgQueue->lockCount = 1;
436 msgQueue->magic = QUEUE_MAGIC;
438 /* Create and initialize our per queue data */
439 msgQueue->pQData = bCreatePerQData ? PERQDATA_CreateInstance() : NULL;
445 /***********************************************************************
446 * QUEUE_DeleteMsgQueue
448 * Unlinks and deletes a message queue.
450 * Note: We need to mask asynchronous events to make sure PostMessage works
451 * even in the signal handler.
453 BOOL QUEUE_DeleteMsgQueue( HQUEUE16 hQueue )
455 MESSAGEQUEUE * msgQueue = QUEUE_Lock(hQueue);
457 TRACE_(msg)("(): Deleting message queue %04x\n", hQueue);
459 if (!hQueue || !msgQueue)
461 ERR_(msg)("invalid argument.\n");
467 if( hActiveQueue == hQueue ) hActiveQueue = 0;
469 HeapLock( GetProcessHeap() ); /* FIXME: a bit overkill */
471 /* Release per queue data if present */
472 if ( msgQueue->pQData )
474 PERQDATA_Release( msgQueue->pQData );
475 msgQueue->pQData = 0;
480 HeapUnlock( GetProcessHeap() );
482 /* free up resource used by MESSAGEQUEUE structure */
483 msgQueue->lockCount--;
484 QUEUE_Unlock( msgQueue );
490 /***********************************************************************
491 * handle_sent_message
493 * Handle the reception of a sent message by calling the corresponding window proc
495 static void handle_sent_message( QMSG *msg )
498 MESSAGEQUEUE *queue = QUEUE_Lock( GetFastQueue16() );
499 DWORD extraInfo = queue->GetMessageExtraInfoVal; /* save ExtraInfo */
500 WND *wndPtr = WIN_FindWndPtr( msg->msg.hwnd );
502 TRACE( "got hwnd %x msg %x (%s) wp %x lp %lx\n",
503 msg->msg.hwnd, msg->msg.message, SPY_GetMsgName(msg->msg.message),
504 msg->msg.wParam, msg->msg.lParam );
506 queue->GetMessageExtraInfoVal = msg->extraInfo;
508 /* call the right version of CallWindowProcXX */
512 result = CallWindowProc16( (WNDPROC16)wndPtr->winproc,
513 (HWND16) msg->msg.hwnd,
514 (UINT16) msg->msg.message,
515 LOWORD(msg->msg.wParam),
519 result = CallWindowProcA( wndPtr->winproc, msg->msg.hwnd, msg->msg.message,
520 msg->msg.wParam, msg->msg.lParam );
523 result = CallWindowProcW( wndPtr->winproc, msg->msg.hwnd, msg->msg.message,
524 msg->msg.wParam, msg->msg.lParam );
528 queue->GetMessageExtraInfoVal = extraInfo; /* Restore extra info */
529 WIN_ReleaseWndPtr(wndPtr);
530 QUEUE_Unlock( queue );
532 SERVER_START_REQ( reply_message )
534 req->result = result;
542 /***********************************************************************
543 * process_sent_messages
545 * Process all pending sent messages
547 static void process_sent_messages(void)
554 SERVER_START_REQ( get_message )
556 req->flags = GET_MSG_REMOVE | GET_MSG_SENT_ONLY;
560 if (!(res = SERVER_CALL()))
562 msg.type = req->type;
563 msg.msg.hwnd = req->win;
564 msg.msg.message = req->msg;
565 msg.msg.wParam = req->wparam;
566 msg.msg.lParam = req->lparam;
567 msg.msg.time = req->time;
568 msg.msg.pt.x = req->x;
569 msg.msg.pt.y = req->y;
570 msg.extraInfo = req->info;
576 handle_sent_message( &msg );
582 /***********************************************************************
585 * See "Windows Internals", p.447
588 * 0 if exit with timeout
591 int QUEUE_WaitBits( WORD bits, DWORD timeout )
596 TRACE_(msg)("q %04x waiting for %04x\n", GetFastQueue16(), bits);
598 hQueue = GetFastQueue16();
599 if (!(queue = QUEUE_Lock( hQueue ))) return 0;
603 unsigned int wake_bits = 0, changed_bits = 0;
606 SERVER_START_REQ( set_queue_mask )
608 req->wake_mask = QS_SENDMESSAGE;
609 req->changed_mask = bits | QS_SENDMESSAGE;
613 wake_bits = req->wake_bits;
614 changed_bits = req->changed_bits;
619 if (changed_bits & bits)
621 /* One of the bits is set; we can return */
622 QUEUE_Unlock( queue );
625 if (wake_bits & QS_SENDMESSAGE)
627 /* Process the sent message immediately */
628 process_sent_messages();
629 continue; /* nested sm crux */
632 TRACE_(msg)("(%04x) mask=%08x, bits=%08x, changed=%08x, waiting\n",
633 queue->self, bits, wake_bits, changed_bits );
635 ReleaseThunkLock( &dwlc );
636 if (dwlc) TRACE_(msg)("had win16 lock\n");
638 if (USER_Driver.pMsgWaitForMultipleObjectsEx)
639 USER_Driver.pMsgWaitForMultipleObjectsEx( 1, &queue->server_queue, timeout, 0, 0 );
641 WaitForSingleObject( queue->server_queue, timeout );
642 if (dwlc) RestoreThunkLock( dwlc );
647 /***********************************************************************
650 * Find a message matching the given parameters. Return FALSE if none available.
652 BOOL QUEUE_FindMsg( HWND hwnd, UINT first, UINT last, BOOL remove, QMSG *msg )
656 if (!first && !last) last = ~0;
660 SERVER_START_REQ( get_message )
662 req->flags = remove ? GET_MSG_REMOVE : 0;
664 req->get_first = first;
665 req->get_last = last;
666 if ((ret = !SERVER_CALL()))
668 msg->kind = req->kind;
669 msg->type = req->type;
670 msg->msg.hwnd = req->win;
671 msg->msg.message = req->msg;
672 msg->msg.wParam = req->wparam;
673 msg->msg.lParam = req->lparam;
674 msg->msg.time = req->time;
675 msg->msg.pt.x = req->x;
676 msg->msg.pt.y = req->y;
677 msg->extraInfo = req->info;
682 if (!ret || (msg->kind != SEND_MESSAGE)) break;
683 handle_sent_message( msg );
686 if (ret) TRACE( "got hwnd %x msg %x (%s) wp %x lp %lx\n",
687 msg->msg.hwnd, msg->msg.message, SPY_GetMsgName(msg->msg.message),
688 msg->msg.wParam, msg->msg.lParam );
694 /***********************************************************************
695 * QUEUE_CleanupWindow
697 * Cleanup the queue to account for a window being deleted.
699 void QUEUE_CleanupWindow( HWND hwnd )
701 SERVER_START_REQ( cleanup_window_queue )
710 /***********************************************************************
713 HTASK16 QUEUE_GetQueueTask( HQUEUE16 hQueue )
717 MESSAGEQUEUE *queue = QUEUE_Lock( hQueue );
721 hTask = queue->teb->htask16;
722 QUEUE_Unlock( queue );
729 /***********************************************************************
730 * PostQuitMessage (USER.6)
732 void WINAPI PostQuitMessage16( INT16 exitCode )
734 PostQuitMessage( exitCode );
738 /***********************************************************************
739 * PostQuitMessage (USER32.@)
741 * PostQuitMessage() posts a message to the system requesting an
742 * application to terminate execution. As a result of this function,
743 * the WM_QUIT message is posted to the application, and
744 * PostQuitMessage() returns immediately. The exitCode parameter
745 * specifies an application-defined exit code, which appears in the
746 * _wParam_ parameter of the WM_QUIT message posted to the application.
752 void WINAPI PostQuitMessage( INT exitCode )
754 PostThreadMessageW( GetCurrentThreadId(), WM_QUIT, exitCode, 0 );
758 /***********************************************************************
759 * GetWindowTask (USER.224)
761 HTASK16 WINAPI GetWindowTask16( HWND16 hwnd )
764 WND *wndPtr = WIN_FindWndPtr( hwnd );
766 if (!wndPtr) return 0;
767 retvalue = QUEUE_GetQueueTask( wndPtr->hmemTaskQ );
768 WIN_ReleaseWndPtr(wndPtr);
772 /***********************************************************************
773 * GetWindowThreadProcessId (USER32.@)
775 DWORD WINAPI GetWindowThreadProcessId( HWND hwnd, LPDWORD process )
780 WND *wndPtr = WIN_FindWndPtr( hwnd );
781 if (!wndPtr) return 0;
783 queue = QUEUE_Lock( wndPtr->hmemTaskQ );
784 WIN_ReleaseWndPtr(wndPtr);
786 if (!queue) return 0;
788 if ( process ) *process = (DWORD)queue->teb->pid;
789 retvalue = (DWORD)queue->teb->tid;
791 QUEUE_Unlock( queue );
796 /***********************************************************************
797 * SetMessageQueue (USER.266)
799 BOOL16 WINAPI SetMessageQueue16( INT16 size )
801 return SetMessageQueue( size );
805 /***********************************************************************
806 * SetMessageQueue (USER32.@)
808 BOOL WINAPI SetMessageQueue( INT size )
810 /* now obsolete the message queue will be expanded dynamically
813 /* access the queue to create it if it's not existing */
819 /***********************************************************************
820 * InitThreadInput (USER.409)
822 HQUEUE16 WINAPI InitThreadInput16( WORD unknown, WORD flags )
825 MESSAGEQUEUE *queuePtr;
827 TEB *teb = NtCurrentTeb();
836 /* Create thread message queue */
837 if( !(hQueue = QUEUE_CreateMsgQueue( TRUE )))
839 ERR_(msg)("failed!\n");
843 /* Link new queue into list */
844 queuePtr = QUEUE_Lock( hQueue );
845 queuePtr->teb = NtCurrentTeb();
847 HeapLock( GetProcessHeap() ); /* FIXME: a bit overkill */
848 SetThreadQueue16( 0, hQueue );
850 HeapUnlock( GetProcessHeap() );
852 QUEUE_Unlock( queuePtr );
858 /***********************************************************************
859 * GetQueueStatus (USER.334)
861 DWORD WINAPI GetQueueStatus16( UINT16 flags )
863 return GetQueueStatus( flags );
866 /***********************************************************************
867 * GetQueueStatus (USER32.@)
869 DWORD WINAPI GetQueueStatus( UINT flags )
873 SERVER_START_REQ( get_queue_status )
877 ret = MAKELONG( req->changed_bits & flags, req->wake_bits & flags );
884 /***********************************************************************
885 * GetInputState (USER.335)
887 BOOL16 WINAPI GetInputState16(void)
889 return GetInputState();
892 /***********************************************************************
893 * GetInputState (USER32.@)
895 BOOL WINAPI GetInputState(void)
899 SERVER_START_REQ( get_queue_status )
903 ret = req->wake_bits & (QS_KEY | QS_MOUSEBUTTON);
909 /***********************************************************************
910 * WaitForInputIdle (USER32.@)
912 DWORD WINAPI WaitForInputIdle (HANDLE hProcess, DWORD dwTimeOut)
915 HANDLE idle_event = -1;
917 SERVER_START_REQ( wait_input_idle )
919 req->handle = hProcess;
920 req->timeout = dwTimeOut;
921 if (!(ret = SERVER_CALL_ERR())) idle_event = req->event;
924 if (ret) return 0xffffffff; /* error */
925 if (!idle_event) return 0; /* no event to wait on */
927 cur_time = GetTickCount();
929 TRACE_(msg)("waiting for %x\n", idle_event );
930 while ( dwTimeOut > GetTickCount() - cur_time || dwTimeOut == INFINITE )
932 ret = MsgWaitForMultipleObjects ( 1, &idle_event, FALSE, dwTimeOut, QS_SENDMESSAGE );
933 if ( ret == ( WAIT_OBJECT_0 + 1 ))
935 process_sent_messages();
938 if ( ret == WAIT_TIMEOUT || ret == 0xFFFFFFFF )
940 TRACE_(msg)("timeout or error\n");
945 TRACE_(msg)("finished\n");
953 /***********************************************************************
954 * UserYield (USER.332)
955 * UserYield16 (USER32.@)
957 void WINAPI UserYield16(void)
959 /* Handle sent messages */
960 process_sent_messages();
965 /* Handle sent messages again */
966 process_sent_messages();
969 /***********************************************************************
970 * GetMessagePos (USER.119) (USER32.@)
972 * The GetMessagePos() function returns a long value representing a
973 * cursor position, in screen coordinates, when the last message
974 * retrieved by the GetMessage() function occurs. The x-coordinate is
975 * in the low-order word of the return value, the y-coordinate is in
976 * the high-order word. The application can use the MAKEPOINT()
977 * macro to obtain a POINT structure from the return value.
979 * For the current cursor position, use GetCursorPos().
983 * Cursor position of last message on success, zero on failure.
990 DWORD WINAPI GetMessagePos(void)
995 if (!(queue = QUEUE_Lock( GetFastQueue16() ))) return 0;
996 ret = queue->GetMessagePosVal;
997 QUEUE_Unlock( queue );
1003 /***********************************************************************
1004 * GetMessageTime (USER.120) (USER32.@)
1006 * GetMessageTime() returns the message time for the last message
1007 * retrieved by the function. The time is measured in milliseconds with
1008 * the same offset as GetTickCount().
1010 * Since the tick count wraps, this is only useful for moderately short
1011 * relative time comparisons.
1015 * Time of last message on success, zero on failure.
1022 LONG WINAPI GetMessageTime(void)
1024 MESSAGEQUEUE *queue;
1027 if (!(queue = QUEUE_Lock( GetFastQueue16() ))) return 0;
1028 ret = queue->GetMessageTimeVal;
1029 QUEUE_Unlock( queue );
1035 /***********************************************************************
1036 * GetMessageExtraInfo (USER.288) (USER32.@)
1038 LONG WINAPI GetMessageExtraInfo(void)
1040 MESSAGEQUEUE *queue;
1043 if (!(queue = QUEUE_Lock( GetFastQueue16() ))) return 0;
1044 ret = queue->GetMessageExtraInfoVal;
1045 QUEUE_Unlock( queue );
1051 /**********************************************************************
1052 * AttachThreadInput (USER32.@) Attaches input of 1 thread to other
1054 * Attaches the input processing mechanism of one thread to that of
1062 * 1. Reset the Key State (currenly per thread key state is not maintained)
1064 BOOL WINAPI AttachThreadInput(
1065 DWORD idAttach, /* [in] Thread to attach */
1066 DWORD idAttachTo, /* [in] Thread to attach to */
1067 BOOL fAttach) /* [in] Attach or detach */
1069 MESSAGEQUEUE *pSrcMsgQ = 0, *pTgtMsgQ = 0;
1072 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1074 /* A thread cannot attach to itself */
1075 if ( idAttach == idAttachTo )
1078 /* According to the docs this method should fail if a
1079 * "Journal record" hook is installed. (attaches all input queues together)
1081 if ( HOOK_IsHooked( WH_JOURNALRECORD ) )
1084 /* Retrieve message queues corresponding to the thread id's */
1085 pTgtMsgQ = QUEUE_Lock( GetThreadQueue16( idAttach ) );
1086 pSrcMsgQ = QUEUE_Lock( GetThreadQueue16( idAttachTo ) );
1088 /* Ensure we have message queues and that Src and Tgt threads
1089 * are not system threads.
1091 if ( !pSrcMsgQ || !pTgtMsgQ || !pSrcMsgQ->pQData || !pTgtMsgQ->pQData )
1094 if (fAttach) /* Attach threads */
1096 /* Only attach if currently detached */
1097 if ( pTgtMsgQ->pQData != pSrcMsgQ->pQData )
1099 /* First release the target threads perQData */
1100 PERQDATA_Release( pTgtMsgQ->pQData );
1102 /* Share a reference to the source threads perQDATA */
1103 PERQDATA_Addref( pSrcMsgQ->pQData );
1104 pTgtMsgQ->pQData = pSrcMsgQ->pQData;
1107 else /* Detach threads */
1109 /* Only detach if currently attached */
1110 if ( pTgtMsgQ->pQData == pSrcMsgQ->pQData )
1112 /* First release the target threads perQData */
1113 PERQDATA_Release( pTgtMsgQ->pQData );
1115 /* Give the target thread its own private perQDATA once more */
1116 pTgtMsgQ->pQData = PERQDATA_CreateInstance();
1120 /* TODO: Reset the Key State */
1122 bRet = 1; /* Success */
1126 /* Unlock the queues before returning */
1128 QUEUE_Unlock( pSrcMsgQ );
1130 QUEUE_Unlock( pTgtMsgQ );