Vista glass in SWT application - continuation
In my previous post about Vista glass I’ve introduced very simple way to enable DWM glass in SWT. Since that time, I did a lot of experiments how one can improve that experience, and I’ve found solutions to almost all issues with DWM/Glass/Vista in SWT. This post became much bigger than I’ve anticipated, therefore it’s divided into “text after more…” :-)
First, let’s see some screenshot of application in work:

Above you see an application running on Windows Vista with DWM and Glass enabled. That part was covered in previous post, but since then I was able to place native widgets on glass without alpha blending of black (and gray) regions. Trick is rather complicated, explaining without an example code would be problematic, so let us see how this widget looks like (beware, it’s work in progress code, and I’m not yet very proficient in writing native widgets in SWT, some errors/memory leaks could be there).
{
private boolean glass;
public OpaqueText(Composite parent, int style)
{
this(parent, style, false);
}
public OpaqueText(Composite parent, int style, boolean glass)
{
super(parent, style);
this.glass = glass;
if (glass)
{
OS.BufferedPaintInit();
}
}
int /* long */callWindowProc(int /* long */hwnd, int msg, int /* long */wParam,
int /* long */lParam)
{
if (handle == 0)
{
return 0;
}
if (!glass)
{
return super.callWindowProc(hwnd, msg, wParam, lParam);
}
boolean redraw = false;
switch (msg)
{
case OS.WM_ERASEBKGND:
{
if (findImageControl() != null)
{
return 0;
}
break;
}
case OS.WM_HSCROLL:
case OS.WM_VSCROLL:
{
redraw = findImageControl() != null && drawCount == 0 && OS.IsWindowVisible(handle);
if (redraw)
{
OS.DefWindowProc(handle, OS.WM_SETREDRAW, 0, 0);
}
break;
}
case OS.WM_PAINT:
{
int /* long */paintDC = 0;
PAINTSTRUCT ps = new PAINTSTRUCT();
paintDC = OS.BeginPaint(handle, ps);
int width = ps.right - ps.left;
int height = ps.bottom - ps.top;
if (width != 0 && height != 0)
{
int /* long */[] phdc = new int /* long */[1];
RECT prcTarget = new RECT();
OS.SetRect(prcTarget, ps.left, ps.top, ps.right, ps.bottom);
int flags = OS.BPBF_TOPDOWNDIB;
int /* long */hBufferedPaint =
OS.BeginBufferedPaint(paintDC, prcTarget, flags, null, phdc);
int /* long */hDC = paintDC;
hDC = phdc[0];
OS.CallWindowProc(EditProc, hwnd, OS.WM_PAINT, hDC, lParam);
OS.BufferedPaintSetAlpha(hBufferedPaint, prcTarget, (byte) 255);
OS.EndBufferedPaint(hBufferedPaint, true);
}
OS.EndPaint(handle, ps);
break;
}
}
int /* long */code = OS.CallWindowProc(EditProc, hwnd, msg, wParam, lParam);
switch (msg)
{
case OS.WM_HSCROLL:
case OS.WM_VSCROLL:
{
if (redraw)
{
OS.DefWindowProc(handle, OS.WM_SETREDRAW, 1, 0);
OS.InvalidateRect(handle, null, true);
OS.UpdateWindow(handle);
}
break;
}
}
return code;
}
LRESULT wmCommandChild(int /* long */wParam, int /* long */lParam)
{
if (!glass)
{
return super.wmCommandChild(wParam, lParam);
}
int code = OS.HIWORD(wParam);
switch (code)
{
case OS.EN_CHANGE:
{
OS.InvalidateRect(handle, null, false);
OS.UpdateWindow(handle);
sendEvent(SWT.Modify);
if (isDisposed())
{
return LRESULT.ZERO;
}
return null;
}
}
return super.wmCommandChild(wParam, lParam);
}
}
Bottom line is, we need to overwrite native widget WM_PAINT message and introduce buffered painting with alpha capabilities. Method callWindowProc() is a bit trimmed down from original Text.callWindowProc() but additional functionalities were not needed for purpose of this example. Another change that had to be made specifically to Text widget was its handling of EN_CHANGE message, without invalidating and updating content of native widget, entire widget was falling back to not buffered paint until new WM_PAINT message was dispatched. Following this pattern, we’re able to place any native widget on glass without loosing its correct painting (as was hinted by Steve in 3rd comment).
Having this done, example application started to look how it should be in glass - the Search text field was now correctly painted and I was able to continue my work on handling scenario when DWM is disabled, but default Vista theme is enabled. You may notice that Explorer (even Internet Explorer > 7) is falling back very nicely to extended chrome (here I will call it header pane):

Very nice indeed, folks that don’t have DWM and glass enabled still have same user interface, just a little bit differently painted. So how we can achieve similar effect in SWT. Well, it’s also very complicated, and showing once again code example would be easier. This time it’ll be two classes that we need to write.
First class is GlassAwareShell that we need to use instead of original Shell class:
{
public static final int DWM_CHANGE = 50;
public static final int WM_DWMCOMPOSITIONCHANGED = 0×31e;
protected boolean activeValue = true;
public GlassAwareComposite header = null;
public GlassAwareShell(Display display, int style)
{
super(display, style);
}
@Override
LRESULT WM_ACTIVATE(int wParam, int lParam)
{
header.setActive(activeValue = OS.LOWORD(wParam) != 0);
if (header != null)
{
RECT rect = new RECT();
rect.right = header.getClientArea().width;
rect.bottom = header.getClientArea().height;
OS.InvalidateRect(header.handle, rect, false);
OS.UpdateWindow(header.handle);
}
return super.WM_ACTIVATE(wParam, lParam);
}
@Override
int windowProc(int hwnd, int msg, int wParam, int lParam)
{
if (handle == 0)
{
return 0;
}
if (hwnd != handle)
{
return super.windowProc(hwnd, msg, wParam, lParam);
}
switch (msg)
{
case WM_DWMCOMPOSITIONCHANGED:
{
postEvent(DWM_CHANGE);
return 0;
}
}
return super.windowProc(hwnd, msg, wParam, lParam);
}
public void setHeader(GlassAwareComposite header)
{
this.header = header;
}
}
New shell provides new method to store header inside shell object, it’s useful in requesting for invalidation and update of its native widget. What’s important is WM_ACTIVATE message handling method. Essentially in default theme, colors change when window is focused or blurred, we need to handle this scenario also. Here I’ve faced a big roadblock. After sending InvalidateRect message to header native widget I had big delays between painting chrome (windows border) and header pane, so noticeable that it was unacceptable. After days of studying MSDN I found that one can request WM_PAINT without going to dispatcher queue using UpdateWindow method on header. That was a big relief, because header started to paint itself immediately after border (still there’s sometimes noticeable, few milliseconds of delay, but I found this delay in Internet Explorer also). Last thing that was added to this shell is listener DWM_CHANGE that is posted when WM_DWMCOMPOSITIONCHANGED message is received by shell. This message is not declared in OS, therefore I’ve added it at the top of the GlassAwareShell class. Message is sent by operating system, when there’s a change of DWM state (disabled/enabled).
Now we need to implement this header pane for ourselves. It’s not provided. Here’s the example code:
{
private final int WP_FRAMELEFT = 7;
private final int FS_ACTIVE = 1;
private final int FS_INACTIVE = 2;
private boolean activeValue = true;
private int hWindowTheme;
private boolean dwmGlass;
private boolean classicTheme;
private boolean windowsVistaOrLater;
public GlassAwareComposite(Shell shell, int style)
{
super(shell, style);
}
@Override
LRESULT WM_PAINT(int wParam, int lParam)
{
if (dwmGlass)
{
return wmPaintDwmGlass();
}
else if (windowsVistaOrLater)
{
return wmPaintVistaOrLaterGlassDisabled();
}
return super.WM_PAINT(wParam, lParam);
}
private LRESULT wmPaintDwmGlass()
{
PAINTSTRUCT ps = new PAINTSTRUCT();
int hDC = OS.BeginPaint(handle, ps);
RECT rect = new RECT();
rect.left = 0;
rect.top = 0;
rect.right = getClientArea().width;
rect.bottom = getClientArea().height;
int hBrush = OS.GetStockObject(OS.BLACK_BRUSH);
OS.FillRect(hDC, rect, hBrush);
OS.EndPaint(handle, ps);
return LRESULT.ZERO;
}
private LRESULT wmPaintVistaOrLaterGlassDisabled()
{
if (hWindowTheme == 0)
{
final char[] WINDOW = new char[]{‘W’, ‘I’, ‘N’, ‘D’, ‘O’, ‘W’, 0};
hWindowTheme = OS.OpenThemeData(getShell().handle, WINDOW);
}
PAINTSTRUCT ps = new PAINTSTRUCT();
int hDC = OS.BeginPaint(handle, ps);
RECT rect = new RECT();
rect.left = -5;
rect.top = 0;
rect.right = getShell().getClientArea().width;
rect.bottom = getShell().getClientArea().height;
int active = activeValue ? FS_ACTIVE : FS_INACTIVE;
OS.DrawThemeBackground(hWindowTheme, hDC, WP_FRAMELEFT, active, rect, null);
OS.EndPaint(handle, ps);
return LRESULT.ZERO;
}
@Override
public void dispose()
{
if (hWindowTheme != 0)
{
OS.CloseThemeData(hWindowTheme);
}
super.dispose();
}
// … getters and setters for fields
}
This GlassAwareComposite is actually useful for DWM enabled and disabled. In each case different paint method will be used, for DWM enabled we draw black background, for DWM disabled we are doing something more interesting. Header pane is painted by UxTheme method DrawThemeBackground. Biggest problem with that was to get correct theme data and image index. Unfortunately in SWT theme data for “WINDOW” object is not available, therefore we need to open it for ourselves. Theme data also needs to be released, which we’re doing in dispose() method. For purpose of painting header pain, we can use same image that is painted on top left side under chrome title bar, but we need to shift it few pixels, because of left border. This is where our WM_ACTIVATE message is extensively used. GlassAwareShell when receive WM_ACTIVATE, sets active state on header pane, then invalidate and update header pane, GlassAwareComposite receive WM_PAINT message and paint it’s content.
After applying all those magic stuff, our application can looks following, activate:

and inactive:

Thanks to DWM_CHANGE listener introduced in GlassAwareShell application can handle DWM change, accordingly to its desires - I found that if we want to have the same user interface alignment in both DWM enabled and disabled we need to change margins in our layout. I’ve also noticed that Glass information (created with DwmExtendFrameIntoClientArea() method) is lost after DWM change, so it’s suggested to call this method once again in DWM_CHANGE listener.
Notice! Due to heavy use of overriding methods with default visibility in Shell, Text and Composite new widgets needs to stay inside org.eclipse.swt.widgets package. This is annoying limitation that could be easily resolved by marking methods protected.
I hope that too much of code wasn’t discouraging and examples were proficient enough to discover that SWT applications under Vista can look the same as those written in C++.
PS. You can observe that I’m also experimenting with explorer toolbar, but unfortunately I wasn’t able to find any windows api to draw it, so I just placed background image on Composite ;-)
WordPress
Steve said:
May 20, 09 at 13:09Hello!
Very nice. Like I said before, I played with a lot of this stuff before but didn’t get to a clean point. The problem with the using native Vista double buffering to get around the alpha channel limitatin is that animation gets lost. There were also, many redraw bugs in the individual controls such as the calendar control.
The visibility issue in SWT (ie. make methods protected) is a hard one. By making a method protected, it becomes API and needs to be supported until the end of time. Worse than that, it is Windows specific API. There was talk way back when of making an platform specific hook into the native event dispatch handling. Every plaform is different so the hook would have a portable API, but the work that was done in the hook would be platform specific.
Steve
Łukasz Milewski said:
May 20, 09 at 14:21Hi Steve!
For me, and for application I’m working on my in spare time, Vista (and Windows 7) glass style is very important - I’m trying very hard to create very rich user interface just by using as much as DWM gives me.
Re double buffering and animation. I haven’t got to the point where I use widget that has animation, but since DWM provides methods to animate, they could be used with double buffering.
Re visibility. I see and understand your point, it was just a thought. Either way, having this hook mechanism would be awesome thing, if you have it somewhere to test it I would be happy to look at it :-)
Last thing, I may be biased by Vista/7, but this kind of stuff presented here is and (probably) always be Windows specific and applications that makes use of it would have to manage that as well (fragments if it is plug-in development, separate binaries for applications - just like swt does)
Steve said:
May 20, 09 at 19:10Naw, I could imagine a constant SWT.GLASS or SWT.AERO that would extend the glass all the way into the shell. Controls that did not set the background (ie. left the default) would then have the glass shining through.
Alternately, there could be an API on Shell that extended the glass into the client area of the shell.
Ch3D said:
Nov 03, 09 at 14:24Hello Łukasz!
Have you ever faced with a problem of black parts of shell when resizing the window?
Łukasz Milewski said:
Nov 03, 09 at 21:45Hi Ch3D!
Yeah, I’ve seen the black parts when resizing, but only on rather slow machines, or on VMware. Similar artefacts can be seen when you resize Chrome Browser window (while having Glass and default skin for Chrome). ATM don’t know how to fix that, maybe some workarounds near WM_RESIZE message, but that’s just wild guess.
Ch3D said:
Nov 05, 09 at 06:05Thanks for quick reply, Łukasz!
Hmm, well.. I have Intel Core 2 Duo with 8GB of RAM and I still have a problem with this artifact. But if I unchecked ‘Enable desktop composition’ in visual-settings of my w7 - everything works fine! :)
Thanks for your answer, I’ll try experiment with WM_RESIZE may be it will give a some result.
Regards,
Dmitry.
Ch3D said:
Dec 01, 09 at 13:41Do you know how can I disable ‘Desktop Composition’ for SWT application? (in window7 & vista)
Kicek said:
Dec 24, 09 at 11:54When can we read sth new? :)
BTW.
Merry Xmas! :)