Aero Glass in GlassCalc (part 1)

2010

I got a couple questions about how I made the Aero Glass effects in GlassCalc. When I started making GlassCalc, I had no idea how to manage Aero Glass, but Google came to my rescue. What I eventually came up with is the amalgamation of code snippets and general knowledge from countless separate articles and examples. I’ll link to as many of those pages as I can remember, but I’ll try and bring everything together in one place here.

Note: GlassCalc is written in C# using WPF for its UI. As such, some of the code examples below are C#, .NET, or WPF specific.

Every window in Windows has a client area and a non-client area. The client area is everything you get to handle and draw on. Normally, this is everything inside the window frame. The non-client area is everything Windows handles by itself: the window frame, icon, window title, min/max/close buttons, resize handles, etc…

Using the Windows API, programs can modify the glass frame and the client/non-client areas. Generally, they either extend the glass frame into the client area, or they extend the client area into the glass frame. GlassCalc does both.

With the default interface, GlassCalc extends the top and bottom of the frame. The client area is still the same size, but the glass frame has been extended into the client area, so GlassCalc can draw the menu and the input box over glass.

[singlepic id=6 w=320 h=240 float=center]

With the full glass interface, GlassCalc extends the frame to cover the entire window, so everything is drawn over glass, but it also draws the menu bar inside the title. This is done by extending the client area upwards so GlassCalc can draw there. This effect takes quite a lot more work, because now that GlassCalc has taken over the non-client area, it must handle all the things Windows used to handle in this area.

[singlepic id=13 w=320 h=240 float=center]

Extending the glass frame

All of the code here involves Windows API calls in some way. To organize everything, I’m putting all Windows API functions and structures in a class called Interop. (some examples use the name NativeMethods) I’ll also make a class called GlassHelper which will (quite appropriately) help deal with Aero Glass.

To extend the glass frame, we need to import a function called DwmExtendFrameIntoClientArea from dwmapi.dll. Since Aero can be disabled, we also need to know whether we can extend the frame or not. For this, we import DwmIsCompositionEnabled. DwmExtendFrameIntoClientArea takes a Margins structure as one of its arguments. Since there is no Margins structure in C#, we need to define it ourselves. Much of the following code comes from this informative article.

Note: You can copy the source of these code examples by hovering over the code, then clicking the view source icon in the toolbar that appears in the upper-right corner.

public static class Interop
{
    [StructLayout(LayoutKind.Sequential)]
    public struct Margins
    {
        public int Left;
        public int Right;
        public int Top;
        public int Bottom;
        public Margins(Thickness t)
        {
            Left = (int)t.Left;
            Right = (int)t.Right;
            Top = (int)t.Top;
            Bottom = (int)t.Bottom;
        }
    }
    [DllImport("dwmapi.dll", PreserveSig = false)]
    public static extern void DwmExtendFrameIntoClientArea(IntPtr hwnd, ref Margins margins);
    [DllImport("dwmapi.dll", PreserveSig = false)]
    public static extern bool DwmIsCompositionEnabled();
}

Now, we’ll make a class that can make use of these functions.

public static class GlassHelper
{
    /// <summary>
    /// Gets whether dwmapi.dll is present and DWM functions can be used
    /// </summary>
    public static bool IsDwmCompositionAvailable
    {
        get
        {
            // Vista is version 6.  Don't do aero stuff if not >= Vista because dwmapi.dll won't exist
            return Environment.OSVersion.Version.Major >= 6;
        }
    }
    /// <summary>
    /// Gets whether DWM is enabled
    /// </summary>
    public static bool IsDwmCompositionEnabled
    {
        get
        {
            // Make sure dwmapi.dll is present.  If not, calling DwmIsCompositionEnabled will throw an exception
            if (!IsDwmCompositionAvailable)
                return false;
            return Interop.DwmIsCompositionEnabled();
        }
    }
    /// <summary>
    /// Extends the glass frame of a window
    /// </summary>
    public static bool ExtendGlassFrame(Window window, Thickness margin)
    {
        if (!IsDwmCompositionEnabled)
            return false;
        IntPtr hwnd = new WindowInteropHelper(window).Handle;
        if (hwnd == IntPtr.Zero)
            throw new InvalidOperationException("The Window must be shown before extending glass.");
        HwndSource source = HwndSource.FromHwnd(hwnd);
        // Set the background to transparent from both the WPF and Win32 perspectives
        window.Background = Brushes.Transparent;
        source.CompositionTarget.BackgroundColor = Colors.Transparent;
        Interop.Margins margins = new Interop.Margins(margin);
        Interop.DwmExtendFrameIntoClientArea(hwnd, ref margins);
        return true;
    }
}

Finally, in override your application’s OnSourceInitialized method to call ExtendGlassFrame. The Thickness object you pass in defines how far in the frame should be extended from each of the sides. If you use -1 for each dimension (like in the code below), the glass will cover the entire window.

public sealed partial class YourApp : Window<
{
    . . .
    protected override void OnSourceInitialized(EventArgs e)
    {
        base.OnSourceInitialized(e);
        GlassHelper.ExtendGlassFrame(this, new Thickness(-1));
    }
    . . .
}

Make the glass area act like an extension of the title bar

If you extend the frame from the top such that the title bar appears bigger, (like in the default GlassCalc interface) you probably also want to make it so that when a user clicks in this empty area, they can drag the window. You can do this by detecting a MouseLeftButtonDown event inside the extended title bar area and calling your window’s DragMove function.

What if Aero gets disabled while my program is running?

There is one other thing you should take care of when extending the frame. If a user turns off Aero while your application is running, you’ll get big black areas where there used to be glass because your window background is transparent. Windows sends a WM_DWMCOMPOSITIONCHANGED message to your application when Aero is turned on or off, so you can handle this by setting up a function to listen for this message.

Add this constant to Interop.

public static class Interop
{
    public const int WM_DWMCOMPOSITIONCHANGED = 0x031E;
    . . .
}

Now, set up a function to listen for messages. When it gets a WM_DWMCOMPOSITIONCHANGED message, check to see whether Aero was enabled or disabled. If Aero was enabled, extend the frame again. If it was disabled, set the background color back to its original value. (This example assumes your window background was white. You could improve it by saving the background color before extending the frame, then reverting it when Aero is disabled.)

public sealed partial class YourApp : Window
{
    . . .
    /// <summary>
    /// Processes messages sent to this window
    /// </summary>
    private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        if (msg == Interop.WM_DWMCOMPOSITIONCHANGED)
        {
            if (GlassHelper.IsDwmCompositionEnabled)
            {
                // Aero was re-enabled.  Extend the glass again.
                GlassHelper.ExtendGlassFrame(this, new Thickness(-1));
            }
            else
            {
                // Aero was disabled.  Reset the window's background color to remove black areas
                HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
                source.CompositionTarget.BackgroundColor = Colors.White;
                this.Background = Brushes.White;
            }
        }
    }
    protected override void OnSourceInitialized(EventArgs e)
    {
        base.OnSourceInitialized(e);
        // Hook up the WndProc function so it receives messages
        HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
        source.AddHook(WndProc);
        GlassHelper.ExtendGlassFrame(this, new Thickness(-1));
    }
    . . .
}

Extending the client area

Since this is already a very long post, and the part on extending the client area will probably be even longer, I’m going to split it up. Extending the client area into the frame will come in part 2… whenever I get around to writing that. For now, here are some of the links I found most useful while figuring this stuff out.

Update 8/19/2010: I know it’s been over a month since I posted part 1, and I still haven’t gotten to part 2.  I’m working on it, but school is start back up, so I can’t say when it’ll be done.

Putting Netbeans AppData in the Right Place

2010

If you use Windows Vista/7 and Netbeans, you’ve likely seen a .netbeans and .netbeans-registration folder appear in your user directory. Netbeans is a very nice piece of software, but like many open-source programs, it goes by the mistaken belief that application data always goes in the home folder. This is perfectly normal for Linux, but Windows is not Linux. Windows application settings go in the AppData folder. It is possible to get Netbeans to store its settings in the right place, and it isn’t too difficult.

This guide will show you how to put Netbeans settings in the right place for Windows Vista/7, but you can also use it to put the settings directories anywhere you want on any system. I’ll assume you are using Netbeans 6.9. If not, change the version number where appropriate.

Find netbeans.conf

Open an explorer window and find the directory where Netbeans is installed. The default location is C:\Program Files\Netbeans 6.9 (C:\Program Files (x86)\Netbeans 6.9 for 64 bit systems). Inside it, there should be a folder called etc and inside that, a file called netbeans.conf. Run an instance of your preferred text editor as administrator (you won’t be able to save the file otherwise) and open netbeans.conf in it.

Change netbeans_default_userdir

The second line of netbeans.conf defines the settings directory. Change it to look like this:

# ${HOME} will be replaced by JVM user.home system property
netbeans_default_userdir="${HOME}/AppData/Roaming/Netbeans"

If you move your .netbeans and .netbeans-registration folders to this new location, Netbeans will pick up your settings, but it will still create the same two directories in your user directory and it will ask you to register again. This is because there are actually two options you need to change. The second one is a little harder to find.

Define -J-Duser.home

Line 6 of netbeans.conf defines a bunch of options, each starting with -J. You need to add another option to this line. Before the quote at the end of the line, add this: (replace USERNAME with your username)
[code lang=”text” light=”true”]-J-Duser.home=C:/Users/USERNAME/AppData/Roaming/Netbeans[/code]

Your netbeans.conf file should look something like this now.

# ${HOME} will be replaced by JVM user.home system property
netbeans_default_userdir="${HOME}/AppData/Roaming/Netbeans"
 
# Options used by NetBeans launcher by default, can be overridden by explicit
# command line switches:
netbeans_default_options="-J-client -J-Xss2m -J-Xms32m -J-XX:PermSize=32m -J-XX:MaxPermSize=200m -J-Dapple.laf.useScreenMenuBar=true -J-Dsun.java2d.noddraw=true -J-Duser.home=C:/Users/USERNAME/AppData/Roaming/Netbeans"
# Note that a default -Xmx is selected for you automatically.
. . .

And now you’re done! Netbeans should no longer clutter your user directory with its settings.

Downtime, Updates, and Glass!

2010

First off, sorry about the recent downtime. My (free) host had a failing hard drive. Naturally, my site was on that drive, and it took a while to move everything to another server and reconfigure it.

I recently finished a Visual Studio 2010 extension that automatically closes braces (because Microsoft still hasn’t implemented it, and there’s no way I’m buying something like ReSharper). Because I am boring and unoriginal, I have named it Brace Completer. You can find it in the software section.

I also updated my Opera/Firefox userscripts with a couple enhancements. The accented character shortcuts script loads a bit sooner on Opera, and the MangaFox script can now use session/localStorage to cache image urls, making things load faster if you return to a previously loaded chapter.

Lastly, I have an update for GlassCalc, which fixes an overflow error with base conversions, a freeze with long input strings of all the same letter, a bug with updates, and a few other things. The full changelog can be found here. Also, I decided that, for a program named GlassCalc, my calculator didn’t have nearly enough glass. I have now remedied this oversight.

[singlepic id=13 w=320 h=240 float=center]

Go to the Options->More Settings page and check Full Glass UI to turn it on. You’ll obviously need Vista or Windows 7 for it to work.

Speaking of things named GlassCalc, as it turns out, the name I chose in an inspired moment of uncreativity has been in use by an English software company for some 25 years. Whoops. They asked if I would link to their site, which I will gladly do here and now (as well as some other places around the site).

Glass Software UK
Glasscalc Ltd are the leading software developers in the specialist field of Pricing / Costing and Invoicing systems for the Glass Industry.

If you happen to be looking for software that deals with actual glass, go check it out!

Adsense, Without Blocking

2010

My web pages pause at ads for a moment. Why?

In some (but not necessarily all) web browsers, if you place a script in the middle of a web page, it has to be executed before the browser can continue rendering the page. The AdSense script is quite slim, but your browser still has to request the JavaScript file from Google, which could take a while depending your Internet speed. This means your pages might load up to the ad, pause a little while, then continue loading. Fortunately, there is a very simple way to keep scripts from blocking your content: put them at the end of the page. This way, all your content is loaded before the scripts are downloaded and executed. Unfortunately, this also puts all your ads at the bottom of the page, but with a little JavaScript, you can put your ads back where they belong.

How to fix it

First, you’ll need some sort of placeholder for where an ad should go. Use a div and set its id so you can easily find it with JavaScript. If you have multiple ads, number the ids so you can replace them all with a loop.

<div id="ad-0"> <!-- the first ad goes here --> </div>
<div id="ad-1"> <!-- a second ad goes here --> </div>

Now, place the AdSense JavaScript at the end of the page. Put each ad inside a div with an id so you can easily find it with JavaScript. Put the everything in a div with display set to none so your ads won’t briefly appear at the bottom of the page.

<div style="display:none">
  <div id="adsource-0">
    <script type="text/javascript"><!--
    google_ad_client = "pub-xxxxxxxxxxxxxxxx";
    /* Ad Name */
    google_ad_slot = "##########";
    google_ad_width = 125;
    google_ad_height = 125;
    //-->
    </script>
    <script type="text/javascript"
    src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
    </script>
  </div>
  <div id="adsource-1">
    ...
  </div>
</div>

Finally, use JavaScript to move each ad to its proper place. This script uses document.getElementById to find the ad and the placeholder, then it uses appendChild to move the ad into the placeholder div.

<script type="text/javascript">
numAds = 1;
for (var i = 0; i < numAds; i++) {
    source = document.getElementById("adsource-" + i);
    placeholder = document.getElementById("ad-" + i);
    placeholder.appendChild(source);
}
</script>

How well does it work?

Consider this entire site a live tech demo. Refresh the page and watch under the “Advertisements” header of the sidebar. After all of the content is loaded, the ads pop into place.

It breaks my layout!

With this setup, the placeholders initially take up no space, so they expand when the ad gets loaded. If this breaks your layout, you can set the height and width of the placeholder to the dimensions of the ad like this:

<div id="ad-0" style="width: 125px; height: 125px"> <!-- this placeholder takes up space! --> </div>

Edit (A bit later, still June 4)

Apparently, this is what I get for writing a post about AdSense. See? This is why we can’t have nice things.

Transcription: Can still see the light

2010

This is a transcription of the piano version of Can still see the light from Phantasy Star Online Songs of RAGOL Odyssey. Soundtrack EP 1 & 2. I’ve always wanted to transcribe this and, well, now I have. You can listen to the original song on YouTube here. The link below will take you to my sheet music page, where you can find the download links.

Please tell me what you think! Comments, suggestions on what to transcribe next, etc. are all welcome!

GlassCalc 1.25

2010

GlassCalc version 1.25 fixes a crash when you check for updates while GlassCalc is already checking for updates. It also reads an “extensions.ini” file, which currently allows you to define extra constants, but may be expanded to let you extend GlassCalc in other ways in the future.

User-defined Constants

To add your own constants to GlassCalc, go to the More Settings menu and open the Extensions tab. You will see two sections: Global Extensions and User Extensions. Global extensions are system-wide and reside in GlassCalc’s program files directory. This means modifying them on Vista/Win 7 requires elevation. (I have not tested this on XP, so please tell me if it gives you trouble) User extensions apply only to your user profile and reside in GlassCalc’s application data directory. For normal cases, I suggest you use only user extensions. If both extensions files are present, user settings will override global ones.

To create the extensions settings file, click Create extensions.ini. The settings file will be created with the default settings and opened. (If you don’t have file associations for .ini files, you should get an Open With dialog. Pick a text editor.) Constant definitions go after the line with [Constants]. Everything on a line after a semicolon is ignored. Each line can contain one definition in the form “name = value”, where “name” contains only letters, numbers, and underscores and doesn’t start with a number. “value” can be any number (exponential notation is supported) or any numeric field, constant, or property defined in mscorlib.dll. (If you are not a .NET programmer, you can safely ignore that last bit.) If the value is not defined in System, you must provide the full namespace as well.

Here is an example file showing some of the ways constants can be defined:

;Stuff after a semicolon is a comment.
[Constants]
euler = 0.577215664901532860606512090082 ;Euler's constant
planck = 6.6260689633e-34 ;Planck's constant using exponential notation
pi = Math.PI ;pi using .NET's built in constant

GlassCalc should update its constants list when you close your text editor. If it does not, try clicking Edit extensions.ini from the settings menu and closing your text editor again. If all else fails, restart GlassCalc and it will pick up the updated settings. If you defined a new constant with the same name as an already present variable, you will need to restart GlassCalc for the changes to take effect.

If any of this isn’t clear or isn’t working, don’t hesitate to ask in the comments section below!

Downloads are on the main GlassCalc page.

GlassCalc 1.24

2010

I uploaded version 1.24 about a week ago, but now that finals are out of the way, here’s a bit about the latest version of GlassCalc.

GlassCalc now automatically sizes the left pane to fit its contents. It also automatically hides the left pane if you make the window very small. If you liked the previous behavior, you can switch it back by unchecking Resize left pane automatically and Hide left pane when window is small in the settings window.

Unit conversions!

The biggest change in version 1.24 is support for unit conversions. If you use something like in to cm (or in:cm) after an expression, GlassCalc will insert the appropriate conversion factor. You can also place unit conversions in the middle of expressions, so sqrt(8 ft^2 to in^2) in to cm is valid. Note that GlassCalc doesn’t care what types of units you use as long as each conversion is valid. For instance, you could evaluate 1 weeks:fortnights in:cm.

Important bit about unit conversions!

GlassCalc supports unit conversions, but it doesn’t come with a unit converter out of the box! GlassCalc requires GNU Units to do its unit conversions. The upside of this is that GlassCalc can convert to and from just about any unit you can imagine. The downside is, you have to install another program. Thankfully, this is very easy to do if you follow my handy guide to installing Units! GlassCalc does not yet support all of the features of GNU Units, namely the special syntax required for non-linear conversions (like temperatures). Since temperature conversions are common (and simple), GlassCalc will handle conversions between Fahrenheit, Celsius, and Kelvin itself. You can use the units fahrenheit, celsius, and kelvin or the abbreviations tempF, tempC, and tempK.

What’s next?

I plan on attempting to port MTParser, the math parser used by GlassCalc, to .NET. If I’m successful, GlassCalc won’t need to mess with DLL registration, and I won’t need to package Visual C++ libraries with it. This means GlassCalc will become a much smaller download, and it won’t require installation, so it can be run as a portable app on any computer with .NET 4.0 installed. It will also make it much easier for me to write plugins for the parser, which may eventually lead to things like high precision math for slower, but much more accurate results. Hopefully, sin(pi) will actually be 0, not 1.22460635382238E-16!

Downloads are on the main GlassCalc page.

Transcription: Bruine Assassine

2010

I just finished transcribing Bruine Assassine, the only piano solo from the So Ra No Wo To original soundtrack. You can download the sheet music from my sheet music page. You can also listen to the original track on YouTube here. Here’s the link to the sheet music again, this time all by itself and with a little line under it for extra emphasis.

Please tell me what you think! Comments, suggestions on what to transcribe next, etc. are all welcome!