1 /* 2 * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * 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.awt.IconInfo; 34 import sun.misc.Unsafe; 35 import java.awt.Insets; 36 import java.awt.Frame; 37 import java.awt.Rectangle; 38 import java.util.Collection; 39 import java.util.HashMap; 40 import java.util.LinkedList; 41 import java.util.regex.Matcher; 42 import java.util.regex.Pattern; 43 import sun.util.logging.PlatformLogger; 44 45 46 /** 47 * Class incapsulating knowledge about window managers in general 48 * Descendants should provide some information about specific window manager. 49 */ 50 final class XWM 51 { 52 53 private final static PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XWM"); 54 private final static PlatformLogger insLog = PlatformLogger.getLogger("sun.awt.X11.insets.XWM"); 55 private final static PlatformLogger stateLog = PlatformLogger.getLogger("sun.awt.X11.states.XWM"); 56 57 static final XAtom XA_MWM_HINTS = new XAtom(); 58 59 private static Unsafe unsafe = XlibWrapper.unsafe; 60 61 62 /* Good old ICCCM */ 63 static XAtom XA_WM_STATE = new XAtom(); 64 65 66 XAtom XA_UTF8_STRING = XAtom.get("UTF8_STRING"); /* like STRING but encoding is UTF-8 */ 67 68 /* Currently we only care about max_v and max_h in _NET_WM_STATE */ 69 final static int AWT_NET_N_KNOWN_STATES=2; 70 71 /* Enlightenment */ 72 final static XAtom XA_E_FRAME_SIZE = new XAtom(); 73 74 /* KWin (KDE2) */ 75 final static XAtom XA_KDE_NET_WM_FRAME_STRUT = new XAtom(); 76 77 /* KWM (KDE 1.x) OBSOLETE??? */ 78 final static XAtom XA_KWM_WIN_ICONIFIED = new XAtom(); 79 final static XAtom XA_KWM_WIN_MAXIMIZED = new XAtom(); 80 81 /* OpenLook */ 82 final static XAtom XA_OL_DECOR_DEL = new XAtom(); 83 final static XAtom XA_OL_DECOR_HEADER = new XAtom(); 84 final static XAtom XA_OL_DECOR_RESIZE = new XAtom(); 85 final static XAtom XA_OL_DECOR_PIN = new XAtom(); 86 final static XAtom XA_OL_DECOR_CLOSE = new XAtom(); 87 88 /* EWMH */ 89 final static XAtom XA_NET_FRAME_EXTENTS = new XAtom(); 90 final static XAtom XA_NET_REQUEST_FRAME_EXTENTS = new XAtom(); 91 92 final static int 93 UNDETERMINED_WM = 1, 94 NO_WM = 2, 95 OTHER_WM = 3, 96 OPENLOOK_WM = 4, 97 MOTIF_WM = 5, 98 CDE_WM = 6, 99 ENLIGHTEN_WM = 7, 100 KDE2_WM = 8, 101 SAWFISH_WM = 9, 102 ICE_WM = 10, 103 METACITY_WM = 11, 104 COMPIZ_WM = 12, 105 LG3D_WM = 13, 106 CWM_WM = 14, 107 MUTTER_WM = 15; toString()108 public String toString() { 109 switch (WMID) { 110 case NO_WM: 111 return "NO WM"; 112 case OTHER_WM: 113 return "Other WM"; 114 case OPENLOOK_WM: 115 return "OPENLOOK"; 116 case MOTIF_WM: 117 return "MWM"; 118 case CDE_WM: 119 return "DTWM"; 120 case ENLIGHTEN_WM: 121 return "Enlightenment"; 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 CWM_WM: 135 return "CWM"; 136 case MUTTER_WM: 137 return "Mutter"; 138 case UNDETERMINED_WM: 139 default: 140 return "Undetermined WM"; 141 } 142 } 143 144 145 int WMID; 146 static final Insets zeroInsets = new Insets(0, 0, 0, 0); 147 static final Insets defaultInsets = new Insets(25, 5, 5, 5); 148 XWM(int WMID)149 XWM(int WMID) { 150 this.WMID = WMID; 151 initializeProtocols(); 152 if (log.isLoggable(PlatformLogger.Level.FINE)) { 153 log.fine("Window manager: " + toString()); 154 } 155 } getID()156 int getID() { 157 return WMID; 158 } 159 160 normalize(Insets insets)161 static Insets normalize(Insets insets) { 162 if (insets.top > 64 || insets.top < 0) { 163 insets.top = 28; 164 } 165 if (insets.left > 32 || insets.left < 0) { 166 insets.left = 6; 167 } 168 if (insets.right > 32 || insets.right < 0) { 169 insets.right = 6; 170 } 171 if (insets.bottom > 32 || insets.bottom < 0) { 172 insets.bottom = 6; 173 } 174 return insets; 175 } 176 177 static XNETProtocol g_net_protocol = null; 178 static XWINProtocol g_win_protocol = null; isNetWMName(String name)179 static boolean isNetWMName(String name) { 180 if (g_net_protocol != null) { 181 return g_net_protocol.isWMName(name); 182 } else { 183 return false; 184 } 185 } 186 initAtoms()187 static void initAtoms() { 188 final Object[][] atomInitList ={ 189 { XA_WM_STATE, "WM_STATE" }, 190 191 { XA_KDE_NET_WM_FRAME_STRUT, "_KDE_NET_WM_FRAME_STRUT" }, 192 193 { XA_E_FRAME_SIZE, "_E_FRAME_SIZE" }, 194 195 { XA_KWM_WIN_ICONIFIED, "KWM_WIN_ICONIFIED" }, 196 { XA_KWM_WIN_MAXIMIZED, "KWM_WIN_MAXIMIZED" }, 197 198 { XA_OL_DECOR_DEL, "_OL_DECOR_DEL" }, 199 { XA_OL_DECOR_HEADER, "_OL_DECOR_HEADER" }, 200 { XA_OL_DECOR_RESIZE, "_OL_DECOR_RESIZE" }, 201 { XA_OL_DECOR_PIN, "_OL_DECOR_PIN" }, 202 { XA_OL_DECOR_CLOSE, "_OL_DECOR_CLOSE" }, 203 { XA_MWM_HINTS, "_MOTIF_WM_HINTS" }, 204 { XA_NET_FRAME_EXTENTS, "_NET_FRAME_EXTENTS" }, 205 { XA_NET_REQUEST_FRAME_EXTENTS, "_NET_REQUEST_FRAME_EXTENTS" }, 206 }; 207 208 String[] names = new String[atomInitList.length]; 209 for (int index = 0; index < names.length; index++) { 210 names[index] = (String)atomInitList[index][1]; 211 } 212 213 int atomSize = XAtom.getAtomSize(); 214 long atoms = unsafe.allocateMemory(names.length*atomSize); 215 XToolkit.awtLock(); 216 try { 217 int status = XlibWrapper.XInternAtoms(XToolkit.getDisplay(), names, false, atoms); 218 if (status == 0) { 219 return; 220 } 221 for (int atom = 0, atomPtr = 0; atom < names.length; atom++, atomPtr += atomSize) { 222 ((XAtom)(atomInitList[atom][0])).setValues(XToolkit.getDisplay(), names[atom], XAtom.getAtom(atoms + atomPtr)); 223 } 224 } finally { 225 XToolkit.awtUnlock(); 226 unsafe.freeMemory(atoms); 227 } 228 } 229 230 /* 231 * MUST BE CALLED UNDER AWTLOCK. 232 * 233 * If *any* window manager is running? 234 * 235 * According to ICCCM 2.0 section 4.3. 236 * WM will acquire ownership of a selection named WM_Sn, where n is 237 * the screen number. 238 * 239 * No selection owner, but, perhaps it is not ICCCM compliant WM 240 * (e.g. CDE/Sawfish). 241 * Try selecting for SubstructureRedirect, that only one client 242 * can select for, and if the request fails, than some other WM is 243 * already running. 244 * 245 * We also treat eXcursion as NO_WM. 246 */ isNoWM()247 private static boolean isNoWM() { 248 /* 249 * Quick checks for specific servers. 250 */ 251 String vendor_string = XlibWrapper.ServerVendor(XToolkit.getDisplay()); 252 if (vendor_string.indexOf("eXcursion") != -1) { 253 /* 254 * Use NO_WM since in all other aspects eXcursion is like not 255 * having a window manager running. I.e. it does not reparent 256 * top level shells. 257 */ 258 if (insLog.isLoggable(PlatformLogger.Level.FINER)) { 259 insLog.finer("eXcursion means NO_WM"); 260 } 261 return true; 262 } 263 264 XSetWindowAttributes substruct = new XSetWindowAttributes(); 265 try { 266 /* 267 * Let's check an owner of WM_Sn selection for the default screen. 268 */ 269 final long default_screen_number = 270 XlibWrapper.DefaultScreen(XToolkit.getDisplay()); 271 final String selection_name = "WM_S" + default_screen_number; 272 273 long selection_owner = 274 XlibWrapper.XGetSelectionOwner(XToolkit.getDisplay(), 275 XAtom.get(selection_name).getAtom()); 276 if (insLog.isLoggable(PlatformLogger.Level.FINER)) { 277 insLog.finer("selection owner of " + selection_name 278 + " is " + selection_owner); 279 } 280 281 if (selection_owner != XConstants.None) { 282 return false; 283 } 284 285 winmgr_running = false; 286 substruct.set_event_mask(XConstants.SubstructureRedirectMask); 287 288 XErrorHandlerUtil.WITH_XERROR_HANDLER(detectWMHandler); 289 XlibWrapper.XChangeWindowAttributes(XToolkit.getDisplay(), 290 XToolkit.getDefaultRootWindow(), 291 XConstants.CWEventMask, 292 substruct.pData); 293 XErrorHandlerUtil.RESTORE_XERROR_HANDLER(); 294 295 /* 296 * If no WM is running then our selection for SubstructureRedirect 297 * succeeded and needs to be undone (hey we are *not* a WM ;-). 298 */ 299 if (!winmgr_running) { 300 substruct.set_event_mask(0); 301 XlibWrapper.XChangeWindowAttributes(XToolkit.getDisplay(), 302 XToolkit.getDefaultRootWindow(), 303 XConstants.CWEventMask, 304 substruct.pData); 305 if (insLog.isLoggable(PlatformLogger.Level.FINER)) { 306 insLog.finer("It looks like there is no WM thus NO_WM"); 307 } 308 } 309 310 return !winmgr_running; 311 } finally { 312 substruct.dispose(); 313 } 314 } 315 316 static XAtom XA_ENLIGHTENMENT_COMMS = new XAtom("ENLIGHTENMENT_COMMS", false); 317 /* 318 * Helper function for isEnlightenment(). 319 * Enlightenment uses STRING property for its comms window id. Gaaa! 320 * The property is ENLIGHTENMENT_COMMS, STRING/8 and the string format 321 * is "WINID %8x". Gee, I haven't been using scanf for *ages*... :-) 322 */ getECommsWindowIDProperty(long window)323 static long getECommsWindowIDProperty(long window) { 324 325 if (!XA_ENLIGHTENMENT_COMMS.isInterned()) { 326 return 0; 327 } 328 329 WindowPropertyGetter getter = 330 new WindowPropertyGetter(window, XA_ENLIGHTENMENT_COMMS, 0, 14, false, 331 XAtom.XA_STRING); 332 try { 333 int status = getter.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 334 if (status != XConstants.Success || getter.getData() == 0) { 335 return 0; 336 } 337 338 if (getter.getActualType() != XAtom.XA_STRING 339 || getter.getActualFormat() != 8 340 || getter.getNumberOfItems() != 14 || getter.getBytesAfter() != 0) 341 { 342 return 0; 343 } 344 345 // Convert data to String, ASCII 346 byte[] bytes = XlibWrapper.getStringBytes(getter.getData()); 347 String id = new String(bytes); 348 349 if (log.isLoggable(PlatformLogger.Level.FINER)) { 350 log.finer("ENLIGHTENMENT_COMMS is " + id); 351 } 352 353 // Parse WINID 354 Pattern winIdPat = Pattern.compile("WINID\\s+(\\p{XDigit}{0,8})"); 355 try { 356 Matcher match = winIdPat.matcher(id); 357 if (match.matches()) { 358 if (log.isLoggable(PlatformLogger.Level.FINEST)) { 359 log.finest("Match group count: " + match.groupCount()); 360 } 361 String longId = match.group(1); 362 if (log.isLoggable(PlatformLogger.Level.FINEST)) { 363 log.finest("Match group 1 " + longId); 364 } 365 long winid = Long.parseLong(longId, 16); 366 if (log.isLoggable(PlatformLogger.Level.FINER)) { 367 log.finer("Enlightenment communication window " + winid); 368 } 369 return winid; 370 } else { 371 log.finer("ENLIGHTENMENT_COMMS has wrong format"); 372 return 0; 373 } 374 } catch (Exception e) { 375 if (log.isLoggable(PlatformLogger.Level.FINER)) { 376 e.printStackTrace(); 377 } 378 return 0; 379 } 380 } finally { 381 getter.dispose(); 382 } 383 } 384 385 /* 386 * Is Enlightenment WM running? Congruent to awt_wm_checkAnchor, but 387 * uses STRING property peculiar to Enlightenment. 388 */ isEnlightenment()389 static boolean isEnlightenment() { 390 391 long root_xref = getECommsWindowIDProperty(XToolkit.getDefaultRootWindow()); 392 if (root_xref == 0) { 393 return false; 394 } 395 396 long self_xref = getECommsWindowIDProperty(root_xref); 397 if (self_xref != root_xref) { 398 return false; 399 } 400 401 return true; 402 } 403 404 /* 405 * Is CDE running? 406 * 407 * XXX: This is hairy... CDE is MWM as well. It seems we simply test 408 * for default setup and will be bitten if user changes things... 409 * 410 * Check for _DT_SM_WINDOW_INFO(_DT_SM_WINDOW_INFO) on root. Take the 411 * second element of the property and check for presence of 412 * _DT_SM_STATE_INFO(_DT_SM_STATE_INFO) on that window. 413 * 414 * XXX: Any header that defines this structures??? 415 */ 416 static final XAtom XA_DT_SM_WINDOW_INFO = new XAtom("_DT_SM_WINDOW_INFO", false); 417 static final XAtom XA_DT_SM_STATE_INFO = new XAtom("_DT_SM_STATE_INFO", false); isCDE()418 static boolean isCDE() { 419 420 if (!XA_DT_SM_WINDOW_INFO.isInterned()) { 421 if (log.isLoggable(PlatformLogger.Level.FINER)) { 422 log.finer("{0} is not interned", XA_DT_SM_WINDOW_INFO); 423 } 424 return false; 425 } 426 427 WindowPropertyGetter getter = 428 new WindowPropertyGetter(XToolkit.getDefaultRootWindow(), 429 XA_DT_SM_WINDOW_INFO, 0, 2, 430 false, XA_DT_SM_WINDOW_INFO); 431 try { 432 int status = getter.execute(); 433 if (status != XConstants.Success || getter.getData() == 0) { 434 log.finer("Getting of _DT_SM_WINDOW_INFO is not successfull"); 435 return false; 436 } 437 if (getter.getActualType() != XA_DT_SM_WINDOW_INFO.getAtom() 438 || getter.getActualFormat() != 32 439 || getter.getNumberOfItems() != 2 || getter.getBytesAfter() != 0) 440 { 441 log.finer("Wrong format of _DT_SM_WINDOW_INFO"); 442 return false; 443 } 444 445 long wmwin = Native.getWindow(getter.getData(), 1); //unsafe.getInt(getter.getData()+4); 446 447 if (wmwin == 0) { 448 log.fine("WARNING: DT_SM_WINDOW_INFO exists but returns zero windows"); 449 return false; 450 } 451 452 /* Now check that this window has _DT_SM_STATE_INFO (ignore contents) */ 453 if (!XA_DT_SM_STATE_INFO.isInterned()) { 454 if (log.isLoggable(PlatformLogger.Level.FINER)) { 455 log.finer("{0} is not interned", XA_DT_SM_STATE_INFO); 456 } 457 return false; 458 } 459 WindowPropertyGetter getter2 = 460 new WindowPropertyGetter(wmwin, XA_DT_SM_STATE_INFO, 0, 1, 461 false, XA_DT_SM_STATE_INFO); 462 try { 463 status = getter2.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance()); 464 465 466 if (status != XConstants.Success || getter2.getData() == 0) { 467 log.finer("Getting of _DT_SM_STATE_INFO is not successfull"); 468 return false; 469 } 470 if (getter2.getActualType() != XA_DT_SM_STATE_INFO.getAtom() 471 || getter2.getActualFormat() != 32) 472 { 473 log.finer("Wrong format of _DT_SM_STATE_INFO"); 474 return false; 475 } 476 477 return true; 478 } finally { 479 getter2.dispose(); 480 } 481 } finally { 482 getter.dispose(); 483 } 484 } 485 486 /* 487 * Is MWM running? (Note that CDE will test positive as well). 488 * 489 * Check for _MOTIF_WM_INFO(_MOTIF_WM_INFO) on root. Take the 490 * second element of the property and check for presence of 491 * _DT_SM_STATE_INFO(_DT_SM_STATE_INFO) on that window. 492 */ 493 static final XAtom XA_MOTIF_WM_INFO = new XAtom("_MOTIF_WM_INFO", false); 494 static final XAtom XA_DT_WORKSPACE_CURRENT = new XAtom("_DT_WORKSPACE_CURRENT", false); isMotif()495 static boolean isMotif() { 496 497 if (!(XA_MOTIF_WM_INFO.isInterned()/* && XA_DT_WORKSPACE_CURRENT.isInterned()*/) ) { 498 return false; 499 } 500 501 WindowPropertyGetter getter = 502 new WindowPropertyGetter(XToolkit.getDefaultRootWindow(), 503 XA_MOTIF_WM_INFO, 0, 504 MWMConstants.PROP_MOTIF_WM_INFO_ELEMENTS, 505 false, XA_MOTIF_WM_INFO); 506 try { 507 int status = getter.execute(); 508 509 if (status != XConstants.Success || getter.getData() == 0) { 510 return false; 511 } 512 513 if (getter.getActualType() != XA_MOTIF_WM_INFO.getAtom() 514 || getter.getActualFormat() != 32 515 || getter.getNumberOfItems() != MWMConstants.PROP_MOTIF_WM_INFO_ELEMENTS 516 || getter.getBytesAfter() != 0) 517 { 518 return false; 519 } 520 521 long wmwin = Native.getLong(getter.getData(), 1); 522 if (wmwin != 0) { 523 if (XA_DT_WORKSPACE_CURRENT.isInterned()) { 524 /* Now check that this window has _DT_WORKSPACE_CURRENT */ 525 XAtom[] curws = XA_DT_WORKSPACE_CURRENT.getAtomListProperty(wmwin); 526 if (curws.length == 0) { 527 return false; 528 } 529 return true; 530 } else { 531 // No DT_WORKSPACE, however in our tests MWM sometimes can be without desktop - 532 // and that is still MWM. So simply check for the validity of this window 533 // (through WM_STATE property). 534 WindowPropertyGetter state_getter = 535 new WindowPropertyGetter(wmwin, 536 XA_WM_STATE, 537 0, 1, false, 538 XA_WM_STATE); 539 try { 540 if (state_getter.execute() == XConstants.Success && 541 state_getter.getData() != 0 && 542 state_getter.getActualType() == XA_WM_STATE.getAtom()) 543 { 544 return true; 545 } 546 } finally { 547 state_getter.dispose(); 548 } 549 } 550 } 551 } finally { 552 getter.dispose(); 553 } 554 return false; 555 } 556 557 /* 558 * Is Sawfish running? 559 */ isSawfish()560 static boolean isSawfish() { 561 return isNetWMName("Sawfish"); 562 } 563 564 /* 565 * Is KDE2 (KWin) running? 566 */ isKDE2()567 static boolean isKDE2() { 568 return isNetWMName("KWin"); 569 } 570 isCompiz()571 static boolean isCompiz() { 572 return isNetWMName("compiz"); 573 } 574 isLookingGlass()575 static boolean isLookingGlass() { 576 return isNetWMName("LG3D"); 577 } 578 isCWM()579 static boolean isCWM() { 580 return isNetWMName("CWM"); 581 } 582 583 /* 584 * Is Metacity running? 585 */ isMetacity()586 static boolean isMetacity() { 587 return isNetWMName("Metacity"); 588 // || ( 589 // XA_NET_SUPPORTING_WM_CHECK. 590 // getIntProperty(XToolkit.getDefaultRootWindow(), XA_NET_SUPPORTING_WM_CHECK. 591 // getIntProperty(XToolkit.getDefaultRootWindow(), XAtom.XA_CARDINAL)) == 0); 592 } 593 isMutter()594 static boolean isMutter() { 595 return isNetWMName("Mutter") || isNetWMName("GNOME Shell"); 596 } 597 598 static int awtWMNonReparenting = -1; isNonReparentingWM()599 static boolean isNonReparentingWM() { 600 if (awtWMNonReparenting == -1) { 601 awtWMNonReparenting = (XToolkit.getEnv("_JAVA_AWT_WM_NONREPARENTING") != null) ? 1 : 0; 602 } 603 return (awtWMNonReparenting == 1 || XWM.getWMID() == XWM.COMPIZ_WM 604 || XWM.getWMID() == XWM.LG3D_WM || XWM.getWMID() == XWM.CWM_WM); 605 } 606 607 /* 608 * Prepare IceWM check. 609 * 610 * The only way to detect IceWM, seems to be by setting 611 * _ICEWM_WINOPTHINT(_ICEWM_WINOPTHINT/8) on root and checking if it 612 * was immediately deleted by IceWM. 613 * 614 * But messing with PropertyNotify here is way too much trouble, so 615 * approximate the check by setting the property in this function and 616 * checking if it still exists later on. 617 * 618 * Gaa, dirty dances... 619 */ 620 static final XAtom XA_ICEWM_WINOPTHINT = new XAtom("_ICEWM_WINOPTHINT", false); 621 static final char opt[] = { 622 'A','W','T','_','I','C','E','W','M','_','T','E','S','T','\0', 623 'a','l','l','W','o','r','k','s','p','a','c','e','s','\0', 624 '0','\0' 625 }; prepareIsIceWM()626 static boolean prepareIsIceWM() { 627 /* 628 * Choose something innocuous: "AWT_ICEWM_TEST allWorkspaces 0". 629 * IceWM expects "class\0option\0arg\0" with zero bytes as delimiters. 630 */ 631 632 if (!XA_ICEWM_WINOPTHINT.isInterned()) { 633 if (log.isLoggable(PlatformLogger.Level.FINER)) { 634 log.finer("{0} is not interned", XA_ICEWM_WINOPTHINT); 635 } 636 return false; 637 } 638 639 XToolkit.awtLock(); 640 try { 641 XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance()); 642 XlibWrapper.XChangePropertyS(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(), 643 XA_ICEWM_WINOPTHINT.getAtom(), 644 XA_ICEWM_WINOPTHINT.getAtom(), 645 8, XConstants.PropModeReplace, 646 new String(opt)); 647 XErrorHandlerUtil.RESTORE_XERROR_HANDLER(); 648 649 if ((XErrorHandlerUtil.saved_error != null) && 650 (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) { 651 log.finer("Erorr getting XA_ICEWM_WINOPTHINT property"); 652 return false; 653 } 654 log.finer("Prepared for IceWM detection"); 655 return true; 656 } finally { 657 XToolkit.awtUnlock(); 658 } 659 } 660 661 /* 662 * Is IceWM running? 663 * 664 * Note well: Only call this if awt_wm_prepareIsIceWM succeeded, or a 665 * false positive will be reported. 666 */ isIceWM()667 static boolean isIceWM() { 668 if (!XA_ICEWM_WINOPTHINT.isInterned()) { 669 if (log.isLoggable(PlatformLogger.Level.FINER)) { 670 log.finer("{0} is not interned", XA_ICEWM_WINOPTHINT); 671 } 672 return false; 673 } 674 675 WindowPropertyGetter getter = 676 new WindowPropertyGetter(XToolkit.getDefaultRootWindow(), 677 XA_ICEWM_WINOPTHINT, 0, 0xFFFF, 678 true, XA_ICEWM_WINOPTHINT); 679 try { 680 int status = getter.execute(); 681 boolean res = (status == XConstants.Success && getter.getActualType() != 0); 682 if (log.isLoggable(PlatformLogger.Level.FINER)) { 683 log.finer("Status getting XA_ICEWM_WINOPTHINT: " + !res); 684 } 685 return !res || isNetWMName("IceWM"); 686 } finally { 687 getter.dispose(); 688 } 689 } 690 691 /* 692 * Is OpenLook WM running? 693 * 694 * This one is pretty lame, but the only property peculiar to OLWM is 695 * _SUN_WM_PROTOCOLS(ATOM[]). Fortunately, olwm deletes it on exit. 696 */ 697 static final XAtom XA_SUN_WM_PROTOCOLS = new XAtom("_SUN_WM_PROTOCOLS", false); isOpenLook()698 static boolean isOpenLook() { 699 if (!XA_SUN_WM_PROTOCOLS.isInterned()) { 700 return false; 701 } 702 703 XAtom[] list = XA_SUN_WM_PROTOCOLS.getAtomListProperty(XToolkit.getDefaultRootWindow()); 704 return (list.length != 0); 705 } 706 707 /* 708 * Temporary error handler that checks if selecting for 709 * SubstructureRedirect failed. 710 */ 711 private static boolean winmgr_running = false; 712 private static XErrorHandler detectWMHandler = new XErrorHandler.XBaseErrorHandler() { 713 @Override 714 public int handleError(long display, XErrorEvent err) { 715 if ((err.get_request_code() == XProtocolConstants.X_ChangeWindowAttributes) && 716 (err.get_error_code() == XConstants.BadAccess)) 717 { 718 winmgr_running = true; 719 return 0; 720 } 721 return super.handleError(display, err); 722 } 723 }; 724 725 /* 726 * Make an educated guess about running window manager. 727 * XXX: ideally, we should detect wm restart. 728 */ 729 static int awt_wmgr = XWM.UNDETERMINED_WM; 730 static XWM wm; getWM()731 static XWM getWM() { 732 if (wm == null) { 733 wm = new XWM(awt_wmgr = getWMID()/*XWM.OTHER_WM*/); 734 } 735 return wm; 736 } getWMID()737 static int getWMID() { 738 if (insLog.isLoggable(PlatformLogger.Level.FINEST)) { 739 insLog.finest("awt_wmgr = " + awt_wmgr); 740 } 741 /* 742 * Ideally, we should support cases when a different WM is started 743 * during a Java app lifetime. 744 */ 745 746 if (awt_wmgr != XWM.UNDETERMINED_WM) { 747 return awt_wmgr; 748 } 749 750 XSetWindowAttributes substruct = new XSetWindowAttributes(); 751 XToolkit.awtLock(); 752 try { 753 if (isNoWM()) { 754 awt_wmgr = XWM.NO_WM; 755 return awt_wmgr; 756 } 757 758 // Initialize _NET protocol - used to detect Window Manager. 759 // Later, WM will initialize its own version of protocol 760 XNETProtocol l_net_protocol = g_net_protocol = new XNETProtocol(); 761 l_net_protocol.detect(); 762 if (log.isLoggable(PlatformLogger.Level.FINE) && l_net_protocol.active()) { 763 log.fine("_NET_WM_NAME is " + l_net_protocol.getWMName()); 764 } 765 XWINProtocol win = g_win_protocol = new XWINProtocol(); 766 win.detect(); 767 768 /* actual check for IceWM to follow below */ 769 boolean doIsIceWM = prepareIsIceWM(); /* and let IceWM to act */ 770 771 /* 772 * Ok, some WM is out there. Check which one by testing for 773 * "distinguishing" atoms. 774 */ 775 if (isEnlightenment()) { 776 awt_wmgr = XWM.ENLIGHTEN_WM; 777 } else if (isMetacity()) { 778 awt_wmgr = XWM.METACITY_WM; 779 } else if (isMutter()) { 780 awt_wmgr = XWM.MUTTER_WM; 781 } else if (isSawfish()) { 782 awt_wmgr = XWM.SAWFISH_WM; 783 } else if (isKDE2()) { 784 awt_wmgr =XWM.KDE2_WM; 785 } else if (isCompiz()) { 786 awt_wmgr = XWM.COMPIZ_WM; 787 } else if (isLookingGlass()) { 788 awt_wmgr = LG3D_WM; 789 } else if (isCWM()) { 790 awt_wmgr = CWM_WM; 791 } else if (doIsIceWM && isIceWM()) { 792 awt_wmgr = XWM.ICE_WM; 793 } 794 /* 795 * We don't check for legacy WM when we already know that WM 796 * supports WIN or _NET wm spec. 797 */ 798 else if (l_net_protocol.active()) { 799 awt_wmgr = XWM.OTHER_WM; 800 } else if (win.active()) { 801 awt_wmgr = XWM.OTHER_WM; 802 } 803 /* 804 * Check for legacy WMs. 805 */ 806 else if (isCDE()) { /* XXX: must come before isMotif */ 807 awt_wmgr = XWM.CDE_WM; 808 } else if (isMotif()) { 809 awt_wmgr = XWM.MOTIF_WM; 810 } else if (isOpenLook()) { 811 awt_wmgr = XWM.OPENLOOK_WM; 812 } else { 813 awt_wmgr = XWM.OTHER_WM; 814 } 815 816 return awt_wmgr; 817 } finally { 818 XToolkit.awtUnlock(); 819 substruct.dispose(); 820 } 821 } 822 823 824 /*****************************************************************************\ 825 * 826 * Size and decoration hints ... 827 * 828 \*****************************************************************************/ 829 830 831 /* 832 * Remove size hints specified by the mask. 833 * XXX: Why do we need this in the first place??? 834 */ removeSizeHints(XDecoratedPeer window, long mask)835 static void removeSizeHints(XDecoratedPeer window, long mask) { 836 mask &= XUtilConstants.PMaxSize | XUtilConstants.PMinSize; 837 838 XToolkit.awtLock(); 839 try { 840 XSizeHints hints = window.getHints(); 841 if ((hints.get_flags() & mask) == 0) { 842 return; 843 } 844 845 hints.set_flags(hints.get_flags() & ~mask); 846 if (insLog.isLoggable(PlatformLogger.Level.FINER)) { 847 insLog.finer("Setting hints, flags " + XlibWrapper.hintsToString(hints.get_flags())); 848 } 849 XlibWrapper.XSetWMNormalHints(XToolkit.getDisplay(), 850 window.getWindow(), 851 hints.pData); 852 } finally { 853 XToolkit.awtUnlock(); 854 } 855 } 856 857 /* 858 * If MWM_DECOR_ALL bit is set, then the rest of the bit-mask is taken 859 * to be subtracted from the decorations. Normalize decoration spec 860 * so that we can map motif decor to something else bit-by-bit in the 861 * rest of the code. 862 */ normalizeMotifDecor(int decorations)863 static int normalizeMotifDecor(int decorations) { 864 if ((decorations & MWMConstants.MWM_DECOR_ALL) == 0) { 865 return decorations; 866 } 867 int d = MWMConstants.MWM_DECOR_BORDER | MWMConstants.MWM_DECOR_RESIZEH 868 | MWMConstants.MWM_DECOR_TITLE 869 | MWMConstants.MWM_DECOR_MENU | MWMConstants.MWM_DECOR_MINIMIZE 870 | MWMConstants.MWM_DECOR_MAXIMIZE; 871 d &= ~decorations; 872 return d; 873 } 874 875 /* 876 * If MWM_FUNC_ALL bit is set, then the rest of the bit-mask is taken 877 * to be subtracted from the functions. Normalize function spec 878 * so that we can map motif func to something else bit-by-bit in the 879 * rest of the code. 880 */ normalizeMotifFunc(int functions)881 static int normalizeMotifFunc(int functions) { 882 if ((functions & MWMConstants.MWM_FUNC_ALL) == 0) { 883 return functions; 884 } 885 int f = MWMConstants.MWM_FUNC_RESIZE | 886 MWMConstants.MWM_FUNC_MOVE | 887 MWMConstants.MWM_FUNC_MAXIMIZE | 888 MWMConstants.MWM_FUNC_MINIMIZE | 889 MWMConstants.MWM_FUNC_CLOSE; 890 f &= ~functions; 891 return f; 892 } 893 894 /* 895 * Infer OL properties from MWM decorations. 896 * Use _OL_DECOR_DEL(ATOM[]) to remove unwanted ones. 897 */ setOLDecor(XWindow window, boolean resizable, int decorations)898 static void setOLDecor(XWindow window, boolean resizable, int decorations) { 899 if (window == null) { 900 return; 901 } 902 903 XAtomList decorDel = new XAtomList(); 904 decorations = normalizeMotifDecor(decorations); 905 if (insLog.isLoggable(PlatformLogger.Level.FINER)) { 906 insLog.finer("Setting OL_DECOR to " + Integer.toBinaryString(decorations)); 907 } 908 if ((decorations & MWMConstants.MWM_DECOR_TITLE) == 0) { 909 decorDel.add(XA_OL_DECOR_HEADER); 910 } 911 if ((decorations & (MWMConstants.MWM_DECOR_RESIZEH | MWMConstants.MWM_DECOR_MAXIMIZE)) == 0) { 912 decorDel.add(XA_OL_DECOR_RESIZE); 913 } 914 if ((decorations & (MWMConstants.MWM_DECOR_MENU | 915 MWMConstants.MWM_DECOR_MAXIMIZE | 916 MWMConstants.MWM_DECOR_MINIMIZE)) == 0) 917 { 918 decorDel.add(XA_OL_DECOR_CLOSE); 919 } 920 if (decorDel.size() == 0) { 921 insLog.finer("Deleting OL_DECOR"); 922 XA_OL_DECOR_DEL.DeleteProperty(window); 923 } else { 924 if (insLog.isLoggable(PlatformLogger.Level.FINER)) { 925 insLog.finer("Setting OL_DECOR to " + decorDel); 926 } 927 XA_OL_DECOR_DEL.setAtomListProperty(window, decorDel); 928 } 929 } 930 931 /* 932 * Set MWM decorations. Set MWM functions depending on resizability. 933 */ setMotifDecor(XWindow window, boolean resizable, int decorations, int functions)934 static void setMotifDecor(XWindow window, boolean resizable, int decorations, int functions) { 935 /* Apparently some WMs don't implement MWM_*_ALL semantic correctly */ 936 if ((decorations & MWMConstants.MWM_DECOR_ALL) != 0 937 && (decorations != MWMConstants.MWM_DECOR_ALL)) 938 { 939 decorations = normalizeMotifDecor(decorations); 940 } 941 if ((functions & MWMConstants.MWM_FUNC_ALL) != 0 942 && (functions != MWMConstants.MWM_FUNC_ALL)) 943 { 944 functions = normalizeMotifFunc(functions); 945 } 946 947 PropMwmHints hints = window.getMWMHints(); 948 hints.set_flags(hints.get_flags() | 949 MWMConstants.MWM_HINTS_FUNCTIONS | 950 MWMConstants.MWM_HINTS_DECORATIONS); 951 hints.set_functions(functions); 952 hints.set_decorations(decorations); 953 954 if (stateLog.isLoggable(PlatformLogger.Level.FINER)) { 955 stateLog.finer("Setting MWM_HINTS to " + hints); 956 } 957 window.setMWMHints(hints); 958 } 959 960 /* 961 * Under some window managers if shell is already mapped, we MUST 962 * unmap and later remap in order to effect the changes we make in the 963 * window manager decorations. 964 * 965 * N.B. This unmapping / remapping of the shell exposes a bug in 966 * X/Motif or the Motif Window Manager. When you attempt to map a 967 * widget which is positioned (partially) off-screen, the window is 968 * relocated to be entirely on screen. Good idea. But if both the x 969 * and the y coordinates are less than the origin (0,0), the first 970 * (re)map will move the window to the origin, and any subsequent 971 * (re)map will relocate the window at some other point on the screen. 972 * I have written a short Motif test program to discover this bug. 973 * This should occur infrequently and it does not cause any real 974 * problem. So for now we'll let it be. 975 */ needRemap(XDecoratedPeer window)976 static boolean needRemap(XDecoratedPeer window) { 977 // Don't remap EmbeddedFrame, 978 // e.g. for TrayIcon it causes problems. 979 return !window.isEmbedded(); 980 } 981 982 /* 983 * Set decoration hints on the shell to wdata->decor adjusted 984 * appropriately if not resizable. 985 */ setShellDecor(XDecoratedPeer window)986 static void setShellDecor(XDecoratedPeer window) { 987 int decorations = window.getDecorations(); 988 int functions = window.getFunctions(); 989 boolean resizable = window.isResizable(); 990 991 if (!resizable) { 992 if ((decorations & MWMConstants.MWM_DECOR_ALL) != 0) { 993 decorations |= MWMConstants.MWM_DECOR_RESIZEH | MWMConstants.MWM_DECOR_MAXIMIZE; 994 } else { 995 decorations &= ~(MWMConstants.MWM_DECOR_RESIZEH | MWMConstants.MWM_DECOR_MAXIMIZE); 996 } 997 } 998 setMotifDecor(window, resizable, decorations, functions); 999 setOLDecor(window, resizable, decorations); 1000 1001 /* Some WMs need remap to redecorate the window */ 1002 if (window.isShowing() && needRemap(window)) { 1003 /* 1004 * Do the re/mapping at the Xlib level. Since we essentially 1005 * work around a WM bug we don't want this hack to be exposed 1006 * to Intrinsics (i.e. don't mess with grabs, callbacks etc). 1007 */ 1008 window.xSetVisible(false); 1009 XToolkit.XSync(); 1010 window.xSetVisible(true); 1011 } 1012 } 1013 1014 /* 1015 * Make specified shell resizable. 1016 */ setShellResizable(XDecoratedPeer window)1017 static void setShellResizable(XDecoratedPeer window) { 1018 if (insLog.isLoggable(PlatformLogger.Level.FINE)) { 1019 insLog.fine("Setting shell resizable " + window); 1020 } 1021 XToolkit.awtLock(); 1022 try { 1023 Rectangle shellBounds = window.getShellBounds(); 1024 shellBounds.translate(-window.currentInsets.left, -window.currentInsets.top); 1025 window.updateSizeHints(window.getDimensions()); 1026 requestWMExtents(window.getWindow()); 1027 XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), window.getShell(), 1028 shellBounds.x, shellBounds.y, shellBounds.width, shellBounds.height); 1029 /* REMINDER: will need to revisit when setExtendedStateBounds is added */ 1030 //Fix for 4320050: Minimum size for java.awt.Frame is not being enforced. 1031 //We need to update frame's minimum size, not to reset it 1032 removeSizeHints(window, XUtilConstants.PMaxSize); 1033 window.updateMinimumSize(); 1034 1035 /* Restore decorations */ 1036 setShellDecor(window); 1037 } finally { 1038 XToolkit.awtUnlock(); 1039 } 1040 } 1041 1042 /* 1043 * Make specified shell non-resizable. 1044 * If justChangeSize is false, update decorations as well. 1045 * @param shellBounds bounds of the shell window 1046 */ setShellNotResizable(XDecoratedPeer window, WindowDimensions newDimensions, Rectangle shellBounds, boolean justChangeSize)1047 static void setShellNotResizable(XDecoratedPeer window, WindowDimensions newDimensions, Rectangle shellBounds, 1048 boolean justChangeSize) 1049 { 1050 if (insLog.isLoggable(PlatformLogger.Level.FINE)) { 1051 insLog.fine("Setting non-resizable shell " + window + ", dimensions " + newDimensions + 1052 ", shellBounds " + shellBounds +", just change size: " + justChangeSize); 1053 } 1054 XToolkit.awtLock(); 1055 try { 1056 /* Fix min/max size hints at the specified values */ 1057 if (!shellBounds.isEmpty()) { 1058 window.updateSizeHints(newDimensions); 1059 requestWMExtents(window.getWindow()); 1060 XToolkit.XSync(); 1061 XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), window.getShell(), 1062 shellBounds.x, shellBounds.y, shellBounds.width, shellBounds.height); 1063 } 1064 if (!justChangeSize) { /* update decorations */ 1065 setShellDecor(window); 1066 } 1067 } finally { 1068 XToolkit.awtUnlock(); 1069 } 1070 } 1071 1072 /*****************************************************************************\ 1073 * Protocols support 1074 */ 1075 private HashMap<Class<?>, Collection<?>> protocolsMap = new HashMap<Class<?>, Collection<?>>(); 1076 /** 1077 * Returns all protocols supporting given protocol interface 1078 */ getProtocols(Class<T> protocolInterface)1079 <T> Collection<T> getProtocols(Class<T> protocolInterface) { 1080 Collection<T> res = (Collection<T>) protocolsMap.get(protocolInterface); 1081 if (res != null) { 1082 return res; 1083 } else { 1084 return new LinkedList<T>(); 1085 } 1086 } 1087 addProtocol(Class<T> protocolInterface, T protocol)1088 private <T> void addProtocol(Class<T> protocolInterface, T protocol) { 1089 Collection<T> protocols = getProtocols(protocolInterface); 1090 protocols.add(protocol); 1091 protocolsMap.put(protocolInterface, protocols); 1092 } 1093 supportsDynamicLayout()1094 boolean supportsDynamicLayout() { 1095 int wm = getWMID(); 1096 switch (wm) { 1097 case XWM.ENLIGHTEN_WM: 1098 case XWM.KDE2_WM: 1099 case XWM.SAWFISH_WM: 1100 case XWM.ICE_WM: 1101 case XWM.METACITY_WM: 1102 return true; 1103 case XWM.OPENLOOK_WM: 1104 case XWM.MOTIF_WM: 1105 case XWM.CDE_WM: 1106 return false; 1107 default: 1108 return false; 1109 } 1110 } 1111 1112 1113 /** 1114 * Check if state is supported. 1115 * Note that a compound state is always reported as not supported. 1116 * Note also that MAXIMIZED_BOTH is considered not a compound state. 1117 * Therefore, a compound state is just ICONIFIED | anything else. 1118 * 1119 */ supportsExtendedState(int state)1120 boolean supportsExtendedState(int state) { 1121 switch (state) { 1122 case Frame.MAXIMIZED_VERT: 1123 case Frame.MAXIMIZED_HORIZ: 1124 /* 1125 * WMs that talk NET/WIN protocol, but do not support 1126 * unidirectional maximization. 1127 */ 1128 if (getWMID() == METACITY_WM) { 1129 /* "This is a deliberate policy decision." -hp */ 1130 return false; 1131 } 1132 /* FALLTROUGH */ 1133 case Frame.MAXIMIZED_BOTH: 1134 for (XStateProtocol proto : getProtocols(XStateProtocol.class)) { 1135 if (proto.supportsState(state)) { 1136 return true; 1137 } 1138 } 1139 default: 1140 return false; 1141 } 1142 } 1143 1144 /*****************************************************************************\ 1145 * 1146 * Reading state from different protocols 1147 * 1148 \*****************************************************************************/ 1149 1150 getExtendedState(XWindowPeer window)1151 int getExtendedState(XWindowPeer window) { 1152 int state = 0; 1153 for (XStateProtocol proto : getProtocols(XStateProtocol.class)) { 1154 state |= proto.getState(window); 1155 } 1156 if (state != 0) { 1157 return state; 1158 } else { 1159 return Frame.NORMAL; 1160 } 1161 } 1162 1163 /*****************************************************************************\ 1164 * 1165 * Notice window state change when WM changes a property on the window ... 1166 * 1167 \*****************************************************************************/ 1168 1169 1170 /* 1171 * Check if property change is a window state protocol message. 1172 */ isStateChange(XDecoratedPeer window, XPropertyEvent e)1173 boolean isStateChange(XDecoratedPeer window, XPropertyEvent e) { 1174 if (!window.isShowing()) { 1175 stateLog.finer("Window is not showing"); 1176 return false; 1177 } 1178 1179 int wm_state = window.getWMState(); 1180 if (wm_state == XUtilConstants.WithdrawnState) { 1181 stateLog.finer("WithdrawnState"); 1182 return false; 1183 } else { 1184 if (stateLog.isLoggable(PlatformLogger.Level.FINER)) { 1185 stateLog.finer("Window WM_STATE is " + wm_state); 1186 } 1187 } 1188 boolean is_state_change = false; 1189 if (e.get_atom() == XA_WM_STATE.getAtom()) { 1190 is_state_change = true; 1191 } 1192 1193 for (XStateProtocol proto : getProtocols(XStateProtocol.class)) { 1194 is_state_change |= proto.isStateChange(e); 1195 if (stateLog.isLoggable(PlatformLogger.Level.FINEST)) { 1196 stateLog.finest(proto + ": is state changed = " + is_state_change); 1197 } 1198 } 1199 return is_state_change; 1200 } 1201 1202 /* 1203 * Returns current state (including extended) of a given window. 1204 */ getState(XDecoratedPeer window)1205 int getState(XDecoratedPeer window) { 1206 int res = 0; 1207 final int wm_state = window.getWMState(); 1208 if (wm_state == XUtilConstants.IconicState) { 1209 res = Frame.ICONIFIED; 1210 } else { 1211 res = Frame.NORMAL; 1212 } 1213 res |= getExtendedState(window); 1214 return res; 1215 } 1216 1217 /*****************************************************************************\ 1218 * 1219 * Setting/changing window state ... 1220 * 1221 \*****************************************************************************/ 1222 1223 /** 1224 * Moves window to the specified layer, layer is one of the constants defined 1225 * in XLayerProtocol 1226 */ setLayer(XWindowPeer window, int layer)1227 void setLayer(XWindowPeer window, int layer) { 1228 for (XLayerProtocol proto : getProtocols(XLayerProtocol.class)) { 1229 if (proto.supportsLayer(layer)) { 1230 proto.setLayer(window, layer); 1231 } 1232 } 1233 XToolkit.XSync(); 1234 } 1235 setExtendedState(XWindowPeer window, int state)1236 void setExtendedState(XWindowPeer window, int state) { 1237 for (XStateProtocol proto : getProtocols(XStateProtocol.class)) { 1238 if (proto.supportsState(state)) { 1239 proto.setState(window, state); 1240 break; 1241 } 1242 } 1243 1244 if (!window.isShowing()) { 1245 /* 1246 * Purge KWM bits. 1247 * Not really tested with KWM, only with WindowMaker. 1248 */ 1249 XToolkit.awtLock(); 1250 try { 1251 XlibWrapper.XDeleteProperty(XToolkit.getDisplay(), 1252 window.getWindow(), 1253 XA_KWM_WIN_ICONIFIED.getAtom()); 1254 XlibWrapper.XDeleteProperty(XToolkit.getDisplay(), 1255 window.getWindow(), 1256 XA_KWM_WIN_MAXIMIZED.getAtom()); 1257 } 1258 finally { 1259 XToolkit.awtUnlock(); 1260 } 1261 } 1262 XToolkit.XSync(); 1263 } 1264 1265 1266 /* 1267 * Work around for 4775545. 1268 * 1269 * If WM exits while the top-level is shaded, the shaded hint remains 1270 * on the top-level properties. When WM restarts and sees the shaded 1271 * window it can reparent it into a "pre-shaded" decoration frame 1272 * (Metacity does), and our insets logic will go crazy, b/c it will 1273 * see a huge nagative bottom inset. There's no clean solution for 1274 * this, so let's just be weasels and drop the shaded hint if we 1275 * detect that WM exited. NB: we are in for a race condition with WM 1276 * restart here. NB2: e.g. WindowMaker saves the state in a private 1277 * property that this code knows nothing about, so this workaround is 1278 * not effective; other WMs might play similar tricks. 1279 */ unshadeKludge(XDecoratedPeer window)1280 void unshadeKludge(XDecoratedPeer window) { 1281 assert(window.isShowing()); 1282 1283 for (XStateProtocol proto : getProtocols(XStateProtocol.class)) { 1284 proto.unshadeKludge(window); 1285 } 1286 XToolkit.XSync(); 1287 } 1288 1289 static boolean inited = false; init()1290 static void init() { 1291 if (inited) { 1292 return; 1293 } 1294 1295 initAtoms(); 1296 getWM(); 1297 inited = true; 1298 } 1299 initializeProtocols()1300 void initializeProtocols() { 1301 XNETProtocol net_protocol = g_net_protocol; 1302 if (net_protocol != null) { 1303 if (!net_protocol.active()) { 1304 net_protocol = null; 1305 } else { 1306 if (net_protocol.doStateProtocol()) { 1307 addProtocol(XStateProtocol.class, net_protocol); 1308 } 1309 if (net_protocol.doLayerProtocol()) { 1310 addProtocol(XLayerProtocol.class, net_protocol); 1311 } 1312 } 1313 } 1314 1315 XWINProtocol win = g_win_protocol; 1316 if (win != null) { 1317 if (win.active()) { 1318 if (win.doStateProtocol()) { 1319 addProtocol(XStateProtocol.class, win); 1320 } 1321 if (win.doLayerProtocol()) { 1322 addProtocol(XLayerProtocol.class, win); 1323 } 1324 } 1325 } 1326 } 1327 1328 HashMap storedInsets = new HashMap(); guessInsets(XDecoratedPeer window)1329 Insets guessInsets(XDecoratedPeer window) { 1330 Insets res = (Insets)storedInsets.get(window.getClass()); 1331 if (res == null) { 1332 switch (WMID) { 1333 case ENLIGHTEN_WM: 1334 res = new Insets(19, 4, 4, 4); 1335 break; 1336 case CDE_WM: 1337 res = new Insets(28, 6, 6, 6); 1338 break; 1339 case NO_WM: 1340 case LG3D_WM: 1341 res = zeroInsets; 1342 break; 1343 case MOTIF_WM: 1344 case OPENLOOK_WM: 1345 default: 1346 res = defaultInsets; 1347 } 1348 } 1349 if (insLog.isLoggable(PlatformLogger.Level.FINEST)) { 1350 insLog.finest("WM guessed insets: " + res); 1351 } 1352 return res; 1353 } 1354 /* 1355 * Some buggy WMs ignore window gravity when processing 1356 * ConfigureRequest and position window as if the gravity is Static. 1357 * We work around this in MWindowPeer.pReshape(). 1358 * 1359 * Starting with 1.5 we have introduced an Environment variable 1360 * _JAVA_AWT_WM_STATIC_GRAVITY that can be set to indicate to Java 1361 * explicitly that the WM has this behaviour, example is FVWM. 1362 */ 1363 1364 static int awtWMStaticGravity = -1; configureGravityBuggy()1365 static boolean configureGravityBuggy() { 1366 1367 if (awtWMStaticGravity == -1) { 1368 awtWMStaticGravity = (XToolkit.getEnv("_JAVA_AWT_WM_STATIC_GRAVITY") != null) ? 1 : 0; 1369 } 1370 1371 if (awtWMStaticGravity == 1) { 1372 return true; 1373 } 1374 1375 switch(getWMID()) { 1376 case XWM.ICE_WM: 1377 /* 1378 * See bug #228981 at IceWM's SourceForge pages. 1379 * Latest stable version 1.0.8-6 still has this problem. 1380 */ 1381 /** 1382 * Version 1.2.2 doesn't have this problem 1383 */ 1384 // Detect IceWM version 1385 if (g_net_protocol != null) { 1386 String wm_name = g_net_protocol.getWMName(); 1387 Pattern pat = Pattern.compile("^IceWM (\\d+)\\.(\\d+)\\.(\\d+).*$"); 1388 try { 1389 Matcher match = pat.matcher(wm_name); 1390 if (match.matches()) { 1391 int v1 = Integer.parseInt(match.group(1)); 1392 int v2 = Integer.parseInt(match.group(2)); 1393 int v3 = Integer.parseInt(match.group(3)); 1394 return !(v1 > 1 || (v1 == 1 && (v2 > 2 || (v2 == 2 && v3 >=2)))); 1395 } 1396 } catch (Exception e) { 1397 return true; 1398 } 1399 } 1400 return true; 1401 case XWM.ENLIGHTEN_WM: 1402 /* At least E16 is buggy. */ 1403 return true; 1404 default: 1405 return false; 1406 } 1407 } 1408 1409 /* 1410 * @return if WM implements the insets property - returns insets with values 1411 * specified in that property, null otherwise. 1412 */ getInsetsFromExtents(long window)1413 public static Insets getInsetsFromExtents(long window) { 1414 if (window == XConstants.None) { 1415 return null; 1416 } 1417 XNETProtocol net_protocol = getWM().getNETProtocol(); 1418 if (net_protocol != null && net_protocol.active()) { 1419 Insets insets = getInsetsFromProp(window, XA_NET_FRAME_EXTENTS); 1420 if (insLog.isLoggable(PlatformLogger.Level.FINE)) { 1421 insLog.fine("_NET_FRAME_EXTENTS: {0}", insets); 1422 } 1423 1424 if (insets != null) { 1425 return insets; 1426 } 1427 } 1428 switch(getWMID()) { 1429 case XWM.KDE2_WM: 1430 return getInsetsFromProp(window, XA_KDE_NET_WM_FRAME_STRUT); 1431 case XWM.ENLIGHTEN_WM: 1432 return getInsetsFromProp(window, XA_E_FRAME_SIZE); 1433 default: 1434 return null; 1435 } 1436 } 1437 1438 /** 1439 * Helper function reads property of type CARDINAL[4] = { left, right, top, bottom } 1440 * and converts it to Insets object. 1441 */ getInsetsFromProp(long window, XAtom atom)1442 public static Insets getInsetsFromProp(long window, XAtom atom) { 1443 if (window == XConstants.None) { 1444 return null; 1445 } 1446 1447 WindowPropertyGetter getter = 1448 new WindowPropertyGetter(window, atom, 1449 0, 4, false, XAtom.XA_CARDINAL); 1450 try { 1451 if (getter.execute() != XConstants.Success 1452 || getter.getData() == 0 1453 || getter.getActualType() != XAtom.XA_CARDINAL 1454 || getter.getActualFormat() != 32) 1455 { 1456 return null; 1457 } else { 1458 return new Insets((int)Native.getCard32(getter.getData(), 2), // top 1459 (int)Native.getCard32(getter.getData(), 0), // left 1460 (int)Native.getCard32(getter.getData(), 3), // bottom 1461 (int)Native.getCard32(getter.getData(), 1)); // right 1462 } 1463 } finally { 1464 getter.dispose(); 1465 } 1466 } 1467 1468 /** 1469 * Asks WM to fill Frame Extents (insets) for the window. 1470 */ requestWMExtents(long window)1471 public static void requestWMExtents(long window) { 1472 if (window == XConstants.None) { // not initialized 1473 return; 1474 } 1475 1476 log.fine("Requesting FRAME_EXTENTS"); 1477 1478 XClientMessageEvent msg = new XClientMessageEvent(); 1479 msg.zero(); 1480 msg.set_type(XConstants.ClientMessage); 1481 msg.set_display(XToolkit.getDisplay()); 1482 msg.set_window(window); 1483 msg.set_format(32); 1484 XToolkit.awtLock(); 1485 try { 1486 XNETProtocol net_protocol = getWM().getNETProtocol(); 1487 if (net_protocol != null && net_protocol.active()) { 1488 msg.set_message_type(XA_NET_REQUEST_FRAME_EXTENTS.getAtom()); 1489 XlibWrapper.XSendEvent(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(), 1490 false, 1491 XConstants.SubstructureRedirectMask | XConstants.SubstructureNotifyMask, 1492 msg.getPData()); 1493 } 1494 if (getWMID() == XWM.KDE2_WM) { 1495 msg.set_message_type(XA_KDE_NET_WM_FRAME_STRUT.getAtom()); 1496 XlibWrapper.XSendEvent(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(), 1497 false, 1498 XConstants.SubstructureRedirectMask | XConstants.SubstructureNotifyMask, 1499 msg.getPData()); 1500 } 1501 // XXX: should we wait for response? XIfEvent() would be useful here :) 1502 } finally { 1503 XToolkit.awtUnlock(); 1504 msg.dispose(); 1505 } 1506 } 1507 1508 /* syncTopLEvelPos() is necessary to insure that the window manager has in 1509 * fact moved us to our final position relative to the reParented WM window. 1510 * We have noted a timing window which our shell has not been moved so we 1511 * screw up the insets thinking they are 0,0. Wait (for a limited period of 1512 * time to let the WM hava a chance to move us. 1513 * @param window window ID of the shell, assuming it is the window 1514 * which will NOT have zero coordinates after the complete 1515 * reparenting 1516 */ syncTopLevelPos(long window, XWindowAttributes attrs)1517 boolean syncTopLevelPos(long window, XWindowAttributes attrs) { 1518 int tries = 0; 1519 XToolkit.awtLock(); 1520 try { 1521 do { 1522 XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), window, attrs.pData); 1523 if (attrs.get_x() != 0 || attrs.get_y() != 0) { 1524 return true; 1525 } 1526 tries++; 1527 XToolkit.XSync(); 1528 } while (tries < 50); 1529 } 1530 finally { 1531 XToolkit.awtUnlock(); 1532 } 1533 return false; 1534 } 1535 getInsets(XDecoratedPeer win, long window, long parent)1536 Insets getInsets(XDecoratedPeer win, long window, long parent) { 1537 /* 1538 * Unfortunately the concept of "insets" borrowed to AWT 1539 * from Win32 is *absolutely*, *unbelievably* foreign to 1540 * X11. Few WMs provide the size of frame decor 1541 * (i.e. insets) in a property they set on the client 1542 * window, so we check if we can get away with just 1543 * peeking at it. [Future versions of wm-spec might add a 1544 * standardized hint for this]. 1545 * 1546 * Otherwise we do some special casing. Actually the 1547 * fallback code ("default" case) seems to cover most of 1548 * the existing WMs (modulo Reparent/Configure order 1549 * perhaps?). 1550 * 1551 * Fallback code tries to account for the two most common cases: 1552 * 1553 * . single reparenting 1554 * parent window is the WM frame 1555 * [twm, olwm, sawfish] 1556 * 1557 * . double reparenting 1558 * parent is a lining exactly the size of the client 1559 * grandpa is the WM frame 1560 * [mwm, e!, kwin, fvwm2 ... ] 1561 */ 1562 Insets correctWM = XWM.getInsetsFromExtents(window); 1563 if (insLog.isLoggable(PlatformLogger.Level.FINER)) { 1564 insLog.finer("Got insets from property: {0}", correctWM); 1565 } 1566 1567 if (correctWM == null) { 1568 correctWM = new Insets(0,0,0,0); 1569 1570 correctWM.top = -1; 1571 correctWM.left = -1; 1572 1573 XWindowAttributes lwinAttr = new XWindowAttributes(); 1574 XWindowAttributes pattr = new XWindowAttributes(); 1575 try { 1576 switch (XWM.getWMID()) { 1577 /* should've been done in awt_wm_getInsetsFromProp */ 1578 case XWM.ENLIGHTEN_WM: { 1579 /* enlightenment does double reparenting */ 1580 syncTopLevelPos(parent, lwinAttr); 1581 correctWM.left = lwinAttr.get_x(); 1582 correctWM.top = lwinAttr.get_y(); 1583 /* 1584 * Now get the actual dimensions of the parent window 1585 * resolve the difference. We can't rely on the left 1586 * to be equal to right or bottom... Enlightment 1587 * breaks that assumption. 1588 */ 1589 XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), 1590 XlibUtil.getParentWindow(parent), 1591 pattr.pData); 1592 correctWM.right = pattr.get_width() - 1593 (lwinAttr.get_width() + correctWM.left); 1594 correctWM.bottom = pattr.get_height() - 1595 (lwinAttr.get_height() + correctWM.top); 1596 1597 break; 1598 } 1599 case XWM.ICE_WM: // for 1.2.2. 1600 case XWM.KDE2_WM: /* should've been done in getInsetsFromProp */ 1601 case XWM.CDE_WM: 1602 case XWM.MOTIF_WM: { 1603 /* these are double reparenting too */ 1604 if (syncTopLevelPos(parent, lwinAttr)) { 1605 correctWM.top = lwinAttr.get_y(); 1606 correctWM.left = lwinAttr.get_x(); 1607 correctWM.right = correctWM.left; 1608 correctWM.bottom = correctWM.left; 1609 } else { 1610 return null; 1611 } 1612 break; 1613 } 1614 case XWM.SAWFISH_WM: 1615 case XWM.OPENLOOK_WM: { 1616 /* single reparenting */ 1617 syncTopLevelPos(window, lwinAttr); 1618 correctWM.top = lwinAttr.get_y(); 1619 correctWM.left = lwinAttr.get_x(); 1620 correctWM.right = correctWM.left; 1621 correctWM.bottom = correctWM.left; 1622 break; 1623 } 1624 case XWM.OTHER_WM: 1625 default: { /* this is very similar to the E! case above */ 1626 if (insLog.isLoggable(PlatformLogger.Level.FINEST)) { 1627 insLog.finest("Getting correct insets for OTHER_WM/default, parent: {0}", parent); 1628 } 1629 syncTopLevelPos(parent, lwinAttr); 1630 int status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), 1631 window, lwinAttr.pData); 1632 status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), 1633 parent, pattr.pData); 1634 if (lwinAttr.get_root() == parent) { 1635 insLog.finest("our parent is root so insets should be zero"); 1636 correctWM = new Insets(0, 0, 0, 0); 1637 break; 1638 } 1639 1640 /* 1641 * Check for double-reparenting WM. 1642 * 1643 * If the parent is exactly the same size as the 1644 * top-level assume taht it's the "lining" window and 1645 * that the grandparent is the actual frame (NB: we 1646 * have already handled undecorated windows). 1647 * 1648 * XXX: what about timing issues that syncTopLevelPos 1649 * is supposed to work around? 1650 */ 1651 if (lwinAttr.get_x() == 0 && lwinAttr.get_y() == 0 1652 && lwinAttr.get_width()+2*lwinAttr.get_border_width() == pattr.get_width() 1653 && lwinAttr.get_height()+2*lwinAttr.get_border_width() == pattr.get_height()) 1654 { 1655 if (insLog.isLoggable(PlatformLogger.Level.FINEST)) { 1656 insLog.finest("Double reparenting detected, pattr({2})={0}, lwinAttr({3})={1}", 1657 lwinAttr, pattr, parent, window); 1658 } 1659 lwinAttr.set_x(pattr.get_x()); 1660 lwinAttr.set_y(pattr.get_y()); 1661 lwinAttr.set_border_width(lwinAttr.get_border_width()+pattr.get_border_width()); 1662 1663 final long grand_parent = XlibUtil.getParentWindow(parent); 1664 1665 if (grand_parent == lwinAttr.get_root()) { 1666 // This is not double-reparenting in a 1667 // general sense - we simply don't have 1668 // correct insets - return null to try to 1669 // get insets later 1670 return null; 1671 } else { 1672 parent = grand_parent; 1673 XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), 1674 parent, 1675 pattr.pData); 1676 } 1677 } 1678 /* 1679 * XXX: To be absolutely correct, we'd need to take 1680 * parent's border-width into account too, but the 1681 * rest of the code is happily unaware about border 1682 * widths and inner/outer distinction, so for the time 1683 * being, just ignore it. 1684 */ 1685 if (insLog.isLoggable(PlatformLogger.Level.FINEST)) { 1686 insLog.finest("Attrs before calculation: pattr({2})={0}, lwinAttr({3})={1}", 1687 lwinAttr, pattr, parent, window); 1688 } 1689 correctWM = new Insets(lwinAttr.get_y() + lwinAttr.get_border_width(), 1690 lwinAttr.get_x() + lwinAttr.get_border_width(), 1691 pattr.get_height() - (lwinAttr.get_y() + lwinAttr.get_height() + 2*lwinAttr.get_border_width()), 1692 pattr.get_width() - (lwinAttr.get_x() + lwinAttr.get_width() + 2*lwinAttr.get_border_width())); 1693 break; 1694 } /* default */ 1695 } /* switch (runningWM) */ 1696 } finally { 1697 lwinAttr.dispose(); 1698 pattr.dispose(); 1699 } 1700 } 1701 if (storedInsets.get(win.getClass()) == null) { 1702 storedInsets.put(win.getClass(), correctWM); 1703 } 1704 return correctWM; 1705 } isDesktopWindow( long w )1706 boolean isDesktopWindow( long w ) { 1707 if (g_net_protocol != null) { 1708 XAtomList wtype = XAtom.get("_NET_WM_WINDOW_TYPE").getAtomListPropertyList( w ); 1709 return wtype.contains( XAtom.get("_NET_WM_WINDOW_TYPE_DESKTOP") ); 1710 } else { 1711 return false; 1712 } 1713 } 1714 getNETProtocol()1715 public XNETProtocol getNETProtocol() { 1716 return g_net_protocol; 1717 } 1718 1719 /** 1720 * Sets _NET_WN_ICON property on the window using the arrays of 1721 * raster-data for icons. If icons is null, removes _NET_WM_ICON 1722 * property. 1723 * This method invokes XNETProtocol.setWMIcon() for WMs that 1724 * support NET protocol. 1725 * 1726 * @return true if hint was modified successfully, false otherwise 1727 */ setNetWMIcon(XWindowPeer window, java.util.List<IconInfo> icons)1728 public boolean setNetWMIcon(XWindowPeer window, java.util.List<IconInfo> icons) { 1729 if (g_net_protocol != null && g_net_protocol.active()) { 1730 g_net_protocol.setWMIcons(window, icons); 1731 return getWMID() != ICE_WM; 1732 } 1733 return false; 1734 } 1735 } 1736