/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see .
*/
// FILE: Win32DIMouse.cpp /////////////////////////////////////////////////////////////////////////
// Created: Colin Day, June 2001
// Desc: Win32 direct input implementation for the mouse
///////////////////////////////////////////////////////////////////////////////////////////////////
#include
#include
#include
#include "Common/Debug.h"
#include "GameClient/Display.h"
#include "Win32Device/GameClient/Win32DIMouse.h"
#include "WinMain.h"
// DEFINES ////////////////////////////////////////////////////////////////////////////////////////
enum { MOUSE_BUFFER_SIZE = 256, };
///////////////////////////////////////////////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
/** Create our direct input object, mouse device, and initialize it to the
* data formats we want */
//-------------------------------------------------------------------------------------------------
void DirectInputMouse::openMouse( void )
{
HRESULT hr;
// create our direct input device for mouse access
hr = DirectInput8Create( ApplicationHInstance,
DIRECTINPUT_VERSION,
IID_IDirectInput8,
(void **)&m_pDirectInput,
NULL );
if( FAILED( hr ) )
{
DEBUG_LOG(( "ERROR - openMouse: Unabled to create direct input interface\n" ));
assert( 0 );
closeMouse();
return;
} // end if
// create a device for the system mouse
hr = m_pDirectInput->CreateDevice( GUID_SysMouse,
&m_pMouseDevice,
NULL );
if( FAILED( hr ) )
{
DEBUG_LOG(( "ERROR - openMouse: Unable to create mouse device\n" ));
assert( 0 );
closeMouse();
return;
} // end if
// set the data format for the mouse
hr = m_pMouseDevice->SetDataFormat( &c_dfDIMouse );
if( FAILED( hr ) )
{
DEBUG_LOG(( "ERROR - openMouse: Unabled to set mouse data format\n" ));
assert( 0 );
closeMouse();
return;
} // end if
// set the mouse cooperative level
hr = m_pMouseDevice->SetCooperativeLevel( ApplicationHWnd,
DISCL_NONEXCLUSIVE |
DISCL_FOREGROUND );
if( FAILED( hr ) )
{
DEBUG_LOG(( "ERROR - openMouse: Unabled to set coop level\n" ));
assert( 0 );
closeMouse();
return;
} // end if
// set the mouse buffer size
DIPROPDWORD prop;
prop.diph.dwSize = sizeof( DIPROPDWORD );
prop.diph.dwHeaderSize = sizeof( DIPROPHEADER );
prop.diph.dwObj = 0;
prop.diph.dwHow = DIPH_DEVICE;
prop.dwData = MOUSE_BUFFER_SIZE;
hr = m_pMouseDevice->SetProperty( DIPROP_BUFFERSIZE, &prop.diph );
if( FAILED( hr ) )
{
DEBUG_LOG(( "ERROR - openMouse: Unabled to set buffer property\n" ));
assert( 0 );
closeMouse();
return;
} // end if
// acquire the mouse
hr = m_pMouseDevice->Acquire();
if( FAILED( hr ) )
{
DEBUG_LOG(( "ERROR - openMouse: Unabled to acquire mouse\n" ));
assert( 0 );
closeMouse();
return;
} // end if
// get some information about the mouse
DIDEVCAPS diDevCaps;
diDevCaps.dwSize = sizeof( DIDEVCAPS );
hr = m_pMouseDevice->GetCapabilities( &diDevCaps );
if( FAILED( hr ) )
{
DEBUG_LOG(( "WARNING - openMouse: Cann't get capabilities of mouse for button setup\n" ));
} // end if
else
{
// keep some data about the mouse we care about
m_numButtons = (UnsignedByte)diDevCaps.dwButtons;
m_numAxes = (UnsignedByte)diDevCaps.dwAxes;
m_forceFeedback = BitTest( diDevCaps.dwFlags, DIDC_FORCEFEEDBACK );
DEBUG_LOG(( "OK - Mouse info: Buttons = '%d', Force Feedback = '%s', Axes = '%d'\n",
m_numButtons, m_forceFeedback ? "Yes" : "No", m_numAxes ));
} // end else
DEBUG_LOG(( "OK - Mouse initialized successfully\n" ));
} // end openMouse
//-------------------------------------------------------------------------------------------------
/** Release any resources for our direct input mouse */
//-------------------------------------------------------------------------------------------------
void DirectInputMouse::closeMouse( void )
{
// release the mouse device
if( m_pMouseDevice )
{
m_pMouseDevice->Unacquire();
m_pMouseDevice->Release();
m_pMouseDevice = NULL;
DEBUG_LOG(( "OK - Mouse device closed\n" ));
} // end if
// release our direct input interface for the mouse
if( m_pDirectInput )
{
m_pDirectInput->Release();
m_pDirectInput = NULL;
DEBUG_LOG(( "OK - Mouse direct input interface closed\n" ));
} // end if
DEBUG_LOG(( "OK - Mouse shutdown complete\n" ));
} // end closeMouse
//-------------------------------------------------------------------------------------------------
/** Get a single mouse event from the device */
//-------------------------------------------------------------------------------------------------
UnsignedByte DirectInputMouse::getMouseEvent( MouseIO *result, Bool flush )
{
HRESULT hr;
DIDEVICEOBJECTDATA mdat;
UnsignedByte mouseResult = MOUSE_NONE;
DWORD num;
/* set these to defaults */
result->leftState = result->middleState = result->rightState = FALSE;
result->leftFrame = result->middleFrame = result->rightFrame = 0;
result->pos.x = result->pos.y = result->wheelPos = 0;
if( m_pMouseDevice )
{
// get 1 event, if available
num = 1;
m_pMouseDevice->Poll();
hr = m_pMouseDevice->GetDeviceData( sizeof( DIDEVICEOBJECTDATA ),
&mdat,
&num,
0 );
switch( hr )
{
// ----------------------------------------------------------------------
case DI_OK:
{
// nothing returned
if( num != 0 )
{
mapDirectInputMouse( result, &mdat );
mouseResult = MOUSE_OK;
}
break;
}
// ----------------------------------------------------------------------
case DIERR_NOTACQUIRED:
case DIERR_INPUTLOST:
{
// if we lost focus, attempt to re-acquire
hr = m_pMouseDevice->Acquire();
switch( hr )
{
// ------------------------------------------------------------------
// If successful... tell system to loop back
case DI_OK:
case S_FALSE:
mouseResult = MOUSE_LOST;
// ------------------------------------------------------------------
//If an error occurs return MOUSE_NONE
case DIERR_INVALIDPARAM:
case DIERR_NOTINITIALIZED:
case DIERR_OTHERAPPHASPRIO:
default:
break;
} // end switch
break;
}
// ----------------------------------------------------------------------
default:
// DBGPRINTF(("GetMouseEvent: GetDeviceData Error: %X.\r\n", hr ));
break;
} // end switch
} // end if
return mouseResult;
} // end getMouseEvent
//-------------------------------------------------------------------------------------------------
/** Map the direct input codes to our own mouse format */
//-------------------------------------------------------------------------------------------------
void DirectInputMouse::mapDirectInputMouse( MouseIO *mouse,
DIDEVICEOBJECTDATA *mdat )
{
switch( mdat->dwOfs )
{
case DIMOFS_BUTTON0:
mouse->leftState = (( mdat->dwData & 0x0080 ) ? TRUE : FALSE);
mouse->leftFrame = mdat->dwSequence;
break;
case DIMOFS_BUTTON1:
mouse->rightState = (( mdat->dwData & 0x0080 ) ? TRUE : FALSE);
mouse->rightFrame = mdat->dwSequence;
break;
case DIMOFS_BUTTON2:
mouse->middleState = (( mdat->dwData & 0x0080 ) ? TRUE : FALSE);
mouse->middleFrame = mdat->dwSequence;
break;
case DIMOFS_BUTTON3:
break;
case DIMOFS_X:
mouse->pos.x = mdat->dwData;
break;
case DIMOFS_Y:
mouse->pos.y = mdat->dwData;
break;
case DIMOFS_Z:
mouse->wheelPos = mdat->dwData;
break;
}
} // end mapDirectInputMouse
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
DirectInputMouse::DirectInputMouse( void )
{
m_pDirectInput = NULL;
m_pMouseDevice = NULL;
} // end DirectInputMouse
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
DirectInputMouse::~DirectInputMouse( void )
{
// relase all mouse resources
closeMouse();
// ShowCursor( TRUE );
} // end ~DirectInputMouse
//-------------------------------------------------------------------------------------------------
/** Initialize the direct input mouse device */
//-------------------------------------------------------------------------------------------------
void DirectInputMouse::init( void )
{
POINT p;
// extending functionality from our base class
Mouse::init();
// open the mouse and create the direct input interfaces we need
openMouse();
// move the window mouse to the location we have initialized in our system
p.x = m_currMouse.pos.x;
p.y = m_currMouse.pos.y;
ClientToScreen( ApplicationHWnd, &p );
SetCursorPos( p.x, p.y );
// ShowCursor( FALSE );
} // end init
//-------------------------------------------------------------------------------------------------
/** Reset direct input mouse */
//-------------------------------------------------------------------------------------------------
void DirectInputMouse::reset( void )
{
// extend
Mouse::reset();
} // end reset
//-------------------------------------------------------------------------------------------------
/** Update the mouse position and button data, this is called once per
* frame in the engine. NOTE that this routine is extendion functionality
* that we may need that is direct input specific, not replacing */
//-------------------------------------------------------------------------------------------------
void DirectInputMouse::update( void )
{
// extendion functionality from our base class
Mouse::update();
//
// since we are currently using the windows cursor because it updates at
// an independent rate of our application we will always just use the windows
// mouse cursor position
//
/** @todo we need to really visit this system and possibly come up with
our own multi-threaded cursor etc */
POINT p;
GetCursorPos( &p );
ScreenToClient( ApplicationHWnd, &p );
moveMouse( p.x, p.y, MOUSE_MOVE_ABSOLUTE );
} // end update
//-------------------------------------------------------------------------------------------------
/** Set the limits which the mouse is allowed to move around in. We
* will limit it to the client area, and if we are windowed we will
* allow for the mouse to move within the title bar at the top of
* the window */
//-------------------------------------------------------------------------------------------------
void DirectInputMouse::setMouseLimits( void )
{
//
// extending functionality, although we may overwrite the limits set
// from the base class
//
Mouse::setMouseLimits();
//
// when runing windowed we want to keep the mouse within the game
// window cause it's annoying to mouse out of the window and click
// on a background window.
//
if( TheDisplay && TheDisplay->getWindowed() == TRUE )
{
RECT windowRect;
// get the window rect
GetWindowRect( ApplicationHWnd, &windowRect );
// keep the cursor clipped to these coords when running windowed
ClipCursor( &windowRect );
} // end if
} // end setMouseLimits
//-------------------------------------------------------------------------------------------------
/** set the cursor position for windows OS */
//-------------------------------------------------------------------------------------------------
void DirectInputMouse::setPosition( Int x, Int y )
{
POINT p;
// extending functionality
Mouse::setPosition( x, y );
// set the windows cursor
p.x = x;
p.y = y;
ClientToScreen( ApplicationHWnd, &p );
// set the window mouse
SetCursorPos( p.x, p.y );
} // end setPosition
//-------------------------------------------------------------------------------------------------
/** Super basic simplistic cursor */
//-------------------------------------------------------------------------------------------------
void DirectInputMouse::setCursor( MouseCursor cursor )
{
// extend
Mouse::setCursor( cursor );
// if we're already on this cursor ignore
if( m_currentCursor == cursor )
return;
switch( cursor )
{
case NONE:
SetCursor( NULL );
break;
case NORMAL:
case ARROW:
SetCursor( LoadCursor( NULL, IDC_ARROW ) );
break;
case SCROLL:
SetCursor( LoadCursor( NULL, IDC_SIZEALL ) );
break;
case CROSS:
SetCursor( LoadCursor( NULL, IDC_CROSS ) );
break;
} // end switch
// save current cursor
m_currentCursor = cursor;
} // end setCursor
//-------------------------------------------------------------------------------------------------
/** Capture the mouse to our application */
//-------------------------------------------------------------------------------------------------
void DirectInputMouse::capture( void )
{
SetCapture( ApplicationHWnd );
} // end capture
//-------------------------------------------------------------------------------------------------
/** Release the mouse capture for our app window */
//-------------------------------------------------------------------------------------------------
void DirectInputMouse::releaseCapture( void )
{
ReleaseCapture();
} // end releaseCapture