From 5030bda77c6b0e01544b84d3ddfab9a01d30b62a Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Fri, 11 Oct 2002 23:41:06 +0000 Subject: [PATCH] Reimplemented Get/SetActiveWindow, Get/SetFocus and Get/SetForegroundWindow by storing the information in the server. Implemented correct inter-process window activation. --- dlls/user/Makefile.in | 2 +- dlls/user/focus.c | 348 ++++++++++++++++++++++++++ dlls/user/message.c | 2 + dlls/x11drv/event.c | 39 +-- dlls/x11drv/winpos.c | 2 +- include/user.h | 3 +- include/win.h | 5 +- include/wine/server_protocol.h | 50 +++- include/winpos.h | 2 - server/protocol.def | 24 ++ server/queue.c | 77 ++++++ server/request.h | 6 + server/trace.c | 42 ++++ server/user.h | 2 + server/window.c | 25 ++ windows/focus.c | 180 -------------- windows/message.c | 51 ++-- windows/nonclient.c | 2 +- windows/win.c | 9 +- windows/winpos.c | 432 +++------------------------------ 20 files changed, 665 insertions(+), 638 deletions(-) create mode 100644 dlls/user/focus.c delete mode 100644 windows/focus.c diff --git a/dlls/user/Makefile.in b/dlls/user/Makefile.in index 8ff5b7d1ac8..c375c0e58bc 100644 --- a/dlls/user/Makefile.in +++ b/dlls/user/Makefile.in @@ -31,7 +31,6 @@ C_SRCS = \ $(TOPOBJDIR)/windows/defwnd.c \ $(TOPOBJDIR)/windows/dialog.c \ $(TOPOBJDIR)/windows/driver.c \ - $(TOPOBJDIR)/windows/focus.c \ $(TOPOBJDIR)/windows/hook.c \ $(TOPOBJDIR)/windows/input.c \ $(TOPOBJDIR)/windows/keyboard.c \ @@ -63,6 +62,7 @@ C_SRCS = \ dde/server.c \ display.c \ exticon.c \ + focus.c \ lstr.c \ message.c \ misc.c \ diff --git a/dlls/user/focus.c b/dlls/user/focus.c new file mode 100644 index 00000000000..cceffa5a09c --- /dev/null +++ b/dlls/user/focus.c @@ -0,0 +1,348 @@ +/* + * Focus and activation functions + * + * Copyright 1993 David Metcalfe + * Copyright 1995 Alex Korobka + * Copyright 1994, 2002 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "winbase.h" +#include "winuser.h" +#include "winerror.h" +#include "win.h" +#include "hook.h" +#include "message.h" +#include "user.h" +#include "wine/server.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(win); + + +/***************************************************************** + * set_focus_window + * + * Change the focus window, sending the WM_SETFOCUS and WM_KILLFOCUS messages + */ +static HWND set_focus_window( HWND hwnd ) +{ + HWND previous = 0; + BOOL ret; + + SERVER_START_REQ( set_focus_window ) + { + req->handle = hwnd; + if ((ret = !wine_server_call_err( req ))) previous = reply->previous; + } + SERVER_END_REQ; + if (!ret) return 0; + if (previous == hwnd) return previous; + + if (previous) + { + SendMessageW( previous, WM_KILLFOCUS, (WPARAM)hwnd, 0 ); + if (hwnd != GetFocus()) return previous; /* changed by the message */ + } + if (IsWindow(hwnd)) + { + if (USER_Driver.pSetFocus) USER_Driver.pSetFocus(hwnd); + SendMessageW( hwnd, WM_SETFOCUS, (WPARAM)previous, 0 ); + } + return previous; +} + + +/******************************************************************* + * set_active_window + */ +static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) +{ + HWND previous = GetActiveWindow(); + BOOL ret; + DWORD old_thread, new_thread; + + if (previous == hwnd) + { + if (prev) *prev = hwnd; + return TRUE; + } + + /* call CBT hook chain */ + if (HOOK_IsHooked( WH_CBT )) + { + CBTACTIVATESTRUCT cbt; + cbt.fMouse = mouse; + cbt.hWndActive = previous; + if (HOOK_CallHooksW( WH_CBT, HCBT_ACTIVATE, (WPARAM)hwnd, (LPARAM)&cbt )) return FALSE; + } + + if (IsWindow(previous)) + { + SendMessageW( previous, WM_NCACTIVATE, FALSE, 0 ); + SendMessageW( previous, WM_ACTIVATE, + MAKEWPARAM( WA_INACTIVE, IsIconic(previous) ), (LPARAM)hwnd ); + } + + SERVER_START_REQ( set_active_window ) + { + req->handle = hwnd; + if ((ret = !wine_server_call_err( req ))) previous = reply->previous; + } + SERVER_END_REQ; + if (!ret) return FALSE; + if (prev) *prev = previous; + if (previous == hwnd) return TRUE; + + if (hwnd) + { + /* send palette messages */ + if (SendMessageW( hwnd, WM_QUERYNEWPALETTE, 0, 0 )) + SendMessageW( HWND_BROADCAST, WM_PALETTEISCHANGING, (WPARAM)hwnd, 0 ); + + if (!(GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_MANAGED)) + SetWindowPos( hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE ); + + if (!IsWindow(hwnd)) return FALSE; + } + + old_thread = previous ? GetWindowThreadProcessId( previous, NULL ) : 0; + new_thread = hwnd ? GetWindowThreadProcessId( hwnd, NULL ) : 0; + + if (old_thread != new_thread) + { + HWND *list, *phwnd; + + if ((list = WIN_ListChildren( GetDesktopWindow() ))) + { + if (old_thread) + { + for (phwnd = list; *phwnd; phwnd++) + { + if (GetWindowThreadProcessId( *phwnd, NULL ) == old_thread) + SendMessageW( *phwnd, WM_ACTIVATEAPP, 0, new_thread ); + } + } + if (new_thread) + { + for (phwnd = list; *phwnd; phwnd++) + { + if (GetWindowThreadProcessId( *phwnd, NULL ) == new_thread) + SendMessageW( *phwnd, WM_ACTIVATEAPP, 1, old_thread ); + } + } + HeapFree( GetProcessHeap(), 0, list ); + } + } + + if (IsWindow(hwnd)) + { + SendMessageW( hwnd, WM_NCACTIVATE, (hwnd == GetForegroundWindow()), 0 ); + SendMessageW( hwnd, WM_ACTIVATE, + MAKEWPARAM( mouse ? WA_CLICKACTIVE : WA_ACTIVE, IsIconic(hwnd) ), + (LPARAM)previous ); + } + + /* now change focus if necessary */ + if (focus) + { + HWND curfocus = GetFocus(); + if (!curfocus || !hwnd || GetAncestor( curfocus, GA_ROOT ) != hwnd) + set_focus_window( hwnd ); + } + + return TRUE; +} + + +/******************************************************************* + * set_foreground_window + */ +static BOOL set_foreground_window( HWND hwnd, BOOL mouse ) +{ + BOOL ret, send_msg_old = FALSE, send_msg_new = FALSE; + HWND previous = 0; + + SERVER_START_REQ( set_foreground_window ) + { + req->handle = hwnd; + if ((ret = !wine_server_call_err( req ))) + { + previous = reply->previous; + send_msg_old = reply->send_msg_old; + send_msg_new = reply->send_msg_new; + } + } + SERVER_END_REQ; + + if (ret) + { + if (send_msg_old) /* old window belongs to other thread */ + SendNotifyMessageW( previous, WM_WINE_SETACTIVEWINDOW, 0, 0 ); + else if (send_msg_new) /* old window belongs to us but new one to other thread */ + ret = set_active_window( 0, NULL, mouse, TRUE ); + + if (send_msg_new) /* new window belongs to other thread */ + SendNotifyMessageW( hwnd, WM_WINE_SETACTIVEWINDOW, hwnd, 0 ); + else /* new window belongs to us */ + ret = set_active_window( hwnd, NULL, mouse, TRUE ); + } + return ret; +} + + +/******************************************************************* + * FOCUS_MouseActivate + * + * Activate a window as a result of a mouse click + */ +BOOL FOCUS_MouseActivate( HWND hwnd ) +{ + return set_foreground_window( hwnd, TRUE ); +} + + +/******************************************************************* + * SetActiveWindow (USER32.@) + */ +HWND WINAPI SetActiveWindow( HWND hwnd ) +{ + HWND prev; + + TRACE( "%x\n", hwnd ); + + if (hwnd) + { + LONG style = GetWindowLongW( hwnd, GWL_STYLE ); + + if (!(style & WS_VISIBLE) || (style & (WS_POPUP|WS_CHILD)) == WS_CHILD) + return GetActiveWindow(); /* Windows doesn't seem to return an error here */ + + hwnd = WIN_GetFullHandle( hwnd ); + } + + if (!set_active_window( hwnd, &prev, FALSE, TRUE )) return 0; + return prev; +} + + +/***************************************************************** + * SetFocus (USER32.@) + */ +HWND WINAPI SetFocus( HWND hwnd ) +{ + HWND hwndTop = hwnd; + HWND previous = GetFocus(); + + TRACE( "%x prev %x\n", hwnd, previous ); + + if (hwnd) + { + /* Check if we can set the focus to this window */ + hwnd = WIN_GetFullHandle( hwnd ); + if (hwnd == previous) return previous; /* nothing to do */ + for (;;) + { + HWND parent; + LONG style = GetWindowLongW( hwndTop, GWL_STYLE ); + if (style & (WS_MINIMIZE | WS_DISABLED)) return 0; + parent = GetAncestor( hwndTop, GA_PARENT ); + if (!parent || parent == GetDesktopWindow()) break; + hwndTop = parent; + } + + /* call hooks */ + if (HOOK_CallHooksW( WH_CBT, HCBT_SETFOCUS, (WPARAM)hwnd, (LPARAM)previous )) return 0; + + /* activate hwndTop if needed. */ + if (hwndTop != GetActiveWindow()) + { + if (!set_active_window( hwndTop, NULL, FALSE, FALSE )) return 0; + if (!IsWindow( hwnd )) return 0; /* Abort if window destroyed */ + } + } + else /* NULL hwnd passed in */ + { + if (!previous) return 0; /* nothing to do */ + if( HOOK_CallHooksW( WH_CBT, HCBT_SETFOCUS, 0, (LPARAM)previous ) ) + return 0; + } + + /* change focus and send messages */ + return set_focus_window( hwnd ); +} + + +/******************************************************************* + * SetForegroundWindow (USER32.@) + */ +BOOL WINAPI SetForegroundWindow( HWND hwnd ) +{ + TRACE( "%x\n", hwnd ); + if (hwnd) hwnd = WIN_GetFullHandle( hwnd ); + return set_foreground_window( hwnd, FALSE ); +} + + +/******************************************************************* + * GetActiveWindow (USER32.@) + */ +HWND WINAPI GetActiveWindow(void) +{ + HWND ret = 0; + + SERVER_START_REQ( get_thread_input ) + { + req->tid = GetCurrentThreadId(); + if (!wine_server_call_err( req )) ret = reply->active; + } + SERVER_END_REQ; + return ret; +} + + +/***************************************************************** + * GetFocus (USER32.@) + */ +HWND WINAPI GetFocus(void) +{ + HWND ret = 0; + + SERVER_START_REQ( get_thread_input ) + { + req->tid = GetCurrentThreadId(); + if (!wine_server_call_err( req )) ret = reply->focus; + } + SERVER_END_REQ; + return ret; +} + + +/******************************************************************* + * GetForegroundWindow (USER32.@) + */ +HWND WINAPI GetForegroundWindow(void) +{ + HWND ret = 0; + + SERVER_START_REQ( get_thread_input ) + { + req->tid = 0; + if (!wine_server_call_err( req )) ret = reply->foreground; + } + SERVER_END_REQ; + return ret; +} diff --git a/dlls/user/message.c b/dlls/user/message.c index 8621241abe8..1b433265344 100644 --- a/dlls/user/message.c +++ b/dlls/user/message.c @@ -1060,6 +1060,8 @@ static LRESULT handle_internal_message( HWND hwnd, UINT msg, WPARAM wparam, LPAR return (LRESULT)SetWindowLongW( hwnd, wparam, lparam ); case WM_WINE_ENABLEWINDOW: return EnableWindow( hwnd, wparam ); + case WM_WINE_SETACTIVEWINDOW: + return SetActiveWindow( (HWND)wparam ); default: FIXME( "unknown internal message %x\n", msg ); return 0; diff --git a/dlls/x11drv/event.c b/dlls/x11drv/event.c index 10b40ad5441..4f5993eaac5 100644 --- a/dlls/x11drv/event.c +++ b/dlls/x11drv/event.c @@ -364,6 +364,20 @@ static void EVENT_ProcessEvent( XEvent *event ) } +/******************************************************************* + * can_activate_window + * + * Check if we can activate the specified window. + */ +inline static BOOL can_activate_window( HWND hwnd ) +{ + LONG style = GetWindowLongW( hwnd, GWL_STYLE ); + if (!(style & WS_VISIBLE)) return FALSE; + if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE; + return !(style & WS_DISABLED); +} + + /********************************************************************** * set_focus_error_handler * @@ -380,16 +394,12 @@ static int set_focus_error_handler( Display *display, XErrorEvent *event, void * */ static void set_focus( HWND hwnd, Time time ) { - HWND focus = GetFocus(); + HWND focus; Window win; - if (hwnd != focus && !IsChild( hwnd, focus )) - { - TRACE( "changing window focus to %x\n", hwnd ); - SetFocus( hwnd ); - } + TRACE( "setting foreground window to %x\n", hwnd ); + SetForegroundWindow( hwnd ); - /* focus window might be changed by the above SetFocus() call */ focus = GetFocus(); win = X11DRV_get_whole_window(focus); @@ -430,7 +440,7 @@ static void handle_wm_protocols_message( HWND hwnd, XClientMessageEvent *event ) hwnd, IsWindowEnabled(hwnd), GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus ); - if (IsWindowEnabled(hwnd)) + if (can_activate_window(hwnd)) { /* simulate a mouse click on the caption to find out * whether the window wants to be activated */ @@ -445,7 +455,7 @@ static void handle_wm_protocols_message( HWND hwnd, XClientMessageEvent *event ) hwnd = GetFocus(); if (!hwnd) hwnd = GetActiveWindow(); if (!hwnd) hwnd = last_focus; - if (hwnd && IsWindowEnabled(hwnd)) set_focus( hwnd, event_time ); + if (hwnd && can_activate_window(hwnd)) set_focus( hwnd, event_time ); } } } @@ -475,17 +485,14 @@ static void EVENT_FocusIn( HWND hwnd, XFocusChangeEvent *event ) if (wmTakeFocus) return; /* ignore FocusIn if we are using take focus */ if (event->detail == NotifyPointer) return; - if (!IsWindowEnabled(hwnd)) + if (!can_activate_window(hwnd)) { HWND hwnd = GetFocus(); if (!hwnd) hwnd = GetActiveWindow(); if (!hwnd) hwnd = x11drv_thread_data()->last_focus; - if (hwnd && IsWindowEnabled(hwnd)) set_focus( hwnd, CurrentTime ); - } - else if (hwnd != GetForegroundWindow()) - { - SetForegroundWindow( hwnd ); + if (hwnd && can_activate_window(hwnd)) set_focus( hwnd, CurrentTime ); } + else SetForegroundWindow( hwnd ); } @@ -503,6 +510,7 @@ static void EVENT_FocusOut( HWND hwnd, XFocusChangeEvent *event ) TRACE( "win %x xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] ); if (event->detail == NotifyPointer) return; + x11drv_thread_data()->last_focus = hwnd; if (hwnd != GetForegroundWindow()) return; SendMessageA( hwnd, WM_CANCELMODE, 0, 0 ); @@ -527,7 +535,6 @@ static void EVENT_FocusOut( HWND hwnd, XFocusChangeEvent *event ) if (hwnd == GetForegroundWindow()) { TRACE( "lost focus, setting fg to 0\n" ); - x11drv_thread_data()->last_focus = hwnd; SetForegroundWindow( 0 ); } } diff --git a/dlls/x11drv/winpos.c b/dlls/x11drv/winpos.c index b279f405f4c..6f0f9776957 100644 --- a/dlls/x11drv/winpos.c +++ b/dlls/x11drv/winpos.c @@ -1025,7 +1025,7 @@ BOOL X11DRV_SetWindowPos( WINDOWPOS *winpos ) if ((GetWindowLongW( winpos->hwnd, GWL_STYLE ) & (WS_CHILD | WS_POPUP)) == WS_CHILD) SendMessageA( winpos->hwnd, WM_CHILDACTIVATE, 0, 0 ); else - SetActiveWindow( winpos->hwnd ); + SetForegroundWindow( winpos->hwnd ); } /* And last, send the WM_WINDOWPOSCHANGED message */ diff --git a/include/user.h b/include/user.h index 13fbbd741be..69f75cf649b 100644 --- a/include/user.h +++ b/include/user.h @@ -57,7 +57,8 @@ enum wine_internal_message WM_WINE_SHOWWINDOW, WM_WINE_SETPARENT, WM_WINE_SETWINDOWLONG, - WM_WINE_ENABLEWINDOW + WM_WINE_ENABLEWINDOW, + WM_WINE_SETACTIVEWINDOW }; /* internal SendInput codes (FIXME) */ diff --git a/include/win.h b/include/win.h index 7c69f62aa28..c83e9e16f3e 100644 --- a/include/win.h +++ b/include/win.h @@ -143,6 +143,8 @@ extern void CARET_GetRect(LPRECT lprc); /* windows/caret.c */ extern HBRUSH DEFWND_ControlColor( HDC hDC, UINT ctlType ); /* windows/defwnd.c */ +extern BOOL FOCUS_MouseActivate( HWND hwnd ); + /* Classes functions */ struct tagCLASS; /* opaque structure */ struct builtin_class_descr; @@ -153,7 +155,4 @@ extern struct tagCLASS *CLASS_AddWindow( ATOM atom, HINSTANCE inst, WINDOWPROCTY extern void CLASS_RemoveWindow( struct tagCLASS *cls ); extern void CLASS_FreeModuleClasses( HMODULE16 hModule ); -/* windows/focus.c */ -extern void FOCUS_SwitchFocus( struct tagMESSAGEQUEUE *pMsgQ, HWND , HWND ); - #endif /* __WINE_WIN_H */ diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 01b6e19ead9..7e0457d7b48 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -2507,6 +2507,7 @@ struct get_window_info_reply { struct reply_header __header; user_handle_t full_handle; + user_handle_t last_active; process_id_t pid; thread_id_t tid; atom_t atom; @@ -2763,6 +2764,44 @@ struct get_thread_input_reply }; +struct set_foreground_window_request +{ + struct request_header __header; + user_handle_t handle; +}; +struct set_foreground_window_reply +{ + struct reply_header __header; + user_handle_t previous; + int send_msg_old; + int send_msg_new; +}; + + +struct set_focus_window_request +{ + struct request_header __header; + user_handle_t handle; +}; +struct set_focus_window_reply +{ + struct reply_header __header; + user_handle_t previous; +}; + + +struct set_active_window_request +{ + struct request_header __header; + user_handle_t handle; +}; +struct set_active_window_reply +{ + struct reply_header __header; + user_handle_t previous; +}; + + enum request { REQ_new_process, @@ -2923,6 +2962,9 @@ enum request REQ_get_window_properties, REQ_attach_thread_input, REQ_get_thread_input, + REQ_set_foreground_window, + REQ_set_focus_window, + REQ_set_active_window, REQ_NB_REQUESTS }; @@ -3088,6 +3130,9 @@ union generic_request struct get_window_properties_request get_window_properties_request; struct attach_thread_input_request attach_thread_input_request; struct get_thread_input_request get_thread_input_request; + struct set_foreground_window_request set_foreground_window_request; + struct set_focus_window_request set_focus_window_request; + struct set_active_window_request set_active_window_request; }; union generic_reply { @@ -3251,8 +3296,11 @@ union generic_reply struct get_window_properties_reply get_window_properties_reply; struct attach_thread_input_reply attach_thread_input_reply; struct get_thread_input_reply get_thread_input_reply; + struct set_foreground_window_reply set_foreground_window_reply; + struct set_focus_window_reply set_focus_window_reply; + struct set_active_window_reply set_active_window_reply; }; -#define SERVER_PROTOCOL_VERSION 85 +#define SERVER_PROTOCOL_VERSION 86 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/include/winpos.h b/include/winpos.h index 3b052286933..f972b55412d 100644 --- a/include/winpos.h +++ b/include/winpos.h @@ -39,8 +39,6 @@ extern BOOL WINPOS_RedrawIconTitle( HWND hWnd ); extern BOOL WINPOS_ShowIconTitle( HWND hwnd, BOOL bShow ); extern void WINPOS_GetMinMaxInfo( HWND hwnd, POINT *maxSize, POINT *maxPos, POINT *minTrack, POINT *maxTrack ); -extern BOOL WINPOS_SetActiveWindow( HWND hWnd, BOOL fMouse, - BOOL fChangeFocus ); extern LONG WINPOS_HandleWindowPosChanging16(HWND hwnd, struct tagWINDOWPOS16 *winpos); extern LONG WINPOS_HandleWindowPosChanging(HWND hwnd, WINDOWPOS *winpos); extern HWND WINPOS_WindowFromPoint( HWND hwndScope, POINT pt, INT *hittest ); diff --git a/server/protocol.def b/server/protocol.def index 43f4fa0fce9..6e72e97e9c4 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1762,6 +1762,7 @@ enum message_type user_handle_t handle; /* handle to the window */ @REPLY user_handle_t full_handle; /* full 32-bit handle */ + user_handle_t last_active; /* last active popup */ process_id_t pid; /* process owning the window */ thread_id_t tid; /* thread owning the window */ atom_t atom; /* class atom */ @@ -1931,3 +1932,26 @@ enum message_type user_handle_t caret; /* handle to the caret window */ rectangle_t rect; /* caret rectangle */ @END + +/* Set the system foreground window */ +@REQ(set_foreground_window) + user_handle_t handle; /* handle to the foreground window */ +@REPLY + user_handle_t previous; /* handle to the previous foreground window */ + int send_msg_old; /* whether we have to send a msg to the old window */ + int send_msg_new; /* whether we have to send a msg to the new window */ +@END + +/* Set the current thread focus window */ +@REQ(set_focus_window) + user_handle_t handle; /* handle to the focus window */ +@REPLY + user_handle_t previous; /* handle to the previous focus window */ +@END + +/* Set the current thread active window */ +@REQ(set_active_window) + user_handle_t handle; /* handle to the active window */ +@REPLY + user_handle_t previous; /* handle to the previous active window */ +@END diff --git a/server/queue.c b/server/queue.c index 5a08668563a..5d51e62aec7 100644 --- a/server/queue.c +++ b/server/queue.c @@ -621,6 +621,25 @@ inline static void thread_input_cleanup_window( struct msg_queue *queue, user_ha if (window == input->caret) input->caret = 0; } +/* check if the specified window can be set in the input data of a given queue */ +static int check_queue_input_window( struct msg_queue *queue, user_handle_t window ) +{ + struct thread *thread; + int ret = 0; + + if (!window) return 1; /* we can always clear the data */ + + if ((thread = get_window_thread( window ))) + { + ret = (queue->input == thread->queue->input); + if (!ret) set_error( STATUS_ACCESS_DENIED ); + release_object( thread ); + } + else set_error( STATUS_INVALID_HANDLE ); + + return ret; +} + /* attach two thread input data structures */ int attach_thread_input( struct thread *thread_from, struct thread *thread_to ) { @@ -1292,3 +1311,61 @@ DECL_HANDLER(get_thread_input) reply->foreground = foreground_input ? foreground_input->active : 0; if (thread) release_object( thread ); } + + +/* set the system foreground window */ +DECL_HANDLER(set_foreground_window) +{ + struct msg_queue *queue = get_current_queue(); + + reply->previous = foreground_input ? foreground_input->active : 0; + reply->send_msg_old = (reply->previous && foreground_input != queue->input); + reply->send_msg_new = FALSE; + + if (req->handle) + { + struct thread *thread; + + if (is_top_level_window( req->handle ) && + ((thread = get_window_thread( req->handle )))) + { + foreground_input = thread->queue->input; + reply->send_msg_new = (foreground_input != queue->input); + release_object( thread ); + } + else set_error( STATUS_INVALID_HANDLE ); + } + else foreground_input = NULL; +} + + +/* set the current thread focus window */ +DECL_HANDLER(set_focus_window) +{ + struct msg_queue *queue = get_current_queue(); + + reply->previous = 0; + if (queue && check_queue_input_window( queue, req->handle )) + { + reply->previous = queue->input->focus; + queue->input->focus = get_user_full_handle( req->handle ); + } +} + + +/* set the current thread active window */ +DECL_HANDLER(set_active_window) +{ + struct msg_queue *queue = get_current_queue(); + + reply->previous = 0; + if (queue && check_queue_input_window( queue, req->handle )) + { + if (!req->handle || make_window_active( req->handle )) + { + reply->previous = queue->input->active; + queue->input->active = get_user_full_handle( req->handle ); + } + else set_error( STATUS_INVALID_HANDLE ); + } +} diff --git a/server/request.h b/server/request.h index 3f1a0367dab..a6076d7fad9 100644 --- a/server/request.h +++ b/server/request.h @@ -261,6 +261,9 @@ DECL_HANDLER(get_window_property); DECL_HANDLER(get_window_properties); DECL_HANDLER(attach_thread_input); DECL_HANDLER(get_thread_input); +DECL_HANDLER(set_foreground_window); +DECL_HANDLER(set_focus_window); +DECL_HANDLER(set_active_window); #ifdef WANT_REQUEST_HANDLERS @@ -425,6 +428,9 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_get_window_properties, (req_handler)req_attach_thread_input, (req_handler)req_get_thread_input, + (req_handler)req_set_foreground_window, + (req_handler)req_set_focus_window, + (req_handler)req_set_active_window, }; #endif /* WANT_REQUEST_HANDLERS */ diff --git a/server/trace.c b/server/trace.c index 776370f7999..e680e589f46 100644 --- a/server/trace.c +++ b/server/trace.c @@ -2005,6 +2005,7 @@ static void dump_get_window_info_request( const struct get_window_info_request * static void dump_get_window_info_reply( const struct get_window_info_reply *req ) { fprintf( stderr, " full_handle=%08x,", req->full_handle ); + fprintf( stderr, " last_active=%08x,", req->last_active ); fprintf( stderr, " pid=%08x,", req->pid ); fprintf( stderr, " tid=%08x,", req->tid ); fprintf( stderr, " atom=%04x", req->atom ); @@ -2200,6 +2201,38 @@ static void dump_get_thread_input_reply( const struct get_thread_input_reply *re dump_rectangle( &req->rect ); } +static void dump_set_foreground_window_request( const struct set_foreground_window_request *req ) +{ + fprintf( stderr, " handle=%08x", req->handle ); +} + +static void dump_set_foreground_window_reply( const struct set_foreground_window_reply *req ) +{ + fprintf( stderr, " previous=%08x,", req->previous ); + fprintf( stderr, " send_msg_old=%d,", req->send_msg_old ); + fprintf( stderr, " send_msg_new=%d", req->send_msg_new ); +} + +static void dump_set_focus_window_request( const struct set_focus_window_request *req ) +{ + fprintf( stderr, " handle=%08x", req->handle ); +} + +static void dump_set_focus_window_reply( const struct set_focus_window_reply *req ) +{ + fprintf( stderr, " previous=%08x", req->previous ); +} + +static void dump_set_active_window_request( const struct set_active_window_request *req ) +{ + fprintf( stderr, " handle=%08x", req->handle ); +} + +static void dump_set_active_window_reply( const struct set_active_window_reply *req ) +{ + fprintf( stderr, " previous=%08x", req->previous ); +} + static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_new_process_request, (dump_func)dump_get_new_process_info_request, @@ -2359,6 +2392,9 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_get_window_properties_request, (dump_func)dump_attach_thread_input_request, (dump_func)dump_get_thread_input_request, + (dump_func)dump_set_foreground_window_request, + (dump_func)dump_set_focus_window_request, + (dump_func)dump_set_active_window_request, }; static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { @@ -2520,6 +2556,9 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_get_window_properties_reply, (dump_func)0, (dump_func)dump_get_thread_input_reply, + (dump_func)dump_set_foreground_window_reply, + (dump_func)dump_set_focus_window_reply, + (dump_func)dump_set_active_window_reply, }; static const char * const req_names[REQ_NB_REQUESTS] = { @@ -2681,6 +2720,9 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "get_window_properties", "attach_thread_input", "get_thread_input", + "set_foreground_window", + "set_focus_window", + "set_active_window", }; /* ### make_requests end ### */ diff --git a/server/user.h b/server/user.h index b1cc439c000..c153d782d58 100644 --- a/server/user.h +++ b/server/user.h @@ -54,6 +54,8 @@ extern void post_message( user_handle_t win, unsigned int message, extern void destroy_thread_windows( struct thread *thread ); extern int is_child_window( user_handle_t parent, user_handle_t child ); +extern int is_top_level_window( user_handle_t window ); +extern int make_window_active( user_handle_t window ); extern struct thread *get_window_thread( user_handle_t handle ); extern user_handle_t find_window_to_repaint( user_handle_t parent, struct thread *thread ); diff --git a/server/window.c b/server/window.c index ad51288bd9a..46317073577 100644 --- a/server/window.c +++ b/server/window.c @@ -62,6 +62,7 @@ struct window user_handle_t handle; /* full handle for this window */ struct thread *thread; /* thread owning the window */ atom_t atom; /* class atom */ + user_handle_t last_active; /* last active popup */ rectangle_t window_rect; /* window rectangle */ rectangle_t client_rect; /* client rectangle */ unsigned int style; /* window style */ @@ -268,6 +269,7 @@ static struct window *create_window( struct window *parent, struct window *owner win->first_unlinked = NULL; win->thread = current; win->atom = atom; + win->last_active = win->handle; win->style = 0; win->ex_style = 0; win->id = 0; @@ -321,6 +323,27 @@ int is_child_window( user_handle_t parent, user_handle_t child ) return 0; } +/* check whether window is a top-level window */ +int is_top_level_window( user_handle_t window ) +{ + struct window *win = get_user_object( window, USER_WINDOW ); + return (win && win->parent == top_window); +} + +/* make a window active if possible */ +int make_window_active( user_handle_t window ) +{ + struct window *owner, *win = get_window( window ); + + if (!win) return 0; + + /* set last active for window and its owner */ + win->last_active = win->handle; + if ((owner = get_user_object( win->owner, USER_WINDOW ))) owner->last_active = win->handle; + return 1; +} + + /* return the thread owning a window */ struct thread *get_window_thread( user_handle_t handle ) { @@ -478,6 +501,8 @@ DECL_HANDLER(get_window_info) if (win) { reply->full_handle = win->handle; + reply->last_active = win->handle; + if (get_user_object( win->last_active, USER_WINDOW )) reply->last_active = win->last_active; if (win->thread) { reply->tid = get_thread_id( win->thread ); diff --git a/windows/focus.c b/windows/focus.c deleted file mode 100644 index 61898dae5db..00000000000 --- a/windows/focus.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Focus functions - * - * Copyright 1993 David Metcalfe - * 1994 Alexandre Julliard - * 1995 Alex Korobka - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "windef.h" -#include "wingdi.h" -#include "wine/winuser16.h" -#include "win.h" -#include "winpos.h" -#include "hook.h" -#include "message.h" -#include "queue.h" -#include "user.h" -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(win); - - -/***************************************************************** - * FOCUS_SwitchFocus - * pMsgQ is the queue whose perQData focus is to be modified - */ -void FOCUS_SwitchFocus( MESSAGEQUEUE *pMsgQ, HWND hFocusFrom, HWND hFocusTo ) -{ - PERQDATA_SetFocusWnd( pMsgQ->pQData, hFocusTo ); - - if (hFocusFrom) - SendNotifyMessageA( hFocusFrom, WM_KILLFOCUS, (WPARAM)hFocusTo, 0 ); - - if( !hFocusTo || hFocusTo != PERQDATA_GetFocusWnd( pMsgQ->pQData ) ) - { - return; - } - - /* According to API docs, the WM_SETFOCUS message is sent AFTER the window - has received the keyboard focus. */ - if (USER_Driver.pSetFocus) USER_Driver.pSetFocus(hFocusTo); - - SendMessageA( hFocusTo, WM_SETFOCUS, (WPARAM)hFocusFrom, 0 ); -} - - -/***************************************************************** - * SetFocus (USER32.@) - */ -HWND WINAPI SetFocus( HWND hwnd ) -{ - HWND hWndFocus = 0, hwndTop = hwnd; - MESSAGEQUEUE *pMsgQ = 0, *pCurMsgQ = 0; - BOOL bRet = 0; - - /* Get the messageQ for the current thread */ - if (!(pCurMsgQ = QUEUE_Current())) - { - WARN("\tCurrent message queue not found. Exiting!\n" ); - return 0; - } - - if (hwnd) - { - /* Check if we can set the focus to this window */ - WND *wndPtr; - - hwnd = WIN_GetFullHandle( hwnd ); - for (;;) - { - HWND parent; - LONG style = GetWindowLongW( hwndTop, GWL_STYLE ); - if (style & (WS_MINIMIZE | WS_DISABLED)) return 0; - parent = GetAncestor( hwndTop, GA_PARENT ); - if (!parent || parent == GetDesktopWindow()) break; - hwndTop = parent; - } - - if (!(wndPtr = WIN_FindWndPtr( hwndTop ))) return 0; - - /* Retrieve the message queue associated with this window */ - pMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( wndPtr->hmemTaskQ ); - WIN_ReleaseWndPtr( wndPtr ); - if ( !pMsgQ ) - { - WARN("\tMessage queue not found. Exiting!\n" ); - return 0; - } - - /* 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_CallHooksA( WH_CBT, HCBT_SETFOCUS, (WPARAM)hwnd, (LPARAM)hWndFocus) ) - goto CLEANUP; - - /* activate hwndTop if needed. */ - if (hwndTop != GetActiveWindow()) - { - if (!WINPOS_SetActiveWindow(hwndTop, 0, 0)) goto CLEANUP; - - if (!IsWindow( 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 /* NULL hwnd passed in */ - { - if( HOOK_CallHooksA( WH_CBT, HCBT_SETFOCUS, 0, (LPARAM)hWndFocus ) ) - return 0; - - /* Get the current focus from the perQ data of the current message Q */ - hWndFocus = PERQDATA_GetFocusWnd( pCurMsgQ->pQData ); - - /* Change focus and send messages */ - FOCUS_SwitchFocus( pCurMsgQ, hWndFocus, hwnd ); - } - - bRet = 1; /* Success */ - -CLEANUP: - - /* Unlock the queues before returning */ - if ( pMsgQ ) - QUEUE_Unlock( pMsgQ ); - - return bRet ? hWndFocus : 0; -} - - -/***************************************************************** - * GetFocus (USER32.@) - */ -HWND WINAPI GetFocus(void) -{ - MESSAGEQUEUE *pCurMsgQ = 0; - - /* Get the messageQ for the current thread */ - if (!(pCurMsgQ = QUEUE_Current())) - { - WARN("\tCurrent message queue not found. Exiting!\n" ); - return 0; - } - - /* Get the current focus from the perQ data of the current message Q */ - return PERQDATA_GetFocusWnd( pCurMsgQ->pQData ); -} diff --git a/windows/message.c b/windows/message.c index 5951bffa9a6..f789f2691c4 100644 --- a/windows/message.c +++ b/windows/message.c @@ -512,8 +512,6 @@ static BOOL process_cooked_mouse_message( MSG *msg, ULONG_PTR extra_info, BOOL r (raw_message == WM_RBUTTONDOWN) || (raw_message == WM_MBUTTONDOWN)) { - HWND hwndTop = GetAncestor( msg->hwnd, GA_ROOT ); - /* Send the WM_PARENTNOTIFY, * note that even for double/nonclient clicks * notification message is still WM_L/M/RBUTTONDOWN. @@ -522,32 +520,37 @@ static BOOL process_cooked_mouse_message( MSG *msg, ULONG_PTR extra_info, BOOL r /* Activate the window if needed */ - if (msg->hwnd != GetActiveWindow() && hwndTop != GetDesktopWindow()) + if (msg->hwnd != GetActiveWindow()) { - LONG ret = SendMessageA( msg->hwnd, WM_MOUSEACTIVATE, (WPARAM)hwndTop, - MAKELONG( hittest, raw_message ) ); - - switch(ret) + HWND hwndTop = msg->hwnd; + while (hwndTop) { - case MA_NOACTIVATEANDEAT: - eatMsg = TRUE; - /* fall through */ - case MA_NOACTIVATE: - break; - case MA_ACTIVATEANDEAT: - eatMsg = TRUE; - /* fall through */ - case MA_ACTIVATE: - case 0: - if (hwndTop != GetForegroundWindow() ) + if ((GetWindowLongW( hwndTop, GWL_STYLE ) & (WS_POPUP|WS_CHILD)) != WS_CHILD) break; + hwndTop = GetParent( hwndTop ); + } + + if (hwndTop && hwndTop != GetDesktopWindow()) + { + LONG ret = SendMessageA( msg->hwnd, WM_MOUSEACTIVATE, (WPARAM)hwndTop, + MAKELONG( hittest, raw_message ) ); + switch(ret) { - if (!WINPOS_SetActiveWindow( hwndTop, TRUE , TRUE )) - eatMsg = TRUE; + case MA_NOACTIVATEANDEAT: + eatMsg = TRUE; + /* fall through */ + case MA_NOACTIVATE: + break; + case MA_ACTIVATEANDEAT: + eatMsg = TRUE; + /* fall through */ + case MA_ACTIVATE: + case 0: + if (!FOCUS_MouseActivate( hwndTop )) eatMsg = TRUE; + break; + default: + WARN( "unknown WM_MOUSEACTIVATE code %ld\n", ret ); + break; } - break; - default: - WARN( "unknown WM_MOUSEACTIVATE code %ld\n", ret ); - break; } } } diff --git a/windows/nonclient.c b/windows/nonclient.c index dbf12703e51..180547bde0e 100644 --- a/windows/nonclient.c +++ b/windows/nonclient.c @@ -1999,7 +1999,7 @@ LONG NC_HandleNCLButtonDown( HWND hwnd, WPARAM wParam, LPARAM lParam ) { HWND top = GetAncestor( hwnd, GA_ROOT ); - if( WINPOS_SetActiveWindow(top, TRUE, TRUE) || (GetActiveWindow() == top) ) + if (FOCUS_MouseActivate( top ) || (GetActiveWindow() == top)) SendMessageW( hwnd, WM_SYSCOMMAND, SC_MOVE + HTCAPTION, lParam ); break; } diff --git a/windows/win.c b/windows/win.c index 782d29e963f..516f6442bcf 100644 --- a/windows/win.c +++ b/windows/win.c @@ -1479,7 +1479,10 @@ BOOL WINAPI DestroyWindow( HWND hwnd ) /* Hide the window */ - ShowWindow( hwnd, SW_HIDE ); + if (!ShowWindow( hwnd, SW_HIDE )) + { + if (hwnd == GetActiveWindow()) WINPOS_ActivateOtherWindow( hwnd ); + } if (!IsWindow(hwnd)) return TRUE; /* Recursively destroy owned windows */ @@ -1510,8 +1513,6 @@ BOOL WINAPI DestroyWindow( HWND hwnd ) if (!got_one) break; } - WINPOS_ActivateOtherWindow( hwnd ); - if ((owner = GetWindow( hwnd, GW_OWNER ))) { WND *ptr = WIN_FindWndPtr( owner ); @@ -3109,7 +3110,7 @@ BOOL WINAPI FlashWindow( HWND hWnd, BOOL bInvert ) { WPARAM16 wparam; if (bInvert) wparam = !(wndPtr->flags & WIN_NCACTIVATED); - else wparam = (hWnd == GetActiveWindow()); + else wparam = (hWnd == GetForegroundWindow()); WIN_ReleaseWndPtr(wndPtr); SendMessageW( hWnd, WM_NCACTIVATE, wparam, (LPARAM)0 ); diff --git a/windows/winpos.c b/windows/winpos.c index f27fdea6b52..cc26357ec03 100644 --- a/windows/winpos.c +++ b/windows/winpos.c @@ -69,14 +69,12 @@ typedef struct /* ----- internal variables ----- */ -static HWND hwndPrevActive = 0; /* Previously active window */ static HWND hGlobalShellWindow=0; /*the shell*/ static HWND hGlobalTaskmanWindow=0; static HWND hGlobalProgmanWindow=0; static LPCSTR atomInternalPos; -extern HQUEUE16 hActiveQueue; /*********************************************************************** * WINPOS_CreateInternalPosAtom @@ -95,30 +93,7 @@ BOOL WINPOS_CreateInternalPosAtom() */ void WINPOS_CheckInternalPos( HWND hwnd ) { - LPINTERNALPOS lpPos; - MESSAGEQUEUE *pMsgQ = 0; - WND *wndPtr = WIN_GetPtr( hwnd ); - - if (!wndPtr || wndPtr == WND_OTHER_PROCESS) return; - - lpPos = (LPINTERNALPOS) GetPropA( hwnd, atomInternalPos ); - - /* Retrieve the message queue associated with this window */ - pMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( wndPtr->hmemTaskQ ); - if ( !pMsgQ ) - { - WARN("\tMessage queue not found. Exiting!\n" ); - WIN_ReleasePtr( wndPtr ); - return; - } - - if( hwnd == hwndPrevActive ) hwndPrevActive = 0; - - if( hwnd == PERQDATA_GetActiveWnd( pMsgQ->pQData ) ) - { - PERQDATA_SetActiveWnd( pMsgQ->pQData, 0 ); - WARN("\tattempt to activate destroyed window!\n"); - } + LPINTERNALPOS lpPos = (LPINTERNALPOS) GetPropA( hwnd, atomInternalPos ); if( lpPos ) { @@ -126,9 +101,6 @@ void WINPOS_CheckInternalPos( HWND hwnd ) DestroyWindow( lpPos->hwndIconTitle ); HeapFree( GetProcessHeap(), 0, lpPos ); } - - QUEUE_Unlock( pMsgQ ); - WIN_ReleasePtr( wndPtr ); } /*********************************************************************** @@ -640,132 +612,6 @@ BOOL WINAPI IsZoomed(HWND hWnd) } -/******************************************************************* - * GetActiveWindow (USER32.@) - */ -HWND WINAPI GetActiveWindow(void) -{ - MESSAGEQUEUE *pCurMsgQ = 0; - - /* Get the messageQ for the current thread */ - if (!(pCurMsgQ = QUEUE_Current())) -{ - WARN("\tCurrent message queue not found. Exiting!\n" ); - return 0; - } - - /* Return the current active window from the perQ data of the current message Q */ - return PERQDATA_GetActiveWnd( pCurMsgQ->pQData ); -} - - -/******************************************************************* - * WINPOS_CanActivate - */ -static BOOL WINPOS_CanActivate(HWND hwnd) -{ - if (!hwnd) return FALSE; - return ((GetWindowLongW( hwnd, GWL_STYLE ) & (WS_DISABLED|WS_CHILD)) == 0); -} - -/******************************************************************* - * WINPOS_IsVisible - */ -static BOOL WINPOS_IsVisible(HWND hwnd) -{ - if (!hwnd) return FALSE; - return ((GetWindowLongW( hwnd, GWL_STYLE ) & WS_VISIBLE) == WS_VISIBLE); -} - - -/******************************************************************* - * SetActiveWindow (USER32.@) - */ -HWND WINAPI SetActiveWindow( HWND hwnd ) -{ - HWND prev = 0; - WND *wndPtr = WIN_FindWndPtr( hwnd ); - MESSAGEQUEUE *pMsgQ = 0, *pCurMsgQ = 0; - - if (!wndPtr) return 0; - - if (wndPtr->dwStyle & (WS_DISABLED | WS_CHILD)) goto error; - - /* Get the messageQ for the current thread */ - if (!(pCurMsgQ = QUEUE_Current())) - { - WARN("\tCurrent message queue not found. Exiting!\n" ); - goto error; - } - - /* Retrieve the message queue associated with this window */ - pMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( wndPtr->hmemTaskQ ); - if ( !pMsgQ ) - { - WARN("\tWindow message queue not found. Exiting!\n" ); - goto error; - } - - /* 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 ) - { - QUEUE_Unlock( pMsgQ ); - goto error; - } - - /* Save current active window */ - prev = PERQDATA_GetActiveWnd( pMsgQ->pQData ); - QUEUE_Unlock( pMsgQ ); - WIN_ReleaseWndPtr(wndPtr); - WINPOS_SetActiveWindow( hwnd, FALSE, TRUE ); - return prev; - - error: - WIN_ReleaseWndPtr(wndPtr); - return 0; -} - - -/******************************************************************* - * GetForegroundWindow (USER32.@) - */ -HWND WINAPI GetForegroundWindow(void) -{ - HWND hwndActive = 0; - - /* Get the foreground window (active window of hActiveQueue) */ - if ( hActiveQueue ) - { - MESSAGEQUEUE *pActiveQueue = QUEUE_Lock( hActiveQueue ); - if ( pActiveQueue ) - hwndActive = PERQDATA_GetActiveWnd( pActiveQueue->pQData ); - - QUEUE_Unlock( pActiveQueue ); - } - - return hwndActive; -} - -/******************************************************************* - * SetForegroundWindow (USER32.@) - */ -BOOL WINAPI SetForegroundWindow( HWND hwnd ) -{ - if (!hwnd) return WINPOS_SetActiveWindow( 0, FALSE, TRUE ); - - /* child windows get WM_CHILDACTIVATE message */ - if ((GetWindowLongW( hwnd, GWL_STYLE ) & (WS_CHILD | WS_POPUP)) == WS_CHILD) - return SendMessageA( hwnd, WM_CHILDACTIVATE, 0, 0 ); - - hwnd = WIN_GetFullHandle( hwnd ); - if( hwnd == GetForegroundWindow() ) return FALSE; - - return WINPOS_SetActiveWindow( hwnd, FALSE, TRUE ); -} - - /******************************************************************* * AllowSetForegroundWindow (USER32.@) */ @@ -1191,221 +1037,24 @@ void WINAPI SetInternalWindowPos( HWND hwnd, UINT showCmd, } } + /******************************************************************* - * WINPOS_SetActiveWindow + * can_activate_window * - * SetActiveWindow() back-end. This is the only function that - * can assign active status to a window. It must be called only - * for the top level windows. + * Check if we can activate the specified window. */ -BOOL WINPOS_SetActiveWindow( HWND hWnd, BOOL fMouse, BOOL fChangeFocus) +static BOOL can_activate_window( HWND hwnd ) { - WND* wndPtr=0, *wndTemp; - HQUEUE16 hOldActiveQueue, hNewActiveQueue; - MESSAGEQUEUE *pOldActiveQueue = 0, *pNewActiveQueue = 0; - WORD wIconized = 0; - HWND hwndActive = 0; - BOOL bRet = 0; + LONG style; - TRACE("(%04x, %d, %d)\n", hWnd, fMouse, fChangeFocus ); - - /* Get current active window from the active queue */ - if ( hActiveQueue ) - { - pOldActiveQueue = QUEUE_Lock( hActiveQueue ); - if ( pOldActiveQueue ) - hwndActive = PERQDATA_GetActiveWnd( pOldActiveQueue->pQData ); - } - - if ((wndPtr = WIN_FindWndPtr(hWnd))) - hWnd = wndPtr->hwndSelf; /* make it a full handle */ - - /* paranoid checks */ - if( hWnd == GetDesktopWindow() || (bRet = (hWnd == hwndActive)) ) - goto CLEANUP_END; - -/* if (wndPtr && (GetFastQueue16() != wndPtr->hmemTaskQ)) - * return 0; - */ - hOldActiveQueue = hActiveQueue; - - if( (wndTemp = WIN_FindWndPtr(hwndActive)) ) - { - wIconized = HIWORD(wndTemp->dwStyle & WS_MINIMIZE); - WIN_ReleaseWndPtr(wndTemp); - } - else - TRACE("no current active window.\n"); - - /* call CBT hook chain */ - if (HOOK_IsHooked( WH_CBT )) - { - CBTACTIVATESTRUCT cbt; - cbt.fMouse = fMouse; - cbt.hWndActive = hwndActive; - if (HOOK_CallHooksA( WH_CBT, HCBT_ACTIVATE, (WPARAM)hWnd, (LPARAM)&cbt )) goto CLEANUP_END; - } - - /* set prev active wnd to current active wnd and send notification */ - if ((hwndPrevActive = hwndActive) && IsWindow(hwndPrevActive)) - { - MESSAGEQUEUE *pTempActiveQueue = 0; - - SendNotifyMessageA( hwndPrevActive, WM_NCACTIVATE, FALSE, 0 ); - SendNotifyMessageA( hwndPrevActive, WM_ACTIVATE, - MAKEWPARAM( WA_INACTIVE, wIconized ), - (LPARAM)hWnd ); - - /* check if something happened during message processing - * (global active queue may have changed) - */ - pTempActiveQueue = QUEUE_Lock( hActiveQueue ); - if(!pTempActiveQueue) - goto CLEANUP_END; - - hwndActive = PERQDATA_GetActiveWnd( pTempActiveQueue->pQData ); - QUEUE_Unlock( pTempActiveQueue ); - if( hwndPrevActive != hwndActive ) - goto CLEANUP_END; - } - - /* Set new active window in the message queue */ - hwndActive = hWnd; - if ( wndPtr ) - { - pNewActiveQueue = QUEUE_Lock( wndPtr->hmemTaskQ ); - if ( pNewActiveQueue ) - PERQDATA_SetActiveWnd( pNewActiveQueue->pQData, hwndActive ); - } - else /* have to do this or MDI frame activation goes to hell */ - if( pOldActiveQueue ) - PERQDATA_SetActiveWnd( pOldActiveQueue->pQData, 0 ); - - /* send palette messages */ - if (hWnd && SendMessageW( hWnd, WM_QUERYNEWPALETTE, 0, 0L)) - SendMessageW( HWND_BROADCAST, WM_PALETTEISCHANGING, (WPARAM)hWnd, 0 ); - - /* if prev wnd is minimized redraw icon title */ - if( IsIconic( hwndPrevActive ) ) WINPOS_RedrawIconTitle(hwndPrevActive); - - /* managed windows will get ConfigureNotify event */ - if (wndPtr && !(wndPtr->dwStyle & WS_CHILD) && !(wndPtr->dwExStyle & WS_EX_MANAGED)) - { - /* check Z-order and bring hWnd to the top */ - HWND tmp = GetTopWindow(0); - while (tmp && !(GetWindowLongA( tmp, GWL_STYLE ) & WS_VISIBLE)) - tmp = GetWindow( tmp, GW_HWNDNEXT ); - - if( tmp != hWnd ) - SetWindowPos(hWnd, HWND_TOP, 0,0,0,0, - SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE ); - if (!IsWindow(hWnd)) - goto CLEANUP; - } - - /* Get a handle to the new active queue */ - hNewActiveQueue = wndPtr ? wndPtr->hmemTaskQ : 0; - - /* send WM_ACTIVATEAPP if necessary */ - if (hOldActiveQueue != hNewActiveQueue) - { - HWND *list, *phwnd; - DWORD old_thread = GetWindowThreadProcessId( hwndPrevActive, NULL ); - DWORD new_thread = GetWindowThreadProcessId( hwndActive, NULL ); - - if ((list = WIN_ListChildren( GetDesktopWindow() ))) - { - for (phwnd = list; *phwnd; phwnd++) - { - if (!IsWindow( *phwnd )) continue; - if (GetWindowThreadProcessId( *phwnd, NULL ) == old_thread) - SendNotifyMessageW( *phwnd, WM_ACTIVATEAPP, 0, new_thread ); - } - HeapFree( GetProcessHeap(), 0, list ); - } - - hActiveQueue = hNewActiveQueue; - - if ((list = WIN_ListChildren( GetDesktopWindow() ))) - { - for (phwnd = list; *phwnd; phwnd++) - { - if (!IsWindow( *phwnd )) continue; - if (GetWindowThreadProcessId( *phwnd, NULL ) == new_thread) - SendMessageW( *phwnd, WM_ACTIVATEAPP, 1, old_thread ); - } - HeapFree( GetProcessHeap(), 0, list ); - } - - if (hWnd && !IsWindow(hWnd)) goto CLEANUP; - } - - if (hWnd) - { - /* walk up to the first unowned window */ - HWND tmp = GetAncestor( hWnd, GA_ROOTOWNER ); - if ((wndTemp = WIN_FindWndPtr( tmp ))) - { - /* and set last active owned popup */ - wndTemp->hwndLastActive = hWnd; - - wIconized = HIWORD(wndTemp->dwStyle & WS_MINIMIZE); - WIN_ReleaseWndPtr(wndTemp); - } - SendMessageA( hWnd, WM_NCACTIVATE, TRUE, 0 ); - SendMessageA( hWnd, WM_ACTIVATE, - MAKEWPARAM( (fMouse) ? WA_CLICKACTIVE : WA_ACTIVE, wIconized), - (LPARAM)hwndPrevActive ); - if( !IsWindow(hWnd) ) goto CLEANUP; - } - - /* change focus if possible */ - if ( fChangeFocus ) - { - if ( pNewActiveQueue ) - { - HWND hOldFocus = PERQDATA_GetFocusWnd( pNewActiveQueue->pQData ); - - if ( !hOldFocus || GetAncestor( hOldFocus, GA_ROOT ) != hwndActive ) - FOCUS_SwitchFocus( pNewActiveQueue, hOldFocus, - (wndPtr && (wndPtr->dwStyle & WS_MINIMIZE))? - 0 : hwndActive ); - } - - if ( pOldActiveQueue && - ( !pNewActiveQueue || - pNewActiveQueue->pQData != pOldActiveQueue->pQData ) ) - { - HWND hOldFocus = PERQDATA_GetFocusWnd( pOldActiveQueue->pQData ); - if ( hOldFocus ) - FOCUS_SwitchFocus( pOldActiveQueue, hOldFocus, 0 ); - } - } - - if( !hwndPrevActive && wndPtr ) - { - if (USER_Driver.pForceWindowRaise) USER_Driver.pForceWindowRaise( wndPtr->hwndSelf ); - } - - /* if active wnd is minimized redraw icon title */ - if( IsIconic(hwndActive) ) WINPOS_RedrawIconTitle(hwndActive); - - bRet = (hWnd == hwndActive); /* Success? */ - -CLEANUP: /* Unlock the message queues before returning */ - - if ( pNewActiveQueue ) - QUEUE_Unlock( pNewActiveQueue ); - -CLEANUP_END: - - if ( pOldActiveQueue ) - QUEUE_Unlock( pOldActiveQueue ); - - WIN_ReleaseWndPtr(wndPtr); - return bRet; + if (!hwnd) return FALSE; + style = GetWindowLongW( hwnd, GWL_STYLE ); + if (!(style & WS_VISIBLE)) return FALSE; + if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE; + return !(style & WS_DISABLED); } + /******************************************************************* * WINPOS_ActivateOtherWindow * @@ -1413,54 +1062,29 @@ CLEANUP_END: */ void WINPOS_ActivateOtherWindow(HWND hwnd) { - HWND hwndActive = 0; - HWND hwndTo = 0; - HWND hwndDefaultTo = 0; - HWND owner; + HWND hwndTo, fg; - /* Get current active window from the active queue */ - if ( hActiveQueue ) + if ((GetWindowLongW( hwnd, GWL_STYLE ) & WS_POPUP) && (hwndTo = GetWindow( hwnd, GW_OWNER ))) { - MESSAGEQUEUE *pActiveQueue = QUEUE_Lock( hActiveQueue ); - if ( pActiveQueue ) - { - hwndActive = PERQDATA_GetActiveWnd( pActiveQueue->pQData ); - QUEUE_Unlock( pActiveQueue ); - } + hwndTo = GetAncestor( hwndTo, GA_ROOT ); + if (can_activate_window( hwndTo )) goto done; } - if (!(hwnd = WIN_IsCurrentThread( hwnd ))) return; - - if( hwnd == hwndPrevActive ) - hwndPrevActive = 0; - - if( hwndActive != hwnd && (hwndActive || USER_IsExitingThread( GetCurrentThreadId() ))) - return; - - if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_POPUP) || - !(owner = GetWindow( hwnd, GW_OWNER )) || - !WINPOS_CanActivate((hwndTo = GetAncestor( owner, GA_ROOT ))) || - !WINPOS_IsVisible(hwndTo)) + hwndTo = hwnd; + for (;;) { - HWND tmp = GetAncestor( hwnd, GA_ROOT ); - hwndTo = hwndPrevActive; - - while( !WINPOS_CanActivate(hwndTo) || !WINPOS_IsVisible(hwndTo)) - { - /* by now owned windows should've been taken care of */ - if(!hwndDefaultTo && WINPOS_CanActivate(hwndTo)) - hwndDefaultTo = hwndTo; - tmp = hwndTo = GetWindow( tmp, GW_HWNDNEXT ); - if( !hwndTo ) - { - hwndTo = hwndDefaultTo; - break; - } - } + if (!(hwndTo = GetWindow( hwndTo, GW_HWNDNEXT ))) break; + if (can_activate_window( hwndTo )) break; } - SetActiveWindow( hwndTo ); - hwndPrevActive = 0; + done: + fg = GetForegroundWindow(); + TRACE("win = %x fg = %x\n", hwndTo, fg); + if (!fg || (hwnd == fg)) + { + if (SetForegroundWindow( hwndTo )) return; + } + if (!SetActiveWindow( hwndTo )) SetActiveWindow(0); }