diff --git a/include/message.h b/include/message.h index 02519973b9..bf721265d6 100644 --- a/include/message.h +++ b/include/message.h @@ -59,7 +59,6 @@ extern BOOL32 EVENT_Pending(void); /* input.c */ extern HWND32 EVENT_Capture( HWND32, INT16 ); -extern INT16 EVENT_GetCaptureInfo(void); extern BOOL32 EVENT_QueryPointer( DWORD *posX, DWORD *posY, DWORD *state ); extern void joySendMessages(void); diff --git a/include/queue.h b/include/queue.h index e31f1fd9f6..5a088ce156 100644 --- a/include/queue.h +++ b/include/queue.h @@ -28,17 +28,26 @@ typedef struct BOOL16 bPending; } QSMCTRL; -/* Per-queue system windows */ +/* Per-queue data for the message queue + * Note that we currently only store the current values for + * Active, Capture and Focus windows currently. + * It might be necessary to store a pointer to the system message queue + * as well since windows 9x maintains per thread system message queues + */ typedef struct tagPERQUEUEDATA { - HWND32 hWndCapture; - HWND32 hWndFocus; - HWND32 hWndActive; + HWND32 hWndFocus; /* Focus window */ + HWND32 hWndActive; /* Active window */ + HWND32 hWndCapture; /* Capture window */ + INT16 nCaptureHT; /* Capture info (hit-test) */ + ULONG ulRefCount; /* Reference count */ + CRITICAL_SECTION cSection; /* Critical section for thread safe access */ } PERQUEUEDATA; +/* Message queue */ typedef struct tagMESSAGEQUEUE { - HQUEUE16 next; /* NNext queue */ + HQUEUE16 next; /* Next queue */ HQUEUE16 self; /* Handle to self (was: reserved) */ THDB* thdb; /* Thread owning queue */ HANDLE32 hEvent; /* Event handle */ @@ -84,7 +93,7 @@ typedef struct tagMESSAGEQUEUE HANDLE16 hCurHook; /* Current hook */ HANDLE16 hooks[WH_NB_HOOKS]; /* Task hooks list */ - HANDLE16 hPerQueue; /* handle on PERQUEUEDATA structure */ + PERQUEUEDATA *pQData; /* pointer to (shared) PERQUEUEDATA structure */ } MESSAGEQUEUE; @@ -99,6 +108,20 @@ typedef struct tagMESSAGEQUEUE #define QUEUE_MAGIC 0xD46E80AF +/* Per queue data management methods */ +PERQUEUEDATA* PERQDATA_CreateInstance( ); +ULONG PERQDATA_Addref( PERQUEUEDATA* pQData ); +ULONG PERQDATA_Release( PERQUEUEDATA* pQData ); +HWND32 PERQDATA_GetFocusWnd( PERQUEUEDATA *pQData ); +HWND32 PERQDATA_SetFocusWnd( PERQUEUEDATA *pQData, HWND32 hWndFocus ); +HWND32 PERQDATA_GetActiveWnd( PERQUEUEDATA *pQData ); +HWND32 PERQDATA_SetActiveWnd( PERQUEUEDATA *pQData, HWND32 hWndActive ); +HWND32 PERQDATA_GetCaptureWnd( PERQUEUEDATA *pQData ); +HWND32 PERQDATA_SetCaptureWnd( PERQUEUEDATA *pQData, HWND32 hWndCapture ); +INT16 PERQDATA_GetCaptureInfo( PERQUEUEDATA *pQData ); +INT16 PERQDATA_SetCaptureInfo( PERQUEUEDATA *pQData, INT16 nCaptureHT ); + +/* Message queue management methods */ extern MESSAGEQUEUE *QUEUE_Lock( HQUEUE16 hQueue ); extern void QUEUE_Unlock( MESSAGEQUEUE *queue ); extern void QUEUE_DumpQueue( HQUEUE16 hQueue ); diff --git a/include/win.h b/include/win.h index 9af596760a..5fe5431dc5 100644 --- a/include/win.h +++ b/include/win.h @@ -11,6 +11,8 @@ #include "ldt.h" #include "windows.h" #include "winproc.h" +#include "queue.h" + #define WND_MAGIC 0x444e4957 /* 'WIND' */ @@ -172,6 +174,6 @@ extern HWND32 ICONTITLE_Create( WND* ); extern BOOL32 ICONTITLE_Init( void ); /* windows/focus.c */ -extern void FOCUS_SwitchFocus( HWND32 , HWND32 ); +extern void FOCUS_SwitchFocus( MESSAGEQUEUE *pMsgQ, HWND32 , HWND32 ); #endif /* __WINE_WIN_H */ diff --git a/include/winpos.h b/include/winpos.h index a572d93228..0779a23cf4 100644 --- a/include/winpos.h +++ b/include/winpos.h @@ -41,7 +41,7 @@ extern LONG WINPOS_SendNCCalcSize(HWND32 hwnd, BOOL32 calcValidRect, extern LONG WINPOS_HandleWindowPosChanging16(WND *wndPtr, WINDOWPOS16 *winpos); extern LONG WINPOS_HandleWindowPosChanging32(WND *wndPtr, WINDOWPOS32 *winpos); extern INT16 WINPOS_WindowFromPoint( WND* scopeWnd, POINT16 pt, WND **ppWnd ); -extern void WINPOS_CheckInternalPos( HWND32 hwnd ); +extern void WINPOS_CheckInternalPos( WND* wndPtr ); extern BOOL32 WINPOS_ActivateOtherWindow(WND* pWnd); extern BOOL32 WINPOS_CreateInternalPosAtom(void); diff --git a/scheduler/thread.c b/scheduler/thread.c index 0bbd009f7c..b68a3f6b19 100644 --- a/scheduler/thread.c +++ b/scheduler/thread.c @@ -20,6 +20,8 @@ #include "server.h" #include "stackframe.h" #include "debug.h" +#include "queue.h" +#include "hook.h" #ifndef __i386__ THDB *pCurrentThread; @@ -788,25 +790,79 @@ BOOL32 WINAPI GetThreadTimes( * RETURNS * Success: TRUE * Failure: FALSE + * + * TODO: + * 1. Reset the Key State (currenly per thread key state is not maintained) */ BOOL32 WINAPI AttachThreadInput( DWORD idAttach, /* [in] Thread to attach */ DWORD idAttachTo, /* [in] Thread to attach to */ BOOL32 fAttach) /* [in] Attach or detach */ { - BOOL32 ret; + MESSAGEQUEUE *pSrcMsgQ = 0, *pTgtMsgQ = 0; + BOOL16 bRet = 0; - FIXME(thread, "(0x%08lx,0x%08lx,%d): stub\n",idAttach,idAttachTo,fAttach); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - if (fAttach) { - /* Attach threads */ - ret = FALSE; + + /* A thread cannot attach to itself */ + if ( idAttach == idAttachTo ) + goto CLEANUP; + + /* According to the docs this method should fail if a + * "Journal record" hook is installed. (attaches all input queues together) + */ + if ( HOOK_IsHooked( WH_JOURNALRECORD ) ) + goto CLEANUP; + + /* Retrieve message queues corresponding to the thread id's */ + pTgtMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( GetThreadQueue( idAttach ) ); + pSrcMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( GetThreadQueue( idAttachTo ) ); + + /* Ensure we have message queues and that Src and Tgt threads + * are not system threads. + */ + if ( !pSrcMsgQ || !pTgtMsgQ || !pSrcMsgQ->pQData || !pTgtMsgQ->pQData ) + goto CLEANUP; + + if (fAttach) /* Attach threads */ + { + /* Only attach if currently detached */ + if ( pTgtMsgQ->pQData != pSrcMsgQ->pQData ) + { + /* First release the target threads perQData */ + PERQDATA_Release( pTgtMsgQ->pQData ); + + /* Share a reference to the source threads perQDATA */ + PERQDATA_Addref( pSrcMsgQ->pQData ); + pTgtMsgQ->pQData = pSrcMsgQ->pQData; + } } - else { - /* Detach threads */ - ret = FALSE; - }; - return ret; + else /* Detach threads */ + { + /* Only detach if currently attached */ + if ( pTgtMsgQ->pQData == pSrcMsgQ->pQData ) + { + /* First release the target threads perQData */ + PERQDATA_Release( pTgtMsgQ->pQData ); + + /* Give the target thread its own private perQDATA once more */ + pTgtMsgQ->pQData = PERQDATA_CreateInstance(); + } + } + + /* TODO: Reset the Key State */ + + bRet = 1; // Success + +CLEANUP: + + /* Unlock the queues before returning */ + if ( pSrcMsgQ ) + QUEUE_Unlock( pSrcMsgQ ); + if ( pTgtMsgQ ) + QUEUE_Unlock( pTgtMsgQ ); + + return bRet; } /********************************************************************** diff --git a/windows/focus.c b/windows/focus.c index 4ff9faff24..55b6430b24 100644 --- a/windows/focus.c +++ b/windows/focus.c @@ -11,17 +11,20 @@ #include "winpos.h" #include "hook.h" #include "message.h" +#include "task.h" +#include "debug.h" -static HWND32 hwndFocus = 0; /***************************************************************** * FOCUS_SwitchFocus + * pMsgQ is the queue whose perQData focus is to be modified */ -void FOCUS_SwitchFocus( HWND32 hFocusFrom, HWND32 hFocusTo ) +void FOCUS_SwitchFocus( MESSAGEQUEUE *pMsgQ, HWND32 hFocusFrom, HWND32 hFocusTo ) { WND *pFocusTo = WIN_FindWndPtr( hFocusTo ); - hwndFocus = hFocusTo; + PERQDATA_SetFocusWnd( pMsgQ->pQData, hFocusTo ); + #if 0 if (hFocusFrom) SendMessage32A( hFocusFrom, WM_KILLFOCUS, hFocusTo, 0 ); #else @@ -29,7 +32,8 @@ void FOCUS_SwitchFocus( HWND32 hFocusFrom, HWND32 hFocusTo ) * intertask at this time */ if (hFocusFrom) SendMessage16( hFocusFrom, WM_KILLFOCUS, hFocusTo, 0 ); #endif - if( !pFocusTo || hFocusTo != hwndFocus ) + + if( !pFocusTo || hFocusTo != PERQDATA_GetFocusWnd( pMsgQ->pQData ) ) return; /* According to API docs, the WM_SETFOCUS message is sent AFTER the window @@ -59,8 +63,17 @@ HWND16 WINAPI SetFocus16( HWND16 hwnd ) */ HWND32 WINAPI SetFocus32( HWND32 hwnd ) { - HWND32 hWndPrevFocus, hwndTop = hwnd; + HWND32 hWndFocus = 0, hwndTop = hwnd; WND *wndPtr = WIN_FindWndPtr( hwnd ); + MESSAGEQUEUE *pMsgQ = 0, *pCurMsgQ = 0; + BOOL16 bRet = 0; + + /* Get the messageQ for the current thread */ + if (!(pCurMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() ))) + { + WARN( win, "\tCurrent message queue not found. Exiting!\n" ); + goto CLEANUP; + } if (wndPtr) { @@ -69,35 +82,79 @@ HWND32 WINAPI SetFocus32( HWND32 hwnd ) while ( (wndPtr->dwStyle & (WS_CHILD | WS_POPUP)) == WS_CHILD ) { if ( wndPtr->dwStyle & ( WS_MINIMIZE | WS_DISABLED) ) - return 0; - if (!(wndPtr = wndPtr->parent)) return 0; + goto CLEANUP; + if (!(wndPtr = wndPtr->parent)) goto CLEANUP; hwndTop = wndPtr->hwndSelf; } - if( hwnd == hwndFocus ) return hwnd; + /* Retrieve the message queue associated with this window */ + pMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( wndPtr->hmemTaskQ ); + if ( !pMsgQ ) + { + WARN( win, "\tMessage queue not found. Exiting!\n" ); + goto CLEANUP; + } + /* Make sure that message queue for the window we are setting focus to + * shares the same perQ data as the current threads message queue. + * In other words you can't set focus to a window owned by a different + * thread unless AttachThreadInput has been called previously. + * (see AttachThreadInput and SetFocus docs) + */ + if ( pCurMsgQ->pQData != pMsgQ->pQData ) + goto CLEANUP; + + /* Get the current focus window from the perQ data */ + hWndFocus = PERQDATA_GetFocusWnd( pMsgQ->pQData ); + + if( hwnd == hWndFocus ) + { + bRet = 1; // Success + goto CLEANUP; // Nothing to do + } + /* call hooks */ if( HOOK_CallHooks16( WH_CBT, HCBT_SETFOCUS, (WPARAM16)hwnd, - (LPARAM)hwndFocus) ) - return 0; + (LPARAM)hWndFocus) ) + goto CLEANUP; /* activate hwndTop if needed. */ if (hwndTop != GetActiveWindow32()) { - if (!WINPOS_SetActiveWindow(hwndTop, 0, 0)) return 0; + if (!WINPOS_SetActiveWindow(hwndTop, 0, 0)) goto CLEANUP; - if (!IsWindow32( hwnd )) return 0; /* Abort if window destroyed */ + if (!IsWindow32( hwnd )) goto CLEANUP; /* Abort if window destroyed */ } + + /* Get the current focus window from the perQ data */ + hWndFocus = PERQDATA_GetFocusWnd( pMsgQ->pQData ); + + /* Change focus and send messages */ + FOCUS_SwitchFocus( pMsgQ, hWndFocus, hwnd ); } - else if( HOOK_CallHooks16( WH_CBT, HCBT_SETFOCUS, 0, (LPARAM)hwndFocus ) ) - return 0; + else /* NULL hwnd passed in */ + { + if( HOOK_CallHooks16( WH_CBT, HCBT_SETFOCUS, 0, (LPARAM)hWndFocus ) ) + goto CLEANUP; + + /* Get the current focus from the perQ data of the current message Q */ + hWndFocus = PERQDATA_GetFocusWnd( pCurMsgQ->pQData ); /* Change focus and send messages */ - hWndPrevFocus = hwndFocus; + FOCUS_SwitchFocus( pCurMsgQ, hWndFocus, hwnd ); + } - FOCUS_SwitchFocus( hwndFocus , hwnd ); + bRet = 1; // Success + +CLEANUP: - return hWndPrevFocus; + /* Unlock the queues before returning */ + if ( pMsgQ ) + QUEUE_Unlock( pMsgQ ); + if ( pCurMsgQ ) + QUEUE_Unlock( pCurMsgQ ); + + return bRet ? hWndFocus : 0; } @@ -106,7 +163,7 @@ HWND32 WINAPI SetFocus32( HWND32 hwnd ) */ HWND16 WINAPI GetFocus16(void) { - return (HWND16)hwndFocus; + return (HWND16)GetFocus32(); } @@ -115,5 +172,20 @@ HWND16 WINAPI GetFocus16(void) */ HWND32 WINAPI GetFocus32(void) { + MESSAGEQUEUE *pCurMsgQ = 0; + HWND32 hwndFocus = 0; + + /* Get the messageQ for the current thread */ + if (!(pCurMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() ))) + { + WARN( win, "\tCurrent message queue not found. Exiting!\n" ); + return 0; + } + + /* Get the current focus from the perQ data of the current message Q */ + hwndFocus = PERQDATA_GetFocusWnd( pCurMsgQ->pQData ); + + QUEUE_Unlock( pCurMsgQ ); + return hwndFocus; } diff --git a/windows/input.c b/windows/input.c index e09c7e078f..5f841745be 100644 --- a/windows/input.c +++ b/windows/input.c @@ -27,9 +27,8 @@ #include "debugtools.h" #include "struct32.h" #include "winerror.h" +#include "task.h" -static INT16 captureHT = HTCLIENT; -static HWND32 captureWnd = 0; static BOOL32 InputEnabled = TRUE; static BOOL32 SwappedButtons = FALSE; @@ -274,7 +273,20 @@ BOOL32 WINAPI SwapMouseButton32( BOOL32 fSwap ) */ HWND32 EVENT_Capture(HWND32 hwnd, INT16 ht) { - HWND32 capturePrev = captureWnd; + HWND32 capturePrev = 0, captureWnd = 0; + MESSAGEQUEUE *pMsgQ = 0, *pCurMsgQ = 0; + WND* wndPtr = 0; + INT16 captureHT = 0; + + /* Get the messageQ for the current thread */ + if (!(pCurMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() ))) + { + WARN( win, "\tCurrent message queue not found. Exiting!\n" ); + goto CLEANUP; + } + + /* Get the current capture window from the perQ data of the current message Q */ + capturePrev = PERQDATA_GetCaptureWnd( pCurMsgQ->pQData ); if (!hwnd) { @@ -283,7 +295,7 @@ HWND32 EVENT_Capture(HWND32 hwnd, INT16 ht) } else { - WND* wndPtr = WIN_FindWndPtr( hwnd ); + wndPtr = WIN_FindWndPtr( hwnd ); if (wndPtr) { TRACE(win, "(0x%04x)\n", hwnd ); @@ -292,22 +304,47 @@ HWND32 EVENT_Capture(HWND32 hwnd, INT16 ht) } } - if( capturePrev && capturePrev != captureWnd ) + /* Update the perQ capture window and send messages */ + if( capturePrev != captureWnd ) + { + if (wndPtr) + { + /* Retrieve the message queue associated with this window */ + pMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( wndPtr->hmemTaskQ ); + if ( !pMsgQ ) + { + WARN( win, "\tMessage queue not found. Exiting!\n" ); + goto CLEANUP; + } + + /* Make sure that message queue for the window we are setting capture to + * shares the same perQ data as the current threads message queue. + */ + if ( pCurMsgQ->pQData != pMsgQ->pQData ) + goto CLEANUP; + } + + PERQDATA_SetCaptureWnd( pCurMsgQ->pQData, captureWnd ); + PERQDATA_SetCaptureInfo( pCurMsgQ->pQData, captureHT ); + + if( capturePrev ) { WND* wndPtr = WIN_FindWndPtr( capturePrev ); if( wndPtr && (wndPtr->flags & WIN_ISWIN32) ) SendMessage32A( capturePrev, WM_CAPTURECHANGED, 0L, hwnd); } +} + +CLEANUP: + /* Unlock the queues before returning */ + if ( pMsgQ ) + QUEUE_Unlock( pMsgQ ); + if ( pCurMsgQ ) + QUEUE_Unlock( pCurMsgQ ); + return capturePrev; } -/********************************************************************** - * EVENT_GetCaptureInfo - */ -INT16 EVENT_GetCaptureInfo() -{ - return captureHT; -} /********************************************************************** * SetCapture16 (USER.18) @@ -332,8 +369,7 @@ HWND32 WINAPI SetCapture32( HWND32 hwnd ) */ void WINAPI ReleaseCapture(void) { - TRACE(win, "captureWnd=%04x\n", captureWnd ); - if( captureWnd ) EVENT_Capture( 0, 0 ); + EVENT_Capture( 0, 0 ); } @@ -342,7 +378,7 @@ void WINAPI ReleaseCapture(void) */ HWND16 WINAPI GetCapture16(void) { - return captureWnd; + return (HWND16)GetCapture32(); } /********************************************************************** @@ -350,7 +386,21 @@ HWND16 WINAPI GetCapture16(void) */ HWND32 WINAPI GetCapture32(void) { - return captureWnd; + MESSAGEQUEUE *pCurMsgQ = 0; + HWND32 hwndCapture = 0; + + /* Get the messageQ for the current thread */ + if (!(pCurMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() ))) +{ + TRACE( win, "GetCapture32: Current message queue not found. Exiting!\n" ); + return 0; + } + + /* Get the current capture window from the perQ data of the current message Q */ + hwndCapture = PERQDATA_GetCaptureWnd( pCurMsgQ->pQData ); + + QUEUE_Unlock( pCurMsgQ ); + return hwndCapture; } /********************************************************************** diff --git a/windows/message.c b/windows/message.c index 5b4586ca7e..a783e8fc40 100644 --- a/windows/message.c +++ b/windows/message.c @@ -122,7 +122,8 @@ static DWORD MSG_TranslateMouseMsg( HWND16 hTopWnd, DWORD filter, else { pWnd = WIN_FindWndPtr(hWnd); - ht = EVENT_GetCaptureInfo(); + if (queue) + ht = PERQDATA_GetCaptureInfo( queue->pQData ); } /* stop if not the right queue */ diff --git a/windows/queue.c b/windows/queue.c index 463d78c698..fcaff9763e 100644 --- a/windows/queue.c +++ b/windows/queue.c @@ -1,5 +1,4 @@ -/* - * Message queues related functions +/* * Message queues related functions * * Copyright 1993, 1994 Alexandre Julliard */ @@ -16,6 +15,7 @@ #include "heap.h" #include "thread.h" #include "process.h" +#include #include "debug.h" #define MAX_QUEUE_SIZE 120 /* Max. size of a message queue */ @@ -24,6 +24,7 @@ static HQUEUE16 hFirstQueue = 0; static HQUEUE16 hExitingQueue = 0; static HQUEUE16 hmemSysMsgQueue = 0; static MESSAGEQUEUE *sysMsgQueue = NULL; +static PERQUEUEDATA *pQDataWin16 = NULL; /* Global perQData for Win16 tasks */ static MESSAGEQUEUE *pMouseQueue = NULL; /* Queue for last mouse message */ static MESSAGEQUEUE *pKbdQueue = NULL; /* Queue for last kbd message */ @@ -32,10 +33,267 @@ HQUEUE16 hCursorQueue = 0; HQUEUE16 hActiveQueue = 0; +/*********************************************************************** + * PERQDATA_CreateInstance + * + * Creates an instance of a reference counted PERQUEUEDATA element + * for the message queue. perQData is stored globally for 16 bit tasks. + * + * Note: We don't implement perQdata exactly the same way Windows does. + * Each perQData element is reference counted since it may be potentially + * shared by multiple message Queues (via AttachThreadInput). + * We only store the current values for Active, Capture and focus windows + * currently. + */ +PERQUEUEDATA * PERQDATA_CreateInstance( ) +{ + PERQUEUEDATA *pQData; + + BOOL16 bIsWin16 = 0; + + TRACE(msg,"()\n"); + + /* Share a single instance of perQData for all 16 bit tasks */ + if ( ( bIsWin16 = THREAD_IsWin16( THREAD_Current() ) ) ) + { + /* If previously allocated, just bump up ref count */ + if ( pQDataWin16 ) + { + PERQDATA_Addref( pQDataWin16 ); + return pQDataWin16; + } + } + + /* Allocate PERQUEUEDATA from the system heap */ + if (!( pQData = (PERQUEUEDATA *) HeapAlloc( SystemHeap, 0, + sizeof(PERQUEUEDATA) ) )) + return 0; + + /* Initialize */ + pQData->hWndCapture = pQData->hWndFocus = pQData->hWndActive = 0; + pQData->ulRefCount = 1; + pQData->nCaptureHT = HTCLIENT; + + /* Note: We have an independent critical section for the per queue data + * since this may be shared by different threads. see AttachThreadInput() + */ + InitializeCriticalSection( &pQData->cSection ); + + /* Save perQData globally for 16 bit tasks */ + if ( bIsWin16 ) + pQDataWin16 = pQData; + + return pQData; +} + + +/*********************************************************************** + * PERQDATA_Addref + * + * Increment reference count for the PERQUEUEDATA instance + * Returns reference count for debugging purposes + */ +ULONG PERQDATA_Addref( PERQUEUEDATA *pQData ) +{ + assert(pQData != 0 ); + TRACE(msg,"(): current refcount %lu ...\n", pQData->ulRefCount); + + EnterCriticalSection( &pQData->cSection ); + ++pQData->ulRefCount; + LeaveCriticalSection( &pQData->cSection ); + + return pQData->ulRefCount; +} + + +/*********************************************************************** + * PERQDATA_Release + * + * Release a reference to a PERQUEUEDATA instance. + * Destroy the instance if no more references exist + * Returns reference count for debugging purposes + */ +ULONG PERQDATA_Release( PERQUEUEDATA *pQData ) +{ + assert(pQData != 0 ); + TRACE(msg,"(): current refcount %lu ...\n", + (LONG)pQData->ulRefCount ); + + EnterCriticalSection( &pQData->cSection ); + if ( --pQData->ulRefCount == 0 ) + { + LeaveCriticalSection( &pQData->cSection ); + DeleteCriticalSection( &pQData->cSection ); + + TRACE(msg,"(): deleting PERQUEUEDATA instance ...\n" ); + + /* Deleting our global 16 bit perQData? */ + if ( pQData == pQDataWin16 ) + pQDataWin16 = 0; + + /* Free the PERQUEUEDATA instance */ + HeapFree( SystemHeap, 0, pQData ); + + return 0; + } + LeaveCriticalSection( &pQData->cSection ); + + return pQData->ulRefCount; +} + + +/*********************************************************************** + * PERQDATA_GetFocusWnd + * + * Get the focus hwnd member in a threadsafe manner + */ +HWND32 PERQDATA_GetFocusWnd( PERQUEUEDATA *pQData ) +{ + HWND32 hWndFocus; + assert(pQData != 0 ); + + EnterCriticalSection( &pQData->cSection ); + hWndFocus = pQData->hWndFocus; + LeaveCriticalSection( &pQData->cSection ); + + return hWndFocus; +} + + +/*********************************************************************** + * PERQDATA_SetFocusWnd + * + * Set the focus hwnd member in a threadsafe manner + */ +HWND32 PERQDATA_SetFocusWnd( PERQUEUEDATA *pQData, HWND32 hWndFocus ) +{ + HWND32 hWndFocusPrv; + assert(pQData != 0 ); + + EnterCriticalSection( &pQData->cSection ); + hWndFocusPrv = pQData->hWndFocus; + pQData->hWndFocus = hWndFocus; + LeaveCriticalSection( &pQData->cSection ); + + return hWndFocusPrv; +} + + +/*********************************************************************** + * PERQDATA_GetActiveWnd + * + * Get the active hwnd member in a threadsafe manner + */ +HWND32 PERQDATA_GetActiveWnd( PERQUEUEDATA *pQData ) +{ + HWND32 hWndActive; + assert(pQData != 0 ); + + EnterCriticalSection( &pQData->cSection ); + hWndActive = pQData->hWndActive; + LeaveCriticalSection( &pQData->cSection ); + + return hWndActive; +} + + +/*********************************************************************** + * PERQDATA_SetActiveWnd + * + * Set the active focus hwnd member in a threadsafe manner + */ +HWND32 PERQDATA_SetActiveWnd( PERQUEUEDATA *pQData, HWND32 hWndActive ) +{ + HWND32 hWndActivePrv; + assert(pQData != 0 ); + + EnterCriticalSection( &pQData->cSection ); + hWndActivePrv = pQData->hWndActive; + pQData->hWndActive = hWndActive; + LeaveCriticalSection( &pQData->cSection ); + + return hWndActivePrv; +} + + +/*********************************************************************** + * PERQDATA_GetCaptureWnd + * + * Get the capture hwnd member in a threadsafe manner + */ +HWND32 PERQDATA_GetCaptureWnd( PERQUEUEDATA *pQData ) +{ + HWND32 hWndCapture; + assert(pQData != 0 ); + + EnterCriticalSection( &pQData->cSection ); + hWndCapture = pQData->hWndCapture; + LeaveCriticalSection( &pQData->cSection ); + + return hWndCapture; +} + + +/*********************************************************************** + * PERQDATA_SetCaptureWnd + * + * Set the capture hwnd member in a threadsafe manner + */ +HWND32 PERQDATA_SetCaptureWnd( PERQUEUEDATA *pQData, HWND32 hWndCapture ) +{ + HWND32 hWndCapturePrv; + assert(pQData != 0 ); + + EnterCriticalSection( &pQData->cSection ); + hWndCapturePrv = pQData->hWndCapture; + pQData->hWndCapture = hWndCapture; + LeaveCriticalSection( &pQData->cSection ); + + return hWndCapturePrv; +} + + +/*********************************************************************** + * PERQDATA_GetCaptureInfo + * + * Get the capture info member in a threadsafe manner + */ +INT16 PERQDATA_GetCaptureInfo( PERQUEUEDATA *pQData ) +{ + INT16 nCaptureHT; + assert(pQData != 0 ); + + EnterCriticalSection( &pQData->cSection ); + nCaptureHT = pQData->nCaptureHT; + LeaveCriticalSection( &pQData->cSection ); + + return nCaptureHT; +} + + +/*********************************************************************** + * PERQDATA_SetCaptureInfo + * + * Set the capture info member in a threadsafe manner + */ +INT16 PERQDATA_SetCaptureInfo( PERQUEUEDATA *pQData, INT16 nCaptureHT ) +{ + INT16 nCaptureHTPrv; + assert(pQData != 0 ); + + EnterCriticalSection( &pQData->cSection ); + nCaptureHTPrv = pQData->nCaptureHT; + pQData->nCaptureHT = nCaptureHT; + LeaveCriticalSection( &pQData->cSection ); + + return nCaptureHTPrv; +} + + /*********************************************************************** * QUEUE_Lock * - * Fonction for getting a 32 bit pointer on queue strcture. For thread + * Function for getting a 32 bit pointer on queue strcture. For thread * safeness programmers should use this function instead of GlobalLock to * retrieve a pointer on the structure. QUEUE_Unlock should also be called * when access to the queue structure is not required anymore. @@ -58,6 +316,7 @@ MESSAGEQUEUE *QUEUE_Lock( HQUEUE16 hQueue ) return queue; } + /*********************************************************************** * QUEUE_Unlock * @@ -169,13 +428,13 @@ void QUEUE_SetExitingQueue( HQUEUE16 hQueue ) * * Creates a message queue. Doesn't link it into queue list! */ -static HQUEUE16 QUEUE_CreateMsgQueue( ) +static HQUEUE16 QUEUE_CreateMsgQueue( BOOL16 bCreatePerQData ) { HQUEUE16 hQueue; MESSAGEQUEUE * msgQueue; TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() ); - TRACE(msg,"Creating message queue...\n"); + TRACE(msg,"(): Creating message queue...\n"); if (!(hQueue = GlobalAlloc16( GMEM_FIXED | GMEM_ZEROINIT, sizeof(MESSAGEQUEUE) ))) @@ -193,6 +452,9 @@ static HQUEUE16 QUEUE_CreateMsgQueue( ) msgQueue->lockCount = 1; msgQueue->magic = QUEUE_MAGIC; + /* Create and initialize our per queue data */ + msgQueue->pQData = bCreatePerQData ? PERQDATA_CreateInstance() : NULL; + return hQueue; } @@ -211,7 +473,7 @@ BOOL32 QUEUE_DeleteMsgQueue( HQUEUE16 hQueue ) HQUEUE16 senderQ; HQUEUE16 *pPrev; - TRACE(msg,"Deleting message queue %04x\n", hQueue); + TRACE(msg,"(): Deleting message queue %04x\n", hQueue); if (!hQueue || !msgQueue) { @@ -238,6 +500,13 @@ BOOL32 QUEUE_DeleteMsgQueue( HQUEUE16 hQueue ) SYSTEM_LOCK(); + /* Release per queue data if present */ + if ( msgQueue->pQData ) + { + PERQDATA_Release( msgQueue->pQData ); + msgQueue->pQData = 0; + } + /* remove the message queue from the global link list */ pPrev = &hFirstQueue; while (*pPrev && (*pPrev != hQueue)) @@ -266,7 +535,10 @@ BOOL32 QUEUE_DeleteMsgQueue( HQUEUE16 hQueue ) */ BOOL32 QUEUE_CreateSysMsgQueue( int size ) { - if (!(hmemSysMsgQueue = QUEUE_CreateMsgQueue( ))) return FALSE; + /* Note: We dont need perQ data for the system message queue */ + if (!(hmemSysMsgQueue = QUEUE_CreateMsgQueue( FALSE ))) + return FALSE; + sysMsgQueue = (MESSAGEQUEUE *) GlobalLock16( hmemSysMsgQueue ); return TRUE; } @@ -947,7 +1219,7 @@ HQUEUE16 WINAPI InitThreadInput( WORD unknown, WORD flags ) if ( !hQueue ) { /* Create thread message queue */ - if( !(hQueue = QUEUE_CreateMsgQueue( 0 ))) + if( !(hQueue = QUEUE_CreateMsgQueue( TRUE ))) { WARN(msg, "failed!\n"); return FALSE; diff --git a/windows/win.c b/windows/win.c index 1754d8567c..f96c2bd25c 100644 --- a/windows/win.c +++ b/windows/win.c @@ -282,7 +282,7 @@ static WND* WIN_DestroyWindow( WND* wndPtr ) /* FIXME: do we need to fake QS_MOUSEMOVE wakebit? */ - WINPOS_CheckInternalPos( hwnd ); + WINPOS_CheckInternalPos( wndPtr ); if( hwnd == GetCapture32()) ReleaseCapture(); /* free resources associated with the window */ @@ -1322,6 +1322,8 @@ BOOL32 WINAPI EnableWindow32( HWND32 hwnd, BOOL32 enable ) { WND *wndPtr; + TRACE(win,"EnableWindow32: ( %x, %d )\n", hwnd, enable); + if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return FALSE; if (enable && (wndPtr->dwStyle & WS_DISABLED)) { @@ -1335,9 +1337,13 @@ BOOL32 WINAPI EnableWindow32( HWND32 hwnd, BOOL32 enable ) /* Disable window */ wndPtr->dwStyle |= WS_DISABLED; if ((hwnd == GetFocus32()) || IsChild32( hwnd, GetFocus32() )) + { SetFocus32( 0 ); /* A disabled window can't have the focus */ + } if ((hwnd == GetCapture32()) || IsChild32( hwnd, GetCapture32() )) + { ReleaseCapture(); /* A disabled window can't capture the mouse */ + } SendMessage32A( hwnd, WM_ENABLE, FALSE, 0 ); return FALSE; } diff --git a/windows/winpos.c b/windows/winpos.c index dcf3a23932..381670dd3a 100644 --- a/windows/winpos.c +++ b/windows/winpos.c @@ -53,7 +53,6 @@ /* ----- internal variables ----- */ -static HWND32 hwndActive = 0; /* Currently active window */ static HWND32 hwndPrevActive = 0; /* Previously active window */ static HWND32 hGlobalShellWindow=0; /*the shell*/ @@ -76,14 +75,27 @@ BOOL32 WINPOS_CreateInternalPosAtom() * * Called when a window is destroyed. */ -void WINPOS_CheckInternalPos( HWND32 hwnd ) +void WINPOS_CheckInternalPos( WND* wndPtr ) { - LPINTERNALPOS lpPos = (LPINTERNALPOS) GetProp32A( hwnd, atomInternalPos ); + LPINTERNALPOS lpPos; + MESSAGEQUEUE *pMsgQ = 0; + HWND32 hwnd = wndPtr->hwndSelf; + + lpPos = (LPINTERNALPOS) GetProp32A( hwnd, atomInternalPos ); + + /* Retrieve the message queue associated with this window */ + pMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( wndPtr->hmemTaskQ ); + if ( !pMsgQ ) + { + WARN( win, "\tMessage queue not found. Exiting!\n" ); + return; + } if( hwnd == hwndPrevActive ) hwndPrevActive = 0; - if( hwnd == hwndActive ) + + if( hwnd == PERQDATA_GetActiveWnd( pMsgQ->pQData ) ) { - hwndActive = 0; + PERQDATA_SetActiveWnd( pMsgQ->pQData, 0 ); WARN(win, "\tattempt to activate destroyed window!\n"); } @@ -93,6 +105,9 @@ void WINPOS_CheckInternalPos( HWND32 hwnd ) DestroyWindow32( lpPos->hwndIconTitle ); HeapFree( SystemHeap, 0, lpPos ); } + + QUEUE_Unlock( pMsgQ ); + return; } /*********************************************************************** @@ -680,7 +695,7 @@ BOOL32 WINAPI IsZoomed32(HWND32 hWnd) */ HWND16 WINAPI GetActiveWindow16(void) { - return (HWND16)hwndActive; + return (HWND16)GetActiveWindow32(); } /******************************************************************* @@ -688,7 +703,21 @@ HWND16 WINAPI GetActiveWindow16(void) */ HWND32 WINAPI GetActiveWindow32(void) { - return (HWND32)hwndActive; + MESSAGEQUEUE *pCurMsgQ = 0; + HWND32 hwndActive = 0; + + /* Get the messageQ for the current thread */ + if (!(pCurMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() ))) +{ + WARN( win, "\tCurrent message queue not found. Exiting!\n" ); + return 0; + } + + /* Return the current active window from the perQ data of the current message Q */ + hwndActive = PERQDATA_GetActiveWnd( pCurMsgQ->pQData ); + + QUEUE_Unlock( pCurMsgQ ); + return hwndActive; } @@ -717,12 +746,45 @@ HWND16 WINAPI SetActiveWindow16( HWND16 hwnd ) */ HWND32 WINAPI SetActiveWindow32( HWND32 hwnd ) { - HWND32 prev = hwndActive; + HWND32 prev = 0; WND *wndPtr = WIN_FindWndPtr( hwnd ); + MESSAGEQUEUE *pMsgQ = 0, *pCurMsgQ = 0; if ( !WINPOS_CanActivate(wndPtr) ) return 0; + /* Get the messageQ for the current thread */ + if (!(pCurMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() ))) + { + WARN( win, "\tCurrent message queue not found. Exiting!\n" ); + goto CLEANUP; + } + + /* Retrieve the message queue associated with this window */ + pMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( wndPtr->hmemTaskQ ); + if ( !pMsgQ ) + { + WARN( win, "\tWindow message queue not found. Exiting!\n" ); + goto CLEANUP; + } + + /* Make sure that the window is associated with the calling threads + * message queue. It must share the same perQ data. + */ + if ( pCurMsgQ->pQData != pMsgQ->pQData ) + goto CLEANUP; + + /* Save current active window */ + prev = PERQDATA_GetActiveWnd( pMsgQ->pQData ); + WINPOS_SetActiveWindow( hwnd, 0, 0 ); + +CLEANUP: + /* Unlock the queues before returning */ + if ( pMsgQ ) + QUEUE_Unlock( pMsgQ ); + if ( pCurMsgQ ) + QUEUE_Unlock( pCurMsgQ ); + return prev; } @@ -1460,10 +1522,21 @@ BOOL32 WINPOS_SetActiveWindow( HWND32 hWnd, BOOL32 fMouse, BOOL32 fChangeFocus) CBTACTIVATESTRUCT16* cbtStruct; WND* wndPtr, *wndTemp; HQUEUE16 hOldActiveQueue, hNewActiveQueue; + MESSAGEQUEUE *pOldActiveQueue = 0, *pNewActiveQueue = 0; WORD wIconized = 0; + HWND32 hwndActive = 0; + BOOL32 bRet = 0; + + /* Get current active window from the active queue */ + if ( hActiveQueue ) + { + pOldActiveQueue = QUEUE_Lock( hActiveQueue ); + if ( pOldActiveQueue ) + hwndActive = PERQDATA_GetActiveWnd( pOldActiveQueue->pQData ); + } /* paranoid checks */ - if( hWnd == GetDesktopWindow32() || hWnd == hwndActive ) return 0; + if( hWnd == GetDesktopWindow32() || hWnd == hwndActive ) goto CLEANUP; /* if (wndPtr && (GetFastQueue() != wndPtr->hmemTaskQ)) * return 0; @@ -1485,15 +1558,23 @@ BOOL32 WINPOS_SetActiveWindow( HWND32 hWnd, BOOL32 fMouse, BOOL32 fChangeFocus) wRet = HOOK_CallHooks16( WH_CBT, HCBT_ACTIVATE, (WPARAM16)hWnd, (LPARAM)SEGPTR_GET(cbtStruct) ); SEGPTR_FREE(cbtStruct); - if (wRet) return wRet; + if (wRet) + { + /* Unlock the active queue before returning */ + if ( pOldActiveQueue ) + QUEUE_Unlock( pOldActiveQueue ); + return wRet; + } } /* set prev active wnd to current active wnd and send notification */ if ((hwndPrevActive = hwndActive) && IsWindow32(hwndPrevActive)) { + MESSAGEQUEUE *pTempActiveQueue = 0; + if (!SendMessage32A( hwndPrevActive, WM_NCACTIVATE, FALSE, 0 )) { - if (GetSysModalWindow16() != hWnd) return 0; + if (GetSysModalWindow16() != hWnd) goto CLEANUP; /* disregard refusal if hWnd is sysmodal */ } @@ -1508,12 +1589,24 @@ BOOL32 WINPOS_SetActiveWindow( HWND32 hWnd, BOOL32 fMouse, BOOL32 fChangeFocus) MAKELPARAM( (HWND16)hWnd, wIconized ) ); #endif - /* check if something happened during message processing */ - if( hwndPrevActive != hwndActive ) return 0; + /* check if something happened during message processing + * (global active queue may have changed) + */ + pTempActiveQueue = QUEUE_Lock( hActiveQueue ); + hwndActive = PERQDATA_GetActiveWnd( pTempActiveQueue->pQData ); + QUEUE_Unlock( pTempActiveQueue ); + if( hwndPrevActive != hwndActive ) + goto CLEANUP; } - /* set active wnd */ + /* Set new active window in the message queue */ hwndActive = hWnd; + if ( wndPtr ) + { + pNewActiveQueue = QUEUE_Lock( wndPtr->hmemTaskQ ); + if ( pNewActiveQueue ) + PERQDATA_SetActiveWnd( pNewActiveQueue->pQData, hwndActive ); + } /* send palette messages */ if (hWnd && SendMessage16( hWnd, WM_QUERYNEWPALETTE, 0, 0L)) @@ -1532,9 +1625,10 @@ BOOL32 WINPOS_SetActiveWindow( HWND32 hWnd, BOOL32 fMouse, BOOL32 fChangeFocus) if( wndTemp != wndPtr ) SetWindowPos32(hWnd, HWND_TOP, 0,0,0,0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE ); - if (!IsWindow32(hWnd)) return 0; + if (!IsWindow32(hWnd)) goto CLEANUP; } + /* Get a handle to the new active queue */ hNewActiveQueue = wndPtr ? wndPtr->hmemTaskQ : 0; /* send WM_ACTIVATEAPP if necessary */ @@ -1570,7 +1664,7 @@ BOOL32 WINPOS_SetActiveWindow( HWND32 hWnd, BOOL32 fMouse, BOOL32 fChangeFocus) HeapFree( SystemHeap, 0, list ); } - if (!IsWindow32(hWnd)) return 0; + if (!IsWindow32(hWnd)) goto CLEANUP; } if (hWnd) @@ -1592,13 +1686,13 @@ BOOL32 WINPOS_SetActiveWindow( HWND32 hWnd, BOOL32 fMouse, BOOL32 fChangeFocus) MAKELPARAM( (HWND16)hwndPrevActive, wIconized) ); #endif - if( !IsWindow32(hWnd) ) return 0; + if( !IsWindow32(hWnd) ) goto CLEANUP; } /* change focus if possible */ if( fChangeFocus && GetFocus32() ) if( WIN_GetTopParent(GetFocus32()) != hwndActive ) - FOCUS_SwitchFocus( GetFocus32(), + FOCUS_SwitchFocus( pNewActiveQueue, GetFocus32(), (wndPtr && (wndPtr->dwStyle & WS_MINIMIZE))? 0: hwndActive @@ -1610,7 +1704,16 @@ BOOL32 WINPOS_SetActiveWindow( HWND32 hWnd, BOOL32 fMouse, BOOL32 fChangeFocus) /* if active wnd is minimized redraw icon title */ if( IsIconic32(hwndActive) ) WINPOS_RedrawIconTitle(hwndActive); - return (hWnd == hwndActive); + bRet = 1; // Success + +CLEANUP: + + /* Unlock the message queues before returning */ + if ( pOldActiveQueue ) + QUEUE_Unlock( pOldActiveQueue ); + if ( pNewActiveQueue ) + QUEUE_Unlock( pNewActiveQueue ); + return bRet ? (hWnd == hwndActive) : 0; } /******************************************************************* @@ -1622,6 +1725,18 @@ BOOL32 WINPOS_ActivateOtherWindow(WND* pWnd) { BOOL32 bRet = 0; WND* pWndTo = NULL; + HWND32 hwndActive = 0; + + /* Get current active window from the active queue */ + if ( hActiveQueue ) + { + MESSAGEQUEUE *pActiveQueue = QUEUE_Lock( hActiveQueue ); + if ( pActiveQueue ) + { + hwndActive = PERQDATA_GetActiveWnd( pActiveQueue->pQData ); + QUEUE_Unlock( pActiveQueue ); + } + } if( pWnd->hwndSelf == hwndPrevActive ) hwndPrevActive = 0; @@ -1663,6 +1778,18 @@ BOOL32 WINPOS_ActivateOtherWindow(WND* pWnd) BOOL32 WINPOS_ChangeActiveWindow( HWND32 hWnd, BOOL32 mouseMsg ) { WND *wndPtr = WIN_FindWndPtr(hWnd); + HWND32 hwndActive = 0; + + /* Get current active window from the active queue */ + if ( hActiveQueue ) + { + MESSAGEQUEUE *pActiveQueue = QUEUE_Lock( hActiveQueue ); + if ( pActiveQueue ) + { + hwndActive = PERQDATA_GetActiveWnd( pActiveQueue->pQData ); + QUEUE_Unlock( pActiveQueue ); + } + } if (!hWnd) return WINPOS_SetActiveWindow( 0, mouseMsg, TRUE ); @@ -2063,6 +2190,18 @@ BOOL32 WINAPI SetWindowPos32( HWND32 hwnd, HWND32 hwndInsertAfter, int result = 0; UINT32 uFlags = 0; BOOL32 resync = FALSE; + HWND32 hwndActive = 0; + + /* Get current active window from the active queue */ + if ( hActiveQueue ) + { + MESSAGEQUEUE *pActiveQueue = QUEUE_Lock( hActiveQueue ); + if ( pActiveQueue ) + { + hwndActive = PERQDATA_GetActiveWnd( pActiveQueue->pQData ); + QUEUE_Unlock( pActiveQueue ); + } + } TRACE(win,"hwnd %04x, (%i,%i)-(%i,%i) flags %08x\n", hwnd, x, y, x+cx, y+cy, flags); @@ -2393,6 +2532,10 @@ BOOL32 WINAPI SetWindowPos32( HWND32 hwnd, HWND32 hwndInsertAfter, if (hwnd == CARET_GetHwnd()) DestroyCaret32(); + /* FIXME: This will cause the window to be activated irrespective + * of whether it is owned by the same thread. + * Should this behaviour be allowed in SetWindowPos? + */ if (winpos.hwnd == hwndActive) WINPOS_ActivateOtherWindow( wndPtr ); }