+ * NOTE: Neither shaped windows nor transparency + * currently works with Java 1.4 under X11. This is at least partly due + * to 1.4 using multiple X11 windows for a single given Java window. It + * *might* be possible to remedy by applying the window + * region/transparency to all descendants, but I haven't tried it. In + * addition, windows must be both displayable and visible + * before the corresponding native Drawable may be obtained; in later + * Java versions, the window need only be displayable. + *
+ * NOTE: If you use {@link EnhancedWindowUtils#setWindowMask(Window, Shape, int)} and override {@link
+ * Window#paint(Graphics)} on OS X, you'll need to explicitly set the clip
+ * mask on the Graphics
object with the window mask; only the
+ * content pane of the window and below have the window mask automatically
+ * applied.
+ * NOTE: On OSX, the property
+ * apple.awt.draggableWindowBackground
is set automatically when
+ * a window's background color has an alpha component. That property must be
+ * set to its final value before the heavyweight peer for the Window
+ * is created. Once {@link Component#addNotify} has been called on the
+ * component, causing creation of the heavyweight peer, changing this
+ * property has no effect.
+ * @see Apple Technote 2007
+ *
+ * @author Andreas "PAX" Lück, onkelpax-git[at]yahoo.de
+ */
+// TODO: setWindowMask() should accept a threshold; some cases want a
+// 50% threshold, some might want zero/non-zero
+public class EnhancedWindowUtils {
+
+ private static final Logger LOG = Logger.getLogger(EnhancedWindowUtils.class.getName());
+
+ private static final String TRANSPARENT_OLD_BG = "transparent-old-bg";
+ private static final String TRANSPARENT_OLD_OPAQUE = "transparent-old-opaque";
+ private static final String TRANSPARENT_ALPHA = "transparent-alpha";
+
+ /** Use this to clear a window mask. */
+ public static final Shape MASK_NONE = null;
+
+ /**
+ * This class forces a heavyweight popup on the parent
+ * {@link Window}. See the implementation of {@link PopupFactory};
+ * a heavyweight is forced if there is an occluding subwindow on the
+ * target window.
+ *
+ * Ideally we'd have more control over {@link PopupFactory} but this
+ * is a fairly simple, lightweight workaround. Note that, at least as of
+ * JDK 1.6, the following do not have the desired effect:
+ *
+ * ToolTipManager.sharedInstance().setLightWeightPopupEnabled(false);
+ * JPopupMenu.setDefaultLightWeightPopupEnabled(false);
+ * System.setProperty("JPopupMenu.defaultLWPopupEnabledKey", "false");
+ *
+ */
+ private static class HeavyweightForcer extends Window {
+ private static final long serialVersionUID = 1L;
+ private final boolean packed;
+
+ public HeavyweightForcer(Window parent) {
+ super(parent);
+ pack();
+ packed = true;
+ }
+
+ @Override
+ public boolean isVisible() {
+ // Only want to be 'visible' once the peer is instantiated
+ // via pack; this tricks PopupFactory into using a heavyweight
+ // popup to avoid being obscured by this window
+ return packed;
+ }
+
+ @Override
+ public Rectangle getBounds() {
+ return getOwner().getBounds();
+ }
+ }
+ /**
+ * This can be installed over a {@link JLayeredPane} in order to
+ * listen for repaint requests. The content's repaint method will be
+ * invoked whenever any part of the ancestor window is repainted.
+ */
+ protected static class RepaintTrigger extends JComponent {
+ private static final long serialVersionUID = 1L;
+
+ protected class Listener
+ extends WindowAdapter
+ implements ComponentListener, HierarchyListener, AWTEventListener {
+ @Override
+ public void windowOpened(WindowEvent e) {
+ repaint();
+ }
+
+ @Override
+ public void componentHidden(ComponentEvent e) {}
+
+ @Override
+ public void componentMoved(ComponentEvent e) {}
+
+ @Override
+ public void componentResized(ComponentEvent e) {
+ setSize(getParent().getSize());
+ repaint();
+ }
+
+ @Override
+ public void componentShown(ComponentEvent e) {
+ repaint();
+ }
+
+ @Override
+ public void hierarchyChanged(HierarchyEvent e) {
+ repaint();
+ }
+
+ @Override
+ public void eventDispatched(AWTEvent e) {
+ if (e instanceof MouseEvent) {
+ Component src = ((MouseEvent)e).getComponent();
+ if (src != null
+ && SwingUtilities.isDescendingFrom(src, content)) {
+ MouseEvent me = SwingUtilities.convertMouseEvent(src, (MouseEvent)e, content);
+ Component c = SwingUtilities.getDeepestComponentAt(content, me.getX(), me.getY());
+ if (c != null) {
+ setCursor(c.getCursor());
+ }
+ }
+ }
+ }
+ }
+
+ private final Listener listener = createListener();
+ private final JComponent content;
+
+ public RepaintTrigger(JComponent content) {
+ this.content = content;
+ }
+
+ @Override
+ public void addNotify() {
+ super.addNotify();
+ Window w = SwingUtilities.getWindowAncestor(this);
+ setSize(getParent().getSize());
+ w.addComponentListener(listener);
+ w.addWindowListener(listener);
+ Toolkit.getDefaultToolkit().addAWTEventListener(listener, AWTEvent.MOUSE_EVENT_MASK|AWTEvent.MOUSE_MOTION_EVENT_MASK);
+ }
+
+ @Override
+ public void removeNotify() {
+ Toolkit.getDefaultToolkit().removeAWTEventListener(listener);
+ Window w = SwingUtilities.getWindowAncestor(this);
+ w.removeComponentListener(listener);
+ w.removeWindowListener(listener);
+ super.removeNotify();
+ }
+
+ private Rectangle dirty;
+ @Override
+ protected void paintComponent(Graphics g) {
+ Rectangle bounds = g.getClipBounds();
+ if (dirty == null || !dirty.contains(bounds)) {
+ if (dirty == null) {
+ dirty = bounds;
+ }
+ else {
+ dirty = dirty.union(bounds);
+ }
+ content.repaint(dirty);
+ }
+ else {
+ dirty = null;
+ }
+ }
+
+ protected Listener createListener() {
+ return new Listener();
+ }
+ };
+
+ /** Window utilities with differing native implementations. */
+ public static abstract class NativeWindowUtils {
+ protected void setMask(Component w, Raster raster, int whichShape) {
+ throw new UnsupportedOperationException("Not supported");
+ };
+
+ protected abstract class TransparentContentPane
+ extends JPanel implements AWTEventListener {
+ private static final long serialVersionUID = 1L;
+ private boolean transparent;
+ public TransparentContentPane(Container oldContent) {
+ super(new BorderLayout());
+ add(oldContent, BorderLayout.CENTER);
+ setTransparent(true);
+ if (oldContent instanceof JPanel) {
+ ((JComponent)oldContent).setOpaque(false);
+ }
+ }
+ @Override
+ public void addNotify() {
+ super.addNotify();
+ Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.CONTAINER_EVENT_MASK);
+ }
+ @Override
+ public void removeNotify() {
+ Toolkit.getDefaultToolkit().removeAWTEventListener(this);
+ super.removeNotify();
+ }
+ public void setTransparent(boolean transparent) {
+ this.transparent = transparent;
+ setOpaque(!transparent);
+ setDoubleBuffered(!transparent);
+ repaint();
+ }
+ @Override
+ public void eventDispatched(AWTEvent e) {
+ if (e.getID() == ContainerEvent.COMPONENT_ADDED
+ && SwingUtilities.isDescendingFrom(((ContainerEvent)e).getChild(), this)) {
+ Component child = ((ContainerEvent)e).getChild();
+ NativeWindowUtils.this.setDoubleBuffered(child, false);
+ }
+ }
+ @Override
+ public void paint(Graphics gr) {
+ if (transparent) {
+ Rectangle r = gr.getClipBounds();
+ final int w = r.width;
+ final int h = r.height;
+ if (getWidth() > 0 && getHeight() > 0) {
+ final BufferedImage buf =
+ new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB_PRE);
+
+ Graphics2D g = buf.createGraphics();
+ g.setComposite(AlphaComposite.Clear);
+ g.fillRect(0, 0, w, h);
+ g.dispose();
+
+ g = buf.createGraphics();
+ g.translate(-r.x, -r.y);
+ super.paint(g);
+ g.dispose();
+
+ paintDirect(buf, r);
+ }
+ }
+ else {
+ super.paint(gr);
+ }
+ }
+ /** Use the contents of the given BufferedImage to paint directly
+ * on this component's ancestor window.
+ */
+ protected abstract void paintDirect(BufferedImage buf, Rectangle bounds);
+ }
+
+ protected Window getWindow(Component c) {
+ return c instanceof Window
+ ? (Window)c : SwingUtilities.getWindowAncestor(c);
+ }
+ /**
+ * Execute the given action when the given window becomes
+ * displayable.
+ */
+ protected void whenDisplayable(Component w, final Runnable action) {
+ if (w.isDisplayable() && (!Holder.requiresVisible || w.isVisible())) {
+ action.run();
+ }
+ else if (Holder.requiresVisible) {
+ getWindow(w).addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowOpened(WindowEvent e) {
+ e.getWindow().removeWindowListener(this);
+ action.run();
+ }
+ @Override
+ public void windowClosed(WindowEvent e) {
+ e.getWindow().removeWindowListener(this);
+ }
+ });
+ }
+ else {
+ // Hierarchy events are fired in direct response to
+ // displayability changes
+ w.addHierarchyListener(new HierarchyListener() {
+ @Override
+ public void hierarchyChanged(HierarchyEvent e) {
+ if ((e.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0
+ && e.getComponent().isDisplayable()) {
+ e.getComponent().removeHierarchyListener(this);
+ action.run();
+ }
+ }
+ });
+ }
+ }
+
+ protected Raster toRaster(Shape mask) {
+ Raster raster = null;
+ if (mask != MASK_NONE) {
+ Rectangle bounds = mask.getBounds();
+ if (bounds.width > 0 && bounds.height > 0) {
+ BufferedImage clip =
+ new BufferedImage(bounds.x + bounds.width,
+ bounds.y + bounds.height,
+ BufferedImage.TYPE_BYTE_BINARY);
+ Graphics2D g = clip.createGraphics();
+ g.setColor(Color.black);
+ g.fillRect(0, 0, bounds.x + bounds.width, bounds.y + bounds.height);
+ g.setColor(Color.white);
+ g.fill(mask);
+ raster = clip.getRaster();
+ }
+ }
+ return raster;
+ }
+
+ protected Raster toRaster(Component c, Icon mask) {
+ Raster raster = null;
+ if (mask != null) {
+ Rectangle bounds = new Rectangle(0, 0, mask.getIconWidth(),
+ mask.getIconHeight());
+ BufferedImage clip = new BufferedImage(bounds.width,
+ bounds.height,
+ BufferedImage.TYPE_INT_ARGB);
+ Graphics2D g = clip.createGraphics();
+ g.setComposite(AlphaComposite.Clear);
+ g.fillRect(0, 0, bounds.width, bounds.height);
+ g.setComposite(AlphaComposite.SrcOver);
+ mask.paintIcon(c, g, 0, 0);
+ raster = clip.getAlphaRaster();
+ }
+ return raster;
+ }
+
+ protected Shape toShape(Raster raster) {
+ final Area area = new Area(new Rectangle(0, 0, 0, 0));
+ RasterRangesUtils.outputOccupiedRanges(raster, new RasterRangesUtils.RangesOutput() {
+ @Override
+ public boolean outputRange(int x, int y, int w, int h) {
+ area.add(new Area(new Rectangle(x, y, w, h)));
+ return true;
+ }
+ });
+ return area;
+ }
+
+ /**
+ * Set the overall alpha transparency of the window. An alpha of
+ * 1.0 is fully opaque, 0.0 is fully transparent.
+ */
+ public void setWindowAlpha(Window w, float alpha) {
+ // do nothing
+ }
+
+ /** Default: no support. */
+ public boolean isWindowAlphaSupported() {
+ return false;
+ }
+
+ /** Return the default graphics configuration. */
+ public GraphicsConfiguration getAlphaCompatibleGraphicsConfiguration() {
+ GraphicsEnvironment env = GraphicsEnvironment
+ .getLocalGraphicsEnvironment();
+ GraphicsDevice dev = env.getDefaultScreenDevice();
+ return dev.getDefaultConfiguration();
+ }
+
+ /**
+ * Set the window to be transparent. Only explicitly painted
+ * pixels will be non-transparent. All pixels will be composited
+ * with whatever is under the window using their alpha values.
+ */
+ public void setWindowTransparent(Window w, boolean transparent) {
+ // do nothing
+ }
+
+ protected void setDoubleBuffered(Component root, boolean buffered) {
+ if (root instanceof JComponent) {
+ ((JComponent)root).setDoubleBuffered(buffered);
+ }
+ if (root instanceof JRootPane && buffered) {
+ ((JRootPane)root).setDoubleBuffered(true);
+ }
+ else if (root instanceof Container) {
+ Component[] kids = ((Container)root).getComponents();
+ for (int i=0;i < kids.length;i++) {
+ setDoubleBuffered(kids[i], buffered);
+ }
+ }
+ }
+
+ protected void setLayersTransparent(Window w, boolean transparent) {
+
+ Color bg = transparent ? new Color(0, 0, 0, 0) : null;
+ if (w instanceof RootPaneContainer) {
+ RootPaneContainer rpc = (RootPaneContainer)w;
+ JRootPane root = rpc.getRootPane();
+ JLayeredPane lp = root.getLayeredPane();
+ Container c = root.getContentPane();
+ JComponent content =
+ (c instanceof JComponent) ? (JComponent)c : null;
+ if (transparent) {
+ lp.putClientProperty(TRANSPARENT_OLD_OPAQUE, Boolean.valueOf(lp.isOpaque()));
+ lp.setOpaque(false);
+ root.putClientProperty(TRANSPARENT_OLD_OPAQUE, Boolean.valueOf(root.isOpaque()));
+ root.setOpaque(false);
+ if (content != null) {
+ content.putClientProperty(TRANSPARENT_OLD_OPAQUE, Boolean.valueOf(content.isOpaque()));
+ content.setOpaque(false);
+ }
+ root.putClientProperty(TRANSPARENT_OLD_BG,
+ root.getParent().getBackground());
+ }
+ else {
+ lp.setOpaque(Boolean.TRUE.equals(lp.getClientProperty(TRANSPARENT_OLD_OPAQUE)));
+ lp.putClientProperty(TRANSPARENT_OLD_OPAQUE, null);
+ root.setOpaque(Boolean.TRUE.equals(root.getClientProperty(TRANSPARENT_OLD_OPAQUE)));
+ root.putClientProperty(TRANSPARENT_OLD_OPAQUE, null);
+ if (content != null) {
+ content.setOpaque(Boolean.TRUE.equals(content.getClientProperty(TRANSPARENT_OLD_OPAQUE)));
+ content.putClientProperty(TRANSPARENT_OLD_OPAQUE, null);
+ }
+ bg = (Color)root.getClientProperty(TRANSPARENT_OLD_BG);
+ root.putClientProperty(TRANSPARENT_OLD_BG, null);
+ }
+ }
+ w.setBackground(bg);
+ }
+
+ /** Override this method to provide bitmap masking of the given
+ * heavyweight component.
+ */
+ protected void setMask(Component c, Raster raster) {
+ throw new UnsupportedOperationException("Window masking is not available");
+ }
+
+ /**
+ * Set the window mask based on the given Raster, which should
+ * be treated as a bitmap (zero/nonzero values only). A value of
+ * null
means to remove the mask.
+ */
+ protected void setWindowMask(Component w, Raster raster, int which) {
+ if (w.isLightweight())
+ throw new IllegalArgumentException("Component must be heavyweight: " + w);
+ setMask(w, raster, which);
+ }
+
+ /** Set the window mask based on a {@link Shape}. */
+ public void setWindowMask(Component w, Shape mask, int which) {
+ setWindowMask(w, toRaster(mask), which);
+ }
+
+ /**
+ * Set the window mask based on an Icon. All non-transparent
+ * pixels will be included in the mask.
+ */
+ public void setWindowMask(Component w, Icon mask) {
+ setWindowMask(w, toRaster(w, mask), Xext.ShapeBounding);
+ }
+
+ /**
+ * Use this method to ensure heavyweight popups are used in
+ * conjunction with a given window. This prevents the window's
+ * alpha setting or mask region from being applied to the popup.
+ */
+ protected void setForceHeavyweightPopups(Window w, boolean force) {
+ if (!(w instanceof HeavyweightForcer)) {
+ Window[] owned = w.getOwnedWindows();
+ for (int i = 0; i < owned.length; i++) {
+ if (owned[i] instanceof HeavyweightForcer) {
+ if (force)
+ return;
+ owned[i].dispose();
+ }
+ }
+ Boolean b = Boolean.valueOf(System.getProperty("jna.force_hw_popups", "true"));
+ if (force && b.booleanValue()) {
+ new HeavyweightForcer(w);
+ }
+ }
+ }
+
+ /**
+ * Obtains the set icon for the window associated with the specified
+ * window handle.
+ *
+ * @param hwnd
+ * The concerning window handle.
+ * @return Either the window's icon or {@code null} if an error
+ * occurred.
+ *
+ * @throws UnsupportedOperationException
+ * Thrown if this method wasn't yet implemented for the
+ * current platform.
+ */
+ protected BufferedImage getWindowIcon(final HWND hwnd) {
+ throw new UnsupportedOperationException("This platform is not supported, yet.");
+ }
+
+ /**
+ * Detects the size of an icon.
+ *
+ * @param hIcon
+ * The icon handle type.
+ * @return Either the requested icon's dimension or an {@link Dimension}
+ * instance of {@code (0, 0)}.
+ *
+ * @throws UnsupportedOperationException
+ * Thrown if this method wasn't yet implemented for the
+ * current platform.
+ */
+ protected Dimension getIconSize(final HICON hIcon) {
+ throw new UnsupportedOperationException("This platform is not supported, yet.");
+ }
+
+ /**
+ * Requests a list of all currently available Desktop windows.
+ *
+ * @param onlyVisibleWindows
+ * Specifies whether only currently visible windows will be
+ * considered ({@code true}). That are windows which are not
+ * minimized. The {@code WS_VISIBLE} flag will be checked
+ * (see: User32.IsWindowVisible(HWND)).
+ *
+ * @return A list with all windows and some detailed information.
+ *
+ * @throws UnsupportedOperationException
+ * Thrown if this method wasn't yet implemented for the
+ * current platform.
+ */
+ protected Listsun.java2d.noddraw
+ * is set
+ */
+ @Override
+ public boolean isWindowAlphaSupported() {
+ return Boolean.getBoolean("sun.java2d.noddraw");
+ }
+
+ /** Indicates whether UpdateLayeredWindow is in use. */
+ private boolean usingUpdateLayeredWindow(Window w) {
+ if (w instanceof RootPaneContainer) {
+ JRootPane root = ((RootPaneContainer)w).getRootPane();
+ return root.getClientProperty(TRANSPARENT_OLD_BG) != null;
+ }
+ return false;
+ }
+
+ /** Keep track of the alpha level, since we can't read it from
+ * the window itself.
+ */
+ private void storeAlpha(Window w, byte alpha) {
+ if (w instanceof RootPaneContainer) {
+ JRootPane root = ((RootPaneContainer)w).getRootPane();
+ Byte b = alpha == (byte)0xFF ? null : Byte.valueOf(alpha);
+ root.putClientProperty(TRANSPARENT_ALPHA, b);
+ }
+ }
+
+ /** Return the last alpha level we set on the window. */
+ private byte getAlpha(Window w) {
+ if (w instanceof RootPaneContainer) {
+ JRootPane root = ((RootPaneContainer)w).getRootPane();
+ Byte b = (Byte)root.getClientProperty(TRANSPARENT_ALPHA);
+ if (b != null) {
+ return b.byteValue();
+ }
+ }
+ return (byte)0xFF;
+ }
+
+ @Override
+ public void setWindowAlpha(final Window w, final float alpha) {
+ if (!isWindowAlphaSupported()) {
+ throw new UnsupportedOperationException("Set sun.java2d.noddraw=true to enable transparent windows");
+ }
+ whenDisplayable(w, new Runnable() {
+ @Override
+ public void run() {
+ HWND hWnd = getHWnd(w);
+ User32 user = User32.INSTANCE;
+ int flags = user.GetWindowLong(hWnd, WinUser.GWL_EXSTYLE);
+ byte level = (byte)((int)(255 * alpha) & 0xFF);
+ if (usingUpdateLayeredWindow(w)) {
+ // If already using UpdateLayeredWindow, continue to
+ // do so
+ BLENDFUNCTION blend = new BLENDFUNCTION();
+ blend.SourceConstantAlpha = level;
+ blend.AlphaFormat = WinUser.AC_SRC_ALPHA;
+ user.UpdateLayeredWindow(hWnd, null, null, null, null,
+ null, 0, blend,
+ WinUser.ULW_ALPHA);
+ }
+ else if (alpha == 1f) {
+ flags &= ~WinUser.WS_EX_LAYERED;
+ user.SetWindowLong(hWnd, WinUser.GWL_EXSTYLE, flags);
+ }
+ else {
+ flags |= WinUser.WS_EX_LAYERED;
+ user.SetWindowLong(hWnd, WinUser.GWL_EXSTYLE, flags);
+ user.SetLayeredWindowAttributes(hWnd, 0, level,
+ WinUser.LWA_ALPHA);
+ }
+ setForceHeavyweightPopups(w, alpha != 1f);
+ storeAlpha(w, level);
+ }
+ });
+ }
+
+ /** W32 makes the client responsible for repainting the entire
+ * window on any change. It also does not paint window decorations
+ * when the window is transparent.
+ */
+ private class W32TransparentContentPane extends TransparentContentPane {
+ private static final long serialVersionUID = 1L;
+ private HDC memDC;
+ private HBITMAP hBitmap;
+ private Pointer pbits;
+ private Dimension bitmapSize;
+ public W32TransparentContentPane(Container content) {
+ super(content);
+ }
+ private void disposeBackingStore() {
+ GDI32 gdi = GDI32.INSTANCE;
+ if (hBitmap != null) {
+ gdi.DeleteObject(hBitmap);
+ hBitmap = null;
+ }
+ if (memDC != null) {
+ gdi.DeleteDC(memDC);
+ memDC = null;
+ }
+ }
+ @Override
+ public void removeNotify() {
+ super.removeNotify();
+ disposeBackingStore();
+ }
+ @Override
+ public void setTransparent(boolean transparent) {
+ super.setTransparent(transparent);
+ if (!transparent) {
+ disposeBackingStore();
+ }
+ }
+ @Override
+ protected void paintDirect(BufferedImage buf, Rectangle bounds) {
+ // TODO: paint frame decoration if window is decorated
+ Window win = SwingUtilities.getWindowAncestor(this);
+ GDI32 gdi = GDI32.INSTANCE;
+ User32 user = User32.INSTANCE;
+ int x = bounds.x;
+ int y = bounds.y;
+ Point origin = SwingUtilities.convertPoint(this, x, y, win);
+ int w = bounds.width;
+ int h = bounds.height;
+ int ww = win.getWidth();
+ int wh = win.getHeight();
+ HDC screenDC = user.GetDC(null);
+ HANDLE oldBitmap = null;
+ try {
+ if (memDC == null) {
+ memDC = gdi.CreateCompatibleDC(screenDC);
+ }
+ if (hBitmap == null || !win.getSize().equals(bitmapSize)) {
+ if (hBitmap != null) {
+ gdi.DeleteObject(hBitmap);
+ hBitmap = null;
+ }
+ BITMAPINFO bmi = new BITMAPINFO();
+ bmi.bmiHeader.biWidth = ww;
+ bmi.bmiHeader.biHeight = wh;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = WinGDI.BI_RGB;
+ bmi.bmiHeader.biSizeImage = ww * wh * 4;
+ PointerByReference ppbits = new PointerByReference();
+ hBitmap = gdi.CreateDIBSection(memDC, bmi,
+ WinGDI.DIB_RGB_COLORS,
+ ppbits, null, 0);
+ pbits = ppbits.getValue();
+ bitmapSize = new Dimension(ww, wh);
+ }
+ oldBitmap = gdi.SelectObject(memDC, hBitmap);
+ Raster raster = buf.getData();
+ int[] pixel = new int[4];
+ int[] bits = new int[w];
+ for (int row = 0; row < h; row++) {
+ for (int col = 0; col < w; col++) {
+ raster.getPixel(col, row, pixel);
+ int alpha = (pixel[3] & 0xFF) << 24;
+ int red = (pixel[2] & 0xFF);
+ int green = (pixel[1] & 0xFF) << 8;
+ int blue = (pixel[0] & 0xFF) << 16;
+ bits[col] = alpha | red | green | blue;
+ }
+ int v = wh - (origin.y + row) - 1;
+ pbits.write((v*ww+origin.x)*4, bits, 0, bits.length);
+ }
+ SIZE winSize = new SIZE();
+ winSize.cx = win.getWidth();
+ winSize.cy = win.getHeight();
+ POINT winLoc = new POINT();
+ winLoc.x = win.getX();
+ winLoc.y = win.getY();
+ POINT srcLoc = new POINT();
+ BLENDFUNCTION blend = new BLENDFUNCTION();
+ HWND hWnd = getHWnd(win);
+ // extract current constant alpha setting, if possible
+ ByteByReference bref = new ByteByReference();
+ IntByReference iref = new IntByReference();
+ byte level = getAlpha(win);
+ try {
+ // GetLayeredwindowAttributes supported WinXP and later
+ if (user.GetLayeredWindowAttributes(hWnd, null, bref, iref)
+ && (iref.getValue() & WinUser.LWA_ALPHA) != 0) {
+ level = bref.getValue();
+ }
+ }
+ catch(UnsatisfiedLinkError e) {
+ }
+ blend.SourceConstantAlpha = level;
+ blend.AlphaFormat = WinUser.AC_SRC_ALPHA;
+ user.UpdateLayeredWindow(hWnd, screenDC, winLoc, winSize, memDC,
+ srcLoc, 0, blend, WinUser.ULW_ALPHA);
+ } finally {
+ user.ReleaseDC(null, screenDC);
+ if (memDC != null && oldBitmap != null) {
+ gdi.SelectObject(memDC, oldBitmap);
+ }
+ }
+ }
+ }
+
+ /** Note that w32 does not paint window decorations when
+ * the window is transparent.
+ */
+ @Override
+ public void setWindowTransparent(final Window w,
+ final boolean transparent) {
+ if (!(w instanceof RootPaneContainer)) {
+ throw new IllegalArgumentException("Window must be a RootPaneContainer");
+ }
+ if (!isWindowAlphaSupported()) {
+ throw new UnsupportedOperationException("Set sun.java2d.noddraw=true to enable transparent windows");
+ }
+ boolean isTransparent = w.getBackground() != null
+ && w.getBackground().getAlpha() == 0;
+ if (transparent == isTransparent)
+ return;
+ whenDisplayable(w, new Runnable() {
+ @Override
+ public void run() {
+ User32 user = User32.INSTANCE;
+ HWND hWnd = getHWnd(w);
+ int flags = user.GetWindowLong(hWnd, WinUser.GWL_EXSTYLE);
+ JRootPane root = ((RootPaneContainer)w).getRootPane();
+ JLayeredPane lp = root.getLayeredPane();
+ Container content = root.getContentPane();
+ if (content instanceof W32TransparentContentPane) {
+ ((W32TransparentContentPane)content).setTransparent(transparent);
+ }
+ else if (transparent) {
+ W32TransparentContentPane w32content =
+ new W32TransparentContentPane(content);
+ root.setContentPane(w32content);
+ lp.add(new RepaintTrigger(w32content),
+ JLayeredPane.DRAG_LAYER);
+ }
+ if (transparent && !usingUpdateLayeredWindow(w)) {
+ flags |= WinUser.WS_EX_LAYERED;
+ user.SetWindowLong(hWnd, WinUser.GWL_EXSTYLE, flags);
+ }
+ else if (!transparent && usingUpdateLayeredWindow(w)) {
+ flags &= ~WinUser.WS_EX_LAYERED;
+ user.SetWindowLong(hWnd, WinUser.GWL_EXSTYLE, flags);
+ }
+ setLayersTransparent(w, transparent);
+ setForceHeavyweightPopups(w, transparent);
+ setDoubleBuffered(w, !transparent);
+ }
+ });
+ }
+
+ @Override
+ public void setWindowMask(final Component w, final Shape mask, int which) {
+ if (mask instanceof Area && ((Area)mask).isPolygonal()) {
+ setMask(w, (Area)mask);
+ }
+ else {
+ super.setWindowMask(w, mask, which);
+ }
+ }
+
+ // NOTE: Deletes hrgn after setting the window region
+ private void setWindowRegion(final Component w, final HRGN hrgn) {
+ whenDisplayable(w, new Runnable() {
+ @Override
+ public void run() {
+ GDI32 gdi = GDI32.INSTANCE;
+ User32 user = User32.INSTANCE;
+ HWND hWnd = getHWnd(w);
+ try {
+ user.SetWindowRgn(hWnd, hrgn, true);
+ setForceHeavyweightPopups(getWindow(w), hrgn != null);
+ }
+ finally {
+ gdi.DeleteObject(hrgn);
+ }
+ }
+ });
+ }
+
+ // Take advantage of CreatePolyPolygonalRgn on w32
+ private void setMask(final Component w, final Area area) {
+ GDI32 gdi = GDI32.INSTANCE;
+ PathIterator pi = area.getPathIterator(null);
+ int mode = pi.getWindingRule() == PathIterator.WIND_NON_ZERO
+ ? WinGDI.WINDING: WinGDI.ALTERNATE;
+ float[] coords = new float[6];
+ Listapple.awt.draggableWindowBackground
must be set to its
+ * final value before the heavyweight peer for the Window is
+ * created. Once {@link Component#addNotify} has been called on the
+ * component, causing creation of the heavyweight peer, changing this
+ * property has no effect.
+ * @see Apple Technote 2007
+ */
+ @Override
+ public void setWindowTransparent(Window w, boolean transparent) {
+ boolean isTransparent = w.getBackground() != null
+ && w.getBackground().getAlpha() == 0;
+ if (transparent != isTransparent) {
+ setBackgroundTransparent(w, transparent, "setWindowTransparent");
+ }
+ }
+
+ /** Setting this false restores the original setting. */
+ private static final String WDRAG = "apple.awt.draggableWindowBackground";
+ private void fixWindowDragging(Window w, String context) {
+ if (w instanceof RootPaneContainer) {
+ JRootPane p = ((RootPaneContainer)w).getRootPane();
+ Boolean oldDraggable = (Boolean)p.getClientProperty(WDRAG);
+ if (oldDraggable == null) {
+ p.putClientProperty(WDRAG, Boolean.FALSE);
+ if (w.isDisplayable()) {
+ LOG.log(Level.WARNING, "{0}(): To avoid content dragging, {1}() must be called before the window is realized, or " + WDRAG + " must be set to Boolean.FALSE before the window is realized. If you really want content dragging, set " + WDRAG + " on the window''s root pane to Boolean.TRUE before calling {2}() to hide this message.",
+ new Object[]{context, context, context});
+ }
+ }
+ }
+ }
+
+ /** Note that the property
+ * apple.awt.draggableWindowBackground
must be set to its
+ * final value before the heavyweight peer for the Window is
+ * created. Once {@link Component#addNotify} has been called on the
+ * component, causing creation of the heavyweight peer, changing this
+ * property has no effect.
+ * @see Apple Technote 2007
+ */
+ @Override
+ public void setWindowAlpha(final Window w, final float alpha) {
+ if (w instanceof RootPaneContainer) {
+ JRootPane p = ((RootPaneContainer)w).getRootPane();
+ p.putClientProperty("Window.alpha", Float.valueOf(alpha));
+ fixWindowDragging(w, "setWindowAlpha");
+ }
+ whenDisplayable(w, new Runnable() {
+ @Override
+ public void run() {
+ try {
+ // This will work with old Apple AWT implementations and
+ // not with openjdk
+ Method getPeer = w.getClass().getMethod("getPeer");
+ Object peer = getPeer.invoke(w);
+ Method setAlpha = peer.getClass().getMethod("setAlpha", new Class[]{ float.class });
+ setAlpha.invoke(peer, Float.valueOf(alpha));
+ }
+ catch (Exception e) {
+ }
+ }
+ });
+ }
+
+ @Override
+ protected void setWindowMask(Component w, Raster raster, int which) {
+ if (raster != null) {
+ setWindowMask(w, toShape(raster), which);
+ }
+ else {
+ setWindowMask(w, new Rectangle(0, 0, w.getWidth(),
+ w.getHeight()), which);
+ }
+ }
+
+ @Override
+ public void setWindowMask(Component c, final Shape shape, int which) {
+ if (c instanceof Window) {
+ Window w = (Window)c;
+ OSXMaskingContentPane content = installMaskingPane(w);
+ content.setMask(shape);
+ setBackgroundTransparent(w, shape != MASK_NONE, "setWindowMask");
+ }
+ else {
+ // not yet implemented
+ }
+ }
+
+ /** Mask out unwanted pixels and ensure background gets cleared.
+ * @author Olivier Chafik
+ */
+ private static class OSXMaskingContentPane extends JPanel {
+ private static final long serialVersionUID = 1L;
+ private Shape shape;
+
+ public OSXMaskingContentPane(Component oldContent) {
+ super(new BorderLayout());
+ if (oldContent != null) {
+ add(oldContent, BorderLayout.CENTER);
+ }
+ }
+
+ public void setMask(Shape shape) {
+ this.shape = shape;
+ repaint();
+ }
+
+ @Override
+ public void paint(Graphics graphics) {
+ Graphics2D g = (Graphics2D)graphics.create();
+ g.setComposite(AlphaComposite.Clear);
+ g.fillRect(0, 0, getWidth(), getHeight());
+ g.dispose();
+ if (shape != null) {
+ g = (Graphics2D)graphics.create();
+ g.setClip(shape);
+ super.paint(g);
+ g.dispose();
+ }
+ else {
+ super.paint(graphics);
+ }
+ }
+ }
+
+ private void setBackgroundTransparent(Window w, boolean transparent, String context) {
+ JRootPane rp = w instanceof RootPaneContainer
+ ? ((RootPaneContainer)w).getRootPane() : null;
+ if (transparent) {
+ if (rp != null) {
+ rp.putClientProperty(TRANSPARENT_OLD_BG, w.getBackground());
+ }
+ w.setBackground(new Color(0,0,0,0));
+ }
+ else {
+ if (rp != null) {
+ Color bg = (Color)rp.getClientProperty(TRANSPARENT_OLD_BG);
+ // If the old bg is a
+ // apple.laf.CColorPaintUIResource, the window's
+ // transparent state will not change
+ if (bg != null) {
+ bg = new Color(bg.getRed(), bg.getGreen(), bg.getBlue(), bg.getAlpha());
+ }
+ w.setBackground(bg);
+ rp.putClientProperty(TRANSPARENT_OLD_BG, null);
+ }
+ else {
+ w.setBackground(null);
+ }
+ }
+ fixWindowDragging(w, context);
+ }
+ }
+ private static class X11WindowUtils extends NativeWindowUtils {
+ private static Pixmap createBitmap(final Display dpy,
+ X11.Window win,
+ Raster raster) {
+ final X11 x11 = X11.INSTANCE;
+ Rectangle bounds = raster.getBounds();
+ int width = bounds.x + bounds.width;
+ int height = bounds.y + bounds.height;
+ final Pixmap pm = x11.XCreatePixmap(dpy, win, width, height, 1);
+ final GC gc = x11.XCreateGC(dpy, pm, new NativeLong(0), null);
+ if (gc == null) {
+ return null;
+ }
+ x11.XSetForeground(dpy, gc, new NativeLong(0));
+ x11.XFillRectangle(dpy, pm, gc, 0, 0, width, height);
+ final Listsun.java2d.noddraw=true
+ * in order for alpha to work.
+ * NOTE: On OSX, the property
+ * apple.awt.draggableWindowBackground
must be set to its
+ * final value before the heavyweight peer for the Window is
+ * created. Once {@link Component#addNotify} has been called on the
+ * component, causing creation of the heavyweight peer, changing this
+ * property has no effect.
+ * @see Apple Technote 2007
+ */
+ public static void setWindowAlpha(Window w, float alpha) {
+ getInstance().setWindowAlpha(w, Math.max(0f, Math.min(alpha, 1f)));
+ }
+
+ /**
+ * Set the window to be transparent. Only explicitly painted pixels
+ * will be non-transparent. All pixels will be composited with
+ * whatever is under the window using their alpha values.
+ *
+ * On OSX, the property apple.awt.draggableWindowBackground
+ * must be set to its final value before the heavyweight peer for
+ * the Window is created. Once {@link Component#addNotify} has been
+ * called on the component, causing creation of the heavyweight peer,
+ * changing this property has no effect.
+ * @see Apple Technote 2007
+ */
+ public static void setWindowTransparent(Window w, boolean transparent) {
+ getInstance().setWindowTransparent(w, transparent);
+ }
+
+ /**
+ * Obtains the set icon for the window associated with the specified
+ * window handle.
+ *
+ * @param hwnd
+ * The concerning window handle.
+ * @return Either the window's icon or {@code null} if an error
+ * occurred.
+ */
+ public static BufferedImage getWindowIcon(final HWND hwnd) {
+ return getInstance().getWindowIcon(hwnd);
+ }
+
+ /**
+ * Detects the size of an icon.
+ *
+ * @param hIcon
+ * The icon handle type.
+ * @return Either the requested icon's dimension or an {@link Dimension}
+ * instance of {@code (0, 0)}.
+ */
+ public static Dimension getIconSize(final HICON hIcon) {
+ return getInstance().getIconSize(hIcon);
+ }
+
+ /**
+ * Requests a list of all currently available Desktop windows.
+ *
+ * @param onlyVisibleWindows
+ * Specifies whether only currently visible windows will be
+ * considered ({@code true}). That are windows which are not
+ * minimized. The {@code WS_VISIBLE} flag will be checked (see:
+ * User32.IsWindowVisible(HWND)).
+ *
+ * @return A list with all windows and some detailed information.
+ */
+ public static List