131 case LG3D_WM:
132 return "LookingGlass";
133 case CWM_WM:
134 return "CWM";
135 case MUTTER_WM:
136 return "Mutter";
137 case UNDETERMINED_WM:
138 default:
139 return "Undetermined WM";
140 }
141 }
142
143
144 int WMID;
145 static final Insets zeroInsets = new Insets(0, 0, 0, 0);
146 static final Insets defaultInsets = new Insets(25, 5, 5, 5);
147
148 XWM(int WMID) {
149 this.WMID = WMID;
150 initializeProtocols();
151 if (log.isLoggable(PlatformLogger.FINE)) {
152 log.fine("Window manager: " + toString());
153 }
154 }
155 int getID() {
156 return WMID;
157 }
158
159
160 static Insets normalize(Insets insets) {
161 if (insets.top > 64 || insets.top < 0) {
162 insets.top = 28;
163 }
164 if (insets.left > 32 || insets.left < 0) {
165 insets.left = 6;
166 }
167 if (insets.right > 32 || insets.right < 0) {
168 insets.right = 6;
169 }
170 if (insets.bottom > 32 || insets.bottom < 0) {
171 insets.bottom = 6;
237 *
238 * No selection owner, but, perhaps it is not ICCCM compliant WM
239 * (e.g. CDE/Sawfish).
240 * Try selecting for SubstructureRedirect, that only one client
241 * can select for, and if the request fails, than some other WM is
242 * already running.
243 *
244 * We also treat eXcursion as NO_WM.
245 */
246 private static boolean isNoWM() {
247 /*
248 * Quick checks for specific servers.
249 */
250 String vendor_string = XlibWrapper.ServerVendor(XToolkit.getDisplay());
251 if (vendor_string.indexOf("eXcursion") != -1) {
252 /*
253 * Use NO_WM since in all other aspects eXcursion is like not
254 * having a window manager running. I.e. it does not reparent
255 * top level shells.
256 */
257 if (insLog.isLoggable(PlatformLogger.FINER)) {
258 insLog.finer("eXcursion means NO_WM");
259 }
260 return true;
261 }
262
263 XSetWindowAttributes substruct = new XSetWindowAttributes();
264 try {
265 /*
266 * Let's check an owner of WM_Sn selection for the default screen.
267 */
268 final long default_screen_number =
269 XlibWrapper.DefaultScreen(XToolkit.getDisplay());
270 final String selection_name = "WM_S" + default_screen_number;
271
272 long selection_owner =
273 XlibWrapper.XGetSelectionOwner(XToolkit.getDisplay(),
274 XAtom.get(selection_name).getAtom());
275 if (insLog.isLoggable(PlatformLogger.FINER)) {
276 insLog.finer("selection owner of " + selection_name
277 + " is " + selection_owner);
278 }
279
280 if (selection_owner != XConstants.None) {
281 return false;
282 }
283
284 winmgr_running = false;
285 substruct.set_event_mask(XConstants.SubstructureRedirectMask);
286
287 XErrorHandlerUtil.WITH_XERROR_HANDLER(detectWMHandler);
288 XlibWrapper.XChangeWindowAttributes(XToolkit.getDisplay(),
289 XToolkit.getDefaultRootWindow(),
290 XConstants.CWEventMask,
291 substruct.pData);
292 XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
293
294 /*
295 * If no WM is running then our selection for SubstructureRedirect
296 * succeeded and needs to be undone (hey we are *not* a WM ;-).
297 */
298 if (!winmgr_running) {
299 substruct.set_event_mask(0);
300 XlibWrapper.XChangeWindowAttributes(XToolkit.getDisplay(),
301 XToolkit.getDefaultRootWindow(),
302 XConstants.CWEventMask,
303 substruct.pData);
304 if (insLog.isLoggable(PlatformLogger.FINER)) {
305 insLog.finer("It looks like there is no WM thus NO_WM");
306 }
307 }
308
309 return !winmgr_running;
310 } finally {
311 substruct.dispose();
312 }
313 }
314
315 static XAtom XA_ENLIGHTENMENT_COMMS = new XAtom("ENLIGHTENMENT_COMMS", false);
316 /*
317 * Helper function for isEnlightenment().
318 * Enlightenment uses STRING property for its comms window id. Gaaa!
319 * The property is ENLIGHTENMENT_COMMS, STRING/8 and the string format
320 * is "WINID %8x". Gee, I haven't been using scanf for *ages*... :-)
321 */
322 static long getECommsWindowIDProperty(long window) {
323
324 if (!XA_ENLIGHTENMENT_COMMS.isInterned()) {
328 WindowPropertyGetter getter =
329 new WindowPropertyGetter(window, XA_ENLIGHTENMENT_COMMS, 0, 14, false,
330 XAtom.XA_STRING);
331 try {
332 int status = getter.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
333 if (status != XConstants.Success || getter.getData() == 0) {
334 return 0;
335 }
336
337 if (getter.getActualType() != XAtom.XA_STRING
338 || getter.getActualFormat() != 8
339 || getter.getNumberOfItems() != 14 || getter.getBytesAfter() != 0)
340 {
341 return 0;
342 }
343
344 // Convert data to String, ASCII
345 byte[] bytes = XlibWrapper.getStringBytes(getter.getData());
346 String id = new String(bytes);
347
348 if (log.isLoggable(PlatformLogger.FINER)) {
349 log.finer("ENLIGHTENMENT_COMMS is " + id);
350 }
351
352 // Parse WINID
353 Pattern winIdPat = Pattern.compile("WINID\\s+(\\p{XDigit}{0,8})");
354 try {
355 Matcher match = winIdPat.matcher(id);
356 if (match.matches()) {
357 if (log.isLoggable(PlatformLogger.FINEST)) {
358 log.finest("Match group count: " + match.groupCount());
359 }
360 String longId = match.group(1);
361 if (log.isLoggable(PlatformLogger.FINEST)) {
362 log.finest("Match group 1 " + longId);
363 }
364 long winid = Long.parseLong(longId, 16);
365 if (log.isLoggable(PlatformLogger.FINER)) {
366 log.finer("Enlightenment communication window " + winid);
367 }
368 return winid;
369 } else {
370 log.finer("ENLIGHTENMENT_COMMS has wrong format");
371 return 0;
372 }
373 } catch (Exception e) {
374 if (log.isLoggable(PlatformLogger.FINER)) {
375 e.printStackTrace();
376 }
377 return 0;
378 }
379 } finally {
380 getter.dispose();
381 }
382 }
383
384 /*
385 * Is Enlightenment WM running? Congruent to awt_wm_checkAnchor, but
386 * uses STRING property peculiar to Enlightenment.
387 */
388 static boolean isEnlightenment() {
389
390 long root_xref = getECommsWindowIDProperty(XToolkit.getDefaultRootWindow());
391 if (root_xref == 0) {
392 return false;
393 }
394
400 return true;
401 }
402
403 /*
404 * Is CDE running?
405 *
406 * XXX: This is hairy... CDE is MWM as well. It seems we simply test
407 * for default setup and will be bitten if user changes things...
408 *
409 * Check for _DT_SM_WINDOW_INFO(_DT_SM_WINDOW_INFO) on root. Take the
410 * second element of the property and check for presence of
411 * _DT_SM_STATE_INFO(_DT_SM_STATE_INFO) on that window.
412 *
413 * XXX: Any header that defines this structures???
414 */
415 static final XAtom XA_DT_SM_WINDOW_INFO = new XAtom("_DT_SM_WINDOW_INFO", false);
416 static final XAtom XA_DT_SM_STATE_INFO = new XAtom("_DT_SM_STATE_INFO", false);
417 static boolean isCDE() {
418
419 if (!XA_DT_SM_WINDOW_INFO.isInterned()) {
420 if (log.isLoggable(PlatformLogger.FINER)) {
421 log.finer("{0} is not interned", XA_DT_SM_WINDOW_INFO);
422 }
423 return false;
424 }
425
426 WindowPropertyGetter getter =
427 new WindowPropertyGetter(XToolkit.getDefaultRootWindow(),
428 XA_DT_SM_WINDOW_INFO, 0, 2,
429 false, XA_DT_SM_WINDOW_INFO);
430 try {
431 int status = getter.execute();
432 if (status != XConstants.Success || getter.getData() == 0) {
433 log.finer("Getting of _DT_SM_WINDOW_INFO is not successfull");
434 return false;
435 }
436 if (getter.getActualType() != XA_DT_SM_WINDOW_INFO.getAtom()
437 || getter.getActualFormat() != 32
438 || getter.getNumberOfItems() != 2 || getter.getBytesAfter() != 0)
439 {
440 log.finer("Wrong format of _DT_SM_WINDOW_INFO");
441 return false;
442 }
443
444 long wmwin = Native.getWindow(getter.getData(), 1); //unsafe.getInt(getter.getData()+4);
445
446 if (wmwin == 0) {
447 log.fine("WARNING: DT_SM_WINDOW_INFO exists but returns zero windows");
448 return false;
449 }
450
451 /* Now check that this window has _DT_SM_STATE_INFO (ignore contents) */
452 if (!XA_DT_SM_STATE_INFO.isInterned()) {
453 if (log.isLoggable(PlatformLogger.FINER)) {
454 log.finer("{0} is not interned", XA_DT_SM_STATE_INFO);
455 }
456 return false;
457 }
458 WindowPropertyGetter getter2 =
459 new WindowPropertyGetter(wmwin, XA_DT_SM_STATE_INFO, 0, 1,
460 false, XA_DT_SM_STATE_INFO);
461 try {
462 status = getter2.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
463
464
465 if (status != XConstants.Success || getter2.getData() == 0) {
466 log.finer("Getting of _DT_SM_STATE_INFO is not successfull");
467 return false;
468 }
469 if (getter2.getActualType() != XA_DT_SM_STATE_INFO.getAtom()
470 || getter2.getActualFormat() != 32)
471 {
472 log.finer("Wrong format of _DT_SM_STATE_INFO");
473 return false;
607 *
608 * But messing with PropertyNotify here is way too much trouble, so
609 * approximate the check by setting the property in this function and
610 * checking if it still exists later on.
611 *
612 * Gaa, dirty dances...
613 */
614 static final XAtom XA_ICEWM_WINOPTHINT = new XAtom("_ICEWM_WINOPTHINT", false);
615 static final char opt[] = {
616 'A','W','T','_','I','C','E','W','M','_','T','E','S','T','\0',
617 'a','l','l','W','o','r','k','s','p','a','c','e','s','\0',
618 '0','\0'
619 };
620 static boolean prepareIsIceWM() {
621 /*
622 * Choose something innocuous: "AWT_ICEWM_TEST allWorkspaces 0".
623 * IceWM expects "class\0option\0arg\0" with zero bytes as delimiters.
624 */
625
626 if (!XA_ICEWM_WINOPTHINT.isInterned()) {
627 if (log.isLoggable(PlatformLogger.FINER)) {
628 log.finer("{0} is not interned", XA_ICEWM_WINOPTHINT);
629 }
630 return false;
631 }
632
633 XToolkit.awtLock();
634 try {
635 XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
636 XlibWrapper.XChangePropertyS(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(),
637 XA_ICEWM_WINOPTHINT.getAtom(),
638 XA_ICEWM_WINOPTHINT.getAtom(),
639 8, XConstants.PropModeReplace,
640 new String(opt));
641 XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
642
643 if ((XErrorHandlerUtil.saved_error != null) &&
644 (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) {
645 log.finer("Erorr getting XA_ICEWM_WINOPTHINT property");
646 return false;
647 }
648 log.finer("Prepared for IceWM detection");
649 return true;
650 } finally {
651 XToolkit.awtUnlock();
652 }
653 }
654
655 /*
656 * Is IceWM running?
657 *
658 * Note well: Only call this if awt_wm_prepareIsIceWM succeeded, or a
659 * false positive will be reported.
660 */
661 static boolean isIceWM() {
662 if (!XA_ICEWM_WINOPTHINT.isInterned()) {
663 if (log.isLoggable(PlatformLogger.FINER)) {
664 log.finer("{0} is not interned", XA_ICEWM_WINOPTHINT);
665 }
666 return false;
667 }
668
669 WindowPropertyGetter getter =
670 new WindowPropertyGetter(XToolkit.getDefaultRootWindow(),
671 XA_ICEWM_WINOPTHINT, 0, 0xFFFF,
672 true, XA_ICEWM_WINOPTHINT);
673 try {
674 int status = getter.execute();
675 boolean res = (status == XConstants.Success && getter.getActualType() != 0);
676 if (log.isLoggable(PlatformLogger.FINER)) {
677 log.finer("Status getting XA_ICEWM_WINOPTHINT: " + !res);
678 }
679 return !res || isNetWMName("IceWM");
680 } finally {
681 getter.dispose();
682 }
683 }
684
685 /*
686 * Is OpenLook WM running?
687 *
688 * This one is pretty lame, but the only property peculiar to OLWM is
689 * _SUN_WM_PROTOCOLS(ATOM[]). Fortunately, olwm deletes it on exit.
690 */
691 static final XAtom XA_SUN_WM_PROTOCOLS = new XAtom("_SUN_WM_PROTOCOLS", false);
692 static boolean isOpenLook() {
693 if (!XA_SUN_WM_PROTOCOLS.isInterned()) {
694 return false;
695 }
696
712 winmgr_running = true;
713 return 0;
714 }
715 return super.handleError(display, err);
716 }
717 };
718
719 /*
720 * Make an educated guess about running window manager.
721 * XXX: ideally, we should detect wm restart.
722 */
723 static int awt_wmgr = XWM.UNDETERMINED_WM;
724 static XWM wm;
725 static XWM getWM() {
726 if (wm == null) {
727 wm = new XWM(awt_wmgr = getWMID()/*XWM.OTHER_WM*/);
728 }
729 return wm;
730 }
731 static int getWMID() {
732 if (insLog.isLoggable(PlatformLogger.FINEST)) {
733 insLog.finest("awt_wmgr = " + awt_wmgr);
734 }
735 /*
736 * Ideally, we should support cases when a different WM is started
737 * during a Java app lifetime.
738 */
739
740 if (awt_wmgr != XWM.UNDETERMINED_WM) {
741 return awt_wmgr;
742 }
743
744 XSetWindowAttributes substruct = new XSetWindowAttributes();
745 XToolkit.awtLock();
746 try {
747 if (isNoWM()) {
748 awt_wmgr = XWM.NO_WM;
749 return awt_wmgr;
750 }
751
752 // Initialize _NET protocol - used to detect Window Manager.
753 // Later, WM will initialize its own version of protocol
754 XNETProtocol l_net_protocol = g_net_protocol = new XNETProtocol();
755 l_net_protocol.detect();
756 if (log.isLoggable(PlatformLogger.FINE) && l_net_protocol.active()) {
757 log.fine("_NET_WM_NAME is " + l_net_protocol.getWMName());
758 }
759 XWINProtocol win = g_win_protocol = new XWINProtocol();
760 win.detect();
761
762 /* actual check for IceWM to follow below */
763 boolean doIsIceWM = prepareIsIceWM(); /* and let IceWM to act */
764
765 /*
766 * Ok, some WM is out there. Check which one by testing for
767 * "distinguishing" atoms.
768 */
769 if (isEnlightenment()) {
770 awt_wmgr = XWM.ENLIGHTEN_WM;
771 } else if (isMetacity()) {
772 awt_wmgr = XWM.METACITY_WM;
773 } else if (isMutter()) {
774 awt_wmgr = XWM.MUTTER_WM;
775 } else if (isSawfish()) {
776 awt_wmgr = XWM.SAWFISH_WM;
820 * Size and decoration hints ...
821 *
822 \*****************************************************************************/
823
824
825 /*
826 * Remove size hints specified by the mask.
827 * XXX: Why do we need this in the first place???
828 */
829 static void removeSizeHints(XDecoratedPeer window, long mask) {
830 mask &= XUtilConstants.PMaxSize | XUtilConstants.PMinSize;
831
832 XToolkit.awtLock();
833 try {
834 XSizeHints hints = window.getHints();
835 if ((hints.get_flags() & mask) == 0) {
836 return;
837 }
838
839 hints.set_flags(hints.get_flags() & ~mask);
840 if (insLog.isLoggable(PlatformLogger.FINER)) {
841 insLog.finer("Setting hints, flags " + XlibWrapper.hintsToString(hints.get_flags()));
842 }
843 XlibWrapper.XSetWMNormalHints(XToolkit.getDisplay(),
844 window.getWindow(),
845 hints.pData);
846 } finally {
847 XToolkit.awtUnlock();
848 }
849 }
850
851 /*
852 * If MWM_DECOR_ALL bit is set, then the rest of the bit-mask is taken
853 * to be subtracted from the decorations. Normalize decoration spec
854 * so that we can map motif decor to something else bit-by-bit in the
855 * rest of the code.
856 */
857 static int normalizeMotifDecor(int decorations) {
858 if ((decorations & MWMConstants.MWM_DECOR_ALL) == 0) {
859 return decorations;
860 }
879 int f = MWMConstants.MWM_FUNC_RESIZE |
880 MWMConstants.MWM_FUNC_MOVE |
881 MWMConstants.MWM_FUNC_MAXIMIZE |
882 MWMConstants.MWM_FUNC_MINIMIZE |
883 MWMConstants.MWM_FUNC_CLOSE;
884 f &= ~functions;
885 return f;
886 }
887
888 /*
889 * Infer OL properties from MWM decorations.
890 * Use _OL_DECOR_DEL(ATOM[]) to remove unwanted ones.
891 */
892 static void setOLDecor(XWindow window, boolean resizable, int decorations) {
893 if (window == null) {
894 return;
895 }
896
897 XAtomList decorDel = new XAtomList();
898 decorations = normalizeMotifDecor(decorations);
899 if (insLog.isLoggable(PlatformLogger.FINER)) {
900 insLog.finer("Setting OL_DECOR to " + Integer.toBinaryString(decorations));
901 }
902 if ((decorations & MWMConstants.MWM_DECOR_TITLE) == 0) {
903 decorDel.add(XA_OL_DECOR_HEADER);
904 }
905 if ((decorations & (MWMConstants.MWM_DECOR_RESIZEH | MWMConstants.MWM_DECOR_MAXIMIZE)) == 0) {
906 decorDel.add(XA_OL_DECOR_RESIZE);
907 }
908 if ((decorations & (MWMConstants.MWM_DECOR_MENU |
909 MWMConstants.MWM_DECOR_MAXIMIZE |
910 MWMConstants.MWM_DECOR_MINIMIZE)) == 0)
911 {
912 decorDel.add(XA_OL_DECOR_CLOSE);
913 }
914 if (decorDel.size() == 0) {
915 insLog.finer("Deleting OL_DECOR");
916 XA_OL_DECOR_DEL.DeleteProperty(window);
917 } else {
918 if (insLog.isLoggable(PlatformLogger.FINER)) {
919 insLog.finer("Setting OL_DECOR to " + decorDel);
920 }
921 XA_OL_DECOR_DEL.setAtomListProperty(window, decorDel);
922 }
923 }
924
925 /*
926 * Set MWM decorations. Set MWM functions depending on resizability.
927 */
928 static void setMotifDecor(XWindow window, boolean resizable, int decorations, int functions) {
929 /* Apparently some WMs don't implement MWM_*_ALL semantic correctly */
930 if ((decorations & MWMConstants.MWM_DECOR_ALL) != 0
931 && (decorations != MWMConstants.MWM_DECOR_ALL))
932 {
933 decorations = normalizeMotifDecor(decorations);
934 }
935 if ((functions & MWMConstants.MWM_FUNC_ALL) != 0
936 && (functions != MWMConstants.MWM_FUNC_ALL))
937 {
938 functions = normalizeMotifFunc(functions);
939 }
940
941 PropMwmHints hints = window.getMWMHints();
942 hints.set_flags(hints.get_flags() |
943 MWMConstants.MWM_HINTS_FUNCTIONS |
944 MWMConstants.MWM_HINTS_DECORATIONS);
945 hints.set_functions(functions);
946 hints.set_decorations(decorations);
947
948 if (stateLog.isLoggable(PlatformLogger.FINER)) {
949 stateLog.finer("Setting MWM_HINTS to " + hints);
950 }
951 window.setMWMHints(hints);
952 }
953
954 /*
955 * Under some window managers if shell is already mapped, we MUST
956 * unmap and later remap in order to effect the changes we make in the
957 * window manager decorations.
958 *
959 * N.B. This unmapping / remapping of the shell exposes a bug in
960 * X/Motif or the Motif Window Manager. When you attempt to map a
961 * widget which is positioned (partially) off-screen, the window is
962 * relocated to be entirely on screen. Good idea. But if both the x
963 * and the y coordinates are less than the origin (0,0), the first
964 * (re)map will move the window to the origin, and any subsequent
965 * (re)map will relocate the window at some other point on the screen.
966 * I have written a short Motif test program to discover this bug.
967 * This should occur infrequently and it does not cause any real
968 * problem. So for now we'll let it be.
992 setMotifDecor(window, resizable, decorations, functions);
993 setOLDecor(window, resizable, decorations);
994
995 /* Some WMs need remap to redecorate the window */
996 if (window.isShowing() && needRemap(window)) {
997 /*
998 * Do the re/mapping at the Xlib level. Since we essentially
999 * work around a WM bug we don't want this hack to be exposed
1000 * to Intrinsics (i.e. don't mess with grabs, callbacks etc).
1001 */
1002 window.xSetVisible(false);
1003 XToolkit.XSync();
1004 window.xSetVisible(true);
1005 }
1006 }
1007
1008 /*
1009 * Make specified shell resizable.
1010 */
1011 static void setShellResizable(XDecoratedPeer window) {
1012 if (insLog.isLoggable(PlatformLogger.FINE)) {
1013 insLog.fine("Setting shell resizable " + window);
1014 }
1015 XToolkit.awtLock();
1016 try {
1017 Rectangle shellBounds = window.getShellBounds();
1018 shellBounds.translate(-window.currentInsets.left, -window.currentInsets.top);
1019 window.updateSizeHints(window.getDimensions());
1020 requestWMExtents(window.getWindow());
1021 XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), window.getShell(),
1022 shellBounds.x, shellBounds.y, shellBounds.width, shellBounds.height);
1023 /* REMINDER: will need to revisit when setExtendedStateBounds is added */
1024 //Fix for 4320050: Minimum size for java.awt.Frame is not being enforced.
1025 //We need to update frame's minimum size, not to reset it
1026 removeSizeHints(window, XUtilConstants.PMaxSize);
1027 window.updateMinimumSize();
1028
1029 /* Restore decorations */
1030 setShellDecor(window);
1031 } finally {
1032 XToolkit.awtUnlock();
1033 }
1034 }
1035
1036 /*
1037 * Make specified shell non-resizable.
1038 * If justChangeSize is false, update decorations as well.
1039 * @param shellBounds bounds of the shell window
1040 */
1041 static void setShellNotResizable(XDecoratedPeer window, WindowDimensions newDimensions, Rectangle shellBounds,
1042 boolean justChangeSize)
1043 {
1044 if (insLog.isLoggable(PlatformLogger.FINE)) {
1045 insLog.fine("Setting non-resizable shell " + window + ", dimensions " + newDimensions +
1046 ", shellBounds " + shellBounds +", just change size: " + justChangeSize);
1047 }
1048 XToolkit.awtLock();
1049 try {
1050 /* Fix min/max size hints at the specified values */
1051 if (!shellBounds.isEmpty()) {
1052 window.updateSizeHints(newDimensions);
1053 requestWMExtents(window.getWindow());
1054 XToolkit.XSync();
1055 XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), window.getShell(),
1056 shellBounds.x, shellBounds.y, shellBounds.width, shellBounds.height);
1057 }
1058 if (!justChangeSize) { /* update decorations */
1059 setShellDecor(window);
1060 }
1061 } finally {
1062 XToolkit.awtUnlock();
1063 }
1064 }
1158 *
1159 * Notice window state change when WM changes a property on the window ...
1160 *
1161 \*****************************************************************************/
1162
1163
1164 /*
1165 * Check if property change is a window state protocol message.
1166 */
1167 boolean isStateChange(XDecoratedPeer window, XPropertyEvent e) {
1168 if (!window.isShowing()) {
1169 stateLog.finer("Window is not showing");
1170 return false;
1171 }
1172
1173 int wm_state = window.getWMState();
1174 if (wm_state == XUtilConstants.WithdrawnState) {
1175 stateLog.finer("WithdrawnState");
1176 return false;
1177 } else {
1178 if (stateLog.isLoggable(PlatformLogger.FINER)) {
1179 stateLog.finer("Window WM_STATE is " + wm_state);
1180 }
1181 }
1182 boolean is_state_change = false;
1183 if (e.get_atom() == XA_WM_STATE.getAtom()) {
1184 is_state_change = true;
1185 }
1186
1187 for (XStateProtocol proto : getProtocols(XStateProtocol.class)) {
1188 is_state_change |= proto.isStateChange(e);
1189 if (stateLog.isLoggable(PlatformLogger.FINEST)) {
1190 stateLog.finest(proto + ": is state changed = " + is_state_change);
1191 }
1192 }
1193 return is_state_change;
1194 }
1195
1196 /*
1197 * Returns current state (including extended) of a given window.
1198 */
1199 int getState(XDecoratedPeer window) {
1200 int res = 0;
1201 final int wm_state = window.getWMState();
1202 if (wm_state == XUtilConstants.IconicState) {
1203 res = Frame.ICONIFIED;
1204 } else {
1205 res = Frame.NORMAL;
1206 }
1207 res |= getExtendedState(window);
1208 return res;
1209 }
1323 Insets guessInsets(XDecoratedPeer window) {
1324 Insets res = (Insets)storedInsets.get(window.getClass());
1325 if (res == null) {
1326 switch (WMID) {
1327 case ENLIGHTEN_WM:
1328 res = new Insets(19, 4, 4, 4);
1329 break;
1330 case CDE_WM:
1331 res = new Insets(28, 6, 6, 6);
1332 break;
1333 case NO_WM:
1334 case LG3D_WM:
1335 res = zeroInsets;
1336 break;
1337 case MOTIF_WM:
1338 case OPENLOOK_WM:
1339 default:
1340 res = defaultInsets;
1341 }
1342 }
1343 if (insLog.isLoggable(PlatformLogger.FINEST)) {
1344 insLog.finest("WM guessed insets: " + res);
1345 }
1346 return res;
1347 }
1348 /*
1349 * Some buggy WMs ignore window gravity when processing
1350 * ConfigureRequest and position window as if the gravity is Static.
1351 * We work around this in MWindowPeer.pReshape().
1352 *
1353 * Starting with 1.5 we have introduced an Environment variable
1354 * _JAVA_AWT_WM_STATIC_GRAVITY that can be set to indicate to Java
1355 * explicitly that the WM has this behaviour, example is FVWM.
1356 */
1357
1358 static int awtWMStaticGravity = -1;
1359 static boolean configureGravityBuggy() {
1360
1361 if (awtWMStaticGravity == -1) {
1362 awtWMStaticGravity = (XToolkit.getEnv("_JAVA_AWT_WM_STATIC_GRAVITY") != null) ? 1 : 0;
1363 }
1394 return true;
1395 case XWM.ENLIGHTEN_WM:
1396 /* At least E16 is buggy. */
1397 return true;
1398 default:
1399 return false;
1400 }
1401 }
1402
1403 /*
1404 * @return if WM implements the insets property - returns insets with values
1405 * specified in that property, null otherwise.
1406 */
1407 public static Insets getInsetsFromExtents(long window) {
1408 if (window == XConstants.None) {
1409 return null;
1410 }
1411 XNETProtocol net_protocol = getWM().getNETProtocol();
1412 if (net_protocol != null && net_protocol.active()) {
1413 Insets insets = getInsetsFromProp(window, XA_NET_FRAME_EXTENTS);
1414 if (insLog.isLoggable(PlatformLogger.FINE)) {
1415 insLog.fine("_NET_FRAME_EXTENTS: {0}", insets);
1416 }
1417
1418 if (insets != null) {
1419 return insets;
1420 }
1421 }
1422 switch(getWMID()) {
1423 case XWM.KDE2_WM:
1424 return getInsetsFromProp(window, XA_KDE_NET_WM_FRAME_STRUT);
1425 case XWM.ENLIGHTEN_WM:
1426 return getInsetsFromProp(window, XA_E_FRAME_SIZE);
1427 default:
1428 return null;
1429 }
1430 }
1431
1432 /**
1433 * Helper function reads property of type CARDINAL[4] = { left, right, top, bottom }
1434 * and converts it to Insets object.
1537 * peeking at it. [Future versions of wm-spec might add a
1538 * standardized hint for this].
1539 *
1540 * Otherwise we do some special casing. Actually the
1541 * fallback code ("default" case) seems to cover most of
1542 * the existing WMs (modulo Reparent/Configure order
1543 * perhaps?).
1544 *
1545 * Fallback code tries to account for the two most common cases:
1546 *
1547 * . single reparenting
1548 * parent window is the WM frame
1549 * [twm, olwm, sawfish]
1550 *
1551 * . double reparenting
1552 * parent is a lining exactly the size of the client
1553 * grandpa is the WM frame
1554 * [mwm, e!, kwin, fvwm2 ... ]
1555 */
1556 Insets correctWM = XWM.getInsetsFromExtents(window);
1557 if (insLog.isLoggable(PlatformLogger.FINER)) {
1558 insLog.finer("Got insets from property: {0}", correctWM);
1559 }
1560
1561 if (correctWM == null) {
1562 correctWM = new Insets(0,0,0,0);
1563
1564 correctWM.top = -1;
1565 correctWM.left = -1;
1566
1567 XWindowAttributes lwinAttr = new XWindowAttributes();
1568 XWindowAttributes pattr = new XWindowAttributes();
1569 try {
1570 switch (XWM.getWMID()) {
1571 /* should've been done in awt_wm_getInsetsFromProp */
1572 case XWM.ENLIGHTEN_WM: {
1573 /* enlightenment does double reparenting */
1574 syncTopLevelPos(parent, lwinAttr);
1575 correctWM.left = lwinAttr.get_x();
1576 correctWM.top = lwinAttr.get_y();
1577 /*
1600 correctWM.left = lwinAttr.get_x();
1601 correctWM.right = correctWM.left;
1602 correctWM.bottom = correctWM.left;
1603 } else {
1604 return null;
1605 }
1606 break;
1607 }
1608 case XWM.SAWFISH_WM:
1609 case XWM.OPENLOOK_WM: {
1610 /* single reparenting */
1611 syncTopLevelPos(window, lwinAttr);
1612 correctWM.top = lwinAttr.get_y();
1613 correctWM.left = lwinAttr.get_x();
1614 correctWM.right = correctWM.left;
1615 correctWM.bottom = correctWM.left;
1616 break;
1617 }
1618 case XWM.OTHER_WM:
1619 default: { /* this is very similar to the E! case above */
1620 if (insLog.isLoggable(PlatformLogger.FINEST)) {
1621 insLog.finest("Getting correct insets for OTHER_WM/default, parent: {0}", parent);
1622 }
1623 syncTopLevelPos(parent, lwinAttr);
1624 int status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1625 window, lwinAttr.pData);
1626 status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1627 parent, pattr.pData);
1628 if (lwinAttr.get_root() == parent) {
1629 insLog.finest("our parent is root so insets should be zero");
1630 correctWM = new Insets(0, 0, 0, 0);
1631 break;
1632 }
1633
1634 /*
1635 * Check for double-reparenting WM.
1636 *
1637 * If the parent is exactly the same size as the
1638 * top-level assume taht it's the "lining" window and
1639 * that the grandparent is the actual frame (NB: we
1640 * have already handled undecorated windows).
1641 *
1642 * XXX: what about timing issues that syncTopLevelPos
1643 * is supposed to work around?
1644 */
1645 if (lwinAttr.get_x() == 0 && lwinAttr.get_y() == 0
1646 && lwinAttr.get_width()+2*lwinAttr.get_border_width() == pattr.get_width()
1647 && lwinAttr.get_height()+2*lwinAttr.get_border_width() == pattr.get_height())
1648 {
1649 if (insLog.isLoggable(PlatformLogger.FINEST)) {
1650 insLog.finest("Double reparenting detected, pattr({2})={0}, lwinAttr({3})={1}",
1651 lwinAttr, pattr, parent, window);
1652 }
1653 lwinAttr.set_x(pattr.get_x());
1654 lwinAttr.set_y(pattr.get_y());
1655 lwinAttr.set_border_width(lwinAttr.get_border_width()+pattr.get_border_width());
1656
1657 final long grand_parent = XlibUtil.getParentWindow(parent);
1658
1659 if (grand_parent == lwinAttr.get_root()) {
1660 // This is not double-reparenting in a
1661 // general sense - we simply don't have
1662 // correct insets - return null to try to
1663 // get insets later
1664 return null;
1665 } else {
1666 parent = grand_parent;
1667 XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1668 parent,
1669 pattr.pData);
1670 }
1671 }
1672 /*
1673 * XXX: To be absolutely correct, we'd need to take
1674 * parent's border-width into account too, but the
1675 * rest of the code is happily unaware about border
1676 * widths and inner/outer distinction, so for the time
1677 * being, just ignore it.
1678 */
1679 if (insLog.isLoggable(PlatformLogger.FINEST)) {
1680 insLog.finest("Attrs before calculation: pattr({2})={0}, lwinAttr({3})={1}",
1681 lwinAttr, pattr, parent, window);
1682 }
1683 correctWM = new Insets(lwinAttr.get_y() + lwinAttr.get_border_width(),
1684 lwinAttr.get_x() + lwinAttr.get_border_width(),
1685 pattr.get_height() - (lwinAttr.get_y() + lwinAttr.get_height() + 2*lwinAttr.get_border_width()),
1686 pattr.get_width() - (lwinAttr.get_x() + lwinAttr.get_width() + 2*lwinAttr.get_border_width()));
1687 break;
1688 } /* default */
1689 } /* switch (runningWM) */
1690 } finally {
1691 lwinAttr.dispose();
1692 pattr.dispose();
1693 }
1694 }
1695 if (storedInsets.get(win.getClass()) == null) {
1696 storedInsets.put(win.getClass(), correctWM);
1697 }
1698 return correctWM;
1699 }
|
131 case LG3D_WM:
132 return "LookingGlass";
133 case CWM_WM:
134 return "CWM";
135 case MUTTER_WM:
136 return "Mutter";
137 case UNDETERMINED_WM:
138 default:
139 return "Undetermined WM";
140 }
141 }
142
143
144 int WMID;
145 static final Insets zeroInsets = new Insets(0, 0, 0, 0);
146 static final Insets defaultInsets = new Insets(25, 5, 5, 5);
147
148 XWM(int WMID) {
149 this.WMID = WMID;
150 initializeProtocols();
151 if (log.isLoggable(PlatformLogger.Level.FINE)) {
152 log.fine("Window manager: " + toString());
153 }
154 }
155 int getID() {
156 return WMID;
157 }
158
159
160 static Insets normalize(Insets insets) {
161 if (insets.top > 64 || insets.top < 0) {
162 insets.top = 28;
163 }
164 if (insets.left > 32 || insets.left < 0) {
165 insets.left = 6;
166 }
167 if (insets.right > 32 || insets.right < 0) {
168 insets.right = 6;
169 }
170 if (insets.bottom > 32 || insets.bottom < 0) {
171 insets.bottom = 6;
237 *
238 * No selection owner, but, perhaps it is not ICCCM compliant WM
239 * (e.g. CDE/Sawfish).
240 * Try selecting for SubstructureRedirect, that only one client
241 * can select for, and if the request fails, than some other WM is
242 * already running.
243 *
244 * We also treat eXcursion as NO_WM.
245 */
246 private static boolean isNoWM() {
247 /*
248 * Quick checks for specific servers.
249 */
250 String vendor_string = XlibWrapper.ServerVendor(XToolkit.getDisplay());
251 if (vendor_string.indexOf("eXcursion") != -1) {
252 /*
253 * Use NO_WM since in all other aspects eXcursion is like not
254 * having a window manager running. I.e. it does not reparent
255 * top level shells.
256 */
257 if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
258 insLog.finer("eXcursion means NO_WM");
259 }
260 return true;
261 }
262
263 XSetWindowAttributes substruct = new XSetWindowAttributes();
264 try {
265 /*
266 * Let's check an owner of WM_Sn selection for the default screen.
267 */
268 final long default_screen_number =
269 XlibWrapper.DefaultScreen(XToolkit.getDisplay());
270 final String selection_name = "WM_S" + default_screen_number;
271
272 long selection_owner =
273 XlibWrapper.XGetSelectionOwner(XToolkit.getDisplay(),
274 XAtom.get(selection_name).getAtom());
275 if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
276 insLog.finer("selection owner of " + selection_name
277 + " is " + selection_owner);
278 }
279
280 if (selection_owner != XConstants.None) {
281 return false;
282 }
283
284 winmgr_running = false;
285 substruct.set_event_mask(XConstants.SubstructureRedirectMask);
286
287 XErrorHandlerUtil.WITH_XERROR_HANDLER(detectWMHandler);
288 XlibWrapper.XChangeWindowAttributes(XToolkit.getDisplay(),
289 XToolkit.getDefaultRootWindow(),
290 XConstants.CWEventMask,
291 substruct.pData);
292 XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
293
294 /*
295 * If no WM is running then our selection for SubstructureRedirect
296 * succeeded and needs to be undone (hey we are *not* a WM ;-).
297 */
298 if (!winmgr_running) {
299 substruct.set_event_mask(0);
300 XlibWrapper.XChangeWindowAttributes(XToolkit.getDisplay(),
301 XToolkit.getDefaultRootWindow(),
302 XConstants.CWEventMask,
303 substruct.pData);
304 if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
305 insLog.finer("It looks like there is no WM thus NO_WM");
306 }
307 }
308
309 return !winmgr_running;
310 } finally {
311 substruct.dispose();
312 }
313 }
314
315 static XAtom XA_ENLIGHTENMENT_COMMS = new XAtom("ENLIGHTENMENT_COMMS", false);
316 /*
317 * Helper function for isEnlightenment().
318 * Enlightenment uses STRING property for its comms window id. Gaaa!
319 * The property is ENLIGHTENMENT_COMMS, STRING/8 and the string format
320 * is "WINID %8x". Gee, I haven't been using scanf for *ages*... :-)
321 */
322 static long getECommsWindowIDProperty(long window) {
323
324 if (!XA_ENLIGHTENMENT_COMMS.isInterned()) {
328 WindowPropertyGetter getter =
329 new WindowPropertyGetter(window, XA_ENLIGHTENMENT_COMMS, 0, 14, false,
330 XAtom.XA_STRING);
331 try {
332 int status = getter.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
333 if (status != XConstants.Success || getter.getData() == 0) {
334 return 0;
335 }
336
337 if (getter.getActualType() != XAtom.XA_STRING
338 || getter.getActualFormat() != 8
339 || getter.getNumberOfItems() != 14 || getter.getBytesAfter() != 0)
340 {
341 return 0;
342 }
343
344 // Convert data to String, ASCII
345 byte[] bytes = XlibWrapper.getStringBytes(getter.getData());
346 String id = new String(bytes);
347
348 if (log.isLoggable(PlatformLogger.Level.FINER)) {
349 log.finer("ENLIGHTENMENT_COMMS is " + id);
350 }
351
352 // Parse WINID
353 Pattern winIdPat = Pattern.compile("WINID\\s+(\\p{XDigit}{0,8})");
354 try {
355 Matcher match = winIdPat.matcher(id);
356 if (match.matches()) {
357 if (log.isLoggable(PlatformLogger.Level.FINEST)) {
358 log.finest("Match group count: " + match.groupCount());
359 }
360 String longId = match.group(1);
361 if (log.isLoggable(PlatformLogger.Level.FINEST)) {
362 log.finest("Match group 1 " + longId);
363 }
364 long winid = Long.parseLong(longId, 16);
365 if (log.isLoggable(PlatformLogger.Level.FINER)) {
366 log.finer("Enlightenment communication window " + winid);
367 }
368 return winid;
369 } else {
370 log.finer("ENLIGHTENMENT_COMMS has wrong format");
371 return 0;
372 }
373 } catch (Exception e) {
374 if (log.isLoggable(PlatformLogger.Level.FINER)) {
375 e.printStackTrace();
376 }
377 return 0;
378 }
379 } finally {
380 getter.dispose();
381 }
382 }
383
384 /*
385 * Is Enlightenment WM running? Congruent to awt_wm_checkAnchor, but
386 * uses STRING property peculiar to Enlightenment.
387 */
388 static boolean isEnlightenment() {
389
390 long root_xref = getECommsWindowIDProperty(XToolkit.getDefaultRootWindow());
391 if (root_xref == 0) {
392 return false;
393 }
394
400 return true;
401 }
402
403 /*
404 * Is CDE running?
405 *
406 * XXX: This is hairy... CDE is MWM as well. It seems we simply test
407 * for default setup and will be bitten if user changes things...
408 *
409 * Check for _DT_SM_WINDOW_INFO(_DT_SM_WINDOW_INFO) on root. Take the
410 * second element of the property and check for presence of
411 * _DT_SM_STATE_INFO(_DT_SM_STATE_INFO) on that window.
412 *
413 * XXX: Any header that defines this structures???
414 */
415 static final XAtom XA_DT_SM_WINDOW_INFO = new XAtom("_DT_SM_WINDOW_INFO", false);
416 static final XAtom XA_DT_SM_STATE_INFO = new XAtom("_DT_SM_STATE_INFO", false);
417 static boolean isCDE() {
418
419 if (!XA_DT_SM_WINDOW_INFO.isInterned()) {
420 if (log.isLoggable(PlatformLogger.Level.FINER)) {
421 log.finer("{0} is not interned", XA_DT_SM_WINDOW_INFO);
422 }
423 return false;
424 }
425
426 WindowPropertyGetter getter =
427 new WindowPropertyGetter(XToolkit.getDefaultRootWindow(),
428 XA_DT_SM_WINDOW_INFO, 0, 2,
429 false, XA_DT_SM_WINDOW_INFO);
430 try {
431 int status = getter.execute();
432 if (status != XConstants.Success || getter.getData() == 0) {
433 log.finer("Getting of _DT_SM_WINDOW_INFO is not successfull");
434 return false;
435 }
436 if (getter.getActualType() != XA_DT_SM_WINDOW_INFO.getAtom()
437 || getter.getActualFormat() != 32
438 || getter.getNumberOfItems() != 2 || getter.getBytesAfter() != 0)
439 {
440 log.finer("Wrong format of _DT_SM_WINDOW_INFO");
441 return false;
442 }
443
444 long wmwin = Native.getWindow(getter.getData(), 1); //unsafe.getInt(getter.getData()+4);
445
446 if (wmwin == 0) {
447 log.fine("WARNING: DT_SM_WINDOW_INFO exists but returns zero windows");
448 return false;
449 }
450
451 /* Now check that this window has _DT_SM_STATE_INFO (ignore contents) */
452 if (!XA_DT_SM_STATE_INFO.isInterned()) {
453 if (log.isLoggable(PlatformLogger.Level.FINER)) {
454 log.finer("{0} is not interned", XA_DT_SM_STATE_INFO);
455 }
456 return false;
457 }
458 WindowPropertyGetter getter2 =
459 new WindowPropertyGetter(wmwin, XA_DT_SM_STATE_INFO, 0, 1,
460 false, XA_DT_SM_STATE_INFO);
461 try {
462 status = getter2.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
463
464
465 if (status != XConstants.Success || getter2.getData() == 0) {
466 log.finer("Getting of _DT_SM_STATE_INFO is not successfull");
467 return false;
468 }
469 if (getter2.getActualType() != XA_DT_SM_STATE_INFO.getAtom()
470 || getter2.getActualFormat() != 32)
471 {
472 log.finer("Wrong format of _DT_SM_STATE_INFO");
473 return false;
607 *
608 * But messing with PropertyNotify here is way too much trouble, so
609 * approximate the check by setting the property in this function and
610 * checking if it still exists later on.
611 *
612 * Gaa, dirty dances...
613 */
614 static final XAtom XA_ICEWM_WINOPTHINT = new XAtom("_ICEWM_WINOPTHINT", false);
615 static final char opt[] = {
616 'A','W','T','_','I','C','E','W','M','_','T','E','S','T','\0',
617 'a','l','l','W','o','r','k','s','p','a','c','e','s','\0',
618 '0','\0'
619 };
620 static boolean prepareIsIceWM() {
621 /*
622 * Choose something innocuous: "AWT_ICEWM_TEST allWorkspaces 0".
623 * IceWM expects "class\0option\0arg\0" with zero bytes as delimiters.
624 */
625
626 if (!XA_ICEWM_WINOPTHINT.isInterned()) {
627 if (log.isLoggable(PlatformLogger.Level.FINER)) {
628 log.finer("{0} is not interned", XA_ICEWM_WINOPTHINT);
629 }
630 return false;
631 }
632
633 XToolkit.awtLock();
634 try {
635 XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
636 XlibWrapper.XChangePropertyS(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(),
637 XA_ICEWM_WINOPTHINT.getAtom(),
638 XA_ICEWM_WINOPTHINT.getAtom(),
639 8, XConstants.PropModeReplace,
640 new String(opt));
641 XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
642
643 if ((XErrorHandlerUtil.saved_error != null) &&
644 (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) {
645 log.finer("Erorr getting XA_ICEWM_WINOPTHINT property");
646 return false;
647 }
648 log.finer("Prepared for IceWM detection");
649 return true;
650 } finally {
651 XToolkit.awtUnlock();
652 }
653 }
654
655 /*
656 * Is IceWM running?
657 *
658 * Note well: Only call this if awt_wm_prepareIsIceWM succeeded, or a
659 * false positive will be reported.
660 */
661 static boolean isIceWM() {
662 if (!XA_ICEWM_WINOPTHINT.isInterned()) {
663 if (log.isLoggable(PlatformLogger.Level.FINER)) {
664 log.finer("{0} is not interned", XA_ICEWM_WINOPTHINT);
665 }
666 return false;
667 }
668
669 WindowPropertyGetter getter =
670 new WindowPropertyGetter(XToolkit.getDefaultRootWindow(),
671 XA_ICEWM_WINOPTHINT, 0, 0xFFFF,
672 true, XA_ICEWM_WINOPTHINT);
673 try {
674 int status = getter.execute();
675 boolean res = (status == XConstants.Success && getter.getActualType() != 0);
676 if (log.isLoggable(PlatformLogger.Level.FINER)) {
677 log.finer("Status getting XA_ICEWM_WINOPTHINT: " + !res);
678 }
679 return !res || isNetWMName("IceWM");
680 } finally {
681 getter.dispose();
682 }
683 }
684
685 /*
686 * Is OpenLook WM running?
687 *
688 * This one is pretty lame, but the only property peculiar to OLWM is
689 * _SUN_WM_PROTOCOLS(ATOM[]). Fortunately, olwm deletes it on exit.
690 */
691 static final XAtom XA_SUN_WM_PROTOCOLS = new XAtom("_SUN_WM_PROTOCOLS", false);
692 static boolean isOpenLook() {
693 if (!XA_SUN_WM_PROTOCOLS.isInterned()) {
694 return false;
695 }
696
712 winmgr_running = true;
713 return 0;
714 }
715 return super.handleError(display, err);
716 }
717 };
718
719 /*
720 * Make an educated guess about running window manager.
721 * XXX: ideally, we should detect wm restart.
722 */
723 static int awt_wmgr = XWM.UNDETERMINED_WM;
724 static XWM wm;
725 static XWM getWM() {
726 if (wm == null) {
727 wm = new XWM(awt_wmgr = getWMID()/*XWM.OTHER_WM*/);
728 }
729 return wm;
730 }
731 static int getWMID() {
732 if (insLog.isLoggable(PlatformLogger.Level.FINEST)) {
733 insLog.finest("awt_wmgr = " + awt_wmgr);
734 }
735 /*
736 * Ideally, we should support cases when a different WM is started
737 * during a Java app lifetime.
738 */
739
740 if (awt_wmgr != XWM.UNDETERMINED_WM) {
741 return awt_wmgr;
742 }
743
744 XSetWindowAttributes substruct = new XSetWindowAttributes();
745 XToolkit.awtLock();
746 try {
747 if (isNoWM()) {
748 awt_wmgr = XWM.NO_WM;
749 return awt_wmgr;
750 }
751
752 // Initialize _NET protocol - used to detect Window Manager.
753 // Later, WM will initialize its own version of protocol
754 XNETProtocol l_net_protocol = g_net_protocol = new XNETProtocol();
755 l_net_protocol.detect();
756 if (log.isLoggable(PlatformLogger.Level.FINE) && l_net_protocol.active()) {
757 log.fine("_NET_WM_NAME is " + l_net_protocol.getWMName());
758 }
759 XWINProtocol win = g_win_protocol = new XWINProtocol();
760 win.detect();
761
762 /* actual check for IceWM to follow below */
763 boolean doIsIceWM = prepareIsIceWM(); /* and let IceWM to act */
764
765 /*
766 * Ok, some WM is out there. Check which one by testing for
767 * "distinguishing" atoms.
768 */
769 if (isEnlightenment()) {
770 awt_wmgr = XWM.ENLIGHTEN_WM;
771 } else if (isMetacity()) {
772 awt_wmgr = XWM.METACITY_WM;
773 } else if (isMutter()) {
774 awt_wmgr = XWM.MUTTER_WM;
775 } else if (isSawfish()) {
776 awt_wmgr = XWM.SAWFISH_WM;
820 * Size and decoration hints ...
821 *
822 \*****************************************************************************/
823
824
825 /*
826 * Remove size hints specified by the mask.
827 * XXX: Why do we need this in the first place???
828 */
829 static void removeSizeHints(XDecoratedPeer window, long mask) {
830 mask &= XUtilConstants.PMaxSize | XUtilConstants.PMinSize;
831
832 XToolkit.awtLock();
833 try {
834 XSizeHints hints = window.getHints();
835 if ((hints.get_flags() & mask) == 0) {
836 return;
837 }
838
839 hints.set_flags(hints.get_flags() & ~mask);
840 if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
841 insLog.finer("Setting hints, flags " + XlibWrapper.hintsToString(hints.get_flags()));
842 }
843 XlibWrapper.XSetWMNormalHints(XToolkit.getDisplay(),
844 window.getWindow(),
845 hints.pData);
846 } finally {
847 XToolkit.awtUnlock();
848 }
849 }
850
851 /*
852 * If MWM_DECOR_ALL bit is set, then the rest of the bit-mask is taken
853 * to be subtracted from the decorations. Normalize decoration spec
854 * so that we can map motif decor to something else bit-by-bit in the
855 * rest of the code.
856 */
857 static int normalizeMotifDecor(int decorations) {
858 if ((decorations & MWMConstants.MWM_DECOR_ALL) == 0) {
859 return decorations;
860 }
879 int f = MWMConstants.MWM_FUNC_RESIZE |
880 MWMConstants.MWM_FUNC_MOVE |
881 MWMConstants.MWM_FUNC_MAXIMIZE |
882 MWMConstants.MWM_FUNC_MINIMIZE |
883 MWMConstants.MWM_FUNC_CLOSE;
884 f &= ~functions;
885 return f;
886 }
887
888 /*
889 * Infer OL properties from MWM decorations.
890 * Use _OL_DECOR_DEL(ATOM[]) to remove unwanted ones.
891 */
892 static void setOLDecor(XWindow window, boolean resizable, int decorations) {
893 if (window == null) {
894 return;
895 }
896
897 XAtomList decorDel = new XAtomList();
898 decorations = normalizeMotifDecor(decorations);
899 if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
900 insLog.finer("Setting OL_DECOR to " + Integer.toBinaryString(decorations));
901 }
902 if ((decorations & MWMConstants.MWM_DECOR_TITLE) == 0) {
903 decorDel.add(XA_OL_DECOR_HEADER);
904 }
905 if ((decorations & (MWMConstants.MWM_DECOR_RESIZEH | MWMConstants.MWM_DECOR_MAXIMIZE)) == 0) {
906 decorDel.add(XA_OL_DECOR_RESIZE);
907 }
908 if ((decorations & (MWMConstants.MWM_DECOR_MENU |
909 MWMConstants.MWM_DECOR_MAXIMIZE |
910 MWMConstants.MWM_DECOR_MINIMIZE)) == 0)
911 {
912 decorDel.add(XA_OL_DECOR_CLOSE);
913 }
914 if (decorDel.size() == 0) {
915 insLog.finer("Deleting OL_DECOR");
916 XA_OL_DECOR_DEL.DeleteProperty(window);
917 } else {
918 if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
919 insLog.finer("Setting OL_DECOR to " + decorDel);
920 }
921 XA_OL_DECOR_DEL.setAtomListProperty(window, decorDel);
922 }
923 }
924
925 /*
926 * Set MWM decorations. Set MWM functions depending on resizability.
927 */
928 static void setMotifDecor(XWindow window, boolean resizable, int decorations, int functions) {
929 /* Apparently some WMs don't implement MWM_*_ALL semantic correctly */
930 if ((decorations & MWMConstants.MWM_DECOR_ALL) != 0
931 && (decorations != MWMConstants.MWM_DECOR_ALL))
932 {
933 decorations = normalizeMotifDecor(decorations);
934 }
935 if ((functions & MWMConstants.MWM_FUNC_ALL) != 0
936 && (functions != MWMConstants.MWM_FUNC_ALL))
937 {
938 functions = normalizeMotifFunc(functions);
939 }
940
941 PropMwmHints hints = window.getMWMHints();
942 hints.set_flags(hints.get_flags() |
943 MWMConstants.MWM_HINTS_FUNCTIONS |
944 MWMConstants.MWM_HINTS_DECORATIONS);
945 hints.set_functions(functions);
946 hints.set_decorations(decorations);
947
948 if (stateLog.isLoggable(PlatformLogger.Level.FINER)) {
949 stateLog.finer("Setting MWM_HINTS to " + hints);
950 }
951 window.setMWMHints(hints);
952 }
953
954 /*
955 * Under some window managers if shell is already mapped, we MUST
956 * unmap and later remap in order to effect the changes we make in the
957 * window manager decorations.
958 *
959 * N.B. This unmapping / remapping of the shell exposes a bug in
960 * X/Motif or the Motif Window Manager. When you attempt to map a
961 * widget which is positioned (partially) off-screen, the window is
962 * relocated to be entirely on screen. Good idea. But if both the x
963 * and the y coordinates are less than the origin (0,0), the first
964 * (re)map will move the window to the origin, and any subsequent
965 * (re)map will relocate the window at some other point on the screen.
966 * I have written a short Motif test program to discover this bug.
967 * This should occur infrequently and it does not cause any real
968 * problem. So for now we'll let it be.
992 setMotifDecor(window, resizable, decorations, functions);
993 setOLDecor(window, resizable, decorations);
994
995 /* Some WMs need remap to redecorate the window */
996 if (window.isShowing() && needRemap(window)) {
997 /*
998 * Do the re/mapping at the Xlib level. Since we essentially
999 * work around a WM bug we don't want this hack to be exposed
1000 * to Intrinsics (i.e. don't mess with grabs, callbacks etc).
1001 */
1002 window.xSetVisible(false);
1003 XToolkit.XSync();
1004 window.xSetVisible(true);
1005 }
1006 }
1007
1008 /*
1009 * Make specified shell resizable.
1010 */
1011 static void setShellResizable(XDecoratedPeer window) {
1012 if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
1013 insLog.fine("Setting shell resizable " + window);
1014 }
1015 XToolkit.awtLock();
1016 try {
1017 Rectangle shellBounds = window.getShellBounds();
1018 shellBounds.translate(-window.currentInsets.left, -window.currentInsets.top);
1019 window.updateSizeHints(window.getDimensions());
1020 requestWMExtents(window.getWindow());
1021 XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), window.getShell(),
1022 shellBounds.x, shellBounds.y, shellBounds.width, shellBounds.height);
1023 /* REMINDER: will need to revisit when setExtendedStateBounds is added */
1024 //Fix for 4320050: Minimum size for java.awt.Frame is not being enforced.
1025 //We need to update frame's minimum size, not to reset it
1026 removeSizeHints(window, XUtilConstants.PMaxSize);
1027 window.updateMinimumSize();
1028
1029 /* Restore decorations */
1030 setShellDecor(window);
1031 } finally {
1032 XToolkit.awtUnlock();
1033 }
1034 }
1035
1036 /*
1037 * Make specified shell non-resizable.
1038 * If justChangeSize is false, update decorations as well.
1039 * @param shellBounds bounds of the shell window
1040 */
1041 static void setShellNotResizable(XDecoratedPeer window, WindowDimensions newDimensions, Rectangle shellBounds,
1042 boolean justChangeSize)
1043 {
1044 if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
1045 insLog.fine("Setting non-resizable shell " + window + ", dimensions " + newDimensions +
1046 ", shellBounds " + shellBounds +", just change size: " + justChangeSize);
1047 }
1048 XToolkit.awtLock();
1049 try {
1050 /* Fix min/max size hints at the specified values */
1051 if (!shellBounds.isEmpty()) {
1052 window.updateSizeHints(newDimensions);
1053 requestWMExtents(window.getWindow());
1054 XToolkit.XSync();
1055 XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), window.getShell(),
1056 shellBounds.x, shellBounds.y, shellBounds.width, shellBounds.height);
1057 }
1058 if (!justChangeSize) { /* update decorations */
1059 setShellDecor(window);
1060 }
1061 } finally {
1062 XToolkit.awtUnlock();
1063 }
1064 }
1158 *
1159 * Notice window state change when WM changes a property on the window ...
1160 *
1161 \*****************************************************************************/
1162
1163
1164 /*
1165 * Check if property change is a window state protocol message.
1166 */
1167 boolean isStateChange(XDecoratedPeer window, XPropertyEvent e) {
1168 if (!window.isShowing()) {
1169 stateLog.finer("Window is not showing");
1170 return false;
1171 }
1172
1173 int wm_state = window.getWMState();
1174 if (wm_state == XUtilConstants.WithdrawnState) {
1175 stateLog.finer("WithdrawnState");
1176 return false;
1177 } else {
1178 if (stateLog.isLoggable(PlatformLogger.Level.FINER)) {
1179 stateLog.finer("Window WM_STATE is " + wm_state);
1180 }
1181 }
1182 boolean is_state_change = false;
1183 if (e.get_atom() == XA_WM_STATE.getAtom()) {
1184 is_state_change = true;
1185 }
1186
1187 for (XStateProtocol proto : getProtocols(XStateProtocol.class)) {
1188 is_state_change |= proto.isStateChange(e);
1189 if (stateLog.isLoggable(PlatformLogger.Level.FINEST)) {
1190 stateLog.finest(proto + ": is state changed = " + is_state_change);
1191 }
1192 }
1193 return is_state_change;
1194 }
1195
1196 /*
1197 * Returns current state (including extended) of a given window.
1198 */
1199 int getState(XDecoratedPeer window) {
1200 int res = 0;
1201 final int wm_state = window.getWMState();
1202 if (wm_state == XUtilConstants.IconicState) {
1203 res = Frame.ICONIFIED;
1204 } else {
1205 res = Frame.NORMAL;
1206 }
1207 res |= getExtendedState(window);
1208 return res;
1209 }
1323 Insets guessInsets(XDecoratedPeer window) {
1324 Insets res = (Insets)storedInsets.get(window.getClass());
1325 if (res == null) {
1326 switch (WMID) {
1327 case ENLIGHTEN_WM:
1328 res = new Insets(19, 4, 4, 4);
1329 break;
1330 case CDE_WM:
1331 res = new Insets(28, 6, 6, 6);
1332 break;
1333 case NO_WM:
1334 case LG3D_WM:
1335 res = zeroInsets;
1336 break;
1337 case MOTIF_WM:
1338 case OPENLOOK_WM:
1339 default:
1340 res = defaultInsets;
1341 }
1342 }
1343 if (insLog.isLoggable(PlatformLogger.Level.FINEST)) {
1344 insLog.finest("WM guessed insets: " + res);
1345 }
1346 return res;
1347 }
1348 /*
1349 * Some buggy WMs ignore window gravity when processing
1350 * ConfigureRequest and position window as if the gravity is Static.
1351 * We work around this in MWindowPeer.pReshape().
1352 *
1353 * Starting with 1.5 we have introduced an Environment variable
1354 * _JAVA_AWT_WM_STATIC_GRAVITY that can be set to indicate to Java
1355 * explicitly that the WM has this behaviour, example is FVWM.
1356 */
1357
1358 static int awtWMStaticGravity = -1;
1359 static boolean configureGravityBuggy() {
1360
1361 if (awtWMStaticGravity == -1) {
1362 awtWMStaticGravity = (XToolkit.getEnv("_JAVA_AWT_WM_STATIC_GRAVITY") != null) ? 1 : 0;
1363 }
1394 return true;
1395 case XWM.ENLIGHTEN_WM:
1396 /* At least E16 is buggy. */
1397 return true;
1398 default:
1399 return false;
1400 }
1401 }
1402
1403 /*
1404 * @return if WM implements the insets property - returns insets with values
1405 * specified in that property, null otherwise.
1406 */
1407 public static Insets getInsetsFromExtents(long window) {
1408 if (window == XConstants.None) {
1409 return null;
1410 }
1411 XNETProtocol net_protocol = getWM().getNETProtocol();
1412 if (net_protocol != null && net_protocol.active()) {
1413 Insets insets = getInsetsFromProp(window, XA_NET_FRAME_EXTENTS);
1414 if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
1415 insLog.fine("_NET_FRAME_EXTENTS: {0}", insets);
1416 }
1417
1418 if (insets != null) {
1419 return insets;
1420 }
1421 }
1422 switch(getWMID()) {
1423 case XWM.KDE2_WM:
1424 return getInsetsFromProp(window, XA_KDE_NET_WM_FRAME_STRUT);
1425 case XWM.ENLIGHTEN_WM:
1426 return getInsetsFromProp(window, XA_E_FRAME_SIZE);
1427 default:
1428 return null;
1429 }
1430 }
1431
1432 /**
1433 * Helper function reads property of type CARDINAL[4] = { left, right, top, bottom }
1434 * and converts it to Insets object.
1537 * peeking at it. [Future versions of wm-spec might add a
1538 * standardized hint for this].
1539 *
1540 * Otherwise we do some special casing. Actually the
1541 * fallback code ("default" case) seems to cover most of
1542 * the existing WMs (modulo Reparent/Configure order
1543 * perhaps?).
1544 *
1545 * Fallback code tries to account for the two most common cases:
1546 *
1547 * . single reparenting
1548 * parent window is the WM frame
1549 * [twm, olwm, sawfish]
1550 *
1551 * . double reparenting
1552 * parent is a lining exactly the size of the client
1553 * grandpa is the WM frame
1554 * [mwm, e!, kwin, fvwm2 ... ]
1555 */
1556 Insets correctWM = XWM.getInsetsFromExtents(window);
1557 if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
1558 insLog.finer("Got insets from property: {0}", correctWM);
1559 }
1560
1561 if (correctWM == null) {
1562 correctWM = new Insets(0,0,0,0);
1563
1564 correctWM.top = -1;
1565 correctWM.left = -1;
1566
1567 XWindowAttributes lwinAttr = new XWindowAttributes();
1568 XWindowAttributes pattr = new XWindowAttributes();
1569 try {
1570 switch (XWM.getWMID()) {
1571 /* should've been done in awt_wm_getInsetsFromProp */
1572 case XWM.ENLIGHTEN_WM: {
1573 /* enlightenment does double reparenting */
1574 syncTopLevelPos(parent, lwinAttr);
1575 correctWM.left = lwinAttr.get_x();
1576 correctWM.top = lwinAttr.get_y();
1577 /*
1600 correctWM.left = lwinAttr.get_x();
1601 correctWM.right = correctWM.left;
1602 correctWM.bottom = correctWM.left;
1603 } else {
1604 return null;
1605 }
1606 break;
1607 }
1608 case XWM.SAWFISH_WM:
1609 case XWM.OPENLOOK_WM: {
1610 /* single reparenting */
1611 syncTopLevelPos(window, lwinAttr);
1612 correctWM.top = lwinAttr.get_y();
1613 correctWM.left = lwinAttr.get_x();
1614 correctWM.right = correctWM.left;
1615 correctWM.bottom = correctWM.left;
1616 break;
1617 }
1618 case XWM.OTHER_WM:
1619 default: { /* this is very similar to the E! case above */
1620 if (insLog.isLoggable(PlatformLogger.Level.FINEST)) {
1621 insLog.finest("Getting correct insets for OTHER_WM/default, parent: {0}", parent);
1622 }
1623 syncTopLevelPos(parent, lwinAttr);
1624 int status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1625 window, lwinAttr.pData);
1626 status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1627 parent, pattr.pData);
1628 if (lwinAttr.get_root() == parent) {
1629 insLog.finest("our parent is root so insets should be zero");
1630 correctWM = new Insets(0, 0, 0, 0);
1631 break;
1632 }
1633
1634 /*
1635 * Check for double-reparenting WM.
1636 *
1637 * If the parent is exactly the same size as the
1638 * top-level assume taht it's the "lining" window and
1639 * that the grandparent is the actual frame (NB: we
1640 * have already handled undecorated windows).
1641 *
1642 * XXX: what about timing issues that syncTopLevelPos
1643 * is supposed to work around?
1644 */
1645 if (lwinAttr.get_x() == 0 && lwinAttr.get_y() == 0
1646 && lwinAttr.get_width()+2*lwinAttr.get_border_width() == pattr.get_width()
1647 && lwinAttr.get_height()+2*lwinAttr.get_border_width() == pattr.get_height())
1648 {
1649 if (insLog.isLoggable(PlatformLogger.Level.FINEST)) {
1650 insLog.finest("Double reparenting detected, pattr({2})={0}, lwinAttr({3})={1}",
1651 lwinAttr, pattr, parent, window);
1652 }
1653 lwinAttr.set_x(pattr.get_x());
1654 lwinAttr.set_y(pattr.get_y());
1655 lwinAttr.set_border_width(lwinAttr.get_border_width()+pattr.get_border_width());
1656
1657 final long grand_parent = XlibUtil.getParentWindow(parent);
1658
1659 if (grand_parent == lwinAttr.get_root()) {
1660 // This is not double-reparenting in a
1661 // general sense - we simply don't have
1662 // correct insets - return null to try to
1663 // get insets later
1664 return null;
1665 } else {
1666 parent = grand_parent;
1667 XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1668 parent,
1669 pattr.pData);
1670 }
1671 }
1672 /*
1673 * XXX: To be absolutely correct, we'd need to take
1674 * parent's border-width into account too, but the
1675 * rest of the code is happily unaware about border
1676 * widths and inner/outer distinction, so for the time
1677 * being, just ignore it.
1678 */
1679 if (insLog.isLoggable(PlatformLogger.Level.FINEST)) {
1680 insLog.finest("Attrs before calculation: pattr({2})={0}, lwinAttr({3})={1}",
1681 lwinAttr, pattr, parent, window);
1682 }
1683 correctWM = new Insets(lwinAttr.get_y() + lwinAttr.get_border_width(),
1684 lwinAttr.get_x() + lwinAttr.get_border_width(),
1685 pattr.get_height() - (lwinAttr.get_y() + lwinAttr.get_height() + 2*lwinAttr.get_border_width()),
1686 pattr.get_width() - (lwinAttr.get_x() + lwinAttr.get_width() + 2*lwinAttr.get_border_width()));
1687 break;
1688 } /* default */
1689 } /* switch (runningWM) */
1690 } finally {
1691 lwinAttr.dispose();
1692 pattr.dispose();
1693 }
1694 }
1695 if (storedInsets.get(win.getClass()) == null) {
1696 storedInsets.put(win.getClass(), correctWM);
1697 }
1698 return correctWM;
1699 }
|