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