Friday, February 23, 2007

Non-rectangular Windows

I've been meaning to play around with shaped windows for a while, but didn't relish the thought of walking through the tedium of JNI configurations and builds. Doing it on one platform is bad enough, but on several? No thanks.

So I thought I'd write a little scriptable shared library access stub once and be done with JNI entirely. Well, turns out it's already been done. Several times.

JNative
NLink
JNA

JNative has some interesting features not found in the others, but actually using it is only slightly better than JNI. NLink is currently w32-only, and has a bit of a COM bent. JNA fit most closely with my objectives, already had implementations for w32 and solaris, and was already platform-agnostic. So I took a couple days to hack in some more features (mostly to get an understanding of the codebase), and here is what I got. The following code is what it takes to make a frame take an arbitrary shape.

User32 user32 = User32.INSTANCE;
GDI32 gdi32 = GDI32.INSTANCE;
JFrame frame = new JFrame(getName());
Pointer p = gdi32.CreateRoundRectRgn(0, -150, 300, 300, 300, 300);
int hWnd = user32.FindWindowA(null, getName());
user32.SetWindowRgn(hWnd, p, true);

Looking up the appropriate w32 calls probably took the most time. How are the w32 libraries defined? How is this for trivial:

public interface User32 extends StdCallLibarary {
User32 INSTANCE = (User32)Native.loadLibary("user32", User32.class);
int FindWindowA(String winClass, String title);
void setWindowRgn(int hWnd, Pointer p, boolean redraw);
}
public interface GDI32 extends StdCallLibrary {
GDI32 INSTANCE = (GDI32)Native.loadLibrary("gdi32", GDI32.class);
Pointer CreateRoundRectRgn(int left, int top, int right, int bottom, int w, int h);
}

Now, somebody's probably going to point me to how SWT has had this functionality for years (does it?), but this is nicely abstracted and based on a very small number of classes. I'll be updating the code at jna.dev.java.net (or maybe opening a new location if I can't get the existing project moved to subversion), but for now, check out the demo by clicking on everyone's favorite orange launch button.




Permissions required, because this runs some custom native code.

Oh, BTW, this is windows-only for the moment. I'll do X11 next and anyone's free to send me some code snippets for setting window masks on other platforms. I could also use some help porting to PPC and other platforms (a very small amount of native ASM to push arguments to the stack, not too hard).

12 comments:

Anonymous said...

To create transparent or shaped Frames on MacOS X you don't need any native code, just do setBackground(new Color(0, 0, 0, 0)) on the Frame.
(Note: Although it works on my maschine, this is a hack and it doesn't work correctly all the time!)

technomage said...

I'm aware of that transparency "feature"; it doesn't actually shape the window, though (every click in the original rectangle still goes to the window). You also have to perform more hackery to make part of your window non-transparent.

Anonymous said...

Well, maybe this behaviour differs on each version of mac os/java but for me it shapes the window too! You just need to set a Content Pane with a custom paintComponent() method. The areas you do not fill won't receive clicks. By setting a fill-color with an alpha channel you can also make the window partially translucent.

technomage said...

After a bit of scrounging about, I discovered SWT does not have any such functionality. Seems they could really use it, since it would considerably reduce the current JNI overhead (they do a 1-1 java to native method mapping, through JNI).

Unknown said...

I'm glad someone is finally looking into this :) and is willing to make it multi platform.

I implemented such non-rectangular window a while back in my Skin Look And Feel (http://skinlf.l2fprod.com) but I did not maintained it and it does not cope very well with newer JREs. One feature I had and which I like was the ability to create the window region from an image. See this demo: http://www.l2fprod.com/software/skinlf/jnlp/clock.jnlp
(from http://skinlf.l2fprod.com/tutorials/page4/tutorial-clock.php)

-fred

Anonymous said...

What if the window is not a simple, basic shape - for example, a rectangle but with another rectangle jutting out of it? Or, with notched regions cut into it?

technomage said...

See the java classes Shape and Area. You can make an arbitrarily complex Shape which doesn't even need to have contiguous pixels.

Anonymous said...

Thanks for your nice post!

Anonymous said...

This is very interesting, but I can't get it to run (Error loading JPA libraries). Can you post the full source code somewhere?

Anonymous said...

Hey how come i cant get this to run!
I would love to use this i think its amazing.

Help! Thanks in Advance,
Cheers,
Claudia

technomage said...

Get the latest from the JNA website, http://jna.dev.java.net.

Tinting Toronto said...

Nice post.Works on my mac.