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