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