Made access to the wnd struct thread-safe.
[wine] / windows / focus.c
1 /*
2  * Focus functions
3  *
4  * Copyright 1993 David Metcalfe
5  *           1994 Alexandre Julliard
6  *           1995 Alex Korobka
7  *
8  */
9
10 #include "win.h"
11 #include "winpos.h"
12 #include "hook.h"
13 #include "message.h"
14 #include "task.h"
15 #include "debug.h"
16
17
18 /*****************************************************************
19  *               FOCUS_SwitchFocus 
20  * pMsgQ is the queue whose perQData focus is to be modified
21  */
22 void FOCUS_SwitchFocus( MESSAGEQUEUE *pMsgQ, HWND hFocusFrom, HWND hFocusTo )
23 {
24     WND *pFocusTo = WIN_FindWndPtr( hFocusTo );
25
26     PERQDATA_SetFocusWnd( pMsgQ->pQData, hFocusTo );
27     
28 #if 0
29     if (hFocusFrom) SendMessageA( hFocusFrom, WM_KILLFOCUS, hFocusTo, 0 );
30 #else
31     /* FIXME: must be SendMessage16() because 32A doesn't do
32      * intertask at this time */
33     if (hFocusFrom) SendMessage16( hFocusFrom, WM_KILLFOCUS, hFocusTo, 0 );
34 #endif
35
36     if( !pFocusTo || hFocusTo != PERQDATA_GetFocusWnd( pMsgQ->pQData ) )
37     {
38         WIN_ReleaseWndPtr(pFocusTo);
39         return;
40     }
41
42     /* According to API docs, the WM_SETFOCUS message is sent AFTER the window
43        has received the keyboard focus. */
44
45     pFocusTo->pDriver->pSetFocus(pFocusTo);
46
47     WIN_ReleaseWndPtr(pFocusTo);
48 #if 0
49     SendMessageA( hFocusTo, WM_SETFOCUS, hFocusFrom, 0 );
50 #else
51     SendMessage16( hFocusTo, WM_SETFOCUS, hFocusFrom, 0 );
52 #endif
53 }
54
55
56 /*****************************************************************
57  *               SetFocus16   (USER.22)
58  */
59 HWND16 WINAPI SetFocus16( HWND16 hwnd )
60 {
61     return (HWND16)SetFocus( hwnd );
62 }
63
64
65 /*****************************************************************
66  *               SetFocus32   (USER32.481)
67  */
68 HWND WINAPI SetFocus( HWND hwnd )
69 {
70     HWND hWndFocus = 0, hwndTop = hwnd;
71     WND *wndPtr = WIN_FindWndPtr( hwnd );
72     MESSAGEQUEUE *pMsgQ = 0, *pCurMsgQ = 0;
73     BOOL16 bRet = 0;
74
75     /* Get the messageQ for the current thread */
76     if (!(pCurMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue16() )))
77     {
78         WARN( win, "\tCurrent message queue not found. Exiting!\n" );
79         goto CLEANUP;
80     }
81
82     if (wndPtr)
83     {
84           /* Check if we can set the focus to this window */
85
86         while ( (wndPtr->dwStyle & (WS_CHILD | WS_POPUP)) == WS_CHILD  )
87         {
88             if ( wndPtr->dwStyle & ( WS_MINIMIZE | WS_DISABLED) )
89                  goto CLEANUP;
90             if (!(wndPtr = wndPtr->parent)) goto CLEANUP;
91             hwndTop = wndPtr->hwndSelf;
92         }
93
94         /* Retrieve the message queue associated with this window */
95         pMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( wndPtr->hmemTaskQ );
96         if ( !pMsgQ )
97         {
98             WARN( win, "\tMessage queue not found. Exiting!\n" );
99             goto CLEANUP;
100         }
101
102         /* Make sure that message queue for the window we are setting focus to
103          * shares the same perQ data as the current threads message queue.
104          * In other words you can't set focus to a window owned by a different
105          * thread unless AttachThreadInput has been called previously.
106          * (see AttachThreadInput and SetFocus docs)
107          */
108         if ( pCurMsgQ->pQData != pMsgQ->pQData )
109             goto CLEANUP;
110
111         /* Get the current focus window from the perQ data */
112         hWndFocus = PERQDATA_GetFocusWnd( pMsgQ->pQData );
113         
114         if( hwnd == hWndFocus )
115         {
116             bRet = 1;      // Success
117             goto CLEANUP;  // Nothing to do
118         }
119         
120         /* call hooks */
121         if( HOOK_CallHooks16( WH_CBT, HCBT_SETFOCUS, (WPARAM16)hwnd,
122                               (LPARAM)hWndFocus) )
123             goto CLEANUP;
124
125         /* activate hwndTop if needed. */
126         if (hwndTop != GetActiveWindow())
127         {
128             if (!WINPOS_SetActiveWindow(hwndTop, 0, 0)) goto CLEANUP;
129
130             if (!IsWindow( hwnd )) goto CLEANUP;  /* Abort if window destroyed */
131         }
132
133         /* Get the current focus window from the perQ data */
134         hWndFocus = PERQDATA_GetFocusWnd( pMsgQ->pQData );
135         
136         /* Change focus and send messages */
137         FOCUS_SwitchFocus( pMsgQ, hWndFocus, hwnd );
138     }
139     else /* NULL hwnd passed in */
140     {
141         if( HOOK_CallHooks16( WH_CBT, HCBT_SETFOCUS, 0, (LPARAM)hWndFocus ) )
142             goto CLEANUP;
143
144         /* Get the current focus from the perQ data of the current message Q */
145         hWndFocus = PERQDATA_GetFocusWnd( pCurMsgQ->pQData );
146
147       /* Change focus and send messages */
148         FOCUS_SwitchFocus( pCurMsgQ, hWndFocus, hwnd );
149     }
150
151     bRet = 1;      // Success
152     
153 CLEANUP:
154
155     /* Unlock the queues before returning */
156     if ( pMsgQ )
157         QUEUE_Unlock( pMsgQ );
158     if ( pCurMsgQ )
159         QUEUE_Unlock( pCurMsgQ );
160
161     WIN_ReleaseWndPtr(wndPtr);
162     return bRet ? hWndFocus : 0;
163 }
164
165
166 /*****************************************************************
167  *               GetFocus16   (USER.23)
168  */
169 HWND16 WINAPI GetFocus16(void)
170 {
171     return (HWND16)GetFocus();
172 }
173
174
175 /*****************************************************************
176  *               GetFocus32   (USER32.240)
177  */
178 HWND WINAPI GetFocus(void)
179 {
180     MESSAGEQUEUE *pCurMsgQ = 0;
181     HWND hwndFocus = 0;
182
183     /* Get the messageQ for the current thread */
184     if (!(pCurMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue16() )))
185     {
186         WARN( win, "\tCurrent message queue not found. Exiting!\n" );
187         return 0;
188     }
189
190     /* Get the current focus from the perQ data of the current message Q */
191     hwndFocus = PERQDATA_GetFocusWnd( pCurMsgQ->pQData );
192
193     QUEUE_Unlock( pCurMsgQ );
194
195     return hwndFocus;
196 }