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