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