home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / graphics / directx / scrawl / scrawl.cpp < prev    next >
C/C++ Source or Header  |  1997-10-13  |  31KB  |  1,072 lines

  1. /**************************************************************************
  2.  
  3.     SCRAWL.CPP - A dumb drawing app demo for DirectInput
  4.  
  5.     Collects mouse data in various modes to demonstrate how it's done.
  6.  
  7.  **************************************************************************/
  8. /**************************************************************************
  9.  
  10.     (C) Copyright 1995-1997 Microsoft Corp.  All rights reserved.
  11.  
  12.     You have a royalty-free right to use, modify, reproduce and
  13.     distribute the Sample Files (and/or any modified version) in
  14.     any way you find useful, provided that you agree that
  15.     Microsoft has no warranty obligations or liability for any
  16.     Sample Application Files which are modified.
  17.  
  18.  **************************************************************************/
  19.  
  20. #include <windows.h>
  21. #include <windowsx.h>
  22. #include <stdarg.h>
  23.  
  24. #define DIRECTINPUT_VERSION 0x0300      /* Remain DX3-compatible */
  25. #include <dinput.h>
  26.  
  27. #include "scrawl.h"
  28.  
  29. /****************************************************************************
  30.  *
  31.  *      This is an incredibly dump app.  It just lets you "draw" on
  32.  *      a monochrome bitmap via DirectInput.  The purpose is not to
  33.  *      dazzle you with mind-altering brilliance.  It's just to show
  34.  *      how to use the DirectInput mouse interface.
  35.  *
  36.  ****************************************************************************/
  37.  
  38. /****************************************************************************
  39.  *
  40.  *      Manifest constants
  41.  *
  42.  ****************************************************************************/
  43.  
  44. #define DINPUT_BUFFERSIZE           16
  45.  
  46. #define DINPUT_CXBITMAP             512
  47. #define DINPUT_CYBITMAP             300
  48.  
  49. /****************************************************************************
  50.  *
  51.  *      Global variables
  52.  *
  53.  ****************************************************************************/
  54.  
  55. const char c_szAppName[] = "Scrawl";    /* My name */
  56.  
  57. HINSTANCE  g_hinst;                     /* My instance handle */
  58. BOOL       g_fActive;                   /* Am I the active window? */
  59.  
  60. int        g_x = DINPUT_CXBITMAP / 2;   /* Virtual x-coordinate */
  61. int        g_y = DINPUT_CYBITMAP / 2;   /* Virtual y-coordinate */
  62.  
  63. int        g_dxFuzz;                    /* Leftover x-fuzz from scaling */
  64. int        g_dyFuzz;                    /* Leftover y-fuzz from scaling */
  65. int        g_iSensitivity;              /* Mouse sensitivity */
  66.  
  67. HDC        g_hdc;                       /* Memory DC our picture lives in */
  68. HBITMAP    g_hbm;                       /* Our picture */
  69. HBITMAP    g_hbmDeselect;               /* Stock bitmap for deselecting */
  70.  
  71. HCURSOR    g_hcurCross;                 /* Crosshairs */
  72. int        g_cxCross;                   /* Width of crosshairs cursor */
  73. int        g_cyCross;                   /* Height of crosshairs cursor */
  74. int        g_dxCrossHot;                /* Hotspot location of crosshairs */
  75. int        g_dyCrossHot;                /* Hotspot location of crosshairs */
  76. int        g_fShowCursor = 1;           /* Should the cursor be shown? */
  77.  
  78. /****************************************************************************
  79.  *
  80.  *      DirectInput globals
  81.  *
  82.  ****************************************************************************/
  83.  
  84. LPDIRECTINPUT          g_pdi;
  85. LPDIRECTINPUTDEVICE    g_pMouse;
  86.  
  87. HANDLE                 g_hevtMouse;
  88.  
  89. /****************************************************************************
  90.  *
  91.  *      Complain
  92.  *
  93.  *      Whine and moan.
  94.  *
  95.  ****************************************************************************/
  96.  
  97. void CDECL
  98. Complain(HWND hwndOwner, HRESULT hr, LPCSTR pszFormat, ...)
  99. {
  100.     va_list ap;
  101.     char szBuf[1024];
  102.     char *pszBuf;
  103.  
  104.     va_start(ap, pszFormat);
  105.  
  106.     pszBuf = szBuf + wsprintf(szBuf, pszFormat, ap);
  107.  
  108.     va_end(ap);
  109.  
  110.     wsprintf(pszBuf, "\n\nError = %08x", hr);
  111.  
  112.     MessageBox(hwndOwner, szBuf, c_szAppName, MB_OK);
  113. }
  114.  
  115. /****************************************************************************
  116.  *
  117.  *      DIInit
  118.  *
  119.  *      Initialize the DirectInput variables.
  120.  *
  121.  ****************************************************************************/
  122.  
  123. BOOL DIInit(HWND hwnd)
  124. {
  125.     HRESULT hr;
  126.  
  127.     /*
  128.      *  Register with DirectInput and get an IDirectInput to play with.
  129.      */
  130.     hr = DirectInputCreate(g_hinst, DIRECTINPUT_VERSION, &g_pdi, NULL);
  131.  
  132.     if (FAILED(hr)) {
  133.         Complain(hwnd, hr, "DirectInputCreate");
  134.         return FALSE;
  135.     }
  136.  
  137.     /*
  138.      *  Obtain an interface to the system mouse device.
  139.      */
  140.     hr = g_pdi->CreateDevice(GUID_SysMouse, &g_pMouse, NULL);
  141.  
  142.     if (FAILED(hr)) {
  143.         Complain(hwnd, hr, "CreateDevice(SysMouse)");
  144.         return FALSE;
  145.     }
  146.  
  147.     /*
  148.      *  Set the data format to "mouse format".
  149.      */
  150.     hr = g_pMouse->SetDataFormat(&c_dfDIMouse);
  151.  
  152.     if (FAILED(hr)) {
  153.         Complain(hwnd, hr, "SetDataFormat(SysMouse, dfDIMouse)");
  154.         return FALSE;
  155.     }
  156.  
  157.  
  158.     /*
  159.      *  Set the cooperativity level.
  160.      */
  161.     hr = g_pMouse->SetCooperativeLevel(hwnd,
  162.                                        DISCL_EXCLUSIVE | DISCL_FOREGROUND);
  163.  
  164.     if (FAILED(hr)) {
  165.         Complain(hwnd, hr, "SetCooperativeLevel(SysMouse)");
  166.         return FALSE;
  167.     }
  168.  
  169.  
  170.     /*
  171.      *  Create the handle that tells us new data is available.
  172.      */
  173.     g_hevtMouse = CreateEvent(0, 0, 0, 0);
  174.  
  175.     if (g_hevtMouse == NULL) {
  176.         Complain(hwnd, GetLastError(), "CreateEvent");
  177.         return FALSE;
  178.     }
  179.  
  180.     /*
  181.      *  Associate the event with the device.
  182.      */
  183.     hr = g_pMouse->SetEventNotification(g_hevtMouse);
  184.  
  185.     if (FAILED(hr)) {
  186.         Complain(hwnd, hr, "SetEventNotification(SysMouse)");
  187.         return FALSE;
  188.     }
  189.  
  190.     /*
  191.      *  Set the buffer size to DINPUT_BUFFERSIZE elements.
  192.      *  The buffer size is a DWORD property associated with the device.
  193.      */
  194.     DIPROPDWORD dipdw =
  195.         {
  196.             {
  197.                 sizeof(DIPROPDWORD),        // diph.dwSize
  198.                 sizeof(DIPROPHEADER),       // diph.dwHeaderSize
  199.                 0,                          // diph.dwObj
  200.                 DIPH_DEVICE,                // diph.dwHow
  201.             },
  202.             DINPUT_BUFFERSIZE,              // dwData
  203.         };
  204.  
  205.     hr = g_pMouse->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph);
  206.  
  207.     if (FAILED(hr)) {
  208.         Complain(hwnd, hr, "Set buffer size(SysMouse)");
  209.         return FALSE;
  210.     }
  211.  
  212.     return TRUE;
  213.  
  214. }
  215.  
  216. /****************************************************************************
  217.  *
  218.  *      DITerm
  219.  *
  220.  *      Terminate our usage of DirectInput.
  221.  *
  222.  ****************************************************************************/
  223.  
  224. void DITerm(void)
  225. {
  226.     if (g_pdi)      g_pdi   ->Release(), g_pdi    = NULL;
  227.     if (g_pMouse)   g_pMouse->Release(), g_pMouse = NULL;
  228.  
  229.     if (g_hevtMouse) CloseHandle(g_hevtMouse), g_hevtMouse = NULL;
  230. }
  231.  
  232. /****************************************************************************
  233.  *
  234.  *      InvalidateCursorRect
  235.  *
  236.  *      Invalidate the rectangle that contains the cursor.
  237.  *
  238.  *      The coordinates are in client coordinates.
  239.  *
  240.  ****************************************************************************/
  241.  
  242. void InvalidateCursorRect(HWND hwnd)
  243. {
  244.     RECT rc = { g_x - g_dxCrossHot,             g_y - g_dyCrossHot,
  245.                 g_x - g_dxCrossHot + g_cxCross, g_y - g_dyCrossHot + g_cyCross };
  246.  
  247.     InvalidateRect(hwnd, &rc, 0);
  248. }
  249.  
  250. /****************************************************************************
  251.  *
  252.  *      UpdateCursorPosition
  253.  *
  254.  *      Move our private cursor in the requested direction, subject
  255.  *      to clipping, scaling, and all that other stuff.
  256.  *
  257.  *      This does not redraw the cursor.  You need to do that yourself.
  258.  *
  259.  ****************************************************************************/
  260.  
  261. void UpdateCursorPosition(int dx, int dy)
  262. {
  263.  
  264.     /*
  265.      *  Pick up any leftover fuzz from last time.  This is important
  266.      *  when scaling down mouse motions.  Otherwise, the user can
  267.      *  drag to the right extremely slow for the length of the table
  268.      *  and not get anywhere.
  269.      */
  270.     dx += g_dxFuzz;     g_dxFuzz = 0;
  271.     dy += g_dyFuzz;     g_dyFuzz = 0;
  272.  
  273.     switch (g_iSensitivity) {
  274.  
  275.     case 1:                             /* High sensitivity: Magnify! */
  276.         dx *= 2;
  277.         dy *= 2;
  278.         break;
  279.  
  280.     case -1:                            /* Low sensitivity: Scale down */
  281.         g_dxFuzz = dx % 2;              /* remember the fuzz for next time */
  282.         g_dyFuzz = dy % 2;
  283.         dx /= 2;
  284.         dy /= 2;
  285.         break;
  286.  
  287.     default:
  288.     case 0:                             /* No sensitivity */
  289.         ;                               /* No adjustments needed */
  290.     }
  291.  
  292.     g_x += dx;
  293.     g_y += dy;
  294.  
  295.     /* Clip the cursor to our client area */
  296.     if (g_x < 0)                g_x = 0;
  297.     if (g_x >= DINPUT_CXBITMAP) g_x = DINPUT_CXBITMAP - 1;
  298.  
  299.     if (g_y < 0)                g_y = 0;
  300.     if (g_y >= DINPUT_CYBITMAP) g_y = DINPUT_CYBITMAP - 1;
  301.  
  302. }
  303.  
  304. /****************************************************************************
  305.  *
  306.  *      Scrawl_SyncAcquire
  307.  *
  308.  *      Acquire or unacquire the devices, depending on the the g_fActive
  309.  *      flag.  This synchronizes the devices with our internal view of
  310.  *      the world.
  311.  *
  312.  *      Also repaint the cursor so that it hides/shows in sync with
  313.  *      acquisition.
  314.  *
  315.  ****************************************************************************/
  316.  
  317. void
  318. Scrawl_SyncAcquire(HWND hwnd)
  319. {
  320.     if (g_fActive) {
  321.         if (g_pMouse) g_pMouse->Acquire();
  322.     } else {
  323.         if (g_pMouse) g_pMouse->Unacquire();
  324.     }
  325.  
  326.     InvalidateCursorRect(hwnd);
  327. }
  328.  
  329. /****************************************************************************
  330.  *
  331.  *      Private messages
  332.  *
  333.  *      WM_SYNCACQUIRE forces us to re-synchronize our acquisition
  334.  *      with the world.
  335.  *
  336.  ****************************************************************************/
  337.  
  338. #define WM_SYNCACQUIRE      (WM_USER + 0)
  339.  
  340. /****************************************************************************
  341.  *
  342.  *      Scrawl_OnClear
  343.  *
  344.  *      Wipe out the bitmap.
  345.  *
  346.  ****************************************************************************/
  347.  
  348. void Scrawl_OnClear(HWND hwnd)
  349. {
  350.     /*
  351.      *  Start out all white.
  352.      */
  353.     PatBlt(g_hdc, 0, 0, DINPUT_CXBITMAP, DINPUT_CYBITMAP, WHITENESS);
  354.  
  355.     if (hwnd) {
  356.         InvalidateRect(hwnd, 0, 0);
  357.     }
  358. }
  359.  
  360. /****************************************************************************
  361.  *
  362.  *      Scrawl_OnCreate
  363.  *
  364.  *      Set up the window by appending our custom commands to the System
  365.  *      menu.
  366.  *
  367.  *      Also disable the menu items we don't want to see.
  368.  *
  369.  ****************************************************************************/
  370.  
  371. BOOL Scrawl_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
  372. {
  373.     HMENU hmenu = GetSystemMenu(hwnd, FALSE);
  374.  
  375.     EnableMenuItem(hmenu, SC_SIZE,     MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
  376.     EnableMenuItem(hmenu, SC_MAXIMIZE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
  377.  
  378.     AppendMenu(hmenu, MF_ENABLED | MF_STRING, IDC_CLEAR, "C&lear\tDel");
  379.     AppendMenu(hmenu, MF_ENABLED | MF_STRING, IDC_ABOUT, "&About\tF1");
  380.  
  381.     AppendMenu(hmenu, MF_ENABLED | MF_STRING | MF_POPUP,
  382.                       (UINT)LoadMenu(g_hinst,
  383.                                      MAKEINTRESOURCE(IDM_SENSITIVITY)),
  384.                      "Sensitivit&y");
  385.  
  386.     return 1;
  387. }
  388.  
  389. /****************************************************************************
  390.  *
  391.  *      Scrawl_OnInitMenuPopup
  392.  *
  393.  *      Initialize the sensitivity item accordingly.
  394.  *
  395.  ****************************************************************************/
  396.  
  397. void
  398. Scrawl_OnInitMenuPopup(HWND hwnd, HMENU hmenu, UINT item, BOOL fSystemMenu)
  399. {
  400.     int iSensitivity;
  401.     for (iSensitivity = -1; iSensitivity <= 1; iSensitivity++) {
  402.         if (g_iSensitivity == iSensitivity) {
  403.             CheckMenuItem(hmenu, IDC_SENSITIVITY_NORMAL + iSensitivity,
  404.                           MF_BYCOMMAND | MF_CHECKED);
  405.         } else {
  406.             CheckMenuItem(hmenu, IDC_SENSITIVITY_NORMAL + iSensitivity,
  407.                           MF_BYCOMMAND | MF_UNCHECKED);
  408.         }
  409.     }
  410.  
  411. }
  412.  
  413. /****************************************************************************
  414.  *
  415.  *      Scrawl_OnKeyDown
  416.  *
  417.  *      See if it's one of our accelerators.
  418.  *
  419.  ****************************************************************************/
  420.  
  421. void Scrawl_OnKeyDown(HWND hwnd, UINT vk, BOOL fDown, int cRepeat, UINT flags)
  422. {
  423.     switch (vk) {
  424.     case '1':
  425.     case '2':
  426.     case '3':
  427.         PostMessage(hwnd, WM_SYSCOMMAND, IDC_SENSITIVITY_NORMAL +
  428.                                          vk - '2', 0);
  429.         break;
  430.  
  431.     case VK_DELETE:
  432.         PostMessage(hwnd, WM_SYSCOMMAND, IDC_CLEAR, 0);
  433.         break;
  434.  
  435.     case VK_F1:
  436.         PostMessage(hwnd, WM_SYSCOMMAND, IDC_ABOUT, 0);
  437.         break;
  438.  
  439.     }
  440. }
  441.  
  442. /****************************************************************************
  443.  *
  444.  *      Scrawl_OnPaint
  445.  *
  446.  *      Blt out our bitmap and draw our cursor on top of it.
  447.  *
  448.  ****************************************************************************/
  449.  
  450. void
  451. Scrawl_OnPaint(HWND hwnd)
  452. {
  453.     PAINTSTRUCT ps;
  454.     HDC hdc = BeginPaint(hwnd, &ps);
  455.  
  456.     if (hdc) {
  457.  
  458.         BitBlt(hdc,
  459.                ps.rcPaint.left,
  460.                ps.rcPaint.top,
  461.                ps.rcPaint.right - ps.rcPaint.left,
  462.                ps.rcPaint.bottom - ps.rcPaint.top,
  463.                g_hdc,
  464.                ps.rcPaint.left,
  465.                ps.rcPaint.top,
  466.                SRCCOPY);
  467.  
  468.         if (g_fActive && g_fShowCursor) {
  469.             DrawIcon(hdc, g_x - g_dxCrossHot,
  470.                           g_y - g_dyCrossHot, g_hcurCross);
  471.         }
  472.  
  473.         EndPaint(hwnd, &ps);
  474.     }
  475. }
  476.  
  477. /****************************************************************************
  478.  *
  479.  *      Scrawl_OnButton0Down_FlushMotion
  480.  *
  481.  *      Flush out any motion that we are holding.
  482.  *
  483.  ****************************************************************************/
  484.  
  485. typedef struct BUTTON0INFO {
  486.  
  487.     HDC hdcWindow;
  488.     BOOL fMoved;
  489.     DWORD dwSeqLastSeen;
  490.  
  491. } BUTTON0INFO, *PBUTTON0INFO;
  492.  
  493. void Scrawl_OnButton0Down_FlushMotion(PBUTTON0INFO pb0i)
  494. {
  495.     if (pb0i->fMoved) {
  496.         pb0i->fMoved = 0;
  497.         pb0i->dwSeqLastSeen = 0;
  498.         LineTo(pb0i->hdcWindow, g_x, g_y);
  499.         LineTo(g_hdc, g_x, g_y);
  500.     }
  501.  
  502. }
  503.  
  504. /****************************************************************************
  505.  *
  506.  *      Scrawl_OnButton0Down
  507.  *
  508.  *      Enter draw mode.
  509.  *
  510.  *      If we are drawing a curve, then read buffered data and draw
  511.  *      lines from point to point.  By reading buffered data, we can
  512.  *      track the motion of the mouse accurately without coalescing.
  513.  *
  514.  *      This function illustrates how a non-message-based program can
  515.  *      process buffered data directly from a device, processing
  516.  *      messages only occasionally (as required by Windows).
  517.  *
  518.  *      This function also illustrates how an application can piece
  519.  *      together buffered data elements based on the sequence number.
  520.  *      A single mouse action (e.g., moving diagonally) is reported
  521.  *      as a series of events, all with the same sequence number.
  522.  *      Zero is never a valid DirectInput sequence number, so it is
  523.  *      safe to use it as a sentinel value.
  524.  *
  525.  ****************************************************************************/
  526.  
  527. void Scrawl_OnButton0Down(HWND hwnd)
  528. {
  529.     BUTTON0INFO b0i;
  530.  
  531.     /* Hide the cursor while scrawling */
  532.     g_fShowCursor = FALSE;
  533.     InvalidateCursorRect(hwnd);
  534.     UpdateWindow(hwnd);
  535.  
  536.     /*
  537.      *  For performance, draw directly onto the window's DC instead of
  538.      *  invalidating and waiting for the WM_PAINT message.  Of course,
  539.      *  we always draw onto our bitmap, too, since that's what really
  540.      *  counts.
  541.      */
  542.  
  543.     /* BUGBUG -- select a decent pen, too */
  544.     b0i.hdcWindow = GetDC(hwnd);
  545.     MoveToEx(b0i.hdcWindow, g_x, g_y, 0);
  546.     MoveToEx(g_hdc, g_x, g_y, 0);
  547.  
  548.     /* BUGBUG -- save old pen */
  549.     SelectObject(b0i.hdcWindow, GetStockObject(BLACK_PEN));
  550.     SelectObject(g_hdc, GetStockObject(BLACK_PEN));
  551.  
  552.     b0i.fMoved = 0;
  553.     b0i.dwSeqLastSeen = 0;
  554.  
  555.     /*
  556.      *  Keep reading data elements until we see a "mouse button up" event.
  557.      */
  558.     BOOL fDone = 0;
  559.     while (!fDone) {
  560.         DIDEVICEOBJECTDATA od;
  561.  
  562.         DWORD dwElements = 1;
  563.  
  564.         HRESULT hr = g_pMouse->GetDeviceData(
  565.                              sizeof(DIDEVICEOBJECTDATA), &od,
  566.                              &dwElements, 0);
  567.  
  568.         /* Unable to read data */
  569.         if (FAILED(hr)) {
  570.             break;
  571.         }
  572.  
  573.         /*
  574.          *  If no data available, finish the element we had been
  575.          *  collecting, and then process our message queue so
  576.          * the system don't think we're hung.
  577.          */
  578.         if (dwElements == 0) {
  579.  
  580.             /* If there is a partial motion, flush it out */
  581.             Scrawl_OnButton0Down_FlushMotion(&b0i);
  582.  
  583.             MSG msg;
  584.             while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
  585.                 /* If it's a quit message, we're outta here */
  586.                 if (msg.message == WM_QUIT) {
  587.                     fDone = TRUE;
  588.                     /*
  589.                      * Re-post the quit message so the
  590.                      * outer loop will see it and exit.
  591.                      */
  592.                     PostQuitMessage(msg.wParam);
  593.                     break;
  594.                 } else {
  595.                     TranslateMessage(&msg);
  596.                     DispatchMessage(&msg);
  597.                 }
  598.             }
  599.             continue;
  600.         }
  601.  
  602.         /* If this is the start of a new event, flush out the old one */
  603.         if (od.dwSequence != b0i.dwSeqLastSeen) {
  604.             Scrawl_OnButton0Down_FlushMotion(&b0i);
  605.             b0i.dwSeqLastSeen = od.dwSequence;
  606.         }
  607.  
  608.         /* Look at the element to see what happened */
  609.  
  610.         switch (od.dwOfs) {
  611.  
  612.         /* DIMOFS_X: Mouse horizontal motion */
  613.         case DIMOFS_X:
  614.             UpdateCursorPosition(od.dwData, 0);
  615.             b0i.fMoved = 1;
  616.             break;
  617.  
  618.  
  619.         /* DIMOFS_Y: Mouse vertical motion */
  620.         case DIMOFS_Y:
  621.             UpdateCursorPosition(0, od.dwData);
  622.             b0i.fMoved = 1;
  623.             break;
  624.  
  625.         /* DIMOFS_BUTTON0: Button 0 pressed or released */
  626.         case DIMOFS_BUTTON0:
  627.  
  628.             if (!(od.dwData & 0x80)) { /* Button released */
  629.                 fDone = 1;
  630.                 Scrawl_OnButton0Down_FlushMotion(&b0i); /* Flush out dregs */
  631.             }
  632.             break;
  633.  
  634.         }
  635.     }
  636.  
  637.     ReleaseDC(hwnd, b0i.hdcWindow);
  638.  
  639.     /* Re-show the cursor now that scrawling is finished */
  640.     g_fShowCursor = TRUE;
  641.     InvalidateCursorRect(hwnd);
  642. }
  643.  
  644.  
  645. /****************************************************************************
  646.  *
  647.  *      Scrawl_OnButton1Up
  648.  *
  649.  *      Pop up a context menu.
  650.  *
  651.  ****************************************************************************/
  652.  
  653. void Scrawl_OnButton1Up(HWND hwnd)
  654. {
  655.     POINT pt = { g_x, g_y };
  656.     ClientToScreen(hwnd, &pt);
  657.  
  658.     /*
  659.      *  Unacquire the devices so the user can interact with the menu.
  660.      *
  661.      *  Put the Windows cursor at the same location as our virtual cursor.
  662.      *
  663.      *  Hide the cursor while moving it so you don't get annoying flicker.
  664.      */
  665.  
  666.     ShowCursor(FALSE);
  667.     g_fActive = FALSE;
  668.     Scrawl_SyncAcquire(hwnd);
  669.     SetCursorPos(pt.x, pt.y);
  670.     ShowCursor(TRUE);
  671.  
  672.     HMENU hmenuPopup = GetSystemMenu(hwnd, FALSE);
  673.  
  674.     UINT idc = TrackPopupMenuEx(hmenuPopup,
  675.                                 TPM_RIGHTBUTTON | TPM_RETURNCMD,
  676.                                 pt.x, pt.y, hwnd, 0);
  677.  
  678.     PostMessage(hwnd, WM_SYSCOMMAND, idc, 0L);
  679.  
  680. }
  681.  
  682. /****************************************************************************
  683.  *
  684.  *      Scrawl_OnMouseInput
  685.  *
  686.  *      The mouse moved while nothing was happening.  Walk the event list
  687.  *      and update the mouse position for each event.  If we see a button
  688.  *      event, then stop pulling events and leave the elements in the
  689.  *      input buffer for the drawing handler to pull.
  690.  *
  691.  *
  692.  ****************************************************************************/
  693.  
  694. void
  695. Scrawl_OnMouseInput(HWND hwnd)
  696. {
  697.     /* Invalidate the old cursor so it will be erased */
  698.     InvalidateCursorRect(hwnd);
  699.  
  700.     /*
  701.      *  Attempt to read one data element.  Continue as long as
  702.      *  device data is available.
  703.      */
  704.     BOOL fDone = 0;
  705.     while (!fDone) {
  706.         DIDEVICEOBJECTDATA od;
  707.  
  708.         DWORD dwElements = 1;
  709.  
  710.         HRESULT hr = g_pMouse->GetDeviceData(
  711.                              sizeof(DIDEVICEOBJECTDATA), &od,
  712.                              &dwElements, 0);
  713.  
  714.         if (hr == DIERR_INPUTLOST) {
  715.             /*
  716.              *  We had acquisition, but lost it.  Try to reacquire it.
  717.              *
  718.              *  WARNING!  DO NOT ATTEMPT TO REACQUIRE IF YOU GET
  719.              *  DIERR_NOTACQUIRED!  Otherwise, you're extremely likely
  720.              *  to get caught in an infinite loop:  The acquire will fail,
  721.              *  and you'll get another DIERR_NOTACQUIRED so you'll
  722.              *  try to aquire again, and that'll fail, etc.
  723.              */
  724.             PostMessage(hwnd, WM_SYNCACQUIRE, 0, 0L);
  725.             break;
  726.         }
  727.  
  728.         /* Unable to read data or no data available */
  729.         if (FAILED(hr) || dwElements == 0) {
  730.             break;
  731.         }
  732.  
  733.         /* Look at the element to see what happened */
  734.  
  735.         switch (od.dwOfs) {
  736.  
  737.         /* DIMOFS_X: Mouse horizontal motion */
  738.         case DIMOFS_X: UpdateCursorPosition(od.dwData, 0); break;
  739.  
  740.  
  741.         /* DIMOFS_Y: Mouse vertical motion */
  742.         case DIMOFS_Y: UpdateCursorPosition(0, od.dwData); break;
  743.  
  744.         /* DIMOFS_BUTTON0: Button 0 pressed or released */
  745.         case DIMOFS_BUTTON0:
  746.  
  747.             if (od.dwData & 0x80) { /* Button pressed */
  748.                 fDone = 1;
  749.                 Scrawl_OnButton0Down(hwnd); /* Go into button-down mode */
  750.             }
  751.             break;
  752.  
  753.         /* DIMOFS_BUTTON1: Button 1 pressed or released */
  754.         case DIMOFS_BUTTON1:
  755.  
  756.             if (!(od.dwData & 0x80)) {  /* Button released */
  757.                 fDone = 1;
  758.                 Scrawl_OnButton1Up(hwnd); /* Context menu time */
  759.             }
  760.         }
  761.  
  762.     }
  763.  
  764.     /* Invalidate the new cursor so it will be drawn */
  765.     InvalidateCursorRect(hwnd);
  766.  
  767. }
  768.  
  769. /****************************************************************************
  770.  *
  771.  *      ScrawlWndProc
  772.  *
  773.  *      Application window procedure.
  774.  *
  775.  ****************************************************************************/
  776.  
  777. LONG CALLBACK ScrawlWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
  778. {
  779.     switch (msg) {
  780.  
  781.     HANDLE_MSG(hwnd, WM_CREATE, Scrawl_OnCreate);
  782.  
  783.     HANDLE_MSG(hwnd, WM_PAINT, Scrawl_OnPaint);
  784.  
  785.     HANDLE_MSG(hwnd, WM_INITMENUPOPUP, Scrawl_OnInitMenuPopup);
  786.  
  787.     HANDLE_MSG(hwnd, WM_KEYDOWN, Scrawl_OnKeyDown);
  788.  
  789.     /*
  790.      *  Reacquire the mouse and keyboard when we are the active window.
  791.      *  Unacquire them when we stop being the active window.
  792.      */
  793.     case WM_ACTIVATE:
  794.         g_fActive = wParam == WA_ACTIVE || wParam == WA_CLICKACTIVE;
  795.         Scrawl_SyncAcquire(hwnd);
  796.         break;
  797.  
  798.     /*
  799.      *  Unacquire the devices if a menu appears, so that the user can
  800.      *  interact with the menu in the normal manner.
  801.      *
  802.      *  From Windows' point of view, we are still the active window
  803.      *  when a menu appears, but we want to act like the menu deactivated
  804.      *  us.
  805.      */
  806.     case WM_ENTERMENULOOP:
  807.     case WM_ENTERSIZEMOVE:
  808.         g_fActive = FALSE;
  809.         Scrawl_SyncAcquire(hwnd);
  810.         break;
  811.  
  812.     /*
  813.      *  Reacquire the devices when the menu goes away.
  814.      *
  815.      *  SUBTLETY 1:  Windows actually sends the WM_EXITMENULOOP message
  816.      *  before all the menu-related stuff is finished, so post ourselves
  817.      *  a private message to reacquire after the menu has been torn
  818.      *  down for real.
  819.      *
  820.      *  SUBTLETY 2:  Don't assume that just because the menu is going
  821.      *  away that you are still the active window.  You might not be.
  822.      *
  823.      *  SUBTLETY 3:  Don't assume that just because you're the active
  824.      *  window that you are restored and ready for action.  You might
  825.      *  just be a taskbar button.
  826.      */
  827.     case WM_EXITMENULOOP:
  828.     case WM_EXITSIZEMOVE:
  829.         g_fActive = GetActiveWindow() == hwnd && !IsIconic(hwnd);
  830.         PostMessage(hwnd, WM_SYNCACQUIRE, 0, 0L);
  831.         break;
  832.  
  833.     case WM_SYNCACQUIRE:
  834.         Scrawl_SyncAcquire(hwnd);
  835.         break;
  836.  
  837.     case WM_SYSCOMMAND:
  838.  
  839.         LRESULT lRc;
  840.         switch (GET_WM_COMMAND_ID(wParam, lParam)) {
  841.         case IDC_CLEAR:
  842.             Scrawl_OnClear(hwnd);
  843.             lRc = 0;
  844.             break;
  845.  
  846.         case IDC_ABOUT:
  847.             MessageBox(hwnd, "Scrawl DirectInput Sample v1.0",
  848.                     c_szAppName, MB_OK);
  849.             lRc = 0;
  850.             break;
  851.  
  852.         /*
  853.          *  Eat the screen-saver notification.
  854.          */
  855.         case SC_SCREENSAVE:
  856.             lRc = 0;
  857.             break;
  858.  
  859.         case IDC_SENSITIVITY_LOW:
  860.         case IDC_SENSITIVITY_NORMAL:
  861.         case IDC_SENSITIVITY_HIGH:
  862.             g_iSensitivity = (signed short)GET_WM_COMMAND_ID(wParam, lParam)
  863.                                 - IDC_SENSITIVITY_NORMAL;
  864.             lRc = 0;
  865.             break;
  866.  
  867.         default:
  868.             lRc = DefWindowProc(hwnd, msg, wParam, lParam);
  869.             break;
  870.         }
  871.  
  872.         /*
  873.          * The WM_SYSCOMMAND might've been a WM_CLOSE, in which case
  874.          * our window no longer exists.  So be careful.
  875.          */
  876.         if (IsWindow(hwnd)) {
  877.             Scrawl_SyncAcquire(hwnd);
  878.         }
  879.         return lRc;
  880.  
  881.  
  882.     case WM_DESTROY:
  883.         PostQuitMessage(0);
  884.         break;
  885.  
  886.     }
  887.  
  888.     return DefWindowProc(hwnd, msg, wParam, lParam);
  889. }
  890.  
  891. /****************************************************************************
  892.  *
  893.  *      AppInit
  894.  *
  895.  *      Set up everything the application needs to get started.
  896.  *
  897.  ****************************************************************************/
  898.  
  899. HWND AppInit(HINSTANCE hinst, int nCmdShow)
  900. {
  901.  
  902.     /* Save instance handle for people who care */
  903.     g_hinst = hinst;
  904.  
  905.     /*
  906.      *  Get our crosshairs cursor and extract the the width and
  907.      *  hotspot location so we can draw it manually.
  908.      */
  909.     g_hcurCross = LoadCursor(NULL, IDC_CROSS);
  910.  
  911.     ICONINFO ii;
  912.  
  913.     GetIconInfo(g_hcurCross, &ii);
  914.  
  915.     BITMAP bm;
  916.  
  917.     GetObject(ii.hbmMask, sizeof(BITMAP), &bm);
  918.  
  919.     if (ii.hbmMask)  DeleteObject(ii.hbmMask);
  920.     if (ii.hbmColor) DeleteObject(ii.hbmColor);
  921.  
  922.     g_dxCrossHot = ii.xHotspot;
  923.     g_dyCrossHot = ii.yHotspot;
  924.  
  925.     g_cxCross = bm.bmWidth;
  926.     g_cyCross = bm.bmHeight;
  927.  
  928.     /*
  929.      *  Create our scrawl bitmap and set it up.
  930.      */
  931.     HDC hdc = GetDC(0);
  932.     g_hdc = CreateCompatibleDC(hdc);
  933.     ReleaseDC(0, hdc);
  934.  
  935.     if (!g_hdc) return NULL;
  936.  
  937.     g_hbm = CreateBitmap(DINPUT_CXBITMAP, DINPUT_CYBITMAP, 1, 1, 0);
  938.  
  939.     if (!g_hbm) return NULL;
  940.  
  941.     g_hbmDeselect = (HBITMAP) SelectObject(g_hdc, g_hbm);
  942.  
  943.     Scrawl_OnClear(0);
  944.  
  945.     /*
  946.      *  Set up the window class.
  947.      */
  948.     WNDCLASS wc;
  949.  
  950.     wc.hCursor        = LoadCursor(0, IDC_ARROW);
  951.     wc.hIcon          = LoadIcon(hinst, MAKEINTRESOURCE(IDI_MAIN));
  952.     wc.lpszMenuName   = NULL;
  953.     wc.lpszClassName  = c_szAppName;
  954.     wc.hbrBackground  = 0;
  955.     wc.hInstance      = hinst;
  956.     wc.style          = 0;
  957.     wc.lpfnWndProc    = ScrawlWndProc;
  958.     wc.cbClsExtra     = 0;
  959.     wc.cbWndExtra     = 0;
  960.  
  961.     if (!RegisterClass(&wc)) {
  962.         return NULL;
  963.     }
  964.  
  965.     DWORD dwStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
  966.     DWORD dwExStyle = WS_EX_APPWINDOW;
  967.  
  968.     RECT rc = { 0, 0, DINPUT_CXBITMAP, DINPUT_CYBITMAP };
  969.  
  970.     AdjustWindowRectEx(&rc, dwStyle, FALSE, dwExStyle);
  971.  
  972.     HWND hwnd = CreateWindowEx(
  973.                     dwExStyle,          // ExStyle
  974.                     c_szAppName,        // Class name
  975.                     c_szAppName,        // Caption
  976.                     dwStyle,            // Style
  977.                     CW_USEDEFAULT, CW_USEDEFAULT,  // Position
  978.                     rc.right - rc.left, // cx
  979.                     rc.bottom - rc.top, // cy
  980.                     0,                  // Parent window (no parent)
  981.                     0,                  // use class menu
  982.                     g_hinst,            // handle to module instance
  983.                     0                   // no params to pass on
  984.                     );
  985.  
  986.     if (!DIInit(hwnd)) {
  987.         DestroyWindow(hwnd);
  988.         return NULL;
  989.     }
  990.  
  991.     ShowWindow(hwnd, nCmdShow);
  992.  
  993.     return hwnd;
  994. }
  995.  
  996. /****************************************************************************
  997.  *
  998.  *      WinMain
  999.  *
  1000.  *      Application entry point.
  1001.  *
  1002.  *      The main message loop illustrates how a message-driven program
  1003.  *      can use event notifications to be signalled when new data is
  1004.  *      available from a device.
  1005.  *
  1006.  ****************************************************************************/
  1007.  
  1008. int PASCAL
  1009. WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR szCmdLine, int nCmdShow)
  1010. {
  1011.     MSG msg;
  1012.     msg.wParam = 0;         /* In case something goes horribly wrong */
  1013.  
  1014.     HWND hwnd = AppInit(hinst, nCmdShow);
  1015.  
  1016.     if (hwnd) {
  1017.  
  1018.         /*
  1019.          *  Since we use notification handles, we need to use
  1020.          *  MsgWaitForMultipleObjects to wait for the event or
  1021.          *  a message, whichever comes first.
  1022.          */
  1023.  
  1024.         BOOL fDone = FALSE;
  1025.  
  1026.         while (!fDone) {
  1027.  
  1028.             DWORD dw = MsgWaitForMultipleObjects(1, &g_hevtMouse, 0, INFINITE,
  1029.                                                  QS_ALLINPUT);
  1030.  
  1031.             switch (dw) {
  1032.  
  1033.             /* WAIT_OBJECT_0 + 0 means that g_hevtMouse was signalled */
  1034.             case WAIT_OBJECT_0 + 0:
  1035.                 Scrawl_OnMouseInput(hwnd);
  1036.                 break;
  1037.  
  1038.             /* WAIT_OBJECT_0 + 1 means that we have messages to process */
  1039.             case WAIT_OBJECT_0 + 1:
  1040.  
  1041.                 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
  1042.  
  1043.                     /* If it's a quit message, we're outta here */
  1044.                     if (msg.message == WM_QUIT) {
  1045.                         fDone = TRUE;
  1046.                     } else {
  1047.                         TranslateMessage(&msg);
  1048.                         DispatchMessage(&msg);
  1049.                     }
  1050.                 }
  1051.                 break;
  1052.             }
  1053.         }
  1054.     }
  1055.  
  1056.     DITerm();
  1057.  
  1058.     if (g_hdc) {
  1059.         if (g_hbmDeselect) {
  1060.             SelectObject(g_hdc, g_hbmDeselect);
  1061.         }
  1062.         DeleteDC(g_hdc);
  1063.     }
  1064.  
  1065.     if (g_hbm) {
  1066.         DeleteObject(g_hbm);
  1067.     }
  1068.  
  1069.   return msg.wParam;
  1070.  
  1071. }
  1072.