All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.jna.platform.WindowUtils Maven / Gradle / Ivy

There is a newer version: 5.15.0
Show newest version
/* Copyright (c) 2007-2008 Timothy Wall, All Rights Reserved
 * Copyright (c) 2007 Olivier Chafik
 *
 * The contents of this file is dual-licensed under 2
 * alternative Open Source/Free licenses: LGPL 2.1 or later and
 * Apache License 2.0. (starting with JNA version 4.0.0).
 *
 * You can freely decide which license you want to apply to
 * the project.
 *
 * You may obtain a copy of the LGPL License at:
 *
 * http://www.gnu.org/licenses/licenses.html
 *
 * A copy is also included in the downloadable source code package
 * containing JNA, in file "LGPL2.1".
 *
 * You may obtain a copy of the Apache License at:
 *
 * http://www.apache.org/licenses/
 *
 * A copy is also included in the downloadable source code package
 * containing JNA, in file "AL2.0".
 */
package com.sun.jna.platform;

import java.awt.AWTEvent;
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.AWTEventListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.ContainerEvent;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Area;
import java.awt.geom.PathIterator;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.PopupFactory;
import javax.swing.RootPaneContainer;
import javax.swing.SwingUtilities;

import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.NativeLong;
import com.sun.jna.Platform;
import com.sun.jna.Pointer;
import com.sun.jna.platform.unix.X11;
import com.sun.jna.platform.unix.X11.Display;
import com.sun.jna.platform.unix.X11.GC;
import com.sun.jna.platform.unix.X11.Pixmap;
import com.sun.jna.platform.unix.X11.XVisualInfo;
import com.sun.jna.platform.unix.X11.Xext;
import com.sun.jna.platform.unix.X11.Xrender.XRenderPictFormat;
import com.sun.jna.platform.win32.GDI32;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.Kernel32Util;
import com.sun.jna.platform.win32.Psapi;
import com.sun.jna.platform.win32.PsapiUtil;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.Win32Exception;
import com.sun.jna.platform.win32.WinBase;
import com.sun.jna.platform.win32.WinDef.DWORDByReference;
import com.sun.jna.platform.win32.WinDef.HBITMAP;
import com.sun.jna.platform.win32.WinDef.HDC;
import com.sun.jna.platform.win32.WinDef.HICON;
import com.sun.jna.platform.win32.WinDef.HRGN;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.LPARAM;
import com.sun.jna.platform.win32.WinDef.LRESULT;
import com.sun.jna.platform.win32.WinDef.POINT;
import com.sun.jna.platform.win32.WinDef.RECT;
import com.sun.jna.platform.win32.WinDef.WPARAM;
import com.sun.jna.platform.win32.WinError;
import com.sun.jna.platform.win32.WinGDI;
import com.sun.jna.platform.win32.WinGDI.BITMAP;
import com.sun.jna.platform.win32.WinGDI.BITMAPINFO;
import com.sun.jna.platform.win32.WinGDI.BITMAPINFOHEADER;
import com.sun.jna.platform.win32.WinGDI.ICONINFO;
import com.sun.jna.platform.win32.WinNT;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.platform.win32.WinUser;
import com.sun.jna.platform.win32.WinUser.BLENDFUNCTION;
import com.sun.jna.platform.win32.WinUser.SIZE;
import com.sun.jna.platform.win32.WinUser.WNDENUMPROC;
import com.sun.jna.ptr.ByteByReference;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.PointerByReference;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Provides additional features on a Java {@link Window}.
 * 
    *
  • Non-rectangular shape (bitmap mask, no antialiasing) *
  • Transparency (constant alpha applied to window contents or * transparent background) *
  • Fully transparent window (the transparency of all painted pixels is * applied to the window). *
* NOTE: since there is no explicit way to force PopupFactory to use a * heavyweight popup, and anything but a heavyweight popup will be * clipped by a window mask, an additional subwindow is added to all * masked windows to implicitly force PopupFactory to use a heavyweight * window and avoid clipping. *

* 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 #setWindowMask(Window,Shape)} 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 WindowUtils { private static final Logger LOG = Logger.getLogger(WindowUtils.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 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) { if (w.isLightweight()) throw new IllegalArgumentException("Component must be heavyweight: " + w); setMask(w, raster); } /** Set the window mask based on a {@link Shape}. */ public void setWindowMask(Component w, Shape mask) { setWindowMask(w, toRaster(mask)); } /** * 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)); } /** * 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 List getAllWindows(final boolean onlyVisibleWindows) { throw new UnsupportedOperationException("This platform is not supported, yet."); } /** * Tries to obtain the Window's title which belongs to the specified * window handle. * * @param hwnd * The concerning window handle. * @return Either the title or an empty string of no title was found or * an error occurred. * * @throws UnsupportedOperationException * Thrown if this method wasn't yet implemented for the */ protected String getWindowTitle(final HWND hwnd) { throw new UnsupportedOperationException("This platform is not supported, yet."); } /** * Detects the full file path of the process associated with the specified * window handle. * * @param hwnd * The concerning window handle for which the PE file path is * required. * @return The full file path of the PE file that is associated with the * specified window handle. * * @throws UnsupportedOperationException * Thrown if this method wasn't yet implemented for the */ protected String getProcessFilePath(final HWND hwnd){ throw new UnsupportedOperationException("This platform is not supported, yet."); } /** * Requests the location and size of the window associated with the * specified window handle. * * @param hwnd * The concerning window handle. * @return The location and size of the window. * * @throws UnsupportedOperationException * Thrown if this method wasn't yet implemented for the */ protected Rectangle getWindowLocationAndSize(final HWND hwnd) { throw new UnsupportedOperationException("This platform is not supported, yet."); } } /** Canonical lazy loading of a singleton. */ private static class Holder { /** * Indicates whether a window must be visible before its native * handle can be obtained. This wart is caused by the Java * 1.4/X11 implementation. */ public static boolean requiresVisible; public static final NativeWindowUtils INSTANCE; static { if (Platform.isWindows()) { INSTANCE = new W32WindowUtils(); } else if (Platform.isMac()) { INSTANCE = new MacWindowUtils(); } else if (Platform.isX11()) { INSTANCE = new X11WindowUtils(); requiresVisible = System.getProperty("java.version") .matches("^1\\.4\\..*"); } else { String os = System.getProperty("os.name"); throw new UnsupportedOperationException("No support for " + os); } } } private static NativeWindowUtils getInstance() { return Holder.INSTANCE; } private static class W32WindowUtils extends NativeWindowUtils { private HWND getHWnd(Component w) { HWND hwnd = new HWND(); hwnd.setPointer(Native.getComponentPointer(w)); return hwnd; } /** * W32 alpha will only work if sun.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) { if (mask instanceof Area && ((Area)mask).isPolygonal()) { setMask(w, (Area)mask); } else { super.setWindowMask(w, mask); } } // 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]; List points = new ArrayList(); int size = 0; List sizes = new ArrayList(); while (!pi.isDone()) { int type = pi.currentSegment(coords); if (type == PathIterator.SEG_MOVETO) { size = 1; points.add(new POINT((int)coords[0], (int)coords[1])); } else if (type == PathIterator.SEG_LINETO) { ++size; points.add(new POINT((int)coords[0], (int)coords[1])); } else if (type == PathIterator.SEG_CLOSE) { sizes.add(Integer.valueOf(size)); } else { throw new RuntimeException("Area is not polygonal: " + area); } pi.next(); } POINT[] lppt = (POINT[])new POINT().toArray(points.size()); POINT[] pts = points.toArray(new POINT[points.size()]); for (int i=0;i < lppt.length;i++) { lppt[i].x = pts[i].x; lppt[i].y = pts[i].y; } int[] counts = new int[sizes.size()]; for (int i=0;i < counts.length;i++) { counts[i] = sizes.get(i).intValue(); } HRGN hrgn = gdi.CreatePolyPolygonRgn(lppt, counts, counts.length, mode); setWindowRegion(w, hrgn); } @Override protected void setMask(final Component w, final Raster raster) { GDI32 gdi = GDI32.INSTANCE; final HRGN region = raster != null ? gdi.CreateRectRgn(0, 0, 0, 0) : null; if (region != null) { final HRGN tempRgn = gdi.CreateRectRgn(0, 0, 0, 0); try { RasterRangesUtils.outputOccupiedRanges(raster, new RasterRangesUtils.RangesOutput() { @Override public boolean outputRange(int x, int y, int w, int h) { GDI32 gdi = GDI32.INSTANCE; gdi.SetRectRgn(tempRgn, x, y, x + w, y + h); return gdi.CombineRgn(region, region, tempRgn, WinGDI.RGN_OR) != WinGDI.ERROR; } }); } finally { gdi.DeleteObject(tempRgn); } } setWindowRegion(w, region); } @Override public BufferedImage getWindowIcon(final HWND hwnd) { // request different kind of icons if any solution fails final DWORDByReference hIconNumber = new DWORDByReference(); LRESULT result = User32.INSTANCE .SendMessageTimeout(hwnd, WinUser.WM_GETICON, new WPARAM(WinUser.ICON_BIG), new LPARAM(0), WinUser.SMTO_ABORTIFHUNG, 500, hIconNumber); if (result.intValue() == 0) result = User32.INSTANCE .SendMessageTimeout(hwnd, WinUser.WM_GETICON, new WPARAM(WinUser.ICON_SMALL), new LPARAM(0), WinUser.SMTO_ABORTIFHUNG, 500, hIconNumber); if (result.intValue() == 0) result = User32.INSTANCE .SendMessageTimeout(hwnd, WinUser.WM_GETICON, new WPARAM(WinUser.ICON_SMALL2), new LPARAM(0), WinUser.SMTO_ABORTIFHUNG, 500, hIconNumber); if (result.intValue() == 0) { result = new LRESULT(User32.INSTANCE .GetClassLongPtr(hwnd, WinUser.GCLP_HICON).intValue()); hIconNumber.getValue().setValue(result.intValue()); } if (result.intValue() == 0) { result = new LRESULT(User32.INSTANCE .GetClassLongPtr(hwnd, WinUser.GCLP_HICONSM).intValue()); hIconNumber.getValue().setValue(result.intValue()); } if (result.intValue() == 0) return null; // draw native icon into Java image final HICON hIcon = new HICON(new Pointer(hIconNumber.getValue() .longValue())); final Dimension iconSize = getIconSize(hIcon); if (iconSize.width == 0 || iconSize.height == 0) return null; final int width = iconSize.width; final int height = iconSize.height; final short depth = 24; final byte[] lpBitsColor = new byte[width * height * depth / 8]; final Pointer lpBitsColorPtr = new Memory(lpBitsColor.length); final byte[] lpBitsMask = new byte[width * height * depth / 8]; final Pointer lpBitsMaskPtr = new Memory(lpBitsMask.length); final BITMAPINFO bitmapInfo = new BITMAPINFO(); final BITMAPINFOHEADER hdr = new BITMAPINFOHEADER(); bitmapInfo.bmiHeader = hdr; hdr.biWidth = width; hdr.biHeight = height; hdr.biPlanes = 1; hdr.biBitCount = depth; hdr.biCompression = 0; hdr.write(); bitmapInfo.write(); final HDC hDC = User32.INSTANCE.GetDC(null); final ICONINFO iconInfo = new ICONINFO(); User32.INSTANCE.GetIconInfo(hIcon, iconInfo); iconInfo.read(); GDI32.INSTANCE.GetDIBits(hDC, iconInfo.hbmColor, 0, height, lpBitsColorPtr, bitmapInfo, 0); lpBitsColorPtr.read(0, lpBitsColor, 0, lpBitsColor.length); GDI32.INSTANCE.GetDIBits(hDC, iconInfo.hbmMask, 0, height, lpBitsMaskPtr, bitmapInfo, 0); lpBitsMaskPtr.read(0, lpBitsMask, 0, lpBitsMask.length); final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); int r, g, b, a, argb; int x = 0, y = height - 1; for (int i = 0; i < lpBitsColor.length; i = i + 3) { b = lpBitsColor[i] & 0xFF; g = lpBitsColor[i + 1] & 0xFF; r = lpBitsColor[i + 2] & 0xFF; a = 0xFF - lpBitsMask[i] & 0xFF; argb = (a << 24) | (r << 16) | (g << 8) | b; image.setRGB(x, y, argb); x = (x + 1) % width; if (x == 0) y--; } User32.INSTANCE.ReleaseDC(null, hDC); return image; } @Override public Dimension getIconSize(final HICON hIcon) { final ICONINFO iconInfo = new ICONINFO(); try { if (!User32.INSTANCE.GetIconInfo(hIcon, iconInfo)) return new Dimension(); iconInfo.read(); final BITMAP bmp = new BITMAP(); if (iconInfo.hbmColor != null && iconInfo.hbmColor.getPointer() != Pointer.NULL) { final int nWrittenBytes = GDI32.INSTANCE.GetObject( iconInfo.hbmColor, bmp.size(), bmp.getPointer()); bmp.read(); if (nWrittenBytes > 0) return new Dimension(bmp.bmWidth.intValue(), bmp.bmHeight.intValue()); } else if (iconInfo.hbmMask != null && iconInfo.hbmMask.getPointer() != Pointer.NULL) { final int nWrittenBytes = GDI32.INSTANCE.GetObject( iconInfo.hbmMask, bmp.size(), bmp.getPointer()); bmp.read(); if (nWrittenBytes > 0) return new Dimension(bmp.bmWidth.intValue(), bmp.bmHeight.intValue() / 2); } } finally { if (iconInfo.hbmColor != null && iconInfo.hbmColor.getPointer() != Pointer.NULL) GDI32.INSTANCE.DeleteObject(iconInfo.hbmColor); if (iconInfo.hbmMask != null && iconInfo.hbmMask.getPointer() != Pointer.NULL) GDI32.INSTANCE.DeleteObject(iconInfo.hbmMask); } return new Dimension(); } @Override public List getAllWindows(final boolean onlyVisibleWindows) { final List result = new LinkedList(); final WNDENUMPROC lpEnumFunc = new WNDENUMPROC() { @Override public boolean callback(final HWND hwnd, final Pointer arg1) { try { final boolean visible = !onlyVisibleWindows || User32.INSTANCE.IsWindowVisible(hwnd); if (visible) { final String title = getWindowTitle(hwnd); final String filePath = getProcessFilePath(hwnd); final Rectangle locAndSize = getWindowLocationAndSize(hwnd); result.add(new DesktopWindow(hwnd, title, filePath, locAndSize)); } } catch (final Exception e) { // FIXME properly handle whatever error is raised e.printStackTrace(); } return true; } }; if (!User32.INSTANCE.EnumWindows(lpEnumFunc, null)) throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); return result; } @Override public String getWindowTitle(final HWND hwnd) { final int requiredLength = User32.INSTANCE .GetWindowTextLength(hwnd) + 1; final char[] title = new char[requiredLength]; final int length = User32.INSTANCE.GetWindowText(hwnd, title, title.length); return Native.toString(Arrays.copyOfRange(title, 0, length)); } @Override public String getProcessFilePath(final HWND hwnd) { final IntByReference pid = new IntByReference(); User32.INSTANCE.GetWindowThreadProcessId(hwnd, pid); // GetProcessImageFileName requires PROCESS_QUERY_INFORMATION on // older windows versions so try that first. If we fail to get // access to that information fallback to // PROCESS_QUERY_LIMITED_INFORMATION. This allows reading image // paths from processes running with elevated privileges (at least // worked successfully for a setup program started from a network // share) HANDLE process = Kernel32.INSTANCE.OpenProcess( WinNT.PROCESS_QUERY_INFORMATION, false, pid.getValue()); if (process == null) { if(Kernel32.INSTANCE.GetLastError() != WinNT.ERROR_ACCESS_DENIED) { throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); } else { process = Kernel32.INSTANCE.OpenProcess( WinNT.PROCESS_QUERY_LIMITED_INFORMATION, false, pid.getValue()); if (process == null) { if (Kernel32.INSTANCE.GetLastError() != WinNT.ERROR_ACCESS_DENIED) { throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); } else { // Ignore windows, that can't be accessed return ""; } } } } try { String processImagePath = PsapiUtil.GetProcessImageFileName(process); // GetProcessImageFileName returns the file name as a device // filename in the form \Device\Harddisk5\. To make this more // familiar and keep compatibility with QueryModuleName try to // map back to known path (DOS path or UNC path) // Map Mup to UNC path if(processImagePath.startsWith("\\Device\\Mup\\")) { return "\\" + processImagePath.substring(11); } // Format of FindFirstVolume is // \\?\Volume{00000000-0000-0000-0000-000000000000}\ char[] volumeUUID = new char[50]; HANDLE h = Kernel32.INSTANCE.FindFirstVolume(volumeUUID, 50); if (h == null || h.equals(WinBase.INVALID_HANDLE_VALUE)) { throw new Win32Exception(Native.getLastError()); } try { do { String volumePath = Native.toString(volumeUUID); for (String s : Kernel32Util.getVolumePathNamesForVolumeName(volumePath)) { if (s.matches("[a-zA-Z]:\\\\")) { for (String path : Kernel32Util.queryDosDevice(s.substring(0, 2), 1024)) { if(processImagePath.startsWith(path)) { return s + processImagePath.substring(path.length() + 1); } } } } } while (Kernel32.INSTANCE.FindNextVolume(h, volumeUUID, 50)); if (Native.getLastError() != WinError.ERROR_NO_MORE_FILES) { throw new Win32Exception(Native.getLastError()); } } finally { Kernel32.INSTANCE.FindVolumeClose(h); } return processImagePath; } finally { Kernel32.INSTANCE.CloseHandle(process); } } @Override public Rectangle getWindowLocationAndSize(final HWND hwnd) { final RECT lpRect = new RECT(); if (!User32.INSTANCE.GetWindowRect(hwnd, lpRect)) throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); return new Rectangle(lpRect.left, lpRect.top, Math.abs(lpRect.right - lpRect.left), Math.abs(lpRect.bottom - lpRect.top)); } } private static class MacWindowUtils extends NativeWindowUtils { @Override public boolean isWindowAlphaSupported() { return true; } private OSXMaskingContentPane installMaskingPane(Window w) { OSXMaskingContentPane content; if (w instanceof RootPaneContainer) { // TODO: replace layered pane instead? final RootPaneContainer rpc = (RootPaneContainer)w; Container oldContent = rpc.getContentPane(); if (oldContent instanceof OSXMaskingContentPane) { content = (OSXMaskingContentPane)oldContent; } else { content = new OSXMaskingContentPane(oldContent); // TODO: listen for content pane changes rpc.setContentPane(content); } } else { Component oldContent = w.getComponentCount() > 0 ? w.getComponent(0) : null; if (oldContent instanceof OSXMaskingContentPane) { content = (OSXMaskingContentPane)oldContent; } else { content = new OSXMaskingContentPane(oldContent); w.add(content); } } return content; } /** 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 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) { if (raster != null) { setWindowMask(w, toShape(raster)); } else { setWindowMask(w, new Rectangle(0, 0, w.getWidth(), w.getHeight())); } } @Override public void setWindowMask(Component c, final Shape shape) { 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 List rlist = new ArrayList(); try { RasterRangesUtils.outputOccupiedRanges(raster, new RasterRangesUtils.RangesOutput() { @Override public boolean outputRange(int x, int y, int w, int h) { rlist.add(new Rectangle(x, y, w, h)); return true; } }); X11.XRectangle[] rects = (X11.XRectangle[]) new X11.XRectangle().toArray(rlist.size()); for (int i=0;i < rects.length;i++) { Rectangle r = rlist.get(i); rects[i].x = (short)r.x; rects[i].y = (short)r.y; rects[i].width = (short)r.width; rects[i].height = (short)r.height; // Optimization: write directly to native memory Pointer p = rects[i].getPointer(); p.setShort(0, (short)r.x); p.setShort(2, (short)r.y); p.setShort(4, (short)r.width); p.setShort(6, (short)r.height); rects[i].setAutoSynch(false); // End optimization } final int UNMASKED = 1; x11.XSetForeground(dpy, gc, new NativeLong(UNMASKED)); x11.XFillRectangles(dpy, pm, gc, rects, rects.length); } finally { x11.XFreeGC(dpy, gc); } return pm; } private boolean didCheck; private long[] alphaVisualIDs = {}; @Override public boolean isWindowAlphaSupported() { return getAlphaVisualIDs().length > 0; } private static long getVisualID(GraphicsConfiguration config) { // Use reflection to call // X11GraphicsConfig.getVisual try { Object o = config.getClass() .getMethod("getVisual", (Class[])null) .invoke(config, (Object[])null); return ((Number)o).longValue(); } catch (Exception e) { // FIXME properly handle this error e.printStackTrace(); return -1; } } /** Return the default graphics configuration. */ @Override public GraphicsConfiguration getAlphaCompatibleGraphicsConfiguration() { if (isWindowAlphaSupported()) { GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice[] devices = env.getScreenDevices(); for (int i = 0; i < devices.length; i++) { GraphicsConfiguration[] configs = devices[i].getConfigurations(); for (int j = 0; j < configs.length; j++) { long visualID = getVisualID(configs[j]); long[] ids = getAlphaVisualIDs(); for (int k = 0; k < ids.length; k++) { if (visualID == ids[k]) { return configs[j]; } } } } } return super.getAlphaCompatibleGraphicsConfiguration(); } /** * Return the visual ID of the visual which supports an alpha * channel. */ private synchronized long[] getAlphaVisualIDs() { if (didCheck) { return alphaVisualIDs; } didCheck = true; X11 x11 = X11.INSTANCE; Display dpy = x11.XOpenDisplay(null); if (dpy == null) return alphaVisualIDs; XVisualInfo info = null; try { int screen = x11.XDefaultScreen(dpy); XVisualInfo template = new XVisualInfo(); template.screen = screen; template.depth = 32; template.c_class = X11.TrueColor; NativeLong mask = new NativeLong(X11.VisualScreenMask | X11.VisualDepthMask | X11.VisualClassMask); IntByReference pcount = new IntByReference(); info = x11.XGetVisualInfo(dpy, mask, template, pcount); if (info != null) { List list = new ArrayList(); XVisualInfo[] infos = (XVisualInfo[])info.toArray(pcount.getValue()); for (int i = 0; i < infos.length; i++) { XRenderPictFormat format = X11.Xrender.INSTANCE.XRenderFindVisualFormat(dpy, infos[i].visual); if (format.type == X11.Xrender.PictTypeDirect && format.direct.alphaMask != 0) { list.add(infos[i].visualid); } } alphaVisualIDs = new long[list.size()]; for (int i=0;i < alphaVisualIDs.length;i++) { alphaVisualIDs[i] = ((Number)list.get(i)).longValue(); } return alphaVisualIDs; } } finally { if (info != null) { x11.XFree(info.getPointer()); } x11.XCloseDisplay(dpy); } return alphaVisualIDs; } private static X11.Window getContentWindow(Window w, X11.Display dpy, X11.Window win, Point offset) { if ((w instanceof Frame && !((Frame)w).isUndecorated()) || (w instanceof Dialog && !((Dialog)w).isUndecorated())) { X11 x11 = X11.INSTANCE; X11.WindowByReference rootp = new X11.WindowByReference(); X11.WindowByReference parentp = new X11.WindowByReference(); PointerByReference childrenp = new PointerByReference(); IntByReference countp = new IntByReference(); x11.XQueryTree(dpy, win, rootp, parentp, childrenp, countp); Pointer p = childrenp.getValue(); int[] ids = p.getIntArray(0, countp.getValue()); for (int id : ids) { // TODO: more verification of correct window? X11.Window child = new X11.Window(id); X11.XWindowAttributes xwa = new X11.XWindowAttributes(); x11.XGetWindowAttributes(dpy, child, xwa); offset.x = -xwa.x; offset.y = -xwa.y; win = child; break; } if (p != null) { x11.XFree(p); } } return win; } private static X11.Window getDrawable(Component w) { int id = (int)Native.getComponentID(w); if (id == X11.None) return null; return new X11.Window(id); } private static final long OPAQUE = 0xFFFFFFFFL; private static final String OPACITY = "_NET_WM_WINDOW_OPACITY"; @Override public void setWindowAlpha(final Window w, final float alpha) { if (!isWindowAlphaSupported()) { throw new UnsupportedOperationException("This X11 display does not provide a 32-bit visual"); } Runnable action = new Runnable() { @Override public void run() { X11 x11 = X11.INSTANCE; Display dpy = x11.XOpenDisplay(null); if (dpy == null) return; try { X11.Window win = getDrawable(w); if (alpha == 1f) { x11.XDeleteProperty(dpy, win, x11.XInternAtom(dpy, OPACITY, false)); } else { int opacity = (int)((long)(alpha * OPAQUE) & 0xFFFFFFFF); IntByReference patom = new IntByReference(opacity); x11.XChangeProperty(dpy, win, x11.XInternAtom(dpy, OPACITY, false), X11.XA_CARDINAL, 32, X11.PropModeReplace, patom.getPointer(), 1); } } finally { x11.XCloseDisplay(dpy); } } }; whenDisplayable(w, action); } private class X11TransparentContentPane extends TransparentContentPane { private static final long serialVersionUID = 1L; public X11TransparentContentPane(Container oldContent) { super(oldContent); } private Memory buffer; private int[] pixels; private final int[] pixel = new int[4]; // Painting directly to the original Graphics // fails to properly composite unless the destination // is pure black. Too bad. @Override protected void paintDirect(BufferedImage buf, Rectangle bounds) { Window window = SwingUtilities.getWindowAncestor(this); X11 x11 = X11.INSTANCE; X11.Display dpy = x11.XOpenDisplay(null); X11.Window win = getDrawable(window); Point offset = new Point(); win = getContentWindow(window, dpy, win, offset); X11.GC gc = x11.XCreateGC(dpy, win, new NativeLong(0), null); Raster raster = buf.getData(); int w = bounds.width; int h = bounds.height; if (buffer == null || buffer.size() != w*h*4) { buffer = new Memory(w*h*4); pixels = new int[w*h]; } for (int y=0;y * NOTE: Windows requires that sun.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 getAllWindows( final boolean onlyVisibleWindows) { return getInstance().getAllWindows(onlyVisibleWindows); } /** * Tries to obtain the Window's title which belongs to the specified window * handle. * * @param hwnd * The concerning window handle. * @return Either the title or an empty string of no title was found or an * error occurred. */ public static String getWindowTitle(final HWND hwnd) { return getInstance().getWindowTitle(hwnd); } /** * Detects the full file path of the process associated with the specified * window handle. * * @param hwnd * The concerning window handle for which the PE file path is * required. * @return The full file path of the PE file that is associated with the * specified window handle. */ public static String getProcessFilePath(final HWND hwnd) { return getInstance().getProcessFilePath(hwnd); } /** * Requests the location and size of the window associated with the * specified window handle. * * @param hwnd * The concerning window handle. * @return The location and size of the window. */ public static Rectangle getWindowLocationAndSize(final HWND hwnd) { return getInstance().getWindowLocationAndSize(hwnd); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy