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