private static void writeLogRecords(PrintStream logps) throws Exception { PrintStream err = System.err; try { System.setErr(logps); Object[] params = new Object[] {new Long(1), "string"}; PlatformLogger plog = PlatformLogger.getLogger("test.log.foo"); plog.severe("Log message {0} {1}", (Object[]) params); // create a java.util.logging.Logger // now java.util.logging.Logger should be created for each platform // logger Logger logger = Logger.getLogger("test.log.bar"); logger.log(Level.SEVERE, "Log message {0} {1}", params); plog.severe("Log message {0} {1}", (Object[]) params); } finally { logps.flush(); logps.close(); System.setErr(err); } }
public class CPlatformWindow extends CFRetainedResource implements PlatformWindow { private native long nativeCreateNSWindow( long nsViewPtr, long ownerPtr, long styleBits, double x, double y, double w, double h); private static native void nativeSetNSWindowStyleBits(long nsWindowPtr, int mask, int data); private static native void nativeSetNSWindowMenuBar(long nsWindowPtr, long menuBarPtr); private static native Insets nativeGetNSWindowInsets(long nsWindowPtr); private static native void nativeSetNSWindowBounds( long nsWindowPtr, double x, double y, double w, double h); private static native void nativeSetNSWindowLocationByPlatform(long nsWindowPtr); private static native void nativeSetNSWindowStandardFrame( long nsWindowPtr, double x, double y, double w, double h); private static native void nativeSetNSWindowMinMax( long nsWindowPtr, double minW, double minH, double maxW, double maxH); private static native void nativePushNSWindowToBack(long nsWindowPtr); private static native void nativePushNSWindowToFront(long nsWindowPtr); private static native void nativeSetNSWindowTitle(long nsWindowPtr, String title); private static native void nativeRevalidateNSWindowShadow(long nsWindowPtr); private static native void nativeSetNSWindowMinimizedIcon(long nsWindowPtr, long nsImage); private static native void nativeSetNSWindowRepresentedFilename( long nsWindowPtr, String representedFilename); private static native void nativeSetEnabled(long nsWindowPtr, boolean isEnabled); private static native void nativeSynthesizeMouseEnteredExitedEvents(); private static native void nativeSynthesizeMouseEnteredExitedEvents( long nsWindowPtr, int eventType); private static native void nativeDispose(long nsWindowPtr); private static native void nativeEnterFullScreenMode(long nsWindowPtr); private static native void nativeExitFullScreenMode(long nsWindowPtr); static native CPlatformWindow nativeGetTopmostPlatformWindowUnderMouse(); // Loger to report issues happened during execution but that do not affect functionality private static final PlatformLogger logger = PlatformLogger.getLogger("sun.lwawt.macosx.CPlatformWindow"); private static final PlatformLogger focusLogger = PlatformLogger.getLogger("sun.lwawt.macosx.focus.CPlatformWindow"); // for client properties public static final String WINDOW_BRUSH_METAL_LOOK = "apple.awt.brushMetalLook"; public static final String WINDOW_DRAGGABLE_BACKGROUND = "apple.awt.draggableWindowBackground"; public static final String WINDOW_ALPHA = "Window.alpha"; public static final String WINDOW_SHADOW = "Window.shadow"; public static final String WINDOW_STYLE = "Window.style"; public static final String WINDOW_SHADOW_REVALIDATE_NOW = "apple.awt.windowShadow.revalidateNow"; public static final String WINDOW_DOCUMENT_MODIFIED = "Window.documentModified"; public static final String WINDOW_DOCUMENT_FILE = "Window.documentFile"; public static final String WINDOW_CLOSEABLE = "Window.closeable"; public static final String WINDOW_MINIMIZABLE = "Window.minimizable"; public static final String WINDOW_ZOOMABLE = "Window.zoomable"; public static final String WINDOW_HIDES_ON_DEACTIVATE = "Window.hidesOnDeactivate"; public static final String WINDOW_DOC_MODAL_SHEET = "apple.awt.documentModalSheet"; public static final String WINDOW_FADE_DELEGATE = "apple.awt._windowFadeDelegate"; public static final String WINDOW_FADE_IN = "apple.awt._windowFadeIn"; public static final String WINDOW_FADE_OUT = "apple.awt._windowFadeOut"; public static final String WINDOW_FULLSCREENABLE = "apple.awt.fullscreenable"; // Yeah, I know. But it's easier to deal with ints from JNI static final int MODELESS = 0; static final int DOCUMENT_MODAL = 1; static final int APPLICATION_MODAL = 2; static final int TOOLKIT_MODAL = 3; // window style bits static final int _RESERVED_FOR_DATA = 1 << 0; // corresponds to native style mask bits static final int DECORATED = 1 << 1; static final int TEXTURED = 1 << 2; static final int UNIFIED = 1 << 3; static final int UTILITY = 1 << 4; static final int HUD = 1 << 5; static final int SHEET = 1 << 6; static final int CLOSEABLE = 1 << 7; static final int MINIMIZABLE = 1 << 8; static final int RESIZABLE = 1 << 9; // both a style bit and prop bit static final int NONACTIVATING = 1 << 24; static final int IS_DIALOG = 1 << 25; static final int IS_MODAL = 1 << 26; static final int IS_POPUP = 1 << 27; static final int _STYLE_PROP_BITMASK = DECORATED | TEXTURED | UNIFIED | UTILITY | HUD | SHEET | CLOSEABLE | MINIMIZABLE | RESIZABLE; // corresponds to method-based properties static final int HAS_SHADOW = 1 << 10; static final int ZOOMABLE = 1 << 11; static final int ALWAYS_ON_TOP = 1 << 15; static final int HIDES_ON_DEACTIVATE = 1 << 17; static final int DRAGGABLE_BACKGROUND = 1 << 19; static final int DOCUMENT_MODIFIED = 1 << 21; static final int FULLSCREENABLE = 1 << 23; static final int _METHOD_PROP_BITMASK = RESIZABLE | HAS_SHADOW | ZOOMABLE | ALWAYS_ON_TOP | HIDES_ON_DEACTIVATE | DRAGGABLE_BACKGROUND | DOCUMENT_MODIFIED | FULLSCREENABLE; // corresponds to callback-based properties static final int SHOULD_BECOME_KEY = 1 << 12; static final int SHOULD_BECOME_MAIN = 1 << 13; static final int MODAL_EXCLUDED = 1 << 16; static final int _CALLBACK_PROP_BITMASK = SHOULD_BECOME_KEY | SHOULD_BECOME_MAIN | MODAL_EXCLUDED; static int SET(final int bits, final int mask, final boolean value) { if (value) return (bits | mask); return bits & ~mask; } static boolean IS(final int bits, final int mask) { return (bits & mask) != 0; } @SuppressWarnings({"unchecked", "rawtypes"}) static ClientPropertyApplicator<JRootPane, CPlatformWindow> CLIENT_PROPERTY_APPLICATOR = new ClientPropertyApplicator<JRootPane, CPlatformWindow>( new Property[] { new Property<CPlatformWindow>(WINDOW_DOCUMENT_MODIFIED) { public void applyProperty(final CPlatformWindow c, final Object value) { c.setStyleBits( DOCUMENT_MODIFIED, value == null ? false : Boolean.parseBoolean(value.toString())); } }, new Property<CPlatformWindow>(WINDOW_BRUSH_METAL_LOOK) { public void applyProperty(final CPlatformWindow c, final Object value) { c.setStyleBits(TEXTURED, Boolean.parseBoolean(value.toString())); } }, new Property<CPlatformWindow>(WINDOW_ALPHA) { public void applyProperty(final CPlatformWindow c, final Object value) { AWTUtilities.setWindowOpacity( c.target, value == null ? 1.0f : Float.parseFloat(value.toString())); } }, new Property<CPlatformWindow>(WINDOW_SHADOW) { public void applyProperty(final CPlatformWindow c, final Object value) { c.setStyleBits( HAS_SHADOW, value == null ? true : Boolean.parseBoolean(value.toString())); } }, new Property<CPlatformWindow>(WINDOW_MINIMIZABLE) { public void applyProperty(final CPlatformWindow c, final Object value) { c.setStyleBits(MINIMIZABLE, Boolean.parseBoolean(value.toString())); } }, new Property<CPlatformWindow>(WINDOW_CLOSEABLE) { public void applyProperty(final CPlatformWindow c, final Object value) { c.setStyleBits(CLOSEABLE, Boolean.parseBoolean(value.toString())); } }, new Property<CPlatformWindow>(WINDOW_ZOOMABLE) { public void applyProperty(final CPlatformWindow c, final Object value) { boolean zoomable = Boolean.parseBoolean(value.toString()); if (c.target instanceof RootPaneContainer && c.getPeer().getPeerType() == PeerType.FRAME) { if (c.isInFullScreen && !zoomable) { c.toggleFullScreen(); } } c.setStyleBits(ZOOMABLE, zoomable); } }, new Property<CPlatformWindow>(WINDOW_FULLSCREENABLE) { public void applyProperty(final CPlatformWindow c, final Object value) { boolean fullscrenable = Boolean.parseBoolean(value.toString()); if (c.target instanceof RootPaneContainer && c.getPeer().getPeerType() == PeerType.FRAME) { if (c.isInFullScreen && !fullscrenable) { c.toggleFullScreen(); } } c.setStyleBits(FULLSCREENABLE, fullscrenable); } }, new Property<CPlatformWindow>(WINDOW_SHADOW_REVALIDATE_NOW) { public void applyProperty(final CPlatformWindow c, final Object value) { nativeRevalidateNSWindowShadow(c.getNSWindowPtr()); } }, new Property<CPlatformWindow>(WINDOW_DOCUMENT_FILE) { public void applyProperty(final CPlatformWindow c, final Object value) { if (value == null || !(value instanceof java.io.File)) { nativeSetNSWindowRepresentedFilename(c.getNSWindowPtr(), null); return; } final String filename = ((java.io.File) value).getAbsolutePath(); nativeSetNSWindowRepresentedFilename(c.getNSWindowPtr(), filename); } } }) { @SuppressWarnings("deprecation") public CPlatformWindow convertJComponentToTarget(final JRootPane p) { Component root = SwingUtilities.getRoot(p); final ComponentAccessor acc = AWTAccessor.getComponentAccessor(); if (root == null || acc.getPeer(root) == null) return null; return (CPlatformWindow) ((LWWindowPeer) acc.getPeer(root)).getPlatformWindow(); } }; // Bounds of the native widget but in the Java coordinate system. // In order to keep it up-to-date we will update them on // 1) setting native bounds via nativeSetBounds() call // 2) getting notification from the native level via deliverMoveResizeEvent() private Rectangle nativeBounds = new Rectangle(0, 0, 0, 0); private volatile boolean isFullScreenMode; private boolean isFullScreenAnimationOn; private volatile boolean isInFullScreen; private Window target; private LWWindowPeer peer; protected CPlatformView contentView; protected CPlatformWindow owner; protected boolean visible = false; // visibility status from native perspective private boolean undecorated; // initialized in getInitialStyleBits() private Rectangle normalBounds = null; // not-null only for undecorated maximized windows private CPlatformResponder responder; public CPlatformWindow() { super(0, true); } /* * Delegate initialization (create native window and all the * related resources). */ @Override // PlatformWindow public void initialize(Window _target, LWWindowPeer _peer, PlatformWindow _owner) { initializeBase(_target, _peer, _owner, new CPlatformView()); final int styleBits = getInitialStyleBits(); responder = createPlatformResponder(); contentView = createContentView(); contentView.initialize(peer, responder); final long ownerPtr = owner != null ? owner.getNSWindowPtr() : 0L; Rectangle bounds; if (!IS(DECORATED, styleBits)) { // For undecorated frames the move/resize event does not come if the frame is centered on the // screen // so we need to set a stub location to force an initial move/resize. Real bounds would be set // later. bounds = new Rectangle(0, 0, 1, 1); } else { bounds = _peer.constrainBounds(_target.getBounds()); } final long nativeWindowPtr = nativeCreateNSWindow( contentView.getAWTView(), ownerPtr, styleBits, bounds.x, bounds.y, bounds.width, bounds.height); setPtr(nativeWindowPtr); if (target instanceof javax.swing.RootPaneContainer) { final javax.swing.JRootPane rootpane = ((javax.swing.RootPaneContainer) target).getRootPane(); if (rootpane != null) rootpane.addPropertyChangeListener( "ancestor", new PropertyChangeListener() { public void propertyChange(final PropertyChangeEvent evt) { CLIENT_PROPERTY_APPLICATOR.attachAndApplyClientProperties(rootpane); rootpane.removePropertyChangeListener("ancestor", this); } }); } validateSurface(); } protected void initializeBase( Window target, LWWindowPeer peer, PlatformWindow owner, CPlatformView view) { this.peer = peer; this.target = target; if (owner instanceof CPlatformWindow) { this.owner = (CPlatformWindow) owner; } this.contentView = view; } protected CPlatformResponder createPlatformResponder() { return new CPlatformResponder(peer, false); } protected CPlatformView createContentView() { return new CPlatformView(); } protected int getInitialStyleBits() { // defaults style bits int styleBits = DECORATED | HAS_SHADOW | CLOSEABLE | MINIMIZABLE | ZOOMABLE | RESIZABLE; if (isNativelyFocusableWindow()) { styleBits = SET(styleBits, SHOULD_BECOME_KEY, true); styleBits = SET(styleBits, SHOULD_BECOME_MAIN, true); } final boolean isFrame = (target instanceof Frame); final boolean isDialog = (target instanceof Dialog); final boolean isPopup = (target.getType() == Window.Type.POPUP); if (isDialog) { styleBits = SET(styleBits, MINIMIZABLE, false); } // Either java.awt.Frame or java.awt.Dialog can be undecorated, however java.awt.Window always // is undecorated. { this.undecorated = isFrame ? ((Frame) target).isUndecorated() : (isDialog ? ((Dialog) target).isUndecorated() : true); if (this.undecorated) styleBits = SET(styleBits, DECORATED, false); } // Either java.awt.Frame or java.awt.Dialog can be resizable, however java.awt.Window is never // resizable { final boolean resizable = isFrame ? ((Frame) target).isResizable() : (isDialog ? ((Dialog) target).isResizable() : false); styleBits = SET(styleBits, RESIZABLE, resizable); if (!resizable) { styleBits = SET(styleBits, ZOOMABLE, false); } else { setCanFullscreen(true); } } if (target.isAlwaysOnTop()) { styleBits = SET(styleBits, ALWAYS_ON_TOP, true); } if (target.getModalExclusionType() == Dialog.ModalExclusionType.APPLICATION_EXCLUDE) { styleBits = SET(styleBits, MODAL_EXCLUDED, true); } // If the target is a dialog, popup or tooltip we want it to ignore the brushed metal look. if (isPopup) { styleBits = SET(styleBits, TEXTURED, false); // Popups in applets don't activate applet's process styleBits = SET(styleBits, NONACTIVATING, true); styleBits = SET(styleBits, IS_POPUP, true); } if (Window.Type.UTILITY.equals(target.getType())) { styleBits = SET(styleBits, UTILITY, true); } if (target instanceof javax.swing.RootPaneContainer) { javax.swing.JRootPane rootpane = ((javax.swing.RootPaneContainer) target).getRootPane(); Object prop = null; prop = rootpane.getClientProperty(WINDOW_BRUSH_METAL_LOOK); if (prop != null) { styleBits = SET(styleBits, TEXTURED, Boolean.parseBoolean(prop.toString())); } if (isDialog && ((Dialog) target).getModalityType() == ModalityType.DOCUMENT_MODAL) { prop = rootpane.getClientProperty(WINDOW_DOC_MODAL_SHEET); if (prop != null) { styleBits = SET(styleBits, SHEET, Boolean.parseBoolean(prop.toString())); } } prop = rootpane.getClientProperty(WINDOW_STYLE); if (prop != null) { if ("small".equals(prop)) { styleBits = SET(styleBits, UTILITY, true); if (target.isAlwaysOnTop() && rootpane.getClientProperty(WINDOW_HIDES_ON_DEACTIVATE) == null) { styleBits = SET(styleBits, HIDES_ON_DEACTIVATE, true); } } if ("textured".equals(prop)) styleBits = SET(styleBits, TEXTURED, true); if ("unified".equals(prop)) styleBits = SET(styleBits, UNIFIED, true); if ("hud".equals(prop)) styleBits = SET(styleBits, HUD, true); } prop = rootpane.getClientProperty(WINDOW_HIDES_ON_DEACTIVATE); if (prop != null) { styleBits = SET(styleBits, HIDES_ON_DEACTIVATE, Boolean.parseBoolean(prop.toString())); } prop = rootpane.getClientProperty(WINDOW_CLOSEABLE); if (prop != null) { styleBits = SET(styleBits, CLOSEABLE, Boolean.parseBoolean(prop.toString())); } prop = rootpane.getClientProperty(WINDOW_MINIMIZABLE); if (prop != null) { styleBits = SET(styleBits, MINIMIZABLE, Boolean.parseBoolean(prop.toString())); } prop = rootpane.getClientProperty(WINDOW_ZOOMABLE); if (prop != null) { styleBits = SET(styleBits, ZOOMABLE, Boolean.parseBoolean(prop.toString())); } prop = rootpane.getClientProperty(WINDOW_FULLSCREENABLE); if (prop != null) { styleBits = SET(styleBits, FULLSCREENABLE, Boolean.parseBoolean(prop.toString())); } prop = rootpane.getClientProperty(WINDOW_SHADOW); if (prop != null) { styleBits = SET(styleBits, HAS_SHADOW, Boolean.parseBoolean(prop.toString())); } prop = rootpane.getClientProperty(WINDOW_DRAGGABLE_BACKGROUND); if (prop != null) { styleBits = SET(styleBits, DRAGGABLE_BACKGROUND, Boolean.parseBoolean(prop.toString())); } } if (isDialog) { styleBits = SET(styleBits, IS_DIALOG, true); if (((Dialog) target).isModal()) { styleBits = SET(styleBits, IS_MODAL, true); } } peer.setTextured(IS(TEXTURED, styleBits)); return styleBits; } // this is the counter-point to -[CWindow _nativeSetStyleBit:] private void setStyleBits(final int mask, final boolean value) { nativeSetNSWindowStyleBits(getNSWindowPtr(), mask, value ? mask : 0); } private native void _toggleFullScreenMode(final long model); public void toggleFullScreen() { _toggleFullScreenMode(getNSWindowPtr()); } @Override // PlatformWindow public void setMenuBar(MenuBar mb) { final long nsWindowPtr = getNSWindowPtr(); CMenuBar mbPeer = (CMenuBar) LWToolkit.targetToPeer(mb); if (mbPeer != null) { nativeSetNSWindowMenuBar(nsWindowPtr, mbPeer.getModel()); } else { nativeSetNSWindowMenuBar(nsWindowPtr, 0); } } @Override // PlatformWindow public void dispose() { contentView.dispose(); nativeDispose(getNSWindowPtr()); CPlatformWindow.super.dispose(); } @Override // PlatformWindow public FontMetrics getFontMetrics(Font f) { // TODO: not implemented (new RuntimeException("unimplemented")).printStackTrace(); return null; } @Override // PlatformWindow public Insets getInsets() { return nativeGetNSWindowInsets(getNSWindowPtr()); } @Override // PlatformWindow public Point getLocationOnScreen() { return new Point(nativeBounds.x, nativeBounds.y); } @Override public GraphicsDevice getGraphicsDevice() { return contentView.getGraphicsDevice(); } @Override // PlatformWindow public SurfaceData getScreenSurface() { // TODO: not implemented return null; } @Override // PlatformWindow public SurfaceData replaceSurfaceData() { return contentView.replaceSurfaceData(); } @Override // PlatformWindow public void setBounds(int x, int y, int w, int h) { nativeSetNSWindowBounds(getNSWindowPtr(), x, y, w, h); } public void setMaximizedBounds(int x, int y, int w, int h) { nativeSetNSWindowStandardFrame(getNSWindowPtr(), x, y, w, h); } private boolean isMaximized() { return undecorated ? this.normalBounds != null : CWrapper.NSWindow.isZoomed(getNSWindowPtr()); } private void maximize() { if (peer == null || isMaximized()) { return; } if (!undecorated) { CWrapper.NSWindow.zoom(getNSWindowPtr()); } else { deliverZoom(true); // We need an up to date size of the peer, so we flush the native events // to be sure that there are no setBounds requests in the queue. LWCToolkit.flushNativeSelectors(); this.normalBounds = peer.getBounds(); Rectangle maximizedBounds = peer.getMaximizedBounds(); setBounds( maximizedBounds.x, maximizedBounds.y, maximizedBounds.width, maximizedBounds.height); } } private void unmaximize() { if (!isMaximized()) { return; } if (!undecorated) { CWrapper.NSWindow.zoom(getNSWindowPtr()); } else { deliverZoom(false); Rectangle toBounds = this.normalBounds; this.normalBounds = null; setBounds(toBounds.x, toBounds.y, toBounds.width, toBounds.height); } } public boolean isVisible() { return this.visible; } @Override // PlatformWindow public void setVisible(boolean visible) { final long nsWindowPtr = getNSWindowPtr(); // Configure stuff updateIconImages(); updateFocusabilityForAutoRequestFocus(false); boolean wasMaximized = isMaximized(); if (visible && target.isLocationByPlatform()) { nativeSetNSWindowLocationByPlatform(getNSWindowPtr()); } // Actually show or hide the window LWWindowPeer blocker = (peer == null) ? null : peer.getBlocker(); if (blocker == null || !visible) { // If it ain't blocked, or is being hidden, go regular way if (visible) { CWrapper.NSWindow.makeFirstResponder(nsWindowPtr, contentView.getAWTView()); boolean isPopup = (target.getType() == Window.Type.POPUP); if (isPopup) { // Popups in applets don't activate applet's process CWrapper.NSWindow.orderFrontRegardless(nsWindowPtr); } else { CWrapper.NSWindow.orderFront(nsWindowPtr); } boolean isKeyWindow = CWrapper.NSWindow.isKeyWindow(nsWindowPtr); if (!isKeyWindow) { CWrapper.NSWindow.makeKeyWindow(nsWindowPtr); } } else { // immediately hide the window CWrapper.NSWindow.orderOut(nsWindowPtr); // process the close CWrapper.NSWindow.close(nsWindowPtr); } } else { // otherwise, put it in a proper z-order CWrapper.NSWindow.orderWindow( nsWindowPtr, CWrapper.NSWindow.NSWindowBelow, ((CPlatformWindow) blocker.getPlatformWindow()).getNSWindowPtr()); } this.visible = visible; // Manage the extended state when showing if (visible) { // Apply the extended state as expected in shared code if (target instanceof Frame) { if (!wasMaximized && isMaximized()) { // setVisible could have changed the native maximized state deliverZoom(true); } else { int frameState = ((Frame) target).getExtendedState(); if ((frameState & Frame.ICONIFIED) != 0) { // Treat all state bit masks with ICONIFIED bit as ICONIFIED state. frameState = Frame.ICONIFIED; } switch (frameState) { case Frame.ICONIFIED: CWrapper.NSWindow.miniaturize(nsWindowPtr); break; case Frame.MAXIMIZED_BOTH: maximize(); break; default: // NORMAL unmaximize(); // in case it was maximized, otherwise this is a no-op break; } } } } nativeSynthesizeMouseEnteredExitedEvents(); // Configure stuff #2 updateFocusabilityForAutoRequestFocus(true); // Manage parent-child relationship when showing final ComponentAccessor acc = AWTAccessor.getComponentAccessor(); if (visible) { // Order myself above my parent if (owner != null && owner.isVisible()) { CWrapper.NSWindow.orderWindow( nsWindowPtr, CWrapper.NSWindow.NSWindowAbove, owner.getNSWindowPtr()); applyWindowLevel(target); } // Order my own children above myself for (Window w : target.getOwnedWindows()) { final Object p = acc.getPeer(w); if (p instanceof LWWindowPeer) { CPlatformWindow pw = (CPlatformWindow) ((LWWindowPeer) p).getPlatformWindow(); if (pw != null && pw.isVisible()) { CWrapper.NSWindow.orderWindow( pw.getNSWindowPtr(), CWrapper.NSWindow.NSWindowAbove, nsWindowPtr); pw.applyWindowLevel(w); } } } } // Deal with the blocker of the window being shown if (blocker != null && visible) { // Make sure the blocker is above its siblings ((CPlatformWindow) blocker.getPlatformWindow()).orderAboveSiblings(); } } @Override // PlatformWindow public void setTitle(String title) { nativeSetNSWindowTitle(getNSWindowPtr(), title); } // Should be called on every window key property change. @Override // PlatformWindow public void updateIconImages() { final long nsWindowPtr = getNSWindowPtr(); final CImage cImage = getImageForTarget(); nativeSetNSWindowMinimizedIcon(nsWindowPtr, cImage == null ? 0L : cImage.ptr); } public long getNSWindowPtr() { final long nsWindowPtr = ptr; if (nsWindowPtr == 0L) { if (logger.isLoggable(PlatformLogger.Level.FINE)) { logger.fine( "NSWindow already disposed?", new Exception("Pointer to native NSWindow is invalid.")); } } return nsWindowPtr; } public SurfaceData getSurfaceData() { return contentView.getSurfaceData(); } @Override // PlatformWindow public void toBack() { final long nsWindowPtr = getNSWindowPtr(); nativePushNSWindowToBack(nsWindowPtr); } @Override // PlatformWindow public void toFront() { final long nsWindowPtr = getNSWindowPtr(); LWCToolkit lwcToolkit = (LWCToolkit) Toolkit.getDefaultToolkit(); Window w = DefaultKeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow(); final ComponentAccessor acc = AWTAccessor.getComponentAccessor(); if (w != null && acc.getPeer(w) != null && ((LWWindowPeer) acc.getPeer(w)).getPeerType() == LWWindowPeer.PeerType.EMBEDDED_FRAME && !lwcToolkit.isApplicationActive()) { lwcToolkit.activateApplicationIgnoringOtherApps(); } updateFocusabilityForAutoRequestFocus(false); nativePushNSWindowToFront(nsWindowPtr); updateFocusabilityForAutoRequestFocus(true); } private void setCanFullscreen(final boolean canFullScreen) { if (target instanceof RootPaneContainer && getPeer().getPeerType() == PeerType.FRAME) { if (isInFullScreen && !canFullScreen) { toggleFullScreen(); } final RootPaneContainer rpc = (RootPaneContainer) target; rpc.getRootPane().putClientProperty(CPlatformWindow.WINDOW_FULLSCREENABLE, canFullScreen); } } @Override public void setResizable(final boolean resizable) { setCanFullscreen(resizable); setStyleBits(RESIZABLE, resizable); setStyleBits(ZOOMABLE, resizable); } @Override public void setSizeConstraints(int minW, int minH, int maxW, int maxH) { nativeSetNSWindowMinMax(getNSWindowPtr(), minW, minH, maxW, maxH); } @Override public boolean rejectFocusRequest(FocusEvent.Cause cause) { // Cross-app activation requests are not allowed. if (cause != FocusEvent.Cause.MOUSE_EVENT && !((LWCToolkit) Toolkit.getDefaultToolkit()).isApplicationActive()) { focusLogger.fine("the app is inactive, so the request is rejected"); return true; } return false; } @Override public boolean requestWindowFocus() { long ptr = getNSWindowPtr(); if (CWrapper.NSWindow.canBecomeMainWindow(ptr)) { CWrapper.NSWindow.makeMainWindow(ptr); } CWrapper.NSWindow.makeKeyAndOrderFront(ptr); return true; } @Override public boolean isActive() { long ptr = getNSWindowPtr(); return CWrapper.NSWindow.isKeyWindow(ptr); } @Override public void updateFocusableWindowState() { final boolean isFocusable = isNativelyFocusableWindow(); setStyleBits(SHOULD_BECOME_KEY | SHOULD_BECOME_MAIN, isFocusable); // set both bits at once } @Override public void setAlwaysOnTop(boolean isAlwaysOnTop) { setStyleBits(ALWAYS_ON_TOP, isAlwaysOnTop); } @Override public void setOpacity(float opacity) { CWrapper.NSWindow.setAlphaValue(getNSWindowPtr(), opacity); } @Override public void setOpaque(boolean isOpaque) { CWrapper.NSWindow.setOpaque(getNSWindowPtr(), isOpaque); boolean isTextured = (peer == null) ? false : peer.isTextured(); if (!isTextured) { if (!isOpaque) { CWrapper.NSWindow.setBackgroundColor(getNSWindowPtr(), 0); } else if (peer != null) { Color color = peer.getBackground(); if (color != null) { int rgb = color.getRGB(); CWrapper.NSWindow.setBackgroundColor(getNSWindowPtr(), rgb); } } } // This is a temporary workaround. Looks like after 7124236 will be fixed // the correct place for invalidateShadow() is CGLayer.drawInCGLContext. SwingUtilities.invokeLater(this::invalidateShadow); } @Override public void enterFullScreenMode() { isFullScreenMode = true; nativeEnterFullScreenMode(getNSWindowPtr()); } @Override public void exitFullScreenMode() { nativeExitFullScreenMode(getNSWindowPtr()); isFullScreenMode = false; } @Override public boolean isFullScreenMode() { return isFullScreenMode; } @Override public void setWindowState(int windowState) { if (peer == null || !peer.isVisible()) { // setVisible() applies the state return; } int prevWindowState = peer.getState(); if (prevWindowState == windowState) return; final long nsWindowPtr = getNSWindowPtr(); if ((windowState & Frame.ICONIFIED) != 0) { // Treat all state bit masks with ICONIFIED bit as ICONIFIED state. windowState = Frame.ICONIFIED; } switch (windowState) { case Frame.ICONIFIED: if (prevWindowState == Frame.MAXIMIZED_BOTH) { // let's return into the normal states first // the zoom call toggles between the normal and the max states unmaximize(); } CWrapper.NSWindow.miniaturize(nsWindowPtr); break; case Frame.MAXIMIZED_BOTH: if (prevWindowState == Frame.ICONIFIED) { // let's return into the normal states first CWrapper.NSWindow.deminiaturize(nsWindowPtr); } maximize(); break; case Frame.NORMAL: if (prevWindowState == Frame.ICONIFIED) { CWrapper.NSWindow.deminiaturize(nsWindowPtr); } else if (prevWindowState == Frame.MAXIMIZED_BOTH) { // the zoom call toggles between the normal and the max states unmaximize(); } break; default: throw new RuntimeException("Unknown window state: " + windowState); } // NOTE: the SWP.windowState field gets updated to the newWindowState // value when the native notification comes to us } @Override public void setModalBlocked(boolean blocked) { if (target.getModalExclusionType() == Dialog.ModalExclusionType.APPLICATION_EXCLUDE) { return; } if (blocked) { // We are going to show a modal window. Previously displayed window will be // blocked/disabled. So we have to send mouse exited event to it now, since // all mouse events are discarded for blocked/disabled windows. nativeSynthesizeMouseEnteredExitedEvents(getNSWindowPtr(), CocoaConstants.NSMouseExited); } nativeSetEnabled(getNSWindowPtr(), !blocked); checkBlockingAndOrder(); } public final void invalidateShadow() { nativeRevalidateNSWindowShadow(getNSWindowPtr()); } // ---------------------------------------------------------------------- // UTILITY METHODS // ---------------------------------------------------------------------- /** * Find image to install into Title or into Application icon. First try icons installed for * toplevel. Null is returned, if there is no icon and default Duke image should be used. */ private CImage getImageForTarget() { CImage icon = null; try { icon = CImage.getCreator().createFromImages(target.getIconImages()); } catch (Exception ignored) { // Perhaps the icon passed into Java is broken. Skipping this icon. } return icon; } /* * Returns LWWindowPeer associated with this delegate. */ @Override public LWWindowPeer getPeer() { return peer; } @Override public boolean isUnderMouse() { return contentView.isUnderMouse(); } public CPlatformView getContentView() { return contentView; } @Override public long getLayerPtr() { return contentView.getWindowLayerPtr(); } private void validateSurface() { SurfaceData surfaceData = getSurfaceData(); if (surfaceData instanceof CGLSurfaceData) { ((CGLSurfaceData) surfaceData).validate(); } } void flushBuffers() { if (isVisible() && !nativeBounds.isEmpty() && !isFullScreenMode) { try { LWCToolkit.invokeAndWait( new Runnable() { @Override public void run() { // Posting an empty to flush the EventQueue without blocking the main thread } }, target); } catch (InvocationTargetException e) { e.printStackTrace(); } } } /** Helper method to get a pointer to the native view from the PlatformWindow. */ static long getNativeViewPtr(PlatformWindow platformWindow) { long nativePeer = 0L; if (platformWindow instanceof CPlatformWindow) { nativePeer = ((CPlatformWindow) platformWindow).getContentView().getAWTView(); } else if (platformWindow instanceof CViewPlatformEmbeddedFrame) { nativePeer = ((CViewPlatformEmbeddedFrame) platformWindow).getNSViewPtr(); } return nativePeer; } /** * *********************************************************** Callbacks from the AWTWindow and * AWTView objc classes. *********************************************************** */ private void deliverWindowFocusEvent(boolean gained, CPlatformWindow opposite) { // Fix for 7150349: ingore "gained" notifications when the app is inactive. if (gained && !((LWCToolkit) Toolkit.getDefaultToolkit()).isApplicationActive()) { focusLogger.fine("the app is inactive, so the notification is ignored"); return; } LWWindowPeer oppositePeer = (opposite == null) ? null : opposite.getPeer(); responder.handleWindowFocusEvent(gained, oppositePeer); } protected void deliverMoveResizeEvent(int x, int y, int width, int height, boolean byUser) { checkZoom(); final Rectangle oldB = nativeBounds; nativeBounds = new Rectangle(x, y, width, height); if (peer != null) { peer.notifyReshape(x, y, width, height); // System-dependent appearance optimization. if ((byUser && !oldB.getSize().equals(nativeBounds.getSize())) || isFullScreenAnimationOn) { flushBuffers(); } } } private void deliverWindowClosingEvent() { if (peer != null && peer.getBlocker() == null) { peer.postEvent(new WindowEvent(target, WindowEvent.WINDOW_CLOSING)); } } private void deliverIconify(final boolean iconify) { if (peer != null) { peer.notifyIconify(iconify); } } private void deliverZoom(final boolean isZoomed) { if (peer != null) { peer.notifyZoom(isZoomed); } } private void checkZoom() { if (peer != null) { int state = peer.getState(); if (state != Frame.MAXIMIZED_BOTH && isMaximized()) { deliverZoom(true); } else if (state == Frame.MAXIMIZED_BOTH && !isMaximized()) { deliverZoom(false); } } } private void deliverNCMouseDown() { if (peer != null) { peer.notifyNCMouseDown(); } } /* * Our focus model is synthetic and only non-simple window * may become natively focusable window. */ private boolean isNativelyFocusableWindow() { if (peer == null) { return false; } return !peer.isSimpleWindow() && target.getFocusableWindowState(); } /* * An utility method for the support of the auto request focus. * Updates the focusable state of the window under certain * circumstances. */ private void updateFocusabilityForAutoRequestFocus(boolean isFocusable) { if (target.isAutoRequestFocus() || !isNativelyFocusableWindow()) return; setStyleBits(SHOULD_BECOME_KEY | SHOULD_BECOME_MAIN, isFocusable); // set both bits at once } private boolean checkBlockingAndOrder() { LWWindowPeer blocker = (peer == null) ? null : peer.getBlocker(); if (blocker == null) { return false; } if (blocker instanceof CPrinterDialogPeer) { return true; } CPlatformWindow pWindow = (CPlatformWindow) blocker.getPlatformWindow(); pWindow.orderAboveSiblings(); final long nsWindowPtr = pWindow.getNSWindowPtr(); CWrapper.NSWindow.orderFrontRegardless(nsWindowPtr); CWrapper.NSWindow.makeKeyAndOrderFront(nsWindowPtr); CWrapper.NSWindow.makeMainWindow(nsWindowPtr); return true; } private void orderAboveSiblings() { if (owner == null) { return; } // NOTE: the logic will fail if we have a hierarchy like: // visible root owner // invisible owner // visible dialog // However, this is an unlikely scenario for real life apps if (owner.isVisible()) { // Recursively pop up the windows from the very bottom so that only // the very top-most one becomes the main window owner.orderAboveSiblings(); // Order the window to front of the stack of child windows final long nsWindowSelfPtr = getNSWindowPtr(); final long nsWindowOwnerPtr = owner.getNSWindowPtr(); CWrapper.NSWindow.orderFront(nsWindowOwnerPtr); CWrapper.NSWindow.orderWindow( nsWindowSelfPtr, CWrapper.NSWindow.NSWindowAbove, nsWindowOwnerPtr); } applyWindowLevel(target); } protected void applyWindowLevel(Window target) { if (target.isAlwaysOnTop() && target.getType() != Window.Type.POPUP) { CWrapper.NSWindow.setLevel(getNSWindowPtr(), CWrapper.NSWindow.NSFloatingWindowLevel); } else if (target.getType() == Window.Type.POPUP) { CWrapper.NSWindow.setLevel(getNSWindowPtr(), CWrapper.NSWindow.NSPopUpMenuWindowLevel); } } // ---------------------------------------------------------------------- // NATIVE CALLBACKS // ---------------------------------------------------------------------- private void windowDidBecomeMain() { if (checkBlockingAndOrder()) return; // If it's not blocked, make sure it's above its siblings orderAboveSiblings(); } private void windowWillEnterFullScreen() { isFullScreenAnimationOn = true; } private void windowDidEnterFullScreen() { isInFullScreen = true; isFullScreenAnimationOn = false; } private void windowWillExitFullScreen() { isFullScreenAnimationOn = true; } private void windowDidExitFullScreen() { isInFullScreen = false; isFullScreenAnimationOn = false; } }
private static void warning(String msg) { PlatformLogger logger = PlatformLogger.getLogger("sun.awt.FontConfiguration"); logger.warning(msg); }
final class XTextFieldPeer extends XComponentPeer implements TextFieldPeer { private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XTextField"); private String text; private final XAWTTextField xtext; private final boolean firstChangeSkipped; XTextFieldPeer(TextField target) { super(target); text = target.getText(); xtext = new XAWTTextField(text, this, target.getParent()); xtext.getDocument().addDocumentListener(xtext); xtext.setCursor(target.getCursor()); XToolkit.specialPeerMap.put(xtext, this); initTextField(); setText(target.getText()); if (target.echoCharIsSet()) { setEchoChar(target.getEchoChar()); } else setEchoChar((char) 0); int start = target.getSelectionStart(); int end = target.getSelectionEnd(); // Fix for 5100200 // Restoring Motif behaviour // Since the end position of the selected text can be greater than the length of the text, // so we should set caret to max position of the text setCaretPosition(Math.min(end, text.length())); if (end > start) { // Should be called after setText() and setCaretPosition() select(start, end); } setEditable(target.isEditable()); // After this line we should not change the component's text firstChangeSkipped = true; AWTAccessor.getComponentAccessor().setPeer(xtext, this); } @Override public void dispose() { XToolkit.specialPeerMap.remove(xtext); // visible caret has a timer thread which must be stopped xtext.getCaret().setVisible(false); super.dispose(); } void initTextField() { setVisible(target.isVisible()); setBounds(x, y, width, height, SET_BOUNDS); AWTAccessor.ComponentAccessor compAccessor = AWTAccessor.getComponentAccessor(); foreground = compAccessor.getForeground(target); if (foreground == null) foreground = SystemColor.textText; setForeground(foreground); background = compAccessor.getBackground(target); if (background == null) { if (((TextField) target).isEditable()) background = SystemColor.text; else background = SystemColor.control; } setBackground(background); if (!target.isBackgroundSet()) { // This is a way to set the background color of the TextArea // without calling setBackground - go through accessor compAccessor.setBackground(target, background); } if (!target.isForegroundSet()) { target.setForeground(SystemColor.textText); } setFont(font); } /** @see java.awt.peer.TextComponentPeer */ @Override public void setEditable(boolean editable) { if (xtext != null) { xtext.setEditable(editable); xtext.repaint(); } } /** @see java.awt.peer.ComponentPeer */ @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); if (xtext != null) { xtext.setEnabled(enabled); xtext.repaint(); } } /** @see java.awt.peer.TextComponentPeer */ @Override public InputMethodRequests getInputMethodRequests() { if (xtext != null) return xtext.getInputMethodRequests(); else return null; } @Override void handleJavaInputMethodEvent(InputMethodEvent e) { if (xtext != null) xtext.processInputMethodEventImpl(e); } /** @see java.awt.peer.TextFieldPeer */ @Override public void setEchoChar(char c) { if (xtext != null) { xtext.setEchoChar(c); xtext.putClientProperty( "JPasswordField.cutCopyAllowed", xtext.echoCharIsSet() ? Boolean.FALSE : Boolean.TRUE); } } /** @see java.awt.peer.TextComponentPeer */ @Override public int getSelectionStart() { return xtext.getSelectionStart(); } /** @see java.awt.peer.TextComponentPeer */ @Override public int getSelectionEnd() { return xtext.getSelectionEnd(); } /** @see java.awt.peer.TextComponentPeer */ @Override @SuppressWarnings("deprecation") public String getText() { return xtext.getText(); } /** @see java.awt.peer.TextComponentPeer */ @Override public void setText(String text) { setXAWTTextField(text); repaint(); } private void setXAWTTextField(String txt) { text = txt; if (xtext != null) { // JTextField.setText() posts two different events (remove & insert). // Since we make no differences between text events, // the document listener has to be disabled while // JTextField.setText() is called. xtext.getDocument().removeDocumentListener(xtext); xtext.setText(txt); if (firstChangeSkipped) { postEvent(new TextEvent(target, TextEvent.TEXT_VALUE_CHANGED)); } xtext.getDocument().addDocumentListener(xtext); xtext.setCaretPosition(0); } } /** * to be implemented. * * @see java.awt.peer.TextComponentPeer */ @Override public void setCaretPosition(int position) { if (xtext != null) xtext.setCaretPosition(position); } void repaintText() { xtext.repaintNow(); } @Override public void setBackground(Color c) { if (log.isLoggable(PlatformLogger.Level.FINE)) { log.fine("target=" + target + ", old=" + background + ", new=" + c); } background = c; if (xtext != null) { if (xtext.getBackground() != c) { xtext.setBackground(c); } xtext.setSelectedTextColor(c); } repaintText(); } @Override public void setForeground(Color c) { foreground = c; if (xtext != null) { if (xtext.getForeground() != c) { xtext.setForeground(foreground); } xtext.setSelectionColor(foreground); xtext.setCaretColor(foreground); } repaintText(); } @Override public void setFont(Font f) { synchronized (getStateLock()) { font = f; if (xtext != null && xtext.getFont() != f) { xtext.setFont(font); } } xtext.validate(); } /** Deselects the highlighted text. */ public void deselect() { int selStart = xtext.getSelectionStart(); int selEnd = xtext.getSelectionEnd(); if (selStart != selEnd) { xtext.select(selStart, selStart); } } /** * to be implemented. * * @see java.awt.peer.TextComponentPeer */ @Override public int getCaretPosition() { return xtext.getCaretPosition(); } /** @see java.awt.peer.TextComponentPeer */ @Override public void select(int s, int e) { xtext.select(s, e); // Fixed 5100806 // We must take care that Swing components repainted correctly xtext.repaint(); } @Override public Dimension getMinimumSize() { return xtext.getMinimumSize(); } @Override public Dimension getPreferredSize() { return xtext.getPreferredSize(); } @Override public Dimension getPreferredSize(int cols) { return getMinimumSize(cols); } private static final int PADDING = 16; @Override public Dimension getMinimumSize(int cols) { Font f = xtext.getFont(); FontMetrics fm = xtext.getFontMetrics(f); return new Dimension( fm.charWidth('0') * cols + 10, fm.getMaxDescent() + fm.getMaxAscent() + PADDING); } @Override public boolean isFocusable() { return true; } // NOTE: This method is called by privileged threads. // DO NOT INVOKE CLIENT CODE ON THIS THREAD! public void action(final long when, final int modifiers) { postEvent(new ActionEvent(target, ActionEvent.ACTION_PERFORMED, text, when, modifiers)); } protected void disposeImpl() {} @Override public void repaint() { if (xtext != null) xtext.repaint(); } @Override void paintPeer(final Graphics g) { if (xtext != null) xtext.paint(g); } @Override public void print(Graphics g) { if (xtext != null) { xtext.print(g); } } @Override public void focusLost(FocusEvent e) { super.focusLost(e); xtext.forwardFocusLost(e); } @Override public void focusGained(FocusEvent e) { super.focusGained(e); xtext.forwardFocusGained(e); } @Override void handleJavaKeyEvent(KeyEvent e) { AWTAccessor.getComponentAccessor().processEvent(xtext, e); } @Override public void handleJavaMouseEvent(MouseEvent mouseEvent) { super.handleJavaMouseEvent(mouseEvent); if (xtext != null) { mouseEvent.setSource(xtext); int id = mouseEvent.getID(); if (id == MouseEvent.MOUSE_DRAGGED || id == MouseEvent.MOUSE_MOVED) xtext.processMouseMotionEventImpl(mouseEvent); else xtext.processMouseEventImpl(mouseEvent); } } /** DEPRECATED */ @Override public Dimension minimumSize() { return getMinimumSize(); } @Override public void setVisible(boolean b) { super.setVisible(b); if (xtext != null) xtext.setVisible(b); } @Override public void setBounds(int x, int y, int width, int height, int op) { super.setBounds(x, y, width, height, op); if (xtext != null) { /* * Fixed 6277332, 6198290: * the coordinates is coming (to peer): relatively to closest HW parent * the coordinates is setting (to textField): relatively to closest ANY parent * the parent of peer is target.getParent() * the parent of textField is the same * see 6277332, 6198290 for more information */ int childX = x; int childY = y; Component parent = target.getParent(); // we up to heavyweight parent in order to be sure // that the coordinates of the text pane is relatively to closest parent while (parent.isLightweight()) { childX -= parent.getX(); childY -= parent.getY(); parent = parent.getParent(); } xtext.setBounds(childX, childY, width, height); xtext.validate(); } } final class AWTTextFieldUI extends MotifPasswordFieldUI { private JTextField jtf; @Override protected String getPropertyPrefix() { JTextComponent comp = getComponent(); if (comp instanceof JPasswordField && ((JPasswordField) comp).echoCharIsSet()) { return "PasswordField"; } else { return "TextField"; } } @Override public void installUI(JComponent c) { super.installUI(c); jtf = (JTextField) c; JTextField editor = jtf; UIDefaults uidefaults = XToolkit.getUIDefaults(); String prefix = getPropertyPrefix(); Font f = editor.getFont(); if ((f == null) || (f instanceof UIResource)) { editor.setFont(uidefaults.getFont(prefix + ".font")); } Color bg = editor.getBackground(); if ((bg == null) || (bg instanceof UIResource)) { editor.setBackground(uidefaults.getColor(prefix + ".background")); } Color fg = editor.getForeground(); if ((fg == null) || (fg instanceof UIResource)) { editor.setForeground(uidefaults.getColor(prefix + ".foreground")); } Color color = editor.getCaretColor(); if ((color == null) || (color instanceof UIResource)) { editor.setCaretColor(uidefaults.getColor(prefix + ".caretForeground")); } Color s = editor.getSelectionColor(); if ((s == null) || (s instanceof UIResource)) { editor.setSelectionColor(uidefaults.getColor(prefix + ".selectionBackground")); } Color sfg = editor.getSelectedTextColor(); if ((sfg == null) || (sfg instanceof UIResource)) { editor.setSelectedTextColor(uidefaults.getColor(prefix + ".selectionForeground")); } Color dfg = editor.getDisabledTextColor(); if ((dfg == null) || (dfg instanceof UIResource)) { editor.setDisabledTextColor(uidefaults.getColor(prefix + ".inactiveForeground")); } Border b = editor.getBorder(); if ((b == null) || (b instanceof UIResource)) { editor.setBorder(uidefaults.getBorder(prefix + ".border")); } Insets margin = editor.getMargin(); if (margin == null || margin instanceof UIResource) { editor.setMargin(uidefaults.getInsets(prefix + ".margin")); } } @Override protected void installKeyboardActions() { super.installKeyboardActions(); JTextComponent comp = getComponent(); UIDefaults uidefaults = XToolkit.getUIDefaults(); String prefix = getPropertyPrefix(); InputMap map = (InputMap) uidefaults.get(prefix + ".focusInputMap"); if (map != null) { SwingUtilities.replaceUIInputMap(comp, JComponent.WHEN_FOCUSED, map); } } @Override protected Caret createCaret() { return new XTextAreaPeer.XAWTCaret(); } } @SuppressWarnings("serial") // JDK-implementation class final class XAWTTextField extends JPasswordField implements ActionListener, DocumentListener { private boolean isFocused = false; private final XComponentPeer xwin; XAWTTextField(String text, XComponentPeer xwin, Container parent) { super(text); this.xwin = xwin; setDoubleBuffered(true); setFocusable(false); AWTAccessor.getComponentAccessor().setParent(this, parent); setBackground(xwin.getPeerBackground()); setForeground(xwin.getPeerForeground()); setFont(xwin.getPeerFont()); setCaretPosition(0); addActionListener(this); addNotify(); } @Override @SuppressWarnings("deprecation") public void actionPerformed(ActionEvent actionEvent) { xwin.postEvent( new ActionEvent( xwin.target, ActionEvent.ACTION_PERFORMED, getText(), actionEvent.getWhen(), actionEvent.getModifiers())); } @Override public void insertUpdate(DocumentEvent e) { if (xwin != null) { xwin.postEvent(new TextEvent(xwin.target, TextEvent.TEXT_VALUE_CHANGED)); } } @Override public void removeUpdate(DocumentEvent e) { if (xwin != null) { xwin.postEvent(new TextEvent(xwin.target, TextEvent.TEXT_VALUE_CHANGED)); } } @Override public void changedUpdate(DocumentEvent e) { if (xwin != null) { xwin.postEvent(new TextEvent(xwin.target, TextEvent.TEXT_VALUE_CHANGED)); } } public void repaintNow() { paintImmediately(getBounds()); } @Override public Graphics getGraphics() { return xwin.getGraphics(); } @Override public void updateUI() { ComponentUI ui = new AWTTextFieldUI(); setUI(ui); } void forwardFocusGained(FocusEvent e) { isFocused = true; FocusEvent fe = new FocusEvent(this, e.getID(), e.isTemporary(), e.getOppositeComponent(), e.getCause()); super.processFocusEvent(fe); } void forwardFocusLost(FocusEvent e) { isFocused = false; FocusEvent fe = new FocusEvent(this, e.getID(), e.isTemporary(), e.getOppositeComponent(), e.getCause()); super.processFocusEvent(fe); } @Override public boolean hasFocus() { return isFocused; } public void processInputMethodEventImpl(InputMethodEvent e) { processInputMethodEvent(e); } public void processMouseEventImpl(MouseEvent e) { processMouseEvent(e); } public void processMouseMotionEventImpl(MouseEvent e) { processMouseMotionEvent(e); } // Fix for 4915454 - override the default implementation to avoid // loading SystemFlavorMap and associated classes. @Override public void setTransferHandler(TransferHandler newHandler) { TransferHandler oldHandler = (TransferHandler) getClientProperty( AWTAccessor.getClientPropertyKeyAccessor().getJComponent_TRANSFER_HANDLER()); putClientProperty( AWTAccessor.getClientPropertyKeyAccessor().getJComponent_TRANSFER_HANDLER(), newHandler); firePropertyChange("transferHandler", oldHandler, newHandler); } @Override public void setEchoChar(char c) { super.setEchoChar(c); ((AWTTextFieldUI) ui).installKeyboardActions(); } } }
public class XEmbedCanvasPeer extends XCanvasPeer implements WindowFocusListener, KeyEventPostProcessor, ModalityListener, WindowIDProvider { private static final PlatformLogger xembedLog = PlatformLogger.getLogger("sun.awt.X11.xembed.XEmbedCanvasPeer"); boolean applicationActive; // Whether the application is active(has focus) XEmbedServer xembed = new XEmbedServer(); // Helper object, contains XEmbed intrinsics Map<Long, AWTKeyStroke> accelerators = new HashMap<Long, AWTKeyStroke>(); // Maps accelerator ID into AWTKeyStroke Map<AWTKeyStroke, Long> accel_lookup = new HashMap<AWTKeyStroke, Long>(); // Maps AWTKeyStroke into accelerator ID Set<GrabbedKey> grabbed_keys = new HashSet<GrabbedKey>(); // A set of keys grabbed by client Object ACCEL_LOCK = accelerators; // Lock object for working with accelerators; Object GRAB_LOCK = grabbed_keys; // Lock object for working with keys grabbed by client XEmbedCanvasPeer() {} XEmbedCanvasPeer(XCreateWindowParams params) { super(params); } XEmbedCanvasPeer(Component target) { super(target); } protected void postInit(XCreateWindowParams params) { super.postInit(params); installActivateListener(); installAcceleratorListener(); installModalityListener(); // XEmbed canvas should be non-traversable. // FIXME: Probably should be removed and enforced setting of it by the users target.setFocusTraversalKeysEnabled(false); } protected void preInit(XCreateWindowParams params) { super.preInit(params); params.put( EVENT_MASK, XConstants.KeyPressMask | XConstants.KeyReleaseMask | XConstants.FocusChangeMask | XConstants.ButtonPressMask | XConstants.ButtonReleaseMask | XConstants.EnterWindowMask | XConstants.LeaveWindowMask | XConstants.PointerMotionMask | XConstants.ButtonMotionMask | XConstants.ExposureMask | XConstants.StructureNotifyMask | XConstants.SubstructureNotifyMask); } void installModalityListener() { ((SunToolkit) Toolkit.getDefaultToolkit()).addModalityListener(this); } void deinstallModalityListener() { ((SunToolkit) Toolkit.getDefaultToolkit()).removeModalityListener(this); } void installAcceleratorListener() { KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventPostProcessor(this); } void deinstallAcceleratorListener() { KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventPostProcessor(this); } void installActivateListener() { // FIXME: should watch for hierarchy changes Window toplevel = getTopLevel(target); if (toplevel != null) { toplevel.addWindowFocusListener(this); applicationActive = toplevel.isFocused(); } } void deinstallActivateListener() { Window toplevel = getTopLevel(target); if (toplevel != null) { toplevel.removeWindowFocusListener(this); } } boolean isXEmbedActive() { return xembed.handle != 0; } boolean isApplicationActive() { return applicationActive; } void initDispatching() { if (xembedLog.isLoggable(PlatformLogger.FINE)) xembedLog.fine("Init embedding for " + Long.toHexString(xembed.handle)); XToolkit.awtLock(); try { XToolkit.addEventDispatcher(xembed.handle, xembed); XlibWrapper.XSelectInput( XToolkit.getDisplay(), xembed.handle, XConstants.StructureNotifyMask | XConstants.PropertyChangeMask); XDropTargetRegistry.getRegistry().registerXEmbedClient(getWindow(), xembed.handle); } finally { XToolkit.awtUnlock(); } xembed.processXEmbedInfo(); notifyChildEmbedded(); } void endDispatching() { xembedLog.fine("End dispatching for " + Long.toHexString(xembed.handle)); XToolkit.awtLock(); try { XDropTargetRegistry.getRegistry().unregisterXEmbedClient(getWindow(), xembed.handle); // We can't deselect input since someone else might be interested in it XToolkit.removeEventDispatcher(xembed.handle, xembed); } finally { XToolkit.awtUnlock(); } } void embedChild(long child) { if (xembed.handle != 0) { detachChild(); } xembed.handle = child; initDispatching(); } void childDestroyed() { xembedLog.fine("Child " + Long.toHexString(xembed.handle) + " has self-destroyed."); endDispatching(); xembed.handle = 0; } public void handleEvent(AWTEvent e) { super.handleEvent(e); if (isXEmbedActive()) { switch (e.getID()) { case FocusEvent.FOCUS_GAINED: canvasFocusGained((FocusEvent) e); break; case FocusEvent.FOCUS_LOST: canvasFocusLost((FocusEvent) e); break; case KeyEvent.KEY_PRESSED: case KeyEvent.KEY_RELEASED: if (!((InputEvent) e).isConsumed()) { forwardKeyEvent((KeyEvent) e); } break; } } } public void dispatchEvent(XEvent ev) { super.dispatchEvent(ev); switch (ev.get_type()) { case XConstants.CreateNotify: XCreateWindowEvent cr = ev.get_xcreatewindow(); if (xembedLog.isLoggable(PlatformLogger.FINEST)) { xembedLog.finest("Message on embedder: " + cr); } if (xembedLog.isLoggable(PlatformLogger.FINER)) { xembedLog.finer( "Create notify for parent " + Long.toHexString(cr.get_parent()) + ", window " + Long.toHexString(cr.get_window())); } embedChild(cr.get_window()); break; case XConstants.DestroyNotify: XDestroyWindowEvent dn = ev.get_xdestroywindow(); if (xembedLog.isLoggable(PlatformLogger.FINEST)) { xembedLog.finest("Message on embedder: " + dn); } if (xembedLog.isLoggable(PlatformLogger.FINER)) { xembedLog.finer("Destroy notify for parent: " + dn); } childDestroyed(); break; case XConstants.ReparentNotify: XReparentEvent rep = ev.get_xreparent(); if (xembedLog.isLoggable(PlatformLogger.FINEST)) { xembedLog.finest("Message on embedder: " + rep); } if (xembedLog.isLoggable(PlatformLogger.FINER)) { xembedLog.finer( "Reparent notify for parent " + Long.toHexString(rep.get_parent()) + ", window " + Long.toHexString(rep.get_window()) + ", event " + Long.toHexString(rep.get_event())); } if (rep.get_parent() == getWindow()) { // Reparented into us - embed it embedChild(rep.get_window()); } else { // Reparented out of us - detach it childDestroyed(); } break; } } public Dimension getPreferredSize() { if (isXEmbedActive()) { XToolkit.awtLock(); try { long p_hints = XlibWrapper.XAllocSizeHints(); XSizeHints hints = new XSizeHints(p_hints); XlibWrapper.XGetWMNormalHints( XToolkit.getDisplay(), xembed.handle, p_hints, XlibWrapper.larg1); Dimension res = new Dimension(hints.get_width(), hints.get_height()); XlibWrapper.XFree(p_hints); return res; } finally { XToolkit.awtUnlock(); } } else { return super.getPreferredSize(); } } public Dimension getMinimumSize() { if (isXEmbedActive()) { XToolkit.awtLock(); try { long p_hints = XlibWrapper.XAllocSizeHints(); XSizeHints hints = new XSizeHints(p_hints); XlibWrapper.XGetWMNormalHints( XToolkit.getDisplay(), xembed.handle, p_hints, XlibWrapper.larg1); Dimension res = new Dimension(hints.get_min_width(), hints.get_min_height()); XlibWrapper.XFree(p_hints); return res; } finally { XToolkit.awtUnlock(); } } else { return super.getMinimumSize(); } } public void dispose() { if (isXEmbedActive()) { detachChild(); } deinstallActivateListener(); deinstallModalityListener(); deinstallAcceleratorListener(); // BUG: Focus traversal doesn't become enabled after the one round of embedding // target.setFocusTraversalKeysEnabled(true); super.dispose(); } // Focusable is true in order to enable focus traversal through this Canvas public boolean isFocusable() { return true; } Window getTopLevel(Component comp) { while (comp != null && !(comp instanceof Window)) { comp = comp.getParent(); } return (Window) comp; } Rectangle getClientBounds() { XToolkit.awtLock(); try { XWindowAttributes wattr = new XWindowAttributes(); try { XToolkit.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance()); int status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), xembed.handle, wattr.pData); XToolkit.RESTORE_XERROR_HANDLER(); if (status == 0 || (XToolkit.saved_error != null && XToolkit.saved_error.get_error_code() != XConstants.Success)) { return null; } return new Rectangle(wattr.get_x(), wattr.get_y(), wattr.get_width(), wattr.get_height()); } finally { wattr.dispose(); } } finally { XToolkit.awtUnlock(); } } void childResized() { if (xembedLog.isLoggable(PlatformLogger.FINER)) { Rectangle bounds = getClientBounds(); xembedLog.finer("Child resized: " + bounds); // It is not required to update embedder's size when client size changes // However, since there is no any means to get client size it seems to be the // only way to provide it. However, it contradicts with Java layout concept - // so it is disabled for now. // Rectangle my_bounds = getBounds(); // setBounds(my_bounds.x, my_bounds.y, bounds.width, bounds.height, SET_BOUNDS); } XToolkit.postEvent( XToolkit.targetToAppContext(target), new ComponentEvent(target, ComponentEvent.COMPONENT_RESIZED)); } void focusNext() { if (isXEmbedActive()) { xembedLog.fine("Requesting focus for the next component after embedder"); postEvent( new InvocationEvent( target, new Runnable() { public void run() { KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent(target); } })); } else { xembedLog.fine("XEmbed is not active - denying focus next"); } } void focusPrev() { if (isXEmbedActive()) { xembedLog.fine("Requesting focus for the next component after embedder"); postEvent( new InvocationEvent( target, new Runnable() { public void run() { KeyboardFocusManager.getCurrentKeyboardFocusManager() .focusPreviousComponent(target); } })); } else { xembedLog.fine("XEmbed is not active - denying focus prev"); } } void requestXEmbedFocus() { if (isXEmbedActive()) { xembedLog.fine("Requesting focus for client"); postEvent( new InvocationEvent( target, new Runnable() { public void run() { target.requestFocus(); } })); } else { xembedLog.fine("XEmbed is not active - denying request focus"); } } void notifyChildEmbedded() { xembed.sendMessage( xembed.handle, XEMBED_EMBEDDED_NOTIFY, getWindow(), Math.min(xembed.version, XEMBED_VERSION), 0); if (isApplicationActive()) { xembedLog.fine("Sending WINDOW_ACTIVATE during initialization"); xembed.sendMessage(xembed.handle, XEMBED_WINDOW_ACTIVATE); if (hasFocus()) { xembedLog.fine("Sending FOCUS_GAINED during initialization"); xembed.sendMessage(xembed.handle, XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT, 0, 0); } } } void detachChild() { if (xembedLog.isLoggable(PlatformLogger.FINE)) xembedLog.fine("Detaching child " + Long.toHexString(xembed.handle)); /** * XEmbed specification: "The embedder can unmap the client and reparent the client window to * the root window. If the client receives an ReparentNotify event, it should check the parent * field of the XReparentEvent structure. If this is the root window of the window's screen, * then the protocol is finished and there is no further interaction. If it is a window other * than the root window, then the protocol continues with the new parent acting as the embedder * window." */ XToolkit.awtLock(); try { XlibWrapper.XUnmapWindow(XToolkit.getDisplay(), xembed.handle); XlibWrapper.XReparentWindow( XToolkit.getDisplay(), xembed.handle, XToolkit.getDefaultRootWindow(), 0, 0); } finally { XToolkit.awtUnlock(); } endDispatching(); xembed.handle = 0; } public void windowGainedFocus(WindowEvent e) { applicationActive = true; if (isXEmbedActive()) { xembedLog.fine("Sending WINDOW_ACTIVATE"); xembed.sendMessage(xembed.handle, XEMBED_WINDOW_ACTIVATE); } } public void windowLostFocus(WindowEvent e) { applicationActive = false; if (isXEmbedActive()) { xembedLog.fine("Sending WINDOW_DEACTIVATE"); xembed.sendMessage(xembed.handle, XEMBED_WINDOW_DEACTIVATE); } } void canvasFocusGained(FocusEvent e) { if (isXEmbedActive()) { xembedLog.fine("Forwarding FOCUS_GAINED"); int flavor = XEMBED_FOCUS_CURRENT; if (e instanceof CausedFocusEvent) { CausedFocusEvent ce = (CausedFocusEvent) e; if (ce.getCause() == CausedFocusEvent.Cause.TRAVERSAL_FORWARD) { flavor = XEMBED_FOCUS_FIRST; } else if (ce.getCause() == CausedFocusEvent.Cause.TRAVERSAL_BACKWARD) { flavor = XEMBED_FOCUS_LAST; } } xembed.sendMessage(xembed.handle, XEMBED_FOCUS_IN, flavor, 0, 0); } } void canvasFocusLost(FocusEvent e) { if (isXEmbedActive() && !e.isTemporary()) { xembedLog.fine("Forwarding FOCUS_LOST"); int num = 0; if (AccessController.doPrivileged(new GetBooleanAction("sun.awt.xembed.testing"))) { Component opp = e.getOppositeComponent(); try { num = Integer.parseInt(opp.getName()); } catch (NumberFormatException nfe) { } } xembed.sendMessage(xembed.handle, XEMBED_FOCUS_OUT, num, 0, 0); } } static Field bdataField; static byte[] getBData(KeyEvent e) { try { if (bdataField == null) { bdataField = SunToolkit.getField(java.awt.AWTEvent.class, "bdata"); } return (byte[]) bdataField.get(e); } catch (IllegalAccessException ex) { return null; } } void forwardKeyEvent(KeyEvent e) { xembedLog.fine("Try to forward key event"); byte[] bdata = getBData(e); long data = Native.toData(bdata); if (data == 0) { return; } try { XKeyEvent ke = new XKeyEvent(data); ke.set_window(xembed.handle); if (xembedLog.isLoggable(PlatformLogger.FINE)) xembedLog.fine("Forwarding native key event: " + ke); XToolkit.awtLock(); try { XlibWrapper.XSendEvent( XToolkit.getDisplay(), xembed.handle, false, XConstants.NoEventMask, data); } finally { XToolkit.awtUnlock(); } } finally { XlibWrapper.unsafe.freeMemory(data); } } /** * Grab/ungrab key functionality is an unofficial API supported by GTK. Unfortunately, it doesn't * support accelerator API, so, since this is the ONLY shortcut-processing API available, we must * support it. See XEmbed.NON_STANDARD_XEMBED_GTK_* messages. The format of these messages is as * follows: - request from client: data[1] = NON_STANDARD_XEMBED_GTK_GRAB_KEY or * NON_STANDARD_XEMBED_GTK_UNGRAB_KEY data[3] = X keysym data[4] = X modifiers * * <p>- response from server (in case the grabbed key has been pressed): forwarded XKeyEvent that * matches keysym/modifiers pair */ void grabKey(final long keysym, final long modifiers) { postEvent( new InvocationEvent( target, new Runnable() { public void run() { GrabbedKey grab = new GrabbedKey(keysym, modifiers); if (xembedLog.isLoggable(PlatformLogger.FINE)) xembedLog.fine("Grabbing key: " + grab); synchronized (GRAB_LOCK) { grabbed_keys.add(grab); } } })); } void ungrabKey(final long keysym, final long modifiers) { postEvent( new InvocationEvent( target, new Runnable() { public void run() { GrabbedKey grab = new GrabbedKey(keysym, modifiers); if (xembedLog.isLoggable(PlatformLogger.FINE)) xembedLog.fine("UnGrabbing key: " + grab); synchronized (GRAB_LOCK) { grabbed_keys.remove(grab); } } })); } void registerAccelerator(final long accel_id, final long keysym, final long modifiers) { postEvent( new InvocationEvent( target, new Runnable() { public void run() { AWTKeyStroke stroke = xembed.getKeyStrokeForKeySym(keysym, modifiers); if (stroke != null) { if (xembedLog.isLoggable(PlatformLogger.FINE)) xembedLog.fine("Registering accelerator " + accel_id + " for " + stroke); synchronized (ACCEL_LOCK) { accelerators.put(accel_id, stroke); accel_lookup.put(stroke, accel_id); } } propogateRegisterAccelerator(stroke); } })); } void unregisterAccelerator(final long accel_id) { postEvent( new InvocationEvent( target, new Runnable() { public void run() { AWTKeyStroke stroke = null; synchronized (ACCEL_LOCK) { stroke = accelerators.get(accel_id); if (stroke != null) { if (xembedLog.isLoggable(PlatformLogger.FINE)) xembedLog.fine("Unregistering accelerator: " + accel_id); accelerators.remove(accel_id); accel_lookup.remove( stroke); // FIXME: How about several accelerators with the same stroke? } } propogateUnRegisterAccelerator(stroke); } })); } void propogateRegisterAccelerator(AWTKeyStroke stroke) { // Find the top-level and see if it is XEmbed client. If so, ask him to // register the accelerator XWindowPeer parent = getToplevelXWindow(); if (parent != null && parent instanceof XEmbeddedFramePeer) { XEmbeddedFramePeer embedded = (XEmbeddedFramePeer) parent; embedded.registerAccelerator(stroke); } } void propogateUnRegisterAccelerator(AWTKeyStroke stroke) { // Find the top-level and see if it is XEmbed client. If so, ask him to // register the accelerator XWindowPeer parent = getToplevelXWindow(); if (parent != null && parent instanceof XEmbeddedFramePeer) { XEmbeddedFramePeer embedded = (XEmbeddedFramePeer) parent; embedded.unregisterAccelerator(stroke); } } public boolean postProcessKeyEvent(KeyEvent e) { // Processing events only if we are in the focused window but // we are not focus owner since otherwise we will get // duplicate shortcut events in the client - one is from // activate_accelerator, another from forwarded event // FIXME: This is probably an incompatibility, protocol // doesn't say anything about disable accelerators when client // is focused. XWindowPeer parent = getToplevelXWindow(); if (parent == null || !((Window) parent.getTarget()).isFocused() || target.isFocusOwner()) { return false; } boolean result = false; if (xembedLog.isLoggable(PlatformLogger.FINER)) xembedLog.finer("Post-processing event " + e); // Process ACCELERATORS AWTKeyStroke stroke = AWTKeyStroke.getAWTKeyStrokeForEvent(e); long accel_id = 0; boolean exists = false; synchronized (ACCEL_LOCK) { exists = accel_lookup.containsKey(stroke); if (exists) { accel_id = accel_lookup.get(stroke).longValue(); } } if (exists) { if (xembedLog.isLoggable(PlatformLogger.FINE)) xembedLog.fine("Activating accelerator " + accel_id); xembed.sendMessage( xembed.handle, XEMBED_ACTIVATE_ACCELERATOR, accel_id, 0, 0); // FIXME: How about overloaded? result = true; } // Process Grabs, unofficial GTK feature exists = false; GrabbedKey key = new GrabbedKey(e); synchronized (GRAB_LOCK) { exists = grabbed_keys.contains(key); } if (exists) { if (xembedLog.isLoggable(PlatformLogger.FINE)) xembedLog.fine("Forwarding grabbed key " + e); forwardKeyEvent(e); result = true; } return result; } public void modalityPushed(ModalityEvent ev) { xembed.sendMessage(xembed.handle, XEMBED_MODALITY_ON); } public void modalityPopped(ModalityEvent ev) { xembed.sendMessage(xembed.handle, XEMBED_MODALITY_OFF); } public void handleClientMessage(XEvent xev) { super.handleClientMessage(xev); XClientMessageEvent msg = xev.get_xclient(); if (xembedLog.isLoggable(PlatformLogger.FINER)) xembedLog.finer("Client message to embedder: " + msg); if (msg.get_message_type() == xembed.XEmbed.getAtom()) { if (xembedLog.isLoggable(PlatformLogger.FINE)) xembedLog.fine(xembed.XEmbedMessageToString(msg)); } if (isXEmbedActive()) { switch ((int) msg.get_data(1)) { case XEMBED_REQUEST_FOCUS: requestXEmbedFocus(); break; case XEMBED_FOCUS_NEXT: focusNext(); break; case XEMBED_FOCUS_PREV: focusPrev(); break; case XEMBED_REGISTER_ACCELERATOR: registerAccelerator(msg.get_data(2), msg.get_data(3), msg.get_data(4)); break; case XEMBED_UNREGISTER_ACCELERATOR: unregisterAccelerator(msg.get_data(2)); break; case NON_STANDARD_XEMBED_GTK_GRAB_KEY: grabKey(msg.get_data(3), msg.get_data(4)); break; case NON_STANDARD_XEMBED_GTK_UNGRAB_KEY: ungrabKey(msg.get_data(3), msg.get_data(4)); break; } } else { xembedLog.finer("But XEmbed is not Active!"); } } private static class XEmbedDropTarget extends DropTarget { public void addDropTargetListener(DropTargetListener dtl) throws TooManyListenersException { // Drop target listeners registered with this target will never be // notified, since all drag notifications are routed to the XEmbed // client. To avoid confusion we prohibit listeners registration // by throwing TooManyListenersException as if there is a listener // registered with this target already. throw new TooManyListenersException(); } } public void setXEmbedDropTarget() { // Register a drop site on the top level. Runnable r = new Runnable() { public void run() { target.setDropTarget(new XEmbedDropTarget()); } }; SunToolkit.executeOnEventHandlerThread(target, r); } public void removeXEmbedDropTarget() { // Unregister a drop site on the top level. Runnable r = new Runnable() { public void run() { if (target.getDropTarget() instanceof XEmbedDropTarget) { target.setDropTarget(null); } } }; SunToolkit.executeOnEventHandlerThread(target, r); } public boolean processXEmbedDnDEvent(long ctxt, int eventID) { if (xembedLog.isLoggable(PlatformLogger.FINEST)) { xembedLog.finest(" Drop target=" + target.getDropTarget()); } if (target.getDropTarget() instanceof XEmbedDropTarget) { AppContext appContext = XToolkit.targetToAppContext(getTarget()); XDropTargetContextPeer peer = XDropTargetContextPeer.getPeer(appContext); peer.forwardEventToEmbedded(xembed.handle, ctxt, eventID); return true; } else { return false; } } class XEmbedServer extends XEmbedHelper implements XEventDispatcher { long handle; // Handle to XEmbed client long version; long flags; boolean processXEmbedInfo() { long xembed_info_data = Native.allocateLongArray(2); try { if (!XEmbedInfo.getAtomData(handle, xembed_info_data, 2)) { // No more XEMBED_INFO? This is not XEmbed client! // Unfortunately this is the initial state of the most clients // FIXME: add 5-state processing // childDestroyed(); xembedLog.finer("Unable to get XEMBED_INFO atom data"); return false; } version = Native.getCard32(xembed_info_data, 0); flags = Native.getCard32(xembed_info_data, 1); boolean new_mapped = (flags & XEMBED_MAPPED) != 0; boolean currently_mapped = XlibUtil.getWindowMapState(handle) != XConstants.IsUnmapped; if (new_mapped != currently_mapped) { if (xembedLog.isLoggable(PlatformLogger.FINER)) xembedLog.fine( "Mapping state of the client has changed, old state: " + currently_mapped + ", new state: " + new_mapped); if (new_mapped) { XToolkit.awtLock(); try { XlibWrapper.XMapWindow(XToolkit.getDisplay(), handle); } finally { XToolkit.awtUnlock(); } } else { XToolkit.awtLock(); try { XlibWrapper.XUnmapWindow(XToolkit.getDisplay(), handle); } finally { XToolkit.awtUnlock(); } } } else { xembedLog.finer("Mapping state didn't change, mapped: " + currently_mapped); } return true; } finally { XlibWrapper.unsafe.freeMemory(xembed_info_data); } } public void handlePropertyNotify(XEvent xev) { if (isXEmbedActive()) { XPropertyEvent ev = xev.get_xproperty(); if (xembedLog.isLoggable(PlatformLogger.FINER)) xembedLog.finer("Property change on client: " + ev); if (ev.get_atom() == XAtom.XA_WM_NORMAL_HINTS) { childResized(); } else if (ev.get_atom() == XEmbedInfo.getAtom()) { processXEmbedInfo(); } else if (ev.get_atom() == XDnDConstants.XA_XdndAware.getAtom()) { XDropTargetRegistry.getRegistry().unregisterXEmbedClient(getWindow(), xembed.handle); if (ev.get_state() == XConstants.PropertyNewValue) { XDropTargetRegistry.getRegistry().registerXEmbedClient(getWindow(), xembed.handle); } } } else { xembedLog.finer("XEmbed is not active"); } } void handleConfigureNotify(XEvent xev) { if (isXEmbedActive()) { XConfigureEvent ev = xev.get_xconfigure(); if (xembedLog.isLoggable(PlatformLogger.FINER)) xembedLog.finer("Bounds change on client: " + ev); if (xev.get_xany().get_window() == handle) { childResized(); } } } public void dispatchEvent(XEvent xev) { int type = xev.get_type(); switch (type) { case XConstants.PropertyNotify: handlePropertyNotify(xev); break; case XConstants.ConfigureNotify: handleConfigureNotify(xev); break; case XConstants.ClientMessage: handleClientMessage(xev); break; } } } static class GrabbedKey { long keysym; long modifiers; GrabbedKey(long keysym, long modifiers) { this.keysym = keysym; this.modifiers = modifiers; } GrabbedKey(KeyEvent ev) { init(ev); } private void init(KeyEvent e) { byte[] bdata = getBData(e); long data = Native.toData(bdata); if (data == 0) { return; } try { XToolkit.awtLock(); try { keysym = XWindow.getKeySymForAWTKeyCode(e.getKeyCode()); } finally { XToolkit.awtUnlock(); } XKeyEvent ke = new XKeyEvent(data); // We recognize only these masks modifiers = ke.get_state() & (XConstants.ShiftMask | XConstants.ControlMask | XConstants.LockMask); if (xembedLog.isLoggable(PlatformLogger.FINEST)) xembedLog.finest("Mapped " + e + " to " + this); } finally { XlibWrapper.unsafe.freeMemory(data); } } public int hashCode() { return (int) keysym & 0xFFFFFFFF; } public boolean equals(Object o) { if (!(o instanceof GrabbedKey)) { return false; } GrabbedKey key = (GrabbedKey) o; return (keysym == key.keysym && modifiers == key.modifiers); } public String toString() { return "Key combination[keysym=" + keysym + ", mods=" + modifiers + "]"; } } }
/** * The AppContext is a table referenced by ThreadGroup which stores application service instances. * (If you are not writing an application service, or don't know what one is, please do not use this * class.) The AppContext allows applet access to what would otherwise be potentially dangerous * services, such as the ability to peek at EventQueues or change the look-and-feel of a Swing * application. * * <p>Most application services use a singleton object to provide their services, either as a * default (such as getSystemEventQueue or getDefaultToolkit) or as static methods with class data * (System). The AppContext works with the former method by extending the concept of "default" to be * ThreadGroup-specific. Application services lookup their singleton in the AppContext. * * <p>For example, here we have a Foo service, with its pre-AppContext code: * * <p><code><pre> * public class Foo { * private static Foo defaultFoo = new Foo(); * * public static Foo getDefaultFoo() { * return defaultFoo; * } * * ... Foo service methods * }</pre></code> * * <p>The problem with the above is that the Foo service is global in scope, so that applets and * other untrusted code can execute methods on the single, shared Foo instance. The Foo service * therefore either needs to block its use by untrusted code using a SecurityManager test, or * restrict its capabilities so that it doesn't matter if untrusted code executes it. * * <p>Here's the Foo class written to use the AppContext: * * <p><code><pre> * public class Foo { * public static Foo getDefaultFoo() { * Foo foo = (Foo)AppContext.getAppContext().get(Foo.class); * if (foo == null) { * foo = new Foo(); * getAppContext().put(Foo.class, foo); * } * return foo; * } * * ... Foo service methods * }</pre></code> * * <p>Since a separate AppContext can exist for each ThreadGroup, trusted and untrusted code have * access to different Foo instances. This allows untrusted code access to "system-wide" services -- * the service remains within the AppContext "sandbox". For example, say a malicious applet wants to * peek all of the key events on the EventQueue to listen for passwords; if separate EventQueues are * used for each ThreadGroup using AppContexts, the only key events that applet will be able to * listen to are its own. A more reasonable applet request would be to change the Swing default * look-and-feel; with that default stored in an AppContext, the applet's look-and-feel will change * without disrupting other applets or potentially the browser itself. * * <p>Because the AppContext is a facility for safely extending application service support to * applets, none of its methods may be blocked by a a SecurityManager check in a valid Java * implementation. Applets may therefore safely invoke any of its methods without worry of being * blocked. * * <p>Note: If a SecurityManager is installed which derives from sun.awt.AWTSecurityManager, it may * override the AWTSecurityManager.getAppContext() method to return the proper AppContext based on * the execution context, in the case where the default ThreadGroup-based AppContext indexing would * return the main "system" AppContext. For example, in an applet situation, if a system thread * calls into an applet, rather than returning the main "system" AppContext (the one corresponding * to the system thread), an installed AWTSecurityManager may return the applet's AppContext based * on the execution context. * * @author Thomas Ball * @author Fred Ecks */ public final class AppContext extends AppContextDC { private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.AppContext"); /* Since the contents of an AppContext are unique to each Java * session, this class should never be serialized. */ /* * The key to put()/get() the Java EventQueue into/from the AppContext. */ public static final Object EVENT_QUEUE_KEY = new StringBuffer("EventQueue"); /* * The keys to store EventQueue push/pop lock and condition. */ public static final Object EVENT_QUEUE_LOCK_KEY = new StringBuilder("EventQueue.Lock"); public static final Object EVENT_QUEUE_COND_KEY = new StringBuilder("EventQueue.Condition"); /* A map of AppContexts, referenced by ThreadGroup. */ private static final Map<ThreadGroup, AppContext> threadGroup2appContext = Collections.synchronizedMap(new IdentityHashMap<ThreadGroup, AppContext>()); /** Returns a set containing all <code>AppContext</code>s. */ public static Set<AppContext> getAppContexts() { synchronized (threadGroup2appContext) { return new HashSet<AppContext>(threadGroup2appContext.values()); } } /* The main "system" AppContext, used by everything not otherwise contained in another AppContext. */ private static volatile AppContext mainAppContext = null; /* * The hash map associated with this AppContext. A private delegate * is used instead of subclassing HashMap so as to avoid all of * HashMap's potentially risky methods, such as clear(), elements(), * putAll(), etc. */ private final HashMap table = new HashMap(); private final ThreadGroup threadGroup; /** * If any <code>PropertyChangeListeners</code> have been registered, the <code>changeSupport * </code> field describes them. * * @see #addPropertyChangeListener * @see #removePropertyChangeListener * @see #firePropertyChange */ private PropertyChangeSupport changeSupport = null; public static final String DISPOSED_PROPERTY_NAME = "disposed"; public static final String GUI_DISPOSED = "guidisposed"; private volatile boolean isDisposed = false; // true if AppContext is disposed public boolean isDisposed() { return isDisposed; } static { // On the main Thread, we get the ThreadGroup, make a corresponding // AppContext, and instantiate the Java EventQueue. This way, legacy // code is unaffected by the move to multiple AppContext ability. AccessController.doPrivileged( new PrivilegedAction() { public Object run() { ThreadGroup currentThreadGroup = Thread.currentThread().getThreadGroup(); ThreadGroup parentThreadGroup = currentThreadGroup.getParent(); while (parentThreadGroup != null) { // Find the root ThreadGroup to construct our main AppContext currentThreadGroup = parentThreadGroup; parentThreadGroup = currentThreadGroup.getParent(); } mainAppContext = new AppContext(currentThreadGroup); numAppContexts = 1; return mainAppContext; } }); } /* * The total number of AppContexts, system-wide. This number is * incremented at the beginning of the constructor, and decremented * at the end of dispose(). getAppContext() checks to see if this * number is 1. If so, it returns the sole AppContext without * checking Thread.currentThread(). */ private static volatile int numAppContexts; /* * The context ClassLoader that was used to create this AppContext. */ private final ClassLoader contextClassLoader; /** * Constructor for AppContext. This method is <i>not</i> public, nor should it ever be used as * such. The proper way to construct an AppContext is through the use of * SunToolkit.createNewAppContext. A ThreadGroup is created for the new AppContext, a Thread is * created within that ThreadGroup, and that Thread calls SunToolkit.createNewAppContext before * calling anything else. That creates both the new AppContext and its EventQueue. * * @param threadGroup The ThreadGroup for the new AppContext * @see sun.awt.SunToolkit * @since 1.2 */ AppContext(ThreadGroup threadGroup) { numAppContexts++; this.threadGroup = threadGroup; threadGroup2appContext.put(threadGroup, this); this.contextClassLoader = AccessController.doPrivileged( new PrivilegedAction<ClassLoader>() { public ClassLoader run() { return Thread.currentThread().getContextClassLoader(); } }); // Initialize push/pop lock and its condition to be used by all the // EventQueues within this AppContext Lock eventQueuePushPopLock = new ReentrantLock(); put(EVENT_QUEUE_LOCK_KEY, eventQueuePushPopLock); Condition eventQueuePushPopCond = eventQueuePushPopLock.newCondition(); put(EVENT_QUEUE_COND_KEY, eventQueuePushPopCond); } private static final ThreadLocal<AppContext> threadAppContext = new ThreadLocal<AppContext>(); /** * Returns the appropriate AppContext for the caller, as determined by its ThreadGroup. If the * main "system" AppContext would be returned and there's an AWTSecurityManager installed, it is * called to get the proper AppContext based on the execution context. * * @return the AppContext for the caller. * @see java.lang.ThreadGroup * @since 1.2 */ public static final AppContext getAppContext() { if (numAppContexts == 1) // If there's only one system-wide, return mainAppContext; // return the main system AppContext. AppContext appContext = threadAppContext.get(); if (null == appContext) { appContext = AccessController.doPrivileged( new PrivilegedAction<AppContext>() { public AppContext run() { // Get the current ThreadGroup, and look for it and its // parents in the hash from ThreadGroup to AppContext -- // it should be found, because we use createNewContext() // when new AppContext objects are created. ThreadGroup currentThreadGroup = Thread.currentThread().getThreadGroup(); ThreadGroup threadGroup = currentThreadGroup; AppContext context = threadGroup2appContext.get(threadGroup); while (context == null) { threadGroup = threadGroup.getParent(); if (threadGroup == null) { // If we get here, we're running under a ThreadGroup that // has no AppContext associated with it. This should never // happen, because createNewContext() should be used by the // toolkit to create the ThreadGroup that everything runs // under. throw new RuntimeException("Invalid ThreadGroup"); } context = threadGroup2appContext.get(threadGroup); } // In case we did anything in the above while loop, we add // all the intermediate ThreadGroups to threadGroup2appContext // so we won't spin again. for (ThreadGroup tg = currentThreadGroup; tg != threadGroup; tg = tg.getParent()) { threadGroup2appContext.put(tg, context); } // Now we're done, so we cache the latest key/value pair. // (we do this before checking with any AWTSecurityManager, so if // this Thread equates with the main AppContext in the cache, it // still will) threadAppContext.set(context); return context; } }); } if (appContext == mainAppContext) { // Before we return the main "system" AppContext, check to // see if there's an AWTSecurityManager installed. If so, // allow it to choose the AppContext to return. SecurityManager securityManager = System.getSecurityManager(); if ((securityManager != null) && (securityManager instanceof AWTSecurityManager)) { AWTSecurityManager awtSecMgr = (AWTSecurityManager) securityManager; AppContext secAppContext = awtSecMgr.getAppContext(); if (secAppContext != null) { appContext = secAppContext; // Return what we're told } } } return appContext; } /** * Returns the main ("system") AppContext. * * @return the main AppContext * @since 1.8 */ static final AppContext getMainAppContext() { return mainAppContext; } private long DISPOSAL_TIMEOUT = 5000; // Default to 5-second timeout // for disposal of all Frames // (we wait for this time twice, // once for dispose(), and once // to clear the EventQueue). private long THREAD_INTERRUPT_TIMEOUT = 1000; // Default to 1-second timeout for all // interrupted Threads to exit, and another // 1 second for all stopped Threads to die. /** * Disposes of this AppContext, all of its top-level Frames, and all Threads and ThreadGroups * contained within it. * * <p>This method must be called from a Thread which is not contained within this AppContext. * * @exception IllegalThreadStateException if the current thread is contained within this * AppContext * @since 1.2 */ public void dispose() throws IllegalThreadStateException { // Check to be sure that the current Thread isn't in this AppContext if (this.threadGroup.parentOf(Thread.currentThread().getThreadGroup())) { throw new IllegalThreadStateException( "Current Thread is contained within AppContext to be disposed."); } synchronized (this) { if (this.isDisposed) { return; // If already disposed, bail. } this.isDisposed = true; } final PropertyChangeSupport changeSupport = this.changeSupport; if (changeSupport != null) { changeSupport.firePropertyChange(DISPOSED_PROPERTY_NAME, false, true); } // First, we post an InvocationEvent to be run on the // EventDispatchThread which disposes of all top-level Frames and TrayIcons final Object notificationLock = new Object(); Runnable runnable = new Runnable() { public void run() { // Alert PropertyChangeListeners that the GUI has been disposed. if (changeSupport != null) { changeSupport.firePropertyChange(GUI_DISPOSED, false, true); } synchronized (notificationLock) { notificationLock.notifyAll(); // Notify caller that we're done } } }; synchronized (notificationLock) { SunToolkit.postEvent(this, new InvocationEvent(Toolkit.getDefaultToolkit(), runnable)); try { notificationLock.wait(DISPOSAL_TIMEOUT); } catch (InterruptedException e) { } } // Next, we post another InvocationEvent to the end of the // EventQueue. When it's executed, we know we've executed all // events in the queue. runnable = new Runnable() { public void run() { synchronized (notificationLock) { notificationLock.notifyAll(); // Notify caller that we're done } } }; synchronized (notificationLock) { SunToolkit.postEvent(this, new InvocationEvent(Toolkit.getDefaultToolkit(), runnable)); try { notificationLock.wait(DISPOSAL_TIMEOUT); } catch (InterruptedException e) { } } // Next, we interrupt all Threads in the ThreadGroup this.threadGroup.interrupt(); // Note, the EventDispatchThread we've interrupted may dump an // InterruptedException to the console here. This needs to be // fixed in the EventDispatchThread, not here. // Next, we sleep 10ms at a time, waiting for all of the active // Threads in the ThreadGroup to exit. long startTime = System.currentTimeMillis(); long endTime = startTime + THREAD_INTERRUPT_TIMEOUT; while ((this.threadGroup.activeCount() > 0) && (System.currentTimeMillis() < endTime)) { try { Thread.sleep(10); } catch (InterruptedException e) { } } // Then, we stop any remaining Threads this.threadGroup.stop(); // Next, we sleep 10ms at a time, waiting for all of the active // Threads in the ThreadGroup to die. startTime = System.currentTimeMillis(); endTime = startTime + THREAD_INTERRUPT_TIMEOUT; while ((this.threadGroup.activeCount() > 0) && (System.currentTimeMillis() < endTime)) { try { Thread.sleep(10); } catch (InterruptedException e) { } } // Next, we remove this and all subThreadGroups from threadGroup2appContext int numSubGroups = this.threadGroup.activeGroupCount(); if (numSubGroups > 0) { ThreadGroup[] subGroups = new ThreadGroup[numSubGroups]; numSubGroups = this.threadGroup.enumerate(subGroups); for (int subGroup = 0; subGroup < numSubGroups; subGroup++) { threadGroup2appContext.remove(subGroups[subGroup]); } } threadGroup2appContext.remove(this.threadGroup); threadAppContext.set(null); // Finally, we destroy the ThreadGroup entirely. try { this.threadGroup.destroy(); } catch (IllegalThreadStateException e) { // Fired if not all the Threads died, ignore it and proceed } synchronized (table) { this.table.clear(); // Clear out the Hashtable to ease garbage collection } numAppContexts--; mostRecentKeyValue = null; } static final class PostShutdownEventRunnable implements Runnable { private final AppContext appContext; public PostShutdownEventRunnable(AppContext ac) { appContext = ac; } public void run() { final EventQueue eq = (EventQueue) appContext.get(EVENT_QUEUE_KEY); if (eq != null) { eq.postEvent(AWTAutoShutdown.getShutdownEvent()); } } } static final class CreateThreadAction implements PrivilegedAction { private final AppContext appContext; private final Runnable runnable; public CreateThreadAction(AppContext ac, Runnable r) { appContext = ac; runnable = r; } public Object run() { Thread t = new Thread(appContext.getThreadGroup(), runnable); t.setContextClassLoader(appContext.getContextClassLoader()); t.setPriority(Thread.NORM_PRIORITY + 1); t.setDaemon(true); return t; } } static void stopEventDispatchThreads() { for (AppContext appContext : getAppContexts()) { if (appContext.isDisposed()) { continue; } Runnable r = new PostShutdownEventRunnable(appContext); // For security reasons EventQueue.postEvent should only be called // on a thread that belongs to the corresponding thread group. if (appContext != AppContext.getAppContext()) { // Create a thread that belongs to the thread group associated // with the AppContext and invokes EventQueue.postEvent. PrivilegedAction action = new CreateThreadAction(appContext, r); Thread thread = (Thread) AccessController.doPrivileged(action); thread.start(); } else { r.run(); } } } private MostRecentKeyValue mostRecentKeyValue = null; private MostRecentKeyValue shadowMostRecentKeyValue = null; /** * Returns the value to which the specified key is mapped in this context. * * @param key a key in the AppContext. * @return the value to which the key is mapped in this AppContext; <code>null</code> if the key * is not mapped to any value. * @see #put(Object, Object) * @since 1.2 */ public Object get(Object key) { /* * The most recent reference should be updated inside a synchronized * block to avoid a race when put() and get() are executed in * parallel on different threads. */ synchronized (table) { // Note: this most recent key/value caching is thread-hot. // A simple test using SwingSet found that 72% of lookups // were matched using the most recent key/value. By instantiating // a simple MostRecentKeyValue object on cache misses, the // cache hits can be processed without synchronization. MostRecentKeyValue recent = mostRecentKeyValue; if ((recent != null) && (recent.key == key)) { return recent.value; } Object value = table.get(key); if (mostRecentKeyValue == null) { mostRecentKeyValue = new MostRecentKeyValue(key, value); shadowMostRecentKeyValue = new MostRecentKeyValue(key, value); } else { MostRecentKeyValue auxKeyValue = mostRecentKeyValue; shadowMostRecentKeyValue.setPair(key, value); mostRecentKeyValue = shadowMostRecentKeyValue; shadowMostRecentKeyValue = auxKeyValue; } return value; } } /** * Maps the specified <code>key</code> to the specified <code>value</code> in this AppContext. * Neither the key nor the value can be <code>null</code>. * * <p>The value can be retrieved by calling the <code>get</code> method with a key that is equal * to the original key. * * @param key the AppContext key. * @param value the value. * @return the previous value of the specified key in this AppContext, or <code>null</code> if it * did not have one. * @exception NullPointerException if the key or value is <code>null</code>. * @see #get(Object) * @since 1.2 */ public Object put(Object key, Object value) { synchronized (table) { MostRecentKeyValue recent = mostRecentKeyValue; if ((recent != null) && (recent.key == key)) recent.value = value; return table.put(key, value); } } /** * Removes the key (and its corresponding value) from this AppContext. This method does nothing if * the key is not in the AppContext. * * @param key the key that needs to be removed. * @return the value to which the key had been mapped in this AppContext, or <code>null</code> if * the key did not have a mapping. * @since 1.2 */ public Object remove(Object key) { synchronized (table) { MostRecentKeyValue recent = mostRecentKeyValue; if ((recent != null) && (recent.key == key)) recent.value = null; return table.remove(key); } } /** * Returns the root ThreadGroup for all Threads contained within this AppContext. * * @since 1.2 */ public ThreadGroup getThreadGroup() { return threadGroup; } /** * Returns the context ClassLoader that was used to create this AppContext. * * @see java.lang.Thread#getContextClassLoader */ public ClassLoader getContextClassLoader() { return contextClassLoader; } /** * Returns a string representation of this AppContext. * * @since 1.2 */ @Override public String toString() { return getClass().getName() + "[threadGroup=" + threadGroup.getName() + "]"; } /** * Returns an array of all the property change listeners registered on this component. * * @return all of this component's <code>PropertyChangeListener</code>s or an empty array if no * property change listeners are currently registered * @see #addPropertyChangeListener * @see #removePropertyChangeListener * @see #getPropertyChangeListeners(java.lang.String) * @see java.beans.PropertyChangeSupport#getPropertyChangeListeners * @since 1.4 */ public synchronized PropertyChangeListener[] getPropertyChangeListeners() { if (changeSupport == null) { return new PropertyChangeListener[0]; } return changeSupport.getPropertyChangeListeners(); } /** * Adds a PropertyChangeListener to the listener list for a specific property. The specified * property may be one of the following: * * <ul> * <li>if this AppContext is disposed ("disposed") * </ul> * * <ul> * <li>if this AppContext's unowned Windows have been disposed ("guidisposed"). Code to cleanup * after the GUI is disposed (such as LookAndFeel.uninitialize()) should execute in response * to this property being fired. Notifications for the "guidisposed" property are sent on * the event dispatch thread. * </ul> * * <p>If listener is null, no exception is thrown and no action is performed. * * @param propertyName one of the property names listed above * @param listener the PropertyChangeListener to be added * @see #removePropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener) * @see #getPropertyChangeListeners(java.lang.String) * @see #addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener) */ public synchronized void addPropertyChangeListener( String propertyName, PropertyChangeListener listener) { if (listener == null) { return; } if (changeSupport == null) { changeSupport = new PropertyChangeSupport(this); } changeSupport.addPropertyChangeListener(propertyName, listener); } /** * Removes a PropertyChangeListener from the listener list for a specific property. This method * should be used to remove PropertyChangeListeners that were registered for a specific bound * property. * * <p>If listener is null, no exception is thrown and no action is performed. * * @param propertyName a valid property name * @param listener the PropertyChangeListener to be removed * @see #addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener) * @see #getPropertyChangeListeners(java.lang.String) * @see #removePropertyChangeListener(java.beans.PropertyChangeListener) */ public synchronized void removePropertyChangeListener( String propertyName, PropertyChangeListener listener) { if (listener == null || changeSupport == null) { return; } changeSupport.removePropertyChangeListener(propertyName, listener); } /** * Returns an array of all the listeners which have been associated with the named property. * * @return all of the <code>PropertyChangeListeners</code> associated with the named property or * an empty array if no listeners have been added * @see #addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener) * @see #removePropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener) * @see #getPropertyChangeListeners * @since 1.4 */ public synchronized PropertyChangeListener[] getPropertyChangeListeners(String propertyName) { if (changeSupport == null) { return new PropertyChangeListener[0]; } return changeSupport.getPropertyChangeListeners(propertyName); } }
/** * This class implements window which serves as content window for decorated frames. Its purpose to * provide correct events dispatching for the complex constructs such as decorated frames. * * <p>It should always be located at (- left inset, - top inset) in the associated decorated window. * So coordinates in it would be the same as java coordinates. */ public final class XContentWindow extends XWindow { private static PlatformLogger insLog = PlatformLogger.getLogger("sun.awt.X11.insets.XContentWindow"); static XContentWindow createContent(XDecoratedPeer parentFrame) { final WindowDimensions dims = parentFrame.getDimensions(); Rectangle rec = dims.getBounds(); // Fix for - set the location of the content window to the (-left inset, -top inset) Insets ins = dims.getInsets(); if (ins != null) { rec.x = -ins.left; rec.y = -ins.top; } else { rec.x = 0; rec.y = 0; } final XContentWindow cw = new XContentWindow(parentFrame, rec); cw.xSetVisible(true); return cw; } private final XDecoratedPeer parentFrame; // A list of expose events that come when the parentFrame is iconified private final java.util.List<SavedExposeEvent> iconifiedExposeEvents = new java.util.ArrayList<SavedExposeEvent>(); private XContentWindow(XDecoratedPeer parentFrame, Rectangle bounds) { super((Component) parentFrame.getTarget(), parentFrame.getShell(), bounds); this.parentFrame = parentFrame; } void preInit(XCreateWindowParams params) { super.preInit(params); params.putIfNull(BIT_GRAVITY, Integer.valueOf(XConstants.NorthWestGravity)); Long eventMask = (Long) params.get(EVENT_MASK); if (eventMask != null) { eventMask = eventMask & ~(XConstants.StructureNotifyMask); params.put(EVENT_MASK, eventMask); } } protected String getWMName() { return "Content window"; } protected boolean isEventDisabled(XEvent e) { switch (e.get_type()) { // Override parentFrame to receive MouseEnter/Exit case XConstants.EnterNotify: case XConstants.LeaveNotify: return false; // We handle ConfigureNotify specifically in XDecoratedPeer case XConstants.ConfigureNotify: return true; // We don't want SHOWN/HIDDEN on content window since it will duplicate XDecoratedPeer case XConstants.MapNotify: case XConstants.UnmapNotify: return true; default: return super.isEventDisabled(e) || parentFrame.isEventDisabled(e); } } // Coordinates are that of the shell void setContentBounds(WindowDimensions dims) { XToolkit.awtLock(); try { // Bounds of content window are of the same size as bounds of Java window and with // location as -(insets) Rectangle newBounds = dims.getBounds(); Insets in = dims.getInsets(); if (in != null) { newBounds.setLocation(-in.left, -in.top); } if (insLog.isLoggable(PlatformLogger.Level.FINE)) { insLog.fine("Setting content bounds {0}, old bounds {1}", newBounds, getBounds()); } // Fix for 5023533: // Change in the size of the content window means, well, change of the size // Change in the location of the content window means change in insets boolean needHandleResize = !(newBounds.equals(getBounds())); reshape(newBounds); if (needHandleResize) { insLog.fine("Sending RESIZED"); handleResize(newBounds); } } finally { XToolkit.awtUnlock(); } validateSurface(); } // NOTE: This method may be called by privileged threads. // DO NOT INVOKE CLIENT CODE ON THIS THREAD! public void handleResize(Rectangle bounds) { AWTAccessor.getComponentAccessor().setSize((Component) target, bounds.width, bounds.height); postEvent(new ComponentEvent(target, ComponentEvent.COMPONENT_RESIZED)); } public void handleExposeEvent(Component target, int x, int y, int w, int h) { // TODO: ? // get rid of 'istanceof' by subclassing: // XContentWindow -> XFrameContentWindow // Expose event(s) that result from deiconification // come before a deicinofication notification. // We reorder these events by saving all expose events // that come when the frame is iconified. Then we // actually handle saved expose events on deiconification. if (parentFrame instanceof XFramePeer && (((XFramePeer) parentFrame).getState() & java.awt.Frame.ICONIFIED) != 0) { // Save expose events if the frame is iconified // in order to handle them on deiconification. iconifiedExposeEvents.add(new SavedExposeEvent(target, x, y, w, h)); } else { // Normal case: [it is not a frame or] the frame is not iconified. super.handleExposeEvent(target, x, y, w, h); } } void purgeIconifiedExposeEvents() { for (SavedExposeEvent evt : iconifiedExposeEvents) { super.handleExposeEvent(evt.target, evt.x, evt.y, evt.w, evt.h); } iconifiedExposeEvents.clear(); } private static class SavedExposeEvent { Component target; int x, y, w, h; SavedExposeEvent(Component target, int x, int y, int w, int h) { this.target = target; this.x = x; this.y = y; this.w = w; this.h = h; } } public String toString() { return getClass().getName() + "[" + getBounds() + "]"; } }
public class LWWindowPeer extends LWContainerPeer<Window, JComponent> implements FramePeer, DialogPeer, FullScreenCapable, DisplayChangedListener, PlatformEventNotifier { public enum PeerType { SIMPLEWINDOW, FRAME, DIALOG, EMBEDDED_FRAME, VIEW_EMBEDDED_FRAME, LW_FRAME } private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.lwawt.focus.LWWindowPeer"); private final PlatformWindow platformWindow; private static final int MINIMUM_WIDTH = 1; private static final int MINIMUM_HEIGHT = 1; private Insets insets = new Insets(0, 0, 0, 0); private GraphicsDevice graphicsDevice; private GraphicsConfiguration graphicsConfig; private SurfaceData surfaceData; private final Object surfaceDataLock = new Object(); private volatile int windowState = Frame.NORMAL; // check that the mouse is over the window private volatile boolean isMouseOver = false; // A peer where the last mouse event came to. Used by cursor manager to // find the component under cursor private static volatile LWComponentPeer<?, ?> lastCommonMouseEventPeer; // A peer where the last mouse event came to. Used to generate // MOUSE_ENTERED/EXITED notifications private volatile LWComponentPeer<?, ?> lastMouseEventPeer; // Peers where all dragged/released events should come to, // depending on what mouse button is being dragged according to Cocoa private static final LWComponentPeer<?, ?>[] mouseDownTarget = new LWComponentPeer<?, ?>[3]; // A bitmask that indicates what mouse buttons produce MOUSE_CLICKED events // on MOUSE_RELEASE. Click events are only generated if there were no drag // events between MOUSE_PRESSED and MOUSE_RELEASED for particular button private static int mouseClickButtons = 0; private volatile boolean isOpaque = true; private static final Font DEFAULT_FONT = new Font("Lucida Grande", Font.PLAIN, 13); private static LWWindowPeer grabbingWindow; private volatile boolean skipNextFocusChange; private static final Color nonOpaqueBackground = new Color(0, 0, 0, 0); private volatile boolean textured; private final PeerType peerType; private final SecurityWarningWindow warningWindow; private volatile boolean targetFocusable; /** * Current modal blocker or null. * * <p>Synchronization: peerTreeLock. */ private LWWindowPeer blocker; public LWWindowPeer( Window target, PlatformComponent platformComponent, PlatformWindow platformWindow, PeerType peerType) { super(target, platformComponent); this.platformWindow = platformWindow; this.peerType = peerType; Window owner = target.getOwner(); LWWindowPeer ownerPeer = owner == null ? null : (LWWindowPeer) AWTAccessor.getComponentAccessor().getPeer(owner); PlatformWindow ownerDelegate = (ownerPeer != null) ? ownerPeer.getPlatformWindow() : null; // The delegate.initialize() needs a non-null GC on X11. GraphicsConfiguration gc = getTarget().getGraphicsConfiguration(); synchronized (getStateLock()) { // graphicsConfig should be updated according to the real window // bounds when the window is shown, see 4868278 this.graphicsConfig = gc; } if (!target.isFontSet()) { target.setFont(DEFAULT_FONT); } if (!target.isBackgroundSet()) { target.setBackground(SystemColor.window); } else { // first we check if user provided alpha for background. This is // similar to what Apple's Java do. // Since JDK7 we should rely on setOpacity() only. // this.opacity = c.getAlpha(); } 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. } platformWindow.initialize(target, this, ownerDelegate); // Init warning window(for applets) SecurityWarningWindow warn = null; if (target.getWarningString() != null) { // accessSystemTray permission allows to display TrayIcon, TrayIcon tooltip // and TrayIcon balloon windows without a warning window. if (!AWTAccessor.getWindowAccessor().isTrayIconWindow(target)) { LWToolkit toolkit = (LWToolkit) Toolkit.getDefaultToolkit(); warn = toolkit.createSecurityWarning(target, this); } } warningWindow = warn; } @Override void initializeImpl() { super.initializeImpl(); if (getTarget() instanceof Frame) { setTitle(((Frame) getTarget()).getTitle()); setState(((Frame) getTarget()).getExtendedState()); } else if (getTarget() instanceof Dialog) { setTitle(((Dialog) getTarget()).getTitle()); } updateAlwaysOnTopState(); updateMinimumSize(); updateFocusableWindowState(); final Shape shape = getTarget().getShape(); if (shape != null) { applyShape(Region.getInstance(shape, null)); } final float opacity = getTarget().getOpacity(); if (opacity < 1.0f) { setOpacity(opacity); } setOpaque(getTarget().isOpaque()); updateInsets(platformWindow.getInsets()); if (getSurfaceData() == null) { replaceSurfaceData(false); } activateDisplayListener(); } // Just a helper method @Override public PlatformWindow getPlatformWindow() { return platformWindow; } @Override protected LWWindowPeer getWindowPeerOrSelf() { return this; } // ---- PEER METHODS ---- // @Override protected void disposeImpl() { deactivateDisplayListener(); SurfaceData oldData = getSurfaceData(); synchronized (surfaceDataLock) { surfaceData = null; } if (oldData != null) { oldData.invalidate(); } if (isGrabbing()) { ungrab(); } if (warningWindow != null) { warningWindow.dispose(); } platformWindow.dispose(); super.disposeImpl(); } @Override protected void setVisibleImpl(final boolean visible) { if (!visible && warningWindow != null) { warningWindow.setVisible(false, false); } updateFocusableWindowState(); super.setVisibleImpl(visible); // TODO: update graphicsConfig, see 4868278 platformWindow.setVisible(visible); if (isSimpleWindow()) { KeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance(); if (visible) { if (!getTarget().isAutoRequestFocus()) { return; } else { requestWindowFocus(CausedFocusEvent.Cause.ACTIVATION); } // Focus the owner in case this window is focused. } else if (kfmPeer.getCurrentFocusedWindow() == getTarget()) { // Transfer focus to the owner. LWWindowPeer owner = getOwnerFrameDialog(LWWindowPeer.this); if (owner != null) { owner.requestWindowFocus(CausedFocusEvent.Cause.ACTIVATION); } } } } @Override public final GraphicsConfiguration getGraphicsConfiguration() { synchronized (getStateLock()) { return graphicsConfig; } } @Override public boolean updateGraphicsData(GraphicsConfiguration gc) { setGraphicsConfig(gc); return false; } protected final Graphics getOnscreenGraphics(Color fg, Color bg, Font f) { if (getSurfaceData() == null) { return null; } if (fg == null) { fg = SystemColor.windowText; } if (bg == null) { bg = SystemColor.window; } if (f == null) { f = DEFAULT_FONT; } return platformWindow.transformGraphics(new SunGraphics2D(getSurfaceData(), fg, bg, f)); } @Override public void setBounds(int x, int y, int w, int h, int op) { if ((op & NO_EMBEDDED_CHECK) == 0 && getPeerType() == PeerType.VIEW_EMBEDDED_FRAME) { return; } if ((op & SET_CLIENT_SIZE) != 0) { // SET_CLIENT_SIZE is only applicable to window peers, so handle it here // instead of pulling 'insets' field up to LWComponentPeer // no need to add insets since Window's notion of width and height includes insets. op &= ~SET_CLIENT_SIZE; op |= SET_SIZE; } // Don't post ComponentMoved/Resized and Paint events // until we've got a notification from the delegate Rectangle cb = constrainBounds(x, y, w, h); Rectangle newBounds = new Rectangle(getBounds()); if ((op & (SET_LOCATION | SET_BOUNDS)) != 0) { newBounds.x = cb.x; newBounds.y = cb.y; } if ((op & (SET_SIZE | SET_BOUNDS)) != 0) { newBounds.width = cb.width; newBounds.height = cb.height; } // Native system could constraint bounds, so the peer wold be updated in the callback platformWindow.setBounds(newBounds.x, newBounds.y, newBounds.width, newBounds.height); } public Rectangle constrainBounds(Rectangle bounds) { return constrainBounds(bounds.x, bounds.y, bounds.width, bounds.height); } public Rectangle constrainBounds(int x, int y, int w, int h) { if (w < MINIMUM_WIDTH) { w = MINIMUM_WIDTH; } if (h < MINIMUM_HEIGHT) { h = MINIMUM_HEIGHT; } final int maxW = getLWGC().getMaxTextureWidth(); final int maxH = getLWGC().getMaxTextureHeight(); if (w > maxW) { w = maxW; } if (h > maxH) { h = maxH; } return new Rectangle(x, y, w, h); } @Override public Point getLocationOnScreen() { return platformWindow.getLocationOnScreen(); } /** * Overridden from LWContainerPeer to return the correct insets. Insets are queried from the * delegate and are kept up to date by requiering when needed (i.e. when the window geometry is * changed). */ @Override public Insets getInsets() { synchronized (getStateLock()) { return insets; } } @Override public FontMetrics getFontMetrics(Font f) { // TODO: check for "use platform metrics" settings return platformWindow.getFontMetrics(f); } @Override public void toFront() { platformWindow.toFront(); } @Override public void toBack() { platformWindow.toBack(); } @Override public void setZOrder(ComponentPeer above) { throw new RuntimeException("not implemented"); } @Override public void updateAlwaysOnTopState() { platformWindow.setAlwaysOnTop(getTarget().isAlwaysOnTop()); } @Override public void updateFocusableWindowState() { targetFocusable = getTarget().isFocusableWindow(); platformWindow.updateFocusableWindowState(); } @Override public void setModalBlocked(Dialog blocker, boolean blocked) { synchronized (getPeerTreeLock()) { ComponentPeer peer = AWTAccessor.getComponentAccessor().getPeer(blocker); if (blocked && (peer instanceof LWWindowPeer)) { this.blocker = (LWWindowPeer) peer; } else { this.blocker = null; } } platformWindow.setModalBlocked(blocked); } @Override public void updateMinimumSize() { final Dimension min; if (getTarget().isMinimumSizeSet()) { min = getTarget().getMinimumSize(); min.width = Math.max(min.width, MINIMUM_WIDTH); min.height = Math.max(min.height, MINIMUM_HEIGHT); } else { min = new Dimension(MINIMUM_WIDTH, MINIMUM_HEIGHT); } final Dimension max; if (getTarget().isMaximumSizeSet()) { max = getTarget().getMaximumSize(); max.width = Math.min(max.width, getLWGC().getMaxTextureWidth()); max.height = Math.min(max.height, getLWGC().getMaxTextureHeight()); } else { max = new Dimension(getLWGC().getMaxTextureWidth(), getLWGC().getMaxTextureHeight()); } platformWindow.setSizeConstraints(min.width, min.height, max.width, max.height); } @Override public void updateIconImages() { getPlatformWindow().updateIconImages(); } @Override public void setBackground(final Color c) { super.setBackground(c); updateOpaque(); } @Override public void setOpacity(float opacity) { getPlatformWindow().setOpacity(opacity); repaintPeer(); } @Override public final void setOpaque(final boolean isOpaque) { if (this.isOpaque != isOpaque) { this.isOpaque = isOpaque; updateOpaque(); } } private void updateOpaque() { getPlatformWindow().setOpaque(!isTranslucent()); replaceSurfaceData(false); repaintPeer(); } @Override public void updateWindow() {} public final boolean isTextured() { return textured; } public final void setTextured(final boolean isTextured) { textured = isTextured; } @Override public final boolean isTranslucent() { synchronized (getStateLock()) { /* * Textured window is a special case of translucent window. * The difference is only in nswindow background. So when we set * texture property our peer became fully translucent. It doesn't * fill background, create non opaque backbuffers and layer etc. */ return !isOpaque || isShaped() || isTextured(); } } @Override final void applyShapeImpl(final Region shape) { super.applyShapeImpl(shape); updateOpaque(); } @Override public void repositionSecurityWarning() { if (warningWindow != null) { AWTAccessor.ComponentAccessor compAccessor = AWTAccessor.getComponentAccessor(); Window target = getTarget(); 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); } } // ---- FRAME PEER METHODS ---- // @Override // FramePeer and DialogPeer public void setTitle(String title) { platformWindow.setTitle(title == null ? "" : title); } @Override public void setMenuBar(MenuBar mb) { platformWindow.setMenuBar(mb); } @Override // FramePeer and DialogPeer public void setResizable(boolean resizable) { platformWindow.setResizable(resizable); } @Override public void setState(int state) { platformWindow.setWindowState(state); } @Override public int getState() { return windowState; } @Override public void setMaximizedBounds(Rectangle bounds) { // TODO: not implemented } @Override public void setBoundsPrivate(int x, int y, int width, int height) { setBounds(x, y, width, height, SET_BOUNDS | NO_EMBEDDED_CHECK); } @Override public Rectangle getBoundsPrivate() { throw new RuntimeException("not implemented"); } // ---- DIALOG PEER METHODS ---- // @Override public void blockWindows(List<Window> windows) { // TODO: LWX will probably need some collectJavaToplevels to speed this up for (Window w : windows) { WindowPeer wp = (WindowPeer) AWTAccessor.getComponentAccessor().getPeer(w); if (wp != null) { wp.setModalBlocked((Dialog) getTarget(), true); } } } // ---- PEER NOTIFICATIONS ---- // @Override public void notifyIconify(boolean iconify) { // The toplevel target is Frame and states are applicable to it. // Otherwise, the target is Window and it don't have state property. // Hopefully, no such events are posted in the queue so consider the // target as Frame in all cases. // REMIND: should we send it anyway if the state not changed since last // time? WindowEvent iconifyEvent = new WindowEvent( getTarget(), iconify ? WindowEvent.WINDOW_ICONIFIED : WindowEvent.WINDOW_DEICONIFIED); postEvent(iconifyEvent); int newWindowState = iconify ? Frame.ICONIFIED : Frame.NORMAL; postWindowStateChangedEvent(newWindowState); // REMIND: RepaintManager doesn't repaint iconified windows and // hence ignores any repaint request during deiconification. // So, we need to repaint window explicitly when it becomes normal. if (!iconify) { repaintPeer(); } } @Override public void notifyZoom(boolean isZoomed) { int newWindowState = isZoomed ? Frame.MAXIMIZED_BOTH : Frame.NORMAL; postWindowStateChangedEvent(newWindowState); } /** Called by the {@code PlatformWindow} when any part of the window should be repainted. */ @Override public void notifyExpose(final Rectangle r) { repaintPeer(r); } /** * Called by the {@code PlatformWindow} when this window is moved/resized by user or window insets * are changed. There's no notifyReshape() in LWComponentPeer as the only components which could * be resized by user are top-level windows. */ @Override public void notifyReshape(int x, int y, int w, int h) { Rectangle oldBounds = getBounds(); final boolean invalid = updateInsets(platformWindow.getInsets()); final boolean moved = (x != oldBounds.x) || (y != oldBounds.y); final boolean resized = (w != oldBounds.width) || (h != oldBounds.height); // Check if anything changed if (!moved && !resized && !invalid) { return; } // First, update peer's bounds setBounds(x, y, w, h, SET_BOUNDS, false, false); // Second, update the graphics config and surface data final boolean isNewDevice = updateGraphicsDevice(); if (resized || isNewDevice) { replaceSurfaceData(); updateMinimumSize(); } // Third, COMPONENT_MOVED/COMPONENT_RESIZED/PAINT events if (moved || invalid) { handleMove(x, y, true); } if (resized || invalid || isNewDevice) { handleResize(w, h, true); repaintPeer(); } repositionSecurityWarning(); } private void clearBackground(final int w, final int h) { final Graphics g = getOnscreenGraphics(getForeground(), getBackground(), getFont()); if (g != null) { try { if (g instanceof Graphics2D) { ((Graphics2D) g).setComposite(AlphaComposite.Src); } if (isTranslucent()) { g.setColor(nonOpaqueBackground); g.fillRect(0, 0, w, h); } if (!isTextured()) { if (g instanceof SunGraphics2D) { ((SunGraphics2D) g).constrain(0, 0, w, h, getRegion()); } g.setColor(getBackground()); g.fillRect(0, 0, w, h); } } finally { g.dispose(); } } } @Override public void notifyUpdateCursor() { getLWToolkit().getCursorManager().updateCursorLater(this); } @Override public void notifyActivation(boolean activation, LWWindowPeer opposite) { Window oppositeWindow = (opposite == null) ? null : opposite.getTarget(); changeFocusedWindow(activation, oppositeWindow); } // MouseDown in non-client area @Override public void notifyNCMouseDown() { // Ungrab except for a click on a Dialog with the grabbing owner if (grabbingWindow != null && !grabbingWindow.isOneOfOwnersOf(this)) { grabbingWindow.ungrab(); } } // ---- EVENTS ---- // /* * Called by the delegate to dispatch the event to Java. Event * coordinates are relative to non-client window are, i.e. the top-left * point of the client area is (insets.top, insets.left). */ @Override public void notifyMouseEvent( PlatformWindow eventPlatformWindow, int id, long when, int button, int x, int y, int screenX, int screenY, int modifiers, int clickCount, boolean popupTrigger, byte[] bdata) { // TODO: fill "bdata" member of AWTEvent Rectangle r = getBounds(); // findPeerAt() expects parent coordinates LWComponentPeer<?, ?> targetPeer = findPeerAt(r.x + x, r.y + y); if (id == MouseEvent.MOUSE_EXITED) { isMouseOver = false; if (lastMouseEventPeer != null) { if (lastMouseEventPeer.isEnabled()) { Point lp = lastMouseEventPeer.windowToLocal(x, y, this); Component target = lastMouseEventPeer.getTarget(); postMouseExitedEvent( target, when, modifiers, lp, screenX, screenY, clickCount, popupTrigger, button); } // Sometimes we may get MOUSE_EXITED after lastCommonMouseEventPeer is switched // to a peer from another window. So we must first check if this peer is // the same as lastWindowPeer if (lastCommonMouseEventPeer != null && lastCommonMouseEventPeer.getWindowPeerOrSelf() == this) { lastCommonMouseEventPeer = null; } lastMouseEventPeer = null; } } else if (id == MouseEvent.MOUSE_ENTERED) { isMouseOver = true; if (targetPeer != null) { if (targetPeer.isEnabled()) { Point lp = targetPeer.windowToLocal(x, y, this); Component target = targetPeer.getTarget(); postMouseEnteredEvent( target, when, modifiers, lp, screenX, screenY, clickCount, popupTrigger, button); } lastCommonMouseEventPeer = targetPeer; lastMouseEventPeer = targetPeer; } } else { LWWindowPeer topmostWindowPeer = eventPlatformWindow != null ? eventPlatformWindow.getPeer() : null; // topmostWindowPeer == null condition is added for the backward // compatibility with applets. It can be removed when the // getTopmostPlatformWindowUnderMouse() method will be properly // implemented in CPlatformEmbeddedFrame class if (topmostWindowPeer == this || topmostWindowPeer == null) { generateMouseEnterExitEventsForComponents( when, button, x, y, screenX, screenY, modifiers, clickCount, popupTrigger, targetPeer); } else { LWComponentPeer<?, ?> topmostTargetPeer = topmostWindowPeer.findPeerAt(r.x + x, r.y + y); topmostWindowPeer.generateMouseEnterExitEventsForComponents( when, button, x, y, screenX, screenY, modifiers, clickCount, popupTrigger, topmostTargetPeer); } // TODO: fill "bdata" member of AWTEvent int eventButtonMask = (button > 0) ? MouseEvent.getMaskForButton(button) : 0; int otherButtonsPressed = modifiers & ~eventButtonMask; // For pressed/dragged/released events OS X treats other // mouse buttons as if they were BUTTON2, so we do the same int targetIdx = (button > 3) ? MouseEvent.BUTTON2 - 1 : button - 1; // MOUSE_ENTERED/EXITED are generated for the components strictly under // mouse even when dragging. That's why we first update lastMouseEventPeer // based on initial targetPeer value and only then recalculate targetPeer // for MOUSE_DRAGGED/RELEASED events if (id == MouseEvent.MOUSE_PRESSED) { // Ungrab only if this window is not an owned window of the grabbing one. if (!isGrabbing() && grabbingWindow != null && !grabbingWindow.isOneOfOwnersOf(this)) { grabbingWindow.ungrab(); } if (otherButtonsPressed == 0) { mouseClickButtons = eventButtonMask; } else { mouseClickButtons |= eventButtonMask; } // The window should be focused on mouse click. If it gets activated by the native platform, // this request will be no op. It will take effect when: // 1. A simple not focused window is clicked. // 2. An active but not focused owner frame/dialog is clicked. // The mouse event then will trigger a focus request "in window" to the component, so the // window // should gain focus before. requestWindowFocus(CausedFocusEvent.Cause.MOUSE_EVENT); mouseDownTarget[targetIdx] = targetPeer; } else if (id == MouseEvent.MOUSE_DRAGGED) { // Cocoa dragged event has the information about which mouse // button is being dragged. Use it to determine the peer that // should receive the dragged event. targetPeer = mouseDownTarget[targetIdx]; mouseClickButtons &= ~modifiers; } else if (id == MouseEvent.MOUSE_RELEASED) { // TODO: currently, mouse released event goes to the same component // that received corresponding mouse pressed event. For most cases, // it's OK, however, we need to make sure that our behavior is consistent // with 1.6 for cases where component in question have been // hidden/removed in between of mouse pressed/released events. targetPeer = mouseDownTarget[targetIdx]; if ((modifiers & eventButtonMask) == 0) { mouseDownTarget[targetIdx] = null; } // mouseClickButtons is updated below, after MOUSE_CLICK is sent } if (targetPeer == null) { // TODO This can happen if this window is invisible. this is correct behavior in this case? targetPeer = this; } Point lp = targetPeer.windowToLocal(x, y, this); if (targetPeer.isEnabled()) { MouseEvent event = new MouseEvent( targetPeer.getTarget(), id, when, modifiers, lp.x, lp.y, screenX, screenY, clickCount, popupTrigger, button); postEvent(event); } if (id == MouseEvent.MOUSE_RELEASED) { if ((mouseClickButtons & eventButtonMask) != 0 && targetPeer.isEnabled()) { postEvent( new MouseEvent( targetPeer.getTarget(), MouseEvent.MOUSE_CLICKED, when, modifiers, lp.x, lp.y, screenX, screenY, clickCount, popupTrigger, button)); } mouseClickButtons &= ~eventButtonMask; } } notifyUpdateCursor(); } private void generateMouseEnterExitEventsForComponents( long when, int button, int x, int y, int screenX, int screenY, int modifiers, int clickCount, boolean popupTrigger, final LWComponentPeer<?, ?> targetPeer) { if (!isMouseOver || targetPeer == lastMouseEventPeer) { return; } // Generate Mouse Exit for components if (lastMouseEventPeer != null && lastMouseEventPeer.isEnabled()) { Point oldp = lastMouseEventPeer.windowToLocal(x, y, this); Component target = lastMouseEventPeer.getTarget(); postMouseExitedEvent( target, when, modifiers, oldp, screenX, screenY, clickCount, popupTrigger, button); } lastCommonMouseEventPeer = targetPeer; lastMouseEventPeer = targetPeer; // Generate Mouse Enter for components if (targetPeer != null && targetPeer.isEnabled()) { Point newp = targetPeer.windowToLocal(x, y, this); Component target = targetPeer.getTarget(); postMouseEnteredEvent( target, when, modifiers, newp, screenX, screenY, clickCount, popupTrigger, button); } } private void postMouseEnteredEvent( Component target, long when, int modifiers, Point loc, int xAbs, int yAbs, int clickCount, boolean popupTrigger, int button) { updateSecurityWarningVisibility(); postEvent( new MouseEvent( target, MouseEvent.MOUSE_ENTERED, when, modifiers, loc.x, loc.y, xAbs, yAbs, clickCount, popupTrigger, button)); } private void postMouseExitedEvent( Component target, long when, int modifiers, Point loc, int xAbs, int yAbs, int clickCount, boolean popupTrigger, int button) { updateSecurityWarningVisibility(); postEvent( new MouseEvent( target, MouseEvent.MOUSE_EXITED, when, modifiers, loc.x, loc.y, xAbs, yAbs, clickCount, popupTrigger, button)); } @Override public void notifyMouseWheelEvent( long when, int x, int y, int modifiers, int scrollType, int scrollAmount, int wheelRotation, double preciseWheelRotation, byte[] bdata) { // TODO: could we just use the last mouse event target here? Rectangle r = getBounds(); // findPeerAt() expects parent coordinates final LWComponentPeer<?, ?> targetPeer = findPeerAt(r.x + x, r.y + y); if (targetPeer == null || !targetPeer.isEnabled()) { return; } Point lp = targetPeer.windowToLocal(x, y, this); // TODO: fill "bdata" member of AWTEvent // TODO: screenX/screenY postEvent( new MouseWheelEvent( targetPeer.getTarget(), MouseEvent.MOUSE_WHEEL, when, modifiers, lp.x, lp.y, 0, 0, /* screenX, Y */ 0 /* clickCount */, false /* popupTrigger */, scrollType, scrollAmount, wheelRotation, preciseWheelRotation)); } /* * Called by the delegate when a key is pressed. */ @Override public void notifyKeyEvent( int id, long when, int modifiers, int keyCode, char keyChar, int keyLocation) { LWKeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance(); Component focusOwner = kfmPeer.getCurrentFocusOwner(); if (focusOwner == null) { focusOwner = kfmPeer.getCurrentFocusedWindow(); if (focusOwner == null) { focusOwner = this.getTarget(); } } KeyEvent keyEvent = new KeyEvent(focusOwner, id, when, modifiers, keyCode, keyChar, keyLocation); AWTAccessor.getKeyEventAccessor() .setExtendedKeyCode( keyEvent, (keyChar == KeyEvent.CHAR_UNDEFINED) ? keyCode : ExtendedKeyCodes.getExtendedKeyCodeForChar(keyChar)); postEvent(keyEvent); } // ---- UTILITY METHODS ---- // private void activateDisplayListener() { final GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); ((SunGraphicsEnvironment) ge).addDisplayChangedListener(this); } private void deactivateDisplayListener() { final GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); ((SunGraphicsEnvironment) ge).removeDisplayChangedListener(this); } private void postWindowStateChangedEvent(int newWindowState) { if (getTarget() instanceof Frame) { AWTAccessor.getFrameAccessor().setExtendedState((Frame) getTarget(), newWindowState); } WindowEvent stateChangedEvent = new WindowEvent(getTarget(), WindowEvent.WINDOW_STATE_CHANGED, windowState, newWindowState); postEvent(stateChangedEvent); windowState = newWindowState; updateSecurityWarningVisibility(); } private static int getGraphicsConfigScreen(GraphicsConfiguration gc) { // TODO: this method can be implemented in a more // efficient way by forwarding to the delegate GraphicsDevice gd = gc.getDevice(); GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice[] gds = ge.getScreenDevices(); for (int i = 0; i < gds.length; i++) { if (gds[i] == gd) { return i; } } // Should never happen if gc is a screen device config return 0; } /* * This method is called when window's graphics config is changed from * the app code (e.g. when the window is made non-opaque) or when * the window is moved to another screen by user. * * Returns true if the graphics config has been changed, false otherwise. */ private boolean setGraphicsConfig(GraphicsConfiguration gc) { synchronized (getStateLock()) { if (graphicsConfig == gc) { return false; } // If window's graphics config is changed from the app code, the // config correspond to the same device as before; when the window // is moved by user, graphicsDevice is updated in notifyReshape(). // In either case, there's nothing to do with screenOn here graphicsConfig = gc; } // SurfaceData is replaced later in updateGraphicsData() return true; } /** Returns true if the GraphicsDevice has been changed, false otherwise. */ public boolean updateGraphicsDevice() { GraphicsDevice newGraphicsDevice = platformWindow.getGraphicsDevice(); synchronized (getStateLock()) { if (graphicsDevice == newGraphicsDevice) { return false; } graphicsDevice = newGraphicsDevice; } final GraphicsConfiguration newGC = newGraphicsDevice.getDefaultConfiguration(); if (!setGraphicsConfig(newGC)) return false; SunToolkit.executeOnEventHandlerThread( getTarget(), new Runnable() { public void run() { AWTAccessor.getComponentAccessor().setGraphicsConfiguration(getTarget(), newGC); } }); return true; } @Override public final void displayChanged() { if (updateGraphicsDevice()) { updateMinimumSize(); } // Replace surface unconditionally, because internal state of the // GraphicsDevice could be changed. replaceSurfaceData(); repaintPeer(); } @Override public final void paletteChanged() { // components do not need to react to this event. } /* * May be called by delegate to provide SD to Java2D code. */ public SurfaceData getSurfaceData() { synchronized (surfaceDataLock) { return surfaceData; } } private void replaceSurfaceData() { replaceSurfaceData(true); } private void replaceSurfaceData(final boolean blit) { synchronized (surfaceDataLock) { final SurfaceData oldData = getSurfaceData(); surfaceData = platformWindow.replaceSurfaceData(); final Rectangle size = getSize(); if (getSurfaceData() != null && oldData != getSurfaceData()) { clearBackground(size.width, size.height); } if (blit) { blitSurfaceData(oldData, getSurfaceData()); } if (oldData != null && oldData != getSurfaceData()) { // TODO: drop oldData for D3D/WGL pipelines // This can only happen when this peer is being created oldData.flush(); } } flushOnscreenGraphics(); } private void blitSurfaceData(final SurfaceData src, final SurfaceData dst) { // TODO blit. proof-of-concept if (src != dst && src != null && dst != null && !(dst instanceof NullSurfaceData) && !(src instanceof NullSurfaceData) && src.getSurfaceType().equals(dst.getSurfaceType()) && src.getDefaultScale() == dst.getDefaultScale()) { final Rectangle size = src.getBounds(); final Blit blit = Blit.locate(src.getSurfaceType(), CompositeType.Src, dst.getSurfaceType()); if (blit != null) { blit.Blit(src, dst, AlphaComposite.Src, null, 0, 0, 0, 0, size.width, size.height); } } } /** * Request the window insets from the delegate and compares it with the current one. This method * is mostly called by the delegate, e.g. when the window state is changed and insets should be * recalculated. * * <p>This method may be called on the toolkit thread. */ public final boolean updateInsets(final Insets newInsets) { synchronized (getStateLock()) { if (insets.equals(newInsets)) { return false; } insets = newInsets; } return true; } public static LWWindowPeer getWindowUnderCursor() { return lastCommonMouseEventPeer != null ? lastCommonMouseEventPeer.getWindowPeerOrSelf() : null; } public static LWComponentPeer<?, ?> getPeerUnderCursor() { return lastCommonMouseEventPeer; } /* * Requests platform to set native focus on a frame/dialog. * In case of a simple window, triggers appropriate java focus change. */ public boolean requestWindowFocus(CausedFocusEvent.Cause cause) { if (focusLog.isLoggable(PlatformLogger.Level.FINE)) { focusLog.fine("requesting native focus to " + this); } if (!focusAllowedFor()) { focusLog.fine("focus is not allowed"); return false; } // if (platformWindow.rejectFocusRequest(cause)) { // return false; // } AppContext targetAppContext = AWTAccessor.getComponentAccessor().getAppContext(getTarget()); KeyboardFocusManager kfm = AWTAccessor.getKeyboardFocusManagerAccessor() .getCurrentKeyboardFocusManager(targetAppContext); Window currentActive = kfm.getActiveWindow(); Window opposite = LWKeyboardFocusManagerPeer.getInstance().getCurrentFocusedWindow(); // Make the owner active window. if (isSimpleWindow()) { focusLog.fine("This is a Simple Window."); LWWindowPeer owner = getOwnerFrameDialog(this); // If owner is not natively active, request native // activation on it w/o sending events up to java. if (owner != null && !owner.platformWindow.isActive()) { if (focusLog.isLoggable(PlatformLogger.Level.FINE)) { focusLog.fine("requesting native focus to the owner " + owner); } LWWindowPeer currentActivePeer = currentActive == null ? null : (LWWindowPeer) AWTAccessor.getComponentAccessor().getPeer(currentActive); // Ensure the opposite is natively active and suppress sending events. if (currentActivePeer != null && currentActivePeer.platformWindow.isActive()) { if (focusLog.isLoggable(PlatformLogger.Level.FINE)) { focusLog.fine("the opposite is " + currentActivePeer); } currentActivePeer.skipNextFocusChange = true; } owner.skipNextFocusChange = true; owner.platformWindow.requestWindowFocus(); } // DKFM will synthesize all the focus/activation events correctly. changeFocusedWindow(true, opposite); focusLog.fine("DKFM will synthesize all the focus/activation events correctly"); return true; // In case the toplevel is active but not focused, change focus directly, // as requesting native focus on it will not have effect. } else if (getTarget() == currentActive && !getTarget().hasFocus()) { changeFocusedWindow(true, opposite); focusLog.fine("toplevel is active but not focused, change focus directly"); return true; } focusLog.fine("platformWindow.requestWindowFocus()"); return platformWindow.requestWindowFocus(); } protected boolean focusAllowedFor() { Window window = getTarget(); // TODO: check if modal blocked boolean allowed = (getBlocker() == null) && window.isVisible() && window.isEnabled() && isFocusableWindow(); focusLog.fine( "Checking whether the focus is allowed [" + allowed + "] for " + window.getName() + "; blocker: " + ((getBlocker() == null) ? "null" : getBlocker().getClass().getName()) + "; window.isVisible: " + window.isVisible() + "; window.isEnabled: " + window.isEnabled() + "; isFocusableWindow: " + isFocusableWindow()); return allowed; } private boolean isFocusableWindow() { boolean focusable = targetFocusable; if (isSimpleWindow()) { LWWindowPeer ownerPeer = getOwnerFrameDialog(this); if (ownerPeer == null) { return false; } return focusable && ownerPeer.targetFocusable; } return focusable; } public boolean isSimpleWindow() { Window window = getTarget(); return !(window instanceof Dialog || window instanceof Frame); } @Override public void emulateActivation(boolean activate) { changeFocusedWindow(activate, null); } private boolean isOneOfOwnersOf(LWWindowPeer peer) { Window owner = (peer != null ? peer.getTarget().getOwner() : null); while (owner != null) { if ((LWWindowPeer) owner.getPeer() == this) { return true; } owner = owner.getOwner(); } return false; } /* * Changes focused window on java level. */ protected void changeFocusedWindow(boolean becomesFocused, Window opposite) { if (focusLog.isLoggable(PlatformLogger.Level.FINE)) { focusLog.fine((becomesFocused ? "gaining" : "loosing") + " focus window: " + this); } if (skipNextFocusChange) { focusLog.fine("skipping focus change"); skipNextFocusChange = false; return; } if (!isFocusableWindow() && becomesFocused) { focusLog.fine("the window is not focusable"); return; } if (becomesFocused) { synchronized (getPeerTreeLock()) { if (blocker != null) { if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) { focusLog.finest("the window is blocked by " + blocker); } return; } } } // Note, the method is not called: // - when the opposite (gaining focus) window is an owned/owner window. // - for a simple window in any case. if (!becomesFocused && (isGrabbing() || this.isOneOfOwnersOf(grabbingWindow))) { if (focusLog.isLoggable(PlatformLogger.Level.FINE)) { focusLog.fine("ungrabbing on " + grabbingWindow); } // ungrab a simple window if its owner looses activation. grabbingWindow.ungrab(); } KeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance(); kfmPeer.setCurrentFocusedWindow(becomesFocused ? getTarget() : null); int eventID = becomesFocused ? WindowEvent.WINDOW_GAINED_FOCUS : WindowEvent.WINDOW_LOST_FOCUS; WindowEvent windowEvent = new TimedWindowEvent(getTarget(), eventID, opposite, System.currentTimeMillis()); // TODO: wrap in SequencedEvent postEvent(windowEvent); } /* * Retrieves the owner of the peer. * Note: this method returns the owner which can be activated, (i.e. the instance * of Frame or Dialog may be returned). */ static LWWindowPeer getOwnerFrameDialog(LWWindowPeer peer) { Window owner = (peer != null ? peer.getTarget().getOwner() : null); while (owner != null && !(owner instanceof Frame || owner instanceof Dialog)) { owner = owner.getOwner(); } return owner == null ? null : (LWWindowPeer) AWTAccessor.getComponentAccessor().getPeer(owner); } /** Returns the foremost modal blocker of this window, or null. */ public LWWindowPeer getBlocker() { synchronized (getPeerTreeLock()) { LWWindowPeer blocker = this.blocker; if (blocker == null) { return null; } while (blocker.blocker != null) { blocker = blocker.blocker; } return blocker; } } @Override public void enterFullScreenMode() { platformWindow.enterFullScreenMode(); updateSecurityWarningVisibility(); } @Override public void exitFullScreenMode() { platformWindow.exitFullScreenMode(); updateSecurityWarningVisibility(); } public long getLayerPtr() { return getPlatformWindow().getLayerPtr(); } void grab() { if (grabbingWindow != null && !isGrabbing()) { grabbingWindow.ungrab(); } grabbingWindow = this; } final void ungrab(boolean doPost) { if (isGrabbing()) { grabbingWindow = null; if (doPost) { postEvent(new UngrabEvent(getTarget())); } } } void ungrab() { ungrab(true); } private boolean isGrabbing() { return this == grabbingWindow; } public PeerType getPeerType() { return peerType; } public void updateSecurityWarningVisibility() { if (warningWindow == null) { return; } if (!isVisible()) { return; // The warning window should already be hidden. } boolean show = false; if (!platformWindow.isFullScreenMode()) { if (isVisible()) { if (LWKeyboardFocusManagerPeer.getInstance().getCurrentFocusedWindow() == getTarget()) { show = true; } if (platformWindow.isUnderMouse() || warningWindow.isUnderMouse()) { show = true; } } } warningWindow.setVisible(show, true); } @Override public String toString() { return super.toString() + " [target is " + getTarget() + "]"; } }
public class XEmbeddedFramePeer extends XFramePeer { private static final PlatformLogger xembedLog = PlatformLogger.getLogger("sun.awt.X11.xembed.XEmbeddedFramePeer"); LinkedList<AWTKeyStroke> strokes; XEmbedClientHelper embedder; // Caution - can be null if XEmbed is not supported public XEmbeddedFramePeer(EmbeddedFrame target) { // Don't specify PARENT_WINDOW param here. Instead we reparent // this embedded frame peer to the proper parent window after // an XEventDispatcher is registered to handle XEmbed events super( new XCreateWindowParams( new Object[] { TARGET, target, VISIBLE, Boolean.TRUE, EMBEDDED, Boolean.TRUE })); } public void preInit(XCreateWindowParams params) { super.preInit(params); strokes = new LinkedList<AWTKeyStroke>(); if (supportsXEmbed()) { embedder = new XEmbedClientHelper(); } } void postInit(XCreateWindowParams params) { super.postInit(params); if (embedder != null) { // install X11 event dispatcher embedder.setClient(this); // reparent to XEmbed server embedder.install(); } else if (getParentWindowHandle() != 0) { XToolkit.awtLock(); try { XlibWrapper.XReparentWindow( XToolkit.getDisplay(), getWindow(), getParentWindowHandle(), 0, 0); } finally { XToolkit.awtUnlock(); } } } @Override public void dispose() { if (embedder != null) { // uninstall X11 event dispatcher embedder.setClient(null); } super.dispose(); } public void updateMinimumSize() {} protected String getWMName() { return "JavaEmbeddedFrame"; } final long getParentWindowHandle() { return ((XEmbeddedFrame) target).handle; } boolean supportsXEmbed() { return ((EmbeddedFrame) target).supportsXEmbed(); } public boolean requestWindowFocus(long time, boolean timeProvided) { // Should check for active state of host application if (embedder != null && embedder.isActive()) { xembedLog.fine("Requesting focus from embedding host"); return embedder.requestFocus(); } else { xembedLog.fine("Requesting focus from X"); return super.requestWindowFocus(time, timeProvided); } } protected void requestInitialFocus() { if (embedder != null && supportsXEmbed()) { embedder.requestFocus(); } else { super.requestInitialFocus(); } } protected boolean isEventDisabled(XEvent e) { if (embedder != null && embedder.isActive()) { switch (e.get_type()) { case XConstants.FocusIn: case XConstants.FocusOut: return true; } } return super.isEventDisabled(e); } public void handleConfigureNotifyEvent(XEvent xev) { assert (SunToolkit.isAWTLockHeldByCurrentThread()); XConfigureEvent xe = xev.get_xconfigure(); if (xembedLog.isLoggable(PlatformLogger.Level.FINE)) { xembedLog.fine(xe.toString()); } // fix for 5063031 // if we use super.handleConfigureNotifyEvent() we would get wrong // size and position because embedded frame really is NOT a decorated one checkIfOnNewScreen( toGlobal(new Rectangle(xe.get_x(), xe.get_y(), xe.get_width(), xe.get_height()))); Rectangle oldBounds = getBounds(); synchronized (getStateLock()) { x = xe.get_x(); y = xe.get_y(); width = xe.get_width(); height = xe.get_height(); dimensions.setClientSize(width, height); dimensions.setLocation(x, y); } if (!getLocation().equals(oldBounds.getLocation())) { handleMoved(dimensions); } reconfigureContentWindow(dimensions); } protected void traverseOutForward() { if (embedder != null && embedder.isActive()) { if (embedder.isApplicationActive()) { xembedLog.fine("Traversing out Forward"); embedder.traverseOutForward(); } } } protected void traverseOutBackward() { if (embedder != null && embedder.isActive()) { if (embedder.isApplicationActive()) { xembedLog.fine("Traversing out Backward"); embedder.traverseOutBackward(); } } } // don't use getLocationOnScreen() inherited from XDecoratedPeer public Point getLocationOnScreen() { XToolkit.awtLock(); try { return toGlobal(0, 0); } finally { XToolkit.awtUnlock(); } } // don't use getBounds() inherited from XDecoratedPeer public Rectangle getBounds() { return new Rectangle(x, y, width, height); } public void setBoundsPrivate(int x, int y, int width, int height) { setBounds(x, y, width, height, SET_BOUNDS | NO_EMBEDDED_CHECK); } public Rectangle getBoundsPrivate() { int x = 0, y = 0; int w = 0, h = 0; XWindowAttributes attr = new XWindowAttributes(); XToolkit.awtLock(); try { XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), getWindow(), attr.pData); x = attr.get_x(); y = attr.get_y(); w = attr.get_width(); h = attr.get_height(); } finally { XToolkit.awtUnlock(); } attr.dispose(); return new Rectangle(x, y, w, h); } void registerAccelerator(AWTKeyStroke stroke) { if (stroke == null) return; strokes.add(stroke); if (embedder != null && embedder.isActive()) { embedder.registerAccelerator(stroke, strokes.size() - 1); } } void unregisterAccelerator(AWTKeyStroke stroke) { if (stroke == null) return; if (embedder != null && embedder.isActive()) { int index = strokes.indexOf(stroke); embedder.unregisterAccelerator(index); } } void notifyStarted() { // Register accelerators if (embedder != null && embedder.isActive()) { int i = 0; Iterator<AWTKeyStroke> iter = strokes.iterator(); while (iter.hasNext()) { embedder.registerAccelerator(iter.next(), i++); } } // Now we know that the the embedder is an XEmbed server, so we // reregister the drop target to enable XDnD protocol support via // XEmbed. updateDropTarget(); } void notifyStopped() { if (embedder != null && embedder.isActive()) { for (int i = strokes.size() - 1; i >= 0; i--) { embedder.unregisterAccelerator(i); } } } long getFocusTargetWindow() { return getWindow(); } boolean isXEmbedActive() { return embedder != null && embedder.isActive(); } public int getAbsoluteX() { Point absoluteLoc = XlibUtil.translateCoordinates( getWindow(), XToolkit.getDefaultRootWindow(), new Point(0, 0)); return absoluteLoc != null ? absoluteLoc.x : 0; } public int getAbsoluteY() { Point absoluteLoc = XlibUtil.translateCoordinates( getWindow(), XToolkit.getDefaultRootWindow(), new Point(0, 0)); return absoluteLoc != null ? absoluteLoc.y : 0; } public int getWidth() { return width; } public int getHeight() { return height; } public Dimension getSize() { return new Dimension(width, height); } // override XWindowPeer's method to let the embedded frame to block // the containing window public void setModalBlocked(Dialog blocker, boolean blocked) { super.setModalBlocked(blocker, blocked); EmbeddedFrame frame = (EmbeddedFrame) target; frame.notifyModalBlocked(blocker, blocked); } public void synthesizeFocusInOut(boolean doFocus) { XFocusChangeEvent xev = new XFocusChangeEvent(); XToolkit.awtLock(); try { xev.set_type(doFocus ? FocusIn : FocusOut); xev.set_window(getFocusProxy().getWindow()); xev.set_mode(NotifyNormal); XlibWrapper.XSendEvent( XToolkit.getDisplay(), getFocusProxy().getWindow(), false, NoEventMask, xev.pData); } finally { XToolkit.awtUnlock(); xev.dispose(); } } }
public void put(URI uri, Map<String, List<String>> responseHeaders) throws IOException { // pre-condition check if (uri == null || responseHeaders == null) { throw new IllegalArgumentException("Argument is null"); } // if there's no default CookieStore, no need to remember any cookie if (cookieJar == null) return; PlatformLogger logger = PlatformLogger.getLogger("java.net.CookieManager"); for (String headerKey : responseHeaders.keySet()) { // RFC 2965 3.2.2, key must be 'Set-Cookie2' // we also accept 'Set-Cookie' here for backward compatibility if (headerKey == null || !(headerKey.equalsIgnoreCase("Set-Cookie2") || headerKey.equalsIgnoreCase("Set-Cookie"))) { continue; } for (String headerValue : responseHeaders.get(headerKey)) { try { List<HttpCookie> cookies; try { cookies = HttpCookie.parse(headerValue); } catch (IllegalArgumentException e) { // Bogus header, make an empty list and log the error cookies = java.util.Collections.EMPTY_LIST; if (logger.isLoggable(PlatformLogger.SEVERE)) { logger.severe("Invalid cookie for " + uri + ": " + headerValue); } } for (HttpCookie cookie : cookies) { if (cookie.getPath() == null) { // If no path is specified, then by default // the path is the directory of the page/doc String path = uri.getPath(); if (!path.endsWith("/")) { int i = path.lastIndexOf("/"); if (i > 0) { path = path.substring(0, i + 1); } else { path = "/"; } } cookie.setPath(path); } // As per RFC 2965, section 3.3.1: // Domain Defaults to the effective request-host. (Note that because // there is no dot at the beginning of effective request-host, // the default Domain can only domain-match itself.) if (cookie.getDomain() == null) { cookie.setDomain(uri.getHost()); } String ports = cookie.getPortlist(); if (ports != null) { int port = uri.getPort(); if (port == -1) { port = "https".equals(uri.getScheme()) ? 443 : 80; } if (ports.isEmpty()) { // Empty port list means this should be restricted // to the incoming URI port cookie.setPortlist("" + port); if (shouldAcceptInternal(uri, cookie)) { cookieJar.add(uri, cookie); } } else { // Only store cookies with a port list // IF the URI port is in that list, as per // RFC 2965 section 3.3.2 if (isInPortList(ports, port) && shouldAcceptInternal(uri, cookie)) { cookieJar.add(uri, cookie); } } } else { if (shouldAcceptInternal(uri, cookie)) { cookieJar.add(uri, cookie); } } } } catch (IllegalArgumentException e) { // invalid set-cookie header string // no-op } } } }
public class WWindowPeer extends WPanelPeer implements WindowPeer, DisplayChangedListener { private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.windows.WWindowPeer"); private static final PlatformLogger screenLog = PlatformLogger.getLogger("sun.awt.windows.screen.WWindowPeer"); // we can't use WDialogPeer as blocker may be an instance of WPrintDialogPeer that // extends WWindowPeer, not WDialogPeer private WWindowPeer modalBlocker = null; private boolean isOpaque; private TranslucentWindowPainter painter; /* * A key used for storing a list of active windows in AppContext. The value * is a list of windows, sorted by the time of activation: later a window is * activated, greater its index is in the list. */ private static final StringBuffer ACTIVE_WINDOWS_KEY = new StringBuffer("active_windows_list"); /* * Listener for 'activeWindow' KFM property changes. It is added to each * AppContext KFM. See ActiveWindowListener inner class below. */ private static PropertyChangeListener activeWindowListener = new ActiveWindowListener(); /* * The object is a listener for the AppContext.GUI_DISPOSED property. */ private static final PropertyChangeListener guiDisposedListener = new GuiDisposedListener(); /* * Called (on the Toolkit thread) before the appropriate * WindowStateEvent is posted to the EventQueue. */ private WindowListener windowListener; /** Initialize JNI field IDs */ private static native void initIDs(); static { initIDs(); } // WComponentPeer overrides @SuppressWarnings("unchecked") protected void disposeImpl() { AppContext appContext = SunToolkit.targetToAppContext(target); synchronized (appContext) { List<WWindowPeer> l = (List<WWindowPeer>) appContext.get(ACTIVE_WINDOWS_KEY); if (l != null) { l.remove(this); } } // Remove ourself from the Map of DisplayChangeListeners GraphicsConfiguration gc = getGraphicsConfiguration(); ((Win32GraphicsDevice) gc.getDevice()).removeDisplayChangedListener(this); synchronized (getStateLock()) { TranslucentWindowPainter currentPainter = painter; if (currentPainter != null) { currentPainter.flush(); // don't set the current one to null here; reduces the chances of // MT issues (like NPEs) } } super.disposeImpl(); } // WindowPeer implementation public void toFront() { updateFocusableWindowState(); _toFront(); } native void _toFront(); public native void toBack(); public native void setAlwaysOnTopNative(boolean value); public void setAlwaysOnTop(boolean value) { if ((value && ((Window) target).isVisible()) || !value) { setAlwaysOnTopNative(value); } } public void updateAlwaysOnTopState() { setAlwaysOnTop(((Window) target).isAlwaysOnTop()); } public void updateFocusableWindowState() { setFocusableWindow(((Window) target).isFocusableWindow()); } native void setFocusableWindow(boolean value); // FramePeer & DialogPeer partial shared implementation public void setTitle(String title) { // allow a null title to pass as an empty string. if (title == null) { title = ""; } _setTitle(title); } native void _setTitle(String title); public void setResizable(boolean resizable) { _setResizable(resizable); } public native void _setResizable(boolean resizable); // Toolkit & peer internals WWindowPeer(Window target) { super(target); } void initialize() { super.initialize(); updateInsets(insets_); Font f = ((Window) target).getFont(); if (f == null) { f = defaultFont; ((Window) target).setFont(f); setFont(f); } // Express our interest in display changes GraphicsConfiguration gc = getGraphicsConfiguration(); ((Win32GraphicsDevice) gc.getDevice()).addDisplayChangedListener(this); initActiveWindowsTracking((Window) target); updateIconImages(); Shape shape = ((Window) target).getShape(); if (shape != null) { applyShape(Region.getInstance(shape, null)); } float opacity = ((Window) target).getOpacity(); if (opacity < 1.0f) { setOpacity(opacity); } synchronized (getStateLock()) { // default value of a boolean field is 'false', so set isOpaque to // true here explicitly this.isOpaque = true; setOpaque(((Window) target).isOpaque()); } } native void createAwtWindow(WComponentPeer parent); private volatile Window.Type windowType = Window.Type.NORMAL; // This method must be called for Window, Dialog, and Frame before creating // the hwnd void preCreate(WComponentPeer parent) { windowType = ((Window) target).getType(); } void create(WComponentPeer parent) { preCreate(parent); createAwtWindow(parent); } @Override final WComponentPeer getNativeParent() { final Container owner = ((Window) target).getOwner(); return (WComponentPeer) WToolkit.targetToPeer(owner); } // should be overriden in WDialogPeer protected void realShow() { super.show(); } public void show() { updateFocusableWindowState(); boolean alwaysOnTop = ((Window) target).isAlwaysOnTop(); // Fix for 4868278. // If we create a window with a specific GraphicsConfig, and then move it with // setLocation() or setBounds() to another one before its peer has been created, // then calling Window.getGraphicsConfig() returns wrong config. That may lead // to some problems like wrong-placed tooltips. It is caused by calling // super.displayChanged() in WWindowPeer.displayChanged() regardless of whether // GraphicsDevice was really changed, or not. So we need to track it here. updateGC(); realShow(); updateMinimumSize(); if (((Window) target).isAlwaysOnTopSupported() && alwaysOnTop) { setAlwaysOnTop(alwaysOnTop); } synchronized (getStateLock()) { if (!isOpaque) { updateWindow(true); } } } // Synchronize the insets members (here & in helper) with actual window // state. native void updateInsets(Insets i); static native int getSysMinWidth(); static native int getSysMinHeight(); static native int getSysIconWidth(); static native int getSysIconHeight(); static native int getSysSmIconWidth(); static native int getSysSmIconHeight(); /** * windows/classes/sun/awt/windows/ Creates native icon from specified raster data and updates * icon for window and all descendant windows that inherit icon. Raster data should be passed in * the ARGB form. Note that raster data format was changed to provide support for XP icons with * alpha-channel */ native void setIconImagesData( int[] iconRaster, int w, int h, int[] smallIconRaster, int smw, int smh); synchronized native void reshapeFrame(int x, int y, int width, int height); public boolean requestWindowFocus(CausedFocusEvent.Cause cause) { if (!focusAllowedFor()) { return false; } return requestWindowFocus(cause == CausedFocusEvent.Cause.MOUSE_EVENT); } public native boolean requestWindowFocus(boolean isMouseEventCause); public boolean focusAllowedFor() { Window window = (Window) this.target; if (!window.isVisible() || !window.isEnabled() || !window.isFocusableWindow()) { return false; } if (isModalBlocked()) { return false; } return true; } public void hide() { WindowListener listener = windowListener; if (listener != null) { // We're not getting WINDOW_CLOSING from the native code when hiding // the window programmatically. So, create it and notify the listener. listener.windowClosing(new WindowEvent((Window) target, WindowEvent.WINDOW_CLOSING)); } super.hide(); } // WARNING: it's called on the Toolkit thread! void preprocessPostEvent(AWTEvent event) { if (event instanceof WindowEvent) { WindowListener listener = windowListener; if (listener != null) { switch (event.getID()) { case WindowEvent.WINDOW_CLOSING: listener.windowClosing((WindowEvent) event); break; case WindowEvent.WINDOW_ICONIFIED: listener.windowIconified((WindowEvent) event); break; } } } } synchronized void addWindowListener(WindowListener l) { windowListener = AWTEventMulticaster.add(windowListener, l); } synchronized void removeWindowListener(WindowListener l) { windowListener = AWTEventMulticaster.remove(windowListener, l); } public void updateMinimumSize() { Dimension minimumSize = null; if (((Component) target).isMinimumSizeSet()) { minimumSize = ((Component) target).getMinimumSize(); } if (minimumSize != null) { int msw = getSysMinWidth(); int msh = getSysMinHeight(); int w = (minimumSize.width >= msw) ? minimumSize.width : msw; int h = (minimumSize.height >= msh) ? minimumSize.height : msh; setMinSize(w, h); } else { setMinSize(0, 0); } } public void updateIconImages() { java.util.List<Image> imageList = ((Window) target).getIconImages(); if (imageList == null || imageList.size() == 0) { setIconImagesData(null, 0, 0, null, 0, 0); } else { int w = getSysIconWidth(); int h = getSysIconHeight(); int smw = getSysSmIconWidth(); int smh = getSysSmIconHeight(); DataBufferInt iconData = SunToolkit.getScaledIconData(imageList, w, h); DataBufferInt iconSmData = SunToolkit.getScaledIconData(imageList, smw, smh); if (iconData != null && iconSmData != null) { setIconImagesData(iconData.getData(), w, h, iconSmData.getData(), smw, smh); } else { setIconImagesData(null, 0, 0, null, 0, 0); } } } native void setMinSize(int width, int height); /* * ---- MODALITY SUPPORT ---- */ /** * Some modality-related code here because WFileDialogPeer, WPrintDialogPeer and WPageDialogPeer * are descendants of WWindowPeer, not WDialogPeer */ public boolean isModalBlocked() { return modalBlocker != null; } @SuppressWarnings("deprecation") public void setModalBlocked(Dialog dialog, boolean blocked) { synchronized ( ((Component) getTarget()).getTreeLock()) // State lock should always be after awtLock { // use WWindowPeer instead of WDialogPeer because of FileDialogs and PrintDialogs WWindowPeer blockerPeer = (WWindowPeer) dialog.getPeer(); if (blocked) { modalBlocker = blockerPeer; // handle native dialogs separately, as they may have not // got HWND yet; modalEnable/modalDisable is called from // their setHWnd() methods if (blockerPeer instanceof WFileDialogPeer) { ((WFileDialogPeer) blockerPeer).blockWindow(this); } else if (blockerPeer instanceof WPrintDialogPeer) { ((WPrintDialogPeer) blockerPeer).blockWindow(this); } else { modalDisable(dialog, blockerPeer.getHWnd()); } } else { modalBlocker = null; if (blockerPeer instanceof WFileDialogPeer) { ((WFileDialogPeer) blockerPeer).unblockWindow(this); } else if (blockerPeer instanceof WPrintDialogPeer) { ((WPrintDialogPeer) blockerPeer).unblockWindow(this); } else { modalEnable(dialog); } } } } native void modalDisable(Dialog blocker, long blockerHWnd); native void modalEnable(Dialog blocker); /* * Returns all the ever active windows from the current AppContext. * The list is sorted by the time of activation, so the latest * active window is always at the end. */ @SuppressWarnings("unchecked") public static long[] getActiveWindowHandles() { AppContext appContext = AppContext.getAppContext(); synchronized (appContext) { List<WWindowPeer> l = (List<WWindowPeer>) appContext.get(ACTIVE_WINDOWS_KEY); if (l == null) { return null; } long[] result = new long[l.size()]; for (int j = 0; j < l.size(); j++) { result[j] = l.get(j).getHWnd(); } return result; } } /* * ----DISPLAY CHANGE SUPPORT---- */ /* * Called from native code when we have been dragged onto another screen. */ void draggedToNewScreen() { SunToolkit.executeOnEventHandlerThread( (Component) target, new Runnable() { public void run() { displayChanged(); } }); } public void updateGC() { int scrn = getScreenImOn(); if (screenLog.isLoggable(PlatformLogger.Level.FINER)) { log.finer("Screen number: " + scrn); } // get current GD Win32GraphicsDevice oldDev = (Win32GraphicsDevice) winGraphicsConfig.getDevice(); Win32GraphicsDevice newDev; GraphicsDevice devs[] = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices(); // Occasionally during device addition/removal getScreenImOn can return // a non-existing screen number. Use the default device in this case. if (scrn >= devs.length) { newDev = (Win32GraphicsDevice) GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); } else { newDev = (Win32GraphicsDevice) devs[scrn]; } // Set winGraphicsConfig to the default GC for the monitor this Window // is now mostly on. winGraphicsConfig = (Win32GraphicsConfig) newDev.getDefaultConfiguration(); if (screenLog.isLoggable(PlatformLogger.Level.FINE)) { if (winGraphicsConfig == null) { screenLog.fine("Assertion (winGraphicsConfig != null) failed"); } } // if on a different display, take off old GD and put on new GD if (oldDev != newDev) { oldDev.removeDisplayChangedListener(this); newDev.addDisplayChangedListener(this); } AWTAccessor.getComponentAccessor() .setGraphicsConfiguration((Component) target, winGraphicsConfig); } /** * From the DisplayChangedListener interface. * * <p>This method handles a display change - either when the display settings are changed, or when * the window has been dragged onto a different display. Called after a change in the display * mode. This event triggers replacing the surfaceData object (since that object reflects the * current display depth information, which has just changed). */ public void displayChanged() { updateGC(); } /** Part of the DisplayChangedListener interface: components do not need to react to this event */ public void paletteChanged() {} private native int getScreenImOn(); // Used in Win32GraphicsDevice. public final native void setFullScreenExclusiveModeState(boolean state); /* * ----END DISPLAY CHANGE SUPPORT---- */ public void grab() { nativeGrab(); } public void ungrab() { nativeUngrab(); } private native void nativeGrab(); private native void nativeUngrab(); private final boolean hasWarningWindow() { return ((Window) target).getWarningString() != null; } boolean isTargetUndecorated() { return true; } // These are the peer bounds. They get updated at: // 1. the WWindowPeer.setBounds() method. // 2. the native code (on WM_SIZE/WM_MOVE) private volatile int sysX = 0; private volatile int sysY = 0; private volatile int sysW = 0; private volatile int sysH = 0; public native void repositionSecurityWarning(); @Override public void setBounds(int x, int y, int width, int height, int op) { sysX = x; sysY = y; sysW = width; sysH = height; super.setBounds(x, y, width, height, op); } @Override 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); } @SuppressWarnings("deprecation") private void replaceSurfaceDataRecursively(Component c) { if (c instanceof Container) { for (Component child : ((Container) c).getComponents()) { replaceSurfaceDataRecursively(child); } } ComponentPeer cp = c.getPeer(); if (cp instanceof WComponentPeer) { ((WComponentPeer) cp).replaceSurfaceDataLater(); } } public final Graphics getTranslucentGraphics() { synchronized (getStateLock()) { return isOpaque ? null : painter.getBackBuffer(false).getGraphics(); } } @Override public void setBackground(Color c) { super.setBackground(c); synchronized (getStateLock()) { if (!isOpaque && ((Window) target).isVisible()) { updateWindow(true); } } } private native void setOpacity(int iOpacity); private float opacity = 1.0f; public void setOpacity(float opacity) { if (!((SunToolkit) ((Window) target).getToolkit()).isWindowOpacitySupported()) { return; } if (opacity < 0.0f || opacity > 1.0f) { throw new IllegalArgumentException( "The value of opacity should be in the range [0.0f .. 1.0f]."); } if (((this.opacity == 1.0f && opacity < 1.0f) || (this.opacity < 1.0f && opacity == 1.0f)) && !Win32GraphicsEnvironment.isVistaOS()) { // non-Vista OS: only replace the surface data if opacity status // changed (see WComponentPeer.isAccelCapable() for more) replaceSurfaceDataRecursively((Component) getTarget()); } this.opacity = opacity; final int maxOpacity = 0xff; int iOpacity = (int) (opacity * maxOpacity); if (iOpacity < 0) { iOpacity = 0; } if (iOpacity > maxOpacity) { iOpacity = maxOpacity; } setOpacity(iOpacity); synchronized (getStateLock()) { if (!isOpaque && ((Window) target).isVisible()) { updateWindow(true); } } } private native void setOpaqueImpl(boolean isOpaque); public void setOpaque(boolean isOpaque) { synchronized (getStateLock()) { if (this.isOpaque == isOpaque) { return; } } Window target = (Window) getTarget(); if (!isOpaque) { SunToolkit sunToolkit = (SunToolkit) target.getToolkit(); if (!sunToolkit.isWindowTranslucencySupported() || !sunToolkit.isTranslucencyCapable(target.getGraphicsConfiguration())) { return; } } boolean isVistaOS = Win32GraphicsEnvironment.isVistaOS(); if (this.isOpaque != isOpaque && !isVistaOS) { // non-Vista OS: only replace the surface data if the opacity // status changed (see WComponentPeer.isAccelCapable() for more) replaceSurfaceDataRecursively(target); } synchronized (getStateLock()) { this.isOpaque = isOpaque; setOpaqueImpl(isOpaque); if (isOpaque) { TranslucentWindowPainter currentPainter = painter; if (currentPainter != null) { currentPainter.flush(); painter = null; } } else { painter = TranslucentWindowPainter.createInstance(this); } } if (isVistaOS) { // On Vista: setting the window non-opaque makes the window look // rectangular, though still catching the mouse clicks within // its shape only. To restore the correct visual appearance // of the window (i.e. w/ the correct shape) we have to reset // the shape. Shape shape = target.getShape(); if (shape != null) { target.setShape(shape); } } if (target.isVisible()) { updateWindow(true); } } public native void updateWindowImpl(int[] data, int width, int height); public void updateWindow() { updateWindow(false); } private void updateWindow(boolean repaint) { Window w = (Window) target; synchronized (getStateLock()) { if (isOpaque || !w.isVisible() || (w.getWidth() <= 0) || (w.getHeight() <= 0)) { return; } TranslucentWindowPainter currentPainter = painter; if (currentPainter != null) { currentPainter.updateWindow(repaint); } else if (log.isLoggable(PlatformLogger.Level.FINER)) { log.finer("Translucent window painter is null in updateWindow"); } } } /* * The method maps the list of the active windows to the window's AppContext, * then the method registers ActiveWindowListener, GuiDisposedListener listeners; * it executes the initilialization only once per AppContext. */ @SuppressWarnings("unchecked") private static void initActiveWindowsTracking(Window w) { AppContext appContext = AppContext.getAppContext(); synchronized (appContext) { List<WWindowPeer> l = (List<WWindowPeer>) appContext.get(ACTIVE_WINDOWS_KEY); if (l == null) { l = new LinkedList<WWindowPeer>(); appContext.put(ACTIVE_WINDOWS_KEY, l); appContext.addPropertyChangeListener(AppContext.GUI_DISPOSED, guiDisposedListener); KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); kfm.addPropertyChangeListener("activeWindow", activeWindowListener); } } } /* * The GuiDisposedListener class listens for the AppContext.GUI_DISPOSED property, * it removes the list of the active windows from the disposed AppContext and * unregisters ActiveWindowListener listener. */ private static class GuiDisposedListener implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent e) { boolean isDisposed = (Boolean) e.getNewValue(); if (isDisposed != true) { if (log.isLoggable(PlatformLogger.Level.FINE)) { log.fine(" Assertion (newValue != true) failed for AppContext.GUI_DISPOSED "); } } AppContext appContext = AppContext.getAppContext(); synchronized (appContext) { appContext.remove(ACTIVE_WINDOWS_KEY); appContext.removePropertyChangeListener(AppContext.GUI_DISPOSED, this); KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); kfm.removePropertyChangeListener("activeWindow", activeWindowListener); } } } /* * Static inner class, listens for 'activeWindow' KFM property changes and * updates the list of active windows per AppContext, so the latest active * window is always at the end of the list. The list is stored in AppContext. */ @SuppressWarnings(value = {"deprecation", "unchecked"}) private static class ActiveWindowListener implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent e) { Window w = (Window) e.getNewValue(); if (w == null) { return; } AppContext appContext = SunToolkit.targetToAppContext(w); synchronized (appContext) { WWindowPeer wp = (WWindowPeer) w.getPeer(); // add/move wp to the end of the list List<WWindowPeer> l = (List<WWindowPeer>) appContext.get(ACTIVE_WINDOWS_KEY); if (l != null) { l.remove(wp); l.add(wp); } } } } }
/** * This utility class is used to suspend execution on a thread while still allowing {@code * EventDispatchThread} to dispatch events. The API methods of the class are thread-safe. * * @author Anton Tarasov, Artem Ananiev * @since 1.7 */ class WaitDispatchSupport implements SecondaryLoop { private static final PlatformLogger log = PlatformLogger.getLogger("java.awt.event.WaitDispatchSupport"); private EventDispatchThread dispatchThread; private EventFilter filter; private volatile Conditional extCondition; private volatile Conditional condition; private long interval; // Use a shared daemon timer to serve all the WaitDispatchSupports private static Timer timer; // When this WDS expires, we cancel the timer task leaving the // shared timer up and running private TimerTask timerTask; private AtomicBoolean keepBlockingEDT = new AtomicBoolean(false); private AtomicBoolean keepBlockingCT = new AtomicBoolean(false); private static synchronized void initializeTimer() { if (timer == null) { timer = new Timer("AWT-WaitDispatchSupport-Timer", true); } } /** * Creates a {@code WaitDispatchSupport} instance to serve the given event dispatch thread. * * @param dispatchThread An event dispatch thread that should not stop dispatching events while * waiting * @since 1.7 */ public WaitDispatchSupport(EventDispatchThread dispatchThread) { this(dispatchThread, null); } /** * Creates a {@code WaitDispatchSupport} instance to serve the given event dispatch thread. * * @param dispatchThread An event dispatch thread that should not stop dispatching events while * waiting * @param extCondition A conditional object used to determine if the loop should be terminated * @since 1.7 */ public WaitDispatchSupport(EventDispatchThread dispatchThread, Conditional extCond) { if (dispatchThread == null) { throw new IllegalArgumentException("The dispatchThread can not be null"); } this.dispatchThread = dispatchThread; this.extCondition = extCond; this.condition = new Conditional() { @Override public boolean evaluate() { if (log.isLoggable(PlatformLogger.Level.FINEST)) { log.finest( "evaluate(): blockingEDT=" + keepBlockingEDT.get() + ", blockingCT=" + keepBlockingCT.get()); } boolean extEvaluate = (extCondition != null) ? extCondition.evaluate() : true; if (!keepBlockingEDT.get() || !extEvaluate) { if (timerTask != null) { timerTask.cancel(); timerTask = null; } return false; } return true; } }; } /** * Creates a {@code WaitDispatchSupport} instance to serve the given event dispatch thread. * * <p>The {@link EventFilter} is set on the {@code dispatchThread} while waiting. The filter is * removed on completion of the waiting process. * * <p> * * @param dispatchThread An event dispatch thread that should not stop dispatching events while * waiting * @param filter {@code EventFilter} to be set * @param interval A time interval to wait for. Note that when the waiting process takes place on * EDT there is no guarantee to stop it in the given time * @since 1.7 */ public WaitDispatchSupport( EventDispatchThread dispatchThread, Conditional extCondition, EventFilter filter, long interval) { this(dispatchThread, extCondition); this.filter = filter; if (interval < 0) { throw new IllegalArgumentException("The interval value must be >= 0"); } this.interval = interval; if (interval != 0) { initializeTimer(); } } /** @inheritDoc */ @Override public boolean enter() { if (log.isLoggable(PlatformLogger.Level.FINE)) { log.fine( "enter(): blockingEDT=" + keepBlockingEDT.get() + ", blockingCT=" + keepBlockingCT.get()); } if (!keepBlockingEDT.compareAndSet(false, true)) { log.fine("The secondary loop is already running, aborting"); return false; } final Runnable run = new Runnable() { public void run() { log.fine("Starting a new event pump"); if (filter == null) { dispatchThread.pumpEvents(condition); } else { dispatchThread.pumpEventsForFilter(condition, filter); } } }; // We have two mechanisms for blocking: if we're on the // dispatch thread, start a new event pump; if we're // on any other thread, call wait() on the treelock Thread currentThread = Thread.currentThread(); if (currentThread == dispatchThread) { if (log.isLoggable(PlatformLogger.Level.FINEST)) { log.finest("On dispatch thread: " + dispatchThread); } if (interval != 0) { if (log.isLoggable(PlatformLogger.Level.FINEST)) { log.finest("scheduling the timer for " + interval + " ms"); } timer.schedule( timerTask = new TimerTask() { @Override public void run() { if (keepBlockingEDT.compareAndSet(true, false)) { wakeupEDT(); } } }, interval); } // Dispose SequencedEvent we are dispatching on the the current // AppContext, to prevent us from hang - see 4531693 for details SequencedEvent currentSE = KeyboardFocusManager.getCurrentKeyboardFocusManager().getCurrentSequencedEvent(); if (currentSE != null) { if (log.isLoggable(PlatformLogger.Level.FINE)) { log.fine("Dispose current SequencedEvent: " + currentSE); } currentSE.dispose(); } // In case the exit() method is called before starting // new event pump it will post the waking event to EDT. // The event will be handled after the the new event pump // starts. Thus, the enter() method will not hang. // // Event pump should be privileged. See 6300270. AccessController.doPrivileged( new PrivilegedAction() { public Object run() { run.run(); return null; } }); } else { if (log.isLoggable(PlatformLogger.Level.FINEST)) { log.finest("On non-dispatch thread: " + currentThread); } synchronized (getTreeLock()) { if (filter != null) { dispatchThread.addEventFilter(filter); } try { EventQueue eq = dispatchThread.getEventQueue(); eq.postEvent(new PeerEvent(this, run, PeerEvent.PRIORITY_EVENT)); keepBlockingCT.set(true); if (interval > 0) { long currTime = System.currentTimeMillis(); while (keepBlockingCT.get() && ((extCondition != null) ? extCondition.evaluate() : true) && (currTime + interval > System.currentTimeMillis())) { getTreeLock().wait(interval); } } else { while (keepBlockingCT.get() && ((extCondition != null) ? extCondition.evaluate() : true)) { getTreeLock().wait(); } } if (log.isLoggable(PlatformLogger.Level.FINE)) { log.fine("waitDone " + keepBlockingEDT.get() + " " + keepBlockingCT.get()); } } catch (InterruptedException e) { if (log.isLoggable(PlatformLogger.Level.FINE)) { log.fine("Exception caught while waiting: " + e); } } finally { if (filter != null) { dispatchThread.removeEventFilter(filter); } } // If the waiting process has been stopped because of the // time interval passed or an exception occurred, the state // should be changed keepBlockingEDT.set(false); keepBlockingCT.set(false); } } return true; } /** @inheritDoc */ public boolean exit() { if (log.isLoggable(PlatformLogger.Level.FINE)) { log.fine( "exit(): blockingEDT=" + keepBlockingEDT.get() + ", blockingCT=" + keepBlockingCT.get()); } if (keepBlockingEDT.compareAndSet(true, false)) { wakeupEDT(); return true; } return false; } private static final Object getTreeLock() { return Component.LOCK; } private final Runnable wakingRunnable = new Runnable() { public void run() { log.fine("Wake up EDT"); synchronized (getTreeLock()) { keepBlockingCT.set(false); getTreeLock().notifyAll(); } log.fine("Wake up EDT done"); } }; private void wakeupEDT() { if (log.isLoggable(PlatformLogger.Level.FINEST)) { log.finest("wakeupEDT(): EDT == " + dispatchThread); } EventQueue eq = dispatchThread.getEventQueue(); eq.postEvent(new PeerEvent(this, wakingRunnable, PeerEvent.PRIORITY_EVENT)); } }
/** * The XDragSourceContextPeer class is the class responsible for handling the interaction between * the XDnD/Motif DnD subsystem and Java drag sources. * * @since 1.5 */ public final class XDragSourceContextPeer extends SunDragSourceContextPeer implements XDragSourceProtocolListener { private static final PlatformLogger logger = PlatformLogger.getLogger("sun.awt.X11.xembed.xdnd.XDragSourceContextPeer"); /* The events selected on the root window when the drag begins. */ private static final int ROOT_EVENT_MASK = (int) XConstants.ButtonMotionMask | (int) XConstants.KeyPressMask | (int) XConstants.KeyReleaseMask; /* The events to be delivered during grab. */ private static final int GRAB_EVENT_MASK = (int) XConstants.ButtonPressMask | (int) XConstants.ButtonMotionMask | (int) XConstants.ButtonReleaseMask; /* The event mask of the root window before the drag operation starts. */ private long rootEventMask = 0; private boolean dndInProgress = false; private boolean dragInProgress = false; private long dragRootWindow = 0; /* The protocol chosen for the communication with the current drop target. */ private XDragSourceProtocol dragProtocol = null; /* The drop action chosen by the current drop target. */ private int targetAction = DnDConstants.ACTION_NONE; /* The set of drop actions supported by the drag source. */ private int sourceActions = DnDConstants.ACTION_NONE; /* The drop action selected by the drag source based on the modifiers state and the action selected by the current drop target. */ private int sourceAction = DnDConstants.ACTION_NONE; /* The data formats supported by the drag source for the current drag operation. */ private long[] sourceFormats = null; /* The XID of the root subwindow that contains the current target. */ private long targetRootSubwindow = 0; /* The pointer location. */ private int xRoot = 0; private int yRoot = 0; /* Keyboard modifiers state. */ private int eventState = 0; /* XEmbed DnD support. We act as a proxy between source and target. */ private long proxyModeSourceWindow = 0; /* The singleton instance. */ private static final XDragSourceContextPeer theInstance = new XDragSourceContextPeer(null); private XDragSourceContextPeer(DragGestureEvent dge) { super(dge); } static XDragSourceProtocolListener getXDragSourceProtocolListener() { return theInstance; } static XDragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge) throws InvalidDnDOperationException { theInstance.setTrigger(dge); return theInstance; } protected void startDrag(Transferable transferable, long[] formats, Map formatMap) { Component component = getTrigger().getComponent(); Component c = null; XWindowPeer wpeer = null; for (c = component; c != null && !(c instanceof Window); c = AWTAccessor.getComponentAccessor().getParent(c)) ; if (c instanceof Window) { wpeer = (XWindowPeer) c.getPeer(); } if (wpeer == null) { throw new InvalidDnDOperationException("Cannot find top-level for the drag source component"); } long xcursor = 0; long rootWindow = 0; long dragWindow = 0; long timeStamp = 0; /* Retrieve the X cursor for the drag operation. */ { Cursor cursor = getCursor(); if (cursor != null) { xcursor = XGlobalCursorManager.getCursor(cursor); } } XToolkit.awtLock(); try { if (proxyModeSourceWindow != 0) { throw new InvalidDnDOperationException("Proxy drag in progress"); } if (dndInProgress) { throw new InvalidDnDOperationException("Drag in progress"); } /* Determine the root window for the drag operation. */ { long screen = XlibWrapper.XScreenNumberOfScreen(wpeer.getScreen()); rootWindow = XlibWrapper.RootWindow(XToolkit.getDisplay(), screen); } dragWindow = XWindow.getXAWTRootWindow().getWindow(); timeStamp = XToolkit.getCurrentServerTime(); int dropActions = getDragSourceContext().getSourceActions(); Iterator dragProtocols = XDragAndDropProtocols.getDragSourceProtocols(); while (dragProtocols.hasNext()) { XDragSourceProtocol dragProtocol = (XDragSourceProtocol) dragProtocols.next(); try { dragProtocol.initializeDrag( dropActions, transferable, formatMap, formats); } catch (XException xe) { throw (InvalidDnDOperationException) new InvalidDnDOperationException().initCause(xe); } } /* Install X grabs. */ { int status; XWindowAttributes wattr = new XWindowAttributes(); try { status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), rootWindow, wattr.pData); if (status == 0) { throw new InvalidDnDOperationException("XGetWindowAttributes failed"); } rootEventMask = wattr.get_your_event_mask(); XlibWrapper.XSelectInput( XToolkit.getDisplay(), rootWindow, rootEventMask | ROOT_EVENT_MASK); } finally { wattr.dispose(); } XBaseWindow.ungrabInput(); status = XlibWrapper.XGrabPointer( XToolkit.getDisplay(), rootWindow, 0, GRAB_EVENT_MASK, XConstants.GrabModeAsync, XConstants.GrabModeAsync, XConstants.None, xcursor, timeStamp); if (status != XConstants.GrabSuccess) { cleanup(timeStamp); throwGrabFailureException("Cannot grab pointer", status); return; } status = XlibWrapper.XGrabKeyboard( XToolkit.getDisplay(), rootWindow, 0, XConstants.GrabModeAsync, XConstants.GrabModeAsync, timeStamp); if (status != XConstants.GrabSuccess) { cleanup(timeStamp); throwGrabFailureException("Cannot grab keyboard", status); return; } } /* Update the global state. */ dndInProgress = true; dragInProgress = true; dragRootWindow = rootWindow; sourceActions = dropActions; sourceFormats = formats; } finally { XToolkit.awtUnlock(); } /* This implementation doesn't use native context */ setNativeContext(0); SunDropTargetContextPeer.setCurrentJVMLocalSourceTransferable(transferable); } public long getProxyModeSourceWindow() { return proxyModeSourceWindow; } private void setProxyModeSourceWindowImpl(long window) { proxyModeSourceWindow = window; } public static void setProxyModeSourceWindow(long window) { theInstance.setProxyModeSourceWindowImpl(window); } /** set cursor */ public void setCursor(Cursor c) throws InvalidDnDOperationException { XToolkit.awtLock(); try { super.setCursor(c); } finally { XToolkit.awtUnlock(); } } protected void setNativeCursor(long nativeCtxt, Cursor c, int cType) { assert XToolkit.isAWTLockHeldByCurrentThread(); if (c == null) { return; } long xcursor = XGlobalCursorManager.getCursor(c); if (xcursor == 0) { return; } XlibWrapper.XChangeActivePointerGrab( XToolkit.getDisplay(), GRAB_EVENT_MASK, xcursor, XConstants.CurrentTime); } protected boolean needsBogusExitBeforeDrop() { return false; } private void throwGrabFailureException(String msg, int grabStatus) throws InvalidDnDOperationException { String msgCause = ""; switch (grabStatus) { case XConstants.GrabNotViewable: msgCause = "not viewable"; break; case XConstants.AlreadyGrabbed: msgCause = "already grabbed"; break; case XConstants.GrabInvalidTime: msgCause = "invalid time"; break; case XConstants.GrabFrozen: msgCause = "grab frozen"; break; default: msgCause = "unknown failure"; break; } throw new InvalidDnDOperationException(msg + ": " + msgCause); } /** The caller must own awtLock. */ public void cleanup(long time) { if (dndInProgress) { if (dragProtocol != null) { dragProtocol.sendLeaveMessage(time); } if (targetAction != DnDConstants.ACTION_NONE) { dragExit(xRoot, yRoot); } dragDropFinished(false, DnDConstants.ACTION_NONE, xRoot, yRoot); } Iterator dragProtocols = XDragAndDropProtocols.getDragSourceProtocols(); while (dragProtocols.hasNext()) { XDragSourceProtocol dragProtocol = (XDragSourceProtocol) dragProtocols.next(); try { dragProtocol.cleanup(); } catch (XException xe) { // Ignore the exception. } } dndInProgress = false; dragInProgress = false; dragRootWindow = 0; sourceFormats = null; sourceActions = DnDConstants.ACTION_NONE; sourceAction = DnDConstants.ACTION_NONE; eventState = 0; xRoot = 0; yRoot = 0; cleanupTargetInfo(); removeDnDGrab(time); } /** The caller must own awtLock. */ private void cleanupTargetInfo() { targetAction = DnDConstants.ACTION_NONE; dragProtocol = null; targetRootSubwindow = 0; } private void removeDnDGrab(long time) { assert XToolkit.isAWTLockHeldByCurrentThread(); XlibWrapper.XUngrabPointer(XToolkit.getDisplay(), time); XlibWrapper.XUngrabKeyboard(XToolkit.getDisplay(), time); /* Restore the root event mask if it was changed. */ if ((rootEventMask | ROOT_EVENT_MASK) != rootEventMask && dragRootWindow != 0) { XlibWrapper.XSelectInput(XToolkit.getDisplay(), dragRootWindow, rootEventMask); } rootEventMask = 0; dragRootWindow = 0; } private boolean processClientMessage(XClientMessageEvent xclient) { if (dragProtocol != null) { return dragProtocol.processClientMessage(xclient); } return false; } /** * Updates the source action according to the specified state. * * @returns true if the source */ private boolean updateSourceAction(int state) { int action = SunDragSourceContextPeer.convertModifiersToDropAction( XWindow.getModifiers(state, 0, 0), sourceActions); if (sourceAction == action) { return false; } sourceAction = action; return true; } /** Returns the client window under the specified root subwindow. */ private static long findClientWindow(long window) { if (XlibUtil.isTrueToplevelWindow(window)) { return window; } Set<Long> children = XlibUtil.getChildWindows(window); for (Long child : children) { long win = findClientWindow(child); if (win != 0) { return win; } } return 0; } private void doUpdateTargetWindow(long subwindow, long time) { long clientWindow = 0; long proxyWindow = 0; XDragSourceProtocol protocol = null; boolean isReceiver = false; if (subwindow != 0) { clientWindow = findClientWindow(subwindow); } if (clientWindow != 0) { Iterator dragProtocols = XDragAndDropProtocols.getDragSourceProtocols(); while (dragProtocols.hasNext()) { XDragSourceProtocol dragProtocol = (XDragSourceProtocol) dragProtocols.next(); if (dragProtocol.attachTargetWindow(clientWindow, time)) { protocol = dragProtocol; break; } } } /* Update the global state. */ dragProtocol = protocol; targetAction = DnDConstants.ACTION_NONE; targetRootSubwindow = subwindow; } private void updateTargetWindow(XMotionEvent xmotion) { assert XToolkit.isAWTLockHeldByCurrentThread(); int x = xmotion.get_x_root(); int y = xmotion.get_y_root(); long time = xmotion.get_time(); long subwindow = xmotion.get_subwindow(); /* * If this event had occurred before the pointer was grabbed, * query the server for the current root subwindow. */ if (xmotion.get_window() != xmotion.get_root()) { XlibWrapper.XQueryPointer( XToolkit.getDisplay(), xmotion.get_root(), XlibWrapper.larg1, // root XlibWrapper.larg2, // subwindow XlibWrapper.larg3, // x_root XlibWrapper.larg4, // y_root XlibWrapper.larg5, // x XlibWrapper.larg6, // y XlibWrapper.larg7); // modifiers subwindow = Native.getLong(XlibWrapper.larg2); } if (targetRootSubwindow != subwindow) { if (dragProtocol != null) { dragProtocol.sendLeaveMessage(time); /* * Neither Motif DnD nor XDnD provide a mean for the target * to notify the source that the pointer exits the drop site * that occupies the whole top level. * We detect this situation and post dragExit. */ if (targetAction != DnDConstants.ACTION_NONE) { dragExit(x, y); } } /* Update the global state. */ doUpdateTargetWindow(subwindow, time); if (dragProtocol != null) { dragProtocol.sendEnterMessage(sourceFormats, sourceAction, sourceActions, time); } } } /* * DO NOT USE is_hint field of xmotion since it could not be set when we * convert XKeyEvent or XButtonRelease to XMotionEvent. */ private void processMouseMove(XMotionEvent xmotion) { if (!dragInProgress) { return; } if (xRoot != xmotion.get_x_root() || yRoot != xmotion.get_y_root()) { xRoot = xmotion.get_x_root(); yRoot = xmotion.get_y_root(); postDragSourceDragEvent( targetAction, XWindow.getModifiers(xmotion.get_state(), 0, 0), xRoot, yRoot, DISPATCH_MOUSE_MOVED); } if (eventState != xmotion.get_state()) { if (updateSourceAction(xmotion.get_state()) && dragProtocol != null) { postDragSourceDragEvent( targetAction, XWindow.getModifiers(xmotion.get_state(), 0, 0), xRoot, yRoot, DISPATCH_CHANGED); } eventState = xmotion.get_state(); } updateTargetWindow(xmotion); if (dragProtocol != null) { dragProtocol.sendMoveMessage( xmotion.get_x_root(), xmotion.get_y_root(), sourceAction, sourceActions, xmotion.get_time()); } } private void processDrop(XButtonEvent xbutton) { try { dragProtocol.initiateDrop( xbutton.get_x_root(), xbutton.get_y_root(), sourceAction, sourceActions, xbutton.get_time()); } catch (XException e) { cleanup(xbutton.get_time()); } } private boolean processProxyModeEvent(XEvent ev) { if (getProxyModeSourceWindow() == 0) { return false; } if (ev.get_type() != (int) XConstants.ClientMessage) { return false; } if (logger.isLoggable(PlatformLogger.Level.FINEST)) { logger.finest(" proxyModeSourceWindow=" + getProxyModeSourceWindow() + " ev=" + ev); } XClientMessageEvent xclient = ev.get_xclient(); Iterator dragProtocols = XDragAndDropProtocols.getDragSourceProtocols(); while (dragProtocols.hasNext()) { XDragSourceProtocol dragProtocol = (XDragSourceProtocol) dragProtocols.next(); if (dragProtocol.processProxyModeEvent(xclient, getProxyModeSourceWindow())) { return true; } } return false; } /** * The caller must own awtLock. * * @returns true if the even was processed and shouldn't be passed along. */ private boolean doProcessEvent(XEvent ev) { assert XToolkit.isAWTLockHeldByCurrentThread(); if (processProxyModeEvent(ev)) { return true; } if (!dndInProgress) { return false; } switch (ev.get_type()) { case XConstants.ClientMessage: { XClientMessageEvent xclient = ev.get_xclient(); return processClientMessage(xclient); } case XConstants.DestroyNotify: { XDestroyWindowEvent xde = ev.get_xdestroywindow(); /* Target crashed during drop processing - cleanup. */ if (!dragInProgress && dragProtocol != null && xde.get_window() == dragProtocol.getTargetWindow()) { cleanup(XConstants.CurrentTime); return true; } /* Pass along */ return false; } } if (!dragInProgress) { return false; } /* Process drag-only messages. */ switch (ev.get_type()) { case XConstants.KeyRelease: case XConstants.KeyPress: { XKeyEvent xkey = ev.get_xkey(); long keysym = XlibWrapper.XKeycodeToKeysym(XToolkit.getDisplay(), xkey.get_keycode(), 0); switch ((int) keysym) { case (int) XKeySymConstants.XK_Escape: { if (ev.get_type() == (int) XConstants.KeyRelease) { cleanup(xkey.get_time()); } break; } case (int) XKeySymConstants.XK_Control_R: case (int) XKeySymConstants.XK_Control_L: case (int) XKeySymConstants.XK_Shift_R: case (int) XKeySymConstants.XK_Shift_L: { XlibWrapper.XQueryPointer( XToolkit.getDisplay(), xkey.get_root(), XlibWrapper.larg1, // root XlibWrapper.larg2, // subwindow XlibWrapper.larg3, // x_root XlibWrapper.larg4, // y_root XlibWrapper.larg5, // x XlibWrapper.larg6, // y XlibWrapper.larg7); // modifiers XMotionEvent xmotion = new XMotionEvent(); try { xmotion.set_type(XConstants.MotionNotify); xmotion.set_serial(xkey.get_serial()); xmotion.set_send_event(xkey.get_send_event()); xmotion.set_display(xkey.get_display()); xmotion.set_window(xkey.get_window()); xmotion.set_root(xkey.get_root()); xmotion.set_subwindow(xkey.get_subwindow()); xmotion.set_time(xkey.get_time()); xmotion.set_x(xkey.get_x()); xmotion.set_y(xkey.get_y()); xmotion.set_x_root(xkey.get_x_root()); xmotion.set_y_root(xkey.get_y_root()); xmotion.set_state((int) Native.getLong(XlibWrapper.larg7)); // we do not use this field, so it's unset for now // xmotion.set_is_hint(???); xmotion.set_same_screen(xkey.get_same_screen()); // It's safe to use key event as motion event since we use only their common // fields. processMouseMove(xmotion); } finally { xmotion.dispose(); } break; } } return true; } case XConstants.ButtonPress: return true; case XConstants.MotionNotify: processMouseMove(ev.get_xmotion()); return true; case XConstants.ButtonRelease: { XButtonEvent xbutton = ev.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 (xbutton.get_button() > SunToolkit.MAX_BUTTONS_SUPPORTED) { return true; } /* * On some X servers it could happen that ButtonRelease coordinates * differ from the latest MotionNotify coordinates, so we need to * process it as a mouse motion. */ XMotionEvent xmotion = new XMotionEvent(); try { xmotion.set_type(XConstants.MotionNotify); xmotion.set_serial(xbutton.get_serial()); xmotion.set_send_event(xbutton.get_send_event()); xmotion.set_display(xbutton.get_display()); xmotion.set_window(xbutton.get_window()); xmotion.set_root(xbutton.get_root()); xmotion.set_subwindow(xbutton.get_subwindow()); xmotion.set_time(xbutton.get_time()); xmotion.set_x(xbutton.get_x()); xmotion.set_y(xbutton.get_y()); xmotion.set_x_root(xbutton.get_x_root()); xmotion.set_y_root(xbutton.get_y_root()); xmotion.set_state(xbutton.get_state()); // we do not use this field, so it's unset for now // xmotion.set_is_hint(???); xmotion.set_same_screen(xbutton.get_same_screen()); // It's safe to use key event as motion event since we use only their common fields. processMouseMove(xmotion); } finally { xmotion.dispose(); } if (xbutton.get_button() == XConstants.buttons[0] || xbutton.get_button() == XConstants.buttons[1]) { // drag is initiated with Button1 or Button2 pressed and // ended on release of either of these buttons (as the same // behavior was with our old Motif DnD-based implementation) removeDnDGrab(xbutton.get_time()); dragInProgress = false; if (dragProtocol != null && targetAction != DnDConstants.ACTION_NONE) { /* * ACTION_NONE indicates that either the drop target rejects the * drop or it haven't responded yet. The latter could happen in * case of fast drag, slow target-server connection or slow * drag notifications processing on the target side. */ processDrop(xbutton); } else { cleanup(xbutton.get_time()); } } return true; } } return false; } static boolean processEvent(XEvent ev) { XToolkit.awtLock(); try { try { return theInstance.doProcessEvent(ev); } catch (XException e) { e.printStackTrace(); return false; } } finally { XToolkit.awtUnlock(); } } /* XDragSourceProtocolListener implementation */ public void handleDragReply(int action) { // NOTE: we have to use the current pointer location, since // the target didn't specify the coordinates for the reply. handleDragReply(action, xRoot, yRoot); } public void handleDragReply(int action, int x, int y) { // NOTE: we have to use the current modifiers state, since // the target didn't specify the modifiers state for the reply. handleDragReply(action, xRoot, yRoot, XWindow.getModifiers(eventState, 0, 0)); } public void handleDragReply(int action, int x, int y, int modifiers) { if (action == DnDConstants.ACTION_NONE && targetAction != DnDConstants.ACTION_NONE) { dragExit(x, y); } else if (action != DnDConstants.ACTION_NONE) { int type = 0; if (targetAction == DnDConstants.ACTION_NONE) { type = SunDragSourceContextPeer.DISPATCH_ENTER; } else { type = SunDragSourceContextPeer.DISPATCH_MOTION; } // Note that we use the modifiers state a postDragSourceDragEvent(action, modifiers, x, y, type); } targetAction = action; } public void handleDragFinished() { /* Assume that the drop was successful. */ handleDragFinished(true); } public void handleDragFinished(boolean success) { /* Assume that the performed drop action is the latest drop action accepted by the drop target. */ handleDragFinished(true, targetAction); } public void handleDragFinished(boolean success, int action) { // NOTE: we have to use the current pointer location, since // the target didn't specify the coordinates for the reply. handleDragFinished(success, action, xRoot, yRoot); } public void handleDragFinished(boolean success, int action, int x, int y) { dragDropFinished(success, action, x, y); dndInProgress = false; cleanup(XConstants.CurrentTime); } }