Translucent Swing Windows on Mac OS X

Many people have complained over the years (I know I did) about the lack of support in Swing/AWT for translucent and non-rectangular windows. There are various solutions to that problem. Mac OS X in particular, makes it easy to create translucent windows without calling into native code.

I have often seen that the solution is to set a translucent color on the window’s background, like so:

JFrame f = new JFrame("Translucent?");
f.setBackground(new Color(0.0f, 0.0f, 0.0f, 0.5f));

This works just fine when you want to remove the background altogether and create a non-rectangular shaped window; in which case the last parameter of the Color constructor should be 0.0f. But this piece of code fails miserably when you want to create a translucent window. First, it affects only the background of the window, and not its children. Then, this makes Swing go crazy and creates many repaint problems (damn old setOpaque() issue.)

Apple’s Java implementation offers a clean solution though and here it is:

public static void setWindowAlpha(Window w, float alpha) {
    ComponentPeer peer = w.getPeer();
    if (peer == null) {
        return;
    }
    Class< ? extends ComponentPeer> peerClass = peer.getClass();

    //noinspection EmptyCatchBlock
    try {
        Class< ?> nativeClass = Class.forName("apple.awt.CWindow");
        if (nativeClass.isAssignableFrom(peerClass)) {
            Method setAlpha = nativeClass.getMethod(
                    "setAlpha", float.class);
            setAlpha.invoke(peer, Math.max(0.0f, Math.min(alpha, 1.0f)));
      }
    } catch (ClassNotFoundException e) {
    } catch (NoSuchMethodException e) {
    } catch (IllegalAccessException e) {
    } catch (InvocationTargetException e) {
    }
}

The bits of reflection are there to let this method compile on any OS. Before calling this method though, you must make sure that the window is visible:

JFrame f = new JFrame("Translucent!");
f.setVisible(true);
setWindowAlpha(f, 0.5f);

Even though this method will fail gracefully on Linux and Windows, you probably need/want a more generic solution. You can use the excellent JNA API, a JNI wrapper, for that purpose. Among the examples, you’ll find a class called WindowUtils which contains methods to shape a window and make it translucent.

Unfortunately, the current implementation contains a bug on Mac OS X. They are using the first approach described above. What you need to do is download examples.jar, include it into your project, download the source code of WindowUtils.java and replace the setWindowAlpha() method in the MacWindowUtils inner class by the code above.

Finally, be careful with the method calls order. Windows and Linux require the window to be visible before you can shape it. Mac OS X on the contrary, needs the window to be invisible, at least with JNA’s implementation. Here is an example of how to make a translucent, non-rectangular window that works on all of the aforementioned OSes:

ShapedWindow window = new ShapedWindow();
if (!"Mac OS X".equals(System.getProperty("os.name"))) {
    window.setVisible(true);
}
WindowUtils.setWindowMask(window, createShape());
window.setVisible(true);
WindowUtils.setWindowAlpha(window, 0.5f);

And don’t forget to see how you can use JNA to easily make use of many interesting native libraries.

38 Responses to “Translucent Swing Windows on Mac OS X”

  1. Mark Fortner says:

    I thought this was handled by several SwingX components already (such as JXPanel, and JXGlassBox).

  2. Romain Guy says:

    Mark: They do but it’s different. We are talking about windows translucency, that is the ability to see through the entire frame. SwingX provides translucent components. The window remains opaque.

  3. Timothy Wall says:

    Hey! Stop advertising my bugs! Actually, window alpha was *supposed* to be unimplemented on OSX, and I accidentally checked in part of an unfinished hack.

    Anyhow, it is now implemented on OSX, thanks to Romain’s contribution, with the demo here http://jna.dev.java.net/demo/ShapedWindowDemo.jnlp

    With respect to applying a shape/alpha mask, what is required on all platforms is that the window be displayable, which basically means that its native peer has been instantiated.

  4. Romain Guy says:

    Timothy: Cool! Thanks. Note that I have encountered bugs while trying to shape a visible window on Mac OS X. And actually, a better solution would be to rely on addNotify() to know when a peer has been installed. That would be safer and more portable.

  5. Timothy Wall says:

    You generally want to change a windows attributes before it’s visible (you don’t want to show a square window and then trim off its edges). You can trigger on displayability by adding a HierarchyListener to the component in question if isDisplayable() returns false, and then checking the subsequent HierarchyEvent’s change flags.

  6. Romain Guy says:

    I do agree. It’s just that on Windows and Linux, setVisible(true) prior to shaping was the easiest path. By the way, any idea whether the support for translucent windows on Linux will soon be available? I’m trying to implement it but I can’t find example of C code showing how to achieve this with X.org’s Composite extension.

  7. Augusto says:

    In XP, the setAlpha() method doesn’t seem to do anything. At least when I try it.

    Shouldn’t this result on a transparent grey rectangle (in WinXP)?

    public static void main(String[] args)
    {
    JFrame f = new JFrame(“Test”);
    f.setUndecorated(true);
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    f.setSize(400,400);
    f.setVisible(true);

    com.sun.jna.examples.WindowUtils.setWindowMask(f,label.getIcon());com.sun.jna.examples.WindowUtils.setWindowAlpha(f, 0.5f);
    }

  8. Romain Guy says:

    Why the call to setWindowMask()?

  9. Augusto says:

    Because I can’t cut and paste! :-)

    Ignore that one line it was commented out (was trying something else) …
    com.sun.jna.examples.WindowUtils.setWindowAlpha(f, 0.5f);

  10. Augusto says:

    Well, looks like this is not supported on XP unless there’s something wrong with my machine …

    This:
    System.out.println(“Alpha supported? => ” +
    com.sun.jna.examples.WindowUtils.isWindowAlphaSupported());

    Results in:
    Alpha supported? => false

  11. Timothy Wall says:

    Romain,
    Unfortunately, on OSX you need Compiz (or beryl) to manage system-wide compositing. I haven’t been able to get a stable install on my linux box, so I haven’t been able to experiment. I haven’t looked at the source either, so I don’t know if their compositing methods can be extracted for general use.
    To date, most X11 wms have resorted to setting the transparency outside the application and have not considered allowing the application to control its *own* transparency.

    Augusto:
    You probably need to set -Dsun.java2d.noddraw=true when launching your VM. I’d also recommend setting the mask after pack but before setvisible(true).

  12. Timothy Wall says:

    Oops, I meant X11, not OSX.

  13. Romain Guy says:

    Timothy: That’s what I thought. Stupid Linux. That said, it is probably possible without Beryl/Compiz. There are applications, like mplayer, that let you set the translucency. However, they require you to enable the Composite extension in your X.org config file (it’s only three lines to add.)

  14. Augusto says:

    @Timothy

    “You probably need to set -Dsun.java2d.noddraw=true when launching your VM. I’d also recommend setting the mask after pack but before setvisible(true).”

    Thanks, found that out while reading the code. Should have thought of that first, I missed the is*Supported call.

    So if I want to do this:
    http://sellmic.com/blog/2007/04/07/60/

    So what i need here is a mask, if the images change the mask has to be updated too. What is the format of the mask? I see the Shape parameter, but there’s an icon one and the code has a raster implementation. Is it a bitmap with on/off values or a mask with 255 levels for transparency or what? thx!

  15. Augusto says:

    There’s something wrong with the comment system … my last comment was posted as the second one!

    Reposting again …

    @Timothy

    “You probably need to set -Dsun.java2d.noddraw=true when launching your VM. I’d also recommend setting the mask after pack but before setvisible(true).”

    Thanks, found that out while reading the code. Should have thought of that first, I missed the is*Supported call.

    So if I want to do this:
    http://sellmic.com/blog/2007/04/07/60/

    So what i need here is a mask, if the images change the mask has to be updated too. What is the format of the mask? I see the Shape parameter, but there’s an icon one and the code has a raster implementation. Is it a bitmap with on/off values or a mask with 255 levels for transparency or what? thx!

  16. Carl says:

    Should Sun put this in Java 1.7?
    JNA and Window shaping behavior.

    -Carl

  17. jcox says:

    Just curious: how are mouse events handled, are they passed to the underlying windowing system if the transparency is set or does Java intercept them across the entire rectangle bounds of the JFrame?

  18. cupofjoe says:

    This is very cool. If your webstart version works then you don’t have the jar and JVM values set up correctly like mentioned above.

    I struggled with this so if anyone is as stupid as I am. (doubtful) ;-)

    This might prove useful:

    You need both jar files in your path from the following location:
    https://jna.dev.java.net/servlets/ProjectDocumentList?folderID=7062&expandFolder=7062&folderID=6470

    Make sure you get BOTH the jna.jar AND the examples.jar! I was a little slow on figuring out I really DID need that examples jar file.

    Then like Romain suggested above.
    You MUST pass the JVM the following arguement: -Dsun.java2d.noddraw=true

    Failing to do this JVM command will cause the JFrame to be fully opaque.

    I’m running 1.6 on XP with an NVidia GeForce 6200 and have no problems.

  19. CommanderKeith says:

    Wow, that webstart clock is a really neat demo, nicw work Tim. Works fine here on Windows XP java 1.6. Thanks for pointing out this excellent development Romain. All we need is for this solution to be integrated into the JRE.

  20. Augusto says:

    Anybody know what win32 call to use to get a window to show transparency based on the alpha values of an image?

    A good example would be the latest Google Desktop sidebar. The window is a bit transparent with an overlay, while the components inside are solid.

  21. clueless says:

    Hi, I’m trying to figure out how to check if a window is the topmost window and I found this Windows API function:
    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/windows/windowreference/windowfunctions/gettopwindow.asp

    Here’s my code:

    public static boolean isTopmostWindow(Window w) {
    // A call to Pointer GetTopWindow(Pointer hWnd) in User32-interface;
    Pointer p = User32.INSTANCE.GetTopWindow(null);
    Pointer p2 = new Pointer(Native.getWindowHandle(w));

    // Never equals
    return p.equals(p2);
    }

    Both pointers seem to be valid, but they just never match. For example, I get this:
    Native Pointer vs Native Pointer

    Anyone have idea what I’m doing wrong?

  22. clueless says:

    Oh and about the frame translucency.. it worked nearly perfectly on my machine (AMD 2700+, Geforce 7600 GS, WinXP) – however, for some reason it’s quite ‘laggy’. Like the input events would come too late for the UI.

    For example when I hover menuitems, the hover state changes quite slowly. However, when I resize my app so that the menu is shown outside the JFrame (thus it’s not translucent anymore), it behaves normally again.

  23. paulo says:

    Trying to use this (jna) with default swing components inside the window seems not to work? Specifically i’d like to see if its possible to make a textcomponent background transparent and the window also, so that the text appears to float alone (and the opposite, text foregroud transparent and window). But it seems that the component is 1) not transparent, even after calling setopaque(false) and
    2) it produces artifacts in the painting? (the textcomponent content is not cleared of the backbuffer when the window is moved, and when the window is moved the components are not painted)

  24. Christophe says:

    hi,
    same request as Augusto : is it possible to make part of the windows half-translucent ?

    With the “setWindowMask” you can set part of the windows fully translucent.

    With the “setWindowAlpha” you can set the whole windows half translucent.

    But I have not found a way to show transparency based on the alpha values of an image for example. That would be great for shadows (for example).

  25. Jim says:

    >Then like Romain suggested above.
    >You MUST pass the JVM the following arguement: -Dsun.java2d.noddraw=true
    >
    >Failing to do this JVM command will cause the JFrame to be fully opaque.

    How does this look if one tries to do this at run time – is it this?

    java.lang.System.setProperty(“sun.java2d.noddraw”,”true”);

  26. Lele says:

    please help, I can’t run the setWindowAlpha class. I need a good solution for transparent background on OS X. If I use Swing alone everytime I update the text it makes a mess (but it does it correctly when I use a non-transparent background)

    Please post (or email) a small but complete class that shows your solution, including the import section.

    Thanks a lot!

    PS: If this worls, I don’t need to go for jna. Only OSX environment.

  27. Maxim says:

    I need a way to clear the background of a JFrame/JWindow and paint in it (paint(Graphics g))

    So i need to make a window transluent, but not the components within it. Is it possible?

  28. Roxanne Foley says:

    3vnom78mpx5jagz2

  29. Arun Kumar says:

    is any one there and reply to my previous post.

  30. your article help me a lot for my job.

  31. Alguel de vans para transporte escolar no Rio de Janeiro