Thread: open multiple windows

  1. #16
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    Quote Originally Posted by Steve A. View Post
    How would I get windows to redraw the entire contents of the window, including the gdi line I have drawn ?
    (without putting the line-draw routine inside of WM_PAINT, that is).
    Any drawing done outside the paint msg will only 'last' (be visible) until the next WM_PAINT msg.

    The easiest way is to put the drawing code inside the WM_PAINT. Problem is that this will often slow down the painting and can cause flicker/shearing of the screen (unless the drawing is VERY simple).


    Otherwise you need to 'double buffer' the drawing. This is creating a copy of the screen in memory and drawing this copy in response to a WM_PAINT msg.

    When the app starts;
    Create a DC compatible to the screen (CreateCompatibleDC) [lets call it memDC]
    Create a bitmap compatible to the screen (CreateCompatibleBitmap) and the same size as the app window or PC screen resolution (see below).
    SelectObject the bitmap into the memDC, saving the default bmp that is 'pushed' out.
    Clean up any GDI objects (ie release any GetDC()'s).

    When the screen changes;
    Draw the changes to the memDC
    Call for a paint msg (InvalidateRect() followed by UpdateWindow() )

    When you get a paint msg
    BitBlt the memDC to the the DC from the PAINTSTRUCT, using the rect from the PAINTSTRUCT (so we redraw the minimum amount and do all drawing in one call)

    When the app changes size;
    Clean up the DC and create a new one the correct size (OR create the compatible bmp the same size as the PC screen in the first place, instead of the app window size)
    redraw to the new size
    call for a paint

    When your app closes;
    SelectObject the default bmp back into the memDC
    DeleteDC the memDC
    DeleteObject the compatible bmp

    I have posted code before if you need (try a search for 'double buffering').
    Last edited by novacain; 02-22-2011 at 11:54 PM. Reason: clarity
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  2. #17
    Registered User
    Join Date
    Sep 2010
    Posts
    69
    Thanks for the detailed information novacain.
    Very instructive.

    Steve

  3. #18
    Registered User
    Join Date
    Sep 2010
    Posts
    69
    Well, I've taken your advise and read-up on "double buffering" as you suggested.
    From what I've read so far, it seems to be a good possibility that could work.
    In fact, I have no doubt that is the way to go. Now I just have to figure a way of implementing it in my situation. Should be do-able.

  4. #19
    Registered User
    Join Date
    Sep 2010
    Posts
    69
    Quote Originally Posted by novacain View Post
    Create a DC compatible to the screen (CreateCompatibleDC)
    Create a bitmap compatible to the screen (CreateCompatibleBitmap) .
    SelectObject the bitmap into the memDC, saving the default bmp that is 'pushed' out.
    Clean up any GDI objects (ie release any GetDC()'s).
    Okay, borrowing from older posts, I have recreated the code I need.
    Code:
        HdcWindow = BeginPaint(hwnd, &ps);
        HdcBuffer = CreateCompatibleDC(HdcWindow);
        DoubleBuffer = CreateCompatibleBitmap(HdcWindow, MYWIDTH, MYHEIGHT);
        OldBuffer = (HBITMAP)SelectObject(HdcBuffer, DoubleBuffer);
    
        FillRect(HdcBuffer, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH));
        HdcBitmap = CreateCompatibleDC(HdcWindow);
    For the most part it seems to be working.
    I have added this to WM_PAINT:
    Code:
        case WM_PAINT:
            BitBlt(HdcWindow, 0, 0, MYWIDTH, MYHEIGHT, HdcBuffer, 0, 0, SRCCOPY);
            break;
    the only problem is, that, for some reason WM_PAINT goes into an endless loop.
    I found that if I add this:
    Code:
        case WM_PAINT:
            hdc = BeginPaint(hwnd, &ps);
            EndPaint(hwnd, &ps);
            BitBlt(HdcWindow, 0, 0, MYWIDTH, MYHEIGHT, HdcBuffer, 0, 0, SRCCOPY);
            break;
    it works just fine. I don't get it...

    Draw the changes to the memDC
    Call for a paint msg (InvalidateRect() followed by UpdateWindow() )
    Well, when the only thing I draw to my buffer is a line, like this:
    Code:
    void DrawLine(HWND hwnd)
    {
        HDC myhDC = HdcBuffer;
        int x = 50, y = 50;
    
        MoveToEx(myhDC, x, y, NULL);
        LineTo(myhDC, x + 100, y);
    
        InvalidateRect(hwnd, &glrect, FALSE);
        UpdateWindow(hwnd);
    }
    this alone does not seem to work.
    However, if I add a BitBlt() at the end, like this:
    Code:
        InvalidateRect(hwnd, &glrect, FALSE);
        UpdateWindow(hwnd);
    
        BitBlt(HdcWindow, 0, 0, MYWIDTH, MYHEIGHT, myhDC, 0, 0, SRCCOPY);
    }
    then it works.
    Of course, I'm forceing it to work. I can live with this.

    What I think I need to fix, though, is the WM_PAINT thing.
    Any suggestions ?

  5. #20
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    You can not call BeginPaint() except in response to a WM_PAINT msg.

    You only need one buffer for this level of drawing, looks like you have one of my two back buffer systems, used for very complex drawing.

    Code:
    //WM_CREATE or WM_INITDIALOG
        //get the app windows DC (or use NULL for screen DC)
        HDC HdcWindow= GetDC(hwnd);
        //create comp DC and BMP
        HdcBuffer = CreateCompatibleDC(HdcWindow);  //global scope HDC
        DoubleBuffer = CreateCompatibleBitmap(HdcBuffer , MYWIDTH, MYHEIGHT); //global scope BMP
    
        //select in BMP, catching 1x1 monochrome default BMP
        OldBuffer = (HBITMAP)SelectObject(HdcBuffer, DoubleBuffer); //global scope BMP
    
        //clear screen
        FillRect(HdcBuffer, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH));
        //clean up GDI
        ReleaseDC(HdcWindow);
    Code:
    //copy the minimum amount from our global HDC (HdcBuffer) to the screen HDC
    
        case WM_PAINT:
            BeginPaint(hwnd, &ps); 
            BitBlt(ps.hdc, //can use the HDC in the PAINTSTRUCT or use the returned HDC from BeginPaint()
                     ps.rect.left,  ps.rect.top,  //draw only the invalidated rect not the whole screen
                     ps.rect.right - ps.rect.left,  ps.rect.bottom -  ps.rect.top,  
                     HdcBuffer, 0, 0, SRCCOPY);
            EndPaint(hwnd);
            break;
    Make sure this code is actually called and use the actual DC, not a copy of it.

    Code:
    //draw any changes to our global HDC (HdcBuffer) 
    void DrawLine(HWND hwnd)
    {
        //clear HDC with FillRect() if we need to erase previous lines etc
    
        //draw
        int x = 50, y = 50;
        
        MoveToEx(HdcBuffer, x, y, NULL);
        LineTo(HdcBuffer, x + 100, y);
    
        //for ease, repaint the whole screen 
        rect ClientRect = {0,0,0,0};
        GetClientRect(hwnd, &ClientRect);
        InvalidateRect(hwnd, &ClientRect, FALSE);
        UpdateWindow(hwnd);
    }
    Don't forget to clean up on WM_CLOSE (or similar).

    NOTE: written from memory, not compiled
    Last edited by novacain; 02-23-2011 at 10:37 PM.
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  6. #21
    Registered User
    Join Date
    Sep 2010
    Posts
    69
    Hey novacain,
    this latest post really explained a lot for me.
    Being a complete noob to GUI programming, I often don't know what goes where, when or why.

    With only minor mods I was able to take your latest code examples and plug it in to the correct slots. It works beautifully. I can't believe how well it works.
    Pretty slick.

    I am reminded that double buffering is a lot like pageing that we used to do back in the old DOS (QuickBasic) days. Where you drew directly on display adapter memory, then flipped pages.

    I'll be testing this out in my interpreter to see if there are any breaking points.

    Thanks.

  7. #22
    Registered User
    Join Date
    Sep 2010
    Posts
    69
    Hey novacain,
    Ok, I've been playing around a lot with the double buffer and it really fits the bill nicely.

    I have discovered, however, that if I resize the visible client area, progamatically or otherwise, it of course has no effect on the back-buffer, which I'm drawing to.
    i.e.
    1) let's say my front client area is one fourth the visible display size, (beginning in the upper left), as is my back-buffer,
    2) I paint my entire buffer area blue, the client rectangle gets repainted to blue,
    3) now, I issue a command to resize the client area to MAXIMIZE,
    it does and it gets repainted: ShowWindow(hwnd, SW_MAXIMIZE);

    4) now, I have a blue rectangle on an otherwise white screen.

    Hmmm ...!

    How do I make changes to the back-buffer, in keeping with changes to the client area ?

  8. #23
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    You need to process the WM_SIZE msgs.

    I suggest you create the buffers the same size as the entire screen when the app starts, so you do not have to re-create them if the client area gats bigger.

    When you get a WM_SIZE msg find the new client area, redraw the screen and call for a paint.
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  9. #24
    Registered User
    Join Date
    Sep 2010
    Posts
    69

    Thumbs up

    Quote Originally Posted by novacain View Post
    You need to process the WM_SIZE msgs.

    I suggest you create the buffers the same size as the entire screen when the app starts, so you do not have to re-create them if the client area gats bigger.

    When you get a WM_SIZE msg find the new client area, redraw the screen and call for a paint.
    Okay,...
    so the buffer (in the background) starts out at full screen size, ... to begin with. (?)
    Okay, I'll give that a try.
    Thanks.

  10. #25
    Registered User
    Join Date
    Sep 2010
    Posts
    69
    Hey novacain & guys,
    here is what my double buffer setup looks like:
    Code:
            case WM_CREATE:
                bufWIDTH = cxScreen;   // maximum size
                bufHEIGHT = cyScreen;  // maximum size
    
                glrect.left = 0;
                glrect.top = 0;
                glrect.right = bufWIDTH;
                glrect.bottom = bufHEIGHT;
    
                HdcWindow = GetDC(hwnd);
    
                HdcBuffer = CreateCompatibleDC(HdcWindow);
                DoubleBuffer = CreateCompatibleBitmap(HdcWindow, bufWIDTH, bufHEIGHT);
    
                OldBuffer = (HBITMAP)SelectObject(HdcBuffer, DoubleBuffer);
    
                FillRect(HdcBuffer, &glrect, (HBRUSH)GetStockObject(WHITE_BRUSH));
                ReleaseDC(hwnd, HdcWindow);
                break;
    
            case WM_PAINT:
                BeginPaint(hwnd, &glps);
                BitBlt(glps.hdc, 0, 0, myWIDTH, myHEIGHT, HdcBuffer, 0, 0, SRCCOPY);
    
                EndPaint(hwnd, &glps);
                break;
    
            case WM_SIZE:
                myWIDTH = LOWORD(lParam);
                myHEIGHT = HIWORD(lParam);
    
                break;
    This basic setup, by making the buffer the full screen size, now seems to be working when I resize the screen.
    Pertaining to resizing my foreground screen, is there anything else you recommend that I should be adding to WM_SIZE ?
    Thanks.

  11. #26
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    If you create the buffer (specifically the bitmap inside it) smaller than the screen, and the user maximaised the app, you would have to destroy the current bitmap and create a bigger one. This is slow while the app is trying to redraw the screen.

    So I create the buffer the same size as the screen at start.

    When the app changes size you need to redraw any part of the screen that is dependent on/proportional to the size.

    In your example you would call a method to fill the new client area with blue rectangle.

    So you need to add some code that calls your drawing routines and pass in the new size. The drawing routine will redraw based on the new size and call for a paint.
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  12. #27
    Registered User
    Join Date
    Sep 2010
    Posts
    69
    Quote Originally Posted by novacain
    -snip-
    So you need to add some code that calls your drawing routines and pass in the new size. The drawing routine will redraw based on the new size and call for a paint.
    I have followed your advise and I am very pleased with the results.
    Thanks novacain.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Windows File open permisisons
    By somekid413 in forum C Programming
    Replies: 2
    Last Post: 10-18-2010, 01:24 PM
  2. Memory Leak in AppWizard-Generated Code
    By jrohde in forum Windows Programming
    Replies: 4
    Last Post: 05-19-2010, 04:24 PM
  3. Multiple Windows
    By napalm in forum C Programming
    Replies: 5
    Last Post: 04-25-2008, 04:02 AM
  4. IE 6 status bar
    By DavidP in forum Tech Board
    Replies: 15
    Last Post: 10-23-2002, 05:31 PM
  5. Multiple Windows, One Program?
    By skyruler54 in forum C++ Programming
    Replies: 3
    Last Post: 10-12-2002, 08:29 PM