Jump to content

GUI double buffering with $WS_EX_COMPOSITED


KaFu
 Share

Recommended Posts

theirs is for sale, mine for free

No it's not, it's totaly free :)

I get the same flickering in Win7 32-bit with and without this style

So there must be something else that causing that flickering..

 

Spoiler

Using OS: Win 7 Professional, Using AutoIt Ver(s): 3.3.6.1 / 3.3.8.1

AutoIt_Rus_Community.png AutoIt Russian Community

My Work...

Spoiler

AutoIt_Icon_small.pngProjects: ATT - Application Translate Tool {new}| BlockIt - Block files & folders {new}| SIP - Selected Image Preview {new}| SISCABMAN - SciTE Abbreviations Manager {new}| AutoIt Path Switcher | AutoIt Menu for Opera! | YouTube Download Center! | Desktop Icons Restorator | Math Tasks | KeyBoard & Mouse Cleaner | CaptureIt - Capture Images Utility | CheckFileSize Program

AutoIt_Icon_small.pngUDFs: OnAutoItErrorRegister - Handle AutoIt critical errors {new}| AutoIt Syntax Highlight {new}| Opera Library! | Winamp Library | GetFolderToMenu | Custom_InputBox()! | _FileRun UDF | _CheckInput() UDF | _GUIInputSetOnlyNumbers() UDF | _FileGetValidName() UDF | _GUICtrlCreateRadioCBox UDF | _GuiCreateGrid() | _PathSplitByRegExp() | _GUICtrlListView_MoveItems - UDF | GUICtrlSetOnHover_UDF! | _ControlTab UDF! | _MouseSetOnEvent() UDF! | _ProcessListEx - UDF | GUICtrl_SetResizing - UDF! | Mod. for _IniString UDFs | _StringStripChars UDF | _ColorIsDarkShade UDF | _ColorConvertValue UDF | _GUICtrlTab_CoverBackground | CUI_App_UDF | _IncludeScripts UDF | _AutoIt3ExecuteCode | _DragList UDF | Mod. for _ListView_Progress | _ListView_SysLink | _GenerateRandomNumbers | _BlockInputEx | _IsPressedEx | OnAutoItExit Handler | _GUICtrlCreateTFLabel UDF | WinControlSetEvent UDF | Mod. for _DirGetSizeEx UDF
 
AutoIt_Icon_small.pngExamples: 
ScreenSaver Demo - Matrix included | Gui Drag Without pause the script | _WinAttach()! | Turn Off/On Monitor | ComboBox Handler Example | Mod. for "Thinking Box" | Cool "About" Box | TasksBar Imitation Demo

Like the Projects/UDFs/Examples? Please rate the topic (up-right corner of the post header: Rating AutoIt_Rating.gif)

* === My topics === *

==================================================
My_Userbar.gif
==================================================

 

 

 

AutoIt is simple, subtle, elegant. © AutoIt Team

Link to comment
Share on other sites

No it's not, it's totaly free B)

Even one less argument to make me trouble B)... thanks for letting me know your worries :), but I'll stick it to my long, long ignore list B)

- So there must be something else that causing that flickering..

- I would say that the cause is the same.

Sounds more like MS found an easy solution in XP and dropped it again in Win 6+ ;)
Link to comment
Share on other sites

  • 1 month later...

Sounds more like MS found an easy solution in XP and dropped it again in Win 6+ ;)

Found this interesting article about gui double buffering in vista...

Will have a deeper look later, I just posted to save the link :evil:, but maybe this can be glued together with $WS_EX_COMPOSITED to a function doing double buffering depending on @OSVersion...

Link to comment
Share on other sites

Here is an incomplete start... if anyone wants to take a look ;) ... at least the controls already stopped flickering, though the label is not erased properly... will be something obvious to the pros what I just don't see...

#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <WinAPI.au3>
#include <WindowsConstants.au3>

HotKeySet("{ESC}", "_Exit")

; ===============
; Test #3 with double buffer - Vista+
; ===============

; http://www.codeguru.com/cpp/w-p/vista/article.php/c15709/

$dllUxTheme = DllOpen("UxTheme.dll")
$dllUser32 = DllOpen("User32.dll")
$res = DllCall($dllUxTheme, "none", "BufferedPaintInit") ; BufferedPaintInit should be called once in every thread that wants to use buffered painting

$hGUI = GUICreate("Test #1 with double buffer - XP", 300, 200, -1, -1, -1)

$button = GUICtrlCreateButton("Double Buffer Test", 50, 45, 200, 20)

$label = GUICtrlCreateLabel("Double Buffer Test", 0, 115, 300, 20, $SS_CENTER)
GUICtrlSetBkColor(-1, $GUI_BKCOLOR_TRANSPARENT)

; Background control, create AFTER foreground control
GUICtrlCreateLabel("", 0, 0, 300, 200)
GUICtrlSetBkColor(-1, 0x21AB9A)

GUIRegisterMsg($WM_PAINT, "WM_PAINT")

GUISetState()
Sleep(1000)

For $i = 0 To 3000
    GUICtrlSetData($label, "Double Buffer Test " & $i)
    GUICtrlSetData($button, "Double Buffer Test " & $i)
Next

GUIDelete($hGUI)

DllCall($dllUxTheme, "none", "BufferedPaintUnInit")

_Exit()

Func _Exit()
    Exit
EndFunc   ;==>_Exit

Func WM_PAINT($hWnd, $Msg, $wParam, $lParam)
    $nNotifyCode = BitShift($wParam, 16)
    $nID = BitAND($wParam, 0x0000FFFF)
    $hCtrl = $lParam

    $tRect = _WinAPI_GetClientRect($hWnd)
    $hDC = _WinAPI_GetDC($hWnd)

    ; http://msdn.microsoft.com/en-us/library/dd162768%28VS.85%29.aspx
    Local Const $tagPAINT = "handle hdc; BOOL fErase; int Left;int Top;int Right;int Bottom; BOOL fRestore; BOOL fIncUpdate; BYTE rgbReserved[32]"
    $tPaint = DllStructCreate($tagPAINT)
    DllStructSetData($tPaint, 1, $hDC)
    DllStructSetData($tPaint, 3, DllStructGetData($tRect, 1))
    DllStructSetData($tPaint, 4, DllStructGetData($tRect, 2))
    DllStructSetData($tPaint, 5, DllStructGetData($tRect, 3))
    DllStructSetData($tPaint, 6, DllStructGetData($tRect, 4))
    $pPaint = DllStructGetPtr($tPaint)

    $res = DllCall($dllUser32, "hwnd", "BeginPaint", "hwnd", $hWnd, "ptr", $pPaint)

    Local Const $BPBF_COMPATIBLEBITMAP = 0
    Local $hDC_New

    $res = DllCall($dllUxTheme, "hwnd", "BeginBufferedPaint", "handle", $hDC, "ptr", $pPaint, "DWORD", $BPBF_COMPATIBLEBITMAP, "none", "", "handle", $hDC_New)
    ;ConsoleWrite(_WinAPI_GetLastError() & @tab & $res & @tab &  IsArray($res) & @tab & $hDC_New & @crlf)

    #cs
    ; http://msdn.microsoft.com/en-us/library/dd145216%28VS.85%29.aspx
    Local Const $PRF_CHECKVISIBLE = 0x00000001
    Local Const $PRF_NONCLIENT = 0x00000002
    Local Const $PRF_CLIENT = 0x00000004
    Local Const $PRF_ERASEBKGND = 0x00000008
    Local Const $PRF_CHILDREN = 0x00000010
    Local Const $PRF_OWNED = 0x00000020

    Local Const $WM_PRINT = 0x0317
    _SendMessage($hWnd, $WM_PRINT, $hDC_New, 0)
    _SendMessage($hWnd, $WM_PRINT, $hDC_New, $PRF_CHECKVISIBLE)
    #ce


    $res = DllCall($dllUxTheme, "hwnd", "EndBufferedPaint", "bool", 1)

    $res = DllCall($dllUser32, "hwnd", "EndPaint", "hwnd", $hWnd, "ptr", $pPaint)

    Return $GUI_RUNDEFMSG

EndFunc   ;==>WM_PAINT_Double_Buffered
Edited by KaFu
Link to comment
Share on other sites

Well, at the very least, it seems to me like you're calling BeginBufferedPaint and EndBufferedPaint completely incorrectly. That is, your DllCall()s to BeginBufferedPaint and EndBufferedPaint don't pass the correct arguments.

Func WM_PAINT($hWnd, $Msg, $wParam, $lParam)
    $tRect = _WinAPI_GetClientRect($hWnd)

    Local Const $tagPAINT = "handle hdc; BOOL fErase; int Left;int Top;int Right;int Bottom; BOOL fRestore; BOOL fIncUpdate; BYTE rgbReserved[32]"
    $tPaint = DllStructCreate($tagPAINT)
    $pPaint = DllStructGetPtr($tPaint)

    $res = DllCall($dllUser32, "hwnd", "BeginPaint", "hwnd", $hWnd, "ptr", $pPaint)
    Local $hDC_New = _WinAPI_CreateCompatibleDC(0)

    Local Const $BPBF_COMPATIBLEBITMAP = 0
    Local $hDC_New

    $res = DllCall($dllUxTheme, "hwnd", "BeginBufferedPaint", "handle", $res[0], "ptr", DllStructGetPtr($tRect), "DWORD", $BPBF_COMPATIBLEBITMAP, "ptr", 0, "handle*", $hDC_New)
 ;   ConsoleWrite(_WinAPI_GetLastError() & @tab & $res & @tab &  IsArray($res) & @tab & $hDC_New & @crlf)

    $res = DllCall($dllUxTheme, "hwnd", "EndBufferedPaint", "handle", $res[0], "bool", 1)

    $res = DllCall($dllUser32, "hwnd", "EndPaint", "hwnd", $hWnd, "ptr", $pPaint)

    Return $GUI_RUNDEFMSG

EndFunc   ;==>WM_PAINT_Double_Buffered

This is a more literal translation of the code in the link you posted above, but clearly, it isn't actually preventing flickers, but is instead, causing more. At the very least, it's doing something by passing what should be more correct parameters. Don't know what to say about the flickering though, since I'm not quite familiar with drawing contexts and all.

Edited by -Ultima-

[ WinINet.au3 | Array.au3 (Optimized) | _UnixTimeParse() ]

Link to comment
Share on other sites

Well, at the very least, it seems to me like you're calling BeginBufferedPaint and EndBufferedPaint completely incorrectly.

I knew I did something wrong but couldn't quiet figure out what it was, that why I threw in the ring what I got ;)... thanks for the input!
Link to comment
Share on other sites

Clearly forgot one thing or another... should have read page 2 ;)... step by step :evil:...

- Call BufferedPaintInit() and BufferedPaintUnInit() once per thread. Should be enough to call once at start and end?

- Added GUIRegisterMsg($WM_ERASEBKGND, "WM_ERASEBKGND") to return 1

- Added call to BufferedPaintClear() in WM_PAINT

- How to replace the "RenderWindow(hNewDC, rc);" function mentioned? I tried this with _SendMessage($hWnd, $WM_PRINT, $res[0], $PRF_NONCLIENT), but that doesn't seem to be the final answer

; Based on this script
; http://www.codeguru.com/cpp/w-p/vista/article.php/c15709/

#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <WinAPI.au3>
#include <WindowsConstants.au3>

HotKeySet("{ESC}", "_Exit")

$dllUxTheme = DllOpen("UxTheme.dll")
$dllUser32 = DllOpen("User32.dll")
$res = DllCall($dllUxTheme, "none", "BufferedPaintInit") ; BufferedPaintInit should be called once in every thread that wants to use buffered painting

$hGUI = GUICreate("Test #1 with double buffer - XP", 300, 200, -1, -1, -1)

$button = GUICtrlCreateButton("Double Buffer Test", 50, 45, 200, 20)

$label = GUICtrlCreateLabel("Double Buffer Test", 0, 115, 300, 20, $SS_CENTER)
GUICtrlSetBkColor(-1, $GUI_BKCOLOR_TRANSPARENT)

; Background control, create AFTER foreground control
GUICtrlCreateLabel("", 0, 0, 300, 200)
GUICtrlSetBkColor(-1, 0x21AB9A)

GUIRegisterMsg($WM_PAINT, "WM_PAINT")
GUIRegisterMsg($WM_ERASEBKGND, "WM_ERASEBKGND")

GUISetState()
Sleep(1000)

For $i = 0 To 3000
    GUICtrlSetData($label, "Double Buffer Test " & $i)
    GUICtrlSetData($button, "Double Buffer Test " & $i)
Next

GUIDelete($hGUI)

DllCall($dllUxTheme, "none", "BufferedPaintUnInit")

_Exit()

Func _Exit()
    Exit
EndFunc   ;==>_Exit


Func WM_PAINT($hWnd, $Msg, $wParam, $lParam)

    Local $tRect = _WinAPI_GetClientRect($hWnd)

    Local Const $tagPAINT = "handle hdc; BOOL fErase; int Left;int Top;int Right;int Bottom; BOOL fRestore; BOOL fIncUpdate; BYTE rgbReserved[32]"
    Local $tPaint = DllStructCreate($tagPAINT)
    Local $pPaint = DllStructGetPtr($tPaint)

    ; http://msdn.microsoft.com/en-us/library/dd183362%28VS.85%29.aspx
    $res = DllCall($dllUser32, "hwnd", "BeginPaint", "hwnd", $hWnd, "ptr", $pPaint)
    if @error then ConsoleWrite(1 & @tab & _WinAPI_GetLastError() & @crlf)

    Local Const $BPBF_COMPATIBLEBITMAP = 0x00000000
    Local Const $BPBF_DIB = 0x00000001
    Local $hDC_New

    ; http://msdn.microsoft.com/en-us/library/bb773257%28VS.85%29.aspx
    $res = DllCall($dllUxTheme, "hwnd", "BeginBufferedPaint", "handle", $res[0], "ptr", DllStructGetPtr($tRect), "DWORD", $BPBF_COMPATIBLEBITMAP, "ptr", 0, "handle", $hDC_New)
    if @error then ConsoleWrite(2 & @tab & _WinAPI_GetLastError() & @crlf)

    ; http://msdn.microsoft.com/en-us/library/bb773262%28VS.85%29.aspx
    DllCall($dllUxTheme, "hwnd", "BufferedPaintClear", "handle", $res[0], "ptr", DllStructGetPtr($tRect))
    if @error then ConsoleWrite(3 & @tab & _WinAPI_GetLastError() & @crlf)

    Local Const $WM_PRINT = 0x0317
    Local Const $PRF_NONCLIENT = 0x00000002
    ; http://msdn.microsoft.com/en-us/library/dd145216%28VS.85%29.aspx
    _SendMessage($hWnd, $WM_PRINT, $res[0], $PRF_NONCLIENT)

    ; http://msdn.microsoft.com/en-us/library/bb773343%28VS.85%29.aspx
    $res = DllCall($dllUxTheme, "hwnd", "EndBufferedPaint", "handle", $res[0], "bool", 1)
    if @error then ConsoleWrite(4 & @tab & _WinAPI_GetLastError() & @crlf)

    ; http://msdn.microsoft.com/en-us/library/dd162598%28VS.85%29.aspx
    $res = DllCall($dllUser32, "hwnd", "EndPaint", "hwnd", $hWnd, "ptr", $pPaint)
    if @error then ConsoleWrite(5 & @tab & _WinAPI_GetLastError() & @crlf)

    Return $GUI_RUNDEFMSG

EndFunc   ;==>WM_PAINT_Double_Buffered

Func WM_ERASEBKGND($hWnd, $Msg, $wParam, $lParam)
   return 1
EndFunc
Link to comment
Share on other sites

Is everything supposed to keep disappearing? Both controls show then hide.

Definitely not, I just posted what I got :evil:. Don't have a clue whats wrong atm, but wanted to give others a chance to point out my failures ;)...
Link to comment
Share on other sites

Clearly forgot one thing or another... should have read page 2 ;)... step by step :evil:...

- Call BufferedPaintInit() and BufferedPaintUnInit() once per thread. Should be enough to call once at start and end?

- Added GUIRegisterMsg($WM_ERASEBKGND, "WM_ERASEBKGND") to return 1

- Added call to BufferedPaintClear() in WM_PAINT

- How to replace the "RenderWindow(hNewDC, rc);" function mentioned? I tried this with _SendMessage($hWnd, $WM_PRINT, $res[0], $PRF_NONCLIENT), but that doesn't seem to be the final answer

You are wrong again :evil:

Your $hDC_New goes in by reference. So it should be (the way you put things):

$res = DllCall($dllUxTheme, "ptr", "BeginBufferedPaint", "ptr", $res[0], "ptr", DllStructGetPtr($tRect), "dword", $BPBF_COMPATIBLEBITMAP, "ptr", 0, "ptr*", 0)
if @error then ConsoleWrite(2 & @tab & _WinAPI_GetLastError() & @crlf)

Local $hDC_New = $res[5]

Don't mess with $WM_ERASEBKGND. That's bullshit. That have sense only in some special cases.

All in all, this is not applicable in AutoIt. At least not in a sensible way.

edit: *

Edited by trancexx

♡♡♡

.

eMyvnE

Link to comment
Share on other sites

  • 3 months later...

Was tinkering with Aero somewhere else, when it came to my mind that the reason why $WS_EX_COMPOSITED does not work on Win7 has to be related to it. So this method of GUI double buffering should work fine on XP, Vista and Win7, though as a workaround you've got to force Aero to be disabled. Test #1 and #3 should be flicker free on XP, on Vista and Win7 only Test #3 is flicker free.

#cs ----------------------------------------------------------------------------

 AutoIt Version: 3.3.6.0
 Author:         KaFu, Authenticity, Zedna

 Script Function:
    Double buffered GUI with $WS_EX_COMPOSITED
    See also http://msdn.microsoft.com/en-us/library/ms632680%28VS.85%29.aspx

#ce ----------------------------------------------------------------------------

#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>

HotKeySet("{ESC}", "_Exit")

; ===============
; Test #1 with double buffer - Aero Enabled
; ===============

Global Const $WS_EX_COMPOSITED = 0x2000000

$hGUI = GUICreate("#1 - Buffered - Aero On", 300, 200, -1, -1, -1, $WS_EX_COMPOSITED)

$button = GUICtrlCreateButton("Double Buffer Test", 50, 45, 200, 20)

$label = GUICtrlCreateLabel("Double Buffer Test", 0, 115, 300, 20, $SS_CENTER)
GUICtrlSetBkColor(-1, $GUI_BKCOLOR_TRANSPARENT)

; Background control, create AFTER foreground control
GUICtrlCreateLabel("", 0, 0, 300, 200)
GUICtrlSetBkColor(-1, 0x21AB9A)

GUISetState()
Sleep(1000)

For $i = 0 To 3000
    GUICtrlSetData($label, "Double Buffer Test " & $i)
    GUICtrlSetData($button, "Double Buffer Test " & $i)
Next

GUIDelete($hGUI)

; ===============
; Test #2 without double buffer - Aero Enabled
; ===============
$hGUI = GUICreate("#2 - Not Buffered - Aero On", 300, 200)

; Background control, create BEFORE foreground control
GUICtrlCreateLabel("", 0, 0, 300, 200)
GUICtrlSetBkColor(-1, 0x21AB9A)

$button = GUICtrlCreateButton("Double Buffer Test", 50, 45, 200, 20)

$label = GUICtrlCreateLabel("Double Buffer Test", 0, 115, 300, 20, $SS_CENTER)
GUICtrlSetBkColor(-1, $GUI_BKCOLOR_TRANSPARENT)

GUISetState()
Sleep(1000)

For $i = 0 To 3000
    GUICtrlSetData($label, "Double Buffer Test " & $i)
    GUICtrlSetData($button, "Double Buffer Test " & $i)
Next

GUIDelete($hGUI)

; ===============
; Disable Aero
; ===============

; http://msdn.microsoft.com/en-us/library/aa969510%28VS.85%29.aspx
Global Const $DWM_EC_DISABLECOMPOSITION = 0
DllCall("dwmapi.dll", "hwnd", "DwmEnableComposition", "uint", $DWM_EC_DISABLECOMPOSITION)

; ===============
; Test #3 with double buffer - Aero Disabled
; ===============

$hGUI = GUICreate("#3 - Buffered - Aero Off", 300, 200, -1, -1, -1, $WS_EX_COMPOSITED)

$button = GUICtrlCreateButton("Double Buffer Test", 50, 45, 200, 20)

$label = GUICtrlCreateLabel("Double Buffer Test", 0, 115, 300, 20, $SS_CENTER)
GUICtrlSetBkColor(-1, $GUI_BKCOLOR_TRANSPARENT)

; Background control, create AFTER foreground control
GUICtrlCreateLabel("", 0, 0, 300, 200)
GUICtrlSetBkColor(-1, 0x21AB9A)

GUISetState()
Sleep(1000)

For $i = 0 To 3000
    GUICtrlSetData($label, "Double Buffer Test " & $i)
    GUICtrlSetData($button, "Double Buffer Test " & $i)
Next

GUIDelete($hGUI)

; ===============
; Test #2 without double buffer - Aero Disabled
; ===============
$hGUI = GUICreate("#4 - Not Buffered - Aero Off", 300, 200)

; Background control, create BEFORE foreground control
GUICtrlCreateLabel("", 0, 0, 300, 200)
GUICtrlSetBkColor(-1, 0x21AB9A)

$button = GUICtrlCreateButton("Double Buffer Test", 50, 45, 200, 20)

$label = GUICtrlCreateLabel("Double Buffer Test", 0, 115, 300, 20, $SS_CENTER)
GUICtrlSetBkColor(-1, $GUI_BKCOLOR_TRANSPARENT)

GUISetState()
Sleep(1000)

For $i = 0 To 3000
    GUICtrlSetData($label, "Double Buffer Test " & $i)
    GUICtrlSetData($button, "Double Buffer Test " & $i)
Next

GUIDelete($hGUI)

_Exit()

Func _Exit()
    Exit
EndFunc   ;==>_Exit
Edited by KaFu
Link to comment
Share on other sites

Hi,

WS_EX_COMPOSITED is buggy (on XP) because the buttons of the system menu in the title bar (minimize, maximize and close) are not highlighted anymore. :idea:

There is no general concept to eleminate flickering from your application.

Some hints:

  • Don't use CS_HREDRAW/CS_VREDRAW in your window class
  • Handle the WM_ERASEBKGRND with return value TRUE
  • Take a look at WS_CLIPSIBLINGS/WS_CLIPCHILDREN
  • Don't use MoveWindow () in WM_SIZE message, use BeginDeferWindowPos, DeferWindowPos and EndDeferWindowPos
  • Use a memory DC and BitBlt
Another workaround I sometimes use is something like this:

Handle the messages WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE and (un)set the WS_EX_COMPOSITED style with SetWindowLongPtr.

EDIT:

The style WS_EX_COMPOSITED now IS declared in WindowsConstants.au3. :)

Regards

Greenhorn

Edited by Greenhorn
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...