/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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.
*/
/**
* Ported from awt_wm.c, SCCS v1.11, author Valeriy Ushakov
* Author: Denis Mikhalkin
*/
/**
* Class incapsulating knowledge about window managers in general
* Descendants should provide some information about specific window manager.
*/
final class XWM
{
/* Good old ICCCM */
/* Currently we only care about max_v and max_h in _NET_WM_STATE */
/* Enlightenment */
/* KWin (KDE2) */
/* KWM (KDE 1.x) OBSOLETE??? */
/* OpenLook */
/* EWMH */
final static int
switch (WMID) {
case NO_WM:
return "NO WM";
case OTHER_WM:
return "Other WM";
case OPENLOOK_WM:
return "OPENLOOK";
case MOTIF_WM:
return "MWM";
case CDE_WM:
return "DTWM";
case ENLIGHTEN_WM:
return "Enlightenment";
case KDE2_WM:
return "KWM2";
case SAWFISH_WM:
return "Sawfish";
case ICE_WM:
return "IceWM";
case METACITY_WM:
return "Metacity";
case COMPIZ_WM:
return "Compiz";
case LG3D_WM:
return "LookingGlass";
case CWM_WM:
return "CWM";
case MUTTER_WM:
return "Mutter";
case UNDETERMINED_WM:
default:
return "Undetermined WM";
}
}
int WMID;
}
int getID() {
return WMID;
}
}
}
}
}
return insets;
}
if (g_net_protocol != null) {
} else {
return false;
}
}
static void initAtoms() {
final Object[][] atomInitList ={
{ XA_WM_STATE, "WM_STATE" },
{ 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" },
{ XA_MWM_HINTS, "_MOTIF_WM_HINTS" },
{ XA_NET_FRAME_EXTENTS, "_NET_FRAME_EXTENTS" },
{ XA_NET_REQUEST_FRAME_EXTENTS, "_NET_REQUEST_FRAME_EXTENTS" },
};
}
try {
if (status == 0) {
return;
}
((XAtom)(atomInitList[atom][0])).setValues(XToolkit.getDisplay(), names[atom], XAtom.getAtom(atoms + atomPtr));
}
} finally {
}
}
/*
* MUST BE CALLED UNDER AWTLOCK.
*
* If *any* window manager is running?
*
* According to ICCCM 2.0 section 4.3.
* WM will acquire ownership of a selection named WM_Sn, where n is
* the screen number.
*
* No selection owner, but, perhaps it is not ICCCM compliant WM
* Try selecting for SubstructureRedirect, that only one client
* can select for, and if the request fails, than some other WM is
* already running.
*
* We also treat eXcursion as NO_WM.
*/
private static boolean isNoWM() {
/*
* Quick checks for specific servers.
*/
/*
* 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.
*/
}
return true;
}
try {
/*
* Let's check an owner of WM_Sn selection for the default screen.
*/
final long default_screen_number =
long selection_owner =
+ " is " + selection_owner);
}
return false;
}
winmgr_running = false;
/*
* If no WM is running then our selection for SubstructureRedirect
* succeeded and needs to be undone (hey we are *not* a WM ;-).
*/
if (!winmgr_running) {
}
}
return !winmgr_running;
} finally {
}
}
/*
* Helper function for 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*... :-)
*/
if (!XA_ENLIGHTENMENT_COMMS.isInterned()) {
return 0;
}
try {
return 0;
}
{
return 0;
}
// Convert data to String, ASCII
// Parse WINID
try {
return winid;
} else {
return 0;
}
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
} finally {
}
}
/*
* Is Enlightenment WM running? Congruent to awt_wm_checkAnchor, but
* uses STRING property peculiar to Enlightenment.
*/
static boolean isEnlightenment() {
if (root_xref == 0) {
return false;
}
return false;
}
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 isCDE() {
if (!XA_DT_SM_WINDOW_INFO.isInterned()) {
return false;
}
false, XA_DT_SM_WINDOW_INFO);
try {
return false;
}
{
return false;
}
if (wmwin == 0) {
return false;
}
/* Now check that this window has _DT_SM_STATE_INFO (ignore contents) */
if (!XA_DT_SM_STATE_INFO.isInterned()) {
return false;
}
false, XA_DT_SM_STATE_INFO);
try {
return false;
}
{
return false;
}
return true;
} finally {
}
} finally {
}
}
/*
* 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 isMotif() {
return false;
}
XA_MOTIF_WM_INFO, 0,
false, XA_MOTIF_WM_INFO);
try {
return false;
}
{
return false;
}
if (wmwin != 0) {
if (XA_DT_WORKSPACE_CURRENT.isInterned()) {
/* Now check that this window has _DT_WORKSPACE_CURRENT */
return false;
}
return true;
} else {
// No DT_WORKSPACE, however in our tests MWM sometimes can be without desktop -
// and that is still MWM. So simply check for the validity of this window
// (through WM_STATE property).
0, 1, false,
try {
{
return true;
}
} finally {
}
}
}
} finally {
}
return false;
}
/*
* Is Sawfish running?
*/
static boolean isSawfish() {
return isNetWMName("Sawfish");
}
/*
* Is KDE2 (KWin) running?
*/
static boolean isKDE2() {
return isNetWMName("KWin");
}
static boolean isCompiz() {
return isNetWMName("compiz");
}
static boolean isLookingGlass() {
return isNetWMName("LG3D");
}
static boolean isCWM() {
return isNetWMName("CWM");
}
/*
* Is Metacity running?
*/
static boolean isMetacity() {
return isNetWMName("Metacity");
// || (
// XA_NET_SUPPORTING_WM_CHECK.
// getIntProperty(XToolkit.getDefaultRootWindow(), XA_NET_SUPPORTING_WM_CHECK.
// getIntProperty(XToolkit.getDefaultRootWindow(), XAtom.XA_CARDINAL)) == 0);
}
static boolean isMutter() {
return isNetWMName("Mutter");
}
static boolean isNonReparentingWM() {
return (XWM.getWMID() == XWM.COMPIZ_WM || XWM.getWMID() == XWM.LG3D_WM || XWM.getWMID() == XWM.CWM_WM);
}
/*
* 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 final 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'
};
static boolean prepareIsIceWM() {
/*
* Choose something innocuous: "AWT_ICEWM_TEST allWorkspaces 0".
* IceWM expects "class\0option\0arg\0" with zero bytes as delimiters.
*/
if (!XA_ICEWM_WINOPTHINT.isInterned()) {
return false;
}
try {
return false;
}
return true;
} finally {
}
}
/*
* Is IceWM running?
*
* Note well: Only call this if awt_wm_prepareIsIceWM succeeded, or a
* false positive will be reported.
*/
static boolean isIceWM() {
if (!XA_ICEWM_WINOPTHINT.isInterned()) {
return false;
}
true, XA_ICEWM_WINOPTHINT);
try {
} finally {
}
}
/*
* 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 isOpenLook() {
if (!XA_SUN_WM_PROTOCOLS.isInterned()) {
return false;
}
}
/*
* Temporary error handler that checks if selecting for
* SubstructureRedirect failed.
*/
private static boolean winmgr_running = false;
{
winmgr_running = true;
return 0;
}
}
};
/*
* Make an educated guess about running window manager.
* XXX: ideally, we should detect wm restart.
*/
}
return wm;
}
static int getWMID() {
}
/*
* Ideally, we should support cases when a different WM is started
* during a Java app lifetime.
*/
return awt_wmgr;
}
try {
if (isNoWM()) {
return awt_wmgr;
}
// Initialize _NET protocol - used to detect Window Manager.
// Later, WM will initialize its own version of protocol
}
/* actual check for IceWM to follow below */
/*
* Ok, some WM is out there. Check which one by testing for
* "distinguishing" atoms.
*/
if (isEnlightenment()) {
} else if (isMetacity()) {
} else if (isMutter()) {
} else if (isSawfish()) {
} else if (isKDE2()) {
} else if (isCompiz()) {
} else if (isLookingGlass()) {
} else if (isCWM()) {
}
/*
* We don't check for legacy WM when we already know that WM
* supports WIN or _NET wm spec.
*/
else if (l_net_protocol.active()) {
}
/*
* Check for legacy WMs.
*/
else if (isCDE()) { /* XXX: must come before isMotif */
} else if (isMotif()) {
} else if (isOpenLook()) {
} else {
}
return awt_wmgr;
} finally {
}
}
/*****************************************************************************\
*
* Size and decoration hints ...
*
\*****************************************************************************/
/*
* Remove size hints specified by the mask.
* XXX: Why do we need this in the first place???
*/
try {
return;
}
if (insLog.isLoggable(PlatformLogger.FINER)) insLog.finer("Setting hints, flags " + XlibWrapper.hintsToString(hints.get_flags()));
} finally {
}
}
/*
* 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.
*/
return decorations;
}
d &= ~decorations;
return d;
}
/*
* If MWM_FUNC_ALL bit is set, then the rest of the bit-mask is taken
* to be subtracted from the functions. Normalize function spec
* so that we can map motif func to something else bit-by-bit in the
* rest of the code.
*/
return functions;
}
int f = MWMConstants.MWM_FUNC_RESIZE |
f &= ~functions;
return f;
}
/*
* Infer OL properties from MWM decorations.
* Use _OL_DECOR_DEL(ATOM[]) to remove unwanted ones.
*/
return;
}
if (insLog.isLoggable(PlatformLogger.FINER)) insLog.finer("Setting OL_DECOR to " + Integer.toBinaryString(decorations));
}
}
{
}
} else {
}
}
/*
* Set MWM decorations. Set MWM functions depending on resizability.
*/
/* Apparently some WMs don't implement MWM_*_ALL semantic correctly */
{
}
{
}
}
/*
* 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
* 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.
*/
// Don't remap EmbeddedFrame,
// e.g. for TrayIcon it causes problems.
return !window.isEmbedded();
}
/*
* Set decoration hints on the shell to wdata->decor adjusted
* appropriately if not resizable.
*/
if (!resizable) {
} else {
}
}
/* Some WMs need remap to redecorate the window */
/*
* 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).
*/
window.xSetVisible(false);
window.xSetVisible(true);
}
}
/*
* Make specified shell resizable.
*/
try {
/* REMINDER: will need to revisit when setExtendedStateBounds is added */
//Fix for 4320050: Minimum size for java.awt.Frame is not being enforced.
//We need to update frame's minimum size, not to reset it
/* Restore decorations */
} finally {
}
}
/*
* Make specified shell non-resizable.
* If justChangeSize is false, update decorations as well.
* @param shellBounds bounds of the shell window
*/
static void setShellNotResizable(XDecoratedPeer window, WindowDimensions newDimensions, Rectangle shellBounds,
boolean justChangeSize)
{
if (insLog.isLoggable(PlatformLogger.FINE)) insLog.fine("Setting non-resizable shell " + window + ", dimensions " + newDimensions +
try {
if (!shellBounds.isEmpty()) {
}
if (!justChangeSize) { /* update decorations */
}
} finally {
}
}
/*****************************************************************************\
* Protocols support
*/
/**
* Returns all protocols supporting given protocol interface
*/
return res;
} else {
return new LinkedList<T>();
}
}
}
boolean supportsDynamicLayout() {
switch (wm) {
case XWM.ENLIGHTEN_WM:
case XWM.SAWFISH_WM:
case XWM.METACITY_WM:
return true;
case XWM.OPENLOOK_WM:
return false;
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.
*
*/
switch (state) {
case Frame.MAXIMIZED_VERT:
case Frame.MAXIMIZED_HORIZ:
/*
* unidirectional maximization.
*/
if (getWMID() == METACITY_WM) {
/* "This is a deliberate policy decision." -hp */
return false;
}
/* FALLTROUGH */
case Frame.MAXIMIZED_BOTH:
return true;
}
}
default:
return false;
}
}
/*****************************************************************************\
*
* Reading state from different protocols
*
\*****************************************************************************/
int state = 0;
}
if (state != 0) {
return state;
} else {
}
}
/*****************************************************************************\
*
* Notice window state change when WM changes a property on the window ...
*
\*****************************************************************************/
/*
* Check if property change is a window state protocol message.
*/
return false;
}
return false;
} else {
}
boolean is_state_change = false;
is_state_change = true;
}
}
return is_state_change;
}
/*
* Returns current state (including extended) of a given window.
*/
int res = 0;
} else {
}
return res;
}
/*****************************************************************************\
*
*
\*****************************************************************************/
/**
* Moves window to the specified layer, layer is one of the constants defined
* in XLayerProtocol
*/
}
}
}
break;
}
}
/*
* Purge KWM bits.
* Not really tested with KWM, only with WindowMaker.
*/
try {
}
finally {
}
}
}
/*
* 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.
*/
}
}
static boolean inited = false;
static void init() {
if (inited) {
return;
}
initAtoms();
getWM();
inited = true;
}
void initializeProtocols() {
if (net_protocol != null) {
if (!net_protocol.active()) {
net_protocol = null;
} else {
if (net_protocol.doStateProtocol()) {
}
if (net_protocol.doLayerProtocol()) {
}
}
}
if (win.doStateProtocol()) {
}
if (win.doLayerProtocol()) {
}
}
}
}
switch (WMID) {
case ENLIGHTEN_WM:
break;
case CDE_WM:
break;
case NO_WM:
case LG3D_WM:
res = zeroInsets;
break;
case MOTIF_WM:
case OPENLOOK_WM:
default:
res = defaultInsets;
}
}
return res;
}
/*
* 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().
*
* Starting with 1.5 we have introduced an Environment variable
* _JAVA_AWT_WM_STATIC_GRAVITY that can be set to indicate to Java
* explicitly that the WM has this behaviour, example is FVWM.
*/
static boolean configureGravityBuggy() {
if (awtWMStaticGravity == -1) {
}
if (awtWMStaticGravity == 1) {
return true;
}
switch(getWMID()) {
/*
* See bug #228981 at IceWM's SourceForge pages.
* Latest stable version 1.0.8-6 still has this problem.
*/
/**
* Version 1.2.2 doesn't have this problem
*/
// Detect IceWM version
if (g_net_protocol != null) {
try {
}
} catch (Exception e) {
return true;
}
}
return true;
case XWM.ENLIGHTEN_WM:
/* At least E16 is buggy. */
return true;
default:
return false;
}
}
/*
* @return if WM implements the insets property - returns insets with values
* specified in that property, null otherwise.
*/
return null;
}
return insets;
}
}
switch(getWMID()) {
case XWM.ENLIGHTEN_WM:
default:
return null;
}
}
/**
* Helper function reads property of type CARDINAL[4] = { left, right, top, bottom }
* and converts it to Insets object.
*/
return null;
}
try {
{
return null;
} else {
}
} finally {
}
}
/**
* Asks WM to fill Frame Extents (insets) for the window.
*/
return;
}
try {
false,
}
false,
}
// XXX: should we wait for response? XIfEvent() would be useful here :)
} finally {
}
}
/* syncTopLEvelPos() is necessary to insure that the window manager has in
* fact moved us to our final position relative to the reParented WM window.
* We have noted a timing window which our shell has not been moved so we
* screw up the insets thinking they are 0,0. Wait (for a limited period of
* time to let the WM hava a chance to move us.
* @param window window ID of the shell, assuming it is the window
* which will NOT have zero coordinates after the complete
* reparenting
*/
int tries = 0;
try {
do {
return true;
}
tries++;
} while (tries < 50);
}
finally {
}
return false;
}
/*
* Unfortunately the concept of "insets" borrowed to AWT
* from Win32 is *absolutely*, *unbelievably* foreign to
* X11. Few WMs provide the size of frame decor
* (i.e. insets) in a property they set on the client
* window, so we check if we can get away with just
* peeking at it. [Future versions of wm-spec might add a
* standardized hint for this].
*
* Otherwise we do some special casing. Actually the
* fallback code ("default" case) seems to cover most of
* perhaps?).
*
* Fallback code tries to account for the two most common cases:
*
* . single reparenting
* parent window is the WM frame
* [twm, olwm, sawfish]
*
* . double reparenting
* parent is a lining exactly the size of the client
* grandpa is the WM frame
* [mwm, e!, kwin, fvwm2 ... ]
*/
try {
/* should've been done in awt_wm_getInsetsFromProp */
case XWM.ENLIGHTEN_WM: {
/* enlightenment does double reparenting */
/*
* Now get the actual dimensions of the parent window
* resolve the difference. We can't rely on the left
* to be equal to right or bottom... Enlightment
* breaks that assumption.
*/
break;
}
/* these are double reparenting too */
} else {
return null;
}
break;
}
case XWM.SAWFISH_WM:
case XWM.OPENLOOK_WM: {
/* single reparenting */
break;
}
default: { /* this is very similar to the E! case above */
break;
}
/*
* Check for double-reparenting WM.
*
* If the parent is exactly the same size as the
* top-level assume taht it's the "lining" window and
* that the grandparent is the actual frame (NB: we
* have already handled undecorated windows).
*
* XXX: what about timing issues that syncTopLevelPos
* is supposed to work around?
*/
{
// This is not double-reparenting in a
// general sense - we simply don't have
// correct insets - return null to try to
// get insets later
return null;
} else {
}
}
/*
* XXX: To be absolutely correct, we'd need to take
* parent's border-width into account too, but the
* rest of the code is happily unaware about border
* being, just ignore it.
*/
break;
} /* default */
} /* switch (runningWM) */
} finally {
}
}
}
return correctWM;
}
boolean isDesktopWindow( long w ) {
if (g_net_protocol != null) {
} else {
return false;
}
}
return g_net_protocol;
}
/**
* Sets _NET_WN_ICON property on the window using the arrays of
* raster-data for icons. If icons is null, removes _NET_WM_ICON
* property.
* This method invokes XNETProtocol.setWMIcon() for WMs that
* support NET protocol.
*
* @return true if hint was modified successfully, false otherwise
*/
}
return false;
}
}