Thread: Screen flickering while running

  1. #1
    Registered User
    Join Date
    Nov 2009
    Posts
    151

    Screen flickering while running

    When the program is running the entire screen (not just the game screen) starts flickering and you can't close out of it directly. Here is my code

    main.h
    Code:
    #include <windows.h>
    #define IDB_BOX 1
    
    typedef int BoxPos;
    const BoxPos TopLeft = 0,
                 TopRight = 1,
                 BottomRight = 2,
                 BottomLeft = 3;
    
    
    HWND g_hWindow;
    HINSTANCE g_hInstance;
    
    class Box
    {
        protected:
          HBITMAP m_hBox;
          BITMAPINFO* m_BitmapInfo;
          BoxPos m_bpBoxPos;
          RECT m_rcClickZone;
          int m_iWidth;
          int m_iHeight;
          BOOL m_bMoving;
    
        public:
          Box();
          ~Box();
    
          void UpdateBox();
          void GamePaint(HDC hDC);
          void LeftClick(int x, int y);
    
          int m_iXPos;
          int m_iYPos;
    };
    
    Box* g_pBox;
    
    void GameEnd();
    main.cpp
    Code:
    #include "main.h"
    
    Box::Box()
    {
        m_hBox = LoadBitmap(g_hInstance, MAKEINTRESOURCE(IDB_BOX));
        m_bpBoxPos = TopLeft;
    
        m_iWidth = 32;//The box is a 32x32 pixel square
        m_iHeight = 32;
    
        m_rcClickZone.left = 0;
        m_rcClickZone.top = 0;
        m_rcClickZone.right = m_iWidth;
        m_rcClickZone.bottom = m_iHeight;
    
        m_iXPos = m_iYPos = 0;
    }
    
    Box::~Box()
    {
    }
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
        switch(msg)
        {
            case WM_CLOSE:
              DestroyWindow(g_hWindow);
              break;
            case WM_DESTROY:
              PostQuitMessage(0);
              break;
    
            case WM_PAINT:
              HDC hDC;
              PAINTSTRUCT ps;
    
              hDC = BeginPaint(hwnd, &ps);
    
              g_pBox->GamePaint(hDC);
    
              EndPaint(hwnd, &ps);
              break;
    
              case WM_LBUTTONDOWN:
                g_pBox->LeftClick(LOWORD(lParam), HIWORD(lParam));
                break;
    
            default:
              return DefWindowProc(hwnd, msg, wParam, lParam);
              break;
        }
    }
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow)
    {
        WNDCLASSEX WinClass;
        MSG msg;
        LPSTR szClassName = "BoxApp";
    
        g_pBox = new Box();
    
        if(g_pBox == NULL)
          MessageBox(g_hWindow, "g_pBox was no initialized", "ERROR", MB_ICONEXCLAMATION);
    
        //Register the window class
        WinClass.cbSize = sizeof(WNDCLASSEX);
        WinClass.style = 0;
        WinClass.lpfnWndProc = WndProc;
        WinClass.cbClsExtra = 0;
        WinClass.cbWndExtra = 0;
        WinClass.hInstance = g_hInstance;
        WinClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        WinClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
        WinClass.hCursor = LoadCursor(NULL, IDC_ARROW);
        WinClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
        WinClass.lpszClassName = szClassName;
        WinClass.lpszMenuName = NULL;
    
        if(!RegisterClassEx(&WinClass))
        {
            MessageBox(NULL, "Window Registration Failed!", "Error!",
    			MB_ICONEXCLAMATION | MB_OK);
    		return 0;
        }
    
        CreateWindowEx(WS_EX_CLIENTEDGE, szClassName, "The Box App",
                        WS_VISIBLE | WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480,
                        NULL, NULL, g_hInstance, NULL);
    
        if(iCmdShow > 0)
          ShowWindow(g_hWindow, iCmdShow);
        else
          ShowWindow(g_hWindow, SW_SHOWNORMAL);
        UpdateWindow(g_hWindow);
    
        while(TRUE)
        {
            g_pBox->UpdateBox();
            InvalidateRect(g_hWindow, NULL, FALSE);
            if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
            {
              TranslateMessage(&msg);
              DispatchMessage(&msg);
            }
        }
    
        GameEnd();
        return msg.wParam;
    }
    
    void Box::UpdateBox()
    {
        switch(m_bpBoxPos)
        {
            case TopLeft:
              m_rcClickZone.left = 0;
              m_rcClickZone.top = 0;
              m_rcClickZone.right = m_iWidth;
              m_rcClickZone.bottom = m_iHeight;
              break;
            case TopRight:
              m_rcClickZone.left = 640 - m_iWidth;
              m_rcClickZone.top = 0;
              m_rcClickZone.right = 640;
              m_rcClickZone.bottom = m_iHeight;
              break;
            case BottomRight:
              m_rcClickZone.left = 640 - m_iWidth;
              m_rcClickZone.top = 480 - m_iHeight;
              m_rcClickZone.right = 640;
              m_rcClickZone.bottom = 480;
              break;
            case BottomLeft:
              m_rcClickZone.left = 0;
              m_rcClickZone.top = 480 - m_iHeight;
              m_rcClickZone.right = m_iWidth;
              m_rcClickZone.bottom = 480;
              break;
        }
    }
    
    void Box::GamePaint(HDC hDC)
    {
        HDC hMemDC = CreateCompatibleDC(hDC);
        if(m_bMoving)
        {
          switch(m_bpBoxPos)
          {
              case TopLeft:
                if(m_iXPos >= 640)
                {
                  m_bpBoxPos = TopRight;
                  break;
                }
                m_iXPos++;
                break;
              case TopRight:
                if(m_iYPos >= 480)
                {
                  m_bpBoxPos = BottomRight;
                  break;
                }
                m_iYPos++;
                break;
              case BottomRight:
                if(m_iXPos <= 0)
                {
                  m_bpBoxPos = BottomLeft;
                  break;
                }
                m_iXPos--;
                break;
              case BottomLeft:
                if(m_iYPos <= 0)
                {
                  m_bpBoxPos = TopLeft;
                  break;
                }
                m_iYPos--;
                break;
          }
        }
        BitBlt(hDC, m_iXPos, m_iYPos, m_iWidth, m_iHeight, hMemDC, 0, 0, SRCCOPY);
    }
    
    void Box::LeftClick(int x, int y)
    {
        if(!m_bMoving)
        {
            if((x >= m_rcClickZone.left) && (x <= m_rcClickZone.right) && (y >= m_rcClickZone.bottom) && (y <= m_rcClickZone.top))
              m_bMoving = TRUE;
        }
    }
    
    void GameEnd()
    {
        delete g_pBox;
        delete g_hWindow;
        delete g_hInstance;
    }
    I ran the debugger, but it didn't pick anything up, so I'm posting the problem here.

  2. #2
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Code:
        while(TRUE)
        {
            g_pBox->UpdateBox();
            InvalidateRect(g_hWindow, NULL, FALSE);
            if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
            {
              TranslateMessage(&msg);
              DispatchMessage(&msg);
            }
        }
    Your probelm appears to be in the section of code above. If you look closely you are not assigning a value to g_hWindow which would have to come from your "Create Window" call.

    Code:
      g_hWindow = CreateWindowEx(WS_EX_CLIENTEDGE, szClassName, "The Box App",
                        WS_VISIBLE | WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480,
                        NULL, NULL, g_hInstance, NULL);
    Your whole screen is flashing because you are passing the resulting null or garbage handle into an InvalidateRect() command right at the top of your message dispatcher... in effect invalidating the desktop window hundreds of times per second, which will then redraw itself just as often. If you want to invalidate the box (main window) rectangle for drawing you should do that immediately after adding/changing something... NOT as part of the dispatcher loop... and handle the WM_PAINT message in your message tosser.

    There are 2 reasons you can't exit the program.
    1) you have provided no means for the operator to close it down.
    To provide operator exit you have to handle at least WM_CLOSE (generated by the X at the top right of the window) and WM_DESTROY which signals the system the window is being closed.

    2) Your message dispatcher wouldn't let them close it in any case.
    Here you must honor the WM_QUIT message, issued in code, to close the dispatcher loop so the whole thing falls out the bottom of your program.

    Code:
    // in message tosser
    case WM_CLOSE :
       // finalize variables, free mem, save settings etc
      PostMessage(g_hWindow,WM_DESTROY,0,0);
      return 0;
    case WM_DESTROY :
      PostQuitmessage(0);
      return 0;
    
    
    
    // message dispatcher
    While (GetMessage(&uMsg,g_hWindow,0,0) > 0)
       { if (! TranslateMessage(&uMsg))
            DispatchMessage(&uMsg) }
    GetMessage returns -1 when it encounters the WM_QUIT message sent by the PostQuitMessage() call.

    Yeah... I know... all this protocal is a pain but that's the way it is with windows.
    Last edited by CommonTater; 10-10-2010 at 02:04 PM.

  3. #3
    Registered User
    Join Date
    Nov 2009
    Posts
    151
    Thanks, but now I have another problem the box isn't being drawn onto the screen.

    Sorry if my remedial questions are getting annoying.

  4. #4
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Your original message dispatcher used a call to "updatebox" which I assume is an animation that needs to be refreshed periodically. This is a task for timers, not the message dispatcher.

    You need to call updatebox() once before the dispatcher starts and then start a timer to update it at some reasonable rate...

    Code:
    // at top of page
    #define ID_TIMER 2010
    
    // in your startup code
    updatebox();                                          // draw the first frame.
    SetTimer(g_hWindow,ID_TIMER,100,NULL);       // update 10 times a second
    // start dispatcher here
    
    
    
    // in your message tosser
    case WM_TIMER :
         If (wparam = ID_TIMER)
           updatebox();
         return 0;
    The timer fires every 100ms (as set above) and will cause an update to your box image.

    Also you need to move the paint code out of the message dispatcher into a proper function call. In windows code it's very unwise to put anything but function calls in switch statements. Any error during code in a switch statment could produce horrific errors, whereas in a function call it's only going to mess up that one function.

  5. #5
    Registered User
    Join Date
    Nov 2009
    Posts
    151
    Nope, it still didn't work, maybe it's something with the BitBlt function, but I can't figure it out.

  6. #6
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by bijan311 View Post
    Nope, it still didn't work, maybe it's something with the BitBlt function, but I can't figure it out.
    Is the image being drawn at least once before you start the timer?

    Perhaps you need to catch the WM_CREATE message from the main window to draw the first image at a time when you are sure the window handle (and it's DCs) are valid.

    I didn't look too closely at how you are drawing/updating the image, so you need to check your process closely.

  7. #7
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    Your paint function creates a HDC but never selects any image into it (so you have a blank 1x1 black and white bitmap in the HDC).

    You then draw this image to the screen.

    Code:
    void Box::GamePaint(HDC hDC)
    {
        HDC hMemDC = CreateCompatibleDC(hDC);//this allocates GDI memory
    
    //<snip> 
    //no call to modify the contents of the created HDC (ie to select in an image)
    
        BitBlt(hDC, m_iXPos, m_iYPos, m_iWidth, m_iHeight, hMemDC, 0, 0, SRCCOPY);
    
    //no call to DeleteDC() so the GDI memory has leaked
    }
    NOTE: You should ONLY redraw the update rectangle from the PAINTSTRUCT, not the whole screen (as BitBlt() is very slow).


    I would not use a timer to update the screen. Timer msgs are the lowest priority and will be ignored when your game is under load (paint is higher priority).
    This may result in 'un-even' drawing (as the actual time between the timer msgs varies).

    Use a 'game loop' something like...

    Code:
        while(1)
        {
             //is there a msg for the game?
             if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
            {
              //did the user QUIT?
              if(msg.message == WM_QUIT)
              {
                  //clean up and exit loop
                  break;
              }
              //else process msg
              TranslateMessage(&msg);
              DispatchMessage(&msg);
            }
             //else
            {
                   //update the game (UpdateBox())
            }
        }

    For faster painting;

    Always call UpdateWindow() after each call to InvalidateRect() for faster painting (so the paint msg is posted directly to the windows callback and not passed thru the OS msg queue).

    Only invalidate the minimum rectangle you have to redraw.

    Overdide the WM_ERASEBKGND msg (return true) to reduce the filckering.

    Quote Originally Posted by CommonTater View Post
    GetMessage returns -1 when it encounters the WM_QUIT message sent by the PostQuitMessage() call.
    According to MSDN GetMessage() returns;
    0 for WM_QUIT,
    >0 for other msgs
    -1 for errors
    Last edited by novacain; 10-10-2010 at 09:14 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

  8. #8
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    novacian... I'm no graphics expert (in fact I avoid it) so I'll defer to your wisdom here.

    But wouldn't a timer callback be more punctual?
    (just a thought, since they don't use the message queue).

  9. #9
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    Timer msgs use the OS msg queue (are queued).

    Quote Originally Posted by MSDN
    The WM_TIMER message is a low-priority message.

    The GetMessage and PeekMessage functions post this message only when no other higher-priority messages are in the thread's message queue.

    <snip>
    messages are processed in the following order:

    Sent messages
    Posted messages
    Input (hardware) messages and system internal events
    Sent messages (again)
    WM_PAINT messages
    WM_TIMER messages


    <snip>
    the system always posts messages at the end of a message queue. This ensures that a window receives its input messages in the proper first in, first out (FIFO) sequence.

    The WM_PAINT message, the WM_TIMER message, and the WM_QUIT message, however, are kept in the queue and are forwarded to the window procedure only when the queue contains no other messages.

    In addition, multiple WM_PAINT messages for the same window are combined into a single WM_PAINT message, consolidating all invalid parts of the client area into a single area. Combining WM_PAINT messages reduces the number of times a window must redraw the contents of its client area.

    Last edited by novacain; 10-10-2010 at 09:30 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

  10. #10
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    Code:
    if(m_hBox==NULL)
              //handle the image failed to load
    
    HDC hMemDC = CreateCompatibleDC(hDC);//this is a slow call, I would not do it too often (normally I create on init, use for life of app and clean up on quit)
    
    //put the image in our HDC and catch the default BITMAP in our created HDC
    HBITMAP* hOrigBMP = (HBITMAP*)SelectObject(hMemDC, m_hBox);
    
    //<snip> 
    
    BitBlt(hDC, m_iXPos, m_iYPos, m_iWidth, m_iHeight, hMemDC, 0, 0, SRCCOPY);
    
    //clean up GDIs
    //put the HDC back to the way we created it
    SelectObject(hMemDC, hOrigBMP);
    //and delete it
    DeleteDC(hMemDC);
    "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

  11. #11
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by novacain View Post
    Timer msgs use the OS msg queue (are queued).
    In their base form
    Code:
    SetTimer(Hwnd,evt_ID,Msecs,NULL);
    Yes they use the message queue, which contributes to their irregularity.

    However; you can also define a non-blocking timer using a callback procedure like this...
    Code:
    // callback proc
    VOID CALLBACK MyTimer(HWND win,UINT msg, UINT evt_ID,DWORD Ticks)
      {  // do something here }
    
    // timer setup
    SetTimer(NULL,evt_ID,Msecs,&MyTimer);
    ... which will never get anywhere near a message queue. When it's time is up it calls the procedure instead of sending a message. If you specify a window handle in the first position it will do both.

    I was thinking that rather than a hacked up dispatcher this might offer good enough resolution for most single player games...

    Anyway... I'll step back and let you take it from here. It's obvious you're better acquainted with windows graphics than I am. Most of my stuff is either text manipulation or lan based networking.

  12. #12
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    A timerproc still uses queued msgs, they are just automatically routed by the DefWindowProc() to the defined timerproc callback function.

    Quote Originally Posted by MSDN
    If you specify a TimerProc callback function, the default window procedure calls the callback function when it processes WM_TIMER. Therefore, you need to dispatch messages in the calling thread, even when you use TimerProc instead of processing WM_TIMER.
    The issue is that GetMessage() does not return until there is a msg for the app (so no other processing can take place while the app waits for a msg).

    PeekMessage() returns immediately, msg or no msg (so during idle times the app can process/draw at will).
    Last edited by novacain; 10-10-2010 at 10:50 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

  13. #13
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    The only problem with using PeekMessage instead of GetMessage is that your dispatcher loop is always running full tilt... 100% CPU usage is a very real risk.

  14. #14
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    Quote Originally Posted by CommonTater View Post
    The only problem with using PeekMessage instead of GetMessage is that your dispatcher loop is always running full tilt... 100% CPU usage is a very real risk.
    Is 100% CPU a problem?

    Usually you use some kind of 'throttling' (way to control the frame rate by sleeping/skipping until required) but yes, most games will use 100% CPU.

    Even MFC uses PeekMessage for its main loop (PumpMessage() call GetMessage() to remove msgs)

    Code:
    //CWinThread::Run()
    
    	// acquire and dispatch messages until a WM_QUIT message is received.
    	for (;;)
    	{
    		// phase1: check to see if we can do idle work
    		while (bIdle &&
    			!::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE))
    		{
    			// call OnIdle while in bIdle state
    			if (!OnIdle(lIdleCount++))
    				bIdle = FALSE; // assume "no idle" state
    		}
    
    		// phase2: pump messages while available
    		do
    		{
    			// pump message, but quit on WM_QUIT
    			if (!PumpMessage())
    				return ExitInstance();
    
    			// reset "no idle" state after pumping "normal" message
    			//if (IsIdleMessage(&m_msgCur))
    			if (IsIdleMessage(&(pState->m_msgCur)))
    			{
    				bIdle = TRUE;
    				lIdleCount = 0;
    			}
    
    		} while (::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE));
    	}
    "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

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. [C] GDI: how to erase material drawn at an entire screen DC
    By pc2-brazil in forum Windows Programming
    Replies: 3
    Last Post: 01-24-2009, 07:24 PM
  2. Check number of times a process is running
    By linuxwolf in forum Windows Programming
    Replies: 6
    Last Post: 10-17-2008, 11:08 AM
  3. Largest screen buffer size?
    By Ash1981 in forum C Programming
    Replies: 2
    Last Post: 01-30-2006, 04:31 AM
  4. Feedback: Functional Specification Wording
    By Ragsdale85 in forum C++ Programming
    Replies: 0
    Last Post: 01-18-2006, 04:56 PM
  5. screen flickering in MFC
    By MrBurns in forum Windows Programming
    Replies: 3
    Last Post: 09-01-2001, 01:42 PM