blob: d1da64f4051ecdcdca2f66b5c31d29039ea4a8c0 [file] [log] [blame]
/*
* Copyright (c) 2001, 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifdef HEADLESS
#error This file should not be included in headless library
#endif
/*
* Some SCIENCE stuff happens, and it is CONFUSING
*/
#include "awt_p.h"
#include <X11/Xproto.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <Xm/MwmUtil.h>
/* JNI headers */
#include "java_awt_Frame.h" /* for frame state constants */
#include "awt_wm.h"
#include "awt_util.h" /* for X11 error handling macros */
/*
* NB: 64 bit awareness.
*
* Since this code reads/writes window properties heavily, one thing
* should be noted well. Xlib uses C type 'long' for properties of
* format 32. Fortunately, typedef for Atom is 'long' as well, so
* passing property data as or casting returned property data to
* arrays of atoms is safe.
*/
/*
* Atoms used to communicate with window manager(s).
* Naming convention:
* o for atom "FOO" the variable is "XA_FOO"
* o for atom "_BAR" the variable is "_XA_BAR"
* Don't forget to add initialization to awt_wm_initAtoms below.
*/
/*
* Before WM rototill JDK used to check for running WM by just testing
* if certain atom is interned or not. We'd better not confuse older
* JDK by interning these atoms. Use awt_wm_atomInterned() to intern
* them lazily.
*
* ENLIGHTENMENT_COMMS
* _ICEWM_WINOPTHINT
* _SAWMILL_TIMESTAMP
* _DT_SM_WINDOW_INFO
* _MOTIF_WM_INFO
* _SUN_WM_PROTOCOLS
*/
/* Good old ICCCM */
static Atom XA_WM_STATE;
/* New "netwm" spec from www.freedesktop.org */
static Atom XA_UTF8_STRING; /* like STRING but encoding is UTF-8 */
static Atom _XA_NET_SUPPORTING_WM_CHECK;
static Atom _XA_NET_SUPPORTED; /* list of protocols (property of root) */
static Atom _XA_NET_WM_NAME; /* window property */
static Atom _XA_NET_WM_STATE; /* both window property and request */
/*
* _NET_WM_STATE is a list of atoms.
* NB: Standard spelling is "HORZ" (yes, without an 'I'), but KDE2
* uses misspelled "HORIZ" (see KDE bug #20229). This was fixed in
* KDE 2.2. Under earlier versions of KDE2 horizontal and full
* maximization doesn't work .
*/
static Atom _XA_NET_WM_STATE_MAXIMIZED_HORZ;
static Atom _XA_NET_WM_STATE_MAXIMIZED_VERT;
static Atom _XA_NET_WM_STATE_SHADED;
static Atom _XA_NET_WM_STATE_ABOVE;
static Atom _XA_NET_WM_STATE_BELOW;
static Atom _XA_NET_WM_STATE_HIDDEN;
/* Currently we only care about max_v and max_h in _NET_WM_STATE */
#define AWT_NET_N_KNOWN_STATES 2
/* Gnome WM spec (superseded by "netwm" above, but still in use) */
static Atom _XA_WIN_SUPPORTING_WM_CHECK;
static Atom _XA_WIN_PROTOCOLS;
static Atom _XA_WIN_STATE;
static Atom _XA_WIN_LAYER;
/* Enlightenment */
static Atom _XA_E_FRAME_SIZE;
/* KWin (KDE2) */
static Atom _XA_KDE_NET_WM_FRAME_STRUT;
/* KWM (KDE 1.x) OBSOLETE??? */
static Atom XA_KWM_WIN_ICONIFIED;
static Atom XA_KWM_WIN_MAXIMIZED;
/* OpenLook */
static Atom _XA_OL_DECOR_DEL;
static Atom _XA_OL_DECOR_HEADER;
static Atom _XA_OL_DECOR_RESIZE;
static Atom _XA_OL_DECOR_PIN;
static Atom _XA_OL_DECOR_CLOSE;
/* For _NET_WM_STATE ClientMessage requests */
#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */
#define _NET_WM_STATE_ADD 1 /* add/set property */
#define _NET_WM_STATE_TOGGLE 2 /* toggle property */
/* _WIN_STATE bits */
#define WIN_STATE_STICKY (1<<0) /* everyone knows sticky */
#define WIN_STATE_MINIMIZED (1<<1) /* Reserved - definition is unclear */
#define WIN_STATE_MAXIMIZED_VERT (1<<2) /* window in maximized V state */
#define WIN_STATE_MAXIMIZED_HORIZ (1<<3) /* window in maximized H state */
#define WIN_STATE_HIDDEN (1<<4) /* not on taskbar but window visible*/
#define WIN_STATE_SHADED (1<<5) /* shaded (MacOS / Afterstep style) */
#define WIN_LAYER_ONTOP 6
#define WIN_LAYER_NORMAL 4
#define URGENCY_HINT (1<<8)
#define LAYER_ALWAYS_ON_TOP 1
#define LAYER_NORMAL 0
/*
* Intern a bunch of atoms we are going use.
*/
static void
awt_wm_initAtoms(void)
{
/* Minimize X traffic by creating atoms en mass... This requires
slightly more code but reduces number of server requests. */
struct atominit {
Atom *atomptr;
const char *name;
};
/* Just add new atoms to this list */
static struct atominit atom_list[] = {
{ &XA_WM_STATE, "WM_STATE" },
{ &XA_UTF8_STRING, "UTF8_STRING" },
{ &_XA_NET_SUPPORTING_WM_CHECK, "_NET_SUPPORTING_WM_CHECK" },
{ &_XA_NET_SUPPORTED, "_NET_SUPPORTED" },
{ &_XA_NET_WM_STATE, "_NET_WM_STATE" },
{ &_XA_NET_WM_STATE_MAXIMIZED_VERT, "_NET_WM_STATE_MAXIMIZED_VERT" },
{ &_XA_NET_WM_STATE_MAXIMIZED_HORZ, "_NET_WM_STATE_MAXIMIZED_HORZ" },
{ &_XA_NET_WM_STATE_SHADED, "_NET_WM_STATE_SHADED" },
{ &_XA_NET_WM_STATE_ABOVE, "_NET_WM_STATE_ABOVE" },
{ &_XA_NET_WM_STATE_BELOW, "_NET_WM_STATE_BELOW" },
{ &_XA_NET_WM_STATE_HIDDEN, "_NET_WM_STATE_HIDDEN" },
{ &_XA_NET_WM_NAME, "_NET_WM_NAME" },
{ &_XA_WIN_SUPPORTING_WM_CHECK, "_WIN_SUPPORTING_WM_CHECK" },
{ &_XA_WIN_PROTOCOLS, "_WIN_PROTOCOLS" },
{ &_XA_WIN_STATE, "_WIN_STATE" },
{ &_XA_WIN_LAYER, "_WIN_LAYER" },
{ &_XA_KDE_NET_WM_FRAME_STRUT, "_KDE_NET_WM_FRAME_STRUT" },
{ &_XA_E_FRAME_SIZE, "_E_FRAME_SIZE" },
{ &XA_KWM_WIN_ICONIFIED, "KWM_WIN_ICONIFIED" },
{ &XA_KWM_WIN_MAXIMIZED, "KWM_WIN_MAXIMIZED" },
{ &_XA_OL_DECOR_DEL, "_OL_DECOR_DEL" },
{ &_XA_OL_DECOR_HEADER, "_OL_DECOR_HEADER" },
{ &_XA_OL_DECOR_RESIZE, "_OL_DECOR_RESIZE" },
{ &_XA_OL_DECOR_PIN, "_OL_DECOR_PIN" },
{ &_XA_OL_DECOR_CLOSE, "_OL_DECOR_CLOSE" }
};
#define ATOM_LIST_LENGTH (sizeof(atom_list)/sizeof(atom_list[0]))
const char *names[ATOM_LIST_LENGTH];
Atom atoms[ATOM_LIST_LENGTH];
Status status;
size_t i;
/* Fill the array of atom names */
for (i = 0; i < ATOM_LIST_LENGTH; ++i) {
names[i] = atom_list[i].name;
}
DTRACE_PRINT("WM: initializing atoms ... ");
status = XInternAtoms(awt_display, (char**)names, ATOM_LIST_LENGTH,
False, atoms);
if (status == 0) {
DTRACE_PRINTLN("failed");
return;
}
/* Store returned atoms into corresponding global variables */
DTRACE_PRINTLN("ok");
for (i = 0; i < ATOM_LIST_LENGTH; ++i) {
*atom_list[i].atomptr = atoms[i];
}
#undef ATOM_LIST_LENGTH
}
/*
* When checking for various WMs don't intern certain atoms we use to
* distinguish those WMs. Rather check if the atom is interned first.
* If it's not, further tests are not necessary anyway.
* This also saves older JDK a great deal of confusion (4487993).
*/
static Boolean
awt_wm_atomInterned(Atom *pa, const char *name)
{
DASSERT(pa != NULL);
if (*pa == None) {
DASSERT(name != NULL);
*pa = XInternAtom(awt_display, name, True);
if (*pa == None) {
DTRACE_PRINTLN1("\"%s\" is not interned", name);
return False;
} else {
return True;
}
} else {
return True;
}
}
/*****************************************************************************\
*
* DTRACE utils for various states ...
*
\*****************************************************************************/
static void
awt_wm_dtraceWMState(uint32_t wm_state)
{
#ifdef DEBUG
DTRACE_PRINT("WM_STATE = ");
switch (wm_state) {
case WithdrawnState:
DTRACE_PRINTLN("Withdrawn");
break;
case NormalState:
DTRACE_PRINTLN("Normal");
break;
case IconicState:
DTRACE_PRINTLN("Iconic");
break;
default:
DTRACE_PRINTLN1("unknown state %d", wm_state);
break;
}
#endif /* DEBUG */
}
static void
awt_wm_dtraceStateNet(Atom *net_wm_state, unsigned long nitems)
{
#ifdef DEBUG
unsigned long i;
DTRACE_PRINT("_NET_WM_STATE = {");
for (i = 0; i < nitems; ++i) {
char *name, *print_name;
name = XGetAtomName(awt_display, net_wm_state[i]);
if (name == NULL) {
print_name = "???";
} else if (strncmp(name, "_NET_WM_STATE", 13) == 0) {
print_name = name + 13; /* skip common prefix to reduce noice */
} else {
print_name = name;
}
DTRACE_PRINT1(" %s", print_name);
if (name) {
XFree(name);
}
}
DTRACE_PRINTLN(" }");
#endif
}
static void
awt_wm_dtraceStateWin(uint32_t win_state)
{
#ifdef DEBUG
DTRACE_PRINT("_WIN_STATE = {");
if (win_state & WIN_STATE_STICKY) {
DTRACE_PRINT(" STICKY");
}
if (win_state & WIN_STATE_MINIMIZED) {
DTRACE_PRINT(" MINIMIZED");
}
if (win_state & WIN_STATE_MAXIMIZED_VERT) {
DTRACE_PRINT(" MAXIMIZED_VERT");
}
if (win_state & WIN_STATE_MAXIMIZED_HORIZ) {
DTRACE_PRINT(" MAXIMIZED_HORIZ");
}
if (win_state & WIN_STATE_HIDDEN) {
DTRACE_PRINT(" HIDDEN");
}
if (win_state & WIN_STATE_SHADED) {
DTRACE_PRINT(" SHADED");
}
DTRACE_PRINTLN(" }");
#endif
}
static void
awt_wm_dtraceStateJava(jint java_state)
{
#ifdef DEBUG
DTRACE_PRINT("java state = ");
if (java_state == java_awt_Frame_NORMAL) {
DTRACE_PRINTLN("NORMAL");
}
else {
DTRACE_PRINT("{");
if (java_state & java_awt_Frame_ICONIFIED) {
DTRACE_PRINT(" ICONIFIED");
}
if ((java_state & java_awt_Frame_MAXIMIZED_BOTH)
== java_awt_Frame_MAXIMIZED_BOTH)
{
DTRACE_PRINT(" MAXIMIZED_BOTH");
}
else if (java_state & java_awt_Frame_MAXIMIZED_HORIZ) {
DTRACE_PRINT(" MAXIMIZED_HORIZ");
}
else if (java_state & java_awt_Frame_MAXIMIZED_VERT) {
DTRACE_PRINT(" MAXIMIZED_VERT");
}
DTRACE_PRINTLN(" }");
}
#endif /* DEBUG */
}
/*****************************************************************************\
*
* Utility functions ...
*
\*****************************************************************************/
/*
* Instead of validating window id, we simply call XGetWindowProperty,
* but temporary install this function as the error handler to ignore
* BadWindow error.
*/
int /* but ingored */
xerror_ignore_bad_window(Display *dpy, XErrorEvent *err)
{
XERROR_SAVE(err);
if (err->error_code == BadWindow) {
DTRACE_PRINTLN("IGNORING BadWindow");
return 0; /* ok to fail */
}
else {
return (*xerror_saved_handler)(dpy, err);
}
}
/*
* Convenience wrapper for XGetWindowProperty for XA_ATOM properties.
* E.g. WM_PROTOCOLS, _NET_WM_STATE, _OL_DECOR_DEL.
* It's up to caller to XFree returned value.
* Number of items returned is stored to nitems_ptr (if non-null).
*/
static Atom *
awt_getAtomListProperty(Window w, Atom property, unsigned long *nitems_ptr)
{
/* Request status */
int status;
/* Returns of XGetWindowProperty */
Atom actual_type;
int actual_format;
unsigned long nitems_stub;
unsigned long bytes_after;
Atom *list;
if (nitems_ptr == NULL) {
/* Caller is not interested in the number of items,
provide a stub for XGetWindowProperty */
nitems_ptr = &nitems_stub;
}
status = XGetWindowProperty(awt_display, w,
property, 0, 0xFFFF, False, XA_ATOM,
&actual_type, &actual_format, nitems_ptr, &bytes_after,
(unsigned char **)&list);
if (status != Success || list == NULL) {
*nitems_ptr = 0;
return NULL;
}
if (actual_type != XA_ATOM || actual_format != 32) {
XFree(list);
*nitems_ptr = 0;
return NULL;
}
if (*nitems_ptr == 0) {
XFree(list);
return NULL;
}
return list;
}
/*
* Auxiliary function that returns the value of 'property' of type
* 'property_type' on window 'w'. Format of the property must be 8.
* Terminating zero added by XGetWindowProperty is preserved.
* It's up to caller to XFree the result.
*/
static unsigned char *
awt_getProperty8(Window w, Atom property, Atom property_type)
{
/* Request status */
int status;
/* Returns of XGetWindowProperty */
Atom actual_type;
int actual_format;
unsigned long nitems;
unsigned long bytes_after;
unsigned char *string;
/* BadWindow is ok and will be blocked by our special handler */
WITH_XERROR_HANDLER(xerror_ignore_bad_window);
{
status = XGetWindowProperty(awt_display, w,
property, 0, 0xFFFF, False, property_type,
&actual_type, &actual_format, &nitems, &bytes_after,
&string);
}
RESTORE_XERROR_HANDLER;
if (status != Success || string == NULL) {
return NULL;
}
if (actual_type != property_type || actual_format != 8) {
XFree(string);
return NULL;
}
/* XGetWindowProperty kindly supplies terminating zero */
return string;
}
/*
* Auxiliary function that returns the value of 'property' of type
* 'property_type' on window 'w'. Format of the property must be 32.
*/
static int32_t
awt_getProperty32(Window w, Atom property, Atom property_type)
{
/* Property value*/
int32_t value;
/* Request status */
int status;
/* Returns of XGetWindowProperty */
Atom actual_type;
int actual_format;
unsigned long nitems;
unsigned long bytes_after;
long *data; /* NB: 64 bit: Format 32 props are 'long' */
/* BadWindow is ok and will be blocked by our special handler */
WITH_XERROR_HANDLER(xerror_ignore_bad_window);
{
status = XGetWindowProperty(awt_display, w,
property, 0, 1, False, property_type,
&actual_type, &actual_format, &nitems, &bytes_after,
(unsigned char **)&data);
}
RESTORE_XERROR_HANDLER;
if (status != Success || data == NULL) {
return 0;
}
if (actual_type != property_type || actual_format != 32) {
XFree(data); /* NULL data already catched above */
return 0;
}
value = (int32_t)*data;
XFree(data);
return value;
}
/*****************************************************************************\
*
* Detecting WM ...
*
\*****************************************************************************/
/*
* Check for anchor_prop(anchor_type) on root, take the value as the
* window id and check if that window exists and has anchor_prop(anchor_type)
* with the same value (i.e. pointing back to self).
*
* Returns the anchor window, as some WM may put interesting stuff in
* its properties (e.g. sawfish).
*/
static Window
awt_wm_checkAnchor(Atom anchor_prop, Atom anchor_type)
{
Window root_xref;
Window self_xref;
root_xref = (Window)awt_getProperty32(DefaultRootWindow(awt_display),
anchor_prop, anchor_type);
if (root_xref == None) {
DTRACE_PRINTLN("no");
return None;
}
DTRACE_PRINT1("0x%x ... ", (unsigned int)root_xref);
self_xref = (Window)awt_getProperty32(root_xref,
anchor_prop, anchor_type);
if (self_xref != root_xref) {
DTRACE_PRINTLN("stale");
return None;
}
DTRACE_PRINTLN("ok");
return self_xref;
}
/*
* New WM spec: KDE 2.0.1, sawfish 0.3x, ...
* <http://www.freedesktop.org/standards/wm-spec.html>
*/
static Window
awt_wm_isNetSupporting(void)
{
static Boolean checked = False;
static Window isNetSupporting = None;
if (checked) {
return isNetSupporting;
}
DTRACE_PRINT("WM: checking for _NET_SUPPORTING ... ");
isNetSupporting = awt_wm_checkAnchor(_XA_NET_SUPPORTING_WM_CHECK,
XA_WINDOW);
checked = True;
return isNetSupporting;
}
/*
* Old Gnome WM spec: WindowMaker, Enlightenment, IceWM ...
* <http://developer.gnome.org/doc/standards/wm/book1.html>
*/
static Window
awt_wm_isWinSupporting(void)
{
static Boolean checked = False;
static Window isWinSupporting = None;
if (checked) {
return isWinSupporting;
}
DTRACE_PRINT("WM: checking for _WIN_SUPPORTING ... ");
isWinSupporting = awt_wm_checkAnchor(_XA_WIN_SUPPORTING_WM_CHECK,
XA_CARDINAL);
checked = True;
return isWinSupporting;
}
/*
* Check that that the list of protocols specified by WM in property
* named LIST_NAME on the root window contains protocol PROTO.
*/
static Boolean
awt_wm_checkProtocol(Atom list_name, Atom proto)
{
Atom *protocols;
unsigned long nproto;
Boolean found;
unsigned long i;
protocols = awt_getAtomListProperty(DefaultRootWindow(awt_display),
list_name, &nproto);
if (protocols == NULL) {
return False;
}
found = False;
for (i = 0; i < nproto; ++i) {
if (protocols[i] == proto) {
found = True;
break;
}
}
if (protocols != NULL) {
XFree(protocols);
}
return found;
}
static Boolean
awt_wm_doStateProtocolNet(void)
{
static Boolean checked = False;
static Boolean supported = False;
if (checked) {
return supported;
}
if (awt_wm_isNetSupporting()) {
DTRACE_PRINT("WM: checking for _NET_WM_STATE in _NET_SUPPORTED ... ");
supported = awt_wm_checkProtocol(_XA_NET_SUPPORTED, _XA_NET_WM_STATE);
DTRACE_PRINTLN1("%s", supported ? "yes" : "no");
}
checked = True;
return supported;
}
static Boolean
awt_wm_doStateProtocolWin(void)
{
static Boolean checked = False;
static Boolean supported = False;
if (checked) {
return supported;
}
if (awt_wm_isWinSupporting()) {
DTRACE_PRINT("WM: checking for _WIN_STATE in _WIN_PROTOCOLS ... ");
supported = awt_wm_checkProtocol(_XA_WIN_PROTOCOLS, _XA_WIN_STATE);
DTRACE_PRINTLN1("%s", supported ? "yes" : "no");
}
checked = True;
return supported;
}
/*
* Helper function for awt_wm_isEnlightenment.
* Enlightenment uses STRING property for its comms window id. Gaaa!
* The property is ENLIGHTENMENT_COMMS, STRING/8 and the string format
* is "WINID %8x". Gee, I haven't been using scanf for *ages*... :-)
*/
static Window
awt_getECommsWindowIDProperty(Window w)
{
static Atom XA_ENLIGHTENMENT_COMMS = None;
/* Property value*/
Window value;
/* Request status */
int status;
/* Returns of XGetWindowProperty */
Atom actual_type;
int actual_format;
unsigned long nitems;
unsigned long bytes_after;
unsigned char *data;
if (!awt_wm_atomInterned(&XA_ENLIGHTENMENT_COMMS, "ENLIGHTENMENT_COMMS")) {
return False;
}
/* BadWindow is ok and will be blocked by our special handler */
WITH_XERROR_HANDLER(xerror_ignore_bad_window);
{
status = XGetWindowProperty(awt_display, w,
XA_ENLIGHTENMENT_COMMS, 0, 14, False, XA_STRING,
&actual_type, &actual_format, &nitems, &bytes_after,
&data);
}
RESTORE_XERROR_HANDLER;
if (status != Success || data == NULL) {
DTRACE_PRINTLN("no ENLIGHTENMENT_COMMS");
return None;
}
if (actual_type != XA_STRING || actual_format != 8
|| nitems != 14 || bytes_after != 0)
{
DTRACE_PRINTLN("malformed ENLIGHTENMENT_COMMS");
XFree(data); /* NULL data already catched above */
return None;
}
value = None;
sscanf((char *)data, "WINID %8lx", &value); /* NB: 64 bit: XID is long */
XFree(data);
return value;
}
/*
* Is Enlightenment WM running? Congruent to awt_wm_checkAnchor, but
* uses STRING property peculiar to Enlightenment.
*/
static Boolean
awt_wm_isEnlightenment(void)
{
Window root_xref;
Window self_xref;
DTRACE_PRINT("WM: checking for Enlightenment ... ");
root_xref = awt_getECommsWindowIDProperty(DefaultRootWindow(awt_display));
if (root_xref == None) {
return False;
}
DTRACE_PRINT1("0x%x ... ", root_xref);
self_xref = awt_getECommsWindowIDProperty(root_xref);
if (self_xref != root_xref) {
return False;
}
DTRACE_PRINTLN("ok");
return True;
}
/*
* Is CDE running?
*
* XXX: This is hairy... CDE is MWM as well. It seems we simply test
* for default setup and will be bitten if user changes things...
*
* Check for _DT_SM_WINDOW_INFO(_DT_SM_WINDOW_INFO) on root. Take the
* second element of the property and check for presence of
* _DT_SM_STATE_INFO(_DT_SM_STATE_INFO) on that window.
*
* XXX: Any header that defines this structures???
*/
static Boolean
awt_wm_isCDE(void)
{
static Atom _XA_DT_SM_WINDOW_INFO = None;
static Atom _XA_DT_SM_STATE_INFO = None;
/* Property value*/
Window wmwin;
/* Request status */
int status;
/* Returns of XGetWindowProperty */
Atom actual_type;
int actual_format;
unsigned long nitems;
unsigned long bytes_after;
long *data; /* NB: 64 bit: Format 32 props are 'long' */
DTRACE_PRINT("WM: checking for CDE ... ");
if (!awt_wm_atomInterned(&_XA_DT_SM_WINDOW_INFO, "_DT_SM_WINDOW_INFO")) {
return False;
}
status = XGetWindowProperty(awt_display, DefaultRootWindow(awt_display),
_XA_DT_SM_WINDOW_INFO, 0, 2, False, _XA_DT_SM_WINDOW_INFO,
&actual_type, &actual_format, &nitems, &bytes_after,
(unsigned char **)&data);
if (status != Success || data == NULL) {
DTRACE_PRINTLN("no _DT_SM_WINDOW_INFO on root");
return False;
}
if (actual_type != _XA_DT_SM_WINDOW_INFO || actual_format != 32
|| nitems != 2 || bytes_after != 0)
{
DTRACE_PRINTLN("malformed _DT_SM_WINDOW_INFO on root");
XFree(data); /* NULL data already catched above */
return False;
}
wmwin = (Window)data[1];
XFree(data);
/* Now check that this window has _DT_SM_STATE_INFO (ignore contents) */
if (!awt_wm_atomInterned(&_XA_DT_SM_STATE_INFO, "_DT_SM_STATE_INFO")) {
return False;
}
/* BadWindow is ok and will be blocked by our special handler */
WITH_XERROR_HANDLER(xerror_ignore_bad_window);
{
status = XGetWindowProperty(awt_display, wmwin,
_XA_DT_SM_STATE_INFO, 0, 1, False, _XA_DT_SM_STATE_INFO,
&actual_type, &actual_format, &nitems, &bytes_after,
(unsigned char **)&data);
}
RESTORE_XERROR_HANDLER;
if (status != Success || data == NULL) {
DTRACE_PRINTLN("no _DT_SM_STATE_INFO");
return False;
}
if (actual_type != _XA_DT_SM_STATE_INFO || actual_format != 32) {
DTRACE_PRINTLN("malformed _DT_SM_STATE_INFO");
XFree(data); /* NULL data already catched above */
return False;
}
DTRACE_PRINTLN("yes");
XFree(data);
return True;
}
/*
* Is MWM running? (Note that CDE will test positive as well).
*
* Check for _MOTIF_WM_INFO(_MOTIF_WM_INFO) on root. Take the
* second element of the property and check for presence of
* _DT_SM_STATE_INFO(_DT_SM_STATE_INFO) on that window.
*/
static Boolean
awt_wm_isMotif(void)
{
/*
* Grr. Motif just had to be different, ain't it!? Everyone use
* "XA" for things of type Atom, but motif folks chose to define
* _XA_MOTIF_* to be atom *names*. How pathetic...
*/
#undef _XA_MOTIF_WM_INFO
static Atom _XA_MOTIF_WM_INFO = None;
static Atom _XA_DT_WORKSPACE_CURRENT = None;
/* Property value */
Window wmwin;
Atom *curws;
/* Request status */
int status;
/* Returns of XGetWindowProperty */
Atom actual_type;
int actual_format;
unsigned long nitems;
unsigned long bytes_after;
long *data; /* NB: 64 bit: Format 32 props are 'long' */
DTRACE_PRINT("WM: checking for MWM ... ");
if (!awt_wm_atomInterned(&_XA_MOTIF_WM_INFO, "_MOTIF_WM_INFO")
|| !awt_wm_atomInterned(&_XA_DT_WORKSPACE_CURRENT, "_DT_WORKSPACE_CURRENT"))
{
return False;
}
status = XGetWindowProperty(awt_display, DefaultRootWindow(awt_display),
_XA_MOTIF_WM_INFO, 0, PROP_MOTIF_WM_INFO_ELEMENTS, False,
_XA_MOTIF_WM_INFO, &actual_type,
&actual_format, &nitems, &bytes_after,
(unsigned char **)&data);
if (status != Success || data == NULL) {
DTRACE_PRINTLN("no _MOTIF_WM_INFO on root");
return False;
}
if (actual_type != _XA_MOTIF_WM_INFO || actual_format != 32
|| nitems != PROP_MOTIF_WM_INFO_ELEMENTS || bytes_after != 0)
{
DTRACE_PRINTLN("malformed _MOTIF_WM_INFO on root");
XFree(data); /* NULL data already catched above */
return False;
}
/* NB: 64 bit: Cannot cast data to MotifWmInfo */
wmwin = (Window)data[1];
XFree(data);
/* Now check that this window has _DT_WORKSPACE_CURRENT */
curws = awt_getAtomListProperty(wmwin, _XA_DT_WORKSPACE_CURRENT, NULL);
if (curws == NULL) {
DTRACE_PRINTLN("no _DT_WORKSPACE_CURRENT");
return False;
}
DTRACE_PRINTLN("yes");
XFree(curws);
return True;
}
static Boolean
awt_wm_isNetWMName(char *name)
{
Window anchor;
unsigned char *net_wm_name;
Boolean matched;
anchor = awt_wm_isNetSupporting();
if (anchor == None) {
return False;
}
DTRACE_PRINT1("WM: checking for %s by _NET_WM_NAME ... ", name);
/*
* Check both UTF8_STRING and STRING. We only call this function
* with ASCII names and UTF8 preserves ASCII bit-wise. wm-spec
* mandates UTF8_STRING for _NET_WM_NAME but at least sawfish-1.0
* still uses STRING. (mmm, moving targets...).
*/
net_wm_name = awt_getProperty8(anchor, _XA_NET_WM_NAME, XA_UTF8_STRING);
if (net_wm_name == NULL) {
net_wm_name = awt_getProperty8(anchor, _XA_NET_WM_NAME, XA_STRING);
}
if (net_wm_name == NULL) {
DTRACE_PRINTLN("no (missing _NET_WM_NAME)");
return False;
}
matched = (strcmp((char *)net_wm_name, name) == 0);
if (matched) {
DTRACE_PRINTLN("yes");
} else {
DTRACE_PRINTLN1("no (_NET_WM_NAME = \"%s\")", net_wm_name);
}
XFree(net_wm_name);
return matched;
}
/*
* Is Sawfish running?
*/
static Boolean
awt_wm_isSawfish(void)
{
return awt_wm_isNetWMName("Sawfish");
}
/*
* Is KDE2 (KWin) running?
*/
static Boolean
awt_wm_isKDE2(void)
{
return awt_wm_isNetWMName("KWin");
}
/*
* Is Metacity running?
*/
static Boolean
awt_wm_isMetacity(void)
{
return awt_wm_isNetWMName("Metacity");
}
/*
* Temporary error handler that ensures that we know if
* XChangeProperty succeeded or not.
*/
static int /* but ignored */
xerror_verify_change_property(Display *dpy, XErrorEvent *err)
{
XERROR_SAVE(err);
if (err->request_code == X_ChangeProperty) {
return 0;
}
else {
return (*xerror_saved_handler)(dpy, err);
}
}
/*
* Prepare IceWM check.
*
* The only way to detect IceWM, seems to be by setting
* _ICEWM_WINOPTHINT(_ICEWM_WINOPTHINT/8) on root and checking if it
* was immediately deleted by IceWM.
*
* But messing with PropertyNotify here is way too much trouble, so
* approximate the check by setting the property in this function and
* checking if it still exists later on.
*
* Gaa, dirty dances...
*/
static Boolean
awt_wm_prepareIsIceWM(void)
{
static Atom _XA_ICEWM_WINOPTHINT = None;
/*
* Choose something innocuous: "AWT_ICEWM_TEST allWorkspaces 0".
* IceWM expects "class\0option\0arg\0" with zero bytes as delimiters.
*/
static unsigned char opt[] = {
'A','W','T','_','I','C','E','W','M','_','T','E','S','T','\0',
'a','l','l','W','o','r','k','s','p','a','c','e','s','\0',
'0','\0'
};
DTRACE_PRINT("WM: scheduling check for IceWM ... ");
if (!awt_wm_atomInterned(&_XA_ICEWM_WINOPTHINT, "_ICEWM_WINOPTHINT")) {
return False;
}
WITH_XERROR_HANDLER(xerror_verify_change_property);
{
XChangeProperty(awt_display, DefaultRootWindow(awt_display),
_XA_ICEWM_WINOPTHINT, _XA_ICEWM_WINOPTHINT, 8,
PropModeReplace, opt, sizeof(opt));
}
RESTORE_XERROR_HANDLER;
if (xerror_code != Success) {
DTRACE_PRINTLN1("can't set _ICEWM_WINOPTHINT, error = %d",
xerror_code);
return False;
}
else {
DTRACE_PRINTLN("scheduled");
return True;
}
}
/*
* Is IceWM running?
*
* Note well: Only call this if awt_wm_prepareIsIceWM succeeded, or a
* false positive will be reported.
*/
static Boolean
awt_wm_isIceWM(void)
{
static Atom _XA_ICEWM_WINOPTHINT = None;
/* Request status */
int status;
/* Returns of XGetWindowProperty */
Atom actual_type;
int actual_format;
unsigned long nitems;
unsigned long bytes_after;
unsigned char *data;
DTRACE_PRINT("WM: checking for IceWM ... ");
if (!awt_wm_atomInterned(&_XA_ICEWM_WINOPTHINT, "_ICEWM_WINOPTHINT")) {
return False;
}
XGetWindowProperty(awt_display, DefaultRootWindow(awt_display),
_XA_ICEWM_WINOPTHINT, 0, 0xFFFF, True, /* NB: deleting! */
_XA_ICEWM_WINOPTHINT, &actual_type,
&actual_format, &nitems, &bytes_after,
&data);
if (data != NULL) {
XFree(data);
}
if (actual_type == None) {
DTRACE_PRINTLN("yes");
return True;
}
else {
DTRACE_PRINTLN("no");
return False;
}
}
/*
* Is OpenLook WM running?
*
* This one is pretty lame, but the only property peculiar to OLWM is
* _SUN_WM_PROTOCOLS(ATOM[]). Fortunately, olwm deletes it on exit.
*/
static Boolean
awt_wm_isOpenLook(void)
{
static Atom _XA_SUN_WM_PROTOCOLS = None;
Atom *list;
DTRACE_PRINT("WM: checking for OpenLook WM ... ");
if (!awt_wm_atomInterned(&_XA_SUN_WM_PROTOCOLS, "_SUN_WM_PROTOCOLS")) {
return False;
}
list = awt_getAtomListProperty(DefaultRootWindow(awt_display),
_XA_SUN_WM_PROTOCOLS, NULL);
if (list == NULL) {
DTRACE_PRINTLN("no _SUN_WM_PROTOCOLS on root");
return False;
}
DTRACE_PRINTLN("yes");
XFree(list);
return True;
}
static Boolean winmgr_running = False;
/*
* Temporary error handler that checks if selecting for
* SubstructureRedirect failed.
*/
static int /* but ignored */
xerror_detect_wm(Display *dpy, XErrorEvent *err)
{
XERROR_SAVE(err);
if (err->request_code == X_ChangeWindowAttributes
&& err->error_code == BadAccess)
{
DTRACE_PRINTLN("some WM is running (hmm, we'll see)");
winmgr_running = True;
return 0;
}
else {
return (*xerror_saved_handler)(dpy, err);
}
}
/*
* Make an educated guess about running window manager.
* XXX: ideally, we should detect wm restart.
*/
enum wmgr_t
awt_wm_getRunningWM(void)
{
/*
* Ideally, we should support cases when a different WM is started
* during a Java app lifetime.
*/
static enum wmgr_t awt_wmgr = UNDETERMINED_WM;
XSetWindowAttributes substruct;
const char *vendor_string;
Boolean doIsIceWM;
if (awt_wmgr != UNDETERMINED_WM) {
return awt_wmgr;
}
/*
* Quick checks for specific servers.
*/
vendor_string = ServerVendor(awt_display);
if (strstr(vendor_string, "eXcursion") != NULL) {
/*
* Use NO_WM since in all other aspects eXcursion is like not
* having a window manager running. I.e. it does not reparent
* top level shells.
*/
DTRACE_PRINTLN("WM: eXcursion detected - treating as NO_WM");
awt_wmgr = NO_WM;
return awt_wmgr;
}
/*
* If *any* window manager is running?
*
* Try selecting for SubstructureRedirect, that only one client
* can select for, and if the request fails, than some other WM is
* already running.
*/
winmgr_running = 0;
substruct.event_mask = SubstructureRedirectMask;
DTRACE_PRINT("WM: trying SubstructureRedirect ... ");
WITH_XERROR_HANDLER(xerror_detect_wm);
{
XChangeWindowAttributes(awt_display, DefaultRootWindow(awt_display),
CWEventMask, &substruct);
}
RESTORE_XERROR_HANDLER;
/*
* If no WM is running than our selection for SubstructureRedirect
* succeeded and needs to be undone (hey we are *not* a WM ;-).
*/
if (!winmgr_running) {
DTRACE_PRINTLN("no WM is running");
awt_wmgr = NO_WM;
substruct.event_mask = 0;
XChangeWindowAttributes(awt_display, DefaultRootWindow(awt_display),
CWEventMask, &substruct);
return NO_WM;
}
/* actual check for IceWM to follow below */
doIsIceWM = awt_wm_prepareIsIceWM(); /* and let IceWM to act */
if (awt_wm_isNetSupporting()) {
awt_wm_doStateProtocolNet();
}
if (awt_wm_isWinSupporting()) {
awt_wm_doStateProtocolWin();
}
/*
* Ok, some WM is out there. Check which one by testing for
* "distinguishing" atoms.
*/
if (doIsIceWM && awt_wm_isIceWM()) {
awt_wmgr = ICE_WM;
}
else if (awt_wm_isEnlightenment()) {
awt_wmgr = ENLIGHTEN_WM;
}
else if (awt_wm_isMetacity()) {
awt_wmgr = METACITY_WM;
}
else if (awt_wm_isSawfish()) {
awt_wmgr = SAWFISH_WM;
}
else if (awt_wm_isKDE2()) {
awt_wmgr = KDE2_WM;
}
/*
* We don't check for legacy WM when we already know that WM
* supports WIN or _NET wm spec.
*/
else if (awt_wm_isNetSupporting()) {
DTRACE_PRINTLN("WM: other WM (supports _NET)");
awt_wmgr = OTHER_WM;
}
else if (awt_wm_isWinSupporting()) {
DTRACE_PRINTLN("WM: other WM (supports _WIN)");
awt_wmgr = OTHER_WM;
}
/*
* Check for legacy WMs.
*/
else if (awt_wm_isCDE()) { /* XXX: must come before isMotif */
awt_wmgr = CDE_WM;
}
else if (awt_wm_isMotif()) {
awt_wmgr = MOTIF_WM;
}
else if (awt_wm_isOpenLook()) {
awt_wmgr = OPENLOOK_WM;
}
else {
DTRACE_PRINTLN("WM: some other legacy WM");
awt_wmgr = OTHER_WM;
}
return awt_wmgr;
}
/*
* Some buggy WMs ignore window gravity when processing
* ConfigureRequest and position window as if the gravity is Static.
* We work around this in MWindowPeer.pReshape().
*/
Boolean
awt_wm_configureGravityBuggy(void)
{
static int env_not_checked = 1;
static int env_buggy = 0;
if (env_not_checked) {
DTRACE_PRINT("WM: checking for _JAVA_AWT_WM_STATIC_GRAVITY in environment ... ");
if (getenv("_JAVA_AWT_WM_STATIC_GRAVITY") != NULL) {
DTRACE_PRINTLN("set");
env_buggy = 1;
} else {
DTRACE_PRINTLN("no");
}
env_not_checked = 0;
}
if (env_buggy) {
return True;
}
switch (awt_wm_getRunningWM()) {
case ICE_WM:
/*
* See bug #228981 at IceWM's SourceForge pages.
* Latest stable version 1.0.8-6 still has this problem.
*/
return True;
case ENLIGHTEN_WM:
/* At least E16 is buggy. */
return True;
default:
return False;
}
}
/**
* Check if state is supported.
* Note that a compound state is always reported as not supported.
* Note also that MAXIMIZED_BOTH is considered not a compound state.
* Therefore, a compound state is just ICONIFIED | anything else.
*
*/
Boolean
awt_wm_supportsExtendedState(jint state)
{
switch (state) {
case java_awt_Frame_MAXIMIZED_VERT:
case java_awt_Frame_MAXIMIZED_HORIZ:
/*
* WMs that talk NET/WIN protocol, but do not support
* unidirectional maximization.
*/
if (awt_wm_getRunningWM() == METACITY_WM) {
/* "This is a deliberate policy decision." -hp */
return JNI_FALSE;
}
/* FALLTROUGH */
case java_awt_Frame_MAXIMIZED_BOTH:
return (awt_wm_doStateProtocolNet() || awt_wm_doStateProtocolWin());
default:
return JNI_FALSE;
}
}
/*****************************************************************************\
*
* Size and decoration hints ...
*
\*****************************************************************************/
/*
* Remove size hints specified by the mask.
* XXX: Why do we need this in the first place???
*/
void
awt_wm_removeSizeHints(Widget shell, long mask)
{
Display *dpy = XtDisplay(shell);
Window shell_win = XtWindow(shell);
XSizeHints *hints = XAllocSizeHints();
long ignore = 0;
if (hints == NULL) {
DTRACE_PRINTLN("WM: removeSizeHints FAILED to allocate XSizeHints");
return;
}
/* sanitize the mask, only do these hints */
mask &= (PMaxSize|PMinSize|USPosition|PPosition);
XGetWMNormalHints(dpy, shell_win, hints, &ignore);
if ((hints->flags & mask) == 0) {
XFree(hints);
return;
}
#ifdef DEBUG
DTRACE_PRINT("WM: removing hints");
if (mask & PMaxSize) {
DTRACE_PRINT(" Max = ");
if (hints->flags & PMaxSize) {
DTRACE_PRINT2("%d x %d;", hints->max_width, hints->max_height);
} else {
DTRACE_PRINT("none;");
}
}
if (mask & PMinSize) {
DTRACE_PRINT(" Min = ");
if (hints->flags & PMinSize) {
DTRACE_PRINT2("%d x %d;", hints->min_width, hints->min_height);
} else {
DTRACE_PRINT("none;");
}
}
DTRACE_PRINTLN("");
#endif
hints->flags &= ~mask;
XSetWMNormalHints(dpy, shell_win, hints);
XFree(hints);
}
/*
*
*
*/
static void
awt_wm_proclaimUrgency(struct FrameData *wdata)
{
Display *dpy = XtDisplay(wdata->winData.shell);
Window shell_win = XtWindow(wdata->winData.shell);
XWMHints *hints = XGetWMHints(dpy, shell_win);
if( hints == NULL ) {
/* For now just */ return;
}
if ((hints->flags & URGENCY_HINT) != 0) {
/* it's here already */
XFree(hints);
return;
}
hints->flags |= URGENCY_HINT;
XSetWMHints(dpy, shell_win, hints);
XFree(hints);
}
/*
* If MWM_DECOR_ALL bit is set, then the rest of the bit-mask is taken
* to be subtracted from the decorations. Normalize decoration spec
* so that we can map motif decor to something else bit-by-bit in the
* rest of the code.
*/
static int
awt_wm_normalizeMotifDecor(int decorations)
{
int d;
if (!(decorations & MWM_DECOR_ALL))
return decorations; /* already normalized */
d = MWM_DECOR_BORDER |MWM_DECOR_RESIZEH | MWM_DECOR_TITLE
| MWM_DECOR_MENU | MWM_DECOR_MINIMIZE | MWM_DECOR_MAXIMIZE;
d &= ~decorations;
return d;
}
/*
* Infer OL properties from MWM decorations.
* Use _OL_DECOR_DEL(ATOM[]) to remove unwanted ones.
*/
static void
awt_wm_setOLDecor(struct FrameData *wdata, Boolean resizable, int decorations)
{
Window shell_win = XtWindow(wdata->winData.shell);
Atom decorDel[3];
int nitems;
if (shell_win == None) {
DTRACE_PRINTLN("WM: setOLDecor - no window, returning");
return;
}
decorations = awt_wm_normalizeMotifDecor(decorations);
DTRACE_PRINT("WM: _OL_DECOR_DEL = {");
nitems = 0;
if (!(decorations & MWM_DECOR_TITLE)) {
DTRACE_PRINT(" _OL_DECOR_HEADER");
decorDel[nitems++] = _XA_OL_DECOR_HEADER;
}
if (!(decorations & (MWM_DECOR_RESIZEH | MWM_DECOR_MAXIMIZE))) {
DTRACE_PRINT(" _OL_DECOR_RESIZE");
decorDel[nitems++] = _XA_OL_DECOR_RESIZE;
}
if (!(decorations & (MWM_DECOR_MENU | MWM_DECOR_MAXIMIZE
| MWM_DECOR_MINIMIZE)))
{
DTRACE_PRINT(" _OL_DECOR_CLOSE");
decorDel[nitems++] = _XA_OL_DECOR_CLOSE;
}
DTRACE_PRINT(" }");
if (nitems == 0) {
DTRACE_PRINTLN(" ... removing");
XDeleteProperty(awt_display, shell_win, _XA_OL_DECOR_DEL);
}
else {
DTRACE_PRINTLN(" ... setting");
XChangeProperty(awt_display, shell_win,
_XA_OL_DECOR_DEL, XA_ATOM, 32,
PropModeReplace, (unsigned char *)decorDel, nitems);
}
}
/*
* Set MWM decorations. Infer MWM functions from decorations.
*/
static void
awt_wm_setMotifDecor(struct FrameData *wdata, Boolean resizable, int decorations)
{
int functions;
/* Apparently some WMs don't implement MWM_*_ALL semantic correctly */
if ((decorations & MWM_DECOR_ALL) && (decorations != MWM_DECOR_ALL)) {
decorations = awt_wm_normalizeMotifDecor(decorations);
DTRACE_PRINTLN1("WM: setMotifDecor normalize exclusions, decor = 0x%X",
decorations);
}
DTRACE_PRINT("WM: setMotifDecor functions = {");
functions = 0;
if (decorations & MWM_DECOR_ALL) {
DTRACE_PRINT(" ALL");
functions |= MWM_FUNC_ALL;
}
else {
/*
* Functions we always want to be enabled as mwm(1) and
* descendants not only hide disabled functions away from
* user, but also ignore corresponding requests from the
* program itself (e.g. 4442047).
*/
DTRACE_PRINT(" CLOSE MOVE MINIMIZE");
functions |= (MWM_FUNC_CLOSE | MWM_FUNC_MOVE | MWM_FUNC_MINIMIZE);
if (resizable) {
DTRACE_PRINT(" RESIZE MAXIMIZE");
functions |= MWM_FUNC_RESIZE | MWM_FUNC_MAXIMIZE;
}
}
DTRACE_PRINTLN(" }");
XtVaSetValues(wdata->winData.shell,
XmNmwmDecorations, decorations,
XmNmwmFunctions, functions,
NULL);
}
/*
* Under some window managers if shell is already mapped, we MUST
* unmap and later remap in order to effect the changes we make in the
* window manager decorations.
*
* N.B. This unmapping / remapping of the shell exposes a bug in
* X/Motif or the Motif Window Manager. When you attempt to map a
* widget which is positioned (partially) off-screen, the window is
* relocated to be entirely on screen. Good idea. But if both the x
* and the y coordinates are less than the origin (0,0), the first
* (re)map will move the window to the origin, and any subsequent
* (re)map will relocate the window at some other point on the screen.
* I have written a short Motif test program to discover this bug.
* This should occur infrequently and it does not cause any real
* problem. So for now we'll let it be.
*/
static Boolean
awt_wm_needRemap()
{
switch (awt_wm_getRunningWM()) {
#if 0 /* XXX */
case OPENLOOK_WM:
case MOTIF_WM:
case CDE_WM:
case ICE_WM:
case ENLIGHTEN_WM:
return True;
#endif
default:
return True;
}
}
/*
* Set decoration hints on the shell to wdata->decor adjusted
* appropriately if not resizable.
*/
void
awt_wm_setShellDecor(struct FrameData *wdata, Boolean resizable)
{
int decorations = wdata->decor;
DTRACE_PRINTLN3("WM: setShellDecor(0x%x/0x%x, %s)",
wdata->winData.shell, XtWindow(wdata->winData.shell),
resizable ? "resizable" : "not resizable");
if (!resizable) {
if (decorations & MWM_DECOR_ALL) {
decorations |= (MWM_DECOR_RESIZEH | MWM_DECOR_MAXIMIZE);
}
else {
decorations &= ~(MWM_DECOR_RESIZEH | MWM_DECOR_MAXIMIZE);
}
}
DTRACE_PRINTLN1("WM: decorations = 0x%X", decorations);
awt_wm_setMotifDecor(wdata, resizable, decorations);
awt_wm_setOLDecor(wdata, resizable, decorations);
/* Some WMs need remap to redecorate the window */
if (wdata->isShowing && awt_wm_needRemap()) {
/*
* Do the re/mapping at the Xlib level. Since we essentially
* work around a WM bug we don't want this hack to be exposed
* to Intrinsics (i.e. don't mess with grabs, callbacks etc).
*/
Display *dpy = XtDisplay(wdata->winData.shell);
Window shell_win = XtWindow(wdata->winData.shell);
DTRACE_PRINT("WM: setShellDecor REMAPPING ... ");
XUnmapWindow(dpy, shell_win);
XSync(dpy, False); /* give WM a chance to catch up */
XMapWindow(dpy, shell_win);
DTRACE_PRINTLN("done");
}
}
/*
* Make specified shell resizable.
*/
void
awt_wm_setShellResizable(struct FrameData *wdata)
{
DTRACE_PRINTLN2("WM: setShellResizable(0x%x/0x%x)",
wdata->winData.shell, XtWindow(wdata->winData.shell));
XtVaSetValues(wdata->winData.shell,
XmNallowShellResize, True,
XmNminWidth, XtUnspecifiedShellInt,
XmNminHeight, XtUnspecifiedShellInt,
XmNmaxWidth, XtUnspecifiedShellInt,
XmNmaxHeight, XtUnspecifiedShellInt,
NULL);
/* REMINDER: will need to revisit when setExtendedStateBounds is added */
awt_wm_removeSizeHints(wdata->winData.shell, PMinSize|PMaxSize);
/* Restore decorations */
awt_wm_setShellDecor(wdata, True);
}
/*
* Make specified shell non-resizable.
* If justChangeSize is false, update decorations as well.
*/
void
awt_wm_setShellNotResizable(struct FrameData *wdata,
int32_t width, int32_t height,
Boolean justChangeSize)
{
DTRACE_PRINTLN5("WM: setShellNotResizable(0x%x/0x%x, %d, %d, %s)",
wdata->winData.shell, XtWindow(wdata->winData.shell),
width, height,
justChangeSize ? "size only" : "redecorate");
/* Fix min/max size hints at the specified values */
if ((width > 0) && (height > 0)) {
XtVaSetValues(wdata->winData.shell,
XmNwidth, (XtArgVal)width,
XmNheight, (XtArgVal)height,
XmNminWidth, (XtArgVal)width,
XmNminHeight, (XtArgVal)height,
XmNmaxWidth, (XtArgVal)width,
XmNmaxHeight, (XtArgVal)height,
NULL);
}
if (!justChangeSize) { /* update decorations */
awt_wm_setShellDecor(wdata, False);
}
}
/*
* Helper function for awt_wm_getInsetsFromProp.
* Read property of type CARDINAL[4] = { left, right, top, bottom }
*/
static Boolean
awt_wm_readInsetsArray(Window shell_win, Atom insets_property,
int32_t *top, int32_t *left, int32_t *bottom, int32_t *right)
{
/* Request status */
int status;
/* Returns of XGetWindowProperty */
Atom actual_type;
int actual_format;
unsigned long nitems;
unsigned long bytes_after;
long *insets = NULL; /* NB: 64 bit: Format 32 props are 'long' */
status = XGetWindowProperty (awt_display, shell_win,
insets_property, 0, 4, False, XA_CARDINAL,
&actual_type, &actual_format, &nitems, &bytes_after,
(unsigned char **)&insets);
if (status != Success || insets == NULL) {
DTRACE_PRINTLN("failed");
return False;
}
if (actual_type != XA_CARDINAL || actual_format != 32) {
DTRACE_PRINTLN("type/format mismatch");
XFree(insets);
return False;
}
*left = (int32_t)insets[0];
*right = (int32_t)insets[1];
*top = (int32_t)insets[2];
*bottom = (int32_t)insets[3];
XFree(insets);
/* Order is that of java.awt.Insets.toString */
DTRACE_PRINTLN4("[top=%d,left=%d,bottom=%d,right=%d]",
*top, *left, *bottom, *right);
return True;
}
/*
* If WM implements the insets property - fill insets with values
* specified in that property.
*/
Boolean
awt_wm_getInsetsFromProp(Window shell_win,
int32_t *top, int32_t *left, int32_t *bottom, int32_t *right)
{
switch (awt_wm_getRunningWM()) {
case ENLIGHTEN_WM:
DTRACE_PRINT("WM: reading _E_FRAME_SIZE ... ");
return awt_wm_readInsetsArray(shell_win, _XA_E_FRAME_SIZE,
top, left, bottom, right);
#if 0
/*
* uwe: disabled for now, as KDE seems to supply bogus values
* when we maximize iconified frame. Need to verify with KDE2.1.
* NB: Also note, that "external" handles (e.g. in laptop decor)
* are also included in the frame strut, which is probably not
* what we want.
*/
case KDE2_WM:
DTRACE_PRINT("WM: reading _KDE_NET_WM_FRAME_STRUT ... ");
return awt_wm_readInsetsArray(shell_win, _XA_KDE_NET_WM_FRAME_STRUT,
top, left, bottom, right);
#endif
default:
return False;
}
}
/*
* XmNiconic and Map/UnmapNotify (that XmNiconic relies on) are
* unreliable, since mapping changes can happen for a virtual desktop
* switch or MacOS style shading that became quite popular under X as
* well. Yes, it probably should not be this way, as it violates
* ICCCM, but reality is that quite a lot of window managers abuse
* mapping state.
*/
int
awt_wm_getWMState(Window shell_win)
{
/* Request status */
int status;
/* Returns of XGetWindowProperty */
Atom actual_type;
int actual_format;
unsigned long nitems;
unsigned long bytes_after;
long *data; /* NB: 64 bit: Format 32 props are 'long' */
int wm_state;
status = XGetWindowProperty(awt_display, shell_win,
XA_WM_STATE, 0, 1, False, XA_WM_STATE,
&actual_type, &actual_format, &nitems, &bytes_after,
(unsigned char **)&data);
if (status != Success || data == NULL) {
return WithdrawnState;
}
if (actual_type != XA_WM_STATE) {
DTRACE_PRINTLN1("WM: WM_STATE(0x%x) - wrong type", shell_win);
XFree(data);
return WithdrawnState;
}
wm_state = (int)*data;
XFree(data);
return wm_state;
}
/*****************************************************************************\
*
* Reading state from properties WM puts on our window ...
*
\*****************************************************************************/
/*
* New "NET" WM spec: _NET_WM_STATE/Atom[]
*/
static jint
awt_wm_getStateNet(Window shell_win)
{
Atom *net_wm_state;
jint java_state;
unsigned long nitems;
unsigned long i;
net_wm_state = awt_getAtomListProperty(shell_win, _XA_NET_WM_STATE, &nitems);
if (nitems == 0) {
DTRACE_PRINTLN("WM: _NET_WM_STATE = { }");
if (net_wm_state) {
XFree(net_wm_state);
}
return java_awt_Frame_NORMAL;
}
#ifdef DEBUG
DTRACE_PRINT("WM: ");
awt_wm_dtraceStateNet(net_wm_state, nitems);
#endif
java_state = java_awt_Frame_NORMAL;
for (i = 0; i < nitems; ++i) {
if (net_wm_state[i] == _XA_NET_WM_STATE_MAXIMIZED_VERT) {
java_state |= java_awt_Frame_MAXIMIZED_VERT;
}
else if (net_wm_state[i] == _XA_NET_WM_STATE_MAXIMIZED_HORZ) {
java_state |= java_awt_Frame_MAXIMIZED_HORIZ;
}
}
XFree(net_wm_state);
return java_state;
}
Boolean
awt_wm_isStateNetHidden(Window shell_win)
{
Atom *net_wm_state;
Boolean result = False;
unsigned long nitems;
unsigned long i;
net_wm_state = awt_getAtomListProperty(shell_win, _XA_NET_WM_STATE, &nitems);
if (nitems == 0) {
DTRACE_PRINTLN("WM: _NET_WM_STATE = { }");
if (net_wm_state) {
XFree(net_wm_state);
}
return False;
}
#ifdef DEBUG
DTRACE_PRINT("WM: ");
awt_wm_dtraceStateNet(net_wm_state, nitems);
#endif
for (i = 0; i < nitems; ++i) {
if (net_wm_state[i] == _XA_NET_WM_STATE_HIDDEN) {
result = True;
}
}
XFree(net_wm_state);
return result;
}
/*
* Similar code to getStateNet, to get layer state.
*/
static int
awt_wm_getLayerNet(Window shell_win)
{
Atom *net_wm_state;
int java_state;
unsigned long nitems;
unsigned long i;
net_wm_state = awt_getAtomListProperty(shell_win, _XA_NET_WM_STATE, &nitems);
if (nitems == 0) {
DTRACE_PRINTLN("WM: _NET_WM_STATE = { }");
if (net_wm_state) {
XFree(net_wm_state);
}
return LAYER_NORMAL;
}
#ifdef DEBUG
DTRACE_PRINT("WM: ");
awt_wm_dtraceStateNet(net_wm_state, nitems);
#endif
java_state = LAYER_NORMAL;
for (i = 0; i < nitems; ++i) {
if (net_wm_state[i] == _XA_NET_WM_STATE_ABOVE) {
java_state = LAYER_ALWAYS_ON_TOP;
}
}
XFree(net_wm_state);
return java_state;
}
/*
* Old Gnome spec: _WIN_STATE/CARDINAL
*/
static jint
awt_wm_getStateWin(Window shell_win)
{
long win_state;
jint java_state;
win_state = awt_getProperty32(shell_win, _XA_WIN_STATE, XA_CARDINAL);
#ifdef DEBUG
DTRACE_PRINT("WM: ");
awt_wm_dtraceStateWin(win_state);
#endif
java_state = java_awt_Frame_NORMAL;
if (win_state & WIN_STATE_MAXIMIZED_VERT) {
java_state |= java_awt_Frame_MAXIMIZED_VERT;
}
if (win_state & WIN_STATE_MAXIMIZED_HORIZ) {
java_state |= java_awt_Frame_MAXIMIZED_HORIZ;
}
return java_state;
}
/*
* Code similar to getStateWin, to get layer state.
*/
static int
awt_wm_getLayerWin(Window shell_win)
{
long win_state;
jint java_state;
win_state = awt_getProperty32(shell_win, _XA_WIN_LAYER, XA_CARDINAL);
#ifdef DEBUG
DTRACE_PRINT("WM: ");
awt_wm_dtraceStateWin(win_state);
#endif
java_state = LAYER_NORMAL;
if (win_state == WIN_LAYER_ONTOP) {
java_state = LAYER_ALWAYS_ON_TOP;
}
return java_state;
}
static jint
awt_wm_getExtendedState(Window shell_win)
{
if (awt_wm_doStateProtocolNet()) {
return awt_wm_getStateNet(shell_win);
}
else if (awt_wm_doStateProtocolWin()) {
return awt_wm_getStateWin(shell_win);
}
else {
return java_awt_Frame_NORMAL;
}
}
jint
awt_wm_getState(struct FrameData *wdata)
{
Window shell_win = XtWindow(wdata->winData.shell);
jint java_state;
DTRACE_PRINTLN2("WM: getState(0x%x/0x%x)",
wdata->winData.shell, shell_win);
if (shell_win == None) {
DTRACE_PRINTLN("WM: no window, use wdata");
java_state = wdata->state;
}
else {
int wm_state = awt_wm_getWMState(shell_win);
if (wm_state == WithdrawnState) {
DTRACE_PRINTLN("WM: window withdrawn, use wdata");
java_state = wdata->state;
}
else {
#ifdef DEBUG
DTRACE_PRINT("WM: ");
awt_wm_dtraceWMState(wm_state);
#endif
if (wm_state == IconicState) {
java_state = java_awt_Frame_ICONIFIED;
} else {
java_state = java_awt_Frame_NORMAL;
}
java_state |= awt_wm_getExtendedState(shell_win);
}
}
#ifdef DEBUG
DTRACE_PRINT("WM: ");
awt_wm_dtraceStateJava(java_state);
#endif
return java_state;
}
/*****************************************************************************\
*
* Notice window state change when WM changes a property on the window ...
*
\*****************************************************************************/
/*
* Check if property change is a window state protocol message.
* If it is - return True and return the new state in *pstate.
*/
Boolean
awt_wm_isStateChange(struct FrameData *wdata, XPropertyEvent *e, jint *pstate)
{
Window shell_win = XtWindow(wdata->winData.shell);
Boolean is_state_change = False;
int wm_state;
if (!wdata->isShowing) {
return False;
}
wm_state = awt_wm_getWMState(shell_win);
if (wm_state == WithdrawnState) {
return False;
}
if (e->atom == XA_WM_STATE) {
is_state_change = True;
}
else if (e->atom == _XA_NET_WM_STATE) {
is_state_change = awt_wm_doStateProtocolNet();
}
else if (e->atom == _XA_WIN_STATE) {
is_state_change = awt_wm_doStateProtocolWin();
}
if (is_state_change) {
#ifdef DEBUG
Widget shell = wdata->winData.shell;
char *name = XGetAtomName(XtDisplay(shell), e->atom);
DTRACE_PRINTLN4("WM: PropertyNotify(0x%x/0x%x) %s %s",
shell, XtWindow(shell),
name != NULL ? name : "???",
e->state == PropertyNewValue ? "changed" : "deleted");
if (name != NULL) {
XFree(name);
}
DTRACE_PRINT("WM: ");
awt_wm_dtraceWMState(wm_state);
#endif
if (wm_state == IconicState) {
*pstate = java_awt_Frame_ICONIFIED;
} else {
*pstate = java_awt_Frame_NORMAL;
}
*pstate |= awt_wm_getExtendedState(shell_win);
#ifdef DEBUG
DTRACE_PRINT("WM: ");
awt_wm_dtraceStateJava(*pstate);
#endif
}
return is_state_change;
}
/*****************************************************************************\
*
* Setting/changing window state ...
*
\*****************************************************************************/
/*
* Request a state transition from a _NET supporting WM by sending
* _NET_WM_STATE ClientMessage to root window.
*/
static void
awt_wm_requestStateNet(struct FrameData *wdata, jint state)
{
Widget shell = wdata->winData.shell;
Window shell_win = XtWindow(shell);
XClientMessageEvent req;
jint old_net_state;
jint max_changed;
/* must use awt_wm_setInitialStateNet for withdrawn windows */
DASSERT(wdata->isShowing);
/*
* We have to use toggle for maximization because of transitions
* from maximization in one direction only to maximization in the
* other direction only.
*/
old_net_state = awt_wm_getStateNet(shell_win);
max_changed = (state ^ old_net_state) & java_awt_Frame_MAXIMIZED_BOTH;
switch (max_changed) {
case 0:
DTRACE_PRINTLN("WM: requestStateNet - maximization unchanged");
return;
case java_awt_Frame_MAXIMIZED_HORIZ:
DTRACE_PRINTLN("WM: requestStateNet - toggling MAX_HORZ");
req.data.l[1] = _XA_NET_WM_STATE_MAXIMIZED_HORZ;
req.data.l[2] = 0;
break;
case java_awt_Frame_MAXIMIZED_VERT:
DTRACE_PRINTLN("WM: requestStateNet - toggling MAX_VERT");
req.data.l[1] = _XA_NET_WM_STATE_MAXIMIZED_VERT;
req.data.l[2] = 0;
break;
default: /* both */
DTRACE_PRINTLN("WM: requestStateNet - toggling HORZ + VERT");
req.data.l[1] = _XA_NET_WM_STATE_MAXIMIZED_HORZ;
req.data.l[2] = _XA_NET_WM_STATE_MAXIMIZED_VERT;
break;
}
req.type = ClientMessage;
req.window = XtWindow(shell);
req.message_type = _XA_NET_WM_STATE;
req.format = 32;
req.data.l[0] = _NET_WM_STATE_TOGGLE;
XSendEvent(XtDisplay(shell), RootWindowOfScreen(XtScreen(shell)), False,
(SubstructureRedirectMask | SubstructureNotifyMask),
(XEvent *)&req);
}
/*
* Request state transition from a Gnome WM (_WIN protocol) by sending
* _WIN_STATE ClientMessage to root window.
*/
static void
awt_wm_requestStateWin(struct FrameData *wdata, jint state)
{
Widget shell = wdata->winData.shell;
XClientMessageEvent req;
long win_state; /* typeof(XClientMessageEvent.data.l) */
/* must use awt_wm_setInitialStateWin for withdrawn windows */
DASSERT(wdata->isShowing);
win_state = 0;
if (state & java_awt_Frame_MAXIMIZED_VERT) {
win_state |= WIN_STATE_MAXIMIZED_VERT;
}
if (state & java_awt_Frame_MAXIMIZED_HORIZ) {
win_state |= WIN_STATE_MAXIMIZED_HORIZ;
}
req.type = ClientMessage;
req.window = XtWindow(shell);
req.message_type = _XA_WIN_STATE;
req.format = 32;
req.data.l[0] = (WIN_STATE_MAXIMIZED_HORIZ | WIN_STATE_MAXIMIZED_VERT);
req.data.l[1] = win_state;
XSendEvent(XtDisplay(shell), RootWindowOfScreen(XtScreen(shell)), False,
(SubstructureRedirectMask | SubstructureNotifyMask),
(XEvent *)&req);
}
/*
* Specify initial state for _NET supporting WM by setting
* _NET_WM_STATE property on the window to the desired state before
* mapping it.
*/
static void
awt_wm_setInitialStateNet(struct FrameData *wdata, jint state)
{
Widget shell = wdata->winData.shell;
Window shell_win = XtWindow(shell);
Display *dpy = XtDisplay(shell);
Atom *old_state;
unsigned long nitems;
/* must use awt_wm_requestStateNet for managed windows */
DASSERT(!wdata->isShowing);
/* Be careful to not wipe out state bits we don't understand */
old_state = awt_getAtomListProperty(shell_win, _XA_NET_WM_STATE, &nitems);
if (nitems == 0) {
/*
* Empty or absent _NET_WM_STATE - set a new one if necessary.
*/
Atom net_wm_state[AWT_NET_N_KNOWN_STATES];
if (old_state != NULL) {
XFree(old_state);
}
if (state & java_awt_Frame_MAXIMIZED_VERT) {
net_wm_state[nitems++] = _XA_NET_WM_STATE_MAXIMIZED_VERT;
}
if (state & java_awt_Frame_MAXIMIZED_HORIZ) {
net_wm_state[nitems++] = _XA_NET_WM_STATE_MAXIMIZED_HORZ;
}
DASSERT(nitems <= AWT_NET_N_KNOWN_STATES);
if (nitems == 0) {
DTRACE_PRINTLN("WM: initial _NET_WM_STATE not necessary");
return;
}
#ifdef DEBUG
DTRACE_PRINT("WM: setting initial ");
awt_wm_dtraceStateNet(net_wm_state, nitems);
#endif
XChangeProperty(dpy, shell_win,
_XA_NET_WM_STATE, XA_ATOM, 32, PropModeReplace,
(unsigned char *)net_wm_state, nitems);
}
else {
/*
* Tweak existing _NET_WM_STATE, preserving bits we don't use.
*/
jint want= state /* which flags we want */
& (java_awt_Frame_MAXIMIZED_HORIZ | java_awt_Frame_MAXIMIZED_VERT);
jint has = 0; /* which flags the window already has */
int mode; /* property mode: replace/append */
Atom *new_state; /* new _net_wm_state value */
int new_nitems;
unsigned long i;
#ifdef DEBUG
DTRACE_PRINT("WM: already has ");
awt_wm_dtraceStateNet(old_state, nitems);
#endif
for (i = 0; i < nitems; ++i) {
if (old_state[i] == _XA_NET_WM_STATE_MAXIMIZED_HORZ) {
has |= java_awt_Frame_MAXIMIZED_HORIZ;
}
else if (old_state[i] == _XA_NET_WM_STATE_MAXIMIZED_VERT) {
has |= java_awt_Frame_MAXIMIZED_VERT;
}
}
if ((has ^ want) == 0) {
DTRACE_PRINTLN("WM: no changes to _NET_WM_STATE necessary");
XFree(old_state);
return;
}
new_nitems = 0;
if (has == 0) { /* only adding flags */
new_state = calloc(AWT_NET_N_KNOWN_STATES, sizeof(Atom));
mode = PropModeAppend;
}
else {
new_state = calloc(nitems + AWT_NET_N_KNOWN_STATES, sizeof(Atom));
mode = PropModeReplace;
}
if (has != 0) { /* copy existing flags */
DTRACE_PRINT("WM: ");
for (i = 0; i < nitems; ++i) {
if (old_state[i] == _XA_NET_WM_STATE_MAXIMIZED_HORZ) {
if (want & java_awt_Frame_MAXIMIZED_HORIZ) {
DTRACE_PRINT(" keep _HORZ");
} else {
DTRACE_PRINT(" drop _HORZ");
continue;
}
}
else if (old_state[i] == _XA_NET_WM_STATE_MAXIMIZED_VERT) {
if (want & java_awt_Frame_MAXIMIZED_VERT) {
DTRACE_PRINT(" keep _VERT");
} else {
DTRACE_PRINT(" drop _VERT");
continue;
}
}
new_state[new_nitems++] = old_state[i];
}
}
/* Add missing flags */
if ((want & java_awt_Frame_MAXIMIZED_HORIZ)
&& !(has & java_awt_Frame_MAXIMIZED_HORIZ))
{
DTRACE_PRINT(" add _HORZ");
new_state[new_nitems] = _XA_NET_WM_STATE_MAXIMIZED_HORZ;
++new_nitems;
}
if ((want & java_awt_Frame_MAXIMIZED_VERT)
&& !(has & java_awt_Frame_MAXIMIZED_VERT))
{
DTRACE_PRINT(" add _VERT");
new_state[new_nitems] = _XA_NET_WM_STATE_MAXIMIZED_VERT;
++new_nitems;
}
DTRACE_PRINTLN(mode == PropModeReplace ?
" ... replacing" : " ... appending");
XChangeProperty(dpy, shell_win,
_XA_NET_WM_STATE, XA_ATOM, 32, mode,
(unsigned char *)new_state, new_nitems);
XFree(old_state);
XFree(new_state);
}
}
/*
* Specify initial state for a Gnome WM (_WIN protocol) by setting
* WIN_STATE property on the window to the desired state before
* mapping it.
*/
static void
awt_wm_setInitialStateWin(struct FrameData *wdata, jint state)
{
Display *dpy = XtDisplay(wdata->winData.shell);
Window shell_win = XtWindow(wdata->winData.shell);
long win_state, old_win_state;
/* must use awt_wm_requestStateWin for managed windows */
DASSERT(!wdata->isShowing);
/* Be careful to not wipe out state bits we don't understand */
win_state = awt_getProperty32(shell_win, _XA_WIN_STATE, XA_CARDINAL);
old_win_state = win_state;
#ifdef DEBUG
if (win_state != 0) {
DTRACE_PRINT("WM: already has ");
awt_wm_dtraceStateWin(win_state);
}
#endif
/*
* In their stupid quest of reinventing every wheel, Gnome WM spec
* have its own "minimized" hint (instead of using initial state
* and WM_STATE hints). This is bogus, but, apparently, some WMs
* pay attention.
*/
if (state & java_awt_Frame_ICONIFIED) {
win_state |= WIN_STATE_MINIMIZED;
} else {
win_state &= ~WIN_STATE_MINIMIZED;
}
if (state & java_awt_Frame_MAXIMIZED_VERT) {
win_state |= WIN_STATE_MAXIMIZED_VERT;
} else {
win_state &= ~WIN_STATE_MAXIMIZED_VERT;
}
if (state & java_awt_Frame_MAXIMIZED_HORIZ) {
win_state |= WIN_STATE_MAXIMIZED_HORIZ;
} else {
win_state &= ~WIN_STATE_MAXIMIZED_HORIZ;
}
if (old_win_state ^ win_state) {
#ifdef DEBUG
DTRACE_PRINT("WM: setting initial ");
awt_wm_dtraceStateWin(win_state);
#endif
XChangeProperty(dpy, shell_win,
_XA_WIN_STATE, XA_CARDINAL, 32, PropModeReplace,
(unsigned char *)&win_state, 1);
}
#ifdef DEBUG
else {
DTRACE_PRINTLN("WM: no changes to _WIN_STATE necessary");
}
#endif
}
/*
* Request a layer change from a _NET supporting WM by sending
* _NET_WM_STATE ClientMessage to root window.
*/
static void
awt_wm_requestLayerNet(struct FrameData *wdata, int state)
{
Widget shell = wdata->winData.shell;
Window shell_win = XtWindow(shell);
XClientMessageEvent req;
int currentLayer;
long cmd;
/* must use awt_wm_setInitialLayerNet for withdrawn windows */
DASSERT(wdata->isShowing);
currentLayer = awt_wm_getLayerNet(shell_win);
if(state == currentLayer) {
return;
}
cmd = currentLayer == LAYER_ALWAYS_ON_TOP && state == LAYER_NORMAL ?
_NET_WM_STATE_REMOVE :
currentLayer == LAYER_NORMAL && state == LAYER_ALWAYS_ON_TOP ?
_NET_WM_STATE_ADD :
_NET_WM_STATE_ADD;
req.type = ClientMessage;
req.window = XtWindow(shell);
req.message_type = _XA_NET_WM_STATE;
req.format = 32;
req.data.l[0] = cmd;
req.data.l[1] = _XA_NET_WM_STATE_ABOVE;
req.data.l[2] = 0L;
XSendEvent(XtDisplay(shell), RootWindowOfScreen(XtScreen(shell)), False,
(SubstructureRedirectMask | SubstructureNotifyMask),
(XEvent *)&req);
}
/*
* Request a layer change from a Gnome WM (_WIN protocol) by sending
* _WIN_LAYER ClientMessage to root window.
*/
static void
awt_wm_requestLayerWin(struct FrameData *wdata, int state)
{
Widget shell = wdata->winData.shell;
XClientMessageEvent req;
Display *dpy = XtDisplay(shell);
/* must use awt_wm_setInitialLayerWin for withdrawn windows */
DASSERT(wdata->isShowing);
req.type = ClientMessage;
req.window = XtWindow(shell);
req.message_type = _XA_WIN_LAYER;
req.format = 32;
req.data.l[0] = state == LAYER_NORMAL ? WIN_LAYER_NORMAL : WIN_LAYER_ONTOP;
req.data.l[1] = 0L;
req.data.l[2] = 0L;
XSendEvent(XtDisplay(shell), RootWindowOfScreen(XtScreen(shell)), False,
/*(SubstructureRedirectMask |*/
SubstructureNotifyMask,
(XEvent *)&req);
}
/*
* Specify initial layer for _NET supporting WM by setting
* _NET_WM_STATE property on the window to the desired state before
* mapping it.
* NB: looks like it doesn't have any effect.
*/
static void
awt_wm_setInitialLayerNet(struct FrameData *wdata, int state)
{
Widget shell = wdata->winData.shell;
Window shell_win = XtWindow(shell);
Display *dpy = XtDisplay(shell);
Atom *old_state;
unsigned long nitems;
Atom new_state = _XA_NET_WM_STATE_ABOVE;
/* must use awt_wm_requestLayerNet for managed windows */
DASSERT(!wdata->isShowing);
/* Be careful to not wipe out state bits we don't understand */
old_state = awt_getAtomListProperty(shell_win, _XA_NET_WM_STATE, &nitems);
if (nitems == 0 && state != LAYER_ALWAYS_ON_TOP) {
if (old_state != NULL) {
XFree(old_state);
}
return;
}else if( nitems == 0 && state == LAYER_ALWAYS_ON_TOP) {
unsigned long data[2];
/* create new state */
if (old_state != NULL) {
XFree(old_state);
}
nitems = 1;
data[0] = new_state;
data[1] = 0;
XChangeProperty(dpy, shell_win,
_XA_NET_WM_STATE, XA_ATOM, 32, PropModeReplace,
(unsigned char *)data, nitems);
XSync(dpy, False);
}else { /* nitems > 0 */
unsigned long i;
Boolean bShift = False;
int mode;
for(i = 0; i < nitems; i++) {
if( bShift ) {
old_state[i-1] = old_state[i];
}else if( old_state[i] == _XA_NET_WM_STATE_ABOVE ) {
if(state == LAYER_ALWAYS_ON_TOP) {
/* no change necessary */
XFree(old_state);
return;
}else{
/* wipe off this atom */
bShift = True;
}
}
}
if( bShift ) {
/* atom was found and removed: change property */
mode = PropModeReplace;
nitems--;
}else if( state != LAYER_ALWAYS_ON_TOP ) {
/* atom was not found and not needed */
XFree( old_state);
return;
}else {
/* must add new atom */
mode = PropModeAppend;
nitems = 1;
}
XChangeProperty(dpy, shell_win,
_XA_NET_WM_STATE, XA_ATOM, 32, mode,
mode == PropModeAppend ?
(unsigned char *)(&new_state) :
(unsigned char *)old_state, nitems);
XFree(old_state);
XSync(dpy, False);
}
}
/*
* Specify initial layer for a Gnome WM (_WIN protocol) by setting
* WIN_LAYER property on the window to the desired state before
* mapping it.
*/
static void
awt_wm_setInitialLayerWin(struct FrameData *wdata, int state)
{
Display *dpy = XtDisplay(wdata->winData.shell);
Window shell_win = XtWindow(wdata->winData.shell);
long win_state, old_win_state;
int currentLayer;
/* must use awt_wm_requestLayerWin for managed windows */
DASSERT(!wdata->isShowing);
currentLayer = awt_wm_getLayerWin(shell_win);
if( currentLayer == state ) {
/* no change necessary */
return;
}
if( state == LAYER_ALWAYS_ON_TOP ) {
win_state = WIN_LAYER_ONTOP;
}else {
win_state = WIN_LAYER_NORMAL;
}
XChangeProperty(dpy, shell_win,
_XA_WIN_LAYER, XA_CARDINAL, 32, PropModeReplace,
(unsigned char *)&win_state, 1);
}
void
awt_wm_setExtendedState(struct FrameData *wdata, jint state)
{
Display *dpy = XtDisplay(wdata->winData.shell);
Window shell_win = XtWindow(wdata->winData.shell);
#ifdef DEBUG
DTRACE_PRINT2("WM: setExtendedState(0x%x/0x%x) ",
wdata->winData.shell, shell_win);
awt_wm_dtraceStateJava(state);
#endif
if (wdata->isShowing) {
/*
* If the window is managed by WM, we should send
* ClientMessage requests.
*/
if (awt_wm_doStateProtocolNet()) {
awt_wm_requestStateNet(wdata, state);
}
else if (awt_wm_doStateProtocolWin()) {
awt_wm_requestStateWin(wdata, state);
}
XSync(dpy, False);
}
else {
/*
* If the window is withdrawn we should set necessary
* properties directly to the window before mapping it.
*/
if (awt_wm_doStateProtocolNet()) {
awt_wm_setInitialStateNet(wdata, state);
}
else if (awt_wm_doStateProtocolWin()) {
awt_wm_setInitialStateWin(wdata, state);
}
#if 1
/*
* Purge KWM bits.
* Not really tested with KWM, only with WindowMaker.
*/
XDeleteProperty(dpy, shell_win, XA_KWM_WIN_ICONIFIED);
XDeleteProperty(dpy, shell_win, XA_KWM_WIN_MAXIMIZED);
#endif /* 1 */
}
}
static Boolean
awt_wm_supportsLayersNet() {
Boolean supported = awt_wm_doStateProtocolNet();
/*
In fact, WM may report this not supported but do support.
*/
supported &= awt_wm_checkProtocol(_XA_NET_SUPPORTED, _XA_NET_WM_STATE_ABOVE);
return supported;
}
static Boolean
awt_wm_supportsLayersWin() {
Boolean supported = awt_wm_doStateProtocolWin();
/*
* In fact, WM may report this supported but do not support.
*/
supported &= awt_wm_checkProtocol(_XA_WIN_PROTOCOLS, _XA_WIN_LAYER);
return supported;
}
void
awt_wm_updateAlwaysOnTop(struct FrameData *wdata, jboolean bLayerState) {
Display *dpy = XtDisplay(wdata->winData.shell);
Window shell_win = XtWindow(wdata->winData.shell);
int layerState = bLayerState ? LAYER_ALWAYS_ON_TOP : LAYER_NORMAL;
if (wdata->isShowing) {
/**
We don't believe anyone, and now send both ClientMessage requests.
And eg Metacity under RH 6.1 required both to work.
**/
awt_wm_requestLayerNet(wdata, layerState);
awt_wm_requestLayerWin(wdata, layerState);
} else {
/**
We don't believe anyone, and now set both atoms.
And eg Metacity under RH 6.1 required both to work.
**/
awt_wm_setInitialLayerNet(wdata, layerState);
awt_wm_setInitialLayerWin(wdata, layerState);
}
XSync(dpy, False);
}
/*
* Work around for 4775545. _NET version.
*/
static void
awt_wm_unshadeKludgeNet(struct FrameData *wdata)
{
Display *dpy = XtDisplay(wdata->winData.shell);
Window shell_win = XtWindow(wdata->winData.shell);
Atom *net_wm_state;
Boolean shaded;
unsigned long nitems;
unsigned long i;
net_wm_state = awt_getAtomListProperty(shell_win,
_XA_NET_WM_STATE, &nitems);
if (nitems == 0) {
DTRACE_PRINTLN("WM: _NET_WM_STATE = { }");
if (net_wm_state) {
XFree(net_wm_state);
}
return;
}
#ifdef DEBUG
DTRACE_PRINT("WM: ");
awt_wm_dtraceStateNet(net_wm_state, nitems);
#endif
shaded = False;
for (i = 0; i < nitems; ++i) {
if (net_wm_state[i] == _XA_NET_WM_STATE_SHADED) {
shaded = True;
break;
}
}
if (!shaded) {
DTRACE_PRINTLN("WM: not _SHADED, no workaround necessary");
return;
}
DTRACE_PRINTLN("WM: removing _SHADED");
++i; /* skip _SHADED */
while (i < nitems) { /* copy the rest */
net_wm_state[i-1] = net_wm_state[i];
++i;
}
--nitems; /* _SHADED has been removed */
#ifdef DEBUG
DTRACE_PRINT("WM: ");
awt_wm_dtraceStateNet(net_wm_state, nitems);
#endif
WITH_XERROR_HANDLER(xerror_verify_change_property);
{
XChangeProperty(dpy, shell_win,
_XA_NET_WM_STATE, XA_ATOM, 32, PropModeReplace,
(unsigned char *)net_wm_state, nitems);
}
RESTORE_XERROR_HANDLER;
if (xerror_code != Success) {
DTRACE_PRINTLN1("WM: XChangeProperty failed, error = %d",
xerror_code);
}
XFree(net_wm_state);
}
/*
* Work around for 4775545. _WIN version.
*/
static void
awt_wm_unshadeKludgeWin(struct FrameData *wdata)
{
Display *dpy = XtDisplay(wdata->winData.shell);
Window shell_win = XtWindow(wdata->winData.shell);
long win_state;
win_state = awt_getProperty32(shell_win, _XA_WIN_STATE, XA_CARDINAL);
#ifdef DEBUG
DTRACE_PRINT("WM: ");
awt_wm_dtraceStateWin(win_state);
#endif
if ((win_state & WIN_STATE_SHADED) == 0) {
DTRACE_PRINTLN("WM: not _SHADED, no workaround necessary");
return;
}
win_state &= ~WIN_STATE_SHADED;
XChangeProperty(dpy, shell_win,
_XA_WIN_STATE, XA_CARDINAL, 32, PropModeReplace,
(unsigned char *)&win_state, 1);
}
/*
* Work around for 4775545.
*
* If WM exits while the top-level is shaded, the shaded hint remains
* on the top-level properties. When WM restarts and sees the shaded
* window it can reparent it into a "pre-shaded" decoration frame
* (Metacity does), and our insets logic will go crazy, b/c it will
* see a huge nagative bottom inset. There's no clean solution for
* this, so let's just be weasels and drop the shaded hint if we
* detect that WM exited. NB: we are in for a race condition with WM
* restart here. NB2: e.g. WindowMaker saves the state in a private
* property that this code knows nothing about, so this workaround is
* not effective; other WMs might play similar tricks.
*/
void
awt_wm_unshadeKludge(struct FrameData *wdata)
{
DTRACE_PRINTLN("WM: unshade kludge");
DASSERT(wdata->isShowing);
if (awt_wm_doStateProtocolNet()) {
awt_wm_unshadeKludgeNet(wdata);
}
else if (awt_wm_doStateProtocolWin()) {
awt_wm_unshadeKludgeWin(wdata);
}
#ifdef DEBUG
else {
DTRACE_PRINTLN("WM: not a _NET or _WIN supporting WM");
}
#endif
XSync(XtDisplay(wdata->winData.shell), False);
}
void
awt_wm_init(void)
{
static Boolean inited = False;
if (inited) {
return;
}
awt_wm_initAtoms();
awt_wm_getRunningWM();
inited = True;
}
Boolean awt_wm_supportsAlwaysOnTop() {
return awt_wm_supportsLayersNet() || awt_wm_supportsLayersWin();
}