0001: /*
0002: * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: /**
0027: * Ported from awt_wm.c, SCCS v1.11, author Valeriy Ushakov
0028: * Author: Denis Mikhalkin
0029: */package sun.awt.X11;
0030:
0031: import sun.misc.Unsafe;
0032: import java.util.regex.*;
0033: import java.awt.Frame;
0034: import java.awt.Rectangle;
0035: import java.util.*;
0036: import java.util.logging.Level;
0037: import java.util.logging.LogManager;
0038: import java.util.logging.Logger;
0039: import java.awt.Insets;
0040:
0041: /**
0042: * Class incapsulating knowledge about window managers in general
0043: * Descendants should provide some information about specific window manager.
0044: */
0045: class XWM implements MWMConstants, XUtilConstants {
0046:
0047: private final static Logger log = Logger
0048: .getLogger("sun.awt.X11.XWM");
0049: private final static Logger insLog = Logger
0050: .getLogger("sun.awt.X11.insets.XWM");
0051: private final static Logger stateLog = Logger
0052: .getLogger("sun.awt.X11.states.XWM");
0053:
0054: static final XAtom XA_MWM_HINTS = new XAtom();
0055:
0056: private static Unsafe unsafe = XlibWrapper.unsafe;
0057:
0058: /* Good old ICCCM */
0059: static XAtom XA_WM_STATE = new XAtom();
0060:
0061: XAtom XA_UTF8_STRING = XAtom.get("UTF8_STRING"); /* like STRING but encoding is UTF-8 */
0062:
0063: /* Currently we only care about max_v and max_h in _NET_WM_STATE */
0064: final static int AWT_NET_N_KNOWN_STATES = 2;
0065:
0066: /* Enlightenment */
0067: final static XAtom XA_E_FRAME_SIZE = new XAtom();
0068:
0069: /* KWin (KDE2) */
0070: final static XAtom XA_KDE_NET_WM_FRAME_STRUT = new XAtom();
0071:
0072: /* KWM (KDE 1.x) OBSOLETE??? */
0073: final static XAtom XA_KWM_WIN_ICONIFIED = new XAtom();
0074: final static XAtom XA_KWM_WIN_MAXIMIZED = new XAtom();
0075:
0076: /* OpenLook */
0077: final static XAtom XA_OL_DECOR_DEL = new XAtom();
0078: final static XAtom XA_OL_DECOR_HEADER = new XAtom();
0079: final static XAtom XA_OL_DECOR_RESIZE = new XAtom();
0080: final static XAtom XA_OL_DECOR_PIN = new XAtom();
0081: final static XAtom XA_OL_DECOR_CLOSE = new XAtom();
0082:
0083: /* EWMH */
0084: final static XAtom XA_NET_FRAME_EXTENTS = new XAtom();
0085: final static XAtom XA_NET_REQUEST_FRAME_EXTENTS = new XAtom();
0086:
0087: final static int UNDETERMINED_WM = 1, NO_WM = 2, OTHER_WM = 3,
0088: OPENLOOK_WM = 4, MOTIF_WM = 5, CDE_WM = 6,
0089: ENLIGHTEN_WM = 7, KDE2_WM = 8, SAWFISH_WM = 9, ICE_WM = 10,
0090: METACITY_WM = 11, COMPIZ_WM = 12, LG3D_WM = 13;
0091:
0092: public String toString() {
0093: switch (WMID) {
0094: case NO_WM:
0095: return "NO WM";
0096: case OTHER_WM:
0097: return "Other WM";
0098: case OPENLOOK_WM:
0099: return "OPENLOOK";
0100: case MOTIF_WM:
0101: return "MWM";
0102: case CDE_WM:
0103: return "DTWM";
0104: case ENLIGHTEN_WM:
0105: return "Enlightenment";
0106: case KDE2_WM:
0107: return "KWM2";
0108: case SAWFISH_WM:
0109: return "Sawfish";
0110: case ICE_WM:
0111: return "IceWM";
0112: case METACITY_WM:
0113: return "Metacity";
0114: case COMPIZ_WM:
0115: return "Compiz";
0116: case LG3D_WM:
0117: return "LookingGlass";
0118: case UNDETERMINED_WM:
0119: default:
0120: return "Undetermined WM";
0121: }
0122: }
0123:
0124: int WMID;
0125: static final Insets zeroInsets = new Insets(0, 0, 0, 0);
0126: static final Insets defaultInsets = new Insets(25, 5, 5, 5);
0127:
0128: XWM(int WMID) {
0129: this .WMID = WMID;
0130: initializeProtocols();
0131: if (log.isLoggable(Level.FINE))
0132: log.fine("Window manager: " + toString());
0133: }
0134:
0135: int getID() {
0136: return WMID;
0137: }
0138:
0139: static Insets normalize(Insets insets) {
0140: if (insets.top > 64 || insets.top < 0) {
0141: insets.top = 28;
0142: }
0143: if (insets.left > 32 || insets.left < 0) {
0144: insets.left = 6;
0145: }
0146: if (insets.right > 32 || insets.right < 0) {
0147: insets.right = 6;
0148: }
0149: if (insets.bottom > 32 || insets.bottom < 0) {
0150: insets.bottom = 6;
0151: }
0152: return insets;
0153: }
0154:
0155: static XNETProtocol g_net_protocol = null;
0156: static XWINProtocol g_win_protocol = null;
0157:
0158: static boolean isNetWMName(String name) {
0159: if (g_net_protocol != null) {
0160: return g_net_protocol.isWMName(name);
0161: } else {
0162: return false;
0163: }
0164: }
0165:
0166: static void initAtoms() {
0167: final Object[][] atomInitList = {
0168: { XA_WM_STATE, "WM_STATE" },
0169:
0170: { XA_KDE_NET_WM_FRAME_STRUT, "_KDE_NET_WM_FRAME_STRUT" },
0171:
0172: { XA_E_FRAME_SIZE, "_E_FRAME_SIZE" },
0173:
0174: { XA_KWM_WIN_ICONIFIED, "KWM_WIN_ICONIFIED" },
0175: { XA_KWM_WIN_MAXIMIZED, "KWM_WIN_MAXIMIZED" },
0176:
0177: { XA_OL_DECOR_DEL, "_OL_DECOR_DEL" },
0178: { XA_OL_DECOR_HEADER, "_OL_DECOR_HEADER" },
0179: { XA_OL_DECOR_RESIZE, "_OL_DECOR_RESIZE" },
0180: { XA_OL_DECOR_PIN, "_OL_DECOR_PIN" },
0181: { XA_OL_DECOR_CLOSE, "_OL_DECOR_CLOSE" },
0182: { XA_MWM_HINTS, "_MOTIF_WM_HINTS" },
0183: { XA_NET_FRAME_EXTENTS, "_NET_FRAME_EXTENTS" },
0184: { XA_NET_REQUEST_FRAME_EXTENTS,
0185: "_NET_REQUEST_FRAME_EXTENTS" }, };
0186:
0187: String[] names = new String[atomInitList.length];
0188: for (int index = 0; index < names.length; index++) {
0189: names[index] = (String) atomInitList[index][1];
0190: }
0191:
0192: int atomSize = XAtom.getAtomSize();
0193: long atoms = unsafe.allocateMemory(names.length * atomSize);
0194: XToolkit.awtLock();
0195: try {
0196: int status = XlibWrapper.XInternAtoms(
0197: XToolkit.getDisplay(), names, false, atoms);
0198: if (status == 0) {
0199: return;
0200: }
0201: for (int atom = 0, atomPtr = 0; atom < names.length; atom++, atomPtr += atomSize) {
0202: ((XAtom) (atomInitList[atom][0])).setValues(XToolkit
0203: .getDisplay(), names[atom], XAtom.getAtom(atoms
0204: + atomPtr));
0205: }
0206: } finally {
0207: XToolkit.awtUnlock();
0208: unsafe.freeMemory(atoms);
0209: }
0210: }
0211:
0212: /*
0213: * MUST BE CALLED UNDER AWTLOCK.
0214: *
0215: * If *any* window manager is running?
0216: *
0217: * According to ICCCM 2.0 section 4.3.
0218: * WM will acquire ownership of a selection named WM_Sn, where n is
0219: * the screen number.
0220: *
0221: * No selection owner, but, perhaps it is not ICCCM compliant WM
0222: * (e.g. CDE/Sawfish).
0223: * Try selecting for SubstructureRedirect, that only one client
0224: * can select for, and if the request fails, than some other WM is
0225: * already running.
0226: *
0227: * We also treat eXcursion as NO_WM.
0228: */
0229: private static boolean isNoWM() {
0230: /*
0231: * Quick checks for specific servers.
0232: */
0233: String vendor_string = XlibWrapper.ServerVendor(XToolkit
0234: .getDisplay());
0235: if (vendor_string.indexOf("eXcursion") != -1) {
0236: /*
0237: * Use NO_WM since in all other aspects eXcursion is like not
0238: * having a window manager running. I.e. it does not reparent
0239: * top level shells.
0240: */
0241: if (insLog.isLoggable(Level.FINE)) {
0242: insLog.finer("eXcursion means NO_WM");
0243: }
0244: return true;
0245: }
0246:
0247: XSetWindowAttributes substruct = new XSetWindowAttributes();
0248: try {
0249: /*
0250: * Let's check an owner of WM_Sn selection for the default screen.
0251: */
0252: final long default_screen_number = XlibWrapper
0253: .DefaultScreen(XToolkit.getDisplay());
0254: final String selection_name = "WM_S"
0255: + default_screen_number;
0256:
0257: long selection_owner = XlibWrapper.XGetSelectionOwner(
0258: XToolkit.getDisplay(), XAtom.get(selection_name)
0259: .getAtom());
0260: if (insLog.isLoggable(Level.FINE)) {
0261: insLog.finer("selection owner of " + selection_name
0262: + " is " + selection_owner);
0263: }
0264:
0265: if (selection_owner != XConstants.None) {
0266: return false;
0267: }
0268:
0269: winmgr_running = false;
0270: substruct
0271: .set_event_mask(XlibWrapper.SubstructureRedirectMask);
0272:
0273: XToolkit.WITH_XERROR_HANDLER(DetectWMHandler);
0274: XlibWrapper.XChangeWindowAttributes(XToolkit.getDisplay(),
0275: XToolkit.getDefaultRootWindow(),
0276: XlibWrapper.CWEventMask, substruct.pData);
0277: XToolkit.RESTORE_XERROR_HANDLER();
0278:
0279: /*
0280: * If no WM is running then our selection for SubstructureRedirect
0281: * succeeded and needs to be undone (hey we are *not* a WM ;-).
0282: */
0283: if (!winmgr_running) {
0284: substruct.set_event_mask(0);
0285: XlibWrapper.XChangeWindowAttributes(XToolkit
0286: .getDisplay(), XToolkit.getDefaultRootWindow(),
0287: XlibWrapper.CWEventMask, substruct.pData);
0288: if (insLog.isLoggable(Level.FINE)) {
0289: insLog
0290: .finer("It looks like there is no WM thus NO_WM");
0291: }
0292: }
0293:
0294: return !winmgr_running;
0295: } finally {
0296: substruct.dispose();
0297: }
0298: }
0299:
0300: static XAtom XA_ENLIGHTENMENT_COMMS = new XAtom(
0301: "ENLIGHTENMENT_COMMS", false);
0302:
0303: /*
0304: * Helper function for isEnlightenment().
0305: * Enlightenment uses STRING property for its comms window id. Gaaa!
0306: * The property is ENLIGHTENMENT_COMMS, STRING/8 and the string format
0307: * is "WINID %8x". Gee, I haven't been using scanf for *ages*... :-)
0308: */
0309: static long getECommsWindowIDProperty(long window) {
0310:
0311: if (!XA_ENLIGHTENMENT_COMMS.isInterned()) {
0312: return 0;
0313: }
0314:
0315: WindowPropertyGetter getter = new WindowPropertyGetter(window,
0316: XA_ENLIGHTENMENT_COMMS, 0, 14, false, XAtom.XA_STRING);
0317: try {
0318: int status = getter
0319: .execute(XToolkit.IgnoreBadWindowHandler);
0320: if (status != XlibWrapper.Success || getter.getData() == 0) {
0321: return 0;
0322: }
0323:
0324: if (getter.getActualType() != XAtom.XA_STRING
0325: || getter.getActualFormat() != 8
0326: || getter.getNumberOfItems() != 14
0327: || getter.getBytesAfter() != 0) {
0328: return 0;
0329: }
0330:
0331: // Convert data to String, ASCII
0332: byte[] bytes = XlibWrapper.getStringBytes(getter.getData());
0333: String id = new String(bytes);
0334:
0335: log.finer("ENLIGHTENMENT_COMMS is " + id);
0336:
0337: // Parse WINID
0338: Pattern winIdPat = Pattern
0339: .compile("WINID\\s+(\\p{XDigit}{0,8})");
0340: try {
0341: Matcher match = winIdPat.matcher(id);
0342: if (match.matches()) {
0343: log.finest("Match group count: "
0344: + match.groupCount());
0345: String longId = match.group(1);
0346: log.finest("Match group 1 " + longId);
0347: long winid = Long.parseLong(longId, 16);
0348: log.finer("Enlightenment communication window "
0349: + winid);
0350: return winid;
0351: } else {
0352: log.finer("ENLIGHTENMENT_COMMS has wrong format");
0353: return 0;
0354: }
0355: } catch (Exception e) {
0356: if (log.isLoggable(Level.FINER)) {
0357: e.printStackTrace();
0358: }
0359: return 0;
0360: }
0361: } finally {
0362: getter.dispose();
0363: }
0364: }
0365:
0366: /*
0367: * Is Enlightenment WM running? Congruent to awt_wm_checkAnchor, but
0368: * uses STRING property peculiar to Enlightenment.
0369: */
0370: static boolean isEnlightenment() {
0371:
0372: long root_xref = getECommsWindowIDProperty(XToolkit
0373: .getDefaultRootWindow());
0374: if (root_xref == 0) {
0375: return false;
0376: }
0377:
0378: long self_xref = getECommsWindowIDProperty(root_xref);
0379: if (self_xref != root_xref) {
0380: return false;
0381: }
0382:
0383: return true;
0384: }
0385:
0386: /*
0387: * Is CDE running?
0388: *
0389: * XXX: This is hairy... CDE is MWM as well. It seems we simply test
0390: * for default setup and will be bitten if user changes things...
0391: *
0392: * Check for _DT_SM_WINDOW_INFO(_DT_SM_WINDOW_INFO) on root. Take the
0393: * second element of the property and check for presence of
0394: * _DT_SM_STATE_INFO(_DT_SM_STATE_INFO) on that window.
0395: *
0396: * XXX: Any header that defines this structures???
0397: */
0398: static final XAtom XA_DT_SM_WINDOW_INFO = new XAtom(
0399: "_DT_SM_WINDOW_INFO", false);
0400: static final XAtom XA_DT_SM_STATE_INFO = new XAtom(
0401: "_DT_SM_STATE_INFO", false);
0402:
0403: static boolean isCDE() {
0404:
0405: if (!XA_DT_SM_WINDOW_INFO.isInterned()) {
0406: log.log(Level.FINER, "{0} is not interned",
0407: new Object[] { XA_DT_SM_WINDOW_INFO });
0408: return false;
0409: }
0410:
0411: WindowPropertyGetter getter = new WindowPropertyGetter(XToolkit
0412: .getDefaultRootWindow(), XA_DT_SM_WINDOW_INFO, 0, 2,
0413: false, XA_DT_SM_WINDOW_INFO);
0414: try {
0415: int status = getter.execute();
0416: if (status != XlibWrapper.Success || getter.getData() == 0) {
0417: log
0418: .finer("Getting of _DT_SM_WINDOW_INFO is not successfull");
0419: return false;
0420: }
0421: if (getter.getActualType() != XA_DT_SM_WINDOW_INFO
0422: .getAtom()
0423: || getter.getActualFormat() != 32
0424: || getter.getNumberOfItems() != 2
0425: || getter.getBytesAfter() != 0) {
0426: log.finer("Wrong format of _DT_SM_WINDOW_INFO");
0427: return false;
0428: }
0429:
0430: long wmwin = Native.getWindow(getter.getData(), 1); //unsafe.getInt(getter.getData()+4);
0431:
0432: if (wmwin == 0) {
0433: log
0434: .fine("WARNING: DT_SM_WINDOW_INFO exists but returns zero windows");
0435: return false;
0436: }
0437:
0438: /* Now check that this window has _DT_SM_STATE_INFO (ignore contents) */
0439: if (!XA_DT_SM_STATE_INFO.isInterned()) {
0440: log.log(Level.FINER, "{0} is not interned",
0441: new Object[] { XA_DT_SM_STATE_INFO });
0442: return false;
0443: }
0444: WindowPropertyGetter getter2 = new WindowPropertyGetter(
0445: wmwin, XA_DT_SM_STATE_INFO, 0, 1, false,
0446: XA_DT_SM_STATE_INFO);
0447: try {
0448: status = getter2
0449: .execute(XToolkit.IgnoreBadWindowHandler);
0450:
0451: if (status != XlibWrapper.Success
0452: || getter2.getData() == 0) {
0453: log
0454: .finer("Getting of _DT_SM_STATE_INFO is not successfull");
0455: return false;
0456: }
0457: if (getter2.getActualType() != XA_DT_SM_STATE_INFO
0458: .getAtom()
0459: || getter2.getActualFormat() != 32) {
0460: log.finer("Wrong format of _DT_SM_STATE_INFO");
0461: return false;
0462: }
0463:
0464: return true;
0465: } finally {
0466: getter2.dispose();
0467: }
0468: } finally {
0469: getter.dispose();
0470: }
0471: }
0472:
0473: /*
0474: * Is MWM running? (Note that CDE will test positive as well).
0475: *
0476: * Check for _MOTIF_WM_INFO(_MOTIF_WM_INFO) on root. Take the
0477: * second element of the property and check for presence of
0478: * _DT_SM_STATE_INFO(_DT_SM_STATE_INFO) on that window.
0479: */
0480: static final XAtom XA_MOTIF_WM_INFO = new XAtom("_MOTIF_WM_INFO",
0481: false);
0482: static final XAtom XA_DT_WORKSPACE_CURRENT = new XAtom(
0483: "_DT_WORKSPACE_CURRENT", false);
0484:
0485: static boolean isMotif() {
0486:
0487: if (!(XA_MOTIF_WM_INFO.isInterned()/* && XA_DT_WORKSPACE_CURRENT.isInterned()*/)) {
0488: return false;
0489: }
0490:
0491: WindowPropertyGetter getter = new WindowPropertyGetter(XToolkit
0492: .getDefaultRootWindow(), XA_MOTIF_WM_INFO, 0,
0493: PROP_MOTIF_WM_INFO_ELEMENTS, false, XA_MOTIF_WM_INFO);
0494: try {
0495: int status = getter.execute();
0496:
0497: if (status != XlibWrapper.Success || getter.getData() == 0) {
0498: return false;
0499: }
0500:
0501: if (getter.getActualType() != XA_MOTIF_WM_INFO.getAtom()
0502: || getter.getActualFormat() != 32
0503: || getter.getNumberOfItems() != PROP_MOTIF_WM_INFO_ELEMENTS
0504: || getter.getBytesAfter() != 0) {
0505: return false;
0506: }
0507:
0508: long wmwin = Native.getLong(getter.getData(), 1);
0509: if (wmwin != 0) {
0510: if (XA_DT_WORKSPACE_CURRENT.isInterned()) {
0511: /* Now check that this window has _DT_WORKSPACE_CURRENT */
0512: XAtom[] curws = XA_DT_WORKSPACE_CURRENT
0513: .getAtomListProperty(wmwin);
0514: if (curws.length == 0) {
0515: return false;
0516: }
0517: return true;
0518: } else {
0519: // No DT_WORKSPACE, however in our tests MWM sometimes can be without desktop -
0520: // and that is still MWM. So simply check for the validity of this window
0521: // (through WM_STATE property).
0522: WindowPropertyGetter state_getter = new WindowPropertyGetter(
0523: wmwin, XA_WM_STATE, 0, 1, false,
0524: XA_WM_STATE);
0525: try {
0526: if (state_getter.execute() == XlibWrapper.Success
0527: && state_getter.getData() != 0
0528: && state_getter.getActualType() == XA_WM_STATE
0529: .getAtom()) {
0530: return true;
0531: }
0532: } finally {
0533: state_getter.dispose();
0534: }
0535: }
0536: }
0537: } finally {
0538: getter.dispose();
0539: }
0540: return false;
0541: }
0542:
0543: /*
0544: * Is Sawfish running?
0545: */
0546: static boolean isSawfish() {
0547: return isNetWMName("Sawfish");
0548: }
0549:
0550: /*
0551: * Is KDE2 (KWin) running?
0552: */
0553: static boolean isKDE2() {
0554: return isNetWMName("KWin");
0555: }
0556:
0557: static boolean isCompiz() {
0558: return isNetWMName("compiz");
0559: }
0560:
0561: static boolean isLookingGlass() {
0562: return isNetWMName("LG3D");
0563: }
0564:
0565: /*
0566: * Is Metacity running?
0567: */
0568: static boolean isMetacity() {
0569: return isNetWMName("Metacity");
0570: // || (
0571: // XA_NET_SUPPORTING_WM_CHECK.
0572: // getIntProperty(XToolkit.getDefaultRootWindow(), XA_NET_SUPPORTING_WM_CHECK.
0573: // getIntProperty(XToolkit.getDefaultRootWindow(), XAtom.XA_CARDINAL)) == 0);
0574: }
0575:
0576: static boolean isNonReparentingWM() {
0577: return (XWM.getWMID() == XWM.COMPIZ_WM || XWM.getWMID() == XWM.LG3D_WM);
0578: }
0579:
0580: /*
0581: * Temporary error handler that ensures that we know if
0582: * XChangeProperty succeeded or not.
0583: */
0584: static XToolkit.XErrorHandler VerifyChangePropertyHandler = new XToolkit.XErrorHandler() {
0585: public int handleError(long display, XErrorEvent err) {
0586: XToolkit.XERROR_SAVE(err);
0587: if (err.get_request_code() == XlibWrapper.X_ChangeProperty) {
0588: return 0;
0589: } else {
0590: return XToolkit.SAVED_ERROR_HANDLER(display, err);
0591: }
0592: }
0593: };
0594:
0595: /*
0596: * Prepare IceWM check.
0597: *
0598: * The only way to detect IceWM, seems to be by setting
0599: * _ICEWM_WINOPTHINT(_ICEWM_WINOPTHINT/8) on root and checking if it
0600: * was immediately deleted by IceWM.
0601: *
0602: * But messing with PropertyNotify here is way too much trouble, so
0603: * approximate the check by setting the property in this function and
0604: * checking if it still exists later on.
0605: *
0606: * Gaa, dirty dances...
0607: */
0608: static final XAtom XA_ICEWM_WINOPTHINT = new XAtom(
0609: "_ICEWM_WINOPTHINT", false);
0610: static final char opt[] = { 'A', 'W', 'T', '_', 'I', 'C', 'E', 'W',
0611: 'M', '_', 'T', 'E', 'S', 'T', '\0', 'a', 'l', 'l', 'W',
0612: 'o', 'r', 'k', 's', 'p', 'a', 'c', 'e', 's', '\0', '0',
0613: '\0' };
0614:
0615: static boolean prepareIsIceWM() {
0616: /*
0617: * Choose something innocuous: "AWT_ICEWM_TEST allWorkspaces 0".
0618: * IceWM expects "class\0option\0arg\0" with zero bytes as delimiters.
0619: */
0620:
0621: if (!XA_ICEWM_WINOPTHINT.isInterned()) {
0622: log.log(Level.FINER, "{0} is not interned",
0623: new Object[] { XA_ICEWM_WINOPTHINT });
0624: return false;
0625: }
0626:
0627: XToolkit.awtLock();
0628: try {
0629: XToolkit.WITH_XERROR_HANDLER(VerifyChangePropertyHandler);
0630: XlibWrapper.XChangePropertyS(XToolkit.getDisplay(),
0631: XToolkit.getDefaultRootWindow(),
0632: XA_ICEWM_WINOPTHINT.getAtom(), XA_ICEWM_WINOPTHINT
0633: .getAtom(), 8, XlibWrapper.PropModeReplace,
0634: new String(opt));
0635: XToolkit.RESTORE_XERROR_HANDLER();
0636:
0637: if (XToolkit.saved_error != null
0638: && XToolkit.saved_error.get_error_code() != XlibWrapper.Success) {
0639: log.finer("Erorr getting XA_ICEWM_WINOPTHINT property");
0640: return false;
0641: }
0642: log.finer("Prepared for IceWM detection");
0643: return true;
0644: } finally {
0645: XToolkit.awtUnlock();
0646: }
0647: }
0648:
0649: /*
0650: * Is IceWM running?
0651: *
0652: * Note well: Only call this if awt_wm_prepareIsIceWM succeeded, or a
0653: * false positive will be reported.
0654: */
0655: static boolean isIceWM() {
0656: if (!XA_ICEWM_WINOPTHINT.isInterned()) {
0657: log.log(Level.FINER, "{0} is not interned",
0658: new Object[] { XA_ICEWM_WINOPTHINT });
0659: return false;
0660: }
0661:
0662: WindowPropertyGetter getter = new WindowPropertyGetter(XToolkit
0663: .getDefaultRootWindow(), XA_ICEWM_WINOPTHINT, 0,
0664: 0xFFFF, true, XA_ICEWM_WINOPTHINT);
0665: try {
0666: int status = getter.execute();
0667: boolean res = (status == XlibWrapper.Success && getter
0668: .getActualType() != 0);
0669: log.finer("Status getting XA_ICEWM_WINOPTHINT: " + !res);
0670: return !res || isNetWMName("IceWM");
0671: } finally {
0672: getter.dispose();
0673: }
0674: }
0675:
0676: /*
0677: * Is OpenLook WM running?
0678: *
0679: * This one is pretty lame, but the only property peculiar to OLWM is
0680: * _SUN_WM_PROTOCOLS(ATOM[]). Fortunately, olwm deletes it on exit.
0681: */
0682: static final XAtom XA_SUN_WM_PROTOCOLS = new XAtom(
0683: "_SUN_WM_PROTOCOLS", false);
0684:
0685: static boolean isOpenLook() {
0686: if (!XA_SUN_WM_PROTOCOLS.isInterned()) {
0687: return false;
0688: }
0689:
0690: XAtom[] list = XA_SUN_WM_PROTOCOLS.getAtomListProperty(XToolkit
0691: .getDefaultRootWindow());
0692: return (list.length != 0);
0693: }
0694:
0695: /*
0696: * Temporary error handler that checks if selecting for
0697: * SubstructureRedirect failed.
0698: */
0699: static boolean winmgr_running = false;
0700: static XToolkit.XErrorHandler DetectWMHandler = new XToolkit.XErrorHandler() {
0701: public int handleError(long display, XErrorEvent err) {
0702: XToolkit.XERROR_SAVE(err);
0703: if (err.get_request_code() == XlibWrapper.X_ChangeWindowAttributes
0704: && err.get_error_code() == XlibWrapper.BadAccess) {
0705: winmgr_running = true;
0706: return 0;
0707: } else {
0708: return XToolkit.SAVED_ERROR_HANDLER(display, err);
0709: }
0710: }
0711: };
0712:
0713: /*
0714: * Make an educated guess about running window manager.
0715: * XXX: ideally, we should detect wm restart.
0716: */
0717: static int awt_wmgr = XWM.UNDETERMINED_WM;
0718: static XWM wm;
0719:
0720: static XWM getWM() {
0721: if (wm == null) {
0722: wm = new XWM(awt_wmgr = getWMID()/*XWM.OTHER_WM*/);
0723: }
0724: return wm;
0725: }
0726:
0727: static int getWMID() {
0728: if (insLog.isLoggable(Level.FINEST)) {
0729: insLog.finest("awt_wmgr = " + awt_wmgr);
0730: }
0731: /*
0732: * Ideally, we should support cases when a different WM is started
0733: * during a Java app lifetime.
0734: */
0735:
0736: if (awt_wmgr != XWM.UNDETERMINED_WM) {
0737: return awt_wmgr;
0738: }
0739:
0740: XSetWindowAttributes substruct = new XSetWindowAttributes();
0741: XToolkit.awtLock();
0742: try {
0743: if (isNoWM()) {
0744: awt_wmgr = XWM.NO_WM;
0745: return awt_wmgr;
0746: }
0747:
0748: // Initialize _NET protocol - used to detect Window Manager.
0749: // Later, WM will initialize its own version of protocol
0750: XNETProtocol l_net_protocol = g_net_protocol = new XNETProtocol();
0751: l_net_protocol.detect();
0752: if (log.isLoggable(Level.FINE) && l_net_protocol.active()) {
0753: log.fine("_NET_WM_NAME is "
0754: + l_net_protocol.getWMName());
0755: }
0756: XWINProtocol win = g_win_protocol = new XWINProtocol();
0757: win.detect();
0758:
0759: /* actual check for IceWM to follow below */
0760: boolean doIsIceWM = prepareIsIceWM(); /* and let IceWM to act */
0761:
0762: /*
0763: * Ok, some WM is out there. Check which one by testing for
0764: * "distinguishing" atoms.
0765: */
0766: if (isEnlightenment()) {
0767: awt_wmgr = XWM.ENLIGHTEN_WM;
0768: } else if (isMetacity()) {
0769: awt_wmgr = XWM.METACITY_WM;
0770: } else if (isSawfish()) {
0771: awt_wmgr = XWM.SAWFISH_WM;
0772: } else if (isKDE2()) {
0773: awt_wmgr = XWM.KDE2_WM;
0774: } else if (isCompiz()) {
0775: awt_wmgr = XWM.COMPIZ_WM;
0776: } else if (isLookingGlass()) {
0777: awt_wmgr = LG3D_WM;
0778: } else if (doIsIceWM && isIceWM()) {
0779: awt_wmgr = XWM.ICE_WM;
0780: }
0781: /*
0782: * We don't check for legacy WM when we already know that WM
0783: * supports WIN or _NET wm spec.
0784: */
0785: else if (l_net_protocol.active()) {
0786: awt_wmgr = XWM.OTHER_WM;
0787: } else if (win.active()) {
0788: awt_wmgr = XWM.OTHER_WM;
0789: }
0790: /*
0791: * Check for legacy WMs.
0792: */
0793: else if (isCDE()) { /* XXX: must come before isMotif */
0794: awt_wmgr = XWM.CDE_WM;
0795: } else if (isMotif()) {
0796: awt_wmgr = XWM.MOTIF_WM;
0797: } else if (isOpenLook()) {
0798: awt_wmgr = XWM.OPENLOOK_WM;
0799: } else {
0800: awt_wmgr = XWM.OTHER_WM;
0801: }
0802:
0803: return awt_wmgr;
0804: } finally {
0805: XToolkit.awtUnlock();
0806: substruct.dispose();
0807: }
0808: }
0809:
0810: /*****************************************************************************\
0811: *
0812: * Size and decoration hints ...
0813: *
0814: \*****************************************************************************/
0815:
0816: /*
0817: * Remove size hints specified by the mask.
0818: * XXX: Why do we need this in the first place???
0819: */
0820: static void removeSizeHints(XDecoratedPeer window, long mask) {
0821: mask &= PMaxSize | PMinSize;
0822:
0823: XToolkit.awtLock();
0824: try {
0825: XSizeHints hints = window.getHints();
0826: if ((hints.get_flags() & mask) == 0) {
0827: return;
0828: }
0829:
0830: hints.set_flags(hints.get_flags() & ~mask);
0831: if (insLog.isLoggable(Level.FINER))
0832: insLog.finer("Setting hints, flags "
0833: + XlibWrapper.hintsToString(hints.get_flags()));
0834: XlibWrapper.XSetWMNormalHints(XToolkit.getDisplay(), window
0835: .getWindow(), hints.pData);
0836: } finally {
0837: XToolkit.awtUnlock();
0838: }
0839: }
0840:
0841: /*
0842: * If MWM_DECOR_ALL bit is set, then the rest of the bit-mask is taken
0843: * to be subtracted from the decorations. Normalize decoration spec
0844: * so that we can map motif decor to something else bit-by-bit in the
0845: * rest of the code.
0846: */
0847: static int normalizeMotifDecor(int decorations) {
0848: if ((decorations & MWM_DECOR_ALL) == 0) {
0849: return decorations;
0850: }
0851: int d = MWM_DECOR_BORDER | MWM_DECOR_RESIZEH | MWM_DECOR_TITLE
0852: | MWM_DECOR_MENU | MWM_DECOR_MINIMIZE
0853: | MWM_DECOR_MAXIMIZE;
0854: d &= ~decorations;
0855: return d;
0856: }
0857:
0858: /*
0859: * If MWM_FUNC_ALL bit is set, then the rest of the bit-mask is taken
0860: * to be subtracted from the functions. Normalize function spec
0861: * so that we can map motif func to something else bit-by-bit in the
0862: * rest of the code.
0863: */
0864: static int normalizeMotifFunc(int functions) {
0865: if ((functions & MWM_FUNC_ALL) == 0) {
0866: return functions;
0867: }
0868: int f = MWM_FUNC_RESIZE | MWM_FUNC_MOVE | MWM_FUNC_MAXIMIZE
0869: | MWM_FUNC_MINIMIZE | MWM_FUNC_CLOSE;
0870: f &= ~functions;
0871: return f;
0872: }
0873:
0874: /*
0875: * Infer OL properties from MWM decorations.
0876: * Use _OL_DECOR_DEL(ATOM[]) to remove unwanted ones.
0877: */
0878: static void setOLDecor(XWindow window, boolean resizable,
0879: int decorations) {
0880: if (window == null) {
0881: return;
0882: }
0883:
0884: XAtomList decorDel = new XAtomList();
0885: decorations = normalizeMotifDecor(decorations);
0886: if (insLog.isLoggable(Level.FINER))
0887: insLog.finer("Setting OL_DECOR to "
0888: + Integer.toBinaryString(decorations));
0889: if ((decorations & MWM_DECOR_TITLE) == 0) {
0890: decorDel.add(XA_OL_DECOR_HEADER);
0891: }
0892: if ((decorations & (MWM_DECOR_RESIZEH | MWM_DECOR_MAXIMIZE)) == 0) {
0893: decorDel.add(XA_OL_DECOR_RESIZE);
0894: }
0895: if ((decorations & (MWM_DECOR_MENU | MWM_DECOR_MAXIMIZE | MWM_DECOR_MINIMIZE)) == 0) {
0896: decorDel.add(XA_OL_DECOR_CLOSE);
0897: }
0898: if (decorDel.size() == 0) {
0899: insLog.finer("Deleting OL_DECOR");
0900: XA_OL_DECOR_DEL.DeleteProperty(window);
0901: } else {
0902: if (insLog.isLoggable(Level.FINER))
0903: insLog.finer("Setting OL_DECOR to " + decorDel);
0904: XA_OL_DECOR_DEL.setAtomListProperty(window, decorDel);
0905: }
0906: }
0907:
0908: /*
0909: * Set MWM decorations. Set MWM functions depending on resizability.
0910: */
0911: static void setMotifDecor(XWindowPeer window, boolean resizable,
0912: int decorations, int functions) {
0913: /* Apparently some WMs don't implement MWM_*_ALL semantic correctly */
0914: if ((decorations & MWM_DECOR_ALL) != 0
0915: && (decorations != MWM_DECOR_ALL)) {
0916: decorations = normalizeMotifDecor(decorations);
0917: }
0918: if ((functions & MWM_FUNC_ALL) != 0
0919: && (functions != MWM_FUNC_ALL)) {
0920: functions = normalizeMotifFunc(functions);
0921: }
0922:
0923: PropMwmHints hints = window.getMWMHints();
0924: hints.set_flags(hints.get_flags() | MWM_HINTS_FUNCTIONS
0925: | MWM_HINTS_DECORATIONS);
0926: hints.set_functions(functions);
0927: hints.set_decorations(decorations);
0928:
0929: if (stateLog.isLoggable(Level.FINER))
0930: stateLog.finer("Setting MWM_HINTS to " + hints);
0931: window.setMWMHints(hints);
0932: }
0933:
0934: /*
0935: * Under some window managers if shell is already mapped, we MUST
0936: * unmap and later remap in order to effect the changes we make in the
0937: * window manager decorations.
0938: *
0939: * N.B. This unmapping / remapping of the shell exposes a bug in
0940: * X/Motif or the Motif Window Manager. When you attempt to map a
0941: * widget which is positioned (partially) off-screen, the window is
0942: * relocated to be entirely on screen. Good idea. But if both the x
0943: * and the y coordinates are less than the origin (0,0), the first
0944: * (re)map will move the window to the origin, and any subsequent
0945: * (re)map will relocate the window at some other point on the screen.
0946: * I have written a short Motif test program to discover this bug.
0947: * This should occur infrequently and it does not cause any real
0948: * problem. So for now we'll let it be.
0949: */
0950: static boolean needRemap(XDecoratedPeer window) {
0951: // Don't remap EmbeddedFrame,
0952: // e.g. for TrayIcon it causes problems.
0953: return !window.isEmbedded();
0954: }
0955:
0956: /*
0957: * Set decoration hints on the shell to wdata->decor adjusted
0958: * appropriately if not resizable.
0959: */
0960: static void setShellDecor(XDecoratedPeer window) {
0961: int decorations = window.getDecorations();
0962: int functions = window.getFunctions();
0963: boolean resizable = window.isResizable();
0964:
0965: if (!resizable) {
0966: if ((decorations & MWM_DECOR_ALL) != 0) {
0967: decorations |= MWM_DECOR_RESIZEH | MWM_DECOR_MAXIMIZE;
0968: } else {
0969: decorations &= ~(MWM_DECOR_RESIZEH | MWM_DECOR_MAXIMIZE);
0970: }
0971: }
0972: setMotifDecor(window, resizable, decorations, functions);
0973: setOLDecor(window, resizable, decorations);
0974:
0975: /* Some WMs need remap to redecorate the window */
0976: if (window.isShowing() && needRemap(window)) {
0977: /*
0978: * Do the re/mapping at the Xlib level. Since we essentially
0979: * work around a WM bug we don't want this hack to be exposed
0980: * to Intrinsics (i.e. don't mess with grabs, callbacks etc).
0981: */
0982: window.xSetVisible(false);
0983: XToolkit.XSync();
0984: window.xSetVisible(true);
0985: }
0986: }
0987:
0988: /*
0989: * Make specified shell resizable.
0990: */
0991: static void setShellResizable(XDecoratedPeer window) {
0992: if (insLog.isLoggable(Level.FINE))
0993: insLog.fine("Setting shell resizable " + window);
0994: XToolkit.awtLock();
0995: try {
0996: Rectangle shellBounds = window.getShellBounds();
0997: shellBounds.translate(-window.currentInsets.left,
0998: -window.currentInsets.top);
0999: window.updateSizeHints(window.getDimensions());
1000: requestWMExtents(window.getWindow());
1001: XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), window
1002: .getShell(), shellBounds.x, shellBounds.y,
1003: shellBounds.width, shellBounds.height);
1004: /* REMINDER: will need to revisit when setExtendedStateBounds is added */
1005: //Fix for 4320050: Minimum size for java.awt.Frame is not being enforced.
1006: //We need to update frame's minimum size, not to reset it
1007: removeSizeHints(window, PMaxSize);
1008: window.updateMinimumSize();
1009:
1010: /* Restore decorations */
1011: setShellDecor(window);
1012: } finally {
1013: XToolkit.awtUnlock();
1014: }
1015: }
1016:
1017: /*
1018: * Make specified shell non-resizable.
1019: * If justChangeSize is false, update decorations as well.
1020: * @param shellBounds bounds of the shell window
1021: */
1022: static void setShellNotResizable(XDecoratedPeer window,
1023: WindowDimensions newDimensions, Rectangle shellBounds,
1024: boolean justChangeSize) {
1025: if (insLog.isLoggable(Level.FINE))
1026: insLog.fine("Setting non-resizable shell " + window
1027: + ", dimensions " + newDimensions
1028: + ", shellBounds " + shellBounds
1029: + ", just change size: " + justChangeSize);
1030: XToolkit.awtLock();
1031: try {
1032: /* Fix min/max size hints at the specified values */
1033: if (!shellBounds.isEmpty()) {
1034: window.updateSizeHints(newDimensions);
1035: requestWMExtents(window.getWindow());
1036: XToolkit.XSync();
1037: XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(),
1038: window.getShell(), shellBounds.x,
1039: shellBounds.y, shellBounds.width,
1040: shellBounds.height);
1041: }
1042: if (!justChangeSize) { /* update decorations */
1043: setShellDecor(window);
1044: }
1045: } finally {
1046: XToolkit.awtUnlock();
1047: }
1048: }
1049:
1050: /*****************************************************************************\
1051: * Protocols support
1052: */
1053: HashMap<Class<?>, Collection<XProtocol>> protocolsMap = new HashMap<Class<?>, Collection<XProtocol>>();
1054:
1055: /**
1056: * Returns all protocols supporting given protocol interface
1057: */
1058: Collection<XProtocol> getProtocols(Class protocolInterface) {
1059: Collection<XProtocol> res = protocolsMap.get(protocolInterface);
1060: if (res != null) {
1061: return (Collection<XProtocol>) res;
1062: } else {
1063: return new LinkedList<XProtocol>();
1064: }
1065: }
1066:
1067: void addProtocol(Class protocolInterface, XProtocol protocol) {
1068: Collection<XProtocol> protocols = getProtocols(protocolInterface);
1069: protocols.add(protocol);
1070: protocolsMap.put(protocolInterface, protocols);
1071: }
1072:
1073: boolean supportsDynamicLayout() {
1074: int wm = getWMID();
1075: switch (wm) {
1076: case XWM.ENLIGHTEN_WM:
1077: case XWM.KDE2_WM:
1078: case XWM.SAWFISH_WM:
1079: case XWM.ICE_WM:
1080: case XWM.METACITY_WM:
1081: return true;
1082: case XWM.OPENLOOK_WM:
1083: case XWM.MOTIF_WM:
1084: case XWM.CDE_WM:
1085: return false;
1086: default:
1087: return false;
1088: }
1089: }
1090:
1091: /**
1092: * Check if state is supported.
1093: * Note that a compound state is always reported as not supported.
1094: * Note also that MAXIMIZED_BOTH is considered not a compound state.
1095: * Therefore, a compound state is just ICONIFIED | anything else.
1096: *
1097: */
1098: boolean supportsExtendedState(int state) {
1099: switch (state) {
1100: case Frame.MAXIMIZED_VERT:
1101: case Frame.MAXIMIZED_HORIZ:
1102: /*
1103: * WMs that talk NET/WIN protocol, but do not support
1104: * unidirectional maximization.
1105: */
1106: if (getWMID() == METACITY_WM) {
1107: /* "This is a deliberate policy decision." -hp */
1108: return false;
1109: }
1110: /* FALLTROUGH */
1111: case Frame.MAXIMIZED_BOTH:
1112: Iterator iter = getProtocols(XStateProtocol.class)
1113: .iterator();
1114: while (iter.hasNext()) {
1115: XStateProtocol proto = (XStateProtocol) iter.next();
1116: if (proto.supportsState(state)) {
1117: return true;
1118: }
1119: }
1120: default:
1121: return false;
1122: }
1123: }
1124:
1125: /*****************************************************************************\
1126: *
1127: * Reading state from different protocols
1128: *
1129: \*****************************************************************************/
1130:
1131: int getExtendedState(XWindowPeer window) {
1132: Iterator iter = getProtocols(XStateProtocol.class).iterator();
1133: int state = 0;
1134: while (iter.hasNext()) {
1135: XStateProtocol proto = (XStateProtocol) iter.next();
1136: state |= proto.getState(window);
1137: }
1138: if (state != 0) {
1139: return state;
1140: } else {
1141: return Frame.NORMAL;
1142: }
1143: }
1144:
1145: /*****************************************************************************\
1146: *
1147: * Notice window state change when WM changes a property on the window ...
1148: *
1149: \*****************************************************************************/
1150:
1151: /*
1152: * Check if property change is a window state protocol message.
1153: * If it is - return the new state as Integer, otherwise return null
1154: */
1155: Integer isStateChange(XDecoratedPeer window, XPropertyEvent e) {
1156: if (!window.isShowing()) {
1157: stateLog.finer("Window is not showing");
1158: return null;
1159: }
1160:
1161: int wm_state = window.getWMState();
1162: if (wm_state == XlibWrapper.WithdrawnState) {
1163: stateLog.finer("WithdrawnState");
1164: return null;
1165: } else {
1166: stateLog.finer("Window WM_STATE is " + wm_state);
1167: }
1168: boolean is_state_change = false;
1169: if (e.get_atom() == XA_WM_STATE.getAtom()) {
1170: is_state_change = true;
1171: }
1172:
1173: Iterator iter = getProtocols(XStateProtocol.class).iterator();
1174: while (iter.hasNext()) {
1175: XStateProtocol proto = (XStateProtocol) iter.next();
1176: is_state_change |= proto.isStateChange(e);
1177: }
1178: int res = 0;
1179:
1180: if (is_state_change) {
1181: if (wm_state == XlibWrapper.IconicState) {
1182: res = Frame.ICONIFIED;
1183: } else {
1184: res = Frame.NORMAL;
1185: }
1186: res |= getExtendedState(window);
1187: }
1188: if (is_state_change) {
1189: return Integer.valueOf(res);
1190: } else {
1191: return null;
1192: }
1193: }
1194:
1195: /*****************************************************************************\
1196: *
1197: * Setting/changing window state ...
1198: *
1199: \*****************************************************************************/
1200:
1201: /**
1202: * Moves window to the specified layer, layer is one of the constants defined
1203: * in XLayerProtocol
1204: */
1205: void setLayer(XWindowPeer window, int layer) {
1206: Iterator iter = getProtocols(XLayerProtocol.class).iterator();
1207: while (iter.hasNext()) {
1208: XLayerProtocol proto = (XLayerProtocol) iter.next();
1209: if (proto.supportsLayer(layer)) {
1210: proto.setLayer(window, layer);
1211: }
1212: }
1213: XToolkit.XSync();
1214: }
1215:
1216: void setExtendedState(XWindowPeer window, int state) {
1217: Iterator iter = getProtocols(XStateProtocol.class).iterator();
1218: while (iter.hasNext()) {
1219: XStateProtocol proto = (XStateProtocol) iter.next();
1220: if (proto.supportsState(state)) {
1221: proto.setState(window, state);
1222: break;
1223: }
1224: }
1225:
1226: if (!window.isShowing()) {
1227: /*
1228: * Purge KWM bits.
1229: * Not really tested with KWM, only with WindowMaker.
1230: */
1231: XToolkit.awtLock();
1232: try {
1233: XlibWrapper.XDeleteProperty(XToolkit.getDisplay(),
1234: window.getWindow(), XA_KWM_WIN_ICONIFIED
1235: .getAtom());
1236: XlibWrapper.XDeleteProperty(XToolkit.getDisplay(),
1237: window.getWindow(), XA_KWM_WIN_MAXIMIZED
1238: .getAtom());
1239: } finally {
1240: XToolkit.awtUnlock();
1241: }
1242: }
1243: XToolkit.XSync();
1244: }
1245:
1246: /*
1247: * Work around for 4775545.
1248: *
1249: * If WM exits while the top-level is shaded, the shaded hint remains
1250: * on the top-level properties. When WM restarts and sees the shaded
1251: * window it can reparent it into a "pre-shaded" decoration frame
1252: * (Metacity does), and our insets logic will go crazy, b/c it will
1253: * see a huge nagative bottom inset. There's no clean solution for
1254: * this, so let's just be weasels and drop the shaded hint if we
1255: * detect that WM exited. NB: we are in for a race condition with WM
1256: * restart here. NB2: e.g. WindowMaker saves the state in a private
1257: * property that this code knows nothing about, so this workaround is
1258: * not effective; other WMs might play similar tricks.
1259: */
1260: void unshadeKludge(XDecoratedPeer window) {
1261: assert (window.isShowing());
1262:
1263: Iterator iter = getProtocols(XStateProtocol.class).iterator();
1264: while (iter.hasNext()) {
1265: XStateProtocol proto = (XStateProtocol) iter.next();
1266: proto.unshadeKludge(window);
1267: }
1268: XToolkit.XSync();
1269: }
1270:
1271: static boolean inited = false;
1272:
1273: static void init() {
1274: if (inited) {
1275: return;
1276: }
1277:
1278: initAtoms();
1279: getWM();
1280: inited = true;
1281: }
1282:
1283: void initializeProtocols() {
1284: XNETProtocol net_protocol = g_net_protocol;
1285: if (net_protocol != null) {
1286: if (!net_protocol.active()) {
1287: net_protocol = null;
1288: } else {
1289: if (net_protocol.doStateProtocol()) {
1290: addProtocol(XStateProtocol.class, net_protocol);
1291: }
1292: if (net_protocol.doLayerProtocol()) {
1293: addProtocol(XLayerProtocol.class, net_protocol);
1294: }
1295: }
1296: }
1297:
1298: XWINProtocol win = g_win_protocol;
1299: if (win != null) {
1300: if (win.active()) {
1301: if (win.doStateProtocol()) {
1302: addProtocol(XStateProtocol.class, win);
1303: }
1304: if (win.doLayerProtocol()) {
1305: addProtocol(XLayerProtocol.class, win);
1306: }
1307: }
1308: }
1309: }
1310:
1311: HashMap storedInsets = new HashMap();
1312:
1313: Insets guessInsets(XDecoratedPeer window) {
1314: Insets res = (Insets) storedInsets.get(window.getClass());
1315: if (res == null) {
1316: switch (WMID) {
1317: case ENLIGHTEN_WM:
1318: res = new Insets(19, 4, 4, 4);
1319: break;
1320: case CDE_WM:
1321: res = new Insets(28, 6, 6, 6);
1322: break;
1323: case NO_WM:
1324: case LG3D_WM:
1325: res = zeroInsets;
1326: break;
1327: case MOTIF_WM:
1328: case OPENLOOK_WM:
1329: default:
1330: res = defaultInsets;
1331: }
1332: }
1333: if (insLog.isLoggable(Level.FINEST))
1334: insLog.finest("WM guessed insets: " + res);
1335: return res;
1336: }
1337:
1338: /*
1339: * Some buggy WMs ignore window gravity when processing
1340: * ConfigureRequest and position window as if the gravity is Static.
1341: * We work around this in MWindowPeer.pReshape().
1342: *
1343: * Starting with 1.5 we have introduced an Environment variable
1344: * _JAVA_AWT_WM_STATIC_GRAVITY that can be set to indicate to Java
1345: * explicitly that the WM has this behaviour, example is FVWM.
1346: */
1347:
1348: static int awtWMStaticGravity = -1;
1349:
1350: static boolean configureGravityBuggy() {
1351:
1352: if (awtWMStaticGravity == -1) {
1353: awtWMStaticGravity = (XToolkit
1354: .getEnv("_JAVA_AWT_WM_STATIC_GRAVITY") != null) ? 1
1355: : 0;
1356: }
1357:
1358: if (awtWMStaticGravity == 1) {
1359: return true;
1360: }
1361:
1362: switch (getWMID()) {
1363: case XWM.ICE_WM:
1364: /*
1365: * See bug #228981 at IceWM's SourceForge pages.
1366: * Latest stable version 1.0.8-6 still has this problem.
1367: */
1368: /**
1369: * Version 1.2.2 doesn't have this problem
1370: */
1371: // Detect IceWM version
1372: if (g_net_protocol != null) {
1373: String wm_name = g_net_protocol.getWMName();
1374: Pattern pat = Pattern
1375: .compile("^IceWM (\\d+)\\.(\\d+)\\.(\\d+).*$");
1376: try {
1377: Matcher match = pat.matcher(wm_name);
1378: if (match.matches()) {
1379: int v1 = Integer.parseInt(match.group(1));
1380: int v2 = Integer.parseInt(match.group(2));
1381: int v3 = Integer.parseInt(match.group(3));
1382: return !(v1 > 1 || (v1 == 1 && (v2 > 2 || (v2 == 2 && v3 >= 2))));
1383: }
1384: } catch (Exception e) {
1385: return true;
1386: }
1387: }
1388: return true;
1389: case XWM.ENLIGHTEN_WM:
1390: /* At least E16 is buggy. */
1391: return true;
1392: default:
1393: return false;
1394: }
1395: }
1396:
1397: /*
1398: * @return if WM implements the insets property - returns insets with values
1399: * specified in that property, null otherwise.
1400: */
1401: public static Insets getInsetsFromExtents(long window) {
1402: if (window == XConstants.None) {
1403: return null;
1404: }
1405: XNETProtocol net_protocol = getWM().getNETProtocol();
1406: if (net_protocol != null && net_protocol.active()) {
1407: Insets insets = getInsetsFromProp(window,
1408: XA_NET_FRAME_EXTENTS);
1409: insLog.log(Level.FINE, "_NET_FRAME_EXTENTS: {0}", insets);
1410:
1411: if (insets != null) {
1412: return insets;
1413: }
1414: }
1415: switch (getWMID()) {
1416: case XWM.KDE2_WM:
1417: return getInsetsFromProp(window, XA_KDE_NET_WM_FRAME_STRUT);
1418: case XWM.ENLIGHTEN_WM:
1419: return getInsetsFromProp(window, XA_E_FRAME_SIZE);
1420: default:
1421: return null;
1422: }
1423: }
1424:
1425: /**
1426: * Helper function reads property of type CARDINAL[4] = { left, right, top, bottom }
1427: * and converts it to Insets object.
1428: */
1429: public static Insets getInsetsFromProp(long window, XAtom atom) {
1430: if (window == XConstants.None) {
1431: return null;
1432: }
1433:
1434: WindowPropertyGetter getter = new WindowPropertyGetter(window,
1435: atom, 0, 4, false, XAtom.XA_CARDINAL);
1436: try {
1437: if (getter.execute() != XlibWrapper.Success
1438: || getter.getData() == 0
1439: || getter.getActualType() != XAtom.XA_CARDINAL
1440: || getter.getActualFormat() != 32) {
1441: return null;
1442: } else {
1443: return new Insets((int) Native.getCard32(getter
1444: .getData(), 2), // top
1445: (int) Native.getCard32(getter.getData(), 0), // left
1446: (int) Native.getCard32(getter.getData(), 3), // bottom
1447: (int) Native.getCard32(getter.getData(), 1)); // right
1448: }
1449: } finally {
1450: getter.dispose();
1451: }
1452: }
1453:
1454: /**
1455: * Asks WM to fill Frame Extents (insets) for the window.
1456: */
1457: public static void requestWMExtents(long window) {
1458: if (window == XConstants.None) { // not initialized
1459: return;
1460: }
1461:
1462: log.fine("Requesting FRAME_EXTENTS");
1463:
1464: XClientMessageEvent msg = new XClientMessageEvent();
1465: msg.zero();
1466: msg.set_type(XlibWrapper.ClientMessage);
1467: msg.set_display(XToolkit.getDisplay());
1468: msg.set_window(window);
1469: msg.set_format(32);
1470: XToolkit.awtLock();
1471: try {
1472: XNETProtocol net_protocol = getWM().getNETProtocol();
1473: if (net_protocol != null && net_protocol.active()) {
1474: msg.set_message_type(XA_NET_REQUEST_FRAME_EXTENTS
1475: .getAtom());
1476: XlibWrapper.XSendEvent(XToolkit.getDisplay(), XToolkit
1477: .getDefaultRootWindow(), false,
1478: XlibWrapper.SubstructureRedirectMask
1479: | XlibWrapper.SubstructureNotifyMask,
1480: msg.getPData());
1481: }
1482: if (getWMID() == XWM.KDE2_WM) {
1483: msg.set_message_type(XA_KDE_NET_WM_FRAME_STRUT
1484: .getAtom());
1485: XlibWrapper.XSendEvent(XToolkit.getDisplay(), XToolkit
1486: .getDefaultRootWindow(), false,
1487: XlibWrapper.SubstructureRedirectMask
1488: | XlibWrapper.SubstructureNotifyMask,
1489: msg.getPData());
1490: }
1491: // XXX: should we wait for response? XIfEvent() would be useful here :)
1492: } finally {
1493: XToolkit.awtUnlock();
1494: msg.dispose();
1495: }
1496: }
1497:
1498: /* syncTopLEvelPos() is necessary to insure that the window manager has in
1499: * fact moved us to our final position relative to the reParented WM window.
1500: * We have noted a timing window which our shell has not been moved so we
1501: * screw up the insets thinking they are 0,0. Wait (for a limited period of
1502: * time to let the WM hava a chance to move us.
1503: * @param window window ID of the shell, assuming it is the window
1504: * which will NOT have zero coordinates after the complete
1505: * reparenting
1506: */
1507: boolean syncTopLevelPos(long window, XWindowAttributes attrs) {
1508: int tries = 0;
1509: XToolkit.awtLock();
1510: try {
1511: do {
1512: XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1513: window, attrs.pData);
1514: if (attrs.get_x() != 0 || attrs.get_y() != 0) {
1515: return true;
1516: }
1517: tries++;
1518: XToolkit.XSync();
1519: } while (tries < 50);
1520: } finally {
1521: XToolkit.awtUnlock();
1522: }
1523: return false;
1524: }
1525:
1526: Insets getInsets(XDecoratedPeer win, long window, long parent) {
1527: /*
1528: * Unfortunately the concept of "insets" borrowed to AWT
1529: * from Win32 is *absolutely*, *unbelievably* foreign to
1530: * X11. Few WMs provide the size of frame decor
1531: * (i.e. insets) in a property they set on the client
1532: * window, so we check if we can get away with just
1533: * peeking at it. [Future versions of wm-spec might add a
1534: * standardized hint for this].
1535: *
1536: * Otherwise we do some special casing. Actually the
1537: * fallback code ("default" case) seems to cover most of
1538: * the existing WMs (modulo Reparent/Configure order
1539: * perhaps?).
1540: *
1541: * Fallback code tries to account for the two most common cases:
1542: *
1543: * . single reparenting
1544: * parent window is the WM frame
1545: * [twm, olwm, sawfish]
1546: *
1547: * . double reparenting
1548: * parent is a lining exactly the size of the client
1549: * grandpa is the WM frame
1550: * [mwm, e!, kwin, fvwm2 ... ]
1551: */
1552: Insets correctWM = XWM.getInsetsFromExtents(window);
1553: insLog.log(Level.FINER, "Got insets from property: {0}",
1554: correctWM);
1555:
1556: if (correctWM == null) {
1557: correctWM = new Insets(0, 0, 0, 0);
1558:
1559: correctWM.top = -1;
1560: correctWM.left = -1;
1561:
1562: XWindowAttributes lwinAttr = new XWindowAttributes();
1563: XWindowAttributes pattr = new XWindowAttributes();
1564: try {
1565: switch (XWM.getWMID()) {
1566: /* should've been done in awt_wm_getInsetsFromProp */
1567: case XWM.ENLIGHTEN_WM: {
1568: /* enlightenment does double reparenting */
1569: syncTopLevelPos(parent, lwinAttr);
1570: correctWM.left = lwinAttr.get_x();
1571: correctWM.top = lwinAttr.get_y();
1572: /*
1573: * Now get the actual dimensions of the parent window
1574: * resolve the difference. We can't rely on the left
1575: * to be equal to right or bottom... Enlightment
1576: * breaks that assumption.
1577: */
1578: XlibWrapper.XGetWindowAttributes(XToolkit
1579: .getDisplay(), XlibUtil
1580: .getParentWindow(parent), pattr.pData);
1581: correctWM.right = pattr.get_width()
1582: - (lwinAttr.get_width() + correctWM.left);
1583: correctWM.bottom = pattr.get_height()
1584: - (lwinAttr.get_height() + correctWM.top);
1585:
1586: break;
1587: }
1588: case XWM.ICE_WM: // for 1.2.2.
1589: case XWM.KDE2_WM: /* should've been done in getInsetsFromProp */
1590: case XWM.CDE_WM:
1591: case XWM.MOTIF_WM: {
1592: /* these are double reparenting too */
1593: if (syncTopLevelPos(parent, lwinAttr)) {
1594: correctWM.top = lwinAttr.get_y();
1595: correctWM.left = lwinAttr.get_x();
1596: correctWM.right = correctWM.left;
1597: correctWM.bottom = correctWM.left;
1598: } else {
1599: return null;
1600: }
1601: break;
1602: }
1603: case XWM.SAWFISH_WM:
1604: case XWM.OPENLOOK_WM: {
1605: /* single reparenting */
1606: syncTopLevelPos(window, lwinAttr);
1607: correctWM.top = lwinAttr.get_y();
1608: correctWM.left = lwinAttr.get_x();
1609: correctWM.right = correctWM.left;
1610: correctWM.bottom = correctWM.left;
1611: break;
1612: }
1613: case XWM.OTHER_WM:
1614: default: { /* this is very similar to the E! case above */
1615: insLog
1616: .log(
1617: Level.FINEST,
1618: "Getting correct insets for OTHER_WM/default, parent: {0}",
1619: parent);
1620: syncTopLevelPos(parent, lwinAttr);
1621: int status = XlibWrapper.XGetWindowAttributes(
1622: XToolkit.getDisplay(), window,
1623: lwinAttr.pData);
1624: status = XlibWrapper.XGetWindowAttributes(XToolkit
1625: .getDisplay(), parent, pattr.pData);
1626: if (lwinAttr.get_root() == parent) {
1627: insLog
1628: .finest("our parent is root so insets should be zero");
1629: correctWM = new Insets(0, 0, 0, 0);
1630: break;
1631: }
1632:
1633: /*
1634: * Check for double-reparenting WM.
1635: *
1636: * If the parent is exactly the same size as the
1637: * top-level assume taht it's the "lining" window and
1638: * that the grandparent is the actual frame (NB: we
1639: * have already handled undecorated windows).
1640: *
1641: * XXX: what about timing issues that syncTopLevelPos
1642: * is supposed to work around?
1643: */
1644: if (lwinAttr.get_x() == 0
1645: && lwinAttr.get_y() == 0
1646: && lwinAttr.get_width() + 2
1647: * lwinAttr.get_border_width() == pattr
1648: .get_width()
1649: && lwinAttr.get_height() + 2
1650: * lwinAttr.get_border_width() == pattr
1651: .get_height()) {
1652: insLog
1653: .log(
1654: Level.FINEST,
1655: "Double reparenting detected, pattr({2})={0}, lwinAttr({3})={1}",
1656: new Object[] { lwinAttr, pattr,
1657: parent, window });
1658: lwinAttr.set_x(pattr.get_x());
1659: lwinAttr.set_y(pattr.get_y());
1660: lwinAttr.set_border_width(lwinAttr
1661: .get_border_width()
1662: + pattr.get_border_width());
1663:
1664: final long grand_parent = XlibUtil
1665: .getParentWindow(parent);
1666:
1667: if (grand_parent == lwinAttr.get_root()) {
1668: // This is not double-reparenting in a
1669: // general sense - we simply don't have
1670: // correct insets - return null to try to
1671: // get insets later
1672: return null;
1673: } else {
1674: parent = grand_parent;
1675: XlibWrapper.XGetWindowAttributes(XToolkit
1676: .getDisplay(), parent, pattr.pData);
1677: }
1678: }
1679: /*
1680: * XXX: To be absolutely correct, we'd need to take
1681: * parent's border-width into account too, but the
1682: * rest of the code is happily unaware about border
1683: * widths and inner/outer distinction, so for the time
1684: * being, just ignore it.
1685: */
1686: insLog
1687: .log(
1688: Level.FINEST,
1689: "Attrs before calculation: pattr({2})={0}, lwinAttr({3})={1}",
1690: new Object[] { lwinAttr, pattr,
1691: parent, window });
1692: correctWM = new Insets(
1693: lwinAttr.get_y()
1694: + lwinAttr.get_border_width(),
1695: lwinAttr.get_x()
1696: + lwinAttr.get_border_width(),
1697: pattr.get_height()
1698: - (lwinAttr.get_y()
1699: + lwinAttr.get_height() + 2 * lwinAttr
1700: .get_border_width()),
1701: pattr.get_width()
1702: - (lwinAttr.get_x()
1703: + lwinAttr.get_width() + 2 * lwinAttr
1704: .get_border_width()));
1705: break;
1706: } /* default */
1707: } /* switch (runningWM) */
1708: } finally {
1709: lwinAttr.dispose();
1710: pattr.dispose();
1711: }
1712: }
1713: if (storedInsets.get(win.getClass()) == null) {
1714: storedInsets.put(win.getClass(), correctWM);
1715: }
1716: return correctWM;
1717: }
1718:
1719: boolean isDesktopWindow(long w) {
1720: if (g_net_protocol != null) {
1721: XAtomList wtype = XAtom.get("_NET_WM_WINDOW_TYPE")
1722: .getAtomListPropertyList(w);
1723: return wtype.contains(XAtom
1724: .get("_NET_WM_WINDOW_TYPE_DESKTOP"));
1725: } else {
1726: return false;
1727: }
1728: }
1729:
1730: public XNETProtocol getNETProtocol() {
1731: return g_net_protocol;
1732: }
1733:
1734: /**
1735: * Sets _NET_WN_ICON property on the window using the arrays of
1736: * raster-data for icons. If icons is null, removes _NET_WM_ICON
1737: * property.
1738: * This method invokes XNETProtocol.setWMIcon() for WMs that
1739: * support NET protocol.
1740: *
1741: * @return true if hint was modified successfully, false otherwise
1742: */
1743: public boolean setNetWMIcon(XWindowPeer window,
1744: java.util.List<XIconInfo> icons) {
1745: if (g_net_protocol != null && g_net_protocol.active()) {
1746: g_net_protocol.setWMIcons(window, icons);
1747: return getWMID() != ICE_WM;
1748: }
1749: return false;
1750: }
1751: }
|