Revise detection of window miniaturization and deminiaturization based

on the ICCCM WM_STATE property and use _NET_WM_STATE only to avoid
generating bogus miniaturize events when using an emwh compliant
window manager. This makes the code more portable and allows
generating reliable window deminiaturize events.


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/back/trunk@29283 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Wolfgang Lux 2010-01-14 22:27:03 +00:00
parent 19150c94d0
commit b4aead1b19
5 changed files with 141 additions and 79 deletions

View file

@ -1,3 +1,13 @@
2010-01-14 Wolfgang Lux <wolfgang.lux@gmail.com>
* Source/x11/XGServerEvent.m (-processEvent:):
* Source/x11/XGServerWindow.m (-_wm_state:, -_ewmh_state, -_checkStyle,
-_rootWindowForScreen:, -window::::, -nativeWindow:::::): Revise
detection of window miniaturization and deminiaturization based on
ICCCM WM_STATE property.
* Source/x11/XGServerWindow.m (-miniwindow:): Add special case for
metacity window manager to avoid miniaturizing shaded windows.
2010-01-14 05:41-EST Gregory John Casamento <greg.casamento@gmail.com>
* Headers/win32/WIN32Server.h: Added decodeWM_MOUSEACTIVATE:..

View file

@ -103,6 +103,7 @@ struct XGGeneric {
Atom protocols_atom;
Atom delete_win_atom;
Atom take_focus_atom;
Atom wm_state_atom;
Atom net_wm_ping_atom;
Atom miniaturize_atom;
Atom win_decor_atom;

View file

@ -90,6 +90,7 @@ typedef struct _gswindow_device_t {
unsigned int border; /* Border size */
int map_state; /* X map state */
int visibility; /* X visibility */
int wm_state; /* X WM state */
NSBackingStoreType type; /* Backing type */
NSRect xframe; /* Window frame */
@ -130,7 +131,8 @@ typedef struct _gswindow_device_t {
/* This needs to go in GSDisplayServer */
- (void) _DPSsetcursor: (Cursor)c : (BOOL)set;
- (BOOL) _ewmh_isMinimized: (Window) win;
- (int) _wm_state: (Window) win;
- (BOOL) _ewmh_isHidden: (Window) win;
@end
extern Pixmap

View file

@ -68,8 +68,6 @@
#define cWin ((gswindow_device_t*)generic.cachedWindow)
extern Atom WM_STATE;
// NumLock's mask (it depends on the keyboard mapping)
static unsigned int _num_lock_mask;
// Modifier state
@ -595,6 +593,7 @@ posixFileDescriptor: (NSPosixFileDescriptor*)fileDescriptor
else if ((Atom)xEvent.xclient.data.l[0]
== generic.miniaturize_atom)
{
NSDebugLLog(@"Miniaturize", @"%d miniaturized", cWin->number);
eventLocation = NSMakePoint(0,0);
e = [NSEvent otherEventWithType: NSAppKitDefined
location: eventLocation
@ -1388,61 +1387,85 @@ posixFileDescriptor: (NSPosixFileDescriptor*)fileDescriptor
NSDebugLLog(@"NSEvent", @"%d PropertyNotify - '%s'\n",
xEvent.xproperty.window,
XGetAtomName(dpy, xEvent.xproperty.atom));
{
/* Note: Don't rely on _NET_STATE_WM_HIDDEN with Window Maker,
* since it is impossible to distinguish miniaturized and hidden
* windows by their window properties. Fortunately, Window Maker
* will send us client message when a window is miniaturized.
*/
if ((generic.wm & XGWM_WINDOWMAKER) == 0
&& xEvent.xproperty.atom == generic.netstates.net_wm_state_atom
&& xEvent.xproperty.state == PropertyNewValue)
{
if (cWin == 0 || xEvent.xproperty.window != cWin->ident)
{
generic.cachedWindow
= [XGServer _windowForXWindow: xEvent.xproperty.window];
}
if (cWin != 0)
{
/*
* FIXME: we really should detect when the state changes from
* unminimized to minimized, or vice versa
*/
if ([self _ewmh_isMinimized: xEvent.xproperty.window])
{
// Same event as when we get ClientMessage with the atom
// equal to generic.miniaturize_atom
eventLocation = NSMakePoint(0,0);
e = [NSEvent otherEventWithType: NSAppKitDefined
location: eventLocation
modifierFlags: 0
timestamp: xEvent.xproperty.time / 1000
windowNumber: cWin->number
context: gcontext
subtype: GSAppKitWindowMiniaturize
data1: 0
data2: 0];
}
else if ([GSWindowWithNumber(cWin->number) isMiniaturized])
{
/* A miniaturised window is now visible ... send event
* to let the gui know it deminiaturised.
*/
eventLocation = NSMakePoint(0,0);
e = [NSEvent otherEventWithType: NSAppKitDefined
location: eventLocation
modifierFlags: 0
timestamp: xEvent.xproperty.time / 1000
windowNumber: cWin->number
context: gcontext
subtype: GSAppKitWindowDeminiaturize
data1: 0
data2: 0];
}
}
}
}
if (xEvent.xproperty.atom == generic.wm_state_atom)
{
if (cWin == 0 || xEvent.xproperty.window != cWin->ident)
{
generic.cachedWindow
= [XGServer _windowForXWindow: xEvent.xproperty.window];
}
if (cWin != 0)
{
int new_state;
/* Get the new window state */
if (xEvent.xproperty.state == PropertyNewValue)
new_state = [self _wm_state: xEvent.xproperty.window];
else
new_state = WithdrawnState;
switch (new_state)
{
case IconicState:
/* Post miniaturize event upon transition from NormalState
to IconicState. If the window manager supports the ewmh
specification, also check that the _NET_WM_STATE
property includes _NET_WM_STATE_HIDDEN. */
/* Note: Don't rely on WM_STATE (nor on _NET_WM_STATE) with
Window Maker, since it is impossible to distinguish
miniaturized windows from hidden windows by their window
properties. Fortunately, Window Maker sends us a client
message when a window is miniaturized. */
if ((generic.wm & XGWM_WINDOWMAKER) == 0 &&
cWin->wm_state == NormalState &&
((generic.wm & XGWM_EWMH) == 0 ||
[self _ewmh_isHidden: xEvent.xproperty.window] == YES))
{
/* Same event as when we get ClientMessage with the
* atom equal to generic.miniaturize_atom
*/
NSDebugLLog(@"Miniaturize", @"%d miniaturized",
cWin->number);
eventLocation = NSMakePoint(0,0);
e = [NSEvent otherEventWithType: NSAppKitDefined
location: eventLocation
modifierFlags: 0
timestamp: xEvent.xproperty.time / 1000
windowNumber: cWin->number
context: gcontext
subtype: GSAppKitWindowMiniaturize
data1: 0
data2: 0];
}
break;
case NormalState:
/* Post deminiaturize event upon transition from IconicState
to NormalState, but only if our window is actually
miniaturized. */
if (cWin->wm_state == IconicState &&
[GSWindowWithNumber(cWin->number) isMiniaturized])
{
NSDebugLLog(@"Miniaturize", @"%d deminiaturized",
cWin->number);
eventLocation = NSMakePoint(0,0);
e = [NSEvent otherEventWithType: NSAppKitDefined
location: eventLocation
modifierFlags: 0
timestamp: xEvent.xproperty.time / 1000
windowNumber: cWin->number
context: gcontext
subtype: GSAppKitWindowDeminiaturize
data1: 0
data2: 0];
}
break;
}
/* save the new state */
cWin->wm_state = new_state;
}
}
break;
// a client successfully reparents a window

View file

@ -750,6 +750,7 @@ _get_next_prop_new_event(Display *display, XEvent *event, char *arg)
window->map_state = IsUnmapped;
window->visibility = 2;
window->wm_state = WithdrawnState;
// Create an X GC for the content view set it's colors
values.foreground = window->xwn_attrs.background_pixel;
@ -1266,6 +1267,7 @@ _get_next_prop_new_event(Display *display, XEvent *event, char *arg)
window->number = -screen;
window->map_state = IsViewable;
window->visibility = -1;
window->wm_state = NormalState;
if (window->ident)
XGetGeometry(dpy, window->ident, &window->root,
&x, &y, &width, &height,
@ -1416,6 +1418,7 @@ _get_next_prop_new_event(Display *display, XEvent *event, char *arg)
generic.protocols_atom = XInternAtom(dpy, "WM_PROTOCOLS", False);
generic.take_focus_atom = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
generic.delete_win_atom = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
generic.wm_state_atom = XInternAtom(dpy, "WM_STATE", False);
generic.net_wm_ping_atom = XInternAtom(dpy, "_NET_WM_PING", False);
generic.miniaturize_atom
= XInternAtom(dpy, "_GNUSTEP_WM_MINIATURIZE_WINDOW", False);
@ -1939,6 +1942,7 @@ _get_next_prop_new_event(Display *display, XEvent *event, char *arg)
window->map_state = IsUnmapped;
window->visibility = -1;
window->wm_state = WithdrawnState;
// Create an X GC for the content view set it's colors
values.foreground = window->xwn_attrs.background_pixel;
@ -1960,8 +1964,8 @@ _get_next_prop_new_event(Display *display, XEvent *event, char *arg)
| EnterWindowMask
| LeaveWindowMask
| FocusChangeMask
// enable property notifications under ewmh to detect window minimizing
| (generic.wm & XGWM_EWMH ? PropertyChangeMask : 0)
/* enable property notifications to detect window (de)miniaturization */
| PropertyChangeMask
// | ColormapChangeMask
| KeymapStateMask
| VisibilityChangeMask
@ -2100,6 +2104,7 @@ _get_next_prop_new_event(Display *display, XEvent *event, char *arg)
window->xwn_attrs.border_pixel = context->black;
window->xwn_attrs.background_pixel = context->white;
window->visibility = -1;
window->wm_state = [self _wm_state: windowRef];
// Create an X GC for the content view set it's colors
values.foreground = window->xwn_attrs.background_pixel;
@ -2577,17 +2582,20 @@ NSLog(@"styleoffsets ... guessing offsets\n");
XSync(dpy, False);
while (XCheckWindowEvent(dpy, window->ident, 0xffffffff, &e) == True) ;
/*
* When the application owns the mini window, we withdraw the window itself
* during miniaturization and put up the mini window instead. However, this
* does not work for WindowMaker, which unmaps the mini window, too, when
* the actual window is withdrawn. Fortunately, miniaturizing the actual
* window does already the right thing on WindowMaker.
*/
if (!generic.flags.appOwnsMiniwindow || (generic.wm & XGWM_WINDOWMAKER))
XIconifyWindow(dpy, window->ident, window->screen);
else
/* When the application owns the mini window, we withdraw the window itself
during miniaturization and put up the mini window instead. However, this
does not work for WindowMaker, which unmaps the mini window, too, when
the actual window is withdrawn. Fortunately, miniaturizing the actual
window does already the right thing on WindowMaker. */
/* Note: The wm_state != IconicState check is there to avoid iconifying a
window when -miniwindow: is called as a consequence of processing a
GSAppKitWindowMiniaturize event. This avoids iconifying shaded windows
under metacity, which sets _NET_WM_STATE for shaded windows to both
_NET_WM_STATE_SHADED and _NET_WM_STATE_HIDDEN. */
if (generic.flags.appOwnsMiniwindow && !(generic.wm & XGWM_WINDOWMAKER))
XWithdrawWindow(dpy, window->ident, window->screen);
else if (window->wm_state != IconicState)
XIconifyWindow(dpy, window->ident, window->screen);
}
/**
@ -4636,11 +4644,34 @@ _computeDepth(int class, int bpp)
}
/*
* Check whether the window is miniaturized according to the EWMH window state
* property. We map the EWMH _NET_WM_STATE_HIDDEN state to GNUstep's
* miniaturized state.
* Check whether the window is miniaturized according to the ICCCM window
* state property.
*/
- (BOOL) _ewmh_isMinimized: (Window)win
- (int) _wm_state: (Window)win
{
long *data;
long state;
data = (long *)PropGetCheckProperty(dpy, win, generic.wm_state_atom,
generic.wm_state_atom, 32, -1, NULL);
if (data)
{
state = *data;
XFree(data);
}
else
state = WithdrawnState;
return state;
}
/*
* Check whether the EWMH window state includes the _NET_WM_STATE_HIDDEN
* state. On EWMH, a window is iconified if it is iconic state and the
* _NET_WM_STATE_HIDDEN is present.
*/
- (BOOL) _ewmh_isHidden: (Window)win
{
Atom *data;
int count;
@ -4656,11 +4687,6 @@ _computeDepth(int class, int bpp)
for (i = 0; i < count; i++)
{
if (data[i] != 0)
{
NSDebugLLog(@"NSEvent", @"%d PropertyNotify detail - '%s'\n",
win, XGetAtomName(dpy, data[i]));
}
if (data[i] == generic.netstates.net_wm_state_hidden_atom)
{
XFree(data);