/* * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.awt.X11; import java.awt.*; import java.awt.event.ComponentEvent; import java.awt.event.FocusEvent; import java.awt.event.WindowEvent; import java.awt.geom.AffineTransform; import java.awt.peer.ComponentPeer; import java.awt.peer.WindowPeer; import java.io.UnsupportedEncodingException; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.Vector; import java.util.concurrent.atomic.AtomicBoolean; import sun.awt.AWTAccessor.ComponentAccessor; import sun.util.logging.PlatformLogger; import sun.awt.AWTAccessor; import sun.awt.DisplayChangedListener; import sun.awt.SunToolkit; import sun.awt.X11GraphicsDevice; import sun.awt.X11GraphicsEnvironment; import sun.awt.IconInfo; import sun.java2d.pipe.Region; class XWindowPeer extends XPanelPeer implements WindowPeer, DisplayChangedListener { private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XWindowPeer"); private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.awt.X11.focus.XWindowPeer"); private static final PlatformLogger insLog = PlatformLogger.getLogger("sun.awt.X11.insets.XWindowPeer"); private static final PlatformLogger grabLog = PlatformLogger.getLogger("sun.awt.X11.grab.XWindowPeer"); private static final PlatformLogger iconLog = PlatformLogger.getLogger("sun.awt.X11.icon.XWindowPeer"); // should be synchronized on awtLock private static Set windows = new HashSet(); private boolean cachedFocusableWindow; XWarningWindow warningWindow; private boolean alwaysOnTop; private boolean locationByPlatform; Dialog modalBlocker; boolean delayedModalBlocking = false; Dimension targetMinimumSize = null; private XWindowPeer ownerPeer; // used for modal blocking to keep existing z-order protected XWindowPeer prevTransientFor, nextTransientFor; // value of WM_TRANSIENT_FOR hint set on this window private XBaseWindow curRealTransientFor; private boolean grab = false; // Whether to do a grab during showing private boolean isMapped = false; // Is this window mapped or not private boolean mustControlStackPosition = false; // Am override-redirect not on top private XEventDispatcher rootPropertyEventDispatcher = null; private static final AtomicBoolean isStartupNotificationRemoved = new AtomicBoolean(); /* * Focus related flags */ private boolean isUnhiding = false; // Is the window unhiding. private boolean isBeforeFirstMapNotify = false; // Is the window (being shown) between // setVisible(true) & handleMapNotify(). /** * The type of the window. * * The type is supposed to be immutable while the peer object exists. * The value gets initialized in the preInit() method. */ private Window.Type windowType = Window.Type.NORMAL; public final Window.Type getWindowType() { return windowType; } // It need to be accessed from XFramePeer. protected Vector toplevelStateListeners = new Vector(); XWindowPeer(XCreateWindowParams params) { super(params.putIfNull(PARENT_WINDOW, Long.valueOf(0))); } XWindowPeer(Window target) { super(new XCreateWindowParams(new Object[] { TARGET, target, PARENT_WINDOW, Long.valueOf(0)})); } /* * This constant defines icon size recommended for using. * Apparently, we should use XGetIconSizes which should * return icon sizes would be most appreciated by the WM. * However, XGetIconSizes always returns 0 for some reason. * So the constant has been introduced. */ private static final int PREFERRED_SIZE_FOR_ICON = 128; /* * Sometimes XChangeProperty(_NET_WM_ICON) doesn't work if * image buffer is too large. This constant holds maximum * length of buffer which can be used with _NET_WM_ICON hint. * It holds int's value. */ private static final int MAXIMUM_BUFFER_LENGTH_NET_WM_ICON = (2<<15) - 1; void preInit(XCreateWindowParams params) { target = (Component)params.get(TARGET); windowType = ((Window)target).getType(); params.put(REPARENTED, Boolean.valueOf(isOverrideRedirect() || isSimpleWindow())); super.preInit(params); params.putIfNull(BIT_GRAVITY, Integer.valueOf(XConstants.NorthWestGravity)); long eventMask = 0; if (params.containsKey(EVENT_MASK)) { eventMask = ((Long)params.get(EVENT_MASK)); } eventMask |= XConstants.VisibilityChangeMask; params.put(EVENT_MASK, eventMask); XA_NET_WM_STATE = XAtom.get("_NET_WM_STATE"); params.put(OVERRIDE_REDIRECT, Boolean.valueOf(isOverrideRedirect())); SunToolkit.awtLock(); try { windows.add(this); } finally { SunToolkit.awtUnlock(); } cachedFocusableWindow = isFocusableWindow(); if (!target.isBackgroundSet()) { target.setBackground(SystemColor.window); } if (!target.isForegroundSet()) { target.setForeground(SystemColor.windowText); } if (!target.isFontSet()) { target.setFont(XWindow.getDefaultFont()); } alwaysOnTop = ((Window)target).isAlwaysOnTop() && ((Window)target).isAlwaysOnTopSupported(); GraphicsConfiguration gc = getGraphicsConfiguration(); ((X11GraphicsDevice)gc.getDevice()).addDisplayChangedListener(this); } protected String getWMName() { String name = target.getName(); if (name == null || name.trim().equals("")) { name = " "; } return name; } private static native String getLocalHostname(); private static native int getJvmPID(); @SuppressWarnings("deprecation") void postInit(XCreateWindowParams params) { super.postInit(params); // Init WM_PROTOCOLS atom initWMProtocols(); // Set _NET_WM_PID and WM_CLIENT_MACHINE using this JVM XAtom.get("WM_CLIENT_MACHINE").setProperty(getWindow(), getLocalHostname()); XAtom.get("_NET_WM_PID").setCard32Property(getWindow(), getJvmPID()); // Set WM_TRANSIENT_FOR and group_leader Window t_window = (Window)target; Window owner = t_window.getOwner(); if (owner != null) { ownerPeer = AWTAccessor.getComponentAccessor().getPeer(owner); if (focusLog.isLoggable(PlatformLogger.Level.FINER)) { focusLog.finer("Owner is " + owner); focusLog.finer("Owner peer is " + ownerPeer); focusLog.finer("Owner X window " + Long.toHexString(ownerPeer.getWindow())); focusLog.finer("Owner content X window " + Long.toHexString(ownerPeer.getContentWindow())); } // as owner window may be an embedded window, we must get a toplevel window // to set as TRANSIENT_FOR hint long ownerWindow = ownerPeer.getWindow(); if (ownerWindow != 0) { XToolkit.awtLock(); try { // Set WM_TRANSIENT_FOR if (focusLog.isLoggable(PlatformLogger.Level.FINE)) { focusLog.fine("Setting transient on " + Long.toHexString(getWindow()) + " for " + Long.toHexString(ownerWindow)); } setToplevelTransientFor(this, ownerPeer, false, true); // Set group leader XWMHints hints = getWMHints(); hints.set_flags(hints.get_flags() | (int)XUtilConstants.WindowGroupHint); hints.set_window_group(ownerWindow); XlibWrapper.XSetWMHints(XToolkit.getDisplay(), getWindow(), hints.pData); } finally { XToolkit.awtUnlock(); } } } if (owner != null || isSimpleWindow()) { XNETProtocol protocol = XWM.getWM().getNETProtocol(); if (protocol != null && protocol.active()) { XToolkit.awtLock(); try { XAtomList net_wm_state = getNETWMState(); net_wm_state.add(protocol.XA_NET_WM_STATE_SKIP_TASKBAR); setNETWMState(net_wm_state); } finally { XToolkit.awtUnlock(); } } } // Init warning window(for applets) if (((Window)target).getWarningString() != null) { // accessSystemTray permission allows to display TrayIcon, TrayIcon tooltip // and TrayIcon balloon windows without a warning window. if (!AWTAccessor.getWindowAccessor().isTrayIconWindow((Window)target)) { warningWindow = new XWarningWindow((Window)target, getWindow(), this); } } setSaveUnder(true); updateIconImages(); updateShape(); updateOpacity(); // no need in updateOpaque() as it is no-op } public void updateIconImages() { Window target = (Window)this.target; java.util.List iconImages = target.getIconImages(); XWindowPeer ownerPeer = getOwnerPeer(); winAttr.icons = new ArrayList(); if (iconImages.size() != 0) { //read icon images from target winAttr.iconsInherited = false; for (Iterator i = iconImages.iterator(); i.hasNext(); ) { Image image = i.next(); if (image == null) { if (log.isLoggable(PlatformLogger.Level.FINEST)) { log.finest("XWindowPeer.updateIconImages: Skipping the image passed into Java because it's null."); } continue; } IconInfo iconInfo; try { iconInfo = new IconInfo(image); } catch (Exception e){ if (log.isLoggable(PlatformLogger.Level.FINEST)) { log.finest("XWindowPeer.updateIconImages: Perhaps the image passed into Java is broken. Skipping this icon."); } continue; } if (iconInfo.isValid()) { winAttr.icons.add(iconInfo); } } } // Fix for CR#6425089 winAttr.icons = normalizeIconImages(winAttr.icons); if (winAttr.icons.size() == 0) { //target.icons is empty or all icon images are broken if (ownerPeer != null) { //icon is inherited from parent winAttr.iconsInherited = true; winAttr.icons = ownerPeer.getIconInfo(); } else { //default icon is used winAttr.iconsInherited = false; winAttr.icons = getDefaultIconInfo(); } } recursivelySetIcon(winAttr.icons); } /* * Sometimes XChangeProperty(_NET_WM_ICON) doesn't work if * image buffer is too large. This function help us accommodate * initial list of the icon images to certainly-acceptable. * It does scale some of these icons to appropriate size * if it's necessary. */ static java.util.List normalizeIconImages(java.util.List icons) { java.util.List result = new ArrayList(); int totalLength = 0; boolean haveLargeIcon = false; for (IconInfo icon : icons) { int width = icon.getWidth(); int height = icon.getHeight(); int length = icon.getRawLength(); if (width > PREFERRED_SIZE_FOR_ICON || height > PREFERRED_SIZE_FOR_ICON) { if (haveLargeIcon) { continue; } int scaledWidth = width; int scaledHeight = height; while (scaledWidth > PREFERRED_SIZE_FOR_ICON || scaledHeight > PREFERRED_SIZE_FOR_ICON) { scaledWidth = scaledWidth / 2; scaledHeight = scaledHeight / 2; } icon.setScaledSize(scaledWidth, scaledHeight); length = icon.getRawLength(); } if (totalLength + length <= MAXIMUM_BUFFER_LENGTH_NET_WM_ICON) { totalLength += length; result.add(icon); if (width > PREFERRED_SIZE_FOR_ICON || height > PREFERRED_SIZE_FOR_ICON) { haveLargeIcon = true; } } } if (iconLog.isLoggable(PlatformLogger.Level.FINEST)) { iconLog.finest(">>> Length_ of buffer of icons data: " + totalLength + ", maximum length: " + MAXIMUM_BUFFER_LENGTH_NET_WM_ICON); } return result; } /* * Dumps each icon from the list */ static void dumpIcons(java.util.List icons) { if (iconLog.isLoggable(PlatformLogger.Level.FINEST)) { iconLog.finest(">>> Sizes of icon images:"); for (Iterator i = icons.iterator(); i.hasNext(); ) { iconLog.finest(" {0}", i.next()); } } } public void recursivelySetIcon(java.util.List icons) { dumpIcons(winAttr.icons); setIconHints(icons); Window target = (Window)this.target; Window[] children = target.getOwnedWindows(); int cnt = children.length; final ComponentAccessor acc = AWTAccessor.getComponentAccessor(); for (int i = 0; i < cnt; i++) { final ComponentPeer childPeer = acc.getPeer(children[i]); if (childPeer != null && childPeer instanceof XWindowPeer) { if (((XWindowPeer)childPeer).winAttr.iconsInherited) { ((XWindowPeer)childPeer).winAttr.icons = icons; ((XWindowPeer)childPeer).recursivelySetIcon(icons); } } } } java.util.List getIconInfo() { return winAttr.icons; } void setIconHints(java.util.List icons) { //This does nothing for XWindowPeer, //It's overriden in XDecoratedPeer } private static ArrayList defaultIconInfo; protected static synchronized java.util.List getDefaultIconInfo() { if (defaultIconInfo == null) { defaultIconInfo = new ArrayList(); if (XlibWrapper.dataModel == 32) { defaultIconInfo.add(new IconInfo(sun.awt.AWTIcon32_java_icon16_png.java_icon16_png)); defaultIconInfo.add(new IconInfo(sun.awt.AWTIcon32_java_icon24_png.java_icon24_png)); defaultIconInfo.add(new IconInfo(sun.awt.AWTIcon32_java_icon32_png.java_icon32_png)); defaultIconInfo.add(new IconInfo(sun.awt.AWTIcon32_java_icon48_png.java_icon48_png)); } else { defaultIconInfo.add(new IconInfo(sun.awt.AWTIcon64_java_icon16_png.java_icon16_png)); defaultIconInfo.add(new IconInfo(sun.awt.AWTIcon64_java_icon24_png.java_icon24_png)); defaultIconInfo.add(new IconInfo(sun.awt.AWTIcon64_java_icon32_png.java_icon32_png)); defaultIconInfo.add(new IconInfo(sun.awt.AWTIcon64_java_icon48_png.java_icon48_png)); } } return defaultIconInfo; } private void updateShape() { // Shape shape = ((Window)target).getShape(); Shape shape = AWTAccessor.getWindowAccessor().getShape((Window)target); if (shape != null) { applyShape(Region.getInstance(shape, null)); } } private void updateOpacity() { // float opacity = ((Window)target).getOpacity(); float opacity = AWTAccessor.getWindowAccessor().getOpacity((Window)target); if (opacity < 1.0f) { setOpacity(opacity); } } public void updateMinimumSize() { //This function only saves minimumSize value in XWindowPeer //Setting WMSizeHints is implemented in XDecoratedPeer targetMinimumSize = (target.isMinimumSizeSet()) ? target.getMinimumSize() : null; } public Dimension getTargetMinimumSize() { return (targetMinimumSize == null) ? null : new Dimension(targetMinimumSize); } public XWindowPeer getOwnerPeer() { return ownerPeer; } //Fix for 6318144: PIT:Setting Min Size bigger than current size enlarges //the window but fails to revalidate, Sol-CDE //This bug is regression for //5025858: Resizing a decorated frame triggers componentResized event twice. //Since events are not posted from Component.setBounds we need to send them here. //Note that this function is overriden in XDecoratedPeer so event //posting is not changing for decorated peers public void setBounds(int x, int y, int width, int height, int op) { XToolkit.awtLock(); try { Rectangle oldBounds = getBounds(); super.setBounds(x, y, width, height, op); Rectangle bounds = getBounds(); XSizeHints hints = getHints(); setSizeHints(hints.get_flags() | XUtilConstants.PPosition | XUtilConstants.PSize, bounds.x, bounds.y, bounds.width, bounds.height); XWM.setMotifDecor(this, false, 0, 0); boolean isResized = !bounds.getSize().equals(oldBounds.getSize()); boolean isMoved = !bounds.getLocation().equals(oldBounds.getLocation()); if (isMoved || isResized) { repositionSecurityWarning(); } if (isResized) { postEventToEventQueue(new ComponentEvent(getEventSource(), ComponentEvent.COMPONENT_RESIZED)); } if (isMoved) { postEventToEventQueue(new ComponentEvent(getEventSource(), ComponentEvent.COMPONENT_MOVED)); } } finally { XToolkit.awtUnlock(); } } void updateFocusability() { updateFocusableWindowState(); XToolkit.awtLock(); try { XWMHints hints = getWMHints(); hints.set_flags(hints.get_flags() | (int)XUtilConstants.InputHint); hints.set_input(false/*isNativelyNonFocusableWindow() ? (0):(1)*/); XlibWrapper.XSetWMHints(XToolkit.getDisplay(), getWindow(), hints.pData); } finally { XToolkit.awtUnlock(); } } public Insets getInsets() { return new Insets(0, 0, 0, 0); } // NOTE: This method may be called by privileged threads. // DO NOT INVOKE CLIENT CODE ON THIS THREAD! public void handleIconify() { postEvent(new WindowEvent((Window)target, WindowEvent.WINDOW_ICONIFIED)); } // NOTE: This method may be called by privileged threads. // DO NOT INVOKE CLIENT CODE ON THIS THREAD! public void handleDeiconify() { postEvent(new WindowEvent((Window)target, WindowEvent.WINDOW_DEICONIFIED)); } // NOTE: This method may be called by privileged threads. // DO NOT INVOKE CLIENT CODE ON THIS THREAD! public void handleStateChange(int oldState, int newState) { postEvent(new WindowEvent((Window)target, WindowEvent.WINDOW_STATE_CHANGED, oldState, newState)); } /** * DEPRECATED: Replaced by getInsets(). */ public Insets insets() { return getInsets(); } boolean isAutoRequestFocus() { if (XToolkit.isToolkitThread()) { return AWTAccessor.getWindowAccessor().isAutoRequestFocus((Window)target); } else { return ((Window)target).isAutoRequestFocus(); } } /* * Retrives real native focused window and converts it into Java peer. */ static XWindowPeer getNativeFocusedWindowPeer() { XBaseWindow baseWindow = XToolkit.windowToXWindow(xGetInputFocus()); return (baseWindow instanceof XWindowPeer) ? (XWindowPeer)baseWindow : (baseWindow instanceof XFocusProxyWindow) ? ((XFocusProxyWindow)baseWindow).getOwner() : null; } /* * Retrives real native focused window and converts it into Java window. */ static Window getNativeFocusedWindow() { XWindowPeer peer = getNativeFocusedWindowPeer(); return peer != null ? (Window)peer.target : null; } boolean isFocusableWindow() { if (XToolkit.isToolkitThread() || SunToolkit.isAWTLockHeldByCurrentThread()) { return cachedFocusableWindow; } else { return ((Window)target).isFocusableWindow(); } } /* WARNING: don't call client code in this method! */ boolean isFocusedWindowModalBlocker() { return false; } long getFocusTargetWindow() { return getContentWindow(); } /** * Returns whether or not this window peer has native X window * configured as non-focusable window. It might happen if: * - Java window is non-focusable * - Java window is simple Window(not Frame or Dialog) */ boolean isNativelyNonFocusableWindow() { if (XToolkit.isToolkitThread() || SunToolkit.isAWTLockHeldByCurrentThread()) { return isSimpleWindow() || !cachedFocusableWindow; } else { return isSimpleWindow() || !(((Window)target).isFocusableWindow()); } } public void handleWindowFocusIn_Dispatch() { if (EventQueue.isDispatchThread()) { XKeyboardFocusManagerPeer.getInstance().setCurrentFocusedWindow((Window) target); WindowEvent we = new WindowEvent((Window)target, WindowEvent.WINDOW_GAINED_FOCUS); SunToolkit.setSystemGenerated(we); target.dispatchEvent(we); } } public void handleWindowFocusInSync(long serial) { WindowEvent we = new WindowEvent((Window)target, WindowEvent.WINDOW_GAINED_FOCUS); XKeyboardFocusManagerPeer.getInstance().setCurrentFocusedWindow((Window) target); sendEvent(we); } // NOTE: This method may be called by privileged threads. // DO NOT INVOKE CLIENT CODE ON THIS THREAD! public void handleWindowFocusIn(long serial) { WindowEvent we = new WindowEvent((Window)target, WindowEvent.WINDOW_GAINED_FOCUS); /* wrap in Sequenced, then post*/ XKeyboardFocusManagerPeer.getInstance().setCurrentFocusedWindow((Window) target); postEvent(wrapInSequenced((AWTEvent) we)); } // NOTE: This method may be called by privileged threads. // DO NOT INVOKE CLIENT CODE ON THIS THREAD! public void handleWindowFocusOut(Window oppositeWindow, long serial) { WindowEvent we = new WindowEvent((Window)target, WindowEvent.WINDOW_LOST_FOCUS, oppositeWindow); XKeyboardFocusManagerPeer.getInstance().setCurrentFocusedWindow(null); XKeyboardFocusManagerPeer.getInstance().setCurrentFocusOwner(null); /* wrap in Sequenced, then post*/ postEvent(wrapInSequenced((AWTEvent) we)); } public void handleWindowFocusOutSync(Window oppositeWindow, long serial) { WindowEvent we = new WindowEvent((Window)target, WindowEvent.WINDOW_LOST_FOCUS, oppositeWindow); XKeyboardFocusManagerPeer.getInstance().setCurrentFocusedWindow(null); XKeyboardFocusManagerPeer.getInstance().setCurrentFocusOwner(null); sendEvent(we); } /* --- DisplayChangedListener Stuff --- */ /* Xinerama * called to check if we've been moved onto a different screen * Based on checkNewXineramaScreen() in awt_GraphicsEnv.c */ public void checkIfOnNewScreen(Rectangle newBounds) { if (!XToolkit.localEnv.runningXinerama()) { return; } if (log.isLoggable(PlatformLogger.Level.FINEST)) { log.finest("XWindowPeer: Check if we've been moved to a new screen since we're running in Xinerama mode"); } int area = newBounds.width * newBounds.height; int intAmt, vertAmt, horizAmt; int largestAmt = 0; int curScreenNum = ((X11GraphicsDevice)getGraphicsConfiguration().getDevice()).getScreen(); int newScreenNum = 0; GraphicsDevice gds[] = XToolkit.localEnv.getScreenDevices(); GraphicsConfiguration newGC = null; Rectangle screenBounds; XToolkit.awtUnlock(); try { for (int i = 0; i < gds.length; i++) { screenBounds = gds[i].getDefaultConfiguration().getBounds(); if (newBounds.intersects(screenBounds)) { horizAmt = Math.min(newBounds.x + newBounds.width, screenBounds.x + screenBounds.width) - Math.max(newBounds.x, screenBounds.x); vertAmt = Math.min(newBounds.y + newBounds.height, screenBounds.y + screenBounds.height)- Math.max(newBounds.y, screenBounds.y); intAmt = horizAmt * vertAmt; if (intAmt == area) { // Completely on this screen - done! newScreenNum = i; newGC = gds[i].getDefaultConfiguration(); break; } if (intAmt > largestAmt) { largestAmt = intAmt; newScreenNum = i; newGC = gds[i].getDefaultConfiguration(); } } } } finally { XToolkit.awtLock(); } if (newScreenNum != curScreenNum) { if (log.isLoggable(PlatformLogger.Level.FINEST)) { log.finest("XWindowPeer: Moved to a new screen"); } executeDisplayChangedOnEDT(newGC); } } /** * Helper method that executes the displayChanged(screen) method on * the event dispatch thread. This method is used in the Xinerama case * and after display mode change events. */ private void executeDisplayChangedOnEDT(final GraphicsConfiguration gc) { Runnable dc = new Runnable() { public void run() { AWTAccessor.getComponentAccessor(). setGraphicsConfiguration(target, gc); } }; SunToolkit.executeOnEventHandlerThread(target, dc); } /** * From the DisplayChangedListener interface; called from * X11GraphicsDevice when the display mode has been changed. */ public void displayChanged() { executeDisplayChangedOnEDT(getGraphicsConfiguration()); } /** * From the DisplayChangedListener interface; top-levels do not need * to react to this event. */ public void paletteChanged() { } private Point queryXLocation() { return XlibUtil.translateCoordinates(getContentWindow(), XlibWrapper .RootWindow(XToolkit.getDisplay(), getScreenNumber()), new Point(0, 0), getScale()); } protected Point getNewLocation(XConfigureEvent xe, int leftInset, int topInset) { // Bounds of the window Rectangle targetBounds = AWTAccessor.getComponentAccessor().getBounds(target); int runningWM = XWM.getWMID(); Point newLocation = targetBounds.getLocation(); if (xe.get_send_event() || runningWM == XWM.NO_WM || XWM.isNonReparentingWM()) { // Location, Client size + insets newLocation = new Point(scaleDown(xe.get_x()) - leftInset, scaleDown(xe.get_y()) - topInset); } else { // ICCCM 4.1.5 states that a real ConfigureNotify will be sent when // a window is resized but the client can not tell if the window was // moved or not. The client should consider the position as unkown // and use TranslateCoordinates to find the actual position. // // TODO this should be the default for every case. switch (runningWM) { case XWM.CDE_WM: case XWM.MOTIF_WM: case XWM.METACITY_WM: case XWM.MUTTER_WM: case XWM.SAWFISH_WM: { Point xlocation = queryXLocation(); if (log.isLoggable(PlatformLogger.Level.FINE)) { log.fine("New X location: {0}", xlocation); } if (xlocation != null) { newLocation = xlocation; } break; } default: break; } } return newLocation; } /* * Overridden to check if we need to update our GraphicsDevice/Config * Added for 4934052. */ @Override public void handleConfigureNotifyEvent(XEvent xev) { assert (SunToolkit.isAWTLockHeldByCurrentThread()); XConfigureEvent xe = xev.get_xconfigure(); if (insLog.isLoggable(PlatformLogger.Level.FINE)) { insLog.fine(xe.toString()); } checkIfOnNewScreen(toGlobal(new Rectangle(scaleDown(xe.get_x()), scaleDown(xe.get_y()), scaleDown(xe.get_width()), scaleDown(xe.get_height())))); Rectangle oldBounds = getBounds(); x = scaleDown(xe.get_x()); y = scaleDown(xe.get_y()); width = scaleDown(xe.get_width()); height = scaleDown(xe.get_height()); if (!getBounds().getSize().equals(oldBounds.getSize())) { AWTAccessor.getComponentAccessor().setSize(target, width, height); postEvent(new ComponentEvent(target, ComponentEvent.COMPONENT_RESIZED)); } if (!getBounds().getLocation().equals(oldBounds.getLocation())) { AWTAccessor.getComponentAccessor().setLocation(target, x, y); postEvent(new ComponentEvent(target, ComponentEvent.COMPONENT_MOVED)); } repositionSecurityWarning(); } final void requestXFocus(long time) { requestXFocus(time, true); } final void requestXFocus() { requestXFocus(0, false); } /** * Requests focus to this top-level. Descendants should override to provide * implementations based on a class of top-level. */ protected void requestXFocus(long time, boolean timeProvided) { // Since in XAWT focus is synthetic and all basic Windows are // override_redirect all we can do is check whether our parent // is active. If it is - we can freely synthesize focus transfer. // Luckily, this logic is already implemented in requestWindowFocus. if (focusLog.isLoggable(PlatformLogger.Level.FINE)) { focusLog.fine("Requesting window focus"); } requestWindowFocus(time, timeProvided); } public final boolean focusAllowedFor() { if (isNativelyNonFocusableWindow()) { return false; } /* Window target = (Window)this.target; if (!target.isVisible() || !target.isEnabled() || !target.isFocusable()) { return false; } */ if (isModalBlocked()) { return false; } return true; } public void handleFocusEvent(XEvent xev) { XFocusChangeEvent xfe = xev.get_xfocus(); FocusEvent fe; if (focusLog.isLoggable(PlatformLogger.Level.FINE)) { focusLog.fine("{0}", xfe); } if (isEventDisabled(xev)) { return; } if (xev.get_type() == XConstants.FocusIn) { // If this window is non-focusable don't post any java focus event if (focusAllowedFor()) { if (xfe.get_mode() == XConstants.NotifyNormal // Normal notify || xfe.get_mode() == XConstants.NotifyWhileGrabbed) // Alt-Tab notify { handleWindowFocusIn(xfe.get_serial()); } } } else { if (xfe.get_mode() == XConstants.NotifyNormal // Normal notify || xfe.get_mode() == XConstants.NotifyWhileGrabbed) // Alt-Tab notify { // If this window is non-focusable don't post any java focus event if (!isNativelyNonFocusableWindow()) { XWindowPeer oppositeXWindow = getNativeFocusedWindowPeer(); Object oppositeTarget = (oppositeXWindow!=null)? oppositeXWindow.getTarget() : null; Window oppositeWindow = null; if (oppositeTarget instanceof Window) { oppositeWindow = (Window) oppositeTarget; } // Check if opposite window is non-focusable. In that case we don't want to // post any event. if (oppositeXWindow != null && oppositeXWindow.isNativelyNonFocusableWindow()) { return; } if (this == oppositeXWindow) { oppositeWindow = null; } else if (oppositeXWindow instanceof XDecoratedPeer) { if (((XDecoratedPeer) oppositeXWindow).actualFocusedWindow != null) { oppositeXWindow = ((XDecoratedPeer) oppositeXWindow).actualFocusedWindow; oppositeTarget = oppositeXWindow.getTarget(); if (oppositeTarget instanceof Window && oppositeXWindow.isVisible() && oppositeXWindow.isNativelyNonFocusableWindow()) { oppositeWindow = ((Window) oppositeTarget); } } } handleWindowFocusOut(oppositeWindow, xfe.get_serial()); } } } } void setSaveUnder(boolean state) {} public void toFront() { if (isOverrideRedirect() && mustControlStackPosition) { mustControlStackPosition = false; removeRootPropertyEventDispatcher(); } if (isVisible()) { super.toFront(); if (isFocusableWindow() && isAutoRequestFocus() && !isModalBlocked() && !isWithdrawn()) { requestInitialFocus(); } } else { setVisible(true); } } public void toBack() { XToolkit.awtLock(); try { if(!isOverrideRedirect()) { XlibWrapper.XLowerWindow(XToolkit.getDisplay(), getWindow()); }else{ lowerOverrideRedirect(); } } finally { XToolkit.awtUnlock(); } } private void lowerOverrideRedirect() { // // make new hash of toplevels of all windows from 'windows' hash. // FIXME: do not call them "toplevel" as it is misleading. // HashSet toplevels = new HashSet<>(); long topl = 0, mytopl = 0; for (XWindowPeer xp : windows) { topl = getToplevelWindow( xp.getWindow() ); if( xp.equals( this ) ) { mytopl = topl; } if( topl > 0 ) toplevels.add( Long.valueOf( topl ) ); } // // find in the root's tree: // (1) my toplevel, (2) lowest java toplevel, (3) desktop // We must enforce (3), (1), (2) order, upward; // note that nautilus on the next restacking will do (1),(3),(2). // long laux, wDesktop = -1, wBottom = -1; int iMy = -1, iDesktop = -1, iBottom = -1; int i = 0; XQueryTree xqt = new XQueryTree(XToolkit.getDefaultRootWindow()); try { if( xqt.execute() > 0 ) { int nchildren = xqt.get_nchildren(); long children = xqt.get_children(); for(i = 0; i < nchildren; i++) { laux = Native.getWindow(children, i); if( laux == mytopl ) { iMy = i; }else if( isDesktopWindow( laux ) ) { // we need topmost desktop of them all. iDesktop = i; wDesktop = laux; }else if(iBottom < 0 && toplevels.contains( Long.valueOf(laux) ) && laux != mytopl) { iBottom = i; wBottom = laux; } } } if( (iMy < iBottom || iBottom < 0 )&& iDesktop < iMy) return; // no action necessary long to_restack = Native.allocateLongArray(2); Native.putLong(to_restack, 0, wBottom); Native.putLong(to_restack, 1, mytopl); XlibWrapper.XRestackWindows(XToolkit.getDisplay(), to_restack, 2); XlibWrapper.unsafe.freeMemory(to_restack); if( !mustControlStackPosition ) { mustControlStackPosition = true; // add root window property listener: // somebody (eg nautilus desktop) may obscure us addRootPropertyEventDispatcher(); } } finally { xqt.dispose(); } } /** Get XID of closest to root window in a given window hierarchy. FIXME: do not call it "toplevel" as it is misleading. On error return 0. */ private long getToplevelWindow( long w ) { long wi = w, ret, root; do { ret = wi; XQueryTree qt = new XQueryTree(wi); try { if (qt.execute() == 0) { return 0; } root = qt.get_root(); wi = qt.get_parent(); } finally { qt.dispose(); } } while (wi != root); return ret; } private static boolean isDesktopWindow( long wi ) { return XWM.getWM().isDesktopWindow( wi ); } private void updateAlwaysOnTop() { if (log.isLoggable(PlatformLogger.Level.FINE)) { log.fine("Promoting always-on-top state {0}", Boolean.valueOf(alwaysOnTop)); } XWM.getWM().setLayer(this, alwaysOnTop ? XLayerProtocol.LAYER_ALWAYS_ON_TOP : XLayerProtocol.LAYER_NORMAL); } public void updateAlwaysOnTopState() { this.alwaysOnTop = ((Window) this.target).isAlwaysOnTop(); if (ownerPeer != null) { XToolkit.awtLock(); try { restoreTransientFor(this); applyWindowType(); } finally { XToolkit.awtUnlock(); } } updateAlwaysOnTop(); } boolean isLocationByPlatform() { return locationByPlatform; } private void promoteDefaultPosition() { this.locationByPlatform = ((Window)target).isLocationByPlatform(); if (locationByPlatform) { XToolkit.awtLock(); try { Rectangle bounds = getBounds(); XSizeHints hints = getHints(); setSizeHints(hints.get_flags() & ~(XUtilConstants.USPosition | XUtilConstants.PPosition), bounds.x, bounds.y, bounds.width, bounds.height); } finally { XToolkit.awtUnlock(); } } } public void setVisible(boolean vis) { if (!isVisible() && vis) { isBeforeFirstMapNotify = true; winAttr.initialFocus = isAutoRequestFocus(); if (!winAttr.initialFocus) { /* * It's easier and safer to temporary suppress WM_TAKE_FOCUS * protocol itself than to ignore WM_TAKE_FOCUS client message. * Because we will have to make the difference between * the message come after showing and the message come after * activation. Also, on Metacity, for some reason, we have _two_ * WM_TAKE_FOCUS client messages when showing a frame/dialog. */ suppressWmTakeFocus(true); } } updateFocusability(); promoteDefaultPosition(); if (!vis && warningWindow != null) { warningWindow.setSecurityWarningVisible(false, false); } boolean refreshChildsTransientFor = isVisible() != vis; super.setVisible(vis); if (refreshChildsTransientFor) { for (Window child : ((Window) target).getOwnedWindows()) { XToolkit.awtLock(); try { if(!child.isLightweight() && child.isVisible()) { ComponentPeer childPeer = AWTAccessor. getComponentAccessor().getPeer(child); if(childPeer instanceof XWindowPeer) { XWindowPeer windowPeer = (XWindowPeer) childPeer; restoreTransientFor(windowPeer); windowPeer.applyWindowType(); } } } finally { XToolkit.awtUnlock(); } } } if (!vis && !isWithdrawn()) { // ICCCM, 4.1.4. Changing Window State: // "Iconic -> Withdrawn - The client should unmap the window and follow it // with a synthetic UnmapNotify event as described later in this section." // The same is true for Normal -> Withdrawn XToolkit.awtLock(); try { XUnmapEvent unmap = new XUnmapEvent(); unmap.set_window(window); unmap.set_event(XToolkit.getDefaultRootWindow()); unmap.set_type(XConstants.UnmapNotify); unmap.set_from_configure(false); XlibWrapper.XSendEvent(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(), false, XConstants.SubstructureNotifyMask | XConstants.SubstructureRedirectMask, unmap.pData); unmap.dispose(); } finally { XToolkit.awtUnlock(); } } // method called somewhere in parent does not generate configure-notify // event for override-redirect. // Ergo, no reshape and bugs like 5085647 in case setBounds was // called before setVisible. if (isOverrideRedirect() && vis) { updateChildrenSizes(); } repositionSecurityWarning(); } protected void suppressWmTakeFocus(boolean doSuppress) { } final boolean isSimpleWindow() { return !(target instanceof Frame || target instanceof Dialog); } boolean hasWarningWindow() { return ((Window)target).getWarningString() != null; } // The height of menu bar window int getMenuBarHeight() { return 0; } // Called when shell changes its size and requires children windows // to update their sizes appropriately void updateChildrenSizes() { } public void repositionSecurityWarning() { // NOTE: On KWin if the window/border snapping option is enabled, // the Java window may be swinging while it's being moved. // This doesn't make the application unusable though looks quite ugly. // Probobly we need to find some hint to assign to our Security // Warning window in order to exclude it from the snapping option. // We are not currently aware of existance of such a property. if (warningWindow != null) { // We can't use the coordinates stored in the XBaseWindow since // they are zeros for decorated frames. ComponentAccessor compAccessor = AWTAccessor.getComponentAccessor(); int x = compAccessor.getX(target); int y = compAccessor.getY(target); int width = compAccessor.getWidth(target); int height = compAccessor.getHeight(target); warningWindow.reposition(x, y, width, height); } } @Override protected void setMouseAbove(boolean above) { super.setMouseAbove(above); updateSecurityWarningVisibility(); } @Override public void setFullScreenExclusiveModeState(boolean state) { super.setFullScreenExclusiveModeState(state); updateSecurityWarningVisibility(); } public void updateSecurityWarningVisibility() { if (warningWindow == null) { return; } if (!isVisible()) { return; // The warning window should already be hidden. } boolean show = false; if (!isFullScreenExclusiveMode()) { int state = getWMState(); // getWMState() always returns 0 (Withdrawn) for simple windows. Hence // we ignore the state for such windows. if (isVisible() && (state == XUtilConstants.NormalState || isSimpleWindow())) { if (XKeyboardFocusManagerPeer.getInstance().getCurrentFocusedWindow() == getTarget()) { show = true; } if (isMouseAbove() || warningWindow.isMouseAbove()) { show = true; } } } warningWindow.setSecurityWarningVisible(show, true); } boolean isOverrideRedirect() { return XWM.getWMID() == XWM.OPENLOOK_WM || Window.Type.POPUP.equals(getWindowType()); } final boolean isOLWMDecorBug() { return XWM.getWMID() == XWM.OPENLOOK_WM && winAttr.nativeDecor == false; } public void dispose() { if (isGrabbed()) { if (grabLog.isLoggable(PlatformLogger.Level.FINE)) { grabLog.fine("Generating UngrabEvent on {0} because of the window disposal", this); } postEventToEventQueue(new sun.awt.UngrabEvent(getEventSource())); } SunToolkit.awtLock(); try { windows.remove(this); } finally { SunToolkit.awtUnlock(); } if (warningWindow != null) { warningWindow.destroy(); } removeRootPropertyEventDispatcher(); mustControlStackPosition = false; super.dispose(); /* * Fix for 6457980. * When disposing an owned Window we should implicitly * return focus to its decorated owner because it won't * receive WM_TAKE_FOCUS. */ if (isSimpleWindow()) { if (target == XKeyboardFocusManagerPeer.getInstance().getCurrentFocusedWindow()) { Window owner = getDecoratedOwner((Window)target); ((XWindowPeer)AWTAccessor.getComponentAccessor().getPeer(owner)).requestWindowFocus(); } } } boolean isResizable() { return winAttr.isResizable; } public void handleVisibilityEvent(XEvent xev) { super.handleVisibilityEvent(xev); XVisibilityEvent ve = xev.get_xvisibility(); winAttr.visibilityState = ve.get_state(); // if (ve.get_state() == XlibWrapper.VisibilityUnobscured) { // // raiseInputMethodWindow // } repositionSecurityWarning(); } void handleRootPropertyNotify(XEvent xev) { XPropertyEvent ev = xev.get_xproperty(); if( mustControlStackPosition && ev.get_atom() == XAtom.get("_NET_CLIENT_LIST_STACKING").getAtom()){ // Restore stack order unhadled/spoiled by WM or some app (nautilus). // As of now, don't use any generic machinery: just // do toBack() again. if(isOverrideRedirect()) { toBack(); } } } private void removeStartupNotification() { if (isStartupNotificationRemoved.getAndSet(true)) { return; } final String desktopStartupId = AccessController.doPrivileged(new PrivilegedAction() { public String run() { return XToolkit.getEnv("DESKTOP_STARTUP_ID"); } }); if (desktopStartupId == null) { return; } final StringBuilder messageBuilder = new StringBuilder("remove: ID="); messageBuilder.append('"'); for (int i = 0; i < desktopStartupId.length(); i++) { if (desktopStartupId.charAt(i) == '"' || desktopStartupId.charAt(i) == '\\') { messageBuilder.append('\\'); } messageBuilder.append(desktopStartupId.charAt(i)); } messageBuilder.append('"'); messageBuilder.append('\0'); final byte[] message; try { message = messageBuilder.toString().getBytes("UTF-8"); } catch (UnsupportedEncodingException cannotHappen) { return; } XClientMessageEvent req = null; XToolkit.awtLock(); try { final XAtom netStartupInfoBeginAtom = XAtom.get("_NET_STARTUP_INFO_BEGIN"); final XAtom netStartupInfoAtom = XAtom.get("_NET_STARTUP_INFO"); req = new XClientMessageEvent(); req.set_type(XConstants.ClientMessage); req.set_window(getWindow()); req.set_message_type(netStartupInfoBeginAtom.getAtom()); req.set_format(8); for (int pos = 0; pos < message.length; pos += 20) { final int msglen = Math.min(message.length - pos, 20); int i = 0; for (; i < msglen; i++) { XlibWrapper.unsafe.putByte(req.get_data() + i, message[pos + i]); } for (; i < 20; i++) { XlibWrapper.unsafe.putByte(req.get_data() + i, (byte)0); } XlibWrapper.XSendEvent(XToolkit.getDisplay(), XlibWrapper.RootWindow(XToolkit.getDisplay(), getScreenNumber()), false, XConstants.PropertyChangeMask, req.pData); req.set_message_type(netStartupInfoAtom.getAtom()); } } finally { XToolkit.awtUnlock(); if (req != null) { req.dispose(); } } } public void handleMapNotifyEvent(XEvent xev) { removeStartupNotification(); // See 6480534. isUnhiding |= isWMStateNetHidden(); super.handleMapNotifyEvent(xev); if (!winAttr.initialFocus) { suppressWmTakeFocus(false); // restore the protocol. /* * For some reason, on Metacity, a frame/dialog being shown * without WM_TAKE_FOCUS protocol doesn't get moved to the front. * So, we do it evidently. */ XToolkit.awtLock(); try { XlibWrapper.XRaiseWindow(XToolkit.getDisplay(), getWindow()); } finally { XToolkit.awtUnlock(); } } if (shouldFocusOnMapNotify()) { focusLog.fine("Automatically request focus on window"); requestInitialFocus(); } isUnhiding = false; isBeforeFirstMapNotify = false; updateAlwaysOnTop(); synchronized (getStateLock()) { if (!isMapped) { isMapped = true; } } } public void handleUnmapNotifyEvent(XEvent xev) { super.handleUnmapNotifyEvent(xev); // On Metacity UnmapNotify comes before PropertyNotify (for _NET_WM_STATE_HIDDEN). // So we also check for the property later in MapNotify. See 6480534. isUnhiding |= isWMStateNetHidden(); synchronized (getStateLock()) { if (isMapped) { isMapped = false; } } } private boolean shouldFocusOnMapNotify() { boolean res = false; if (isBeforeFirstMapNotify) { res = (winAttr.initialFocus || // Window.autoRequestFocus isFocusedWindowModalBlocker()); } else { res = isUnhiding; // Unhiding } res = res && isFocusableWindow() && // General focusability !isModalBlocked(); // Modality return res; } protected boolean isWMStateNetHidden() { XNETProtocol protocol = XWM.getWM().getNETProtocol(); return (protocol != null && protocol.isWMStateNetHidden(this)); } protected void requestInitialFocus() { requestXFocus(); } public void addToplevelStateListener(ToplevelStateListener l){ toplevelStateListeners.add(l); } public void removeToplevelStateListener(ToplevelStateListener l){ toplevelStateListeners.remove(l); } /** * Override this methods to get notifications when top-level window state changes. The state is * meant in terms of ICCCM: WithdrawnState, IconicState, NormalState */ @Override protected void stateChanged(long time, int oldState, int newState) { // Fix for 6401700, 6412803 // If this window is modal blocked, it is put into the transient_for // chain using prevTransientFor and nextTransientFor hints. However, // the real WM_TRANSIENT_FOR hint shouldn't be set for windows in // different WM states (except for owner-window relationship), so // if the window changes its state, its real WM_TRANSIENT_FOR hint // should be updated accordingly. updateTransientFor(); for (ToplevelStateListener topLevelListenerTmp : toplevelStateListeners) { topLevelListenerTmp.stateChangedICCCM(oldState, newState); } updateSecurityWarningVisibility(); } boolean isWithdrawn() { return getWMState() == XUtilConstants.WithdrawnState; } boolean hasDecorations(int decor) { if (!winAttr.nativeDecor) { return false; } else { int myDecor = winAttr.decorations; boolean hasBits = ((myDecor & decor) == decor); if ((myDecor & XWindowAttributesData.AWT_DECOR_ALL) != 0) return !hasBits; else return hasBits; } } void setReparented(boolean newValue) { super.setReparented(newValue); XToolkit.awtLock(); try { if (isReparented() && delayedModalBlocking) { addToTransientFors(AWTAccessor.getComponentAccessor().getPeer(modalBlocker)); delayedModalBlocking = false; } } finally { XToolkit.awtUnlock(); } } /* * Returns a Vector of all Java top-level windows, * sorted by their current Z-order */ static Vector collectJavaToplevels() { Vector javaToplevels = new Vector(); Vector v = new Vector(); X11GraphicsEnvironment ge = (X11GraphicsEnvironment)GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice[] gds = ge.getScreenDevices(); if (!ge.runningXinerama() && (gds.length > 1)) { for (GraphicsDevice gd : gds) { int screen = ((X11GraphicsDevice)gd).getScreen(); long rootWindow = XlibWrapper.RootWindow(XToolkit.getDisplay(), screen); v.add(rootWindow); } } else { v.add(XToolkit.getDefaultRootWindow()); } final int windowsCount = windows.size(); while ((v.size() > 0) && (javaToplevels.size() < windowsCount)) { long win = v.remove(0); XQueryTree qt = new XQueryTree(win); try { if (qt.execute() != 0) { int nchildren = qt.get_nchildren(); long children = qt.get_children(); // XQueryTree returns window children ordered by z-order for (int i = 0; i < nchildren; i++) { long child = Native.getWindow(children, i); XBaseWindow childWindow = XToolkit.windowToXWindow(child); // filter out Java non-toplevels if ((childWindow != null) && !(childWindow instanceof XWindowPeer)) { continue; } else { v.add(child); } if (childWindow instanceof XWindowPeer) { XWindowPeer np = (XWindowPeer)childWindow; javaToplevels.add(np); // XQueryTree returns windows sorted by their z-order. However, // if WM has not handled transient for hint for a child window, // it may appear in javaToplevels before its owner. Move such // children after their owners. int k = 0; XWindowPeer toCheck = javaToplevels.get(k); while (toCheck != np) { XWindowPeer toCheckOwnerPeer = toCheck.getOwnerPeer(); if (toCheckOwnerPeer == np) { javaToplevels.remove(k); javaToplevels.add(toCheck); } else { k++; } toCheck = javaToplevels.get(k); } } } } } finally { qt.dispose(); } } return javaToplevels; } public void setModalBlocked(Dialog d, boolean blocked) { setModalBlocked(d, blocked, null); } public void setModalBlocked(Dialog d, boolean blocked, Vector javaToplevels) { XToolkit.awtLock(); try { // State lock should always be after awtLock synchronized(getStateLock()) { XDialogPeer blockerPeer = AWTAccessor.getComponentAccessor().getPeer(d); if (blocked) { if (log.isLoggable(PlatformLogger.Level.FINE)) { log.fine("{0} is blocked by {1}", this, blockerPeer); } modalBlocker = d; if (isReparented() || XWM.isNonReparentingWM()) { addToTransientFors(blockerPeer, javaToplevels); } else { delayedModalBlocking = true; } } else { if (d != modalBlocker) { throw new IllegalStateException("Trying to unblock window blocked by another dialog"); } modalBlocker = null; if (isReparented() || XWM.isNonReparentingWM()) { removeFromTransientFors(); } else { delayedModalBlocking = false; } } updateTransientFor(); } } finally { XToolkit.awtUnlock(); } } /* * Sets the TRANSIENT_FOR hint to the given top-level window. This * method is used when a window is modal blocked/unblocked or * changed its state from/to NormalState to/from other states. * If window or transientForWindow are embedded frames, the containing * top-level windows are used. * * @param window specifies the top-level window that the hint * is to be set to * @param transientForWindow the top-level window * @param updateChain specifies if next/prevTransientFor fields are * to be updated * @param allStates if set to {@code true} then TRANSIENT_FOR hint * is set regardless of the state of window and transientForWindow, * otherwise it is set only if both are in the same state */ static void setToplevelTransientFor(XWindowPeer window, XWindowPeer transientForWindow, boolean updateChain, boolean allStates) { if ((window == null) || (transientForWindow == null)) { return; } if (updateChain) { window.prevTransientFor = transientForWindow; transientForWindow.nextTransientFor = window; } if (!allStates && (window.getWMState() != transientForWindow.getWMState())) { return; } if (window.getScreenNumber() != transientForWindow.getScreenNumber()) { return; } long bpw = window.getWindow(); while (!XlibUtil.isToplevelWindow(bpw) && !XlibUtil.isXAWTToplevelWindow(bpw)) { bpw = XlibUtil.getParentWindow(bpw); } long tpw = transientForWindow.getWindow(); XBaseWindow parent = transientForWindow; while (tpw != 0 && ((!XlibUtil.isToplevelWindow(tpw) && !XlibUtil.isXAWTToplevelWindow(tpw)) || !parent.isVisible())) { tpw = XlibUtil.getParentWindow(tpw); parent = XToolkit.windowToXWindow(tpw); } XlibWrapper.XSetTransientFor(XToolkit.getDisplay(), bpw, tpw); window.curRealTransientFor = parent; } /* * This method does nothing if this window is not blocked by any modal dialog. * For modal blocked windows this method looks up for the nearest * prevTransiendFor window that is in the same state (Normal/Iconified/Withdrawn) * as this one and makes this window transient for it. The same operation is * performed for nextTransientFor window. * Values of prevTransientFor and nextTransientFor fields are not changed. */ void updateTransientFor() { int state = getWMState(); XWindowPeer p = prevTransientFor; while ((p != null) && ((p.getWMState() != state) || (p.getScreenNumber() != getScreenNumber()))) { p = p.prevTransientFor; } if (p != null) { setToplevelTransientFor(this, p, false, false); } else { restoreTransientFor(this); } XWindowPeer n = nextTransientFor; while ((n != null) && ((n.getWMState() != state) || (n.getScreenNumber() != getScreenNumber()))) { n = n.nextTransientFor; } if (n != null) { setToplevelTransientFor(n, this, false, false); } } /* * Removes the TRANSIENT_FOR hint from the given top-level window. * If window or transientForWindow are embedded frames, the containing * top-level windows are used. * * @param window specifies the top-level window that the hint * is to be removed from */ private static void removeTransientForHint(XWindowPeer window) { XAtom XA_WM_TRANSIENT_FOR = XAtom.get(XAtom.XA_WM_TRANSIENT_FOR); long bpw = window.getWindow(); while (!XlibUtil.isToplevelWindow(bpw) && !XlibUtil.isXAWTToplevelWindow(bpw)) { bpw = XlibUtil.getParentWindow(bpw); } XlibWrapper.XDeleteProperty(XToolkit.getDisplay(), bpw, XA_WM_TRANSIENT_FOR.getAtom()); window.curRealTransientFor = null; } /* * When a modal dialog is shown, all its blocked windows are lined up into * a chain in such a way that each window is a transient_for window for * the next one. That allows us to keep the modal dialog above all its * blocked windows (even if there are some another modal dialogs between * them). * This method adds this top-level window to the chain of the given modal * dialog. To keep the current relative z-order, we should use the * XQueryTree to find the place to insert this window to. As each window * can be blocked by only one modal dialog (such checks are performed in * shared code), both this and blockerPeer are on the top of their chains * (chains may be empty). * If this window is a modal dialog and has its own chain, these chains are * merged according to the current z-order (XQueryTree is used again). * Below are some simple examples (z-order is from left to right, -- is * modal blocking). * * Example 0: * T (current chain of this, no windows are blocked by this) * W1---B (current chain of blockerPeer, W2 is blocked by blockerPeer) * Result is: * W1-T-B (merged chain, all the windows are blocked by blockerPeer) * * Example 1: * W1-T (current chain of this, W1 is blocked by this) * W2-B (current chain of blockerPeer, W2 is blocked by blockerPeer) * Result is: * W1-T-W2-B (merged chain, all the windows are blocked by blockerPeer) * * Example 2: * W1----T (current chain of this, W1 is blocked by this) * W2---B (current chain of blockerPeer, W2 is blocked by blockerPeer) * Result is: * W1-W2-T-B (merged chain, all the windows are blocked by blockerPeer) * * This method should be called under the AWT lock. * * @see #removeFromTransientFors * @see #setModalBlocked */ private void addToTransientFors(XDialogPeer blockerPeer) { addToTransientFors(blockerPeer, null); } private void addToTransientFors(XDialogPeer blockerPeer, Vector javaToplevels) { // blockerPeer chain iterator XWindowPeer blockerChain = blockerPeer; while (blockerChain.prevTransientFor != null) { blockerChain = blockerChain.prevTransientFor; } // this window chain iterator // each window can be blocked no more than once, so this window // is on top of its chain XWindowPeer thisChain = this; while (thisChain.prevTransientFor != null) { thisChain = thisChain.prevTransientFor; } // if there are no windows blocked by modalBlocker, simply add this window // and its chain to blocker's chain if (blockerChain == blockerPeer) { setToplevelTransientFor(blockerPeer, this, true, false); } else { // Collect all the Java top-levels, if required if (javaToplevels == null) { javaToplevels = collectJavaToplevels(); } // merged chain tail XWindowPeer mergedChain = null; for (XWindowPeer w : javaToplevels) { XWindowPeer prevMergedChain = mergedChain; if (w == thisChain) { if (thisChain == this) { if (prevMergedChain != null) { setToplevelTransientFor(this, prevMergedChain, true, false); } setToplevelTransientFor(blockerChain, this, true, false); break; } else { mergedChain = thisChain; thisChain = thisChain.nextTransientFor; } } else if (w == blockerChain) { mergedChain = blockerChain; blockerChain = blockerChain.nextTransientFor; } else { continue; } if (prevMergedChain == null) { mergedChain.prevTransientFor = null; } else { setToplevelTransientFor(mergedChain, prevMergedChain, true, false); mergedChain.updateTransientFor(); } if (blockerChain == blockerPeer) { setToplevelTransientFor(thisChain, mergedChain, true, false); setToplevelTransientFor(blockerChain, this, true, false); break; } } } XToolkit.XSync(); } static void restoreTransientFor(XWindowPeer window) { XWindowPeer ownerPeer = window.getOwnerPeer(); if (ownerPeer != null) { setToplevelTransientFor(window, ownerPeer, false, true); } else { removeTransientForHint(window); } } /* * When a window is modally unblocked, it should be removed from its blocker * chain, see {@link #addToTransientFor addToTransientFors} method for the * chain definition. * The problem is that we cannot simply restore window's original * TRANSIENT_FOR hint (if any) and link prevTransientFor and * nextTransientFor together as the whole chain could be created as a merge * of two other chains in addToTransientFors. In that case, if this window is * a modal dialog, it would lost all its own chain, if we simply exclude it * from the chain. * The correct behaviour of this method should be to split the chain, this * window is currently in, into two chains. First chain is this window own * chain (i. e. all the windows blocked by this one, directly or indirectly), * if any, and the rest windows from the current chain. * * Example: * Original state: * W1-B1 (window W1 is blocked by B1) * W2-B2 (window W2 is blocked by B2) * B3 is shown and blocks B1 and B2: * W1-W2-B1-B2-B3 (a single chain after B1.addToTransientFors() and B2.addToTransientFors()) * If we then unblock B1, the state should be: * W1-B1 (window W1 is blocked by B1) * W2-B2-B3 (window W2 is blocked by B2 and B2 is blocked by B3) * * This method should be called under the AWT lock. * * @see #addToTransientFors * @see #setModalBlocked */ private void removeFromTransientFors() { // the head of the chain of this window XWindowPeer thisChain = this; // the head of the current chain // nextTransientFor is always not null as this window is in the chain XWindowPeer otherChain = nextTransientFor; // the set of blockers in this chain: if this dialog blocks some other // modal dialogs, their blocked windows should stay in this dialog's chain Set thisChainBlockers = new HashSet(); thisChainBlockers.add(this); // current chain iterator in the order from next to prev XWindowPeer chainToSplit = prevTransientFor; while (chainToSplit != null) { XWindowPeer blocker = AWTAccessor.getComponentAccessor().getPeer(chainToSplit.modalBlocker); if (thisChainBlockers.contains(blocker)) { // add to this dialog's chain setToplevelTransientFor(thisChain, chainToSplit, true, false); thisChain = chainToSplit; thisChainBlockers.add(chainToSplit); } else { // leave in the current chain setToplevelTransientFor(otherChain, chainToSplit, true, false); otherChain = chainToSplit; } chainToSplit = chainToSplit.prevTransientFor; } restoreTransientFor(thisChain); thisChain.prevTransientFor = null; restoreTransientFor(otherChain); otherChain.prevTransientFor = null; nextTransientFor = null; XToolkit.XSync(); } boolean isModalBlocked() { return modalBlocker != null; } static Window getDecoratedOwner(Window window) { while ((null != window) && !(window instanceof Frame || window instanceof Dialog)) { window = (Window) AWTAccessor.getComponentAccessor().getParent(window); } return window; } public boolean requestWindowFocus(XWindowPeer actualFocusedWindow) { setActualFocusedWindow(actualFocusedWindow); return requestWindowFocus(); } public boolean requestWindowFocus() { return requestWindowFocus(0, false); } public boolean requestWindowFocus(long time, boolean timeProvided) { focusLog.fine("Request for window focus"); // If this is Frame or Dialog we can't assure focus request success - but we still can try // If this is Window and its owner Frame is active we can be sure request succedded. Window ownerWindow = XWindowPeer.getDecoratedOwner((Window)target); Window focusedWindow = XKeyboardFocusManagerPeer.getInstance().getCurrentFocusedWindow(); Window activeWindow = XWindowPeer.getDecoratedOwner(focusedWindow); if (isWMStateNetHidden()) { focusLog.fine("The window is unmapped, so rejecting the request"); return false; } if (activeWindow == ownerWindow) { focusLog.fine("Parent window is active - generating focus for this window"); handleWindowFocusInSync(-1); return true; } focusLog.fine("Parent window is not active"); XDecoratedPeer wpeer = AWTAccessor.getComponentAccessor().getPeer(ownerWindow); if (wpeer != null && wpeer.requestWindowFocus(this, time, timeProvided)) { focusLog.fine("Parent window accepted focus request - generating focus for this window"); return true; } focusLog.fine("Denied - parent window is not active and didn't accept focus request"); return false; } // This method is to be overriden in XDecoratedPeer. void setActualFocusedWindow(XWindowPeer actualFocusedWindow) { } /** * Applies the current window type. */ private void applyWindowType() { XNETProtocol protocol = XWM.getWM().getNETProtocol(); if (protocol == null) { return; } XAtom typeAtom = null; switch (getWindowType()) { case NORMAL: typeAtom = curRealTransientFor == null ? protocol.XA_NET_WM_WINDOW_TYPE_NORMAL : protocol.XA_NET_WM_WINDOW_TYPE_DIALOG; break; case UTILITY: typeAtom = protocol.XA_NET_WM_WINDOW_TYPE_UTILITY; break; case POPUP: typeAtom = protocol.XA_NET_WM_WINDOW_TYPE_POPUP_MENU; break; } if (typeAtom != null) { XAtomList wtype = new XAtomList(); wtype.add(typeAtom); protocol.XA_NET_WM_WINDOW_TYPE. setAtomListProperty(getWindow(), wtype); } else { protocol.XA_NET_WM_WINDOW_TYPE. DeleteProperty(getWindow()); } } @Override public void xSetVisible(boolean visible) { if (log.isLoggable(PlatformLogger.Level.FINE)) { log.fine("Setting visible on " + this + " to " + visible); } XToolkit.awtLock(); try { this.visible = visible; if (visible) { applyWindowType(); XlibWrapper.XMapRaised(XToolkit.getDisplay(), getWindow()); } else { XlibWrapper.XUnmapWindow(XToolkit.getDisplay(), getWindow()); } XlibWrapper.XFlush(XToolkit.getDisplay()); } finally { XToolkit.awtUnlock(); } } // should be synchronized on awtLock private int dropTargetCount = 0; public void addDropTarget() { XToolkit.awtLock(); try { if (dropTargetCount == 0) { long window = getWindow(); if (window != 0) { XDropTargetRegistry.getRegistry().registerDropSite(window); } } dropTargetCount++; } finally { XToolkit.awtUnlock(); } } public void removeDropTarget() { XToolkit.awtLock(); try { dropTargetCount--; if (dropTargetCount == 0) { long window = getWindow(); if (window != 0) { XDropTargetRegistry.getRegistry().unregisterDropSite(window); } } } finally { XToolkit.awtUnlock(); } } void addRootPropertyEventDispatcher() { if( rootPropertyEventDispatcher == null ) { rootPropertyEventDispatcher = new XEventDispatcher() { public void dispatchEvent(XEvent ev) { if( ev.get_type() == XConstants.PropertyNotify ) { handleRootPropertyNotify( ev ); } } }; XlibWrapper.XSelectInput( XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(), XConstants.PropertyChangeMask); XToolkit.addEventDispatcher(XToolkit.getDefaultRootWindow(), rootPropertyEventDispatcher); } } void removeRootPropertyEventDispatcher() { if( rootPropertyEventDispatcher != null ) { XToolkit.removeEventDispatcher(XToolkit.getDefaultRootWindow(), rootPropertyEventDispatcher); rootPropertyEventDispatcher = null; } } public void updateFocusableWindowState() { cachedFocusableWindow = isFocusableWindow(); } XAtom XA_NET_WM_STATE; XAtomList net_wm_state; public XAtomList getNETWMState() { if (net_wm_state == null) { net_wm_state = XA_NET_WM_STATE.getAtomListPropertyList(this); } return net_wm_state; } public void setNETWMState(XAtomList state) { net_wm_state = state; if (state != null) { XA_NET_WM_STATE.setAtomListProperty(this, state); } } public PropMwmHints getMWMHints() { if (mwm_hints == null) { mwm_hints = new PropMwmHints(); if (!XWM.XA_MWM_HINTS.getAtomData(getWindow(), mwm_hints.pData, MWMConstants.PROP_MWM_HINTS_ELEMENTS)) { mwm_hints.zero(); } } return mwm_hints; } public void setMWMHints(PropMwmHints hints) { mwm_hints = hints; if (hints != null) { XWM.XA_MWM_HINTS.setAtomData(getWindow(), mwm_hints.pData, MWMConstants.PROP_MWM_HINTS_ELEMENTS); } } protected void updateDropTarget() { XToolkit.awtLock(); try { if (dropTargetCount > 0) { long window = getWindow(); if (window != 0) { XDropTargetRegistry.getRegistry().unregisterDropSite(window); XDropTargetRegistry.getRegistry().registerDropSite(window); } } } finally { XToolkit.awtUnlock(); } } public void setGrab(boolean grab) { this.grab = grab; if (grab) { pressTarget = this; grabInput(); } else { ungrabInput(); } } public boolean isGrabbed() { return grab && XAwtState.getGrabWindow() == this; } public void handleXCrossingEvent(XEvent xev) { XCrossingEvent xce = xev.get_xcrossing(); if (grabLog.isLoggable(PlatformLogger.Level.FINE)) { grabLog.fine("{0}, when grabbed {1}, contains {2}", xce, isGrabbed(), containsGlobal(scaleDown(xce.get_x_root()), scaleDown(xce.get_y_root()))); } if (isGrabbed()) { // When window is grabbed, all events are dispatched to // it. Retarget them to the corresponding windows (notice // that XBaseWindow.dispatchEvent does the opposite // translation) // Note that we need to retarget XCrossingEvents to content window // since it generates MOUSE_ENTERED/MOUSE_EXITED for frame and dialog. // (fix for 6390326) XBaseWindow target = XToolkit.windowToXWindow(xce.get_window()); if (grabLog.isLoggable(PlatformLogger.Level.FINER)) { grabLog.finer(" - Grab event target {0}", target); } if (target != null && target != this) { target.dispatchEvent(xev); return; } } super.handleXCrossingEvent(xev); } public void handleMotionNotify(XEvent xev) { XMotionEvent xme = xev.get_xmotion(); if (grabLog.isLoggable(PlatformLogger.Level.FINER)) { grabLog.finer("{0}, when grabbed {1}, contains {2}", xme, isGrabbed(), containsGlobal(scaleDown(xme.get_x_root()), scaleDown(xme.get_y_root()))); } if (isGrabbed()) { boolean dragging = false; final int buttonsNumber = XToolkit.getNumberOfButtonsForMask(); for (int i = 0; i < buttonsNumber; i++){ // here is the bug in WM: extra buttons doesn't have state!=0 as they should. if ((i != 4) && (i != 5)){ dragging = dragging || ((xme.get_state() & XlibUtil.getButtonMask(i + 1)) != 0); } } // When window is grabbed, all events are dispatched to // it. Retarget them to the corresponding windows (notice // that XBaseWindow.dispatchEvent does the opposite // translation) XBaseWindow target = XToolkit.windowToXWindow(xme.get_window()); if (dragging && pressTarget != target) { // for some reasons if we grab input MotionNotify for drag is reported with target // to underlying window, not to window on which we have initiated drag // so we need to retarget them. Here I use simplified logic which retarget all // such events to source of mouse press (or the grabber). It helps with fix for 6390326. // So, I do not want to implement complicated logic for better retargeting. target = pressTarget.isVisible() ? pressTarget : this; xme.set_window(target.getWindow()); Point localCoord = target.toLocal(scaleDown(xme.get_x_root()), scaleDown(xme.get_y_root())); xme.set_x(scaleUp(localCoord.x)); xme.set_y(scaleUp(localCoord.y)); } if (grabLog.isLoggable(PlatformLogger.Level.FINER)) { grabLog.finer(" - Grab event target {0}", target); } if (target != null) { if (target != getContentXWindow() && target != this) { target.dispatchEvent(xev); return; } } // note that we need to pass dragging events to the grabber (6390326) // see comment above for more inforamtion. if (!containsGlobal(scaleDown(xme.get_x_root()), scaleDown(xme.get_y_root())) && !dragging) { // Outside of Java return; } } super.handleMotionNotify(xev); } // we use it to retarget mouse drag and mouse release during grab. private XBaseWindow pressTarget = this; public void handleButtonPressRelease(XEvent xev) { XButtonEvent xbe = xev.get_xbutton(); /* * Ignore the buttons above 20 due to the bit limit for * InputEvent.BUTTON_DOWN_MASK. * One more bit is reserved for FIRST_HIGH_BIT. */ if (xbe.get_button() > SunToolkit.MAX_BUTTONS_SUPPORTED) { return; } if (grabLog.isLoggable(PlatformLogger.Level.FINE)) { grabLog.fine("{0}, when grabbed {1}, contains {2} ({3}, {4}, {5}x{6})", xbe, isGrabbed(), containsGlobal(scaleDown(xbe.get_x_root()), scaleDown(xbe.get_y_root())), getAbsoluteX(), getAbsoluteY(), getWidth(), getHeight()); } if (isGrabbed()) { // When window is grabbed, all events are dispatched to // it. Retarget them to the corresponding windows (notice // that XBaseWindow.dispatchEvent does the opposite // translation) XBaseWindow target = XToolkit.windowToXWindow(xbe.get_window()); try { if (grabLog.isLoggable(PlatformLogger.Level.FINER)) { grabLog.finer(" - Grab event target {0} (press target {1})", target, pressTarget); } if (xbe.get_type() == XConstants.ButtonPress && xbe.get_button() == XConstants.buttons[0]) { // need to keep it to retarget mouse release pressTarget = target; } else if (xbe.get_type() == XConstants.ButtonRelease && xbe.get_button() == XConstants.buttons[0] && pressTarget != target) { // during grab we do receive mouse release on different component (not on the source // of mouse press). So we need to retarget it. // see 6390326 for more information. target = pressTarget.isVisible() ? pressTarget : this; xbe.set_window(target.getWindow()); Point localCoord = target.toLocal(scaleDown(xbe.get_x_root()), scaleDown(xbe.get_y_root())); xbe.set_x(scaleUp(localCoord.x)); xbe.set_y(scaleUp(localCoord.y)); pressTarget = this; } if (target != null && target != getContentXWindow() && target != this) { target.dispatchEvent(xev); return; } } finally { if (target != null) { // Target is either us or our content window - // check that event is inside. 'Us' in case of // shell will mean that this will also filter out press on title if ((target == this || target == getContentXWindow()) && !containsGlobal(scaleDown(xbe.get_x_root()), scaleDown(xbe.get_y_root()))) { // Outside this toplevel hierarchy // According to the specification of UngrabEvent, post it // when press occurs outside of the window and not on its owned windows if (xbe.get_type() == XConstants.ButtonPress) { if (grabLog.isLoggable(PlatformLogger.Level.FINE)) { grabLog.fine("Generating UngrabEvent on {0} because not inside of shell", this); } postEventToEventQueue(new sun.awt.UngrabEvent(getEventSource())); return; } } // First, get the toplevel XWindowPeer toplevel = target.getToplevelXWindow(); if (toplevel != null) { Window w = (Window)toplevel.target; while (w != null && toplevel != this && !(toplevel instanceof XDialogPeer)) { w = (Window) AWTAccessor.getComponentAccessor().getParent(w); if (w != null) { toplevel = AWTAccessor.getComponentAccessor().getPeer(w); } } if (w == null || (w != this.target && w instanceof Dialog)) { // toplevel == null - outside of // hierarchy, toplevel is Dialog - should // send ungrab (but shouldn't for Window) if (grabLog.isLoggable(PlatformLogger.Level.FINE)) { grabLog.fine("Generating UngrabEvent on {0} because hierarchy ended", this); } postEventToEventQueue(new sun.awt.UngrabEvent(getEventSource())); } } else { // toplevel is null - outside of hierarchy if (grabLog.isLoggable(PlatformLogger.Level.FINE)) { grabLog.fine("Generating UngrabEvent on {0} because toplevel is null", this); } postEventToEventQueue(new sun.awt.UngrabEvent(getEventSource())); return; } } else { // target doesn't map to XAWT window - outside of hierarchy if (grabLog.isLoggable(PlatformLogger.Level.FINE)) { grabLog.fine("Generating UngrabEvent on because target is null {0}", this); } postEventToEventQueue(new sun.awt.UngrabEvent(getEventSource())); return; } } } super.handleButtonPressRelease(xev); } public void print(Graphics g) { // We assume we print the whole frame, // so we expect no clip was set previously Shape shape = AWTAccessor.getWindowAccessor().getShape((Window)target); if (shape != null) { g.setClip(shape); } super.print(g); } @Override public void setOpacity(float opacity) { final long maxOpacity = 0xffffffffl; long iOpacity = (long)(opacity * maxOpacity); if (iOpacity < 0) { iOpacity = 0; } if (iOpacity > maxOpacity) { iOpacity = maxOpacity; } XAtom netWmWindowOpacityAtom = XAtom.get("_NET_WM_WINDOW_OPACITY"); if (iOpacity == maxOpacity) { netWmWindowOpacityAtom.DeleteProperty(getWindow()); } else { netWmWindowOpacityAtom.setCard32Property(getWindow(), iOpacity); } } @Override public void setOpaque(boolean isOpaque) { // no-op } @Override public void updateWindow() { // no-op } }