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