Improving Wine focus handling
Jukka Heinonen
jhei at iki.fi
Sun Oct 21 09:28:32 CDT 2001
In order to make Wine ICCCM compliant and to fix focus
livelock problems, it is necessary for Wine to use
WM_TAKE_FOCUS protocol. This makes it necessary to
stop using override-redirect for non-transient windows.
I have included below a patch that does both of these things.
The patch seems to work fine even with full-screen games
and it does make Wine focus handling to cooperate a lot better
with window managers. However, I feel reluctant to submit this
patch to wine-patches, at least before I do get some feedback
about the patch.
Here are some notes about the patch:
- Done against the latest Wine cvs tree,
file wine/dlls/ntdll/debugtools.c has some bugs
that might need to be fixed first, though.
- There have recently been lots of changes in directory
wine/windows/x11drv and I'm not sure whether the changes
are over yet.
- Alexandre did mention that this fix is in his todo list,
perhaps he has already planned a better way to do this fix.
- Menus do work but activating them makes parent window lose focus.
This does not look particularly nice.
- It is highly likely that every single case of Wine
using XSetInputFocus breaks ICCCM compliance. These need fixing.
- It might be a good idea to make X11DRV_SetFocus call EVENT_TakeFocus,
since there seems to be much shared code.
- Mouse clicks over non-active windows should not automatically
result into focus transfer. In these cases, Windows sends a special
message that makes certain that the window really wants to gain focus.
However, I don't think this is very important thing to do.
Index: wine/windows/x11drv/event.c
===================================================================
RCS file: /home/wine/wine/windows/x11drv/event.c,v
retrieving revision 1.112
diff -u -r1.112 event.c
--- wine/windows/x11drv/event.c 2001/10/18 21:38:59 1.112
+++ wine/windows/x11drv/event.c 2001/10/21 12:26:07
@@ -42,6 +42,7 @@
extern Atom wmProtocols;
extern Atom wmDeleteWindow;
+extern Atom wmTakeFocus;
extern Atom dndProtocol;
extern Atom dndSelection;
@@ -116,7 +117,59 @@
static INPUT_TYPE current_input_type = X11DRV_INPUT_ABSOLUTE;
static BOOL in_transition = FALSE; /* This is not used as for today */
+/***********************************************************************
+ * EVENT_TakeFocus
+ */
+static void EVENT_TakeFocus( HWND hWnd, XAnyEvent *event, Time time )
+{
+ XWindowAttributes win_attr;
+ BOOL bIsDisabled;
+ Window focusWindow;
+ HWND focusHandle;
+
+ TRACE("called, X11 window is %08lx, window handle is %04x.\n",
+ event->window, hWnd);
+
+ if (!hWnd) return;
+
+ bIsDisabled = GetWindowLongA( hWnd, GWL_STYLE ) & WS_DISABLED;
+
+ /* If the window has been disabled,
+ * 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 (bIsDisabled)
+ focusWindow = glastXFocusWin;
+ else
+ focusWindow = event->window;
+
+ wine_tsx11_lock();
+
+ /* Change focus only if focus window is registered and viewable. */
+ if (XFindContext( event->display, focusWindow, winContext, (char **)&focusHandle ) ||
+ !XGetWindowAttributes( event->display, focusWindow, &win_attr ) ||
+ (win_attr.map_state != IsViewable))
+ {
+ wine_tsx11_unlock();
+ return;
+ }
+
+ TRACE("Set X11 focus to: %08lx\n", focusWindow);
+ XSetInputFocus( event->display, focusWindow, RevertToParent, time );
+
+ wine_tsx11_unlock();
+
+ glastXFocusWin = focusWindow;
+ if (focusHandle != GetForegroundWindow())
+ {
+ TRACE("Set foreground window to: %04x\n", focusHandle);
+ SetForegroundWindow( focusHandle );
+ }
+}
+
+
/***********************************************************************
* process_events
*/
@@ -274,6 +327,7 @@
break;
case ButtonPress:
+ EVENT_TakeFocus(hWnd, (XAnyEvent*)event, ((XButtonEvent*)event)->time);
X11DRV_ButtonPress( hWnd, (XButtonEvent*)event );
break;
@@ -354,39 +408,6 @@
*/
static void EVENT_FocusIn( HWND hWnd, XFocusChangeEvent *event )
{
- 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)
- {
- /* 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();
- }
-
- if (event->detail != NotifyPointer && hWnd != GetForegroundWindow())
- SetForegroundWindow( hWnd );
}
@@ -397,10 +418,7 @@
*/
static void EVENT_FocusOut( HWND hWnd, XFocusChangeEvent *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())
{
@@ -1252,6 +1270,10 @@
EVENT_DropFromOffiX(hWnd, event);
else if (event->data.l[0] == DndURL)
EVENT_DropURLs(hWnd, event);
+ }
+ else if (event->message_type == wmProtocols && ((Atom)event->data.l[0]) == wmTakeFocus)
+ {
+ EVENT_TakeFocus(hWnd, (XAnyEvent*)event, (Time)event->data.l[1]);
}
else {
#if 0
Index: wine/dlls/x11drv/window.c
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/window.c,v
retrieving revision 1.27
diff -u -r1.27 window.c
--- wine/dlls/x11drv/window.c 2001/10/18 21:38:59 1.27
+++ wine/dlls/x11drv/window.c 2001/10/21 12:27:57
@@ -44,6 +44,7 @@
Atom wmChangeState = None;
Atom kwmDockWindow = None;
Atom _kde_net_wm_system_tray_window_for = None; /* KDE 2 Final */
+Atom wmMotifHints = None;
static LPCSTR whole_window_atom;
static LPCSTR client_window_atom;
@@ -109,7 +110,7 @@
if (managed) win->dwExStyle |= WS_EX_MANAGED;
else win->dwExStyle &= ~WS_EX_MANAGED;
- attr->override_redirect = !managed;
+ attr->override_redirect = 0; // FIXME: true for transient windows (menus)
attr->colormap = X11DRV_PALETTE_PaletteXColormap;
attr->save_under = ((win->clsStyle & CS_SAVEBITS) != 0);
attr->cursor = None;
@@ -332,6 +333,16 @@
wine_tsx11_lock();
+ /* motif hints */
+ if(!(win->dwExStyle & WS_EX_MANAGED))
+ {
+ INT32 hints[5] = {2,0,0,0,0};
+ XChangeProperty( display, data->whole_window,
+ wmMotifHints, wmMotifHints,
+ 32, PropModeReplace,
+ (char*)hints, 5 );
+ }
+
/* wm protocols */
i = 0;
protocols[i++] = wmDeleteWindow;
@@ -594,8 +605,8 @@
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 */
+ wmTakeFocus = XInternAtom( display, "WM_TAKE_FOCUS", False );
+ wmMotifHints = XInternAtom( display, "_MOTIF_WM_HINTS", False );
dndProtocol = XInternAtom( display, "DndProtocol" , False );
dndSelection = XInternAtom( display, "DndSelection" , False );
wmChangeState = XInternAtom (display, "WM_CHANGE_STATE", False);
--
Jukka Heinonen <http://www.iki.fi/jhei/>
More information about the wine-devel
mailing list