Skip navigation

Using RAWINPUT, based off the example, except this one is in a d3d window


#pragma region includes
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include <d3d9.h>      // core direct3d
#include <d3dx9.h>     // aux libs

#include <dxerr.h>    // detailed error messages

#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")  // aux libs
#ifdef _DEBUG
#pragma comment(lib,"d3dx9d.lib")
#else
#pragma comment(lib,"d3dx9.lib")
#endif

#pragma comment(lib, "dxerr.lib")

// Macros.
#define SAFE_RELEASE(ptr) if(ptr) { ptr->Release(); ptr = NULL; }
#define CAST_AS_DWORD(x) *((DWORD*)&x)
#define PI 3.14159
#pragma endregion

#pragma region define the Vertex structure
struct Vertex
{
  float x,y,z ;
  DWORD color ;
  
  // Ctor starts you at origin in black
  // with alpha (opacity) set to 100%
  Vertex()
  {
    x=y=z = 0.0f;
    color = D3DCOLOR_XRGB( 0,0,0 ) ;
  }

  Vertex( float ix, float iy, float iz )
  {
    x=ix;y=iy;z=iz;
    color = D3DCOLOR_XRGB( 255,255,255 ) ;
  }

  // Ctor.
  Vertex( float ix, float iy, float iz,
    unsigned char ir, unsigned char ig, unsigned char ib )
  {
    x=ix;y=iy;z=iz;
    color = D3DCOLOR_XRGB( ir, ig, ib ) ;
  }

  // Ctor that lets you pick alpha
  Vertex( float ix, float iy, float iz,
    unsigned char ir, unsigned char ig, unsigned char ib, unsigned char ALPHA )
  {
    x=ix;y=iy;z=iz;
    color = D3DCOLOR_ARGB( ALPHA, ir, ig, ib ) ;
  }
} ;
#pragma endregion

struct Globals
{
  struct _Win
  {
    HINSTANCE hInstance;    // window app instance
    HWND hwnd;              // handle for the window
    HWND hConsole ;         // handle for the console window

    int width, height;
  } win ;

  IDirect3D9 * d3d ;
  IDirect3DDevice9 * gpu ; 
};

///////////////////////////
// GLOBALS
Globals g;

///////////////////////////
// FUNCTION PROTOTYPES
void printWindowsLastError( char *msg ) ;
inline bool CHECK( HRESULT hr, char * msg, bool stop=true ) ;  // checks for errors on the HR passed.
bool initD3D() ;         // function to initialize the BEAST that is Direct3D9
void initRawinput( HWND hwnd ) ;
void update() ;          // changes geometry of the scene
void drawAxes() ;        // draws the ever-important axes (for finding your way around your own scene!)
void draw() ;            // drawing function containing Direct3D drawing calls

LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam );
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int iCmdShow );


void printWindowsLastError( char *msg )
{
  LPSTR errorString = NULL ;

  int result = FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER |
                 FORMAT_MESSAGE_FROM_SYSTEM,
                 0,
                 GetLastError(),
                 0,
                 (LPSTR)&errorString,
                 0,
                 0 );

  printf( "%s %s\n", msg, errorString ) ;
  
  LocalFree( errorString ) ;
}

inline bool CHECK( HRESULT hr, char * msg, bool stop )
{
  if( FAILED( hr ) )
  {
    printf( "%s. %s:  %s\n",
            msg, DXGetErrorStringA( hr ), DXGetErrorDescriptionA( hr ) ) ;

    // Pause so we can see the error and deal with it.
    if( stop )  system("pause") ;

    return false ;
  }

  else
    return true ;
}

/// Initializes Direct3D9.  Returns true on success.
bool initD3D()
{
  // start by nulling out both pointers:
  g.d3d = 0 ;
  g.gpu = 0 ;

  g.d3d = Direct3DCreate9( D3D_SDK_VERSION ) ;

  if( g.d3d == NULL )
  {
    // DEVICE CREATION FAILED!!!! OH NO!!!
    puts( "Oh.. PHOOEY!!!!!  Device creation FAILED!!! WHAT NOW???\n" ) ;
    return false ;
  }

  puts( "Direct3D9 creation success!" ) ;

  D3DPRESENT_PARAMETERS pps = { 0 } ;

  pps.Windowed = true ;
  pps.BackBufferCount = 1 ;
  pps.SwapEffect = D3DSWAPEFFECT_DISCARD ;
  pps.BackBufferFormat = D3DFMT_UNKNOWN ;
  pps.EnableAutoDepthStencil = true ;
  pps.AutoDepthStencilFormat = D3DFMT_D16 ;

  HRESULT hr = g.d3d->CreateDevice(

    D3DADAPTER_DEFAULT, // primary display adapter
    D3DDEVTYPE_HAL,     // use HARDWARE rendering (fast!)
    g.win.hwnd,
    D3DCREATE_HARDWARE_VERTEXPROCESSING,
    &pps,
    &g.gpu

  ) ;

  if( !CHECK( hr, "OH NOS!! I could not initialize Direct3D!  Bailing...\n" ) )
  {
    return false ;
  }

  puts( "Direct3D9 GPU device creation successful" ) ;


  #pragma region setup interleaved arrays
  D3DVERTEXELEMENT9 pos ;

  pos.Usage = D3DDECLUSAGE_POSITION ;
  pos.UsageIndex = 0 ;
  pos.Stream = 0 ;
  pos.Type = D3DDECLTYPE_FLOAT3 ;
  pos.Offset = 0 ;

  pos.Method = D3DDECLMETHOD_DEFAULT ; 

  D3DVERTEXELEMENT9 col;

  col.Usage = D3DDECLUSAGE_COLOR ;
  col.UsageIndex = 0 ;
  col.Stream = 0 ;
  col.Type = D3DDECLTYPE_D3DCOLOR ;
  col.Offset = 3*sizeof( float ) ;
  col.Method = D3DDECLMETHOD_DEFAULT ;

  D3DVERTEXELEMENT9 vertexElements[] =
  {
    pos,
    col,
    D3DDECL_END()
  } ;

  IDirect3DVertexDeclaration9 * Vdecl ;

  hr = g.gpu->CreateVertexDeclaration( vertexElements, &Vdecl ) ;
  CHECK( hr, "CreateVertexDeclaration FAILED!" ) ;

  hr = g.gpu->SetVertexDeclaration( Vdecl ) ;
  CHECK( hr, "SetVertexDeclaration FAILED!" ) ;
  #pragma endregion

  
  hr = g.gpu->SetRenderState( D3DRS_COLORVERTEX, TRUE ) ;
  CHECK( hr, "SetRenderState( COLORVERTEX ) FAILED!" ) ;

  hr = g.gpu->SetRenderState( D3DRS_LIGHTING, FALSE ) ;
  CHECK( hr, "Lighting off" ) ;

  hr = g.gpu->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE ) ;
  CHECK( hr, "cull mode off" ) ;

  initRawinput( g.win.hwnd ) ;

  return true ;
}

void initRawinput( HWND hwnd )
{
  puts( "Starting up rawinput devices..." ) ;

  // After the window has been created, 
  // register raw input devices
  RAWINPUTDEVICE Rid[2] ;
        
  Rid[0].usUsagePage = 0x01 ;  // magic numbers
  Rid[0].usUsage = 0x02 ;      // magically means mouse
  Rid[0].dwFlags = 0 ; // (use this if you DO NOT WANT to capture mouse)
  //Rid[0].dwFlags = RIDEV_CAPTUREMOUSE | RIDEV_NOLEGACY ;  // (use this to CAPTURE MOUSE)
  Rid[0].hwndTarget = hwnd ;

  Rid[1].usUsagePage = 0x01 ;  // magic numbers
  Rid[1].usUsage = 0x06 ;      // magically means keyboard
  Rid[1].dwFlags = 0 ;         // use RIDEV_NOHOTKEYS for no winkey
  Rid[1].hwndTarget = hwnd ;

  if( !RegisterRawInputDevices( Rid, 2, sizeof(Rid[0]) ) )
  {
    //registration failed. Check your Rid structs above.
    printWindowsLastError( "RegisterRawInputDevices" ) ;
    puts( "Could not register raw input devices. Check your Rid structs, please." ) ;
    exit(1);
  }
}

void update()
{
}

////////////////////////
// DRAWING FUNCTIONS
void drawAxes()
{

  static float axisLen = 2.0f ;
  static Vertex axis[] = {

    // x-axis is red
    Vertex( -axisLen, 0, 0, 255, 0, 0 ),
    Vertex( +axisLen, 0, 0, 255, 0, 0 ),

    // y-axis green
    Vertex( 0, -axisLen, 0, 0, 255, 0 ),
    Vertex( 0, +axisLen, 0, 0, 255, 0 ),

    // z-axis blue
    Vertex( 0, 0, -axisLen, 0, 0, 255 ),
    Vertex( 0, 0, +axisLen, 0, 0, 255 )

  } ;

  HRESULT hr = g.gpu->DrawPrimitiveUP( D3DPT_LINELIST, 3, axis, sizeof( Vertex ) ) ;
  CHECK( hr, "DrawPrimitiveUP FAILED!" ) ;

  static float pointSize = 8.0f ;

  g.gpu->SetRenderState( D3DRS_POINTSIZE, CAST_AS_DWORD( pointSize ) ) ;

  // Draw points at end of axis.
  static Vertex points[] = {
    Vertex( axisLen, 0, 0, 255, 0, 0 ),
    Vertex( 0, axisLen, 0, 0, 255, 0 ),
    Vertex( 0, 0, axisLen, 0, 0, 255 ),
  } ;

  hr = g.gpu->DrawPrimitiveUP( D3DPT_POINTLIST, 3, points, sizeof( Vertex ) ) ;
  CHECK( hr, "DrawPrimitiveUP FAILED!" ) ;

}

void draw()
{
  HRESULT hr ;

  hr = g.gpu->Clear( 0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
    D3DCOLOR_ARGB( 255, 25, 25, 25 ), 1.0f, 0 ) ;
  CHECK( hr, "Clear FAILED!" ) ;

  #pragma region set up the camera
  D3DXMATRIX projx ;

  D3DXMatrixOrthoRH( &projx, 5, 5, 1, 100 ) ;
  //D3DXMatrixPerspectiveFovRH( &projx, PI/4, (float)g.win.width/g.win.height, 1.0f, 1000.0f ) ;

  g.gpu->SetTransform( D3DTS_PROJECTION, &projx ) ;

  D3DXMATRIX viewx ;

  int x = 2 ;
  int y = 2 ;
  D3DXVECTOR3 eye( x, y, 4 ) ;
  D3DXVECTOR3 look( x, y, 0 ) ;
  D3DXVECTOR3 up( 0, 1, 0 ) ;
  D3DXMatrixLookAtRH( &viewx, &eye, &look, &up ) ;
  g.gpu->SetTransform( D3DTS_VIEW, &viewx ) ;
  #pragma endregion

  hr = g.gpu->BeginScene() ;
  CHECK( hr, "BeginScene FAILED!" ) ;


  drawAxes();


  hr = g.gpu->EndScene() ;
  CHECK( hr, "EndScene FAILED!" ) ;

  // And finally, PRESENT what we drew to the backbuffer
  g.gpu->Present( 0, 0, 0, 0 ) ;

}

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int iCmdShow )
{
  //////////////////
  // First we'll start by saving a copy of
  // the hInstance parameter inside our
  // "glob" of globals "g":
  g.win.hInstance = hInstance;
  // In case we need it later, we'll have it
  // with firsthand easy access.

  #pragma region part 0 - attach a console
  // Attach a console
  AllocConsole();
  AttachConsole( GetCurrentProcessId() ) ;
  freopen( "CON", "w", stdout ) ; // redirect stdout to console

  // Move the console over to the top left
  g.win.hConsole = GetConsoleWindow();
  MoveWindow( g.win.hConsole, 0, 0, 400, 400, true ) ;

  printf( "* * Computer Program Begin * *\n" ) ;
  #pragma endregion

  #pragma region part 1 - create a window
  // The next few lines you should already
  // be used to:  create a WNDCLASSEX
  // that describes the properties of
  // the window we're going to soon create.
  // A.  Create the WNDCLASSEX
  WNDCLASSEX wcx = { 0 } ;
  wcx.cbSize = sizeof( WNDCLASSEX );
  wcx.hbrBackground = (HBRUSH)GetStockObject( BLACK_BRUSH );
  wcx.hCursor = LoadCursor( NULL, IDC_ARROW );
  wcx.hIcon = LoadIcon( NULL, IDI_APPLICATION );
  wcx.hInstance = hInstance;
  wcx.lpfnWndProc = WndProc;
  wcx.lpszClassName = TEXT("Philip");
  wcx.lpszMenuName = 0;
  wcx.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;

  // Register that class with the Windows O/S..
  RegisterClassEx( &wcx );

  int width = 800, height = 600;
  int leftEdge = 400, topEdge = 25 ;
  RECT rect;
  SetRect( &rect,
    leftEdge,  // left
    topEdge,   // top
    leftEdge + width, // right
    topEdge  + height ); // bottom

  // Save width and height off.
  g.win.width = rect.right - rect.left;
  g.win.height = rect.bottom - rect.top;

  // Adjust it.
  DWORD windowStyle = WS_OVERLAPPEDWINDOW ; // typical features of a normal window
  DWORD windowExStyle = 0 ; // I want the window to be topmost

  AdjustWindowRectEx( &rect, windowStyle, false, windowExStyle );

  g.win.hwnd = CreateWindowEx(
    windowExStyle,
    TEXT("Philip"),
    TEXT("DIRECT3D WINDOW"),
    windowStyle,
    rect.left, rect.top,  // adjusted x, y positions
    rect.right - rect.left, rect.bottom - rect.top,  // adjusted width and height
    NULL, NULL,
    hInstance, NULL);

  // check to see that the window
  // was created successfully!
  if( g.win.hwnd == NULL )
  {
    FatalAppExit( NULL, TEXT("CreateWindow() failed!") );
  }

  // and show.
  ShowWindow( g.win.hwnd, iCmdShow );

  // JUMP to the initD3D() method.
  if( !initD3D() )
  {
    FatalAppExit( 0, TEXT("SORRY!!!  DEVICE CREATION FAILED!!! YOU LOSE, WITHOUT EVEN PLAYING THE GAME!!!" ) ) ;
  }

  #pragma endregion

  #pragma region message loop
  MSG msg;

  while( 1 )
  {
    if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
    {
      if( msg.message == WM_QUIT )
      {
        break;
      }

      TranslateMessage( &msg );
      DispatchMessage( &msg );
    }
    else
    {
      update();
      draw();
    }
  }
  #pragma endregion

  //////////////
  // clean up
  SAFE_RELEASE( g.gpu ) ;
  SAFE_RELEASE( g.d3d ) ;

  // and a cheesy fade exit
  AnimateWindow( g.win.hwnd, 200, AW_HIDE | AW_BLEND );

  printf( "* * This Computer Program Has Ended * *\n" ) ;

  return msg.wParam;
}

////////////////////////
// WNDPROC
// Notice that WndProc is very very neglected.
// We hardly do anything with it!  That's because
// we do all of our processing in the draw()
// function.
LRESULT CALLBACK WndProc(   HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam )
{
  switch( message )
  {
  case WM_CREATE:
    Beep( 50, 10 );
    return 0;
    break;

  case WM_PAINT:
    {
      HDC hdc;
      PAINTSTRUCT ps;
      hdc = BeginPaint( hwnd, &ps );
      // don't draw here.  would be waaay too slow.
      // draw in the draw() function instead.
      EndPaint( hwnd, &ps );
    }
    return 0;
    break;

  case WM_KEYDOWN:
    switch( wparam )
    {
    case VK_ESCAPE:
      PostQuitMessage( 0 );
      break;
    default:
      break;
    }
    return 0;



  case WM_INPUT: 
    {
      UINT dwSize;

      GetRawInputData((HRAWINPUT)lparam, RID_INPUT, NULL, &dwSize, 
        sizeof(RAWINPUTHEADER));
      LPBYTE lpb = new BYTE[dwSize];
      if (lpb == NULL) 
      {
        return 0;
      } 

      int readSize = GetRawInputData( (HRAWINPUT)lparam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER) ) ;

      if( readSize != dwSize )
        puts( "ERROR:  GetRawInputData didn't return correct size!" ) ; 

      RAWINPUT* raw = (RAWINPUT*)lpb;

      if (raw->header.dwType == RIM_TYPEKEYBOARD) 
      {
        if( raw->data.keyboard.VKey == VK_SPACE )
        {
          puts( "You are pressing space" ) ;
        }
      }
      else if (raw->header.dwType == RIM_TYPEMOUSE) 
      {
        int dx = raw->data.mouse.lLastX ;
        int dy = raw->data.mouse.lLastY ;

        printf( "%d %d\n", dx, dy ) ;
      } 

      delete[] lpb; 
      return 0;
    } 

  case WM_SIZE:
    {
      int width = LOWORD( lparam ) ;
      int height = HIWORD( lparam ) ;
      printf( "RESIZED TO width=%d height=%d\n", width, height ) ;
    }
    break;

  case WM_DESTROY:
    PostQuitMessage( 0 ) ;
    return 0;
    break;
  }

  return DefWindowProc( hwnd, message, wparam, lparam );
}

4 Comments

  1. By any chance can elaborate about the mouse coordinates?
    more specifically, how to get a screen space coordinates of the mouse,
    at any time (simple function), using raw input.

    What you have is the difference, and I can’t get the screen space coordinates from it.

  2. A mouse doesn’t supply absolute coordinates, only relative. A mouse has no memory!

    A pen input device or touch screen supplies absolute coordinates.

    The type of input you got (relative or absolute) is in raw->data.mouse.usFlags.

    Check
    if( raw->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE )

    Then the user was using a pen device.

    Device dependent.

    To keep screen space coordinates, you can:
    – Maintain a global mouse_x,mouse_y with current mouse position (just keep adding dx and dy each time there is raw input, and draw your mouse icon at mouse_x, mouse_y)
    – Use an alternative API function, like WM_MOUSEMOVE (http://msdn.microsoft.com/en-us/library/ms645616(VS.85).aspx) (slower!)

    • Anonymous
    • Posted June 22, 2012 at 8:33 pm
    • Permalink

    I tried using the global variables in order to keep track of it, but I seem to be doing wrong.
    My bet is on the initial values. I tried starting with values of 0, even though it didn’t make sense. That didn’t work, of course.

    After, I tried getting the initial mouse position from GetCursorPos(), then adding to it. It seems i’m still doing it wrong though. If I had to guess, I would say my problem is _when_ to call GetCursorPos(). Currently, i’m calling it after calling Register, which logically should be right. I can’t be sure about it.

    I would love if you can create or link me to a working sample which shows the absolute screen position using raw input.

  3. __You determine__ absolute screen coordinates. The API supplies you with dx and dy to mouse pos. Now you say where the mouse __is__, and draw it there.


Leave a comment