user32: Fix MapWindowPoints behavior in the process that owns the desktop window.
[wine] / dlls / user32 / focus.c
1 /*
2  * Focus and activation functions
3  *
4  * Copyright 1993 David Metcalfe
5  * Copyright 1995 Alex Korobka
6  * Copyright 1994, 2002 Alexandre Julliard
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22
23 #include "config.h"
24 #include "wine/port.h"
25
26 #include <stdarg.h>
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winuser.h"
31 #include "win.h"
32 #include "user_private.h"
33 #include "wine/server.h"
34 #include "wine/debug.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(win);
37
38
39 /*****************************************************************
40  *              set_focus_window
41  *
42  * Change the focus window, sending the WM_SETFOCUS and WM_KILLFOCUS messages
43  */
44 static HWND set_focus_window( HWND hwnd )
45 {
46     HWND previous = 0;
47     BOOL ret;
48
49     SERVER_START_REQ( set_focus_window )
50     {
51         req->handle = hwnd;
52         if ((ret = !wine_server_call_err( req ))) previous = reply->previous;
53     }
54     SERVER_END_REQ;
55     if (!ret) return 0;
56     if (previous == hwnd) return previous;
57
58     if (previous)
59     {
60         SendMessageW( previous, WM_KILLFOCUS, (WPARAM)hwnd, 0 );
61         if (hwnd != GetFocus()) return previous;  /* changed by the message */
62     }
63     if (IsWindow(hwnd))
64     {
65         USER_Driver->pSetFocus(hwnd);
66         SendMessageW( hwnd, WM_SETFOCUS, (WPARAM)previous, 0 );
67     }
68     return previous;
69 }
70
71
72 /*******************************************************************
73  *              set_active_window
74  */
75 static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus )
76 {
77     HWND previous = GetActiveWindow();
78     BOOL ret;
79     DWORD old_thread, new_thread;
80     CBTACTIVATESTRUCT cbt;
81
82     if (previous == hwnd)
83     {
84         if (prev) *prev = hwnd;
85         return TRUE;
86     }
87
88     /* call CBT hook chain */
89     cbt.fMouse     = mouse;
90     cbt.hWndActive = previous;
91     if (HOOK_CallHooks( WH_CBT, HCBT_ACTIVATE, (WPARAM)hwnd, (LPARAM)&cbt, TRUE )) return FALSE;
92
93     if (IsWindow(previous))
94     {
95         SendMessageW( previous, WM_NCACTIVATE, FALSE, (LPARAM)hwnd );
96         SendMessageW( previous, WM_ACTIVATE,
97                       MAKEWPARAM( WA_INACTIVE, IsIconic(previous) ), (LPARAM)hwnd );
98     }
99
100     SERVER_START_REQ( set_active_window )
101     {
102         req->handle = hwnd;
103         if ((ret = !wine_server_call_err( req ))) previous = reply->previous;
104     }
105     SERVER_END_REQ;
106     if (!ret) return FALSE;
107     if (prev) *prev = previous;
108     if (previous == hwnd) return TRUE;
109
110     if (hwnd)
111     {
112         /* send palette messages */
113         if (SendMessageW( hwnd, WM_QUERYNEWPALETTE, 0, 0 ))
114             SendMessageTimeoutW( HWND_BROADCAST, WM_PALETTEISCHANGING, (WPARAM)hwnd, 0,
115                                  SMTO_ABORTIFHUNG, 2000, NULL );
116         if (!IsWindow(hwnd)) return FALSE;
117     }
118
119     old_thread = previous ? GetWindowThreadProcessId( previous, NULL ) : 0;
120     new_thread = hwnd ? GetWindowThreadProcessId( hwnd, NULL ) : 0;
121
122     if (old_thread != new_thread)
123     {
124         HWND *list, *phwnd;
125
126         if ((list = WIN_ListChildren( GetDesktopWindow() )))
127         {
128             if (old_thread)
129             {
130                 for (phwnd = list; *phwnd; phwnd++)
131                 {
132                     if (GetWindowThreadProcessId( *phwnd, NULL ) == old_thread)
133                         SendMessageW( *phwnd, WM_ACTIVATEAPP, 0, new_thread );
134                 }
135             }
136             if (new_thread)
137             {
138                 for (phwnd = list; *phwnd; phwnd++)
139                 {
140                     if (GetWindowThreadProcessId( *phwnd, NULL ) == new_thread)
141                         SendMessageW( *phwnd, WM_ACTIVATEAPP, 1, old_thread );
142                 }
143             }
144             HeapFree( GetProcessHeap(), 0, list );
145         }
146     }
147
148     if (IsWindow(hwnd))
149     {
150         SendMessageW( hwnd, WM_NCACTIVATE, (hwnd == GetForegroundWindow()), (LPARAM)previous );
151         SendMessageW( hwnd, WM_ACTIVATE,
152                       MAKEWPARAM( mouse ? WA_CLICKACTIVE : WA_ACTIVE, IsIconic(hwnd) ),
153                       (LPARAM)previous );
154     }
155
156     /* now change focus if necessary */
157     if (focus)
158     {
159         GUITHREADINFO info;
160
161         GetGUIThreadInfo( GetCurrentThreadId(), &info );
162         /* Do not change focus if the window is no more active */
163         if (hwnd == info.hwndActive)
164         {
165             if (!info.hwndFocus || !hwnd || GetAncestor( info.hwndFocus, GA_ROOT ) != hwnd)
166                 set_focus_window( hwnd );
167         }
168     }
169
170     return TRUE;
171 }
172
173
174 /*******************************************************************
175  *              set_foreground_window
176  */
177 static BOOL set_foreground_window( HWND hwnd, BOOL mouse )
178 {
179     BOOL ret, send_msg_old = FALSE, send_msg_new = FALSE;
180     HWND previous = 0;
181
182     SERVER_START_REQ( set_foreground_window )
183     {
184         req->handle = hwnd;
185         if ((ret = !wine_server_call_err( req )))
186         {
187             previous = reply->previous;
188             send_msg_old = reply->send_msg_old;
189             send_msg_new = reply->send_msg_new;
190         }
191     }
192     SERVER_END_REQ;
193
194     if (ret)
195     {
196         if (send_msg_old)  /* old window belongs to other thread */
197             SendNotifyMessageW( previous, WM_WINE_SETACTIVEWINDOW, 0, 0 );
198         else if (send_msg_new)  /* old window belongs to us but new one to other thread */
199             ret = set_active_window( 0, NULL, mouse, TRUE );
200
201         if (send_msg_new)  /* new window belongs to other thread */
202             SendNotifyMessageW( hwnd, WM_WINE_SETACTIVEWINDOW, (WPARAM)hwnd, 0 );
203         else  /* new window belongs to us */
204             ret = set_active_window( hwnd, NULL, mouse, TRUE );
205     }
206     return ret;
207 }
208
209
210 /*******************************************************************
211  *              FOCUS_MouseActivate
212  *
213  * Activate a window as a result of a mouse click
214  */
215 BOOL FOCUS_MouseActivate( HWND hwnd )
216 {
217     return set_foreground_window( hwnd, TRUE );
218 }
219
220
221 /*******************************************************************
222  *              SetActiveWindow (USER32.@)
223  */
224 HWND WINAPI SetActiveWindow( HWND hwnd )
225 {
226     HWND prev;
227
228     TRACE( "%p\n", hwnd );
229
230     if (hwnd)
231     {
232         LONG style = GetWindowLongW( hwnd, GWL_STYLE );
233
234         if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD)
235             return GetActiveWindow();  /* Windows doesn't seem to return an error here */
236
237         hwnd = WIN_GetFullHandle( hwnd );
238     }
239
240     if (!set_active_window( hwnd, &prev, FALSE, TRUE )) return 0;
241     return prev;
242 }
243
244
245 /*****************************************************************
246  *              SetFocus  (USER32.@)
247  */
248 HWND WINAPI SetFocus( HWND hwnd )
249 {
250     HWND hwndTop = hwnd;
251     HWND previous = GetFocus();
252
253     TRACE( "%p prev %p\n", hwnd, previous );
254
255     if (hwnd)
256     {
257         /* Check if we can set the focus to this window */
258         hwnd = WIN_GetFullHandle( hwnd );
259         if (hwnd == previous) return previous;  /* nothing to do */
260         for (;;)
261         {
262             HWND parent;
263             LONG style = GetWindowLongW( hwndTop, GWL_STYLE );
264             if (style & (WS_MINIMIZE | WS_DISABLED)) return 0;
265             parent = GetAncestor( hwndTop, GA_PARENT );
266             if (!parent || parent == GetDesktopWindow()) break;
267             if (parent == get_hwnd_message_parent()) return 0;
268             hwndTop = parent;
269         }
270
271         /* call hooks */
272         if (HOOK_CallHooks( WH_CBT, HCBT_SETFOCUS, (WPARAM)hwnd, (LPARAM)previous, TRUE )) return 0;
273
274         /* activate hwndTop if needed. */
275         if (hwndTop != GetActiveWindow())
276         {
277             if (!set_active_window( hwndTop, NULL, FALSE, FALSE )) return 0;
278             if (!IsWindow( hwnd )) return 0;  /* Abort if window destroyed */
279         }
280     }
281     else /* NULL hwnd passed in */
282     {
283         if (!previous) return 0;  /* nothing to do */
284         if (HOOK_CallHooks( WH_CBT, HCBT_SETFOCUS, 0, (LPARAM)previous, TRUE )) return 0;
285     }
286
287     /* change focus and send messages */
288     return set_focus_window( hwnd );
289 }
290
291
292 /*******************************************************************
293  *              SetForegroundWindow  (USER32.@)
294  */
295 BOOL WINAPI SetForegroundWindow( HWND hwnd )
296 {
297     TRACE( "%p\n", hwnd );
298
299     hwnd = WIN_GetFullHandle( hwnd );
300     return set_foreground_window( hwnd, FALSE );
301 }
302
303
304 /*******************************************************************
305  *              GetActiveWindow  (USER32.@)
306  */
307 HWND WINAPI GetActiveWindow(void)
308 {
309     HWND ret = 0;
310
311     SERVER_START_REQ( get_thread_input )
312     {
313         req->tid = GetCurrentThreadId();
314         if (!wine_server_call_err( req )) ret = reply->active;
315     }
316     SERVER_END_REQ;
317     return ret;
318 }
319
320
321 /*****************************************************************
322  *              GetFocus  (USER32.@)
323  */
324 HWND WINAPI GetFocus(void)
325 {
326     HWND ret = 0;
327
328     SERVER_START_REQ( get_thread_input )
329     {
330         req->tid = GetCurrentThreadId();
331         if (!wine_server_call_err( req )) ret = reply->focus;
332     }
333     SERVER_END_REQ;
334     return ret;
335 }
336
337
338 /*******************************************************************
339  *              GetForegroundWindow  (USER32.@)
340  */
341 HWND WINAPI GetForegroundWindow(void)
342 {
343     HWND ret = 0;
344
345     SERVER_START_REQ( get_thread_input )
346     {
347         req->tid = 0;
348         if (!wine_server_call_err( req )) ret = reply->foreground;
349     }
350     SERVER_END_REQ;
351     return ret;
352 }
353
354
355 /***********************************************************************
356 *               SetShellWindowEx (USER32.@)
357 * hwndShell =    Progman[Program Manager]
358 *                |-> SHELLDLL_DefView
359 * hwndListView = |   |-> SysListView32
360 *                |   |   |-> tooltips_class32
361 *                |   |
362 *                |   |-> SysHeader32
363 *                |
364 *                |-> ProxyTarget
365 */
366 BOOL WINAPI SetShellWindowEx(HWND hwndShell, HWND hwndListView)
367 {
368     BOOL ret;
369
370     if (GetShellWindow())
371         return FALSE;
372
373     if (GetWindowLongW(hwndShell, GWL_EXSTYLE) & WS_EX_TOPMOST)
374         return FALSE;
375
376     if (hwndListView != hwndShell)
377         if (GetWindowLongW(hwndListView, GWL_EXSTYLE) & WS_EX_TOPMOST)
378             return FALSE;
379
380     if (hwndListView && hwndListView!=hwndShell)
381         SetWindowPos(hwndListView, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
382
383     SetWindowPos(hwndShell, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
384
385     SERVER_START_REQ(set_global_windows)
386     {
387         req->flags          = SET_GLOBAL_SHELL_WINDOWS;
388         req->shell_window   = hwndShell;
389         req->shell_listview = hwndListView;
390         ret = !wine_server_call_err(req);
391     }
392     SERVER_END_REQ;
393
394     return ret;
395 }
396
397
398 /*******************************************************************
399 *               SetShellWindow (USER32.@)
400 */
401 BOOL WINAPI SetShellWindow(HWND hwndShell)
402 {
403     return SetShellWindowEx(hwndShell, hwndShell);
404 }
405
406
407 /*******************************************************************
408 *               GetShellWindow (USER32.@)
409 */
410 HWND WINAPI GetShellWindow(void)
411 {
412     HWND hwndShell = 0;
413
414     SERVER_START_REQ(set_global_windows)
415     {
416         req->flags = 0;
417         if (!wine_server_call_err(req))
418             hwndShell = reply->old_shell_window;
419     }
420     SERVER_END_REQ;
421
422     return hwndShell;
423 }
424
425
426 /***********************************************************************
427  *              SetProgmanWindow (USER32.@)
428  */
429 HWND WINAPI SetProgmanWindow ( HWND hwnd )
430 {
431     SERVER_START_REQ(set_global_windows)
432     {
433         req->flags          = SET_GLOBAL_PROGMAN_WINDOW;
434         req->progman_window = hwnd;
435         if (wine_server_call_err( req )) hwnd = 0;
436     }
437     SERVER_END_REQ;
438     return hwnd;
439 }
440
441
442 /***********************************************************************
443  *              GetProgmanWindow (USER32.@)
444  */
445 HWND WINAPI GetProgmanWindow(void)
446 {
447     HWND ret = 0;
448
449     SERVER_START_REQ(set_global_windows)
450     {
451         req->flags = 0;
452         if (!wine_server_call_err(req)) ret = reply->old_progman_window;
453     }
454     SERVER_END_REQ;
455     return ret;
456 }
457
458
459 /***********************************************************************
460  *              SetTaskmanWindow (USER32.@)
461  * NOTES
462  *   hwnd = MSTaskSwWClass
463  *          |-> SysTabControl32
464  */
465 HWND WINAPI SetTaskmanWindow ( HWND hwnd )
466 {
467     SERVER_START_REQ(set_global_windows)
468     {
469         req->flags          = SET_GLOBAL_TASKMAN_WINDOW;
470         req->taskman_window = hwnd;
471         if (wine_server_call_err( req )) hwnd = 0;
472     }
473     SERVER_END_REQ;
474     return hwnd;
475 }
476
477 /***********************************************************************
478  *              GetTaskmanWindow (USER32.@)
479  */
480 HWND WINAPI GetTaskmanWindow(void)
481 {
482     HWND ret = 0;
483
484     SERVER_START_REQ(set_global_windows)
485     {
486         req->flags = 0;
487         if (!wine_server_call_err(req)) ret = reply->old_taskman_window;
488     }
489     SERVER_END_REQ;
490     return ret;
491 }