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