[Lazarus / Free Pascal] Getting rid of form flicker...

Discussion in 'Scripting' started by Michaela Joy, Jun 30, 2015.

  1. Michaela Joy

    Michaela Joy MDL Crazy Lady

    Jul 26, 2012
    3,558
    3,780
    120
    #1 Michaela Joy, Jun 30, 2015
    Last edited by a moderator: Apr 20, 2017
    Greetz, :)

    I'm building an app using Lazarus (Like Delphi but free. ) and I noticed a very annoying screen flicker. So I used all of the little Delphi tricks that I know. Much to my chagrin, I couldn't get rid of it. After poking around on the Lazarus forum, I got a hint as to what to do.

    Lazarus, unlike Delphi, doesn't allow you to subclass certain Windows message by overriding them in the form code. Instead, you have to subclass the Window procedure and handle them there. To get rid of flicker, you must handle the WM_ENTERSIZEMOVE and the WM_EXITSIZEMOVE messages. The LockWindowUpdate() function locks screen painting.
    Usually, it is called when the WM_SETREDRAW message is sent to a Window to inhibit drawing.

    So here's How I did it.

    First, I created a Window procedure. "OldWndProc" is used to store the original Window proc address. We will call it if we don't use a message.

    Code:
    
    implementation
    
    
    {$R *.lfm}
    
    var
      OldWndProc: WNDPROC;    // The old Window procedure...
      WindowLocked: Boolean = FALSE;   // A flag used to indicate whether or not we've locked the window.
    
    function Form1WndCallback(hwnd: HWND; uMsg: UINT; wParam: WParam; lParam: LParam): LRESULT; stdcall;
    begin
    case uMsg of
        WM_ENTERSIZEMOVE:
        begin
               if hwnd = Form1.Handle then
               begin
                     WindowLocked := TRUE;
                     LockWindowUpdate(hwnd);
               end;
               Result := 0;
               Exit;
        end;
    
        WM_EXITSIZEMOVE:
        begin
              if WindowLocked = TRUE then
              begin
                   WindowLocked := FALSE;
                   LockWindowUpdate(0);
                   if hwnd = Form1.Handle then
                          Form1.Repaint;
              end;
            Result := 0;
            Exit;
        end;
    
        WM_MOVING:
        begin
          if WindowLocked = TRUE then
          begin
                LockWindowUpdate(0);
    // This allows dynamic update for child and sibling forms.
                 LockWindowUpdate(hwnd);
           end;
        end;
    end;
    Result := CallWindowProc(OldWndProc,hwnd,uMsg,WParam,LParam);
    end;
    
    
    Now, we have to subclass the window. We add it on the form Create and remove it on the form Destroy.

    Code:
    procedure TForm1.FormCreate(Sender:TObject);
    begin
    {$hints off}
    OldWndProc := Windows.WNDPROC(SetWindowLongPtr(Form1.Handle,GWL_WNDPROC,PtrInt(@Form1WndCallback)));
    {$hints on}
    end;
    
    
    procedure TForm1.FormDestroy(Sender:TObject);
    begin
    {$hints off}
    SetWindowLongPtr(Form1.Handle,GWL_WNDPROC,PtrInt(OldWndProc));
    {$hints on}
    end;
    
    
    the Hints on and Hints off directives are needed to keep FPC from squaking like a retarded canary.

    And of course, Override the WM_ERASEBKGND message. You can do that in the Form code. :)

    Code:
    
    procedure TForm1.WMEraseBkgnd(var Msg: TWMEraseBkgnd);
    begin
    Msg.Result := 1;
    end;
    
    
    The result is a form that barely flickers at all. Bear in mind that you can now handle -any- Windows message using this technique.

    I Hope this helps someone. :)

    :MJ
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...