home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / mfc / advanced / mtgdi / threads.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-03-27  |  10.8 KB  |  342 lines

  1. // threads.cpp : contains all the different GDI thread classes
  2. //
  3. // This is a part of the Microsoft Foundation Classes C++ library.
  4. // Copyright (C) 1992-1998 Microsoft Corporation
  5. // All rights reserved.
  6. //
  7. // This source code is only intended as a supplement to the
  8. // Microsoft Foundation Classes Reference and related
  9. // electronic documentation provided with the library.
  10. // See these sources for detailed information regarding the
  11. // Microsoft Foundation Classes product.
  12.  
  13. #include "stdafx.h"
  14. #include "mtgdi.h"
  15. #include "threads.h"
  16.  
  17. #ifdef _DEBUG
  18. #undef THIS_FILE
  19. static char BASED_CODE THIS_FILE[] = __FILE__;
  20. #endif
  21.  
  22. // critical section to protect while drawing to the DC
  23. CRITICAL_SECTION CGDIThread::m_csGDILock;
  24.  
  25. // m_hAnotherDead is used to signal that one or more threads have ended
  26. //  (it is an auto-reset event; and starts out not signaled)
  27. HANDLE CGDIThread::m_hAnotherDead = CreateEvent(NULL, FALSE, FALSE, NULL);
  28.  
  29. /////////////////////////////////////////////////////////////////////////////
  30. // CGDIThread
  31.  
  32. IMPLEMENT_DYNAMIC(CGDIThread, CWinThread)
  33.  
  34. BEGIN_MESSAGE_MAP(CGDIThread, CWinThread)
  35.     //{{AFX_MSG_MAP(CGDIThread)
  36.         // NOTE - the ClassWizard will add and remove mapping macros here.
  37.     //}}AFX_MSG_MAP
  38. END_MESSAGE_MAP()
  39.  
  40. CGDIThread::CGDIThread(CWnd* pWnd, HDC hDC)
  41. {
  42.     m_bAutoDelete = FALSE;
  43.     m_pMainWnd = pWnd;
  44.     m_pMainWnd->GetClientRect(&m_rectBorder);
  45.     m_hDC = hDC;
  46.  
  47.     // kill event starts out in the signaled state
  48.     m_hEventKill = CreateEvent(NULL, TRUE, FALSE, NULL);
  49.     m_hEventDead = CreateEvent(NULL, TRUE, FALSE, NULL);
  50. }
  51.  
  52. // The reason we don't just get a CDC from our owner and simply use it is because
  53. // MFC GDI objects can't be passed between threads.  So we must instead pass a
  54. // handle and then reconvert them back to MFC objects.  The reason  for this is
  55. // because MFC maintains a list of all GDI objects on a per thread basis.  So if
  56. // you pass a GDI object to another thread, it won't be in the correct list
  57. // and MFC will assert.
  58.  
  59. BOOL CGDIThread::InitInstance()
  60. {
  61.     // thread setup
  62.     m_dc.Attach(m_hDC);
  63.  
  64.     // loop but check for kill notification
  65.     while (WaitForSingleObject(m_hEventKill, 0) == WAIT_TIMEOUT)
  66.         SingleStep();
  67.  
  68.     // thread cleanup
  69.     m_dc.Detach();
  70.  
  71.     // avoid entering standard message loop by returning FALSE
  72.     return FALSE;
  73. }
  74.  
  75. void CGDIThread::Delete()
  76. {
  77.     // calling the base here won't do anything but it is a good habit
  78.     CWinThread::Delete();
  79.  
  80.     // acknowledge receipt of kill notification
  81.     VERIFY(SetEvent(m_hEventDead));
  82.     VERIFY(SetEvent(m_hAnotherDead));
  83. }
  84.  
  85. CGDIThread::~CGDIThread()
  86. {
  87.     CloseHandle(m_hEventKill);
  88.     CloseHandle(m_hEventDead);
  89. }
  90.  
  91. void CGDIThread::KillThread()
  92. {
  93.     // Note: this function is called in the context of other threads,
  94.     //  not the thread itself.
  95.  
  96.     // reset the m_hEventKill which signals the thread to shutdown
  97.     VERIFY(SetEvent(m_hEventKill));
  98.  
  99.     // allow thread to run at higher priority during kill process
  100.     SetThreadPriority(THREAD_PRIORITY_ABOVE_NORMAL);
  101.     WaitForSingleObject(m_hEventDead, INFINITE);
  102.     WaitForSingleObject(m_hThread, INFINITE);
  103.  
  104.     // now delete CWinThread object since no longer necessary
  105.     delete this;
  106. }
  107.  
  108. void CGDIThread::UpdateBorder()
  109. {
  110.     // Since another thread can access this variable, we block them when we write
  111.     // it. In this case, it is overkill since the other thread would just animate
  112.     // in the wrong direction while reading intermediate values and finally adjust
  113.     // itself when the new value is valid.  It is a good idea to avoid such
  114.     // anomalies, however.
  115.  
  116.     EnterCriticalSection(&CGDIThread::m_csGDILock);
  117.     {
  118.         m_pMainWnd->GetClientRect(&m_rectBorder);
  119.     }
  120.     LeaveCriticalSection(&CGDIThread::m_csGDILock);
  121. }
  122.  
  123. /////////////////////////////////////////////////////////////////////////////
  124. // CBallThread
  125.  
  126. IMPLEMENT_DYNAMIC(CBallThread, CGDIThread)
  127.  
  128. BEGIN_MESSAGE_MAP(CBallThread, CGDIThread)
  129.     //{{AFX_MSG_MAP(CBallThread)
  130.         // NOTE - the ClassWizard will add and remove mapping macros here.
  131.     //}}AFX_MSG_MAP
  132. END_MESSAGE_MAP()
  133.  
  134. CBallThread::CBallThread(CWnd* pWnd, HDC hDC, CPoint ptPos, CPoint ptVel,
  135.     CSize Size, COLORREF color)
  136.     : CGDIThread(pWnd,hDC)
  137. {
  138.     m_rectPosition.SetRect(ptPos.x,ptPos.y,ptPos.x+Size.cx,ptPos.y+Size.cx);
  139.     m_ptVelocity = ptVel;
  140.  
  141.     CBrush brush(color);
  142.     m_hBrush = (HBRUSH)brush.Detach();
  143. }
  144.  
  145. // The reason we don't just store the CBrush from our owner and simply use it is
  146. // because MFC GDI objects can't be passed between threads.  So we must instead
  147. // pass a handle and then reconvert them back to MFC objects.  The reason for
  148. // this is because MFC maintains a list of all GDI objects on a per thread basis.
  149. // So if you pass a GDI object to another thread, it won't be in the correct
  150. // list and MFC will assert.
  151.  
  152. BOOL CBallThread::InitInstance()
  153. {
  154.     m_brush.Attach(m_hBrush);
  155.  
  156.     return CGDIThread::InitInstance();
  157. }
  158.  
  159. void CBallThread::SingleStep()
  160. {
  161.     m_rectPosition.OffsetRect(m_ptVelocity);
  162.  
  163.     if (m_rectPosition.top<m_rectBorder.top)
  164.         m_ptVelocity.y = (m_ptVelocity.y>0) ? m_ptVelocity.y : -m_ptVelocity.y;
  165.  
  166.     if (m_rectPosition.bottom>m_rectBorder.bottom)
  167.         m_ptVelocity.y = (m_ptVelocity.y>0) ? -m_ptVelocity.y : m_ptVelocity.y;
  168.  
  169.     if (m_rectPosition.left<m_rectBorder.left)
  170.         m_ptVelocity.x = (m_ptVelocity.x>0) ? m_ptVelocity.x : -m_ptVelocity.x;
  171.  
  172.     if (m_rectPosition.right>m_rectBorder.right)
  173.         m_ptVelocity.x = (m_ptVelocity.x>0) ? -m_ptVelocity.x : m_ptVelocity.x;
  174.  
  175.     // Since all threads share the same HDC it is necessary
  176.     // to block all other threads while we render in the HDC
  177.     EnterCriticalSection(&CGDIThread::m_csGDILock);
  178.     {
  179.         CBrush* oldbrush;
  180.  
  181.         oldbrush = m_dc.SelectObject(&m_brush);
  182.         m_dc.Ellipse(m_rectPosition);
  183.         m_dc.SelectObject(oldbrush);
  184.  
  185.         // Win32 optimizes GDI calls by collecting them in a batch
  186.         // and then thunking the whole batch at once on a per
  187.         // thread basis.  Since we share an HDC with multiple
  188.         // threads, we must flush the batch before yielding to
  189.         // other threads that will adjust the HDC.  To see what
  190.         // I mean, comment out the GdiFlush() function call and
  191.         // watch the results.
  192.         GdiFlush();
  193.     }
  194.     LeaveCriticalSection(&CGDIThread::m_csGDILock);
  195. }
  196.  
  197. /////////////////////////////////////////////////////////////////////////////
  198. //
  199. // CRectThread
  200. //
  201. /////////////////////////////////////////////////////////////////////////////
  202.  
  203. IMPLEMENT_DYNAMIC(CRectThread, CGDIThread)
  204.  
  205. BEGIN_MESSAGE_MAP(CRectThread, CCmdTarget)
  206.     //{{AFX_MSG_MAP(CRectThread)
  207.         // NOTE - the ClassWizard will add and remove mapping macros here.
  208.     //}}AFX_MSG_MAP
  209. END_MESSAGE_MAP()
  210.  
  211. CRectThread::CRectThread(CWnd* pWnd, HDC hDC, CPoint ptPos, CPoint ptVel,
  212.     CSize Size, COLORREF color) : CGDIThread(pWnd, hDC)
  213. {
  214.     m_rectPosition.SetRect(ptPos.x ,ptPos.y, ptPos.x+Size.cx, ptPos.y+Size.cy);
  215.     m_ptVelocity = ptVel;
  216.  
  217.     CBrush brush(color);
  218.     m_hBrush = (HBRUSH)brush.Detach();
  219. }
  220.  
  221. BOOL CRectThread::InitInstance()
  222. {
  223.     m_brush.Attach(m_hBrush);
  224.  
  225.     return CGDIThread::InitInstance();
  226. }
  227.  
  228. void CRectThread::SingleStep()
  229. {
  230.     m_rectPosition.OffsetRect(m_ptVelocity);
  231.  
  232.     if (m_rectPosition.top<m_rectBorder.top)
  233.         m_ptVelocity.y = (m_ptVelocity.y>0) ? m_ptVelocity.y : -m_ptVelocity.y;
  234.  
  235.     if (m_rectPosition.bottom>m_rectBorder.bottom)
  236.         m_ptVelocity.y = (m_ptVelocity.y>0) ? -m_ptVelocity.y : m_ptVelocity.y;
  237.  
  238.     if (m_rectPosition.left<m_rectBorder.left)
  239.         m_ptVelocity.x = (m_ptVelocity.x>0) ? m_ptVelocity.x : -m_ptVelocity.x;
  240.  
  241.     if (m_rectPosition.right>m_rectBorder.right)
  242.         m_ptVelocity.x = (m_ptVelocity.x>0) ? -m_ptVelocity.x : m_ptVelocity.x;
  243.  
  244.     // Since all threads share the same HDC it is necessary
  245.     // to block all other threads while we render in the HDC
  246.     EnterCriticalSection(&CGDIThread::m_csGDILock);
  247.     {
  248.         CBrush* oldbrush;
  249.  
  250.         oldbrush = m_dc.SelectObject(&m_brush);
  251.         m_dc.Rectangle(m_rectPosition);
  252.         m_dc.SelectObject(oldbrush);
  253.  
  254.         // Win32 optimizes GDI calls by collecting them in a batch
  255.         // and then thunking the whole batch at once on a per
  256.         // thread basis.  Since we share an HDC with multiple
  257.         // threads, we must flush the batch before yielding to
  258.         // other threads that will adjust the HDC.  To see what
  259.         // I mean, comment out the GdiFlush() function call and
  260.         // watch the results.
  261.         GdiFlush();
  262.     }
  263.     LeaveCriticalSection(&CGDIThread::m_csGDILock);
  264. }
  265.  
  266. /////////////////////////////////////////////////////////////////////////////
  267. // CLineThread
  268.  
  269. IMPLEMENT_DYNAMIC(CLineThread, CGDIThread)
  270.  
  271. BEGIN_MESSAGE_MAP(CLineThread, CCmdTarget)
  272.     //{{AFX_MSG_MAP(CLineThread)
  273.         // NOTE - the ClassWizard will add and remove mapping macros here.
  274.     //}}AFX_MSG_MAP
  275. END_MESSAGE_MAP()
  276.  
  277. CLineThread::CLineThread(CWnd* pWnd, HDC hDC, CPoint ptPos1, CPoint ptVel1,
  278.     CPoint ptVel2, CSize Size, COLORREF color) : CGDIThread(pWnd, hDC)
  279. {
  280.     m_ptPosition1 = ptPos1;
  281.     m_ptPosition2 = ptPos1 + Size;
  282.     m_ptVelocity1 = ptVel1;
  283.     m_ptVelocity2 = ptVel2;
  284.  
  285.     CPen pen(PS_SOLID, 1, color);
  286.     m_hPen = (HPEN)pen.Detach();
  287. }
  288.  
  289. BOOL CLineThread::InitInstance()
  290. {
  291.     m_pen.Attach(m_hPen);
  292.  
  293.     return CGDIThread::InitInstance();
  294. }
  295.  
  296. void CLineThread::SingleStep()
  297. {
  298.     m_ptPosition1.Offset(m_ptVelocity1);
  299.     m_ptPosition2.Offset(m_ptVelocity2);
  300.  
  301.     if (m_ptPosition1.y<m_rectBorder.top)
  302.         m_ptVelocity1.y = (m_ptVelocity1.y>0) ? m_ptVelocity1.y : -m_ptVelocity1.y;
  303.     else if (m_ptPosition1.y>m_rectBorder.bottom)
  304.         m_ptVelocity1.y = (m_ptVelocity1.y>0) ? -m_ptVelocity1.y : m_ptVelocity1.y;
  305.  
  306.     if (m_ptPosition2.y<m_rectBorder.top)
  307.         m_ptVelocity2.y = (m_ptVelocity2.y>0) ? m_ptVelocity2.y : -m_ptVelocity2.y;
  308.     else if (m_ptPosition2.y>m_rectBorder.bottom)
  309.         m_ptVelocity2.y = (m_ptVelocity2.y>0) ? -m_ptVelocity2.y : m_ptVelocity2.y;
  310.  
  311.     if (m_ptPosition1.x<m_rectBorder.left)
  312.         m_ptVelocity1.x = (m_ptVelocity1.x>0) ? m_ptVelocity1.x : -m_ptVelocity1.x;
  313.     else if (m_ptPosition1.x>m_rectBorder.right)
  314.         m_ptVelocity1.x = (m_ptVelocity1.x>0) ? -m_ptVelocity1.x : m_ptVelocity1.x;
  315.  
  316.     if (m_ptPosition2.x<m_rectBorder.left)
  317.         m_ptVelocity2.x = (m_ptVelocity2.x>0) ? m_ptVelocity2.x : -m_ptVelocity2.x;
  318.     else if (m_ptPosition2.x>m_rectBorder.right)
  319.         m_ptVelocity2.x = (m_ptVelocity2.x>0) ? -m_ptVelocity2.x : m_ptVelocity2.x;
  320.  
  321.     // Since all threads share the same HDC it is necessary
  322.     // to block all other threads while we render in the HDC
  323.     EnterCriticalSection(&CGDIThread::m_csGDILock);
  324.     {
  325.         CPen* oldpen;
  326.  
  327.         oldpen = m_dc.SelectObject(&m_pen);
  328.         m_dc.MoveTo(m_ptPosition1);
  329.         m_dc.LineTo(m_ptPosition2);
  330.         m_dc.SelectObject(oldpen);
  331.         // Win32 optimizes GDI calls by collecting them in a batch
  332.         // and then thunking the whole batch at once on a per
  333.         // thread basis.  Since we share an HDC with multiple
  334.         // threads, we must flush the batch before yielding to
  335.         // other threads that will adjust the HDC.  To see what
  336.         // I mean, comment out the GdiFlush() function call and
  337.         // watch the results.
  338.         GdiFlush();
  339.     }
  340.     LeaveCriticalSection(&CGDIThread::m_csGDILock);
  341. }
  342.