user32/tests: Fix a few failures in DPI scaling mode.
[wine.git] / dlls / user32 / focus.c
blobf1c883167ed52c2cec0116f969547ee8e0dc887c
1 /*
2 * Focus and activation functions
4 * Copyright 1993 David Metcalfe
5 * Copyright 1995 Alex Korobka
6 * Copyright 1994, 2002 Alexandre Julliard
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.
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.
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
23 #include "config.h"
24 #include "wine/port.h"
26 #include <stdarg.h>
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"
37 WINE_DEFAULT_DEBUG_CHANNEL(win);
40 /*****************************************************************
41 * set_focus_window
43 * Change the focus window, sending the WM_SETFOCUS and WM_KILLFOCUS messages
45 static HWND set_focus_window( HWND hwnd )
47 HWND previous = 0;
48 BOOL ret;
50 SERVER_START_REQ( set_focus_window )
52 req->handle = wine_server_user_handle( hwnd );
53 if ((ret = !wine_server_call_err( req )))
54 previous = wine_server_ptr_handle( reply->previous );
56 SERVER_END_REQ;
57 if (!ret) return 0;
58 if (previous == hwnd) return previous;
60 if (previous)
62 SendMessageW( previous, WM_KILLFOCUS, (WPARAM)hwnd, 0 );
63 if (hwnd != GetFocus()) return previous; /* changed by the message */
65 if (IsWindow(hwnd))
67 USER_Driver->pSetFocus(hwnd);
68 SendMessageW( hwnd, WM_SETFOCUS, (WPARAM)previous, 0 );
70 return previous;
74 /*******************************************************************
75 * set_active_window
77 static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus )
79 HWND previous = GetActiveWindow();
80 BOOL ret;
81 DWORD old_thread, new_thread;
82 CBTACTIVATESTRUCT cbt;
84 if (previous == hwnd)
86 if (prev) *prev = hwnd;
87 return TRUE;
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;
95 if (IsWindow(previous))
97 SendMessageW( previous, WM_NCACTIVATE, FALSE, (LPARAM)hwnd );
98 SendMessageW( previous, WM_ACTIVATE,
99 MAKEWPARAM( WA_INACTIVE, IsIconic(previous) ), (LPARAM)hwnd );
102 SERVER_START_REQ( set_active_window )
104 req->handle = wine_server_user_handle( hwnd );
105 if ((ret = !wine_server_call_err( req )))
106 previous = wine_server_ptr_handle( reply->previous );
108 SERVER_END_REQ;
109 if (!ret) return FALSE;
110 if (prev) *prev = previous;
111 if (previous == hwnd) return TRUE;
113 if (hwnd)
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;
122 old_thread = previous ? GetWindowThreadProcessId( previous, NULL ) : 0;
123 new_thread = hwnd ? GetWindowThreadProcessId( hwnd, NULL ) : 0;
125 if (old_thread != new_thread)
127 HWND *list, *phwnd;
129 if ((list = WIN_ListChildren( GetDesktopWindow() )))
131 if (old_thread)
133 for (phwnd = list; *phwnd; phwnd++)
135 if (GetWindowThreadProcessId( *phwnd, NULL ) == old_thread)
136 SendMessageW( *phwnd, WM_ACTIVATEAPP, 0, new_thread );
139 if (new_thread)
141 for (phwnd = list; *phwnd; phwnd++)
143 if (GetWindowThreadProcessId( *phwnd, NULL ) == new_thread)
144 SendMessageW( *phwnd, WM_ACTIVATEAPP, 1, old_thread );
147 HeapFree( GetProcessHeap(), 0, list );
151 if (IsWindow(hwnd))
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 if (GetAncestor( hwnd, GA_PARENT ) == GetDesktopWindow())
158 PostMessageW( GetDesktopWindow(), WM_PARENTNOTIFY, WM_NCACTIVATE, (LPARAM)hwnd );
161 /* now change focus if necessary */
162 if (focus)
164 GUITHREADINFO info;
166 info.cbSize = sizeof(info);
167 GetGUIThreadInfo( GetCurrentThreadId(), &info );
168 /* Do not change focus if the window is no more active */
169 if (hwnd == info.hwndActive)
171 if (!info.hwndFocus || !hwnd || GetAncestor( info.hwndFocus, GA_ROOT ) != hwnd)
172 set_focus_window( hwnd );
176 return TRUE;
180 /*******************************************************************
181 * set_foreground_window
183 static BOOL set_foreground_window( HWND hwnd, BOOL mouse )
185 BOOL ret, send_msg_old = FALSE, send_msg_new = FALSE;
186 HWND previous = 0;
188 SERVER_START_REQ( set_foreground_window )
190 req->handle = wine_server_user_handle( hwnd );
191 if ((ret = !wine_server_call_err( req )))
193 previous = wine_server_ptr_handle( reply->previous );
194 send_msg_old = reply->send_msg_old;
195 send_msg_new = reply->send_msg_new;
198 SERVER_END_REQ;
200 if (ret && previous != hwnd)
202 if (send_msg_old) /* old window belongs to other thread */
203 SendNotifyMessageW( previous, WM_WINE_SETACTIVEWINDOW, 0, 0 );
204 else if (send_msg_new) /* old window belongs to us but new one to other thread */
205 ret = set_active_window( 0, NULL, mouse, TRUE );
207 if (send_msg_new) /* new window belongs to other thread */
208 SendNotifyMessageW( hwnd, WM_WINE_SETACTIVEWINDOW, (WPARAM)hwnd, 0 );
209 else /* new window belongs to us */
210 ret = set_active_window( hwnd, NULL, mouse, TRUE );
212 return ret;
216 /*******************************************************************
217 * FOCUS_MouseActivate
219 * Activate a window as a result of a mouse click
221 BOOL FOCUS_MouseActivate( HWND hwnd )
223 return set_foreground_window( hwnd, TRUE );
227 /*******************************************************************
228 * SetActiveWindow (USER32.@)
230 HWND WINAPI SetActiveWindow( HWND hwnd )
232 HWND prev;
234 TRACE( "%p\n", hwnd );
236 if (hwnd)
238 LONG style;
240 hwnd = WIN_GetFullHandle( hwnd );
241 if (!IsWindow( hwnd ))
243 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
244 return 0;
247 style = GetWindowLongW( hwnd, GWL_STYLE );
248 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD)
249 return GetActiveWindow(); /* Windows doesn't seem to return an error here */
252 if (!set_active_window( hwnd, &prev, FALSE, TRUE )) return 0;
253 return prev;
257 /*****************************************************************
258 * SetFocus (USER32.@)
260 HWND WINAPI SetFocus( HWND hwnd )
262 HWND hwndTop = hwnd;
263 HWND previous = GetFocus();
265 TRACE( "%p prev %p\n", hwnd, previous );
267 if (hwnd)
269 /* Check if we can set the focus to this window */
270 hwnd = WIN_GetFullHandle( hwnd );
271 if (!IsWindow( hwnd ))
273 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
274 return 0;
276 if (hwnd == previous) return previous; /* nothing to do */
277 for (;;)
279 HWND parent;
280 LONG style = GetWindowLongW( hwndTop, GWL_STYLE );
281 if (style & (WS_MINIMIZE | WS_DISABLED)) return 0;
282 if (!(style & WS_CHILD)) break;
283 parent = GetAncestor( hwndTop, GA_PARENT );
284 if (!parent || parent == GetDesktopWindow())
286 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return 0;
287 break;
289 if (parent == get_hwnd_message_parent()) return 0;
290 hwndTop = parent;
293 /* call hooks */
294 if (HOOK_CallHooks( WH_CBT, HCBT_SETFOCUS, (WPARAM)hwnd, (LPARAM)previous, TRUE )) return 0;
296 /* activate hwndTop if needed. */
297 if (hwndTop != GetActiveWindow())
299 if (!set_active_window( hwndTop, NULL, FALSE, FALSE )) return 0;
300 if (!IsWindow( hwnd )) return 0; /* Abort if window destroyed */
302 /* Do not change focus if the window is no longer active */
303 if (hwndTop != GetActiveWindow()) return 0;
306 else /* NULL hwnd passed in */
308 if (!previous) return 0; /* nothing to do */
309 if (HOOK_CallHooks( WH_CBT, HCBT_SETFOCUS, 0, (LPARAM)previous, TRUE )) return 0;
312 /* change focus and send messages */
313 return set_focus_window( hwnd );
317 /*******************************************************************
318 * SetForegroundWindow (USER32.@)
320 BOOL WINAPI SetForegroundWindow( HWND hwnd )
322 TRACE( "%p\n", hwnd );
324 hwnd = WIN_GetFullHandle( hwnd );
325 return set_foreground_window( hwnd, FALSE );
329 /*******************************************************************
330 * GetActiveWindow (USER32.@)
332 HWND WINAPI GetActiveWindow(void)
334 HWND ret = 0;
336 SERVER_START_REQ( get_thread_input )
338 req->tid = GetCurrentThreadId();
339 if (!wine_server_call_err( req )) ret = wine_server_ptr_handle( reply->active );
341 SERVER_END_REQ;
342 return ret;
346 /*****************************************************************
347 * GetFocus (USER32.@)
349 HWND WINAPI GetFocus(void)
351 HWND ret = 0;
353 SERVER_START_REQ( get_thread_input )
355 req->tid = GetCurrentThreadId();
356 if (!wine_server_call_err( req )) ret = wine_server_ptr_handle( reply->focus );
358 SERVER_END_REQ;
359 return ret;
363 /*******************************************************************
364 * GetForegroundWindow (USER32.@)
366 HWND WINAPI GetForegroundWindow(void)
368 HWND ret = 0;
370 SERVER_START_REQ( get_thread_input )
372 req->tid = 0;
373 if (!wine_server_call_err( req )) ret = wine_server_ptr_handle( reply->foreground );
375 SERVER_END_REQ;
376 return ret;
380 /***********************************************************************
381 * SetShellWindowEx (USER32.@)
382 * hwndShell = Progman[Program Manager]
383 * |-> SHELLDLL_DefView
384 * hwndListView = | |-> SysListView32
385 * | | |-> tooltips_class32
386 * | |
387 * | |-> SysHeader32
389 * |-> ProxyTarget
391 BOOL WINAPI SetShellWindowEx(HWND hwndShell, HWND hwndListView)
393 BOOL ret;
395 if (GetShellWindow())
396 return FALSE;
398 if (GetWindowLongW(hwndShell, GWL_EXSTYLE) & WS_EX_TOPMOST)
399 return FALSE;
401 if (hwndListView != hwndShell)
402 if (GetWindowLongW(hwndListView, GWL_EXSTYLE) & WS_EX_TOPMOST)
403 return FALSE;
405 if (hwndListView && hwndListView!=hwndShell)
406 SetWindowPos(hwndListView, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
408 SetWindowPos(hwndShell, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
410 SERVER_START_REQ(set_global_windows)
412 req->flags = SET_GLOBAL_SHELL_WINDOWS;
413 req->shell_window = wine_server_user_handle( hwndShell );
414 req->shell_listview = wine_server_user_handle( hwndListView );
415 ret = !wine_server_call_err(req);
417 SERVER_END_REQ;
419 return ret;
423 /*******************************************************************
424 * SetShellWindow (USER32.@)
426 BOOL WINAPI SetShellWindow(HWND hwndShell)
428 return SetShellWindowEx(hwndShell, hwndShell);
432 /*******************************************************************
433 * GetShellWindow (USER32.@)
435 HWND WINAPI GetShellWindow(void)
437 HWND hwndShell = 0;
439 SERVER_START_REQ(set_global_windows)
441 req->flags = 0;
442 if (!wine_server_call_err(req))
443 hwndShell = wine_server_ptr_handle( reply->old_shell_window );
445 SERVER_END_REQ;
447 return hwndShell;
451 /***********************************************************************
452 * SetProgmanWindow (USER32.@)
454 HWND WINAPI SetProgmanWindow ( HWND hwnd )
456 SERVER_START_REQ(set_global_windows)
458 req->flags = SET_GLOBAL_PROGMAN_WINDOW;
459 req->progman_window = wine_server_user_handle( hwnd );
460 if (wine_server_call_err( req )) hwnd = 0;
462 SERVER_END_REQ;
463 return hwnd;
467 /***********************************************************************
468 * GetProgmanWindow (USER32.@)
470 HWND WINAPI GetProgmanWindow(void)
472 HWND ret = 0;
474 SERVER_START_REQ(set_global_windows)
476 req->flags = 0;
477 if (!wine_server_call_err(req))
478 ret = wine_server_ptr_handle( reply->old_progman_window );
480 SERVER_END_REQ;
481 return ret;
485 /***********************************************************************
486 * SetTaskmanWindow (USER32.@)
487 * NOTES
488 * hwnd = MSTaskSwWClass
489 * |-> SysTabControl32
491 HWND WINAPI SetTaskmanWindow ( HWND hwnd )
493 SERVER_START_REQ(set_global_windows)
495 req->flags = SET_GLOBAL_TASKMAN_WINDOW;
496 req->taskman_window = wine_server_user_handle( hwnd );
497 if (wine_server_call_err( req )) hwnd = 0;
499 SERVER_END_REQ;
500 return hwnd;
503 /***********************************************************************
504 * GetTaskmanWindow (USER32.@)
506 HWND WINAPI GetTaskmanWindow(void)
508 HWND ret = 0;
510 SERVER_START_REQ(set_global_windows)
512 req->flags = 0;
513 if (!wine_server_call_err(req))
514 ret = wine_server_ptr_handle( reply->old_taskman_window );
516 SERVER_END_REQ;
517 return ret;