13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26
27 /**
28 * Ported from awt_wm.c, SCCS v1.11, author Valeriy Ushakov
29 * Author: Denis Mikhalkin
30 */
31 package sun.awt.X11;
32
33 import sun.misc.Unsafe;
34 import java.awt.Insets;
35 import java.awt.Frame;
36 import java.awt.Rectangle;
37 import java.util.Collection;
38 import java.util.HashMap;
39 import java.util.LinkedList;
40 import java.util.logging.Level;
41 import java.util.logging.Logger;
42 import java.util.regex.Matcher;
43 import java.util.regex.Pattern;
44
45 /**
46 * Class incapsulating knowledge about window managers in general
47 * Descendants should provide some information about specific window manager.
48 */
49 final class XWM
50 {
51
52 private final static Logger log = Logger.getLogger("sun.awt.X11.XWM");
53 private final static Logger insLog = Logger.getLogger("sun.awt.X11.insets.XWM");
54 private final static Logger stateLog = Logger.getLogger("sun.awt.X11.states.XWM");
55
56 static final XAtom XA_MWM_HINTS = new XAtom();
57
58 private static Unsafe unsafe = XlibWrapper.unsafe;
59
60
61 /* Good old ICCCM */
62 static XAtom XA_WM_STATE = new XAtom();
63
64
119 case KDE2_WM:
120 return "KWM2";
121 case SAWFISH_WM:
122 return "Sawfish";
123 case ICE_WM:
124 return "IceWM";
125 case METACITY_WM:
126 return "Metacity";
127 case COMPIZ_WM:
128 return "Compiz";
129 case LG3D_WM:
130 return "LookingGlass";
131 case UNDETERMINED_WM:
132 default:
133 return "Undetermined WM";
134 }
135 }
136
137
138 int WMID;
139 static final Insets zeroInsets = new Insets(0, 0, 0, 0);
140 static final Insets defaultInsets = new Insets(25, 5, 5, 5);
141
142 XWM(int WMID) {
143 this.WMID = WMID;
144 initializeProtocols();
145 if (log.isLoggable(Level.FINE)) log.fine("Window manager: " + toString());
146 }
147 int getID() {
148 return WMID;
149 }
150
151
152 static Insets normalize(Insets insets) {
153 if (insets.top > 64 || insets.top < 0) {
154 insets.top = 28;
155 }
156 if (insets.left > 32 || insets.left < 0) {
157 insets.left = 6;
158 }
159 if (insets.right > 32 || insets.right < 0) {
160 insets.right = 6;
161 }
162 if (insets.bottom > 32 || insets.bottom < 0) {
163 insets.bottom = 6;
164 }
165 return insets;
166 }
167
168 static XNETProtocol g_net_protocol = null;
169 static XWINProtocol g_win_protocol = null;
170 static boolean isNetWMName(String name) {
171 if (g_net_protocol != null) {
172 return g_net_protocol.isWMName(name);
173 } else {
174 return false;
175 }
176 }
177
178 static void initAtoms() {
179 final Object[][] atomInitList ={
180 { XA_WM_STATE, "WM_STATE" },
181
182 { XA_KDE_NET_WM_FRAME_STRUT, "_KDE_NET_WM_FRAME_STRUT" },
183
184 { XA_E_FRAME_SIZE, "_E_FRAME_SIZE" },
185
186 { XA_KWM_WIN_ICONIFIED, "KWM_WIN_ICONIFIED" },
264 long selection_owner =
265 XlibWrapper.XGetSelectionOwner(XToolkit.getDisplay(),
266 XAtom.get(selection_name).getAtom());
267 if (insLog.isLoggable(Level.FINE)) {
268 insLog.finer("selection owner of " + selection_name
269 + " is " + selection_owner);
270 }
271
272 if (selection_owner != XConstants.None) {
273 return false;
274 }
275
276 winmgr_running = false;
277 substruct.set_event_mask(XConstants.SubstructureRedirectMask);
278
279 XToolkit.WITH_XERROR_HANDLER(detectWMHandler);
280 XlibWrapper.XChangeWindowAttributes(XToolkit.getDisplay(),
281 XToolkit.getDefaultRootWindow(),
282 XConstants.CWEventMask,
283 substruct.pData);
284 XToolkit.RESTORE_XERROR_HANDLER();
285
286 /*
287 * If no WM is running then our selection for SubstructureRedirect
288 * succeeded and needs to be undone (hey we are *not* a WM ;-).
289 */
290 if (!winmgr_running) {
291 substruct.set_event_mask(0);
292 XlibWrapper.XChangeWindowAttributes(XToolkit.getDisplay(),
293 XToolkit.getDefaultRootWindow(),
294 XConstants.CWEventMask,
295 substruct.pData);
296 if (insLog.isLoggable(Level.FINE)) {
297 insLog.finer("It looks like there is no WM thus NO_WM");
298 }
299 }
300
301 return !winmgr_running;
302 } finally {
303 substruct.dispose();
543 /*
544 * Is KDE2 (KWin) running?
545 */
546 static boolean isKDE2() {
547 return isNetWMName("KWin");
548 }
549
550 static boolean isCompiz() {
551 return isNetWMName("compiz");
552 }
553
554 static boolean isLookingGlass() {
555 return isNetWMName("LG3D");
556 }
557
558 /*
559 * Is Metacity running?
560 */
561 static boolean isMetacity() {
562 return isNetWMName("Metacity");
563 // || (
564 // XA_NET_SUPPORTING_WM_CHECK.
565 // getIntProperty(XToolkit.getDefaultRootWindow(), XA_NET_SUPPORTING_WM_CHECK.
566 // getIntProperty(XToolkit.getDefaultRootWindow(), XAtom.XA_CARDINAL)) == 0);
567 }
568
569 static boolean isNonReparentingWM() {
570 return (XWM.getWMID() == XWM.COMPIZ_WM || XWM.getWMID() == XWM.LG3D_WM);
571 }
572
573 /*
574 * Prepare IceWM check.
575 *
576 * The only way to detect IceWM, seems to be by setting
577 * _ICEWM_WINOPTHINT(_ICEWM_WINOPTHINT/8) on root and checking if it
578 * was immediately deleted by IceWM.
579 *
580 * But messing with PropertyNotify here is way too much trouble, so
581 * approximate the check by setting the property in this function and
582 * checking if it still exists later on.
583 *
584 * Gaa, dirty dances...
585 */
586 static final XAtom XA_ICEWM_WINOPTHINT = new XAtom("_ICEWM_WINOPTHINT", false);
587 static final char opt[] = {
588 'A','W','T','_','I','C','E','W','M','_','T','E','S','T','\0',
589 'a','l','l','W','o','r','k','s','p','a','c','e','s','\0',
590 '0','\0'
591 };
592 static boolean prepareIsIceWM() {
666 /*
667 * Temporary error handler that checks if selecting for
668 * SubstructureRedirect failed.
669 */
670 private static boolean winmgr_running = false;
671 private static XErrorHandler detectWMHandler = new XErrorHandler.XBaseErrorHandler() {
672 @Override
673 public int handleError(long display, XErrorEvent err) {
674 if ((err.get_request_code() == XProtocolConstants.X_ChangeWindowAttributes) &&
675 (err.get_error_code() == XConstants.BadAccess))
676 {
677 winmgr_running = true;
678 return 0;
679 }
680 return super.handleError(display, err);
681 }
682 };
683
684 /*
685 * Make an educated guess about running window manager.
686 * XXX: ideally, we should detect wm restart.
687 */
688 static int awt_wmgr = XWM.UNDETERMINED_WM;
689 static XWM wm;
690 static XWM getWM() {
691 if (wm == null) {
692 wm = new XWM(awt_wmgr = getWMID()/*XWM.OTHER_WM*/);
693 }
694 return wm;
695 }
696 static int getWMID() {
697 if (insLog.isLoggable(Level.FINEST)) {
698 insLog.finest("awt_wmgr = " + awt_wmgr);
699 }
700 /*
701 * Ideally, we should support cases when a different WM is started
702 * during a Java app lifetime.
703 */
704
705 if (awt_wmgr != XWM.UNDETERMINED_WM) {
706 return awt_wmgr;
707 }
708
709 XSetWindowAttributes substruct = new XSetWindowAttributes();
710 XToolkit.awtLock();
711 try {
712 if (isNoWM()) {
713 awt_wmgr = XWM.NO_WM;
714 return awt_wmgr;
715 }
716
717 // Initialize _NET protocol - used to detect Window Manager.
718 // Later, WM will initialize its own version of protocol
719 XNETProtocol l_net_protocol = g_net_protocol = new XNETProtocol();
720 l_net_protocol.detect();
721 if (log.isLoggable(Level.FINE) && l_net_protocol.active()) {
722 log.fine("_NET_WM_NAME is " + l_net_protocol.getWMName());
723 }
949 if (window.isShowing() && needRemap(window)) {
950 /*
951 * Do the re/mapping at the Xlib level. Since we essentially
952 * work around a WM bug we don't want this hack to be exposed
953 * to Intrinsics (i.e. don't mess with grabs, callbacks etc).
954 */
955 window.xSetVisible(false);
956 XToolkit.XSync();
957 window.xSetVisible(true);
958 }
959 }
960
961 /*
962 * Make specified shell resizable.
963 */
964 static void setShellResizable(XDecoratedPeer window) {
965 if (insLog.isLoggable(Level.FINE)) insLog.fine("Setting shell resizable " + window);
966 XToolkit.awtLock();
967 try {
968 Rectangle shellBounds = window.getShellBounds();
969 shellBounds.translate(-window.currentInsets.left, -window.currentInsets.top);
970 window.updateSizeHints(window.getDimensions());
971 requestWMExtents(window.getWindow());
972 XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), window.getShell(),
973 shellBounds.x, shellBounds.y, shellBounds.width, shellBounds.height);
974 /* REMINDER: will need to revisit when setExtendedStateBounds is added */
975 //Fix for 4320050: Minimum size for java.awt.Frame is not being enforced.
976 //We need to update frame's minimum size, not to reset it
977 removeSizeHints(window, XUtilConstants.PMaxSize);
978 window.updateMinimumSize();
979
980 /* Restore decorations */
981 setShellDecor(window);
982 } finally {
983 XToolkit.awtUnlock();
984 }
985 }
986
987 /*
988 * Make specified shell non-resizable.
989 * If justChangeSize is false, update decorations as well.
1031 private <T> void addProtocol(Class<T> protocolInterface, T protocol) {
1032 Collection<T> protocols = getProtocols(protocolInterface);
1033 protocols.add(protocol);
1034 protocolsMap.put(protocolInterface, protocols);
1035 }
1036
1037 boolean supportsDynamicLayout() {
1038 int wm = getWMID();
1039 switch (wm) {
1040 case XWM.ENLIGHTEN_WM:
1041 case XWM.KDE2_WM:
1042 case XWM.SAWFISH_WM:
1043 case XWM.ICE_WM:
1044 case XWM.METACITY_WM:
1045 return true;
1046 case XWM.OPENLOOK_WM:
1047 case XWM.MOTIF_WM:
1048 case XWM.CDE_WM:
1049 return false;
1050 default:
1051 return false;
1052 }
1053 }
1054
1055
1056 /**
1057 * Check if state is supported.
1058 * Note that a compound state is always reported as not supported.
1059 * Note also that MAXIMIZED_BOTH is considered not a compound state.
1060 * Therefore, a compound state is just ICONIFIED | anything else.
1061 *
1062 */
1063 boolean supportsExtendedState(int state) {
1064 switch (state) {
1065 case Frame.MAXIMIZED_VERT:
1066 case Frame.MAXIMIZED_HORIZ:
1067 /*
1068 * WMs that talk NET/WIN protocol, but do not support
1069 * unidirectional maximization.
1070 */
1247 }
1248 if (net_protocol.doLayerProtocol()) {
1249 addProtocol(XLayerProtocol.class, net_protocol);
1250 }
1251 }
1252 }
1253
1254 XWINProtocol win = g_win_protocol;
1255 if (win != null) {
1256 if (win.active()) {
1257 if (win.doStateProtocol()) {
1258 addProtocol(XStateProtocol.class, win);
1259 }
1260 if (win.doLayerProtocol()) {
1261 addProtocol(XLayerProtocol.class, win);
1262 }
1263 }
1264 }
1265 }
1266
1267 HashMap storedInsets = new HashMap();
1268 Insets guessInsets(XDecoratedPeer window) {
1269 Insets res = (Insets)storedInsets.get(window.getClass());
1270 if (res == null) {
1271 switch (WMID) {
1272 case ENLIGHTEN_WM:
1273 res = new Insets(19, 4, 4, 4);
1274 break;
1275 case CDE_WM:
1276 res = new Insets(28, 6, 6, 6);
1277 break;
1278 case NO_WM:
1279 case LG3D_WM:
1280 res = zeroInsets;
1281 break;
1282 case MOTIF_WM:
1283 case OPENLOOK_WM:
1284 default:
1285 res = defaultInsets;
1286 }
1287 }
1288 if (insLog.isLoggable(Level.FINEST)) insLog.finest("WM guessed insets: " + res);
1289 return res;
1290 }
1291 /*
1292 * Some buggy WMs ignore window gravity when processing
1293 * ConfigureRequest and position window as if the gravity is Static.
1294 * We work around this in MWindowPeer.pReshape().
1295 *
1296 * Starting with 1.5 we have introduced an Environment variable
1297 * _JAVA_AWT_WM_STATIC_GRAVITY that can be set to indicate to Java
1298 * explicitly that the WM has this behaviour, example is FVWM.
1299 */
1300
1301 static int awtWMStaticGravity = -1;
1302 static boolean configureGravityBuggy() {
1303
1304 if (awtWMStaticGravity == -1) {
1305 awtWMStaticGravity = (XToolkit.getEnv("_JAVA_AWT_WM_STATIC_GRAVITY") != null) ? 1 : 0;
1306 }
1307
1308 if (awtWMStaticGravity == 1) {
1309 return true;
1310 }
1384 0, 4, false, XAtom.XA_CARDINAL);
1385 try {
1386 if (getter.execute() != XConstants.Success
1387 || getter.getData() == 0
1388 || getter.getActualType() != XAtom.XA_CARDINAL
1389 || getter.getActualFormat() != 32)
1390 {
1391 return null;
1392 } else {
1393 return new Insets((int)Native.getCard32(getter.getData(), 2), // top
1394 (int)Native.getCard32(getter.getData(), 0), // left
1395 (int)Native.getCard32(getter.getData(), 3), // bottom
1396 (int)Native.getCard32(getter.getData(), 1)); // right
1397 }
1398 } finally {
1399 getter.dispose();
1400 }
1401 }
1402
1403 /**
1404 * Asks WM to fill Frame Extents (insets) for the window.
1405 */
1406 public static void requestWMExtents(long window) {
1407 if (window == XConstants.None) { // not initialized
1408 return;
1409 }
1410
1411 log.fine("Requesting FRAME_EXTENTS");
1412
1413 XClientMessageEvent msg = new XClientMessageEvent();
1414 msg.zero();
1415 msg.set_type(XConstants.ClientMessage);
1416 msg.set_display(XToolkit.getDisplay());
1417 msg.set_window(window);
1418 msg.set_format(32);
1419 XToolkit.awtLock();
1420 try {
1421 XNETProtocol net_protocol = getWM().getNETProtocol();
1422 if (net_protocol != null && net_protocol.active()) {
1423 msg.set_message_type(XA_NET_REQUEST_FRAME_EXTENTS.getAtom());
1424 XlibWrapper.XSendEvent(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(),
1425 false,
1426 XConstants.SubstructureRedirectMask | XConstants.SubstructureNotifyMask,
1427 msg.getPData());
1428 }
1429 if (getWMID() == XWM.KDE2_WM) {
1430 msg.set_message_type(XA_KDE_NET_WM_FRAME_STRUT.getAtom());
1431 XlibWrapper.XSendEvent(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(),
1432 false,
1433 XConstants.SubstructureRedirectMask | XConstants.SubstructureNotifyMask,
1434 msg.getPData());
1435 }
1436 // XXX: should we wait for response? XIfEvent() would be useful here :)
1437 } finally {
1438 XToolkit.awtUnlock();
1439 msg.dispose();
1440 }
1441 }
1442
1443 /* syncTopLEvelPos() is necessary to insure that the window manager has in
1444 * fact moved us to our final position relative to the reParented WM window.
1445 * We have noted a timing window which our shell has not been moved so we
1446 * screw up the insets thinking they are 0,0. Wait (for a limited period of
1447 * time to let the WM hava a chance to move us.
1448 * @param window window ID of the shell, assuming it is the window
1449 * which will NOT have zero coordinates after the complete
1450 * reparenting
1451 */
1452 boolean syncTopLevelPos(long window, XWindowAttributes attrs) {
1453 int tries = 0;
1454 XToolkit.awtLock();
1455 try {
1456 do {
1457 XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), window, attrs.pData);
1458 if (attrs.get_x() != 0 || attrs.get_y() != 0) {
1459 return true;
1460 }
1461 tries++;
1462 XToolkit.XSync();
1463 } while (tries < 50);
1464 }
1465 finally {
1466 XToolkit.awtUnlock();
1467 }
1468 return false;
1469 }
1470
1471 Insets getInsets(XDecoratedPeer win, long window, long parent) {
1472 /*
1473 * Unfortunately the concept of "insets" borrowed to AWT
1474 * from Win32 is *absolutely*, *unbelievably* foreign to
1475 * X11. Few WMs provide the size of frame decor
1476 * (i.e. insets) in a property they set on the client
1477 * window, so we check if we can get away with just
1478 * peeking at it. [Future versions of wm-spec might add a
1479 * standardized hint for this].
1480 *
1481 * Otherwise we do some special casing. Actually the
1482 * fallback code ("default" case) seems to cover most of
1483 * the existing WMs (modulo Reparent/Configure order
1484 * perhaps?).
1485 *
1486 * Fallback code tries to account for the two most common cases:
1487 *
1488 * . single reparenting
1489 * parent window is the WM frame
1490 * [twm, olwm, sawfish]
1491 *
1492 * . double reparenting
1493 * parent is a lining exactly the size of the client
1494 * grandpa is the WM frame
1495 * [mwm, e!, kwin, fvwm2 ... ]
1496 */
1497 Insets correctWM = XWM.getInsetsFromExtents(window);
1498 insLog.log(Level.FINER, "Got insets from property: {0}", correctWM);
1499
1500 if (correctWM == null) {
1501 correctWM = new Insets(0,0,0,0);
1502
1503 correctWM.top = -1;
1504 correctWM.left = -1;
1505
1506 XWindowAttributes lwinAttr = new XWindowAttributes();
1507 XWindowAttributes pattr = new XWindowAttributes();
1508 try {
1509 switch (XWM.getWMID()) {
1510 /* should've been done in awt_wm_getInsetsFromProp */
1511 case XWM.ENLIGHTEN_WM: {
1512 /* enlightenment does double reparenting */
1513 syncTopLevelPos(parent, lwinAttr);
1514 correctWM.left = lwinAttr.get_x();
1515 correctWM.top = lwinAttr.get_y();
1516 /*
1517 * Now get the actual dimensions of the parent window
1518 * resolve the difference. We can't rely on the left
1519 * to be equal to right or bottom... Enlightment
1520 * breaks that assumption.
1521 */
1522 XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1523 XlibUtil.getParentWindow(parent),
1524 pattr.pData);
1525 correctWM.right = pattr.get_width() -
1526 (lwinAttr.get_width() + correctWM.left);
1527 correctWM.bottom = pattr.get_height() -
1528 (lwinAttr.get_height() + correctWM.top);
1529
1530 break;
1531 }
1532 case XWM.ICE_WM: // for 1.2.2.
1533 case XWM.KDE2_WM: /* should've been done in getInsetsFromProp */
1534 case XWM.CDE_WM:
1535 case XWM.MOTIF_WM: {
1536 /* these are double reparenting too */
1537 if (syncTopLevelPos(parent, lwinAttr)) {
1538 correctWM.top = lwinAttr.get_y();
1539 correctWM.left = lwinAttr.get_x();
1540 correctWM.right = correctWM.left;
1541 correctWM.bottom = correctWM.left;
1542 } else {
1543 return null;
1544 }
1545 break;
1546 }
1547 case XWM.SAWFISH_WM:
1548 case XWM.OPENLOOK_WM: {
1549 /* single reparenting */
1550 syncTopLevelPos(window, lwinAttr);
1551 correctWM.top = lwinAttr.get_y();
1552 correctWM.left = lwinAttr.get_x();
1553 correctWM.right = correctWM.left;
1554 correctWM.bottom = correctWM.left;
1555 break;
1556 }
1557 case XWM.OTHER_WM:
1558 default: { /* this is very similar to the E! case above */
1559 insLog.log(Level.FINEST, "Getting correct insets for OTHER_WM/default, parent: {0}", parent);
1560 syncTopLevelPos(parent, lwinAttr);
1561 int status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1562 window, lwinAttr.pData);
1563 status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1564 parent, pattr.pData);
1565 if (lwinAttr.get_root() == parent) {
1566 insLog.finest("our parent is root so insets should be zero");
1567 correctWM = new Insets(0, 0, 0, 0);
1568 break;
1569 }
1570
1571 /*
1572 * Check for double-reparenting WM.
1573 *
1574 * If the parent is exactly the same size as the
1575 * top-level assume taht it's the "lining" window and
1576 * that the grandparent is the actual frame (NB: we
1577 * have already handled undecorated windows).
1578 *
1579 * XXX: what about timing issues that syncTopLevelPos
1580 * is supposed to work around?
1581 */
1582 if (lwinAttr.get_x() == 0 && lwinAttr.get_y() == 0
1583 && lwinAttr.get_width()+2*lwinAttr.get_border_width() == pattr.get_width()
1584 && lwinAttr.get_height()+2*lwinAttr.get_border_width() == pattr.get_height())
1585 {
1586 insLog.log(Level.FINEST, "Double reparenting detected, pattr({2})={0}, lwinAttr({3})={1}",
1587 new Object[] {lwinAttr, pattr, parent, window});
1588 lwinAttr.set_x(pattr.get_x());
1589 lwinAttr.set_y(pattr.get_y());
1590 lwinAttr.set_border_width(lwinAttr.get_border_width()+pattr.get_border_width());
1591
1592 final long grand_parent = XlibUtil.getParentWindow(parent);
1593
1594 if (grand_parent == lwinAttr.get_root()) {
1595 // This is not double-reparenting in a
1596 // general sense - we simply don't have
1597 // correct insets - return null to try to
1598 // get insets later
1599 return null;
1600 } else {
1601 parent = grand_parent;
1602 XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1603 parent,
1604 pattr.pData);
1605 }
1606 }
1607 /*
1608 * XXX: To be absolutely correct, we'd need to take
1609 * parent's border-width into account too, but the
1610 * rest of the code is happily unaware about border
1611 * widths and inner/outer distinction, so for the time
1612 * being, just ignore it.
1613 */
1614 insLog.log(Level.FINEST, "Attrs before calculation: pattr({2})={0}, lwinAttr({3})={1}",
1615 new Object[] {lwinAttr, pattr, parent, window});
1616 correctWM = new Insets(lwinAttr.get_y() + lwinAttr.get_border_width(),
1617 lwinAttr.get_x() + lwinAttr.get_border_width(),
1618 pattr.get_height() - (lwinAttr.get_y() + lwinAttr.get_height() + 2*lwinAttr.get_border_width()),
1619 pattr.get_width() - (lwinAttr.get_x() + lwinAttr.get_width() + 2*lwinAttr.get_border_width()));
1620 break;
1621 } /* default */
1622 } /* switch (runningWM) */
1623 } finally {
1624 lwinAttr.dispose();
1625 pattr.dispose();
1626 }
1627 }
1628 if (storedInsets.get(win.getClass()) == null) {
1629 storedInsets.put(win.getClass(), correctWM);
1630 }
1631 return correctWM;
1632 }
1633 boolean isDesktopWindow( long w ) {
1634 if (g_net_protocol != null) {
1635 XAtomList wtype = XAtom.get("_NET_WM_WINDOW_TYPE").getAtomListPropertyList( w );
1636 return wtype.contains( XAtom.get("_NET_WM_WINDOW_TYPE_DESKTOP") );
1637 } else {
1638 return false;
1639 }
1640 }
1641
1642 public XNETProtocol getNETProtocol() {
1643 return g_net_protocol;
1644 }
1645
1646 /**
1647 * Sets _NET_WN_ICON property on the window using the arrays of
1648 * raster-data for icons. If icons is null, removes _NET_WM_ICON
1649 * property.
1650 * This method invokes XNETProtocol.setWMIcon() for WMs that
1651 * support NET protocol.
1652 *
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26
27 /**
28 * Ported from awt_wm.c, SCCS v1.11, author Valeriy Ushakov
29 * Author: Denis Mikhalkin
30 */
31 package sun.awt.X11;
32
33 import java.awt.Insets;
34 import java.awt.Frame;
35 import java.awt.Rectangle;
36 import java.security.AccessController;
37 import java.util.Collection;
38 import java.util.HashMap;
39 import java.util.LinkedList;
40 import java.util.logging.Level;
41 import java.util.logging.Logger;
42 import java.util.regex.Matcher;
43 import java.util.regex.Pattern;
44
45 import sun.misc.Unsafe;
46 import sun.security.action.GetIntegerAction;
47
48 /**
49 * Class incapsulating knowledge about window managers in general
50 * Descendants should provide some information about specific window manager.
51 */
52 final class XWM
53 {
54
55 private final static Logger log = Logger.getLogger("sun.awt.X11.XWM");
56 private final static Logger insLog = Logger.getLogger("sun.awt.X11.insets.XWM");
57 private final static Logger stateLog = Logger.getLogger("sun.awt.X11.states.XWM");
58
59 static final XAtom XA_MWM_HINTS = new XAtom();
60
61 private static Unsafe unsafe = XlibWrapper.unsafe;
62
63
64 /* Good old ICCCM */
65 static XAtom XA_WM_STATE = new XAtom();
66
67
122 case KDE2_WM:
123 return "KWM2";
124 case SAWFISH_WM:
125 return "Sawfish";
126 case ICE_WM:
127 return "IceWM";
128 case METACITY_WM:
129 return "Metacity";
130 case COMPIZ_WM:
131 return "Compiz";
132 case LG3D_WM:
133 return "LookingGlass";
134 case UNDETERMINED_WM:
135 default:
136 return "Undetermined WM";
137 }
138 }
139
140
141 int WMID;
142
143 XWM(int WMID) {
144 this.WMID = WMID;
145 initializeProtocols();
146 if (log.isLoggable(Level.FINE)) log.fine("Window manager: " + toString());
147 }
148 int getID() {
149 return WMID;
150 }
151
152
153
154 static XNETProtocol g_net_protocol = null;
155 static XWINProtocol g_win_protocol = null;
156 static boolean isNetWMName(String name) {
157 if (g_net_protocol != null) {
158 return g_net_protocol.isWMName(name);
159 } else {
160 return false;
161 }
162 }
163
164 static void initAtoms() {
165 final Object[][] atomInitList ={
166 { XA_WM_STATE, "WM_STATE" },
167
168 { XA_KDE_NET_WM_FRAME_STRUT, "_KDE_NET_WM_FRAME_STRUT" },
169
170 { XA_E_FRAME_SIZE, "_E_FRAME_SIZE" },
171
172 { XA_KWM_WIN_ICONIFIED, "KWM_WIN_ICONIFIED" },
250 long selection_owner =
251 XlibWrapper.XGetSelectionOwner(XToolkit.getDisplay(),
252 XAtom.get(selection_name).getAtom());
253 if (insLog.isLoggable(Level.FINE)) {
254 insLog.finer("selection owner of " + selection_name
255 + " is " + selection_owner);
256 }
257
258 if (selection_owner != XConstants.None) {
259 return false;
260 }
261
262 winmgr_running = false;
263 substruct.set_event_mask(XConstants.SubstructureRedirectMask);
264
265 XToolkit.WITH_XERROR_HANDLER(detectWMHandler);
266 XlibWrapper.XChangeWindowAttributes(XToolkit.getDisplay(),
267 XToolkit.getDefaultRootWindow(),
268 XConstants.CWEventMask,
269 substruct.pData);
270 XToolkit.XSync();
271 XToolkit.RESTORE_XERROR_HANDLER();
272
273 /*
274 * If no WM is running then our selection for SubstructureRedirect
275 * succeeded and needs to be undone (hey we are *not* a WM ;-).
276 */
277 if (!winmgr_running) {
278 substruct.set_event_mask(0);
279 XlibWrapper.XChangeWindowAttributes(XToolkit.getDisplay(),
280 XToolkit.getDefaultRootWindow(),
281 XConstants.CWEventMask,
282 substruct.pData);
283 if (insLog.isLoggable(Level.FINE)) {
284 insLog.finer("It looks like there is no WM thus NO_WM");
285 }
286 }
287
288 return !winmgr_running;
289 } finally {
290 substruct.dispose();
530 /*
531 * Is KDE2 (KWin) running?
532 */
533 static boolean isKDE2() {
534 return isNetWMName("KWin");
535 }
536
537 static boolean isCompiz() {
538 return isNetWMName("compiz");
539 }
540
541 static boolean isLookingGlass() {
542 return isNetWMName("LG3D");
543 }
544
545 /*
546 * Is Metacity running?
547 */
548 static boolean isMetacity() {
549 return isNetWMName("Metacity");
550 }
551
552 //XXX: Perhaps we need to consider OTHER_WM as non-reparenting also?
553 public static boolean isNonReparentingWM() {
554 return (XWM.getWMID() == XWM.COMPIZ_WM || XWM.getWMID() == XWM.LG3D_WM);
555 }
556
557 /**
558 * Indicates if the window manager does not send a synthetic
559 * ConfigureNotify when the user resizes the window using the left or top
560 * border of the window.
561 * In that cases we receive a real event only.
562 * See 6261336.
563 */
564 public static boolean isNoSyntheticConfigureNotifyOnLeftTopResize() {
565 switch (XWM.getWMID()) {
566 case XWM.CDE_WM:
567 case XWM.MOTIF_WM:
568 case XWM.METACITY_WM:
569 case XWM.SAWFISH_WM:
570 return true;
571 default:
572 return false;
573 }
574 }
575
576 /*
577 * Prepare IceWM check.
578 *
579 * The only way to detect IceWM, seems to be by setting
580 * _ICEWM_WINOPTHINT(_ICEWM_WINOPTHINT/8) on root and checking if it
581 * was immediately deleted by IceWM.
582 *
583 * But messing with PropertyNotify here is way too much trouble, so
584 * approximate the check by setting the property in this function and
585 * checking if it still exists later on.
586 *
587 * Gaa, dirty dances...
588 */
589 static final XAtom XA_ICEWM_WINOPTHINT = new XAtom("_ICEWM_WINOPTHINT", false);
590 static final char opt[] = {
591 'A','W','T','_','I','C','E','W','M','_','T','E','S','T','\0',
592 'a','l','l','W','o','r','k','s','p','a','c','e','s','\0',
593 '0','\0'
594 };
595 static boolean prepareIsIceWM() {
669 /*
670 * Temporary error handler that checks if selecting for
671 * SubstructureRedirect failed.
672 */
673 private static boolean winmgr_running = false;
674 private static XErrorHandler detectWMHandler = new XErrorHandler.XBaseErrorHandler() {
675 @Override
676 public int handleError(long display, XErrorEvent err) {
677 if ((err.get_request_code() == XProtocolConstants.X_ChangeWindowAttributes) &&
678 (err.get_error_code() == XConstants.BadAccess))
679 {
680 winmgr_running = true;
681 return 0;
682 }
683 return super.handleError(display, err);
684 }
685 };
686
687 /*
688 * Make an educated guess about running window manager.
689 * XXX: would be nice to synchronize access to these variables.
690 */
691 private static int awt_wmgr = XWM.UNDETERMINED_WM;
692 private static XWM wm;
693 static XWM getWM() {
694 if (wm == null) {
695 wm = new XWM(awt_wmgr = getWMID());
696 }
697 return wm;
698 }
699
700 /**
701 * Indicates if there's currently a window manager running.
702 */
703 public static boolean isRunning() {
704 return XWM.getWMID() != XWM.NO_WM;
705 }
706
707 /**
708 * Resets the currently detected window manager.
709 * Must be called whenever we detect that the currently running window
710 * manager exits.
711 */
712 public static void reset() {
713 awt_wmgr = XWM.UNDETERMINED_WM;
714 g_net_protocol = null;
715 g_win_protocol = null;
716 wm = null;
717 }
718
719
720 static int getWMID() {
721 if (insLog.isLoggable(Level.FINEST)) {
722 insLog.finest("awt_wmgr = " + awt_wmgr);
723 }
724
725 if (awt_wmgr != XWM.UNDETERMINED_WM) {
726 return awt_wmgr;
727 }
728
729 XSetWindowAttributes substruct = new XSetWindowAttributes();
730 XToolkit.awtLock();
731 try {
732 if (isNoWM()) {
733 awt_wmgr = XWM.NO_WM;
734 return awt_wmgr;
735 }
736
737 // Initialize _NET protocol - used to detect Window Manager.
738 // Later, WM will initialize its own version of protocol
739 XNETProtocol l_net_protocol = g_net_protocol = new XNETProtocol();
740 l_net_protocol.detect();
741 if (log.isLoggable(Level.FINE) && l_net_protocol.active()) {
742 log.fine("_NET_WM_NAME is " + l_net_protocol.getWMName());
743 }
969 if (window.isShowing() && needRemap(window)) {
970 /*
971 * Do the re/mapping at the Xlib level. Since we essentially
972 * work around a WM bug we don't want this hack to be exposed
973 * to Intrinsics (i.e. don't mess with grabs, callbacks etc).
974 */
975 window.xSetVisible(false);
976 XToolkit.XSync();
977 window.xSetVisible(true);
978 }
979 }
980
981 /*
982 * Make specified shell resizable.
983 */
984 static void setShellResizable(XDecoratedPeer window) {
985 if (insLog.isLoggable(Level.FINE)) insLog.fine("Setting shell resizable " + window);
986 XToolkit.awtLock();
987 try {
988 Rectangle shellBounds = window.getShellBounds();
989 Insets insets = window.getNativeInsets();
990 shellBounds.translate(-insets.left, -insets.top);
991 window.updateSizeHints(window.getDimensions());
992 requestWMExtents(window.getWindow());
993 XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), window.getShell(),
994 shellBounds.x, shellBounds.y, shellBounds.width, shellBounds.height);
995 /* REMINDER: will need to revisit when setExtendedStateBounds is added */
996 //Fix for 4320050: Minimum size for java.awt.Frame is not being enforced.
997 //We need to update frame's minimum size, not to reset it
998 removeSizeHints(window, XUtilConstants.PMaxSize);
999 window.updateMinimumSize();
1000
1001 /* Restore decorations */
1002 setShellDecor(window);
1003 } finally {
1004 XToolkit.awtUnlock();
1005 }
1006 }
1007
1008 /*
1009 * Make specified shell non-resizable.
1010 * If justChangeSize is false, update decorations as well.
1052 private <T> void addProtocol(Class<T> protocolInterface, T protocol) {
1053 Collection<T> protocols = getProtocols(protocolInterface);
1054 protocols.add(protocol);
1055 protocolsMap.put(protocolInterface, protocols);
1056 }
1057
1058 boolean supportsDynamicLayout() {
1059 int wm = getWMID();
1060 switch (wm) {
1061 case XWM.ENLIGHTEN_WM:
1062 case XWM.KDE2_WM:
1063 case XWM.SAWFISH_WM:
1064 case XWM.ICE_WM:
1065 case XWM.METACITY_WM:
1066 return true;
1067 case XWM.OPENLOOK_WM:
1068 case XWM.MOTIF_WM:
1069 case XWM.CDE_WM:
1070 return false;
1071 default:
1072 //XXX: so what about compiz?
1073 return false;
1074 }
1075 }
1076
1077
1078 /**
1079 * Check if state is supported.
1080 * Note that a compound state is always reported as not supported.
1081 * Note also that MAXIMIZED_BOTH is considered not a compound state.
1082 * Therefore, a compound state is just ICONIFIED | anything else.
1083 *
1084 */
1085 boolean supportsExtendedState(int state) {
1086 switch (state) {
1087 case Frame.MAXIMIZED_VERT:
1088 case Frame.MAXIMIZED_HORIZ:
1089 /*
1090 * WMs that talk NET/WIN protocol, but do not support
1091 * unidirectional maximization.
1092 */
1269 }
1270 if (net_protocol.doLayerProtocol()) {
1271 addProtocol(XLayerProtocol.class, net_protocol);
1272 }
1273 }
1274 }
1275
1276 XWINProtocol win = g_win_protocol;
1277 if (win != null) {
1278 if (win.active()) {
1279 if (win.doStateProtocol()) {
1280 addProtocol(XStateProtocol.class, win);
1281 }
1282 if (win.doLayerProtocol()) {
1283 addProtocol(XLayerProtocol.class, win);
1284 }
1285 }
1286 }
1287 }
1288
1289 /**
1290 * Returns the "default" insets for the currently running window manager.
1291 */
1292 public final Insets getDefaultInsets() {
1293 switch (WMID) {
1294 case ENLIGHTEN_WM:
1295 return new Insets(19, 4, 4, 4);
1296 case CDE_WM:
1297 return new Insets(28, 6, 6, 6);
1298 case NO_WM:
1299 case LG3D_WM:
1300 return XDecoratedPeer.ZERO_INSETS;
1301 default:
1302 return null;
1303 }
1304 }
1305
1306 /*
1307 * Some buggy WMs ignore window gravity when processing
1308 * ConfigureRequest and position window as if the gravity is Static.
1309 * We work around this in MWindowPeer.pReshape().
1310 *
1311 * Starting with 1.5 we have introduced an Environment variable
1312 * _JAVA_AWT_WM_STATIC_GRAVITY that can be set to indicate to Java
1313 * explicitly that the WM has this behaviour, example is FVWM.
1314 */
1315
1316 static int awtWMStaticGravity = -1;
1317 static boolean configureGravityBuggy() {
1318
1319 if (awtWMStaticGravity == -1) {
1320 awtWMStaticGravity = (XToolkit.getEnv("_JAVA_AWT_WM_STATIC_GRAVITY") != null) ? 1 : 0;
1321 }
1322
1323 if (awtWMStaticGravity == 1) {
1324 return true;
1325 }
1399 0, 4, false, XAtom.XA_CARDINAL);
1400 try {
1401 if (getter.execute() != XConstants.Success
1402 || getter.getData() == 0
1403 || getter.getActualType() != XAtom.XA_CARDINAL
1404 || getter.getActualFormat() != 32)
1405 {
1406 return null;
1407 } else {
1408 return new Insets((int)Native.getCard32(getter.getData(), 2), // top
1409 (int)Native.getCard32(getter.getData(), 0), // left
1410 (int)Native.getCard32(getter.getData(), 3), // bottom
1411 (int)Native.getCard32(getter.getData(), 1)); // right
1412 }
1413 } finally {
1414 getter.dispose();
1415 }
1416 }
1417
1418 /**
1419 * Asks the WM to fill Frame Extents (insets) for the window.
1420 *
1421 * @return true if a request has been actually sent, false otherwise.
1422 */
1423 public static boolean requestWMExtents(long window) {
1424 if (window == XConstants.None) { // not initialized
1425 return false;
1426 }
1427
1428 log.fine("Requesting FRAME_EXTENTS");
1429
1430 XClientMessageEvent msg = new XClientMessageEvent();
1431 msg.zero();
1432 msg.set_type(XConstants.ClientMessage);
1433 msg.set_display(XToolkit.getDisplay());
1434 msg.set_window(window);
1435 msg.set_format(32);
1436 XToolkit.awtLock();
1437 try {
1438 XNETProtocol net_protocol = getWM().getNETProtocol();
1439 if (net_protocol != null && net_protocol.active()) {
1440 msg.set_message_type(XA_NET_REQUEST_FRAME_EXTENTS.getAtom());
1441 XlibWrapper.XSendEvent(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(),
1442 false,
1443 XConstants.SubstructureRedirectMask | XConstants.SubstructureNotifyMask,
1444 msg.getPData());
1445 return true;
1446 }
1447 if (getWMID() == XWM.KDE2_WM) {
1448 msg.set_message_type(XA_KDE_NET_WM_FRAME_STRUT.getAtom());
1449 XlibWrapper.XSendEvent(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(),
1450 false,
1451 XConstants.SubstructureRedirectMask | XConstants.SubstructureNotifyMask,
1452 msg.getPData());
1453 return true;
1454 }
1455 } finally {
1456 XToolkit.awtUnlock();
1457 msg.dispose();
1458 }
1459 return false;
1460 }
1461
1462 /**
1463 * Indicates if the given atom represents a property containing the extents
1464 * of a frame.
1465 */
1466 public static boolean isExtentsPropertyAtom(long atom) {
1467 return atom == XA_KDE_NET_WM_FRAME_STRUT.getAtom()
1468 || atom == XA_NET_FRAME_EXTENTS.getAtom()
1469 || atom == XA_E_FRAME_SIZE.getAtom();
1470 }
1471
1472 // Synchronization: XWM.class
1473 private static Integer syncDelay = null;
1474
1475 /**
1476 * Retrieves the synchronization delay in milliseconds.
1477 *
1478 * Sending a request to the window manager and doing XSync(), we do not
1479 * immediately get the requested result because the window manager is not
1480 * the X server, but a separate process. An example of such request is the
1481 * _NET_REQUEST_FRAME_EXTENTS: the _NET_FRAME_EXTENTS window property may
1482 * be updated with a delay after the request has been dispatched to the X
1483 * server via the XSync() method.
1484 *
1485 * To wait for the response we need a delay value. This method is supposed
1486 * to return a reasonable value for such delay. The default value is
1487 * considered fine when running on a local display. However, for
1488 * connections over TCP, SSH, or other types of connections, the delay may
1489 * have to be increased. For such purposes there's a private system
1490 * property: sun.awt.wmsyncdelay that may be set to an integer value
1491 * representing the number of milliseconds to wait for a window manager's
1492 * reposnse.
1493 */
1494 public static synchronized long getSyncDelay() {
1495 if (syncDelay == null) {
1496 syncDelay = AccessController.doPrivileged(
1497 new GetIntegerAction("sun.awt.wmsyncdelay", 150));
1498 }
1499 return syncDelay;
1500 }
1501
1502 private static XlibWrapper.CheckEventPredicate
1503 checkExtentsUpdateEventPredicate =
1504 new XlibWrapper.CheckEventPredicate() {
1505 public boolean doesMatch(XEvent event) {
1506 return event.get_type() == XConstants.PropertyNotify
1507 && isExtentsPropertyAtom(event.get_xproperty().get_atom());
1508 }
1509 };
1510
1511 /**
1512 * Waits for a PropertyNotify containing the requested frame extents.
1513 *
1514 * @return the PropertyNotify event, or null if nothing got dispatched
1515 */
1516 public static XEvent waitForExtentsUpdateEvent() {
1517 final long TRIES = 5;
1518 long timeout = getSyncDelay();
1519 if (timeout < TRIES) {
1520 timeout = TRIES;
1521 }
1522 final long delay = timeout / TRIES;
1523
1524 XEvent event = null;
1525 do {
1526 XToolkit.awtLock();
1527 try {
1528 XToolkit.XSync();
1529 event = XlibWrapper.CheckIfEvent(XToolkit.getDisplay(),
1530 checkExtentsUpdateEventPredicate);
1531 } finally {
1532 XToolkit.awtUnlock();
1533 }
1534 if (event != null) {
1535 break;
1536 }
1537 try {
1538 Thread.sleep(delay);
1539 } catch (InterruptedException ex) {
1540 }
1541 } while ((timeout -= delay) > 0);
1542
1543 return event;
1544 }
1545
1546 boolean isDesktopWindow( long w ) {
1547 if (g_net_protocol != null) {
1548 XAtomList wtype = XAtom.get("_NET_WM_WINDOW_TYPE").getAtomListPropertyList( w );
1549 return wtype.contains( XAtom.get("_NET_WM_WINDOW_TYPE_DESKTOP") );
1550 } else {
1551 return false;
1552 }
1553 }
1554
1555 public XNETProtocol getNETProtocol() {
1556 return g_net_protocol;
1557 }
1558
1559 /**
1560 * Sets _NET_WN_ICON property on the window using the arrays of
1561 * raster-data for icons. If icons is null, removes _NET_WM_ICON
1562 * property.
1563 * This method invokes XNETProtocol.setWMIcon() for WMs that
1564 * support NET protocol.
1565 *
|