Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / multimedia / OpenGL

HUD window 64-bit (DWM composition)

4.59/5 (9 votes)
2 Jan 2014CPOL12 min read 36.9K  
DWM composition

Introduction

The purpose of a HUD window is to create composited presentations, based on the use of the Desktop Window Manager (DWM), that is an important part of the OS, since Vista. It is based on the same concept that was used to create DreamScenes on Vista Pro.

It has been inspired by movies like Avatar, Iron man, and Oblivion, that are using plainty of transparent display, and like those HUD applications that can be found in modern jet fighter cockpit.

This article is closely related to the use of third party addons (BASS.dll, GDImage.dll, WinLIFT.dll), however I shall try to explain as much as i can the concept, because i am not aware of other tools able to do this using only the core API, but if there are some, i would gladly learn about them.

It is a mix of several languages: C++, PowerBASIC, OpenGL, and the whole application result in a cooperative work between 32 and 64-bit modules.

Desktop Window Manager

The desktop composition feature, introduced in Windows Vista, fundamentally changed the way applications display pixels on the screen. When desktop composition is enabled, individual windows no longer draw directly to the screen or primary display device as they did in previous versions of Windows. Instead, their drawing is redirected to off-screen surfaces in video memory, which are then rendered into a desktop image and presented on the display.

In Windows 8, Desktop Window Manager (DWM) is always ON and cannot be disabled by end users and apps. As in Windows 7, DWM is used to compose the desktop. In addition to experiences enabled in Windows 7, now DWM desktop composition enables desktop composition for all themes, support for Stereoscopic 3D, and management, separation, and protection of the experience with Windows Store apps.

In Windows Vista and Windows 7, desktop composition is enabled only with the AERO Glass Theme.

When using DWM, everything is rendered ultimately onto the DirectDraw surface, using the GPU rather than the CPU, this opens a wealth of capabilities for a graphic engine that is able to use it. To illustrate this article i am using the free trial version of my GDImage 7.00 and WinLIFT 5.00, because they are both designed to use the GPU rather than the CPU, and they are able to combine most of the graphic technologies altogether.

The composited mode

In order to work in composited mode, all drawing must be done in 32-bit, because the use of the alpha channel is a mandatory to handle individual variable opacity of child controls (we do not use a layered window, because we want to preserve each of the child control variable opacity).

In Windows 8:

  • All of the options for disabling desktop composition that exist in Window 7 are removed
  • Desktop composition is responsible for composing all themes
  • Apps cannot use DwmEnableComposition to disable desktop composition. In order to maintain backward compatibility, a call to this API will return success; however, desktop composition is not disabled
  • The "Disable desktop composition" shim is removed
  • The option to "Disable desktop composition" from the compatibility tab of the Application Properties dialogue box is removed.
  • The AERO blur mode has been removed.

In Vista and Seven, here are a few functions to enable/disable composition:

C++
#include <Dwmapi.h>
#define long_proc typedef long (__stdcall *zProc)

// Check for the DWMAPI
HMODULE LoadDWM () {
    static HMODULE hLib;
    static long nChecked;
    if (nChecked == 0) { nChecked = -1; hLib = LoadLibrary (L"DWMAPI"); }
    return hLib;
}

// Enable DWM composition
long zDwmEnableComposition (IN long uCompositionAction) {
    long nRet = 0;
    HMODULE hLib = LoadDWM();
    if (hLib) {
        long_proc (long);
        zProc hProc = (zProc) GetProcAddress(hLib, "DwmEnableComposition");
        if (hProc) { nRet = hProc(uCompositionAction); }
    }
    return nRet;
}

// Check if DWM composition is enabled
long zDwmIsCompositionEnabled () {
    long nRet = 0;
    HMODULE hLib = LoadDWM();
    if (hLib) {
        long_proc (BOOL*);
        zProc hProc = (zProc) GetProcAddress(hLib, "DwmIsCompositionEnabled");
        if (hProc) { 
            BOOL bAero = FALSE;
            if (hProc(&bAero) == 0) {
                if (bAero) { nRet = -1; }
            }
        }
    }
    return nRet;
}

// This is the most IMPORTANT API to create a transparent window
// We fool DWM with a fake region, because we want to use the client, and the non-client area,
// just like with a transparent layered window.
// This one must also be used with Windows 8+
void zSetCrystalBehindMode (IN HWND hWnd, IN BOOL nBOOL) {
    static FARPROC hProc;
    HMODULE hLib = LoadDWM ();
    if (hLib) {
        if (zDwmIsCompositionEnabled()) {
            long_proc (HWND, DWM_BLURBEHIND*);
            zProc hProc = (zProc) GetProcAddress(hLib, 
                   "DwmEnableBlurBehindWindow");
            if (hProc) {
                LRESULT nRet = S_OK;
                HRGN hRgnBlur = 0;
                DWM_BLURBEHIND bb = {0};
                // Create and populate the BlurBehind structure.
                // Set Blur Behind and Blur Region.
                bb.fEnable = nBOOL;
                bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
                bb.fTransitionOnMaximized = 0;
                // Fool DWM with a fake region
                if (nBOOL) { hRgnBlur = CreateRectRgn(-1, -1, 0, 0); }
                bb.hRgnBlur = hRgnBlur;
                // Set Blur Behind mode.
                nRet = hProc(hWnd, &bb);
            }
        }
    }
}

Take full control of your window

In order to draw in composited mode on a DirectDraw surface, you could not use anymore GDI32, mainly because DirectDraw (and also OpenGL) do not store the 32-bit ARGB components in the same order, and also because RGB(0,0,0) doesn't produce a solid black color brush, but a transparent one. The workaround is to use GDIPLUS rather than GDI32, and perform a Red and Green permutation when drawing directly onto the DD surface (like when creating OpenGL textures).

Another caveat is to draw all controls and objects from bottom to top with strict respect of the z-order, to render correctly the variable opacity. This means you should not let Windows itself do the default painting process, but render everything into a DIB memory then blit the resulting composited image onto the DD surface once all the overlapping layers have been drawn in cached memory (this is the hard part that should be delegate to a SkinEngine).

In order to take full control of a popup window, I have written a tutorial that explains the basic steps to create a simple SkinEngine. The tutorial is written in BASIC to ease the reading of the code, however because it uses only the low level SDK API, it is easy to translate from one language to another. The full tutorial can be found here: http://www.jose.it-berater.org/smfforum/index.php?topic=1164.0.

For those wanting to figure what could be done with a Skin Engine that is DWM compatible, without retyping/translating all the code, use the link at the end of this article to download the C++ demo project that is provided with a copy of the WinLIFT/GDImage C++ 64-bit trial version.

About the demo

The demo is totaly atypical because it does use a mix of 32-bit and 64-bit inside of the same application altogether with multiple languages (C++ and PowerBASIC), the low level coding part is written in PowerBASIC 32-bit, while the main EXE is written in C++ 64-bit.

The purpose of keeping the low level coding part in 32-bit, is that the same code can be used with a final EXE written either in 32 or 64-bit.

ZWP.exe player

In order to perform the intra process communication between the 64 and 32-bit coding sections, we use a runtime named ZWP.exe that is in charge to monitor and display the (BassBox) OpenGL plugins used to perform the visual background animations.

ZWP.exe itself, is written in PowerBASIC (source code in the ZIP file) to produce very small EXE (25 Kb) with no run-time requirement and no extra dependencies. It is using a popup window with the WS_EX_TOOLWINDOW extension style, to avoid showing itself on the Windows task bar.

The ZWP {child popup} must always stay under the main HUD window and it must use the same size and location, than its parent window, this part of the code is handled inside of the ZWP_SizeMove procedure below:

C++
void ZWP_SizeMove() {
    RECT lpw;
    int x, y, w, h;
    HWND hWnd, hPopup;
    GetWindowRect(gh_Main, &lpw);
    x = lpw.left + skGetSystemMetrics(SK_DWM_LEFT);
    y = lpw.top + skGetSystemMetrics(SK_DWM_TOP);
    w = lpw.right - skGetSystemMetrics(SK_DWM_RIGHT) - x;
    h = lpw.bottom - skGetSystemMetrics(SK_DWM_BOTTOM) - y;
    // Use this to move the window in case of AERO shake
    // because setting the new location with SetWindowPos
    // may fail!
    hWnd = ZWP_Handle();
    if (hWnd) {
        hPopup = skGetDWMregion(gh_Main); // This is the region drawn behind the child controls
        if (hPopup == 0) { hPopup = gh_Main; }

        SetWindowPos(hWnd, GetWindow(hPopup, GW_HWNDNEXT), 0, 0, 0, 0, 
          SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_NOACTIVATE | SWP_ASYNCWINDOWPOS);
        MoveWindow(hWnd, x, y, w, h, 0);
    }
}

skGetDWMregion is a special procedure of the SkinEngine that draws a black brush region behind the edit part of the Combo, because Windows still use the old edit control when creating a ComboBox using a GDI32 brush to paint the background, thus making it transparent in composited mode.

So far I didn't find an easy way to solve this behavior when redrawing the whole scene. Thus i choose to redraw the region only when the visual plugins are hidden.

In order to communicate with ZWP.exe, we define our own private message WM_ZWPNOTIFICATION, and we use SendMessageA altogether with the WM_SETTEXT to exchange parameters, with this set of API (see details in the C++ project source code):

  • ZWP_Destroy, kill the ZWP child process.
  • ZWP_Handle, to retrieve the ZWP window handle.
  • ZWP_Hide, to hide the ZWP window.
  • ZWP_Message, to communicate with the ZWP child process.
  • ZWP_Play, play a specific audio file.
  • ZWP_Show, to show the ZWP window.
  • ZWP_SizeMove, to resize/move the ZWP window to follow the size/move of the main EXE.
  • ZWP_Stop, to stop audio playing.
  • ZWP_UsePlugin, to specify the visual OpenGL plugin to use.

ZWP.exe, itself is using a subset of the freeware BassBox audio player engine, that works closely with the BASS.dll from Ian Luck (www.un4seeen.com).

Its purpose is to play the visual plugins and the audio altogether, in full concurrent mode of the main window application, rather than like a child thread. I am doing this, because Windows does not handle a thread, and a distinct process, with the same level of priority.

Note: The complete PowerBASIC source code of ZWP.bas is provided altogether with the C++ project inside of the ZIP file.

Visual Plugins

They are all pieces of true artwork programming, Matrix is probably one of the best version you have ever seen, same for Attractor that never produces twice the same display. All the plugins are small individual 32-bit DLLs, their purpose is to render the animation in real time, based on the analyze of the audio signal.

There are more visual plugins I could share with those interested, and i could also explain how to create them if there is a need for it. You can already learn about the BassBox visual plugin concept here.

Using visual plugins and audio files altogether:

The demo is provided with a set of audio files stored in the "\Audio" subfolder, where you can add your own set of audio files, in either sound tracker music (xm, it, mod, s3m, mtm) or audio stream format (mp3, ogg, wav). In order to play or stop audio, you just have to click on the "Use visual plugin" check box. You can select a song from the first combo, or if you want you can use drag & drop from the Windows Explorer, to play a specific audio file.

To change the visual plugin, select a new one from the second combo.

GDImage 7.00

In order to play the animations, i had to use GDImage version 7.00, because it is able to play a static animations (same as gif animation) in full 32-bit composited mode.

All the animations are draggable with the mouse inside of the graphic container. Each of the graphic components could be anchored to a specific location, just like child controls in a window.

The text "DWM composition" is using a private font, to ensure that it always look the same on all computers, without the need to install first the missing font.

The main centered animation could be rotated at any angle from any of the round knob shown on the bottom of the window (click with the right mouse button on any of the knob control, to restore the initial angle).

The vertical slider, on the right side of the window, can be used to resize the main animation on the fly. (Note: some of the GDImage features are disabled in the 64-bit trial version).

WinLIFT 5.00

Is a SkinEngine designed specifically to cooperate with GDImage to render the whole interface in composited mode.

The fundamental of creating your own SkinEngine is explained in the serie of articles named "Take control of your window" here.

Layered backgrounds

The SkinEngine is able to use multiple layered composited backgrounds, they are a few provided within the "\Background" subfolder.

To change them, click either with the left or the right mouse button on the Iron Man icon located on the top left corner.

To better see the change of the subtile layered background, it is better to use a dark wallpaper as main Windows background.

Transparent HUD mode

When this option is checked, the composited layered background is not applied behind the graphic container, to better see the visual plugin animation playing at the bottom of the z-order.

Spinner animations

The project uses static animations, named "spinner", their initial purpose is to display a layered animation that works just like GIF animation, except that it is based on transparent .PNG or .SKI image (proprietary GDImage format) that could be used in full composited mode.

You can learn more about the use of a Spinner control, to inform the user that a lengthy process or critical task is running, here.

Here is an example of PNG file that could be used to perform a static animation:

Important: each of the frame must fit exactly within a square size, and use an horizontal alignment from left to right, to ease computation.

HARDWARE requirement

Due to the multimedia nature of the demo, it is intended to run on I7 multi-core with a good graphic card (nVidia or AMD/ATI).

When used on modern hardware, and because of the use of the GPU, HUDplus.exe should use less than 2% of the CPU, and ZWP.exe 0%, except when using the Matrix plugin that is the most demanding and then it could rise up to 10 % of the CPU resource.

These values are from an ASUS N76V CORE i7 with a GEFORCE GT 650M and 2 GB of graphic ram.

If your hardware config matches the above specs, then you can use the built-in thread animation, instead of a timer, because this allow smooth dragging of the sprite while keeping the animation running.

C++
long StartAnimation (IN long Delay) {
    long nRet = LB_ERR;
    gn_Delay = Delay;
    DWORD dwThreadId = 0;
    HANDLE hThread = CreateThread(NULL,                                  // default security attributes
                                  0,                                     // use default stack size
                                  (LPTHREAD_START_ROUTINE ) Animate,     // thread function name
                                  (LPVOID) Delay,                        // argument to thread function
                                  0,                                     // use default creation flags
                                  &dwThreadId);                          // returns the thread identifier
    if (hThread) { nRet = 0; Sleep(100); }
    CloseHandle(hThread);
    return nRet;
}

Source Code

The project source code is using UNICODE, it is provided in pure SDK coding style like documented into Charles Petzold 5th edition (the SDK coder Bible).

To be cooperative with the other running applications, it doesn't not use a game loop, but a dedicated thread on multicore i7, that could be replaced with a simple timer on older machine.

For the purpose of inter-language compatibility, this project uses only procedural coding style and the core API.

Screenshot

This screen shot is just a limited subset preview of the DWM composition.

This kind of HUD window is the only way to mix easily 2D and 3D altogether, and it could be used to create a complex game interface.

DreamScenes

If there is an interest for it, i could post another demo project using VIDEO instead of OpenGL plugins, and working exactly like the DreamScenes in Vista Pro, but less intruisive and specific to a single application, rather than using the whole desktop.

It does use the same API that the one used in the C# project here.

SDK programming

Why I am using it:

  • Low level programming is the only way to have full access to the power of the core Windows API.
  • It doesn't use a proprietary syntax nor it hides you what is going on behind the hood.
  • It is the only common denominator understood by all compilers, including .NET.
  • It keeps your code small and fast.
  • It allows you to write procedural code that is not harder to learn than OOP.
  • It allows you to use ALL the Windows controls.
  • It is easy to use with third party DLL, because DLL are the core meat of SDK.
  • It handles ALL the Windows message and structures.
  • It is not a dead end, because Microsoft despite what they say is always enhancing it.
  • You are no tied to anybody else to write your own controls when you need them.
  • You get rid of all the extra bloated dependencies found in {modern} programming languages.

Link to download the project

Because the size of the ZIP file is out of the limit allowed on CodeProject, you have to download the full project here. (28 171 Kb)

History

This is version 1.00, from January 2, 2014.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)