Get same Blur and Crystal appearance on both VISTA and XP
This screen shot shows you both "Moonraker" and "PhotoComposer" (GDImage) using the new Skin Engine!
As you can see you can create stunning interface with it...
(http://www.zapsolution.com/pictures/sdk/zskinphoto.jpg)
This is the result of many days of brain storming trying to understand DWM, and to work around all the bugs VISTA causes to existing advanced graphic application.
In short, it doesn't respect own Microsoft rules to perform compositing!
and there are many situations where DWM would cause indeed much flickering than on XP, if you are not aware of it.
This is the reason why this new version uses its own way to perform compositing on VISTA.
Note: if you are using Dual monitor with VISTA, there is a problem if you move the skinned window to the second monitor (see the next post for the solution).
Now about this new VISTA version, it allows you to do easily things that are very hard to perform even with VS2005 and WPF.
The new DWM code section:
%S_OK = 0
%WM_DWMCOMPOSITIONCHANGED = &H031E
%DWM_BB_ENABLE = &H00000001
%DWM_BB_BLURREGION = &H00000002
%DWM_BB_TRANSITIONONMAXIMIZED = &H00000004
%DWM_EC_ENABLECOMPOSITION = 1
%DWM_EC_DISABLECOMPOSITION = 0
TYPE DWM_BLURBEHIND
dwFlags AS DWORD
fEnable AS LONG
hRgnBlur AS DWORD
fTransitionOnMaximized AS LONG
END TYPE
TYPE MARGINS
cxLeftWidth AS LONG
cxRightWidth AS LONG
cyTopHeight AS LONG
cyBottomHeight AS LONG
END TYPE
FUNCTION LoadDWM () AS LONG
STATIC hLib AS DWORD, nChecked AS LONG
IF nChecked = 0 THEN nChecked = -1: hLib = LoadLibrary ("dwmapi.dll")
FUNCTION = hLib
END FUNCTION
DECLARE FUNCTION DwmEnableComposition (BYVAL uCompositionAction AS LONG) AS LONG
SUB zDwmEnableComposition (BYVAL uCompositionAction AS LONG)
LOCAL nRet AS LONG, hLib AS DWORD
STATIC hProc AS DWORD
hLib = LoadDWM()
IF hLib THEN
IF hProc = 0 THEN hProc = GetProcAddress(hLib, "DwmEnableComposition")
IF hProc THEN
CALL DWORD hProc USING DwmEnableComposition(uCompositionAction) TO nRet
END IF
END IF
END SUB
DECLARE FUNCTION DwmIsCompositionEnabled () AS LONG
FUNCTION zDwmIsCompositionEnabled () AS LONG
LOCAL nRet AS LONG, hLib AS DWORD
STATIC hProc AS DWORD
hLib = LoadDWM()
IF hLib THEN
IF hProc = 0 THEN hProc = GetProcAddress(hLib, "DwmIsCompositionEnabled")
IF hProc THEN
CALL DWORD hProc USING DwmIsCompositionEnabled() TO nRet
IF nRet THEN FUNCTION = -1
END IF
END IF
END FUNCTION
DECLARE FUNCTION DwmEnableBlurBehindWindow (BYVAL LONG, pBlurBehind AS DWM_BLURBEHIND) AS LONG
SUB zSetBlurBehindMode(BYVAl hWnd AS LONG, BYVAL nBOOL AS LONG, BYVAL hRgnBlur AS LONG)
LOCAL bb AS DWM_BLURBEHIND
LOCAL hLib AS DWORD, nRet AS LONG
STATIC hProc AS DWORD
hLib = LoadDWM ()
IF hLIB THEN
IF zDwmIsCompositionEnabled() THEN
IF hProc = 0 THEN hProc = GetProcAddress(hLib, "DwmEnableBlurBehindWindow")
IF hProc THEN
nRet = %S_OK
' // Create and populate the BlurBehind structure.
' // Set Blur Behind and Blur Region.
bb.fEnable = nBOOL
bb.dwFlags = %DWM_BB_ENABLE OR %DWM_BB_BLURREGION
bb.hRgnBlur = hRgnBlur
' // Set Blur Behind mode.
CALL DWORD hProc USING DwmEnableBlurBehindWindow(hWnd, bb) TO nRet
END IF
END IF
END IF
END SUB
SUB zSetCrystalBehindMode(BYVAl hWnd AS LONG, BYVAL nBOOL AS LONG)
LOCAL bb AS DWM_BLURBEHIND
LOCAL hLib AS DWORD, nRet, hRgnBlur AS LONG
STATIC hProc AS DWORD
hLib = LoadDWM ()
IF hLIB THEN
IF zDwmIsCompositionEnabled() THEN
IF hProc = 0 THEN hProc = GetProcAddress(hLib, "DwmEnableBlurBehindWindow")
IF hProc THEN
nRet = %S_OK
' // Create and populate the BlurBehind structure.
' // Set Blur Behind and Blur Region.
bb.dwFlags = %DWM_BB_ENABLE OR %DWM_BB_BLURREGION
bb.fEnable = nBOOL
bb.fTransitionOnMaximized = 0
' // Fool VISTA with a fake region
hRgnBlur = CreateRectRgn(-1, -1, 0, 0)
bb.hRgnBlur = hRgnBlur
' // Set Blur Behind mode.
CALL DWORD hProc USING DwmEnableBlurBehindWindow(hWnd, bb) TO nRet
END IF
END IF
END IF
END SUB
The complete project is in the attached zskin13.zip
And now that all the hard stuff is done, we will be able to play the fun... using GDImage. 8)
Patrice Terrier
www.zapsolution.com
QuoteNote: if you are using Dual monitor with VISTA, there is a problem if you move the skinned window to the second monitor.
To fix the Dual monitor mouse location problem, replace in the source code all reference to
LOWRD(lParam), HIWRD(lParam)
with
LO(INTEGER, lParam), HI(INTEGER, lParam)
to get a signed value instead of the unsigned WORD.
'// Monitor Windows DEF PROC to take over HITTEST detection
FUNCTION zDefWindowProc(BYVAL hWnd AS LONG, BYVAL Msg AS LONG, BYVAL wParam AS LONG, BYVAL lParam AS LONG) AS LONG LOCAL nRet, HITTEST AS LONG
LOCAL rc AS RECT
nRet = DefWindowProc(hWnd, Msg, wParam, lParam)
IF Msg = %WM_NCHITTEST THEN
LOCAL p AS POINTAPI
LOCAL CaptionHeight AS LONG
CALL zGetImageSize(zGetProperty(hWnd, %FORM_TopMid), 0, CaptionHeight)
p.X = LO(INTEGER, lParam): p.Y = HI(INTEGER, lParam) CALL ScreenToClient(hWnd, p)
IF IsZoomed(hWnd) = 0 THEN
IF nRet = %HTCLIENT THEN
HITTEST = %HTCAPTION
IF SK_DRAG_BACKGROUND() = 0 THEN
IF p.Y > CaptionHeight AND CaptionHeight > 0 THEN
HITTEST = %HTNOWHERE
IF hWnd <> GetForegroundWindow THEN CALL SetFocus(hWnd)
END IF
END IF
Patrice,
thanks for the update on your perfect project!
Just one question - is there some simple way to get info determining that user is running on single or multi-display configuration ?
Thanks,
Petr
Petr,
See MSDN there:
http://msdn2.microsoft.com/en-us/library/ms534604.aspx (http://msdn2.microsoft.com/en-us/library/ms534604.aspx)See also:
- MonitorFromPoint
- MonitorfromRect
- MonitorFromWindow
- GetMonitorInfo
- MONITORINFOEX
Example:
#include <windows.h>
#include "multimon.h"
#define MONITOR_CENTER 0x0001 // center rect to monitor
#define MONITOR_CLIP 0x0000 // clip rect to monitor
#define MONITOR_WORKAREA 0x0002 // use monitor work area
#define MONITOR_AREA 0x0000 // use monitor entire area
//
// ClipOrCenterRectToMonitor
//
// The most common problem apps have when running on a
// multimonitor system is that they "clip" or "pin" windows
// based on the SM_CXSCREEN and SM_CYSCREEN system metrics.
// Because of app compatibility reasons these system metrics
// return the size of the primary monitor.
//
// This shows how you use the multi-monitor functions
// to do the same thing.
//
void ClipOrCenterRectToMonitor(LPRECT prc, UINT flags)
{
HMONITOR hMonitor;
MONITORINFO mi;
RECT rc;
int w = prc->right - prc->left;
int h = prc->bottom - prc->top;
//
// get the nearest monitor to the passed rect.
//
hMonitor = MonitorFromRect(prc, MONITOR_DEFAULTTONEAREST);
//
// get the work area or entire monitor rect.
//
mi.cbSize = sizeof(mi);
GetMonitorInfo(hMonitor, &mi);
if (flags & MONITOR_WORKAREA)
rc = mi.rcWork;
else
rc = mi.rcMonitor;
//
// center or clip the passed rect to the monitor rect
//
if (flags & MONITOR_CENTER)
{
prc->left = rc.left + (rc.right - rc.left - w) / 2;
prc->top = rc.top + (rc.bottom - rc.top - h) / 2;
prc->right = prc->left + w;
prc->bottom = prc->top + h;
}
else
{
prc->left = max(rc.left, min(rc.right-w, prc->left));
prc->top = max(rc.top, min(rc.bottom-h, prc->top));
prc->right = prc->left + w;
prc->bottom = prc->top + h;
}
}
void ClipOrCenterWindowToMonitor(HWND hwnd, UINT flags)
{
RECT rc;
GetWindowRect(hwnd, &rc);
ClipOrCenterRectToMonitor(&rc, flags);
SetWindowPos(hwnd, NULL, rc.left, rc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
}
Thanks a lot Patrice,
Petr
Petr,
Multiple Monitor System Metrics:
The GetSystemMetrics function returns values for the primary monitor, except for SM_CXMAXTRACK and SM_CYMAXTRACK, which refer to the entire desktop. The following metrics are the same for all device drivers: SM_CXCURSOR, SM_CYCURSOR, SM_CXICON, SMCYICON. The following display capabilities are the same for all monitors: LOGPIXELSX, LOGPIXELSY, DESTOPHORZRES, DESKTOPVERTRES.
GetSystemMetrics also has constants that refer only to a Multiple Monitor system. SM_XVIRTUALSCREEN and SM_YVIRTUALSCREEN identify the upper-left corner of the virtual screen, SM_CXVIRTUALSCREEN and SM_CYVIRTUALSCREEN are the vertical and horizontal measurements of the virtual screen, SM_CMONITORS is the number of monitors attached to the desktop, and SM_SAMEDISPLAYFORMAT indicates whether all the monitors on the desktop have the same color format.
To get information about a single display monitor or all of the display monitors in a desktop, use EnumDisplayMonitors. The rectangle of the desktop window returned by GetWindowRect or GetClientRect is always equal to the rectangle of the primary monitor, for compatibility with existing applications. Thus, the result of
CALL GetWindowRect(GetDesktopWindow(), rc)
will be:
rc.nLeft = 0
rc.nTop = 0
rc.nRight = GetSystemMetrics (%SM_CXSCREEN)
rc.nBottom = GetSystemMetrics (%SM_CYSCREEN)
To change the work area of a monitor, call SystemParametersInfo with SPI_SETWORKAREA and pvParam pointing to a RECT structure that is on the desired monitor. If pvParam is NULL, the work area of the primary monitor is modified. Using SPI_GETWORKAREA always returns the work area of the primary monitor. To get the work area of a monitor other than the primary monitor, call GetMonitorInfo.
The zskin13.zip file has been updated to use HI / LO instead of HiWrd LoWrd and there is a small boost of the display speed on XP version (see change below)
If you don't want to download the whole ZIP file again, then remove the code shown below in red that is located at the end of zskin13.bas.
CASE %WM_MOVING
IF zDwmIsCompositionEnabled() = 0 THEN
IF SK_AEROEMULATE() AND zIsAeroLayered() THEN
DIM rw AS RECT PTR
rw = lparam
CALL zUpdateWindow(hWnd, 0)
CALL zMoveBackground(@rw.nLeft,@rw.nTop)
CALL zUpdateWindow(hWnd, 0)
FUNCTION = 1: EXIT FUNCTION
END IF
END IF
' //END AERO section ---------------------------------------------------------
Thanks Patrice,
again very useful information.
Thanks a lot,
Petr
The first post of this thread has been updated, to fix the ZIP file corruption caused by the "Server Collapse".
...