diff options
author | erikj <none@none> | 2017-09-12 19:03:39 +0200 |
---|---|---|
committer | erikj <none@none> | 2017-09-12 19:03:39 +0200 |
commit | 5ed9fb4367908cf380f1191a55f479ae335d1c87 (patch) | |
tree | bae73db0cb8f29c3370d16b487a0b2fbe3c48ada /src/java.desktop/unix/classes/sun/awt/X11/XWindowPeer.java | |
parent | 70cebea6a7eb12b61ca3c162963541168e3339c9 (diff) | |
download | JetBrainsRuntime-5ed9fb4367908cf380f1191a55f479ae335d1c87.tar.gz |
8187443: Forest Consolidation: Move files to unified layout
Reviewed-by: darcy, ihse
Diffstat (limited to 'src/java.desktop/unix/classes/sun/awt/X11/XWindowPeer.java')
-rw-r--r-- | src/java.desktop/unix/classes/sun/awt/X11/XWindowPeer.java | 2414 |
1 files changed, 2414 insertions, 0 deletions
diff --git a/src/java.desktop/unix/classes/sun/awt/X11/XWindowPeer.java b/src/java.desktop/unix/classes/sun/awt/X11/XWindowPeer.java new file mode 100644 index 00000000000..05fe8163e93 --- /dev/null +++ b/src/java.desktop/unix/classes/sun/awt/X11/XWindowPeer.java @@ -0,0 +1,2414 @@ +/* + * Copyright (c) 2002, 2016, 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<XWindowPeer> windows = new HashSet<XWindowPeer>(); + + + 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 <ToplevelStateListener> toplevelStateListeners = new Vector<ToplevelStateListener>(); + 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.isFontSet()) { + target.setFont(XWindow.getDefaultFont()); + // we should not call setFont because it will call a repaint + // which the peer may not be ready to do yet. + } + if (!target.isBackgroundSet()) { + target.setBackground(SystemColor.window); + // we should not call setBackGround because it will call a repaint + // which the peer may not be ready to do yet. + + } + if (!target.isForegroundSet()) { + target.setForeground(SystemColor.windowText); + // we should not call setForeGround because it will call a repaint + // which the peer may not be ready to do yet. + } + + + 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<Image> iconImages = target.getIconImages(); + XWindowPeer ownerPeer = getOwnerPeer(); + winAttr.icons = new ArrayList<IconInfo>(); + if (iconImages.size() != 0) { + //read icon images from target + winAttr.iconsInherited = false; + for (Iterator<Image> 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<IconInfo> normalizeIconImages(java.util.List<IconInfo> icons) { + java.util.List<IconInfo> result = new ArrayList<IconInfo>(); + 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<IconInfo> icons) { + if (iconLog.isLoggable(PlatformLogger.Level.FINEST)) { + iconLog.finest(">>> Sizes of icon images:"); + for (Iterator<IconInfo> i = icons.iterator(); i.hasNext(); ) { + iconLog.finest(" {0}", i.next()); + } + } + } + + public void recursivelySetIcon(java.util.List<IconInfo> 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<IconInfo> getIconInfo() { + return winAttr.icons; + } + void setIconHints(java.util.List<IconInfo> icons) { + //This does nothing for XWindowPeer, + //It's overriden in XDecoratedPeer + } + + private static ArrayList<IconInfo> defaultIconInfo; + protected static synchronized java.util.List<IconInfo> getDefaultIconInfo() { + if (defaultIconInfo == null) { + defaultIconInfo = new ArrayList<IconInfo>(); + 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: + case XWM.UNITY_COMPIZ_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<Long> 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<String>() { + 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<XWindowPeer> collectJavaToplevels() { + Vector<XWindowPeer> javaToplevels = new Vector<XWindowPeer>(); + Vector<Long> v = new Vector<Long>(); + 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<XWindowPeer> 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<XWindowPeer> 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<XWindowPeer> thisChainBlockers = new HashSet<XWindowPeer>(); + 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); + } + // Do not post Ungrab Event for mouse scroll + if ((xbe.get_button() != XConstants.buttons[3]) + && (xbe.get_button() != XConstants.buttons[4])) { + 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); + } + // For mouse wheel event, do not send UngrabEvent + if (xbe.get_type() != XConstants.ButtonPress) { + postEventToEventQueue(new sun.awt.UngrabEvent(getEventSource())); + } else if ((xbe.get_button() != XConstants.buttons[3]) + && (xbe.get_button() != XConstants.buttons[4])) { + 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); + } + // For mouse wheel event, do not send UngrabEvent + if (xbe.get_type() != XConstants.ButtonPress) { + postEventToEventQueue(new sun.awt.UngrabEvent(getEventSource())); + } else if ((xbe.get_button() != XConstants.buttons[3]) + && (xbe.get_button() != XConstants.buttons[4])) { + 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); + } + // For mouse wheel event, do not send UngrabEvent + if (xbe.get_type() != XConstants.ButtonPress) { + postEventToEventQueue(new sun.awt.UngrabEvent(getEventSource())); + } else if ((xbe.get_button() != XConstants.buttons[3]) + && (xbe.get_button() != XConstants.buttons[4])) { + 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 + } +} |