Fix for focus loops
Alexandre Julliard
julliard at winehq.com
Tue May 7 12:44:08 CDT 2002
Jukka Heinonen <jhei at iki.fi> writes:
> The patch should not change how Wine handles
> focus (even though Wine focus handling is quite
> far from ICCCM compliance). The patch adds one
> SetFocus call, the missing of which is actually bug
> because it means that X11 and Wine may have different
> ideas about which window has focus.
>
> Changelog:
> Changed the input model of managed windows from
> passive input into globally active input.
> Moved code to detect focus changes from FocusIn
> handler to WM_TAKE_FOCUS handler.
I've been working on this too, and I found that the locally active
model seems to work better with most window managers, even though it's
not 100% correct per the spec. Here's the patch I have, please give it
a try and let me know how it works for you.
Index: dlls/x11drv/event.c
===================================================================
RCS file: /opt/cvs-commit/wine/dlls/x11drv/event.c,v
retrieving revision 1.1
diff -u -r1.1 event.c
--- dlls/x11drv/event.c 30 Apr 2002 21:16:39 -0000 1.1
+++ dlls/x11drv/event.c 7 May 2002 17:38:25 -0000
@@ -73,9 +73,6 @@
#define DndURL 128 /* KDE drag&drop */
-/* The last X window which had the focus */
-static Window glastXFocusWin = 0;
-
static const char * const event_names[] =
{
"", "", "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
@@ -90,7 +87,6 @@
static void EVENT_ProcessEvent( XEvent *event );
-static BOOL X11DRV_CheckFocus(void);
/* Event handlers */
static void EVENT_FocusIn( HWND hWnd, XFocusChangeEvent *event );
@@ -364,91 +360,147 @@
/**********************************************************************
- * EVENT_FocusIn
+ * set_focus
*/
-static void EVENT_FocusIn( HWND hWnd, XFocusChangeEvent *event )
+static void set_focus( HWND hwnd, Time time )
{
- WND *pWndLastFocus;
- XWindowAttributes win_attr;
- BOOL bIsDisabled;
-
- if (!hWnd) return;
-
- bIsDisabled = GetWindowLongA( hWnd, GWL_STYLE ) & WS_DISABLED;
-
- /* If the window has been disabled and we are in managed mode,
- * revert the X focus back to the last focus window. This is to disallow
- * the window manager from switching focus away while the app is
- * in a modal state.
- */
- if ( Options.managed && bIsDisabled && glastXFocusWin)
+ HWND focus = GetFocus();
+ Window win = X11DRV_get_whole_window( hwnd );
+
+ if (win)
{
- /* Change focus only if saved focus window is registered and viewable */
- wine_tsx11_lock();
- if (XFindContext( event->display, glastXFocusWin, winContext,
- (char **)&pWndLastFocus ) == 0 )
- {
- if (XGetWindowAttributes( event->display, glastXFocusWin, &win_attr ) &&
- (win_attr.map_state == IsViewable) )
- {
- XSetInputFocus( event->display, glastXFocusWin, RevertToParent, CurrentTime );
- wine_tsx11_unlock();
- return;
- }
- }
- wine_tsx11_unlock();
+ TRACE( "setting focus to %x (%lx) time=%ld\n", hwnd, win, time );
+ TSXSetInputFocus( thread_display(), win, RevertToParent, time );
}
- if (event->detail != NotifyPointer && hWnd != GetForegroundWindow())
- SetForegroundWindow( hWnd );
+ if (hwnd != focus && !IsChild( hwnd, focus ))
+ {
+ TRACE( "changing window focus to %x\n", hwnd );
+ SetFocus( hwnd );
+ }
+ else TRACE( "focus already OK (%x/%x)\n", hwnd, focus );
}
/**********************************************************************
- * EVENT_FocusOut
- *
- * Note: only top-level override-redirect windows get FocusOut events.
+ * handle_wm_protocols_message
*/
-static void EVENT_FocusOut( HWND hWnd, XFocusChangeEvent *event )
+static void handle_wm_protocols_message( HWND hwnd, XClientMessageEvent *event )
{
- /* Save the last window which had the focus */
- glastXFocusWin = event->window;
- if (!hWnd) return;
- if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_DISABLED) glastXFocusWin = 0;
-
- if (event->detail != NotifyPointer && hWnd == GetForegroundWindow())
- {
- /* don't reset the foreground window, if the window which is
- getting the focus is a Wine window */
- if (!X11DRV_CheckFocus())
- {
- SendMessageA( hWnd, WM_CANCELMODE, 0, 0 );
- /* Abey : 6-Oct-99. Check again if the focus out window is the
- Foreground window, because in most cases the messages sent
- above must have already changed the foreground window, in which
- case we don't have to change the foreground window to 0 */
+ Atom protocol = (Atom)event->data.l[0];
+
+ if (!protocol) return;
+
+ if (protocol == wmDeleteWindow)
+ {
+ /* Ignore the delete window request if the window has been disabled
+ * and we are in managed mode. This is to disallow applications from
+ * being closed by the window manager while in a modal state.
+ */
+ if (IsWindowEnabled(hwnd)) PostMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
+ }
+ else if (protocol == wmTakeFocus)
+ {
+ Time event_time = (Time)event->data.l[1];
+ HWND last_focus = x11drv_thread_data()->last_focus;
- if (hWnd == GetForegroundWindow())
- SetForegroundWindow( 0 );
+ TRACE( "got take focus msg for %x, enabled=%d, focus=%x, active=%x, fg=%x, last=%x\n",
+ hwnd, IsWindowEnabled(hwnd), GetFocus(), GetActiveWindow(),
+ GetForegroundWindow(), last_focus );
+
+ if (IsWindowEnabled(hwnd))
+ {
+ /* simulate a mouse click on the caption to find out
+ * whether the window wants to be activated */
+ LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
+ GetAncestor( hwnd, GA_ROOT ),
+ MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
+ if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE) set_focus( hwnd, event_time );
+ else TRACE( "not setting focus to %x (%lx), ma=%ld\n", hwnd, event->window, ma );
+ }
+ else
+ {
+ hwnd = GetFocus();
+ if (!hwnd) hwnd = GetActiveWindow();
+ if (!hwnd) hwnd = last_focus;
+ if (hwnd && IsWindowEnabled(hwnd)) set_focus( hwnd, event_time );
}
}
}
+
+static const char * const focus_details[] =
+{
+ "NotifyAncestor",
+ "NotifyVirtual",
+ "NotifyInferior",
+ "NotifyNonlinear",
+ "NotifyNonlinearVirtual",
+ "NotifyPointer",
+ "NotifyPointerRoot",
+ "NotifyDetailNone"
+};
+
+/**********************************************************************
+ * EVENT_FocusIn
+ */
+static void EVENT_FocusIn( HWND hwnd, XFocusChangeEvent *event )
+{
+ if (!hwnd) return;
+
+ TRACE( "win %x xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
+
+ if (wmTakeFocus) return; /* ignore FocusIn if we are using take focus */
+ if (event->detail == NotifyPointer) return;
+
+ if (!IsWindowEnabled(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 );
+ }
+}
+
+
/**********************************************************************
- * CheckFocus (X11DRV.@)
+ * EVENT_FocusOut
+ *
+ * Note: only top-level windows get FocusOut events.
*/
-static BOOL X11DRV_CheckFocus(void)
+static void EVENT_FocusOut( HWND hwnd, XFocusChangeEvent *event )
{
- Display *display = thread_display();
- HWND hWnd;
- Window xW;
- int state;
-
- TSXGetInputFocus(display, &xW, &state);
- if( xW == None ||
- TSXFindContext(display, xW, winContext, (char **)&hWnd) )
- return FALSE;
- return TRUE;
+ HWND hwnd_tmp;
+ Window focus_win;
+ int revert;
+
+ TRACE( "win %x xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
+
+ if (event->detail == NotifyPointer) return;
+ if (hwnd != GetForegroundWindow()) return;
+ SendMessageA( hwnd, WM_CANCELMODE, 0, 0 );
+
+ /* don't reset the foreground window, if the window which is
+ getting the focus is a Wine window */
+
+ TSXGetInputFocus( thread_display(), &focus_win, &revert );
+ if (!focus_win || TSXFindContext( thread_display(), focus_win, winContext, (char **)&hwnd_tmp ))
+ {
+ /* Abey : 6-Oct-99. Check again if the focus out window is the
+ Foreground window, because in most cases the messages sent
+ above must have already changed the foreground window, in which
+ case we don't have to change the foreground window to 0 */
+ if (hwnd == GetForegroundWindow())
+ {
+ TRACE( "lost focus, setting fg to 0\n" );
+ x11drv_thread_data()->last_focus = hwnd;
+ SetForegroundWindow( 0 );
+ }
+ }
}
@@ -1240,19 +1292,16 @@
}
}
+
/**********************************************************************
* EVENT_ClientMessage
*/
static void EVENT_ClientMessage( HWND hWnd, XClientMessageEvent *event )
{
- if (event->message_type != None && event->format == 32) {
- if ((event->message_type == wmProtocols) &&
- (((Atom) event->data.l[0]) == wmDeleteWindow))
- {
- /* Ignore the delete window request if the window has been disabled */
- if (!(GetWindowLongA( hWnd, GWL_STYLE ) & WS_DISABLED))
- PostMessageA( hWnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
- }
+ if (event->message_type != None && event->format == 32)
+ {
+ if (event->message_type == wmProtocols)
+ handle_wm_protocols_message( hWnd, event );
else if (event->message_type == dndProtocol)
{
/* query window (drag&drop event contains only drag window) */
Index: dlls/x11drv/window.c
===================================================================
RCS file: /opt/cvs-commit/wine/dlls/x11drv/window.c,v
retrieving revision 1.32
diff -u -r1.32 window.c
--- dlls/x11drv/window.c 7 May 2002 01:52:15 -0000 1.32
+++ dlls/x11drv/window.c 7 May 2002 17:38:28 -0000
@@ -420,9 +420,7 @@
if ((wm_hints = TSXAllocWMHints()))
{
wm_hints->flags = InputHint | StateHint | WindowGroupHint;
- /* use globally active model if take focus is supported,
- * passive model otherwise (cf. ICCCM) */
- wm_hints->input = !wmTakeFocus;
+ wm_hints->input = !(win->dwStyle & WS_DISABLED);
set_icon_hints( display, win, wm_hints );
@@ -637,8 +635,7 @@
winContext = XUniqueContext();
wmProtocols = XInternAtom( display, "WM_PROTOCOLS", False );
wmDeleteWindow = XInternAtom( display, "WM_DELETE_WINDOW", False );
-/* wmTakeFocus = XInternAtom( display, "WM_TAKE_FOCUS", False );*/
- wmTakeFocus = 0; /* not yet */
+ if (use_take_focus) wmTakeFocus = XInternAtom( display, "WM_TAKE_FOCUS", False );
dndProtocol = XInternAtom( display, "DndProtocol" , False );
dndSelection = XInternAtom( display, "DndSelection" , False );
wmChangeState = XInternAtom( display, "WM_CHANGE_STATE", False );
@@ -831,7 +828,8 @@
*/
BOOL X11DRV_DestroyWindow( HWND hwnd )
{
- Display *display = thread_display();
+ struct x11drv_thread_data *thread_data = x11drv_thread_data();
+ Display *display = thread_data->display;
WND *wndPtr = WIN_GetPtr( hwnd );
X11DRV_WND_DATA *data = wndPtr->pDriverData;
@@ -840,6 +838,7 @@
if (data->whole_window)
{
TRACE( "win %x xwin %lx/%lx\n", hwnd, data->whole_window, data->client_window );
+ if (thread_data->last_focus == hwnd) thread_data->last_focus = 0;
wine_tsx11_lock();
XSync( gdi_display, False ); /* flush any reference to this drawable in GDI queue */
XDeleteContext( display, data->whole_window, winContext );
Index: dlls/x11drv/x11drv_main.c
===================================================================
RCS file: /opt/cvs-commit/wine/dlls/x11drv/x11drv_main.c,v
retrieving revision 1.49
diff -u -r1.49 x11drv_main.c
--- dlls/x11drv/x11drv_main.c 24 Apr 2002 21:32:11 -0000 1.49
+++ dlls/x11drv/x11drv_main.c 7 May 2002 17:38:29 -0000
@@ -61,6 +61,7 @@
unsigned int screen_depth;
Window root_window;
int dxgrab, usedga, usexvidmode;
+BOOL use_take_focus = TRUE;
unsigned int X11DRV_server_startticks;
@@ -249,6 +250,9 @@
if (!get_config_key( hkey, appkey, "UseXVidMode", buffer, sizeof(buffer) ))
usexvidmode = IS_OPTION_TRUE( buffer[0] );
+ if (!get_config_key( hkey, appkey, "UseTakeFocus", buffer, sizeof(buffer) ))
+ use_take_focus = IS_OPTION_TRUE( buffer[0] );
+
screen_depth = 0;
if (!get_config_key( hkey, appkey, "ScreenDepth", buffer, sizeof(buffer) ))
screen_depth = atoi(buffer);
@@ -460,6 +464,7 @@
data->display_fd = FILE_DupUnixHandle( ConnectionNumber(data->display),
GENERIC_READ | SYNCHRONIZE, FALSE );
data->process_event_count = 0;
+ data->last_focus = 0;
NtCurrentTeb()->driver_data = data;
return data;
}
Index: include/x11drv.h
===================================================================
RCS file: /opt/cvs-commit/wine/include/x11drv.h,v
retrieving revision 1.104
diff -u -r1.104 x11drv.h
--- include/x11drv.h 2 May 2002 01:39:48 -0000 1.104
+++ include/x11drv.h 7 May 2002 17:38:31 -0000
@@ -332,6 +332,7 @@
Display *display;
HANDLE display_fd;
int process_event_count; /* recursion count for event processing */
+ HWND last_focus; /* last window that had focus */
};
extern struct x11drv_thread_data *x11drv_init_thread_data(void);
@@ -351,6 +352,7 @@
extern unsigned int screen_height;
extern unsigned int screen_depth;
extern unsigned int text_caps;
+extern BOOL use_take_focus;
extern Atom wmProtocols;
extern Atom wmDeleteWindow;
--
Alexandre Julliard
julliard at winehq.com
More information about the wine-patches
mailing list